Compare commits

..

10 Commits

Author SHA1 Message Date
Péter Szilágyi
a269a713d6 Merge pull request #2606 from ethereum/release/1.4
VERSION, cmd/geth: bumped version 1.4.5
2016-05-24 11:31:56 +03:00
Péter Szilágyi
27df30f30f VERSION, cmd/geth: bumped version 1.4.5 2016-05-24 11:26:21 +03:00
Jeffrey Wilcke
311f5a0ed1 Merge branch 'release/1.4' 2016-05-24 10:03:21 +02:00
Péter Szilágyi
68ae6b52e9 [release 1.4.5] accounts/abi: fix abi test for go vet...
(cherry picked from commit 251b3c6406)
2016-05-24 09:45:40 +02:00
Péter Szilágyi
1776c717bf [release 1.4.5] accounts/abi/bind, eth: rely on getCode for sanity checks, not estimate and call
(cherry picked from commit 1580ec1804)
2016-05-24 09:33:15 +02:00
Jeffrey Wilcke
0f6e3e873a [release 1.4.5] eth: fixed regression in eth_signTransaction fixes #2578
Sign transaction returned the unsigned transaction rather than the
signed one.

(cherry picked from commit 4b1a7d3868)
2016-05-24 09:33:10 +02:00
Bas van Kervel
7a7a5acc9f [release 1.4.5] eth/filter: bugfix which can cause a nil pointer crash when parsing filter arguments
(cherry picked from commit 67cd4ee8d2)
2016-05-24 09:33:05 +02:00
Felix Lange
66d74dfb75 [release 1.4.5] cmd/geth: fix console history exclusion
Calls to 'personal' API should be excluded from console history because
they can be called with an account passphrase as argument. The check for
such calls was inverted and didn't work.

(cherry picked from commit 86da6feb40)

Conflicts:
	cmd/geth/js.go
2016-05-24 09:32:55 +02:00
Bas van Kervel
b950a2977c [release/1.4.5] eth: add new RPC method (personal.) SignAndSendTransaction
(cherry picked from commit 64a6c2c1b6)

Conflicts:
	cmd/geth/js.go
	internal/web3ext/web3ext.go
2016-05-24 09:32:45 +02:00
Jeffrey Wilcke
8ea3c88e44 Fake commit to restart the build servers
Fake commit to hopefully fix the PPA issue.
2016-05-18 01:10:24 +02:00
18 changed files with 563 additions and 176 deletions

View File

