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