diff --git a/accounts/abi/bind/auth.go b/accounts/abi/bind/auth.go index a20852fca0..65237955f8 100644 --- a/accounts/abi/bind/auth.go +++ b/accounts/abi/bind/auth.go @@ -56,7 +56,7 @@ func NewKeyedTransactor(key *ecdsa.PrivateKey) *TransactOpts { if err != nil { return nil, err } - return tx.WithSignature(signer, signature) + return tx.WithSignature(signer, signature), nil }, } } diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 00a8cd3e92..41eb072bd9 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -167,7 +167,7 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM if err != nil { return nil, err } - rval, _, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state) + rval, _, err := b.callContract(call, b.blockchain.CurrentBlock(), state) return rval, err } @@ -177,7 +177,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu defer b.mu.Unlock() defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) - rval, _, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + rval, _, err := b.callContract(call, b.pendingBlock, b.pendingState) return rval, err } @@ -203,32 +203,16 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs defer b.mu.Unlock() defer b.pendingState.RevertToSnapshot(b.pendingState.Snapshot()) - _, gas, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState) + _, gas, err := b.callContract(call, b.pendingBlock, b.pendingState) return gas, err } // callContract implemens common code between normal and pending contract calls. // state is modified during execution, make sure to copy it if necessary. -func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) ([]byte, *big.Int, error) { - // Ensure message is initialized properly. - if call.GasPrice == nil { - call.GasPrice = big.NewInt(1) - } - if call.Gas == nil || call.Gas.BitLen() == 0 { - call.Gas = big.NewInt(50000000) - } - if call.Value == nil { - call.Value = new(big.Int) - } - // Set infinite balance to the fake caller account. - from := statedb.GetOrNewStateObject(call.From) - from.SetBalance(common.MaxBig) - // Execute the call. - msg := callmsg{call} - vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) - gaspool := new(core.GasPool).AddGas(common.MaxBig) - ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb() - return ret, gasUsed, err +func (b *SimulatedBackend) callContract(call ethereum.CallMsg, block *types.Block, state *state.StateDB) ([]byte, *big.Int, error) { + return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment { + return core.NewEnv(state, chainConfig, b.blockchain, msg, block.Header(), vm.Config{}) + }) } // SendTransaction updates the pending block to include the given transaction. @@ -256,17 +240,3 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database) return nil } - -// callmsg implements core.Message to allow passing it as a transaction simulator. -type callmsg struct { - ethereum.CallMsg -} - -func (m callmsg) From() common.Address { return m.CallMsg.From } -func (m callmsg) Nonce() uint64 { return 0 } -func (m callmsg) CheckNonce() bool { return false } -func (m callmsg) To() *common.Address { return m.CallMsg.To } -func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } -func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas } -func (m callmsg) Value() *big.Int { return m.CallMsg.Value } -func (m callmsg) Data() []byte { return m.CallMsg.Data } diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index fc7685b77f..ac5fe562c0 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -776,7 +776,7 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { fullNode, err := eth.New(ctx, ethConf) if fullNode != nil && ethConf.LightServ > 0 { - ls, _ := les.NewLesServer(fullNode, ethConf) + ls, _ := les.NewLesServer(fullNode, stack.EventMux(), ethConf) fullNode.AddLesServer(ls) } return fullNode, err diff --git a/contracts/release/release.go b/contracts/release/release.go index cd79112cd1..346005e903 100644 --- a/contracts/release/release.go +++ b/contracts/release/release.go @@ -27,7 +27,6 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/les" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" @@ -62,20 +61,20 @@ type ReleaseService struct { // releases and notify the user of such. func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { // Retrieve the Ethereum service dependency to access the blockchain - var apiBackend ethapi.Backend + var apiBackend bind.ContractBackend var ethereum *eth.Ethereum if err := ctx.Service(ðereum); err == nil { - apiBackend = ethereum.ApiBackend + apiBackend = ethereum } else { var ethereum *les.LightEthereum if err := ctx.Service(ðereum); err == nil { - apiBackend = ethereum.ApiBackend + apiBackend = ethereum } else { return nil, err } } // Construct the release service - contract, err := NewReleaseOracle(config.Oracle, eth.NewContractBackend(apiBackend)) + contract, err := NewReleaseOracle(config.Oracle, apiBackend) if err != nil { return nil, err } diff --git a/core/database_util.go b/core/database_util.go index 84669de35b..7771e02479 100644 --- a/core/database_util.go +++ b/core/database_util.go @@ -86,7 +86,7 @@ func GetCanonicalHash(db ethdb.Database, number uint64) common.Hash { // missingNumber is returned by GetBlockNumber if no header with the // given block hash has been stored in the database -const missingNumber = uint64(0xffffffffffffffff) +const MissingNumber = uint64(0xffffffffffffffff) // GetBlockNumber returns the block number assigned to a block hash // if the corresponding header is present in the database @@ -95,7 +95,7 @@ func GetBlockNumber(db ethdb.Database, hash common.Hash) uint64 { if len(data) != 8 { data, _ := db.Get(append(append(oldBlockPrefix, hash.Bytes()...), oldHeaderSuffix...)) if len(data) == 0 { - return missingNumber + return MissingNumber } header := new(types.Header) if err := rlp.Decode(bytes.NewReader(data), header); err != nil { @@ -251,7 +251,7 @@ func GetBlockReceipts(db ethdb.Database, hash common.Hash, number uint64) types. // GetTransaction retrieves a specific transaction from the database, along with // its added positional metadata. -func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { +func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, int) { // Retrieve the transaction itself from the database data, _ := db.Get(hash.Bytes()) if len(data) == 0 { @@ -274,7 +274,7 @@ func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, co if err := rlp.DecodeBytes(data, &meta); err != nil { return nil, common.Hash{}, 0, 0 } - return &tx, meta.BlockHash, meta.BlockIndex, meta.Index + return &tx, meta.BlockHash, meta.BlockIndex, int(meta.Index) } // GetReceipt returns a receipt by hash diff --git a/core/database_util_test.go b/core/database_util_test.go index 83750aa60a..e1613699dd 100644 --- a/core/database_util_test.go +++ b/core/database_util_test.go @@ -369,7 +369,7 @@ func TestTransactionStorage(t *testing.T) { if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil { t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash()) } else { - if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) { + if hash != block.Hash() || number != block.NumberU64() || index != i { t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i) } if tx.String() != txn.String() { diff --git a/core/headerchain.go b/core/headerchain.go index c536945712..60552fd43c 100644 --- a/core/headerchain.go +++ b/core/headerchain.go @@ -124,7 +124,7 @@ func (hc *HeaderChain) GetBlockNumber(hash common.Hash) uint64 { return cached.(uint64) } number := GetBlockNumber(hc.chainDb, hash) - if number != missingNumber { + if number != MissingNumber { hc.numberCache.Add(hash, number) } return number diff --git a/core/helper_test.go b/core/helper_test.go deleted file mode 100644 index fd6a5491c5..0000000000 --- a/core/helper_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2014 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "container/list" - "fmt" - - "github.com/ethereum/go-ethereum/core/types" - // "github.com/ethereum/go-ethereum/crypto" - - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" -) - -// Implement our EthTest Manager -type TestManager struct { - // stateManager *StateManager - eventMux *event.TypeMux - - db ethdb.Database - txPool *TxPool - blockChain *BlockChain - Blocks []*types.Block -} - -func (s *TestManager) IsListening() bool { - return false -} - -func (s *TestManager) IsMining() bool { - return false -} - -func (s *TestManager) PeerCount() int { - return 0 -} - -func (s *TestManager) Peers() *list.List { - return list.New() -} - -func (s *TestManager) BlockChain() *BlockChain { - return s.blockChain -} - -func (tm *TestManager) TxPool() *TxPool { - return tm.txPool -} - -// func (tm *TestManager) StateManager() *StateManager { -// return tm.stateManager -// } - -func (tm *TestManager) EventMux() *event.TypeMux { - return tm.eventMux -} - -// func (tm *TestManager) KeyManager() *crypto.KeyManager { -// return nil -// } - -func (tm *TestManager) Db() ethdb.Database { - return tm.db -} - -func NewTestManager() *TestManager { - db, err := ethdb.NewMemDatabase() - if err != nil { - fmt.Println("Could not create mem-db, failing") - return nil - } - - testManager := &TestManager{} - testManager.eventMux = new(event.TypeMux) - testManager.db = db - // testManager.txPool = NewTxPool(testManager) - // testManager.blockChain = NewBlockChain(testManager) - // testManager.stateManager = NewStateManager(testManager) - - return testManager -} diff --git a/core/state_transition.go b/core/state_transition.go index 8abe17b0a2..5322bed8fd 100644 --- a/core/state_transition.go +++ b/core/state_transition.go @@ -20,6 +20,7 @@ import ( "fmt" "math/big" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/logger" @@ -134,6 +135,39 @@ func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.In return ret, gasUsed, err } +// ApplyCallMessage runs the given message as a read-only contract call. +// +// It modifies the environment to provide a near infinite amount of ether to the sender +// account and runs the message with the amount of gas configured in the message. This +// function is meant to be used outside of regular block processing. +// +// It returns the bytes returned by any EVM execution (if it took place), the gas used +// (not including gas refunds) and an error if it failed. An error always indicates a core +// error meaning that the message would always fail for that particular state and would +// never be accepted within a block. +func ApplyCallMessage(call ethereum.CallMsg, newEnv func(Message) vm.Environment) ([]byte, *big.Int, error) { + // Ensure message is initialized properly. + if call.GasPrice == nil { + call.GasPrice = big.NewInt(1) + } + if call.Gas == nil || call.Gas.BitLen() == 0 { + call.Gas = big.NewInt(50000000) + } + if call.Value == nil { + call.Value = new(big.Int) + } + msg := callmsg{call} + env := newEnv(msg) + // Provide ether to the caller account. + have := env.Db().GetBalance(call.From) + need := new(big.Int).Sub(common.MaxBig, have) + env.Db().AddBalance(call.From, need) + // Execute the call. + gaspool := new(GasPool).AddGas(call.Gas) + ret, gasUsed, _, err := NewStateTransition(env, msg, gaspool).TransitionDb() + return ret, gasUsed, err +} + func (self *StateTransition) from() vm.Account { f := self.msg.From() if !self.state.Exist(f) { @@ -283,3 +317,17 @@ func (self *StateTransition) refundGas() { func (self *StateTransition) gasUsed() *big.Int { return new(big.Int).Sub(self.initialGas, self.gas) } + +// callmsg wraps ethereum.CallMsg so it implements Message. +type callmsg struct { + ethereum.CallMsg +} + +func (m callmsg) From() common.Address { return m.CallMsg.From } +func (m callmsg) Nonce() uint64 { return 0 } +func (m callmsg) CheckNonce() bool { return false } +func (m callmsg) To() *common.Address { return m.CallMsg.To } +func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice } +func (m callmsg) Gas() *big.Int { return m.CallMsg.Gas } +func (m callmsg) Value() *big.Int { return m.CallMsg.Value } +func (m callmsg) Data() []byte { return m.CallMsg.Data } diff --git a/core/tx_pool.go b/core/tx_pool.go index edcbc21ebf..e38d963ee0 100644 --- a/core/tx_pool.go +++ b/core/tx_pool.go @@ -195,11 +195,12 @@ func (pool *TxPool) Stop() { glog.V(logger.Info).Infoln("Transaction pool stopped") } -func (pool *TxPool) State() *state.ManagedState { - pool.mu.RLock() - defer pool.mu.RUnlock() +// NonceAt returns the next valid nonce for the given account. +func (pool *TxPool) NonceAt(addr common.Address) uint64 { + pool.mu.Lock() + defer pool.mu.Unlock() - return pool.pendingState + return pool.pendingState.GetNonce(addr) } // Stats retrieves the current pool stats, namely the number of pending and the diff --git a/core/types/block_test.go b/core/types/block_test.go index b95bddcfce..9316125aba 100644 --- a/core/types/block_test.go +++ b/core/types/block_test.go @@ -18,7 +18,6 @@ package types import ( "bytes" - "fmt" "math/big" "reflect" "testing" @@ -52,11 +51,10 @@ func TestBlockEncoding(t *testing.T) { check("Size", block.Size(), common.StorageSize(len(blockEnc))) tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil) - - tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b11b")) - fmt.Println(block.Transactions()[0].Hash()) - fmt.Println(tx1.data) - fmt.Println(tx1.Hash()) + tx1 = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b11b")) + t.Log(block.Transactions()[0].Hash()) + t.Log(tx1.data) + t.Log(tx1.Hash()) check("len(Transactions)", len(block.Transactions()), 1) check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash()) diff --git a/core/types/transaction.go b/core/types/transaction.go index 323bfaee60..6c255e2637 100644 --- a/core/types/transaction.go +++ b/core/types/transaction.go @@ -340,12 +340,12 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) { // XXX This only makes for a nice API: NewTx(...).SignECDSA(signer, prv). Should // we keep this? func (tx *Transaction) SignECDSA(signer Signer, prv *ecdsa.PrivateKey) (*Transaction, error) { - return signer.SignECDSA(tx, prv) + return SignECDSA(signer, tx, prv) } // WithSignature returns a new transaction with the given signature. // This signature needs to be formatted as described in the yellow paper (v+27). -func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) { +func (tx *Transaction) WithSignature(signer Signer, sig []byte) *Transaction { return signer.WithSignature(tx, sig) } diff --git a/core/types/transaction_signing.go b/core/types/transaction_signing.go index 7d571643f2..f5335a49c1 100644 --- a/core/types/transaction_signing.go +++ b/core/types/transaction_signing.go @@ -57,7 +57,7 @@ func SignECDSA(s Signer, tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, if err != nil { return nil, err } - return s.WithSignature(tx, sig) + return s.WithSignature(tx, sig), nil } // Sender derives the sender from the tx using the signer derivation @@ -101,10 +101,9 @@ type Signer interface { Hash(tx *Transaction) common.Hash // PubilcKey returns the public key derived from the signature PublicKey(tx *Transaction) ([]byte, error) - // SignECDSA signs the transaction with the given and returns a copy of the tx - SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) - // WithSignature returns a copy of the transaction with the given signature - WithSignature(tx *Transaction, sig []byte) (*Transaction, error) + // WithSignature returns a copy of the transaction with the given signature. + // It panics if the signature has the wrong size. + WithSignature(tx *Transaction, sig []byte) *Transaction // Checks for equality on the signers Equal(Signer) bool } @@ -129,10 +128,6 @@ func (s EIP155Signer) Equal(s2 Signer) bool { return ok && eip155.chainId.Cmp(s.chainId) == 0 } -func (s EIP155Signer) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { - return SignECDSA(s, tx, prv) -} - func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) { // if the transaction is not protected fall back to homestead signer if !tx.Protected() { @@ -169,7 +164,7 @@ func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) { // WithSignature returns a new transaction with the given signature. // This signature needs to be formatted as described in the yellow paper (v+27). -func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { +func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) *Transaction { if len(sig) != 65 { panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) } @@ -182,7 +177,7 @@ func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, cpy.data.V = big.NewInt(int64(sig[64] - 27 + 35)) cpy.data.V.Add(cpy.data.V, s.chainIdMul) } - return cpy, nil + return cpy } // Hash returns the hash to be signed by the sender. @@ -199,15 +194,6 @@ func (s EIP155Signer) Hash(tx *Transaction) common.Hash { }) } -func (s EIP155Signer) SigECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { - h := s.Hash(tx) - sig, err := crypto.SignEthereum(h[:], prv) - if err != nil { - return nil, err - } - return s.WithSignature(tx, sig) -} - // HomesteadTransaction implements TransactionInterface using the // homestead rules. type HomesteadSigner struct{ FrontierSigner } @@ -219,7 +205,7 @@ func (s HomesteadSigner) Equal(s2 Signer) bool { // WithSignature returns a new transaction with the given snature. // This snature needs to be formatted as described in the yellow paper (v+27). -func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { +func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) *Transaction { if len(sig) != 65 { panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) } @@ -227,16 +213,7 @@ func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transacti cpy.data.R = new(big.Int).SetBytes(sig[:32]) cpy.data.S = new(big.Int).SetBytes(sig[32:64]) cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) - return cpy, nil -} - -func (hs HomesteadSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { - h := hs.Hash(tx) - sig, err := crypto.SignEthereum(h[:], prv) - if err != nil { - return nil, err - } - return hs.WithSignature(tx, sig) + return cpy } func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) { @@ -275,7 +252,7 @@ func (s FrontierSigner) Equal(s2 Signer) bool { // WithSignature returns a new transaction with the given snature. // This snature needs to be formatted as described in the yellow paper (v+27). -func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) { +func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) *Transaction { if len(sig) != 65 { panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig))) } @@ -283,16 +260,7 @@ func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transactio cpy.data.R = new(big.Int).SetBytes(sig[:32]) cpy.data.S = new(big.Int).SetBytes(sig[32:64]) cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]}) - return cpy, nil -} - -func (fs FrontierSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) { - h := fs.Hash(tx) - sig, err := crypto.SignEthereum(h[:], prv) - if err != nil { - return nil, err - } - return fs.WithSignature(tx, sig) + return cpy } // Hash returns the hash to be sned by the sender. diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go index ca105566ad..e2c8191d5d 100644 --- a/core/types/transaction_test.go +++ b/core/types/transaction_test.go @@ -37,8 +37,7 @@ var ( big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, ) - - rightvrsTx, _ = NewTransaction( + rightvrsTx = NewTransaction( 3, common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"), big.NewInt(10), diff --git a/eth/api.go b/eth/api.go index b3185c3926..f026174fb6 100644 --- a/eth/api.go +++ b/eth/api.go @@ -516,7 +516,7 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common. return nil, fmt.Errorf("sender retrieval failed: %v", err) } // Mutate the state if we haven't reached the tracing transaction yet - if uint64(idx) < txIndex { + if idx < txIndex { vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{}) _, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas())) if err != nil { diff --git a/eth/api_backend.go b/eth/api_backend.go index 0925132ef0..8fc06ce09a 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -17,197 +17,305 @@ package eth import ( + "errors" + "fmt" "math/big" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" + "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/params" - rpc "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) -// EthApiBackend implements ethapi.Backend for full nodes -type EthApiBackend struct { - eth *Ethereum - gpo *gasprice.GasPriceOracle -} +// Assert at compile time that this implementation has all optional API features. +var ( + _ ethapi.TransactionInclusionBlock = (*Ethereum)(nil) + _ ethapi.PendingState = (*Ethereum)(nil) + _ bind.ContractBackend = (*Ethereum)(nil) + _ bind.PendingContractCaller = (*Ethereum)(nil) +) -func (b *EthApiBackend) ChainConfig() *params.ChainConfig { - return b.eth.chainConfig -} +var ( + ErrBlockNotFound = errors.New("block not found") + ErrTxNotFound = errors.New("transaction not found") +) -func (b *EthApiBackend) CurrentBlock() *types.Block { - return b.eth.blockchain.CurrentBlock() -} - -func (b *EthApiBackend) SetHead(number uint64) { - b.eth.blockchain.SetHead(number) -} - -func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { - // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, _ := b.eth.miner.Pending() - return block.Header(), nil +// HeaderByNumber returns headers from the canonical chain. +func (eth *Ethereum) HeaderByNumber(ctx context.Context, num *big.Int) (*types.Header, error) { + if num == nil { + return eth.blockchain.CurrentBlock().Header(), nil } - // Otherwise resolve and return the block - if blockNr == rpc.LatestBlockNumber { - return b.eth.blockchain.CurrentBlock().Header(), nil + if h := eth.blockchain.GetHeaderByNumber(num.Uint64()); h != nil { + return h, nil } - return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr)), nil + return nil, ErrBlockNotFound } -func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { - // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, _ := b.eth.miner.Pending() - return block, nil +// HeaderByHash returns the header with the given hash. +func (eth *Ethereum) HeaderByHash(ctx context.Context, blockhash common.Hash) (*types.Header, error) { + if h := eth.blockchain.GetHeaderByHash(blockhash); h != nil { + return h, nil } - // Otherwise resolve and return the block - if blockNr == rpc.LatestBlockNumber { - return b.eth.blockchain.CurrentBlock(), nil + return nil, ErrBlockNotFound +} + +// BlockByNumber returns blocks from the canonical chain. +func (eth *Ethereum) BlockByNumber(ctx context.Context, num *big.Int) (*types.Block, error) { + if num == nil { + return eth.blockchain.CurrentBlock(), nil } - return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil -} - -func (b *EthApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) { - // Pending state is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, state := b.eth.miner.Pending() - return EthApiState{state}, block.Header(), nil + if b := eth.blockchain.GetBlockByNumber(num.Uint64()); b != nil { + return b, nil } - // Otherwise resolve the block number and return its state - header, err := b.HeaderByNumber(ctx, blockNr) - if header == nil || err != nil { - return nil, nil, err + return nil, ErrBlockNotFound +} + +// BlockByHash returns the block with the given hash. +func (eth *Ethereum) BlockByHash(ctx context.Context, blockhash common.Hash) (*types.Block, error) { + if b := eth.blockchain.GetBlockByHash(blockhash); b != nil { + return b, nil } - stateDb, err := b.eth.BlockChain().StateAt(header.Root) - return EthApiState{stateDb}, header, err + return nil, ErrBlockNotFound } -func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { - return b.eth.blockchain.GetBlockByHash(blockHash), nil +// BlockReceipts returns all receipts contained in the given block. +func (eth *Ethereum) BlockReceipts(ctx context.Context, blockhash common.Hash, number uint64) ([]*types.Receipt, error) { + r := core.GetBlockReceipts(eth.chainDb, blockhash, number) + if r == nil { + return nil, errors.New("database has no valid receipts for the given block") + } + return r, nil } -func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { - return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil +// TransactionCount returns the number of transactions in a block. +func (eth *Ethereum) TransactionCount(ctx context.Context, blockhash common.Hash) (uint, error) { + b := eth.blockchain.GetBlockByHash(blockhash) + if b == nil { + return 0, nil + } + return uint(len(b.Transactions())), nil } -func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int { - return b.eth.blockchain.GetTdByHash(blockHash) +// TransactionInBlock returns the i'th transaction in the given block. +func (eth *Ethereum) TransactionInBlock(ctx context.Context, blockhash common.Hash, i uint) (*types.Transaction, error) { + b := eth.blockchain.GetBlockByHash(blockhash) + if b == nil { + return nil, fmt.Errorf("transaction index %d out of range for non-existent block", i) + } + if i >= uint(len(b.Transactions())) { + return nil, fmt.Errorf("transaction index %d out of range", i) + } + return b.Transactions()[i], nil } -func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { - statedb := state.(EthApiState).state - from := statedb.GetOrNewStateObject(msg.From()) - from.SetBalance(common.MaxBig) - vmError := func() error { return nil } - return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}), vmError, nil +// TransactionByHash returns the transaction with the given hash. +func (eth *Ethereum) TransactionByHash(ctx context.Context, txhash common.Hash) (tx *types.Transaction, isPending bool, err error) { + if tx = eth.txPool.Get(txhash); tx != nil { + return tx, true, nil + } + if tx, _, _, _ = core.GetTransaction(eth.chainDb, txhash); tx != nil { + return tx, false, nil + } + return nil, false, ErrTxNotFound } -func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() - - b.eth.txPool.SetLocal(signedTx) - return b.eth.txPool.Add(signedTx) +// TransactionInclusionBlock returns the block in which the given transaction was included. +func (eth *Ethereum) TransactionInclusionBlock(txhash common.Hash) (bhash common.Hash, bnum uint64, index int, err error) { + var tx *types.Transaction + if tx, bhash, bnum, index = core.GetTransaction(eth.chainDb, txhash); tx == nil { + err = ErrTxNotFound + } + return bhash, bnum, index, err } -func (b *EthApiBackend) RemoveTx(txHash common.Hash) { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() - - b.eth.txPool.Remove(txHash) +// TransactionReceipt returns the receipt of a transaction. +func (eth *Ethereum) TransactionReceipt(ctx context.Context, txhash common.Hash) (*types.Receipt, error) { + r := core.GetReceipt(eth.chainDb, txhash) + if r == nil { + return nil, ErrTxNotFound + } + return r, nil } -func (b *EthApiBackend) GetPoolTransactions() types.Transactions { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() +// BlockTD returns the total difficulty of a certain block. +func (eth *Ethereum) BlockTD(blockhash common.Hash) *big.Int { + return eth.blockchain.GetTdByHash(blockhash) +} + +// BalanceAt returns the balance of the given account. +func (eth *Ethereum) BalanceAt(ctx context.Context, addr common.Address, block *big.Int) (bal *big.Int, err error) { + err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { bal = st.GetBalance(addr) }) + return bal, err +} + +// CodeAt returns the code of the given account. +func (eth *Ethereum) CodeAt(ctx context.Context, addr common.Address, block *big.Int) (code []byte, err error) { + err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { code = st.GetCode(addr) }) + return code, err +} + +// NonceAt returns the nonce of the given account. +func (eth *Ethereum) NonceAt(ctx context.Context, addr common.Address, block *big.Int) (nonce uint64, err error) { + err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { nonce = st.GetNonce(addr) }) + return nonce, err +} + +// StorageAt returns a storage value of the given account. +func (eth *Ethereum) StorageAt(ctx context.Context, addr common.Address, key common.Hash, block *big.Int) (val []byte, err error) { + err = eth.withStateAt(ctx, block, false, func(st *state.StateDB) { v := st.GetState(addr, key); val = v[:] }) + return val, err +} + +// PendingBalanceAt returns the balance of the given account in the pending state. +func (eth *Ethereum) PendingBalanceAt(ctx context.Context, addr common.Address) (bal *big.Int, err error) { + err = eth.withStateAt(ctx, nil, true, func(st *state.StateDB) { bal = st.GetBalance(addr) }) + return bal, err +} + +// PendingBalanceAt returns the code of the given account in the pending state. +func (eth *Ethereum) PendingCodeAt(ctx context.Context, addr common.Address) (code []byte, err error) { + err = eth.withStateAt(ctx, nil, true, func(st *state.StateDB) { code = st.GetCode(addr) }) + return code, err +} + +// PendingBalanceAt returns a storage value of the given account in the pending state. +func (eth *Ethereum) PendingStorageAt(ctx context.Context, addr common.Address, key common.Hash) (val []byte, err error) { + err = eth.withStateAt(ctx, nil, true, func(st *state.StateDB) { v := st.GetState(addr, key); val = v[:] }) + return val, err +} + +// PendingTransaction count returns the number of transactions in the pending block. +func (eth *Ethereum) PendingTransactionCount(ctx context.Context) (uint, error) { + b, _ := eth.miner.Pending() + return uint(len(b.Transactions())), nil +} + +func (eth *Ethereum) withStateAt(ctx context.Context, block *big.Int, pending bool, fn func(st *state.StateDB)) error { + var st *state.StateDB + if pending { + _, st = eth.miner.Pending() + } else { + header, err := eth.HeaderByNumber(ctx, block) + if err != nil { + return err + } + st, err = eth.BlockChain().StateAt(header.Root) + if err != nil { + return err + } + } + fn(st) + return nil +} + +// PendingBlock returns the next block as envisioned by the pending state. +func (eth *Ethereum) PendingBlock() (*types.Block, error) { + b, _ := eth.miner.Pending() + return b, nil +} + +// PendingNonceAt returns the next valid nonce according to the local transaction pool. +func (eth *Ethereum) PendingNonceAt(ctx context.Context, addr common.Address) (uint64, error) { + return eth.txPool.NonceAt(addr), nil +} + +// PendingTransactions returns all known pending transactions. +func (eth *Ethereum) PendingTransactions() []*types.Transaction { + eth.txMu.Lock() + defer eth.txMu.Unlock() var txs types.Transactions - for _, batch := range b.eth.txPool.Pending() { + for _, batch := range eth.txPool.Pending() { txs = append(txs, batch...) } return txs } -func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() +// SendTransaction queues a transaction in the pool. +func (eth *Ethereum) SendTransaction(ctx context.Context, signedTx *types.Transaction) error { + eth.txMu.Lock() + defer eth.txMu.Unlock() - return b.eth.txPool.Get(hash) + eth.txPool.SetLocal(signedTx) + return eth.txPool.Add(signedTx) } -func (b *EthApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() - - return b.eth.txPool.State().GetNonce(addr), nil +// SyncProgress returns sync status information. +func (b *Ethereum) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { + return b.protocolManager.downloader.Progress(), nil } -func (b *EthApiBackend) Stats() (pending int, queued int) { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() - - return b.eth.txPool.Stats() +// SuggestGasPrice returns a suitable gas price based on the content of recently seen blocks. +func (eth *Ethereum) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + return eth.gpo.SuggestPrice(), nil } -func (b *EthApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { - b.eth.txMu.Lock() - defer b.eth.txMu.Unlock() +// RemoveTransaction removes the given transaction from the local pool. +func (eth *Ethereum) RemoveTransaction(txHash common.Hash) { + eth.txMu.Lock() + defer eth.txMu.Unlock() - return b.eth.TxPool().Content() + eth.txPool.Remove(txHash) } -func (b *EthApiBackend) Downloader() *downloader.Downloader { - return b.eth.Downloader() +// ProtocolVersion returns the active protocol version. +func (eth *Ethereum) ProtocolVersion() int { + return int(eth.protocolManager.SubProtocols[0].Version) } -func (b *EthApiBackend) ProtocolVersion() int { - return b.eth.EthVersion() +// ChainConfig returns the active chain configuration. +func (eth *Ethereum) ChainConfig() *params.ChainConfig { + return eth.chainConfig } -func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(), nil +// AccountManager returns the internal account manager that accesses the data directory. +// Deprecated: get the account manager through node.Node instead. +func (eth *Ethereum) AccountManager() *accounts.Manager { + return eth.accountManager } -func (b *EthApiBackend) ChainDb() ethdb.Database { - return b.eth.ChainDb() +// ResetHeadBlock resets the blockchain to the given number. +// Use this method if you know what you're doing. +func (eth *Ethereum) ResetHeadBlock(blocknum uint64) { + eth.blockchain.SetHead(blocknum) } -func (b *EthApiBackend) EventMux() *event.TypeMux { - return b.eth.EventMux() +func (eth *Ethereum) EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) { + block, state := eth.miner.Pending() + _, gas, err := eth.callContract(call, block.Header(), state) + return gas, err } -func (b *EthApiBackend) AccountManager() *accounts.Manager { - return b.eth.AccountManager() +func (eth *Ethereum) PendingCallContract(ctx context.Context, call ethereum.CallMsg) ([]byte, error) { + block, state := eth.miner.Pending() + val, _, err := eth.callContract(call, block.Header(), state) + return val, err } -type EthApiState struct { - state *state.StateDB +func (eth *Ethereum) CallContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, error) { + var head *types.Header + if blocknum == nil { + head = eth.blockchain.CurrentHeader() + } else if head = eth.blockchain.GetHeaderByNumber(blocknum.Uint64()); head == nil { + return nil, ErrBlockNotFound + } + state, err := eth.blockchain.StateAt(head.Root) + if err != nil { + return nil, err + } + val, _, err := eth.callContract(call, head, state) + return val, err } -func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) { - return s.state.GetBalance(addr), nil -} - -func (s EthApiState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) { - return s.state.GetCode(addr), nil -} - -func (s EthApiState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) { - return s.state.GetState(a, b), nil -} - -func (s EthApiState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { - return s.state.GetNonce(addr), nil +func (eth *Ethereum) callContract(call ethereum.CallMsg, head *types.Header, state *state.StateDB) ([]byte, *big.Int, error) { + return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment { + return core.NewEnv(state, eth.chainConfig, eth.blockchain, msg, head, vm.Config{}) + }) } diff --git a/eth/backend.go b/eth/backend.go index d20efdabdc..34ebe1dadc 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -31,7 +31,6 @@ import ( "github.com/ethereum/ethash" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/httpclient" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth/downloader" @@ -128,14 +127,13 @@ type Ethereum struct { pow *ethash.Ethash accountManager *accounts.Manager - ApiBackend *EthApiBackend - miner *miner.Miner Mining bool MinerThreads int AutoDAG bool autodagquit chan bool etherbase common.Address + gpo *gasprice.GasPriceOracle solcPath string NatSpec bool @@ -215,14 +213,14 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { eth.chainConfig = config.ChainConfig - eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux()) + eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.eventMux) if err != nil { if err == core.ErrNoGenesis { return nil, fmt.Errorf(`No chain found. Please initialise a new chain using the "init" subcommand.`) } return nil, err } - newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) + newPool := core.NewTxPool(eth.chainConfig, eth.eventMux, eth.blockchain.State, eth.blockchain.GasLimit) eth.txPool = newPool maxPeers := config.MaxPeers @@ -239,10 +237,13 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { if eth.protocolManager, err = NewProtocolManager(eth.chainConfig, config.FastSync, config.NetworkId, maxPeers, eth.eventMux, eth.txPool, eth.pow, eth.blockchain, chainDb); err != nil { return nil, err } - eth.miner = miner.New(eth, eth.chainConfig, eth.EventMux(), eth.pow) + + // Set up the miner. + eth.miner = miner.New(eth, eth.chainConfig, eth.eventMux, eth.pow) eth.miner.SetGasPrice(config.GasPrice) eth.miner.SetExtra(config.ExtraData) + // Set up the gas price oracle. gpoParams := &gasprice.GpoParams{ GpoMinGasPrice: config.GpoMinGasPrice, GpoMaxGasPrice: config.GpoMaxGasPrice, @@ -251,8 +252,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { GpobaseStepUp: config.GpobaseStepUp, GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, } - gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) - eth.ApiBackend = &EthApiBackend{eth, gpo} + eth.gpo = gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) return eth, nil } @@ -306,50 +306,58 @@ func CreatePoW(config *Config) (*ethash.Ethash, error) { // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. -func (s *Ethereum) APIs() []rpc.API { - return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ +func (eth *Ethereum) APIs() []rpc.API { + return append(ethapi.GetAPIs(eth, eth.solcPath), []rpc.API{ { Namespace: "eth", Version: "1.0", - Service: NewPublicEthereumAPI(s), + Service: NewPublicEthereumAPI(eth), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: NewPublicMinerAPI(s), + Service: NewPublicMinerAPI(eth), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), + Service: downloader.NewPublicDownloaderAPI(eth.protocolManager.downloader, eth.eventMux), Public: true, }, { Namespace: "miner", Version: "1.0", - Service: NewPrivateMinerAPI(s), + Service: NewPrivateMinerAPI(eth), Public: false, }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, false), + Service: filters.NewPublicFilterAPI(eth, eth.eventMux, eth.chainDb, false), Public: true, }, { Namespace: "admin", Version: "1.0", - Service: NewPrivateAdminAPI(s), + Service: NewPrivateAdminAPI(eth), }, { Namespace: "debug", Version: "1.0", - Service: NewPublicDebugAPI(s), + Service: NewPublicDebugAPI(eth), Public: true, }, { Namespace: "debug", Version: "1.0", - Service: NewPrivateDebugAPI(s.chainConfig, s), + Service: ethapi.NewPrivateDebugAPI(eth, eth.chainDb), + }, { + Namespace: "debug", + Version: "1.0", + Service: NewPrivateDebugAPI(eth.chainConfig, eth), + }, { + Namespace: "txpool", + Version: "1.0", + Service: ethapi.TxPoolDebugAPI{Pool: eth.txPool}, }, { Namespace: "net", Version: "1.0", - Service: s.netRPCService, + Service: eth.netRPCService, Public: true, }, }...) @@ -388,20 +396,16 @@ func (s *Ethereum) StartMining(threads int) error { return nil } -func (s *Ethereum) StopMining() { s.miner.Stop() } -func (s *Ethereum) IsMining() bool { return s.miner.Mining() } -func (s *Ethereum) Miner() *miner.Miner { return s.miner } +func (s *Ethereum) StopMining() { s.miner.Stop() } +func (s *Ethereum) IsMining() bool { return s.miner.Mining() } -func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } -func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } -func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } -func (s *Ethereum) EventMux() *event.TypeMux { return s.eventMux } -func (s *Ethereum) Pow() *ethash.Ethash { return s.pow } -func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } -func (s *Ethereum) IsListening() bool { return true } // Always listening -func (s *Ethereum) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } -func (s *Ethereum) NetVersion() int { return s.netVersionId } -func (s *Ethereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } +// Deprecated Accessors + +func (s *Ethereum) BlockChain() *core.BlockChain { return s.blockchain } +func (s *Ethereum) ChainDb() ethdb.Database { return s.chainDb } +func (s *Ethereum) TxPool() *core.TxPool { return s.txPool } +func (s *Ethereum) Pow() *ethash.Ethash { return s.pow } +func (s *Ethereum) Miner() *miner.Miner { return s.miner } // Protocols implements node.Service, returning all the currently configured // network protocols to start. @@ -416,7 +420,7 @@ func (s *Ethereum) Protocols() []p2p.Protocol { // Start implements node.Service, starting all internal goroutines needed by the // Ethereum protocol implementation. func (s *Ethereum) Start(srvr *p2p.Server) error { - s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) + s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.netVersionId) if s.AutoDAG { s.StartAutoDAG() } @@ -518,12 +522,6 @@ func (self *Ethereum) StopAutoDAG() { glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir) } -// HTTPClient returns the light http client used for fetching offchain docs -// (natspec, source for verification) -func (self *Ethereum) HTTPClient() *httpclient.HTTPClient { - return self.httpclient -} - // dagFiles(epoch) returns the two alternative DAG filenames (not a path) // 1) - 2) full-R- func dagFiles(epoch uint64) (string, string) { diff --git a/eth/bind.go b/eth/bind.go deleted file mode 100644 index 747965d370..0000000000 --- a/eth/bind.go +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package eth - -import ( - "math/big" - - "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/context" -) - -// ContractBackend implements bind.ContractBackend with direct calls to Ethereum -// internals to support operating on contracts within subprotocols like eth and -// swarm. -// -// Internally this backend uses the already exposed API endpoints of the Ethereum -// object. These should be rewritten to internal Go method calls when the Go API -// is refactored to support a clean library use. -type ContractBackend struct { - eapi *ethapi.PublicEthereumAPI // Wrapper around the Ethereum object to access metadata - bcapi *ethapi.PublicBlockChainAPI // Wrapper around the blockchain to access chain data - txapi *ethapi.PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data -} - -// NewContractBackend creates a new native contract backend using an existing -// Etheruem object. -func NewContractBackend(apiBackend ethapi.Backend) *ContractBackend { - return &ContractBackend{ - eapi: ethapi.NewPublicEthereumAPI(apiBackend), - bcapi: ethapi.NewPublicBlockChainAPI(apiBackend), - txapi: ethapi.NewPublicTransactionPoolAPI(apiBackend), - } -} - -// CodeAt retrieves any code associated with the contract from the local API. -func (b *ContractBackend) CodeAt(ctx context.Context, contract common.Address, blockNum *big.Int) ([]byte, error) { - out, err := b.bcapi.GetCode(ctx, contract, toBlockNumber(blockNum)) - return common.FromHex(out), err -} - -// CodeAt retrieves any code associated with the contract from the local API. -func (b *ContractBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) { - out, err := b.bcapi.GetCode(ctx, contract, rpc.PendingBlockNumber) - return common.FromHex(out), err -} - -// ContractCall implements bind.ContractCaller executing an Ethereum contract -// call with the specified data as the input. The pending flag requests execution -// against the pending block, not the stable head of the chain. -func (b *ContractBackend) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNum *big.Int) ([]byte, error) { - out, err := b.bcapi.Call(ctx, toCallArgs(msg), toBlockNumber(blockNum)) - return common.FromHex(out), err -} - -// ContractCall implements bind.ContractCaller executing an Ethereum contract -// call with the specified data as the input. The pending flag requests execution -// against the pending block, not the stable head of the chain. -func (b *ContractBackend) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) { - out, err := b.bcapi.Call(ctx, toCallArgs(msg), rpc.PendingBlockNumber) - return common.FromHex(out), err -} - -func toCallArgs(msg ethereum.CallMsg) ethapi.CallArgs { - args := ethapi.CallArgs{ - To: msg.To, - From: msg.From, - Data: common.ToHex(msg.Data), - } - if msg.Gas != nil { - args.Gas = *rpc.NewHexNumber(msg.Gas) - } - if msg.GasPrice != nil { - args.GasPrice = *rpc.NewHexNumber(msg.GasPrice) - } - if msg.Value != nil { - args.Value = *rpc.NewHexNumber(msg.Value) - } - return args -} - -func toBlockNumber(num *big.Int) rpc.BlockNumber { - if num == nil { - return rpc.LatestBlockNumber - } - return rpc.BlockNumber(num.Int64()) -} - -// PendingAccountNonce implements bind.ContractTransactor retrieving the current -// pending nonce associated with an account. -func (b *ContractBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { - out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber) - return out.Uint64(), err -} - -// SuggestGasPrice implements bind.ContractTransactor retrieving the currently -// suggested gas price to allow a timely execution of a transaction. -func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { - return b.eapi.GasPrice(ctx) -} - -// EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas -// needed to execute a specific transaction based on the current pending state of -// the backend blockchain. There is no guarantee that this is the true gas limit -// requirement as other transactions may be added or removed by miners, but it -// should provide a basis for setting a reasonable default. -func (b *ContractBackend) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) { - out, err := b.bcapi.EstimateGas(ctx, toCallArgs(msg)) - return out.BigInt(), err -} - -// SendTransaction implements bind.ContractTransactor injects the transaction -// into the pending pool for execution. -func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error { - raw, _ := rlp.EncodeToBytes(tx) - _, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw)) - return err -} diff --git a/eth/downloader/api.go b/eth/downloader/api.go index e41376810f..672f6124c1 100644 --- a/eth/downloader/api.go +++ b/eth/downloader/api.go @@ -76,7 +76,7 @@ func (api *PublicDownloaderAPI) eventLoop() { case StartEvent: notification = &SyncingResult{ Syncing: true, - Status: api.d.Progress(), + Status: *api.d.Progress(), } case DoneEvent, FailedEvent: notification = false diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index b1f4b8169b..5cd7fb2491 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -212,7 +212,7 @@ func New(mode SyncMode, stateDb ethdb.Database, mux *event.TypeMux, hasHeader he // In addition, during the state download phase of fast synchronisation the number // of processed and the total number of known states are also returned. Otherwise // these are zero. -func (d *Downloader) Progress() ethereum.SyncProgress { +func (d *Downloader) Progress() *ethereum.SyncProgress { // Fetch the pending state count outside of the lock to prevent unforeseen deadlocks pendingStates := uint64(d.queue.PendingNodeData()) @@ -229,7 +229,7 @@ func (d *Downloader) Progress() ethereum.SyncProgress { case LightSync: current = d.headHeader().Number.Uint64() } - return ethereum.SyncProgress{ + return ðereum.SyncProgress{ StartingBlock: d.syncStatsChainOrigin, CurrentBlock: current, HighestBlock: d.syncStatsChainHeight, diff --git a/eth/filters/api.go b/eth/filters/api.go index 8345132629..9c0c3fcab0 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -56,20 +56,20 @@ type PublicFilterAPI struct { useMipMap bool mux *event.TypeMux quit chan struct{} - chainDb ethdb.Database + chaindb ethdb.Database events *EventSystem filtersMu sync.Mutex filters map[rpc.ID]*filter } // NewPublicFilterAPI returns a new PublicFilterAPI instance. -func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { +func NewPublicFilterAPI(backend Backend, mux *event.TypeMux, chaindb ethdb.Database, lightMode bool) *PublicFilterAPI { api := &PublicFilterAPI{ backend: backend, useMipMap: !lightMode, - mux: backend.EventMux(), - chainDb: backend.ChainDb(), - events: NewEventSystem(backend.EventMux(), backend, lightMode), + mux: mux, + chaindb: chaindb, + events: NewEventSystem(mux, backend, lightMode), filters: make(map[rpc.ID]*filter), } @@ -326,7 +326,7 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([ crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) } - filter := New(api.backend, api.useMipMap) + filter := New(api.backend, api.chaindb, api.useMipMap) filter.SetBeginBlock(crit.FromBlock.Int64()) filter.SetEndBlock(crit.ToBlock.Int64()) filter.SetAddresses(crit.Addresses) @@ -366,7 +366,7 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log return []Log{}, nil } - filter := New(api.backend, api.useMipMap) + filter := New(api.backend, api.chaindb, api.useMipMap) filter.SetBeginBlock(f.crit.FromBlock.Int64()) filter.SetEndBlock(f.crit.ToBlock.Int64()) filter.SetAddresses(f.crit.Addresses) diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 4004af3002..ce012df10e 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -18,22 +18,20 @@ package filters import ( "math" + "math/big" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) type Backend interface { - ChainDb() ethdb.Database - EventMux() *event.TypeMux - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) + HeaderByHash(context.Context, common.Hash) (*types.Header, error) + HeaderByNumber(context.Context, *big.Int) (*types.Header, error) + BlockReceipts(ctx context.Context, blockhash common.Hash, blocknum uint64) ([]*types.Receipt, error) } // Filter can be used to retrieve and filter logs @@ -51,11 +49,11 @@ type Filter struct { // New creates a new filter which uses a bloom filter on blocks to figure out whether // a particular block is interesting or not. -func New(backend Backend, useMipMap bool) *Filter { +func New(backend Backend, chaindb ethdb.Database, useMipMap bool) *Filter { return &Filter{ backend: backend, useMipMap: useMipMap, - db: backend.ChainDb(), + db: chaindb, } } @@ -84,7 +82,7 @@ func (f *Filter) SetTopics(topics [][]common.Hash) { // Run filters logs with the current parameters set func (f *Filter) Find(ctx context.Context) ([]Log, error) { - head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + head, _ := f.backend.HeaderByNumber(ctx, nil) if head == nil { return nil, nil } @@ -141,7 +139,7 @@ func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) { func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) { for i := start; i <= end; i++ { - header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(i)) + header, err := f.backend.HeaderByNumber(ctx, new(big.Int).SetUint64(i)) if header == nil || err != nil { return logs, err } @@ -150,7 +148,7 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er // current parameters if f.bloomFilter(header.Bloom) { // Get the logs of the block - receipts, err := f.backend.GetReceipts(ctx, header.Hash()) + receipts, err := f.backend.BlockReceipts(ctx, header.Hash(), i) if err != nil { return nil, err } diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index c2c072a9f9..b3dd8838a0 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -30,6 +30,8 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -301,27 +303,44 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) { func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func(*types.Header, bool)) { oldh := es.lastHead - es.lastHead = newHeader if oldh == nil { return } - newh := newHeader + ctx, _ := context.WithTimeout(context.Background(), time.Second*5) + // find common ancestor, create list of rolled back and new block hashes - var oldHeaders, newHeaders []*types.Header + var ( + oldHeaders, newHeaders []*types.Header + newh = newHeader + err error + ) for oldh.Hash() != newh.Hash() { if oldh.Number.Uint64() >= newh.Number.Uint64() { oldHeaders = append(oldHeaders, oldh) - oldh = core.GetHeader(es.backend.ChainDb(), oldh.ParentHash, oldh.Number.Uint64()-1) + oldh, err = es.backend.HeaderByHash(ctx, oldh.ParentHash) + if err != nil { + break + } } if oldh.Number.Uint64() < newh.Number.Uint64() { newHeaders = append(newHeaders, newh) - newh = core.GetHeader(es.backend.ChainDb(), newh.ParentHash, newh.Number.Uint64()-1) + newh, err = es.backend.HeaderByHash(ctx, newh.ParentHash) + if err != nil { + break + } if newh == nil { // happens when CHT syncing, nothing to do newh = oldh } } } + if err != nil { + if glog.V(logger.Error) { + glog.Errorf("aborted rollback, can't fetch header: %v", err) + } + return + } + // roll back old blocks for _, h := range oldHeaders { callBack(h, true) @@ -330,6 +349,7 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func for i := len(newHeaders) - 1; i >= 0; i-- { callBack(newHeaders[i], false) } + es.lastHead = newh } // filter logs of a single header in light client mode @@ -339,7 +359,7 @@ func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common. //fmt.Println("bloom match") // Get the logs of the block ctx, _ := context.WithTimeout(context.Background(), time.Second*5) - receipts, err := es.backend.GetReceipts(ctx, header.Hash()) + receipts, err := es.backend.BlockReceipts(ctx, header.Hash(), header.Number.Uint64()) if err != nil { return nil } diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index d6d4199cc2..3220b73203 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -38,7 +38,7 @@ var ( mux = new(event.TypeMux) db, _ = ethdb.NewMemDatabase() backend = &testBackend{mux, db} - api = NewPublicFilterAPI(backend, false) + api = NewPublicFilterAPI(backend, mux, db, false) ) type testBackend struct { @@ -46,30 +46,26 @@ type testBackend struct { db ethdb.Database } -func (b *testBackend) ChainDb() ethdb.Database { - return b.db -} - -func (b *testBackend) EventMux() *event.TypeMux { - return b.mux -} - -func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { +func (b *testBackend) HeaderByNumber(ctx context.Context, blockNum *big.Int) (*types.Header, error) { var hash common.Hash var num uint64 - if blockNr == rpc.LatestBlockNumber { + if blockNum == nil { hash = core.GetHeadBlockHash(b.db) num = core.GetBlockNumber(b.db, hash) } else { - num = uint64(blockNr) + num = blockNum.Uint64() hash = core.GetCanonicalHash(b.db, num) } return core.GetHeader(b.db, hash, num), nil } -func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { +func (b *testBackend) HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) { num := core.GetBlockNumber(b.db, blockHash) - return core.GetBlockReceipts(b.db, blockHash, num), nil + return core.GetHeader(b.db, blockHash, num), nil +} + +func (b *testBackend) BlockReceipts(ctx context.Context, blockHash common.Hash, blockNum uint64) ([]*types.Receipt, error) { + return core.GetBlockReceipts(b.db, blockHash, blockNum), nil } // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index a8c767ead6..96bb09f3f2 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -104,7 +104,7 @@ func BenchmarkMipmaps(b *testing.B) { } b.ResetTimer() - filter := New(backend, true) + filter := New(backend, db, true) filter.SetAddresses([]common.Address{addr1, addr2, addr3, addr4}) filter.SetBeginBlock(0) filter.SetEndBlock(-1) @@ -206,7 +206,7 @@ func TestFilters(t *testing.T) { } } - filter := New(backend, true) + filter := New(backend, db, true) filter.SetAddresses([]common.Address{addr}) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2, hash3, hash4}}) filter.SetBeginBlock(0) @@ -217,7 +217,7 @@ func TestFilters(t *testing.T) { t.Error("expected 4 log, got", len(logs)) } - filter = New(backend, true) + filter = New(backend, db, true) filter.SetAddresses([]common.Address{addr}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetBeginBlock(900) @@ -230,7 +230,7 @@ func TestFilters(t *testing.T) { t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) } - filter = New(backend, true) + filter = New(backend, db, true) filter.SetAddresses([]common.Address{addr}) filter.SetTopics([][]common.Hash{[]common.Hash{hash3}}) filter.SetBeginBlock(990) @@ -243,7 +243,7 @@ func TestFilters(t *testing.T) { t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) } - filter = New(backend, true) + filter = New(backend, db, true) filter.SetTopics([][]common.Hash{[]common.Hash{hash1, hash2}}) filter.SetBeginBlock(1) filter.SetEndBlock(10) @@ -254,7 +254,7 @@ func TestFilters(t *testing.T) { } failHash := common.BytesToHash([]byte("fail")) - filter = New(backend, true) + filter = New(backend, db, true) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}}) filter.SetBeginBlock(0) filter.SetEndBlock(-1) @@ -265,7 +265,7 @@ func TestFilters(t *testing.T) { } failAddr := common.BytesToAddress([]byte("failmenow")) - filter = New(backend, true) + filter = New(backend, db, true) filter.SetAddresses([]common.Address{failAddr}) filter.SetBeginBlock(0) filter.SetEndBlock(-1) @@ -275,7 +275,7 @@ func TestFilters(t *testing.T) { t.Error("expected 0 log, got", len(logs)) } - filter = New(backend, true) + filter = New(backend, db, true) filter.SetTopics([][]common.Hash{[]common.Hash{failHash}, []common.Hash{hash1}}) filter.SetBeginBlock(0) filter.SetEndBlock(-1) diff --git a/eth/gasprice/lightprice.go b/eth/gasprice/lightprice.go index 8886d32d7d..c96288a026 100644 --- a/eth/gasprice/lightprice.go +++ b/eth/gasprice/lightprice.go @@ -21,9 +21,8 @@ import ( "sort" "sync" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) @@ -38,7 +37,7 @@ const ( // LightPriceOracle recommends gas prices based on the content of recent // blocks. Suitable for both light and full clients. type LightPriceOracle struct { - backend ethapi.Backend + backend ethereum.ChainReader lastHead common.Hash lastPrice *big.Int cacheLock sync.RWMutex @@ -46,7 +45,7 @@ type LightPriceOracle struct { } // NewLightPriceOracle returns a new oracle. -func NewLightPriceOracle(backend ethapi.Backend) *LightPriceOracle { +func NewLightPriceOracle(backend ethereum.ChainReader) *LightPriceOracle { return &LightPriceOracle{ backend: backend, lastPrice: big.NewInt(LpoDefaultPrice), @@ -60,7 +59,7 @@ func (self *LightPriceOracle) SuggestPrice(ctx context.Context) (*big.Int, error lastPrice := self.lastPrice self.cacheLock.RUnlock() - head, _ := self.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) + head, _ := self.backend.HeaderByNumber(ctx, nil) headHash := head.Hash() if headHash == lastHead { return lastPrice, nil @@ -132,7 +131,7 @@ type lpResult struct { // getLowestPrice calculates the lowest transaction gas price in a given block // and sends it to the result channel. If the block is empty, price is nil. func (self *LightPriceOracle) getLowestPrice(ctx context.Context, blockNum uint64, chn chan lpResult) { - block, err := self.backend.BlockByNumber(ctx, rpc.BlockNumber(blockNum)) + block, err := self.backend.BlockByNumber(ctx, new(big.Int).SetUint64(blockNum)) if block == nil { chn <- lpResult{nil, err} return diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index a095aa0767..9e0a87dae4 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -19,6 +19,7 @@ package ethclient import ( "encoding/json" + "errors" "fmt" "math/big" @@ -143,15 +144,18 @@ func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.H } // TransactionByHash returns the transaction with the given hash. -func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, error) { - var tx *types.Transaction - err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash) +func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) { + var rtx struct { + *types.Transaction + BlockHash *common.Hash + } + err = ec.c.CallContext(ctx, &rtx, "eth_getTransactionByHash", hash) if err == nil { if _, r, _ := tx.RawSignatureValues(); r == nil { - return nil, fmt.Errorf("server returned transaction without signature") + return nil, false, errors.New("server returned transaction without signature") } } - return tx, err + return rtx.Transaction, rtx.BlockHash == nil, err } // TransactionCount returns the total number of transactions in the given block. diff --git a/interfaces.go b/interfaces.go index aab0e2029c..c9a2e40a73 100644 --- a/interfaces.go +++ b/interfaces.go @@ -53,8 +53,8 @@ type ChainReader interface { HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) - TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, error) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) + TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) } // ChainStateReader wraps access to the state trie of the canonical blockchain. Note that @@ -164,6 +164,9 @@ type PendingStateReader interface { PendingTransactionCount(ctx context.Context) (uint, error) } +// TODO(fjl): PendingNonceAt should have its own interface because it is provided by the tx pool +// and more widely available than the others. We could also make it part of TransactionSender. + // PendingContractCaller can be used to perform calls against the pending state. type PendingContractCaller interface { PendingCallContract(ctx context.Context, call CallMsg) ([]byte, error) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index a25eff5ed8..ba918700ee 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -20,15 +20,16 @@ import ( "bytes" "encoding/hex" "encoding/json" + "errors" "fmt" "math/big" "strings" "time" "github.com/ethereum/ethash" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" @@ -45,6 +46,8 @@ import ( const defaultGas = uint64(90000) +var ErrNoPendingState = errors.New("API backend does not provide access to pending state") + // PublicEthereumAPI provides an API to access Ethereum related information. // It offers only methods that operate on public data that is freely available to anyone. type PublicEthereumAPI struct { @@ -58,7 +61,7 @@ func NewPublicEthereumAPI(b Backend) *PublicEthereumAPI { // GasPrice returns a suggestion for a gas price. func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*big.Int, error) { - return s.b.SuggestPrice(ctx) + return s.b.SuggestGasPrice(ctx) } // ProtocolVersion returns the current Ethereum protocol version this node supports @@ -73,8 +76,11 @@ func (s *PublicEthereumAPI) ProtocolVersion() *rpc.HexNumber { // - highestBlock: block number of the highest block header this node has received from peers // - pulledStates: number of state entries processed until now // - knownStates: number of known state entries that still need to be pulled -func (s *PublicEthereumAPI) Syncing() (interface{}, error) { - progress := s.b.Downloader().Progress() +func (s *PublicEthereumAPI) Syncing(ctx context.Context) (interface{}, error) { + progress, err := s.b.SyncProgress(ctx) + if err != nil { + return nil, err + } // Return not syncing if the synchronisation already completed if progress.CurrentBlock >= progress.HighestBlock { @@ -90,87 +96,6 @@ func (s *PublicEthereumAPI) Syncing() (interface{}, error) { }, nil } -// PublicTxPoolAPI offers and API for the transaction pool. It only operates on data that is non confidential. -type PublicTxPoolAPI struct { - b Backend -} - -// NewPublicTxPoolAPI creates a new tx pool service that gives information about the transaction pool. -func NewPublicTxPoolAPI(b Backend) *PublicTxPoolAPI { - return &PublicTxPoolAPI{b} -} - -// Content returns the transactions contained within the transaction pool. -func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string]*RPCTransaction { - content := map[string]map[string]map[string]*RPCTransaction{ - "pending": make(map[string]map[string]*RPCTransaction), - "queued": make(map[string]map[string]*RPCTransaction), - } - pending, queue := s.b.TxPoolContent() - - // Flatten the pending transactions - for account, txs := range pending { - dump := make(map[string]*RPCTransaction) - for nonce, tx := range txs { - dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) - } - content["pending"][account.Hex()] = dump - } - // Flatten the queued transactions - for account, txs := range queue { - dump := make(map[string]*RPCTransaction) - for nonce, tx := range txs { - dump[fmt.Sprintf("%d", nonce)] = newRPCPendingTransaction(tx) - } - content["queued"][account.Hex()] = dump - } - return content -} - -// Status returns the number of pending and queued transaction in the pool. -func (s *PublicTxPoolAPI) Status() map[string]*rpc.HexNumber { - pending, queue := s.b.Stats() - return map[string]*rpc.HexNumber{ - "pending": rpc.NewHexNumber(pending), - "queued": rpc.NewHexNumber(queue), - } -} - -// Inspect retrieves the content of the transaction pool and flattens it into an -// easily inspectable list. -func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string]string { - content := map[string]map[string]map[string]string{ - "pending": make(map[string]map[string]string), - "queued": make(map[string]map[string]string), - } - pending, queue := s.b.TxPoolContent() - - // Define a formatter to flatten a transaction into a string - var format = func(tx *types.Transaction) string { - if to := tx.To(); to != nil { - return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice()) - } - return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice()) - } - // Flatten the pending transactions - for account, txs := range pending { - dump := make(map[string]string) - for nonce, tx := range txs { - dump[fmt.Sprintf("%d", nonce)] = format(tx) - } - content["pending"][account.Hex()] = dump - } - // Flatten the queued transactions - for account, txs := range queue { - dump := make(map[string]string) - for nonce, tx := range txs { - dump[fmt.Sprintf("%d", nonce)] = format(tx) - } - content["queued"][account.Hex()] = dump - } - return content -} - // PublicAccountAPI provides an API to access accounts managed by this node. // It offers only methods that can retrieve accounts. type PublicAccountAPI struct { @@ -265,7 +190,7 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs } if args.Nonce == nil { - nonce, err := s.b.GetPoolNonce(ctx, args.From) + nonce, err := s.b.PendingNonceAt(ctx, args.From) if err != nil { return common.Hash{}, err } @@ -279,13 +204,17 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } - signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + head, err := s.b.HeaderByNumber(ctx, nil) + if err != nil { + return common.Hash{}, err + } + signer := types.MakeSigner(s.b.ChainConfig(), head.Number) signature, err := s.am.SignWithPassphrase(args.From, passwd, signer.Hash(tx).Bytes()) if err != nil { return common.Hash{}, err } - return submitTransaction(ctx, s.b, tx, signature) + return submitTransaction(ctx, s.b, tx, signer, signature) } // signHash is a helper function that calculates a hash for the given message that can be @@ -367,28 +296,16 @@ func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { // BlockNumber returns the block number of the chain head. func (s *PublicBlockChainAPI) BlockNumber() *big.Int { - header, _ := s.b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber) // latest header should always be available + header, _ := s.b.HeaderByNumber(context.Background(), nil) // latest header should always be available return header.Number } -// GetBalance returns the amount of wei for the given address in the state of the -// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta -// block numbers are also allowed. -func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return nil, err - } - - return state.GetBalance(ctx, address) -} - // GetBlockByNumber returns the requested block. When blockNr is -1 the chain head is returned. When fullTx is true all // transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { - block, err := s.b.BlockByNumber(ctx, blockNr) + block, err := blockByNumber(ctx, s.b, blockNr) if block != nil { - response, err := s.rpcOutputBlock(block, true, fullTx) + response, err := s.rpcOutputBlock(ctx, block, true, fullTx) if err == nil && blockNr == rpc.PendingBlockNumber { // Pending blocks need to nil out a few fields for _, field := range []string{"hash", "nonce", "logsBloom", "miner"} { @@ -400,12 +317,27 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc. return nil, err } +func blockByNumber(ctx context.Context, b Backend, blockNr rpc.BlockNumber) (*types.Block, error) { + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := b.(PendingState) + if !ok { + return nil, fmt.Errorf("can't access the pending block with the current backend") + } + return ps.PendingBlock() + case rpc.LatestBlockNumber: + return b.BlockByNumber(ctx, nil) + default: + return b.BlockByNumber(ctx, big.NewInt(int64(blockNr))) + } +} + // GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full // detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { - block, err := s.b.GetBlock(ctx, blockHash) + block, err := s.b.BlockByHash(ctx, blockHash) if block != nil { - return s.rpcOutputBlock(block, true, fullTx) + return s.rpcOutputBlock(ctx, block, true, fullTx) } return nil, err } @@ -413,7 +345,7 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, blockHash comm // GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index. When fullTx is true // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (map[string]interface{}, error) { - block, err := s.b.BlockByNumber(ctx, blockNr) + block, err := blockByNumber(ctx, s.b, blockNr) if block != nil { uncles := block.Uncles() if index.Int() < 0 || index.Int() >= len(uncles) { @@ -421,7 +353,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, return nil, nil } block = types.NewBlockWithHeader(uncles[index.Int()]) - return s.rpcOutputBlock(block, false, false) + return s.rpcOutputBlock(ctx, block, false, false) } return nil, err } @@ -429,7 +361,7 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, // GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index. When fullTx is true // all transactions in the block are returned in full detail, otherwise only the transaction hash is returned. func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (map[string]interface{}, error) { - block, err := s.b.GetBlock(ctx, blockHash) + block, err := s.b.BlockByHash(ctx, blockHash) if block != nil { uncles := block.Uncles() if index.Int() < 0 || index.Int() >= len(uncles) { @@ -437,14 +369,14 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, b return nil, nil } block = types.NewBlockWithHeader(uncles[index.Int()]) - return s.rpcOutputBlock(block, false, false) + return s.rpcOutputBlock(ctx, block, false, false) } return nil, err } // GetUncleCountByBlockNumber returns number of uncles in the block for the given block number func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *rpc.HexNumber { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil { return rpc.NewHexNumber(len(block.Uncles())) } return nil @@ -452,60 +384,78 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, bl // GetUncleCountByBlockHash returns number of uncles in the block for the given block hash func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *rpc.HexNumber { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { return rpc.NewHexNumber(len(block.Uncles())) } return nil } +// GetBalance returns the amount of wei for the given address in the state of the +// given block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta +// block numbers are also allowed. +func (s *PublicBlockChainAPI) GetBalance(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := s.b.(PendingState) + if !ok { + return nil, ErrNoPendingState + } + return ps.PendingBalanceAt(ctx, address) + case rpc.LatestBlockNumber: + return s.b.BalanceAt(ctx, address, nil) + default: + return s.b.BalanceAt(ctx, address, big.NewInt(int64(blockNr))) + } +} + // GetCode returns the code stored at the given address in the state for the given block number. func (s *PublicBlockChainAPI) GetCode(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (string, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return "", err + var code []byte + var err error + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := s.b.(PendingState) + if !ok { + return "", ErrNoPendingState + } + code, err = ps.PendingCodeAt(ctx, address) + case rpc.LatestBlockNumber: + code, err = s.b.CodeAt(ctx, address, nil) + default: + code, err = s.b.CodeAt(ctx, address, big.NewInt(int64(blockNr))) } - res, err := state.GetCode(ctx, address) - if len(res) == 0 || err != nil { // backwards compatibility + + if len(code) == 0 || err != nil { // backwards compatibility return "0x", err } - return common.ToHex(res), nil + return common.ToHex(code), nil } // GetStorageAt returns the storage from the state at the given address, key and // block number. The rpc.LatestBlockNumber and rpc.PendingBlockNumber meta block // numbers are also allowed. -func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key string, blockNr rpc.BlockNumber) (string, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { +func (s *PublicBlockChainAPI) GetStorageAt(ctx context.Context, address common.Address, key common.Hash, blockNr rpc.BlockNumber) (string, error) { + var val []byte + var err error + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := s.b.(PendingState) + if !ok { + return "", ErrNoPendingState + } + val, err = ps.PendingStorageAt(ctx, address, key) + case rpc.LatestBlockNumber: + val, err = s.b.StorageAt(ctx, address, key, nil) + default: + val, err = s.b.StorageAt(ctx, address, key, big.NewInt(int64(blockNr))) + } + + if len(val) == 0 || err != nil { // backwards compatibility return "0x", err } - res, err := state.GetState(ctx, address, common.HexToHash(key)) - if err != nil { - return "0x", err - } - return res.Hex(), nil + return common.ToHex(val), nil } -// callmsg is the message type used for call transations. -type callmsg struct { - addr common.Address - to *common.Address - gas, gasPrice *big.Int - value *big.Int - data []byte -} - -// accessor boilerplate to implement core.Message -func (m callmsg) From() (common.Address, error) { return m.addr, nil } -func (m callmsg) FromFrontier() (common.Address, error) { return m.addr, nil } -func (m callmsg) Nonce() uint64 { return 0 } -func (m callmsg) CheckNonce() bool { return false } -func (m callmsg) To() *common.Address { return m.to } -func (m callmsg) GasPrice() *big.Int { return m.gasPrice } -func (m callmsg) Gas() *big.Int { return m.gas } -func (m callmsg) Value() *big.Int { return m.value } -func (m callmsg) Data() []byte { return m.data } - // CallArgs represents the arguments for a call. type CallArgs struct { From common.Address `json:"from"` @@ -513,66 +463,43 @@ type CallArgs struct { Gas rpc.HexNumber `json:"gas"` GasPrice rpc.HexNumber `json:"gasPrice"` Value rpc.HexNumber `json:"value"` - Data string `json:"data"` + Data rpc.HexBytes `json:"data"` } -func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { - defer func(start time.Time) { glog.V(logger.Debug).Infof("call took %v", time.Since(start)) }(time.Now()) - - state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return "0x", common.Big0, err +func (args CallArgs) Msg() ethereum.CallMsg { + return ethereum.CallMsg{ + From: args.From, + To: args.To, + Gas: args.Gas.BigInt(), + GasPrice: args.GasPrice.BigInt(), + Value: args.Value.BigInt(), + Data: args.Data, } - - // Set the account address to interact with - var addr common.Address - if args.From == (common.Address{}) { - accounts := s.b.AccountManager().Accounts() - if len(accounts) == 0 { - addr = common.Address{} - } else { - addr = accounts[0].Address - } - } else { - addr = args.From - } - - // Assemble the CALL invocation - gas, gasPrice := args.Gas.BigInt(), args.GasPrice.BigInt() - if gas.Cmp(common.Big0) == 0 { - gas = big.NewInt(50000000) - } - if gasPrice.Cmp(common.Big0) == 0 { - gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) - } - msg := types.NewMessage(addr, args.To, 0, args.Value.BigInt(), gas, gasPrice, common.FromHex(args.Data), false) - - // Execute the call and return - vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) - if err != nil { - return "0x", common.Big0, err - } - gp := new(core.GasPool).AddGas(common.MaxBig) - res, gas, err := core.ApplyMessage(vmenv, msg, gp) - if err := vmError(); err != nil { - return "0x", common.Big0, err - } - if len(res) == 0 { // backwards compatability - return "0x", gas, err - } - return common.ToHex(res), gas, err } // Call executes the given transaction on the state for the given block number. // It doesn't make and changes in the state/blockchain and is usefull to execute and retrieve values. func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, error) { - result, _, err := s.doCall(ctx, args, blockNr) - return result, err + var result []byte + var err error + switch blockNr { + case rpc.PendingBlockNumber: + ps, ok := s.b.(PendingState) + if !ok { + return "", ErrNoPendingState + } + result, err = ps.PendingCallContract(ctx, args.Msg()) + case rpc.LatestBlockNumber: + result, err = s.b.CallContract(ctx, args.Msg(), nil) + default: + result, err = s.b.CallContract(ctx, args.Msg(), big.NewInt(int64(blockNr))) + } + return common.ToHex(result), err } // EstimateGas returns an estimate of the amount of gas needed to execute the given transaction. func (s *PublicBlockChainAPI) EstimateGas(ctx context.Context, args CallArgs) (*rpc.HexNumber, error) { - _, gas, err := s.doCall(ctx, args, rpc.PendingBlockNumber) + gas, err := s.b.EstimateGas(ctx, args.Msg()) return rpc.NewHexNumber(gas), err } @@ -632,7 +559,7 @@ func FormatLogs(structLogs []vm.StructLog) []StructLogRes { // rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are // returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain // transaction hashes. -func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { +func (s *PublicBlockChainAPI) rpcOutputBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) { head := b.Header() // copies the header once fields := map[string]interface{}{ "number": rpc.NewHexNumber(head.Number), @@ -645,7 +572,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx "stateRoot": head.Root, "miner": head.Coinbase, "difficulty": rpc.NewHexNumber(head.Difficulty), - "totalDifficulty": rpc.NewHexNumber(s.b.GetTd(b.Hash())), + "totalDifficulty": rpc.NewHexNumber(s.b.BlockTD(b.Hash())), "extraData": rpc.HexBytes(head.Extra), "size": rpc.NewHexNumber(b.Size().Int64()), "gasLimit": rpc.NewHexNumber(head.GasLimit), @@ -656,22 +583,15 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx } if inclTx { - formatTx := func(tx *types.Transaction) (interface{}, error) { - return tx.Hash(), nil - } - - if fullTx { - formatTx = func(tx *types.Transaction) (interface{}, error) { - return newRPCTransaction(b, tx.Hash()) - } - } - txs := b.Transactions() transactions := make([]interface{}, len(txs)) - var err error for i, tx := range b.Transactions() { - if transactions[i], err = formatTx(tx); err != nil { - return nil, err + if fullTx { + rtx := newRPCTransaction(tx) + rtx.setInclusionBlock(b.Hash(), b.NumberU64(), i) + transactions = append(transactions, rtx) + } else { + transactions = append(transactions, tx.Hash()) } } fields["transactions"] = transactions @@ -705,14 +625,20 @@ type RPCTransaction struct { S *rpc.HexNumber `json:"s"` } -// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation -func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { +func (tx *RPCTransaction) setInclusionBlock(blockhash common.Hash, blocknum uint64, index int) { + tx.BlockHash = blockhash + tx.BlockNumber = rpc.NewHexNumber(blocknum) + tx.TransactionIndex = rpc.NewHexNumber(index) +} + +// newRPCTransaction returns a pending transaction that will serialize to the RPC representation +func newRPCTransaction(tx *types.Transaction) *RPCTransaction { var signer types.Signer = types.FrontierSigner{} if tx.Protected() { signer = types.NewEIP155Signer(tx.ChainId()) } from, _ := types.Sender(signer, tx) - v, r, s := types.SignatureValues(signer, tx) + v, r, s := tx.RawSignatureValues() return &RPCTransaction{ From: from, Gas: rpc.NewHexNumber(tx.Gas()), @@ -729,34 +655,13 @@ func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { } // newRPCTransaction returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransaction, error) { +func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) *RPCTransaction { if txIndex >= 0 && txIndex < len(b.Transactions()) { - tx := b.Transactions()[txIndex] - var signer types.Signer = types.FrontierSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } - from, _ := types.Sender(signer, tx) - v, r, s := tx.RawSignatureValues() - return &RPCTransaction{ - BlockHash: b.Hash(), - BlockNumber: rpc.NewHexNumber(b.Number()), - From: from, - Gas: rpc.NewHexNumber(tx.Gas()), - GasPrice: rpc.NewHexNumber(tx.GasPrice()), - Hash: tx.Hash(), - Input: rpc.HexBytes(tx.Data()), - Nonce: rpc.NewHexNumber(tx.Nonce()), - To: tx.To(), - TransactionIndex: rpc.NewHexNumber(txIndex), - Value: rpc.NewHexNumber(tx.Value()), - V: rpc.NewHexNumber(v), - R: rpc.NewHexNumber(r), - S: rpc.NewHexNumber(s), - }, nil + tx := newRPCTransaction(b.Transactions()[txIndex]) + tx.setInclusionBlock(b.Hash(), b.NumberU64(), txIndex) + return tx } - - return nil, nil + return nil } // newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. @@ -765,18 +670,6 @@ func newRPCRawTransactionFromBlockIndex(b *types.Block, txIndex int) (rpc.HexByt tx := b.Transactions()[txIndex] return rlp.EncodeToBytes(tx) } - - return nil, nil -} - -// newRPCTransaction returns a transaction that will serialize to the RPC representation. -func newRPCTransaction(b *types.Block, txHash common.Hash) (*RPCTransaction, error) { - for idx, tx := range b.Transactions() { - if tx.Hash() == txHash { - return newRPCTransactionFromBlockIndex(b, idx) - } - } - return nil, nil } @@ -790,27 +683,9 @@ func NewPublicTransactionPoolAPI(b Backend) *PublicTransactionPoolAPI { return &PublicTransactionPoolAPI{b} } -func getTransaction(chainDb ethdb.Database, b Backend, txHash common.Hash) (*types.Transaction, bool, error) { - txData, err := chainDb.Get(txHash.Bytes()) - isPending := false - tx := new(types.Transaction) - - if err == nil && len(txData) > 0 { - if err := rlp.DecodeBytes(txData, tx); err != nil { - return nil, isPending, err - } - } else { - // pending transaction? - tx = b.GetPoolTransaction(txHash) - isPending = true - } - - return tx, isPending, nil -} - // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *rpc.HexNumber { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil { return rpc.NewHexNumber(len(block.Transactions())) } return nil @@ -818,31 +693,32 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context. // GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *rpc.HexNumber { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { - return rpc.NewHexNumber(len(block.Transactions())) + count, err := s.b.TransactionCount(ctx, blockHash) + if err == nil { + return rpc.NewHexNumber(count) } return nil } // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (*RPCTransaction, error) { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { +func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) *RPCTransaction { + if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil { return newRPCTransactionFromBlockIndex(block, index.Int()) } - return nil, nil + return nil } // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (*RPCTransaction, error) { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { +func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) *RPCTransaction { + if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { return newRPCTransactionFromBlockIndex(block, index.Int()) } - return nil, nil + return nil } // GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index rpc.HexNumber) (rpc.HexBytes, error) { - if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { + if block, _ := blockByNumber(ctx, s.b, blockNr); block != nil { return newRPCRawTransactionFromBlockIndex(block, index.Int()) } return nil, nil @@ -850,7 +726,7 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx co // GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index rpc.HexNumber) (rpc.HexBytes, error) { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil { return newRPCRawTransactionFromBlockIndex(block, index.Int()) } return nil, nil @@ -858,11 +734,16 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx cont // GetTransactionCount returns the number of transactions the given address has sent for the given block number func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) { - state, _, err := s.b.StateAndHeaderByNumber(ctx, blockNr) - if state == nil || err != nil { - return nil, err + var nonce uint64 + var err error + switch blockNr { + case rpc.PendingBlockNumber: + nonce, err = s.b.PendingNonceAt(ctx, address) + case rpc.LatestBlockNumber: + nonce, err = s.b.NonceAt(ctx, address, nil) + default: + nonce, err = s.b.NonceAt(ctx, address, big.NewInt(int64(blockNr))) } - nonce, err := state.GetNonce(ctx, address) if err != nil { return nil, err } @@ -892,82 +773,57 @@ func getTransactionBlockData(chainDb ethdb.Database, txHash common.Hash) (common } // GetTransactionByHash returns the transaction for the given hash -func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txHash common.Hash) (*RPCTransaction, error) { - var tx *types.Transaction - var isPending bool - var err error - - if tx, isPending, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } else if tx == nil { +func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, txhash common.Hash) (*RPCTransaction, error) { + tx, isPending, err := s.b.TransactionByHash(ctx, txhash) + if tx == nil || err != nil { return nil, nil } - + rtx := newRPCTransaction(tx) if isPending { - return newRPCPendingTransaction(tx), nil + return rtx, nil + } else if tib, ok := s.b.(TransactionInclusionBlock); ok { + bhash, bnum, index, err := tib.TransactionInclusionBlock(txhash) + if err != nil { + return nil, err + } + rtx.setInclusionBlock(bhash, bnum, index) } - - blockHash, _, _, err := getTransactionBlockData(s.b.ChainDb(), txHash) - if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } - - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { - return newRPCTransaction(block, txHash) - } - - return nil, nil + return rtx, nil } // GetRawTransactionByHash returns the bytes of the transaction for the given hash. -func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txHash common.Hash) (rpc.HexBytes, error) { - var tx *types.Transaction - var err error - - if tx, _, err = getTransaction(s.b.ChainDb(), s.b, txHash); err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } else if tx == nil { +func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, txhash common.Hash) (rpc.HexBytes, error) { + tx, _, err := s.b.TransactionByHash(ctx, txhash) + if tx == nil || err != nil { return nil, nil } - return rlp.EncodeToBytes(tx) } // GetTransactionReceipt returns the transaction receipt for the given transaction hash. -func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (map[string]interface{}, error) { - receipt := core.GetReceipt(s.b.ChainDb(), txHash) - if receipt == nil { - glog.V(logger.Debug).Infof("receipt not found for transaction %s", txHash.Hex()) - return nil, nil - } - - tx, _, err := getTransaction(s.b.ChainDb(), s.b, txHash) +func (s *PublicTransactionPoolAPI) GetTransactionReceipt(ctx context.Context, txhash common.Hash) (map[string]interface{}, error) { + receipt, err := s.b.TransactionReceipt(ctx, txhash) if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) + glog.V(logger.Debug).Infof("can't find receipt for transaction %s: %v", txhash.Hex(), err) return nil, nil } - - txBlock, blockIndex, index, err := getTransactionBlockData(s.b.ChainDb(), txHash) + tx, isPending, err := s.b.TransactionByHash(ctx, txhash) if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) + glog.V(logger.Debug).Infof("can't find transaction %s: %v\n", txhash.Hex(), err) return nil, nil } - var signer types.Signer = types.FrontierSigner{} if tx.Protected() { signer = types.NewEIP155Signer(tx.ChainId()) } - from, _ := types.Sender(signer, tx) + from, err := types.Sender(signer, tx) + if err != nil { + return nil, err + } fields := map[string]interface{}{ "root": rpc.HexBytes(receipt.PostState), - "blockHash": txBlock, - "blockNumber": rpc.NewHexNumber(blockIndex), - "transactionHash": txHash, - "transactionIndex": rpc.NewHexNumber(index), + "transactionHash": txhash, "from": from, "to": tx.To(), "gasUsed": rpc.NewHexNumber(receipt.GasUsed), @@ -976,10 +832,21 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma "logs": receipt.Logs, "logsBloom": receipt.Bloom, } + // Set block inclusion information if available. + if tib, ok := s.b.(TransactionInclusionBlock); !isPending && ok { + bhash, bnum, index, err := tib.TransactionInclusionBlock(txhash) + if err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } + fields["blockHash"] = bhash + fields["blockNumber"] = rpc.NewHexNumber(bnum) + fields["transactionIndex"] = rpc.NewHexNumber(index) + } + if receipt.Logs == nil { fields["logs"] = []vm.Logs{} } - // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation if receipt.ContractAddress != (common.Address{}) { fields["contractAddress"] = receipt.ContractAddress } @@ -987,14 +854,14 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(txHash common.Hash) (ma } // sign is a helper function that signs a transaction with the private key of the given address. -func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transaction) (*types.Transaction, error) { - signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) - - signature, err := s.b.AccountManager().SignEthereum(addr, signer.Hash(tx).Bytes()) +func (s *PublicTransactionPoolAPI) sign(ctx context.Context, addr common.Address, tx *types.Transaction) ([]byte, types.Signer, error) { + head, err := s.b.HeaderByNumber(ctx, nil) if err != nil { - return nil, err + return nil, nil, err } - return tx.WithSignature(signer, signature) + signer := types.MakeSigner(s.b.ChainConfig(), head.Number) + sig, err := s.b.AccountManager().SignEthereum(addr, signer.Hash(tx).Bytes()) + return sig, signer, err } // SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. @@ -1014,7 +881,7 @@ func prepareSendTxArgs(ctx context.Context, args SendTxArgs, b Backend) (SendTxA args.Gas = rpc.NewHexNumber(defaultGas) } if args.GasPrice == nil { - price, err := b.SuggestPrice(ctx) + price, err := b.SuggestGasPrice(ctx) if err != nil { return args, err } @@ -1027,18 +894,11 @@ func prepareSendTxArgs(ctx context.Context, args SendTxArgs, b Backend) (SendTxA } // submitTransaction is a helper function that submits tx to txPool and creates a log entry. -func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, signature []byte) (common.Hash, error) { - signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number()) - - signedTx, err := tx.WithSignature(signer, signature) - if err != nil { +func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction, signer types.Signer, signature []byte) (common.Hash, error) { + signedTx := signer.WithSignature(tx, signature) + if err := b.SendTransaction(ctx, signedTx); err != nil { return common.Hash{}, err } - - if err := b.SendTx(ctx, signedTx); err != nil { - return common.Hash{}, err - } - if signedTx.To() == nil { from, _ := types.Sender(signer, signedTx) addr := crypto.CreateAddress(from, signedTx.Nonce()) @@ -1060,7 +920,7 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen } if args.Nonce == nil { - nonce, err := s.b.GetPoolNonce(ctx, args.From) + nonce, err := s.b.PendingNonceAt(ctx, args.From) if err != nil { return common.Hash{}, err } @@ -1074,13 +934,17 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } - signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) + head, err := s.b.HeaderByNumber(ctx, nil) + if err != nil { + return common.Hash{}, err + } + signer := types.MakeSigner(s.b.ChainConfig(), head.Number) signature, err := s.b.AccountManager().SignEthereum(args.From, signer.Hash(tx).Bytes()) if err != nil { return common.Hash{}, err } - return submitTransaction(ctx, s.b, tx, signature) + return submitTransaction(ctx, s.b, tx, signer, signature) } // SendRawTransaction will add the signed transaction to the transaction pool. @@ -1090,23 +954,9 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod if err := rlp.DecodeBytes(common.FromHex(encodedTx), tx); err != nil { return "", err } - - if err := s.b.SendTx(ctx, tx); err != nil { + if err := s.b.SendTransaction(ctx, tx); err != nil { return "", err } - - signer := types.MakeSigner(s.b.ChainConfig(), s.b.CurrentBlock().Number()) - if tx.To() == nil { - from, err := types.Sender(signer, tx) - if err != nil { - return "", err - } - addr := crypto.CreateAddress(from, tx.Nonce()) - glog.V(logger.Info).Infof("Tx(%x) created: %x\n", tx.Hash(), addr) - } else { - glog.V(logger.Info).Infof("Tx(%x) to: %x\n", tx.Hash(), tx.To()) - } - return tx.Hash().Hex(), nil } @@ -1233,7 +1083,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig args.Gas = rpc.NewHexNumber(defaultGas) } if args.GasPrice == nil { - price, err := s.b.SuggestPrice(ctx) + price, err := s.b.SuggestGasPrice(ctx) if err != nil { return nil, err } @@ -1244,7 +1094,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig } if args.Nonce == nil { - nonce, err := s.b.GetPoolNonce(ctx, args.From) + nonce, err := s.b.PendingNonceAt(ctx, args.From) if err != nil { return nil, err } @@ -1258,11 +1108,11 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) } - signedTx, err := s.sign(args.From, tx) + signature, signer, err := s.sign(ctx, args.From, tx) if err != nil { return nil, err } - + signedTx := signer.WithSignature(tx, signature) data, err := rlp.EncodeToBytes(signedTx) if err != nil { return nil, err @@ -1274,7 +1124,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sig // PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of // the accounts this node manages. func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { - pending := s.b.GetPoolTransactions() + pending := s.b.PendingTransactions() transactions := make([]*RPCTransaction, 0, len(pending)) for _, tx := range pending { var signer types.Signer = types.HomesteadSigner{} @@ -1283,7 +1133,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { } from, _ := types.Sender(signer, tx) if s.b.AccountManager().HasAddress(from) { - transactions = append(transactions, newRPCPendingTransaction(tx)) + transactions = append(transactions, newRPCTransaction(tx)) } } return transactions @@ -1292,7 +1142,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction { // Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the // pool and reinsert it with the new gas price and limit. func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { - pending := s.b.GetPoolTransactions() + pending := s.b.PendingTransactions() for _, p := range pending { var signer types.Signer = types.HomesteadSigner{} if p.Protected() { @@ -1314,17 +1164,14 @@ func (s *PublicTransactionPoolAPI) Resend(ctx context.Context, tx Tx, gasPrice, newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasLimit.BigInt(), gasPrice.BigInt(), tx.tx.Data()) } - signedTx, err := s.sign(tx.From, newTx) + signature, signer, err := s.sign(ctx, tx.From, newTx) if err != nil { return common.Hash{}, err } + newTx = signer.WithSignature(newTx, signature) - s.b.RemoveTx(tx.Hash) - if err = s.b.SendTx(ctx, signedTx); err != nil { - return common.Hash{}, err - } - - return signedTx.Hash(), nil + s.b.RemoveTransaction(tx.Hash) + return submitTransaction(ctx, s.b, newTx, signer, signature) } } @@ -1344,8 +1191,8 @@ func NewPublicDebugAPI(b Backend) *PublicDebugAPI { } // GetBlockRlp retrieves the RLP encoded for of a single block. -func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (string, error) { - block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) +func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number rpc.BlockNumber) (string, error) { + block, _ := blockByNumber(ctx, api.b, number) if block == nil { return "", fmt.Errorf("block #%d not found", number) } @@ -1357,8 +1204,8 @@ func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (stri } // PrintBlock retrieves a block and returns its pretty printed form. -func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (string, error) { - block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) +func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number rpc.BlockNumber) (string, error) { + block, _ := blockByNumber(ctx, api.b, number) if block == nil { return "", fmt.Errorf("block #%d not found", number) } @@ -1367,10 +1214,6 @@ func (api *PublicDebugAPI) PrintBlock(ctx context.Context, number uint64) (strin // SeedHash retrieves the seed hash of a block. func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, error) { - block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number)) - if block == nil { - return "", fmt.Errorf("block #%d not found", number) - } hash, err := ethash.GetSeedHash(number) if err != nil { return "", err @@ -1378,21 +1221,22 @@ func (api *PublicDebugAPI) SeedHash(ctx context.Context, number uint64) (string, return fmt.Sprintf("0x%x", hash), nil } -// PrivateDebugAPI is the collection of Etheruem APIs exposed over the private +// PrivateDebugAPI is the collection of Ethereum APIs exposed over the private // debugging endpoint. type PrivateDebugAPI struct { - b Backend + b Backend + chaindb ethdb.Database } // NewPrivateDebugAPI creates a new API definition for the private debug methods // of the Ethereum service. -func NewPrivateDebugAPI(b Backend) *PrivateDebugAPI { - return &PrivateDebugAPI{b: b} +func NewPrivateDebugAPI(b Backend, chaindb ethdb.Database) *PrivateDebugAPI { + return &PrivateDebugAPI{b, chaindb} } // ChaindbProperty returns leveldb properties of the chain database. func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { - ldb, ok := api.b.ChainDb().(interface { + ldb, ok := api.chaindb.(interface { LDB() *leveldb.DB }) if !ok { @@ -1407,7 +1251,7 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { } func (api *PrivateDebugAPI) ChaindbCompact() error { - ldb, ok := api.b.ChainDb().(interface { + ldb, ok := api.chaindb.(interface { LDB() *leveldb.DB }) if !ok { @@ -1426,7 +1270,7 @@ func (api *PrivateDebugAPI) ChaindbCompact() error { // SetHead rewinds the head of the blockchain to a previous block. func (api *PrivateDebugAPI) SetHead(number rpc.HexNumber) { - api.b.SetHead(uint64(number.Int64())) + api.b.ResetHeadBlock(number.Uint64()) } // PublicNetAPI offers network related RPC methods diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index fdc4a39dc8..7c52e311b2 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -20,56 +20,46 @@ package ethapi import ( "math/big" + "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) -// Backend interface provides the common API services (that are provided by -// both full and light clients) with access to necessary functions. +// Backend defines most methods required for the RPC API. type Backend interface { - // general Ethereum API - Downloader() *downloader.Downloader - ProtocolVersion() int - SuggestPrice(ctx context.Context) (*big.Int, error) - ChainDb() ethdb.Database - EventMux() *event.TypeMux - AccountManager() *accounts.Manager - // BlockChain API - SetHead(number uint64) - HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) - BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) - StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (State, *types.Header, error) - GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) - GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) - GetTd(blockHash common.Hash) *big.Int - GetVMEnv(ctx context.Context, msg core.Message, state State, header *types.Header) (vm.Environment, func() error, error) - // TxPool API - SendTx(ctx context.Context, signedTx *types.Transaction) error - RemoveTx(txHash common.Hash) - GetPoolTransactions() types.Transactions - GetPoolTransaction(txHash common.Hash) *types.Transaction - GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) - Stats() (pending int, queued int) - TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) + ethereum.ChainReader + ethereum.ChainStateReader + ethereum.ChainSyncReader + ethereum.TransactionSender + ethereum.GasPricer + ethereum.GasEstimator + ethereum.ContractCaller + ProtocolVersion() int ChainConfig() *params.ChainConfig - CurrentBlock() *types.Block + AccountManager() *accounts.Manager // TODO(fjl): this should be a constructor argb + BlockTD(common.Hash) *big.Int + RemoveTransaction(txhash common.Hash) + PendingTransactions() []*types.Transaction + PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) + ResetHeadBlock(number uint64) // for admin API } -type State interface { - GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) - GetCode(ctx context.Context, addr common.Address) ([]byte, error) - GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) - GetNonce(ctx context.Context, addr common.Address) (uint64, error) +// PendingState is implemented by the eth.Ethereum backend and provides access to optional +// features that can only be provided by a full pending state. +type PendingState interface { + PendingBlock() (*types.Block, error) + ethereum.PendingStateReader + ethereum.PendingContractCaller +} + +type TransactionInclusionBlock interface { + // returns the block at which the given transaction was included in the blockchain + TransactionInclusionBlock(txhash common.Hash) (blockhash common.Hash, blocknum uint64, index int, err error) } func GetAPIs(apiBackend Backend, solcPath string) []rpc.API { @@ -90,20 +80,11 @@ func GetAPIs(apiBackend Backend, solcPath string) []rpc.API { Version: "1.0", Service: NewPublicTransactionPoolAPI(apiBackend), Public: true, - }, { - Namespace: "txpool", - Version: "1.0", - Service: NewPublicTxPoolAPI(apiBackend), - Public: true, }, { Namespace: "debug", Version: "1.0", Service: NewPublicDebugAPI(apiBackend), Public: true, - }, { - Namespace: "debug", - Version: "1.0", - Service: NewPrivateDebugAPI(apiBackend), }, { Namespace: "eth", Version: "1.0", diff --git a/internal/ethapi/txpool.go b/internal/ethapi/txpool.go new file mode 100644 index 0000000000..1f458edf54 --- /dev/null +++ b/internal/ethapi/txpool.go @@ -0,0 +1,105 @@ +// Copyright 2016 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package ethapi + +import ( + "fmt" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" +) + +type TxPool interface { + Stats() (pending int, queued int) + Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) +} + +// TxPoolDebugAPI offers and API for the transaction pool. It only operates on data that +// is non confidential. +type TxPoolDebugAPI struct{ Pool TxPool } + +// Content returns the transactions contained within the transaction pool. +func (s TxPoolDebugAPI) Content() map[string]map[string]map[string]*RPCTransaction { + content := map[string]map[string]map[string]*RPCTransaction{ + "pending": make(map[string]map[string]*RPCTransaction), + "queued": make(map[string]map[string]*RPCTransaction), + } + pending, queue := s.Pool.Content() + + // Flatten the pending transactions + for account, txs := range pending { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCTransaction(tx) + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, txs := range queue { + dump := make(map[string]*RPCTransaction) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = newRPCTransaction(tx) + } + content["queued"][account.Hex()] = dump + } + return content +} + +// Status returns the number of pending and queued transaction in the pool. +func (s TxPoolDebugAPI) Status() map[string]*rpc.HexNumber { + pending, queue := s.Pool.Stats() + return map[string]*rpc.HexNumber{ + "pending": rpc.NewHexNumber(pending), + "queued": rpc.NewHexNumber(queue), + } +} + +// Inspect retrieves the content of the transaction pool and flattens it into an +// easily inspectable list. +func (s TxPoolDebugAPI) Inspect() map[string]map[string]map[string]string { + content := map[string]map[string]map[string]string{ + "pending": make(map[string]map[string]string), + "queued": make(map[string]map[string]string), + } + pending, queue := s.Pool.Content() + + // Define a formatter to flatten a transaction into a string + var format = func(tx *types.Transaction) string { + if to := tx.To(); to != nil { + return fmt.Sprintf("%s: %v wei + %v × %v gas", tx.To().Hex(), tx.Value(), tx.Gas(), tx.GasPrice()) + } + return fmt.Sprintf("contract creation: %v wei + %v × %v gas", tx.Value(), tx.Gas(), tx.GasPrice()) + } + // Flatten the pending transactions + for account, txs := range pending { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, txs := range queue { + dump := make(map[string]string) + for nonce, tx := range txs { + dump[fmt.Sprintf("%d", nonce)] = format(tx) + } + content["queued"][account.Hex()] = dump + } + return content +} diff --git a/les/api_backend.go b/les/api_backend.go index b77767ed70..413784c9a5 100644 --- a/les/api_backend.go +++ b/les/api_backend.go @@ -17,137 +17,208 @@ package les import ( + "errors" + "fmt" "math/big" + ethereum "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/params" - rpc "github.com/ethereum/go-ethereum/rpc" "golang.org/x/net/context" ) -type LesApiBackend struct { - eth *LightEthereum - gpo *gasprice.LightPriceOracle -} +var ( + ErrBlockNotFound = errors.New("block not found") + ErrTxNotFound = errors.New("the light client cannot retrieve past transactions by hash") + ErrReceiptNotFound = errors.New("the light client cannot retrieve receipts by hash") +) -func (b *LesApiBackend) ChainConfig() *params.ChainConfig { - return b.eth.chainConfig -} - -func (b *LesApiBackend) CurrentBlock() *types.Block { - return types.NewBlockWithHeader(b.eth.BlockChain().CurrentHeader()) -} - -func (b *LesApiBackend) SetHead(number uint64) { - b.eth.blockchain.SetHead(number) -} - -func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { - if blockNr == rpc.LatestBlockNumber || blockNr == rpc.PendingBlockNumber { - return b.eth.blockchain.CurrentHeader(), nil +func (eth *LightEthereum) HeaderByHash(ctx context.Context, blockhash common.Hash) (*types.Header, error) { + if h := eth.blockchain.GetHeaderByHash(blockhash); h != nil { + return h, nil } - - return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr)) + return nil, ErrBlockNotFound } -func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { - header, err := b.HeaderByNumber(ctx, blockNr) - if header == nil || err != nil { +func (eth *LightEthereum) HeaderByNumber(ctx context.Context, blocknum *big.Int) (*types.Header, error) { + if blocknum == nil { + return eth.blockchain.CurrentHeader(), nil + } + h, err := eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blocknum.Uint64())) + if err != nil { return nil, err } - return b.GetBlock(ctx, header.Hash()) -} - -func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) { - header, err := b.HeaderByNumber(ctx, blockNr) - if header == nil || err != nil { - return nil, nil, err + if h == nil { + return nil, ErrBlockNotFound } - return light.NewLightState(light.StateTrieID(header), b.eth.odr), header, nil + return h, nil } -func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { - return b.eth.blockchain.GetBlockByHash(ctx, blockHash) -} - -func (b *LesApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { - return light.GetBlockReceipts(ctx, b.eth.odr, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)) -} - -func (b *LesApiBackend) GetTd(blockHash common.Hash) *big.Int { - return b.eth.blockchain.GetTdByHash(blockHash) -} - -func (b *LesApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) { - stateDb := state.(*light.LightState).Copy() - addr := msg.From() - from, err := stateDb.GetOrNewStateObject(ctx, addr) +func (eth *LightEthereum) BlockByNumber(ctx context.Context, blocknum *big.Int) (*types.Block, error) { + header, err := eth.HeaderByNumber(ctx, blocknum) if err != nil { - return nil, nil, err + return nil, err } - from.SetBalance(common.MaxBig) - env := light.NewEnv(ctx, stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}) - return env, env.Error, nil + return eth.BlockByHash(ctx, header.Hash()) } -func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { - return b.eth.txPool.Add(ctx, signedTx) +func (eth *LightEthereum) BlockByHash(ctx context.Context, blockhash common.Hash) (*types.Block, error) { + return eth.blockchain.GetBlockByHash(ctx, blockhash) } -func (b *LesApiBackend) RemoveTx(txHash common.Hash) { - b.eth.txPool.RemoveTx(txHash) +func (eth *LightEthereum) BlockReceipts(ctx context.Context, blockhash common.Hash, blocknum uint64) ([]*types.Receipt, error) { + return light.GetBlockReceipts(ctx, eth.odr, blockhash, blocknum) } -func (b *LesApiBackend) GetPoolTransactions() types.Transactions { - return b.eth.txPool.GetTransactions() +func (eth *LightEthereum) BlockTD(blockHash common.Hash) *big.Int { + return eth.blockchain.GetTdByHash(blockHash) } -func (b *LesApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { - return b.eth.txPool.GetTransaction(txHash) +func (eth *LightEthereum) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) { + if tx = eth.txPool.GetTransaction(txHash); tx != nil { + return tx, true, nil + } + return nil, false, ErrTxNotFound } -func (b *LesApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { - return b.eth.txPool.GetNonce(ctx, addr) +func (eth *LightEthereum) TransactionInBlock(ctx context.Context, blockhash common.Hash, index uint) (*types.Transaction, error) { + b, err := eth.blockchain.GetBlockByHash(ctx, blockhash) + if err != nil { + return nil, err + } + if index >= uint(len(b.Transactions())) { + return nil, fmt.Errorf("transaction index out of range") + } + return b.Transactions()[index], nil } -func (b *LesApiBackend) Stats() (pending int, queued int) { - return b.eth.txPool.Stats(), 0 +func (eth *LightEthereum) TransactionReceipt(ctx context.Context, txhash common.Hash) (*types.Receipt, error) { + return nil, ErrReceiptNotFound } -func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { - return b.eth.txPool.Content() +func (eth *LightEthereum) TransactionCount(ctx context.Context, blockhash common.Hash) (uint, error) { + b, err := eth.blockchain.GetBlockByHash(ctx, blockhash) + if err != nil { + return 0, err + } + return uint(len(b.Transactions())), nil } -func (b *LesApiBackend) Downloader() *downloader.Downloader { - return b.eth.Downloader() +func (eth *LightEthereum) BalanceAt(ctx context.Context, addr common.Address, blocknum *big.Int) (*big.Int, error) { + st, err := eth.state(ctx, blocknum) + if err != nil { + return nil, err + } + return st.GetBalance(ctx, addr) } -func (b *LesApiBackend) ProtocolVersion() int { - return b.eth.LesVersion() + 10000 +func (eth *LightEthereum) CodeAt(ctx context.Context, addr common.Address, blocknum *big.Int) ([]byte, error) { + st, err := eth.state(ctx, blocknum) + if err != nil { + return nil, err + } + return st.GetCode(ctx, addr) } -func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx) +func (eth *LightEthereum) NonceAt(ctx context.Context, addr common.Address, blocknum *big.Int) (uint64, error) { + st, err := eth.state(ctx, blocknum) + if err != nil { + return 0, err + } + return st.GetNonce(ctx, addr) } -func (b *LesApiBackend) ChainDb() ethdb.Database { - return b.eth.chainDb +func (eth *LightEthereum) StorageAt(ctx context.Context, addr common.Address, key common.Hash, blocknum *big.Int) ([]byte, error) { + st, err := eth.state(ctx, blocknum) + if err != nil { + return nil, err + } + v, err := st.GetState(ctx, addr, key) + if err != nil { + return nil, err + } + return v[:], nil } -func (b *LesApiBackend) EventMux() *event.TypeMux { - return b.eth.eventMux +func (eth *LightEthereum) PendingCodeAt(ctx context.Context, addr common.Address) ([]byte, error) { + // TODO(fjl): find a way to get rid of PendingCodeAt here. CodeAt is a bad emulation + // of PendingCodeAt because it forces users to wait for transactions to get mined. + return eth.CodeAt(ctx, addr, nil) } -func (b *LesApiBackend) AccountManager() *accounts.Manager { - return b.eth.accountManager +func (eth *LightEthereum) state(ctx context.Context, blocknum *big.Int) (*light.LightState, error) { + header, err := eth.HeaderByNumber(ctx, blocknum) + if err != nil { + return nil, err + } + return light.NewLightState(light.StateTrieID(header), eth.odr), nil +} + +func (eth *LightEthereum) SendTransaction(ctx context.Context, signedTx *types.Transaction) error { + return eth.txPool.Add(ctx, signedTx) +} + +func (eth *LightEthereum) RemoveTransaction(txHash common.Hash) { + eth.txPool.RemoveTx(txHash) +} + +func (eth *LightEthereum) PendingTransactions() []*types.Transaction { + return eth.txPool.GetTransactions() +} + +func (eth *LightEthereum) PendingNonceAt(ctx context.Context, addr common.Address) (uint64, error) { + return eth.txPool.GetNonce(ctx, addr) +} + +func (eth *LightEthereum) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) { + return eth.protocolManager.downloader.Progress(), nil +} + +// ChainConfig returns the active chain configuration. +func (eth *LightEthereum) ChainConfig() *params.ChainConfig { + return eth.chainConfig +} + +func (eth *LightEthereum) ProtocolVersion() int { + return int(eth.protocolManager.SubProtocols[0].Version) + 10000 +} + +func (eth *LightEthereum) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + return eth.gpo.SuggestPrice(ctx) +} + +func (eth *LightEthereum) AccountManager() *accounts.Manager { + return eth.accountManager +} + +func (eth *LightEthereum) ResetHeadBlock(number uint64) { + eth.blockchain.SetHead(number) +} + +func (eth *LightEthereum) EstimateGas(ctx context.Context, call ethereum.CallMsg) (usedGas *big.Int, err error) { + _, gas, err := eth.callContract(ctx, call, nil) + return gas, err +} + +func (eth *LightEthereum) CallContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, error) { + val, _, err := eth.callContract(ctx, call, blocknum) + return val, err +} + +func (eth *LightEthereum) callContract(ctx context.Context, call ethereum.CallMsg, blocknum *big.Int) ([]byte, *big.Int, error) { + var head *types.Header + if blocknum == nil { + head = eth.blockchain.CurrentHeader() + } else if head = eth.blockchain.GetHeaderByNumber(blocknum.Uint64()); head == nil { + return nil, nil, ErrBlockNotFound + } + state := light.NewLightState(light.StateTrieID(head), eth.odr) + return core.ApplyCallMessage(call, func(msg core.Message) vm.Environment { + return light.NewEnv(ctx, state, eth.chainConfig, eth.blockchain, msg, head, vm.Config{}) + }) } diff --git a/les/backend.go b/les/backend.go index 1264acfd18..ea260a8a28 100644 --- a/les/backend.go +++ b/les/backend.go @@ -58,7 +58,7 @@ type LightEthereum struct { // DB interfaces chainDb ethdb.Database // Block chain database - ApiBackend *LesApiBackend + gpo *gasprice.LightPriceOracle eventMux *event.TypeMux pow *ethash.Ethash @@ -120,8 +120,8 @@ func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { return nil, err } - eth.ApiBackend = &LesApiBackend{eth, nil} - eth.ApiBackend.gpo = gasprice.NewLightPriceOracle(eth.ApiBackend) + eth.gpo = gasprice.NewLightPriceOracle(eth) + return eth, nil } @@ -149,9 +149,13 @@ func (s *LightDummyAPI) Mining() bool { // APIs returns the collection of RPC services the ethereum package offers. // NOTE, some of these services probably need to be moved to somewhere else. -func (s *LightEthereum) APIs() []rpc.API { - return append(ethapi.GetAPIs(s.ApiBackend, s.solcPath), []rpc.API{ +func (eth *LightEthereum) APIs() []rpc.API { + return append(ethapi.GetAPIs(eth, eth.solcPath), []rpc.API{ { + Namespace: "debug", + Version: "1.0", + Service: ethapi.NewPrivateDebugAPI(eth, eth.chainDb), + }, { Namespace: "eth", Version: "1.0", Service: &LightDummyAPI{}, @@ -159,17 +163,22 @@ func (s *LightEthereum) APIs() []rpc.API { }, { Namespace: "eth", Version: "1.0", - Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), + Service: downloader.NewPublicDownloaderAPI(eth.protocolManager.downloader, eth.eventMux), Public: true, }, { Namespace: "eth", Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, true), + Service: filters.NewPublicFilterAPI(eth, eth.eventMux, eth.chainDb, true), Public: true, }, { + Namespace: "txpool", + Version: "1.0", + Service: ethapi.TxPoolDebugAPI{Pool: eth.txPool}, + }, + { Namespace: "net", Version: "1.0", - Service: s.netRPCService, + Service: eth.netRPCService, Public: true, }, }...) @@ -179,11 +188,6 @@ func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) { s.blockchain.ResetWithGenesisBlock(gb) } -func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain } -func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool } -func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } -func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } - // Protocols implements node.Service, returning all the currently configured // network protocols to start. func (s *LightEthereum) Protocols() []p2p.Protocol { diff --git a/les/server.go b/les/server.go index c763e8c632..924bf7c800 100644 --- a/les/server.go +++ b/les/server.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/les/flowcontrol" "github.com/ethereum/go-ethereum/light" "github.com/ethereum/go-ethereum/logger" @@ -44,8 +45,8 @@ type LesServer struct { defParams *flowcontrol.ServerParams } -func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { - pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, eth.EventMux(), eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil) +func NewLesServer(eth *eth.Ethereum, mux *event.TypeMux, config *eth.Config) (*LesServer, error) { + pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, mux, eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil) if err != nil { return nil, err } diff --git a/light/txpool.go b/light/txpool.go index 309bc3a322..ef0947b05a 100644 --- a/light/txpool.go +++ b/light/txpool.go @@ -325,12 +325,11 @@ func (pool *TxPool) Stop() { } // Stats returns the number of currently pending (locally created) transactions -func (pool *TxPool) Stats() (pending int) { +func (pool *TxPool) Stats() (pending int, queued int) { pool.mu.RLock() defer pool.mu.RUnlock() - pending = len(pool.pending) - return + return len(pool.pending), 0 } // validateTx checks whether a transaction is valid according to the consensus rules. diff --git a/mobile/ethclient.go b/mobile/ethclient.go index 668d65e322..002de5fcdf 100644 --- a/mobile/ethclient.go +++ b/mobile/ethclient.go @@ -73,7 +73,8 @@ func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (*Header // GetTransactionByHash returns the transaction with the given hash. func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (*Transaction, error) { - tx, err := ec.client.TransactionByHash(ctx.context, hash.hash) + // TODO(karalabe): use isPending result + tx, _, err := ec.client.TransactionByHash(ctx.context, hash.hash) return &Transaction{tx}, err } diff --git a/mobile/types.go b/mobile/types.go index bb5ccc6251..6382f43671 100644 --- a/mobile/types.go +++ b/mobile/types.go @@ -154,9 +154,9 @@ func (tx *Transaction) GetTo() *Address { return nil } -func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) { - t, err := tx.tx.WithSignature(types.HomesteadSigner{}, sig) - return &Transaction{t}, err +func (tx *Transaction) WithSignature(sig []byte) *Transaction { + t := tx.tx.WithSignature(types.HomesteadSigner{}, sig) + return &Transaction{t} } // Transactions represents a slice of transactions.