国密算法合约EVM debug之旅

项目中用到了国密算法,hash为国密方法,用solc编译器编译合约代码,调用合约方法执行失败,找了好长时间发现了原因。

首先部署合约是没问题的,合约执行方法的时候不成功,后面发现是交易input中的add方法签名和用以太坊solc编译出来的方法签名不一致,合约执行过程中找不到方法,交易执行失败

solidity源码如下

pragma solidity ^0.6.4;

contract C {
    uint256 a;
    constructor() public  {
      a = 1;
    }
    function add(uint256 b) public {
        a = a + b;
    }
}

Go合约调用

solcjs --bin math.sol
solcjs --abi math.sol

生成go调用合约模板命令

//生成合约 go文件
abigen --bin=math_sol_C.bin --abi=math_sol_C.abi --pkg=math --out=Math.go

Go源码如下

package math

import (
	"fmt"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/log"
	"github.com/truechain/truechain-engineering-code/accounts/abi/bind"
	"github.com/truechain/truechain-engineering-code/accounts/abi/bind/backends"
	"github.com/truechain/truechain-engineering-code/core/types"
	"math/big"
	"os"
	"testing"
)

func init() {
	log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
}

var (
	key, _   = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
	addr     = crypto.PubkeyToAddress(key.PublicKey)
	testAddr = common.HexToAddress("0x1234123412341234123412341234123412341234")
)

func TestMath(t *testing.T) {
	contractBackend := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000)
	transactOpts := bind.NewKeyedTransactor(key)

	// Deploy the ENS registry
	ensAddr, _, _, err := DeployToken(transactOpts, contractBackend)
	if err != nil {
		t.Fatalf("can't DeployContract: %v", err)
	}
	ens, err := NewToken(ensAddr, contractBackend)
	if err != nil {
		t.Fatalf("can't NewContract: %v", err)
	}

	contractBackend.Commit()

	tx, err := ens.Add(transactOpts, big.NewInt(50000))
	if err != nil {
		log.Error("Failed to request token transfer", ": %v", err)
	}
	fmt.Printf("Transfer pending: 0x%x\n", tx.Hash())
	contractBackend.Commit()
}

执行测试用例,部署可以成功

// 这是部署的日志状态打印
SetCode  171   0xdbff645123207cac88f3054b7d9159225f4f45a59d8d8d85e384f1dd7b162904   0xe8CCbD4fcE87173a98561A17E028DDc1bC7d0BFe
// 只有一个变量a,在状态中的存储key是索引0,value是1.
updateTrie 0xe8CCbD4fcE87173a98561A17E028DDc1bC7d0BFe  key  0x0000000000000000000000000000000000000000000000000000000000000000  v  0x0000000000000000000000000000000000000000000000000000000000000001

执行ens.Add方法,调用会失败

DEBUG[05-29|18:40:27.021] VM returned with error                   err="evm: execution reverted"

是不是很奇怪,部署没问题,调用方法出错了

EVM Trace log

交易执行中添加合约trace log,上篇文件加了,没用上。

		prefix := fmt.Sprintf("block_%d-%d-%#x-",block.NumberU64(), i, tx.Hash().Bytes()[:4])
		dump, _ = ioutil.TempFile(os.TempDir(), prefix)
		// Swap out the noop logger to the standard tracer
		writer = bufio.NewWriter(dump)
		vmConf = vm.Config{
			Debug:                   true,
			Tracer:                  vm.NewJSONLogger(&logConfig, writer),
			EnablePreimageRecording: true,
		}
		receipt, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, usedGas, feeAmount, vmConf)
		if err != nil {
			return nil, nil, 0, err
		}
		if writer != nil {
			writer.Flush()
		}
		if dump != nil {
			dump.Close()
			log.Info("Wrote standard trace", "file", dump.Name())
		}

下面是对比成功调用和失败的trace log。图片里面画圈的地方不一致,方法签名用到了hash算法。
在这里插入图片描述
取出交易的Input, 前几个字段是方法签名,后面c350是add方法参数5000的16进制表示

1003e2d2000000000000000000000000000000000000000000000000000000000000c350

caf838c8000000000000000000000000000000000000000000000000000000000000c350

hash生成方法签名

func TestMethod(t *testing.T) {
	method := []byte("add(uint256)")
	//国密
	sig := crypto.Keccak256(method)[:4]
	fmt.Println(" ", hex.EncodeToString(sig))
	// 原生
	d := sha3.NewLegacyKeccak256()
	d.Write(method)
	fmt.Println(" ", hex.EncodeToString(d.Sum(nil)[:4]))
}

执行结果

=== RUN   TestMethod
  caf838c8
  1003e2d2
--- PASS: TestMethod (0.00s)

验证可知是方法签名不一致导致合约执行过程中方法找不到

// 以太坊 code
60806040523480156100115760006000fd5b505b600160006000508190909055505b610026565b60ab806100346000396000f3fe608060405234801560105760006000fd5b5060043610602c5760003560e01c80631003e2d214603257602c565b60006000fd5b605c6004803603602081101560475760006000fd5b8101908080359060200190929190505050605e565b005b806000600050540160006000508190909055505b5056fea2646970667358221220c76ddd0af49ffc6b50b07f255c3b36ade4aa285b54aced71e2930afea3df133364736f6c63430006040033

// 国密 code
0x60606040523415600b57fe5b5b60016000819055505b5b6092806100246000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063caf838c814603a575bfe5b3415604157fe5b605560048080359060200190919050506057565b005b80600054016000819055505b505600a165627a7a72305820d2459cc400ba3e9a500c20bd88d035902735f29d9d58ff5538f75e510bb703ed0029

使用如下国密input+国密code即可合约调用成功。

caf838c8000000000000000000000000000000000000000000000000000000000000c350

国密合约code可使用 国密solc编译,ABI无需修改
以太坊Solc编译器

猜你喜欢

转载自blog.csdn.net/JIYILANZHOU/article/details/106430738