Merge branch 'release/1.4'
This commit is contained in:
commit
311f5a0ed1
|
@ -305,10 +305,10 @@ func TestUnpackSetInterfaceSlice(t *testing.T) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if *var1 != 1 {
|
if *var1 != 1 {
|
||||||
t.Errorf("expected var1 to be 1, got", *var1)
|
t.Error("expected var1 to be 1, got", *var1)
|
||||||
}
|
}
|
||||||
if *var2 != 2 {
|
if *var2 != 2 {
|
||||||
t.Errorf("expected var2 to be 2, got", *var2)
|
t.Error("expected var2 to be 2, got", *var2)
|
||||||
}
|
}
|
||||||
|
|
||||||
out = []interface{}{var1}
|
out = []interface{}{var1}
|
||||||
|
|
|
@ -27,15 +27,16 @@ import (
|
||||||
// ErrNoCode is returned by call and transact operations for which the requested
|
// ErrNoCode is returned by call and transact operations for which the requested
|
||||||
// recipient contract to operate on does not exist in the state db or does not
|
// recipient contract to operate on does not exist in the state db or does not
|
||||||
// have any code associated with it (i.e. suicided).
|
// have any code associated with it (i.e. suicided).
|
||||||
//
|
|
||||||
// Please note, this error string is part of the RPC API and is expected by the
|
|
||||||
// native contract bindings to signal this particular error. Do not change this
|
|
||||||
// as it will break all dependent code!
|
|
||||||
var ErrNoCode = errors.New("no contract code at given address")
|
var ErrNoCode = errors.New("no contract code at given address")
|
||||||
|
|
||||||
// ContractCaller defines the methods needed to allow operating with contract on a read
|
// ContractCaller defines the methods needed to allow operating with contract on a read
|
||||||
// only basis.
|
// only basis.
|
||||||
type ContractCaller interface {
|
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)
|
||||||
|
|
||||||
// ContractCall executes an Ethereum contract call with the specified data as
|
// ContractCall executes an Ethereum contract call with the specified data as
|
||||||
// the input. The pending flag requests execution against the pending block, not
|
// the input. The pending flag requests execution against the pending block, not
|
||||||
// the stable head of the chain.
|
// the stable head of the chain.
|
||||||
|
@ -55,6 +56,11 @@ type ContractTransactor interface {
|
||||||
// execution of a transaction.
|
// execution of a transaction.
|
||||||
SuggestGasPrice() (*big.Int, error)
|
SuggestGasPrice() (*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)
|
||||||
|
|
||||||
// EstimateGasLimit tries to estimate the gas needed to execute a specific
|
// EstimateGasLimit tries to estimate the gas needed to execute a specific
|
||||||
// transaction based on the current pending state of the backend blockchain.
|
// transaction based on the current pending state of the backend blockchain.
|
||||||
// There is no guarantee that this is the true gas limit requirement as other
|
// There is no guarantee that this is the true gas limit requirement as other
|
||||||
|
@ -68,7 +74,38 @@ type ContractTransactor interface {
|
||||||
|
|
||||||
// ContractBackend defines the methods needed to allow operating with contract
|
// ContractBackend defines the methods needed to allow operating with contract
|
||||||
// on a read-write basis.
|
// on a read-write basis.
|
||||||
|
//
|
||||||
|
// This interface is essentially the union of ContractCaller and ContractTransactor
|
||||||
|
// but due to a bug in the Go compiler (https://github.com/golang/go/issues/6977),
|
||||||
|
// we cannot simply list it as the two interfaces. The other solution is to add a
|
||||||
|
// third interface containing the common methods, but that convolutes the user API
|
||||||
|
// as it introduces yet another parameter to require for initialization.
|
||||||
type ContractBackend interface {
|
type ContractBackend interface {
|
||||||
ContractCaller
|
// HasCode checks if the contract at the given address has any code associated
|
||||||
ContractTransactor
|
// 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)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// PendingAccountNonce retrieves the current pending nonce associated with an
|
||||||
|
// account.
|
||||||
|
PendingAccountNonce(account common.Address) (uint64, error)
|
||||||
|
|
||||||
|
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
||||||
|
// execution of a transaction.
|
||||||
|
SuggestGasPrice() (*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)
|
||||||
|
|
||||||
|
// SendTransaction injects the transaction into the pending pool for execution.
|
||||||
|
SendTransaction(tx *types.Transaction) error
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,7 @@ func (*nilBackend) ContractCall(common.Address, []byte, bool) ([]byte, error) {
|
||||||
func (*nilBackend) EstimateGasLimit(common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) {
|
func (*nilBackend) EstimateGasLimit(common.Address, *common.Address, *big.Int, []byte) (*big.Int, error) {
|
||||||
panic("not implemented")
|
panic("not implemented")
|
||||||
}
|
}
|
||||||
|
func (*nilBackend) HasCode(common.Address, bool) (bool, error) { panic("not implemented") }
|
||||||
func (*nilBackend) SuggestGasPrice() (*big.Int, error) { panic("not implemented") }
|
func (*nilBackend) SuggestGasPrice() (*big.Int, error) { panic("not implemented") }
|
||||||
func (*nilBackend) PendingAccountNonce(common.Address) (uint64, error) { panic("not implemented") }
|
func (*nilBackend) PendingAccountNonce(common.Address) (uint64, error) { panic("not implemented") }
|
||||||
func (*nilBackend) SendTransaction(*types.Transaction) error { panic("not implemented") }
|
func (*nilBackend) SendTransaction(*types.Transaction) error { panic("not implemented") }
|
||||||
|
|
|
@ -111,6 +111,26 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa
|
||||||
return res.Result, nil
|
return res.Result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// Execute the RPC code retrieval
|
||||||
|
block := "latest"
|
||||||
|
if pending {
|
||||||
|
block = "pending"
|
||||||
|
}
|
||||||
|
res, err := b.request("eth_getCode", []interface{}{contract.Hex(), block})
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
var hex string
|
||||||
|
if err := json.Unmarshal(res, &hex); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
// Convert the response back to a Go byte slice and return
|
||||||
|
return len(common.FromHex(hex)) > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ContractCall implements ContractCaller.ContractCall, delegating the execution of
|
// ContractCall implements ContractCaller.ContractCall, delegating the execution of
|
||||||
// a contract call to the remote node, returning the reply to for local processing.
|
// a contract call to the remote node, returning the reply to for local processing.
|
||||||
func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) {
|
func (b *rpcBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) {
|
||||||
|
|
|
@ -78,6 +78,16 @@ func (b *SimulatedBackend) Rollback() {
|
||||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
if pending {
|
||||||
|
return len(b.pendingState.GetCode(contract)) > 0, nil
|
||||||
|
}
|
||||||
|
statedb, _ := b.blockchain.State()
|
||||||
|
return len(statedb.GetCode(contract)) > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ContractCall implements ContractCaller.ContractCall, executing the specified
|
// ContractCall implements ContractCaller.ContractCall, executing the specified
|
||||||
// contract with the given input data.
|
// contract with the given input data.
|
||||||
func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) {
|
func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) {
|
||||||
|
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
@ -56,6 +57,9 @@ type BoundContract struct {
|
||||||
abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
|
abi abi.ABI // Reflect based ABI to access the correct Ethereum methods
|
||||||
caller ContractCaller // Read interface to interact with the blockchain
|
caller ContractCaller // Read interface to interact with the blockchain
|
||||||
transactor ContractTransactor // Write interface to interact with the blockchain
|
transactor ContractTransactor // Write interface to interact with the blockchain
|
||||||
|
|
||||||
|
latestHasCode uint32 // Cached verification that the latest state contains code for this contract
|
||||||
|
pendingHasCode uint32 // Cached verification that the pending state contains code for this contract
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBoundContract creates a low level contract interface through which calls
|
// NewBoundContract creates a low level contract interface through which calls
|
||||||
|
@ -96,6 +100,19 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = new(CallOpts)
|
opts = new(CallOpts)
|
||||||
}
|
}
|
||||||
|
// 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 {
|
||||||
|
return err
|
||||||
|
} else if !code {
|
||||||
|
return ErrNoCode
|
||||||
|
}
|
||||||
|
if opts.Pending {
|
||||||
|
atomic.StoreUint32(&c.pendingHasCode, 1)
|
||||||
|
} else {
|
||||||
|
atomic.StoreUint32(&c.latestHasCode, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
// Pack the input, call and unpack the results
|
// Pack the input, call and unpack the results
|
||||||
input, err := c.abi.Pack(method, params...)
|
input, err := c.abi.Pack(method, params...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -153,6 +170,16 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
|
||||||
}
|
}
|
||||||
gasLimit := opts.GasLimit
|
gasLimit := opts.GasLimit
|
||||||
if gasLimit == nil {
|
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 {
|
||||||
|
return nil, err
|
||||||
|
} else if !code {
|
||||||
|
return nil, ErrNoCode
|
||||||
|
}
|
||||||
|
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.From, contract, value, input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to exstimate gas needed: %v", err)
|
return nil, fmt.Errorf("failed to exstimate gas needed: %v", err)
|
||||||
|
|
|
@ -147,9 +147,21 @@ func (am *Manager) Sign(addr common.Address, hash []byte) (signature []byte, err
|
||||||
return crypto.Sign(hash, unlockedKey.PrivateKey)
|
return crypto.Sign(hash, unlockedKey.PrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignWithPassphrase signs hash if the private key matching the given address can be
|
||||||
|
// decrypted with the given passphrase.
|
||||||
|
func (am *Manager) SignWithPassphrase(addr common.Address, passphrase string, hash []byte) (signature []byte, err error) {
|
||||||
|
_, key, err := am.getDecryptedKey(Account{Address: addr}, passphrase)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer zeroKey(key.PrivateKey)
|
||||||
|
return crypto.Sign(hash, key.PrivateKey)
|
||||||
|
}
|
||||||
|
|
||||||
// Unlock unlocks the given account indefinitely.
|
// Unlock unlocks the given account indefinitely.
|
||||||
func (am *Manager) Unlock(a Account, keyAuth string) error {
|
func (am *Manager) Unlock(a Account, passphrase string) error {
|
||||||
return am.TimedUnlock(a, keyAuth, 0)
|
return am.TimedUnlock(a, passphrase, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lock removes the private key with the given address from memory.
|
// Lock removes the private key with the given address from memory.
|
||||||
|
|
|
@ -81,6 +81,34 @@ func TestSign(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSignWithPassphrase(t *testing.T) {
|
||||||
|
dir, am := tmpManager(t, true)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
pass := "passwd"
|
||||||
|
acc, err := am.NewAccount(pass)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, unlocked := am.unlocked[acc.Address]; unlocked {
|
||||||
|
t.Fatal("expected account to be locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = am.SignWithPassphrase(acc.Address, pass, testSigData)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, unlocked := am.unlocked[acc.Address]; unlocked {
|
||||||
|
t.Fatal("expected account to be locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = am.SignWithPassphrase(acc.Address, "invalid passwd", testSigData); err == nil {
|
||||||
|
t.Fatal("expected SignHash to fail with invalid password")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestTimedUnlock(t *testing.T) {
|
func TestTimedUnlock(t *testing.T) {
|
||||||
dir, am := tmpManager(t, true)
|
dir, am := tmpManager(t, true)
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
|
@ -41,8 +41,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
passwordRegexp = regexp.MustCompile("personal.[nu]")
|
passwordRegexp = regexp.MustCompile("personal.[nus]")
|
||||||
leadingSpace = regexp.MustCompile("^ ")
|
|
||||||
onlyws = regexp.MustCompile("^\\s*$")
|
onlyws = regexp.MustCompile("^\\s*$")
|
||||||
exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$")
|
exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$")
|
||||||
)
|
)
|
||||||
|
@ -361,7 +360,7 @@ func (self *jsre) interactive() {
|
||||||
str += input + "\n"
|
str += input + "\n"
|
||||||
self.setIndent()
|
self.setIndent()
|
||||||
if indentCount <= 0 {
|
if indentCount <= 0 {
|
||||||
if mustLogInHistory(str) {
|
if !excludeFromHistory(str) {
|
||||||
utils.Stdin.AppendHistory(str[:len(str)-1])
|
utils.Stdin.AppendHistory(str[:len(str)-1])
|
||||||
}
|
}
|
||||||
self.parseInput(str)
|
self.parseInput(str)
|
||||||
|
@ -371,10 +370,8 @@ func (self *jsre) interactive() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustLogInHistory(input string) bool {
|
func excludeFromHistory(input string) bool {
|
||||||
return len(input) == 0 ||
|
return len(input) == 0 || input[0] == ' ' || passwordRegexp.MatchString(input)
|
||||||
passwordRegexp.MatchString(input) ||
|
|
||||||
!leadingSpace.MatchString(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *jsre) withHistory(datadir string, op func(*os.File)) {
|
func (self *jsre) withHistory(datadir string, op func(*os.File)) {
|
||||||
|
|
195
eth/api.go
195
eth/api.go
|
@ -52,15 +52,6 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
// errNoCode is returned by call and transact operations for which the requested
|
|
||||||
// recipient contract to operate on does not exist in the state db or does not
|
|
||||||
// have any code associated with it (i.e. suicided).
|
|
||||||
//
|
|
||||||
// Please note, this error string is part of the RPC API and is expected by the
|
|
||||||
// native contract bindings to signal this particular error. Do not change this
|
|
||||||
// as it will break all dependent code!
|
|
||||||
var errNoCode = errors.New("no contract code at given address")
|
|
||||||
|
|
||||||
const defaultGas = uint64(90000)
|
const defaultGas = uint64(90000)
|
||||||
|
|
||||||
// blockByNumber is a commonly used helper function which retrieves and returns
|
// blockByNumber is a commonly used helper function which retrieves and returns
|
||||||
|
@ -148,7 +139,7 @@ func (s *PublicEthereumAPI) Etherbase() (common.Address, error) {
|
||||||
return s.e.Etherbase()
|
return s.e.Etherbase()
|
||||||
}
|
}
|
||||||
|
|
||||||
// see Etherbase
|
// Coinbase is the address that mining rewards will be send to (alias for Etherbase)
|
||||||
func (s *PublicEthereumAPI) Coinbase() (common.Address, error) {
|
func (s *PublicEthereumAPI) Coinbase() (common.Address, error) {
|
||||||
return s.Etherbase()
|
return s.Etherbase()
|
||||||
}
|
}
|
||||||
|
@ -217,18 +208,17 @@ func (s *PublicMinerAPI) SubmitWork(nonce rpc.HexNumber, solution, digest common
|
||||||
// result[0], 32 bytes hex encoded current block header pow-hash
|
// result[0], 32 bytes hex encoded current block header pow-hash
|
||||||
// result[1], 32 bytes hex encoded seed hash used for DAG
|
// result[1], 32 bytes hex encoded seed hash used for DAG
|
||||||
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
|
||||||
func (s *PublicMinerAPI) GetWork() ([]string, error) {
|
func (s *PublicMinerAPI) GetWork() (work [3]string, err error) {
|
||||||
if !s.e.IsMining() {
|
if !s.e.IsMining() {
|
||||||
if err := s.e.StartMining(0, ""); err != nil {
|
if err := s.e.StartMining(0, ""); err != nil {
|
||||||
return nil, err
|
return work, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if work, err := s.agent.GetWork(); err == nil {
|
if work, err = s.agent.GetWork(); err == nil {
|
||||||
return work[:], nil
|
return
|
||||||
} else {
|
|
||||||
glog.Infof("%v\n", err)
|
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("mining not ready")
|
glog.V(logger.Debug).Infof("%v", err)
|
||||||
|
return work, fmt.Errorf("mining not ready")
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
|
// SubmitHashrate can be used for remote miners to submit their hash rate. This enables the node to report the combined
|
||||||
|
@ -423,14 +413,23 @@ func (s *PublicAccountAPI) Accounts() []accounts.Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
// PrivateAccountAPI provides an API to access accounts managed by this node.
|
// PrivateAccountAPI provides an API to access accounts managed by this node.
|
||||||
// It offers methods to create, (un)lock en list accounts.
|
// It offers methods to create, (un)lock en list accounts. Some methods accept
|
||||||
|
// passwords and are therefore considered private by default.
|
||||||
type PrivateAccountAPI struct {
|
type PrivateAccountAPI struct {
|
||||||
am *accounts.Manager
|
am *accounts.Manager
|
||||||
|
txPool *core.TxPool
|
||||||
|
txMu *sync.Mutex
|
||||||
|
gpo *GasPriceOracle
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPrivateAccountAPI create a new PrivateAccountAPI.
|
// NewPrivateAccountAPI create a new PrivateAccountAPI.
|
||||||
func NewPrivateAccountAPI(am *accounts.Manager) *PrivateAccountAPI {
|
func NewPrivateAccountAPI(e *Ethereum) *PrivateAccountAPI {
|
||||||
return &PrivateAccountAPI{am}
|
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.
|
// ListAccounts will return a list of addresses for accounts this node manages.
|
||||||
|
@ -452,6 +451,8 @@ func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error)
|
||||||
return common.Address{}, err
|
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) {
|
func (s *PrivateAccountAPI) ImportRawKey(privkey string, password string) (common.Address, error) {
|
||||||
hexkey, err := hex.DecodeString(privkey)
|
hexkey, err := hex.DecodeString(privkey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -482,6 +483,34 @@ func (s *PrivateAccountAPI) LockAccount(addr common.Address) bool {
|
||||||
return s.am.Lock(addr) == nil
|
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.
|
// PublicBlockChainAPI provides an API to access the Ethereum blockchain.
|
||||||
// It offers only methods that operate on public data that is freely available to anyone.
|
// It offers only methods that operate on public data that is freely available to anyone.
|
||||||
type PublicBlockChainAPI struct {
|
type PublicBlockChainAPI struct {
|
||||||
|
@ -645,15 +674,14 @@ func (s *PublicBlockChainAPI) NewBlocks(ctx context.Context, args NewBlocksArgs)
|
||||||
// add a callback that is called on chain events which will format the block and notify the client
|
// add a callback that is called on chain events which will format the block and notify the client
|
||||||
s.muNewBlockSubscriptions.Lock()
|
s.muNewBlockSubscriptions.Lock()
|
||||||
s.newBlockSubscriptions[subscription.ID()] = func(e core.ChainEvent) error {
|
s.newBlockSubscriptions[subscription.ID()] = func(e core.ChainEvent) error {
|
||||||
if notification, err := s.rpcOutputBlock(e.Block, args.IncludeTransactions, args.TransactionDetails); err == nil {
|
notification, err := s.rpcOutputBlock(e.Block, args.IncludeTransactions, args.TransactionDetails)
|
||||||
|
if err == nil {
|
||||||
return subscription.Notify(notification)
|
return subscription.Notify(notification)
|
||||||
} else {
|
|
||||||
glog.V(logger.Warn).Info("unable to format block %v\n", err)
|
|
||||||
}
|
}
|
||||||
|
glog.V(logger.Warn).Info("unable to format block %v\n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
s.muNewBlockSubscriptions.Unlock()
|
s.muNewBlockSubscriptions.Unlock()
|
||||||
|
|
||||||
return subscription, nil
|
return subscription, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -700,6 +728,7 @@ func (m callmsg) Gas() *big.Int { return m.gas }
|
||||||
func (m callmsg) Value() *big.Int { return m.value }
|
func (m callmsg) Value() *big.Int { return m.value }
|
||||||
func (m callmsg) Data() []byte { return m.data }
|
func (m callmsg) Data() []byte { return m.data }
|
||||||
|
|
||||||
|
// CallArgs represents the arguments for a call.
|
||||||
type CallArgs struct {
|
type CallArgs struct {
|
||||||
From common.Address `json:"from"`
|
From common.Address `json:"from"`
|
||||||
To *common.Address `json:"to"`
|
To *common.Address `json:"to"`
|
||||||
|
@ -717,12 +746,6 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st
|
||||||
}
|
}
|
||||||
stateDb = stateDb.Copy()
|
stateDb = stateDb.Copy()
|
||||||
|
|
||||||
// If there's no code to interact with, respond with an appropriate error
|
|
||||||
if args.To != nil {
|
|
||||||
if code := stateDb.GetCode(*args.To); len(code) == 0 {
|
|
||||||
return "0x", nil, errNoCode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Retrieve the account state object to interact with
|
// Retrieve the account state object to interact with
|
||||||
var from *state.StateObject
|
var from *state.StateObject
|
||||||
if args.From == (common.Address{}) {
|
if args.From == (common.Address{}) {
|
||||||
|
@ -911,7 +934,7 @@ type PublicTransactionPoolAPI struct {
|
||||||
miner *miner.Miner
|
miner *miner.Miner
|
||||||
am *accounts.Manager
|
am *accounts.Manager
|
||||||
txPool *core.TxPool
|
txPool *core.TxPool
|
||||||
txMu sync.Mutex
|
txMu *sync.Mutex
|
||||||
muPendingTxSubs sync.Mutex
|
muPendingTxSubs sync.Mutex
|
||||||
pendingTxSubs map[string]rpc.Subscription
|
pendingTxSubs map[string]rpc.Subscription
|
||||||
}
|
}
|
||||||
|
@ -925,6 +948,7 @@ func NewPublicTransactionPoolAPI(e *Ethereum) *PublicTransactionPoolAPI {
|
||||||
bc: e.blockchain,
|
bc: e.blockchain,
|
||||||
am: e.accountManager,
|
am: e.accountManager,
|
||||||
txPool: e.txPool,
|
txPool: e.txPool,
|
||||||
|
txMu: &e.txMu,
|
||||||
miner: e.miner,
|
miner: e.miner,
|
||||||
pendingTxSubs: make(map[string]rpc.Subscription),
|
pendingTxSubs: make(map[string]rpc.Subscription),
|
||||||
}
|
}
|
||||||
|
@ -1124,6 +1148,7 @@ func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transacti
|
||||||
return tx.WithSignature(signature)
|
return tx.WithSignature(signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendTxArgs represents the arguments to sumbit a new transaction into the transaction pool.
|
||||||
type SendTxArgs struct {
|
type SendTxArgs struct {
|
||||||
From common.Address `json:"from"`
|
From common.Address `json:"from"`
|
||||||
To *common.Address `json:"to"`
|
To *common.Address `json:"to"`
|
||||||
|
@ -1134,18 +1159,47 @@ type SendTxArgs struct {
|
||||||
Nonce *rpc.HexNumber `json:"nonce"`
|
Nonce *rpc.HexNumber `json:"nonce"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendTransaction will create a transaction for the given transaction argument, sign it and submit it to the
|
// prepareSendTxArgs is a helper function that fills in default values for unspecified tx fields.
|
||||||
// transaction pool.
|
func prepareSendTxArgs(args SendTxArgs, gpo *GasPriceOracle) SendTxArgs {
|
||||||
func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash, error) {
|
|
||||||
if args.Gas == nil {
|
if args.Gas == nil {
|
||||||
args.Gas = rpc.NewHexNumber(defaultGas)
|
args.Gas = rpc.NewHexNumber(defaultGas)
|
||||||
}
|
}
|
||||||
if args.GasPrice == nil {
|
if args.GasPrice == nil {
|
||||||
args.GasPrice = rpc.NewHexNumber(s.gpo.SuggestPrice())
|
args.GasPrice = rpc.NewHexNumber(gpo.SuggestPrice())
|
||||||
}
|
}
|
||||||
if args.Value == nil {
|
if args.Value == nil {
|
||||||
args.Value = rpc.NewHexNumber(0)
|
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()
|
s.txMu.Lock()
|
||||||
defer s.txMu.Unlock()
|
defer s.txMu.Unlock()
|
||||||
|
@ -1155,32 +1209,18 @@ func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
var tx *types.Transaction
|
var tx *types.Transaction
|
||||||
contractCreation := (args.To == nil)
|
if args.To == nil {
|
||||||
|
|
||||||
if contractCreation {
|
|
||||||
tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
||||||
} else {
|
} else {
|
||||||
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
||||||
}
|
}
|
||||||
|
|
||||||
signedTx, err := s.sign(args.From, tx)
|
signature, err := s.am.Sign(args.From, tx.SigHash().Bytes())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
return common.Hash{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.txPool.SetLocal(signedTx)
|
return submitTransaction(s.txPool, tx, signature)
|
||||||
if err := s.txPool.Add(signedTx); err != nil {
|
|
||||||
return common.Hash{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if contractCreation {
|
|
||||||
addr := crypto.CreateAddress(args.From, args.Nonce.Uint64())
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SendRawTransaction will add the signed transaction to the transaction pool.
|
// SendRawTransaction will add the signed transaction to the transaction pool.
|
||||||
|
@ -1217,6 +1257,7 @@ func (s *PublicTransactionPoolAPI) Sign(addr common.Address, hash common.Hash) (
|
||||||
return common.ToHex(signature), error
|
return common.ToHex(signature), error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignTransactionArgs represents the arguments to sign a transaction.
|
||||||
type SignTransactionArgs struct {
|
type SignTransactionArgs struct {
|
||||||
From common.Address
|
From common.Address
|
||||||
To *common.Address
|
To *common.Address
|
||||||
|
@ -1243,6 +1284,7 @@ type Tx struct {
|
||||||
Hash common.Hash `json:"hash"`
|
Hash common.Hash `json:"hash"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON parses JSON data into tx.
|
||||||
func (tx *Tx) UnmarshalJSON(b []byte) (err error) {
|
func (tx *Tx) UnmarshalJSON(b []byte) (err error) {
|
||||||
req := struct {
|
req := struct {
|
||||||
To *common.Address `json:"to"`
|
To *common.Address `json:"to"`
|
||||||
|
@ -1283,8 +1325,7 @@ func (tx *Tx) UnmarshalJSON(b []byte) (err error) {
|
||||||
tx.GasPrice = rpc.NewHexNumber(int64(50000000000))
|
tx.GasPrice = rpc.NewHexNumber(int64(50000000000))
|
||||||
}
|
}
|
||||||
|
|
||||||
contractCreation := (req.To == nil)
|
if req.To == nil {
|
||||||
if contractCreation {
|
|
||||||
tx.tx = types.NewContractCreation(tx.Nonce.Uint64(), tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data)
|
tx.tx = types.NewContractCreation(tx.Nonce.Uint64(), tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data)
|
||||||
} else {
|
} else {
|
||||||
tx.tx = types.NewTransaction(tx.Nonce.Uint64(), *tx.To, tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data)
|
tx.tx = types.NewTransaction(tx.Nonce.Uint64(), *tx.To, tx.Value.BigInt(), tx.GasLimit.BigInt(), tx.GasPrice.BigInt(), data)
|
||||||
|
@ -1293,6 +1334,7 @@ func (tx *Tx) UnmarshalJSON(b []byte) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SignTransactionResult represents a RLP encoded signed transaction.
|
||||||
type SignTransactionResult struct {
|
type SignTransactionResult struct {
|
||||||
Raw string `json:"raw"`
|
Raw string `json:"raw"`
|
||||||
Tx *Tx `json:"tx"`
|
Tx *Tx `json:"tx"`
|
||||||
|
@ -1335,9 +1377,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(args SignTransactionArgs) (*S
|
||||||
}
|
}
|
||||||
|
|
||||||
var tx *types.Transaction
|
var tx *types.Transaction
|
||||||
contractCreation := (args.To == nil)
|
if args.To == nil {
|
||||||
|
|
||||||
if contractCreation {
|
|
||||||
tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
tx = types.NewContractCreation(args.Nonce.Uint64(), args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
||||||
} else {
|
} else {
|
||||||
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
||||||
|
@ -1353,14 +1393,14 @@ func (s *PublicTransactionPoolAPI) SignTransaction(args SignTransactionArgs) (*S
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &SignTransactionResult{"0x" + common.Bytes2Hex(data), newTx(tx)}, nil
|
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
|
// PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of
|
||||||
// the accounts this node manages.
|
// the accounts this node manages.
|
||||||
func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
|
func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
|
||||||
pending := s.txPool.GetTransactions()
|
pending := s.txPool.GetTransactions()
|
||||||
transactions := make([]*RPCTransaction, 0)
|
transactions := make([]*RPCTransaction, 0, len(pending))
|
||||||
for _, tx := range pending {
|
for _, tx := range pending {
|
||||||
from, _ := tx.FromFrontier()
|
from, _ := tx.FromFrontier()
|
||||||
if s.am.HasAddress(from) {
|
if s.am.HasAddress(from) {
|
||||||
|
@ -1370,7 +1410,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
|
||||||
return transactions
|
return transactions
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPendingTransaction creates a subscription that is triggered each time a transaction enters the transaction pool
|
// 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.
|
// and is send from one of the transactions this nodes manages.
|
||||||
func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (rpc.Subscription, error) {
|
func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (rpc.Subscription, error) {
|
||||||
notifier, supported := rpc.NotifierFromContext(ctx)
|
notifier, supported := rpc.NotifierFromContext(ctx)
|
||||||
|
@ -1410,8 +1450,7 @@ func (s *PublicTransactionPoolAPI) Resend(tx Tx, gasPrice, gasLimit *rpc.HexNumb
|
||||||
}
|
}
|
||||||
|
|
||||||
var newTx *types.Transaction
|
var newTx *types.Transaction
|
||||||
contractCreation := (tx.tx.To() == nil)
|
if tx.tx.To() == nil {
|
||||||
if contractCreation {
|
|
||||||
newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
|
newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
|
||||||
} else {
|
} else {
|
||||||
newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
|
newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
|
||||||
|
@ -1612,7 +1651,7 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) {
|
||||||
return ldb.LDB().GetProperty(property)
|
return ldb.LDB().GetProperty(property)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BlockTraceResults is the returned value when replaying a block to check for
|
// BlockTraceResult is the returned value when replaying a block to check for
|
||||||
// consensus results and full VM trace logs for all included transactions.
|
// consensus results and full VM trace logs for all included transactions.
|
||||||
type BlockTraceResult struct {
|
type BlockTraceResult struct {
|
||||||
Validated bool `json:"validated"`
|
Validated bool `json:"validated"`
|
||||||
|
@ -1647,7 +1686,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) B
|
||||||
return api.TraceBlock(blockRlp, config)
|
return api.TraceBlock(blockRlp, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TraceProcessBlock processes the block by canonical block number.
|
// TraceBlockByNumber processes the block by canonical block number.
|
||||||
func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult {
|
func (api *PrivateDebugAPI) TraceBlockByNumber(number uint64, config *vm.Config) BlockTraceResult {
|
||||||
// Fetch the block that we aim to reprocess
|
// Fetch the block that we aim to reprocess
|
||||||
block := api.eth.BlockChain().GetBlockByNumber(number)
|
block := api.eth.BlockChain().GetBlockByNumber(number)
|
||||||
|
@ -1752,15 +1791,6 @@ type structLogRes struct {
|
||||||
Storage map[string]string `json:"storage"`
|
Storage map[string]string `json:"storage"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// VmLoggerOptions are the options used for debugging transactions and capturing
|
|
||||||
// specific data.
|
|
||||||
type VmLoggerOptions struct {
|
|
||||||
DisableMemory bool // disable memory capture
|
|
||||||
DisableStack bool // disable stack capture
|
|
||||||
DisableStorage bool // disable storage capture
|
|
||||||
FullStorage bool // show full storage (slow)
|
|
||||||
}
|
|
||||||
|
|
||||||
// formatLogs formats EVM returned structured logs for json output
|
// formatLogs formats EVM returned structured logs for json output
|
||||||
func formatLogs(structLogs []vm.StructLog) []structLogRes {
|
func formatLogs(structLogs []vm.StructLog) []structLogRes {
|
||||||
formattedStructLogs := make([]structLogRes, len(structLogs))
|
formattedStructLogs := make([]structLogRes, len(structLogs))
|
||||||
|
@ -1802,25 +1832,25 @@ func formatError(err error) string {
|
||||||
|
|
||||||
// TraceTransaction returns the structured logs created during the execution of EVM
|
// TraceTransaction returns the structured logs created during the execution of EVM
|
||||||
// and returns them as a JSON object.
|
// and returns them as a JSON object.
|
||||||
func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ExecutionResult, error) {
|
func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogConfig) (*ExecutionResult, error) {
|
||||||
if logger == nil {
|
if logger == nil {
|
||||||
logger = new(vm.LogConfig)
|
logger = new(vm.LogConfig)
|
||||||
}
|
}
|
||||||
// Retrieve the tx from the chain and the containing block
|
// Retrieve the tx from the chain and the containing block
|
||||||
tx, blockHash, _, txIndex := core.GetTransaction(s.eth.ChainDb(), txHash)
|
tx, blockHash, _, txIndex := core.GetTransaction(api.eth.ChainDb(), txHash)
|
||||||
if tx == nil {
|
if tx == nil {
|
||||||
return nil, fmt.Errorf("transaction %x not found", txHash)
|
return nil, fmt.Errorf("transaction %x not found", txHash)
|
||||||
}
|
}
|
||||||
block := s.eth.BlockChain().GetBlock(blockHash)
|
block := api.eth.BlockChain().GetBlock(blockHash)
|
||||||
if block == nil {
|
if block == nil {
|
||||||
return nil, fmt.Errorf("block %x not found", blockHash)
|
return nil, fmt.Errorf("block %x not found", blockHash)
|
||||||
}
|
}
|
||||||
// Create the state database to mutate and eventually trace
|
// Create the state database to mutate and eventually trace
|
||||||
parent := s.eth.BlockChain().GetBlock(block.ParentHash())
|
parent := api.eth.BlockChain().GetBlock(block.ParentHash())
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return nil, fmt.Errorf("block parent %x not found", block.ParentHash())
|
return nil, fmt.Errorf("block parent %x not found", block.ParentHash())
|
||||||
}
|
}
|
||||||
stateDb, err := state.New(parent.Root(), s.eth.ChainDb())
|
stateDb, err := state.New(parent.Root(), api.eth.ChainDb())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1841,7 +1871,7 @@ func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogCon
|
||||||
}
|
}
|
||||||
// Mutate the state if we haven't reached the tracing transaction yet
|
// Mutate the state if we haven't reached the tracing transaction yet
|
||||||
if uint64(idx) < txIndex {
|
if uint64(idx) < txIndex {
|
||||||
vmenv := core.NewEnv(stateDb, s.config, s.eth.BlockChain(), msg, block.Header(), vm.Config{})
|
vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
|
||||||
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("mutation failed: %v", err)
|
return nil, fmt.Errorf("mutation failed: %v", err)
|
||||||
|
@ -1849,7 +1879,7 @@ func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogCon
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// Otherwise trace the transaction and return
|
// Otherwise trace the transaction and return
|
||||||
vmenv := core.NewEnv(stateDb, s.config, s.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Logger: *logger})
|
vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Logger: *logger})
|
||||||
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||||
|
@ -1863,6 +1893,7 @@ func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogCon
|
||||||
return nil, errors.New("database inconsistency")
|
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) {
|
func (s *PublicBlockChainAPI) TraceCall(args CallArgs, blockNr rpc.BlockNumber) (*ExecutionResult, error) {
|
||||||
// Fetch the state associated with the block number
|
// Fetch the state associated with the block number
|
||||||
stateDb, block, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
|
stateDb, block, err := stateAndBlockByNumber(s.miner, s.bc, blockNr, s.chainDb)
|
||||||
|
@ -1931,12 +1962,12 @@ func (s *PublicNetAPI) Listening() bool {
|
||||||
return true // always listening
|
return true // always listening
|
||||||
}
|
}
|
||||||
|
|
||||||
// Peercount returns the number of connected peers
|
// PeerCount returns the number of connected peers
|
||||||
func (s *PublicNetAPI) PeerCount() *rpc.HexNumber {
|
func (s *PublicNetAPI) PeerCount() *rpc.HexNumber {
|
||||||
return rpc.NewHexNumber(s.net.PeerCount())
|
return rpc.NewHexNumber(s.net.PeerCount())
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProtocolVersion returns the current ethereum protocol version.
|
// Version returns the current ethereum protocol version.
|
||||||
func (s *PublicNetAPI) Version() string {
|
func (s *PublicNetAPI) Version() string {
|
||||||
return fmt.Sprintf("%d", s.networkVersion)
|
return fmt.Sprintf("%d", s.networkVersion)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/ethash"
|
"github.com/ethereum/ethash"
|
||||||
|
@ -113,6 +114,7 @@ type Ethereum struct {
|
||||||
|
|
||||||
// Handlers
|
// Handlers
|
||||||
txPool *core.TxPool
|
txPool *core.TxPool
|
||||||
|
txMu sync.Mutex
|
||||||
blockchain *core.BlockChain
|
blockchain *core.BlockChain
|
||||||
accountManager *accounts.Manager
|
accountManager *accounts.Manager
|
||||||
pow *ethash.Ethash
|
pow *ethash.Ethash
|
||||||
|
@ -293,7 +295,7 @@ func (s *Ethereum) APIs() []rpc.API {
|
||||||
}, {
|
}, {
|
||||||
Namespace: "personal",
|
Namespace: "personal",
|
||||||
Version: "1.0",
|
Version: "1.0",
|
||||||
Service: NewPrivateAccountAPI(s.accountManager),
|
Service: NewPrivateAccountAPI(s),
|
||||||
Public: false,
|
Public: false,
|
||||||
}, {
|
}, {
|
||||||
Namespace: "eth",
|
Namespace: "eth",
|
||||||
|
|
18
eth/bind.go
18
eth/bind.go
|
@ -19,7 +19,6 @@ package eth
|
||||||
import (
|
import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
|
@ -49,6 +48,17 @@ func NewContractBackend(eth *Ethereum) *ContractBackend {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
block := rpc.LatestBlockNumber
|
||||||
|
if pending {
|
||||||
|
block = rpc.PendingBlockNumber
|
||||||
|
}
|
||||||
|
out, err := b.bcapi.GetCode(contract, block)
|
||||||
|
return len(common.FromHex(out)) > 0, err
|
||||||
|
}
|
||||||
|
|
||||||
// ContractCall implements bind.ContractCaller executing an Ethereum contract
|
// ContractCall implements bind.ContractCaller executing an Ethereum contract
|
||||||
// call with the specified data as the input. The pending flag requests execution
|
// call with the specified data as the input. The pending flag requests execution
|
||||||
// against the pending block, not the stable head of the chain.
|
// against the pending block, not the stable head of the chain.
|
||||||
|
@ -64,9 +74,6 @@ func (b *ContractBackend) ContractCall(contract common.Address, data []byte, pen
|
||||||
}
|
}
|
||||||
// Execute the call and convert the output back to Go types
|
// Execute the call and convert the output back to Go types
|
||||||
out, err := b.bcapi.Call(args, block)
|
out, err := b.bcapi.Call(args, block)
|
||||||
if err == errNoCode {
|
|
||||||
err = bind.ErrNoCode
|
|
||||||
}
|
|
||||||
return common.FromHex(out), err
|
return common.FromHex(out), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,9 +102,6 @@ func (b *ContractBackend) EstimateGasLimit(sender common.Address, contract *comm
|
||||||
Value: *rpc.NewHexNumber(value),
|
Value: *rpc.NewHexNumber(value),
|
||||||
Data: common.ToHex(data),
|
Data: common.ToHex(data),
|
||||||
})
|
})
|
||||||
if err == errNoCode {
|
|
||||||
err = bind.ErrNoCode
|
|
||||||
}
|
|
||||||
return out.BigInt(), err
|
return out.BigInt(), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -233,6 +233,7 @@ func (s *PublicFilterAPI) newLogFilter(earliest, latest int64, addresses []commo
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs creates a subscription that fires for all new log that match the given filter criteria.
|
||||||
func (s *PublicFilterAPI) Logs(ctx context.Context, args NewFilterArgs) (rpc.Subscription, error) {
|
func (s *PublicFilterAPI) Logs(ctx context.Context, args NewFilterArgs) (rpc.Subscription, error) {
|
||||||
notifier, supported := rpc.NotifierFromContext(ctx)
|
notifier, supported := rpc.NotifierFromContext(ctx)
|
||||||
if !supported {
|
if !supported {
|
||||||
|
@ -291,12 +292,13 @@ type NewFilterArgs struct {
|
||||||
Topics [][]common.Hash
|
Topics [][]common.Hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalJSON sets *args fields with given data.
|
||||||
func (args *NewFilterArgs) UnmarshalJSON(data []byte) error {
|
func (args *NewFilterArgs) UnmarshalJSON(data []byte) error {
|
||||||
type input struct {
|
type input struct {
|
||||||
From *rpc.BlockNumber `json:"fromBlock"`
|
From *rpc.BlockNumber `json:"fromBlock"`
|
||||||
ToBlock *rpc.BlockNumber `json:"toBlock"`
|
ToBlock *rpc.BlockNumber `json:"toBlock"`
|
||||||
Addresses interface{} `json:"address"`
|
Addresses interface{} `json:"address"`
|
||||||
Topics interface{} `json:"topics"`
|
Topics []interface{} `json:"topics"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var raw input
|
var raw input
|
||||||
|
@ -321,7 +323,6 @@ func (args *NewFilterArgs) UnmarshalJSON(data []byte) error {
|
||||||
if raw.Addresses != nil {
|
if raw.Addresses != nil {
|
||||||
// raw.Address can contain a single address or an array of addresses
|
// raw.Address can contain a single address or an array of addresses
|
||||||
var addresses []common.Address
|
var addresses []common.Address
|
||||||
|
|
||||||
if strAddrs, ok := raw.Addresses.([]interface{}); ok {
|
if strAddrs, ok := raw.Addresses.([]interface{}); ok {
|
||||||
for i, addr := range strAddrs {
|
for i, addr := range strAddrs {
|
||||||
if strAddr, ok := addr.(string); ok {
|
if strAddr, ok := addr.(string); ok {
|
||||||
|
@ -352,57 +353,54 @@ func (args *NewFilterArgs) UnmarshalJSON(data []byte) error {
|
||||||
args.Addresses = addresses
|
args.Addresses = addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// helper function which parses a string to a topic hash
|
||||||
topicConverter := func(raw string) (common.Hash, error) {
|
topicConverter := func(raw string) (common.Hash, error) {
|
||||||
if len(raw) == 0 {
|
if len(raw) == 0 {
|
||||||
return common.Hash{}, nil
|
return common.Hash{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') {
|
if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') {
|
||||||
raw = raw[2:]
|
raw = raw[2:]
|
||||||
}
|
}
|
||||||
|
if len(raw) != 2 * common.HashLength {
|
||||||
|
return common.Hash{}, errors.New("invalid topic(s)")
|
||||||
|
}
|
||||||
if decAddr, err := hex.DecodeString(raw); err == nil {
|
if decAddr, err := hex.DecodeString(raw); err == nil {
|
||||||
return common.BytesToHash(decAddr), nil
|
return common.BytesToHash(decAddr), nil
|
||||||
}
|
}
|
||||||
|
return common.Hash{}, errors.New("invalid topic(s)")
|
||||||
return common.Hash{}, errors.New("invalid topic given")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// topics is an array consisting of strings or arrays of strings
|
// topics is an array consisting of strings and/or arrays of strings.
|
||||||
if raw.Topics != nil {
|
// JSON null values are converted to common.Hash{} and ignored by the filter manager.
|
||||||
topics, ok := raw.Topics.([]interface{})
|
if len(raw.Topics) > 0 {
|
||||||
if ok {
|
args.Topics = make([][]common.Hash, len(raw.Topics))
|
||||||
parsedTopics := make([][]common.Hash, len(topics))
|
for i, t := range raw.Topics {
|
||||||
for i, topic := range topics {
|
if t == nil { // ignore topic when matching logs
|
||||||
if topic == nil {
|
args.Topics[i] = []common.Hash{common.Hash{}}
|
||||||
parsedTopics[i] = []common.Hash{common.StringToHash("")}
|
} else if topic, ok := t.(string); ok { // match specific topic
|
||||||
} else if strTopic, ok := topic.(string); ok {
|
top, err := topicConverter(topic)
|
||||||
if t, err := topicConverter(strTopic); err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("invalid topic on index %d", i)
|
return err
|
||||||
} else {
|
|
||||||
parsedTopics[i] = []common.Hash{t}
|
|
||||||
}
|
}
|
||||||
} else if arrTopic, ok := topic.([]interface{}); ok {
|
args.Topics[i] = []common.Hash{top}
|
||||||
parsedTopics[i] = make([]common.Hash, len(arrTopic))
|
} else if topics, ok := t.([]interface{}); ok { // or case e.g. [null, "topic0", "topic1"]
|
||||||
for j := 0; j < len(parsedTopics[i]); i++ {
|
for _, rawTopic := range topics {
|
||||||
if arrTopic[j] == nil {
|
if rawTopic == nil {
|
||||||
parsedTopics[i][j] = common.StringToHash("")
|
args.Topics[i] = append(args.Topics[i], common.Hash{})
|
||||||
} else if str, ok := arrTopic[j].(string); ok {
|
} else if topic, ok := rawTopic.(string); ok {
|
||||||
if t, err := topicConverter(str); err != nil {
|
parsed, err := topicConverter(topic)
|
||||||
return fmt.Errorf("invalid topic on index %d", i)
|
if err != nil {
|
||||||
} else {
|
return err
|
||||||
parsedTopics[i] = []common.Hash{t}
|
|
||||||
}
|
}
|
||||||
|
args.Topics[i] = append(args.Topics[i], parsed)
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("topic[%d][%d] not a string", i, j)
|
return fmt.Errorf("invalid topic(s)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return fmt.Errorf("topic[%d] invalid", i)
|
return fmt.Errorf("invalid topic(s)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
args.Topics = parsedTopics
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -0,0 +1,198 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package filters_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/eth/filters"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnmarshalJSONNewFilterArgs(t *testing.T) {
|
||||||
|
var (
|
||||||
|
fromBlock rpc.BlockNumber = 0x123435
|
||||||
|
toBlock rpc.BlockNumber = 0xabcdef
|
||||||
|
address0 = common.StringToAddress("70c87d191324e6712a591f304b4eedef6ad9bb9d")
|
||||||
|
address1 = common.StringToAddress("9b2055d370f73ec7d8a03e965129118dc8f5bf83")
|
||||||
|
topic0 = common.HexToHash("3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1ca")
|
||||||
|
topic1 = common.HexToHash("9084a792d2f8b16a62b882fd56f7860c07bf5fa91dd8a2ae7e809e5180fef0b3")
|
||||||
|
topic2 = common.HexToHash("6ccae1c4af4152f460ff510e573399795dfab5dcf1fa60d1f33ac8fdc1e480ce")
|
||||||
|
nullTopic = common.Hash{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// default values
|
||||||
|
var test0 filters.NewFilterArgs
|
||||||
|
if err := json.Unmarshal([]byte("{}"), &test0); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if test0.FromBlock != rpc.LatestBlockNumber {
|
||||||
|
t.Fatalf("expected %d, got %d", rpc.LatestBlockNumber, test0.FromBlock)
|
||||||
|
}
|
||||||
|
if test0.ToBlock != rpc.LatestBlockNumber {
|
||||||
|
t.Fatalf("expected %d, got %d", rpc.LatestBlockNumber, test0.ToBlock)
|
||||||
|
}
|
||||||
|
if len(test0.Addresses) != 0 {
|
||||||
|
t.Fatalf("expected 0 addresses, got %d", len(test0.Addresses))
|
||||||
|
}
|
||||||
|
if len(test0.Topics) != 0 {
|
||||||
|
t.Fatalf("expected 0 topics, got %d topics", len(test0.Topics))
|
||||||
|
}
|
||||||
|
|
||||||
|
// from, to block number
|
||||||
|
var test1 filters.NewFilterArgs
|
||||||
|
vector := fmt.Sprintf(`{"fromBlock":"0x%x","toBlock":"0x%x"}`, fromBlock, toBlock)
|
||||||
|
if err := json.Unmarshal([]byte(vector), &test1); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if test1.FromBlock != fromBlock {
|
||||||
|
t.Fatalf("expected FromBlock %d, got %d", fromBlock, test1.FromBlock)
|
||||||
|
}
|
||||||
|
if test1.ToBlock != toBlock {
|
||||||
|
t.Fatalf("expected ToBlock %d, got %d", toBlock, test1.ToBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// single address
|
||||||
|
var test2 filters.NewFilterArgs
|
||||||
|
vector = fmt.Sprintf(`{"address": "%s"}`, address0.Hex())
|
||||||
|
if err := json.Unmarshal([]byte(vector), &test2); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(test2.Addresses) != 1 {
|
||||||
|
t.Fatalf("expected 1 address, got %d address(es)", len(test2.Addresses))
|
||||||
|
}
|
||||||
|
if test2.Addresses[0] != address0 {
|
||||||
|
t.Fatalf("expected address %x, got %x", address0, test2.Addresses[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiple address
|
||||||
|
var test3 filters.NewFilterArgs
|
||||||
|
vector = fmt.Sprintf(`{"address": ["%s", "%s"]}`, address0.Hex(), address1.Hex())
|
||||||
|
if err := json.Unmarshal([]byte(vector), &test3); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(test3.Addresses) != 2 {
|
||||||
|
t.Fatalf("expected 2 addresses, got %d address(es)", len(test3.Addresses))
|
||||||
|
}
|
||||||
|
if test3.Addresses[0] != address0 {
|
||||||
|
t.Fatalf("expected address %x, got %x", address0, test3.Addresses[0])
|
||||||
|
}
|
||||||
|
if test3.Addresses[1] != address1 {
|
||||||
|
t.Fatalf("expected address %x, got %x", address1, test3.Addresses[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// single topic
|
||||||
|
var test4 filters.NewFilterArgs
|
||||||
|
vector = fmt.Sprintf(`{"topics": ["%s"]}`, topic0.Hex())
|
||||||
|
if err := json.Unmarshal([]byte(vector), &test4); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(test4.Topics) != 1 {
|
||||||
|
t.Fatalf("expected 1 topic, got %d", len(test4.Topics))
|
||||||
|
}
|
||||||
|
if len(test4.Topics[0]) != 1 {
|
||||||
|
t.Fatalf("expected len(topics[0]) to be 1, got %d", len(test4.Topics[0]))
|
||||||
|
}
|
||||||
|
if test4.Topics[0][0] != topic0 {
|
||||||
|
t.Fatalf("got %x, expected %x", test4.Topics[0][0], topic0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test multiple "AND" topics
|
||||||
|
var test5 filters.NewFilterArgs
|
||||||
|
vector = fmt.Sprintf(`{"topics": ["%s", "%s"]}`, topic0.Hex(), topic1.Hex())
|
||||||
|
if err := json.Unmarshal([]byte(vector), &test5); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(test5.Topics) != 2 {
|
||||||
|
t.Fatalf("expected 2 topics, got %d", len(test5.Topics))
|
||||||
|
}
|
||||||
|
if len(test5.Topics[0]) != 1 {
|
||||||
|
t.Fatalf("expected 1 topic, got %d", len(test5.Topics[0]))
|
||||||
|
}
|
||||||
|
if test5.Topics[0][0] != topic0 {
|
||||||
|
t.Fatalf("got %x, expected %x", test5.Topics[0][0], topic0)
|
||||||
|
}
|
||||||
|
if len(test5.Topics[1]) != 1 {
|
||||||
|
t.Fatalf("expected 1 topic, got %d", len(test5.Topics[1]))
|
||||||
|
}
|
||||||
|
if test5.Topics[1][0] != topic1 {
|
||||||
|
t.Fatalf("got %x, expected %x", test5.Topics[1][0], topic1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test optional topic
|
||||||
|
var test6 filters.NewFilterArgs
|
||||||
|
vector = fmt.Sprintf(`{"topics": ["%s", null, "%s"]}`, topic0.Hex(), topic2.Hex())
|
||||||
|
if err := json.Unmarshal([]byte(vector), &test6); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(test6.Topics) != 3 {
|
||||||
|
t.Fatalf("expected 3 topics, got %d", len(test6.Topics))
|
||||||
|
}
|
||||||
|
if len(test6.Topics[0]) != 1 {
|
||||||
|
t.Fatalf("expected 1 topic, got %d", len(test6.Topics[0]))
|
||||||
|
}
|
||||||
|
if test6.Topics[0][0] != topic0 {
|
||||||
|
t.Fatalf("got %x, expected %x", test6.Topics[0][0], topic0)
|
||||||
|
}
|
||||||
|
if len(test6.Topics[1]) != 1 {
|
||||||
|
t.Fatalf("expected 1 topic, got %d", len(test6.Topics[1]))
|
||||||
|
}
|
||||||
|
if test6.Topics[1][0] != nullTopic {
|
||||||
|
t.Fatalf("got %x, expected empty hash", test6.Topics[1][0])
|
||||||
|
}
|
||||||
|
if len(test6.Topics[2]) != 1 {
|
||||||
|
t.Fatalf("expected 1 topic, got %d", len(test6.Topics[2]))
|
||||||
|
}
|
||||||
|
if test6.Topics[2][0] != topic2 {
|
||||||
|
t.Fatalf("got %x, expected %x", test6.Topics[2][0], topic2)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test OR topics
|
||||||
|
var test7 filters.NewFilterArgs
|
||||||
|
vector = fmt.Sprintf(`{"topics": [["%s", "%s"], null, ["%s", null]]}`, topic0.Hex(), topic1.Hex(), topic2.Hex())
|
||||||
|
if err := json.Unmarshal([]byte(vector), &test7); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(test7.Topics) != 3 {
|
||||||
|
t.Fatalf("expected 3 topics, got %d topics", len(test7.Topics))
|
||||||
|
}
|
||||||
|
if len(test7.Topics[0]) != 2 {
|
||||||
|
t.Fatalf("expected 2 topics, got %d topics", len(test7.Topics[0]))
|
||||||
|
}
|
||||||
|
if test7.Topics[0][0] != topic0 || test7.Topics[0][1] != topic1 {
|
||||||
|
t.Fatalf("invalid topics expected [%x,%x], got [%x,%x]",
|
||||||
|
topic0, topic1, test7.Topics[0][0], test7.Topics[0][1],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if len(test7.Topics[1]) != 1 {
|
||||||
|
t.Fatalf("expected 1 topic, got %d topics", len(test7.Topics[1]))
|
||||||
|
}
|
||||||
|
if test7.Topics[1][0] != nullTopic {
|
||||||
|
t.Fatalf("expected empty hash, got %x", test7.Topics[1][0])
|
||||||
|
}
|
||||||
|
if len(test7.Topics[2]) != 2 {
|
||||||
|
t.Fatalf("expected 2 topics, got %d topics", len(test7.Topics[2]))
|
||||||
|
}
|
||||||
|
if test7.Topics[2][0] != topic2 || test7.Topics[2][1] != nullTopic {
|
||||||
|
t.Fatalf("invalid topics expected [%x,%x], got [%x,%x]",
|
||||||
|
topic2, nullTopic, test7.Topics[2][0], test7.Topics[2][1],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -176,20 +176,6 @@ web3._extend({
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
|
|
||||||
const Personal_JS = `
|
|
||||||
web3._extend({
|
|
||||||
property: 'personal',
|
|
||||||
methods:
|
|
||||||
[
|
|
||||||
new web3._extend.Method({
|
|
||||||
name: 'importRawKey',
|
|
||||||
call: 'personal_importRawKey',
|
|
||||||
params: 2
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
`
|
|
||||||
|
|
||||||
const Eth_JS = `
|
const Eth_JS = `
|
||||||
web3._extend({
|
web3._extend({
|
||||||
property: 'eth',
|
property: 'eth',
|
||||||
|
@ -244,20 +230,6 @@ web3._extend({
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
|
|
||||||
const Net_JS = `
|
|
||||||
web3._extend({
|
|
||||||
property: 'net',
|
|
||||||
methods: [],
|
|
||||||
properties:
|
|
||||||
[
|
|
||||||
new web3._extend.Property({
|
|
||||||
name: 'version',
|
|
||||||
getter: 'net_version'
|
|
||||||
})
|
|
||||||
]
|
|
||||||
});
|
|
||||||
`
|
|
||||||
|
|
||||||
const Debug_JS = `
|
const Debug_JS = `
|
||||||
web3._extend({
|
web3._extend({
|
||||||
property: 'debug',
|
property: 'debug',
|
||||||
|
@ -463,6 +435,54 @@ web3._extend({
|
||||||
});
|
});
|
||||||
`
|
`
|
||||||
|
|
||||||
|
const Net_JS = `
|
||||||
|
web3._extend({
|
||||||
|
property: 'net',
|
||||||
|
methods: [],
|
||||||
|
properties:
|
||||||
|
[
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'version',
|
||||||
|
getter: 'net_version'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
|
|
||||||
|
const Personal_JS = `
|
||||||
|
web3._extend({
|
||||||
|
property: 'personal',
|
||||||
|
methods:
|
||||||
|
[
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'importRawKey',
|
||||||
|
call: 'personal_importRawKey',
|
||||||
|
params: 2
|
||||||
|
}),
|
||||||
|
new web3._extend.Method({
|
||||||
|
name: 'signAndSendTransaction',
|
||||||
|
call: 'personal_signAndSendTransaction',
|
||||||
|
params: 2,
|
||||||
|
inputFormatter: [web3._extend.formatters.inputTransactionFormatter, null]
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
|
|
||||||
|
const RPC_JS = `
|
||||||
|
web3._extend({
|
||||||
|
property: 'rpc',
|
||||||
|
methods: [],
|
||||||
|
properties:
|
||||||
|
[
|
||||||
|
new web3._extend.Property({
|
||||||
|
name: 'modules',
|
||||||
|
getter: 'rpc_modules'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
});
|
||||||
|
`
|
||||||
|
|
||||||
const Shh_JS = `
|
const Shh_JS = `
|
||||||
web3._extend({
|
web3._extend({
|
||||||
property: 'shh',
|
property: 'shh',
|
||||||
|
|
Loading…
Reference in New Issue