diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | block_pool.go | 24 | ||||
-rw-r--r-- | ethchain/block.go | 7 | ||||
-rw-r--r-- | ethchain/block_chain.go | 157 | ||||
-rw-r--r-- | ethchain/state_manager.go | 2 | ||||
-rw-r--r-- | ethchain/state_transition.go | 11 | ||||
-rw-r--r-- | ethchain/transaction.go | 5 | ||||
-rw-r--r-- | ethchain/transaction_pool.go | 2 | ||||
-rw-r--r-- | ethcrypto/crypto_test.go | 17 | ||||
-rw-r--r-- | ethdb/database.go | 13 | ||||
-rw-r--r-- | ethereum.go | 12 | ||||
-rw-r--r-- | ethpipe/js_pipe.go | 3 | ||||
-rw-r--r-- | ethpipe/js_types.go | 33 | ||||
-rw-r--r-- | ethreact/reactor.go | 5 | ||||
-rw-r--r-- | ethstate/state.go | 11 | ||||
-rw-r--r-- | ethstate/state_object.go | 12 | ||||
-rw-r--r-- | ethutil/list.go | 42 | ||||
-rw-r--r-- | ethutil/script.go | 18 | ||||
-rw-r--r-- | ethutil/size.go | 15 | ||||
-rw-r--r-- | ethutil/size_test.go | 12 | ||||
-rw-r--r-- | ethutil/value.go | 12 | ||||
-rw-r--r-- | ethutil/value_test.go | 6 | ||||
-rw-r--r-- | ethvm/closure.go | 1 | ||||
-rw-r--r-- | ethvm/types.go | 47 | ||||
-rw-r--r-- | ethvm/vm.go | 230 | ||||
-rw-r--r-- | ethwire/messaging.go | 33 | ||||
-rw-r--r-- | natpmp.go | 1 | ||||
-rw-r--r-- | peer.go | 180 |
28 files changed, 494 insertions, 419 deletions
@@ -6,7 +6,7 @@ Ethereum Ethereum Go Development package (C) Jeffrey Wilcke 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 individual package for more information. diff --git a/block_pool.go b/block_pool.go index 3225bdff2..25627eb5c 100644 --- a/block_pool.go +++ b/block_pool.go @@ -49,11 +49,11 @@ 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()) - if self.pool[string(hash)] == nil { - self.pool[hash] = &block{nil, nil} + if self.pool[hash] == nil { + self.pool[hash] = &block{peer, nil} } self.pool[hash].block = b @@ -65,6 +65,10 @@ func (self *BlockPool) CheckLinkAndProcess(f func(block *ethchain.Block)) bool { if self.IsLinked() { for i, hash := range self.hashPool { + if self.pool[string(hash)] == nil { + continue + } + block := self.pool[string(hash)].block if block != nil { f(block) @@ -88,9 +92,15 @@ func (self *BlockPool) IsLinked() bool { return false } - block := self.pool[string(self.hashPool[0])].block - if block != nil { - return self.eth.BlockChain().HasBlock(block.PrevHash) + for i := 0; i < len(self.hashPool); i++ { + item := self.pool[string(self.hashPool[i])] + if item != nil && item.block != nil { + if self.eth.BlockChain().HasBlock(item.block.PrevHash) { + self.hashPool = self.hashPool[i:] + + return true + } + } } return false @@ -104,7 +114,7 @@ func (self *BlockPool) Take(amount int, peer *Peer) (hashes [][]byte) { j := 0 for i := 0; i < len(self.hashPool) && j < num; 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 hashes = append(hashes, self.hashPool[i]) diff --git a/ethchain/block.go b/ethchain/block.go index 5765abd51..d2d012e55 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -351,7 +351,7 @@ func (block *Block) header() []interface{} { func (block *Block) String() string { return fmt.Sprintf(` - BLOCK(%x): + BLOCK(%x): Size: %v PrevHash: %x UncleSha: %x Coinbase: %x @@ -368,6 +368,7 @@ func (block *Block) String() string { NumTx: %v `, block.Hash(), + block.Size(), block.PrevHash, block.UncleSha, block.Coinbase, @@ -384,3 +385,7 @@ func (block *Block) String() string { len(block.transactions), ) } + +func (self *Block) Size() ethutil.StorageSize { + return ethutil.StorageSize(len(self.RlpEncode())) +} diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 3445bbb87..74f47aa90 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -2,12 +2,10 @@ package ethchain import ( "bytes" - "math" "math/big" "github.com/ethereum/eth-go/ethlog" "github.com/ethereum/eth-go/ethutil" - "github.com/ethereum/eth-go/ethwire" ) var chainlogger = ethlog.NewLogger("CHAIN") @@ -110,99 +108,6 @@ func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int { 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 { return bc.genesisBlock @@ -228,66 +133,6 @@ func (self *BlockChain) GetChainHashesFromHash(hash []byte, max uint64) (chain [ 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) { for _, addr := range []string{ "51ba59315b3a95761d0863b05ccc7a7f54703d99", @@ -331,7 +176,7 @@ func (bc *BlockChain) setLastBlock() { } 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 } diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 08bd22d29..33af259cf 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -237,6 +237,8 @@ func (sm *StateManager) Process(block *Block, dontReact bool) (err error) { // Add the block to the chain sm.bc.Add(block) + sm.transState = state.Copy() + // Create a bloom bin for this block filter := sm.createBloomFilter(state) // Persist the data diff --git a/ethchain/state_transition.go b/ethchain/state_transition.go index 9fbc160a5..c1180a641 100644 --- a/ethchain/state_transition.go +++ b/ethchain/state_transition.go @@ -140,7 +140,7 @@ func (self *StateTransition) preCheck() (err error) { } func (self *StateTransition) TransitionState() (err error) { - statelogger.Infof("(~) %x\n", self.tx.Hash()) + statelogger.Debugf("(~) %x\n", self.tx.Hash()) /* defer func() { @@ -278,6 +278,15 @@ func (self *StateTransition) Eval(msg *ethstate.Message, script []byte, context 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 } diff --git a/ethchain/transaction.go b/ethchain/transaction.go index e1b48a3d3..e7e8f3a9f 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -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} func IsContractAddr(addr []byte) bool { - return bytes.Compare(addr, ContractAddr) == 0 + return len(addr) == 0 + //return bytes.Compare(addr, ContractAddr) == 0 } type Transaction struct { @@ -31,7 +32,7 @@ type Transaction struct { } 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 { diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index b0d62fd91..bd8b27a6d 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -72,8 +72,6 @@ type TxPool struct { func NewTxPool(ethereum EthManager) *TxPool { return &TxPool{ - //server: s, - mutex: sync.Mutex{}, pool: list.New(), queueChan: make(chan *Transaction, txPoolQueueSize), quit: make(chan bool), diff --git a/ethcrypto/crypto_test.go b/ethcrypto/crypto_test.go new file mode 100644 index 000000000..7323e1646 --- /dev/null +++ b/ethcrypto/crypto_test.go @@ -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) + } +} diff --git a/ethdb/database.go b/ethdb/database.go index 09e9d8c7d..e4b069930 100644 --- a/ethdb/database.go +++ b/ethdb/database.go @@ -2,9 +2,10 @@ package ethdb import ( "fmt" + "path" + "github.com/ethereum/eth-go/ethutil" "github.com/syndtr/goleveldb/leveldb" - "path" ) type LDBDatabase struct { @@ -45,7 +46,7 @@ func (db *LDBDatabase) Db() *leveldb.DB { } func (db *LDBDatabase) LastKnownTD() []byte { - data, _ := db.db.Get([]byte("LastKnownTotalDifficulty"), nil) + data, _ := db.db.Get([]byte("LTD"), nil) if len(data) == 0 { data = []byte{0x0} @@ -54,14 +55,6 @@ func (db *LDBDatabase) LastKnownTD() []byte { return data } -/* -func (db *LDBDatabase) GetKeys() []*ethutil.Key { - data, _ := db.Get([]byte("KeyRing")) - - return []*ethutil.Key{ethutil.NewKeyFromBytes(data)} -} -*/ - func (db *LDBDatabase) Close() { // Close the leveldb database db.db.Close() diff --git a/ethereum.go b/ethereum.go index 1e1891589..4c5e13b6d 100644 --- a/ethereum.go +++ b/ethereum.go @@ -90,7 +90,6 @@ type Ethereum struct { } func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager *ethcrypto.KeyManager, caps Caps, usePnp bool) (*Ethereum, error) { - var err error var nat NAT @@ -101,6 +100,8 @@ func New(db ethutil.Database, clientIdentity ethwire.ClientIdentity, keyManager } } + bootstrapDb(db) + ethutil.Config.Db = db nonce, _ := ethutil.RandomUint64() @@ -534,3 +535,12 @@ out: } } } + +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()) + } +} diff --git a/ethpipe/js_pipe.go b/ethpipe/js_pipe.go index b32e94a10..eeece5179 100644 --- a/ethpipe/js_pipe.go +++ b/ethpipe/js_pipe.go @@ -99,7 +99,8 @@ func (self *JSPipe) NumberToHuman(balance string) string { func (self *JSPipe) StorageAt(addr, storageAddr string) string { storage := self.World().SafeGet(ethutil.Hex2Bytes(addr)).Storage(ethutil.Hex2Bytes(storageAddr)) - return storage.BigInt().String() + + return ethutil.Bytes2Hex(storage.Bytes()) } func (self *JSPipe) TxCountAt(address string) int { diff --git a/ethpipe/js_types.go b/ethpipe/js_types.go index 0fb3a3876..8d2805f3d 100644 --- a/ethpipe/js_types.go +++ b/ethpipe/js_types.go @@ -1,7 +1,6 @@ package ethpipe import ( - "encoding/json" "strconv" "strings" @@ -13,15 +12,17 @@ import ( // Block interface exposed to QML type JSBlock struct { + //Transactions string `json:"transactions"` ref *ethchain.Block - Number int `json:"number"` - Hash string `json:"hash"` - Transactions string `json:"transactions"` - Time int64 `json:"time"` - Coinbase string `json:"coinbase"` - Name string `json:"name"` - GasLimit string `json:"gasLimit"` - GasUsed string `json:"gasUsed"` + Size string `json:"size"` + Number int `json:"number"` + Hash string `json:"hash"` + Transactions *ethutil.List `json:"transactions"` + Time int64 `json:"time"` + Coinbase string `json:"coinbase"` + Name string `json:"name"` + GasLimit string `json:"gasLimit"` + GasUsed string `json:"gasUsed"` } // Creates a new QML Block from a chain block @@ -35,12 +36,16 @@ func NewJSBlock(block *ethchain.Block) *JSBlock { ptxs = append(ptxs, *NewJSTx(tx)) } - txJson, err := json.Marshal(ptxs) - if err != nil { - return nil - } + /* + txJson, err := json.Marshal(ptxs) + 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 { diff --git a/ethreact/reactor.go b/ethreact/reactor.go index 7fe2356db..2edcbbbd9 100644 --- a/ethreact/reactor.go +++ b/ethreact/reactor.go @@ -1,8 +1,9 @@ package ethreact import ( - "github.com/ethereum/eth-go/ethlog" "sync" + + "github.com/ethereum/eth-go/ethlog" ) var logger = ethlog.NewLogger("REACTOR") @@ -32,7 +33,7 @@ func (e *EventHandler) Post(event Event) { select { case ch <- event: 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) } } } diff --git a/ethstate/state.go b/ethstate/state.go index cf060e795..42bbf021b 100644 --- a/ethstate/state.go +++ b/ethstate/state.go @@ -49,6 +49,15 @@ func (self *State) GetNonce(addr []byte) uint64 { 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 // @@ -103,7 +112,7 @@ func (self *State) GetOrNewStateObject(addr []byte) *StateObject { func (self *State) NewStateObject(addr []byte) *StateObject { addr = ethutil.Address(addr) - statelogger.Infof("(+) %x\n", addr) + statelogger.Debugf("(+) %x\n", addr) stateObject := NewStateObject(addr) self.stateObjects[string(addr)] = stateObject diff --git a/ethstate/state_object.go b/ethstate/state_object.go index 67d09edd8..6fc0696a8 100644 --- a/ethstate/state_object.go +++ b/ethstate/state_object.go @@ -245,6 +245,7 @@ func (self *StateObject) Copy() *StateObject { stateObject.InitCode = ethutil.CopyBytes(self.InitCode) stateObject.storage = self.storage.Copy() stateObject.gasPool.Set(self.gasPool) + stateObject.remove = self.remove return stateObject } @@ -271,6 +272,11 @@ func (c *StateObject) Init() Code { return c.InitCode } +// To satisfy ClosureRef +func (self *StateObject) Object() *StateObject { + return self +} + // Debug stuff func (self *StateObject) CreateOutputForDiff() { fmt.Printf("%x %x %x %x\n", self.Address(), self.State.Root(), self.Balance.Bytes(), self.Nonce) @@ -291,8 +297,12 @@ func (c *StateObject) RlpEncode() []byte { } else { root = "" } + var codeHash []byte + if len(c.Code) > 0 { + codeHash = ethcrypto.Sha3Bin(c.Code) + } - return ethutil.Encode([]interface{}{c.Nonce, c.Balance, root, ethcrypto.Sha3Bin(c.Code)}) + return ethutil.Encode([]interface{}{c.Nonce, c.Balance, root, codeHash}) } func (c *StateObject) RlpDecode(data []byte) { diff --git a/ethutil/list.go b/ethutil/list.go new file mode 100644 index 000000000..18bf04792 --- /dev/null +++ b/ethutil/list.go @@ -0,0 +1,42 @@ +package ethutil + +import "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()} +} + +// 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() +} diff --git a/ethutil/script.go b/ethutil/script.go index b796e7c1e..bd087e7e0 100644 --- a/ethutil/script.go +++ b/ethutil/script.go @@ -2,9 +2,11 @@ package ethutil import ( "fmt" + "strings" + "github.com/obscuren/mutan" "github.com/obscuren/mutan/backends" - "strings" + "github.com/obscuren/serpent-go" ) // General compile function @@ -14,15 +16,13 @@ func Compile(script string, silent bool) (ret []byte, err error) { if len(line) > 1 && line[0:2] == "#!" { switch line { - /* - case "#!serpent": - byteCode, err := serpent.Compile(script) - if err != nil { - return nil, err - } + case "#!serpent": + byteCode, err := serpent.Compile(script) + if err != nil { + return nil, err + } - return byteCode, nil - */ + return byteCode, nil } } else { diff --git a/ethutil/size.go b/ethutil/size.go new file mode 100644 index 000000000..b4426465e --- /dev/null +++ b/ethutil/size.go @@ -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) + } +} diff --git a/ethutil/size_test.go b/ethutil/size_test.go new file mode 100644 index 000000000..82aa1c653 --- /dev/null +++ b/ethutil/size_test.go @@ -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)) +} diff --git a/ethutil/value.go b/ethutil/value.go index 608d332ba..e8148b990 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -1,9 +1,11 @@ package ethutil import ( + "bytes" "fmt" "math/big" "reflect" + "strconv" ) // 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() } else if Val, ok := val.Val.(*big.Int); ok { return Val.Int64() + } else if Val, ok := val.Val.(string); ok { + n, _ := strconv.Atoi(Val) + return int64(n) } return 0 @@ -141,6 +146,8 @@ func (val *Value) Bytes() []byte { return []byte(s) } else if s, ok := val.Val.(*big.Int); ok { return s.Bytes() + } else { + return big.NewInt(val.Int()).Bytes() } return []byte{} @@ -244,10 +251,7 @@ func (val *Value) Cmp(o *Value) bool { } func (self *Value) DeepCmp(o *Value) bool { - a := NewValue(self.BigInt()) - b := NewValue(o.BigInt()) - - return a.Cmp(b) + return bytes.Compare(self.Bytes(), o.Bytes()) == 0 } func (val *Value) Encode() []byte { diff --git a/ethutil/value_test.go b/ethutil/value_test.go index 710cbd887..5452a0790 100644 --- a/ethutil/value_test.go +++ b/ethutil/value_test.go @@ -2,6 +2,7 @@ package ethutil import ( "bytes" + "fmt" "math/big" "testing" ) @@ -78,3 +79,8 @@ func TestMath(t *testing.T) { t.Error("Expected 0, got", a) } } + +func TestString(t *testing.T) { + a := NewValue("10") + fmt.Println("VALUE WITH STRING:", a.Int()) +} diff --git a/ethvm/closure.go b/ethvm/closure.go index 54bfd05f4..c047a83b7 100644 --- a/ethvm/closure.go +++ b/ethvm/closure.go @@ -12,6 +12,7 @@ import ( type ClosureRef interface { ReturnGas(*big.Int, *big.Int) Address() []byte + Object() *ethstate.StateObject GetStorage(*big.Int) *ethutil.Value SetStorage(*big.Int, *ethutil.Value) } diff --git a/ethvm/types.go b/ethvm/types.go index 36ba395d6..9cddd7c33 100644 --- a/ethvm/types.go +++ b/ethvm/types.go @@ -49,6 +49,8 @@ const ( CODESIZE = 0x38 CODECOPY = 0x39 GASPRICE = 0x3a + EXTCODECOPY = 0x3b + EXTCODESIZE = 0x3c // 0x40 range - block operations PREVHASH = 0x40 @@ -142,9 +144,11 @@ const ( SWAP16 = 0x9f // 0xf0 range - closures - CREATE = 0xf0 - CALL = 0xf1 - RETURN = 0xf2 + CREATE = 0xf0 + CALL = 0xf1 + RETURN = 0xf2 + POST = 0xf3 + CALLSTATELESS = 0xf4 // 0x70 range - other LOG = 0xfe // XXX Unofficial @@ -196,12 +200,14 @@ var opCodeToString = map[OpCode]string{ GASPRICE: "TXGASPRICE", // 0x40 range - block operations - PREVHASH: "PREVHASH", - COINBASE: "COINBASE", - TIMESTAMP: "TIMESTAMP", - NUMBER: "NUMBER", - DIFFICULTY: "DIFFICULTY", - GASLIMIT: "GASLIMIT", + PREVHASH: "PREVHASH", + COINBASE: "COINBASE", + TIMESTAMP: "TIMESTAMP", + NUMBER: "NUMBER", + DIFFICULTY: "DIFFICULTY", + GASLIMIT: "GASLIMIT", + EXTCODESIZE: "EXTCODESIZE", + EXTCODECOPY: "EXTCODECOPY", // 0x50 range - 'storage' and execution POP: "POP", @@ -287,9 +293,11 @@ var opCodeToString = map[OpCode]string{ SWAP16: "SWAP16", // 0xf0 range - CREATE: "CREATE", - CALL: "CALL", - RETURN: "RETURN", + CREATE: "CREATE", + CALL: "CALL", + RETURN: "RETURN", + POST: "POST", + CALLSTATELESS: "CALLSTATELESS", // 0x70 range - other LOG: "LOG", @@ -342,7 +350,12 @@ var OpCodes = map[string]byte{ "CALLVALUE": 0x34, "CALLDATALOAD": 0x35, "CALLDATASIZE": 0x36, - "GASPRICE": 0x38, + "CALLDATACOPY": 0x37, + "CODESIZE": 0x38, + "CODECOPY": 0x39, + "GASPRICE": 0x3a, + "EXTCODECOPY": 0x3b, + "EXTCODESIZE": 0x3c, // 0x40 range - block operations "PREVHASH": 0x40, @@ -435,9 +448,11 @@ var OpCodes = map[string]byte{ "SWAP16": 0x9f, // 0xf0 range - closures - "CREATE": 0xf0, - "CALL": 0xf1, - "RETURN": 0xf2, + "CREATE": 0xf0, + "CALL": 0xf1, + "RETURN": 0xf2, + "POST": 0xf3, + "CALLSTATELESS": 0xf4, // 0x70 range - other "LOG": 0xfe, diff --git a/ethvm/vm.go b/ethvm/vm.go index 873a80c44..fba8c4a0e 100644 --- a/ethvm/vm.go +++ b/ethvm/vm.go @@ -1,6 +1,7 @@ package ethvm import ( + "container/list" "fmt" "math" "math/big" @@ -18,11 +19,6 @@ type Debugger interface { } type Vm struct { - // Stack for processing contracts - stack *Stack - // non-persistent key/value memory storage - mem map[string]*big.Int - env Environment Verbose bool @@ -40,6 +36,8 @@ type Vm struct { Fn string Recoverable bool + + queue *list.List } type Environment interface { @@ -66,7 +64,7 @@ func New(env Environment) *Vm { lt = LogTyDiff } - return &Vm{env: env, logTy: lt, Recoverable: true} + return &Vm{env: env, logTy: lt, Recoverable: true, queue: list.New()} } func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { @@ -199,7 +197,11 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { require(3) newMemSize = stack.Peek().Uint64() + stack.data[stack.Len()-3].Uint64() - case CALL: + case EXTCODECOPY: + require(4) + + newMemSize = stack.data[stack.Len()-1].Uint64() + stack.data[stack.Len()-4].Uint64() + case CALL, CALLSTATELESS: require(7) gas.Set(GasCall) addStepGasUsage(stack.data[stack.Len()-1]) @@ -215,6 +217,7 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { newMemSize = stack.data[stack.Len()-2].Uint64() + stack.data[stack.Len()-3].Uint64() } + // BUG This will break on overflows. https://github.com/ethereum/eth-go/issues/47 newMemSize = (newMemSize + 31) / 32 * 32 if newMemSize > uint64(mem.Len()) { m := GasMemory.Uint64() * (newMemSize - uint64(mem.Len())) / 32 @@ -551,14 +554,32 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { code := closure.Args[cOff : cOff+l] mem.Set(mOff, l, code) - case CODESIZE: - l := big.NewInt(int64(len(closure.Code))) + case CODESIZE, EXTCODESIZE: + 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) 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 ( - size = int64(len(closure.Code)) + size = int64(len(code)) mOff = stack.Pop().Int64() cOff = stack.Pop().Int64() l = stack.Pop().Int64() @@ -571,9 +592,9 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { 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: stack.Push(closure.Price) @@ -631,12 +652,12 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { require(1) stack.Pop() 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) stack.Dupn(n) self.Printf(" => [%d] 0x%x", n, stack.Peek().Bytes()) 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) x, y := stack.Swapn(n) self.Printf(" => [%d] %x [0] %x", n, x.Bytes(), y.Bytes()) @@ -711,6 +732,8 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { err error value = stack.Pop() 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 // revert back to it later. @@ -726,37 +749,10 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { self.Printf(" (*) %x", addr).Endl() - msg := self.env.State().Manifest().AddMessage(ðstate.Message{ - To: addr, From: closure.Address(), - Origin: self.env.Origin(), - Block: self.env.BlockHash(), Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(), - Value: value, - }) - - // Create a new contract - contract := self.env.State().NewStateObject(addr) - if contract.Balance.Cmp(value) >= 0 { - closure.object.SubAmount(value) - contract.AddAmount(value) - - // Set the init script - initCode := mem.Get(offset.Int64(), size.Int64()) - msg.Input = initCode - - // Transfer all remaining gas to the new - // contract so it may run the init script - gas := new(big.Int).Set(closure.Gas) - closure.UseGas(closure.Gas) - - // 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) - } + closure.UseGas(closure.Gas) + msg := NewMessage(self, addr, input, gas, closure.Price, value) + ret, err := msg.Exec(addr, closure) if err != nil { stack.Push(ethutil.BigFalse) @@ -765,17 +761,18 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { self.Printf("CREATE err %v", err) } else { - stack.Push(ethutil.BigD(addr)) + msg.object.Code = ret - msg.Output = contract.Code + stack.Push(ethutil.BigD(addr)) } + self.Endl() // Debug hook if self.Dbg != nil { self.Dbg.SetCode(closure.Code) } - case CALL: + case CALL, CALLSTATELESS: require(7) self.Endl() @@ -791,51 +788,48 @@ func (self *Vm) RunClosure(closure *Closure) (ret []byte, err error) { // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) - msg := self.env.State().Manifest().AddMessage(ðstate.Message{ - To: addr.Bytes(), From: closure.Address(), - Input: args, - Origin: self.env.Origin(), - Block: self.env.BlockHash(), Timestamp: self.env.Time(), Coinbase: self.env.Coinbase(), Number: self.env.BlockNumber(), - Value: value, - }) - - if closure.object.Balance.Cmp(value) < 0 { - vmlogger.Debugf("Insufficient funds to transfer value. Req %v, has %v", value, closure.object.Balance) + snapshot := self.env.State().Copy() - closure.ReturnGas(gas, nil) + var executeAddr []byte + if op == CALLSTATELESS { + executeAddr = closure.Address() + } else { + executeAddr = addr.Bytes() + } + msg := NewMessage(self, executeAddr, args, gas, closure.Price, value) + ret, err := msg.Exec(addr.Bytes(), closure) + if err != nil { stack.Push(ethutil.BigFalse) - } else { - snapshot := self.env.State().Copy() - stateObject := self.env.State().GetOrNewStateObject(addr.Bytes()) + self.env.State().Set(snapshot) + } else { + stack.Push(ethutil.BigTrue) - closure.object.SubAmount(value) - stateObject.AddAmount(value) + mem.Set(retOffset.Int64(), retSize.Int64(), ret) + } - // 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) + // Debug hook + if self.Dbg != nil { + self.Dbg.SetCode(closure.Code) + } - vmlogger.Debugf("Closure execution failed. %v\n", err) + case POST: + require(6) - self.env.State().Set(snapshot) - } else { - stack.Push(ethutil.BigTrue) + self.Endl() - mem.Set(retOffset.Int64(), retSize.Int64(), ret) - } + 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.Output = ret + msg := NewMessage(self, addr.Bytes(), args, gas, closure.Price, value) - // Debug hook - if self.Dbg != nil { - self.Dbg.SetCode(closure.Code) - } - } + msg.Postpone() case RETURN: require(2) size, offset := stack.Popn() @@ -887,6 +881,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 { if self.Verbose && self.logTy == LogTyPretty { self.logStr += fmt.Sprintf(format, v...) @@ -918,3 +916,71 @@ func ensure256(x *big.Int) { 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) { + 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, object, 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 +} diff --git a/ethwire/messaging.go b/ethwire/messaging.go index 7ac0188a1..67a866f73 100644 --- a/ethwire/messaging.go +++ b/ethwire/messaging.go @@ -27,24 +27,20 @@ const ( // Values are given explicitly instead of by iota because these values are // defined by the wire protocol spec; it is easier for humans to ensure // correctness when values are explicit. - MsgHandshakeTy = 0x00 - MsgDiscTy = 0x01 - MsgPingTy = 0x02 - MsgPongTy = 0x03 - MsgGetPeersTy = 0x10 - MsgPeersTy = 0x11 + MsgHandshakeTy = 0x00 + MsgDiscTy = 0x01 + MsgPingTy = 0x02 + MsgPongTy = 0x03 + MsgGetPeersTy = 0x04 + MsgPeersTy = 0x05 + + MsgStatusTy = 0x10 + MsgGetTxsTy = 0x11 MsgTxTy = 0x12 - MsgGetChainTy = 0x14 - MsgNotInChainTy = 0x15 - MsgGetTxsTy = 0x16 - MsgGetBlockHashesTy = 0x17 - MsgBlockHashesTy = 0x18 - MsgGetBlocksTy = 0x19 - MsgBlockTy = 0x13 - - MsgOldBlockTy = 0xbb - - MsgTalkTy = 0xff + MsgGetBlockHashesTy = 0x13 + MsgBlockHashesTy = 0x14 + MsgGetBlocksTy = 0x15 + MsgBlockTy = 0x16 ) var msgTypeToString = map[MsgType]string{ @@ -53,12 +49,11 @@ var msgTypeToString = map[MsgType]string{ MsgPingTy: "Ping", MsgPongTy: "Pong", MsgGetPeersTy: "Get peers", + MsgStatusTy: "Status", MsgPeersTy: "Peers", MsgTxTy: "Transactions", MsgBlockTy: "Blocks", - MsgGetChainTy: "Get chain", MsgGetTxsTy: "Get Txs", - MsgNotInChainTy: "Not in chain", MsgGetBlockHashesTy: "Get block hashes", MsgBlockHashesTy: "Block hashes", MsgGetBlocksTy: "Get blocks", @@ -1,7 +1,6 @@ package eth import ( - //natpmp "code.google.com/p/go-nat-pmp" "fmt" "net" @@ -25,6 +25,8 @@ const ( outputBufferSize = 50 // Current protocol version ProtocolVersion = 28 + // Current P2P version + P2PVersion = 0 // Interval for ping/pong message pingPongTimer = 2 * time.Second ) @@ -122,6 +124,7 @@ type Peer struct { // 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. versionKnown bool + statusKnown bool // Last received pong message lastPong int64 @@ -271,6 +274,14 @@ func (p *Peer) writeMessage(msg *ethwire.Msg) { default: // Anything but ack is allowed 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) @@ -356,9 +367,9 @@ func (p *Peer) HandleInbound() { // Version message p.handleHandshake(msg) - if p.caps.IsCap(CapPeerDiscTy) { - p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, "")) - } + //if p.caps.IsCap(CapPeerDiscTy) { + p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, "")) + //} case ethwire.MsgDiscTy: p.Stop() @@ -396,6 +407,10 @@ func (p *Peer) HandleInbound() { // Connect to the list of peers p.ethereum.ProcessPeerList(peers) + + case ethwire.MsgStatusTy: + // Handle peer's status msg + p.handleStatus(msg) case ethwire.MsgGetTxsTy: // Get the current transactions of the pool txs := p.ethereum.TxPool().CurrentTransactions() @@ -474,7 +489,7 @@ func (p *Peer) HandleInbound() { for it.Next() { block := ethchain.NewBlockFromRlpValue(it.Value()) - blockPool.SetBlock(block) + blockPool.SetBlock(block, p) p.lastBlockReceived = time.Now() } @@ -507,6 +522,7 @@ func (self *Peer) FetchHashes() { if self.td.Cmp(blockPool.td) >= 0 { peerlogger.Debugf("Requesting hashes from %x\n", self.lastReceivedHash) + blockPool.td = self.td if !blockPool.HasLatestHash() { self.QueueMessage(ethwire.NewMessage(ethwire.MsgGetBlockHashesTy, []interface{}{self.lastReceivedHash, uint32(200)})) @@ -580,6 +596,7 @@ func (p *Peer) Stop() { p.ethereum.RemovePeer(p) } +/* func (p *Peer) pushHandshake() error { pubkey := p.ethereum.KeyManager().PublicKey() msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{ @@ -591,6 +608,7 @@ func (p *Peer) pushHandshake() error { return nil } +*/ func (p *Peer) peersMessage() *ethwire.Msg { outPeers := make([]interface{}, len(p.ethereum.InOutPeers())) @@ -611,13 +629,72 @@ func (p *Peer) pushPeers() { p.QueueMessage(p.peersMessage()) } +func (p *Peer) pushHandshake() error { + pubkey := p.ethereum.KeyManager().PublicKey() + msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{ + uint32(0), []byte(p.version), []string{"eth"}, p.port, pubkey[1:], + }) + + p.QueueMessage(msg) + + return nil +} + +func (self *Peer) pushStatus() { + const netVersion = 0 + msg := ethwire.NewMessage(ethwire.MsgStatusTy, []interface{}{ + uint32(ProtocolVersion), + netVersion, + self.ethereum.BlockChain().TD.Uint64(), + self.ethereum.BlockChain().CurrentBlock.Hash(), + self.ethereum.BlockChain().Genesis().Hash(), + }) + + self.QueueMessage(msg) +} + +func (self *Peer) handleStatus(msg *ethwire.Msg) { + c := msg.Data + // Set the peer's caps + //p.caps = Caps(c.Get(3).Byte()) + + // Get the td and last hash + self.td = c.Get(6).BigInt() + self.bestHash = c.Get(7).Bytes() + self.lastReceivedHash = self.bestHash + + // 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", self.td, self.bestHash) +} + func (p *Peer) handleHandshake(msg *ethwire.Msg) { c := msg.Data - // Set pubkey - p.pubkey = c.Get(5).Bytes() + var ( + p2pVersion = c.Get(0).Uint() + clientId = c.Get(1).Str() + caps = c.Get(2).Raw() + port = c.Get(3).Uint() + pub = c.Get(4).Bytes() + ) + + fmt.Println("PEER CAPS", caps) + + // 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 + } - if p.pubkey == nil { + // Handle the pub key (validation, uniqueness) + if len(pub) == 0 { peerlogger.Warnln("Pubkey required, not supplied in handshake.") p.Stop() return @@ -625,9 +702,8 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { usedPub := 0 // 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) { - if bytes.Compare(p.pubkey, peer.pubkey) == 0 { + if bytes.Compare(pub, peer.pubkey) == 0 { usedPub++ } }) @@ -637,19 +713,11 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { p.Stop() return } - - 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 + p.pubkey = pub // If this is an inbound connection send an ack back if p.inbound { - p.port = uint16(c.Get(4).Uint()) + p.port = uint16(port) // Self connect detection pubkey := p.ethereum.KeyManager().PublicKey() @@ -660,41 +728,18 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { } } + p.SetVersion(clientId) - // Set the peer's caps - 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.versionKnown = true p.ethereum.PushPeer(p) 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) - - /* - // Catch up with the connected peer - if !p.ethereum.IsUpToDate() { - peerlogger.Debugln("Already syncing up with a peer; sleeping") - time.Sleep(10 * time.Second) - } - */ - //p.SyncWithPeerToLastKnown() - - if p.td.Cmp(p.ethereum.BlockChain().TD) > 0 { - p.ethereum.blockPool.AddHash(p.lastReceivedHash) - p.FetchHashes() - } + ethlogger.Infof("Added peer (%s) %d / %d \n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers) peerlogger.Debugln(p) + + p.pushStatus() } func (p *Peer) String() string { @@ -714,47 +759,6 @@ func (p *Peer) String() string { 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{} { return []interface{}{p.host, p.port, p.pubkey} |