@@ -82,3 +82,5 @@ included in our repository in the `COPYING.LESSER` file.
The go-ethereum binaries (i.e. all code inside of the `cmd` directory) is licensed under the
[GNU General Public License v3.0](http://www.gnu.org/licenses/gpl-3.0.en.html), also included
in our repository in the `COPYING` file.

View File

@@ -1 +1 @@
1.4.4
1.4.5

View File

@@ -305,10 +305,10 @@ func TestUnpackSetInterfaceSlice(t *testing.T) {
t.Fatal(err)
}
if *var1 != 1 {
t.Errorf("expected var1 to be 1, got", *var1)
t.Error("expected var1 to be 1, got", *var1)
}
if *var2 != 2 {
t.Errorf("expected var2 to be 2, got", *var2)
t.Error("expected var2 to be 2, got", *var2)
}
out = []interface{}{var1}

View File

@@ -27,15 +27,16 @@ import (
// 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")
// ContractCaller defines the methods needed to allow operating with contract on a read
// only basis.
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
// the input. The pending flag requests execution against the pending block, not
// the stable head of the chain.
@@ -55,6 +56,11 @@ type ContractTransactor interface {
// execution of a transaction.
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
// 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
@@ -68,7 +74,38 @@ type ContractTransactor interface {
// ContractBackend defines the methods needed to allow operating with contract
// 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 {
ContractCaller
ContractTransactor
// 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
// 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
}

View File

@@ -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) {
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") }

View File

@@ -111,6 +111,26 @@ func (b *rpcBackend) request(method string, params []interface{}) (json.RawMessa
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
// 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) {

View File

@@ -78,6 +78,16 @@ func (b *SimulatedBackend) Rollback() {
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
// contract with the given input data.
func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pending bool) ([]byte, error) {

View File

@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
"math/big"
"sync/atomic"
"github.com/ethereum/go-ethereum/accounts/abi"
"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
caller ContractCaller // Read 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
@@ -96,6 +100,19 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
if opts == nil {
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
input, err := c.abi.Pack(method, params...)
if err != nil {
@@ -153,6 +170,16 @@ func (c *BoundContract) transact(opts *TransactOpts, contract *common.Address, i
}
gasLimit := opts.GasLimit
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)
if err != nil {
return nil, fmt.Errorf("failed to exstimate gas needed: %v", err)

View File

@@ -147,9 +147,21 @@ func (am *Manager) Sign(addr common.Address, hash []byte) (signature []byte, err
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.
func (am *Manager) Unlock(a Account, keyAuth string) error {
return am.TimedUnlock(a, keyAuth, 0)
func (am *Manager) Unlock(a Account, passphrase string) error {
return am.TimedUnlock(a, passphrase, 0)
}
// Lock removes the private key with the given address from memory.

View File

@@ -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) {
dir, am := tmpManager(t, true)
defer os.RemoveAll(dir)

View File

@@ -41,8 +41,7 @@ import (
)
var (
passwordRegexp = regexp.MustCompile("personal.[nu]")
leadingSpace = regexp.MustCompile("^ ")
passwordRegexp = regexp.MustCompile("personal.[nus]")
onlyws = regexp.MustCompile("^\\s*$")
exit = regexp.MustCompile("^\\s*exit\\s*;*\\s*$")
)
@@ -361,7 +360,7 @@ func (self *jsre) interactive() {
str += input + "\n"
self.setIndent()
if indentCount <= 0 {
if mustLogInHistory(str) {
if !excludeFromHistory(str) {
utils.Stdin.AppendHistory(str[:len(str)-1])
}
self.parseInput(str)
@@ -371,10 +370,8 @@ func (self *jsre) interactive() {
}
}
func mustLogInHistory(input string) bool {
return len(input) == 0 ||
passwordRegexp.MatchString(input) ||
!leadingSpace.MatchString(input)
func excludeFromHistory(input string) bool {
return len(input) == 0 || input[0] == ' ' || passwordRegexp.MatchString(input)
}
func (self *jsre) withHistory(datadir string, op func(*os.File)) {

View File

@@ -50,7 +50,7 @@ const (
clientIdentifier = "Geth" // Client identifier to advertise over the network
versionMajor = 1 // Major version component of the current release
versionMinor = 4 // Minor version component of the current release
versionPatch = 4 // Patch version component of the current release
versionPatch = 5 // Patch version component of the current release
versionMeta = "stable" // Version metadata to append to the version string
versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle

View File

@@ -52,15 +52,6 @@ import (
"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)
// 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()
}
// see Etherbase
// Coinbase is the address that mining rewards will be send to (alias for Etherbase)
func (s *PublicEthereumAPI) Coinbase() (common.Address, error) {
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[1], 32 bytes hex encoded seed hash used for DAG
// 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 err := s.e.StartMining(0, ""); err != nil {
return nil, err
return work, err
}
}
if work, err := s.agent.GetWork(); err == nil {
return work[:], nil
} else {
glog.Infof("%v\n", err)
if work, err = s.agent.GetWork(); err == nil {
return
}
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
@@ -423,14 +413,23 @@ func (s *PublicAccountAPI) Accounts() []accounts.Account {
}
// 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 {
am *accounts.Manager
am *accounts.Manager
txPool *core.TxPool
txMu *sync.Mutex
gpo *GasPriceOracle
}
// NewPrivateAccountAPI create a new PrivateAccountAPI.
func NewPrivateAccountAPI(am *accounts.Manager) *PrivateAccountAPI {
return &PrivateAccountAPI{am}
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.
@@ -452,6 +451,8 @@ func (s *PrivateAccountAPI) NewAccount(password string) (common.Address, error)
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 {
@@ -482,6 +483,34 @@ 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 {
@@ -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
s.muNewBlockSubscriptions.Lock()
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)
} 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
}
s.muNewBlockSubscriptions.Unlock()
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) 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"`
@@ -717,12 +746,6 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st
}
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
var from *state.StateObject
if args.From == (common.Address{}) {
@@ -911,7 +934,7 @@ type PublicTransactionPoolAPI struct {
miner *miner.Miner
am *accounts.Manager
txPool *core.TxPool
txMu sync.Mutex
txMu *sync.Mutex
muPendingTxSubs sync.Mutex
pendingTxSubs map[string]rpc.Subscription
}
@@ -925,6 +948,7 @@ func NewPublicTransactionPoolAPI(e *Ethereum) *PublicTransactionPoolAPI {
bc: e.blockchain,
am: e.accountManager,
txPool: e.txPool,
txMu: &e.txMu,
miner: e.miner,
pendingTxSubs: make(map[string]rpc.Subscription),
}
@@ -1124,6 +1148,7 @@ func (s *PublicTransactionPoolAPI) sign(addr common.Address, tx *types.Transacti
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"`
@@ -1134,18 +1159,47 @@ type SendTxArgs struct {
Nonce *rpc.HexNumber `json:"nonce"`
}
// SendTransaction will create a transaction for the given transaction argument, sign it and submit it to the
// transaction pool.
func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash, error) {
// 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(s.gpo.SuggestPrice())
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()
@@ -1155,32 +1209,18 @@ func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash
}
var tx *types.Transaction
contractCreation := (args.To == nil)
if contractCreation {
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)
signature, err := s.am.Sign(args.From, tx.SigHash().Bytes())
if err != nil {
return common.Hash{}, err
}
s.txPool.SetLocal(signedTx)
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
return submitTransaction(s.txPool, tx, signature)
}
// 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
}
// SignTransactionArgs represents the arguments to sign a transaction.
type SignTransactionArgs struct {
From common.Address
To *common.Address
@@ -1243,6 +1284,7 @@ type Tx struct {
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"`
@@ -1283,8 +1325,7 @@ func (tx *Tx) UnmarshalJSON(b []byte) (err error) {
tx.GasPrice = rpc.NewHexNumber(int64(50000000000))
}
contractCreation := (req.To == nil)
if contractCreation {
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)
@@ -1293,6 +1334,7 @@ func (tx *Tx) UnmarshalJSON(b []byte) (err error) {
return nil
}
// SignTransactionResult represents a RLP encoded signed transaction.
type SignTransactionResult struct {
Raw string `json:"raw"`
Tx *Tx `json:"tx"`
@@ -1335,9 +1377,7 @@ func (s *PublicTransactionPoolAPI) SignTransaction(args SignTransactionArgs) (*S
}
var tx *types.Transaction
contractCreation := (args.To == nil)
if contractCreation {
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))
@@ -1353,14 +1393,14 @@ func (s *PublicTransactionPoolAPI) SignTransaction(args SignTransactionArgs) (*S
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
// the accounts this node manages.
func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
pending := s.txPool.GetTransactions()
transactions := make([]*RPCTransaction, 0)
transactions := make([]*RPCTransaction, 0, len(pending))
for _, tx := range pending {
from, _ := tx.FromFrontier()
if s.am.HasAddress(from) {
@@ -1370,7 +1410,7 @@ func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
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.
func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (rpc.Subscription, error) {
notifier, supported := rpc.NotifierFromContext(ctx)
@@ -1410,8 +1450,7 @@ func (s *PublicTransactionPoolAPI) Resend(tx Tx, gasPrice, gasLimit *rpc.HexNumb
}
var newTx *types.Transaction
contractCreation := (tx.tx.To() == nil)
if contractCreation {
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())
@@ -1612,7 +1651,7 @@ func (api *PrivateDebugAPI) ChaindbProperty(property string) (string, error) {
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.
type BlockTraceResult struct {
Validated bool `json:"validated"`
@@ -1647,7 +1686,7 @@ func (api *PrivateDebugAPI) TraceBlockFromFile(file string, config *vm.Config) B
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 {
// Fetch the block that we aim to reprocess
block := api.eth.BlockChain().GetBlockByNumber(number)
@@ -1752,15 +1791,6 @@ type structLogRes struct {
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
func formatLogs(structLogs []vm.StructLog) []structLogRes {
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
// 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 {
logger = new(vm.LogConfig)
}
// 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 {
return nil, fmt.Errorf("transaction %x not found", txHash)
}
block := s.eth.BlockChain().GetBlock(blockHash)
block := api.eth.BlockChain().GetBlock(blockHash)
if block == nil {
return nil, fmt.Errorf("block %x not found", blockHash)
}
// 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 {
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 {
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
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()))
if err != nil {
return nil, fmt.Errorf("mutation failed: %v", err)
@@ -1849,7 +1879,7 @@ func (s *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogCon
continue
}
// 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()))
if err != nil {
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")
}
// 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)
@@ -1931,12 +1962,12 @@ func (s *PublicNetAPI) Listening() bool {
return true // always listening
}
// Peercount returns the number of connected peers
// PeerCount returns the number of connected peers
func (s *PublicNetAPI) PeerCount() *rpc.HexNumber {
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 {
return fmt.Sprintf("%d", s.networkVersion)
}

View File

@@ -26,6 +26,7 @@ import (
"path/filepath"
"regexp"
"strings"
"sync"
"time"
"github.com/ethereum/ethash"
@@ -113,6 +114,7 @@ type Ethereum struct {
// Handlers
txPool *core.TxPool
txMu sync.Mutex
blockchain *core.BlockChain
accountManager *accounts.Manager
pow *ethash.Ethash
@@ -293,7 +295,7 @@ func (s *Ethereum) APIs() []rpc.API {
}, {
Namespace: "personal",
Version: "1.0",
Service: NewPrivateAccountAPI(s.accountManager),
Service: NewPrivateAccountAPI(s),
Public: false,
}, {
Namespace: "eth",

View File

@@ -19,7 +19,6 @@ package eth
import (
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"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
// call with the specified data as the input. The pending flag requests execution
// 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
out, err := b.bcapi.Call(args, block)
if err == errNoCode {
err = bind.ErrNoCode
}
return common.FromHex(out), err
}
@@ -95,9 +102,6 @@ func (b *ContractBackend) EstimateGasLimit(sender common.Address, contract *comm
Value: *rpc.NewHexNumber(value),
Data: common.ToHex(data),
})
if err == errNoCode {
err = bind.ErrNoCode
}
return out.BigInt(), err
}

View File

@@ -233,6 +233,7 @@ func (s *PublicFilterAPI) newLogFilter(earliest, latest int64, addresses []commo
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) {
notifier, supported := rpc.NotifierFromContext(ctx)
if !supported {
@@ -291,12 +292,13 @@ type NewFilterArgs struct {
Topics [][]common.Hash
}
// UnmarshalJSON sets *args fields with given data.
func (args *NewFilterArgs) UnmarshalJSON(data []byte) error {
type input struct {
From *rpc.BlockNumber `json:"fromBlock"`
ToBlock *rpc.BlockNumber `json:"toBlock"`
Addresses interface{} `json:"address"`
Topics interface{} `json:"topics"`
Topics []interface{} `json:"topics"`
}
var raw input
@@ -321,7 +323,6 @@ func (args *NewFilterArgs) UnmarshalJSON(data []byte) error {
if raw.Addresses != nil {
// raw.Address can contain a single address or an array of addresses
var addresses []common.Address
if strAddrs, ok := raw.Addresses.([]interface{}); ok {
for i, addr := range strAddrs {
if strAddr, ok := addr.(string); ok {
@@ -352,56 +353,53 @@ func (args *NewFilterArgs) UnmarshalJSON(data []byte) error {
args.Addresses = addresses
}
// helper function which parses a string to a topic hash
topicConverter := func(raw string) (common.Hash, error) {
if len(raw) == 0 {
return common.Hash{}, nil
}
if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') {
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 {
return common.BytesToHash(decAddr), nil
}
return common.Hash{}, errors.New("invalid topic given")
return common.Hash{}, errors.New("invalid topic(s)")
}
// topics is an array consisting of strings or arrays of strings
if raw.Topics != nil {
topics, ok := raw.Topics.([]interface{})
if ok {
parsedTopics := make([][]common.Hash, len(topics))
for i, topic := range topics {
if topic == nil {
parsedTopics[i] = []common.Hash{common.StringToHash("")}
} else if strTopic, ok := topic.(string); ok {
if t, err := topicConverter(strTopic); err != nil {
return fmt.Errorf("invalid topic on index %d", i)
} else {
parsedTopics[i] = []common.Hash{t}
}
} else if arrTopic, ok := topic.([]interface{}); ok {
parsedTopics[i] = make([]common.Hash, len(arrTopic))
for j := 0; j < len(parsedTopics[i]); i++ {
if arrTopic[j] == nil {
parsedTopics[i][j] = common.StringToHash("")
} else if str, ok := arrTopic[j].(string); ok {
if t, err := topicConverter(str); err != nil {
return fmt.Errorf("invalid topic on index %d", i)
} else {
parsedTopics[i] = []common.Hash{t}
}
} else {
return fmt.Errorf("topic[%d][%d] not a string", i, j)
}
}
} else {
return fmt.Errorf("topic[%d] invalid", i)
// topics is an array consisting of strings and/or arrays of strings.
// JSON null values are converted to common.Hash{} and ignored by the filter manager.
if len(raw.Topics) > 0 {
args.Topics = make([][]common.Hash, len(raw.Topics))
for i, t := range raw.Topics {
if t == nil { // ignore topic when matching logs
args.Topics[i] = []common.Hash{common.Hash{}}
} else if topic, ok := t.(string); ok { // match specific topic
top, err := topicConverter(topic)
if err != nil {
return err
}
args.Topics[i] = []common.Hash{top}
} else if topics, ok := t.([]interface{}); ok { // or case e.g. [null, "topic0", "topic1"]
for _, rawTopic := range topics {
if rawTopic == nil {
args.Topics[i] = append(args.Topics[i], common.Hash{})
} else if topic, ok := rawTopic.(string); ok {
parsed, err := topicConverter(topic)
if err != nil {
return err
}
args.Topics[i] = append(args.Topics[i], parsed)
} else {
return fmt.Errorf("invalid topic(s)")
}
}
} else {
return fmt.Errorf("invalid topic(s)")
}
args.Topics = parsedTopics
}
}

198
eth/filters/api_test.go Normal file
View File

@@ -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],
)
}
}

View File

@@ -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 = `
web3._extend({
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 = `
web3._extend({
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 = `
web3._extend({
property: 'shh',