diff options
Diffstat (limited to 'ethchain/block_manager.go')
-rw-r--r-- | ethchain/block_manager.go | 627 |
1 files changed, 627 insertions, 0 deletions
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() +} |