aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorobscuren <geffobscura@gmail.com>2014-02-28 19:21:12 +0800
committerobscuren <geffobscura@gmail.com>2014-02-28 19:21:12 +0800
commit839bd73fbb525f6c51e4205ce6519b6154cda2f0 (patch)
tree8bbb80d53425cfdb0391798eea77ff3af09ffe78
parent30ee32a7254658878b35c6a89e463427f99bd1b4 (diff)
parent3f7ec1a83fe13dc934d92a405ff01b0be6c04ac0 (diff)
downloadgo-tangerine-839bd73fbb525f6c51e4205ce6519b6154cda2f0.tar
go-tangerine-839bd73fbb525f6c51e4205ce6519b6154cda2f0.tar.gz
go-tangerine-839bd73fbb525f6c51e4205ce6519b6154cda2f0.tar.bz2
go-tangerine-839bd73fbb525f6c51e4205ce6519b6154cda2f0.tar.lz
go-tangerine-839bd73fbb525f6c51e4205ce6519b6154cda2f0.tar.xz
go-tangerine-839bd73fbb525f6c51e4205ce6519b6154cda2f0.tar.zst
go-tangerine-839bd73fbb525f6c51e4205ce6519b6154cda2f0.zip
Merge branch 'release/0.3.0'
-rw-r--r--LICENSE2
-rw-r--r--ethchain/address.go60
-rw-r--r--ethchain/address_test.go8
-rw-r--r--ethchain/block.go75
-rw-r--r--ethchain/block_chain.go28
-rw-r--r--ethchain/block_manager.go497
-rw-r--r--ethchain/block_manager_test.go84
-rw-r--r--ethchain/contract.go53
-rw-r--r--ethchain/fees.go24
-rw-r--r--ethchain/genesis.go4
-rw-r--r--ethchain/stack.go129
-rw-r--r--ethchain/state.go56
-rw-r--r--ethchain/transaction.go20
-rw-r--r--ethchain/transaction_pool.go67
-rw-r--r--ethchain/transaction_test.go16
-rw-r--r--ethchain/vm.go436
-rw-r--r--ethchain/vm_test.go106
-rw-r--r--ethdb/database.go20
-rw-r--r--ethdb/memory_database.go12
-rw-r--r--ethereum.go68
-rw-r--r--ethutil/README.md4
-rw-r--r--ethutil/big.go15
-rw-r--r--ethutil/common.go35
-rw-r--r--ethutil/common_test.go17
-rw-r--r--ethutil/config.go78
-rw-r--r--ethutil/db.go2
-rw-r--r--ethutil/encoding.go3
-rw-r--r--ethutil/encoding_test.go30
-rw-r--r--ethutil/helpers.go5
-rw-r--r--ethutil/key.go19
-rw-r--r--ethutil/parsing.go141
-rw-r--r--ethutil/parsing_test.go6
-rw-r--r--ethutil/rlp.go8
-rw-r--r--ethutil/rlp_test.go49
-rw-r--r--ethutil/trie.go212
-rw-r--r--ethutil/trie_test.go142
-rw-r--r--ethutil/value.go34
-rw-r--r--ethutil/value_test.go52
-rw-r--r--ethwire/messaging.go3
-rw-r--r--peer.go46
40 files changed, 1821 insertions, 845 deletions
diff --git a/LICENSE b/LICENSE
index 803f2ef4e..b77f7909a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (c) 2013 Geff Obscura
+Copyright (c) 2013 Jeffrey Wilcke
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/ethchain/address.go b/ethchain/address.go
new file mode 100644
index 000000000..a228c7566
--- /dev/null
+++ b/ethchain/address.go
@@ -0,0 +1,60 @@
+package ethchain
+
+import (
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
+)
+
+type Address struct {
+ Amount *big.Int
+ Nonce uint64
+}
+
+func NewAddress(amount *big.Int) *Address {
+ return &Address{Amount: amount, Nonce: 0}
+}
+
+func NewAddressFromData(data []byte) *Address {
+ address := &Address{}
+ address.RlpDecode(data)
+
+ return address
+}
+
+func (a *Address) AddFee(fee *big.Int) {
+ a.Amount.Add(a.Amount, fee)
+}
+
+func (a *Address) RlpEncode() []byte {
+ return ethutil.Encode([]interface{}{a.Amount, a.Nonce})
+}
+
+func (a *Address) RlpDecode(data []byte) {
+ decoder := ethutil.NewValueFromBytes(data)
+
+ a.Amount = decoder.Get(0).BigInt()
+ a.Nonce = decoder.Get(1).Uint()
+}
+
+type AddrStateStore struct {
+ states map[string]*AddressState
+}
+
+func NewAddrStateStore() *AddrStateStore {
+ return &AddrStateStore{states: make(map[string]*AddressState)}
+}
+
+func (s *AddrStateStore) Add(addr []byte, account *Address) *AddressState {
+ state := &AddressState{Nonce: account.Nonce, Account: account}
+ s.states[string(addr)] = state
+ return state
+}
+
+func (s *AddrStateStore) Get(addr []byte) *AddressState {
+ return s.states[string(addr)]
+}
+
+type AddressState struct {
+ Nonce uint64
+ Account *Address
+}
diff --git a/ethchain/address_test.go b/ethchain/address_test.go
new file mode 100644
index 000000000..161e1b251
--- /dev/null
+++ b/ethchain/address_test.go
@@ -0,0 +1,8 @@
+package ethchain
+
+import (
+ "testing"
+)
+
+func TestAddressState(t *testing.T) {
+}
diff --git a/ethchain/block.go b/ethchain/block.go
index 0678f64e2..7ca44a47d 100644
--- a/ethchain/block.go
+++ b/ethchain/block.go
@@ -46,6 +46,8 @@ type Block struct {
// List of transactions and/or contracts
transactions []*Transaction
TxSha []byte
+
+ contractStates map[string]*ethutil.Trie
}
// New block takes a raw encoded string
@@ -79,14 +81,15 @@ func CreateBlock(root interface{},
block := &Block{
// Slice of transactions to include in this block
- transactions: txes,
- PrevHash: prevHash,
- Coinbase: base,
- Difficulty: Difficulty,
- Nonce: Nonce,
- Time: time.Now().Unix(),
- Extra: extra,
- UncleSha: EmptyShaList,
+ transactions: txes,
+ PrevHash: prevHash,
+ Coinbase: base,
+ Difficulty: Difficulty,
+ Nonce: Nonce,
+ Time: time.Now().Unix(),
+ Extra: extra,
+ UncleSha: EmptyShaList,
+ contractStates: make(map[string]*ethutil.Trie),
}
block.SetTransactions(txes)
block.SetUncles([]*Block{})
@@ -128,14 +131,26 @@ func (block *Block) GetContract(addr []byte) *Contract {
return nil
}
+ value := ethutil.NewValueFromBytes([]byte(data))
+ if value.Len() == 2 {
+ return nil
+ }
+
contract := &Contract{}
contract.RlpDecode([]byte(data))
+ cachedState := block.contractStates[string(addr)]
+ if cachedState != nil {
+ contract.state = cachedState
+ } else {
+ block.contractStates[string(addr)] = contract.state
+ }
+
return contract
}
func (block *Block) UpdateContract(addr []byte, contract *Contract) {
// Make sure the state is synced
- contract.State().Sync()
+ //contract.State().Sync()
block.state.Update(string(addr), string(contract.RlpEncode()))
}
@@ -190,18 +205,29 @@ func (block *Block) BlockInfo() BlockInfo {
return bi
}
+// Sync the block's state and contract respectively
+func (block *Block) Sync() {
+ // Sync all contracts currently in cache
+ for _, val := range block.contractStates {
+ val.Sync()
+ }
+ // Sync the block state itself
+ block.state.Sync()
+}
+
+func (block *Block) Undo() {
+ // Sync all contracts currently in cache
+ for _, val := range block.contractStates {
+ val.Undo()
+ }
+ // Sync the block state itself
+ block.state.Undo()
+}
+
func (block *Block) MakeContract(tx *Transaction) {
- // Create contract if there's no recipient
- if tx.IsContract() {
- addr := tx.Hash()
-
- value := tx.Value
- contract := NewContract(value, []byte(""))
- block.state.Update(string(addr), string(contract.RlpEncode()))
- for i, val := range tx.Data {
- contract.state.Update(string(ethutil.NumberToBytes(uint64(i), 32)), val)
- }
- block.UpdateContract(addr, contract)
+ contract := MakeContract(tx, NewState(block.state))
+ if contract != nil {
+ block.contractStates[string(tx.Hash()[12:])] = contract.state
}
}
@@ -288,6 +314,7 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
block.Time = int64(header.Get(6).BigInt().Uint64())
block.Extra = header.Get(7).Str()
block.Nonce = header.Get(8).Bytes()
+ block.contractStates = make(map[string]*ethutil.Trie)
// Tx list might be empty if this is an uncle. Uncles only have their
// header set.
@@ -298,12 +325,6 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
tx := NewTransactionFromValue(txes.Get(i))
block.transactions[i] = tx
-
- /*
- if ethutil.Config.Debug {
- ethutil.Config.Db.Put(tx.Hash(), ethutil.Encode(tx))
- }
- */
}
}
@@ -335,7 +356,7 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block {
}
func (block *Block) String() string {
- return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce)
+ return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x\nTxs:%d\n", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce, len(block.transactions))
}
//////////// UNEXPORTED /////////////////
diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go
index 54f48bc60..96d22366d 100644
--- a/ethchain/block_chain.go
+++ b/ethchain/block_chain.go
@@ -104,7 +104,6 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
currentHash = block.PrevHash
chain = append(chain, block.Value().Val)
- //chain = append([]interface{}{block.RlpValue().Value}, chain...)
num--
}
@@ -112,6 +111,24 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
return chain
}
+func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block {
+ genHash := bc.genesisBlock.Hash()
+
+ block := bc.GetBlock(hash)
+ var blocks []*Block
+
+ for i := 0; i < amount && block != nil; block = bc.GetBlock(block.PrevHash) {
+ blocks = append([]*Block{block}, blocks...)
+
+ if bytes.Compare(genHash, block.Hash()) == 0 {
+ break
+ }
+ i++
+ }
+
+ return blocks
+}
+
func (bc *BlockChain) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 {
@@ -141,11 +158,16 @@ func (bc *BlockChain) Add(block *Block) {
bc.CurrentBlock = block
bc.LastBlockHash = block.Hash()
- ethutil.Config.Db.Put(block.Hash(), block.RlpEncode())
+ encodedBlock := block.RlpEncode()
+ ethutil.Config.Db.Put(block.Hash(), encodedBlock)
+ ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock)
}
func (bc *BlockChain) GetBlock(hash []byte) *Block {
data, _ := ethutil.Config.Db.Get(hash)
+ if len(data) == 0 {
+ return nil
+ }
return NewBlockFromData(data)
}
@@ -177,8 +199,6 @@ func (bc *BlockChain) writeBlockInfo(block *Block) {
func (bc *BlockChain) Stop() {
if bc.CurrentBlock != nil {
- ethutil.Config.Db.Put([]byte("LastBlock"), bc.CurrentBlock.RlpEncode())
-
log.Println("[CHAIN] Stopped")
}
}
diff --git a/ethchain/block_manager.go b/ethchain/block_manager.go
index 8b237a29a..364a06158 100644
--- a/ethchain/block_manager.go
+++ b/ethchain/block_manager.go
@@ -5,11 +5,9 @@ import (
"encoding/hex"
"fmt"
"github.com/ethereum/eth-go/ethutil"
- "github.com/obscuren/secp256k1-go"
+ _ "github.com/ethereum/eth-go/ethwire"
"log"
- "math"
"math/big"
- "strconv"
"sync"
"time"
)
@@ -18,10 +16,7 @@ type BlockProcessor interface {
ProcessBlock(block *Block)
}
-func CalculateBlockReward(block *Block, uncleLength int) *big.Int {
- return BlockReward
-}
-
+// TODO rename to state manager
type BlockManager struct {
// Mutex for locking the block processor. Blocks can only be handled one at a time
mutex sync.Mutex
@@ -29,6 +24,10 @@ type BlockManager struct {
// The block chain :)
bc *BlockChain
+ // States for addresses. You can watch any address
+ // at any given time
+ addrStateStore *AddrStateStore
+
// Stack for processing contracts
stack *Stack
// non-persistent key/value memory storage
@@ -46,9 +45,9 @@ type BlockManager struct {
func AddTestNetFunds(block *Block) {
for _, addr := range []string{
"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
- "93658b04240e4bd4046fd2d6d417d20f146f4b43", // Jeffrey
+ "e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
- "80c01a26338f0d905e295fccb71fa9ea849ffa12", // Alex
+ "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
} {
//log.Println("2^200 Wei to", addr)
codedAddr, _ := hex.DecodeString(addr)
@@ -61,26 +60,47 @@ func AddTestNetFunds(block *Block) {
func NewBlockManager(speaker PublicSpeaker) *BlockManager {
bm := &BlockManager{
//server: s,
- bc: NewBlockChain(),
- stack: NewStack(),
- mem: make(map[string]*big.Int),
- Pow: &EasyPow{},
- Speaker: speaker,
+ bc: NewBlockChain(),
+ stack: NewStack(),
+ mem: make(map[string]*big.Int),
+ Pow: &EasyPow{},
+ Speaker: speaker,
+ addrStateStore: NewAddrStateStore(),
}
if bm.bc.CurrentBlock == nil {
AddTestNetFunds(bm.bc.genesisBlock)
+
+ bm.bc.genesisBlock.State().Sync()
// Prepare the genesis block
bm.bc.Add(bm.bc.genesisBlock)
- log.Printf("Genesis: %x\n", bm.bc.genesisBlock.Hash())
//log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
//bm.bc.genesisBlock.PrintHash()
}
+ log.Printf("Last block: %x\n", bm.bc.CurrentBlock.Hash())
+
return bm
}
+// Watches any given address and puts it in the address state store
+func (bm *BlockManager) WatchAddr(addr []byte) *AddressState {
+ account := bm.bc.CurrentBlock.GetAddr(addr)
+
+ return bm.addrStateStore.Add(addr, account)
+}
+
+func (bm *BlockManager) GetAddrState(addr []byte) *AddressState {
+ account := bm.addrStateStore.Get(addr)
+ if account == nil {
+ a := bm.bc.CurrentBlock.GetAddr(addr)
+ account = &AddressState{Nonce: a.Nonce, Account: a}
+ }
+
+ return account
+}
+
func (bm *BlockManager) BlockChain() *BlockChain {
return bm.bc
}
@@ -91,9 +111,15 @@ 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 {
+ err := bm.TransactionPool.ProcessTransaction(tx, block)
+ if err != nil {
+ ethutil.Config.Log.Infoln("[BMGR]", err)
+ }
+ }
}
}
}
@@ -103,6 +129,11 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
// Processing a blocks may never happen simultaneously
bm.mutex.Lock()
defer bm.mutex.Unlock()
+ // Defer the Undo on the Trie. If the block processing happened
+ // we don't want to undo but since undo only happens on dirty
+ // nodes this won't happen because Commit would have been called
+ // before that.
+ defer bm.bc.CurrentBlock.Undo()
hash := block.Hash()
@@ -110,12 +141,6 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
return nil
}
- /*
- if ethutil.Config.Debug {
- log.Printf("[BMGR] Processing block(%x)\n", hash)
- }
- */
-
// 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) && bm.bc.CurrentBlock != nil {
@@ -137,37 +162,19 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
}
if !block.State().Cmp(bm.bc.CurrentBlock.State()) {
- //if block.State().Root != state.Root {
return fmt.Errorf("Invalid merkle root. Expected %x, got %x", block.State().Root, bm.bc.CurrentBlock.State().Root)
}
// Calculate the new total difficulty and sync back to the db
if bm.CalculateTD(block) {
- // Sync the current block's state to the database
- bm.bc.CurrentBlock.State().Sync()
- // Add the block to the chain
- bm.bc.Add(block)
-
- /*
- ethutil.Config.Db.Put(block.Hash(), block.RlpEncode())
- bm.bc.CurrentBlock = block
- bm.LastBlockHash = block.Hash()
- bm.writeBlockInfo(block)
- */
-
- /*
- txs := bm.TransactionPool.Flush()
- var coded = []interface{}{}
- for _, tx := range txs {
- err := bm.TransactionPool.ValidateTransaction(tx)
- if err == nil {
- coded = append(coded, tx.RlpEncode())
- }
- }
- */
+ // Sync the current block's state to the database and cancelling out the deferred Undo
+ bm.bc.CurrentBlock.Sync()
// Broadcast the valid block back to the wire
- //bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.RlpValue().Value})
+ //bm.Speaker.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val})
+
+ // Add the block to the chain
+ bm.bc.Add(block)
// If there's a block processor present, pass in the block for further
// processing
@@ -175,7 +182,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
bm.SecondaryBlockProcessor.ProcessBlock(block)
}
- log.Printf("[BMGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash())
+ ethutil.Config.Log.Infof("[BMGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash())
} else {
fmt.Println("total diff failed")
}
@@ -246,6 +253,18 @@ func (bm *BlockManager) ValidateBlock(block *Block) error {
return nil
}
+func CalculateBlockReward(block *Block, uncleLength int) *big.Int {
+ base := new(big.Int)
+ for i := 0; i < uncleLength; i++ {
+ base.Add(base, UncleInclusionReward)
+ }
+ return base.Add(base, BlockReward)
+}
+
+func CalculateUncleReward(block *Block) *big.Int {
+ return UncleReward
+}
+
func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error {
// Get the coinbase rlp data
addr := processor.GetAddr(block.Coinbase)
@@ -254,7 +273,12 @@ func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error
processor.UpdateAddr(block.Coinbase, addr)
- // TODO Reward each uncle
+ for _, uncle := range block.Uncles {
+ uncleAddr := processor.GetAddr(uncle.Coinbase)
+ uncleAddr.AddFee(CalculateUncleReward(uncle))
+
+ processor.UpdateAddr(uncle.Coinbase, uncleAddr)
+ }
return nil
}
@@ -263,363 +287,26 @@ 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() {
- if r := recover(); r != nil {
- fmt.Println("Recovered from VM execution with err =", r)
- }
- }()
-
- // 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
- })
-}
-
-// Contract evaluation is done here.
-func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallback) {
-
- // Instruction pointer
- pc := 0
- blockInfo := bm.bc.BlockInfo(block)
-
- contract := block.GetContract(tx.Hash())
- if contract == nil {
- fmt.Println("Contract not found")
- return
- }
-
- Pow256 := ethutil.BigPow(2, 256)
-
- if ethutil.Config.Debug {
- 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 ethutil.Config.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.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(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.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
- 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 := 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++
+ /*
+ defer func() {
+ if r := recover(); r != nil {
+ fmt.Println("Recovered from VM execution with err =", r)
}
- // 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()
- }
- pc++
- }
-}
-
-// 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()
+ 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,
+ })
}
diff --git a/ethchain/block_manager_test.go b/ethchain/block_manager_test.go
index 502c50b97..ec4fbe8c5 100644
--- a/ethchain/block_manager_test.go
+++ b/ethchain/block_manager_test.go
@@ -1,75 +1,33 @@
package ethchain
-/*
import (
_ "fmt"
+ "github.com/ethereum/eth-go/ethdb"
+ "github.com/ethereum/eth-go/ethutil"
+ "math/big"
"testing"
)
func TestVm(t *testing.T) {
InitFees()
-
- db, _ := NewMemDatabase()
- Db = db
-
- ctrct := NewTransaction("", 200000000, []string{
- "PUSH", "1a2f2e",
- "PUSH", "hallo",
- "POP", // POP hallo
- "PUSH", "3",
- "LOAD", // Load hallo back on the stack
-
- "PUSH", "1",
- "PUSH", "2",
- "ADD",
-
- "PUSH", "2",
- "PUSH", "1",
- "SUB",
-
- "PUSH", "100000000000000000000000",
- "PUSH", "10000000000000",
- "SDIV",
-
- "PUSH", "105",
- "PUSH", "200",
- "MOD",
-
- "PUSH", "100000000000000000000000",
- "PUSH", "10000000000000",
- "SMOD",
-
- "PUSH", "5",
- "PUSH", "10",
- "LT",
-
- "PUSH", "5",
- "PUSH", "5",
- "LE",
-
- "PUSH", "50",
- "PUSH", "5",
- "GT",
-
- "PUSH", "5",
- "PUSH", "5",
- "GE",
-
- "PUSH", "10",
- "PUSH", "10",
- "NOT",
-
- "MYADDRESS",
- "TXSENDER",
-
- "STOP",
+ ethutil.ReadConfig("")
+
+ db, _ := ethdb.NewMemDatabase()
+ ethutil.Config.Db = db
+ bm := NewBlockManager(nil)
+
+ block := bm.bc.genesisBlock
+ script := Compile([]string{
+ "PUSH",
+ "1",
+ "PUSH",
+ "2",
})
- tx := NewTransaction("1e8a42ea8cce13", 100, []string{})
-
- block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx})
- db.Put(block.Hash(), block.RlpEncode())
+ tx := NewTransaction(ContractAddr, big.NewInt(200000000), script)
+ addr := tx.Hash()[12:]
+ bm.ApplyTransactions(block, []*Transaction{tx})
- bm := NewBlockManager()
- bm.ProcessBlock(block)
+ 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 70189593b..dbcbb3697 100644
--- a/ethchain/contract.go
+++ b/ethchain/contract.go
@@ -30,37 +30,42 @@ func (c *Contract) RlpDecode(data []byte) {
c.state = ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())
}
-func (c *Contract) State() *ethutil.Trie {
- return c.state
-}
-
-type Address struct {
- Amount *big.Int
- Nonce uint64
+func (c *Contract) Addr(addr []byte) *ethutil.Value {
+ return ethutil.NewValueFromBytes([]byte(c.state.Get(string(addr))))
}
-func NewAddress(amount *big.Int) *Address {
- return &Address{Amount: amount, Nonce: 0}
+func (c *Contract) SetAddr(addr []byte, value interface{}) {
+ c.state.Update(string(addr), string(ethutil.NewValue(value).Encode()))
}
-func NewAddressFromData(data []byte) *Address {
- address := &Address{}
- address.RlpDecode(data)
-
- return address
+func (c *Contract) State() *ethutil.Trie {
+ return c.state
}
-func (a *Address) AddFee(fee *big.Int) {
- a.Amount.Add(a.Amount, fee)
-}
+func (c *Contract) GetMem(num int) *ethutil.Value {
+ nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256)
-func (a *Address) RlpEncode() []byte {
- return ethutil.Encode([]interface{}{a.Amount, a.Nonce})
+ return c.Addr(nb)
}
-func (a *Address) RlpDecode(data []byte) {
- decoder := ethutil.NewValueFromBytes(data)
-
- a.Amount = decoder.Get(0).BigInt()
- a.Nonce = decoder.Get(1).Uint()
+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/fees.go b/ethchain/fees.go
index 8f1646ab4..02f09fa04 100644
--- a/ethchain/fees.go
+++ b/ethchain/fees.go
@@ -4,22 +4,32 @@ import (
"math/big"
)
-var StepFee *big.Int = new(big.Int)
var TxFeeRat *big.Int = big.NewInt(100000000000000)
+
var TxFee *big.Int = big.NewInt(100)
-var ContractFee *big.Int = new(big.Int)
-var MemFee *big.Int = new(big.Int)
-var DataFee *big.Int = new(big.Int)
-var CryptoFee *big.Int = new(big.Int)
-var ExtroFee *big.Int = new(big.Int)
+var StepFee *big.Int = big.NewInt(1)
+var StoreFee *big.Int = big.NewInt(5)
+var DataFee *big.Int = big.NewInt(20)
+var ExtroFee *big.Int = big.NewInt(40)
+var CryptoFee *big.Int = big.NewInt(20)
+var ContractFee *big.Int = big.NewInt(100)
+
+var BlockReward *big.Int = big.NewInt(1.5e+18)
+var UncleReward *big.Int = big.NewInt(1.125e+18)
+var UncleInclusionReward *big.Int = big.NewInt(1.875e+17)
-var BlockReward *big.Int = big.NewInt(1500000000000000000)
var Period1Reward *big.Int = new(big.Int)
var Period2Reward *big.Int = new(big.Int)
var Period3Reward *big.Int = new(big.Int)
var Period4Reward *big.Int = new(big.Int)
func InitFees() {
+ StepFee.Mul(StepFee, TxFeeRat)
+ StoreFee.Mul(StoreFee, TxFeeRat)
+ DataFee.Mul(DataFee, TxFeeRat)
+ ExtroFee.Mul(ExtroFee, TxFeeRat)
+ CryptoFee.Mul(CryptoFee, TxFeeRat)
+ ContractFee.Mul(ContractFee, TxFeeRat)
/*
// Base for 2**64
b60 := new(big.Int)
diff --git a/ethchain/genesis.go b/ethchain/genesis.go
index 060d347e4..935978a69 100644
--- a/ethchain/genesis.go
+++ b/ethchain/genesis.go
@@ -13,7 +13,7 @@ var ZeroHash256 = make([]byte, 32)
var ZeroHash160 = make([]byte, 20)
var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{}))
-var GenisisHeader = []interface{}{
+var GenesisHeader = []interface{}{
// Previous hash (none)
//"",
ZeroHash256,
@@ -36,4 +36,4 @@ var GenisisHeader = []interface{}{
ethutil.Sha3Bin(big.NewInt(42).Bytes()),
}
-var Genesis = []interface{}{GenisisHeader, []interface{}{}, []interface{}{}}
+var Genesis = []interface{}{GenesisHeader, []interface{}{}, []interface{}{}}
diff --git a/ethchain/stack.go b/ethchain/stack.go
index c80d01e5c..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",
@@ -159,9 +158,33 @@ func (st *Stack) Popn() (*big.Int, *big.Int) {
return ints[0], ints[1]
}
+func (st *Stack) Peek() *big.Int {
+ s := len(st.data)
+
+ str := st.data[s-1]
+
+ return str
+}
+
+func (st *Stack) Peekn() (*big.Int, *big.Int) {
+ s := len(st.data)
+
+ ints := st.data[s-2:]
+
+ return ints[0], ints[1]
+}
+
func (st *Stack) Push(d *big.Int) {
st.data = append(st.data, d)
}
func (st *Stack) Print() {
- fmt.Println(st.data)
+ fmt.Println("### STACK ###")
+ if len(st.data) > 0 {
+ for i, val := range st.data {
+ fmt.Printf("%-3d %v\n", i, val)
+ }
+ } else {
+ fmt.Println("-- empty --")
+ }
+ fmt.Println("#############")
}
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 1a9258201..57df9cdc4 100644
--- a/ethchain/transaction.go
+++ b/ethchain/transaction.go
@@ -1,11 +1,14 @@
package ethchain
import (
+ "bytes"
"github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go"
"math/big"
)
+var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
type Transaction struct {
Nonce uint64
Recipient []byte
@@ -21,20 +24,17 @@ 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, err := ethutil.CompileInstr(val)
- if err != nil {
- //fmt.Printf("compile error:%d %v\n", i+1, err)
- }
-
- tx.Data[i] = instr
- }
+ tx.Data = data
return &tx
}
+// XXX Deprecated
func NewTransactionFromData(data []byte) *Transaction {
+ return NewTransactionFromBytes(data)
+}
+
+func NewTransactionFromBytes(data []byte) *Transaction {
tx := &Transaction{}
tx.RlpDecode(data)
@@ -65,7 +65,7 @@ func (tx *Transaction) Hash() []byte {
}
func (tx *Transaction) IsContract() bool {
- return len(tx.Recipient) == 0
+ return bytes.Compare(tx.Recipient, ContractAddr) == 0
}
func (tx *Transaction) Signature(key []byte) []byte {
diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go
index c2d65a2a7..cd09bf02e 100644
--- a/ethchain/transaction_pool.go
+++ b/ethchain/transaction_pool.go
@@ -17,6 +17,17 @@ const (
)
type TxPoolHook chan *Transaction
+type TxMsgTy byte
+
+const (
+ TxPre = iota
+ TxPost
+)
+
+type TxMsg struct {
+ Tx *Transaction
+ Type TxMsgTy
+}
func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction {
for e := pool.Front(); e != nil; e = e.Next() {
@@ -34,6 +45,10 @@ type PublicSpeaker interface {
Broadcast(msgType ethwire.MsgType, data []interface{})
}
+type TxProcessor interface {
+ ProcessTransaction(tx *Transaction)
+}
+
// The tx pool a thread safe transaction pool handler. In order to
// guarantee a non blocking pool we use a queue channel which can be
// independently read without needing access to the actual pool. If the
@@ -54,7 +69,9 @@ type TxPool struct {
BlockManager *BlockManager
- Hook TxPoolHook
+ SecondaryProcessor TxProcessor
+
+ subscribers []chan TxMsg
}
func NewTxPool() *TxPool {
@@ -80,8 +97,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
// Process transaction validates the Tx and processes funds from the
// sender to the recipient.
func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) {
- log.Printf("[TXPL] Processing Tx %x\n", tx.Hash())
-
defer func() {
if r := recover(); r != nil {
log.Println(r)
@@ -106,17 +121,30 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error
}
}
- // Subtract the amount from the senders account
- sender.Amount.Sub(sender.Amount, totAmount)
- sender.Nonce += 1
-
// Get the receiver
receiver := block.GetAddr(tx.Recipient)
- // Add the amount to receivers account which should conclude this transaction
- receiver.Amount.Add(receiver.Amount, tx.Value)
+ sender.Nonce += 1
+
+ // Send Tx to self
+ if bytes.Compare(tx.Recipient, tx.Sender()) == 0 {
+ // Subtract the fee
+ sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat))
+ } else {
+ // Subtract the amount from the senders account
+ sender.Amount.Sub(sender.Amount, totAmount)
+
+ // Add the amount to receivers account which should conclude this transaction
+ receiver.Amount.Add(receiver.Amount, tx.Value)
+
+ block.UpdateAddr(tx.Recipient, receiver)
+ }
block.UpdateAddr(tx.Sender(), sender)
- block.UpdateAddr(tx.Recipient, receiver)
+
+ log.Printf("[TXPL] Processed Tx %x\n", tx.Hash())
+
+ // Notify the subscribers
+ pool.notifySubscribers(TxPost, tx)
return
}
@@ -131,7 +159,8 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
}
// Get the sender
- sender := block.GetAddr(tx.Sender())
+ accountState := pool.BlockManager.GetAddrState(tx.Sender())
+ sender := accountState.Account
totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat))
// Make sure there's enough in the sender's account. Having insufficient
@@ -171,9 +200,8 @@ out:
// doesn't matter since this is a goroutine
pool.addTransaction(tx)
- if pool.Hook != nil {
- pool.Hook <- tx
- }
+ // Notify the subscribers
+ pool.notifySubscribers(TxPre, tx)
}
case <-pool.quit:
break out
@@ -217,3 +245,14 @@ func (pool *TxPool) Stop() {
pool.Flush()
}
+
+func (pool *TxPool) Subscribe(channel chan TxMsg) {
+ pool.subscribers = append(pool.subscribers, channel)
+}
+
+func (pool *TxPool) notifySubscribers(ty TxMsgTy, tx *Transaction) {
+ msg := TxMsg{Type: ty, Tx: tx}
+ for _, subscriber := range pool.subscribers {
+ subscriber <- msg
+ }
+}
diff --git a/ethchain/transaction_test.go b/ethchain/transaction_test.go
index c9090b83d..a49768aea 100644
--- a/ethchain/transaction_test.go
+++ b/ethchain/transaction_test.go
@@ -2,8 +2,6 @@ package ethchain
import (
"encoding/hex"
- "fmt"
- "github.com/ethereum/eth-go/ethutil"
"math/big"
"testing"
)
@@ -42,13 +40,15 @@ func TestAddressRetrieval2(t *testing.T) {
tx.Sign(key)
//data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
//tx := NewTransactionFromData(data)
- fmt.Println(tx.RlpValue())
+ /*
+ fmt.Println(tx.RlpValue())
- fmt.Printf("rlp %x\n", tx.RlpEncode())
- fmt.Printf("sha rlp %x\n", tx.Hash())
+ fmt.Printf("rlp %x\n", tx.RlpEncode())
+ fmt.Printf("sha rlp %x\n", tx.Hash())
- //tx.Sign(key)
+ //tx.Sign(key)
- fmt.Printf("hex tx key %x\n", tx.PublicKey())
- fmt.Printf("seder %x\n", tx.Sender())
+ fmt.Printf("hex tx key %x\n", tx.PublicKey())
+ fmt.Printf("seder %x\n", tx.Sender())
+ */
}
diff --git a/ethchain/vm.go b/ethchain/vm.go
new file mode 100644
index 000000000..c7a91a9c5
--- /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 {
+ ethutil.Config.Log.Debugf("# 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 {
+ ethutil.Config.Log.Debugf("%-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), "")
+
+ ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr)
+ break out
+ default:
+ fmt.Printf("Invalid OPCODE: %x\n", op)
+ }
+ ethutil.Config.Log.Debugln("")
+ //vm.stack.Print()
+ pc++
+ }
+
+ state.UpdateContract(addr, contract)
+}
+
+func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) {
+ ethutil.Config.Log.Debugf(" => 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..3dbff36de 100644
--- a/ethdb/database.go
+++ b/ethdb/database.go
@@ -11,8 +11,8 @@ type LDBDatabase struct {
db *leveldb.DB
}
-func NewLDBDatabase() (*LDBDatabase, error) {
- dbPath := path.Join(ethutil.Config.ExecPath, "database")
+func NewLDBDatabase(name string) (*LDBDatabase, error) {
+ dbPath := path.Join(ethutil.Config.ExecPath, name)
// Open the db
db, err := leveldb.OpenFile(dbPath, nil)
@@ -36,6 +36,14 @@ 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) Db() *leveldb.DB {
+ return db.db
+}
+
func (db *LDBDatabase) LastKnownTD() []byte {
data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil)
@@ -46,13 +54,19 @@ func (db *LDBDatabase) LastKnownTD() []byte {
return data
}
+func (db *LDBDatabase) GetKeys() []*ethutil.Key {
+ data, _ := db.Get([]byte("KeyRing"))
+
+ return []*ethutil.Key{ethutil.NewKeyFromBytes(data)}
+}
+
func (db *LDBDatabase) Close() {
// Close the leveldb database
db.db.Close()
}
func (db *LDBDatabase) Print() {
- iter := db.db.NewIterator(nil)
+ iter := db.db.NewIterator(nil, nil)
for iter.Next() {
key := iter.Key()
value := iter.Value()
diff --git a/ethdb/memory_database.go b/ethdb/memory_database.go
index cd9f24000..b0fa64ed7 100644
--- a/ethdb/memory_database.go
+++ b/ethdb/memory_database.go
@@ -26,6 +26,18 @@ func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return db.db[string(key)], nil
}
+func (db *MemDatabase) GetKeys() []*ethutil.Key {
+ data, _ := db.Get([]byte("KeyRing"))
+
+ return []*ethutil.Key{ethutil.NewKeyFromBytes(data)}
+}
+
+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/ethereum.go b/ethereum.go
index bd6caac08..90682b396 100644
--- a/ethereum.go
+++ b/ethereum.go
@@ -47,6 +47,7 @@ type Ethereum struct {
Nonce uint64
Addr net.Addr
+ Port string
peerMut sync.Mutex
@@ -60,7 +61,7 @@ type Ethereum struct {
}
func New(caps Caps, usePnp bool) (*Ethereum, error) {
- db, err := ethdb.NewLDBDatabase()
+ db, err := ethdb.NewLDBDatabase("database")
//db, err := ethdb.NewMemDatabase()
if err != nil {
return nil, err
@@ -70,7 +71,7 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
if usePnp {
nat, err = Discover()
if err != nil {
- log.Println("UPnP failed", err)
+ ethutil.Config.Log.Debugln("UPnP failed", err)
}
}
@@ -85,7 +86,6 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
Nonce: nonce,
serverCaps: caps,
nat: nat,
- MaxPeers: 5,
}
ethereum.TxPool = ethchain.NewTxPool()
ethereum.TxPool.Speaker = ethereum
@@ -94,6 +94,9 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
ethereum.TxPool.BlockManager = ethereum.BlockManager
ethereum.BlockManager.TransactionPool = ethereum.TxPool
+ // Start the tx pool
+ ethereum.TxPool.Start()
+
return ethereum, nil
}
@@ -114,28 +117,32 @@ func (s *Ethereum) ProcessPeerList(addrs []string) {
}
func (s *Ethereum) ConnectToPeer(addr string) error {
- var alreadyConnected bool
+ if s.peers.Len() < s.MaxPeers {
+ var alreadyConnected bool
- eachPeer(s.peers, func(p *Peer, v *list.Element) {
- if p.conn == nil {
- return
- }
- phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
- ahost, _, _ := net.SplitHostPort(addr)
+ eachPeer(s.peers, func(p *Peer, v *list.Element) {
+ if p.conn == nil {
+ return
+ }
+ phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
+ ahost, _, _ := net.SplitHostPort(addr)
- if phost == ahost {
- alreadyConnected = true
- return
+ if phost == ahost {
+ alreadyConnected = true
+ return
+ }
+ })
+
+ if alreadyConnected {
+ return nil
}
- })
- if alreadyConnected {
- return nil
- }
+ peer := NewOutboundPeer(addr, s, s.serverCaps)
- peer := NewOutboundPeer(addr, s, s.serverCaps)
+ s.peers.PushBack(peer)
- s.peers.PushBack(peer)
+ log.Printf("[SERV] Adding peer %d / %d\n", s.peers.Len(), s.MaxPeers)
+ }
return nil
}
@@ -226,12 +233,12 @@ func (s *Ethereum) ReapDeadPeerHandler() {
// Start the ethereum
func (s *Ethereum) Start() {
// Bind to addr and port
- ln, err := net.Listen("tcp", ":30303")
+ ln, err := net.Listen("tcp", ":"+s.Port)
if err != nil {
log.Println("Connection listening disabled. Acting as client")
} else {
// Starting accepting connections
- log.Println("Ready and accepting connections")
+ ethutil.Config.Log.Infoln("Ready and accepting connections")
// Start the peer handler
go s.peerHandler(ln)
}
@@ -243,13 +250,10 @@ func (s *Ethereum) Start() {
// Start the reaping processes
go s.ReapDeadPeerHandler()
- // Start the tx pool
- s.TxPool.Start()
-
if ethutil.Config.Seed {
- log.Println("Seeding")
+ ethutil.Config.Log.Debugln("Seeding")
// Testnet seed bootstrapping
- resp, err := http.Get("http://www.ethereum.org/servers.poc2.txt")
+ resp, err := http.Get("http://www.ethereum.org/servers.poc3.txt")
if err != nil {
log.Println("Fetching seed failed:", err)
return
@@ -269,7 +273,7 @@ func (s *Ethereum) peerHandler(listener net.Listener) {
for {
conn, err := listener.Accept()
if err != nil {
- log.Println(err)
+ ethutil.Config.Log.Debugln(err)
continue
}
@@ -303,7 +307,7 @@ func (s *Ethereum) upnpUpdateThread() {
// Go off immediately to prevent code duplication, thereafter we renew
// lease every 15 minutes.
timer := time.NewTimer(0 * time.Second)
- lport, _ := strconv.ParseInt("30303", 10, 16)
+ lport, _ := strconv.ParseInt(s.Port, 10, 16)
first := true
out:
for {
@@ -312,13 +316,13 @@ out:
var err error
_, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
if err != nil {
- log.Println("can't add UPnP port mapping:", err)
+ ethutil.Config.Log.Debugln("can't add UPnP port mapping:", err)
break out
}
if first && err == nil {
_, err = s.nat.GetExternalAddress()
if err != nil {
- log.Println("UPnP can't get external address:", err)
+ ethutil.Config.Log.Debugln("UPnP can't get external address:", err)
continue out
}
first = false
@@ -332,8 +336,8 @@ out:
timer.Stop()
if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil {
- log.Println("unable to remove UPnP port mapping:", err)
+ ethutil.Config.Log.Debugln("unable to remove UPnP port mapping:", err)
} else {
- log.Println("succesfully disestablished UPnP port mapping")
+ ethutil.Config.Log.Debugln("succesfully disestablished UPnP port mapping")
}
}
diff --git a/ethutil/README.md b/ethutil/README.md
index c98612e1e..1ed56b71b 100644
--- a/ethutil/README.md
+++ b/ethutil/README.md
@@ -53,6 +53,8 @@ trie.Put("doge", "coin")
// Look up the key "do" in the trie
out := trie.Get("do")
fmt.Println(out) // => verb
+
+trie.Delete("puppy")
```
The patricia trie, in combination with RLP, provides a robust,
@@ -82,7 +84,7 @@ type (e.g. `Slice()` returns []interface{}, `Uint()` return 0, etc).
`NewEmptyValue()` returns a new \*Value with it's initial value set to a
`[]interface{}`
-`AppendLint()` appends a list to the current value.
+`AppendList()` appends a list to the current value.
`Append(v)` appends the value (v) to the current value/list.
diff --git a/ethutil/big.go b/ethutil/big.go
index 979078bef..1a3902fa3 100644
--- a/ethutil/big.go
+++ b/ethutil/big.go
@@ -35,3 +35,18 @@ func BigD(data []byte) *big.Int {
return n
}
+
+func BigToBytes(num *big.Int, base int) []byte {
+ ret := make([]byte, base/8)
+
+ return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...)
+}
+
+// Functions like the build in "copy" function
+// but works on big integers
+func BigCopy(src *big.Int) (ret *big.Int) {
+ ret = new(big.Int)
+ ret.Add(ret, src)
+
+ return
+}
diff --git a/ethutil/common.go b/ethutil/common.go
new file mode 100644
index 000000000..07df6bb13
--- /dev/null
+++ b/ethutil/common.go
@@ -0,0 +1,35 @@
+package ethutil
+
+import (
+ "fmt"
+ "math/big"
+)
+
+var (
+ Ether = BigPow(10, 18)
+ Finney = BigPow(10, 15)
+ Szabo = BigPow(10, 12)
+ Vito = BigPow(10, 9)
+ Turing = BigPow(10, 6)
+ Eins = BigPow(10, 3)
+ Wei = big.NewInt(1)
+)
+
+func CurrencyToString(num *big.Int) string {
+ switch {
+ case num.Cmp(Ether) >= 0:
+ return fmt.Sprintf("%v Ether", new(big.Int).Div(num, Ether))
+ case num.Cmp(Finney) >= 0:
+ return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney))
+ case num.Cmp(Szabo) >= 0:
+ return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo))
+ case num.Cmp(Vito) >= 0:
+ return fmt.Sprintf("%v Vito", new(big.Int).Div(num, Vito))
+ case num.Cmp(Turing) >= 0:
+ return fmt.Sprintf("%v Turing", new(big.Int).Div(num, Turing))
+ case num.Cmp(Eins) >= 0:
+ return fmt.Sprintf("%v Eins", new(big.Int).Div(num, Eins))
+ }
+
+ return fmt.Sprintf("%v Wei", num)
+}
diff --git a/ethutil/common_test.go b/ethutil/common_test.go
new file mode 100644
index 000000000..3a6a37ff5
--- /dev/null
+++ b/ethutil/common_test.go
@@ -0,0 +1,17 @@
+package ethutil
+
+import (
+ "fmt"
+ "math/big"
+ "testing"
+)
+
+func TestCommon(t *testing.T) {
+ fmt.Println(CurrencyToString(BigPow(10, 19)))
+ fmt.Println(CurrencyToString(BigPow(10, 16)))
+ fmt.Println(CurrencyToString(BigPow(10, 13)))
+ fmt.Println(CurrencyToString(BigPow(10, 10)))
+ fmt.Println(CurrencyToString(BigPow(10, 7)))
+ fmt.Println(CurrencyToString(BigPow(10, 4)))
+ fmt.Println(CurrencyToString(big.NewInt(10)))
+}
diff --git a/ethutil/config.go b/ethutil/config.go
index 2a239f8e2..5bf56134d 100644
--- a/ethutil/config.go
+++ b/ethutil/config.go
@@ -1,6 +1,7 @@
package ethutil
import (
+ "fmt"
"log"
"os"
"os/user"
@@ -18,7 +19,7 @@ const (
type config struct {
Db Database
- Log Logger
+ Log *Logger
ExecPath string
Debug bool
Ver string
@@ -34,17 +35,19 @@ func ReadConfig(base string) *config {
usr, _ := user.Current()
path := path.Join(usr.HomeDir, base)
- //Check if the logging directory already exists, create it if not
- _, err := os.Stat(path)
- if err != nil {
- if os.IsNotExist(err) {
- log.Printf("Debug logging directory %s doesn't exist, creating it", path)
- os.Mkdir(path, 0777)
+ if len(base) > 0 {
+ //Check if the logging directory already exists, create it if not
+ _, err := os.Stat(path)
+ if err != nil {
+ if os.IsNotExist(err) {
+ log.Printf("Debug logging directory %s doesn't exist, creating it\n", path)
+ os.Mkdir(path, 0777)
+ }
}
}
- Config = &config{ExecPath: path, Debug: true, Ver: "0.2.2"}
- Config.Log = NewLogger(LogFile|LogStd, 0)
+ Config = &config{ExecPath: path, Debug: true, Ver: "0.3.0"}
+ Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug)
}
return Config
@@ -57,15 +60,20 @@ const (
LogStd = 0x2
)
+type LogSystem interface {
+ Println(v ...interface{})
+ Printf(format string, v ...interface{})
+}
+
type Logger struct {
- logSys []*log.Logger
+ logSys []LogSystem
logLevel int
}
-func NewLogger(flag LoggerType, level int) Logger {
- var loggers []*log.Logger
+func NewLogger(flag LoggerType, level int) *Logger {
+ var loggers []LogSystem
- flags := log.LstdFlags | log.Lshortfile
+ flags := log.LstdFlags
if flag&LogFile > 0 {
file, err := os.OpenFile(path.Join(Config.ExecPath, "debug.log"), os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
@@ -73,30 +81,60 @@ func NewLogger(flag LoggerType, level int) Logger {
log.Panic("unable to create file logger", err)
}
- log := log.New(file, "[ETH]", flags)
+ log := log.New(file, "", flags)
loggers = append(loggers, log)
}
if flag&LogStd > 0 {
- log := log.New(os.Stdout, "[ETH]", flags)
+ log := log.New(os.Stdout, "", flags)
loggers = append(loggers, log)
}
- return Logger{logSys: loggers, logLevel: level}
+ return &Logger{logSys: loggers, logLevel: level}
+}
+
+func (log *Logger) AddLogSystem(logger LogSystem) {
+ log.logSys = append(log.logSys, logger)
+}
+
+const (
+ LogLevelDebug = iota
+ LogLevelInfo
+)
+
+func (log *Logger) Debugln(v ...interface{}) {
+ if log.logLevel != LogLevelDebug {
+ return
+ }
+
+ for _, logger := range log.logSys {
+ logger.Println(v...)
+ }
+}
+
+func (log *Logger) Debugf(format string, v ...interface{}) {
+ if log.logLevel != LogLevelDebug {
+ return
+ }
+
+ for _, logger := range log.logSys {
+ logger.Printf(format, v...)
+ }
}
-func (log Logger) Debugln(v ...interface{}) {
- if log.logLevel != 0 {
+func (log *Logger) Infoln(v ...interface{}) {
+ if log.logLevel > LogLevelInfo {
return
}
+ fmt.Println(len(log.logSys))
for _, logger := range log.logSys {
logger.Println(v...)
}
}
-func (log Logger) Debugf(format string, v ...interface{}) {
- if log.logLevel != 0 {
+func (log *Logger) Infof(format string, v ...interface{}) {
+ if log.logLevel > LogLevelInfo {
return
}
diff --git a/ethutil/db.go b/ethutil/db.go
index 3681c4b05..abbf4a2b0 100644
--- a/ethutil/db.go
+++ b/ethutil/db.go
@@ -4,6 +4,8 @@ package ethutil
type Database interface {
Put(key []byte, value []byte)
Get(key []byte) ([]byte, error)
+ GetKeys() []*Key
+ Delete(key []byte) error
LastKnownTD() []byte
Close()
Print()
diff --git a/ethutil/encoding.go b/ethutil/encoding.go
index 207548c93..1f661947a 100644
--- a/ethutil/encoding.go
+++ b/ethutil/encoding.go
@@ -3,7 +3,6 @@ package ethutil
import (
"bytes"
"encoding/hex"
- _ "fmt"
"strings"
)
@@ -36,7 +35,7 @@ func CompactEncode(hexSlice []int) string {
func CompactDecode(str string) []int {
base := CompactHexDecode(str)
base = base[:len(base)-1]
- if base[0] >= 2 { // && base[len(base)-1] != 16 {
+ if base[0] >= 2 {
base = append(base, 16)
}
if base[0]%2 == 1 {
diff --git a/ethutil/encoding_test.go b/ethutil/encoding_test.go
index bcabab0b1..cbfbc0eaf 100644
--- a/ethutil/encoding_test.go
+++ b/ethutil/encoding_test.go
@@ -35,3 +35,33 @@ func TestCompactHexDecode(t *testing.T) {
t.Error("Error compact hex decode. Expected", exp, "got", res)
}
}
+
+func TestCompactDecode(t *testing.T) {
+ exp := []int{1, 2, 3, 4, 5}
+ res := CompactDecode("\x11\x23\x45")
+
+ if !CompareIntSlice(res, exp) {
+ t.Error("odd compact decode. Expected", exp, "got", res)
+ }
+
+ exp = []int{0, 1, 2, 3, 4, 5}
+ res = CompactDecode("\x00\x01\x23\x45")
+
+ if !CompareIntSlice(res, exp) {
+ t.Error("even compact decode. Expected", exp, "got", res)
+ }
+
+ exp = []int{0, 15, 1, 12, 11, 8 /*term*/, 16}
+ res = CompactDecode("\x20\x0f\x1c\xb8")
+
+ if !CompareIntSlice(res, exp) {
+ t.Error("even terminated compact decode. Expected", exp, "got", res)
+ }
+
+ exp = []int{15, 1, 12, 11, 8 /*term*/, 16}
+ res = CompactDecode("\x3f\x1c\xb8")
+
+ if !CompareIntSlice(res, exp) {
+ t.Error("even terminated compact decode. Expected", exp, "got", res)
+ }
+} \ No newline at end of file
diff --git a/ethutil/helpers.go b/ethutil/helpers.go
index 1c6adf256..aa0f79a04 100644
--- a/ethutil/helpers.go
+++ b/ethutil/helpers.go
@@ -27,7 +27,6 @@ func Ripemd160(data []byte) []byte {
func Sha3Bin(data []byte) []byte {
d := sha3.NewKeccak256()
- d.Reset()
d.Write(data)
return d.Sum(nil)
@@ -59,3 +58,7 @@ func MatchingNibbleLength(a, b []int) int {
func Hex(d []byte) string {
return hex.EncodeToString(d)
}
+func FromHex(str string) []byte {
+ h, _ := hex.DecodeString(str)
+ return h
+}
diff --git a/ethutil/key.go b/ethutil/key.go
new file mode 100644
index 000000000..ec195f213
--- /dev/null
+++ b/ethutil/key.go
@@ -0,0 +1,19 @@
+package ethutil
+
+type Key struct {
+ PrivateKey []byte
+ PublicKey []byte
+}
+
+func NewKeyFromBytes(data []byte) *Key {
+ val := NewValueFromBytes(data)
+ return &Key{val.Get(0).Bytes(), val.Get(1).Bytes()}
+}
+
+func (k *Key) Address() []byte {
+ return Sha3Bin(k.PublicKey[1:])[12:]
+}
+
+func (k *Key) RlpEncode() []byte {
+ return EmptyValue().Append(k.PrivateKey).Append(k.PublicKey).Encode()
+}
diff --git a/ethutil/parsing.go b/ethutil/parsing.go
index 2c41fb4df..553bb9717 100644
--- a/ethutil/parsing.go
+++ b/ethutil/parsing.go
@@ -1,95 +1,88 @@
package ethutil
import (
- "errors"
- "fmt"
"math/big"
"strconv"
- "strings"
)
// Op codes
-var OpCodes = map[string]string{
- "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",
-
- "PUSH": "48",
- "POP": "49",
- "LOAD": "54",
+var OpCodes = map[string]byte{
+ "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 CompileInstr(s string) (string, error) {
- tokens := strings.Split(s, " ")
- if OpCodes[tokens[0]] == "" {
- return s, errors.New(fmt.Sprintf("OP not found: %s", tokens[0]))
+func IsOpCode(s string) bool {
+ for key, _ := range OpCodes {
+ if key == s {
+ return true
+ }
}
+ return false
+}
- code := OpCodes[tokens[0]] // Replace op codes with the proper numerical equivalent
- op := new(big.Int)
- op.SetString(code, 0)
-
- args := make([]*big.Int, 6)
- for i, val := range tokens[1:len(tokens)] {
- num := new(big.Int)
- num.SetString(val, 0)
- args[i] = num
- }
-
- // Big int equation = op + x * 256 + y * 256**2 + z * 256**3 + a * 256**4 + b * 256**5 + c * 256**6
- base := new(big.Int)
- x := new(big.Int)
- y := new(big.Int)
- z := new(big.Int)
- a := new(big.Int)
- b := new(big.Int)
- c := new(big.Int)
-
- if args[0] != nil {
- x.Mul(args[0], big.NewInt(256))
- }
- if args[1] != nil {
- y.Mul(args[1], BigPow(256, 2))
- }
- if args[2] != nil {
- z.Mul(args[2], BigPow(256, 3))
- }
- if args[3] != nil {
- a.Mul(args[3], BigPow(256, 4))
- }
- if args[4] != nil {
- b.Mul(args[4], BigPow(256, 5))
- }
- if args[5] != nil {
- c.Mul(args[5], BigPow(256, 6))
+func CompileInstr(s string) ([]byte, error) {
+ isOp := IsOpCode(s)
+ if isOp {
+ return []byte{OpCodes[s]}, nil
}
- base.Add(op, x)
- base.Add(base, y)
- base.Add(base, z)
- base.Add(base, a)
- base.Add(base, b)
- base.Add(base, c)
+ num := new(big.Int)
+ num.SetString(s, 0)
- return base.String(), nil
+ return num.Bytes(), nil
}
func Instr(instr string) (int, []string, error) {
+
base := new(big.Int)
base.SetString(instr, 0)
diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go
index 482eef3ee..6b59777e6 100644
--- a/ethutil/parsing_test.go
+++ b/ethutil/parsing_test.go
@@ -1,5 +1,6 @@
package ethutil
+/*
import (
"math"
"testing"
@@ -13,20 +14,19 @@ func TestCompile(t *testing.T) {
}
calc := (48 + 0*256 + 0*int64(math.Pow(256, 2)))
- if Big(instr).Int64() != calc {
+ if BigD(instr).Int64() != calc {
t.Error("Expected", calc, ", got:", instr)
}
}
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/rlp.go b/ethutil/rlp.go
index 025d269a0..e633f5f1d 100644
--- a/ethutil/rlp.go
+++ b/ethutil/rlp.go
@@ -86,13 +86,6 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} {
// TODO Use a bytes.Buffer instead of a raw byte slice.
// Cleaner code, and use draining instead of seeking the next bytes to read
func Decode(data []byte, pos uint64) (interface{}, uint64) {
- /*
- if pos > uint64(len(data)-1) {
- log.Println(data)
- log.Panicf("index out of range %d for data %q, l = %d", pos, data, len(data))
- }
- */
-
var slice []interface{}
char := int(data[pos])
switch {
@@ -131,7 +124,6 @@ func Decode(data []byte, pos uint64) (interface{}, uint64) {
case char <= 0xff:
l := uint64(data[pos]) - 0xf7
- //b := BigD(data[pos+1 : pos+1+l]).Uint64()
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
pos = pos + l + 1
diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go
index 32bcbdce1..2a58bfc0f 100644
--- a/ethutil/rlp_test.go
+++ b/ethutil/rlp_test.go
@@ -2,15 +2,13 @@ package ethutil
import (
"bytes"
- "encoding/hex"
- "fmt"
"math/big"
"reflect"
"testing"
)
func TestRlpValueEncoding(t *testing.T) {
- val := EmptyRlpValue()
+ val := EmptyValue()
val.AppendList().Append(1).Append(2).Append(3)
val.Append("4").AppendList().Append(5)
@@ -63,7 +61,7 @@ func TestEncode(t *testing.T) {
str := string(bytes)
if str != strRes {
- t.Error(fmt.Sprintf("Expected %q, got %q", strRes, str))
+ t.Errorf("Expected %q, got %q", strRes, str)
}
sliceRes := "\xcc\x83dog\x83god\x83cat"
@@ -71,7 +69,7 @@ func TestEncode(t *testing.T) {
bytes = Encode(strs)
slice := string(bytes)
if slice != sliceRes {
- t.Error(fmt.Sprintf("Expected %q, got %q", sliceRes, slice))
+ t.Error("Expected %q, got %q", sliceRes, slice)
}
intRes := "\x82\x04\x00"
@@ -108,13 +106,9 @@ func TestEncodeDecodeBigInt(t *testing.T) {
encoded := Encode(bigInt)
value := NewValueFromBytes(encoded)
- fmt.Println(value.BigInt(), bigInt)
if value.BigInt().Cmp(bigInt) != 0 {
t.Errorf("Expected %v, got %v", bigInt, value.BigInt())
}
-
- dec, _ := hex.DecodeString("52f4fc1e")
- fmt.Println(NewValueFromBytes(dec).BigInt())
}
func TestEncodeDecodeBytes(t *testing.T) {
@@ -125,43 +119,6 @@ func TestEncodeDecodeBytes(t *testing.T) {
}
}
-/*
-var ZeroHash256 = make([]byte, 32)
-var ZeroHash160 = make([]byte, 20)
-var EmptyShaList = Sha3Bin(Encode([]interface{}{}))
-
-var GenisisHeader = []interface{}{
- // Previous hash (none)
- //"",
- ZeroHash256,
- // Sha of uncles
- Sha3Bin(Encode([]interface{}{})),
- // Coinbase
- ZeroHash160,
- // Root state
- "",
- // Sha of transactions
- //EmptyShaList,
- Sha3Bin(Encode([]interface{}{})),
- // Difficulty
- BigPow(2, 22),
- // Time
- //big.NewInt(0),
- int64(0),
- // extra
- "",
- // Nonce
- big.NewInt(42),
-}
-
-func TestEnc(t *testing.T) {
- //enc := Encode(GenisisHeader)
- //fmt.Printf("%x (%d)\n", enc, len(enc))
- h, _ := hex.DecodeString("f8a0a00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a06d076baa9c4074fb2df222dd16a96b0155a1e6686b3e5748b4e9ca0a208a425ca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493478340000080802a")
- fmt.Printf("%x\n", Sha3Bin(h))
-}
-*/
-
func BenchmarkEncodeDecode(b *testing.B) {
for i := 0; i < b.N; i++ {
bytes := Encode([]interface{}{"dog", "god", "cat"})
diff --git a/ethutil/trie.go b/ethutil/trie.go
index 0a3f73136..a17dc37ad 100644
--- a/ethutil/trie.go
+++ b/ethutil/trie.go
@@ -5,6 +5,15 @@ import (
"reflect"
)
+// TODO
+// A StateObject is an object that has a state root
+// This is goig to be the object for the second level caching (the caching of object which have a state such as contracts)
+type StateObject interface {
+ State() *Trie
+ Sync()
+ Undo()
+}
+
type Node struct {
Key []byte
Value *Value
@@ -20,8 +29,9 @@ func (n *Node) Copy() *Node {
}
type Cache struct {
- nodes map[string]*Node
- db Database
+ nodes map[string]*Node
+ db Database
+ IsDirty bool
}
func NewCache(db Database) *Cache {
@@ -36,6 +46,7 @@ func (cache *Cache) Put(v interface{}) interface{} {
sha := Sha3Bin(enc)
cache.nodes[string(sha)] = NewNode(sha, value, true)
+ cache.IsDirty = true
return sha
}
@@ -59,13 +70,25 @@ 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 {
+ return
+ }
+
for key, node := range cache.nodes {
if node.Dirty {
cache.db.Put([]byte(key), node.Value.Encode())
node.Dirty = false
}
}
+ cache.IsDirty = false
// If the nodes grows beyond the 200 entries we simple empty it
// FIXME come up with something better
@@ -80,6 +103,7 @@ func (cache *Cache) Undo() {
delete(cache.nodes, key)
}
}
+ cache.IsDirty = false
}
// A (modified) Radix Trie implementation. The Trie implements
@@ -89,18 +113,29 @@ func (cache *Cache) Undo() {
// Please note that the data isn't persisted unless `Sync` is
// explicitly called.
type Trie struct {
- Root interface{}
+ prevRoot interface{}
+ Root interface{}
//db Database
cache *Cache
}
func NewTrie(db Database, Root interface{}) *Trie {
- return &Trie{cache: NewCache(db), Root: Root}
+ return &Trie{cache: NewCache(db), Root: Root, prevRoot: Root}
}
// Save the cached value to the database.
func (t *Trie) Sync() {
t.cache.Commit()
+ t.prevRoot = t.Root
+}
+
+func (t *Trie) Undo() {
+ t.cache.Undo()
+ t.Root = t.prevRoot
+}
+
+func (t *Trie) Cache() *Cache {
+ return t.cache
}
/*
@@ -119,6 +154,10 @@ func (t *Trie) Get(key string) string {
return c.Str()
}
+func (t *Trie) Delete(key string) {
+ t.Update(key, "")
+}
+
func (t *Trie) GetState(node interface{}, key []int) interface{} {
n := NewValue(node)
// Return the node if key is empty (= found)
@@ -168,13 +207,15 @@ func (t *Trie) GetNode(node interface{}) *Value {
}
func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{} {
+
if value != "" {
return t.InsertState(node, key, value)
} else {
// delete it
+ return t.DeleteState(node, key)
}
- return ""
+ return t.Root
}
func (t *Trie) Put(node interface{}) interface{} {
@@ -228,6 +269,7 @@ func (t *Trie) InsertState(node interface{}, key []int, value interface{}) inter
// Check for "special" 2 slice type node
if currentNode.Len() == 2 {
// Decode the key
+
k := CompactDecode(currentNode.Get(0).Str())
v := currentNode.Get(1).Raw()
@@ -282,6 +324,87 @@ func (t *Trie) InsertState(node interface{}, key []int, value interface{}) inter
return ""
}
+func (t *Trie) DeleteState(node interface{}, key []int) interface{} {
+ if len(key) == 0 {
+ return ""
+ }
+
+ // New node
+ n := NewValue(node)
+ if node == nil || (n.Type() == reflect.String && (n.Str() == "" || n.Get(0).IsNil())) || n.Len() == 0 {
+ return ""
+ }
+
+ currentNode := t.GetNode(node)
+ // Check for "special" 2 slice type node
+ if currentNode.Len() == 2 {
+ // Decode the key
+ k := CompactDecode(currentNode.Get(0).Str())
+ v := currentNode.Get(1).Raw()
+
+ // Matching key pair (ie. there's already an object with this key)
+ if CompareIntSlice(k, key) {
+ return ""
+ } else if CompareIntSlice(key[:len(k)], k) {
+ hash := t.DeleteState(v, key[len(k):])
+ child := t.GetNode(hash)
+
+ var newNode []interface{}
+ if child.Len() == 2 {
+ newKey := append(k, CompactDecode(child.Get(0).Str())...)
+ newNode = []interface{}{CompactEncode(newKey), child.Get(1).Raw()}
+ } else {
+ newNode = []interface{}{currentNode.Get(0).Str(), hash}
+ }
+
+ return t.Put(newNode)
+ } else {
+ return node
+ }
+ } else {
+ // Copy the current node over to the new node and replace the first nibble in the key
+ n := EmptyStringSlice(17)
+ var newNode []interface{}
+
+ for i := 0; i < 17; i++ {
+ cpy := currentNode.Get(i).Raw()
+ if cpy != nil {
+ n[i] = cpy
+ }
+ }
+
+ n[key[0]] = t.DeleteState(n[key[0]], key[1:])
+ amount := -1
+ for i := 0; i < 17; i++ {
+ if n[i] != "" {
+ if amount == -1 {
+ amount = i
+ } else {
+ amount = -2
+ }
+ }
+ }
+ if amount == 16 {
+ newNode = []interface{}{CompactEncode([]int{16}), n[amount]}
+ } else if amount >= 0 {
+ child := t.GetNode(n[amount])
+ if child.Len() == 17 {
+ newNode = []interface{}{CompactEncode([]int{amount}), n[amount]}
+ } else if child.Len() == 2 {
+ key := append([]int{amount}, CompactDecode(child.Get(0).Str())...)
+ newNode = []interface{}{CompactEncode(key), child.Get(1).Str()}
+ }
+
+ } else {
+ newNode = n
+ }
+
+ return t.Put(newNode)
+ }
+
+ return ""
+}
+
// Simple compare function which creates a rlp value out of the evaluated objects
func (t *Trie) Cmp(trie *Trie) bool {
return NewValue(t.Root).Cmp(NewValue(trie.Root))
@@ -296,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).Str() == "" {
+ 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).Str() == "" {
+ 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 b87d35e1a..7c398f1de 100644
--- a/ethutil/trie_test.go
+++ b/ethutil/trie_test.go
@@ -1,11 +1,12 @@
package ethutil
import (
- _ "encoding/hex"
- _ "fmt"
+ "reflect"
"testing"
)
+const LONG_WORD = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ"
+
type MemDatabase struct {
db map[string][]byte
}
@@ -20,15 +21,24 @@ 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) GetKeys() []*Key { return nil }
func (db *MemDatabase) Print() {}
func (db *MemDatabase) Close() {}
func (db *MemDatabase) LastKnownTD() []byte { return nil }
-func TestTrieSync(t *testing.T) {
+func New() (*MemDatabase, *Trie) {
db, _ := NewMemDatabase()
- trie := NewTrie(db, "")
+ return db, NewTrie(db, "")
+}
- trie.Update("dog", "kindofalongsentencewhichshouldbeencodedinitsentirety")
+func TestTrieSync(t *testing.T) {
+ db, trie := New()
+
+ trie.Update("dog", LONG_WORD)
if len(db.db) != 0 {
t.Error("Expected no data in database")
}
@@ -38,3 +48,125 @@ func TestTrieSync(t *testing.T) {
t.Error("Expected data to be persisted")
}
}
+
+func TestTrieDirtyTracking(t *testing.T) {
+ _, trie := New()
+ trie.Update("dog", LONG_WORD)
+ if !trie.cache.IsDirty {
+ t.Error("Expected trie to be dirty")
+ }
+
+ trie.Sync()
+ if trie.cache.IsDirty {
+ t.Error("Expected trie not to be dirty")
+ }
+
+ trie.Update("test", LONG_WORD)
+ trie.cache.Undo()
+ if trie.cache.IsDirty {
+ t.Error("Expected trie not to be dirty")
+ }
+
+}
+
+func TestTrieReset(t *testing.T) {
+ _, trie := New()
+
+ trie.Update("cat", LONG_WORD)
+ if len(trie.cache.nodes) == 0 {
+ t.Error("Expected cached nodes")
+ }
+
+ trie.cache.Undo()
+
+ if len(trie.cache.nodes) != 0 {
+ t.Error("Expected no nodes after undo")
+ }
+}
+
+func TestTrieGet(t *testing.T) {
+ _, trie := New()
+
+ trie.Update("cat", LONG_WORD)
+ x := trie.Get("cat")
+ if x != LONG_WORD {
+ t.Error("expected %s, got %s", LONG_WORD, x)
+ }
+}
+
+func TestTrieUpdating(t *testing.T) {
+ _, trie := New()
+ trie.Update("cat", LONG_WORD)
+ trie.Update("cat", LONG_WORD+"1")
+ x := trie.Get("cat")
+ if x != LONG_WORD+"1" {
+ t.Error("expected %S, got %s", LONG_WORD+"1", x)
+ }
+}
+
+func TestTrieCmp(t *testing.T) {
+ _, trie1 := New()
+ _, trie2 := New()
+
+ trie1.Update("doge", LONG_WORD)
+ trie2.Update("doge", LONG_WORD)
+ if !trie1.Cmp(trie2) {
+ t.Error("Expected tries to be equal")
+ }
+
+ trie1.Update("dog", LONG_WORD)
+ trie2.Update("cat", LONG_WORD)
+ if trie1.Cmp(trie2) {
+ t.Errorf("Expected tries not to be equal %x %x", trie1.Root, trie2.Root)
+ }
+}
+
+func TestTrieDelete(t *testing.T) {
+ _, trie := New()
+ trie.Update("cat", LONG_WORD)
+ exp := trie.Root
+ trie.Update("dog", LONG_WORD)
+ trie.Delete("dog")
+ if !reflect.DeepEqual(exp, trie.Root) {
+ t.Errorf("Expected tries to be equal %x : %x", exp, trie.Root)
+ }
+
+ trie.Update("dog", LONG_WORD)
+ exp = trie.Root
+ trie.Update("dude", LONG_WORD)
+ trie.Delete("dude")
+ if !reflect.DeepEqual(exp, trie.Root) {
+ t.Errorf("Expected tries to be equal %x : %x", exp, trie.Root)
+ }
+}
+
+func TestTrieDeleteWithValue(t *testing.T) {
+ _, trie := New()
+ trie.Update("c", LONG_WORD)
+ exp := trie.Root
+ trie.Update("ca", LONG_WORD)
+ trie.Update("cat", LONG_WORD)
+ trie.Delete("ca")
+ trie.Delete("cat")
+ if !reflect.DeepEqual(exp, trie.Root) {
+ t.Errorf("Expected tries to be equal %x : %x", exp, trie.Root)
+ }
+
+}
+
+func TestTrieIterator(t *testing.T) {
+ _, trie := New()
+ trie.Update("c", LONG_WORD)
+ trie.Update("ca", LONG_WORD)
+ trie.Update("cat", LONG_WORD)
+
+ lenBefore := len(trie.cache.nodes)
+ it := trie.NewIterator()
+ if num := it.Purge(); num != 3 {
+ t.Errorf("Expected purge to return 3, got %d", num)
+ }
+
+ if lenBefore == len(trie.cache.nodes) {
+ t.Errorf("Expected cached nodes to be deleted")
+ }
+}
diff --git a/ethutil/value.go b/ethutil/value.go
index 2a990783e..3dd84d12d 100644
--- a/ethutil/value.go
+++ b/ethutil/value.go
@@ -36,7 +36,8 @@ func (val *Value) Len() int {
if data, ok := val.Val.([]interface{}); ok {
return len(data)
} else if data, ok := val.Val.([]byte); ok {
- // FIXME
+ return len(data)
+ } else if data, ok := val.Val.(string); ok {
return len(data)
}
@@ -60,6 +61,10 @@ func (val *Value) Uint() uint64 {
return uint64(Val)
} else if Val, ok := val.Val.(uint64); ok {
return Val
+ } else if Val, ok := val.Val.(int); ok {
+ return uint64(Val)
+ } else if Val, ok := val.Val.(uint); ok {
+ return uint64(Val)
} else if Val, ok := val.Val.([]byte); ok {
return ReadVarint(bytes.NewReader(Val))
}
@@ -80,6 +85,8 @@ func (val *Value) BigInt() *big.Int {
b := new(big.Int).SetBytes(a)
return b
+ } else if a, ok := val.Val.(*big.Int); ok {
+ return a
} else {
return big.NewInt(int64(val.Uint()))
}
@@ -92,6 +99,8 @@ func (val *Value) Str() string {
return string(a)
} else if a, ok := val.Val.(string); ok {
return a
+ } else if a, ok := val.Val.(byte); ok {
+ return string(a)
}
return ""
@@ -102,7 +111,7 @@ func (val *Value) Bytes() []byte {
return a
}
- return make([]byte, 0)
+ return []byte{}
}
func (val *Value) Slice() []interface{} {
@@ -131,6 +140,19 @@ func (val *Value) SliceFromTo(from, to int) *Value {
return NewValue(slice[from:to])
}
+// TODO More type checking methods
+func (val *Value) IsSlice() bool {
+ return val.Type() == reflect.Slice
+}
+
+func (val *Value) IsStr() bool {
+ return val.Type() == reflect.String
+}
+
+func (val *Value) IsEmpty() bool {
+ return val.Val == nil || ((val.IsSlice() || val.IsStr()) && val.Len() == 0)
+}
+
// Threat the value as a slice
func (val *Value) Get(idx int) *Value {
if d, ok := val.Val.([]interface{}); ok {
@@ -140,7 +162,7 @@ func (val *Value) Get(idx int) *Value {
}
if idx < 0 {
- panic("negative idx for Rlp Get")
+ panic("negative idx for Value Get")
}
return NewValue(d[idx])
@@ -158,9 +180,9 @@ func (val *Value) Encode() []byte {
return Encode(val.Val)
}
-func NewValueFromBytes(rlpData []byte) *Value {
- if len(rlpData) != 0 {
- data, _ := Decode(rlpData, 0)
+func NewValueFromBytes(data []byte) *Value {
+ if len(data) != 0 {
+ data, _ := Decode(data, 0)
return NewValue(data)
}
diff --git a/ethutil/value_test.go b/ethutil/value_test.go
new file mode 100644
index 000000000..0e2da5328
--- /dev/null
+++ b/ethutil/value_test.go
@@ -0,0 +1,52 @@
+package ethutil
+
+import (
+ "bytes"
+ "math/big"
+ "testing"
+)
+
+func TestValueCmp(t *testing.T) {
+ val1 := NewValue("hello")
+ val2 := NewValue("world")
+ if val1.Cmp(val2) {
+ t.Error("Expected values not to be equal")
+ }
+
+ val3 := NewValue("hello")
+ val4 := NewValue("hello")
+ if !val3.Cmp(val4) {
+ t.Error("Expected values to be equal")
+ }
+}
+
+func TestValueTypes(t *testing.T) {
+ str := NewValue("str")
+ num := NewValue(1)
+ inter := NewValue([]interface{}{1})
+ byt := NewValue([]byte{1, 2, 3, 4})
+ bigInt := NewValue(big.NewInt(10))
+
+ if str.Str() != "str" {
+ t.Errorf("expected Str to return 'str', got %s", str.Str())
+ }
+
+ if num.Uint() != 1 {
+ t.Errorf("expected Uint to return '1', got %d", num.Uint())
+ }
+
+ interExp := []interface{}{1}
+ if !NewValue(inter.Interface()).Cmp(NewValue(interExp)) {
+ t.Errorf("expected Interface to return '%v', got %v", interExp, num.Interface())
+ }
+
+ bytExp := []byte{1, 2, 3, 4}
+ if bytes.Compare(byt.Bytes(), bytExp) != 0 {
+ t.Errorf("expected Bytes to return '%v', got %v", bytExp, byt.Bytes())
+ }
+
+ bigExp := big.NewInt(10)
+ if bigInt.BigInt().Cmp(bigExp) != 0 {
+ t.Errorf("expected BigInt to return '%v', got %v", bigExp, bigInt.BigInt())
+ }
+}
diff --git a/ethwire/messaging.go b/ethwire/messaging.go
index 651bf4710..185faa341 100644
--- a/ethwire/messaging.go
+++ b/ethwire/messaging.go
@@ -19,6 +19,9 @@ var MagicToken = []byte{34, 64, 8, 145}
type MsgType byte
const (
+ // Values are given explicitly instead of by iota because these values are
+ // defined by the wire protocol spec; it is easier for humans to ensure
+ // correctness when values are explicit.
MsgHandshakeTy = 0x00
MsgDiscTy = 0x01
MsgPingTy = 0x02
diff --git a/peer.go b/peer.go
index 7b17b8b09..a8708206f 100644
--- a/peer.go
+++ b/peer.go
@@ -6,7 +6,6 @@ import (
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
- "log"
"net"
"runtime"
"strconv"
@@ -23,6 +22,9 @@ const (
type DiscReason byte
const (
+ // Values are given explicitly instead of by iota because these values are
+ // defined by the wire protocol spec; it is easier for humans to ensure
+ // correctness when values are explicit.
DiscReRequested = 0x00
DiscReTcpSysErr = 0x01
DiscBadProto = 0x02
@@ -56,9 +58,9 @@ func (d DiscReason) String() string {
type Caps byte
const (
- CapPeerDiscTy = 0x01
- CapTxTy = 0x02
- CapChainTy = 0x04
+ CapPeerDiscTy = 1 << iota
+ CapTxTy
+ CapChainTy
CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy
)
@@ -162,7 +164,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
conn, err := net.DialTimeout("tcp", addr, 30*time.Second)
if err != nil {
- log.Println("Connection to peer failed", err)
+ ethutil.Config.Log.Debugln("Connection to peer failed", err)
p.Stop()
return
}
@@ -199,7 +201,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) {
err := ethwire.WriteMessage(p.conn, msg)
if err != nil {
- log.Println("Can't send message:", err)
+ ethutil.Config.Log.Debugln("Can't send message:", err)
// Stop the client if there was an error writing to it
p.Stop()
return
@@ -261,7 +263,7 @@ func (p *Peer) HandleInbound() {
// Wait for a message from the peer
msgs, err := ethwire.ReadMessages(p.conn)
if err != nil {
- log.Println(err)
+ ethutil.Config.Log.Debugln(err)
}
for _, msg := range msgs {
switch msg.Type {
@@ -274,7 +276,7 @@ func (p *Peer) HandleInbound() {
}
case ethwire.MsgDiscTy:
p.Stop()
- log.Println("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint()))
+ ethutil.Config.Log.Infoln("Disconnect peer:", DiscReason(msg.Data.Get(0).Uint()))
case ethwire.MsgPingTy:
// Respond back with pong
p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
@@ -285,7 +287,6 @@ func (p *Peer) HandleInbound() {
p.lastPong = time.Now().Unix()
case ethwire.MsgBlockTy:
// Get all blocks and process them
- msg.Data = msg.Data
var block, lastBlock *ethchain.Block
var err error
for i := msg.Data.Len() - 1; i >= 0; i-- {
@@ -293,7 +294,10 @@ func (p *Peer) HandleInbound() {
err = p.ethereum.BlockManager.ProcessBlock(block)
if err != nil {
- log.Println("bckmsg", err)
+ if ethutil.Config.Debug {
+ ethutil.Config.Log.Infof("[PEER] Block %x failed\n", block.Hash())
+ ethutil.Config.Log.Infof("[PEER] %v\n", err)
+ }
break
} else {
lastBlock = block
@@ -303,7 +307,7 @@ func (p *Peer) HandleInbound() {
if err != nil {
// If the parent is unknown try to catch up with this peer
if ethchain.IsParentErr(err) {
- log.Println("Attempting to catch up")
+ ethutil.Config.Log.Infoln("Attempting to catch up")
p.catchingUp = false
p.CatchupWithPeer()
} else if ethchain.IsValidationErr(err) {
@@ -315,7 +319,7 @@ func (p *Peer) HandleInbound() {
if p.catchingUp && msg.Data.Len() > 1 {
if ethutil.Config.Debug && lastBlock != nil {
blockInfo := lastBlock.BlockInfo()
- log.Printf("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
+ ethutil.Config.Log.Infof("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash)
}
p.catchingUp = false
p.CatchupWithPeer()
@@ -386,12 +390,12 @@ func (p *Peer) HandleInbound() {
p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
}
case ethwire.MsgNotInChainTy:
- log.Printf("Not in chain %x\n", msg.Data)
+ ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data)
// TODO
// Unofficial but fun nonetheless
case ethwire.MsgTalkTy:
- log.Printf("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str())
+ ethutil.Config.Log.Infoln("%v says: %s\n", p.conn.RemoteAddr(), msg.Data.Str())
}
}
}
@@ -434,7 +438,7 @@ func (p *Peer) Start() {
err := p.pushHandshake()
if err != nil {
- log.Printf("Peer can't send outbound version ack", err)
+ ethutil.Config.Log.Debugln("Peer can't send outbound version ack", err)
p.Stop()
@@ -465,7 +469,7 @@ func (p *Peer) pushHandshake() error {
pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes()
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
- uint32(4), uint32(0), p.Version, byte(p.caps), p.port, pubkey,
+ uint32(5), uint32(0), p.Version, byte(p.caps), p.port, pubkey,
})
p.QueueMessage(msg)
@@ -492,8 +496,8 @@ func (p *Peer) pushPeers() {
func (p *Peer) handleHandshake(msg *ethwire.Msg) {
c := msg.Data
- if c.Get(0).Uint() != 4 {
- log.Println("Invalid peer version. Require protocol v4")
+ if c.Get(0).Uint() != 5 {
+ ethutil.Config.Log.Debugln("Invalid peer version. Require protocol v5")
p.Stop()
return
}
@@ -525,7 +529,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
// Get a reference to the peers version
p.Version = c.Get(2).Str()
- log.Println(p)
+ ethutil.Config.Log.Debugln("[PEER]", p)
}
func (p *Peer) String() string {
@@ -542,7 +546,7 @@ func (p *Peer) String() string {
strConnectType = "disconnected"
}
- return fmt.Sprintf("peer [%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.Version, p.caps)
+ return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.Version, p.caps)
}
@@ -552,7 +556,7 @@ func (p *Peer) CatchupWithPeer() {
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash(), uint64(50)})
p.QueueMessage(msg)
- log.Printf("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])
}
}