Go调用以太坊合约和在测试用例中调用合约

首先使用npm下载solc

npm install -g solc

这个主要是编译solidity文件生成对应的ABI和ABI BIN。如果使用这个需要到remix上面生成。

pragma solidity ^0.6.0;
  library SafeMath {
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
      assert(b <= a);
      return a - b;
    }
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
      uint256 c = a + b;
      assert(c >= a);
      return c;
    }
  }
  contract ERCToken {
    using SafeMath for uint256;
    string public constant name = "ERCToken";
    string public constant symbol = "ERC";
    uint256 public constant decimals = 18;
    uint256 public constant totalSupply = 100000000000000000000000000;
    //address private founder = 0x0;
    uint256 private distributed = 0;
    mapping (address => uint256) private balances;
    mapping (address => mapping (address => uint256)) private allowed;
//    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    constructor() public {
      balances[msg.sender] = totalSupply;
    }
    function balanceOf(address _owner) public view returns (uint256 balance) {
      return balances[_owner];
    }
    function transfer(address _to, uint256 _value) public returns (bool success) {
     // require (_to != 0x0, "");
      require((balances[msg.sender] >= _value), "");
      balances[msg.sender] = balances[msg.sender].sub(_value);
      balances[_to] = balances[_to].add(_value);
 //     emit Transfer(msg.sender, _to, _value);
      return true;
    }
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
      //require (_to != 0x0, "");
      require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value, "not eng aaa");
      allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
      balances[_from] = balances[_from].sub(_value);
      balances[_to] = balances[_to].add(_value);
      emit Transfer(_from, _to, _value);
      return true;
    }
    function approve(address _spender, uint256 _value) public returns (bool success) {
      allowed[msg.sender][_spender] = _value;
      emit Approval(msg.sender, _spender, _value);
      return true;
    }
    function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
      return allowed[_owner][_spender];
    }
  }

我自己随便写了个solidity,然后编译生成 token_sol_ERCToken.abi

solcjs --abi token.sol

abigen将ABI转换Go文件。 这个新文件将包含我们可以用来与Go应用程序中的智能合约进行交互的所有可用方法。

go install ./cmd/abigen

abigen --abi=token_sol_ERCToken.abi --pkg=token --out=Token.go

为了Go部署合约,需要将solidity智能合约编译为EVM字节码。 EVM字节码将在事务的数据字段中发送。 在Go文件上生成部署方法需要bin文件。 

solcjs --bin token.sol

现在还需要编译合约文件包含bin文件,生成合约deploy部署方法。

abigen --bin=token_sol_ERCToken.bin --abi=token_sol_ERCToken.abi --pkg=token --out=Token.go

这样我们使用Go通过RPC就可以调用合约了。

package contract

import (
	"fmt"
	"math/big"
	"os"
	"testing"

	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/log"
)

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 TestENS(t *testing.T) {
	contractBackend := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000)
	transactOpts := bind.NewKeyedTransactor(key)

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

	contractBackend.Commit()


	// Set ourself as the owner of the name.
	var name string
	err = ens.ContractCaller.contract.Call(nil,&name,"name")
	if err != nil {
		log.Error("Failed to retrieve token ","name: %v", err)
	}
	fmt.Println("Token name:", name)

	var totalSupply *big.Int
	err = ens.ContractCaller.contract.Call(nil,&totalSupply,"totalSupply")
	if err != nil {
		log.Error("Failed to retrieve token ","name: %v", err)
	}
	fmt.Println("totalSupply ", totalSupply)

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

 然后我想在测试用例中调用合约,测试生成的LOG,起初我在chain maker中死活调不成功。

package eth

import (
	"fmt"
	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/contracts/ensTest"
	"github.com/ethereum/go-ethereum/core"
	"github.com/ethereum/go-ethereum/core/vm"
	"github.com/ethereum/go-ethereum/log"
	"math/big"
	"os"
	"strings"

	"github.com/ethereum/go-ethereum/consensus/ethash"
	"github.com/ethereum/go-ethereum/core/rawdb"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/params"
)

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


func ExampleGenerateChain() {
	var (
		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
		addr1   = crypto.PubkeyToAddress(key1.PublicKey)
		db      = rawdb.NewMemoryDatabase()
		contract1 common.Address
		contractabi, _ = abi.JSON(strings.NewReader(contract.ContractABI))
		testAddr = common.HexToAddress("0x1234123412341234123412341234123412341234")
	)

	// Ensure that key1 has some funds in the genesis block.
	gspec := &core.Genesis{
		Config: params.AllEthashProtocolChanges,
		Alloc:  core.GenesisAlloc{addr1: {Balance: big.NewInt(500000000000)}},
	}
	genesis := gspec.MustCommit(db)

	// Import the chain. This runs all block validation rules.
	blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
	defer blockchain.Stop()

	// This call generates a chain of 5 blocks. The function runs for
	// each block and adds different features to gen based on the
	// block index.
	signer := types.HomesteadSigner{}
	chain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 6, func(i int, gen *core.BlockGen) {
		switch i {
		case 1:
			fmt.Println("ensAddr contract1 ",addr1.String()," gen.TxNonce(addr1)",gen.TxNonce(addr1))
			contract1 = crypto.CreateAddress(addr1, gen.TxNonce(addr1))
			fmt.Println("ensAddr contract1 ",contract1.String())
			// Block 3 is empty but was mined by addr3.
			tx1, _ := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), nil, 820143, big.NewInt(1), common.FromHex(contract.ContractBin)), signer, key1)
			gen.AddTx(tx1)
		case 2:
			// Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
			input, _ := contractabi.Pack("transfer",testAddr, big.NewInt(50000))
			tx1, _ := types.SignTx(types.NewTransaction(gen.TxNonce(addr1),contract1, new(big.Int), 52045, big.NewInt(1), input), signer, key1)
			gen.AddTxWithChain(blockchain,tx1)
			fmt.Println("ensAddr ",contract1.String()," Nonce ",gen.TxNonce(addr1))
		}
	})


	if i, err := blockchain.InsertChain(chain); err != nil {
		fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
		return
	}
}

经过一轮测试

contract1 = crypto.CreateAddress(addr1, gen.TxNonce(addr1))

这段代码写在了addTX后面,修改了下,放在了types.SignTx签名交易前面,合约调用成功了。

下面是加的一些调试EVM,生成trace的代码,会将EVM的执行过程记录下来。

func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
	var (
		receipts types.Receipts
		usedGas  = new(uint64)
		header   = block.Header()
		allLogs  []*types.Log
		gp       = new(GasPool).AddGas(block.GasLimit())
	)
	// Mutate the block and state according to any hard-fork specs
	if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
		misc.ApplyDAOHardFork(statedb)
	}

	var (
		vmConf vm.Config
		dump   *os.File
		writer *bufio.Writer
		logConfig vm.LogConfig
	)

	logConfig.Debug = true
	// Generate a unique temporary file to dump it into

	// Iterate over and process the individual transactions
	for i, tx := range block.Transactions() {
		statedb.Prepare(tx.Hash(), block.Hash(), i)
		prefix := fmt.Sprintf("block_%#x-%d-%#x-", block.Hash().Bytes()[:4], 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, nil, gp, statedb, header, tx, usedGas, 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())
		}
		receipts = append(receipts, receipt)
		allLogs = append(allLogs, receipt.Logs...)
	}
	// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
	p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles())

	return receipts, allLogs, *usedGas, nil
}

 

发布了35 篇原创文章 · 获赞 3 · 访问量 4万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览