diff options
author | obscuren <geffobscura@gmail.com> | 2014-01-24 03:16:52 +0800 |
---|---|---|
committer | obscuren <geffobscura@gmail.com> | 2014-01-24 03:16:52 +0800 |
commit | 477e8a7a73820ece05bbb990607a1919d3788960 (patch) | |
tree | c13d1d7f709f0934b54ef10377e671446d165db7 /block_manager.go | |
parent | 3616080db46931202003157bacf10748008bebc0 (diff) | |
download | go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.gz go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.bz2 go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.lz go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.xz go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.tar.zst go-tangerine-477e8a7a73820ece05bbb990607a1919d3788960.zip |
Rearrange packages
Diffstat (limited to 'block_manager.go')
-rw-r--r-- | block_manager.go | 591 |
1 files changed, 0 insertions, 591 deletions
diff --git a/block_manager.go b/block_manager.go deleted file mode 100644 index 87af9f293..000000000 --- a/block_manager.go +++ /dev/null @@ -1,591 +0,0 @@ -package main - -import ( - "bytes" - "errors" - "fmt" - "github.com/ethereum/ethutil-go" - "github.com/obscuren/secp256k1-go" - "log" - "math" - "math/big" - "strconv" - "time" -) - -type BlockChain struct { - // Last block - LastBlock *ethutil.Block - // The famous, the fabulous Mister GENESIIIIIIS (block) - genesisBlock *ethutil.Block - // Last known total difficulty - TD *big.Int -} - -func NewBlockChain() *BlockChain { - bc := &BlockChain{} - bc.genesisBlock = ethutil.NewBlock(ethutil.Encode(ethutil.Genesis)) - - // Set the last know difficulty (might be 0x0 as initial value, Genesis) - bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD()) - - // TODO get last block from the database - bc.LastBlock = bc.genesisBlock - - return bc -} - -func (bc *BlockChain) HasBlock(hash string) bool { - data, _ := ethutil.Config.Db.Get([]byte(hash)) - return len(data) != 0 -} - -func (bc *BlockChain) GenesisBlock() *ethutil.Block { - return bc.genesisBlock -} - -type BlockManager struct { - server *Server - // The block chain :) - bc *BlockChain - - // Last known block number - LastBlockNumber *big.Int - - // Stack for processing contracts - stack *Stack - // non-persistent key/value memory storage - mem map[string]*big.Int -} - -func NewBlockManager(s *Server) *BlockManager { - bm := &BlockManager{ - server: s, - bc: NewBlockChain(), - stack: NewStack(), - mem: make(map[string]*big.Int), - } - - // Set the last known block number based on the blockchains last - // block - bm.LastBlockNumber = bm.BlockInfo(bm.bc.LastBlock).Number - - return bm -} - -// Process a block. -func (bm *BlockManager) ProcessBlock(block *ethutil.Block) error { - // 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(block); err != nil { - return err - } - - // Get the tx count. Used to create enough channels to 'join' the go routines - txCount := len(block.Transactions()) - // Locking channel. When it has been fully buffered this method will return - lockChan := make(chan bool, txCount) - - // Process each transaction/contract - for _, tx := range block.Transactions() { - // If there's no recipient, it's a contract - if tx.IsContract() { - go bm.ProcessContract(tx, block, lockChan) - } else { - // "finish" tx which isn't a contract - lockChan <- true - } - } - - // Wait for all Tx to finish processing - for i := 0; i < txCount; i++ { - <-lockChan - } - - // Calculate the new total difficulty and sync back to the db - if bm.CalculateTD(block) { - ethutil.Config.Db.Put(block.Hash(), block.RlpEncode()) - bm.bc.LastBlock = block - } - - return nil -} - -// Unexported method for writing extra non-essential block info to the db -func (bm *BlockManager) writeBlockInfo(block *ethutil.Block) { - bi := ethutil.BlockInfo{Number: bm.LastBlockNumber.Add(bm.LastBlockNumber, big.NewInt(1))} - - // 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 (bm *BlockManager) BlockInfo(block *ethutil.Block) ethutil.BlockInfo { - bi := ethutil.BlockInfo{} - data, _ := ethutil.Config.Db.Get(append(block.Hash(), []byte("Info")...)) - bi.RlpDecode(data) - - return bi -} - -func (bm *BlockManager) CalculateTD(block *ethutil.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 { - bm.bc.LastBlock = block - // Set the new total difficulty back to the block chain - bm.bc.TD = td - - if Debug { - log.Println("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 *ethutil.Block) error { - // Genesis block - if bm.bc.LastBlock == nil && block.PrevHash == "" { - return nil - } - // TODO - // 2. Check if the difficulty is correct - - // 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) { - return errors.New("Block's parent unknown") - } - - // Check each uncle's previous hash. In order for it to be valid - // is if it has the same block hash as the current - for _, uncle := range block.Uncles { - if uncle.PrevHash != block.PrevHash { - if Debug { - log.Printf("Uncle prvhash mismatch %x %x\n", block.PrevHash, uncle.PrevHash) - } - - return errors.New("Mismatching Prvhash from uncle") - } - } - - diff := block.Time - bm.bc.LastBlock.Time - if diff < 0 { - return fmt.Errorf("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 errors.New("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 !DaggerVerify(ethutil.BigD(block.Hash()), block.Difficulty, block.Nonce) { - - return errors.New("Block's nonce is invalid") - } - - log.Println("Block validation PASSED") - - return nil -} - -func (bm *BlockManager) AccumelateRewards(block *ethutil.Block) error { - // Get the coinbase rlp data - d := block.State().Get(block.Coinbase) - - ether := ethutil.NewEtherFromData([]byte(d)) - - // Reward amount of ether to the coinbase address - ether.AddFee(ethutil.CalculateBlockReward(block, len(block.Uncles))) - block.State().Update(block.Coinbase, string(ether.RlpEncode())) - - // TODO Reward each uncle - - return nil -} - -func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil.Block, lockChan chan bool) { - // 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) - // Let the channel know where done even though it failed (so the execution may resume normally) - lockChan <- true - } - }() - - // 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 - }) - - // Broadcast we're done - lockChan <- true -} - -// Contract evaluation is done here. -func (bm *BlockManager) ProcContract(tx *ethutil.Transaction, block *ethutil.Block, cb TxCallback) { - // Instruction pointer - pc := 0 - blockInfo := bm.BlockInfo(block) - - contract := block.GetContract(tx.Hash()) - if contract == nil { - fmt.Println("Contract not found") - return - } - - Pow256 := ethutil.BigPow(2, 256) - - //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 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.Big(block.PrevHash)) - case oBLK_COINBASE: - bm.stack.Push(ethutil.Big(block.Coinbase)) - case oBLK_TIMESTAMP: - bm.stack.Push(big.NewInt(block.Time)) - case oBLK_NUMBER: - bm.stack.Push(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.NewRlpDecoder([]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 := ethutil.NewEtherFromData([]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 := ethutil.NewTransaction(string(addr.Bytes()), value, dataItems) - // Add the transaction to the tx pool - bm.server.txPool.QueueTransaction(tx) - case oSUICIDE: - //addr := bm.stack.Pop() - } - pc++ - } - - bm.stack.Print() -} - -// Returns an address from the specified contract's address -func getContractMemory(block *ethutil.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.NewRlpDecoder([]byte(val)) - if decoder.IsNil() { - return ethutil.BigFalse - } - - return decoder.AsBigInt() -} |