From c97b4e89fe6e5c5f1b75535771f0bc649d9bbdef Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Fri, 30 Oct 2015 09:59:21 +0100 Subject: [PATCH] accounts/contract: implemented callable contracts --- accounts/contract/contract.go | 87 +++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 accounts/contract/contract.go diff --git a/accounts/contract/contract.go b/accounts/contract/contract.go new file mode 100644 index 0000000000..d339a9b15f --- /dev/null +++ b/accounts/contract/contract.go @@ -0,0 +1,87 @@ +package contract + +import ( + "math/big" + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/state" +) + +type Contract struct { + backend core.Backend + abi.ABI + Address common.Address +} + +func getAccount(backend core.Backend) *state.StateObject { + statedb, _ := state.New(backend.BlockChain().CurrentHeader().Root, backend.ChainDb()) + accounts, err := backend.AccountManager().Accounts() + if err != nil || len(accounts) == 0 { + return statedb.GetOrNewStateObject(common.Address{}) + } else { + return statedb.GetOrNewStateObject(accounts[0].Address) + } +} + +func New(definition string, addr common.Address, backend core.Backend) (*Contract, error) { + abi, err := abi.JSON(strings.NewReader(definition)) + if err != nil { + return nil, err + } + + return &Contract{ + backend: backend, + ABI: abi, + Address: addr, + }, nil +} + +func (c *Contract) Call(method string, args ...interface{}) ([]byte, *big.Int, error) { + msg := CallMessage{ + gas: new(big.Int).Set(common.MaxBig), + gasPrice: new(big.Int), + value: new(big.Int), + from: getAccount(c.backend), + } + return c.CallWithMessage(msg, method, args...) +} + +func (c *Contract) CallWithMessage(msg CallMessage, method string, args ...interface{}) ([]byte, *big.Int, error) { + input, err := c.ABI.Pack(method, args...) + if err != nil { + return nil, nil, err + } + msg.data = input + msg.to = &c.Address + + header := c.backend.BlockChain().CurrentHeader() + statedb, err := state.New(header.Root, c.backend.ChainDb()) + if err != nil { + return nil, nil, err + } + vmenv := core.NewEnv(statedb, c.backend.BlockChain(), msg, header) + gp := new(core.GasPool).AddGas(common.MaxBig) + + return core.ApplyMessage(vmenv, msg, gp) +} + +// CallMessage is the message type used for call transations. +type CallMessage struct { + from *state.StateObject + to *common.Address + gas, gasPrice *big.Int + value *big.Int + data []byte +} + +// accessor boilerplate to implement core.Message +func (m CallMessage) From() (common.Address, error) { return m.from.Address(), nil } +func (m CallMessage) Nonce() uint64 { return m.from.Nonce() } +func (m CallMessage) To() *common.Address { return m.to } +func (m CallMessage) GasPrice() *big.Int { return m.gasPrice } +func (m CallMessage) Gas() *big.Int { return m.gas } +func (m CallMessage) Value() *big.Int { return m.value } +func (m CallMessage) Data() []byte { return m.data }