aboutsummaryrefslogblamecommitdiffstats
path: root/block_manager.go
blob: 87af9f29307985dc34b2f60d01152c446021501e (plain) (tree)
1
2
3
4
5
6
7
8
9


            
               
                

                                        
                                          
             
              
                  
                 
              

 
                        
                     
                                
                                                               
                                   
                                      
                   


                                  

                                                                           
 
                                                                                
                                                             
 
                                                
                                      
 
                 

 
                                                  





                                                      

 
                          
                      

                             
 

                                  



                                                  
                               

 
                                               
                            



                                                  
         
 



                                                                        
                 


                   
                                                                  
                           



                                                       





                                                                   




















                                                                                     
                                                                     
                                  
                                                                      


                                       
                  

 
                                                                         


                                                                                                  

                                                                                      




                                                                                 
                          



                 

















                                                                                                              



                                                      






                                                                          

                                                                                  
                                                                   



                                                           
               



                                                                            
                                            








                                                                                                            
                         

                                                                           
                 

         









                                                                                                
                                                                           
                                                                                     
 

                                                             
 

                                              










                                                                            
                                                                       


                                 
                  

 
                                                                                                            









                                                                                                                       
                                                             









                                                                 
 
 

                                                                                                    

                              
                                        





























                                                                                   

                                 





                                                       
                                           





                                                       
                                           





                                                       
                                           




                                                       
                                           














                                                      
                                        


                                               
                                           














                                                      
                                        



                                               
                                           
                          

                                                        



                                               
                                                              
                                
                                                               




                                               
                                                              
                                
                                                               




                                               
                                                              
                                
                                                               




                                               
                                                              
                                
                                                               




                                               
                                                              
                                
                                                               





                                                                       
                                                              
                               
                                                                
                              
                                               
                              
                                                                      
                             
                                           

                                                                        
                                                               
                                
                                                                               

                                   
                                                                  
                                   
                                                                  
                                    
                                                             
                                 
                                                       
                                     
                                                       















                                                                                             
                                        
                                                

                                                
                                                                                         



                                                                                      
                                                                                         



                                                             
                                                                                             

                                                                                           
                                
                                                                                             
                         
                            

                                           
                                             
 
                                                                 
                                                 

                                                    
                                                                              


                                                         

                                                               
                         




                                                         
 



                                
                           
                            
                                                               


                                                         
                          



                                           
                           



                                                   
                            

                                                         
                             

                                               
                            








                                                                                                  
                             


                                                                                      
                          



                                                                                                     
                           





                                                         
                          
                                                            
                            




                                                                                      
                              



                                                                                 
                           












                                                                                            
                              
                                                





                        
















                                                                                              
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()
}