aboutsummaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/bench_test.go163
-rw-r--r--core/block_cache_test.go6
-rw-r--r--core/block_processor.go140
-rw-r--r--core/block_processor_test.go17
-rw-r--r--core/canary.go28
-rw-r--r--core/chain_makers.go213
-rw-r--r--core/chain_makers_test.go78
-rw-r--r--core/chain_manager.go355
-rw-r--r--core/chain_manager_test.go45
-rw-r--r--core/genesis.go55
-rw-r--r--core/state_transition.go66
-rw-r--r--core/transaction_pool.go19
-rw-r--r--core/transaction_pool_test.go91
-rw-r--r--core/types/block.go438
-rw-r--r--core/types/block_test.go19
-rw-r--r--core/types/transaction.go265
-rw-r--r--core/types/transaction_test.go32
-rw-r--r--core/vm_env.go34
18 files changed, 1168 insertions, 896 deletions
diff --git a/core/bench_test.go b/core/bench_test.go
new file mode 100644
index 000000000..6d851febd
--- /dev/null
+++ b/core/bench_test.go
@@ -0,0 +1,163 @@
+package core
+
+import (
+ "crypto/ecdsa"
+ "io/ioutil"
+ "math/big"
+ "os"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+func BenchmarkInsertChain_empty_memdb(b *testing.B) {
+ benchInsertChain(b, false, nil)
+}
+func BenchmarkInsertChain_empty_diskdb(b *testing.B) {
+ benchInsertChain(b, true, nil)
+}
+func BenchmarkInsertChain_valueTx_memdb(b *testing.B) {
+ benchInsertChain(b, false, genValueTx(0))
+}
+func BenchmarkInsertChain_valueTx_diskdb(b *testing.B) {
+ benchInsertChain(b, true, genValueTx(0))
+}
+func BenchmarkInsertChain_valueTx_100kB_memdb(b *testing.B) {
+ benchInsertChain(b, false, genValueTx(100*1024))
+}
+func BenchmarkInsertChain_valueTx_100kB_diskdb(b *testing.B) {
+ benchInsertChain(b, true, genValueTx(100*1024))
+}
+func BenchmarkInsertChain_uncles_memdb(b *testing.B) {
+ benchInsertChain(b, false, genUncles)
+}
+func BenchmarkInsertChain_uncles_diskdb(b *testing.B) {
+ benchInsertChain(b, true, genUncles)
+}
+func BenchmarkInsertChain_ring200_memdb(b *testing.B) {
+ benchInsertChain(b, false, genTxRing(200))
+}
+func BenchmarkInsertChain_ring200_diskdb(b *testing.B) {
+ benchInsertChain(b, true, genTxRing(200))
+}
+func BenchmarkInsertChain_ring1000_memdb(b *testing.B) {
+ benchInsertChain(b, false, genTxRing(1000))
+}
+func BenchmarkInsertChain_ring1000_diskdb(b *testing.B) {
+ benchInsertChain(b, true, genTxRing(1000))
+}
+
+var (
+ // This is the content of the genesis block used by the benchmarks.
+ benchRootKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ benchRootAddr = crypto.PubkeyToAddress(benchRootKey.PublicKey)
+ benchRootFunds = common.BigPow(2, 100)
+)
+
+// genValueTx returns a block generator that includes a single
+// value-transfer transaction with n bytes of extra data in each
+// block.
+func genValueTx(nbytes int) func(int, *BlockGen) {
+ return func(i int, gen *BlockGen) {
+ toaddr := common.Address{}
+ data := make([]byte, nbytes)
+ gas := IntrinsicGas(data)
+ tx, _ := types.NewTransaction(gen.TxNonce(benchRootAddr), toaddr, big.NewInt(1), gas, nil, data).SignECDSA(benchRootKey)
+ gen.AddTx(tx)
+ }
+}
+
+var (
+ ringKeys = make([]*ecdsa.PrivateKey, 1000)
+ ringAddrs = make([]common.Address, len(ringKeys))
+)
+
+func init() {
+ ringKeys[0] = benchRootKey
+ ringAddrs[0] = benchRootAddr
+ for i := 1; i < len(ringKeys); i++ {
+ ringKeys[i], _ = crypto.GenerateKey()
+ ringAddrs[i] = crypto.PubkeyToAddress(ringKeys[i].PublicKey)
+ }
+}
+
+// genTxRing returns a block generator that sends ether in a ring
+// among n accounts. This is creates n entries in the state database
+// and fills the blocks with many small transactions.
+func genTxRing(naccounts int) func(int, *BlockGen) {
+ from := 0
+ return func(i int, gen *BlockGen) {
+ gas := CalcGasLimit(gen.PrevBlock(i - 1))
+ for {
+ gas.Sub(gas, params.TxGas)
+ if gas.Cmp(params.TxGas) < 0 {
+ break
+ }
+ to := (from + 1) % naccounts
+ tx := types.NewTransaction(
+ gen.TxNonce(ringAddrs[from]),
+ ringAddrs[to],
+ benchRootFunds,
+ params.TxGas,
+ nil,
+ nil,
+ )
+ tx, _ = tx.SignECDSA(ringKeys[from])
+ gen.AddTx(tx)
+ from = to
+ }
+ }
+}
+
+// genUncles generates blocks with two uncle headers.
+func genUncles(i int, gen *BlockGen) {
+ if i >= 6 {
+ b2 := gen.PrevBlock(i - 6).Header()
+ b2.Extra = []byte("foo")
+ gen.AddUncle(b2)
+ b3 := gen.PrevBlock(i - 6).Header()
+ b3.Extra = []byte("bar")
+ gen.AddUncle(b3)
+ }
+}
+
+func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
+ // Create the database in memory or in a temporary directory.
+ var db common.Database
+ if !disk {
+ db, _ = ethdb.NewMemDatabase()
+ } else {
+ dir, err := ioutil.TempDir("", "eth-core-bench")
+ if err != nil {
+ b.Fatalf("cannot create temporary directory: %v", err)
+ }
+ defer os.RemoveAll(dir)
+ db, err = ethdb.NewLDBDatabase(dir)
+ if err != nil {
+ b.Fatalf("cannot create temporary database: %v", err)
+ }
+ defer db.Close()
+ }
+
+ // Generate a chain of b.N blocks using the supplied block
+ // generator function.
+ genesis := GenesisBlockForTesting(db, benchRootAddr, benchRootFunds)
+ chain := GenerateChain(genesis, db, b.N, gen)
+
+ // Time the insertion of the new chain.
+ // State and blocks are stored in the same DB.
+ evmux := new(event.TypeMux)
+ chainman, _ := NewChainManager(genesis, db, db, FakePow{}, evmux)
+ chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux))
+ defer chainman.Stop()
+ b.ReportAllocs()
+ b.ResetTimer()
+ if i, err := chainman.InsertChain(chain); err != nil {
+ b.Fatalf("insert error (block %d): %v\n", i, err)
+ }
+}
diff --git a/core/block_cache_test.go b/core/block_cache_test.go
index 43ab847f9..80d118599 100644
--- a/core/block_cache_test.go
+++ b/core/block_cache_test.go
@@ -11,12 +11,12 @@ import (
func newChain(size int) (chain []*types.Block) {
var parentHash common.Hash
for i := 0; i < size; i++ {
- block := types.NewBlock(parentHash, common.Address{}, common.Hash{}, new(big.Int), 0, nil)
- block.Header().Number = big.NewInt(int64(i))
+ head := &types.Header{ParentHash: parentHash, Number: big.NewInt(int64(i))}
+ block := types.NewBlock(head, nil, nil, nil)
chain = append(chain, block)
parentHash = block.Hash()
}
- return
+ return chain
}
func insertChainCache(cache *BlockCache, chain []*types.Block) {
diff --git a/core/block_processor.go b/core/block_processor.go
index e30ced312..22d4c7c27 100644
--- a/core/block_processor.go
+++ b/core/block_processor.go
@@ -57,8 +57,8 @@ func NewBlockProcessor(db, extra common.Database, pow pow.PoW, chainManager *Cha
}
func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block *types.Block, transientProcess bool) (receipts types.Receipts, err error) {
- coinbase := statedb.GetOrNewStateObject(block.Header().Coinbase)
- coinbase.SetGasLimit(block.Header().GasLimit)
+ coinbase := statedb.GetOrNewStateObject(block.Coinbase())
+ coinbase.SetGasLimit(block.GasLimit())
// Process the transactions on to parent state
receipts, err = sm.ApplyTransactions(coinbase, statedb, block, block.Transactions(), transientProcess)
@@ -69,11 +69,11 @@ func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block
return receipts, nil
}
-func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
+func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) {
// If we are mining this block and validating we want to set the logs back to 0
cb := statedb.GetStateObject(coinbase.Address())
- _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, block), tx, cb)
+ _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, header), tx, cb)
if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
return nil, nil, err
}
@@ -81,9 +81,8 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated
// Update the state with pending changes
statedb.Update()
- cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas))
- receipt := types.NewReceipt(statedb.Root().Bytes(), cumulative)
-
+ usedGas.Add(usedGas, gas)
+ receipt := types.NewReceipt(statedb.Root().Bytes(), usedGas)
logs := statedb.GetLogs(tx.Hash())
receipt.SetLogs(logs)
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
@@ -108,12 +107,13 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state
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(coinbase, statedb, block, tx, totalUsedGas, transientProcess)
+ receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, header, tx, totalUsedGas, transientProcess)
if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) {
return nil, err
}
@@ -142,11 +142,10 @@ func (sm *BlockProcessor) RetryProcess(block *types.Block) (logs state.Logs, err
sm.mutex.Lock()
defer sm.mutex.Unlock()
- header := block.Header()
- if !sm.bc.HasBlock(header.ParentHash) {
- return nil, ParentError(header.ParentHash)
+ if !sm.bc.HasBlock(block.ParentHash()) {
+ return nil, ParentError(block.ParentHash())
}
- parent := sm.bc.GetBlock(header.ParentHash)
+ parent := sm.bc.GetBlock(block.ParentHash())
// FIXME Change to full header validation. See #1225
errch := make(chan bool)
@@ -168,30 +167,32 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, err erro
sm.mutex.Lock()
defer sm.mutex.Unlock()
- header := block.Header()
- if sm.bc.HasBlock(header.Hash()) {
- return nil, &KnownBlockError{header.Number, header.Hash()}
+ if sm.bc.HasBlock(block.Hash()) {
+ return nil, &KnownBlockError{block.Number(), block.Hash()}
}
- if !sm.bc.HasBlock(header.ParentHash) {
- return nil, ParentError(header.ParentHash)
+ if !sm.bc.HasBlock(block.ParentHash()) {
+ return nil, ParentError(block.ParentHash())
}
- parent := sm.bc.GetBlock(header.ParentHash)
+ parent := sm.bc.GetBlock(block.ParentHash())
return sm.processWithParent(block, parent)
}
func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, err error) {
// Create a new state based on the parent's root (e.g., create copy)
state := state.New(parent.Root(), sm.db)
+ header := block.Header()
+ uncles := block.Uncles()
+ txs := block.Transactions()
// Block validation
- if err = ValidateHeader(sm.Pow, block.Header(), parent.Header(), false); err != nil {
+ if err = ValidateHeader(sm.Pow, header, parent, false); err != nil {
return
}
// There can be at most two uncles
- if len(block.Uncles()) > 2 {
- return nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(block.Uncles()))
+ if len(uncles) > 2 {
+ return nil, ValidationError("Block can only contain maximum 2 uncles (contained %v)", len(uncles))
}
receipts, err := sm.TransitionState(state, parent, block, false)
@@ -199,8 +200,6 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
return
}
- header := block.Header()
-
// 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)
@@ -211,7 +210,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
// 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())
+ txSha := types.DeriveSha(txs)
if txSha != header.TxHash {
err = fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha)
return
@@ -225,7 +224,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
}
// Verify UncleHash before running other uncle validations
- unclesSha := block.CalculateUnclesHash()
+ unclesSha := types.CalcUncleHash(uncles)
if unclesSha != header.UncleHash {
err = fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha)
return
@@ -236,7 +235,7 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
return
}
// Accumulate static rewards; block reward, uncle's and uncle inclusion.
- AccumulateRewards(state, block)
+ AccumulateRewards(state, header, uncles)
// Commit state objects/accounts to a temporary trie (does not save)
// used to calculate the state root.
@@ -260,20 +259,44 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
return state.Logs(), 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 {
- ancestors := set.New()
uncles := set.New()
- ancestorHeaders := make(map[common.Hash]*types.Header)
- for _, ancestor := range sm.bc.GetAncestors(block, 7) {
- ancestorHeaders[ancestor.Hash()] = ancestor.Header()
- ancestors.Add(ancestor.Hash())
+ 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) {
@@ -282,22 +305,20 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
}
uncles.Add(hash)
- if ancestors.Has(hash) {
+ if ancestors[hash] != nil {
branch := fmt.Sprintf(" O - %x\n |\n", block.Hash())
- ancestors.Each(func(item interface{}) bool {
- branch += fmt.Sprintf(" O - %x\n |\n", hash)
- return true
- })
+ 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.Has(uncle.ParentHash) || uncle.ParentHash == parent.Hash() {
+ 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, ancestorHeaders[uncle.ParentHash], true); err != nil {
+ if err := ValidateHeader(sm.Pow, uncle, ancestors[uncle.ParentHash], true); err != nil {
return ValidationError(fmt.Sprintf("uncle[%d](%x) header invalid: %v", i, hash[:4], err))
}
}
@@ -325,7 +346,7 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
// TODO: remove backward compatibility
var (
- parent = sm.bc.GetBlock(block.Header().ParentHash)
+ parent = sm.bc.GetBlock(block.ParentHash())
state = state.New(parent.Root(), sm.db)
)
@@ -336,19 +357,22 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
// See YP section 4.3.4. "Block Header Validity"
// Validates a block. Returns an error if the block is invalid.
-func ValidateHeader(pow pow.PoW, block, parent *types.Header, checkPow bool) error {
+func ValidateHeader(pow pow.PoW, block *types.Header, parent *types.Block, checkPow bool) error {
if big.NewInt(int64(len(block.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Block extra data too long (%d)", len(block.Extra))
}
- expd := CalcDifficulty(block, parent)
+ expd := CalcDifficulty(int64(block.Time), int64(parent.Time()), parent.Difficulty())
if expd.Cmp(block.Difficulty) != 0 {
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
}
- a := new(big.Int).Sub(block.GasLimit, parent.GasLimit)
+ var a, b *big.Int
+ a = parent.GasLimit()
+ a = a.Sub(a, block.GasLimit)
a.Abs(a)
- b := new(big.Int).Div(parent.GasLimit, params.GasLimitBoundDivisor)
+ b = parent.GasLimit()
+ b = b.Div(b, params.GasLimitBoundDivisor)
if !(a.Cmp(b) < 0) || (block.GasLimit.Cmp(params.MinGasLimit) == -1) {
return fmt.Errorf("GasLimit check failed for block %v (%v > %v)", block.GasLimit, a, b)
}
@@ -357,11 +381,13 @@ func ValidateHeader(pow pow.PoW, block, parent *types.Header, checkPow bool) err
return BlockFutureErr
}
- if new(big.Int).Sub(block.Number, parent.Number).Cmp(big.NewInt(1)) != 0 {
+ num := parent.Number()
+ num.Sub(block.Number, num)
+ if num.Cmp(big.NewInt(1)) != 0 {
return BlockNumberErr
}
- if block.Time <= parent.Time {
+ if block.Time <= uint64(parent.Time()) {
return BlockEqualTSErr //ValidationError("Block timestamp equal or less than previous block (%v - %v)", block.Time, parent.Time)
}
@@ -375,26 +401,6 @@ func ValidateHeader(pow pow.PoW, block, parent *types.Header, checkPow bool) err
return nil
}
-func AccumulateRewards(statedb *state.StateDB, block *types.Block) {
- reward := new(big.Int).Set(BlockReward)
-
- for _, uncle := range block.Uncles() {
- num := new(big.Int).Add(big.NewInt(8), uncle.Number)
- num.Sub(num, block.Number())
-
- r := new(big.Int)
- r.Mul(BlockReward, num)
- r.Div(r, big.NewInt(8))
-
- statedb.AddBalance(uncle.Coinbase, r)
-
- reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
- }
-
- // Get the account associated with the coinbase
- statedb.AddBalance(block.Header().Coinbase, reward)
-}
-
func getBlockReceipts(db common.Database, bhash common.Hash) (receipts types.Receipts, err error) {
var rdata []byte
rdata, err = db.Get(append(receiptsPre, bhash[:]...))
diff --git a/core/block_processor_test.go b/core/block_processor_test.go
index e38c815ef..dc328a3ea 100644
--- a/core/block_processor_test.go
+++ b/core/block_processor_test.go
@@ -26,20 +26,19 @@ func proc() (*BlockProcessor, *ChainManager) {
}
func TestNumber(t *testing.T) {
- _, chain := proc()
- block1 := chain.NewBlock(common.Address{})
- block1.Header().Number = big.NewInt(3)
- block1.Header().Time--
-
pow := ezp.New()
+ _, chain := proc()
- err := ValidateHeader(pow, block1.Header(), chain.Genesis().Header(), false)
+ statedb := state.New(chain.Genesis().Root(), chain.stateDb)
+ header := makeHeader(chain.Genesis(), statedb)
+ header.Number = big.NewInt(3)
+ err := ValidateHeader(pow, header, chain.Genesis(), false)
if err != BlockNumberErr {
- t.Errorf("expected block number error %v", err)
+ t.Errorf("expected block number error, got %q", err)
}
- block1 = chain.NewBlock(common.Address{})
- err = ValidateHeader(pow, block1.Header(), chain.Genesis().Header(), false)
+ header = makeHeader(chain.Genesis(), statedb)
+ err = ValidateHeader(pow, header, chain.Genesis(), false)
if err == BlockNumberErr {
t.Errorf("didn't expect block number error")
}
diff --git a/core/canary.go b/core/canary.go
new file mode 100644
index 000000000..de77c4bba
--- /dev/null
+++ b/core/canary.go
@@ -0,0 +1,28 @@
+package core
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/state"
+)
+
+var (
+ jeff = common.HexToAddress("9d38997c624a71b21278389ea2fdc460d000e4b2")
+ vitalik = common.HexToAddress("b1e570be07eaa673e4fd0c8265b64ef739385709")
+ christoph = common.HexToAddress("529bc43a5d93789fa28de1961db6a07e752204ae")
+ gav = common.HexToAddress("e3e942b2aa524293c84ff6c7f87a6635790ad5e4")
+)
+
+// Canary will check the 0'd address of the 4 contracts above.
+// If two or more are set to anything other than a 0 the canary
+// dies a horrible death.
+func Canary(statedb *state.StateDB) bool {
+ r := new(big.Int)
+ r.Add(r, statedb.GetState(jeff, common.Hash{}).Big())
+ r.Add(r, statedb.GetState(vitalik, common.Hash{}).Big())
+ r.Add(r, statedb.GetState(christoph, common.Hash{}).Big())
+ r.Add(r, statedb.GetState(gav, common.Hash{}).Big())
+
+ return r.Cmp(big.NewInt(1)) > 0
+}
diff --git a/core/chain_makers.go b/core/chain_makers.go
index 76acfd6ca..72ae7970e 100644
--- a/core/chain_makers.go
+++ b/core/chain_makers.go
@@ -1,7 +1,6 @@
package core
import (
- "fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
@@ -11,7 +10,8 @@ import (
"github.com/ethereum/go-ethereum/pow"
)
-// So we can generate blocks easily
+// FakePow is a non-validating proof of work implementation.
+// It returns true from Verify for any block.
type FakePow struct{}
func (f FakePow) Search(block pow.Block, stop <-chan struct{}) (uint64, []byte) {
@@ -23,81 +23,125 @@ func (f FakePow) Turbo(bool) {}
// So we can deterministically seed different blockchains
var (
- CanonicalSeed = 1
- ForkSeed = 2
+ canonicalSeed = 1
+ forkSeed = 2
)
-// Utility functions for making chains on the fly
-// Exposed for sake of testing from other packages (eg. go-ethash)
-func NewBlockFromParent(addr common.Address, parent *types.Block) *types.Block {
- return newBlockFromParent(addr, parent)
+// BlockGen creates blocks for testing.
+// See GenerateChain for a detailed explanation.
+type BlockGen struct {
+ i int
+ parent *types.Block
+ chain []*types.Block
+ header *types.Header
+ statedb *state.StateDB
+
+ coinbase *state.StateObject
+ txs []*types.Transaction
+ receipts []*types.Receipt
+ uncles []*types.Header
}
-func MakeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Database, seed int) *types.Block {
- return makeBlock(bman, parent, i, db, seed)
-}
-
-func MakeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks {
- return makeChain(bman, parent, max, db, seed)
+// SetCoinbase sets the coinbase of the generated block.
+// It can be called at most once.
+func (b *BlockGen) SetCoinbase(addr common.Address) {
+ if b.coinbase != nil {
+ if len(b.txs) > 0 {
+ panic("coinbase must be set before adding transactions")
+ }
+ panic("coinbase can only be set once")
+ }
+ b.header.Coinbase = addr
+ b.coinbase = b.statedb.GetOrNewStateObject(addr)
+ b.coinbase.SetGasLimit(b.header.GasLimit)
}
-func NewChainMan(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager {
- return newChainManager(block, eventMux, db)
+// SetExtra sets the extra data field of the generated block.
+func (b *BlockGen) SetExtra(data []byte) {
+ b.header.Extra = data
}
-func NewBlockProc(db common.Database, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
- return newBlockProcessor(db, cman, eventMux)
+// AddTx adds a transaction to the generated block. If no coinbase has
+// been set, the block's coinbase is set to the zero address.
+//
+// AddTx panics if the transaction cannot be executed. In addition to
+// the protocol-imposed limitations (gas limit, etc.), there are some
+// further limitations on the content of transactions that can be
+// added. Notably, contract code relying on the BLOCKHASH instruction
+// will panic during execution.
+func (b *BlockGen) AddTx(tx *types.Transaction) {
+ if b.coinbase == nil {
+ b.SetCoinbase(common.Address{})
+ }
+ _, gas, err := ApplyMessage(NewEnv(b.statedb, nil, tx, b.header), tx, b.coinbase)
+ if err != nil {
+ panic(err)
+ }
+ b.statedb.Update()
+ b.header.GasUsed.Add(b.header.GasUsed, gas)
+ receipt := types.NewReceipt(b.statedb.Root().Bytes(), b.header.GasUsed)
+ logs := b.statedb.GetLogs(tx.Hash())
+ receipt.SetLogs(logs)
+ receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
+ b.txs = append(b.txs, tx)
+ b.receipts = append(b.receipts, receipt)
}
-func NewCanonical(n int, db common.Database) (*BlockProcessor, error) {
- return newCanonical(n, db)
+// TxNonce returns the next valid transaction nonce for the
+// account at addr. It panics if the account does not exist.
+func (b *BlockGen) TxNonce(addr common.Address) uint64 {
+ if !b.statedb.HasAccount(addr) {
+ panic("account does not exist")
+ }
+ return b.statedb.GetNonce(addr)
}
-// block time is fixed at 10 seconds
-func newBlockFromParent(addr common.Address, parent *types.Block) *types.Block {
- block := types.NewBlock(parent.Hash(), addr, parent.Root(), common.BigPow(2, 32), 0, nil)
- block.SetUncles(nil)
- block.SetTransactions(nil)
- block.SetReceipts(nil)
-
- header := block.Header()
- header.Difficulty = CalcDifficulty(block.Header(), parent.Header())
- header.Number = new(big.Int).Add(parent.Header().Number, common.Big1)
- header.Time = parent.Header().Time + 10
- header.GasLimit = CalcGasLimit(parent)
-
- block.Td = parent.Td
-
- return block
+// AddUncle adds an uncle header to the generated block.
+func (b *BlockGen) AddUncle(h *types.Header) {
+ b.uncles = append(b.uncles, h)
}
-// Actually make a block by simulating what miner would do
-// we seed chains by the first byte of the coinbase
-func makeBlock(bman *BlockProcessor, parent *types.Block, i int, db common.Database, seed int) *types.Block {
- var addr common.Address
- addr[0], addr[19] = byte(seed), byte(i)
- block := newBlockFromParent(addr, parent)
- state := state.New(block.Root(), db)
- cbase := state.GetOrNewStateObject(addr)
- cbase.SetGasLimit(CalcGasLimit(parent))
- cbase.AddBalance(BlockReward)
- state.Update()
- block.SetRoot(state.Root())
- return block
+// PrevBlock returns a previously generated block by number. It panics if
+// num is greater or equal to the number of the block being generated.
+// For index -1, PrevBlock returns the parent block given to GenerateChain.
+func (b *BlockGen) PrevBlock(index int) *types.Block {
+ if index >= b.i {
+ panic("block index out of range")
+ }
+ if index == -1 {
+ return b.parent
+ }
+ return b.chain[index]
}
-// Make a chain with real blocks
-// Runs ProcessWithParent to get proper state roots
-func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Database, seed int) types.Blocks {
- bman.bc.currentBlock = parent
- blocks := make(types.Blocks, max)
- for i := 0; i < max; i++ {
- block := makeBlock(bman, parent, i, db, seed)
- _, err := bman.processWithParent(block, parent)
- if err != nil {
- fmt.Println("process with parent failed", err)
- panic(err)
+// GenerateChain creates a chain of n blocks. The first block's
+// parent will be the provided parent. db is used to store
+// intermediate states and should contain the parent's state trie.
+//
+// The generator function is called with a new block generator for
+// every block. Any transactions and uncles added to the generator
+// become part of the block. If gen is nil, the blocks will be empty
+// and their coinbase will be the zero address.
+//
+// Blocks created by GenerateChain do not contain valid proof of work
+// values. Inserting them into ChainManager requires use of FakePow or
+// a similar non-validating proof of work implementation.
+func GenerateChain(parent *types.Block, db common.Database, n int, gen func(int, *BlockGen)) []*types.Block {
+ statedb := state.New(parent.Root(), db)
+ blocks := make(types.Blocks, n)
+ genblock := func(i int, h *types.Header) *types.Block {
+ b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb}
+ if gen != nil {
+ gen(i, b)
}
+ AccumulateRewards(statedb, h, b.uncles)
+ statedb.Update()
+ h.Root = statedb.Root()
+ return types.NewBlock(h, b.txs, b.uncles, b.receipts)
+ }
+ for i := 0; i < n; i++ {
+ header := makeHeader(parent, statedb)
+ block := genblock(i, header)
block.Td = CalcTD(block, parent)
blocks[i] = block
parent = block
@@ -105,41 +149,38 @@ func makeChain(bman *BlockProcessor, parent *types.Block, max int, db common.Dat
return blocks
}
-// Create a new chain manager starting from given block
-// Effectively a fork factory
-func newChainManager(block *types.Block, eventMux *event.TypeMux, db common.Database) *ChainManager {
- genesis := GenesisBlock(0, db)
- bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: eventMux, pow: FakePow{}}
- bc.txState = state.ManageState(state.New(genesis.Root(), db))
- bc.futureBlocks = NewBlockCache(1000)
- if block == nil {
- bc.Reset()
- } else {
- bc.currentBlock = block
- bc.td = block.Td
+func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
+ time := parent.Time() + 10 // block time is fixed at 10 seconds
+ return &types.Header{
+ Root: state.Root(),
+ ParentHash: parent.Hash(),
+ Coinbase: parent.Coinbase(),
+ Difficulty: CalcDifficulty(time, parent.Time(), parent.Difficulty()),
+ GasLimit: CalcGasLimit(parent),
+ GasUsed: new(big.Int),
+ Number: new(big.Int).Add(parent.Number(), common.Big1),
+ Time: uint64(time),
}
- return bc
}
-// block processor with fake pow
-func newBlockProcessor(db common.Database, cman *ChainManager, eventMux *event.TypeMux) *BlockProcessor {
- chainMan := newChainManager(nil, eventMux, db)
- bman := NewBlockProcessor(db, db, FakePow{}, chainMan, eventMux)
- return bman
-}
-
-// Make a new, deterministic canonical chain by running InsertChain
-// on result of makeChain
+// newCanonical creates a new deterministic canonical chain by running
+// InsertChain on the result of makeChain.
func newCanonical(n int, db common.Database) (*BlockProcessor, error) {
- eventMux := &event.TypeMux{}
-
- bman := newBlockProcessor(db, newChainManager(nil, eventMux, db), eventMux)
+ evmux := &event.TypeMux{}
+ chainman, _ := NewChainManager(GenesisBlock(0, db), db, db, FakePow{}, evmux)
+ bman := NewBlockProcessor(db, db, FakePow{}, chainman, evmux)
bman.bc.SetProcessor(bman)
parent := bman.bc.CurrentBlock()
if n == 0 {
return bman, nil
}
- lchain := makeChain(bman, parent, n, db, CanonicalSeed)
+ lchain := makeChain(parent, n, db, canonicalSeed)
_, err := bman.bc.InsertChain(lchain)
return bman, err
}
+
+func makeChain(parent *types.Block, n int, db common.Database, seed int) []*types.Block {
+ return GenerateChain(parent, db, n, func(i int, b *BlockGen) {
+ b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
+ })
+}
diff --git a/core/chain_makers_test.go b/core/chain_makers_test.go
new file mode 100644
index 000000000..d5125e1c3
--- /dev/null
+++ b/core/chain_makers_test.go
@@ -0,0 +1,78 @@
+package core
+
+import (
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+func ExampleGenerateChain() {
+ var (
+ key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
+ key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
+ addr1 = crypto.PubkeyToAddress(key1.PublicKey)
+ addr2 = crypto.PubkeyToAddress(key2.PublicKey)
+ addr3 = crypto.PubkeyToAddress(key3.PublicKey)
+ db, _ = ethdb.NewMemDatabase()
+ )
+
+ // Ensure that key1 has some funds in the genesis block.
+ genesis := GenesisBlockForTesting(db, addr1, big.NewInt(1000000))
+
+ // This call generates a chain of 5 blocks. The function runs for
+ // each block and adds different features to gen based on the
+ // block index.
+ chain := GenerateChain(genesis, db, 5, func(i int, gen *BlockGen) {
+ switch i {
+ case 0:
+ // In block 1, addr1 sends addr2 some ether.
+ tx, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(10000), params.TxGas, nil, nil).SignECDSA(key1)
+ gen.AddTx(tx)
+ case 1:
+ // In block 2, addr1 sends some more ether to addr2.
+ // addr2 passes it on to addr3.
+ tx1, _ := types.NewTransaction(gen.TxNonce(addr1), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key1)
+ tx2, _ := types.NewTransaction(gen.TxNonce(addr2), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2)
+ gen.AddTx(tx1)
+ gen.AddTx(tx2)
+ case 2:
+ // Block 3 is empty but was mined by addr3.
+ gen.SetCoinbase(addr3)
+ gen.SetExtra([]byte("yeehaw"))
+ case 3:
+ // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data).
+ b2 := gen.PrevBlock(1).Header()
+ b2.Extra = []byte("foo")
+ gen.AddUncle(b2)
+ b3 := gen.PrevBlock(2).Header()
+ b3.Extra = []byte("foo")
+ gen.AddUncle(b3)
+ }
+ })
+
+ // Import the chain. This runs all block validation rules.
+ evmux := &event.TypeMux{}
+ chainman, _ := NewChainManager(genesis, db, db, FakePow{}, evmux)
+ chainman.SetProcessor(NewBlockProcessor(db, db, FakePow{}, chainman, evmux))
+ if i, err := chainman.InsertChain(chain); err != nil {
+ fmt.Printf("insert error (block %d): %v\n", i, err)
+ return
+ }
+
+ state := chainman.State()
+ fmt.Printf("last block: #%d\n", chainman.CurrentBlock().Number())
+ fmt.Println("balance of addr1:", state.GetBalance(addr1))
+ fmt.Println("balance of addr2:", state.GetBalance(addr2))
+ fmt.Println("balance of addr3:", state.GetBalance(addr3))
+ // Output:
+ // last block: #5
+ // balance of addr1: 989000
+ // balance of addr2: 10000
+ // balance of addr3: 5906250000000001000
+}
diff --git a/core/chain_manager.go b/core/chain_manager.go
index 3b9b7517b..fc1922b3b 100644
--- a/core/chain_manager.go
+++ b/core/chain_manager.go
@@ -11,15 +11,19 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/compression/rle"
"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/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"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/hashicorp/golang-lru"
"github.com/rcrowley/go-metrics"
+ "github.com/syndtr/goleveldb/leveldb"
)
var (
@@ -33,35 +37,40 @@ var (
)
const (
- blockCacheLimit = 10000
+ blockCacheLimit = 256
maxFutureBlocks = 256
maxTimeFutureBlocks = 30
)
-func CalcDifficulty(block, parent *types.Header) *big.Int {
+// CalcDifficulty is the difficulty adjustment algorithm. It returns
+// the difficulty that a new block b should have when created at time
+// given the parent block's time and difficulty.
+func CalcDifficulty(time int64, parentTime int64, parentDiff *big.Int) *big.Int {
diff := new(big.Int)
-
- adjust := new(big.Int).Div(parent.Difficulty, params.DifficultyBoundDivisor)
- if big.NewInt(int64(block.Time)-int64(parent.Time)).Cmp(params.DurationLimit) < 0 {
- diff.Add(parent.Difficulty, adjust)
+ adjust := new(big.Int).Div(parentDiff, params.DifficultyBoundDivisor)
+ if big.NewInt(time-parentTime).Cmp(params.DurationLimit) < 0 {
+ diff.Add(parentDiff, adjust)
} else {
- diff.Sub(parent.Difficulty, adjust)
+ diff.Sub(parentDiff, adjust)
}
-
if diff.Cmp(params.MinimumDifficulty) < 0 {
return params.MinimumDifficulty
}
-
return diff
}
+// CalcTD computes the total difficulty of block.
func CalcTD(block, parent *types.Block) *big.Int {
if parent == nil {
return block.Difficulty()
}
- return new(big.Int).Add(parent.Td, block.Header().Difficulty)
+ d := block.Difficulty()
+ d.Add(d, parent.Td)
+ return d
}
+// CalcGasLimit computes the gas limit of the next block after parent.
+// The result may be modified by the caller.
func CalcGasLimit(parent *types.Block) *big.Int {
decay := new(big.Int).Div(parent.GasLimit(), params.GasLimitBoundDivisor)
contrib := new(big.Int).Mul(parent.GasUsed(), big.NewInt(3))
@@ -71,11 +80,11 @@ func CalcGasLimit(parent *types.Block) *big.Int {
gl := new(big.Int).Sub(parent.GasLimit(), decay)
gl = gl.Add(gl, contrib)
gl = gl.Add(gl, big.NewInt(1))
- gl = common.BigMax(gl, params.MinGasLimit)
+ gl.Set(common.BigMax(gl, params.MinGasLimit))
if gl.Cmp(params.GenesisGasLimit) < 0 {
- gl2 := new(big.Int).Add(parent.GasLimit(), decay)
- return common.BigMin(params.GenesisGasLimit, gl2)
+ gl.Add(parent.GasLimit(), decay)
+ gl.Set(common.BigMin(gl, params.GenesisGasLimit))
}
return gl
}
@@ -100,8 +109,9 @@ type ChainManager struct {
transState *state.StateDB
txState *state.ManagedState
- cache *BlockCache
- futureBlocks *BlockCache
+ cache *lru.Cache // cache is the LRU caching
+ futureBlocks *lru.Cache // future blocks are blocks added for later processing
+ pendingBlocks *lru.Cache // pending blocks contain blocks not yet written to the db
quit chan struct{}
// procInterrupt must be atomically called
@@ -112,13 +122,14 @@ type ChainManager struct {
}
func NewChainManager(genesis *types.Block, blockDb, stateDb common.Database, pow pow.PoW, mux *event.TypeMux) (*ChainManager, error) {
+ cache, _ := lru.New(blockCacheLimit)
bc := &ChainManager{
blockDb: blockDb,
stateDb: stateDb,
genesisBlock: GenesisBlock(42, stateDb),
eventMux: mux,
quit: make(chan struct{}),
- cache: NewBlockCache(blockCacheLimit),
+ cache: cache,
pow: pow,
}
// Check the genesis block given to the chain manager. If the genesis block mismatches block number 0
@@ -147,7 +158,7 @@ func NewChainManager(genesis *types.Block, blockDb, stateDb common.Database, pow
// Take ownership of this particular state
bc.txState = state.ManageState(bc.State().Copy())
- bc.futureBlocks = NewBlockCache(maxFutureBlocks)
+ bc.futureBlocks, _ = lru.New(maxFutureBlocks)
bc.makeCache()
go bc.update()
@@ -159,11 +170,11 @@ func (bc *ChainManager) SetHead(head *types.Block) {
bc.mu.Lock()
defer bc.mu.Unlock()
- for block := bc.currentBlock; block != nil && block.Hash() != head.Hash(); block = bc.GetBlock(block.Header().ParentHash) {
+ for block := bc.currentBlock; block != nil && block.Hash() != head.Hash(); block = bc.GetBlock(block.ParentHash()) {
bc.removeBlock(block)
}
- bc.cache = NewBlockCache(blockCacheLimit)
+ bc.cache, _ = lru.New(blockCacheLimit)
bc.currentBlock = head
bc.makeCache()
@@ -251,65 +262,23 @@ func (bc *ChainManager) setLastState() {
}
func (bc *ChainManager) makeCache() {
- if bc.cache == nil {
- bc.cache = NewBlockCache(blockCacheLimit)
- }
+ bc.cache, _ = lru.New(blockCacheLimit)
// load in last `blockCacheLimit` - 1 blocks. Last block is the current.
- ancestors := bc.GetAncestors(bc.currentBlock, blockCacheLimit-1)
- ancestors = append(ancestors, bc.currentBlock)
- for _, block := range ancestors {
- bc.cache.Push(block)
+ bc.cache.Add(bc.genesisBlock.Hash(), bc.genesisBlock)
+ for _, block := range bc.GetBlocksFromHash(bc.currentBlock.Hash(), blockCacheLimit) {
+ bc.cache.Add(block.Hash(), block)
}
}
-// Block creation & chain handling
-func (bc *ChainManager) NewBlock(coinbase common.Address) *types.Block {
- bc.mu.RLock()
- defer bc.mu.RUnlock()
-
- var (
- root common.Hash
- parentHash common.Hash
- )
-
- if bc.currentBlock != nil {
- root = bc.currentBlock.Header().Root
- parentHash = bc.lastBlockHash
- }
-
- block := types.NewBlock(
- parentHash,
- coinbase,
- root,
- common.BigPow(2, 32),
- 0,
- nil)
- block.SetUncles(nil)
- block.SetTransactions(nil)
- block.SetReceipts(nil)
-
- parent := bc.currentBlock
- if parent != nil {
- header := block.Header()
- header.Difficulty = CalcDifficulty(block.Header(), parent.Header())
- header.Number = new(big.Int).Add(parent.Header().Number, common.Big1)
- header.GasLimit = CalcGasLimit(parent)
- }
-
- return block
-}
-
func (bc *ChainManager) Reset() {
bc.mu.Lock()
defer bc.mu.Unlock()
- for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
+ for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) {
bc.removeBlock(block)
}
- if bc.cache == nil {
- bc.cache = NewBlockCache(blockCacheLimit)
- }
+ bc.cache, _ = lru.New(blockCacheLimit)
// Prepare the genesis block
bc.write(bc.genesisBlock)
@@ -328,7 +297,7 @@ func (bc *ChainManager) ResetWithGenesisBlock(gb *types.Block) {
bc.mu.Lock()
defer bc.mu.Unlock()
- for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.Header().ParentHash) {
+ for block := bc.currentBlock; block != nil; block = bc.GetBlock(block.ParentHash()) {
bc.removeBlock(block)
}
@@ -393,15 +362,20 @@ func (bc *ChainManager) insert(block *types.Block) {
}
func (bc *ChainManager) write(block *types.Block) {
- enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block))
- key := append(blockHashPre, block.Hash().Bytes()...)
- err := bc.blockDb.Put(key, enc)
- if err != nil {
- glog.Fatal("db write fail:", err)
- }
+ tstart := time.Now()
+
+ go func() {
+ enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block))
+ key := append(blockHashPre, block.Hash().Bytes()...)
+ err := bc.blockDb.Put(key, enc)
+ if err != nil {
+ glog.Fatal("db write fail:", err)
+ }
+ }()
- // Push block to cache
- bc.cache.Push(block)
+ if glog.V(logger.Debug) {
+ glog.Infof("wrote block #%v %s. Took %v\n", block.Number(), common.PP(block.Hash().Bytes()), time.Since(tstart))
+ }
}
// Accessors
@@ -411,6 +385,16 @@ func (bc *ChainManager) Genesis() *types.Block {
// Block fetching methods
func (bc *ChainManager) HasBlock(hash common.Hash) bool {
+ if bc.cache.Contains(hash) {
+ return true
+ }
+
+ if bc.pendingBlocks != nil {
+ if _, exist := bc.pendingBlocks.Get(hash); exist {
+ return true
+ }
+ }
+
data, _ := bc.blockDb.Get(append(blockHashPre, hash[:]...))
return len(data) != 0
}
@@ -437,11 +421,15 @@ func (self *ChainManager) GetBlockHashesFromHash(hash common.Hash, max uint64) (
}
func (self *ChainManager) GetBlock(hash common.Hash) *types.Block {
- /*
- if block := self.cache.Get(hash); block != nil {
- return block
+ if block, ok := self.cache.Get(hash); ok {
+ return block.(*types.Block)
+ }
+
+ if self.pendingBlocks != nil {
+ if block, _ := self.pendingBlocks.Get(hash); block != nil {
+ return block.(*types.Block)
}
- */
+ }
data, _ := self.blockDb.Get(append(blockHashPre, hash[:]...))
if len(data) == 0 {
@@ -452,6 +440,10 @@ func (self *ChainManager) GetBlock(hash common.Hash) *types.Block {
glog.V(logger.Error).Infof("invalid block RLP for hash %x: %v", hash, err)
return nil
}
+
+ // Add the block to the cache
+ self.cache.Add(hash, (*types.Block)(&block))
+
return (*types.Block)(&block)
}
@@ -463,6 +455,19 @@ func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
}
+// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
+func (self *ChainManager) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
+ for i := 0; i < n; i++ {
+ block := self.GetBlock(hash)
+ if block == nil {
+ break
+ }
+ blocks = append(blocks, block)
+ hash = block.ParentHash()
+ }
+ return
+}
+
// non blocking version
func (self *ChainManager) getBlockByNumber(num uint64) *types.Block {
key, _ := self.blockDb.Get(append(blockNumPre, big.NewInt(int64(num)).Bytes()...))
@@ -482,45 +487,12 @@ func (self *ChainManager) GetUnclesInChain(block *types.Block, length int) (uncl
return
}
-func (self *ChainManager) GetAncestors(block *types.Block, length int) (blocks []*types.Block) {
- for i := 0; i < length; i++ {
- block = self.GetBlock(block.ParentHash())
- if block == nil {
- break
- }
-
- blocks = append(blocks, block)
- }
-
- return
-}
-
// setTotalDifficulty updates the TD of the chain manager. Note, this function
// assumes that the `mu` mutex is held!
func (bc *ChainManager) setTotalDifficulty(td *big.Int) {
bc.td = new(big.Int).Set(td)
}
-func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
- parent := self.GetBlock(block.Header().ParentHash)
- if parent == nil {
- return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.Header().ParentHash)
- }
-
- parentTd := parent.Td
-
- uncleDiff := new(big.Int)
- for _, uncle := range block.Uncles() {
- uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
- }
-
- td := new(big.Int)
- td = td.Add(parentTd, uncleDiff)
- td = td.Add(td, block.Header().Difficulty)
-
- return td, nil
-}
-
func (bc *ChainManager) Stop() {
close(bc.quit)
atomic.StoreInt32(&bc.procInterrupt, 1)
@@ -538,16 +510,94 @@ type queueEvent struct {
}
func (self *ChainManager) procFutureBlocks() {
- var blocks []*types.Block
- self.futureBlocks.Each(func(i int, block *types.Block) {
- blocks = append(blocks, block)
- })
+ blocks := make([]*types.Block, self.futureBlocks.Len())
+ for i, hash := range self.futureBlocks.Keys() {
+ block, _ := self.futureBlocks.Get(hash)
+ blocks[i] = block.(*types.Block)
+ }
if len(blocks) > 0 {
types.BlockBy(types.Number).Sort(blocks)
self.InsertChain(blocks)
}
}
+func (self *ChainManager) enqueueForWrite(block *types.Block) {
+ self.pendingBlocks.Add(block.Hash(), block)
+}
+
+func (self *ChainManager) flushQueuedBlocks() {
+ db, batchWrite := self.blockDb.(*ethdb.LDBDatabase)
+ batch := new(leveldb.Batch)
+ for _, key := range self.pendingBlocks.Keys() {
+ b, _ := self.pendingBlocks.Get(key)
+ block := b.(*types.Block)
+
+ enc, _ := rlp.EncodeToBytes((*types.StorageBlock)(block))
+ key := append(blockHashPre, block.Hash().Bytes()...)
+ if batchWrite {
+ batch.Put(key, rle.Compress(enc))
+ } else {
+ self.blockDb.Put(key, enc)
+ }
+ }
+
+ if batchWrite {
+ db.LDB().Write(batch, nil)
+ }
+}
+
+type writeStatus byte
+
+const (
+ nonStatTy writeStatus = iota
+ canonStatTy
+ splitStatTy
+ sideStatTy
+)
+
+func (self *ChainManager) WriteBlock(block *types.Block) (status writeStatus, err error) {
+ self.wg.Add(1)
+ defer self.wg.Done()
+
+ cblock := self.currentBlock
+ // Compare the TD of the last known block in the canonical chain to make sure it's greater.
+ // At this point it's possible that a different chain (fork) becomes the new canonical chain.
+ if block.Td.Cmp(self.Td()) > 0 {
+ // chain fork
+ if block.ParentHash() != cblock.Hash() {
+ // during split we merge two different chains and create the new canonical chain
+ err := self.merge(cblock, block)
+ if err != nil {
+ return nonStatTy, err
+ }
+
+ status = splitStatTy
+ }
+
+ self.mu.Lock()
+ self.setTotalDifficulty(block.Td)
+ self.insert(block)
+ self.mu.Unlock()
+
+ self.setTransState(state.New(block.Root(), self.stateDb))
+ self.txState.SetState(state.New(block.Root(), self.stateDb))
+
+ status = canonStatTy
+ } else {
+ status = sideStatTy
+ }
+
+ // Write block to database. Eventually we'll have to improve on this and throw away blocks that are
+ // not in the canonical chain.
+ self.mu.Lock()
+ self.enqueueForWrite(block)
+ self.mu.Unlock()
+ // Delete from future blocks
+ self.futureBlocks.Remove(block.Hash())
+
+ return
+}
+
// InsertChain will attempt to insert the given chain in to the canonical chain or, otherwise, create a fork. It an error is returned
// it will return the index number of the failing block as well an error describing what went wrong (for possible errors see core/errors.go).
func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
@@ -557,6 +607,8 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
self.chainmu.Lock()
defer self.chainmu.Unlock()
+ self.pendingBlocks, _ = lru.New(len(chain))
+
// A queued approach to delivering events. This is generally
// faster than direct delivery and requires much less mutex
// acquiring.
@@ -574,6 +626,7 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
// Start the parallel nonce verifier.
go verifyNonces(self.pow, chain, nonceQuit, nonceDone)
defer close(nonceQuit)
+ defer self.flushQueuedBlocks()
txcount := 0
for i, block := range chain {
@@ -621,15 +674,13 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
return i, fmt.Errorf("%v: BlockFutureErr, %v > %v", BlockFutureErr, block.Time(), max)
}
- block.SetQueued(true)
- self.futureBlocks.Push(block)
+ self.futureBlocks.Add(block.Hash(), block)
stats.queued++
continue
}
- if IsParentErr(err) && self.futureBlocks.Has(block.ParentHash()) {
- block.SetQueued(true)
- self.futureBlocks.Push(block)
+ if IsParentErr(err) && self.futureBlocks.Contains(block.ParentHash()) {
+ self.futureBlocks.Add(block.Hash(), block)
stats.queued++
continue
}
@@ -641,59 +692,29 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) {
txcount += len(block.Transactions())
- cblock := self.currentBlock
- // Compare the TD of the last known block in the canonical chain to make sure it's greater.
- // At this point it's possible that a different chain (fork) becomes the new canonical chain.
- if block.Td.Cmp(self.Td()) > 0 {
- // chain fork
- if block.ParentHash() != cblock.Hash() {
- // during split we merge two different chains and create the new canonical chain
- err := self.merge(cblock, block)
- if err != nil {
- return i, err
- }
-
- queue[i] = ChainSplitEvent{block, logs}
- queueEvent.splitCount++
- }
-
- self.mu.Lock()
- self.setTotalDifficulty(block.Td)
- self.insert(block)
- self.mu.Unlock()
-
- jsonlogger.LogJson(&logger.EthChainNewHead{
- BlockHash: block.Hash().Hex(),
- BlockNumber: block.Number(),
- ChainHeadHash: cblock.Hash().Hex(),
- BlockPrevHash: block.ParentHash().Hex(),
- })
-
- self.setTransState(state.New(block.Root(), self.stateDb))
- self.txState.SetState(state.New(block.Root(), self.stateDb))
-
- queue[i] = ChainEvent{block, block.Hash(), logs}
- queueEvent.canonicalCount++
-
+ // write the block to the chain and get the status
+ status, err := self.WriteBlock(block)
+ if err != nil {
+ return i, err
+ }
+ switch status {
+ case canonStatTy:
if glog.V(logger.Debug) {
glog.Infof("[%v] inserted block #%d (%d TXs %d UNCs) (%x...). Took %v\n", time.Now().UnixNano(), block.Number(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
}
- } else {
+ queue[i] = ChainEvent{block, block.Hash(), logs}
+ queueEvent.canonicalCount++
+ case sideStatTy:
if glog.V(logger.Detail) {
glog.Infof("inserted forked block #%d (TD=%v) (%d TXs %d UNCs) (%x...). Took %v\n", block.Number(), block.Difficulty(), len(block.Transactions()), len(block.Uncles()), block.Hash().Bytes()[0:4], time.Since(bstart))
}
-
queue[i] = ChainSideEvent{block, logs}
queueEvent.sideCount++
+ case splitStatTy:
+ queue[i] = ChainSplitEvent{block, logs}
+ queueEvent.splitCount++
}
- // Write block to database. Eventually we'll have to improve on this and throw away blocks that are
- // not in the canonical chain.
- self.write(block)
- // Delete from future blocks
- self.futureBlocks.Delete(block.Hash())
-
stats.processed++
- blockInsertTimer.UpdateSince(bstart)
}
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
@@ -752,9 +773,9 @@ func (self *ChainManager) diff(oldBlock, newBlock *types.Block) (types.Blocks, e
}
}
- if glog.V(logger.Info) {
+ if glog.V(logger.Debug) {
commonHash := commonBlock.Hash()
- glog.Infof("Fork detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4])
+ glog.Infof("Chain split detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4])
}
return newChain, nil
diff --git a/core/chain_manager_test.go b/core/chain_manager_test.go
index c56a3b3e1..8b3ea9e85 100644
--- a/core/chain_manager_test.go
+++ b/core/chain_manager_test.go
@@ -18,6 +18,7 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp"
+ "github.com/hashicorp/golang-lru"
)
func init() {
@@ -62,12 +63,11 @@ func testFork(t *testing.T, bman *BlockProcessor, i, N int, f func(td1, td2 *big
if bi1 != bi2 {
t.Fatal("chains do not have the same hash at height", i)
}
-
bman2.bc.SetProcessor(bman2)
// extend the fork
parent := bman2.bc.CurrentBlock()
- chainB := makeChain(bman2, parent, N, db, ForkSeed)
+ chainB := makeChain(parent, N, db, forkSeed)
_, err = bman2.bc.InsertChain(chainB)
if err != nil {
t.Fatal("Insert chain error for fork:", err)
@@ -109,7 +109,8 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
bman.bc.mu.Lock()
{
- bman.bc.write(block)
+ bman.bc.enqueueForWrite(block)
+ //bman.bc.write(block)
}
bman.bc.mu.Unlock()
}
@@ -117,7 +118,7 @@ func testChain(chainB types.Blocks, bman *BlockProcessor) (*big.Int, error) {
}
func loadChain(fn string, t *testing.T) (types.Blocks, error) {
- fh, err := os.OpenFile(filepath.Join(os.Getenv("GOPATH"), "src", "github.com", "ethereum", "go-ethereum", "_data", fn), os.O_RDONLY, os.ModePerm)
+ fh, err := os.OpenFile(filepath.Join("..", "_data", fn), os.O_RDONLY, os.ModePerm)
if err != nil {
return nil, err
}
@@ -256,7 +257,7 @@ func TestBrokenChain(t *testing.T) {
}
bman2.bc.SetProcessor(bman2)
parent := bman2.bc.CurrentBlock()
- chainB := makeChain(bman2, parent, 5, db2, ForkSeed)
+ chainB := makeChain(parent, 5, db2, forkSeed)
chainB = chainB[1:]
_, err = testChain(chainB, bman)
if err == nil {
@@ -265,7 +266,7 @@ func TestBrokenChain(t *testing.T) {
}
func TestChainInsertions(t *testing.T) {
- t.Skip() // travil fails.
+ t.Skip("Skipped: outdated test files")
db, _ := ethdb.NewMemDatabase()
@@ -303,7 +304,7 @@ func TestChainInsertions(t *testing.T) {
}
func TestChainMultipleInsertions(t *testing.T) {
- t.Skip() // travil fails.
+ t.Skip("Skipped: outdated test files")
db, _ := ethdb.NewMemDatabase()
@@ -346,8 +347,8 @@ func TestChainMultipleInsertions(t *testing.T) {
}
}
-func TestGetAncestors(t *testing.T) {
- t.Skip() // travil fails.
+func TestGetBlocksFromHash(t *testing.T) {
+ t.Skip("Skipped: outdated test files")
db, _ := ethdb.NewMemDatabase()
chainMan := theChainManager(db, t)
@@ -361,8 +362,8 @@ func TestGetAncestors(t *testing.T) {
chainMan.write(block)
}
- ancestors := chainMan.GetAncestors(chain[len(chain)-1], 4)
- fmt.Println(ancestors)
+ blocks := chainMan.GetBlocksFromHash(chain[len(chain)-1].Hash(), 4)
+ fmt.Println(blocks)
}
type bproc struct{}
@@ -372,15 +373,17 @@ func (bproc) Process(*types.Block) (state.Logs, error) { return nil, nil }
func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block {
var chain []*types.Block
for i, difficulty := range d {
- header := &types.Header{Number: big.NewInt(int64(i + 1)), Difficulty: big.NewInt(int64(difficulty))}
- block := types.NewBlockWithHeader(header)
- copy(block.HeaderHash[:2], []byte{byte(i + 1), seed})
+ header := &types.Header{
+ Coinbase: common.Address{seed},
+ Number: big.NewInt(int64(i + 1)),
+ Difficulty: big.NewInt(int64(difficulty)),
+ }
if i == 0 {
- block.ParentHeaderHash = genesis.Hash()
+ header.ParentHash = genesis.Hash()
} else {
- copy(block.ParentHeaderHash[:2], []byte{byte(i), seed})
+ header.ParentHash = chain[i-1].Hash()
}
-
+ block := types.NewBlockWithHeader(header)
chain = append(chain, block)
}
return chain
@@ -389,8 +392,8 @@ func makeChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Block
func chm(genesis *types.Block, db common.Database) *ChainManager {
var eventMux event.TypeMux
bc := &ChainManager{blockDb: db, stateDb: db, genesisBlock: genesis, eventMux: &eventMux, pow: FakePow{}}
- bc.cache = NewBlockCache(100)
- bc.futureBlocks = NewBlockCache(100)
+ bc.cache, _ = lru.New(100)
+ bc.futureBlocks, _ = lru.New(100)
bc.processor = bproc{}
bc.ResetWithGenesisBlock(genesis)
bc.txState = state.ManageState(bc.State())
@@ -399,7 +402,6 @@ func chm(genesis *types.Block, db common.Database) *ChainManager {
}
func TestReorgLongest(t *testing.T) {
- t.Skip("skipped while cache is removed")
db, _ := ethdb.NewMemDatabase()
genesis := GenesisBlock(0, db)
bc := chm(genesis, db)
@@ -419,7 +421,6 @@ func TestReorgLongest(t *testing.T) {
}
func TestReorgShortest(t *testing.T) {
- t.Skip("skipped while cache is removed")
db, _ := ethdb.NewMemDatabase()
genesis := GenesisBlock(0, db)
bc := chm(genesis, db)
@@ -444,7 +445,7 @@ func TestInsertNonceError(t *testing.T) {
genesis := GenesisBlock(0, db)
bc := chm(genesis, db)
bc.processor = NewBlockProcessor(db, db, bc.pow, bc, bc.eventMux)
- blocks := makeChain(bc.processor.(*BlockProcessor), bc.currentBlock, i, db, 0)
+ blocks := makeChain(bc.currentBlock, i, db, 0)
fail := rand.Int() % len(blocks)
failblock := blocks[fail]
diff --git a/core/genesis.go b/core/genesis.go
index dd894e0b0..df13466ec 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -3,6 +3,7 @@ package core
import (
"encoding/json"
"fmt"
+ "math/big"
"os"
"github.com/ethereum/go-ethereum/common"
@@ -11,38 +12,18 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-/*
- * This is the special genesis block.
- */
-
-var ZeroHash256 = make([]byte, 32)
-var ZeroHash160 = make([]byte, 20)
-var ZeroHash512 = make([]byte, 64)
-
+// GenesisBlock creates a genesis block with the given nonce.
func GenesisBlock(nonce uint64, db common.Database) *types.Block {
- genesis := types.NewBlock(common.Hash{}, common.Address{}, common.Hash{}, params.GenesisDifficulty, nonce, nil)
- genesis.Header().Number = common.Big0
- genesis.Header().GasLimit = params.GenesisGasLimit
- genesis.Header().GasUsed = common.Big0
- genesis.Header().Time = 0
-
- genesis.Td = common.Big0
-
- genesis.SetUncles([]*types.Header{})
- genesis.SetTransactions(types.Transactions{})
- genesis.SetReceipts(types.Receipts{})
-
var accounts map[string]struct {
Balance string
Code string
}
err := json.Unmarshal(GenesisAccounts, &accounts)
if err != nil {
- fmt.Println("enable to decode genesis json data:", err)
+ fmt.Println("unable to decode genesis json data:", err)
os.Exit(1)
}
-
- statedb := state.New(genesis.Root(), db)
+ statedb := state.New(common.Hash{}, db)
for addr, account := range accounts {
codedAddr := common.Hex2Bytes(addr)
accountState := statedb.CreateAccount(common.BytesToAddress(codedAddr))
@@ -51,10 +32,15 @@ func GenesisBlock(nonce uint64, db common.Database) *types.Block {
statedb.UpdateStateObject(accountState)
}
statedb.Sync()
- genesis.Header().Root = statedb.Root()
- genesis.Td = params.GenesisDifficulty
- return genesis
+ block := types.NewBlock(&types.Header{
+ Difficulty: params.GenesisDifficulty,
+ GasLimit: params.GenesisGasLimit,
+ Nonce: types.EncodeNonce(nonce),
+ Root: statedb.Root(),
+ }, nil, nil, nil)
+ block.Td = params.GenesisDifficulty
+ return block
}
var GenesisAccounts = []byte(`{
@@ -71,3 +57,20 @@ var GenesisAccounts = []byte(`{
"e6716f9544a56c530d868e4bfbacb172315bdead": {"balance": "1606938044258990275541962092341162602522202993782792835301376"},
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4": {"balance": "1606938044258990275541962092341162602522202993782792835301376"}
}`)
+
+// GenesisBlockForTesting creates a block in which addr has the given wei balance.
+// The state trie of the block is written to db.
+func GenesisBlockForTesting(db common.Database, addr common.Address, balance *big.Int) *types.Block {
+ statedb := state.New(common.Hash{}, db)
+ obj := statedb.GetOrNewStateObject(addr)
+ obj.SetBalance(balance)
+ statedb.Update()
+ statedb.Sync()
+ block := types.NewBlock(&types.Header{
+ Difficulty: params.GenesisDifficulty,
+ GasLimit: params.GenesisGasLimit,
+ Root: statedb.Root(),
+ }, nil, nil, nil)
+ block.Td = params.GenesisDifficulty
+ return block
+}
diff --git a/core/state_transition.go b/core/state_transition.go
index 915dd466b..e2212dfef 100644
--- a/core/state_transition.go
+++ b/core/state_transition.go
@@ -13,10 +13,6 @@ import (
"github.com/ethereum/go-ethereum/params"
)
-const tryJit = false
-
-var ()
-
/*
* The State transitioning model
*
@@ -69,20 +65,24 @@ func MessageCreatesContract(msg Message) bool {
return msg.To() == nil
}
-func MessageGasValue(msg Message) *big.Int {
- return new(big.Int).Mul(msg.Gas(), msg.GasPrice())
-}
-
-func IntrinsicGas(msg Message) *big.Int {
+// IntrinsicGas computes the 'intrisic gas' for a message
+// with the given data.
+func IntrinsicGas(data []byte) *big.Int {
igas := new(big.Int).Set(params.TxGas)
- for _, byt := range msg.Data() {
- if byt != 0 {
- igas.Add(igas, params.TxDataNonZeroGas)
- } else {
- igas.Add(igas, params.TxDataZeroGas)
+ if len(data) > 0 {
+ var nz int64
+ for _, byt := range data {
+ if byt != 0 {
+ nz++
+ }
}
+ m := big.NewInt(nz)
+ m.Mul(m, params.TxDataNonZeroGas)
+ igas.Add(igas, m)
+ m.SetInt64(int64(len(data)) - nz)
+ m.Mul(m, params.TxDataZeroGas)
+ igas.Add(igas, m)
}
-
return igas
}
@@ -96,7 +96,7 @@ func NewStateTransition(env vm.Environment, msg Message, coinbase *state.StateOb
env: env,
msg: msg,
gas: new(big.Int),
- gasPrice: new(big.Int).Set(msg.GasPrice()),
+ gasPrice: msg.GasPrice(),
initialGas: new(big.Int),
value: msg.Value(),
data: msg.Data(),
@@ -140,26 +140,22 @@ func (self *StateTransition) AddGas(amount *big.Int) {
}
func (self *StateTransition) BuyGas() error {
- var err error
+ mgas := self.msg.Gas()
+ mgval := new(big.Int).Mul(mgas, self.gasPrice)
sender, err := self.From()
if err != nil {
return err
}
- if sender.Balance().Cmp(MessageGasValue(self.msg)) < 0 {
- return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], MessageGasValue(self.msg), sender.Balance())
+ if sender.Balance().Cmp(mgval) < 0 {
+ return fmt.Errorf("insufficient ETH for gas (%x). Req %v, has %v", sender.Address().Bytes()[:4], mgval, sender.Balance())
}
-
- coinbase := self.Coinbase()
- err = coinbase.SubGas(self.msg.Gas(), self.msg.GasPrice())
- if err != nil {
+ if err = self.Coinbase().SubGas(mgas, self.gasPrice); err != nil {
return err
}
-
- self.AddGas(self.msg.Gas())
- self.initialGas.Set(self.msg.Gas())
- sender.SubBalance(MessageGasValue(self.msg))
-
+ self.AddGas(mgas)
+ self.initialGas.Set(mgas)
+ sender.SubBalance(mgval)
return nil
}
@@ -195,14 +191,14 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
sender, _ := self.From() // err checked in preCheck
// Pay intrinsic gas
- if err = self.UseGas(IntrinsicGas(self.msg)); err != nil {
+ if err = self.UseGas(IntrinsicGas(self.data)); err != nil {
return nil, nil, InvalidTxError(err)
}
vmenv := self.env
var ref vm.ContextRef
if MessageCreatesContract(msg) {
- ret, err, ref = vmenv.Create(sender, self.msg.Data(), self.gas, self.gasPrice, self.value)
+ ret, err, ref = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
if err == nil {
dataGas := big.NewInt(int64(len(ret)))
dataGas.Mul(dataGas, params.CreateDataGas)
@@ -216,7 +212,7 @@ func (self *StateTransition) transitionState() (ret []byte, usedGas *big.Int, er
} else {
// Increment the nonce for the next transaction
self.state.SetNonce(sender.Address(), sender.Nonce()+1)
- ret, err = vmenv.Call(sender, self.To().Address(), self.msg.Data(), self.gas, self.gasPrice, self.value)
+ ret, err = vmenv.Call(sender, self.To().Address(), self.data, self.gas, self.gasPrice, self.value)
}
if err != nil && IsValueTransferErr(err) {
@@ -237,15 +233,15 @@ func (self *StateTransition) refundGas() {
coinbase := self.Coinbase()
sender, _ := self.From() // err already checked
// Return remaining gas
- remaining := new(big.Int).Mul(self.gas, self.msg.GasPrice())
+ remaining := new(big.Int).Mul(self.gas, self.gasPrice)
sender.AddBalance(remaining)
- uhalf := new(big.Int).Div(self.gasUsed(), common.Big2)
+ uhalf := remaining.Div(self.gasUsed(), common.Big2)
refund := common.BigMin(uhalf, self.state.Refunds())
self.gas.Add(self.gas, refund)
- self.state.AddBalance(sender.Address(), refund.Mul(refund, self.msg.GasPrice()))
+ self.state.AddBalance(sender.Address(), refund.Mul(refund, self.gasPrice))
- coinbase.AddGas(self.gas, self.msg.GasPrice())
+ coinbase.AddGas(self.gas, self.gasPrice)
}
func (self *StateTransition) gasUsed() *big.Int {
diff --git a/core/transaction_pool.go b/core/transaction_pool.go
index 34a1733d7..bf28647c3 100644
--- a/core/transaction_pool.go
+++ b/core/transaction_pool.go
@@ -162,27 +162,25 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
// Check the transaction doesn't exceed the current
// block limit gas.
- if pool.gasLimit().Cmp(tx.GasLimit) < 0 {
+ if pool.gasLimit().Cmp(tx.Gas()) < 0 {
return ErrGasLimit
}
// Transactions can't be negative. This may never happen
// using RLP decoded transactions but may occur if you create
// a transaction using the RPC for example.
- if tx.Amount.Cmp(common.Big0) < 0 {
+ if tx.Value().Cmp(common.Big0) < 0 {
return ErrNegativeValue
}
// Transactor should have enough funds to cover the costs
// cost == V + GP * GL
- total := new(big.Int).Mul(tx.Price, tx.GasLimit)
- total.Add(total, tx.Value())
- if pool.currentState().GetBalance(from).Cmp(total) < 0 {
+ if pool.currentState().GetBalance(from).Cmp(tx.Cost()) < 0 {
return ErrInsufficientFunds
}
// Should supply enough intrinsic gas
- if tx.GasLimit.Cmp(IntrinsicGas(tx)) < 0 {
+ if tx.Gas().Cmp(IntrinsicGas(tx.Data())) < 0 {
return ErrIntrinsicGas
}
@@ -238,7 +236,7 @@ func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Trans
// Increment the nonce on the pending state. This can only happen if
// the nonce is +1 to the previous one.
- pool.pendingState.SetNonce(addr, tx.AccountNonce+1)
+ pool.pendingState.SetNonce(addr, tx.Nonce()+1)
// Notify the subscribers. This event is posted in a goroutine
// because it's possible that somewhere during the post "Remove transaction"
// gets called which will then wait for the global tx pool lock and deadlock.
@@ -341,7 +339,7 @@ func (pool *TxPool) checkQueue() {
trueNonce := pool.currentState().GetNonce(address)
addq := addq[:0]
for hash, tx := range txs {
- if tx.AccountNonce < trueNonce {
+ if tx.Nonce() < trueNonce {
// Drop queued transactions whose nonce is lower than
// the account nonce because they have been processed.
delete(txs, hash)
@@ -362,8 +360,7 @@ func (pool *TxPool) checkQueue() {
delete(pool.queue[address], e.hash)
continue
}
-
- if e.AccountNonce > guessedNonce {
+ if e.Nonce() > guessedNonce {
break
}
delete(txs, e.hash)
@@ -418,4 +415,4 @@ type txQueueEntry struct {
func (q txQueue) Len() int { return len(q) }
func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
-func (q txQueue) Less(i, j int) bool { return q[i].AccountNonce < q[j].AccountNonce }
+func (q txQueue) Less(i, j int) bool { return q[i].Nonce() < q[j].Nonce() }
diff --git a/core/transaction_pool_test.go b/core/transaction_pool_test.go
index b763c196d..ff8b9c730 100644
--- a/core/transaction_pool_test.go
+++ b/core/transaction_pool_test.go
@@ -13,8 +13,9 @@ import (
"github.com/ethereum/go-ethereum/event"
)
-func transaction() *types.Transaction {
- return types.NewTransactionMessage(common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(100), nil)
+func transaction(nonce uint64, gaslimit *big.Int, key *ecdsa.PrivateKey) *types.Transaction {
+ tx, _ := types.NewTransaction(nonce, common.Address{}, big.NewInt(100), gaslimit, big.NewInt(1), nil).SignECDSA(key)
+ return tx
}
func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
@@ -29,43 +30,34 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
func TestInvalidTransactions(t *testing.T) {
pool, key := setupTxPool()
- tx := transaction()
- tx.SignECDSA(key)
- err := pool.Add(tx)
- if err != ErrNonExistentAccount {
+ tx := transaction(0, big.NewInt(100), key)
+ if err := pool.Add(tx); err != ErrNonExistentAccount {
t.Error("expected", ErrNonExistentAccount)
}
from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1))
- err = pool.Add(tx)
- if err != ErrInsufficientFunds {
+ if err := pool.Add(tx); err != ErrInsufficientFunds {
t.Error("expected", ErrInsufficientFunds)
}
balance := new(big.Int).Add(tx.Value(), new(big.Int).Mul(tx.Gas(), tx.GasPrice()))
pool.currentState().AddBalance(from, balance)
- err = pool.Add(tx)
- if err != ErrIntrinsicGas {
+ if err := pool.Add(tx); err != ErrIntrinsicGas {
t.Error("expected", ErrIntrinsicGas, "got", err)
}
pool.currentState().SetNonce(from, 1)
pool.currentState().AddBalance(from, big.NewInt(0xffffffffffffff))
- tx.GasLimit = big.NewInt(100000)
- tx.Price = big.NewInt(1)
- tx.SignECDSA(key)
-
- err = pool.Add(tx)
- if err != ErrNonce {
+ tx = transaction(0, big.NewInt(100000), key)
+ if err := pool.Add(tx); err != ErrNonce {
t.Error("expected", ErrNonce)
}
}
func TestTransactionQueue(t *testing.T) {
pool, key := setupTxPool()
- tx := transaction()
- tx.SignECDSA(key)
+ tx := transaction(0, big.NewInt(100), key)
from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1))
pool.queueTx(tx.Hash(), tx)
@@ -75,9 +67,7 @@ func TestTransactionQueue(t *testing.T) {
t.Error("expected valid txs to be 1 is", len(pool.pending))
}
- tx = transaction()
- tx.SetNonce(1)
- tx.SignECDSA(key)
+ tx = transaction(1, big.NewInt(100), key)
from, _ = tx.From()
pool.currentState().SetNonce(from, 2)
pool.queueTx(tx.Hash(), tx)
@@ -91,12 +81,9 @@ func TestTransactionQueue(t *testing.T) {
}
pool, key = setupTxPool()
- tx1, tx2, tx3 := transaction(), transaction(), transaction()
- tx2.SetNonce(10)
- tx3.SetNonce(11)
- tx1.SignECDSA(key)
- tx2.SignECDSA(key)
- tx3.SignECDSA(key)
+ tx1 := transaction(0, big.NewInt(100), key)
+ tx2 := transaction(10, big.NewInt(100), key)
+ tx3 := transaction(11, big.NewInt(100), key)
pool.queueTx(tx1.Hash(), tx1)
pool.queueTx(tx2.Hash(), tx2)
pool.queueTx(tx3.Hash(), tx3)
@@ -114,8 +101,7 @@ func TestTransactionQueue(t *testing.T) {
func TestRemoveTx(t *testing.T) {
pool, key := setupTxPool()
- tx := transaction()
- tx.SignECDSA(key)
+ tx := transaction(0, big.NewInt(100), key)
from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1))
pool.queueTx(tx.Hash(), tx)
@@ -142,13 +128,10 @@ func TestRemoveTx(t *testing.T) {
func TestNegativeValue(t *testing.T) {
pool, key := setupTxPool()
- tx := transaction()
- tx.Value().Set(big.NewInt(-1))
- tx.SignECDSA(key)
+ tx, _ := types.NewTransaction(0, common.Address{}, big.NewInt(-1), big.NewInt(100), big.NewInt(1), nil).SignECDSA(key)
from, _ := tx.From()
pool.currentState().AddBalance(from, big.NewInt(1))
- err := pool.Add(tx)
- if err != ErrNegativeValue {
+ if err := pool.Add(tx); err != ErrNegativeValue {
t.Error("expected", ErrNegativeValue, "got", err)
}
}
@@ -165,20 +148,15 @@ func TestTransactionChainFork(t *testing.T) {
}
resetState()
- tx := transaction()
- tx.GasLimit = big.NewInt(100000)
- tx.SignECDSA(key)
-
- err := pool.add(tx)
- if err != nil {
+ tx := transaction(0, big.NewInt(100000), key)
+ if err := pool.add(tx); err != nil {
t.Error("didn't expect error", err)
}
pool.RemoveTransactions([]*types.Transaction{tx})
// reset the pool's internal state
resetState()
- err = pool.add(tx)
- if err != nil {
+ if err := pool.add(tx); err != nil {
t.Error("didn't expect error", err)
}
}
@@ -195,24 +173,14 @@ func TestTransactionDoubleNonce(t *testing.T) {
}
resetState()
- tx := transaction()
- tx.GasLimit = big.NewInt(100000)
- tx.SignECDSA(key)
-
- err := pool.add(tx)
- if err != nil {
+ tx := transaction(0, big.NewInt(100000), key)
+ tx2 := transaction(0, big.NewInt(1000000), key)
+ if err := pool.add(tx); err != nil {
t.Error("didn't expect error", err)
}
-
- tx2 := transaction()
- tx2.GasLimit = big.NewInt(1000000)
- tx2.SignECDSA(key)
-
- err = pool.add(tx2)
- if err != nil {
+ if err := pool.add(tx2); err != nil {
t.Error("didn't expect error", err)
}
-
if len(pool.pending) != 2 {
t.Error("expected 2 pending txs. Got", len(pool.pending))
}
@@ -222,20 +190,13 @@ func TestMissingNonce(t *testing.T) {
pool, key := setupTxPool()
addr := crypto.PubkeyToAddress(key.PublicKey)
pool.currentState().AddBalance(addr, big.NewInt(100000000000000))
- tx := transaction()
- tx.AccountNonce = 1
- tx.GasLimit = big.NewInt(100000)
- tx.SignECDSA(key)
-
- err := pool.add(tx)
- if err != nil {
+ tx := transaction(1, big.NewInt(100000), key)
+ if err := pool.add(tx); err != nil {
t.Error("didn't expect error", err)
}
-
if len(pool.pending) != 0 {
t.Error("expected 0 pending transactions, got", len(pool.pending))
}
-
if len(pool.queue[addr]) != 1 {
t.Error("expected 1 queued transaction, got", len(pool.queue[addr]))
}
diff --git a/core/types/block.go b/core/types/block.go
index d7963981e..b7eb700ca 100644
--- a/core/types/block.go
+++ b/core/types/block.go
@@ -8,6 +8,7 @@ import (
"io"
"math/big"
"sort"
+ "sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -15,71 +16,59 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
-type Header struct {
- // Hash to the previous block
- ParentHash common.Hash
- // Uncles of this block
- UncleHash common.Hash
- // The coin base address
- Coinbase common.Address
- // Block Trie state
- Root common.Hash
- // Tx sha
- TxHash common.Hash
- // Receipt sha
- ReceiptHash common.Hash
- // Bloom
- Bloom Bloom
- // Difficulty for the current block
- Difficulty *big.Int
- // The block number
- Number *big.Int
- // Gas limit
- GasLimit *big.Int
- // Gas used
- GasUsed *big.Int
- // Creation time
- Time uint64
- // Extra data
- Extra []byte
- // Mix digest for quick checking to prevent DOS
- MixDigest common.Hash
- // Nonce
- Nonce [8]byte
-}
+// A BlockNonce is a 64-bit hash which proves (combined with the
+// mix-hash) that a suffcient amount of computation has been carried
+// out on a block.
+type BlockNonce [8]byte
-func (self *Header) Hash() common.Hash {
- return rlpHash(self.rlpData(true))
+func EncodeNonce(i uint64) BlockNonce {
+ var n BlockNonce
+ binary.BigEndian.PutUint64(n[:], i)
+ return n
}
-func (self *Header) HashNoNonce() common.Hash {
- return rlpHash(self.rlpData(false))
+func (n BlockNonce) Uint64() uint64 {
+ return binary.BigEndian.Uint64(n[:])
}
-func (self *Header) rlpData(withNonce bool) []interface{} {
- fields := []interface{}{
- self.ParentHash,
- self.UncleHash,
- self.Coinbase,
- self.Root,
- self.TxHash,
- self.ReceiptHash,
- self.Bloom,
- self.Difficulty,
- self.Number,
- self.GasLimit,
- self.GasUsed,
- self.Time,
- self.Extra,
- }
- if withNonce {
- fields = append(fields, self.MixDigest, self.Nonce)
- }
- return fields
-}
-
-func (self *Header) RlpData() interface{} {
- return self.rlpData(true)
+type Header struct {
+ ParentHash common.Hash // Hash to the previous block
+ UncleHash common.Hash // Uncles of this block
+ Coinbase common.Address // The coin base address
+ Root common.Hash // Block Trie state
+ TxHash common.Hash // Tx sha
+ ReceiptHash common.Hash // Receipt sha
+ Bloom Bloom // Bloom
+ Difficulty *big.Int // Difficulty for the current block
+ Number *big.Int // The block number
+ GasLimit *big.Int // Gas limit
+ GasUsed *big.Int // Gas used
+ Time uint64 // Creation time
+ Extra []byte // Extra data
+ MixDigest common.Hash // for quick difficulty verification
+ Nonce BlockNonce
+}
+
+func (h *Header) Hash() common.Hash {
+ return rlpHash(h)
+}
+
+func (h *Header) HashNoNonce() common.Hash {
+ return rlpHash([]interface{}{
+ h.ParentHash,
+ h.UncleHash,
+ h.Coinbase,
+ h.Root,
+ h.TxHash,
+ h.ReceiptHash,
+ h.Bloom,
+ h.Difficulty,
+ h.Number,
+ h.GasLimit,
+ h.GasUsed,
+ h.Time,
+ h.Extra,
+ })
}
func (h *Header) UnmarshalJSON(data []byte) error {
@@ -112,20 +101,21 @@ func rlpHash(x interface{}) (h common.Hash) {
}
type Block struct {
- // Preset Hash for mock (Tests)
- HeaderHash common.Hash
- ParentHeaderHash common.Hash
- // ^^^^ ignore ^^^^
-
header *Header
uncles []*Header
transactions Transactions
- Td *big.Int
- queued bool // flag for blockpool to skip TD check
+ receipts Receipts
- ReceivedAt time.Time
+ // 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
- receipts Receipts
+ // ReceivedAt is used by package eth to track block propagation time.
+ ReceivedAt time.Time
}
// StorageBlock defines the RLP encoding of a Block stored in the
@@ -148,43 +138,90 @@ type storageblock struct {
TD *big.Int
}
-func NewBlock(parentHash common.Hash, coinbase common.Address, root common.Hash, difficulty *big.Int, nonce uint64, extra []byte) *Block {
- header := &Header{
- Root: root,
- ParentHash: parentHash,
- Coinbase: coinbase,
- Difficulty: difficulty,
- Time: uint64(time.Now().Unix()),
- Extra: extra,
- GasUsed: new(big.Int),
- GasLimit: new(big.Int),
- Number: new(big.Int),
+var (
+ emptyRootHash = DeriveSha(Transactions{})
+ emptyUncleHash = CalcUncleHash(nil)
+)
+
+// 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)
}
- header.SetNonce(nonce)
- block := &Block{header: header}
- block.Td = new(big.Int)
- return block
-}
+ if len(receipts) == 0 {
+ b.header.ReceiptHash = emptyRootHash
+ } else {
+ b.header.ReceiptHash = DeriveSha(Receipts(receipts))
+ b.header.Bloom = CreateBloom(receipts)
+ b.receipts = make([]*Receipt, len(receipts))
+ copy(b.receipts, 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])
+ }
+ }
-func (self *Header) SetNonce(nonce uint64) {
- binary.BigEndian.PutUint64(self.Nonce[:], nonce)
+ 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: header}
+ return &Block{header: copyHeader(header)}
}
-func (self *Block) ValidateFields() error {
- if self.header == nil {
+func copyHeader(h *Header) *Header {
+ cpy := *h
+ 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 cpy.GasLimit = new(big.Int); h.GasLimit != nil {
+ cpy.GasLimit.Set(h.GasLimit)
+ }
+ if cpy.GasUsed = new(big.Int); h.GasUsed != nil {
+ cpy.GasUsed.Set(h.GasUsed)
+ }
+ if len(h.Extra) > 0 {
+ cpy.Extra = make([]byte, len(h.Extra))
+ copy(cpy.Extra, h.Extra)
+ }
+ return &cpy
+}
+
+func (b *Block) ValidateFields() error {
+ if b.header == nil {
return fmt.Errorf("header is nil")
}
- for i, transaction := range self.transactions {
+ for i, transaction := range b.transactions {
if transaction == nil {
return fmt.Errorf("transaction %d is nil", i)
}
}
- for i, uncle := range self.uncles {
+ for i, uncle := range b.uncles {
if uncle == nil {
return fmt.Errorf("uncle %d is nil", i)
}
@@ -192,64 +229,50 @@ func (self *Block) ValidateFields() error {
return nil
}
-func (self *Block) DecodeRLP(s *rlp.Stream) error {
+func (b *Block) DecodeRLP(s *rlp.Stream) error {
var eb extblock
+ _, size, _ := s.Kind()
if err := s.Decode(&eb); err != nil {
return err
}
- self.header, self.uncles, self.transactions = eb.Header, eb.Uncles, eb.Txs
+ b.header, b.uncles, b.transactions = eb.Header, eb.Uncles, eb.Txs
+ b.size.Store(common.StorageSize(rlp.ListSize(size)))
return nil
}
-func (self Block) EncodeRLP(w io.Writer) error {
+func (b Block) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, extblock{
- Header: self.header,
- Txs: self.transactions,
- Uncles: self.uncles,
+ Header: b.header,
+ Txs: b.transactions,
+ Uncles: b.uncles,
})
}
-func (self *StorageBlock) DecodeRLP(s *rlp.Stream) error {
+func (b *StorageBlock) DecodeRLP(s *rlp.Stream) error {
var sb storageblock
if err := s.Decode(&sb); err != nil {
return err
}
- self.header, self.uncles, self.transactions, self.Td = sb.Header, sb.Uncles, sb.Txs, sb.TD
+ b.header, b.uncles, b.transactions, b.Td = sb.Header, sb.Uncles, sb.Txs, sb.TD
return nil
}
-func (self StorageBlock) EncodeRLP(w io.Writer) error {
+func (b StorageBlock) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, storageblock{
- Header: self.header,
- Txs: self.transactions,
- Uncles: self.uncles,
- TD: self.Td,
+ Header: b.header,
+ Txs: b.transactions,
+ Uncles: b.uncles,
+ TD: b.Td,
})
}
-func (self *Block) Header() *Header {
- return self.header
-}
-
-func (self *Block) Uncles() []*Header {
- return self.uncles
-}
-
-func (self *Block) CalculateUnclesHash() common.Hash {
- return rlpHash(self.uncles)
-}
+// TODO: copies
+func (b *Block) Uncles() []*Header { return b.uncles }
+func (b *Block) Transactions() Transactions { return b.transactions }
+func (b *Block) Receipts() Receipts { return b.receipts }
-func (self *Block) SetUncles(uncleHeaders []*Header) {
- self.uncles = uncleHeaders
- self.header.UncleHash = rlpHash(uncleHeaders)
-}
-
-func (self *Block) Transactions() Transactions {
- return self.transactions
-}
-
-func (self *Block) Transaction(hash common.Hash) *Transaction {
- for _, transaction := range self.transactions {
+func (b *Block) Transaction(hash common.Hash) *Transaction {
+ for _, transaction := range b.transactions {
if transaction.Hash() == hash {
return transaction
}
@@ -257,74 +280,37 @@ func (self *Block) Transaction(hash common.Hash) *Transaction {
return nil
}
-func (self *Block) SetTransactions(transactions Transactions) {
- self.transactions = transactions
- self.header.TxHash = DeriveSha(transactions)
-}
-func (self *Block) AddTransaction(transaction *Transaction) {
- self.transactions = append(self.transactions, transaction)
- self.SetTransactions(self.transactions)
-}
+func (b *Block) Number() *big.Int { return new(big.Int).Set(b.header.Number) }
+func (b *Block) GasLimit() *big.Int { return new(big.Int).Set(b.header.GasLimit) }
+func (b *Block) GasUsed() *big.Int { return new(big.Int).Set(b.header.GasUsed) }
+func (b *Block) Difficulty() *big.Int { return new(big.Int).Set(b.header.Difficulty) }
-func (self *Block) Receipts() Receipts {
- return self.receipts
-}
+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) Time() int64 { return int64(b.header.Time) }
+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 (self *Block) SetReceipts(receipts Receipts) {
- self.receipts = receipts
- self.header.ReceiptHash = DeriveSha(receipts)
- self.header.Bloom = CreateBloom(receipts)
-}
-func (self *Block) AddReceipt(receipt *Receipt) {
- self.receipts = append(self.receipts, receipt)
- self.SetReceipts(self.receipts)
-}
+func (b *Block) Header() *Header { return copyHeader(b.header) }
-func (self *Block) RlpData() interface{} {
- return []interface{}{self.header, self.transactions, self.uncles}
+func (b *Block) HashNoNonce() common.Hash {
+ return b.header.HashNoNonce()
}
-func (self *Block) RlpDataForStorage() interface{} {
- return []interface{}{self.header, self.transactions, self.uncles, self.Td /* TODO receipts */}
-}
-
-// Header accessors (add as you need them)
-func (self *Block) Number() *big.Int { return self.header.Number }
-func (self *Block) NumberU64() uint64 { return self.header.Number.Uint64() }
-func (self *Block) MixDigest() common.Hash { return self.header.MixDigest }
-func (self *Block) Nonce() uint64 {
- return binary.BigEndian.Uint64(self.header.Nonce[:])
-}
-func (self *Block) SetNonce(nonce uint64) {
- self.header.SetNonce(nonce)
-}
-
-func (self *Block) Queued() bool { return self.queued }
-func (self *Block) SetQueued(q bool) { self.queued = q }
-
-func (self *Block) Bloom() Bloom { return self.header.Bloom }
-func (self *Block) Coinbase() common.Address { return self.header.Coinbase }
-func (self *Block) Time() int64 { return int64(self.header.Time) }
-func (self *Block) GasLimit() *big.Int { return self.header.GasLimit }
-func (self *Block) GasUsed() *big.Int { return self.header.GasUsed }
-func (self *Block) Root() common.Hash { return self.header.Root }
-func (self *Block) SetRoot(root common.Hash) { self.header.Root = root }
-func (self *Block) GetTransaction(i int) *Transaction {
- if len(self.transactions) > i {
- return self.transactions[i]
- }
- return nil
-}
-func (self *Block) GetUncle(i int) *Header {
- if len(self.uncles) > i {
- return self.uncles[i]
+func (b *Block) Size() common.StorageSize {
+ if size := b.size.Load(); size != nil {
+ return size.(common.StorageSize)
}
- return nil
-}
-
-func (self *Block) Size() common.StorageSize {
c := writeCounter(0)
- rlp.Encode(&c, self)
+ rlp.Encode(&c, b)
+ b.size.Store(common.StorageSize(c))
return common.StorageSize(c)
}
@@ -335,48 +321,37 @@ func (c *writeCounter) Write(b []byte) (int, error) {
return len(b), nil
}
-// Implement pow.Block
-func (self *Block) Difficulty() *big.Int { return self.header.Difficulty }
-func (self *Block) HashNoNonce() common.Hash { return self.header.HashNoNonce() }
-
-func (self *Block) Hash() common.Hash {
- if (self.HeaderHash != common.Hash{}) {
- return self.HeaderHash
- } else {
- return self.header.Hash()
- }
+func CalcUncleHash(uncles []*Header) common.Hash {
+ return rlpHash(uncles)
}
-func (self *Block) ParentHash() common.Hash {
- if (self.ParentHeaderHash != common.Hash{}) {
- return self.ParentHeaderHash
- } else {
- return self.header.ParentHash
+// WithMiningResult returns a new block with the data from b
+// where nonce and mix digest are set to the provided values.
+func (b *Block) WithMiningResult(nonce uint64, mixDigest common.Hash) *Block {
+ cpy := *b.header
+ binary.BigEndian.PutUint64(cpy.Nonce[:], nonce)
+ cpy.MixDigest = mixDigest
+ return &Block{
+ header: &cpy,
+ transactions: b.transactions,
+ receipts: b.receipts,
+ uncles: b.uncles,
+ Td: b.Td,
}
}
-func (self *Block) Copy() *Block {
- block := NewBlock(self.header.ParentHash, self.Coinbase(), self.Root(), new(big.Int), self.Nonce(), self.header.Extra)
- block.header.Bloom = self.header.Bloom
- block.header.TxHash = self.header.TxHash
- block.transactions = self.transactions
- block.header.UncleHash = self.header.UncleHash
- block.uncles = self.uncles
- block.header.GasLimit.Set(self.header.GasLimit)
- block.header.GasUsed.Set(self.header.GasUsed)
- block.header.ReceiptHash = self.header.ReceiptHash
- block.header.Difficulty.Set(self.header.Difficulty)
- block.header.Number.Set(self.header.Number)
- block.header.Time = self.header.Time
- block.header.MixDigest = self.header.MixDigest
- if self.Td != nil {
- block.Td.Set(self.Td)
- }
+// Implement pow.Block
- return block
+func (b *Block) Hash() common.Hash {
+ if hash := b.hash.Load(); hash != nil {
+ return hash.(common.Hash)
+ }
+ v := rlpHash(b.header)
+ b.hash.Store(v)
+ return v
}
-func (self *Block) String() string {
+func (b *Block) String() string {
str := fmt.Sprintf(`Block(#%v): Size: %v TD: %v {
MinerHash: %x
%v
@@ -385,20 +360,11 @@ Transactions:
Uncles:
%v
}
-`, self.Number(), self.Size(), self.Td, self.header.HashNoNonce(), self.header, self.transactions, self.uncles)
-
- if (self.HeaderHash != common.Hash{}) {
- str += fmt.Sprintf("\nFake hash = %x", self.HeaderHash)
- }
-
- if (self.ParentHeaderHash != common.Hash{}) {
- str += fmt.Sprintf("\nFake parent hash = %x", self.ParentHeaderHash)
- }
-
+`, b.Number(), b.Size(), b.Td, b.header.HashNoNonce(), b.header, b.transactions, b.uncles)
return str
}
-func (self *Header) String() string {
+func (h *Header) String() string {
return fmt.Sprintf(`Header(%x):
[
ParentHash: %x
@@ -414,9 +380,9 @@ func (self *Header) String() string {
GasUsed: %v
Time: %v
Extra: %s
- MixDigest: %x
+ MixDigest: %x
Nonce: %x
-]`, self.Hash(), self.ParentHash, self.UncleHash, self.Coinbase, self.Root, self.TxHash, self.ReceiptHash, self.Bloom, self.Difficulty, self.Number, self.GasLimit, self.GasUsed, self.Time, self.Extra, self.MixDigest, self.Nonce)
+]`, h.Hash(), h.ParentHash, h.UncleHash, h.Coinbase, h.Root, h.TxHash, h.ReceiptHash, h.Bloom, h.Difficulty, h.Number, h.GasLimit, h.GasUsed, h.Time, h.Extra, h.MixDigest, h.Nonce)
}
type Blocks []*Block
@@ -442,4 +408,4 @@ func (self blockSorter) Swap(i, j int) {
}
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 }
+func Number(b1, b2 *Block) bool { return b1.header.Number.Cmp(b2.header.Number) < 0 }
diff --git a/core/types/block_test.go b/core/types/block_test.go
index b52ddffdc..03e6881be 100644
--- a/core/types/block_test.go
+++ b/core/types/block_test.go
@@ -13,7 +13,6 @@ import (
// 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)
@@ -35,20 +34,10 @@ func TestBlockEncoding(t *testing.T) {
check("Time", block.Time(), int64(1426516743))
check("Size", block.Size(), common.StorageSize(len(blockEnc)))
- to := common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87")
- check("Transactions", block.Transactions(), Transactions{
- {
- Payload: []byte{},
- Amount: big.NewInt(10),
- Price: big.NewInt(10),
- GasLimit: big.NewInt(50000),
- AccountNonce: 0,
- V: 27,
- R: common.String2Big("0x9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f"),
- S: common.String2Big("0x8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b1"),
- Recipient: &to,
- },
- })
+ tx1 := NewTransaction(0, common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"), big.NewInt(10), big.NewInt(50000), big.NewInt(10), nil)
+ tx1, _ = tx1.WithSignature(common.Hex2Bytes("9bea4c4daac7c7c52e093e6a4c35dbbcf8856f1af7b059ba20253e70848d094f8a8fae537ce25ed8cb5af9adac3f141af69bd515bd2ba031522df09b97dd72b100"))
+ 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 {
diff --git a/core/types/transaction.go b/core/types/transaction.go
index a03a6b847..95deac36e 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -4,7 +4,9 @@ import (
"crypto/ecdsa"
"errors"
"fmt"
+ "io"
"math/big"
+ "sync/atomic"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
@@ -18,38 +20,63 @@ func IsContractAddr(addr []byte) bool {
}
type Transaction struct {
- AccountNonce uint64
- Price *big.Int
- GasLimit *big.Int
- Recipient *common.Address `rlp:"nil"` // nil means contract creation
- Amount *big.Int
- Payload []byte
- V byte
- R, S *big.Int
+ data txdata
+ // caches
+ hash atomic.Value
+ size atomic.Value
+ from atomic.Value
}
-func NewContractCreationTx(amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
- return &Transaction{
- Recipient: nil,
- Amount: amount,
- GasLimit: gasLimit,
- Price: gasPrice,
- Payload: data,
- R: new(big.Int),
- S: new(big.Int),
+type txdata struct {
+ AccountNonce uint64
+ Price, GasLimit *big.Int
+ Recipient *common.Address `rlp:"nil"` // nil means contract creation
+ Amount *big.Int
+ Payload []byte
+ V byte // signature
+ R, S *big.Int // signature
+}
+
+func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
+ if len(data) > 0 {
+ data = common.CopyBytes(data)
}
+ return &Transaction{data: txdata{
+ AccountNonce: nonce,
+ Recipient: nil,
+ Amount: new(big.Int).Set(amount),
+ GasLimit: new(big.Int).Set(gasLimit),
+ Price: new(big.Int).Set(gasPrice),
+ Payload: data,
+ R: new(big.Int),
+ S: new(big.Int),
+ }}
}
-func NewTransactionMessage(to common.Address, amount, gasAmount, gasPrice *big.Int, data []byte) *Transaction {
- return &Transaction{
- Recipient: &to,
- Amount: amount,
- GasLimit: gasAmount,
- Price: gasPrice,
- Payload: data,
- R: new(big.Int),
- S: new(big.Int),
+func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, 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: new(big.Int),
+ Price: new(big.Int),
+ R: new(big.Int),
+ S: new(big.Int),
+ }
+ if amount != nil {
+ d.Amount.Set(amount)
+ }
+ if gasLimit != nil {
+ d.GasLimit.Set(gasLimit)
}
+ if gasPrice != nil {
+ d.Price.Set(gasPrice)
+ }
+ return &Transaction{data: d}
}
func NewTransactionFromBytes(data []byte) *Transaction {
@@ -61,112 +88,128 @@ func NewTransactionFromBytes(data []byte) *Transaction {
return tx
}
-func (tx *Transaction) Hash() common.Hash {
- return rlpHash([]interface{}{
- tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload,
- })
-}
-
-// Size returns the encoded RLP size of tx.
-func (self *Transaction) Size() common.StorageSize {
- c := writeCounter(0)
- rlp.Encode(&c, self)
- return common.StorageSize(c)
+func (tx *Transaction) EncodeRLP(w io.Writer) error {
+ return rlp.Encode(w, &tx.data)
}
-func (self *Transaction) Data() []byte {
- return self.Payload
-}
-
-func (self *Transaction) Gas() *big.Int {
- return self.GasLimit
+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
}
-func (self *Transaction) GasPrice() *big.Int {
- return self.Price
-}
+func (tx *Transaction) Data() []byte { return common.CopyBytes(tx.data.Payload) }
+func (tx *Transaction) Gas() *big.Int { return new(big.Int).Set(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 (self *Transaction) Value() *big.Int {
- return self.Amount
+func (tx *Transaction) To() *common.Address {
+ if tx.data.Recipient == nil {
+ return nil
+ } else {
+ to := *tx.data.Recipient
+ return &to
+ }
}
-func (self *Transaction) Nonce() uint64 {
- return self.AccountNonce
+func (tx *Transaction) Hash() common.Hash {
+ if hash := tx.hash.Load(); hash != nil {
+ return hash.(common.Hash)
+ }
+ v := rlpHash([]interface{}{
+ tx.data.AccountNonce,
+ tx.data.Price,
+ tx.data.GasLimit,
+ tx.data.Recipient,
+ tx.data.Amount,
+ tx.data.Payload,
+ })
+ tx.hash.Store(v)
+ return v
}
-func (self *Transaction) SetNonce(AccountNonce uint64) {
- self.AccountNonce = AccountNonce
+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)
}
-func (self *Transaction) From() (common.Address, error) {
- pubkey, err := self.PublicKey()
+func (tx *Transaction) From() (common.Address, error) {
+ if from := tx.from.Load(); from != nil {
+ return from.(common.Address), nil
+ }
+ pubkey, err := tx.publicKey()
if err != nil {
return common.Address{}, err
}
-
var addr common.Address
copy(addr[:], crypto.Sha3(pubkey[1:])[12:])
+ tx.from.Store(addr)
return addr, nil
}
-// To returns the recipient of the transaction.
-// If transaction is a contract creation (with no recipient address)
-// To returns nil.
-func (tx *Transaction) To() *common.Address {
- return tx.Recipient
+// Cost returns amount + gasprice * gaslimit.
+func (tx *Transaction) Cost() *big.Int {
+ total := new(big.Int).Mul(tx.data.Price, tx.data.GasLimit)
+ total.Add(total, tx.data.Amount)
+ return total
}
-func (tx *Transaction) GetSignatureValues() (v byte, r []byte, s []byte) {
- v = byte(tx.V)
- r = common.LeftPadBytes(tx.R.Bytes(), 32)
- s = common.LeftPadBytes(tx.S.Bytes(), 32)
- return
+func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) {
+ return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
}
-func (tx *Transaction) PublicKey() ([]byte, error) {
- if !crypto.ValidateSignatureValues(tx.V, tx.R, tx.S) {
+func (tx *Transaction) publicKey() ([]byte, error) {
+ if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S) {
return nil, errors.New("invalid v, r, s values")
}
- hash := tx.Hash()
- v, r, s := tx.GetSignatureValues()
- sig := append(r, s...)
- sig = append(sig, v-27)
+ // encode the signature in uncompressed format
+ r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
+ sig := make([]byte, 65)
+ copy(sig[32-len(r):32], r)
+ copy(sig[64-len(s):64], s)
+ sig[64] = tx.data.V - 27
- p, err := crypto.SigToPub(hash[:], sig)
+ // recover the public key from the signature
+ hash := tx.Hash()
+ pub, err := crypto.Ecrecover(hash[:], sig)
if err != nil {
glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err)
return nil, err
}
-
- pubkey := crypto.FromECDSAPub(p)
- if len(pubkey) == 0 || pubkey[0] != 4 {
+ if len(pub) == 0 || pub[0] != 4 {
return nil, errors.New("invalid public key")
}
- return pubkey, nil
+ return pub, nil
}
-func (tx *Transaction) SetSignatureValues(sig []byte) error {
- tx.R = common.Bytes2Big(sig[:32])
- tx.S = common.Bytes2Big(sig[32:64])
- tx.V = sig[64] + 27
- return nil
+func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) {
+ if len(sig) != 65 {
+ panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
+ }
+ cpy := &Transaction{data: tx.data}
+ cpy.data.R = new(big.Int).SetBytes(sig[:32])
+ cpy.data.S = new(big.Int).SetBytes(sig[32:64])
+ cpy.data.V = sig[64] + 27
+ return cpy, nil
}
-func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) error {
+func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) {
h := tx.Hash()
sig, err := crypto.Sign(h[:], prv)
if err != nil {
- return err
+ return nil, err
}
- tx.SetSignatureValues(sig)
- return nil
-}
-
-// TODO: remove
-func (tx *Transaction) RlpData() interface{} {
- data := []interface{}{tx.AccountNonce, tx.Price, tx.GasLimit, tx.Recipient, tx.Amount, tx.Payload}
- return append(data, tx.V, tx.R.Bytes(), tx.S.Bytes())
+ return tx.WithSignature(sig)
}
func (tx *Transaction) String() string {
@@ -176,12 +219,12 @@ func (tx *Transaction) String() string {
} else {
from = fmt.Sprintf("%x", f[:])
}
- if t := tx.To(); t == nil {
+ if tx.data.Recipient == nil {
to = "[contract creation]"
} else {
- to = fmt.Sprintf("%x", t[:])
+ to = fmt.Sprintf("%x", tx.data.Recipient[:])
}
- enc, _ := rlp.EncodeToBytes(tx)
+ enc, _ := rlp.EncodeToBytes(&tx.data)
return fmt.Sprintf(`
TX(%x)
Contract: %v
@@ -198,36 +241,24 @@ func (tx *Transaction) String() string {
Hex: %x
`,
tx.Hash(),
- len(tx.Recipient) == 0,
+ len(tx.data.Recipient) == 0,
from,
to,
- tx.AccountNonce,
- tx.Price,
- tx.GasLimit,
- tx.Amount,
- tx.Payload,
- tx.V,
- tx.R,
- tx.S,
+ tx.data.AccountNonce,
+ tx.data.Price,
+ tx.data.GasLimit,
+ tx.data.Amount,
+ tx.data.Payload,
+ tx.data.V,
+ tx.data.R,
+ tx.data.S,
enc,
)
}
-// Transaction slice type for basic sorting
+// Transaction slice type for basic sorting.
type Transactions []*Transaction
-// TODO: remove
-func (self Transactions) RlpData() interface{} {
- // Marshal the transactions of this block
- enc := make([]interface{}, len(self))
- for i, tx := range self {
- // Cast it to a string (safe)
- enc[i] = tx.RlpData()
- }
-
- return enc
-}
-
func (s Transactions) Len() int { return len(s) }
func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
@@ -239,5 +270,5 @@ func (s Transactions) GetRlp(i int) []byte {
type TxByNonce struct{ Transactions }
func (s TxByNonce) Less(i, j int) bool {
- return s.Transactions[i].AccountNonce < s.Transactions[j].AccountNonce
+ return s.Transactions[i].data.AccountNonce < s.Transactions[j].data.AccountNonce
}
diff --git a/core/types/transaction_test.go b/core/types/transaction_test.go
index 492059c28..dd9c5e87b 100644
--- a/core/types/transaction_test.go
+++ b/core/types/transaction_test.go
@@ -15,40 +15,35 @@ import (
// at github.com/ethereum/tests.
var (
- emptyTx = NewTransactionMessage(
+ emptyTx = NewTransaction(
+ 0,
common.HexToAddress("095e7baea6a6c7c4c2dfeb977efac326af552d87"),
big.NewInt(0), big.NewInt(0), big.NewInt(0),
nil,
)
- rightvrsRecipient = common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b")
- rightvrsTx = &Transaction{
- Recipient: &rightvrsRecipient,
- AccountNonce: 3,
- Price: big.NewInt(1),
- GasLimit: big.NewInt(2000),
- Amount: big.NewInt(10),
- Payload: common.FromHex("5544"),
- V: 28,
- R: common.String2Big("0x98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a"),
- S: common.String2Big("0x8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3"),
- }
+ rightvrsTx, _ = NewTransaction(
+ 3,
+ common.HexToAddress("b94f5374fce5edbc8e2a8697c15331677e6ebf0b"),
+ big.NewInt(10),
+ big.NewInt(2000),
+ big.NewInt(1),
+ common.FromHex("5544"),
+ ).WithSignature(
+ common.Hex2Bytes("98ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4a8887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a301"),
+ )
)
func TestTransactionHash(t *testing.T) {
- // "EmptyTransaction"
if emptyTx.Hash() != common.HexToHash("c775b99e7ad12f50d819fcd602390467e28141316969f4b57f0626f74fe3b386") {
t.Errorf("empty transaction hash mismatch, got %x", emptyTx.Hash())
}
-
- // "RightVRSTest"
if rightvrsTx.Hash() != common.HexToHash("fe7a79529ed5f7c3375d06b26b186a8644e0e16c373d7a12be41c62d6042b77a") {
t.Errorf("RightVRS transaction hash mismatch, got %x", rightvrsTx.Hash())
}
}
func TestTransactionEncode(t *testing.T) {
- // "RightVRSTest"
txb, err := rlp.EncodeToBytes(rightvrsTx)
if err != nil {
t.Fatalf("encode error: %v", err)
@@ -72,19 +67,16 @@ func defaultTestKey() (*ecdsa.PrivateKey, common.Address) {
func TestRecipientEmpty(t *testing.T) {
_, addr := defaultTestKey()
-
tx, err := decodeTx(common.Hex2Bytes("f8498080808080011ca09b16de9d5bdee2cf56c28d16275a4da68cd30273e2525f3959f5d62557489921a0372ebd8fb3345f7db7b5a86d42e24d36e983e259b0664ceb8c227ec9af572f3d"))
if err != nil {
t.Error(err)
t.FailNow()
}
-
from, err := tx.From()
if err != nil {
t.Error(err)
t.FailNow()
}
-
if addr != from {
t.Error("derived address doesn't match")
}
diff --git a/core/vm_env.go b/core/vm_env.go
index da862d5c8..6dd83acde 100644
--- a/core/vm_env.go
+++ b/core/vm_env.go
@@ -10,32 +10,32 @@ import (
)
type VMEnv struct {
- state *state.StateDB
- block *types.Block
- msg Message
- depth int
- chain *ChainManager
- typ vm.Type
+ state *state.StateDB
+ header *types.Header
+ msg Message
+ depth int
+ chain *ChainManager
+ typ vm.Type
// structured logging
logs []vm.StructLog
}
-func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, block *types.Block) *VMEnv {
+func NewEnv(state *state.StateDB, chain *ChainManager, msg Message, header *types.Header) *VMEnv {
return &VMEnv{
- chain: chain,
- state: state,
- block: block,
- msg: msg,
- typ: vm.StdVmTy,
+ chain: chain,
+ state: state,
+ header: header,
+ msg: msg,
+ typ: vm.StdVmTy,
}
}
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
-func (self *VMEnv) BlockNumber() *big.Int { return self.block.Number() }
-func (self *VMEnv) Coinbase() common.Address { return self.block.Coinbase() }
-func (self *VMEnv) Time() int64 { return self.block.Time() }
-func (self *VMEnv) Difficulty() *big.Int { return self.block.Difficulty() }
-func (self *VMEnv) GasLimit() *big.Int { return self.block.GasLimit() }
+func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
+func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
+func (self *VMEnv) Time() int64 { return int64(self.header.Time) }
+func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
+func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
func (self *VMEnv) State() *state.StateDB { return self.state }
func (self *VMEnv) Depth() int { return self.depth }