This commit is contained in:
Felix Lange 2016-11-13 11:34:53 +00:00 committed by GitHub
commit 27ca56101e
34 changed files with 938 additions and 2059 deletions

View file

@ -164,7 +164,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
if err != nil { if err != nil {
return nil, err 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 return rval, err
} }
@ -174,7 +174,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
defer b.mu.Unlock() defer b.mu.Unlock()
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) 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 return rval, err
} }
@ -200,32 +200,16 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
defer b.mu.Unlock() defer b.mu.Unlock()
defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) 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 return gas, err
} }
// callContract implemens common code between normal and pending contract calls. // callContract implemens common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary. // 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) { func (b *SimulatedBackend) callContract(call ethereum.CallMsg, block *types.Block, state *state.StateDB) ([]byte, *big.Int, error) {
// Ensure message is initialized properly. return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment {
if call.GasPrice == nil { return core.NewEnv(state, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
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
} }
// SendTransaction updates the pending block to include the given transaction. // 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) b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
return nil 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 }

View file

@ -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) { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
fullNode, err := eth.New(ctx, ethConf) fullNode, err := eth.New(ctx, ethConf)
if fullNode != nil && ethConf.LightServ > 0 { if fullNode != nil && ethConf.LightServ > 0 {
ls, _ := les.NewLesServer(fullNode, ethConf) ls, _ := les.NewLesServer(fullNode, stack.EventMux(), ethConf)
fullNode.AddLesServer(ls) fullNode.AddLesServer(ls)
} }
return fullNode, err return fullNode, err

File diff suppressed because one or more lines are too long

View file

@ -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 <http://www.gnu.org/licenses/>.
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: &registryAPIBackend{
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
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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<X> 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):], ""
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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)
}
}
}

View file

