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) 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal 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 // List of transactions and/or contracts
transactions []*Transaction transactions []*Transaction
TxSha []byte TxSha []byte
contractStates map[string]*ethutil.Trie
} }
// New block takes a raw encoded string // New block takes a raw encoded string
@ -79,14 +81,15 @@ func CreateBlock(root interface{},
block := &Block{ block := &Block{
// Slice of transactions to include in this block // Slice of transactions to include in this block
transactions: txes, transactions: txes,
PrevHash: prevHash, PrevHash: prevHash,
Coinbase: base, Coinbase: base,
Difficulty: Difficulty, Difficulty: Difficulty,
Nonce: Nonce, Nonce: Nonce,
Time: time.Now().Unix(), Time: time.Now().Unix(),
Extra: extra, Extra: extra,
UncleSha: EmptyShaList, UncleSha: EmptyShaList,
contractStates: make(map[string]*ethutil.Trie),
} }
block.SetTransactions(txes) block.SetTransactions(txes)
block.SetUncles([]*Block{}) block.SetUncles([]*Block{})
@ -128,14 +131,26 @@ func (block *Block) GetContract(addr []byte) *Contract {
return nil return nil
} }
value := ethutil.NewValueFromBytes([]byte(data))
if value.Len() == 2 {
return nil
}
contract := &Contract{} contract := &Contract{}
contract.RlpDecode([]byte(data)) contract.RlpDecode([]byte(data))
cachedState := block.contractStates[string(addr)]
if cachedState != nil {
contract.state = cachedState
} else {
block.contractStates[string(addr)] = contract.state
}
return contract return contract
} }
func (block *Block) UpdateContract(addr []byte, contract *Contract) { func (block *Block) UpdateContract(addr []byte, contract *Contract) {
// Make sure the state is synced // Make sure the state is synced
contract.State().Sync() //contract.State().Sync()
block.state.Update(string(addr), string(contract.RlpEncode())) block.state.Update(string(addr), string(contract.RlpEncode()))
} }
@ -190,18 +205,29 @@ func (block *Block) BlockInfo() BlockInfo {
return bi return bi
} }
func (block *Block) MakeContract(tx *Transaction) { // Sync the block's state and contract respectively
// Create contract if there's no recipient func (block *Block) Sync() {
if tx.IsContract() { // Sync all contracts currently in cache
addr := tx.Hash() for _, val := range block.contractStates {
val.Sync()
}
// Sync the block state itself
block.state.Sync()
}
value := tx.Value func (block *Block) Undo() {
contract := NewContract(value, []byte("")) // Sync all contracts currently in cache
block.state.Update(string(addr), string(contract.RlpEncode())) for _, val := range block.contractStates {
for i, val := range tx.Data { val.Undo()
contract.state.Update(string(ethutil.NumberToBytes(uint64(i), 32)), val) }
} // Sync the block state itself
block.UpdateContract(addr, contract) 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.Time = int64(header.Get(6).BigInt().Uint64())
block.Extra = header.Get(7).Str() block.Extra = header.Get(7).Str()
block.Nonce = header.Get(8).Bytes() 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 // Tx list might be empty if this is an uncle. Uncles only have their
// header set. // header set.
@ -298,12 +325,6 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) {
tx := NewTransactionFromValue(txes.Get(i)) tx := NewTransactionFromValue(txes.Get(i))
block.transactions[i] = tx 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 { 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 ///////////////// //////////// UNEXPORTED /////////////////

View File

@ -104,7 +104,6 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
currentHash = block.PrevHash currentHash = block.PrevHash
chain = append(chain, block.Value().Val) chain = append(chain, block.Value().Val)
//chain = append([]interface{}{block.RlpValue().Value}, chain...)
num-- num--
} }
@ -112,6 +111,24 @@ func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
return chain 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() { func (bc *BlockChain) setLastBlock() {
data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 { if len(data) != 0 {
@ -141,11 +158,16 @@ func (bc *BlockChain) Add(block *Block) {
bc.CurrentBlock = block bc.CurrentBlock = block
bc.LastBlockHash = block.Hash() 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 { func (bc *BlockChain) GetBlock(hash []byte) *Block {
data, _ := ethutil.Config.Db.Get(hash) data, _ := ethutil.Config.Db.Get(hash)
if len(data) == 0 {
return nil
}
return NewBlockFromData(data) return NewBlockFromData(data)
} }
@ -177,8 +199,6 @@ func (bc *BlockChain) writeBlockInfo(block *Block) {
func (bc *BlockChain) Stop() { func (bc *BlockChain) Stop() {
if bc.CurrentBlock != nil { if bc.CurrentBlock != nil {
ethutil.Config.Db.Put([]byte("LastBlock"), bc.CurrentBlock.RlpEncode())
log.Println("[CHAIN] Stopped") log.Println("[CHAIN] Stopped")
} }
} }

View File

@ -5,11 +5,9 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go" _ "github.com/ethereum/eth-go/ethwire"
"log" "log"
"math"
"math/big" "math/big"
"strconv"
"sync" "sync"
"time" "time"
) )
@ -18,10 +16,7 @@ type BlockProcessor interface {
ProcessBlock(block *Block) ProcessBlock(block *Block)
} }
func CalculateBlockReward(block *Block, uncleLength int) *big.Int { // TODO rename to state manager
return BlockReward
}
type BlockManager struct { type BlockManager struct {
// Mutex for locking the block processor. Blocks can only be handled one at a time // Mutex for locking the block processor. Blocks can only be handled one at a time
mutex sync.Mutex mutex sync.Mutex
@ -29,6 +24,10 @@ type BlockManager struct {
// The block chain :) // The block chain :)
bc *BlockChain bc *BlockChain
// States for addresses. You can watch any address
// at any given time
addrStateStore *AddrStateStore
// Stack for processing contracts // Stack for processing contracts
stack *Stack stack *Stack
// non-persistent key/value memory storage // non-persistent key/value memory storage
@ -46,9 +45,9 @@ type BlockManager struct {
func AddTestNetFunds(block *Block) { func AddTestNetFunds(block *Block) {
for _, addr := range []string{ for _, addr := range []string{
"8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin "8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin
"93658b04240e4bd4046fd2d6d417d20f146f4b43", // Jeffrey "e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey
"1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit
"80c01a26338f0d905e295fccb71fa9ea849ffa12", // Alex "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex
} { } {
//log.Println("2^200 Wei to", addr) //log.Println("2^200 Wei to", addr)
codedAddr, _ := hex.DecodeString(addr) codedAddr, _ := hex.DecodeString(addr)
@ -61,26 +60,47 @@ func AddTestNetFunds(block *Block) {
func NewBlockManager(speaker PublicSpeaker) *BlockManager { func NewBlockManager(speaker PublicSpeaker) *BlockManager {
bm := &BlockManager{ bm := &BlockManager{
//server: s, //server: s,
bc: NewBlockChain(), bc: NewBlockChain(),
stack: NewStack(), stack: NewStack(),
mem: make(map[string]*big.Int), mem: make(map[string]*big.Int),
Pow: &EasyPow{}, Pow: &EasyPow{},
Speaker: speaker, Speaker: speaker,
addrStateStore: NewAddrStateStore(),
} }
if bm.bc.CurrentBlock == nil { if bm.bc.CurrentBlock == nil {
AddTestNetFunds(bm.bc.genesisBlock) AddTestNetFunds(bm.bc.genesisBlock)
bm.bc.genesisBlock.State().Sync()
// Prepare the genesis block // Prepare the genesis block
bm.bc.Add(bm.bc.genesisBlock) 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) //log.Printf("root %x\n", bm.bc.genesisBlock.State().Root)
//bm.bc.genesisBlock.PrintHash() //bm.bc.genesisBlock.PrintHash()
} }
log.Printf("Last block: %x\n", bm.bc.CurrentBlock.Hash())
return bm 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 { func (bm *BlockManager) BlockChain() *BlockChain {
return bm.bc 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 there's no recipient, it's a contract
if tx.IsContract() { if tx.IsContract() {
block.MakeContract(tx) block.MakeContract(tx)
bm.ProcessContract(tx, block)
} else { } 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 // Processing a blocks may never happen simultaneously
bm.mutex.Lock() bm.mutex.Lock()
defer bm.mutex.Unlock() 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() hash := block.Hash()
@ -110,12 +141,6 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
return nil 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 // 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 // Reasons might be catching up or simply an invalid block
if !bm.bc.HasBlock(block.PrevHash) && bm.bc.CurrentBlock != nil { 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().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) 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 // Calculate the new total difficulty and sync back to the db
if bm.CalculateTD(block) { if bm.CalculateTD(block) {
// Sync the current block's state to the database // Sync the current block's state to the database and cancelling out the deferred Undo
bm.bc.CurrentBlock.State().Sync() bm.bc.CurrentBlock.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())
}
}
*/
// Broadcast the valid block back to the wire // 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 // If there's a block processor present, pass in the block for further
// processing // processing
@ -175,7 +182,7 @@ func (bm *BlockManager) ProcessBlock(block *Block) error {
bm.SecondaryBlockProcessor.ProcessBlock(block) 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 { } else {
fmt.Println("total diff failed") fmt.Println("total diff failed")
} }
@ -246,6 +253,18 @@ func (bm *BlockManager) ValidateBlock(block *Block) error {
return nil 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 { func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error {
// Get the coinbase rlp data // Get the coinbase rlp data
addr := processor.GetAddr(block.Coinbase) addr := processor.GetAddr(block.Coinbase)
@ -254,7 +273,12 @@ func (bm *BlockManager) AccumelateRewards(processor *Block, block *Block) error
processor.UpdateAddr(block.Coinbase, addr) 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 return nil
} }
@ -263,363 +287,26 @@ func (bm *BlockManager) Stop() {
bm.bc.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 // Recovering function in case the VM had any errors
defer func() { /*
if r := recover(); r != nil { defer func() {
fmt.Println("Recovered from VM execution with err =", r) if r := recover(); r != nil {
} fmt.Println("Recovered from VM execution with err =", r)
}() }
}()
*/
// Process contract vm := &Vm{}
bm.ProcContract(tx, block, func(opType OpType) bool { vm.Process(contract, NewState(block.state), RuntimeVars{
// TODO turn on once big ints are in place address: tx.Hash()[12:],
//if !block.PayFee(tx.Hash(), StepFee.Uint64()) { blockNumber: block.BlockInfo().Number,
// return false sender: tx.Sender(),
//} prevHash: block.PrevHash,
coinbase: block.Coinbase,
return true // Continue 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 package ethchain
/*
import ( import (
_ "fmt" _ "fmt"
"github.com/ethereum/eth-go/ethdb"
"github.com/ethereum/eth-go/ethutil"
"math/big"
"testing" "testing"
) )
func TestVm(t *testing.T) { func TestVm(t *testing.T) {
InitFees() InitFees()
ethutil.ReadConfig("")
db, _ := NewMemDatabase() db, _ := ethdb.NewMemDatabase()
Db = db ethutil.Config.Db = db
bm := NewBlockManager(nil)
ctrct := NewTransaction("", 200000000, []string{ block := bm.bc.genesisBlock
"PUSH", "1a2f2e", script := Compile([]string{
"PUSH", "hallo", "PUSH",
"POP", // POP hallo "1",
"PUSH", "3", "PUSH",
"LOAD", // Load hallo back on the stack "2",
"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",
}) })
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}) tx2 := NewTransaction(addr, big.NewInt(1e17), nil)
db.Put(block.Hash(), block.RlpEncode()) tx2.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"))
bm.ApplyTransactions(block, []*Transaction{tx2})
bm := NewBlockManager()
bm.ProcessBlock(block)
} }
*/

View File

@ -30,37 +30,42 @@ func (c *Contract) RlpDecode(data []byte) {
c.state = ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface()) 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 { func (c *Contract) State() *ethutil.Trie {
return c.state return c.state
} }
type Address struct { func (c *Contract) GetMem(num int) *ethutil.Value {
Amount *big.Int nb := ethutil.BigToBytes(big.NewInt(int64(num)), 256)
Nonce uint64
return c.Addr(nb)
} }
func NewAddress(amount *big.Int) *Address { func MakeContract(tx *Transaction, state *State) *Contract {
return &Address{Amount: amount, Nonce: 0} // Create contract if there's no recipient
} if tx.IsContract() {
addr := tx.Hash()[12:]
func NewAddressFromData(data []byte) *Address {
address := &Address{} value := tx.Value
address.RlpDecode(data) contract := NewContract(value, []byte(""))
state.trie.Update(string(addr), string(contract.RlpEncode()))
return address for i, val := range tx.Data {
} if len(val) > 0 {
bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256)
func (a *Address) AddFee(fee *big.Int) { contract.state.Update(string(bytNum), string(ethutil.Encode(val)))
a.Amount.Add(a.Amount, fee) }
} }
state.trie.Update(string(addr), string(contract.RlpEncode()))
func (a *Address) RlpEncode() []byte {
return ethutil.Encode([]interface{}{a.Amount, a.Nonce}) return contract
} }
func (a *Address) RlpDecode(data []byte) { return nil
decoder := ethutil.NewValueFromBytes(data)
a.Amount = decoder.Get(0).BigInt()
a.Nonce = decoder.Get(1).Uint()
} }

View File

@ -4,22 +4,32 @@ import (
"math/big" "math/big"
) )
var StepFee *big.Int = new(big.Int)
var TxFeeRat *big.Int = big.NewInt(100000000000000) 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 Period1Reward *big.Int = new(big.Int)
var Period2Reward *big.Int = new(big.Int) var Period2Reward *big.Int = new(big.Int)
var Period3Reward *big.Int = new(big.Int) var Period3Reward *big.Int = new(big.Int)
var Period4Reward *big.Int = new(big.Int) var Period4Reward *big.Int = new(big.Int)
func InitFees() { 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 // Base for 2**64
b60 := new(big.Int) b60 := new(big.Int)

View File

@ -13,7 +13,7 @@ var ZeroHash256 = make([]byte, 32)
var ZeroHash160 = make([]byte, 20) var ZeroHash160 = make([]byte, 20)
var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{})) var EmptyShaList = ethutil.Sha3Bin(ethutil.Encode([]interface{}{}))
var GenisisHeader = []interface{}{ var GenesisHeader = []interface{}{
// Previous hash (none) // Previous hash (none)
//"", //"",
ZeroHash256, ZeroHash256,
@ -36,4 +36,4 @@ var GenisisHeader = []interface{}{
ethutil.Sha3Bin(big.NewInt(42).Bytes()), 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 // Op codes
const ( const (
oSTOP OpCode = iota oSTOP = 0x00
oADD oADD = 0x01
oMUL oMUL = 0x02
oSUB oSUB = 0x03
oDIV oDIV = 0x04
oSDIV oSDIV = 0x05
oMOD oMOD = 0x06
oSMOD oSMOD = 0x07
oEXP oEXP = 0x08
oNEG oNEG = 0x09
oLT oLT = 0x0a
oLE oLE = 0x0b
oGT oGT = 0x0c
oGE oGE = 0x0d
oEQ oEQ = 0x0e
oNOT oNOT = 0x0f
oMYADDRESS oMYADDRESS = 0x10
oTXSENDER oTXSENDER = 0x11
oTXVALUE oTXVALUE = 0x12
oTXFEE oTXDATAN = 0x13
oTXDATAN oTXDATA = 0x14
oTXDATA oBLK_PREVHASH = 0x15
oBLK_PREVHASH oBLK_COINBASE = 0x16
oBLK_COINBASE oBLK_TIMESTAMP = 0x17
oBLK_TIMESTAMP oBLK_NUMBER = 0x18
oBLK_NUMBER oBLK_DIFFICULTY = 0x19
oBLK_DIFFICULTY oBLK_NONCE = 0x1a
oBASEFEE oBASEFEE = 0x1b
oSHA256 OpCode = 32 oSHA256 = 0x20
oRIPEMD160 OpCode = 33 oRIPEMD160 = 0x21
oECMUL OpCode = 34 oECMUL = 0x22
oECADD OpCode = 35 oECADD = 0x23
oECSIGN OpCode = 36 oECSIGN = 0x24
oECRECOVER OpCode = 37 oECRECOVER = 0x25
oECVALID OpCode = 38 oECVALID = 0x26
oSHA3 OpCode = 39 oSHA3 = 0x27
oPUSH OpCode = 48 oPUSH = 0x30
oPOP OpCode = 49 oPOP = 0x31
oDUP OpCode = 50 oDUP = 0x32
oSWAP OpCode = 51 oSWAP = 0x33
oMLOAD OpCode = 52 oMLOAD = 0x34
oMSTORE OpCode = 53 oMSTORE = 0x35
oSLOAD OpCode = 54 oSLOAD = 0x36
oSSTORE OpCode = 55 oSSTORE = 0x37
oJMP OpCode = 56 oJMP = 0x38
oJMPI OpCode = 57 oJMPI = 0x39
oIND OpCode = 58 oIND = 0x3a
oEXTRO OpCode = 59 oEXTRO = 0x3b
oBALANCE OpCode = 60 oBALANCE = 0x3c
oMKTX OpCode = 61 oMKTX = 0x3d
oSUICIDE OpCode = 62 oSUICIDE = 0x3f
) )
// Since the opcodes aren't all in order we can't use a regular slice // 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", oMYADDRESS: "MYADDRESS",
oTXSENDER: "TXSENDER", oTXSENDER: "TXSENDER",
oTXVALUE: "TXVALUE", oTXVALUE: "TXVALUE",
oTXFEE: "TXFEE",
oTXDATAN: "TXDATAN", oTXDATAN: "TXDATAN",
oTXDATA: "TXDATA", oTXDATA: "TXDATA",
oBLK_PREVHASH: "BLK_PREVHASH", oBLK_PREVHASH: "BLK_PREVHASH",
@ -159,9 +158,33 @@ func (st *Stack) Popn() (*big.Int, *big.Int) {
return ints[0], ints[1] 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) { func (st *Stack) Push(d *big.Int) {
st.data = append(st.data, d) st.data = append(st.data, d)
} }
func (st *Stack) Print() { 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 package ethchain
import ( import (
"bytes"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/obscuren/secp256k1-go" "github.com/obscuren/secp256k1-go"
"math/big" "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 { type Transaction struct {
Nonce uint64 Nonce uint64
Recipient []byte Recipient []byte
@ -21,20 +24,17 @@ func NewTransaction(to []byte, value *big.Int, data []string) *Transaction {
tx.Nonce = 0 tx.Nonce = 0
// Serialize the data // Serialize the data
tx.Data = make([]string, len(data)) tx.Data = 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
}
return &tx return &tx
} }
// XXX Deprecated
func NewTransactionFromData(data []byte) *Transaction { func NewTransactionFromData(data []byte) *Transaction {
return NewTransactionFromBytes(data)
}
func NewTransactionFromBytes(data []byte) *Transaction {
tx := &Transaction{} tx := &Transaction{}
tx.RlpDecode(data) tx.RlpDecode(data)
@ -65,7 +65,7 @@ func (tx *Transaction) Hash() []byte {
} }
func (tx *Transaction) IsContract() bool { func (tx *Transaction) IsContract() bool {
return len(tx.Recipient) == 0 return bytes.Compare(tx.Recipient, ContractAddr) == 0
} }
func (tx *Transaction) Signature(key []byte) []byte { func (tx *Transaction) Signature(key []byte) []byte {

View File

@ -17,6 +17,17 @@ const (
) )
type TxPoolHook chan *Transaction 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 { func FindTx(pool *list.List, finder func(*Transaction, *list.Element) bool) *Transaction {
for e := pool.Front(); e != nil; e = e.Next() { for e := pool.Front(); e != nil; e = e.Next() {
@ -34,6 +45,10 @@ type PublicSpeaker interface {
Broadcast(msgType ethwire.MsgType, data []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 // 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 // guarantee a non blocking pool we use a queue channel which can be
// independently read without needing access to the actual pool. If the // independently read without needing access to the actual pool. If the
@ -54,7 +69,9 @@ type TxPool struct {
BlockManager *BlockManager BlockManager *BlockManager
Hook TxPoolHook SecondaryProcessor TxProcessor
subscribers []chan TxMsg
} }
func NewTxPool() *TxPool { func NewTxPool() *TxPool {
@ -80,8 +97,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) {
// Process transaction validates the Tx and processes funds from the // Process transaction validates the Tx and processes funds from the
// sender to the recipient. // sender to the recipient.
func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) { func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) {
log.Printf("[TXPL] Processing Tx %x\n", tx.Hash())
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
log.Println(r) 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 // Get the receiver
receiver := block.GetAddr(tx.Recipient) receiver := block.GetAddr(tx.Recipient)
// Add the amount to receivers account which should conclude this transaction sender.Nonce += 1
receiver.Amount.Add(receiver.Amount, tx.Value)
// 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.Sender(), sender)
block.UpdateAddr(tx.Recipient, receiver)
log.Printf("[TXPL] Processed Tx %x\n", tx.Hash())
// Notify the subscribers
pool.notifySubscribers(TxPost, tx)
return return
} }
@ -131,7 +159,8 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
} }
// Get the sender // 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)) 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 // 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 // doesn't matter since this is a goroutine
pool.addTransaction(tx) pool.addTransaction(tx)
if pool.Hook != nil { // Notify the subscribers
pool.Hook <- tx pool.notifySubscribers(TxPre, tx)
}
} }
case <-pool.quit: case <-pool.quit:
break out break out
@ -217,3 +245,14 @@ func (pool *TxPool) Stop() {
pool.Flush() 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 ( import (
"encoding/hex" "encoding/hex"
"fmt"
"github.com/ethereum/eth-go/ethutil"
"math/big" "math/big"
"testing" "testing"
) )
@ -42,13 +40,15 @@ func TestAddressRetrieval2(t *testing.T) {
tx.Sign(key) tx.Sign(key)
//data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7") //data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7")
//tx := NewTransactionFromData(data) //tx := NewTransactionFromData(data)
fmt.Println(tx.RlpValue()) /*
fmt.Println(tx.RlpValue())
fmt.Printf("rlp %x\n", tx.RlpEncode()) fmt.Printf("rlp %x\n", tx.RlpEncode())
fmt.Printf("sha rlp %x\n", tx.Hash()) 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("hex tx key %x\n", tx.PublicKey())
fmt.Printf("seder %x\n", tx.Sender()) 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 db *leveldb.DB
} }
func NewLDBDatabase() (*LDBDatabase, error) { func NewLDBDatabase(name string) (*LDBDatabase, error) {
dbPath := path.Join(ethutil.Config.ExecPath, "database") dbPath := path.Join(ethutil.Config.ExecPath, name)
// Open the db // Open the db
db, err := leveldb.OpenFile(dbPath, nil) db, err := leveldb.OpenFile(dbPath, nil)
@ -36,6 +36,14 @@ func (db *LDBDatabase) Get(key []byte) ([]byte, error) {
return db.db.Get(key, nil) 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 { func (db *LDBDatabase) LastKnownTD() []byte {
data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil) data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil)
@ -46,13 +54,19 @@ func (db *LDBDatabase) LastKnownTD() []byte {
return data return data
} }
func (db *LDBDatabase) GetKeys() []*ethutil.Key {
data, _ := db.Get([]byte("KeyRing"))
return []*ethutil.Key{ethutil.NewKeyFromBytes(data)}
}
func (db *LDBDatabase) Close() { func (db *LDBDatabase) Close() {
// Close the leveldb database // Close the leveldb database
db.db.Close() db.db.Close()
} }
func (db *LDBDatabase) Print() { func (db *LDBDatabase) Print() {
iter := db.db.NewIterator(nil) iter := db.db.NewIterator(nil, nil)
for iter.Next() { for iter.Next() {
key := iter.Key() key := iter.Key()
value := iter.Value() value := iter.Value()

View File

@ -26,6 +26,18 @@ func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return db.db[string(key)], nil 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() { func (db *MemDatabase) Print() {
for key, val := range db.db { for key, val := range db.db {
fmt.Printf("%x(%d): ", key, len(key)) fmt.Printf("%x(%d): ", key, len(key))

View File

@ -47,6 +47,7 @@ type Ethereum struct {
Nonce uint64 Nonce uint64
Addr net.Addr Addr net.Addr
Port string
peerMut sync.Mutex peerMut sync.Mutex
@ -60,7 +61,7 @@ type Ethereum struct {
} }
func New(caps Caps, usePnp bool) (*Ethereum, error) { func New(caps Caps, usePnp bool) (*Ethereum, error) {
db, err := ethdb.NewLDBDatabase() db, err := ethdb.NewLDBDatabase("database")
//db, err := ethdb.NewMemDatabase() //db, err := ethdb.NewMemDatabase()
if err != nil { if err != nil {
return nil, err return nil, err
@ -70,7 +71,7 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
if usePnp { if usePnp {
nat, err = Discover() nat, err = Discover()
if err != nil { 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, Nonce: nonce,
serverCaps: caps, serverCaps: caps,
nat: nat, nat: nat,
MaxPeers: 5,
} }
ethereum.TxPool = ethchain.NewTxPool() ethereum.TxPool = ethchain.NewTxPool()
ethereum.TxPool.Speaker = ethereum ethereum.TxPool.Speaker = ethereum
@ -94,6 +94,9 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) {
ethereum.TxPool.BlockManager = ethereum.BlockManager ethereum.TxPool.BlockManager = ethereum.BlockManager
ethereum.BlockManager.TransactionPool = ethereum.TxPool ethereum.BlockManager.TransactionPool = ethereum.TxPool
// Start the tx pool
ethereum.TxPool.Start()
return ethereum, nil return ethereum, nil
} }
@ -114,29 +117,33 @@ func (s *Ethereum) ProcessPeerList(addrs []string) {
} }
func (s *Ethereum) ConnectToPeer(addr string) error { 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) { eachPeer(s.peers, func(p *Peer, v *list.Element) {
if p.conn == nil { if p.conn == nil {
return 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 { peer := NewOutboundPeer(addr, s, s.serverCaps)
alreadyConnected = true
return
}
})
if alreadyConnected { s.peers.PushBack(peer)
return nil
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 return nil
} }
@ -226,12 +233,12 @@ func (s *Ethereum) ReapDeadPeerHandler() {
// Start the ethereum // Start the ethereum
func (s *Ethereum) Start() { func (s *Ethereum) Start() {
// Bind to addr and port // Bind to addr and port
ln, err := net.Listen("tcp", ":30303") ln, err := net.Listen("tcp", ":"+s.Port)
if err != nil { if err != nil {
log.Println("Connection listening disabled. Acting as client") log.Println("Connection listening disabled. Acting as client")
} else { } else {
// Starting accepting connections // Starting accepting connections
log.Println("Ready and accepting connections") ethutil.Config.Log.Infoln("Ready and accepting connections")
// Start the peer handler // Start the peer handler
go s.peerHandler(ln) go s.peerHandler(ln)
} }
@ -243,13 +250,10 @@ func (s *Ethereum) Start() {
// Start the reaping processes // Start the reaping processes
go s.ReapDeadPeerHandler() go s.ReapDeadPeerHandler()
// Start the tx pool
s.TxPool.Start()
if ethutil.Config.Seed { if ethutil.Config.Seed {
log.Println("Seeding") ethutil.Config.Log.Debugln("Seeding")
// Testnet seed bootstrapping // 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 { if err != nil {
log.Println("Fetching seed failed:", err) log.Println("Fetching seed failed:", err)
return return
@ -269,7 +273,7 @@ func (s *Ethereum) peerHandler(listener net.Listener) {
for { for {
conn, err := listener.Accept() conn, err := listener.Accept()
if err != nil { if err != nil {
log.Println(err) ethutil.Config.Log.Debugln(err)
continue continue
} }
@ -303,7 +307,7 @@ func (s *Ethereum) upnpUpdateThread() {
// Go off immediately to prevent code duplication, thereafter we renew // Go off immediately to prevent code duplication, thereafter we renew
// lease every 15 minutes. // lease every 15 minutes.
timer := time.NewTimer(0 * time.Second) timer := time.NewTimer(0 * time.Second)
lport, _ := strconv.ParseInt("30303", 10, 16) lport, _ := strconv.ParseInt(s.Port, 10, 16)
first := true first := true
out: out:
for { for {
@ -312,13 +316,13 @@ out:
var err error var err error
_, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60) _, err = s.nat.AddPortMapping("TCP", int(lport), int(lport), "eth listen port", 20*60)
if err != nil { 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 break out
} }
if first && err == nil { if first && err == nil {
_, err = s.nat.GetExternalAddress() _, err = s.nat.GetExternalAddress()
if err != nil { 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 continue out
} }
first = false first = false
@ -332,8 +336,8 @@ out:
timer.Stop() timer.Stop()
if err := s.nat.DeletePortMapping("TCP", int(lport), int(lport)); err != nil { 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 { } 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 // Look up the key "do" in the trie
out := trie.Get("do") out := trie.Get("do")
fmt.Println(out) // => verb fmt.Println(out) // => verb
trie.Delete("puppy")
``` ```
The patricia trie, in combination with RLP, provides a robust, 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 `NewEmptyValue()` returns a new \*Value with it's initial value set to a
`[]interface{}` `[]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. `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 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 package ethutil
import ( import (
"fmt"
"log" "log"
"os" "os"
"os/user" "os/user"
@ -18,7 +19,7 @@ const (
type config struct { type config struct {
Db Database Db Database
Log Logger Log *Logger
ExecPath string ExecPath string
Debug bool Debug bool
Ver string Ver string
@ -34,17 +35,19 @@ func ReadConfig(base string) *config {
usr, _ := user.Current() usr, _ := user.Current()
path := path.Join(usr.HomeDir, base) path := path.Join(usr.HomeDir, base)
//Check if the logging directory already exists, create it if not if len(base) > 0 {
_, err := os.Stat(path) //Check if the logging directory already exists, create it if not
if err != nil { _, err := os.Stat(path)
if os.IsNotExist(err) { if err != nil {
log.Printf("Debug logging directory %s doesn't exist, creating it", path) if os.IsNotExist(err) {
os.Mkdir(path, 0777) 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 = &config{ExecPath: path, Debug: true, Ver: "0.3.0"}
Config.Log = NewLogger(LogFile|LogStd, 0) Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug)
} }
return Config return Config
@ -57,15 +60,20 @@ const (
LogStd = 0x2 LogStd = 0x2
) )
type LogSystem interface {
Println(v ...interface{})
Printf(format string, v ...interface{})
}
type Logger struct { type Logger struct {
logSys []*log.Logger logSys []LogSystem
logLevel int logLevel int
} }
func NewLogger(flag LoggerType, level int) Logger { func NewLogger(flag LoggerType, level int) *Logger {
var loggers []*log.Logger var loggers []LogSystem
flags := log.LstdFlags | log.Lshortfile flags := log.LstdFlags
if flag&LogFile > 0 { 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) 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.Panic("unable to create file logger", err)
} }
log := log.New(file, "[ETH]", flags) log := log.New(file, "", flags)
loggers = append(loggers, log) loggers = append(loggers, log)
} }
if flag&LogStd > 0 { if flag&LogStd > 0 {
log := log.New(os.Stdout, "[ETH]", flags) log := log.New(os.Stdout, "", flags)
loggers = append(loggers, log) loggers = append(loggers, log)
} }
return Logger{logSys: loggers, logLevel: level} return &Logger{logSys: loggers, logLevel: level}
} }
func (log Logger) Debugln(v ...interface{}) { func (log *Logger) AddLogSystem(logger LogSystem) {
if log.logLevel != 0 { log.logSys = append(log.logSys, logger)
}
const (
LogLevelDebug = iota
LogLevelInfo
)
func (log *Logger) Debugln(v ...interface{}) {
if log.logLevel != LogLevelDebug {
return return
} }
@ -95,8 +112,29 @@ func (log Logger) Debugln(v ...interface{}) {
} }
} }
func (log Logger) Debugf(format string, v ...interface{}) { func (log *Logger) Debugf(format string, v ...interface{}) {
if log.logLevel != 0 { 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 return
} }

View File

@ -4,6 +4,8 @@ package ethutil
type Database interface { type Database interface {
Put(key []byte, value []byte) Put(key []byte, value []byte)
Get(key []byte) ([]byte, error) Get(key []byte) ([]byte, error)
GetKeys() []*Key
Delete(key []byte) error
LastKnownTD() []byte LastKnownTD() []byte
Close() Close()
Print() Print()

View File

@ -3,7 +3,6 @@ package ethutil
import ( import (
"bytes" "bytes"
"encoding/hex" "encoding/hex"
_ "fmt"
"strings" "strings"
) )
@ -36,7 +35,7 @@ func CompactEncode(hexSlice []int) string {
func CompactDecode(str string) []int { func CompactDecode(str string) []int {
base := CompactHexDecode(str) base := CompactHexDecode(str)
base = base[:len(base)-1] base = base[:len(base)-1]
if base[0] >= 2 { // && base[len(base)-1] != 16 { if base[0] >= 2 {
base = append(base, 16) base = append(base, 16)
} }
if base[0]%2 == 1 { 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) 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 { func Sha3Bin(data []byte) []byte {
d := sha3.NewKeccak256() d := sha3.NewKeccak256()
d.Reset()
d.Write(data) d.Write(data)
return d.Sum(nil) return d.Sum(nil)
@ -59,3 +58,7 @@ func MatchingNibbleLength(a, b []int) int {
func Hex(d []byte) string { func Hex(d []byte) string {
return hex.EncodeToString(d) 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 package ethutil
import ( import (
"errors"
"fmt"
"math/big" "math/big"
"strconv" "strconv"
"strings"
) )
// Op codes // Op codes
var OpCodes = map[string]string{ var OpCodes = map[string]byte{
"STOP": "0", "STOP": 0x00,
"ADD": "1", "ADD": 0x01,
"MUL": "2", "MUL": 0x02,
"SUB": "3", "SUB": 0x03,
"DIV": "4", "DIV": 0x04,
"SDIV": "5", "SDIV": 0x05,
"MOD": "6", "MOD": 0x06,
"SMOD": "7", "SMOD": 0x07,
"EXP": "8", "EXP": 0x08,
"NEG": "9", "NEG": 0x09,
"LT": "10", "LT": 0x0a,
"LE": "11", "LE": 0x0b,
"GT": "12", "GT": 0x0c,
"GE": "13", "GE": 0x0d,
"EQ": "14", "EQ": 0x0e,
"NOT": "15", "NOT": 0x0f,
"MYADDRESS": "16", "MYADDRESS": 0x10,
"TXSENDER": "17", "TXSENDER": 0x11,
"TXVALUE": 0x12,
"PUSH": "48", "TXDATAN": 0x13,
"POP": "49", "TXDATA": 0x14,
"LOAD": "54", "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) { func IsOpCode(s string) bool {
tokens := strings.Split(s, " ") for key, _ := range OpCodes {
if OpCodes[tokens[0]] == "" { if key == s {
return s, errors.New(fmt.Sprintf("OP not found: %s", tokens[0])) 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 num := new(big.Int)
op := new(big.Int) num.SetString(s, 0)
op.SetString(code, 0)
args := make([]*big.Int, 6) return num.Bytes(), nil
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
} }
func Instr(instr string) (int, []string, error) { func Instr(instr string) (int, []string, error) {
base := new(big.Int) base := new(big.Int)
base.SetString(instr, 0) base.SetString(instr, 0)

View File

@ -1,5 +1,6 @@
package ethutil package ethutil
/*
import ( import (
"math" "math"
"testing" "testing"
@ -13,20 +14,19 @@ func TestCompile(t *testing.T) {
} }
calc := (48 + 0*256 + 0*int64(math.Pow(256, 2))) 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) t.Error("Expected", calc, ", got:", instr)
} }
} }
func TestValidInstr(t *testing.T) { func TestValidInstr(t *testing.T) {
/*
op, args, err := Instr("68163") op, args, err := Instr("68163")
if err != nil { if err != nil {
t.Error("Error decoding instruction") t.Error("Error decoding instruction")
} }
*/
} }
func TestInvalidInstr(t *testing.T) { 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. // TODO Use a bytes.Buffer instead of a raw byte slice.
// Cleaner code, and use draining instead of seeking the next bytes to read // Cleaner code, and use draining instead of seeking the next bytes to read
func Decode(data []byte, pos uint64) (interface{}, uint64) { 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{} var slice []interface{}
char := int(data[pos]) char := int(data[pos])
switch { switch {
@ -131,7 +124,6 @@ func Decode(data []byte, pos uint64) (interface{}, uint64) {
case char <= 0xff: case char <= 0xff:
l := uint64(data[pos]) - 0xf7 l := uint64(data[pos]) - 0xf7
//b := BigD(data[pos+1 : pos+1+l]).Uint64()
b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l])) b := ReadVarint(bytes.NewReader(data[pos+1 : pos+1+l]))
pos = pos + l + 1 pos = pos + l + 1

View File

@ -2,15 +2,13 @@ package ethutil
import ( import (
"bytes" "bytes"
"encoding/hex"
"fmt"
"math/big" "math/big"
"reflect" "reflect"
"testing" "testing"
) )
func TestRlpValueEncoding(t *testing.T) { func TestRlpValueEncoding(t *testing.T) {
val := EmptyRlpValue() val := EmptyValue()
val.AppendList().Append(1).Append(2).Append(3) val.AppendList().Append(1).Append(2).Append(3)
val.Append("4").AppendList().Append(5) val.Append("4").AppendList().Append(5)
@ -63,7 +61,7 @@ func TestEncode(t *testing.T) {
str := string(bytes) str := string(bytes)
if str != strRes { 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" sliceRes := "\xcc\x83dog\x83god\x83cat"
@ -71,7 +69,7 @@ func TestEncode(t *testing.T) {
bytes = Encode(strs) bytes = Encode(strs)
slice := string(bytes) slice := string(bytes)
if slice != sliceRes { 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" intRes := "\x82\x04\x00"
@ -108,13 +106,9 @@ func TestEncodeDecodeBigInt(t *testing.T) {
encoded := Encode(bigInt) encoded := Encode(bigInt)
value := NewValueFromBytes(encoded) value := NewValueFromBytes(encoded)
fmt.Println(value.BigInt(), bigInt)
if value.BigInt().Cmp(bigInt) != 0 { if value.BigInt().Cmp(bigInt) != 0 {
t.Errorf("Expected %v, got %v", bigInt, value.BigInt()) t.Errorf("Expected %v, got %v", bigInt, value.BigInt())
} }
dec, _ := hex.DecodeString("52f4fc1e")
fmt.Println(NewValueFromBytes(dec).BigInt())
} }
func TestEncodeDecodeBytes(t *testing.T) { 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) { func BenchmarkEncodeDecode(b *testing.B) {
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
bytes := Encode([]interface{}{"dog", "god", "cat"}) bytes := Encode([]interface{}{"dog", "god", "cat"})

View File

@ -5,6 +5,15 @@ import (
"reflect" "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 { type Node struct {
Key []byte Key []byte
Value *Value Value *Value
@ -20,8 +29,9 @@ func (n *Node) Copy() *Node {
} }
type Cache struct { type Cache struct {
nodes map[string]*Node nodes map[string]*Node
db Database db Database
IsDirty bool
} }
func NewCache(db Database) *Cache { func NewCache(db Database) *Cache {
@ -36,6 +46,7 @@ func (cache *Cache) Put(v interface{}) interface{} {
sha := Sha3Bin(enc) sha := Sha3Bin(enc)
cache.nodes[string(sha)] = NewNode(sha, value, true) cache.nodes[string(sha)] = NewNode(sha, value, true)
cache.IsDirty = true
return sha return sha
} }
@ -59,13 +70,25 @@ func (cache *Cache) Get(key []byte) *Value {
return value return value
} }
func (cache *Cache) Delete(key []byte) {
delete(cache.nodes, string(key))
cache.db.Delete(key)
}
func (cache *Cache) Commit() { func (cache *Cache) Commit() {
// Don't try to commit if it isn't dirty
if !cache.IsDirty {
return
}
for key, node := range cache.nodes { for key, node := range cache.nodes {
if node.Dirty { if node.Dirty {
cache.db.Put([]byte(key), node.Value.Encode()) cache.db.Put([]byte(key), node.Value.Encode())
node.Dirty = false node.Dirty = false
} }
} }
cache.IsDirty = false
// If the nodes grows beyond the 200 entries we simple empty it // If the nodes grows beyond the 200 entries we simple empty it
// FIXME come up with something better // FIXME come up with something better
@ -80,6 +103,7 @@ func (cache *Cache) Undo() {
delete(cache.nodes, key) delete(cache.nodes, key)
} }
} }
cache.IsDirty = false
} }
// A (modified) Radix Trie implementation. The Trie implements // 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 // Please note that the data isn't persisted unless `Sync` is
// explicitly called. // explicitly called.
type Trie struct { type Trie struct {
Root interface{} prevRoot interface{}
Root interface{}
//db Database //db Database
cache *Cache cache *Cache
} }
func NewTrie(db Database, Root interface{}) *Trie { 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. // Save the cached value to the database.
func (t *Trie) Sync() { func (t *Trie) Sync() {
t.cache.Commit() 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() return c.Str()
} }
func (t *Trie) Delete(key string) {
t.Update(key, "")
}
func (t *Trie) GetState(node interface{}, key []int) interface{} { func (t *Trie) GetState(node interface{}, key []int) interface{} {
n := NewValue(node) n := NewValue(node)
// Return the node if key is empty (= found) // 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{} { func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{} {
if value != "" { if value != "" {
return t.InsertState(node, key, value) return t.InsertState(node, key, value)
} else { } else {
// delete it // delete it
return t.DeleteState(node, key)
} }
return "" return t.Root
} }
func (t *Trie) Put(node interface{}) interface{} { 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 // Check for "special" 2 slice type node
if currentNode.Len() == 2 { if currentNode.Len() == 2 {
// Decode the key // Decode the key
k := CompactDecode(currentNode.Get(0).Str()) k := CompactDecode(currentNode.Get(0).Str())
v := currentNode.Get(1).Raw() v := currentNode.Get(1).Raw()
@ -282,6 +324,87 @@ func (t *Trie) InsertState(node interface{}, key []int, value interface{}) inter
return "" 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 // Simple compare function which creates a rlp value out of the evaluated objects
func (t *Trie) Cmp(trie *Trie) bool { func (t *Trie) Cmp(trie *Trie) bool {
return NewValue(t.Root).Cmp(NewValue(trie.Root)) return NewValue(t.Root).Cmp(NewValue(trie.Root))
@ -296,3 +419,82 @@ func (t *Trie) Copy() *Trie {
return 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 package ethutil
import ( import (
_ "encoding/hex" "reflect"
_ "fmt"
"testing" "testing"
) )
const LONG_WORD = "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ"
type MemDatabase struct { type MemDatabase struct {
db map[string][]byte db map[string][]byte
} }
@ -20,15 +21,24 @@ func (db *MemDatabase) Put(key []byte, value []byte) {
func (db *MemDatabase) Get(key []byte) ([]byte, error) { func (db *MemDatabase) Get(key []byte) ([]byte, error) {
return db.db[string(key)], nil 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) Print() {}
func (db *MemDatabase) Close() {} func (db *MemDatabase) Close() {}
func (db *MemDatabase) LastKnownTD() []byte { return nil } func (db *MemDatabase) LastKnownTD() []byte { return nil }
func TestTrieSync(t *testing.T) { func New() (*MemDatabase, *Trie) {
db, _ := NewMemDatabase() 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 { if len(db.db) != 0 {
t.Error("Expected no data in database") t.Error("Expected no data in database")
} }
@ -38,3 +48,125 @@ func TestTrieSync(t *testing.T) {
t.Error("Expected data to be persisted") 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 { if data, ok := val.Val.([]interface{}); ok {
return len(data) return len(data)
} else if data, ok := val.Val.([]byte); ok { } else if data, ok := val.Val.([]byte); ok {
// FIXME return len(data)
} else if data, ok := val.Val.(string); ok {
return len(data) return len(data)
} }
@ -60,6 +61,10 @@ func (val *Value) Uint() uint64 {
return uint64(Val) return uint64(Val)
} else if Val, ok := val.Val.(uint64); ok { } else if Val, ok := val.Val.(uint64); ok {
return Val 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 { } else if Val, ok := val.Val.([]byte); ok {
return ReadVarint(bytes.NewReader(Val)) return ReadVarint(bytes.NewReader(Val))
} }
@ -80,6 +85,8 @@ func (val *Value) BigInt() *big.Int {
b := new(big.Int).SetBytes(a) b := new(big.Int).SetBytes(a)
return b return b
} else if a, ok := val.Val.(*big.Int); ok {
return a
} else { } else {
return big.NewInt(int64(val.Uint())) return big.NewInt(int64(val.Uint()))
} }
@ -92,6 +99,8 @@ func (val *Value) Str() string {
return string(a) return string(a)
} else if a, ok := val.Val.(string); ok { } else if a, ok := val.Val.(string); ok {
return a return a
} else if a, ok := val.Val.(byte); ok {
return string(a)
} }
return "" return ""
@ -102,7 +111,7 @@ func (val *Value) Bytes() []byte {
return a return a
} }
return make([]byte, 0) return []byte{}
} }
func (val *Value) Slice() []interface{} { func (val *Value) Slice() []interface{} {
@ -131,6 +140,19 @@ func (val *Value) SliceFromTo(from, to int) *Value {
return NewValue(slice[from:to]) 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 // Threat the value as a slice
func (val *Value) Get(idx int) *Value { func (val *Value) Get(idx int) *Value {
if d, ok := val.Val.([]interface{}); ok { if d, ok := val.Val.([]interface{}); ok {
@ -140,7 +162,7 @@ func (val *Value) Get(idx int) *Value {
} }
if idx < 0 { if idx < 0 {
panic("negative idx for Rlp Get") panic("negative idx for Value Get")
} }
return NewValue(d[idx]) return NewValue(d[idx])
@ -158,9 +180,9 @@ func (val *Value) Encode() []byte {
return Encode(val.Val) return Encode(val.Val)
} }
func NewValueFromBytes(rlpData []byte) *Value { func NewValueFromBytes(data []byte) *Value {
if len(rlpData) != 0 { if len(data) != 0 {
data, _ := Decode(rlpData, 0) data, _ := Decode(data, 0)
return NewValue(data) 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 type MsgType byte
const ( 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 MsgHandshakeTy = 0x00
MsgDiscTy = 0x01 MsgDiscTy = 0x01
MsgPingTy = 0x02 MsgPingTy = 0x02

46
peer.go
View File

@ -6,7 +6,6 @@ import (
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
"log"
"net" "net"
"runtime" "runtime"
"strconv" "strconv"
@ -23,6 +22,9 @@ const (
type DiscReason byte type DiscReason byte
const ( 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 DiscReRequested = 0x00
DiscReTcpSysErr = 0x01 DiscReTcpSysErr = 0x01
DiscBadProto = 0x02 DiscBadProto = 0x02
@ -56,9 +58,9 @@ func (d DiscReason) String() string {
type Caps byte type Caps byte
const ( const (
CapPeerDiscTy = 0x01 CapPeerDiscTy = 1 << iota
CapTxTy = 0x02 CapTxTy
CapChainTy = 0x04 CapChainTy
CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy 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) conn, err := net.DialTimeout("tcp", addr, 30*time.Second)
if err != nil { if err != nil {
log.Println("Connection to peer failed", err) ethutil.Config.Log.Debugln("Connection to peer failed", err)
p.Stop() p.Stop()
return return
} }
@ -199,7 +201,7 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) {
err := ethwire.WriteMessage(p.conn, msg) err := ethwire.WriteMessage(p.conn, msg)
if err != nil { 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 // Stop the client if there was an error writing to it
p.Stop() p.Stop()
return return
@ -261,7 +263,7 @@ func (p *Peer) HandleInbound() {
// Wait for a message from the peer // Wait for a message from the peer
msgs, err := ethwire.ReadMessages(p.conn) msgs, err := ethwire.ReadMessages(p.conn)
if err != nil { if err != nil {
log.Println(err) ethutil.Config.Log.Debugln(err)
} }
for _, msg := range msgs { for _, msg := range msgs {
switch msg.Type { switch msg.Type {
@ -274,7 +276,7 @@ func (p *Peer) HandleInbound() {
} }
case ethwire.MsgDiscTy: case ethwire.MsgDiscTy:
p.Stop() 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: case ethwire.MsgPingTy:
// Respond back with pong // Respond back with pong
p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, "")) p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
@ -285,7 +287,6 @@ func (p *Peer) HandleInbound() {
p.lastPong = time.Now().Unix() p.lastPong = time.Now().Unix()
case ethwire.MsgBlockTy: case ethwire.MsgBlockTy:
// Get all blocks and process them // Get all blocks and process them
msg.Data = msg.Data
var block, lastBlock *ethchain.Block var block, lastBlock *ethchain.Block
var err error var err error
for i := msg.Data.Len() - 1; i >= 0; i-- { for i := msg.Data.Len() - 1; i >= 0; i-- {
@ -293,7 +294,10 @@ func (p *Peer) HandleInbound() {
err = p.ethereum.BlockManager.ProcessBlock(block) err = p.ethereum.BlockManager.ProcessBlock(block)
if err != nil { 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 break
} else { } else {
lastBlock = block lastBlock = block
@ -303,7 +307,7 @@ func (p *Peer) HandleInbound() {
if err != nil { if err != nil {
// If the parent is unknown try to catch up with this peer // If the parent is unknown try to catch up with this peer
if ethchain.IsParentErr(err) { if ethchain.IsParentErr(err) {
log.Println("Attempting to catch up") ethutil.Config.Log.Infoln("Attempting to catch up")
p.catchingUp = false p.catchingUp = false
p.CatchupWithPeer() p.CatchupWithPeer()
} else if ethchain.IsValidationErr(err) { } else if ethchain.IsValidationErr(err) {
@ -315,7 +319,7 @@ func (p *Peer) HandleInbound() {
if p.catchingUp && msg.Data.Len() > 1 { if p.catchingUp && msg.Data.Len() > 1 {
if ethutil.Config.Debug && lastBlock != nil { if ethutil.Config.Debug && lastBlock != nil {
blockInfo := lastBlock.BlockInfo() 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.catchingUp = false
p.CatchupWithPeer() p.CatchupWithPeer()
@ -386,12 +390,12 @@ func (p *Peer) HandleInbound() {
p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()})) p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()}))
} }
case ethwire.MsgNotInChainTy: case ethwire.MsgNotInChainTy:
log.Printf("Not in chain %x\n", msg.Data) ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data)
// TODO // TODO
// Unofficial but fun nonetheless // Unofficial but fun nonetheless
case ethwire.MsgTalkTy: 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() err := p.pushHandshake()
if err != nil { 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() p.Stop()
@ -465,7 +469,7 @@ func (p *Peer) pushHandshake() error {
pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes() pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes()
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{ 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) p.QueueMessage(msg)
@ -492,8 +496,8 @@ func (p *Peer) pushPeers() {
func (p *Peer) handleHandshake(msg *ethwire.Msg) { func (p *Peer) handleHandshake(msg *ethwire.Msg) {
c := msg.Data c := msg.Data
if c.Get(0).Uint() != 4 { if c.Get(0).Uint() != 5 {
log.Println("Invalid peer version. Require protocol v4") ethutil.Config.Log.Debugln("Invalid peer version. Require protocol v5")
p.Stop() p.Stop()
return return
} }
@ -525,7 +529,7 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
// Get a reference to the peers version // Get a reference to the peers version
p.Version = c.Get(2).Str() p.Version = c.Get(2).Str()
log.Println(p) ethutil.Config.Log.Debugln("[PEER]", p)
} }
func (p *Peer) String() string { func (p *Peer) String() string {
@ -542,7 +546,7 @@ func (p *Peer) String() string {
strConnectType = "disconnected" 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)}) msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockManager.BlockChain().CurrentBlock.Hash(), uint64(50)})
p.QueueMessage(msg) 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])
} }
} }