diff options
author | obscuren <geffobscura@gmail.com> | 2014-02-15 06:56:09 +0800 |
---|---|---|
committer | obscuren <geffobscura@gmail.com> | 2014-02-15 06:56:09 +0800 |
commit | f6d1bfe45bf3709d7bad40bf563b5c09228622e3 (patch) | |
tree | df48311a5a494c66c74dcd51f1056cc699f01507 | |
parent | c2fb9f06ad018d01ce335c82b3542de16045a32d (diff) | |
download | go-tangerine-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar go-tangerine-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.gz go-tangerine-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.bz2 go-tangerine-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.lz go-tangerine-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.xz go-tangerine-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.zst go-tangerine-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.zip |
The great merge
43 files changed, 4547 insertions, 12 deletions
diff --git a/ethchain/.gitignore b/ethchain/.gitignore new file mode 100644 index 000000000..f725d58d1 --- /dev/null +++ b/ethchain/.gitignore @@ -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 + diff --git a/ethchain/block.go b/ethchain/block.go new file mode 100644 index 000000000..a7a1f787b --- /dev/null +++ b/ethchain/block.go @@ -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, + } +} diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go new file mode 100644 index 000000000..56bc43a8e --- /dev/null +++ b/ethchain/block_chain.go @@ -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") + } +} diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go new file mode 100644 index 000000000..92f20e253 --- /dev/null +++ b/ethchain/block_manager.go @@ -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() +} diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go new file mode 100644 index 000000000..502c50b97 --- /dev/null +++ b/ethchain/block_manager_test.go @@ -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) +} +*/ diff --git a/ethchain/contract.go b/ethchain/contract.go new file mode 100644 index 000000000..d1fcec3b4 --- /dev/null +++ b/ethchain/contract.go @@ -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() +} diff --git a/ethchain/dagger.go b/ethchain/dagger.go new file mode 100644 index 000000000..5b4f8b2cd --- /dev/null +++ b/ethchain/dagger.go @@ -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)) +} diff --git a/ethchain/dagger_test.go b/ethchain/dagger_test.go new file mode 100644 index 000000000..9d4e03c92 --- /dev/null +++ b/ethchain/dagger_test.go @@ -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) +} diff --git a/ethchain/error.go b/ethchain/error.go new file mode 100644 index 000000000..0f1d061c0 --- /dev/null +++ b/ethchain/error.go @@ -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 +} diff --git a/ethchain/fees.go b/ethchain/fees.go new file mode 100644 index 000000000..8f1646ab4 --- /dev/null +++ b/ethchain/fees.go @@ -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) + */ +} diff --git a/ethchain/genesis.go b/ethchain/genesis.go new file mode 100644 index 000000000..060d347e4 --- /dev/null +++ b/ethchain/genesis.go @@ -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{}{}} diff --git a/ethchain/stack.go b/ethchain/stack.go new file mode 100644 index 000000000..c80d01e5c --- /dev/null +++ b/ethchain/stack.go @@ -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) +} diff --git a/ethchain/transaction.go b/ethchain/transaction.go new file mode 100644 index 000000000..1a9258201 --- /dev/null +++ b/ethchain/transaction.go @@ -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() +} diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go new file mode 100644 index 000000000..c2d65a2a7 --- /dev/null +++ b/ethchain/transaction_pool.go @@ -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() +} diff --git a/ethchain/transaction_test.go b/ethchain/transaction_test.go new file mode 100644 index 000000000..c9090b83d --- /dev/null +++ b/ethchain/transaction_test.go @@ -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()) +} diff --git a/ethdb/.gitignore b/ethdb/.gitignore new file mode 100644 index 000000000..f725d58d1 --- /dev/null +++ b/ethdb/.gitignore @@ -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 + diff --git a/ethdb/README.md b/ethdb/README.md new file mode 100644 index 000000000..5bed8eedc --- /dev/null +++ b/ethdb/README.md @@ -0,0 +1,11 @@ +# ethdb + +The ethdb package contains the ethereum database interfaces + +# Installation + +`go get github.com/ethereum/ethdb-go` + +# Usage + +Todo :-) diff --git a/ethdb/database.go b/ethdb/database.go new file mode 100644 index 000000000..76e4b4e4d --- /dev/null +++ b/ethdb/database.go @@ -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) + } +} diff --git a/ethdb/database_test.go b/ethdb/database_test.go new file mode 100644 index 000000000..bb1b4de2a --- /dev/null +++ b/ethdb/database_test.go @@ -0,0 +1,6 @@ +package ethdb + +import ( + _ "fmt" + _ "testing" +) diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go new file mode 100644 index 000000000..656de9f0e --- /dev/null +++ b/ethdb/memory_database.go @@ -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 +} diff --git a/ethereum.go b/ethereum.go index e1bc38542..9feb5a15c 100644 --- a/ethereum.go +++ b/ethereum.go @@ -2,10 +2,10 @@ package eth import ( "container/list" - "github.com/ethereum/ethchain-go" - "github.com/ethereum/ethdb-go" - "github.com/ethereum/ethutil-go" - "github.com/ethereum/ethwire-go" + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/eth-go/ethwire" "io/ioutil" "log" "net" @@ -60,8 +60,8 @@ type Ethereum struct { } func New(caps Caps, usePnp bool) (*Ethereum, error) { - db, err := ethdb.NewLDBDatabase() - //db, err := ethdb.NewMemDatabase() + //db, err := ethdb.NewLDBDatabase() + db, err := ethdb.NewMemDatabase() if err != nil { return nil, err } diff --git a/ethutil/.gitignore b/ethutil/.gitignore new file mode 100644 index 000000000..f725d58d1 --- /dev/null +++ b/ethutil/.gitignore @@ -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 + diff --git a/ethutil/.travil.yml b/ethutil/.travil.yml new file mode 100644 index 000000000..69359072d --- /dev/null +++ b/ethutil/.travil.yml @@ -0,0 +1,3 @@ +language: go +go: + - 1.2 diff --git a/ethutil/README.md b/ethutil/README.md new file mode 100644 index 000000000..c98612e1e --- /dev/null +++ b/ethutil/README.md @@ -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) +``` diff --git a/ethutil/big.go b/ethutil/big.go new file mode 100644 index 000000000..979078bef --- /dev/null +++ b/ethutil/big.go @@ -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 +} diff --git a/ethutil/bytes.go b/ethutil/bytes.go new file mode 100644 index 000000000..40903a5f1 --- /dev/null +++ b/ethutil/bytes.go @@ -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) +} diff --git a/ethutil/config.go b/ethutil/config.go new file mode 100644 index 000000000..7782e7daa --- /dev/null +++ b/ethutil/config.go @@ -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...) + } +} diff --git a/ethutil/db.go b/ethutil/db.go new file mode 100644 index 000000000..3681c4b05 --- /dev/null +++ b/ethutil/db.go @@ -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() +} diff --git a/ethutil/encoding.go b/ethutil/encoding.go new file mode 100644 index 000000000..207548c93 --- /dev/null +++ b/ethutil/encoding.go @@ -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 +} diff --git a/ethutil/encoding_test.go b/ethutil/encoding_test.go new file mode 100644 index 000000000..bcabab0b1 --- /dev/null +++ b/ethutil/encoding_test.go @@ -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) + } +} diff --git a/ethutil/helpers.go b/ethutil/helpers.go new file mode 100644 index 000000000..1c6adf256 --- /dev/null +++ b/ethutil/helpers.go @@ -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) +} diff --git a/ethutil/parsing.go b/ethutil/parsing.go new file mode 100644 index 000000000..2c41fb4df --- /dev/null +++ b/ethutil/parsing.go @@ -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 +} diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go new file mode 100644 index 000000000..482eef3ee --- /dev/null +++ b/ethutil/parsing_test.go @@ -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) { +} diff --git a/ethutil/rand.go b/ethutil/rand.go new file mode 100644 index 000000000..91dafec7e --- /dev/null +++ b/ethutil/rand.go @@ -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) +} diff --git a/ethutil/rlp.go b/ethutil/rlp.go new file mode 100644 index 000000000..0f88933b3 --- /dev/null +++ b/ethutil/rlp.go @@ -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() +} diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go new file mode 100644 index 000000000..32bcbdce1 --- /dev/null +++ b/ethutil/rlp_test.go @@ -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) + } +} diff --git a/ethutil/trie.go b/ethutil/trie.go new file mode 100644 index 000000000..44d2d5774 --- /dev/null +++ b/ethutil/trie.go @@ -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 +} diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go new file mode 100644 index 000000000..b87d35e1a --- /dev/null +++ b/ethutil/trie_test.go @@ -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") + } +} diff --git a/ethutil/value.go b/ethutil/value.go new file mode 100644 index 000000000..2a990783e --- /dev/null +++ b/ethutil/value.go @@ -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 +} diff --git a/ethwire/.gitignore b/ethwire/.gitignore new file mode 100644 index 000000000..f725d58d1 --- /dev/null +++ b/ethwire/.gitignore @@ -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 + diff --git a/ethwire/README.md b/ethwire/README.md new file mode 100644 index 000000000..7f63688b3 --- /dev/null +++ b/ethwire/README.md @@ -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{}{...}, +}) +``` diff --git a/ethwire/messaging.go b/ethwire/messaging.go new file mode 100644 index 000000000..651bf4710 --- /dev/null +++ b/ethwire/messaging.go @@ -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 +} @@ -3,9 +3,9 @@ package eth import ( "bytes" "fmt" - "github.com/ethereum/ethchain-go" - "github.com/ethereum/ethutil-go" - "github.com/ethereum/ethwire-go" + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/eth-go/ethwire" "log" "net" "runtime" @@ -293,7 +293,7 @@ func (p *Peer) HandleInbound() { err = p.ethereum.BlockManager.ProcessBlock(block) if err != nil { - log.Println(err) + log.Println("bckmsg", err) break } else { lastBlock = block @@ -306,8 +306,7 @@ func (p *Peer) HandleInbound() { log.Println("Attempting to catch up") p.catchingUp = false p.CatchupWithPeer() - } - if ethchain.IsValidationErr(err) { + } else if ethchain.IsValidationErr(err) { // TODO } } else { |