parent
97ed44db29
commit
89156bd899
@ -0,0 +1,35 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package core |
||||||
|
|
||||||
|
import "errors" |
||||||
|
|
||||||
|
var ( |
||||||
|
// ErrKnownBlock is returned when a block to import is already known locally.
|
||||||
|
ErrKnownBlock = errors.New("block already known") |
||||||
|
|
||||||
|
// ErrGasLimitReached is returned by the gas pool if the amount of gas required
|
||||||
|
// by a transaction is higher than what's left in the block.
|
||||||
|
ErrGasLimitReached = errors.New("gas limit reached") |
||||||
|
|
||||||
|
// ErrBlacklistedHash is returned if a block to import is on the blacklist.
|
||||||
|
ErrBlacklistedHash = errors.New("blacklisted hash") |
||||||
|
|
||||||
|
// ErrNonceTooHigh is returned if the nonce of a transaction is higher than the
|
||||||
|
// next one expected based on the local chain.
|
||||||
|
ErrNonceTooHigh = errors.New("nonce too high") |
||||||
|
) |
@ -0,0 +1,48 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
) |
||||||
|
|
||||||
|
// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
|
||||||
|
type NewTxsEvent struct{ Txs []*types.Transaction } |
||||||
|
|
||||||
|
// PendingLogsEvent is posted pre mining and notifies of pending logs.
|
||||||
|
type PendingLogsEvent struct { |
||||||
|
Logs []*types.Log |
||||||
|
} |
||||||
|
|
||||||
|
// NewMinedBlockEvent is posted when a block has been imported.
|
||||||
|
type NewMinedBlockEvent struct{ Block *types.Block } |
||||||
|
|
||||||
|
// RemovedLogsEvent is posted when a reorg happens
|
||||||
|
type RemovedLogsEvent struct{ Logs []*types.Log } |
||||||
|
|
||||||
|
type ChainEvent struct { |
||||||
|
Block *types.Block |
||||||
|
Hash common.Hash |
||||||
|
Logs []*types.Log |
||||||
|
} |
||||||
|
|
||||||
|
type ChainSideEvent struct { |
||||||
|
Block *types.Block |
||||||
|
} |
||||||
|
|
||||||
|
type ChainHeadEvent struct{ Block *types.Block } |
@ -0,0 +1,54 @@ |
|||||||
|
// Copyright 2015 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"math" |
||||||
|
) |
||||||
|
|
||||||
|
// GasPool tracks the amount of gas available during execution of the transactions
|
||||||
|
// in a block. The zero value is a pool with zero gas available.
|
||||||
|
type GasPool uint64 |
||||||
|
|
||||||
|
// AddGas makes gas available for execution.
|
||||||
|
func (gp *GasPool) AddGas(amount uint64) *GasPool { |
||||||
|
if uint64(*gp) > math.MaxUint64-amount { |
||||||
|
panic("gas pool pushed above uint64") |
||||||
|
} |
||||||
|
*(*uint64)(gp) += amount |
||||||
|
return gp |
||||||
|
} |
||||||
|
|
||||||
|
// SubGas deducts the given amount from the pool if enough gas is
|
||||||
|
// available and returns an error otherwise.
|
||||||
|
func (gp *GasPool) SubGas(amount uint64) error { |
||||||
|
if uint64(*gp) < amount { |
||||||
|
return ErrGasLimitReached |
||||||
|
} |
||||||
|
*(*uint64)(gp) -= amount |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Gas returns the amount of gas remaining in the pool.
|
||||||
|
func (gp *GasPool) Gas() uint64 { |
||||||
|
return uint64(*gp) |
||||||
|
} |
||||||
|
|
||||||
|
func (gp *GasPool) String() string { |
||||||
|
return fmt.Sprintf("%d", *gp) |
||||||
|
} |
@ -0,0 +1,250 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"math" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/vm" |
||||||
|
"github.com/ethereum/go-ethereum/log" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
errInsufficientBalanceForGas = errors.New("insufficient balance to pay for gas") |
||||||
|
) |
||||||
|
|
||||||
|
/* |
||||||
|
The State Transitioning Model |
||||||
|
|
||||||
|
A state transition is a change made when a transaction is applied to the current world state |
||||||
|
The state transitioning model does all the necessary work to work out a valid new state root. |
||||||
|
|
||||||
|
1) Nonce handling |
||||||
|
2) Pre pay gas |
||||||
|
3) Create a new state object if the recipient is \0*32 |
||||||
|
4) Value transfer |
||||||
|
== If contract creation == |
||||||
|
4a) Attempt to run transaction data |
||||||
|
4b) If valid, use result as code for the new state object |
||||||
|
== end == |
||||||
|
5) Run Script section |
||||||
|
6) Derive new state root |
||||||
|
*/ |
||||||
|
type StateTransition struct { |
||||||
|
gp *GasPool |
||||||
|
msg Message |
||||||
|
gas uint64 |
||||||
|
gasPrice *big.Int |
||||||
|
initialGas uint64 |
||||||
|
value *big.Int |
||||||
|
data []byte |
||||||
|
state vm.StateDB |
||||||
|
evm *vm.EVM |
||||||
|
} |
||||||
|
|
||||||
|
// Message represents a message sent to a contract.
|
||||||
|
type Message interface { |
||||||
|
From() common.Address |
||||||
|
//FromFrontier() (common.Address, error)
|
||||||
|
To() *common.Address |
||||||
|
|
||||||
|
GasPrice() *big.Int |
||||||
|
Gas() uint64 |
||||||
|
Value() *big.Int |
||||||
|
|
||||||
|
Nonce() uint64 |
||||||
|
CheckNonce() bool |
||||||
|
Data() []byte |
||||||
|
} |
||||||
|
|
||||||
|
// IntrinsicGas computes the 'intrinsic gas' for a message with the given data.
|
||||||
|
func IntrinsicGas(data []byte, contractCreation, homestead bool) (uint64, error) { |
||||||
|
// Set the starting gas for the raw transaction
|
||||||
|
var gas uint64 |
||||||
|
if contractCreation && homestead { |
||||||
|
gas = params.TxGasContractCreation |
||||||
|
} else { |
||||||
|
gas = params.TxGas |
||||||
|
} |
||||||
|
// Bump the required gas by the amount of transactional data
|
||||||
|
if len(data) > 0 { |
||||||
|
// Zero and non-zero bytes are priced differently
|
||||||
|
var nz uint64 |
||||||
|
for _, byt := range data { |
||||||
|
if byt != 0 { |
||||||
|
nz++ |
||||||
|
} |
||||||
|
} |
||||||
|
// Make sure we don't exceed uint64 for all data combinations
|
||||||
|
if (math.MaxUint64-gas)/params.TxDataNonZeroGas < nz { |
||||||
|
return 0, vm.ErrOutOfGas |
||||||
|
} |
||||||
|
gas += nz * params.TxDataNonZeroGas |
||||||
|
|
||||||
|
z := uint64(len(data)) - nz |
||||||
|
if (math.MaxUint64-gas)/params.TxDataZeroGas < z { |
||||||
|
return 0, vm.ErrOutOfGas |
||||||
|
} |
||||||
|
gas += z * params.TxDataZeroGas |
||||||
|
} |
||||||
|
return gas, nil |
||||||
|
} |
||||||
|
|
||||||
|
// NewStateTransition initialises and returns a new state transition object.
|
||||||
|
func NewStateTransition(evm *vm.EVM, msg Message, gp *GasPool) *StateTransition { |
||||||
|
return &StateTransition{ |
||||||
|
gp: gp, |
||||||
|
evm: evm, |
||||||
|
msg: msg, |
||||||
|
gasPrice: msg.GasPrice(), |
||||||
|
value: msg.Value(), |
||||||
|
data: msg.Data(), |
||||||
|
state: evm.StateDB, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyMessage computes the new state by applying the given message
|
||||||
|
// against the old state within the environment.
|
||||||
|
//
|
||||||
|
// ApplyMessage returns the bytes returned by any EVM execution (if it took place),
|
||||||
|
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
||||||
|
// indicates a core error meaning that the message would always fail for that particular
|
||||||
|
// state and would never be accepted within a block.
|
||||||
|
func ApplyMessage(evm *vm.EVM, msg Message, gp *GasPool) ([]byte, uint64, bool, error) { |
||||||
|
return NewStateTransition(evm, msg, gp).TransitionDb() |
||||||
|
} |
||||||
|
|
||||||
|
// to returns the recipient of the message.
|
||||||
|
func (st *StateTransition) to() common.Address { |
||||||
|
if st.msg == nil || st.msg.To() == nil /* contract creation */ { |
||||||
|
return common.Address{} |
||||||
|
} |
||||||
|
return *st.msg.To() |
||||||
|
} |
||||||
|
|
||||||
|
func (st *StateTransition) useGas(amount uint64) error { |
||||||
|
if st.gas < amount { |
||||||
|
return vm.ErrOutOfGas |
||||||
|
} |
||||||
|
st.gas -= amount |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (st *StateTransition) buyGas() error { |
||||||
|
mgval := new(big.Int).Mul(new(big.Int).SetUint64(st.msg.Gas()), st.gasPrice) |
||||||
|
if st.state.GetBalance(st.msg.From()).Cmp(mgval) < 0 { |
||||||
|
return errInsufficientBalanceForGas |
||||||
|
} |
||||||
|
if err := st.gp.SubGas(st.msg.Gas()); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
st.gas += st.msg.Gas() |
||||||
|
|
||||||
|
st.initialGas = st.msg.Gas() |
||||||
|
st.state.SubBalance(st.msg.From(), mgval) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (st *StateTransition) preCheck() error { |
||||||
|
// Make sure this transaction's nonce is correct.
|
||||||
|
if st.msg.CheckNonce() { |
||||||
|
nonce := st.state.GetNonce(st.msg.From()) |
||||||
|
if nonce < st.msg.Nonce() { |
||||||
|
return ErrNonceTooHigh |
||||||
|
} else if nonce > st.msg.Nonce() { |
||||||
|
return ErrNonceTooLow |
||||||
|
} |
||||||
|
} |
||||||
|
return st.buyGas() |
||||||
|
} |
||||||
|
|
||||||
|
// TransitionDb will transition the state by applying the current message and
|
||||||
|
// returning the result including the used gas. It returns an error if failed.
|
||||||
|
// An error indicates a consensus issue.
|
||||||
|
func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bool, err error) { |
||||||
|
if err = st.preCheck(); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
msg := st.msg |
||||||
|
sender := vm.AccountRef(msg.From()) |
||||||
|
homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber) |
||||||
|
contractCreation := msg.To() == nil |
||||||
|
|
||||||
|
// Pay intrinsic gas
|
||||||
|
gas, err := IntrinsicGas(st.data, contractCreation, homestead) |
||||||
|
if err != nil { |
||||||
|
return nil, 0, false, err |
||||||
|
} |
||||||
|
if err = st.useGas(gas); err != nil { |
||||||
|
return nil, 0, false, err |
||||||
|
} |
||||||
|
|
||||||
|
var ( |
||||||
|
evm = st.evm |
||||||
|
// vm errors do not effect consensus and are therefor
|
||||||
|
// not assigned to err, except for insufficient balance
|
||||||
|
// error.
|
||||||
|
vmerr error |
||||||
|
) |
||||||
|
if contractCreation { |
||||||
|
ret, _, st.gas, vmerr = evm.Create(sender, st.data, st.gas, st.value) |
||||||
|
} else { |
||||||
|
// Increment the nonce for the next transaction
|
||||||
|
st.state.SetNonce(msg.From(), st.state.GetNonce(sender.Address())+1) |
||||||
|
ret, st.gas, vmerr = evm.Call(sender, st.to(), st.data, st.gas, st.value) |
||||||
|
} |
||||||
|
if vmerr != nil { |
||||||
|
log.Debug("VM returned with error", "err", vmerr) |
||||||
|
// The only possible consensus-error would be if there wasn't
|
||||||
|
// sufficient balance to make the transfer happen. The first
|
||||||
|
// balance transfer may never fail.
|
||||||
|
if vmerr == vm.ErrInsufficientBalance { |
||||||
|
return nil, 0, false, vmerr |
||||||
|
} |
||||||
|
} |
||||||
|
st.refundGas() |
||||||
|
st.state.AddBalance(st.evm.Coinbase, new(big.Int).Mul(new(big.Int).SetUint64(st.gasUsed()), st.gasPrice)) |
||||||
|
|
||||||
|
return ret, st.gasUsed(), vmerr != nil, err |
||||||
|
} |
||||||
|
|
||||||
|
func (st *StateTransition) refundGas() { |
||||||
|
// Apply refund counter, capped to half of the used gas.
|
||||||
|
refund := st.gasUsed() / 2 |
||||||
|
if refund > st.state.GetRefund() { |
||||||
|
refund = st.state.GetRefund() |
||||||
|
} |
||||||
|
st.gas += refund |
||||||
|
|
||||||
|
// Return ETH for remaining gas, exchanged at the original rate.
|
||||||
|
remaining := new(big.Int).Mul(new(big.Int).SetUint64(st.gas), st.gasPrice) |
||||||
|
st.state.AddBalance(st.msg.From(), remaining) |
||||||
|
|
||||||
|
// Also return remaining gas to the block gas counter so it is
|
||||||
|
// available for the next transaction.
|
||||||
|
st.gp.AddGas(st.gas) |
||||||
|
} |
||||||
|
|
||||||
|
// gasUsed returns the amount of gas used up by the state transition.
|
||||||
|
func (st *StateTransition) gasUsed() uint64 { |
||||||
|
return st.initialGas - st.gas |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
// Copyright 2018 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 core |
||||||
|
|
||||||
|
import ( |
||||||
|
"runtime" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
) |
||||||
|
|
||||||
|
// senderCacher is a concurrent transaction sender recoverer anc cacher.
|
||||||
|
var senderCacher = newTxSenderCacher(runtime.NumCPU()) |
||||||
|
|
||||||
|
// txSenderCacherRequest is a request for recovering transaction senders with a
|
||||||
|
// specific signature scheme and caching it into the transactions themselves.
|
||||||
|
//
|
||||||
|
// The inc field defines the number of transactions to skip after each recovery,
|
||||||
|
// which is used to feed the same underlying input array to different threads but
|
||||||
|
// ensure they process the early transactions fast.
|
||||||
|
type txSenderCacherRequest struct { |
||||||
|
signer types.Signer |
||||||
|
txs []*types.Transaction |
||||||
|
inc int |
||||||
|
} |
||||||
|
|
||||||
|
// txSenderCacher is a helper structure to concurrently ecrecover transaction
|
||||||
|
// senders from digital signatures on background threads.
|
||||||
|
type txSenderCacher struct { |
||||||
|
threads int |
||||||
|
tasks chan *txSenderCacherRequest |
||||||
|
} |
||||||
|
|
||||||
|
// newTxSenderCacher creates a new transaction sender background cacher and starts
|
||||||
|
// as many processing goroutines as allowed by the GOMAXPROCS on construction.
|
||||||
|
func newTxSenderCacher(threads int) *txSenderCacher { |
||||||
|
cacher := &txSenderCacher{ |
||||||
|
tasks: make(chan *txSenderCacherRequest, threads), |
||||||
|
threads: threads, |
||||||
|
} |
||||||
|
for i := 0; i < threads; i++ { |
||||||
|
go cacher.cache() |
||||||
|
} |
||||||
|
return cacher |
||||||
|
} |
||||||
|
|
||||||
|
// cache is an infinite loop, caching transaction senders from various forms of
|
||||||
|
// data structures.
|
||||||
|
func (cacher *txSenderCacher) cache() { |
||||||
|
for task := range cacher.tasks { |
||||||
|
for i := 0; i < len(task.txs); i += task.inc { |
||||||
|
types.Sender(task.signer, task.txs[i]) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// recover recovers the senders from a batch of transactions and caches them
|
||||||
|
// back into the same data structures. There is no validation being done, nor
|
||||||
|
// any reaction to invalid signatures. That is up to calling code later.
|
||||||
|
func (cacher *txSenderCacher) recover(signer types.Signer, txs []*types.Transaction) { |
||||||
|
// If there's nothing to recover, abort
|
||||||
|
if len(txs) == 0 { |
||||||
|
return |
||||||
|
} |
||||||
|
// Ensure we have meaningful task sizes and schedule the recoveries
|
||||||
|
tasks := cacher.threads |
||||||
|
if len(txs) < tasks*4 { |
||||||
|
tasks = (len(txs) + 3) / 4 |
||||||
|
} |
||||||
|
for i := 0; i < tasks; i++ { |
||||||
|
cacher.tasks <- &txSenderCacherRequest{ |
||||||
|
signer: signer, |
||||||
|
txs: txs[i:], |
||||||
|
inc: tasks, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// recoverFromBlocks recovers the senders from a batch of blocks and caches them
|
||||||
|
// back into the same data structures. There is no validation being done, nor
|
||||||
|
// any reaction to invalid signatures. That is up to calling code later.
|
||||||
|
func (cacher *txSenderCacher) recoverFromBlocks(signer types.Signer, blocks []*types.Block) { |
||||||
|
count := 0 |
||||||
|
for _, block := range blocks { |
||||||
|
count += len(block.Transactions()) |
||||||
|
} |
||||||
|
txs := make([]*types.Transaction, 0, count) |
||||||
|
for _, block := range blocks { |
||||||
|
txs = append(txs, block.Transactions()...) |
||||||
|
} |
||||||
|
cacher.recover(signer, txs) |
||||||
|
} |
@ -0,0 +1,180 @@ |
|||||||
|
// Copyright 2017 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 core |
||||||
|
|
||||||
|
import ( |
||||||
|
"errors" |
||||||
|
"io" |
||||||
|
"os" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/log" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
// errNoActiveJournal is returned if a transaction is attempted to be inserted
|
||||||
|
// into the journal, but no such file is currently open.
|
||||||
|
var errNoActiveJournal = errors.New("no active journal") |
||||||
|
|
||||||
|
// devNull is a WriteCloser that just discards anything written into it. Its
|
||||||
|
// goal is to allow the transaction journal to write into a fake journal when
|
||||||
|
// loading transactions on startup without printing warnings due to no file
|
||||||
|
// being read for write.
|
||||||
|
type devNull struct{} |
||||||
|
|
||||||
|
func (*devNull) Write(p []byte) (n int, err error) { return len(p), nil } |
||||||
|
func (*devNull) Close() error { return nil } |
||||||
|
|
||||||
|
// txJournal is a rotating log of transactions with the aim of storing locally
|
||||||
|
// created transactions to allow non-executed ones to survive node restarts.
|
||||||
|
type txJournal struct { |
||||||
|
path string // Filesystem path to store the transactions at
|
||||||
|
writer io.WriteCloser // Output stream to write new transactions into
|
||||||
|
} |
||||||
|
|
||||||
|
// newTxJournal creates a new transaction journal to
|
||||||
|
func newTxJournal(path string) *txJournal { |
||||||
|
return &txJournal{ |
||||||
|
path: path, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// load parses a transaction journal dump from disk, loading its contents into
|
||||||
|
// the specified pool.
|
||||||
|
func (journal *txJournal) load(add func([]*types.Transaction) []error) error { |
||||||
|
// Skip the parsing if the journal file doesn't exist at all
|
||||||
|
if _, err := os.Stat(journal.path); os.IsNotExist(err) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
// Open the journal for loading any past transactions
|
||||||
|
input, err := os.Open(journal.path) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
defer input.Close() |
||||||
|
|
||||||
|
// Temporarily discard any journal additions (don't double add on load)
|
||||||
|
journal.writer = new(devNull) |
||||||
|
defer func() { journal.writer = nil }() |
||||||
|
|
||||||
|
// Inject all transactions from the journal into the pool
|
||||||
|
stream := rlp.NewStream(input, 0) |
||||||
|
total, dropped := 0, 0 |
||||||
|
|
||||||
|
// Create a method to load a limited batch of transactions and bump the
|
||||||
|
// appropriate progress counters. Then use this method to load all the
|
||||||
|
// journaled transactions in small-ish batches.
|
||||||
|
loadBatch := func(txs types.Transactions) { |
||||||
|
for _, err := range add(txs) { |
||||||
|
if err != nil { |
||||||
|
log.Debug("Failed to add journaled transaction", "err", err) |
||||||
|
dropped++ |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
var ( |
||||||
|
failure error |
||||||
|
batch types.Transactions |
||||||
|
) |
||||||
|
for { |
||||||
|
// Parse the next transaction and terminate on error
|
||||||
|
tx := new(types.Transaction) |
||||||
|
if err = stream.Decode(tx); err != nil { |
||||||
|
if err != io.EOF { |
||||||
|
failure = err |
||||||
|
} |
||||||
|
if batch.Len() > 0 { |
||||||
|
loadBatch(batch) |
||||||
|
} |
||||||
|
break |
||||||
|
} |
||||||
|
// New transaction parsed, queue up for later, import if threshold is reached
|
||||||
|
total++ |
||||||
|
|
||||||
|
if batch = append(batch, tx); batch.Len() > 1024 { |
||||||
|
loadBatch(batch) |
||||||
|
batch = batch[:0] |
||||||
|
} |
||||||
|
} |
||||||
|
log.Info("Loaded local transaction journal", "transactions", total, "dropped", dropped) |
||||||
|
|
||||||
|
return failure |
||||||
|
} |
||||||
|
|
||||||
|
// insert adds the specified transaction to the local disk journal.
|
||||||
|
func (journal *txJournal) insert(tx *types.Transaction) error { |
||||||
|
if journal.writer == nil { |
||||||
|
return errNoActiveJournal |
||||||
|
} |
||||||
|
if err := rlp.Encode(journal.writer, tx); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// rotate regenerates the transaction journal based on the current contents of
|
||||||
|
// the transaction pool.
|
||||||
|
func (journal *txJournal) rotate(all map[common.Address]types.Transactions) error { |
||||||
|
// Close the current journal (if any is open)
|
||||||
|
if journal.writer != nil { |
||||||
|
if err := journal.writer.Close(); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
journal.writer = nil |
||||||
|
} |
||||||
|
// Generate a new journal with the contents of the current pool
|
||||||
|
replacement, err := os.OpenFile(journal.path+".new", os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
journaled := 0 |
||||||
|
for _, txs := range all { |
||||||
|
for _, tx := range txs { |
||||||
|
if err = rlp.Encode(replacement, tx); err != nil { |
||||||
|
replacement.Close() |
||||||
|
return err |
||||||
|
} |
||||||
|
} |
||||||
|
journaled += len(txs) |
||||||
|
} |
||||||
|
replacement.Close() |
||||||
|
|
||||||
|
// Replace the live journal with the newly generated one
|
||||||
|
if err = os.Rename(journal.path+".new", journal.path); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
sink, err := os.OpenFile(journal.path, os.O_WRONLY|os.O_APPEND, 0755) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
journal.writer = sink |
||||||
|
log.Info("Regenerated local transaction journal", "transactions", journaled, "accounts", len(all)) |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// close flushes the transaction journal contents to disk and closes the file.
|
||||||
|
func (journal *txJournal) close() error { |
||||||
|
var err error |
||||||
|
|
||||||
|
if journal.writer != nil { |
||||||
|
err = journal.writer.Close() |
||||||
|
journal.writer = nil |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
@ -0,0 +1,520 @@ |
|||||||
|
// 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 core |
||||||
|
|
||||||
|
import ( |
||||||
|
"container/heap" |
||||||
|
"math" |
||||||
|
"math/big" |
||||||
|
"sort" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/log" |
||||||
|
) |
||||||
|
|
||||||
|
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
|
||||||
|
// retrieving sorted transactions from the possibly gapped future queue.
|
||||||
|
type nonceHeap []uint64 |
||||||
|
|
||||||
|
func (h nonceHeap) Len() int { return len(h) } |
||||||
|
func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] } |
||||||
|
func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } |
||||||
|
|
||||||
|
func (h *nonceHeap) Push(x interface{}) { |
||||||
|
*h = append(*h, x.(uint64)) |
||||||
|
} |
||||||
|
|
||||||
|
func (h *nonceHeap) Pop() interface{} { |
||||||
|
old := *h |
||||||
|
n := len(old) |
||||||
|
x := old[n-1] |
||||||
|
*h = old[0 : n-1] |
||||||
|
return x |
||||||
|
} |
||||||
|
|
||||||
|
// txSortedMap is a nonce->transaction hash map with a heap based index to allow
|
||||||
|
// iterating over the contents in a nonce-incrementing way.
|
||||||
|
type txSortedMap struct { |
||||||
|
items map[uint64]*types.Transaction // Hash map storing the transaction data
|
||||||
|
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
|
||||||
|
cache types.Transactions // Cache of the transactions already sorted
|
||||||
|
} |
||||||
|
|
||||||
|
// newTxSortedMap creates a new nonce-sorted transaction map.
|
||||||
|
func newTxSortedMap() *txSortedMap { |
||||||
|
return &txSortedMap{ |
||||||
|
items: make(map[uint64]*types.Transaction), |
||||||
|
index: new(nonceHeap), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Get retrieves the current transactions associated with the given nonce.
|
||||||
|
func (m *txSortedMap) Get(nonce uint64) *types.Transaction { |
||||||
|
return m.items[nonce] |
||||||
|
} |
||||||
|
|
||||||
|
// Put inserts a new transaction into the map, also updating the map's nonce
|
||||||
|
// index. If a transaction already exists with the same nonce, it's overwritten.
|
||||||
|
func (m *txSortedMap) Put(tx *types.Transaction) { |
||||||
|
nonce := tx.Nonce() |
||||||
|
if m.items[nonce] == nil { |
||||||
|
heap.Push(m.index, nonce) |
||||||
|
} |
||||||
|
m.items[nonce], m.cache = tx, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Forward removes all transactions from the map with a nonce lower than the
|
||||||
|
// provided threshold. Every removed transaction is returned for any post-removal
|
||||||
|
// maintenance.
|
||||||
|
func (m *txSortedMap) Forward(threshold uint64) types.Transactions { |
||||||
|
var removed types.Transactions |
||||||
|
|
||||||
|
// Pop off heap items until the threshold is reached
|
||||||
|
for m.index.Len() > 0 && (*m.index)[0] < threshold { |
||||||
|
nonce := heap.Pop(m.index).(uint64) |
||||||
|
removed = append(removed, m.items[nonce]) |
||||||
|
delete(m.items, nonce) |
||||||
|
} |
||||||
|
// If we had a cached order, shift the front
|
||||||
|
if m.cache != nil { |
||||||
|
m.cache = m.cache[len(removed):] |
||||||
|
} |
||||||
|
return removed |
||||||
|
} |
||||||
|
|
||||||
|
// Filter iterates over the list of transactions and removes all of them for which
|
||||||
|
// the specified function evaluates to true.
|
||||||
|
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions { |
||||||
|
var removed types.Transactions |
||||||
|
|
||||||
|
// Collect all the transactions to filter out
|
||||||
|
for nonce, tx := range m.items { |
||||||
|
if filter(tx) { |
||||||
|
removed = append(removed, tx) |
||||||
|
delete(m.items, nonce) |
||||||
|
} |
||||||
|
} |
||||||
|
// If transactions were removed, the heap and cache are ruined
|
||||||
|
if len(removed) > 0 { |
||||||
|
*m.index = make([]uint64, 0, len(m.items)) |
||||||
|
for nonce := range m.items { |
||||||
|
*m.index = append(*m.index, nonce) |
||||||
|
} |
||||||
|
heap.Init(m.index) |
||||||
|
|
||||||
|
m.cache = nil |
||||||
|
} |
||||||
|
return removed |
||||||
|
} |
||||||
|
|
||||||
|
// Cap places a hard limit on the number of items, returning all transactions
|
||||||
|
// exceeding that limit.
|
||||||
|
func (m *txSortedMap) Cap(threshold int) types.Transactions { |
||||||
|
// Short circuit if the number of items is under the limit
|
||||||
|
if len(m.items) <= threshold { |
||||||
|
return nil |
||||||
|
} |
||||||
|
// Otherwise gather and drop the highest nonce'd transactions
|
||||||
|
var drops types.Transactions |
||||||
|
|
||||||
|
sort.Sort(*m.index) |
||||||
|
for size := len(m.items); size > threshold; size-- { |
||||||
|
drops = append(drops, m.items[(*m.index)[size-1]]) |
||||||
|
delete(m.items, (*m.index)[size-1]) |
||||||
|
} |
||||||
|
*m.index = (*m.index)[:threshold] |
||||||
|
heap.Init(m.index) |
||||||
|
|
||||||
|
// If we had a cache, shift the back
|
||||||
|
if m.cache != nil { |
||||||
|
m.cache = m.cache[:len(m.cache)-len(drops)] |
||||||
|
} |
||||||
|
return drops |
||||||
|
} |
||||||
|
|
||||||
|
// Remove deletes a transaction from the maintained map, returning whether the
|
||||||
|
// transaction was found.
|
||||||
|
func (m *txSortedMap) Remove(nonce uint64) bool { |
||||||
|
// Short circuit if no transaction is present
|
||||||
|
_, ok := m.items[nonce] |
||||||
|
if !ok { |
||||||
|
return false |
||||||
|
} |
||||||
|
// Otherwise delete the transaction and fix the heap index
|
||||||
|
for i := 0; i < m.index.Len(); i++ { |
||||||
|
if (*m.index)[i] == nonce { |
||||||
|
heap.Remove(m.index, i) |
||||||
|
break |
||||||
|
} |
||||||
|
} |
||||||
|
delete(m.items, nonce) |
||||||
|
m.cache = nil |
||||||
|
|
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
// Ready retrieves a sequentially increasing list of transactions starting at the
|
||||||
|
// provided nonce that is ready for processing. The returned transactions will be
|
||||||
|
// removed from the list.
|
||||||
|
//
|
||||||
|
// Note, all transactions with nonces lower than start will also be returned to
|
||||||
|
// prevent getting into and invalid state. This is not something that should ever
|
||||||
|
// happen but better to be self correcting than failing!
|
||||||
|
func (m *txSortedMap) Ready(start uint64) types.Transactions { |
||||||
|
// Short circuit if no transactions are available
|
||||||
|
if m.index.Len() == 0 || (*m.index)[0] > start { |
||||||
|
return nil |
||||||
|
} |
||||||
|
// Otherwise start accumulating incremental transactions
|
||||||
|
var ready types.Transactions |
||||||
|
for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ { |
||||||
|
ready = append(ready, m.items[next]) |
||||||
|
delete(m.items, next) |
||||||
|
heap.Pop(m.index) |
||||||
|
} |
||||||
|
m.cache = nil |
||||||
|
|
||||||
|
return ready |
||||||
|
} |
||||||
|
|
||||||
|
// Len returns the length of the transaction map.
|
||||||
|
func (m *txSortedMap) Len() int { |
||||||
|
return len(m.items) |
||||||
|
} |
||||||
|
|
||||||
|
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||||
|
// sorted internal representation. The result of the sorting is cached in case
|
||||||
|
// it's requested again before any modifications are made to the contents.
|
||||||
|
func (m *txSortedMap) Flatten() types.Transactions { |
||||||
|
// If the sorting was not cached yet, create and cache it
|
||||||
|
if m.cache == nil { |
||||||
|
m.cache = make(types.Transactions, 0, len(m.items)) |
||||||
|
for _, tx := range m.items { |
||||||
|
m.cache = append(m.cache, tx) |
||||||
|
} |
||||||
|
sort.Sort(types.TxByNonce(m.cache)) |
||||||
|
} |
||||||
|
// Copy the cache to prevent accidental modifications
|
||||||
|
txs := make(types.Transactions, len(m.cache)) |
||||||
|
copy(txs, m.cache) |
||||||
|
return txs |
||||||
|
} |
||||||
|
|
||||||
|
// txList is a "list" of transactions belonging to an account, sorted by account
|
||||||
|
// nonce. The same type can be used both for storing contiguous transactions for
|
||||||
|
// the executable/pending queue; and for storing gapped transactions for the non-
|
||||||
|
// executable/future queue, with minor behavioral changes.
|
||||||
|
type txList struct { |
||||||
|
strict bool // Whether nonces are strictly continuous or not
|
||||||
|
txs *txSortedMap // Heap indexed sorted hash map of the transactions
|
||||||
|
|
||||||
|
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
|
||||||
|
gascap uint64 // Gas limit of the highest spending transaction (reset only if exceeds block limit)
|
||||||
|
} |
||||||
|
|
||||||
|
// newTxList create a new transaction list for maintaining nonce-indexable fast,
|
||||||
|
// gapped, sortable transaction lists.
|
||||||
|
func newTxList(strict bool) *txList { |
||||||
|
return &txList{ |
||||||
|
strict: strict, |
||||||
|
txs: newTxSortedMap(), |
||||||
|
costcap: new(big.Int), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Overlaps returns whether the transaction specified has the same nonce as one
|
||||||
|
// already contained within the list.
|
||||||
|
func (l *txList) Overlaps(tx *types.Transaction) bool { |
||||||
|
return l.txs.Get(tx.Nonce()) != nil |
||||||
|
} |
||||||
|
|
||||||
|
// Add tries to insert a new transaction into the list, returning whether the
|
||||||
|
// transaction was accepted, and if yes, any previous transaction it replaced.
|
||||||
|
//
|
||||||
|
// If the new transaction is accepted into the list, the lists' cost and gas
|
||||||
|
// thresholds are also potentially updated.
|
||||||
|
func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Transaction) { |
||||||
|
// If there's an older better transaction, abort
|
||||||
|
old := l.txs.Get(tx.Nonce()) |
||||||
|
if old != nil { |
||||||
|
threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100)) |
||||||
|
// Have to ensure that the new gas price is higher than the old gas
|
||||||
|
// price as well as checking the percentage threshold to ensure that
|
||||||
|
// this is accurate for low (Wei-level) gas price replacements
|
||||||
|
if old.GasPrice().Cmp(tx.GasPrice()) >= 0 || threshold.Cmp(tx.GasPrice()) > 0 { |
||||||
|
return false, nil |
||||||
|
} |
||||||
|
} |
||||||
|
// Otherwise overwrite the old transaction with the current one
|
||||||
|
l.txs.Put(tx) |
||||||
|
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 { |
||||||
|
l.costcap = cost |
||||||
|
} |
||||||
|
if gas := tx.Gas(); l.gascap < gas { |
||||||
|
l.gascap = gas |
||||||
|
} |
||||||
|
return true, old |
||||||
|
} |
||||||
|
|
||||||
|
// Forward removes all transactions from the list with a nonce lower than the
|
||||||
|
// provided threshold. Every removed transaction is returned for any post-removal
|
||||||
|
// maintenance.
|
||||||
|
func (l *txList) Forward(threshold uint64) types.Transactions { |
||||||
|
return l.txs.Forward(threshold) |
||||||
|
} |
||||||
|
|
||||||
|
// Filter removes all transactions from the list with a cost or gas limit higher
|
||||||
|
// than the provided thresholds. Every removed transaction is returned for any
|
||||||
|
// post-removal maintenance. Strict-mode invalidated transactions are also
|
||||||
|
// returned.
|
||||||
|
//
|
||||||
|
// This method uses the cached costcap and gascap to quickly decide if there's even
|
||||||
|
// a point in calculating all the costs or if the balance covers all. If the threshold
|
||||||
|
// is lower than the costgas cap, the caps will be reset to a new high after removing
|
||||||
|
// the newly invalidated transactions.
|
||||||
|
func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions, types.Transactions) { |
||||||
|
// If all transactions are below the threshold, short circuit
|
||||||
|
if l.costcap.Cmp(costLimit) <= 0 && l.gascap <= gasLimit { |
||||||
|
return nil, nil |
||||||
|
} |
||||||
|
l.costcap = new(big.Int).Set(costLimit) // Lower the caps to the thresholds
|
||||||
|
l.gascap = gasLimit |
||||||
|
|
||||||
|
// Filter out all the transactions above the account's funds
|
||||||
|
removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas() > gasLimit }) |
||||||
|
|
||||||
|
// If the list was strict, filter anything above the lowest nonce
|
||||||
|
var invalids types.Transactions |
||||||
|
|
||||||
|
if l.strict && len(removed) > 0 { |
||||||
|
lowest := uint64(math.MaxUint64) |
||||||
|
for _, tx := range removed { |
||||||
|
if nonce := tx.Nonce(); lowest > nonce { |
||||||
|
lowest = nonce |
||||||
|
} |
||||||
|
} |
||||||
|
invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest }) |
||||||
|
} |
||||||
|
return removed, invalids |
||||||
|
} |
||||||
|
|
||||||
|
// Cap places a hard limit on the number of items, returning all transactions
|
||||||
|
// exceeding that limit.
|
||||||
|
func (l *txList) Cap(threshold int) types.Transactions { |
||||||
|
return l.txs.Cap(threshold) |
||||||
|
} |
||||||
|
|
||||||
|
// Remove deletes a transaction from the maintained list, returning whether the
|
||||||
|
// transaction was found, and also returning any transaction invalidated due to
|
||||||
|
// the deletion (strict mode only).
|
||||||
|
func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) { |
||||||
|
// Remove the transaction from the set
|
||||||
|
nonce := tx.Nonce() |
||||||
|
if removed := l.txs.Remove(nonce); !removed { |
||||||
|
return false, nil |
||||||
|
} |
||||||
|
// In strict mode, filter out non-executable transactions
|
||||||
|
if l.strict { |
||||||
|
return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce }) |
||||||
|
} |
||||||
|
return true, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Ready retrieves a sequentially increasing list of transactions starting at the
|
||||||
|
// provided nonce that is ready for processing. The returned transactions will be
|
||||||
|
// removed from the list.
|
||||||
|
//
|
||||||
|
// Note, all transactions with nonces lower than start will also be returned to
|
||||||
|
// prevent getting into and invalid state. This is not something that should ever
|
||||||
|
// happen but better to be self correcting than failing!
|
||||||
|
func (l *txList) Ready(start uint64) types.Transactions { |
||||||
|
return l.txs.Ready(start) |
||||||
|
} |
||||||
|
|
||||||
|
// Len returns the length of the transaction list.
|
||||||
|
func (l *txList) Len() int { |
||||||
|
return l.txs.Len() |
||||||
|
} |
||||||
|
|
||||||
|
// Empty returns whether the list of transactions is empty or not.
|
||||||
|
func (l *txList) Empty() bool { |
||||||
|
return l.Len() == 0 |
||||||
|
} |
||||||
|
|
||||||
|
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||||
|
// sorted internal representation. The result of the sorting is cached in case
|
||||||
|
// it's requested again before any modifications are made to the contents.
|
||||||
|
func (l *txList) Flatten() types.Transactions { |
||||||
|
return l.txs.Flatten() |
||||||
|
} |
||||||
|
|
||||||
|
// priceHeap is a heap.Interface implementation over transactions for retrieving
|
||||||
|
// price-sorted transactions to discard when the pool fills up.
|
||||||
|
type priceHeap []*types.Transaction |
||||||
|
|
||||||
|
func (h priceHeap) Len() int { return len(h) } |
||||||
|
func (h priceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] } |
||||||
|
|
||||||
|
func (h priceHeap) Less(i, j int) bool { |
||||||
|
// Sort primarily by price, returning the cheaper one
|
||||||
|
switch h[i].GasPrice().Cmp(h[j].GasPrice()) { |
||||||
|
case -1: |
||||||
|
return true |
||||||
|
case 1: |
||||||
|
return false |
||||||
|
} |
||||||
|
// If the prices match, stabilize via nonces (high nonce is worse)
|
||||||
|
return h[i].Nonce() > h[j].Nonce() |
||||||
|
} |
||||||
|
|
||||||
|
func (h *priceHeap) Push(x interface{}) { |
||||||
|
*h = append(*h, x.(*types.Transaction)) |
||||||
|
} |
||||||
|
|
||||||
|
func (h *priceHeap) Pop() interface{} { |
||||||
|
old := *h |
||||||
|
n := len(old) |
||||||
|
x := old[n-1] |
||||||
|
*h = old[0 : n-1] |
||||||
|
return x |
||||||
|
} |
||||||
|
|
||||||
|
// txPricedList is a price-sorted heap to allow operating on transactions pool
|
||||||
|
// contents in a price-incrementing way.
|
||||||
|
type txPricedList struct { |
||||||
|
all *txLookup // Pointer to the map of all transactions
|
||||||
|
items *priceHeap // Heap of prices of all the stored transactions
|
||||||
|
stales int // Number of stale price points to (re-heap trigger)
|
||||||
|
} |
||||||
|
|
||||||
|
// newTxPricedList creates a new price-sorted transaction heap.
|
||||||
|
func newTxPricedList(all *txLookup) *txPricedList { |
||||||
|
return &txPricedList{ |
||||||
|
all: all, |
||||||
|
items: new(priceHeap), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Put inserts a new transaction into the heap.
|
||||||
|
func (l *txPricedList) Put(tx *types.Transaction) { |
||||||
|
heap.Push(l.items, tx) |
||||||
|
} |
||||||
|
|
||||||
|
// Removed notifies the prices transaction list that an old transaction dropped
|
||||||
|
// from the pool. The list will just keep a counter of stale objects and update
|
||||||
|
// the heap if a large enough ratio of transactions go stale.
|
||||||
|
func (l *txPricedList) Removed() { |
||||||
|
// Bump the stale counter, but exit if still too low (< 25%)
|
||||||
|
l.stales++ |
||||||
|
if l.stales <= len(*l.items)/4 { |
||||||
|
return |
||||||
|
} |
||||||
|
// Seems we've reached a critical number of stale transactions, reheap
|
||||||
|
reheap := make(priceHeap, 0, l.all.Count()) |
||||||
|
|
||||||
|
l.stales, l.items = 0, &reheap |
||||||
|
l.all.Range(func(hash common.Hash, tx *types.Transaction) bool { |
||||||
|
*l.items = append(*l.items, tx) |
||||||
|
return true |
||||||
|
}) |
||||||
|
heap.Init(l.items) |
||||||
|
} |
||||||
|
|
||||||
|
// Cap finds all the transactions below the given price threshold, drops them
|
||||||
|
// from the priced list and returns them for further removal from the entire pool.
|
||||||
|
func (l *txPricedList) Cap(threshold *big.Int, local *accountSet) types.Transactions { |
||||||
|
drop := make(types.Transactions, 0, 128) // Remote underpriced transactions to drop
|
||||||
|
save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
|
||||||
|
|
||||||
|
for len(*l.items) > 0 { |
||||||
|
// Discard stale transactions if found during cleanup
|
||||||
|
tx := heap.Pop(l.items).(*types.Transaction) |
||||||
|
if l.all.Get(tx.Hash()) == nil { |
||||||
|
l.stales-- |
||||||
|
continue |
||||||
|
} |
||||||
|
// Stop the discards if we've reached the threshold
|
||||||
|
if tx.GasPrice().Cmp(threshold) >= 0 { |
||||||
|
save = append(save, tx) |
||||||
|
break |
||||||
|
} |
||||||
|
// Non stale transaction found, discard unless local
|
||||||
|
if local.containsTx(tx) { |
||||||
|
save = append(save, tx) |
||||||
|
} else { |
||||||
|
drop = append(drop, tx) |
||||||
|
} |
||||||
|
} |
||||||
|
for _, tx := range save { |
||||||
|
heap.Push(l.items, tx) |
||||||
|
} |
||||||
|
return drop |
||||||
|
} |
||||||
|
|
||||||
|
// Underpriced checks whether a transaction is cheaper than (or as cheap as) the
|
||||||
|
// lowest priced transaction currently being tracked.
|
||||||
|
func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) bool { |
||||||
|
// Local transactions cannot be underpriced
|
||||||
|
if local.containsTx(tx) { |
||||||
|
return false |
||||||
|
} |
||||||
|
// Discard stale price points if found at the heap start
|
||||||
|
for len(*l.items) > 0 { |
||||||
|
head := []*types.Transaction(*l.items)[0] |
||||||
|
if l.all.Get(head.Hash()) == nil { |
||||||
|
l.stales-- |
||||||
|
heap.Pop(l.items) |
||||||
|
continue |
||||||
|
} |
||||||
|
break |
||||||
|
} |
||||||
|
// Check if the transaction is underpriced or not
|
||||||
|
if len(*l.items) == 0 { |
||||||
|
log.Error("Pricing query for empty pool") // This cannot happen, print to catch programming errors
|
||||||
|
return false |
||||||
|
} |
||||||
|
cheapest := []*types.Transaction(*l.items)[0] |
||||||
|
return cheapest.GasPrice().Cmp(tx.GasPrice()) >= 0 |
||||||
|
} |
||||||
|
|
||||||
|
// Discard finds a number of most underpriced transactions, removes them from the
|
||||||
|
// priced list and returns them for further removal from the entire pool.
|
||||||
|
func (l *txPricedList) Discard(count int, local *accountSet) types.Transactions { |
||||||
|
drop := make(types.Transactions, 0, count) // Remote underpriced transactions to drop
|
||||||
|
save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
|
||||||
|
|
||||||
|
for len(*l.items) > 0 && count > 0 { |
||||||
|
// Discard stale transactions if found during cleanup
|
||||||
|
tx := heap.Pop(l.items).(*types.Transaction) |
||||||
|
if l.all.Get(tx.Hash()) == nil { |
||||||
|
l.stales-- |
||||||
|
continue |
||||||
|
} |
||||||
|
// Non stale transaction found, discard unless local
|
||||||
|
if local.containsTx(tx) { |
||||||
|
save = append(save, tx) |
||||||
|
} else { |
||||||
|
drop = append(drop, tx) |
||||||
|
count-- |
||||||
|
} |
||||||
|
} |
||||||
|
for _, tx := range save { |
||||||
|
heap.Push(l.items, tx) |
||||||
|
} |
||||||
|
return drop |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
// 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 core |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/rand" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
) |
||||||
|
|
||||||
|
// Tests that transactions can be added to strict lists and list contents and
|
||||||
|
// nonce boundaries are correctly maintained.
|
||||||
|
func TestStrictTxListAdd(t *testing.T) { |
||||||
|
// Generate a list of transactions to insert
|
||||||
|
key, _ := crypto.GenerateKey() |
||||||
|
|
||||||
|
txs := make(types.Transactions, 1024) |
||||||
|
for i := 0; i < len(txs); i++ { |
||||||
|
txs[i] = transaction(uint64(i), 0, key) |
||||||
|
} |
||||||
|
// Insert the transactions in a random order
|
||||||
|
list := newTxList(true) |
||||||
|
for _, v := range rand.Perm(len(txs)) { |
||||||
|
list.Add(txs[v], DefaultTxPoolConfig.PriceBump) |
||||||
|
} |
||||||
|
// Verify internal state
|
||||||
|
if len(list.txs.items) != len(txs) { |
||||||
|
t.Errorf("transaction count mismatch: have %d, want %d", len(list.txs.items), len(txs)) |
||||||
|
} |
||||||
|
for i, tx := range txs { |
||||||
|
if list.txs.items[tx.Nonce()] != tx { |
||||||
|
t.Errorf("item %d: transaction mismatch: have %v, want %v", i, list.txs.items[tx.Nonce()], tx) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,391 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Package types contains data types related to Ethereum consensus.
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/binary" |
||||||
|
"io" |
||||||
|
"math/big" |
||||||
|
"sort" |
||||||
|
"sync/atomic" |
||||||
|
"time" |
||||||
|
"unsafe" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
"github.com/ethereum/go-ethereum/crypto/sha3" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
EmptyRootHash = DeriveSha(Transactions{}) |
||||||
|
EmptyUncleHash = CalcUncleHash(nil) |
||||||
|
) |
||||||
|
|
||||||
|
// A BlockNonce is a 64-bit hash which proves (combined with the
|
||||||
|
// mix-hash) that a sufficient amount of computation has been carried
|
||||||
|
// out on a block.
|
||||||
|
type BlockNonce [8]byte |
||||||
|
|
||||||
|
// EncodeNonce converts the given integer to a block nonce.
|
||||||
|
func EncodeNonce(i uint64) BlockNonce { |
||||||
|
var n BlockNonce |
||||||
|
binary.BigEndian.PutUint64(n[:], i) |
||||||
|
return n |
||||||
|
} |
||||||
|
|
||||||
|
// Uint64 returns the integer value of a block nonce.
|
||||||
|
func (n BlockNonce) Uint64() uint64 { |
||||||
|
return binary.BigEndian.Uint64(n[:]) |
||||||
|
} |
||||||
|
|
||||||
|
// MarshalText encodes n as a hex string with 0x prefix.
|
||||||
|
func (n BlockNonce) MarshalText() ([]byte, error) { |
||||||
|
return hexutil.Bytes(n[:]).MarshalText() |
||||||
|
} |
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (n *BlockNonce) UnmarshalText(input []byte) error { |
||||||
|
return hexutil.UnmarshalFixedText("BlockNonce", input, n[:]) |
||||||
|
} |
||||||
|
|
||||||
|
//go:generate gencodec -type Header -field-override headerMarshaling -out gen_header_json.go
|
||||||
|
|
||||||
|
// Header represents a block header in the Ethereum blockchain.
|
||||||
|
type Header struct { |
||||||
|
ParentHash common.Hash `json:"parentHash" gencodec:"required"` |
||||||
|
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` |
||||||
|
Coinbase common.Address `json:"miner" gencodec:"required"` |
||||||
|
Root common.Hash `json:"stateRoot" gencodec:"required"` |
||||||
|
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` |
||||||
|
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` |
||||||
|
Bloom Bloom `json:"logsBloom" gencodec:"required"` |
||||||
|
Difficulty *big.Int `json:"difficulty" gencodec:"required"` |
||||||
|
Number *big.Int `json:"number" gencodec:"required"` |
||||||
|
GasLimit uint64 `json:"gasLimit" gencodec:"required"` |
||||||
|
GasUsed uint64 `json:"gasUsed" gencodec:"required"` |
||||||
|
Time *big.Int `json:"timestamp" gencodec:"required"` |
||||||
|
Extra []byte `json:"extraData" gencodec:"required"` |
||||||
|
MixDigest common.Hash `json:"mixHash" gencodec:"required"` |
||||||
|
Nonce BlockNonce `json:"nonce" gencodec:"required"` |
||||||
|
} |
||||||
|
|
||||||
|
// field type overrides for gencodec
|
||||||
|
type headerMarshaling struct { |
||||||
|
Difficulty *hexutil.Big |
||||||
|
Number *hexutil.Big |
||||||
|
GasLimit hexutil.Uint64 |
||||||
|
GasUsed hexutil.Uint64 |
||||||
|
Time *hexutil.Big |
||||||
|
Extra hexutil.Bytes |
||||||
|
Hash common.Hash `json:"hash"` // adds call to Hash() in MarshalJSON
|
||||||
|
} |
||||||
|
|
||||||
|
// Hash returns the block hash of the header, which is simply the keccak256 hash of its
|
||||||
|
// RLP encoding.
|
||||||
|
func (h *Header) Hash() common.Hash { |
||||||
|
return rlpHash(h) |
||||||
|
} |
||||||
|
|
||||||
|
// Size returns the approximate memory used by all internal contents. It is used
|
||||||
|
// to approximate and limit the memory consumption of various caches.
|
||||||
|
func (h *Header) Size() common.StorageSize { |
||||||
|
return common.StorageSize(unsafe.Sizeof(*h)) + common.StorageSize(len(h.Extra)+(h.Difficulty.BitLen()+h.Number.BitLen()+h.Time.BitLen())/8) |
||||||
|
} |
||||||
|
|
||||||
|
func rlpHash(x interface{}) (h common.Hash) { |
||||||
|
hw := sha3.NewKeccak256() |
||||||
|
rlp.Encode(hw, x) |
||||||
|
hw.Sum(h[:0]) |
||||||
|
return h |
||||||
|
} |
||||||
|
|
||||||
|
// Body is a simple (mutable, non-safe) data container for storing and moving
|
||||||
|
// a block's data contents (transactions and uncles) together.
|
||||||
|
type Body struct { |
||||||
|
Transactions []*Transaction |
||||||
|
Uncles []*Header |
||||||
|
} |
||||||
|
|
||||||
|
// Block represents an entire block in the Ethereum blockchain.
|
||||||
|
type Block struct { |
||||||
|
header *Header |
||||||
|
uncles []*Header |
||||||
|
transactions Transactions |
||||||
|
|
||||||
|
// caches
|
||||||
|
hash atomic.Value |
||||||
|
size atomic.Value |
||||||
|
|
||||||
|
// Td is used by package core to store the total difficulty
|
||||||
|
// of the chain up to and including the block.
|
||||||
|
td *big.Int |
||||||
|
|
||||||
|
// These fields are used by package eth to track
|
||||||
|
// inter-peer block relay.
|
||||||
|
ReceivedAt time.Time |
||||||
|
ReceivedFrom interface{} |
||||||
|
} |
||||||
|
|
||||||
|
// DeprecatedTd is an old relic for extracting the TD of a block. It is in the
|
||||||
|
// code solely to facilitate upgrading the database from the old format to the
|
||||||
|
// new, after which it should be deleted. Do not use!
|
||||||
|
func (b *Block) DeprecatedTd() *big.Int { |
||||||
|
return b.td |
||||||
|
} |
||||||
|
|
||||||
|
// [deprecated by eth/63]
|
||||||
|
// StorageBlock defines the RLP encoding of a Block stored in the
|
||||||
|
// state database. The StorageBlock encoding contains fields that
|
||||||
|
// would otherwise need to be recomputed.
|
||||||
|
type StorageBlock Block |
||||||
|
|
||||||
|
// "external" block encoding. used for eth protocol, etc.
|
||||||
|
type extblock struct { |
||||||
|
Header *Header |
||||||
|
Txs []*Transaction |
||||||
|
Uncles []*Header |
||||||
|
} |
||||||
|
|
||||||
|
// [deprecated by eth/63]
|
||||||
|
// "storage" block encoding. used for database.
|
||||||
|
type storageblock struct { |
||||||
|
Header *Header |
||||||
|
Txs []*Transaction |
||||||
|
Uncles []*Header |
||||||
|
TD *big.Int |
||||||
|
} |
||||||
|
|
||||||
|
// NewBlock creates a new block. The input data is copied,
|
||||||
|
// changes to header and to the field values will not affect the
|
||||||
|
// block.
|
||||||
|
//
|
||||||
|
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
|
||||||
|
// are ignored and set to values derived from the given txs, uncles
|
||||||
|
// and receipts.
|
||||||
|
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block { |
||||||
|
b := &Block{header: CopyHeader(header), td: new(big.Int)} |
||||||
|
|
||||||
|
// TODO: panic if len(txs) != len(receipts)
|
||||||
|
if len(txs) == 0 { |
||||||
|
b.header.TxHash = EmptyRootHash |
||||||
|
} else { |
||||||
|
b.header.TxHash = DeriveSha(Transactions(txs)) |
||||||
|
b.transactions = make(Transactions, len(txs)) |
||||||
|
copy(b.transactions, txs) |
||||||
|
} |
||||||
|
|
||||||
|
if len(receipts) == 0 { |
||||||
|
b.header.ReceiptHash = EmptyRootHash |
||||||
|
} else { |
||||||
|
b.header.ReceiptHash = DeriveSha(Receipts(receipts)) |
||||||
|
b.header.Bloom = CreateBloom(receipts) |
||||||
|
} |
||||||
|
|
||||||
|
if len(uncles) == 0 { |
||||||
|
b.header.UncleHash = EmptyUncleHash |
||||||
|
} else { |
||||||
|
b.header.UncleHash = CalcUncleHash(uncles) |
||||||
|
b.uncles = make([]*Header, len(uncles)) |
||||||
|
for i := range uncles { |
||||||
|
b.uncles[i] = CopyHeader(uncles[i]) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return b |
||||||
|
} |
||||||
|
|
||||||
|
// NewBlockWithHeader creates a block with the given header data. The
|
||||||
|
// header data is copied, changes to header and to the field values
|
||||||
|
// will not affect the block.
|
||||||
|
func NewBlockWithHeader(header *Header) *Block { |
||||||
|
return &Block{header: CopyHeader(header)} |
||||||
|
} |
||||||
|
|
||||||
|
// CopyHeader creates a deep copy of a block header to prevent side effects from
|
||||||
|
// modifying a header variable.
|
||||||
|
func CopyHeader(h *Header) *Header { |
||||||
|
cpy := *h |
||||||
|
if cpy.Time = new(big.Int); h.Time != nil { |
||||||
|
cpy.Time.Set(h.Time) |
||||||
|
} |
||||||
|
if cpy.Difficulty = new(big.Int); h.Difficulty != nil { |
||||||
|
cpy.Difficulty.Set(h.Difficulty) |
||||||
|
} |
||||||
|
if cpy.Number = new(big.Int); h.Number != nil { |
||||||
|
cpy.Number.Set(h.Number) |
||||||
|
} |
||||||
|
if len(h.Extra) > 0 { |
||||||
|
cpy.Extra = make([]byte, len(h.Extra)) |
||||||
|
copy(cpy.Extra, h.Extra) |
||||||
|
} |
||||||
|
return &cpy |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeRLP decodes the Ethereum
|
||||||
|
func (b *Block) DecodeRLP(s *rlp.Stream) error { |
||||||
|
var eb extblock |
||||||
|
_, size, _ := s.Kind() |
||||||
|
if err := s.Decode(&eb); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs |
||||||
|
b.size.Store(common.StorageSize(rlp.ListSize(size))) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// EncodeRLP serializes b into the Ethereum RLP block format.
|
||||||
|
func (b *Block) EncodeRLP(w io.Writer) error { |
||||||
|
return rlp.Encode(w, extblock{ |
||||||
|
Header: b.header, |
||||||
|
Txs: b.transactions, |
||||||
|
Uncles: b.uncles, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// [deprecated by eth/63]
|
||||||
|
func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error { |
||||||
|
var sb storageblock |
||||||
|
if err := s.Decode(&sb); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
b.header, b.uncles, b.transactions, b.td = sb.Header, sb.Uncles, sb.Txs, sb.TD |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: copies
|
||||||
|
|
||||||
|
func (b *Block) Uncles() []*Header { return b.uncles } |
||||||
|
func (b *Block) Transactions() Transactions { return b.transactions } |
||||||
|
|
||||||
|
func (b *Block) Transaction(hash common.Hash) *Transaction { |
||||||
|
for _, transaction := range b.transactions { |
||||||
|
if transaction.Hash() == hash { |
||||||
|
return transaction |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) } |
||||||
|
func (b *Block) GasLimit() uint64 { return b.header.GasLimit } |
||||||
|
func (b *Block) GasUsed() uint64 { return b.header.GasUsed } |
||||||
|
func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) } |
||||||
|
func (b *Block) Time() *big.Int { return new(big.Int).Set(b.header.Time) } |
||||||
|
|
||||||
|
func (b *Block) NumberU64() uint64 { return b.header.Number.Uint64() } |
||||||
|
func (b *Block) MixDigest() common.Hash { return b.header.MixDigest } |
||||||
|
func (b *Block) Nonce() uint64 { return binary.BigEndian.Uint64(b.header.Nonce[:]) } |
||||||
|
func (b *Block) Bloom() Bloom { return b.header.Bloom } |
||||||
|
func (b *Block) Coinbase() common.Address { return b.header.Coinbase } |
||||||
|
func (b *Block) Root() common.Hash { return b.header.Root } |
||||||
|
func (b *Block) ParentHash() common.Hash { return b.header.ParentHash } |
||||||
|
func (b *Block) TxHash() common.Hash { return b.header.TxHash } |
||||||
|
func (b *Block) ReceiptHash() common.Hash { return b.header.ReceiptHash } |
||||||
|
func (b *Block) UncleHash() common.Hash { return b.header.UncleHash } |
||||||
|
func (b *Block) Extra() []byte { return common.CopyBytes(b.header.Extra) } |
||||||
|
|
||||||
|
func (b *Block) Header() *Header { return CopyHeader(b.header) } |
||||||
|
|
||||||
|
// Body returns the non-header content of the block.
|
||||||
|
func (b *Block) Body() *Body { return &Body{b.transactions, b.uncles} } |
||||||
|
|
||||||
|
// Size returns the true RLP encoded storage size of the block, either by encoding
|
||||||
|
// and returning it, or returning a previsouly cached value.
|
||||||
|
func (b *Block) Size() common.StorageSize { |
||||||
|
if size := b.size.Load(); size != nil { |
||||||
|
return size.(common.StorageSize) |
||||||
|
} |
||||||
|
c := writeCounter(0) |
||||||
|
rlp.Encode(&c, b) |
||||||
|
b.size.Store(common.StorageSize(c)) |
||||||
|
return common.StorageSize(c) |
||||||
|
} |
||||||
|
|
||||||
|
type writeCounter common.StorageSize |
||||||
|
|
||||||
|
func (c *writeCounter) Write(b []byte) (int, error) { |
||||||
|
*c += writeCounter(len(b)) |
||||||
|
return len(b), nil |
||||||
|
} |
||||||
|
|
||||||
|
func CalcUncleHash(uncles []*Header) common.Hash { |
||||||
|
return rlpHash(uncles) |
||||||
|
} |
||||||
|
|
||||||
|
// WithSeal returns a new block with the data from b but the header replaced with
|
||||||
|
// the sealed one.
|
||||||
|
func (b *Block) WithSeal(header *Header) *Block { |
||||||
|
cpy := *header |
||||||
|
|
||||||
|
return &Block{ |
||||||
|
header: &cpy, |
||||||
|
transactions: b.transactions, |
||||||
|
uncles: b.uncles, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// WithBody returns a new block with the given transaction and uncle contents.
|
||||||
|
func (b *Block) WithBody(transactions []*Transaction, uncles []*Header) *Block { |
||||||
|
block := &Block{ |
||||||
|
header: CopyHeader(b.header), |
||||||
|
transactions: make([]*Transaction, len(transactions)), |
||||||
|
uncles: make([]*Header, len(uncles)), |
||||||
|
} |
||||||
|
copy(block.transactions, transactions) |
||||||
|
for i := range uncles { |
||||||
|
block.uncles[i] = CopyHeader(uncles[i]) |
||||||
|
} |
||||||
|
return block |
||||||
|
} |
||||||
|
|
||||||
|
// Hash returns the keccak256 hash of b's header.
|
||||||
|
// The hash is computed on the first call and cached thereafter.
|
||||||
|
func (b *Block) Hash() common.Hash { |
||||||
|
if hash := b.hash.Load(); hash != nil { |
||||||
|
return hash.(common.Hash) |
||||||
|
} |
||||||
|
v := b.header.Hash() |
||||||
|
b.hash.Store(v) |
||||||
|
return v |
||||||
|
} |
||||||
|
|
||||||
|
type Blocks []*Block |
||||||
|
|
||||||
|
type BlockBy func(b1, b2 *Block) bool |
||||||
|
|
||||||
|
func (self BlockBy) Sort(blocks Blocks) { |
||||||
|
bs := blockSorter{ |
||||||
|
blocks: blocks, |
||||||
|
by: self, |
||||||
|
} |
||||||
|
sort.Sort(bs) |
||||||
|
} |
||||||
|
|
||||||
|
type blockSorter struct { |
||||||
|
blocks Blocks |
||||||
|
by func(b1, b2 *Block) bool |
||||||
|
} |
||||||
|
|
||||||
|
func (self blockSorter) Len() int { return len(self.blocks) } |
||||||
|
func (self blockSorter) Swap(i, j int) { |
||||||
|
self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i] |
||||||
|
} |
||||||
|
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) } |
||||||
|
|
||||||
|
func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 } |
@ -0,0 +1,70 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
"reflect" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
// from bcValidBlockTest.json, "SimpleTx"
|
||||||
|
func TestBlockEncoding(t *testing.T) { |
||||||
|
blockEnc := common.FromHex("f90260f901f9a083cafc574e1f51ba9dc0568fc617a08ea2429fb384059c972f13b19fa1c8dd55a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948888f1f195afa192cfee860698584c030f4c9db1a0ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017a05fe50b260da6308036625b850b5d6ced6d0a9f814c0688bc91ffb7b7a3a54b67a0bc37d79753ad738a6dac4921e57392f145d8887476de3f783dfa7edae9283e52b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008302000001832fefd8825208845506eb0780a0bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff49888a13a5a8c8f2bb1c4f861f85f800a82c35094095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba09bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094fa08a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1c0") |
||||||
|
var block Block |
||||||
|
if err := rlp.DecodeBytes(blockEnc, &block); err != nil { |
||||||
|
t.Fatal("decode error: ", err) |
||||||
|
} |
||||||
|
|
||||||
|
check := func(f string, got, want interface{}) { |
||||||
|
if !reflect.DeepEqual(got, want) { |
||||||
|
t.Errorf("%s mismatch: got %v, want %v", f, got, want) |
||||||
|
} |
||||||
|
} |
||||||
|
check("Difficulty", block.Difficulty(), big.NewInt(131072)) |
||||||
|
check("GasLimit", block.GasLimit(), uint64(3141592)) |
||||||
|
check("GasUsed", block.GasUsed(), uint64(21000)) |
||||||
|
check("Coinbase", block.Coinbase(), common.HexToAddress("8888f1f195afa192cfee860698584c030f4c9db1")) |
||||||
|
check("MixDigest", block.MixDigest(), common.HexToHash("bd4472abb6659ebe3ee06ee4d7b72a00a9f4d001caca51342001075469aff498")) |
||||||
|
check("Root", block.Root(), common.HexToHash("ef1552a40b7165c3cd773806b9e0c165b75356e0314bf0706f279c729f51e017")) |
||||||
|
check("Hash", block.Hash(), common.HexToHash("0a5843ac1cb04865017cb35a57b50b07084e5fcee39b5acadade33149f4fff9e")) |
||||||
|
check("Nonce", block.Nonce(), uint64(0xa13a5a8c8f2bb1c4)) |
||||||
|
check("Time", block.Time(), big.NewInt(1426516743)) |
||||||
|
check("Size", block.Size(), common.StorageSize(len(blockEnc))) |
||||||
|
|
||||||
|
tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), 50000, big.NewInt(10), nil) |
||||||
|
|
||||||
|
tx1, _ = tx1.WithSignature(HomesteadSigner{}, common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100")) |
||||||
|
fmt.Println(block.Transactions()[0].Hash()) |
||||||
|
fmt.Println(tx1.data) |
||||||
|
fmt.Println(tx1.Hash()) |
||||||
|
check("len(Transactions)", len(block.Transactions()), 1) |
||||||
|
check("Transactions[0].Hash", block.Transactions()[0].Hash(), tx1.Hash()) |
||||||
|
|
||||||
|
ourBlockEnc, err := rlp.EncodeToBytes(&block) |
||||||
|
if err != nil { |
||||||
|
t.Fatal("encode error: ", err) |
||||||
|
} |
||||||
|
if !bytes.Equal(ourBlockEnc, blockEnc) { |
||||||
|
t.Errorf("encoded block mismatch:\ngot: %x\nwant: %x", ourBlockEnc, blockEnc) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,136 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
) |
||||||
|
|
||||||
|
type bytesBacked interface { |
||||||
|
Bytes() []byte |
||||||
|
} |
||||||
|
|
||||||
|
const ( |
||||||
|
// BloomByteLength represents the number of bytes used in a header log bloom.
|
||||||
|
BloomByteLength = 256 |
||||||
|
|
||||||
|
// BloomBitLength represents the number of bits used in a header log bloom.
|
||||||
|
BloomBitLength = 8 * BloomByteLength |
||||||
|
) |
||||||
|
|
||||||
|
// Bloom represents a 2048 bit bloom filter.
|
||||||
|
type Bloom [BloomByteLength]byte |
||||||
|
|
||||||
|
// BytesToBloom converts a byte slice to a bloom filter.
|
||||||
|
// It panics if b is not of suitable size.
|
||||||
|
func BytesToBloom(b []byte) Bloom { |
||||||
|
var bloom Bloom |
||||||
|
bloom.SetBytes(b) |
||||||
|
return bloom |
||||||
|
} |
||||||
|
|
||||||
|
// SetBytes sets the content of b to the given bytes.
|
||||||
|
// It panics if d is not of suitable size.
|
||||||
|
func (b *Bloom) SetBytes(d []byte) { |
||||||
|
if len(b) < len(d) { |
||||||
|
panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d))) |
||||||
|
} |
||||||
|
copy(b[BloomByteLength-len(d):], d) |
||||||
|
} |
||||||
|
|
||||||
|
// Add adds d to the filter. Future calls of Test(d) will return true.
|
||||||
|
func (b *Bloom) Add(d *big.Int) { |
||||||
|
bin := new(big.Int).SetBytes(b[:]) |
||||||
|
bin.Or(bin, bloom9(d.Bytes())) |
||||||
|
b.SetBytes(bin.Bytes()) |
||||||
|
} |
||||||
|
|
||||||
|
// Big converts b to a big integer.
|
||||||
|
func (b Bloom) Big() *big.Int { |
||||||
|
return new(big.Int).SetBytes(b[:]) |
||||||
|
} |
||||||
|
|
||||||
|
func (b Bloom) Bytes() []byte { |
||||||
|
return b[:] |
||||||
|
} |
||||||
|
|
||||||
|
func (b Bloom) Test(test *big.Int) bool { |
||||||
|
return BloomLookup(b, test) |
||||||
|
} |
||||||
|
|
||||||
|
func (b Bloom) TestBytes(test []byte) bool { |
||||||
|
return b.Test(new(big.Int).SetBytes(test)) |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// MarshalText encodes b as a hex string with 0x prefix.
|
||||||
|
func (b Bloom) MarshalText() ([]byte, error) { |
||||||
|
return hexutil.Bytes(b[:]).MarshalText() |
||||||
|
} |
||||||
|
|
||||||
|
// UnmarshalText b as a hex string with 0x prefix.
|
||||||
|
func (b *Bloom) UnmarshalText(input []byte) error { |
||||||
|
return hexutil.UnmarshalFixedText("Bloom", input, b[:]) |
||||||
|
} |
||||||
|
|
||||||
|
func CreateBloom(receipts Receipts) Bloom { |
||||||
|
bin := new(big.Int) |
||||||
|
for _, receipt := range receipts { |
||||||
|
bin.Or(bin, LogsBloom(receipt.Logs)) |
||||||
|
} |
||||||
|
|
||||||
|
return BytesToBloom(bin.Bytes()) |
||||||
|
} |
||||||
|
|
||||||
|
func LogsBloom(logs []*Log) *big.Int { |
||||||
|
bin := new(big.Int) |
||||||
|
for _, log := range logs { |
||||||
|
bin.Or(bin, bloom9(log.Address.Bytes())) |
||||||
|
for _, b := range log.Topics { |
||||||
|
bin.Or(bin, bloom9(b[:])) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return bin |
||||||
|
} |
||||||
|
|
||||||
|
func bloom9(b []byte) *big.Int { |
||||||
|
b = crypto.Keccak256(b) |
||||||
|
|
||||||
|
r := new(big.Int) |
||||||
|
|
||||||
|
for i := 0; i < 6; i += 2 { |
||||||
|
t := big.NewInt(1) |
||||||
|
b := (uint(b[i+1]) + (uint(b[i]) << 8)) & 2047 |
||||||
|
r.Or(r, t.Lsh(t, b)) |
||||||
|
} |
||||||
|
|
||||||
|
return r |
||||||
|
} |
||||||
|
|
||||||
|
var Bloom9 = bloom9 |
||||||
|
|
||||||
|
func BloomLookup(bin Bloom, topic bytesBacked) bool { |
||||||
|
bloom := bin.Big() |
||||||
|
cmp := bloom9(topic.Bytes()) |
||||||
|
|
||||||
|
return bloom.And(bloom, cmp).Cmp(cmp) == 0 |
||||||
|
} |
@ -0,0 +1,81 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
func TestBloom(t *testing.T) { |
||||||
|
positive := []string{ |
||||||
|
"testtest", |
||||||
|
"test", |
||||||
|
"hallo", |
||||||
|
"other", |
||||||
|
} |
||||||
|
negative := []string{ |
||||||
|
"tes", |
||||||
|
"lo", |
||||||
|
} |
||||||
|
|
||||||
|
var bloom Bloom |
||||||
|
for _, data := range positive { |
||||||
|
bloom.Add(new(big.Int).SetBytes([]byte(data))) |
||||||
|
} |
||||||
|
|
||||||
|
for _, data := range positive { |
||||||
|
if !bloom.TestBytes([]byte(data)) { |
||||||
|
t.Error("expected", data, "to test true") |
||||||
|
} |
||||||
|
} |
||||||
|
for _, data := range negative { |
||||||
|
if bloom.TestBytes([]byte(data)) { |
||||||
|
t.Error("did not expect", data, "to test true") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
import ( |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
) |
||||||
|
|
||||||
|
func TestBloom9(t *testing.T) { |
||||||
|
testCase := []byte("testtest") |
||||||
|
bin := LogsBloom([]state.Log{ |
||||||
|
{testCase, [][]byte{[]byte("hellohello")}, nil}, |
||||||
|
}).Bytes() |
||||||
|
res := BloomLookup(bin, testCase) |
||||||
|
|
||||||
|
if !res { |
||||||
|
t.Errorf("Bloom lookup failed") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
func TestAddress(t *testing.T) { |
||||||
|
block := &Block{} |
||||||
|
block.Coinbase = common.Hex2Bytes("22341ae42d6dd7384bc8584e50419ea3ac75b83f") |
||||||
|
fmt.Printf("%x\n", crypto.Keccak256(block.Coinbase)) |
||||||
|
|
||||||
|
bin := CreateBloom(block) |
||||||
|
fmt.Printf("bin = %x\n", common.LeftPadBytes(bin, 64)) |
||||||
|
} |
||||||
|
*/ |
@ -0,0 +1,41 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
"github.com/ethereum/go-ethereum/trie" |
||||||
|
) |
||||||
|
|
||||||
|
type DerivableList interface { |
||||||
|
Len() int |
||||||
|
GetRlp(i int) []byte |
||||||
|
} |
||||||
|
|
||||||
|
func DeriveSha(list DerivableList) common.Hash { |
||||||
|
keybuf := new(bytes.Buffer) |
||||||
|
trie := new(trie.Trie) |
||||||
|
for i := 0; i < list.Len(); i++ { |
||||||
|
keybuf.Reset() |
||||||
|
rlp.Encode(keybuf, uint(i)) |
||||||
|
trie.Update(keybuf.Bytes(), list.GetRlp(i)) |
||||||
|
} |
||||||
|
return trie.Hash() |
||||||
|
} |
@ -0,0 +1,138 @@ |
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
) |
||||||
|
|
||||||
|
var _ = (*headerMarshaling)(nil) |
||||||
|
|
||||||
|
func (h Header) MarshalJSON() ([]byte, error) { |
||||||
|
type Header struct { |
||||||
|
ParentHash common.Hash `json:"parentHash" gencodec:"required"` |
||||||
|
UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` |
||||||
|
Coinbase common.Address `json:"miner" gencodec:"required"` |
||||||
|
Root common.Hash `json:"stateRoot" gencodec:"required"` |
||||||
|
TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` |
||||||
|
ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` |
||||||
|
Bloom Bloom `json:"logsBloom" gencodec:"required"` |
||||||
|
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` |
||||||
|
Number *hexutil.Big `json:"number" gencodec:"required"` |
||||||
|
GasLimit hexutil.Uint64 `json:"gasLimit" gencodec:"required"` |
||||||
|
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` |
||||||
|
Time *hexutil.Big `json:"timestamp" gencodec:"required"` |
||||||
|
Extra hexutil.Bytes `json:"extraData" gencodec:"required"` |
||||||
|
MixDigest common.Hash `json:"mixHash" gencodec:"required"` |
||||||
|
Nonce BlockNonce `json:"nonce" gencodec:"required"` |
||||||
|
Hash common.Hash `json:"hash"` |
||||||
|
} |
||||||
|
var enc Header |
||||||
|
enc.ParentHash = h.ParentHash |
||||||
|
enc.UncleHash = h.UncleHash |
||||||
|
enc.Coinbase = h.Coinbase |
||||||
|
enc.Root = h.Root |
||||||
|
enc.TxHash = h.TxHash |
||||||
|
enc.ReceiptHash = h.ReceiptHash |
||||||
|
enc.Bloom = h.Bloom |
||||||
|
enc.Difficulty = (*hexutil.Big)(h.Difficulty) |
||||||
|
enc.Number = (*hexutil.Big)(h.Number) |
||||||
|
enc.GasLimit = hexutil.Uint64(h.GasLimit) |
||||||
|
enc.GasUsed = hexutil.Uint64(h.GasUsed) |
||||||
|
enc.Time = (*hexutil.Big)(h.Time) |
||||||
|
enc.Extra = h.Extra |
||||||
|
enc.MixDigest = h.MixDigest |
||||||
|
enc.Nonce = h.Nonce |
||||||
|
enc.Hash = h.Hash() |
||||||
|
return json.Marshal(&enc) |
||||||
|
} |
||||||
|
|
||||||
|
func (h *Header) UnmarshalJSON(input []byte) error { |
||||||
|
type Header struct { |
||||||
|
ParentHash *common.Hash `json:"parentHash" gencodec:"required"` |
||||||
|
UncleHash *common.Hash `json:"sha3Uncles" gencodec:"required"` |
||||||
|
Coinbase *common.Address `json:"miner" gencodec:"required"` |
||||||
|
Root *common.Hash `json:"stateRoot" gencodec:"required"` |
||||||
|
TxHash *common.Hash `json:"transactionsRoot" gencodec:"required"` |
||||||
|
ReceiptHash *common.Hash `json:"receiptsRoot" gencodec:"required"` |
||||||
|
Bloom *Bloom `json:"logsBloom" gencodec:"required"` |
||||||
|
Difficulty *hexutil.Big `json:"difficulty" gencodec:"required"` |
||||||
|
Number *hexutil.Big `json:"number" gencodec:"required"` |
||||||
|
GasLimit *hexutil.Uint64 `json:"gasLimit" gencodec:"required"` |
||||||
|
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` |
||||||
|
Time *hexutil.Big `json:"timestamp" gencodec:"required"` |
||||||
|
Extra *hexutil.Bytes `json:"extraData" gencodec:"required"` |
||||||
|
MixDigest *common.Hash `json:"mixHash" gencodec:"required"` |
||||||
|
Nonce *BlockNonce `json:"nonce" gencodec:"required"` |
||||||
|
} |
||||||
|
var dec Header |
||||||
|
if err := json.Unmarshal(input, &dec); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if dec.ParentHash == nil { |
||||||
|
return errors.New("missing required field 'parentHash' for Header") |
||||||
|
} |
||||||
|
h.ParentHash = *dec.ParentHash |
||||||
|
if dec.UncleHash == nil { |
||||||
|
return errors.New("missing required field 'sha3Uncles' for Header") |
||||||
|
} |
||||||
|
h.UncleHash = *dec.UncleHash |
||||||
|
if dec.Coinbase == nil { |
||||||
|
return errors.New("missing required field 'miner' for Header") |
||||||
|
} |
||||||
|
h.Coinbase = *dec.Coinbase |
||||||
|
if dec.Root == nil { |
||||||
|
return errors.New("missing required field 'stateRoot' for Header") |
||||||
|
} |
||||||
|
h.Root = *dec.Root |
||||||
|
if dec.TxHash == nil { |
||||||
|
return errors.New("missing required field 'transactionsRoot' for Header") |
||||||
|
} |
||||||
|
h.TxHash = *dec.TxHash |
||||||
|
if dec.ReceiptHash == nil { |
||||||
|
return errors.New("missing required field 'receiptsRoot' for Header") |
||||||
|
} |
||||||
|
h.ReceiptHash = *dec.ReceiptHash |
||||||
|
if dec.Bloom == nil { |
||||||
|
return errors.New("missing required field 'logsBloom' for Header") |
||||||
|
} |
||||||
|
h.Bloom = *dec.Bloom |
||||||
|
if dec.Difficulty == nil { |
||||||
|
return errors.New("missing required field 'difficulty' for Header") |
||||||
|
} |
||||||
|
h.Difficulty = (*big.Int)(dec.Difficulty) |
||||||
|
if dec.Number == nil { |
||||||
|
return errors.New("missing required field 'number' for Header") |
||||||
|
} |
||||||
|
h.Number = (*big.Int)(dec.Number) |
||||||
|
if dec.GasLimit == nil { |
||||||
|
return errors.New("missing required field 'gasLimit' for Header") |
||||||
|
} |
||||||
|
h.GasLimit = uint64(*dec.GasLimit) |
||||||
|
if dec.GasUsed == nil { |
||||||
|
return errors.New("missing required field 'gasUsed' for Header") |
||||||
|
} |
||||||
|
h.GasUsed = uint64(*dec.GasUsed) |
||||||
|
if dec.Time == nil { |
||||||
|
return errors.New("missing required field 'timestamp' for Header") |
||||||
|
} |
||||||
|
h.Time = (*big.Int)(dec.Time) |
||||||
|
if dec.Extra == nil { |
||||||
|
return errors.New("missing required field 'extraData' for Header") |
||||||
|
} |
||||||
|
h.Extra = *dec.Extra |
||||||
|
if dec.MixDigest == nil { |
||||||
|
return errors.New("missing required field 'mixHash' for Header") |
||||||
|
} |
||||||
|
h.MixDigest = *dec.MixDigest |
||||||
|
if dec.Nonce == nil { |
||||||
|
return errors.New("missing required field 'nonce' for Header") |
||||||
|
} |
||||||
|
h.Nonce = *dec.Nonce |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
) |
||||||
|
|
||||||
|
var _ = (*logMarshaling)(nil) |
||||||
|
|
||||||
|
func (l Log) MarshalJSON() ([]byte, error) { |
||||||
|
type Log struct { |
||||||
|
Address common.Address `json:"address" gencodec:"required"` |
||||||
|
Topics []common.Hash `json:"topics" gencodec:"required"` |
||||||
|
Data hexutil.Bytes `json:"data" gencodec:"required"` |
||||||
|
BlockNumber hexutil.Uint64 `json:"blockNumber"` |
||||||
|
TxHash common.Hash `json:"transactionHash" gencodec:"required"` |
||||||
|
TxIndex hexutil.Uint `json:"transactionIndex" gencodec:"required"` |
||||||
|
BlockHash common.Hash `json:"blockHash"` |
||||||
|
Index hexutil.Uint `json:"logIndex" gencodec:"required"` |
||||||
|
Removed bool `json:"removed"` |
||||||
|
} |
||||||
|
var enc Log |
||||||
|
enc.Address = l.Address |
||||||
|
enc.Topics = l.Topics |
||||||
|
enc.Data = l.Data |
||||||
|
enc.BlockNumber = hexutil.Uint64(l.BlockNumber) |
||||||
|
enc.TxHash = l.TxHash |
||||||
|
enc.TxIndex = hexutil.Uint(l.TxIndex) |
||||||
|
enc.BlockHash = l.BlockHash |
||||||
|
enc.Index = hexutil.Uint(l.Index) |
||||||
|
enc.Removed = l.Removed |
||||||
|
return json.Marshal(&enc) |
||||||
|
} |
||||||
|
|
||||||
|
func (l *Log) UnmarshalJSON(input []byte) error { |
||||||
|
type Log struct { |
||||||
|
Address *common.Address `json:"address" gencodec:"required"` |
||||||
|
Topics []common.Hash `json:"topics" gencodec:"required"` |
||||||
|
Data *hexutil.Bytes `json:"data" gencodec:"required"` |
||||||
|
BlockNumber *hexutil.Uint64 `json:"blockNumber"` |
||||||
|
TxHash *common.Hash `json:"transactionHash" gencodec:"required"` |
||||||
|
TxIndex *hexutil.Uint `json:"transactionIndex" gencodec:"required"` |
||||||
|
BlockHash *common.Hash `json:"blockHash"` |
||||||
|
Index *hexutil.Uint `json:"logIndex" gencodec:"required"` |
||||||
|
Removed *bool `json:"removed"` |
||||||
|
} |
||||||
|
var dec Log |
||||||
|
if err := json.Unmarshal(input, &dec); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if dec.Address == nil { |
||||||
|
return errors.New("missing required field 'address' for Log") |
||||||
|
} |
||||||
|
l.Address = *dec.Address |
||||||
|
if dec.Topics == nil { |
||||||
|
return errors.New("missing required field 'topics' for Log") |
||||||
|
} |
||||||
|
l.Topics = dec.Topics |
||||||
|
if dec.Data == nil { |
||||||
|
return errors.New("missing required field 'data' for Log") |
||||||
|
} |
||||||
|
l.Data = *dec.Data |
||||||
|
if dec.BlockNumber != nil { |
||||||
|
l.BlockNumber = uint64(*dec.BlockNumber) |
||||||
|
} |
||||||
|
if dec.TxHash == nil { |
||||||
|
return errors.New("missing required field 'transactionHash' for Log") |
||||||
|
} |
||||||
|
l.TxHash = *dec.TxHash |
||||||
|
if dec.TxIndex == nil { |
||||||
|
return errors.New("missing required field 'transactionIndex' for Log") |
||||||
|
} |
||||||
|
l.TxIndex = uint(*dec.TxIndex) |
||||||
|
if dec.BlockHash != nil { |
||||||
|
l.BlockHash = *dec.BlockHash |
||||||
|
} |
||||||
|
if dec.Index == nil { |
||||||
|
return errors.New("missing required field 'logIndex' for Log") |
||||||
|
} |
||||||
|
l.Index = uint(*dec.Index) |
||||||
|
if dec.Removed != nil { |
||||||
|
l.Removed = *dec.Removed |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
) |
||||||
|
|
||||||
|
var _ = (*receiptMarshaling)(nil) |
||||||
|
|
||||||
|
// MarshalJSON marshals as JSON.
|
||||||
|
func (r Receipt) MarshalJSON() ([]byte, error) { |
||||||
|
type Receipt struct { |
||||||
|
PostState hexutil.Bytes `json:"root"` |
||||||
|
Status hexutil.Uint64 `json:"status"` |
||||||
|
CumulativeGasUsed hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"` |
||||||
|
Bloom Bloom `json:"logsBloom" gencodec:"required"` |
||||||
|
Logs []*Log `json:"logs" gencodec:"required"` |
||||||
|
TxHash common.Hash `json:"transactionHash" gencodec:"required"` |
||||||
|
ContractAddress common.Address `json:"contractAddress"` |
||||||
|
GasUsed hexutil.Uint64 `json:"gasUsed" gencodec:"required"` |
||||||
|
} |
||||||
|
var enc Receipt |
||||||
|
enc.PostState = r.PostState |
||||||
|
enc.Status = hexutil.Uint64(r.Status) |
||||||
|
enc.CumulativeGasUsed = hexutil.Uint64(r.CumulativeGasUsed) |
||||||
|
enc.Bloom = r.Bloom |
||||||
|
enc.Logs = r.Logs |
||||||
|
enc.TxHash = r.TxHash |
||||||
|
enc.ContractAddress = r.ContractAddress |
||||||
|
enc.GasUsed = hexutil.Uint64(r.GasUsed) |
||||||
|
return json.Marshal(&enc) |
||||||
|
} |
||||||
|
|
||||||
|
// UnmarshalJSON unmarshals from JSON.
|
||||||
|
func (r *Receipt) UnmarshalJSON(input []byte) error { |
||||||
|
type Receipt struct { |
||||||
|
PostState *hexutil.Bytes `json:"root"` |
||||||
|
Status *hexutil.Uint64 `json:"status"` |
||||||
|
CumulativeGasUsed *hexutil.Uint64 `json:"cumulativeGasUsed" gencodec:"required"` |
||||||
|
Bloom *Bloom `json:"logsBloom" gencodec:"required"` |
||||||
|
Logs []*Log `json:"logs" gencodec:"required"` |
||||||
|
TxHash *common.Hash `json:"transactionHash" gencodec:"required"` |
||||||
|
ContractAddress *common.Address `json:"contractAddress"` |
||||||
|
GasUsed *hexutil.Uint64 `json:"gasUsed" gencodec:"required"` |
||||||
|
} |
||||||
|
var dec Receipt |
||||||
|
if err := json.Unmarshal(input, &dec); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if dec.PostState != nil { |
||||||
|
r.PostState = *dec.PostState |
||||||
|
} |
||||||
|
if dec.Status != nil { |
||||||
|
r.Status = uint64(*dec.Status) |
||||||
|
} |
||||||
|
if dec.CumulativeGasUsed == nil { |
||||||
|
return errors.New("missing required field 'cumulativeGasUsed' for Receipt") |
||||||
|
} |
||||||
|
r.CumulativeGasUsed = uint64(*dec.CumulativeGasUsed) |
||||||
|
if dec.Bloom == nil { |
||||||
|
return errors.New("missing required field 'logsBloom' for Receipt") |
||||||
|
} |
||||||
|
r.Bloom = *dec.Bloom |
||||||
|
if dec.Logs == nil { |
||||||
|
return errors.New("missing required field 'logs' for Receipt") |
||||||
|
} |
||||||
|
r.Logs = dec.Logs |
||||||
|
if dec.TxHash == nil { |
||||||
|
return errors.New("missing required field 'transactionHash' for Receipt") |
||||||
|
} |
||||||
|
r.TxHash = *dec.TxHash |
||||||
|
if dec.ContractAddress != nil { |
||||||
|
r.ContractAddress = *dec.ContractAddress |
||||||
|
} |
||||||
|
if dec.GasUsed == nil { |
||||||
|
return errors.New("missing required field 'gasUsed' for Receipt") |
||||||
|
} |
||||||
|
r.GasUsed = uint64(*dec.GasUsed) |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,99 @@ |
|||||||
|
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"errors" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
) |
||||||
|
|
||||||
|
var _ = (*txdataMarshaling)(nil) |
||||||
|
|
||||||
|
func (t txdata) MarshalJSON() ([]byte, error) { |
||||||
|
type txdata struct { |
||||||
|
AccountNonce hexutil.Uint64 `json:"nonce" gencodec:"required"` |
||||||
|
Price *hexutil.Big `json:"gasPrice" gencodec:"required"` |
||||||
|
GasLimit hexutil.Uint64 `json:"gas" gencodec:"required"` |
||||||
|
Recipient *common.Address `json:"to" rlp:"nil"` |
||||||
|
Amount *hexutil.Big `json:"value" gencodec:"required"` |
||||||
|
Payload hexutil.Bytes `json:"input" gencodec:"required"` |
||||||
|
V *hexutil.Big `json:"v" gencodec:"required"` |
||||||
|
R *hexutil.Big `json:"r" gencodec:"required"` |
||||||
|
S *hexutil.Big `json:"s" gencodec:"required"` |
||||||
|
Hash *common.Hash `json:"hash" rlp:"-"` |
||||||
|
} |
||||||
|
var enc txdata |
||||||
|
enc.AccountNonce = hexutil.Uint64(t.AccountNonce) |
||||||
|
enc.Price = (*hexutil.Big)(t.Price) |
||||||
|
enc.GasLimit = hexutil.Uint64(t.GasLimit) |
||||||
|
enc.Recipient = t.Recipient |
||||||
|
enc.Amount = (*hexutil.Big)(t.Amount) |
||||||
|
enc.Payload = t.Payload |
||||||
|
enc.V = (*hexutil.Big)(t.V) |
||||||
|
enc.R = (*hexutil.Big)(t.R) |
||||||
|
enc.S = (*hexutil.Big)(t.S) |
||||||
|
enc.Hash = t.Hash |
||||||
|
return json.Marshal(&enc) |
||||||
|
} |
||||||
|
|
||||||
|
func (t *txdata) UnmarshalJSON(input []byte) error { |
||||||
|
type txdata struct { |
||||||
|
AccountNonce *hexutil.Uint64 `json:"nonce" gencodec:"required"` |
||||||
|
Price *hexutil.Big `json:"gasPrice" gencodec:"required"` |
||||||
|
GasLimit *hexutil.Uint64 `json:"gas" gencodec:"required"` |
||||||
|
Recipient *common.Address `json:"to" rlp:"nil"` |
||||||
|
Amount *hexutil.Big `json:"value" gencodec:"required"` |
||||||
|
Payload *hexutil.Bytes `json:"input" gencodec:"required"` |
||||||
|
V *hexutil.Big `json:"v" gencodec:"required"` |
||||||
|
R *hexutil.Big `json:"r" gencodec:"required"` |
||||||
|
S *hexutil.Big `json:"s" gencodec:"required"` |
||||||
|
Hash *common.Hash `json:"hash" rlp:"-"` |
||||||
|
} |
||||||
|
var dec txdata |
||||||
|
if err := json.Unmarshal(input, &dec); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if dec.AccountNonce == nil { |
||||||
|
return errors.New("missing required field 'nonce' for txdata") |
||||||
|
} |
||||||
|
t.AccountNonce = uint64(*dec.AccountNonce) |
||||||
|
if dec.Price == nil { |
||||||
|
return errors.New("missing required field 'gasPrice' for txdata") |
||||||
|
} |
||||||
|
t.Price = (*big.Int)(dec.Price) |
||||||
|
if dec.GasLimit == nil { |
||||||
|
return errors.New("missing required field 'gas' for txdata") |
||||||
|
} |
||||||
|
t.GasLimit = uint64(*dec.GasLimit) |
||||||
|
if dec.Recipient != nil { |
||||||
|
t.Recipient = dec.Recipient |
||||||
|
} |
||||||
|
if dec.Amount == nil { |
||||||
|
return errors.New("missing required field 'value' for txdata") |
||||||
|
} |
||||||
|
t.Amount = (*big.Int)(dec.Amount) |
||||||
|
if dec.Payload == nil { |
||||||
|
return errors.New("missing required field 'input' for txdata") |
||||||
|
} |
||||||
|
t.Payload = *dec.Payload |
||||||
|
if dec.V == nil { |
||||||
|
return errors.New("missing required field 'v' for txdata") |
||||||
|
} |
||||||
|
t.V = (*big.Int)(dec.V) |
||||||
|
if dec.R == nil { |
||||||
|
return errors.New("missing required field 'r' for txdata") |
||||||
|
} |
||||||
|
t.R = (*big.Int)(dec.R) |
||||||
|
if dec.S == nil { |
||||||
|
return errors.New("missing required field 's' for txdata") |
||||||
|
} |
||||||
|
t.S = (*big.Int)(dec.S) |
||||||
|
if dec.Hash != nil { |
||||||
|
t.Hash = dec.Hash |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,132 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"io" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
//go:generate gencodec -type Log -field-override logMarshaling -out gen_log_json.go
|
||||||
|
|
||||||
|
// Log represents a contract log event. These events are generated by the LOG opcode and
|
||||||
|
// stored/indexed by the node.
|
||||||
|
type Log struct { |
||||||
|
// Consensus fields:
|
||||||
|
// address of the contract that generated the event
|
||||||
|
Address common.Address `json:"address" gencodec:"required"` |
||||||
|
// list of topics provided by the contract.
|
||||||
|
Topics []common.Hash `json:"topics" gencodec:"required"` |
||||||
|
// supplied by the contract, usually ABI-encoded
|
||||||
|
Data []byte `json:"data" gencodec:"required"` |
||||||
|
|
||||||
|
// Derived fields. These fields are filled in by the node
|
||||||
|
// but not secured by consensus.
|
||||||
|
// block in which the transaction was included
|
||||||
|
BlockNumber uint64 `json:"blockNumber"` |
||||||
|
// hash of the transaction
|
||||||
|
TxHash common.Hash `json:"transactionHash" gencodec:"required"` |
||||||
|
// index of the transaction in the block
|
||||||
|
TxIndex uint `json:"transactionIndex" gencodec:"required"` |
||||||
|
// hash of the block in which the transaction was included
|
||||||
|
BlockHash common.Hash `json:"blockHash"` |
||||||
|
// index of the log in the block
|
||||||
|
Index uint `json:"logIndex" gencodec:"required"` |
||||||
|
|
||||||
|
// The Removed field is true if this log was reverted due to a chain reorganisation.
|
||||||
|
// You must pay attention to this field if you receive logs through a filter query.
|
||||||
|
Removed bool `json:"removed"` |
||||||
|
} |
||||||
|
|
||||||
|
type logMarshaling struct { |
||||||
|
Data hexutil.Bytes |
||||||
|
BlockNumber hexutil.Uint64 |
||||||
|
TxIndex hexutil.Uint |
||||||
|
Index hexutil.Uint |
||||||
|
} |
||||||
|
|
||||||
|
type rlpLog struct { |
||||||
|
Address common.Address |
||||||
|
Topics []common.Hash |
||||||
|
Data []byte |
||||||
|
} |
||||||
|
|
||||||
|
type rlpStorageLog struct { |
||||||
|
Address common.Address |
||||||
|
Topics []common.Hash |
||||||
|
Data []byte |
||||||
|
BlockNumber uint64 |
||||||
|
TxHash common.Hash |
||||||
|
TxIndex uint |
||||||
|
BlockHash common.Hash |
||||||
|
Index uint |
||||||
|
} |
||||||
|
|
||||||
|
// EncodeRLP implements rlp.Encoder.
|
||||||
|
func (l *Log) EncodeRLP(w io.Writer) error { |
||||||
|
return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data}) |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeRLP implements rlp.Decoder.
|
||||||
|
func (l *Log) DecodeRLP(s *rlp.Stream) error { |
||||||
|
var dec rlpLog |
||||||
|
err := s.Decode(&dec) |
||||||
|
if err == nil { |
||||||
|
l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// LogForStorage is a wrapper around a Log that flattens and parses the entire content of
|
||||||
|
// a log including non-consensus fields.
|
||||||
|
type LogForStorage Log |
||||||
|
|
||||||
|
// EncodeRLP implements rlp.Encoder.
|
||||||
|
func (l *LogForStorage) EncodeRLP(w io.Writer) error { |
||||||
|
return rlp.Encode(w, rlpStorageLog{ |
||||||
|
Address: l.Address, |
||||||
|
Topics: l.Topics, |
||||||
|
Data: l.Data, |
||||||
|
BlockNumber: l.BlockNumber, |
||||||
|
TxHash: l.TxHash, |
||||||
|
TxIndex: l.TxIndex, |
||||||
|
BlockHash: l.BlockHash, |
||||||
|
Index: l.Index, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeRLP implements rlp.Decoder.
|
||||||
|
func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error { |
||||||
|
var dec rlpStorageLog |
||||||
|
err := s.Decode(&dec) |
||||||
|
if err == nil { |
||||||
|
*l = LogForStorage{ |
||||||
|
Address: dec.Address, |
||||||
|
Topics: dec.Topics, |
||||||
|
Data: dec.Data, |
||||||
|
BlockNumber: dec.BlockNumber, |
||||||
|
TxHash: dec.TxHash, |
||||||
|
TxIndex: dec.TxIndex, |
||||||
|
BlockHash: dec.BlockHash, |
||||||
|
Index: dec.Index, |
||||||
|
} |
||||||
|
} |
||||||
|
return err |
||||||
|
} |
@ -0,0 +1,132 @@ |
|||||||
|
// 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 types |
||||||
|
|
||||||
|
import ( |
||||||
|
"encoding/json" |
||||||
|
"fmt" |
||||||
|
"reflect" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/davecgh/go-spew/spew" |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
) |
||||||
|
|
||||||
|
var unmarshalLogTests = map[string]struct { |
||||||
|
input string |
||||||
|
want *Log |
||||||
|
wantError error |
||||||
|
}{ |
||||||
|
"ok": { |
||||||
|
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, |
||||||
|
want: &Log{ |
||||||
|
Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"), |
||||||
|
BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"), |
||||||
|
BlockNumber: 2019236, |
||||||
|
Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000001a055690d9db80000"), |
||||||
|
Index: 2, |
||||||
|
TxIndex: 3, |
||||||
|
TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"), |
||||||
|
Topics: []common.Hash{ |
||||||
|
common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), |
||||||
|
common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"), |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
"empty data": { |
||||||
|
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, |
||||||
|
want: &Log{ |
||||||
|
Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"), |
||||||
|
BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"), |
||||||
|
BlockNumber: 2019236, |
||||||
|
Data: []byte{}, |
||||||
|
Index: 2, |
||||||
|
TxIndex: 3, |
||||||
|
TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"), |
||||||
|
Topics: []common.Hash{ |
||||||
|
common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), |
||||||
|
common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"), |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
"missing block fields (pending logs)": { |
||||||
|
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","data":"0x","logIndex":"0x0","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, |
||||||
|
want: &Log{ |
||||||
|
Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"), |
||||||
|
BlockHash: common.Hash{}, |
||||||
|
BlockNumber: 0, |
||||||
|
Data: []byte{}, |
||||||
|
Index: 0, |
||||||
|
TxIndex: 3, |
||||||
|
TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"), |
||||||
|
Topics: []common.Hash{ |
||||||
|
common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
"Removed: true": { |
||||||
|
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3","removed":true}`, |
||||||
|
want: &Log{ |
||||||
|
Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"), |
||||||
|
BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"), |
||||||
|
BlockNumber: 2019236, |
||||||
|
Data: []byte{}, |
||||||
|
Index: 2, |
||||||
|
TxIndex: 3, |
||||||
|
TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"), |
||||||
|
Topics: []common.Hash{ |
||||||
|
common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), |
||||||
|
}, |
||||||
|
Removed: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
"missing data": { |
||||||
|
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`, |
||||||
|
wantError: fmt.Errorf("missing required field 'data' for Log"), |
||||||
|
}, |
||||||
|
} |
||||||
|
|
||||||
|
func TestUnmarshalLog(t *testing.T) { |
||||||
|
dumper := spew.ConfigState{DisableMethods: true, Indent: " "} |
||||||
|
for name, test := range unmarshalLogTests { |
||||||
|
var log *Log |
||||||
|
err := json.Unmarshal([]byte(test.input), &log) |
||||||
|
checkError(t, name, err, test.wantError) |
||||||
|
if test.wantError == nil && err == nil { |
||||||
|
if !reflect.DeepEqual(log, test.want) { |
||||||
|
t.Errorf("test %q:\nGOT %sWANT %s", name, dumper.Sdump(log), dumper.Sdump(test.want)) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func checkError(t *testing.T, testname string, got, want error) bool { |
||||||
|
if got == nil { |
||||||
|
if want != nil { |
||||||
|
t.Errorf("test %q: got no error, want %q", testname, want) |
||||||
|
return false |
||||||
|
} |
||||||
|
return true |
||||||
|
} |
||||||
|
if want == nil { |
||||||
|
t.Errorf("test %q: unexpected error %q", testname, got) |
||||||
|
} else if got.Error() != want.Error() { |
||||||
|
t.Errorf("test %q: got error %q, want %q", testname, got, want) |
||||||
|
} |
||||||
|
return false |
||||||
|
} |
@ -0,0 +1,208 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
"unsafe" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
//go:generate gencodec -type Receipt -field-override receiptMarshaling -out gen_receipt_json.go
|
||||||
|
|
||||||
|
var ( |
||||||
|
receiptStatusFailedRLP = []byte{} |
||||||
|
receiptStatusSuccessfulRLP = []byte{0x01} |
||||||
|
) |
||||||
|
|
||||||
|
const ( |
||||||
|
// ReceiptStatusFailed is the status code of a transaction if execution failed.
|
||||||
|
ReceiptStatusFailed = uint64(0) |
||||||
|
|
||||||
|
// ReceiptStatusSuccessful is the status code of a transaction if execution succeeded.
|
||||||
|
ReceiptStatusSuccessful = uint64(1) |
||||||
|
) |
||||||
|
|
||||||
|
// Receipt represents the results of a transaction.
|
||||||
|
type Receipt struct { |
||||||
|
// Consensus fields
|
||||||
|
PostState []byte `json:"root"` |
||||||
|
Status uint64 `json:"status"` |
||||||
|
CumulativeGasUsed uint64 `json:"cumulativeGasUsed" gencodec:"required"` |
||||||
|
Bloom Bloom `json:"logsBloom" gencodec:"required"` |
||||||
|
Logs []*Log `json:"logs" gencodec:"required"` |
||||||
|
|
||||||
|
// Implementation fields (don't reorder!)
|
||||||
|
TxHash common.Hash `json:"transactionHash" gencodec:"required"` |
||||||
|
ContractAddress common.Address `json:"contractAddress"` |
||||||
|
GasUsed uint64 `json:"gasUsed" gencodec:"required"` |
||||||
|
} |
||||||
|
|
||||||
|
type receiptMarshaling struct { |
||||||
|
PostState hexutil.Bytes |
||||||
|
Status hexutil.Uint64 |
||||||
|
CumulativeGasUsed hexutil.Uint64 |
||||||
|
GasUsed hexutil.Uint64 |
||||||
|
} |
||||||
|
|
||||||
|
// receiptRLP is the consensus encoding of a receipt.
|
||||||
|
type receiptRLP struct { |
||||||
|
PostStateOrStatus []byte |
||||||
|
CumulativeGasUsed uint64 |
||||||
|
Bloom Bloom |
||||||
|
Logs []*Log |
||||||
|
} |
||||||
|
|
||||||
|
type receiptStorageRLP struct { |
||||||
|
PostStateOrStatus []byte |
||||||
|
CumulativeGasUsed uint64 |
||||||
|
Bloom Bloom |
||||||
|
TxHash common.Hash |
||||||
|
ContractAddress common.Address |
||||||
|
Logs []*LogForStorage |
||||||
|
GasUsed uint64 |
||||||
|
} |
||||||
|
|
||||||
|
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||||
|
func NewReceipt(root []byte, failed bool, cumulativeGasUsed uint64) *Receipt { |
||||||
|
r := &Receipt{PostState: common.CopyBytes(root), CumulativeGasUsed: cumulativeGasUsed} |
||||||
|
if failed { |
||||||
|
r.Status = ReceiptStatusFailed |
||||||
|
} else { |
||||||
|
r.Status = ReceiptStatusSuccessful |
||||||
|
} |
||||||
|
return r |
||||||
|
} |
||||||
|
|
||||||
|
// EncodeRLP implements rlp.Encoder, and flattens the consensus fields of a receipt
|
||||||
|
// into an RLP stream. If no post state is present, byzantium fork is assumed.
|
||||||
|
func (r *Receipt) EncodeRLP(w io.Writer) error { |
||||||
|
return rlp.Encode(w, &receiptRLP{r.statusEncoding(), r.CumulativeGasUsed, r.Bloom, r.Logs}) |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeRLP implements rlp.Decoder, and loads the consensus fields of a receipt
|
||||||
|
// from an RLP stream.
|
||||||
|
func (r *Receipt) DecodeRLP(s *rlp.Stream) error { |
||||||
|
var dec receiptRLP |
||||||
|
if err := s.Decode(&dec); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if err := r.setStatus(dec.PostStateOrStatus); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
r.CumulativeGasUsed, r.Bloom, r.Logs = dec.CumulativeGasUsed, dec.Bloom, dec.Logs |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (r *Receipt) setStatus(postStateOrStatus []byte) error { |
||||||
|
switch { |
||||||
|
case bytes.Equal(postStateOrStatus, receiptStatusSuccessfulRLP): |
||||||
|
r.Status = ReceiptStatusSuccessful |
||||||
|
case bytes.Equal(postStateOrStatus, receiptStatusFailedRLP): |
||||||
|
r.Status = ReceiptStatusFailed |
||||||
|
case len(postStateOrStatus) == len(common.Hash{}): |
||||||
|
r.PostState = postStateOrStatus |
||||||
|
default: |
||||||
|
return fmt.Errorf("invalid receipt status %x", postStateOrStatus) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (r *Receipt) statusEncoding() []byte { |
||||||
|
if len(r.PostState) == 0 { |
||||||
|
if r.Status == ReceiptStatusFailed { |
||||||
|
return receiptStatusFailedRLP |
||||||
|
} |
||||||
|
return receiptStatusSuccessfulRLP |
||||||
|
} |
||||||
|
return r.PostState |
||||||
|
} |
||||||
|
|
||||||
|
// Size returns the approximate memory used by all internal contents. It is used
|
||||||
|
// to approximate and limit the memory consumption of various caches.
|
||||||
|
func (r *Receipt) Size() common.StorageSize { |
||||||
|
size := common.StorageSize(unsafe.Sizeof(*r)) + common.StorageSize(len(r.PostState)) |
||||||
|
|
||||||
|
size += common.StorageSize(len(r.Logs)) * common.StorageSize(unsafe.Sizeof(Log{})) |
||||||
|
for _, log := range r.Logs { |
||||||
|
size += common.StorageSize(len(log.Topics)*common.HashLength + len(log.Data)) |
||||||
|
} |
||||||
|
return size |
||||||
|
} |
||||||
|
|
||||||
|
// ReceiptForStorage is a wrapper around a Receipt that flattens and parses the
|
||||||
|
// entire content of a receipt, as opposed to only the consensus fields originally.
|
||||||
|
type ReceiptForStorage Receipt |
||||||
|
|
||||||
|
// EncodeRLP implements rlp.Encoder, and flattens all content fields of a receipt
|
||||||
|
// into an RLP stream.
|
||||||
|
func (r *ReceiptForStorage) EncodeRLP(w io.Writer) error { |
||||||
|
enc := &receiptStorageRLP{ |
||||||
|
PostStateOrStatus: (*Receipt)(r).statusEncoding(), |
||||||
|
CumulativeGasUsed: r.CumulativeGasUsed, |
||||||
|
Bloom: r.Bloom, |
||||||
|
TxHash: r.TxHash, |
||||||
|
ContractAddress: r.ContractAddress, |
||||||
|
Logs: make([]*LogForStorage, len(r.Logs)), |
||||||
|
GasUsed: r.GasUsed, |
||||||
|
} |
||||||
|
for i, log := range r.Logs { |
||||||
|
enc.Logs[i] = (*LogForStorage)(log) |
||||||
|
} |
||||||
|
return rlp.Encode(w, enc) |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeRLP implements rlp.Decoder, and loads both consensus and implementation
|
||||||
|
// fields of a receipt from an RLP stream.
|
||||||
|
func (r *ReceiptForStorage) DecodeRLP(s *rlp.Stream) error { |
||||||
|
var dec receiptStorageRLP |
||||||
|
if err := s.Decode(&dec); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if err := (*Receipt)(r).setStatus(dec.PostStateOrStatus); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
// Assign the consensus fields
|
||||||
|
r.CumulativeGasUsed, r.Bloom = dec.CumulativeGasUsed, dec.Bloom |
||||||
|
r.Logs = make([]*Log, len(dec.Logs)) |
||||||
|
for i, log := range dec.Logs { |
||||||
|
r.Logs[i] = (*Log)(log) |
||||||
|
} |
||||||
|
// Assign the implementation fields
|
||||||
|
r.TxHash, r.ContractAddress, r.GasUsed = dec.TxHash, dec.ContractAddress, dec.GasUsed |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// Receipts is a wrapper around a Receipt array to implement DerivableList.
|
||||||
|
type Receipts []*Receipt |
||||||
|
|
||||||
|
// Len returns the number of receipts in this list.
|
||||||
|
func (r Receipts) Len() int { return len(r) } |
||||||
|
|
||||||
|
// GetRlp returns the RLP encoding of one receipt from the list.
|
||||||
|
func (r Receipts) GetRlp(i int) []byte { |
||||||
|
bytes, err := rlp.EncodeToBytes(r[i]) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
return bytes |
||||||
|
} |
@ -0,0 +1,417 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"container/heap" |
||||||
|
"errors" |
||||||
|
"io" |
||||||
|
"math/big" |
||||||
|
"sync/atomic" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/common/hexutil" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
//go:generate gencodec -type txdata -field-override txdataMarshaling -out gen_tx_json.go
|
||||||
|
|
||||||
|
var ( |
||||||
|
ErrInvalidSig = errors.New("invalid transaction v, r, s values") |
||||||
|
) |
||||||
|
|
||||||
|
type Transaction struct { |
||||||
|
data txdata |
||||||
|
// caches
|
||||||
|
hash atomic.Value |
||||||
|
size atomic.Value |
||||||
|
from atomic.Value |
||||||
|
} |
||||||
|
|
||||||
|
type txdata struct { |
||||||
|
AccountNonce uint64 `json:"nonce" gencodec:"required"` |
||||||
|
Price *big.Int `json:"gasPrice" gencodec:"required"` |
||||||
|
GasLimit uint64 `json:"gas" gencodec:"required"` |
||||||
|
Recipient *common.Address `json:"to" rlp:"nil"` // nil means contract creation
|
||||||
|
Amount *big.Int `json:"value" gencodec:"required"` |
||||||
|
Payload []byte `json:"input" gencodec:"required"` |
||||||
|
|
||||||
|
// Signature values
|
||||||
|
V *big.Int `json:"v" gencodec:"required"` |
||||||
|
R *big.Int `json:"r" gencodec:"required"` |
||||||
|
S *big.Int `json:"s" gencodec:"required"` |
||||||
|
|
||||||
|
// This is only used when marshaling to JSON.
|
||||||
|
Hash *common.Hash `json:"hash" rlp:"-"` |
||||||
|
} |
||||||
|
|
||||||
|
type txdataMarshaling struct { |
||||||
|
AccountNonce hexutil.Uint64 |
||||||
|
Price *hexutil.Big |
||||||
|
GasLimit hexutil.Uint64 |
||||||
|
Amount *hexutil.Big |
||||||
|
Payload hexutil.Bytes |
||||||
|
V *hexutil.Big |
||||||
|
R *hexutil.Big |
||||||
|
S *hexutil.Big |
||||||
|
} |
||||||
|
|
||||||
|
func NewTransaction(nonce uint64, to common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { |
||||||
|
return newTransaction(nonce, &to, amount, gasLimit, gasPrice, data) |
||||||
|
} |
||||||
|
|
||||||
|
func NewContractCreation(nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { |
||||||
|
return newTransaction(nonce, nil, amount, gasLimit, gasPrice, data) |
||||||
|
} |
||||||
|
|
||||||
|
func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte) *Transaction { |
||||||
|
if len(data) > 0 { |
||||||
|
data = common.CopyBytes(data) |
||||||
|
} |
||||||
|
d := txdata{ |
||||||
|
AccountNonce: nonce, |
||||||
|
Recipient: to, |
||||||
|
Payload: data, |
||||||
|
Amount: new(big.Int), |
||||||
|
GasLimit: gasLimit, |
||||||
|
Price: new(big.Int), |
||||||
|
V: new(big.Int), |
||||||
|
R: new(big.Int), |
||||||
|
S: new(big.Int), |
||||||
|
} |
||||||
|
if amount != nil { |
||||||
|
d.Amount.Set(amount) |
||||||
|
} |
||||||
|
if gasPrice != nil { |
||||||
|
d.Price.Set(gasPrice) |
||||||
|
} |
||||||
|
|
||||||
|
return &Transaction{data: d} |
||||||
|
} |
||||||
|
|
||||||
|
// ChainId returns which chain id this transaction was signed for (if at all)
|
||||||
|
func (tx *Transaction) ChainId() *big.Int { |
||||||
|
return deriveChainId(tx.data.V) |
||||||
|
} |
||||||
|
|
||||||
|
// Protected returns whether the transaction is protected from replay protection.
|
||||||
|
func (tx *Transaction) Protected() bool { |
||||||
|
return isProtectedV(tx.data.V) |
||||||
|
} |
||||||
|
|
||||||
|
func isProtectedV(V *big.Int) bool { |
||||||
|
if V.BitLen() <= 8 { |
||||||
|
v := V.Uint64() |
||||||
|
return v != 27 && v != 28 |
||||||
|
} |
||||||
|
// anything not 27 or 28 is considered protected
|
||||||
|
return true |
||||||
|
} |
||||||
|
|
||||||
|
// EncodeRLP implements rlp.Encoder
|
||||||
|
func (tx *Transaction) EncodeRLP(w io.Writer) error { |
||||||
|
return rlp.Encode(w, &tx.data) |
||||||
|
} |
||||||
|
|
||||||
|
// DecodeRLP implements rlp.Decoder
|
||||||
|
func (tx *Transaction) DecodeRLP(s *rlp.Stream) error { |
||||||
|
_, size, _ := s.Kind() |
||||||
|
err := s.Decode(&tx.data) |
||||||
|
if err == nil { |
||||||
|
tx.size.Store(common.StorageSize(rlp.ListSize(size))) |
||||||
|
} |
||||||
|
|
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// MarshalJSON encodes the web3 RPC transaction format.
|
||||||
|
func (tx *Transaction) MarshalJSON() ([]byte, error) { |
||||||
|
hash := tx.Hash() |
||||||
|
data := tx.data |
||||||
|
data.Hash = &hash |
||||||
|
return data.MarshalJSON() |
||||||
|
} |
||||||
|
|
||||||
|
// UnmarshalJSON decodes the web3 RPC transaction format.
|
||||||
|
func (tx *Transaction) UnmarshalJSON(input []byte) error { |
||||||
|
var dec txdata |
||||||
|
if err := dec.UnmarshalJSON(input); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
withSignature := dec.V.Sign() != 0 || dec.R.Sign() != 0 || dec.S.Sign() != 0 |
||||||
|
if withSignature { |
||||||
|
var V byte |
||||||
|
if isProtectedV(dec.V) { |
||||||
|
chainID := deriveChainId(dec.V).Uint64() |
||||||
|
V = byte(dec.V.Uint64() - 35 - 2*chainID) |
||||||
|
} else { |
||||||
|
V = byte(dec.V.Uint64() - 27) |
||||||
|
} |
||||||
|
if !crypto.ValidateSignatureValues(V, dec.R, dec.S, false) { |
||||||
|
return ErrInvalidSig |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
*tx = Transaction{data: dec} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) } |
||||||
|
func (tx *Transaction) Gas() uint64 { return tx.data.GasLimit } |
||||||
|
func (tx *Transaction) GasPrice() *big.Int { return new(big.Int).Set(tx.data.Price) } |
||||||
|
func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amount) } |
||||||
|
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce } |
||||||
|
func (tx *Transaction) CheckNonce() bool { return true } |
||||||
|
|
||||||
|
// To returns the recipient address of the transaction.
|
||||||
|
// It returns nil if the transaction is a contract creation.
|
||||||
|
func (tx *Transaction) To() *common.Address { |
||||||
|
if tx.data.Recipient == nil { |
||||||
|
return nil |
||||||
|
} |
||||||
|
to := *tx.data.Recipient |
||||||
|
return &to |
||||||
|
} |
||||||
|
|
||||||
|
// Hash hashes the RLP encoding of tx.
|
||||||
|
// It uniquely identifies the transaction.
|
||||||
|
func (tx *Transaction) Hash() common.Hash { |
||||||
|
if hash := tx.hash.Load(); hash != nil { |
||||||
|
return hash.(common.Hash) |
||||||
|
} |
||||||
|
v := rlpHash(tx) |
||||||
|
tx.hash.Store(v) |
||||||
|
return v |
||||||
|
} |
||||||
|
|
||||||
|
// Size returns the true RLP encoded storage size of the transaction, either by
|
||||||
|
// encoding and returning it, or returning a previsouly cached value.
|
||||||
|
func (tx *Transaction) Size() common.StorageSize { |
||||||
|
if size := tx.size.Load(); size != nil { |
||||||
|
return size.(common.StorageSize) |
||||||
|
} |
||||||
|
c := writeCounter(0) |
||||||
|
rlp.Encode(&c, &tx.data) |
||||||
|
tx.size.Store(common.StorageSize(c)) |
||||||
|
return common.StorageSize(c) |
||||||
|
} |
||||||
|
|
||||||
|
// AsMessage returns the transaction as a core.Message.
|
||||||
|
//
|
||||||
|
// AsMessage requires a signer to derive the sender.
|
||||||
|
//
|
||||||
|
// XXX Rename message to something less arbitrary?
|
||||||
|
func (tx *Transaction) AsMessage(s Signer) (Message, error) { |
||||||
|
msg := Message{ |
||||||
|
nonce: tx.data.AccountNonce, |
||||||
|
gasLimit: tx.data.GasLimit, |
||||||
|
gasPrice: new(big.Int).Set(tx.data.Price), |
||||||
|
to: tx.data.Recipient, |
||||||
|
amount: tx.data.Amount, |
||||||
|
data: tx.data.Payload, |
||||||
|
checkNonce: true, |
||||||
|
} |
||||||
|
|
||||||
|
var err error |
||||||
|
msg.from, err = Sender(s, tx) |
||||||
|
return msg, err |
||||||
|
} |
||||||
|
|
||||||
|
// WithSignature returns a new transaction with the given signature.
|
||||||
|
// This signature needs to be formatted as described in the yellow paper (v+27).
|
||||||
|
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) { |
||||||
|
r, s, v, err := signer.SignatureValues(tx, sig) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
cpy := &Transaction{data: tx.data} |
||||||
|
cpy.data.R, cpy.data.S, cpy.data.V = r, s, v |
||||||
|
return cpy, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Cost returns amount + gasprice * gaslimit.
|
||||||
|
func (tx *Transaction) Cost() *big.Int { |
||||||
|
total := new(big.Int).Mul(tx.data.Price, new(big.Int).SetUint64(tx.data.GasLimit)) |
||||||
|
total.Add(total, tx.data.Amount) |
||||||
|
return total |
||||||
|
} |
||||||
|
|
||||||
|
func (tx *Transaction) RawSignatureValues() (*big.Int, *big.Int, *big.Int) { |
||||||
|
return tx.data.V, tx.data.R, tx.data.S |
||||||
|
} |
||||||
|
|
||||||
|
// Transactions is a Transaction slice type for basic sorting.
|
||||||
|
type Transactions []*Transaction |
||||||
|
|
||||||
|
// Len returns the length of s.
|
||||||
|
func (s Transactions) Len() int { return len(s) } |
||||||
|
|
||||||
|
// Swap swaps the i'th and the j'th element in s.
|
||||||
|
func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
||||||
|
|
||||||
|
// GetRlp implements Rlpable and returns the i'th element of s in rlp.
|
||||||
|
func (s Transactions) GetRlp(i int) []byte { |
||||||
|
enc, _ := rlp.EncodeToBytes(s[i]) |
||||||
|
return enc |
||||||
|
} |
||||||
|
|
||||||
|
// TxDifference returns a new set which is the difference between a and b.
|
||||||
|
func TxDifference(a, b Transactions) Transactions { |
||||||
|
keep := make(Transactions, 0, len(a)) |
||||||
|
|
||||||
|
remove := make(map[common.Hash]struct{}) |
||||||
|
for _, tx := range b { |
||||||
|
remove[tx.Hash()] = struct{}{} |
||||||
|
} |
||||||
|
|
||||||
|
for _, tx := range a { |
||||||
|
if _, ok := remove[tx.Hash()]; !ok { |
||||||
|
keep = append(keep, tx) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return keep |
||||||
|
} |
||||||
|
|
||||||
|
// TxByNonce implements the sort interface to allow sorting a list of transactions
|
||||||
|
// by their nonces. This is usually only useful for sorting transactions from a
|
||||||
|
// single account, otherwise a nonce comparison doesn't make much sense.
|
||||||
|
type TxByNonce Transactions |
||||||
|
|
||||||
|
func (s TxByNonce) Len() int { return len(s) } |
||||||
|
func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce } |
||||||
|
func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
||||||
|
|
||||||
|
// TxByPrice implements both the sort and the heap interface, making it useful
|
||||||
|
// for all at once sorting as well as individually adding and removing elements.
|
||||||
|
type TxByPrice Transactions |
||||||
|
|
||||||
|
func (s TxByPrice) Len() int { return len(s) } |
||||||
|
func (s TxByPrice) Less(i, j int) bool { return s[i].data.Price.Cmp(s[j].data.Price) > 0 } |
||||||
|
func (s TxByPrice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
||||||
|
|
||||||
|
func (s *TxByPrice) Push(x interface{}) { |
||||||
|
*s = append(*s, x.(*Transaction)) |
||||||
|
} |
||||||
|
|
||||||
|
func (s *TxByPrice) Pop() interface{} { |
||||||
|
old := *s |
||||||
|
n := len(old) |
||||||
|
x := old[n-1] |
||||||
|
*s = old[0 : n-1] |
||||||
|
return x |
||||||
|
} |
||||||
|
|
||||||
|
// TransactionsByPriceAndNonce represents a set of transactions that can return
|
||||||
|
// transactions in a profit-maximizing sorted order, while supporting removing
|
||||||
|
// entire batches of transactions for non-executable accounts.
|
||||||
|
type TransactionsByPriceAndNonce struct { |
||||||
|
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
|
||||||
|
heads TxByPrice // Next transaction for each unique account (price heap)
|
||||||
|
signer Signer // Signer for the set of transactions
|
||||||
|
} |
||||||
|
|
||||||
|
// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve
|
||||||
|
// price sorted transactions in a nonce-honouring way.
|
||||||
|
//
|
||||||
|
// Note, the input map is reowned so the caller should not interact any more with
|
||||||
|
// if after providing it to the constructor.
|
||||||
|
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce { |
||||||
|
// Initialize a price based heap with the head transactions
|
||||||
|
heads := make(TxByPrice, 0, len(txs)) |
||||||
|
for from, accTxs := range txs { |
||||||
|
heads = append(heads, accTxs[0]) |
||||||
|
// Ensure the sender address is from the signer
|
||||||
|
acc, _ := Sender(signer, accTxs[0]) |
||||||
|
txs[acc] = accTxs[1:] |
||||||
|
if from != acc { |
||||||
|
delete(txs, from) |
||||||
|
} |
||||||
|
} |
||||||
|
heap.Init(&heads) |
||||||
|
|
||||||
|
// Assemble and return the transaction set
|
||||||
|
return &TransactionsByPriceAndNonce{ |
||||||
|
txs: txs, |
||||||
|
heads: heads, |
||||||
|
signer: signer, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Peek returns the next transaction by price.
|
||||||
|
func (t *TransactionsByPriceAndNonce) Peek() *Transaction { |
||||||
|
if len(t.heads) == 0 { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return t.heads[0] |
||||||
|
} |
||||||
|
|
||||||
|
// Shift replaces the current best head with the next one from the same account.
|
||||||
|
func (t *TransactionsByPriceAndNonce) Shift() { |
||||||
|
acc, _ := Sender(t.signer, t.heads[0]) |
||||||
|
if txs, ok := t.txs[acc]; ok && len(txs) > 0 { |
||||||
|
t.heads[0], t.txs[acc] = txs[0], txs[1:] |
||||||
|
heap.Fix(&t.heads, 0) |
||||||
|
} else { |
||||||
|
heap.Pop(&t.heads) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Pop removes the best transaction, *not* replacing it with the next one from
|
||||||
|
// the same account. This should be used when a transaction cannot be executed
|
||||||
|
// and hence all subsequent ones should be discarded from the same account.
|
||||||
|
func (t *TransactionsByPriceAndNonce) Pop() { |
||||||
|
heap.Pop(&t.heads) |
||||||
|
} |
||||||
|
|
||||||
|
// Message is a fully derived transaction and implements core.Message
|
||||||
|
//
|
||||||
|
// NOTE: In a future PR this will be removed.
|
||||||
|
type Message struct { |
||||||
|
to *common.Address |
||||||
|
from common.Address |
||||||
|
nonce uint64 |
||||||
|
amount *big.Int |
||||||
|
gasLimit uint64 |
||||||
|
gasPrice *big.Int |
||||||
|
data []byte |
||||||
|
checkNonce bool |
||||||
|
} |
||||||
|
|
||||||
|
func NewMessage(from common.Address, to *common.Address, nonce uint64, amount *big.Int, gasLimit uint64, gasPrice *big.Int, data []byte, checkNonce bool) Message { |
||||||
|
return Message{ |
||||||
|
from: from, |
||||||
|
to: to, |
||||||
|
nonce: nonce, |
||||||
|
amount: amount, |
||||||
|
gasLimit: gasLimit, |
||||||
|
gasPrice: gasPrice, |
||||||
|
data: data, |
||||||
|
checkNonce: checkNonce, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (m Message) From() common.Address { return m.from } |
||||||
|
func (m Message) To() *common.Address { return m.to } |
||||||
|
func (m Message) GasPrice() *big.Int { return m.gasPrice } |
||||||
|
func (m Message) Value() *big.Int { return m.amount } |
||||||
|
func (m Message) Gas() uint64 { return m.gasLimit } |
||||||
|
func (m Message) Nonce() uint64 { return m.nonce } |
||||||
|
func (m Message) Data() []byte { return m.data } |
||||||
|
func (m Message) CheckNonce() bool { return m.checkNonce } |
@ -0,0 +1,260 @@ |
|||||||
|
// 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 types |
||||||
|
|
||||||
|
import ( |
||||||
|
"crypto/ecdsa" |
||||||
|
"errors" |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
ErrInvalidChainId = errors.New("invalid chain id for signer") |
||||||
|
) |
||||||
|
|
||||||
|
// sigCache is used to cache the derived sender and contains
|
||||||
|
// the signer used to derive it.
|
||||||
|
type sigCache struct { |
||||||
|
signer Signer |
||||||
|
from common.Address |
||||||
|
} |
||||||
|
|
||||||
|
// MakeSigner returns a Signer based on the given chain config and block number.
|
||||||
|
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer { |
||||||
|
var signer Signer |
||||||
|
switch { |
||||||
|
case config.IsEIP155(blockNumber): |
||||||
|
signer = NewEIP155Signer(config.ChainID) |
||||||
|
case config.IsHomestead(blockNumber): |
||||||
|
signer = HomesteadSigner{} |
||||||
|
default: |
||||||
|
signer = FrontierSigner{} |
||||||
|
} |
||||||
|
return signer |
||||||
|
} |
||||||
|
|
||||||
|
// SignTx signs the transaction using the given signer and private key
|
||||||
|
func SignTx(tx *Transaction, s Signer, prv *ecdsa.PrivateKey) (*Transaction, error) { |
||||||
|
h := s.Hash(tx) |
||||||
|
sig, err := crypto.Sign(h[:], prv) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
return tx.WithSignature(s, sig) |
||||||
|
} |
||||||
|
|
||||||
|
// Sender returns the address derived from the signature (V, R, S) using secp256k1
|
||||||
|
// elliptic curve and an error if it failed deriving or upon an incorrect
|
||||||
|
// signature.
|
||||||
|
//
|
||||||
|
// Sender may cache the address, allowing it to be used regardless of
|
||||||
|
// signing method. The cache is invalidated if the cached signer does
|
||||||
|
// not match the signer used in the current call.
|
||||||
|
func Sender(signer Signer, tx *Transaction) (common.Address, error) { |
||||||
|
if sc := tx.from.Load(); sc != nil { |
||||||
|
sigCache := sc.(sigCache) |
||||||
|
// If the signer used to derive from in a previous
|
||||||
|
// call is not the same as used current, invalidate
|
||||||
|
// the cache.
|
||||||
|
if sigCache.signer.Equal(signer) { |
||||||
|
return sigCache.from, nil |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
addr, err := signer.Sender(tx) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, err |
||||||
|
} |
||||||
|
tx.from.Store(sigCache{signer: signer, from: addr}) |
||||||
|
return addr, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Signer encapsulates transaction signature handling. Note that this interface is not a
|
||||||
|
// stable API and may change at any time to accommodate new protocol rules.
|
||||||
|
type Signer interface { |
||||||
|
// Sender returns the sender address of the transaction.
|
||||||
|
Sender(tx *Transaction) (common.Address, error) |
||||||
|
// SignatureValues returns the raw R, S, V values corresponding to the
|
||||||
|
// given signature.
|
||||||
|
SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) |
||||||
|
// Hash returns the hash to be signed.
|
||||||
|
Hash(tx *Transaction) common.Hash |
||||||
|
// Equal returns true if the given signer is the same as the receiver.
|
||||||
|
Equal(Signer) bool |
||||||
|
} |
||||||
|
|
||||||
|
// EIP155Transaction implements Signer using the EIP155 rules.
|
||||||
|
type EIP155Signer struct { |
||||||
|
chainId, chainIdMul *big.Int |
||||||
|
} |
||||||
|
|
||||||
|
func NewEIP155Signer(chainId *big.Int) EIP155Signer { |
||||||
|
if chainId == nil { |
||||||
|
chainId = new(big.Int) |
||||||
|
} |
||||||
|
return EIP155Signer{ |
||||||
|
chainId: chainId, |
||||||
|
chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)), |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (s EIP155Signer) Equal(s2 Signer) bool { |
||||||
|
eip155, ok := s2.(EIP155Signer) |
||||||
|
return ok && eip155.chainId.Cmp(s.chainId) == 0 |
||||||
|
} |
||||||
|
|
||||||
|
var big8 = big.NewInt(8) |
||||||
|
|
||||||
|
func (s EIP155Signer) Sender(tx *Transaction) (common.Address, error) { |
||||||
|
if !tx.Protected() { |
||||||
|
return HomesteadSigner{}.Sender(tx) |
||||||
|
} |
||||||
|
if tx.ChainId().Cmp(s.chainId) != 0 { |
||||||
|
return common.Address{}, ErrInvalidChainId |
||||||
|
} |
||||||
|
V := new(big.Int).Sub(tx.data.V, s.chainIdMul) |
||||||
|
V.Sub(V, big8) |
||||||
|
return recoverPlain(s.Hash(tx), tx.data.R, tx.data.S, V, true) |
||||||
|
} |
||||||
|
|
||||||
|
// SignatureValues returns signature values. This signature
|
||||||
|
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||||
|
func (s EIP155Signer) SignatureValues(tx *Transaction, sig []byte) (R, S, V *big.Int, err error) { |
||||||
|
R, S, V, err = HomesteadSigner{}.SignatureValues(tx, sig) |
||||||
|
if err != nil { |
||||||
|
return nil, nil, nil, err |
||||||
|
} |
||||||
|
if s.chainId.Sign() != 0 { |
||||||
|
V = big.NewInt(int64(sig[64] + 35)) |
||||||
|
V.Add(V, s.chainIdMul) |
||||||
|
} |
||||||
|
return R, S, V, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Hash returns the hash to be signed by the sender.
|
||||||
|
// It does not uniquely identify the transaction.
|
||||||
|
func (s EIP155Signer) Hash(tx *Transaction) common.Hash { |
||||||
|
return rlpHash([]interface{}{ |
||||||
|
tx.data.AccountNonce, |
||||||
|
tx.data.Price, |
||||||
|
tx.data.GasLimit, |
||||||
|
tx.data.Recipient, |
||||||
|
tx.data.Amount, |
||||||
|
tx.data.Payload, |
||||||
|
s.chainId, uint(0), uint(0), |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
// HomesteadTransaction implements TransactionInterface using the
|
||||||
|
// homestead rules.
|
||||||
|
type HomesteadSigner struct{ FrontierSigner } |
||||||
|
|
||||||
|
func (s HomesteadSigner) Equal(s2 Signer) bool { |
||||||
|
_, ok := s2.(HomesteadSigner) |
||||||
|
return ok |
||||||
|
} |
||||||
|
|
||||||
|
// SignatureValues returns signature values. This signature
|
||||||
|
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||||
|
func (hs HomesteadSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) { |
||||||
|
return hs.FrontierSigner.SignatureValues(tx, sig) |
||||||
|
} |
||||||
|
|
||||||
|
func (hs HomesteadSigner) Sender(tx *Transaction) (common.Address, error) { |
||||||
|
return recoverPlain(hs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, true) |
||||||
|
} |
||||||
|
|
||||||
|
type FrontierSigner struct{} |
||||||
|
|
||||||
|
func (s FrontierSigner) Equal(s2 Signer) bool { |
||||||
|
_, ok := s2.(FrontierSigner) |
||||||
|
return ok |
||||||
|
} |
||||||
|
|
||||||
|
// SignatureValues returns signature values. This signature
|
||||||
|
// needs to be in the [R || S || V] format where V is 0 or 1.
|
||||||
|
func (fs FrontierSigner) SignatureValues(tx *Transaction, sig []byte) (r, s, v *big.Int, err error) { |
||||||
|
if len(sig) != 65 { |
||||||
|
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig))) |
||||||
|
} |
||||||
|
r = new(big.Int).SetBytes(sig[:32]) |
||||||
|
s = new(big.Int).SetBytes(sig[32:64]) |
||||||
|
v = new(big.Int).SetBytes([]byte{sig[64] + 27}) |
||||||
|
return r, s, v, nil |
||||||
|
} |
||||||
|
|
||||||
|
// Hash returns the hash to be signed by the sender.
|
||||||
|
// It does not uniquely identify the transaction.
|
||||||
|
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash { |
||||||
|
return rlpHash([]interface{}{ |
||||||
|
tx.data.AccountNonce, |
||||||
|
tx.data.Price, |
||||||
|
tx.data.GasLimit, |
||||||
|
tx.data.Recipient, |
||||||
|
tx.data.Amount, |
||||||
|
tx.data.Payload, |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
func (fs FrontierSigner) Sender(tx *Transaction) (common.Address, error) { |
||||||
|
return recoverPlain(fs.Hash(tx), tx.data.R, tx.data.S, tx.data.V, false) |
||||||
|
} |
||||||
|
|
||||||
|
func recoverPlain(sighash common.Hash, R, S, Vb *big.Int, homestead bool) (common.Address, error) { |
||||||
|
if Vb.BitLen() > 8 { |
||||||
|
return common.Address{}, ErrInvalidSig |
||||||
|
} |
||||||
|
V := byte(Vb.Uint64() - 27) |
||||||
|
if !crypto.ValidateSignatureValues(V, R, S, homestead) { |
||||||
|
return common.Address{}, ErrInvalidSig |
||||||
|
} |
||||||
|
// encode the signature in uncompressed format
|
||||||
|
r, s := R.Bytes(), S.Bytes() |
||||||
|
sig := make([]byte, 65) |
||||||
|
copy(sig[32-len(r):32], r) |
||||||
|
copy(sig[64-len(s):64], s) |
||||||
|
sig[64] = V |
||||||
|
// recover the public key from the signature
|
||||||
|
pub, err := crypto.Ecrecover(sighash[:], sig) |
||||||
|
if err != nil { |
||||||
|
return common.Address{}, err |
||||||
|
} |
||||||
|
if len(pub) == 0 || pub[0] != 4 { |
||||||
|
return common.Address{}, errors.New("invalid public key") |
||||||
|
} |
||||||
|
var addr common.Address |
||||||
|
copy(addr[:], crypto.Keccak256(pub[1:])[12:]) |
||||||
|
return addr, nil |
||||||
|
} |
||||||
|
|
||||||
|
// deriveChainId derives the chain id from the given v parameter
|
||||||
|
func deriveChainId(v *big.Int) *big.Int { |
||||||
|
if v.BitLen() <= 64 { |
||||||
|
v := v.Uint64() |
||||||
|
if v == 27 || v == 28 { |
||||||
|
return new(big.Int) |
||||||
|
} |
||||||
|
return new(big.Int).SetUint64((v - 35) / 2) |
||||||
|
} |
||||||
|
v = new(big.Int).Sub(v, big.NewInt(35)) |
||||||
|
return v.Div(v, big.NewInt(2)) |
||||||
|
} |
@ -0,0 +1,138 @@ |
|||||||
|
// 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 types |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
func TestEIP155Signing(t *testing.T) { |
||||||
|
key, _ := crypto.GenerateKey() |
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey) |
||||||
|
|
||||||
|
signer := NewEIP155Signer(big.NewInt(18)) |
||||||
|
tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
from, err := Sender(signer, tx) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
if from != addr { |
||||||
|
t.Errorf("exected from and address to be equal. Got %x want %x", from, addr) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestEIP155ChainId(t *testing.T) { |
||||||
|
key, _ := crypto.GenerateKey() |
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey) |
||||||
|
|
||||||
|
signer := NewEIP155Signer(big.NewInt(18)) |
||||||
|
tx, err := SignTx(NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil), signer, key) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
if !tx.Protected() { |
||||||
|
t.Fatal("expected tx to be protected") |
||||||
|
} |
||||||
|
|
||||||
|
if tx.ChainId().Cmp(signer.chainId) != 0 { |
||||||
|
t.Error("expected chainId to be", signer.chainId, "got", tx.ChainId()) |
||||||
|
} |
||||||
|
|
||||||
|
tx = NewTransaction(0, addr, new(big.Int), 0, new(big.Int), nil) |
||||||
|
tx, err = SignTx(tx, HomesteadSigner{}, key) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
if tx.Protected() { |
||||||
|
t.Error("didn't expect tx to be protected") |
||||||
|
} |
||||||
|
|
||||||
|
if tx.ChainId().Sign() != 0 { |
||||||
|
t.Error("expected chain id to be 0 got", tx.ChainId()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestEIP155SigningVitalik(t *testing.T) { |
||||||
|
// Test vectors come from http://vitalik.ca/files/eip155_testvec.txt
|
||||||
|
for i, test := range []struct { |
||||||
|
txRlp, addr string |
||||||
|
}{ |
||||||
|
{"f864808504a817c800825208943535353535353535353535353535353535353535808025a0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116da0044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", "0xf0f6f18bca1b28cd68e4357452947e021241e9ce"}, |
||||||
|
{"f864018504a817c80182a410943535353535353535353535353535353535353535018025a0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bcaa0489efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6", "0x23ef145a395ea3fa3deb533b8a9e1b4c6c25d112"}, |
||||||
|
{"f864028504a817c80282f618943535353535353535353535353535353535353535088025a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5a02d7c5bef027816a800da1736444fb58a807ef4c9603b7848673f7e3a68eb14a5", "0x2e485e0c23b4c3c542628a5f672eeab0ad4888be"}, |
||||||
|
{"f865038504a817c803830148209435353535353535353535353535353535353535351b8025a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4e0a02a80e1ef1d7842f27f2e6be0972bb708b9a135c38860dbe73c27c3486c34f4de", "0x82a88539669a3fd524d669e858935de5e5410cf0"}, |
||||||
|
{"f865048504a817c80483019a28943535353535353535353535353535353535353535408025a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c063a013600b294191fc92924bb3ce4b969c1e7e2bab8f4c93c3fc6d0a51733df3c060", "0xf9358f2538fd5ccfeb848b64a96b743fcc930554"}, |
||||||
|
{"f865058504a817c8058301ec309435353535353535353535353535353535353535357d8025a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1a04eebf77a833b30520287ddd9478ff51abbdffa30aa90a8d655dba0e8a79ce0c1", "0xa8f7aba377317440bc5b26198a363ad22af1f3a4"}, |
||||||
|
{"f866068504a817c80683023e3894353535353535353535353535353535353535353581d88025a06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2fa06455bf8ea6e7463a1046a0b52804526e119b4bf5136279614e0b1e8e296a4e2d", "0xf1f571dc362a0e5b2696b8e775f8491d3e50de35"}, |
||||||
|
{"f867078504a817c807830290409435353535353535353535353535353535353535358201578025a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021a052f1a9b320cab38e5da8a8f97989383aab0a49165fc91c737310e4f7e9821021", "0xd37922162ab7cea97c97a87551ed02c9a38b7332"}, |
||||||
|
{"f867088504a817c8088302e2489435353535353535353535353535353535353535358202008025a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c12a064b1702d9298fee62dfeccc57d322a463ad55ca201256d01f62b45b2e1c21c10", "0x9bddad43f934d313c2b79ca28a432dd2b7281029"}, |
||||||
|
{"f867098504a817c809830334509435353535353535353535353535353535353535358202d98025a052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afba052f8f61201b2b11a78d6e866abc9c3db2ae8631fa656bfe5cb53668255367afb", "0x3c24d7329e92f84f08556ceb6df1cdb0104ca49f"}, |
||||||
|
} { |
||||||
|
signer := NewEIP155Signer(big.NewInt(1)) |
||||||
|
|
||||||
|
var tx *Transaction |
||||||
|
err := rlp.DecodeBytes(common.Hex2Bytes(test.txRlp), &tx) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("%d: %v", i, err) |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
from, err := Sender(signer, tx) |
||||||
|
if err != nil { |
||||||
|
t.Errorf("%d: %v", i, err) |
||||||
|
continue |
||||||
|
} |
||||||
|
|
||||||
|
addr := common.HexToAddress(test.addr) |
||||||
|
if from != addr { |
||||||
|
t.Errorf("%d: expected %x got %x", i, addr, from) |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestChainId(t *testing.T) { |
||||||
|
key, _ := defaultTestKey() |
||||||
|
|
||||||
|
tx := NewTransaction(0, common.Address{}, new(big.Int), 0, new(big.Int), nil) |
||||||
|
|
||||||
|
var err error |
||||||
|
tx, err = SignTx(tx, NewEIP155Signer(big.NewInt(1)), key) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
_, err = Sender(NewEIP155Signer(big.NewInt(2)), tx) |
||||||
|
if err != ErrInvalidChainId { |
||||||
|
t.Error("expected error:", ErrInvalidChainId) |
||||||
|
} |
||||||
|
|
||||||
|
_, err = Sender(NewEIP155Signer(big.NewInt(1)), tx) |
||||||
|
if err != nil { |
||||||
|
t.Error("expected no error") |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,226 @@ |
|||||||
|
// Copyright 2014 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"crypto/ecdsa" |
||||||
|
"encoding/json" |
||||||
|
"math/big" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
) |
||||||
|
|
||||||
|
// The values in those tests are from the Transaction Tests
|
||||||
|
// at github.com/ethereum/tests.
|
||||||
|
var ( |
||||||
|
emptyTx = NewTransaction( |
||||||
|
0, |
||||||
|
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), |
||||||
|
big.NewInt(0), 0, big.NewInt(0), |
||||||
|
nil, |
||||||
|
) |
||||||
|
|
||||||
|
rightvrsTx, _ = NewTransaction( |
||||||
|
3, |
||||||
|
common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"), |
||||||
|
big.NewInt(10), |
||||||
|
2000, |
||||||
|
big.NewInt(1), |
||||||
|
common.FromHex("5544"), |
||||||
|
).WithSignature( |
||||||
|
HomesteadSigner{}, |
||||||
|
common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"), |
||||||
|
) |
||||||
|
) |
||||||
|
|
||||||
|
func TestTransactionSigHash(t *testing.T) { |
||||||
|
var homestead HomesteadSigner |
||||||
|
if homestead.Hash(emptyTx) != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") { |
||||||
|
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash()) |
||||||
|
} |
||||||
|
if homestead.Hash(rightvrsTx) != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") { |
||||||
|
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestTransactionEncode(t *testing.T) { |
||||||
|
txb, err := rlp.EncodeToBytes(rightvrsTx) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("encode error: %v", err) |
||||||
|
} |
||||||
|
should := common.FromHex("f86103018207d094b94f5374fce5edbc8e2a8697c15331677e6ebf0b0a8255441ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa08887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3") |
||||||
|
if !bytes.Equal(txb, should) { |
||||||
|
t.Errorf("encoded RLP mismatch, got %x", txb) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func decodeTx(data []byte) (*Transaction, error) { |
||||||
|
var tx Transaction |
||||||
|
t, err := &tx, rlp.Decode(bytes.NewReader(data), &tx) |
||||||
|
|
||||||
|
return t, err |
||||||
|
} |
||||||
|
|
||||||
|
func defaultTestKey() (*ecdsa.PrivateKey, common.Address) { |
||||||
|
key, _ := crypto.HexToECDSA("45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8") |
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey) |
||||||
|
return key, addr |
||||||
|
} |
||||||
|
|
||||||
|
func TestRecipientEmpty(t *testing.T) { |
||||||
|
_, addr := defaultTestKey() |
||||||
|
tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d")) |
||||||
|
if err != nil { |
||||||
|
t.Error(err) |
||||||
|
t.FailNow() |
||||||
|
} |
||||||
|
|
||||||
|
from, err := Sender(HomesteadSigner{}, tx) |
||||||
|
if err != nil { |
||||||
|
t.Error(err) |
||||||
|
t.FailNow() |
||||||
|
} |
||||||
|
if addr != from { |
||||||
|
t.Error("derived address doesn't match") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestRecipientNormal(t *testing.T) { |
||||||
|
_, addr := defaultTestKey() |
||||||
|
|
||||||
|
tx, err := decodeTx(common.Hex2Bytes("f85d80808094000000000000000000000000000000000000000080011ca0527c0d8f5c63f7b9f41324a7c8a563ee1190bcbf0dac8ab446291bdbf32f5c79a0552c4ef0a09a04395074dab9ed34d3fbfb843c2f2546cc30fe89ec143ca94ca6")) |
||||||
|
if err != nil { |
||||||
|
t.Error(err) |
||||||
|
t.FailNow() |
||||||
|
} |
||||||
|
|
||||||
|
from, err := Sender(HomesteadSigner{}, tx) |
||||||
|
if err != nil { |
||||||
|
t.Error(err) |
||||||
|
t.FailNow() |
||||||
|
} |
||||||
|
|
||||||
|
if addr != from { |
||||||
|
t.Error("derived address doesn't match") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Tests that transactions can be correctly sorted according to their price in
|
||||||
|
// decreasing order, but at the same time with increasing nonces when issued by
|
||||||
|
// the same account.
|
||||||
|
func TestTransactionPriceNonceSort(t *testing.T) { |
||||||
|
// Generate a batch of accounts to start with
|
||||||
|
keys := make([]*ecdsa.PrivateKey, 25) |
||||||
|
for i := 0; i < len(keys); i++ { |
||||||
|
keys[i], _ = crypto.GenerateKey() |
||||||
|
} |
||||||
|
|
||||||
|
signer := HomesteadSigner{} |
||||||
|
// Generate a batch of transactions with overlapping values, but shifted nonces
|
||||||
|
groups := map[common.Address]Transactions{} |
||||||
|
for start, key := range keys { |
||||||
|
addr := crypto.PubkeyToAddress(key.PublicKey) |
||||||
|
for i := 0; i < 25; i++ { |
||||||
|
tx, _ := SignTx(NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), 100, big.NewInt(int64(start+i)), nil), signer, key) |
||||||
|
groups[addr] = append(groups[addr], tx) |
||||||
|
} |
||||||
|
} |
||||||
|
// Sort the transactions and cross check the nonce ordering
|
||||||
|
txset := NewTransactionsByPriceAndNonce(signer, groups) |
||||||
|
|
||||||
|
txs := Transactions{} |
||||||
|
for tx := txset.Peek(); tx != nil; tx = txset.Peek() { |
||||||
|
txs = append(txs, tx) |
||||||
|
txset.Shift() |
||||||
|
} |
||||||
|
if len(txs) != 25*25 { |
||||||
|
t.Errorf("expected %d transactions, found %d", 25*25, len(txs)) |
||||||
|
} |
||||||
|
for i, txi := range txs { |
||||||
|
fromi, _ := Sender(signer, txi) |
||||||
|
|
||||||
|
// Make sure the nonce order is valid
|
||||||
|
for j, txj := range txs[i+1:] { |
||||||
|
fromj, _ := Sender(signer, txj) |
||||||
|
|
||||||
|
if fromi == fromj && txi.Nonce() > txj.Nonce() { |
||||||
|
t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// If the next tx has different from account, the price must be lower than the current one
|
||||||
|
if i+1 < len(txs) { |
||||||
|
next := txs[i+1] |
||||||
|
fromNext, _ := Sender(signer, next) |
||||||
|
if fromi != fromNext && txi.GasPrice().Cmp(next.GasPrice()) < 0 { |
||||||
|
t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// TestTransactionJSON tests serializing/de-serializing to/from JSON.
|
||||||
|
func TestTransactionJSON(t *testing.T) { |
||||||
|
key, err := crypto.GenerateKey() |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("could not generate key: %v", err) |
||||||
|
} |
||||||
|
signer := NewEIP155Signer(common.Big1) |
||||||
|
|
||||||
|
transactions := make([]*Transaction, 0, 50) |
||||||
|
for i := uint64(0); i < 25; i++ { |
||||||
|
var tx *Transaction |
||||||
|
switch i % 2 { |
||||||
|
case 0: |
||||||
|
tx = NewTransaction(i, common.Address{1}, common.Big0, 1, common.Big2, []byte("abcdef")) |
||||||
|
case 1: |
||||||
|
tx = NewContractCreation(i, common.Big0, 1, common.Big2, []byte("abcdef")) |
||||||
|
} |
||||||
|
transactions = append(transactions, tx) |
||||||
|
|
||||||
|
signedTx, err := SignTx(tx, signer, key) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("could not sign transaction: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
transactions = append(transactions, signedTx) |
||||||
|
} |
||||||
|
|
||||||
|
for _, tx := range transactions { |
||||||
|
data, err := json.Marshal(tx) |
||||||
|
if err != nil { |
||||||
|
t.Fatalf("json.Marshal failed: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
var parsedTx *Transaction |
||||||
|
if err := json.Unmarshal(data, &parsedTx); err != nil { |
||||||
|
t.Fatalf("json.Unmarshal failed: %v", err) |
||||||
|
} |
||||||
|
|
||||||
|
// compare nonce, price, gaslimit, recipient, amount, payload, V, R, S
|
||||||
|
if tx.Hash() != parsedTx.Hash() { |
||||||
|
t.Errorf("parsed tx differs from original tx, want %v, got %v", tx, parsedTx) |
||||||
|
} |
||||||
|
if tx.ChainId().Cmp(parsedTx.ChainId()) != 0 { |
||||||
|
t.Errorf("invalid chain id, want %d, got %d", tx.ChainId(), parsedTx.ChainId()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue