Merge branch 'release/0.3.0'

This commit is contained in:
obscuren 2014-02-28 12:21:12 +01:00
commit 839bd73fbb
40 changed files with 1826 additions and 850 deletions

View File

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

60
ethchain/address.go Normal file
View File

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

8
ethchain/address_test.go Normal file
View File

@ -0,0 +1,8 @@
package ethchain
import (
"testing"
)
func TestAddressState(t *testing.T) {
}

View File

@ -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
}
func (block *Block) MakeContract(tx *Transaction) {
// Create contract if there's no recipient
if tx.IsContract() {
addr := tx.Hash()
// 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()
}
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)
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) {
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 /////////////////

View File

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

View File

@ -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)
}
}()
/*
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
vm := &Vm{}
vm.Process(contract, NewState(block.state), RuntimeVars{
address: tx.Hash()[12:],
blockNumber: block.BlockInfo().Number,
sender: tx.Sender(),
prevHash: block.PrevHash,
coinbase: block.Coinbase,
time: block.Time,
diff: block.Difficulty,
txValue: tx.Value,
txData: tx.Data,
})
}
// Contract evaluation is done here.
func (bm *BlockManager) ProcContract(tx *Transaction, block *Block, cb TxCallback) {
// 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++
}
// 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()
}

View File

@ -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()
ethutil.ReadConfig("")
db, _ := NewMemDatabase()
Db = db
db, _ := ethdb.NewMemDatabase()
ethutil.Config.Db = db
bm := NewBlockManager(nil)
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",
block := bm.bc.genesisBlock
script := Compile([]string{
"PUSH",
"1",
"PUSH",
"2",
})
tx := NewTransaction("1e8a42ea8cce13", 100, []string{})
tx := NewTransaction(ContractAddr, big.NewInt(200000000), script)
addr := tx.Hash()[12:]
bm.ApplyTransactions(block, []*Transaction{tx})
block := CreateBlock("", 0, "", "c014ba53", 0, 0, "", []*Transaction{ctrct, tx})
db.Put(block.Hash(), block.RlpEncode())
bm := NewBlockManager()
bm.ProcessBlock(block)
tx2 := NewTransaction(addr, big.NewInt(1e17), nil)
tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
bm.ApplyTransactions(block, []*Transaction{tx2})
}
*/

View File

@ -30,37 +30,42 @@ func (c *Contract) RlpDecode(data []byte) {
c.state = ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())
}
func (c *Contract) Addr(addr []byte) *ethutil.Value {
return ethutil.NewValueFromBytes([]byte(c.state.Get(string(addr))))
}
func (c *Contract) SetAddr(addr []byte, value interface{}) {
c.state.Update(string(addr), string(ethutil.NewValue(value).Encode()))
}
func (c *Contract) State() *ethutil.Trie {
return c.state
}
type Address struct {
Amount *big.Int
Nonce uint64
func (c *Contract) GetMem(num int) *ethutil.Value {
nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256)
return c.Addr(nb)
}
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()
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
}

View File

@ -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 BlockReward *big.Int = big.NewInt(1500000000000000000)
var TxFee *big.Int = big.NewInt(100)
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 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)

View File

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

View File

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

56
ethchain/state.go Normal file
View File

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

View File

@ -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 {

View File

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

View File

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

436
ethchain/vm.go Normal file
View File

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

106
ethchain/vm_test.go Normal file
View File

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

View File

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

View File

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

View File

@ -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,29 +117,33 @@ 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
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 alreadyConnected {
return nil
}
phost, _, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
ahost, _, _ := net.SplitHostPort(addr)
if phost == ahost {
alreadyConnected = true
return
}
})
peer := NewOutboundPeer(addr, s, s.serverCaps)
if alreadyConnected {
return nil
s.peers.PushBack(peer)
log.Printf("[SERV] Adding peer %d / %d\n", s.peers.Len(), s.MaxPeers)
}
peer := NewOutboundPeer(addr, s, s.serverCaps)
s.peers.PushBack(peer)
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")
}
}

View File

@ -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.

View File

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

35
ethutil/common.go Normal file
View File

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

17
ethutil/common_test.go Normal file
View File

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

View File

@ -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,20 +81,29 @@ 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) Debugln(v ...interface{}) {
if log.logLevel != 0 {
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
}
@ -95,8 +112,29 @@ func (log Logger) Debugln(v ...interface{}) {
}
}
func (log Logger) Debugf(format string, v ...interface{}) {
if log.logLevel != 0 {
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) Infoln(v ...interface{}) {
if log.logLevel > LogLevelInfo {
return
}
fmt.Println(len(log.logSys))
for _, logger := range log.logSys {
logger.Println(v...)
}
}
func (log *Logger) Infof(format string, v ...interface{}) {
if log.logLevel > LogLevelInfo {
return
}

View File

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

View File

@ -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 {

View File

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

View File

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

19
ethutil/key.go Normal file
View File

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

View File

@ -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
}
func CompileInstr(s string) ([]byte, error) {
isOp := IsOpCode(s)
if isOp {
return []byte{OpCodes[s]}, nil
}
code := OpCodes[tokens[0]] // Replace op codes with the proper numerical equivalent
op := new(big.Int)
op.SetString(code, 0)
num := new(big.Int)
num.SetString(s, 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))
}
base.Add(op, x)
base.Add(base, y)
base.Add(base, z)
base.Add(base, a)
base.Add(base, b)
base.Add(base, c)
return base.String(), nil
return num.Bytes(), nil
}
func Instr(instr string) (int, []string, error) {
base := new(big.Int)
base.SetString(instr, 0)

View File

@ -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) {
}
*/

View File

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

View File

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

View File

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

View File

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

View File

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

52
ethutil/value_test.go Normal file
View File

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

View File

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

46
peer.go
View File

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