From ff58d84f81d584646ca3f8fe00493cdcec8239ed Mon Sep 17 00:00:00 2001 From: Determinant Date: Thu, 15 Aug 2019 18:49:42 -0400 Subject: add smart contract example --- coreth.go | 4 ++ examples/counter/counter.sol | 14 ++++ examples/counter/main.go | 153 +++++++++++++++++++++++++++++++++++++++++++ examples/payments/main.go | 4 +- 4 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 examples/counter/counter.sol create mode 100644 examples/counter/main.go diff --git a/coreth.go b/coreth.go index 84a6b59..a164452 100644 --- a/coreth.go +++ b/coreth.go @@ -106,6 +106,10 @@ func (self *ETHChain) SetHead(hash common.Hash) error { return self.backend.BlockChain().FastSyncCommitHead(hash) } +func (self *ETHChain) GetReceiptsByHash(hash common.Hash) types.Receipts { + return self.backend.BlockChain().GetReceiptsByHash(hash) +} + type Key struct { Address common.Address PrivateKey *ecdsa.PrivateKey diff --git a/examples/counter/counter.sol b/examples/counter/counter.sol new file mode 100644 index 0000000..c05bf23 --- /dev/null +++ b/examples/counter/counter.sol @@ -0,0 +1,14 @@ +pragma solidity ^0.5.10; + +contract Counter { + uint256 x; + + constructor() public { + x = 42; + } + + function add(uint256 y) public returns (uint256) { + x = x + y; + return x; + } +} diff --git a/examples/counter/main.go b/examples/counter/main.go new file mode 100644 index 0000000..662fea1 --- /dev/null +++ b/examples/counter/main.go @@ -0,0 +1,153 @@ +package main + +import ( + "crypto/rand" + "encoding/hex" + "fmt" + "github.com/ava-labs/coreth" + "github.com/ava-labs/coreth/eth" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/compiler" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/params" + "math/big" + "os" + "os/signal" + "path/filepath" + "syscall" + "time" +) + +func checkError(err error) { + if err != nil { + panic(err) + } +} + +func main() { + // configure the chain + config := eth.DefaultConfig + chainConfig := ¶ms.ChainConfig{ + ChainID: big.NewInt(1), + HomesteadBlock: big.NewInt(0), + DAOForkBlock: big.NewInt(0), + DAOForkSupport: true, + EIP150Block: big.NewInt(0), + EIP150Hash: common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0"), + EIP155Block: big.NewInt(0), + EIP158Block: big.NewInt(0), + ByzantiumBlock: big.NewInt(0), + ConstantinopleBlock: big.NewInt(0), + PetersburgBlock: big.NewInt(0), + IstanbulBlock: nil, + Ethash: nil, + } + + // configure the genesis block + genBalance := big.NewInt(100000000000000000) + genKey, _ := coreth.NewKey(rand.Reader) + + config.Genesis = &core.Genesis{ + Config: chainConfig, + Nonce: 0, + Number: 0, + ExtraData: hexutil.MustDecode("0x00"), + GasLimit: 100000000, + Difficulty: big.NewInt(0), + Alloc: core.GenesisAlloc{genKey.Address: {Balance: genBalance}}, + } + + // grab the control of block generation and disable auto uncle + config.Miner.ManualMining = true + config.Miner.ManualUncle = true + + // compile the smart contract + counterSrc, err := filepath.Abs("./counter.sol") + checkError(err) + contracts, err := compiler.CompileSolidity("", counterSrc) + checkError(err) + contract, _ := contracts[fmt.Sprintf("%s:%s", counterSrc, "Counter")] + + // info required to generate a transaction + chainID := chainConfig.ChainID + nonce := uint64(0) + gasLimit := 10000000 + gasPrice := big.NewInt(1000000000) + + blockCount := 0 + chain := coreth.NewETHChain(&config, nil) + firstBlock := false + var contractAddr common.Address + postGen := func(block *types.Block) bool { + if blockCount == 15 { + state, err := chain.CurrentState() + checkError(err) + log.Info(fmt.Sprintf("genesis balance = %s", state.GetBalance(genKey.Address))) + log.Info(fmt.Sprintf("contract balance = %s", state.GetBalance(contractAddr))) + log.Info(fmt.Sprintf("state = %s", state.Dump(true, false, true))) + log.Info(fmt.Sprintf("x = %s", state.GetState(contractAddr, common.BigToHash(big.NewInt(0))).String())) + return true + } + if !firstBlock { + firstBlock = true + receipts := chain.GetReceiptsByHash(block.Hash()) + if len(receipts) != 1 { + panic("# receipts is not 1") + } + contractAddr = receipts[0].ContractAddress + txHash := receipts[0].TxHash + log.Info(fmt.Sprintf("deploy tx = %s", txHash.String())) + log.Info(fmt.Sprintf("contract addr = %s", contractAddr.String())) + call := common.Hex2Bytes("1003e2d20000000000000000000000000000000000000000000000000000000000000001") + state, _ := chain.CurrentState() + log.Info(fmt.Sprintf("code = %s", hex.EncodeToString(state.GetCode(contractAddr)))) + go func() { + for i := 0; i < 10; i++ { + tx := types.NewTransaction(nonce, contractAddr, big.NewInt(0), uint64(gasLimit), gasPrice, call) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), genKey.PrivateKey) + checkError(err) + chain.AddRemoteTxs([]*types.Transaction{signedTx}) + time.Sleep(1000 * time.Millisecond) + nonce++ + } + }() + } + return false + } + chain.SetOnSeal(func(block *types.Block) error { + go func() { + // the minimum time gap is 1s + time.Sleep(1000 * time.Millisecond) + // generate 15 blocks + blockCount++ + if postGen(block) { + return + } + chain.GenBlock() + }() + return nil + }) + + // start the chain + chain.Start() + + _ = contract + code := common.Hex2Bytes(contract.Code[2:]) + tx := types.NewContractCreation(nonce, big.NewInt(0), uint64(gasLimit), gasPrice, code) + signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), genKey.PrivateKey) + checkError(err) + chain.AddRemoteTxs([]*types.Transaction{signedTx}) + time.Sleep(1000 * time.Millisecond) + nonce++ + + chain.GenBlock() + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt, syscall.SIGTERM) + signal.Notify(c, os.Interrupt, syscall.SIGINT) + <-c + chain.Stop() +} diff --git a/examples/payments/main.go b/examples/payments/main.go index 1c0a18e..db43319 100644 --- a/examples/payments/main.go +++ b/examples/payments/main.go @@ -76,8 +76,8 @@ func main() { showBalance := func() { state, err := chain.CurrentState() checkError(err) - log.Info(fmt.Sprintf("genesis balanche = %s", state.GetBalance(genKey.Address))) - log.Info(fmt.Sprintf("bob's balanche = %s", state.GetBalance(bob.Address))) + log.Info(fmt.Sprintf("genesis balance = %s", state.GetBalance(genKey.Address))) + log.Info(fmt.Sprintf("bob's balance = %s", state.GetBalance(bob.Address))) } chain.SetOnSeal(func(block *types.Block) error { go func() { -- cgit v1.2.3