aboutsummaryrefslogtreecommitdiffstats
path: root/block_manager.go
diff options
context:
space:
mode:
Diffstat (limited to 'block_manager.go')
-rw-r--r--block_manager.go382
1 files changed, 370 insertions, 12 deletions
diff --git a/block_manager.go b/block_manager.go
index 7e16cdb3a..9322d0d3a 100644
--- a/block_manager.go
+++ b/block_manager.go
@@ -1,18 +1,23 @@
package main
import (
+ "bytes"
"errors"
"fmt"
"github.com/ethereum/ethutil-go"
+ "github.com/obscuren/secp256k1-go"
"log"
+ "math"
"math/big"
+ "strconv"
)
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
}
@@ -21,11 +26,10 @@ func NewBlockChain() *BlockChain {
bc.genesisBlock = ethutil.NewBlock(ethutil.Encode(ethutil.Genesis))
// Set the last know difficulty (might be 0x0 as initial value, Genesis)
- bc.TD = new(big.Int)
- bc.TD.SetBytes(ethutil.Config.Db.LastKnownTD())
+ bc.TD = ethutil.BigD(ethutil.Config.Db.LastKnownTD())
// TODO get last block from the database
- //bc.LastBlock = bc.genesisBlock
+ bc.LastBlock = bc.genesisBlock
return bc
}
@@ -40,18 +44,29 @@ func (bc *BlockChain) GenesisBlock() *ethutil.Block {
}
type BlockManager struct {
- // Ethereum virtual machine for processing contracts
- vm *Vm
// 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() *BlockManager {
bm := &BlockManager{
- vm: NewVm(),
- bc: NewBlockChain(),
+ 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
}
@@ -89,14 +104,31 @@ func (bm *BlockManager) ProcessBlock(block *ethutil.Block) error {
<-lockChan
}
+ // Calculate the new total difficulty and sync back to the db
if bm.CalculateTD(block) {
- ethutil.Config.Db.Put(block.Hash(), block.MarshalRlp())
+ 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 {
@@ -171,7 +203,7 @@ func (bm *BlockManager) AccumelateRewards(block *ethutil.Block) error {
// Reward amount of ether to the coinbase address
ether.AddFee(ethutil.CalculateBlockReward(block, len(block.Uncles)))
- block.State().Update(block.Coinbase, string(ether.MarshalRlp()))
+ block.State().Update(block.Coinbase, string(ether.RlpEncode()))
// TODO Reward each uncle
@@ -189,7 +221,7 @@ func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil.
}()
// Process contract
- bm.vm.ProcContract(tx, block, func(opType OpType) bool {
+ 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
@@ -201,3 +233,329 @@ func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil.
// 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, 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 {
+ 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 oSHA3:
+ 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:
+ case oSUICIDE:
+ }
+ 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()
+}