From 83396cd836f6d963573295b9304be87bf45f5ad6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 16 Jan 2014 15:20:41 +0100 Subject: [PATCH] Moved stack to its own file. Moved contract processing to block manager --- block_manager.go | 207 +++++++++++++++++++++++++++++++++++++++++++++-- stack.go | 164 +++++++++++++++++++++++++++++++++++++ 2 files changed, 366 insertions(+), 5 deletions(-) create mode 100644 stack.go diff --git a/block_manager.go b/block_manager.go index 7e16cdb3a..7b2d01f29 100644 --- a/block_manager.go +++ b/block_manager.go @@ -6,6 +6,7 @@ import ( "github.com/ethereum/ethutil-go" "log" "math/big" + "strconv" ) type BlockChain struct { @@ -40,16 +41,17 @@ func (bc *BlockChain) GenesisBlock() *ethutil.Block { } type BlockManager struct { - // Ethereum virtual machine for processing contracts - vm *Vm // The block chain :) bc *BlockChain + + // Stack for processing contracts + stack *Stack } func NewBlockManager() *BlockManager { bm := &BlockManager{ - vm: NewVm(), - bc: NewBlockChain(), + bc: NewBlockChain(), + stack: NewStack(), } return bm @@ -189,7 +191,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 +203,198 @@ func (bm *BlockManager) ProcessContract(tx *ethutil.Transaction, block *ethutil. // Broadcast we're done lockChan <- true } + +func (bm *BlockManager) ProcContract(tx *ethutil.Transaction, + block *ethutil.Block, cb TxCallback) { + // Instruction pointer + pc := 0 + + 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 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.String()) + 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.String()) + 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.String()) + case oDIV: + x, y := bm.stack.Popn() + // floor(x / y) + base.Div(x, y) + // Pop result back on the stack + bm.stack.Push(base.String()) + 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.String()) + case oMOD: + x, y := bm.stack.Popn() + base.Mod(x, y) + bm.stack.Push(base.String()) + 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.String()) + case oEXP: + x, y := bm.stack.Popn() + base.Exp(x, y, Pow256) + + bm.stack.Push(base.String()) + case oNEG: + base.Sub(Pow256, ethutil.Big(bm.stack.Pop())) + bm.stack.Push(base.String()) + case oLT: + x, y := bm.stack.Popn() + // x < y + if x.Cmp(y) < 0 { + bm.stack.Push("1") + } else { + bm.stack.Push("0") + } + case oLE: + x, y := bm.stack.Popn() + // x <= y + if x.Cmp(y) < 1 { + bm.stack.Push("1") + } else { + bm.stack.Push("0") + } + case oGT: + x, y := bm.stack.Popn() + // x > y + if x.Cmp(y) > 0 { + bm.stack.Push("1") + } else { + bm.stack.Push("0") + } + case oGE: + x, y := bm.stack.Popn() + // x >= y + if x.Cmp(y) > -1 { + bm.stack.Push("1") + } else { + bm.stack.Push("0") + } + case oNOT: + x, y := bm.stack.Popn() + // x != y + if x.Cmp(y) != 0 { + bm.stack.Push("1") + } else { + bm.stack.Push("0") + } + + // 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(string(tx.Hash())) + case oTXSENDER: + bm.stack.Push(string(tx.Sender())) + case oTXVALUE: + bm.stack.Push(tx.Value.String()) + case oTXDATAN: + bm.stack.Push(big.NewInt(int64(len(tx.Data))).String()) + case oTXDATA: + v := ethutil.Big(bm.stack.Pop()) + // v >= len(data) + if v.Cmp(big.NewInt(int64(len(tx.Data)))) >= 0 { + //I know this will change. It makes no + //sense. Read comment above + bm.stack.Push(ethutil.Big("0").String()) + } else { + bm.stack.Push(ethutil.Big(tx.Data[v.Uint64()]).String()) + } + case oBLK_PREVHASH: + bm.stack.Push(string(block.PrevHash)) + case oBLK_COINBASE: + bm.stack.Push(block.Coinbase) + case oBLK_TIMESTAMP: + bm.stack.Push(big.NewInt(block.Time).String()) + case oBLK_NUMBER: + + case oPUSH: + // Get the next entry and pushes the value on the stack + pc++ + bm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(pc), 32)))) + case oPOP: + // Pop current value of the stack + bm.stack.Pop() + case oLOAD: + // Load instruction X on the stack + i, _ := strconv.Atoi(bm.stack.Pop()) + bm.stack.Push(contract.State().Get(string(ethutil.NumberToBytes(uint64(i), 32)))) + case oSTOP: + break out + } + pc++ + } + + bm.stack.Print() +} diff --git a/stack.go b/stack.go new file mode 100644 index 000000000..2b2f075cf --- /dev/null +++ b/stack.go @@ -0,0 +1,164 @@ +package main + +import ( + "fmt" + "github.com/ethereum/ethutil-go" + "math/big" +) + +type OpCode byte + +// Op codes +const ( + oSTOP OpCode = 0x00 + oADD OpCode = 0x01 + oMUL OpCode = 0x02 + oSUB OpCode = 0x03 + oDIV OpCode = 0x04 + oSDIV OpCode = 0x05 + oMOD OpCode = 0x06 + oSMOD OpCode = 0x07 + oEXP OpCode = 0x08 + oNEG OpCode = 0x09 + oLT OpCode = 0x0a + oLE OpCode = 0x0b + oGT OpCode = 0x0c + oGE OpCode = 0x0d + oEQ OpCode = 0x0e + oNOT OpCode = 0x0f + oMYADDRESS OpCode = 0x10 + oTXSENDER OpCode = 0x11 + oTXVALUE OpCode = 0x12 + oTXFEE OpCode = 0x13 + oTXDATAN OpCode = 0x14 + oTXDATA OpCode = 0x15 + oBLK_PREVHASH OpCode = 0x16 + oBLK_COINBASE OpCode = 0x17 + oBLK_TIMESTAMP OpCode = 0x18 + oBLK_NUMBER OpCode = 0x19 + oBLK_DIFFICULTY OpCode = 0x1a + oSHA256 OpCode = 0x20 + oRIPEMD160 OpCode = 0x21 + oECMUL OpCode = 0x22 + oECADD OpCode = 0x23 + oECSIGN OpCode = 0x24 + oECRECOVER OpCode = 0x25 + oECVALID OpCode = 0x26 + oPUSH OpCode = 0x30 + oPOP OpCode = 0x31 + oDUP OpCode = 0x32 + oDUPN OpCode = 0x33 + oSWAP OpCode = 0x34 + oSWAPN OpCode = 0x35 + oLOAD OpCode = 0x36 + oSTORE OpCode = 0x37 + oJMP OpCode = 0x40 + oJMPI OpCode = 0x41 + oIND OpCode = 0x42 + oEXTRO OpCode = 0x50 + oBALANCE OpCode = 0x51 + oMKTX OpCode = 0x60 + oSUICIDE OpCode = 0xff +) + +// Since the opcodes aren't all in order we can't use a regular slice +var opCodeToString = map[OpCode]string{ + oSTOP: "STOP", + oADD: "ADD", + oMUL: "MUL", + oSUB: "SUB", + oDIV: "DIV", + oSDIV: "SDIV", + oMOD: "MOD", + oSMOD: "SMOD", + oEXP: "EXP", + oNEG: "NEG", + oLT: "LT", + oLE: "LE", + oGT: "GT", + oGE: "GE", + oEQ: "EQ", + oNOT: "NOT", + oMYADDRESS: "MYADDRESS", + oTXSENDER: "TXSENDER", + oTXVALUE: "TXVALUE", + oTXFEE: "TXFEE", + oTXDATAN: "TXDATAN", + oTXDATA: "TXDATA", + oBLK_PREVHASH: "BLK_PREVHASH", + oBLK_COINBASE: "BLK_COINBASE", + oBLK_TIMESTAMP: "BLK_TIMESTAMP", + oBLK_NUMBER: "BLK_NUMBER", + oBLK_DIFFICULTY: "BLK_DIFFIFULTY", + oSHA256: "SHA256", + oRIPEMD160: "RIPEMD160", + oECMUL: "ECMUL", + oECADD: "ECADD", + oECSIGN: "ECSIGN", + oECRECOVER: "ECRECOVER", + oECVALID: "ECVALID", + oPUSH: "PUSH", + oPOP: "POP", + oDUP: "DUP", + oDUPN: "DUPN", + oSWAP: "SWAP", + oSWAPN: "SWAPN", + oLOAD: "LOAD", + oSTORE: "STORE", + oJMP: "JMP", + oJMPI: "JMPI", + oIND: "IND", + oEXTRO: "EXTRO", + oBALANCE: "BALANCE", + oMKTX: "MKTX", + oSUICIDE: "SUICIDE", +} + +func (o OpCode) String() string { + return opCodeToString[o] +} + +type OpType int + +const ( + tNorm = iota + tData + tExtro + tCrypto +) + +type TxCallback func(opType OpType) bool + +// Simple push/pop stack mechanism +type Stack struct { + data []string +} + +func NewStack() *Stack { + return &Stack{} +} + +func (st *Stack) Pop() string { + s := len(st.data) + + str := st.data[s-1] + st.data = st.data[:s-1] + + return str +} + +func (st *Stack) Popn() (*big.Int, *big.Int) { + s := len(st.data) + + strs := st.data[s-2:] + st.data = st.data[:s-2] + + return ethutil.Big(strs[0]), ethutil.Big(strs[1]) +} + +func (st *Stack) Push(d string) { + st.data = append(st.data, d) +} +func (st *Stack) Print() { + fmt.Println(st.data) +}