From af65d52c277ce033d497a5c5991eba60014f88a4 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 11 Nov 2016 05:18:04 +0100 Subject: [PATCH 1/2] common/registrar: delete the old registrar code The registrar was broken, unmaintained and there is a much better replacement: ENS. --- common/registrar/contracts.go | 163 ----------- common/registrar/ethreg/api.go | 282 ------------------- common/registrar/registrar.go | 436 ----------------------------- common/registrar/registrar_test.go | 158 ----------- eth/backend.go | 7 - 5 files changed, 1046 deletions(-) delete mode 100644 common/registrar/contracts.go delete mode 100644 common/registrar/ethreg/api.go delete mode 100644 common/registrar/registrar.go delete mode 100644 common/registrar/registrar_test.go 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/eth/backend.go b/eth/backend.go index ec501043a5..f58fb0f156 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -32,7 +32,6 @@ import ( "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,7 +126,6 @@ type Ethereum struct { eventMux *event.TypeMux pow *ethash.Ethash - httpclient *httpclient.HTTPClient accountManager *accounts.Manager ApiBackend *EthApiBackend @@ -173,7 +171,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, @@ -358,10 +355,6 @@ func (s *Ethereum) APIs() []rpc.API { Version: "1.0", Service: s.netRPCService, Public: true, - }, { - Namespace: "admin", - Version: "1.0", - Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager), }, }...) } From 6353cddea22e3a40c89122759fb6776f9562f564 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 11 Nov 2016 05:20:21 +0100 Subject: [PATCH 2/2] all: run RPC APIs on top of the stable Go API In this commit, the RPC API is adapted to run on top of the new Go API. This required lots of changes to many packages, but has a few side benefits: - Ethereum and LightEthereum can now be used as a contract backend. - Some duplicated code could be removed (there is added duplication in other places though) - It is now much easier to see which operations are unsupported with the light client. Package les previously relied on the full node RPC API backend, which masked several issues because the RPC API performed direct access to the chain database. Changes to packages in detail: accounts/abi/bind: - Contract call boilerplate has moved to package core. cmd/utils: - les now inherits the event.TypeMux from the Node instance contracts/release: - The ReleaseService now uses Ethereum and LightEthereum as backend. core: - MissingNumber is exposed so it can be used in package eth. - GetTransaction now returns the index as an int, for convenience reasons. - ApplyCallMessage has been added as the new one and only implementation of read-only contract calls. - TxPool exposes NonceAt instead of the more general accessor for the ManagedState. eth: - EthApiBackend is gone. In its place, Ethereum gains many new methods which implement a large portion of the new Go API. It does not yet support event subscriptions and log filtering. - Some accessors for internal objects are gone. - ethapi.PrivateDebugAPI and ethapi.TxPoolDebugAPI are now created in package eth for dependency reasons. eth/downloader: - Progress returns a pointer to simplify callers. eth/filters: - The Backend interface is simpler and uses the stable Go API where possible. The new BlockReceipts method saves one DB read because BlockReceipts also takes a block number argument. - ChainDB is no longer passed via the Backend interface. - EventSystem now relies on HeaderByHash for reorgs in light client mode instead of reading from the chain database. eth/gasprice: - The LightPriceOracle now uses ethereum.ChainReader instead of ethapi.Backend. ethclient: - TransactionByHash is adapted for the last-minute API change which adds the isPending return value. internal/ethapi: - PublicTxPoolAPI is now called TxPoolDebugAPI, moved to its own file and talks to the transaction pool instead of using the main Backend. - The API no longer accesses the chain database directly. All access is mediated through Backend methods. - The backend is now split into three interfaces. Implementing Backend is mandatory but does not require the pending state. The other two (PendingState, TransactionInclusionBlock) are optional and discovered at runtime. les: - LesApiBackend is gone, LightEthereum gets all the methods. - Weird accessors copied from package eth are now gone as well. light: - TxPool.Stats now returns a queued count of zero. It implements the ethapi.TxPool interface and can be used with TxPoolDebugAPI. --- accounts/abi/bind/backends/simulated.go | 45 +- cmd/utils/flags.go | 2 +- contracts/release/release.go | 9 +- core/database_util.go | 8 +- core/database_util_test.go | 2 +- core/headerchain.go | 2 +- core/helper_test.go | 96 ---- core/state_transition.go | 49 ++ core/tx_pool.go | 9 +- eth/api.go | 2 +- eth/api_backend.go | 350 +++++++++----- eth/backend.go | 76 ++-- eth/bind.go | 136 ------ eth/downloader/api.go | 2 +- eth/downloader/downloader.go | 4 +- eth/filters/api.go | 14 +- eth/filters/filter.go | 20 +- eth/filters/filter_system.go | 32 +- eth/filters/filter_system_test.go | 24 +- eth/filters/filter_test.go | 16 +- eth/gasprice/lightprice.go | 11 +- ethclient/ethclient.go | 15 +- interfaces.go | 5 +- internal/ethapi/api.go | 580 +++++++++--------------- internal/ethapi/backend.go | 71 ++- internal/ethapi/txpool.go | 105 +++++ les/api_backend.go | 226 +++++---- les/backend.go | 30 +- les/server.go | 5 +- light/txpool.go | 5 +- 30 files changed, 938 insertions(+), 1013 deletions(-) delete mode 100644 core/helper_test.go delete mode 100644 eth/bind.go create mode 100644 internal/ethapi/txpool.go 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/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 f58fb0f156..f14ba13f19 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -31,7 +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/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" @@ -128,14 +127,13 @@ type Ethereum struct { pow *ethash.Ethash 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 @@ -219,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 @@ -243,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, @@ -255,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 } @@ -310,50 +310,58 @@ 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, }, }...) @@ -392,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. @@ -420,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() } @@ -522,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 daa28be058..28760fa517 100644 --- a/les/server.go +++ b/les/server.go @@ -29,6 +29,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/p2p" @@ -43,8 +44,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.