Merge branch 'release/0.6.5'
This commit is contained in:
commit
ce149d2733
@ -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.
|
||||||
|
@ -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)
|
||||||
f(block)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delete(self.pool, string(hash))
|
ethchain.BlockBy(ethchain.Number).Sort(blocks)
|
||||||
} else {
|
for _, block := range blocks {
|
||||||
self.hashPool = self.hashPool[i:]
|
if self.eth.BlockChain().HasBlock(block.PrevHash) {
|
||||||
|
f(block)
|
||||||
|
|
||||||
return false
|
hash := block.Hash()
|
||||||
}
|
self.hashPool = ethutil.DeleteFromByteSlice(self.hashPool, hash)
|
||||||
|
delete(self.pool, string(hash))
|
||||||
}
|
}
|
||||||
|
|
||||||
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])
|
||||||
|
@ -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()))
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
|
||||||
|
@ -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
|
||||||
|
@ -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 {
|
/*
|
||||||
if bytes.Compare(uncle.PrevHash, previousBlock.PrevHash) != 0 {
|
for _, uncle := range block.Uncles {
|
||||||
return ValidationError("Mismatch uncle's previous hash. Expected %x, got %x", previousBlock.PrevHash, uncle.PrevHash)
|
if bytes.Compare(uncle.PrevHash,parent.PrevHash) != 0 {
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
uncleParent := sm.bc.GetBlock(uncle.PrevHash)
|
||||||
|
if uncleParent == nil {
|
||||||
|
return UncleError("Uncle's parent unknown")
|
||||||
|
}
|
||||||
|
|
||||||
|
if uncleParent.Number.Cmp(new(big.Int).Sub(parent.Number, big.NewInt(6))) < 0 {
|
||||||
|
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)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.Add(base, BlockReward)
|
|
||||||
}
|
|
||||||
|
|
||||||
func CalculateUncleReward(block *Block) *big.Int {
|
|
||||||
return UncleReward
|
|
||||||
}
|
|
||||||
|
|
||||||
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, ðstate.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
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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)
|
||||||
|
@ -27,10 +27,12 @@ const (
|
|||||||
NOT = 0x0f
|
NOT = 0x0f
|
||||||
|
|
||||||
// 0x10 range - bit ops
|
// 0x10 range - bit ops
|
||||||
AND = 0x10
|
AND = 0x10
|
||||||
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
|
||||||
@ -57,9 +61,9 @@ const (
|
|||||||
GASLIMIT = 0x45
|
GASLIMIT = 0x45
|
||||||
|
|
||||||
// 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
|
||||||
@ -136,10 +176,12 @@ var opCodeToString = map[OpCode]string{
|
|||||||
NOT: "NOT",
|
NOT: "NOT",
|
||||||
|
|
||||||
// 0x10 range - bit ops
|
// 0x10 range - bit ops
|
||||||
AND: "AND",
|
AND: "AND",
|
||||||
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",
|
||||||
@ -158,17 +200,19 @@ var opCodeToString = map[OpCode]string{
|
|||||||
GASPRICE: "TXGASPRICE",
|
GASPRICE: "TXGASPRICE",
|
||||||
|
|
||||||
// 0x40 range - block operations
|
// 0x40 range - block operations
|
||||||
PREVHASH: "PREVHASH",
|
PREVHASH: "PREVHASH",
|
||||||
COINBASE: "COINBASE",
|
COINBASE: "COINBASE",
|
||||||
TIMESTAMP: "TIMESTAMP",
|
TIMESTAMP: "TIMESTAMP",
|
||||||
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
17
ethcrypto/crypto_test.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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()
|
||||||
|
175
ethereum.go
175
ethereum.go
@ -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,47 +399,55 @@ func (s *Ethereum) Start(seed bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Ethereum) Seed() {
|
func (s *Ethereum) Seed() {
|
||||||
ethlogger.Debugln("Retrieving seed nodes")
|
ips := PastPeers()
|
||||||
|
if len(ips) > 0 {
|
||||||
// Eth-Go Bootstrapping
|
|
||||||
ips, er := net.LookupIP("seed.bysh.me")
|
|
||||||
if er == nil {
|
|
||||||
peers := []string{}
|
|
||||||
for _, ip := range ips {
|
for _, ip := range ips {
|
||||||
node := fmt.Sprintf("%s:%d", ip.String(), 30303)
|
ethlogger.Infoln("Connecting to previous peer ", ip)
|
||||||
ethlogger.Debugln("Found DNS Go Peer:", node)
|
s.ConnectToPeer(ip)
|
||||||
peers = append(peers, node)
|
|
||||||
}
|
}
|
||||||
s.ProcessPeerList(peers)
|
} else {
|
||||||
}
|
ethlogger.Debugln("Retrieving seed nodes")
|
||||||
|
|
||||||
// Official DNS Bootstrapping
|
// Eth-Go Bootstrapping
|
||||||
_, nodes, err := net.LookupSRV("eth", "tcp", "ethereum.org")
|
ips, er := net.LookupIP("seed.bysh.me")
|
||||||
if err == nil {
|
if er == nil {
|
||||||
peers := []string{}
|
peers := []string{}
|
||||||
// Iterate SRV nodes
|
for _, ip := range ips {
|
||||||
for _, n := range nodes {
|
node := fmt.Sprintf("%s:%d", ip.String(), 30303)
|
||||||
target := n.Target
|
ethlogger.Debugln("Found DNS Go Peer:", node)
|
||||||
port := strconv.Itoa(int(n.Port))
|
peers = append(peers, node)
|
||||||
// Resolve target to ip (Go returns list, so may resolve to multiple ips?)
|
|
||||||
addr, err := net.LookupHost(target)
|
|
||||||
if err == nil {
|
|
||||||
for _, a := range addr {
|
|
||||||
// Build string out of SRV port and Resolved IP
|
|
||||||
peer := net.JoinHostPort(a, port)
|
|
||||||
ethlogger.Debugln("Found DNS Bootstrap Peer:", peer)
|
|
||||||
peers = append(peers, peer)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ethlogger.Debugln("Couldn't resolve :", target)
|
|
||||||
}
|
}
|
||||||
|
s.ProcessPeerList(peers)
|
||||||
}
|
}
|
||||||
// Connect to Peer list
|
|
||||||
s.ProcessPeerList(peers)
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX tmp
|
// Official DNS Bootstrapping
|
||||||
s.ConnectToPeer(seedNodeAddress)
|
_, nodes, err := net.LookupSRV("eth", "tcp", "ethereum.org")
|
||||||
|
if err == nil {
|
||||||
|
peers := []string{}
|
||||||
|
// Iterate SRV nodes
|
||||||
|
for _, n := range nodes {
|
||||||
|
target := n.Target
|
||||||
|
port := strconv.Itoa(int(n.Port))
|
||||||
|
// Resolve target to ip (Go returns list, so may resolve to multiple ips?)
|
||||||
|
addr, err := net.LookupHost(target)
|
||||||
|
if err == nil {
|
||||||
|
for _, a := range addr {
|
||||||
|
// Build string out of SRV port and Resolved IP
|
||||||
|
peer := net.JoinHostPort(a, port)
|
||||||
|
ethlogger.Debugln("Found DNS Bootstrap Peer:", peer)
|
||||||
|
peers = append(peers, peer)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ethlogger.Debugln("Couldn't resolve :", target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Connect to Peer list
|
||||||
|
s.ProcessPeerList(peers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX tmp
|
||||||
|
s.ConnectToPeer(seedNodeAddress)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Ethereum) peerHandler(listener net.Listener) {
|
func (s *Ethereum) peerHandler(listener net.Listener) {
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package ethpipe
|
package ethpipe
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -13,15 +12,17 @@ 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
|
||||||
Number int `json:"number"`
|
Size string `json:"size"`
|
||||||
Hash string `json:"hash"`
|
Number int `json:"number"`
|
||||||
Transactions string `json:"transactions"`
|
Hash string `json:"hash"`
|
||||||
Time int64 `json:"time"`
|
Transactions *ethutil.List `json:"transactions"`
|
||||||
Coinbase string `json:"coinbase"`
|
Time int64 `json:"time"`
|
||||||
Name string `json:"name"`
|
Coinbase string `json:"coinbase"`
|
||||||
GasLimit string `json:"gasLimit"`
|
Name string `json:"name"`
|
||||||
GasUsed string `json:"gasUsed"`
|
GasLimit string `json:"gasLimit"`
|
||||||
|
GasUsed string `json:"gasUsed"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new QML Block from a chain block
|
// Creates a new QML Block from a chain block
|
||||||
@ -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)
|
/*
|
||||||
if err != nil {
|
txJson, err := json.Marshal(ptxs)
|
||||||
return nil
|
if err != 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 {
|
||||||
|
@ -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(ðstate.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 {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
61
ethutil/list.go
Normal 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)
|
||||||
|
}
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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,15 +16,13 @@ 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 {
|
return nil, err
|
||||||
return nil, err
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return byteCode, nil
|
return byteCode, nil
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
36
ethutil/set.go
Normal file
36
ethutil/set.go
Normal 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
15
ethutil/size.go
Normal 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
12
ethutil/size_test.go
Normal 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))
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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())
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
@ -142,9 +144,11 @@ const (
|
|||||||
SWAP16 = 0x9f
|
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
|
||||||
@ -196,12 +200,14 @@ var opCodeToString = map[OpCode]string{
|
|||||||
GASPRICE: "TXGASPRICE",
|
GASPRICE: "TXGASPRICE",
|
||||||
|
|
||||||
// 0x40 range - block operations
|
// 0x40 range - block operations
|
||||||
PREVHASH: "PREVHASH",
|
PREVHASH: "PREVHASH",
|
||||||
COINBASE: "COINBASE",
|
COINBASE: "COINBASE",
|
||||||
TIMESTAMP: "TIMESTAMP",
|
TIMESTAMP: "TIMESTAMP",
|
||||||
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",
|
||||||
@ -287,9 +293,11 @@ var opCodeToString = map[OpCode]string{
|
|||||||
SWAP16: "SWAP16",
|
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",
|
||||||
@ -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,
|
||||||
@ -435,9 +448,11 @@ var OpCodes = map[string]byte{
|
|||||||
"SWAP16": 0x9f,
|
"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,
|
"LOG": 0xfe,
|
||||||
|
347
ethvm/vm.go
347
ethvm/vm.go
@ -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,15 +132,13 @@ 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) {
|
value.Decode()
|
||||||
value.Decode()
|
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(ðstate.Message{
|
closure.UseGas(closure.Gas)
|
||||||
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)
|
|
||||||
|
|
||||||
// Create the closure
|
|
||||||
c := NewClosure(msg, closure, contract, initCode, gas, closure.Price)
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
msg := NewMessage(self, addr, input, gas, closure.Price, value)
|
||||||
|
ret, err := msg.Exec(addr, closure)
|
||||||
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,51 +809,48 @@ 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(ðstate.Message{
|
snapshot := self.env.State().Copy()
|
||||||
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 {
|
var executeAddr []byte
|
||||||
vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance)
|
if op == CALLSTATELESS {
|
||||||
|
executeAddr = closure.Address()
|
||||||
closure.ReturnGas(gas, nil)
|
|
||||||
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
} else {
|
} else {
|
||||||
snapshot := self.env.State().Copy()
|
executeAddr = addr.Bytes()
|
||||||
|
|
||||||
stateObject := self.env.State().GetOrNewStateObject(addr.Bytes())
|
|
||||||
|
|
||||||
closure.object.SubAmount(value)
|
|
||||||
stateObject.AddAmount(value)
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
stack.Push(ethutil.BigFalse)
|
|
||||||
|
|
||||||
vmlogger.Debugf("Closure execution failed. %v\n", err)
|
|
||||||
|
|
||||||
self.env.State().Set(snapshot)
|
|
||||||
} else {
|
|
||||||
stack.Push(ethutil.BigTrue)
|
|
||||||
|
|
||||||
mem.Set(retOffset.Int64(), retSize.Int64(), ret)
|
|
||||||
}
|
|
||||||
|
|
||||||
msg.Output = ret
|
|
||||||
|
|
||||||
// Debug hook
|
|
||||||
if self.Dbg != nil {
|
|
||||||
self.Dbg.SetCode(closure.Code)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg := NewMessage(self, executeAddr, args, gas, closure.Price, value)
|
||||||
|
ret, err := msg.Exec(addr.Bytes(), closure)
|
||||||
|
if err != nil {
|
||||||
|
stack.Push(ethutil.BigFalse)
|
||||||
|
|
||||||
|
self.env.State().Set(snapshot)
|
||||||
|
} else {
|
||||||
|
stack.Push(ethutil.BigTrue)
|
||||||
|
|
||||||
|
mem.Set(retOffset.Int64(), retSize.Int64(), ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug hook
|
||||||
|
if self.Dbg != nil {
|
||||||
|
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(ðstate.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
|
||||||
|
}
|
||||||
|
@ -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
199
ethwire/messages2.go
Normal 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
|
||||||
|
}
|
@ -4,7 +4,6 @@ package ethwire
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
@ -27,24 +26,20 @@ const (
|
|||||||
// Values are given explicitly instead of by iota because these values are
|
// Values are given explicitly instead of by iota because these values are
|
||||||
// defined by the wire protocol spec; it is easier for humans to ensure
|
// defined by the wire protocol spec; it is easier for humans to ensure
|
||||||
// correctness when values are explicit.
|
// correctness when values are explicit.
|
||||||
MsgHandshakeTy = 0x00
|
MsgHandshakeTy = 0x00
|
||||||
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])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the length of the message
|
||||||
|
msgLength = int(ethutil.BytesToNumber(buff[4:8]))
|
||||||
|
|
||||||
|
// Remove the token and length
|
||||||
|
buff = buff[8:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buff) >= msgLength {
|
||||||
|
messages = append(messages, buff[:msgLength])
|
||||||
|
buff = buff[msgLength:]
|
||||||
|
msgLength = 0
|
||||||
|
|
||||||
|
if len(buff) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reslice buffer
|
for _, m := range messages {
|
||||||
buff = buff[:totalBytes]
|
decoder := ethutil.NewValueFromBytes(m)
|
||||||
msg, remaining, done, err := ReadMessage(buff)
|
// Type of message
|
||||||
for ; done != true; msg, remaining, done, err = ReadMessage(remaining) {
|
t := decoder.Get(0).Uint()
|
||||||
//log.Println("rx", msg)
|
// Actual data
|
||||||
|
d := decoder.SliceFrom(1)
|
||||||
|
|
||||||
if msg != nil {
|
msgs = append(msgs, &Msg{Type: MsgType(t), Data: d})
|
||||||
msgs = append(msgs, msg)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package eth
|
package eth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
//natpmp "code.google.com/p/go-nat-pmp"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
416
peer.go
416
peer.go
@ -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,95 +443,111 @@ 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.MsgGetTxsTy:
|
|
||||||
// Get the current transactions of the pool
|
|
||||||
txs := p.ethereum.TxPool().CurrentTransactions()
|
|
||||||
// Get the RlpData values from the txs
|
|
||||||
txsInterface := make([]interface{}, len(txs))
|
|
||||||
for i, tx := range txs {
|
|
||||||
txsInterface[i] = tx.RlpData()
|
|
||||||
}
|
|
||||||
// Broadcast it back to the peer
|
|
||||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgTxTy, txsInterface))
|
|
||||||
|
|
||||||
case ethwire.MsgGetBlockHashesTy:
|
case ethwire.MsgStatusTy:
|
||||||
if msg.Data.Len() < 2 {
|
// Handle peer's status msg
|
||||||
peerlogger.Debugln("err: argument length invalid ", msg.Data.Len())
|
p.handleStatus(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := msg.Data.Get(0).Bytes()
|
// TMP
|
||||||
amount := msg.Data.Get(1).Uint()
|
if p.statusKnown {
|
||||||
|
switch msg.Type {
|
||||||
hashes := p.ethereum.BlockChain().GetChainHashesFromHash(hash, amount)
|
case ethwire.MsgGetTxsTy:
|
||||||
|
// Get the current transactions of the pool
|
||||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockHashesTy, ethutil.ByteSliceToInterface(hashes)))
|
txs := p.ethereum.TxPool().CurrentTransactions()
|
||||||
|
// Get the RlpData values from the txs
|
||||||
case ethwire.MsgGetBlocksTy:
|
txsInterface := make([]interface{}, len(txs))
|
||||||
// Limit to max 300 blocks
|
for i, tx := range txs {
|
||||||
max := int(math.Min(float64(msg.Data.Len()), 300.0))
|
txsInterface[i] = tx.RlpData()
|
||||||
var blocks []interface{}
|
|
||||||
|
|
||||||
for i := 0; i < max; i++ {
|
|
||||||
hash := msg.Data.Get(i).Bytes()
|
|
||||||
block := p.ethereum.BlockChain().GetBlock(hash)
|
|
||||||
if block != nil {
|
|
||||||
blocks = append(blocks, block.Value().Raw())
|
|
||||||
}
|
}
|
||||||
}
|
// Broadcast it back to the peer
|
||||||
|
p.QueueMessage(ethwire.NewMessage(ethwire.MsgTxTy, txsInterface))
|
||||||
|
|
||||||
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, blocks))
|
case ethwire.MsgGetBlockHashesTy:
|
||||||
|
if msg.Data.Len() < 2 {
|
||||||
case ethwire.MsgBlockHashesTy:
|
peerlogger.Debugln("err: argument length invalid ", msg.Data.Len())
|
||||||
p.catchingUp = true
|
|
||||||
|
|
||||||
blockPool := p.ethereum.blockPool
|
|
||||||
|
|
||||||
foundCommonHash := false
|
|
||||||
|
|
||||||
it := msg.Data.NewIterator()
|
|
||||||
for it.Next() {
|
|
||||||
hash := it.Value().Bytes()
|
|
||||||
|
|
||||||
if blockPool.HasCommonHash(hash) {
|
|
||||||
foundCommonHash = true
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
blockPool.AddHash(hash)
|
hash := msg.Data.Get(0).Bytes()
|
||||||
|
amount := msg.Data.Get(1).Uint()
|
||||||
|
|
||||||
p.lastReceivedHash = hash
|
hashes := p.ethereum.BlockChain().GetChainHashesFromHash(hash, amount)
|
||||||
|
|
||||||
p.lastBlockReceived = time.Now()
|
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockHashesTy, ethutil.ByteSliceToInterface(hashes)))
|
||||||
}
|
|
||||||
|
|
||||||
if foundCommonHash {
|
case ethwire.MsgGetBlocksTy:
|
||||||
p.FetchBlocks()
|
// Limit to max 300 blocks
|
||||||
} else {
|
max := int(math.Min(float64(msg.Data.Len()), 300.0))
|
||||||
p.FetchHashes()
|
var blocks []interface{}
|
||||||
}
|
|
||||||
|
|
||||||
case ethwire.MsgBlockTy:
|
for i := 0; i < max; i++ {
|
||||||
p.catchingUp = true
|
hash := msg.Data.Get(i).Bytes()
|
||||||
|
block := p.ethereum.BlockChain().GetBlock(hash)
|
||||||
|
if block != nil {
|
||||||
|
blocks = append(blocks, block.Value().Raw())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
blockPool := p.ethereum.blockPool
|
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, blocks))
|
||||||
|
|
||||||
it := msg.Data.NewIterator()
|
case ethwire.MsgBlockHashesTy:
|
||||||
|
p.catchingUp = true
|
||||||
|
|
||||||
for it.Next() {
|
blockPool := p.ethereum.blockPool
|
||||||
block := ethchain.NewBlockFromRlpValue(it.Value())
|
|
||||||
|
|
||||||
blockPool.SetBlock(block)
|
foundCommonHash := false
|
||||||
|
|
||||||
p.lastBlockReceived = time.Now()
|
it := msg.Data.NewIterator()
|
||||||
}
|
for it.Next() {
|
||||||
|
hash := it.Value().Bytes()
|
||||||
|
|
||||||
linked := blockPool.CheckLinkAndProcess(func(block *ethchain.Block) {
|
if blockPool.HasCommonHash(hash) {
|
||||||
p.ethereum.StateManager().Process(block, false)
|
foundCommonHash = true
|
||||||
})
|
|
||||||
|
|
||||||
if !linked {
|
break
|
||||||
p.FetchBlocks()
|
}
|
||||||
|
|
||||||
|
blockPool.AddHash(hash)
|
||||||
|
|
||||||
|
p.lastReceivedHash = hash
|
||||||
|
|
||||||
|
p.lastBlockReceived = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundCommonHash || msg.Data.Len() == 0 {
|
||||||
|
p.FetchBlocks()
|
||||||
|
} else {
|
||||||
|
p.FetchHashes()
|
||||||
|
}
|
||||||
|
|
||||||
|
case ethwire.MsgBlockTy:
|
||||||
|
p.catchingUp = true
|
||||||
|
|
||||||
|
blockPool := p.ethereum.blockPool
|
||||||
|
|
||||||
|
it := msg.Data.NewIterator()
|
||||||
|
for it.Next() {
|
||||||
|
block := ethchain.NewBlockFromRlpValue(it.Value())
|
||||||
|
//fmt.Printf("%v %x - %x\n", block.Number, block.Hash()[0:4], block.PrevHash[0:4])
|
||||||
|
|
||||||
|
blockPool.SetBlock(block, p)
|
||||||
|
|
||||||
|
p.lastBlockReceived = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
blockPool.CheckLinkAndProcess(func(block *ethchain.Block) {
|
||||||
|
err = p.ethereum.StateManager().Process(block, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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}
|
||||||
|
Loading…
Reference in New Issue
Block a user