diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index bdc2a98bc8..31b528865e 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -164,7 +164,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM if err != nil { return nil, err } - rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) + rval, _, err := b.callContract(call, b.blockchain.CurrentBlock(), state) return rval, err } @@ -174,7 +174,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu defer b.mu.Unlock() defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) - rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + rval, _, err := b.callContract(call, b.pendingBlock, b.pendingState) return rval, err } @@ -200,32 +200,16 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs defer b.mu.Unlock() defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) - _, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + _, gas, err := b.callContract(call, b.pendingBlock, b.pendingState) return gas, err } // callContract implemens common code between normal and pending contract calls. // state is modified during execution, make sure to copy it if necessary. -func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) { - // Ensure message is initialized properly. - if call.GasPrice == nil { - call.GasPrice = big.NewInt(1) - } - if call.Gas == nil || call.Gas.BitLen() == 0 { - call.Gas = big.NewInt(50000000) - } - if call.Value == nil { - call.Value = new(big.Int) - } - // Set infinite balance to the fake caller account. - from := statedb.GetOrNewStateObject(call.From) - from.SetBalance(common.MaxBig) - // Execute the call. - msg := callmsg{call} - vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) - gaspool := new(core.GasPool).AddGas(common.MaxBig) - ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() - return ret, gasUsed, err +func (b *SimulatedBackend) callContract(call ethereum.CallMsg, block *types.Block, state *state.StateDB) ([]byte, *big.Int, error) { + return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment { + return core.NewEnv(state, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) + }) } // SendTransaction updates the pending block to include the given transaction. @@ -253,18 +237,3 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) return nil } - -// callmsg implements core.Message to allow passing it as a transaction simulator. -type callmsg struct { - ethereum.CallMsg -} - -func (m callmsg) From() (common.Address, error) { return m.CallMsg.From, nil } -func (m callmsg) FromFrontier() (common.Address, error) { return m.CallMsg.From, nil } -func (m callmsg) Nonce() uint64 { return 0 } -func (m callmsg) CheckNonce() bool { return false } -func (m callmsg) To() *common.Address { return m.CallMsg.To } -func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } -func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas } -func (m callmsg) Value() *big.Int { return m.CallMsg.Value } -func (m callmsg) Data() []byte { return m.CallMsg.Data } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 626c2615d9..15d4c45f5b 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -768,7 +768,7 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { fullNode, err := eth.New(ctx, ethConf) if fullNode != nil && ethConf.LightServ > 0 { - ls, _ := les.NewLesServer(fullNode, ethConf) + ls, _ := les.NewLesServer(fullNode, stack.EventMux(), ethConf) fullNode.AddLesServer(ls) } return fullNode, err diff --git a/common/registrar/contracts.go b/common/registrar/contracts.go deleted file mode 100644 index cd80dfcaba..0000000000 --- a/common/registrar/contracts.go +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package registrar - -const ( // built-in contracts address source code and evm code - UrlHintCode = "0x60c180600c6000396000f30060003560e060020a90048063300a3bbf14601557005b6024600435602435604435602a565b60006000f35b6000600084815260200190815260200160002054600160a060020a0316600014806078575033600160a060020a03166000600085815260200190815260200160002054600160a060020a0316145b607f5760bc565b336000600085815260200190815260200160002081905550806001600085815260200190815260200160002083610100811060b657005b01819055505b50505056" - - UrlHintSrc = ` -contract URLhint { - function register(uint256 _hash, uint8 idx, uint256 _url) { - if (owner[_hash] == 0 || owner[_hash] == msg.sender) { - owner[_hash] = msg.sender; - url[_hash][idx] = _url; - } - } - mapping (uint256 => address) owner; - (uint256 => uint256[256]) url; -} - ` - - HashRegCode = "0x609880600c6000396000f30060003560e060020a9004806331e12c2014601f578063d66d6c1014602b57005b6025603d565b60006000f35b6037600435602435605d565b60006000f35b600054600160a060020a0316600014605357605b565b336000819055505b565b600054600160a060020a031633600160a060020a031614607b576094565b8060016000848152602001908152602001600020819055505b505056" - - HashRegSrc = ` -contract HashReg { - function setowner() { - if (owner == 0) { - owner = msg.sender; - } - } - function register(uint256 _key, uint256 _content) { - if (msg.sender == owner) { - content[_key] = _content; - } - } - address owner; - mapping (uint256 => uint256) content; -} -` - - GlobalRegistrarSrc = ` -//sol - -import "owned"; - -contract NameRegister { - function addr(bytes32 _name) constant returns (address o_owner) {} - function name(address _owner) constant returns (bytes32 o_name) {} -} - -contract Registrar is NameRegister { - event Changed(bytes32 indexed name); - event PrimaryChanged(bytes32 indexed name, address indexed addr); - - function owner(bytes32 _name) constant returns (address o_owner) {} - function addr(bytes32 _name) constant returns (address o_address) {} - function subRegistrar(bytes32 _name) constant returns (address o_subRegistrar) {} - function content(bytes32 _name) constant returns (bytes32 o_content) {} - - function name(address _owner) constant returns (bytes32 o_name) {} -} - -contract GlobalRegistrar is Registrar { - struct Record { - address owner; - address primary; - address subRegistrar; - bytes32 content; - uint value; - uint renewalDate; - } - - function Registrar() { - // TODO: Populate with hall-of-fame. - } - - function reserve(bytes32 _name) { - // Don't allow the same name to be overwritten. - // TODO: bidding mechanism - if (m_toRecord[_name].owner == 0) { - m_toRecord[_name].owner = msg.sender; - Changed(_name); - } - } - - /* - TODO - > 12 chars: free - <= 12 chars: auction: - 1. new names are auctioned - - 7 day period to collect all bid bytes32es + deposits - - 1 day period to collect all bids to be considered (validity requires associated deposit to be >10% of bid) - - all valid bids are burnt except highest - difference between that and second highest is returned to winner - 2. remember when last auctioned/renewed - 3. anyone can force renewal process: - - 7 day period to collect all bid bytes32es + deposits - - 1 day period to collect all bids & full amounts - bids only uncovered if sufficiently high. - - 1% of winner burnt; original owner paid rest. - */ - - modifier onlyrecordowner(bytes32 _name) { if (m_toRecord[_name].owner == msg.sender) _ } - - function transfer(bytes32 _name, address _newOwner) onlyrecordowner(_name) { - m_toRecord[_name].owner = _newOwner; - Changed(_name); - } - - function disown(bytes32 _name) onlyrecordowner(_name) { - if (m_toName[m_toRecord[_name].primary] == _name) - { - PrimaryChanged(_name, m_toRecord[_name].primary); - m_toName[m_toRecord[_name].primary] = ""; - } - delete m_toRecord[_name]; - Changed(_name); - } - - function setAddress(bytes32 _name, address _a, bool _primary) onlyrecordowner(_name) { - m_toRecord[_name].primary = _a; - if (_primary) - { - PrimaryChanged(_name, _a); - m_toName[_a] = _name; - } - Changed(_name); - } - function setSubRegistrar(bytes32 _name, address _registrar) onlyrecordowner(_name) { - m_toRecord[_name].subRegistrar = _registrar; - Changed(_name); - } - function setContent(bytes32 _name, bytes32 _content) onlyrecordowner(_name) { - m_toRecord[_name].content = _content; - Changed(_name); - } - - function owner(bytes32 _name) constant returns (address) { return m_toRecord[_name].owner; } - function addr(bytes32 _name) constant returns (address) { return m_toRecord[_name].primary; } -// function subRegistrar(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // TODO: bring in on next iteration. - function register(bytes32 _name) constant returns (address) { return m_toRecord[_name].subRegistrar; } // only possible for now - function content(bytes32 _name) constant returns (bytes32) { return m_toRecord[_name].content; } - function name(address _owner) constant returns (bytes32 o_name) { return m_toName[_owner]; } - - mapping (address => bytes32) m_toName; - mapping (bytes32 => Record) m_toRecord; -} -` - GlobalRegistrarAbi = `[{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"name","outputs":[{"name":"o_name","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"content","outputs":[{"name":"","type":"bytes32"}],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"addr","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"reserve","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"subRegistrar","outputs":[{"name":"o_subRegistrar","type":"address"}],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_newOwner","type":"address"}],"name":"transfer","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_registrar","type":"address"}],"name":"setSubRegistrar","outputs":[],"type":"function"},{"constant":false,"inputs":[],"name":"Registrar","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_a","type":"address"},{"name":"_primary","type":"bool"}],"name":"setAddress","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"},{"name":"_content","type":"bytes32"}],"name":"setContent","outputs":[],"type":"function"},{"constant":false,"inputs":[{"name":"_name","type":"bytes32"}],"name":"disown","outputs":[],"type":"function"},{"constant":true,"inputs":[{"name":"_name","type":"bytes32"}],"name":"register","outputs":[{"name":"","type":"address"}],"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"}],"name":"Changed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"name","type":"bytes32"},{"indexed":true,"name":"addr","type":"address"}],"name":"PrimaryChanged","type":"event"}]` - - GlobalRegistrarCode = `0x610b408061000e6000396000f3006000357c01000000000000000000000000000000000000000000000000000000009004806301984892146100b357806302571be3146100ce5780632dff6941146100ff5780633b3b57de1461011a578063432ced041461014b5780635a3a05bd1461016257806379ce9fac1461019357806389a69c0e146101b0578063b9f37c86146101cd578063be99a980146101de578063c3d014d614610201578063d93e75731461021e578063e1fa8e841461023557005b6100c4600480359060200150610b02565b8060005260206000f35b6100df6004803590602001506109f3565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b610110600480359060200150610ad4565b8060005260206000f35b61012b600480359060200150610a3e565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b61015c600480359060200150610271565b60006000f35b610173600480359060200150610266565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b6101aa600480359060200180359060200150610341565b60006000f35b6101c7600480359060200180359060200150610844565b60006000f35b6101d860045061026e565b60006000f35b6101fb6004803590602001803590602001803590602001506106de565b60006000f35b61021860048035906020018035906020015061092c565b60006000f35b61022f600480359060200150610429565b60006000f35b610246600480359060200150610a89565b8073ffffffffffffffffffffffffffffffffffffffff1660005260206000f35b60005b919050565b5b565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561033d57336001600050600083815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550807fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b5b50565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561042357816001600050600085815260200190815260200160002060005060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b803373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156106d95781600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000505414156105fd576001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16827ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a36000600060005060006001600050600086815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b6001600050600083815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556002820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556003820160005060009055600482016000506000905560058201600050600090555050817fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b50565b823373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561083d57826001600050600086815260200190815260200160002060005060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908302179055508115610811578273ffffffffffffffffffffffffffffffffffffffff16847ff63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a85456040604090036040a383600060005060008573ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050819055505b837fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b505050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141561092657816001600050600085815260200190815260200160002060005060020160006101000a81548173ffffffffffffffffffffffffffffffffffffffff02191690830217905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b813373ffffffffffffffffffffffffffffffffffffffff166001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614156109ed57816001600050600085815260200190815260200160002060005060030160005081905550827fa6697e974e6a320f454390be03f74955e8978f1a6971ea6730542e37b66179bc6040604090036040a25b505b5050565b60006001600050600083815260200190815260200160002060005060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a39565b919050565b60006001600050600083815260200190815260200160002060005060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610a84565b919050565b60006001600050600083815260200190815260200160002060005060020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050610acf565b919050565b600060016000506000838152602001908152602001600020600050600301600050549050610afd565b919050565b6000600060005060008373ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600050549050610b3b565b91905056` -) diff --git a/common/registrar/ethreg/api.go b/common/registrar/ethreg/api.go deleted file mode 100644 index 10050a5457..0000000000 --- a/common/registrar/ethreg/api.go +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package ethreg - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/compiler" - "github.com/ethereum/go-ethereum/common/registrar" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" -) - -// registryAPIBackend is a backend for an Ethereum Registry. -type registryAPIBackend struct { - config *core.ChainConfig - bc *core.BlockChain - chainDb ethdb.Database - txPool *core.TxPool - am *accounts.Manager -} - -// PrivateRegistarAPI offers various functions to access the Ethereum registry. -type PrivateRegistarAPI struct { - config *core.ChainConfig - be *registryAPIBackend -} - -// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance. -func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI { - return &PrivateRegistarAPI{ - config: config, - be: ®istryAPIBackend{ - config: config, - bc: bc, - chainDb: chainDb, - txPool: txPool, - am: am, - }, - } -} - -// SetGlobalRegistrar allows clients to set the global registry for the node. -// This method can be used to deploy a new registry. First zero out the current -// address by calling the method with namereg = '0x0' and then call this method -// again with '' as namereg. This will submit a transaction to the network which -// will deploy a new registry on execution. The TX hash is returned. When called -// with namereg '' and the current address is not zero the current global is -// address is returned.. -func (api *PrivateRegistarAPI) SetGlobalRegistrar(namereg string, from common.Address) (string, error) { - return registrar.New(api.be).SetGlobalRegistrar(namereg, from) -} - -// SetHashReg queries the registry for a hash. -func (api *PrivateRegistarAPI) SetHashReg(hashreg string, from common.Address) (string, error) { - return registrar.New(api.be).SetHashReg(hashreg, from) -} - -// SetUrlHint queries the registry for an url. -func (api *PrivateRegistarAPI) SetUrlHint(hashreg string, from common.Address) (string, error) { - return registrar.New(api.be).SetUrlHint(hashreg, from) -} - -// SaveInfo stores contract information on the local file system. -func (api *PrivateRegistarAPI) SaveInfo(info *compiler.ContractInfo, filename string) (contenthash common.Hash, err error) { - return compiler.SaveInfo(info, filename) -} - -// Register registers a new content hash in the registry. -func (api *PrivateRegistarAPI) Register(sender common.Address, addr common.Address, contentHashHex string) (bool, error) { - block := api.be.bc.CurrentBlock() - state, err := state.New(block.Root(), api.be.chainDb) - if err != nil { - return false, err - } - - codeb := state.GetCode(addr) - codeHash := common.BytesToHash(crypto.Keccak256(codeb)) - contentHash := common.HexToHash(contentHashHex) - - _, err = registrar.New(api.be).SetHashToHash(sender, codeHash, contentHash) - return err == nil, err -} - -// RegisterUrl registers a new url in the registry. -func (api *PrivateRegistarAPI) RegisterUrl(sender common.Address, contentHashHex string, url string) (bool, error) { - _, err := registrar.New(api.be).SetUrlToHash(sender, common.HexToHash(contentHashHex), url) - return err == nil, err -} - -// callmsg is the message type used for call transations. -type callmsg struct { - from *state.StateObject - to *common.Address - gas, gasPrice *big.Int - value *big.Int - data []byte -} - -// accessor boilerplate to implement core.Message -func (m callmsg) From() (common.Address, error) { - return m.from.Address(), nil -} -func (m callmsg) FromFrontier() (common.Address, error) { - return m.from.Address(), nil -} -func (m callmsg) Nonce() uint64 { - return 0 -} -func (m callmsg) CheckNonce() bool { - return false -} -func (m callmsg) To() *common.Address { - return m.to -} -func (m callmsg) GasPrice() *big.Int { - return m.gasPrice -} -func (m callmsg) Gas() *big.Int { - return m.gas -} -func (m callmsg) Value() *big.Int { - return m.value -} -func (m callmsg) Data() []byte { - return m.data -} - -// Call forms a transaction from the given arguments and tries to execute it on -// a private VM with a copy of the state. Any changes are therefore only temporary -// and not part of the actual state. This allows for local execution/queries. -func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) { - block := be.bc.CurrentBlock() - statedb, err := state.New(block.Root(), be.chainDb) - if err != nil { - return "", "", err - } - - var from *state.StateObject - if len(fromStr) == 0 { - accounts := be.am.Accounts() - if len(accounts) == 0 { - from = statedb.GetOrNewStateObject(common.Address{}) - } else { - from = statedb.GetOrNewStateObject(accounts[0].Address) - } - } else { - from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr)) - } - - from.SetBalance(common.MaxBig) - - msg := callmsg{ - from: from, - gas: common.Big(gasStr), - gasPrice: common.Big(gasPriceStr), - value: common.Big(valueStr), - data: common.FromHex(dataStr), - } - if len(toStr) > 0 { - addr := common.HexToAddress(toStr) - msg.to = &addr - } - - if msg.gas.Cmp(big.NewInt(0)) == 0 { - msg.gas = big.NewInt(50000000) - } - - if msg.gasPrice.Cmp(big.NewInt(0)) == 0 { - msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) - } - - header := be.bc.CurrentBlock().Header() - vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{}) - gp := new(core.GasPool).AddGas(common.MaxBig) - res, gas, err := core.ApplyMessage(vmenv, msg, gp) - - return common.ToHex(res), gas.String(), err -} - -// StorageAt returns the data stores in the state for the given address and location. -func (be *registryAPIBackend) StorageAt(addr string, storageAddr string) string { - block := be.bc.CurrentBlock() - state, err := state.New(block.Root(), be.chainDb) - if err != nil { - return "" - } - return state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex() -} - -// Transact forms a transaction from the given arguments and submits it to the -// transactio pool for execution. -func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { - if len(toStr) > 0 && toStr != "0x" && !common.IsHexAddress(toStr) { - return "", errors.New("invalid address") - } - - var ( - from = common.HexToAddress(fromStr) - to = common.HexToAddress(toStr) - value = common.Big(valueStr) - gas *big.Int - price *big.Int - data []byte - contractCreation bool - ) - - if len(gasStr) == 0 { - gas = big.NewInt(90000) - } else { - gas = common.Big(gasStr) - } - - if len(gasPriceStr) == 0 { - price = big.NewInt(10000000000000) - } else { - price = common.Big(gasPriceStr) - } - - data = common.FromHex(codeStr) - if len(toStr) == 0 { - contractCreation = true - } - - nonce := be.txPool.State().GetNonce(from) - if len(nonceStr) != 0 { - nonce = common.Big(nonceStr).Uint64() - } - - var tx *types.Transaction - if contractCreation { - tx = types.NewContractCreation(nonce, value, gas, price, data) - } else { - tx = types.NewTransaction(nonce, to, value, gas, price, data) - } - - signature, err := be.am.SignEthereum(from, tx.SigHash().Bytes()) - if err != nil { - return "", err - } - signedTx, err := tx.WithSignature(signature) - if err != nil { - return "", err - } - - be.txPool.SetLocal(signedTx) - if err := be.txPool.Add(signedTx); err != nil { - return "", nil - } - - if contractCreation { - addr := crypto.CreateAddress(from, nonce) - glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) - } else { - glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex()) - } - - return signedTx.Hash().Hex(), nil -} diff --git a/common/registrar/registrar.go b/common/registrar/registrar.go deleted file mode 100644 index 0606f6985f..0000000000 --- a/common/registrar/registrar.go +++ /dev/null @@ -1,436 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package registrar - -import ( - "encoding/binary" - "fmt" - "math/big" - "regexp" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" -) - -/* -Registrar implements the Ethereum name registrar services mapping -- arbitrary strings to ethereum addresses -- hashes to hashes -- hashes to arbitrary strings -(likely will provide lookup service for all three) - -The Registrar is used by -* the roundtripper transport implementation of -url schemes to resolve domain names and services that register these names -* contract info retrieval (NatSpec). - -The Registrar uses 3 contracts on the blockchain: -* GlobalRegistrar: Name (string) -> Address (Owner) -* HashReg : Key Hash (hash of domain name or contract code) -> Content Hash -* UrlHint : Content Hash -> Url Hint - -These contracts are (currently) not included in the genesis block. -Each Set needs to be called once on each blockchain/network once. - -Contract addresses need to be set the first time any Registrar method is called -in a client session. -This is done for frontier by default, otherwise the caller needs to make sure -the relevant environment initialised the desired contracts -*/ -var ( - // GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" // olympic - GlobalRegistrarAddr = "0x33990122638b9132ca29c723bdf037f1a891a70c" // frontier - HashRegAddr = "0x23bf622b5a65f6060d855fca401133ded3520620" // frontier - UrlHintAddr = "0x73ed5ef6c010727dfd2671dbb70faac19ec18626" // frontier - - zero = regexp.MustCompile("^(0x)?0*$") -) - -const ( - trueHex = "0000000000000000000000000000000000000000000000000000000000000001" - falseHex = "0000000000000000000000000000000000000000000000000000000000000000" -) - -func abiSignature(s string) string { - return common.ToHex(crypto.Keccak256([]byte(s))[:4]) -} - -var ( - HashRegName = "HashReg" - UrlHintName = "UrlHint" - - registerContentHashAbi = abiSignature("register(uint256,uint256)") - registerUrlAbi = abiSignature("register(uint256,uint8,uint256)") - setOwnerAbi = abiSignature("setowner()") - reserveAbi = abiSignature("reserve(bytes32)") - resolveAbi = abiSignature("addr(bytes32)") - registerAbi = abiSignature("setAddress(bytes32,address,bool)") - addressAbiPrefix = falseHex[:24] -) - -// Registrar's backend is defined as an interface (implemented by xeth, but could be remote) -type Backend interface { - StorageAt(string, string) string - Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) - Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) -} - -// TODO Registrar should also just implement The Resolver and Registry interfaces -// Simplify for now. -type VersionedRegistrar interface { - Resolver(*big.Int) *Registrar - Registry() *Registrar -} - -type Registrar struct { - backend Backend -} - -func New(b Backend) (res *Registrar) { - res = &Registrar{b} - return -} - -func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (txhash string, err error) { - if namereg != "" { - GlobalRegistrarAddr = namereg - return - } - if zero.MatchString(GlobalRegistrarAddr) { - if (addr == common.Address{}) { - err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given") - return - } else { - txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode) - if err != nil { - err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err) - return - } - } - } - return -} - -func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (txhash string, err error) { - if hashreg != "" { - HashRegAddr = hashreg - } else { - if !zero.MatchString(HashRegAddr) { - return - } - nameHex, extra := encodeName(HashRegName, 2) - hashRegAbi := resolveAbi + nameHex + extra - glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi) - var res string - res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi) - if len(res) >= 40 { - HashRegAddr = "0x" + res[len(res)-40:len(res)] - } - if err != nil || zero.MatchString(HashRegAddr) { - if (addr == common.Address{}) { - err = fmt.Errorf("HashReg address not found and sender for creation not given") - return - } - - txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", HashRegCode) - if err != nil { - err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err) - } - glog.V(logger.Detail).Infof("created HashRegAddr @ txhash %v\n", txhash) - } else { - glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr) - return - } - } - - return -} - -func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, err error) { - if urlhint != "" { - UrlHintAddr = urlhint - } else { - if !zero.MatchString(UrlHintAddr) { - return - } - nameHex, extra := encodeName(UrlHintName, 2) - urlHintAbi := resolveAbi + nameHex + extra - glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr) - var res string - res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi) - if len(res) >= 40 { - UrlHintAddr = "0x" + res[len(res)-40:len(res)] - } - if err != nil || zero.MatchString(UrlHintAddr) { - if (addr == common.Address{}) { - err = fmt.Errorf("UrlHint address not found and sender for creation not given") - return - } - txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode) - if err != nil { - err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err) - } - glog.V(logger.Detail).Infof("created UrlHint @ txhash %v\n", txhash) - } else { - glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr) - return - } - } - - return -} - -// ReserveName(from, name) reserves name for the sender address in the globalRegistrar -// the tx needs to be mined to take effect -func (self *Registrar) ReserveName(address common.Address, name string) (txh string, err error) { - if zero.MatchString(GlobalRegistrarAddr) { - return "", fmt.Errorf("GlobalRegistrar address is not set") - } - nameHex, extra := encodeName(name, 2) - abi := reserveAbi + nameHex + extra - glog.V(logger.Detail).Infof("Reserve data: %s", abi) - return self.backend.Transact( - address.Hex(), - GlobalRegistrarAddr, - "", "", "", "", - abi, - ) -} - -// SetAddressToName(from, name, addr) will set the Address to address for name -// in the globalRegistrar using from as the sender of the transaction -// the tx needs to be mined to take effect -func (self *Registrar) SetAddressToName(from common.Address, name string, address common.Address) (txh string, err error) { - if zero.MatchString(GlobalRegistrarAddr) { - return "", fmt.Errorf("GlobalRegistrar address is not set") - } - - nameHex, extra := encodeName(name, 6) - addrHex := encodeAddress(address) - - abi := registerAbi + nameHex + addrHex + trueHex + extra - glog.V(logger.Detail).Infof("SetAddressToName data: %s to %s ", abi, GlobalRegistrarAddr) - - return self.backend.Transact( - from.Hex(), - GlobalRegistrarAddr, - "", "", "", "", - abi, - ) -} - -// NameToAddr(from, name) queries the registrar for the address on name -func (self *Registrar) NameToAddr(from common.Address, name string) (address common.Address, err error) { - if zero.MatchString(GlobalRegistrarAddr) { - return address, fmt.Errorf("GlobalRegistrar address is not set") - } - - nameHex, extra := encodeName(name, 2) - abi := resolveAbi + nameHex + extra - glog.V(logger.Detail).Infof("NameToAddr data: %s", abi) - res, _, err := self.backend.Call( - from.Hex(), - GlobalRegistrarAddr, - "", "", "", - abi, - ) - if err != nil { - return - } - address = common.HexToAddress(res) - return -} - -// called as first step in the registration process on HashReg -func (self *Registrar) SetOwner(address common.Address) (txh string, err error) { - if zero.MatchString(HashRegAddr) { - return "", fmt.Errorf("HashReg address is not set") - } - return self.backend.Transact( - address.Hex(), - HashRegAddr, - "", "", "", "", - setOwnerAbi, - ) -} - -// registers some content hash to a key/code hash -// e.g., the contract Info combined Json Doc's ContentHash -// to CodeHash of a contract or hash of a domain -func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) { - if zero.MatchString(HashRegAddr) { - return "", fmt.Errorf("HashReg address is not set") - } - - _, err = self.SetOwner(address) - if err != nil { - return - } - codehex := common.Bytes2Hex(codehash[:]) - dochex := common.Bytes2Hex(dochash[:]) - - data := registerContentHashAbi + codehex + dochex - glog.V(logger.Detail).Infof("SetHashToHash data: %s sent to %v\n", data, HashRegAddr) - return self.backend.Transact( - address.Hex(), - HashRegAddr, - "", "", "", "", - data, - ) -} - -// SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched -// address is used as sender for the transaction and will be the owner of a new -// registry entry on first time use -// FIXME: silently doing nothing if sender is not the owner -// note that with content addressed storage, this step is no longer necessary -func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) { - if zero.MatchString(UrlHintAddr) { - return "", fmt.Errorf("UrlHint address is not set") - } - - hashHex := common.Bytes2Hex(hash[:]) - var urlHex string - urlb := []byte(url) - var cnt byte - n := len(urlb) - - for n > 0 { - if n > 32 { - n = 32 - } - urlHex = common.Bytes2Hex(urlb[:n]) - urlb = urlb[n:] - n = len(urlb) - bcnt := make([]byte, 32) - bcnt[31] = cnt - data := registerUrlAbi + - hashHex + - common.Bytes2Hex(bcnt) + - common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32)) - txh, err = self.backend.Transact( - address.Hex(), - UrlHintAddr, - "", "", "", "", - data, - ) - if err != nil { - return - } - cnt++ - } - return -} - -// HashToHash(key) resolves contenthash for key (a hash) using HashReg -// resolution is costless non-transactional -// implemented as direct retrieval from db -func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) { - if zero.MatchString(HashRegAddr) { - return common.Hash{}, fmt.Errorf("HashReg address is not set") - } - - // look up in hashReg - at := HashRegAddr[2:] - key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:])) - hash := self.backend.StorageAt(at, key) - - if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) { - err = fmt.Errorf("HashToHash: content hash not found for '%v'", khash.Hex()) - return - } - copy(chash[:], common.Hex2BytesFixed(hash[2:], 32)) - return -} - -// HashToUrl(contenthash) resolves the url for contenthash using UrlHint -// resolution is costless non-transactional -// implemented as direct retrieval from db -// if we use content addressed storage, this step is no longer necessary -func (self *Registrar) HashToUrl(chash common.Hash) (uri string, err error) { - if zero.MatchString(UrlHintAddr) { - return "", fmt.Errorf("UrlHint address is not set") - } - // look up in URL reg - var str string = " " - var idx uint32 - for len(str) > 0 { - mapaddr := storageMapping(storageIdx2Addr(1), chash[:]) - key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx))) - hex := self.backend.StorageAt(UrlHintAddr[2:], key) - str = string(common.Hex2Bytes(hex[2:])) - l := 0 - for (l < len(str)) && (str[l] == 0) { - l++ - } - - str = str[l:] - uri = uri + str - idx++ - } - - if len(uri) == 0 { - err = fmt.Errorf("HashToUrl: URL hint not found for '%v'", chash.Hex()) - } - return -} - -func storageIdx2Addr(varidx uint32) []byte { - data := make([]byte, 32) - binary.BigEndian.PutUint32(data[28:32], varidx) - return data -} - -func storageMapping(addr, key []byte) []byte { - data := make([]byte, 64) - copy(data[0:32], key[0:32]) - copy(data[32:64], addr[0:32]) - sha := crypto.Keccak256(data) - return sha -} - -func storageFixedArray(addr, idx []byte) []byte { - var carry byte - for i := 31; i >= 0; i-- { - var b byte = addr[i] + idx[i] + carry - if b < addr[i] { - carry = 1 - } else { - carry = 0 - } - addr[i] = b - } - return addr -} - -func storageAddress(addr []byte) string { - return common.ToHex(addr) -} - -func encodeAddress(address common.Address) string { - return addressAbiPrefix + address.Hex()[2:] -} - -func encodeName(name string, index uint8) (string, string) { - extra := common.Bytes2Hex([]byte(name)) - if len(name) > 32 { - return fmt.Sprintf("%064x", index), extra - } - return extra + falseHex[len(extra):], "" -} diff --git a/common/registrar/registrar_test.go b/common/registrar/registrar_test.go deleted file mode 100644 index b2287803c2..0000000000 --- a/common/registrar/registrar_test.go +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package registrar - -import ( - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -type testBackend struct { - // contracts mock - contracts map[string](map[string]string) -} - -var ( - text = "test" - codehash = common.StringToHash("1234") - hash = common.BytesToHash(crypto.Keccak256([]byte(text))) - url = "bzz://bzzhash/my/path/contr.act" -) - -func NewTestBackend() *testBackend { - self := &testBackend{} - self.contracts = make(map[string](map[string]string)) - return self -} - -func (self *testBackend) initHashReg() { - self.contracts[HashRegAddr[2:]] = make(map[string]string) - key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:])) - self.contracts[HashRegAddr[2:]][key] = hash.Hex() -} - -func (self *testBackend) initUrlHint() { - self.contracts[UrlHintAddr[2:]] = make(map[string]string) - mapaddr := storageMapping(storageIdx2Addr(1), hash[:]) - - key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0))) - self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url)) - key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1))) - self.contracts[UrlHintAddr[2:]][key] = "0x0" -} - -func (self *testBackend) StorageAt(ca, sa string) (res string) { - c := self.contracts[ca] - if c == nil { - return "0x0" - } - res = c[sa] - return -} - -func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) { - return "", nil -} - -func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) { - return "", "", nil -} - -func TestSetGlobalRegistrar(t *testing.T) { - b := NewTestBackend() - res := New(b) - _, err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1)) - if err != nil { - t.Errorf("unexpected error: %v'", err) - } -} - -func TestHashToHash(t *testing.T) { - b := NewTestBackend() - res := New(b) - - HashRegAddr = "0x0" - got, err := res.HashToHash(codehash) - if err == nil { - t.Errorf("expected error") - } else { - exp := "HashReg address is not set" - if err.Error() != exp { - t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error()) - } - } - - HashRegAddr = common.BigToAddress(common.Big1).Hex() //[2:] - got, err = res.HashToHash(codehash) - if err == nil { - t.Errorf("expected error") - } else { - exp := "HashToHash: content hash not found for '" + codehash.Hex() + "'" - if err.Error() != exp { - t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error()) - } - } - - b.initHashReg() - got, err = res.HashToHash(codehash) - if err != nil { - t.Errorf("expected no error, got %v", err) - } else { - if got != hash { - t.Errorf("incorrect result, expected '%v', got '%v'", hash.Hex(), got.Hex()) - } - } -} - -func TestHashToUrl(t *testing.T) { - b := NewTestBackend() - res := New(b) - - UrlHintAddr = "0x0" - got, err := res.HashToUrl(hash) - if err == nil { - t.Errorf("expected error") - } else { - exp := "UrlHint address is not set" - if err.Error() != exp { - t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error()) - } - } - - UrlHintAddr = common.BigToAddress(common.Big2).Hex() //[2:] - got, err = res.HashToUrl(hash) - if err == nil { - t.Errorf("expected error") - } else { - exp := "HashToUrl: URL hint not found for '" + hash.Hex() + "'" - if err.Error() != exp { - t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error()) - } - } - - b.initUrlHint() - got, err = res.HashToUrl(hash) - if err != nil { - t.Errorf("expected no error, got %v", err) - } else { - if got != url { - t.Errorf("incorrect result, expected '%v', got '%s'", url, got) - } - } -} diff --git a/contracts/release/release.go b/contracts/release/release.go index cd79112cd1..346005e903 100644 --- a/contracts/release/release.go +++ b/contracts/release/release.go @@ -27,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -62,20 +61,20 @@ type ReleaseService struct { // releases and notify the user of such. func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { // Retrieve the Ethereum service dependency to access the blockchain - var apiBackend ethapi.Backend + var apiBackend bind.ContractBackend var ethereum *eth.Ethereum if err := ctx.Service(ðereum); err == nil { - apiBackend = ethereum.ApiBackend + apiBackend = ethereum } else { var ethereum *les.LightEthereum if err := ctx.Service(ðereum); err == nil { - apiBackend = ethereum.ApiBackend + apiBackend = ethereum } else { return nil, err } } // Construct the release service - contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(apiBackend)) + contract, err := NewReleaseOracle(config.Oracle, apiBackend) if err != nil { return nil, err } diff --git a/core/database_util.go b/core/database_util.go index 0fb593554b..a37013015b 100644 --- a/core/database_util.go +++ b/core/database_util.go @@ -82,7 +82,7 @@ func GetCanonicalHash(db ethdb.Database, number uint64) common.Hash { // missingNumber is returned by GetBlockNumber if no header with the // given block hash has been stored in the database -const missingNumber = uint64(0xffffffffffffffff) +const MissingNumber = uint64(0xffffffffffffffff) // GetBlockNumber returns the block number assigned to a block hash // if the corresponding header is present in the database @@ -91,7 +91,7 @@ func GetBlockNumber(db ethdb.Database, hash common.Hash) uint64 { if len(data) != 8 { data, _ := db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldHeaderSuffix...)) if len(data) == 0 { - return missingNumber + return MissingNumber } header := new(types.Header) if err := rlp.Decode(bytes.NewReader(data), header); err != nil { @@ -247,7 +247,7 @@ func GetBlockReceipts(db ethdb.Database, hash common.Hash, number uint64) types. // GetTransaction retrieves a specific transaction from the database, along with // its added positional metadata. -func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { +func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, int) { // Retrieve the transaction itself from the database data, _ := db.Get(hash.Bytes()) if len(data) == 0 { @@ -270,7 +270,7 @@ func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, co if err := rlp.DecodeBytes(data, &meta); err != nil { return nil, common.Hash{}, 0, 0 } - return &tx, meta.BlockHash, meta.BlockIndex, meta.Index + return &tx, meta.BlockHash, meta.BlockIndex, int(meta.Index) } // GetReceipt returns a receipt by hash diff --git a/core/database_util_test.go b/core/database_util_test.go index ab14ae156e..c351279b90 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -368,7 +368,7 @@ func TestTransactionStorage(t *testing.T) { if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil { t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash()) } else { - if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) { + if hash != block.Hash() || number != block.NumberU64() || index != i { t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i) } if tx.String() != txn.String() { diff --git a/core/headerchain.go b/core/headerchain.go index 8ca06d9b48..838c035505 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -123,7 +123,7 @@ func (hc *HeaderChain) GetBlockNumber(hash common.Hash) uint64 { return cached.(uint64) } number := GetBlockNumber(hc.chainDb, hash) - if number != missingNumber { + if number != MissingNumber { hc.numberCache.Add(hash, number) } return number diff --git a/core/helper_test.go b/core/helper_test.go deleted file mode 100644 index fd6a5491c5..0000000000 --- a/core/helper_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "container/list" - "fmt" - - "github.com/ethereum/go-ethereum/core/types" - // "github.com/ethereum/go-ethereum/crypto" - - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" -) - -// Implement our EthTest Manager -type TestManager struct { - // stateManager *StateManager - eventMux *event.TypeMux - - db ethdb.Database - txPool *TxPool - blockChain *BlockChain - Blocks []*types.Block -} - -func (s *TestManager) IsListening() bool { - return false -} - -func (s *TestManager) IsMining() bool { - return false -} - -func (s *TestManager) PeerCount() int { - return 0 -} - -func (s *TestManager) Peers() *list.List { - return list.New() -} - -func (s *TestManager) BlockChain() *BlockChain { - return s.blockChain -} - -func (tm *TestManager) TxPool() *TxPool { - return tm.txPool -} - -// func (tm *TestManager) StateManager() *StateManager { -// return tm.stateManager -// } - -func (tm *TestManager) EventMux() *event.TypeMux { - return tm.eventMux -} - -// func (tm *TestManager) KeyManager() *crypto.KeyManager { -// return nil -// } - -func (tm *TestManager) Db() ethdb.Database { - return tm.db -} - -func NewTestManager() *TestManager { - db, err := ethdb.NewMemDatabase() - if err != nil { - fmt.Println("Could not create mem-db, failing") - return nil - } - - testManager := &TestManager{} - testManager.eventMux = new(event.TypeMux) - testManager.db = db - // testManager.txPool = NewTxPool(testManager) - // testManager.blockChain = NewBlockChain(testManager) - // testManager.stateManager = NewStateManager(testManager) - - return testManager -} diff --git a/core/state_transition.go b/core/state_transition.go index 9e6b2f567e..b7b2bf7911 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -20,6 +20,7 @@ import ( "fmt" "math/big" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/logger" @@ -134,6 +135,39 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In return ret, gasUsed, err } +// ApplyCallMessage runs the given message as a read-only contract call. +// +// It modifies the environment to provide a near infinite amount of ether to the sender +// account and runs the message with the amount of gas configured in the message. This +// function is meant to be used outside of regular block processing. +// +// It returns the bytes returned by any EVM execution (if it took place), the gas used +// (not including gas refunds) and an error if it failed. An error always indicates a core +// error meaning that the message would always fail for that particular state and would +// never be accepted within a block. +func ApplyCallMessage(call ethereum.CallMsg, newEnv func(Message) vm.Environment) ([]byte, *big.Int, error) { + // Ensure message is initialized properly. + if call.GasPrice == nil { + call.GasPrice = big.NewInt(1) + } + if call.Gas == nil || call.Gas.BitLen() == 0 { + call.Gas = big.NewInt(50000000) + } + if call.Value == nil { + call.Value = new(big.Int) + } + msg := callmsg{call} + env := newEnv(msg) + // Provide ether to the caller account. + have := env.Db().GetBalance(call.From) + need := new(big.Int).Sub(common.MaxBig, have) + env.Db().AddBalance(call.From, need) + // Execute the call. + gaspool := new(GasPool).AddGas(call.Gas) + ret, gasUsed, _, err := NewStateTransition(env, msg, gaspool).TransitionDb() + return ret, gasUsed, err +} + func (self *StateTransition) from() (vm.Account, error) { var ( f common.Address @@ -300,3 +334,18 @@ func (self *StateTransition) refundGas() { func (self *StateTransition) gasUsed() *big.Int { return new(big.Int).Sub(self.initialGas, self.gas) } + +// callmsg wraps ethereum.CallMsg so it implements Message. +type callmsg struct { + ethereum.CallMsg +} + +func (m callmsg) From() (common.Address, error) { return m.CallMsg.From, nil } +func (m callmsg) FromFrontier() (common.Address, error) { return m.CallMsg.From, nil } +func (m callmsg) Nonce() uint64 { return 0 } +func (m callmsg) CheckNonce() bool { return false } +func (m callmsg) To() *common.Address { return m.CallMsg.To } +func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas } +func (m callmsg) Value() *big.Int { return m.CallMsg.Value } +func (m callmsg) Data() []byte { return m.CallMsg.Data } diff --git a/core/tx_pool.go b/core/tx_pool.go index 419d9945e7..de411347d8 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -190,11 +190,12 @@ func (pool *TxPool) Stop() { glog.V(logger.Info).Infoln("Transaction pool stopped") } -func (pool *TxPool) State() *state.ManagedState { - pool.mu.RLock() - defer pool.mu.RUnlock() +// NonceAt returns the next valid nonce for the given account. +func (pool *TxPool) NonceAt(addr common.Address) uint64 { + pool.mu.Lock() + defer pool.mu.Unlock() - return pool.pendingState + return pool.pendingState.GetNonce(addr) } // Stats retrieves the current pool stats, namely the number of pending and the diff --git a/eth/api.go b/eth/api.go index 48f512b1b4..e83b772fcc 100644 --- a/eth/api.go +++ b/eth/api.go @@ -521,7 +521,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. data: tx.Data(), } // Mutate the state if we haven't reached the tracing transaction yet - if uint64(idx) < txIndex { + if idx < txIndex { vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{}) _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { diff --git a/eth/api_backend.go b/eth/api_backend.go index efe9a7a011..145ea09851 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -17,189 +17,299 @@ package eth import ( + "errors" + "fmt" "math/big" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" - rpc "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) -// EthApiBackend implements ethapi.Backend for full nodes -type EthApiBackend struct { - eth *Ethereum - gpo *gasprice.GasPriceOracle -} +// Assert at compile time that this implementation has all optional API features. +var ( + _ ethapi.TransactionInclusionBlock = (*Ethereum)(nil) + _ ethapi.PendingState = (*Ethereum)(nil) + _ bind.ContractBackend = (*Ethereum)(nil) + _ bind.PendingContractCaller = (*Ethereum)(nil) +) -func (b *EthApiBackend) SetHead(number uint64) { - b.eth.blockchain.SetHead(number) -} +var ( + ErrBlockNotFound = errors.New("block not found") + ErrTxNotFound = errors.New("transaction not found") +) -func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { - // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, _ := b.eth.miner.Pending() - return block.Header(), nil +// HeaderByNumber returns headers from the canonical chain. +func (eth *Ethereum) HeaderByNumber(ctx context.Context, num *big.Int) (*types.Header, error) { + if num == nil { + return eth.blockchain.CurrentBlock().Header(), nil } - // Otherwise resolve and return the block - if blockNr == rpc.LatestBlockNumber { - return b.eth.blockchain.CurrentBlock().Header(), nil + if h := eth.blockchain.GetHeaderByNumber(num.Uint64()); h != nil { + return h, nil } - return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)), nil + return nil, ErrBlockNotFound } -func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { - // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, _ := b.eth.miner.Pending() - return block, nil +// HeaderByHash returns the header with the given hash. +func (eth *Ethereum) HeaderByHash(ctx context.Context, blockhash common.Hash) (*types.Header, error) { + if h := eth.blockchain.GetHeaderByHash(blockhash); h != nil { + return h, nil } - // Otherwise resolve and return the block - if blockNr == rpc.LatestBlockNumber { - return b.eth.blockchain.CurrentBlock(), nil + return nil, ErrBlockNotFound +} + +// BlockByNumber returns blocks from the canonical chain. +func (eth *Ethereum) BlockByNumber(ctx context.Context, num *big.Int) (*types.Block, error) { + if num == nil { + return eth.blockchain.CurrentBlock(), nil } - return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil -} - -func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) { - // Pending state is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, state := b.eth.miner.Pending() - return EthApiState{state}, block.Header(), nil + if b := eth.blockchain.GetBlockByNumber(num.Uint64()); b != nil { + return b, nil } - // Otherwise resolve the block number and return its state - header, err := b.HeaderByNumber(ctx, blockNr) - if header == nil || err != nil { - return nil, nil, err + return nil, ErrBlockNotFound +} + +// BlockByHash returns the block with the given hash. +func (eth *Ethereum) BlockByHash(ctx context.Context, blockhash common.Hash) (*types.Block, error) { + if b := eth.blockchain.GetBlockByHash(blockhash); b != nil { + return b, nil } - stateDb, err := b.eth.BlockChain().StateAt(header.Root) - return EthApiState{stateDb}, header, err + return nil, ErrBlockNotFound } -func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { - return b.eth.blockchain.GetBlockByHash(blockHash), nil +// BlockReceipts returns all receipts contained in the given block. +func (eth *Ethereum) BlockReceipts(ctx context.Context, blockhash common.Hash, number uint64) ([]*types.Receipt, error) { + r := core.GetBlockReceipts(eth.chainDb, blockhash, number) + if r == nil { + return nil, errors.New("database has no valid receipts for the given block") + } + return r, nil } -func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { - return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil +// TransactionCount returns the number of transactions in a block. +func (eth *Ethereum) TransactionCount(ctx context.Context, blockhash common.Hash) (uint, error) { + b := eth.blockchain.GetBlockByHash(blockhash) + if b == nil { + return 0, nil + } + return uint(len(b.Transactions())), nil } -func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { - return b.eth.blockchain.GetTdByHash(blockHash) +// TransactionInBlock returns the i'th transaction in the given block. +func (eth *Ethereum) TransactionInBlock(ctx context.Context, blockhash common.Hash, i uint) (*types.Transaction, error) { + b := eth.blockchain.GetBlockByHash(blockhash) + if b == nil { + return nil, fmt.Errorf("transaction index %d out of range for non-existent block", i) + } + if i >= uint(len(b.Transactions())) { + return nil, fmt.Errorf("transaction index %d out of range", i) + } + return b.Transactions()[i], nil } -func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { - statedb := state.(EthApiState).state - addr, _ := msg.From() - from := statedb.GetOrNewStateObject(addr) - from.SetBalance(common.MaxBig) - vmError := func() error { return nil } - return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil +// TransactionByHash returns the transaction with the given hash. +func (eth *Ethereum) TransactionByHash(ctx context.Context, txhash common.Hash) (tx *types.Transaction, isPending bool, err error) { + if tx = eth.txPool.Get(txhash); tx != nil { + return tx, true, nil + } + if tx, _, _, _ = core.GetTransaction(eth.chainDb, txhash); tx != nil { + return tx, false, nil + } + return nil, false, ErrTxNotFound } -func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() - - b.eth.txPool.SetLocal(signedTx) - return b.eth.txPool.Add(signedTx) +// TransactionInclusionBlock returns the block in which the given transaction was included. +func (eth *Ethereum) TransactionInclusionBlock(txhash common.Hash) (bhash common.Hash, bnum uint64, index int, err error) { + var tx *types.Transaction + if tx, bhash, bnum, index = core.GetTransaction(eth.chainDb, txhash); tx == nil { + err = ErrTxNotFound + } + return bhash, bnum, index, err } -func (b *EthApiBackend) RemoveTx(txHash common.Hash) { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() - - b.eth.txPool.Remove(txHash) +// TransactionReceipt returns the receipt of a transaction. +func (eth *Ethereum) TransactionReceipt(ctx context.Context, txhash common.Hash) (*types.Receipt, error) { + r := core.GetReceipt(eth.chainDb, txhash) + if r == nil { + return nil, ErrTxNotFound + } + return r, nil } -func (b *EthApiBackend) GetPoolTransactions() types.Transactions { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() +// BlockTD returns the total difficulty of a certain block. +func (eth *Ethereum) BlockTD(blockhash common.Hash) *big.Int { + return eth.blockchain.GetTdByHash(blockhash) +} + +// BalanceAt returns the balance of the given account. +func (eth *Ethereum) BalanceAt(ctx context.Context, addr common.Address, block *big.Int) (bal *big.Int, err error) { + err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { bal = st.GetBalance(addr) }) + return bal, err +} + +// CodeAt returns the code of the given account. +func (eth *Ethereum) CodeAt(ctx context.Context, addr common.Address, block *big.Int) (code []byte, err error) { + err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { code = st.GetCode(addr) }) + return code, err +} + +// NonceAt returns the nonce of the given account. +func (eth *Ethereum) NonceAt(ctx context.Context, addr common.Address, block *big.Int) (nonce uint64, err error) { + err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { nonce = st.GetNonce(addr) }) + return nonce, err +} + +// StorageAt returns a storage value of the given account. +func (eth *Ethereum) StorageAt(ctx context.Context, addr common.Address, key common.Hash, block *big.Int) (val []byte, err error) { + err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { v := st.GetState(addr, key); val = v[:] }) + return val, err +} + +// PendingBalanceAt returns the balance of the given account in the pending state. +func (eth *Ethereum) PendingBalanceAt(ctx context.Context, addr common.Address) (bal *big.Int, err error) { + err = eth.withStateAt(ctx, nil, true, func(st *state.StateDB) { bal = st.GetBalance(addr) }) + return bal, err +} + +// PendingBalanceAt returns the code of the given account in the pending state. +func (eth *Ethereum) PendingCodeAt(ctx context.Context, addr common.Address) (code []byte, err error) { + err = eth.withStateAt(ctx, nil, true, func(st *state.StateDB) { code = st.GetCode(addr) }) + return code, err +} + +// PendingBalanceAt returns a storage value of the given account in the pending state. +func (eth *Ethereum) PendingStorageAt(ctx context.Context, addr common.Address, key common.Hash) (val []byte, err error) { + err = eth.withStateAt(ctx, nil, true, func(st *state.StateDB) { v := st.GetState(addr, key); val = v[:] }) + return val, err +} + +// PendingTransaction count returns the number of transactions in the pending block. +func (eth *Ethereum) PendingTransactionCount(ctx context.Context) (uint, error) { + b, _ := eth.miner.Pending() + return uint(len(b.Transactions())), nil +} + +func (eth *Ethereum) withStateAt(ctx context.Context, block *big.Int, pending bool, fn func(st *state.StateDB)) error { + var st *state.StateDB + if pending { + _, st = eth.miner.Pending() + } else { + header, err := eth.HeaderByNumber(ctx, block) + if err != nil { + return err + } + st, err = eth.BlockChain().StateAt(header.Root) + if err != nil { + return err + } + } + fn(st) + return nil +} + +// PendingBlock returns the next block as envisioned by the pending state. +func (eth *Ethereum) PendingBlock() (*types.Block, error) { + b, _ := eth.miner.Pending() + return b, nil +} + +// PendingNonceAt returns the next valid nonce according to the local transaction pool. +func (eth *Ethereum) PendingNonceAt(ctx context.Context, addr common.Address) (uint64, error) { + return eth.txPool.NonceAt(addr), nil +} + +// PendingTransactions returns all known pending transactions. +func (eth *Ethereum) PendingTransactions() []*types.Transaction { + eth.txMu.Lock() + defer eth.txMu.Unlock() var txs types.Transactions - for _, batch := range b.eth.txPool.Pending() { + for _, batch := range eth.txPool.Pending() { txs = append(txs, batch...) } return txs } -func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() +// SendTransaction queues a transaction in the pool. +func (eth *Ethereum) SendTransaction(ctx context.Context, signedTx *types.Transaction) error { + eth.txMu.Lock() + defer eth.txMu.Unlock() - return b.eth.txPool.Get(hash) + eth.txPool.SetLocal(signedTx) + return eth.txPool.Add(signedTx) } -func (b *EthApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() - - return b.eth.txPool.State().GetNonce(addr), nil +// SyncProgress returns sync status information. +func (b *Ethereum) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { + return b.protocolManager.downloader.Progress(), nil } -func (b *EthApiBackend) Stats() (pending int, queued int) { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() - - return b.eth.txPool.Stats() +// SuggestGasPrice returns a suitable gas price based on the content of recently seen blocks. +func (eth *Ethereum) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + return eth.gpo.SuggestPrice(), nil } -func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() +// RemoveTransaction removes the given transaction from the local pool. +func (eth *Ethereum) RemoveTransaction(txHash common.Hash) { + eth.txMu.Lock() + defer eth.txMu.Unlock() - return b.eth.TxPool().Content() + eth.txPool.Remove(txHash) } -func (b *EthApiBackend) Downloader() *downloader.Downloader { - return b.eth.Downloader() +// ProtocolVersion returns the active protocol version. +func (eth *Ethereum) ProtocolVersion() int { + return int(eth.protocolManager.SubProtocols[0].Version) } -func (b *EthApiBackend) ProtocolVersion() int { - return b.eth.EthVersion() +// AccountManager returns the internal account manager that accesses the data directory. +// Deprecated: get the account manager through node.Node instead. +func (eth *Ethereum) AccountManager() *accounts.Manager { + return eth.accountManager } -func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(), nil +// ResetHeadBlock resets the blockchain to the given number. +// Use this method if you know what you're doing. +func (eth *Ethereum) ResetHeadBlock(blocknum uint64) { + eth.blockchain.SetHead(blocknum) } -func (b *EthApiBackend) ChainDb() ethdb.Database { - return b.eth.ChainDb() +func (eth *Ethereum) EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) { + block, state := eth.miner.Pending() + _, gas, err := eth.callContract(call, block.Header(), state) + return gas, err } -func (b *EthApiBackend) EventMux() *event.TypeMux { - return b.eth.EventMux() +func (eth *Ethereum) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { + block, state := eth.miner.Pending() + val, _, err := eth.callContract(call, block.Header(), state) + return val, err } -func (b *EthApiBackend) AccountManager() *accounts.Manager { - return b.eth.AccountManager() +func (eth *Ethereum) CallContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, error) { + var head *types.Header + if blocknum == nil { + head = eth.blockchain.CurrentHeader() + } else if head = eth.blockchain.GetHeaderByNumber(blocknum.Uint64()); head == nil { + return nil, ErrBlockNotFound + } + state, err := eth.blockchain.StateAt(head.Root) + if err != nil { + return nil, err + } + val, _, err := eth.callContract(call, head, state) + return val, err } -type EthApiState struct { - state *state.StateDB -} - -func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) { - return s.state.GetBalance(addr), nil -} - -func (s EthApiState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) { - return s.state.GetCode(addr), nil -} - -func (s EthApiState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) { - return s.state.GetState(a, b), nil -} - -func (s EthApiState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { - return s.state.GetNonce(addr), nil +func (eth *Ethereum) callContract(call ethereum.CallMsg, head *types.Header, state *state.StateDB) ([]byte, *big.Int, error) { + return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment { + return core.NewEnv(state, eth.chainConfig, eth.blockchain, msg, head, vm.Config{}) + }) } diff --git a/eth/backend.go b/eth/backend.go index ec501043a5..f14ba13f19 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -31,8 +31,6 @@ import ( "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/httpclient" - "github.com/ethereum/go-ethereum/common/registrar/ethreg" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -127,17 +125,15 @@ type Ethereum struct { eventMux *event.TypeMux pow *ethash.Ethash - httpclient *httpclient.HTTPClient accountManager *accounts.Manager - ApiBackend *EthApiBackend - miner *miner.Miner Mining bool MinerThreads int AutoDAG bool autodagquit chan bool etherbase common.Address + gpo *gasprice.GasPriceOracle solcPath string NatSpec bool @@ -173,7 +169,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { pow: pow, shutdownChan: make(chan bool), stopDbUpgrade: stopDbUpgrade, - httpclient: httpclient.New(config.DocRoot), netVersionId: config.NetworkId, NatSpec: config.NatSpec, PowTest: config.PowTest, @@ -222,14 +217,14 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { ForceJit: config.ForceJit, } - eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux()) + eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.eventMux) if err != nil { if err == core.ErrNoGenesis { return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`) } return nil, err } - newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) + newPool := core.NewTxPool(eth.chainConfig, eth.eventMux, eth.blockchain.State, eth.blockchain.GasLimit) eth.txPool = newPool maxPeers := config.MaxPeers @@ -246,10 +241,13 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { return nil, err } - eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow) + + // Set up the miner. + eth.miner = miner.New(eth, eth.chainConfig, eth.eventMux, eth.pow) eth.miner.SetGasPrice(config.GasPrice) eth.miner.SetExtra(config.ExtraData) + // Set up the gas price oracle. gpoParams := &gasprice.GpoParams{ GpoMinGasPrice: config.GpoMinGasPrice, GpoMaxGasPrice: config.GpoMaxGasPrice, @@ -258,8 +256,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { GpobaseStepUp: config.GpobaseStepUp, GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, } - gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) - eth.ApiBackend = &EthApiBackend{eth, gpo} + eth.gpo = gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) return eth, nil } @@ -313,55 +310,59 @@ func CreatePoW(config *Config) (*ethash.Ethash, error) { // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. -func (s *Ethereum) APIs() []rpc.API { - return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ +func (eth *Ethereum) APIs() []rpc.API { + return append(ethapi.GetAPIs(eth, eth.solcPath), []rpc.API{ { Namespace: "eth", Version: "1.0", - Service: NewPublicEthereumAPI(s), + Service: NewPublicEthereumAPI(eth), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: NewPublicMinerAPI(s), + Service: NewPublicMinerAPI(eth), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), + Service: downloader.NewPublicDownloaderAPI(eth.protocolManager.downloader, eth.eventMux), Public: true, }, { Namespace: "miner", Version: "1.0", - Service: NewPrivateMinerAPI(s), + Service: NewPrivateMinerAPI(eth), Public: false, }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, false), + Service: filters.NewPublicFilterAPI(eth, eth.eventMux, eth.chainDb, false), Public: true, }, { Namespace: "admin", Version: "1.0", - Service: NewPrivateAdminAPI(s), + Service: NewPrivateAdminAPI(eth), }, { Namespace: "debug", Version: "1.0", - Service: NewPublicDebugAPI(s), + Service: NewPublicDebugAPI(eth), Public: true, }, { Namespace: "debug", Version: "1.0", - Service: NewPrivateDebugAPI(s.chainConfig, s), + Service: ethapi.NewPrivateDebugAPI(eth, eth.chainDb), + }, { + Namespace: "debug", + Version: "1.0", + Service: NewPrivateDebugAPI(eth.chainConfig, eth), + }, { + Namespace: "txpool", + Version: "1.0", + Service: ethapi.TxPoolDebugAPI{Pool: eth.txPool}, }, { Namespace: "net", Version: "1.0", - Service: s.netRPCService, + Service: eth.netRPCService, Public: true, - }, { - Namespace: "admin", - Version: "1.0", - Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager), }, }...) } @@ -399,20 +400,16 @@ func (s *Ethereum) StartMining(threads int) error { return nil } -func (s *Ethereum) StopMining() { s.miner.Stop() } -func (s *Ethereum) IsMining() bool { return s.miner.Mining() } -func (s *Ethereum) Miner() *miner.Miner { return s.miner } +func (s *Ethereum) StopMining() { s.miner.Stop() } +func (s *Ethereum) IsMining() bool { return s.miner.Mining() } -func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } -func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } -func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } -func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } -func (s *Ethereum) Pow() *ethash.Ethash { return s.pow } -func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } -func (s *Ethereum) IsListening() bool { return true } // Always listening -func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } -func (s *Ethereum) NetVersion() int { return s.netVersionId } -func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } +// Deprecated Accessors + +func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } +func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } +func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } +func (s *Ethereum) Pow() *ethash.Ethash { return s.pow } +func (s *Ethereum) Miner() *miner.Miner { return s.miner } // Protocols implements node.Service, returning all the currently configured // network protocols to start. @@ -427,7 +424,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol { // Start implements node.Service, starting all internal goroutines needed by the // Ethereum protocol implementation. func (s *Ethereum) Start(srvr *p2p.Server) error { - s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) + s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.netVersionId) if s.AutoDAG { s.StartAutoDAG() } @@ -529,12 +526,6 @@ func (self *Ethereum) StopAutoDAG() { glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir) } -// HTTPClient returns the light http client used for fetching offchain docs -// (natspec, source for verification) -func (self *Ethereum) HTTPClient() *httpclient.HTTPClient { - return self.httpclient -} - // dagFiles(epoch) returns the two alternative DAG filenames (not a path) // 1) - 2) full-R- func dagFiles(epoch uint64) (string, string) { diff --git a/eth/bind.go b/eth/bind.go deleted file mode 100644 index 747965d370..0000000000 --- a/eth/bind.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package eth - -import ( - "math/big" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" -) - -// ContractBackend implements bind.ContractBackend with direct calls to Ethereum -// internals to support operating on contracts within subprotocols like eth and -// swarm. -// -// Internally this backend uses the already exposed API endpoints of the Ethereum -// object. These should be rewritten to internal Go method calls when the Go API -// is refactored to support a clean library use. -type ContractBackend struct { - eapi *ethapi.PublicEthereumAPI // Wrapper around the Ethereum object to access metadata - bcapi *ethapi.PublicBlockChainAPI // Wrapper around the blockchain to access chain data - txapi *ethapi.PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data -} - -// NewContractBackend creates a new native contract backend using an existing -// Etheruem object. -func NewContractBackend(apiBackend ethapi.Backend) *ContractBackend { - return &ContractBackend{ - eapi: ethapi.NewPublicEthereumAPI(apiBackend), - bcapi: ethapi.NewPublicBlockChainAPI(apiBackend), - txapi: ethapi.NewPublicTransactionPoolAPI(apiBackend), - } -} - -// CodeAt retrieves any code associated with the contract from the local API. -func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) { - out, err := b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum)) - return common.FromHex(out), err -} - -// CodeAt retrieves any code associated with the contract from the local API. -func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { - out, err := b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber) - return common.FromHex(out), err -} - -// ContractCall implements bind.ContractCaller executing an Ethereum contract -// call with the specified data as the input. The pending flag requests execution -// against the pending block, not the stable head of the chain. -func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) { - out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum)) - return common.FromHex(out), err -} - -// ContractCall implements bind.ContractCaller executing an Ethereum contract -// call with the specified data as the input. The pending flag requests execution -// against the pending block, not the stable head of the chain. -func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { - out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber) - return common.FromHex(out), err -} - -func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs { - args := ethapi.CallArgs{ - To: msg.To, - From: msg.From, - Data: common.ToHex(msg.Data), - } - if msg.Gas != nil { - args.Gas = *rpc.NewHexNumber(msg.Gas) - } - if msg.GasPrice != nil { - args.GasPrice = *rpc.NewHexNumber(msg.GasPrice) - } - if msg.Value != nil { - args.Value = *rpc.NewHexNumber(msg.Value) - } - return args -} - -func toBlockNumber(num *big.Int) rpc.BlockNumber { - if num == nil { - return rpc.LatestBlockNumber - } - return rpc.BlockNumber(num.Int64()) -} - -// PendingAccountNonce implements bind.ContractTransactor retrieving the current -// pending nonce associated with an account. -func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { - out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber) - return out.Uint64(), err -} - -// SuggestGasPrice implements bind.ContractTransactor retrieving the currently -// suggested gas price to allow a timely execution of a transaction. -func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - return b.eapi.GasPrice(ctx) -} - -// EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas -// needed to execute a specific transaction based on the current pending state of -// the backend blockchain. There is no guarantee that this is the true gas limit -// requirement as other transactions may be added or removed by miners, but it -// should provide a basis for setting a reasonable default. -func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) { - out, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg)) - return out.BigInt(), err -} - -// SendTransaction implements bind.ContractTransactor injects the transaction -// into the pending pool for execution. -func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { - raw, _ := rlp.EncodeToBytes(tx) - _, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw)) - return err -} diff --git a/eth/downloader/api.go b/eth/downloader/api.go index e41376810f..672f6124c1 100644 --- a/eth/downloader/api.go +++ b/eth/downloader/api.go @@ -76,7 +76,7 @@ func (api *PublicDownloaderAPI) eventLoop() { case StartEvent: notification = &SyncingResult{ Syncing: true, - Status: api.d.Progress(), + Status: *api.d.Progress(), } case DoneEvent, FailedEvent: notification = false diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index b1f4b8169b..5cd7fb2491 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -212,7 +212,7 @@ func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, hasHeader he // In addition, during the state download phase of fast synchronisation the number // of processed and the total number of known states are also returned. Otherwise // these are zero. -func (d *Downloader) Progress() ethereum.SyncProgress { +func (d *Downloader) Progress() *ethereum.SyncProgress { // Fetch the pending state count outside of the lock to prevent unforeseen deadlocks pendingStates := uint64(d.queue.PendingNodeData()) @@ -229,7 +229,7 @@ func (d *Downloader) Progress() ethereum.SyncProgress { case LightSync: current = d.headHeader().Number.Uint64() } - return ethereum.SyncProgress{ + return ðereum.SyncProgress{ StartingBlock: d.syncStatsChainOrigin, CurrentBlock: current, HighestBlock: d.syncStatsChainHeight, diff --git a/eth/filters/api.go b/eth/filters/api.go index 8345132629..9c0c3fcab0 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -56,20 +56,20 @@ type PublicFilterAPI struct { useMipMap bool mux *event.TypeMux quit chan struct{} - chainDb ethdb.Database + chaindb ethdb.Database events *EventSystem filtersMu sync.Mutex filters map[rpc.ID]*filter } // NewPublicFilterAPI returns a new PublicFilterAPI instance. -func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { +func NewPublicFilterAPI(backend Backend, mux *event.TypeMux, chaindb ethdb.Database, lightMode bool) *PublicFilterAPI { api := &PublicFilterAPI{ backend: backend, useMipMap: !lightMode, - mux: backend.EventMux(), - chainDb: backend.ChainDb(), - events: NewEventSystem(backend.EventMux(), backend, lightMode), + mux: mux, + chaindb: chaindb, + events: NewEventSystem(mux, backend, lightMode), filters: make(map[rpc.ID]*filter), } @@ -326,7 +326,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([ crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) } - filter := New(api.backend, api.useMipMap) + filter := New(api.backend, api.chaindb, api.useMipMap) filter.SetBeginBlock(crit.FromBlock.Int64()) filter.SetEndBlock(crit.ToBlock.Int64()) filter.SetAddresses(crit.Addresses) @@ -366,7 +366,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log return []Log{}, nil } - filter := New(api.backend, api.useMipMap) + filter := New(api.backend, api.chaindb, api.useMipMap) filter.SetBeginBlock(f.crit.FromBlock.Int64()) filter.SetEndBlock(f.crit.ToBlock.Int64()) filter.SetAddresses(f.crit.Addresses) diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 4004af3002..ce012df10e 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -18,22 +18,20 @@ package filters import ( "math" + "math/big" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) type Backend interface { - ChainDb() ethdb.Database - EventMux() *event.TypeMux - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) + HeaderByHash(context.Context, common.Hash) (*types.Header, error) + HeaderByNumber(context.Context, *big.Int) (*types.Header, error) + BlockReceipts(ctx context.Context, blockhash common.Hash, blocknum uint64) ([]*types.Receipt, error) } // Filter can be used to retrieve and filter logs @@ -51,11 +49,11 @@ type Filter struct { // New creates a new filter which uses a bloom filter on blocks to figure out whether // a particular block is interesting or not. -func New(backend Backend, useMipMap bool) *Filter { +func New(backend Backend, chaindb ethdb.Database, useMipMap bool) *Filter { return &Filter{ backend: backend, useMipMap: useMipMap, - db: backend.ChainDb(), + db: chaindb, } } @@ -84,7 +82,7 @@ func (f *Filter) SetTopics(topics [][]common.Hash) { // Run filters logs with the current parameters set func (f *Filter) Find(ctx context.Context) ([]Log, error) { - head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + head, _ := f.backend.HeaderByNumber(ctx, nil) if head == nil { return nil, nil } @@ -141,7 +139,7 @@ func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) { for i := start; i <= end; i++ { - header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(i)) + header, err := f.backend.HeaderByNumber(ctx, new(big.Int).SetUint64(i)) if header == nil || err != nil { return logs, err } @@ -150,7 +148,7 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er // current parameters if f.bloomFilter(header.Bloom) { // Get the logs of the block - receipts, err := f.backend.GetReceipts(ctx, header.Hash()) + receipts, err := f.backend.BlockReceipts(ctx, header.Hash(), i) if err != nil { return nil, err } diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index c2c072a9f9..b3dd8838a0 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -30,6 +30,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -301,27 +303,44 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) { func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { oldh := es.lastHead - es.lastHead = newHeader if oldh == nil { return } - newh := newHeader + ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + // find common ancestor, create list of rolled back and new block hashes - var oldHeaders, newHeaders []*types.Header + var ( + oldHeaders, newHeaders []*types.Header + newh = newHeader + err error + ) for oldh.Hash() != newh.Hash() { if oldh.Number.Uint64() >= newh.Number.Uint64() { oldHeaders = append(oldHeaders, oldh) - oldh = core.GetHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) + oldh, err = es.backend.HeaderByHash(ctx, oldh.ParentHash) + if err != nil { + break + } } if oldh.Number.Uint64() < newh.Number.Uint64() { newHeaders = append(newHeaders, newh) - newh = core.GetHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) + newh, err = es.backend.HeaderByHash(ctx, newh.ParentHash) + if err != nil { + break + } if newh == nil { // happens when CHT syncing, nothing to do newh = oldh } } } + if err != nil { + if glog.V(logger.Error) { + glog.Errorf("aborted rollback, can't fetch header: %v", err) + } + return + } + // roll back old blocks for _, h := range oldHeaders { callBack(h, true) @@ -330,6 +349,7 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func for i := len(newHeaders) - 1; i >= 0; i-- { callBack(newHeaders[i], false) } + es.lastHead = newh } // filter logs of a single header in light client mode @@ -339,7 +359,7 @@ func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common. //fmt.Println("bloom match") // Get the logs of the block ctx, _ := context.WithTimeout(context.Background(), time.Second*5) - receipts, err := es.backend.GetReceipts(ctx, header.Hash()) + receipts, err := es.backend.BlockReceipts(ctx, header.Hash(), header.Number.Uint64()) if err != nil { return nil } diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index 48d6811c0c..0edaeacd7c 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -37,7 +37,7 @@ var ( mux = new(event.TypeMux) db, _ = ethdb.NewMemDatabase() backend = &testBackend{mux, db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, mux, db, false) ) type testBackend struct { @@ -45,30 +45,26 @@ type testBackend struct { db ethdb.Database } -func (b *testBackend) ChainDb() ethdb.Database { - return b.db -} - -func (b *testBackend) EventMux() *event.TypeMux { - return b.mux -} - -func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { +func (b *testBackend) HeaderByNumber(ctx context.Context, blockNum *big.Int) (*types.Header, error) { var hash common.Hash var num uint64 - if blockNr == rpc.LatestBlockNumber { + if blockNum == nil { hash = core.GetHeadBlockHash(b.db) num = core.GetBlockNumber(b.db, hash) } else { - num = uint64(blockNr) + num = blockNum.Uint64() hash = core.GetCanonicalHash(b.db, num) } return core.GetHeader(b.db, hash, num), nil } -func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { +func (b *testBackend) HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) { num := core.GetBlockNumber(b.db, blockHash) - return core.GetBlockReceipts(b.db, blockHash, num), nil + return core.GetHeader(b.db, blockHash, num), nil +} + +func (b *testBackend) BlockReceipts(ctx context.Context, blockHash common.Hash, blockNum uint64) ([]*types.Receipt, error) { + return core.GetBlockReceipts(b.db, blockHash, blockNum), nil } // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index e0b24046c0..19d3026679 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -103,7 +103,7 @@ func BenchmarkMipmaps(b *testing.B) { } b.ResetTimer() - filter := New(backend, true) + filter := New(backend, db, true) filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4}) filter.SetBeginBlock(0) filter.SetEndBlock(-1) @@ -205,7 +205,7 @@ func TestFilters(t *testing.T) { } } - filter := New(backend, true) + filter := New(backend, db, true) filter.SetAddresses([]common.Address{addr}) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}}) filter.SetBeginBlock(0) @@ -216,7 +216,7 @@ func TestFilters(t *testing.T) { t.Error("expected 4 log, got", len(logs)) } - filter = New(backend, true) + filter = New(backend, db, true) filter.SetAddresses([]common.Address{addr}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetBeginBlock(900) @@ -229,7 +229,7 @@ func TestFilters(t *testing.T) { t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) } - filter = New(backend, true) + filter = New(backend, db, true) filter.SetAddresses([]common.Address{addr}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetBeginBlock(990) @@ -242,7 +242,7 @@ func TestFilters(t *testing.T) { t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) } - filter = New(backend, true) + filter = New(backend, db, true) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}}) filter.SetBeginBlock(1) filter.SetEndBlock(10) @@ -253,7 +253,7 @@ func TestFilters(t *testing.T) { } failHash := common.BytesToHash([]byte("fail")) - filter = New(backend, true) + filter = New(backend, db, true) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}}) filter.SetBeginBlock(0) filter.SetEndBlock(-1) @@ -264,7 +264,7 @@ func TestFilters(t *testing.T) { } failAddr := common.BytesToAddress([]byte("failmenow")) - filter = New(backend, true) + filter = New(backend, db, true) filter.SetAddresses([]common.Address{failAddr}) filter.SetBeginBlock(0) filter.SetEndBlock(-1) @@ -274,7 +274,7 @@ func TestFilters(t *testing.T) { t.Error("expected 0 log, got", len(logs)) } - filter = New(backend, true) + filter = New(backend, db, true) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}}) filter.SetBeginBlock(0) filter.SetEndBlock(-1) diff --git a/eth/gasprice/lightprice.go b/eth/gasprice/lightprice.go index 8886d32d7d..c96288a026 100644 --- a/eth/gasprice/lightprice.go +++ b/eth/gasprice/lightprice.go @@ -21,9 +21,8 @@ import ( "sort" "sync" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -38,7 +37,7 @@ const ( // LightPriceOracle recommends gas prices based on the content of recent // blocks. Suitable for both light and full clients. type LightPriceOracle struct { - backend ethapi.Backend + backend ethereum.ChainReader lastHead common.Hash lastPrice *big.Int cacheLock sync.RWMutex @@ -46,7 +45,7 @@ type LightPriceOracle struct { } // NewLightPriceOracle returns a new oracle. -func NewLightPriceOracle(backend ethapi.Backend) *LightPriceOracle { +func NewLightPriceOracle(backend ethereum.ChainReader) *LightPriceOracle { return &LightPriceOracle{ backend: backend, lastPrice: big.NewInt(LpoDefaultPrice), @@ -60,7 +59,7 @@ func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error lastPrice := self.lastPrice self.cacheLock.RUnlock() - head, _ := self.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + head, _ := self.backend.HeaderByNumber(ctx, nil) headHash := head.Hash() if headHash == lastHead { return lastPrice, nil @@ -132,7 +131,7 @@ type lpResult struct { // getLowestPrice calculates the lowest transaction gas price in a given block // and sends it to the result channel. If the block is empty, price is nil. func (self *LightPriceOracle) getLowestPrice(ctx context.Context, blockNum uint64, chn chan lpResult) { - block, err := self.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) + block, err := self.backend.BlockByNumber(ctx, new(big.Int).SetUint64(blockNum)) if block == nil { chn <- lpResult{nil, err} return diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 6e2b1378a7..db4b84ce33 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -143,15 +143,18 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H } // TransactionByHash returns the transaction with the given hash. -func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, error) { - var tx *types.Transaction - err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) +func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { + var rtx struct { + *types.Transaction + BlockHash *common.Hash + } + err = ec.c.CallContext(ctx, &rtx, "eth_getTransactionByHash", hash) if err == nil { - if _, r, _ := tx.SignatureValues(); r == nil { - return nil, fmt.Errorf("server returned transaction without signature") + if _, r, _ := rtx.SignatureValues(); r == nil { + return nil, false, fmt.Errorf("server returned transaction without signature") } } - return tx, err + return rtx.Transaction, rtx.BlockHash == nil, err } // TransactionCount returns the total number of transactions in the given block. diff --git a/interfaces.go b/interfaces.go index aab0e2029c..c9a2e40a73 100644 --- a/interfaces.go +++ b/interfaces.go @@ -53,8 +53,8 @@ type ChainReader interface { HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) - TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) } // ChainStateReader wraps access to the state trie of the canonical blockchain. Note that @@ -164,6 +164,9 @@ type PendingStateReader interface { PendingTransactionCount(ctx context.Context) (uint, error) } +// TODO(fjl): PendingNonceAt should have its own interface because it is provided by the tx pool +// and more widely available than the others. We could also make it part of TransactionSender. + // PendingContractCaller can be used to perform calls against the pending state. type PendingContractCaller interface { PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 36bb8c077c..51dbbac386 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -20,15 +20,16 @@ import ( "bytes" "encoding/hex" "encoding/json" + "errors" "fmt" "math/big" "strings" "time" "github.com/ethereum/ethash" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -45,6 +46,8 @@ import ( const defaultGas = uint64(90000) +var ErrNoPendingState = errors.New("API backend does not provide access to pending state") + // PublicEthereumAPI provides an API to access Ethereum related information. // It offers only methods that operate on public data that is freely available to anyone. type PublicEthereumAPI struct { @@ -58,7 +61,7 @@ func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI { // GasPrice returns a suggestion for a gas price. func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*big.Int, error) { - return s.b.SuggestPrice(ctx) + return s.b.SuggestGasPrice(ctx) } // ProtocolVersion returns the current Ethereum protocol version this node supports @@ -73,8 +76,11 @@ func (s *PublicEthereumAPI) ProtocolVersion() *rpc.HexNumber { // - highestBlock: block number of the highest block header this node has received from peers // - pulledStates: number of state entries processed until now // - knownStates: number of known state entries that still need to be pulled -func (s *PublicEthereumAPI) Syncing() (interface{}, error) { - progress := s.b.Downloader().Progress() +func (s *PublicEthereumAPI) Syncing(ctx context.Context) (interface{}, error) { + progress, err := s.b.SyncProgress(ctx) + if err != nil { + return nil, err + } // Return not syncing if the synchronisation already completed if progress.CurrentBlock >= progress.HighestBlock { @@ -90,87 +96,6 @@ func (s *PublicEthereumAPI) Syncing() (interface{}, error) { }, nil } -// PublicTxPoolAPI offers and API for the transaction pool. It only operates on data that is non confidential. -type PublicTxPoolAPI struct { - b Backend -} - -// NewPublicTxPoolAPI creates a new tx pool service that gives information about the transaction pool. -func NewPublicTxPoolAPI(b Backend) *PublicTxPoolAPI { - return &PublicTxPoolAPI{b} -} - -// Content returns the transactions contained within the transaction pool. -func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { - content := map[string]map[string]map[string]*RPCTransaction{ - "pending": make(map[string]map[string]*RPCTransaction), - "queued": make(map[string]map[string]*RPCTransaction), - } - pending, queue := s.b.TxPoolContent() - - // Flatten the pending transactions - for account, txs := range pending { - dump := make(map[string]*RPCTransaction) - for nonce, tx := range txs { - dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) - } - content["pending"][account.Hex()] = dump - } - // Flatten the queued transactions - for account, txs := range queue { - dump := make(map[string]*RPCTransaction) - for nonce, tx := range txs { - dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) - } - content["queued"][account.Hex()] = dump - } - return content -} - -// Status returns the number of pending and queued transaction in the pool. -func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber { - pending, queue := s.b.Stats() - return map[string]*rpc.HexNumber{ - "pending": rpc.NewHexNumber(pending), - "queued": rpc.NewHexNumber(queue), - } -} - -// Inspect retrieves the content of the transaction pool and flattens it into an -// easily inspectable list. -func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string]string { - content := map[string]map[string]map[string]string{ - "pending": make(map[string]map[string]string), - "queued": make(map[string]map[string]string), - } - pending, queue := s.b.TxPoolContent() - - // Define a formatter to flatten a transaction into a string - var format = func(tx *types.Transaction) string { - if to := tx.To(); to != nil { - return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice()) - } - return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice()) - } - // Flatten the pending transactions - for account, txs := range pending { - dump := make(map[string]string) - for nonce, tx := range txs { - dump[fmt.Sprintf("%d", nonce)] = format(tx) - } - content["pending"][account.Hex()] = dump - } - // Flatten the queued transactions - for account, txs := range queue { - dump := make(map[string]string) - for nonce, tx := range txs { - dump[fmt.Sprintf("%d", nonce)] = format(tx) - } - content["queued"][account.Hex()] = dump - } - return content -} - // PublicAccountAPI provides an API to access accounts managed by this node. // It offers only methods that can retrieve accounts. type PublicAccountAPI struct { @@ -265,7 +190,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs } if args.Nonce == nil { - nonce, err := s.b.GetPoolNonce(ctx, args.From) + nonce, err := s.b.PendingNonceAt(ctx, args.From) if err != nil { return common.Hash{}, err } @@ -366,28 +291,16 @@ func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { // BlockNumber returns the block number of the chain head. func (s *PublicBlockChainAPI) BlockNumber() *big.Int { - header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available + header, _ := s.b.HeaderByNumber(context.Background(), nil) // latest header should always be available return header.Number } -// GetBalance returns the amount of wei for the given address in the state of the -// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta -// block numbers are also allowed. -func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return nil, err - } - - return state.GetBalance(ctx, address) -} - // GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all // transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { - block, err := s.b.BlockByNumber(ctx, blockNr) + block, err := blockByNumber(ctx, s.b, blockNr) if block != nil { - response, err := s.rpcOutputBlock(block, true, fullTx) + response, err := s.rpcOutputBlock(ctx, block, true, fullTx) if err == nil && blockNr == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "logsBloom", "miner"} { @@ -399,12 +312,27 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc. return nil, err } +func blockByNumber(ctx context.Context, b Backend, blockNr rpc.BlockNumber) (*types.Block, error) { + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := b.(PendingState) + if !ok { + return nil, fmt.Errorf("can't access the pending block with the current backend") + } + return ps.PendingBlock() + case rpc.LatestBlockNumber: + return b.BlockByNumber(ctx, nil) + default: + return b.BlockByNumber(ctx, big.NewInt(int64(blockNr))) + } +} + // GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full // detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { - block, err := s.b.GetBlock(ctx, blockHash) + block, err := s.b.BlockByHash(ctx, blockHash) if block != nil { - return s.rpcOutputBlock(block, true, fullTx) + return s.rpcOutputBlock(ctx, block, true, fullTx) } return nil, err } @@ -412,7 +340,7 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash comm // GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (map[string]interface{}, error) { - block, err := s.b.BlockByNumber(ctx, blockNr) + block, err := blockByNumber(ctx, s.b, blockNr) if block != nil { uncles := block.Uncles() if index.Int() < 0 || index.Int() >= len(uncles) { @@ -420,7 +348,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, return nil, nil } block = types.NewBlockWithHeader(uncles[index.Int()]) - return s.rpcOutputBlock(block, false, false) + return s.rpcOutputBlock(ctx, block, false, false) } return nil, err } @@ -428,7 +356,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, // GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. When fullTx is true // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (map[string]interface{}, error) { - block, err := s.b.GetBlock(ctx, blockHash) + block, err := s.b.BlockByHash(ctx, blockHash) if block != nil { uncles := block.Uncles() if index.Int() < 0 || index.Int() >= len(uncles) { @@ -436,14 +364,14 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, b return nil, nil } block = types.NewBlockWithHeader(uncles[index.Int()]) - return s.rpcOutputBlock(block, false, false) + return s.rpcOutputBlock(ctx, block, false, false) } return nil, err } // GetUncleCountByBlockNumber returns number of uncles in the block for the given block number func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *rpc.HexNumber { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil { return rpc.NewHexNumber(len(block.Uncles())) } return nil @@ -451,60 +379,78 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, bl // GetUncleCountByBlockHash returns number of uncles in the block for the given block hash func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *rpc.HexNumber { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { return rpc.NewHexNumber(len(block.Uncles())) } return nil } +// GetBalance returns the amount of wei for the given address in the state of the +// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta +// block numbers are also allowed. +func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := s.b.(PendingState) + if !ok { + return nil, ErrNoPendingState + } + return ps.PendingBalanceAt(ctx, address) + case rpc.LatestBlockNumber: + return s.b.BalanceAt(ctx, address, nil) + default: + return s.b.BalanceAt(ctx, address, big.NewInt(int64(blockNr))) + } +} + // GetCode returns the code stored at the given address in the state for the given block number. func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (string, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return "", err + var code []byte + var err error + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := s.b.(PendingState) + if !ok { + return "", ErrNoPendingState + } + code, err = ps.PendingCodeAt(ctx, address) + case rpc.LatestBlockNumber: + code, err = s.b.CodeAt(ctx, address, nil) + default: + code, err = s.b.CodeAt(ctx, address, big.NewInt(int64(blockNr))) } - res, err := state.GetCode(ctx, address) - if len(res) == 0 || err != nil { // backwards compatibility + + if len(code) == 0 || err != nil { // backwards compatibility return "0x", err } - return common.ToHex(res), nil + return common.ToHex(code), nil } // GetStorageAt returns the storage from the state at the given address, key and // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // numbers are also allowed. -func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (string, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { +func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key common.Hash, blockNr rpc.BlockNumber) (string, error) { + var val []byte + var err error + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := s.b.(PendingState) + if !ok { + return "", ErrNoPendingState + } + val, err = ps.PendingStorageAt(ctx, address, key) + case rpc.LatestBlockNumber: + val, err = s.b.StorageAt(ctx, address, key, nil) + default: + val, err = s.b.StorageAt(ctx, address, key, big.NewInt(int64(blockNr))) + } + + if len(val) == 0 || err != nil { // backwards compatibility return "0x", err } - res, err := state.GetState(ctx, address, common.HexToHash(key)) - if err != nil { - return "0x", err - } - return res.Hex(), nil + return common.ToHex(val), nil } -// callmsg is the message type used for call transations. -type callmsg struct { - addr common.Address - to *common.Address - gas, gasPrice *big.Int - value *big.Int - data []byte -} - -// accessor boilerplate to implement core.Message -func (m callmsg) From() (common.Address, error) { return m.addr, nil } -func (m callmsg) FromFrontier() (common.Address, error) { return m.addr, nil } -func (m callmsg) Nonce() uint64 { return 0 } -func (m callmsg) CheckNonce() bool { return false } -func (m callmsg) To() *common.Address { return m.to } -func (m callmsg) GasPrice() *big.Int { return m.gasPrice } -func (m callmsg) Gas() *big.Int { return m.gas } -func (m callmsg) Value() *big.Int { return m.value } -func (m callmsg) Data() []byte { return m.data } - // CallArgs represents the arguments for a call. type CallArgs struct { From common.Address `json:"from"` @@ -512,74 +458,43 @@ type CallArgs struct { Gas rpc.HexNumber `json:"gas"` GasPrice rpc.HexNumber `json:"gasPrice"` Value rpc.HexNumber `json:"value"` - Data string `json:"data"` + Data rpc.HexBytes `json:"data"` } -func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { - defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now()) - - state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return "0x", common.Big0, err +func (args CallArgs) Msg() ethereum.CallMsg { + return ethereum.CallMsg{ + From: args.From, + To: args.To, + Gas: args.Gas.BigInt(), + GasPrice: args.GasPrice.BigInt(), + Value: args.Value.BigInt(), + Data: args.Data, } - - // Set the account address to interact with - var addr common.Address - if args.From == (common.Address{}) { - accounts := s.b.AccountManager().Accounts() - if len(accounts) == 0 { - addr = common.Address{} - } else { - addr = accounts[0].Address - } - } else { - addr = args.From - } - - // Assemble the CALL invocation - msg := callmsg{ - addr: addr, - to: args.To, - gas: args.Gas.BigInt(), - gasPrice: args.GasPrice.BigInt(), - value: args.Value.BigInt(), - data: common.FromHex(args.Data), - } - - if msg.gas.Cmp(common.Big0) == 0 { - msg.gas = big.NewInt(50000000) - } - - if msg.gasPrice.Cmp(common.Big0) == 0 { - msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) - } - - // Execute the call and return - vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) - if err != nil { - return "0x", common.Big0, err - } - gp := new(core.GasPool).AddGas(common.MaxBig) - res, gas, err := core.ApplyMessage(vmenv, msg, gp) - if err := vmError(); err != nil { - return "0x", common.Big0, err - } - if len(res) == 0 { // backwards compatability - return "0x", gas, err - } - return common.ToHex(res), gas, err } // Call executes the given transaction on the state for the given block number. // It doesn't make and changes in the state/blockchain and is usefull to execute and retrieve values. func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, error) { - result, _, err := s.doCall(ctx, args, blockNr) - return result, err + var result []byte + var err error + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := s.b.(PendingState) + if !ok { + return "", ErrNoPendingState + } + result, err = ps.PendingCallContract(ctx, args.Msg()) + case rpc.LatestBlockNumber: + result, err = s.b.CallContract(ctx, args.Msg(), nil) + default: + result, err = s.b.CallContract(ctx, args.Msg(), big.NewInt(int64(blockNr))) + } + return common.ToHex(result), err } // EstimateGas returns an estimate of the amount of gas needed to execute the given transaction. func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*rpc.HexNumber, error) { - _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber) + gas, err := s.b.EstimateGas(ctx, args.Msg()) return rpc.NewHexNumber(gas), err } @@ -639,7 +554,7 @@ func FormatLogs(structLogs []vm.StructLog) []StructLogRes { // rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain // transaction hashes. -func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { +func (s *PublicBlockChainAPI) rpcOutputBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { head := b.Header() // copies the header once fields := map[string]interface{}{ "number": rpc.NewHexNumber(head.Number), @@ -652,7 +567,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx "stateRoot": head.Root, "miner": head.Coinbase, "difficulty": rpc.NewHexNumber(head.Difficulty), - "totalDifficulty": rpc.NewHexNumber(s.b.GetTd(b.Hash())), + "totalDifficulty": rpc.NewHexNumber(s.b.BlockTD(b.Hash())), "extraData": rpc.HexBytes(head.Extra), "size": rpc.NewHexNumber(b.Size().Int64()), "gasLimit": rpc.NewHexNumber(head.GasLimit), @@ -663,22 +578,15 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx } if inclTx { - formatTx := func(tx *types.Transaction) (interface{}, error) { - return tx.Hash(), nil - } - - if fullTx { - formatTx = func(tx *types.Transaction) (interface{}, error) { - return newRPCTransaction(b, tx.Hash()) - } - } - txs := b.Transactions() transactions := make([]interface{}, len(txs)) - var err error for i, tx := range b.Transactions() { - if transactions[i], err = formatTx(tx); err != nil { - return nil, err + if fullTx { + rtx := newRPCTransaction(tx) + rtx.setInclusionBlock(b.Hash(), b.NumberU64(), i) + transactions = append(transactions, rtx) + } else { + transactions = append(transactions, tx.Hash()) } } fields["transactions"] = transactions @@ -712,8 +620,14 @@ type RPCTransaction struct { S *rpc.HexNumber `json:"s"` } -// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation -func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { +func (tx *RPCTransaction) setInclusionBlock(blockhash common.Hash, blocknum uint64, index int) { + tx.BlockHash = blockhash + tx.BlockNumber = rpc.NewHexNumber(blocknum) + tx.TransactionIndex = rpc.NewHexNumber(index) +} + +// newRPCTransaction returns a pending transaction that will serialize to the RPC representation +func newRPCTransaction(tx *types.Transaction) *RPCTransaction { from, _ := tx.FromFrontier() v, r, s := tx.SignatureValues() return &RPCTransaction{ @@ -732,33 +646,13 @@ func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { } // newRPCTransaction returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransaction, error) { +func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) *RPCTransaction { if txIndex >= 0 && txIndex < len(b.Transactions()) { - tx := b.Transactions()[txIndex] - from, err := tx.FromFrontier() - if err != nil { - return nil, err - } - v, r, s := tx.SignatureValues() - return &RPCTransaction{ - BlockHash: b.Hash(), - BlockNumber: rpc.NewHexNumber(b.Number()), - From: from, - Gas: rpc.NewHexNumber(tx.Gas()), - GasPrice: rpc.NewHexNumber(tx.GasPrice()), - Hash: tx.Hash(), - Input: rpc.HexBytes(tx.Data()), - Nonce: rpc.NewHexNumber(tx.Nonce()), - To: tx.To(), - TransactionIndex: rpc.NewHexNumber(txIndex), - Value: rpc.NewHexNumber(tx.Value()), - V: rpc.NewHexNumber(v), - R: rpc.NewHexNumber(r), - S: rpc.NewHexNumber(s), - }, nil + tx := newRPCTransaction(b.Transactions()[txIndex]) + tx.setInclusionBlock(b.Hash(), b.NumberU64(), txIndex) + return tx } - - return nil, nil + return nil } // newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. @@ -767,18 +661,6 @@ func newRPCRawTransactionFromBlockIndex(b *types.Block, txIndex int) (rpc.HexByt tx := b.Transactions()[txIndex] return rlp.EncodeToBytes(tx) } - - return nil, nil -} - -// newRPCTransaction returns a transaction that will serialize to the RPC representation. -func newRPCTransaction(b *types.Block, txHash common.Hash) (*RPCTransaction, error) { - for idx, tx := range b.Transactions() { - if tx.Hash() == txHash { - return newRPCTransactionFromBlockIndex(b, idx) - } - } - return nil, nil } @@ -792,27 +674,9 @@ func NewPublicTransactionPoolAPI(b Backend) *PublicTransactionPoolAPI { return &PublicTransactionPoolAPI{b} } -func getTransaction(chainDb ethdb.Database, b Backend, txHash common.Hash) (*types.Transaction, bool, error) { - txData, err := chainDb.Get(txHash.Bytes()) - isPending := false - tx := new(types.Transaction) - - if err == nil && len(txData) > 0 { - if err := rlp.DecodeBytes(txData, tx); err != nil { - return nil, isPending, err - } - } else { - // pending transaction? - tx = b.GetPoolTransaction(txHash) - isPending = true - } - - return tx, isPending, nil -} - // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *rpc.HexNumber { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil { return rpc.NewHexNumber(len(block.Transactions())) } return nil @@ -820,31 +684,32 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context. // GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *rpc.HexNumber { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { - return rpc.NewHexNumber(len(block.Transactions())) + count, err := s.b.TransactionCount(ctx, blockHash) + if err == nil { + return rpc.NewHexNumber(count) } return nil } // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (*RPCTransaction, error) { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { +func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) *RPCTransaction { + if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil { return newRPCTransactionFromBlockIndex(block, index.Int()) } - return nil, nil + return nil } // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (*RPCTransaction, error) { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { +func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) *RPCTransaction { + if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { return newRPCTransactionFromBlockIndex(block, index.Int()) } - return nil, nil + return nil } // GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (rpc.HexBytes, error) { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil { return newRPCRawTransactionFromBlockIndex(block, index.Int()) } return nil, nil @@ -852,7 +717,7 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx co // GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (rpc.HexBytes, error) { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { return newRPCRawTransactionFromBlockIndex(block, index.Int()) } return nil, nil @@ -860,11 +725,16 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx cont // GetTransactionCount returns the number of transactions the given address has sent for the given block number func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return nil, err + var nonce uint64 + var err error + switch blockNr { + case rpc.PendingBlockNumber: + nonce, err = s.b.PendingNonceAt(ctx, address) + case rpc.LatestBlockNumber: + nonce, err = s.b.NonceAt(ctx, address, nil) + default: + nonce, err = s.b.NonceAt(ctx, address, big.NewInt(int64(blockNr))) } - nonce, err := state.GetNonce(ctx, address) if err != nil { return nil, err } @@ -894,82 +764,50 @@ func getTransactionBlockData(chainDb ethdb.Database, txHash common.Hash) (common } // GetTransactionByHash returns the transaction for the given hash -func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txHash common.Hash) (*RPCTransaction, error) { - var tx *types.Transaction - var isPending bool - var err error - - if tx, isPending, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } else if tx == nil { +func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txhash common.Hash) (*RPCTransaction, error) { + tx, isPending, err := s.b.TransactionByHash(ctx, txhash) + if tx == nil || err != nil { return nil, nil } - + rtx := newRPCTransaction(tx) if isPending { - return newRPCPendingTransaction(tx), nil + return rtx, nil + } else if tib, ok := s.b.(TransactionInclusionBlock); ok { + bhash, bnum, index, err := tib.TransactionInclusionBlock(txhash) + if err != nil { + return nil, err + } + rtx.setInclusionBlock(bhash, bnum, index) } - - blockHash, _, _, err := getTransactionBlockData(s.b.ChainDb(), txHash) - if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } - - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { - return newRPCTransaction(block, txHash) - } - - return nil, nil + return rtx, nil } // GetRawTransactionByHash returns the bytes of the transaction for the given hash. -func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txHash common.Hash) (rpc.HexBytes, error) { - var tx *types.Transaction - var err error - - if tx, _, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } else if tx == nil { +func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txhash common.Hash) (rpc.HexBytes, error) { + tx, _, err := s.b.TransactionByHash(ctx, txhash) + if tx == nil || err != nil { return nil, nil } - return rlp.EncodeToBytes(tx) } // GetTransactionReceipt returns the transaction receipt for the given transaction hash. -func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (map[string]interface{}, error) { - receipt := core.GetReceipt(s.b.ChainDb(), txHash) - if receipt == nil { - glog.V(logger.Debug).Infof("receipt not found for transaction %s", txHash.Hex()) - return nil, nil - } - - tx, _, err := getTransaction(s.b.ChainDb(), s.b, txHash) +func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, txhash common.Hash) (map[string]interface{}, error) { + receipt, err := s.b.TransactionReceipt(ctx, txhash) if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) + glog.V(logger.Debug).Infof("receipt not found for transaction %s: %v", txhash.Hex(), err) return nil, nil } - - txBlock, blockIndex, index, err := getTransactionBlockData(s.b.ChainDb(), txHash) + tx, isPending, err := s.b.TransactionByHash(ctx, txhash) if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } - - from, err := tx.FromFrontier() - if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) + glog.V(logger.Debug).Infof("can't get transaction %s: %v\n", txhash.Hex(), err) return nil, nil } + from, _ := tx.FromFrontier() fields := map[string]interface{}{ "root": rpc.HexBytes(receipt.PostState), - "blockHash": txBlock, - "blockNumber": rpc.NewHexNumber(blockIndex), - "transactionHash": txHash, - "transactionIndex": rpc.NewHexNumber(index), + "transactionHash": txhash, "from": from, "to": tx.To(), "gasUsed": rpc.NewHexNumber(receipt.GasUsed), @@ -978,10 +816,21 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma "logs": receipt.Logs, "logsBloom": receipt.Bloom, } + // Set block inclusion information if available. + if tib, ok := s.b.(TransactionInclusionBlock); !isPending && ok { + bhash, bnum, index, err := tib.TransactionInclusionBlock(txhash) + if err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } + fields["blockHash"] = bhash + fields["blockNumber"] = rpc.NewHexNumber(bnum) + fields["transactionIndex"] = rpc.NewHexNumber(index) + } + if receipt.Logs == nil { fields["logs"] = []vm.Logs{} } - // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation if receipt.ContractAddress != (common.Address{}) { fields["contractAddress"] = receipt.ContractAddress } @@ -1014,7 +863,7 @@ func prepareSendTxArgs(ctx context.Context, args SendTxArgs, b Backend) (SendTxA args.Gas = rpc.NewHexNumber(defaultGas) } if args.GasPrice == nil { - price, err := b.SuggestPrice(ctx) + price, err := b.SuggestGasPrice(ctx) if err != nil { return args, err } @@ -1033,7 +882,7 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, si return common.Hash{}, err } - if err := b.SendTx(ctx, signedTx); err != nil { + if err := b.SendTransaction(ctx, signedTx); err != nil { return common.Hash{}, err } @@ -1058,7 +907,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen } if args.Nonce == nil { - nonce, err := s.b.GetPoolNonce(ctx, args.From) + nonce, err := s.b.PendingNonceAt(ctx, args.From) if err != nil { return common.Hash{}, err } @@ -1088,7 +937,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod return "", err } - if err := s.b.SendTx(ctx, tx); err != nil { + if err := s.b.SendTransaction(ctx, tx); err != nil { return "", err } @@ -1224,7 +1073,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig args.Gas = rpc.NewHexNumber(defaultGas) } if args.GasPrice == nil { - price, err := s.b.SuggestPrice(ctx) + price, err := s.b.SuggestGasPrice(ctx) if err != nil { return nil, err } @@ -1235,7 +1084,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig } if args.Nonce == nil { - nonce, err := s.b.GetPoolNonce(ctx, args.From) + nonce, err := s.b.PendingNonceAt(ctx, args.From) if err != nil { return nil, err } @@ -1265,12 +1114,12 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig // PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of // the accounts this node manages. func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { - pending := s.b.GetPoolTransactions() + pending := s.b.PendingTransactions() transactions := make([]*RPCTransaction, 0, len(pending)) for _, tx := range pending { from, _ := tx.FromFrontier() if s.b.AccountManager().HasAddress(from) { - transactions = append(transactions, newRPCPendingTransaction(tx)) + transactions = append(transactions, newRPCTransaction(tx)) } } return transactions @@ -1279,7 +1128,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { // Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the // pool and reinsert it with the new gas price and limit. func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { - pending := s.b.GetPoolTransactions() + pending := s.b.PendingTransactions() for _, p := range pending { if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() { if gasPrice == nil { @@ -1301,8 +1150,8 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice, return common.Hash{}, err } - s.b.RemoveTx(tx.Hash) - if err = s.b.SendTx(ctx, signedTx); err != nil { + s.b.RemoveTransaction(tx.Hash) + if err = s.b.SendTransaction(ctx, signedTx); err != nil { return common.Hash{}, err } @@ -1326,8 +1175,8 @@ func NewPublicDebugAPI(b Backend) *PublicDebugAPI { } // GetBlockRlp retrieves the RLP encoded for of a single block. -func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (string, error) { - block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) +func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number rpc.BlockNumber) (string, error) { + block, _ := blockByNumber(ctx, api.b, number) if block == nil { return "", fmt.Errorf("block #%d not found", number) } @@ -1339,8 +1188,8 @@ func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (stri } // PrintBlock retrieves a block and returns its pretty printed form. -func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (string, error) { - block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) +func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number rpc.BlockNumber) (string, error) { + block, _ := blockByNumber(ctx, api.b, number) if block == nil { return "", fmt.Errorf("block #%d not found", number) } @@ -1349,10 +1198,6 @@ func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (strin // SeedHash retrieves the seed hash of a block. func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, error) { - block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) - if block == nil { - return "", fmt.Errorf("block #%d not found", number) - } hash, err := ethash.GetSeedHash(number) if err != nil { return "", err @@ -1360,21 +1205,22 @@ func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, return fmt.Sprintf("0x%x", hash), nil } -// PrivateDebugAPI is the collection of Etheruem APIs exposed over the private +// PrivateDebugAPI is the collection of Ethereum APIs exposed over the private // debugging endpoint. type PrivateDebugAPI struct { - b Backend + b Backend + chaindb ethdb.Database } // NewPrivateDebugAPI creates a new API definition for the private debug methods // of the Ethereum service. -func NewPrivateDebugAPI(b Backend) *PrivateDebugAPI { - return &PrivateDebugAPI{b: b} +func NewPrivateDebugAPI(b Backend, chaindb ethdb.Database) *PrivateDebugAPI { + return &PrivateDebugAPI{b, chaindb} } // ChaindbProperty returns leveldb properties of the chain database. func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { - ldb, ok := api.b.ChainDb().(interface { + ldb, ok := api.chaindb.(interface { LDB() *leveldb.DB }) if !ok { @@ -1389,7 +1235,7 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { } func (api *PrivateDebugAPI) ChaindbCompact() error { - ldb, ok := api.b.ChainDb().(interface { + ldb, ok := api.chaindb.(interface { LDB() *leveldb.DB }) if !ok { @@ -1408,7 +1254,7 @@ func (api *PrivateDebugAPI) ChaindbCompact() error { // SetHead rewinds the head of the blockchain to a previous block. func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) { - api.b.SetHead(uint64(number.Int64())) + api.b.ResetHeadBlock(number.Uint64()) } // PublicNetAPI offers network related RPC methods diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index f9358b6cfd..2a898d16ef 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -20,52 +20,44 @@ package ethapi import ( "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) -// Backend interface provides the common API services (that are provided by -// both full and light clients) with access to necessary functions. +// Backend defines most methods required for the RPC API. type Backend interface { - // general Ethereum API - Downloader() *downloader.Downloader + ethereum.ChainReader + ethereum.ChainStateReader + ethereum.ChainSyncReader + ethereum.TransactionSender + ethereum.GasPricer + ethereum.GasEstimator + ethereum.ContractCaller + ProtocolVersion() int - SuggestPrice(ctx context.Context) (*big.Int, error) - ChainDb() ethdb.Database - EventMux() *event.TypeMux - AccountManager() *accounts.Manager - // BlockChain API - SetHead(number uint64) - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) - BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) - StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (State, *types.Header, error) - GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) - GetTd(blockHash common.Hash) *big.Int - GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (vm.Environment, func() error, error) - // TxPool API - SendTx(ctx context.Context, signedTx *types.Transaction) error - RemoveTx(txHash common.Hash) - GetPoolTransactions() types.Transactions - GetPoolTransaction(txHash common.Hash) *types.Transaction - GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) - Stats() (pending int, queued int) - TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) + AccountManager() *accounts.Manager // TODO(fjl): this should be a constructor argb + BlockTD(common.Hash) *big.Int + RemoveTransaction(txhash common.Hash) + PendingTransactions() []*types.Transaction + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + ResetHeadBlock(number uint64) // for admin API } -type State interface { - GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) - GetCode(ctx context.Context, addr common.Address) ([]byte, error) - GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) - GetNonce(ctx context.Context, addr common.Address) (uint64, error) +// PendingState is implemented by the eth.Ethereum backend and provides access to optional +// features that can only be provided by a full pending state. +type PendingState interface { + PendingBlock() (*types.Block, error) + ethereum.PendingStateReader + ethereum.PendingContractCaller +} + +type TransactionInclusionBlock interface { + // returns the block at which the given transaction was included in the blockchain + TransactionInclusionBlock(txhash common.Hash) (blockhash common.Hash, blocknum uint64, index int, err error) } func GetAPIs(apiBackend Backend, solcPath string) []rpc.API { @@ -86,20 +78,11 @@ func GetAPIs(apiBackend Backend, solcPath string) []rpc.API { Version: "1.0", Service: NewPublicTransactionPoolAPI(apiBackend), Public: true, - }, { - Namespace: "txpool", - Version: "1.0", - Service: NewPublicTxPoolAPI(apiBackend), - Public: true, }, { Namespace: "debug", Version: "1.0", Service: NewPublicDebugAPI(apiBackend), Public: true, - }, { - Namespace: "debug", - Version: "1.0", - Service: NewPrivateDebugAPI(apiBackend), }, { Namespace: "eth", Version: "1.0", diff --git a/internal/ethapi/txpool.go b/internal/ethapi/txpool.go new file mode 100644 index 0000000000..1f458edf54 --- /dev/null +++ b/internal/ethapi/txpool.go @@ -0,0 +1,105 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" +) + +type TxPool interface { + Stats() (pending int, queued int) + Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) +} + +// TxPoolDebugAPI offers and API for the transaction pool. It only operates on data that +// is non confidential. +type TxPoolDebugAPI struct{ Pool TxPool } + +// Content returns the transactions contained within the transaction pool. +func (s TxPoolDebugAPI) Content() map[string]map[string]map[string]*RPCTransaction { + content := map[string]map[string]map[string]*RPCTransaction{ + "pending": make(map[string]map[string]*RPCTransaction), + "queued": make(map[string]map[string]*RPCTransaction), + } + pending, queue := s.Pool.Content() + + // Flatten the pending transactions + for account, txs := range pending { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCTransaction(tx) + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, txs := range queue { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCTransaction(tx) + } + content["queued"][account.Hex()] = dump + } + return content +} + +// Status returns the number of pending and queued transaction in the pool. +func (s TxPoolDebugAPI) Status() map[string]*rpc.HexNumber { + pending, queue := s.Pool.Stats() + return map[string]*rpc.HexNumber{ + "pending": rpc.NewHexNumber(pending), + "queued": rpc.NewHexNumber(queue), + } +} + +// Inspect retrieves the content of the transaction pool and flattens it into an +// easily inspectable list. +func (s TxPoolDebugAPI) Inspect() map[string]map[string]map[string]string { + content := map[string]map[string]map[string]string{ + "pending": make(map[string]map[string]string), + "queued": make(map[string]map[string]string), + } + pending, queue := s.Pool.Content() + + // Define a formatter to flatten a transaction into a string + var format = func(tx *types.Transaction) string { + if to := tx.To(); to != nil { + return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice()) + } + return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice()) + } + // Flatten the pending transactions + for account, txs := range pending { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, txs := range queue { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) + } + content["queued"][account.Hex()] = dump + } + return content +} diff --git a/les/api_backend.go b/les/api_backend.go index 04120c669f..40de277714 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -17,128 +17,202 @@ package les import ( + "errors" + "fmt" "math/big" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/light" - rpc "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) -type LesApiBackend struct { - eth *LightEthereum - gpo *gasprice.LightPriceOracle -} +var ( + ErrBlockNotFound = errors.New("block not found") + ErrTxNotFound = errors.New("the light client cannot retrieve past transactions by hash") + ErrReceiptNotFound = errors.New("the light client cannot retrieve receipts by hash") +) -func (b *LesApiBackend) SetHead(number uint64) { - b.eth.blockchain.SetHead(number) -} - -func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { - if blockNr == rpc.LatestBlockNumber || blockNr == rpc.PendingBlockNumber { - return b.eth.blockchain.CurrentHeader(), nil +func (eth *LightEthereum) HeaderByHash(ctx context.Context, blockhash common.Hash) (*types.Header, error) { + if h := eth.blockchain.GetHeaderByHash(blockhash); h != nil { + return h, nil } - - return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr)) + return nil, ErrBlockNotFound } -func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { - header, err := b.HeaderByNumber(ctx, blockNr) - if header == nil || err != nil { +func (eth *LightEthereum) HeaderByNumber(ctx context.Context, blocknum *big.Int) (*types.Header, error) { + if blocknum == nil { + return eth.blockchain.CurrentHeader(), nil + } + h, err := eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blocknum.Uint64())) + if err != nil { return nil, err } - return b.GetBlock(ctx, header.Hash()) -} - -func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) { - header, err := b.HeaderByNumber(ctx, blockNr) - if header == nil || err != nil { - return nil, nil, err + if h == nil { + return nil, ErrBlockNotFound } - return light.NewLightState(light.StateTrieID(header), b.eth.odr), header, nil + return h, nil } -func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { - return b.eth.blockchain.GetBlockByHash(ctx, blockHash) -} - -func (b *LesApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { - return light.GetBlockReceipts(ctx, b.eth.odr, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)) -} - -func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int { - return b.eth.blockchain.GetTdByHash(blockHash) -} - -func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { - stateDb := state.(*light.LightState).Copy() - addr, _ := msg.From() - from, err := stateDb.GetOrNewStateObject(ctx, addr) +func (eth *LightEthereum) BlockByNumber(ctx context.Context, blocknum *big.Int) (*types.Block, error) { + header, err := eth.HeaderByNumber(ctx, blocknum) if err != nil { - return nil, nil, err + return nil, err } - from.SetBalance(common.MaxBig) - env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig) - return env, env.Error, nil + return eth.BlockByHash(ctx, header.Hash()) } -func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { - return b.eth.txPool.Add(ctx, signedTx) +func (eth *LightEthereum) BlockByHash(ctx context.Context, blockhash common.Hash) (*types.Block, error) { + return eth.blockchain.GetBlockByHash(ctx, blockhash) } -func (b *LesApiBackend) RemoveTx(txHash common.Hash) { - b.eth.txPool.RemoveTx(txHash) +func (eth *LightEthereum) BlockReceipts(ctx context.Context, blockhash common.Hash, blocknum uint64) ([]*types.Receipt, error) { + return light.GetBlockReceipts(ctx, eth.odr, blockhash, blocknum) } -func (b *LesApiBackend) GetPoolTransactions() types.Transactions { - return b.eth.txPool.GetTransactions() +func (eth *LightEthereum) BlockTD(blockHash common.Hash) *big.Int { + return eth.blockchain.GetTdByHash(blockHash) } -func (b *LesApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { - return b.eth.txPool.GetTransaction(txHash) +func (eth *LightEthereum) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) { + if tx = eth.txPool.GetTransaction(txHash); tx != nil { + return tx, true, nil + } + return nil, false, ErrTxNotFound } -func (b *LesApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { - return b.eth.txPool.GetNonce(ctx, addr) +func (eth *LightEthereum) TransactionInBlock(ctx context.Context, blockhash common.Hash, index uint) (*types.Transaction, error) { + b, err := eth.blockchain.GetBlockByHash(ctx, blockhash) + if err != nil { + return nil, err + } + if index >= uint(len(b.Transactions())) { + return nil, fmt.Errorf("transaction index out of range") + } + return b.Transactions()[index], nil } -func (b *LesApiBackend) Stats() (pending int, queued int) { - return b.eth.txPool.Stats(), 0 +func (eth *LightEthereum) TransactionReceipt(ctx context.Context, txhash common.Hash) (*types.Receipt, error) { + return nil, ErrReceiptNotFound } -func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { - return b.eth.txPool.Content() +func (eth *LightEthereum) TransactionCount(ctx context.Context, blockhash common.Hash) (uint, error) { + b, err := eth.blockchain.GetBlockByHash(ctx, blockhash) + if err != nil { + return 0, err + } + return uint(len(b.Transactions())), nil } -func (b *LesApiBackend) Downloader() *downloader.Downloader { - return b.eth.Downloader() +func (eth *LightEthereum) BalanceAt(ctx context.Context, addr common.Address, blocknum *big.Int) (*big.Int, error) { + st, err := eth.state(ctx, blocknum) + if err != nil { + return nil, err + } + return st.GetBalance(ctx, addr) } -func (b *LesApiBackend) ProtocolVersion() int { - return b.eth.LesVersion() + 10000 +func (eth *LightEthereum) CodeAt(ctx context.Context, addr common.Address, blocknum *big.Int) ([]byte, error) { + st, err := eth.state(ctx, blocknum) + if err != nil { + return nil, err + } + return st.GetCode(ctx, addr) } -func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx) +func (eth *LightEthereum) NonceAt(ctx context.Context, addr common.Address, blocknum *big.Int) (uint64, error) { + st, err := eth.state(ctx, blocknum) + if err != nil { + return 0, err + } + return st.GetNonce(ctx, addr) } -func (b *LesApiBackend) ChainDb() ethdb.Database { - return b.eth.chainDb +func (eth *LightEthereum) StorageAt(ctx context.Context, addr common.Address, key common.Hash, blocknum *big.Int) ([]byte, error) { + st, err := eth.state(ctx, blocknum) + if err != nil { + return nil, err + } + v, err := st.GetState(ctx, addr, key) + if err != nil { + return nil, err + } + return v[:], nil } -func (b *LesApiBackend) EventMux() *event.TypeMux { - return b.eth.eventMux +func (eth *LightEthereum) PendingCodeAt(ctx context.Context, addr common.Address) ([]byte, error) { + // TODO(fjl): find a way to get rid of PendingCodeAt here. CodeAt is a bad emulation + // of PendingCodeAt because it forces users to wait for transactions to get mined. + return eth.CodeAt(ctx, addr, nil) } -func (b *LesApiBackend) AccountManager() *accounts.Manager { - return b.eth.accountManager +func (eth *LightEthereum) state(ctx context.Context, blocknum *big.Int) (*light.LightState, error) { + header, err := eth.HeaderByNumber(ctx, blocknum) + if err != nil { + return nil, err + } + return light.NewLightState(light.StateTrieID(header), eth.odr), nil +} + +func (eth *LightEthereum) SendTransaction(ctx context.Context, signedTx *types.Transaction) error { + return eth.txPool.Add(ctx, signedTx) +} + +func (eth *LightEthereum) RemoveTransaction(txHash common.Hash) { + eth.txPool.RemoveTx(txHash) +} + +func (eth *LightEthereum) PendingTransactions() []*types.Transaction { + return eth.txPool.GetTransactions() +} + +func (eth *LightEthereum) PendingNonceAt(ctx context.Context, addr common.Address) (uint64, error) { + return eth.txPool.GetNonce(ctx, addr) +} + +func (eth *LightEthereum) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { + return eth.protocolManager.downloader.Progress(), nil +} + +func (eth *LightEthereum) ProtocolVersion() int { + return int(eth.protocolManager.SubProtocols[0].Version) + 10000 +} + +func (eth *LightEthereum) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + return eth.gpo.SuggestPrice(ctx) +} + +func (eth *LightEthereum) AccountManager() *accounts.Manager { + return eth.accountManager +} + +func (eth *LightEthereum) ResetHeadBlock(number uint64) { + eth.blockchain.SetHead(number) +} + +func (eth *LightEthereum) EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) { + _, gas, err := eth.callContract(ctx, call, nil) + return gas, err +} + +func (eth *LightEthereum) CallContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, error) { + val, _, err := eth.callContract(ctx, call, blocknum) + return val, err +} + +func (eth *LightEthereum) callContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, *big.Int, error) { + var head *types.Header + if blocknum == nil { + head = eth.blockchain.CurrentHeader() + } else if head = eth.blockchain.GetHeaderByNumber(blocknum.Uint64()); head == nil { + return nil, nil, ErrBlockNotFound + } + state := light.NewLightState(light.StateTrieID(head), eth.odr) + return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment { + return light.NewEnv(ctx, state, eth.chainConfig, eth.blockchain, msg, head, vm.Config{}) + }) } diff --git a/les/backend.go b/les/backend.go index 5eb53d728a..734d01310d 100644 --- a/les/backend.go +++ b/les/backend.go @@ -58,7 +58,7 @@ type LightEthereum struct { // DB interfaces chainDb ethdb.Database // Block chain database - ApiBackend *LesApiBackend + gpo *gasprice.LightPriceOracle eventMux *event.TypeMux pow *ethash.Ethash @@ -124,8 +124,8 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { return nil, err } - eth.ApiBackend = &LesApiBackend{eth, nil} - eth.ApiBackend.gpo = gasprice.NewLightPriceOracle(eth.ApiBackend) + eth.gpo = gasprice.NewLightPriceOracle(eth) + return eth, nil } @@ -153,9 +153,13 @@ func (s *LightDummyAPI) Mining() bool { // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. -func (s *LightEthereum) APIs() []rpc.API { - return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ +func (eth *LightEthereum) APIs() []rpc.API { + return append(ethapi.GetAPIs(eth, eth.solcPath), []rpc.API{ { + Namespace: "debug", + Version: "1.0", + Service: ethapi.NewPrivateDebugAPI(eth, eth.chainDb), + }, { Namespace: "eth", Version: "1.0", Service: &LightDummyAPI{}, @@ -163,17 +167,22 @@ func (s *LightEthereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), + Service: downloader.NewPublicDownloaderAPI(eth.protocolManager.downloader, eth.eventMux), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, true), + Service: filters.NewPublicFilterAPI(eth, eth.eventMux, eth.chainDb, true), Public: true, }, { + Namespace: "txpool", + Version: "1.0", + Service: ethapi.TxPoolDebugAPI{Pool: eth.txPool}, + }, + { Namespace: "net", Version: "1.0", - Service: s.netRPCService, + Service: eth.netRPCService, Public: true, }, }...) @@ -183,11 +192,6 @@ func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) { s.blockchain.ResetWithGenesisBlock(gb) } -func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain } -func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool } -func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } -func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } - // Protocols implements node.Service, returning all the currently configured // network protocols to start. func (s *LightEthereum) Protocols() []p2p.Protocol { diff --git a/les/server.go b/les/server.go index 60af6a9e18..10b163f2c5 100644 --- a/les/server.go +++ b/les/server.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/logger" @@ -44,8 +45,8 @@ type LesServer struct { defParams *flowcontrol.ServerParams } -func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { - pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, eth.EventMux(), eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil) +func NewLesServer(eth *eth.Ethereum, mux *event.TypeMux, config *eth.Config) (*LesServer, error) { + pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, mux, eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil) if err != nil { return nil, err } diff --git a/light/txpool.go b/light/txpool.go index 825a0f9099..ecf234468c 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -321,12 +321,11 @@ func (pool *TxPool) Stop() { } // Stats returns the number of currently pending (locally created) transactions -func (pool *TxPool) Stats() (pending int) { +func (pool *TxPool) Stats() (pending int, queued int) { pool.mu.RLock() defer pool.mu.RUnlock() - pending = len(pool.pending) - return + return len(pool.pending), 0 } // validateTx checks whether a transaction is valid according to the consensus rules.