Commit 1e50f5dd authored by Péter Szilágyi's avatar Péter Szilágyi Committed by GitHub

Merge pull request #2159 from zsfelfoldi/light-backend

eth: separate common and full node-specific API and backend service
parents f127799d 3a97280a
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"golang.org/x/net/context"
) )
// ErrNoCode is returned by call and transact operations for which the requested // ErrNoCode is returned by call and transact operations for which the requested
...@@ -35,12 +36,12 @@ type ContractCaller interface { ...@@ -35,12 +36,12 @@ type ContractCaller interface {
// HasCode checks if the contract at the given address has any code associated // HasCode checks if the contract at the given address has any code associated
// with it or not. This is needed to differentiate between contract internal // with it or not. This is needed to differentiate between contract internal
// errors and the local chain being out of sync. // errors and the local chain being out of sync.
HasCode(contract common.Address, pending bool) (bool, error) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error)
// ContractCall executes an Ethereum contract call with the specified data as // ContractCall executes an Ethereum contract call with the specified data as
// the input. The pending flag requests execution against the pending block, not // the input. The pending flag requests execution against the pending block, not
// the stable head of the chain. // the stable head of the chain.
ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error)
} }
// ContractTransactor defines the methods needed to allow operating with contract // ContractTransactor defines the methods needed to allow operating with contract
...@@ -50,26 +51,26 @@ type ContractCaller interface { ...@@ -50,26 +51,26 @@ type ContractCaller interface {
type ContractTransactor interface { type ContractTransactor interface {
// PendingAccountNonce retrieves the current pending nonce associated with an // PendingAccountNonce retrieves the current pending nonce associated with an
// account. // account.
PendingAccountNonce(account common.Address) (uint64, error) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error)
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
// execution of a transaction. // execution of a transaction.
SuggestGasPrice() (*big.Int, error) SuggestGasPrice(ctx context.Context) (*big.Int, error)
// HasCode checks if the contract at the given address has any code associated // HasCode checks if the contract at the given address has any code associated
// with it or not. This is needed to differentiate between contract internal // with it or not. This is needed to differentiate between contract internal
// errors and the local chain being out of sync. // errors and the local chain being out of sync.
HasCode(contract common.Address, pending bool) (bool, error) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error)
// EstimateGasLimit tries to estimate the gas needed to execute a specific // EstimateGasLimit tries to estimate the gas needed to execute a specific
// transaction based on the current pending state of the backend blockchain. // 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 // 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 // transactions may be added or removed by miners, but it should provide a basis
// for setting a reasonable default. // for setting a reasonable default.
EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error)
// SendTransaction injects the transaction into the pending pool for execution. // SendTransaction injects the transaction into the pending pool for execution.
SendTransaction(tx *types.Transaction) error SendTransaction(ctx context.Context, tx *types.Transaction) error
} }
// ContractBackend defines the methods needed to allow operating with contract // ContractBackend defines the methods needed to allow operating with contract
...@@ -84,28 +85,28 @@ type ContractBackend interface { ...@@ -84,28 +85,28 @@ type ContractBackend interface {
// HasCode checks if the contract at the given address has any code associated // HasCode checks if the contract at the given address has any code associated
// with it or not. This is needed to differentiate between contract internal // with it or not. This is needed to differentiate between contract internal
// errors and the local chain being out of sync. // errors and the local chain being out of sync.
HasCode(contract common.Address, pending bool) (bool, error) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error)
// ContractCall executes an Ethereum contract call with the specified data as // ContractCall executes an Ethereum contract call with the specified data as
// the input. The pending flag requests execution against the pending block, not // the input. The pending flag requests execution against the pending block, not
// the stable head of the chain. // the stable head of the chain.
ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error)
// PendingAccountNonce retrieves the current pending nonce associated with an // PendingAccountNonce retrieves the current pending nonce associated with an
// account. // account.
PendingAccountNonce(account common.Address) (uint64, error) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error)
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely // SuggestGasPrice retrieves the currently suggested gas price to allow a timely
// execution of a transaction. // execution of a transaction.
SuggestGasPrice() (*big.Int, error) SuggestGasPrice(ctx context.Context) (*big.Int, error)
// EstimateGasLimit tries to estimate the gas needed to execute a specific // EstimateGasLimit tries to estimate the gas needed to execute a specific
// transaction based on the current pending state of the backend blockchain. // 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 // 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 // transactions may be added or removed by miners, but it should provide a basis
// for setting a reasonable default. // for setting a reasonable default.
EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error)
// SendTransaction injects the transaction into the pending pool for execution. // SendTransaction injects the transaction into the pending pool for execution.
SendTransaction(tx *types.Transaction) error SendTransaction(ctx context.Context, tx *types.Transaction) error
} }
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"golang.org/x/net/context"
) )
// This nil assignment ensures compile time that nilBackend implements bind.ContractBackend. // This nil assignment ensures compile time that nilBackend implements bind.ContractBackend.
...@@ -32,16 +33,22 @@ var _ bind.ContractBackend = (*nilBackend)(nil) ...@@ -32,16 +33,22 @@ var _ bind.ContractBackend = (*nilBackend)(nil)
// wrappers without calling any methods on them. // wrappers without calling any methods on them.
type nilBackend struct{} type nilBackend struct{}
func (*nilBackend) ContractCall(common.Address, []byte, bool) ([]byte, error) { func (*nilBackend) ContractCall(context.Context, common.Address, []byte, bool) ([]byte, error) {
panic("not implemented") panic("not implemented")
} }
func (*nilBackend) EstimateGasLimit(common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) { func (*nilBackend) EstimateGasLimit(context.Context, common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) {
panic("not implemented")
}
func (*nilBackend) HasCode(context.Context, common.Address, bool) (bool, error) {
panic("not implemented")
}
func (*nilBackend) SuggestGasPrice(context.Context) (*big.Int, error) { panic("not implemented") }
func (*nilBackend) PendingAccountNonce(context.Context, common.Address) (uint64, error) {
panic("not implemented")
}
func (*nilBackend) SendTransaction(context.Context, *types.Transaction) error {
panic("not implemented") panic("not implemented")
} }
func (*nilBackend) HasCode(common.Address, bool) (bool, error) { panic("not implemented") }
func (*nilBackend) SuggestGasPrice() (*big.Int, error) { panic("not implemented") }
func (*nilBackend) PendingAccountNonce(common.Address) (uint64, error) { panic("not implemented") }
func (*nilBackend) SendTransaction(*types.Transaction) error { panic("not implemented") }
// NewNilBackend creates a new binding backend that can be used for instantiation // NewNilBackend creates a new binding backend that can be used for instantiation
// but will panic on any invocation. Its sole purpose is to help testing. // but will panic on any invocation. Its sole purpose is to help testing.
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context"
) )
// This nil assignment ensures compile time that rpcBackend implements bind.ContractBackend. // This nil assignment ensures compile time that rpcBackend implements bind.ContractBackend.
...@@ -80,18 +81,23 @@ type failure struct { ...@@ -80,18 +81,23 @@ type failure struct {
// //
// This is currently painfully non-concurrent, but it will have to do until we // This is currently painfully non-concurrent, but it will have to do until we
// find the time for niceties like this :P // find the time for niceties like this :P
func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessage, error) { func (b *rpcBackend) request(ctx context.Context, method string, params []interface{}) (json.RawMessage, error) {
b.lock.Lock() b.lock.Lock()
defer b.lock.Unlock() defer b.lock.Unlock()
if ctx == nil {
ctx = context.Background()
}
// Ugly hack to serialize an empty list properly // Ugly hack to serialize an empty list properly
if params == nil { if params == nil {
params = []interface{}{} params = []interface{}{}
} }
// Assemble the request object // Assemble the request object
reqID := int(atomic.AddUint32(&b.autoid, 1))
req := &request{ req := &request{
JSONRPC: "2.0", JSONRPC: "2.0",
ID: int(atomic.AddUint32(&b.autoid, 1)), ID: reqID,
Method: method, Method: method,
Params: params, Params: params,
} }
...@@ -99,9 +105,18 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa ...@@ -99,9 +105,18 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa
return nil, err return nil, err
} }
res := new(response) res := new(response)
if err := b.client.Recv(res); err != nil { errc := make(chan error, 1)
go func() {
errc <- b.client.Recv(res)
}()
select {
case err := <-errc:
if err != nil {
return nil, err return nil, err
} }
case <-ctx.Done():
return nil, ctx.Err()
}
if res.Error != nil { if res.Error != nil {
if res.Error.Message == bind.ErrNoCode.Error() { if res.Error.Message == bind.ErrNoCode.Error() {
return nil, bind.ErrNoCode return nil, bind.ErrNoCode
...@@ -113,13 +128,13 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa ...@@ -113,13 +128,13 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa
// HasCode implements ContractVerifier.HasCode by retrieving any code associated // HasCode implements ContractVerifier.HasCode by retrieving any code associated
// with the contract from the remote node, and checking its size. // with the contract from the remote node, and checking its size.
func (b *rpcBackend) HasCode(contract common.Address, pending bool) (bool, error) { func (b *rpcBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
// Execute the RPC code retrieval // Execute the RPC code retrieval
block := "latest" block := "latest"
if pending { if pending {
block = "pending" block = "pending"
} }
res, err := b.request("eth_getCode", []interface{}{contract.Hex(), block}) res, err := b.request(ctx, "eth_getCode", []interface{}{contract.Hex(), block})
if err != nil { if err != nil {
return false, err return false, err
} }
...@@ -133,7 +148,7 @@ func (b *rpcBackend) HasCode(contract common.Address, pending bool) (bool, error ...@@ -133,7 +148,7 @@ func (b *rpcBackend) HasCode(contract common.Address, pending bool) (bool, error
// ContractCall implements ContractCaller.ContractCall, delegating the execution of // ContractCall implements ContractCaller.ContractCall, delegating the execution of
// a contract call to the remote node, returning the reply to for local processing. // a contract call to the remote node, returning the reply to for local processing.
func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) { func (b *rpcBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
// Pack up the request into an RPC argument // Pack up the request into an RPC argument
args := struct { args := struct {
To common.Address `json:"to"` To common.Address `json:"to"`
...@@ -147,7 +162,7 @@ func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending ...@@ -147,7 +162,7 @@ func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending
if pending { if pending {
block = "pending" block = "pending"
} }
res, err := b.request("eth_call", []interface{}{args, block}) res, err := b.request(ctx, "eth_call", []interface{}{args, block})
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -161,8 +176,8 @@ func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending ...@@ -161,8 +176,8 @@ func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending
// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating // PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating
// the current account nonce retrieval to the remote node. // the current account nonce retrieval to the remote node.
func (b *rpcBackend) PendingAccountNonce(account common.Address) (uint64, error) { func (b *rpcBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
res, err := b.request("eth_getTransactionCount", []interface{}{account.Hex(), "pending"}) res, err := b.request(ctx, "eth_getTransactionCount", []interface{}{account.Hex(), "pending"})
if err != nil { if err != nil {
return 0, err return 0, err
} }
...@@ -179,8 +194,8 @@ func (b *rpcBackend) PendingAccountNonce(account common.Address) (uint64, error) ...@@ -179,8 +194,8 @@ func (b *rpcBackend) PendingAccountNonce(account common.Address) (uint64, error)
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the // SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the
// gas price oracle request to the remote node. // gas price oracle request to the remote node.
func (b *rpcBackend) SuggestGasPrice() (*big.Int, error) { func (b *rpcBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
res, err := b.request("eth_gasPrice", nil) res, err := b.request(ctx, "eth_gasPrice", nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -197,7 +212,7 @@ func (b *rpcBackend) SuggestGasPrice() (*big.Int, error) { ...@@ -197,7 +212,7 @@ func (b *rpcBackend) SuggestGasPrice() (*big.Int, error) {
// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating // EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating
// the gas estimation to the remote node. // the gas estimation to the remote node.
func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { func (b *rpcBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
// Pack up the request into an RPC argument // Pack up the request into an RPC argument
args := struct { args := struct {
From common.Address `json:"from"` From common.Address `json:"from"`
...@@ -211,7 +226,7 @@ func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Ad ...@@ -211,7 +226,7 @@ func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Ad
Value: rpc.NewHexNumber(value), Value: rpc.NewHexNumber(value),
} }
// Execute the RPC call and retrieve the response // Execute the RPC call and retrieve the response
res, err := b.request("eth_estimateGas", []interface{}{args}) res, err := b.request(ctx, "eth_estimateGas", []interface{}{args})
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -228,12 +243,12 @@ func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Ad ...@@ -228,12 +243,12 @@ func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Ad
// SendTransaction implements ContractTransactor.SendTransaction, delegating the // SendTransaction implements ContractTransactor.SendTransaction, delegating the
// raw transaction injection to the remote node. // raw transaction injection to the remote node.
func (b *rpcBackend) SendTransaction(tx *types.Transaction) error { func (b *rpcBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
data, err := rlp.EncodeToBytes(tx) data, err := rlp.EncodeToBytes(tx)
if err != nil { if err != nil {
return err return err
} }
res, err := b.request("eth_sendRawTransaction", []interface{}{common.ToHex(data)}) res, err := b.request(ctx, "eth_sendRawTransaction", []interface{}{common.ToHex(data)})
if err != nil { if err != nil {
return err return err
} }
......
...@@ -27,6 +27,7 @@ import ( ...@@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"golang.org/x/net/context"
) )
// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier) // Default chain configuration which sets homestead phase at block 0 (i.e. no frontier)
...@@ -80,7 +81,7 @@ func (b *SimulatedBackend) Rollback() { ...@@ -80,7 +81,7 @@ func (b *SimulatedBackend) Rollback() {
// HasCode implements ContractVerifier.HasCode, checking whether there is any // HasCode implements ContractVerifier.HasCode, checking whether there is any
// code associated with a certain account in the blockchain. // code associated with a certain account in the blockchain.
func (b *SimulatedBackend) HasCode(contract common.Address, pending bool) (bool, error) { func (b *SimulatedBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
if pending { if pending {
return len(b.pendingState.GetCode(contract)) > 0, nil return len(b.pendingState.GetCode(contract)) > 0, nil
} }
...@@ -90,7 +91,7 @@ func (b *SimulatedBackend) HasCode(contract common.Address, pending bool) (bool, ...@@ -90,7 +91,7 @@ func (b *SimulatedBackend) HasCode(contract common.Address, pending bool) (bool,
// ContractCall implements ContractCaller.ContractCall, executing the specified // ContractCall implements ContractCaller.ContractCall, executing the specified
// contract with the given input data. // contract with the given input data.
func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) { func (b *SimulatedBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
// Create a copy of the current state db to screw around with // Create a copy of the current state db to screw around with
var ( var (
block *types.Block block *types.Block
...@@ -129,20 +130,20 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe ...@@ -129,20 +130,20 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
// PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving // PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving
// the nonce currently pending for the account. // the nonce currently pending for the account.
func (b *SimulatedBackend) PendingAccountNonce(account common.Address) (uint64, error) { func (b *SimulatedBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
return b.pendingState.GetOrNewStateObject(account).Nonce(), nil return b.pendingState.GetOrNewStateObject(account).Nonce(), nil
} }
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
// chain doens't have miners, we just return a gas price of 1 for any call. // chain doens't have miners, we just return a gas price of 1 for any call.
func (b *SimulatedBackend) SuggestGasPrice() (*big.Int, error) { func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
return big.NewInt(1), nil return big.NewInt(1), nil
} }
// EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the // EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the
// requested code against the currently pending block/state and returning the used // requested code against the currently pending block/state and returning the used
// gas. // gas.
func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { func (b *SimulatedBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
// Create a copy of the currently pending state db to screw around with // Create a copy of the currently pending state db to screw around with
var ( var (
block = b.pendingBlock block = b.pendingBlock
...@@ -177,7 +178,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com ...@@ -177,7 +178,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw // SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
// transaction injection to the remote node. // transaction injection to the remote node.
func (b *SimulatedBackend) SendTransaction(tx *types.Transaction) error { func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
blocks, _ := core.GenerateChain(b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) { blocks, _ := core.GenerateChain(b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() { for _, tx := range b.pendingBlock.Transactions() {
block.AddTx(tx) block.AddTx(tx)
......
...@@ -26,6 +26,7 @@ import ( ...@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"golang.org/x/net/context"
) )
// SignerFn is a signer function callback when a contract requires a method to // SignerFn is a signer function callback when a contract requires a method to
...@@ -35,6 +36,8 @@ type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, erro ...@@ -35,6 +36,8 @@ type SignerFn func(common.Address, *types.Transaction) (*types.Transaction, erro
// CallOpts is the collection of options to fine tune a contract call request. // CallOpts is the collection of options to fine tune a contract call request.
type CallOpts struct { type CallOpts struct {
Pending bool // Whether to operate on the pending state or the last known one Pending bool // Whether to operate on the pending state or the last known one
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
} }
// TransactOpts is the collection of authorization data required to create a // TransactOpts is the collection of authorization data required to create a
...@@ -47,6 +50,8 @@ type TransactOpts struct { ...@@ -47,6 +50,8 @@ type TransactOpts struct {
Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds) Value *big.Int // Funds to transfer along along the transaction (nil = 0 = no funds)
GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle) GasPrice *big.Int // Gas price to use for the transaction execution (nil = gas price oracle)
GasLimit *big.Int // Gas limit to set for the transaction execution (nil = estimate + 10%) GasLimit *big.Int // Gas limit to set for the transaction execution (nil = estimate + 10%)
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
} }
// BoundContract is the base wrapper object that reflects a contract on the // BoundContract is the base wrapper object that reflects a contract on the
...@@ -102,7 +107,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, ...@@ -102,7 +107,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
} }
// Make sure we have a contract to operate on, and bail out otherwise // Make sure we have a contract to operate on, and bail out otherwise
if (opts.Pending && atomic.LoadUint32(&c.pendingHasCode) == 0) || (!opts.Pending && atomic.LoadUint32(&c.latestHasCode) == 0) { if (opts.Pending && atomic.LoadUint32(&c.pendingHasCode) == 0) || (!opts.Pending && atomic.LoadUint32(&c.latestHasCode) == 0) {
if code, err := c.caller.HasCode(c.address, opts.Pending); err != nil { if code, err := c.caller.HasCode(opts.Context, c.address, opts.Pending); err != nil {
return err return err
} else if !code { } else if !code {
return ErrNoCode return ErrNoCode
...@@ -118,7 +123,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, ...@@ -118,7 +123,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
if err != nil { if err != nil {
return err return err
} }
output, err := c.caller.ContractCall(c.address, input, opts.Pending) output, err := c.caller.ContractCall(opts.Context, c.address, input, opts.Pending)
if err != nil { if err != nil {
return err return err
} }
...@@ -153,7 +158,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i ...@@ -153,7 +158,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
} }
nonce := uint64(0) nonce := uint64(0)
if opts.Nonce == nil { if opts.Nonce == nil {
nonce, err = c.transactor.PendingAccountNonce(opts.From) nonce, err = c.transactor.PendingAccountNonce(opts.Context, opts.From)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to retrieve account nonce: %v", err) return nil, fmt.Errorf("failed to retrieve account nonce: %v", err)
} }
...@@ -163,7 +168,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i ...@@ -163,7 +168,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
// Figure out the gas allowance and gas price values // Figure out the gas allowance and gas price values
gasPrice := opts.GasPrice gasPrice := opts.GasPrice
if gasPrice == nil { if gasPrice == nil {
gasPrice, err = c.transactor.SuggestGasPrice() gasPrice, err = c.transactor.SuggestGasPrice(opts.Context)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to suggest gas price: %v", err) return nil, fmt.Errorf("failed to suggest gas price: %v", err)
} }
...@@ -172,7 +177,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i ...@@ -172,7 +177,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
if gasLimit == nil { if gasLimit == nil {
// Gas estimation cannot succeed without code for method invocations // Gas estimation cannot succeed without code for method invocations
if contract != nil && atomic.LoadUint32(&c.pendingHasCode) == 0 { if contract != nil && atomic.LoadUint32(&c.pendingHasCode) == 0 {
if code, err := c.transactor.HasCode(c.address, true); err != nil { if code, err := c.transactor.HasCode(opts.Context, c.address, true); err != nil {
return nil, err return nil, err
} else if !code { } else if !code {
return nil, ErrNoCode return nil, ErrNoCode
...@@ -180,7 +185,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i ...@@ -180,7 +185,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
atomic.StoreUint32(&c.pendingHasCode, 1) atomic.StoreUint32(&c.pendingHasCode, 1)
} }
// If the contract surely has code (or code is not needed), estimate the transaction // If the contract surely has code (or code is not needed), estimate the transaction
gasLimit, err = c.transactor.EstimateGasLimit(opts.From, contract, value, input) gasLimit, err = c.transactor.EstimateGasLimit(opts.Context, opts.From, contract, value, input)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to exstimate gas needed: %v", err) return nil, fmt.Errorf("failed to exstimate gas needed: %v", err)
} }
...@@ -199,7 +204,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i ...@@ -199,7 +204,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := c.transactor.SendTransaction(signedTx); err != nil { if err := c.transactor.SendTransaction(opts.Context, signedTx); err != nil {
return nil, err return nil, err
} }
return signedTx, nil return signedTx, nil
......
...@@ -34,6 +34,8 @@ import ( ...@@ -34,6 +34,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
) )
var ( var (
...@@ -340,3 +342,23 @@ func zeroKey(k *ecdsa.PrivateKey) { ...@@ -340,3 +342,23 @@ func zeroKey(k *ecdsa.PrivateKey) {
b[i] = 0 b[i] = 0
} }
} }
// APIs implements node.Service
func (am *Manager) APIs() []rpc.API {
return nil
}
// Protocols implements node.Service
func (am *Manager) Protocols() []p2p.Protocol {
return nil
}
// Start implements node.Service
func (am *Manager) Start(srvr *p2p.Server) error {
return nil
}
// Stop implements node.Service
func (am *Manager) Stop() error {
return nil
}
...@@ -29,6 +29,7 @@ import ( ...@@ -29,6 +29,7 @@ import (
"time" "time"
"github.com/ethereum/ethash" "github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/console"
...@@ -313,11 +314,10 @@ func startNode(ctx *cli.Context, stack *node.Node) { ...@@ -313,11 +314,10 @@ func startNode(ctx *cli.Context, stack *node.Node) {
utils.StartNode(stack) utils.StartNode(stack)
// Unlock any account specifically requested // Unlock any account specifically requested
var ethereum *eth.Ethereum var accman *accounts.Manager
if err := stack.Service(&ethereum); err != nil { if err := stack.Service(&accman); err != nil {
utils.Fatalf("ethereum service not running: %v", err) utils.Fatalf("ethereum service not running: %v", err)
} }
accman := ethereum.AccountManager()
passwords := utils.MakePasswordList(ctx) passwords := utils.MakePasswordList(ctx)
accounts := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",") accounts := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
...@@ -328,6 +328,10 @@ func startNode(ctx *cli.Context, stack *node.Node) { ...@@ -328,6 +328,10 @@ func startNode(ctx *cli.Context, stack *node.Node) {
} }
// Start auxiliary services if enabled // Start auxiliary services if enabled
if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { if ctx.GlobalBool(utils.MiningEnabledFlag.Name) {
var ethereum *eth.FullNodeService
if err := stack.Service(&ethereum); err != nil {
utils.Fatalf("ethereum service not running: %v", err)
}
if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil { if err := ethereum.StartMining(ctx.GlobalInt(utils.MinerThreadsFlag.Name), ctx.GlobalString(utils.MiningGPUFlag.Name)); err != nil {
utils.Fatalf("Failed to start mining: %v", err) utils.Fatalf("Failed to start mining: %v", err)
} }
......
...@@ -146,7 +146,7 @@ func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node ...@@ -146,7 +146,7 @@ func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node
// RunTest executes the specified test against an already pre-configured protocol // RunTest executes the specified test against an already pre-configured protocol
// stack to ensure basic checks pass before running RPC tests. // stack to ensure basic checks pass before running RPC tests.
func RunTest(stack *node.Node, test *tests.BlockTest) error { func RunTest(stack *node.Node, test *tests.BlockTest) error {
var ethereum *eth.Ethereum var ethereum *eth.FullNodeService
stack.Service(&ethereum) stack.Service(&ethereum)
blockchain := ethereum.BlockChain() blockchain := ethereum.BlockChain()
......
...@@ -763,6 +763,13 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte, ...@@ -763,6 +763,13 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
if err != nil { if err != nil {
Fatalf("Failed to create the protocol stack: %v", err) Fatalf("Failed to create the protocol stack: %v", err)
} }
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return accman, nil
}); err != nil {
Fatalf("Failed to register the account manager service: %v", err)
}
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return eth.New(ctx, ethConf) return eth.New(ctx, ethConf)
}); err != nil { }); err != nil {
......
...@@ -99,7 +99,7 @@ const ( ...@@ -99,7 +99,7 @@ const (
type testFrontend struct { type testFrontend struct {
t *testing.T t *testing.T
ethereum *eth.Ethereum ethereum *eth.FullNodeService
xeth *xe.XEth xeth *xe.XEth
wait chan *big.Int wait chan *big.Int
lastConfirm string lastConfirm string
...@@ -123,7 +123,7 @@ func (self *testFrontend) ConfirmTransaction(tx string) bool { ...@@ -123,7 +123,7 @@ func (self *testFrontend) ConfirmTransaction(tx string) bool {
return true return true
} }
func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { func testEth(t *testing.T) (ethereum *eth.FullNodeService, err error) {
tmp, err := ioutil.TempDir("", "natspec-test") tmp, err := ioutil.TempDir("", "natspec-test")
if err != nil { if err != nil {
......
...@@ -76,7 +76,7 @@ func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {} ...@@ -76,7 +76,7 @@ func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {}
type tester struct { type tester struct {
workspace string workspace string
stack *node.Node stack *node.Node
ethereum *eth.Ethereum ethereum *eth.FullNodeService
console *Console console *Console
input *hookedPrompter input *hookedPrompter
output *bytes.Buffer output *bytes.Buffer
...@@ -134,7 +134,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { ...@@ -134,7 +134,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
t.Fatalf("failed to create JavaScript console: %v", err) t.Fatalf("failed to create JavaScript console: %v", err)
} }
// Create the final tester and return // Create the final tester and return
var ethereum *eth.Ethereum var ethereum *eth.FullNodeService
stack.Service(&ethereum) stack.Service(&ethereum)
return &tester{ return &tester{
......
...@@ -73,6 +73,8 @@ type Environment interface { ...@@ -73,6 +73,8 @@ type Environment interface {
DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error)
// Create a new contract // Create a new contract
Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
StructLogs() []StructLog
} }
// Vm is the basic interface for an implementation of the EVM. // Vm is the basic interface for an implementation of the EVM.
......
This diff is collapsed.
// Copyright 2015 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package eth
import (
"math/big"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
rpc "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context"
)
// EthApiBackend implements ethapi.Backend for full nodes
type EthApiBackend struct {
eth *FullNodeService
gpo *gasprice.GasPriceOracle
}
func (b *EthApiBackend) SetHead(number uint64) {
b.eth.blockchain.SetHead(number)
}
func (b *EthApiBackend) HeaderByNumber(blockNr rpc.BlockNumber) *types.Header {
// Pending block is only known by the miner
if blockNr == rpc.PendingBlockNumber {
block, _ := b.eth.miner.Pending()
return block.Header()
}
// Otherwise resolve and return the block
if blockNr == rpc.LatestBlockNumber {
return b.eth.blockchain.CurrentBlock().Header()
}
return b.eth.blockchain.GetHeaderByNumber(uint64(blockNr))
}
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
}
// Otherwise resolve and return the block
if blockNr == rpc.LatestBlockNumber {
return b.eth.blockchain.CurrentBlock(), nil
}
return b.eth.blockchain.GetBlockByNumber(uint64(blockNr)), nil
}
func (b *EthApiBackend) StateAndHeaderByNumber(blockNr rpc.BlockNumber) (ethapi.State, *types.Header, error) {
// Pending state is only known by the miner
if blockNr == rpc.PendingBlockNumber {
block, state := b.eth.miner.Pending()
return EthApiState{state}, block.Header(), nil
}
// Otherwise resolve the block number and return its state
header := b.HeaderByNumber(blockNr)
if header == nil {
return nil, nil, nil
}
stateDb, err := state.New(header.Root, b.eth.chainDb)
return EthApiState{stateDb}, header, err
}
func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) {
return b.eth.blockchain.GetBlockByHash(blockHash), 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
}
func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
return b.eth.blockchain.GetTdByHash(blockHash)
}
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.Copy()
addr, _ := msg.From()
from := stateDb.GetOrNewStateObject(addr)
from.SetBalance(common.MaxBig)
vmError := func() error { return nil }
return core.NewEnv(stateDb, b.eth.chainConfig, b.eth.blockchain, msg, header, b.eth.chainConfig.VmConfig), vmError, nil
}
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)
}
func (b *EthApiBackend) RemoveTx(txHash common.Hash) {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
b.eth.txPool.RemoveTx(txHash)
}
func (b *EthApiBackend) GetPoolTransactions() types.Transactions {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
return b.eth.txPool.GetTransactions()
}
func (b *EthApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
return b.eth.txPool.GetTransaction(txHash)
}
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
}
func (b *EthApiBackend) Stats() (pending int, queued int) {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
return b.eth.txPool.Stats()
}
func (b *EthApiBackend) TxPoolContent() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) {
b.eth.txMu.Lock()
defer b.eth.txMu.Unlock()
return b.eth.TxPool().Content()
}
func (b *EthApiBackend) Downloader() *downloader.Downloader {
return b.eth.Downloader()
}
func (b *EthApiBackend) ProtocolVersion() int {
return b.eth.EthVersion()
}
func (b *EthApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) {
return b.gpo.SuggestPrice(), nil
}
func (b *EthApiBackend) ChainDb() ethdb.Database {
return b.eth.ChainDb()
}
func (b *EthApiBackend) EventMux() *event.TypeMux {
return b.eth.EventMux()
}
func (b *EthApiBackend) AccountManager() *accounts.Manager {
return b.eth.AccountManager()
}
type EthApiState struct {
state *state.StateDB
}
func (s EthApiState) GetBalance(ctx context.Context, addr common.Address) (*big.Int, error) {
return s.state.GetBalance(addr), nil
}
func (s EthApiState) GetCode(ctx context.Context, addr common.Address) ([]byte, error) {
return s.state.GetCode(addr), nil
}
func (s EthApiState) GetState(ctx context.Context, a common.Address, b common.Hash) (common.Hash, error) {
return s.state.GetState(a, b), nil
}
func (s EthApiState) GetNonce(ctx context.Context, addr common.Address) (uint64, error) {
return s.state.GetNonce(addr), nil
}
This diff is collapsed.
...@@ -21,8 +21,10 @@ import ( ...@@ -21,8 +21,10 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "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/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context"
) )
// ContractBackend implements bind.ContractBackend with direct calls to Ethereum // ContractBackend implements bind.ContractBackend with direct calls to Ethereum
...@@ -33,38 +35,44 @@ import ( ...@@ -33,38 +35,44 @@ import (
// object. These should be rewritten to internal Go method calls when the Go API // object. These should be rewritten to internal Go method calls when the Go API
// is refactored to support a clean library use. // is refactored to support a clean library use.
type ContractBackend struct { type ContractBackend struct {
eapi *PublicEthereumAPI // Wrapper around the Ethereum object to access metadata eapi *ethapi.PublicEthereumAPI // Wrapper around the Ethereum object to access metadata
bcapi *PublicBlockChainAPI // Wrapper around the blockchain to access chain data bcapi *ethapi.PublicBlockChainAPI // Wrapper around the blockchain to access chain data
txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data txapi *ethapi.PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data
} }
// NewContractBackend creates a new native contract backend using an existing // NewContractBackend creates a new native contract backend using an existing
// Etheruem object. // Etheruem object.
func NewContractBackend(eth *Ethereum) *ContractBackend { func NewContractBackend(eth *FullNodeService) *ContractBackend {
return &ContractBackend{ return &ContractBackend{
eapi: NewPublicEthereumAPI(eth), eapi: ethapi.NewPublicEthereumAPI(eth.apiBackend, nil, nil),
bcapi: NewPublicBlockChainAPI(eth.chainConfig, eth.blockchain, eth.miner, eth.chainDb, eth.gpo, eth.eventMux, eth.accountManager), bcapi: ethapi.NewPublicBlockChainAPI(eth.apiBackend),
txapi: NewPublicTransactionPoolAPI(eth), txapi: ethapi.NewPublicTransactionPoolAPI(eth.apiBackend),
} }
} }
// HasCode implements bind.ContractVerifier.HasCode by retrieving any code associated // HasCode implements bind.ContractVerifier.HasCode by retrieving any code associated
// with the contract from the local API, and checking its size. // with the contract from the local API, and checking its size.
func (b *ContractBackend) HasCode(contract common.Address, pending bool) (bool, error) { func (b *ContractBackend) HasCode(ctx context.Context, contract common.Address, pending bool) (bool, error) {
if ctx == nil {
ctx = context.Background()
}
block := rpc.LatestBlockNumber block := rpc.LatestBlockNumber
if pending { if pending {
block = rpc.PendingBlockNumber block = rpc.PendingBlockNumber
} }
out, err := b.bcapi.GetCode(contract, block) out, err := b.bcapi.GetCode(ctx, contract, block)
return len(common.FromHex(out)) > 0, err return len(common.FromHex(out)) > 0, err
} }
// ContractCall implements bind.ContractCaller executing an Ethereum contract // ContractCall implements bind.ContractCaller executing an Ethereum contract
// call with the specified data as the input. The pending flag requests execution // call with the specified data as the input. The pending flag requests execution
// against the pending block, not the stable head of the chain. // against the pending block, not the stable head of the chain.
func (b *ContractBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) { func (b *ContractBackend) ContractCall(ctx context.Context, contract common.Address, data []byte, pending bool) ([]byte, error) {
if ctx == nil {
ctx = context.Background()
}
// Convert the input args to the API spec // Convert the input args to the API spec
args := CallArgs{ args := ethapi.CallArgs{
To: &contract, To: &contract,
Data: common.ToHex(data), Data: common.ToHex(data),
} }
...@@ -73,21 +81,27 @@ func (b *ContractBackend) ContractCall(contract common.Address, data []byte, pen ...@@ -73,21 +81,27 @@ func (b *ContractBackend) ContractCall(contract common.Address, data []byte, pen
block = rpc.PendingBlockNumber block = rpc.PendingBlockNumber
} }
// Execute the call and convert the output back to Go types // Execute the call and convert the output back to Go types
out, err := b.bcapi.Call(args, block) out, err := b.bcapi.Call(ctx, args, block)
return common.FromHex(out), err return common.FromHex(out), err
} }
// PendingAccountNonce implements bind.ContractTransactor retrieving the current // PendingAccountNonce implements bind.ContractTransactor retrieving the current
// pending nonce associated with an account. // pending nonce associated with an account.
func (b *ContractBackend) PendingAccountNonce(account common.Address) (uint64, error) { func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) {
out, err := b.txapi.GetTransactionCount(account, rpc.PendingBlockNumber) if ctx == nil {
ctx = context.Background()
}
out, err := b.txapi.GetTransactionCount(ctx, account, rpc.PendingBlockNumber)
return out.Uint64(), err return out.Uint64(), err
} }
// SuggestGasPrice implements bind.ContractTransactor retrieving the currently // SuggestGasPrice implements bind.ContractTransactor retrieving the currently
// suggested gas price to allow a timely execution of a transaction. // suggested gas price to allow a timely execution of a transaction.
func (b *ContractBackend) SuggestGasPrice() (*big.Int, error) { func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
return b.eapi.GasPrice(), nil if ctx == nil {
ctx = context.Background()
}
return b.eapi.GasPrice(ctx)
} }
// EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas // EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas
...@@ -95,8 +109,11 @@ func (b *ContractBackend) SuggestGasPrice() (*big.Int, error) { ...@@ -95,8 +109,11 @@ func (b *ContractBackend) SuggestGasPrice() (*big.Int, error) {
// the backend blockchain. There is no guarantee that this is the true gas limit // 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 // requirement as other transactions may be added or removed by miners, but it
// should provide a basis for setting a reasonable default. // should provide a basis for setting a reasonable default.
func (b *ContractBackend) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { func (b *ContractBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) {
out, err := b.bcapi.EstimateGas(CallArgs{ if ctx == nil {
ctx = context.Background()
}
out, err := b.bcapi.EstimateGas(ctx, ethapi.CallArgs{
From: sender, From: sender,
To: contract, To: contract,
Value: *rpc.NewHexNumber(value), Value: *rpc.NewHexNumber(value),
...@@ -107,8 +124,11 @@ func (b *ContractBackend) EstimateGasLimit(sender common.Address, contract *comm ...@@ -107,8 +124,11 @@ func (b *ContractBackend) EstimateGasLimit(sender common.Address, contract *comm
// SendTransaction implements bind.ContractTransactor injects the transaction // SendTransaction implements bind.ContractTransactor injects the transaction
// into the pending pool for execution. // into the pending pool for execution.
func (b *ContractBackend) SendTransaction(tx *types.Transaction) error { func (b *ContractBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
if ctx == nil {
ctx = context.Background()
}
raw, _ := rlp.EncodeToBytes(tx) raw, _ := rlp.EncodeToBytes(tx)
_, err := b.txapi.SendRawTransaction(common.ToHex(raw)) _, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw))
return err return err
} }
...@@ -28,7 +28,7 @@ import ( ...@@ -28,7 +28,7 @@ import (
const disabledInfo = "Set GO_OPENCL and re-build to enable." const disabledInfo = "Set GO_OPENCL and re-build to enable."
func (s *Ethereum) StartMining(threads int, gpus string) error { func (s *FullNodeService) StartMining(threads int, gpus string) error {
eb, err := s.Etherbase() eb, err := s.Etherbase()
if err != nil { if err != nil {
err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Lesser General Public License // You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package eth package gasprice
import ( import (
"math/big" "math/big"
...@@ -23,6 +23,8 @@ import ( ...@@ -23,6 +23,8 @@ import (
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/logger/glog"
) )
...@@ -39,10 +41,22 @@ type blockPriceInfo struct { ...@@ -39,10 +41,22 @@ type blockPriceInfo struct {
baseGasPrice *big.Int baseGasPrice *big.Int
} }
type GpoParams struct {
GpoMinGasPrice *big.Int
GpoMaxGasPrice *big.Int
GpoFullBlockRatio int
GpobaseStepDown int
GpobaseStepUp int
GpobaseCorrectionFactor int
}
// GasPriceOracle recommends gas prices based on the content of recent // GasPriceOracle recommends gas prices based on the content of recent
// blocks. // blocks.
type GasPriceOracle struct { type GasPriceOracle struct {
eth *Ethereum chain *core.BlockChain
db ethdb.Database
evmux *event.TypeMux
params *GpoParams
initOnce sync.Once initOnce sync.Once
minPrice *big.Int minPrice *big.Int
lastBaseMutex sync.Mutex lastBaseMutex sync.Mutex
...@@ -55,17 +69,20 @@ type GasPriceOracle struct { ...@@ -55,17 +69,20 @@ type GasPriceOracle struct {
} }
// NewGasPriceOracle returns a new oracle. // NewGasPriceOracle returns a new oracle.
func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle { func NewGasPriceOracle(chain *core.BlockChain, db ethdb.Database, evmux *event.TypeMux, params *GpoParams) *GasPriceOracle {
minprice := eth.GpoMinGasPrice minprice := params.GpoMinGasPrice
if minprice == nil { if minprice == nil {
minprice = big.NewInt(gpoDefaultMinGasPrice) minprice = big.NewInt(gpoDefaultMinGasPrice)
} }
minbase := new(big.Int).Mul(minprice, big.NewInt(100)) minbase := new(big.Int).Mul(minprice, big.NewInt(100))
if eth.GpobaseCorrectionFactor > 0 { if params.GpobaseCorrectionFactor > 0 {
minbase = minbase.Div(minbase, big.NewInt(int64(eth.GpobaseCorrectionFactor))) minbase = minbase.Div(minbase, big.NewInt(int64(params.GpobaseCorrectionFactor)))
} }
return &GasPriceOracle{ return &GasPriceOracle{
eth: eth, chain: chain,
db: db,
evmux: evmux,
params: params,
blocks: make(map[uint64]*blockPriceInfo), blocks: make(map[uint64]*blockPriceInfo),
minBase: minbase, minBase: minbase,
minPrice: minprice, minPrice: minprice,
...@@ -75,14 +92,14 @@ func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle { ...@@ -75,14 +92,14 @@ func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle {
func (gpo *GasPriceOracle) init() { func (gpo *GasPriceOracle) init() {
gpo.initOnce.Do(func() { gpo.initOnce.Do(func() {
gpo.processPastBlocks(gpo.eth.BlockChain()) gpo.processPastBlocks()
go gpo.listenLoop() go gpo.listenLoop()
}) })
} }
func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { func (self *GasPriceOracle) processPastBlocks() {
last := int64(-1) last := int64(-1)
cblock := chain.CurrentBlock() cblock := self.chain.CurrentBlock()
if cblock != nil { if cblock != nil {
last = int64(cblock.NumberU64()) last = int64(cblock.NumberU64())
} }
...@@ -92,7 +109,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { ...@@ -92,7 +109,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) {
} }
self.firstProcessed = uint64(first) self.firstProcessed = uint64(first)
for i := first; i <= last; i++ { for i := first; i <= last; i++ {
block := chain.GetBlockByNumber(uint64(i)) block := self.chain.GetBlockByNumber(uint64(i))
if block != nil { if block != nil {
self.processBlock(block) self.processBlock(block)
} }
...@@ -101,7 +118,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { ...@@ -101,7 +118,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) {
} }
func (self *GasPriceOracle) listenLoop() { func (self *GasPriceOracle) listenLoop() {
events := self.eth.EventMux().Subscribe(core.ChainEvent{}, core.ChainSplitEvent{}) events := self.evmux.Subscribe(core.ChainEvent{}, core.ChainSplitEvent{})
defer events.Unsubscribe() defer events.Unsubscribe()
for event := range events.Chan() { for event := range events.Chan() {
...@@ -136,9 +153,9 @@ func (self *GasPriceOracle) processBlock(block *types.Block) { ...@@ -136,9 +153,9 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
} }
if lastBase.Cmp(lp) < 0 { if lastBase.Cmp(lp) < 0 {
corr = self.eth.GpobaseStepUp corr = self.params.GpobaseStepUp
} else { } else {
corr = -self.eth.GpobaseStepDown corr = -self.params.GpobaseStepDown
} }
crand := int64(corr * (900 + rand.Intn(201))) crand := int64(corr * (900 + rand.Intn(201)))
...@@ -159,14 +176,14 @@ func (self *GasPriceOracle) processBlock(block *types.Block) { ...@@ -159,14 +176,14 @@ func (self *GasPriceOracle) processBlock(block *types.Block) {
self.lastBase = newBase self.lastBase = newBase
self.lastBaseMutex.Unlock() self.lastBaseMutex.Unlock()
glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", block.NumberU64(), newBase.Int64()) glog.V(logger.Detail).Infof("Processed block #%v, base price is %v\n", i, newBase.Int64())
} }
// returns the lowers possible price with which a tx was or could have been included // returns the lowers possible price with which a tx was or could have been included
func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
gasUsed := big.NewInt(0) gasUsed := big.NewInt(0)
receipts := core.GetBlockReceipts(self.eth.ChainDb(), block.Hash(), block.NumberU64()) receipts := core.GetBlockReceipts(self.db, block.Hash(), block.NumberU64())
if len(receipts) > 0 { if len(receipts) > 0 {
if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil { if cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil {
gasUsed = receipts[len(receipts)-1].CumulativeGasUsed gasUsed = receipts[len(receipts)-1].CumulativeGasUsed
...@@ -174,7 +191,7 @@ func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { ...@@ -174,7 +191,7 @@ func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int {
} }
if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(), if new(big.Int).Mul(gasUsed, big.NewInt(100)).Cmp(new(big.Int).Mul(block.GasLimit(),
big.NewInt(int64(self.eth.GpoFullBlockRatio)))) < 0 { big.NewInt(int64(self.params.GpoFullBlockRatio)))) < 0 {
// block is not full, could have posted a tx with MinGasPrice // block is not full, could have posted a tx with MinGasPrice
return big.NewInt(0) return big.NewInt(0)
} }
...@@ -201,12 +218,12 @@ func (self *GasPriceOracle) SuggestPrice() *big.Int { ...@@ -201,12 +218,12 @@ func (self *GasPriceOracle) SuggestPrice() *big.Int {
price := new(big.Int).Set(self.lastBase) price := new(big.Int).Set(self.lastBase)
self.lastBaseMutex.Unlock() self.lastBaseMutex.Unlock()
price.Mul(price, big.NewInt(int64(self.eth.GpobaseCorrectionFactor))) price.Mul(price, big.NewInt(int64(self.params.GpobaseCorrectionFactor)))
price.Div(price, big.NewInt(100)) price.Div(price, big.NewInt(100))
if price.Cmp(self.minPrice) < 0 { if price.Cmp(self.minPrice) < 0 {
price.Set(self.minPrice) price.Set(self.minPrice)
} else if self.eth.GpoMaxGasPrice != nil && price.Cmp(self.eth.GpoMaxGasPrice) > 0 { } else if self.params.GpoMaxGasPrice != nil && price.Cmp(self.params.GpoMaxGasPrice) > 0 {
price.Set(self.eth.GpoMaxGasPrice) price.Set(self.params.GpoMaxGasPrice)
} }
return price return price
} }
...@@ -33,7 +33,7 @@ import ( ...@@ -33,7 +33,7 @@ import (
"github.com/ethereum/go-ethereum/miner" "github.com/ethereum/go-ethereum/miner"
) )
func (s *Ethereum) StartMining(threads int, gpus string) error { func (s *FullNodeService) StartMining(threads int, gpus string) error {
eb, err := s.Etherbase() eb, err := s.Etherbase()
if err != nil { if err != nil {
err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) err = fmt.Errorf("Cannot start mining without etherbase address: %v", err)
......
This diff is collapsed.
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package ethapi implements the general Ethereum API functions.
package ethapi
import (
"math/big"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context"
)
// Backend interface provides the common API services (that are provided by
// both full and light clients) with access to necessary functions.
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(blockNr rpc.BlockNumber) *types.Header
BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error)
StateAndHeaderByNumber(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]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction)
}
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)
}
func GetAPIs(apiBackend Backend, solcPath *string, solc **compiler.Solidity) []rpc.API {
return []rpc.API{
{
Namespace: "eth",
Version: "1.0",
Service: NewPublicEthereumAPI(apiBackend, solcPath, solc),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicBlockChainAPI(apiBackend),
Public: true,
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicTransactionPoolAPI(apiBackend),
Public: true,
}, {
Namespace: "txpool",
Version: "1.0",
Service: NewPublicTxPoolAPI(apiBackend),
Public: true,
}, {
Namespace: "admin",
Version: "1.0",
Service: NewPrivateAdminAPI(apiBackend, solcPath, solc),
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPublicDebugAPI(apiBackend),
Public: true,
}, {
Namespace: "debug",
Version: "1.0",
Service: NewPrivateDebugAPI(apiBackend),
}, {
Namespace: "eth",
Version: "1.0",
Service: NewPublicAccountAPI(apiBackend.AccountManager()),
Public: true,
}, {
Namespace: "personal",
Version: "1.0",
Service: NewPrivateAccountAPI(apiBackend),
Public: false,
},
}
}
...@@ -30,6 +30,7 @@ import ( ...@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context"
) )
// Interval to check for new releases // Interval to check for new releases
...@@ -57,7 +58,7 @@ type ReleaseService struct { ...@@ -57,7 +58,7 @@ type ReleaseService struct {
// releases and notify the user of such. // releases and notify the user of such.
func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) { func NewReleaseService(ctx *node.ServiceContext, config Config) (node.Service, error) {
// Retrieve the Ethereum service dependency to access the blockchain // Retrieve the Ethereum service dependency to access the blockchain
var ethereum *eth.Ethereum var ethereum *eth.FullNodeService
if err := ctx.Service(&ethereum); err != nil { if err := ctx.Service(&ethereum); err != nil {
return nil, err return nil, err
} }
...@@ -110,7 +111,9 @@ func (r *ReleaseService) checker() { ...@@ -110,7 +111,9 @@ func (r *ReleaseService) checker() {
timer.Reset(releaseRecheckInterval) timer.Reset(releaseRecheckInterval)
// Retrieve the current version, and handle missing contracts gracefully // Retrieve the current version, and handle missing contracts gracefully
version, err := r.oracle.CurrentVersion(nil) ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
opts := &bind.CallOpts{Context: ctx}
version, err := r.oracle.CurrentVersion(opts)
if err != nil { if err != nil {
if err == bind.ErrNoCode { if err == bind.ErrNoCode {
glog.V(logger.Debug).Infof("Release oracle not found at %x", r.config.Oracle) glog.V(logger.Debug).Infof("Release oracle not found at %x", r.config.Oracle)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment