mirror of https://github.com/ethereum/go-ethereum
commit
5490437942
@ -1,41 +0,0 @@ |
|||||||
// Copyright 2015 The go-ethereum Authors
|
|
||||||
// This file is part of go-ethereum.
|
|
||||||
//
|
|
||||||
// go-ethereum is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// (at your option) any later version.
|
|
||||||
//
|
|
||||||
// go-ethereum 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 General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package utils |
|
||||||
|
|
||||||
const ( |
|
||||||
legalese = ` |
|
||||||
======================================= |
|
||||||
Disclaimer of Liabilites and Warranties |
|
||||||
======================================= |
|
||||||
|
|
||||||
THE USER EXPRESSLY KNOWS AND AGREES THAT THE USER IS USING THE ETHEREUM PLATFORM AT THE USER’S SOLE |
|
||||||
RISK. THE USER REPRESENTS THAT THE USER HAS AN ADEQUATE UNDERSTANDING OF THE RISKS, USAGE AND |
|
||||||
INTRICACIES OF CRYPTOGRAPHIC TOKENS AND BLOCKCHAIN-BASED OPEN SOURCE SOFTWARE, ETH PLATFORM AND ETH. |
|
||||||
THE USER ACKNOWLEDGES AND AGREES THAT, TO THE FULLEST EXTENT PERMITTED BY ANY APPLICABLE LAW, THE |
|
||||||
DISCLAIMERS OF LIABILITY CONTAINED HEREIN APPLY TO ANY AND ALL DAMAGES OR INJURY WHATSOEVER CAUSED |
|
||||||
BY OR RELATED TO RISKS OF, USE OF, OR INABILITY TO USE, ETH OR THE ETHEREUM PLATFORM UNDER ANY CAUSE |
|
||||||
OR ACTION WHATSOEVER OF ANY KIND IN ANY JURISDICTION, INCLUDING, WITHOUT LIMITATION, ACTIONS FOR |
|
||||||
BREACH OF WARRANTY, BREACH OF CONTRACT OR TORT (INCLUDING NEGLIGENCE) AND THAT NEITHER STIFTUNG |
|
||||||
ETHEREUM NOR ETHEREUM TEAM SHALL BE LIABLE FOR ANY INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR |
|
||||||
CONSEQUENTIAL DAMAGES, INCLUDING FOR LOSS OF PROFITS, GOODWILL OR DATA. SOME JURISDICTIONS DO NOT |
|
||||||
ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR CERTAIN |
|
||||||
TYPES OF DAMAGES. THEREFORE, SOME OF THE ABOVE LIMITATIONS IN THIS SECTION MAY NOT APPLY TO A USER. |
|
||||||
IN PARTICULAR, NOTHING IN THESE TERMS SHALL AFFECT THE STATUTORY RIGHTS OF ANY USER OR EXCLUDE |
|
||||||
INJURY ARISING FROM ANY WILLFUL MISCONDUCT OR FRAUD OF STIFTUNG ETHEREUM. |
|
||||||
|
|
||||||
Do you accept this agreement?` |
|
||||||
) |
|
@ -1,460 +0,0 @@ |
|||||||
// 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 ( |
|
||||||
"fmt" |
|
||||||
"math/big" |
|
||||||
"sync" |
|
||||||
"time" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/core/state" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/core/vm" |
|
||||||
"github.com/ethereum/go-ethereum/crypto" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
"github.com/ethereum/go-ethereum/event" |
|
||||||
"github.com/ethereum/go-ethereum/logger" |
|
||||||
"github.com/ethereum/go-ethereum/logger/glog" |
|
||||||
"github.com/ethereum/go-ethereum/params" |
|
||||||
"github.com/ethereum/go-ethereum/pow" |
|
||||||
"gopkg.in/fatih/set.v0" |
|
||||||
) |
|
||||||
|
|
||||||
const ( |
|
||||||
// must be bumped when consensus algorithm is changed, this forces the upgradedb
|
|
||||||
// command to be run (forces the blocks to be imported again using the new algorithm)
|
|
||||||
BlockChainVersion = 3 |
|
||||||
) |
|
||||||
|
|
||||||
type BlockProcessor struct { |
|
||||||
chainDb ethdb.Database |
|
||||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
|
||||||
mutex sync.Mutex |
|
||||||
// Canonical block chain
|
|
||||||
bc *BlockChain |
|
||||||
// non-persistent key/value memory storage
|
|
||||||
mem map[string]*big.Int |
|
||||||
// Proof of work used for validating
|
|
||||||
Pow pow.PoW |
|
||||||
|
|
||||||
events event.Subscription |
|
||||||
|
|
||||||
eventMux *event.TypeMux |
|
||||||
} |
|
||||||
|
|
||||||
// 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 big.Int |
|
||||||
|
|
||||||
// AddGas makes gas available for execution.
|
|
||||||
func (gp *GasPool) AddGas(amount *big.Int) *GasPool { |
|
||||||
i := (*big.Int)(gp) |
|
||||||
i.Add(i, 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 *big.Int) error { |
|
||||||
i := (*big.Int)(gp) |
|
||||||
if i.Cmp(amount) < 0 { |
|
||||||
return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount} |
|
||||||
} |
|
||||||
i.Sub(i, amount) |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (gp *GasPool) String() string { |
|
||||||
return (*big.Int)(gp).String() |
|
||||||
} |
|
||||||
|
|
||||||
func NewBlockProcessor(db ethdb.Database, pow pow.PoW, blockchain *BlockChain, eventMux *event.TypeMux) *BlockProcessor { |
|
||||||
sm := &BlockProcessor{ |
|
||||||
chainDb: db, |
|
||||||
mem: make(map[string]*big.Int), |
|
||||||
Pow: pow, |
|
||||||
bc: blockchain, |
|
||||||
eventMux: eventMux, |
|
||||||
} |
|
||||||
return sm |
|
||||||
} |
|
||||||
|
|
||||||
func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) { |
|
||||||
gp := new(GasPool).AddGas(block.GasLimit()) |
|
||||||
if glog.V(logger.Core) { |
|
||||||
glog.Infof("%x: gas (+ %v)", block.Coinbase(), gp) |
|
||||||
} |
|
||||||
|
|
||||||
// Process the transactions on to parent state
|
|
||||||
receipts, err = sm.ApplyTransactions(gp, statedb, block, block.Transactions(), transientProcess) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
return receipts, nil |
|
||||||
} |
|
||||||
|
|
||||||
func (self *BlockProcessor) ApplyTransaction(gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) { |
|
||||||
_, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, gp) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
|
|
||||||
// Update the state with pending changes
|
|
||||||
usedGas.Add(usedGas, gas) |
|
||||||
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) |
|
||||||
receipt.TxHash = tx.Hash() |
|
||||||
receipt.GasUsed = new(big.Int).Set(gas) |
|
||||||
if MessageCreatesContract(tx) { |
|
||||||
from, _ := tx.From() |
|
||||||
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) |
|
||||||
} |
|
||||||
|
|
||||||
logs := statedb.GetLogs(tx.Hash()) |
|
||||||
receipt.Logs = logs |
|
||||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) |
|
||||||
|
|
||||||
glog.V(logger.Debug).Infoln(receipt) |
|
||||||
|
|
||||||
// Notify all subscribers
|
|
||||||
if !transientProcess { |
|
||||||
go self.eventMux.Post(TxPostEvent{tx}) |
|
||||||
go self.eventMux.Post(logs) |
|
||||||
} |
|
||||||
|
|
||||||
return receipt, gas, err |
|
||||||
} |
|
||||||
func (self *BlockProcessor) BlockChain() *BlockChain { |
|
||||||
return self.bc |
|
||||||
} |
|
||||||
|
|
||||||
func (self *BlockProcessor) ApplyTransactions(gp *GasPool, statedb *state.StateDB, block *types.Block, txs types.Transactions, transientProcess bool) (types.Receipts, error) { |
|
||||||
var ( |
|
||||||
receipts types.Receipts |
|
||||||
totalUsedGas = big.NewInt(0) |
|
||||||
err error |
|
||||||
cumulativeSum = new(big.Int) |
|
||||||
header = block.Header() |
|
||||||
) |
|
||||||
|
|
||||||
for i, tx := range txs { |
|
||||||
statedb.StartRecord(tx.Hash(), block.Hash(), i) |
|
||||||
|
|
||||||
receipt, txGas, err := self.ApplyTransaction(gp, statedb, header, tx, totalUsedGas, transientProcess) |
|
||||||
if err != nil { |
|
||||||
return nil, err |
|
||||||
} |
|
||||||
|
|
||||||
if err != nil { |
|
||||||
glog.V(logger.Core).Infoln("TX err:", err) |
|
||||||
} |
|
||||||
receipts = append(receipts, receipt) |
|
||||||
|
|
||||||
cumulativeSum.Add(cumulativeSum, new(big.Int).Mul(txGas, tx.GasPrice())) |
|
||||||
} |
|
||||||
|
|
||||||
if block.GasUsed().Cmp(totalUsedGas) != 0 { |
|
||||||
return nil, ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), totalUsedGas)) |
|
||||||
} |
|
||||||
|
|
||||||
if transientProcess { |
|
||||||
go self.eventMux.Post(PendingBlockEvent{block, statedb.Logs()}) |
|
||||||
} |
|
||||||
|
|
||||||
return receipts, err |
|
||||||
} |
|
||||||
|
|
||||||
func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs vm.Logs, err error) { |
|
||||||
// Processing a blocks may never happen simultaneously
|
|
||||||
sm.mutex.Lock() |
|
||||||
defer sm.mutex.Unlock() |
|
||||||
|
|
||||||
if !sm.bc.HasBlock(block.ParentHash()) { |
|
||||||
return nil, ParentError(block.ParentHash()) |
|
||||||
} |
|
||||||
parent := sm.bc.GetBlock(block.ParentHash()) |
|
||||||
|
|
||||||
// FIXME Change to full header validation. See #1225
|
|
||||||
errch := make(chan bool) |
|
||||||
go func() { errch <- sm.Pow.Verify(block) }() |
|
||||||
|
|
||||||
logs, _, err = sm.processWithParent(block, parent) |
|
||||||
if !<-errch { |
|
||||||
return nil, ValidationError("Block's nonce is invalid (= %x)", block.Nonce) |
|
||||||
} |
|
||||||
|
|
||||||
return logs, err |
|
||||||
} |
|
||||||
|
|
||||||
// Process block will attempt to process the given block's transactions and applies them
|
|
||||||
// on top of the block's parent state (given it exists) and will return wether it was
|
|
||||||
// successful or not.
|
|
||||||
func (sm *BlockProcessor) Process(block *types.Block) (logs vm.Logs, receipts types.Receipts, err error) { |
|
||||||
// Processing a blocks may never happen simultaneously
|
|
||||||
sm.mutex.Lock() |
|
||||||
defer sm.mutex.Unlock() |
|
||||||
|
|
||||||
if sm.bc.HasBlock(block.Hash()) { |
|
||||||
if _, err := state.New(block.Root(), sm.chainDb); err == nil { |
|
||||||
return nil, nil, &KnownBlockError{block.Number(), block.Hash()} |
|
||||||
} |
|
||||||
} |
|
||||||
if parent := sm.bc.GetBlock(block.ParentHash()); parent != nil { |
|
||||||
if _, err := state.New(parent.Root(), sm.chainDb); err == nil { |
|
||||||
return sm.processWithParent(block, parent) |
|
||||||
} |
|
||||||
} |
|
||||||
return nil, nil, ParentError(block.ParentHash()) |
|
||||||
} |
|
||||||
|
|
||||||
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs vm.Logs, receipts types.Receipts, err error) { |
|
||||||
// Create a new state based on the parent's root (e.g., create copy)
|
|
||||||
state, err := state.New(parent.Root(), sm.chainDb) |
|
||||||
if err != nil { |
|
||||||
return nil, nil, err |
|
||||||
} |
|
||||||
header := block.Header() |
|
||||||
uncles := block.Uncles() |
|
||||||
txs := block.Transactions() |
|
||||||
|
|
||||||
// Block validation
|
|
||||||
if err = ValidateHeader(sm.Pow, header, parent.Header(), false, false); err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// There can be at most two uncles
|
|
||||||
if len(uncles) > 2 { |
|
||||||
return nil, nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(uncles)) |
|
||||||
} |
|
||||||
|
|
||||||
receipts, err = sm.TransitionState(state, parent, block, false) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// 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 { |
|
||||||
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
|
|
||||||
// can be used by light clients to make sure they've received the correct Txs
|
|
||||||
txSha := types.DeriveSha(txs) |
|
||||||
if txSha != header.TxHash { |
|
||||||
err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
|
|
||||||
receiptSha := types.DeriveSha(receipts) |
|
||||||
if receiptSha != header.ReceiptHash { |
|
||||||
err = fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Verify UncleHash before running other uncle validations
|
|
||||||
unclesSha := types.CalcUncleHash(uncles) |
|
||||||
if unclesSha != header.UncleHash { |
|
||||||
err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha) |
|
||||||
return |
|
||||||
} |
|
||||||
|
|
||||||
// Verify uncles
|
|
||||||
if err = sm.VerifyUncles(state, block, parent); err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
// Accumulate static rewards; block reward, uncle's and uncle inclusion.
|
|
||||||
AccumulateRewards(state, header, uncles) |
|
||||||
|
|
||||||
// Commit state objects/accounts to a database batch and calculate
|
|
||||||
// the state root. The database is not modified if the root
|
|
||||||
// doesn't match.
|
|
||||||
root, batch := state.CommitBatch() |
|
||||||
if header.Root != root { |
|
||||||
return nil, nil, fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root) |
|
||||||
} |
|
||||||
|
|
||||||
// Execute the database writes.
|
|
||||||
batch.Write() |
|
||||||
|
|
||||||
return state.Logs(), receipts, nil |
|
||||||
} |
|
||||||
|
|
||||||
var ( |
|
||||||
big8 = big.NewInt(8) |
|
||||||
big32 = big.NewInt(32) |
|
||||||
) |
|
||||||
|
|
||||||
// AccumulateRewards credits the coinbase of the given block with the
|
|
||||||
// mining reward. The total reward consists of the static block reward
|
|
||||||
// and rewards for included uncles. The coinbase of each uncle block is
|
|
||||||
// also rewarded.
|
|
||||||
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) { |
|
||||||
reward := new(big.Int).Set(BlockReward) |
|
||||||
r := new(big.Int) |
|
||||||
for _, uncle := range uncles { |
|
||||||
r.Add(uncle.Number, big8) |
|
||||||
r.Sub(r, header.Number) |
|
||||||
r.Mul(r, BlockReward) |
|
||||||
r.Div(r, big8) |
|
||||||
statedb.AddBalance(uncle.Coinbase, r) |
|
||||||
|
|
||||||
r.Div(BlockReward, big32) |
|
||||||
reward.Add(reward, r) |
|
||||||
} |
|
||||||
statedb.AddBalance(header.Coinbase, reward) |
|
||||||
} |
|
||||||
|
|
||||||
func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *types.Block) error { |
|
||||||
uncles := set.New() |
|
||||||
ancestors := make(map[common.Hash]*types.Block) |
|
||||||
for _, ancestor := range sm.bc.GetBlocksFromHash(block.ParentHash(), 7) { |
|
||||||
ancestors[ancestor.Hash()] = ancestor |
|
||||||
// Include ancestors uncles in the uncle set. Uncles must be unique.
|
|
||||||
for _, uncle := range ancestor.Uncles() { |
|
||||||
uncles.Add(uncle.Hash()) |
|
||||||
} |
|
||||||
} |
|
||||||
ancestors[block.Hash()] = block |
|
||||||
uncles.Add(block.Hash()) |
|
||||||
|
|
||||||
for i, uncle := range block.Uncles() { |
|
||||||
hash := uncle.Hash() |
|
||||||
if uncles.Has(hash) { |
|
||||||
// Error not unique
|
|
||||||
return UncleError("uncle[%d](%x) not unique", i, hash[:4]) |
|
||||||
} |
|
||||||
uncles.Add(hash) |
|
||||||
|
|
||||||
if ancestors[hash] != nil { |
|
||||||
branch := fmt.Sprintf(" O - %x\n |\n", block.Hash()) |
|
||||||
for h := range ancestors { |
|
||||||
branch += fmt.Sprintf(" O - %x\n |\n", h) |
|
||||||
} |
|
||||||
glog.Infoln(branch) |
|
||||||
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4]) |
|
||||||
} |
|
||||||
|
|
||||||
if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() { |
|
||||||
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) |
|
||||||
} |
|
||||||
|
|
||||||
if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil { |
|
||||||
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetBlockReceipts returns the receipts beloniging to the block hash
|
|
||||||
func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) types.Receipts { |
|
||||||
if block := sm.BlockChain().GetBlock(bhash); block != nil { |
|
||||||
return GetBlockReceipts(sm.chainDb, block.Hash()) |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// GetLogs returns the logs of the given block. This method is using a two step approach
|
|
||||||
// where it tries to get it from the (updated) method which gets them from the receipts or
|
|
||||||
// the depricated way by re-processing the block.
|
|
||||||
func (sm *BlockProcessor) GetLogs(block *types.Block) (logs vm.Logs, err error) { |
|
||||||
receipts := GetBlockReceipts(sm.chainDb, block.Hash()) |
|
||||||
// coalesce logs
|
|
||||||
for _, receipt := range receipts { |
|
||||||
logs = append(logs, receipt.Logs...) |
|
||||||
} |
|
||||||
return logs, nil |
|
||||||
} |
|
||||||
|
|
||||||
// ValidateHeader verifies the validity of a header, relying on the database and
|
|
||||||
// POW behind the block processor.
|
|
||||||
func (sm *BlockProcessor) ValidateHeader(header *types.Header, checkPow, uncle bool) error { |
|
||||||
// Short circuit if the header's already known or its parent missing
|
|
||||||
if sm.bc.HasHeader(header.Hash()) { |
|
||||||
return nil |
|
||||||
} |
|
||||||
if parent := sm.bc.GetHeader(header.ParentHash); parent == nil { |
|
||||||
return ParentError(header.ParentHash) |
|
||||||
} else { |
|
||||||
return ValidateHeader(sm.Pow, header, parent, checkPow, uncle) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// ValidateHeaderWithParent verifies the validity of a header, relying on the database and
|
|
||||||
// POW behind the block processor.
|
|
||||||
func (sm *BlockProcessor) ValidateHeaderWithParent(header, parent *types.Header, checkPow, uncle bool) error { |
|
||||||
if sm.bc.HasHeader(header.Hash()) { |
|
||||||
return nil |
|
||||||
} |
|
||||||
return ValidateHeader(sm.Pow, header, parent, checkPow, uncle) |
|
||||||
} |
|
||||||
|
|
||||||
// See YP section 4.3.4. "Block Header Validity"
|
|
||||||
// Validates a header. Returns an error if the header is invalid.
|
|
||||||
func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { |
|
||||||
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { |
|
||||||
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) |
|
||||||
} |
|
||||||
if uncle { |
|
||||||
if header.Time.Cmp(common.MaxBig) == 1 { |
|
||||||
return BlockTSTooBigErr |
|
||||||
} |
|
||||||
} else { |
|
||||||
if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 { |
|
||||||
return BlockFutureErr |
|
||||||
} |
|
||||||
} |
|
||||||
if header.Time.Cmp(parent.Time) != 1 { |
|
||||||
return BlockEqualTSErr |
|
||||||
} |
|
||||||
|
|
||||||
expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) |
|
||||||
if expd.Cmp(header.Difficulty) != 0 { |
|
||||||
return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd) |
|
||||||
} |
|
||||||
|
|
||||||
a := new(big.Int).Set(parent.GasLimit) |
|
||||||
a = a.Sub(a, header.GasLimit) |
|
||||||
a.Abs(a) |
|
||||||
b := new(big.Int).Set(parent.GasLimit) |
|
||||||
b = b.Div(b, params.GasLimitBoundDivisor) |
|
||||||
if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) { |
|
||||||
return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b) |
|
||||||
} |
|
||||||
|
|
||||||
num := new(big.Int).Set(parent.Number) |
|
||||||
num.Sub(header.Number, num) |
|
||||||
if num.Cmp(big.NewInt(1)) != 0 { |
|
||||||
return BlockNumberErr |
|
||||||
} |
|
||||||
|
|
||||||
if checkPow { |
|
||||||
// Verify the nonce of the header. Return an error if it's not valid
|
|
||||||
if !pow.Verify(types.NewBlockWithHeader(header)) { |
|
||||||
return &BlockNonceErr{Hash: header.Hash(), Number: header.Number, Nonce: header.Nonce.Uint64()} |
|
||||||
} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
@ -0,0 +1,243 @@ |
|||||||
|
// 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 ( |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/logger/glog" |
||||||
|
"github.com/ethereum/go-ethereum/params" |
||||||
|
"github.com/ethereum/go-ethereum/pow" |
||||||
|
"gopkg.in/fatih/set.v0" |
||||||
|
) |
||||||
|
|
||||||
|
// BlockValidator is responsible for validating block headers, uncles and
|
||||||
|
// processed state.
|
||||||
|
//
|
||||||
|
// BlockValidator implements Validator.
|
||||||
|
type BlockValidator struct { |
||||||
|
bc *BlockChain // Canonical block chain
|
||||||
|
Pow pow.PoW // Proof of work used for validating
|
||||||
|
} |
||||||
|
|
||||||
|
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||||
|
func NewBlockValidator(blockchain *BlockChain, pow pow.PoW) *BlockValidator { |
||||||
|
validator := &BlockValidator{ |
||||||
|
Pow: pow, |
||||||
|
bc: blockchain, |
||||||
|
} |
||||||
|
return validator |
||||||
|
} |
||||||
|
|
||||||
|
// ValidateBlock validates the given block's header and uncles and verifies the
|
||||||
|
// the block header's transaction and uncle roots.
|
||||||
|
//
|
||||||
|
// ValidateBlock does not validate the header's pow. The pow work validated
|
||||||
|
// seperately so we can process them in paralel.
|
||||||
|
//
|
||||||
|
// ValidateBlock also validates and makes sure that any previous state (or present)
|
||||||
|
// state that might or might not be present is checked to make sure that fast
|
||||||
|
// sync has done it's job proper. This prevents the block validator form accepting
|
||||||
|
// false positives where a header is present but the state is not.
|
||||||
|
func (v *BlockValidator) ValidateBlock(block *types.Block) error { |
||||||
|
if v.bc.HasBlock(block.Hash()) { |
||||||
|
if _, err := state.New(block.Root(), v.bc.chainDb); err == nil { |
||||||
|
return &KnownBlockError{block.Number(), block.Hash()} |
||||||
|
} |
||||||
|
} |
||||||
|
parent := v.bc.GetBlock(block.ParentHash()) |
||||||
|
if parent == nil { |
||||||
|
return ParentError(block.ParentHash()) |
||||||
|
} |
||||||
|
if _, err := state.New(parent.Root(), v.bc.chainDb); err != nil { |
||||||
|
return ParentError(block.ParentHash()) |
||||||
|
} |
||||||
|
|
||||||
|
header := block.Header() |
||||||
|
// validate the block header
|
||||||
|
if err := ValidateHeader(v.Pow, header, parent.Header(), false, false); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
// verify the uncles are correctly rewarded
|
||||||
|
if err := v.VerifyUncles(block, parent); err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
|
||||||
|
// Verify UncleHash before running other uncle validations
|
||||||
|
unclesSha := types.CalcUncleHash(block.Uncles()) |
||||||
|
if unclesSha != header.UncleHash { |
||||||
|
return fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha) |
||||||
|
} |
||||||
|
|
||||||
|
// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
|
||||||
|
// can be used by light clients to make sure they've received the correct Txs
|
||||||
|
txSha := types.DeriveSha(block.Transactions()) |
||||||
|
if txSha != header.TxHash { |
||||||
|
return fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha) |
||||||
|
} |
||||||
|
|
||||||
|
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 succes
|
||||||
|
// otherwise nil and an error is returned.
|
||||||
|
func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) { |
||||||
|
header := block.Header() |
||||||
|
if block.GasUsed().Cmp(usedGas) != 0 { |
||||||
|
return ValidationError(fmt.Sprintf("gas used error (%v / %v)", 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("unable to replicate block's bloom=%x", 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. received=%x calculated=%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(); header.Root != root { |
||||||
|
return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// VerifyUncles verifies the given block's uncles and applies the Ethereum
|
||||||
|
// consensus rules to the various block headers included; it will return an
|
||||||
|
// error if any of the included uncle headers were invalid. It returns an error
|
||||||
|
// if the validation failed.
|
||||||
|
func (v *BlockValidator) VerifyUncles(block, parent *types.Block) error { |
||||||
|
// validate that there at most 2 uncles included in this block
|
||||||
|
if len(block.Uncles()) > 2 { |
||||||
|
return ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles())) |
||||||
|
} |
||||||
|
|
||||||
|
uncles := set.New() |
||||||
|
ancestors := make(map[common.Hash]*types.Block) |
||||||
|
for _, ancestor := range v.bc.GetBlocksFromHash(block.ParentHash(), 7) { |
||||||
|
ancestors[ancestor.Hash()] = ancestor |
||||||
|
// Include ancestors uncles in the uncle set. Uncles must be unique.
|
||||||
|
for _, uncle := range ancestor.Uncles() { |
||||||
|
uncles.Add(uncle.Hash()) |
||||||
|
} |
||||||
|
} |
||||||
|
ancestors[block.Hash()] = block |
||||||
|
uncles.Add(block.Hash()) |
||||||
|
|
||||||
|
for i, uncle := range block.Uncles() { |
||||||
|
hash := uncle.Hash() |
||||||
|
if uncles.Has(hash) { |
||||||
|
// Error not unique
|
||||||
|
return UncleError("uncle[%d](%x) not unique", i, hash[:4]) |
||||||
|
} |
||||||
|
uncles.Add(hash) |
||||||
|
|
||||||
|
if ancestors[hash] != nil { |
||||||
|
branch := fmt.Sprintf(" O - %x\n |\n", block.Hash()) |
||||||
|
for h := range ancestors { |
||||||
|
branch += fmt.Sprintf(" O - %x\n |\n", h) |
||||||
|
} |
||||||
|
glog.Infoln(branch) |
||||||
|
return UncleError("uncle[%d](%x) is ancestor", i, hash[:4]) |
||||||
|
} |
||||||
|
|
||||||
|
if ancestors[uncle.ParentHash] == nil || uncle.ParentHash == parent.Hash() { |
||||||
|
return UncleError("uncle[%d](%x)'s parent is not ancestor (%x)", i, hash[:4], uncle.ParentHash[0:4]) |
||||||
|
} |
||||||
|
|
||||||
|
if err := ValidateHeader(v.Pow, uncle, ancestors[uncle.ParentHash].Header(), true, true); err != nil { |
||||||
|
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
// ValidateHeader validates the given header and, depending on the pow arg,
|
||||||
|
// checks the proof of work of the given header. Returns an error if the
|
||||||
|
// validation failed.
|
||||||
|
func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow bool) error { |
||||||
|
// Short circuit if the parent is missing.
|
||||||
|
if parent == nil { |
||||||
|
return ParentError(header.ParentHash) |
||||||
|
} |
||||||
|
// Short circuit if the header's already known or its parent missing
|
||||||
|
if v.bc.HasHeader(header.Hash()) { |
||||||
|
return nil |
||||||
|
} |
||||||
|
return ValidateHeader(v.Pow, header, parent, checkPow, false) |
||||||
|
} |
||||||
|
|
||||||
|
// Validates a header. Returns an error if the header is invalid.
|
||||||
|
//
|
||||||
|
// See YP section 4.3.4. "Block Header Validity"
|
||||||
|
func ValidateHeader(pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error { |
||||||
|
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 { |
||||||
|
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra)) |
||||||
|
} |
||||||
|
|
||||||
|
if uncle { |
||||||
|
if header.Time.Cmp(common.MaxBig) == 1 { |
||||||
|
return BlockTSTooBigErr |
||||||
|
} |
||||||
|
} else { |
||||||
|
if header.Time.Cmp(big.NewInt(time.Now().Unix())) == 1 { |
||||||
|
return BlockFutureErr |
||||||
|
} |
||||||
|
} |
||||||
|
if header.Time.Cmp(parent.Time) != 1 { |
||||||
|
return BlockEqualTSErr |
||||||
|
} |
||||||
|
|
||||||
|
expd := CalcDifficulty(header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty) |
||||||
|
if expd.Cmp(header.Difficulty) != 0 { |
||||||
|
return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd) |
||||||
|
} |
||||||
|
|
||||||
|
a := new(big.Int).Set(parent.GasLimit) |
||||||
|
a = a.Sub(a, header.GasLimit) |
||||||
|
a.Abs(a) |
||||||
|
b := new(big.Int).Set(parent.GasLimit) |
||||||
|
b = b.Div(b, params.GasLimitBoundDivisor) |
||||||
|
if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) { |
||||||
|
return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b) |
||||||
|
} |
||||||
|
|
||||||
|
num := new(big.Int).Set(parent.Number) |
||||||
|
num.Sub(header.Number, num) |
||||||
|
if num.Cmp(big.NewInt(1)) != 0 { |
||||||
|
return BlockNumberErr |
||||||
|
} |
||||||
|
|
||||||
|
if checkPow { |
||||||
|
// Verify the nonce of the header. Return an error if it's not valid
|
||||||
|
if !pow.Verify(types.NewBlockWithHeader(header)) { |
||||||
|
return &BlockNonceErr{header.Number, header.Hash(), header.Nonce.Uint64()} |
||||||
|
} |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
// 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 "math/big" |
||||||
|
|
||||||
|
// 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 big.Int |
||||||
|
|
||||||
|
// AddGas makes gas available for execution.
|
||||||
|
func (gp *GasPool) AddGas(amount *big.Int) *GasPool { |
||||||
|
i := (*big.Int)(gp) |
||||||
|
i.Add(i, 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 *big.Int) error { |
||||||
|
i := (*big.Int)(gp) |
||||||
|
if i.Cmp(amount) < 0 { |
||||||
|
return &GasLimitErr{Have: new(big.Int).Set(i), Want: amount} |
||||||
|
} |
||||||
|
i.Sub(i, amount) |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func (gp *GasPool) String() string { |
||||||
|
return (*big.Int)(gp).String() |
||||||
|
} |
@ -0,0 +1,107 @@ |
|||||||
|
package core |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/core/vm" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/ethereum/go-ethereum/logger" |
||||||
|
"github.com/ethereum/go-ethereum/logger/glog" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
big8 = big.NewInt(8) |
||||||
|
big32 = big.NewInt(32) |
||||||
|
) |
||||||
|
|
||||||
|
type StateProcessor struct { |
||||||
|
bc *BlockChain |
||||||
|
} |
||||||
|
|
||||||
|
func NewStateProcessor(bc *BlockChain) *StateProcessor { |
||||||
|
return &StateProcessor{bc} |
||||||
|
} |
||||||
|
|
||||||
|
// 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) (types.Receipts, vm.Logs, *big.Int, error) { |
||||||
|
var ( |
||||||
|
receipts types.Receipts |
||||||
|
totalUsedGas = big.NewInt(0) |
||||||
|
err error |
||||||
|
header = block.Header() |
||||||
|
allLogs vm.Logs |
||||||
|
gp = new(GasPool).AddGas(block.GasLimit()) |
||||||
|
) |
||||||
|
|
||||||
|
for i, tx := range block.Transactions() { |
||||||
|
statedb.StartRecord(tx.Hash(), block.Hash(), i) |
||||||
|
|
||||||
|
receipt, logs, _, err := ApplyTransaction(p.bc, gp, statedb, header, tx, totalUsedGas) |
||||||
|
if err != nil { |
||||||
|
return nil, nil, totalUsedGas, err |
||||||
|
} |
||||||
|
receipts = append(receipts, receipt) |
||||||
|
allLogs = append(allLogs, logs...) |
||||||
|
} |
||||||
|
AccumulateRewards(statedb, header, block.Uncles()) |
||||||
|
|
||||||
|
return receipts, allLogs, totalUsedGas, err |
||||||
|
} |
||||||
|
|
||||||
|
// ApplyTransaction attemps to apply a transaction to the given state database
|
||||||
|
// and uses the input parameters for its environment.
|
||||||
|
//
|
||||||
|
// ApplyTransactions returns the generated receipts and vm logs during the
|
||||||
|
// execution of the state transition phase.
|
||||||
|
func ApplyTransaction(bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int) (*types.Receipt, vm.Logs, *big.Int, error) { |
||||||
|
_, gas, err := ApplyMessage(NewEnv(statedb, bc, tx, header), tx, gp) |
||||||
|
if err != nil { |
||||||
|
return nil, nil, nil, err |
||||||
|
} |
||||||
|
|
||||||
|
// Update the state with pending changes
|
||||||
|
usedGas.Add(usedGas, gas) |
||||||
|
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas) |
||||||
|
receipt.TxHash = tx.Hash() |
||||||
|
receipt.GasUsed = new(big.Int).Set(gas) |
||||||
|
if MessageCreatesContract(tx) { |
||||||
|
from, _ := tx.From() |
||||||
|
receipt.ContractAddress = crypto.CreateAddress(from, tx.Nonce()) |
||||||
|
} |
||||||
|
|
||||||
|
logs := statedb.GetLogs(tx.Hash()) |
||||||
|
receipt.Logs = logs |
||||||
|
receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) |
||||||
|
|
||||||
|
glog.V(logger.Debug).Infoln(receipt) |
||||||
|
|
||||||
|
return receipt, logs, gas, err |
||||||
|
} |
||||||
|
|
||||||
|
// AccumulateRewards credits the coinbase of the given block with the
|
||||||
|
// mining reward. The total reward consists of the static block reward
|
||||||
|
// and rewards for included uncles. The coinbase of each uncle block is
|
||||||
|
// also rewarded.
|
||||||
|
func AccumulateRewards(statedb *state.StateDB, header *types.Header, uncles []*types.Header) { |
||||||
|
reward := new(big.Int).Set(BlockReward) |
||||||
|
r := new(big.Int) |
||||||
|
for _, uncle := range uncles { |
||||||
|
r.Add(uncle.Number, big8) |
||||||
|
r.Sub(r, header.Number) |
||||||
|
r.Mul(r, BlockReward) |
||||||
|
r.Div(r, big8) |
||||||
|
statedb.AddBalance(uncle.Coinbase, r) |
||||||
|
|
||||||
|
r.Div(BlockReward, big32) |
||||||
|
reward.Add(reward, r) |
||||||
|
} |
||||||
|
statedb.AddBalance(header.Coinbase, reward) |
||||||
|
} |
@ -1,171 +0,0 @@ |
|||||||
// 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/common" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
"github.com/ethereum/go-ethereum/logger" |
|
||||||
"github.com/ethereum/go-ethereum/logger/glog" |
|
||||||
"github.com/ethereum/go-ethereum/rlp" |
|
||||||
"github.com/syndtr/goleveldb/leveldb" |
|
||||||
) |
|
||||||
|
|
||||||
var ( |
|
||||||
receiptsPre = []byte("receipts-") |
|
||||||
blockReceiptsPre = []byte("receipts-block-") |
|
||||||
) |
|
||||||
|
|
||||||
// PutTransactions stores the transactions in the given database
|
|
||||||
func PutTransactions(db ethdb.Database, block *types.Block, txs types.Transactions) error { |
|
||||||
batch := db.NewBatch() |
|
||||||
|
|
||||||
for i, tx := range block.Transactions() { |
|
||||||
rlpEnc, err := rlp.EncodeToBytes(tx) |
|
||||||
if err != nil { |
|
||||||
return fmt.Errorf("failed encoding tx: %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
batch.Put(tx.Hash().Bytes(), rlpEnc) |
|
||||||
|
|
||||||
var txExtra struct { |
|
||||||
BlockHash common.Hash |
|
||||||
BlockIndex uint64 |
|
||||||
Index uint64 |
|
||||||
} |
|
||||||
txExtra.BlockHash = block.Hash() |
|
||||||
txExtra.BlockIndex = block.NumberU64() |
|
||||||
txExtra.Index = uint64(i) |
|
||||||
rlpMeta, err := rlp.EncodeToBytes(txExtra) |
|
||||||
if err != nil { |
|
||||||
return fmt.Errorf("failed encoding tx meta data: %v", err) |
|
||||||
} |
|
||||||
|
|
||||||
batch.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta) |
|
||||||
} |
|
||||||
|
|
||||||
if err := batch.Write(); err != nil { |
|
||||||
return fmt.Errorf("failed writing tx to db: %v", err) |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func DeleteTransaction(db ethdb.Database, txHash common.Hash) { |
|
||||||
db.Delete(txHash[:]) |
|
||||||
} |
|
||||||
|
|
||||||
func GetTransaction(db ethdb.Database, txhash common.Hash) *types.Transaction { |
|
||||||
data, _ := db.Get(txhash[:]) |
|
||||||
if len(data) != 0 { |
|
||||||
var tx types.Transaction |
|
||||||
if err := rlp.DecodeBytes(data, &tx); err != nil { |
|
||||||
return nil |
|
||||||
} |
|
||||||
return &tx |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// PutReceipts stores the receipts in the current database
|
|
||||||
func PutReceipts(db ethdb.Database, receipts types.Receipts) error { |
|
||||||
batch := new(leveldb.Batch) |
|
||||||
_, batchWrite := db.(*ethdb.LDBDatabase) |
|
||||||
|
|
||||||
for _, receipt := range receipts { |
|
||||||
storageReceipt := (*types.ReceiptForStorage)(receipt) |
|
||||||
bytes, err := rlp.EncodeToBytes(storageReceipt) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
|
|
||||||
if batchWrite { |
|
||||||
batch.Put(append(receiptsPre, receipt.TxHash[:]...), bytes) |
|
||||||
} else { |
|
||||||
err = db.Put(append(receiptsPre, receipt.TxHash[:]...), bytes) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
if db, ok := db.(*ethdb.LDBDatabase); ok { |
|
||||||
if err := db.LDB().Write(batch, nil); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
// Delete a receipts from the database
|
|
||||||
func DeleteReceipt(db ethdb.Database, txHash common.Hash) { |
|
||||||
db.Delete(append(receiptsPre, txHash[:]...)) |
|
||||||
} |
|
||||||
|
|
||||||
// GetReceipt returns a receipt by hash
|
|
||||||
func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt { |
|
||||||
data, _ := db.Get(append(receiptsPre, txHash[:]...)) |
|
||||||
if len(data) == 0 { |
|
||||||
return nil |
|
||||||
} |
|
||||||
var receipt types.ReceiptForStorage |
|
||||||
err := rlp.DecodeBytes(data, &receipt) |
|
||||||
if err != nil { |
|
||||||
glog.V(logger.Core).Infoln("GetReceipt err:", err) |
|
||||||
} |
|
||||||
return (*types.Receipt)(&receipt) |
|
||||||
} |
|
||||||
|
|
||||||
// GetBlockReceipts returns the receipts generated by the transactions
|
|
||||||
// included in block's given hash.
|
|
||||||
func GetBlockReceipts(db ethdb.Database, hash common.Hash) types.Receipts { |
|
||||||
data, _ := db.Get(append(blockReceiptsPre, hash[:]...)) |
|
||||||
if len(data) == 0 { |
|
||||||
return nil |
|
||||||
} |
|
||||||
rs := []*types.ReceiptForStorage{} |
|
||||||
if err := rlp.DecodeBytes(data, &rs); err != nil { |
|
||||||
glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err) |
|
||||||
return nil |
|
||||||
} |
|
||||||
receipts := make(types.Receipts, len(rs)) |
|
||||||
for i, receipt := range rs { |
|
||||||
receipts[i] = (*types.Receipt)(receipt) |
|
||||||
} |
|
||||||
return receipts |
|
||||||
} |
|
||||||
|
|
||||||
// PutBlockReceipts stores the block's transactions associated receipts
|
|
||||||
// and stores them by block hash in a single slice. This is required for
|
|
||||||
// forks and chain reorgs
|
|
||||||
func PutBlockReceipts(db ethdb.Database, hash common.Hash, receipts types.Receipts) error { |
|
||||||
rs := make([]*types.ReceiptForStorage, len(receipts)) |
|
||||||
for i, receipt := range receipts { |
|
||||||
rs[i] = (*types.ReceiptForStorage)(receipt) |
|
||||||
} |
|
||||||
bytes, err := rlp.EncodeToBytes(rs) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
err = db.Put(append(blockReceiptsPre, hash[:]...), bytes) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
@ -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 core |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts" |
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/core/vm" |
||||||
|
"github.com/ethereum/go-ethereum/ethdb" |
||||||
|
"github.com/ethereum/go-ethereum/event" |
||||||
|
) |
||||||
|
|
||||||
|
// Validator is an interface which defines the standard for block validation.
|
||||||
|
//
|
||||||
|
// The validator is responsible for validating incoming block or, if desired,
|
||||||
|
// validates headers for fast validation.
|
||||||
|
//
|
||||||
|
// ValidateBlock validates the given block and should return an error if it
|
||||||
|
// failed to do so and should be used for "full" validation.
|
||||||
|
//
|
||||||
|
// ValidateHeader validates the given header and parent and returns an error
|
||||||
|
// if it failed to do so.
|
||||||
|
//
|
||||||
|
// ValidateStack validates the given statedb and optionally the receipts and
|
||||||
|
// gas used. The implementor should decide what to do with the given input.
|
||||||
|
type Validator interface { |
||||||
|
ValidateBlock(block *types.Block) error |
||||||
|
ValidateHeader(header, parent *types.Header, checkPow bool) error |
||||||
|
ValidateState(block, parent *types.Block, state *state.StateDB, receipts types.Receipts, usedGas *big.Int) 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) (types.Receipts, vm.Logs, *big.Int, error) |
||||||
|
} |
||||||
|
|
||||||
|
// Backend is an interface defining the basic functionality for an operable node
|
||||||
|
// with all the functionality to be a functional, valid Ethereum operator.
|
||||||
|
//
|
||||||
|
// TODO Remove this
|
||||||
|
type Backend interface { |
||||||
|
AccountManager() *accounts.Manager |
||||||
|
BlockChain() *BlockChain |
||||||
|
TxPool() *TxPool |
||||||
|
ChainDb() ethdb.Database |
||||||
|
DappDb() ethdb.Database |
||||||
|
EventMux() *event.TypeMux |
||||||
|
} |
@ -0,0 +1,106 @@ |
|||||||
|
// 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 runtime |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core" |
||||||
|
"github.com/ethereum/go-ethereum/core/state" |
||||||
|
"github.com/ethereum/go-ethereum/core/vm" |
||||||
|
) |
||||||
|
|
||||||
|
// Env is a basic runtime environment required for running the EVM.
|
||||||
|
type Env struct { |
||||||
|
depth int |
||||||
|
state *state.StateDB |
||||||
|
|
||||||
|
origin common.Address |
||||||
|
coinbase common.Address |
||||||
|
|
||||||
|
number *big.Int |
||||||
|
time *big.Int |
||||||
|
difficulty *big.Int |
||||||
|
gasLimit *big.Int |
||||||
|
|
||||||
|
logs []vm.StructLog |
||||||
|
|
||||||
|
getHashFn func(uint64) common.Hash |
||||||
|
} |
||||||
|
|
||||||
|
// NewEnv returns a new vm.Environment
|
||||||
|
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment { |
||||||
|
return &Env{ |
||||||
|
state: state, |
||||||
|
origin: cfg.Origin, |
||||||
|
coinbase: cfg.Coinbase, |
||||||
|
number: cfg.BlockNumber, |
||||||
|
time: cfg.Time, |
||||||
|
difficulty: cfg.Difficulty, |
||||||
|
gasLimit: cfg.GasLimit, |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Env) StructLogs() []vm.StructLog { |
||||||
|
return self.logs |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Env) AddStructLog(log vm.StructLog) { |
||||||
|
self.logs = append(self.logs, log) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Env) Origin() common.Address { return self.origin } |
||||||
|
func (self *Env) BlockNumber() *big.Int { return self.number } |
||||||
|
func (self *Env) Coinbase() common.Address { return self.coinbase } |
||||||
|
func (self *Env) Time() *big.Int { return self.time } |
||||||
|
func (self *Env) Difficulty() *big.Int { return self.difficulty } |
||||||
|
func (self *Env) Db() vm.Database { return self.state } |
||||||
|
func (self *Env) GasLimit() *big.Int { return self.gasLimit } |
||||||
|
func (self *Env) VmType() vm.Type { return vm.StdVmTy } |
||||||
|
func (self *Env) GetHash(n uint64) common.Hash { |
||||||
|
return self.getHashFn(n) |
||||||
|
} |
||||||
|
func (self *Env) AddLog(log *vm.Log) { |
||||||
|
self.state.AddLog(log) |
||||||
|
} |
||||||
|
func (self *Env) Depth() int { return self.depth } |
||||||
|
func (self *Env) SetDepth(i int) { self.depth = i } |
||||||
|
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool { |
||||||
|
return self.state.GetBalance(from).Cmp(balance) >= 0 |
||||||
|
} |
||||||
|
func (self *Env) MakeSnapshot() vm.Database { |
||||||
|
return self.state.Copy() |
||||||
|
} |
||||||
|
func (self *Env) SetSnapshot(copy vm.Database) { |
||||||
|
self.state.Set(copy.(*state.StateDB)) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) { |
||||||
|
core.Transfer(from, to, amount) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { |
||||||
|
return core.Call(self, caller, addr, data, gas, price, value) |
||||||
|
} |
||||||
|
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) { |
||||||
|
return core.CallCode(self, caller, addr, data, gas, price, value) |
||||||
|
} |
||||||
|
|
||||||
|
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) { |
||||||
|
return core.Create(self, caller, data, gas, price, value) |
||||||
|
} |
@ -0,0 +1,121 @@ |
|||||||
|
// 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 runtime |
||||||
|
|
||||||
|
import ( |
||||||
|
"math/big" |
||||||
|
"time" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"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/ethdb" |
||||||
|
) |
||||||
|
|
||||||
|
// Config is a basic type specifing certain configuration flags for running
|
||||||
|
// the EVM.
|
||||||
|
type Config struct { |
||||||
|
Difficulty *big.Int |
||||||
|
Origin common.Address |
||||||
|
Coinbase common.Address |
||||||
|
BlockNumber *big.Int |
||||||
|
Time *big.Int |
||||||
|
GasLimit *big.Int |
||||||
|
GasPrice *big.Int |
||||||
|
Value *big.Int |
||||||
|
DisableJit bool // "disable" so it's enabled by default
|
||||||
|
Debug bool |
||||||
|
|
||||||
|
GetHashFn func(n uint64) common.Hash |
||||||
|
} |
||||||
|
|
||||||
|
// sets defaults on the config
|
||||||
|
func setDefaults(cfg *Config) { |
||||||
|
if cfg.Difficulty == nil { |
||||||
|
cfg.Difficulty = new(big.Int) |
||||||
|
} |
||||||
|
if cfg.Time == nil { |
||||||
|
cfg.Time = big.NewInt(time.Now().Unix()) |
||||||
|
} |
||||||
|
if cfg.GasLimit == nil { |
||||||
|
cfg.GasLimit = new(big.Int).Set(common.MaxBig) |
||||||
|
} |
||||||
|
if cfg.GasPrice == nil { |
||||||
|
cfg.GasPrice = new(big.Int) |
||||||
|
} |
||||||
|
if cfg.Value == nil { |
||||||
|
cfg.Value = new(big.Int) |
||||||
|
} |
||||||
|
if cfg.BlockNumber == nil { |
||||||
|
cfg.BlockNumber = new(big.Int) |
||||||
|
} |
||||||
|
if cfg.GetHashFn == nil { |
||||||
|
cfg.GetHashFn = func(n uint64) common.Hash { |
||||||
|
return common.BytesToHash(crypto.Sha3([]byte(new(big.Int).SetUint64(n).String()))) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Execute executes the code using the input as call data during the execution.
|
||||||
|
// It returns the EVM's return value, the new state and an error if it failed.
|
||||||
|
//
|
||||||
|
// Executes sets up a in memory, temporarily, environment for the execution of
|
||||||
|
// the given code. It enabled the JIT by default and make sure that it's restored
|
||||||
|
// to it's original state afterwards.
|
||||||
|
func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) { |
||||||
|
if cfg == nil { |
||||||
|
cfg = new(Config) |
||||||
|
} |
||||||
|
setDefaults(cfg) |
||||||
|
|
||||||
|
// defer the call to setting back the original values
|
||||||
|
defer func(debug, forceJit, enableJit bool) { |
||||||
|
vm.Debug = debug |
||||||
|
vm.ForceJit = forceJit |
||||||
|
vm.EnableJit = enableJit |
||||||
|
}(vm.Debug, vm.ForceJit, vm.EnableJit) |
||||||
|
|
||||||
|
vm.ForceJit = !cfg.DisableJit |
||||||
|
vm.EnableJit = !cfg.DisableJit |
||||||
|
vm.Debug = cfg.Debug |
||||||
|
|
||||||
|
var ( |
||||||
|
db, _ = ethdb.NewMemDatabase() |
||||||
|
statedb, _ = state.New(common.Hash{}, db) |
||||||
|
vmenv = NewEnv(cfg, statedb) |
||||||
|
sender = statedb.CreateAccount(cfg.Origin) |
||||||
|
receiver = statedb.CreateAccount(common.StringToAddress("contract")) |
||||||
|
) |
||||||
|
// set the receiver's (the executing contract) code for execution.
|
||||||
|
receiver.SetCode(code) |
||||||
|
|
||||||
|
// Call the code with the given configuration.
|
||||||
|
ret, err := vmenv.Call( |
||||||
|
sender, |
||||||
|
receiver.Address(), |
||||||
|
input, |
||||||
|
cfg.GasLimit, |
||||||
|
cfg.GasPrice, |
||||||
|
cfg.Value, |
||||||
|
) |
||||||
|
|
||||||
|
if cfg.Debug { |
||||||
|
vm.StdErrFormat(vmenv.StructLogs()) |
||||||
|
} |
||||||
|
return ret, statedb, err |
||||||
|
} |
@ -0,0 +1,120 @@ |
|||||||
|
// 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 runtime |
||||||
|
|
||||||
|
import ( |
||||||
|
"strings" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/accounts/abi" |
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/vm" |
||||||
|
) |
||||||
|
|
||||||
|
func TestDefaults(t *testing.T) { |
||||||
|
cfg := new(Config) |
||||||
|
setDefaults(cfg) |
||||||
|
|
||||||
|
if cfg.Difficulty == nil { |
||||||
|
t.Error("expected difficulty to be non nil") |
||||||
|
} |
||||||
|
|
||||||
|
if cfg.Time == nil { |
||||||
|
t.Error("expected time to be non nil") |
||||||
|
} |
||||||
|
if cfg.GasLimit == nil { |
||||||
|
t.Error("expected time to be non nil") |
||||||
|
} |
||||||
|
if cfg.GasPrice == nil { |
||||||
|
t.Error("expected time to be non nil") |
||||||
|
} |
||||||
|
if cfg.Value == nil { |
||||||
|
t.Error("expected time to be non nil") |
||||||
|
} |
||||||
|
if cfg.GetHashFn == nil { |
||||||
|
t.Error("expected time to be non nil") |
||||||
|
} |
||||||
|
if cfg.BlockNumber == nil { |
||||||
|
t.Error("expected block number to be non nil") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func TestEnvironment(t *testing.T) { |
||||||
|
defer func() { |
||||||
|
if r := recover(); r != nil { |
||||||
|
t.Fatalf("crashed with: %v", r) |
||||||
|
} |
||||||
|
}() |
||||||
|
|
||||||
|
Execute([]byte{ |
||||||
|
byte(vm.DIFFICULTY), |
||||||
|
byte(vm.TIMESTAMP), |
||||||
|
byte(vm.GASLIMIT), |
||||||
|
byte(vm.PUSH1), |
||||||
|
byte(vm.ORIGIN), |
||||||
|
byte(vm.BLOCKHASH), |
||||||
|
byte(vm.COINBASE), |
||||||
|
}, nil, nil) |
||||||
|
} |
||||||
|
|
||||||
|
func TestRestoreDefaults(t *testing.T) { |
||||||
|
Execute(nil, nil, &Config{Debug: true}) |
||||||
|
if vm.ForceJit { |
||||||
|
t.Error("expected force jit to be disabled") |
||||||
|
} |
||||||
|
|
||||||
|
if vm.Debug { |
||||||
|
t.Error("expected debug to be disabled") |
||||||
|
} |
||||||
|
|
||||||
|
if vm.EnableJit { |
||||||
|
t.Error("expected jit to be disabled") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
func BenchmarkCall(b *testing.B) { |
||||||
|
var definition = `[{"constant":true,"inputs":[],"name":"seller","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"abort","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"type":"function"},{"constant":false,"inputs":[],"name":"refund","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"buyer","outputs":[{"name":"","type":"address"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmReceived","outputs":[],"type":"function"},{"constant":true,"inputs":[],"name":"state","outputs":[{"name":"","type":"uint8"}],"type":"function"},{"constant":false,"inputs":[],"name":"confirmPurchase","outputs":[],"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[],"name":"Aborted","type":"event"},{"anonymous":false,"inputs":[],"name":"PurchaseConfirmed","type":"event"},{"anonymous":false,"inputs":[],"name":"ItemReceived","type":"event"},{"anonymous":false,"inputs":[],"name":"Refunded","type":"event"}]` |
||||||
|
|
||||||
|
var code = common.Hex2Bytes("6060604052361561006c5760e060020a600035046308551a53811461007457806335a063b4146100865780633fa4f245146100a6578063590e1ae3146100af5780637150d8ae146100cf57806373fac6f0146100e1578063c19d93fb146100fe578063d696069714610112575b610131610002565b610133600154600160a060020a031681565b610131600154600160a060020a0390811633919091161461015057610002565b61014660005481565b610131600154600160a060020a039081163391909116146102d557610002565b610133600254600160a060020a031681565b610131600254600160a060020a0333811691161461023757610002565b61014660025460ff60a060020a9091041681565b61013160025460009060ff60a060020a9091041681146101cc57610002565b005b600160a060020a03166060908152602090f35b6060908152602090f35b60025460009060a060020a900460ff16811461016b57610002565b600154600160a060020a03908116908290301631606082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f72c874aeff0b183a56e2b79c71b46e1aed4dee5e09862134b8821ba2fddbf8bf9250a150565b80546002023414806101dd57610002565b6002805460a060020a60ff021973ffffffffffffffffffffffffffffffffffffffff1990911633171660a060020a1790557fd5d55c8a68912e9a110618df8d5e2e83b8d83211c57a8ddd1203df92885dc881826060a15050565b60025460019060a060020a900460ff16811461025257610002565b60025460008054600160a060020a0390921691606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517fe89152acd703c9d8c7d28829d443260b411454d45394e7995815140c8cbcbcf79250a150565b60025460019060a060020a900460ff1681146102f057610002565b6002805460008054600160a060020a0390921692909102606082818181858883f150508354604051600160a060020a0391821694503090911631915082818181858883f150506002805460a060020a60ff02191660a160020a179055506040517f8616bbbbad963e4e65b1366f1d75dfb63f9e9704bbbf91fb01bec70849906cf79250a15056") |
||||||
|
|
||||||
|
abi, err := abi.JSON(strings.NewReader(definition)) |
||||||
|
if err != nil { |
||||||
|
b.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
cpurchase, err := abi.Pack("confirmPurchase") |
||||||
|
if err != nil { |
||||||
|
b.Fatal(err) |
||||||
|
} |
||||||
|
creceived, err := abi.Pack("confirmReceived") |
||||||
|
if err != nil { |
||||||
|
b.Fatal(err) |
||||||
|
} |
||||||
|
refund, err := abi.Pack("refund") |
||||||
|
if err != nil { |
||||||
|
b.Fatal(err) |
||||||
|
} |
||||||
|
|
||||||
|
b.ResetTimer() |
||||||
|
for i := 0; i < b.N; i++ { |
||||||
|
for j := 0; j < 400; j++ { |
||||||
|
Execute(code, cpurchase, nil) |
||||||
|
Execute(code, creceived, nil) |
||||||
|
Execute(code, refund, nil) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
// 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 secp256k1 |
||||||
|
|
||||||
|
import "C" |
||||||
|
import "unsafe" |
||||||
|
|
||||||
|
// Callbacks for converting libsecp256k1 internal faults into
|
||||||
|
// recoverable Go panics.
|
||||||
|
|
||||||
|
//export secp256k1GoPanicIllegal
|
||||||
|
func secp256k1GoPanicIllegal(msg *C.char, data unsafe.Pointer) { |
||||||
|
panic("illegal argument: " + C.GoString(msg)) |
||||||
|
} |
||||||
|
|
||||||
|
//export secp256k1GoPanicError
|
||||||
|
func secp256k1GoPanicError(msg *C.char, data unsafe.Pointer) { |
||||||
|
panic("internal error: " + C.GoString(msg)) |
||||||
|
} |
Loading…
Reference in new issue