@ -27,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
@ -62,20 +61,20 @@ type ReleaseService struct {
// releases and notify the user of such. // releases and notify the user of such.
func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) {
// Retrieve the Ethereum service dependency to access the blockchain // Retrieve the Ethereum service dependency to access the blockchain
var apiBackend ethapi.Backend var apiBackend bind.ContractBackend
var ethereum *eth.Ethereum var ethereum *eth.Ethereum
if err := ctx.Service(&ethereum); err == nil { if err := ctx.Service(&ethereum); err == nil {
apiBackend = ethereum.ApiBackend apiBackend = ethereum
} else { } else {
var ethereum *les.LightEthereum var ethereum *les.LightEthereum
if err := ctx.Service(&ethereum); err == nil { if err := ctx.Service(&ethereum); err == nil {
apiBackend = ethereum.ApiBackend apiBackend = ethereum
} else { } else {
return nil, err return nil, err
} }
} }
// Construct the release service // Construct the release service
contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(apiBackend)) contract, err := NewReleaseOracle(config.Oracle, apiBackend)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -82,7 +82,7 @@ func GetCanonicalHash(db ethdb.Database, number uint64) common.Hash {
// missingNumber is returned by GetBlockNumber if no header with the // missingNumber is returned by GetBlockNumber if no header with the
// given block hash has been stored in the database // 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 // GetBlockNumber returns the block number assigned to a block hash
// if the corresponding header is present in the database // 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 { if len(data) != 8 {
data, _ := db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldHeaderSuffix...)) data, _ := db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldHeaderSuffix...))
if len(data) == 0 { if len(data) == 0 {
return missingNumber return MissingNumber
} }
header := new(types.Header) header := new(types.Header)
if err := rlp.Decode(bytes.NewReader(data), header); err != nil { 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 // GetTransaction retrieves a specific transaction from the database, along with
// its added positional metadata. // 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 // Retrieve the transaction itself from the database
data, _ := db.Get(hash.Bytes()) data, _ := db.Get(hash.Bytes())
if len(data) == 0 { 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 { if err := rlp.DecodeBytes(data, &meta); err != nil {
return nil, common.Hash{}, 0, 0 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 // GetReceipt returns a receipt by hash

View file

@ -368,7 +368,7 @@ func TestTransactionStorage(t *testing.T) {
if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil { if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil {
t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash()) t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
} else { } 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) 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() { if tx.String() != txn.String() {

View file

@ -123,7 +123,7 @@ func (hc *HeaderChain) GetBlockNumber(hash common.Hash) uint64 {
return cached.(uint64) return cached.(uint64)
} }
number := GetBlockNumber(hc.chainDb, hash) number := GetBlockNumber(hc.chainDb, hash)
if number != missingNumber { if number != MissingNumber {
hc.numberCache.Add(hash, number) hc.numberCache.Add(hash, number)
} }
return number return number

View file

@ -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 <http://www.gnu.org/licenses/>.
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
}

View file

@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"math/big" "math/big"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/logger" "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 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) { func (self *StateTransition) from() (vm.Account, error) {
var ( var (
f common.Address f common.Address
@ -300,3 +334,18 @@ func (self *StateTransition) refundGas() {
func (self *StateTransition) gasUsed() *big.Int { func (self *StateTransition) gasUsed() *big.Int {
return new(big.Int).Sub(self.initialGas, self.gas) 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 }

View file

@ -190,11 +190,12 @@ func (pool *TxPool) Stop() {
glog.V(logger.Info).Infoln("Transaction pool stopped") glog.V(logger.Info).Infoln("Transaction pool stopped")
} }
func (pool *TxPool) State() *state.ManagedState { // NonceAt returns the next valid nonce for the given account.
pool.mu.RLock() func (pool *TxPool) NonceAt(addr common.Address) uint64 {
defer pool.mu.RUnlock() 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 // Stats retrieves the current pool stats, namely the number of pending and the

View file

@ -521,7 +521,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
data: tx.Data(), data: tx.Data(),
} }
// Mutate the state if we haven't reached the tracing transaction yet // 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{}) 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())) _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
if err != nil { if err != nil {

View file

@ -17,189 +17,299 @@
package eth package eth
import ( import (
"errors"
"fmt"
"math/big" "math/big"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "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/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "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/internal/ethapi"
rpc "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// EthApiBackend implements ethapi.Backend for full nodes // Assert at compile time that this implementation has all optional API features.
type EthApiBackend struct { var (
eth *Ethereum _ ethapi.TransactionInclusionBlock = (*Ethereum)(nil)
gpo *gasprice.GasPriceOracle _ ethapi.PendingState = (*Ethereum)(nil)
} _ bind.ContractBackend = (*Ethereum)(nil)
_ bind.PendingContractCaller = (*Ethereum)(nil)
)
func (b *EthApiBackend) SetHead(number uint64) { var (
b.eth.blockchain.SetHead(number) 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) { // HeaderByNumber returns headers from the canonical chain.
// Pending block is only known by the miner func (eth *Ethereum) HeaderByNumber(ctx context.Context, num *big.Int) (*types.Header, error) {
if blockNr == rpc.PendingBlockNumber { if num == nil {
block, _ := b.eth.miner.Pending() return eth.blockchain.CurrentBlock().Header(), nil
return block.Header(), nil
} }
// Otherwise resolve and return the block if h := eth.blockchain.GetHeaderByNumber(num.Uint64()); h != nil {
if blockNr == rpc.LatestBlockNumber { return h, nil
return b.eth.blockchain.CurrentBlock().Header(), 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) { // HeaderByHash returns the header with the given hash.
// Pending block is only known by the miner func (eth *Ethereum) HeaderByHash(ctx context.Context, blockhash common.Hash) (*types.Header, error) {
if blockNr == rpc.PendingBlockNumber { if h := eth.blockchain.GetHeaderByHash(blockhash); h != nil {
block, _ := b.eth.miner.Pending() return h, nil
return block, nil
} }
// Otherwise resolve and return the block return nil, ErrBlockNotFound
if blockNr == rpc.LatestBlockNumber { }
return b.eth.blockchain.CurrentBlock(), nil
// 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 if b := eth.blockchain.GetBlockByNumber(num.Uint64()); b != nil {
} return b, 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
} }
// Otherwise resolve the block number and return its state return nil, ErrBlockNotFound
header, err := b.HeaderByNumber(ctx, blockNr) }
if header == nil || err != nil {
return nil, nil, err // 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 nil, ErrBlockNotFound
return EthApiState{stateDb}, header, err
} }
func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { // BlockReceipts returns all receipts contained in the given block.
return b.eth.blockchain.GetBlockByHash(blockHash), nil 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) { // TransactionCount returns the number of transactions in a block.
return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil 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 { // TransactionInBlock returns the i'th transaction in the given block.
return b.eth.blockchain.GetTdByHash(blockHash) 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) { // TransactionByHash returns the transaction with the given hash.
statedb := state.(EthApiState).state func (eth *Ethereum) TransactionByHash(ctx context.Context, txhash common.Hash) (tx *types.Transaction, isPending bool, err error) {
addr, _ := msg.From() if tx = eth.txPool.Get(txhash); tx != nil {
from := statedb.GetOrNewStateObject(addr) return tx, true, nil
from.SetBalance(common.MaxBig) }
vmError := func() error { return nil } if tx, _, _, _ = core.GetTransaction(eth.chainDb, txhash); tx != nil {
return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil return tx, false, nil
}
return nil, false, ErrTxNotFound
} }
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { // TransactionInclusionBlock returns the block in which the given transaction was included.
b.eth.txMu.Lock() func (eth *Ethereum) TransactionInclusionBlock(txhash common.Hash) (bhash common.Hash, bnum uint64, index int, err error) {
defer b.eth.txMu.Unlock() var tx *types.Transaction
if tx, bhash, bnum, index = core.GetTransaction(eth.chainDb, txhash); tx == nil {
b.eth.txPool.SetLocal(signedTx) err = ErrTxNotFound
return b.eth.txPool.Add(signedTx) }
return bhash, bnum, index, err
} }
func (b *EthApiBackend) RemoveTx(txHash common.Hash) { // TransactionReceipt returns the receipt of a transaction.
b.eth.txMu.Lock() func (eth *Ethereum) TransactionReceipt(ctx context.Context, txhash common.Hash) (*types.Receipt, error) {
defer b.eth.txMu.Unlock() r := core.GetReceipt(eth.chainDb, txhash)
if r == nil {
b.eth.txPool.Remove(txHash) return nil, ErrTxNotFound
}
return r, nil
} }
func (b *EthApiBackend) GetPoolTransactions() types.Transactions { // BlockTD returns the total difficulty of a certain block.
b.eth.txMu.Lock() func (eth *Ethereum) BlockTD(blockhash common.Hash) *big.Int {
defer b.eth.txMu.Unlock() 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 var txs types.Transactions
for _, batch := range b.eth.txPool.Pending() { for _, batch := range eth.txPool.Pending() {
txs = append(txs, batch...) txs = append(txs, batch...)
} }
return txs return txs
} }
func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction { // SendTransaction queues a transaction in the pool.
b.eth.txMu.Lock() func (eth *Ethereum) SendTransaction(ctx context.Context, signedTx *types.Transaction) error {
defer b.eth.txMu.Unlock() 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) { // SyncProgress returns sync status information.
b.eth.txMu.Lock() func (b *Ethereum) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) {
defer b.eth.txMu.Unlock() return b.protocolManager.downloader.Progress(), nil
return b.eth.txPool.State().GetNonce(addr), nil
} }
func (b *EthApiBackend) Stats() (pending int, queued int) { // SuggestGasPrice returns a suitable gas price based on the content of recently seen blocks.
b.eth.txMu.Lock() func (eth *Ethereum) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
defer b.eth.txMu.Unlock() return eth.gpo.SuggestPrice(), nil
return b.eth.txPool.Stats()
} }
func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { // RemoveTransaction removes the given transaction from the local pool.
b.eth.txMu.Lock() func (eth *Ethereum) RemoveTransaction(txHash common.Hash) {
defer b.eth.txMu.Unlock() eth.txMu.Lock()
defer eth.txMu.Unlock()
return b.eth.TxPool().Content() eth.txPool.Remove(txHash)
} }
func (b *EthApiBackend) Downloader() *downloader.Downloader { // ProtocolVersion returns the active protocol version.
return b.eth.Downloader() func (eth *Ethereum) ProtocolVersion() int {
return int(eth.protocolManager.SubProtocols[0].Version)
} }
func (b *EthApiBackend) ProtocolVersion() int { // AccountManager returns the internal account manager that accesses the data directory.
return b.eth.EthVersion() // 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) { // ResetHeadBlock resets the blockchain to the given number.
return b.gpo.SuggestPrice(), nil // 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 { func (eth *Ethereum) EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) {
return b.eth.ChainDb() block, state := eth.miner.Pending()
_, gas, err := eth.callContract(call, block.Header(), state)
return gas, err
} }
func (b *EthApiBackend) EventMux() *event.TypeMux { func (eth *Ethereum) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) {
return b.eth.EventMux() block, state := eth.miner.Pending()
val, _, err := eth.callContract(call, block.Header(), state)
return val, err
} }
func (b *EthApiBackend) AccountManager() *accounts.Manager { func (eth *Ethereum) CallContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, error) {
return b.eth.AccountManager() 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 { func (eth *Ethereum) callContract(call ethereum.CallMsg, head *types.Header, state *state.StateDB) ([]byte, *big.Int, error) {
state *state.StateDB return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment {
} return core.NewEnv(state, eth.chainConfig, eth.blockchain, msg, head, vm.Config{})
})
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
} }

View file

@ -31,8 +31,6 @@ import (
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "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"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
@ -127,17 +125,15 @@ type Ethereum struct {
eventMux *event.TypeMux eventMux *event.TypeMux
pow *ethash.Ethash pow *ethash.Ethash
httpclient *httpclient.HTTPClient
accountManager *accounts.Manager accountManager *accounts.Manager
ApiBackend *EthApiBackend
miner *miner.Miner miner *miner.Miner
Mining bool Mining bool
MinerThreads int MinerThreads int
AutoDAG bool AutoDAG bool
autodagquit chan bool autodagquit chan bool
etherbase common.Address etherbase common.Address
gpo *gasprice.GasPriceOracle
solcPath string solcPath string
NatSpec bool NatSpec bool
@ -173,7 +169,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
pow: pow, pow: pow,
shutdownChan: make(chan bool), shutdownChan: make(chan bool),
stopDbUpgrade: stopDbUpgrade, stopDbUpgrade: stopDbUpgrade,
httpclient: httpclient.New(config.DocRoot),
netVersionId: config.NetworkId, netVersionId: config.NetworkId,
NatSpec: config.NatSpec, NatSpec: config.NatSpec,
PowTest: config.PowTest, PowTest: config.PowTest,
@ -222,14 +217,14 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
ForceJit: config.ForceJit, 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 != nil {
if err == core.ErrNoGenesis { if err == core.ErrNoGenesis {
return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`) return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`)
} }
return nil, err 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 eth.txPool = newPool
maxPeers := config.MaxPeers maxPeers := config.MaxPeers
@ -246,10 +241,13 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { 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 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.SetGasPrice(config.GasPrice)
eth.miner.SetExtra(config.ExtraData) eth.miner.SetExtra(config.ExtraData)
// Set up the gas price oracle.
gpoParams := &gasprice.GpoParams{ gpoParams := &gasprice.GpoParams{
GpoMinGasPrice: config.GpoMinGasPrice, GpoMinGasPrice: config.GpoMinGasPrice,
GpoMaxGasPrice: config.GpoMaxGasPrice, GpoMaxGasPrice: config.GpoMaxGasPrice,
@ -258,8 +256,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
GpobaseStepUp: config.GpobaseStepUp, GpobaseStepUp: config.GpobaseStepUp,
GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, GpobaseCorrectionFactor: config.GpobaseCorrectionFactor,
} }
gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) eth.gpo = gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams)
eth.ApiBackend = &EthApiBackend{eth, gpo}
return eth, nil return eth, nil
} }
@ -313,55 +310,59 @@ func CreatePoW(config *Config) (*ethash.Ethash, error) {
// APIs returns the collection of RPC services the ethereum package offers. // APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else. // NOTE, some of these services probably need to be moved to somewhere else.
func (s *Ethereum) APIs() []rpc.API { func (eth *Ethereum) APIs() []rpc.API {
return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ return append(ethapi.GetAPIs(eth, eth.solcPath), []rpc.API{
{ {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: NewPublicEthereumAPI(s), Service: NewPublicEthereumAPI(eth),
Public: true, Public: true,
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: NewPublicMinerAPI(s), Service: NewPublicMinerAPI(eth),
Public: true, Public: true,
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), Service: downloader.NewPublicDownloaderAPI(eth.protocolManager.downloader, eth.eventMux),
Public: true, Public: true,
}, { }, {
Namespace: "miner", Namespace: "miner",
Version: "1.0", Version: "1.0",
Service: NewPrivateMinerAPI(s), Service: NewPrivateMinerAPI(eth),
Public: false, Public: false,
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: filters.NewPublicFilterAPI(s.ApiBackend, false), Service: filters.NewPublicFilterAPI(eth, eth.eventMux, eth.chainDb, false),
Public: true, Public: true,
}, { }, {
Namespace: "admin", Namespace: "admin",
Version: "1.0", Version: "1.0",
Service: NewPrivateAdminAPI(s), Service: NewPrivateAdminAPI(eth),
}, { }, {
Namespace: "debug", Namespace: "debug",
Version: "1.0", Version: "1.0",
Service: NewPublicDebugAPI(s), Service: NewPublicDebugAPI(eth),
Public: true, Public: true,
}, { }, {
Namespace: "debug", Namespace: "debug",
Version: "1.0", 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", Namespace: "net",
Version: "1.0", Version: "1.0",
Service: s.netRPCService, Service: eth.netRPCService,
Public: true, Public: true,
}, {
Namespace: "admin",
Version: "1.0",
Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager),
}, },
}...) }...)
} }
@ -399,20 +400,16 @@ func (s *Ethereum) StartMining(threads int) error {
return nil return nil
} }
func (s *Ethereum) StopMining() { s.miner.Stop() } func (s *Ethereum) StopMining() { s.miner.Stop() }
func (s *Ethereum) IsMining() bool { return s.miner.Mining() } func (s *Ethereum) IsMining() bool { return s.miner.Mining() }
func (s *Ethereum) Miner() *miner.Miner { return s.miner }
func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } // Deprecated Accessors
func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain }
func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb }
func (s *Ethereum) Pow() *ethash.Ethash { return s.pow } func (s *Ethereum) TxPool() *core.TxPool { return s.txPool }
func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } func (s *Ethereum) Pow() *ethash.Ethash { return s.pow }
func (s *Ethereum) IsListening() bool { return true } // Always listening func (s *Ethereum) Miner() *miner.Miner { return s.miner }
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 }
// Protocols implements node.Service, returning all the currently configured // Protocols implements node.Service, returning all the currently configured
// network protocols to start. // network protocols to start.
@ -427,7 +424,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol {
// Start implements node.Service, starting all internal goroutines needed by the // Start implements node.Service, starting all internal goroutines needed by the
// Ethereum protocol implementation. // Ethereum protocol implementation.
func (s *Ethereum) Start(srvr *p2p.Server) error { 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 { if s.AutoDAG {
s.StartAutoDAG() s.StartAutoDAG()
} }
@ -529,12 +526,6 @@ func (self *Ethereum) StopAutoDAG() {
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir) 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) // dagFiles(epoch) returns the two alternative DAG filenames (not a path)
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])> // 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
func dagFiles(epoch uint64) (string, string) { func dagFiles(epoch uint64) (string, string) {

View file

@ -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 <http://www.gnu.org/licenses/>.
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
}

View file

@ -76,7 +76,7 @@ func (api *PublicDownloaderAPI) eventLoop() {
case StartEvent: case StartEvent:
notification = &SyncingResult{ notification = &SyncingResult{
Syncing: true, Syncing: true,
Status: api.d.Progress(), Status: *api.d.Progress(),
} }
case DoneEvent, FailedEvent: case DoneEvent, FailedEvent:
notification = false notification = false

View file

@ -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 // 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 // of processed and the total number of known states are also returned. Otherwise
// these are zero. // 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 // Fetch the pending state count outside of the lock to prevent unforeseen deadlocks
pendingStates := uint64(d.queue.PendingNodeData()) pendingStates := uint64(d.queue.PendingNodeData())
@ -229,7 +229,7 @@ func (d *Downloader) Progress() ethereum.SyncProgress {
case LightSync: case LightSync:
current = d.headHeader().Number.Uint64() current = d.headHeader().Number.Uint64()
} }
return ethereum.SyncProgress{ return &ethereum.SyncProgress{
StartingBlock: d.syncStatsChainOrigin, StartingBlock: d.syncStatsChainOrigin,
CurrentBlock: current, CurrentBlock: current,
HighestBlock: d.syncStatsChainHeight, HighestBlock: d.syncStatsChainHeight,

View file

@ -56,20 +56,20 @@ type PublicFilterAPI struct {
useMipMap bool useMipMap bool
mux *event.TypeMux mux *event.TypeMux
quit chan struct{} quit chan struct{}
chainDb ethdb.Database chaindb ethdb.Database
events *EventSystem events *EventSystem
filtersMu sync.Mutex filtersMu sync.Mutex
filters map[rpc.ID]*filter filters map[rpc.ID]*filter
} }
// NewPublicFilterAPI returns a new PublicFilterAPI instance. // 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{ api := &PublicFilterAPI{
backend: backend, backend: backend,
useMipMap: !lightMode, useMipMap: !lightMode,
mux: backend.EventMux(), mux: mux,
chainDb: backend.ChainDb(), chaindb: chaindb,
events: NewEventSystem(backend.EventMux(), backend, lightMode), events: NewEventSystem(mux, backend, lightMode),
filters: make(map[rpc.ID]*filter), 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()) 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.SetBeginBlock(crit.FromBlock.Int64())
filter.SetEndBlock(crit.ToBlock.Int64()) filter.SetEndBlock(crit.ToBlock.Int64())
filter.SetAddresses(crit.Addresses) filter.SetAddresses(crit.Addresses)
@ -366,7 +366,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log
return []Log{}, nil return []Log{}, nil
} }
filter := New(api.backend, api.useMipMap) filter := New(api.backend, api.chaindb, api.useMipMap)
filter.SetBeginBlock(f.crit.FromBlock.Int64()) filter.SetBeginBlock(f.crit.FromBlock.Int64())
filter.SetEndBlock(f.crit.ToBlock.Int64()) filter.SetEndBlock(f.crit.ToBlock.Int64())
filter.SetAddresses(f.crit.Addresses) filter.SetAddresses(f.crit.Addresses)

