项目中用到了国密算法,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
生成合约CODE
和ABI
文件,详情可查看上一篇 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编译器