commit
52c7db6e5b
@ -0,0 +1,91 @@ |
||||
package consensus |
||||
|
||||
import ( |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/core/state" |
||||
"github.com/ethereum/go-ethereum/params" |
||||
"github.com/ethereum/go-ethereum/rpc" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// ChainReader defines a small collection of methods needed to access the local
|
||||
// blockchain during header and/or uncle verification.
|
||||
type ChainReader interface { |
||||
// Config retrieves the blockchain's chain configuration.
|
||||
Config() *params.ChainConfig |
||||
|
||||
// CurrentHeader retrieves the current header from the local chain.
|
||||
CurrentHeader() *types.Header |
||||
|
||||
// GetHeader retrieves a block header from the database by hash and number.
|
||||
GetHeader(hash common.Hash, number uint64) *types.Header |
||||
|
||||
// GetHeaderByNumber retrieves a block header from the database by number.
|
||||
GetHeaderByNumber(number uint64) *types.Header |
||||
|
||||
// GetHeaderByHash retrieves a block header from the database by its hash.
|
||||
GetHeaderByHash(hash common.Hash) *types.Header |
||||
|
||||
// GetBlock retrieves a block from the database by hash and number.
|
||||
GetBlock(hash common.Hash, number uint64) *types.Block |
||||
} |
||||
|
||||
// Engine is an algorithm agnostic consensus engine.
|
||||
type Engine interface { |
||||
// Author retrieves the Ethereum address of the account that minted the given
|
||||
// block, which may be different from the header's coinbase if a consensus
|
||||
// engine is based on signatures.
|
||||
Author(header *types.Header) (common.Address, error) |
||||
|
||||
// VerifyHeader checks whether a header conforms to the consensus rules of a
|
||||
// given engine. Verifying the seal may be done optionally here, or explicitly
|
||||
// via the VerifySeal method.
|
||||
VerifyHeader(chain ChainReader, header *types.Header, seal bool) error |
||||
|
||||
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
|
||||
// concurrently. The method returns a quit channel to abort the operations and
|
||||
// a results channel to retrieve the async verifications (the order is that of
|
||||
// the input slice).
|
||||
VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) |
||||
|
||||
// VerifyUncles verifies that the given block's uncles conform to the consensus
|
||||
// rules of a given engine.
|
||||
VerifyUncles(chain ChainReader, block *types.Block) error |
||||
|
||||
// VerifySeal checks whether the crypto seal on a header is valid according to
|
||||
// the consensus rules of the given engine.
|
||||
VerifySeal(chain ChainReader, header *types.Header) error |
||||
|
||||
// Prepare initializes the consensus fields of a block header according to the
|
||||
// rules of a particular engine. The changes are executed inline.
|
||||
Prepare(chain ChainReader, header *types.Header) error |
||||
|
||||
// Finalize runs any post-transaction state modifications (e.g. block rewards)
|
||||
// and assembles the final block.
|
||||
// Note: The block header and state database might be updated to reflect any
|
||||
// consensus rules that happen at finalization (e.g. block rewards).
|
||||
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, |
||||
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) |
||||
|
||||
// Seal generates a new sealing request for the given input block and pushes
|
||||
// the result into the given channel.
|
||||
//
|
||||
// Note, the method returns immediately and will send the result async. More
|
||||
// than one result may also be returned depending on the consensus algorithm.
|
||||
Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error |
||||
|
||||
// SealHash returns the hash of a block prior to it being sealed.
|
||||
SealHash(header *types.Header) common.Hash |
||||
|
||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
|
||||
// that a new block should have.
|
||||
CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int |
||||
|
||||
// APIs returns the RPC APIs this consensus engine provides.
|
||||
APIs(chain ChainReader) []rpc.API |
||||
|
||||
// Close terminates any background threads maintained by the consensus engine.
|
||||
Close() error |
||||
} |
@ -0,0 +1,37 @@ |
||||
// 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 consensus |
||||
|
||||
import "errors" |
||||
|
||||
var ( |
||||
// ErrUnknownAncestor is returned when validating a block requires an ancestor
|
||||
// that is unknown.
|
||||
ErrUnknownAncestor = errors.New("unknown ancestor") |
||||
|
||||
// ErrPrunedAncestor is returned when validating a block requires an ancestor
|
||||
// that is known, but the state of which is not available.
|
||||
ErrPrunedAncestor = errors.New("pruned ancestor") |
||||
|
||||
// ErrFutureBlock is returned when a block's timestamp is in the future according
|
||||
// to the current node.
|
||||
ErrFutureBlock = errors.New("block in the future") |
||||
|
||||
// ErrInvalidNumber is returned if a block's number doesn't equal it's parent's
|
||||
// plus one.
|
||||
ErrInvalidNumber = errors.New("invalid block number") |
||||
) |
@ -0,0 +1,139 @@ |
||||
// 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" |
||||
|
||||
"github.com/ethereum/go-ethereum/core/state" |
||||
"github.com/ethereum/go-ethereum/params" |
||||
"github.com/simple-rules/harmony-benchmark/consensus" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// BlockValidator is responsible for validating block headers, uncles and
|
||||
// processed state.
|
||||
//
|
||||
// BlockValidator implements Validator.
|
||||
type BlockValidator struct { |
||||
config *params.ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
engine consensus.Engine // Consensus engine used for validating
|
||||
} |
||||
|
||||
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engine consensus.Engine) *BlockValidator { |
||||
validator := &BlockValidator{ |
||||
config: config, |
||||
engine: engine, |
||||
bc: blockchain, |
||||
} |
||||
return validator |
||||
} |
||||
|
||||
// ValidateBody validates the given block's uncles and verifies the block
|
||||
// header's transaction and uncle roots. The headers are assumed to be already
|
||||
// validated at this point.
|
||||
func (v *BlockValidator) ValidateBody(block *types.Block) error { |
||||
// Check whether the block's known, and if not, that it's linkable
|
||||
if v.bc.HasBlockAndState(block.Hash(), block.NumberU64()) { |
||||
return ErrKnownBlock |
||||
} |
||||
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) { |
||||
if !v.bc.HasBlock(block.ParentHash(), block.NumberU64()-1) { |
||||
return consensus.ErrUnknownAncestor |
||||
} |
||||
return consensus.ErrPrunedAncestor |
||||
} |
||||
// Header validity is known at this point, check the uncles and transactions
|
||||
header := block.Header() |
||||
//if err := v.engine.VerifyUncles(v.bc, block); err != nil {
|
||||
// return err
|
||||
//}
|
||||
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash { |
||||
return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash) |
||||
} |
||||
if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash { |
||||
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// ValidateState validates the various changes that happen after a state
|
||||
// transition, such as amount of used gas, the receipt roots and the state root
|
||||
// itself. ValidateState returns a database batch if the validation was a success
|
||||
// otherwise nil and an error is returned.
|
||||
func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas uint64) error { |
||||
header := block.Header() |
||||
if block.GasUsed() != usedGas { |
||||
return fmt.Errorf("invalid gas used (remote: %d local: %d)", block.GasUsed(), usedGas) |
||||
} |
||||
// Validate the received block's bloom with the one derived from the generated receipts.
|
||||
// For valid blocks this should always validate to true.
|
||||
rbloom := types.CreateBloom(receipts) |
||||
if rbloom != header.Bloom { |
||||
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom) |
||||
} |
||||
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
|
||||
receiptSha := types.DeriveSha(receipts) |
||||
if receiptSha != header.ReceiptHash { |
||||
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha) |
||||
} |
||||
// Validate the state root against the received state root and throw
|
||||
// an error if they don't match.
|
||||
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root { |
||||
return fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// CalcGasLimit computes the gas limit of the next block after parent. It aims
|
||||
// to keep the baseline gas above the provided floor, and increase it towards the
|
||||
// ceil if the blocks are full. If the ceil is exceeded, it will always decrease
|
||||
// the gas allowance.
|
||||
func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 { |
||||
// contrib = (parentGasUsed * 3 / 2) / 1024
|
||||
contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor |
||||
|
||||
// decay = parentGasLimit / 1024 -1
|
||||
decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1 |
||||
|
||||
/* |
||||
strategy: gasLimit of block-to-mine is set based on parent's |
||||
gasUsed value. if parentGasUsed > parentGasLimit * (2/3) then we |
||||
increase it, otherwise lower it (or leave it unchanged if it's right |
||||
at that usage) the amount increased/decreased depends on how far away |
||||
from parentGasLimit * (2/3) parentGasUsed is. |
||||
*/ |
||||
limit := parent.GasLimit() - decay + contrib |
||||
if limit < params.MinGasLimit { |
||||
limit = params.MinGasLimit |
||||
} |
||||
// If we're outside our allowed gas range, we try to hone towards them
|
||||
if limit < gasFloor { |
||||
limit = parent.GasLimit() + decay |
||||
if limit > gasFloor { |
||||
limit = gasFloor |
||||
} |
||||
} else if limit > gasCeil { |
||||
limit = parent.GasLimit() - decay |
||||
if limit < gasCeil { |
||||
limit = gasCeil |
||||
} |
||||
} |
||||
return limit |
||||
} |
File diff suppressed because it is too large
Load Diff
@ -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/simple-rules/harmony-benchmark/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,97 @@ |
||||
// 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/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/core/vm" |
||||
"github.com/simple-rules/harmony-benchmark/consensus" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// ChainContext supports retrieving headers and consensus parameters from the
|
||||
// current blockchain to be used during transaction processing.
|
||||
type ChainContext interface { |
||||
// Engine retrieves the chain's consensus engine.
|
||||
Engine() consensus.Engine |
||||
|
||||
// GetHeader returns the hash corresponding to their hash.
|
||||
GetHeader(common.Hash, uint64) *types.Header |
||||
} |
||||
|
||||
// NewEVMContext creates a new context for use in the EVM.
|
||||
func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author *common.Address) vm.Context { |
||||
// If we don't have an explicit author (i.e. not mining), extract from the header
|
||||
var beneficiary common.Address |
||||
if author == nil { |
||||
beneficiary, _ = chain.Engine().Author(header) // Ignore error, we're past header validation
|
||||
} else { |
||||
beneficiary = *author |
||||
} |
||||
return vm.Context{ |
||||
CanTransfer: CanTransfer, |
||||
Transfer: Transfer, |
||||
GetHash: GetHashFn(header, chain), |
||||
Origin: msg.From(), |
||||
Coinbase: beneficiary, |
||||
BlockNumber: new(big.Int).Set(header.Number), |
||||
Time: new(big.Int).Set(header.Time), |
||||
Difficulty: new(big.Int).Set(header.Difficulty), |
||||
GasLimit: header.GasLimit, |
||||
GasPrice: new(big.Int).Set(msg.GasPrice()), |
||||
} |
||||
} |
||||
|
||||
// GetHashFn returns a GetHashFunc which retrieves header hashes by number
|
||||
func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash { |
||||
var cache map[uint64]common.Hash |
||||
|
||||
return func(n uint64) common.Hash { |
||||
// If there's no hash cache yet, make one
|
||||
if cache == nil { |
||||
cache = map[uint64]common.Hash{ |
||||
ref.Number.Uint64() - 1: ref.ParentHash, |
||||
} |
||||
} |
||||
// Try to fulfill the request from the cache
|
||||
if hash, ok := cache[n]; ok { |
||||
return hash |
||||
} |
||||
// Not cached, iterate the blocks and cache the hashes
|
||||
for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) { |
||||
cache[header.Number.Uint64()-1] = header.ParentHash |
||||
if n == header.Number.Uint64()-1 { |
||||
return header.ParentHash |
||||
} |
||||
} |
||||
return common.Hash{} |
||||
} |
||||
} |
||||
|
||||
// CanTransfer checks whether there are enough funds in the address' account to make a transfer.
|
||||
// This does not take the necessary gas in to account to make the transfer valid.
|
||||
func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool { |
||||
return db.GetBalance(addr).Cmp(amount) >= 0 |
||||
} |
||||
|
||||
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
|
||||
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) { |
||||
db.SubBalance(sender, amount) |
||||
db.AddBalance(recipient, amount) |
||||
} |
@ -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,351 @@ |
||||
// 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 ( |
||||
"bytes" |
||||
"encoding/hex" |
||||
"encoding/json" |
||||
"errors" |
||||
"fmt" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/common/hexutil" |
||||
"github.com/ethereum/go-ethereum/common/math" |
||||
"github.com/ethereum/go-ethereum/core/rawdb" |
||||
"github.com/ethereum/go-ethereum/core/state" |
||||
"github.com/ethereum/go-ethereum/core/types" |
||||
"github.com/ethereum/go-ethereum/ethdb" |
||||
"github.com/ethereum/go-ethereum/log" |
||||
"github.com/ethereum/go-ethereum/params" |
||||
) |
||||
|
||||
//go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
|
||||
//go:generate gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go
|
||||
|
||||
var errGenesisNoConfig = errors.New("genesis has no chain configuration") |
||||
|
||||
// Genesis specifies the header fields, state of a genesis block. It also defines hard
|
||||
// fork switch-over blocks through the chain configuration.
|
||||
type Genesis struct { |
||||
Config *params.ChainConfig `json:"config"` |
||||
Nonce uint64 `json:"nonce"` |
||||
Timestamp uint64 `json:"timestamp"` |
||||
ExtraData []byte `json:"extraData"` |
||||
GasLimit uint64 `json:"gasLimit" gencodec:"required"` |
||||
Difficulty *big.Int `json:"difficulty" gencodec:"required"` |
||||
Mixhash common.Hash `json:"mixHash"` |
||||
Coinbase common.Address `json:"coinbase"` |
||||
Alloc GenesisAlloc `json:"alloc" gencodec:"required"` |
||||
|
||||
// These fields are used for consensus tests. Please don't use them
|
||||
// in actual genesis blocks.
|
||||
Number uint64 `json:"number"` |
||||
GasUsed uint64 `json:"gasUsed"` |
||||
ParentHash common.Hash `json:"parentHash"` |
||||
} |
||||
|
||||
// GenesisAlloc specifies the initial state that is part of the genesis block.
|
||||
type GenesisAlloc map[common.Address]GenesisAccount |
||||
|
||||
func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error { |
||||
m := make(map[common.UnprefixedAddress]GenesisAccount) |
||||
if err := json.Unmarshal(data, &m); err != nil { |
||||
return err |
||||
} |
||||
*ga = make(GenesisAlloc) |
||||
for addr, a := range m { |
||||
(*ga)[common.Address(addr)] = a |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// GenesisAccount is an account in the state of the genesis block.
|
||||
type GenesisAccount struct { |
||||
Code []byte `json:"code,omitempty"` |
||||
Storage map[common.Hash]common.Hash `json:"storage,omitempty"` |
||||
Balance *big.Int `json:"balance" gencodec:"required"` |
||||
Nonce uint64 `json:"nonce,omitempty"` |
||||
PrivateKey []byte `json:"secretKey,omitempty"` // for tests
|
||||
} |
||||
|
||||
// field type overrides for gencodec
|
||||
type genesisSpecMarshaling struct { |
||||
Nonce math.HexOrDecimal64 |
||||
Timestamp math.HexOrDecimal64 |
||||
ExtraData hexutil.Bytes |
||||
GasLimit math.HexOrDecimal64 |
||||
GasUsed math.HexOrDecimal64 |
||||
Number math.HexOrDecimal64 |
||||
Difficulty *math.HexOrDecimal256 |
||||
Alloc map[common.UnprefixedAddress]GenesisAccount |
||||
} |
||||
|
||||
type genesisAccountMarshaling struct { |
||||
Code hexutil.Bytes |
||||
Balance *math.HexOrDecimal256 |
||||
Nonce math.HexOrDecimal64 |
||||
Storage map[storageJSON]storageJSON |
||||
PrivateKey hexutil.Bytes |
||||
} |
||||
|
||||
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
|
||||
// unmarshaling from hex.
|
||||
type storageJSON common.Hash |
||||
|
||||
func (h *storageJSON) UnmarshalText(text []byte) error { |
||||
text = bytes.TrimPrefix(text, []byte("0x")) |
||||
if len(text) > 64 { |
||||
return fmt.Errorf("too many hex characters in storage key/value %q", text) |
||||
} |
||||
offset := len(h) - len(text)/2 // pad on the left
|
||||
if _, err := hex.Decode(h[offset:], text); err != nil { |
||||
fmt.Println(err) |
||||
return fmt.Errorf("invalid hex storage key/value %q", text) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
func (h storageJSON) MarshalText() ([]byte, error) { |
||||
return hexutil.Bytes(h[:]).MarshalText() |
||||
} |
||||
|
||||
// GenesisMismatchError is raised when trying to overwrite an existing
|
||||
// genesis block with an incompatible one.
|
||||
type GenesisMismatchError struct { |
||||
Stored, New common.Hash |
||||
} |
||||
|
||||
func (e *GenesisMismatchError) Error() string { |
||||
return fmt.Sprintf("database already contains an incompatible genesis block (have %x, new %x)", e.Stored[:8], e.New[:8]) |
||||
} |
||||
|
||||
// SetupGenesisBlock writes or updates the genesis block in db.
|
||||
// The block that will be used is:
|
||||
//
|
||||
// genesis == nil genesis != nil
|
||||
// +------------------------------------------
|
||||
// db has no genesis | main-net default | genesis
|
||||
// db has genesis | from DB | genesis (if compatible)
|
||||
//
|
||||
// The stored chain configuration will be updated if it is compatible (i.e. does not
|
||||
// specify a fork block below the local head block). In case of a conflict, the
|
||||
// error is a *params.ConfigCompatError and the new, unwritten config is returned.
|
||||
//
|
||||
// The returned chain configuration is never nil.
|
||||
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { |
||||
if genesis != nil && genesis.Config == nil { |
||||
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig |
||||
} |
||||
|
||||
// Just commit the new block if there is no stored genesis block.
|
||||
stored := rawdb.ReadCanonicalHash(db, 0) |
||||
if (stored == common.Hash{}) { |
||||
if genesis == nil { |
||||
log.Info("Writing default main-net genesis block") |
||||
genesis = DefaultGenesisBlock() |
||||
} else { |
||||
log.Info("Writing custom genesis block") |
||||
} |
||||
block, err := genesis.Commit(db) |
||||
return genesis.Config, block.Hash(), err |
||||
} |
||||
|
||||
// Check whether the genesis block is already written.
|
||||
if genesis != nil { |
||||
hash := genesis.ToBlock(nil).Hash() |
||||
if hash != stored { |
||||
return genesis.Config, hash, &GenesisMismatchError{stored, hash} |
||||
} |
||||
} |
||||
|
||||
// Get the existing chain configuration.
|
||||
newcfg := genesis.configOrDefault(stored) |
||||
storedcfg := rawdb.ReadChainConfig(db, stored) |
||||
if storedcfg == nil { |
||||
log.Warn("Found genesis block without chain config") |
||||
rawdb.WriteChainConfig(db, stored, newcfg) |
||||
return newcfg, stored, nil |
||||
} |
||||
// Special case: don't change the existing config of a non-mainnet chain if no new
|
||||
// config is supplied. These chains would get AllProtocolChanges (and a compat error)
|
||||
// if we just continued here.
|
||||
if genesis == nil && stored != params.MainnetGenesisHash { |
||||
return storedcfg, stored, nil |
||||
} |
||||
|
||||
// Check config compatibility and write the config. Compatibility errors
|
||||
// are returned to the caller unless we're already at block zero.
|
||||
height := rawdb.ReadHeaderNumber(db, rawdb.ReadHeadHeaderHash(db)) |
||||
if height == nil { |
||||
return newcfg, stored, fmt.Errorf("missing block number for head header hash") |
||||
} |
||||
compatErr := storedcfg.CheckCompatible(newcfg, *height) |
||||
if compatErr != nil && *height != 0 && compatErr.RewindTo != 0 { |
||||
return newcfg, stored, compatErr |
||||
} |
||||
rawdb.WriteChainConfig(db, stored, newcfg) |
||||
return newcfg, stored, nil |
||||
} |
||||
|
||||
func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig { |
||||
switch { |
||||
case g != nil: |
||||
return g.Config |
||||
case ghash == params.MainnetGenesisHash: |
||||
return params.MainnetChainConfig |
||||
case ghash == params.TestnetGenesisHash: |
||||
return params.TestnetChainConfig |
||||
default: |
||||
return params.AllEthashProtocolChanges |
||||
} |
||||
} |
||||
|
||||
// ToBlock creates the genesis block and writes state of a genesis specification
|
||||
// to the given database (or discards it if nil).
|
||||
func (g *Genesis) ToBlock(db ethdb.Database) *types.Block { |
||||
if db == nil { |
||||
db = ethdb.NewMemDatabase() |
||||
} |
||||
statedb, _ := state.New(common.Hash{}, state.NewDatabase(db)) |
||||
for addr, account := range g.Alloc { |
||||
statedb.AddBalance(addr, account.Balance) |
||||
statedb.SetCode(addr, account.Code) |
||||
statedb.SetNonce(addr, account.Nonce) |
||||
for key, value := range account.Storage { |
||||
statedb.SetState(addr, key, value) |
||||
} |
||||
} |
||||
root := statedb.IntermediateRoot(false) |
||||
head := &types.Header{ |
||||
Number: new(big.Int).SetUint64(g.Number), |
||||
Nonce: types.EncodeNonce(g.Nonce), |
||||
Time: new(big.Int).SetUint64(g.Timestamp), |
||||
ParentHash: g.ParentHash, |
||||
Extra: g.ExtraData, |
||||
GasLimit: g.GasLimit, |
||||
GasUsed: g.GasUsed, |
||||
Difficulty: g.Difficulty, |
||||
MixDigest: g.Mixhash, |
||||
Coinbase: g.Coinbase, |
||||
Root: root, |
||||
} |
||||
if g.GasLimit == 0 { |
||||
head.GasLimit = params.GenesisGasLimit |
||||
} |
||||
if g.Difficulty == nil { |
||||
head.Difficulty = params.GenesisDifficulty |
||||
} |
||||
statedb.Commit(false) |
||||
statedb.Database().TrieDB().Commit(root, true) |
||||
|
||||
return types.NewBlock(head, nil, nil, nil) |
||||
} |
||||
|
||||
// Commit writes the block and state of a genesis specification to the database.
|
||||
// The block is committed as the canonical head block.
|
||||
func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) { |
||||
block := g.ToBlock(db) |
||||
if block.Number().Sign() != 0 { |
||||
return nil, fmt.Errorf("can't commit genesis block with number > 0") |
||||
} |
||||
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty) |
||||
rawdb.WriteBlock(db, block) |
||||
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil) |
||||
rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64()) |
||||
rawdb.WriteHeadBlockHash(db, block.Hash()) |
||||
rawdb.WriteHeadHeaderHash(db, block.Hash()) |
||||
|
||||
config := g.Config |
||||
if config == nil { |
||||
config = params.AllEthashProtocolChanges |
||||
} |
||||
rawdb.WriteChainConfig(db, block.Hash(), config) |
||||
return block, nil |
||||
} |
||||
|
||||
// MustCommit writes the genesis block and state to db, panicking on error.
|
||||
// The block is committed as the canonical head block.
|
||||
func (g *Genesis) MustCommit(db ethdb.Database) *types.Block { |
||||
block, err := g.Commit(db) |
||||
if err != nil { |
||||
panic(err) |
||||
} |
||||
return block |
||||
} |
||||
|
||||
// GenesisBlockForTesting creates and writes a block in which addr has the given wei balance.
|
||||
func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { |
||||
g := Genesis{Alloc: GenesisAlloc{addr: {Balance: balance}}} |
||||
return g.MustCommit(db) |
||||
} |
||||
|
||||
// DefaultGenesisBlock returns the Ethereum main net genesis block.
|
||||
func DefaultGenesisBlock() *Genesis { |
||||
return &Genesis{ |
||||
Config: params.MainnetChainConfig, |
||||
Nonce: 66, |
||||
ExtraData: hexutil.MustDecode("0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa"), |
||||
GasLimit: 5000, |
||||
Difficulty: big.NewInt(17179869184), |
||||
Alloc: decodePrealloc("empty"), |
||||
} |
||||
} |
||||
|
||||
// DeveloperGenesisBlock returns the 'geth --dev' genesis block. Note, this must
|
||||
// be seeded with the
|
||||
func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis { |
||||
// Override the default period to the user requested one
|
||||
config := *params.AllCliqueProtocolChanges |
||||
config.Clique.Period = period |
||||
|
||||
// Assemble and return the genesis with the precompiles and faucet pre-funded
|
||||
return &Genesis{ |
||||
Config: &config, |
||||
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...), |
||||
GasLimit: 6283185, |
||||
Difficulty: big.NewInt(1), |
||||
Alloc: map[common.Address]GenesisAccount{ |
||||
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
||||
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
|
||||
common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD
|
||||
common.BytesToAddress([]byte{4}): {Balance: big.NewInt(1)}, // Identity
|
||||
common.BytesToAddress([]byte{5}): {Balance: big.NewInt(1)}, // ModExp
|
||||
common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd
|
||||
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
|
||||
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
|
||||
faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}, |
||||
}, |
||||
} |
||||
} |
||||
|
||||
func decodePrealloc(data string) GenesisAlloc { |
||||
var p []struct{ Addr, Balance *big.Int } |
||||
|
||||
// Create empty allocation for now
|
||||
// TODO: create genesis block with actual content
|
||||
//if err := rlp.NewStream(strings.NewReader(data), 0).Decode(&p); err != nil {
|
||||
// panic(err)
|
||||
//}
|
||||
_ = data |
||||
ga := make(GenesisAlloc, len(p)) |
||||
for _, account := range p { |
||||
ga[common.BigToAddress(account.Addr)] = GenesisAccount{Balance: account.Balance} |
||||
} |
||||
return ga |
||||
} |
@ -0,0 +1,507 @@ |
||||
// 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 ( |
||||
crand "crypto/rand" |
||||
"errors" |
||||
"fmt" |
||||
"math" |
||||
"math/big" |
||||
mrand "math/rand" |
||||
"sync/atomic" |
||||
"time" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/ethdb" |
||||
"github.com/ethereum/go-ethereum/log" |
||||
"github.com/ethereum/go-ethereum/params" |
||||
"github.com/hashicorp/golang-lru" |
||||
"github.com/simple-rules/harmony-benchmark/consensus" |
||||
"github.com/simple-rules/harmony-benchmark/core/rawdb" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
const ( |
||||
headerCacheLimit = 512 |
||||
tdCacheLimit = 1024 |
||||
numberCacheLimit = 2048 |
||||
) |
||||
|
||||
// HeaderChain implements the basic block header chain logic that is shared by
|
||||
// core.BlockChain and light.LightChain. It is not usable in itself, only as
|
||||
// a part of either structure.
|
||||
// It is not thread safe either, the encapsulating chain structures should do
|
||||
// the necessary mutex locking/unlocking.
|
||||
type HeaderChain struct { |
||||
config *params.ChainConfig |
||||
|
||||
chainDb ethdb.Database |
||||
genesisHeader *types.Header |
||||
|
||||
currentHeader atomic.Value // Current head of the header chain (may be above the block chain!)
|
||||
currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
|
||||
|
||||
headerCache *lru.Cache // Cache for the most recent block headers
|
||||
tdCache *lru.Cache // Cache for the most recent block total difficulties
|
||||
numberCache *lru.Cache // Cache for the most recent block numbers
|
||||
|
||||
procInterrupt func() bool |
||||
|
||||
rand *mrand.Rand |
||||
engine consensus.Engine |
||||
} |
||||
|
||||
// NewHeaderChain creates a new HeaderChain structure.
|
||||
// getValidator should return the parent's validator
|
||||
// procInterrupt points to the parent's interrupt semaphore
|
||||
// wg points to the parent's shutdown wait group
|
||||
func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) { |
||||
headerCache, _ := lru.New(headerCacheLimit) |
||||
tdCache, _ := lru.New(tdCacheLimit) |
||||
numberCache, _ := lru.New(numberCacheLimit) |
||||
|
||||
// Seed a fast but crypto originating random generator
|
||||
seed, err := crand.Int(crand.Reader, big.NewInt(math.MaxInt64)) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
hc := &HeaderChain{ |
||||
config: config, |
||||
chainDb: chainDb, |
||||
headerCache: headerCache, |
||||
tdCache: tdCache, |
||||
numberCache: numberCache, |
||||
procInterrupt: procInterrupt, |
||||
rand: mrand.New(mrand.NewSource(seed.Int64())), |
||||
engine: engine, |
||||
} |
||||
|
||||
hc.genesisHeader = hc.GetHeaderByNumber(0) |
||||
if hc.genesisHeader == nil { |
||||
return nil, ErrNoGenesis |
||||
} |
||||
|
||||
hc.currentHeader.Store(hc.genesisHeader) |
||||
if head := rawdb.ReadHeadBlockHash(chainDb); head != (common.Hash{}) { |
||||
if chead := hc.GetHeaderByHash(head); chead != nil { |
||||
hc.currentHeader.Store(chead) |
||||
} |
||||
} |
||||
hc.currentHeaderHash = hc.CurrentHeader().Hash() |
||||
|
||||
return hc, nil |
||||
} |
||||
|
||||
// GetBlockNumber retrieves the block number belonging to the given hash
|
||||
// from the cache or database
|
||||
func (hc *HeaderChain) GetBlockNumber(hash common.Hash) *uint64 { |
||||
if cached, ok := hc.numberCache.Get(hash); ok { |
||||
number := cached.(uint64) |
||||
return &number |
||||
} |
||||
number := rawdb.ReadHeaderNumber(hc.chainDb, hash) |
||||
if number != nil { |
||||
hc.numberCache.Add(hash, *number) |
||||
} |
||||
return number |
||||
} |
||||
|
||||
// WriteHeader writes a header into the local chain, given that its parent is
|
||||
// already known. If the total difficulty of the newly inserted header becomes
|
||||
// greater than the current known TD, the canonical chain is re-routed.
|
||||
//
|
||||
// Note: This method is not concurrent-safe with inserting blocks simultaneously
|
||||
// into the chain, as side effects caused by reorganisations cannot be emulated
|
||||
// without the real blocks. Hence, writing headers directly should only be done
|
||||
// in two scenarios: pure-header mode of operation (light clients), or properly
|
||||
// separated header/block phases (non-archive clients).
|
||||
func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, err error) { |
||||
// Cache some values to prevent constant recalculation
|
||||
var ( |
||||
hash = header.Hash() |
||||
number = header.Number.Uint64() |
||||
) |
||||
// Calculate the total difficulty of the header
|
||||
ptd := hc.GetTd(header.ParentHash, number-1) |
||||
if ptd == nil { |
||||
return NonStatTy, consensus.ErrUnknownAncestor |
||||
} |
||||
localTd := hc.GetTd(hc.currentHeaderHash, hc.CurrentHeader().Number.Uint64()) |
||||
externTd := new(big.Int).Add(header.Difficulty, ptd) |
||||
|
||||
// Irrelevant of the canonical status, write the td and header to the database
|
||||
if err := hc.WriteTd(hash, number, externTd); err != nil { |
||||
log.Crit("Failed to write header total difficulty", "err", err) |
||||
} |
||||
//rawdb.WriteHeader(hc.chainDb, header)
|
||||
|
||||
// If the total difficulty is higher than our known, add it to the canonical chain
|
||||
// Second clause in the if statement reduces the vulnerability to selfish mining.
|
||||
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
|
||||
if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) { |
||||
// Delete any canonical number assignments above the new head
|
||||
batch := hc.chainDb.NewBatch() |
||||
for i := number + 1; ; i++ { |
||||
hash := rawdb.ReadCanonicalHash(hc.chainDb, i) |
||||
if hash == (common.Hash{}) { |
||||
break |
||||
} |
||||
rawdb.DeleteCanonicalHash(batch, i) |
||||
} |
||||
batch.Write() |
||||
|
||||
// Overwrite any stale canonical number assignments
|
||||
var ( |
||||
headHash = header.ParentHash |
||||
headNumber = header.Number.Uint64() - 1 |
||||
headHeader = hc.GetHeader(headHash, headNumber) |
||||
) |
||||
for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash { |
||||
rawdb.WriteCanonicalHash(hc.chainDb, headHash, headNumber) |
||||
|
||||
headHash = headHeader.ParentHash |
||||
headNumber = headHeader.Number.Uint64() - 1 |
||||
headHeader = hc.GetHeader(headHash, headNumber) |
||||
} |
||||
// Extend the canonical chain with the new header
|
||||
rawdb.WriteCanonicalHash(hc.chainDb, hash, number) |
||||
rawdb.WriteHeadHeaderHash(hc.chainDb, hash) |
||||
|
||||
hc.currentHeaderHash = hash |
||||
hc.currentHeader.Store(types.CopyHeader(header)) |
||||
|
||||
status = CanonStatTy |
||||
} else { |
||||
status = SideStatTy |
||||
} |
||||
|
||||
hc.headerCache.Add(hash, header) |
||||
hc.numberCache.Add(hash, number) |
||||
|
||||
return |
||||
} |
||||
|
||||
// WhCallback is a callback function for inserting individual headers.
|
||||
// A callback is used for two reasons: first, in a LightChain, status should be
|
||||
// processed and light chain events sent, while in a BlockChain this is not
|
||||
// necessary since chain events are sent after inserting blocks. Second, the
|
||||
// header writes should be protected by the parent chain mutex individually.
|
||||
type WhCallback func(*types.Header) error |
||||
|
||||
func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int) (int, error) { |
||||
// Do a sanity check that the provided chain is actually ordered and linked
|
||||
for i := 1; i < len(chain); i++ { |
||||
if chain[i].Number.Uint64() != chain[i-1].Number.Uint64()+1 || chain[i].ParentHash != chain[i-1].Hash() { |
||||
// Chain broke ancestry, log a message (programming error) and skip insertion
|
||||
log.Error("Non contiguous header insert", "number", chain[i].Number, "hash", chain[i].Hash(), |
||||
"parent", chain[i].ParentHash, "prevnumber", chain[i-1].Number, "prevhash", chain[i-1].Hash()) |
||||
|
||||
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].Number, |
||||
chain[i-1].Hash().Bytes()[:4], i, chain[i].Number, chain[i].Hash().Bytes()[:4], chain[i].ParentHash[:4]) |
||||
} |
||||
} |
||||
|
||||
// Generate the list of seal verification requests, and start the parallel verifier
|
||||
seals := make([]bool, len(chain)) |
||||
for i := 0; i < len(seals)/checkFreq; i++ { |
||||
index := i*checkFreq + hc.rand.Intn(checkFreq) |
||||
if index >= len(seals) { |
||||
index = len(seals) - 1 |
||||
} |
||||
seals[index] = true |
||||
} |
||||
seals[len(seals)-1] = true // Last should always be verified to avoid junk
|
||||
|
||||
//abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
|
||||
//defer close(abort)
|
||||
//
|
||||
//// Iterate over the headers and ensure they all check out
|
||||
//for i, _ := range chain {
|
||||
// // If the chain is terminating, stop processing blocks
|
||||
// if hc.procInterrupt() {
|
||||
// log.Debug("Premature abort during headers verification")
|
||||
// return 0, errors.New("aborted")
|
||||
// }
|
||||
//
|
||||
// // Otherwise wait for headers checks and ensure they pass
|
||||
// if err := <-results; err != nil {
|
||||
// return i, err
|
||||
// }
|
||||
//}
|
||||
|
||||
return 0, nil |
||||
} |
||||
|
||||
// InsertHeaderChain attempts to insert the given header chain in to the local
|
||||
// chain, possibly creating a reorg. If an error is returned, it will return the
|
||||
// index number of the failing header as well an error describing what went wrong.
|
||||
//
|
||||
// The verify parameter can be used to fine tune whether nonce verification
|
||||
// should be done or not. The reason behind the optional check is because some
|
||||
// of the header retrieval mechanisms already need to verfy nonces, as well as
|
||||
// because nonces can be verified sparsely, not needing to check each.
|
||||
func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, writeHeader WhCallback, start time.Time) (int, error) { |
||||
// Collect some import statistics to report on
|
||||
stats := struct{ processed, ignored int }{} |
||||
// All headers passed verification, import them into the database
|
||||
for i, header := range chain { |
||||
// Short circuit insertion if shutting down
|
||||
if hc.procInterrupt() { |
||||
log.Debug("Premature abort during headers import") |
||||
return i, errors.New("aborted") |
||||
} |
||||
// If the header's already known, skip it, otherwise store
|
||||
if hc.HasHeader(header.Hash(), header.Number.Uint64()) { |
||||
stats.ignored++ |
||||
continue |
||||
} |
||||
if err := writeHeader(header); err != nil { |
||||
return i, err |
||||
} |
||||
stats.processed++ |
||||
} |
||||
// Report some public statistics so the user has a clue what's going on
|
||||
last := chain[len(chain)-1] |
||||
|
||||
context := []interface{}{ |
||||
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)), |
||||
"number", last.Number, "hash", last.Hash(), |
||||
} |
||||
if timestamp := time.Unix(last.Time.Int64(), 0); time.Since(timestamp) > time.Minute { |
||||
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...) |
||||
} |
||||
if stats.ignored > 0 { |
||||
context = append(context, []interface{}{"ignored", stats.ignored}...) |
||||
} |
||||
log.Info("Imported new block headers", context...) |
||||
|
||||
return 0, nil |
||||
} |
||||
|
||||
// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
|
||||
// hash, fetching towards the genesis block.
|
||||
func (hc *HeaderChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { |
||||
// Get the origin header from which to fetch
|
||||
header := hc.GetHeaderByHash(hash) |
||||
if header == nil { |
||||
return nil |
||||
} |
||||
// Iterate the headers until enough is collected or the genesis reached
|
||||
chain := make([]common.Hash, 0, max) |
||||
for i := uint64(0); i < max; i++ { |
||||
next := header.ParentHash |
||||
if header = hc.GetHeader(next, header.Number.Uint64()-1); header == nil { |
||||
break |
||||
} |
||||
chain = append(chain, next) |
||||
if header.Number.Sign() == 0 { |
||||
break |
||||
} |
||||
} |
||||
return chain |
||||
} |
||||
|
||||
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
|
||||
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
|
||||
// number of blocks to be individually checked before we reach the canonical chain.
|
||||
//
|
||||
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
|
||||
func (hc *HeaderChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) { |
||||
if ancestor > number { |
||||
return common.Hash{}, 0 |
||||
} |
||||
if ancestor == 1 { |
||||
// in this case it is cheaper to just read the header
|
||||
if header := hc.GetHeader(hash, number); header != nil { |
||||
return header.ParentHash, number - 1 |
||||
} else { |
||||
return common.Hash{}, 0 |
||||
} |
||||
} |
||||
for ancestor != 0 { |
||||
if rawdb.ReadCanonicalHash(hc.chainDb, number) == hash { |
||||
number -= ancestor |
||||
return rawdb.ReadCanonicalHash(hc.chainDb, number), number |
||||
} |
||||
if *maxNonCanonical == 0 { |
||||
return common.Hash{}, 0 |
||||
} |
||||
*maxNonCanonical-- |
||||
ancestor-- |
||||
header := hc.GetHeader(hash, number) |
||||
if header == nil { |
||||
return common.Hash{}, 0 |
||||
} |
||||
hash = header.ParentHash |
||||
number-- |
||||
} |
||||
return hash, number |
||||
} |
||||
|
||||
// GetTd retrieves a block's total difficulty in the canonical chain from the
|
||||
// database by hash and number, caching it if found.
|
||||
func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int { |
||||
// Short circuit if the td's already in the cache, retrieve otherwise
|
||||
if cached, ok := hc.tdCache.Get(hash); ok { |
||||
return cached.(*big.Int) |
||||
} |
||||
td := rawdb.ReadTd(hc.chainDb, hash, number) |
||||
if td == nil { |
||||
return nil |
||||
} |
||||
// Cache the found body for next time and return
|
||||
hc.tdCache.Add(hash, td) |
||||
return td |
||||
} |
||||
|
||||
// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
|
||||
// database by hash, caching it if found.
|
||||
func (hc *HeaderChain) GetTdByHash(hash common.Hash) *big.Int { |
||||
number := hc.GetBlockNumber(hash) |
||||
if number == nil { |
||||
return nil |
||||
} |
||||
return hc.GetTd(hash, *number) |
||||
} |
||||
|
||||
// WriteTd stores a block's total difficulty into the database, also caching it
|
||||
// along the way.
|
||||
func (hc *HeaderChain) WriteTd(hash common.Hash, number uint64, td *big.Int) error { |
||||
rawdb.WriteTd(hc.chainDb, hash, number, td) |
||||
hc.tdCache.Add(hash, new(big.Int).Set(td)) |
||||
return nil |
||||
} |
||||
|
||||
// GetHeader retrieves a block header from the database by hash and number,
|
||||
// caching it if found.
|
||||
func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header { |
||||
// Short circuit if the header's already in the cache, retrieve otherwise
|
||||
if header, ok := hc.headerCache.Get(hash); ok { |
||||
return header.(*types.Header) |
||||
} |
||||
header := rawdb.ReadHeader(hc.chainDb, hash, number) |
||||
if header == nil { |
||||
return nil |
||||
} |
||||
// Cache the found header for next time and return
|
||||
hc.headerCache.Add(hash, header) |
||||
return header |
||||
} |
||||
|
||||
// GetHeaderByHash retrieves a block header from the database by hash, caching it if
|
||||
// found.
|
||||
func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header { |
||||
number := hc.GetBlockNumber(hash) |
||||
if number == nil { |
||||
return nil |
||||
} |
||||
return hc.GetHeader(hash, *number) |
||||
} |
||||
|
||||
// HasHeader checks if a block header is present in the database or not.
|
||||
func (hc *HeaderChain) HasHeader(hash common.Hash, number uint64) bool { |
||||
if hc.numberCache.Contains(hash) || hc.headerCache.Contains(hash) { |
||||
return true |
||||
} |
||||
return rawdb.HasHeader(hc.chainDb, hash, number) |
||||
} |
||||
|
||||
// GetHeaderByNumber retrieves a block header from the database by number,
|
||||
// caching it (associated with its hash) if found.
|
||||
func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header { |
||||
hash := rawdb.ReadCanonicalHash(hc.chainDb, number) |
||||
if hash == (common.Hash{}) { |
||||
return nil |
||||
} |
||||
return hc.GetHeader(hash, number) |
||||
} |
||||
|
||||
// CurrentHeader retrieves the current head header of the canonical chain. The
|
||||
// header is retrieved from the HeaderChain's internal cache.
|
||||
func (hc *HeaderChain) CurrentHeader() *types.Header { |
||||
return hc.currentHeader.Load().(*types.Header) |
||||
} |
||||
|
||||
// SetCurrentHeader sets the current head header of the canonical chain.
|
||||
func (hc *HeaderChain) SetCurrentHeader(head *types.Header) { |
||||
rawdb.WriteHeadHeaderHash(hc.chainDb, head.Hash()) |
||||
|
||||
hc.currentHeader.Store(head) |
||||
hc.currentHeaderHash = head.Hash() |
||||
} |
||||
|
||||
// DeleteCallback is a callback function that is called by SetHead before
|
||||
// each header is deleted.
|
||||
type DeleteCallback func(rawdb.DatabaseDeleter, common.Hash, uint64) |
||||
|
||||
// SetHead rewinds the local chain to a new head. Everything above the new head
|
||||
// will be deleted and the new one set.
|
||||
func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) { |
||||
height := uint64(0) |
||||
|
||||
if hdr := hc.CurrentHeader(); hdr != nil { |
||||
height = hdr.Number.Uint64() |
||||
} |
||||
batch := hc.chainDb.NewBatch() |
||||
for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() { |
||||
hash := hdr.Hash() |
||||
num := hdr.Number.Uint64() |
||||
if delFn != nil { |
||||
delFn(batch, hash, num) |
||||
} |
||||
rawdb.DeleteHeader(batch, hash, num) |
||||
rawdb.DeleteTd(batch, hash, num) |
||||
|
||||
hc.currentHeader.Store(hc.GetHeader(hdr.ParentHash, hdr.Number.Uint64()-1)) |
||||
} |
||||
// Roll back the canonical chain numbering
|
||||
for i := height; i > head; i-- { |
||||
rawdb.DeleteCanonicalHash(batch, i) |
||||
} |
||||
batch.Write() |
||||
|
||||
// Clear out any stale content from the caches
|
||||
hc.headerCache.Purge() |
||||
hc.tdCache.Purge() |
||||
hc.numberCache.Purge() |
||||
|
||||
if hc.CurrentHeader() == nil { |
||||
hc.currentHeader.Store(hc.genesisHeader) |
||||
} |
||||
hc.currentHeaderHash = hc.CurrentHeader().Hash() |
||||
|
||||
rawdb.WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash) |
||||
} |
||||
|
||||
// SetGenesis sets a new genesis block header for the chain
|
||||
func (hc *HeaderChain) SetGenesis(head *types.Header) { |
||||
hc.genesisHeader = head |
||||
} |
||||
|
||||
// Config retrieves the header chain's chain configuration.
|
||||
func (hc *HeaderChain) Config() *params.ChainConfig { return hc.config } |
||||
|
||||
// Engine retrieves the header chain's consensus engine.
|
||||
func (hc *HeaderChain) Engine() consensus.Engine { return hc.engine } |
||||
|
||||
// GetBlock implements consensus.ChainReader, and returns nil for every input as
|
||||
// a header chain does not have blocks available for retrieval.
|
||||
func (hc *HeaderChain) GetBlock(hash common.Hash, number uint64) *types.Block { |
||||
return nil |
||||
} |
@ -0,0 +1,375 @@ |
||||
// 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 rawdb |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/binary" |
||||
"math/big" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/log" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
|
||||
func ReadCanonicalHash(db DatabaseReader, number uint64) common.Hash { |
||||
data, _ := db.Get(headerHashKey(number)) |
||||
if len(data) == 0 { |
||||
return common.Hash{} |
||||
} |
||||
return common.BytesToHash(data) |
||||
} |
||||
|
||||
// WriteCanonicalHash stores the hash assigned to a canonical block number.
|
||||
func WriteCanonicalHash(db DatabaseWriter, hash common.Hash, number uint64) { |
||||
if err := db.Put(headerHashKey(number), hash.Bytes()); err != nil { |
||||
log.Crit("Failed to store number to hash mapping", "err", err) |
||||
} |
||||
} |
||||
|
||||
// DeleteCanonicalHash removes the number to hash canonical mapping.
|
||||
func DeleteCanonicalHash(db DatabaseDeleter, number uint64) { |
||||
if err := db.Delete(headerHashKey(number)); err != nil { |
||||
log.Crit("Failed to delete number to hash mapping", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadHeaderNumber returns the header number assigned to a hash.
|
||||
func ReadHeaderNumber(db DatabaseReader, hash common.Hash) *uint64 { |
||||
data, _ := db.Get(headerNumberKey(hash)) |
||||
if len(data) != 8 { |
||||
return nil |
||||
} |
||||
number := binary.BigEndian.Uint64(data) |
||||
return &number |
||||
} |
||||
|
||||
// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
|
||||
func ReadHeadHeaderHash(db DatabaseReader) common.Hash { |
||||
data, _ := db.Get(headHeaderKey) |
||||
if len(data) == 0 { |
||||
return common.Hash{} |
||||
} |
||||
return common.BytesToHash(data) |
||||
} |
||||
|
||||
// WriteHeadHeaderHash stores the hash of the current canonical head header.
|
||||
func WriteHeadHeaderHash(db DatabaseWriter, hash common.Hash) { |
||||
if err := db.Put(headHeaderKey, hash.Bytes()); err != nil { |
||||
log.Crit("Failed to store last header's hash", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadHeadBlockHash retrieves the hash of the current canonical head block.
|
||||
func ReadHeadBlockHash(db DatabaseReader) common.Hash { |
||||
data, _ := db.Get(headBlockKey) |
||||
if len(data) == 0 { |
||||
return common.Hash{} |
||||
} |
||||
return common.BytesToHash(data) |
||||
} |
||||
|
||||
// WriteHeadBlockHash stores the head block's hash.
|
||||
func WriteHeadBlockHash(db DatabaseWriter, hash common.Hash) { |
||||
if err := db.Put(headBlockKey, hash.Bytes()); err != nil { |
||||
log.Crit("Failed to store last block's hash", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
|
||||
func ReadHeadFastBlockHash(db DatabaseReader) common.Hash { |
||||
data, _ := db.Get(headFastBlockKey) |
||||
if len(data) == 0 { |
||||
return common.Hash{} |
||||
} |
||||
return common.BytesToHash(data) |
||||
} |
||||
|
||||
// WriteHeadFastBlockHash stores the hash of the current fast-sync head block.
|
||||
func WriteHeadFastBlockHash(db DatabaseWriter, hash common.Hash) { |
||||
if err := db.Put(headFastBlockKey, hash.Bytes()); err != nil { |
||||
log.Crit("Failed to store last fast block's hash", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
|
||||
// reporting correct numbers across restarts.
|
||||
func ReadFastTrieProgress(db DatabaseReader) uint64 { |
||||
data, _ := db.Get(fastTrieProgressKey) |
||||
if len(data) == 0 { |
||||
return 0 |
||||
} |
||||
return new(big.Int).SetBytes(data).Uint64() |
||||
} |
||||
|
||||
// WriteFastTrieProgress stores the fast sync trie process counter to support
|
||||
// retrieving it across restarts.
|
||||
func WriteFastTrieProgress(db DatabaseWriter, count uint64) { |
||||
if err := db.Put(fastTrieProgressKey, new(big.Int).SetUint64(count).Bytes()); err != nil { |
||||
log.Crit("Failed to store fast sync trie progress", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||
func ReadHeaderRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue { |
||||
data, _ := db.Get(headerKey(number, hash)) |
||||
return data |
||||
} |
||||
|
||||
// HasHeader verifies the existence of a block header corresponding to the hash.
|
||||
func HasHeader(db DatabaseReader, hash common.Hash, number uint64) bool { |
||||
if has, err := db.Has(headerKey(number, hash)); !has || err != nil { |
||||
return false |
||||
} |
||||
return true |
||||
} |
||||
|
||||
// ReadHeader retrieves the block header corresponding to the hash.
|
||||
func ReadHeader(db DatabaseReader, hash common.Hash, number uint64) *types.Header { |
||||
data := ReadHeaderRLP(db, hash, number) |
||||
if len(data) == 0 { |
||||
return nil |
||||
} |
||||
header := new(types.Header) |
||||
if err := rlp.Decode(bytes.NewReader(data), header); err != nil { |
||||
log.Error("Invalid block header RLP", "hash", hash, "err", err) |
||||
return nil |
||||
} |
||||
return header |
||||
} |
||||
|
||||
// WriteHeader stores a block header into the database and also stores the hash-
|
||||
// to-number mapping.
|
||||
func WriteHeader(db DatabaseWriter, header *types.Header) { |
||||
// Write the hash -> number mapping
|
||||
var ( |
||||
hash = header.Hash() |
||||
number = header.Number.Uint64() |
||||
encoded = encodeBlockNumber(number) |
||||
) |
||||
key := headerNumberKey(hash) |
||||
if err := db.Put(key, encoded); err != nil { |
||||
log.Crit("Failed to store hash to number mapping", "err", err) |
||||
} |
||||
// Write the encoded header
|
||||
data, err := rlp.EncodeToBytes(header) |
||||
if err != nil { |
||||
log.Crit("Failed to RLP encode header", "err", err) |
||||
} |
||||
key = headerKey(number, hash) |
||||
if err := db.Put(key, data); err != nil { |
||||
log.Crit("Failed to store header", "err", err) |
||||
} |
||||
} |
||||
|
||||
// DeleteHeader removes all block header data associated with a hash.
|
||||
func DeleteHeader(db DatabaseDeleter, hash common.Hash, number uint64) { |
||||
if err := db.Delete(headerKey(number, hash)); err != nil { |
||||
log.Crit("Failed to delete header", "err", err) |
||||
} |
||||
if err := db.Delete(headerNumberKey(hash)); err != nil { |
||||
log.Crit("Failed to delete hash to number mapping", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
|
||||
func ReadBodyRLP(db DatabaseReader, hash common.Hash, number uint64) rlp.RawValue { |
||||
data, _ := db.Get(blockBodyKey(number, hash)) |
||||
return data |
||||
} |
||||
|
||||
// WriteBodyRLP stores an RLP encoded block body into the database.
|
||||
func WriteBodyRLP(db DatabaseWriter, hash common.Hash, number uint64, rlp rlp.RawValue) { |
||||
if err := db.Put(blockBodyKey(number, hash), rlp); err != nil { |
||||
log.Crit("Failed to store block body", "err", err) |
||||
} |
||||
} |
||||
|
||||
// HasBody verifies the existence of a block body corresponding to the hash.
|
||||
func HasBody(db DatabaseReader, hash common.Hash, number uint64) bool { |
||||
if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil { |
||||
return false |
||||
} |
||||
return true |
||||
} |
||||
|
||||
// ReadBody retrieves the block body corresponding to the hash.
|
||||
func ReadBody(db DatabaseReader, hash common.Hash, number uint64) *types.Body { |
||||
data := ReadBodyRLP(db, hash, number) |
||||
if len(data) == 0 { |
||||
return nil |
||||
} |
||||
body := new(types.Body) |
||||
if err := rlp.Decode(bytes.NewReader(data), body); err != nil { |
||||
log.Error("Invalid block body RLP", "hash", hash, "err", err) |
||||
return nil |
||||
} |
||||
return body |
||||
} |
||||
|
||||
// WriteBody storea a block body into the database.
|
||||
func WriteBody(db DatabaseWriter, hash common.Hash, number uint64, body *types.Body) { |
||||
data, err := rlp.EncodeToBytes(body) |
||||
if err != nil { |
||||
log.Crit("Failed to RLP encode body", "err", err) |
||||
} |
||||
WriteBodyRLP(db, hash, number, data) |
||||
} |
||||
|
||||
// DeleteBody removes all block body data associated with a hash.
|
||||
func DeleteBody(db DatabaseDeleter, hash common.Hash, number uint64) { |
||||
if err := db.Delete(blockBodyKey(number, hash)); err != nil { |
||||
log.Crit("Failed to delete block body", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadTd retrieves a block's total difficulty corresponding to the hash.
|
||||
func ReadTd(db DatabaseReader, hash common.Hash, number uint64) *big.Int { |
||||
data, _ := db.Get(headerTDKey(number, hash)) |
||||
if len(data) == 0 { |
||||
return nil |
||||
} |
||||
td := new(big.Int) |
||||
if err := rlp.Decode(bytes.NewReader(data), td); err != nil { |
||||
log.Error("Invalid block total difficulty RLP", "hash", hash, "err", err) |
||||
return nil |
||||
} |
||||
return td |
||||
} |
||||
|
||||
// WriteTd stores the total difficulty of a block into the database.
|
||||
func WriteTd(db DatabaseWriter, hash common.Hash, number uint64, td *big.Int) { |
||||
data, err := rlp.EncodeToBytes(td) |
||||
if err != nil { |
||||
log.Crit("Failed to RLP encode block total difficulty", "err", err) |
||||
} |
||||
if err := db.Put(headerTDKey(number, hash), data); err != nil { |
||||
log.Crit("Failed to store block total difficulty", "err", err) |
||||
} |
||||
} |
||||
|
||||
// DeleteTd removes all block total difficulty data associated with a hash.
|
||||
func DeleteTd(db DatabaseDeleter, hash common.Hash, number uint64) { |
||||
if err := db.Delete(headerTDKey(number, hash)); err != nil { |
||||
log.Crit("Failed to delete block total difficulty", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadReceipts retrieves all the transaction receipts belonging to a block.
|
||||
func ReadReceipts(db DatabaseReader, hash common.Hash, number uint64) types.Receipts { |
||||
// Retrieve the flattened receipt slice
|
||||
data, _ := db.Get(blockReceiptsKey(number, hash)) |
||||
if len(data) == 0 { |
||||
return nil |
||||
} |
||||
// Convert the receipts from their storage form to their internal representation
|
||||
storageReceipts := []*types.ReceiptForStorage{} |
||||
if err := rlp.DecodeBytes(data, &storageReceipts); err != nil { |
||||
log.Error("Invalid receipt array RLP", "hash", hash, "err", err) |
||||
return nil |
||||
} |
||||
receipts := make(types.Receipts, len(storageReceipts)) |
||||
for i, receipt := range storageReceipts { |
||||
receipts[i] = (*types.Receipt)(receipt) |
||||
} |
||||
return receipts |
||||
} |
||||
|
||||
// WriteReceipts stores all the transaction receipts belonging to a block.
|
||||
func WriteReceipts(db DatabaseWriter, hash common.Hash, number uint64, receipts types.Receipts) { |
||||
// Convert the receipts into their storage form and serialize them
|
||||
storageReceipts := make([]*types.ReceiptForStorage, len(receipts)) |
||||
for i, receipt := range receipts { |
||||
storageReceipts[i] = (*types.ReceiptForStorage)(receipt) |
||||
} |
||||
bytes, err := rlp.EncodeToBytes(storageReceipts) |
||||
if err != nil { |
||||
log.Crit("Failed to encode block receipts", "err", err) |
||||
} |
||||
// Store the flattened receipt slice
|
||||
if err := db.Put(blockReceiptsKey(number, hash), bytes); err != nil { |
||||
log.Crit("Failed to store block receipts", "err", err) |
||||
} |
||||
} |
||||
|
||||
// DeleteReceipts removes all receipt data associated with a block hash.
|
||||
func DeleteReceipts(db DatabaseDeleter, hash common.Hash, number uint64) { |
||||
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil { |
||||
log.Crit("Failed to delete block receipts", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadBlock retrieves an entire block corresponding to the hash, assembling it
|
||||
// back from the stored header and body. If either the header or body could not
|
||||
// be retrieved nil is returned.
|
||||
//
|
||||
// Note, due to concurrent download of header and block body the header and thus
|
||||
// canonical hash can be stored in the database but the body data not (yet).
|
||||
func ReadBlock(db DatabaseReader, hash common.Hash, number uint64) *types.Block { |
||||
header := ReadHeader(db, hash, number) |
||||
if header == nil { |
||||
return nil |
||||
} |
||||
body := ReadBody(db, hash, number) |
||||
if body == nil { |
||||
return nil |
||||
} |
||||
return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles) |
||||
} |
||||
|
||||
// WriteBlock serializes a block into the database, header and body separately.
|
||||
func WriteBlock(db DatabaseWriter, block *types.Block) { |
||||
WriteBody(db, block.Hash(), block.NumberU64(), block.Body()) |
||||
WriteHeader(db, block.Header()) |
||||
} |
||||
|
||||
// DeleteBlock removes all block data associated with a hash.
|
||||
func DeleteBlock(db DatabaseDeleter, hash common.Hash, number uint64) { |
||||
DeleteReceipts(db, hash, number) |
||||
DeleteHeader(db, hash, number) |
||||
DeleteBody(db, hash, number) |
||||
DeleteTd(db, hash, number) |
||||
} |
||||
|
||||
// FindCommonAncestor returns the last common ancestor of two block headers
|
||||
func FindCommonAncestor(db DatabaseReader, a, b *types.Header) *types.Header { |
||||
for bn := b.Number.Uint64(); a.Number.Uint64() > bn; { |
||||
a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1) |
||||
if a == nil { |
||||
return nil |
||||
} |
||||
} |
||||
for an := a.Number.Uint64(); an < b.Number.Uint64(); { |
||||
b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1) |
||||
if b == nil { |
||||
return nil |
||||
} |
||||
} |
||||
for a.Hash() != b.Hash() { |
||||
a = ReadHeader(db, a.ParentHash, a.Number.Uint64()-1) |
||||
if a == nil { |
||||
return nil |
||||
} |
||||
b = ReadHeader(db, b.ParentHash, b.Number.Uint64()-1) |
||||
if b == nil { |
||||
return nil |
||||
} |
||||
} |
||||
return a |
||||
} |
@ -0,0 +1,319 @@ |
||||
// 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 rawdb |
||||
|
||||
import ( |
||||
"bytes" |
||||
"math/big" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/crypto/sha3" |
||||
"github.com/ethereum/go-ethereum/ethdb" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// Tests block header storage and retrieval operations.
|
||||
func TestHeaderStorage(t *testing.T) { |
||||
db := ethdb.NewMemDatabase() |
||||
|
||||
// Create a test header to move around the database and make sure it's really new
|
||||
header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")} |
||||
if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil { |
||||
t.Fatalf("Non existent header returned: %v", entry) |
||||
} |
||||
// Write and verify the header in the database
|
||||
WriteHeader(db, header) |
||||
if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry == nil { |
||||
t.Fatalf("Stored header not found") |
||||
} else if entry.Hash() != header.Hash() { |
||||
t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header) |
||||
} |
||||
if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil { |
||||
t.Fatalf("Stored header RLP not found") |
||||
} else { |
||||
hasher := sha3.NewKeccak256() |
||||
hasher.Write(entry) |
||||
|
||||
if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() { |
||||
t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header) |
||||
} |
||||
} |
||||
// Delete the header and verify the execution
|
||||
DeleteHeader(db, header.Hash(), header.Number.Uint64()) |
||||
if entry := ReadHeader(db, header.Hash(), header.Number.Uint64()); entry != nil { |
||||
t.Fatalf("Deleted header returned: %v", entry) |
||||
} |
||||
} |
||||
|
||||
// Tests block body storage and retrieval operations.
|
||||
func TestBodyStorage(t *testing.T) { |
||||
db := ethdb.NewMemDatabase() |
||||
|
||||
// Create a test body to move around the database and make sure it's really new
|
||||
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}} |
||||
|
||||
hasher := sha3.NewKeccak256() |
||||
rlp.Encode(hasher, body) |
||||
hash := common.BytesToHash(hasher.Sum(nil)) |
||||
|
||||
if entry := ReadBody(db, hash, 0); entry != nil { |
||||
t.Fatalf("Non existent body returned: %v", entry) |
||||
} |
||||
// Write and verify the body in the database
|
||||
WriteBody(db, hash, 0, body) |
||||
if entry := ReadBody(db, hash, 0); entry == nil { |
||||
t.Fatalf("Stored body not found") |
||||
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) { |
||||
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body) |
||||
} |
||||
if entry := ReadBodyRLP(db, hash, 0); entry == nil { |
||||
t.Fatalf("Stored body RLP not found") |
||||
} else { |
||||
hasher := sha3.NewKeccak256() |
||||
hasher.Write(entry) |
||||
|
||||
if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash { |
||||
t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body) |
||||
} |
||||
} |
||||
// Delete the body and verify the execution
|
||||
DeleteBody(db, hash, 0) |
||||
if entry := ReadBody(db, hash, 0); entry != nil { |
||||
t.Fatalf("Deleted body returned: %v", entry) |
||||
} |
||||
} |
||||
|
||||
// Tests block storage and retrieval operations.
|
||||
func TestBlockStorage(t *testing.T) { |
||||
db := ethdb.NewMemDatabase() |
||||
|
||||
// Create a test block to move around the database and make sure it's really new
|
||||
block := types.NewBlockWithHeader(&types.Header{ |
||||
Extra: []byte("test block"), |
||||
UncleHash: types.EmptyUncleHash, |
||||
TxHash: types.EmptyRootHash, |
||||
ReceiptHash: types.EmptyRootHash, |
||||
}) |
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil { |
||||
t.Fatalf("Non existent block returned: %v", entry) |
||||
} |
||||
if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil { |
||||
t.Fatalf("Non existent header returned: %v", entry) |
||||
} |
||||
if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil { |
||||
t.Fatalf("Non existent body returned: %v", entry) |
||||
} |
||||
// Write and verify the block in the database
|
||||
WriteBlock(db, block) |
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil { |
||||
t.Fatalf("Stored block not found") |
||||
} else if entry.Hash() != block.Hash() { |
||||
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block) |
||||
} |
||||
if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry == nil { |
||||
t.Fatalf("Stored header not found") |
||||
} else if entry.Hash() != block.Header().Hash() { |
||||
t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header()) |
||||
} |
||||
if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil { |
||||
t.Fatalf("Stored body not found") |
||||
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) { |
||||
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body()) |
||||
} |
||||
// Delete the block and verify the execution
|
||||
DeleteBlock(db, block.Hash(), block.NumberU64()) |
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil { |
||||
t.Fatalf("Deleted block returned: %v", entry) |
||||
} |
||||
if entry := ReadHeader(db, block.Hash(), block.NumberU64()); entry != nil { |
||||
t.Fatalf("Deleted header returned: %v", entry) |
||||
} |
||||
if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry != nil { |
||||
t.Fatalf("Deleted body returned: %v", entry) |
||||
} |
||||
} |
||||
|
||||
// Tests that partial block contents don't get reassembled into full blocks.
|
||||
func TestPartialBlockStorage(t *testing.T) { |
||||
db := ethdb.NewMemDatabase() |
||||
block := types.NewBlockWithHeader(&types.Header{ |
||||
Extra: []byte("test block"), |
||||
UncleHash: types.EmptyUncleHash, |
||||
TxHash: types.EmptyRootHash, |
||||
ReceiptHash: types.EmptyRootHash, |
||||
}) |
||||
// Store a header and check that it's not recognized as a block
|
||||
WriteHeader(db, block.Header()) |
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil { |
||||
t.Fatalf("Non existent block returned: %v", entry) |
||||
} |
||||
DeleteHeader(db, block.Hash(), block.NumberU64()) |
||||
|
||||
// Store a body and check that it's not recognized as a block
|
||||
WriteBody(db, block.Hash(), block.NumberU64(), block.Body()) |
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry != nil { |
||||
t.Fatalf("Non existent block returned: %v", entry) |
||||
} |
||||
DeleteBody(db, block.Hash(), block.NumberU64()) |
||||
|
||||
// Store a header and a body separately and check reassembly
|
||||
WriteHeader(db, block.Header()) |
||||
WriteBody(db, block.Hash(), block.NumberU64(), block.Body()) |
||||
|
||||
if entry := ReadBlock(db, block.Hash(), block.NumberU64()); entry == nil { |
||||
t.Fatalf("Stored block not found") |
||||
} else if entry.Hash() != block.Hash() { |
||||
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block) |
||||
} |
||||
} |
||||
|
||||
// Tests block total difficulty storage and retrieval operations.
|
||||
func TestTdStorage(t *testing.T) { |
||||
db := ethdb.NewMemDatabase() |
||||
|
||||
// Create a test TD to move around the database and make sure it's really new
|
||||
hash, td := common.Hash{}, big.NewInt(314) |
||||
if entry := ReadTd(db, hash, 0); entry != nil { |
||||
t.Fatalf("Non existent TD returned: %v", entry) |
||||
} |
||||
// Write and verify the TD in the database
|
||||
WriteTd(db, hash, 0, td) |
||||
if entry := ReadTd(db, hash, 0); entry == nil { |
||||
t.Fatalf("Stored TD not found") |
||||
} else if entry.Cmp(td) != 0 { |
||||
t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td) |
||||
} |
||||
// Delete the TD and verify the execution
|
||||
DeleteTd(db, hash, 0) |
||||
if entry := ReadTd(db, hash, 0); entry != nil { |
||||
t.Fatalf("Deleted TD returned: %v", entry) |
||||
} |
||||
} |
||||
|
||||
// Tests that canonical numbers can be mapped to hashes and retrieved.
|
||||
func TestCanonicalMappingStorage(t *testing.T) { |
||||
db := ethdb.NewMemDatabase() |
||||
|
||||
// Create a test canonical number and assinged hash to move around
|
||||
hash, number := common.Hash{0: 0xff}, uint64(314) |
||||
if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) { |
||||
t.Fatalf("Non existent canonical mapping returned: %v", entry) |
||||
} |
||||
// Write and verify the TD in the database
|
||||
WriteCanonicalHash(db, hash, number) |
||||
if entry := ReadCanonicalHash(db, number); entry == (common.Hash{}) { |
||||
t.Fatalf("Stored canonical mapping not found") |
||||
} else if entry != hash { |
||||
t.Fatalf("Retrieved canonical mapping mismatch: have %v, want %v", entry, hash) |
||||
} |
||||
// Delete the TD and verify the execution
|
||||
DeleteCanonicalHash(db, number) |
||||
if entry := ReadCanonicalHash(db, number); entry != (common.Hash{}) { |
||||
t.Fatalf("Deleted canonical mapping returned: %v", entry) |
||||
} |
||||
} |
||||
|
||||
// Tests that head headers and head blocks can be assigned, individually.
|
||||
func TestHeadStorage(t *testing.T) { |
||||
db := ethdb.NewMemDatabase() |
||||
|
||||
blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")}) |
||||
blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")}) |
||||
blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")}) |
||||
|
||||
// Check that no head entries are in a pristine database
|
||||
if entry := ReadHeadHeaderHash(db); entry != (common.Hash{}) { |
||||
t.Fatalf("Non head header entry returned: %v", entry) |
||||
} |
||||
if entry := ReadHeadBlockHash(db); entry != (common.Hash{}) { |
||||
t.Fatalf("Non head block entry returned: %v", entry) |
||||
} |
||||
if entry := ReadHeadFastBlockHash(db); entry != (common.Hash{}) { |
||||
t.Fatalf("Non fast head block entry returned: %v", entry) |
||||
} |
||||
// Assign separate entries for the head header and block
|
||||
WriteHeadHeaderHash(db, blockHead.Hash()) |
||||
WriteHeadBlockHash(db, blockFull.Hash()) |
||||
WriteHeadFastBlockHash(db, blockFast.Hash()) |
||||
|
||||
// Check that both heads are present, and different (i.e. two heads maintained)
|
||||
if entry := ReadHeadHeaderHash(db); entry != blockHead.Hash() { |
||||
t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash()) |
||||
} |
||||
if entry := ReadHeadBlockHash(db); entry != blockFull.Hash() { |
||||
t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash()) |
||||
} |
||||
if entry := ReadHeadFastBlockHash(db); entry != blockFast.Hash() { |
||||
t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash()) |
||||
} |
||||
} |
||||
|
||||
// Tests that receipts associated with a single block can be stored and retrieved.
|
||||
func TestBlockReceiptStorage(t *testing.T) { |
||||
db := ethdb.NewMemDatabase() |
||||
|
||||
receipt1 := &types.Receipt{ |
||||
Status: types.ReceiptStatusFailed, |
||||
CumulativeGasUsed: 1, |
||||
Logs: []*types.Log{ |
||||
{Address: common.BytesToAddress([]byte{0x11})}, |
||||
{Address: common.BytesToAddress([]byte{0x01, 0x11})}, |
||||
}, |
||||
TxHash: common.BytesToHash([]byte{0x11, 0x11}), |
||||
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), |
||||
GasUsed: 111111, |
||||
} |
||||
receipt2 := &types.Receipt{ |
||||
PostState: common.Hash{2}.Bytes(), |
||||
CumulativeGasUsed: 2, |
||||
Logs: []*types.Log{ |
||||
{Address: common.BytesToAddress([]byte{0x22})}, |
||||
{Address: common.BytesToAddress([]byte{0x02, 0x22})}, |
||||
}, |
||||
TxHash: common.BytesToHash([]byte{0x22, 0x22}), |
||||
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), |
||||
GasUsed: 222222, |
||||
} |
||||
receipts := []*types.Receipt{receipt1, receipt2} |
||||
|
||||
// Check that no receipt entries are in a pristine database
|
||||
hash := common.BytesToHash([]byte{0x03, 0x14}) |
||||
if rs := ReadReceipts(db, hash, 0); len(rs) != 0 { |
||||
t.Fatalf("non existent receipts returned: %v", rs) |
||||
} |
||||
// Insert the receipt slice into the database and check presence
|
||||
WriteReceipts(db, hash, 0, receipts) |
||||
if rs := ReadReceipts(db, hash, 0); len(rs) == 0 { |
||||
t.Fatalf("no receipts returned") |
||||
} else { |
||||
for i := 0; i < len(receipts); i++ { |
||||
rlpHave, _ := rlp.EncodeToBytes(rs[i]) |
||||
rlpWant, _ := rlp.EncodeToBytes(receipts[i]) |
||||
|
||||
if !bytes.Equal(rlpHave, rlpWant) { |
||||
t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i]) |
||||
} |
||||
} |
||||
} |
||||
// Delete the receipt slice and check purge
|
||||
DeleteReceipts(db, hash, 0) |
||||
if rs := ReadReceipts(db, hash, 0); len(rs) != 0 { |
||||
t.Fatalf("deleted receipts returned: %v", rs) |
||||
} |
||||
} |
@ -0,0 +1,107 @@ |
||||
// 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 rawdb |
||||
|
||||
import ( |
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/log" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// ReadTxLookupEntry retrieves the positional metadata associated with a transaction
|
||||
// hash to allow retrieving the transaction or receipt by hash.
|
||||
func ReadTxLookupEntry(db DatabaseReader, hash common.Hash) (common.Hash, uint64, uint64) { |
||||
data, _ := db.Get(txLookupKey(hash)) |
||||
if len(data) == 0 { |
||||
return common.Hash{}, 0, 0 |
||||
} |
||||
var entry TxLookupEntry |
||||
if err := rlp.DecodeBytes(data, &entry); err != nil { |
||||
log.Error("Invalid transaction lookup entry RLP", "hash", hash, "err", err) |
||||
return common.Hash{}, 0, 0 |
||||
} |
||||
return entry.BlockHash, entry.BlockIndex, entry.Index |
||||
} |
||||
|
||||
// WriteTxLookupEntries stores a positional metadata for every transaction from
|
||||
// a block, enabling hash based transaction and receipt lookups.
|
||||
func WriteTxLookupEntries(db DatabaseWriter, block *types.Block) { |
||||
for i, tx := range block.Transactions() { |
||||
entry := TxLookupEntry{ |
||||
BlockHash: block.Hash(), |
||||
BlockIndex: block.NumberU64(), |
||||
Index: uint64(i), |
||||
} |
||||
data, err := rlp.EncodeToBytes(entry) |
||||
if err != nil { |
||||
log.Crit("Failed to encode transaction lookup entry", "err", err) |
||||
} |
||||
if err := db.Put(txLookupKey(tx.Hash()), data); err != nil { |
||||
log.Crit("Failed to store transaction lookup entry", "err", err) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// DeleteTxLookupEntry removes all transaction data associated with a hash.
|
||||
func DeleteTxLookupEntry(db DatabaseDeleter, hash common.Hash) { |
||||
db.Delete(txLookupKey(hash)) |
||||
} |
||||
|
||||
// ReadTransaction retrieves a specific transaction from the database, along with
|
||||
// its added positional metadata.
|
||||
func ReadTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { |
||||
blockHash, blockNumber, txIndex := ReadTxLookupEntry(db, hash) |
||||
if blockHash == (common.Hash{}) { |
||||
return nil, common.Hash{}, 0, 0 |
||||
} |
||||
body := ReadBody(db, blockHash, blockNumber) |
||||
if body == nil || len(body.Transactions) <= int(txIndex) { |
||||
log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash, "index", txIndex) |
||||
return nil, common.Hash{}, 0, 0 |
||||
} |
||||
return body.Transactions[txIndex], blockHash, blockNumber, txIndex |
||||
} |
||||
|
||||
// ReadReceipt retrieves a specific transaction receipt from the database, along with
|
||||
// its added positional metadata.
|
||||
func ReadReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) { |
||||
blockHash, blockNumber, receiptIndex := ReadTxLookupEntry(db, hash) |
||||
if blockHash == (common.Hash{}) { |
||||
return nil, common.Hash{}, 0, 0 |
||||
} |
||||
receipts := ReadReceipts(db, blockHash, blockNumber) |
||||
if len(receipts) <= int(receiptIndex) { |
||||
log.Error("Receipt refereced missing", "number", blockNumber, "hash", blockHash, "index", receiptIndex) |
||||
return nil, common.Hash{}, 0, 0 |
||||
} |
||||
return receipts[receiptIndex], blockHash, blockNumber, receiptIndex |
||||
} |
||||
|
||||
// ReadBloomBits retrieves the compressed bloom bit vector belonging to the given
|
||||
// section and bit index from the.
|
||||
func ReadBloomBits(db DatabaseReader, bit uint, section uint64, head common.Hash) ([]byte, error) { |
||||
return db.Get(bloomBitsKey(bit, section, head)) |
||||
} |
||||
|
||||
// WriteBloomBits stores the compressed bloom bits vector belonging to the given
|
||||
// section and bit index.
|
||||
func WriteBloomBits(db DatabaseWriter, bit uint, section uint64, head common.Hash, bits []byte) { |
||||
if err := db.Put(bloomBitsKey(bit, section, head), bits); err != nil { |
||||
log.Crit("Failed to store bloom bits", "err", err) |
||||
} |
||||
} |
@ -0,0 +1,68 @@ |
||||
// 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 rawdb |
||||
|
||||
import ( |
||||
"math/big" |
||||
"testing" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/ethdb" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// Tests that positional lookup metadata can be stored and retrieved.
|
||||
func TestLookupStorage(t *testing.T) { |
||||
db := ethdb.NewMemDatabase() |
||||
|
||||
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) |
||||
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22}) |
||||
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) |
||||
txs := []*types.Transaction{tx1, tx2, tx3} |
||||
|
||||
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil) |
||||
|
||||
// Check that no transactions entries are in a pristine database
|
||||
for i, tx := range txs { |
||||
if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil { |
||||
t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn) |
||||
} |
||||
} |
||||
// Insert all the transactions into the database, and verify contents
|
||||
WriteBlock(db, block) |
||||
WriteTxLookupEntries(db, block) |
||||
|
||||
for i, tx := range txs { |
||||
if txn, hash, number, index := ReadTransaction(db, tx.Hash()); txn == nil { |
||||
t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash()) |
||||
} else { |
||||
if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) { |
||||
t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i) |
||||
} |
||||
if tx.Hash() != txn.Hash() { |
||||
t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx) |
||||
} |
||||
} |
||||
} |
||||
// Delete the transactions and check purge
|
||||
for i, tx := range txs { |
||||
DeleteTxLookupEntry(db, tx.Hash()) |
||||
if txn, _, _, _ := ReadTransaction(db, tx.Hash()); txn != nil { |
||||
t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,90 @@ |
||||
// 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 rawdb |
||||
|
||||
import ( |
||||
"encoding/json" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/log" |
||||
"github.com/ethereum/go-ethereum/params" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
) |
||||
|
||||
// ReadDatabaseVersion retrieves the version number of the database.
|
||||
func ReadDatabaseVersion(db DatabaseReader) int { |
||||
var version int |
||||
|
||||
enc, _ := db.Get(databaseVerisionKey) |
||||
rlp.DecodeBytes(enc, &version) |
||||
|
||||
return version |
||||
} |
||||
|
||||
// WriteDatabaseVersion stores the version number of the database
|
||||
func WriteDatabaseVersion(db DatabaseWriter, version int) { |
||||
enc, _ := rlp.EncodeToBytes(version) |
||||
if err := db.Put(databaseVerisionKey, enc); err != nil { |
||||
log.Crit("Failed to store the database version", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadChainConfig retrieves the consensus settings based on the given genesis hash.
|
||||
func ReadChainConfig(db DatabaseReader, hash common.Hash) *params.ChainConfig { |
||||
data, _ := db.Get(configKey(hash)) |
||||
if len(data) == 0 { |
||||
return nil |
||||
} |
||||
var config params.ChainConfig |
||||
if err := json.Unmarshal(data, &config); err != nil { |
||||
log.Error("Invalid chain config JSON", "hash", hash, "err", err) |
||||
return nil |
||||
} |
||||
return &config |
||||
} |
||||
|
||||
// WriteChainConfig writes the chain config settings to the database.
|
||||
func WriteChainConfig(db DatabaseWriter, hash common.Hash, cfg *params.ChainConfig) { |
||||
if cfg == nil { |
||||
return |
||||
} |
||||
data, err := json.Marshal(cfg) |
||||
if err != nil { |
||||
log.Crit("Failed to JSON encode chain config", "err", err) |
||||
} |
||||
if err := db.Put(configKey(hash), data); err != nil { |
||||
log.Crit("Failed to store chain config", "err", err) |
||||
} |
||||
} |
||||
|
||||
// ReadPreimage retrieves a single preimage of the provided hash.
|
||||
func ReadPreimage(db DatabaseReader, hash common.Hash) []byte { |
||||
data, _ := db.Get(preimageKey(hash)) |
||||
return data |
||||
} |
||||
|
||||
// WritePreimages writes the provided set of preimages to the database. `number` is the
|
||||
// current block number, and is used for debug messages only.
|
||||
func WritePreimages(db DatabaseWriter, number uint64, preimages map[common.Hash][]byte) { |
||||
for hash, preimage := range preimages { |
||||
if err := db.Put(preimageKey(hash), preimage); err != nil { |
||||
log.Crit("Failed to store trie preimage", "err", err) |
||||
} |
||||
} |
||||
preimageCounter.Inc(int64(len(preimages))) |
||||
preimageHitCounter.Inc(int64(len(preimages))) |
||||
} |
@ -0,0 +1,33 @@ |
||||
// 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 rawdb |
||||
|
||||
// DatabaseReader wraps the Has and Get method of a backing data store.
|
||||
type DatabaseReader interface { |
||||
Has(key []byte) (bool, error) |
||||
Get(key []byte) ([]byte, error) |
||||
} |
||||
|
||||
// DatabaseWriter wraps the Put method of a backing data store.
|
||||
type DatabaseWriter interface { |
||||
Put(key []byte, value []byte) error |
||||
} |
||||
|
||||
// DatabaseDeleter wraps the Delete method of a backing data store.
|
||||
type DatabaseDeleter interface { |
||||
Delete(key []byte) error |
||||
} |
@ -0,0 +1,134 @@ |
||||
// 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 rawdb contains a collection of low level database accessors.
|
||||
package rawdb |
||||
|
||||
import ( |
||||
"encoding/binary" |
||||
|
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/metrics" |
||||
) |
||||
|
||||
// The fields below define the low level database schema prefixing.
|
||||
var ( |
||||
// databaseVerisionKey tracks the current database version.
|
||||
databaseVerisionKey = []byte("DatabaseVersion") |
||||
|
||||
// headHeaderKey tracks the latest know header's hash.
|
||||
headHeaderKey = []byte("LastHeader") |
||||
|
||||
// headBlockKey tracks the latest know full block's hash.
|
||||
headBlockKey = []byte("LastBlock") |
||||
|
||||
// headFastBlockKey tracks the latest known incomplete block's hash duirng fast sync.
|
||||
headFastBlockKey = []byte("LastFast") |
||||
|
||||
// fastTrieProgressKey tracks the number of trie entries imported during fast sync.
|
||||
fastTrieProgressKey = []byte("TrieSync") |
||||
|
||||
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
||||
headerPrefix = []byte("h") // headerPrefix + num (uint64 big endian) + hash -> header
|
||||
headerTDSuffix = []byte("t") // headerPrefix + num (uint64 big endian) + hash + headerTDSuffix -> td
|
||||
headerHashSuffix = []byte("n") // headerPrefix + num (uint64 big endian) + headerHashSuffix -> hash
|
||||
headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
|
||||
|
||||
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
|
||||
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
|
||||
|
||||
txLookupPrefix = []byte("l") // txLookupPrefix + hash -> transaction/receipt lookup metadata
|
||||
bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
|
||||
|
||||
preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
|
||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||
|
||||
// Chain index prefixes (use `i` + single byte to avoid mixing data types).
|
||||
BloomBitsIndexPrefix = []byte("iB") // BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
|
||||
|
||||
preimageCounter = metrics.NewRegisteredCounter("db/preimage/total", nil) |
||||
preimageHitCounter = metrics.NewRegisteredCounter("db/preimage/hits", nil) |
||||
) |
||||
|
||||
// TxLookupEntry is a positional metadata to help looking up the data content of
|
||||
// a transaction or receipt given only its hash.
|
||||
type TxLookupEntry struct { |
||||
BlockHash common.Hash |
||||
BlockIndex uint64 |
||||
Index uint64 |
||||
} |
||||
|
||||
// encodeBlockNumber encodes a block number as big endian uint64
|
||||
func encodeBlockNumber(number uint64) []byte { |
||||
enc := make([]byte, 8) |
||||
binary.BigEndian.PutUint64(enc, number) |
||||
return enc |
||||
} |
||||
|
||||
// headerKey = headerPrefix + num (uint64 big endian) + hash
|
||||
func headerKey(number uint64, hash common.Hash) []byte { |
||||
return append(append(headerPrefix, encodeBlockNumber(number)...), hash.Bytes()...) |
||||
} |
||||
|
||||
// headerTDKey = headerPrefix + num (uint64 big endian) + hash + headerTDSuffix
|
||||
func headerTDKey(number uint64, hash common.Hash) []byte { |
||||
return append(headerKey(number, hash), headerTDSuffix...) |
||||
} |
||||
|
||||
// headerHashKey = headerPrefix + num (uint64 big endian) + headerHashSuffix
|
||||
func headerHashKey(number uint64) []byte { |
||||
return append(append(headerPrefix, encodeBlockNumber(number)...), headerHashSuffix...) |
||||
} |
||||
|
||||
// headerNumberKey = headerNumberPrefix + hash
|
||||
func headerNumberKey(hash common.Hash) []byte { |
||||
return append(headerNumberPrefix, hash.Bytes()...) |
||||
} |
||||
|
||||
// blockBodyKey = blockBodyPrefix + num (uint64 big endian) + hash
|
||||
func blockBodyKey(number uint64, hash common.Hash) []byte { |
||||
return append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...) |
||||
} |
||||
|
||||
// blockReceiptsKey = blockReceiptsPrefix + num (uint64 big endian) + hash
|
||||
func blockReceiptsKey(number uint64, hash common.Hash) []byte { |
||||
return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...) |
||||
} |
||||
|
||||
// txLookupKey = txLookupPrefix + hash
|
||||
func txLookupKey(hash common.Hash) []byte { |
||||
return append(txLookupPrefix, hash.Bytes()...) |
||||
} |
||||
|
||||
// bloomBitsKey = bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash
|
||||
func bloomBitsKey(bit uint, section uint64, hash common.Hash) []byte { |
||||
key := append(append(bloomBitsPrefix, make([]byte, 10)...), hash.Bytes()...) |
||||
|
||||
binary.BigEndian.PutUint16(key[1:], uint16(bit)) |
||||
binary.BigEndian.PutUint64(key[3:], section) |
||||
|
||||
return key |
||||
} |
||||
|
||||
// preimageKey = preimagePrefix + hash
|
||||
func preimageKey(hash common.Hash) []byte { |
||||
return append(preimagePrefix, hash.Bytes()...) |
||||
} |
||||
|
||||
// configKey = configPrefix + hash
|
||||
func configKey(hash common.Hash) []byte { |
||||
return append(configPrefix, hash.Bytes()...) |
||||
} |
@ -0,0 +1,126 @@ |
||||
// 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 ( |
||||
"github.com/ethereum/go-ethereum/common" |
||||
"github.com/ethereum/go-ethereum/consensus/misc" |
||||
"github.com/ethereum/go-ethereum/core/state" |
||||
"github.com/ethereum/go-ethereum/core/vm" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/params" |
||||
"github.com/simple-rules/harmony-benchmark/consensus" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// StateProcessor is a basic Processor, which takes care of transitioning
|
||||
// state from one point to another.
|
||||
//
|
||||
// StateProcessor implements Processor.
|
||||
type StateProcessor struct { |
||||
config *params.ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
engine consensus.Engine // Consensus engine used for block rewards
|
||||
} |
||||
|
||||
// NewStateProcessor initialises a new StateProcessor.
|
||||
func NewStateProcessor(config *params.ChainConfig, bc *BlockChain, engine consensus.Engine) *StateProcessor { |
||||
return &StateProcessor{ |
||||
config: config, |
||||
bc: bc, |
||||
engine: engine, |
||||
} |
||||
} |
||||
|
||||
// Process processes the state changes according to the Ethereum rules by running
|
||||
// the transaction messages using the statedb and applying any rewards to both
|
||||
// the processor (coinbase) and any included uncles.
|
||||
//
|
||||
// Process returns the receipts and logs accumulated during the process and
|
||||
// returns the amount of gas that was used in the process. If any of the
|
||||
// transactions failed to execute due to insufficient gas it will return an error.
|
||||
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) { |
||||
var ( |
||||
receipts types.Receipts |
||||
usedGas = new(uint64) |
||||
header = block.Header() |
||||
allLogs []*types.Log |
||||
gp = new(GasPool).AddGas(block.GasLimit()) |
||||
) |
||||
// Mutate the block and state according to any hard-fork specs
|
||||
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 { |
||||
misc.ApplyDAOHardFork(statedb) |
||||
} |
||||
// Iterate over and process the individual transactions
|
||||
for i, tx := range block.Transactions() { |
||||
statedb.Prepare(tx.Hash(), block.Hash(), i) |
||||
receipt, _, err := ApplyTransaction(p.config, p.bc, nil, gp, statedb, header, tx, usedGas, cfg) |
||||
if err != nil { |
||||
return nil, nil, 0, err |
||||
} |
||||
receipts = append(receipts, receipt) |
||||
allLogs = append(allLogs, receipt.Logs...) |
||||
} |
||||
// Finalize the block, applying any consensus engine specific extras (e.g. block rewards)
|
||||
p.engine.Finalize(p.bc, header, statedb, block.Transactions(), block.Uncles(), receipts) |
||||
|
||||
return receipts, allLogs, *usedGas, nil |
||||
} |
||||
|
||||
// ApplyTransaction attempts to apply a transaction to the given state database
|
||||
// and uses the input parameters for its environment. It returns the receipt
|
||||
// for the transaction, gas used and an error if the transaction failed,
|
||||
// indicating the block was invalid.
|
||||
func ApplyTransaction(config *params.ChainConfig, bc ChainContext, author *common.Address, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64, cfg vm.Config) (*types.Receipt, uint64, error) { |
||||
msg, err := tx.AsMessage(types.MakeSigner(config, header.Number)) |
||||
if err != nil { |
||||
return nil, 0, err |
||||
} |
||||
// Create a new context to be used in the EVM environment
|
||||
context := NewEVMContext(msg, header, bc, author) |
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := vm.NewEVM(context, statedb, config, cfg) |
||||
// Apply the transaction to the current state (included in the env)
|
||||
_, gas, failed, err := ApplyMessage(vmenv, msg, gp) |
||||
if err != nil { |
||||
return nil, 0, err |
||||
} |
||||
// Update the state with pending changes
|
||||
var root []byte |
||||
if config.IsByzantium(header.Number) { |
||||
statedb.Finalise(true) |
||||
} else { |
||||
root = statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes() |
||||
} |
||||
*usedGas += gas |
||||
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||
// based on the eip phase, we're passing whether the root touch-delete accounts.
|
||||
receipt := types.NewReceipt(root, failed, *usedGas) |
||||
receipt.TxHash = tx.Hash() |
||||
receipt.GasUsed = gas |
||||
// if the transaction created a contract, store the creation address in the receipt.
|
||||
if msg.To() == nil { |
||||
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce()) |
||||
} |
||||
// Set the receipt logs and create a bloom for filtering
|
||||
//receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) |
||||
|
||||
return receipt, gas, err |
||||
} |
@ -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/log" |
||||
"github.com/ethereum/go-ethereum/rlp" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// 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/log" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// 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/crypto" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// 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,46 @@ |
||||
// 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 ( |
||||
"github.com/ethereum/go-ethereum/core/state" |
||||
"github.com/ethereum/go-ethereum/core/vm" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
) |
||||
|
||||
// Validator is an interface which defines the standard for block validation. It
|
||||
// is only responsible for validating block contents, as the header validation is
|
||||
// done by the specific consensus engines.
|
||||
//
|
||||
type Validator interface { |
||||
// ValidateBody validates the given block's content.
|
||||
ValidateBody(block *types.Block) error |
||||
|
||||
// ValidateState validates the given statedb and optionally the receipts and
|
||||
// gas used.
|
||||
ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas uint64) error |
||||
} |
||||
|
||||
// Processor is an interface for processing blocks using a given initial state.
|
||||
//
|
||||
// Process takes the block to be processed and the statedb upon which the
|
||||
// initial state is based. It should return the receipts generated, amount
|
||||
// of gas used in the process and return an error if any of the internal rules
|
||||
// failed.
|
||||
type Processor interface { |
||||
Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) |
||||
} |
@ -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,115 @@ |
||||
// 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 ( |
||||
"fmt" |
||||
"testing" |
||||
|
||||
"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 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()) |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,56 @@ |
||||
package harmony |
||||
|
||||
import ( |
||||
"github.com/ethereum/go-ethereum/core/vm" |
||||
"github.com/ethereum/go-ethereum/crypto" |
||||
"github.com/ethereum/go-ethereum/ethdb" |
||||
"github.com/ethereum/go-ethereum/params" |
||||
"github.com/simple-rules/harmony-benchmark/core" |
||||
"github.com/simple-rules/harmony-benchmark/core/types" |
||||
"math/big" |
||||
) |
||||
|
||||
var ( |
||||
|
||||
// Test accounts
|
||||
testBankKey, _ = crypto.GenerateKey() |
||||
testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) |
||||
testBankFunds = big.NewInt(1000000000000000000) |
||||
|
||||
testUserKey, _ = crypto.GenerateKey() |
||||
testUserAddress = crypto.PubkeyToAddress(testUserKey.PublicKey) |
||||
|
||||
chainConfig = params.TestChainConfig |
||||
|
||||
// Test transactions
|
||||
pendingTxs []*types.Transaction |
||||
newTxs []*types.Transaction |
||||
) |
||||
|
||||
type testWorkerBackend struct { |
||||
db ethdb.Database |
||||
txPool *core.TxPool |
||||
chain *core.BlockChain |
||||
} |
||||
|
||||
func main() { |
||||
|
||||
var ( |
||||
database = ethdb.NewMemDatabase() |
||||
gspec = core.Genesis{ |
||||
Config: chainConfig, |
||||
Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, |
||||
} |
||||
) |
||||
|
||||
chain, _ := core.NewBlockChain(database, nil, gspec.Config, nil, vm.Config{}, nil) |
||||
|
||||
txpool := core.NewTxPool(core.DefaultTxPoolConfig, chainConfig, chain) |
||||
|
||||
backend := &testWorkerBackend{ |
||||
db: database, |
||||
chain: chain, |
||||
txPool: txpool, |
||||
} |
||||
backend.txPool.AddLocals(pendingTxs) |
||||
} |
Loading…
Reference in new issue