Merge pull request #194 from ethereum/poc8

Update tests branch to PoC8
This commit is contained in:
Taylor Gerring 2014-11-27 13:23:31 +01:00
commit ef7961b7d2
61 changed files with 5388 additions and 1139 deletions

View File

@ -36,65 +36,11 @@ Automated (dev) builds
* [Windows] Coming soon™
* [Linux] Coming soon™
Packages
Binaries
========
Ethereum Go is split up in several sub packages Please refer to each
individual package for more information.
1. [eth](https://github.com/ethereum/go-ethereum)
2. [ethchain](https://github.com/ethereum/go-ethereum/tree/master/ethchain)
3. [ethwire](https://github.com/ethereum/go-ethereum/tree/master/ethwire)
4. [ethdb](https://github.com/ethereum/go-ethereum/tree/master/ethdb)
5. [ethutil](https://github.com/ethereum/go-ethereum/tree/master/ethutil)
6. [ethpipe](https://github.com/ethereum/go-ethereum/tree/master/ethpipe)
7. [ethvm](https://github.com/ethereum/go-ethereum/tree/master/ethvm)
8. [ethtrie](https://github.com/ethereum/go-ethereum/tree/master/ethtrie)
9. [ethreact](https://github.com/ethereum/go-ethereum/tree/master/ethreact)
10. [ethlog](https://github.com/ethereum/go-ethereum/tree/master/ethlog)
The [eth](https://github.com/ethereum/go-ethereum) is the top-level package
of the Ethereum protocol. It functions as the Ethereum bootstrapping and
peer communication layer. The [ethchain](https://github.com/ethereum/go-ethereum/tree/master/ethchain)
contains the Ethereum blockchain, block manager, transaction and
transaction handlers. The [ethwire](https://github.com/ethereum/go-ethereum/tree/master/ethwire) contains
the Ethereum [wire protocol](http://wiki.ethereum.org/index.php/Wire_Protocol) which can be used
to hook in to the Ethereum network. [ethutil](https://github.com/ethereum/go-ethereum/tree/master/ethutil) contains
utility functions which are not Ethereum specific. The utility package
contains the [patricia trie](http://wiki.ethereum.org/index.php/Patricia_Tree),
[RLP Encoding](http://wiki.ethereum.org/index.php/RLP) and hex encoding
helpers. The [ethdb](https://github.com/ethereum/go-ethereum/tree/master/ethdb) package
contains the LevelDB interface and memory DB interface.
General command line options
============================
```
Shared between ethereum and Mist
-id Set the custom identifier of the client (shows up on other clients)
-port Port on which the server will accept incomming connections
-upnp Enable UPnP
-maxpeer Desired amount of peers
-rpc Start JSON RPC
-dir Data directory used to store configs and databases
-import Import a private key
-genaddr Generates a new address and private key (destructive action)
-h This
Ethereum only
ethereum [options] [filename]
-js Start the JavaScript REPL
filename Load the given file and interpret as JavaScript
-m Start mining blocks
Mist only
-asset_path absolute path to GUI assets directory
```
Tools
=====
Go Ethereum comes with several binaries:
Go Ethereum comes with several binaries found in
[cmd](https://github.com/ethereum/go-ethereum/tree/master/cmd):
* `mist` Official Ethereum Browser
* `ethereum` Ethereum CLI
@ -103,6 +49,38 @@ Go Ethereum comes with several binaries:
* `evm` is a generic Ethereum Virtual Machine: `evm -code 60ff60ff -gas
10000 -price 0 -dump`. See `-h` for a detailed description.
General command line options
============================
```
== Shared between ethereum and Mist ==
= Settings
-id Set the custom identifier of the client (shows up on other clients)
-port Port on which the server will accept incomming connections
-upnp Enable UPnP
-maxpeer Desired amount of peers
-rpc Start JSON RPC
-dir Data directory used to store configs and databases
= Utility
-h This
-import Import a private key
-genaddr Generates a new address and private key (destructive action)
-dump Dump a specific state of a block to stdout given the -number or -hash
-difftool Supress all output and prints VM output to stdout
-diff vm=only vm output, all=all output including state storage
Ethereum only
ethereum [options] [filename]
-js Start the JavaScript REPL
filename Load the given file and interpret as JavaScript
-m Start mining blocks
== Mist only ==
-asset_path absolute path to GUI assets directory
```
Contribution
============

View File

@ -10,8 +10,10 @@ import (
"time"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/wire"
)
@ -20,7 +22,7 @@ var poollogger = logger.NewLogger("BPOOL")
type block struct {
from *Peer
peer *Peer
block *chain.Block
block *types.Block
reqAt time.Time
requested int
}
@ -73,7 +75,7 @@ func (self *BlockPool) HasCommonHash(hash []byte) bool {
return self.eth.ChainManager().GetBlock(hash) != nil
}
func (self *BlockPool) Blocks() (blocks chain.Blocks) {
func (self *BlockPool) Blocks() (blocks types.Blocks) {
for _, item := range self.pool {
if item.block != nil {
blocks = append(blocks, item.block)
@ -123,15 +125,15 @@ func (self *BlockPool) AddHash(hash []byte, peer *Peer) {
}
}
func (self *BlockPool) Add(b *chain.Block, peer *Peer) {
func (self *BlockPool) Add(b *types.Block, peer *Peer) {
self.addBlock(b, peer, false)
}
func (self *BlockPool) AddNew(b *chain.Block, peer *Peer) {
func (self *BlockPool) AddNew(b *types.Block, peer *Peer) {
self.addBlock(b, peer, true)
}
func (self *BlockPool) addBlock(b *chain.Block, peer *Peer, newBlock bool) {
func (self *BlockPool) addBlock(b *types.Block, peer *Peer, newBlock bool) {
self.mut.Lock()
defer self.mut.Unlock()
@ -283,7 +285,7 @@ out:
break out
case <-procTimer.C:
blocks := self.Blocks()
chain.BlockBy(chain.Number).Sort(blocks)
types.BlockBy(types.Number).Sort(blocks)
// Find common block
for i, block := range blocks {
@ -309,10 +311,6 @@ out:
}
}
// TODO figure out whether we were catching up
// If caught up and just a new block has been propagated:
// sm.eth.EventMux().Post(NewBlockEvent{block})
// otherwise process and don't emit anything
if len(blocks) > 0 {
chainManager := self.eth.ChainManager()
// Test and import
@ -333,9 +331,14 @@ out:
self.td = ethutil.Big0
self.peer = nil
} else {
chainManager.InsertChain(bchain)
for _, block := range blocks {
self.Remove(block.Hash())
if !chain.IsTDError(err) {
chainManager.InsertChain(bchain, func(block *types.Block, messages state.Messages) {
self.eth.EventMux().Post(chain.NewBlockEvent{block})
self.eth.EventMux().Post(messages)
self.Remove(block.Hash())
})
}
}
}

View File

@ -9,6 +9,7 @@ import (
"sync"
"time"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
@ -69,7 +70,7 @@ type BlockManager struct {
// The last attempted block is mainly used for debugging purposes
// This does not have to be a valid block and will be set during
// 'Process' & canonical validation.
lastAttemptedBlock *Block
lastAttemptedBlock *types.Block
events event.Subscription
}
@ -117,11 +118,11 @@ func (sm *BlockManager) ChainManager() *ChainManager {
return sm.bc
}
func (self *BlockManager) ProcessTransactions(coinbase *state.StateObject, state *state.State, block, parent *Block, txs Transactions) (Receipts, Transactions, Transactions, Transactions, error) {
func (self *BlockManager) ProcessTransactions(coinbase *state.StateObject, state *state.State, block, parent *types.Block, txs types.Transactions) (types.Receipts, types.Transactions, types.Transactions, types.Transactions, error) {
var (
receipts Receipts
handled, unhandled Transactions
erroneous Transactions
receipts types.Receipts
handled, unhandled types.Transactions
erroneous types.Transactions
totalUsedGas = big.NewInt(0)
err error
)
@ -159,8 +160,9 @@ done:
txGas.Sub(txGas, st.gas)
cumulative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, txGas))
receipt := &Receipt{ethutil.CopyBytes(state.Root()), cumulative, nil /*bloom*/, state.Logs()}
receipt.Bloom = CreateBloom(Receipts{receipt})
receipt := types.NewReceipt(state.Root(), cumulative)
receipt.SetLogs(state.Logs())
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
// Notify all subscribers
go self.eth.EventMux().Post(TxPostEvent{tx})
@ -178,7 +180,7 @@ done:
return receipts, handled, unhandled, erroneous, err
}
func (sm *BlockManager) Process(block *Block) (td *big.Int, msgs state.Messages, err error) {
func (sm *BlockManager) Process(block *types.Block) (td *big.Int, msgs state.Messages, err error) {
// Processing a blocks may never happen simultaneously
sm.mutex.Lock()
defer sm.mutex.Unlock()
@ -195,7 +197,7 @@ func (sm *BlockManager) Process(block *Block) (td *big.Int, msgs state.Messages,
return sm.ProcessWithParent(block, parent)
}
func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, messages state.Messages, err error) {
func (sm *BlockManager) ProcessWithParent(block, parent *types.Block) (td *big.Int, messages state.Messages, err error) {
sm.lastAttemptedBlock = block
state := parent.State().Copy()
@ -215,13 +217,13 @@ func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, me
return
}
txSha := DeriveSha(block.transactions)
txSha := types.DeriveSha(block.Transactions())
if bytes.Compare(txSha, block.TxSha) != 0 {
err = fmt.Errorf("validating transaction root. received=%x got=%x", block.TxSha, txSha)
return
}
receiptSha := DeriveSha(receipts)
receiptSha := types.DeriveSha(receipts)
if bytes.Compare(receiptSha, block.ReceiptSha) != 0 {
err = fmt.Errorf("validating receipt root. received=%x got=%x", block.ReceiptSha, receiptSha)
return
@ -238,8 +240,8 @@ func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, me
return
}
block.receipts = receipts // although this isn't necessary it be in the future
rbloom := CreateBloom(receipts)
//block.receipts = receipts // although this isn't necessary it be in the future
rbloom := types.CreateBloom(receipts)
if bytes.Compare(rbloom, block.LogsBloom) != 0 {
err = fmt.Errorf("unable to replicate block's bloom=%x", rbloom)
return
@ -272,7 +274,7 @@ func (sm *BlockManager) ProcessWithParent(block, parent *Block) (td *big.Int, me
}
}
func (sm *BlockManager) ApplyDiff(state *state.State, parent, block *Block) (receipts Receipts, err error) {
func (sm *BlockManager) ApplyDiff(state *state.State, parent, block *types.Block) (receipts types.Receipts, err error) {
coinbase := state.GetOrNewStateObject(block.Coinbase)
coinbase.SetGasPool(block.CalcGasLimit(parent))
@ -285,7 +287,7 @@ func (sm *BlockManager) ApplyDiff(state *state.State, parent, block *Block) (rec
return receipts, nil
}
func (sm *BlockManager) CalculateTD(block *Block) (*big.Int, bool) {
func (sm *BlockManager) CalculateTD(block *types.Block) (*big.Int, bool) {
uncleDiff := new(big.Int)
for _, uncle := range block.Uncles {
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty)
@ -311,7 +313,7 @@ func (sm *BlockManager) CalculateTD(block *Block) (*big.Int, bool) {
// Validates the current block. Returns an error if the block was invalid,
// an uncle or anything that isn't on the current block chain.
// Validation validates easy over difficult (dagger takes longer time = difficult)
func (sm *BlockManager) ValidateBlock(block, parent *Block) error {
func (sm *BlockManager) ValidateBlock(block, parent *types.Block) error {
expd := CalcDifficulty(block, parent)
if expd.Cmp(block.Difficulty) < 0 {
return fmt.Errorf("Difficulty check failed for block %v, %v", block.Difficulty, expd)
@ -337,7 +339,7 @@ func (sm *BlockManager) ValidateBlock(block, parent *Block) error {
return nil
}
func (sm *BlockManager) AccumelateRewards(state *state.State, block, parent *Block) error {
func (sm *BlockManager) AccumelateRewards(state *state.State, block, parent *types.Block) error {
reward := new(big.Int).Set(BlockReward)
knownUncles := ethutil.Set(parent.Uncles)
@ -380,7 +382,7 @@ func (sm *BlockManager) AccumelateRewards(state *state.State, block, parent *Blo
return nil
}
func (sm *BlockManager) GetMessages(block *Block) (messages []*state.Message, err error) {
func (sm *BlockManager) GetMessages(block *types.Block) (messages []*state.Message, err error) {
if !sm.bc.HasBlock(block.PrevHash) {
return nil, ParentError(block.PrevHash)
}

View File

@ -6,6 +6,7 @@ import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
@ -13,36 +14,88 @@ import (
var chainlogger = logger.NewLogger("CHAIN")
func AddTestNetFunds(block *types.Block) {
for _, addr := range []string{
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
"e4157b34ea9615cfbde6b4fda419828124b70c78",
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
"6c386a4b26f73c802f34673f7248bb118f97424a",
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
"e6716f9544a56c530d868e4bfbacb172315bdead",
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
} {
codedAddr := ethutil.Hex2Bytes(addr)
account := block.State().GetAccount(codedAddr)
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
block.State().UpdateStateObject(account)
}
}
func CalcDifficulty(block, parent *types.Block) *big.Int {
diff := new(big.Int)
adjust := new(big.Int).Rsh(parent.Difficulty, 10)
if block.Time >= parent.Time+5 {
diff.Sub(parent.Difficulty, adjust)
} else {
diff.Add(parent.Difficulty, adjust)
}
return diff
}
type ChainManager struct {
Ethereum EthManager
// The famous, the fabulous Mister GENESIIIIIIS (block)
genesisBlock *Block
//eth EthManager
processor types.BlockProcessor
genesisBlock *types.Block
// Last known total difficulty
TD *big.Int
LastBlockNumber uint64
CurrentBlock *Block
CurrentBlock *types.Block
LastBlockHash []byte
workingChain *BlockChain
}
func NewChainManager(ethereum EthManager) *ChainManager {
func NewChainManager() *ChainManager {
bc := &ChainManager{}
bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis))
bc.Ethereum = ethereum
bc.genesisBlock = types.NewBlockFromBytes(ethutil.Encode(Genesis))
//bc.eth = ethereum
bc.setLastBlock()
return bc
}
func (bc *ChainManager) Genesis() *Block {
return bc.genesisBlock
func (self *ChainManager) SetProcessor(proc types.BlockProcessor) {
self.processor = proc
}
func (bc *ChainManager) NewBlock(coinbase []byte) *Block {
func (bc *ChainManager) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 {
// Prep genesis
AddTestNetFunds(bc.genesisBlock)
block := types.NewBlockFromBytes(data)
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
bc.LastBlockNumber = block.Number.Uint64()
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
} else {
bc.Reset()
}
chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash())
}
// Block creation & chain handling
func (bc *ChainManager) NewBlock(coinbase []byte) *types.Block {
var root interface{}
hash := ZeroHash256
@ -51,7 +104,7 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *Block {
hash = bc.LastBlockHash
}
block := CreateBlock(
block := types.CreateBlock(
root,
hash,
coinbase,
@ -72,23 +125,10 @@ func (bc *ChainManager) NewBlock(coinbase []byte) *Block {
return block
}
func CalcDifficulty(block, parent *Block) *big.Int {
diff := new(big.Int)
adjust := new(big.Int).Rsh(parent.Difficulty, 10)
if block.Time >= parent.Time+5 {
diff.Sub(parent.Difficulty, adjust)
} else {
diff.Add(parent.Difficulty, adjust)
}
return diff
}
func (bc *ChainManager) Reset() {
AddTestNetFunds(bc.genesisBlock)
bc.genesisBlock.state.Trie.Sync()
bc.genesisBlock.Trie().Sync()
// Prepare the genesis block
bc.add(bc.genesisBlock)
bc.CurrentBlock = bc.genesisBlock
@ -99,38 +139,31 @@ func (bc *ChainManager) Reset() {
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
}
// Add a block to the chain and record addition information
func (bc *ChainManager) add(block *types.Block) {
bc.writeBlockInfo(block)
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
encodedBlock := block.RlpEncode()
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
//chainlogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4])
}
// Accessors
func (bc *ChainManager) Genesis() *types.Block {
return bc.genesisBlock
}
// Block fetching methods
func (bc *ChainManager) HasBlock(hash []byte) bool {
data, _ := ethutil.Config.Db.Get(hash)
return len(data) != 0
}
// TODO: At one point we might want to save a block by prevHash in the db to optimise this...
func (bc *ChainManager) HasBlockWithPrevHash(hash []byte) bool {
block := bc.CurrentBlock
for ; block != nil; block = bc.GetBlock(block.PrevHash) {
if bytes.Compare(hash, block.PrevHash) == 0 {
return true
}
}
return false
}
func (bc *ChainManager) CalculateBlockTD(block *Block) *big.Int {
blockDiff := new(big.Int)
for _, uncle := range block.Uncles {
blockDiff = blockDiff.Add(blockDiff, uncle.Difficulty)
}
blockDiff = blockDiff.Add(blockDiff, block.Difficulty)
return blockDiff
}
func (bc *ChainManager) GenesisBlock() *Block {
return bc.genesisBlock
}
func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain [][]byte) {
block := self.GetBlock(hash)
if block == nil {
@ -152,42 +185,37 @@ func (self *ChainManager) GetChainHashesFromHash(hash []byte, max uint64) (chain
return
}
func AddTestNetFunds(block *Block) {
for _, addr := range []string{
"51ba59315b3a95761d0863b05ccc7a7f54703d99",
"e4157b34ea9615cfbde6b4fda419828124b70c78",
"b9c015918bdaba24b4ff057a92a3873d6eb201be",
"6c386a4b26f73c802f34673f7248bb118f97424a",
"cd2a3d9f938e13cd947ec05abc7fe734df8dd826",
"2ef47100e0787b915105fd5e3f4ff6752079d5cb",
"e6716f9544a56c530d868e4bfbacb172315bdead",
"1a26338f0d905e295fccb71fa9ea849ffa12aaf4",
} {
codedAddr := ethutil.Hex2Bytes(addr)
account := block.state.GetAccount(codedAddr)
account.SetBalance(ethutil.Big("1606938044258990275541962092341162602522202993782792835301376")) //ethutil.BigPow(2, 200)
block.state.UpdateStateObject(account)
func (self *ChainManager) GetBlock(hash []byte) *types.Block {
data, _ := ethutil.Config.Db.Get(hash)
if len(data) == 0 {
if self.workingChain != nil {
// Check the temp chain
for e := self.workingChain.Front(); e != nil; e = e.Next() {
if bytes.Compare(e.Value.(*link).Block.Hash(), hash) == 0 {
return e.Value.(*link).Block
}
}
}
return nil
}
return types.NewBlockFromBytes(data)
}
func (bc *ChainManager) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 {
// Prep genesis
AddTestNetFunds(bc.genesisBlock)
block := NewBlockFromBytes(data)
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
bc.LastBlockNumber = block.Number.Uint64()
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
} else {
bc.Reset()
func (self *ChainManager) GetBlockByNumber(num uint64) *types.Block {
block := self.CurrentBlock
for ; block != nil; block = self.GetBlock(block.PrevHash) {
if block.Number.Uint64() == num {
break
}
}
chainlogger.Infof("Last block (#%d) %x\n", bc.LastBlockNumber, bc.CurrentBlock.Hash())
if block != nil && block.Number.Uint64() == 0 && num != 0 {
return nil
}
return block
}
func (bc *ChainManager) SetTotalDifficulty(td *big.Int) {
@ -195,21 +223,7 @@ func (bc *ChainManager) SetTotalDifficulty(td *big.Int) {
bc.TD = td
}
// Add a block to the chain and record addition information
func (bc *ChainManager) add(block *Block) {
bc.writeBlockInfo(block)
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
encodedBlock := block.RlpEncode()
ethutil.Config.Db.Put(block.Hash(), encodedBlock)
ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
//chainlogger.Infof("Imported block #%d (%x...)\n", block.Number, block.Hash()[0:4])
}
func (self *ChainManager) CalcTotalDiff(block *Block) (*big.Int, error) {
func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
parent := self.GetBlock(block.PrevHash)
if parent == nil {
return nil, fmt.Errorf("Unable to calculate total diff without known parent %x", block.PrevHash)
@ -229,59 +243,8 @@ func (self *ChainManager) CalcTotalDiff(block *Block) (*big.Int, error) {
return td, nil
}
func (self *ChainManager) GetBlock(hash []byte) *Block {
data, _ := ethutil.Config.Db.Get(hash)
if len(data) == 0 {
if self.workingChain != nil {
// Check the temp chain
for e := self.workingChain.Front(); e != nil; e = e.Next() {
if bytes.Compare(e.Value.(*link).block.Hash(), hash) == 0 {
return e.Value.(*link).block
}
}
}
return nil
}
return NewBlockFromBytes(data)
}
func (self *ChainManager) GetBlockByNumber(num uint64) *Block {
block := self.CurrentBlock
for ; block != nil; block = self.GetBlock(block.PrevHash) {
if block.Number.Uint64() == num {
break
}
}
if block != nil && block.Number.Uint64() == 0 && num != 0 {
return nil
}
return block
}
func (self *ChainManager) GetBlockBack(num uint64) *Block {
block := self.CurrentBlock
for ; num != 0 && block != nil; num-- {
block = self.GetBlock(block.PrevHash)
}
return block
}
func (bc *ChainManager) BlockInfoByHash(hash []byte) BlockInfo {
bi := BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...))
bi.RlpDecode(data)
return bi
}
func (bc *ChainManager) BlockInfo(block *Block) BlockInfo {
bi := BlockInfo{}
func (bc *ChainManager) BlockInfo(block *types.Block) types.BlockInfo {
bi := types.BlockInfo{}
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...))
bi.RlpDecode(data)
@ -289,9 +252,9 @@ func (bc *ChainManager) BlockInfo(block *Block) BlockInfo {
}
// Unexported method for writing extra non-essential block info to the db
func (bc *ChainManager) writeBlockInfo(block *Block) {
func (bc *ChainManager) writeBlockInfo(block *types.Block) {
bc.LastBlockNumber++
bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.TD}
bi := types.BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash, TD: bc.TD}
// For now we use the block hash with the words "info" appended as key
ethutil.Config.Db.Put(append(block.Hash(), []byte("Info")...), bi.RlpEncode())
@ -303,40 +266,24 @@ func (bc *ChainManager) Stop() {
}
}
type link struct {
block *Block
messages state.Messages
td *big.Int
}
type BlockChain struct {
*list.List
}
func NewChain(blocks Blocks) *BlockChain {
chain := &BlockChain{list.New()}
for _, block := range blocks {
chain.PushBack(&link{block, nil, nil})
}
return chain
func (self *ChainManager) NewIterator(startHash []byte) *ChainIterator {
return &ChainIterator{self, self.GetBlock(startHash)}
}
// This function assumes you've done your checking. No checking is done at this stage anymore
func (self *ChainManager) InsertChain(chain *BlockChain) {
func (self *ChainManager) InsertChain(chain *BlockChain, call func(*types.Block, state.Messages)) {
for e := chain.Front(); e != nil; e = e.Next() {
link := e.Value.(*link)
self.add(link.block)
self.SetTotalDifficulty(link.td)
self.Ethereum.EventMux().Post(NewBlockEvent{link.block})
self.Ethereum.EventMux().Post(link.messages)
self.add(link.Block)
self.SetTotalDifficulty(link.Td)
call(link.Block, link.Messages)
}
b, e := chain.Front(), chain.Back()
if b != nil && e != nil {
front, back := b.Value.(*link).block, e.Value.(*link).block
front, back := b.Value.(*link).Block, e.Value.(*link).Block
chainlogger.Infof("Imported %d blocks. #%v (%x) / %#v (%x)", chain.Len(), front.Number, front.Hash()[0:4], back.Number, back.Hash()[0:4])
}
}
@ -348,20 +295,17 @@ func (self *ChainManager) TestChain(chain *BlockChain) (td *big.Int, err error)
for e := chain.Front(); e != nil; e = e.Next() {
var (
l = e.Value.(*link)
block = l.block
block = l.Block
parent = self.GetBlock(block.PrevHash)
)
//fmt.Println("parent", parent)
//fmt.Println("current", block)
if parent == nil {
err = fmt.Errorf("incoming chain broken on hash %x\n", block.PrevHash[0:4])
return
}
var messages state.Messages
td, messages, err = self.Ethereum.BlockManager().ProcessWithParent(block, parent)
td, messages, err = self.processor.ProcessWithParent(block, parent) //self.eth.BlockManager().ProcessWithParent(block, parent)
if err != nil {
chainlogger.Infoln(err)
chainlogger.Debugf("Block #%v failed (%x...)\n", block.Number, block.Hash()[0:4])
@ -370,8 +314,8 @@ func (self *ChainManager) TestChain(chain *BlockChain) (td *big.Int, err error)
err = fmt.Errorf("incoming chain failed %v\n", err)
return
}
l.td = td
l.messages = messages
l.Td = td
l.Messages = messages
}
if td.Cmp(self.TD) <= 0 {
@ -383,3 +327,42 @@ func (self *ChainManager) TestChain(chain *BlockChain) (td *big.Int, err error)
return
}
type link struct {
Block *types.Block
Messages state.Messages
Td *big.Int
}
type BlockChain struct {
*list.List
}
func NewChain(blocks types.Blocks) *BlockChain {
chain := &BlockChain{list.New()}
for _, block := range blocks {
chain.PushBack(&link{block, nil, nil})
}
return chain
}
func (self *BlockChain) RlpEncode() []byte {
dat := make([]interface{}, 0)
for e := self.Front(); e != nil; e = e.Next() {
dat = append(dat, e.Value.(*link).Block.RlpData())
}
return ethutil.Encode(dat)
}
type ChainIterator struct {
cm *ChainManager
block *types.Block // current block in the iterator
}
func (self *ChainIterator) Prev() *types.Block {
self.block = self.cm.GetBlock(self.block.PrevHash)
return self.block
}

View File

@ -1 +1,116 @@
package chain
import (
"fmt"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
)
var TD *big.Int
func init() {
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
ethutil.Config.Db, _ = ethdb.NewMemDatabase()
}
type fakeproc struct {
}
func (self fakeproc) ProcessWithParent(a, b *types.Block) (*big.Int, state.Messages, error) {
TD = new(big.Int).Add(TD, big.NewInt(1))
return TD, nil, nil
}
func makechain(cman *ChainManager, max int) *BlockChain {
blocks := make(types.Blocks, max)
for i := 0; i < max; i++ {
addr := ethutil.LeftPadBytes([]byte{byte(i)}, 20)
block := cman.NewBlock(addr)
if i != 0 {
cman.CurrentBlock = blocks[i-1]
}
blocks[i] = block
}
return NewChain(blocks)
}
func TestLongerFork(t *testing.T) {
cman := NewChainManager()
cman.SetProcessor(fakeproc{})
TD = big.NewInt(1)
chainA := makechain(cman, 5)
TD = big.NewInt(1)
chainB := makechain(cman, 10)
td, err := cman.TestChain(chainA)
if err != nil {
t.Error("unable to create new TD from chainA:", err)
}
cman.TD = td
_, err = cman.TestChain(chainB)
if err != nil {
t.Error("expected chainB not to give errors:", err)
}
}
func TestEqualFork(t *testing.T) {
cman := NewChainManager()
cman.SetProcessor(fakeproc{})
TD = big.NewInt(1)
chainA := makechain(cman, 5)
TD = big.NewInt(2)
chainB := makechain(cman, 5)
td, err := cman.TestChain(chainA)
if err != nil {
t.Error("unable to create new TD from chainA:", err)
}
cman.TD = td
_, err = cman.TestChain(chainB)
if err != nil {
t.Error("expected chainB not to give errors:", err)
}
}
func TestBrokenChain(t *testing.T) {
cman := NewChainManager()
cman.SetProcessor(fakeproc{})
TD = big.NewInt(1)
chain := makechain(cman, 5)
chain.Remove(chain.Front())
_, err := cman.TestChain(chain)
if err == nil {
t.Error("expected broken chain to return error")
}
}
func BenchmarkChainTesting(b *testing.B) {
const chainlen = 1000
ethutil.ReadConfig(".ethtest", "/tmp/ethtest", "")
ethutil.Config.Db, _ = ethdb.NewMemDatabase()
cman := NewChainManager()
cman.SetProcessor(fakeproc{})
TD = big.NewInt(1)
chain := makechain(cman, chainlen)
stime := time.Now()
cman.TestChain(chain)
fmt.Println(chainlen, "took", time.Since(stime))
}

View File

@ -6,6 +6,7 @@ import (
"math/rand"
"time"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
@ -15,7 +16,7 @@ import (
var powlogger = logger.NewLogger("POW")
type PoW interface {
Search(block *Block, stop <-chan struct{}) []byte
Search(block *types.Block, stop <-chan struct{}) []byte
Verify(hash []byte, diff *big.Int, nonce []byte) bool
GetHashrate() int64
Turbo(bool)
@ -35,7 +36,7 @@ func (pow *EasyPow) Turbo(on bool) {
pow.turbo = on
}
func (pow *EasyPow) Search(block *Block, stop <-chan struct{}) []byte {
func (pow *EasyPow) Search(block *types.Block, stop <-chan struct{}) []byte {
r := rand.New(rand.NewSource(time.Now().UnixNano()))
hash := block.HashNoNonce()
diff := block.Difficulty

View File

@ -1,10 +1,12 @@
package chain
import "github.com/ethereum/go-ethereum/chain/types"
// TxPreEvent is posted when a transaction enters the transaction pool.
type TxPreEvent struct{ Tx *Transaction }
type TxPreEvent struct{ Tx *types.Transaction }
// TxPostEvent is posted when a transaction has been processed.
type TxPostEvent struct{ Tx *Transaction }
type TxPostEvent struct{ Tx *types.Transaction }
// NewBlockEvent is posted when a block has been imported.
type NewBlockEvent struct{ Block *Block }
type NewBlockEvent struct{ Block *types.Block }

View File

@ -5,6 +5,7 @@ import (
"math"
"math/big"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
)
@ -24,7 +25,7 @@ type Filter struct {
Altered []AccountChange
BlockCallback func(*Block)
BlockCallback func(*types.Block)
MessageCallback func(state.Messages)
}
@ -171,11 +172,11 @@ func (self *Filter) FilterMessages(msgs []*state.Message) []*state.Message {
return messages
}
func (self *Filter) bloomFilter(block *Block) bool {
func (self *Filter) bloomFilter(block *types.Block) bool {
var fromIncluded, toIncluded bool
if len(self.from) > 0 {
for _, from := range self.from {
if BloomLookup(block.LogsBloom, from) {
if types.BloomLookup(block.LogsBloom, from) {
fromIncluded = true
break
}
@ -186,7 +187,7 @@ func (self *Filter) bloomFilter(block *Block) bool {
if len(self.to) > 0 {
for _, to := range self.to {
if BloomLookup(block.LogsBloom, ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) {
if types.BloomLookup(block.LogsBloom, ethutil.U256(new(big.Int).Add(ethutil.Big1, ethutil.BigD(to))).Bytes()) {
toIncluded = true
break
}

View File

@ -4,6 +4,7 @@ import (
"container/list"
"fmt"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
@ -19,7 +20,7 @@ type TestManager struct {
db ethutil.Database
txPool *TxPool
blockChain *ChainManager
Blocks []*Block
Blocks []*types.Block
}
func (s *TestManager) IsListening() bool {

View File

@ -4,6 +4,7 @@ import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/vm"
)
@ -26,17 +27,17 @@ import (
*/
type StateTransition struct {
coinbase, receiver []byte
tx *Transaction
tx *types.Transaction
gas, gasPrice *big.Int
value *big.Int
data []byte
state *state.State
block *Block
block *types.Block
cb, rec, sen *state.StateObject
}
func NewStateTransition(coinbase *state.StateObject, tx *Transaction, state *state.State, block *Block) *StateTransition {
func NewStateTransition(coinbase *state.StateObject, tx *types.Transaction, state *state.State, block *types.Block) *StateTransition {
return &StateTransition{coinbase.Address(), tx.Recipient, tx, new(big.Int), new(big.Int).Set(tx.GasPrice), tx.Value, tx.Data, state, block, coinbase, nil, nil}
}
@ -203,7 +204,7 @@ func (self *StateTransition) TransitionState() (err error) {
})
// Process the init code and create 'valid' contract
if IsContractAddr(self.receiver) {
if types.IsContractAddr(self.receiver) {
// Evaluate the initialization script
// and use the return value as the
// script section for the state object.
@ -280,7 +281,7 @@ func (self *StateTransition) Eval(msg *state.Message, script []byte, context *st
}
// Converts an transaction in to a state object
func MakeContract(tx *Transaction, state *state.State) *state.StateObject {
func MakeContract(tx *types.Transaction, state *state.State) *state.StateObject {
addr := tx.CreationAddress(state)
contract := state.GetOrNewStateObject(addr)

View File

@ -7,6 +7,7 @@ import (
"math/big"
"sync"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/wire"
@ -16,7 +17,7 @@ var txplogger = logger.NewLogger("TXP")
const txPoolQueueSize = 50
type TxPoolHook chan *Transaction
type TxPoolHook chan *types.Transaction
type TxMsgTy byte
const (
@ -26,21 +27,21 @@ const (
var MinGasPrice = big.NewInt(10000000000000)
type TxMsg struct {
Tx *Transaction
Tx *types.Transaction
Type TxMsgTy
}
func EachTx(pool *list.List, it func(*Transaction, *list.Element) bool) {
func EachTx(pool *list.List, it func(*types.Transaction, *list.Element) bool) {
for e := pool.Front(); e != nil; e = e.Next() {
if it(e.Value.(*Transaction), e) {
if it(e.Value.(*types.Transaction), e) {
break
}
}
}
func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction {
func FindTx(pool *list.List, finder func(*types.Transaction, *list.Element) bool) *types.Transaction {
for e := pool.Front(); e != nil; e = e.Next() {
if tx, ok := e.Value.(*Transaction); ok {
if tx, ok := e.Value.(*types.Transaction); ok {
if finder(tx, e) {
return tx
}
@ -51,7 +52,7 @@ func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Tra
}
type TxProcessor interface {
ProcessTransaction(tx *Transaction)
ProcessTransaction(tx *types.Transaction)
}
// The tx pool a thread safe transaction pool handler. In order to
@ -65,7 +66,7 @@ type TxPool struct {
mutex sync.Mutex
// Queueing channel for reading and writing incoming
// transactions to
queueChan chan *Transaction
queueChan chan *types.Transaction
// Quiting channel
quit chan bool
// The actual pool
@ -79,14 +80,14 @@ type TxPool struct {
func NewTxPool(ethereum EthManager) *TxPool {
return &TxPool{
pool: list.New(),
queueChan: make(chan *Transaction, txPoolQueueSize),
queueChan: make(chan *types.Transaction, txPoolQueueSize),
quit: make(chan bool),
Ethereum: ethereum,
}
}
// Blocking function. Don't use directly. Use QueueTransaction instead
func (pool *TxPool) addTransaction(tx *Transaction) {
func (pool *TxPool) addTransaction(tx *types.Transaction) {
pool.mutex.Lock()
defer pool.mutex.Unlock()
@ -96,7 +97,7 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
pool.Ethereum.Broadcast(wire.MsgTxTy, []interface{}{tx.RlpData()})
}
func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
func (pool *TxPool) ValidateTransaction(tx *types.Transaction) error {
// Get the last block so we can retrieve the sender and receiver from
// the merkle trie
block := pool.Ethereum.ChainManager().CurrentBlock
@ -142,7 +143,7 @@ out:
select {
case tx := <-pool.queueChan:
hash := tx.Hash()
foundTx := FindTx(pool.pool, func(tx *Transaction, e *list.Element) bool {
foundTx := FindTx(pool.pool, func(tx *types.Transaction, e *list.Element) bool {
return bytes.Compare(tx.Hash(), hash) == 0
})
@ -172,18 +173,18 @@ out:
}
}
func (pool *TxPool) QueueTransaction(tx *Transaction) {
func (pool *TxPool) QueueTransaction(tx *types.Transaction) {
pool.queueChan <- tx
}
func (pool *TxPool) CurrentTransactions() []*Transaction {
func (pool *TxPool) CurrentTransactions() []*types.Transaction {
pool.mutex.Lock()
defer pool.mutex.Unlock()
txList := make([]*Transaction, pool.pool.Len())
txList := make([]*types.Transaction, pool.pool.Len())
i := 0
for e := pool.pool.Front(); e != nil; e = e.Next() {
tx := e.Value.(*Transaction)
tx := e.Value.(*types.Transaction)
txList[i] = tx
@ -198,7 +199,7 @@ func (pool *TxPool) RemoveInvalid(state *state.State) {
defer pool.mutex.Unlock()
for e := pool.pool.Front(); e != nil; e = e.Next() {
tx := e.Value.(*Transaction)
tx := e.Value.(*types.Transaction)
sender := state.GetAccount(tx.Sender())
err := pool.ValidateTransaction(tx)
if err != nil || sender.Nonce >= tx.Nonce {
@ -207,12 +208,12 @@ func (pool *TxPool) RemoveInvalid(state *state.State) {
}
}
func (self *TxPool) RemoveSet(txs Transactions) {
func (self *TxPool) RemoveSet(txs types.Transactions) {
self.mutex.Lock()
defer self.mutex.Unlock()
for _, tx := range txs {
EachTx(self.pool, func(t *Transaction, element *list.Element) bool {
EachTx(self.pool, func(t *types.Transaction, element *list.Element) bool {
if t == tx {
self.pool.Remove(element)
return true // To stop the loop
@ -222,7 +223,7 @@ func (self *TxPool) RemoveSet(txs Transactions) {
}
}
func (pool *TxPool) Flush() []*Transaction {
func (pool *TxPool) Flush() []*types.Transaction {
txList := pool.CurrentTransactions()
// Recreate a new list all together

View File

@ -1 +0,0 @@
package chain

View File

@ -1,4 +1,4 @@
package chain
package types
import (
"bytes"
@ -156,7 +156,7 @@ func (block *Block) State() *state.State {
return block.state
}
func (block *Block) Transactions() []*Transaction {
func (block *Block) Transactions() Transactions {
return block.transactions
}

View File

@ -1,4 +1,4 @@
package chain
package types
import (
"math/big"

View File

@ -1,4 +1,4 @@
package chain
package types
/*
import (

10
chain/types/common.go Normal file
View File

@ -0,0 +1,10 @@
package types
import (
"math/big"
"github.com/ethereum/go-ethereum/state"
)
type BlockProcessor interface {
ProcessWithParent(*Block, *Block) (*big.Int, state.Messages, error)
}

View File

@ -1,4 +1,4 @@
package chain
package types
import (
"github.com/ethereum/go-ethereum/ethutil"

View File

@ -1,4 +1,4 @@
package chain
package types
import (
"bytes"
@ -16,6 +16,10 @@ type Receipt struct {
logs state.Logs
}
func NewReceipt(root []byte, cumalativeGasUsed *big.Int) *Receipt {
return &Receipt{PostState: ethutil.CopyBytes(root), CumulativeGasUsed: cumalativeGasUsed}
}
func NewRecieptFromValue(val *ethutil.Value) *Receipt {
r := &Receipt{}
r.RlpValueDecode(val)
@ -23,6 +27,10 @@ func NewRecieptFromValue(val *ethutil.Value) *Receipt {
return r
}
func (self *Receipt) SetLogs(logs state.Logs) {
self.logs = logs
}
func (self *Receipt) RlpValueDecode(decoder *ethutil.Value) {
self.PostState = decoder.Get(0).Bytes()
self.CumulativeGasUsed = decoder.Get(1).BigInt()

View File

@ -1,4 +1,4 @@
package chain
package types
import (
"fmt"

View File

@ -0,0 +1 @@
package types

View File

@ -3,17 +3,18 @@ package chain
import (
"math/big"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/vm"
)
type VMEnv struct {
state *state.State
block *Block
tx *Transaction
block *types.Block
tx *types.Transaction
}
func NewEnv(state *state.State, tx *Transaction, block *Block) *VMEnv {
func NewEnv(state *state.State, tx *types.Transaction, block *types.Block) *VMEnv {
return &VMEnv{
state: state,
block: block,

View File

@ -21,8 +21,7 @@ import (
"fmt"
"os"
"runtime"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
@ -74,7 +73,7 @@ func main() {
ethereum := utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
if Dump {
var block *chain.Block
var block *types.Block
if len(DumpHash) == 0 && DumpNumber == -1 {
block = ethereum.ChainManager().CurrentBlock

View File

@ -15,7 +15,7 @@
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @authors
* @authors:
* Jeffrey Wilcke <i@jev.io>
* @date 2014
*

View File

@ -21,8 +21,7 @@ import (
"encoding/json"
"os"
"strconv"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
@ -106,7 +105,7 @@ func (self *Gui) DumpState(hash, path string) {
if len(hash) == 0 {
stateDump = self.eth.BlockManager().CurrentState().Dump()
} else {
var block *chain.Block
var block *types.Block
if hash[0] == '#' {
i, _ := strconv.Atoi(hash[1:])
block = self.eth.ChainManager().GetBlockByNumber(uint64(i))

View File

@ -21,6 +21,7 @@ import (
"encoding/json"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/state"
@ -36,7 +37,7 @@ type AppContainer interface {
Window() *qml.Window
Engine() *qml.Engine
NewBlock(*chain.Block)
NewBlock(*types.Block)
NewWatcher(chan bool)
Messages(state.Messages, string)
Post(string, int)

View File

@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
@ -63,6 +64,15 @@ func LoadExtension(path string) (uintptr, error) {
return ptr.Interface().(uintptr), nil
}
*/
/*
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
fmt.Printf("Fetched vec with addr: %#x\n", vec)
if errr != nil {
fmt.Println(errr)
} else {
context.SetVar("vec", (unsafe.Pointer)(vec))
}
*/
var guilogger = logger.NewLogger("GUI")
@ -112,9 +122,10 @@ func NewWindow(ethereum *eth.Ethereum, config *ethutil.ConfigManager, clientIden
}
func (gui *Gui) Start(assetPath string) {
defer gui.txDb.Close()
guilogger.Infoln("Starting GUI")
// Register ethereum functions
qml.RegisterTypes("Ethereum", 1, 0, []qml.TypeSpec{{
Init: func(p *xeth.JSBlock, obj qml.Object) { p.Number = 0; p.Hash = "" },
@ -132,16 +143,6 @@ func (gui *Gui) Start(assetPath string) {
context.SetVar("gui", gui)
context.SetVar("eth", gui.uiLib)
/*
vec, errr := LoadExtension("/Users/jeffrey/Desktop/build-libqmltest-Desktop_Qt_5_2_1_clang_64bit-Debug/liblibqmltest_debug.dylib")
fmt.Printf("Fetched vec with addr: %#x\n", vec)
if errr != nil {
fmt.Println(errr)
} else {
context.SetVar("vec", (unsafe.Pointer)(vec))
}
*/
// Load the main QML interface
data, _ := ethutil.Config.Db.Get([]byte("KeyRing"))
@ -160,7 +161,6 @@ func (gui *Gui) Start(assetPath string) {
panic(err)
}
guilogger.Infoln("Starting GUI")
gui.open = true
win.Show()
@ -245,7 +245,7 @@ func (gui *Gui) CreateAndSetPrivKey() (string, string, string, string) {
return gui.eth.KeyManager().KeyPair().AsStrings()
}
func (gui *Gui) setInitialChainManager() {
func (gui *Gui) setInitialChain(ancientBlocks bool) {
sBlk := gui.eth.ChainManager().LastBlockHash
blk := gui.eth.ChainManager().GetBlock(sBlk)
for ; blk != nil; blk = gui.eth.ChainManager().GetBlock(sBlk) {
@ -255,10 +255,6 @@ func (gui *Gui) setInitialChainManager() {
}
}
type address struct {
Name, Address string
}
func (gui *Gui) loadAddressBook() {
view := gui.getObjectByName("infoView")
nameReg := gui.pipe.World().Config().Get("NameReg")
@ -295,7 +291,7 @@ func (self *Gui) loadMergedMiningOptions() {
}
}
func (gui *Gui) insertTransaction(window string, tx *chain.Transaction) {
func (gui *Gui) insertTransaction(window string, tx *types.Transaction) {
pipe := xeth.New(gui.eth)
nameReg := pipe.World().Config().Get("NameReg")
addr := gui.address()
@ -345,7 +341,7 @@ func (gui *Gui) insertTransaction(window string, tx *chain.Transaction) {
func (gui *Gui) readPreviousTransactions() {
it := gui.txDb.NewIterator()
for it.Next() {
tx := chain.NewTransactionFromBytes(it.Value())
tx := types.NewTransactionFromBytes(it.Value())
gui.insertTransaction("post", tx)
@ -353,7 +349,7 @@ func (gui *Gui) readPreviousTransactions() {
it.Release()
}
func (gui *Gui) processBlock(block *chain.Block, initial bool) {
func (gui *Gui) processBlock(block *types.Block, initial bool) {
name := strings.Trim(gui.pipe.World().Config().Get("NameReg").Storage(block.Coinbase).Str(), "\x00")
b := xeth.NewJSBlock(block)
b.Name = name
@ -385,11 +381,11 @@ func (self *Gui) getObjectByName(objectName string) qml.Object {
func (gui *Gui) update() {
// We have to wait for qml to be done loading all the windows.
for !gui.qmlDone {
time.Sleep(500 * time.Millisecond)
time.Sleep(300 * time.Millisecond)
}
go func() {
go gui.setInitialChainManager()
go gui.setInitialChain(false)
gui.loadAddressBook()
gui.loadMergedMiningOptions()
gui.setPeerInfo()
@ -493,14 +489,7 @@ func (gui *Gui) update() {
case <-generalUpdateTicker.C:
statusText := "#" + gui.eth.ChainManager().CurrentBlock.Number.String()
lastBlockLabel.Set("text", statusText)
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(gui.uiLib.miner.GetPow().GetHashrate(), 10)+"Khash")
/*
if gui.miner != nil {
pow := gui.miner.GetPow()
miningLabel.Set("text", "Mining @ "+strconv.FormatInt(pow.GetHashrate(), 10)+"Khash")
}
*/
blockLength := gui.eth.BlockPool().BlocksProcessed
chainLength := gui.eth.BlockPool().ChainLength
@ -510,7 +499,6 @@ func (gui *Gui) update() {
dlWidget = gui.win.Root().ObjectByName("downloadIndicator")
dlLabel = gui.win.Root().ObjectByName("downloadLabel")
)
dlWidget.Set("value", pct)
dlLabel.Set("text", fmt.Sprintf("%d / %d", blockLength, chainLength))
@ -549,7 +537,6 @@ NumGC: %d
func (gui *Gui) setPeerInfo() {
gui.win.Root().Call("setPeers", fmt.Sprintf("%d / %d", gui.eth.PeerCount(), gui.eth.MaxPeers))
gui.win.Root().Call("resetPeers")
for _, peer := range gui.pipe.Peers() {
gui.win.Root().Call("addPeer", peer)

View File

@ -26,8 +26,7 @@ import (
"os"
"path"
"path/filepath"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/javascript"
"github.com/ethereum/go-ethereum/state"
@ -138,7 +137,7 @@ func (app *HtmlApplication) Window() *qml.Window {
return app.win
}
func (app *HtmlApplication) NewBlock(block *chain.Block) {
func (app *HtmlApplication) NewBlock(block *types.Block) {
b := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
app.webView.Call("onNewBlockCb", b)
}

View File

@ -18,8 +18,10 @@
package main
import (
"fmt"
"os"
"runtime"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/cmd/utils"
@ -38,6 +40,7 @@ func run() error {
// precedence: code-internal flag default < config file < environment variables < command line
Init() // parsing command line
tstart := time.Now()
config := utils.InitConfig(VmType, ConfigFile, Datadir, "ETH")
utils.InitDataDir(Datadir)
@ -51,14 +54,11 @@ func run() error {
os.Exit(1)
}
keyManager := utils.NewKeyManager(KeyStore, Datadir, db)
// create, import, export keys
utils.KeyTasks(keyManager, KeyRing, GenAddr, SecretFile, ExportDir, NonInteractive)
clientIdentity := utils.NewClientIdentity(ClientIdentifier, Version, Identifier)
ethereum = utils.NewEthereum(db, clientIdentity, keyManager, UseUPnP, OutboundPort, MaxPeer)
if ShowGenesis {
@ -75,7 +75,10 @@ func run() error {
utils.RegisterInterrupt(func(os.Signal) {
gui.Stop()
})
utils.StartEthereum(ethereum, UseSeed)
go utils.StartEthereum(ethereum, UseSeed)
fmt.Println("ETH stack took", time.Since(tstart))
// gui blocks the main thread
gui.Start(AssetPath)

View File

@ -20,8 +20,7 @@ package main
import (
"fmt"
"runtime"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/xeth"
@ -65,7 +64,7 @@ func (app *QmlApplication) NewWatcher(quitChan chan bool) {
}
// Events
func (app *QmlApplication) NewBlock(block *chain.Block) {
func (app *QmlApplication) NewBlock(block *types.Block) {
pblock := &xeth.JSBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Bytes2Hex(block.Hash())}
app.win.Call("onNewBlockCb", pblock)
}

View File

@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/javascript"
@ -126,7 +127,7 @@ func (self *UiLib) PastPeers() *ethutil.List {
}
func (self *UiLib) ImportTx(rlpTx string) {
tx := chain.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx))
tx := types.NewTransactionFromBytes(ethutil.Hex2Bytes(rlpTx))
self.eth.TxPool().QueueTransaction(tx)
}
@ -228,7 +229,7 @@ func (self *UiLib) NewFilter(object map[string]interface{}) (id int) {
func (self *UiLib) NewFilterString(typ string) (id int) {
filter := chain.NewFilter(self.eth)
filter.BlockCallback = func(block *chain.Block) {
filter.BlockCallback = func(block *types.Block) {
if self.win != nil && self.win.Root() != nil {
self.win.Root().Call("invokeFilterCallback", "{}", id)
} else {

View File

@ -2,21 +2,20 @@ package utils
import (
"math/big"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/vm"
)
type VMEnv struct {
state *state.State
block *chain.Block
block *types.Block
transactor []byte
value *big.Int
}
func NewEnv(state *state.State, block *chain.Block, transactor []byte, value *big.Int) *VMEnv {
func NewEnv(state *state.State, block *types.Block, transactor []byte, value *big.Int) *VMEnv {
return &VMEnv{
state: state,
block: block,

View File

@ -23,6 +23,10 @@ func (db *MemDatabase) Put(key []byte, value []byte) {
db.db[string(key)] = value
}
func (db *MemDatabase) Set(key []byte, value []byte) {
db.Put(key, value)
}
func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return db.db[string(key)], nil
}

View File

@ -129,8 +129,9 @@ func New(db ethutil.Database, clientIdentity wire.ClientIdentity, keyManager *cr
ethereum.blockPool = NewBlockPool(ethereum)
ethereum.txPool = chain.NewTxPool(ethereum)
ethereum.blockChain = chain.NewChainManager(ethereum)
ethereum.blockChain = chain.NewChainManager()
ethereum.blockManager = chain.NewBlockManager(ethereum)
ethereum.blockChain.SetProcessor(ethereum.blockManager)
// Start the tx pool
ethereum.txPool.Start()

View File

@ -1,40 +0,0 @@
mode: count
github.com/ethereum/go-ethereum/event/event.go:41.66,45.17 4 1005
github.com/ethereum/go-ethereum/event/event.go:63.2,63.12 1 1004
github.com/ethereum/go-ethereum/event/event.go:45.17,47.3 1 1
github.com/ethereum/go-ethereum/event/event.go:47.3,48.22 1 1004
github.com/ethereum/go-ethereum/event/event.go:51.3,51.27 1 1004
github.com/ethereum/go-ethereum/event/event.go:48.22,50.4 1 5
github.com/ethereum/go-ethereum/event/event.go:51.27,54.32 3 1006
github.com/ethereum/go-ethereum/event/event.go:57.4,60.25 4 1005
github.com/ethereum/go-ethereum/event/event.go:54.32,56.5 1 1
github.com/ethereum/go-ethereum/event/event.go:68.48,71.17 3 3513
github.com/ethereum/go-ethereum/event/event.go:75.2,77.27 3 3511
github.com/ethereum/go-ethereum/event/event.go:80.2,80.12 1 3509
github.com/ethereum/go-ethereum/event/event.go:71.17,74.3 2 2
github.com/ethereum/go-ethereum/event/event.go:77.27,79.3 1 2576
github.com/ethereum/go-ethereum/event/event.go:86.28,88.32 2 5
github.com/ethereum/go-ethereum/event/event.go:93.2,95.20 3 5
github.com/ethereum/go-ethereum/event/event.go:88.32,89.28 1 3
github.com/ethereum/go-ethereum/event/event.go:89.28,91.4 1 3
github.com/ethereum/go-ethereum/event/event.go:98.36,100.34 2 1001
github.com/ethereum/go-ethereum/event/event.go:109.2,109.22 1 1001
github.com/ethereum/go-ethereum/event/event.go:100.34,101.37 1 1001
github.com/ethereum/go-ethereum/event/event.go:101.37,102.22 1 1001
github.com/ethereum/go-ethereum/event/event.go:102.22,104.5 1 2
github.com/ethereum/go-ethereum/event/event.go:104.5,106.5 1 999
github.com/ethereum/go-ethereum/event/event.go:112.46,113.26 1 2007
github.com/ethereum/go-ethereum/event/event.go:118.2,118.11 1 1005
github.com/ethereum/go-ethereum/event/event.go:113.26,114.16 1 181499
github.com/ethereum/go-ethereum/event/event.go:114.16,116.4 1 1002
github.com/ethereum/go-ethereum/event/event.go:121.52,126.2 4 999
github.com/ethereum/go-ethereum/event/event.go:142.35,150.2 2 1005
github.com/ethereum/go-ethereum/event/event.go:152.44,154.2 1 1003
github.com/ethereum/go-ethereum/event/event.go:156.32,159.2 2 1001
github.com/ethereum/go-ethereum/event/event.go:161.30,164.14 3 1004
github.com/ethereum/go-ethereum/event/event.go:167.2,173.19 6 1003
github.com/ethereum/go-ethereum/event/event.go:164.14,166.3 1 1
github.com/ethereum/go-ethereum/event/event.go:176.42,178.9 2 2575
github.com/ethereum/go-ethereum/event/event.go:182.2,182.20 1 2575
github.com/ethereum/go-ethereum/event/event.go:179.2,179.21 0 1004
github.com/ethereum/go-ethereum/event/event.go:180.2,180.19 0 1571

View File

@ -9,6 +9,7 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/event"
@ -130,7 +131,7 @@ func (self *JSRE) dump(call otto.FunctionCall) otto.Value {
var state *state.State
if len(call.ArgumentList) > 0 {
var block *chain.Block
var block *types.Block
if call.Argument(0).IsNumber() {
num, _ := call.Argument(0).ToInteger()
block = self.ethereum.ChainManager().GetBlockByNumber(uint64(num))

View File

@ -29,8 +29,10 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/wire"
@ -44,7 +46,7 @@ type LocalTx struct {
Value string `json:"value"`
}
func (self *LocalTx) Sign(key []byte) *chain.Transaction {
func (self *LocalTx) Sign(key []byte) *types.Transaction {
return nil
}
@ -54,7 +56,7 @@ type Miner struct {
eth *eth.Ethereum
events event.Subscription
uncles chain.Blocks
uncles types.Blocks
localTxs map[int]*LocalTx
localTxId int
@ -212,13 +214,15 @@ func (self *Miner) mine() {
nonce := self.pow.Search(block, self.powQuitCh)
if nonce != nil {
block.Nonce = nonce
lchain := chain.NewChain(chain.Blocks{block})
lchain := chain.NewChain(types.Blocks{block})
_, err := chainMan.TestChain(lchain)
if err != nil {
minerlogger.Infoln(err)
} else {
chainMan.InsertChain(lchain)
//self.eth.EventMux().Post(chain.NewBlockEvent{block})
chainMan.InsertChain(lchain, func(block *types.Block, _ state.Messages) {
self.eth.EventMux().Post(chain.NewBlockEvent{block})
})
self.eth.Broadcast(wire.MsgBlockTy, []interface{}{block.Value().Val})
minerlogger.Infof("🔨 Mined block %x\n", block.Hash())
@ -229,15 +233,15 @@ func (self *Miner) mine() {
}
}
func (self *Miner) finiliseTxs() chain.Transactions {
func (self *Miner) finiliseTxs() types.Transactions {
// Sort the transactions by nonce in case of odd network propagation
var txs chain.Transactions
var txs types.Transactions
state := self.eth.BlockManager().TransState()
// XXX This has to change. Coinbase is, for new, same as key.
key := self.eth.KeyManager()
for _, ltx := range self.localTxs {
tx := chain.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data)
tx := types.NewTransactionMessage(ltx.To, ethutil.Big(ltx.Value), ethutil.Big(ltx.Gas), ethutil.Big(ltx.GasPrice), ltx.Data)
tx.Nonce = state.GetNonce(self.Coinbase)
state.SetNonce(self.Coinbase, tx.Nonce+1)
@ -247,7 +251,7 @@ func (self *Miner) finiliseTxs() chain.Transactions {
}
txs = append(txs, self.eth.TxPool().CurrentTransactions()...)
sort.Sort(chain.TxByNonce{txs})
sort.Sort(types.TxByNonce{txs})
return txs
}

12
peer.go
View File

@ -12,7 +12,7 @@ import (
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/wire"
@ -24,7 +24,7 @@ const (
// The size of the output buffer for writing messages
outputBufferSize = 50
// Current protocol version
ProtocolVersion = 42
ProtocolVersion = 43
// Current P2P version
P2PVersion = 2
// Ethereum network version
@ -155,7 +155,7 @@ type Peer struct {
pingTime time.Duration
pingStartTime time.Time
lastRequestedBlock *chain.Block
lastRequestedBlock *types.Block
protocolCaps *ethutil.Value
}
@ -429,7 +429,7 @@ func (p *Peer) HandleInbound() {
// in the TxPool where it will undergo validation and
// processing when a new block is found
for i := 0; i < msg.Data.Len(); i++ {
tx := chain.NewTransactionFromValue(msg.Data.Get(i))
tx := types.NewTransactionFromValue(msg.Data.Get(i))
p.ethereum.TxPool().QueueTransaction(tx)
}
case wire.MsgGetPeersTy:
@ -535,7 +535,7 @@ func (p *Peer) HandleInbound() {
it := msg.Data.NewIterator()
for it.Next() {
block := chain.NewBlockFromRlpValue(it.Value())
block := types.NewBlockFromRlpValue(it.Value())
blockPool.Add(block, p)
p.lastBlockReceived = time.Now()
@ -543,7 +543,7 @@ func (p *Peer) HandleInbound() {
case wire.MsgNewBlockTy:
var (
blockPool = p.ethereum.blockPool
block = chain.NewBlockFromRlpValue(msg.Data.Get(0))
block = types.NewBlockFromRlpValue(msg.Data.Get(0))
td = msg.Data.Get(1).BigInt()
)

File diff suppressed because it is too large Load Diff

42
ptrie/cache.go Normal file
View File

@ -0,0 +1,42 @@
package ptrie
type Backend interface {
Get([]byte) ([]byte, error)
Put([]byte, []byte)
}
type Cache struct {
store map[string][]byte
backend Backend
}
func NewCache(backend Backend) *Cache {
return &Cache{make(map[string][]byte), backend}
}
func (self *Cache) Get(key []byte) []byte {
data := self.store[string(key)]
if data == nil {
data, _ = self.backend.Get(key)
}
return data
}
func (self *Cache) Put(key []byte, data []byte) {
self.store[string(key)] = data
}
func (self *Cache) Flush() {
for k, v := range self.store {
self.backend.Put([]byte(k), v)
}
// This will eventually grow too large. We'd could
// do a make limit on storage and push out not-so-popular nodes.
//self.Reset()
}
func (self *Cache) Reset() {
self.store = make(map[string][]byte)
}

69
ptrie/fullnode.go Normal file
View File

@ -0,0 +1,69 @@
package ptrie
type FullNode struct {
trie *Trie
nodes [17]Node
}
func NewFullNode(t *Trie) *FullNode {
return &FullNode{trie: t}
}
func (self *FullNode) Dirty() bool { return true }
func (self *FullNode) Value() Node {
self.nodes[16] = self.trie.trans(self.nodes[16])
return self.nodes[16]
}
func (self *FullNode) Branches() []Node {
return self.nodes[:16]
}
func (self *FullNode) Copy() Node {
nnode := NewFullNode(self.trie)
for i, node := range self.nodes {
nnode.nodes[i] = node
}
return nnode
}
// Returns the length of non-nil nodes
func (self *FullNode) Len() (amount int) {
for _, node := range self.nodes {
if node != nil {
amount++
}
}
return
}
func (self *FullNode) Hash() interface{} {
return self.trie.store(self)
}
func (self *FullNode) RlpData() interface{} {
t := make([]interface{}, 17)
for i, node := range self.nodes {
if node != nil {
t[i] = node.Hash()
} else {
t[i] = ""
}
}
return t
}
func (self *FullNode) set(k byte, value Node) {
self.nodes[int(k)] = value
}
func (self *FullNode) branch(i byte) Node {
if self.nodes[int(i)] != nil {
self.nodes[int(i)] = self.trie.trans(self.nodes[int(i)])
return self.nodes[int(i)]
}
return nil
}

22
ptrie/hashnode.go Normal file
View File

@ -0,0 +1,22 @@
package ptrie
type HashNode struct {
key []byte
}
func NewHash(key []byte) *HashNode {
return &HashNode{key}
}
func (self *HashNode) RlpData() interface{} {
return self.key
}
func (self *HashNode) Hash() interface{} {
return self.key
}
// These methods will never be called but we have to satisfy Node interface
func (self *HashNode) Value() Node { return nil }
func (self *HashNode) Dirty() bool { return true }
func (self *HashNode) Copy() Node { return self }

115
ptrie/iterator.go Normal file
View File

@ -0,0 +1,115 @@
package ptrie
import (
"bytes"
"github.com/ethereum/go-ethereum/trie"
)
type Iterator struct {
trie *Trie
Key []byte
Value []byte
}
func NewIterator(trie *Trie) *Iterator {
return &Iterator{trie: trie, Key: []byte{0}}
}
func (self *Iterator) Next() bool {
self.trie.mu.Lock()
defer self.trie.mu.Unlock()
key := trie.RemTerm(trie.CompactHexDecode(string(self.Key)))
k := self.next(self.trie.root, key)
self.Key = []byte(trie.DecodeCompact(k))
return len(k) > 0
}
func (self *Iterator) next(node Node, key []byte) []byte {
if node == nil {
return nil
}
switch node := node.(type) {
case *FullNode:
if len(key) > 0 {
k := self.next(node.branch(key[0]), key[1:])
if k != nil {
return append([]byte{key[0]}, k...)
}
}
var r byte
if len(key) > 0 {
r = key[0] + 1
}
for i := r; i < 16; i++ {
k := self.key(node.branch(byte(i)))
if k != nil {
return append([]byte{i}, k...)
}
}
case *ShortNode:
k := trie.RemTerm(node.Key())
if vnode, ok := node.Value().(*ValueNode); ok {
if bytes.Compare([]byte(k), key) > 0 {
self.Value = vnode.Val()
return k
}
} else {
cnode := node.Value()
var ret []byte
skey := key[len(k):]
if trie.BeginsWith(key, k) {
ret = self.next(cnode, skey)
} else if bytes.Compare(k, key[:len(k)]) > 0 {
ret = self.key(node)
}
if ret != nil {
return append(k, ret...)
}
}
}
return nil
}
func (self *Iterator) key(node Node) []byte {
switch node := node.(type) {
case *ShortNode:
// Leaf node
if vnode, ok := node.Value().(*ValueNode); ok {
k := trie.RemTerm(node.Key())
self.Value = vnode.Val()
return k
} else {
k := trie.RemTerm(node.Key())
return append(k, self.key(node.Value())...)
}
case *FullNode:
if node.Value() != nil {
self.Value = node.Value().(*ValueNode).Val()
return []byte{16}
}
for i := 0; i < 16; i++ {
k := self.key(node.branch(byte(i)))
if k != nil {
return append([]byte{byte(i)}, k...)
}
}
}
return nil
}

33
ptrie/iterator_test.go Normal file
View File

@ -0,0 +1,33 @@
package ptrie
import "testing"
func TestIterator(t *testing.T) {
trie := NewEmpty()
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"dog", "puppy"},
{"somethingveryoddindeedthis is", "myothernodedata"},
}
v := make(map[string]bool)
for _, val := range vals {
v[val.k] = false
trie.UpdateString(val.k, val.v)
}
trie.Commit()
it := trie.Iterator()
for it.Next() {
v[string(it.Key)] = true
}
for k, found := range v {
if !found {
t.Error("iterator didn't find", k)
}
}
}

40
ptrie/node.go Normal file
View File

@ -0,0 +1,40 @@
package ptrie
import "fmt"
var indices = []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "[17]"}
type Node interface {
Value() Node
Copy() Node // All nodes, for now, return them self
Dirty() bool
fstring(string) string
Hash() interface{}
RlpData() interface{}
}
// Value node
func (self *ValueNode) String() string { return self.fstring("") }
func (self *FullNode) String() string { return self.fstring("") }
func (self *ShortNode) String() string { return self.fstring("") }
func (self *ValueNode) fstring(ind string) string { return fmt.Sprintf("%s ", self.data) }
func (self *HashNode) fstring(ind string) string { return fmt.Sprintf("%x ", self.key) }
// Full node
func (self *FullNode) fstring(ind string) string {
resp := fmt.Sprintf("[\n%s ", ind)
for i, node := range self.nodes {
if node == nil {
resp += fmt.Sprintf("%s: <nil> ", indices[i])
} else {
resp += fmt.Sprintf("%s: %v", indices[i], node.fstring(ind+" "))
}
}
return resp + fmt.Sprintf("\n%s] ", ind)
}
// Short node
func (self *ShortNode) fstring(ind string) string {
return fmt.Sprintf("[ %s: %v ] ", self.key, self.value.fstring(ind+" "))
}

31
ptrie/shortnode.go Normal file
View File

@ -0,0 +1,31 @@
package ptrie
import "github.com/ethereum/go-ethereum/trie"
type ShortNode struct {
trie *Trie
key []byte
value Node
}
func NewShortNode(t *Trie, key []byte, value Node) *ShortNode {
return &ShortNode{t, []byte(trie.CompactEncode(key)), value}
}
func (self *ShortNode) Value() Node {
self.value = self.trie.trans(self.value)
return self.value
}
func (self *ShortNode) Dirty() bool { return true }
func (self *ShortNode) Copy() Node { return NewShortNode(self.trie, self.key, self.value) }
func (self *ShortNode) RlpData() interface{} {
return []interface{}{self.key, self.value.Hash()}
}
func (self *ShortNode) Hash() interface{} {
return self.trie.store(self)
}
func (self *ShortNode) Key() []byte {
return trie.CompactDecode(string(self.key))
}

312
ptrie/trie.go Normal file
View File

@ -0,0 +1,312 @@
package ptrie
import (
"bytes"
"container/list"
"fmt"
"sync"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/trie"
)
func ParanoiaCheck(t1 *Trie, backend Backend) (bool, *Trie) {
t2 := New(nil, backend)
it := t1.Iterator()
for it.Next() {
t2.Update(it.Key, it.Value)
}
return bytes.Compare(t2.Hash(), t1.Hash()) == 0, t2
}
type Trie struct {
mu sync.Mutex
root Node
roothash []byte
cache *Cache
revisions *list.List
}
func New(root []byte, backend Backend) *Trie {
trie := &Trie{}
trie.revisions = list.New()
trie.roothash = root
trie.cache = NewCache(backend)
if root != nil {
value := ethutil.NewValueFromBytes(trie.cache.Get(root))
trie.root = trie.mknode(value)
}
return trie
}
func (self *Trie) Iterator() *Iterator {
return NewIterator(self)
}
// Legacy support
func (self *Trie) Root() []byte { return self.Hash() }
func (self *Trie) Hash() []byte {
var hash []byte
if self.root != nil {
//hash = self.root.Hash().([]byte)
t := self.root.Hash()
if byts, ok := t.([]byte); ok {
hash = byts
} else {
hash = crypto.Sha3(ethutil.Encode(self.root.RlpData()))
}
} else {
hash = crypto.Sha3(ethutil.Encode(""))
}
if !bytes.Equal(hash, self.roothash) {
self.revisions.PushBack(self.roothash)
self.roothash = hash
}
return hash
}
func (self *Trie) Commit() {
// Hash first
self.Hash()
self.cache.Flush()
}
// Reset should only be called if the trie has been hashed
func (self *Trie) Reset() {
self.cache.Reset()
revision := self.revisions.Remove(self.revisions.Back()).([]byte)
self.roothash = revision
value := ethutil.NewValueFromBytes(self.cache.Get(self.roothash))
self.root = self.mknode(value)
}
func (self *Trie) UpdateString(key, value string) Node { return self.Update([]byte(key), []byte(value)) }
func (self *Trie) Update(key, value []byte) Node {
self.mu.Lock()
defer self.mu.Unlock()
k := trie.CompactHexDecode(string(key))
if len(value) != 0 {
self.root = self.insert(self.root, k, &ValueNode{self, value})
} else {
self.root = self.delete(self.root, k)
}
return self.root
}
func (self *Trie) GetString(key string) []byte { return self.Get([]byte(key)) }
func (self *Trie) Get(key []byte) []byte {
self.mu.Lock()
defer self.mu.Unlock()
k := trie.CompactHexDecode(string(key))
n := self.get(self.root, k)
if n != nil {
return n.(*ValueNode).Val()
}
return nil
}
func (self *Trie) DeleteString(key string) Node { return self.Delete([]byte(key)) }
func (self *Trie) Delete(key []byte) Node {
self.mu.Lock()
defer self.mu.Unlock()
k := trie.CompactHexDecode(string(key))
self.root = self.delete(self.root, k)
return self.root
}
func (self *Trie) insert(node Node, key []byte, value Node) Node {
if len(key) == 0 {
return value
}
if node == nil {
return NewShortNode(self, key, value)
}
switch node := node.(type) {
case *ShortNode:
k := node.Key()
cnode := node.Value()
if bytes.Equal(k, key) {
return NewShortNode(self, key, value)
}
var n Node
matchlength := trie.MatchingNibbleLength(key, k)
if matchlength == len(k) {
n = self.insert(cnode, key[matchlength:], value)
} else {
pnode := self.insert(nil, k[matchlength+1:], cnode)
nnode := self.insert(nil, key[matchlength+1:], value)
fulln := NewFullNode(self)
fulln.set(k[matchlength], pnode)
fulln.set(key[matchlength], nnode)
n = fulln
}
if matchlength == 0 {
return n
}
return NewShortNode(self, key[:matchlength], n)
case *FullNode:
cpy := node.Copy().(*FullNode)
cpy.set(key[0], self.insert(node.branch(key[0]), key[1:], value))
return cpy
default:
panic("Invalid node")
}
}
func (self *Trie) get(node Node, key []byte) Node {
if len(key) == 0 {
return node
}
if node == nil {
return nil
}
switch node := node.(type) {
case *ShortNode:
k := node.Key()
cnode := node.Value()
if len(key) >= len(k) && bytes.Equal(k, key[:len(k)]) {
return self.get(cnode, key[len(k):])
}
return nil
case *FullNode:
return self.get(node.branch(key[0]), key[1:])
default:
panic(fmt.Sprintf("%T: invalid node: %v", node, node))
}
}
func (self *Trie) delete(node Node, key []byte) Node {
if len(key) == 0 {
return nil
}
switch node := node.(type) {
case *ShortNode:
k := node.Key()
cnode := node.Value()
if bytes.Equal(key, k) {
return nil
} else if bytes.Equal(key[:len(k)], k) {
child := self.delete(cnode, key[len(k):])
var n Node
switch child := child.(type) {
case *ShortNode:
nkey := append(k, child.Key()...)
n = NewShortNode(self, nkey, child.Value())
case *FullNode:
n = NewShortNode(self, node.key, child)
}
return n
} else {
return node
}
case *FullNode:
n := node.Copy().(*FullNode)
n.set(key[0], self.delete(n.branch(key[0]), key[1:]))
pos := -1
for i := 0; i < 17; i++ {
if n.branch(byte(i)) != nil {
if pos == -1 {
pos = i
} else {
pos = -2
}
}
}
var nnode Node
if pos == 16 {
nnode = NewShortNode(self, []byte{16}, n.branch(byte(pos)))
} else if pos >= 0 {
cnode := n.branch(byte(pos))
switch cnode := cnode.(type) {
case *ShortNode:
// Stitch keys
k := append([]byte{byte(pos)}, cnode.Key()...)
nnode = NewShortNode(self, k, cnode.Value())
case *FullNode:
nnode = NewShortNode(self, []byte{byte(pos)}, n.branch(byte(pos)))
}
} else {
nnode = n
}
return nnode
default:
panic("Invalid node")
}
}
// casting functions and cache storing
func (self *Trie) mknode(value *ethutil.Value) Node {
l := value.Len()
switch l {
case 2:
return NewShortNode(self, trie.CompactDecode(string(value.Get(0).Bytes())), self.mknode(value.Get(1)))
case 17:
fnode := NewFullNode(self)
for i := 0; i < l; i++ {
fnode.set(byte(i), self.mknode(value.Get(i)))
}
return fnode
case 32:
return &HashNode{value.Bytes()}
default:
return &ValueNode{self, value.Bytes()}
}
}
func (self *Trie) trans(node Node) Node {
switch node := node.(type) {
case *HashNode:
value := ethutil.NewValueFromBytes(self.cache.Get(node.key))
return self.mknode(value)
default:
return node
}
}
func (self *Trie) store(node Node) interface{} {
data := ethutil.Encode(node)
if len(data) >= 32 {
key := crypto.Sha3(data)
self.cache.Put(key, data)
return key
}
return node.RlpData()
}

259
ptrie/trie_test.go Normal file
View File

@ -0,0 +1,259 @@
package ptrie
import (
"bytes"
"fmt"
"testing"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
)
type Db map[string][]byte
func (self Db) Get(k []byte) ([]byte, error) { return self[string(k)], nil }
func (self Db) Put(k, v []byte) { self[string(k)] = v }
// Used for testing
func NewEmpty() *Trie {
return New(nil, make(Db))
}
func TestEmptyTrie(t *testing.T) {
trie := NewEmpty()
res := trie.Hash()
exp := crypto.Sha3(ethutil.Encode(""))
if !bytes.Equal(res, exp) {
t.Errorf("expected %x got %x", exp, res)
}
}
func TestInsert(t *testing.T) {
trie := NewEmpty()
trie.UpdateString("doe", "reindeer")
trie.UpdateString("dog", "puppy")
trie.UpdateString("dogglesworth", "cat")
exp := ethutil.Hex2Bytes("8aad789dff2f538bca5d8ea56e8abe10f4c7ba3a5dea95fea4cd6e7c3a1168d3")
root := trie.Hash()
if !bytes.Equal(root, exp) {
t.Errorf("exp %x got %x", exp, root)
}
trie = NewEmpty()
trie.UpdateString("A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
exp = ethutil.Hex2Bytes("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab")
root = trie.Hash()
if !bytes.Equal(root, exp) {
t.Errorf("exp %x got %x", exp, root)
}
}
func TestGet(t *testing.T) {
trie := NewEmpty()
trie.UpdateString("doe", "reindeer")
trie.UpdateString("dog", "puppy")
trie.UpdateString("dogglesworth", "cat")
res := trie.GetString("dog")
if !bytes.Equal(res, []byte("puppy")) {
t.Errorf("expected puppy got %x", res)
}
unknown := trie.GetString("unknown")
if unknown != nil {
t.Errorf("expected nil got %x", unknown)
}
}
func TestDelete(t *testing.T) {
trie := NewEmpty()
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"ether", ""},
{"dog", "puppy"},
{"shaman", ""},
}
for _, val := range vals {
if val.v != "" {
trie.UpdateString(val.k, val.v)
} else {
trie.DeleteString(val.k)
}
}
hash := trie.Hash()
exp := ethutil.Hex2Bytes("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
if !bytes.Equal(hash, exp) {
t.Errorf("expected %x got %x", exp, hash)
}
}
func TestEmptyValues(t *testing.T) {
trie := NewEmpty()
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"ether", ""},
{"dog", "puppy"},
{"shaman", ""},
}
for _, val := range vals {
trie.UpdateString(val.k, val.v)
}
hash := trie.Hash()
exp := ethutil.Hex2Bytes("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
if !bytes.Equal(hash, exp) {
t.Errorf("expected %x got %x", exp, hash)
}
}
func TestReplication(t *testing.T) {
trie := NewEmpty()
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"ether", ""},
{"dog", "puppy"},
{"shaman", ""},
{"somethingveryoddindeedthis is", "myothernodedata"},
}
for _, val := range vals {
trie.UpdateString(val.k, val.v)
}
trie.Commit()
trie2 := New(trie.roothash, trie.cache.backend)
if string(trie2.GetString("horse")) != "stallion" {
t.Error("expected to have harse => stallion")
}
hash := trie2.Hash()
exp := trie.Hash()
if !bytes.Equal(hash, exp) {
t.Errorf("root failure. expected %x got %x", exp, hash)
}
}
func TestReset(t *testing.T) {
trie := NewEmpty()
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
}
for _, val := range vals {
trie.UpdateString(val.k, val.v)
}
trie.Commit()
before := ethutil.CopyBytes(trie.roothash)
trie.UpdateString("should", "revert")
trie.Hash()
// Should have no effect
trie.Hash()
trie.Hash()
// ###
trie.Reset()
after := ethutil.CopyBytes(trie.roothash)
if !bytes.Equal(before, after) {
t.Errorf("expected roots to be equal. %x - %x", before, after)
}
}
func TestParanoia(t *testing.T) {
t.Skip()
trie := NewEmpty()
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"ether", ""},
{"dog", "puppy"},
{"shaman", ""},
{"somethingveryoddindeedthis is", "myothernodedata"},
}
for _, val := range vals {
trie.UpdateString(val.k, val.v)
}
trie.Commit()
ok, t2 := ParanoiaCheck(trie, trie.cache.backend)
if !ok {
t.Errorf("trie paranoia check failed %x %x", trie.roothash, t2.roothash)
}
}
// Not an actual test
func TestOutput(t *testing.T) {
t.Skip()
base := "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
trie := NewEmpty()
for i := 0; i < 50; i++ {
trie.UpdateString(fmt.Sprintf("%s%d", base, i), "valueeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee")
}
fmt.Println("############################## FULL ################################")
fmt.Println(trie.root)
trie.Commit()
fmt.Println("############################## SMALL ################################")
trie2 := New(trie.roothash, trie.cache.backend)
trie2.GetString(base + "20")
fmt.Println(trie2.root)
}
func BenchmarkGets(b *testing.B) {
trie := NewEmpty()
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"ether", ""},
{"dog", "puppy"},
{"shaman", ""},
{"somethingveryoddindeedthis is", "myothernodedata"},
}
for _, val := range vals {
trie.UpdateString(val.k, val.v)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
trie.Get([]byte("horse"))
}
}
func BenchmarkUpdate(b *testing.B) {
trie := NewEmpty()
b.ResetTimer()
for i := 0; i < b.N; i++ {
trie.UpdateString(fmt.Sprintf("aaaaaaaaa%d", i), "value")
}
trie.Hash()
}

13
ptrie/valuenode.go Normal file
View File

@ -0,0 +1,13 @@
package ptrie
type ValueNode struct {
trie *Trie
data []byte
}
func (self *ValueNode) Value() Node { return self } // Best not to call :-)
func (self *ValueNode) Val() []byte { return self.data }
func (self *ValueNode) Dirty() bool { return true }
func (self *ValueNode) Copy() Node { return &ValueNode{self.trie, self.data} }
func (self *ValueNode) RlpData() interface{} { return self.data }
func (self *ValueNode) Hash() interface{} { return self.data }

667
rlp/decode.go Normal file
View File

@ -0,0 +1,667 @@
package rlp
import (
"encoding/binary"
"errors"
"fmt"
"io"
"math/big"
"reflect"
)
var (
errNoPointer = errors.New("rlp: interface given to Decode must be a pointer")
errDecodeIntoNil = errors.New("rlp: pointer given to Decode must not be nil")
)
// Decoder is implemented by types that require custom RLP
// decoding rules or need to decode into private fields.
//
// The DecodeRLP method should read one value from the given
// Stream. It is not forbidden to read less or more, but it might
// be confusing.
type Decoder interface {
DecodeRLP(*Stream) error
}
// Decode parses RLP-encoded data from r and stores the result
// in the value pointed to by val. Val must be a non-nil pointer.
//
// Decode uses the following type-dependent decoding rules:
//
// If the type implements the Decoder interface, decode calls
// DecodeRLP.
//
// To decode into a pointer, Decode will set the pointer to nil if the
// input has size zero or the input is a single byte with value zero.
// If the input has nonzero size, Decode will allocate a new value of
// the type being pointed to.
//
// To decode into a struct, Decode expects the input to be an RLP
// list. The decoded elements of the list are assigned to each public
// field in the order given by the struct's definition. If the input
// list has too few elements, no error is returned and the remaining
// fields will have the zero value.
// Recursive struct types are supported.
//
// To decode into a slice, the input must be a list and the resulting
// slice will contain the input elements in order.
// As a special case, if the slice has a byte-size element type, the input
// can also be an RLP string.
//
// To decode into a Go string, the input must be an RLP string. The
// bytes are taken as-is and will not necessarily be valid UTF-8.
//
// To decode into an integer type, the input must also be an RLP
// string. The bytes are interpreted as a big endian representation of
// the integer. If the RLP string is larger than the bit size of the
// type, Decode will return an error. Decode also supports *big.Int.
// There is no size limit for big integers.
//
// To decode into an interface value, Decode stores one of these
// in the value:
//
// []interface{}, for RLP lists
// []byte, for RLP strings
//
// Non-empty interface types are not supported, nor are bool, float32,
// float64, maps, channel types and functions.
func Decode(r ByteReader, val interface{}) error {
return NewStream(r).Decode(val)
}
func makeNumDecoder(typ reflect.Type) decoder {
kind := typ.Kind()
switch {
case kind <= reflect.Int64:
return decodeInt
case kind <= reflect.Uint64:
return decodeUint
default:
panic("fallthrough")
}
}
func decodeInt(s *Stream, val reflect.Value) error {
num, err := s.uint(val.Type().Bits())
if err != nil {
return err
}
val.SetInt(int64(num))
return nil
}
func decodeUint(s *Stream, val reflect.Value) error {
num, err := s.uint(val.Type().Bits())
if err != nil {
return err
}
val.SetUint(num)
return nil
}
func decodeString(s *Stream, val reflect.Value) error {
b, err := s.Bytes()
if err != nil {
return err
}
val.SetString(string(b))
return nil
}
func decodeBigIntNoPtr(s *Stream, val reflect.Value) error {
return decodeBigInt(s, val.Addr())
}
func decodeBigInt(s *Stream, val reflect.Value) error {
b, err := s.Bytes()
if err != nil {
return err
}
i := val.Interface().(*big.Int)
if i == nil {
i = new(big.Int)
val.Set(reflect.ValueOf(i))
}
i.SetBytes(b)
return nil
}
const maxInt = int(^uint(0) >> 1)
func makeListDecoder(typ reflect.Type) (decoder, error) {
etype := typ.Elem()
if etype.Kind() == reflect.Uint8 && !reflect.PtrTo(etype).Implements(decoderInterface) {
if typ.Kind() == reflect.Array {
return decodeByteArray, nil
} else {
return decodeByteSlice, nil
}
}
etypeinfo, err := cachedTypeInfo1(etype)
if err != nil {
return nil, err
}
var maxLen = maxInt
if typ.Kind() == reflect.Array {
maxLen = typ.Len()
}
dec := func(s *Stream, val reflect.Value) error {
return decodeList(s, val, etypeinfo.decoder, maxLen)
}
return dec, nil
}
// decodeList decodes RLP list elements into slices and arrays.
//
// The approach here is stolen from package json, although we differ
// in the semantics for arrays. package json discards remaining
// elements that would not fit into the array. We generate an error in
// this case because we'd be losing information.
func decodeList(s *Stream, val reflect.Value, elemdec decoder, maxelem int) error {
size, err := s.List()
if err != nil {
return err
}
if size == 0 {
if val.Kind() == reflect.Slice {
val.Set(reflect.MakeSlice(val.Type(), 0, 0))
} else {
zero(val, 0)
}
return s.ListEnd()
}
i := 0
for {
if i > maxelem {
return fmt.Errorf("rlp: input List has more than %d elements", maxelem)
}
if val.Kind() == reflect.Slice {
// grow slice if necessary
if i >= val.Cap() {
newcap := val.Cap() + val.Cap()/2
if newcap < 4 {
newcap = 4
}
newv := reflect.MakeSlice(val.Type(), val.Len(), newcap)
reflect.Copy(newv, val)
val.Set(newv)
}
if i >= val.Len() {
val.SetLen(i + 1)
}
}
// decode into element
if err := elemdec(s, val.Index(i)); err == EOL {
break
} else if err != nil {
return err
}
i++
}
if i < val.Len() {
if val.Kind() == reflect.Array {
// zero the rest of the array.
zero(val, i)
} else {
val.SetLen(i)
}
}
return s.ListEnd()
}
func decodeByteSlice(s *Stream, val reflect.Value) error {
kind, _, err := s.Kind()
if err != nil {
return err
}
if kind == List {
return decodeList(s, val, decodeUint, maxInt)
}
b, err := s.Bytes()
if err == nil {
val.SetBytes(b)
}
return err
}
var errStringDoesntFitArray = errors.New("rlp: string value doesn't fit into target array")
func decodeByteArray(s *Stream, val reflect.Value) error {
kind, size, err := s.Kind()
if err != nil {
return err
}
switch kind {
case Byte:
if val.Len() == 0 {
return errStringDoesntFitArray
}
bv, _ := s.Uint()
val.Index(0).SetUint(bv)
zero(val, 1)
case String:
if uint64(val.Len()) < size {
return errStringDoesntFitArray
}
slice := val.Slice(0, int(size)).Interface().([]byte)
if err := s.readFull(slice); err != nil {
return err
}
zero(val, int(size))
case List:
return decodeList(s, val, decodeUint, val.Len())
}
return nil
}
func zero(val reflect.Value, start int) {
z := reflect.Zero(val.Type().Elem())
for i := start; i < val.Len(); i++ {
val.Index(i).Set(z)
}
}
type field struct {
index int
info *typeinfo
}
func makeStructDecoder(typ reflect.Type) (decoder, error) {
var fields []field
for i := 0; i < typ.NumField(); i++ {
if f := typ.Field(i); f.PkgPath == "" { // exported
info, err := cachedTypeInfo1(f.Type)
if err != nil {
return nil, err
}
fields = append(fields, field{i, info})
}
}
dec := func(s *Stream, val reflect.Value) (err error) {
if _, err = s.List(); err != nil {
return err
}
for _, f := range fields {
err = f.info.decoder(s, val.Field(f.index))
if err == EOL {
// too few elements. leave the rest at their zero value.
break
} else if err != nil {
return err
}
}
if err = s.ListEnd(); err == errNotAtEOL {
err = errors.New("rlp: input List has too many elements")
}
return err
}
return dec, nil
}
func makePtrDecoder(typ reflect.Type) (decoder, error) {
etype := typ.Elem()
etypeinfo, err := cachedTypeInfo1(etype)
if err != nil {
return nil, err
}
dec := func(s *Stream, val reflect.Value) (err error) {
_, size, err := s.Kind()
if err != nil || size == 0 && s.byteval == 0 {
val.Set(reflect.Zero(typ)) // set to nil
return err
}
newval := val
if val.IsNil() {
newval = reflect.New(etype)
}
if err = etypeinfo.decoder(s, newval.Elem()); err == nil {
val.Set(newval)
}
return err
}
return dec, nil
}
var ifsliceType = reflect.TypeOf([]interface{}{})
func decodeInterface(s *Stream, val reflect.Value) error {
kind, _, err := s.Kind()
if err != nil {
return err
}
if kind == List {
slice := reflect.New(ifsliceType).Elem()
if err := decodeList(s, slice, decodeInterface, maxInt); err != nil {
return err
}
val.Set(slice)
} else {
b, err := s.Bytes()
if err != nil {
return err
}
val.Set(reflect.ValueOf(b))
}
return nil
}
// This decoder is used for non-pointer values of types
// that implement the Decoder interface using a pointer receiver.
func decodeDecoderNoPtr(s *Stream, val reflect.Value) error {
return val.Addr().Interface().(Decoder).DecodeRLP(s)
}
func decodeDecoder(s *Stream, val reflect.Value) error {
// Decoder instances are not handled using the pointer rule if the type
// implements Decoder with pointer receiver (i.e. always)
// because it might handle empty values specially.
// We need to allocate one here in this case, like makePtrDecoder does.
if val.Kind() == reflect.Ptr && val.IsNil() {
val.Set(reflect.New(val.Type().Elem()))
}
return val.Interface().(Decoder).DecodeRLP(s)
}
// Kind represents the kind of value contained in an RLP stream.
type Kind int
const (
Byte Kind = iota
String
List
)
func (k Kind) String() string {
switch k {
case Byte:
return "Byte"
case String:
return "String"
case List:
return "List"
default:
return fmt.Sprintf("Unknown(%d)", k)
}
}
var (
// EOL is returned when the end of the current list
// has been reached during streaming.
EOL = errors.New("rlp: end of list")
// Other errors
ErrExpectedString = errors.New("rlp: expected String or Byte")
ErrExpectedList = errors.New("rlp: expected List")
ErrElemTooLarge = errors.New("rlp: element is larger than containing list")
// internal errors
errNotInList = errors.New("rlp: call of ListEnd outside of any list")
errNotAtEOL = errors.New("rlp: call of ListEnd not positioned at EOL")
)
// ByteReader must be implemented by any input reader for a Stream. It
// is implemented by e.g. bufio.Reader and bytes.Reader.
type ByteReader interface {
io.Reader
io.ByteReader
}
// Stream can be used for piecemeal decoding of an input stream. This
// is useful if the input is very large or if the decoding rules for a
// type depend on the input structure. Stream does not keep an
// internal buffer. After decoding a value, the input reader will be
// positioned just before the type information for the next value.
//
// When decoding a list and the input position reaches the declared
// length of the list, all operations will return error EOL.
// The end of the list must be acknowledged using ListEnd to continue
// reading the enclosing list.
//
// Stream is not safe for concurrent use.
type Stream struct {
r ByteReader
uintbuf []byte
kind Kind // kind of value ahead
size uint64 // size of value ahead
byteval byte // value of single byte in type tag
stack []listpos
}
type listpos struct{ pos, size uint64 }
func NewStream(r ByteReader) *Stream {
return &Stream{r: r, uintbuf: make([]byte, 8), kind: -1}
}
// Bytes reads an RLP string and returns its contents as a byte slice.
// If the input does not contain an RLP string, the returned
// error will be ErrExpectedString.
func (s *Stream) Bytes() ([]byte, error) {
kind, size, err := s.Kind()
if err != nil {
return nil, err
}
switch kind {
case Byte:
s.kind = -1 // rearm Kind
return []byte{s.byteval}, nil
case String:
b := make([]byte, size)
if err = s.readFull(b); err != nil {
return nil, err
}
return b, nil
default:
return nil, ErrExpectedString
}
}
// Uint reads an RLP string of up to 8 bytes and returns its contents
// as an unsigned integer. If the input does not contain an RLP string, the
// returned error will be ErrExpectedString.
func (s *Stream) Uint() (uint64, error) {
return s.uint(64)
}
func (s *Stream) uint(maxbits int) (uint64, error) {
kind, size, err := s.Kind()
if err != nil {
return 0, err
}
switch kind {
case Byte:
s.kind = -1 // rearm Kind
return uint64(s.byteval), nil
case String:
if size > uint64(maxbits/8) {
return 0, fmt.Errorf("rlp: string is larger than %d bits", maxbits)
}
return s.readUint(byte(size))
default:
return 0, ErrExpectedString
}
}
// List starts decoding an RLP list. If the input does not contain a
// list, the returned error will be ErrExpectedList. When the list's
// end has been reached, any Stream operation will return EOL.
func (s *Stream) List() (size uint64, err error) {
kind, size, err := s.Kind()
if err != nil {
return 0, err
}
if kind != List {
return 0, ErrExpectedList
}
s.stack = append(s.stack, listpos{0, size})
s.kind = -1
s.size = 0
return size, nil
}
// ListEnd returns to the enclosing list.
// The input reader must be positioned at the end of a list.
func (s *Stream) ListEnd() error {
if len(s.stack) == 0 {
return errNotInList
}
tos := s.stack[len(s.stack)-1]
if tos.pos != tos.size {
return errNotAtEOL
}
s.stack = s.stack[:len(s.stack)-1] // pop
if len(s.stack) > 0 {
s.stack[len(s.stack)-1].pos += tos.size
}
s.kind = -1
s.size = 0
return nil
}
// Decode decodes a value and stores the result in the value pointed
// to by val. Please see the documentation for the Decode function
// to learn about the decoding rules.
func (s *Stream) Decode(val interface{}) error {
if val == nil {
return errDecodeIntoNil
}
rval := reflect.ValueOf(val)
rtyp := rval.Type()
if rtyp.Kind() != reflect.Ptr {
return errNoPointer
}
if rval.IsNil() {
return errDecodeIntoNil
}
info, err := cachedTypeInfo(rtyp.Elem())
if err != nil {
return err
}
return info.decoder(s, rval.Elem())
}
// Kind returns the kind and size of the next value in the
// input stream.
//
// The returned size is the number of bytes that make up the value.
// For kind == Byte, the size is zero because the value is
// contained in the type tag.
//
// The first call to Kind will read size information from the input
// reader and leave it positioned at the start of the actual bytes of
// the value. Subsequent calls to Kind (until the value is decoded)
// will not advance the input reader and return cached information.
func (s *Stream) Kind() (kind Kind, size uint64, err error) {
var tos *listpos
if len(s.stack) > 0 {
tos = &s.stack[len(s.stack)-1]
}
if s.kind < 0 {
if tos != nil && tos.pos == tos.size {
return 0, 0, EOL
}
kind, size, err = s.readKind()
if err != nil {
return 0, 0, err
}
s.kind, s.size = kind, size
}
if tos != nil && tos.pos+s.size > tos.size {
return 0, 0, ErrElemTooLarge
}
return s.kind, s.size, nil
}
func (s *Stream) readKind() (kind Kind, size uint64, err error) {
b, err := s.readByte()
if err != nil {
return 0, 0, err
}
s.byteval = 0
switch {
case b < 0x80:
// For a single byte whose value is in the [0x00, 0x7F] range, that byte
// is its own RLP encoding.
s.byteval = b
return Byte, 0, nil
case b < 0xB8:
// Otherwise, if a string is 0-55 bytes long,
// the RLP encoding consists of a single byte with value 0x80 plus the
// length of the string followed by the string. The range of the first
// byte is thus [0x80, 0xB7].
return String, uint64(b - 0x80), nil
case b < 0xC0:
// If a string is more than 55 bytes long, the
// RLP encoding consists of a single byte with value 0xB7 plus the length
// of the length of the string in binary form, followed by the length of
// the string, followed by the string. For example, a length-1024 string
// would be encoded as 0xB90400 followed by the string. The range of
// the first byte is thus [0xB8, 0xBF].
size, err = s.readUint(b - 0xB7)
return String, size, err
case b < 0xF8:
// If the total payload of a list
// (i.e. the combined length of all its items) is 0-55 bytes long, the
// RLP encoding consists of a single byte with value 0xC0 plus the length
// of the list followed by the concatenation of the RLP encodings of the
// items. The range of the first byte is thus [0xC0, 0xF7].
return List, uint64(b - 0xC0), nil
default:
// If the total payload of a list is more than 55 bytes long,
// the RLP encoding consists of a single byte with value 0xF7
// plus the length of the length of the payload in binary
// form, followed by the length of the payload, followed by
// the concatenation of the RLP encodings of the items. The
// range of the first byte is thus [0xF8, 0xFF].
size, err = s.readUint(b - 0xF7)
return List, size, err
}
}
func (s *Stream) readUint(size byte) (uint64, error) {
if size == 1 {
b, err := s.readByte()
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return uint64(b), err
}
start := int(8 - size)
for i := 0; i < start; i++ {
s.uintbuf[i] = 0
}
err := s.readFull(s.uintbuf[start:])
return binary.BigEndian.Uint64(s.uintbuf), err
}
func (s *Stream) readFull(buf []byte) (err error) {
s.willRead(uint64(len(buf)))
var nn, n int
for n < len(buf) && err == nil {
nn, err = s.r.Read(buf[n:])
n += nn
}
if err == io.EOF {
err = io.ErrUnexpectedEOF
}
return err
}
func (s *Stream) readByte() (byte, error) {
s.willRead(1)
b, err := s.r.ReadByte()
if len(s.stack) > 0 && err == io.EOF {
err = io.ErrUnexpectedEOF
}
return b, err
}
func (s *Stream) willRead(n uint64) {
s.kind = -1 // rearm Kind
if len(s.stack) > 0 {
s.stack[len(s.stack)-1].pos += n
}
}

476
rlp/decode_test.go Normal file
View File

@ -0,0 +1,476 @@
package rlp
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"io"
"math/big"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/ethutil"
)
func TestStreamKind(t *testing.T) {
tests := []struct {
input string
wantKind Kind
wantLen uint64
}{
{"00", Byte, 0},
{"01", Byte, 0},
{"7F", Byte, 0},
{"80", String, 0},
{"B7", String, 55},
{"B800", String, 0},
{"B90400", String, 1024},
{"BA000400", String, 1024},
{"BB00000400", String, 1024},
{"BFFFFFFFFFFFFFFFFF", String, ^uint64(0)},
{"C0", List, 0},
{"C8", List, 8},
{"F7", List, 55},
{"F800", List, 0},
{"F804", List, 4},
{"F90400", List, 1024},
{"FFFFFFFFFFFFFFFFFF", List, ^uint64(0)},
}
for i, test := range tests {
s := NewStream(bytes.NewReader(unhex(test.input)))
kind, len, err := s.Kind()
if err != nil {
t.Errorf("test %d: Type returned error: %v", i, err)
continue
}
if kind != test.wantKind {
t.Errorf("test %d: kind mismatch: got %d, want %d", i, kind, test.wantKind)
}
if len != test.wantLen {
t.Errorf("test %d: len mismatch: got %d, want %d", i, len, test.wantLen)
}
}
}
func TestStreamErrors(t *testing.T) {
type calls []string
tests := []struct {
string
calls
error
}{
{"", calls{"Kind"}, io.EOF},
{"", calls{"List"}, io.EOF},
{"", calls{"Uint"}, io.EOF},
{"C0", calls{"Bytes"}, ErrExpectedString},
{"C0", calls{"Uint"}, ErrExpectedString},
{"81", calls{"Bytes"}, io.ErrUnexpectedEOF},
{"81", calls{"Uint"}, io.ErrUnexpectedEOF},
{"BFFFFFFFFFFFFFFF", calls{"Bytes"}, io.ErrUnexpectedEOF},
{"89000000000000000001", calls{"Uint"}, errors.New("rlp: string is larger than 64 bits")},
{"00", calls{"List"}, ErrExpectedList},
{"80", calls{"List"}, ErrExpectedList},
{"C0", calls{"List", "Uint"}, EOL},
{"C801", calls{"List", "Uint", "Uint"}, io.ErrUnexpectedEOF},
{"C8C9", calls{"List", "Kind"}, ErrElemTooLarge},
{"C3C2010201", calls{"List", "List", "Uint", "Uint", "ListEnd", "Uint"}, EOL},
{"00", calls{"ListEnd"}, errNotInList},
{"C40102", calls{"List", "Uint", "ListEnd"}, errNotAtEOL},
}
testfor:
for i, test := range tests {
s := NewStream(bytes.NewReader(unhex(test.string)))
rs := reflect.ValueOf(s)
for j, call := range test.calls {
fval := rs.MethodByName(call)
ret := fval.Call(nil)
err := "<nil>"
if lastret := ret[len(ret)-1].Interface(); lastret != nil {
err = lastret.(error).Error()
}
if j == len(test.calls)-1 {
if err != test.error.Error() {
t.Errorf("test %d: last call (%s) error mismatch\ngot: %s\nwant: %v",
i, call, err, test.error)
}
} else if err != "<nil>" {
t.Errorf("test %d: call %d (%s) unexpected error: %q", i, j, call, err)
continue testfor
}
}
}
}
func TestStreamList(t *testing.T) {
s := NewStream(bytes.NewReader(unhex("C80102030405060708")))
len, err := s.List()
if err != nil {
t.Fatalf("List error: %v", err)
}
if len != 8 {
t.Fatalf("List returned invalid length, got %d, want 8", len)
}
for i := uint64(1); i <= 8; i++ {
v, err := s.Uint()
if err != nil {
t.Fatalf("Uint error: %v", err)
}
if i != v {
t.Errorf("Uint returned wrong value, got %d, want %d", v, i)
}
}
if _, err := s.Uint(); err != EOL {
t.Errorf("Uint error mismatch, got %v, want %v", err, EOL)
}
if err = s.ListEnd(); err != nil {
t.Fatalf("ListEnd error: %v", err)
}
}
func TestDecodeErrors(t *testing.T) {
r := bytes.NewReader(nil)
if err := Decode(r, nil); err != errDecodeIntoNil {
t.Errorf("Decode(r, nil) error mismatch, got %q, want %q", err, errDecodeIntoNil)
}
var nilptr *struct{}
if err := Decode(r, nilptr); err != errDecodeIntoNil {
t.Errorf("Decode(r, nilptr) error mismatch, got %q, want %q", err, errDecodeIntoNil)
}
if err := Decode(r, struct{}{}); err != errNoPointer {
t.Errorf("Decode(r, struct{}{}) error mismatch, got %q, want %q", err, errNoPointer)
}
expectErr := "rlp: type chan bool is not RLP-serializable"
if err := Decode(r, new(chan bool)); err == nil || err.Error() != expectErr {
t.Errorf("Decode(r, new(chan bool)) error mismatch, got %q, want %q", err, expectErr)
}
if err := Decode(r, new(int)); err != io.EOF {
t.Errorf("Decode(r, new(int)) error mismatch, got %q, want %q", err, io.EOF)
}
}
type decodeTest struct {
input string
ptr interface{}
value interface{}
error error
}
type simplestruct struct {
A int
B string
}
type recstruct struct {
I int
Child *recstruct
}
var (
veryBigInt = big.NewInt(0).Add(
big.NewInt(0).Lsh(big.NewInt(0xFFFFFFFFFFFFFF), 16),
big.NewInt(0xFFFF),
)
)
var (
sharedByteArray [5]byte
sharedPtr = new(*int)
)
var decodeTests = []decodeTest{
// integers
{input: "05", ptr: new(uint32), value: uint32(5)},
{input: "80", ptr: new(uint32), value: uint32(0)},
{input: "8105", ptr: new(uint32), value: uint32(5)},
{input: "820505", ptr: new(uint32), value: uint32(0x0505)},
{input: "83050505", ptr: new(uint32), value: uint32(0x050505)},
{input: "8405050505", ptr: new(uint32), value: uint32(0x05050505)},
{input: "850505050505", ptr: new(uint32), error: errors.New("rlp: string is larger than 32 bits")},
{input: "C0", ptr: new(uint32), error: ErrExpectedString},
// slices
{input: "C0", ptr: new([]int), value: []int{}},
{input: "C80102030405060708", ptr: new([]int), value: []int{1, 2, 3, 4, 5, 6, 7, 8}},
// arrays
{input: "C0", ptr: new([5]int), value: [5]int{}},
{input: "C50102030405", ptr: new([5]int), value: [5]int{1, 2, 3, 4, 5}},
{input: "C6010203040506", ptr: new([5]int), error: errors.New("rlp: input List has more than 5 elements")},
// byte slices
{input: "01", ptr: new([]byte), value: []byte{1}},
{input: "80", ptr: new([]byte), value: []byte{}},
{input: "8D6162636465666768696A6B6C6D", ptr: new([]byte), value: []byte("abcdefghijklm")},
{input: "C0", ptr: new([]byte), value: []byte{}},
{input: "C3010203", ptr: new([]byte), value: []byte{1, 2, 3}},
{input: "C3820102", ptr: new([]byte), error: errors.New("rlp: string is larger than 8 bits")},
// byte arrays
{input: "01", ptr: new([5]byte), value: [5]byte{1}},
{input: "80", ptr: new([5]byte), value: [5]byte{}},
{input: "850102030405", ptr: new([5]byte), value: [5]byte{1, 2, 3, 4, 5}},
{input: "C0", ptr: new([5]byte), value: [5]byte{}},
{input: "C3010203", ptr: new([5]byte), value: [5]byte{1, 2, 3, 0, 0}},
{input: "C3820102", ptr: new([5]byte), error: errors.New("rlp: string is larger than 8 bits")},
{input: "86010203040506", ptr: new([5]byte), error: errStringDoesntFitArray},
{input: "850101", ptr: new([5]byte), error: io.ErrUnexpectedEOF},
// byte array reuse (should be zeroed)
{input: "850102030405", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 4, 5}},
{input: "8101", ptr: &sharedByteArray, value: [5]byte{1}}, // kind: String
{input: "850102030405", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 4, 5}},
{input: "01", ptr: &sharedByteArray, value: [5]byte{1}}, // kind: Byte
{input: "C3010203", ptr: &sharedByteArray, value: [5]byte{1, 2, 3, 0, 0}},
{input: "C101", ptr: &sharedByteArray, value: [5]byte{1}}, // kind: List
// zero sized byte arrays
{input: "80", ptr: new([0]byte), value: [0]byte{}},
{input: "C0", ptr: new([0]byte), value: [0]byte{}},
{input: "01", ptr: new([0]byte), error: errStringDoesntFitArray},
{input: "8101", ptr: new([0]byte), error: errStringDoesntFitArray},
// strings
{input: "00", ptr: new(string), value: "\000"},
{input: "8D6162636465666768696A6B6C6D", ptr: new(string), value: "abcdefghijklm"},
{input: "C0", ptr: new(string), error: ErrExpectedString},
// big ints
{input: "01", ptr: new(*big.Int), value: big.NewInt(1)},
{input: "89FFFFFFFFFFFFFFFFFF", ptr: new(*big.Int), value: veryBigInt},
{input: "10", ptr: new(big.Int), value: *big.NewInt(16)}, // non-pointer also works
{input: "C0", ptr: new(*big.Int), error: ErrExpectedString},
// structs
{input: "C0", ptr: new(simplestruct), value: simplestruct{0, ""}},
{input: "C105", ptr: new(simplestruct), value: simplestruct{5, ""}},
{input: "C50583343434", ptr: new(simplestruct), value: simplestruct{5, "444"}},
{input: "C3010101", ptr: new(simplestruct), error: errors.New("rlp: input List has too many elements")},
{
input: "C501C302C103",
ptr: new(recstruct),
value: recstruct{1, &recstruct{2, &recstruct{3, nil}}},
},
// pointers
{input: "00", ptr: new(*int), value: (*int)(nil)},
{input: "80", ptr: new(*int), value: (*int)(nil)},
{input: "C0", ptr: new(*int), value: (*int)(nil)},
{input: "07", ptr: new(*int), value: intp(7)},
{input: "8108", ptr: new(*int), value: intp(8)},
{input: "C109", ptr: new(*[]int), value: &[]int{9}},
{input: "C58403030303", ptr: new(*[][]byte), value: &[][]byte{{3, 3, 3, 3}}},
// pointer should be reset to nil
{input: "05", ptr: sharedPtr, value: intp(5)},
{input: "80", ptr: sharedPtr, value: (*int)(nil)},
// interface{}
{input: "00", ptr: new(interface{}), value: []byte{0}},
{input: "01", ptr: new(interface{}), value: []byte{1}},
{input: "80", ptr: new(interface{}), value: []byte{}},
{input: "850505050505", ptr: new(interface{}), value: []byte{5, 5, 5, 5, 5}},
{input: "C0", ptr: new(interface{}), value: []interface{}{}},
{input: "C50183040404", ptr: new(interface{}), value: []interface{}{[]byte{1}, []byte{4, 4, 4}}},
}
func intp(i int) *int { return &i }
func TestDecode(t *testing.T) {
for i, test := range decodeTests {
input, err := hex.DecodeString(test.input)
if err != nil {
t.Errorf("test %d: invalid hex input %q", i, test.input)
continue
}
err = Decode(bytes.NewReader(input), test.ptr)
if err != nil && test.error == nil {
t.Errorf("test %d: unexpected Decode error: %v\ndecoding into %T\ninput %q",
i, err, test.ptr, test.input)
continue
}
if test.error != nil && fmt.Sprint(err) != fmt.Sprint(test.error) {
t.Errorf("test %d: Decode error mismatch\ngot %v\nwant %v\ndecoding into %T\ninput %q",
i, err, test.error, test.ptr, test.input)
continue
}
deref := reflect.ValueOf(test.ptr).Elem().Interface()
if err == nil && !reflect.DeepEqual(deref, test.value) {
t.Errorf("test %d: value mismatch\ngot %#v\nwant %#v\ndecoding into %T\ninput %q",
i, deref, test.value, test.ptr, test.input)
}
}
}
type testDecoder struct{ called bool }
func (t *testDecoder) DecodeRLP(s *Stream) error {
if _, err := s.Uint(); err != nil {
return err
}
t.called = true
return nil
}
func TestDecodeDecoder(t *testing.T) {
var s struct {
T1 testDecoder
T2 *testDecoder
T3 **testDecoder
}
if err := Decode(bytes.NewReader(unhex("C3010203")), &s); err != nil {
t.Fatalf("Decode error: %v", err)
}
if !s.T1.called {
t.Errorf("DecodeRLP was not called for (non-pointer) testDecoder")
}
if s.T2 == nil {
t.Errorf("*testDecoder has not been allocated")
} else if !s.T2.called {
t.Errorf("DecodeRLP was not called for *testDecoder")
}
if s.T3 == nil || *s.T3 == nil {
t.Errorf("**testDecoder has not been allocated")
} else if !(*s.T3).called {
t.Errorf("DecodeRLP was not called for **testDecoder")
}
}
type byteDecoder byte
func (bd *byteDecoder) DecodeRLP(s *Stream) error {
_, err := s.Uint()
*bd = 255
return err
}
func (bd byteDecoder) called() bool {
return bd == 255
}
// This test verifies that the byte slice/byte array logic
// does not kick in for element types implementing Decoder.
func TestDecoderInByteSlice(t *testing.T) {
var slice []byteDecoder
if err := Decode(bytes.NewReader(unhex("C101")), &slice); err != nil {
t.Errorf("unexpected Decode error %v", err)
} else if !slice[0].called() {
t.Errorf("DecodeRLP not called for slice element")
}
var array [1]byteDecoder
if err := Decode(bytes.NewReader(unhex("C101")), &array); err != nil {
t.Errorf("unexpected Decode error %v", err)
} else if !array[0].called() {
t.Errorf("DecodeRLP not called for array element")
}
}
func ExampleDecode() {
input, _ := hex.DecodeString("C90A1486666F6F626172")
type example struct {
A, B int
private int // private fields are ignored
String string
}
var s example
err := Decode(bytes.NewReader(input), &s)
if err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Printf("Decoded value: %#v\n", s)
}
// Output:
// Decoded value: rlp.example{A:10, B:20, private:0, String:"foobar"}
}
func ExampleStream() {
input, _ := hex.DecodeString("C90A1486666F6F626172")
s := NewStream(bytes.NewReader(input))
// Check what kind of value lies ahead
kind, size, _ := s.Kind()
fmt.Printf("Kind: %v size:%d\n", kind, size)
// Enter the list
if _, err := s.List(); err != nil {
fmt.Printf("List error: %v\n", err)
return
}
// Decode elements
fmt.Println(s.Uint())
fmt.Println(s.Uint())
fmt.Println(s.Bytes())
// Acknowledge end of list
if err := s.ListEnd(); err != nil {
fmt.Printf("ListEnd error: %v\n", err)
}
// Output:
// Kind: List size:9
// 10 <nil>
// 20 <nil>
// [102 111 111 98 97 114] <nil>
}
func BenchmarkDecode(b *testing.B) {
enc := encTest(90000)
b.SetBytes(int64(len(enc)))
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
var s []int
r := bytes.NewReader(enc)
if err := Decode(r, &s); err != nil {
b.Fatalf("Decode error: %v", err)
}
}
}
func BenchmarkDecodeIntSliceReuse(b *testing.B) {
enc := encTest(100000)
b.SetBytes(int64(len(enc)))
b.ReportAllocs()
b.ResetTimer()
var s []int
for i := 0; i < b.N; i++ {
r := bytes.NewReader(enc)
if err := Decode(r, &s); err != nil {
b.Fatalf("Decode error: %v", err)
}
}
}
func encTest(n int) []byte {
s := make([]interface{}, n)
for i := 0; i < n; i++ {
s[i] = i
}
return ethutil.Encode(s)
}
func unhex(str string) []byte {
b, err := hex.DecodeString(str)
if err != nil {
panic(fmt.Sprintf("invalid hex string: %q", str))
}
return b
}

17
rlp/doc.go Normal file
View File

@ -0,0 +1,17 @@
/*
Package rlp implements the RLP serialization format.
The purpose of RLP (Recursive Linear Prefix) qis to encode arbitrarily
nested arrays of binary data, and RLP is the main encoding method used
to serialize objects in Ethereum. The only purpose of RLP is to encode
structure; encoding specific atomic data types (eg. strings, ints,
floats) is left up to higher-order protocols; in Ethereum integers
must be represented in big endian binary form with no leading zeroes
(thus making the integer value zero be equivalent to the empty byte
array).
RLP values are distinguished by a type tag. The type tag precedes the
value in the input stream and defines the size and kind of the bytes
that follow.
*/
package rlp

91
rlp/typecache.go Normal file
View File

@ -0,0 +1,91 @@
package rlp
import (
"fmt"
"math/big"
"reflect"
"sync"
)
type decoder func(*Stream, reflect.Value) error
type typeinfo struct {
decoder
}
var (
typeCacheMutex sync.RWMutex
typeCache = make(map[reflect.Type]*typeinfo)
)
func cachedTypeInfo(typ reflect.Type) (*typeinfo, error) {
typeCacheMutex.RLock()
info := typeCache[typ]
typeCacheMutex.RUnlock()
if info != nil {
return info, nil
}
// not in the cache, need to generate info for this type.
typeCacheMutex.Lock()
defer typeCacheMutex.Unlock()
return cachedTypeInfo1(typ)
}
func cachedTypeInfo1(typ reflect.Type) (*typeinfo, error) {
info := typeCache[typ]
if info != nil {
// another goroutine got the write lock first
return info, nil
}
// put a dummmy value into the cache before generating.
// if the generator tries to lookup itself, it will get
// the dummy value and won't call itself recursively.
typeCache[typ] = new(typeinfo)
info, err := genTypeInfo(typ)
if err != nil {
// remove the dummy value if the generator fails
delete(typeCache, typ)
return nil, err
}
*typeCache[typ] = *info
return typeCache[typ], err
}
var (
decoderInterface = reflect.TypeOf(new(Decoder)).Elem()
bigInt = reflect.TypeOf(big.Int{})
)
func genTypeInfo(typ reflect.Type) (info *typeinfo, err error) {
info = new(typeinfo)
kind := typ.Kind()
switch {
case typ.Implements(decoderInterface):
info.decoder = decodeDecoder
case kind != reflect.Ptr && reflect.PtrTo(typ).Implements(decoderInterface):
info.decoder = decodeDecoderNoPtr
case typ.AssignableTo(reflect.PtrTo(bigInt)):
info.decoder = decodeBigInt
case typ.AssignableTo(bigInt):
info.decoder = decodeBigIntNoPtr
case isInteger(kind):
info.decoder = makeNumDecoder(typ)
case kind == reflect.String:
info.decoder = decodeString
case kind == reflect.Slice || kind == reflect.Array:
info.decoder, err = makeListDecoder(typ)
case kind == reflect.Struct:
info.decoder, err = makeStructDecoder(typ)
case kind == reflect.Ptr:
info.decoder, err = makePtrDecoder(typ)
case kind == reflect.Interface && typ.NumMethod() == 0:
info.decoder = decodeInterface
default:
err = fmt.Errorf("rlp: type %v is not RLP-serializable", typ)
}
return info, err
}
func isInteger(k reflect.Kind) bool {
return k >= reflect.Int && k <= reflect.Uintptr
}

View File

@ -46,3 +46,11 @@ func (self *State) Dump() []byte {
return json
}
// Debug stuff
func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.balance.Bytes(), self.Nonce)
self.EachStorage(func(addr string, value *ethutil.Value) {
fmt.Printf("%x %x\n", addr, value.Bytes())
})
}

View File

@ -148,9 +148,7 @@ func (self *StateObject) EachStorage(cb trie.EachCallback) {
func (self *StateObject) Sync() {
for key, value := range self.storage {
if value.Len() == 0 { // value.BigInt().Cmp(ethutil.Big0) == 0 {
//data := self.getStorage([]byte(key))
//fmt.Printf("deleting %x %x 0x%x\n", self.Address(), []byte(key), data)
if value.Len() == 0 {
self.State.Trie.Delete(string(key))
continue
}
@ -287,14 +285,6 @@ func (self *StateObject) Root() []byte {
return self.State.Trie.GetRoot()
}
// Debug stuff
func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.balance.Bytes(), self.Nonce)
self.EachStorage(func(addr string, value *ethutil.Value) {
fmt.Printf("%x %x\n", addr, value.Bytes())
})
}
//
// Encoding
//

View File

@ -197,7 +197,12 @@ func (t *Trie) Update(key, value string) {
k := CompactHexDecode(key)
root := t.UpdateState(t.Root, k, value)
var root interface{}
if value != "" {
root = t.UpdateState(t.Root, k, value)
} else {
root = t.deleteState(t.Root, k)
}
t.setRoot(root)
}

View File

@ -1,12 +1,14 @@
package trie
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"testing"
"time"
checker "gopkg.in/check.v1"
@ -387,3 +389,59 @@ func TestRndCase(t *testing.T) {
fmt.Printf("%x\n", trie.Get(string(ethutil.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))))
}
*/
func TestOtherSomething(t *testing.T) {
_, trie := NewTrie()
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"ether", ""},
{"dog", "puppy"},
{"shaman", ""},
}
for _, val := range vals {
trie.Update(val.k, val.v)
}
exp := ethutil.Hex2Bytes("5991bb8c6514148a29db676a14ac506cd2cd5775ace63c30a4fe457715e9ac84")
hash := trie.Root.([]byte)
if !bytes.Equal(hash, exp) {
t.Errorf("expected %x got %x", exp, hash)
}
}
func BenchmarkGets(b *testing.B) {
_, trie := NewTrie()
vals := []struct{ k, v string }{
{"do", "verb"},
{"ether", "wookiedoo"},
{"horse", "stallion"},
{"shaman", "horse"},
{"doge", "coin"},
{"ether", ""},
{"dog", "puppy"},
{"shaman", ""},
{"somethingveryoddindeedthis is", "myothernodedata"},
}
for _, val := range vals {
trie.Update(val.k, val.v)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
trie.Get("horse")
}
}
func BenchmarkUpdate(b *testing.B) {
_, trie := NewTrie()
b.ResetTimer()
for i := 0; i < b.N; i++ {
trie.Update(fmt.Sprintf("aaaaaaaaaaaaaaa%d", i), "value")
}
}

View File

@ -805,7 +805,6 @@ func (self *DebugVm) RunClosure(closure *Closure) (ret []byte, err error) {
stack.Push(closure.Gas)
// 0x60 range
case CREATE:
var (
err error
value = stack.Pop()

View File

@ -6,6 +6,7 @@ import (
"sync/atomic"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
@ -209,7 +210,7 @@ func (self *JSXEth) Transact(key, toStr, valueStr, gasStr, gasPriceStr, codeStr
gas = ethutil.Big(gasStr)
gasPrice = ethutil.Big(gasPriceStr)
data []byte
tx *chain.Transaction
tx *types.Transaction
)
if ethutil.IsHex(codeStr) {
@ -219,9 +220,9 @@ func (self *JSXEth) Transact(key, toStr, valueStr, gasStr, gasPriceStr, codeStr
}
if contractCreation {
tx = chain.NewContractCreationTx(value, gas, gasPrice, data)
tx = types.NewContractCreationTx(value, gas, gasPrice, data)
} else {
tx = chain.NewTransactionMessage(hash, value, gas, gasPrice, data)
tx = types.NewTransactionMessage(hash, value, gas, gasPrice, data)
}
acc := self.obj.BlockManager().TransState().GetOrNewStateObject(keyPair.Address())
@ -240,7 +241,7 @@ func (self *JSXEth) Transact(key, toStr, valueStr, gasStr, gasPriceStr, codeStr
}
func (self *JSXEth) PushTx(txStr string) (*JSReceipt, error) {
tx := chain.NewTransactionFromBytes(ethutil.Hex2Bytes(txStr))
tx := types.NewTransactionFromBytes(ethutil.Hex2Bytes(txStr))
self.obj.TxPool().QueueTransaction(tx)
return NewJSReciept(tx.CreatesContract(), tx.CreationAddress(self.World().State()), tx.Hash(), tx.Sender()), nil
}

View File

@ -6,6 +6,7 @@ import (
"strings"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/state"
@ -14,7 +15,7 @@ import (
// Block interface exposed to QML
type JSBlock struct {
//Transactions string `json:"transactions"`
ref *chain.Block
ref *types.Block
Size string `json:"size"`
Number int `json:"number"`
Hash string `json:"hash"`
@ -31,7 +32,7 @@ type JSBlock struct {
}
// Creates a new QML Block from a chain block
func NewJSBlock(block *chain.Block) *JSBlock {
func NewJSBlock(block *types.Block) *JSBlock {
if block == nil {
return &JSBlock{}
}
@ -79,7 +80,7 @@ func (self *JSBlock) GetTransaction(hash string) *JSTransaction {
}
type JSTransaction struct {
ref *chain.Transaction
ref *types.Transaction
Value string `json:"value"`
Gas string `json:"gas"`
@ -94,7 +95,7 @@ type JSTransaction struct {
Confirmations int `json:"confirmations"`
}
func NewJSTx(tx *chain.Transaction, state *state.State) *JSTransaction {
func NewJSTx(tx *types.Transaction, state *state.State) *JSTransaction {
hash := ethutil.Bytes2Hex(tx.Hash())
receiver := ethutil.Bytes2Hex(tx.Recipient)
if receiver == "0000000000000000000000000000000000000000" {

View File

@ -9,6 +9,7 @@ import (
"strings"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethutil"
"github.com/ethereum/go-ethereum/logger"
@ -72,7 +73,7 @@ func (self *XEth) ExecuteObject(object *Object, data []byte, value, gas, price *
return ret, err
}
func (self *XEth) Block(hash []byte) *chain.Block {
func (self *XEth) Block(hash []byte) *types.Block {
return self.blockChain.GetBlock(hash)
}
@ -115,7 +116,7 @@ func (self *XEth) Transact(key *crypto.KeyPair, rec []byte, value, gas, price *e
contractCreation = true
}
var tx *chain.Transaction
var tx *types.Transaction
// Compile and assemble the given data
if contractCreation {
script, err := ethutil.Compile(string(data), false)
@ -123,7 +124,7 @@ func (self *XEth) Transact(key *crypto.KeyPair, rec []byte, value, gas, price *e
return nil, err
}
tx = chain.NewContractCreationTx(value.BigInt(), gas.BigInt(), price.BigInt(), script)
tx = types.NewContractCreationTx(value.BigInt(), gas.BigInt(), price.BigInt(), script)
} else {
data := ethutil.StringToByteFunc(string(data), func(s string) (ret []byte) {
slice := strings.Split(s, "\n")
@ -134,7 +135,7 @@ func (self *XEth) Transact(key *crypto.KeyPair, rec []byte, value, gas, price *e
return
})
tx = chain.NewTransactionMessage(hash, value.BigInt(), gas.BigInt(), price.BigInt(), data)
tx = types.NewTransactionMessage(hash, value.BigInt(), gas.BigInt(), price.BigInt(), data)
}
acc := self.blockManager.TransState().GetOrNewStateObject(key.Address())
@ -155,7 +156,7 @@ func (self *XEth) Transact(key *crypto.KeyPair, rec []byte, value, gas, price *e
return tx.Hash(), nil
}
func (self *XEth) PushTx(tx *chain.Transaction) ([]byte, error) {
func (self *XEth) PushTx(tx *types.Transaction) ([]byte, error) {
self.obj.TxPool().QueueTransaction(tx)
if tx.Recipient == nil {
addr := tx.CreationAddress(self.World().State())

View File

@ -2,20 +2,19 @@ package xeth
import (
"math/big"
"github.com/ethereum/go-ethereum/chain"
"github.com/ethereum/go-ethereum/chain/types"
"github.com/ethereum/go-ethereum/state"
"github.com/ethereum/go-ethereum/vm"
)
type VMEnv struct {
state *state.State
block *chain.Block
block *types.Block
value *big.Int
sender []byte
}
func NewEnv(state *state.State, block *chain.Block, value *big.Int, sender []byte) *VMEnv {
func NewEnv(state *state.State, block *types.Block, value *big.Int, sender []byte) *VMEnv {
return &VMEnv{
state: state,
block: block,