View file

@ -18,22 +18,20 @@ package filters
import ( import (
"math" "math"
"math/big"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
type Backend interface { type Backend interface {
ChainDb() ethdb.Database HeaderByHash(context.Context, common.Hash) (*types.Header, error)
EventMux() *event.TypeMux HeaderByNumber(context.Context, *big.Int) (*types.Header, error)
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) BlockReceipts(ctx context.Context, blockhash common.Hash, blocknum uint64) ([]*types.Receipt, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
} }
// Filter can be used to retrieve and filter logs // 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 // New creates a new filter which uses a bloom filter on blocks to figure out whether
// a particular block is interesting or not. // 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{ return &Filter{
backend: backend, backend: backend,
useMipMap: useMipMap, 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 // Run filters logs with the current parameters set
func (f *Filter) Find(ctx context.Context) ([]Log, error) { 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 { if head == nil {
return nil, 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) { func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) {
for i := start; i <= end; i++ { 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 { if header == nil || err != nil {
return logs, err return logs, err
} }
@ -150,7 +148,7 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er
// current parameters // current parameters
if f.bloomFilter(header.Bloom) { if f.bloomFilter(header.Bloom) {
// Get the logs of the block // 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 { if err != nil {
return nil, err return nil, err
} }

View file

@ -30,6 +30,8 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/event" "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" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "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)) { func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) {
oldh := es.lastHead oldh := es.lastHead
es.lastHead = newHeader
if oldh == nil { if oldh == nil {
return return
} }
newh := newHeader ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
// find common ancestor, create list of rolled back and new block hashes // 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() { for oldh.Hash() != newh.Hash() {
if oldh.Number.Uint64() >= newh.Number.Uint64() { if oldh.Number.Uint64() >= newh.Number.Uint64() {
oldHeaders = append(oldHeaders, oldh) 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() { if oldh.Number.Uint64() < newh.Number.Uint64() {
newHeaders = append(newHeaders, newh) 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 { if newh == nil {
// happens when CHT syncing, nothing to do // happens when CHT syncing, nothing to do
newh = oldh 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 // roll back old blocks
for _, h := range oldHeaders { for _, h := range oldHeaders {
callBack(h, true) callBack(h, true)
@ -330,6 +349,7 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func
for i := len(newHeaders) - 1; i >= 0; i-- { for i := len(newHeaders) - 1; i >= 0; i-- {
callBack(newHeaders[i], false) callBack(newHeaders[i], false)
} }
es.lastHead = newh
} }
// filter logs of a single header in light client mode // 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") //fmt.Println("bloom match")
// Get the logs of the block // Get the logs of the block
ctx, _ := context.WithTimeout(context.Background(), time.Second*5) 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 { if err != nil {
return nil return nil
} }

View file

@ -37,7 +37,7 @@ var (
mux = new(event.TypeMux) mux = new(event.TypeMux)
db, _ = ethdb.NewMemDatabase() db, _ = ethdb.NewMemDatabase()
backend = &testBackend{mux, db} backend = &testBackend{mux, db}
api = NewPublicFilterAPI(backend, false) api = NewPublicFilterAPI(backend, mux, db, false)
) )
type testBackend struct { type testBackend struct {
@ -45,30 +45,26 @@ type testBackend struct {
db ethdb.Database db ethdb.Database
} }
func (b *testBackend) ChainDb() ethdb.Database { func (b *testBackend) HeaderByNumber(ctx context.Context, blockNum *big.Int) (*types.Header, error) {
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) {
var hash common.Hash var hash common.Hash
var num uint64 var num uint64
if blockNr == rpc.LatestBlockNumber { if blockNum == nil {
hash = core.GetHeadBlockHash(b.db) hash = core.GetHeadBlockHash(b.db)
num = core.GetBlockNumber(b.db, hash) num = core.GetBlockNumber(b.db, hash)
} else { } else {
num = uint64(blockNr) num = blockNum.Uint64()
hash = core.GetCanonicalHash(b.db, num) hash = core.GetCanonicalHash(b.db, num)
} }
return core.GetHeader(b.db, hash, num), nil 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) 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. // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events.

View file

@ -103,7 +103,7 @@ func BenchmarkMipmaps(b *testing.B) {
} }
b.ResetTimer() b.ResetTimer()
filter := New(backend, true) filter := New(backend, db, true)
filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4}) filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) 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.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
@ -216,7 +216,7 @@ func TestFilters(t *testing.T) {
t.Error("expected 4 log, got", len(logs)) t.Error("expected 4 log, got", len(logs))
} }
filter = New(backend, true) filter = New(backend, db, true)
filter.SetAddresses([]common.Address{addr}) filter.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
filter.SetBeginBlock(900) 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]) 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.SetAddresses([]common.Address{addr})
filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}})
filter.SetBeginBlock(990) 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]) 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.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}})
filter.SetBeginBlock(1) filter.SetBeginBlock(1)
filter.SetEndBlock(10) filter.SetEndBlock(10)
@ -253,7 +253,7 @@ func TestFilters(t *testing.T) {
} }
failHash := common.BytesToHash([]byte("fail")) failHash := common.BytesToHash([]byte("fail"))
filter = New(backend, true) filter = New(backend, db, true)
filter.SetTopics([][]common.Hash{[]common.Hash{failHash}}) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -264,7 +264,7 @@ func TestFilters(t *testing.T) {
} }
failAddr := common.BytesToAddress([]byte("failmenow")) failAddr := common.BytesToAddress([]byte("failmenow"))
filter = New(backend, true) filter = New(backend, db, true)
filter.SetAddresses([]common.Address{failAddr}) filter.SetAddresses([]common.Address{failAddr})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)
@ -274,7 +274,7 @@ func TestFilters(t *testing.T) {
t.Error("expected 0 log, got", len(logs)) 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.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}})
filter.SetBeginBlock(0) filter.SetBeginBlock(0)
filter.SetEndBlock(-1) filter.SetEndBlock(-1)

