aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ethchain/block.go18
-rw-r--r--ethchain/block_manager.go420
-rw-r--r--ethchain/block_manager_test.go11
-rw-r--r--ethchain/contract.go28
-rw-r--r--ethchain/stack.go103
-rw-r--r--ethchain/state.go56
-rw-r--r--ethchain/transaction.go7
-rw-r--r--ethchain/vm.go436
-rw-r--r--ethchain/vm_test.go106
-rw-r--r--ethdb/database.go4
-rw-r--r--ethdb/memory_database.go6
-rw-r--r--ethutil/db.go1
-rw-r--r--ethutil/parsing.go102
-rw-r--r--ethutil/parsing_test.go4
-rw-r--r--ethutil/trie.go85
-rw-r--r--ethutil/trie_test.go24
-rw-r--r--peer.go4
17 files changed, 880 insertions, 535 deletions
diff --git a/ethchain/block.go b/ethchain/block.go
index 0b4f93e8c..7ad9984be 100644
--- a/ethchain/block.go
+++ b/ethchain/block.go
@@ -220,23 +220,9 @@ func (block *Block) Undo() {
}
func (block *Block) MakeContract(tx *Transaction) {
- // Create contract if there's no recipient
- if tx.IsContract() {
- addr := tx.Hash()[12:]
-
- value := tx.Value
- contract := NewContract(value, []byte(""))
- block.state.Update(string(addr), string(contract.RlpEncode()))
- for i, val := range tx.Data {
- if len(val) > 0 {
- bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256)
- contract.state.Update(string(bytNum), val)
- }
- }
- block.UpdateContract(addr, contract)
+ contract := MakeContract(tx, NewState(block.state))
- block.contractStates[string(addr)] = contract.state
- }
+ block.contractStates[string(tx.Hash()[12:])] = contract.state
}
/////// Block Encoding
diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go
index b82e5a74a..ad96f6a34 100644
--- a/ethchain/block_manager.go
+++ b/ethchain/block_manager.go
@@ -6,11 +6,8 @@ import (
"fmt"
"github.com/ethereum/eth-go/ethutil"
_ "github.com/ethereum/eth-go/ethwire"
- "github.com/obscuren/secp256k1-go"
"log"
- "math"
"math/big"
- "strconv"
"sync"
"time"
)
@@ -113,9 +110,12 @@ func (bm *BlockManager) ApplyTransactions(block *Block, txs []*Transaction) {
// If there's no recipient, it's a contract
if tx.IsContract() {
block.MakeContract(tx)
- bm.ProcessContract(tx, block)
} else {
- bm.TransactionPool.ProcessTransaction(tx, block)
+ if contract := block.GetContract(tx.Recipient); contract != nil {
+ bm.ProcessContract(contract, tx, block)
+ } else {
+ bm.TransactionPool.ProcessTransaction(tx, block)
+ }
}
}
}
@@ -300,7 +300,7 @@ func (bm *BlockManager) Stop() {
bm.bc.Stop()
}
-func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) {
+func (bm *BlockManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) {
// Recovering function in case the VM had any errors
/*
defer func() {
@@ -310,402 +310,16 @@ func (bm *BlockManager) ProcessContract(tx *Transaction, block *Block) {
}()
*/
- // 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
+ vm := &Vm{}
+ vm.Process(contract, NewState(block.state), RuntimeVars{
+ address: tx.Hash()[12:],
+ blockNumber: block.BlockInfo().Number,
+ sender: tx.Sender(),
+ prevHash: block.PrevHash,
+ coinbase: block.Coinbase,
+ time: block.Time,
+ diff: block.Difficulty,
+ txValue: tx.Value,
+ txData: tx.Data,
})
}
-
-// Contract evaluation is done here.
-func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallback) {
- addr := tx.Hash()[12:]
- // Instruction pointer
- pc := 0
- blockInfo := bm.bc.BlockInfo(block)
-
- contract := block.GetContract(addr)
-
- if contract == nil {
- fmt.Println("Contract not found")
- return
- }
-
- Pow256 := ethutil.BigPow(2, 256)
-
- if ethutil.Config.Debug {
- fmt.Printf("# op\n")
- }
-
- stepcount := 0
- totalFee := new(big.Int)
-
- // helper function for getting a contract's memory address
- getMem := func(num int) *ethutil.Value {
- nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256)
- return contract.Addr(nb)
- }
-out:
- for {
- stepcount++
- // The base big int for all calculations. Use this for any results.
- base := new(big.Int)
- val := getMem(pc)
- //fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb)
- op := OpCode(val.Uint())
-
- var fee *big.Int = new(big.Int)
- var fee2 *big.Int = new(big.Int)
- if stepcount > 16 {
- fee.Add(fee, StepFee)
- }
-
- // Calculate the fees
- switch op {
- case oSSTORE:
- y, x := bm.stack.Peekn()
- val := contract.Addr(ethutil.BigToBytes(x, 256))
- if val.IsEmpty() && len(y.Bytes()) > 0 {
- fee2.Add(DataFee, StoreFee)
- } else {
- fee2.Sub(DataFee, StoreFee)
- }
- case oSLOAD:
- fee.Add(fee, StoreFee)
- case oEXTRO, oBALANCE:
- fee.Add(fee, ExtroFee)
- case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID:
- fee.Add(fee, CryptoFee)
- case oMKTX:
- fee.Add(fee, ContractFee)
- }
-
- tf := new(big.Int).Add(fee, fee2)
- if contract.Amount.Cmp(tf) < 0 {
- break
- }
- // Add the fee to the total fee. It's subtracted when we're done looping
- totalFee.Add(totalFee, tf)
-
- if !cb(0) {
- break
- }
-
- if ethutil.Config.Debug {
- fmt.Printf("%-3d %-4s", pc, op.String())
- }
-
- switch op {
- case oSTOP:
- fmt.Println("")
- 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)
- }
- 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.BigD(block.PrevHash))
- case oBLK_COINBASE:
- bm.stack.Push(ethutil.BigD(block.Coinbase))
- case oBLK_TIMESTAMP:
- bm.stack.Push(big.NewInt(block.Time))
- case oBLK_NUMBER:
- bm.stack.Push(big.NewInt(int64(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(getMem(pc).BigInt())
- 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.NewValueFromBytes([]byte(contract.State().Get(x.String())))
- if !decoder.IsNil() {
- bm.stack.Push(decoder.BigInt())
- } else {
- bm.stack.Push(ethutil.BigFalse)
- }
- case oSSTORE:
- // Store Y at index X
- y, x := bm.stack.Popn()
- addr := ethutil.BigToBytes(x, 256)
- fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr))
- contract.SetAddr(addr, y)
- //contract.State().Update(string(idx), string(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 := NewAddressFromData([]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 := NewTransaction(addr.Bytes(), value, dataItems)
- // Add the transaction to the tx pool
- bm.TransactionPool.QueueTransaction(tx)
- case oSUICIDE:
- //addr := bm.stack.Pop()
- default:
- fmt.Println("Invalid OPCODE", op)
- }
- fmt.Println("")
- bm.stack.Print()
- pc++
- }
-
- block.UpdateContract(addr, contract)
-}
-
-// Returns an address from the specified contract's address
-func getContractMemory(block *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.NewValueFromBytes([]byte(val))
- if decoder.IsNil() {
- return ethutil.BigFalse
- }
-
- return decoder.BigInt()
-}
diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go
index 853d459d8..ec4fbe8c5 100644
--- a/ethchain/block_manager_test.go
+++ b/ethchain/block_manager_test.go
@@ -17,12 +17,17 @@ func TestVm(t *testing.T) {
bm := NewBlockManager(nil)
block := bm.bc.genesisBlock
- ctrct := NewTransaction(ContractAddr, big.NewInt(200000000), []string{
+ script := Compile([]string{
"PUSH",
"1",
"PUSH",
"2",
- "STOP",
})
- bm.ApplyTransactions(block, []*Transaction{ctrct})
+ tx := NewTransaction(ContractAddr, big.NewInt(200000000), script)
+ addr := tx.Hash()[12:]
+ bm.ApplyTransactions(block, []*Transaction{tx})
+
+ tx2 := NewTransaction(addr, big.NewInt(1e17), nil)
+ tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
+ bm.ApplyTransactions(block, []*Transaction{tx2})
}
diff --git a/ethchain/contract.go b/ethchain/contract.go
index 68ec39f0b..dbcbb3697 100644
--- a/ethchain/contract.go
+++ b/ethchain/contract.go
@@ -41,3 +41,31 @@ func (c *Contract) SetAddr(addr []byte, value interface{}) {
func (c *Contract) State() *ethutil.Trie {
return c.state
}
+
+func (c *Contract) GetMem(num int) *ethutil.Value {
+ nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256)
+
+ return c.Addr(nb)
+}
+
+func MakeContract(tx *Transaction, state *State) *Contract {
+ // Create contract if there's no recipient
+ if tx.IsContract() {
+ addr := tx.Hash()[12:]
+
+ value := tx.Value
+ contract := NewContract(value, []byte(""))
+ state.trie.Update(string(addr), string(contract.RlpEncode()))
+ for i, val := range tx.Data {
+ if len(val) > 0 {
+ bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256)
+ contract.state.Update(string(bytNum), string(ethutil.Encode(val)))
+ }
+ }
+ state.trie.Update(string(addr), string(contract.RlpEncode()))
+
+ return contract
+ }
+
+ return nil
+}
diff --git a/ethchain/stack.go b/ethchain/stack.go
index e08f84082..13b0f247b 100644
--- a/ethchain/stack.go
+++ b/ethchain/stack.go
@@ -9,57 +9,57 @@ type OpCode int
// Op codes
const (
- oSTOP OpCode = iota
- oADD
- oMUL
- oSUB
- oDIV
- oSDIV
- oMOD
- oSMOD
- oEXP
- oNEG
- oLT
- oLE
- oGT
- oGE
- oEQ
- oNOT
- oMYADDRESS
- oTXSENDER
- oTXVALUE
- oTXFEE
- oTXDATAN
- oTXDATA
- oBLK_PREVHASH
- oBLK_COINBASE
- oBLK_TIMESTAMP
- oBLK_NUMBER
- oBLK_DIFFICULTY
- oBASEFEE
- oSHA256 OpCode = 32
- oRIPEMD160 OpCode = 33
- oECMUL OpCode = 34
- oECADD OpCode = 35
- oECSIGN OpCode = 36
- oECRECOVER OpCode = 37
- oECVALID OpCode = 38
- oSHA3 OpCode = 39
- oPUSH OpCode = 48
- oPOP OpCode = 49
- oDUP OpCode = 50
- oSWAP OpCode = 51
- oMLOAD OpCode = 52
- oMSTORE OpCode = 53
- oSLOAD OpCode = 54
- oSSTORE OpCode = 55
- oJMP OpCode = 56
- oJMPI OpCode = 57
- oIND OpCode = 58
- oEXTRO OpCode = 59
- oBALANCE OpCode = 60
- oMKTX OpCode = 61
- oSUICIDE OpCode = 62
+ oSTOP = 0x00
+ oADD = 0x01
+ oMUL = 0x02
+ oSUB = 0x03
+ oDIV = 0x04
+ oSDIV = 0x05
+ oMOD = 0x06
+ oSMOD = 0x07
+ oEXP = 0x08
+ oNEG = 0x09
+ oLT = 0x0a
+ oLE = 0x0b
+ oGT = 0x0c
+ oGE = 0x0d
+ oEQ = 0x0e
+ oNOT = 0x0f
+ oMYADDRESS = 0x10
+ oTXSENDER = 0x11
+ oTXVALUE = 0x12
+ oTXDATAN = 0x13
+ oTXDATA = 0x14
+ oBLK_PREVHASH = 0x15
+ oBLK_COINBASE = 0x16
+ oBLK_TIMESTAMP = 0x17
+ oBLK_NUMBER = 0x18
+ oBLK_DIFFICULTY = 0x19
+ oBLK_NONCE = 0x1a
+ oBASEFEE = 0x1b
+ oSHA256 = 0x20
+ oRIPEMD160 = 0x21
+ oECMUL = 0x22
+ oECADD = 0x23
+ oECSIGN = 0x24
+ oECRECOVER = 0x25
+ oECVALID = 0x26
+ oSHA3 = 0x27
+ oPUSH = 0x30
+ oPOP = 0x31
+ oDUP = 0x32
+ oSWAP = 0x33
+ oMLOAD = 0x34
+ oMSTORE = 0x35
+ oSLOAD = 0x36
+ oSSTORE = 0x37
+ oJMP = 0x38
+ oJMPI = 0x39
+ oIND = 0x3a
+ oEXTRO = 0x3b
+ oBALANCE = 0x3c
+ oMKTX = 0x3d
+ oSUICIDE = 0x3f
)
// Since the opcodes aren't all in order we can't use a regular slice
@@ -83,7 +83,6 @@ var opCodeToString = map[OpCode]string{
oMYADDRESS: "MYADDRESS",
oTXSENDER: "TXSENDER",
oTXVALUE: "TXVALUE",
- oTXFEE: "TXFEE",
oTXDATAN: "TXDATAN",
oTXDATA: "TXDATA",
oBLK_PREVHASH: "BLK_PREVHASH",
diff --git a/ethchain/state.go b/ethchain/state.go
new file mode 100644
index 000000000..1a18ea1d7
--- /dev/null
+++ b/ethchain/state.go
@@ -0,0 +1,56 @@
+package ethchain
+
+import (
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+)
+
+type State struct {
+ trie *ethutil.Trie
+}
+
+func NewState(trie *ethutil.Trie) *State {
+ return &State{trie: trie}
+}
+
+func (s *State) GetContract(addr []byte) *Contract {
+ data := s.trie.Get(string(addr))
+ if data == "" {
+ return nil
+ }
+
+ contract := &Contract{}
+ contract.RlpDecode([]byte(data))
+
+ return contract
+}
+
+func (s *State) UpdateContract(addr []byte, contract *Contract) {
+ s.trie.Update(string(addr), string(contract.RlpEncode()))
+}
+
+func Compile(code []string) (script []string) {
+ script = make([]string, len(code))
+ for i, val := range code {
+ instr, _ := ethutil.CompileInstr(val)
+
+ script[i] = string(instr)
+ }
+
+ return
+}
+
+func (s *State) GetAccount(addr []byte) (account *Address) {
+ data := s.trie.Get(string(addr))
+ if data == "" {
+ account = NewAddress(big.NewInt(0))
+ } else {
+ account = NewAddressFromData([]byte(data))
+ }
+
+ return
+}
+
+func (s *State) UpdateAccount(addr []byte, account *Address) {
+ s.trie.Update(string(addr), string(account.RlpEncode()))
+}
diff --git a/ethchain/transaction.go b/ethchain/transaction.go
index 2417bbd7d..39b2bda94 100644
--- a/ethchain/transaction.go
+++ b/ethchain/transaction.go
@@ -24,12 +24,7 @@ func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
tx.Nonce = 0
// Serialize the data
- tx.Data = make([]string, len(data))
- for i, val := range data {
- instr, _ := ethutil.CompileInstr(val)
-
- tx.Data[i] = string(instr)
- }
+ tx.Data = data
return &tx
}
diff --git a/ethchain/vm.go b/ethchain/vm.go
new file mode 100644
index 000000000..9abd9cd41
--- /dev/null
+++ b/ethchain/vm.go
@@ -0,0 +1,436 @@
+package ethchain
+
+import (
+ "bytes"
+ "fmt"
+ "github.com/ethereum/eth-go/ethutil"
+ "github.com/obscuren/secp256k1-go"
+ "log"
+ "math"
+ "math/big"
+)
+
+type Vm struct {
+ txPool *TxPool
+ // Stack for processing contracts
+ stack *Stack
+ // non-persistent key/value memory storage
+ mem map[string]*big.Int
+
+ vars RuntimeVars
+}
+
+type RuntimeVars struct {
+ address []byte
+ blockNumber uint64
+ sender []byte
+ prevHash []byte
+ coinbase []byte
+ time int64
+ diff *big.Int
+ txValue *big.Int
+ txData []string
+}
+
+func (vm *Vm) Process(contract *Contract, state *State, vars RuntimeVars) {
+ vm.mem = make(map[string]*big.Int)
+ vm.stack = NewStack()
+
+ addr := vars.address // tx.Hash()[12:]
+ // Instruction pointer
+ pc := 0
+
+ if contract == nil {
+ fmt.Println("Contract not found")
+ return
+ }
+
+ Pow256 := ethutil.BigPow(2, 256)
+
+ if ethutil.Config.Debug {
+ fmt.Printf("# op\n")
+ }
+
+ stepcount := 0
+ totalFee := new(big.Int)
+
+out:
+ for {
+ stepcount++
+ // The base big int for all calculations. Use this for any results.
+ base := new(big.Int)
+ val := contract.GetMem(pc)
+ //fmt.Printf("%x = %d, %v %x\n", r, len(r), v, nb)
+ op := OpCode(val.Uint())
+
+ var fee *big.Int = new(big.Int)
+ var fee2 *big.Int = new(big.Int)
+ if stepcount > 16 {
+ fee.Add(fee, StepFee)
+ }
+
+ // Calculate the fees
+ switch op {
+ case oSSTORE:
+ y, x := vm.stack.Peekn()
+ val := contract.Addr(ethutil.BigToBytes(x, 256))
+ if val.IsEmpty() && len(y.Bytes()) > 0 {
+ fee2.Add(DataFee, StoreFee)
+ } else {
+ fee2.Sub(DataFee, StoreFee)
+ }
+ case oSLOAD:
+ fee.Add(fee, StoreFee)
+ case oEXTRO, oBALANCE:
+ fee.Add(fee, ExtroFee)
+ case oSHA256, oRIPEMD160, oECMUL, oECADD, oECSIGN, oECRECOVER, oECVALID:
+ fee.Add(fee, CryptoFee)
+ case oMKTX:
+ fee.Add(fee, ContractFee)
+ }
+
+ tf := new(big.Int).Add(fee, fee2)
+ if contract.Amount.Cmp(tf) < 0 {
+ fmt.Println("Insufficient fees to continue running the contract", tf, contract.Amount)
+ break
+ }
+ // Add the fee to the total fee. It's subtracted when we're done looping
+ totalFee.Add(totalFee, tf)
+
+ if ethutil.Config.Debug {
+ fmt.Printf("%-3d %-4s", pc, op.String())
+ }
+
+ switch op {
+ case oSTOP:
+ fmt.Println("")
+ break out
+ case oADD:
+ x, y := vm.stack.Popn()
+ // (x + y) % 2 ** 256
+ base.Add(x, y)
+ base.Mod(base, Pow256)
+ // Pop result back on the stack
+ vm.stack.Push(base)
+ case oSUB:
+ x, y := vm.stack.Popn()
+ // (x - y) % 2 ** 256
+ base.Sub(x, y)
+ base.Mod(base, Pow256)
+ // Pop result back on the stack
+ vm.stack.Push(base)
+ case oMUL:
+ x, y := vm.stack.Popn()
+ // (x * y) % 2 ** 256
+ base.Mul(x, y)
+ base.Mod(base, Pow256)
+ // Pop result back on the stack
+ vm.stack.Push(base)
+ case oDIV:
+ x, y := vm.stack.Popn()
+ // floor(x / y)
+ base.Div(x, y)
+ // Pop result back on the stack
+ vm.stack.Push(base)
+ case oSDIV:
+ x, y := vm.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
+ vm.stack.Push(z)
+ case oMOD:
+ x, y := vm.stack.Popn()
+ base.Mod(x, y)
+ vm.stack.Push(base)
+ case oSMOD:
+ x, y := vm.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
+ vm.stack.Push(z)
+ case oEXP:
+ x, y := vm.stack.Popn()
+ base.Exp(x, y, Pow256)
+
+ vm.stack.Push(base)
+ case oNEG:
+ base.Sub(Pow256, vm.stack.Pop())
+ vm.stack.Push(base)
+ case oLT:
+ x, y := vm.stack.Popn()
+ // x < y
+ if x.Cmp(y) < 0 {
+ vm.stack.Push(ethutil.BigTrue)
+ } else {
+ vm.stack.Push(ethutil.BigFalse)
+ }
+ case oLE:
+ x, y := vm.stack.Popn()
+ // x <= y
+ if x.Cmp(y) < 1 {
+ vm.stack.Push(ethutil.BigTrue)
+ } else {
+ vm.stack.Push(ethutil.BigFalse)
+ }
+ case oGT:
+ x, y := vm.stack.Popn()
+ // x > y
+ if x.Cmp(y) > 0 {
+ vm.stack.Push(ethutil.BigTrue)
+ } else {
+ vm.stack.Push(ethutil.BigFalse)
+ }
+ case oGE:
+ x, y := vm.stack.Popn()
+ // x >= y
+ if x.Cmp(y) > -1 {
+ vm.stack.Push(ethutil.BigTrue)
+ } else {
+ vm.stack.Push(ethutil.BigFalse)
+ }
+ case oNOT:
+ x, y := vm.stack.Popn()
+ // x != y
+ if x.Cmp(y) != 0 {
+ vm.stack.Push(ethutil.BigTrue)
+ } else {
+ vm.stack.Push(ethutil.BigFalse)
+ }
+ case oMYADDRESS:
+ vm.stack.Push(ethutil.BigD(addr))
+ case oTXSENDER:
+ vm.stack.Push(ethutil.BigD(vars.sender))
+ case oTXVALUE:
+ vm.stack.Push(vars.txValue)
+ case oTXDATAN:
+ vm.stack.Push(big.NewInt(int64(len(vars.txData))))
+ case oTXDATA:
+ v := vm.stack.Pop()
+ // v >= len(data)
+ if v.Cmp(big.NewInt(int64(len(vars.txData)))) >= 0 {
+ vm.stack.Push(ethutil.Big("0"))
+ } else {
+ vm.stack.Push(ethutil.Big(vars.txData[v.Uint64()]))
+ }
+ case oBLK_PREVHASH:
+ vm.stack.Push(ethutil.BigD(vars.prevHash))
+ case oBLK_COINBASE:
+ vm.stack.Push(ethutil.BigD(vars.coinbase))
+ case oBLK_TIMESTAMP:
+ vm.stack.Push(big.NewInt(vars.time))
+ case oBLK_NUMBER:
+ vm.stack.Push(big.NewInt(int64(vars.blockNumber)))
+ case oBLK_DIFFICULTY:
+ vm.stack.Push(vars.diff)
+ 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(vars.diff)
+ 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))
+ vm.stack.Push(x)
+ case oSHA256, oSHA3, oRIPEMD160:
+ // This is probably save
+ // ceil(pop / 32)
+ length := int(math.Ceil(float64(vm.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(vm.stack.Pop().Bytes(), 256)
+ data.WriteString(string(num))
+ }
+
+ if op == oSHA256 {
+ vm.stack.Push(base.SetBytes(ethutil.Sha256Bin(data.Bytes())))
+ } else if op == oSHA3 {
+ vm.stack.Push(base.SetBytes(ethutil.Sha3Bin(data.Bytes())))
+ } else {
+ vm.stack.Push(base.SetBytes(ethutil.Ripemd160(data.Bytes())))
+ }
+ case oECMUL:
+ y := vm.stack.Pop()
+ x := vm.stack.Pop()
+ //n := vm.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
+ vm.stack.Push(ethutil.Big("0"))
+ vm.stack.Push(ethutil.Big("0"))
+ }
+ //} else {
+ // // Invalid, push infinity
+ // vm.stack.Push("0")
+ // vm.stack.Push("0")
+ //}
+
+ case oECADD:
+ case oECSIGN:
+ case oECRECOVER:
+ case oECVALID:
+ case oPUSH:
+ pc++
+ vm.stack.Push(contract.GetMem(pc).BigInt())
+ case oPOP:
+ // Pop current value of the stack
+ vm.stack.Pop()
+ case oDUP:
+ // Dup top stack
+ x := vm.stack.Pop()
+ vm.stack.Push(x)
+ vm.stack.Push(x)
+ case oSWAP:
+ // Swap two top most values
+ x, y := vm.stack.Popn()
+ vm.stack.Push(y)
+ vm.stack.Push(x)
+ case oMLOAD:
+ x := vm.stack.Pop()
+ vm.stack.Push(vm.mem[x.String()])
+ case oMSTORE:
+ x, y := vm.stack.Popn()
+ vm.mem[x.String()] = y
+ case oSLOAD:
+ // Load the value in storage and push it on the stack
+ x := vm.stack.Pop()
+ // decode the object as a big integer
+ decoder := ethutil.NewValueFromBytes([]byte(contract.State().Get(x.String())))
+ if !decoder.IsNil() {
+ vm.stack.Push(decoder.BigInt())
+ } else {
+ vm.stack.Push(ethutil.BigFalse)
+ }
+ case oSSTORE:
+ // Store Y at index X
+ y, x := vm.stack.Popn()
+ addr := ethutil.BigToBytes(x, 256)
+ fmt.Printf(" => %x (%v) @ %v", y.Bytes(), y, ethutil.BigD(addr))
+ contract.SetAddr(addr, y)
+ //contract.State().Update(string(idx), string(y))
+ case oJMP:
+ x := int(vm.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 := vm.stack.Pop()
+ // Set pc to x if it's non zero
+ if x.Cmp(ethutil.BigFalse) != 0 {
+ pc = int(x.Uint64())
+ pc--
+ }
+ case oIND:
+ vm.stack.Push(big.NewInt(int64(pc)))
+ case oEXTRO:
+ memAddr := vm.stack.Pop()
+ contractAddr := vm.stack.Pop().Bytes()
+
+ // Push the contract's memory on to the stack
+ vm.stack.Push(contractMemory(state, contractAddr, memAddr))
+ case oBALANCE:
+ // Pushes the balance of the popped value on to the stack
+ account := state.GetAccount(vm.stack.Pop().Bytes())
+ vm.stack.Push(account.Amount)
+ case oMKTX:
+ addr, value := vm.stack.Popn()
+ from, length := vm.stack.Popn()
+
+ makeInlineTx(addr.Bytes(), value, from, length, contract, state)
+ case oSUICIDE:
+ recAddr := vm.stack.Pop().Bytes()
+ // Purge all memory
+ deletedMemory := contract.state.NewIterator().Purge()
+ // Add refunds to the pop'ed address
+ refund := new(big.Int).Mul(StoreFee, big.NewInt(int64(deletedMemory)))
+ account := state.GetAccount(recAddr)
+ account.Amount.Add(account.Amount, refund)
+ // Update the refunding address
+ state.UpdateAccount(recAddr, account)
+ // Delete the contract
+ state.trie.Update(string(addr), "")
+
+ fmt.Printf("(%d) => %x\n", deletedMemory, recAddr)
+ break out
+ default:
+ fmt.Printf("Invalid OPCODE: %x\n", op)
+ }
+ fmt.Println("")
+ //vm.stack.Print()
+ pc++
+ }
+
+ state.UpdateContract(addr, contract)
+}
+
+func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) {
+ fmt.Printf(" => creating inline tx %x %v %v %v", addr, value, from, length)
+ j := 0
+ dataItems := make([]string, int(length.Uint64()))
+ for i := from.Uint64(); i < length.Uint64(); i++ {
+ dataItems[j] = contract.GetMem(j).Str()
+ j++
+ }
+
+ tx := NewTransaction(addr, value, dataItems)
+ if tx.IsContract() {
+ contract := MakeContract(tx, state)
+ state.UpdateContract(tx.Hash()[12:], contract)
+ } else {
+ account := state.GetAccount(tx.Recipient)
+ account.Amount.Add(account.Amount, tx.Value)
+ state.UpdateAccount(tx.Recipient, account)
+ }
+}
+
+// Returns an address from the specified contract's address
+func contractMemory(state *State, contractAddr []byte, memAddr *big.Int) *big.Int {
+ contract := state.GetContract(contractAddr)
+ if contract == nil {
+ log.Panicf("invalid contract addr %x", contractAddr)
+ }
+ val := state.trie.Get(memAddr.String())
+
+ // decode the object as a big integer
+ decoder := ethutil.NewValueFromBytes([]byte(val))
+ if decoder.IsNil() {
+ return ethutil.BigFalse
+ }
+
+ return decoder.BigInt()
+}
diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go
new file mode 100644
index 000000000..6ceb0ff15
--- /dev/null
+++ b/ethchain/vm_test.go
@@ -0,0 +1,106 @@
+package ethchain
+
+import (
+ "fmt"
+ "github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+ "testing"
+)
+
+func TestRun(t *testing.T) {
+ InitFees()
+
+ ethutil.ReadConfig("")
+
+ db, _ := ethdb.NewMemDatabase()
+ state := NewState(ethutil.NewTrie(db, ""))
+
+ script := Compile([]string{
+ "TXSENDER",
+ "SUICIDE",
+ })
+
+ tx := NewTransaction(ContractAddr, big.NewInt(1e17), script)
+ fmt.Printf("contract addr %x\n", tx.Hash()[12:])
+ contract := MakeContract(tx, state)
+ vm := &Vm{}
+
+ vm.Process(contract, state, RuntimeVars{
+ address: tx.Hash()[12:],
+ blockNumber: 1,
+ sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
+ prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
+ coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
+ time: 1,
+ diff: big.NewInt(256),
+ txValue: tx.Value,
+ txData: tx.Data,
+ })
+}
+
+func TestRun1(t *testing.T) {
+ ethutil.ReadConfig("")
+
+ db, _ := ethdb.NewMemDatabase()
+ state := NewState(ethutil.NewTrie(db, ""))
+
+ script := Compile([]string{
+ "PUSH", "0",
+ "PUSH", "0",
+ "TXSENDER",
+ "PUSH", "10000000",
+ "MKTX",
+ })
+ fmt.Println(ethutil.NewValue(script))
+
+ tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
+ fmt.Printf("contract addr %x\n", tx.Hash()[12:])
+ contract := MakeContract(tx, state)
+ vm := &Vm{}
+
+ vm.Process(contract, state, RuntimeVars{
+ address: tx.Hash()[12:],
+ blockNumber: 1,
+ sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
+ prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
+ coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
+ time: 1,
+ diff: big.NewInt(256),
+ txValue: tx.Value,
+ txData: tx.Data,
+ })
+}
+
+func TestRun2(t *testing.T) {
+ ethutil.ReadConfig("")
+
+ db, _ := ethdb.NewMemDatabase()
+ state := NewState(ethutil.NewTrie(db, ""))
+
+ script := Compile([]string{
+ "PUSH", "0",
+ "PUSH", "0",
+ "TXSENDER",
+ "PUSH", "10000000",
+ "MKTX",
+ })
+ fmt.Println(ethutil.NewValue(script))
+
+ tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script)
+ fmt.Printf("contract addr %x\n", tx.Hash()[12:])
+ contract := MakeContract(tx, state)
+ vm := &Vm{}
+
+ vm.Process(contract, state, RuntimeVars{
+ address: tx.Hash()[12:],
+ blockNumber: 1,
+ sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"),
+ prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"),
+ coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"),
+ time: 1,
+ diff: big.NewInt(256),
+ txValue: tx.Value,
+ txData: tx.Data,
+ })
+}
diff --git a/ethdb/database.go b/ethdb/database.go
index 76e4b4e4d..1e987920d 100644
--- a/ethdb/database.go
+++ b/ethdb/database.go
@@ -36,6 +36,10 @@ func (db *LDBDatabase) Get(key []byte) ([]byte, error) {
return db.db.Get(key, nil)
}
+func (db *LDBDatabase) Delete(key []byte) error {
+ return db.db.Delete(key, nil)
+}
+
func (db *LDBDatabase) LastKnownTD() []byte {
data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil)
diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go
index cd9f24000..9e91eb7d6 100644
--- a/ethdb/memory_database.go
+++ b/ethdb/memory_database.go
@@ -26,6 +26,12 @@ func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return db.db[string(key)], nil
}
+func (db *MemDatabase) Delete(key []byte) error {
+ delete(db.db, string(key))
+
+ return nil
+}
+
func (db *MemDatabase) Print() {
for key, val := range db.db {
fmt.Printf("%x(%d): ", key, len(key))
diff --git a/ethutil/db.go b/ethutil/db.go
index 3681c4b05..b11d5d726 100644
--- a/ethutil/db.go
+++ b/ethutil/db.go
@@ -4,6 +4,7 @@ package ethutil
type Database interface {
Put(key []byte, value []byte)
Get(key []byte) ([]byte, error)
+ Delete(key []byte) error
LastKnownTD() []byte
Close()
Print()
diff --git a/ethutil/parsing.go b/ethutil/parsing.go
index b43dac064..553bb9717 100644
--- a/ethutil/parsing.go
+++ b/ethutil/parsing.go
@@ -7,57 +7,57 @@ import (
// Op codes
var OpCodes = map[string]byte{
- "STOP": 0,
- "ADD": 1,
- "MUL": 2,
- "SUB": 3,
- "DIV": 4,
- "SDIV": 5,
- "MOD": 6,
- "SMOD": 7,
- "EXP": 8,
- "NEG": 9,
- "LT": 10,
- "LE": 11,
- "GT": 12,
- "GE": 13,
- "EQ": 14,
- "NOT": 15,
- "MYADDRESS": 16,
- "TXSENDER": 17,
- "TXVALUE": 18,
- "TXFEE": 19,
- "TXDATAN": 20,
- "TXDATA": 21,
- "BLK_PREVHASH": 22,
- "BLK_COINBASE": 23,
- "BLK_TIMESTAMP": 24,
- "BLK_NUMBER": 25,
- "BLK_DIFFICULTY": 26,
- "BASEFEE": 27,
- "SHA256": 32,
- "RIPEMD160": 33,
- "ECMUL": 34,
- "ECADD": 35,
- "ECSIGN": 36,
- "ECRECOVER": 37,
- "ECVALID": 38,
- "SHA3": 39,
- "PUSH": 48,
- "POP": 49,
- "DUP": 50,
- "SWAP": 51,
- "MLOAD": 52,
- "MSTORE": 53,
- "SLOAD": 54,
- "SSTORE": 55,
- "JMP": 56,
- "JMPI": 57,
- "IND": 58,
- "EXTRO": 59,
- "BALANCE": 60,
- "MKTX": 61,
- "SUICIDE": 62,
+ "STOP": 0x00,
+ "ADD": 0x01,
+ "MUL": 0x02,
+ "SUB": 0x03,
+ "DIV": 0x04,
+ "SDIV": 0x05,
+ "MOD": 0x06,
+ "SMOD": 0x07,
+ "EXP": 0x08,
+ "NEG": 0x09,
+ "LT": 0x0a,
+ "LE": 0x0b,
+ "GT": 0x0c,
+ "GE": 0x0d,
+ "EQ": 0x0e,
+ "NOT": 0x0f,
+ "MYADDRESS": 0x10,
+ "TXSENDER": 0x11,
+ "TXVALUE": 0x12,
+ "TXDATAN": 0x13,
+ "TXDATA": 0x14,
+ "BLK_PREVHASH": 0x15,
+ "BLK_COINBASE": 0x16,
+ "BLK_TIMESTAMP": 0x17,
+ "BLK_NUMBER": 0x18,
+ "BLK_DIFFICULTY": 0x19,
+ "BLK_NONCE": 0x1a,
+ "BASEFEE": 0x1b,
+ "SHA256": 0x20,
+ "RIPEMD160": 0x21,
+ "ECMUL": 0x22,
+ "ECADD": 0x23,
+ "ECSIGN": 0x24,
+ "ECRECOVER": 0x25,
+ "ECVALID": 0x26,
+ "SHA3": 0x27,
+ "PUSH": 0x30,
+ "POP": 0x31,
+ "DUP": 0x32,
+ "SWAP": 0x33,
+ "MLOAD": 0x34,
+ "MSTORE": 0x35,
+ "SLOAD": 0x36,
+ "SSTORE": 0x37,
+ "JMP": 0x38,
+ "JMPI": 0x39,
+ "IND": 0x3a,
+ "EXTRO": 0x3b,
+ "BALANCE": 0x3c,
+ "MKTX": 0x3d,
+ "SUICIDE": 0x3f,
}
func IsOpCode(s string) bool {
diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go
index 69a5e9016..6b59777e6 100644
--- a/ethutil/parsing_test.go
+++ b/ethutil/parsing_test.go
@@ -1,5 +1,6 @@
package ethutil
+/*
import (
"math"
"testing"
@@ -19,14 +20,13 @@ func TestCompile(t *testing.T) {
}
func TestValidInstr(t *testing.T) {
- /*
op, args, err := Instr("68163")
if err != nil {
t.Error("Error decoding instruction")
}
- */
}
func TestInvalidInstr(t *testing.T) {
}
+*/
diff --git a/ethutil/trie.go b/ethutil/trie.go
index 322f77647..83527d364 100644
--- a/ethutil/trie.go
+++ b/ethutil/trie.go
@@ -70,6 +70,12 @@ func (cache *Cache) Get(key []byte) *Value {
return value
}
+func (cache *Cache) Delete(key []byte) {
+ delete(cache.nodes, string(key))
+
+ cache.db.Delete(key)
+}
+
func (cache *Cache) Commit() {
// Don't try to commit if it isn't dirty
if !cache.IsDirty {
@@ -413,3 +419,82 @@ func (t *Trie) Copy() *Trie {
return trie
}
+
+type TrieIterator struct {
+ trie *Trie
+ key string
+ value string
+
+ shas [][]byte
+ values []string
+}
+
+func (t *Trie) NewIterator() *TrieIterator {
+ return &TrieIterator{trie: t}
+}
+
+// Some time in the near future this will need refactoring :-)
+// XXX Note to self, IsSlice == inline node. Str == sha3 to node
+func (it *TrieIterator) workNode(currentNode *Value) {
+ if currentNode.Len() == 2 {
+ k := CompactDecode(currentNode.Get(0).Str())
+
+ if currentNode.Get(1).IsSlice() {
+ it.workNode(currentNode.Get(1))
+ } else {
+ if k[len(k)-1] == 16 {
+ it.values = append(it.values, currentNode.Get(1).Str())
+ } else {
+ it.shas = append(it.shas, currentNode.Get(1).Bytes())
+ it.getNode(currentNode.Get(1).Bytes())
+ }
+ }
+ } else {
+ for i := 0; i < currentNode.Len(); i++ {
+ if i == 16 && currentNode.Get(i).Len() != 0 {
+ it.values = append(it.values, currentNode.Get(i).Str())
+ } else {
+ if currentNode.Get(i).IsSlice() {
+ it.workNode(currentNode.Get(i))
+ } else {
+ val := currentNode.Get(i).Str()
+ if val != "" {
+ it.shas = append(it.shas, currentNode.Get(1).Bytes())
+ it.getNode([]byte(val))
+ }
+ }
+ }
+ }
+ }
+}
+
+func (it *TrieIterator) getNode(node []byte) {
+ currentNode := it.trie.cache.Get(node)
+ it.workNode(currentNode)
+}
+
+func (it *TrieIterator) Collect() [][]byte {
+ if it.trie.Root == "" {
+ return nil
+ }
+
+ it.getNode(NewValue(it.trie.Root).Bytes())
+
+ return it.shas
+}
+
+func (it *TrieIterator) Purge() int {
+ shas := it.Collect()
+ for _, sha := range shas {
+ it.trie.cache.Delete(sha)
+ }
+ return len(it.values)
+}
+
+func (it *TrieIterator) Key() string {
+ return ""
+}
+
+func (it *TrieIterator) Value() string {
+ return ""
+}
diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go
index 9d2c8e19f..c3a8f224d 100644
--- a/ethutil/trie_test.go
+++ b/ethutil/trie_test.go
@@ -1,6 +1,7 @@
package ethutil
import (
+ "fmt"
"reflect"
"testing"
)
@@ -21,6 +22,10 @@ func (db *MemDatabase) Put(key []byte, value []byte) {
func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return db.db[string(key)], nil
}
+func (db *MemDatabase) Delete(key []byte) error {
+ delete(db.db, string(key))
+ return nil
+}
func (db *MemDatabase) Print() {}
func (db *MemDatabase) Close() {}
func (db *MemDatabase) LastKnownTD() []byte { return nil }
@@ -148,3 +153,22 @@ func TestTrieDeleteWithValue(t *testing.T) {
}
}
+
+func TestTrieIterator(t *testing.T) {
+ _, trie := New()
+ trie.Update("c", LONG_WORD)
+ trie.Update("ca", LONG_WORD)
+ trie.Update("cat", LONG_WORD)
+
+ it := trie.NewIterator()
+ fmt.Println("purging")
+ fmt.Println("len =", it.Purge())
+ /*
+ for it.Next() {
+ k := it.Key()
+ v := it.Value()
+
+ fmt.Println(k, v)
+ }
+ */
+}
diff --git a/peer.go b/peer.go
index 9538e6500..a8708206f 100644
--- a/peer.go
+++ b/peer.go
@@ -390,7 +390,7 @@ func (p *Peer) HandleInbound() {
p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
}
case ethwire.MsgNotInChainTy:
- ethutil.Config.Log.Infoln("Not in chain %x\n", msg.Data)
+ ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data)
// TODO
// Unofficial but fun nonetheless
@@ -556,7 +556,7 @@ func (p *Peer) CatchupWithPeer() {
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash(), uint64(50)})
p.QueueMessage(msg)
- ethutil.Config.Log.Debugln("Requesting blockchain %x...\n", p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash()[:4])
+ ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash()[:4])
}
}