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 /ethchain | |
parent | c2fb9f06ad018d01ce335c82b3542de16045a32d (diff) | |
download | dexon-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar dexon-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.gz dexon-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.bz2 dexon-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.lz dexon-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.xz dexon-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.tar.zst dexon-f6d1bfe45bf3709d7bad40bf563b5c09228622e3.zip |
The great merge
Diffstat (limited to 'ethchain')
-rw-r--r-- | ethchain/.gitignore | 12 | ||||
-rw-r--r-- | ethchain/block.go | 363 | ||||
-rw-r--r-- | ethchain/block_chain.go | 184 | ||||
-rw-r--r-- | ethchain/block_manager.go | 627 | ||||
-rw-r--r-- | ethchain/block_manager_test.go | 75 | ||||
-rw-r--r-- | ethchain/contract.go | 66 | ||||
-rw-r--r-- | ethchain/dagger.go | 199 | ||||
-rw-r--r-- | ethchain/dagger_test.go | 18 | ||||
-rw-r--r-- | ethchain/error.go | 42 | ||||
-rw-r--r-- | ethchain/fees.go | 65 | ||||
-rw-r--r-- | ethchain/genesis.go | 39 | ||||
-rw-r--r-- | ethchain/stack.go | 167 | ||||
-rw-r--r-- | ethchain/transaction.go | 157 | ||||
-rw-r--r-- | ethchain/transaction_pool.go | 219 | ||||
-rw-r--r-- | ethchain/transaction_test.go | 54 |
15 files changed, 2287 insertions, 0 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()) +} |