View file

@ -21,9 +21,8 @@ import (
"sort" "sort"
"sync" "sync"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -38,7 +37,7 @@ const (
// LightPriceOracle recommends gas prices based on the content of recent // LightPriceOracle recommends gas prices based on the content of recent
// blocks. Suitable for both light and full clients. // blocks. Suitable for both light and full clients.
type LightPriceOracle struct { type LightPriceOracle struct {
backend ethapi.Backend backend ethereum.ChainReader
lastHead common.Hash lastHead common.Hash
lastPrice *big.Int lastPrice *big.Int
cacheLock sync.RWMutex cacheLock sync.RWMutex
@ -46,7 +45,7 @@ type LightPriceOracle struct {
} }
// NewLightPriceOracle returns a new oracle. // NewLightPriceOracle returns a new oracle.
func NewLightPriceOracle(backend ethapi.Backend) *LightPriceOracle { func NewLightPriceOracle(backend ethereum.ChainReader) *LightPriceOracle {
return &LightPriceOracle{ return &LightPriceOracle{
backend: backend, backend: backend,
lastPrice: big.NewInt(LpoDefaultPrice), lastPrice: big.NewInt(LpoDefaultPrice),
@ -60,7 +59,7 @@ func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error
lastPrice := self.lastPrice lastPrice := self.lastPrice
self.cacheLock.RUnlock() self.cacheLock.RUnlock()
head, _ := self.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) head, _ := self.backend.HeaderByNumber(ctx, nil)
headHash := head.Hash() headHash := head.Hash()
if headHash == lastHead { if headHash == lastHead {
return lastPrice, nil return lastPrice, nil
@ -132,7 +131,7 @@ type lpResult struct {
// getLowestPrice calculates the lowest transaction gas price in a given block // 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. // 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) { 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 { if block == nil {
chn <- lpResult{nil, err} chn <- lpResult{nil, err}
return return

View file

@ -143,15 +143,18 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H
} }
// TransactionByHash returns the transaction with the given hash. // TransactionByHash returns the transaction with the given hash.
func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, error) { func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
var tx *types.Transaction var rtx struct {
err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) *types.Transaction
BlockHash *common.Hash
}
err = ec.c.CallContext(ctx, &rtx, "eth_getTransactionByHash", hash)
if err == nil { if err == nil {
if _, r, _ := tx.SignatureValues(); r == nil { if _, r, _ := rtx.SignatureValues(); r == nil {
return nil, fmt.Errorf("server returned transaction without signature") 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. // TransactionCount returns the total number of transactions in the given block.

View file

@ -53,8 +53,8 @@ type ChainReader interface {
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error)
TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, 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) 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 // 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) 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. // PendingContractCaller can be used to perform calls against the pending state.
type PendingContractCaller interface { type PendingContractCaller interface {
PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error) PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error)

View file

@ -20,15 +20,16 @@ import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"math/big" "math/big"
"strings" "strings"
"time" "time"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "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/types"
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
@ -45,6 +46,8 @@ import (
const defaultGas = uint64(90000) 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. // PublicEthereumAPI provides an API to access Ethereum related information.
// It offers only methods that operate on public data that is freely available to anyone. // It offers only methods that operate on public data that is freely available to anyone.
type PublicEthereumAPI struct { type PublicEthereumAPI struct {
@ -58,7 +61,7 @@ func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI {
// GasPrice returns a suggestion for a gas price. // GasPrice returns a suggestion for a gas price.
func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*big.Int, error) { 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 // 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 // - highestBlock: block number of the highest block header this node has received from peers
// - pulledStates: number of state entries processed until now // - pulledStates: number of state entries processed until now
// - knownStates: number of known state entries that still need to be pulled // - knownStates: number of known state entries that still need to be pulled
func (s *PublicEthereumAPI) Syncing() (interface{}, error) { func (s *PublicEthereumAPI) Syncing(ctx context.Context) (interface{}, error) {
progress := s.b.Downloader().Progress() progress, err := s.b.SyncProgress(ctx)
if err != nil {
return nil, err
}
// Return not syncing if the synchronisation already completed // Return not syncing if the synchronisation already completed
if progress.CurrentBlock >= progress.HighestBlock { if progress.CurrentBlock >= progress.HighestBlock {
@ -90,87 +96,6 @@ func (s *PublicEthereumAPI) Syncing() (interface{}, error) {
}, nil }, 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. // PublicAccountAPI provides an API to access accounts managed by this node.
// It offers only methods that can retrieve accounts. // It offers only methods that can retrieve accounts.
type PublicAccountAPI struct { type PublicAccountAPI struct {
@ -265,7 +190,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
} }
if args.Nonce == nil { if args.Nonce == nil {
nonce, err := s.b.GetPoolNonce(ctx, args.From) nonce, err := s.b.PendingNonceAt(ctx, args.From)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -366,28 +291,16 @@ func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI {
// BlockNumber returns the block number of the chain head. // BlockNumber returns the block number of the chain head.
func (s *PublicBlockChainAPI) BlockNumber() *big.Int { 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 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 // 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. // 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) { 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 { if block != nil {
response, err := s.rpcOutputBlock(block, true, fullTx) response, err := s.rpcOutputBlock(ctx, block, true, fullTx)
if err == nil && blockNr == rpc.PendingBlockNumber { if err == nil && blockNr == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields // Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "logsBloom", "miner"} { for _, field := range []string{"hash", "nonce", "logsBloom", "miner"} {
@ -399,12 +312,27 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.
return nil, err 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 // 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. // detail, otherwise only the transaction hash is returned.
func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { 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 { if block != nil {
return s.rpcOutputBlock(block, true, fullTx) return s.rpcOutputBlock(ctx, block, true, fullTx)
} }
return nil, err 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 // 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. // 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) { 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 { if block != nil {
uncles := block.Uncles() uncles := block.Uncles()
if index.Int() < 0 || index.Int() >= len(uncles) { if index.Int() < 0 || index.Int() >= len(uncles) {
@ -420,7 +348,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context,
return nil, nil return nil, nil
} }
block = types.NewBlockWithHeader(uncles[index.Int()]) block = types.NewBlockWithHeader(uncles[index.Int()])
return s.rpcOutputBlock(block, false, false) return s.rpcOutputBlock(ctx, block, false, false)
} }
return nil, err 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 // 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. // 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) { 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 { if block != nil {
uncles := block.Uncles() uncles := block.Uncles()
if index.Int() < 0 || index.Int() >= len(uncles) { if index.Int() < 0 || index.Int() >= len(uncles) {
@ -436,14 +364,14 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, b
return nil, nil return nil, nil
} }
block = types.NewBlockWithHeader(uncles[index.Int()]) block = types.NewBlockWithHeader(uncles[index.Int()])
return s.rpcOutputBlock(block, false, false) return s.rpcOutputBlock(ctx, block, false, false)
} }
return nil, err return nil, err
} }
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number // 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 { 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 rpc.NewHexNumber(len(block.Uncles()))
} }
return nil 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 // 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 { 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 rpc.NewHexNumber(len(block.Uncles()))
} }
return nil 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. // 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) { func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (string, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) var code []byte
if state == nil || err != nil { var err error
return "", err 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 "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 // GetStorageAt returns the storage from the state at the given address, key and
// block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block
// numbers are also allowed. // numbers are also allowed.
func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (string, error) { func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key common.Hash, blockNr rpc.BlockNumber) (string, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) var val []byte
if state == nil || err != nil { 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 return "0x", err
} }
res, err := state.GetState(ctx, address, common.HexToHash(key)) return common.ToHex(val), nil
if err != nil {
return "0x", err
}
return res.Hex(), 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. // CallArgs represents the arguments for a call.
type CallArgs struct { type CallArgs struct {
From common.Address `json:"from"` From common.Address `json:"from"`
@ -512,74 +458,43 @@ type CallArgs struct {
Gas rpc.HexNumber `json:"gas"` Gas rpc.HexNumber `json:"gas"`
GasPrice rpc.HexNumber `json:"gasPrice"` GasPrice rpc.HexNumber `json:"gasPrice"`
Value rpc.HexNumber `json:"value"` 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) { func (args CallArgs) Msg() ethereum.CallMsg {
defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now()) return ethereum.CallMsg{
From: args.From,
state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) To: args.To,
if state == nil || err != nil { Gas: args.Gas.BigInt(),
return "0x", common.Big0, err 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. // 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. // 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) { func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, error) {
result, _, err := s.doCall(ctx, args, blockNr) var result []byte
return result, err 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. // 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) { 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 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 // 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 // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes. // 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 head := b.Header() // copies the header once
fields := map[string]interface{}{ fields := map[string]interface{}{
"number": rpc.NewHexNumber(head.Number), "number": rpc.NewHexNumber(head.Number),
@ -652,7 +567,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
"stateRoot": head.Root, "stateRoot": head.Root,
"miner": head.Coinbase, "miner": head.Coinbase,
"difficulty": rpc.NewHexNumber(head.Difficulty), "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), "extraData": rpc.HexBytes(head.Extra),
"size": rpc.NewHexNumber(b.Size().Int64()), "size": rpc.NewHexNumber(b.Size().Int64()),
"gasLimit": rpc.NewHexNumber(head.GasLimit), "gasLimit": rpc.NewHexNumber(head.GasLimit),
@ -663,22 +578,15 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
} }
if inclTx { 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() txs := b.Transactions()
transactions := make([]interface{}, len(txs)) transactions := make([]interface{}, len(txs))
var err error
for i, tx := range b.Transactions() { for i, tx := range b.Transactions() {
if transactions[i], err = formatTx(tx); err != nil { if fullTx {
return nil, err rtx := newRPCTransaction(tx)
rtx.setInclusionBlock(b.Hash(), b.NumberU64(), i)
transactions = append(transactions, rtx)
} else {
transactions = append(transactions, tx.Hash())
} }
} }
fields["transactions"] = transactions fields["transactions"] = transactions
@ -712,8 +620,14 @@ type RPCTransaction struct {
S *rpc.HexNumber `json:"s"` S *rpc.HexNumber `json:"s"`
} }
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation func (tx *RPCTransaction) setInclusionBlock(blockhash common.Hash, blocknum uint64, index int) {
func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { 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() from, _ := tx.FromFrontier()
v, r, s := tx.SignatureValues() v, r, s := tx.SignatureValues()
return &RPCTransaction{ return &RPCTransaction{
@ -732,33 +646,13 @@ func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction {
} }
// newRPCTransaction returns a transaction that will serialize to the RPC representation. // 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()) { if txIndex >= 0 && txIndex < len(b.Transactions()) {
tx := b.Transactions()[txIndex] tx := newRPCTransaction(b.Transactions()[txIndex])
from, err := tx.FromFrontier() tx.setInclusionBlock(b.Hash(), b.NumberU64(), txIndex)
if err != nil { return tx
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
} }
return nil
return nil, nil
} }
// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. // 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] tx := b.Transactions()[txIndex]
return rlp.EncodeToBytes(tx) 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 return nil, nil
} }
@ -792,27 +674,9 @@ func NewPublicTransactionPoolAPI(b Backend) *PublicTransactionPoolAPI {
return &PublicTransactionPoolAPI{b} 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. // 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 { 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 rpc.NewHexNumber(len(block.Transactions()))
} }
return nil 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. // 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 { func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *rpc.HexNumber {
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { count, err := s.b.TransactionCount(ctx, blockHash)
return rpc.NewHexNumber(len(block.Transactions())) if err == nil {
return rpc.NewHexNumber(count)
} }
return nil return nil
} }
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. // 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) { func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) *RPCTransaction {
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil {
return newRPCTransactionFromBlockIndex(block, index.Int()) return newRPCTransactionFromBlockIndex(block, index.Int())
} }
return nil, nil return nil
} }
// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. // 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) { func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) *RPCTransaction {
if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil {
return newRPCTransactionFromBlockIndex(block, index.Int()) return newRPCTransactionFromBlockIndex(block, index.Int())
} }
return nil, nil return nil
} }
// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. // 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) { 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 newRPCRawTransactionFromBlockIndex(block, index.Int())
} }
return nil, nil 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. // 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) { 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 newRPCRawTransactionFromBlockIndex(block, index.Int())
} }
return nil, nil 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 // 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) { func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) {
state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) var nonce uint64
if state == nil || err != nil { var err error
return nil, err 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 { if err != nil {
return nil, err return nil, err
} }
@ -894,82 +764,50 @@ func getTransactionBlockData(chainDb ethdb.Database, txHash common.Hash) (common
} }
// GetTransactionByHash returns the transaction for the given hash // GetTransactionByHash returns the transaction for the given hash
func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txHash common.Hash) (*RPCTransaction, error) { func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txhash common.Hash) (*RPCTransaction, error) {
var tx *types.Transaction tx, isPending, err := s.b.TransactionByHash(ctx, txhash)
var isPending bool if tx == nil || err != nil {
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 {
return nil, nil return nil, nil
} }
rtx := newRPCTransaction(tx)
if isPending { 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)
} }
return rtx, nil
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
} }
// GetRawTransactionByHash returns the bytes of the transaction for the given hash. // GetRawTransactionByHash returns the bytes of the transaction for the given hash.
func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txHash common.Hash) (rpc.HexBytes, error) { func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txhash common.Hash) (rpc.HexBytes, error) {
var tx *types.Transaction tx, _, err := s.b.TransactionByHash(ctx, txhash)
var err error if tx == nil || err != nil {
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 {
return nil, nil return nil, nil
} }
return rlp.EncodeToBytes(tx) return rlp.EncodeToBytes(tx)
} }
// GetTransactionReceipt returns the transaction receipt for the given transaction hash. // GetTransactionReceipt returns the transaction receipt for the given transaction hash.
func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (map[string]interface{}, error) { func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, txhash common.Hash) (map[string]interface{}, error) {
receipt := core.GetReceipt(s.b.ChainDb(), txHash) receipt, err := s.b.TransactionReceipt(ctx, 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)
if err != nil { 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 return nil, nil
} }
tx, isPending, err := s.b.TransactionByHash(ctx, txhash)
txBlock, blockIndex, index, err := getTransactionBlockData(s.b.ChainDb(), txHash)
if err != nil { 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, err := tx.FromFrontier()
if err != nil {
glog.V(logger.Debug).Infof("%v\n", err)
return nil, nil return nil, nil
} }
from, _ := tx.FromFrontier()
fields := map[string]interface{}{ fields := map[string]interface{}{
"root": rpc.HexBytes(receipt.PostState), "root": rpc.HexBytes(receipt.PostState),
"blockHash": txBlock, "transactionHash": txhash,
"blockNumber": rpc.NewHexNumber(blockIndex),
"transactionHash": txHash,
"transactionIndex": rpc.NewHexNumber(index),
"from": from, "from": from,
"to": tx.To(), "to": tx.To(),
"gasUsed": rpc.NewHexNumber(receipt.GasUsed), "gasUsed": rpc.NewHexNumber(receipt.GasUsed),
@ -978,10 +816,21 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma
"logs": receipt.Logs, "logs": receipt.Logs,
"logsBloom": receipt.Bloom, "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 { if receipt.Logs == nil {
fields["logs"] = []vm.Logs{} fields["logs"] = []vm.Logs{}
} }
// If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation
if receipt.ContractAddress != (common.Address{}) { if receipt.ContractAddress != (common.Address{}) {
fields["contractAddress"] = receipt.ContractAddress fields["contractAddress"] = receipt.ContractAddress
} }
@ -1014,7 +863,7 @@ func prepareSendTxArgs(ctx context.Context, args SendTxArgs, b Backend) (SendTxA
args.Gas = rpc.NewHexNumber(defaultGas) args.Gas = rpc.NewHexNumber(defaultGas)
} }
if args.GasPrice == nil { if args.GasPrice == nil {
price, err := b.SuggestPrice(ctx) price, err := b.SuggestGasPrice(ctx)
if err != nil { if err != nil {
return args, err return args, err
} }
@ -1033,7 +882,7 @@ func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, si
return common.Hash{}, err return common.Hash{}, err
} }
if err := b.SendTx(ctx, signedTx); err != nil { if err := b.SendTransaction(ctx, signedTx); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -1058,7 +907,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
} }
if args.Nonce == nil { if args.Nonce == nil {
nonce, err := s.b.GetPoolNonce(ctx, args.From) nonce, err := s.b.PendingNonceAt(ctx, args.From)
if err != nil { if err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -1088,7 +937,7 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
return "", err return "", err
} }
if err := s.b.SendTx(ctx, tx); err != nil { if err := s.b.SendTransaction(ctx, tx); err != nil {
return "", err return "", err
} }
@ -1224,7 +1073,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig
args.Gas = rpc.NewHexNumber(defaultGas) args.Gas = rpc.NewHexNumber(defaultGas)
} }
if args.GasPrice == nil { if args.GasPrice == nil {
price, err := s.b.SuggestPrice(ctx) price, err := s.b.SuggestGasPrice(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1235,7 +1084,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig
} }
if args.Nonce == nil { if args.Nonce == nil {
nonce, err := s.b.GetPoolNonce(ctx, args.From) nonce, err := s.b.PendingNonceAt(ctx, args.From)
if err != nil { if err != nil {
return nil, err 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 // PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of
// the accounts this node manages. // the accounts this node manages.
func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
pending := s.b.GetPoolTransactions() pending := s.b.PendingTransactions()
transactions := make([]*RPCTransaction, 0, len(pending)) transactions := make([]*RPCTransaction, 0, len(pending))
for _, tx := range pending { for _, tx := range pending {
from, _ := tx.FromFrontier() from, _ := tx.FromFrontier()
if s.b.AccountManager().HasAddress(from) { if s.b.AccountManager().HasAddress(from) {
transactions = append(transactions, newRPCPendingTransaction(tx)) transactions = append(transactions, newRPCTransaction(tx))
} }
} }
return transactions 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 // 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. // 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) { 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 { for _, p := range pending {
if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() { if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() {
if gasPrice == nil { if gasPrice == nil {
@ -1301,8 +1150,8 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice,
return common.Hash{}, err return common.Hash{}, err
} }
s.b.RemoveTx(tx.Hash) s.b.RemoveTransaction(tx.Hash)
if err = s.b.SendTx(ctx, signedTx); err != nil { if err = s.b.SendTransaction(ctx, signedTx); err != nil {
return common.Hash{}, err return common.Hash{}, err
} }
@ -1326,8 +1175,8 @@ func NewPublicDebugAPI(b Backend) *PublicDebugAPI {
} }
// GetBlockRlp retrieves the RLP encoded for of a single block. // GetBlockRlp retrieves the RLP encoded for of a single block.
func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (string, error) { func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number rpc.BlockNumber) (string, error) {
block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) block, _ := blockByNumber(ctx, api.b, number)
if block == nil { if block == nil {
return "", fmt.Errorf("block #%d not found", number) 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. // PrintBlock retrieves a block and returns its pretty printed form.
func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (string, error) { func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number rpc.BlockNumber) (string, error) {
block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) block, _ := blockByNumber(ctx, api.b, number)
if block == nil { if block == nil {
return "", fmt.Errorf("block #%d not found", number) 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. // SeedHash retrieves the seed hash of a block.
func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, error) { 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) hash, err := ethash.GetSeedHash(number)
if err != nil { if err != nil {
return "", err return "", err
@ -1360,21 +1205,22 @@ func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string,
return fmt.Sprintf("0x%x", hash), nil 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. // debugging endpoint.
type PrivateDebugAPI struct { type PrivateDebugAPI struct {
b Backend b Backend
chaindb ethdb.Database
} }
// NewPrivateDebugAPI creates a new API definition for the private debug methods // NewPrivateDebugAPI creates a new API definition for the private debug methods
// of the Ethereum service. // of the Ethereum service.
func NewPrivateDebugAPI(b Backend) *PrivateDebugAPI { func NewPrivateDebugAPI(b Backend, chaindb ethdb.Database) *PrivateDebugAPI {
return &PrivateDebugAPI{b: b} return &PrivateDebugAPI{b, chaindb}
} }
// ChaindbProperty returns leveldb properties of the chain database. // ChaindbProperty returns leveldb properties of the chain database.
func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) {
ldb, ok := api.b.ChainDb().(interface { ldb, ok := api.chaindb.(interface {
LDB() *leveldb.DB LDB() *leveldb.DB
}) })
if !ok { if !ok {
@ -1389,7 +1235,7 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) {
} }
func (api *PrivateDebugAPI) ChaindbCompact() error { func (api *PrivateDebugAPI) ChaindbCompact() error {
ldb, ok := api.b.ChainDb().(interface { ldb, ok := api.chaindb.(interface {
LDB() *leveldb.DB LDB() *leveldb.DB
}) })
if !ok { if !ok {
@ -1408,7 +1254,7 @@ func (api *PrivateDebugAPI) ChaindbCompact() error {
// SetHead rewinds the head of the blockchain to a previous block. // SetHead rewinds the head of the blockchain to a previous block.
func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) { func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) {
api.b.SetHead(uint64(number.Int64())) api.b.ResetHeadBlock(number.Uint64())
} }
// PublicNetAPI offers network related RPC methods // PublicNetAPI offers network related RPC methods

View file

@ -20,52 +20,44 @@ package ethapi
import ( import (
"math/big" "math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "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/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" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
// Backend interface provides the common API services (that are provided by // Backend defines most methods required for the RPC API.
// both full and light clients) with access to necessary functions.
type Backend interface { type Backend interface {
// general Ethereum API ethereum.ChainReader
Downloader() *downloader.Downloader ethereum.ChainStateReader
ethereum.ChainSyncReader
ethereum.TransactionSender
ethereum.GasPricer
ethereum.GasEstimator
ethereum.ContractCaller
ProtocolVersion() int ProtocolVersion() int
SuggestPrice(ctx context.Context) (*big.Int, error) AccountManager() *accounts.Manager // TODO(fjl): this should be a constructor argb
ChainDb() ethdb.Database BlockTD(common.Hash) *big.Int
EventMux() *event.TypeMux RemoveTransaction(txhash common.Hash)
AccountManager() *accounts.Manager PendingTransactions() []*types.Transaction
// BlockChain API PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
SetHead(number uint64) ResetHeadBlock(number uint64) // for admin API
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)
} }
type State interface { // PendingState is implemented by the eth.Ethereum backend and provides access to optional
GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) // features that can only be provided by a full pending state.
GetCode(ctx context.Context, addr common.Address) ([]byte, error) type PendingState interface {
GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) PendingBlock() (*types.Block, error)
GetNonce(ctx context.Context, addr common.Address) (uint64, 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 { func GetAPIs(apiBackend Backend, solcPath string) []rpc.API {
@ -86,20 +78,11 @@ func GetAPIs(apiBackend Backend, solcPath string) []rpc.API {
Version: "1.0", Version: "1.0",
Service: NewPublicTransactionPoolAPI(apiBackend), Service: NewPublicTransactionPoolAPI(apiBackend),
Public: true, Public: true,
}, {
Namespace: "txpool",
Version: "1.0",
Service: NewPublicTxPoolAPI(apiBackend),
Public: true,
}, { }, {
Namespace: "debug", Namespace: "debug",
Version: "1.0", Version: "1.0",
Service: NewPublicDebugAPI(apiBackend), Service: NewPublicDebugAPI(apiBackend),
Public: true, Public: true,
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPrivateDebugAPI(apiBackend),
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",

105
internal/ethapi/txpool.go Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
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
}

View file

@ -17,128 +17,202 @@
package les package les
import ( import (
"errors"
"fmt"
"math/big" "math/big"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm" "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" "github.com/ethereum/go-ethereum/light"
rpc "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
type LesApiBackend struct { var (
eth *LightEthereum ErrBlockNotFound = errors.New("block not found")
gpo *gasprice.LightPriceOracle 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) { func (eth *LightEthereum) HeaderByHash(ctx context.Context, blockhash common.Hash) (*types.Header, error) {
b.eth.blockchain.SetHead(number) if h := eth.blockchain.GetHeaderByHash(blockhash); h != nil {
} return h, nil
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
} }
return nil, ErrBlockNotFound
return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr))
} }
func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { func (eth *LightEthereum) HeaderByNumber(ctx context.Context, blocknum *big.Int) (*types.Header, error) {
header, err := b.HeaderByNumber(ctx, blockNr) if blocknum == nil {
if header == nil || err != nil { return eth.blockchain.CurrentHeader(), nil
}
h, err := eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blocknum.Uint64()))
if err != nil {
return nil, err return nil, err
} }
return b.GetBlock(ctx, header.Hash()) if h == nil {
} return nil, ErrBlockNotFound
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
} }
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) { func (eth *LightEthereum) BlockByNumber(ctx context.Context, blocknum *big.Int) (*types.Block, error) {
return b.eth.blockchain.GetBlockByHash(ctx, blockHash) header, err := eth.HeaderByNumber(ctx, blocknum)
}
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)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
from.SetBalance(common.MaxBig) return eth.BlockByHash(ctx, header.Hash())
env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig)
return env, env.Error, nil
} }
func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { func (eth *LightEthereum) BlockByHash(ctx context.Context, blockhash common.Hash) (*types.Block, error) {
return b.eth.txPool.Add(ctx, signedTx) return eth.blockchain.GetBlockByHash(ctx, blockhash)
} }
func (b *LesApiBackend) RemoveTx(txHash common.Hash) { func (eth *LightEthereum) BlockReceipts(ctx context.Context, blockhash common.Hash, blocknum uint64) ([]*types.Receipt, error) {
b.eth.txPool.RemoveTx(txHash) return light.GetBlockReceipts(ctx, eth.odr, blockhash, blocknum)
} }
func (b *LesApiBackend) GetPoolTransactions() types.Transactions { func (eth *LightEthereum) BlockTD(blockHash common.Hash) *big.Int {
return b.eth.txPool.GetTransactions() return eth.blockchain.GetTdByHash(blockHash)
} }
func (b *LesApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { func (eth *LightEthereum) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) {
return b.eth.txPool.GetTransaction(txHash) 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) { func (eth *LightEthereum) TransactionInBlock(ctx context.Context, blockhash common.Hash, index uint) (*types.Transaction, error) {
return b.eth.txPool.GetNonce(ctx, addr) 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) { func (eth *LightEthereum) TransactionReceipt(ctx context.Context, txhash common.Hash) (*types.Receipt, error) {
return b.eth.txPool.Stats(), 0 return nil, ErrReceiptNotFound
} }
func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { func (eth *LightEthereum) TransactionCount(ctx context.Context, blockhash common.Hash) (uint, error) {
return b.eth.txPool.Content() 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 { func (eth *LightEthereum) BalanceAt(ctx context.Context, addr common.Address, blocknum *big.Int) (*big.Int, error) {
return b.eth.Downloader() st, err := eth.state(ctx, blocknum)
if err != nil {
return nil, err
}
return st.GetBalance(ctx, addr)
} }
func (b *LesApiBackend) ProtocolVersion() int { func (eth *LightEthereum) CodeAt(ctx context.Context, addr common.Address, blocknum *big.Int) ([]byte, error) {
return b.eth.LesVersion() + 10000 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) { func (eth *LightEthereum) NonceAt(ctx context.Context, addr common.Address, blocknum *big.Int) (uint64, error) {
return b.gpo.SuggestPrice(ctx) st, err := eth.state(ctx, blocknum)
if err != nil {
return 0, err
}
return st.GetNonce(ctx, addr)
} }
func (b *LesApiBackend) ChainDb() ethdb.Database { func (eth *LightEthereum) StorageAt(ctx context.Context, addr common.Address, key common.Hash, blocknum *big.Int) ([]byte, error) {
return b.eth.chainDb 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 { func (eth *LightEthereum) PendingCodeAt(ctx context.Context, addr common.Address) ([]byte, error) {
return b.eth.eventMux // 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 { func (eth *LightEthereum) state(ctx context.Context, blocknum *big.Int) (*light.LightState, error) {
return b.eth.accountManager 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{})
})
} }

View file

@ -58,7 +58,7 @@ type LightEthereum struct {
// DB interfaces // DB interfaces
chainDb ethdb.Database // Block chain database chainDb ethdb.Database // Block chain database
ApiBackend *LesApiBackend gpo *gasprice.LightPriceOracle
eventMux *event.TypeMux eventMux *event.TypeMux
pow *ethash.Ethash pow *ethash.Ethash
@ -124,8 +124,8 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) {
return nil, err return nil, err
} }
eth.ApiBackend = &LesApiBackend{eth, nil} eth.gpo = gasprice.NewLightPriceOracle(eth)
eth.ApiBackend.gpo = gasprice.NewLightPriceOracle(eth.ApiBackend)
return eth, nil return eth, nil
} }
@ -153,9 +153,13 @@ func (s *LightDummyAPI) Mining() bool {
// APIs returns the collection of RPC services the ethereum package offers. // APIs returns the collection of RPC services the ethereum package offers.
// NOTE, some of these services probably need to be moved to somewhere else. // NOTE, some of these services probably need to be moved to somewhere else.
func (s *LightEthereum) APIs() []rpc.API { func (eth *LightEthereum) APIs() []rpc.API {
return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ return append(ethapi.GetAPIs(eth, eth.solcPath), []rpc.API{
{ {
Namespace: "debug",
Version: "1.0",
Service: ethapi.NewPrivateDebugAPI(eth, eth.chainDb),
}, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: &LightDummyAPI{}, Service: &LightDummyAPI{},
@ -163,17 +167,22 @@ func (s *LightEthereum) APIs() []rpc.API {
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), Service: downloader.NewPublicDownloaderAPI(eth.protocolManager.downloader, eth.eventMux),
Public: true, Public: true,
}, { }, {
Namespace: "eth", Namespace: "eth",
Version: "1.0", Version: "1.0",
Service: filters.NewPublicFilterAPI(s.ApiBackend, true), Service: filters.NewPublicFilterAPI(eth, eth.eventMux, eth.chainDb, true),
Public: true, Public: true,
}, { }, {
Namespace: "txpool",
Version: "1.0",
Service: ethapi.TxPoolDebugAPI{Pool: eth.txPool},
},
{
Namespace: "net", Namespace: "net",
Version: "1.0", Version: "1.0",
Service: s.netRPCService, Service: eth.netRPCService,
Public: true, Public: true,
}, },
}...) }...)
@ -183,11 +192,6 @@ func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) {
s.blockchain.ResetWithGenesisBlock(gb) 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 // Protocols implements node.Service, returning all the currently configured
// network protocols to start. // network protocols to start.
func (s *LightEthereum) Protocols() []p2p.Protocol { func (s *LightEthereum) Protocols() []p2p.Protocol {

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/les/flowcontrol"
"github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
@ -44,8 +45,8 @@ type LesServer struct {
defParams *flowcontrol.ServerParams defParams *flowcontrol.ServerParams
} }
func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { func NewLesServer(eth *eth.Ethereum, mux *event.TypeMux, 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) pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, mux, eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -321,12 +321,11 @@ func (pool *TxPool) Stop() {
} }
// Stats returns the number of currently pending (locally created) transactions // 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() pool.mu.RLock()
defer pool.mu.RUnlock() defer pool.mu.RUnlock()
pending = len(pool.pending) return len(pool.pending), 0
return
} }
// validateTx checks whether a transaction is valid according to the consensus rules. // validateTx checks whether a transaction is valid according to the consensus rules.