Moved stack to its own file. Moved contract processing to block manager

This commit is contained in:
obscuren 2014-01-16 15:20:41 +01:00
parent a80a3eeb23
commit 83396cd836
2 changed files with 366 additions and 5 deletions

View File

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

164
stack.go Normal file
View File

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