From 3a97280ae889bb6852ba16e70750a37b2ed08473 Mon Sep 17 00:00:00 2001 From: zsfelfoldi Date: Wed, 16 Dec 2015 04:26:23 +0100 Subject: [PATCH] eth: separate common and full node-specific API and backend service --- accounts/abi/bind/backend.go | 27 +- accounts/abi/bind/backends/nil.go | 19 +- accounts/abi/bind/backends/remote.go | 47 +- accounts/abi/bind/backends/simulated.go | 13 +- accounts/abi/bind/base.go | 19 +- accounts/account_manager.go | 22 + cmd/geth/main.go | 10 +- cmd/gethrpctest/main.go | 2 +- cmd/utils/flags.go | 7 + common/natspec/natspec_e2e_test.go | 4 +- console/console_test.go | 4 +- core/vm/environment.go | 2 + eth/api.go | 1607 ++--------------------- eth/api_backend.go | 201 +++ eth/backend.go | 328 +++-- eth/bind.go | 60 +- eth/cpu_mining.go | 2 +- eth/{ => gasprice}/gasprice.go | 57 +- eth/gpu_mining.go | 2 +- internal/ethapi/api.go | 1542 ++++++++++++++++++++++ internal/ethapi/backend.go | 119 ++ release/release.go | 7 +- 22 files changed, 2296 insertions(+), 1805 deletions(-) create mode 100644 eth/api_backend.go rename eth/{ => gasprice}/gasprice.go (75%) create mode 100644 internal/ethapi/api.go create mode 100644 internal/ethapi/backend.go diff --git a/accounts/abi/bind/backend.go b/accounts/abi/bind/backend.go index 65806aef4..bec742c29 100644 --- a/accounts/abi/bind/backend.go +++ b/accounts/abi/bind/backend.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "golang.org/x/net/context" ) // ErrNoCode is returned by call and transact operations for which the requested @@ -35,12 +36,12 @@ type ContractCaller interface { // 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 // 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 // the input. The pending flag requests execution against the pending block, not // 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 @@ -50,26 +51,26 @@ type ContractCaller interface { type ContractTransactor interface { // PendingAccountNonce retrieves the current pending nonce associated with an // 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 // 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 // with it or not. This is needed to differentiate between contract internal // 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 // 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. - 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(tx *types.Transaction) error + SendTransaction(ctx context.Context, tx *types.Transaction) error } // ContractBackend defines the methods needed to allow operating with contract @@ -84,28 +85,28 @@ type ContractBackend interface { // 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 // 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 // the input. The pending flag requests execution against the pending block, not // 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 // 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 // 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 // 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. - 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(tx *types.Transaction) error + SendTransaction(ctx context.Context, tx *types.Transaction) error } diff --git a/accounts/abi/bind/backends/nil.go b/accounts/abi/bind/backends/nil.go index f10bb61ac..54b222f1f 100644 --- a/accounts/abi/bind/backends/nil.go +++ b/accounts/abi/bind/backends/nil.go @@ -22,6 +22,7 @@ import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" + "golang.org/x/net/context" ) // This nil assignment ensures compile time that nilBackend implements bind.ContractBackend. @@ -32,16 +33,22 @@ var _ bind.ContractBackend = (*nilBackend)(nil) // wrappers without calling any methods on them. 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") } -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") } -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 // but will panic on any invocation. Its sole purpose is to help testing. diff --git a/accounts/abi/bind/backends/remote.go b/accounts/abi/bind/backends/remote.go index d903cbc8f..4793143e4 100644 --- a/accounts/abi/bind/backends/remote.go +++ b/accounts/abi/bind/backends/remote.go @@ -28,6 +28,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" ) // This nil assignment ensures compile time that rpcBackend implements bind.ContractBackend. @@ -80,18 +81,23 @@ type failure struct { // // This is currently painfully non-concurrent, but it will have to do until we // 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() defer b.lock.Unlock() + if ctx == nil { + ctx = context.Background() + } + // Ugly hack to serialize an empty list properly if params == nil { params = []interface{}{} } // Assemble the request object + reqID := int(atomic.AddUint32(&b.autoid, 1)) req := &request{ JSONRPC: "2.0", - ID: int(atomic.AddUint32(&b.autoid, 1)), + ID: reqID, Method: method, Params: params, } @@ -99,8 +105,17 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa return nil, err } res := new(response) - if err := b.client.Recv(res); err != nil { - return nil, err + errc := make(chan error, 1) + go func() { + errc <- b.client.Recv(res) + }() + select { + case err := <-errc: + if err != nil { + return nil, err + } + case <-ctx.Done(): + return nil, ctx.Err() } if res.Error != nil { if res.Error.Message == bind.ErrNoCode.Error() { @@ -113,13 +128,13 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa // HasCode implements ContractVerifier.HasCode by retrieving any code associated // 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 block := "latest" if 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 { return false, err } @@ -133,7 +148,7 @@ func (b *rpcBackend) HasCode(contract common.Address, pending bool) (bool, error // ContractCall implements ContractCaller.ContractCall, delegating the execution of // 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 args := struct { To common.Address `json:"to"` @@ -147,7 +162,7 @@ func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending if 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 { return nil, err } @@ -161,8 +176,8 @@ func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending // PendingAccountNonce implements ContractTransactor.PendingAccountNonce, delegating // the current account nonce retrieval to the remote node. -func (b *rpcBackend) PendingAccountNonce(account common.Address) (uint64, error) { - res, err := b.request("eth_getTransactionCount", []interface{}{account.Hex(), "pending"}) +func (b *rpcBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { + res, err := b.request(ctx, "eth_getTransactionCount", []interface{}{account.Hex(), "pending"}) if err != nil { return 0, err } @@ -179,8 +194,8 @@ func (b *rpcBackend) PendingAccountNonce(account common.Address) (uint64, error) // SuggestGasPrice implements ContractTransactor.SuggestGasPrice, delegating the // gas price oracle request to the remote node. -func (b *rpcBackend) SuggestGasPrice() (*big.Int, error) { - res, err := b.request("eth_gasPrice", nil) +func (b *rpcBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + res, err := b.request(ctx, "eth_gasPrice", nil) if err != nil { return nil, err } @@ -197,7 +212,7 @@ func (b *rpcBackend) SuggestGasPrice() (*big.Int, error) { // EstimateGasLimit implements ContractTransactor.EstimateGasLimit, delegating // 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 args := struct { From common.Address `json:"from"` @@ -211,7 +226,7 @@ func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Ad Value: rpc.NewHexNumber(value), } // 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 { return nil, err } @@ -228,12 +243,12 @@ func (b *rpcBackend) EstimateGasLimit(sender common.Address, contract *common.Ad // SendTransaction implements ContractTransactor.SendTransaction, delegating the // 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) if err != nil { 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 { return err } diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go index 54b1ce603..490da82a6 100644 --- a/accounts/abi/bind/backends/simulated.go +++ b/accounts/abi/bind/backends/simulated.go @@ -27,6 +27,7 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/ethdb" "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) @@ -80,7 +81,7 @@ func (b *SimulatedBackend) Rollback() { // HasCode implements ContractVerifier.HasCode, checking whether there is any // 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 { return len(b.pendingState.GetCode(contract)) > 0, nil } @@ -90,7 +91,7 @@ func (b *SimulatedBackend) HasCode(contract common.Address, pending bool) (bool, // ContractCall implements ContractCaller.ContractCall, executing the specified // 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 var ( block *types.Block @@ -129,20 +130,20 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe // PendingAccountNonce implements ContractTransactor.PendingAccountNonce, retrieving // 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 } // SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated // 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 } // EstimateGasLimit implements ContractTransactor.EstimateGasLimit, executing the // requested code against the currently pending block/state and returning the used // 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 var ( block = b.pendingBlock @@ -177,7 +178,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com // SendTransaction implements ContractTransactor.SendTransaction, delegating the raw // 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) { for _, tx := range b.pendingBlock.Transactions() { block.AddTx(tx) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 75e8d5bc8..80948d3f1 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -26,6 +26,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" + "golang.org/x/net/context" ) // 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 // CallOpts is the collection of options to fine tune a contract call request. type CallOpts struct { 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 @@ -47,6 +50,8 @@ type TransactOpts struct { 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) 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 @@ -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 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 } else if !code { return ErrNoCode @@ -118,7 +123,7 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, if err != nil { 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 { return err } @@ -153,7 +158,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i } nonce := uint64(0) if opts.Nonce == nil { - nonce, err = c.transactor.PendingAccountNonce(opts.From) + nonce, err = c.transactor.PendingAccountNonce(opts.Context, opts.From) if err != nil { 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 // Figure out the gas allowance and gas price values gasPrice := opts.GasPrice if gasPrice == nil { - gasPrice, err = c.transactor.SuggestGasPrice() + gasPrice, err = c.transactor.SuggestGasPrice(opts.Context) if err != nil { 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 if gasLimit == nil { // Gas estimation cannot succeed without code for method invocations 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 } else if !code { return nil, ErrNoCode @@ -180,7 +185,7 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i atomic.StoreUint32(&c.pendingHasCode, 1) } // 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 { 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 if err != nil { 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 signedTx, nil diff --git a/accounts/account_manager.go b/accounts/account_manager.go index bfb7556d6..982a2ca6e 100644 --- a/accounts/account_manager.go +++ b/accounts/account_manager.go @@ -34,6 +34,8 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rpc" ) var ( @@ -340,3 +342,23 @@ func zeroKey(k *ecdsa.PrivateKey) { 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 +} diff --git a/cmd/geth/main.go b/cmd/geth/main.go index c372430f1..623f8ac81 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -29,6 +29,7 @@ import ( "time" "github.com/ethereum/ethash" + "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/console" @@ -313,11 +314,10 @@ func startNode(ctx *cli.Context, stack *node.Node) { utils.StartNode(stack) // Unlock any account specifically requested - var ethereum *eth.Ethereum - if err := stack.Service(ðereum); err != nil { + var accman *accounts.Manager + if err := stack.Service(&accman); err != nil { utils.Fatalf("ethereum service not running: %v", err) } - accman := ethereum.AccountManager() passwords := utils.MakePasswordList(ctx) accounts := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",") @@ -328,6 +328,10 @@ func startNode(ctx *cli.Context, stack *node.Node) { } // Start auxiliary services if enabled if ctx.GlobalBool(utils.MiningEnabledFlag.Name) { + var ethereum *eth.FullNodeService + if err := stack.Service(ðereum); 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 { utils.Fatalf("Failed to start mining: %v", err) } diff --git a/cmd/gethrpctest/main.go b/cmd/gethrpctest/main.go index 2e07e9426..668efbfc7 100644 --- a/cmd/gethrpctest/main.go +++ b/cmd/gethrpctest/main.go @@ -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 // stack to ensure basic checks pass before running RPC tests. func RunTest(stack *node.Node, test *tests.BlockTest) error { - var ethereum *eth.Ethereum + var ethereum *eth.FullNodeService stack.Service(ðereum) blockchain := ethereum.BlockChain() diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 14898b987..38ba3a9ba 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -763,6 +763,13 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte, if err != nil { 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) { return eth.New(ctx, ethConf) }); err != nil { diff --git a/common/natspec/natspec_e2e_test.go b/common/natspec/natspec_e2e_test.go index 255260576..ac0bbe784 100644 --- a/common/natspec/natspec_e2e_test.go +++ b/common/natspec/natspec_e2e_test.go @@ -99,7 +99,7 @@ const ( type testFrontend struct { t *testing.T - ethereum *eth.Ethereum + ethereum *eth.FullNodeService xeth *xe.XEth wait chan *big.Int lastConfirm string @@ -123,7 +123,7 @@ func (self *testFrontend) ConfirmTransaction(tx string) bool { 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") if err != nil { diff --git a/console/console_test.go b/console/console_test.go index 7738d0c44..b40db0604 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -76,7 +76,7 @@ func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {} type tester struct { workspace string stack *node.Node - ethereum *eth.Ethereum + ethereum *eth.FullNodeService console *Console input *hookedPrompter output *bytes.Buffer @@ -134,7 +134,7 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { t.Fatalf("failed to create JavaScript console: %v", err) } // Create the final tester and return - var ethereum *eth.Ethereum + var ethereum *eth.FullNodeService stack.Service(ðereum) return &tester{ diff --git a/core/vm/environment.go b/core/vm/environment.go index 747627565..664887454 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -73,6 +73,8 @@ type Environment interface { DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) // Create a new contract 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. diff --git a/eth/api.go b/eth/api.go index 3cb6c4d10..8fd759a21 100644 --- a/eth/api.go +++ b/eth/api.go @@ -18,8 +18,6 @@ package eth import ( "bytes" - "encoding/hex" - "encoding/json" "errors" "fmt" "io" @@ -27,166 +25,56 @@ import ( "math/big" "os" "runtime" - "strings" - "sync" - "time" "github.com/ethereum/ethash" - "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/state" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" + "github.com/ethereum/go-ethereum/internal/ethapi" "github.com/ethereum/go-ethereum/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/miner" - "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rpc" - "github.com/syndtr/goleveldb/leveldb" - "golang.org/x/net/context" ) -const defaultGas = uint64(90000) - -// blockByNumber is a commonly used helper function which retrieves and returns -// the block for the given block number, capable of handling two special blocks: -// rpc.LatestBlockNumber and rpc.PendingBlockNumber. It returns nil when no block -// could be found. -func blockByNumber(m *miner.Miner, bc *core.BlockChain, blockNr rpc.BlockNumber) *types.Block { - // Pending block is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, _ := m.Pending() - return block - } - // Otherwise resolve and return the block - if blockNr == rpc.LatestBlockNumber { - return bc.CurrentBlock() - } - return bc.GetBlockByNumber(uint64(blockNr)) +// PublicFullEthereumAPI provides an API to access Ethereum full node-related +// information. +type PublicFullEthereumAPI struct { + e *FullNodeService } -// stateAndBlockByNumber is a commonly used helper function which retrieves and -// returns the state and containing block for the given block number, capable of -// handling two special states: rpc.LatestBlockNumber and rpc.PendingBlockNumber. -// It returns nil when no block or state could be found. -func stateAndBlockByNumber(m *miner.Miner, bc *core.BlockChain, blockNr rpc.BlockNumber, chainDb ethdb.Database) (*state.StateDB, *types.Block, error) { - // Pending state is only known by the miner - if blockNr == rpc.PendingBlockNumber { - block, state := m.Pending() - return state, block, nil - } - // Otherwise resolve the block number and return its state - block := blockByNumber(m, bc, blockNr) - if block == nil { - return nil, nil, nil - } - stateDb, err := state.New(block.Root(), chainDb) - return stateDb, block, err -} - -// 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 { - e *Ethereum - gpo *GasPriceOracle -} - -// NewPublicEthereumAPI creates a new Ethereum protocol API. -func NewPublicEthereumAPI(e *Ethereum) *PublicEthereumAPI { - return &PublicEthereumAPI{ - e: e, - gpo: e.gpo, - } -} - -// GasPrice returns a suggestion for a gas price. -func (s *PublicEthereumAPI) GasPrice() *big.Int { - return s.gpo.SuggestPrice() -} - -// GetCompilers returns the collection of available smart contract compilers -func (s *PublicEthereumAPI) GetCompilers() ([]string, error) { - solc, err := s.e.Solc() - if err == nil && solc != nil { - return []string{"Solidity"}, nil - } - - return []string{}, nil -} - -// CompileSolidity compiles the given solidity source -func (s *PublicEthereumAPI) CompileSolidity(source string) (map[string]*compiler.Contract, error) { - solc, err := s.e.Solc() - if err != nil { - return nil, err - } - - if solc == nil { - return nil, errors.New("solc (solidity compiler) not found") - } - - return solc.Compile(source) +// NewPublicFullEthereumAPI creates a new Etheruem protocol API for full nodes. +func NewPublicFullEthereumAPI(e *FullNodeService) *PublicFullEthereumAPI { + return &PublicFullEthereumAPI{e} } // Etherbase is the address that mining rewards will be send to -func (s *PublicEthereumAPI) Etherbase() (common.Address, error) { +func (s *PublicFullEthereumAPI) Etherbase() (common.Address, error) { return s.e.Etherbase() } // Coinbase is the address that mining rewards will be send to (alias for Etherbase) -func (s *PublicEthereumAPI) Coinbase() (common.Address, error) { +func (s *PublicFullEthereumAPI) Coinbase() (common.Address, error) { return s.Etherbase() } -// ProtocolVersion returns the current Ethereum protocol version this node supports -func (s *PublicEthereumAPI) ProtocolVersion() *rpc.HexNumber { - return rpc.NewHexNumber(s.e.EthVersion()) -} - // Hashrate returns the POW hashrate -func (s *PublicEthereumAPI) Hashrate() *rpc.HexNumber { +func (s *PublicFullEthereumAPI) Hashrate() *rpc.HexNumber { return rpc.NewHexNumber(s.e.Miner().HashRate()) } -// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not -// yet received the latest block headers from its pears. In case it is synchronizing: -// - startingBlock: block number this node started to synchronise from -// - currentBlock: block number this node is currently importing -// - 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) { - origin, current, height, pulled, known := s.e.Downloader().Progress() - - // Return not syncing if the synchronisation already completed - if current >= height { - return false, nil - } - // Otherwise gather the block sync stats - return map[string]interface{}{ - "startingBlock": rpc.NewHexNumber(origin), - "currentBlock": rpc.NewHexNumber(current), - "highestBlock": rpc.NewHexNumber(height), - "pulledStates": rpc.NewHexNumber(pulled), - "knownStates": rpc.NewHexNumber(known), - }, nil -} - // PublicMinerAPI provides an API to control the miner. // It offers only methods that operate on data that pose no security risk when it is publicly accessible. type PublicMinerAPI struct { - e *Ethereum + e *FullNodeService agent *miner.RemoteAgent } // NewPublicMinerAPI create a new PublicMinerAPI instance. -func NewPublicMinerAPI(e *Ethereum) *PublicMinerAPI { +func NewPublicMinerAPI(e *FullNodeService) *PublicMinerAPI { agent := miner.NewRemoteAgent() e.Miner().Register(agent) @@ -232,11 +120,11 @@ func (s *PublicMinerAPI) SubmitHashrate(hashrate rpc.HexNumber, id common.Hash) // PrivateMinerAPI provides private RPC methods to control the miner. // These methods can be abused by external users and must be considered insecure for use by untrusted users. type PrivateMinerAPI struct { - e *Ethereum + e *FullNodeService } // NewPrivateMinerAPI create a new RPC service which controls the miner of this node. -func NewPrivateMinerAPI(e *Ethereum) *PrivateMinerAPI { +func NewPrivateMinerAPI(e *FullNodeService) *PrivateMinerAPI { return &PrivateMinerAPI{e: e} } @@ -303,1199 +191,20 @@ func (s *PrivateMinerAPI) MakeDAG(blockNr rpc.BlockNumber) (bool, error) { return true, nil } -// PublicTxPoolAPI offers and API for the transaction pool. It only operates on data that is non confidential. -type PublicTxPoolAPI struct { - e *Ethereum +// PrivateFullAdminAPI is the collection of Etheruem full node-related APIs +// exposed over the private admin endpoint. +type PrivateFullAdminAPI struct { + eth *FullNodeService } -// NewPublicTxPoolAPI creates a new tx pool service that gives information about the transaction pool. -func NewPublicTxPoolAPI(e *Ethereum) *PublicTxPoolAPI { - return &PublicTxPoolAPI{e} -} - -// 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.e.TxPool().Content() - - // Flatten the pending transactions - for account, batches := range pending { - dump := make(map[string][]*RPCTransaction) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) - } - } - content["pending"][account.Hex()] = dump - } - // Flatten the queued transactions - for account, batches := range queue { - dump := make(map[string][]*RPCTransaction) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[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.e.TxPool().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.e.TxPool().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, batches := range pending { - dump := make(map[string][]string) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[nonce], format(tx)) - } - } - content["pending"][account.Hex()] = dump - } - // Flatten the queued transactions - for account, batches := range queue { - dump := make(map[string][]string) - for nonce, txs := range batches { - nonce := fmt.Sprintf("%d", nonce) - for _, tx := range txs { - dump[nonce] = append(dump[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 { - am *accounts.Manager -} - -// NewPublicAccountAPI creates a new PublicAccountAPI. -func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI { - return &PublicAccountAPI{am: am} -} - -// Accounts returns the collection of accounts this node manages -func (s *PublicAccountAPI) Accounts() []accounts.Account { - return s.am.Accounts() -} - -// PrivateAccountAPI provides an API to access accounts managed by this node. -// It offers methods to create, (un)lock en list accounts. Some methods accept -// passwords and are therefore considered private by default. -type PrivateAccountAPI struct { - am *accounts.Manager - txPool *core.TxPool - txMu *sync.Mutex - gpo *GasPriceOracle -} - -// NewPrivateAccountAPI create a new PrivateAccountAPI. -func NewPrivateAccountAPI(e *Ethereum) *PrivateAccountAPI { - return &PrivateAccountAPI{ - am: e.accountManager, - txPool: e.txPool, - txMu: &e.txMu, - gpo: e.gpo, - } -} - -// ListAccounts will return a list of addresses for accounts this node manages. -func (s *PrivateAccountAPI) ListAccounts() []common.Address { - accounts := s.am.Accounts() - addresses := make([]common.Address, len(accounts)) - for i, acc := range accounts { - addresses[i] = acc.Address - } - return addresses -} - -// NewAccount will create a new account and returns the address for the new account. -func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) { - acc, err := s.am.NewAccount(password) - if err == nil { - return acc.Address, nil - } - return common.Address{}, err -} - -// ImportRawKey stores the given hex encoded ECDSA key into the key directory, -// encrypting it with the passphrase. -func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { - hexkey, err := hex.DecodeString(privkey) - if err != nil { - return common.Address{}, err - } - - acc, err := s.am.ImportECDSA(crypto.ToECDSA(hexkey), password) - return acc.Address, err -} - -// UnlockAccount will unlock the account associated with the given address with -// the given password for duration seconds. If duration is nil it will use a -// default of 300 seconds. It returns an indication if the account was unlocked. -func (s *PrivateAccountAPI) UnlockAccount(addr common.Address, password string, duration *rpc.HexNumber) (bool, error) { - if duration == nil { - duration = rpc.NewHexNumber(300) - } - a := accounts.Account{Address: addr} - d := time.Duration(duration.Int64()) * time.Second - if err := s.am.TimedUnlock(a, password, d); err != nil { - return false, err - } - return true, nil -} - -// LockAccount will lock the account associated with the given address when it's unlocked. -func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool { - return s.am.Lock(addr) == nil -} - -// SignAndSendTransaction will create a transaction from the given arguments and -// tries to sign it with the key associated with args.To. If the given passwd isn't -// able to decrypt the key it fails. -func (s *PrivateAccountAPI) SignAndSendTransaction(args SendTxArgs, passwd string) (common.Hash, error) { - args = prepareSendTxArgs(args, s.gpo) - - s.txMu.Lock() - defer s.txMu.Unlock() - - if args.Nonce == nil { - args.Nonce = rpc.NewHexNumber(s.txPool.State().GetNonce(args.From)) - } - - var tx *types.Transaction - if args.To == nil { - tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) - } else { - tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) - } - - signature, err := s.am.SignWithPassphrase(args.From, passwd, tx.SigHash().Bytes()) - if err != nil { - return common.Hash{}, err - } - - return submitTransaction(s.txPool, tx, signature) -} - -// PublicBlockChainAPI provides an API to access the Ethereum blockchain. -// It offers only methods that operate on public data that is freely available to anyone. -type PublicBlockChainAPI struct { - config *core.ChainConfig - bc *core.BlockChain - chainDb ethdb.Database - eventMux *event.TypeMux - muNewBlockSubscriptions sync.Mutex // protects newBlocksSubscriptions - newBlockSubscriptions map[string]func(core.ChainEvent) error // callbacks for new block subscriptions - am *accounts.Manager - miner *miner.Miner - gpo *GasPriceOracle -} - -// NewPublicBlockChainAPI creates a new Etheruem blockchain API. -func NewPublicBlockChainAPI(config *core.ChainConfig, bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, gpo *GasPriceOracle, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI { - api := &PublicBlockChainAPI{ - config: config, - bc: bc, - miner: m, - chainDb: chainDb, - eventMux: eventMux, - am: am, - newBlockSubscriptions: make(map[string]func(core.ChainEvent) error), - gpo: gpo, - } - - go api.subscriptionLoop() - - return api -} - -// subscriptionLoop reads events from the global event mux and creates notifications for the matched subscriptions. -func (s *PublicBlockChainAPI) subscriptionLoop() { - sub := s.eventMux.Subscribe(core.ChainEvent{}) - for event := range sub.Chan() { - if chainEvent, ok := event.Data.(core.ChainEvent); ok { - s.muNewBlockSubscriptions.Lock() - for id, notifyOf := range s.newBlockSubscriptions { - if notifyOf(chainEvent) == rpc.ErrNotificationNotFound { - delete(s.newBlockSubscriptions, id) - } - } - s.muNewBlockSubscriptions.Unlock() - } - } -} - -// BlockNumber returns the block number of the chain head. -func (s *PublicBlockChainAPI) BlockNumber() *big.Int { - return s.bc.CurrentHeader().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(address common.Address, blockNr rpc.BlockNumber) (*big.Int, error) { - state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb) - if state == nil || err != nil { - return nil, err - } - return state.GetBalance(address), nil -} - -// 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(blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) { - if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { - response, err := s.rpcOutputBlock(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"} { - response[field] = nil - } - } - return response, err - } - return nil, nil -} - -// 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(blockHash common.Hash, fullTx bool) (map[string]interface{}, error) { - if block := s.bc.GetBlockByHash(blockHash); block != nil { - return s.rpcOutputBlock(block, true, fullTx) - } - return nil, nil -} - -// 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(blockNr rpc.BlockNumber, index rpc.HexNumber) (map[string]interface{}, error) { - if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { - uncles := block.Uncles() - if index.Int() < 0 || index.Int() >= len(uncles) { - glog.V(logger.Debug).Infof("uncle block on index %d not found for block #%d", index.Int(), blockNr) - return nil, nil - } - block = types.NewBlockWithHeader(uncles[index.Int()]) - return s.rpcOutputBlock(block, false, false) - } - return nil, nil -} - -// 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(blockHash common.Hash, index rpc.HexNumber) (map[string]interface{}, error) { - if block := s.bc.GetBlockByHash(blockHash); block != nil { - uncles := block.Uncles() - if index.Int() < 0 || index.Int() >= len(uncles) { - glog.V(logger.Debug).Infof("uncle block on index %d not found for block %s", index.Int(), blockHash.Hex()) - return nil, nil - } - block = types.NewBlockWithHeader(uncles[index.Int()]) - return s.rpcOutputBlock(block, false, false) - } - return nil, nil -} - -// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number -func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(blockNr rpc.BlockNumber) *rpc.HexNumber { - if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { - return rpc.NewHexNumber(len(block.Uncles())) - } - return nil -} - -// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash -func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(blockHash common.Hash) *rpc.HexNumber { - if block := s.bc.GetBlockByHash(blockHash); block != nil { - return rpc.NewHexNumber(len(block.Uncles())) - } - return nil -} - -// NewBlocksArgs allows the user to specify if the returned block should include transactions and in which format. -type NewBlocksArgs struct { - IncludeTransactions bool `json:"includeTransactions"` - TransactionDetails bool `json:"transactionDetails"` -} - -// NewBlocks triggers a new block event each time a block is appended to the chain. It accepts an argument which allows -// the caller to specify whether the output should contain transactions and in what format. -func (s *PublicBlockChainAPI) NewBlocks(ctx context.Context, args NewBlocksArgs) (rpc.Subscription, error) { - notifier, supported := rpc.NotifierFromContext(ctx) - if !supported { - return nil, rpc.ErrNotificationsUnsupported - } - - // create a subscription that will remove itself when unsubscribed/cancelled - subscription, err := notifier.NewSubscription(func(subId string) { - s.muNewBlockSubscriptions.Lock() - delete(s.newBlockSubscriptions, subId) - s.muNewBlockSubscriptions.Unlock() - }) - - if err != nil { - return nil, err - } - - // add a callback that is called on chain events which will format the block and notify the client - s.muNewBlockSubscriptions.Lock() - s.newBlockSubscriptions[subscription.ID()] = func(e core.ChainEvent) error { - notification, err := s.rpcOutputBlock(e.Block, args.IncludeTransactions, args.TransactionDetails) - if err == nil { - return subscription.Notify(notification) - } - glog.V(logger.Warn).Info("unable to format block %v\n", err) - return nil - } - s.muNewBlockSubscriptions.Unlock() - return subscription, nil -} - -// GetCode returns the code stored at the given address in the state for the given block number. -func (s *PublicBlockChainAPI) GetCode(address common.Address, blockNr rpc.BlockNumber) (string, error) { - state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb) - if state == nil || err != nil { - return "", err - } - res := state.GetCode(address) - if len(res) == 0 { // backwards compatibility - return "0x", nil - } - return common.ToHex(res), 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(address common.Address, key string, blockNr rpc.BlockNumber) (string, error) { - state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb) - if state == nil || err != nil { - return "0x", err - } - return state.GetState(address, common.HexToHash(key)).Hex(), nil -} - -// callmsg is the message type used for call transactions. -type callmsg struct { - from *state.StateObject - to *common.Address - gas, gasPrice *big.Int - value *big.Int - data []byte -} - -// accessor boilerplate to implement core.Message -func (m callmsg) From() (common.Address, error) { return m.from.Address(), nil } -func (m callmsg) FromFrontier() (common.Address, error) { return m.from.Address(), nil } -func (m callmsg) Nonce() uint64 { return m.from.Nonce() } -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"` - To *common.Address `json:"to"` - Gas *rpc.HexNumber `json:"gas"` - GasPrice *rpc.HexNumber `json:"gasPrice"` - Value rpc.HexNumber `json:"value"` - Data string `json:"data"` -} - -func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { - // Fetch the state associated with the block number - stateDb, block, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb) - if stateDb == nil || err != nil { - return "0x", nil, err - } - stateDb = stateDb.Copy() - - // Retrieve the account state object to interact with - var from *state.StateObject - if args.From == (common.Address{}) { - accounts := s.am.Accounts() - if len(accounts) == 0 { - from = stateDb.GetOrNewStateObject(common.Address{}) - } else { - from = stateDb.GetOrNewStateObject(accounts[0].Address) - } - } else { - from = stateDb.GetOrNewStateObject(args.From) - } - from.SetBalance(common.MaxBig) - - // Assemble the CALL invocation - msg := callmsg{ - from: from, - to: args.To, - gas: args.Gas.BigInt(), - gasPrice: args.GasPrice.BigInt(), - value: args.Value.BigInt(), - data: common.FromHex(args.Data), - } - if msg.gas == nil { - msg.gas = big.NewInt(50000000) - } - if msg.gasPrice == nil { - msg.gasPrice = s.gpo.SuggestPrice() - } - - // Execute the call and return - vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), s.config.VmConfig) - gp := new(core.GasPool).AddGas(common.MaxBig) - - res, requiredGas, _, err := core.NewStateTransition(vmenv, msg, gp).TransitionDb() - if len(res) == 0 { // backwards compatibility - return "0x", requiredGas, err - } - return common.ToHex(res), requiredGas, 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 useful to execute and retrieve values. -func (s *PublicBlockChainAPI) Call(args CallArgs, blockNr rpc.BlockNumber) (string, error) { - result, _, err := s.doCall(args, blockNr) - return result, err -} - -// EstimateGas returns an estimate of the amount of gas needed to execute the given transaction. -func (s *PublicBlockChainAPI) EstimateGas(args CallArgs) (*rpc.HexNumber, error) { - _, gas, err := s.doCall(args, rpc.PendingBlockNumber) - return rpc.NewHexNumber(gas), err -} - -// 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) { - fields := map[string]interface{}{ - "number": rpc.NewHexNumber(b.Number()), - "hash": b.Hash(), - "parentHash": b.ParentHash(), - "nonce": b.Header().Nonce, - "sha3Uncles": b.UncleHash(), - "logsBloom": b.Bloom(), - "stateRoot": b.Root(), - "miner": b.Coinbase(), - "difficulty": rpc.NewHexNumber(b.Difficulty()), - "totalDifficulty": rpc.NewHexNumber(s.bc.GetTd(b.Hash(), b.NumberU64())), - "extraData": fmt.Sprintf("0x%x", b.Extra()), - "size": rpc.NewHexNumber(b.Size().Int64()), - "gasLimit": rpc.NewHexNumber(b.GasLimit()), - "gasUsed": rpc.NewHexNumber(b.GasUsed()), - "timestamp": rpc.NewHexNumber(b.Time()), - "transactionsRoot": b.TxHash(), - "receiptRoot": b.ReceiptHash(), - } - - 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 - } - } - fields["transactions"] = transactions - } - - uncles := b.Uncles() - uncleHashes := make([]common.Hash, len(uncles)) - for i, uncle := range uncles { - uncleHashes[i] = uncle.Hash() - } - fields["uncles"] = uncleHashes - - return fields, nil -} - -// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction -type RPCTransaction struct { - BlockHash common.Hash `json:"blockHash"` - BlockNumber *rpc.HexNumber `json:"blockNumber"` - From common.Address `json:"from"` - Gas *rpc.HexNumber `json:"gas"` - GasPrice *rpc.HexNumber `json:"gasPrice"` - Hash common.Hash `json:"hash"` - Input string `json:"input"` - Nonce *rpc.HexNumber `json:"nonce"` - To *common.Address `json:"to"` - TransactionIndex *rpc.HexNumber `json:"transactionIndex"` - Value *rpc.HexNumber `json:"value"` -} - -// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation -func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { - from, _ := tx.FromFrontier() - - return &RPCTransaction{ - From: from, - Gas: rpc.NewHexNumber(tx.Gas()), - GasPrice: rpc.NewHexNumber(tx.GasPrice()), - Hash: tx.Hash(), - Input: fmt.Sprintf("0x%x", tx.Data()), - Nonce: rpc.NewHexNumber(tx.Nonce()), - To: tx.To(), - Value: rpc.NewHexNumber(tx.Value()), - } -} - -// newRPCTransaction returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransaction, error) { - if txIndex >= 0 && txIndex < len(b.Transactions()) { - tx := b.Transactions()[txIndex] - from, err := tx.FromFrontier() - if err != nil { - return nil, err - } - - 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: fmt.Sprintf("0x%x", tx.Data()), - Nonce: rpc.NewHexNumber(tx.Nonce()), - To: tx.To(), - TransactionIndex: rpc.NewHexNumber(txIndex), - Value: rpc.NewHexNumber(tx.Value()), - }, nil - } - - 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 -} - -// PublicTransactionPoolAPI exposes methods for the RPC interface -type PublicTransactionPoolAPI struct { - eventMux *event.TypeMux - chainDb ethdb.Database - gpo *GasPriceOracle - bc *core.BlockChain - miner *miner.Miner - am *accounts.Manager - txPool *core.TxPool - txMu *sync.Mutex - muPendingTxSubs sync.Mutex - pendingTxSubs map[string]rpc.Subscription -} - -// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. -func NewPublicTransactionPoolAPI(e *Ethereum) *PublicTransactionPoolAPI { - api := &PublicTransactionPoolAPI{ - eventMux: e.eventMux, - gpo: e.gpo, - chainDb: e.chainDb, - bc: e.blockchain, - am: e.accountManager, - txPool: e.txPool, - txMu: &e.txMu, - miner: e.miner, - pendingTxSubs: make(map[string]rpc.Subscription), - } - go api.subscriptionLoop() - - return api -} - -// subscriptionLoop listens for events on the global event mux and creates notifications for subscriptions. -func (s *PublicTransactionPoolAPI) subscriptionLoop() { - sub := s.eventMux.Subscribe(core.TxPreEvent{}) - for event := range sub.Chan() { - tx := event.Data.(core.TxPreEvent) - if from, err := tx.Tx.FromFrontier(); err == nil { - if s.am.HasAddress(from) { - s.muPendingTxSubs.Lock() - for id, sub := range s.pendingTxSubs { - if sub.Notify(tx.Tx.Hash()) == rpc.ErrNotificationNotFound { - delete(s.pendingTxSubs, id) - } - } - s.muPendingTxSubs.Unlock() - } - } - } -} - -func getTransaction(chainDb ethdb.Database, txPool *core.TxPool, 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 = txPool.GetTransaction(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(blockNr rpc.BlockNumber) *rpc.HexNumber { - if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { - return rpc.NewHexNumber(len(block.Transactions())) - } - return nil -} - -// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash. -func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(blockHash common.Hash) *rpc.HexNumber { - if block := s.bc.GetBlockByHash(blockHash); block != nil { - return rpc.NewHexNumber(len(block.Transactions())) - } - return nil -} - -// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(blockNr rpc.BlockNumber, index rpc.HexNumber) (*RPCTransaction, error) { - if block := blockByNumber(s.miner, s.bc, blockNr); block != nil { - return newRPCTransactionFromBlockIndex(block, index.Int()) - } - return nil, nil -} - -// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(blockHash common.Hash, index rpc.HexNumber) (*RPCTransaction, error) { - if block := s.bc.GetBlockByHash(blockHash); block != nil { - return newRPCTransactionFromBlockIndex(block, index.Int()) - } - return nil, nil -} - -// GetTransactionCount returns the number of transactions the given address has sent for the given block number -func (s *PublicTransactionPoolAPI) GetTransactionCount(address common.Address, blockNr rpc.BlockNumber) (*rpc.HexNumber, error) { - state, _, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb) - if state == nil || err != nil { - return nil, err - } - return rpc.NewHexNumber(state.GetNonce(address)), nil -} - -// getTransactionBlockData fetches the meta data for the given transaction from the chain database. This is useful to -// retrieve block information for a hash. It returns the block hash, block index and transaction index. -func getTransactionBlockData(chainDb ethdb.Database, txHash common.Hash) (common.Hash, uint64, uint64, error) { - var txBlock struct { - BlockHash common.Hash - BlockIndex uint64 - Index uint64 - } - - blockData, err := chainDb.Get(append(txHash.Bytes(), 0x0001)) - if err != nil { - return common.Hash{}, uint64(0), uint64(0), err - } - - reader := bytes.NewReader(blockData) - if err = rlp.Decode(reader, &txBlock); err != nil { - return common.Hash{}, uint64(0), uint64(0), err - } - - return txBlock.BlockHash, txBlock.BlockIndex, txBlock.Index, nil -} - -// GetTransactionByHash returns the transaction for the given hash -func (s *PublicTransactionPoolAPI) GetTransactionByHash(txHash common.Hash) (*RPCTransaction, error) { - var tx *types.Transaction - var isPending bool - var err error - - if tx, isPending, err = getTransaction(s.chainDb, s.txPool, txHash); err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } else if tx == nil { - return nil, nil - } - - if isPending { - return newRPCPendingTransaction(tx), nil - } - - blockHash, _, _, err := getTransactionBlockData(s.chainDb, txHash) - if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } - - if block := s.bc.GetBlockByHash(blockHash); block != nil { - return newRPCTransaction(block, txHash) - } - - return nil, nil -} - -// 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.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.chainDb, s.txPool, txHash) - if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } - - txBlock, blockIndex, index, err := getTransactionBlockData(s.chainDb, txHash) - if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } - - from, err := tx.FromFrontier() - if err != nil { - glog.V(logger.Debug).Infof("%v\n", err) - return nil, nil - } - - fields := map[string]interface{}{ - "root": common.Bytes2Hex(receipt.PostState), - "blockHash": txBlock, - "blockNumber": rpc.NewHexNumber(blockIndex), - "transactionHash": txHash, - "transactionIndex": rpc.NewHexNumber(index), - "from": from, - "to": tx.To(), - "gasUsed": rpc.NewHexNumber(receipt.GasUsed), - "cumulativeGasUsed": rpc.NewHexNumber(receipt.CumulativeGasUsed), - "contractAddress": nil, - "logs": receipt.Logs, - } - - if receipt.Logs == nil { - fields["logs"] = []vm.Logs{} - } - - // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation - if bytes.Compare(receipt.ContractAddress.Bytes(), bytes.Repeat([]byte{0}, 20)) != 0 { - fields["contractAddress"] = receipt.ContractAddress - } - - return fields, nil -} - -// 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) { - signature, err := s.am.Sign(addr, tx.SigHash().Bytes()) - if err != nil { - return nil, err - } - return tx.WithSignature(signature) -} - -// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. -type SendTxArgs struct { - From common.Address `json:"from"` - To *common.Address `json:"to"` - Gas *rpc.HexNumber `json:"gas"` - GasPrice *rpc.HexNumber `json:"gasPrice"` - Value *rpc.HexNumber `json:"value"` - Data string `json:"data"` - Nonce *rpc.HexNumber `json:"nonce"` -} - -// prepareSendTxArgs is a helper function that fills in default values for unspecified tx fields. -func prepareSendTxArgs(args SendTxArgs, gpo *GasPriceOracle) SendTxArgs { - if args.Gas == nil { - args.Gas = rpc.NewHexNumber(defaultGas) - } - if args.GasPrice == nil { - args.GasPrice = rpc.NewHexNumber(gpo.SuggestPrice()) - } - if args.Value == nil { - args.Value = rpc.NewHexNumber(0) - } - return args -} - -// submitTransaction is a helper function that submits tx to txPool and creates a log entry. -func submitTransaction(txPool *core.TxPool, tx *types.Transaction, signature []byte) (common.Hash, error) { - signedTx, err := tx.WithSignature(signature) - if err != nil { - return common.Hash{}, err - } - - txPool.SetLocal(signedTx) - if err := txPool.Add(signedTx); err != nil { - return common.Hash{}, err - } - - if signedTx.To() == nil { - from, _ := signedTx.From() - addr := crypto.CreateAddress(from, signedTx.Nonce()) - glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) - } else { - glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex()) - } - - return signedTx.Hash(), nil -} - -// SendTransaction creates a transaction for the given argument, sign it and submit it to the -// transaction pool. -func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash, error) { - args = prepareSendTxArgs(args, s.gpo) - - s.txMu.Lock() - defer s.txMu.Unlock() - - if args.Nonce == nil { - args.Nonce = rpc.NewHexNumber(s.txPool.State().GetNonce(args.From)) - } - - var tx *types.Transaction - if args.To == nil { - tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) - } else { - tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) - } - - signature, err := s.am.Sign(args.From, tx.SigHash().Bytes()) - if err != nil { - return common.Hash{}, err - } - - return submitTransaction(s.txPool, tx, signature) -} - -// SendRawTransaction will add the signed transaction to the transaction pool. -// The sender is responsible for signing the transaction and using the correct nonce. -func (s *PublicTransactionPoolAPI) SendRawTransaction(encodedTx string) (string, error) { - tx := new(types.Transaction) - if err := rlp.DecodeBytes(common.FromHex(encodedTx), tx); err != nil { - return "", err - } - - s.txPool.SetLocal(tx) - if err := s.txPool.Add(tx); err != nil { - return "", err - } - - if tx.To() == nil { - from, err := tx.FromFrontier() - 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 -} - -// Sign signs the given hash using the key that matches the address. The key must be -// unlocked in order to sign the hash. -func (s *PublicTransactionPoolAPI) Sign(addr common.Address, hash common.Hash) (string, error) { - signature, error := s.am.Sign(addr, hash[:]) - return common.ToHex(signature), error -} - -// SignTransactionArgs represents the arguments to sign a transaction. -type SignTransactionArgs struct { - From common.Address - To *common.Address - Nonce *rpc.HexNumber - Value *rpc.HexNumber - Gas *rpc.HexNumber - GasPrice *rpc.HexNumber - Data string - - BlockNumber int64 -} - -// Tx is a helper object for argument and return values -type Tx struct { - tx *types.Transaction - - To *common.Address `json:"to"` - From common.Address `json:"from"` - Nonce *rpc.HexNumber `json:"nonce"` - Value *rpc.HexNumber `json:"value"` - Data string `json:"data"` - GasLimit *rpc.HexNumber `json:"gas"` - GasPrice *rpc.HexNumber `json:"gasPrice"` - Hash common.Hash `json:"hash"` -} - -// UnmarshalJSON parses JSON data into tx. -func (tx *Tx) UnmarshalJSON(b []byte) (err error) { - req := struct { - To *common.Address `json:"to"` - From common.Address `json:"from"` - Nonce *rpc.HexNumber `json:"nonce"` - Value *rpc.HexNumber `json:"value"` - Data string `json:"data"` - GasLimit *rpc.HexNumber `json:"gas"` - GasPrice *rpc.HexNumber `json:"gasPrice"` - Hash common.Hash `json:"hash"` - }{} - - if err := json.Unmarshal(b, &req); err != nil { - return err - } - - tx.To = req.To - tx.From = req.From - tx.Nonce = req.Nonce - tx.Value = req.Value - tx.Data = req.Data - tx.GasLimit = req.GasLimit - tx.GasPrice = req.GasPrice - tx.Hash = req.Hash - - data := common.Hex2Bytes(tx.Data) - - if tx.Nonce == nil { - return fmt.Errorf("need nonce") - } - if tx.Value == nil { - tx.Value = rpc.NewHexNumber(0) - } - if tx.GasLimit == nil { - tx.GasLimit = rpc.NewHexNumber(0) - } - if tx.GasPrice == nil { - tx.GasPrice = rpc.NewHexNumber(int64(50000000000)) - } - - if req.To == nil { - tx.tx = types.NewContractCreation(tx.Nonce.Uint64(), tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data) - } else { - tx.tx = types.NewTransaction(tx.Nonce.Uint64(), *tx.To, tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data) - } - - return nil -} - -// SignTransactionResult represents a RLP encoded signed transaction. -type SignTransactionResult struct { - Raw string `json:"raw"` - Tx *Tx `json:"tx"` -} - -func newTx(t *types.Transaction) *Tx { - from, _ := t.FromFrontier() - return &Tx{ - tx: t, - To: t.To(), - From: from, - Value: rpc.NewHexNumber(t.Value()), - Nonce: rpc.NewHexNumber(t.Nonce()), - Data: "0x" + common.Bytes2Hex(t.Data()), - GasLimit: rpc.NewHexNumber(t.Gas()), - GasPrice: rpc.NewHexNumber(t.GasPrice()), - Hash: t.Hash(), - } -} - -// SignTransaction will sign the given transaction with the from account. -// The node needs to have the private key of the account corresponding with -// the given from address and it needs to be unlocked. -func (s *PublicTransactionPoolAPI) SignTransaction(args SignTransactionArgs) (*SignTransactionResult, error) { - if args.Gas == nil { - args.Gas = rpc.NewHexNumber(defaultGas) - } - if args.GasPrice == nil { - args.GasPrice = rpc.NewHexNumber(s.gpo.SuggestPrice()) - } - if args.Value == nil { - args.Value = rpc.NewHexNumber(0) - } - - s.txMu.Lock() - defer s.txMu.Unlock() - - if args.Nonce == nil { - args.Nonce = rpc.NewHexNumber(s.txPool.State().GetNonce(args.From)) - } - - var tx *types.Transaction - if args.To == nil { - tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) - } else { - 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) - if err != nil { - return nil, err - } - - data, err := rlp.EncodeToBytes(signedTx) - if err != nil { - return nil, err - } - - return &SignTransactionResult{"0x" + common.Bytes2Hex(data), newTx(signedTx)}, nil -} - -// 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.txPool.GetTransactions() - transactions := make([]*RPCTransaction, 0, len(pending)) - for _, tx := range pending { - from, _ := tx.FromFrontier() - if s.am.HasAddress(from) { - transactions = append(transactions, newRPCPendingTransaction(tx)) - } - } - return transactions -} - -// NewPendingTransactions creates a subscription that is triggered each time a transaction enters the transaction pool -// and is send from one of the transactions this nodes manages. -func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (rpc.Subscription, error) { - notifier, supported := rpc.NotifierFromContext(ctx) - if !supported { - return nil, rpc.ErrNotificationsUnsupported - } - - subscription, err := notifier.NewSubscription(func(id string) { - s.muPendingTxSubs.Lock() - delete(s.pendingTxSubs, id) - s.muPendingTxSubs.Unlock() - }) - - if err != nil { - return nil, err - } - - s.muPendingTxSubs.Lock() - s.pendingTxSubs[subscription.ID()] = subscription - s.muPendingTxSubs.Unlock() - - return subscription, nil -} - -// 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(tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) { - - pending := s.txPool.GetTransactions() - for _, p := range pending { - if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() { - if gasPrice == nil { - gasPrice = rpc.NewHexNumber(tx.tx.GasPrice()) - } - if gasLimit == nil { - gasLimit = rpc.NewHexNumber(tx.tx.Gas()) - } - - var newTx *types.Transaction - if tx.tx.To() == nil { - newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) - } else { - newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) - } - - signedTx, err := s.sign(tx.From, newTx) - if err != nil { - return common.Hash{}, err - } - - s.txPool.RemoveTx(tx.Hash) - if err = s.txPool.Add(signedTx); err != nil { - return common.Hash{}, err - } - - return signedTx.Hash(), nil - } - } - - return common.Hash{}, fmt.Errorf("Transaction %#x not found", tx.Hash) -} - -// PrivateAdminAPI is the collection of Etheruem APIs exposed over the private -// admin endpoint. -type PrivateAdminAPI struct { - eth *Ethereum -} - -// NewPrivateAdminAPI creates a new API definition for the private admin methods -// of the Ethereum service. -func NewPrivateAdminAPI(eth *Ethereum) *PrivateAdminAPI { - return &PrivateAdminAPI{eth: eth} -} - -// SetSolc sets the Solidity compiler path to be used by the node. -func (api *PrivateAdminAPI) SetSolc(path string) (string, error) { - solc, err := api.eth.SetSolc(path) - if err != nil { - return "", err - } - return solc.Info(), nil +// NewPrivateAdminAPI creates a new API definition for the full node private +// admin methods of the Ethereum service. +func NewPrivateFullAdminAPI(eth *FullNodeService) *PrivateFullAdminAPI { + return &PrivateFullAdminAPI{eth: eth} } // ExportChain exports the current blockchain into a local file. -func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) { +func (api *PrivateFullAdminAPI) ExportChain(file string) (bool, error) { // Make sure we can create the file to export into out, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) if err != nil { @@ -1521,7 +230,7 @@ func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool { } // ImportChain imports a blockchain from a local file. -func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) { +func (api *PrivateFullAdminAPI) ImportChain(file string) (bool, error) { // Make sure the can access the file to import in, err := os.Open(file) if err != nil { @@ -1562,20 +271,20 @@ func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) { return true, nil } -// PublicDebugAPI is the collection of Etheruem APIs exposed over the public -// debugging endpoint. -type PublicDebugAPI struct { - eth *Ethereum +// PublicFullDebugAPI is the collection of Etheruem full node APIs exposed +// over the public debugging endpoint. +type PublicFullDebugAPI struct { + eth *FullNodeService } -// NewPublicDebugAPI creates a new API definition for the public debug methods -// of the Ethereum service. -func NewPublicDebugAPI(eth *Ethereum) *PublicDebugAPI { - return &PublicDebugAPI{eth: eth} +// NewPublicFullDebugAPI creates a new API definition for the full node- +// related public debug methods of the Ethereum service. +func NewPublicFullDebugAPI(eth *FullNodeService) *PublicFullDebugAPI { + return &PublicFullDebugAPI{eth: eth} } // DumpBlock retrieves the entire state of the database at a given block. -func (api *PublicDebugAPI) DumpBlock(number uint64) (state.World, error) { +func (api *PublicFullDebugAPI) DumpBlock(number uint64) (state.World, error) { block := api.eth.BlockChain().GetBlockByNumber(number) if block == nil { return state.World{}, fmt.Errorf("block #%d not found", number) @@ -1587,81 +296,30 @@ func (api *PublicDebugAPI) DumpBlock(number uint64) (state.World, error) { return stateDb.RawDump(), nil } -// GetBlockRlp retrieves the RLP encoded for of a single block. -func (api *PublicDebugAPI) GetBlockRlp(number uint64) (string, error) { - block := api.eth.BlockChain().GetBlockByNumber(number) - if block == nil { - return "", fmt.Errorf("block #%d not found", number) - } - encoded, err := rlp.EncodeToBytes(block) - if err != nil { - return "", err - } - return fmt.Sprintf("%x", encoded), nil -} - -// PrintBlock retrieves a block and returns its pretty printed form. -func (api *PublicDebugAPI) PrintBlock(number uint64) (string, error) { - block := api.eth.BlockChain().GetBlockByNumber(number) - if block == nil { - return "", fmt.Errorf("block #%d not found", number) - } - return fmt.Sprintf("%s", block), nil -} - -// SeedHash retrieves the seed hash of a block. -func (api *PublicDebugAPI) SeedHash(number uint64) (string, error) { - block := api.eth.BlockChain().GetBlockByNumber(number) - if block == nil { - return "", fmt.Errorf("block #%d not found", number) - } - hash, err := ethash.GetSeedHash(number) - if err != nil { - return "", err - } - return fmt.Sprintf("0x%x", hash), nil -} - -// PrivateDebugAPI is the collection of Etheruem APIs exposed over the private -// debugging endpoint. -type PrivateDebugAPI struct { +// PrivateFullDebugAPI is the collection of Etheruem full node APIs exposed over +// the private debugging endpoint. +type PrivateFullDebugAPI struct { config *core.ChainConfig - eth *Ethereum + eth *FullNodeService } -// NewPrivateDebugAPI creates a new API definition for the private debug methods -// of the Ethereum service. -func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAPI { - return &PrivateDebugAPI{config: config, eth: eth} -} - -// ChaindbProperty returns leveldb properties of the chain database. -func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { - ldb, ok := api.eth.chainDb.(interface { - LDB() *leveldb.DB - }) - if !ok { - return "", fmt.Errorf("chaindbProperty does not work for memory databases") - } - if property == "" { - property = "leveldb.stats" - } else if !strings.HasPrefix(property, "leveldb.") { - property = "leveldb." + property - } - return ldb.LDB().GetProperty(property) +// NewPrivateFullDebugAPI creates a new API definition for the full node-related +// private debug methods of the Ethereum service. +func NewPrivateFullDebugAPI(config *core.ChainConfig, eth *FullNodeService) *PrivateFullDebugAPI { + return &PrivateFullDebugAPI{config: config, eth: eth} } // BlockTraceResult is the returned value when replaying a block to check for // consensus results and full VM trace logs for all included transactions. type BlockTraceResult struct { - Validated bool `json:"validated"` - StructLogs []structLogRes `json:"structLogs"` - Error string `json:"error"` + Validated bool `json:"validated"` + StructLogs []ethapi.StructLogRes `json:"structLogs"` + Error string `json:"error"` } // TraceBlock processes the given block's RLP but does not import the block in to // the chain. -func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) BlockTraceResult { +func (api *PrivateFullDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) BlockTraceResult { var block types.Block err := rlp.Decode(bytes.NewReader(blockRlp), &block) if err != nil { @@ -1671,14 +329,14 @@ func (api *PrivateDebugAPI) TraceBlock(blockRlp []byte, config *vm.Config) Block validated, logs, err := api.traceBlock(&block, config) return BlockTraceResult{ Validated: validated, - StructLogs: formatLogs(logs), + StructLogs: ethapi.FormatLogs(logs), Error: formatError(err), } } // TraceBlockFromFile loads the block's RLP from the given file name and attempts to // process it but does not import the block in to the chain. -func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) BlockTraceResult { +func (api *PrivateFullDebugAPI) TraceBlockFromFile(file string, config *vm.Config) BlockTraceResult { blockRlp, err := ioutil.ReadFile(file) if err != nil { return BlockTraceResult{Error: fmt.Sprintf("could not read file: %v", err)} @@ -1687,7 +345,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) B } // TraceBlockByNumber processes the block by canonical block number. -func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult { +func (api *PrivateFullDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult { // Fetch the block that we aim to reprocess block := api.eth.BlockChain().GetBlockByNumber(number) if block == nil { @@ -1697,13 +355,13 @@ func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) validated, logs, err := api.traceBlock(block, config) return BlockTraceResult{ Validated: validated, - StructLogs: formatLogs(logs), + StructLogs: ethapi.FormatLogs(logs), Error: formatError(err), } } // TraceBlockByHash processes the block by hash. -func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config) BlockTraceResult { +func (api *PrivateFullDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config) BlockTraceResult { // Fetch the block that we aim to reprocess block := api.eth.BlockChain().GetBlockByHash(hash) if block == nil { @@ -1713,7 +371,7 @@ func (api *PrivateDebugAPI) TraceBlockByHash(hash common.Hash, config *vm.Config validated, logs, err := api.traceBlock(block, config) return BlockTraceResult{ Validated: validated, - StructLogs: formatLogs(logs), + StructLogs: ethapi.FormatLogs(logs), Error: formatError(err), } } @@ -1731,7 +389,7 @@ func (t *TraceCollector) AddStructLog(slog vm.StructLog) { } // traceBlock processes the given block but does not save the state. -func (api *PrivateDebugAPI) traceBlock(block *types.Block, config *vm.Config) (bool, []vm.StructLog, error) { +func (api *PrivateFullDebugAPI) traceBlock(block *types.Block, config *vm.Config) (bool, []vm.StructLog, error) { // Validate and reprocess the block var ( blockchain = api.eth.BlockChain() @@ -1763,63 +421,25 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, config *vm.Config) (b return true, collector.traces, nil } -// SetHead rewinds the head of the blockchain to a previous block. -func (api *PrivateDebugAPI) SetHead(number uint64) { - api.eth.BlockChain().SetHead(number) +// callmsg is the message type used for call transations. +type callmsg struct { + addr common.Address + nonce uint64 + to *common.Address + gas, gasPrice *big.Int + value *big.Int + data []byte } -// ExecutionResult groups all structured logs emitted by the EVM -// while replaying a transaction in debug mode as well as the amount of -// gas used and the return value -type ExecutionResult struct { - Gas *big.Int `json:"gas"` - ReturnValue string `json:"returnValue"` - StructLogs []structLogRes `json:"structLogs"` -} - -// structLogRes stores a structured log emitted by the EVM while replaying a -// transaction in debug mode -type structLogRes struct { - Pc uint64 `json:"pc"` - Op string `json:"op"` - Gas *big.Int `json:"gas"` - GasCost *big.Int `json:"gasCost"` - Depth int `json:"depth"` - Error string `json:"error"` - Stack []string `json:"stack"` - Memory []string `json:"memory"` - Storage map[string]string `json:"storage"` -} - -// formatLogs formats EVM returned structured logs for json output -func formatLogs(structLogs []vm.StructLog) []structLogRes { - formattedStructLogs := make([]structLogRes, len(structLogs)) - for index, trace := range structLogs { - formattedStructLogs[index] = structLogRes{ - Pc: trace.Pc, - Op: trace.Op.String(), - Gas: trace.Gas, - GasCost: trace.GasCost, - Depth: trace.Depth, - Error: formatError(trace.Err), - Stack: make([]string, len(trace.Stack)), - Storage: make(map[string]string), - } - - for i, stackValue := range trace.Stack { - formattedStructLogs[index].Stack[i] = fmt.Sprintf("%x", common.LeftPadBytes(stackValue.Bytes(), 32)) - } - - for i := 0; i+32 <= len(trace.Memory); i += 32 { - formattedStructLogs[index].Memory = append(formattedStructLogs[index].Memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) - } - - for i, storageValue := range trace.Storage { - formattedStructLogs[index].Storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) - } - } - return formattedStructLogs -} +// 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 m.nonce } +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 } // formatError formats a Go error into either an empty string or the data content // of the error itself. @@ -1832,7 +452,7 @@ func formatError(err error) string { // TraceTransaction returns the structured logs created during the execution of EVM // and returns them as a JSON object. -func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ExecutionResult, error) { +func (api *PrivateFullDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ethapi.ExecutionResult, error) { if logger == nil { logger = new(vm.LogConfig) } @@ -1862,7 +482,7 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC return nil, fmt.Errorf("sender retrieval failed: %v", err) } msg := callmsg{ - from: stateDb.GetOrNewStateObject(from), + addr: from, to: tx.To(), gas: tx.Gas(), gasPrice: tx.GasPrice(), @@ -1884,90 +504,11 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC if err != nil { return nil, fmt.Errorf("tracing failed: %v", err) } - return &ExecutionResult{ + return ðapi.ExecutionResult{ Gas: gas, ReturnValue: fmt.Sprintf("%x", ret), - StructLogs: formatLogs(vmenv.StructLogs()), + StructLogs: ethapi.FormatLogs(vmenv.StructLogs()), }, nil } return nil, errors.New("database inconsistency") } - -// TraceCall executes a call and returns the amount of gas, created logs and optionally returned values. -func (s *PublicBlockChainAPI) TraceCall(args CallArgs, blockNr rpc.BlockNumber) (*ExecutionResult, error) { - // Fetch the state associated with the block number - stateDb, block, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb) - if stateDb == nil || err != nil { - return nil, err - } - stateDb = stateDb.Copy() - - // Retrieve the account state object to interact with - var from *state.StateObject - if args.From == (common.Address{}) { - accounts := s.am.Accounts() - if len(accounts) == 0 { - from = stateDb.GetOrNewStateObject(common.Address{}) - } else { - from = stateDb.GetOrNewStateObject(accounts[0].Address) - } - } else { - from = stateDb.GetOrNewStateObject(args.From) - } - from.SetBalance(common.MaxBig) - - // Assemble the CALL invocation - msg := callmsg{ - from: from, - to: args.To, - gas: args.Gas.BigInt(), - gasPrice: args.GasPrice.BigInt(), - value: args.Value.BigInt(), - data: common.FromHex(args.Data), - } - if msg.gas.Cmp(common.Big0) == 0 { - msg.gas = big.NewInt(50000000) - } - if msg.gasPrice.Cmp(common.Big0) == 0 { - msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) - } - - // Execute the call and return - vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), vm.Config{ - Debug: true, - }) - gp := new(core.GasPool).AddGas(common.MaxBig) - - ret, gas, err := core.ApplyMessage(vmenv, msg, gp) - return &ExecutionResult{ - Gas: gas, - ReturnValue: fmt.Sprintf("%x", ret), - StructLogs: formatLogs(vmenv.StructLogs()), - }, nil -} - -// PublicNetAPI offers network related RPC methods -type PublicNetAPI struct { - net *p2p.Server - networkVersion int -} - -// NewPublicNetAPI creates a new net API instance. -func NewPublicNetAPI(net *p2p.Server, networkVersion int) *PublicNetAPI { - return &PublicNetAPI{net, networkVersion} -} - -// Listening returns an indication if the node is listening for network connections. -func (s *PublicNetAPI) Listening() bool { - return true // always listening -} - -// PeerCount returns the number of connected peers -func (s *PublicNetAPI) PeerCount() *rpc.HexNumber { - return rpc.NewHexNumber(s.net.PeerCount()) -} - -// Version returns the current ethereum protocol version. -func (s *PublicNetAPI) Version() string { - return fmt.Sprintf("%d", s.networkVersion) -} diff --git a/eth/api_backend.go b/eth/api_backend.go new file mode 100644 index 000000000..9dbebb226 --- /dev/null +++ b/eth/api_backend.go @@ -0,0 +1,201 @@ +// 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 . + +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 +} diff --git a/eth/backend.go b/eth/backend.go index 006523484..f0ca38cbd 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -39,8 +39,10 @@ import ( "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/eth/downloader" "github.com/ethereum/go-ethereum/eth/filters" + "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/logger" "github.com/ethereum/go-ethereum/logger/glog" "github.com/ethereum/go-ethereum/miner" @@ -101,95 +103,86 @@ type Config struct { TestGenesisState ethdb.Database // Genesis state to seed the database with (testing only!) } -type Ethereum struct { - chainConfig *core.ChainConfig +// FullNodeService implements the Ethereum full node service. +type FullNodeService struct { + chainConfig *core.ChainConfig + // Channel for shutting down the service shutdownChan chan bool // Channel for shutting down the ethereum stopDbUpgrade func() // stop chain db sequential key upgrade - - // DB interfaces - chainDb ethdb.Database // Block chain database - dappDb ethdb.Database // Dapp database - // Handlers txPool *core.TxPool txMu sync.Mutex blockchain *core.BlockChain - accountManager *accounts.Manager - pow *ethash.Ethash protocolManager *ProtocolManager - SolcPath string - solc *compiler.Solidity - gpo *GasPriceOracle + // DB interfaces + chainDb ethdb.Database // Block chain database + dappDb ethdb.Database // Dapp database - GpoMinGasPrice *big.Int - GpoMaxGasPrice *big.Int - GpoFullBlockRatio int - GpobaseStepDown int - GpobaseStepUp int - GpobaseCorrectionFactor int + eventMux *event.TypeMux + pow *ethash.Ethash + httpclient *httpclient.HTTPClient + accountManager *accounts.Manager - httpclient *httpclient.HTTPClient + apiBackend *EthApiBackend - eventMux *event.TypeMux - miner *miner.Miner + miner *miner.Miner + Mining bool + MinerThreads int + AutoDAG bool + autodagquit chan bool + etherbase common.Address + solcPath string + solc *compiler.Solidity - Mining bool - MinerThreads int NatSpec bool - AutoDAG bool PowTest bool - autodagquit chan bool - etherbase common.Address netVersionId int - netRPCService *PublicNetAPI + netRPCService *ethapi.PublicNetAPI } -func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { - // Open the chain database and perform any upgrades needed - chainDb, err := ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles) +// New creates a new FullNodeService object (including the +// initialisation of the common Ethereum object) +func New(ctx *node.ServiceContext, config *Config) (*FullNodeService, error) { + chainDb, dappDb, err := CreateDBs(ctx, config) if err != nil { return nil, err } - if db, ok := chainDb.(*ethdb.LDBDatabase); ok { - db.Meter("eth/db/chaindata/") + stopDbUpgrade := upgradeSequentialKeys(chainDb) + if err := SetupGenesisBlock(&chainDb, config); err != nil { + return nil, err } + pow, err := CreatePoW(config) + if err != nil { + return nil, err + } + + eth := &FullNodeService{ + chainDb: chainDb, + dappDb: dappDb, + eventMux: ctx.EventMux, + accountManager: config.AccountManager, + pow: pow, + shutdownChan: make(chan bool), + stopDbUpgrade: stopDbUpgrade, + httpclient: httpclient.New(config.DocRoot), + netVersionId: config.NetworkId, + NatSpec: config.NatSpec, + PowTest: config.PowTest, + etherbase: config.Etherbase, + MinerThreads: config.MinerThreads, + AutoDAG: config.AutoDAG, + solcPath: config.SolcPath, + } + if err := upgradeChainDatabase(chainDb); err != nil { return nil, err } if err := addMipmapBloomBins(chainDb); err != nil { return nil, err } - stopDbUpgrade := upgradeSequentialKeys(chainDb) - dappDb, err := ctx.OpenDatabase("dapp", config.DatabaseCache, config.DatabaseHandles) - if err != nil { - return nil, err - } - if db, ok := dappDb.(*ethdb.LDBDatabase); ok { - db.Meter("eth/db/dapp/") - } glog.V(logger.Info).Infof("Protocol Versions: %v, Network Id: %v", ProtocolVersions, config.NetworkId) - // Load up any custom genesis block if requested - if len(config.Genesis) > 0 { - block, err := core.WriteGenesisBlock(chainDb, strings.NewReader(config.Genesis)) - if err != nil { - return nil, err - } - glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash()) - } - - // Load up a test setup if directly injected - if config.TestGenesisState != nil { - chainDb = config.TestGenesisState - } - if config.TestGenesisBlock != nil { - core.WriteTd(chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty()) - core.WriteBlock(chainDb, config.TestGenesisBlock) - core.WriteCanonicalHash(chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64()) - core.WriteHeadBlockHash(chainDb, config.TestGenesisBlock.Hash()) - } - if !config.SkipBcVersionCheck { bcVersion := core.GetBlockChainVersion(chainDb) if bcVersion != config.BlockChainVersion && bcVersion != 0 { @@ -197,44 +190,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } core.WriteBlockChainVersion(chainDb, config.BlockChainVersion) } - glog.V(logger.Info).Infof("Blockchain DB Version: %d", config.BlockChainVersion) - - eth := &Ethereum{ - shutdownChan: make(chan bool), - stopDbUpgrade: stopDbUpgrade, - chainDb: chainDb, - dappDb: dappDb, - eventMux: ctx.EventMux, - accountManager: config.AccountManager, - etherbase: config.Etherbase, - netVersionId: config.NetworkId, - NatSpec: config.NatSpec, - MinerThreads: config.MinerThreads, - SolcPath: config.SolcPath, - AutoDAG: config.AutoDAG, - PowTest: config.PowTest, - GpoMinGasPrice: config.GpoMinGasPrice, - GpoMaxGasPrice: config.GpoMaxGasPrice, - GpoFullBlockRatio: config.GpoFullBlockRatio, - GpobaseStepDown: config.GpobaseStepDown, - GpobaseStepUp: config.GpobaseStepUp, - GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, - httpclient: httpclient.New(config.DocRoot), - } - switch { - case config.PowTest: - glog.V(logger.Info).Infof("ethash used in test mode") - eth.pow, err = ethash.NewForTesting() - if err != nil { - return nil, err - } - case config.PowShared: - glog.V(logger.Info).Infof("ethash used in shared mode") - eth.pow = ethash.NewShared() - - default: - eth.pow = ethash.New() - } // load the genesis block or write a new one if no genesis // block is prenent in the database. @@ -263,8 +218,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { } return nil, err } - eth.gpo = NewGasPriceOracle(eth) - newPool := core.NewTxPool(eth.chainConfig, eth.EventMux(), eth.blockchain.State, eth.blockchain.GasLimit) eth.txPool = newPool @@ -275,37 +228,87 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) { eth.miner.SetGasPrice(config.GasPrice) eth.miner.SetExtra(config.ExtraData) + gpoParams := &gasprice.GpoParams{ + GpoMinGasPrice: config.GpoMinGasPrice, + GpoMaxGasPrice: config.GpoMaxGasPrice, + GpoFullBlockRatio: config.GpoFullBlockRatio, + GpobaseStepDown: config.GpobaseStepDown, + GpobaseStepUp: config.GpobaseStepUp, + GpobaseCorrectionFactor: config.GpobaseCorrectionFactor, + } + gpo := gasprice.NewGasPriceOracle(eth.blockchain, chainDb, eth.eventMux, gpoParams) + eth.apiBackend = &EthApiBackend{eth, gpo} + return eth, nil } +// CreateDBs creates the chain and dapp databases for an Ethereum service +func CreateDBs(ctx *node.ServiceContext, config *Config) (chainDb, dappDb ethdb.Database, err error) { + // Open the chain database and perform any upgrades needed + chainDb, err = ctx.OpenDatabase("chaindata", config.DatabaseCache, config.DatabaseHandles) + if err != nil { + return nil, nil, err + } + if db, ok := chainDb.(*ethdb.LDBDatabase); ok { + db.Meter("eth/db/chaindata/") + } + + dappDb, err = ctx.OpenDatabase("dapp", config.DatabaseCache, config.DatabaseHandles) + if err != nil { + return nil, nil, err + } + if db, ok := dappDb.(*ethdb.LDBDatabase); ok { + db.Meter("eth/db/dapp/") + } + return +} + +// SetupGenesisBlock initializes the genesis block for an Ethereum service +func SetupGenesisBlock(chainDb *ethdb.Database, config *Config) error { + // Load up any custom genesis block if requested + if len(config.Genesis) > 0 { + block, err := core.WriteGenesisBlock(*chainDb, strings.NewReader(config.Genesis)) + if err != nil { + return err + } + glog.V(logger.Info).Infof("Successfully wrote custom genesis block: %x", block.Hash()) + } + // Load up a test setup if directly injected + if config.TestGenesisState != nil { + *chainDb = config.TestGenesisState + } + if config.TestGenesisBlock != nil { + core.WriteTd(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64(), config.TestGenesisBlock.Difficulty()) + core.WriteBlock(*chainDb, config.TestGenesisBlock) + core.WriteCanonicalHash(*chainDb, config.TestGenesisBlock.Hash(), config.TestGenesisBlock.NumberU64()) + core.WriteHeadBlockHash(*chainDb, config.TestGenesisBlock.Hash()) + } + return nil +} + +// CreatePoW creates the required type of PoW instance for an Ethereum service +func CreatePoW(config *Config) (*ethash.Ethash, error) { + switch { + case config.PowTest: + glog.V(logger.Info).Infof("ethash used in test mode") + return ethash.NewForTesting() + case config.PowShared: + glog.V(logger.Info).Infof("ethash used in shared mode") + return ethash.NewShared(), nil + + default: + return ethash.New(), nil + } +} + // 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 []rpc.API{ +func (s *FullNodeService) APIs() []rpc.API { + return append(ethapi.GetAPIs(s.apiBackend, &s.solcPath, &s.solc), []rpc.API{ { Namespace: "eth", Version: "1.0", - Service: NewPublicEthereumAPI(s), - Public: true, - }, { - Namespace: "eth", - Version: "1.0", - Service: NewPublicAccountAPI(s.accountManager), - Public: true, - }, { - Namespace: "personal", - Version: "1.0", - Service: NewPrivateAccountAPI(s), - Public: false, - }, { - Namespace: "eth", - Version: "1.0", - Service: NewPublicBlockChainAPI(s.chainConfig, s.blockchain, s.miner, s.chainDb, s.gpo, s.eventMux, s.accountManager), - Public: true, - }, { - Namespace: "eth", - Version: "1.0", - Service: NewPublicTransactionPoolAPI(s), + Service: NewPublicFullEthereumAPI(s), Public: true, }, { Namespace: "eth", @@ -322,11 +325,6 @@ func (s *Ethereum) APIs() []rpc.API { Version: "1.0", Service: NewPrivateMinerAPI(s), Public: false, - }, { - Namespace: "txpool", - Version: "1.0", - Service: NewPublicTxPoolAPI(s), - Public: true, }, { Namespace: "eth", Version: "1.0", @@ -335,16 +333,16 @@ func (s *Ethereum) APIs() []rpc.API { }, { Namespace: "admin", Version: "1.0", - Service: NewPrivateAdminAPI(s), + Service: NewPrivateFullAdminAPI(s), }, { Namespace: "debug", Version: "1.0", - Service: NewPublicDebugAPI(s), + Service: NewPublicFullDebugAPI(s), Public: true, }, { Namespace: "debug", Version: "1.0", - Service: NewPrivateDebugAPI(s.chainConfig, s), + Service: NewPrivateFullDebugAPI(s.chainConfig, s), }, { Namespace: "net", Version: "1.0", @@ -355,14 +353,14 @@ func (s *Ethereum) APIs() []rpc.API { Version: "1.0", Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager), }, - } + }...) } -func (s *Ethereum) ResetWithGenesisBlock(gb *types.Block) { +func (s *FullNodeService) ResetWithGenesisBlock(gb *types.Block) { s.blockchain.ResetWithGenesisBlock(gb) } -func (s *Ethereum) Etherbase() (eb common.Address, err error) { +func (s *FullNodeService) Etherbase() (eb common.Address, err error) { eb = s.etherbase if (eb == common.Address{}) { firstAccount, err := s.AccountManager().AccountByIndex(0) @@ -375,46 +373,47 @@ func (s *Ethereum) Etherbase() (eb common.Address, err error) { } // set in js console via admin interface or wrapper from cli flags -func (self *Ethereum) SetEtherbase(etherbase common.Address) { +func (self *FullNodeService) SetEtherbase(etherbase common.Address) { self.etherbase = etherbase self.miner.SetEtherbase(etherbase) } -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 *FullNodeService) StopMining() { s.miner.Stop() } +func (s *FullNodeService) IsMining() bool { return s.miner.Mining() } +func (s *FullNodeService) Miner() *miner.Miner { return s.miner } -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) ChainDb() ethdb.Database { return s.chainDb } -func (s *Ethereum) DappDb() ethdb.Database { return s.dappDb } -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 } +func (s *FullNodeService) AccountManager() *accounts.Manager { return s.accountManager } +func (s *FullNodeService) BlockChain() *core.BlockChain { return s.blockchain } +func (s *FullNodeService) TxPool() *core.TxPool { return s.txPool } +func (s *FullNodeService) EventMux() *event.TypeMux { return s.eventMux } +func (s *FullNodeService) Pow() *ethash.Ethash { return s.pow } +func (s *FullNodeService) ChainDb() ethdb.Database { return s.chainDb } +func (s *FullNodeService) DappDb() ethdb.Database { return s.dappDb } +func (s *FullNodeService) IsListening() bool { return true } // Always listening +func (s *FullNodeService) EthVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } +func (s *FullNodeService) NetVersion() int { return s.netVersionId } +func (s *FullNodeService) Downloader() *downloader.Downloader { return s.protocolManager.downloader } // Protocols implements node.Service, returning all the currently configured // network protocols to start. -func (s *Ethereum) Protocols() []p2p.Protocol { +func (s *FullNodeService) Protocols() []p2p.Protocol { return s.protocolManager.SubProtocols } // Start implements node.Service, starting all internal goroutines needed by the -// Ethereum protocol implementation. -func (s *Ethereum) Start(srvr *p2p.Server) error { +// FullNodeService protocol implementation. +func (s *FullNodeService) Start(srvr *p2p.Server) error { + s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.NetVersion()) if s.AutoDAG { s.StartAutoDAG() } s.protocolManager.Start() - s.netRPCService = NewPublicNetAPI(srvr, s.NetVersion()) return nil } // Stop implements node.Service, terminating all internal goroutines used by the // Ethereum protocol. -func (s *Ethereum) Stop() error { +func (s *FullNodeService) Stop() error { if s.stopDbUpgrade != nil { s.stopDbUpgrade() } @@ -434,7 +433,7 @@ func (s *Ethereum) Stop() error { } // This function will wait for a shutdown and resumes main thread execution -func (s *Ethereum) WaitForShutdown() { +func (s *FullNodeService) WaitForShutdown() { <-s.shutdownChan } @@ -447,7 +446,7 @@ func (s *Ethereum) WaitForShutdown() { // stop any number of times. // For any more sophisticated pattern of DAG generation, use CLI subcommand // makedag -func (self *Ethereum) StartAutoDAG() { +func (self *FullNodeService) StartAutoDAG() { if self.autodagquit != nil { return // already started } @@ -493,7 +492,7 @@ func (self *Ethereum) StartAutoDAG() { } // stopAutoDAG stops automatic DAG pregeneration by quitting the loop -func (self *Ethereum) StopAutoDAG() { +func (self *FullNodeService) StopAutoDAG() { if self.autodagquit != nil { close(self.autodagquit) self.autodagquit = nil @@ -503,25 +502,10 @@ func (self *Ethereum) StopAutoDAG() { // HTTPClient returns the light http client used for fetching offchain docs // (natspec, source for verification) -func (self *Ethereum) HTTPClient() *httpclient.HTTPClient { +func (self *FullNodeService) HTTPClient() *httpclient.HTTPClient { return self.httpclient } -func (self *Ethereum) Solc() (*compiler.Solidity, error) { - var err error - if self.solc == nil { - self.solc, err = compiler.New(self.SolcPath) - } - return self.solc, err -} - -// set in js console via admin interface or wrapper from cli flags -func (self *Ethereum) SetSolc(solcPath string) (*compiler.Solidity, error) { - self.SolcPath = solcPath - self.solc = nil - return self.Solc() -} - // 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 index fb7f29f60..7eb15ca1b 100644 --- a/eth/bind.go +++ b/eth/bind.go @@ -21,8 +21,10 @@ import ( "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 @@ -33,38 +35,44 @@ import ( // 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 *PublicEthereumAPI // Wrapper around the Ethereum object to access metadata - bcapi *PublicBlockChainAPI // Wrapper around the blockchain to access chain data - txapi *PublicTransactionPoolAPI // Wrapper around the transaction pool to access transaction data + 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(eth *Ethereum) *ContractBackend { +func NewContractBackend(eth *FullNodeService) *ContractBackend { return &ContractBackend{ - eapi: NewPublicEthereumAPI(eth), - bcapi: NewPublicBlockChainAPI(eth.chainConfig, eth.blockchain, eth.miner, eth.chainDb, eth.gpo, eth.eventMux, eth.accountManager), - txapi: NewPublicTransactionPoolAPI(eth), + eapi: ethapi.NewPublicEthereumAPI(eth.apiBackend, nil, nil), + bcapi: ethapi.NewPublicBlockChainAPI(eth.apiBackend), + txapi: ethapi.NewPublicTransactionPoolAPI(eth.apiBackend), } } // HasCode implements bind.ContractVerifier.HasCode by retrieving any code associated // 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 if pending { 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 } // 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) 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 - args := CallArgs{ + args := ethapi.CallArgs{ To: &contract, Data: common.ToHex(data), } @@ -73,21 +81,27 @@ func (b *ContractBackend) ContractCall(contract common.Address, data []byte, pen block = rpc.PendingBlockNumber } // 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 } // PendingAccountNonce implements bind.ContractTransactor retrieving the current // pending nonce associated with an account. -func (b *ContractBackend) PendingAccountNonce(account common.Address) (uint64, error) { - out, err := b.txapi.GetTransactionCount(account, rpc.PendingBlockNumber) +func (b *ContractBackend) PendingAccountNonce(ctx context.Context, account common.Address) (uint64, error) { + if ctx == nil { + ctx = context.Background() + } + 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() (*big.Int, error) { - return b.eapi.GasPrice(), nil +func (b *ContractBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) { + if ctx == nil { + ctx = context.Background() + } + return b.eapi.GasPrice(ctx) } // EstimateGasLimit implements bind.ContractTransactor triing to estimate the gas @@ -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 // 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) EstimateGasLimit(sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { - out, err := b.bcapi.EstimateGas(CallArgs{ +func (b *ContractBackend) EstimateGasLimit(ctx context.Context, sender common.Address, contract *common.Address, value *big.Int, data []byte) (*big.Int, error) { + if ctx == nil { + ctx = context.Background() + } + out, err := b.bcapi.EstimateGas(ctx, ethapi.CallArgs{ From: sender, To: contract, Value: *rpc.NewHexNumber(value), @@ -107,8 +124,11 @@ func (b *ContractBackend) EstimateGasLimit(sender common.Address, contract *comm // SendTransaction implements bind.ContractTransactor injects the transaction // 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) - _, err := b.txapi.SendRawTransaction(common.ToHex(raw)) + _, err := b.txapi.SendRawTransaction(ctx, common.ToHex(raw)) return err } diff --git a/eth/cpu_mining.go b/eth/cpu_mining.go index 3469d394e..143b9c98a 100644 --- a/eth/cpu_mining.go +++ b/eth/cpu_mining.go @@ -28,7 +28,7 @@ import ( 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() if err != nil { err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) diff --git a/eth/gasprice.go b/eth/gasprice/gasprice.go similarity index 75% rename from eth/gasprice.go rename to eth/gasprice/gasprice.go index ef203f8fe..eb2df4a96 100644 --- a/eth/gasprice.go +++ b/eth/gasprice/gasprice.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . -package eth +package gasprice import ( "math/big" @@ -23,6 +23,8 @@ import ( "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/logger" "github.com/ethereum/go-ethereum/logger/glog" ) @@ -39,10 +41,22 @@ type blockPriceInfo struct { 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 // blocks. type GasPriceOracle struct { - eth *Ethereum + chain *core.BlockChain + db ethdb.Database + evmux *event.TypeMux + params *GpoParams initOnce sync.Once minPrice *big.Int lastBaseMutex sync.Mutex @@ -55,17 +69,20 @@ type GasPriceOracle struct { } // NewGasPriceOracle returns a new oracle. -func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle { - minprice := eth.GpoMinGasPrice +func NewGasPriceOracle(chain *core.BlockChain, db ethdb.Database, evmux *event.TypeMux, params *GpoParams) *GasPriceOracle { + minprice := params.GpoMinGasPrice if minprice == nil { minprice = big.NewInt(gpoDefaultMinGasPrice) } minbase := new(big.Int).Mul(minprice, big.NewInt(100)) - if eth.GpobaseCorrectionFactor > 0 { - minbase = minbase.Div(minbase, big.NewInt(int64(eth.GpobaseCorrectionFactor))) + if params.GpobaseCorrectionFactor > 0 { + minbase = minbase.Div(minbase, big.NewInt(int64(params.GpobaseCorrectionFactor))) } return &GasPriceOracle{ - eth: eth, + chain: chain, + db: db, + evmux: evmux, + params: params, blocks: make(map[uint64]*blockPriceInfo), minBase: minbase, minPrice: minprice, @@ -75,14 +92,14 @@ func NewGasPriceOracle(eth *Ethereum) *GasPriceOracle { func (gpo *GasPriceOracle) init() { gpo.initOnce.Do(func() { - gpo.processPastBlocks(gpo.eth.BlockChain()) + gpo.processPastBlocks() go gpo.listenLoop() }) } -func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { +func (self *GasPriceOracle) processPastBlocks() { last := int64(-1) - cblock := chain.CurrentBlock() + cblock := self.chain.CurrentBlock() if cblock != nil { last = int64(cblock.NumberU64()) } @@ -92,7 +109,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { } self.firstProcessed = uint64(first) for i := first; i <= last; i++ { - block := chain.GetBlockByNumber(uint64(i)) + block := self.chain.GetBlockByNumber(uint64(i)) if block != nil { self.processBlock(block) } @@ -101,7 +118,7 @@ func (self *GasPriceOracle) processPastBlocks(chain *core.BlockChain) { } func (self *GasPriceOracle) listenLoop() { - events := self.eth.EventMux().Subscribe(core.ChainEvent{}, core.ChainSplitEvent{}) + events := self.evmux.Subscribe(core.ChainEvent{}, core.ChainSplitEvent{}) defer events.Unsubscribe() for event := range events.Chan() { @@ -136,9 +153,9 @@ func (self *GasPriceOracle) processBlock(block *types.Block) { } if lastBase.Cmp(lp) < 0 { - corr = self.eth.GpobaseStepUp + corr = self.params.GpobaseStepUp } else { - corr = -self.eth.GpobaseStepDown + corr = -self.params.GpobaseStepDown } crand := int64(corr * (900 + rand.Intn(201))) @@ -159,14 +176,14 @@ func (self *GasPriceOracle) processBlock(block *types.Block) { self.lastBase = newBase 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 func (self *GasPriceOracle) lowestPrice(block *types.Block) *big.Int { 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 cgu := receipts[len(receipts)-1].CumulativeGasUsed; cgu != nil { gasUsed = receipts[len(receipts)-1].CumulativeGasUsed @@ -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(), - 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 return big.NewInt(0) } @@ -201,12 +218,12 @@ func (self *GasPriceOracle) SuggestPrice() *big.Int { price := new(big.Int).Set(self.lastBase) 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)) if price.Cmp(self.minPrice) < 0 { price.Set(self.minPrice) - } else if self.eth.GpoMaxGasPrice != nil && price.Cmp(self.eth.GpoMaxGasPrice) > 0 { - price.Set(self.eth.GpoMaxGasPrice) + } else if self.params.GpoMaxGasPrice != nil && price.Cmp(self.params.GpoMaxGasPrice) > 0 { + price.Set(self.params.GpoMaxGasPrice) } return price } diff --git a/eth/gpu_mining.go b/eth/gpu_mining.go index 12fa74601..0208bba80 100644 --- a/eth/gpu_mining.go +++ b/eth/gpu_mining.go @@ -33,7 +33,7 @@ import ( "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() if err != nil { err = fmt.Errorf("Cannot start mining without etherbase address: %v", err) diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go new file mode 100644 index 000000000..6e888fc93 --- /dev/null +++ b/internal/ethapi/api.go @@ -0,0 +1,1542 @@ +// 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 ( + "bytes" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "math/big" + "strings" + "sync" + "time" + + "github.com/ethereum/ethash" + "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/crypto" + "github.com/ethereum/go-ethereum/ethdb" + "github.com/ethereum/go-ethereum/logger" + "github.com/ethereum/go-ethereum/logger/glog" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/rlp" + "github.com/ethereum/go-ethereum/rpc" + "github.com/syndtr/goleveldb/leveldb" + "golang.org/x/net/context" +) + +const defaultGas = uint64(90000) + +// 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 { + b Backend + solcPath *string + solc **compiler.Solidity +} + +// NewPublicEthereumAPI creates a new Etheruem protocol API. +func NewPublicEthereumAPI(b Backend, solcPath *string, solc **compiler.Solidity) *PublicEthereumAPI { + return &PublicEthereumAPI{b, solcPath, solc} +} + +// GasPrice returns a suggestion for a gas price. +func (s *PublicEthereumAPI) GasPrice(ctx context.Context) (*big.Int, error) { + return s.b.SuggestPrice(ctx) +} + +func (s *PublicEthereumAPI) getSolc() (*compiler.Solidity, error) { + var err error + solc := *s.solc + if solc == nil { + solc, err = compiler.New(*s.solcPath) + } + return solc, err +} + +// GetCompilers returns the collection of available smart contract compilers +func (s *PublicEthereumAPI) GetCompilers() ([]string, error) { + solc, err := s.getSolc() + if err == nil && solc != nil { + return []string{"Solidity"}, nil + } + + return []string{}, nil +} + +// CompileSolidity compiles the given solidity source +func (s *PublicEthereumAPI) CompileSolidity(source string) (map[string]*compiler.Contract, error) { + solc, err := s.getSolc() + if err != nil { + return nil, err + } + + if solc == nil { + return nil, errors.New("solc (solidity compiler) not found") + } + + return solc.Compile(source) +} + +// ProtocolVersion returns the current Ethereum protocol version this node supports +func (s *PublicEthereumAPI) ProtocolVersion() *rpc.HexNumber { + return rpc.NewHexNumber(s.b.ProtocolVersion()) +} + +// Syncing returns false in case the node is currently not syncing with the network. It can be up to date or has not +// yet received the latest block headers from its pears. In case it is synchronizing: +// - startingBlock: block number this node started to synchronise from +// - currentBlock: block number this node is currently importing +// - 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) { + origin, current, height, pulled, known := s.b.Downloader().Progress() + + // Return not syncing if the synchronisation already completed + if current >= height { + return false, nil + } + // Otherwise gather the block sync stats + return map[string]interface{}{ + "startingBlock": rpc.NewHexNumber(origin), + "currentBlock": rpc.NewHexNumber(current), + "highestBlock": rpc.NewHexNumber(height), + "pulledStates": rpc.NewHexNumber(pulled), + "knownStates": rpc.NewHexNumber(known), + }, 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, batches := range pending { + dump := make(map[string][]*RPCTransaction) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx)) + } + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, batches := range queue { + dump := make(map[string][]*RPCTransaction) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[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, batches := range pending { + dump := make(map[string][]string) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[nonce], format(tx)) + } + } + content["pending"][account.Hex()] = dump + } + // Flatten the queued transactions + for account, batches := range queue { + dump := make(map[string][]string) + for nonce, txs := range batches { + nonce := fmt.Sprintf("%d", nonce) + for _, tx := range txs { + dump[nonce] = append(dump[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 { + am *accounts.Manager +} + +// NewPublicAccountAPI creates a new PublicAccountAPI. +func NewPublicAccountAPI(am *accounts.Manager) *PublicAccountAPI { + return &PublicAccountAPI{am: am} +} + +// Accounts returns the collection of accounts this node manages +func (s *PublicAccountAPI) Accounts() []accounts.Account { + return s.am.Accounts() +} + +// PrivateAccountAPI provides an API to access accounts managed by this node. +// It offers methods to create, (un)lock en list accounts. Some methods accept +// passwords and are therefore considered private by default. +type PrivateAccountAPI struct { + am *accounts.Manager + b Backend +} + +// NewPrivateAccountAPI create a new PrivateAccountAPI. +func NewPrivateAccountAPI(b Backend) *PrivateAccountAPI { + return &PrivateAccountAPI{ + am: b.AccountManager(), + b: b, + } +} + +// ListAccounts will return a list of addresses for accounts this node manages. +func (s *PrivateAccountAPI) ListAccounts() []common.Address { + accounts := s.am.Accounts() + addresses := make([]common.Address, len(accounts)) + for i, acc := range accounts { + addresses[i] = acc.Address + } + return addresses +} + +// NewAccount will create a new account and returns the address for the new account. +func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error) { + acc, err := s.am.NewAccount(password) + if err == nil { + return acc.Address, nil + } + return common.Address{}, err +} + +// ImportRawKey stores the given hex encoded ECDSA key into the key directory, +// encrypting it with the passphrase. +func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) { + hexkey, err := hex.DecodeString(privkey) + if err != nil { + return common.Address{}, err + } + + acc, err := s.am.ImportECDSA(crypto.ToECDSA(hexkey), password) + return acc.Address, err +} + +// UnlockAccount will unlock the account associated with the given address with +// the given password for duration seconds. If duration is nil it will use a +// default of 300 seconds. It returns an indication if the account was unlocked. +func (s *PrivateAccountAPI) UnlockAccount(addr common.Address, password string, duration *rpc.HexNumber) (bool, error) { + if duration == nil { + duration = rpc.NewHexNumber(300) + } + a := accounts.Account{Address: addr} + d := time.Duration(duration.Int64()) * time.Second + if err := s.am.TimedUnlock(a, password, d); err != nil { + return false, err + } + return true, nil +} + +// LockAccount will lock the account associated with the given address when it's unlocked. +func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool { + return s.am.Lock(addr) == nil +} + +// SignAndSendTransaction will create a transaction from the given arguments and +// tries to sign it with the key associated with args.To. If the given passwd isn't +// able to decrypt the key it fails. +func (s *PrivateAccountAPI) SignAndSendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) { + var err error + args, err = prepareSendTxArgs(ctx, args, s.b) + if err != nil { + return common.Hash{}, err + } + + if args.Nonce == nil { + nonce, err := s.b.GetPoolNonce(ctx, args.From) + if err != nil { + return common.Hash{}, err + } + args.Nonce = rpc.NewHexNumber(nonce) + } + + var tx *types.Transaction + if args.To == nil { + tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) + } else { + tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) + } + + signature, err := s.am.SignWithPassphrase(args.From, passwd, tx.SigHash().Bytes()) + if err != nil { + return common.Hash{}, err + } + + return submitTransaction(ctx, s.b, tx, signature) +} + +// PublicBlockChainAPI provides an API to access the Ethereum blockchain. +// It offers only methods that operate on public data that is freely available to anyone. +type PublicBlockChainAPI struct { + b Backend + muNewBlockSubscriptions sync.Mutex // protects newBlocksSubscriptions + newBlockSubscriptions map[string]func(core.ChainEvent) error // callbacks for new block subscriptions +} + +// NewPublicBlockChainAPI creates a new Etheruem blockchain API. +func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI { + api := &PublicBlockChainAPI{ + b: b, + newBlockSubscriptions: make(map[string]func(core.ChainEvent) error), + } + + go api.subscriptionLoop() + + return api +} + +// subscriptionLoop reads events from the global event mux and creates notifications for the matched subscriptions. +func (s *PublicBlockChainAPI) subscriptionLoop() { + sub := s.b.EventMux().Subscribe(core.ChainEvent{}) + for event := range sub.Chan() { + if chainEvent, ok := event.Data.(core.ChainEvent); ok { + s.muNewBlockSubscriptions.Lock() + for id, notifyOf := range s.newBlockSubscriptions { + if notifyOf(chainEvent) == rpc.ErrNotificationNotFound { + delete(s.newBlockSubscriptions, id) + } + } + s.muNewBlockSubscriptions.Unlock() + } + } +} + +// BlockNumber returns the block number of the chain head. +func (s *PublicBlockChainAPI) BlockNumber() *big.Int { + return s.b.HeaderByNumber(rpc.LatestBlockNumber).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(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) + if block != nil { + response, err := s.rpcOutputBlock(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"} { + response[field] = nil + } + } + return response, err + } + return nil, err +} + +// 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) + if block != nil { + return s.rpcOutputBlock(block, true, fullTx) + } + return nil, err +} + +// 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) + if block != nil { + uncles := block.Uncles() + if index.Int() < 0 || index.Int() >= len(uncles) { + glog.V(logger.Debug).Infof("uncle block on index %d not found for block #%d", index.Int(), blockNr) + return nil, nil + } + block = types.NewBlockWithHeader(uncles[index.Int()]) + return s.rpcOutputBlock(block, false, false) + } + return nil, err +} + +// 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) + if block != nil { + uncles := block.Uncles() + if index.Int() < 0 || index.Int() >= len(uncles) { + glog.V(logger.Debug).Infof("uncle block on index %d not found for block %s", index.Int(), blockHash.Hex()) + return nil, nil + } + block = types.NewBlockWithHeader(uncles[index.Int()]) + return s.rpcOutputBlock(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 { + return rpc.NewHexNumber(len(block.Uncles())) + } + return nil +} + +// 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 { + return rpc.NewHexNumber(len(block.Uncles())) + } + return nil +} + +// NewBlocksArgs allows the user to specify if the returned block should include transactions and in which format. +type NewBlocksArgs struct { + IncludeTransactions bool `json:"includeTransactions"` + TransactionDetails bool `json:"transactionDetails"` +} + +// NewBlocks triggers a new block event each time a block is appended to the chain. It accepts an argument which allows +// the caller to specify whether the output should contain transactions and in what format. +func (s *PublicBlockChainAPI) NewBlocks(ctx context.Context, args NewBlocksArgs) (rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return nil, rpc.ErrNotificationsUnsupported + } + + // create a subscription that will remove itself when unsubscribed/cancelled + subscription, err := notifier.NewSubscription(func(subId string) { + s.muNewBlockSubscriptions.Lock() + delete(s.newBlockSubscriptions, subId) + s.muNewBlockSubscriptions.Unlock() + }) + + if err != nil { + return nil, err + } + + // add a callback that is called on chain events which will format the block and notify the client + s.muNewBlockSubscriptions.Lock() + s.newBlockSubscriptions[subscription.ID()] = func(e core.ChainEvent) error { + notification, err := s.rpcOutputBlock(e.Block, args.IncludeTransactions, args.TransactionDetails) + if err == nil { + return subscription.Notify(notification) + } + glog.V(logger.Warn).Info("unable to format block %v\n", err) + return nil + } + s.muNewBlockSubscriptions.Unlock() + return subscription, nil +} + +// 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(blockNr) + if state == nil || err != nil { + return "", err + } + res, err := state.GetCode(ctx, address) + if len(res) == 0 || err != nil { // backwards compatibility + return "0x", err + } + return common.ToHex(res), 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(blockNr) + if state == nil || err != nil { + return "0x", err + } + res, err := state.GetState(ctx, address, common.HexToHash(key)) + if err != nil { + return "0x", err + } + return res.Hex(), nil +} + +// callmsg is the message type used for call transations. +type callmsg struct { + addr common.Address + nonce uint64 + 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 m.nonce } +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"` + To *common.Address `json:"to"` + Gas rpc.HexNumber `json:"gas"` + GasPrice rpc.HexNumber `json:"gasPrice"` + Value rpc.HexNumber `json:"value"` + Data string `json:"data"` +} + +func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, *big.Int, error) { + state, header, err := s.b.StateAndHeaderByNumber(blockNr) + if state == nil || err != nil { + return "0x", common.Big0, err + } + + // Set the account address to interact with + var addr common.Address + if args.From == (common.Address{}) { + accounts := s.b.AccountManager().Accounts() + if len(accounts) == 0 { + addr = common.Address{} + } else { + addr = accounts[0].Address + } + } else { + addr = args.From + } + + // Assemble the CALL invocation + msg := callmsg{ + addr: addr, + to: args.To, + gas: args.Gas.BigInt(), + gasPrice: args.GasPrice.BigInt(), + value: args.Value.BigInt(), + data: common.FromHex(args.Data), + } + + if msg.gas.Cmp(common.Big0) == 0 { + msg.gas = big.NewInt(50000000) + } + + if msg.gasPrice.Cmp(common.Big0) == 0 { + msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) + } + + // Execute the call and return + vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) + if err != nil { + return "0x", common.Big0, err + } + gp := new(core.GasPool).AddGas(common.MaxBig) + res, gas, err := core.ApplyMessage(vmenv, msg, gp) + if err := vmError(); err != nil { + return "0x", common.Big0, err + } + if len(res) == 0 { // backwards compatability + return "0x", gas, err + } + return common.ToHex(res), gas, err +} + +// Call executes the given transaction on the state for the given block number. +// It doesn't make and changes in the state/blockchain and is usefull to execute and retrieve values. +func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (string, error) { + result, _, err := s.doCall(ctx, args, blockNr) + return result, err +} + +// 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) + return rpc.NewHexNumber(gas), err +} + +// ExecutionResult groups all structured logs emitted by the EVM +// while replaying a transaction in debug mode as well as the amount of +// gas used and the return value +type ExecutionResult struct { + Gas *big.Int `json:"gas"` + ReturnValue string `json:"returnValue"` + StructLogs []StructLogRes `json:"structLogs"` +} + +// StructLogRes stores a structured log emitted by the EVM while replaying a +// transaction in debug mode +type StructLogRes struct { + Pc uint64 `json:"pc"` + Op string `json:"op"` + Gas *big.Int `json:"gas"` + GasCost *big.Int `json:"gasCost"` + Depth int `json:"depth"` + Error error `json:"error"` + Stack []string `json:"stack"` + Memory []string `json:"memory"` + Storage map[string]string `json:"storage"` +} + +// formatLogs formats EVM returned structured logs for json output +func FormatLogs(structLogs []vm.StructLog) []StructLogRes { + formattedStructLogs := make([]StructLogRes, len(structLogs)) + for index, trace := range structLogs { + formattedStructLogs[index] = StructLogRes{ + Pc: trace.Pc, + Op: trace.Op.String(), + Gas: trace.Gas, + GasCost: trace.GasCost, + Depth: trace.Depth, + Error: trace.Err, + Stack: make([]string, len(trace.Stack)), + Storage: make(map[string]string), + } + + for i, stackValue := range trace.Stack { + formattedStructLogs[index].Stack[i] = fmt.Sprintf("%x", common.LeftPadBytes(stackValue.Bytes(), 32)) + } + + for i := 0; i+32 <= len(trace.Memory); i += 32 { + formattedStructLogs[index].Memory = append(formattedStructLogs[index].Memory, fmt.Sprintf("%x", trace.Memory[i:i+32])) + } + + for i, storageValue := range trace.Storage { + formattedStructLogs[index].Storage[fmt.Sprintf("%x", i)] = fmt.Sprintf("%x", storageValue) + } + } + return formattedStructLogs +} + +// TraceCall executes a call and returns the amount of gas, created logs and optionally returned values. +func (s *PublicBlockChainAPI) TraceCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (*ExecutionResult, error) { + state, header, err := s.b.StateAndHeaderByNumber(blockNr) + if state == nil || err != nil { + return nil, err + } + + var addr common.Address + if args.From == (common.Address{}) { + accounts := s.b.AccountManager().Accounts() + if len(accounts) == 0 { + addr = common.Address{} + } else { + addr = accounts[0].Address + } + } else { + addr = args.From + } + + // Assemble the CALL invocation + msg := callmsg{ + addr: addr, + to: args.To, + gas: args.Gas.BigInt(), + gasPrice: args.GasPrice.BigInt(), + value: args.Value.BigInt(), + data: common.FromHex(args.Data), + } + + if msg.gas.Cmp(common.Big0) == 0 { + msg.gas = big.NewInt(50000000) + } + + if msg.gasPrice.Cmp(common.Big0) == 0 { + msg.gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon) + } + + // Execute the call and return + vmenv, vmError, err := s.b.GetVMEnv(ctx, msg, state, header) + if err != nil { + return nil, err + } + gp := new(core.GasPool).AddGas(common.MaxBig) + ret, gas, err := core.ApplyMessage(vmenv, msg, gp) + if err := vmError(); err != nil { + return nil, err + } + return &ExecutionResult{ + Gas: gas, + ReturnValue: fmt.Sprintf("%x", ret), + StructLogs: FormatLogs(vmenv.StructLogs()), + }, nil +} + +// 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) { + fields := map[string]interface{}{ + "number": rpc.NewHexNumber(b.Number()), + "hash": b.Hash(), + "parentHash": b.ParentHash(), + "nonce": b.Header().Nonce, + "sha3Uncles": b.UncleHash(), + "logsBloom": b.Bloom(), + "stateRoot": b.Root(), + "miner": b.Coinbase(), + "difficulty": rpc.NewHexNumber(b.Difficulty()), + "totalDifficulty": rpc.NewHexNumber(s.b.GetTd(b.Hash())), + "extraData": fmt.Sprintf("0x%x", b.Extra()), + "size": rpc.NewHexNumber(b.Size().Int64()), + "gasLimit": rpc.NewHexNumber(b.GasLimit()), + "gasUsed": rpc.NewHexNumber(b.GasUsed()), + "timestamp": rpc.NewHexNumber(b.Time()), + "transactionsRoot": b.TxHash(), + "receiptRoot": b.ReceiptHash(), + } + + 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 + } + } + fields["transactions"] = transactions + } + + uncles := b.Uncles() + uncleHashes := make([]common.Hash, len(uncles)) + for i, uncle := range uncles { + uncleHashes[i] = uncle.Hash() + } + fields["uncles"] = uncleHashes + + return fields, nil +} + +// RPCTransaction represents a transaction that will serialize to the RPC representation of a transaction +type RPCTransaction struct { + BlockHash common.Hash `json:"blockHash"` + BlockNumber *rpc.HexNumber `json:"blockNumber"` + From common.Address `json:"from"` + Gas *rpc.HexNumber `json:"gas"` + GasPrice *rpc.HexNumber `json:"gasPrice"` + Hash common.Hash `json:"hash"` + Input string `json:"input"` + Nonce *rpc.HexNumber `json:"nonce"` + To *common.Address `json:"to"` + TransactionIndex *rpc.HexNumber `json:"transactionIndex"` + Value *rpc.HexNumber `json:"value"` +} + +// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation +func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { + from, _ := tx.FromFrontier() + + return &RPCTransaction{ + From: from, + Gas: rpc.NewHexNumber(tx.Gas()), + GasPrice: rpc.NewHexNumber(tx.GasPrice()), + Hash: tx.Hash(), + Input: fmt.Sprintf("0x%x", tx.Data()), + Nonce: rpc.NewHexNumber(tx.Nonce()), + To: tx.To(), + Value: rpc.NewHexNumber(tx.Value()), + } +} + +// newRPCTransaction returns a transaction that will serialize to the RPC representation. +func newRPCTransactionFromBlockIndex(b *types.Block, txIndex int) (*RPCTransaction, error) { + if txIndex >= 0 && txIndex < len(b.Transactions()) { + tx := b.Transactions()[txIndex] + from, err := tx.FromFrontier() + if err != nil { + return nil, err + } + + 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: fmt.Sprintf("0x%x", tx.Data()), + Nonce: rpc.NewHexNumber(tx.Nonce()), + To: tx.To(), + TransactionIndex: rpc.NewHexNumber(txIndex), + Value: rpc.NewHexNumber(tx.Value()), + }, nil + } + + 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 +} + +// PublicTransactionPoolAPI exposes methods for the RPC interface +type PublicTransactionPoolAPI struct { + b Backend + muPendingTxSubs sync.Mutex + pendingTxSubs map[string]rpc.Subscription +} + +// NewPublicTransactionPoolAPI creates a new RPC service with methods specific for the transaction pool. +func NewPublicTransactionPoolAPI(b Backend) *PublicTransactionPoolAPI { + api := &PublicTransactionPoolAPI{ + b: b, + pendingTxSubs: make(map[string]rpc.Subscription), + } + + go api.subscriptionLoop() + + return api +} + +// subscriptionLoop listens for events on the global event mux and creates notifications for subscriptions. +func (s *PublicTransactionPoolAPI) subscriptionLoop() { + sub := s.b.EventMux().Subscribe(core.TxPreEvent{}) + for event := range sub.Chan() { + tx := event.Data.(core.TxPreEvent) + if from, err := tx.Tx.FromFrontier(); err == nil { + if s.b.AccountManager().HasAddress(from) { + s.muPendingTxSubs.Lock() + for id, sub := range s.pendingTxSubs { + if sub.Notify(tx.Tx.Hash()) == rpc.ErrNotificationNotFound { + delete(s.pendingTxSubs, id) + } + } + s.muPendingTxSubs.Unlock() + } + } + } +} + +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 { + return rpc.NewHexNumber(len(block.Transactions())) + } + return nil +} + +// 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())) + } + 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 { + return newRPCTransactionFromBlockIndex(block, index.Int()) + } + return nil, 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 { + return newRPCTransactionFromBlockIndex(block, index.Int()) + } + return nil, nil +} + +// 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(blockNr) + if state == nil || err != nil { + return nil, err + } + nonce, err := state.GetNonce(ctx, address) + if err != nil { + return nil, err + } + return rpc.NewHexNumber(nonce), nil +} + +// getTransactionBlockData fetches the meta data for the given transaction from the chain database. This is useful to +// retrieve block information for a hash. It returns the block hash, block index and transaction index. +func getTransactionBlockData(chainDb ethdb.Database, txHash common.Hash) (common.Hash, uint64, uint64, error) { + var txBlock struct { + BlockHash common.Hash + BlockIndex uint64 + Index uint64 + } + + blockData, err := chainDb.Get(append(txHash.Bytes(), 0x0001)) + if err != nil { + return common.Hash{}, uint64(0), uint64(0), err + } + + reader := bytes.NewReader(blockData) + if err = rlp.Decode(reader, &txBlock); err != nil { + return common.Hash{}, uint64(0), uint64(0), err + } + + return txBlock.BlockHash, txBlock.BlockIndex, txBlock.Index, nil +} + +// 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 { + return nil, nil + } + + if isPending { + return newRPCPendingTransaction(tx), nil + } + + blockHash, _, _, err := getTransactionBlockData(s.b.ChainDb(), txHash) + if err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } + + if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { + return newRPCTransaction(block, txHash) + } + + return nil, nil +} + +// 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) + if err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } + + txBlock, blockIndex, index, err := getTransactionBlockData(s.b.ChainDb(), txHash) + if err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } + + from, err := tx.FromFrontier() + if err != nil { + glog.V(logger.Debug).Infof("%v\n", err) + return nil, nil + } + + fields := map[string]interface{}{ + "root": common.Bytes2Hex(receipt.PostState), + "blockHash": txBlock, + "blockNumber": rpc.NewHexNumber(blockIndex), + "transactionHash": txHash, + "transactionIndex": rpc.NewHexNumber(index), + "from": from, + "to": tx.To(), + "gasUsed": rpc.NewHexNumber(receipt.GasUsed), + "cumulativeGasUsed": rpc.NewHexNumber(receipt.CumulativeGasUsed), + "contractAddress": nil, + "logs": receipt.Logs, + } + + if receipt.Logs == nil { + fields["logs"] = []vm.Logs{} + } + + // If the ContractAddress is 20 0x0 bytes, assume it is not a contract creation + if bytes.Compare(receipt.ContractAddress.Bytes(), bytes.Repeat([]byte{0}, 20)) != 0 { + fields["contractAddress"] = receipt.ContractAddress + } + + return fields, nil +} + +// 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) { + signature, err := s.b.AccountManager().Sign(addr, tx.SigHash().Bytes()) + if err != nil { + return nil, err + } + return tx.WithSignature(signature) +} + +// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool. +type SendTxArgs struct { + From common.Address `json:"from"` + To *common.Address `json:"to"` + Gas *rpc.HexNumber `json:"gas"` + GasPrice *rpc.HexNumber `json:"gasPrice"` + Value *rpc.HexNumber `json:"value"` + Data string `json:"data"` + Nonce *rpc.HexNumber `json:"nonce"` +} + +// prepareSendTxArgs is a helper function that fills in default values for unspecified tx fields. +func prepareSendTxArgs(ctx context.Context, args SendTxArgs, b Backend) (SendTxArgs, error) { + if args.Gas == nil { + args.Gas = rpc.NewHexNumber(defaultGas) + } + if args.GasPrice == nil { + price, err := b.SuggestPrice(ctx) + if err != nil { + return args, err + } + args.GasPrice = rpc.NewHexNumber(price) + } + if args.Value == nil { + args.Value = rpc.NewHexNumber(0) + } + return args, nil +} + +// 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) { + signedTx, err := tx.WithSignature(signature) + if err != nil { + return common.Hash{}, err + } + + if err := b.SendTx(ctx, signedTx); err != nil { + return common.Hash{}, err + } + + if signedTx.To() == nil { + from, _ := signedTx.From() + addr := crypto.CreateAddress(from, signedTx.Nonce()) + glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex()) + } else { + glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex()) + } + + return signedTx.Hash(), nil +} + +// SendTransaction creates a transaction for the given argument, sign it and submit it to the +// transaction pool. +func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args SendTxArgs) (common.Hash, error) { + var err error + args, err = prepareSendTxArgs(ctx, args, s.b) + if err != nil { + return common.Hash{}, err + } + + if args.Nonce == nil { + nonce, err := s.b.GetPoolNonce(ctx, args.From) + if err != nil { + return common.Hash{}, err + } + args.Nonce = rpc.NewHexNumber(nonce) + } + + var tx *types.Transaction + if args.To == nil { + tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) + } else { + tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) + } + + signature, err := s.b.AccountManager().Sign(args.From, tx.SigHash().Bytes()) + if err != nil { + return common.Hash{}, err + } + + return submitTransaction(ctx, s.b, tx, signature) +} + +// SendRawTransaction will add the signed transaction to the transaction pool. +// The sender is responsible for signing the transaction and using the correct nonce. +func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encodedTx string) (string, error) { + tx := new(types.Transaction) + if err := rlp.DecodeBytes(common.FromHex(encodedTx), tx); err != nil { + return "", err + } + + if err := s.b.SendTx(ctx, tx); err != nil { + return "", err + } + + if tx.To() == nil { + from, err := tx.FromFrontier() + 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 +} + +// Sign signs the given hash using the key that matches the address. The key must be +// unlocked in order to sign the hash. +func (s *PublicTransactionPoolAPI) Sign(addr common.Address, hash common.Hash) (string, error) { + signature, error := s.b.AccountManager().Sign(addr, hash[:]) + return common.ToHex(signature), error +} + +// SignTransactionArgs represents the arguments to sign a transaction. +type SignTransactionArgs struct { + From common.Address + To *common.Address + Nonce *rpc.HexNumber + Value *rpc.HexNumber + Gas *rpc.HexNumber + GasPrice *rpc.HexNumber + Data string + + BlockNumber int64 +} + +// Tx is a helper object for argument and return values +type Tx struct { + tx *types.Transaction + + To *common.Address `json:"to"` + From common.Address `json:"from"` + Nonce *rpc.HexNumber `json:"nonce"` + Value *rpc.HexNumber `json:"value"` + Data string `json:"data"` + GasLimit *rpc.HexNumber `json:"gas"` + GasPrice *rpc.HexNumber `json:"gasPrice"` + Hash common.Hash `json:"hash"` +} + +// UnmarshalJSON parses JSON data into tx. +func (tx *Tx) UnmarshalJSON(b []byte) (err error) { + req := struct { + To *common.Address `json:"to"` + From common.Address `json:"from"` + Nonce *rpc.HexNumber `json:"nonce"` + Value *rpc.HexNumber `json:"value"` + Data string `json:"data"` + GasLimit *rpc.HexNumber `json:"gas"` + GasPrice *rpc.HexNumber `json:"gasPrice"` + Hash common.Hash `json:"hash"` + }{} + + if err := json.Unmarshal(b, &req); err != nil { + return err + } + + tx.To = req.To + tx.From = req.From + tx.Nonce = req.Nonce + tx.Value = req.Value + tx.Data = req.Data + tx.GasLimit = req.GasLimit + tx.GasPrice = req.GasPrice + tx.Hash = req.Hash + + data := common.Hex2Bytes(tx.Data) + + if tx.Nonce == nil { + return fmt.Errorf("need nonce") + } + if tx.Value == nil { + tx.Value = rpc.NewHexNumber(0) + } + if tx.GasLimit == nil { + tx.GasLimit = rpc.NewHexNumber(0) + } + if tx.GasPrice == nil { + tx.GasPrice = rpc.NewHexNumber(int64(50000000000)) + } + + if req.To == nil { + tx.tx = types.NewContractCreation(tx.Nonce.Uint64(), tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data) + } else { + tx.tx = types.NewTransaction(tx.Nonce.Uint64(), *tx.To, tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data) + } + + return nil +} + +// SignTransactionResult represents a RLP encoded signed transaction. +type SignTransactionResult struct { + Raw string `json:"raw"` + Tx *Tx `json:"tx"` +} + +func newTx(t *types.Transaction) *Tx { + from, _ := t.FromFrontier() + return &Tx{ + tx: t, + To: t.To(), + From: from, + Value: rpc.NewHexNumber(t.Value()), + Nonce: rpc.NewHexNumber(t.Nonce()), + Data: "0x" + common.Bytes2Hex(t.Data()), + GasLimit: rpc.NewHexNumber(t.Gas()), + GasPrice: rpc.NewHexNumber(t.GasPrice()), + Hash: t.Hash(), + } +} + +// SignTransaction will sign the given transaction with the from account. +// The node needs to have the private key of the account corresponding with +// the given from address and it needs to be unlocked. +func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args SignTransactionArgs) (*SignTransactionResult, error) { + if args.Gas == nil { + args.Gas = rpc.NewHexNumber(defaultGas) + } + if args.GasPrice == nil { + price, err := s.b.SuggestPrice(ctx) + if err != nil { + return nil, err + } + args.GasPrice = rpc.NewHexNumber(price) + } + if args.Value == nil { + args.Value = rpc.NewHexNumber(0) + } + + if args.Nonce == nil { + nonce, err := s.b.GetPoolNonce(ctx, args.From) + if err != nil { + return nil, err + } + args.Nonce = rpc.NewHexNumber(nonce) + } + + var tx *types.Transaction + if args.To == nil { + tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data)) + } else { + 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) + if err != nil { + return nil, err + } + + data, err := rlp.EncodeToBytes(signedTx) + if err != nil { + return nil, err + } + + return &SignTransactionResult{"0x" + common.Bytes2Hex(data), newTx(signedTx)}, nil +} + +// 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() + transactions := make([]*RPCTransaction, 0, len(pending)) + for _, tx := range pending { + from, _ := tx.FromFrontier() + if s.b.AccountManager().HasAddress(from) { + transactions = append(transactions, newRPCPendingTransaction(tx)) + } + } + return transactions +} + +// NewPendingTransactions creates a subscription that is triggered each time a transaction enters the transaction pool +// and is send from one of the transactions this nodes manages. +func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (rpc.Subscription, error) { + notifier, supported := rpc.NotifierFromContext(ctx) + if !supported { + return nil, rpc.ErrNotificationsUnsupported + } + + subscription, err := notifier.NewSubscription(func(id string) { + s.muPendingTxSubs.Lock() + delete(s.pendingTxSubs, id) + s.muPendingTxSubs.Unlock() + }) + + if err != nil { + return nil, err + } + + s.muPendingTxSubs.Lock() + s.pendingTxSubs[subscription.ID()] = subscription + s.muPendingTxSubs.Unlock() + + return subscription, nil +} + +// 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() + for _, p := range pending { + if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() { + if gasPrice == nil { + gasPrice = rpc.NewHexNumber(tx.tx.GasPrice()) + } + if gasLimit == nil { + gasLimit = rpc.NewHexNumber(tx.tx.Gas()) + } + + var newTx *types.Transaction + if tx.tx.To() == nil { + newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) + } else { + newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data()) + } + + signedTx, err := s.sign(tx.From, newTx) + if err != nil { + return common.Hash{}, err + } + + s.b.RemoveTx(tx.Hash) + if err = s.b.SendTx(ctx, signedTx); err != nil { + return common.Hash{}, err + } + + return signedTx.Hash(), nil + } + } + + return common.Hash{}, fmt.Errorf("Transaction %#x not found", tx.Hash) +} + +// PrivateAdminAPI is the collection of Etheruem APIs exposed over the private +// admin endpoint. +type PrivateAdminAPI struct { + b Backend + solcPath *string + solc **compiler.Solidity +} + +// NewPrivateAdminAPI creates a new API definition for the private admin methods +// of the Ethereum service. +func NewPrivateAdminAPI(b Backend, solcPath *string, solc **compiler.Solidity) *PrivateAdminAPI { + return &PrivateAdminAPI{b, solcPath, solc} +} + +// SetSolc sets the Solidity compiler path to be used by the node. +func (api *PrivateAdminAPI) SetSolc(path string) (string, error) { + var err error + *api.solcPath = path + *api.solc, err = compiler.New(path) + if err != nil { + return "", err + } + return (*api.solc).Info(), nil +} + +// PublicDebugAPI is the collection of Etheruem APIs exposed over the public +// debugging endpoint. +type PublicDebugAPI struct { + b Backend +} + +// NewPublicDebugAPI creates a new API definition for the public debug methods +// of the Ethereum service. +func NewPublicDebugAPI(b Backend) *PublicDebugAPI { + return &PublicDebugAPI{b: b} +} + +// 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)) + if block == nil { + return "", fmt.Errorf("block #%d not found", number) + } + encoded, err := rlp.EncodeToBytes(block) + if err != nil { + return "", err + } + return fmt.Sprintf("%x", encoded), nil +} + +// 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)) + if block == nil { + return "", fmt.Errorf("block #%d not found", number) + } + return fmt.Sprintf("%s", block), nil +} + +// 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 + } + return fmt.Sprintf("0x%x", hash), nil +} + +// PrivateDebugAPI is the collection of Etheruem APIs exposed over the private +// debugging endpoint. +type PrivateDebugAPI struct { + b Backend +} + +// NewPrivateDebugAPI creates a new API definition for the private debug methods +// of the Ethereum service. +func NewPrivateDebugAPI(b Backend) *PrivateDebugAPI { + return &PrivateDebugAPI{b: b} +} + +// ChaindbProperty returns leveldb properties of the chain database. +func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) { + ldb, ok := api.b.ChainDb().(interface { + LDB() *leveldb.DB + }) + if !ok { + return "", fmt.Errorf("chaindbProperty does not work for memory databases") + } + if property == "" { + property = "leveldb.stats" + } else if !strings.HasPrefix(property, "leveldb.") { + property = "leveldb." + property + } + return ldb.LDB().GetProperty(property) +} + +// SetHead rewinds the head of the blockchain to a previous block. +func (api *PrivateDebugAPI) SetHead(number uint64) { + api.b.SetHead(number) +} + +// PublicNetAPI offers network related RPC methods +type PublicNetAPI struct { + net *p2p.Server + networkVersion int +} + +// NewPublicNetAPI creates a new net API instance. +func NewPublicNetAPI(net *p2p.Server, networkVersion int) *PublicNetAPI { + return &PublicNetAPI{net, networkVersion} +} + +// Listening returns an indication if the node is listening for network connections. +func (s *PublicNetAPI) Listening() bool { + return true // always listening +} + +// PeerCount returns the number of connected peers +func (s *PublicNetAPI) PeerCount() *rpc.HexNumber { + return rpc.NewHexNumber(s.net.PeerCount()) +} + +// Version returns the current ethereum protocol version. +func (s *PublicNetAPI) Version() string { + return fmt.Sprintf("%d", s.networkVersion) +} diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go new file mode 100644 index 000000000..d112a6aef --- /dev/null +++ b/internal/ethapi/backend.go @@ -0,0 +1,119 @@ +// 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 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, + }, + } +} diff --git a/release/release.go b/release/release.go index 05b4885b5..07eb9359c 100644 --- a/release/release.go +++ b/release/release.go @@ -30,6 +30,7 @@ import ( "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/rpc" + "golang.org/x/net/context" ) // Interval to check for new releases @@ -57,7 +58,7 @@ 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 ethereum *eth.Ethereum + var ethereum *eth.FullNodeService if err := ctx.Service(ðereum); err != nil { return nil, err } @@ -110,7 +111,9 @@ func (r *ReleaseService) checker() { timer.Reset(releaseRecheckInterval) // 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 == bind.ErrNoCode { glog.V(logger.Debug).Infof("Release oracle not found at %x", r.config.Oracle)