commit
ef7961b7d2
92
README.md
92
README.md
@ -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
|
||||
============
|
||||
|
@ -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())
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 }
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -1 +0,0 @@
|
||||
package chain
|
@ -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
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package chain
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
@ -1,4 +1,4 @@
|
||||
package chain
|
||||
package types
|
||||
|
||||
/*
|
||||
import (
|
10
chain/types/common.go
Normal file
10
chain/types/common.go
Normal 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)
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package chain
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/ethutil"
|
@ -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()
|
@ -1,4 +1,4 @@
|
||||
package chain
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
1
chain/types/transaction_test.go
Normal file
1
chain/types/transaction_test.go
Normal file
@ -0,0 +1 @@
|
||||
package types
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
@ -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))
|
||||
|
@ -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
12
peer.go
@ -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()
|
||||
)
|
||||
|
||||
|
3294
profile.cov
3294
profile.cov
File diff suppressed because it is too large
Load Diff
42
ptrie/cache.go
Normal file
42
ptrie/cache.go
Normal 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
69
ptrie/fullnode.go
Normal 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
22
ptrie/hashnode.go
Normal 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
115
ptrie/iterator.go
Normal 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
33
ptrie/iterator_test.go
Normal 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
40
ptrie/node.go
Normal 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
31
ptrie/shortnode.go
Normal 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
312
ptrie/trie.go
Normal 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
259
ptrie/trie_test.go
Normal 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
13
ptrie/valuenode.go
Normal 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
667
rlp/decode.go
Normal 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
476
rlp/decode_test.go
Normal 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
17
rlp/doc.go
Normal 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
91
rlp/typecache.go
Normal 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
|
||||
}
|
@ -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())
|
||||
})
|
||||
}
|
||||
|
@ -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
|
||||
//
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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" {
|
||||
|
11
xeth/pipe.go
11
xeth/pipe.go
@ -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())
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user