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.Add(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() // Defer the Undo on the Trie. If the block processing happened // we don't want to undo but since undo only happens on dirty // nodes this won't happen because Commit would have been called // before that. defer bm.bc.CurrentBlock.State().Undo() 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.NewValueFromBytes([]byte(contract.State().Get(x.String()))) if !decoder.IsNil() { bm.stack.Push(decoder.BigInt()) } 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.NewValueFromBytes([]byte(val)) if decoder.IsNil() { return ethutil.BigFalse } return decoder.BigInt() }