Merge branch 'release/0.6.5'

This commit is contained in:
obscuren 2014-09-22 19:34:31 +02:00
commit ce149d2733
44 changed files with 1520 additions and 1167 deletions

View File

@ -6,7 +6,7 @@ Ethereum
Ethereum Go Development package (C) Jeffrey Wilcke Ethereum Go Development package (C) Jeffrey Wilcke
Ethereum is currently in its testing phase. The current state is "Proof Ethereum is currently in its testing phase. The current state is "Proof
of Concept 0.6.4". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). of Concept 0.6.5". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)).
Ethereum Go is split up in several sub packages Please refer to each Ethereum Go is split up in several sub packages Please refer to each
individual package for more information. individual package for more information.

View File

@ -33,6 +33,10 @@ func NewBlockPool(eth *Ethereum) *BlockPool {
} }
} }
func (self *BlockPool) Len() int {
return len(self.hashPool)
}
func (self *BlockPool) HasLatestHash() bool { func (self *BlockPool) HasLatestHash() bool {
return self.pool[string(self.eth.BlockChain().CurrentBlock.Hash())] != nil return self.pool[string(self.eth.BlockChain().CurrentBlock.Hash())] != nil
} }
@ -49,51 +53,37 @@ func (self *BlockPool) AddHash(hash []byte) {
} }
} }
func (self *BlockPool) SetBlock(b *ethchain.Block) { func (self *BlockPool) SetBlock(b *ethchain.Block, peer *Peer) {
hash := string(b.Hash()) hash := string(b.Hash())
if self.pool[string(hash)] == nil { if self.pool[hash] == nil && !self.eth.BlockChain().HasBlock(b.Hash()) {
self.pool[hash] = &block{nil, nil} self.hashPool = append(self.hashPool, b.Hash())
} self.pool[hash] = &block{peer, b}
} else if self.pool[hash] != nil {
self.pool[hash].block = b self.pool[hash].block = b
} }
}
func (self *BlockPool) CheckLinkAndProcess(f func(block *ethchain.Block)) bool { func (self *BlockPool) CheckLinkAndProcess(f func(block *ethchain.Block)) {
self.mut.Lock()
defer self.mut.Unlock()
if self.IsLinked() { var blocks ethchain.Blocks
for i, hash := range self.hashPool { for _, item := range self.pool {
block := self.pool[string(hash)].block if item.block != nil {
if block != nil { blocks = append(blocks, item.block)
}
}
ethchain.BlockBy(ethchain.Number).Sort(blocks)
for _, block := range blocks {
if self.eth.BlockChain().HasBlock(block.PrevHash) {
f(block) f(block)
hash := block.Hash()
self.hashPool = ethutil.DeleteFromByteSlice(self.hashPool, hash)
delete(self.pool, string(hash)) delete(self.pool, string(hash))
} else {
self.hashPool = self.hashPool[i:]
return false
}
} }
return true
} }
return false
}
func (self *BlockPool) IsLinked() bool {
if len(self.hashPool) == 0 {
return false
}
block := self.pool[string(self.hashPool[0])].block
if block != nil {
return self.eth.BlockChain().HasBlock(block.PrevHash)
}
return false
} }
func (self *BlockPool) Take(amount int, peer *Peer) (hashes [][]byte) { func (self *BlockPool) Take(amount int, peer *Peer) (hashes [][]byte) {
@ -104,7 +94,7 @@ func (self *BlockPool) Take(amount int, peer *Peer) (hashes [][]byte) {
j := 0 j := 0
for i := 0; i < len(self.hashPool) && j < num; i++ { for i := 0; i < len(self.hashPool) && j < num; i++ {
hash := string(self.hashPool[i]) hash := string(self.hashPool[i])
if self.pool[hash].peer == nil || self.pool[hash].peer == peer { if self.pool[hash] != nil && (self.pool[hash].peer == nil || self.pool[hash].peer == peer) && self.pool[hash].block == nil {
self.pool[hash].peer = peer self.pool[hash].peer = peer
hashes = append(hashes, self.hashPool[i]) hashes = append(hashes, self.hashPool[i])

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"math/big" "math/big"
"sort"
_ "strconv" _ "strconv"
"time" "time"
@ -31,11 +32,45 @@ func (bi *BlockInfo) RlpEncode() []byte {
return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent}) return ethutil.Encode([]interface{}{bi.Number, bi.Hash, bi.Parent})
} }
type Blocks []*Block
func (self Blocks) AsSet() ethutil.UniqueSet {
set := make(ethutil.UniqueSet)
for _, block := range self {
set.Insert(block.Hash())
}
return set
}
type BlockBy func(b1, b2 *Block) bool
func (self BlockBy) Sort(blocks Blocks) {
bs := blockSorter{
blocks: blocks,
by: self,
}
sort.Sort(bs)
}
type blockSorter struct {
blocks Blocks
by func(b1, b2 *Block) bool
}
func (self blockSorter) Len() int { return len(self.blocks) }
func (self blockSorter) Swap(i, j int) {
self.blocks[i], self.blocks[j] = self.blocks[j], self.blocks[i]
}
func (self blockSorter) Less(i, j int) bool { return self.by(self.blocks[i], self.blocks[j]) }
func Number(b1, b2 *Block) bool { return b1.Number.Cmp(b2.Number) < 0 }
type Block struct { type Block struct {
// Hash to the previous block // Hash to the previous block
PrevHash []byte PrevHash ethutil.Bytes
// Uncles of this block // Uncles of this block
Uncles []*Block Uncles Blocks
UncleSha []byte UncleSha []byte
// The coin base address // The coin base address
Coinbase []byte Coinbase []byte
@ -57,7 +92,7 @@ type Block struct {
// Extra data // Extra data
Extra string Extra string
// Block Nonce for verification // Block Nonce for verification
Nonce []byte Nonce ethutil.Bytes
// List of transactions and/or contracts // List of transactions and/or contracts
transactions []*Transaction transactions []*Transaction
receipts []*Receipt receipts []*Receipt
@ -106,8 +141,9 @@ func CreateBlock(root interface{},
} }
// Returns a hash of the block // Returns a hash of the block
func (block *Block) Hash() []byte { func (block *Block) Hash() ethutil.Bytes {
return ethcrypto.Sha3Bin(block.Value().Encode()) return ethcrypto.Sha3Bin(ethutil.NewValue(block.header()).Encode())
//return ethcrypto.Sha3Bin(block.Value().Encode())
} }
func (block *Block) HashNoNonce() []byte { func (block *Block) HashNoNonce() []byte {
@ -351,7 +387,7 @@ func (block *Block) header() []interface{} {
func (block *Block) String() string { func (block *Block) String() string {
return fmt.Sprintf(` return fmt.Sprintf(`
BLOCK(%x): BLOCK(%x): Size: %v
PrevHash: %x PrevHash: %x
UncleSha: %x UncleSha: %x
Coinbase: %x Coinbase: %x
@ -368,6 +404,7 @@ func (block *Block) String() string {
NumTx: %v NumTx: %v
`, `,
block.Hash(), block.Hash(),
block.Size(),
block.PrevHash, block.PrevHash,
block.UncleSha, block.UncleSha,
block.Coinbase, block.Coinbase,
@ -384,3 +421,7 @@ func (block *Block) String() string {
len(block.transactions), len(block.transactions),
) )
} }
func (self *Block) Size() ethutil.StorageSize {
return ethutil.StorageSize(len(self.RlpEncode()))
}

View File

@ -2,12 +2,10 @@ package ethchain
import ( import (
"bytes" "bytes"
"math"
"math/big" "math/big"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
) )
var chainlogger = ethlog.NewLogger("CHAIN") var chainlogger = ethlog.NewLogger("CHAIN")
@ -60,24 +58,20 @@ func (bc *BlockChain) NewBlock(coinbase []byte) *Block {
block.MinGasPrice = big.NewInt(10000000000000) block.MinGasPrice = big.NewInt(10000000000000)
if bc.CurrentBlock != nil { parent := bc.CurrentBlock
var mul *big.Int if parent != nil {
if block.Time < lastBlockTime+42 {
mul = big.NewInt(1)
} else {
mul = big.NewInt(-1)
}
diff := new(big.Int) diff := new(big.Int)
diff.Add(diff, bc.CurrentBlock.Difficulty)
diff.Div(diff, big.NewInt(1024)) adjust := new(big.Int).Rsh(parent.Difficulty, 10)
diff.Mul(diff, mul) if block.Time >= lastBlockTime+5 {
diff.Add(diff, bc.CurrentBlock.Difficulty) diff.Sub(parent.Difficulty, adjust)
} else {
diff.Add(parent.Difficulty, adjust)
}
block.Difficulty = diff block.Difficulty = diff
block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1) block.Number = new(big.Int).Add(bc.CurrentBlock.Number, ethutil.Big1)
block.GasLimit = block.CalcGasLimit(bc.CurrentBlock) block.GasLimit = block.CalcGasLimit(bc.CurrentBlock)
} }
return block return block
@ -110,99 +104,6 @@ func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int {
return blockDiff return blockDiff
} }
func (bc *BlockChain) FindCanonicalChainFromMsg(msg *ethwire.Msg, commonBlockHash []byte) bool {
var blocks []*Block
for i := 0; i < (msg.Data.Len() - 1); i++ {
block := NewBlockFromRlpValue(msg.Data.Get(i))
blocks = append(blocks, block)
}
return bc.FindCanonicalChain(blocks, commonBlockHash)
}
// Is tasked by finding the CanonicalChain and resetting the chain if we are not the Conical one
// Return true if we are the using the canonical chain false if not
func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte) bool {
// 1. Calculate TD of the current chain
// 2. Calculate TD of the new chain
// Reset state to the correct one
chainDifficulty := new(big.Int)
// Calculate the entire chain until the block we both have
// Start with the newest block we got, all the way back to the common block we both know
for _, block := range blocks {
if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
chainlogger.Infoln("We have found the common parent block, breaking")
break
}
chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block))
}
chainlogger.Infoln("Incoming chain difficulty:", chainDifficulty)
curChainDifficulty := new(big.Int)
block := bc.CurrentBlock
for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) {
i++
if bytes.Compare(block.Hash(), commonBlockHash) == 0 {
chainlogger.Infoln("Found the common parent block")
break
}
anOtherBlock := bc.GetBlock(block.PrevHash)
if anOtherBlock == nil {
// We do not want to count the genesis block for difficulty since that's not being sent
chainlogger.Infoln("Found genesis block. Stop")
break
}
curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block))
}
chainlogger.Infoln("Current chain difficulty:", curChainDifficulty)
if chainDifficulty.Cmp(curChainDifficulty) == 1 {
chainlogger.Infof("Resetting to block %x. Changing chain.")
bc.ResetTillBlockHash(commonBlockHash)
return false
} else {
chainlogger.Infoln("Current chain is longest chain. Ignoring incoming chain.")
return true
}
}
func (bc *BlockChain) ResetTillBlockHash(hash []byte) error {
lastBlock := bc.CurrentBlock
var returnTo *Block
// Reset to Genesis if that's all the origin there is.
if bytes.Compare(hash, bc.genesisBlock.Hash()) == 0 {
returnTo = bc.genesisBlock
bc.CurrentBlock = bc.genesisBlock
bc.LastBlockHash = bc.genesisBlock.Hash()
bc.LastBlockNumber = 1
} else {
returnTo = bc.GetBlock(hash)
bc.CurrentBlock = returnTo
bc.LastBlockHash = returnTo.Hash()
bc.LastBlockNumber = returnTo.Number.Uint64()
}
// Manually reset the last sync block
err := ethutil.Config.Db.Delete(lastBlock.Hash())
if err != nil {
return err
}
var block *Block
for ; block != nil; block = bc.GetBlock(block.PrevHash) {
if bytes.Compare(block.Hash(), hash) == 0 {
chainlogger.Infoln("We have arrived at the the common parent block, breaking")
break
}
err = ethutil.Config.Db.Delete(block.Hash())
if err != nil {
return err
}
}
chainlogger.Infoln("Split chain deleted and reverted to common parent block.")
return nil
}
func (bc *BlockChain) GenesisBlock() *Block { func (bc *BlockChain) GenesisBlock() *Block {
return bc.genesisBlock return bc.genesisBlock
@ -228,66 +129,6 @@ func (self *BlockChain) GetChainHashesFromHash(hash []byte, max uint64) (chain [
return return
} }
// Get chain return blocks from hash up to max in RLP format
func (bc *BlockChain) GetChainFromHash(hash []byte, max uint64) []interface{} {
var chain []interface{}
// Get the current hash to start with
currentHash := bc.CurrentBlock.Hash()
// Get the last number on the block chain
lastNumber := bc.CurrentBlock.Number.Uint64()
// Get the parents number
parentNumber := bc.GetBlock(hash).Number.Uint64()
// Get the min amount. We might not have max amount of blocks
count := uint64(math.Min(float64(lastNumber-parentNumber), float64(max)))
startNumber := parentNumber + count
num := lastNumber
for num > startNumber {
num--
block := bc.GetBlock(currentHash)
if block == nil {
break
}
currentHash = block.PrevHash
}
for i := uint64(0); bytes.Compare(currentHash, hash) != 0 && num >= parentNumber && i < count; i++ {
// Get the block of the chain
block := bc.GetBlock(currentHash)
if block == nil {
chainlogger.Debugf("Unexpected error during GetChainFromHash: Unable to find %x\n", currentHash)
break
}
currentHash = block.PrevHash
chain = append(chain, block.Value().Val)
num--
}
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 AddTestNetFunds(block *Block) { func AddTestNetFunds(block *Block) {
for _, addr := range []string{ for _, addr := range []string{
"51ba59315b3a95761d0863b05ccc7a7f54703d99", "51ba59315b3a95761d0863b05ccc7a7f54703d99",
@ -307,6 +148,9 @@ func AddTestNetFunds(block *Block) {
} }
func (bc *BlockChain) setLastBlock() { func (bc *BlockChain) setLastBlock() {
// Prep genesis
AddTestNetFunds(bc.genesisBlock)
data, _ := ethutil.Config.Db.Get([]byte("LastBlock")) data, _ := ethutil.Config.Db.Get([]byte("LastBlock"))
if len(data) != 0 { if len(data) != 0 {
block := NewBlockFromBytes(data) block := NewBlockFromBytes(data)
@ -315,13 +159,12 @@ func (bc *BlockChain) setLastBlock() {
bc.LastBlockNumber = block.Number.Uint64() bc.LastBlockNumber = block.Number.Uint64()
} else { } else {
AddTestNetFunds(bc.genesisBlock)
bc.genesisBlock.state.Trie.Sync() bc.genesisBlock.state.Trie.Sync()
// Prepare the genesis block // Prepare the genesis block
bc.Add(bc.genesisBlock) bc.Add(bc.genesisBlock)
fk := append([]byte("bloom"), bc.genesisBlock.Hash()...) fk := append([]byte("bloom"), bc.genesisBlock.Hash()...)
bc.Ethereum.Db().Put(fk, make([]byte, 255)) bc.Ethereum.Db().Put(fk, make([]byte, 255))
bc.CurrentBlock = bc.genesisBlock
} }
// Set the last know difficulty (might be 0x0 as initial value, Genesis) // Set the last know difficulty (might be 0x0 as initial value, Genesis)
@ -331,7 +174,7 @@ func (bc *BlockChain) setLastBlock() {
} }
func (bc *BlockChain) SetTotalDifficulty(td *big.Int) { func (bc *BlockChain) SetTotalDifficulty(td *big.Int) {
ethutil.Config.Db.Put([]byte("LastKnownTotalDifficulty"), td.Bytes()) ethutil.Config.Db.Put([]byte("LTD"), td.Bytes())
bc.TD = td bc.TD = td
} }
@ -359,10 +202,13 @@ func (bc *BlockChain) GetBlock(hash []byte) *Block {
func (self *BlockChain) GetBlockByNumber(num uint64) *Block { func (self *BlockChain) GetBlockByNumber(num uint64) *Block {
block := self.CurrentBlock block := self.CurrentBlock
for ; block.Number.Uint64() != num; block = self.GetBlock(block.PrevHash) { for ; block != nil; block = self.GetBlock(block.PrevHash) {
if block.Number.Uint64() == num {
break
}
} }
if block.Number.Uint64() == 0 && num != 0 { if block != nil && block.Number.Uint64() == 0 && num != 0 {
return nil return nil
} }

View File

@ -25,6 +25,24 @@ func IsParentErr(err error) bool {
return ok return ok
} }
type UncleErr struct {
Message string
}
func (err *UncleErr) Error() string {
return err.Message
}
func UncleError(str string) error {
return &UncleErr{Message: str}
}
func IsUncleErr(err error) bool {
_, ok := err.(*UncleErr)
return ok
}
// Block validation error. If any validation fails, this error will be thrown // Block validation error. If any validation fails, this error will be thrown
type ValidationErr struct { type ValidationErr struct {
Message string Message string

View File

@ -5,5 +5,3 @@ import (
) )
var BlockReward *big.Int = big.NewInt(1.5e+18) 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)

View File

@ -23,6 +23,9 @@ type Filter struct {
max int max int
altered []data altered []data
BlockCallback func(*Block)
MessageCallback func(ethstate.Messages)
} }
// Create a new filter which uses a bloom filter on blocks to figure out whether a particular block // Create a new filter which uses a bloom filter on blocks to figure out whether a particular block

View File

@ -5,6 +5,7 @@ import (
"container/list" "container/list"
"fmt" "fmt"
"math/big" "math/big"
"os"
"sync" "sync"
"time" "time"
@ -154,6 +155,10 @@ done:
if i < len(block.Receipts()) { if i < len(block.Receipts()) {
original := block.Receipts()[i] original := block.Receipts()[i]
if !original.Cmp(receipt) { if !original.Cmp(receipt) {
if ethutil.Config.Diff {
os.Exit(1)
}
return nil, nil, nil, fmt.Errorf("err diff #%d (r) %v ~ %x <=> (c) %v ~ %x (%x)\n", i+1, original.CumulativeGasUsed, original.PostState[0:4], receipt.CumulativeGasUsed, receipt.PostState[0:4], receipt.Tx.Hash()) return nil, nil, nil, fmt.Errorf("err diff #%d (r) %v ~ %x <=> (c) %v ~ %x (%x)\n", i+1, original.CumulativeGasUsed, original.PostState[0:4], receipt.CumulativeGasUsed, receipt.PostState[0:4], receipt.Tx.Hash())
} }
} }
@ -217,13 +222,13 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
return err return err
} }
// I'm not sure, but I don't know if there should be thrown if err = sm.AccumelateRewards(state, block, parent); err != nil {
// any errors at this time.
if err = sm.AccumelateRewards(state, block); err != nil {
statelogger.Errorln("Error accumulating reward", err) statelogger.Errorln("Error accumulating reward", err)
return err return err
} }
state.Update()
if !block.State().Cmp(state) { if !block.State().Cmp(state) {
err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().Trie.Root, state.Trie.Root) err = fmt.Errorf("Invalid merkle root.\nrec: %x\nis: %x", block.State().Trie.Root, state.Trie.Root)
return return
@ -237,6 +242,8 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) {
// Add the block to the chain // Add the block to the chain
sm.bc.Add(block) sm.bc.Add(block)
sm.transState = state.Copy()
// Create a bloom bin for this block // Create a bloom bin for this block
filter := sm.createBloomFilter(state) filter := sm.createBloomFilter(state)
// Persist the data // Persist the data
@ -305,14 +312,16 @@ func (sm *StateManager) ValidateBlock(block *Block) error {
// Check each uncle's previous hash. In order for it to be valid // Check each uncle's previous hash. In order for it to be valid
// is if it has the same block hash as the current // is if it has the same block hash as the current
previousBlock := sm.bc.GetBlock(block.PrevHash) parent := sm.bc.GetBlock(block.PrevHash)
/*
for _, uncle := range block.Uncles { for _, uncle := range block.Uncles {
if bytes.Compare(uncle.PrevHash, previousBlock.PrevHash) != 0 { if bytes.Compare(uncle.PrevHash,parent.PrevHash) != 0 {
return ValidationError("Mismatch uncle's previous hash. Expected %x, got %x", previousBlock.PrevHash, uncle.PrevHash) return ValidationError("Mismatch uncle's previous hash. Expected %x, got %x",parent.PrevHash, uncle.PrevHash)
} }
} }
*/
diff := block.Time - previousBlock.Time diff := block.Time - parent.Time
if diff < 0 { if diff < 0 {
return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock.Time) return ValidationError("Block timestamp less then prev block %v (%v - %v)", diff, block.Time, sm.bc.CurrentBlock.Time)
} }
@ -332,35 +341,45 @@ func (sm *StateManager) ValidateBlock(block *Block) error {
return nil return nil
} }
func CalculateBlockReward(block *Block, uncleLength int) *big.Int { func (sm *StateManager) AccumelateRewards(state *ethstate.State, block, parent *Block) error {
base := new(big.Int) reward := new(big.Int).Set(BlockReward)
for i := 0; i < uncleLength; i++ {
base.Add(base, UncleInclusionReward) knownUncles := ethutil.Set(parent.Uncles)
nonces := ethutil.NewSet(block.Nonce)
for _, uncle := range block.Uncles {
if nonces.Include(uncle.Nonce) {
// Error not unique
return UncleError("Uncle not unique")
} }
return base.Add(base, BlockReward) uncleParent := sm.bc.GetBlock(uncle.PrevHash)
if uncleParent == nil {
return UncleError("Uncle's parent unknown")
} }
func CalculateUncleReward(block *Block) *big.Int { if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 {
return UncleReward return UncleError("Uncle too old")
}
if knownUncles.Include(uncle.Hash()) {
return UncleError("Uncle in chain")
}
nonces.Insert(uncle.Nonce)
r := new(big.Int)
r.Mul(BlockReward, big.NewInt(15)).Div(r, big.NewInt(16))
uncleAccount := state.GetAccount(uncle.Coinbase)
uncleAccount.AddAmount(r)
reward.Add(reward, new(big.Int).Div(BlockReward, big.NewInt(32)))
} }
func (sm *StateManager) AccumelateRewards(state *ethstate.State, block *Block) error {
// Get the account associated with the coinbase // Get the account associated with the coinbase
account := state.GetAccount(block.Coinbase) account := state.GetAccount(block.Coinbase)
// Reward amount of ether to the coinbase address // Reward amount of ether to the coinbase address
account.AddAmount(CalculateBlockReward(block, len(block.Uncles))) account.AddAmount(reward)
addr := make([]byte, len(block.Coinbase))
copy(addr, block.Coinbase)
state.UpdateStateObject(account)
for _, uncle := range block.Uncles {
uncleAccount := state.GetAccount(uncle.Coinbase)
uncleAccount.AddAmount(CalculateUncleReward(uncle))
state.UpdateStateObject(uncleAccount)
}
return nil return nil
} }
@ -373,14 +392,6 @@ func (sm *StateManager) Stop() {
func (sm *StateManager) createBloomFilter(state *ethstate.State) *BloomFilter { func (sm *StateManager) createBloomFilter(state *ethstate.State) *BloomFilter {
bloomf := NewBloomFilter(nil) bloomf := NewBloomFilter(nil)
/*
for addr, stateObject := range state.Manifest().ObjectChanges {
// Set the bloom filter's bin
bloomf.Set([]byte(addr))
sm.Ethereum.Reactor().Post("object:"+addr, stateObject)
}
*/
for _, msg := range state.Manifest().Messages { for _, msg := range state.Manifest().Messages {
bloomf.Set(msg.To) bloomf.Set(msg.To)
bloomf.Set(msg.From) bloomf.Set(msg.From)
@ -388,17 +399,6 @@ func (sm *StateManager) createBloomFilter(state *ethstate.State) *BloomFilter {
sm.Ethereum.Reactor().Post("messages", state.Manifest().Messages) sm.Ethereum.Reactor().Post("messages", state.Manifest().Messages)
/*
for stateObjectAddr, mappedObjects := range state.Manifest().StorageChanges {
for addr, value := range mappedObjects {
// Set the bloom filter's bin
bloomf.Set(ethcrypto.Sha3Bin([]byte(stateObjectAddr + addr)))
sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &ethstate.StorageState{[]byte(stateObjectAddr), []byte(addr), value})
}
}
*/
return bloomf return bloomf
} }
@ -418,7 +418,7 @@ func (sm *StateManager) GetMessages(block *Block) (messages []*ethstate.Message,
sm.ApplyDiff(state, parent, block) sm.ApplyDiff(state, parent, block)
sm.AccumelateRewards(state, block) sm.AccumelateRewards(state, block, parent)
return state.Manifest().Messages, nil return state.Manifest().Messages, nil
} }

View File

@ -140,7 +140,7 @@ func (self *StateTransition) preCheck() (err error) {
} }
func (self *StateTransition) TransitionState() (err error) { func (self *StateTransition) TransitionState() (err error) {
statelogger.Infof("(~) %x\n", self.tx.Hash()) statelogger.Debugf("(~) %x\n", self.tx.Hash())
/* /*
defer func() { defer func() {
@ -278,6 +278,15 @@ func (self *StateTransition) Eval(msg *ethstate.Message, script []byte, context
ret, _, err = callerClosure.Call(vm, self.tx.Data) ret, _, err = callerClosure.Call(vm, self.tx.Data)
if err == nil {
// Execute POSTs
for e := vm.Queue().Front(); e != nil; e = e.Next() {
msg := e.Value.(*ethvm.Message)
msg.Exec(msg.Addr(), transactor)
}
}
return return
} }

View File

@ -13,7 +13,8 @@ import (
var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} var ContractAddr = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
func IsContractAddr(addr []byte) bool { func IsContractAddr(addr []byte) bool {
return bytes.Compare(addr, ContractAddr) == 0 return len(addr) == 0
//return bytes.Compare(addr, ContractAddr) == 0
} }
type Transaction struct { type Transaction struct {
@ -31,7 +32,7 @@ type Transaction struct {
} }
func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction { func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction {
return &Transaction{Recipient: ContractAddr, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true} return &Transaction{Recipient: nil, Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true}
} }
func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction { func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction {

View File

@ -72,8 +72,6 @@ type TxPool struct {
func NewTxPool(ethereum EthManager) *TxPool { func NewTxPool(ethereum EthManager) *TxPool {
return &TxPool{ return &TxPool{
//server: s,
mutex: sync.Mutex{},
pool: list.New(), pool: list.New(),
queueChan: make(chan *Transaction, txPoolQueueSize), queueChan: make(chan *Transaction, txPoolQueueSize),
quit: make(chan bool), quit: make(chan bool),
@ -101,7 +99,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error {
return fmt.Errorf("[TXPL] No last block on the block chain") return fmt.Errorf("[TXPL] No last block on the block chain")
} }
if len(tx.Recipient) != 20 { if len(tx.Recipient) != 0 && len(tx.Recipient) != 20 {
return fmt.Errorf("[TXPL] Invalid recipient. len = %d", len(tx.Recipient)) return fmt.Errorf("[TXPL] Invalid recipient. len = %d", len(tx.Recipient))
} }
@ -150,7 +148,10 @@ out:
// Call blocking version. // Call blocking version.
pool.addTransaction(tx) pool.addTransaction(tx)
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tx.Recipient[:4], tx.Value, tx.Hash()) tmp := make([]byte, 4)
copy(tmp, tx.Recipient)
txplogger.Debugf("(t) %x => %x (%v) %x\n", tx.Sender()[:4], tmp, tx.Value, tx.Hash())
// Notify the subscribers // Notify the subscribers
pool.Ethereum.Reactor().Post("newTx:pre", tx) pool.Ethereum.Reactor().Post("newTx:pre", tx)

View File

@ -31,6 +31,8 @@ const (
OR = 0x11 OR = 0x11
XOR = 0x12 XOR = 0x12
BYTE = 0x13 BYTE = 0x13
ADDMOD = 0x14
MULMOD = 0x15
// 0x20 range - crypto // 0x20 range - crypto
SHA3 = 0x20 SHA3 = 0x20
@ -47,6 +49,8 @@ const (
CODESIZE = 0x38 CODESIZE = 0x38
CODECOPY = 0x39 CODECOPY = 0x39
GASPRICE = 0x3a GASPRICE = 0x3a
EXTCODECOPY = 0x3b
EXTCODESIZE = 0x3c
// 0x40 range - block operations // 0x40 range - block operations
PREVHASH = 0x40 PREVHASH = 0x40
@ -58,8 +62,8 @@ const (
// 0x50 range - 'storage' and execution // 0x50 range - 'storage' and execution
POP = 0x50 POP = 0x50
DUP = 0x51 //DUP = 0x51
SWAP = 0x52 //SWAP = 0x52
MLOAD = 0x53 MLOAD = 0x53
MSTORE = 0x54 MSTORE = 0x54
MSTORE8 = 0x55 MSTORE8 = 0x55
@ -105,10 +109,46 @@ const (
PUSH31 = 0x7e PUSH31 = 0x7e
PUSH32 = 0x7f PUSH32 = 0x7f
DUP1 = 0x80
DUP2 = 0x81
DUP3 = 0x82
DUP4 = 0x83
DUP5 = 0x84
DUP6 = 0x85
DUP7 = 0x86
DUP8 = 0x87
DUP9 = 0x88
DUP10 = 0x89
DUP11 = 0x8a
DUP12 = 0x8b
DUP13 = 0x8c
DUP14 = 0x8d
DUP15 = 0x8e
DUP16 = 0x8f
SWAP1 = 0x90
SWAP2 = 0x91
SWAP3 = 0x92
SWAP4 = 0x93
SWAP5 = 0x94
SWAP6 = 0x95
SWAP7 = 0x96
SWAP8 = 0x97
SWAP9 = 0x98
SWAP10 = 0x99
SWAP11 = 0x9a
SWAP12 = 0x9b
SWAP13 = 0x9c
SWAP14 = 0x9d
SWAP15 = 0x9e
SWAP16 = 0x9f
// 0xf0 range - closures // 0xf0 range - closures
CREATE = 0xf0 CREATE = 0xf0
CALL = 0xf1 CALL = 0xf1
RETURN = 0xf2 RETURN = 0xf2
POST = 0xf3
CALLSTATELESS = 0xf4
// 0x70 range - other // 0x70 range - other
LOG = 0xfe // XXX Unofficial LOG = 0xfe // XXX Unofficial
@ -140,6 +180,8 @@ var opCodeToString = map[OpCode]string{
OR: "OR", OR: "OR",
XOR: "XOR", XOR: "XOR",
BYTE: "BYTE", BYTE: "BYTE",
ADDMOD: "ADDMOD",
MULMOD: "MULMOD",
// 0x20 range - crypto // 0x20 range - crypto
SHA3: "SHA3", SHA3: "SHA3",
@ -164,11 +206,13 @@ var opCodeToString = map[OpCode]string{
NUMBER: "NUMBER", NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY", DIFFICULTY: "DIFFICULTY",
GASLIMIT: "GASLIMIT", GASLIMIT: "GASLIMIT",
EXTCODESIZE: "EXTCODESIZE",
EXTCODECOPY: "EXTCODECOPY",
// 0x50 range - 'storage' and execution // 0x50 range - 'storage' and execution
POP: "POP", POP: "POP",
DUP: "DUP", //DUP: "DUP",
SWAP: "SWAP", //SWAP: "SWAP",
MLOAD: "MLOAD", MLOAD: "MLOAD",
MSTORE: "MSTORE", MSTORE: "MSTORE",
MSTORE8: "MSTORE8", MSTORE8: "MSTORE8",
@ -214,10 +258,46 @@ var opCodeToString = map[OpCode]string{
PUSH31: "PUSH31", PUSH31: "PUSH31",
PUSH32: "PUSH32", PUSH32: "PUSH32",
DUP1: "DUP1",
DUP2: "DUP2",
DUP3: "DUP3",
DUP4: "DUP4",
DUP5: "DUP5",
DUP6: "DUP6",
DUP7: "DUP7",
DUP8: "DUP8",
DUP9: "DUP9",
DUP10: "DUP10",
DUP11: "DUP11",
DUP12: "DUP12",
DUP13: "DUP13",
DUP14: "DUP14",
DUP15: "DUP15",
DUP16: "DUP16",
SWAP1: "SWAP1",
SWAP2: "SWAP2",
SWAP3: "SWAP3",
SWAP4: "SWAP4",
SWAP5: "SWAP5",
SWAP6: "SWAP6",
SWAP7: "SWAP7",
SWAP8: "SWAP8",
SWAP9: "SWAP9",
SWAP10: "SWAP10",
SWAP11: "SWAP11",
SWAP12: "SWAP12",
SWAP13: "SWAP13",
SWAP14: "SWAP14",
SWAP15: "SWAP15",
SWAP16: "SWAP16",
// 0xf0 range // 0xf0 range
CREATE: "CREATE", CREATE: "CREATE",
CALL: "CALL", CALL: "CALL",
RETURN: "RETURN", RETURN: "RETURN",
POST: "POST",
CALLSTATELESS: "CALLSTATELESS",
// 0x70 range - other // 0x70 range - other
LOG: "LOG", LOG: "LOG",
@ -232,115 +312,3 @@ func (o OpCode) String() string {
return str return str
} }
// Op codes for assembling
var OpCodes = map[string]byte{
// 0x0 range - arithmetic ops
"STOP": 0x00,
"ADD": 0x01,
"MUL": 0x02,
"SUB": 0x03,
"DIV": 0x04,
"SDIV": 0x05,
"MOD": 0x06,
"SMOD": 0x07,
"EXP": 0x08,
"NEG": 0x09,
"LT": 0x0a,
"GT": 0x0b,
"EQ": 0x0c,
"NOT": 0x0d,
// 0x10 range - bit ops
"AND": 0x10,
"OR": 0x11,
"XOR": 0x12,
"BYTE": 0x13,
// 0x20 range - crypto
"SHA3": 0x20,
// 0x30 range - closure state
"ADDRESS": 0x30,
"BALANCE": 0x31,
"ORIGIN": 0x32,
"CALLER": 0x33,
"CALLVALUE": 0x34,
"CALLDATALOAD": 0x35,
"CALLDATASIZE": 0x36,
"GASPRICE": 0x38,
// 0x40 range - block operations
"PREVHASH": 0x40,
"COINBASE": 0x41,
"TIMESTAMP": 0x42,
"NUMBER": 0x43,
"DIFFICULTY": 0x44,
"GASLIMIT": 0x45,
// 0x50 range - 'storage' and execution
"POP": 0x51,
"DUP": 0x52,
"SWAP": 0x53,
"MLOAD": 0x54,
"MSTORE": 0x55,
"MSTORE8": 0x56,
"SLOAD": 0x57,
"SSTORE": 0x58,
"JUMP": 0x59,
"JUMPI": 0x5a,
"PC": 0x5b,
"MSIZE": 0x5c,
// 0x70 range - 'push'
"PUSH1": 0x60,
"PUSH2": 0x61,
"PUSH3": 0x62,
"PUSH4": 0x63,
"PUSH5": 0x64,
"PUSH6": 0x65,
"PUSH7": 0x66,
"PUSH8": 0x67,
"PUSH9": 0x68,
"PUSH10": 0x69,
"PUSH11": 0x6a,
"PUSH12": 0x6b,
"PUSH13": 0x6c,
"PUSH14": 0x6d,
"PUSH15": 0x6e,
"PUSH16": 0x6f,
"PUSH17": 0x70,
"PUSH18": 0x71,
"PUSH19": 0x72,
"PUSH20": 0x73,
"PUSH21": 0x74,
"PUSH22": 0x75,
"PUSH23": 0x76,
"PUSH24": 0x77,
"PUSH25": 0x78,
"PUSH26": 0x70,
"PUSH27": 0x7a,
"PUSH28": 0x7b,
"PUSH29": 0x7c,
"PUSH30": 0x7d,
"PUSH31": 0x7e,
"PUSH32": 0x7f,
// 0xf0 range - closures
"CREATE": 0xf0,
"CALL": 0xf1,
"RETURN": 0xf2,
// 0x70 range - other
"LOG": 0xfe,
"SUICIDE": 0x7f,
}
func IsOpCode(s string) bool {
for key, _ := range OpCodes {
if key == s {
return true
}
}
return false
}

17
ethcrypto/crypto_test.go Normal file
View File

@ -0,0 +1,17 @@
package ethcrypto
import (
"bytes"
"testing"
"github.com/ethereum/eth-go/ethutil"
)
// FIPS 202 test (reverted back to FIPS 180)
func TestSha3(t *testing.T) {
const exp = "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532"
sha3_256 := Sha3Bin([]byte("abc"))
if bytes.Compare(sha3_256, ethutil.Hex2Bytes(exp)) != 0 {
t.Errorf("Sha3_256 failed. Incorrect result %x", sha3_256)
}
}

View File

@ -2,9 +2,10 @@ package ethdb
import ( import (
"fmt" "fmt"
"path"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb"
"path"
) )
type LDBDatabase struct { type LDBDatabase struct {
@ -45,7 +46,7 @@ func (db *LDBDatabase) Db() *leveldb.DB {
} }
func (db *LDBDatabase) LastKnownTD() []byte { func (db *LDBDatabase) LastKnownTD() []byte {
data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil) data, _ := db.db.Get([]byte("LTD"), nil)
if len(data) == 0 { if len(data) == 0 {
data = []byte{0x0} data = []byte{0x0}
@ -54,14 +55,6 @@ 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()

View File

@ -2,9 +2,11 @@ package eth
import ( import (
"container/list" "container/list"
"encoding/json"
"fmt" "fmt"
"math/rand" "math/rand"
"net" "net"
"path"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -16,13 +18,14 @@ import (
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethreact" "github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethrpc" "github.com/ethereum/eth-go/ethrpc"
"github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire" "github.com/ethereum/eth-go/ethwire"
) )
const ( const (
seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt" seedTextFileUri string = "http://www.ethereum.org/servers.poc3.txt"
seedNodeAddress = "54.76.56.74:30303" seedNodeAddress = "poc-6.ethdev.com:30303"
) )
var ethlogger = ethlog.NewLogger("SERV") var ethlogger = ethlog.NewLogger("SERV")
@ -30,9 +33,7 @@ var ethlogger = ethlog.NewLogger("SERV")
func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) { func eachPeer(peers *list.List, callback func(*Peer, *list.Element)) {
// Loop thru the peers and close them (if we had them) // Loop thru the peers and close them (if we had them)
for e := peers.Front(); e != nil; e = e.Next() { for e := peers.Front(); e != nil; e = e.Next() {
if peer, ok := e.Value.(*Peer); ok { callback(e.Value.(*Peer), e)
callback(peer, e)
}
} }
} }
@ -87,10 +88,11 @@ type Ethereum struct {
clientIdentity ethwire.ClientIdentity clientIdentity ethwire.ClientIdentity
isUpToDate bool isUpToDate bool
filters map[int]*ethchain.Filter
} }
func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager *ethcrypto.KeyManager, caps Caps, usePnp bool) (*Ethereum, error) { func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager *ethcrypto.KeyManager, caps Caps, usePnp bool) (*Ethereum, error) {
var err error var err error
var nat NAT var nat NAT
@ -101,6 +103,8 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager
} }
} }
bootstrapDb(db)
ethutil.Config.Db = db ethutil.Config.Db = db
nonce, _ := ethutil.RandomUint64() nonce, _ := ethutil.RandomUint64()
@ -115,6 +119,7 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager
keyManager: keyManager, keyManager: keyManager,
clientIdentity: clientIdentity, clientIdentity: clientIdentity,
isUpToDate: true, isUpToDate: true,
filters: make(map[int]*ethchain.Filter),
} }
ethereum.reactor = ethreact.New() ethereum.reactor = ethreact.New()
@ -385,6 +390,7 @@ func (s *Ethereum) Start(seed bool) {
// Start the reaping processes // Start the reaping processes
go s.ReapDeadPeerHandler() go s.ReapDeadPeerHandler()
go s.update() go s.update()
go s.filterLoop()
if seed { if seed {
s.Seed() s.Seed()
@ -393,6 +399,13 @@ func (s *Ethereum) Start(seed bool) {
} }
func (s *Ethereum) Seed() { func (s *Ethereum) Seed() {
ips := PastPeers()
if len(ips) > 0 {
for _, ip := range ips {
ethlogger.Infoln("Connecting to previous peer ", ip)
s.ConnectToPeer(ip)
}
} else {
ethlogger.Debugln("Retrieving seed nodes") ethlogger.Debugln("Retrieving seed nodes")
// Eth-Go Bootstrapping // Eth-Go Bootstrapping
@ -435,6 +448,7 @@ func (s *Ethereum) Seed() {
// XXX tmp // XXX tmp
s.ConnectToPeer(seedNodeAddress) s.ConnectToPeer(seedNodeAddress)
} }
}
func (s *Ethereum) peerHandler(listener net.Listener) { func (s *Ethereum) peerHandler(listener net.Listener) {
for { for {
@ -453,6 +467,16 @@ func (s *Ethereum) Stop() {
// Close the database // Close the database
defer s.db.Close() defer s.db.Close()
var ips []string
eachPeer(s.peers, func(p *Peer, e *list.Element) {
ips = append(ips, p.conn.RemoteAddr().String())
})
if len(ips) > 0 {
d, _ := json.MarshalIndent(ips, "", " ")
ethutil.WriteFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"), d)
}
eachPeer(s.peers, func(p *Peer, e *list.Element) { eachPeer(s.peers, func(p *Peer, e *list.Element) {
p.Stop() p.Stop()
}) })
@ -534,3 +558,74 @@ out:
} }
} }
} }
var filterId = 0
func (self *Ethereum) InstallFilter(object map[string]interface{}) (*ethchain.Filter, int) {
defer func() { filterId++ }()
filter := ethchain.NewFilterFromMap(object, self)
self.filters[filterId] = filter
return filter, filterId
}
func (self *Ethereum) UninstallFilter(id int) {
delete(self.filters, id)
}
func (self *Ethereum) GetFilter(id int) *ethchain.Filter {
return self.filters[id]
}
func (self *Ethereum) filterLoop() {
blockChan := make(chan ethreact.Event, 5)
messageChan := make(chan ethreact.Event, 5)
// Subscribe to events
reactor := self.Reactor()
reactor.Subscribe("newBlock", blockChan)
reactor.Subscribe("messages", messageChan)
out:
for {
select {
case <-self.quit:
break out
case block := <-blockChan:
if block, ok := block.Resource.(*ethchain.Block); ok {
for _, filter := range self.filters {
if filter.BlockCallback != nil {
filter.BlockCallback(block)
}
}
}
case msg := <-messageChan:
if messages, ok := msg.Resource.(ethstate.Messages); ok {
for _, filter := range self.filters {
if filter.MessageCallback != nil {
msgs := filter.FilterMessages(messages)
if len(msgs) > 0 {
filter.MessageCallback(msgs)
}
}
}
}
}
}
}
func bootstrapDb(db ethutil.Database) {
d, _ := db.Get([]byte("ProtocolVersion"))
protov := ethutil.NewValue(d).Uint()
if protov == 0 {
db.Put([]byte("ProtocolVersion"), ethutil.NewValue(ProtocolVersion).Bytes())
}
}
func PastPeers() []string {
var ips []string
data, _ := ethutil.ReadAllFile(path.Join(ethutil.Config.ExecPath, "known_peers.json"))
json.Unmarshal([]byte(data), &ips)
return ips
}

View File

@ -187,7 +187,7 @@ func (self *Miner) mineNewBlock() {
self.block.SetReceipts(receipts, txs) self.block.SetReceipts(receipts, txs)
// Accumulate the rewards included for this block // Accumulate the rewards included for this block
stateManager.AccumelateRewards(self.block.State(), self.block) stateManager.AccumelateRewards(self.block.State(), self.block, parent)
self.block.State().Update() self.block.State().Update()

View File

@ -3,12 +3,10 @@ package ethpipe
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"sync/atomic" "sync/atomic"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethreact"
"github.com/ethereum/eth-go/ethstate" "github.com/ethereum/eth-go/ethstate"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
) )
@ -87,10 +85,6 @@ func (self *JSPipe) CoinBase() string {
return ethutil.Bytes2Hex(self.obj.KeyManager().Address()) return ethutil.Bytes2Hex(self.obj.KeyManager().Address())
} }
func (self *JSPipe) BalanceAt(addr string) string {
return self.World().SafeGet(ethutil.Hex2Bytes(addr)).Balance.String()
}
func (self *JSPipe) NumberToHuman(balance string) string { func (self *JSPipe) NumberToHuman(balance string) string {
b := ethutil.Big(balance) b := ethutil.Big(balance)
@ -99,13 +93,22 @@ func (self *JSPipe) NumberToHuman(balance string) string {
func (self *JSPipe) StorageAt(addr, storageAddr string) string { func (self *JSPipe) StorageAt(addr, storageAddr string) string {
storage := self.World().SafeGet(ethutil.Hex2Bytes(addr)).Storage(ethutil.Hex2Bytes(storageAddr)) storage := self.World().SafeGet(ethutil.Hex2Bytes(addr)).Storage(ethutil.Hex2Bytes(storageAddr))
return storage.BigInt().String()
return ethutil.Bytes2Hex(storage.Bytes())
}
func (self *JSPipe) BalanceAt(addr string) string {
return self.World().SafeGet(ethutil.Hex2Bytes(addr)).Balance.String()
} }
func (self *JSPipe) TxCountAt(address string) int { func (self *JSPipe) TxCountAt(address string) int {
return int(self.World().SafeGet(ethutil.Hex2Bytes(address)).Nonce) return int(self.World().SafeGet(ethutil.Hex2Bytes(address)).Nonce)
} }
func (self *JSPipe) CodeAt(address string) string {
return ethutil.Bytes2Hex(self.World().SafeGet(ethutil.Hex2Bytes(address)).Code)
}
func (self *JSPipe) IsContract(address string) bool { func (self *JSPipe) IsContract(address string) bool {
return len(self.World().SafeGet(ethutil.Hex2Bytes(address)).Code) > 0 return len(self.World().SafeGet(ethutil.Hex2Bytes(address)).Code) > 0
} }
@ -119,6 +122,18 @@ func (self *JSPipe) SecretToAddress(key string) string {
return ethutil.Bytes2Hex(pair.Address()) return ethutil.Bytes2Hex(pair.Address())
} }
func (self *JSPipe) Execute(addr, value, gas, price, data string) (string, error) {
ret, err := self.ExecuteObject(&Object{
self.World().safeGet(ethutil.Hex2Bytes(addr))},
ethutil.Hex2Bytes(data),
ethutil.NewValue(value),
ethutil.NewValue(gas),
ethutil.NewValue(price),
)
return ethutil.Bytes2Hex(ret), err
}
type KeyVal struct { type KeyVal struct {
Key string `json:"key"` Key string `json:"key"`
Value string `json:"value"` Value string `json:"value"`
@ -224,6 +239,12 @@ func (self *JSPipe) Transact(key, toStr, valueStr, gasStr, gasPriceStr, codeStr
return NewJSReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil return NewJSReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil
} }
func (self *JSPipe) PushTx(txStr string) (*JSReceipt, error) {
tx := ethchain.NewTransactionFromBytes(ethutil.Hex2Bytes(txStr))
self.obj.TxPool().QueueTransaction(tx)
return NewJSReciept(tx.CreatesContract(), tx.CreationAddress(), tx.Hash(), tx.Sender()), nil
}
func (self *JSPipe) CompileMutan(code string) string { func (self *JSPipe) CompileMutan(code string) string {
data, err := self.Pipe.CompileMutan(code) data, err := self.Pipe.CompileMutan(code)
if err != nil { if err != nil {
@ -233,102 +254,11 @@ func (self *JSPipe) CompileMutan(code string) string {
return ethutil.Bytes2Hex(data) return ethutil.Bytes2Hex(data)
} }
func (self *JSPipe) Watch(object map[string]interface{}) *JSFilter { func ToJSMessages(messages ethstate.Messages) *ethutil.List {
return NewJSFilterFromMap(object, self.Pipe.obj)
/*} else if str, ok := object.(string); ok {
println("str")
return NewJSFilterFromString(str, self.Pipe.obj)
*/
}
func (self *JSPipe) Messages(object map[string]interface{}) string {
filter := self.Watch(object)
filter.Uninstall()
return filter.Messages()
}
type JSFilter struct {
eth ethchain.EthManager
*ethchain.Filter
quit chan bool
BlockCallback func(*ethchain.Block)
MessageCallback func(ethstate.Messages)
}
func NewJSFilterFromMap(object map[string]interface{}, eth ethchain.EthManager) *JSFilter {
filter := &JSFilter{eth, ethchain.NewFilterFromMap(object, eth), make(chan bool), nil, nil}
go filter.mainLoop()
return filter
}
func NewJSFilterFromString(str string, eth ethchain.EthManager) *JSFilter {
return nil
}
func (self *JSFilter) MessagesToJson(messages ethstate.Messages) string {
var msgs []JSMessage var msgs []JSMessage
for _, m := range messages { for _, m := range messages {
msgs = append(msgs, NewJSMessage(m)) msgs = append(msgs, NewJSMessage(m))
} }
// Return an empty array instead of "null" return ethutil.NewList(msgs)
if len(msgs) == 0 {
return "[]"
}
b, err := json.Marshal(msgs)
if err != nil {
return "{\"error\":" + err.Error() + "}"
}
return string(b)
}
func (self *JSFilter) Messages() string {
return self.MessagesToJson(self.Find())
}
func (self *JSFilter) mainLoop() {
blockChan := make(chan ethreact.Event, 5)
messageChan := make(chan ethreact.Event, 5)
// Subscribe to events
reactor := self.eth.Reactor()
reactor.Subscribe("newBlock", blockChan)
reactor.Subscribe("messages", messageChan)
out:
for {
select {
case <-self.quit:
break out
case block := <-blockChan:
if block, ok := block.Resource.(*ethchain.Block); ok {
if self.BlockCallback != nil {
self.BlockCallback(block)
}
}
case msg := <-messageChan:
if messages, ok := msg.Resource.(ethstate.Messages); ok {
if self.MessageCallback != nil {
println("messages!")
msgs := self.FilterMessages(messages)
if len(msgs) > 0 {
self.MessageCallback(msgs)
}
}
}
}
}
}
func (self *JSFilter) Changed(object interface{}) {
fmt.Printf("%T\n", object)
}
func (self *JSFilter) Uninstall() {
self.quit <- true
} }

View File

@ -1,7 +1,6 @@
package ethpipe package ethpipe
import ( import (
"encoding/json"
"strconv" "strconv"
"strings" "strings"
@ -13,10 +12,12 @@ import (
// Block interface exposed to QML // Block interface exposed to QML
type JSBlock struct { type JSBlock struct {
//Transactions string `json:"transactions"`
ref *ethchain.Block ref *ethchain.Block
Size string `json:"size"`
Number int `json:"number"` Number int `json:"number"`
Hash string `json:"hash"` Hash string `json:"hash"`
Transactions string `json:"transactions"` Transactions *ethutil.List `json:"transactions"`
Time int64 `json:"time"` Time int64 `json:"time"`
Coinbase string `json:"coinbase"` Coinbase string `json:"coinbase"`
Name string `json:"name"` Name string `json:"name"`
@ -35,12 +36,16 @@ func NewJSBlock(block *ethchain.Block) *JSBlock {
ptxs = append(ptxs, *NewJSTx(tx)) ptxs = append(ptxs, *NewJSTx(tx))
} }
/*
txJson, err := json.Marshal(ptxs) txJson, err := json.Marshal(ptxs)
if err != nil { if err != nil {
return nil return nil
} }
return &JSBlock{ref: block, Size: block.Size().String(), Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Bytes2Hex(block.Coinbase)}
*/
list := ethutil.NewList(ptxs)
return &JSBlock{ref: block, Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()), Transactions: string(txJson), Time: block.Time, Coinbase: ethutil.Bytes2Hex(block.Coinbase)} return &JSBlock{ref: block, Size: block.Size().String(), Number: int(block.Number.Uint64()), GasUsed: block.GasUsed.String(), GasLimit: block.GasLimit.String(), Hash: ethutil.Bytes2Hex(block.Hash()), Transactions: list, Time: block.Time, Coinbase: ethutil.Bytes2Hex(block.Coinbase)}
} }
func (self *JSBlock) ToString() string { func (self *JSBlock) ToString() string {

View File

@ -1,6 +1,7 @@
package ethpipe package ethpipe
import ( import (
"fmt"
"strings" "strings"
"github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethchain"
@ -51,18 +52,19 @@ func (self *Pipe) Execute(addr []byte, data []byte, value, gas, price *ethutil.V
func (self *Pipe) ExecuteObject(object *Object, data []byte, value, gas, price *ethutil.Value) ([]byte, error) { func (self *Pipe) ExecuteObject(object *Object, data []byte, value, gas, price *ethutil.Value) ([]byte, error) {
var ( var (
initiator = ethstate.NewStateObject([]byte{0}) initiator = ethstate.NewStateObject(self.obj.KeyManager().KeyPair().Address())
block = self.blockChain.CurrentBlock block = self.blockChain.CurrentBlock
stateObject = object.StateObject
) )
if self.Vm.State == nil {
self.Vm.State = self.World().State().Copy() self.Vm.State = self.World().State().Copy()
}
vm := ethvm.New(NewEnv(self.Vm.State, block, value.BigInt(), initiator.Address())) vm := ethvm.New(NewEnv(self.Vm.State, block, value.BigInt(), initiator.Address()))
vm.Verbose = true
closure := ethvm.NewClosure(&ethstate.Message{}, initiator, stateObject, object.Code, gas.BigInt(), price.BigInt()) msg := ethvm.NewMessage(vm, object.Address(), data, gas.BigInt(), price.BigInt(), value.BigInt())
ret, _, err := closure.Call(vm, data) ret, err := msg.Exec(object.Address(), initiator)
fmt.Println("returned from call", ret, err)
return ret, err return ret, err
} }
@ -149,6 +151,15 @@ func (self *Pipe) Transact(key *ethcrypto.KeyPair, rec []byte, value, gas, price
return tx.Hash(), nil return tx.Hash(), nil
} }
func (self *Pipe) PushTx(tx *ethchain.Transaction) ([]byte, error) {
self.obj.TxPool().QueueTransaction(tx)
if tx.Recipient == nil {
logger.Infof("Contract addr %x", tx.CreationAddress())
return tx.CreationAddress(), nil
}
return tx.Hash(), nil
}
func (self *Pipe) CompileMutan(code string) ([]byte, error) { func (self *Pipe) CompileMutan(code string) ([]byte, error) {
data, err := ethutil.Compile(code, false) data, err := ethutil.Compile(code, false)
if err != nil { if err != nil {

View File

@ -1,8 +1,9 @@
package ethreact package ethreact
import ( import (
"github.com/ethereum/eth-go/ethlog"
"sync" "sync"
"github.com/ethereum/eth-go/ethlog"
) )
var logger = ethlog.NewLogger("REACTOR") var logger = ethlog.NewLogger("REACTOR")
@ -32,7 +33,7 @@ func (e *EventHandler) Post(event Event) {
select { select {
case ch <- event: case ch <- event:
default: default:
logger.Warnf("subscribing channel %d to event %s blocked. skipping\n", i, event.Name) logger.Debugf("subscribing channel %d to event %s blocked. skipping\n", i, event.Name)
} }
} }
} }

View File

@ -145,6 +145,27 @@ func (p *EthereumApi) Create(args *NewTxArgs, reply *string) error {
return nil return nil
} }
type PushTxArgs struct {
Tx string
}
func (a *PushTxArgs) requirementsPushTx() error {
if a.Tx == "" {
return NewErrorResponse("PushTx requires a 'tx' as argument")
}
return nil
}
func (p *EthereumApi) PushTx(args *PushTxArgs, reply *string) error {
err := args.requirementsPushTx()
if err != nil {
return err
}
result, _ := p.pipe.PushTx(args.Tx)
*reply = NewSuccessRes(result)
return nil
}
func (p *EthereumApi) GetKey(args interface{}, reply *string) error { func (p *EthereumApi) GetKey(args interface{}, reply *string) error {
*reply = NewSuccessRes(p.pipe.Key()) *reply = NewSuccessRes(p.pipe.Key())
return nil return nil

View File

@ -28,7 +28,7 @@ func (self *State) Dump() []byte {
self.Trie.NewIterator().Each(func(key string, value *ethutil.Value) { self.Trie.NewIterator().Each(func(key string, value *ethutil.Value) {
stateObject := NewStateObjectFromBytes([]byte(key), value.Bytes()) stateObject := NewStateObjectFromBytes([]byte(key), value.Bytes())
account := Account{Balance: stateObject.Balance.String(), Nonce: stateObject.Nonce, CodeHash: ethutil.Bytes2Hex(stateObject.CodeHash)} account := Account{Balance: stateObject.Balance.String(), Nonce: stateObject.Nonce, CodeHash: ethutil.Bytes2Hex(stateObject.codeHash)}
account.Storage = make(map[string]string) account.Storage = make(map[string]string)
stateObject.EachStorage(func(key string, value *ethutil.Value) { stateObject.EachStorage(func(key string, value *ethutil.Value) {

View File

@ -3,7 +3,6 @@ package ethstate
import ( import (
"math/big" "math/big"
"github.com/ethereum/eth-go/ethcrypto"
"github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethtrie" "github.com/ethereum/eth-go/ethtrie"
"github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethutil"
@ -49,6 +48,15 @@ func (self *State) GetNonce(addr []byte) uint64 {
return 0 return 0
} }
func (self *State) GetCode(addr []byte) []byte {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.Code
}
return nil
}
// //
// Setting, updating & deleting state object methods // Setting, updating & deleting state object methods
// //
@ -57,7 +65,9 @@ func (self *State) GetNonce(addr []byte) uint64 {
func (self *State) UpdateStateObject(stateObject *StateObject) { func (self *State) UpdateStateObject(stateObject *StateObject) {
addr := stateObject.Address() addr := stateObject.Address()
ethutil.Config.Db.Put(ethcrypto.Sha3Bin(stateObject.Code), stateObject.Code) if len(stateObject.CodeHash()) > 0 {
ethutil.Config.Db.Put(stateObject.CodeHash(), stateObject.Code)
}
self.Trie.Update(string(addr), string(stateObject.RlpEncode())) self.Trie.Update(string(addr), string(stateObject.RlpEncode()))
} }
@ -103,7 +113,7 @@ func (self *State) GetOrNewStateObject(addr []byte) *StateObject {
func (self *State) NewStateObject(addr []byte) *StateObject { func (self *State) NewStateObject(addr []byte) *StateObject {
addr = ethutil.Address(addr) addr = ethutil.Address(addr)
statelogger.Infof("(+) %x\n", addr) statelogger.Debugf("(+) %x\n", addr)
stateObject := NewStateObject(addr) stateObject := NewStateObject(addr)
self.stateObjects[string(addr)] = stateObject self.stateObjects[string(addr)] = stateObject

View File

@ -32,7 +32,7 @@ type StateObject struct {
address []byte address []byte
// Shared attributes // Shared attributes
Balance *big.Int Balance *big.Int
CodeHash []byte codeHash []byte
Nonce uint64 Nonce uint64
// Contract related attributes // Contract related attributes
State *State State *State
@ -236,7 +236,7 @@ func (self *StateObject) RefundGas(gas, price *big.Int) {
func (self *StateObject) Copy() *StateObject { func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address()) stateObject := NewStateObject(self.Address())
stateObject.Balance.Set(self.Balance) stateObject.Balance.Set(self.Balance)
stateObject.CodeHash = ethutil.CopyBytes(self.CodeHash) stateObject.codeHash = ethutil.CopyBytes(self.codeHash)
stateObject.Nonce = self.Nonce stateObject.Nonce = self.Nonce
if self.State != nil { if self.State != nil {
stateObject.State = self.State.Copy() stateObject.State = self.State.Copy()
@ -245,6 +245,7 @@ func (self *StateObject) Copy() *StateObject {
stateObject.InitCode = ethutil.CopyBytes(self.InitCode) stateObject.InitCode = ethutil.CopyBytes(self.InitCode)
stateObject.storage = self.storage.Copy() stateObject.storage = self.storage.Copy()
stateObject.gasPool.Set(self.gasPool) stateObject.gasPool.Set(self.gasPool)
stateObject.remove = self.remove
return stateObject return stateObject
} }
@ -271,6 +272,11 @@ func (c *StateObject) Init() Code {
return c.InitCode return c.InitCode
} }
// To satisfy ClosureRef
func (self *StateObject) Object() *StateObject {
return self
}
// Debug stuff // Debug stuff
func (self *StateObject) CreateOutputForDiff() { func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.Balance.Bytes(), self.Nonce) fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.Balance.Bytes(), self.Nonce)
@ -292,7 +298,16 @@ func (c *StateObject) RlpEncode() []byte {
root = "" root = ""
} }
return ethutil.Encode([]interface{}{c.Nonce, c.Balance, root, ethcrypto.Sha3Bin(c.Code)}) return ethutil.Encode([]interface{}{c.Nonce, c.Balance, root, c.CodeHash()})
}
func (c *StateObject) CodeHash() ethutil.Bytes {
var codeHash []byte
if len(c.Code) > 0 {
codeHash = ethcrypto.Sha3Bin(c.Code)
}
return codeHash
} }
func (c *StateObject) RlpDecode(data []byte) { func (c *StateObject) RlpDecode(data []byte) {
@ -304,9 +319,9 @@ func (c *StateObject) RlpDecode(data []byte) {
c.storage = make(map[string]*ethutil.Value) c.storage = make(map[string]*ethutil.Value)
c.gasPool = new(big.Int) c.gasPool = new(big.Int)
c.CodeHash = decoder.Get(3).Bytes() c.codeHash = decoder.Get(3).Bytes()
c.Code, _ = ethutil.Config.Db.Get(c.CodeHash) c.Code, _ = ethutil.Config.Db.Get(c.codeHash)
} }
// Storage change object. Used by the manifest for notifying changes to // Storage change object. Used by the manifest for notifying changes to

View File

@ -92,6 +92,13 @@ func (cache *Cache) Get(key []byte) *ethutil.Value {
data, _ := cache.db.Get(key) data, _ := cache.db.Get(key)
// Create the cached value // Create the cached value
value := ethutil.NewValueFromBytes(data) value := ethutil.NewValueFromBytes(data)
defer func() {
if r := recover(); r != nil {
fmt.Println("RECOVER GET", cache, cache.nodes)
panic("bye")
}
}()
// Create caching node // Create caching node
cache.nodes[string(key)] = NewNode(key, value, false) cache.nodes[string(key)] = NewNode(key, value, false)

View File

@ -9,6 +9,22 @@ import (
"strings" "strings"
) )
type Bytes []byte
func (self Bytes) String() string {
return string(self)
}
func DeleteFromByteSlice(s [][]byte, hash []byte) [][]byte {
for i, h := range s {
if bytes.Compare(h, hash) == 0 {
return append(s[:i], s[i+1:]...)
}
}
return s
}
// Number to bytes // Number to bytes
// //
// Returns the number in bytes with the specified base // Returns the number in bytes with the specified base

61
ethutil/list.go Normal file
View File

@ -0,0 +1,61 @@
package ethutil
import (
"encoding/json"
"reflect"
)
// The list type is an anonymous slice handler which can be used
// for containing any slice type to use in an environment which
// does not support slice types (e.g., JavaScript, QML)
type List struct {
list reflect.Value
Length int
}
// Initialise a new list. Panics if non-slice type is given.
func NewList(t interface{}) *List {
list := reflect.ValueOf(t)
if list.Kind() != reflect.Slice {
panic("list container initialized with a non-slice type")
}
return &List{list, list.Len()}
}
func EmptyList() *List {
return NewList([]interface{}{})
}
// Get N element from the embedded slice. Returns nil if OOB.
func (self *List) Get(i int) interface{} {
if self.list.Len() > i {
return self.list.Index(i).Interface()
}
return nil
}
// Appends value at the end of the slice. Panics when incompatible value
// is given.
func (self *List) Append(v interface{}) {
self.list = reflect.Append(self.list, reflect.ValueOf(v))
self.Length = self.list.Len()
}
// Returns the underlying slice as interface.
func (self *List) Interface() interface{} {
return self.list.Interface()
}
// For JavaScript <3
func (self *List) ToJSON() string {
var list []interface{}
for i := 0; i < self.Length; i++ {
list = append(list, self.Get(i))
}
data, _ := json.Marshal(list)
return string(data)
}

View File

@ -45,7 +45,7 @@ func ReadAllFile(filePath string) (string, error) {
} }
func WriteFile(filePath string, content []byte) error { func WriteFile(filePath string, content []byte) error {
fh, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, os.ModePerm) fh, err := os.OpenFile(filePath, os.O_TRUNC|os.O_RDWR|os.O_CREATE, os.ModePerm)
if err != nil { if err != nil {
return err return err
} }

View File

@ -15,6 +15,10 @@ type RlpEncodeDecode interface {
RlpValue() []interface{} RlpValue() []interface{}
} }
func Rlp(encoder RlpEncode) []byte {
return encoder.RlpEncode()
}
type RlpEncoder struct { type RlpEncoder struct {
rlpData []byte rlpData []byte
} }
@ -124,6 +128,8 @@ func Encode(object interface{}) []byte {
} else { } else {
buff.Write(Encode(t.Bytes())) buff.Write(Encode(t.Bytes()))
} }
case Bytes:
buff.Write(Encode([]byte(t)))
case []byte: case []byte:
if len(t) == 1 && t[0] <= 0x7f { if len(t) == 1 && t[0] <= 0x7f {
buff.Write(t) buff.Write(t)

View File

@ -2,9 +2,11 @@ package ethutil
import ( import (
"fmt" "fmt"
"strings"
"github.com/obscuren/mutan" "github.com/obscuren/mutan"
"github.com/obscuren/mutan/backends" "github.com/obscuren/mutan/backends"
"strings" "github.com/obscuren/serpent-go"
) )
// General compile function // General compile function
@ -14,7 +16,6 @@ func Compile(script string, silent bool) (ret []byte, err error) {
if len(line) > 1 && line[0:2] == "#!" { if len(line) > 1 && line[0:2] == "#!" {
switch line { switch line {
/*
case "#!serpent": case "#!serpent":
byteCode, err := serpent.Compile(script) byteCode, err := serpent.Compile(script)
if err != nil { if err != nil {
@ -22,7 +23,6 @@ func Compile(script string, silent bool) (ret []byte, err error) {
} }
return byteCode, nil return byteCode, nil
*/
} }
} else { } else {

36
ethutil/set.go Normal file
View File

@ -0,0 +1,36 @@
package ethutil
type Settable interface {
AsSet() UniqueSet
}
type Stringable interface {
String() string
}
type UniqueSet map[string]struct{}
func NewSet(v ...Stringable) UniqueSet {
set := make(UniqueSet)
for _, val := range v {
set.Insert(val)
}
return set
}
func (self UniqueSet) Insert(k Stringable) UniqueSet {
self[k.String()] = struct{}{}
return self
}
func (self UniqueSet) Include(k Stringable) bool {
_, ok := self[k.String()]
return ok
}
func Set(s Settable) UniqueSet {
return s.AsSet()
}

15
ethutil/size.go Normal file
View File

@ -0,0 +1,15 @@
package ethutil
import "fmt"
type StorageSize float64
func (self StorageSize) String() string {
if self > 1000000 {
return fmt.Sprintf("%.2f mB", self/1000000)
} else if self > 1000 {
return fmt.Sprintf("%.2f kB", self/1000)
} else {
return fmt.Sprintf("%.2f B", self)
}
}

12
ethutil/size_test.go Normal file
View File

@ -0,0 +1,12 @@
package ethutil
import (
"fmt"
"testing"
)
func TestSize(t *testing.T) {
fmt.Println(StorageSize(2381273))
fmt.Println(StorageSize(2192))
fmt.Println(StorageSize(12))
}

View File

@ -1,9 +1,11 @@
package ethutil package ethutil
import ( import (
"bytes"
"fmt" "fmt"
"math/big" "math/big"
"reflect" "reflect"
"strconv"
) )
// Data values are returned by the rlp decoder. The data values represents // Data values are returned by the rlp decoder. The data values represents
@ -93,6 +95,9 @@ func (val *Value) Int() int64 {
return new(big.Int).SetBytes(Val).Int64() return new(big.Int).SetBytes(Val).Int64()
} else if Val, ok := val.Val.(*big.Int); ok { } else if Val, ok := val.Val.(*big.Int); ok {
return Val.Int64() return Val.Int64()
} else if Val, ok := val.Val.(string); ok {
n, _ := strconv.Atoi(Val)
return int64(n)
} }
return 0 return 0
@ -113,6 +118,8 @@ func (val *Value) BigInt() *big.Int {
return b return b
} else if a, ok := val.Val.(*big.Int); ok { } else if a, ok := val.Val.(*big.Int); ok {
return a return a
} else if a, ok := val.Val.(string); ok {
return Big(a)
} else { } else {
return big.NewInt(int64(val.Uint())) return big.NewInt(int64(val.Uint()))
} }
@ -141,6 +148,8 @@ func (val *Value) Bytes() []byte {
return []byte(s) return []byte(s)
} else if s, ok := val.Val.(*big.Int); ok { } else if s, ok := val.Val.(*big.Int); ok {
return s.Bytes() return s.Bytes()
} else {
return big.NewInt(val.Int()).Bytes()
} }
return []byte{} return []byte{}
@ -244,10 +253,7 @@ func (val *Value) Cmp(o *Value) bool {
} }
func (self *Value) DeepCmp(o *Value) bool { func (self *Value) DeepCmp(o *Value) bool {
a := NewValue(self.BigInt()) return bytes.Compare(self.Bytes(), o.Bytes()) == 0
b := NewValue(o.BigInt())
return a.Cmp(b)
} }
func (val *Value) Encode() []byte { func (val *Value) Encode() []byte {

View File

@ -2,6 +2,7 @@ package ethutil
import ( import (
"bytes" "bytes"
"fmt"
"math/big" "math/big"
"testing" "testing"
) )
@ -78,3 +79,8 @@ func TestMath(t *testing.T) {
t.Error("Expected 0, got", a) t.Error("Expected 0, got", a)
} }
} }
func TestString(t *testing.T) {
a := NewValue("10")
fmt.Println("VALUE WITH STRING:", a.Int())
}

View File

@ -12,6 +12,7 @@ import (
type ClosureRef interface { type ClosureRef interface {
ReturnGas(*big.Int, *big.Int) ReturnGas(*big.Int, *big.Int)
Address() []byte Address() []byte
Object() *ethstate.StateObject
GetStorage(*big.Int) *ethutil.Value GetStorage(*big.Int) *ethutil.Value
SetStorage(*big.Int, *ethutil.Value) SetStorage(*big.Int, *ethutil.Value)
} }

View File

@ -65,13 +65,13 @@ func (st *Stack) Peekn() (*big.Int, *big.Int) {
} }
func (st *Stack) Swapn(n int) (*big.Int, *big.Int) { func (st *Stack) Swapn(n int) (*big.Int, *big.Int) {
st.data[n], st.data[0] = st.data[0], st.data[n] st.data[len(st.data)-n], st.data[len(st.data)-1] = st.data[len(st.data)-1], st.data[len(st.data)-n]
return st.data[n], st.data[0] return st.data[len(st.data)-n], st.data[len(st.data)-1]
} }
func (st *Stack) Dupn(n int) *big.Int { func (st *Stack) Dupn(n int) *big.Int {
st.Push(st.data[n]) st.Push(st.data[len(st.data)-n])
return st.Peek() return st.Peek()
} }

View File

@ -49,6 +49,8 @@ const (
CODESIZE = 0x38 CODESIZE = 0x38
CODECOPY = 0x39 CODECOPY = 0x39
GASPRICE = 0x3a GASPRICE = 0x3a
EXTCODECOPY = 0x3b
EXTCODESIZE = 0x3c
// 0x40 range - block operations // 0x40 range - block operations
PREVHASH = 0x40 PREVHASH = 0x40
@ -145,6 +147,8 @@ const (
CREATE = 0xf0 CREATE = 0xf0
CALL = 0xf1 CALL = 0xf1
RETURN = 0xf2 RETURN = 0xf2
POST = 0xf3
CALLSTATELESS = 0xf4
// 0x70 range - other // 0x70 range - other
LOG = 0xfe // XXX Unofficial LOG = 0xfe // XXX Unofficial
@ -202,6 +206,8 @@ var opCodeToString = map[OpCode]string{
NUMBER: "NUMBER", NUMBER: "NUMBER",
DIFFICULTY: "DIFFICULTY", DIFFICULTY: "DIFFICULTY",
GASLIMIT: "GASLIMIT", GASLIMIT: "GASLIMIT",
EXTCODESIZE: "EXTCODESIZE",
EXTCODECOPY: "EXTCODECOPY",
// 0x50 range - 'storage' and execution // 0x50 range - 'storage' and execution
POP: "POP", POP: "POP",
@ -290,6 +296,8 @@ var opCodeToString = map[OpCode]string{
CREATE: "CREATE", CREATE: "CREATE",
CALL: "CALL", CALL: "CALL",
RETURN: "RETURN", RETURN: "RETURN",
POST: "POST",
CALLSTATELESS: "CALLSTATELESS",
// 0x70 range - other // 0x70 range - other
LOG: "LOG", LOG: "LOG",
@ -342,7 +350,12 @@ var OpCodes = map[string]byte{
"CALLVALUE": 0x34, "CALLVALUE": 0x34,
"CALLDATALOAD": 0x35, "CALLDATALOAD": 0x35,
"CALLDATASIZE": 0x36, "CALLDATASIZE": 0x36,
"GASPRICE": 0x38, "CALLDATACOPY": 0x37,
"CODESIZE": 0x38,
"CODECOPY": 0x39,
"GASPRICE": 0x3a,
"EXTCODECOPY": 0x3b,
"EXTCODESIZE": 0x3c,
// 0x40 range - block operations // 0x40 range - block operations
"PREVHASH": 0x40, "PREVHASH": 0x40,
@ -438,6 +451,8 @@ var OpCodes = map[string]byte{
"CREATE": 0xf0, "CREATE": 0xf0,
"CALL": 0xf1, "CALL": 0xf1,
"RETURN": 0xf2, "RETURN": 0xf2,
"POST": 0xf3,
"CALLSTATELESS": 0xf4,
// 0x70 range - other // 0x70 range - other
"LOG": 0xfe, "LOG": 0xfe,

View File

@ -1,8 +1,8 @@
package ethvm package ethvm
import ( import (
"container/list"
"fmt" "fmt"
"math"
"math/big" "math/big"
"github.com/ethereum/eth-go/ethcrypto" "github.com/ethereum/eth-go/ethcrypto"
@ -18,11 +18,6 @@ type Debugger interface {
} }
type Vm struct { type Vm struct {
// Stack for processing contracts
stack *Stack
// non-persistent key/value memory storage
mem map[string]*big.Int
env Environment env Environment
Verbose bool Verbose bool
@ -40,6 +35,8 @@ type Vm struct {
Fn string Fn string
Recoverable bool Recoverable bool
queue *list.List
} }
type Environment interface { type Environment interface {
@ -66,7 +63,20 @@ func New(env Environment) *Vm {
lt = LogTyDiff lt = LogTyDiff
} }
return &Vm{env: env, logTy: lt, Recoverable: true} return &Vm{env: env, logTy: lt, Recoverable: true, queue: list.New()}
}
func calcMemSize(off, l *big.Int) *big.Int {
if l.Cmp(ethutil.Big0) == 0 {
return ethutil.Big0
}
return new(big.Int).Add(off, l)
}
// Simple helper
func u256(n int64) *big.Int {
return big.NewInt(n)
} }
func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
@ -122,7 +132,6 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
// XXX Leave this Println intact. Don't change this to the log system. // XXX Leave this Println intact. Don't change this to the log system.
// Used for creating diffs between implementations // Used for creating diffs between implementations
if self.logTy == LogTyDiff { if self.logTy == LogTyDiff {
/*
switch op { switch op {
case STOP, RETURN, SUICIDE: case STOP, RETURN, SUICIDE:
closure.object.EachStorage(func(key string, value *ethutil.Value) { closure.object.EachStorage(func(key string, value *ethutil.Value) {
@ -130,7 +139,6 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes()) fmt.Printf("%x %x\n", new(big.Int).SetBytes([]byte(key)).Bytes(), value.Bytes())
}) })
} }
*/
b := pc.Bytes() b := pc.Bytes()
if len(b) == 0 { if len(b) == 0 {
@ -149,7 +157,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
addStepGasUsage(GasStep) addStepGasUsage(GasStep)
var newMemSize uint64 = 0 var newMemSize *big.Int = ethutil.Big0
switch op { switch op {
case STOP: case STOP:
gas.Set(ethutil.Big0) gas.Set(ethutil.Big0)
@ -173,52 +181,64 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
gas.Set(GasBalance) gas.Set(GasBalance)
case MSTORE: case MSTORE:
require(2) require(2)
newMemSize = stack.Peek().Uint64() + 32 newMemSize = calcMemSize(stack.Peek(), u256(32))
case MLOAD: case MLOAD:
require(1) require(1)
newMemSize = stack.Peek().Uint64() + 32 newMemSize = calcMemSize(stack.Peek(), u256(32))
case MSTORE8: case MSTORE8:
require(2) require(2)
newMemSize = stack.Peek().Uint64() + 1 newMemSize = calcMemSize(stack.Peek(), u256(1))
case RETURN: case RETURN:
require(2) require(2)
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64() newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2])
case SHA3: case SHA3:
require(2) require(2)
gas.Set(GasSha) gas.Set(GasSha)
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-2].Uint64() newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-2])
case CALLDATACOPY: case CALLDATACOPY:
require(3) require(2)
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3])
case CODECOPY: case CODECOPY:
require(3) require(3)
newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() newMemSize = calcMemSize(stack.Peek(), stack.data[stack.Len()-3])
case CALL: case EXTCODECOPY:
require(4)
newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-4])
case CALL, CALLSTATELESS:
require(7) require(7)
gas.Set(GasCall) gas.Set(GasCall)
addStepGasUsage(stack.data[stack.Len()-1]) addStepGasUsage(stack.data[stack.Len()-1])
x := stack.data[stack.Len()-6].Uint64() + stack.data[stack.Len()-7].Uint64() x := calcMemSize(stack.data[stack.Len()-6], stack.data[stack.Len()-7])
y := stack.data[stack.Len()-4].Uint64() + stack.data[stack.Len()-5].Uint64() y := calcMemSize(stack.data[stack.Len()-4], stack.data[stack.Len()-5])
newMemSize = uint64(math.Max(float64(x), float64(y))) newMemSize = ethutil.BigMax(x, y)
case CREATE: case CREATE:
require(3) require(3)
gas.Set(GasCreate) gas.Set(GasCreate)
newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64() newMemSize = calcMemSize(stack.data[stack.Len()-2], stack.data[stack.Len()-3])
} }
newMemSize = (newMemSize + 31) / 32 * 32 if newMemSize.Cmp(ethutil.Big0) > 0 {
if newMemSize > uint64(mem.Len()) { newMemSize.Add(newMemSize, u256(31))
m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32 newMemSize.Div(newMemSize, u256(32))
addStepGasUsage(big.NewInt(int64(m))) newMemSize.Mul(newMemSize, u256(32))
if newMemSize.Cmp(u256(int64(mem.Len()))) > 0 {
memGasUsage := new(big.Int).Sub(newMemSize, u256(int64(mem.Len())))
memGasUsage.Mul(GasMemory, memGasUsage)
memGasUsage.Div(memGasUsage, u256(32))
addStepGasUsage(memGasUsage)
}
} }
if !closure.UseGas(gas) { if !closure.UseGas(gas) {
@ -232,7 +252,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf("(pc) %-3d -o- %-14s", pc, op.String()) self.Printf("(pc) %-3d -o- %-14s", pc, op.String())
self.Printf(" (g) %-3v (%v)", gas, closure.Gas) self.Printf(" (g) %-3v (%v)", gas, closure.Gas)
mem.Resize(newMemSize) mem.Resize(newMemSize.Uint64())
switch op { switch op {
case LOG: case LOG:
@ -551,14 +571,32 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
code := closure.Args[cOff : cOff+l] code := closure.Args[cOff : cOff+l]
mem.Set(mOff, l, code) mem.Set(mOff, l, code)
case CODESIZE: case CODESIZE, EXTCODESIZE:
l := big.NewInt(int64(len(closure.Code))) var code []byte
if op == EXTCODECOPY {
addr := stack.Pop().Bytes()
code = self.env.State().GetCode(addr)
} else {
code = closure.Code
}
l := big.NewInt(int64(len(code)))
stack.Push(l) stack.Push(l)
self.Printf(" => %d", l) self.Printf(" => %d", l)
case CODECOPY: case CODECOPY, EXTCODECOPY:
var code []byte
if op == EXTCODECOPY {
addr := stack.Pop().Bytes()
code = self.env.State().GetCode(addr)
} else {
code = closure.Code
}
var ( var (
size = int64(len(closure.Code)) size = int64(len(code))
mOff = stack.Pop().Int64() mOff = stack.Pop().Int64()
cOff = stack.Pop().Int64() cOff = stack.Pop().Int64()
l = stack.Pop().Int64() l = stack.Pop().Int64()
@ -571,9 +609,9 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
l = 0 l = 0
} }
code := closure.Code[cOff : cOff+l] codeCopy := code[cOff : cOff+l]
mem.Set(mOff, l, code) mem.Set(mOff, l, codeCopy)
case GASPRICE: case GASPRICE:
stack.Push(closure.Price) stack.Push(closure.Price)
@ -632,11 +670,15 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
stack.Pop() stack.Pop()
case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16: case DUP1, DUP2, DUP3, DUP4, DUP5, DUP6, DUP7, DUP8, DUP9, DUP10, DUP11, DUP12, DUP13, DUP14, DUP15, DUP16:
n := int(op - DUP1 + 1) n := int(op - DUP1 + 1)
stack.Dupn(n) v := stack.Dupn(n)
self.Printf(" => [%d] 0x%x", n, stack.Peek().Bytes()) self.Printf(" => [%d] 0x%x", n, stack.Peek().Bytes())
if OpCode(closure.Get(new(big.Int).Add(pc, ethutil.Big1)).Uint()) == POP && OpCode(closure.Get(new(big.Int).Add(pc, big.NewInt(2))).Uint()) == POP {
fmt.Println(toValue(v))
}
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16: case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
n := int(op - SWAP1 + 1) n := int(op - SWAP1 + 2)
x, y := stack.Swapn(n) x, y := stack.Swapn(n)
self.Printf(" => [%d] %x [0] %x", n, x.Bytes(), y.Bytes()) self.Printf(" => [%d] %x [0] %x", n, x.Bytes(), y.Bytes())
@ -656,12 +698,12 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf(" => 0x%x", val) self.Printf(" => 0x%x", val)
case MSTORE8: case MSTORE8:
require(2) require(2)
val, mStart := stack.Popn() off := stack.Pop()
//base.And(val, new(big.Int).SetInt64(0xff)) val := stack.Pop()
//mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256))
mem.store[mStart.Int64()] = byte(val.Int64() & 0xff)
self.Printf(" => 0x%x", val) mem.store[off.Int64()] = byte(val.Int64() & 0xff)
self.Printf(" => [%v] 0x%x", off, val)
case SLOAD: case SLOAD:
require(1) require(1)
loc := stack.Pop() loc := stack.Pop()
@ -711,6 +753,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
err error err error
value = stack.Pop() value = stack.Pop()
size, offset = stack.Popn() size, offset = stack.Popn()
input = mem.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(closure.Gas)
// Snapshot the current stack so we are able to // Snapshot the current stack so we are able to
// revert back to it later. // revert back to it later.
@ -726,37 +770,10 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf(" (*) %x", addr).Endl() self.Printf(" (*) %x", addr).Endl()
msg := self.env.State().Manifest().AddMessage(&ethstate.Message{
To: addr, From: closure.Address(),
Origin: self.env.Origin(),
Block: self.env.BlockHash(), Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(),
Value: value,
})
// Create a new contract
contract := self.env.State().NewStateObject(addr)
if contract.Balance.Cmp(value) >= 0 {
closure.object.SubAmount(value)
contract.AddAmount(value)
// Set the init script
initCode := mem.Get(offset.Int64(), size.Int64())
msg.Input = initCode
// Transfer all remaining gas to the new
// contract so it may run the init script
gas := new(big.Int).Set(closure.Gas)
closure.UseGas(closure.Gas) closure.UseGas(closure.Gas)
// Create the closure msg := NewMessage(self, addr, input, gas, closure.Price, value)
c := NewClosure(msg, closure, contract, initCode, gas, closure.Price) ret, err := msg.Exec(addr, closure)
// Call the closure and set the return value as
// main script.
contract.Code, _, err = c.Call(self, nil)
} else {
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
}
if err != nil { if err != nil {
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
@ -765,17 +782,18 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
self.Printf("CREATE err %v", err) self.Printf("CREATE err %v", err)
} else { } else {
stack.Push(ethutil.BigD(addr)) msg.object.Code = ret
msg.Output = contract.Code stack.Push(ethutil.BigD(addr))
} }
self.Endl() self.Endl()
// Debug hook // Debug hook
if self.Dbg != nil { if self.Dbg != nil {
self.Dbg.SetCode(closure.Code) self.Dbg.SetCode(closure.Code)
} }
case CALL: case CALL, CALLSTATELESS:
require(7) require(7)
self.Endl() self.Endl()
@ -791,37 +809,20 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
// Get the arguments from the memory // Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64()) args := mem.Get(inOffset.Int64(), inSize.Int64())
msg := self.env.State().Manifest().AddMessage(&ethstate.Message{
To: addr.Bytes(), From: closure.Address(),
Input: args,
Origin: self.env.Origin(),
Block: self.env.BlockHash(), Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(),
Value: value,
})
if closure.object.Balance.Cmp(value) < 0 {
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
closure.ReturnGas(gas, nil)
stack.Push(ethutil.BigFalse)
} else {
snapshot := self.env.State().Copy() snapshot := self.env.State().Copy()
stateObject := self.env.State().GetOrNewStateObject(addr.Bytes()) var executeAddr []byte
if op == CALLSTATELESS {
executeAddr = closure.Address()
} else {
executeAddr = addr.Bytes()
}
closure.object.SubAmount(value) msg := NewMessage(self, executeAddr, args, gas, closure.Price, value)
stateObject.AddAmount(value) ret, err := msg.Exec(addr.Bytes(), closure)
// Create a new callable closure
c := NewClosure(msg, closure, stateObject, stateObject.Code, gas, closure.Price)
// Executer the closure and get the return value (if any)
ret, _, err := c.Call(self, args)
if err != nil { if err != nil {
stack.Push(ethutil.BigFalse) stack.Push(ethutil.BigFalse)
vmlogger.Debugf("Closure execution failed. %v\n", err)
self.env.State().Set(snapshot) self.env.State().Set(snapshot)
} else { } else {
stack.Push(ethutil.BigTrue) stack.Push(ethutil.BigTrue)
@ -829,13 +830,27 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
mem.Set(retOffset.Int64(), retSize.Int64(), ret) mem.Set(retOffset.Int64(), retSize.Int64(), ret)
} }
msg.Output = ret
// Debug hook // Debug hook
if self.Dbg != nil { if self.Dbg != nil {
self.Dbg.SetCode(closure.Code) self.Dbg.SetCode(closure.Code)
} }
}
case POST:
require(5)
self.Endl()
gas := stack.Pop()
// Pop gas and value of the stack.
value, addr := stack.Popn()
// Pop input size and offset
inSize, inOffset := stack.Popn()
// Get the arguments from the memory
args := mem.Get(inOffset.Int64(), inSize.Int64())
msg := NewMessage(self, addr.Bytes(), args, gas, closure.Price, value)
msg.Postpone()
case RETURN: case RETURN:
require(2) require(2)
size, offset := stack.Popn() size, offset := stack.Popn()
@ -861,6 +876,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
default: default:
vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op) vmlogger.Debugf("(pc) %-3v Invalid opcode %x\n", pc, op)
//panic(fmt.Sprintf("Invalid opcode %x", op))
return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op) return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op)
} }
@ -887,6 +904,10 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) {
} }
} }
func (self *Vm) Queue() *list.List {
return self.queue
}
func (self *Vm) Printf(format string, v ...interface{}) *Vm { func (self *Vm) Printf(format string, v ...interface{}) *Vm {
if self.Verbose && self.logTy == LogTyPretty { if self.Verbose && self.logTy == LogTyPretty {
self.logStr += fmt.Sprintf(format, v...) self.logStr += fmt.Sprintf(format, v...)
@ -918,3 +939,83 @@ func ensure256(x *big.Int) {
x.SetInt64(0) x.SetInt64(0)
} }
} }
type Message struct {
vm *Vm
closure *Closure
address, input []byte
gas, price, value *big.Int
object *ethstate.StateObject
}
func NewMessage(vm *Vm, address, input []byte, gas, gasPrice, value *big.Int) *Message {
return &Message{vm: vm, address: address, input: input, gas: gas, price: gasPrice, value: value}
}
func (self *Message) Postpone() {
self.vm.queue.PushBack(self)
}
func (self *Message) Addr() []byte {
return self.address
}
func (self *Message) Exec(codeAddr []byte, caller ClosureRef) (ret []byte, err error) {
fmt.Printf("%x %x\n", codeAddr[0:4], self.address[0:4])
queue := self.vm.queue
self.vm.queue = list.New()
defer func() {
if err == nil {
queue.PushBackList(self.vm.queue)
}
self.vm.queue = queue
}()
msg := self.vm.env.State().Manifest().AddMessage(&ethstate.Message{
To: self.address, From: caller.Address(),
Input: self.input,
Origin: self.vm.env.Origin(),
Block: self.vm.env.BlockHash(), Timestamp: self.vm.env.Time(), Coinbase: self.vm.env.Coinbase(), Number: self.vm.env.BlockNumber(),
Value: self.value,
})
object := caller.Object()
if object.Balance.Cmp(self.value) < 0 {
caller.ReturnGas(self.gas, self.price)
err = fmt.Errorf("Insufficient funds to transfer value. Req %v, has %v", self.value, object.Balance)
} else {
stateObject := self.vm.env.State().GetOrNewStateObject(self.address)
self.object = stateObject
caller.Object().SubAmount(self.value)
stateObject.AddAmount(self.value)
// Retrieve the executing code
code := self.vm.env.State().GetCode(codeAddr)
// Create a new callable closure
c := NewClosure(msg, caller, stateObject, code, self.gas, self.price)
// Executer the closure and get the return value (if any)
ret, _, err = c.Call(self.vm, self.input)
msg.Output = ret
return ret, err
}
return
}
// Mainly used for print variables and passing to Print*
func toValue(val *big.Int) interface{} {
// Let's assume a string on right padded zero's
b := val.Bytes()
if b[0] != 0 && b[len(b)-1] == 0x0 && b[len(b)-2] == 0x0 {
return string(b)
}
return val
}

View File

@ -11,7 +11,6 @@ type ClientIdentity interface {
} }
type SimpleClientIdentity struct { type SimpleClientIdentity struct {
clientString string
clientIdentifier string clientIdentifier string
version string version string
customIdentifier string customIdentifier string
@ -25,28 +24,31 @@ func NewSimpleClientIdentity(clientIdentifier string, version string, customIden
version: version, version: version,
customIdentifier: customIdentifier, customIdentifier: customIdentifier,
os: runtime.GOOS, os: runtime.GOOS,
implementation: "Go", implementation: runtime.Version(),
} }
clientIdentity.init()
return clientIdentity return clientIdentity
} }
func (c *SimpleClientIdentity) init() { func (c *SimpleClientIdentity) init() {
c.clientString = fmt.Sprintf("%s/v%s/%s/%s/%s", }
func (c *SimpleClientIdentity) String() string {
var id string
if len(c.customIdentifier) > 0 {
id = "/" + c.customIdentifier
}
return fmt.Sprintf("%s/v%s%s/%s/%s",
c.clientIdentifier, c.clientIdentifier,
c.version, c.version,
c.customIdentifier, id,
c.os, c.os,
c.implementation) c.implementation)
} }
func (c *SimpleClientIdentity) String() string {
return c.clientString
}
func (c *SimpleClientIdentity) SetCustomIdentifier(customIdentifier string) { func (c *SimpleClientIdentity) SetCustomIdentifier(customIdentifier string) {
c.customIdentifier = customIdentifier c.customIdentifier = customIdentifier
c.init()
} }
func (c *SimpleClientIdentity) GetCustomIdentifier() string { func (c *SimpleClientIdentity) GetCustomIdentifier() string {

199
ethwire/messages2.go Normal file
View File

@ -0,0 +1,199 @@
package ethwire
import (
"bytes"
"errors"
"fmt"
"net"
"time"
"github.com/ethereum/eth-go/ethutil"
)
// The connection object allows you to set up a connection to the Ethereum network.
// The Connection object takes care of all encoding and sending objects properly over
// the network.
type Connection struct {
conn net.Conn
nTimeout time.Duration
pendingMessages Messages
}
// Create a new connection to the Ethereum network
func New(conn net.Conn) *Connection {
return &Connection{conn: conn, nTimeout: 500}
}
// Read, reads from the network. It will block until the next message is received.
func (self *Connection) Read() *Msg {
if len(self.pendingMessages) == 0 {
self.readMessages()
}
ret := self.pendingMessages[0]
self.pendingMessages = self.pendingMessages[1:]
return ret
}
// Write to the Ethereum network specifying the type of the message and
// the data. Data can be of type RlpEncodable or []interface{}. Returns
// nil or if something went wrong an error.
func (self *Connection) Write(typ MsgType, v ...interface{}) error {
var pack []byte
slice := [][]interface{}{[]interface{}{byte(typ)}}
for _, value := range v {
if encodable, ok := value.(ethutil.RlpEncodeDecode); ok {
slice = append(slice, encodable.RlpValue())
} else if raw, ok := value.([]interface{}); ok {
slice = append(slice, raw)
} else {
panic(fmt.Sprintf("Unable to 'write' object of type %T", value))
}
}
// Encode the type and the (RLP encoded) data for sending over the wire
encoded := ethutil.NewValue(slice).Encode()
payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32)
// Write magic token and payload length (first 8 bytes)
pack = append(MagicToken, payloadLength...)
pack = append(pack, encoded...)
// Write to the connection
_, err := self.conn.Write(pack)
if err != nil {
return err
}
return nil
}
func (self *Connection) readMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
if len(data) == 0 {
return nil, nil, true, nil
}
if len(data) <= 8 {
return nil, remaining, false, errors.New("Invalid message")
}
// Check if the received 4 first bytes are the magic token
if bytes.Compare(MagicToken, data[:4]) != 0 {
return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
}
messageLength := ethutil.BytesToNumber(data[4:8])
remaining = data[8+messageLength:]
if int(messageLength) > len(data[8:]) {
return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
}
message := data[8 : 8+messageLength]
decoder := ethutil.NewValueFromBytes(message)
// Type of message
t := decoder.Get(0).Uint()
// Actual data
d := decoder.SliceFrom(1)
msg = &Msg{
Type: MsgType(t),
Data: d,
}
return
}
// The basic message reader waits for data on the given connection, decoding
// and doing a few sanity checks such as if there's a data type and
// unmarhals the given data
func (self *Connection) readMessages() (err error) {
// The recovering function in case anything goes horribly wrong
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("ethwire.ReadMessage error: %v", r)
}
}()
// Buff for writing network message to
//buff := make([]byte, 1440)
var buff []byte
var totalBytes int
for {
// Give buffering some time
self.conn.SetReadDeadline(time.Now().Add(self.nTimeout * time.Millisecond))
// Create a new temporarily buffer
b := make([]byte, 1440)
// Wait for a message from this peer
n, _ := self.conn.Read(b)
if err != nil && n == 0 {
if err.Error() != "EOF" {
fmt.Println("err now", err)
return err
} else {
break
}
// Messages can't be empty
} else if n == 0 {
break
}
buff = append(buff, b[:n]...)
totalBytes += n
}
// Reslice buffer
buff = buff[:totalBytes]
msg, remaining, done, err := self.readMessage(buff)
for ; done != true; msg, remaining, done, err = self.readMessage(remaining) {
//log.Println("rx", msg)
if msg != nil {
self.pendingMessages = append(self.pendingMessages, msg)
}
}
return
}
func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
if len(data) == 0 {
return nil, nil, true, nil
}
if len(data) <= 8 {
return nil, remaining, false, errors.New("Invalid message")
}
// Check if the received 4 first bytes are the magic token
if bytes.Compare(MagicToken, data[:4]) != 0 {
return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
}
messageLength := ethutil.BytesToNumber(data[4:8])
remaining = data[8+messageLength:]
if int(messageLength) > len(data[8:]) {
return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
}
message := data[8 : 8+messageLength]
decoder := ethutil.NewValueFromBytes(message)
// Type of message
t := decoder.Get(0).Uint()
// Actual data
d := decoder.SliceFrom(1)
msg = &Msg{
Type: MsgType(t),
Data: d,
}
return
}
func bufferedRead(conn net.Conn) ([]byte, error) {
return nil, nil
}

View File

@ -4,7 +4,6 @@ package ethwire
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"net" "net"
"time" "time"
@ -31,20 +30,16 @@ const (
MsgDiscTy = 0x01 MsgDiscTy = 0x01
MsgPingTy = 0x02 MsgPingTy = 0x02
MsgPongTy = 0x03 MsgPongTy = 0x03
MsgGetPeersTy = 0x10 MsgGetPeersTy = 0x04
MsgPeersTy = 0x11 MsgPeersTy = 0x05
MsgStatusTy = 0x10
MsgGetTxsTy = 0x11
MsgTxTy = 0x12 MsgTxTy = 0x12
MsgGetChainTy = 0x14 MsgGetBlockHashesTy = 0x13
MsgNotInChainTy = 0x15 MsgBlockHashesTy = 0x14
MsgGetTxsTy = 0x16 MsgGetBlocksTy = 0x15
MsgGetBlockHashesTy = 0x17 MsgBlockTy = 0x16
MsgBlockHashesTy = 0x18
MsgGetBlocksTy = 0x19
MsgBlockTy = 0x13
MsgOldBlockTy = 0xbb
MsgTalkTy = 0xff
) )
var msgTypeToString = map[MsgType]string{ var msgTypeToString = map[MsgType]string{
@ -53,12 +48,11 @@ var msgTypeToString = map[MsgType]string{
MsgPingTy: "Ping", MsgPingTy: "Ping",
MsgPongTy: "Pong", MsgPongTy: "Pong",
MsgGetPeersTy: "Get peers", MsgGetPeersTy: "Get peers",
MsgStatusTy: "Status",
MsgPeersTy: "Peers", MsgPeersTy: "Peers",
MsgTxTy: "Transactions", MsgTxTy: "Transactions",
MsgBlockTy: "Blocks", MsgBlockTy: "Blocks",
MsgGetChainTy: "Get chain",
MsgGetTxsTy: "Get Txs", MsgGetTxsTy: "Get Txs",
MsgNotInChainTy: "Not in chain",
MsgGetBlockHashesTy: "Get block hashes", MsgGetBlockHashesTy: "Get block hashes",
MsgBlockHashesTy: "Block hashes", MsgBlockHashesTy: "Block hashes",
MsgGetBlocksTy: "Get blocks", MsgGetBlocksTy: "Get blocks",
@ -83,194 +77,6 @@ func NewMessage(msgType MsgType, data interface{}) *Msg {
type Messages []*Msg type Messages []*Msg
// The connection object allows you to set up a connection to the Ethereum network.
// The Connection object takes care of all encoding and sending objects properly over
// the network.
type Connection struct {
conn net.Conn
nTimeout time.Duration
pendingMessages Messages
}
// Create a new connection to the Ethereum network
func New(conn net.Conn) *Connection {
return &Connection{conn: conn, nTimeout: 500}
}
// Read, reads from the network. It will block until the next message is received.
func (self *Connection) Read() *Msg {
if len(self.pendingMessages) == 0 {
self.readMessages()
}
ret := self.pendingMessages[0]
self.pendingMessages = self.pendingMessages[1:]
return ret
}
// Write to the Ethereum network specifying the type of the message and
// the data. Data can be of type RlpEncodable or []interface{}. Returns
// nil or if something went wrong an error.
func (self *Connection) Write(typ MsgType, v ...interface{}) error {
var pack []byte
slice := [][]interface{}{[]interface{}{byte(typ)}}
for _, value := range v {
if encodable, ok := value.(ethutil.RlpEncodeDecode); ok {
slice = append(slice, encodable.RlpValue())
} else if raw, ok := value.([]interface{}); ok {
slice = append(slice, raw)
} else {
panic(fmt.Sprintf("Unable to 'write' object of type %T", value))
}
}
// Encode the type and the (RLP encoded) data for sending over the wire
encoded := ethutil.NewValue(slice).Encode()
payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32)
// Write magic token and payload length (first 8 bytes)
pack = append(MagicToken, payloadLength...)
pack = append(pack, encoded...)
// Write to the connection
_, err := self.conn.Write(pack)
if err != nil {
return err
}
return nil
}
func (self *Connection) readMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
if len(data) == 0 {
return nil, nil, true, nil
}
if len(data) <= 8 {
return nil, remaining, false, errors.New("Invalid message")
}
// Check if the received 4 first bytes are the magic token
if bytes.Compare(MagicToken, data[:4]) != 0 {
return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
}
messageLength := ethutil.BytesToNumber(data[4:8])
remaining = data[8+messageLength:]
if int(messageLength) > len(data[8:]) {
return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
}
message := data[8 : 8+messageLength]
decoder := ethutil.NewValueFromBytes(message)
// Type of message
t := decoder.Get(0).Uint()
// Actual data
d := decoder.SliceFrom(1)
msg = &Msg{
Type: MsgType(t),
Data: d,
}
return
}
// The basic message reader waits for data on the given connection, decoding
// and doing a few sanity checks such as if there's a data type and
// unmarhals the given data
func (self *Connection) readMessages() (err error) {
// The recovering function in case anything goes horribly wrong
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("ethwire.ReadMessage error: %v", r)
}
}()
// Buff for writing network message to
//buff := make([]byte, 1440)
var buff []byte
var totalBytes int
for {
// Give buffering some time
self.conn.SetReadDeadline(time.Now().Add(self.nTimeout * time.Millisecond))
// Create a new temporarily buffer
b := make([]byte, 1440)
// Wait for a message from this peer
n, _ := self.conn.Read(b)
if err != nil && n == 0 {
if err.Error() != "EOF" {
fmt.Println("err now", err)
return err
} else {
break
}
// Messages can't be empty
} else if n == 0 {
break
}
buff = append(buff, b[:n]...)
totalBytes += n
}
// Reslice buffer
buff = buff[:totalBytes]
msg, remaining, done, err := self.readMessage(buff)
for ; done != true; msg, remaining, done, err = self.readMessage(remaining) {
//log.Println("rx", msg)
if msg != nil {
self.pendingMessages = append(self.pendingMessages, msg)
}
}
return
}
func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
if len(data) == 0 {
return nil, nil, true, nil
}
if len(data) <= 8 {
return nil, remaining, false, errors.New("Invalid message")
}
// Check if the received 4 first bytes are the magic token
if bytes.Compare(MagicToken, data[:4]) != 0 {
return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
}
messageLength := ethutil.BytesToNumber(data[4:8])
remaining = data[8+messageLength:]
if int(messageLength) > len(data[8:]) {
return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
}
message := data[8 : 8+messageLength]
decoder := ethutil.NewValueFromBytes(message)
// Type of message
t := decoder.Get(0).Uint()
// Actual data
d := decoder.SliceFrom(1)
msg = &Msg{
Type: MsgType(t),
Data: d,
}
return
}
func bufferedRead(conn net.Conn) ([]byte, error) {
return nil, nil
}
// The basic message reader waits for data on the given connection, decoding // The basic message reader waits for data on the given connection, decoding
// and doing a few sanity checks such as if there's a data type and // and doing a few sanity checks such as if there's a data type and
// unmarhals the given data // unmarhals the given data
@ -282,16 +88,17 @@ func ReadMessages(conn net.Conn) (msgs []*Msg, err error) {
} }
}() }()
// Buff for writing network message to var (
//buff := make([]byte, 1440) buff []byte
var buff []byte messages [][]byte
var totalBytes int msgLength int
)
for { for {
// Give buffering some time // Give buffering some time
conn.SetReadDeadline(time.Now().Add(500 * time.Millisecond)) conn.SetReadDeadline(time.Now().Add(5 * time.Millisecond))
// Create a new temporarily buffer // Create a new temporarily buffer
b := make([]byte, 1440) b := make([]byte, 1440)
// Wait for a message from this peer
n, _ := conn.Read(b) n, _ := conn.Read(b)
if err != nil && n == 0 { if err != nil && n == 0 {
if err.Error() != "EOF" { if err.Error() != "EOF" {
@ -300,25 +107,48 @@ func ReadMessages(conn net.Conn) (msgs []*Msg, err error) {
} else { } else {
break break
} }
}
// Messages can't be empty if n == 0 && len(buff) == 0 {
} else if n == 0 { // If there's nothing on the wire wait for a bit
break time.Sleep(200 * time.Millisecond)
continue
} }
buff = append(buff, b[:n]...) buff = append(buff, b[:n]...)
totalBytes += n if msgLength == 0 {
// Check if the received 4 first bytes are the magic token
if bytes.Compare(MagicToken, buff[:4]) != 0 {
return nil, fmt.Errorf("MagicToken mismatch. Received %v", buff[:4])
} }
// Reslice buffer // Read the length of the message
buff = buff[:totalBytes] msgLength = int(ethutil.BytesToNumber(buff[4:8]))
msg, remaining, done, err := ReadMessage(buff)
for ; done != true; msg, remaining, done, err = ReadMessage(remaining) {
//log.Println("rx", msg)
if msg != nil { // Remove the token and length
msgs = append(msgs, msg) buff = buff[8:]
} }
if len(buff) >= msgLength {
messages = append(messages, buff[:msgLength])
buff = buff[msgLength:]
msgLength = 0
if len(buff) == 0 {
break
}
}
}
for _, m := range messages {
decoder := ethutil.NewValueFromBytes(m)
// Type of message
t := decoder.Get(0).Uint()
// Actual data
d := decoder.SliceFrom(1)
msgs = append(msgs, &Msg{Type: MsgType(t), Data: d})
} }
return return

View File

@ -1,7 +1,6 @@
package eth package eth
import ( import (
//natpmp "code.google.com/p/go-nat-pmp"
"fmt" "fmt"
"net" "net"

282
peer.go
View File

@ -24,7 +24,11 @@ const (
// The size of the output buffer for writing messages // The size of the output buffer for writing messages
outputBufferSize = 50 outputBufferSize = 50
// Current protocol version // Current protocol version
ProtocolVersion = 28 ProtocolVersion = 33
// Current P2P version
P2PVersion = 0
// Ethereum network version
NetVersion = 0
// Interval for ping/pong message // Interval for ping/pong message
pingPongTimer = 2 * time.Second pingPongTimer = 2 * time.Second
) )
@ -70,7 +74,7 @@ func (d DiscReason) String() string {
type Caps byte type Caps byte
const ( const (
CapPeerDiscTy = 1 << iota CapPeerDiscTy Caps = 1 << iota
CapTxTy CapTxTy
CapChainTy CapChainTy
@ -122,6 +126,7 @@ type Peer struct {
// This flag is used by writeMessage to check if messages are allowed // This flag is used by writeMessage to check if messages are allowed
// to be send or not. If no version is known all messages are ignored. // to be send or not. If no version is known all messages are ignored.
versionKnown bool versionKnown bool
statusKnown bool
// Last received pong message // Last received pong message
lastPong int64 lastPong int64
@ -179,6 +184,7 @@ func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
inbound: false, inbound: false,
connected: 0, connected: 0,
disconnect: 0, disconnect: 0,
port: 30303,
caps: caps, caps: caps,
version: ethereum.ClientIdentity().String(), version: ethereum.ClientIdentity().String(),
} }
@ -271,9 +277,19 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) {
default: // Anything but ack is allowed default: // Anything but ack is allowed
return return
} }
} else {
/*
if !p.statusKnown {
switch msg.Type {
case ethwire.MsgStatusTy: // Ok
default: // Anything but ack is allowed
return
}
}
*/
} }
peerlogger.DebugDetailf("(%v) <= %v %v\n", p.conn.RemoteAddr(), msg.Type, msg.Data) peerlogger.DebugDetailf("(%v) <= %v\n", p.conn.RemoteAddr(), formatMessage(msg))
err := ethwire.WriteMessage(p.conn, msg) err := ethwire.WriteMessage(p.conn, msg)
if err != nil { if err != nil {
@ -295,6 +311,14 @@ out:
select { select {
// Main message queue. All outbound messages are processed through here // Main message queue. All outbound messages are processed through here
case msg := <-p.outputQueue: case msg := <-p.outputQueue:
if !p.statusKnown {
switch msg.Type {
case ethwire.MsgGetTxsTy, ethwire.MsgGetBlockHashesTy, ethwire.MsgGetBlocksTy, ethwire.MsgBlockHashesTy, ethwire.MsgBlockTy:
peerlogger.Debugln("Blocked outgoing [eth] message to peer without the [eth] cap.")
break
}
}
p.writeMessage(msg) p.writeMessage(msg)
p.lastSend = time.Now() p.lastSend = time.Now()
@ -337,6 +361,29 @@ clean:
} }
} }
func formatMessage(msg *ethwire.Msg) (ret string) {
ret = fmt.Sprintf("%v %v", msg.Type, msg.Data)
/*
XXX Commented out because I need the log level here to determine
if i should or shouldn't generate this message
*/
/*
switch msg.Type {
case ethwire.MsgPeersTy:
ret += fmt.Sprintf("(%d entries)", msg.Data.Len())
case ethwire.MsgBlockTy:
b1, b2 := ethchain.NewBlockFromRlpValue(msg.Data.Get(0)), ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len()-1))
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), b1.Hash()[0:4], b2.Hash()[0:4])
case ethwire.MsgBlockHashesTy:
h1, h2 := msg.Data.Get(0).Bytes(), msg.Data.Get(msg.Data.Len()-1).Bytes()
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), h1, h2)
}
*/
return
}
// Inbound handler. Inbound messages are received here and passed to the appropriate methods // Inbound handler. Inbound messages are received here and passed to the appropriate methods
func (p *Peer) HandleInbound() { func (p *Peer) HandleInbound() {
for atomic.LoadInt32(&p.disconnect) == 0 { for atomic.LoadInt32(&p.disconnect) == 0 {
@ -349,16 +396,16 @@ func (p *Peer) HandleInbound() {
peerlogger.Debugln(err) peerlogger.Debugln(err)
} }
for _, msg := range msgs { for _, msg := range msgs {
peerlogger.DebugDetailf("(%v) => %v %v\n", p.conn.RemoteAddr(), msg.Type, msg.Data) peerlogger.DebugDetailf("(%v) => %v\n", p.conn.RemoteAddr(), formatMessage(msg))
switch msg.Type { switch msg.Type {
case ethwire.MsgHandshakeTy: case ethwire.MsgHandshakeTy:
// Version message // Version message
p.handleHandshake(msg) p.handleHandshake(msg)
if p.caps.IsCap(CapPeerDiscTy) { //if p.caps.IsCap(CapPeerDiscTy) {
p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, "")) p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, ""))
} //}
case ethwire.MsgDiscTy: case ethwire.MsgDiscTy:
p.Stop() p.Stop()
@ -396,6 +443,15 @@ func (p *Peer) HandleInbound() {
// Connect to the list of peers // Connect to the list of peers
p.ethereum.ProcessPeerList(peers) p.ethereum.ProcessPeerList(peers)
case ethwire.MsgStatusTy:
// Handle peer's status msg
p.handleStatus(msg)
}
// TMP
if p.statusKnown {
switch msg.Type {
case ethwire.MsgGetTxsTy: case ethwire.MsgGetTxsTy:
// Get the current transactions of the pool // Get the current transactions of the pool
txs := p.ethereum.TxPool().CurrentTransactions() txs := p.ethereum.TxPool().CurrentTransactions()
@ -458,7 +514,7 @@ func (p *Peer) HandleInbound() {
p.lastBlockReceived = time.Now() p.lastBlockReceived = time.Now()
} }
if foundCommonHash { if foundCommonHash || msg.Data.Len() == 0 {
p.FetchBlocks() p.FetchBlocks()
} else { } else {
p.FetchHashes() p.FetchHashes()
@ -470,25 +526,32 @@ func (p *Peer) HandleInbound() {
blockPool := p.ethereum.blockPool blockPool := p.ethereum.blockPool
it := msg.Data.NewIterator() it := msg.Data.NewIterator()
for it.Next() { for it.Next() {
block := ethchain.NewBlockFromRlpValue(it.Value()) block := ethchain.NewBlockFromRlpValue(it.Value())
//fmt.Printf("%v %x - %x\n", block.Number, block.Hash()[0:4], block.PrevHash[0:4])
blockPool.SetBlock(block) blockPool.SetBlock(block, p)
p.lastBlockReceived = time.Now() p.lastBlockReceived = time.Now()
} }
linked := blockPool.CheckLinkAndProcess(func(block *ethchain.Block) { var err error
p.ethereum.StateManager().Process(block, false) blockPool.CheckLinkAndProcess(func(block *ethchain.Block) {
err = p.ethereum.StateManager().Process(block, false)
}) })
if !linked { if err != nil {
peerlogger.Infoln(err)
} else {
// Don't trigger if there's just one block.
if blockPool.Len() != 0 && msg.Data.Len() > 1 {
p.FetchBlocks() p.FetchBlocks()
} }
} }
} }
} }
}
}
p.Stop() p.Stop()
} }
@ -506,10 +569,10 @@ func (self *Peer) FetchHashes() {
blockPool := self.ethereum.blockPool blockPool := self.ethereum.blockPool
if self.td.Cmp(blockPool.td) >= 0 { if self.td.Cmp(blockPool.td) >= 0 {
peerlogger.Debugf("Requesting hashes from %x\n", self.lastReceivedHash) blockPool.td = self.td
if !blockPool.HasLatestHash() { if !blockPool.HasLatestHash() {
self.QueueMessage(ethwire.NewMessage(ethwire.MsgGetBlockHashesTy, []interface{}{self.lastReceivedHash, uint32(200)})) self.QueueMessage(ethwire.NewMessage(ethwire.MsgGetBlockHashesTy, []interface{}{self.lastReceivedHash, uint32(256)}))
} }
} }
} }
@ -580,18 +643,6 @@ func (p *Peer) Stop() {
p.ethereum.RemovePeer(p) p.ethereum.RemovePeer(p)
} }
func (p *Peer) pushHandshake() error {
pubkey := p.ethereum.KeyManager().PublicKey()
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
uint32(ProtocolVersion), uint32(0), []byte(p.version), byte(p.caps), p.port, pubkey[1:],
p.ethereum.BlockChain().TD.Uint64(), p.ethereum.BlockChain().CurrentBlock.Hash(),
})
p.QueueMessage(msg)
return nil
}
func (p *Peer) peersMessage() *ethwire.Msg { func (p *Peer) peersMessage() *ethwire.Msg {
outPeers := make([]interface{}, len(p.ethereum.InOutPeers())) outPeers := make([]interface{}, len(p.ethereum.InOutPeers()))
// Serialise each peer // Serialise each peer
@ -611,13 +662,93 @@ func (p *Peer) pushPeers() {
p.QueueMessage(p.peersMessage()) p.QueueMessage(p.peersMessage())
} }
func (self *Peer) pushStatus() {
msg := ethwire.NewMessage(ethwire.MsgStatusTy, []interface{}{
uint32(ProtocolVersion),
uint32(NetVersion),
self.ethereum.BlockChain().TD,
self.ethereum.BlockChain().CurrentBlock.Hash(),
self.ethereum.BlockChain().Genesis().Hash(),
})
self.QueueMessage(msg)
}
func (self *Peer) handleStatus(msg *ethwire.Msg) {
c := msg.Data
var (
protoVersion = c.Get(0).Uint()
netVersion = c.Get(1).Uint()
td = c.Get(2).BigInt()
bestHash = c.Get(3).Bytes()
genesis = c.Get(4).Bytes()
)
if bytes.Compare(self.ethereum.BlockChain().Genesis().Hash(), genesis) != 0 {
ethlogger.Warnf("Invalid genisis hash %x. Disabling [eth]\n", genesis)
return
}
if netVersion != NetVersion {
ethlogger.Warnf("Invalid network version %d. Disabling [eth]\n", netVersion)
return
}
if protoVersion != ProtocolVersion {
ethlogger.Warnf("Invalid protocol version %d. Disabling [eth]\n", protoVersion)
return
}
// Get the td and last hash
self.td = td
self.bestHash = bestHash
self.lastReceivedHash = bestHash
self.statusKnown = true
// Compare the total TD with the blockchain TD. If remote is higher
// fetch hashes from highest TD node.
if self.td.Cmp(self.ethereum.BlockChain().TD) > 0 {
self.ethereum.blockPool.AddHash(self.lastReceivedHash)
self.FetchHashes()
}
ethlogger.Infof("Peer is [eth] capable. (TD = %v ~ %x) %d / %d", self.td, self.bestHash, protoVersion, netVersion)
}
func (p *Peer) pushHandshake() error {
pubkey := p.ethereum.KeyManager().PublicKey()
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
P2PVersion, []byte(p.version), []interface{}{"eth"}, p.port, pubkey[1:],
})
p.QueueMessage(msg)
return nil
}
func (p *Peer) handleHandshake(msg *ethwire.Msg) { func (p *Peer) handleHandshake(msg *ethwire.Msg) {
c := msg.Data c := msg.Data
// Set pubkey var (
p.pubkey = c.Get(5).Bytes() p2pVersion = c.Get(0).Uint()
clientId = c.Get(1).Str()
caps = c.Get(2)
port = c.Get(3).Uint()
pub = c.Get(4).Bytes()
)
if p.pubkey == nil { // Check correctness of p2p protocol version
if p2pVersion != P2PVersion {
peerlogger.Debugf("Invalid P2P version. Require protocol %d, received %d\n", P2PVersion, p2pVersion)
p.Stop()
return
}
// Handle the pub key (validation, uniqueness)
if len(pub) == 0 {
peerlogger.Warnln("Pubkey required, not supplied in handshake.") peerlogger.Warnln("Pubkey required, not supplied in handshake.")
p.Stop() p.Stop()
return return
@ -625,9 +756,8 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
usedPub := 0 usedPub := 0
// This peer is already added to the peerlist so we expect to find a double pubkey at least once // This peer is already added to the peerlist so we expect to find a double pubkey at least once
eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) { eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) {
if bytes.Compare(p.pubkey, peer.pubkey) == 0 { if bytes.Compare(pub, peer.pubkey) == 0 {
usedPub++ usedPub++
} }
}) })
@ -637,19 +767,11 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
p.Stop() p.Stop()
return return
} }
p.pubkey = pub
if c.Get(0).Uint() != ProtocolVersion {
peerlogger.Debugf("Invalid peer version. Require protocol: %d. Received: %d\n", ProtocolVersion, c.Get(0).Uint())
p.Stop()
return
}
// [PROTOCOL_VERSION, NETWORK_ID, CLIENT_ID, CAPS, PORT, PUBKEY]
p.versionKnown = true
// If this is an inbound connection send an ack back // If this is an inbound connection send an ack back
if p.inbound { if p.inbound {
p.port = uint16(c.Get(4).Uint()) p.port = uint16(port)
// Self connect detection // Self connect detection
pubkey := p.ethereum.KeyManager().PublicKey() pubkey := p.ethereum.KeyManager().PublicKey()
@ -660,40 +782,27 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) {
} }
} }
p.SetVersion(clientId)
// Set the peer's caps p.versionKnown = true
p.caps = Caps(c.Get(3).Byte())
// Get a reference to the peers version
versionString := c.Get(2).Str()
if len(versionString) > 0 {
p.SetVersion(c.Get(2).Str())
}
// Get the td and last hash
p.td = c.Get(6).BigInt()
p.bestHash = c.Get(7).Bytes()
p.lastReceivedHash = p.bestHash
p.ethereum.PushPeer(p) p.ethereum.PushPeer(p)
p.ethereum.reactor.Post("peerList", p.ethereum.Peers()) p.ethereum.reactor.Post("peerList", p.ethereum.Peers())
ethlogger.Infof("Added peer (%s) %d / %d (TD = %v ~ %x)\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers, p.td, p.bestHash) capsIt := caps.NewIterator()
var capsStrs []string
/* for capsIt.Next() {
// Catch up with the connected peer cap := capsIt.Value().Str()
if !p.ethereum.IsUpToDate() { switch cap {
peerlogger.Debugln("Already syncing up with a peer; sleeping") case "eth":
time.Sleep(10 * time.Second) p.pushStatus()
} }
*/
//p.SyncWithPeerToLastKnown()
if p.td.Cmp(p.ethereum.BlockChain().TD) > 0 { capsStrs = append(capsStrs, cap)
p.ethereum.blockPool.AddHash(p.lastReceivedHash)
p.FetchHashes()
} }
ethlogger.Infof("Added peer (%s) %d / %d (%v)\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers, capsStrs)
peerlogger.Debugln(p) peerlogger.Debugln(p)
} }
@ -714,47 +823,6 @@ func (p *Peer) String() string {
return fmt.Sprintf("[%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)
} }
func (p *Peer) SyncWithPeerToLastKnown() {
p.catchingUp = false
p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash())
}
func (p *Peer) FindCommonParentBlock() {
if p.catchingUp {
return
}
p.catchingUp = true
if p.blocksRequested == 0 {
p.blocksRequested = 20
}
blocks := p.ethereum.BlockChain().GetChain(p.ethereum.BlockChain().CurrentBlock.Hash(), p.blocksRequested)
var hashes []interface{}
for _, block := range blocks {
hashes = append(hashes, block.Hash())
}
msgInfo := append(hashes, uint64(len(hashes)))
peerlogger.DebugDetailf("Asking for block from %x (%d total) from %s\n", p.ethereum.BlockChain().CurrentBlock.Hash(), len(hashes), p.conn.RemoteAddr().String())
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, msgInfo)
p.QueueMessage(msg)
}
func (p *Peer) CatchupWithPeer(blockHash []byte) {
if !p.catchingUp {
// Make sure nobody else is catching up when you want to do this
p.catchingUp = true
msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(100)})
p.QueueMessage(msg)
peerlogger.DebugDetailf("Requesting blockchain %x... from peer %s\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4], p.conn.RemoteAddr())
msg = ethwire.NewMessage(ethwire.MsgGetTxsTy, []interface{}{})
p.QueueMessage(msg)
}
}
func (p *Peer) RlpData() []interface{} { func (p *Peer) RlpData() []interface{} {
return []interface{}{p.host, p.port, p.pubkey} return []interface{}{p.host, p.port, p.pubkey}