mirror of https://github.com/ethereum/go-ethereum
parent
c2fb9f06ad
commit
f6d1bfe45b
@ -0,0 +1,12 @@ |
||||
# See http://help.github.com/ignore-files/ for more about ignoring files. |
||||
# |
||||
# If you find yourself ignoring temporary files generated by your text editor |
||||
# or operating system, you probably want to add a global ignore instead: |
||||
# git config --global core.excludesfile ~/.gitignore_global |
||||
|
||||
/tmp |
||||
*/**/*un~ |
||||
*un~ |
||||
.DS_Store |
||||
*/**/.DS_Store |
||||
|
@ -0,0 +1,363 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"fmt" |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"math/big" |
||||
"time" |
||||
) |
||||
|
||||
type BlockInfo struct { |
||||
Number uint64 |
||||
Hash []byte |
||||
Parent []byte |
||||
} |
||||
|
||||
func (bi *BlockInfo) RlpDecode(data []byte) { |
||||
decoder := ethutil.NewValueFromBytes(data) |
||||
|
||||
bi.Number = decoder.Get(0).Uint() |
||||
bi.Hash = decoder.Get(1).Bytes() |
||||
bi.Parent = decoder.Get(2).Bytes() |
||||
} |
||||
|
||||
func (bi *BlockInfo) RlpEncode() []byte { |
||||
return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent}) |
||||
} |
||||
|
||||
type Block struct { |
||||
// Hash to the previous block
|
||||
PrevHash []byte |
||||
// Uncles of this block
|
||||
Uncles []*Block |
||||
UncleSha []byte |
||||
// The coin base address
|
||||
Coinbase []byte |
||||
// Block Trie state
|
||||
state *ethutil.Trie |
||||
// Difficulty for the current block
|
||||
Difficulty *big.Int |
||||
// Creation time
|
||||
Time int64 |
||||
// Extra data
|
||||
Extra string |
||||
// Block Nonce for verification
|
||||
Nonce []byte |
||||
// List of transactions and/or contracts
|
||||
transactions []*Transaction |
||||
TxSha []byte |
||||
} |
||||
|
||||
// New block takes a raw encoded string
|
||||
// XXX DEPRICATED
|
||||
func NewBlockFromData(raw []byte) *Block { |
||||
return NewBlockFromBytes(raw) |
||||
} |
||||
|
||||
func NewBlockFromBytes(raw []byte) *Block { |
||||
block := &Block{} |
||||
block.RlpDecode(raw) |
||||
|
||||
return block |
||||
} |
||||
|
||||
// New block takes a raw encoded string
|
||||
func NewBlockFromRlpValue(rlpValue *ethutil.Value) *Block { |
||||
block := &Block{} |
||||
block.RlpValueDecode(rlpValue) |
||||
|
||||
return block |
||||
} |
||||
|
||||
func CreateBlock(root interface{}, |
||||
prevHash []byte, |
||||
base []byte, |
||||
Difficulty *big.Int, |
||||
Nonce []byte, |
||||
extra string, |
||||
txes []*Transaction) *Block { |
||||
|
||||
block := &Block{ |
||||
// Slice of transactions to include in this block
|
||||
transactions: txes, |
||||
PrevHash: prevHash, |
||||
Coinbase: base, |
||||
Difficulty: Difficulty, |
||||
Nonce: Nonce, |
||||
Time: time.Now().Unix(), |
||||
Extra: extra, |
||||
UncleSha: EmptyShaList, |
||||
} |
||||
block.SetTransactions(txes) |
||||
block.SetUncles([]*Block{}) |
||||
|
||||
block.state = ethutil.NewTrie(ethutil.Config.Db, root) |
||||
|
||||
for _, tx := range txes { |
||||
block.MakeContract(tx) |
||||
} |
||||
|
||||
return block |
||||
} |
||||
|
||||
// Returns a hash of the block
|
||||
func (block *Block) Hash() []byte { |
||||
return ethutil.Sha3Bin(block.RlpValue().Encode()) |
||||
} |
||||
|
||||
func (block *Block) HashNoNonce() []byte { |
||||
return ethutil.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Extra})) |
||||
} |
||||
|
||||
func (block *Block) PrintHash() { |
||||
fmt.Println(block) |
||||
fmt.Println(ethutil.NewValue(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Extra, block.Nonce}))) |
||||
} |
||||
|
||||
func (block *Block) State() *ethutil.Trie { |
||||
return block.state |
||||
} |
||||
|
||||
func (block *Block) Transactions() []*Transaction { |
||||
return block.transactions |
||||
} |
||||
|
||||
func (block *Block) GetContract(addr []byte) *Contract { |
||||
data := block.state.Get(string(addr)) |
||||
if data == "" { |
||||
return nil |
||||
} |
||||
|
||||
contract := &Contract{} |
||||
contract.RlpDecode([]byte(data)) |
||||
|
||||
return contract |
||||
} |
||||
func (block *Block) UpdateContract(addr []byte, contract *Contract) { |
||||
// Make sure the state is synced
|
||||
contract.State().Sync() |
||||
|
||||
block.state.Update(string(addr), string(contract.RlpEncode())) |
||||
} |
||||
|
||||
func (block *Block) GetAddr(addr []byte) *Address { |
||||
var address *Address |
||||
|
||||
data := block.State().Get(string(addr)) |
||||
if data == "" { |
||||
address = NewAddress(big.NewInt(0)) |
||||
} else { |
||||
address = NewAddressFromData([]byte(data)) |
||||
} |
||||
|
||||
return address |
||||
} |
||||
func (block *Block) UpdateAddr(addr []byte, address *Address) { |
||||
block.state.Update(string(addr), string(address.RlpEncode())) |
||||
} |
||||
|
||||
func (block *Block) PayFee(addr []byte, fee *big.Int) bool { |
||||
contract := block.GetContract(addr) |
||||
// If we can't pay the fee return
|
||||
if contract == nil || contract.Amount.Cmp(fee) < 0 /* amount < fee */ { |
||||
fmt.Println("Contract has insufficient funds", contract.Amount, fee) |
||||
|
||||
return false |
||||
} |
||||
|
||||
base := new(big.Int) |
||||
contract.Amount = base.Sub(contract.Amount, fee) |
||||
block.state.Update(string(addr), string(contract.RlpEncode())) |
||||
|
||||
data := block.state.Get(string(block.Coinbase)) |
||||
|
||||
// Get the ether (Coinbase) and add the fee (gief fee to miner)
|
||||
ether := NewAddressFromData([]byte(data)) |
||||
|
||||
base = new(big.Int) |
||||
ether.Amount = base.Add(ether.Amount, fee) |
||||
|
||||
block.state.Update(string(block.Coinbase), string(ether.RlpEncode())) |
||||
|
||||
return true |
||||
} |
||||
|
||||
func (block *Block) BlockInfo() BlockInfo { |
||||
bi := BlockInfo{} |
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) |
||||
bi.RlpDecode(data) |
||||
|
||||
return bi |
||||
} |
||||
|
||||
func (block *Block) MakeContract(tx *Transaction) { |
||||
// Create contract if there's no recipient
|
||||
if tx.IsContract() { |
||||
addr := tx.Hash() |
||||
|
||||
value := tx.Value |
||||
contract := NewContract(value, []byte("")) |
||||
block.state.Update(string(addr), string(contract.RlpEncode())) |
||||
for i, val := range tx.Data { |
||||
contract.state.Update(string(ethutil.NumberToBytes(uint64(i), 32)), val) |
||||
} |
||||
block.UpdateContract(addr, contract) |
||||
} |
||||
} |
||||
|
||||
/////// Block Encoding
|
||||
func (block *Block) encodedUncles() interface{} { |
||||
uncles := make([]interface{}, len(block.Uncles)) |
||||
for i, uncle := range block.Uncles { |
||||
uncles[i] = uncle.RlpEncode() |
||||
} |
||||
|
||||
return uncles |
||||
} |
||||
|
||||
func (block *Block) encodedTxs() interface{} { |
||||
// Marshal the transactions of this block
|
||||
encTx := make([]interface{}, len(block.transactions)) |
||||
for i, tx := range block.transactions { |
||||
// Cast it to a string (safe)
|
||||
encTx[i] = tx.RlpData() |
||||
} |
||||
|
||||
return encTx |
||||
} |
||||
|
||||
func (block *Block) rlpTxs() interface{} { |
||||
// Marshal the transactions of this block
|
||||
encTx := make([]interface{}, len(block.transactions)) |
||||
for i, tx := range block.transactions { |
||||
// Cast it to a string (safe)
|
||||
encTx[i] = tx.RlpData() |
||||
} |
||||
|
||||
return encTx |
||||
} |
||||
|
||||
func (block *Block) rlpUncles() interface{} { |
||||
// Marshal the transactions of this block
|
||||
uncles := make([]interface{}, len(block.Uncles)) |
||||
for i, uncle := range block.Uncles { |
||||
// Cast it to a string (safe)
|
||||
uncles[i] = uncle.header() |
||||
} |
||||
|
||||
return uncles |
||||
} |
||||
|
||||
func (block *Block) SetUncles(uncles []*Block) { |
||||
block.Uncles = uncles |
||||
|
||||
// Sha of the concatenated uncles
|
||||
block.UncleSha = ethutil.Sha3Bin(ethutil.Encode(block.rlpUncles())) |
||||
} |
||||
|
||||
func (block *Block) SetTransactions(txs []*Transaction) { |
||||
block.transactions = txs |
||||
|
||||
block.TxSha = ethutil.Sha3Bin(ethutil.Encode(block.rlpTxs())) |
||||
} |
||||
|
||||
func (block *Block) RlpValue() *ethutil.RlpValue { |
||||
return ethutil.NewRlpValue([]interface{}{block.header(), block.rlpTxs(), block.rlpUncles()}) |
||||
} |
||||
|
||||
func (block *Block) RlpEncode() []byte { |
||||
// Encode a slice interface which contains the header and the list of
|
||||
// transactions.
|
||||
return block.RlpValue().Encode() |
||||
} |
||||
|
||||
func (block *Block) RlpDecode(data []byte) { |
||||
rlpValue := ethutil.NewValueFromBytes(data) |
||||
block.RlpValueDecode(rlpValue) |
||||
} |
||||
|
||||
func (block *Block) RlpValueDecode(decoder *ethutil.Value) { |
||||
header := decoder.Get(0) |
||||
|
||||
block.PrevHash = header.Get(0).Bytes() |
||||
block.UncleSha = header.Get(1).Bytes() |
||||
block.Coinbase = header.Get(2).Bytes() |
||||
block.state = ethutil.NewTrie(ethutil.Config.Db, header.Get(3).Val) |
||||
block.TxSha = header.Get(4).Bytes() |
||||
block.Difficulty = header.Get(5).BigInt() |
||||
block.Time = int64(header.Get(6).BigInt().Uint64()) |
||||
block.Extra = header.Get(7).Str() |
||||
block.Nonce = header.Get(8).Bytes() |
||||
|
||||
// Tx list might be empty if this is an uncle. Uncles only have their
|
||||
// header set.
|
||||
if decoder.Get(1).IsNil() == false { // Yes explicitness
|
||||
txes := decoder.Get(1) |
||||
block.transactions = make([]*Transaction, txes.Len()) |
||||
for i := 0; i < txes.Len(); i++ { |
||||
tx := NewTransactionFromValue(txes.Get(i)) |
||||
|
||||
block.transactions[i] = tx |
||||
|
||||
/* |
||||
if ethutil.Config.Debug { |
||||
ethutil.Config.Db.Put(tx.Hash(), ethutil.Encode(tx)) |
||||
} |
||||
*/ |
||||
} |
||||
|
||||
} |
||||
|
||||
if decoder.Get(2).IsNil() == false { // Yes explicitness
|
||||
uncles := decoder.Get(2) |
||||
block.Uncles = make([]*Block, uncles.Len()) |
||||
for i := 0; i < uncles.Len(); i++ { |
||||
block.Uncles[i] = NewUncleBlockFromValue(uncles.Get(i)) |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
func NewUncleBlockFromValue(header *ethutil.Value) *Block { |
||||
block := &Block{} |
||||
|
||||
block.PrevHash = header.Get(0).Bytes() |
||||
block.UncleSha = header.Get(1).Bytes() |
||||
block.Coinbase = header.Get(2).Bytes() |
||||
block.state = ethutil.NewTrie(ethutil.Config.Db, header.Get(3).Val) |
||||
block.TxSha = header.Get(4).Bytes() |
||||
block.Difficulty = header.Get(5).BigInt() |
||||
block.Time = int64(header.Get(6).BigInt().Uint64()) |
||||
block.Extra = header.Get(7).Str() |
||||
block.Nonce = header.Get(8).Bytes() |
||||
|
||||
return block |
||||
} |
||||
|
||||
func (block *Block) String() string { |
||||
return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce) |
||||
} |
||||
|
||||
//////////// UNEXPORTED /////////////////
|
||||
func (block *Block) header() []interface{} { |
||||
return []interface{}{ |
||||
// Sha of the previous block
|
||||
block.PrevHash, |
||||
// Sha of uncles
|
||||
block.UncleSha, |
||||
// Coinbase address
|
||||
block.Coinbase, |
||||
// root state
|
||||
block.state.Root, |
||||
// Sha of tx
|
||||
block.TxSha, |
||||
// Current block Difficulty
|
||||
block.Difficulty, |
||||
// Time the block was found?
|
||||
block.Time, |
||||
// Extra data
|
||||
block.Extra, |
||||
// Block's Nonce for validation
|
||||
block.Nonce, |
||||
} |
||||
} |
@ -0,0 +1,184 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"bytes" |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"log" |
||||
"math" |
||||
"math/big" |
||||
) |
||||
|
||||
type BlockChain struct { |
||||
// The famous, the fabulous Mister GENESIIIIIIS (block)
|
||||
genesisBlock *Block |
||||
// Last known total difficulty
|
||||
TD *big.Int |
||||
|
||||
LastBlockNumber uint64 |
||||
|
||||
CurrentBlock *Block |
||||
LastBlockHash []byte |
||||
} |
||||
|
||||
func NewBlockChain() *BlockChain { |
||||
bc := &BlockChain{} |
||||
bc.genesisBlock = NewBlockFromData(ethutil.Encode(Genesis)) |
||||
|
||||
bc.setLastBlock() |
||||
|
||||
return bc |
||||
} |
||||
|
||||
func (bc *BlockChain) Genesis() *Block { |
||||
return bc.genesisBlock |
||||
} |
||||
|
||||
func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block { |
||||
var root interface{} |
||||
var lastBlockTime int64 |
||||
hash := ZeroHash256 |
||||
|
||||
if bc.CurrentBlock != nil { |
||||
root = bc.CurrentBlock.State().Root |
||||
hash = bc.LastBlockHash |
||||
lastBlockTime = bc.CurrentBlock.Time |
||||
} |
||||
|
||||
block := CreateBlock( |
||||
root, |
||||
hash, |
||||
coinbase, |
||||
ethutil.BigPow(2, 32), |
||||
nil, |
||||
"", |
||||
txs) |
||||
|
||||
if bc.CurrentBlock != nil { |
||||
var mul *big.Int |
||||
if block.Time < lastBlockTime+42 { |
||||
mul = big.NewInt(1) |
||||
} else { |
||||
mul = big.NewInt(-1) |
||||
} |
||||
|
||||
diff := new(big.Int) |
||||
diff.Add(diff, bc.CurrentBlock.Difficulty) |
||||
diff.Div(diff, big.NewInt(1024)) |
||||
diff.Mul(diff, mul) |
||||
diff.Add(diff, bc.CurrentBlock.Difficulty) |
||||
block.Difficulty = diff |
||||
} |
||||
|
||||
return block |
||||
} |
||||
|
||||
func (bc *BlockChain) HasBlock(hash []byte) bool { |
||||
data, _ := ethutil.Config.Db.Get(hash) |
||||
return len(data) != 0 |
||||
} |
||||
|
||||
func (bc *BlockChain) GenesisBlock() *Block { |
||||
return bc.genesisBlock |
||||
} |
||||
|
||||
// Get chain return blocks from hash up to max in RLP format
|
||||
func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} { |
||||
var chain []interface{} |
||||
// Get the current hash to start with
|
||||
currentHash := bc.CurrentBlock.Hash() |
||||
// Get the last number on the block chain
|
||||
lastNumber := bc.BlockInfo(bc.CurrentBlock).Number |
||||
// Get the parents number
|
||||
parentNumber := bc.BlockInfoByHash(hash).Number |
||||
// Get the min amount. We might not have max amount of blocks
|
||||
count := uint64(math.Min(float64(lastNumber-parentNumber), float64(max))) |
||||
startNumber := parentNumber + count |
||||
|
||||
num := lastNumber |
||||
for ; num > startNumber; currentHash = bc.GetBlock(currentHash).PrevHash { |
||||
num-- |
||||
} |
||||
for i := uint64(0); bytes.Compare(currentHash, hash) != 0 && num >= parentNumber && i < count; i++ { |
||||
// Get the block of the chain
|
||||
block := bc.GetBlock(currentHash) |
||||
currentHash = block.PrevHash |
||||
|
||||
chain = append(chain, block.RlpValue().Value) |
||||
//chain = append([]interface{}{block.RlpValue().Value}, chain...)
|
||||
|
||||
num-- |
||||
} |
||||
|
||||
return chain |
||||
} |
||||
|
||||
func (bc *BlockChain) setLastBlock() { |
||||
data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) |
||||
if len(data) != 0 { |
||||
block := NewBlockFromBytes(data) |
||||
info := bc.BlockInfo(block) |
||||
bc.CurrentBlock = block |
||||
bc.LastBlockHash = block.Hash() |
||||
bc.LastBlockNumber = info.Number |
||||
|
||||
log.Printf("[CHAIN] Last known block height #%d\n", bc.LastBlockNumber) |
||||
} |
||||
|
||||
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
|
||||
bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) |
||||
} |
||||
|
||||
func (bc *BlockChain) SetTotalDifficulty(td *big.Int) { |
||||
ethutil.Config.Db.Put([]byte("LastKnownTotalDifficulty"), td.Bytes()) |
||||
bc.TD = td |
||||
} |
||||
|
||||
// Add a block to the chain and record addition information
|
||||
func (bc *BlockChain) Add(block *Block) { |
||||
bc.writeBlockInfo(block) |
||||
|
||||
// Prepare the genesis block
|
||||
bc.CurrentBlock = block |
||||
bc.LastBlockHash = block.Hash() |
||||
|
||||
ethutil.Config.Db.Put(block.Hash(), block.RlpEncode()) |
||||
} |
||||
|
||||
func (bc *BlockChain) GetBlock(hash []byte) *Block { |
||||
data, _ := ethutil.Config.Db.Get(hash) |
||||
|
||||
return NewBlockFromData(data) |
||||
} |
||||
|
||||
func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo { |
||||
bi := BlockInfo{} |
||||
data, _ := ethutil.Config.Db.Get(append(hash, []byte("Info")...)) |
||||
bi.RlpDecode(data) |
||||
|
||||
return bi |
||||
} |
||||
|
||||
func (bc *BlockChain) BlockInfo(block *Block) BlockInfo { |
||||
bi := BlockInfo{} |
||||
data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) |
||||
bi.RlpDecode(data) |
||||
|
||||
return bi |
||||
} |
||||
|
||||
// Unexported method for writing extra non-essential block info to the db
|
||||
func (bc *BlockChain) writeBlockInfo(block *Block) { |
||||
bc.LastBlockNumber++ |
||||
bi := BlockInfo{Number: bc.LastBlockNumber, Hash: block.Hash(), Parent: block.PrevHash} |
||||
|
||||
// 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()) |
||||
} |
||||
|
||||
func (bc *BlockChain) Stop() { |
||||
if bc.CurrentBlock != nil { |
||||
ethutil.Config.Db.Put([]byte("LastBlock"), bc.CurrentBlock.RlpEncode()) |
||||
|
||||
log.Println("[CHAIN] Stopped") |
||||
} |
||||
} |
@ -0,0 +1,627 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/hex" |
||||
"fmt" |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"github.com/obscuren/secp256k1-go" |
||||
"log" |
||||
"math" |
||||
"math/big" |
||||
"strconv" |
||||
"sync" |
||||
"time" |
||||
) |
||||
|
||||
type BlockProcessor interface { |
||||
ProcessBlock(block *Block) |
||||
} |
||||
|
||||
func CalculateBlockReward(block *Block, uncleLength int) *big.Int { |
||||
return BlockReward |
||||
} |
||||
|
||||
type BlockManager struct { |
||||
// Mutex for locking the block processor. Blocks can only be handled one at a time
|
||||
mutex sync.Mutex |
||||
|
||||
// The block chain :)
|
||||
bc *BlockChain |
||||
|
||||
// Stack for processing contracts
|
||||
stack *Stack |
||||
// non-persistent key/value memory storage
|
||||
mem map[string]*big.Int |
||||
|
||||
TransactionPool *TxPool |
||||
|
||||
Pow PoW |
||||
|
||||
Speaker PublicSpeaker |
||||
|
||||
SecondaryBlockProcessor BlockProcessor |
||||
} |
||||
|
||||
func AddTestNetFunds(block *Block) { |
||||
for _, addr := range []string{ |
||||
"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
|
||||
"93658b04240e4bd4046fd2d6d417d20f146f4b43", // Jeffrey
|
||||
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
|
||||
"80c01a26338f0d905e295fccb71fa9ea849ffa12", // Alex
|
||||
} { |
||||
//log.Println("2^200 Wei to", addr)
|
||||
codedAddr, _ := hex.DecodeString(addr) |
||||
addr := block.GetAddr(codedAddr) |
||||
addr.Amount = ethutil.BigPow(2, 200) |
||||
block.UpdateAddr(codedAddr, addr) |
||||
} |
||||
} |
||||
|
||||
func NewBlockManager(speaker PublicSpeaker) *BlockManager { |
||||
bm := &BlockManager{ |
||||
//server: s,
|
||||
bc: NewBlockChain(), |
||||
stack: NewStack(), |
||||
mem: make(map[string]*big.Int), |
||||
Pow: &EasyPow{}, |
||||
Speaker: speaker, |
||||
} |
||||
|
||||
if bm.bc.CurrentBlock == nil { |
||||
AddTestNetFunds(bm.bc.genesisBlock) |
||||
// Prepare the genesis block
|
||||
//bm.bc.genesisBlock.State().Sync()
|
||||
bm.bc.Add(bm.bc.genesisBlock) |
||||
log.Println(bm.bc.genesisBlock) |
||||
|
||||
log.Printf("Genesis: %x\n", bm.bc.genesisBlock.Hash()) |
||||
//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
|
||||
//bm.bc.genesisBlock.PrintHash()
|
||||
} |
||||
|
||||
return bm |
||||
} |
||||
|
||||
func (bm *BlockManager) BlockChain() *BlockChain { |
||||
return bm.bc |
||||
} |
||||
|
||||
func (bm *BlockManager) ApplyTransactions(block *Block, txs []*Transaction) { |
||||
// Process each transaction/contract
|
||||
for _, tx := range txs { |
||||
// If there's no recipient, it's a contract
|
||||
if tx.IsContract() { |
||||
block.MakeContract(tx) |
||||
bm.ProcessContract(tx, block) |
||||
} else { |
||||
bm.TransactionPool.ProcessTransaction(tx, block) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Block processing and validating with a given (temporarily) state
|
||||
func (bm *BlockManager) ProcessBlock(block *Block) error { |
||||
// Processing a blocks may never happen simultaneously
|
||||
bm.mutex.Lock() |
||||
defer bm.mutex.Unlock() |
||||
|
||||
hash := block.Hash() |
||||
|
||||
if bm.bc.HasBlock(hash) { |
||||
return nil |
||||
} |
||||
|
||||
/* |
||||
if ethutil.Config.Debug { |
||||
log.Printf("[BMGR] Processing block(%x)\n", hash) |
||||
} |
||||
*/ |
||||
|
||||
// Check if we have the parent hash, if it isn't known we discard it
|
||||
// Reasons might be catching up or simply an invalid block
|
||||
if !bm.bc.HasBlock(block.PrevHash) && bm.bc.CurrentBlock != nil { |
||||
return ParentError(block.PrevHash) |
||||
} |
||||
|
||||
// Process the transactions on to current block
|
||||
bm.ApplyTransactions(bm.bc.CurrentBlock, block.Transactions()) |
||||
|
||||
// Block validation
|
||||
if err := bm.ValidateBlock(block); err != nil { |
||||
return err |
||||
} |
||||
|
||||
// I'm not sure, but I don't know if there should be thrown
|
||||
// any errors at this time.
|
||||
if err := bm.AccumelateRewards(bm.bc.CurrentBlock, block); err != nil { |
||||
return err |
||||
} |
||||
|
||||
if !block.State().Cmp(bm.bc.CurrentBlock.State()) { |
||||
//if block.State().Root != state.Root {
|
||||
return fmt.Errorf("Invalid merkle root. Expected %x, got %x", block.State().Root, bm.bc.CurrentBlock.State().Root) |
||||
} |
||||
|
||||
// Calculate the new total difficulty and sync back to the db
|
||||
if bm.CalculateTD(block) { |
||||
// Sync the current block's state to the database
|
||||
bm.bc.CurrentBlock.State().Sync() |
||||
// Add the block to the chain
|
||||
bm.bc.Add(block) |
||||
|
||||
/* |
||||
ethutil.Config.Db.Put(block.Hash(), block.RlpEncode()) |
||||
bm.bc.CurrentBlock = block |
||||
bm.LastBlockHash = block.Hash() |
||||
bm.writeBlockInfo(block) |
||||
*/ |
||||
|
||||
/* |
||||
txs := bm.TransactionPool.Flush() |
||||
var coded = []interface{}{} |
||||
for _, tx := range txs { |
||||
err := bm.TransactionPool.ValidateTransaction(tx) |
||||
if err == nil { |
||||
coded = append(coded, tx.RlpEncode()) |
||||
} |
||||
} |
||||
*/ |
||||
|
||||
// Broadcast the valid block back to the wire
|
||||
//bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.RlpValue().Value})
|
||||
|
||||
// If there's a block processor present, pass in the block for further
|
||||
// processing
|
||||
if bm.SecondaryBlockProcessor != nil { |
||||
bm.SecondaryBlockProcessor.ProcessBlock(block) |
||||
} |
||||
|
||||
log.Printf("[BMGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash()) |
||||
} else { |
||||
fmt.Println("total diff failed") |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (bm *BlockManager) CalculateTD(block *Block) bool { |
||||
uncleDiff := new(big.Int) |
||||
for _, uncle := range block.Uncles { |
||||
uncleDiff = uncleDiff.Add(uncleDiff, uncle.Difficulty) |
||||
} |
||||
|
||||
// TD(genesis_block) = 0 and TD(B) = TD(B.parent) + sum(u.difficulty for u in B.uncles) + B.difficulty
|
||||
td := new(big.Int) |
||||
td = td.Add(bm.bc.TD, uncleDiff) |
||||
td = td.Add(td, block.Difficulty) |
||||
|
||||
// The new TD will only be accepted if the new difficulty is
|
||||
// is greater than the previous.
|
||||
if td.Cmp(bm.bc.TD) > 0 { |
||||
// Set the new total difficulty back to the block chain
|
||||
bm.bc.SetTotalDifficulty(td) |
||||
|
||||
/* |
||||
if ethutil.Config.Debug { |
||||
log.Println("[BMGR] TD(block) =", td) |
||||
} |
||||
*/ |
||||
|
||||
return true |
||||
} |
||||
|
||||
return false |
||||
} |
||||
|
||||
// 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 (bm *BlockManager) ValidateBlock(block *Block) error { |
||||
// TODO
|
||||
// 2. Check if the difficulty is correct
|
||||
|
||||
// Check each uncle's previous hash. In order for it to be valid
|
||||
// is if it has the same block hash as the current
|
||||
previousBlock := bm.bc.GetBlock(block.PrevHash) |
||||
for _, uncle := range block.Uncles { |
||||
if bytes.Compare(uncle.PrevHash, previousBlock.PrevHash) != 0 { |
||||
return ValidationError("Mismatch uncle's previous hash. Expected %x, got %x", previousBlock.PrevHash, uncle.PrevHash) |
||||
} |
||||
} |
||||
|
||||
diff := block.Time - bm.bc.CurrentBlock.Time |
||||
if diff < 0 { |
||||
return ValidationError("Block timestamp less then prev block %v", diff) |
||||
} |
||||
|
||||
// New blocks must be within the 15 minute range of the last block.
|
||||
if diff > int64(15*time.Minute) { |
||||
return ValidationError("Block is too far in the future of last block (> 15 minutes)") |
||||
} |
||||
|
||||
// Verify the nonce of the block. Return an error if it's not valid
|
||||
if !bm.Pow.Verify(block.HashNoNonce(), block.Difficulty, block.Nonce) { |
||||
return ValidationError("Block's nonce is invalid (= %v)", block.Nonce) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error { |
||||
// Get the coinbase rlp data
|
||||
addr := processor.GetAddr(block.Coinbase) |
||||
// Reward amount of ether to the coinbase address
|
||||
addr.AddFee(CalculateBlockReward(block, len(block.Uncles))) |
||||
|
||||
processor.UpdateAddr(block.Coinbase, addr) |
||||
|
||||
// TODO Reward each uncle
|
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (bm *BlockManager) Stop() { |
||||
bm.bc.Stop() |
||||
} |
||||
|
||||
func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) { |
||||
// Recovering function in case the VM had any errors
|
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
fmt.Println("Recovered from VM execution with err =", r) |
||||
} |
||||
}() |
||||
|
||||
// Process contract
|
||||
bm.ProcContract(tx, block, func(opType OpType) bool { |
||||
// TODO turn on once big ints are in place
|
||||
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) {
|
||||
// return false
|
||||
//}
|
||||
|
||||
return true // Continue
|
||||
}) |
||||
} |
||||
|
||||
// Contract evaluation is done here.
|
||||
func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallback) { |
||||
|
||||
// Instruction pointer
|
||||
pc := 0 |
||||
blockInfo := bm.bc.BlockInfo(block) |
||||
|
||||
contract := block.GetContract(tx.Hash()) |
||||
if contract == nil { |
||||
fmt.Println("Contract not found") |
||||
return |
||||
} |
||||
|
||||
Pow256 := ethutil.BigPow(2, 256) |
||||
|
||||
if ethutil.Config.Debug { |
||||
fmt.Printf("# op arg\n") |
||||
} |
||||
out: |
||||
for { |
||||
// The base big int for all calculations. Use this for any results.
|
||||
base := new(big.Int) |
||||
// XXX Should Instr return big int slice instead of string slice?
|
||||
// Get the next instruction from the contract
|
||||
//op, _, _ := Instr(contract.state.Get(string(Encode(uint32(pc)))))
|
||||
nb := ethutil.NumberToBytes(uint64(pc), 32) |
||||
o, _, _ := ethutil.Instr(contract.State().Get(string(nb))) |
||||
op := OpCode(o) |
||||
|
||||
if !cb(0) { |
||||
break |
||||
} |
||||
|
||||
if ethutil.Config.Debug { |
||||
fmt.Printf("%-3d %-4s\n", pc, op.String()) |
||||
} |
||||
|
||||
switch op { |
||||
case oSTOP: |
||||
break out |
||||
case oADD: |
||||
x, y := bm.stack.Popn() |
||||
// (x + y) % 2 ** 256
|
||||
base.Add(x, y) |
||||
base.Mod(base, Pow256) |
||||
// Pop result back on the stack
|
||||
bm.stack.Push(base) |
||||
case oSUB: |
||||
x, y := bm.stack.Popn() |
||||
// (x - y) % 2 ** 256
|
||||
base.Sub(x, y) |
||||
base.Mod(base, Pow256) |
||||
// Pop result back on the stack
|
||||
bm.stack.Push(base) |
||||
case oMUL: |
||||
x, y := bm.stack.Popn() |
||||
// (x * y) % 2 ** 256
|
||||
base.Mul(x, y) |
||||
base.Mod(base, Pow256) |
||||
// Pop result back on the stack
|
||||
bm.stack.Push(base) |
||||
case oDIV: |
||||
x, y := bm.stack.Popn() |
||||
// floor(x / y)
|
||||
base.Div(x, y) |
||||
// Pop result back on the stack
|
||||
bm.stack.Push(base) |
||||
case oSDIV: |
||||
x, y := bm.stack.Popn() |
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 { |
||||
x.Sub(Pow256, x) |
||||
} |
||||
if y.Cmp(Pow256) > 0 { |
||||
y.Sub(Pow256, y) |
||||
} |
||||
z := new(big.Int) |
||||
z.Div(x, y) |
||||
if z.Cmp(Pow256) > 0 { |
||||
z.Sub(Pow256, z) |
||||
} |
||||
// Push result on to the stack
|
||||
bm.stack.Push(z) |
||||
case oMOD: |
||||
x, y := bm.stack.Popn() |
||||
base.Mod(x, y) |
||||
bm.stack.Push(base) |
||||
case oSMOD: |
||||
x, y := bm.stack.Popn() |
||||
// n > 2**255
|
||||
if x.Cmp(Pow256) > 0 { |
||||
x.Sub(Pow256, x) |
||||
} |
||||
if y.Cmp(Pow256) > 0 { |
||||
y.Sub(Pow256, y) |
||||
} |
||||
z := new(big.Int) |
||||
z.Mod(x, y) |
||||
if z.Cmp(Pow256) > 0 { |
||||
z.Sub(Pow256, z) |
||||
} |
||||
// Push result on to the stack
|
||||
bm.stack.Push(z) |
||||
case oEXP: |
||||
x, y := bm.stack.Popn() |
||||
base.Exp(x, y, Pow256) |
||||
|
||||
bm.stack.Push(base) |
||||
case oNEG: |
||||
base.Sub(Pow256, bm.stack.Pop()) |
||||
bm.stack.Push(base) |
||||
case oLT: |
||||
x, y := bm.stack.Popn() |
||||
// x < y
|
||||
if x.Cmp(y) < 0 { |
||||
bm.stack.Push(ethutil.BigTrue) |
||||
} else { |
||||
bm.stack.Push(ethutil.BigFalse) |
||||
} |
||||
case oLE: |
||||
x, y := bm.stack.Popn() |
||||
// x <= y
|
||||
if x.Cmp(y) < 1 { |
||||
bm.stack.Push(ethutil.BigTrue) |
||||
} else { |
||||
bm.stack.Push(ethutil.BigFalse) |
||||
} |
||||
case oGT: |
||||
x, y := bm.stack.Popn() |
||||
// x > y
|
||||
if x.Cmp(y) > 0 { |
||||
bm.stack.Push(ethutil.BigTrue) |
||||
} else { |
||||
bm.stack.Push(ethutil.BigFalse) |
||||
} |
||||
case oGE: |
||||
x, y := bm.stack.Popn() |
||||
// x >= y
|
||||
if x.Cmp(y) > -1 { |
||||
bm.stack.Push(ethutil.BigTrue) |
||||
} else { |
||||
bm.stack.Push(ethutil.BigFalse) |
||||
} |
||||
case oNOT: |
||||
x, y := bm.stack.Popn() |
||||
// x != y
|
||||
if x.Cmp(y) != 0 { |
||||
bm.stack.Push(ethutil.BigTrue) |
||||
} else { |
||||
bm.stack.Push(ethutil.BigFalse) |
||||
} |
||||
|
||||
// Please note that the following code contains some
|
||||
// ugly string casting. This will have to change to big
|
||||
// ints. TODO :)
|
||||
case oMYADDRESS: |
||||
bm.stack.Push(ethutil.BigD(tx.Hash())) |
||||
case oTXSENDER: |
||||
bm.stack.Push(ethutil.BigD(tx.Sender())) |
||||
case oTXVALUE: |
||||
bm.stack.Push(tx.Value) |
||||
case oTXDATAN: |
||||
bm.stack.Push(big.NewInt(int64(len(tx.Data)))) |
||||
case oTXDATA: |
||||
v := bm.stack.Pop() |
||||
// v >= len(data)
|
||||
if v.Cmp(big.NewInt(int64(len(tx.Data)))) >= 0 { |
||||
bm.stack.Push(ethutil.Big("0")) |
||||
} else { |
||||
bm.stack.Push(ethutil.Big(tx.Data[v.Uint64()])) |
||||
} |
||||
case oBLK_PREVHASH: |
||||
bm.stack.Push(ethutil.BigD(block.PrevHash)) |
||||
case oBLK_COINBASE: |
||||
bm.stack.Push(ethutil.BigD(block.Coinbase)) |
||||
case oBLK_TIMESTAMP: |
||||
bm.stack.Push(big.NewInt(block.Time)) |
||||
case oBLK_NUMBER: |
||||
bm.stack.Push(big.NewInt(int64(blockInfo.Number))) |
||||
case oBLK_DIFFICULTY: |
||||
bm.stack.Push(block.Difficulty) |
||||
case oBASEFEE: |
||||
// e = 10^21
|
||||
e := big.NewInt(0).Exp(big.NewInt(10), big.NewInt(21), big.NewInt(0)) |
||||
d := new(big.Rat) |
||||
d.SetInt(block.Difficulty) |
||||
c := new(big.Rat) |
||||
c.SetFloat64(0.5) |
||||
// d = diff / 0.5
|
||||
d.Quo(d, c) |
||||
// base = floor(d)
|
||||
base.Div(d.Num(), d.Denom()) |
||||
|
||||
x := new(big.Int) |
||||
x.Div(e, base) |
||||
|
||||
// x = floor(10^21 / floor(diff^0.5))
|
||||
bm.stack.Push(x) |
||||
case oSHA256, oSHA3, oRIPEMD160: |
||||
// This is probably save
|
||||
// ceil(pop / 32)
|
||||
length := int(math.Ceil(float64(bm.stack.Pop().Uint64()) / 32.0)) |
||||
// New buffer which will contain the concatenated popped items
|
||||
data := new(bytes.Buffer) |
||||
for i := 0; i < length; i++ { |
||||
// Encode the number to bytes and have it 32bytes long
|
||||
num := ethutil.NumberToBytes(bm.stack.Pop().Bytes(), 256) |
||||
data.WriteString(string(num)) |
||||
} |
||||
|
||||
if op == oSHA256 { |
||||
bm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes()))) |
||||
} else if op == oSHA3 { |
||||
bm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes()))) |
||||
} else { |
||||
bm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes()))) |
||||
} |
||||
case oECMUL: |
||||
y := bm.stack.Pop() |
||||
x := bm.stack.Pop() |
||||
//n := bm.stack.Pop()
|
||||
|
||||
//if ethutil.Big(x).Cmp(ethutil.Big(y)) {
|
||||
data := new(bytes.Buffer) |
||||
data.WriteString(x.String()) |
||||
data.WriteString(y.String()) |
||||
if secp256k1.VerifyPubkeyValidity(data.Bytes()) == 1 { |
||||
// TODO
|
||||
} else { |
||||
// Invalid, push infinity
|
||||
bm.stack.Push(ethutil.Big("0")) |
||||
bm.stack.Push(ethutil.Big("0")) |
||||
} |
||||
//} else {
|
||||
// // Invalid, push infinity
|
||||
// bm.stack.Push("0")
|
||||
// bm.stack.Push("0")
|
||||
//}
|
||||
|
||||
case oECADD: |
||||
case oECSIGN: |
||||
case oECRECOVER: |
||||
case oECVALID: |
||||
case oPUSH: |
||||
pc++ |
||||
bm.stack.Push(bm.mem[strconv.Itoa(pc)]) |
||||
case oPOP: |
||||
// Pop current value of the stack
|
||||
bm.stack.Pop() |
||||
case oDUP: |
||||
// Dup top stack
|
||||
x := bm.stack.Pop() |
||||
bm.stack.Push(x) |
||||
bm.stack.Push(x) |
||||
case oSWAP: |
||||
// Swap two top most values
|
||||
x, y := bm.stack.Popn() |
||||
bm.stack.Push(y) |
||||
bm.stack.Push(x) |
||||
case oMLOAD: |
||||
x := bm.stack.Pop() |
||||
bm.stack.Push(bm.mem[x.String()]) |
||||
case oMSTORE: |
||||
x, y := bm.stack.Popn() |
||||
bm.mem[x.String()] = y |
||||
case oSLOAD: |
||||
// Load the value in storage and push it on the stack
|
||||
x := bm.stack.Pop() |
||||
// decode the object as a big integer
|
||||
decoder := ethutil.NewRlpValueFromBytes([]byte(contract.State().Get(x.String()))) |
||||
if !decoder.IsNil() { |
||||
bm.stack.Push(decoder.AsBigInt()) |
||||
} else { |
||||
bm.stack.Push(ethutil.BigFalse) |
||||
} |
||||
case oSSTORE: |
||||
// Store Y at index X
|
||||
x, y := bm.stack.Popn() |
||||
contract.State().Update(x.String(), string(ethutil.Encode(y))) |
||||
case oJMP: |
||||
x := int(bm.stack.Pop().Uint64()) |
||||
// Set pc to x - 1 (minus one so the incrementing at the end won't effect it)
|
||||
pc = x |
||||
pc-- |
||||
case oJMPI: |
||||
x := bm.stack.Pop() |
||||
// Set pc to x if it's non zero
|
||||
if x.Cmp(ethutil.BigFalse) != 0 { |
||||
pc = int(x.Uint64()) |
||||
pc-- |
||||
} |
||||
case oIND: |
||||
bm.stack.Push(big.NewInt(int64(pc))) |
||||
case oEXTRO: |
||||
memAddr := bm.stack.Pop() |
||||
contractAddr := bm.stack.Pop().Bytes() |
||||
|
||||
// Push the contract's memory on to the stack
|
||||
bm.stack.Push(getContractMemory(block, contractAddr, memAddr)) |
||||
case oBALANCE: |
||||
// Pushes the balance of the popped value on to the stack
|
||||
d := block.State().Get(bm.stack.Pop().String()) |
||||
ether := NewAddressFromData([]byte(d)) |
||||
bm.stack.Push(ether.Amount) |
||||
case oMKTX: |
||||
value, addr := bm.stack.Popn() |
||||
from, length := bm.stack.Popn() |
||||
|
||||
j := 0 |
||||
dataItems := make([]string, int(length.Uint64())) |
||||
for i := from.Uint64(); i < length.Uint64(); i++ { |
||||
dataItems[j] = string(bm.mem[strconv.Itoa(int(i))].Bytes()) |
||||
j++ |
||||
} |
||||
// TODO sign it?
|
||||
tx := NewTransaction(addr.Bytes(), value, dataItems) |
||||
// Add the transaction to the tx pool
|
||||
bm.TransactionPool.QueueTransaction(tx) |
||||
case oSUICIDE: |
||||
//addr := bm.stack.Pop()
|
||||
} |
||||
pc++ |
||||
} |
||||
} |
||||
|
||||
// Returns an address from the specified contract's address
|
||||
func getContractMemory(block *Block, contractAddr []byte, memAddr *big.Int) *big.Int { |
||||
contract := block.GetContract(contractAddr) |
||||
if contract == nil { |
||||
log.Panicf("invalid contract addr %x", contractAddr) |
||||
} |
||||
val := contract.State().Get(memAddr.String()) |
||||
|
||||
// decode the object as a big integer
|
||||
decoder := ethutil.NewRlpValueFromBytes([]byte(val)) |
||||
if decoder.IsNil() { |
||||
return ethutil.BigFalse |
||||
} |
||||
|
||||
return decoder.AsBigInt() |
||||
} |
@ -0,0 +1,75 @@ |
||||
package ethchain |
||||
|
||||
/* |
||||
import ( |
||||
_ "fmt" |
||||
"testing" |
||||
) |
||||
|
||||
func TestVm(t *testing.T) { |
||||
InitFees() |
||||
|
||||
db, _ := NewMemDatabase() |
||||
Db = db |
||||
|
||||
ctrct := NewTransaction("", 200000000, []string{ |
||||
"PUSH", "1a2f2e", |
||||
"PUSH", "hallo", |
||||
"POP", // POP hallo
|
||||
"PUSH", "3", |
||||
"LOAD", // Load hallo back on the stack
|
||||
|
||||
"PUSH", "1", |
||||
"PUSH", "2", |
||||
"ADD", |
||||
|
||||
"PUSH", "2", |
||||
"PUSH", "1", |
||||
"SUB", |
||||
|
||||
"PUSH", "100000000000000000000000", |
||||
"PUSH", "10000000000000", |
||||
"SDIV", |
||||
|
||||
"PUSH", "105", |
||||
"PUSH", "200", |
||||
"MOD", |
||||
|
||||
"PUSH", "100000000000000000000000", |
||||
"PUSH", "10000000000000", |
||||
"SMOD", |
||||
|
||||
"PUSH", "5", |
||||
"PUSH", "10", |
||||
"LT", |
||||
|
||||
"PUSH", "5", |
||||
"PUSH", "5", |
||||
"LE", |
||||
|
||||
"PUSH", "50", |
||||
"PUSH", "5", |
||||
"GT", |
||||
|
||||
"PUSH", "5", |
||||
"PUSH", "5", |
||||
"GE", |
||||
|
||||
"PUSH", "10", |
||||
"PUSH", "10", |
||||
"NOT", |
||||
|
||||
"MYADDRESS", |
||||
"TXSENDER", |
||||
|
||||
"STOP", |
||||
}) |
||||
tx := NewTransaction("1e8a42ea8cce13", 100, []string{}) |
||||
|
||||
block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx}) |
||||
db.Put(block.Hash(), block.RlpEncode()) |
||||
|
||||
bm := NewBlockManager() |
||||
bm.ProcessBlock(block) |
||||
} |
||||
*/ |
@ -0,0 +1,66 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"math/big" |
||||
) |
||||
|
||||
type Contract struct { |
||||
Amount *big.Int |
||||
Nonce uint64 |
||||
state *ethutil.Trie |
||||
} |
||||
|
||||
func NewContract(Amount *big.Int, root []byte) *Contract { |
||||
contract := &Contract{Amount: Amount, Nonce: 0} |
||||
contract.state = ethutil.NewTrie(ethutil.Config.Db, string(root)) |
||||
|
||||
return contract |
||||
} |
||||
|
||||
func (c *Contract) RlpEncode() []byte { |
||||
return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.Root}) |
||||
} |
||||
|
||||
func (c *Contract) RlpDecode(data []byte) { |
||||
decoder := ethutil.NewRlpValueFromBytes(data) |
||||
|
||||
c.Amount = decoder.Get(0).AsBigInt() |
||||
c.Nonce = decoder.Get(1).AsUint() |
||||
c.state = ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).AsRaw()) |
||||
} |
||||
|
||||
func (c *Contract) State() *ethutil.Trie { |
||||
return c.state |
||||
} |
||||
|
||||
type Address struct { |
||||
Amount *big.Int |
||||
Nonce uint64 |
||||
} |
||||
|
||||
func NewAddress(amount *big.Int) *Address { |
||||
return &Address{Amount: amount, Nonce: 0} |
||||
} |
||||
|
||||
func NewAddressFromData(data []byte) *Address { |
||||
address := &Address{} |
||||
address.RlpDecode(data) |
||||
|
||||
return address |
||||
} |
||||
|
||||
func (a *Address) AddFee(fee *big.Int) { |
||||
a.Amount.Add(a.Amount, fee) |
||||
} |
||||
|
||||
func (a *Address) RlpEncode() []byte { |
||||
return ethutil.Encode([]interface{}{a.Amount, a.Nonce}) |
||||
} |
||||
|
||||
func (a *Address) RlpDecode(data []byte) { |
||||
decoder := ethutil.NewRlpValueFromBytes(data) |
||||
|
||||
a.Amount = decoder.Get(0).AsBigInt() |
||||
a.Nonce = decoder.Get(1).AsUint() |
||||
} |
@ -0,0 +1,199 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"github.com/obscuren/sha3" |
||||
"hash" |
||||
"log" |
||||
"math/big" |
||||
"math/rand" |
||||
"time" |
||||
) |
||||
|
||||
type PoW interface { |
||||
Search(block *Block) []byte |
||||
Verify(hash []byte, diff *big.Int, nonce []byte) bool |
||||
} |
||||
|
||||
type EasyPow struct { |
||||
hash *big.Int |
||||
} |
||||
|
||||
func (pow *EasyPow) Search(block *Block) []byte { |
||||
r := rand.New(rand.NewSource(time.Now().UnixNano())) |
||||
|
||||
hash := block.HashNoNonce() |
||||
diff := block.Difficulty |
||||
for { |
||||
sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes()) |
||||
if pow.Verify(hash, diff, sha) { |
||||
return sha |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (pow *EasyPow) Verify(hash []byte, diff *big.Int, nonce []byte) bool { |
||||
sha := sha3.NewKeccak256() |
||||
|
||||
d := append(hash, nonce...) |
||||
sha.Write(d) |
||||
|
||||
v := ethutil.BigPow(2, 256) |
||||
ret := new(big.Int).Div(v, diff) |
||||
|
||||
res := new(big.Int) |
||||
res.SetBytes(sha.Sum(nil)) |
||||
|
||||
return res.Cmp(ret) == -1 |
||||
} |
||||
|
||||
func (pow *EasyPow) SetHash(hash *big.Int) { |
||||
} |
||||
|
||||
type Dagger struct { |
||||
hash *big.Int |
||||
xn *big.Int |
||||
} |
||||
|
||||
var Found bool |
||||
|
||||
func (dag *Dagger) Find(obj *big.Int, resChan chan int64) { |
||||
r := rand.New(rand.NewSource(time.Now().UnixNano())) |
||||
|
||||
for i := 0; i < 1000; i++ { |
||||
rnd := r.Int63() |
||||
|
||||
res := dag.Eval(big.NewInt(rnd)) |
||||
log.Printf("rnd %v\nres %v\nobj %v\n", rnd, res, obj) |
||||
if res.Cmp(obj) < 0 { |
||||
// Post back result on the channel
|
||||
resChan <- rnd |
||||
// Notify other threads we've found a valid nonce
|
||||
Found = true |
||||
} |
||||
|
||||
// Break out if found
|
||||
if Found { |
||||
break |
||||
} |
||||
} |
||||
|
||||
resChan <- 0 |
||||
} |
||||
|
||||
func (dag *Dagger) Search(hash, diff *big.Int) *big.Int { |
||||
// TODO fix multi threading. Somehow it results in the wrong nonce
|
||||
amountOfRoutines := 1 |
||||
|
||||
dag.hash = hash |
||||
|
||||
obj := ethutil.BigPow(2, 256) |
||||
obj = obj.Div(obj, diff) |
||||
|
||||
Found = false |
||||
resChan := make(chan int64, 3) |
||||
var res int64 |
||||
|
||||
for k := 0; k < amountOfRoutines; k++ { |
||||
go dag.Find(obj, resChan) |
||||
} |
||||
|
||||
// Wait for each go routine to finish
|
||||
for k := 0; k < amountOfRoutines; k++ { |
||||
// Get the result from the channel. 0 = quit
|
||||
if r := <-resChan; r != 0 { |
||||
res = r |
||||
} |
||||
} |
||||
|
||||
return big.NewInt(res) |
||||
} |
||||
|
||||
func (dag *Dagger) Verify(hash, diff, nonce *big.Int) bool { |
||||
dag.hash = hash |
||||
|
||||
obj := ethutil.BigPow(2, 256) |
||||
obj = obj.Div(obj, diff) |
||||
|
||||
return dag.Eval(nonce).Cmp(obj) < 0 |
||||
} |
||||
|
||||
func DaggerVerify(hash, diff, nonce *big.Int) bool { |
||||
dagger := &Dagger{} |
||||
dagger.hash = hash |
||||
|
||||
obj := ethutil.BigPow(2, 256) |
||||
obj = obj.Div(obj, diff) |
||||
|
||||
return dagger.Eval(nonce).Cmp(obj) < 0 |
||||
} |
||||
|
||||
func (dag *Dagger) Node(L uint64, i uint64) *big.Int { |
||||
if L == i { |
||||
return dag.hash |
||||
} |
||||
|
||||
var m *big.Int |
||||
if L == 9 { |
||||
m = big.NewInt(16) |
||||
} else { |
||||
m = big.NewInt(3) |
||||
} |
||||
|
||||
sha := sha3.NewKeccak256() |
||||
sha.Reset() |
||||
d := sha3.NewKeccak256() |
||||
b := new(big.Int) |
||||
ret := new(big.Int) |
||||
|
||||
for k := 0; k < int(m.Uint64()); k++ { |
||||
d.Reset() |
||||
d.Write(dag.hash.Bytes()) |
||||
d.Write(dag.xn.Bytes()) |
||||
d.Write(big.NewInt(int64(L)).Bytes()) |
||||
d.Write(big.NewInt(int64(i)).Bytes()) |
||||
d.Write(big.NewInt(int64(k)).Bytes()) |
||||
|
||||
b.SetBytes(Sum(d)) |
||||
pk := b.Uint64() & ((1 << ((L - 1) * 3)) - 1) |
||||
sha.Write(dag.Node(L-1, pk).Bytes()) |
||||
} |
||||
|
||||
ret.SetBytes(Sum(sha)) |
||||
|
||||
return ret |
||||
} |
||||
|
||||
func Sum(sha hash.Hash) []byte { |
||||
//in := make([]byte, 32)
|
||||
return sha.Sum(nil) |
||||
} |
||||
|
||||
func (dag *Dagger) Eval(N *big.Int) *big.Int { |
||||
pow := ethutil.BigPow(2, 26) |
||||
dag.xn = pow.Div(N, pow) |
||||
|
||||
sha := sha3.NewKeccak256() |
||||
sha.Reset() |
||||
ret := new(big.Int) |
||||
|
||||
for k := 0; k < 4; k++ { |
||||
d := sha3.NewKeccak256() |
||||
b := new(big.Int) |
||||
|
||||
d.Reset() |
||||
d.Write(dag.hash.Bytes()) |
||||
d.Write(dag.xn.Bytes()) |
||||
d.Write(N.Bytes()) |
||||
d.Write(big.NewInt(int64(k)).Bytes()) |
||||
|
||||
b.SetBytes(Sum(d)) |
||||
pk := (b.Uint64() & 0x1ffffff) |
||||
|
||||
sha.Write(dag.Node(9, pk).Bytes()) |
||||
} |
||||
|
||||
return ret.SetBytes(Sum(sha)) |
||||
} |
@ -0,0 +1,18 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"math/big" |
||||
"testing" |
||||
) |
||||
|
||||
func BenchmarkDaggerSearch(b *testing.B) { |
||||
hash := big.NewInt(0) |
||||
diff := ethutil.BigPow(2, 36) |
||||
o := big.NewInt(0) // nonce doesn't matter. We're only testing against speed, not validity
|
||||
|
||||
// Reset timer so the big generation isn't included in the benchmark
|
||||
b.ResetTimer() |
||||
// Validate
|
||||
DaggerVerify(hash, diff, o) |
||||
} |
@ -0,0 +1,42 @@ |
||||
package ethchain |
||||
|
||||
import "fmt" |
||||
|
||||
// Parent error. In case a parent is unknown this error will be thrown
|
||||
// by the block manager
|
||||
type ParentErr struct { |
||||
Message string |
||||
} |
||||
|
||||
func (err *ParentErr) Error() string { |
||||
return err.Message |
||||
} |
||||
|
||||
func ParentError(hash []byte) error { |
||||
return &ParentErr{Message: fmt.Sprintf("Block's parent unkown %x", hash)} |
||||
} |
||||
|
||||
func IsParentErr(err error) bool { |
||||
_, ok := err.(*ParentErr) |
||||
|
||||
return ok |
||||
} |
||||
|
||||
// Block validation error. If any validation fails, this error will be thrown
|
||||
type ValidationErr struct { |
||||
Message string |
||||
} |
||||
|
||||
func (err *ValidationErr) Error() string { |
||||
return err.Message |
||||
} |
||||
|
||||
func ValidationError(format string, v ...interface{}) *ValidationErr { |
||||
return &ValidationErr{Message: fmt.Sprintf(format, v...)} |
||||
} |
||||
|
||||
func IsValidationErr(err error) bool { |
||||
_, ok := err.(*ValidationErr) |
||||
|
||||
return ok |
||||
} |
@ -0,0 +1,65 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"math/big" |
||||
) |
||||
|
||||
var StepFee *big.Int = new(big.Int) |
||||
var TxFeeRat *big.Int = big.NewInt(100000000000000) |
||||
var TxFee *big.Int = big.NewInt(100) |
||||
var ContractFee *big.Int = new(big.Int) |
||||
var MemFee *big.Int = new(big.Int) |
||||
var DataFee *big.Int = new(big.Int) |
||||
var CryptoFee *big.Int = new(big.Int) |
||||
var ExtroFee *big.Int = new(big.Int) |
||||
|
||||
var BlockReward *big.Int = big.NewInt(1500000000000000000) |
||||
var Period1Reward *big.Int = new(big.Int) |
||||
var Period2Reward *big.Int = new(big.Int) |
||||
var Period3Reward *big.Int = new(big.Int) |
||||
var Period4Reward *big.Int = new(big.Int) |
||||
|
||||
func InitFees() { |
||||
/* |
||||
// Base for 2**64
|
||||
b60 := new(big.Int) |
||||
b60.Exp(big.NewInt(2), big.NewInt(64), big.NewInt(0)) |
||||
// Base for 2**80
|
||||
b80 := new(big.Int) |
||||
b80.Exp(big.NewInt(2), big.NewInt(80), big.NewInt(0)) |
||||
|
||||
StepFee.Exp(big.NewInt(10), big.NewInt(16), big.NewInt(0)) |
||||
//StepFee.Div(b60, big.NewInt(64))
|
||||
//fmt.Println("StepFee:", StepFee)
|
||||
|
||||
TxFee.Exp(big.NewInt(2), big.NewInt(64), big.NewInt(0)) |
||||
//fmt.Println("TxFee:", TxFee)
|
||||
|
||||
ContractFee.Exp(big.NewInt(2), big.NewInt(64), big.NewInt(0)) |
||||
//fmt.Println("ContractFee:", ContractFee)
|
||||
|
||||
MemFee.Div(b60, big.NewInt(4)) |
||||
//fmt.Println("MemFee:", MemFee)
|
||||
|
||||
DataFee.Div(b60, big.NewInt(16)) |
||||
//fmt.Println("DataFee:", DataFee)
|
||||
|
||||
CryptoFee.Div(b60, big.NewInt(16)) |
||||
//fmt.Println("CrytoFee:", CryptoFee)
|
||||
|
||||
ExtroFee.Div(b60, big.NewInt(16)) |
||||
//fmt.Println("ExtroFee:", ExtroFee)
|
||||
|
||||
Period1Reward.Mul(b80, big.NewInt(1024)) |
||||
//fmt.Println("Period1Reward:", Period1Reward)
|
||||
|
||||
Period2Reward.Mul(b80, big.NewInt(512)) |
||||
//fmt.Println("Period2Reward:", Period2Reward)
|
||||
|
||||
Period3Reward.Mul(b80, big.NewInt(256)) |
||||
//fmt.Println("Period3Reward:", Period3Reward)
|
||||
|
||||
Period4Reward.Mul(b80, big.NewInt(128)) |
||||
//fmt.Println("Period4Reward:", Period4Reward)
|
||||
*/ |
||||
} |
@ -0,0 +1,39 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"math/big" |
||||
) |
||||
|
||||
/* |
||||
* This is the special genesis block. |
||||
*/ |
||||
|
||||
var ZeroHash256 = make([]byte, 32) |
||||
var ZeroHash160 = make([]byte, 20) |
||||
var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{})) |
||||
|
||||
var GenisisHeader = []interface{}{ |
||||
// Previous hash (none)
|
||||
//"",
|
||||
ZeroHash256, |
||||
// Sha of uncles
|
||||
ethutil.Sha3Bin(ethutil.Encode([]interface{}{})), |
||||
// Coinbase
|
||||
ZeroHash160, |
||||
// Root state
|
||||
"", |
||||
// Sha of transactions
|
||||
//EmptyShaList,
|
||||
ethutil.Sha3Bin(ethutil.Encode([]interface{}{})), |
||||
// Difficulty
|
||||
ethutil.BigPow(2, 22), |
||||
// Time
|
||||
int64(0), |
||||
// Extra
|
||||
"", |
||||
// Nonce
|
||||
ethutil.Sha3Bin(big.NewInt(42).Bytes()), |
||||
} |
||||
|
||||
var Genesis = []interface{}{GenisisHeader, []interface{}{}, []interface{}{}} |
@ -0,0 +1,167 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"fmt" |
||||
"math/big" |
||||
) |
||||
|
||||
type OpCode int |
||||
|
||||
// Op codes
|
||||
const ( |
||||
oSTOP OpCode = iota |
||||
oADD |
||||
oMUL |
||||
oSUB |
||||
oDIV |
||||
oSDIV |
||||
oMOD |
||||
oSMOD |
||||
oEXP |
||||
oNEG |
||||
oLT |
||||
oLE |
||||
oGT |
||||
oGE |
||||
oEQ |
||||
oNOT |
||||
oMYADDRESS |
||||
oTXSENDER |
||||
oTXVALUE |
||||
oTXFEE |
||||
oTXDATAN |
||||
oTXDATA |
||||
oBLK_PREVHASH |
||||
oBLK_COINBASE |
||||
oBLK_TIMESTAMP |
||||
oBLK_NUMBER |
||||
oBLK_DIFFICULTY |
||||
oBASEFEE |
||||
oSHA256 OpCode = 32 |
||||
oRIPEMD160 OpCode = 33 |
||||
oECMUL OpCode = 34 |
||||
oECADD OpCode = 35 |
||||
oECSIGN OpCode = 36 |
||||
oECRECOVER OpCode = 37 |
||||
oECVALID OpCode = 38 |
||||
oSHA3 OpCode = 39 |
||||
oPUSH OpCode = 48 |
||||
oPOP OpCode = 49 |
||||
oDUP OpCode = 50 |
||||
oSWAP OpCode = 51 |
||||
oMLOAD OpCode = 52 |
||||
oMSTORE OpCode = 53 |
||||
oSLOAD OpCode = 54 |
||||
oSSTORE OpCode = 55 |
||||
oJMP OpCode = 56 |
||||
oJMPI OpCode = 57 |
||||
oIND OpCode = 58 |
||||
oEXTRO OpCode = 59 |
||||
oBALANCE OpCode = 60 |
||||
oMKTX OpCode = 61 |
||||
oSUICIDE OpCode = 62 |
||||
) |
||||
|
||||
// Since the opcodes aren't all in order we can't use a regular slice
|
||||
var opCodeToString = map[OpCode]string{ |
||||
oSTOP: "STOP", |
||||
oADD: "ADD", |
||||
oMUL: "MUL", |
||||
oSUB: "SUB", |
||||
oDIV: "DIV", |
||||
oSDIV: "SDIV", |
||||
oMOD: "MOD", |
||||
oSMOD: "SMOD", |
||||
oEXP: "EXP", |
||||
oNEG: "NEG", |
||||
oLT: "LT", |
||||
oLE: "LE", |
||||
oGT: "GT", |
||||
oGE: "GE", |
||||
oEQ: "EQ", |
||||
oNOT: "NOT", |
||||
oMYADDRESS: "MYADDRESS", |
||||
oTXSENDER: "TXSENDER", |
||||
oTXVALUE: "TXVALUE", |
||||
oTXFEE: "TXFEE", |
||||
oTXDATAN: "TXDATAN", |
||||
oTXDATA: "TXDATA", |
||||
oBLK_PREVHASH: "BLK_PREVHASH", |
||||
oBLK_COINBASE: "BLK_COINBASE", |
||||
oBLK_TIMESTAMP: "BLK_TIMESTAMP", |
||||
oBLK_NUMBER: "BLK_NUMBER", |
||||
oBLK_DIFFICULTY: "BLK_DIFFICULTY", |
||||
oBASEFEE: "BASEFEE", |
||||
oSHA256: "SHA256", |
||||
oRIPEMD160: "RIPEMD160", |
||||
oECMUL: "ECMUL", |
||||
oECADD: "ECADD", |
||||
oECSIGN: "ECSIGN", |
||||
oECRECOVER: "ECRECOVER", |
||||
oECVALID: "ECVALID", |
||||
oSHA3: "SHA3", |
||||
oPUSH: "PUSH", |
||||
oPOP: "POP", |
||||
oDUP: "DUP", |
||||
oSWAP: "SWAP", |
||||
oMLOAD: "MLOAD", |
||||
oMSTORE: "MSTORE", |
||||
oSLOAD: "SLOAD", |
||||
oSSTORE: "SSTORE", |
||||
oJMP: "JMP", |
||||
oJMPI: "JMPI", |
||||
oIND: "IND", |
||||
oEXTRO: "EXTRO", |
||||
oBALANCE: "BALANCE", |
||||
oMKTX: "MKTX", |
||||
oSUICIDE: "SUICIDE", |
||||
} |
||||
|
||||
func (o OpCode) String() string { |
||||
return opCodeToString[o] |
||||
} |
||||
|
||||
type OpType int |
||||
|
||||
const ( |
||||
tNorm = iota |
||||
tData |
||||
tExtro |
||||
tCrypto |
||||
) |
||||
|
||||
type TxCallback func(opType OpType) bool |
||||
|
||||
// Simple push/pop stack mechanism
|
||||
type Stack struct { |
||||
data []*big.Int |
||||
} |
||||
|
||||
func NewStack() *Stack { |
||||
return &Stack{} |
||||
} |
||||
|
||||
func (st *Stack) Pop() *big.Int { |
||||
s := len(st.data) |
||||
|
||||
str := st.data[s-1] |
||||
st.data = st.data[:s-1] |
||||
|
||||
return str |
||||
} |
||||
|
||||
func (st *Stack) Popn() (*big.Int, *big.Int) { |
||||
s := len(st.data) |
||||
|
||||
ints := st.data[s-2:] |
||||
st.data = st.data[:s-2] |
||||
|
||||
return ints[0], ints[1] |
||||
} |
||||
|
||||
func (st *Stack) Push(d *big.Int) { |
||||
st.data = append(st.data, d) |
||||
} |
||||
func (st *Stack) Print() { |
||||
fmt.Println(st.data) |
||||
} |
@ -0,0 +1,157 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"github.com/obscuren/secp256k1-go" |
||||
"math/big" |
||||
) |
||||
|
||||
type Transaction struct { |
||||
Nonce uint64 |
||||
Recipient []byte |
||||
Value *big.Int |
||||
Data []string |
||||
Memory []int |
||||
v byte |
||||
r, s []byte |
||||
} |
||||
|
||||
func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { |
||||
tx := Transaction{Recipient: to, Value: value} |
||||
tx.Nonce = 0 |
||||
|
||||
// Serialize the data
|
||||
tx.Data = make([]string, len(data)) |
||||
for i, val := range data { |
||||
instr, err := ethutil.CompileInstr(val) |
||||
if err != nil { |
||||
//fmt.Printf("compile error:%d %v\n", i+1, err)
|
||||
} |
||||
|
||||
tx.Data[i] = instr |
||||
} |
||||
|
||||
return &tx |
||||
} |
||||
|
||||
func NewTransactionFromData(data []byte) *Transaction { |
||||
tx := &Transaction{} |
||||
tx.RlpDecode(data) |
||||
|
||||
return tx |
||||
} |
||||
|
||||
func NewTransactionFromValue(val *ethutil.Value) *Transaction { |
||||
tx := &Transaction{} |
||||
tx.RlpValueDecode(val) |
||||
|
||||
return tx |
||||
} |
||||
|
||||
func (tx *Transaction) Hash() []byte { |
||||
data := make([]interface{}, len(tx.Data)) |
||||
for i, val := range tx.Data { |
||||
data[i] = val |
||||
} |
||||
|
||||
preEnc := []interface{}{ |
||||
tx.Nonce, |
||||
tx.Recipient, |
||||
tx.Value, |
||||
data, |
||||
} |
||||
|
||||
return ethutil.Sha3Bin(ethutil.Encode(preEnc)) |
||||
} |
||||
|
||||
func (tx *Transaction) IsContract() bool { |
||||
return len(tx.Recipient) == 0 |
||||
} |
||||
|
||||
func (tx *Transaction) Signature(key []byte) []byte { |
||||
hash := tx.Hash() |
||||
|
||||
sig, _ := secp256k1.Sign(hash, key) |
||||
|
||||
return sig |
||||
} |
||||
|
||||
func (tx *Transaction) PublicKey() []byte { |
||||
hash := tx.Hash() |
||||
|
||||
// If we don't make a copy we will overwrite the existing underlying array
|
||||
dst := make([]byte, len(tx.r)) |
||||
copy(dst, tx.r) |
||||
|
||||
sig := append(dst, tx.s...) |
||||
sig = append(sig, tx.v-27) |
||||
|
||||
pubkey, _ := secp256k1.RecoverPubkey(hash, sig) |
||||
|
||||
return pubkey |
||||
} |
||||
|
||||
func (tx *Transaction) Sender() []byte { |
||||
pubkey := tx.PublicKey() |
||||
|
||||
// Validate the returned key.
|
||||
// Return nil if public key isn't in full format
|
||||
if pubkey[0] != 4 { |
||||
return nil |
||||
} |
||||
|
||||
return ethutil.Sha3Bin(pubkey[1:])[12:] |
||||
} |
||||
|
||||
func (tx *Transaction) Sign(privk []byte) error { |
||||
|
||||
sig := tx.Signature(privk) |
||||
|
||||
tx.r = sig[:32] |
||||
tx.s = sig[32:64] |
||||
tx.v = sig[64] + 27 |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (tx *Transaction) RlpData() interface{} { |
||||
// Prepare the transaction for serialization
|
||||
return []interface{}{ |
||||
tx.Nonce, |
||||
tx.Recipient, |
||||
tx.Value, |
||||
ethutil.NewSliceValue(tx.Data).Slice(), |
||||
tx.v, |
||||
tx.r, |
||||
tx.s, |
||||
} |
||||
} |
||||
|
||||
func (tx *Transaction) RlpValue() *ethutil.Value { |
||||
return ethutil.NewValue(tx.RlpData()) |
||||
} |
||||
|
||||
func (tx *Transaction) RlpEncode() []byte { |
||||
return tx.RlpValue().Encode() |
||||
} |
||||
|
||||
func (tx *Transaction) RlpDecode(data []byte) { |
||||
tx.RlpValueDecode(ethutil.NewValueFromBytes(data)) |
||||
} |
||||
|
||||
func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { |
||||
tx.Nonce = decoder.Get(0).Uint() |
||||
tx.Recipient = decoder.Get(1).Bytes() |
||||
tx.Value = decoder.Get(2).BigInt() |
||||
|
||||
d := decoder.Get(3) |
||||
tx.Data = make([]string, d.Len()) |
||||
for i := 0; i < d.Len(); i++ { |
||||
tx.Data[i] = d.Get(i).Str() |
||||
} |
||||
|
||||
// TODO something going wrong here
|
||||
tx.v = byte(decoder.Get(4).Uint()) |
||||
tx.r = decoder.Get(5).Bytes() |
||||
tx.s = decoder.Get(6).Bytes() |
||||
} |
@ -0,0 +1,219 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"bytes" |
||||
"container/list" |
||||
"errors" |
||||
"fmt" |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"github.com/ethereum/eth-go/ethwire" |
||||
"log" |
||||
"math/big" |
||||
"sync" |
||||
) |
||||
|
||||
const ( |
||||
txPoolQueueSize = 50 |
||||
) |
||||
|
||||
type TxPoolHook chan *Transaction |
||||
|
||||
func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction { |
||||
for e := pool.Front(); e != nil; e = e.Next() { |
||||
if tx, ok := e.Value.(*Transaction); ok { |
||||
if finder(tx, e) { |
||||
return tx |
||||
} |
||||
} |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
type PublicSpeaker interface { |
||||
Broadcast(msgType ethwire.MsgType, data []interface{}) |
||||
} |
||||
|
||||
// The tx pool a thread safe transaction pool handler. In order to
|
||||
// guarantee a non blocking pool we use a queue channel which can be
|
||||
// independently read without needing access to the actual pool. If the
|
||||
// pool is being drained or synced for whatever reason the transactions
|
||||
// will simple queue up and handled when the mutex is freed.
|
||||
type TxPool struct { |
||||
//server *Server
|
||||
Speaker PublicSpeaker |
||||
// The mutex for accessing the Tx pool.
|
||||
mutex sync.Mutex |
||||
// Queueing channel for reading and writing incoming
|
||||
// transactions to
|
||||
queueChan chan *Transaction |
||||
// Quiting channel
|
||||
quit chan bool |
||||
// The actual pool
|
||||
pool *list.List |
||||
|
||||
BlockManager *BlockManager |
||||
|
||||
Hook TxPoolHook |
||||
} |
||||
|
||||
func NewTxPool() *TxPool { |
||||
return &TxPool{ |
||||
//server: s,
|
||||
mutex: sync.Mutex{}, |
||||
pool: list.New(), |
||||
queueChan: make(chan *Transaction, txPoolQueueSize), |
||||
quit: make(chan bool), |
||||
} |
||||
} |
||||
|
||||
// Blocking function. Don't use directly. Use QueueTransaction instead
|
||||
func (pool *TxPool) addTransaction(tx *Transaction) { |
||||
pool.mutex.Lock() |
||||
pool.pool.PushBack(tx) |
||||
pool.mutex.Unlock() |
||||
|
||||
// Broadcast the transaction to the rest of the peers
|
||||
pool.Speaker.Broadcast(ethwire.MsgTxTy, []interface{}{tx.RlpData()}) |
||||
} |
||||
|
||||
// Process transaction validates the Tx and processes funds from the
|
||||
// sender to the recipient.
|
||||
func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) { |
||||
log.Printf("[TXPL] Processing Tx %x\n", tx.Hash()) |
||||
|
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
log.Println(r) |
||||
err = fmt.Errorf("%v", r) |
||||
} |
||||
}() |
||||
// Get the sender
|
||||
sender := block.GetAddr(tx.Sender()) |
||||
|
||||
// Make sure there's enough in the sender's account. Having insufficient
|
||||
// funds won't invalidate this transaction but simple ignores it.
|
||||
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) |
||||
if sender.Amount.Cmp(totAmount) < 0 { |
||||
return errors.New("Insufficient amount in sender's account") |
||||
} |
||||
|
||||
if sender.Nonce != tx.Nonce { |
||||
if ethutil.Config.Debug { |
||||
return fmt.Errorf("Invalid nonce %d(%d) continueing anyway", tx.Nonce, sender.Nonce) |
||||
} else { |
||||
return fmt.Errorf("Invalid nonce %d(%d)", tx.Nonce, sender.Nonce) |
||||
} |
||||
} |
||||
|
||||
// Subtract the amount from the senders account
|
||||
sender.Amount.Sub(sender.Amount, totAmount) |
||||
sender.Nonce += 1 |
||||
|
||||
// Get the receiver
|
||||
receiver := block.GetAddr(tx.Recipient) |
||||
// Add the amount to receivers account which should conclude this transaction
|
||||
receiver.Amount.Add(receiver.Amount, tx.Value) |
||||
|
||||
block.UpdateAddr(tx.Sender(), sender) |
||||
block.UpdateAddr(tx.Recipient, receiver) |
||||
|
||||
return |
||||
} |
||||
|
||||
func (pool *TxPool) ValidateTransaction(tx *Transaction) error { |
||||
// Get the last block so we can retrieve the sender and receiver from
|
||||
// the merkle trie
|
||||
block := pool.BlockManager.BlockChain().CurrentBlock |
||||
// Something has gone horribly wrong if this happens
|
||||
if block == nil { |
||||
return errors.New("No last block on the block chain") |
||||
} |
||||
|
||||
// Get the sender
|
||||
sender := block.GetAddr(tx.Sender()) |
||||
|
||||
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) |
||||
// Make sure there's enough in the sender's account. Having insufficient
|
||||
// funds won't invalidate this transaction but simple ignores it.
|
||||
if sender.Amount.Cmp(totAmount) < 0 { |
||||
return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender()) |
||||
} |
||||
|
||||
// Increment the nonce making each tx valid only once to prevent replay
|
||||
// attacks
|
||||
|
||||
return nil |
||||
} |
||||
|
||||
func (pool *TxPool) queueHandler() { |
||||
out: |
||||
for { |
||||
select { |
||||
case tx := <-pool.queueChan: |
||||
hash := tx.Hash() |
||||
foundTx := FindTx(pool.pool, func(tx *Transaction, e *list.Element) bool { |
||||
return bytes.Compare(tx.Hash(), hash) == 0 |
||||
}) |
||||
|
||||
if foundTx != nil { |
||||
break |
||||
} |
||||
|
||||
// Validate the transaction
|
||||
err := pool.ValidateTransaction(tx) |
||||
if err != nil { |
||||
if ethutil.Config.Debug { |
||||
log.Println("Validating Tx failed", err) |
||||
} |
||||
} else { |
||||
// Call blocking version. At this point it
|
||||
// doesn't matter since this is a goroutine
|
||||
pool.addTransaction(tx) |
||||
|
||||
if pool.Hook != nil { |
||||
pool.Hook <- tx |
||||
} |
||||
} |
||||
case <-pool.quit: |
||||
break out |
||||
} |
||||
} |
||||
} |
||||
|
||||
func (pool *TxPool) QueueTransaction(tx *Transaction) { |
||||
pool.queueChan <- tx |
||||
} |
||||
|
||||
func (pool *TxPool) Flush() []*Transaction { |
||||
pool.mutex.Lock() |
||||
defer pool.mutex.Unlock() |
||||
|
||||
txList := make([]*Transaction, pool.pool.Len()) |
||||
i := 0 |
||||
for e := pool.pool.Front(); e != nil; e = e.Next() { |
||||
if tx, ok := e.Value.(*Transaction); ok { |
||||
txList[i] = tx |
||||
} |
||||
|
||||
i++ |
||||
} |
||||
|
||||
// Recreate a new list all together
|
||||
// XXX Is this the fastest way?
|
||||
pool.pool = list.New() |
||||
|
||||
return txList |
||||
} |
||||
|
||||
func (pool *TxPool) Start() { |
||||
go pool.queueHandler() |
||||
} |
||||
|
||||
func (pool *TxPool) Stop() { |
||||
log.Println("[TXP] Stopping...") |
||||
|
||||
close(pool.quit) |
||||
|
||||
pool.Flush() |
||||
} |
@ -0,0 +1,54 @@ |
||||
package ethchain |
||||
|
||||
import ( |
||||
"encoding/hex" |
||||
"fmt" |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"math/big" |
||||
"testing" |
||||
) |
||||
|
||||
func TestAddressRetrieval(t *testing.T) { |
||||
// TODO
|
||||
// 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
|
||||
key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4") |
||||
|
||||
tx := &Transaction{ |
||||
Nonce: 0, |
||||
Recipient: ZeroHash160, |
||||
Value: big.NewInt(0), |
||||
Data: nil, |
||||
} |
||||
//fmt.Printf("rlp %x\n", tx.RlpEncode())
|
||||
//fmt.Printf("sha rlp %x\n", tx.Hash())
|
||||
|
||||
tx.Sign(key) |
||||
|
||||
//fmt.Printf("hex tx key %x\n", tx.PublicKey())
|
||||
//fmt.Printf("seder %x\n", tx.Sender())
|
||||
} |
||||
|
||||
func TestAddressRetrieval2(t *testing.T) { |
||||
// TODO
|
||||
// 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f
|
||||
key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4") |
||||
addr, _ := hex.DecodeString("944400f4b88ac9589a0f17ed4671da26bddb668b") |
||||
tx := &Transaction{ |
||||
Nonce: 0, |
||||
Recipient: addr, |
||||
Value: big.NewInt(1000), |
||||
Data: nil, |
||||
} |
||||
tx.Sign(key) |
||||
//data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
|
||||
//tx := NewTransactionFromData(data)
|
||||
fmt.Println(tx.RlpValue()) |
||||
|
||||
fmt.Printf("rlp %x\n", tx.RlpEncode()) |
||||
fmt.Printf("sha rlp %x\n", tx.Hash()) |
||||
|
||||
//tx.Sign(key)
|
||||
|
||||
fmt.Printf("hex tx key %x\n", tx.PublicKey()) |
||||
fmt.Printf("seder %x\n", tx.Sender()) |
||||
} |
@ -0,0 +1,12 @@ |
||||
# See http://help.github.com/ignore-files/ for more about ignoring files. |
||||
# |
||||
# If you find yourself ignoring temporary files generated by your text editor |
||||
# or operating system, you probably want to add a global ignore instead: |
||||
# git config --global core.excludesfile ~/.gitignore_global |
||||
|
||||
/tmp |
||||
*/**/*un~ |
||||
*un~ |
||||
.DS_Store |
||||
*/**/.DS_Store |
||||
|
@ -0,0 +1,11 @@ |
||||
# ethdb |
||||
|
||||
The ethdb package contains the ethereum database interfaces |
||||
|
||||
# Installation |
||||
|
||||
`go get github.com/ethereum/ethdb-go` |
||||
|
||||
# Usage |
||||
|
||||
Todo :-) |
@ -0,0 +1,64 @@ |
||||
package ethdb |
||||
|
||||
import ( |
||||
"fmt" |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"github.com/syndtr/goleveldb/leveldb" |
||||
"path" |
||||
) |
||||
|
||||
type LDBDatabase struct { |
||||
db *leveldb.DB |
||||
} |
||||
|
||||
func NewLDBDatabase() (*LDBDatabase, error) { |
||||
dbPath := path.Join(ethutil.Config.ExecPath, "database") |
||||
|
||||
// Open the db
|
||||
db, err := leveldb.OpenFile(dbPath, nil) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
|
||||
database := &LDBDatabase{db: db} |
||||
|
||||
return database, nil |
||||
} |
||||
|
||||
func (db *LDBDatabase) Put(key []byte, value []byte) { |
||||
err := db.db.Put(key, value, nil) |
||||
if err != nil { |
||||
fmt.Println("Error put", err) |
||||
} |
||||
} |
||||
|
||||
func (db *LDBDatabase) Get(key []byte) ([]byte, error) { |
||||
return db.db.Get(key, nil) |
||||
} |
||||
|
||||
func (db *LDBDatabase) LastKnownTD() []byte { |
||||
data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil) |
||||
|
||||
if len(data) == 0 { |
||||
data = []byte{0x0} |
||||
} |
||||
|
||||
return data |
||||
} |
||||
|
||||
func (db *LDBDatabase) Close() { |
||||
// Close the leveldb database
|
||||
db.db.Close() |
||||
} |
||||
|
||||
func (db *LDBDatabase) Print() { |
||||
iter := db.db.NewIterator(nil) |
||||
for iter.Next() { |
||||
key := iter.Key() |
||||
value := iter.Value() |
||||
|
||||
fmt.Printf("%x(%d): ", key, len(key)) |
||||
node := ethutil.NewValueFromBytes(value) |
||||
fmt.Printf("%v\n", node) |
||||
} |
||||
} |
@ -0,0 +1,6 @@ |
||||
package ethdb |
||||
|
||||
import ( |
||||
_ "fmt" |
||||
_ "testing" |
||||
) |
@ -0,0 +1,49 @@ |
||||
package ethdb |
||||
|
||||
import ( |
||||
"fmt" |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
) |
||||
|
||||
/* |
||||
* This is a test memory database. Do not use for any production it does not get persisted |
||||
*/ |
||||
type MemDatabase struct { |
||||
db map[string][]byte |
||||
} |
||||
|
||||
func NewMemDatabase() (*MemDatabase, error) { |
||||
db := &MemDatabase{db: make(map[string][]byte)} |
||||
|
||||
return db, nil |
||||
} |
||||
|
||||
func (db *MemDatabase) Put(key []byte, value []byte) { |
||||
db.db[string(key)] = value |
||||
} |
||||
|
||||
func (db *MemDatabase) Get(key []byte) ([]byte, error) { |
||||
return db.db[string(key)], nil |
||||
} |
||||
|
||||
func (db *MemDatabase) Print() { |
||||
for key, val := range db.db { |
||||
fmt.Printf("%x(%d): ", key, len(key)) |
||||
dec, _ := ethutil.Decode(val, 0) |
||||
node := ethutil.Conv(dec) |
||||
fmt.Printf("%q\n", node.AsRaw()) |
||||
} |
||||
} |
||||
|
||||
func (db *MemDatabase) Close() { |
||||
} |
||||
|
||||
func (db *MemDatabase) LastKnownTD() []byte { |
||||
data, _ := db.Get([]byte("LastKnownTotalDifficulty")) |
||||
|
||||
if len(data) == 0 || data == nil { |
||||
data = []byte{0x0} |
||||
} |
||||
|
||||
return data |
||||
} |
@ -0,0 +1,12 @@ |
||||
# See http://help.github.com/ignore-files/ for more about ignoring files. |
||||
# |
||||
# If you find yourself ignoring temporary files generated by your text editor |
||||
# or operating system, you probably want to add a global ignore instead: |
||||
# git config --global core.excludesfile ~/.gitignore_global |
||||
|
||||
/tmp |
||||
*/**/*un~ |
||||
*un~ |
||||
.DS_Store |
||||
*/**/.DS_Store |
||||
|
@ -0,0 +1,3 @@ |
||||
language: go |
||||
go: |
||||
- 1.2 |
@ -0,0 +1,137 @@ |
||||
# ethutil |
||||
|
||||
[![Build |
||||
Status](https://travis-ci.org/ethereum/go-ethereum.png?branch=master)](https://travis-ci.org/ethereum/go-ethereum) |
||||
|
||||
The ethutil package contains the ethereum utility library. |
||||
|
||||
# Installation |
||||
|
||||
`go get github.com/ethereum/ethutil-go` |
||||
|
||||
# Usage |
||||
|
||||
## RLP (Recursive Linear Prefix) Encoding |
||||
|
||||
RLP Encoding is an encoding scheme utilized by the Ethereum project. It |
||||
encodes any native value or list to string. |
||||
|
||||
More in depth information about the Encoding scheme see the [Wiki](http://wiki.ethereum.org/index.php/RLP) |
||||
article. |
||||
|
||||
```go |
||||
rlp := ethutil.Encode("doge") |
||||
fmt.Printf("%q\n", rlp) // => "\0x83dog" |
||||
|
||||
rlp = ethutil.Encode([]interface{}{"dog", "cat"}) |
||||
fmt.Printf("%q\n", rlp) // => "\0xc8\0x83dog\0x83cat" |
||||
decoded := ethutil.Decode(rlp) |
||||
fmt.Println(decoded) // => ["dog" "cat"] |
||||
``` |
||||
|
||||
## Patricia Trie |
||||
|
||||
Patricie Tree is a merkle trie utilized by the Ethereum project. |
||||
|
||||
More in depth information about the (modified) Patricia Trie can be |
||||
found on the [Wiki](http://wiki.ethereum.org/index.php/Patricia_Tree). |
||||
|
||||
The patricia trie uses a db as backend and could be anything as long as |
||||
it satisfies the Database interface found in `ethutil/db.go`. |
||||
|
||||
```go |
||||
db := NewDatabase() |
||||
|
||||
// db, root |
||||
trie := ethutil.NewTrie(db, "") |
||||
|
||||
trie.Put("puppy", "dog") |
||||
trie.Put("horse", "stallion") |
||||
trie.Put("do", "verb") |
||||
trie.Put("doge", "coin") |
||||
|
||||
// Look up the key "do" in the trie |
||||
out := trie.Get("do") |
||||
fmt.Println(out) // => verb |
||||
``` |
||||
|
||||
The patricia trie, in combination with RLP, provides a robust, |
||||
cryptographically authenticated data structure that can be used to store |
||||
all (key, value) bindings. |
||||
|
||||
```go |
||||
// ... Create db/trie |
||||
|
||||
// Note that RLP uses interface slices as list |
||||
value := ethutil.Encode([]interface{}{"one", 2, "three", []interface{}{42}}) |
||||
// Store the RLP encoded value of the list |
||||
trie.Put("mykey", value) |
||||
``` |
||||
|
||||
## Value |
||||
|
||||
Value is a Generic Value which is used in combination with RLP data or |
||||
`([])interface{}` structures. It may serve as a bridge between RLP data |
||||
and actual real values and takes care of all the type checking and |
||||
casting. Unlike Go's `reflect.Value` it does not panic if it's unable to |
||||
cast to the requested value. It simple returns the base value of that |
||||
type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc). |
||||
|
||||
### Creating a new Value |
||||
|
||||
`NewEmptyValue()` returns a new \*Value with it's initial value set to a |
||||
`[]interface{}` |
||||
|
||||
`AppendLint()` appends a list to the current value. |
||||
|
||||
`Append(v)` appends the value (v) to the current value/list. |
||||
|
||||
```go |
||||
val := ethutil.NewEmptyValue().Append(1).Append("2") |
||||
val.AppendList().Append(3) |
||||
``` |
||||
|
||||
### Retrieving values |
||||
|
||||
`Get(i)` returns the `i` item in the list. |
||||
|
||||
`Uint()` returns the value as an unsigned int64. |
||||
|
||||
`Slice()` returns the value as a interface slice. |
||||
|
||||
`Str()` returns the value as a string. |
||||
|
||||
`Bytes()` returns the value as a byte slice. |
||||
|
||||
`Len()` assumes current to be a slice and returns its length. |
||||
|
||||
`Byte()` returns the value as a single byte. |
||||
|
||||
```go |
||||
val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}}) |
||||
val.Get(0).Uint() // => 1 |
||||
val.Get(1).Str() // => "2" |
||||
s := val.Get(2) // => Value([]interface{}{3}) |
||||
s.Get(0).Uint() // => 3 |
||||
``` |
||||
|
||||
## Decoding |
||||
|
||||
Decoding streams of RLP data is simplified |
||||
|
||||
```go |
||||
val := ethutil.NewValueFromBytes(rlpData) |
||||
val.Get(0).Uint() |
||||
``` |
||||
|
||||
## Encoding |
||||
|
||||
Encoding from Value to RLP is done with the `Encode` method. The |
||||
underlying value can be anything RLP can encode (int, str, lists, bytes) |
||||
|
||||
```go |
||||
val := ethutil.NewValue([]interface{}{1,"2",[]interface{}{3}}) |
||||
rlp := val.Encode() |
||||
// Store the rlp data |
||||
Store(rlp) |
||||
``` |
@ -0,0 +1,37 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"math/big" |
||||
) |
||||
|
||||
var BigInt0 *big.Int = big.NewInt(0) |
||||
|
||||
// True
|
||||
var BigTrue *big.Int = big.NewInt(1) |
||||
|
||||
// False
|
||||
var BigFalse *big.Int = big.NewInt(0) |
||||
|
||||
// Returns the power of two integers
|
||||
func BigPow(a, b int) *big.Int { |
||||
c := new(big.Int) |
||||
c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0)) |
||||
|
||||
return c |
||||
} |
||||
|
||||
// Like big.NewInt(uint64); this takes a string instead.
|
||||
func Big(num string) *big.Int { |
||||
n := new(big.Int) |
||||
n.SetString(num, 0) |
||||
|
||||
return n |
||||
} |
||||
|
||||
// Like big.NewInt(uint64); this takes a byte buffer instead.
|
||||
func BigD(data []byte) *big.Int { |
||||
n := new(big.Int) |
||||
n.SetBytes(data) |
||||
|
||||
return n |
||||
} |
@ -0,0 +1,64 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/binary" |
||||
"fmt" |
||||
) |
||||
|
||||
func NumberToBytes(num interface{}, bits int) []byte { |
||||
buf := new(bytes.Buffer) |
||||
err := binary.Write(buf, binary.BigEndian, num) |
||||
if err != nil { |
||||
fmt.Println("NumberToBytes failed:", err) |
||||
} |
||||
|
||||
return buf.Bytes()[buf.Len()-(bits/8):] |
||||
} |
||||
|
||||
func BytesToNumber(b []byte) uint64 { |
||||
var number uint64 |
||||
|
||||
// Make sure the buffer is 64bits
|
||||
data := make([]byte, 8) |
||||
data = append(data[:len(b)], b...) |
||||
|
||||
buf := bytes.NewReader(data) |
||||
err := binary.Read(buf, binary.BigEndian, &number) |
||||
if err != nil { |
||||
fmt.Println("BytesToNumber failed:", err) |
||||
} |
||||
|
||||
return number |
||||
} |
||||
|
||||
// Read variable integer in big endian
|
||||
func ReadVarint(reader *bytes.Reader) (ret uint64) { |
||||
if reader.Len() == 8 { |
||||
var num uint64 |
||||
binary.Read(reader, binary.BigEndian, &num) |
||||
ret = uint64(num) |
||||
} else if reader.Len() == 4 { |
||||
var num uint32 |
||||
binary.Read(reader, binary.BigEndian, &num) |
||||
ret = uint64(num) |
||||
} else if reader.Len() == 2 { |
||||
var num uint16 |
||||
binary.Read(reader, binary.BigEndian, &num) |
||||
ret = uint64(num) |
||||
} else { |
||||
var num uint8 |
||||
binary.Read(reader, binary.BigEndian, &num) |
||||
ret = uint64(num) |
||||
} |
||||
|
||||
return ret |
||||
} |
||||
|
||||
func BinaryLength(num int) int { |
||||
if num == 0 { |
||||
return 0 |
||||
} |
||||
|
||||
return 1 + BinaryLength(num>>8) |
||||
} |
@ -0,0 +1,106 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"log" |
||||
"os" |
||||
"os/user" |
||||
"path" |
||||
) |
||||
|
||||
type LogType byte |
||||
|
||||
const ( |
||||
LogTypeStdIn = 1 |
||||
LogTypeFile = 2 |
||||
) |
||||
|
||||
// Config struct isn't exposed
|
||||
type config struct { |
||||
Db Database |
||||
|
||||
Log Logger |
||||
ExecPath string |
||||
Debug bool |
||||
Ver string |
||||
Pubkey []byte |
||||
Seed bool |
||||
} |
||||
|
||||
var Config *config |
||||
|
||||
// Read config doesn't read anything yet.
|
||||
func ReadConfig(base string) *config { |
||||
if Config == nil { |
||||
usr, _ := user.Current() |
||||
path := path.Join(usr.HomeDir, base) |
||||
|
||||
//Check if the logging directory already exists, create it if not
|
||||
_, err := os.Stat(path) |
||||
if err != nil { |
||||
if os.IsNotExist(err) { |
||||
log.Printf("Debug logging directory %s doesn't exist, creating it", path) |
||||
os.Mkdir(path, 0777) |
||||
} |
||||
} |
||||
|
||||
Config = &config{ExecPath: path, Debug: true, Ver: "0.2.1"} |
||||
Config.Log = NewLogger(LogFile|LogStd, 0) |
||||
} |
||||
|
||||
return Config |
||||
} |
||||
|
||||
type LoggerType byte |
||||
|
||||
const ( |
||||
LogFile = 0x1 |
||||
LogStd = 0x2 |
||||
) |
||||
|
||||
type Logger struct { |
||||
logSys []*log.Logger |
||||
logLevel int |
||||
} |
||||
|
||||
func NewLogger(flag LoggerType, level int) Logger { |
||||
var loggers []*log.Logger |
||||
|
||||
flags := log.LstdFlags | log.Lshortfile |
||||
|
||||
if flag&LogFile > 0 { |
||||
file, err := os.OpenFile(path.Join(Config.ExecPath, "debug.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm) |
||||
if err != nil { |
||||
log.Panic("unable to create file logger", err) |
||||
} |
||||
|
||||
log := log.New(file, "[ETH]", flags) |
||||
|
||||
loggers = append(loggers, log) |
||||
} |
||||
if flag&LogStd > 0 { |
||||
log := log.New(os.Stdout, "[ETH]", flags) |
||||
loggers = append(loggers, log) |
||||
} |
||||
|
||||
return Logger{logSys: loggers, logLevel: level} |
||||
} |
||||
|
||||
func (log Logger) Debugln(v ...interface{}) { |
||||
if log.logLevel != 0 { |
||||
return |
||||
} |
||||
|
||||
for _, logger := range log.logSys { |
||||
logger.Println(v...) |
||||
} |
||||
} |
||||
|
||||
func (log Logger) Debugf(format string, v ...interface{}) { |
||||
if log.logLevel != 0 { |
||||
return |
||||
} |
||||
|
||||
for _, logger := range log.logSys { |
||||
logger.Printf(format, v...) |
||||
} |
||||
} |
@ -0,0 +1,10 @@ |
||||
package ethutil |
||||
|
||||
// Database interface
|
||||
type Database interface { |
||||
Put(key []byte, value []byte) |
||||
Get(key []byte) ([]byte, error) |
||||
LastKnownTD() []byte |
||||
Close() |
||||
Print() |
||||
} |
@ -0,0 +1,62 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/hex" |
||||
_ "fmt" |
||||
"strings" |
||||
) |
||||
|
||||
func CompactEncode(hexSlice []int) string { |
||||
terminator := 0 |
||||
if hexSlice[len(hexSlice)-1] == 16 { |
||||
terminator = 1 |
||||
} |
||||
|
||||
if terminator == 1 { |
||||
hexSlice = hexSlice[:len(hexSlice)-1] |
||||
} |
||||
|
||||
oddlen := len(hexSlice) % 2 |
||||
flags := 2*terminator + oddlen |
||||
if oddlen != 0 { |
||||
hexSlice = append([]int{flags}, hexSlice...) |
||||
} else { |
||||
hexSlice = append([]int{flags, 0}, hexSlice...) |
||||
} |
||||
|
||||
var buff bytes.Buffer |
||||
for i := 0; i < len(hexSlice); i += 2 { |
||||
buff.WriteByte(byte(16*hexSlice[i] + hexSlice[i+1])) |
||||
} |
||||
|
||||
return buff.String() |
||||
} |
||||
|
||||
func CompactDecode(str string) []int { |
||||
base := CompactHexDecode(str) |
||||
base = base[:len(base)-1] |
||||
if base[0] >= 2 { // && base[len(base)-1] != 16 {
|
||||
base = append(base, 16) |
||||
} |
||||
if base[0]%2 == 1 { |
||||
base = base[1:] |
||||
} else { |
||||
base = base[2:] |
||||
} |
||||
|
||||
return base |
||||
} |
||||
|
||||
func CompactHexDecode(str string) []int { |
||||
base := "0123456789abcdef" |
||||
hexSlice := make([]int, 0) |
||||
|
||||
enc := hex.EncodeToString([]byte(str)) |
||||
for _, v := range enc { |
||||
hexSlice = append(hexSlice, strings.IndexByte(base, byte(v))) |
||||
} |
||||
hexSlice = append(hexSlice, 16) |
||||
|
||||
return hexSlice |
||||
} |
@ -0,0 +1,37 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"fmt" |
||||
"testing" |
||||
) |
||||
|
||||
func TestCompactEncode(t *testing.T) { |
||||
test1 := []int{1, 2, 3, 4, 5} |
||||
if res := CompactEncode(test1); res != "\x11\x23\x45" { |
||||
t.Error(fmt.Sprintf("even compact encode failed. Got: %q", res)) |
||||
} |
||||
|
||||
test2 := []int{0, 1, 2, 3, 4, 5} |
||||
if res := CompactEncode(test2); res != "\x00\x01\x23\x45" { |
||||
t.Error(fmt.Sprintf("odd compact encode failed. Got: %q", res)) |
||||
} |
||||
|
||||
test3 := []int{0, 15, 1, 12, 11, 8 /*term*/, 16} |
||||
if res := CompactEncode(test3); res != "\x20\x0f\x1c\xb8" { |
||||
t.Error(fmt.Sprintf("odd terminated compact encode failed. Got: %q", res)) |
||||
} |
||||
|
||||
test4 := []int{15, 1, 12, 11, 8 /*term*/, 16} |
||||
if res := CompactEncode(test4); res != "\x3f\x1c\xb8" { |
||||
t.Error(fmt.Sprintf("even terminated compact encode failed. Got: %q", res)) |
||||
} |
||||
} |
||||
|
||||
func TestCompactHexDecode(t *testing.T) { |
||||
exp := []int{7, 6, 6, 5, 7, 2, 6, 2, 16} |
||||
res := CompactHexDecode("verb") |
||||
|
||||
if !CompareIntSlice(res, exp) { |
||||
t.Error("Error compact hex decode. Expected", exp, "got", res) |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"code.google.com/p/go.crypto/ripemd160" |
||||
"crypto/sha256" |
||||
"encoding/hex" |
||||
"github.com/obscuren/sha3" |
||||
"strconv" |
||||
) |
||||
|
||||
func Uitoa(i uint32) string { |
||||
return strconv.FormatUint(uint64(i), 10) |
||||
} |
||||
|
||||
func Sha256Bin(data []byte) []byte { |
||||
hash := sha256.Sum256(data) |
||||
|
||||
return hash[:] |
||||
} |
||||
|
||||
func Ripemd160(data []byte) []byte { |
||||
ripemd := ripemd160.New() |
||||
ripemd.Write(data) |
||||
|
||||
return ripemd.Sum(nil) |
||||
} |
||||
|
||||
func Sha3Bin(data []byte) []byte { |
||||
d := sha3.NewKeccak256() |
||||
d.Reset() |
||||
d.Write(data) |
||||
|
||||
return d.Sum(nil) |
||||
} |
||||
|
||||
// Helper function for comparing slices
|
||||
func CompareIntSlice(a, b []int) bool { |
||||
if len(a) != len(b) { |
||||
return false |
||||
} |
||||
for i, v := range a { |
||||
if v != b[i] { |
||||
return false |
||||
} |
||||
} |
||||
return true |
||||
} |
||||
|
||||
// Returns the amount of nibbles that match each other from 0 ...
|
||||
func MatchingNibbleLength(a, b []int) int { |
||||
i := 0 |
||||
for CompareIntSlice(a[:i+1], b[:i+1]) && i < len(b) { |
||||
i += 1 |
||||
} |
||||
|
||||
return i |
||||
} |
||||
|
||||
func Hex(d []byte) string { |
||||
return hex.EncodeToString(d) |
||||
} |
@ -0,0 +1,108 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"math/big" |
||||
"strconv" |
||||
"strings" |
||||
) |
||||
|
||||
// Op codes
|
||||
var OpCodes = map[string]string{ |
||||
"STOP": "0", |
||||
"ADD": "1", |
||||
"MUL": "2", |
||||
"SUB": "3", |
||||
"DIV": "4", |
||||
"SDIV": "5", |
||||
"MOD": "6", |
||||
"SMOD": "7", |
||||
"EXP": "8", |
||||
"NEG": "9", |
||||
"LT": "10", |
||||
"LE": "11", |
||||
"GT": "12", |
||||
"GE": "13", |
||||
"EQ": "14", |
||||
"NOT": "15", |
||||
"MYADDRESS": "16", |
||||
"TXSENDER": "17", |
||||
|
||||
"PUSH": "48", |
||||
"POP": "49", |
||||
"LOAD": "54", |
||||
} |
||||
|
||||
func CompileInstr(s string) (string, error) { |
||||
tokens := strings.Split(s, " ") |
||||
if OpCodes[tokens[0]] == "" { |
||||
return s, errors.New(fmt.Sprintf("OP not found: %s", tokens[0])) |
||||
} |
||||
|
||||
code := OpCodes[tokens[0]] // Replace op codes with the proper numerical equivalent
|
||||
op := new(big.Int) |
||||
op.SetString(code, 0) |
||||
|
||||
args := make([]*big.Int, 6) |
||||
for i, val := range tokens[1:len(tokens)] { |
||||
num := new(big.Int) |
||||
num.SetString(val, 0) |
||||
args[i] = num |
||||
} |
||||
|
||||
// Big int equation = op + x * 256 + y * 256**2 + z * 256**3 + a * 256**4 + b * 256**5 + c * 256**6
|
||||
base := new(big.Int) |
||||
x := new(big.Int) |
||||
y := new(big.Int) |
||||
z := new(big.Int) |
||||
a := new(big.Int) |
||||
b := new(big.Int) |
||||
c := new(big.Int) |
||||
|
||||
if args[0] != nil { |
||||
x.Mul(args[0], big.NewInt(256)) |
||||
} |
||||
if args[1] != nil { |
||||
y.Mul(args[1], BigPow(256, 2)) |
||||
} |
||||
if args[2] != nil { |
||||
z.Mul(args[2], BigPow(256, 3)) |
||||
} |
||||
if args[3] != nil { |
||||
a.Mul(args[3], BigPow(256, 4)) |
||||
} |
||||
if args[4] != nil { |
||||
b.Mul(args[4], BigPow(256, 5)) |
||||
} |
||||
if args[5] != nil { |
||||
c.Mul(args[5], BigPow(256, 6)) |
||||
} |
||||
|
||||
base.Add(op, x) |
||||
base.Add(base, y) |
||||
base.Add(base, z) |
||||
base.Add(base, a) |
||||
base.Add(base, b) |
||||
base.Add(base, c) |
||||
|
||||
return base.String(), nil |
||||
} |
||||
|
||||
func Instr(instr string) (int, []string, error) { |
||||
base := new(big.Int) |
||||
base.SetString(instr, 0) |
||||
|
||||
args := make([]string, 7) |
||||
for i := 0; i < 7; i++ { |
||||
// int(int(val) / int(math.Pow(256,float64(i)))) % 256
|
||||
exp := BigPow(256, i) |
||||
num := new(big.Int) |
||||
num.Div(base, exp) |
||||
|
||||
args[i] = num.Mod(num, big.NewInt(256)).String() |
||||
} |
||||
op, _ := strconv.Atoi(args[0]) |
||||
|
||||
return op, args[1:7], nil |
||||
} |
@ -0,0 +1,32 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"math" |
||||
"testing" |
||||
) |
||||
|
||||
func TestCompile(t *testing.T) { |
||||
instr, err := CompileInstr("PUSH") |
||||
|
||||
if err != nil { |
||||
t.Error("Failed compiling instruction") |
||||
} |
||||
|
||||
calc := (48 + 0*256 + 0*int64(math.Pow(256, 2))) |
||||
if Big(instr).Int64() != calc { |
||||
t.Error("Expected", calc, ", got:", instr) |
||||
} |
||||
} |
||||
|
||||
func TestValidInstr(t *testing.T) { |
||||
/* |
||||
op, args, err := Instr("68163") |
||||
if err != nil { |
||||
t.Error("Error decoding instruction") |
||||
} |
||||
*/ |
||||
|
||||
} |
||||
|
||||
func TestInvalidInstr(t *testing.T) { |
||||
} |
@ -0,0 +1,24 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"crypto/rand" |
||||
"encoding/binary" |
||||
"io" |
||||
) |
||||
|
||||
func randomUint64(r io.Reader) (uint64, error) { |
||||
b := make([]byte, 8) |
||||
n, err := r.Read(b) |
||||
if n != len(b) { |
||||
return 0, io.ErrShortBuffer |
||||
} |
||||
if err != nil { |
||||
return 0, err |
||||
} |
||||
return binary.BigEndian.Uint64(b), nil |
||||
} |
||||
|
||||
// RandomUint64 returns a cryptographically random uint64 value.
|
||||
func RandomUint64() (uint64, error) { |
||||
return randomUint64(rand.Reader) |
||||
} |
@ -0,0 +1,418 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"bytes" |
||||
_ "encoding/binary" |
||||
"fmt" |
||||
_ "log" |
||||
_ "math" |
||||
"math/big" |
||||
"reflect" |
||||
) |
||||
|
||||
///////////////////////////////////////
|
||||
type EthEncoder interface { |
||||
EncodeData(rlpData interface{}) []byte |
||||
} |
||||
type EthDecoder interface { |
||||
Get(idx int) *RlpValue |
||||
} |
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
type RlpEncoder struct { |
||||
rlpData []byte |
||||
} |
||||
|
||||
func NewRlpEncoder() *RlpEncoder { |
||||
encoder := &RlpEncoder{} |
||||
|
||||
return encoder |
||||
} |
||||
func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte { |
||||
return Encode(rlpData) |
||||
} |
||||
|
||||
// Data rlpValueutes are returned by the rlp decoder. The data rlpValueutes represents
|
||||
// one item within the rlp data structure. It's responsible for all the casting
|
||||
// It always returns something rlpValueid
|
||||
type RlpValue struct { |
||||
Value interface{} |
||||
kind reflect.Value |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) String() string { |
||||
return fmt.Sprintf("%q", rlpValue.Value) |
||||
} |
||||
|
||||
func Conv(rlpValue interface{}) *RlpValue { |
||||
return &RlpValue{Value: rlpValue, kind: reflect.ValueOf(rlpValue)} |
||||
} |
||||
|
||||
func NewRlpValue(rlpValue interface{}) *RlpValue { |
||||
return &RlpValue{Value: rlpValue} |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) Type() reflect.Kind { |
||||
return reflect.TypeOf(rlpValue.Value).Kind() |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) IsNil() bool { |
||||
return rlpValue.Value == nil |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) Length() int { |
||||
//return rlpValue.kind.Len()
|
||||
if data, ok := rlpValue.Value.([]interface{}); ok { |
||||
return len(data) |
||||
} |
||||
|
||||
return 0 |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsRaw() interface{} { |
||||
return rlpValue.Value |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsUint() uint64 { |
||||
if Value, ok := rlpValue.Value.(uint8); ok { |
||||
return uint64(Value) |
||||
} else if Value, ok := rlpValue.Value.(uint16); ok { |
||||
return uint64(Value) |
||||
} else if Value, ok := rlpValue.Value.(uint32); ok { |
||||
return uint64(Value) |
||||
} else if Value, ok := rlpValue.Value.(uint64); ok { |
||||
return Value |
||||
} |
||||
|
||||
return 0 |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsByte() byte { |
||||
if Value, ok := rlpValue.Value.(byte); ok { |
||||
return Value |
||||
} |
||||
|
||||
return 0x0 |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsBigInt() *big.Int { |
||||
if a, ok := rlpValue.Value.([]byte); ok { |
||||
b := new(big.Int) |
||||
b.SetBytes(a) |
||||
return b |
||||
} |
||||
|
||||
return big.NewInt(0) |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsString() string { |
||||
if a, ok := rlpValue.Value.([]byte); ok { |
||||
return string(a) |
||||
} else if a, ok := rlpValue.Value.(string); ok { |
||||
return a |
||||
} else { |
||||
//panic(fmt.Sprintf("not string %T: %v", rlpValue.Value, rlpValue.Value))
|
||||
} |
||||
|
||||
return "" |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsBytes() []byte { |
||||
if a, ok := rlpValue.Value.([]byte); ok { |
||||
return a |
||||
} |
||||
|
||||
return make([]byte, 0) |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsSlice() []interface{} { |
||||
if d, ok := rlpValue.Value.([]interface{}); ok { |
||||
return d |
||||
} |
||||
|
||||
return []interface{}{} |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsSliceFrom(from int) *RlpValue { |
||||
slice := rlpValue.AsSlice() |
||||
|
||||
return NewRlpValue(slice[from:]) |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsSliceTo(to int) *RlpValue { |
||||
slice := rlpValue.AsSlice() |
||||
|
||||
return NewRlpValue(slice[:to]) |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AsSliceFromTo(from, to int) *RlpValue { |
||||
slice := rlpValue.AsSlice() |
||||
|
||||
return NewRlpValue(slice[from:to]) |
||||
} |
||||
|
||||
// Threat the rlpValueute as a slice
|
||||
func (rlpValue *RlpValue) Get(idx int) *RlpValue { |
||||
if d, ok := rlpValue.Value.([]interface{}); ok { |
||||
// Guard for oob
|
||||
if len(d) <= idx { |
||||
return NewRlpValue(nil) |
||||
} |
||||
|
||||
if idx < 0 { |
||||
panic("negative idx for Rlp Get") |
||||
} |
||||
|
||||
return NewRlpValue(d[idx]) |
||||
} |
||||
|
||||
// If this wasn't a slice you probably shouldn't be using this function
|
||||
return NewRlpValue(nil) |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) Cmp(o *RlpValue) bool { |
||||
return reflect.DeepEqual(rlpValue.Value, o.Value) |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) Encode() []byte { |
||||
return Encode(rlpValue.Value) |
||||
} |
||||
|
||||
func NewRlpValueFromBytes(rlpData []byte) *RlpValue { |
||||
if len(rlpData) != 0 { |
||||
data, _ := Decode(rlpData, 0) |
||||
return NewRlpValue(data) |
||||
} |
||||
|
||||
return NewRlpValue(nil) |
||||
} |
||||
|
||||
// RlpValue value setters
|
||||
// An empty rlp value is always a list
|
||||
func EmptyRlpValue() *RlpValue { |
||||
return NewRlpValue([]interface{}{}) |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) AppendList() *RlpValue { |
||||
list := EmptyRlpValue() |
||||
rlpValue.Value = append(rlpValue.AsSlice(), list) |
||||
|
||||
return list |
||||
} |
||||
|
||||
func (rlpValue *RlpValue) Append(v interface{}) *RlpValue { |
||||
rlpValue.Value = append(rlpValue.AsSlice(), v) |
||||
|
||||
return rlpValue |
||||
} |
||||
|
||||
/* |
||||
func FromBin(data []byte) uint64 { |
||||
if len(data) == 0 { |
||||
return 0 |
||||
} |
||||
|
||||
return FromBin(data[:len(data)-1])*256 + uint64(data[len(data)-1]) |
||||
} |
||||
*/ |
||||
|
||||
const ( |
||||
RlpEmptyList = 0x80 |
||||
RlpEmptyStr = 0x40 |
||||
) |
||||
|
||||
func Char(c []byte) int { |
||||
if len(c) > 0 { |
||||
return int(c[0]) |
||||
} |
||||
|
||||
return 0 |
||||
} |
||||
|
||||
func DecodeWithReader(reader *bytes.Buffer) interface{} { |
||||
var slice []interface{} |
||||
|
||||
// Read the next byte
|
||||
char := Char(reader.Next(1)) |
||||
switch { |
||||
case char == 0: |
||||
return nil |
||||
case char <= 0x7c: |
||||
return char |
||||
|
||||
case char <= 0xb7: |
||||
return reader.Next(int(char - 0x80)) |
||||
|
||||
case char <= 0xbf: |
||||
buff := bytes.NewReader(reader.Next(int(char - 0xb8))) |
||||
length := ReadVarint(buff) |
||||
|
||||
return reader.Next(int(length)) |
||||
|
||||
case char <= 0xf7: |
||||
length := int(char - 0xc0) |
||||
for i := 0; i < length; i++ { |
||||
obj := DecodeWithReader(reader) |
||||
if obj != nil { |
||||
slice = append(slice, obj) |
||||
} else { |
||||
break |
||||
} |
||||
} |
||||
|
||||
return slice |
||||
|
||||
} |
||||
|
||||
return slice |
||||
} |
||||
|
||||
// TODO Use a bytes.Buffer instead of a raw byte slice.
|
||||
// Cleaner code, and use draining instead of seeking the next bytes to read
|
||||
func Decode(data []byte, pos uint64) (interface{}, uint64) { |
||||
/* |
||||
if pos > uint64(len(data)-1) { |
||||
log.Println(data) |
||||
log.Panicf("index out of range %d for data %q, l = %d", pos, data, len(data)) |
||||
} |
||||
*/ |
||||
|
||||
var slice []interface{} |
||||
char := int(data[pos]) |
||||
switch { |
||||
case char <= 0x7f: |
||||
return data[pos], pos + 1 |
||||
|
||||
case char <= 0xb7: |
||||
b := uint64(data[pos]) - 0x80 |
||||
|
||||
return data[pos+1 : pos+1+b], pos + 1 + b |
||||
|
||||
case char <= 0xbf: |
||||
b := uint64(data[pos]) - 0xb7 |
||||
|
||||
b2 := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+b])) |
||||
|
||||
return data[pos+1+b : pos+1+b+b2], pos + 1 + b + b2 |
||||
|
||||
case char <= 0xf7: |
||||
b := uint64(data[pos]) - 0xc0 |
||||
prevPos := pos |
||||
pos++ |
||||
for i := uint64(0); i < b; { |
||||
var obj interface{} |
||||
|
||||
// Get the next item in the data list and append it
|
||||
obj, prevPos = Decode(data, pos) |
||||
slice = append(slice, obj) |
||||
|
||||
// Increment i by the amount bytes read in the previous
|
||||
// read
|
||||
i += (prevPos - pos) |
||||
pos = prevPos |
||||
} |
||||
return slice, pos |
||||
|
||||
case char <= 0xff: |
||||
l := uint64(data[pos]) - 0xf7 |
||||
//b := BigD(data[pos+1 : pos+1+l]).Uint64()
|
||||
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l])) |
||||
|
||||
pos = pos + l + 1 |
||||
|
||||
prevPos := b |
||||
for i := uint64(0); i < uint64(b); { |
||||
var obj interface{} |
||||
|
||||
obj, prevPos = Decode(data, pos) |
||||
slice = append(slice, obj) |
||||
|
||||
i += (prevPos - pos) |
||||
pos = prevPos |
||||
} |
||||
return slice, pos |
||||
|
||||
default: |
||||
panic(fmt.Sprintf("byte not supported: %q", char)) |
||||
} |
||||
|
||||
return slice, 0 |
||||
} |
||||
|
||||
var ( |
||||
directRlp = big.NewInt(0x7f) |
||||
numberRlp = big.NewInt(0xb7) |
||||
zeroRlp = big.NewInt(0x0) |
||||
) |
||||
|
||||
func Encode(object interface{}) []byte { |
||||
var buff bytes.Buffer |
||||
|
||||
if object != nil { |
||||
switch t := object.(type) { |
||||
case *RlpValue: |
||||
buff.Write(Encode(t.AsRaw())) |
||||
// Code dup :-/
|
||||
case int: |
||||
buff.Write(Encode(big.NewInt(int64(t)))) |
||||
case uint: |
||||
buff.Write(Encode(big.NewInt(int64(t)))) |
||||
case int8: |
||||
buff.Write(Encode(big.NewInt(int64(t)))) |
||||
case int16: |
||||
buff.Write(Encode(big.NewInt(int64(t)))) |
||||
case int32: |
||||
buff.Write(Encode(big.NewInt(int64(t)))) |
||||
case int64: |
||||
buff.Write(Encode(big.NewInt(t))) |
||||
case uint16: |
||||
buff.Write(Encode(big.NewInt(int64(t)))) |
||||
case uint32: |
||||
buff.Write(Encode(big.NewInt(int64(t)))) |
||||
case uint64: |
||||
buff.Write(Encode(big.NewInt(int64(t)))) |
||||
case byte: |
||||
buff.Write(Encode(big.NewInt(int64(t)))) |
||||
case *big.Int: |
||||
buff.Write(Encode(t.Bytes())) |
||||
case []byte: |
||||
if len(t) == 1 && t[0] <= 0x7f { |
||||
buff.Write(t) |
||||
} else if len(t) < 56 { |
||||
buff.WriteByte(byte(len(t) + 0x80)) |
||||
buff.Write(t) |
||||
} else { |
||||
b := big.NewInt(int64(len(t))) |
||||
buff.WriteByte(byte(len(b.Bytes()) + 0xb7)) |
||||
buff.Write(b.Bytes()) |
||||
buff.Write(t) |
||||
} |
||||
case string: |
||||
buff.Write(Encode([]byte(t))) |
||||
case []interface{}: |
||||
// Inline function for writing the slice header
|
||||
WriteSliceHeader := func(length int) { |
||||
if length < 56 { |
||||
buff.WriteByte(byte(length + 0xc0)) |
||||
} else { |
||||
b := big.NewInt(int64(length)) |
||||
buff.WriteByte(byte(len(b.Bytes()) + 0xf7)) |
||||
buff.Write(b.Bytes()) |
||||
} |
||||
} |
||||
|
||||
var b bytes.Buffer |
||||
for _, val := range t { |
||||
b.Write(Encode(val)) |
||||
} |
||||
WriteSliceHeader(len(b.Bytes())) |
||||
buff.Write(b.Bytes()) |
||||
} |
||||
} else { |
||||
// Empty list for nil
|
||||
buff.WriteByte(0xc0) |
||||
} |
||||
|
||||
return buff.Bytes() |
||||
} |
@ -0,0 +1,170 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"bytes" |
||||
"encoding/hex" |
||||
"fmt" |
||||
"math/big" |
||||
"reflect" |
||||
"testing" |
||||
) |
||||
|
||||
func TestRlpValueEncoding(t *testing.T) { |
||||
val := EmptyRlpValue() |
||||
val.AppendList().Append(1).Append(2).Append(3) |
||||
val.Append("4").AppendList().Append(5) |
||||
|
||||
res := val.Encode() |
||||
exp := Encode([]interface{}{[]interface{}{1, 2, 3}, "4", []interface{}{5}}) |
||||
if bytes.Compare(res, exp) != 0 { |
||||
t.Errorf("expected %q, got %q", res, exp) |
||||
} |
||||
} |
||||
|
||||
func TestValueSlice(t *testing.T) { |
||||
val := []interface{}{ |
||||
"value1", |
||||
"valeu2", |
||||
"value3", |
||||
} |
||||
|
||||
value := NewValue(val) |
||||
splitVal := value.SliceFrom(1) |
||||
|
||||
if splitVal.Len() != 2 { |
||||
t.Error("SliceFrom: Expected len", 2, "got", splitVal.Len()) |
||||
} |
||||
|
||||
splitVal = value.SliceTo(2) |
||||
if splitVal.Len() != 2 { |
||||
t.Error("SliceTo: Expected len", 2, "got", splitVal.Len()) |
||||
} |
||||
|
||||
splitVal = value.SliceFromTo(1, 3) |
||||
if splitVal.Len() != 2 { |
||||
t.Error("SliceFromTo: Expected len", 2, "got", splitVal.Len()) |
||||
} |
||||
} |
||||
|
||||
func TestValue(t *testing.T) { |
||||
value := NewValueFromBytes([]byte("\xcd\x83dog\x83god\x83cat\x01")) |
||||
if value.Get(0).Str() != "dog" { |
||||
t.Errorf("expected '%v', got '%v'", value.Get(0).Str(), "dog") |
||||
} |
||||
|
||||
if value.Get(3).Uint() != 1 { |
||||
t.Errorf("expected '%v', got '%v'", value.Get(3).Uint(), 1) |
||||
} |
||||
} |
||||
|
||||
func TestEncode(t *testing.T) { |
||||
strRes := "\x83dog" |
||||
bytes := Encode("dog") |
||||
|
||||
str := string(bytes) |
||||
if str != strRes { |
||||
t.Error(fmt.Sprintf("Expected %q, got %q", strRes, str)) |
||||
} |
||||
|
||||
sliceRes := "\xcc\x83dog\x83god\x83cat" |
||||
strs := []interface{}{"dog", "god", "cat"} |
||||
bytes = Encode(strs) |
||||
slice := string(bytes) |
||||
if slice != sliceRes { |
||||
t.Error(fmt.Sprintf("Expected %q, got %q", sliceRes, slice)) |
||||
} |
||||
|
||||
intRes := "\x82\x04\x00" |
||||
bytes = Encode(1024) |
||||
if string(bytes) != intRes { |
||||
t.Errorf("Expected %q, got %q", intRes, bytes) |
||||
} |
||||
} |
||||
|
||||
func TestDecode(t *testing.T) { |
||||
single := []byte("\x01") |
||||
b, _ := Decode(single, 0) |
||||
|
||||
if b.(uint8) != 1 { |
||||
t.Errorf("Expected 1, got %q", b) |
||||
} |
||||
|
||||
str := []byte("\x83dog") |
||||
b, _ = Decode(str, 0) |
||||
if bytes.Compare(b.([]byte), []byte("dog")) != 0 { |
||||
t.Errorf("Expected dog, got %q", b) |
||||
} |
||||
|
||||
slice := []byte("\xcc\x83dog\x83god\x83cat") |
||||
res := []interface{}{"dog", "god", "cat"} |
||||
b, _ = Decode(slice, 0) |
||||
if reflect.DeepEqual(b, res) { |
||||
t.Errorf("Expected %q, got %q", res, b) |
||||
} |
||||
} |
||||
|
||||
func TestEncodeDecodeBigInt(t *testing.T) { |
||||
bigInt := big.NewInt(1391787038) |
||||
encoded := Encode(bigInt) |
||||
|
||||
value := NewValueFromBytes(encoded) |
||||
fmt.Println(value.BigInt(), bigInt) |
||||
if value.BigInt().Cmp(bigInt) != 0 { |
||||
t.Errorf("Expected %v, got %v", bigInt, value.BigInt()) |
||||
} |
||||
|
||||
dec, _ := hex.DecodeString("52f4fc1e") |
||||
fmt.Println(NewValueFromBytes(dec).BigInt()) |
||||
} |
||||
|
||||
func TestEncodeDecodeBytes(t *testing.T) { |
||||
b := NewValue([]interface{}{[]byte{1, 2, 3, 4, 5}, byte(6)}) |
||||
val := NewValueFromBytes(b.Encode()) |
||||
if !b.Cmp(val) { |
||||
t.Errorf("Expected %v, got %v", val, b) |
||||
} |
||||
} |
||||
|
||||
/* |
||||
var ZeroHash256 = make([]byte, 32) |
||||
var ZeroHash160 = make([]byte, 20) |
||||
var EmptyShaList = Sha3Bin(Encode([]interface{}{})) |
||||
|
||||
var GenisisHeader = []interface{}{ |
||||
// Previous hash (none)
|
||||
//"",
|
||||
ZeroHash256, |
||||
// Sha of uncles
|
||||
Sha3Bin(Encode([]interface{}{})), |
||||
// Coinbase
|
||||
ZeroHash160, |
||||
// Root state
|
||||
"", |
||||
// Sha of transactions
|
||||
//EmptyShaList,
|
||||
Sha3Bin(Encode([]interface{}{})), |
||||
// Difficulty
|
||||
BigPow(2, 22), |
||||
// Time
|
||||
//big.NewInt(0),
|
||||
int64(0), |
||||
// extra
|
||||
"", |
||||
// Nonce
|
||||
big.NewInt(42), |
||||
} |
||||
|
||||
func TestEnc(t *testing.T) { |
||||
//enc := Encode(GenisisHeader)
|
||||
//fmt.Printf("%x (%d)\n", enc, len(enc))
|
||||
h, _ := hex.DecodeString("f8a0a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a06d076baa9c4074fb2df222dd16a96b0155a1e6686b3e5748b4e9ca0a208a425ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493478340000080802a") |
||||
fmt.Printf("%x\n", Sha3Bin(h)) |
||||
} |
||||
*/ |
||||
|
||||
func BenchmarkEncodeDecode(b *testing.B) { |
||||
for i := 0; i < b.N; i++ { |
||||
bytes := Encode([]interface{}{"dog", "god", "cat"}) |
||||
Decode(bytes, 0) |
||||
} |
||||
} |
@ -0,0 +1,354 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"fmt" |
||||
"reflect" |
||||
) |
||||
|
||||
type Node struct { |
||||
Key []byte |
||||
Value *Value |
||||
Dirty bool |
||||
} |
||||
|
||||
func NewNode(key []byte, val *Value, dirty bool) *Node { |
||||
return &Node{Key: key, Value: val, Dirty: dirty} |
||||
} |
||||
|
||||
func (n *Node) Copy() *Node { |
||||
return NewNode(n.Key, n.Value, n.Dirty) |
||||
} |
||||
|
||||
type Cache struct { |
||||
nodes map[string]*Node |
||||
db Database |
||||
} |
||||
|
||||
func NewCache(db Database) *Cache { |
||||
return &Cache{db: db, nodes: make(map[string]*Node)} |
||||
} |
||||
|
||||
func (cache *Cache) Put(v interface{}) interface{} { |
||||
value := NewValue(v) |
||||
|
||||
enc := value.Encode() |
||||
if len(enc) >= 32 { |
||||
sha := Sha3Bin(enc) |
||||
|
||||
cache.nodes[string(sha)] = NewNode(sha, value, true) |
||||
|
||||
return sha |
||||
} |
||||
|
||||
return v |
||||
} |
||||
|
||||
func (cache *Cache) Get(key []byte) *Value { |
||||
// First check if the key is the cache
|
||||
if cache.nodes[string(key)] != nil { |
||||
return cache.nodes[string(key)].Value |
||||
} |
||||
|
||||
// Get the key of the database instead and cache it
|
||||
data, _ := cache.db.Get(key) |
||||
// Create the cached value
|
||||
value := NewValueFromBytes(data) |
||||
// Create caching node
|
||||
cache.nodes[string(key)] = NewNode(key, value, false) |
||||
|
||||
return value |
||||
} |
||||
|
||||
func (cache *Cache) Commit() { |
||||
for key, node := range cache.nodes { |
||||
if node.Dirty { |
||||
cache.db.Put([]byte(key), node.Value.Encode()) |
||||
node.Dirty = false |
||||
} |
||||
} |
||||
|
||||
// If the nodes grows beyond the 200 entries we simple empty it
|
||||
// FIXME come up with something better
|
||||
if len(cache.nodes) > 200 { |
||||
cache.nodes = make(map[string]*Node) |
||||
} |
||||
} |
||||
|
||||
func (cache *Cache) Undo() { |
||||
for key, node := range cache.nodes { |
||||
if node.Dirty { |
||||
delete(cache.nodes, key) |
||||
} |
||||
} |
||||
} |
||||
|
||||
// A (modified) Radix Trie implementation
|
||||
type Trie struct { |
||||
Root interface{} |
||||
//db Database
|
||||
cache *Cache |
||||
} |
||||
|
||||
func NewTrie(db Database, Root interface{}) *Trie { |
||||
return &Trie{cache: NewCache(db), Root: Root} |
||||
} |
||||
|
||||
func (t *Trie) Sync() { |
||||
t.cache.Commit() |
||||
} |
||||
|
||||
/* |
||||
* Public (query) interface functions |
||||
*/ |
||||
func (t *Trie) Update(key string, value string) { |
||||
k := CompactHexDecode(key) |
||||
|
||||
t.Root = t.UpdateState(t.Root, k, value) |
||||
} |
||||
|
||||
func (t *Trie) Get(key string) string { |
||||
k := CompactHexDecode(key) |
||||
c := NewValue(t.GetState(t.Root, k)) |
||||
|
||||
return c.Str() |
||||
} |
||||
|
||||
func (t *Trie) GetState(node interface{}, key []int) interface{} { |
||||
n := NewValue(node) |
||||
// Return the node if key is empty (= found)
|
||||
if len(key) == 0 || n.IsNil() || n.Len() == 0 { |
||||
return node |
||||
} |
||||
|
||||
currentNode := t.GetNode(node) |
||||
length := currentNode.Len() |
||||
|
||||
if length == 0 { |
||||
return "" |
||||
} else if length == 2 { |
||||
// Decode the key
|
||||
k := CompactDecode(currentNode.Get(0).Str()) |
||||
v := currentNode.Get(1).Raw() |
||||
|
||||
if len(key) >= len(k) && CompareIntSlice(k, key[:len(k)]) { |
||||
return t.GetState(v, key[len(k):]) |
||||
} else { |
||||
return "" |
||||
} |
||||
} else if length == 17 { |
||||
return t.GetState(currentNode.Get(key[0]).Raw(), key[1:]) |
||||
} |
||||
|
||||
// It shouldn't come this far
|
||||
fmt.Println("GetState unexpected return") |
||||
return "" |
||||
} |
||||
|
||||
func (t *Trie) GetNode(node interface{}) *Value { |
||||
n := NewValue(node) |
||||
|
||||
if !n.Get(0).IsNil() { |
||||
return n |
||||
} |
||||
|
||||
str := n.Str() |
||||
if len(str) == 0 { |
||||
return n |
||||
} else if len(str) < 32 { |
||||
return NewValueFromBytes([]byte(str)) |
||||
} |
||||
/* |
||||
else { |
||||
// Fetch the encoded node from the db
|
||||
o, err := t.db.Get(n.Bytes()) |
||||
if err != nil { |
||||
fmt.Println("Error InsertState", err) |
||||
return NewValue("") |
||||
} |
||||
|
||||
return NewValueFromBytes(o) |
||||
} |
||||
*/ |
||||
return t.cache.Get(n.Bytes()) |
||||
|
||||
} |
||||
|
||||
func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{} { |
||||
if value != "" { |
||||
return t.InsertState(node, key, value) |
||||
} else { |
||||
// delete it
|
||||
} |
||||
|
||||
return "" |
||||
} |
||||
|
||||
func (t *Trie) Put(node interface{}) interface{} { |
||||
/* |
||||
enc := Encode(node) |
||||
if len(enc) >= 32 { |
||||
var sha []byte |
||||
sha = Sha3Bin(enc) |
||||
//t.db.Put([]byte(sha), enc)
|
||||
|
||||
return sha |
||||
} |
||||
return node |
||||
*/ |
||||
|
||||
/* |
||||
TODO? |
||||
c := Conv(t.Root) |
||||
fmt.Println(c.Type(), c.Length()) |
||||
if c.Type() == reflect.String && c.AsString() == "" { |
||||
return enc |
||||
} |
||||
*/ |
||||
|
||||
return t.cache.Put(node) |
||||
|
||||
} |
||||
|
||||
func EmptyStringSlice(l int) []interface{} { |
||||
slice := make([]interface{}, l) |
||||
for i := 0; i < l; i++ { |
||||
slice[i] = "" |
||||
} |
||||
return slice |
||||
} |
||||
|
||||
func (t *Trie) InsertState(node interface{}, key []int, value interface{}) interface{} { |
||||
if len(key) == 0 { |
||||
return value |
||||
} |
||||
|
||||
// New node
|
||||
n := NewValue(node) |
||||
if node == nil || (n.Type() == reflect.String && (n.Str() == "" || n.Get(0).IsNil())) || n.Len() == 0 { |
||||
newNode := []interface{}{CompactEncode(key), value} |
||||
|
||||
return t.Put(newNode) |
||||
} |
||||
|
||||
currentNode := t.GetNode(node) |
||||
// Check for "special" 2 slice type node
|
||||
if currentNode.Len() == 2 { |
||||
// Decode the key
|
||||
k := CompactDecode(currentNode.Get(0).Str()) |
||||
v := currentNode.Get(1).Raw() |
||||
|
||||
// Matching key pair (ie. there's already an object with this key)
|
||||
if CompareIntSlice(k, key) { |
||||
newNode := []interface{}{CompactEncode(key), value} |
||||
return t.Put(newNode) |
||||
} |
||||
|
||||
var newHash interface{} |
||||
matchingLength := MatchingNibbleLength(key, k) |
||||
if matchingLength == len(k) { |
||||
// Insert the hash, creating a new node
|
||||
newHash = t.InsertState(v, key[matchingLength:], value) |
||||
} else { |
||||
// Expand the 2 length slice to a 17 length slice
|
||||
oldNode := t.InsertState("", k[matchingLength+1:], v) |
||||
newNode := t.InsertState("", key[matchingLength+1:], value) |
||||
// Create an expanded slice
|
||||
scaledSlice := EmptyStringSlice(17) |
||||
// Set the copied and new node
|
||||
scaledSlice[k[matchingLength]] = oldNode |
||||
scaledSlice[key[matchingLength]] = newNode |
||||
|
||||
newHash = t.Put(scaledSlice) |
||||
} |
||||
|
||||
if matchingLength == 0 { |
||||
// End of the chain, return
|
||||
return newHash |
||||
} else { |
||||
newNode := []interface{}{CompactEncode(key[:matchingLength]), newHash} |
||||
return t.Put(newNode) |
||||
} |
||||
} else { |
||||
|
||||
// Copy the current node over to the new node and replace the first nibble in the key
|
||||
newNode := EmptyStringSlice(17) |
||||
|
||||
for i := 0; i < 17; i++ { |
||||
cpy := currentNode.Get(i).Raw() |
||||
if cpy != nil { |
||||
newNode[i] = cpy |
||||
} |
||||
} |
||||
|
||||
newNode[key[0]] = t.InsertState(currentNode.Get(key[0]).Raw(), key[1:], value) |
||||
|
||||
return t.Put(newNode) |
||||
} |
||||
|
||||
return "" |
||||
} |
||||
|
||||
// Simple compare function which creates a rlp value out of the evaluated objects
|
||||
func (t *Trie) Cmp(trie *Trie) bool { |
||||
return NewValue(t.Root).Cmp(NewValue(trie.Root)) |
||||
} |
||||
|
||||
// Returns a copy of this trie
|
||||
func (t *Trie) Copy() *Trie { |
||||
trie := NewTrie(t.cache.db, t.Root) |
||||
for key, node := range t.cache.nodes { |
||||
trie.cache.nodes[key] = node.Copy() |
||||
} |
||||
|
||||
return trie |
||||
} |
||||
|
||||
/* |
||||
* Trie helper functions |
||||
*/ |
||||
// Helper function for printing out the raw contents of a slice
|
||||
func PrintSlice(slice []string) { |
||||
fmt.Printf("[") |
||||
for i, val := range slice { |
||||
fmt.Printf("%q", val) |
||||
if i != len(slice)-1 { |
||||
fmt.Printf(",") |
||||
} |
||||
} |
||||
fmt.Printf("]\n") |
||||
} |
||||
|
||||
func PrintSliceT(slice interface{}) { |
||||
c := Conv(slice) |
||||
for i := 0; i < c.Length(); i++ { |
||||
val := c.Get(i) |
||||
if val.Type() == reflect.Slice { |
||||
PrintSliceT(val.AsRaw()) |
||||
} else { |
||||
fmt.Printf("%q", val) |
||||
if i != c.Length()-1 { |
||||
fmt.Printf(",") |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
// RLP Decodes a node in to a [2] or [17] string slice
|
||||
func DecodeNode(data []byte) []string { |
||||
dec, _ := Decode(data, 0) |
||||
if slice, ok := dec.([]interface{}); ok { |
||||
strSlice := make([]string, len(slice)) |
||||
|
||||
for i, s := range slice { |
||||
if str, ok := s.([]byte); ok { |
||||
strSlice[i] = string(str) |
||||
} |
||||
} |
||||
|
||||
return strSlice |
||||
} else { |
||||
fmt.Printf("It wasn't a []. It's a %T\n", dec) |
||||
} |
||||
|
||||
return nil |
||||
} |
@ -0,0 +1,40 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
_ "encoding/hex" |
||||
_ "fmt" |
||||
"testing" |
||||
) |
||||
|
||||
type MemDatabase struct { |
||||
db map[string][]byte |
||||
} |
||||
|
||||
func NewMemDatabase() (*MemDatabase, error) { |
||||
db := &MemDatabase{db: make(map[string][]byte)} |
||||
return db, nil |
||||
} |
||||
func (db *MemDatabase) Put(key []byte, value []byte) { |
||||
db.db[string(key)] = value |
||||
} |
||||
func (db *MemDatabase) Get(key []byte) ([]byte, error) { |
||||
return db.db[string(key)], nil |
||||
} |
||||
func (db *MemDatabase) Print() {} |
||||
func (db *MemDatabase) Close() {} |
||||
func (db *MemDatabase) LastKnownTD() []byte { return nil } |
||||
|
||||
func TestTrieSync(t *testing.T) { |
||||
db, _ := NewMemDatabase() |
||||
trie := NewTrie(db, "") |
||||
|
||||
trie.Update("dog", "kindofalongsentencewhichshouldbeencodedinitsentirety") |
||||
if len(db.db) != 0 { |
||||
t.Error("Expected no data in database") |
||||
} |
||||
|
||||
trie.Sync() |
||||
if len(db.db) == 0 { |
||||
t.Error("Expected data to be persisted") |
||||
} |
||||
} |
@ -0,0 +1,204 @@ |
||||
package ethutil |
||||
|
||||
import ( |
||||
"bytes" |
||||
"fmt" |
||||
"math/big" |
||||
"reflect" |
||||
) |
||||
|
||||
// Data values are returned by the rlp decoder. The data values represents
|
||||
// one item within the rlp data structure. It's responsible for all the casting
|
||||
// It always returns something valid
|
||||
type Value struct { |
||||
Val interface{} |
||||
kind reflect.Value |
||||
} |
||||
|
||||
func (val *Value) String() string { |
||||
return fmt.Sprintf("%q", val.Val) |
||||
} |
||||
|
||||
func NewValue(val interface{}) *Value { |
||||
return &Value{Val: val} |
||||
} |
||||
|
||||
func (val *Value) Type() reflect.Kind { |
||||
return reflect.TypeOf(val.Val).Kind() |
||||
} |
||||
|
||||
func (val *Value) IsNil() bool { |
||||
return val.Val == nil |
||||
} |
||||
|
||||
func (val *Value) Len() int { |
||||
//return val.kind.Len()
|
||||
if data, ok := val.Val.([]interface{}); ok { |
||||
return len(data) |
||||
} else if data, ok := val.Val.([]byte); ok { |
||||
// FIXME
|
||||
return len(data) |
||||
} |
||||
|
||||
return 0 |
||||
} |
||||
|
||||
func (val *Value) Raw() interface{} { |
||||
return val.Val |
||||
} |
||||
|
||||
func (val *Value) Interface() interface{} { |
||||
return val.Val |
||||
} |
||||
|
||||
func (val *Value) Uint() uint64 { |
||||
if Val, ok := val.Val.(uint8); ok { |
||||
return uint64(Val) |
||||
} else if Val, ok := val.Val.(uint16); ok { |
||||
return uint64(Val) |
||||
} else if Val, ok := val.Val.(uint32); ok { |
||||
return uint64(Val) |
||||
} else if Val, ok := val.Val.(uint64); ok { |
||||
return Val |
||||
} else if Val, ok := val.Val.([]byte); ok { |
||||
return ReadVarint(bytes.NewReader(Val)) |
||||
} |
||||
|
||||
return 0 |
||||
} |
||||
|
||||
func (val *Value) Byte() byte { |
||||
if Val, ok := val.Val.(byte); ok { |
||||
return Val |
||||
} |
||||
|
||||
return 0x0 |
||||
} |
||||
|
||||
func (val *Value) BigInt() *big.Int { |
||||
if a, ok := val.Val.([]byte); ok { |
||||
b := new(big.Int).SetBytes(a) |
||||
|
||||
return b |
||||
} else { |
||||
return big.NewInt(int64(val.Uint())) |
||||
} |
||||
|
||||
return big.NewInt(0) |
||||
} |
||||
|
||||
func (val *Value) Str() string { |
||||
if a, ok := val.Val.([]byte); ok { |
||||
return string(a) |
||||
} else if a, ok := val.Val.(string); ok { |
||||
return a |
||||
} |
||||
|
||||
return "" |
||||
} |
||||
|
||||
func (val *Value) Bytes() []byte { |
||||
if a, ok := val.Val.([]byte); ok { |
||||
return a |
||||
} |
||||
|
||||
return make([]byte, 0) |
||||
} |
||||
|
||||
func (val *Value) Slice() []interface{} { |
||||
if d, ok := val.Val.([]interface{}); ok { |
||||
return d |
||||
} |
||||
|
||||
return []interface{}{} |
||||
} |
||||
|
||||
func (val *Value) SliceFrom(from int) *Value { |
||||
slice := val.Slice() |
||||
|
||||
return NewValue(slice[from:]) |
||||
} |
||||
|
||||
func (val *Value) SliceTo(to int) *Value { |
||||
slice := val.Slice() |
||||
|
||||
return NewValue(slice[:to]) |
||||
} |
||||
|
||||
func (val *Value) SliceFromTo(from, to int) *Value { |
||||
slice := val.Slice() |
||||
|
||||
return NewValue(slice[from:to]) |
||||
} |
||||
|
||||
// Threat the value as a slice
|
||||
func (val *Value) Get(idx int) *Value { |
||||
if d, ok := val.Val.([]interface{}); ok { |
||||
// Guard for oob
|
||||
if len(d) <= idx { |
||||
return NewValue(nil) |
||||
} |
||||
|
||||
if idx < 0 { |
||||
panic("negative idx for Rlp Get") |
||||
} |
||||
|
||||
return NewValue(d[idx]) |
||||
} |
||||
|
||||
// If this wasn't a slice you probably shouldn't be using this function
|
||||
return NewValue(nil) |
||||
} |
||||
|
||||
func (val *Value) Cmp(o *Value) bool { |
||||
return reflect.DeepEqual(val.Val, o.Val) |
||||
} |
||||
|
||||
func (val *Value) Encode() []byte { |
||||
return Encode(val.Val) |
||||
} |
||||
|
||||
func NewValueFromBytes(rlpData []byte) *Value { |
||||
if len(rlpData) != 0 { |
||||
data, _ := Decode(rlpData, 0) |
||||
return NewValue(data) |
||||
} |
||||
|
||||
return NewValue(nil) |
||||
} |
||||
|
||||
// Value setters
|
||||
func NewSliceValue(s interface{}) *Value { |
||||
list := EmptyValue() |
||||
|
||||
if s != nil { |
||||
if slice, ok := s.([]interface{}); ok { |
||||
for _, val := range slice { |
||||
list.Append(val) |
||||
} |
||||
} else if slice, ok := s.([]string); ok { |
||||
for _, val := range slice { |
||||
list.Append(val) |
||||
} |
||||
} |
||||
} |
||||
|
||||
return list |
||||
} |
||||
|
||||
func EmptyValue() *Value { |
||||
return NewValue([]interface{}{}) |
||||
} |
||||
|
||||
func (val *Value) AppendList() *Value { |
||||
list := EmptyValue() |
||||
val.Val = append(val.Slice(), list) |
||||
|
||||
return list |
||||
} |
||||
|
||||
func (val *Value) Append(v interface{}) *Value { |
||||
val.Val = append(val.Slice(), v) |
||||
|
||||
return val |
||||
} |
@ -0,0 +1,12 @@ |
||||
# See http://help.github.com/ignore-files/ for more about ignoring files. |
||||
# |
||||
# If you find yourself ignoring temporary files generated by your text editor |
||||
# or operating system, you probably want to add a global ignore instead: |
||||
# git config --global core.excludesfile ~/.gitignore_global |
||||
|
||||
/tmp |
||||
*/**/*un~ |
||||
*un~ |
||||
.DS_Store |
||||
*/**/.DS_Store |
||||
|
@ -0,0 +1,36 @@ |
||||
# ethwire |
||||
|
||||
The ethwire package contains the ethereum wire protocol. The ethwire |
||||
package is required to write and read from the ethereum network. |
||||
|
||||
# Installation |
||||
|
||||
`go get github.com/ethereum/ethwire-go` |
||||
|
||||
# Messaging overview |
||||
|
||||
The Ethereum Wire protocol defines the communication between the nodes |
||||
running Ethereum. Further reader reading can be done on the |
||||
[Wiki](http://wiki.ethereum.org/index.php/Wire_Protocol). |
||||
|
||||
# Reading Messages |
||||
|
||||
```go |
||||
// Read and validate the next eth message from the provided connection. |
||||
// returns a error message with the details. |
||||
msg, err := ethwire.ReadMessage(conn) |
||||
if err != nil { |
||||
// Handle error |
||||
} |
||||
``` |
||||
|
||||
# Writing Messages |
||||
|
||||
```go |
||||
// Constructs a message which can be interpreted by the eth network. |
||||
// Write the inventory to network |
||||
err := ethwire.WriteMessage(conn, &Msg{ |
||||
Type: ethwire.MsgInvTy, |
||||
Data : []interface{}{...}, |
||||
}) |
||||
``` |
@ -0,0 +1,180 @@ |
||||
package ethwire |
||||
|
||||
import ( |
||||
"bytes" |
||||
"errors" |
||||
"fmt" |
||||
"github.com/ethereum/eth-go/ethutil" |
||||
"net" |
||||
"time" |
||||
) |
||||
|
||||
// Message:
|
||||
// [4 bytes token] RLP([TYPE, DATA])
|
||||
// Refer to http://wiki.ethereum.org/index.php/Wire_Protocol
|
||||
|
||||
// The magic token which should be the first 4 bytes of every message.
|
||||
var MagicToken = []byte{34, 64, 8, 145} |
||||
|
||||
type MsgType byte |
||||
|
||||
const ( |
||||
MsgHandshakeTy = 0x00 |
||||
MsgDiscTy = 0x01 |
||||
MsgPingTy = 0x02 |
||||
MsgPongTy = 0x03 |
||||
MsgGetPeersTy = 0x10 |
||||
MsgPeersTy = 0x11 |
||||
MsgTxTy = 0x12 |
||||
MsgBlockTy = 0x13 |
||||
MsgGetChainTy = 0x14 |
||||
MsgNotInChainTy = 0x15 |
||||
|
||||
MsgTalkTy = 0xff |
||||
) |
||||
|
||||
var msgTypeToString = map[MsgType]string{ |
||||
MsgHandshakeTy: "Handshake", |
||||
MsgDiscTy: "Disconnect", |
||||
MsgPingTy: "Ping", |
||||
MsgPongTy: "Pong", |
||||
MsgGetPeersTy: "Get peers", |
||||
MsgPeersTy: "Peers", |
||||
MsgTxTy: "Transactions", |
||||
MsgBlockTy: "Blocks", |
||||
MsgGetChainTy: "Get chain", |
||||
MsgNotInChainTy: "Not in chain", |
||||
} |
||||
|
||||
func (mt MsgType) String() string { |
||||
return msgTypeToString[mt] |
||||
} |
||||
|
||||
type Msg struct { |
||||
Type MsgType // Specifies how the encoded data should be interpreted
|
||||
//Data []byte
|
||||
Data *ethutil.Value |
||||
} |
||||
|
||||
func NewMessage(msgType MsgType, data interface{}) *Msg { |
||||
return &Msg{ |
||||
Type: msgType, |
||||
Data: ethutil.NewValue(data), |
||||
} |
||||
} |
||||
|
||||
func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) { |
||||
if len(data) == 0 { |
||||
return nil, nil, true, nil |
||||
} |
||||
|
||||
if len(data) <= 8 { |
||||
return nil, remaining, false, errors.New("Invalid message") |
||||
} |
||||
|
||||
// Check if the received 4 first bytes are the magic token
|
||||
if bytes.Compare(MagicToken, data[:4]) != 0 { |
||||
return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4]) |
||||
} |
||||
|
||||
messageLength := ethutil.BytesToNumber(data[4:8]) |
||||
remaining = data[8+messageLength:] |
||||
if int(messageLength) > len(data[8:]) { |
||||
return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength) |
||||
} |
||||
|
||||
message := data[8 : 8+messageLength] |
||||
decoder := ethutil.NewValueFromBytes(message) |
||||
// Type of message
|
||||
t := decoder.Get(0).Uint() |
||||
// Actual data
|
||||
d := decoder.SliceFrom(1) |
||||
|
||||
msg = &Msg{ |
||||
Type: MsgType(t), |
||||
Data: d, |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func bufferedRead(conn net.Conn) ([]byte, error) { |
||||
return nil, nil |
||||
} |
||||
|
||||
// The basic message reader waits for data on the given connection, decoding
|
||||
// and doing a few sanity checks such as if there's a data type and
|
||||
// unmarhals the given data
|
||||
func ReadMessages(conn net.Conn) (msgs []*Msg, err error) { |
||||
// The recovering function in case anything goes horribly wrong
|
||||
defer func() { |
||||
if r := recover(); r != nil { |
||||
err = fmt.Errorf("ethwire.ReadMessage error: %v", r) |
||||
} |
||||
}() |
||||
|
||||
// Buff for writing network message to
|
||||
//buff := make([]byte, 1440)
|
||||
var buff []byte |
||||
var totalBytes int |
||||
for { |
||||
// Give buffering some time
|
||||
conn.SetReadDeadline(time.Now().Add(20 * time.Millisecond)) |
||||
// Create a new temporarily buffer
|
||||
b := make([]byte, 1440) |
||||
// Wait for a message from this peer
|
||||
n, _ := conn.Read(b) |
||||
if err != nil && n == 0 { |
||||
if err.Error() != "EOF" { |
||||
fmt.Println("err now", err) |
||||
return nil, err |
||||
} else { |
||||
fmt.Println("IOF NOW") |
||||
break |
||||
} |
||||
|
||||
// Messages can't be empty
|
||||
} else if n == 0 { |
||||
break |
||||
} |
||||
|
||||
buff = append(buff, b[:n]...) |
||||
totalBytes += n |
||||
} |
||||
|
||||
// Reslice buffer
|
||||
buff = buff[:totalBytes] |
||||
msg, remaining, done, err := ReadMessage(buff) |
||||
for ; done != true; msg, remaining, done, err = ReadMessage(remaining) { |
||||
//log.Println("rx", msg)
|
||||
|
||||
if msg != nil { |
||||
msgs = append(msgs, msg) |
||||
} |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
// The basic message writer takes care of writing data over the given
|
||||
// connection and does some basic error checking
|
||||
func WriteMessage(conn net.Conn, msg *Msg) error { |
||||
var pack []byte |
||||
|
||||
// Encode the type and the (RLP encoded) data for sending over the wire
|
||||
encoded := ethutil.NewValue(append([]interface{}{byte(msg.Type)}, msg.Data.Slice()...)).Encode() |
||||
payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32) |
||||
|
||||
// Write magic token and payload length (first 8 bytes)
|
||||
pack = append(MagicToken, payloadLength...) |
||||
pack = append(pack, encoded...) |
||||
//fmt.Printf("payload %v (%v) %q\n", msg.Type, conn.RemoteAddr(), encoded)
|
||||
|
||||
// Write to the connection
|
||||
_, err := conn.Write(pack) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
return nil |
||||
} |
Loading…
Reference in new issue