From d5efeab8f92509dec3cafcafb36e1856bb084f12 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 10 Mar 2014 11:53:02 +0100 Subject: [PATCH 001/100] Initial smart-miner stuff --- ethchain/block_chain.go | 1 + ethchain/dagger.go | 24 ++++++++++++++++-------- ethchain/state_manager.go | 15 +++++++++++++++ ethchain/transaction_pool.go | 8 +++++++- ethereum.go | 8 ++++++++ 5 files changed, 47 insertions(+), 9 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 2865e0a21..93970a2c5 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -136,6 +136,7 @@ func AddTestNetFunds(block *Block) { "e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex + "2ef47100e0787b915105fd5e3f4ff6752079d5cb", // Maran } { //log.Println("2^200 Wei to", addr) codedAddr := ethutil.FromHex(addr) diff --git a/ethchain/dagger.go b/ethchain/dagger.go index 5b4f8b2cd..c33b3c14e 100644 --- a/ethchain/dagger.go +++ b/ethchain/dagger.go @@ -11,7 +11,7 @@ import ( ) type PoW interface { - Search(block *Block) []byte + Search(block *Block, breakChan chan bool) []byte Verify(hash []byte, diff *big.Int, nonce []byte) bool } @@ -19,15 +19,23 @@ type EasyPow struct { hash *big.Int } -func (pow *EasyPow) Search(block *Block) []byte { +func (pow *EasyPow) Search(block *Block, breakChan chan bool) []byte { r := rand.New(rand.NewSource(time.Now().UnixNano())) - hash := block.HashNoNonce() diff := block.Difficulty + for { - sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes()) - if pow.Verify(hash, diff, sha) { - return sha + select { + case shouldbreak := <-breakChan: + if shouldbreak { + log.Println("Got signal: Breaking out mining.") + return nil + } + default: + sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes()) + if pow.Verify(hash, diff, sha) { + return sha + } } } @@ -98,9 +106,9 @@ func (dag *Dagger) Search(hash, diff *big.Int) *big.Int { for k := 0; k < amountOfRoutines; k++ { go dag.Find(obj, resChan) - } - // Wait for each go routine to finish + // Wait for each go routine to finish + } for k := 0; k < amountOfRoutines; k++ { // Get the result from the channel. 0 = quit if r := <-resChan; r != 0 { diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 7085146df..2652f3f29 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -19,6 +19,7 @@ type EthManager interface { BlockChain() *BlockChain TxPool() *TxPool Broadcast(msgType ethwire.MsgType, data []interface{}) + Reactor() *ethutil.ReactorEngine } // TODO rename to state manager @@ -50,6 +51,9 @@ type StateManager struct { // Comparative state it used for comparing and validating end // results compState *State + + // Mining state, solely used for mining + miningState *State } func NewStateManager(ethereum EthManager) *StateManager { @@ -69,6 +73,10 @@ func (sm *StateManager) ProcState() *State { return sm.procState } +func (sm *StateManager) MiningState() *State { + return sm.miningState +} + // Watches any given address and puts it in the address state store func (sm *StateManager) WatchAddr(addr []byte) *AccountState { //FIXME account := sm.procState.GetAccount(addr) @@ -97,6 +105,8 @@ func (sm *StateManager) MakeContract(tx *Transaction) { sm.procState.states[string(tx.Hash()[12:])] = contract.state } } +func (sm *StateManager) ApplyTransaction(block *Block, tx *Transaction) { +} func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { // Process each transaction/contract @@ -126,6 +136,10 @@ func (sm *StateManager) Prepare(processer *State, comparative *State) { sm.procState = processer } +func (sm *StateManager) PrepareMiningState() { + sm.miningState = sm.BlockChain().CurrentBlock.State() +} + // Default prepare function func (sm *StateManager) PrepareDefault(block *Block) { sm.Prepare(sm.BlockChain().CurrentBlock.State(), block.State()) @@ -193,6 +207,7 @@ func (sm *StateManager) ProcessBlock(block *Block) error { } ethutil.Config.Log.Infof("[smGR] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash()) + sm.Ethereum.Reactor().Post("newBlock", block) } else { fmt.Println("total diff failed") } diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 2c9a26936..345764d09 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -91,6 +91,7 @@ func (pool *TxPool) addTransaction(tx *Transaction) { // Process transaction validates the Tx and processes funds from the // sender to the recipient. func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) { + log.Println("Processing TX") defer func() { if r := recover(); r != nil { log.Println(r) @@ -137,7 +138,6 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error log.Printf("[TXPL] Processed Tx %x\n", tx.Hash()) - // Notify the subscribers pool.notifySubscribers(TxPost, tx) return @@ -174,6 +174,7 @@ out: for { select { case tx := <-pool.queueChan: + log.Println("Received new Tx to queue") hash := tx.Hash() foundTx := FindTx(pool.pool, func(tx *Transaction, e *list.Element) bool { return bytes.Compare(tx.Hash(), hash) == 0 @@ -190,9 +191,14 @@ out: log.Println("Validating Tx failed", err) } } else { + log.Println("Transaction ok, adding") // Call blocking version. At this point it // doesn't matter since this is a goroutine pool.addTransaction(tx) + log.Println("Added") + + // Notify the subscribers + pool.Ethereum.Reactor().Post("newTx", tx) // Notify the subscribers pool.notifySubscribers(TxPre, tx) diff --git a/ethereum.go b/ethereum.go index 342f4f573..302f3c04f 100644 --- a/ethereum.go +++ b/ethereum.go @@ -60,6 +60,8 @@ type Ethereum struct { // Specifies the desired amount of maximum peers MaxPeers int + + reactor *ethutil.ReactorEngine } func New(caps Caps, usePnp bool) (*Ethereum, error) { @@ -89,6 +91,8 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) { serverCaps: caps, nat: nat, } + ethereum.reactor = ethutil.NewReactorEngine() + ethereum.txPool = ethchain.NewTxPool(ethereum) ethereum.blockChain = ethchain.NewBlockChain(ethereum) ethereum.stateManager = ethchain.NewStateManager(ethereum) @@ -99,6 +103,10 @@ func New(caps Caps, usePnp bool) (*Ethereum, error) { return ethereum, nil } +func (s *Ethereum) Reactor() *ethutil.ReactorEngine { + return s.reactor +} + func (s *Ethereum) BlockChain() *ethchain.BlockChain { return s.blockChain } From 3274e0a2496e622a847b213bb5ba0272650ef06c Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 17 Mar 2014 10:37:29 +0100 Subject: [PATCH 002/100] Removed extra invalid nonce return --- ethchain/transaction_pool.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 345764d09..b8366d274 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -109,11 +109,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error } if sender.Nonce != tx.Nonce { - if ethutil.Config.Debug { - return fmt.Errorf("Invalid nonce %d(%d) continueing anyway", tx.Nonce, sender.Nonce) - } else { - return fmt.Errorf("Invalid nonce %d(%d)", tx.Nonce, sender.Nonce) - } + return fmt.Errorf("Invalid nonce %d(%d)", tx.Nonce, sender.Nonce) } // Get the receiver From 8ea7e21f64842380833cce7aafa52b909cb8426b Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 17 Mar 2014 10:37:37 +0100 Subject: [PATCH 003/100] Merge --- README.md | 4 +-- ethchain/dagger.go | 23 +++++++++--- ethchain/keypair.go | 74 +++++++++++++++++++++++++++++++++++++++ ethchain/state_manager.go | 1 - ethereum.go | 51 ++++++++++++++++++++------- ethutil/trie.go | 12 ------- 6 files changed, 132 insertions(+), 33 deletions(-) create mode 100644 ethchain/keypair.go diff --git a/README.md b/README.md index f6c49cc2d..3553a5e35 100644 --- a/README.md +++ b/README.md @@ -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 2". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Edge). +of Concept 3". 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. @@ -35,7 +35,7 @@ get a node and connectivity going. Build ======= -This is the Developer package. For the development client please see +This is the Developer package. For the Ethereal client please see [Ethereum(G)](https://github.com/ethereum/go-ethereum). `go get -u github.com/ethereum/eth-go` diff --git a/ethchain/dagger.go b/ethchain/dagger.go index c33b3c14e..4d2034e20 100644 --- a/ethchain/dagger.go +++ b/ethchain/dagger.go @@ -11,7 +11,7 @@ import ( ) type PoW interface { - Search(block *Block, breakChan chan bool) []byte + Search(block *Block, minerChan chan ethutil.React) []byte Verify(hash []byte, diff *big.Int, nonce []byte) bool } @@ -19,19 +19,32 @@ type EasyPow struct { hash *big.Int } -func (pow *EasyPow) Search(block *Block, breakChan chan bool) []byte { +func (pow *EasyPow) Search(block *Block, minerChan chan ethutil.React) []byte { r := rand.New(rand.NewSource(time.Now().UnixNano())) hash := block.HashNoNonce() diff := block.Difficulty + i := int64(0) + start := time.Now().UnixNano() for { select { - case shouldbreak := <-breakChan: - if shouldbreak { - log.Println("Got signal: Breaking out mining.") + case chanMessage := <-minerChan: + if _, ok := chanMessage.Resource.(*Block); ok { + log.Println("BREAKING OUT: BLOCK") + return nil + } + if _, ok := chanMessage.Resource.(*Transaction); ok { + log.Println("BREAKING OUT: TX") return nil } default: + i++ + if i%1234567 == 0 { + elapsed := time.Now().UnixNano() - start + hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000 + log.Println("Hashing @", int64(hashes), "khash") + } + sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes()) if pow.Verify(hash, diff, sha) { return sha diff --git a/ethchain/keypair.go b/ethchain/keypair.go new file mode 100644 index 000000000..9fdc95972 --- /dev/null +++ b/ethchain/keypair.go @@ -0,0 +1,74 @@ +package ethchain + +import ( + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +type KeyPair struct { + PrivateKey []byte + PublicKey []byte + + // The associated account + account *Account + state *State +} + +func NewKeyPairFromValue(val *ethutil.Value) *KeyPair { + keyPair := &KeyPair{PrivateKey: val.Get(0).Bytes(), PublicKey: val.Get(1).Bytes()} + + return keyPair +} + +func (k *KeyPair) Address() []byte { + return ethutil.Sha3Bin(k.PublicKey[1:])[12:] +} + +func (k *KeyPair) Account() *Account { + if k.account == nil { + k.account = k.state.GetAccount(k.Address()) + } + + return k.account +} + +// Create transaction, creates a new and signed transaction, ready for processing +func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Transaction { + tx := NewTransaction(receiver, value, data) + tx.Nonce = k.account.Nonce + + // Sign the transaction with the private key in this key chain + tx.Sign(k.PrivateKey) + + return tx +} + +func (k *KeyPair) RlpEncode() []byte { + return ethutil.EmptyValue().Append(k.PrivateKey).Append(k.PublicKey).Encode() +} + +type KeyRing struct { + keys []*KeyPair +} + +func (k *KeyRing) Add(pair *KeyPair) { + k.keys = append(k.keys, pair) +} + +// The public "singleton" keyring +var keyRing *KeyRing + +func GetKeyRing(state *State) *KeyRing { + if keyRing == nil { + keyRing = &KeyRing{} + + data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) + it := ethutil.NewValueFromBytes(data).NewIterator() + for it.Next() { + v := it.Value() + keyRing.Add(NewKeyPairFromValue(v)) + } + } + + return keyRing +} diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 2652f3f29..39dece40e 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -22,7 +22,6 @@ type EthManager interface { Reactor() *ethutil.ReactorEngine } -// TODO rename to state manager type StateManager struct { // Mutex for locking the block processor. Blocks can only be handled one at a time mutex sync.Mutex diff --git a/ethereum.go b/ethereum.go index 302f3c04f..c906a6954 100644 --- a/ethereum.go +++ b/ethereum.go @@ -271,20 +271,45 @@ func (s *Ethereum) Start() { if ethutil.Config.Seed { ethutil.Config.Log.Debugln("Seeding") - // Testnet seed bootstrapping - resp, err := http.Get("https://www.ethereum.org/servers.poc3.txt") - if err != nil { - log.Println("Fetching seed failed:", err) - return - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Println("Reading seed failed:", err) - return - } + // DNS Bootstrapping + _, 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) + log.Println("Found DNS Bootstrap Peer:", peer) + peers = append(peers, peer) + } + } else { + log.Println("Couldn't resolve :", target) + } + } + // Connect to Peer list + s.ProcessPeerList(peers) + } else { + // Fallback to servers.poc3.txt + resp, err := http.Get("http://www.ethereum.org/servers.poc3.txt") + if err != nil { + log.Println("Fetching seed failed:", err) + return + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + log.Println("Reading seed failed:", err) + return + } - s.ConnectToPeer(string(body)) + s.ConnectToPeer(string(body)) + } } } diff --git a/ethutil/trie.go b/ethutil/trie.go index a17dc37ad..c67f750bc 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -219,18 +219,6 @@ func (t *Trie) UpdateState(node interface{}, key []int, value string) interface{ } func (t *Trie) Put(node interface{}) interface{} { - /* - enc := Encode(node) - if len(enc) >= 32 { - var sha []byte - sha = Sha3Bin(enc) - //t.db.Put([]byte(sha), enc) - - return sha - } - return node - */ - /* TODO? c := Conv(t.Root) From ae837c4719855384921fcaadb1a575942dc9833d Mon Sep 17 00:00:00 2001 From: Maran Date: Thu, 20 Mar 2014 11:20:29 +0100 Subject: [PATCH 004/100] More mining rework --- ethchain/block.go | 3 + ethchain/block_chain.go | 3 +- ethchain/dagger.go | 16 ++-- ethchain/state_manager.go | 28 ++----- ethchain/transaction_pool.go | 9 +-- ethminer/miner.go | 149 +++++++++++++++++++++++++++++++++++ peer.go | 2 +- 7 files changed, 171 insertions(+), 39 deletions(-) create mode 100644 ethminer/miner.go diff --git a/ethchain/block.go b/ethchain/block.go index 20af73ba2..d42aa7d83 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -304,6 +304,9 @@ func NewUncleBlockFromValue(header *ethutil.Value) *Block { func (block *Block) String() string { return fmt.Sprintf("Block(%x):\nPrevHash:%x\nUncleSha:%x\nCoinbase:%x\nRoot:%x\nTxSha:%x\nDiff:%v\nTime:%d\nNonce:%x\nTxs:%d\n", block.Hash(), block.PrevHash, block.UncleSha, block.Coinbase, block.state.trie.Root, block.TxSha, block.Difficulty, block.Time, block.Nonce, len(block.transactions)) } +func (block *Block) GetRoot() interface{} { + return block.state.trie.Root +} //////////// UNEXPORTED ///////////////// func (block *Block) header() []interface{} { diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 93970a2c5..90ad4516a 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -44,7 +44,6 @@ func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block { hash = bc.LastBlockHash lastBlockTime = bc.CurrentBlock.Time } - block := CreateBlock( root, hash, @@ -181,8 +180,8 @@ func (bc *BlockChain) SetTotalDifficulty(td *big.Int) { // Add a block to the chain and record addition information func (bc *BlockChain) Add(block *Block) { bc.writeBlockInfo(block) - // Prepare the genesis block + bc.CurrentBlock = block bc.LastBlockHash = block.Hash() diff --git a/ethchain/dagger.go b/ethchain/dagger.go index 4d2034e20..a80a9d421 100644 --- a/ethchain/dagger.go +++ b/ethchain/dagger.go @@ -11,7 +11,7 @@ import ( ) type PoW interface { - Search(block *Block, minerChan chan ethutil.React) []byte + Search(block *Block, reactChan chan ethutil.React) []byte Verify(hash []byte, diff *big.Int, nonce []byte) bool } @@ -19,7 +19,7 @@ type EasyPow struct { hash *big.Int } -func (pow *EasyPow) Search(block *Block, minerChan chan ethutil.React) []byte { +func (pow *EasyPow) Search(block *Block, reactChan chan ethutil.React) []byte { r := rand.New(rand.NewSource(time.Now().UnixNano())) hash := block.HashNoNonce() diff := block.Difficulty @@ -28,15 +28,9 @@ func (pow *EasyPow) Search(block *Block, minerChan chan ethutil.React) []byte { for { select { - case chanMessage := <-minerChan: - if _, ok := chanMessage.Resource.(*Block); ok { - log.Println("BREAKING OUT: BLOCK") - return nil - } - if _, ok := chanMessage.Resource.(*Transaction); ok { - log.Println("BREAKING OUT: TX") - return nil - } + case <-reactChan: + log.Println("[pow] Received reactor event; breaking out.") + return nil default: i++ if i%1234567 == 0 { diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 3be940745..46d8228d9 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -50,9 +50,6 @@ type StateManager struct { // Comparative state it used for comparing and validating end // results compState *State - - // Mining state, solely used for mining - miningState *State } func NewStateManager(ethereum EthManager) *StateManager { @@ -65,7 +62,6 @@ func NewStateManager(ethereum EthManager) *StateManager { bc: ethereum.BlockChain(), } sm.procState = ethereum.BlockChain().CurrentBlock.State() - return sm } @@ -73,10 +69,6 @@ func (sm *StateManager) ProcState() *State { return sm.procState } -func (sm *StateManager) MiningState() *State { - return sm.miningState -} - // Watches any given address and puts it in the address state store func (sm *StateManager) WatchAddr(addr []byte) *AccountState { //XXX account := sm.bc.CurrentBlock.state.GetAccount(addr) @@ -105,8 +97,6 @@ func (sm *StateManager) MakeContract(tx *Transaction) { sm.procState.states[string(tx.Hash()[12:])] = contract.state } } -func (sm *StateManager) ApplyTransaction(block *Block, tx *Transaction) { -} func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { // Process each transaction/contract @@ -136,17 +126,13 @@ func (sm *StateManager) Prepare(processer *State, comparative *State) { sm.procState = processer } -func (sm *StateManager) PrepareMiningState() { - sm.miningState = sm.BlockChain().CurrentBlock.State() -} - // Default prepare function func (sm *StateManager) PrepareDefault(block *Block) { sm.Prepare(sm.BlockChain().CurrentBlock.State(), block.State()) } // Block processing and validating with a given (temporarily) state -func (sm *StateManager) ProcessBlock(block *Block) error { +func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { // Processing a blocks may never happen simultaneously sm.mutex.Lock() defer sm.mutex.Unlock() @@ -155,7 +141,6 @@ func (sm *StateManager) ProcessBlock(block *Block) error { // nodes this won't happen because Commit would have been called // before that. defer sm.bc.CurrentBlock.Undo() - hash := block.Hash() if sm.bc.HasBlock(hash) { @@ -207,7 +192,9 @@ func (sm *StateManager) ProcessBlock(block *Block) error { } ethutil.Config.Log.Infof("[STATE] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash()) - sm.Ethereum.Reactor().Post("newBlock", block) + if dontReact == false { + sm.Ethereum.Reactor().Post("newBlock", block) + } } else { fmt.Println("total diff failed") } @@ -285,15 +272,16 @@ func CalculateUncleReward(block *Block) *big.Int { } func (sm *StateManager) AccumelateRewards(block *Block) error { + // Get the coinbase rlp data //XXX addr := processor.state.GetAccount(block.Coinbase) addr := sm.procState.GetAccount(block.Coinbase) // Reward amount of ether to the coinbase address addr.AddFee(CalculateBlockReward(block, len(block.Uncles))) - //XXX processor.state.UpdateAccount(block.Coinbase, addr) - sm.procState.UpdateAccount(block.Coinbase, addr) - + var acc []byte + copy(acc, block.Coinbase) + sm.procState.UpdateAccount(acc, addr) for _, uncle := range block.Uncles { uncleAddr := sm.procState.GetAccount(uncle.Coinbase) uncleAddr.AddFee(CalculateUncleReward(uncle)) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index b0df1b6c0..26827c289 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -91,7 +91,6 @@ func (pool *TxPool) addTransaction(tx *Transaction) { // Process transaction validates the Tx and processes funds from the // sender to the recipient. func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) { - log.Println("Processing TX") defer func() { if r := recover(); r != nil { log.Println(r) @@ -105,11 +104,11 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error // funds won't invalidate this transaction but simple ignores it. totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) if sender.Amount.Cmp(totAmount) < 0 { - return errors.New("Insufficient amount in sender's account") + return errors.New("[TXPL] Insufficient amount in sender's account") } if sender.Nonce != tx.Nonce { - return fmt.Errorf("Invalid nonce %d(%d)", tx.Nonce, sender.Nonce) + return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transactoin nonce is %d instead", sender.Nonce, tx.Nonce) } // Get the receiver @@ -145,7 +144,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { block := pool.Ethereum.BlockChain().CurrentBlock // Something has gone horribly wrong if this happens if block == nil { - return errors.New("No last block on the block chain") + return errors.New("[TXPL] No last block on the block chain") } // Get the sender @@ -156,7 +155,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { // Make sure there's enough in the sender's account. Having insufficient // funds won't invalidate this transaction but simple ignores it. if sender.Amount.Cmp(totAmount) < 0 { - return fmt.Errorf("Insufficient amount in sender's (%x) account", tx.Sender()) + return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) } // Increment the nonce making each tx valid only once to prevent replay diff --git a/ethminer/miner.go b/ethminer/miner.go new file mode 100644 index 000000000..f4f697aba --- /dev/null +++ b/ethminer/miner.go @@ -0,0 +1,149 @@ +package ethminer + +import ( + "bytes" + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/eth-go/ethwire" + "log" +) + +type Miner struct { + pow ethchain.PoW + ethereum ethchain.EthManager + coinbase []byte + reactChan chan ethutil.React + txs []*ethchain.Transaction + uncles []*ethchain.Block + block *ethchain.Block + powChan chan []byte + quitChan chan ethutil.React +} + +func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner { + reactChan := make(chan ethutil.React, 1) // This is the channel that receives 'updates' when ever a new transaction or block comes in + powChan := make(chan []byte, 1) // This is the channel that receives valid sha hases for a given block + quitChan := make(chan ethutil.React, 1) // This is the channel that can exit the miner thread + + ethereum.Reactor().Subscribe("newBlock", reactChan) + ethereum.Reactor().Subscribe("newTx", reactChan) + + // We need the quit chan to be a Reactor event. + // The POW search method is actually blocking and if we don't + // listen to the reactor events inside of the pow itself + // The miner overseer will never get the reactor events themselves + // Only after the miner will find the sha + ethereum.Reactor().Subscribe("newBlock", quitChan) + ethereum.Reactor().Subscribe("newTx", quitChan) + + miner := Miner{ + pow: ðchain.EasyPow{}, + ethereum: ethereum, + coinbase: coinbase, + reactChan: reactChan, + powChan: powChan, + quitChan: quitChan, + } + + // Insert initial TXs in our little miner 'pool' + miner.txs = ethereum.TxPool().Flush() + miner.block = ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) + + return miner +} +func (miner *Miner) Start() { + // Prepare inital block + miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State()) + go func() { miner.listener() }() +} +func (miner *Miner) listener() { + for { + select { + case chanMessage := <-miner.reactChan: + if block, ok := chanMessage.Resource.(*ethchain.Block); ok { + log.Println("[miner] Got new block via Reactor") + if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 { + // TODO: Perhaps continue mining to get some uncle rewards + log.Println("[miner] New top block found resetting state") + + // Filter out which Transactions we have that were not in this block + var newtxs []*ethchain.Transaction + for _, tx := range miner.txs { + found := false + for _, othertx := range block.Transactions() { + if bytes.Compare(tx.Hash(), othertx.Hash()) == 0 { + found = true + } + } + if found == false { + newtxs = append(newtxs, tx) + } + } + miner.txs = newtxs + + // Setup a fresh state to mine on + miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) + + } else { + if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 { + log.Println("[miner] Adding uncle block") + miner.uncles = append(miner.uncles, block) + miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State()) + } + } + } + + if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok { + log.Println("[miner] Got new transaction from Reactor", tx) + found := false + for _, ctx := range miner.txs { + if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found { + break + } + + } + if found == false { + log.Println("[miner] We did not know about this transaction, adding") + miner.txs = append(miner.txs, tx) + miner.block.SetTransactions(miner.txs) + } else { + log.Println("[miner] We already had this transaction, ignoring") + } + } + default: + log.Println("[miner] Mining on block. Includes", len(miner.txs), "transactions") + + // Apply uncles + if len(miner.uncles) > 0 { + miner.block.SetUncles(miner.uncles) + } + + // Apply all transactions to the block + miner.ethereum.StateManager().ApplyTransactions(miner.block, miner.block.Transactions()) + miner.ethereum.StateManager().AccumelateRewards(miner.block) + + // Search the nonce + log.Println("[miner] Initialision complete, starting mining") + miner.block.Nonce = miner.pow.Search(miner.block, miner.quitChan) + if miner.block.Nonce != nil { + miner.ethereum.StateManager().PrepareDefault(miner.block) + err := miner.ethereum.StateManager().ProcessBlock(miner.block, true) + if err != nil { + log.Println("Error result from process block:", err) + log.Println(miner.block) + } else { + + if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) { + log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce)) + } + miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val}) + log.Printf("[miner] 🔨 Mined block %x\n", miner.block.Hash()) + log.Println(miner.block) + + miner.txs = []*ethchain.Transaction{} // Move this somewhere neat + miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) + } + } + } + } +} diff --git a/peer.go b/peer.go index 4e927ada4..6b914710d 100644 --- a/peer.go +++ b/peer.go @@ -295,7 +295,7 @@ func (p *Peer) HandleInbound() { block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i)) p.ethereum.StateManager().PrepareDefault(block) - err = p.ethereum.StateManager().ProcessBlock(block) + err = p.ethereum.StateManager().ProcessBlock(block, true) if err != nil { if ethutil.Config.Debug { From b52b1fca89fd56549ecc0f086d96a39d6009e568 Mon Sep 17 00:00:00 2001 From: Maran Date: Fri, 21 Mar 2014 15:06:23 +0100 Subject: [PATCH 005/100] Initial block reorganisation code --- ethchain/block_chain.go | 97 ++++++++++++++++++++++++++++++ ethchain/state_manager.go | 4 +- ethminer/miner.go | 19 +++--- peer.go | 121 +++++++++++++++++++++++++++++++------- 4 files changed, 210 insertions(+), 31 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 90ad4516a..6eea14652 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -3,6 +3,7 @@ package ethchain import ( "bytes" "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/eth-go/ethwire" "log" "math" "math/big" @@ -24,6 +25,7 @@ type BlockChain struct { func NewBlockChain(ethereum EthManager) *BlockChain { bc := &BlockChain{} bc.genesisBlock = NewBlockFromData(ethutil.Encode(Genesis)) + bc.Ethereum = ethereum bc.setLastBlock() @@ -77,6 +79,101 @@ func (bc *BlockChain) HasBlock(hash []byte) bool { return len(data) != 0 } +// TODO: At one point we might want to save a block by prevHash in the db to optimise this... +func (bc *BlockChain) HasBlockWithPrevHash(hash []byte) bool { + block := bc.CurrentBlock + + for ; block != nil; block = bc.GetBlock(block.PrevHash) { + if bytes.Compare(hash, block.PrevHash) == 0 { + return true + } + } + return false +} + +func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int { + blockDiff := new(big.Int) + + for _, uncle := range block.Uncles { + blockDiff = blockDiff.Add(blockDiff, uncle.Difficulty) + } + blockDiff = blockDiff.Add(blockDiff, block.Difficulty) + + return blockDiff +} + +// Is tasked by finding the CanonicalChain and resetting the chain if we are not the Conical one +func (bc *BlockChain) FindCanonicalChain(msg *ethwire.Msg, commonBlockHash []byte) { + // 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 i := 0; i < (msg.Data.Len() - 1); i++ { + block := NewBlockFromRlpValue(msg.Data.Get(i)) + if bytes.Compare(block.Hash(), commonBlockHash) == 0 { + log.Println("[BCHAIN] We have found the common parent block, breaking") + break + } + chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block)) + } + + log.Println("[BCHAIN] Incoming chain difficulty:", chainDifficulty) + + curChainDifficulty := new(big.Int) + block := bc.CurrentBlock + + for ; block != nil; block = bc.GetBlock(block.PrevHash) { + if bytes.Compare(block.Hash(), commonBlockHash) == 0 { + log.Println("[BCHAIN] We have found the common parent block, breaking") + break + } + curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block)) + } + + log.Println("[BCHAIN] Current chain difficulty:", curChainDifficulty) + if chainDifficulty.Cmp(curChainDifficulty) == 1 { + log.Println("[BCHAIN] The incoming Chain beat our asses, resetting") + bc.ResetTillBlockHash(commonBlockHash) + } else { + log.Println("[BCHAIN] Our chain showed the incoming chain who is boss. Ignoring.") + } +} +func (bc *BlockChain) ResetTillBlockHash(hash []byte) error { + lastBlock := bc.CurrentBlock + returnTo := bc.GetBlock(hash) + + // TODO: REFACTOR TO FUNCTION, Used multiple times + bc.CurrentBlock = returnTo + bc.LastBlockHash = returnTo.Hash() + info := bc.BlockInfo(returnTo) + bc.LastBlockNumber = info.Number + // END TODO + + bc.Ethereum.StateManager().PrepareDefault(returnTo) + 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 { + log.Println("[CHAIN] We have arrived at the the common parent block, breaking") + break + } + err = ethutil.Config.Db.Delete(block.Hash()) + if err != nil { + return err + } + } + log.Println("[CHAIN] Split chain deleted and reverted to common parent block.") + return nil +} + func (bc *BlockChain) GenesisBlock() *Block { return bc.genesisBlock } diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 46d8228d9..9118db211 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -201,7 +201,6 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { return nil } - func (sm *StateManager) CalculateTD(block *Block) bool { uncleDiff := new(big.Int) for _, uncle := range block.Uncles { @@ -215,6 +214,9 @@ func (sm *StateManager) CalculateTD(block *Block) bool { // The new TD will only be accepted if the new difficulty is // is greater than the previous. + fmt.Println("new block td:", td) + fmt.Println("cur block td:", sm.bc.TD) + if td.Cmp(sm.bc.TD) > 0 { // Set the new total difficulty back to the block chain sm.bc.SetTotalDifficulty(td) diff --git a/ethminer/miner.go b/ethminer/miner.go index f4f697aba..cb752e3de 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -61,10 +61,10 @@ func (miner *Miner) listener() { select { case chanMessage := <-miner.reactChan: if block, ok := chanMessage.Resource.(*ethchain.Block); ok { - log.Println("[miner] Got new block via Reactor") + log.Println("[MINER] Got new block via Reactor") if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 { // TODO: Perhaps continue mining to get some uncle rewards - log.Println("[miner] New top block found resetting state") + log.Println("[MINER] New top block found resetting state") // Filter out which Transactions we have that were not in this block var newtxs []*ethchain.Transaction @@ -86,7 +86,7 @@ func (miner *Miner) listener() { } else { if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 { - log.Println("[miner] Adding uncle block") + log.Println("[MINER] Adding uncle block") miner.uncles = append(miner.uncles, block) miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State()) } @@ -94,7 +94,7 @@ func (miner *Miner) listener() { } if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok { - log.Println("[miner] Got new transaction from Reactor", tx) + log.Println("[MINER] Got new transaction from Reactor", tx) found := false for _, ctx := range miner.txs { if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found { @@ -103,15 +103,15 @@ func (miner *Miner) listener() { } if found == false { - log.Println("[miner] We did not know about this transaction, adding") + log.Println("[MINER] We did not know about this transaction, adding") miner.txs = append(miner.txs, tx) miner.block.SetTransactions(miner.txs) } else { - log.Println("[miner] We already had this transaction, ignoring") + log.Println("[MINER] We already had this transaction, ignoring") } } default: - log.Println("[miner] Mining on block. Includes", len(miner.txs), "transactions") + log.Println("[MINER] Mining on block. Includes", len(miner.txs), "transactions") // Apply uncles if len(miner.uncles) > 0 { @@ -123,7 +123,7 @@ func (miner *Miner) listener() { miner.ethereum.StateManager().AccumelateRewards(miner.block) // Search the nonce - log.Println("[miner] Initialision complete, starting mining") + log.Println("[MINER] Initialision complete, starting mining") miner.block.Nonce = miner.pow.Search(miner.block, miner.quitChan) if miner.block.Nonce != nil { miner.ethereum.StateManager().PrepareDefault(miner.block) @@ -137,8 +137,7 @@ func (miner *Miner) listener() { log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce)) } miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val}) - log.Printf("[miner] 🔨 Mined block %x\n", miner.block.Hash()) - log.Println(miner.block) + log.Printf("[MINER] 🔨 Mined block %x\n", miner.block.Hash()) miner.txs = []*ethchain.Transaction{} // Move this somewhere neat miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) diff --git a/peer.go b/peer.go index 6b914710d..2cc940400 100644 --- a/peer.go +++ b/peer.go @@ -126,7 +126,8 @@ type Peer struct { pubkey []byte // Indicated whether the node is catching up or not - catchingUp bool + catchingUp bool + blocksRequested int Version string } @@ -136,15 +137,16 @@ func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer { pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes() return &Peer{ - outputQueue: make(chan *ethwire.Msg, outputBufferSize), - quit: make(chan bool), - ethereum: ethereum, - conn: conn, - inbound: inbound, - disconnect: 0, - connected: 1, - port: 30303, - pubkey: pubkey, + outputQueue: make(chan *ethwire.Msg, outputBufferSize), + quit: make(chan bool), + ethereum: ethereum, + conn: conn, + inbound: inbound, + disconnect: 0, + connected: 1, + port: 30303, + pubkey: pubkey, + blocksRequested: 10, } } @@ -291,11 +293,62 @@ func (p *Peer) HandleInbound() { // Get all blocks and process them var block, lastBlock *ethchain.Block var err error + + // 1. Compare the first block over the wire's prev-hash with the hash of your last block + // 2. If these two values are the same you can just link the chains together. + // [1:0,2:1,3:2] <- Current blocks (format block:previous_block) + // [1:0,2:1,3:2,4:3,5:4] <- incoming blocks + // == [1,2,3,4,5] + // 3. If the values are not the same we will have to go back and calculate the chain with the highest total difficulty + // [1:0,2:1,3:2,11:3,12:11,13:12] + // [1:0,2:1,3:2,4:3,5:4,6:5] + + // [3:2,11:3,12:11,13:12] + // [3:2,4:3,5:4,6:5] + // Heb ik dit blok? + // Nee: heb ik een blok met PrevHash 3? + // Ja: DIVERSION + // Nee; Adding to chain + + // See if we can find a common ancestor + // 1. Get the earliest block in the package. + // 2. Do we have this block? + // 3. Yes: Let's continue what we are doing + // 4. No: Let's request more blocks back. + + if msg.Data.Len()-1 > 1 { + lastBlock = ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len() - 1)) + if p.ethereum.StateManager().BlockChain().HasBlock(lastBlock.Hash()) { + fmt.Println("[PEER] We found a common ancestor, let's continue.") + } else { + fmt.Println("[PEER] No common ancestor found, requesting more blocks.") + p.blocksRequested = p.blocksRequested * 2 + p.catchingUp = false + p.SyncWithBlocks() + } + + for i := msg.Data.Len() - 1; i >= 0; i-- { + block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i)) + // Do we have this block on our chain? + if p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) { + fmt.Println("[PEER] Block found, checking next one.") + } else { + // We don't have this block, but we do have a block with the same prevHash, diversion time! + if p.ethereum.StateManager().BlockChain().HasBlockWithPrevHash(block.PrevHash) { + fmt.Printf("[PEER] Local and foreign chain have diverted after %x, we are going to get freaky with it!\n", block.PrevHash) + p.ethereum.StateManager().BlockChain().FindCanonicalChain(msg, block.PrevHash) + } else { + fmt.Println("[PEER] Both local and foreign chain have same parent. Continue normally") + } + } + } + } + for i := msg.Data.Len() - 1; i >= 0; i-- { block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i)) p.ethereum.StateManager().PrepareDefault(block) - err = p.ethereum.StateManager().ProcessBlock(block, true) + err = p.ethereum.StateManager().ProcessBlock(block, false) if err != nil { if ethutil.Config.Debug { @@ -305,6 +358,7 @@ func (p *Peer) HandleInbound() { } break } else { + ethutil.Config.Log.Infof("[PEER] Block %x added\n", block.Hash()) lastBlock = block } } @@ -314,7 +368,7 @@ func (p *Peer) HandleInbound() { if ethchain.IsParentErr(err) { ethutil.Config.Log.Infoln("Attempting to catch up") p.catchingUp = false - p.CatchupWithPeer() + p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash()) } else if ethchain.IsValidationErr(err) { // TODO } @@ -327,7 +381,7 @@ func (p *Peer) HandleInbound() { ethutil.Config.Log.Infof("Synced to block height #%d %x %x\n", blockInfo.Number, lastBlock.Hash(), blockInfo.Hash) } p.catchingUp = false - p.CatchupWithPeer() + p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash()) } } case ethwire.MsgTxTy: @@ -374,11 +428,11 @@ func (p *Peer) HandleInbound() { // Amount of parents in the canonical chain //amountOfBlocks := msg.Data.Get(l).AsUint() amountOfBlocks := uint64(100) + // Check each SHA block hash from the message and determine whether // the SHA is in the database for i := 0; i < l; i++ { - if data := - msg.Data.Get(i).Bytes(); p.ethereum.StateManager().BlockChain().HasBlock(data) { + if data := msg.Data.Get(i).Bytes(); p.ethereum.StateManager().BlockChain().HasBlock(data) { parent = p.ethereum.BlockChain().GetBlock(data) break } @@ -386,9 +440,12 @@ func (p *Peer) HandleInbound() { // If a parent is found send back a reply if parent != nil { + ethutil.Config.Log.Infof("[PEER] Found conical block, returning chain from: %x ", parent.Hash()) chain := p.ethereum.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks) + ethutil.Config.Log.Infof("[PEER] Returning %d blocks: %x ", len(chain), parent.Hash()) p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain)) } else { + ethutil.Config.Log.Infof("[PEER] Could not find a similar block") // If no blocks are found we send back a reply with msg not in chain // and the last hash from get chain lastHash := msg.Data.Get(l - 1) @@ -527,7 +584,8 @@ func (p *Peer) handleHandshake(msg *ethwire.Msg) { } // Catch up with the connected peer - p.CatchupWithPeer() + // p.CatchupWithPeer(p.ethereum.BlockChain().CurrentBlock.Hash()) + p.SyncWithBlocks() // Set the peer's caps p.caps = Caps(c.Get(3).Byte()) @@ -554,14 +612,37 @@ 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) CatchupWithPeer() { +func (p *Peer) SyncWithBlocks() { if !p.catchingUp { p.catchingUp = true - msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{p.ethereum.BlockChain().CurrentBlock.Hash(), uint64(50)}) + // FIXME: THIS SHOULD NOT BE NEEDED + if p.blocksRequested == 0 { + p.blocksRequested = 10 + } + fmt.Printf("Currenb lock %x\n", p.ethereum.BlockChain().CurrentBlock.Hash()) + fmt.Println("Amount:", p.blocksRequested) + blocks := p.ethereum.BlockChain().GetChain(p.ethereum.BlockChain().CurrentBlock.Hash(), p.blocksRequested) + + var hashes []interface{} + for _, block := range blocks { + hashes = append(hashes, block.Hash()) + } + fmt.Printf("Requesting hashes from network: %x", hashes) + + msgInfo := append(hashes, uint64(50)) + + msg := ethwire.NewMessage(ethwire.MsgGetChainTy, msgInfo) + p.QueueMessage(msg) + } +} + +func (p *Peer) CatchupWithPeer(blockHash []byte) { + if !p.catchingUp { + p.catchingUp = true + msg := ethwire.NewMessage(ethwire.MsgGetChainTy, []interface{}{blockHash, uint64(50)}) p.QueueMessage(msg) - ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4]) + ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", blockHash[:4]) } } From 274d5cc91c45349ec8d7a1f5a20ef29896b38b2e Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 24 Mar 2014 10:24:06 +0100 Subject: [PATCH 006/100] FindCanonicalChain returns true or false when we are on the Canonical chain or not --- ethchain/block_chain.go | 5 ++++- peer.go | 14 +++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 6eea14652..f25f0ca5a 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -103,7 +103,8 @@ func (bc *BlockChain) CalculateBlockTD(block *Block) *big.Int { } // Is tasked by finding the CanonicalChain and resetting the chain if we are not the Conical one -func (bc *BlockChain) FindCanonicalChain(msg *ethwire.Msg, commonBlockHash []byte) { +// Return true if we are the using the canonical chain false if not +func (bc *BlockChain) FindCanonicalChain(msg *ethwire.Msg, commonBlockHash []byte) bool { // 1. Calculate TD of the current chain // 2. Calculate TD of the new chain // Reset state to the correct one @@ -138,8 +139,10 @@ func (bc *BlockChain) FindCanonicalChain(msg *ethwire.Msg, commonBlockHash []byt if chainDifficulty.Cmp(curChainDifficulty) == 1 { log.Println("[BCHAIN] The incoming Chain beat our asses, resetting") bc.ResetTillBlockHash(commonBlockHash) + return false } else { log.Println("[BCHAIN] Our chain showed the incoming chain who is boss. Ignoring.") + return true } } func (bc *BlockChain) ResetTillBlockHash(hash []byte) error { diff --git a/peer.go b/peer.go index 2cc940400..22bbe7a4c 100644 --- a/peer.go +++ b/peer.go @@ -316,11 +316,18 @@ func (p *Peer) HandleInbound() { // 3. Yes: Let's continue what we are doing // 4. No: Let's request more blocks back. + // Make sure we are actually receiving anything if msg.Data.Len()-1 > 1 { + // We requested blocks and now we need to make sure we have a common ancestor somewhere in these blocks so we can find + // common ground to start syncing from lastBlock = ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len() - 1)) if p.ethereum.StateManager().BlockChain().HasBlock(lastBlock.Hash()) { fmt.Println("[PEER] We found a common ancestor, let's continue.") } else { + + // If we can't find a common ancenstor we need to request more blocks. + // FIXME: At one point this won't scale anymore since we are not asking for an offset + // we just keep increasing the amount of blocks. fmt.Println("[PEER] No common ancestor found, requesting more blocks.") p.blocksRequested = p.blocksRequested * 2 p.catchingUp = false @@ -329,14 +336,16 @@ func (p *Peer) HandleInbound() { for i := msg.Data.Len() - 1; i >= 0; i-- { block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i)) - // Do we have this block on our chain? + // Do we have this block on our chain? If so we can continue if p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) { fmt.Println("[PEER] Block found, checking next one.") } else { // We don't have this block, but we do have a block with the same prevHash, diversion time! if p.ethereum.StateManager().BlockChain().HasBlockWithPrevHash(block.PrevHash) { fmt.Printf("[PEER] Local and foreign chain have diverted after %x, we are going to get freaky with it!\n", block.PrevHash) - p.ethereum.StateManager().BlockChain().FindCanonicalChain(msg, block.PrevHash) + if p.ethereum.StateManager().BlockChain().FindCanonicalChain(msg, block.PrevHash) { + return + } } else { fmt.Println("[PEER] Both local and foreign chain have same parent. Continue normally") } @@ -358,7 +367,6 @@ func (p *Peer) HandleInbound() { } break } else { - ethutil.Config.Log.Infof("[PEER] Block %x added\n", block.Hash()) lastBlock = block } } From ec6ec62dd4f3c4132c79b33fc20467ba98c16f10 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 24 Mar 2014 10:56:52 +0100 Subject: [PATCH 007/100] Remove some xtra logs --- ethchain/dagger.go | 4 ++-- ethchain/state_manager.go | 3 --- peer.go | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/ethchain/dagger.go b/ethchain/dagger.go index a80a9d421..9d2df4069 100644 --- a/ethchain/dagger.go +++ b/ethchain/dagger.go @@ -29,14 +29,14 @@ func (pow *EasyPow) Search(block *Block, reactChan chan ethutil.React) []byte { for { select { case <-reactChan: - log.Println("[pow] Received reactor event; breaking out.") + log.Println("[POW] Received reactor event; breaking out.") return nil default: i++ if i%1234567 == 0 { elapsed := time.Now().UnixNano() - start hashes := ((float64(1e9) / float64(elapsed)) * float64(i)) / 1000 - log.Println("Hashing @", int64(hashes), "khash") + log.Println("[POW] Hashing @", int64(hashes), "khash") } sha := ethutil.Sha3Bin(big.NewInt(r.Int63()).Bytes()) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 5692a1d88..d907141a4 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -214,9 +214,6 @@ func (sm *StateManager) CalculateTD(block *Block) bool { // The new TD will only be accepted if the new difficulty is // is greater than the previous. - fmt.Println("new block td:", td) - fmt.Println("cur block td:", sm.bc.TD) - if td.Cmp(sm.bc.TD) > 0 { // Set the new total difficulty back to the block chain sm.bc.SetTotalDifficulty(td) diff --git a/peer.go b/peer.go index b8e75c686..63059bcfb 100644 --- a/peer.go +++ b/peer.go @@ -316,7 +316,7 @@ func (p *Peer) HandleInbound() { // 4. No: Let's request more blocks back. // Make sure we are actually receiving anything - if msg.Data.Len()-1 > 1 { + if msg.Data.Len()-1 > 1 && p.catchingUp { // We requested blocks and now we need to make sure we have a common ancestor somewhere in these blocks so we can find // common ground to start syncing from lastBlock = ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len() - 1)) From e0b6091d7ef709902f534c1a4b57151f0171e03c Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 24 Mar 2014 13:20:34 +0100 Subject: [PATCH 008/100] Test fixes and removed old code. Added VM gas fees --- ethchain/stack.go | 8 +-- ethchain/vm.go | 48 +++++++++++++- ethchain/vm_test.go | 147 +++++++++++++------------------------------ ethutil/common.go | 1 + ethutil/parsing.go | 2 +- ethutil/rlp_test.go | 6 ++ ethutil/trie_test.go | 2 +- 7 files changed, 103 insertions(+), 111 deletions(-) diff --git a/ethchain/stack.go b/ethchain/stack.go index 3c2899e62..57165c432 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -202,7 +202,7 @@ func (st *Stack) Push(d *big.Int) { st.data = append(st.data, d) } func (st *Stack) Print() { - fmt.Println("### STACK ###") + fmt.Println("### stack ###") if len(st.data) > 0 { for i, val := range st.data { fmt.Printf("%-3d %v\n", i, val) @@ -242,15 +242,15 @@ func (m *Memory) Len() int { } func (m *Memory) Print() { - fmt.Println("### MEM ###") + fmt.Printf("### mem %d bytes ###\n", len(m.store)) if len(m.store) > 0 { addr := 0 - for i := 0; i+32 < len(m.store); i += 32 { + for i := 0; i+32 <= len(m.store); i += 32 { fmt.Printf("%03d %v\n", addr, m.store[i:i+32]) addr++ } } else { fmt.Println("-- empty --") } - fmt.Println("###########") + fmt.Println("####################") } diff --git a/ethchain/vm.go b/ethchain/vm.go index 126592b25..9b6807925 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -10,6 +10,17 @@ import ( "math/big" ) +var ( + GasStep = big.NewInt(1) + GasSha = big.NewInt(20) + GasSLoad = big.NewInt(20) + GasSStore = big.NewInt(100) + GasBalance = big.NewInt(20) + GasCreate = big.NewInt(100) + GasCall = big.NewInt(20) + GasMemory = big.NewInt(1) +) + type Vm struct { txPool *TxPool // Stack for processing contracts @@ -70,10 +81,41 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { } // TODO Get each instruction cost properly - fee := new(big.Int) - fee.Add(fee, big.NewInt(1000)) + gas := new(big.Int) + useGas := func(amount *big.Int) { + gas.Add(gas, amount) + } - if closure.Gas.Cmp(fee) < 0 { + switch op { + case oSHA3: + useGas(GasSha) + case oSLOAD: + useGas(GasSLoad) + case oSSTORE: + var mult *big.Int + y, x := stack.Peekn() + val := closure.GetMem(x) + if val.IsEmpty() && len(y.Bytes()) > 0 { + mult = ethutil.Big2 + } else if !val.IsEmpty() && len(y.Bytes()) == 0 { + mult = ethutil.Big0 + } else { + mult = ethutil.Big1 + } + useGas(base.Mul(mult, GasSStore)) + case oBALANCE: + useGas(GasBalance) + case oCREATE: + useGas(GasCreate) + case oCALL: + useGas(GasCall) + case oMLOAD, oMSIZE, oMSTORE8, oMSTORE: + useGas(GasMemory) + default: + useGas(GasStep) + } + + if closure.Gas.Cmp(gas) < 0 { return closure.Return(nil) } diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 047531e09..308a65432 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -2,113 +2,15 @@ package ethchain import ( "bytes" + "fmt" "github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethutil" + "github.com/obscuren/mutan" "math/big" + "strings" "testing" ) -/* - -func TestRun(t *testing.T) { - InitFees() - - ethutil.ReadConfig("") - - db, _ := ethdb.NewMemDatabase() - state := NewState(ethutil.NewTrie(db, "")) - - script := Compile([]string{ - "TXSENDER", - "SUICIDE", - }) - - tx := NewTransaction(ContractAddr, big.NewInt(1e17), script) - fmt.Printf("contract addr %x\n", tx.Hash()[12:]) - contract := MakeContract(tx, state) - vm := &Vm{} - - vm.Process(contract, state, RuntimeVars{ - address: tx.Hash()[12:], - blockNumber: 1, - sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), - prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), - coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), - time: 1, - diff: big.NewInt(256), - txValue: tx.Value, - txData: tx.Data, - }) -} - -func TestRun1(t *testing.T) { - ethutil.ReadConfig("") - - db, _ := ethdb.NewMemDatabase() - state := NewState(ethutil.NewTrie(db, "")) - - script := Compile([]string{ - "PUSH", "0", - "PUSH", "0", - "TXSENDER", - "PUSH", "10000000", - "MKTX", - }) - fmt.Println(ethutil.NewValue(script)) - - tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) - fmt.Printf("contract addr %x\n", tx.Hash()[12:]) - contract := MakeContract(tx, state) - vm := &Vm{} - - vm.Process(contract, state, RuntimeVars{ - address: tx.Hash()[12:], - blockNumber: 1, - sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), - prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), - coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), - time: 1, - diff: big.NewInt(256), - txValue: tx.Value, - txData: tx.Data, - }) -} - -func TestRun2(t *testing.T) { - ethutil.ReadConfig("") - - db, _ := ethdb.NewMemDatabase() - state := NewState(ethutil.NewTrie(db, "")) - - script := Compile([]string{ - "PUSH", "0", - "PUSH", "0", - "TXSENDER", - "PUSH", "10000000", - "MKTX", - }) - fmt.Println(ethutil.NewValue(script)) - - tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) - fmt.Printf("contract addr %x\n", tx.Hash()[12:]) - contract := MakeContract(tx, state) - vm := &Vm{} - - vm.Process(contract, state, RuntimeVars{ - address: tx.Hash()[12:], - blockNumber: 1, - sender: ethutil.FromHex("cd1722f3947def4cf144679da39c4c32bdc35681"), - prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), - coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), - time: 1, - diff: big.NewInt(256), - txValue: tx.Value, - txData: tx.Data, - }) -} -*/ - -// XXX Full stack test func TestRun3(t *testing.T) { ethutil.ReadConfig("") @@ -132,7 +34,7 @@ func TestRun3(t *testing.T) { contract := MakeContract(tx, state) state.UpdateContract(contract) - callerScript := ethutil.Compile( + callerScript := ethutil.Assemble( "PUSH", 1337, // Argument "PUSH", 65, // argument mem offset "MSTORE", @@ -172,3 +74,44 @@ func TestRun3(t *testing.T) { t.Errorf("expected return value to be %v, got %v", exp, ret) } } + +func TestRun4(t *testing.T) { + ethutil.ReadConfig("") + + db, _ := ethdb.NewMemDatabase() + state := NewState(ethutil.NewTrie(db, "")) + + mutan.NewCompiler().Compile(strings.NewReader(` +a = 1337 +c = 1 +[0] = 50 +d = [0] +`)) + + asm := mutan.NewCompiler().Compile(strings.NewReader(` + a = 3 + 3 + [1000] = a + [1000] +`)) + asm = append(asm, "LOG") + fmt.Println(asm) + + callerScript := ethutil.Assemble(asm...) + callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) + + // Contract addr as test address + account := NewAccount(ContractAddr, big.NewInt(10000000)) + callerClosure := NewClosure(account, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int)) + + vm := NewVm(state, RuntimeVars{ + origin: account.Address(), + blockNumber: 1, + prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), + coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + time: 1, + diff: big.NewInt(256), + // XXX Tx data? Could be just an argument to the closure instead + txData: nil, + }) + callerClosure.Call(vm, nil) +} diff --git a/ethutil/common.go b/ethutil/common.go index f15b10e6d..c63af29a6 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -36,6 +36,7 @@ func CurrencyToString(num *big.Int) string { var ( Big1 = big.NewInt(1) + Big2 = big.NewInt(1) Big0 = big.NewInt(0) Big256 = big.NewInt(0xff) ) diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 8929f0829..16ed2d06d 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -131,7 +131,7 @@ func Instr(instr string) (int, []string, error) { // Script compilation functions // Compiles strings to machine code -func Compile(instructions ...interface{}) (script []string) { +func Assemble(instructions ...interface{}) (script []string) { script = make([]string, len(instructions)) for i, val := range instructions { diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go index 2a58bfc0f..dc10db632 100644 --- a/ethutil/rlp_test.go +++ b/ethutil/rlp_test.go @@ -2,6 +2,7 @@ package ethutil import ( "bytes" + "fmt" "math/big" "reflect" "testing" @@ -119,6 +120,11 @@ func TestEncodeDecodeBytes(t *testing.T) { } } +func TestEncodeZero(t *testing.T) { + b := NewValue(0).Encode() + fmt.Println(b) +} + func BenchmarkEncodeDecode(b *testing.B) { for i := 0; i < b.N; i++ { bytes := Encode([]interface{}{"dog", "god", "cat"}) diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index 79e5de921..0be512d9f 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -1,7 +1,7 @@ package ethutil import ( - "fmt" + _ "fmt" "reflect" "testing" ) From 6253d109389d49e47772597de24cd11874b91338 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 24 Mar 2014 15:04:29 +0100 Subject: [PATCH 009/100] initial testcode for canonical chain --- ethchain/block_chain.go | 35 +++++++++----- ethchain/block_chain_test.go | 92 ++++++++++++++++++++++++++++++++++++ ethchain/state_manager.go | 3 ++ ethminer/miner.go | 1 + peer.go | 2 +- 5 files changed, 119 insertions(+), 14 deletions(-) create mode 100644 ethchain/block_chain_test.go diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index f25f0ca5a..0e3601a4b 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -24,7 +24,7 @@ type BlockChain struct { func NewBlockChain(ethereum EthManager) *BlockChain { bc := &BlockChain{} - bc.genesisBlock = NewBlockFromData(ethutil.Encode(Genesis)) + bc.genesisBlock = NewBlockFromBytes(ethutil.Encode(Genesis)) bc.Ethereum = ethereum bc.setLastBlock() @@ -101,10 +101,18 @@ 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(msg *ethwire.Msg, commonBlockHash []byte) bool { +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 @@ -113,35 +121,35 @@ func (bc *BlockChain) FindCanonicalChain(msg *ethwire.Msg, commonBlockHash []byt // 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 i := 0; i < (msg.Data.Len() - 1); i++ { - block := NewBlockFromRlpValue(msg.Data.Get(i)) + for _, block := range blocks { if bytes.Compare(block.Hash(), commonBlockHash) == 0 { - log.Println("[BCHAIN] We have found the common parent block, breaking") + log.Println("[CHAIN] We have found the common parent block, breaking") break } chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block)) } - log.Println("[BCHAIN] Incoming chain difficulty:", chainDifficulty) + log.Println("[CHAIN] Incoming chain difficulty:", chainDifficulty) curChainDifficulty := new(big.Int) block := bc.CurrentBlock - - for ; block != nil; block = bc.GetBlock(block.PrevHash) { + for i := 0; block != nil; block = bc.GetBlock(block.PrevHash) { + i++ if bytes.Compare(block.Hash(), commonBlockHash) == 0 { - log.Println("[BCHAIN] We have found the common parent block, breaking") + log.Println("[CHAIN] We have found the common parent block, breaking") break } + log.Println("CHECKING BLOGK:", i) curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block)) } - log.Println("[BCHAIN] Current chain difficulty:", curChainDifficulty) + log.Println("[CHAIN] Current chain difficulty:", curChainDifficulty) if chainDifficulty.Cmp(curChainDifficulty) == 1 { - log.Println("[BCHAIN] The incoming Chain beat our asses, resetting") + log.Println("[CHAIN] The incoming Chain beat our asses, resetting") bc.ResetTillBlockHash(commonBlockHash) return false } else { - log.Println("[BCHAIN] Our chain showed the incoming chain who is boss. Ignoring.") + log.Println("[CHAIN] Our chain showed the incoming chain who is boss. Ignoring.") return true } } @@ -286,6 +294,7 @@ func (bc *BlockChain) Add(block *Block) { bc.LastBlockHash = block.Hash() encodedBlock := block.RlpEncode() + log.Println(encodedBlock) ethutil.Config.Db.Put(block.Hash(), encodedBlock) ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock) } @@ -296,7 +305,7 @@ func (bc *BlockChain) GetBlock(hash []byte) *Block { return nil } - return NewBlockFromData(data) + return NewBlockFromBytes(data) } func (bc *BlockChain) BlockInfoByHash(hash []byte) BlockInfo { diff --git a/ethchain/block_chain_test.go b/ethchain/block_chain_test.go new file mode 100644 index 000000000..736247e83 --- /dev/null +++ b/ethchain/block_chain_test.go @@ -0,0 +1,92 @@ +package ethchain + +import ( + "fmt" + "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/ethutil" + "github.com/ethereum/eth-go/ethwire" + "testing" +) + +// Implement our EthTest Manager +type TestManager struct { + stateManager *StateManager + reactor *ethutil.ReactorEngine + + txPool *TxPool + blockChain *BlockChain + Blocks []*Block +} + +func (s *TestManager) BlockChain() *BlockChain { + return s.blockChain +} + +func (tm *TestManager) TxPool() *TxPool { + return tm.txPool +} + +func (tm *TestManager) StateManager() *StateManager { + return tm.stateManager +} + +func (tm *TestManager) Reactor() *ethutil.ReactorEngine { + return tm.reactor +} +func (tm *TestManager) Broadcast(msgType ethwire.MsgType, data []interface{}) { + fmt.Println("Broadcast not implemented") +} + +func NewTestManager() *TestManager { + ethutil.ReadConfig(".ethtest") + + db, err := ethdb.NewMemDatabase() + if err != nil { + fmt.Println("Could not create mem-db, failing") + return nil + } + ethutil.Config.Db = db + + testManager := &TestManager{} + testManager.reactor = ethutil.NewReactorEngine() + + testManager.txPool = NewTxPool(testManager) + testManager.blockChain = NewBlockChain(testManager) + testManager.stateManager = NewStateManager(testManager) + + // Start the tx pool + testManager.txPool.Start() + + return testManager +} +func (tm *TestManager) AddFakeBlock(blk []byte) error { + block := NewBlockFromBytes(blk) + tm.Blocks = append(tm.Blocks, block) + tm.StateManager().PrepareDefault(block) + err := tm.StateManager().ProcessBlock(block, false) + return err +} +func (tm *TestManager) CreateChain1() error { + err := tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 58, 253, 98, 206, 198, 181, 152, 223, 201, 116, 197, 154, 111, 104, 54, 113, 249, 184, 246, 15, 226, 142, 187, 47, 138, 60, 201, 66, 226, 237, 29, 7, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 103, 109, 19, 120, 219, 91, 248, 48, 204, 17, 28, 7, 146, 72, 203, 15, 207, 251, 31, 216, 138, 26, 59, 34, 238, 40, 114, 233, 1, 13, 207, 90, 71, 136, 124, 86, 196, 127, 10, 176, 193, 154, 165, 76, 155, 154, 59, 45, 34, 96, 183, 212, 99, 41, 27, 40, 119, 171, 231, 160, 114, 56, 218, 173, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 240, 0, 132, 83, 48, 32, 251, 128, 160, 4, 10, 11, 225, 132, 86, 146, 227, 229, 137, 164, 245, 16, 139, 219, 12, 251, 178, 154, 168, 210, 18, 84, 40, 250, 41, 124, 92, 169, 242, 246, 180, 192, 192}) + err = tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 222, 229, 152, 228, 200, 163, 244, 144, 120, 18, 203, 253, 195, 185, 105, 131, 163, 226, 116, 40, 140, 68, 249, 198, 221, 152, 121, 0, 124, 11, 180, 125, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 103, 109, 19, 120, 219, 91, 248, 48, 204, 17, 28, 7, 146, 72, 203, 15, 207, 251, 31, 216, 138, 26, 59, 34, 238, 40, 114, 233, 1, 13, 207, 90, 71, 136, 124, 86, 196, 127, 10, 176, 193, 154, 165, 76, 155, 154, 59, 45, 34, 96, 183, 212, 99, 41, 27, 40, 119, 171, 231, 160, 114, 56, 218, 173, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 224, 4, 132, 83, 48, 36, 250, 128, 160, 79, 58, 51, 246, 238, 249, 210, 253, 136, 83, 71, 134, 49, 114, 190, 189, 242, 78, 100, 238, 101, 84, 204, 176, 198, 25, 139, 151, 60, 84, 51, 126, 192, 192}) + err = tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 68, 52, 33, 210, 160, 189, 217, 255, 78, 37, 196, 217, 94, 247, 166, 169, 224, 199, 102, 110, 85, 213, 45, 13, 173, 106, 4, 103, 151, 195, 38, 86, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 103, 109, 19, 120, 219, 91, 248, 48, 204, 17, 28, 7, 146, 72, 203, 15, 207, 251, 31, 216, 138, 26, 59, 34, 238, 40, 114, 233, 1, 13, 207, 90, 71, 136, 124, 86, 196, 127, 10, 176, 193, 154, 165, 76, 155, 154, 59, 45, 34, 96, 183, 212, 99, 41, 27, 40, 119, 171, 231, 160, 114, 56, 218, 173, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 208, 12, 132, 83, 48, 38, 206, 128, 160, 65, 147, 32, 128, 177, 198, 131, 57, 57, 68, 135, 65, 198, 178, 138, 43, 25, 135, 92, 174, 208, 119, 103, 225, 26, 207, 243, 31, 225, 29, 173, 119, 192, 192}) + return err +} +func (tm *TestManager) CreateChain2() error { + err := tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 58, 253, 98, 206, 198, 181, 152, 223, 201, 116, 197, 154, 111, 104, 54, 113, 249, 184, 246, 15, 226, 142, 187, 47, 138, 60, 201, 66, 226, 237, 29, 7, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 72, 201, 77, 81, 160, 103, 70, 18, 102, 204, 82, 192, 86, 157, 40, 30, 117, 218, 224, 202, 1, 36, 249, 88, 82, 210, 19, 156, 112, 31, 13, 117, 227, 0, 125, 221, 190, 165, 16, 193, 163, 161, 175, 33, 37, 184, 235, 62, 201, 93, 102, 185, 143, 54, 146, 114, 30, 253, 178, 245, 87, 38, 191, 214, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 240, 0, 132, 83, 48, 40, 35, 128, 160, 162, 214, 119, 207, 212, 186, 64, 47, 14, 186, 98, 118, 203, 79, 172, 205, 33, 206, 225, 177, 225, 194, 98, 188, 63, 219, 13, 151, 47, 32, 204, 27, 192, 192}) + err = tm.AddFakeBlock([]byte{248, 246, 248, 242, 160, 0, 210, 76, 6, 13, 18, 219, 190, 18, 250, 23, 178, 198, 117, 254, 85, 14, 74, 104, 116, 56, 144, 116, 172, 14, 3, 236, 99, 248, 228, 142, 91, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 184, 65, 4, 72, 201, 77, 81, 160, 103, 70, 18, 102, 204, 82, 192, 86, 157, 40, 30, 117, 218, 224, 202, 1, 36, 249, 88, 82, 210, 19, 156, 112, 31, 13, 117, 227, 0, 125, 221, 190, 165, 16, 193, 163, 161, 175, 33, 37, 184, 235, 62, 201, 93, 102, 185, 143, 54, 146, 114, 30, 253, 178, 245, 87, 38, 191, 214, 160, 80, 218, 177, 253, 147, 35, 101, 59, 37, 87, 97, 193, 119, 21, 132, 111, 93, 53, 152, 203, 38, 134, 25, 104, 138, 236, 92, 27, 176, 89, 229, 176, 160, 29, 204, 77, 232, 222, 199, 93, 122, 171, 133, 181, 103, 182, 204, 212, 26, 211, 18, 69, 27, 148, 138, 116, 19, 240, 161, 66, 253, 64, 212, 147, 71, 131, 63, 255, 252, 132, 83, 48, 40, 74, 128, 160, 185, 20, 138, 2, 210, 15, 71, 144, 89, 167, 94, 155, 148, 118, 170, 157, 122, 70, 70, 114, 50, 221, 231, 8, 132, 167, 115, 239, 44, 245, 41, 226, 192, 192}) + return err +} + +func TestBlockChainReorg(t *testing.T) { + testManager := NewTestManager() + testManager.CreateChain1() + testManager2 := NewTestManager() + testManager2.CreateChain2() + + // This fails because we keep resetting the DB + block := testManager.BlockChain().GetBlock(testManager.BlockChain().CurrentBlock.PrevHash) + fmt.Println(block) + //testManager.BlockChain().FindCanonicalChain(testManager2.Blocks, testManager.BlockChain().GenesisBlock().Hash()) + +} diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index d907141a4..140b0efd0 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -144,6 +144,7 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { hash := block.Hash() if sm.bc.HasBlock(hash) { + fmt.Println("[SM] We already have this block, ignoring") return nil } @@ -158,12 +159,14 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { // Block validation if err := sm.ValidateBlock(block); err != nil { + fmt.Println("[SM] Error validating block:", err) return err } // I'm not sure, but I don't know if there should be thrown // any errors at this time. if err := sm.AccumelateRewards(block); err != nil { + fmt.Println("[SM] Error accumulating reward", err) return err } diff --git a/ethminer/miner.go b/ethminer/miner.go index cb752e3de..125eb6fb1 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -138,6 +138,7 @@ func (miner *Miner) listener() { } miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val}) log.Printf("[MINER] 🔨 Mined block %x\n", miner.block.Hash()) + log.Println(miner.block) miner.txs = []*ethchain.Transaction{} // Move this somewhere neat miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) diff --git a/peer.go b/peer.go index 63059bcfb..21d55ce6d 100644 --- a/peer.go +++ b/peer.go @@ -342,7 +342,7 @@ func (p *Peer) HandleInbound() { // We don't have this block, but we do have a block with the same prevHash, diversion time! if p.ethereum.StateManager().BlockChain().HasBlockWithPrevHash(block.PrevHash) { fmt.Printf("[PEER] Local and foreign chain have diverted after %x, we are going to get freaky with it!\n", block.PrevHash) - if p.ethereum.StateManager().BlockChain().FindCanonicalChain(msg, block.PrevHash) { + if p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) { return } } else { From 308c59320c25845f9668e76559b581e2161fec15 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 27 Mar 2014 15:38:55 +0100 Subject: [PATCH 010/100] Fixed typo --- ethutil/rlp.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethutil/rlp.go b/ethutil/rlp.go index 33ec0d359..e6c75696e 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -57,7 +57,7 @@ func DecodeWithReader(reader *bytes.Buffer) interface{} { switch { case char == 0: return nil - case char <= 0x7c: + case char <= 0x7f: return char case char <= 0xb7: From 43cad6901620ca077e43f195cc5ae4d1c8edb2d0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 27 Mar 2014 15:42:39 +0100 Subject: [PATCH 011/100] Reworked transaction constructors --- ethchain/keypair.go | 3 ++ ethchain/transaction.go | 62 +++++++++++++++++++++++++++++++++-------- ethchain/vm.go | 3 +- peer.go | 3 +- 4 files changed, 58 insertions(+), 13 deletions(-) diff --git a/ethchain/keypair.go b/ethchain/keypair.go index 9fdc95972..9daaedbee 100644 --- a/ethchain/keypair.go +++ b/ethchain/keypair.go @@ -34,6 +34,7 @@ func (k *KeyPair) Account() *Account { // Create transaction, creates a new and signed transaction, ready for processing func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Transaction { + /* TODO tx := NewTransaction(receiver, value, data) tx.Nonce = k.account.Nonce @@ -41,6 +42,8 @@ func (k *KeyPair) CreateTx(receiver []byte, value *big.Int, data []string) *Tran tx.Sign(k.PrivateKey) return tx + */ + return nil } func (k *KeyPair) RlpEncode() []byte { diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 3b07c81d4..695071251 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -18,16 +18,21 @@ type Transaction struct { Data []string v byte r, s []byte + + // Indicates whether this tx is a contract creation transaction + contractCreation bool } +/* func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { tx := Transaction{Recipient: to, Value: value, Nonce: 0, Data: data} return &tx } +*/ func NewContractCreationTx(value, gasprice *big.Int, data []string) *Transaction { - return &Transaction{Value: value, Gasprice: gasprice, Data: data} + return &Transaction{Value: value, Gasprice: gasprice, Data: data, contractCreation: true} } func NewContractMessageTx(to []byte, value, gasprice, gas *big.Int, data []string) *Transaction { @@ -38,10 +43,12 @@ func NewTx(to []byte, value *big.Int, data []string) *Transaction { return &Transaction{Recipient: to, Value: value, Gasprice: big.NewInt(0), Gas: big.NewInt(0), Nonce: 0, Data: data} } +/* // XXX Deprecated func NewTransactionFromData(data []byte) *Transaction { return NewTransactionFromBytes(data) } +*/ func NewTransactionFromBytes(data []byte) *Transaction { tx := &Transaction{} @@ -148,19 +155,52 @@ func (tx *Transaction) RlpDecode(data []byte) { tx.RlpValueDecode(ethutil.NewValueFromBytes(data)) } +// [ NONCE, VALUE, GASPRICE, TO, GAS, DATA, V, R, S ] func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { tx.Nonce = decoder.Get(0).Uint() - tx.Recipient = decoder.Get(1).Bytes() - tx.Value = decoder.Get(2).BigInt() + tx.Value = decoder.Get(1).BigInt() + tx.Gasprice = decoder.Get(2).BigInt() - d := decoder.Get(3) - tx.Data = make([]string, d.Len()) - for i := 0; i < d.Len(); i++ { - tx.Data[i] = d.Get(i).Str() + // If the 4th item is a list(slice) this tx + // is a contract creation tx + if decoder.Get(3).IsSlice() { + d := decoder.Get(3) + tx.Data = make([]string, d.Len()) + for i := 0; i < d.Len(); i++ { + tx.Data[i] = d.Get(i).Str() + } + + tx.v = byte(decoder.Get(4).Uint()) + tx.r = decoder.Get(5).Bytes() + tx.s = decoder.Get(6).Bytes() + tx.contractCreation = true + } else { + tx.Recipient = decoder.Get(3).Bytes() + tx.Gas = decoder.Get(4).BigInt() + + d := decoder.Get(5) + tx.Data = make([]string, d.Len()) + for i := 0; i < d.Len(); i++ { + tx.Data[i] = d.Get(i).Str() + } + + tx.v = byte(decoder.Get(6).Uint()) + tx.r = decoder.Get(7).Bytes() + tx.s = decoder.Get(8).Bytes() } + /* + tx.Nonce = decoder.Get(0).Uint() + tx.Recipient = decoder.Get(1).Bytes() + tx.Value = decoder.Get(2).BigInt() - // TODO something going wrong here - tx.v = byte(decoder.Get(4).Uint()) - tx.r = decoder.Get(5).Bytes() - tx.s = decoder.Get(6).Bytes() + d := decoder.Get(3) + tx.Data = make([]string, d.Len()) + for i := 0; i < d.Len(); i++ { + tx.Data[i] = d.Get(i).Str() + } + + tx.v = byte(decoder.Get(4).Uint()) + tx.r = decoder.Get(5).Bytes() + tx.s = decoder.Get(6).Bytes() + */ } diff --git a/ethchain/vm.go b/ethchain/vm.go index 9b6807925..aefc8ff0c 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -5,7 +5,6 @@ import ( _ "fmt" "github.com/ethereum/eth-go/ethutil" _ "github.com/obscuren/secp256k1-go" - "log" _ "math" "math/big" ) @@ -359,6 +358,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { } } +/* func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) { ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length) j := int64(0) @@ -395,3 +395,4 @@ func contractMemory(state *State, contractAddr []byte, memAddr *big.Int) *big.In return decoder.BigInt() } +*/ diff --git a/peer.go b/peer.go index 24a5e97c9..82c983927 100644 --- a/peer.go +++ b/peer.go @@ -334,7 +334,8 @@ func (p *Peer) HandleInbound() { // in the TxPool where it will undergo validation and // processing when a new block is found for i := 0; i < msg.Data.Len(); i++ { - p.ethereum.TxPool().QueueTransaction(ethchain.NewTransactionFromData(msg.Data.Get(i).Encode())) + //p.ethereum.TxPool().QueueTransaction(ethchain.NewTransactionFromData(msg.Data.Get(i).Encode())) + p.ethereum.TxPool().QueueTransaction(ethchain.NewTransactionFromValue(msg.Data.Get(i))) } case ethwire.MsgGetPeersTy: // Flag this peer as a 'requested of new peers' this to From 7660e1ed907e213a53408fe60a63619a68fd817b Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 27 Mar 2014 19:42:01 +0100 Subject: [PATCH 012/100] Added a IsList method for type checking []interface{} --- ethutil/value.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ethutil/value.go b/ethutil/value.go index 46681ec2a..04131aba9 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -149,6 +149,15 @@ func (val *Value) IsStr() bool { return val.Type() == reflect.String } +// Special list checking function. Something is considered +// a list if it's of type []interface{}. The list is usually +// used in conjunction with rlp decoded streams. +func (val *Value) IsList() bool { + _, ok := val.Val.([]interface{}) + + return ok +} + func (val *Value) IsEmpty() bool { return val.Val == nil || ((val.IsSlice() || val.IsStr()) && val.Len() == 0) } From 00c5f9b9a67a6ab6f2850b756804dfa6efd8a824 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 27 Mar 2014 19:49:47 +0100 Subject: [PATCH 013/100] Updated transaction model Changed the behaviour of decoding rlp data. Something is considered to be creating a contract if the 4th item is a list. Changed constructors. --- ethchain/transaction.go | 42 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 695071251..d71f9c7f7 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -2,6 +2,7 @@ package ethchain import ( "bytes" + "fmt" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/secp256k1-go" "math/big" @@ -23,33 +24,14 @@ type Transaction struct { contractCreation bool } -/* -func NewTransaction(to []byte, value *big.Int, data []string) *Transaction { - tx := Transaction{Recipient: to, Value: value, Nonce: 0, Data: data} - - return &tx -} -*/ - func NewContractCreationTx(value, gasprice *big.Int, data []string) *Transaction { return &Transaction{Value: value, Gasprice: gasprice, Data: data, contractCreation: true} } -func NewContractMessageTx(to []byte, value, gasprice, gas *big.Int, data []string) *Transaction { +func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []string) *Transaction { return &Transaction{Recipient: to, Value: value, Gasprice: gasprice, Gas: gas, Data: data} } -func NewTx(to []byte, value *big.Int, data []string) *Transaction { - return &Transaction{Recipient: to, Value: value, Gasprice: big.NewInt(0), Gas: big.NewInt(0), Nonce: 0, Data: data} -} - -/* -// XXX Deprecated -func NewTransactionFromData(data []byte) *Transaction { - return NewTransactionFromBytes(data) -} -*/ - func NewTransactionFromBytes(data []byte) *Transaction { tx := &Transaction{} tx.RlpDecode(data) @@ -131,16 +113,13 @@ func (tx *Transaction) Sign(privk []byte) error { } func (tx *Transaction) RlpData() interface{} { - // Prepare the transaction for serialization - return []interface{}{ - tx.Nonce, - tx.Recipient, - tx.Value, - ethutil.NewSliceValue(tx.Data).Slice(), - tx.v, - tx.r, - tx.s, + data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice} + + if !tx.contractCreation { + data = append(data, tx.Recipient, tx.Gas) } + + return append(data, ethutil.NewSliceValue(tx.Data).Slice(), tx.v, tx.r, tx.s) } func (tx *Transaction) RlpValue() *ethutil.Value { @@ -156,14 +135,16 @@ func (tx *Transaction) RlpDecode(data []byte) { } // [ NONCE, VALUE, GASPRICE, TO, GAS, DATA, V, R, S ] +//["" "\x03\xe8" "" "\xaa" "\x03\xe8" [] '\x1c' "\x10C\x15\xfc\xe5\xd0\t\xe4\r\xe7\xefa\xf5aE\xd6\x14\xaed\xb5.\xf5\x18\xa1S_j\xe0A\xdc5U" "dQ\nqy\xf8\x17+\xbf\xd7Jx\xda-\xcb\xd7\xcfQ\x1bI\xb8_9\b\x80\xeaë“Ži|\x1f"] func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { + fmt.Println(decoder) tx.Nonce = decoder.Get(0).Uint() tx.Value = decoder.Get(1).BigInt() tx.Gasprice = decoder.Get(2).BigInt() // If the 4th item is a list(slice) this tx // is a contract creation tx - if decoder.Get(3).IsSlice() { + if decoder.Get(3).IsList() { d := decoder.Get(3) tx.Data = make([]string, d.Len()) for i := 0; i < d.Len(); i++ { @@ -173,6 +154,7 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { tx.v = byte(decoder.Get(4).Uint()) tx.r = decoder.Get(5).Bytes() tx.s = decoder.Get(6).Bytes() + tx.contractCreation = true } else { tx.Recipient = decoder.Get(3).Bytes() From 56a58ad70db22b0714a8f81fe31eaedc2a1e8e0d Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 27 Mar 2014 22:02:39 +0100 Subject: [PATCH 014/100] Removed debug and comments --- ethchain/transaction.go | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index d71f9c7f7..af27fe639 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -2,7 +2,6 @@ package ethchain import ( "bytes" - "fmt" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/secp256k1-go" "math/big" @@ -134,10 +133,7 @@ func (tx *Transaction) RlpDecode(data []byte) { tx.RlpValueDecode(ethutil.NewValueFromBytes(data)) } -// [ NONCE, VALUE, GASPRICE, TO, GAS, DATA, V, R, S ] -//["" "\x03\xe8" "" "\xaa" "\x03\xe8" [] '\x1c' "\x10C\x15\xfc\xe5\xd0\t\xe4\r\xe7\xefa\xf5aE\xd6\x14\xaed\xb5.\xf5\x18\xa1S_j\xe0A\xdc5U" "dQ\nqy\xf8\x17+\xbf\xd7Jx\xda-\xcb\xd7\xcfQ\x1bI\xb8_9\b\x80\xeaë“Ži|\x1f"] func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { - fmt.Println(decoder) tx.Nonce = decoder.Get(0).Uint() tx.Value = decoder.Get(1).BigInt() tx.Gasprice = decoder.Get(2).BigInt() @@ -170,19 +166,4 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { tx.r = decoder.Get(7).Bytes() tx.s = decoder.Get(8).Bytes() } - /* - tx.Nonce = decoder.Get(0).Uint() - tx.Recipient = decoder.Get(1).Bytes() - tx.Value = decoder.Get(2).BigInt() - - d := decoder.Get(3) - tx.Data = make([]string, d.Len()) - for i := 0; i < d.Len(); i++ { - tx.Data[i] = d.Get(i).Str() - } - - tx.v = byte(decoder.Get(4).Uint()) - tx.r = decoder.Get(5).Bytes() - tx.s = decoder.Get(6).Bytes() - */ } From 3c3431d111ae8ba7f03349f93c9b191fcdf92254 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 27 Mar 2014 23:17:14 +0100 Subject: [PATCH 015/100] Fixed IsContract method to use the contractCreation flag --- ethchain/transaction.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index af27fe639..9fdf55b4d 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -1,7 +1,6 @@ package ethchain import ( - "bytes" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/secp256k1-go" "math/big" @@ -62,7 +61,7 @@ func (tx *Transaction) Hash() []byte { } func (tx *Transaction) IsContract() bool { - return bytes.Compare(tx.Recipient, ContractAddr) == 0 + return tx.contractCreation } func (tx *Transaction) Signature(key []byte) []byte { From 75e6406c1f1034dbf96aca28193d7e1e0653db50 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 27 Mar 2014 23:17:23 +0100 Subject: [PATCH 016/100] Fixed tests --- ethchain/vm_test.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 308a65432..5acc47659 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -29,7 +29,7 @@ func TestRun3(t *testing.T) { "PUSH", "0", "RETURN", }) - tx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), script) + tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script) addr := tx.Hash()[12:] contract := MakeContract(tx, state) state.UpdateContract(contract) @@ -51,7 +51,7 @@ func TestRun3(t *testing.T) { "PUSH", 0, "RETURN", ) - callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) + callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript) // Contract addr as test address account := NewAccount(ContractAddr, big.NewInt(10000000)) @@ -84,20 +84,20 @@ func TestRun4(t *testing.T) { mutan.NewCompiler().Compile(strings.NewReader(` a = 1337 c = 1 -[0] = 50 -d = [0] +store[0] = 50 +d = store[0] `)) - asm := mutan.NewCompiler().Compile(strings.NewReader(` + asm, _ := mutan.NewCompiler().Compile(strings.NewReader(` a = 3 + 3 - [1000] = a - [1000] + stotre[1000] = a + store[1000] `)) asm = append(asm, "LOG") fmt.Println(asm) callerScript := ethutil.Assemble(asm...) - callerTx := NewTransaction(ContractAddr, ethutil.Big("100000000000000000000000000000000000000000000000000"), callerScript) + callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript) // Contract addr as test address account := NewAccount(ContractAddr, big.NewInt(10000000)) From 60fd2f3521471b300107847271f4df2919f1b0d4 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Thu, 27 Mar 2014 23:25:03 +0100 Subject: [PATCH 017/100] Update vm_test.go store ... --- ethchain/vm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 5acc47659..c802420cb 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -90,7 +90,7 @@ d = store[0] asm, _ := mutan.NewCompiler().Compile(strings.NewReader(` a = 3 + 3 - stotre[1000] = a + store[1000] = a store[1000] `)) asm = append(asm, "LOG") From b888652201277ab86e9e8c280e75e23ced5e3d38 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 28 Mar 2014 11:20:07 +0100 Subject: [PATCH 018/100] Added missing GetTx (0x16) wire message --- ethchain/transaction_pool.go | 8 +++++++- ethwire/messaging.go | 2 ++ peer.go | 18 ++++++++++++++++-- 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index fdc386303..4a4f2e809 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -207,7 +207,7 @@ func (pool *TxPool) QueueTransaction(tx *Transaction) { pool.queueChan <- tx } -func (pool *TxPool) Flush() []*Transaction { +func (pool *TxPool) CurrentTransactions() []*Transaction { pool.mutex.Lock() defer pool.mutex.Unlock() @@ -221,6 +221,12 @@ func (pool *TxPool) Flush() []*Transaction { i++ } + return txList +} + +func (pool *TxPool) Flush() []*Transaction { + txList := pool.CurrentTransactions() + // Recreate a new list all together // XXX Is this the fastest way? pool.pool = list.New() diff --git a/ethwire/messaging.go b/ethwire/messaging.go index 185faa341..b622376f3 100644 --- a/ethwire/messaging.go +++ b/ethwire/messaging.go @@ -32,6 +32,7 @@ const ( MsgBlockTy = 0x13 MsgGetChainTy = 0x14 MsgNotInChainTy = 0x15 + MsgGetTxsTy = 0x16 MsgTalkTy = 0xff ) @@ -46,6 +47,7 @@ var msgTypeToString = map[MsgType]string{ MsgTxTy: "Transactions", MsgBlockTy: "Blocks", MsgGetChainTy: "Get chain", + MsgGetTxsTy: "Get Txs", MsgNotInChainTy: "Not in chain", } diff --git a/peer.go b/peer.go index 82c983927..279b0bc7f 100644 --- a/peer.go +++ b/peer.go @@ -334,8 +334,8 @@ func (p *Peer) HandleInbound() { // in the TxPool where it will undergo validation and // processing when a new block is found for i := 0; i < msg.Data.Len(); i++ { - //p.ethereum.TxPool().QueueTransaction(ethchain.NewTransactionFromData(msg.Data.Get(i).Encode())) - p.ethereum.TxPool().QueueTransaction(ethchain.NewTransactionFromValue(msg.Data.Get(i))) + tx := ethchain.NewTransactionFromValue(msg.Data.Get(i)) + p.ethereum.TxPool().QueueTransaction(tx) } case ethwire.MsgGetPeersTy: // Flag this peer as a 'requested of new peers' this to @@ -398,6 +398,16 @@ func (p *Peer) HandleInbound() { case ethwire.MsgNotInChainTy: ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data) // TODO + 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)) // Unofficial but fun nonetheless case ethwire.MsgTalkTy: @@ -562,6 +572,10 @@ func (p *Peer) CatchupWithPeer() { p.QueueMessage(msg) ethutil.Config.Log.Debugf("Requesting blockchain %x...\n", p.ethereum.BlockChain().CurrentBlock.Hash()[:4]) + + msg = ethwire.NewMessage(ethwire.MsgGetTxsTy, []interface{}{}) + p.QueueMessage(msg) + ethutil.Config.Log.Debugln("Requested transactions") } } From 6625b6ffbdb93a47de2187198d6e826fb32c1ba6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 30 Mar 2014 12:58:37 +0200 Subject: [PATCH 019/100] Changed to new mutan API --- ethchain/state_manager.go | 26 +++++++++++++------------- ethchain/vm_test.go | 25 ++++++++++++++----------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 3b5507740..5c693442b 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -100,16 +100,21 @@ func (sm *StateManager) MakeContract(tx *Transaction) { } } +// Apply transactions uses the transaction passed to it and applies them onto +// the current processing state. func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { // Process each transaction/contract for _, tx := range txs { // If there's no recipient, it's a contract + // Check if this is a contract creation traction and if so + // create a contract of this tx. if tx.IsContract() { sm.MakeContract(tx) - //XXX block.MakeContract(tx) } else { + // Figure out if the address this transaction was sent to is a + // contract or an actual account. In case of a contract, we process that + // contract instead of moving funds between accounts. if contract := sm.procState.GetContract(tx.Recipient); contract != nil { - //XXX if contract := block.state.GetContract(tx.Recipient); contract != nil { sm.ProcessContract(contract, tx, block) } else { err := sm.Ethereum.TxPool().ProcessTransaction(tx, block) @@ -172,14 +177,12 @@ func (sm *StateManager) ProcessBlock(block *Block) error { // if !sm.compState.Cmp(sm.procState) if !sm.compState.Cmp(sm.procState) { - //XXX return fmt.Errorf("Invalid merkle root. Expected %x, got %x", block.State().trie.Root, sm.bc.CurrentBlock.State().trie.Root) return fmt.Errorf("Invalid merkle root. Expected %x, got %x", sm.compState.trie.Root, sm.procState.trie.Root) } // Calculate the new total difficulty and sync back to the db if sm.CalculateTD(block) { // Sync the current block's state to the database and cancelling out the deferred Undo - //XXX sm.bc.CurrentBlock.Sync() sm.procState.Sync() // Broadcast the valid block back to the wire @@ -273,12 +276,10 @@ func CalculateUncleReward(block *Block) *big.Int { func (sm *StateManager) AccumelateRewards(block *Block) error { // Get the coinbase rlp data - //XXX addr := processor.state.GetAccount(block.Coinbase) addr := sm.procState.GetAccount(block.Coinbase) // Reward amount of ether to the coinbase address addr.AddFee(CalculateBlockReward(block, len(block.Uncles))) - //XXX processor.state.UpdateAccount(block.Coinbase, addr) sm.procState.UpdateAccount(block.Coinbase, addr) for _, uncle := range block.Uncles { @@ -298,13 +299,12 @@ func (sm *StateManager) Stop() { func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) { // Recovering function in case the VM had any errors - /* - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered from VM execution with err =", r) - } - }() - */ + defer func() { + if r := recover(); r != nil { + fmt.Println("Recovered from VM execution with err =", r) + } + }() + caller := sm.procState.GetAccount(tx.Sender()) closure := NewClosure(caller, contract, sm.procState, tx.Gas, tx.Value) vm := NewVm(sm.procState, RuntimeVars{ diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index c802420cb..589f0bf4a 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -81,18 +81,21 @@ func TestRun4(t *testing.T) { db, _ := ethdb.NewMemDatabase() state := NewState(ethutil.NewTrie(db, "")) - mutan.NewCompiler().Compile(strings.NewReader(` -a = 1337 -c = 1 -store[0] = 50 -d = store[0] -`)) + mutan.Compile(strings.NewReader(` + a = 1337 + c = 1 + store[0] = 50 + d = store[0] + `), false) - asm, _ := mutan.NewCompiler().Compile(strings.NewReader(` - a = 3 + 3 - store[1000] = a - store[1000] -`)) + asm, err := mutan.Compile(strings.NewReader(` + a = 3 + 3 + store[1000] = a + store[1000] + `), false) + if err != nil { + fmt.Println(err) + } asm = append(asm, "LOG") fmt.Println(asm) From 205e33bc831bb44f41dc899ae41bbfe0e44ddc5d Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 30 Mar 2014 18:55:51 +0200 Subject: [PATCH 020/100] Fixed bug in stack to expand beyond expectations. Fixed EQ and NOT opcode --- ethchain/stack.go | 14 +++++++++----- ethchain/vm.go | 20 ++++++++++++++------ ethchain/vm_test.go | 21 +++++++++------------ 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/ethchain/stack.go b/ethchain/stack.go index 57165c432..e3fc4b684 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -173,21 +173,25 @@ func NewStack() *Stack { } func (st *Stack) Pop() *big.Int { - str := st.data[0] - st.data = st.data[1:] + str := st.data[len(st.data)-1] + + copy(st.data[:len(st.data)-1], st.data[:len(st.data)-1]) + st.data = st.data[:len(st.data)-1] return str } func (st *Stack) Popn() (*big.Int, *big.Int) { - ints := st.data[:2] - st.data = st.data[2:] + ints := st.data[len(st.data)-2:] + + copy(st.data[:len(st.data)-2], st.data[:len(st.data)-2]) + st.data = st.data[:len(st.data)-2] return ints[0], ints[1] } func (st *Stack) Peek() *big.Int { - str := st.data[0] + str := st.data[len(st.data)-1] return str } diff --git a/ethchain/vm.go b/ethchain/vm.go index aefc8ff0c..18b7fe607 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -2,7 +2,7 @@ package ethchain import ( _ "bytes" - _ "fmt" + "fmt" "github.com/ethereum/eth-go/ethutil" _ "github.com/obscuren/secp256k1-go" _ "math" @@ -213,10 +213,17 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { } else { stack.Push(ethutil.BigFalse) } - case oNOT: + case oEQ: x, y := stack.Popn() - // x != y - if x.Cmp(y) != 0 { + // x == y + if x.Cmp(y) == 0 { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case oNOT: + x := stack.Pop() + if x.Cmp(ethutil.BigFalse) == 0 { stack.Push(ethutil.BigTrue) } else { stack.Push(ethutil.BigFalse) @@ -300,8 +307,8 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { case oJUMP: pc = stack.Pop() case oJUMPI: - pos, cond := stack.Popn() - if cond.Cmp(big.NewInt(0)) > 0 { + cond, pos := stack.Popn() + if cond.Cmp(ethutil.BigTrue) == 0 { pc = pos } case oPC: @@ -314,6 +321,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { retSize, retOffset := stack.Popn() // Pop input size and offset inSize, inOffset := stack.Popn() + fmt.Println(inSize, inOffset) // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) // Pop gas and value of the stack. diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 589f0bf4a..e3880d26e 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -1,7 +1,7 @@ package ethchain import ( - "bytes" + _ "bytes" "fmt" "github.com/ethereum/eth-go/ethdb" "github.com/ethereum/eth-go/ethutil" @@ -11,6 +11,7 @@ import ( "testing" ) +/* func TestRun3(t *testing.T) { ethutil.ReadConfig("") @@ -73,7 +74,7 @@ func TestRun3(t *testing.T) { if bytes.Compare(ret, exp) != 0 { t.Errorf("expected return value to be %v, got %v", exp, ret) } -} +}*/ func TestRun4(t *testing.T) { ethutil.ReadConfig("") @@ -81,17 +82,13 @@ func TestRun4(t *testing.T) { db, _ := ethdb.NewMemDatabase() state := NewState(ethutil.NewTrie(db, "")) - mutan.Compile(strings.NewReader(` - a = 1337 - c = 1 - store[0] = 50 - d = store[0] - `), false) - asm, err := mutan.Compile(strings.NewReader(` - a = 3 + 3 - store[1000] = a - store[1000] + a = 10 + b = 10 + if a == b { + b = 1000 + c = 10 + } `), false) if err != nil { fmt.Println(err) From 7cc28c8b469ba8df8bad1e3bbbba7fbd99b88535 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 30 Mar 2014 22:03:08 +0200 Subject: [PATCH 021/100] Added storage test --- ethchain/vm_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index e3880d26e..2a02bcf4c 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -86,14 +86,22 @@ func TestRun4(t *testing.T) { a = 10 b = 10 if a == b { - b = 1000 c = 10 + if c == 10 { + d = 1000 + e = 10 + } } + + store[0] = 20 + test = store[0] + store[a] = 20 + f = store[400] `), false) if err != nil { fmt.Println(err) } - asm = append(asm, "LOG") + //asm = append(asm, "LOG") fmt.Println(asm) callerScript := ethutil.Assemble(asm...) From 7277c420479239fbea78417e42c43ee0162c2728 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 31 Mar 2014 01:03:28 +0200 Subject: [PATCH 022/100] Fixed some state issues --- ethchain/state_manager.go | 6 +++--- ethchain/transaction.go | 3 ++- ethchain/vm.go | 2 ++ ethchain/vm_test.go | 2 -- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 5c693442b..d9831d49f 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -82,7 +82,7 @@ func (sm *StateManager) WatchAddr(addr []byte) *AccountState { func (sm *StateManager) GetAddrState(addr []byte) *AccountState { account := sm.addrStateStore.Get(addr) if account == nil { - a := sm.bc.CurrentBlock.state.GetAccount(addr) + a := sm.procState.GetAccount(addr) account = &AccountState{Nonce: a.Nonce, Account: a} } @@ -128,9 +128,9 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { // The prepare function, prepares the state manager for the next // "ProcessBlock" action. -func (sm *StateManager) Prepare(processer *State, comparative *State) { +func (sm *StateManager) Prepare(processor *State, comparative *State) { sm.compState = comparative - sm.procState = processer + sm.procState = processor } // Default prepare function diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 9fdf55b4d..506e3c159 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -116,8 +116,9 @@ func (tx *Transaction) RlpData() interface{} { if !tx.contractCreation { data = append(data, tx.Recipient, tx.Gas) } + d := ethutil.NewSliceValue(tx.Data).Slice() - return append(data, ethutil.NewSliceValue(tx.Data).Slice(), tx.v, tx.r, tx.s) + return append(data, d, tx.v, tx.r, tx.s) } func (tx *Transaction) RlpValue() *ethutil.Value { diff --git a/ethchain/vm.go b/ethchain/vm.go index 18b7fe607..98aaa603a 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -115,6 +115,8 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { } if closure.Gas.Cmp(gas) < 0 { + ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas) + return closure.Return(nil) } diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 2a02bcf4c..838f12f56 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -94,9 +94,7 @@ func TestRun4(t *testing.T) { } store[0] = 20 - test = store[0] store[a] = 20 - f = store[400] `), false) if err != nil { fmt.Println(err) From 5f49a659c36dbfb8c330ddc3d4565c19a9a936b5 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 31 Mar 2014 12:54:37 +0200 Subject: [PATCH 023/100] More blockchain testing --- ethchain/block_chain.go | 13 +++++++++++-- ethchain/block_chain_test.go | 37 +++++++++++++++++++++++++++++------- ethutil/rlp_test.go | 10 ++++++++++ ethutil/trie_test.go | 2 +- 4 files changed, 52 insertions(+), 10 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 0e3601a4b..8c03eec38 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -126,6 +126,7 @@ func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte log.Println("[CHAIN] We have found the common parent block, breaking") break } + log.Println("Checking incoming blocks:") chainDifficulty.Add(chainDifficulty, bc.CalculateBlockTD(block)) } @@ -139,13 +140,20 @@ func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte log.Println("[CHAIN] We have found the common parent block, breaking") break } - log.Println("CHECKING BLOGK:", i) + 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 + log.Println("[CHAIN] At genesis block, breaking") + break + } + log.Printf("CHECKING OUR OWN BLOCKS: %x", block.Hash()) + log.Printf("%x", bc.GenesisBlock().Hash()) curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block)) } log.Println("[CHAIN] Current chain difficulty:", curChainDifficulty) if chainDifficulty.Cmp(curChainDifficulty) == 1 { - log.Println("[CHAIN] The incoming Chain beat our asses, resetting") + log.Printf("[CHAIN] The incoming Chain beat our asses, resetting to block: %x", commonBlockHash) bc.ResetTillBlockHash(commonBlockHash) return false } else { @@ -165,6 +173,7 @@ func (bc *BlockChain) ResetTillBlockHash(hash []byte) error { // END TODO bc.Ethereum.StateManager().PrepareDefault(returnTo) + err := ethutil.Config.Db.Delete(lastBlock.Hash()) if err != nil { return err diff --git a/ethchain/block_chain_test.go b/ethchain/block_chain_test.go index 736247e83..30eb62266 100644 --- a/ethchain/block_chain_test.go +++ b/ethchain/block_chain_test.go @@ -78,15 +78,38 @@ func (tm *TestManager) CreateChain2() error { return err } -func TestBlockChainReorg(t *testing.T) { - testManager := NewTestManager() - testManager.CreateChain1() +func TestNegativeBlockChainReorg(t *testing.T) { + // We are resetting the database between creation so we need to cache our information testManager2 := NewTestManager() testManager2.CreateChain2() + tm2Blocks := testManager2.Blocks - // This fails because we keep resetting the DB - block := testManager.BlockChain().GetBlock(testManager.BlockChain().CurrentBlock.PrevHash) - fmt.Println(block) - //testManager.BlockChain().FindCanonicalChain(testManager2.Blocks, testManager.BlockChain().GenesisBlock().Hash()) + testManager := NewTestManager() + testManager.CreateChain1() + oldState := testManager.BlockChain().CurrentBlock.State() + + if testManager.BlockChain().FindCanonicalChain(tm2Blocks, testManager.BlockChain().GenesisBlock().Hash()) != true { + t.Error("I expected TestManager to have the longest chain, but it was TestManager2 instead.") + } + if testManager.BlockChain().CurrentBlock.State() != oldState { + t.Error("I expected the top state to be the same as it was as before the reorg") + } } + +func TestPositiveBlockChainReorg(t *testing.T) { + testManager := NewTestManager() + testManager.CreateChain1() + tm1Blocks := testManager.Blocks + + testManager2 := NewTestManager() + testManager2.CreateChain2() + oldState := testManager2.BlockChain().CurrentBlock.State() + + if testManager2.BlockChain().FindCanonicalChain(tm1Blocks, testManager.BlockChain().GenesisBlock().Hash()) == true { + t.Error("I expected TestManager to have the longest chain, but it was TestManager2 instead.") + } + if testManager2.BlockChain().CurrentBlock.State() == oldState { + t.Error("I expected the top state to have been modified but it was not") + } +} diff --git a/ethutil/rlp_test.go b/ethutil/rlp_test.go index 2a58bfc0f..ce2535663 100644 --- a/ethutil/rlp_test.go +++ b/ethutil/rlp_test.go @@ -2,6 +2,7 @@ package ethutil import ( "bytes" + "fmt" "math/big" "reflect" "testing" @@ -55,6 +56,15 @@ func TestValue(t *testing.T) { } } +func TestEncodeDecodeMaran(t *testing.T) { + b := NewValue([]interface{}{"dog", 15, []interface{}{"cat", "cat", []interface{}{}}, 1024, "tachikoma"}) + a := b.Encode() + fmt.Println("voor maran", a) + f, i := Decode(a, 0) + fmt.Println("voor maran 2", f) + fmt.Println(i) +} + func TestEncode(t *testing.T) { strRes := "\x83dog" bytes := Encode("dog") diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index 79e5de921..0be512d9f 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -1,7 +1,7 @@ package ethutil import ( - "fmt" + _ "fmt" "reflect" "testing" ) From 7d0348e4baf45197ca506070e06e756a4ba6ccf6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 1 Apr 2014 10:41:30 +0200 Subject: [PATCH 024/100] Handle contract messages --- ethchain/state_manager.go | 18 +++++++++++++----- ethchain/transaction_pool.go | 12 +++++++----- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index d9831d49f..95e46e41d 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -114,13 +114,18 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { // Figure out if the address this transaction was sent to is a // contract or an actual account. In case of a contract, we process that // contract instead of moving funds between accounts. + var err error if contract := sm.procState.GetContract(tx.Recipient); contract != nil { - sm.ProcessContract(contract, tx, block) - } else { - err := sm.Ethereum.TxPool().ProcessTransaction(tx, block) - if err != nil { - ethutil.Config.Log.Infoln("[STATE]", err) + err = sm.Ethereum.TxPool().ProcessTransaction(tx, sm.procState, true) + if err == nil { + sm.ProcessContract(contract, tx, block) } + } else { + err = sm.Ethereum.TxPool().ProcessTransaction(tx, sm.procState, false) + } + + if err != nil { + ethutil.Config.Log.Infoln("[STATE]", err) } } } @@ -318,4 +323,7 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo txData: nil, }) closure.Call(vm, nil) + + // Update the account (refunds) + sm.procState.UpdateAccount(tx.Sender(), caller) } diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 4a4f2e809..66828adfb 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -90,7 +90,7 @@ func (pool *TxPool) addTransaction(tx *Transaction) { // Process transaction validates the Tx and processes funds from the // sender to the recipient. -func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error) { +func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract bool) (err error) { defer func() { if r := recover(); r != nil { log.Println(r) @@ -98,7 +98,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error } }() // Get the sender - sender := block.state.GetAccount(tx.Sender()) + sender := state.GetAccount(tx.Sender()) // Make sure there's enough in the sender's account. Having insufficient // funds won't invalidate this transaction but simple ignores it. @@ -116,13 +116,15 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error } // Get the receiver - receiver := block.state.GetAccount(tx.Recipient) + receiver := state.GetAccount(tx.Recipient) sender.Nonce += 1 // Send Tx to self if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { // Subtract the fee sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat)) + } else if toContract { + sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat)) } else { // Subtract the amount from the senders account sender.Amount.Sub(sender.Amount, totAmount) @@ -130,10 +132,10 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block) (err error // Add the amount to receivers account which should conclude this transaction receiver.Amount.Add(receiver.Amount, tx.Value) - block.state.UpdateAccount(tx.Recipient, receiver) + state.UpdateAccount(tx.Recipient, receiver) } - block.state.UpdateAccount(tx.Sender(), sender) + state.UpdateAccount(tx.Sender(), sender) log.Printf("[TXPL] Processed Tx %x\n", tx.Hash()) From 3558dd5ed4e14f124f04e2bf72fc3b989dff9e77 Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 1 Apr 2014 14:42:48 +0200 Subject: [PATCH 025/100] Finalize blockchain reverting test --- ethchain/block_chain.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 8c03eec38..a8d9793d6 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -163,14 +163,20 @@ func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte } func (bc *BlockChain) ResetTillBlockHash(hash []byte) error { lastBlock := bc.CurrentBlock - returnTo := bc.GetBlock(hash) - - // TODO: REFACTOR TO FUNCTION, Used multiple times - bc.CurrentBlock = returnTo - bc.LastBlockHash = returnTo.Hash() - info := bc.BlockInfo(returnTo) - bc.LastBlockNumber = info.Number - // END TODO + 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() + info := bc.BlockInfo(returnTo) + bc.LastBlockNumber = info.Number + } bc.Ethereum.StateManager().PrepareDefault(returnTo) From 782910eaa76bb31be4c2bcd0f4505b8085acb57c Mon Sep 17 00:00:00 2001 From: Maran Date: Tue, 1 Apr 2014 15:54:29 +0200 Subject: [PATCH 026/100] Small tweaks --- ethchain/block_chain.go | 3 --- ethminer/miner.go | 2 -- peer.go | 9 +++------ 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index a8d9793d6..f621965ae 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -146,8 +146,6 @@ func (bc *BlockChain) FindCanonicalChain(blocks []*Block, commonBlockHash []byte log.Println("[CHAIN] At genesis block, breaking") break } - log.Printf("CHECKING OUR OWN BLOCKS: %x", block.Hash()) - log.Printf("%x", bc.GenesisBlock().Hash()) curChainDifficulty.Add(curChainDifficulty, bc.CalculateBlockTD(block)) } @@ -309,7 +307,6 @@ func (bc *BlockChain) Add(block *Block) { bc.LastBlockHash = block.Hash() encodedBlock := block.RlpEncode() - log.Println(encodedBlock) ethutil.Config.Db.Put(block.Hash(), encodedBlock) ethutil.Config.Db.Put([]byte("LastBlock"), encodedBlock) } diff --git a/ethminer/miner.go b/ethminer/miner.go index 125eb6fb1..60af3ab31 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -130,7 +130,6 @@ func (miner *Miner) listener() { err := miner.ethereum.StateManager().ProcessBlock(miner.block, true) if err != nil { log.Println("Error result from process block:", err) - log.Println(miner.block) } else { if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) { @@ -138,7 +137,6 @@ func (miner *Miner) listener() { } miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val}) log.Printf("[MINER] 🔨 Mined block %x\n", miner.block.Hash()) - log.Println(miner.block) miner.txs = []*ethchain.Transaction{} // Move this somewhere neat miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) diff --git a/peer.go b/peer.go index f2267c29b..0ecd13e60 100644 --- a/peer.go +++ b/peer.go @@ -337,16 +337,16 @@ func (p *Peer) HandleInbound() { block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i)) // Do we have this block on our chain? If so we can continue if p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) { - fmt.Println("[PEER] Block found, checking next one.") + ethutil.Config.Log.Debugf("[PEER] Block found, checking next one.\n") } else { // We don't have this block, but we do have a block with the same prevHash, diversion time! if p.ethereum.StateManager().BlockChain().HasBlockWithPrevHash(block.PrevHash) { - fmt.Printf("[PEER] Local and foreign chain have diverted after %x, we are going to get freaky with it!\n", block.PrevHash) + ethutil.Config.Log.Infof("[PEER] Local and foreign chain have diverted after %x, finding best chain!\n", block.PrevHash) if p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) { return } } else { - fmt.Println("[PEER] Both local and foreign chain have same parent. Continue normally") + ethutil.Config.Log.Debugf("[PEER] Both local and foreign chain have same parent. Continue normally\n") } } } @@ -362,7 +362,6 @@ func (p *Peer) HandleInbound() { if ethutil.Config.Debug { ethutil.Config.Log.Infof("[PEER] Block %x failed\n", block.Hash()) ethutil.Config.Log.Infof("[PEER] %v\n", err) - ethutil.Config.Log.Infoln(block) } break } else { @@ -637,8 +636,6 @@ func (p *Peer) SyncWithBlocks() { if p.blocksRequested == 0 { p.blocksRequested = 10 } - fmt.Printf("Currenb lock %x\n", p.ethereum.BlockChain().CurrentBlock.Hash()) - fmt.Println("Amount:", p.blocksRequested) blocks := p.ethereum.BlockChain().GetChain(p.ethereum.BlockChain().CurrentBlock.Hash(), p.blocksRequested) var hashes []interface{} From 90bb512f420f204f50ba451a4a25682ca8443746 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 5 Apr 2014 10:49:07 +0200 Subject: [PATCH 027/100] Update --- ethchain/vm_test.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 838f12f56..85ec4c693 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -83,18 +83,23 @@ func TestRun4(t *testing.T) { state := NewState(ethutil.NewTrie(db, "")) asm, err := mutan.Compile(strings.NewReader(` - a = 10 - b = 10 + int32 a = 10 + int32 b = 10 if a == b { - c = 10 + int32 c = 10 if c == 10 { - d = 1000 - e = 10 + int32 d = 1000 + int32 e = 10 } } store[0] = 20 store[a] = 20 + store[b] = this.caller() + + int8[10] ret + int8[10] arg + call(1234, 0, 100000000, arg, ret) `), false) if err != nil { fmt.Println(err) From 35a82f8f4a69019d72ff3379e0f61bf2be475e42 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 7 Apr 2014 14:00:02 +0200 Subject: [PATCH 028/100] Added support for mneomnic privkeys --- ethutil/mnemonic.go | 1686 ++++++++++++++++++++++++++++++++++++++ ethutil/mnemonic_test.go | 74 ++ 2 files changed, 1760 insertions(+) create mode 100644 ethutil/mnemonic.go create mode 100644 ethutil/mnemonic_test.go diff --git a/ethutil/mnemonic.go b/ethutil/mnemonic.go new file mode 100644 index 000000000..d21c20f00 --- /dev/null +++ b/ethutil/mnemonic.go @@ -0,0 +1,1686 @@ +package ethutil + +import ( + "fmt" + "strconv" +) + +var words []string = []string{ + "like", + "just", + "love", + "know", + "never", + "want", + "time", + "out", + "there", + "make", + "look", + "eye", + "down", + "only", + "think", + "heart", + "back", + "then", + "into", + "about", + "more", + "away", + "still", + "them", + "take", + "thing", + "even", + "through", + "long", + "always", + "world", + "too", + "friend", + "tell", + "try", + "hand", + "thought", + "over", + "here", + "other", + "need", + "smile", + "again", + "much", + "cry", + "been", + "night", + "ever", + "little", + "said", + "end", + "some", + "those", + "around", + "mind", + "people", + "girl", + "leave", + "dream", + "left", + "turn", + "myself", + "give", + "nothing", + "really", + "off", + "before", + "something", + "find", + "walk", + "wish", + "good", + "once", + "place", + "ask", + "stop", + "keep", + "watch", + "seem", + "everything", + "wait", + "got", + "yet", + "made", + "remember", + "start", + "alone", + "run", + "hope", + "maybe", + "believe", + "body", + "hate", + "after", + "close", + "talk", + "stand", + "own", + "each", + "hurt", + "help", + "home", + "god", + "soul", + "new", + "many", + "two", + "inside", + "should", + "true", + "first", + "fear", + "mean", + "better", + "play", + "another", + "gone", + "change", + "use", + "wonder", + "someone", + "hair", + "cold", + "open", + "best", + "any", + "behind", + "happen", + "water", + "dark", + "laugh", + "stay", + "forever", + "name", + "work", + "show", + "sky", + "break", + "came", + "deep", + "door", + "put", + "black", + "together", + "upon", + "happy", + "such", + "great", + "white", + "matter", + "fill", + "past", + "please", + "burn", + "cause", + "enough", + "touch", + "moment", + "soon", + "voice", + "scream", + "anything", + "stare", + "sound", + "red", + "everyone", + "hide", + "kiss", + "truth", + "death", + "beautiful", + "mine", + "blood", + "broken", + "very", + "pass", + "next", + "forget", + "tree", + "wrong", + "air", + "mother", + "understand", + "lip", + "hit", + "wall", + "memory", + "sleep", + "free", + "high", + "realize", + "school", + "might", + "skin", + "sweet", + "perfect", + "blue", + "kill", + "breath", + "dance", + "against", + "fly", + "between", + "grow", + "strong", + "under", + "listen", + "bring", + "sometimes", + "speak", + "pull", + "person", + "become", + "family", + "begin", + "ground", + "real", + "small", + "father", + "sure", + "feet", + "rest", + "young", + "finally", + "land", + "across", + "today", + "different", + "guy", + "line", + "fire", + "reason", + "reach", + "second", + "slowly", + "write", + "eat", + "smell", + "mouth", + "step", + "learn", + "three", + "floor", + "promise", + "breathe", + "darkness", + "push", + "earth", + "guess", + "save", + "song", + "above", + "along", + "both", + "color", + "house", + "almost", + "sorry", + "anymore", + "brother", + "okay", + "dear", + "game", + "fade", + "already", + "apart", + "warm", + "beauty", + "heard", + "notice", + "question", + "shine", + "began", + "piece", + "whole", + "shadow", + "secret", + "street", + "within", + "finger", + "point", + "morning", + "whisper", + "child", + "moon", + "green", + "story", + "glass", + "kid", + "silence", + "since", + "soft", + "yourself", + "empty", + "shall", + "angel", + "answer", + "baby", + "bright", + "dad", + "path", + "worry", + "hour", + "drop", + "follow", + "power", + "war", + "half", + "flow", + "heaven", + "act", + "chance", + "fact", + "least", + "tired", + "children", + "near", + "quite", + "afraid", + "rise", + "sea", + "taste", + "window", + "cover", + "nice", + "trust", + "lot", + "sad", + "cool", + "force", + "peace", + "return", + "blind", + "easy", + "ready", + "roll", + "rose", + "drive", + "held", + "music", + "beneath", + "hang", + "mom", + "paint", + "emotion", + "quiet", + "clear", + "cloud", + "few", + "pretty", + "bird", + "outside", + "paper", + "picture", + "front", + "rock", + "simple", + "anyone", + "meant", + "reality", + "road", + "sense", + "waste", + "bit", + "leaf", + "thank", + "happiness", + "meet", + "men", + "smoke", + "truly", + "decide", + "self", + "age", + "book", + "form", + "alive", + "carry", + "escape", + "damn", + "instead", + "able", + "ice", + "minute", + "throw", + "catch", + "leg", + "ring", + "course", + "goodbye", + "lead", + "poem", + "sick", + "corner", + "desire", + "known", + "problem", + "remind", + "shoulder", + "suppose", + "toward", + "wave", + "drink", + "jump", + "woman", + "pretend", + "sister", + "week", + "human", + "joy", + "crack", + "grey", + "pray", + "surprise", + "dry", + "knee", + "less", + "search", + "bleed", + "caught", + "clean", + "embrace", + "future", + "king", + "son", + "sorrow", + "chest", + "hug", + "remain", + "sat", + "worth", + "blow", + "daddy", + "final", + "parent", + "tight", + "also", + "create", + "lonely", + "safe", + "cross", + "dress", + "evil", + "silent", + "bone", + "fate", + "perhaps", + "anger", + "class", + "scar", + "snow", + "tiny", + "tonight", + "continue", + "control", + "dog", + "edge", + "mirror", + "month", + "suddenly", + "comfort", + "given", + "loud", + "quickly", + "gaze", + "plan", + "rush", + "stone", + "town", + "battle", + "ignore", + "spirit", + "stood", + "stupid", + "yours", + "brown", + "build", + "dust", + "hey", + "kept", + "pay", + "phone", + "twist", + "although", + "ball", + "beyond", + "hidden", + "nose", + "taken", + "fail", + "float", + "pure", + "somehow", + "wash", + "wrap", + "angry", + "cheek", + "creature", + "forgotten", + "heat", + "rip", + "single", + "space", + "special", + "weak", + "whatever", + "yell", + "anyway", + "blame", + "job", + "choose", + "country", + "curse", + "drift", + "echo", + "figure", + "grew", + "laughter", + "neck", + "suffer", + "worse", + "yeah", + "disappear", + "foot", + "forward", + "knife", + "mess", + "somewhere", + "stomach", + "storm", + "beg", + "idea", + "lift", + "offer", + "breeze", + "field", + "five", + "often", + "simply", + "stuck", + "win", + "allow", + "confuse", + "enjoy", + "except", + "flower", + "seek", + "strength", + "calm", + "grin", + "gun", + "heavy", + "hill", + "large", + "ocean", + "shoe", + "sigh", + "straight", + "summer", + "tongue", + "accept", + "crazy", + "everyday", + "exist", + "grass", + "mistake", + "sent", + "shut", + "surround", + "table", + "ache", + "brain", + "destroy", + "heal", + "nature", + "shout", + "sign", + "stain", + "choice", + "doubt", + "glance", + "glow", + "mountain", + "queen", + "stranger", + "throat", + "tomorrow", + "city", + "either", + "fish", + "flame", + "rather", + "shape", + "spin", + "spread", + "ash", + "distance", + "finish", + "image", + "imagine", + "important", + "nobody", + "shatter", + "warmth", + "became", + "feed", + "flesh", + "funny", + "lust", + "shirt", + "trouble", + "yellow", + "attention", + "bare", + "bite", + "money", + "protect", + "amaze", + "appear", + "born", + "choke", + "completely", + "daughter", + "fresh", + "friendship", + "gentle", + "probably", + "six", + "deserve", + "expect", + "grab", + "middle", + "nightmare", + "river", + "thousand", + "weight", + "worst", + "wound", + "barely", + "bottle", + "cream", + "regret", + "relationship", + "stick", + "test", + "crush", + "endless", + "fault", + "itself", + "rule", + "spill", + "art", + "circle", + "join", + "kick", + "mask", + "master", + "passion", + "quick", + "raise", + "smooth", + "unless", + "wander", + "actually", + "broke", + "chair", + "deal", + "favorite", + "gift", + "note", + "number", + "sweat", + "box", + "chill", + "clothes", + "lady", + "mark", + "park", + "poor", + "sadness", + "tie", + "animal", + "belong", + "brush", + "consume", + "dawn", + "forest", + "innocent", + "pen", + "pride", + "stream", + "thick", + "clay", + "complete", + "count", + "draw", + "faith", + "press", + "silver", + "struggle", + "surface", + "taught", + "teach", + "wet", + "bless", + "chase", + "climb", + "enter", + "letter", + "melt", + "metal", + "movie", + "stretch", + "swing", + "vision", + "wife", + "beside", + "crash", + "forgot", + "guide", + "haunt", + "joke", + "knock", + "plant", + "pour", + "prove", + "reveal", + "steal", + "stuff", + "trip", + "wood", + "wrist", + "bother", + "bottom", + "crawl", + "crowd", + "fix", + "forgive", + "frown", + "grace", + "loose", + "lucky", + "party", + "release", + "surely", + "survive", + "teacher", + "gently", + "grip", + "speed", + "suicide", + "travel", + "treat", + "vein", + "written", + "cage", + "chain", + "conversation", + "date", + "enemy", + "however", + "interest", + "million", + "page", + "pink", + "proud", + "sway", + "themselves", + "winter", + "church", + "cruel", + "cup", + "demon", + "experience", + "freedom", + "pair", + "pop", + "purpose", + "respect", + "shoot", + "softly", + "state", + "strange", + "bar", + "birth", + "curl", + "dirt", + "excuse", + "lord", + "lovely", + "monster", + "order", + "pack", + "pants", + "pool", + "scene", + "seven", + "shame", + "slide", + "ugly", + "among", + "blade", + "blonde", + "closet", + "creek", + "deny", + "drug", + "eternity", + "gain", + "grade", + "handle", + "key", + "linger", + "pale", + "prepare", + "swallow", + "swim", + "tremble", + "wheel", + "won", + "cast", + "cigarette", + "claim", + "college", + "direction", + "dirty", + "gather", + "ghost", + "hundred", + "loss", + "lung", + "orange", + "present", + "swear", + "swirl", + "twice", + "wild", + "bitter", + "blanket", + "doctor", + "everywhere", + "flash", + "grown", + "knowledge", + "numb", + "pressure", + "radio", + "repeat", + "ruin", + "spend", + "unknown", + "buy", + "clock", + "devil", + "early", + "false", + "fantasy", + "pound", + "precious", + "refuse", + "sheet", + "teeth", + "welcome", + "add", + "ahead", + "block", + "bury", + "caress", + "content", + "depth", + "despite", + "distant", + "marry", + "purple", + "threw", + "whenever", + "bomb", + "dull", + "easily", + "grasp", + "hospital", + "innocence", + "normal", + "receive", + "reply", + "rhyme", + "shade", + "someday", + "sword", + "toe", + "visit", + "asleep", + "bought", + "center", + "consider", + "flat", + "hero", + "history", + "ink", + "insane", + "muscle", + "mystery", + "pocket", + "reflection", + "shove", + "silently", + "smart", + "soldier", + "spot", + "stress", + "train", + "type", + "view", + "whether", + "bus", + "energy", + "explain", + "holy", + "hunger", + "inch", + "magic", + "mix", + "noise", + "nowhere", + "prayer", + "presence", + "shock", + "snap", + "spider", + "study", + "thunder", + "trail", + "admit", + "agree", + "bag", + "bang", + "bound", + "butterfly", + "cute", + "exactly", + "explode", + "familiar", + "fold", + "further", + "pierce", + "reflect", + "scent", + "selfish", + "sharp", + "sink", + "spring", + "stumble", + "universe", + "weep", + "women", + "wonderful", + "action", + "ancient", + "attempt", + "avoid", + "birthday", + "branch", + "chocolate", + "core", + "depress", + "drunk", + "especially", + "focus", + "fruit", + "honest", + "match", + "palm", + "perfectly", + "pillow", + "pity", + "poison", + "roar", + "shift", + "slightly", + "thump", + "truck", + "tune", + "twenty", + "unable", + "wipe", + "wrote", + "coat", + "constant", + "dinner", + "drove", + "egg", + "eternal", + "flight", + "flood", + "frame", + "freak", + "gasp", + "glad", + "hollow", + "motion", + "peer", + "plastic", + "root", + "screen", + "season", + "sting", + "strike", + "team", + "unlike", + "victim", + "volume", + "warn", + "weird", + "attack", + "await", + "awake", + "built", + "charm", + "crave", + "despair", + "fought", + "grant", + "grief", + "horse", + "limit", + "message", + "ripple", + "sanity", + "scatter", + "serve", + "split", + "string", + "trick", + "annoy", + "blur", + "boat", + "brave", + "clearly", + "cling", + "connect", + "fist", + "forth", + "imagination", + "iron", + "jock", + "judge", + "lesson", + "milk", + "misery", + "nail", + "naked", + "ourselves", + "poet", + "possible", + "princess", + "sail", + "size", + "snake", + "society", + "stroke", + "torture", + "toss", + "trace", + "wise", + "bloom", + "bullet", + "cell", + "check", + "cost", + "darling", + "during", + "footstep", + "fragile", + "hallway", + "hardly", + "horizon", + "invisible", + "journey", + "midnight", + "mud", + "nod", + "pause", + "relax", + "shiver", + "sudden", + "value", + "youth", + "abuse", + "admire", + "blink", + "breast", + "bruise", + "constantly", + "couple", + "creep", + "curve", + "difference", + "dumb", + "emptiness", + "gotta", + "honor", + "plain", + "planet", + "recall", + "rub", + "ship", + "slam", + "soar", + "somebody", + "tightly", + "weather", + "adore", + "approach", + "bond", + "bread", + "burst", + "candle", + "coffee", + "cousin", + "crime", + "desert", + "flutter", + "frozen", + "grand", + "heel", + "hello", + "language", + "level", + "movement", + "pleasure", + "powerful", + "random", + "rhythm", + "settle", + "silly", + "slap", + "sort", + "spoken", + "steel", + "threaten", + "tumble", + "upset", + "aside", + "awkward", + "bee", + "blank", + "board", + "button", + "card", + "carefully", + "complain", + "crap", + "deeply", + "discover", + "drag", + "dread", + "effort", + "entire", + "fairy", + "giant", + "gotten", + "greet", + "illusion", + "jeans", + "leap", + "liquid", + "march", + "mend", + "nervous", + "nine", + "replace", + "rope", + "spine", + "stole", + "terror", + "accident", + "apple", + "balance", + "boom", + "childhood", + "collect", + "demand", + "depression", + "eventually", + "faint", + "glare", + "goal", + "group", + "honey", + "kitchen", + "laid", + "limb", + "machine", + "mere", + "mold", + "murder", + "nerve", + "painful", + "poetry", + "prince", + "rabbit", + "shelter", + "shore", + "shower", + "soothe", + "stair", + "steady", + "sunlight", + "tangle", + "tease", + "treasure", + "uncle", + "begun", + "bliss", + "canvas", + "cheer", + "claw", + "clutch", + "commit", + "crimson", + "crystal", + "delight", + "doll", + "existence", + "express", + "fog", + "football", + "gay", + "goose", + "guard", + "hatred", + "illuminate", + "mass", + "math", + "mourn", + "rich", + "rough", + "skip", + "stir", + "student", + "style", + "support", + "thorn", + "tough", + "yard", + "yearn", + "yesterday", + "advice", + "appreciate", + "autumn", + "bank", + "beam", + "bowl", + "capture", + "carve", + "collapse", + "confusion", + "creation", + "dove", + "feather", + "girlfriend", + "glory", + "government", + "harsh", + "hop", + "inner", + "loser", + "moonlight", + "neighbor", + "neither", + "peach", + "pig", + "praise", + "screw", + "shield", + "shimmer", + "sneak", + "stab", + "subject", + "throughout", + "thrown", + "tower", + "twirl", + "wow", + "army", + "arrive", + "bathroom", + "bump", + "cease", + "cookie", + "couch", + "courage", + "dim", + "guilt", + "howl", + "hum", + "husband", + "insult", + "led", + "lunch", + "mock", + "mostly", + "natural", + "nearly", + "needle", + "nerd", + "peaceful", + "perfection", + "pile", + "price", + "remove", + "roam", + "sanctuary", + "serious", + "shiny", + "shook", + "sob", + "stolen", + "tap", + "vain", + "void", + "warrior", + "wrinkle", + "affection", + "apologize", + "blossom", + "bounce", + "bridge", + "cheap", + "crumble", + "decision", + "descend", + "desperately", + "dig", + "dot", + "flip", + "frighten", + "heartbeat", + "huge", + "lazy", + "lick", + "odd", + "opinion", + "process", + "puzzle", + "quietly", + "retreat", + "score", + "sentence", + "separate", + "situation", + "skill", + "soak", + "square", + "stray", + "taint", + "task", + "tide", + "underneath", + "veil", + "whistle", + "anywhere", + "bedroom", + "bid", + "bloody", + "burden", + "careful", + "compare", + "concern", + "curtain", + "decay", + "defeat", + "describe", + "double", + "dreamer", + "driver", + "dwell", + "evening", + "flare", + "flicker", + "grandma", + "guitar", + "harm", + "horrible", + "hungry", + "indeed", + "lace", + "melody", + "monkey", + "nation", + "object", + "obviously", + "rainbow", + "salt", + "scratch", + "shown", + "shy", + "stage", + "stun", + "third", + "tickle", + "useless", + "weakness", + "worship", + "worthless", + "afternoon", + "beard", + "boyfriend", + "bubble", + "busy", + "certain", + "chin", + "concrete", + "desk", + "diamond", + "doom", + "drawn", + "due", + "felicity", + "freeze", + "frost", + "garden", + "glide", + "harmony", + "hopefully", + "hunt", + "jealous", + "lightning", + "mama", + "mercy", + "peel", + "physical", + "position", + "pulse", + "punch", + "quit", + "rant", + "respond", + "salty", + "sane", + "satisfy", + "savior", + "sheep", + "slept", + "social", + "sport", + "tuck", + "utter", + "valley", + "wolf", + "aim", + "alas", + "alter", + "arrow", + "awaken", + "beaten", + "belief", + "brand", + "ceiling", + "cheese", + "clue", + "confidence", + "connection", + "daily", + "disguise", + "eager", + "erase", + "essence", + "everytime", + "expression", + "fan", + "flag", + "flirt", + "foul", + "fur", + "giggle", + "glorious", + "ignorance", + "law", + "lifeless", + "measure", + "mighty", + "muse", + "north", + "opposite", + "paradise", + "patience", + "patient", + "pencil", + "petal", + "plate", + "ponder", + "possibly", + "practice", + "slice", + "spell", + "stock", + "strife", + "strip", + "suffocate", + "suit", + "tender", + "tool", + "trade", + "velvet", + "verse", + "waist", + "witch", + "aunt", + "bench", + "bold", + "cap", + "certainly", + "click", + "companion", + "creator", + "dart", + "delicate", + "determine", + "dish", + "dragon", + "drama", + "drum", + "dude", + "everybody", + "feast", + "forehead", + "former", + "fright", + "fully", + "gas", + "hook", + "hurl", + "invite", + "juice", + "manage", + "moral", + "possess", + "raw", + "rebel", + "royal", + "scale", + "scary", + "several", + "slight", + "stubborn", + "swell", + "talent", + "tea", + "terrible", + "thread", + "torment", + "trickle", + "usually", + "vast", + "violence", + "weave", + "acid", + "agony", + "ashamed", + "awe", + "belly", + "blend", + "blush", + "character", + "cheat", + "common", + "company", + "coward", + "creak", + "danger", + "deadly", + "defense", + "define", + "depend", + "desperate", + "destination", + "dew", + "duck", + "dusty", + "embarrass", + "engine", + "example", + "explore", + "foe", + "freely", + "frustrate", + "generation", + "glove", + "guilty", + "health", + "hurry", + "idiot", + "impossible", + "inhale", + "jaw", + "kingdom", + "mention", + "mist", + "moan", + "mumble", + "mutter", + "observe", + "ode", + "pathetic", + "pattern", + "pie", + "prefer", + "puff", + "rape", + "rare", + "revenge", + "rude", + "scrape", + "spiral", + "squeeze", + "strain", + "sunset", + "suspend", + "sympathy", + "thigh", + "throne", + "total", + "unseen", + "weapon", + "weary", +} + +var n int64 = 1626 + +func IndexOf(slice []string, value string) int64 { + for p, v := range slice { + if v == value { + return int64(p) + } + } + return -1 +} + +func MnemonicEncode(message string) []string { + var out []string + + for i := 0; i < len(message); i += (len(message) / 8) { + x := message[i : i+8] + bit, _ := strconv.ParseInt(x, 16, 64) + w1 := (bit % n) + w2 := ((bit / n) + w1) % n + w3 := ((bit / n / n) + w2) % n + out = append(out, words[w1], words[w2], words[w3]) + } + return out +} + +func MnemonicDecode(wordsar []string) string { + var out string + for i := 0; i < len(wordsar); i += 3 { + word1 := wordsar[i] + word2 := wordsar[i+1] + word3 := wordsar[i+2] + w1 := IndexOf(words, word1) + w2 := IndexOf(words, word2) + w3 := IndexOf(words, word3) + + y := (w2 - w1) % n + z := (w3 - w2) % n + + // Golang handles modulo with negative numbers different then most languages + if z < 0 { + z += n + } + if y < 0 { + y += n + } + x := w1 + n*(y) + n*n*(z) + out += fmt.Sprintf("%08x", x) + } + return out +} diff --git a/ethutil/mnemonic_test.go b/ethutil/mnemonic_test.go new file mode 100644 index 000000000..ccf3f9883 --- /dev/null +++ b/ethutil/mnemonic_test.go @@ -0,0 +1,74 @@ +package ethutil + +import ( + "testing" +) + +func TestMnDecode(t *testing.T) { + words := []string{ + "ink", + "balance", + "gain", + "fear", + "happen", + "melt", + "mom", + "surface", + "stir", + "bottle", + "unseen", + "expression", + "important", + "curl", + "grant", + "fairy", + "across", + "back", + "figure", + "breast", + "nobody", + "scratch", + "worry", + "yesterday", + } + encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338" + result := MnemonicDecode(words) + if encode != result { + t.Error("We expected", encode, "got", result, "instead") + } +} +func TestMnEncode(t *testing.T) { + encode := "c61d43dc5bb7a4e754d111dae8105b6f25356492df5e50ecb33b858d94f8c338" + result := []string{ + "ink", + "balance", + "gain", + "fear", + "happen", + "melt", + "mom", + "surface", + "stir", + "bottle", + "unseen", + "expression", + "important", + "curl", + "grant", + "fairy", + "across", + "back", + "figure", + "breast", + "nobody", + "scratch", + "worry", + "yesterday", + } + words := MnemonicEncode(encode) + for i, word := range words { + if word != result[i] { + t.Error("Mnenonic does not match:", words, result) + } + } +} From c0a030ef0a3ce8342fda2a53cdafd50a271b4837 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 9 Apr 2014 14:08:18 +0200 Subject: [PATCH 029/100] Added new insruction methods --- ethchain/closure.go | 5 +++++ ethchain/contract.go | 9 +++++++++ ethchain/vm.go | 26 +++++++++++++++++++++++++- 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 2e809aa9d..e9cb2c8bc 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -17,6 +17,7 @@ type ClosureBody interface { ethutil.RlpEncodable GetMem(*big.Int) *ethutil.Value SetMem(*big.Int, *ethutil.Value) + GetInstr(*big.Int) *ethutil.Value } // Basic inline closure object which implement the 'closure' interface @@ -46,6 +47,10 @@ func (c *Closure) GetMem(x *big.Int) *ethutil.Value { return m } +func (c *Closure) GetInstr(x *big.Int) *ethutil.Value { + return c.object.GetInstr(x) +} + func (c *Closure) SetMem(x *big.Int, val *ethutil.Value) { c.object.SetMem(x, val) } diff --git a/ethchain/contract.go b/ethchain/contract.go index f7ae01753..f68dcf367 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -11,6 +11,7 @@ type Contract struct { //state *ethutil.Trie state *State address []byte + script []byte } func NewContract(address []byte, Amount *big.Int, root []byte) *Contract { @@ -45,6 +46,14 @@ func (c *Contract) GetMem(num *big.Int) *ethutil.Value { return c.Addr(nb) } +func (c *Contract) GetInstr(pc *big.Int) *ethutil.Value { + if int64(len(c.script)-1) < pc.Int64() { + return ethutil.NewValue(0) + } + + return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]}) +} + func (c *Contract) SetMem(num *big.Int, val *ethutil.Value) { addr := ethutil.BigToBytes(num, 256) c.state.trie.Update(string(addr), string(val.Encode())) diff --git a/ethchain/vm.go b/ethchain/vm.go index 98aaa603a..b4b2177bf 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -72,7 +72,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { for { step++ // Get the memory location of pc - val := closure.GetMem(pc) + val := closure.GetInstr(pc) // Get the opcode (it must be an opcode!) op := OpCode(val.Uint()) if ethutil.Config.Debug { @@ -233,13 +233,37 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // 0x10 range case oAND: + x, y := stack.Popn() + if (x.Cmp(ethutil.BigTrue) >= 0) && (y.Cmp(ethutil.BigTrue) >= 0) { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } + case oOR: + x, y := stack.Popn() + if (x.Cmp(ethutil.BigInt0) >= 0) || (y.Cmp(ethutil.BigInt0) >= 0) { + stack.Push(ethutil.BigTrue) + } else { + stack.Push(ethutil.BigFalse) + } case oXOR: + x, y := stack.Popn() + stack.Push(base.Xor(x, y)) case oBYTE: + val, th := stack.Popn() + if th.Cmp(big.NewInt(32)) < 0 { + stack.Push(big.NewInt(int64(len(val.Bytes())-1) - th.Int64())) + } else { + stack.Push(ethutil.BigFalse) + } // 0x20 range case oSHA3: + size, offset := stack.Popn() + data := mem.Get(offset.Int64(), size.Int64()) + stack.Push(ethutil.BigD(data)) // 0x30 range case oADDRESS: stack.Push(ethutil.BigD(closure.Object().Address())) From 527a3bbc2aa9cfd26bd8419d33b50adef536067d Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 9 Apr 2014 14:53:20 +0200 Subject: [PATCH 030/100] Typo fix --- ethchain/transaction_pool.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index a6265afd6..849909677 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -108,7 +108,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract } if sender.Nonce != tx.Nonce { - return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transactoin nonce is %d instead", sender.Nonce, tx.Nonce) + return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transaction nonce is %d instead", sender.Nonce, tx.Nonce) } // Get the receiver From b66a99e32dd51ddd66a81141a9a37f93144935bd Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 9 Apr 2014 08:55:39 -0400 Subject: [PATCH 031/100] Added todo --- ethchain/block_chain.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index f621965ae..2a50ef687 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -169,6 +169,8 @@ func (bc *BlockChain) ResetTillBlockHash(hash []byte) error { bc.LastBlockHash = bc.genesisBlock.Hash() bc.LastBlockNumber = 1 } else { + // TODO: Somehow this doesn't really give the right numbers, double check. + // TODO: Change logs into debug lines returnTo = bc.GetBlock(hash) bc.CurrentBlock = returnTo bc.LastBlockHash = returnTo.Hash() From 035f0ffb8ac95faa1742c0575cc9b5409ec54379 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 9 Apr 2014 15:08:10 +0200 Subject: [PATCH 032/100] Reverted changes --- ethchain/state_manager.go | 4 ++-- ethchain/transaction_pool.go | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 4b0ea2515..c33ba9272 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -114,12 +114,12 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { // contract instead of moving funds between accounts. var err error if contract := sm.procState.GetContract(tx.Recipient); contract != nil { - err = sm.Ethereum.TxPool().ProcessTransaction(tx, sm.procState, true) + err = sm.Ethereum.TxPool().ProcessTransaction(tx, block, true) if err == nil { sm.ProcessContract(contract, tx, block) } } else { - err = sm.Ethereum.TxPool().ProcessTransaction(tx, sm.procState, false) + err = sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) } if err != nil { diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 849909677..0bcfe6923 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -90,7 +90,7 @@ func (pool *TxPool) addTransaction(tx *Transaction) { // Process transaction validates the Tx and processes funds from the // sender to the recipient. -func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract bool) (err error) { +func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract bool) (err error) { defer func() { if r := recover(); r != nil { log.Println(r) @@ -98,7 +98,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract } }() // Get the sender - sender := state.GetAccount(tx.Sender()) + sender := block.state.GetAccount(tx.Sender()) // Make sure there's enough in the sender's account. Having insufficient // funds won't invalidate this transaction but simple ignores it. @@ -112,7 +112,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract } // Get the receiver - receiver := state.GetAccount(tx.Recipient) + receiver := block.state.GetAccount(tx.Recipient) sender.Nonce += 1 // Send Tx to self @@ -128,10 +128,10 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract // Add the amount to receivers account which should conclude this transaction receiver.Amount.Add(receiver.Amount, tx.Value) - state.UpdateAccount(tx.Recipient, receiver) + block.state.UpdateAccount(tx.Recipient, receiver) } - state.UpdateAccount(tx.Sender(), sender) + block.state.UpdateAccount(tx.Sender(), sender) log.Printf("[TXPL] Processed Tx %x\n", tx.Hash()) From a83db489dfddb16786b1de3d28aee4a4af9e12d5 Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 9 Apr 2014 09:48:17 -0400 Subject: [PATCH 033/100] Fix transaction on new blocks --- ethminer/miner.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ethminer/miner.go b/ethminer/miner.go index 60af3ab31..d84977342 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -105,6 +105,7 @@ func (miner *Miner) listener() { if found == false { log.Println("[MINER] We did not know about this transaction, adding") miner.txs = append(miner.txs, tx) + miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) miner.block.SetTransactions(miner.txs) } else { log.Println("[MINER] We already had this transaction, ignoring") From 6d28bf534f0fabb5740ef035b829e5edc4dc3131 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 9 Apr 2014 16:00:28 +0200 Subject: [PATCH 034/100] Added a length for copy --- ethchain/state_manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index c33ba9272..4c001a387 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -288,7 +288,7 @@ func (sm *StateManager) AccumelateRewards(block *Block) error { // Reward amount of ether to the coinbase address addr.AddFee(CalculateBlockReward(block, len(block.Uncles))) - var acc []byte + acc := make([]byte, len(block.Coinbase)) copy(acc, block.Coinbase) sm.procState.UpdateAccount(acc, addr) From 03e139d23b4350172b69d81c2c5690d0da282fb7 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 9 Apr 2014 16:04:36 +0200 Subject: [PATCH 035/100] Switched variable names --- ethchain/state_manager.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 4c001a387..0f8ef19a7 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -284,13 +284,13 @@ func CalculateUncleReward(block *Block) *big.Int { func (sm *StateManager) AccumelateRewards(block *Block) error { // Get the coinbase rlp data - addr := sm.procState.GetAccount(block.Coinbase) + acc := sm.procState.GetAccount(block.Coinbase) // Reward amount of ether to the coinbase address - addr.AddFee(CalculateBlockReward(block, len(block.Uncles))) + acc.AddFee(CalculateBlockReward(block, len(block.Uncles))) - acc := make([]byte, len(block.Coinbase)) - copy(acc, block.Coinbase) - sm.procState.UpdateAccount(acc, addr) + addr := make([]byte, len(block.Coinbase)) + copy(addr, block.Coinbase) + sm.procState.UpdateAccount(addr, acc) for _, uncle := range block.Uncles { uncleAddr := sm.procState.GetAccount(uncle.Coinbase) From 272b135b74931fd159d4e50a2328ea32a73f787c Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 9 Apr 2014 10:40:55 -0400 Subject: [PATCH 036/100] One more line of comment --- ethutil/mnemonic.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ethutil/mnemonic.go b/ethutil/mnemonic.go index d21c20f00..cc58de84a 100644 --- a/ethutil/mnemonic.go +++ b/ethutil/mnemonic.go @@ -5,6 +5,7 @@ import ( "strconv" ) +// Electrum word list var words []string = []string{ "like", "just", @@ -1673,6 +1674,7 @@ func MnemonicDecode(wordsar []string) string { z := (w3 - w2) % n // Golang handles modulo with negative numbers different then most languages + // The modulo can be negative, we don't want that. if z < 0 { z += n } From 5714a82778c688a332f4b42bee28e99fb2b30e0b Mon Sep 17 00:00:00 2001 From: Maran Date: Wed, 9 Apr 2014 11:06:30 -0400 Subject: [PATCH 037/100] Small tweaks to mnemonic --- ethutil/mnemonic.go | 106 ++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/ethutil/mnemonic.go b/ethutil/mnemonic.go index cc58de84a..00f089e3b 100644 --- a/ethutil/mnemonic.go +++ b/ethutil/mnemonic.go @@ -5,6 +5,60 @@ import ( "strconv" ) +// TODO: See if we can refactor this into a shared util lib if we need it multiple times +func IndexOf(slice []string, value string) int64 { + for p, v := range slice { + if v == value { + return int64(p) + } + } + return -1 +} + +func MnemonicEncode(message string) []string { + var out []string + n := int64(len(words)) + + for i := 0; i < len(message); i += (len(message) / 8) { + x := message[i : i+8] + bit, _ := strconv.ParseInt(x, 16, 64) + w1 := (bit % n) + w2 := ((bit / n) + w1) % n + w3 := ((bit / n / n) + w2) % n + out = append(out, words[w1], words[w2], words[w3]) + } + return out +} + +func MnemonicDecode(wordsar []string) string { + var out string + n := int64(len(words)) + + for i := 0; i < len(wordsar); i += 3 { + word1 := wordsar[i] + word2 := wordsar[i+1] + word3 := wordsar[i+2] + w1 := IndexOf(words, word1) + w2 := IndexOf(words, word2) + w3 := IndexOf(words, word3) + + y := (w2 - w1) % n + z := (w3 - w2) % n + + // Golang handles modulo with negative numbers different then most languages + // The modulo can be negative, we don't want that. + if z < 0 { + z += n + } + if y < 0 { + y += n + } + x := w1 + n*(y) + n*n*(z) + out += fmt.Sprintf("%08x", x) + } + return out +} + // Electrum word list var words []string = []string{ "like", @@ -1634,55 +1688,3 @@ var words []string = []string{ "weapon", "weary", } - -var n int64 = 1626 - -func IndexOf(slice []string, value string) int64 { - for p, v := range slice { - if v == value { - return int64(p) - } - } - return -1 -} - -func MnemonicEncode(message string) []string { - var out []string - - for i := 0; i < len(message); i += (len(message) / 8) { - x := message[i : i+8] - bit, _ := strconv.ParseInt(x, 16, 64) - w1 := (bit % n) - w2 := ((bit / n) + w1) % n - w3 := ((bit / n / n) + w2) % n - out = append(out, words[w1], words[w2], words[w3]) - } - return out -} - -func MnemonicDecode(wordsar []string) string { - var out string - for i := 0; i < len(wordsar); i += 3 { - word1 := wordsar[i] - word2 := wordsar[i+1] - word3 := wordsar[i+2] - w1 := IndexOf(words, word1) - w2 := IndexOf(words, word2) - w3 := IndexOf(words, word3) - - y := (w2 - w1) % n - z := (w3 - w2) % n - - // Golang handles modulo with negative numbers different then most languages - // The modulo can be negative, we don't want that. - if z < 0 { - z += n - } - if y < 0 { - y += n - } - x := w1 + n*(y) + n*n*(z) - out += fmt.Sprintf("%08x", x) - } - return out -} From 4f2e9c2640eaa962d085db329221bfd6f1a1799e Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 9 Apr 2014 12:27:25 -0400 Subject: [PATCH 038/100] Check for nil --- ethutil/rlp.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ethutil/rlp.go b/ethutil/rlp.go index e6c75696e..d95ace425 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -186,7 +186,12 @@ func Encode(object interface{}) []byte { case byte: buff.Write(Encode(big.NewInt(int64(t)))) case *big.Int: - buff.Write(Encode(t.Bytes())) + // Not sure how this is possible while we check for + if t == nil { + buff.WriteByte(0xc0) + } else { + buff.Write(Encode(t.Bytes())) + } case []byte: if len(t) == 1 && t[0] <= 0x7f { buff.Write(t) From e09f0a5f2c1e1b46226656dbac9a4ae10e0dcd14 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 9 Apr 2014 12:27:54 -0400 Subject: [PATCH 039/100] Split code for contracts --- ethchain/closure.go | 22 ++++++++++++++-------- ethchain/contract.go | 22 ++++++++++++++-------- ethchain/state_manager.go | 2 +- ethchain/vm.go | 4 ++-- ethchain/vm_test.go | 3 ++- 5 files changed, 33 insertions(+), 20 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index e9cb2c8bc..d1fac0f43 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -12,18 +12,18 @@ type Callee interface { Address() []byte } -type ClosureBody interface { +type Reference interface { Callee ethutil.RlpEncodable GetMem(*big.Int) *ethutil.Value SetMem(*big.Int, *ethutil.Value) - GetInstr(*big.Int) *ethutil.Value } // Basic inline closure object which implement the 'closure' interface type Closure struct { callee Callee - object ClosureBody + object Reference + Script []byte State *State Gas *big.Int @@ -33,8 +33,8 @@ type Closure struct { } // Create a new closure for the given data items -func NewClosure(callee Callee, object ClosureBody, state *State, gas, val *big.Int) *Closure { - return &Closure{callee, object, state, gas, val, nil} +func NewClosure(callee Callee, object Reference, script []byte, state *State, gas, val *big.Int) *Closure { + return &Closure{callee, object, script, state, gas, val, nil} } // Retuns the x element in data slice @@ -47,8 +47,14 @@ func (c *Closure) GetMem(x *big.Int) *ethutil.Value { return m } -func (c *Closure) GetInstr(x *big.Int) *ethutil.Value { - return c.object.GetInstr(x) +func (c *Closure) Get(x *big.Int) *ethutil.Value { + return c.Gets(x, big.NewInt(1)) +} + +func (c *Closure) Gets(x, y *big.Int) *ethutil.Value { + partial := c.Script[x.Int64() : x.Int64()+y.Int64()] + + return ethutil.NewValue(partial) } func (c *Closure) SetMem(x *big.Int, val *ethutil.Value) { @@ -86,7 +92,7 @@ func (c *Closure) ReturnGas(gas *big.Int, state *State) { c.Gas.Add(c.Gas, gas) } -func (c *Closure) Object() ClosureBody { +func (c *Closure) Object() Reference { return c.object } diff --git a/ethchain/contract.go b/ethchain/contract.go index f68dcf367..113d067a4 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -9,9 +9,10 @@ type Contract struct { Amount *big.Int Nonce uint64 //state *ethutil.Trie - state *State - address []byte - script []byte + state *State + address []byte + script []byte + initScript []byte } func NewContract(address []byte, Amount *big.Int, root []byte) *Contract { @@ -88,12 +89,17 @@ func MakeContract(tx *Transaction, state *State) *Contract { value := tx.Value contract := NewContract(addr, value, []byte("")) state.trie.Update(string(addr), string(contract.RlpEncode())) - for i, val := range tx.Data { - if len(val) > 0 { - bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256) - contract.state.trie.Update(string(bytNum), string(ethutil.Encode(val))) + contract.script = tx.Data + contract.initScript = tx.Init + + /* + for i, val := range tx.Data { + if len(val) > 0 { + bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256) + contract.state.trie.Update(string(bytNum), string(ethutil.Encode(val))) + } } - } + */ state.trie.Update(string(addr), string(contract.RlpEncode())) return contract diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 95e46e41d..7a2a762b2 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -311,7 +311,7 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo }() caller := sm.procState.GetAccount(tx.Sender()) - closure := NewClosure(caller, contract, sm.procState, tx.Gas, tx.Value) + closure := NewClosure(caller, contract, contract.script, sm.procState, tx.Gas, tx.Value) vm := NewVm(sm.procState, RuntimeVars{ origin: caller.Address(), blockNumber: block.BlockInfo().Number, diff --git a/ethchain/vm.go b/ethchain/vm.go index b4b2177bf..a6a02dc9f 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -72,7 +72,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { for { step++ // Get the memory location of pc - val := closure.GetInstr(pc) + val := closure.Get(pc) // Get the opcode (it must be an opcode!) op := OpCode(val.Uint()) if ethutil.Config.Debug { @@ -357,7 +357,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // Fetch the contract which will serve as the closure body contract := vm.state.GetContract(addr.Bytes()) // Create a new callable closure - closure := NewClosure(closure, contract, vm.state, gas, value) + closure := NewClosure(closure, contract, contract.script, vm.state, gas, value) // Executer the closure and get the return value (if any) ret := closure.Call(vm, args) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 85ec4c693..745005b09 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -112,7 +112,8 @@ func TestRun4(t *testing.T) { // Contract addr as test address account := NewAccount(ContractAddr, big.NewInt(10000000)) - callerClosure := NewClosure(account, MakeContract(callerTx, state), state, big.NewInt(1000000000), new(big.Int)) + c := MakeContract(callerTx, state) + callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), new(big.Int)) vm := NewVm(state, RuntimeVars{ origin: account.Address(), From 720521ed4a28c8a1b74bedd03e82bf4f887c9cb5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 9 Apr 2014 12:28:16 -0400 Subject: [PATCH 040/100] Changed how txs define their data & added init field --- ethchain/transaction.go | 66 ++++++++++++++---------------------- ethchain/transaction_test.go | 53 ----------------------------- 2 files changed, 25 insertions(+), 94 deletions(-) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 506e3c159..b359c9151 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -14,7 +14,8 @@ type Transaction struct { Value *big.Int Gas *big.Int Gasprice *big.Int - Data []string + Data []byte + Init []byte v byte r, s []byte @@ -22,11 +23,11 @@ type Transaction struct { contractCreation bool } -func NewContractCreationTx(value, gasprice *big.Int, data []string) *Transaction { +func NewContractCreationTx(value, gasprice *big.Int, data []byte) *Transaction { return &Transaction{Value: value, Gasprice: gasprice, Data: data, contractCreation: true} } -func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []string) *Transaction { +func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []byte) *Transaction { return &Transaction{Recipient: to, Value: value, Gasprice: gasprice, Gas: gas, Data: data} } @@ -45,19 +46,12 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction { } func (tx *Transaction) Hash() []byte { - data := make([]interface{}, len(tx.Data)) - for i, val := range tx.Data { - data[i] = val + data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice, tx.Gas, tx.Recipient, string(tx.Data)} + if tx.contractCreation { + data = append(data, string(tx.Init)) } - preEnc := []interface{}{ - tx.Nonce, - tx.Recipient, - tx.Value, - data, - } - - return ethutil.Sha3Bin(ethutil.Encode(preEnc)) + return ethutil.Sha3Bin(ethutil.NewValue(data).Encode()) } func (tx *Transaction) IsContract() bool { @@ -110,15 +104,17 @@ func (tx *Transaction) Sign(privk []byte) error { return nil } +// [ NONCE, VALUE, GASPRICE, GAS, TO, DATA, V, R, S ] +// [ NONCE, VALUE, GASPRICE, GAS, 0, CODE, INIT, V, R, S ] func (tx *Transaction) RlpData() interface{} { - data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice} + data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice, tx.Gas, tx.Recipient, tx.Data} - if !tx.contractCreation { - data = append(data, tx.Recipient, tx.Gas) + if tx.contractCreation { + data = append(data, tx.Init) } - d := ethutil.NewSliceValue(tx.Data).Slice() + //d := ethutil.NewSliceValue(tx.Data).Slice() - return append(data, d, tx.v, tx.r, tx.s) + return append(data, tx.v, tx.r, tx.s) } func (tx *Transaction) RlpValue() *ethutil.Value { @@ -137,31 +133,19 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { tx.Nonce = decoder.Get(0).Uint() tx.Value = decoder.Get(1).BigInt() tx.Gasprice = decoder.Get(2).BigInt() + tx.Gas = decoder.Get(3).BigInt() + tx.Recipient = decoder.Get(4).Bytes() + tx.Data = decoder.Get(5).Bytes() - // If the 4th item is a list(slice) this tx - // is a contract creation tx - if decoder.Get(3).IsList() { - d := decoder.Get(3) - tx.Data = make([]string, d.Len()) - for i := 0; i < d.Len(); i++ { - tx.Data[i] = d.Get(i).Str() - } - - tx.v = byte(decoder.Get(4).Uint()) - tx.r = decoder.Get(5).Bytes() - tx.s = decoder.Get(6).Bytes() - + // If the list is of length 10 it's a contract creation tx + if decoder.Len() == 10 { tx.contractCreation = true + tx.Init = decoder.Get(6).Bytes() + + tx.v = byte(decoder.Get(7).Uint()) + tx.r = decoder.Get(8).Bytes() + tx.s = decoder.Get(9).Bytes() } else { - tx.Recipient = decoder.Get(3).Bytes() - tx.Gas = decoder.Get(4).BigInt() - - d := decoder.Get(5) - tx.Data = make([]string, d.Len()) - for i := 0; i < d.Len(); i++ { - tx.Data[i] = d.Get(i).Str() - } - tx.v = byte(decoder.Get(6).Uint()) tx.r = decoder.Get(7).Bytes() tx.s = decoder.Get(8).Bytes() diff --git a/ethchain/transaction_test.go b/ethchain/transaction_test.go index a49768aea..3603fd8a7 100644 --- a/ethchain/transaction_test.go +++ b/ethchain/transaction_test.go @@ -1,54 +1 @@ package ethchain - -import ( - "encoding/hex" - "math/big" - "testing" -) - -func TestAddressRetrieval(t *testing.T) { - // TODO - // 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f - key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4") - - tx := &Transaction{ - Nonce: 0, - Recipient: ZeroHash160, - Value: big.NewInt(0), - Data: nil, - } - //fmt.Printf("rlp %x\n", tx.RlpEncode()) - //fmt.Printf("sha rlp %x\n", tx.Hash()) - - tx.Sign(key) - - //fmt.Printf("hex tx key %x\n", tx.PublicKey()) - //fmt.Printf("seder %x\n", tx.Sender()) -} - -func TestAddressRetrieval2(t *testing.T) { - // TODO - // 88f9b82462f6c4bf4a0fb15e5c3971559a316e7f - key, _ := hex.DecodeString("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4") - addr, _ := hex.DecodeString("944400f4b88ac9589a0f17ed4671da26bddb668b") - tx := &Transaction{ - Nonce: 0, - Recipient: addr, - Value: big.NewInt(1000), - Data: nil, - } - tx.Sign(key) - //data, _ := hex.DecodeString("f85d8094944400f4b88ac9589a0f17ed4671da26bddb668b8203e8c01ca0363b2a410de00bc89be40f468d16e70e543b72191fbd8a684a7c5bef51dc451fa02d8ecf40b68f9c64ed623f6ee24c9c878943b812e1e76bd73ccb2bfef65579e7") - //tx := NewTransactionFromData(data) - /* - fmt.Println(tx.RlpValue()) - - fmt.Printf("rlp %x\n", tx.RlpEncode()) - fmt.Printf("sha rlp %x\n", tx.Hash()) - - //tx.Sign(key) - - fmt.Printf("hex tx key %x\n", tx.PublicKey()) - fmt.Printf("seder %x\n", tx.Sender()) - */ -} From 0fccbeabcc3b8c110ce3712e5488ad99245f92ee Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 9 Apr 2014 12:28:34 -0400 Subject: [PATCH 041/100] No longer return a list, but raw bytes --- ethutil/parsing.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 16ed2d06d..a9d50e425 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -131,13 +131,14 @@ func Instr(instr string) (int, []string, error) { // Script compilation functions // Compiles strings to machine code -func Assemble(instructions ...interface{}) (script []string) { - script = make([]string, len(instructions)) +func Assemble(instructions ...interface{}) (script []byte) { + //script = make([]string, len(instructions)) - for i, val := range instructions { + for _, val := range instructions { instr, _ := CompileInstr(val) - script[i] = string(instr) + //script[i] = string(instr) + script = append(script, instr...) } return From 6a530ea3717e592407737c6cd2ebeba0200c9cd8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 10 Apr 2014 14:40:12 -0400 Subject: [PATCH 042/100] Call fixed --- ethchain/closure.go | 4 ++++ ethchain/contract.go | 4 +++- ethchain/vm.go | 30 ++++++++++++++++++------------ ethchain/vm_test.go | 23 ++++++++++++++++++++--- ethutil/parsing.go | 28 +++++++--------------------- 5 files changed, 52 insertions(+), 37 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index d1fac0f43..8e57a0d03 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -52,6 +52,10 @@ func (c *Closure) Get(x *big.Int) *ethutil.Value { } func (c *Closure) Gets(x, y *big.Int) *ethutil.Value { + if x.Int64() > int64(len(c.Script)) || y.Int64() > int64(len(c.Script)) { + return ethutil.NewValue(0) + } + partial := c.Script[x.Int64() : x.Int64()+y.Int64()] return ethutil.NewValue(partial) diff --git a/ethchain/contract.go b/ethchain/contract.go index 113d067a4..e99e413f7 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -70,7 +70,7 @@ func (c *Contract) Address() []byte { } func (c *Contract) RlpEncode() []byte { - return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root}) + return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root, c.script, c.initScript}) } func (c *Contract) RlpDecode(data []byte) { @@ -79,6 +79,8 @@ func (c *Contract) RlpDecode(data []byte) { c.Amount = decoder.Get(0).BigInt() c.Nonce = decoder.Get(1).Uint() c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) + c.script = decoder.Get(3).Bytes() + c.initScript = decoder.Get(4).Bytes() } func MakeContract(tx *Transaction, state *State) *Contract { diff --git a/ethchain/vm.go b/ethchain/vm.go index a6a02dc9f..f94425d2d 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -2,7 +2,7 @@ package ethchain import ( _ "bytes" - "fmt" + _ "fmt" "github.com/ethereum/eth-go/ethutil" _ "github.com/obscuren/secp256k1-go" _ "math" @@ -301,9 +301,14 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // 0x50 range case oPUSH: // Push PC+1 on to the stack pc.Add(pc, ethutil.Big1) + //val := closure.GetMem(pc).BigInt() + data := closure.Gets(pc, big.NewInt(32)) + val := ethutil.BigD(data.Bytes()) - val := closure.GetMem(pc).BigInt() + // Push value to stack stack.Push(val) + + pc.Add(pc, big.NewInt(31)) case oPOP: stack.Pop() case oDUP: @@ -343,17 +348,16 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { stack.Push(big.NewInt(int64(mem.Len()))) // 0x60 range case oCALL: - // Pop return size and offset - retSize, retOffset := stack.Popn() - // Pop input size and offset - inSize, inOffset := stack.Popn() - fmt.Println(inSize, inOffset) - // Get the arguments from the memory - args := mem.Get(inOffset.Int64(), inSize.Int64()) - // Pop gas and value of the stack. - gas, value := stack.Popn() // Closure addr addr := stack.Pop() + // Pop gas and value of the stack. + gas, value := stack.Popn() + // Pop input size and offset + inSize, inOffset := stack.Popn() + // Pop return size and offset + retSize, retOffset := stack.Popn() + // Get the arguments from the memory + args := mem.Get(inOffset.Int64(), inSize.Int64()) // Fetch the contract which will serve as the closure body contract := vm.state.GetContract(addr.Bytes()) // Create a new callable closure @@ -385,7 +389,9 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { break out */ default: - ethutil.Config.Log.Debugln("Invalid opcode", op) + ethutil.Config.Log.Debugf("Invalid opcode %x\n", op) + + return closure.Return(nil) } pc.Add(pc, ethutil.Big1) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 745005b09..65113ff57 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -83,6 +83,21 @@ func TestRun4(t *testing.T) { state := NewState(ethutil.NewTrie(db, "")) asm, err := mutan.Compile(strings.NewReader(` + int32 a = 10 + int32 b = 20 + if a > b { + int32 c = this.caller() + } + exit() + `), false) + script := ethutil.Assemble(asm...) + tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script) + addr := tx.Hash()[12:] + contract := MakeContract(tx, state) + state.UpdateContract(contract) + fmt.Printf("%x\n", addr) + + asm, err = mutan.Compile(strings.NewReader(` int32 a = 10 int32 b = 10 if a == b { @@ -97,9 +112,9 @@ func TestRun4(t *testing.T) { store[a] = 20 store[b] = this.caller() - int8[10] ret - int8[10] arg - call(1234, 0, 100000000, arg, ret) + int8 ret = 0 + int8 arg = 10 + call(938726394128221156290138488023434115948430767407, 0, 100000000, arg, ret) `), false) if err != nil { fmt.Println(err) @@ -113,6 +128,8 @@ func TestRun4(t *testing.T) { // Contract addr as test address account := NewAccount(ContractAddr, big.NewInt(10000000)) c := MakeContract(callerTx, state) + //fmt.Println(c.script[230:240]) + //fmt.Println(c.script) callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), new(big.Int)) vm := NewVm(state, RuntimeVars{ diff --git a/ethutil/parsing.go b/ethutil/parsing.go index a9d50e425..0de396654 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -1,8 +1,8 @@ package ethutil import ( + _ "fmt" "math/big" - "strconv" ) // Op codes @@ -98,11 +98,16 @@ func CompileInstr(s interface{}) ([]byte, error) { // Assume regular bytes during compilation if !success { num.SetBytes([]byte(str)) + } else { + // tmp fix for 32 bytes + n := BigToBytes(num, 256) + return n, nil } return num.Bytes(), nil case int: - return big.NewInt(int64(s.(int))).Bytes(), nil + num := BigToBytes(big.NewInt(int64(s.(int))), 256) + return num, nil case []byte: return BigD(s.([]byte)).Bytes(), nil } @@ -110,25 +115,6 @@ func CompileInstr(s interface{}) ([]byte, error) { return nil, nil } -func Instr(instr string) (int, []string, error) { - - base := new(big.Int) - base.SetString(instr, 0) - - args := make([]string, 7) - for i := 0; i < 7; i++ { - // int(int(val) / int(math.Pow(256,float64(i)))) % 256 - exp := BigPow(256, i) - num := new(big.Int) - num.Div(base, exp) - - args[i] = num.Mod(num, big.NewInt(256)).String() - } - op, _ := strconv.Atoi(args[0]) - - return op, args[1:7], nil -} - // Script compilation functions // Compiles strings to machine code func Assemble(instructions ...interface{}) (script []byte) { From 969e748dce5562fc543990b6911d53ab699e393e Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 10 Apr 2014 15:30:14 -0400 Subject: [PATCH 043/100] Call fixed --- ethchain/vm_test.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 65113ff57..dc74422cc 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -119,7 +119,7 @@ func TestRun4(t *testing.T) { if err != nil { fmt.Println(err) } - //asm = append(asm, "LOG") + asm = append(asm, "LOG") fmt.Println(asm) callerScript := ethutil.Assemble(asm...) @@ -128,8 +128,6 @@ func TestRun4(t *testing.T) { // Contract addr as test address account := NewAccount(ContractAddr, big.NewInt(10000000)) c := MakeContract(callerTx, state) - //fmt.Println(c.script[230:240]) - //fmt.Println(c.script) callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), new(big.Int)) vm := NewVm(state, RuntimeVars{ From 891f7259091cba0fe5e8c9370e7b0b1055b56683 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 10 Apr 2014 18:14:19 -0400 Subject: [PATCH 044/100] Added better address format --- ethchain/stack.go | 1 + ethchain/vm.go | 11 ++++++++++- ethchain/vm_test.go | 5 +++-- ethutil/parsing.go | 5 ++++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ethchain/stack.go b/ethchain/stack.go index e3fc4b684..0dadd15e5 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -55,6 +55,7 @@ const ( // 0x50 range - 'storage' and execution oPUSH = 0x50 + oPUSH20 = 0x80 oPOP = 0x51 oDUP = 0x52 oSWAP = 0x53 diff --git a/ethchain/vm.go b/ethchain/vm.go index f94425d2d..dd99ee790 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -301,7 +301,6 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // 0x50 range case oPUSH: // Push PC+1 on to the stack pc.Add(pc, ethutil.Big1) - //val := closure.GetMem(pc).BigInt() data := closure.Gets(pc, big.NewInt(32)) val := ethutil.BigD(data.Bytes()) @@ -309,6 +308,16 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { stack.Push(val) pc.Add(pc, big.NewInt(31)) + case oPUSH20: + pc.Add(pc, ethutil.Big1) + data := closure.Gets(pc, big.NewInt(20)) + val := ethutil.BigD(data.Bytes()) + + // Push value to stack + stack.Push(val) + + pc.Add(pc, big.NewInt(19)) + case oPOP: stack.Pop() case oDUP: diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index dc74422cc..923d3526c 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -114,12 +114,13 @@ func TestRun4(t *testing.T) { int8 ret = 0 int8 arg = 10 - call(938726394128221156290138488023434115948430767407, 0, 100000000, arg, ret) + addr address = "a46df28529eb8aa8b8c025b0b413c5f4b688352f" + call(address, 0, 100000000, arg, ret) `), false) if err != nil { fmt.Println(err) } - asm = append(asm, "LOG") + //asm = append(asm, "LOG") fmt.Println(asm) callerScript := ethutil.Assemble(asm...) diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 0de396654..278414982 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -51,7 +51,10 @@ var OpCodes = map[string]byte{ "GASLIMIT": 0x45, // 0x50 range - 'storage' and execution - "PUSH": 0x50, + "PUSH": 0x50, + + "PUSH20": 0x80, + "POP": 0x51, "DUP": 0x52, "SWAP": 0x53, From afc92fb7d799a4085d2256a7106ee9f7b9ea2f9e Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 10 Apr 2014 18:32:54 -0400 Subject: [PATCH 045/100] Added better address format --- ethchain/vm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 923d3526c..55fb71dbe 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -120,7 +120,7 @@ func TestRun4(t *testing.T) { if err != nil { fmt.Println(err) } - //asm = append(asm, "LOG") + asm = append(asm, "LOG") fmt.Println(asm) callerScript := ethutil.Assemble(asm...) From 25dd46061fc3b732056ea87fe4a9696e160179cc Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 10 Apr 2014 21:03:14 -0400 Subject: [PATCH 046/100] Added push20 --- ethchain/stack.go | 2 +- ethchain/vm_test.go | 24 +++++++++++------------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/ethchain/stack.go b/ethchain/stack.go index 0dadd15e5..d475f2f8e 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -251,7 +251,7 @@ func (m *Memory) Print() { if len(m.store) > 0 { addr := 0 for i := 0; i+32 <= len(m.store); i += 32 { - fmt.Printf("%03d %v\n", addr, m.store[i:i+32]) + fmt.Printf("%03d: % x\n", addr, m.store[i:i+32]) addr++ } } else { diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 55fb71dbe..4075dfbc6 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -98,24 +98,22 @@ func TestRun4(t *testing.T) { fmt.Printf("%x\n", addr) asm, err = mutan.Compile(strings.NewReader(` - int32 a = 10 - int32 b = 10 - if a == b { - int32 c = 10 - if c == 10 { - int32 d = 1000 - int32 e = 10 - } + // Check if there's any cash in the initial store + if store[1000] == 0 { + store[1000] = 10^20 } - store[0] = 20 - store[a] = 20 - store[b] = this.caller() + store[1001] = this.value() * 20 + store[this.origin()] = store[this.origin()] + 1000 + + if store[1001] > 20 { + store[1001] = 10^50 + } int8 ret = 0 int8 arg = 10 - addr address = "a46df28529eb8aa8b8c025b0b413c5f4b688352f" - call(address, 0, 100000000, arg, ret) + store[1002] = "a46df28529eb8aa8b8c025b0b413c5f4b688352f" + call(store[1002], 0, 100000000, arg, ret) `), false) if err != nil { fmt.Println(err) From ca747f268800590ee855b1ce593b61e95d073311 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 11 Apr 2014 08:28:30 -0400 Subject: [PATCH 047/100] Added the possibility for debug hooks during closure call --- ethchain/closure.go | 6 +++-- ethchain/state_manager.go | 2 +- ethchain/vm.go | 47 +++++---------------------------------- ethchain/vm_test.go | 2 +- 4 files changed, 12 insertions(+), 45 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 8e57a0d03..3d15f2a99 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -69,10 +69,12 @@ func (c *Closure) Address() []byte { return c.object.Address() } -func (c *Closure) Call(vm *Vm, args []byte) []byte { +type DebugHook func(op OpCode) + +func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) []byte { c.Args = args - return vm.RunClosure(c) + return vm.RunClosure(c, hook) } func (c *Closure) Return(ret []byte) []byte { diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 3043c3d15..111d2c019 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -327,7 +327,7 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo // XXX Tx data? Could be just an argument to the closure instead txData: nil, }) - closure.Call(vm, nil) + closure.Call(vm, nil, nil) // Update the account (refunds) sm.procState.UpdateAccount(tx.Sender(), caller) diff --git a/ethchain/vm.go b/ethchain/vm.go index dd99ee790..dce972cc7 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -48,7 +48,7 @@ func NewVm(state *State, vars RuntimeVars) *Vm { var Pow256 = ethutil.BigPow(2, 256) -func (vm *Vm) RunClosure(closure *Closure) []byte { +func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // If the amount of gas supplied is less equal to 0 if closure.Gas.Cmp(big.NewInt(0)) <= 0 { // TODO Do something @@ -372,7 +372,7 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { // Create a new callable closure closure := NewClosure(closure, contract, contract.script, vm.state, gas, value) // Executer the closure and get the return value (if any) - ret := closure.Call(vm, args) + ret := closure.Call(vm, args, hook) mem.Set(retOffset.Int64(), retSize.Int64(), ret) case oRETURN: @@ -404,44 +404,9 @@ func (vm *Vm) RunClosure(closure *Closure) []byte { } pc.Add(pc, ethutil.Big1) + + if hook != nil { + hook(op) + } } } - -/* -func makeInlineTx(addr []byte, value, from, length *big.Int, contract *Contract, state *State) { - ethutil.Config.Log.Debugf(" => creating inline tx %x %v %v %v", addr, value, from, length) - j := int64(0) - dataItems := make([]string, int(length.Uint64())) - for i := from.Int64(); i < length.Int64(); i++ { - dataItems[j] = contract.GetMem(big.NewInt(j)).Str() - j++ - } - - tx := NewTransaction(addr, value, dataItems) - if tx.IsContract() { - contract := MakeContract(tx, state) - state.UpdateContract(contract) - } else { - account := state.GetAccount(tx.Recipient) - account.Amount.Add(account.Amount, tx.Value) - state.UpdateAccount(tx.Recipient, account) - } -} - -// Returns an address from the specified contract's address -func contractMemory(state *State, contractAddr []byte, memAddr *big.Int) *big.Int { - contract := state.GetContract(contractAddr) - if contract == nil { - log.Panicf("invalid contract addr %x", contractAddr) - } - val := state.trie.Get(memAddr.String()) - - // decode the object as a big integer - decoder := ethutil.NewValueFromBytes([]byte(val)) - if decoder.IsNil() { - return ethutil.BigFalse - } - - return decoder.BigInt() -} -*/ diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 4075dfbc6..5a1419c0f 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -139,5 +139,5 @@ func TestRun4(t *testing.T) { // XXX Tx data? Could be just an argument to the closure instead txData: nil, }) - callerClosure.Call(vm, nil) + callerClosure.Call(vm, nil, nil) } From 116516158da637cde50d27d600db6661732fc402 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 11 Apr 2014 13:29:57 -0400 Subject: [PATCH 048/100] Renamed --- ethchain/closure.go | 4 +- ethchain/contract.go | 8 ++++ ethchain/stack.go | 8 ++++ ethchain/state_manager.go | 14 +++--- ethchain/vm.go | 98 ++++++++++++++++++++++++++++++--------- ethchain/vm_test.go | 29 +++++++++--- 6 files changed, 124 insertions(+), 37 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 3d15f2a99..de2196499 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -52,7 +52,7 @@ func (c *Closure) Get(x *big.Int) *ethutil.Value { } func (c *Closure) Gets(x, y *big.Int) *ethutil.Value { - if x.Int64() > int64(len(c.Script)) || y.Int64() > int64(len(c.Script)) { + if x.Int64() >= int64(len(c.Script)) || y.Int64() >= int64(len(c.Script)) { return ethutil.NewValue(0) } @@ -69,7 +69,7 @@ func (c *Closure) Address() []byte { return c.object.Address() } -type DebugHook func(op OpCode) +type DebugHook func(op OpCode, mem *Memory, stack *Stack) func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) []byte { c.Args = args diff --git a/ethchain/contract.go b/ethchain/contract.go index e99e413f7..af348667c 100644 --- a/ethchain/contract.go +++ b/ethchain/contract.go @@ -69,6 +69,14 @@ func (c *Contract) Address() []byte { return c.address } +func (c *Contract) Script() []byte { + return c.script +} + +func (c *Contract) Init() []byte { + return c.initScript +} + func (c *Contract) RlpEncode() []byte { return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root, c.script, c.initScript}) } diff --git a/ethchain/stack.go b/ethchain/stack.go index d475f2f8e..2aca0a350 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -173,6 +173,10 @@ func NewStack() *Stack { return &Stack{} } +func (st *Stack) Data() []*big.Int { + return st.data +} + func (st *Stack) Pop() *big.Int { str := st.data[len(st.data)-1] @@ -246,6 +250,10 @@ func (m *Memory) Len() int { return len(m.store) } +func (m *Memory) Data() []byte { + return m.store +} + func (m *Memory) Print() { fmt.Printf("### mem %d bytes ###\n", len(m.store)) if len(m.store) > 0 { diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 111d2c019..549d59959 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -318,14 +318,14 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo caller := sm.procState.GetAccount(tx.Sender()) closure := NewClosure(caller, contract, contract.script, sm.procState, tx.Gas, tx.Value) vm := NewVm(sm.procState, RuntimeVars{ - origin: caller.Address(), - blockNumber: block.BlockInfo().Number, - prevHash: block.PrevHash, - coinbase: block.Coinbase, - time: block.Time, - diff: block.Difficulty, + Origin: caller.Address(), + BlockNumber: block.BlockInfo().Number, + PrevHash: block.PrevHash, + Coinbase: block.Coinbase, + Time: block.Time, + Diff: block.Difficulty, // XXX Tx data? Could be just an argument to the closure instead - txData: nil, + TxData: nil, }) closure.Call(vm, nil, nil) diff --git a/ethchain/vm.go b/ethchain/vm.go index dce972cc7..fbe0d0439 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -2,7 +2,7 @@ package ethchain import ( _ "bytes" - _ "fmt" + "fmt" "github.com/ethereum/eth-go/ethutil" _ "github.com/obscuren/secp256k1-go" _ "math" @@ -33,13 +33,13 @@ type Vm struct { } type RuntimeVars struct { - origin []byte - blockNumber uint64 - prevHash []byte - coinbase []byte - time int64 - diff *big.Int - txData []string + Origin []byte + BlockNumber uint64 + PrevHash []byte + Coinbase []byte + Time int64 + Diff *big.Int + TxData []string } func NewVm(state *State, vars RuntimeVars) *Vm { @@ -65,9 +65,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // The base for all big integer arithmetic base := new(big.Int) - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("# op\n") - } + /* + if ethutil.Config.Debug { + ethutil.Config.Log.Debugf("# op\n") + } + */ for { step++ @@ -75,9 +77,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { val := closure.Get(pc) // Get the opcode (it must be an opcode!) op := OpCode(val.Uint()) - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) - } + /* + if ethutil.Config.Debug { + ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) + } + */ // TODO Get each instruction cost properly gas := new(big.Int) @@ -270,7 +274,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { case oBALANCE: stack.Push(closure.Value) case oORIGIN: - stack.Push(ethutil.BigD(vm.vars.origin)) + stack.Push(ethutil.BigD(vm.vars.Origin)) case oCALLER: stack.Push(ethutil.BigD(closure.Callee().Address())) case oCALLVALUE: @@ -286,15 +290,15 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // 0x40 range case oPREVHASH: - stack.Push(ethutil.BigD(vm.vars.prevHash)) + stack.Push(ethutil.BigD(vm.vars.PrevHash)) case oCOINBASE: - stack.Push(ethutil.BigD(vm.vars.coinbase)) + stack.Push(ethutil.BigD(vm.vars.Coinbase)) case oTIMESTAMP: - stack.Push(big.NewInt(vm.vars.time)) + stack.Push(big.NewInt(vm.vars.Time)) case oNUMBER: - stack.Push(big.NewInt(int64(vm.vars.blockNumber))) + stack.Push(big.NewInt(int64(vm.vars.BlockNumber))) case oDIFFICULTY: - stack.Push(vm.vars.diff) + stack.Push(vm.vars.Diff) case oGASLIMIT: // TODO @@ -406,7 +410,59 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { pc.Add(pc, ethutil.Big1) if hook != nil { - hook(op) + hook(op, mem, stack) } } } + +func Disassemble(script []byte) (asm []string) { + pc := new(big.Int) + for { + if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { + return + } + + // Get the memory location of pc + val := script[pc.Int64()] + // Get the opcode (it must be an opcode!) + op := OpCode(val) + + asm = append(asm, fmt.Sprintf("%v", op)) + + switch op { + case oPUSH: // Push PC+1 on to the stack + pc.Add(pc, ethutil.Big1) + data := script[pc.Int64() : pc.Int64()+32] + val := ethutil.BigD(data) + + var b []byte + if val.Int64() == 0 { + b = []byte{0} + } else { + b = val.Bytes() + } + + asm = append(asm, fmt.Sprintf("0x%x", b)) + + pc.Add(pc, big.NewInt(31)) + case oPUSH20: + pc.Add(pc, ethutil.Big1) + data := script[pc.Int64() : pc.Int64()+20] + val := ethutil.BigD(data) + var b []byte + if val.Int64() == 0 { + b = []byte{0} + } else { + b = val.Bytes() + } + + asm = append(asm, fmt.Sprintf("0x%x", b)) + + pc.Add(pc, big.NewInt(19)) + } + + pc.Add(pc, ethutil.Big1) + } + + return +} diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 5a1419c0f..a0add9532 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -130,14 +130,29 @@ func TestRun4(t *testing.T) { callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), new(big.Int)) vm := NewVm(state, RuntimeVars{ - origin: account.Address(), - blockNumber: 1, - prevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), - coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), - time: 1, - diff: big.NewInt(256), + Origin: account.Address(), + BlockNumber: 1, + PrevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), + Coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), + Time: 1, + Diff: big.NewInt(256), // XXX Tx data? Could be just an argument to the closure instead - txData: nil, + TxData: nil, }) callerClosure.Call(vm, nil, nil) } + +func TestRun5(t *testing.T) { + ethutil.ReadConfig("") + + asm, _ := mutan.Compile(strings.NewReader(` + int32 a = 10 + int32 b = 20 + if a > b { + int32 c = this.caller() + } + exit() + `), false) + script := ethutil.Assemble(asm...) + fmt.Println(Disassemble(script)) +} From 086acd122b59071255b0c1cfae569748b1d7427a Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 12 Apr 2014 00:13:42 -0400 Subject: [PATCH 049/100] Added pre processing of script data --- ethchain/closure.go | 2 +- ethchain/vm.go | 5 +++-- ethutil/parsing.go | 31 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 3 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index de2196499..0fbe48f92 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -69,7 +69,7 @@ func (c *Closure) Address() []byte { return c.object.Address() } -type DebugHook func(op OpCode, mem *Memory, stack *Stack) +type DebugHook func(step int, op OpCode, mem *Memory, stack *Stack) func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) []byte { c.Args = args diff --git a/ethchain/vm.go b/ethchain/vm.go index fbe0d0439..b88cd2861 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -312,6 +312,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Push(val) pc.Add(pc, big.NewInt(31)) + step++ case oPUSH20: pc.Add(pc, ethutil.Big1) data := closure.Gets(pc, big.NewInt(20)) @@ -321,7 +322,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Push(val) pc.Add(pc, big.NewInt(19)) - + step++ case oPOP: stack.Pop() case oDUP: @@ -410,7 +411,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { pc.Add(pc, ethutil.Big1) if hook != nil { - hook(op, mem, stack) + hook(step-1, op, mem, stack) } } } diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 278414982..328704cae 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -3,6 +3,7 @@ package ethutil import ( _ "fmt" "math/big" + "regexp" ) // Op codes @@ -132,3 +133,33 @@ func Assemble(instructions ...interface{}) (script []byte) { return } + +/* +Prepocessing function that takes init and main apart: +init() { + // something +} + +main() { + // main something +} +*/ +func PreProcess(data string) (mainInput, initInput string) { + reg := "\\(\\)\\s*{([\\d\\w\\W\\n\\s]+?)}" + mainReg := regexp.MustCompile("main" + reg) + initReg := regexp.MustCompile("init" + reg) + + main := mainReg.FindStringSubmatch(data) + if len(main) > 0 { + mainInput = main[1] + } else { + mainInput = data + } + + init := initReg.FindStringSubmatch(data) + if len(init) > 0 { + initInput = init[1] + } + + return +} From ca13e3b1058f0d680b79dc1d9319d427a09493f8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 15 Apr 2014 16:16:38 -0400 Subject: [PATCH 050/100] Moved assembler stage processing to it's own file --- ethchain/asm.go | 126 ++++++++++++++++++++++++ ethchain/closure.go | 2 +- ethchain/stack.go | 151 +---------------------------- ethchain/types.go | 230 ++++++++++++++++++++++++++++++++++++++++++++ ethchain/vm.go | 134 +++++++++++++------------- 5 files changed, 430 insertions(+), 213 deletions(-) create mode 100644 ethchain/asm.go create mode 100644 ethchain/types.go diff --git a/ethchain/asm.go b/ethchain/asm.go new file mode 100644 index 000000000..5f901f8a2 --- /dev/null +++ b/ethchain/asm.go @@ -0,0 +1,126 @@ +package ethchain + +import ( + "fmt" + "github.com/ethereum/eth-go/ethutil" + "math/big" + "regexp" +) + +func CompileInstr(s interface{}) ([]byte, error) { + switch s.(type) { + case string: + str := s.(string) + isOp := IsOpCode(str) + if isOp { + return []byte{OpCodes[str]}, nil + } + + num := new(big.Int) + _, success := num.SetString(str, 0) + // Assume regular bytes during compilation + if !success { + num.SetBytes([]byte(str)) + } else { + // tmp fix for 32 bytes + n := ethutil.BigToBytes(num, 256) + return n, nil + } + + return num.Bytes(), nil + case int: + num := ethutil.BigToBytes(big.NewInt(int64(s.(int))), 256) + return num, nil + case []byte: + return ethutil.BigD(s.([]byte)).Bytes(), nil + } + + return nil, nil +} + +// Script compilation functions +// Compiles strings to machine code +func Assemble(instructions ...interface{}) (script []byte) { + //script = make([]string, len(instructions)) + + for _, val := range instructions { + instr, _ := CompileInstr(val) + + //script[i] = string(instr) + script = append(script, instr...) + } + + return +} + +func Disassemble(script []byte) (asm []string) { + pc := new(big.Int) + for { + if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { + return + } + + // Get the memory location of pc + val := script[pc.Int64()] + // Get the opcode (it must be an opcode!) + op := OpCode(val) + + asm = append(asm, fmt.Sprintf("%v", op)) + + switch op { + case oPUSH: // Push PC+1 on to the stack + pc.Add(pc, ethutil.Big1) + data := script[pc.Int64() : pc.Int64()+32] + val := ethutil.BigD(data) + + var b []byte + if val.Int64() == 0 { + b = []byte{0} + } else { + b = val.Bytes() + } + + asm = append(asm, fmt.Sprintf("0x%x", b)) + + pc.Add(pc, big.NewInt(31)) + case oPUSH20: + pc.Add(pc, ethutil.Big1) + data := script[pc.Int64() : pc.Int64()+20] + val := ethutil.BigD(data) + var b []byte + if val.Int64() == 0 { + b = []byte{0} + } else { + b = val.Bytes() + } + + asm = append(asm, fmt.Sprintf("0x%x", b)) + + pc.Add(pc, big.NewInt(19)) + } + + pc.Add(pc, ethutil.Big1) + } + + return +} + +func PreProcess(data string) (mainInput, initInput string) { + reg := "\\(\\)\\s*{([\\d\\w\\W\\n\\s]+?)}" + mainReg := regexp.MustCompile("main" + reg) + initReg := regexp.MustCompile("init" + reg) + + main := mainReg.FindStringSubmatch(data) + if len(main) > 0 { + mainInput = main[1] + } else { + mainInput = data + } + + init := initReg.FindStringSubmatch(data) + if len(init) > 0 { + initInput = init[1] + } + + return +} diff --git a/ethchain/closure.go b/ethchain/closure.go index 0fbe48f92..0762e8f49 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -71,7 +71,7 @@ func (c *Closure) Address() []byte { type DebugHook func(step int, op OpCode, mem *Memory, stack *Stack) -func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) []byte { +func (c *Closure) Call(vm *Vm, args []byte, hook DebugHook) ([]byte, error) { c.Args = args return vm.RunClosure(c, hook) diff --git a/ethchain/stack.go b/ethchain/stack.go index 2aca0a350..288360062 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -6,153 +6,6 @@ import ( "math/big" ) -type OpCode int - -// Op codes -const ( - // 0x0 range - arithmetic ops - oSTOP = 0x00 - oADD = 0x01 - oMUL = 0x02 - oSUB = 0x03 - oDIV = 0x04 - oSDIV = 0x05 - oMOD = 0x06 - oSMOD = 0x07 - oEXP = 0x08 - oNEG = 0x09 - oLT = 0x0a - oGT = 0x0b - oEQ = 0x0c - oNOT = 0x0d - - // 0x10 range - bit ops - oAND = 0x10 - oOR = 0x11 - oXOR = 0x12 - oBYTE = 0x13 - - // 0x20 range - crypto - oSHA3 = 0x20 - - // 0x30 range - closure state - oADDRESS = 0x30 - oBALANCE = 0x31 - oORIGIN = 0x32 - oCALLER = 0x33 - oCALLVALUE = 0x34 - oCALLDATA = 0x35 - oCALLDATASIZE = 0x36 - oGASPRICE = 0x37 - - // 0x40 range - block operations - oPREVHASH = 0x40 - oCOINBASE = 0x41 - oTIMESTAMP = 0x42 - oNUMBER = 0x43 - oDIFFICULTY = 0x44 - oGASLIMIT = 0x45 - - // 0x50 range - 'storage' and execution - oPUSH = 0x50 - oPUSH20 = 0x80 - oPOP = 0x51 - oDUP = 0x52 - oSWAP = 0x53 - oMLOAD = 0x54 - oMSTORE = 0x55 - oMSTORE8 = 0x56 - oSLOAD = 0x57 - oSSTORE = 0x58 - oJUMP = 0x59 - oJUMPI = 0x5a - oPC = 0x5b - oMSIZE = 0x5c - - // 0x60 range - closures - oCREATE = 0x60 - oCALL = 0x61 - oRETURN = 0x62 - - // 0x70 range - other - oLOG = 0x70 // XXX Unofficial - oSUICIDE = 0x7f -) - -// Since the opcodes aren't all in order we can't use a regular slice -var opCodeToString = map[OpCode]string{ - // 0x0 range - arithmetic ops - oSTOP: "STOP", - oADD: "ADD", - oMUL: "MUL", - oSUB: "SUB", - oDIV: "DIV", - oSDIV: "SDIV", - oMOD: "MOD", - oSMOD: "SMOD", - oEXP: "EXP", - oNEG: "NEG", - oLT: "LT", - oGT: "GT", - oEQ: "EQ", - oNOT: "NOT", - - // 0x10 range - bit ops - oAND: "AND", - oOR: "OR", - oXOR: "XOR", - oBYTE: "BYTE", - - // 0x20 range - crypto - oSHA3: "SHA3", - - // 0x30 range - closure state - oADDRESS: "ADDRESS", - oBALANCE: "BALANCE", - oORIGIN: "ORIGIN", - oCALLER: "CALLER", - oCALLVALUE: "CALLVALUE", - oCALLDATA: "CALLDATA", - oCALLDATASIZE: "CALLDATASIZE", - oGASPRICE: "TXGASPRICE", - - // 0x40 range - block operations - oPREVHASH: "PREVHASH", - oCOINBASE: "COINBASE", - oTIMESTAMP: "TIMESTAMP", - oNUMBER: "NUMBER", - oDIFFICULTY: "DIFFICULTY", - oGASLIMIT: "GASLIMIT", - - // 0x50 range - 'storage' and execution - oPUSH: "PUSH", - oPOP: "POP", - oDUP: "DUP", - oSWAP: "SWAP", - oMLOAD: "MLOAD", - oMSTORE: "MSTORE", - oMSTORE8: "MSTORE8", - oSLOAD: "SLOAD", - oSSTORE: "SSTORE", - oJUMP: "JUMP", - oJUMPI: "JUMPI", - oPC: "PC", - oMSIZE: "MSIZE", - - // 0x60 range - closures - oCREATE: "CREATE", - oCALL: "CALL", - oRETURN: "RETURN", - - // 0x70 range - other - oLOG: "LOG", - oSUICIDE: "SUICIDE", -} - -func (o OpCode) String() string { - return opCodeToString[o] -} - type OpType int const ( @@ -177,6 +30,10 @@ func (st *Stack) Data() []*big.Int { return st.data } +func (st *Stack) Len() int { + return len(st.data) +} + func (st *Stack) Pop() *big.Int { str := st.data[len(st.data)-1] diff --git a/ethchain/types.go b/ethchain/types.go new file mode 100644 index 000000000..24aad82c3 --- /dev/null +++ b/ethchain/types.go @@ -0,0 +1,230 @@ +package ethchain + +type OpCode int + +// Op codes +const ( + // 0x0 range - arithmetic ops + oSTOP = 0x00 + oADD = 0x01 + oMUL = 0x02 + oSUB = 0x03 + oDIV = 0x04 + oSDIV = 0x05 + oMOD = 0x06 + oSMOD = 0x07 + oEXP = 0x08 + oNEG = 0x09 + oLT = 0x0a + oGT = 0x0b + oEQ = 0x0c + oNOT = 0x0d + + // 0x10 range - bit ops + oAND = 0x10 + oOR = 0x11 + oXOR = 0x12 + oBYTE = 0x13 + + // 0x20 range - crypto + oSHA3 = 0x20 + + // 0x30 range - closure state + oADDRESS = 0x30 + oBALANCE = 0x31 + oORIGIN = 0x32 + oCALLER = 0x33 + oCALLVALUE = 0x34 + oCALLDATA = 0x35 + oCALLDATASIZE = 0x36 + oGASPRICE = 0x37 + + // 0x40 range - block operations + oPREVHASH = 0x40 + oCOINBASE = 0x41 + oTIMESTAMP = 0x42 + oNUMBER = 0x43 + oDIFFICULTY = 0x44 + oGASLIMIT = 0x45 + + // 0x50 range - 'storage' and execution + oPUSH = 0x50 + oPUSH20 = 0x80 + oPOP = 0x51 + oDUP = 0x52 + oSWAP = 0x53 + oMLOAD = 0x54 + oMSTORE = 0x55 + oMSTORE8 = 0x56 + oSLOAD = 0x57 + oSSTORE = 0x58 + oJUMP = 0x59 + oJUMPI = 0x5a + oPC = 0x5b + oMSIZE = 0x5c + + // 0x60 range - closures + oCREATE = 0x60 + oCALL = 0x61 + oRETURN = 0x62 + + // 0x70 range - other + oLOG = 0x70 // XXX Unofficial + oSUICIDE = 0x7f +) + +// Since the opcodes aren't all in order we can't use a regular slice +var opCodeToString = map[OpCode]string{ + // 0x0 range - arithmetic ops + oSTOP: "STOP", + oADD: "ADD", + oMUL: "MUL", + oSUB: "SUB", + oDIV: "DIV", + oSDIV: "SDIV", + oMOD: "MOD", + oSMOD: "SMOD", + oEXP: "EXP", + oNEG: "NEG", + oLT: "LT", + oGT: "GT", + oEQ: "EQ", + oNOT: "NOT", + + // 0x10 range - bit ops + oAND: "AND", + oOR: "OR", + oXOR: "XOR", + oBYTE: "BYTE", + + // 0x20 range - crypto + oSHA3: "SHA3", + + // 0x30 range - closure state + oADDRESS: "ADDRESS", + oBALANCE: "BALANCE", + oORIGIN: "ORIGIN", + oCALLER: "CALLER", + oCALLVALUE: "CALLVALUE", + oCALLDATA: "CALLDATA", + oCALLDATASIZE: "CALLDATASIZE", + oGASPRICE: "TXGASPRICE", + + // 0x40 range - block operations + oPREVHASH: "PREVHASH", + oCOINBASE: "COINBASE", + oTIMESTAMP: "TIMESTAMP", + oNUMBER: "NUMBER", + oDIFFICULTY: "DIFFICULTY", + oGASLIMIT: "GASLIMIT", + + // 0x50 range - 'storage' and execution + oPUSH: "PUSH", + oPOP: "POP", + oDUP: "DUP", + oSWAP: "SWAP", + oMLOAD: "MLOAD", + oMSTORE: "MSTORE", + oMSTORE8: "MSTORE8", + oSLOAD: "SLOAD", + oSSTORE: "SSTORE", + oJUMP: "JUMP", + oJUMPI: "JUMPI", + oPC: "PC", + oMSIZE: "MSIZE", + + // 0x60 range - closures + oCREATE: "CREATE", + oCALL: "CALL", + oRETURN: "RETURN", + + // 0x70 range - other + oLOG: "LOG", + oSUICIDE: "SUICIDE", +} + +func (o OpCode) String() string { + return opCodeToString[o] +} + +// 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, + "CALLDATA": 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 + "PUSH": 0x50, + + "PUSH20": 0x80, + + "POP": 0x51, + "DUP": 0x52, + "SWAP": 0x53, + "MLOAD": 0x54, + "MSTORE": 0x55, + "MSTORE8": 0x56, + "SLOAD": 0x57, + "SSTORE": 0x58, + "JUMP": 0x59, + "JUMPI": 0x5a, + "PC": 0x5b, + "MSIZE": 0x5c, + + // 0x60 range - closures + "CREATE": 0x60, + "CALL": 0x61, + "RETURN": 0x62, + + // 0x70 range - other + "LOG": 0x70, + "SUICIDE": 0x7f, +} + +func IsOpCode(s string) bool { + for key, _ := range OpCodes { + if key == s { + return true + } + } + return false +} diff --git a/ethchain/vm.go b/ethchain/vm.go index b88cd2861..33d667457 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -48,7 +48,19 @@ func NewVm(state *State, vars RuntimeVars) *Vm { var Pow256 = ethutil.BigPow(2, 256) -func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { +var isRequireError = false + +func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err error) { + // Recover from any require exception + defer func() { + if r := recover(); r != nil && isRequireError { + fmt.Println(r) + + ret = closure.Return(nil) + err = fmt.Errorf("%v", r) + } + }() + // If the amount of gas supplied is less equal to 0 if closure.Gas.Cmp(big.NewInt(0)) <= 0 { // TODO Do something @@ -58,6 +70,13 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { mem := &Memory{} // New stack (should this be shared?) stack := NewStack() + require := func(m int) { + if stack.Len()-1 > m { + isRequireError = true + panic(fmt.Sprintf("stack = %d, req = %d", stack.Len(), m)) + } + } + // Instruction pointer pc := big.NewInt(0) // Current step count @@ -121,7 +140,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { if closure.Gas.Cmp(gas) < 0 { ethutil.Config.Log.Debugln("Insufficient gas", closure.Gas, gas) - return closure.Return(nil) + return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) } switch op { @@ -129,10 +148,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Print() mem.Print() case oSTOP: // Stop the closure - return closure.Return(nil) + return closure.Return(nil), nil - // 0x20 range + // 0x20 range case oADD: + require(2) x, y := stack.Popn() // (x + y) % 2 ** 256 base.Add(x, y) @@ -140,6 +160,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // Pop result back on the stack stack.Push(base) case oSUB: + require(2) x, y := stack.Popn() // (x - y) % 2 ** 256 base.Sub(x, y) @@ -147,6 +168,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // Pop result back on the stack stack.Push(base) case oMUL: + require(2) x, y := stack.Popn() // (x * y) % 2 ** 256 base.Mul(x, y) @@ -154,12 +176,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // Pop result back on the stack stack.Push(base) case oDIV: + require(2) x, y := stack.Popn() // floor(x / y) base.Div(x, y) // Pop result back on the stack stack.Push(base) case oSDIV: + require(2) x, y := stack.Popn() // n > 2**255 if x.Cmp(Pow256) > 0 { @@ -176,10 +200,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // Push result on to the stack stack.Push(z) case oMOD: + require(2) x, y := stack.Popn() base.Mod(x, y) stack.Push(base) case oSMOD: + require(2) x, y := stack.Popn() // n > 2**255 if x.Cmp(Pow256) > 0 { @@ -196,14 +222,17 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // Push result on to the stack stack.Push(z) case oEXP: + require(2) x, y := stack.Popn() base.Exp(x, y, Pow256) stack.Push(base) case oNEG: + require(1) base.Sub(Pow256, stack.Pop()) stack.Push(base) case oLT: + require(2) x, y := stack.Popn() // x < y if x.Cmp(y) < 0 { @@ -212,6 +241,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Push(ethutil.BigFalse) } case oGT: + require(2) x, y := stack.Popn() // x > y if x.Cmp(y) > 0 { @@ -220,6 +250,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Push(ethutil.BigFalse) } case oEQ: + require(2) x, y := stack.Popn() // x == y if x.Cmp(y) == 0 { @@ -228,6 +259,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Push(ethutil.BigFalse) } case oNOT: + require(1) x := stack.Pop() if x.Cmp(ethutil.BigFalse) == 0 { stack.Push(ethutil.BigTrue) @@ -235,8 +267,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Push(ethutil.BigFalse) } - // 0x10 range + // 0x10 range case oAND: + require(2) x, y := stack.Popn() if (x.Cmp(ethutil.BigTrue) >= 0) && (y.Cmp(ethutil.BigTrue) >= 0) { stack.Push(ethutil.BigTrue) @@ -245,6 +278,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { } case oOR: + require(2) x, y := stack.Popn() if (x.Cmp(ethutil.BigInt0) >= 0) || (y.Cmp(ethutil.BigInt0) >= 0) { stack.Push(ethutil.BigTrue) @@ -252,9 +286,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Push(ethutil.BigFalse) } case oXOR: + require(2) x, y := stack.Popn() stack.Push(base.Xor(x, y)) case oBYTE: + require(2) val, th := stack.Popn() if th.Cmp(big.NewInt(32)) < 0 { stack.Push(big.NewInt(int64(len(val.Bytes())-1) - th.Int64())) @@ -262,13 +298,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Push(ethutil.BigFalse) } - // 0x20 range + // 0x20 range case oSHA3: + require(2) size, offset := stack.Popn() data := mem.Get(offset.Int64(), size.Int64()) stack.Push(ethutil.BigD(data)) - // 0x30 range + // 0x30 range case oADDRESS: stack.Push(ethutil.BigD(closure.Object().Address())) case oBALANCE: @@ -281,6 +318,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // FIXME: Original value of the call, not the current value stack.Push(closure.Value) case oCALLDATA: + require(1) offset := stack.Pop() mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args) case oCALLDATASIZE: @@ -288,7 +326,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { case oGASPRICE: // TODO - // 0x40 range + // 0x40 range case oPREVHASH: stack.Push(ethutil.BigD(vm.vars.PrevHash)) case oCOINBASE: @@ -300,7 +338,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { case oDIFFICULTY: stack.Push(vm.vars.Diff) case oGASLIMIT: - // TODO + // TODO // 0x50 range case oPUSH: // Push PC+1 on to the stack @@ -324,34 +362,44 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { pc.Add(pc, big.NewInt(19)) step++ case oPOP: + require(1) stack.Pop() case oDUP: + require(1) stack.Push(stack.Peek()) case oSWAP: + require(2) x, y := stack.Popn() stack.Push(y) stack.Push(x) case oMLOAD: + require(1) offset := stack.Pop() stack.Push(ethutil.BigD(mem.Get(offset.Int64(), 32))) case oMSTORE: // Store the value at stack top-1 in to memory at location stack top + require(2) // Pop value of the stack val, mStart := stack.Popn() mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(val, 256)) case oMSTORE8: + require(2) val, mStart := stack.Popn() base.And(val, new(big.Int).SetInt64(0xff)) mem.Set(mStart.Int64(), 32, ethutil.BigToBytes(base, 256)) case oSLOAD: + require(1) loc := stack.Pop() val := closure.GetMem(loc) stack.Push(val.BigInt()) case oSSTORE: + require(2) val, loc := stack.Popn() closure.SetMem(loc, ethutil.NewValue(val)) case oJUMP: + require(1) pc = stack.Pop() case oJUMPI: + require(2) cond, pos := stack.Popn() if cond.Cmp(ethutil.BigTrue) == 0 { pc = pos @@ -360,8 +408,10 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { stack.Push(pc) case oMSIZE: stack.Push(big.NewInt(int64(mem.Len()))) - // 0x60 range + // 0x60 range + case oCREATE: case oCALL: + require(8) // Closure addr addr := stack.Pop() // Pop gas and value of the stack. @@ -377,14 +427,20 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { // Create a new callable closure closure := NewClosure(closure, contract, contract.script, vm.state, gas, value) // Executer the closure and get the return value (if any) - ret := closure.Call(vm, args, hook) + ret, err := closure.Call(vm, args, hook) + if err != nil { + stack.Push(ethutil.BigFalse) + } else { + stack.Push(ethutil.BigTrue) + } mem.Set(retOffset.Int64(), retSize.Int64(), ret) case oRETURN: + require(2) size, offset := stack.Popn() ret := mem.Get(offset.Int64(), size.Int64()) - return closure.Return(ret) + return closure.Return(ret), nil case oSUICIDE: /* recAddr := stack.Pop().Bytes() @@ -405,7 +461,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { default: ethutil.Config.Log.Debugf("Invalid opcode %x\n", op) - return closure.Return(nil) + return closure.Return(nil), fmt.Errorf("Invalid opcode %x", op) } pc.Add(pc, ethutil.Big1) @@ -415,55 +471,3 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) []byte { } } } - -func Disassemble(script []byte) (asm []string) { - pc := new(big.Int) - for { - if pc.Cmp(big.NewInt(int64(len(script)))) >= 0 { - return - } - - // Get the memory location of pc - val := script[pc.Int64()] - // Get the opcode (it must be an opcode!) - op := OpCode(val) - - asm = append(asm, fmt.Sprintf("%v", op)) - - switch op { - case oPUSH: // Push PC+1 on to the stack - pc.Add(pc, ethutil.Big1) - data := script[pc.Int64() : pc.Int64()+32] - val := ethutil.BigD(data) - - var b []byte - if val.Int64() == 0 { - b = []byte{0} - } else { - b = val.Bytes() - } - - asm = append(asm, fmt.Sprintf("0x%x", b)) - - pc.Add(pc, big.NewInt(31)) - case oPUSH20: - pc.Add(pc, ethutil.Big1) - data := script[pc.Int64() : pc.Int64()+20] - val := ethutil.BigD(data) - var b []byte - if val.Int64() == 0 { - b = []byte{0} - } else { - b = val.Bytes() - } - - asm = append(asm, fmt.Sprintf("0x%x", b)) - - pc.Add(pc, big.NewInt(19)) - } - - pc.Add(pc, ethutil.Big1) - } - - return -} From 9c6aca78933c14ca107da30c4690808950718368 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 16 Apr 2014 04:06:51 +0200 Subject: [PATCH 051/100] Merged accounts and contracts in to StateObject * Account removed * Contract removed * Address state changed to CachedStateObject * Added StateObject --- ethchain/address.go | 76 ---------------- ethchain/address_test.go | 8 -- ethchain/block.go | 7 +- ethchain/block_chain.go | 6 +- ethchain/contract.go | 119 ------------------------- ethchain/keypair.go | 4 +- ethchain/state.go | 65 ++++---------- ethchain/state_manager.go | 54 +++++------- ethchain/state_object.go | 162 +++++++++++++++++++++++++++++++++++ ethchain/transaction.go | 4 +- ethchain/transaction_pool.go | 14 +-- 11 files changed, 219 insertions(+), 300 deletions(-) delete mode 100644 ethchain/address.go delete mode 100644 ethchain/address_test.go delete mode 100644 ethchain/contract.go create mode 100644 ethchain/state_object.go diff --git a/ethchain/address.go b/ethchain/address.go deleted file mode 100644 index 0b3ef7c05..000000000 --- a/ethchain/address.go +++ /dev/null @@ -1,76 +0,0 @@ -package ethchain - -import ( - "github.com/ethereum/eth-go/ethutil" - "math/big" -) - -type Account struct { - address []byte - Amount *big.Int - Nonce uint64 -} - -func NewAccount(address []byte, amount *big.Int) *Account { - return &Account{address, amount, 0} -} - -func NewAccountFromData(address, data []byte) *Account { - account := &Account{address: address} - account.RlpDecode(data) - - return account -} - -func (a *Account) AddFee(fee *big.Int) { - a.AddFunds(fee) -} - -func (a *Account) AddFunds(funds *big.Int) { - a.Amount.Add(a.Amount, funds) -} - -func (a *Account) Address() []byte { - return a.address -} - -// Implements Callee -func (a *Account) ReturnGas(value *big.Int, state *State) { - // Return the value back to the sender - a.AddFunds(value) - state.UpdateAccount(a.address, a) -} - -func (a *Account) RlpEncode() []byte { - return ethutil.Encode([]interface{}{a.Amount, a.Nonce}) -} - -func (a *Account) RlpDecode(data []byte) { - decoder := ethutil.NewValueFromBytes(data) - - a.Amount = decoder.Get(0).BigInt() - a.Nonce = decoder.Get(1).Uint() -} - -type AddrStateStore struct { - states map[string]*AccountState -} - -func NewAddrStateStore() *AddrStateStore { - return &AddrStateStore{states: make(map[string]*AccountState)} -} - -func (s *AddrStateStore) Add(addr []byte, account *Account) *AccountState { - state := &AccountState{Nonce: account.Nonce, Account: account} - s.states[string(addr)] = state - return state -} - -func (s *AddrStateStore) Get(addr []byte) *AccountState { - return s.states[string(addr)] -} - -type AccountState struct { - Nonce uint64 - Account *Account -} diff --git a/ethchain/address_test.go b/ethchain/address_test.go deleted file mode 100644 index 161e1b251..000000000 --- a/ethchain/address_test.go +++ /dev/null @@ -1,8 +0,0 @@ -package ethchain - -import ( - "testing" -) - -func TestAddressState(t *testing.T) { -} diff --git a/ethchain/block.go b/ethchain/block.go index 732739c1b..8c93947fb 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -142,12 +142,13 @@ func (block *Block) PayFee(addr []byte, fee *big.Int) bool { data := block.state.trie.Get(string(block.Coinbase)) // Get the ether (Coinbase) and add the fee (gief fee to miner) - ether := NewAccountFromData(block.Coinbase, []byte(data)) + account := NewStateObjectFromBytes(block.Coinbase, []byte(data)) base = new(big.Int) - ether.Amount = base.Add(ether.Amount, fee) + account.Amount = base.Add(account.Amount, fee) - block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode())) + //block.state.trie.Update(string(block.Coinbase), string(ether.RlpEncode())) + block.state.UpdateStateObject(account) return true } diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 2a50ef687..d65c38fe4 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -262,9 +262,9 @@ func AddTestNetFunds(block *Block) { } { //log.Println("2^200 Wei to", addr) codedAddr := ethutil.FromHex(addr) - addr := block.state.GetAccount(codedAddr) - addr.Amount = ethutil.BigPow(2, 200) - block.state.UpdateAccount(codedAddr, addr) + account := block.state.GetAccount(codedAddr) + account.Amount = ethutil.BigPow(2, 200) + block.state.UpdateStateObject(account) } } diff --git a/ethchain/contract.go b/ethchain/contract.go deleted file mode 100644 index af348667c..000000000 --- a/ethchain/contract.go +++ /dev/null @@ -1,119 +0,0 @@ -package ethchain - -import ( - "github.com/ethereum/eth-go/ethutil" - "math/big" -) - -type Contract struct { - Amount *big.Int - Nonce uint64 - //state *ethutil.Trie - state *State - address []byte - script []byte - initScript []byte -} - -func NewContract(address []byte, Amount *big.Int, root []byte) *Contract { - contract := &Contract{address: address, Amount: Amount, Nonce: 0} - contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root))) - - return contract -} - -func NewContractFromBytes(address, data []byte) *Contract { - contract := &Contract{address: address} - contract.RlpDecode(data) - - return contract -} - -func (c *Contract) Addr(addr []byte) *ethutil.Value { - return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) -} - -func (c *Contract) SetAddr(addr []byte, value interface{}) { - c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) -} - -func (c *Contract) State() *State { - return c.state -} - -func (c *Contract) GetMem(num *big.Int) *ethutil.Value { - nb := ethutil.BigToBytes(num, 256) - - return c.Addr(nb) -} - -func (c *Contract) GetInstr(pc *big.Int) *ethutil.Value { - if int64(len(c.script)-1) < pc.Int64() { - return ethutil.NewValue(0) - } - - return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]}) -} - -func (c *Contract) SetMem(num *big.Int, val *ethutil.Value) { - addr := ethutil.BigToBytes(num, 256) - c.state.trie.Update(string(addr), string(val.Encode())) -} - -// Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *Contract) ReturnGas(val *big.Int, state *State) { - c.Amount.Add(c.Amount, val) -} - -func (c *Contract) Address() []byte { - return c.address -} - -func (c *Contract) Script() []byte { - return c.script -} - -func (c *Contract) Init() []byte { - return c.initScript -} - -func (c *Contract) RlpEncode() []byte { - return ethutil.Encode([]interface{}{c.Amount, c.Nonce, c.state.trie.Root, c.script, c.initScript}) -} - -func (c *Contract) RlpDecode(data []byte) { - decoder := ethutil.NewValueFromBytes(data) - - c.Amount = decoder.Get(0).BigInt() - c.Nonce = decoder.Get(1).Uint() - c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) - c.script = decoder.Get(3).Bytes() - c.initScript = decoder.Get(4).Bytes() -} - -func MakeContract(tx *Transaction, state *State) *Contract { - // Create contract if there's no recipient - if tx.IsContract() { - addr := tx.Hash()[12:] - - value := tx.Value - contract := NewContract(addr, value, []byte("")) - state.trie.Update(string(addr), string(contract.RlpEncode())) - contract.script = tx.Data - contract.initScript = tx.Init - - /* - for i, val := range tx.Data { - if len(val) > 0 { - bytNum := ethutil.BigToBytes(big.NewInt(int64(i)), 256) - contract.state.trie.Update(string(bytNum), string(ethutil.Encode(val))) - } - } - */ - state.trie.Update(string(addr), string(contract.RlpEncode())) - - return contract - } - - return nil -} diff --git a/ethchain/keypair.go b/ethchain/keypair.go index 9daaedbee..a5af791d0 100644 --- a/ethchain/keypair.go +++ b/ethchain/keypair.go @@ -10,7 +10,7 @@ type KeyPair struct { PublicKey []byte // The associated account - account *Account + account *StateObject state *State } @@ -24,7 +24,7 @@ func (k *KeyPair) Address() []byte { return ethutil.Sha3Bin(k.PublicKey[1:])[12:] } -func (k *KeyPair) Account() *Account { +func (k *KeyPair) Account() *StateObject { if k.account == nil { k.account = k.state.GetAccount(k.Address()) } diff --git a/ethchain/state.go b/ethchain/state.go index 1860647f2..655848932 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -47,23 +47,14 @@ func (s *State) Purge() int { return s.trie.NewIterator().Purge() } -func (s *State) GetContract(addr []byte) *Contract { +func (s *State) GetContract(addr []byte) *StateObject { data := s.trie.Get(string(addr)) if data == "" { return nil } - // Whet get contract is called the retrieved value might - // be an account. The StateManager uses this to check - // to see if the address a tx was sent to is a contract - // or an account - value := ethutil.NewValueFromBytes([]byte(data)) - if value.Len() == 2 { - return nil - } - // build contract - contract := NewContractFromBytes(addr, []byte(data)) + contract := NewStateObjectFromBytes(addr, []byte(data)) // Check if there's a cached state for this contract cachedState := s.states[string(addr)] @@ -77,28 +68,17 @@ func (s *State) GetContract(addr []byte) *Contract { return contract } -func (s *State) UpdateContract(contract *Contract) { - addr := contract.Address() - - s.states[string(addr)] = contract.state - s.trie.Update(string(addr), string(contract.RlpEncode())) -} - -func (s *State) GetAccount(addr []byte) (account *Account) { +func (s *State) GetAccount(addr []byte) (account *StateObject) { data := s.trie.Get(string(addr)) if data == "" { account = NewAccount(addr, big.NewInt(0)) } else { - account = NewAccountFromData(addr, []byte(data)) + account = NewStateObjectFromBytes(addr, []byte(data)) } return } -func (s *State) UpdateAccount(addr []byte, account *Account) { - s.trie.Update(string(addr), string(account.RlpEncode())) -} - func (s *State) Cmp(other *State) bool { return s.trie.Cmp(other.trie) } @@ -119,7 +99,7 @@ const ( // Returns the object stored at key and the type stored at key // Returns nil if nothing is stored -func (s *State) Get(key []byte) (*ethutil.Value, ObjType) { +func (s *State) GetStateObject(key []byte) (*ethutil.Value, ObjType) { // Fetch data from the trie data := s.trie.Get(string(key)) // Returns the nil type, indicating nothing could be retrieved. @@ -145,6 +125,17 @@ func (s *State) Get(key []byte) (*ethutil.Value, ObjType) { return val, typ } +// Updates any given state object +func (s *State) UpdateStateObject(object *StateObject) { + addr := object.Address() + + if object.state != nil { + s.states[string(addr)] = object.state + } + + s.trie.Update(string(addr), string(object.RlpEncode())) +} + func (s *State) Put(key, object []byte) { s.trie.Update(string(key), string(object)) } @@ -152,27 +143,3 @@ func (s *State) Put(key, object []byte) { func (s *State) Root() interface{} { return s.trie.Root } - -// Script compilation functions -// Compiles strings to machine code -func Compile(code []string) (script []string) { - script = make([]string, len(code)) - for i, val := range code { - instr, _ := ethutil.CompileInstr(val) - - script[i] = string(instr) - } - - return -} - -func CompileToValues(code []string) (script []*ethutil.Value) { - script = make([]*ethutil.Value, len(code)) - for i, val := range code { - instr, _ := ethutil.CompileInstr(val) - - script[i] = ethutil.NewValue(instr) - } - - return -} diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 549d59959..5e30d7280 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -30,7 +30,7 @@ type StateManager struct { bc *BlockChain // States for addresses. You can watch any address // at any given time - addrStateStore *AddrStateStore + stateObjectCache *StateObjectCache // Stack for processing contracts stack *Stack @@ -54,12 +54,12 @@ type StateManager struct { func NewStateManager(ethereum EthManager) *StateManager { sm := &StateManager{ - stack: NewStack(), - mem: make(map[string]*big.Int), - Pow: &EasyPow{}, - Ethereum: ethereum, - addrStateStore: NewAddrStateStore(), - bc: ethereum.BlockChain(), + stack: NewStack(), + mem: make(map[string]*big.Int), + Pow: &EasyPow{}, + Ethereum: ethereum, + stateObjectCache: NewStateObjectCache(), + bc: ethereum.BlockChain(), } sm.procState = ethereum.BlockChain().CurrentBlock.State() return sm @@ -70,18 +70,18 @@ func (sm *StateManager) ProcState() *State { } // Watches any given address and puts it in the address state store -func (sm *StateManager) WatchAddr(addr []byte) *AccountState { +func (sm *StateManager) WatchAddr(addr []byte) *CachedStateObject { //XXX account := sm.bc.CurrentBlock.state.GetAccount(addr) account := sm.procState.GetAccount(addr) - return sm.addrStateStore.Add(addr, account) + return sm.stateObjectCache.Add(addr, account) } -func (sm *StateManager) GetAddrState(addr []byte) *AccountState { - account := sm.addrStateStore.Get(addr) +func (sm *StateManager) GetAddrState(addr []byte) *CachedStateObject { + account := sm.stateObjectCache.Get(addr) if account == nil { a := sm.procState.GetAccount(addr) - account = &AccountState{Nonce: a.Nonce, Account: a} + account = &CachedStateObject{Nonce: a.Nonce, Object: a} } return account @@ -116,7 +116,7 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { if contract := sm.procState.GetContract(tx.Recipient); contract != nil { err = sm.Ethereum.TxPool().ProcessTransaction(tx, block, true) if err == nil { - sm.ProcessContract(contract, tx, block) + sm.EvalScript(contract.Script(), contract, tx, block) } } else { err = sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) @@ -180,7 +180,6 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { return err } - // if !sm.compState.Cmp(sm.procState) if !sm.compState.Cmp(sm.procState) { return fmt.Errorf("Invalid merkle root. Expected %x, got %x", sm.compState.trie.Root, sm.procState.trie.Root) } @@ -190,9 +189,6 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { // Sync the current block's state to the database and cancelling out the deferred Undo sm.procState.Sync() - // Broadcast the valid block back to the wire - //sm.Ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{block.Value().Val}) - // Add the block to the chain sm.bc.Add(block) @@ -282,22 +278,20 @@ func CalculateUncleReward(block *Block) *big.Int { } func (sm *StateManager) AccumelateRewards(block *Block) error { - - // Get the coinbase rlp data - acc := sm.procState.GetAccount(block.Coinbase) + // Get the account associated with the coinbase + account := sm.procState.GetAccount(block.Coinbase) // Reward amount of ether to the coinbase address - acc.AddFee(CalculateBlockReward(block, len(block.Uncles))) + account.AddAmount(CalculateBlockReward(block, len(block.Uncles))) addr := make([]byte, len(block.Coinbase)) copy(addr, block.Coinbase) - sm.procState.UpdateAccount(addr, acc) + sm.procState.UpdateStateObject(account) for _, uncle := range block.Uncles { - uncleAddr := sm.procState.GetAccount(uncle.Coinbase) - uncleAddr.AddFee(CalculateUncleReward(uncle)) + uncleAccount := sm.procState.GetAccount(uncle.Coinbase) + uncleAccount.AddAmount(CalculateUncleReward(uncle)) - //processor.state.UpdateAccount(uncle.Coinbase, uncleAddr) - sm.procState.UpdateAccount(uncle.Coinbase, uncleAddr) + sm.procState.UpdateStateObject(uncleAccount) } return nil @@ -307,7 +301,7 @@ func (sm *StateManager) Stop() { sm.bc.Stop() } -func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, block *Block) { +func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Transaction, block *Block) { // Recovering function in case the VM had any errors defer func() { if r := recover(); r != nil { @@ -316,7 +310,7 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo }() caller := sm.procState.GetAccount(tx.Sender()) - closure := NewClosure(caller, contract, contract.script, sm.procState, tx.Gas, tx.Value) + closure := NewClosure(caller, object, script, sm.procState, tx.Gas, tx.Value) vm := NewVm(sm.procState, RuntimeVars{ Origin: caller.Address(), BlockNumber: block.BlockInfo().Number, @@ -324,11 +318,9 @@ func (sm *StateManager) ProcessContract(contract *Contract, tx *Transaction, blo Coinbase: block.Coinbase, Time: block.Time, Diff: block.Difficulty, - // XXX Tx data? Could be just an argument to the closure instead - TxData: nil, }) closure.Call(vm, nil, nil) // Update the account (refunds) - sm.procState.UpdateAccount(tx.Sender(), caller) + sm.procState.UpdateStateObject(caller) } diff --git a/ethchain/state_object.go b/ethchain/state_object.go new file mode 100644 index 000000000..8b4de0c4f --- /dev/null +++ b/ethchain/state_object.go @@ -0,0 +1,162 @@ +package ethchain + +import ( + "github.com/ethereum/eth-go/ethutil" + "math/big" +) + +type StateObject struct { + // Address of the object + address []byte + // Shared attributes + Amount *big.Int + Nonce uint64 + // Contract related attributes + state *State + script []byte + initScript []byte +} + +func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { + contract := &StateObject{address: address, Amount: Amount, Nonce: 0} + contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root))) + + return contract +} + +// Returns a newly created account +func NewAccount(address []byte, amount *big.Int) *StateObject { + account := &StateObject{address: address, Amount: amount, Nonce: 0} + + return account +} + +func NewStateObjectFromBytes(address, data []byte) *StateObject { + object := &StateObject{address: address} + object.RlpDecode(data) + + return object +} + +func (c *StateObject) Addr(addr []byte) *ethutil.Value { + return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) +} + +func (c *StateObject) SetAddr(addr []byte, value interface{}) { + c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) +} + +func (c *StateObject) State() *State { + return c.state +} + +func (c *StateObject) GetMem(num *big.Int) *ethutil.Value { + nb := ethutil.BigToBytes(num, 256) + + return c.Addr(nb) +} + +func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { + if int64(len(c.script)-1) < pc.Int64() { + return ethutil.NewValue(0) + } + + return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]}) +} + +func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) { + addr := ethutil.BigToBytes(num, 256) + c.state.trie.Update(string(addr), string(val.Encode())) +} + +// Return the gas back to the origin. Used by the Virtual machine or Closures +func (c *StateObject) ReturnGas(val *big.Int, state *State) { + c.AddAmount(val) +} + +func (c *StateObject) AddAmount(amount *big.Int) { + c.Amount.Add(c.Amount, amount) +} + +func (c *StateObject) SubAmount(amount *big.Int) { + c.Amount.Sub(c.Amount, amount) +} + +func (c *StateObject) Address() []byte { + return c.address +} + +func (c *StateObject) Script() []byte { + return c.script +} + +func (c *StateObject) Init() []byte { + return c.initScript +} + +func (c *StateObject) RlpEncode() []byte { + var root interface{} + if c.state != nil { + root = c.state.trie.Root + } else { + root = nil + } + return ethutil.Encode([]interface{}{c.Amount, c.Nonce, root, c.script}) +} + +func (c *StateObject) RlpDecode(data []byte) { + decoder := ethutil.NewValueFromBytes(data) + + c.Amount = decoder.Get(0).BigInt() + c.Nonce = decoder.Get(1).Uint() + c.state = NewState(ethutil.NewTrie(ethutil.Config.Db, decoder.Get(2).Interface())) + c.script = decoder.Get(3).Bytes() +} + +func MakeContract(tx *Transaction, state *State) *StateObject { + // Create contract if there's no recipient + if tx.IsContract() { + // FIXME + addr := tx.Hash()[12:] + + value := tx.Value + contract := NewContract(addr, value, []byte("")) + state.UpdateStateObject(contract) + + contract.script = tx.Data + contract.initScript = tx.Init + + state.UpdateStateObject(contract) + + return contract + } + + return nil +} + +// The cached state and state object cache are helpers which will give you somewhat +// control over the nonce. When creating new transactions you're interested in the 'next' +// nonce rather than the current nonce. This to avoid creating invalid-nonce transactions. +type StateObjectCache struct { + cachedObjects map[string]*CachedStateObject +} + +func NewStateObjectCache() *StateObjectCache { + return &StateObjectCache{cachedObjects: make(map[string]*CachedStateObject)} +} + +func (s *StateObjectCache) Add(addr []byte, object *StateObject) *CachedStateObject { + state := &CachedStateObject{Nonce: object.Nonce, Object: object} + s.cachedObjects[string(addr)] = state + + return state +} + +func (s *StateObjectCache) Get(addr []byte) *CachedStateObject { + return s.cachedObjects[string(addr)] +} + +type CachedStateObject struct { + Nonce uint64 + Object *StateObject +} diff --git a/ethchain/transaction.go b/ethchain/transaction.go index b359c9151..78044e840 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -23,8 +23,8 @@ type Transaction struct { contractCreation bool } -func NewContractCreationTx(value, gasprice *big.Int, data []byte) *Transaction { - return &Transaction{Value: value, Gasprice: gasprice, Data: data, contractCreation: true} +func NewContractCreationTx(value, gasprice *big.Int, script []byte, init []byte) *Transaction { + return &Transaction{Value: value, Gasprice: gasprice, Data: script, Init: init, contractCreation: true} } func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []byte) *Transaction { diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 0bcfe6923..5cdda17e2 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -118,20 +118,20 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract // Send Tx to self if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { // Subtract the fee - sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat)) + sender.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat)) } else if toContract { - sender.Amount.Sub(sender.Amount, new(big.Int).Mul(TxFee, TxFeeRat)) + sender.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat)) } else { // Subtract the amount from the senders account - sender.Amount.Sub(sender.Amount, totAmount) + sender.SubAmount(totAmount) // Add the amount to receivers account which should conclude this transaction - receiver.Amount.Add(receiver.Amount, tx.Value) + receiver.AddAmount(tx.Value) - block.state.UpdateAccount(tx.Recipient, receiver) + block.state.UpdateStateObject(receiver) } - block.state.UpdateAccount(tx.Sender(), sender) + block.state.UpdateStateObject(sender) log.Printf("[TXPL] Processed Tx %x\n", tx.Hash()) @@ -151,7 +151,7 @@ func (pool *TxPool) ValidateTransaction(tx *Transaction) error { // Get the sender accountState := pool.Ethereum.StateManager().GetAddrState(tx.Sender()) - sender := accountState.Account + sender := accountState.Object totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) // Make sure there's enough in the sender's account. Having insufficient From d811920d8b5643b944a7df58d5d75095539242c4 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 16 Apr 2014 04:07:21 +0200 Subject: [PATCH 052/100] Hack for miner problem added w/ note @maranh please check --- ethminer/miner.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ethminer/miner.go b/ethminer/miner.go index d84977342..5bbf6b977 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -119,6 +119,9 @@ func (miner *Miner) listener() { miner.block.SetUncles(miner.uncles) } + // FIXME @ maranh, first block doesn't need this. Everything after the first block does. + // Please check and fix + miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State()) // Apply all transactions to the block miner.ethereum.StateManager().ApplyTransactions(miner.block, miner.block.Transactions()) miner.ethereum.StateManager().AccumelateRewards(miner.block) From c5729d7ecc564f8eff6df565173a4f5cc6c43cb0 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 16 Apr 2014 04:07:52 +0200 Subject: [PATCH 053/100] comments --- ethchain/asm.go | 1 + 1 file changed, 1 insertion(+) diff --git a/ethchain/asm.go b/ethchain/asm.go index 5f901f8a2..a6c85cb60 100644 --- a/ethchain/asm.go +++ b/ethchain/asm.go @@ -106,6 +106,7 @@ func Disassemble(script []byte) (asm []string) { } func PreProcess(data string) (mainInput, initInput string) { + // Regexp for parsing anything between brackets reg := "\\(\\)\\s*{([\\d\\w\\W\\n\\s]+?)}" mainReg := regexp.MustCompile("main" + reg) initReg := regexp.MustCompile("init" + reg) From a96c8c8af969665cc0c357eef81d43b5b7285dfe Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 18 Apr 2014 13:41:07 +0200 Subject: [PATCH 054/100] Added proper gas handling --- ethchain/closure.go | 5 +++-- ethchain/state_manager.go | 3 ++- ethchain/transaction.go | 12 ++++++------ ethchain/vm.go | 9 +++++---- ethchain/vm_test.go | 17 ++++++++++------- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 0762e8f49..5c508179e 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -27,14 +27,15 @@ type Closure struct { State *State Gas *big.Int + Price *big.Int Value *big.Int Args []byte } // Create a new closure for the given data items -func NewClosure(callee Callee, object Reference, script []byte, state *State, gas, val *big.Int) *Closure { - return &Closure{callee, object, script, state, gas, val, nil} +func NewClosure(callee Callee, object Reference, script []byte, state *State, gas, price, val *big.Int) *Closure { + return &Closure{callee, object, script, state, gas, price, val, nil} } // Retuns the x element in data slice diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 5e30d7280..75a78e9f3 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -310,7 +310,7 @@ func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Trans }() caller := sm.procState.GetAccount(tx.Sender()) - closure := NewClosure(caller, object, script, sm.procState, tx.Gas, tx.Value) + closure := NewClosure(caller, object, script, sm.procState, tx.Gas, tx.GasPrice, tx.Value) vm := NewVm(sm.procState, RuntimeVars{ Origin: caller.Address(), BlockNumber: block.BlockInfo().Number, @@ -318,6 +318,7 @@ func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Trans Coinbase: block.Coinbase, Time: block.Time, Diff: block.Difficulty, + //Price: tx.GasPrice, }) closure.Call(vm, nil, nil) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 78044e840..1e43a2bae 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -13,7 +13,7 @@ type Transaction struct { Recipient []byte Value *big.Int Gas *big.Int - Gasprice *big.Int + GasPrice *big.Int Data []byte Init []byte v byte @@ -24,11 +24,11 @@ type Transaction struct { } func NewContractCreationTx(value, gasprice *big.Int, script []byte, init []byte) *Transaction { - return &Transaction{Value: value, Gasprice: gasprice, Data: script, Init: init, contractCreation: true} + return &Transaction{Value: value, GasPrice: gasprice, Data: script, Init: init, contractCreation: true} } func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []byte) *Transaction { - return &Transaction{Recipient: to, Value: value, Gasprice: gasprice, Gas: gas, Data: data} + return &Transaction{Recipient: to, Value: value, GasPrice: gasprice, Gas: gas, Data: data} } func NewTransactionFromBytes(data []byte) *Transaction { @@ -46,7 +46,7 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction { } func (tx *Transaction) Hash() []byte { - data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice, tx.Gas, tx.Recipient, string(tx.Data)} + data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, string(tx.Data)} if tx.contractCreation { data = append(data, string(tx.Init)) } @@ -107,7 +107,7 @@ func (tx *Transaction) Sign(privk []byte) error { // [ NONCE, VALUE, GASPRICE, GAS, TO, DATA, V, R, S ] // [ NONCE, VALUE, GASPRICE, GAS, 0, CODE, INIT, V, R, S ] func (tx *Transaction) RlpData() interface{} { - data := []interface{}{tx.Nonce, tx.Value, tx.Gasprice, tx.Gas, tx.Recipient, tx.Data} + data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, tx.Data} if tx.contractCreation { data = append(data, tx.Init) @@ -132,7 +132,7 @@ func (tx *Transaction) RlpDecode(data []byte) { func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { tx.Nonce = decoder.Get(0).Uint() tx.Value = decoder.Get(1).BigInt() - tx.Gasprice = decoder.Get(2).BigInt() + tx.GasPrice = decoder.Get(2).BigInt() tx.Gas = decoder.Get(3).BigInt() tx.Recipient = decoder.Get(4).Bytes() tx.Data = decoder.Get(5).Bytes() diff --git a/ethchain/vm.go b/ethchain/vm.go index 33d667457..85aefa685 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -71,7 +71,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // New stack (should this be shared?) stack := NewStack() require := func(m int) { - if stack.Len()-1 > m { + if stack.Len() < m { isRequireError = true panic(fmt.Sprintf("stack = %d, req = %d", stack.Len(), m)) } @@ -105,7 +105,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // TODO Get each instruction cost properly gas := new(big.Int) useGas := func(amount *big.Int) { - gas.Add(gas, amount) + gas.Add(gas, new(big.Int).Mul(amount, closure.Price)) } switch op { @@ -142,6 +142,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) } + closure.Gas.Sub(closure.Gas, gas) switch op { case oLOG: @@ -411,7 +412,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // 0x60 range case oCREATE: case oCALL: - require(8) + require(7) // Closure addr addr := stack.Pop() // Pop gas and value of the stack. @@ -425,7 +426,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Fetch the contract which will serve as the closure body contract := vm.state.GetContract(addr.Bytes()) // Create a new callable closure - closure := NewClosure(closure, contract, contract.script, vm.state, gas, value) + closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price, value) // Executer the closure and get the return value (if any) ret, err := closure.Call(vm, args, hook) if err != nil { diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index a0add9532..f66f2a896 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -91,10 +91,10 @@ func TestRun4(t *testing.T) { exit() `), false) script := ethutil.Assemble(asm...) - tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script) + tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script, nil) addr := tx.Hash()[12:] contract := MakeContract(tx, state) - state.UpdateContract(contract) + state.UpdateStateObject(contract) fmt.Printf("%x\n", addr) asm, err = mutan.Compile(strings.NewReader(` @@ -122,12 +122,13 @@ func TestRun4(t *testing.T) { fmt.Println(asm) callerScript := ethutil.Assemble(asm...) - callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript) + callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript, nil) // Contract addr as test address account := NewAccount(ContractAddr, big.NewInt(10000000)) + fmt.Println(account) c := MakeContract(callerTx, state) - callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), new(big.Int)) + callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), big.NewInt(10), big.NewInt(0)) vm := NewVm(state, RuntimeVars{ Origin: account.Address(), @@ -136,10 +137,12 @@ func TestRun4(t *testing.T) { Coinbase: ethutil.FromHex("2adc25665018aa1fe0e6bc666dac8fc2697ff9ba"), Time: 1, Diff: big.NewInt(256), - // XXX Tx data? Could be just an argument to the closure instead - TxData: nil, }) - callerClosure.Call(vm, nil, nil) + _, e := callerClosure.Call(vm, nil, nil) + if e != nil { + fmt.Println("error", e) + } + fmt.Println(account) } func TestRun5(t *testing.T) { From 6930260962f4c6d1fe11d07a10300da96886ecd6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 20 Apr 2014 01:31:01 +0200 Subject: [PATCH 055/100] Updated VM --- ethchain/closure.go | 10 +++----- ethchain/state_object.go | 22 ++++++++++++++-- ethchain/vm.go | 54 +++++++++++++++++++++++++++------------- ethchain/vm_test.go | 41 +++++++++++++++++++++--------- 4 files changed, 90 insertions(+), 37 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 5c508179e..defd8b5c8 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -8,7 +8,7 @@ import ( ) type Callee interface { - ReturnGas(*big.Int, *State) + ReturnGas(*big.Int, *big.Int, *State) Address() []byte } @@ -83,18 +83,16 @@ func (c *Closure) Return(ret []byte) []byte { // If no callee is present return it to // the origin (i.e. contract or tx) if c.callee != nil { - c.callee.ReturnGas(c.Gas, c.State) + c.callee.ReturnGas(c.Gas, c.Price, c.State) } else { - c.object.ReturnGas(c.Gas, c.State) - // TODO incase it's a POST contract we gotta serialise the contract again. - // But it's not yet defined + c.object.ReturnGas(c.Gas, c.Price, c.State) } return ret } // Implement the Callee interface -func (c *Closure) ReturnGas(gas *big.Int, state *State) { +func (c *Closure) ReturnGas(gas, price *big.Int, state *State) { // Return the gas to the closure c.Gas.Add(c.Gas, gas) } diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 8b4de0c4f..f562e5b04 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -1,6 +1,7 @@ package ethchain import ( + "fmt" "github.com/ethereum/eth-go/ethutil" "math/big" ) @@ -70,8 +71,9 @@ func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) { } // Return the gas back to the origin. Used by the Virtual machine or Closures -func (c *StateObject) ReturnGas(val *big.Int, state *State) { - c.AddAmount(val) +func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { + remainder := new(big.Int).Mul(gas, price) + c.AddAmount(remainder) } func (c *StateObject) AddAmount(amount *big.Int) { @@ -82,18 +84,33 @@ func (c *StateObject) SubAmount(amount *big.Int) { c.Amount.Sub(c.Amount, amount) } +func (c *StateObject) ConvertGas(gas, price *big.Int) error { + total := new(big.Int).Mul(gas, price) + if total.Cmp(c.Amount) > 0 { + return fmt.Errorf("insufficient amount: %v, %v", c.Amount, total) + } + + c.SubAmount(total) + + return nil +} + +// Returns the address of the contract/account func (c *StateObject) Address() []byte { return c.address } +// Returns the main script body func (c *StateObject) Script() []byte { return c.script } +// Returns the initialization script func (c *StateObject) Init() []byte { return c.initScript } +// State object encoding methods func (c *StateObject) RlpEncode() []byte { var root interface{} if c.state != nil { @@ -113,6 +130,7 @@ func (c *StateObject) RlpDecode(data []byte) { c.script = decoder.Get(3).Bytes() } +// Converts an transaction in to a state object func MakeContract(tx *Transaction, state *State) *StateObject { // Create contract if there's no recipient if tx.IsContract() { diff --git a/ethchain/vm.go b/ethchain/vm.go index 85aefa685..c249adfeb 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -102,10 +102,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } */ - // TODO Get each instruction cost properly gas := new(big.Int) useGas := func(amount *big.Int) { - gas.Add(gas, new(big.Int).Mul(amount, closure.Price)) + gas.Add(gas, amount) } switch op { @@ -142,6 +141,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro return closure.Return(nil), fmt.Errorf("insufficient gas %v %v", closure.Gas, gas) } + + // Sub the amount of gas from the remaining closure.Gas.Sub(closure.Gas, gas) switch op { @@ -157,7 +158,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro x, y := stack.Popn() // (x + y) % 2 ** 256 base.Add(x, y) - base.Mod(base, Pow256) // Pop result back on the stack stack.Push(base) case oSUB: @@ -165,7 +165,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro x, y := stack.Popn() // (x - y) % 2 ** 256 base.Sub(x, y) - base.Mod(base, Pow256) // Pop result back on the stack stack.Push(base) case oMUL: @@ -173,7 +172,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro x, y := stack.Popn() // (x * y) % 2 ** 256 base.Mul(x, y) - base.Mod(base, Pow256) // Pop result back on the stack stack.Push(base) case oDIV: @@ -325,7 +323,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oCALLDATASIZE: stack.Push(big.NewInt(int64(len(closure.Args)))) case oGASPRICE: - // TODO + stack.Push(closure.Price) // 0x40 range case oPREVHASH: @@ -339,7 +337,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oDIFFICULTY: stack.Push(vm.vars.Diff) case oGASLIMIT: - // TODO + // TODO + stack.Push(big.NewInt(0)) // 0x50 range case oPUSH: // Push PC+1 on to the stack @@ -399,11 +398,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oJUMP: require(1) pc = stack.Pop() + // Reduce pc by one because of the increment that's at the end of this for loop + pc.Sub(pc, ethutil.Big1) case oJUMPI: require(2) cond, pos := stack.Popn() if cond.Cmp(ethutil.BigTrue) == 0 { pc = pos + pc.Sub(pc, ethutil.Big1) } case oPC: stack.Push(pc) @@ -421,21 +423,39 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro inSize, inOffset := stack.Popn() // Pop return size and offset retSize, retOffset := stack.Popn() + // Make sure there's enough gas + if closure.Gas.Cmp(gas) < 0 { + stack.Push(ethutil.BigFalse) + + break + } // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) // Fetch the contract which will serve as the closure body contract := vm.state.GetContract(addr.Bytes()) - // Create a new callable closure - closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price, value) - // Executer the closure and get the return value (if any) - ret, err := closure.Call(vm, args, hook) - if err != nil { - stack.Push(ethutil.BigFalse) - } else { - stack.Push(ethutil.BigTrue) - } - mem.Set(retOffset.Int64(), retSize.Int64(), ret) + if contract != nil { + // Prepay for the gas + // If gas is set to 0 use all remaining gas for the next call + if gas.Cmp(big.NewInt(0)) == 0 { + gas = closure.Gas + } + closure.Gas.Sub(closure.Gas, gas) + // Create a new callable closure + closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price, value) + // Executer the closure and get the return value (if any) + ret, err := closure.Call(vm, args, hook) + if err != nil { + stack.Push(ethutil.BigFalse) + } else { + stack.Push(ethutil.BigTrue) + } + + mem.Set(retOffset.Int64(), retSize.Int64(), ret) + } else { + ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes()) + stack.Push(ethutil.BigFalse) + } case oRETURN: require(2) size, offset := stack.Popn() diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index f66f2a896..cca9b876a 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -86,9 +86,9 @@ func TestRun4(t *testing.T) { int32 a = 10 int32 b = 20 if a > b { - int32 c = this.caller() + int32 c = this.Caller() } - exit() + Exit() `), false) script := ethutil.Assemble(asm...) tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script, nil) @@ -103,8 +103,9 @@ func TestRun4(t *testing.T) { store[1000] = 10^20 } - store[1001] = this.value() * 20 - store[this.origin()] = store[this.origin()] + 1000 + + store[1001] = this.Value() * 20 + store[this.Origin()] = store[this.Origin()] + 1000 if store[1001] > 20 { store[1001] = 10^50 @@ -112,8 +113,18 @@ func TestRun4(t *testing.T) { int8 ret = 0 int8 arg = 10 - store[1002] = "a46df28529eb8aa8b8c025b0b413c5f4b688352f" - call(store[1002], 0, 100000000, arg, ret) + Call(0xe6a12555fad1fb6eaaaed69001a87313d1fd7b54, 0, 100, arg, ret) + + big t + for int8 i = 0; i < 10; i++ { + t = i + } + + if 10 > 20 { + int8 shouldnt = 2 + } else { + int8 should = 1 + } `), false) if err != nil { fmt.Println(err) @@ -125,10 +136,17 @@ func TestRun4(t *testing.T) { callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript, nil) // Contract addr as test address + gas := big.NewInt(1000) + gasPrice := big.NewInt(10) account := NewAccount(ContractAddr, big.NewInt(10000000)) - fmt.Println(account) + fmt.Println("account.Amount =", account.Amount) c := MakeContract(callerTx, state) - callerClosure := NewClosure(account, c, c.script, state, big.NewInt(1000000000), big.NewInt(10), big.NewInt(0)) + e := account.ConvertGas(gas, gasPrice) + if e != nil { + fmt.Println(err) + } + fmt.Println("account.Amount =", account.Amount) + callerClosure := NewClosure(account, c, c.script, state, gas, gasPrice, big.NewInt(0)) vm := NewVm(state, RuntimeVars{ Origin: account.Address(), @@ -138,11 +156,11 @@ func TestRun4(t *testing.T) { Time: 1, Diff: big.NewInt(256), }) - _, e := callerClosure.Call(vm, nil, nil) + _, e = callerClosure.Call(vm, nil, nil) if e != nil { fmt.Println("error", e) } - fmt.Println(account) + fmt.Println("account.Amount =", account.Amount) } func TestRun5(t *testing.T) { @@ -156,6 +174,5 @@ func TestRun5(t *testing.T) { } exit() `), false) - script := ethutil.Assemble(asm...) - fmt.Println(Disassemble(script)) + ethutil.Assemble(asm...) } From 11c26e32114dabc6524ad9fb1f868440f5d3fff3 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Apr 2014 11:50:17 +0200 Subject: [PATCH 056/100] Implemented ethereum package reader --- ethutil/package.go | 123 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 ethutil/package.go diff --git a/ethutil/package.go b/ethutil/package.go new file mode 100644 index 000000000..e5df989d2 --- /dev/null +++ b/ethutil/package.go @@ -0,0 +1,123 @@ +package ethutil + +import ( + "archive/zip" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "strings" +) + +// Manifest object +// +// The manifest object holds all the relevant information supplied with the +// the manifest specified in the package +type Manifest struct { + Entry string + Height, Width int +} + +// External package +// +// External package contains the main html file and manifest +type ExtPackage struct { + EntryHtml string + Manifest *Manifest +} + +// Read file +// +// Read a given compressed file and returns the read bytes. +// Returns an error otherwise +func ReadFile(f *zip.File) ([]byte, error) { + rc, err := f.Open() + if err != nil { + return nil, err + } + defer rc.Close() + + content, err := ioutil.ReadAll(rc) + if err != nil { + return nil, err + } + + return content, nil +} + +// Reads manifest +// +// Reads and returns a manifest object. Returns error otherwise +func ReadManifest(m []byte) (*Manifest, error) { + var manifest Manifest + + dec := json.NewDecoder(strings.NewReader(string(m))) + if err := dec.Decode(&manifest); err == io.EOF { + } else if err != nil { + return nil, err + } + + return &manifest, nil +} + +// Find file in archive +// +// Returns the index of the given file name if it exists. -1 if file not found +func FindFileInArchive(fn string, files []*zip.File) (index int) { + index = -1 + // Find the manifest first + for i, f := range files { + if f.Name == fn { + index = i + } + } + + return +} + +// Open package +// +// Opens a prepared ethereum package +// Reads the manifest file and determines file contents and returns and +// the external package. +func OpenPackage(fn string) (*ExtPackage, error) { + r, err := zip.OpenReader(fn) + if err != nil { + return nil, err + } + defer r.Close() + + manifestIndex := FindFileInArchive("manifest.json", r.File) + + if manifestIndex < 0 { + return nil, fmt.Errorf("No manifest file found in archive") + } + + f, err := ReadFile(r.File[manifestIndex]) + if err != nil { + return nil, err + } + + manifest, err := ReadManifest(f) + if err != nil { + return nil, err + } + + if manifest.Entry == "" { + return nil, fmt.Errorf("Entry file specified but appears to be empty: %s", manifest.Entry) + } + + entryIndex := FindFileInArchive(manifest.Entry, r.File) + if entryIndex < 0 { + return nil, fmt.Errorf("Entry file not found: '%s'", manifest.Entry) + } + + f, err = ReadFile(r.File[entryIndex]) + if err != nil { + return nil, err + } + + extPackage := &ExtPackage{string(f), manifest} + + return extPackage, nil +} From 61cd1594b52514244efcb47bd93722aaec0fe456 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Apr 2014 11:50:38 +0200 Subject: [PATCH 057/100] Fixed gas, price & value setters on initialization --- ethchain/closure.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index defd8b5c8..f8135c514 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -35,7 +35,15 @@ type Closure struct { // Create a new closure for the given data items func NewClosure(callee Callee, object Reference, script []byte, state *State, gas, price, val *big.Int) *Closure { - return &Closure{callee, object, script, state, gas, price, val, nil} + c := &Closure{callee: callee, object: object, Script: script, State: state, Args: nil} + + // In most cases gas, price and value are pointers to transaction objects + // and we don't want the transaction's values to change. + c.Gas = new(big.Int).Set(gas) + c.Price = new(big.Int).Set(price) + c.Value = new(big.Int).Set(val) + + return c } // Retuns the x element in data slice From 3a9a252f6e44abb0f45f57a46c0fa91e2f73c545 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Apr 2014 11:51:04 +0200 Subject: [PATCH 058/100] Fixed minor issue with gas and added state object init --- ethchain/state_manager.go | 53 +++++++++++++++++++++++++++++++----- ethchain/transaction.go | 14 +++++----- ethchain/transaction_pool.go | 4 +-- ethchain/types.go | 6 ++-- ethchain/vm.go | 24 ++++++++-------- ethutil/parsing.go | 49 +++++++++++++++++++++++++++++++-- ethutil/parsing_test.go | 41 ++++++++++++++-------------- 7 files changed, 135 insertions(+), 56 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 75a78e9f3..23da77fae 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -91,11 +91,15 @@ func (sm *StateManager) BlockChain() *BlockChain { return sm.bc } -func (sm *StateManager) MakeContract(tx *Transaction) { +func (sm *StateManager) MakeContract(tx *Transaction) *StateObject { contract := MakeContract(tx, sm.procState) if contract != nil { sm.procState.states[string(tx.Hash()[12:])] = contract.state + + return contract } + + return nil } // Apply transactions uses the transaction passed to it and applies them onto @@ -103,11 +107,44 @@ func (sm *StateManager) MakeContract(tx *Transaction) { func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { // Process each transaction/contract for _, tx := range txs { + fmt.Printf("Processing Tx: %x\n", tx.Hash()) // If there's no recipient, it's a contract // Check if this is a contract creation traction and if so // create a contract of this tx. if tx.IsContract() { - sm.MakeContract(tx) + err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) + if err == nil { + contract := sm.MakeContract(tx) + if contract != nil { + sm.EvalScript(contract.Init(), contract, tx, block) + } else { + ethutil.Config.Log.Infoln("[STATE] Unable to create contract") + } + } else { + ethutil.Config.Log.Infoln("[STATE] contract create:", err) + } + } else { + err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) + contract := sm.procState.GetContract(tx.Recipient) + if err == nil && len(contract.Script()) > 0 { + sm.EvalScript(contract.Script(), contract, tx, block) + } else if err != nil { + ethutil.Config.Log.Infoln("[STATE] process:", err) + } + } + } + // Process each transaction/contract + for _, tx := range txs { + // If there's no recipient, it's a contract + // Check if this is a contract creation traction and if so + // create a contract of this tx. + if tx.IsContract() { + contract := sm.MakeContract(tx) + if contract != nil { + sm.EvalScript(contract.Init(), contract, tx, block) + } else { + ethutil.Config.Log.Infoln("[STATE] Unable to create contract") + } } else { // Figure out if the address this transaction was sent to is a // contract or an actual account. In case of a contract, we process that @@ -303,11 +340,13 @@ func (sm *StateManager) Stop() { func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Transaction, block *Block) { // Recovering function in case the VM had any errors - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered from VM execution with err =", r) - } - }() + /* + defer func() { + if r := recover(); r != nil { + fmt.Println("Recovered from VM execution with err =", r) + } + }() + */ caller := sm.procState.GetAccount(tx.Sender()) closure := NewClosure(caller, object, script, sm.procState, tx.Gas, tx.GasPrice, tx.Value) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 1e43a2bae..421f26c98 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -23,12 +23,12 @@ type Transaction struct { contractCreation bool } -func NewContractCreationTx(value, gasprice *big.Int, script []byte, init []byte) *Transaction { - return &Transaction{Value: value, GasPrice: gasprice, Data: script, Init: init, contractCreation: true} +func NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte, init []byte) *Transaction { + return &Transaction{Value: value, Gas: gas, GasPrice: gasPrice, Data: script, Init: init, contractCreation: true} } -func NewTransactionMessage(to []byte, value, gasprice, gas *big.Int, data []byte) *Transaction { - return &Transaction{Recipient: to, Value: value, GasPrice: gasprice, Gas: gas, Data: data} +func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction { + return &Transaction{Recipient: to, Value: value, GasPrice: gasPrice, Gas: gas, Data: data} } func NewTransactionFromBytes(data []byte) *Transaction { @@ -46,9 +46,10 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction { } func (tx *Transaction) Hash() []byte { - data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, string(tx.Data)} + data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, tx.Data} + if tx.contractCreation { - data = append(data, string(tx.Init)) + data = append(data, tx.Init) } return ethutil.Sha3Bin(ethutil.NewValue(data).Encode()) @@ -112,7 +113,6 @@ func (tx *Transaction) RlpData() interface{} { if tx.contractCreation { data = append(data, tx.Init) } - //d := ethutil.NewSliceValue(tx.Data).Slice() return append(data, tx.v, tx.r, tx.s) } diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 5cdda17e2..957381ac7 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -104,7 +104,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract // funds won't invalidate this transaction but simple ignores it. totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) if sender.Amount.Cmp(totAmount) < 0 { - return errors.New("[TXPL] Insufficient amount in sender's account") + return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) } if sender.Nonce != tx.Nonce { @@ -119,8 +119,6 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract if bytes.Compare(tx.Recipient, tx.Sender()) == 0 { // Subtract the fee sender.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat)) - } else if toContract { - sender.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat)) } else { // Subtract the amount from the senders account sender.SubAmount(totAmount) diff --git a/ethchain/types.go b/ethchain/types.go index 24aad82c3..827d4f27f 100644 --- a/ethchain/types.go +++ b/ethchain/types.go @@ -35,7 +35,7 @@ const ( oORIGIN = 0x32 oCALLER = 0x33 oCALLVALUE = 0x34 - oCALLDATA = 0x35 + oCALLDATALOAD = 0x35 oCALLDATASIZE = 0x36 oGASPRICE = 0x37 @@ -106,7 +106,7 @@ var opCodeToString = map[OpCode]string{ oORIGIN: "ORIGIN", oCALLER: "CALLER", oCALLVALUE: "CALLVALUE", - oCALLDATA: "CALLDATA", + oCALLDATALOAD: "CALLDATALOAD", oCALLDATASIZE: "CALLDATASIZE", oGASPRICE: "TXGASPRICE", @@ -180,7 +180,7 @@ var OpCodes = map[string]byte{ "ORIGIN": 0x32, "CALLER": 0x33, "CALLVALUE": 0x34, - "CALLDATA": 0x35, + "CALLDATALOAD": 0x35, "CALLDATASIZE": 0x36, "GASPRICE": 0x38, diff --git a/ethchain/vm.go b/ethchain/vm.go index c249adfeb..33541cb3b 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -84,11 +84,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // The base for all big integer arithmetic base := new(big.Int) - /* - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("# op\n") - } - */ + if ethutil.Config.Debug { + ethutil.Config.Log.Debugf("# op\n") + } for { step++ @@ -96,11 +94,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro val := closure.Get(pc) // Get the opcode (it must be an opcode!) op := OpCode(val.Uint()) - /* - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) - } - */ + if ethutil.Config.Debug { + ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) + } gas := new(big.Int) useGas := func(amount *big.Int) { @@ -316,10 +312,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oCALLVALUE: // FIXME: Original value of the call, not the current value stack.Push(closure.Value) - case oCALLDATA: + case oCALLDATALOAD: require(1) - offset := stack.Pop() - mem.Set(offset.Int64(), int64(len(closure.Args)), closure.Args) + offset := stack.Pop().Int64() + val := closure.Args[offset : offset+31] + + stack.Push(ethutil.BigD(val)) case oCALLDATASIZE: stack.Push(big.NewInt(int64(len(closure.Args)))) case oGASPRICE: diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 328704cae..9775cf328 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -3,7 +3,7 @@ package ethutil import ( _ "fmt" "math/big" - "regexp" + _ "regexp" ) // Op codes @@ -143,7 +143,6 @@ init() { main() { // main something } -*/ func PreProcess(data string) (mainInput, initInput string) { reg := "\\(\\)\\s*{([\\d\\w\\W\\n\\s]+?)}" mainReg := regexp.MustCompile("main" + reg) @@ -163,3 +162,49 @@ func PreProcess(data string) (mainInput, initInput string) { return } +*/ + +// Very, very dumb parser. Heed no attention :-) +func FindFor(blockMatcher, input string) string { + curCount := -1 + length := len(blockMatcher) + matchfst := rune(blockMatcher[0]) + var currStr string + + for i, run := range input { + // Find init + if curCount == -1 && run == matchfst && input[i:i+length] == blockMatcher { + curCount = 0 + } else if curCount > -1 { + if run == '{' { + curCount++ + if curCount == 1 { + continue + } + } else if run == '}' { + curCount-- + if curCount == 0 { + // we are done + curCount = -1 + break + } + } + + if curCount > 0 { + currStr += string(run) + } + } + } + + return currStr +} + +func PreProcess(data string) (mainInput, initInput string) { + mainInput = FindFor("main", data) + if mainInput == "" { + mainInput = data + } + initInput = FindFor("init", data) + + return +} diff --git a/ethutil/parsing_test.go b/ethutil/parsing_test.go index 6b59777e6..a9ad347dd 100644 --- a/ethutil/parsing_test.go +++ b/ethutil/parsing_test.go @@ -1,32 +1,31 @@ package ethutil -/* import ( - "math" + "fmt" "testing" ) -func TestCompile(t *testing.T) { - instr, err := CompileInstr("PUSH") - - if err != nil { - t.Error("Failed compiling instruction") +func TestPreProcess(t *testing.T) { + main, init := PreProcess(` + init { + // init + if a > b { + if { + } + } } - calc := (48 + 0*256 + 0*int64(math.Pow(256, 2))) - if BigD(instr).Int64() != calc { - t.Error("Expected", calc, ", got:", instr) + main { + // main + if a > b { + if c > d { + } + } } -} - -func TestValidInstr(t *testing.T) { - op, args, err := Instr("68163") - if err != nil { - t.Error("Error decoding instruction") - } + `) + fmt.Println("main") + fmt.Println(main) + fmt.Println("init") + fmt.Println(init) } - -func TestInvalidInstr(t *testing.T) { -} -*/ From 6b08efabf837c9c763e116b91dc9b566a2c76d80 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Apr 2014 12:14:28 +0200 Subject: [PATCH 059/100] @maranh see comment --- ethminer/miner.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/ethminer/miner.go b/ethminer/miner.go index 5bbf6b977..d1636ccee 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -61,10 +61,10 @@ func (miner *Miner) listener() { select { case chanMessage := <-miner.reactChan: if block, ok := chanMessage.Resource.(*ethchain.Block); ok { - log.Println("[MINER] Got new block via Reactor") + //log.Println("[MINER] Got new block via Reactor") if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 { // TODO: Perhaps continue mining to get some uncle rewards - log.Println("[MINER] New top block found resetting state") + //log.Println("[MINER] New top block found resetting state") // Filter out which Transactions we have that were not in this block var newtxs []*ethchain.Transaction @@ -86,7 +86,7 @@ func (miner *Miner) listener() { } else { if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 { - log.Println("[MINER] Adding uncle block") + //log.Println("[MINER] Adding uncle block") miner.uncles = append(miner.uncles, block) miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State()) } @@ -94,7 +94,7 @@ func (miner *Miner) listener() { } if tx, ok := chanMessage.Resource.(*ethchain.Transaction); ok { - log.Println("[MINER] Got new transaction from Reactor", tx) + //log.Println("[MINER] Got new transaction from Reactor", tx) found := false for _, ctx := range miner.txs { if found = bytes.Compare(ctx.Hash(), tx.Hash()) == 0; found { @@ -103,12 +103,12 @@ func (miner *Miner) listener() { } if found == false { - log.Println("[MINER] We did not know about this transaction, adding") + //log.Println("[MINER] We did not know about this transaction, adding") miner.txs = append(miner.txs, tx) miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) miner.block.SetTransactions(miner.txs) } else { - log.Println("[MINER] We already had this transaction, ignoring") + //log.Println("[MINER] We already had this transaction, ignoring") } } default: @@ -127,7 +127,7 @@ func (miner *Miner) listener() { miner.ethereum.StateManager().AccumelateRewards(miner.block) // Search the nonce - log.Println("[MINER] Initialision complete, starting mining") + //log.Println("[MINER] Initialision complete, starting mining") miner.block.Nonce = miner.pow.Search(miner.block, miner.quitChan) if miner.block.Nonce != nil { miner.ethereum.StateManager().PrepareDefault(miner.block) @@ -136,6 +136,7 @@ func (miner *Miner) listener() { log.Println("Error result from process block:", err) } else { + // XXX @maranh This is already done in the state manager, why a 2nd time? if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) { log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce)) } From f7d4e3cd6b276e4d66abe89b4f1b2dfa2faf90df Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Apr 2014 15:52:50 +0200 Subject: [PATCH 060/100] Copy over bytes from previous root Copy over instead of directly using the previous root. This is order to avoid resetting problems --- ethchain/block.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ethchain/block.go b/ethchain/block.go index 8c93947fb..c9197c69a 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -80,6 +80,9 @@ func CreateBlock(root interface{}, extra string, txes []*Transaction) *Block { + // Copy over the bytes + copiedRoot := ethutil.NewValue(root).Bytes() + block := &Block{ // Slice of transactions to include in this block transactions: txes, @@ -95,7 +98,7 @@ func CreateBlock(root interface{}, block.SetTransactions(txes) block.SetUncles([]*Block{}) - block.state = NewState(ethutil.NewTrie(ethutil.Config.Db, root)) + block.state = NewState(ethutil.NewTrie(ethutil.Config.Db, copiedRoot)) for _, tx := range txes { block.MakeContract(tx) From ef7f3f36e261bc500016dac0a703c5b7931a1721 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Apr 2014 15:53:34 +0200 Subject: [PATCH 061/100] Renamed CALLDATA to CALLDATALOAD --- ethutil/parsing.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethutil/parsing.go b/ethutil/parsing.go index 9775cf328..514fd92cd 100644 --- a/ethutil/parsing.go +++ b/ethutil/parsing.go @@ -39,7 +39,7 @@ var OpCodes = map[string]byte{ "ORIGIN": 0x32, "CALLER": 0x33, "CALLVALUE": 0x34, - "CALLDATA": 0x35, + "CALLDATALOAD": 0x35, "CALLDATASIZE": 0x36, "GASPRICE": 0x38, From c81804444f69ae1653d54551d8555ff924651cd9 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Apr 2014 15:53:53 +0200 Subject: [PATCH 062/100] Call initial closure with proper tx argument --- ethchain/block_chain.go | 1 + ethchain/state_manager.go | 33 +-------------------------------- ethchain/vm.go | 4 +++- 3 files changed, 5 insertions(+), 33 deletions(-) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index d65c38fe4..08886c9cd 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -46,6 +46,7 @@ func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block { hash = bc.LastBlockHash lastBlockTime = bc.CurrentBlock.Time } + block := CreateBlock( root, hash, diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 23da77fae..668a44c3f 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -133,37 +133,6 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { } } } - // Process each transaction/contract - for _, tx := range txs { - // If there's no recipient, it's a contract - // Check if this is a contract creation traction and if so - // create a contract of this tx. - if tx.IsContract() { - contract := sm.MakeContract(tx) - if contract != nil { - sm.EvalScript(contract.Init(), contract, tx, block) - } else { - ethutil.Config.Log.Infoln("[STATE] Unable to create contract") - } - } else { - // Figure out if the address this transaction was sent to is a - // contract or an actual account. In case of a contract, we process that - // contract instead of moving funds between accounts. - var err error - if contract := sm.procState.GetContract(tx.Recipient); contract != nil { - err = sm.Ethereum.TxPool().ProcessTransaction(tx, block, true) - if err == nil { - sm.EvalScript(contract.Script(), contract, tx, block) - } - } else { - err = sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) - } - - if err != nil { - ethutil.Config.Log.Infoln("[STATE]", err) - } - } - } } // The prepare function, prepares the state manager for the next @@ -359,7 +328,7 @@ func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Trans Diff: block.Difficulty, //Price: tx.GasPrice, }) - closure.Call(vm, nil, nil) + closure.Call(vm, tx.Data, nil) // Update the account (refunds) sm.procState.UpdateStateObject(caller) diff --git a/ethchain/vm.go b/ethchain/vm.go index 33541cb3b..90b591f50 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -53,11 +53,12 @@ var isRequireError = false func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err error) { // Recover from any require exception defer func() { - if r := recover(); r != nil && isRequireError { + if r := recover(); r != nil /*&& isRequireError*/ { fmt.Println(r) ret = closure.Return(nil) err = fmt.Errorf("%v", r) + fmt.Println("vm err", err) } }() @@ -315,6 +316,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oCALLDATALOAD: require(1) offset := stack.Pop().Int64() + fmt.Println(closure.Args) val := closure.Args[offset : offset+31] stack.Push(ethutil.BigD(val)) From 0651af9dfd701ba09e6c734f21eff85f61454476 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 23 Apr 2014 15:54:15 +0200 Subject: [PATCH 063/100] Removed some log statements and disabled additional validation checks --- ethminer/miner.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ethminer/miner.go b/ethminer/miner.go index d1636ccee..08a4626e4 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -134,14 +134,18 @@ func (miner *Miner) listener() { err := miner.ethereum.StateManager().ProcessBlock(miner.block, true) if err != nil { log.Println("Error result from process block:", err) + miner.block.State().Reset() } else { - // XXX @maranh This is already done in the state manager, why a 2nd time? - if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) { - log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce)) - } + /* + // XXX @maranh This is already done in the state manager, why a 2nd time? + if !miner.ethereum.StateManager().Pow.Verify(miner.block.HashNoNonce(), miner.block.Difficulty, miner.block.Nonce) { + log.Printf("Second stage verification error: Block's nonce is invalid (= %v)\n", ethutil.Hex(miner.block.Nonce)) + } + */ miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val}) log.Printf("[MINER] 🔨 Mined block %x\n", miner.block.Hash()) + log.Println(miner.block) miner.txs = []*ethchain.Transaction{} // Move this somewhere neat miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) From 1c85d8c66b9db23687b0446b4a7e97e3e61fe188 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Apr 2014 00:00:50 +0200 Subject: [PATCH 064/100] Minor improvements and bug fixes * Fixed VM base bug --- ethchain/state.go | 4 ++-- ethchain/state_manager.go | 2 ++ ethchain/transaction_pool.go | 8 ++++---- ethchain/vm.go | 6 ++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/ethchain/state.go b/ethchain/state.go index 655848932..fa63accf8 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -34,12 +34,12 @@ func (s *State) Reset() { // Syncs the trie and all siblings func (s *State) Sync() { - s.trie.Sync() - // Sync all nested states for _, state := range s.states { state.Sync() } + + s.trie.Sync() } // Purges the current trie. diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 668a44c3f..29c3cd16b 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -117,6 +117,7 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { contract := sm.MakeContract(tx) if contract != nil { sm.EvalScript(contract.Init(), contract, tx, block) + fmt.Printf("state root of contract %x\n", contract.State().Root()) } else { ethutil.Config.Log.Infoln("[STATE] Unable to create contract") } @@ -332,4 +333,5 @@ func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Trans // Update the account (refunds) sm.procState.UpdateStateObject(caller) + sm.procState.UpdateStateObject(object) } diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 957381ac7..91fad2635 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -100,6 +100,10 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract // Get the sender sender := block.state.GetAccount(tx.Sender()) + if sender.Nonce != tx.Nonce { + return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transaction nonce is %d instead", sender.Nonce, tx.Nonce) + } + // Make sure there's enough in the sender's account. Having insufficient // funds won't invalidate this transaction but simple ignores it. totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) @@ -107,10 +111,6 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) } - if sender.Nonce != tx.Nonce { - return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transaction nonce is %d instead", sender.Nonce, tx.Nonce) - } - // Get the receiver receiver := block.state.GetAccount(tx.Recipient) sender.Nonce += 1 diff --git a/ethchain/vm.go b/ethchain/vm.go index 90b591f50..7df63b181 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -82,14 +82,15 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro pc := big.NewInt(0) // Current step count step := 0 - // The base for all big integer arithmetic - base := new(big.Int) if ethutil.Config.Debug { ethutil.Config.Log.Debugf("# op\n") } for { + // The base for all big integer arithmetic + base := new(big.Int) + step++ // Get the memory location of pc val := closure.Get(pc) @@ -390,6 +391,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro require(1) loc := stack.Pop() val := closure.GetMem(loc) + fmt.Printf("load %x = %v\n", loc.Bytes(), val.BigInt()) stack.Push(val.BigInt()) case oSSTORE: require(2) From ee7c16a8d977389c63ef60ea6c5eaff11e150ca4 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Apr 2014 13:30:57 +0200 Subject: [PATCH 065/100] Fixed Base problem and sload/sstore --- ethchain/vm.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ethchain/vm.go b/ethchain/vm.go index 7df63b181..bc4c65d03 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -121,7 +121,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } else { mult = ethutil.Big1 } - useGas(base.Mul(mult, GasSStore)) + useGas(new(big.Int).Mul(mult, GasSStore)) case oBALANCE: useGas(GasBalance) case oCREATE: @@ -156,6 +156,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro x, y := stack.Popn() // (x + y) % 2 ** 256 base.Add(x, y) + fmt.Println(x, y, base) // Pop result back on the stack stack.Push(base) case oSUB: @@ -317,8 +318,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oCALLDATALOAD: require(1) offset := stack.Pop().Int64() - fmt.Println(closure.Args) - val := closure.Args[offset : offset+31] + val := closure.Args[offset : offset+32] + fmt.Println(ethutil.BigD(val)) stack.Push(ethutil.BigD(val)) case oCALLDATASIZE: From f3818478e2601df1d9cfc9cc36b021366f870856 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 24 Apr 2014 13:48:33 +0200 Subject: [PATCH 066/100] Removed debug & unused functions --- ethchain/block.go | 25 ------------------------- ethminer/miner.go | 1 - 2 files changed, 26 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index c9197c69a..d95ebf4b5 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -116,11 +116,6 @@ func (block *Block) HashNoNonce() []byte { return ethutil.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.trie.Root, block.TxSha, block.Difficulty, block.Time, block.Extra})) } -func (block *Block) PrintHash() { - fmt.Println(block) - fmt.Println(ethutil.NewValue(ethutil.Encode([]interface{}{block.PrevHash, block.UncleSha, block.Coinbase, block.state.trie.Root, block.TxSha, block.Difficulty, block.Time, block.Extra, block.Nonce}))) -} - func (block *Block) State() *State { return block.state } @@ -182,26 +177,6 @@ func (block *Block) MakeContract(tx *Transaction) { } /////// Block Encoding -func (block *Block) encodedUncles() interface{} { - uncles := make([]interface{}, len(block.Uncles)) - for i, uncle := range block.Uncles { - uncles[i] = uncle.RlpEncode() - } - - return uncles -} - -func (block *Block) encodedTxs() interface{} { - // Marshal the transactions of this block - encTx := make([]interface{}, len(block.transactions)) - for i, tx := range block.transactions { - // Cast it to a string (safe) - encTx[i] = tx.RlpData() - } - - return encTx -} - func (block *Block) rlpTxs() interface{} { // Marshal the transactions of this block encTx := make([]interface{}, len(block.transactions)) diff --git a/ethminer/miner.go b/ethminer/miner.go index 08a4626e4..791e8e402 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -145,7 +145,6 @@ func (miner *Miner) listener() { */ miner.ethereum.Broadcast(ethwire.MsgBlockTy, []interface{}{miner.block.Value().Val}) log.Printf("[MINER] 🔨 Mined block %x\n", miner.block.Hash()) - log.Println(miner.block) miner.txs = []*ethchain.Transaction{} // Move this somewhere neat miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) From 0f93da400ab7fd238eb7286f14c229d780f73636 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 26 Apr 2014 01:47:55 +0200 Subject: [PATCH 067/100] Added new state object change echanism --- ethchain/state_manager.go | 33 +++++++++++++------ ethchain/state_object.go | 67 +++++++++++++++++++++------------------ ethchain/vm.go | 6 ++-- ethutil/value.go | 7 +++- 4 files changed, 69 insertions(+), 44 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 29c3cd16b..1ab58386a 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -50,6 +50,10 @@ type StateManager struct { // Comparative state it used for comparing and validating end // results compState *State + + // It's generally know that a map is faster for small lookups than arrays + // we'll eventually have to make a decision if the map grows too large + watchedAddresses map[string]bool } func NewStateManager(ethereum EthManager) *StateManager { @@ -60,6 +64,7 @@ func NewStateManager(ethereum EthManager) *StateManager { Ethereum: ethereum, stateObjectCache: NewStateObjectCache(), bc: ethereum.BlockChain(), + watchedAddresses: make(map[string]bool), } sm.procState = ethereum.BlockChain().CurrentBlock.State() return sm @@ -309,18 +314,9 @@ func (sm *StateManager) Stop() { } func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Transaction, block *Block) { - // Recovering function in case the VM had any errors - /* - defer func() { - if r := recover(); r != nil { - fmt.Println("Recovered from VM execution with err =", r) - } - }() - */ - caller := sm.procState.GetAccount(tx.Sender()) closure := NewClosure(caller, object, script, sm.procState, tx.Gas, tx.GasPrice, tx.Value) - vm := NewVm(sm.procState, RuntimeVars{ + vm := NewVm(sm.procState, sm, RuntimeVars{ Origin: caller.Address(), BlockNumber: block.BlockInfo().Number, PrevHash: block.PrevHash, @@ -333,5 +329,22 @@ func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Trans // Update the account (refunds) sm.procState.UpdateStateObject(caller) + sm.Changed(caller) sm.procState.UpdateStateObject(object) + sm.Changed(object) +} + +// Watch a specific address +func (sm *StateManager) Watch(addr []byte) { + if !sm.watchedAddresses[string(addr)] { + sm.watchedAddresses[string(addr)] = true + } +} + +// The following objects are used when changing a value and using the "watched" attribute +// to determine whether the reactor should be used to notify any subscribers on the address +func (sm *StateManager) Changed(stateObject *StateObject) { + if sm.watchedAddresses[string(stateObject.Address())] { + sm.Ethereum.Reactor().Post("addressChanged", stateObject) + } } diff --git a/ethchain/state_object.go b/ethchain/state_object.go index f562e5b04..8d86ef44e 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -18,6 +18,28 @@ type StateObject struct { initScript []byte } +// Converts an transaction in to a state object +func MakeContract(tx *Transaction, state *State) *StateObject { + // Create contract if there's no recipient + if tx.IsContract() { + // FIXME + addr := tx.Hash()[12:] + + value := tx.Value + contract := NewContract(addr, value, []byte("")) + state.UpdateStateObject(contract) + + contract.script = tx.Data + contract.initScript = tx.Init + + state.UpdateStateObject(contract) + + return contract + } + + return nil +} + func NewContract(address []byte, Amount *big.Int, root []byte) *StateObject { contract := &StateObject{address: address, Amount: Amount, Nonce: 0} contract.state = NewState(ethutil.NewTrie(ethutil.Config.Db, string(root))) @@ -39,6 +61,10 @@ func NewStateObjectFromBytes(address, data []byte) *StateObject { return object } +func (c *StateObject) State() *State { + return c.state +} + func (c *StateObject) Addr(addr []byte) *ethutil.Value { return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) } @@ -47,8 +73,10 @@ func (c *StateObject) SetAddr(addr []byte, value interface{}) { c.state.trie.Update(string(addr), string(ethutil.NewValue(value).Encode())) } -func (c *StateObject) State() *State { - return c.state +func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) { + addr := ethutil.BigToBytes(num, 256) + c.SetAddr(addr, val) + //c.state.trie.Update(string(addr), string(val.Encode())) } func (c *StateObject) GetMem(num *big.Int) *ethutil.Value { @@ -65,11 +93,6 @@ func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { return ethutil.NewValueFromBytes([]byte{c.script[pc.Int64()]}) } -func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) { - addr := ethutil.BigToBytes(num, 256) - c.state.trie.Update(string(addr), string(val.Encode())) -} - // Return the gas back to the origin. Used by the Virtual machine or Closures func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { remainder := new(big.Int).Mul(gas, price) @@ -77,11 +100,15 @@ func (c *StateObject) ReturnGas(gas, price *big.Int, state *State) { } func (c *StateObject) AddAmount(amount *big.Int) { - c.Amount.Add(c.Amount, amount) + c.SetAmount(new(big.Int).Add(c.Amount, amount)) } func (c *StateObject) SubAmount(amount *big.Int) { - c.Amount.Sub(c.Amount, amount) + c.SetAmount(new(big.Int).Sub(c.Amount, amount)) +} + +func (c *StateObject) SetAmount(amount *big.Int) { + c.Amount = amount } func (c *StateObject) ConvertGas(gas, price *big.Int) error { @@ -130,28 +157,6 @@ func (c *StateObject) RlpDecode(data []byte) { c.script = decoder.Get(3).Bytes() } -// Converts an transaction in to a state object -func MakeContract(tx *Transaction, state *State) *StateObject { - // Create contract if there's no recipient - if tx.IsContract() { - // FIXME - addr := tx.Hash()[12:] - - value := tx.Value - contract := NewContract(addr, value, []byte("")) - state.UpdateStateObject(contract) - - contract.script = tx.Data - contract.initScript = tx.Init - - state.UpdateStateObject(contract) - - return contract - } - - return nil -} - // The cached state and state object cache are helpers which will give you somewhat // control over the nonce. When creating new transactions you're interested in the 'next' // nonce rather than the current nonce. This to avoid creating invalid-nonce transactions. diff --git a/ethchain/vm.go b/ethchain/vm.go index bc4c65d03..93557007d 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -30,6 +30,8 @@ type Vm struct { vars RuntimeVars state *State + + stateManager *StateManager } type RuntimeVars struct { @@ -42,8 +44,8 @@ type RuntimeVars struct { TxData []string } -func NewVm(state *State, vars RuntimeVars) *Vm { - return &Vm{vars: vars, state: state} +func NewVm(state *State, stateManager *StateManager, vars RuntimeVars) *Vm { + return &Vm{vars: vars, state: state, stateManager: stateManager} } var Pow256 = ethutil.BigPow(2, 256) diff --git a/ethutil/value.go b/ethutil/value.go index 04131aba9..b7756f9b1 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -20,7 +20,12 @@ func (val *Value) String() string { } func NewValue(val interface{}) *Value { - return &Value{Val: val} + t := val + if v, ok := val.(*Value); ok { + t = v.Val + } + + return &Value{Val: t} } func (val *Value) Type() reflect.Kind { From d3a159ad3d6842ffb137fc1df48a54703345faa4 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 26 Apr 2014 01:54:45 +0200 Subject: [PATCH 068/100] Fixed tests --- ethchain/vm_test.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index cca9b876a..75a4f5afb 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -91,7 +91,7 @@ func TestRun4(t *testing.T) { Exit() `), false) script := ethutil.Assemble(asm...) - tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), script, nil) + tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), script, nil) addr := tx.Hash()[12:] contract := MakeContract(tx, state) state.UpdateStateObject(contract) @@ -133,7 +133,7 @@ func TestRun4(t *testing.T) { fmt.Println(asm) callerScript := ethutil.Assemble(asm...) - callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), callerScript, nil) + callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript, nil) // Contract addr as test address gas := big.NewInt(1000) @@ -148,7 +148,7 @@ func TestRun4(t *testing.T) { fmt.Println("account.Amount =", account.Amount) callerClosure := NewClosure(account, c, c.script, state, gas, gasPrice, big.NewInt(0)) - vm := NewVm(state, RuntimeVars{ + vm := NewVm(state, nil, RuntimeVars{ Origin: account.Address(), BlockNumber: 1, PrevHash: ethutil.FromHex("5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"), From ca6e3f6defae958cece52ee7d26ca1b53b0adebb Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 26 Apr 2014 02:06:25 +0200 Subject: [PATCH 069/100] Notify of changes --- ethchain/vm.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ethchain/vm.go b/ethchain/vm.go index 93557007d..a4b4d351b 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -56,8 +56,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Recover from any require exception defer func() { if r := recover(); r != nil /*&& isRequireError*/ { - fmt.Println(r) - ret = closure.Return(nil) err = fmt.Errorf("%v", r) fmt.Println("vm err", err) @@ -158,7 +156,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro x, y := stack.Popn() // (x + y) % 2 ** 256 base.Add(x, y) - fmt.Println(x, y, base) // Pop result back on the stack stack.Push(base) case oSUB: @@ -321,7 +318,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro require(1) offset := stack.Pop().Int64() val := closure.Args[offset : offset+32] - fmt.Println(ethutil.BigD(val)) stack.Push(ethutil.BigD(val)) case oCALLDATASIZE: @@ -394,7 +390,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro require(1) loc := stack.Pop() val := closure.GetMem(loc) - fmt.Printf("load %x = %v\n", loc.Bytes(), val.BigInt()) stack.Push(val.BigInt()) case oSSTORE: require(2) @@ -452,8 +447,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro ret, err := closure.Call(vm, args, hook) if err != nil { stack.Push(ethutil.BigFalse) + // Reset the changes applied this object + //contract.State().Reset() } else { stack.Push(ethutil.BigTrue) + // Notify of the changes + vm.stateManager.Changed(contract) } mem.Set(retOffset.Int64(), retSize.Int64(), ret) From 05d2d8f27d0bea5b20be9bc3b4a259a12298ecab Mon Sep 17 00:00:00 2001 From: obscuren Date: Sat, 26 Apr 2014 02:11:00 +0200 Subject: [PATCH 070/100] Actually convert gas --- ethchain/state_manager.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 1ab58386a..628ab6a27 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -314,10 +314,17 @@ func (sm *StateManager) Stop() { } func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Transaction, block *Block) { - caller := sm.procState.GetAccount(tx.Sender()) - closure := NewClosure(caller, object, script, sm.procState, tx.Gas, tx.GasPrice, tx.Value) + account := sm.procState.GetAccount(tx.Sender()) + + err := account.ConvertGas(tx.Gas, tx.GasPrice) + if err != nil { + ethutil.Config.Log.Debugln(err) + return + } + + closure := NewClosure(account, object, script, sm.procState, tx.Gas, tx.GasPrice, tx.Value) vm := NewVm(sm.procState, sm, RuntimeVars{ - Origin: caller.Address(), + Origin: account.Address(), BlockNumber: block.BlockInfo().Number, PrevHash: block.PrevHash, Coinbase: block.Coinbase, @@ -328,8 +335,8 @@ func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Trans closure.Call(vm, tx.Data, nil) // Update the account (refunds) - sm.procState.UpdateStateObject(caller) - sm.Changed(caller) + sm.procState.UpdateStateObject(account) + sm.Changed(account) sm.procState.UpdateStateObject(object) sm.Changed(object) } From 16e52327a4baa5547c38965fce53b3ff40b98173 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 27 Apr 2014 16:50:44 +0200 Subject: [PATCH 071/100] Upped version number --- README.md | 2 +- ethchain/closure.go | 23 +++++++++++------ ethchain/stack.go | 12 +++++++++ ethchain/state.go | 29 +++++++++++++++++++++ ethchain/state_object.go | 4 +++ ethchain/vm.go | 54 ++++++++++++++++++++++++++++++++++++++-- ethutil/big.go | 8 ++++++ ethutil/common.go | 7 ++++++ ethutil/config.go | 2 +- 9 files changed, 129 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 0f0a33edb..9d810643b 100644 --- a/README.md +++ b/README.md @@ -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 3.5". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). +of Concept 5.0". 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/ethchain/closure.go b/ethchain/closure.go index f8135c514..57abaa91e 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -8,21 +8,24 @@ import ( ) type Callee interface { - ReturnGas(*big.Int, *big.Int, *State) - Address() []byte } type Reference interface { Callee - ethutil.RlpEncodable +} + +type ClosureRef interface { + ReturnGas(*big.Int, *big.Int, *State) + Address() []byte GetMem(*big.Int) *ethutil.Value SetMem(*big.Int, *ethutil.Value) + N() *big.Int } // Basic inline closure object which implement the 'closure' interface type Closure struct { - callee Callee - object Reference + callee ClosureRef + object ClosureRef Script []byte State *State @@ -34,7 +37,7 @@ type Closure struct { } // Create a new closure for the given data items -func NewClosure(callee Callee, object Reference, script []byte, state *State, gas, price, val *big.Int) *Closure { +func NewClosure(callee, object ClosureRef, script []byte, state *State, gas, price, val *big.Int) *Closure { c := &Closure{callee: callee, object: object, Script: script, State: state, Args: nil} // In most cases gas, price and value are pointers to transaction objects @@ -105,10 +108,14 @@ func (c *Closure) ReturnGas(gas, price *big.Int, state *State) { c.Gas.Add(c.Gas, gas) } -func (c *Closure) Object() Reference { +func (c *Closure) Object() ClosureRef { return c.object } -func (c *Closure) Callee() Callee { +func (c *Closure) Callee() ClosureRef { return c.callee } + +func (c *Closure) N() *big.Int { + return c.object.N() +} diff --git a/ethchain/stack.go b/ethchain/stack.go index 288360062..e9297b324 100644 --- a/ethchain/stack.go +++ b/ethchain/stack.go @@ -67,6 +67,18 @@ func (st *Stack) Peekn() (*big.Int, *big.Int) { func (st *Stack) Push(d *big.Int) { st.data = append(st.data, d) } + +func (st *Stack) Get(amount *big.Int) []*big.Int { + // offset + size <= len(data) + length := big.NewInt(int64(len(st.data))) + if amount.Cmp(length) <= 0 { + start := new(big.Int).Sub(length, amount) + return st.data[start.Int64():length.Int64()] + } + + return nil +} + func (st *Stack) Print() { fmt.Println("### stack ###") if len(st.data) > 0 { diff --git a/ethchain/state.go b/ethchain/state.go index fa63accf8..1b5655d4c 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -47,6 +47,7 @@ func (s *State) Purge() int { return s.trie.NewIterator().Purge() } +// XXX Deprecated func (s *State) GetContract(addr []byte) *StateObject { data := s.trie.Get(string(addr)) if data == "" { @@ -68,6 +69,32 @@ func (s *State) GetContract(addr []byte) *StateObject { return contract } +func (s *State) GetStateObject(addr []byte) *StateObject { + data := s.trie.Get(string(addr)) + if data == "" { + return nil + } + + stateObject := NewStateObjectFromBytes(addr, []byte(data)) + + // Check if there's a cached state for this contract + cachedStateObject := s.states[string(addr)] + if cachedStateObject != nil { + stateObject.state = cachedStateObject + } else { + // If it isn't cached, cache the state + s.states[string(addr)] = stateObject.state + } + + return stateObject +} + +func (s *State) SetStateObject(stateObject *StateObject) { + s.states[string(stateObject.address)] = stateObject.state + + s.UpdateStateObject(stateObject) +} + func (s *State) GetAccount(addr []byte) (account *StateObject) { data := s.trie.Get(string(addr)) if data == "" { @@ -97,6 +124,7 @@ const ( UnknownTy ) +/* // Returns the object stored at key and the type stored at key // Returns nil if nothing is stored func (s *State) GetStateObject(key []byte) (*ethutil.Value, ObjType) { @@ -124,6 +152,7 @@ func (s *State) GetStateObject(key []byte) (*ethutil.Value, ObjType) { return val, typ } +*/ // Updates any given state object func (s *State) UpdateStateObject(object *StateObject) { diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 8d86ef44e..8e921795d 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -65,6 +65,10 @@ func (c *StateObject) State() *State { return c.state } +func (c *StateObject) N() *big.Int { + return big.NewInt(int64(c.Nonce)) +} + func (c *StateObject) Addr(addr []byte) *ethutil.Value { return ethutil.NewValueFromBytes([]byte(c.state.trie.Get(string(addr)))) } diff --git a/ethchain/vm.go b/ethchain/vm.go index a4b4d351b..b983e88ff 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -20,6 +20,17 @@ var ( GasMemory = big.NewInt(1) ) +func CalculateTxGas(initSize, scriptSize *big.Int) *big.Int { + totalGas := new(big.Int) + totalGas.Add(totalGas, GasCreate) + + txTotalBytes := new(big.Int).Add(initSize, scriptSize) + txTotalBytes.Div(txTotalBytes, ethutil.Big32) + totalGas.Add(totalGas, new(big.Int).Mul(txTotalBytes, GasSStore)) + + return totalGas +} + type Vm struct { txPool *TxPool // Stack for processing contracts @@ -125,7 +136,12 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oBALANCE: useGas(GasBalance) case oCREATE: - useGas(GasCreate) + require(3) + + args := stack.Get(big.NewInt(3)) + initSize := new(big.Int).Add(args[1], args[0]) + + useGas(CalculateTxGas(initSize, ethutil.Big0)) case oCALL: useGas(GasCall) case oMLOAD, oMSIZE, oMSTORE8, oMSTORE: @@ -413,6 +429,39 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro stack.Push(big.NewInt(int64(mem.Len()))) // 0x60 range case oCREATE: + require(3) + + value := stack.Pop() + size, offset := stack.Popn() + + // Generate a new address + addr := ethutil.CreateAddress(closure.callee.Address(), closure.callee.N()) + // Create a new contract + contract := NewContract(addr, value, []byte("")) + // Set the init script + contract.initScript = mem.Get(offset.Int64(), size.Int64()) + // Transfer all remaining gas to the new + // contract so it may run the init script + gas := new(big.Int).Set(closure.Gas) + closure.Gas.Sub(closure.Gas, gas) + // Create the closure + closure := NewClosure(closure.callee, + closure.Object(), + contract.initScript, + vm.state, + gas, + closure.Price, + value) + // Call the closure and set the return value as + // main script. + closure.Script, err = closure.Call(vm, nil, hook) + if err != nil { + stack.Push(ethutil.BigFalse) + } else { + stack.Push(ethutil.BigD(addr)) + + vm.state.SetStateObject(contract) + } case oCALL: require(7) // Closure addr @@ -438,7 +487,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Prepay for the gas // If gas is set to 0 use all remaining gas for the next call if gas.Cmp(big.NewInt(0)) == 0 { - gas = closure.Gas + // Copy + gas = new(big.Int).Set(closure.Gas) } closure.Gas.Sub(closure.Gas, gas) // Create a new callable closure diff --git a/ethutil/big.go b/ethutil/big.go index 1a3902fa3..c0488a71f 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -50,3 +50,11 @@ func BigCopy(src *big.Int) (ret *big.Int) { return } + +func BigMax(x, y *big.Int) *big.Int { + if x.Cmp(y) <= 0 { + return x + } + + return y +} diff --git a/ethutil/common.go b/ethutil/common.go index c63af29a6..d0ee7b538 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -38,5 +38,12 @@ var ( Big1 = big.NewInt(1) Big2 = big.NewInt(1) Big0 = big.NewInt(0) + Big32 = big.NewInt(32) Big256 = big.NewInt(0xff) ) + +func CreateAddress(b []byte, nonce *big.Int) []byte { + addrBytes := append(b, nonce.Bytes()...) + + return Sha3Bin(addrBytes)[12:] +} diff --git a/ethutil/config.go b/ethutil/config.go index 54b066fb9..86c0a855d 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -48,7 +48,7 @@ func ReadConfig(base string) *config { } } - Config = &config{ExecPath: path, Debug: true, Ver: "0.3.1"} + Config = &config{ExecPath: path, Debug: true, Ver: "0.5"} Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug) Config.SetClientString("/Ethereum(G)") } From 338b6980915c990c6e6287a7249ddd98e6be20eb Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 27 Apr 2014 17:15:44 +0200 Subject: [PATCH 072/100] Refactoring and added documentation comments --- ethutil/{parsing.go => asm.go} | 55 ++++++++---------------- ethutil/{parsing_test.go => asm_test.go} | 0 ethutil/big.go | 31 ++++++++----- ethutil/bytes.go | 13 +++++- ethutil/common.go | 12 ++++-- ethutil/config.go | 9 +++- ethutil/rlp.go | 10 ----- 7 files changed, 68 insertions(+), 62 deletions(-) rename ethutil/{parsing.go => asm.go} (80%) rename ethutil/{parsing_test.go => asm_test.go} (100%) diff --git a/ethutil/parsing.go b/ethutil/asm.go similarity index 80% rename from ethutil/parsing.go rename to ethutil/asm.go index 514fd92cd..a547d3ac1 100644 --- a/ethutil/parsing.go +++ b/ethutil/asm.go @@ -79,6 +79,10 @@ var OpCodes = map[string]byte{ "SUICIDE": 0x7f, } +// Is op code +// +// Check whether the given string matches anything in +// the OpCode list func IsOpCode(s string) bool { for key, _ := range OpCodes { if key == s { @@ -88,6 +92,10 @@ func IsOpCode(s string) bool { return false } +// Compile instruction +// +// Attempts to compile and parse the given instruction in "s" +// and returns the byte sequence func CompileInstr(s interface{}) ([]byte, error) { switch s.(type) { case string: @@ -119,8 +127,9 @@ func CompileInstr(s interface{}) ([]byte, error) { return nil, nil } -// Script compilation functions -// Compiles strings to machine code +// Assemble +// +// Assembles the given instructions and returns EVM byte code func Assemble(instructions ...interface{}) (script []byte) { //script = make([]string, len(instructions)) @@ -134,38 +143,22 @@ func Assemble(instructions ...interface{}) (script []byte) { return } -/* -Prepocessing function that takes init and main apart: -init() { - // something -} - -main() { - // main something -} +// Pre process script +// +// Take data apart and attempt to find the "init" section and +// "main" section. `main { } init { }` func PreProcess(data string) (mainInput, initInput string) { - reg := "\\(\\)\\s*{([\\d\\w\\W\\n\\s]+?)}" - mainReg := regexp.MustCompile("main" + reg) - initReg := regexp.MustCompile("init" + reg) - - main := mainReg.FindStringSubmatch(data) - if len(main) > 0 { - mainInput = main[1] - } else { + mainInput = getCodeSectionFor("main", data) + if mainInput == "" { mainInput = data } - - init := initReg.FindStringSubmatch(data) - if len(init) > 0 { - initInput = init[1] - } + initInput = getCodeSectionFor("init", data) return } -*/ // Very, very dumb parser. Heed no attention :-) -func FindFor(blockMatcher, input string) string { +func getCodeSectionFor(blockMatcher, input string) string { curCount := -1 length := len(blockMatcher) matchfst := rune(blockMatcher[0]) @@ -198,13 +191,3 @@ func FindFor(blockMatcher, input string) string { return currStr } - -func PreProcess(data string) (mainInput, initInput string) { - mainInput = FindFor("main", data) - if mainInput == "" { - mainInput = data - } - initInput = FindFor("init", data) - - return -} diff --git a/ethutil/parsing_test.go b/ethutil/asm_test.go similarity index 100% rename from ethutil/parsing_test.go rename to ethutil/asm_test.go diff --git a/ethutil/big.go b/ethutil/big.go index c0488a71f..891d476ad 100644 --- a/ethutil/big.go +++ b/ethutil/big.go @@ -12,7 +12,9 @@ var BigTrue *big.Int = big.NewInt(1) // False var BigFalse *big.Int = big.NewInt(0) -// Returns the power of two integers +// Big pow +// +// Returns the power of two big integers func BigPow(a, b int) *big.Int { c := new(big.Int) c.Exp(big.NewInt(int64(a)), big.NewInt(int64(b)), big.NewInt(0)) @@ -20,7 +22,9 @@ func BigPow(a, b int) *big.Int { return c } -// Like big.NewInt(uint64); this takes a string instead. +// Big +// +// Shortcut for new(big.Int).SetString(..., 0) func Big(num string) *big.Int { n := new(big.Int) n.SetString(num, 0) @@ -28,7 +32,9 @@ func Big(num string) *big.Int { return n } -// Like big.NewInt(uint64); this takes a byte buffer instead. +// BigD +// +// Shortcut for new(big.Int).SetBytes(...) func BigD(data []byte) *big.Int { n := new(big.Int) n.SetBytes(data) @@ -36,21 +42,26 @@ func BigD(data []byte) *big.Int { return n } +// Big to bytes +// +// Returns the bytes of a big integer with the size specified by **base** +// Attempts to pad the byte array with zeros. func BigToBytes(num *big.Int, base int) []byte { ret := make([]byte, base/8) return append(ret[:len(ret)-len(num.Bytes())], num.Bytes()...) } -// Functions like the build in "copy" function -// but works on big integers -func BigCopy(src *big.Int) (ret *big.Int) { - ret = new(big.Int) - ret.Add(ret, src) - - return +// Big copy +// +// Creates a copy of the given big integer +func BigCopy(src *big.Int) *big.Int { + return new(big.Int).Set(src) } +// Big max +// +// Returns the maximum size big integer func BigMax(x, y *big.Int) *big.Int { if x.Cmp(y) <= 0 { return x diff --git a/ethutil/bytes.go b/ethutil/bytes.go index 40903a5f1..957fa254a 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -6,6 +6,9 @@ import ( "fmt" ) +// Number to bytes +// +// Returns the number in bytes with the specified base func NumberToBytes(num interface{}, bits int) []byte { buf := new(bytes.Buffer) err := binary.Write(buf, binary.BigEndian, num) @@ -16,6 +19,9 @@ func NumberToBytes(num interface{}, bits int) []byte { return buf.Bytes()[buf.Len()-(bits/8):] } +// Bytes to number +// +// Attempts to cast a byte slice to a unsigned integer func BytesToNumber(b []byte) uint64 { var number uint64 @@ -32,7 +38,9 @@ func BytesToNumber(b []byte) uint64 { return number } -// Read variable integer in big endian +// Read variable int +// +// Read a variable length number in big endian byte order func ReadVarint(reader *bytes.Reader) (ret uint64) { if reader.Len() == 8 { var num uint64 @@ -55,6 +63,9 @@ func ReadVarint(reader *bytes.Reader) (ret uint64) { return ret } +// Binary length +// +// Returns the true binary length of the given number func BinaryLength(num int) int { if num == 0 { return 0 diff --git a/ethutil/common.go b/ethutil/common.go index d0ee7b538..983ea5d1b 100644 --- a/ethutil/common.go +++ b/ethutil/common.go @@ -5,16 +5,20 @@ import ( "math/big" ) +// The different number of units var ( Ether = BigPow(10, 18) Finney = BigPow(10, 15) Szabo = BigPow(10, 12) - Vito = BigPow(10, 9) + Vita = BigPow(10, 9) Turing = BigPow(10, 6) Eins = BigPow(10, 3) Wei = big.NewInt(1) ) +// Currency to string +// +// Returns a string representing a human readable format func CurrencyToString(num *big.Int) string { switch { case num.Cmp(Ether) >= 0: @@ -23,8 +27,8 @@ func CurrencyToString(num *big.Int) string { return fmt.Sprintf("%v Finney", new(big.Int).Div(num, Finney)) case num.Cmp(Szabo) >= 0: return fmt.Sprintf("%v Szabo", new(big.Int).Div(num, Szabo)) - case num.Cmp(Vito) >= 0: - return fmt.Sprintf("%v Vito", new(big.Int).Div(num, Vito)) + case num.Cmp(Vita) >= 0: + return fmt.Sprintf("%v Vita", new(big.Int).Div(num, Vita)) case num.Cmp(Turing) >= 0: return fmt.Sprintf("%v Turing", new(big.Int).Div(num, Turing)) case num.Cmp(Eins) >= 0: @@ -34,6 +38,7 @@ func CurrencyToString(num *big.Int) string { return fmt.Sprintf("%v Wei", num) } +// Common big integers often used var ( Big1 = big.NewInt(1) Big2 = big.NewInt(1) @@ -42,6 +47,7 @@ var ( Big256 = big.NewInt(0xff) ) +// Creates an ethereum address given the bytes and the nonce func CreateAddress(b []byte, nonce *big.Int) []byte { addrBytes := append(b, nonce.Bytes()...) diff --git a/ethutil/config.go b/ethutil/config.go index 86c0a855d..323773ba7 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -9,6 +9,7 @@ import ( "runtime" ) +// Log types available type LogType byte const ( @@ -16,7 +17,7 @@ const ( LogTypeFile = 2 ) -// Config struct isn't exposed +// Config struct type config struct { Db Database @@ -31,7 +32,9 @@ type config struct { var Config *config -// Read config doesn't read anything yet. +// Read config +// +// Initialize the global Config variable with default settings func ReadConfig(base string) *config { if Config == nil { usr, _ := user.Current() @@ -56,6 +59,8 @@ func ReadConfig(base string) *config { return Config } +// Set client string +// func (c *config) SetClientString(str string) { Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS) } diff --git a/ethutil/rlp.go b/ethutil/rlp.go index d95ace425..69f80a0a6 100644 --- a/ethutil/rlp.go +++ b/ethutil/rlp.go @@ -26,16 +26,6 @@ func (coder *RlpEncoder) EncodeData(rlpData interface{}) []byte { return Encode(rlpData) } -/* -func FromBin(data []byte) uint64 { - if len(data) == 0 { - return 0 - } - - return FromBin(data[:len(data)-1])*256 + uint64(data[len(data)-1]) -} -*/ - const ( RlpEmptyList = 0x80 RlpEmptyStr = 0x40 From bf850974f3e1794a3e2aa4ff33b527297cc50f91 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 27 Apr 2014 18:00:38 +0200 Subject: [PATCH 073/100] Using mutan assembler stage --- ethchain/vm_test.go | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 75a4f5afb..35a7b2e3f 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -82,7 +82,7 @@ func TestRun4(t *testing.T) { db, _ := ethdb.NewMemDatabase() state := NewState(ethutil.NewTrie(db, "")) - asm, err := mutan.Compile(strings.NewReader(` + script, err := mutan.Compile(strings.NewReader(` int32 a = 10 int32 b = 20 if a > b { @@ -90,14 +90,13 @@ func TestRun4(t *testing.T) { } Exit() `), false) - script := ethutil.Assemble(asm...) tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), script, nil) addr := tx.Hash()[12:] contract := MakeContract(tx, state) state.UpdateStateObject(contract) fmt.Printf("%x\n", addr) - asm, err = mutan.Compile(strings.NewReader(` + callerScript, err := mutan.Compile(strings.NewReader(` // Check if there's any cash in the initial store if store[1000] == 0 { store[1000] = 10^20 @@ -129,10 +128,7 @@ func TestRun4(t *testing.T) { if err != nil { fmt.Println(err) } - asm = append(asm, "LOG") - fmt.Println(asm) - callerScript := ethutil.Assemble(asm...) callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript, nil) // Contract addr as test address @@ -162,17 +158,3 @@ func TestRun4(t *testing.T) { } fmt.Println("account.Amount =", account.Amount) } - -func TestRun5(t *testing.T) { - ethutil.ReadConfig("") - - asm, _ := mutan.Compile(strings.NewReader(` - int32 a = 10 - int32 b = 20 - if a > b { - int32 c = this.caller() - } - exit() - `), false) - ethutil.Assemble(asm...) -} From 21f8806eed4c926ea31144c0e061ca8e0bbe35f8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 27 Apr 2014 18:01:37 +0200 Subject: [PATCH 074/100] Moved assembler stage to the mutan compiler --- ethutil/asm.go | 193 -------------------------------------------- ethutil/asm_test.go | 31 ------- 2 files changed, 224 deletions(-) delete mode 100644 ethutil/asm.go delete mode 100644 ethutil/asm_test.go diff --git a/ethutil/asm.go b/ethutil/asm.go deleted file mode 100644 index a547d3ac1..000000000 --- a/ethutil/asm.go +++ /dev/null @@ -1,193 +0,0 @@ -package ethutil - -import ( - _ "fmt" - "math/big" - _ "regexp" -) - -// Op codes -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 - "PUSH": 0x50, - - "PUSH20": 0x80, - - "POP": 0x51, - "DUP": 0x52, - "SWAP": 0x53, - "MLOAD": 0x54, - "MSTORE": 0x55, - "MSTORE8": 0x56, - "SLOAD": 0x57, - "SSTORE": 0x58, - "JUMP": 0x59, - "JUMPI": 0x5a, - "PC": 0x5b, - "MSIZE": 0x5c, - - // 0x60 range - closures - "CREATE": 0x60, - "CALL": 0x61, - "RETURN": 0x62, - - // 0x70 range - other - "LOG": 0x70, - "SUICIDE": 0x7f, -} - -// Is op code -// -// Check whether the given string matches anything in -// the OpCode list -func IsOpCode(s string) bool { - for key, _ := range OpCodes { - if key == s { - return true - } - } - return false -} - -// Compile instruction -// -// Attempts to compile and parse the given instruction in "s" -// and returns the byte sequence -func CompileInstr(s interface{}) ([]byte, error) { - switch s.(type) { - case string: - str := s.(string) - isOp := IsOpCode(str) - if isOp { - return []byte{OpCodes[str]}, nil - } - - num := new(big.Int) - _, success := num.SetString(str, 0) - // Assume regular bytes during compilation - if !success { - num.SetBytes([]byte(str)) - } else { - // tmp fix for 32 bytes - n := BigToBytes(num, 256) - return n, nil - } - - return num.Bytes(), nil - case int: - num := BigToBytes(big.NewInt(int64(s.(int))), 256) - return num, nil - case []byte: - return BigD(s.([]byte)).Bytes(), nil - } - - return nil, nil -} - -// Assemble -// -// Assembles the given instructions and returns EVM byte code -func Assemble(instructions ...interface{}) (script []byte) { - //script = make([]string, len(instructions)) - - for _, val := range instructions { - instr, _ := CompileInstr(val) - - //script[i] = string(instr) - script = append(script, instr...) - } - - return -} - -// Pre process script -// -// Take data apart and attempt to find the "init" section and -// "main" section. `main { } init { }` -func PreProcess(data string) (mainInput, initInput string) { - mainInput = getCodeSectionFor("main", data) - if mainInput == "" { - mainInput = data - } - initInput = getCodeSectionFor("init", data) - - return -} - -// Very, very dumb parser. Heed no attention :-) -func getCodeSectionFor(blockMatcher, input string) string { - curCount := -1 - length := len(blockMatcher) - matchfst := rune(blockMatcher[0]) - var currStr string - - for i, run := range input { - // Find init - if curCount == -1 && run == matchfst && input[i:i+length] == blockMatcher { - curCount = 0 - } else if curCount > -1 { - if run == '{' { - curCount++ - if curCount == 1 { - continue - } - } else if run == '}' { - curCount-- - if curCount == 0 { - // we are done - curCount = -1 - break - } - } - - if curCount > 0 { - currStr += string(run) - } - } - } - - return currStr -} diff --git a/ethutil/asm_test.go b/ethutil/asm_test.go deleted file mode 100644 index a9ad347dd..000000000 --- a/ethutil/asm_test.go +++ /dev/null @@ -1,31 +0,0 @@ -package ethutil - -import ( - "fmt" - "testing" -) - -func TestPreProcess(t *testing.T) { - main, init := PreProcess(` - init { - // init - if a > b { - if { - } - } - } - - main { - // main - if a > b { - if c > d { - } - } - } - `) - - fmt.Println("main") - fmt.Println(main) - fmt.Println("init") - fmt.Println(init) -} From 5516efdfa0494e028fc3649e4a38da81c56ed598 Mon Sep 17 00:00:00 2001 From: obscuren Date: Sun, 27 Apr 2014 18:05:30 +0200 Subject: [PATCH 075/100] Removed old code --- ethchain/asm.go | 68 ------------------------------------------------- 1 file changed, 68 deletions(-) diff --git a/ethchain/asm.go b/ethchain/asm.go index a6c85cb60..3194549ba 100644 --- a/ethchain/asm.go +++ b/ethchain/asm.go @@ -4,55 +4,8 @@ import ( "fmt" "github.com/ethereum/eth-go/ethutil" "math/big" - "regexp" ) -func CompileInstr(s interface{}) ([]byte, error) { - switch s.(type) { - case string: - str := s.(string) - isOp := IsOpCode(str) - if isOp { - return []byte{OpCodes[str]}, nil - } - - num := new(big.Int) - _, success := num.SetString(str, 0) - // Assume regular bytes during compilation - if !success { - num.SetBytes([]byte(str)) - } else { - // tmp fix for 32 bytes - n := ethutil.BigToBytes(num, 256) - return n, nil - } - - return num.Bytes(), nil - case int: - num := ethutil.BigToBytes(big.NewInt(int64(s.(int))), 256) - return num, nil - case []byte: - return ethutil.BigD(s.([]byte)).Bytes(), nil - } - - return nil, nil -} - -// Script compilation functions -// Compiles strings to machine code -func Assemble(instructions ...interface{}) (script []byte) { - //script = make([]string, len(instructions)) - - for _, val := range instructions { - instr, _ := CompileInstr(val) - - //script[i] = string(instr) - script = append(script, instr...) - } - - return -} - func Disassemble(script []byte) (asm []string) { pc := new(big.Int) for { @@ -104,24 +57,3 @@ func Disassemble(script []byte) (asm []string) { return } - -func PreProcess(data string) (mainInput, initInput string) { - // Regexp for parsing anything between brackets - reg := "\\(\\)\\s*{([\\d\\w\\W\\n\\s]+?)}" - mainReg := regexp.MustCompile("main" + reg) - initReg := regexp.MustCompile("init" + reg) - - main := mainReg.FindStringSubmatch(data) - if len(main) > 0 { - mainInput = main[1] - } else { - mainInput = data - } - - init := initReg.FindStringSubmatch(data) - if len(init) > 0 { - initInput = init[1] - } - - return -} From 38d6b67b5cfbfb63620a244ea01b5b534917128f Mon Sep 17 00:00:00 2001 From: obscuren Date: Tue, 29 Apr 2014 12:36:27 +0200 Subject: [PATCH 076/100] Fixed state problem --- ethchain/block.go | 5 +---- ethchain/block_chain.go | 3 ++- ethchain/state_manager.go | 13 +++++++------ ethminer/miner.go | 12 +++++++----- ethutil/bytes.go | 10 ++++++++++ ethutil/trie.go | 19 +++++++++++++++++-- peer.go | 6 ++++-- 7 files changed, 48 insertions(+), 20 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index d95ebf4b5..aac50ccb1 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -80,9 +80,6 @@ func CreateBlock(root interface{}, extra string, txes []*Transaction) *Block { - // Copy over the bytes - copiedRoot := ethutil.NewValue(root).Bytes() - block := &Block{ // Slice of transactions to include in this block transactions: txes, @@ -98,7 +95,7 @@ func CreateBlock(root interface{}, block.SetTransactions(txes) block.SetUncles([]*Block{}) - block.state = NewState(ethutil.NewTrie(ethutil.Config.Db, copiedRoot)) + block.state = NewState(ethutil.NewTrie(ethutil.Config.Db, root)) for _, tx := range txes { block.MakeContract(tx) diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index 08886c9cd..2be4cd92b 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -179,7 +179,8 @@ func (bc *BlockChain) ResetTillBlockHash(hash []byte) error { bc.LastBlockNumber = info.Number } - bc.Ethereum.StateManager().PrepareDefault(returnTo) + // XXX Why are we resetting? This is the block chain, it has nothing to do with states + //bc.Ethereum.StateManager().PrepareDefault(returnTo) err := ethutil.Config.Db.Delete(lastBlock.Hash()) if err != nil { diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 628ab6a27..70d4155c3 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -158,17 +158,18 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { // Processing a blocks may never happen simultaneously sm.mutex.Lock() defer sm.mutex.Unlock() + hash := block.Hash() + + if sm.bc.HasBlock(hash) { + fmt.Println("[STATE] We already have this block, ignoring") + return nil + } + // Defer the Undo on the Trie. If the block processing happened // we don't want to undo but since undo only happens on dirty // nodes this won't happen because Commit would have been called // before that. defer sm.bc.CurrentBlock.Undo() - hash := block.Hash() - - if sm.bc.HasBlock(hash) { - fmt.Println("[SM] We already have this block, ignoring") - return nil - } // Check if we have the parent hash, if it isn't known we discard it // Reasons might be catching up or simply an invalid block diff --git a/ethminer/miner.go b/ethminer/miner.go index 791e8e402..c93267161 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -2,6 +2,7 @@ package ethminer import ( "bytes" + "fmt" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" @@ -61,10 +62,10 @@ func (miner *Miner) listener() { select { case chanMessage := <-miner.reactChan: if block, ok := chanMessage.Resource.(*ethchain.Block); ok { - //log.Println("[MINER] Got new block via Reactor") + log.Println("[MINER] Got new block via Reactor") if bytes.Compare(miner.ethereum.BlockChain().CurrentBlock.Hash(), block.Hash()) == 0 { // TODO: Perhaps continue mining to get some uncle rewards - //log.Println("[MINER] New top block found resetting state") + log.Println("[MINER] New top block found resetting state") // Filter out which Transactions we have that were not in this block var newtxs []*ethchain.Transaction @@ -86,7 +87,7 @@ func (miner *Miner) listener() { } else { if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 { - //log.Println("[MINER] Adding uncle block") + log.Println("[MINER] Adding uncle block") miner.uncles = append(miner.uncles, block) miner.ethereum.StateManager().Prepare(miner.block.State(), miner.block.State()) } @@ -133,8 +134,9 @@ func (miner *Miner) listener() { miner.ethereum.StateManager().PrepareDefault(miner.block) err := miner.ethereum.StateManager().ProcessBlock(miner.block, true) if err != nil { - log.Println("Error result from process block:", err) - miner.block.State().Reset() + log.Println(err) + miner.txs = []*ethchain.Transaction{} // Move this somewhere neat + miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) } else { /* diff --git a/ethutil/bytes.go b/ethutil/bytes.go index 957fa254a..500368017 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -73,3 +73,13 @@ func BinaryLength(num int) int { return 1 + BinaryLength(num>>8) } + +// Copy bytes +// +// Returns an exact copy of the provided bytes +func CopyBytes(b []byte) (copiedBytes []byte) { + copiedBytes = make([]byte, len(b)) + copy(copiedBytes, b) + + return +} diff --git a/ethutil/trie.go b/ethutil/trie.go index c67f750bc..4d088ccff 100644 --- a/ethutil/trie.go +++ b/ethutil/trie.go @@ -119,14 +119,29 @@ type Trie struct { cache *Cache } +func copyRoot(root interface{}) interface{} { + var prevRootCopy interface{} + if b, ok := root.([]byte); ok { + prevRootCopy = CopyBytes(b) + } else { + prevRootCopy = root + } + + return prevRootCopy +} + func NewTrie(db Database, Root interface{}) *Trie { - return &Trie{cache: NewCache(db), Root: Root, prevRoot: Root} + // Make absolute sure the root is copied + r := copyRoot(Root) + p := copyRoot(Root) + + return &Trie{cache: NewCache(db), Root: r, prevRoot: p} } // Save the cached value to the database. func (t *Trie) Sync() { t.cache.Commit() - t.prevRoot = t.Root + t.prevRoot = copyRoot(t.Root) } func (t *Trie) Undo() { diff --git a/peer.go b/peer.go index 0ecd13e60..28ccc324c 100644 --- a/peer.go +++ b/peer.go @@ -449,8 +449,10 @@ func (p *Peer) HandleInbound() { if parent != nil { ethutil.Config.Log.Infof("[PEER] Found conical block, returning chain from: %x ", parent.Hash()) chain := p.ethereum.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks) - ethutil.Config.Log.Infof("[PEER] Returning %d blocks: %x ", len(chain), parent.Hash()) - p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain)) + if len(chain) > 0 { + ethutil.Config.Log.Infof("[PEER] Returning %d blocks: %x ", len(chain), parent.Hash()) + p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain)) + } } else { ethutil.Config.Log.Infof("[PEER] Could not find a similar block") // If no blocks are found we send back a reply with msg not in chain From 21724f7ef960f0f2df0d2b0f3cccfd030a4aaee8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Apr 2014 14:43:32 +0200 Subject: [PATCH 077/100] Added manifest changes and changed closures --- ethchain/closure.go | 17 +++------- ethchain/state_manager.go | 67 ++++++++++++++++++++++++++++----------- ethchain/vm.go | 7 ++-- ethminer/miner.go | 1 - 4 files changed, 59 insertions(+), 33 deletions(-) diff --git a/ethchain/closure.go b/ethchain/closure.go index 57abaa91e..7e911ad99 100644 --- a/ethchain/closure.go +++ b/ethchain/closure.go @@ -7,13 +7,6 @@ import ( "math/big" ) -type Callee interface { -} - -type Reference interface { - Callee -} - type ClosureRef interface { ReturnGas(*big.Int, *big.Int, *State) Address() []byte @@ -24,8 +17,8 @@ type ClosureRef interface { // Basic inline closure object which implement the 'closure' interface type Closure struct { - callee ClosureRef - object ClosureRef + callee *StateObject + object *StateObject Script []byte State *State @@ -37,7 +30,7 @@ type Closure struct { } // Create a new closure for the given data items -func NewClosure(callee, object ClosureRef, script []byte, state *State, gas, price, val *big.Int) *Closure { +func NewClosure(callee, object *StateObject, script []byte, state *State, gas, price, val *big.Int) *Closure { c := &Closure{callee: callee, object: object, Script: script, State: state, Args: nil} // In most cases gas, price and value are pointers to transaction objects @@ -108,11 +101,11 @@ func (c *Closure) ReturnGas(gas, price *big.Int, state *State) { c.Gas.Add(c.Gas, gas) } -func (c *Closure) Object() ClosureRef { +func (c *Closure) Object() *StateObject { return c.object } -func (c *Closure) Callee() ClosureRef { +func (c *Closure) Callee() *StateObject { return c.callee } diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 70d4155c3..072fabc0e 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -51,9 +51,7 @@ type StateManager struct { // results compState *State - // It's generally know that a map is faster for small lookups than arrays - // we'll eventually have to make a decision if the map grows too large - watchedAddresses map[string]bool + manifest *Manifest } func NewStateManager(ethereum EthManager) *StateManager { @@ -64,7 +62,7 @@ func NewStateManager(ethereum EthManager) *StateManager { Ethereum: ethereum, stateObjectCache: NewStateObjectCache(), bc: ethereum.BlockChain(), - watchedAddresses: make(map[string]bool), + manifest: NewManifest(), } sm.procState = ethereum.BlockChain().CurrentBlock.State() return sm @@ -112,7 +110,6 @@ func (sm *StateManager) MakeContract(tx *Transaction) *StateObject { func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { // Process each transaction/contract for _, tx := range txs { - fmt.Printf("Processing Tx: %x\n", tx.Hash()) // If there's no recipient, it's a contract // Check if this is a contract creation traction and if so // create a contract of this tx. @@ -122,7 +119,6 @@ func (sm *StateManager) ApplyTransactions(block *Block, txs []*Transaction) { contract := sm.MakeContract(tx) if contract != nil { sm.EvalScript(contract.Init(), contract, tx, block) - fmt.Printf("state root of contract %x\n", contract.State().Root()) } else { ethutil.Config.Log.Infoln("[STATE] Unable to create contract") } @@ -214,6 +210,10 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { ethutil.Config.Log.Infof("[STATE] Added block #%d (%x)\n", block.BlockInfo().Number, block.Hash()) if dontReact == false { sm.Ethereum.Reactor().Post("newBlock", block) + + sm.notifyChanges() + + sm.manifest.Reset() } } else { fmt.Println("total diff failed") @@ -337,22 +337,53 @@ func (sm *StateManager) EvalScript(script []byte, object *StateObject, tx *Trans // Update the account (refunds) sm.procState.UpdateStateObject(account) - sm.Changed(account) + sm.manifest.AddObjectChange(account) + sm.procState.UpdateStateObject(object) - sm.Changed(object) + sm.manifest.AddObjectChange(object) } -// Watch a specific address -func (sm *StateManager) Watch(addr []byte) { - if !sm.watchedAddresses[string(addr)] { - sm.watchedAddresses[string(addr)] = true +func (sm *StateManager) notifyChanges() { + for addr, stateObject := range sm.manifest.objectChanges { + sm.Ethereum.Reactor().Post("object:"+addr, stateObject) + } + + for stateObjectAddr, mappedObjects := range sm.manifest.storageChanges { + for addr, value := range mappedObjects { + sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, value.String()) + } } } -// The following objects are used when changing a value and using the "watched" attribute -// to determine whether the reactor should be used to notify any subscribers on the address -func (sm *StateManager) Changed(stateObject *StateObject) { - if sm.watchedAddresses[string(stateObject.Address())] { - sm.Ethereum.Reactor().Post("addressChanged", stateObject) - } +type Manifest struct { + // XXX These will be handy in the future. Not important for now. + objectAddresses map[string]bool + storageAddresses map[string]map[string]bool + + objectChanges map[string]*StateObject + storageChanges map[string]map[string]*big.Int +} + +func NewManifest() *Manifest { + m := &Manifest{objectAddresses: make(map[string]bool), storageAddresses: make(map[string]map[string]bool)} + m.Reset() + + return m +} + +func (m *Manifest) Reset() { + m.objectChanges = make(map[string]*StateObject) + m.storageChanges = make(map[string]map[string]*big.Int) +} + +func (m *Manifest) AddObjectChange(stateObject *StateObject) { + m.objectChanges[string(stateObject.Address())] = stateObject +} + +func (m *Manifest) AddStorageChange(stateObject *StateObject, storageAddr []byte, storage *big.Int) { + if m.storageChanges[string(stateObject.Address())] == nil { + m.storageChanges[string(stateObject.Address())] = make(map[string]*big.Int) + } + + m.storageChanges[string(stateObject.Address())][string(storageAddr)] = storage } diff --git a/ethchain/vm.go b/ethchain/vm.go index b983e88ff..0a3690c41 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -411,6 +411,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro require(2) val, loc := stack.Popn() closure.SetMem(loc, ethutil.NewValue(val)) + + // Add the change to manifest + vm.stateManager.manifest.AddStorageChange(closure.Object(), loc.Bytes(), val) case oJUMP: require(1) pc = stack.Pop() @@ -492,7 +495,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } closure.Gas.Sub(closure.Gas, gas) // Create a new callable closure - closure := NewClosure(closure, contract, contract.script, vm.state, gas, closure.Price, value) + closure := NewClosure(closure.Object(), contract, contract.script, vm.state, gas, closure.Price, value) // Executer the closure and get the return value (if any) ret, err := closure.Call(vm, args, hook) if err != nil { @@ -502,7 +505,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } else { stack.Push(ethutil.BigTrue) // Notify of the changes - vm.stateManager.Changed(contract) + vm.stateManager.manifest.AddObjectChange(contract) } mem.Set(retOffset.Int64(), retSize.Int64(), ret) diff --git a/ethminer/miner.go b/ethminer/miner.go index c93267161..3796c873e 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -2,7 +2,6 @@ package ethminer import ( "bytes" - "fmt" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" From c3293641e7b49c7e2d85d2bd69b37bc74cb5b00d Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Apr 2014 17:13:32 +0200 Subject: [PATCH 078/100] Removed debug logging --- ethchain/error.go | 23 ++++++++++++++++++++++- ethchain/state_manager.go | 2 +- ethchain/transaction_pool.go | 10 +++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/ethchain/error.go b/ethchain/error.go index 0f1d061c0..8d37b0208 100644 --- a/ethchain/error.go +++ b/ethchain/error.go @@ -1,6 +1,8 @@ package ethchain -import "fmt" +import ( + "fmt" +) // Parent error. In case a parent is unknown this error will be thrown // by the block manager @@ -40,3 +42,22 @@ func IsValidationErr(err error) bool { return ok } + +type NonceErr struct { + Message string + Is, Exp uint64 +} + +func (err *NonceErr) Error() string { + return err.Message +} + +func NonceError(is, exp uint64) *NonceErr { + return &NonceErr{Message: fmt.Sprintf("Nonce err. Is %d, expected %d", is, exp), Is: is, Exp: exp} +} + +func IsNonceErr(err error) bool { + _, ok := err.(*NonceErr) + + return ok +} diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 072fabc0e..02d0345d7 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -157,7 +157,7 @@ func (sm *StateManager) ProcessBlock(block *Block, dontReact bool) error { hash := block.Hash() if sm.bc.HasBlock(hash) { - fmt.Println("[STATE] We already have this block, ignoring") + //fmt.Println("[STATE] We already have this block, ignoring") return nil } diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 91fad2635..fc807c580 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -98,7 +98,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract } }() // Get the sender - sender := block.state.GetAccount(tx.Sender()) + sender := block.state.GetStateObject(tx.Sender()) if sender.Nonce != tx.Nonce { return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transaction nonce is %d instead", sender.Nonce, tx.Nonce) @@ -112,7 +112,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract } // Get the receiver - receiver := block.state.GetAccount(tx.Recipient) + receiver := block.state.GetStateObject(tx.Recipient) sender.Nonce += 1 // Send Tx to self @@ -169,7 +169,6 @@ out: for { select { case tx := <-pool.queueChan: - log.Println("Received new Tx to queue") hash := tx.Hash() foundTx := FindTx(pool.pool, func(tx *Transaction, e *list.Element) bool { return bytes.Compare(tx.Hash(), hash) == 0 @@ -186,11 +185,8 @@ out: log.Println("Validating Tx failed", err) } } else { - log.Println("Transaction ok, adding") - // Call blocking version. At this point it - // doesn't matter since this is a goroutine + // Call blocking version. pool.addTransaction(tx) - log.Println("Added") // Notify the subscribers pool.Ethereum.Reactor().Post("newTx", tx) From d2ab322267e489f47b4b908d060411eb0554a029 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 30 Apr 2014 17:43:48 +0200 Subject: [PATCH 079/100] Removed debugging log --- ethchain/transaction_pool.go | 18 ++++++++++-------- peer.go | 16 ++++------------ 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index fc807c580..8fbe676f5 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -91,14 +91,16 @@ func (pool *TxPool) addTransaction(tx *Transaction) { // Process transaction validates the Tx and processes funds from the // sender to the recipient. func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract bool) (err error) { - defer func() { - if r := recover(); r != nil { - log.Println(r) - err = fmt.Errorf("%v", r) - } - }() + /* + defer func() { + if r := recover(); r != nil { + log.Println(r) + err = fmt.Errorf("%v", r) + } + }() + */ // Get the sender - sender := block.state.GetStateObject(tx.Sender()) + sender := block.state.GetAccount(tx.Sender()) if sender.Nonce != tx.Nonce { return fmt.Errorf("[TXPL] Invalid account nonce, state nonce is %d transaction nonce is %d instead", sender.Nonce, tx.Nonce) @@ -112,7 +114,7 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract } // Get the receiver - receiver := block.state.GetStateObject(tx.Recipient) + receiver := block.state.GetAccount(tx.Recipient) sender.Nonce += 1 // Send Tx to self diff --git a/peer.go b/peer.go index 28ccc324c..4f7005ac4 100644 --- a/peer.go +++ b/peer.go @@ -320,14 +320,11 @@ func (p *Peer) HandleInbound() { // We requested blocks and now we need to make sure we have a common ancestor somewhere in these blocks so we can find // common ground to start syncing from lastBlock = ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len() - 1)) - if p.ethereum.StateManager().BlockChain().HasBlock(lastBlock.Hash()) { - fmt.Println("[PEER] We found a common ancestor, let's continue.") - } else { - + if !p.ethereum.StateManager().BlockChain().HasBlock(lastBlock.Hash()) { // If we can't find a common ancenstor we need to request more blocks. // FIXME: At one point this won't scale anymore since we are not asking for an offset // we just keep increasing the amount of blocks. - fmt.Println("[PEER] No common ancestor found, requesting more blocks.") + //fmt.Println("[PEER] No common ancestor found, requesting more blocks.") p.blocksRequested = p.blocksRequested * 2 p.catchingUp = false p.SyncWithBlocks() @@ -336,17 +333,13 @@ func (p *Peer) HandleInbound() { for i := msg.Data.Len() - 1; i >= 0; i-- { block = ethchain.NewBlockFromRlpValue(msg.Data.Get(i)) // Do we have this block on our chain? If so we can continue - if p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) { - ethutil.Config.Log.Debugf("[PEER] Block found, checking next one.\n") - } else { + if !p.ethereum.StateManager().BlockChain().HasBlock(block.Hash()) { // We don't have this block, but we do have a block with the same prevHash, diversion time! if p.ethereum.StateManager().BlockChain().HasBlockWithPrevHash(block.PrevHash) { - ethutil.Config.Log.Infof("[PEER] Local and foreign chain have diverted after %x, finding best chain!\n", block.PrevHash) + //ethutil.Config.Log.Infof("[PEER] Local and foreign chain have diverted after %x, finding best chain!\n", block.PrevHash) if p.ethereum.StateManager().BlockChain().FindCanonicalChainFromMsg(msg, block.PrevHash) { return } - } else { - ethutil.Config.Log.Debugf("[PEER] Both local and foreign chain have same parent. Continue normally\n") } } } @@ -644,7 +637,6 @@ func (p *Peer) SyncWithBlocks() { for _, block := range blocks { hashes = append(hashes, block.Hash()) } - fmt.Printf("Requesting hashes from network: %x", hashes) msgInfo := append(hashes, uint64(50)) From e6a68f0c3ab4987fa5e0e35cac765d40ff305aea Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 1 May 2014 22:13:59 +0200 Subject: [PATCH 080/100] Removed debug log --- ethchain/state_object.go | 1 - ethchain/transaction_pool.go | 14 ++++------ peer.go | 54 ++++++++++++++++++------------------ 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 8e921795d..4ec91d2e0 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -80,7 +80,6 @@ func (c *StateObject) SetAddr(addr []byte, value interface{}) { func (c *StateObject) SetMem(num *big.Int, val *ethutil.Value) { addr := ethutil.BigToBytes(num, 256) c.SetAddr(addr, val) - //c.state.trie.Update(string(addr), string(val.Encode())) } func (c *StateObject) GetMem(num *big.Int) *ethutil.Value { diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index 8fbe676f5..72836d6cb 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -91,14 +91,12 @@ func (pool *TxPool) addTransaction(tx *Transaction) { // Process transaction validates the Tx and processes funds from the // sender to the recipient. func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract bool) (err error) { - /* - defer func() { - if r := recover(); r != nil { - log.Println(r) - err = fmt.Errorf("%v", r) - } - }() - */ + defer func() { + if r := recover(); r != nil { + log.Println(r) + err = fmt.Errorf("%v", r) + } + }() // Get the sender sender := block.state.GetAccount(tx.Sender()) diff --git a/peer.go b/peer.go index 4f7005ac4..80ddc5142 100644 --- a/peer.go +++ b/peer.go @@ -440,14 +440,14 @@ func (p *Peer) HandleInbound() { // If a parent is found send back a reply if parent != nil { - ethutil.Config.Log.Infof("[PEER] Found conical block, returning chain from: %x ", parent.Hash()) + ethutil.Config.Log.Debugf("[PEER] Found conical block, returning chain from: %x ", parent.Hash()) chain := p.ethereum.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks) if len(chain) > 0 { - ethutil.Config.Log.Infof("[PEER] Returning %d blocks: %x ", len(chain), parent.Hash()) + ethutil.Config.Log.Debugf("[PEER] Returning %d blocks: %x ", len(chain), parent.Hash()) p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, chain)) } } else { - ethutil.Config.Log.Infof("[PEER] Could not find a similar block") + ethutil.Config.Log.Debugf("[PEER] Could not find a similar block") // If no blocks are found we send back a reply with msg not in chain // and the last hash from get chain lastHash := msg.Data.Get(l - 1) @@ -455,7 +455,7 @@ func (p *Peer) HandleInbound() { p.QueueMessage(ethwire.NewMessage(ethwire.MsgNotInChainTy, []interface{}{lastHash.Raw()})) } case ethwire.MsgNotInChainTy: - ethutil.Config.Log.Infof("Not in chain %x\n", msg.Data) + ethutil.Config.Log.Debugf("Not in chain %x\n", msg.Data) // TODO case ethwire.MsgGetTxsTy: // Get the current transactions of the pool @@ -478,29 +478,6 @@ func (p *Peer) HandleInbound() { p.Stop() } -func packAddr(address, port string) ([]interface{}, uint16) { - addr := strings.Split(address, ".") - a, _ := strconv.Atoi(addr[0]) - b, _ := strconv.Atoi(addr[1]) - c, _ := strconv.Atoi(addr[2]) - d, _ := strconv.Atoi(addr[3]) - host := []interface{}{int32(a), int32(b), int32(c), int32(d)} - prt, _ := strconv.Atoi(port) - - return host, uint16(prt) -} - -func unpackAddr(value *ethutil.Value, p uint64) string { - a := strconv.Itoa(int(value.Get(0).Uint())) - b := strconv.Itoa(int(value.Get(1).Uint())) - c := strconv.Itoa(int(value.Get(2).Uint())) - d := strconv.Itoa(int(value.Get(3).Uint())) - host := strings.Join([]string{a, b, c, d}, ".") - port := strconv.Itoa(int(p)) - - return net.JoinHostPort(host, port) -} - func (p *Peer) Start() { peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String()) servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String()) @@ -662,3 +639,26 @@ func (p *Peer) CatchupWithPeer(blockHash []byte) { func (p *Peer) RlpData() []interface{} { return []interface{}{p.host, p.port, p.pubkey} } + +func packAddr(address, port string) ([]interface{}, uint16) { + addr := strings.Split(address, ".") + a, _ := strconv.Atoi(addr[0]) + b, _ := strconv.Atoi(addr[1]) + c, _ := strconv.Atoi(addr[2]) + d, _ := strconv.Atoi(addr[3]) + host := []interface{}{int32(a), int32(b), int32(c), int32(d)} + prt, _ := strconv.Atoi(port) + + return host, uint16(prt) +} + +func unpackAddr(value *ethutil.Value, p uint64) string { + a := strconv.Itoa(int(value.Get(0).Uint())) + b := strconv.Itoa(int(value.Get(1).Uint())) + c := strconv.Itoa(int(value.Get(2).Uint())) + d := strconv.Itoa(int(value.Get(3).Uint())) + host := strings.Join([]string{a, b, c, d}, ".") + port := strconv.Itoa(int(p)) + + return net.JoinHostPort(host, port) +} From 91aa189ef31adff7f4a084cae770aaa7c01f11e5 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 1 May 2014 22:14:20 +0200 Subject: [PATCH 081/100] Fixed Upnp bug --- ethereum.go | 2 +- natupnp.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ethereum.go b/ethereum.go index c906a6954..b9174eaf8 100644 --- a/ethereum.go +++ b/ethereum.go @@ -350,7 +350,7 @@ func (s *Ethereum) WaitForShutdown() { func (s *Ethereum) upnpUpdateThread() { // Go off immediately to prevent code duplication, thereafter we renew // lease every 15 minutes. - timer := time.NewTimer(0 * time.Second) + timer := time.NewTimer(5 * time.Minute) lport, _ := strconv.ParseInt(s.Port, 10, 16) first := true out: diff --git a/natupnp.go b/natupnp.go index e4072d0dd..c7f9eeb62 100644 --- a/natupnp.go +++ b/natupnp.go @@ -246,6 +246,10 @@ func soapRequest(url, function, message string) (r *http.Response, err error) { //fmt.Println(fullMessage) r, err = http.DefaultClient.Do(req) + if err != nil { + return + } + if r.Body != nil { defer r.Body.Close() } From 17674fb888d3dc2de081f1c781a227b61c961189 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 1 May 2014 22:14:34 +0200 Subject: [PATCH 082/100] Added suicide back in --- ethchain/vm.go | 42 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/ethchain/vm.go b/ethchain/vm.go index 0a3690c41..3a3b3447a 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -73,10 +73,7 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro } }() - // If the amount of gas supplied is less equal to 0 - if closure.Gas.Cmp(big.NewInt(0)) <= 0 { - // TODO Do something - } + ethutil.Config.Log.Debugf("[VM] Running closure %x\n", closure.object.Address()) // Memory for the current closure mem := &Memory{} @@ -107,9 +104,11 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro val := closure.Get(pc) // Get the opcode (it must be an opcode!) op := OpCode(val.Uint()) - if ethutil.Config.Debug { - ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) - } + /* + if ethutil.Config.Debug { + ethutil.Config.Log.Debugf("%-3d %-4s", pc, op.String()) + } + */ gas := new(big.Int) useGas := func(amount *big.Int) { @@ -163,9 +162,6 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro case oLOG: stack.Print() mem.Print() - case oSTOP: // Stop the closure - return closure.Return(nil), nil - // 0x20 range case oADD: require(2) @@ -520,22 +516,18 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro return closure.Return(ret), nil case oSUICIDE: - /* - recAddr := stack.Pop().Bytes() - // Purge all memory - deletedMemory := contract.state.Purge() - // Add refunds to the pop'ed address - refund := new(big.Int).Mul(StoreFee, big.NewInt(int64(deletedMemory))) - account := state.GetAccount(recAddr) - account.Amount.Add(account.Amount, refund) - // Update the refunding address - state.UpdateAccount(recAddr, account) - // Delete the contract - state.trie.Update(string(addr), "") + require(1) - ethutil.Config.Log.Debugf("(%d) => %x\n", deletedMemory, recAddr) - break out - */ + receiver := vm.state.GetAccount(stack.Pop().Bytes()) + receiver.AddAmount(closure.object.Amount) + + vm.stateManager.manifest.AddObjectChange(receiver) + + closure.object.state.Purge() + + fallthrough + case oSTOP: // Stop the closure + return closure.Return(nil), nil default: ethutil.Config.Log.Debugf("Invalid opcode %x\n", op) From 70c8656640a861d93ac40181c6c0bdd8faef856b Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 2 May 2014 12:11:55 +0200 Subject: [PATCH 083/100] Added a KeyPairFromSec function which creates a new keypair based on the given seckey --- ethchain/keypair.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ethchain/keypair.go b/ethchain/keypair.go index a5af791d0..0f23bacdf 100644 --- a/ethchain/keypair.go +++ b/ethchain/keypair.go @@ -2,6 +2,7 @@ package ethchain import ( "github.com/ethereum/eth-go/ethutil" + "github.com/obscuren/secp256k1-go" "math/big" ) @@ -14,6 +15,15 @@ type KeyPair struct { state *State } +func NewKeyPairFromSec(seckey []byte) (*KeyPair, error) { + pubkey, err := secp256k1.GeneratePubKey(seckey) + if err != nil { + return nil, err + } + + return &KeyPair{PrivateKey: seckey, PublicKey: pubkey}, nil +} + func NewKeyPairFromValue(val *ethutil.Value) *KeyPair { keyPair := &KeyPair{PrivateKey: val.Get(0).Bytes(), PublicKey: val.Get(1).Bytes()} From ebdf339a614b9d03a0b0a0292d1ea24f854d6b3e Mon Sep 17 00:00:00 2001 From: Maran Date: Fri, 2 May 2014 13:35:25 +0200 Subject: [PATCH 084/100] Implemented RPC framework --- ethereum.go | 4 + etherpc/packages.go | 194 ++++++++++++++++++++++++++++++++++++++++++++ etherpc/server.go | 59 ++++++++++++++ 3 files changed, 257 insertions(+) create mode 100644 etherpc/packages.go create mode 100644 etherpc/server.go diff --git a/ethereum.go b/ethereum.go index c906a6954..df8e9ef7d 100644 --- a/ethereum.go +++ b/ethereum.go @@ -4,6 +4,7 @@ import ( "container/list" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/etherpc" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "io/ioutil" @@ -62,6 +63,8 @@ type Ethereum struct { MaxPeers int reactor *ethutil.ReactorEngine + + RpcServer *etherpc.JsonRpcServer } func New(caps Caps, usePnp bool) (*Ethereum, error) { @@ -338,6 +341,7 @@ func (s *Ethereum) Stop() { s.txPool.Stop() s.stateManager.Stop() + s.RpcServer.Stop() close(s.shutdownChan) } diff --git a/etherpc/packages.go b/etherpc/packages.go new file mode 100644 index 000000000..8bc78498f --- /dev/null +++ b/etherpc/packages.go @@ -0,0 +1,194 @@ +package etherpc + +import ( + "encoding/json" + "errors" + "math/big" +) + +type MainPackage struct{} + +type JsonArgs interface { + requirements() error +} + +type BlockResponse struct { + Name string + Id int +} +type GetBlockArgs struct { + BlockNumber int + Hash string +} + +type ErrorResponse struct { + Error bool `json:"error"` + ErrorText string `json:"errorText"` +} + +type JsonResponse interface { +} + +type SuccessRes struct { + Error bool `json:"error"` + Result JsonResponse `json:"result"` +} + +func NewSuccessRes(object JsonResponse) string { + e := SuccessRes{Error: false, Result: object} + res, err := json.Marshal(e) + if err != nil { + // This should never happen + panic("Creating json error response failed, help") + } + success := string(res) + return success +} + +func NewErrorResponse(msg string) error { + e := ErrorResponse{Error: true, ErrorText: msg} + res, err := json.Marshal(e) + if err != nil { + // This should never happen + panic("Creating json error response failed, help") + } + newErr := errors.New(string(res)) + return newErr +} + +func (b *GetBlockArgs) requirements() error { + if b.BlockNumber == 0 && b.Hash == "" { + return NewErrorResponse("GetBlock requires either a block 'number' or a block 'hash' as argument") + } + return nil +} + +func (p *MainPackage) GetBlock(args *GetBlockArgs, reply *BlockResponse) error { + err := args.requirements() + if err != nil { + return err + } + // Do something + + return nil +} + +type NewTxArgs struct { + Sec string + Recipient string + Value *big.Int + Gas *big.Int + GasPrice *big.Int + Init string + Body string +} +type TxResponse struct { + Hash string +} + +func (a *NewTxArgs) requirements() error { + if a.Recipient == "" { + return NewErrorResponse("Transact requires a 'recipient' address as argument") + } + if a.Value == nil { + return NewErrorResponse("Transact requires a 'value' as argument") + } + if a.Gas == nil { + return NewErrorResponse("Transact requires a 'gas' value as argument") + } + if a.GasPrice == nil { + return NewErrorResponse("Transact requires a 'gasprice' value as argument") + } + return nil +} + +func (a *NewTxArgs) requirementsContract() error { + if a.Value == nil { + return NewErrorResponse("Create requires a 'value' as argument") + } + if a.Gas == nil { + return NewErrorResponse("Create requires a 'gas' value as argument") + } + if a.GasPrice == nil { + return NewErrorResponse("Create requires a 'gasprice' value as argument") + } + if a.Init == "" { + return NewErrorResponse("Create requires a 'init' value as argument") + } + if a.Body == "" { + return NewErrorResponse("Create requires a 'body' value as argument") + } + return nil +} + +func (p *MainPackage) Transact(args *NewTxArgs, reply *TxResponse) error { + err := args.requirements() + if err != nil { + return err + } + return nil +} + +func (p *MainPackage) Create(args *NewTxArgs, reply *string) error { + err := args.requirementsContract() + if err != nil { + return err + } + return nil +} + +func (p *MainPackage) getKey(args interface{}, reply *string) error { + return nil +} + +type GetStorageArgs struct { + Address string + Key string +} + +func (a *GetStorageArgs) requirements() error { + if a.Address == "" { + return NewErrorResponse("GetStorageAt requires an 'address' value as argument") + } + if a.Key == "" { + return NewErrorResponse("GetStorageAt requires an 'key' value as argument") + } + return nil +} + +func (p *MainPackage) getStorageAt(args *GetStorageArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + return nil +} + +type GetBalanceArgs struct { + Address string +} + +func (a *GetBalanceArgs) requirements() error { + if a.Address == "" { + return NewErrorResponse("GetBalanceAt requires an 'address' value as argument") + } + return nil +} + +func (p *MainPackage) GetBalanceAt(args *GetBalanceArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + return nil +} + +type TestRes struct { + JsonResponse `json:"-"` + Answer int `json:"answer"` +} + +func (p *MainPackage) Test(args *GetBlockArgs, reply *string) error { + *reply = NewSuccessRes(TestRes{Answer: 15}) + return nil +} diff --git a/etherpc/server.go b/etherpc/server.go new file mode 100644 index 000000000..ba0c51006 --- /dev/null +++ b/etherpc/server.go @@ -0,0 +1,59 @@ +package etherpc + +import ( + "github.com/ethereum/eth-go/ethutil" + "net" + "net/rpc" + "net/rpc/jsonrpc" +) + +type JsonRpcServer struct { + quit chan bool + listener net.Listener +} + +func (s *JsonRpcServer) exitHandler() { +out: + for { + select { + case <-s.quit: + s.listener.Close() + break out + } + } + + ethutil.Config.Log.Infoln("[JSON] Shutdown JSON-RPC server") +} + +func (s *JsonRpcServer) Stop() { + close(s.quit) +} + +func (s *JsonRpcServer) Start() { + ethutil.Config.Log.Infoln("[JSON] Starting JSON-RPC server") + go s.exitHandler() + rpc.Register(new(MainPackage)) + rpc.HandleHTTP() + + for { + conn, err := s.listener.Accept() + if err != nil { + ethutil.Config.Log.Infoln("[JSON] Error starting JSON-RPC:", err) + continue + } + ethutil.Config.Log.Debugln("[JSON] Incoming request.") + go jsonrpc.ServeConn(conn) + } +} + +func NewJsonRpcServer() *JsonRpcServer { + l, err := net.Listen("tcp", ":30304") + if err != nil { + ethutil.Config.Log.Infoln("Error starting JSON-RPC") + } + + return &JsonRpcServer{ + listener: l, + quit: make(chan bool), + } +} From e798f221dd9d6aaffaa709d559f01a41447e4896 Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 2 May 2014 13:55:43 +0200 Subject: [PATCH 085/100] Added public interface --- ethpub/pub.go | 108 ++++++++++++++++++++++++++++++++++++++++++++++ ethpub/types.go | 91 ++++++++++++++++++++++++++++++++++++++ ethutil/script.go | 41 ++++++++++++++++++ 3 files changed, 240 insertions(+) create mode 100644 ethpub/pub.go create mode 100644 ethpub/types.go create mode 100644 ethutil/script.go diff --git a/ethpub/pub.go b/ethpub/pub.go new file mode 100644 index 000000000..4950f6eb5 --- /dev/null +++ b/ethpub/pub.go @@ -0,0 +1,108 @@ +package ethpub + +import ( + "github.com/ethereum/eth-go" + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethutil" +) + +type PEthereum struct { + stateManager *ethchain.StateManager + blockChain *ethchain.BlockChain + txPool *ethchain.TxPool +} + +func NewPEthereum(eth *eth.Ethereum) *PEthereum { + return &PEthereum{ + eth.StateManager(), + eth.BlockChain(), + eth.TxPool(), + } +} + +func (lib *PEthereum) GetBlock(hexHash string) *PBlock { + hash := ethutil.FromHex(hexHash) + + block := lib.blockChain.GetBlock(hash) + + return &PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())} +} + +func (lib *PEthereum) GetKey() *PKey { + keyPair, err := ethchain.NewKeyPairFromSec(ethutil.Config.Db.GetKeys()[0].PrivateKey) + if err != nil { + return nil + } + + return NewPKey(keyPair) +} + +func (lib *PEthereum) GetStateObject(address string) *PStateObject { + stateObject := lib.stateManager.ProcState().GetContract(ethutil.FromHex(address)) + if stateObject != nil { + return NewPStateObject(stateObject) + } + + // See GetStorage for explanation on "nil" + return NewPStateObject(nil) +} + +func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { + return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr, "") +} + +func (lib *PEthereum) Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyStr string) (string, error) { + return lib.createTx(key, "", valueStr, gasStr, gasPriceStr, initStr, bodyStr) +} + +func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, initStr, scriptStr string) (string, error) { + var hash []byte + var contractCreation bool + if len(recipient) == 0 { + contractCreation = true + } else { + hash = ethutil.FromHex(recipient) + } + + keyPair, err := ethchain.NewKeyPairFromSec([]byte(ethutil.FromHex(key))) + if err != nil { + return "", err + } + + value := ethutil.Big(valueStr) + gas := ethutil.Big(gasStr) + gasPrice := ethutil.Big(gasPriceStr) + var tx *ethchain.Transaction + // Compile and assemble the given data + if contractCreation { + initScript, err := ethutil.Compile(initStr) + if err != nil { + return "", err + } + mainScript, err := ethutil.Compile(scriptStr) + if err != nil { + return "", err + } + + tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript) + } else { + // Just in case it was submitted as a 0x prefixed string + if initStr[0:2] == "0x" { + initStr = initStr[2:len(initStr)] + } + tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, ethutil.FromHex(initStr)) + } + + acc := lib.stateManager.GetAddrState(keyPair.Address()) + tx.Nonce = acc.Nonce + tx.Sign(keyPair.PrivateKey) + lib.txPool.QueueTransaction(tx) + + if contractCreation { + ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:]) + } else { + ethutil.Config.Log.Infof("Tx hash %x", tx.Hash()) + } + + return ethutil.Hex(tx.Hash()), nil +} diff --git a/ethpub/types.go b/ethpub/types.go new file mode 100644 index 000000000..5e7e4613d --- /dev/null +++ b/ethpub/types.go @@ -0,0 +1,91 @@ +package ethpub + +import ( + "encoding/hex" + "github.com/ethereum/eth-go/ethchain" + "github.com/ethereum/eth-go/ethutil" +) + +// Block interface exposed to QML +type PBlock struct { + Number int + Hash string +} + +// Creates a new QML Block from a chain block +func NewPBlock(block *ethchain.Block) *PBlock { + info := block.BlockInfo() + hash := hex.EncodeToString(block.Hash()) + + return &PBlock{Number: int(info.Number), Hash: hash} +} + +type PTx struct { + Value, Hash, Address string + Contract bool +} + +func NewPTx(tx *ethchain.Transaction) *PTx { + hash := hex.EncodeToString(tx.Hash()) + sender := hex.EncodeToString(tx.Recipient) + isContract := len(tx.Data) > 0 + + return &PTx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender, Contract: isContract} +} + +type PKey struct { + Address string + PrivateKey string + PublicKey string +} + +func NewPKey(key *ethchain.KeyPair) *PKey { + return &PKey{ethutil.Hex(key.Address()), ethutil.Hex(key.PrivateKey), ethutil.Hex(key.PublicKey)} +} + +/* +type PKeyRing struct { + Keys []interface{} +} + +func NewPKeyRing(keys []interface{}) *PKeyRing { + return &PKeyRing{Keys: keys} +} +*/ + +type PStateObject struct { + object *ethchain.StateObject +} + +func NewPStateObject(object *ethchain.StateObject) *PStateObject { + return &PStateObject{object: object} +} + +func (c *PStateObject) GetStorage(address string) string { + // Because somehow, even if you return nil to QML it + // still has some magical object so we can't rely on + // undefined or null at the QML side + if c.object != nil { + val := c.object.GetMem(ethutil.Big("0x" + address)) + + return val.BigInt().String() + } + + return "" +} + +func (c *PStateObject) Value() string { + if c.object != nil { + return c.object.Amount.String() + } + + return "" +} + +func (c *PStateObject) Address() string { + if c.object != nil { + return ethutil.Hex(c.object.Address()) + } + + return "" +} diff --git a/ethutil/script.go b/ethutil/script.go new file mode 100644 index 000000000..620658025 --- /dev/null +++ b/ethutil/script.go @@ -0,0 +1,41 @@ +package ethutil + +import ( + "fmt" + "github.com/obscuren/mutan" + "strings" +) + +// General compile function +func Compile(script string) ([]byte, error) { + byteCode, errors := mutan.Compile(strings.NewReader(script), false) + if len(errors) > 0 { + var errs string + for _, er := range errors { + if er != nil { + errs += er.Error() + } + } + return nil, fmt.Errorf("%v", errs) + } + + return byteCode, nil +} + +func CompileScript(script string) ([]byte, []byte, error) { + // Preprocess + mainInput, initInput := mutan.PreProcess(script) + // Compile main script + mainScript, err := Compile(mainInput) + if err != nil { + return nil, nil, err + } + + // Compile init script + initScript, err := Compile(initInput) + if err != nil { + return nil, nil, err + } + + return mainScript, initScript, nil +} From 1f6df0cd522842095c5ca723d2e11fc6b97b8b6a Mon Sep 17 00:00:00 2001 From: obscuren Date: Fri, 2 May 2014 14:08:54 +0200 Subject: [PATCH 086/100] Added receipts for tx creation --- ethchain/transaction.go | 4 ++++ ethpub/pub.go | 16 ++++++++-------- ethpub/types.go | 16 ++++++++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/ethchain/transaction.go b/ethchain/transaction.go index 421f26c98..e93e610be 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -59,6 +59,10 @@ func (tx *Transaction) IsContract() bool { return tx.contractCreation } +func (tx *Transaction) CreationAddress() []byte { + return tx.Hash()[12:] +} + func (tx *Transaction) Signature(key []byte) []byte { hash := tx.Hash() diff --git a/ethpub/pub.go b/ethpub/pub.go index 4950f6eb5..c6f177124 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -47,15 +47,15 @@ func (lib *PEthereum) GetStateObject(address string) *PStateObject { return NewPStateObject(nil) } -func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (string, error) { +func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (*PReceipt, error) { return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr, "") } -func (lib *PEthereum) Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyStr string) (string, error) { +func (lib *PEthereum) Create(key, valueStr, gasStr, gasPriceStr, initStr, bodyStr string) (*PReceipt, error) { return lib.createTx(key, "", valueStr, gasStr, gasPriceStr, initStr, bodyStr) } -func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, initStr, scriptStr string) (string, error) { +func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, initStr, scriptStr string) (*PReceipt, error) { var hash []byte var contractCreation bool if len(recipient) == 0 { @@ -66,7 +66,7 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, in keyPair, err := ethchain.NewKeyPairFromSec([]byte(ethutil.FromHex(key))) if err != nil { - return "", err + return nil, err } value := ethutil.Big(valueStr) @@ -77,11 +77,11 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, in if contractCreation { initScript, err := ethutil.Compile(initStr) if err != nil { - return "", err + return nil, err } mainScript, err := ethutil.Compile(scriptStr) if err != nil { - return "", err + return nil, err } tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript) @@ -99,10 +99,10 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, in lib.txPool.QueueTransaction(tx) if contractCreation { - ethutil.Config.Log.Infof("Contract addr %x", tx.Hash()[12:]) + ethutil.Config.Log.Infof("Contract addr %x", tx.CreationAddress()) } else { ethutil.Config.Log.Infof("Tx hash %x", tx.Hash()) } - return ethutil.Hex(tx.Hash()), nil + return NewPReciept(contractCreation, tx.CreationAddress(), tx.Hash(), keyPair.Address()), nil } diff --git a/ethpub/types.go b/ethpub/types.go index 5e7e4613d..bf06ce2f6 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -43,6 +43,22 @@ func NewPKey(key *ethchain.KeyPair) *PKey { return &PKey{ethutil.Hex(key.Address()), ethutil.Hex(key.PrivateKey), ethutil.Hex(key.PublicKey)} } +type PReceipt struct { + CreatedContract bool + Address string + Hash string + Sender string +} + +func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt { + return &PReceipt{ + contractCreation, + ethutil.Hex(creationAddress), + ethutil.Hex(hash), + ethutil.Hex(address), + } +} + /* type PKeyRing struct { Keys []interface{} From 4f20e8f649a19168718a7f0fe7619e3bdb626aa8 Mon Sep 17 00:00:00 2001 From: Maran Date: Fri, 2 May 2014 20:00:58 +0200 Subject: [PATCH 087/100] Implemented first few methods via public api --- ethereum.go | 5 ++--- etherpc/packages.go | 39 +++++++++++++++++++++++---------------- etherpc/server.go | 7 +++++-- ethpub/pub.go | 2 +- ethpub/types.go | 12 ++++++------ 5 files changed, 37 insertions(+), 28 deletions(-) diff --git a/ethereum.go b/ethereum.go index 4181f9cd8..6cb1a916f 100644 --- a/ethereum.go +++ b/ethereum.go @@ -4,7 +4,6 @@ import ( "container/list" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" - "github.com/ethereum/eth-go/etherpc" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "io/ioutil" @@ -64,7 +63,7 @@ type Ethereum struct { reactor *ethutil.ReactorEngine - RpcServer *etherpc.JsonRpcServer + // TODO: This no worky: RpcServer *etherpc.JsonRpcServer } func New(caps Caps, usePnp bool) (*Ethereum, error) { @@ -341,7 +340,7 @@ func (s *Ethereum) Stop() { s.txPool.Stop() s.stateManager.Stop() - s.RpcServer.Stop() + // TODO: THIS NO WORKY: s.RpcServer.Stop() close(s.shutdownChan) } diff --git a/etherpc/packages.go b/etherpc/packages.go index 8bc78498f..271a59879 100644 --- a/etherpc/packages.go +++ b/etherpc/packages.go @@ -3,18 +3,20 @@ package etherpc import ( "encoding/json" "errors" - "math/big" + "github.com/ethereum/eth-go/ethpub" + _ "log" ) -type MainPackage struct{} +type MainPackage struct { + ethp *ethpub.PEthereum +} type JsonArgs interface { requirements() error } type BlockResponse struct { - Name string - Id int + JsonResponse } type GetBlockArgs struct { BlockNumber int @@ -63,22 +65,23 @@ func (b *GetBlockArgs) requirements() error { return nil } -func (p *MainPackage) GetBlock(args *GetBlockArgs, reply *BlockResponse) error { +func (p *MainPackage) GetBlock(args *GetBlockArgs, reply *string) error { err := args.requirements() if err != nil { return err } // Do something - + block := p.ethp.GetBlock(args.Hash) + *reply = NewSuccessRes(block) return nil } type NewTxArgs struct { Sec string Recipient string - Value *big.Int - Gas *big.Int - GasPrice *big.Int + Value string + Gas string + GasPrice string Init string Body string } @@ -90,26 +93,26 @@ func (a *NewTxArgs) requirements() error { if a.Recipient == "" { return NewErrorResponse("Transact requires a 'recipient' address as argument") } - if a.Value == nil { + if a.Value == "" { return NewErrorResponse("Transact requires a 'value' as argument") } - if a.Gas == nil { + if a.Gas == "" { return NewErrorResponse("Transact requires a 'gas' value as argument") } - if a.GasPrice == nil { + if a.GasPrice == "" { return NewErrorResponse("Transact requires a 'gasprice' value as argument") } return nil } func (a *NewTxArgs) requirementsContract() error { - if a.Value == nil { + if a.Value == "" { return NewErrorResponse("Create requires a 'value' as argument") } - if a.Gas == nil { + if a.Gas == "" { return NewErrorResponse("Create requires a 'gas' value as argument") } - if a.GasPrice == nil { + if a.GasPrice == "" { return NewErrorResponse("Create requires a 'gasprice' value as argument") } if a.Init == "" { @@ -121,11 +124,13 @@ func (a *NewTxArgs) requirementsContract() error { return nil } -func (p *MainPackage) Transact(args *NewTxArgs, reply *TxResponse) error { +func (p *MainPackage) Transact(args *NewTxArgs, reply *string) error { err := args.requirements() if err != nil { return err } + result, _ := p.ethp.Transact(p.ethp.GetKey().PrivateKey, args.Recipient, args.Value, args.Gas, args.GasPrice, args.Body) + *reply = NewSuccessRes(result) return nil } @@ -134,6 +139,8 @@ func (p *MainPackage) Create(args *NewTxArgs, reply *string) error { if err != nil { return err } + result, _ := p.ethp.Create(p.ethp.GetKey().PrivateKey, args.Value, args.Gas, args.GasPrice, args.Init, args.Body) + *reply = NewSuccessRes(result) return nil } diff --git a/etherpc/server.go b/etherpc/server.go index ba0c51006..7929563cb 100644 --- a/etherpc/server.go +++ b/etherpc/server.go @@ -1,6 +1,7 @@ package etherpc import ( + "github.com/ethereum/eth-go/ethpub" "github.com/ethereum/eth-go/ethutil" "net" "net/rpc" @@ -10,6 +11,7 @@ import ( type JsonRpcServer struct { quit chan bool listener net.Listener + ethp *ethpub.PEthereum } func (s *JsonRpcServer) exitHandler() { @@ -32,7 +34,7 @@ func (s *JsonRpcServer) Stop() { func (s *JsonRpcServer) Start() { ethutil.Config.Log.Infoln("[JSON] Starting JSON-RPC server") go s.exitHandler() - rpc.Register(new(MainPackage)) + rpc.Register(&MainPackage{ethp: s.ethp}) rpc.HandleHTTP() for { @@ -46,7 +48,7 @@ func (s *JsonRpcServer) Start() { } } -func NewJsonRpcServer() *JsonRpcServer { +func NewJsonRpcServer(ethp *ethpub.PEthereum) *JsonRpcServer { l, err := net.Listen("tcp", ":30304") if err != nil { ethutil.Config.Log.Infoln("Error starting JSON-RPC") @@ -55,5 +57,6 @@ func NewJsonRpcServer() *JsonRpcServer { return &JsonRpcServer{ listener: l, quit: make(chan bool), + ethp: ethp, } } diff --git a/ethpub/pub.go b/ethpub/pub.go index c6f177124..3c579001e 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -87,7 +87,7 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, in tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript) } else { // Just in case it was submitted as a 0x prefixed string - if initStr[0:2] == "0x" { + if len(initStr) > 0 && initStr[0:2] == "0x" { initStr = initStr[2:len(initStr)] } tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, ethutil.FromHex(initStr)) diff --git a/ethpub/types.go b/ethpub/types.go index bf06ce2f6..4d9f9ad85 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -8,8 +8,8 @@ import ( // Block interface exposed to QML type PBlock struct { - Number int - Hash string + Number int `json:"number"` + Hash string `json:"hash"` } // Creates a new QML Block from a chain block @@ -44,10 +44,10 @@ func NewPKey(key *ethchain.KeyPair) *PKey { } type PReceipt struct { - CreatedContract bool - Address string - Hash string - Sender string + CreatedContract bool `json:"createdContract"` + Address string `json:"address"` + Hash string `json:"hash"` + Sender string `json:"sender"` } func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) *PReceipt { From 7c91159449c528daa099aec5a3744aa8a6b5a826 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 5 May 2014 11:56:25 +0200 Subject: [PATCH 088/100] Added different storage notification object --- ethchain/state_manager.go | 2 +- ethchain/state_object.go | 6 ++++++ ethpub/pub.go | 12 ++++++++++++ ethpub/types.go | 36 ++++++++++++++++++++++++++---------- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 02d0345d7..501ec102b 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -350,7 +350,7 @@ func (sm *StateManager) notifyChanges() { for stateObjectAddr, mappedObjects := range sm.manifest.storageChanges { for addr, value := range mappedObjects { - sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, value.String()) + sm.Ethereum.Reactor().Post("storage:"+stateObjectAddr+":"+addr, &StorageState{[]byte(stateObjectAddr), []byte(addr), value}) } } } diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 4ec91d2e0..617646077 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -186,3 +186,9 @@ type CachedStateObject struct { Nonce uint64 Object *StateObject } + +type StorageState struct { + StateAddress []byte + Address []byte + Value *big.Int +} diff --git a/ethpub/pub.go b/ethpub/pub.go index c6f177124..5f23018f7 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -47,6 +47,18 @@ func (lib *PEthereum) GetStateObject(address string) *PStateObject { return NewPStateObject(nil) } +func (lib *PEthereum) GetStorage(address, storageAddress string) string { + return lib.GetStateObject(address).GetStorage(storageAddress) +} + +func (lib *PEthereum) GetTxCount(address string) int { + return lib.GetStateObject(address).Nonce() +} + +func (lib *PEthereum) IsContract(address string) bool { + return lib.GetStateObject(address).IsContract() +} + func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (*PReceipt, error) { return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr, "") } diff --git a/ethpub/types.go b/ethpub/types.go index bf06ce2f6..7ae476339 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -59,16 +59,6 @@ func NewPReciept(contractCreation bool, creationAddress, hash, address []byte) * } } -/* -type PKeyRing struct { - Keys []interface{} -} - -func NewPKeyRing(keys []interface{}) *PKeyRing { - return &PKeyRing{Keys: keys} -} -*/ - type PStateObject struct { object *ethchain.StateObject } @@ -105,3 +95,29 @@ func (c *PStateObject) Address() string { return "" } + +func (c *PStateObject) Nonce() int { + if c.object != nil { + return int(c.object.Nonce) + } + + return 0 +} + +func (c *PStateObject) IsContract() bool { + if c.object != nil { + return len(c.object.Script()) > 0 + } + + return false +} + +type PStorageState struct { + StateAddress string + Address string + Value string +} + +func NewPStorageState(storageObject *ethchain.StorageState) *PStorageState { + return &PStorageState{ethutil.Hex(storageObject.StateAddress), ethutil.Hex(storageObject.Address), storageObject.Value.String()} +} From 39b8c83ba66d429d04fa8b9be62813c848f0d606 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 5 May 2014 13:01:02 +0200 Subject: [PATCH 089/100] Impelemented GetStorageAt --- etherpc/packages.go | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/etherpc/packages.go b/etherpc/packages.go index 271a59879..85306d3d7 100644 --- a/etherpc/packages.go +++ b/etherpc/packages.go @@ -115,9 +115,6 @@ func (a *NewTxArgs) requirementsContract() error { if a.GasPrice == "" { return NewErrorResponse("Create requires a 'gasprice' value as argument") } - if a.Init == "" { - return NewErrorResponse("Create requires a 'init' value as argument") - } if a.Body == "" { return NewErrorResponse("Create requires a 'body' value as argument") } @@ -144,7 +141,8 @@ func (p *MainPackage) Create(args *NewTxArgs, reply *string) error { return nil } -func (p *MainPackage) getKey(args interface{}, reply *string) error { +func (p *MainPackage) GetKey(args interface{}, reply *string) error { + *reply = NewSuccessRes(p.ethp.GetKey()) return nil } @@ -163,11 +161,20 @@ func (a *GetStorageArgs) requirements() error { return nil } -func (p *MainPackage) getStorageAt(args *GetStorageArgs, reply *string) error { +type GetStorageAtRes struct { + Key string `json:"key"` + Value string `json:"value"` + Address string `json:"address"` +} + +func (p *MainPackage) GetStorageAt(args *GetStorageArgs, reply *string) error { err := args.requirements() if err != nil { return err } + state := p.ethp.GetStateObject(args.Address) + value := state.GetStorage(args.Key) + *reply = NewSuccessRes(&GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value}) return nil } From 5757f5df2a5ffc4207ab2be5f1280d4f2e95d303 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 5 May 2014 13:09:29 +0200 Subject: [PATCH 090/100] Added secret to address method --- ethpub/pub.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ethpub/pub.go b/ethpub/pub.go index 5f23018f7..f9d2ebc72 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -59,6 +59,15 @@ func (lib *PEthereum) IsContract(address string) bool { return lib.GetStateObject(address).IsContract() } +func (lib *PEthereum) SecretToAddress(key string) string { + pair, err := ethchain.NewKeyPairFromSec(ethutil.FromHex(key)) + if err != nil { + return "" + } + + return ethutil.Hex(pair.Address()) +} + func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (*PReceipt, error) { return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr, "") } From 8adad0654a46e4cad925a14f1c238081958558df Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 5 May 2014 13:49:46 +0200 Subject: [PATCH 091/100] Added more JSON niceties to types --- ethpub/types.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ethpub/types.go b/ethpub/types.go index 4d9f9ad85..522f0e7ac 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -34,9 +34,9 @@ func NewPTx(tx *ethchain.Transaction) *PTx { } type PKey struct { - Address string - PrivateKey string - PublicKey string + Address string `json:"address"` + PrivateKey string `json:"privateKey"` + PublicKey string `json:"publicKey"` } func NewPKey(key *ethchain.KeyPair) *PKey { From c496aad20bebdeea240ee4b6cc8730d484240ca8 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 5 May 2014 13:49:59 +0200 Subject: [PATCH 092/100] Renamed Ethereum RPC package --- etherpc/packages.go | 25 ++++++++++++++++--------- etherpc/server.go | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/etherpc/packages.go b/etherpc/packages.go index 85306d3d7..5074b0219 100644 --- a/etherpc/packages.go +++ b/etherpc/packages.go @@ -7,7 +7,7 @@ import ( _ "log" ) -type MainPackage struct { +type EthereumApi struct { ethp *ethpub.PEthereum } @@ -65,7 +65,7 @@ func (b *GetBlockArgs) requirements() error { return nil } -func (p *MainPackage) GetBlock(args *GetBlockArgs, reply *string) error { +func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *string) error { err := args.requirements() if err != nil { return err @@ -121,7 +121,7 @@ func (a *NewTxArgs) requirementsContract() error { return nil } -func (p *MainPackage) Transact(args *NewTxArgs, reply *string) error { +func (p *EthereumApi) Transact(args *NewTxArgs, reply *string) error { err := args.requirements() if err != nil { return err @@ -131,7 +131,7 @@ func (p *MainPackage) Transact(args *NewTxArgs, reply *string) error { return nil } -func (p *MainPackage) Create(args *NewTxArgs, reply *string) error { +func (p *EthereumApi) Create(args *NewTxArgs, reply *string) error { err := args.requirementsContract() if err != nil { return err @@ -141,7 +141,7 @@ func (p *MainPackage) Create(args *NewTxArgs, reply *string) error { return nil } -func (p *MainPackage) GetKey(args interface{}, reply *string) error { +func (p *EthereumApi) GetKey(args interface{}, reply *string) error { *reply = NewSuccessRes(p.ethp.GetKey()) return nil } @@ -167,14 +167,14 @@ type GetStorageAtRes struct { Address string `json:"address"` } -func (p *MainPackage) GetStorageAt(args *GetStorageArgs, reply *string) error { +func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *string) error { err := args.requirements() if err != nil { return err } state := p.ethp.GetStateObject(args.Address) value := state.GetStorage(args.Key) - *reply = NewSuccessRes(&GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value}) + *reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value}) return nil } @@ -189,11 +189,18 @@ func (a *GetBalanceArgs) requirements() error { return nil } -func (p *MainPackage) GetBalanceAt(args *GetBalanceArgs, reply *string) error { +type BalanceRes struct { + Balance string `json:"balance"` + Address string `json:"address"` +} + +func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error { err := args.requirements() if err != nil { return err } + state := p.ethp.GetStateObject(args.Address) + *reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address}) return nil } @@ -202,7 +209,7 @@ type TestRes struct { Answer int `json:"answer"` } -func (p *MainPackage) Test(args *GetBlockArgs, reply *string) error { +func (p *EthereumApi) Test(args *GetBlockArgs, reply *string) error { *reply = NewSuccessRes(TestRes{Answer: 15}) return nil } diff --git a/etherpc/server.go b/etherpc/server.go index 7929563cb..49b4604cc 100644 --- a/etherpc/server.go +++ b/etherpc/server.go @@ -34,7 +34,7 @@ func (s *JsonRpcServer) Stop() { func (s *JsonRpcServer) Start() { ethutil.Config.Log.Infoln("[JSON] Starting JSON-RPC server") go s.exitHandler() - rpc.Register(&MainPackage{ethp: s.ethp}) + rpc.Register(&EthereumApi{ethp: s.ethp}) rpc.HandleHTTP() for { From fde3e01f80420c367139a9fe647c618b0223842d Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 5 May 2014 14:15:31 +0200 Subject: [PATCH 093/100] Fixed import cycle --- ethereum.go | 5 +++-- etherpc/server.go | 2 +- ethpub/pub.go | 9 ++++----- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ethereum.go b/ethereum.go index 6cb1a916f..d6154e213 100644 --- a/ethereum.go +++ b/ethereum.go @@ -4,6 +4,7 @@ import ( "container/list" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/etherpc" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "io/ioutil" @@ -63,7 +64,7 @@ type Ethereum struct { reactor *ethutil.ReactorEngine - // TODO: This no worky: RpcServer *etherpc.JsonRpcServer + RpcServer *etherpc.JsonRpcServer } func New(caps Caps, usePnp bool) (*Ethereum, error) { @@ -338,9 +339,9 @@ func (s *Ethereum) Stop() { close(s.quit) + s.RpcServer.Stop() s.txPool.Stop() s.stateManager.Stop() - // TODO: THIS NO WORKY: s.RpcServer.Stop() close(s.shutdownChan) } diff --git a/etherpc/server.go b/etherpc/server.go index 49b4604cc..0eb229536 100644 --- a/etherpc/server.go +++ b/etherpc/server.go @@ -41,7 +41,7 @@ func (s *JsonRpcServer) Start() { conn, err := s.listener.Accept() if err != nil { ethutil.Config.Log.Infoln("[JSON] Error starting JSON-RPC:", err) - continue + break } ethutil.Config.Log.Debugln("[JSON] Incoming request.") go jsonrpc.ServeConn(conn) diff --git a/ethpub/pub.go b/ethpub/pub.go index 3c579001e..5dc08160f 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -1,7 +1,6 @@ package ethpub import ( - "github.com/ethereum/eth-go" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethutil" ) @@ -12,11 +11,11 @@ type PEthereum struct { txPool *ethchain.TxPool } -func NewPEthereum(eth *eth.Ethereum) *PEthereum { +func NewPEthereum(sm *ethchain.StateManager, bc *ethchain.BlockChain, txp *ethchain.TxPool) *PEthereum { return &PEthereum{ - eth.StateManager(), - eth.BlockChain(), - eth.TxPool(), + sm, + bc, + txp, } } From b98cc2fb4e296c7a9efe50293cc43d6f9ef6f23d Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 5 May 2014 14:15:58 +0200 Subject: [PATCH 094/100] Fixed GetBlock when no block can be found --- ethpub/pub.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ethpub/pub.go b/ethpub/pub.go index 5dc08160f..64109dbfa 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -24,7 +24,15 @@ func (lib *PEthereum) GetBlock(hexHash string) *PBlock { block := lib.blockChain.GetBlock(hash) - return &PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())} + var blockInfo *PBlock + + if block != nil { + blockInfo = &PBlock{Number: int(block.BlockInfo().Number), Hash: ethutil.Hex(block.Hash())} + } else { + blockInfo = &PBlock{Number: -1, Hash: ""} + } + + return blockInfo } func (lib *PEthereum) GetKey() *PKey { From 9b1f11695d544b99ba0b7ddedc4ac561a68d8197 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 5 May 2014 14:20:20 +0200 Subject: [PATCH 095/100] Get rid of that annoying number that keeps popping up in stdout --- ethutil/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ethutil/config.go b/ethutil/config.go index 323773ba7..609200649 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -139,7 +139,7 @@ func (log *Logger) Infoln(v ...interface{}) { return } - fmt.Println(len(log.logSys)) + //fmt.Println(len(log.logSys)) for _, logger := range log.logSys { logger.Println(v...) } From a4ca9927abc774fe524635a38d2f3bf2c4831278 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 5 May 2014 15:15:14 +0200 Subject: [PATCH 096/100] Renamed etherpc to ethrpc --- ethereum.go | 4 +- etherpc/packages.go | 215 -------------------------------------------- etherpc/server.go | 62 ------------- 3 files changed, 2 insertions(+), 279 deletions(-) delete mode 100644 etherpc/packages.go delete mode 100644 etherpc/server.go diff --git a/ethereum.go b/ethereum.go index d6154e213..707938639 100644 --- a/ethereum.go +++ b/ethereum.go @@ -4,7 +4,7 @@ import ( "container/list" "github.com/ethereum/eth-go/ethchain" "github.com/ethereum/eth-go/ethdb" - "github.com/ethereum/eth-go/etherpc" + "github.com/ethereum/eth-go/ethrpc" "github.com/ethereum/eth-go/ethutil" "github.com/ethereum/eth-go/ethwire" "io/ioutil" @@ -64,7 +64,7 @@ type Ethereum struct { reactor *ethutil.ReactorEngine - RpcServer *etherpc.JsonRpcServer + RpcServer *ethrpc.JsonRpcServer } func New(caps Caps, usePnp bool) (*Ethereum, error) { diff --git a/etherpc/packages.go b/etherpc/packages.go deleted file mode 100644 index 5074b0219..000000000 --- a/etherpc/packages.go +++ /dev/null @@ -1,215 +0,0 @@ -package etherpc - -import ( - "encoding/json" - "errors" - "github.com/ethereum/eth-go/ethpub" - _ "log" -) - -type EthereumApi struct { - ethp *ethpub.PEthereum -} - -type JsonArgs interface { - requirements() error -} - -type BlockResponse struct { - JsonResponse -} -type GetBlockArgs struct { - BlockNumber int - Hash string -} - -type ErrorResponse struct { - Error bool `json:"error"` - ErrorText string `json:"errorText"` -} - -type JsonResponse interface { -} - -type SuccessRes struct { - Error bool `json:"error"` - Result JsonResponse `json:"result"` -} - -func NewSuccessRes(object JsonResponse) string { - e := SuccessRes{Error: false, Result: object} - res, err := json.Marshal(e) - if err != nil { - // This should never happen - panic("Creating json error response failed, help") - } - success := string(res) - return success -} - -func NewErrorResponse(msg string) error { - e := ErrorResponse{Error: true, ErrorText: msg} - res, err := json.Marshal(e) - if err != nil { - // This should never happen - panic("Creating json error response failed, help") - } - newErr := errors.New(string(res)) - return newErr -} - -func (b *GetBlockArgs) requirements() error { - if b.BlockNumber == 0 && b.Hash == "" { - return NewErrorResponse("GetBlock requires either a block 'number' or a block 'hash' as argument") - } - return nil -} - -func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *string) error { - err := args.requirements() - if err != nil { - return err - } - // Do something - block := p.ethp.GetBlock(args.Hash) - *reply = NewSuccessRes(block) - return nil -} - -type NewTxArgs struct { - Sec string - Recipient string - Value string - Gas string - GasPrice string - Init string - Body string -} -type TxResponse struct { - Hash string -} - -func (a *NewTxArgs) requirements() error { - if a.Recipient == "" { - return NewErrorResponse("Transact requires a 'recipient' address as argument") - } - if a.Value == "" { - return NewErrorResponse("Transact requires a 'value' as argument") - } - if a.Gas == "" { - return NewErrorResponse("Transact requires a 'gas' value as argument") - } - if a.GasPrice == "" { - return NewErrorResponse("Transact requires a 'gasprice' value as argument") - } - return nil -} - -func (a *NewTxArgs) requirementsContract() error { - if a.Value == "" { - return NewErrorResponse("Create requires a 'value' as argument") - } - if a.Gas == "" { - return NewErrorResponse("Create requires a 'gas' value as argument") - } - if a.GasPrice == "" { - return NewErrorResponse("Create requires a 'gasprice' value as argument") - } - if a.Body == "" { - return NewErrorResponse("Create requires a 'body' value as argument") - } - return nil -} - -func (p *EthereumApi) Transact(args *NewTxArgs, reply *string) error { - err := args.requirements() - if err != nil { - return err - } - result, _ := p.ethp.Transact(p.ethp.GetKey().PrivateKey, args.Recipient, args.Value, args.Gas, args.GasPrice, args.Body) - *reply = NewSuccessRes(result) - return nil -} - -func (p *EthereumApi) Create(args *NewTxArgs, reply *string) error { - err := args.requirementsContract() - if err != nil { - return err - } - result, _ := p.ethp.Create(p.ethp.GetKey().PrivateKey, args.Value, args.Gas, args.GasPrice, args.Init, args.Body) - *reply = NewSuccessRes(result) - return nil -} - -func (p *EthereumApi) GetKey(args interface{}, reply *string) error { - *reply = NewSuccessRes(p.ethp.GetKey()) - return nil -} - -type GetStorageArgs struct { - Address string - Key string -} - -func (a *GetStorageArgs) requirements() error { - if a.Address == "" { - return NewErrorResponse("GetStorageAt requires an 'address' value as argument") - } - if a.Key == "" { - return NewErrorResponse("GetStorageAt requires an 'key' value as argument") - } - return nil -} - -type GetStorageAtRes struct { - Key string `json:"key"` - Value string `json:"value"` - Address string `json:"address"` -} - -func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *string) error { - err := args.requirements() - if err != nil { - return err - } - state := p.ethp.GetStateObject(args.Address) - value := state.GetStorage(args.Key) - *reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value}) - return nil -} - -type GetBalanceArgs struct { - Address string -} - -func (a *GetBalanceArgs) requirements() error { - if a.Address == "" { - return NewErrorResponse("GetBalanceAt requires an 'address' value as argument") - } - return nil -} - -type BalanceRes struct { - Balance string `json:"balance"` - Address string `json:"address"` -} - -func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error { - err := args.requirements() - if err != nil { - return err - } - state := p.ethp.GetStateObject(args.Address) - *reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address}) - return nil -} - -type TestRes struct { - JsonResponse `json:"-"` - Answer int `json:"answer"` -} - -func (p *EthereumApi) Test(args *GetBlockArgs, reply *string) error { - *reply = NewSuccessRes(TestRes{Answer: 15}) - return nil -} diff --git a/etherpc/server.go b/etherpc/server.go deleted file mode 100644 index 0eb229536..000000000 --- a/etherpc/server.go +++ /dev/null @@ -1,62 +0,0 @@ -package etherpc - -import ( - "github.com/ethereum/eth-go/ethpub" - "github.com/ethereum/eth-go/ethutil" - "net" - "net/rpc" - "net/rpc/jsonrpc" -) - -type JsonRpcServer struct { - quit chan bool - listener net.Listener - ethp *ethpub.PEthereum -} - -func (s *JsonRpcServer) exitHandler() { -out: - for { - select { - case <-s.quit: - s.listener.Close() - break out - } - } - - ethutil.Config.Log.Infoln("[JSON] Shutdown JSON-RPC server") -} - -func (s *JsonRpcServer) Stop() { - close(s.quit) -} - -func (s *JsonRpcServer) Start() { - ethutil.Config.Log.Infoln("[JSON] Starting JSON-RPC server") - go s.exitHandler() - rpc.Register(&EthereumApi{ethp: s.ethp}) - rpc.HandleHTTP() - - for { - conn, err := s.listener.Accept() - if err != nil { - ethutil.Config.Log.Infoln("[JSON] Error starting JSON-RPC:", err) - break - } - ethutil.Config.Log.Debugln("[JSON] Incoming request.") - go jsonrpc.ServeConn(conn) - } -} - -func NewJsonRpcServer(ethp *ethpub.PEthereum) *JsonRpcServer { - l, err := net.Listen("tcp", ":30304") - if err != nil { - ethutil.Config.Log.Infoln("Error starting JSON-RPC") - } - - return &JsonRpcServer{ - listener: l, - quit: make(chan bool), - ethp: ethp, - } -} From 3e5a7b34b111e5b72cd4878cdee42af956143406 Mon Sep 17 00:00:00 2001 From: Maran Date: Mon, 5 May 2014 15:15:43 +0200 Subject: [PATCH 097/100] Now with files --- ethrpc/packages.go | 215 +++++++++++++++++++++++++++++++++++++++++++++ ethrpc/server.go | 62 +++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 ethrpc/packages.go create mode 100644 ethrpc/server.go diff --git a/ethrpc/packages.go b/ethrpc/packages.go new file mode 100644 index 000000000..b989a65cb --- /dev/null +++ b/ethrpc/packages.go @@ -0,0 +1,215 @@ +package ethrpc + +import ( + "encoding/json" + "errors" + "github.com/ethereum/eth-go/ethpub" + _ "log" +) + +type EthereumApi struct { + ethp *ethpub.PEthereum +} + +type JsonArgs interface { + requirements() error +} + +type BlockResponse struct { + JsonResponse +} +type GetBlockArgs struct { + BlockNumber int + Hash string +} + +type ErrorResponse struct { + Error bool `json:"error"` + ErrorText string `json:"errorText"` +} + +type JsonResponse interface { +} + +type SuccessRes struct { + Error bool `json:"error"` + Result JsonResponse `json:"result"` +} + +func NewSuccessRes(object JsonResponse) string { + e := SuccessRes{Error: false, Result: object} + res, err := json.Marshal(e) + if err != nil { + // This should never happen + panic("Creating json error response failed, help") + } + success := string(res) + return success +} + +func NewErrorResponse(msg string) error { + e := ErrorResponse{Error: true, ErrorText: msg} + res, err := json.Marshal(e) + if err != nil { + // This should never happen + panic("Creating json error response failed, help") + } + newErr := errors.New(string(res)) + return newErr +} + +func (b *GetBlockArgs) requirements() error { + if b.BlockNumber == 0 && b.Hash == "" { + return NewErrorResponse("GetBlock requires either a block 'number' or a block 'hash' as argument") + } + return nil +} + +func (p *EthereumApi) GetBlock(args *GetBlockArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + // Do something + block := p.ethp.GetBlock(args.Hash) + *reply = NewSuccessRes(block) + return nil +} + +type NewTxArgs struct { + Sec string + Recipient string + Value string + Gas string + GasPrice string + Init string + Body string +} +type TxResponse struct { + Hash string +} + +func (a *NewTxArgs) requirements() error { + if a.Recipient == "" { + return NewErrorResponse("Transact requires a 'recipient' address as argument") + } + if a.Value == "" { + return NewErrorResponse("Transact requires a 'value' as argument") + } + if a.Gas == "" { + return NewErrorResponse("Transact requires a 'gas' value as argument") + } + if a.GasPrice == "" { + return NewErrorResponse("Transact requires a 'gasprice' value as argument") + } + return nil +} + +func (a *NewTxArgs) requirementsContract() error { + if a.Value == "" { + return NewErrorResponse("Create requires a 'value' as argument") + } + if a.Gas == "" { + return NewErrorResponse("Create requires a 'gas' value as argument") + } + if a.GasPrice == "" { + return NewErrorResponse("Create requires a 'gasprice' value as argument") + } + if a.Body == "" { + return NewErrorResponse("Create requires a 'body' value as argument") + } + return nil +} + +func (p *EthereumApi) Transact(args *NewTxArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + result, _ := p.ethp.Transact(p.ethp.GetKey().PrivateKey, args.Recipient, args.Value, args.Gas, args.GasPrice, args.Body) + *reply = NewSuccessRes(result) + return nil +} + +func (p *EthereumApi) Create(args *NewTxArgs, reply *string) error { + err := args.requirementsContract() + if err != nil { + return err + } + result, _ := p.ethp.Create(p.ethp.GetKey().PrivateKey, args.Value, args.Gas, args.GasPrice, args.Init, args.Body) + *reply = NewSuccessRes(result) + return nil +} + +func (p *EthereumApi) GetKey(args interface{}, reply *string) error { + *reply = NewSuccessRes(p.ethp.GetKey()) + return nil +} + +type GetStorageArgs struct { + Address string + Key string +} + +func (a *GetStorageArgs) requirements() error { + if a.Address == "" { + return NewErrorResponse("GetStorageAt requires an 'address' value as argument") + } + if a.Key == "" { + return NewErrorResponse("GetStorageAt requires an 'key' value as argument") + } + return nil +} + +type GetStorageAtRes struct { + Key string `json:"key"` + Value string `json:"value"` + Address string `json:"address"` +} + +func (p *EthereumApi) GetStorageAt(args *GetStorageArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + state := p.ethp.GetStateObject(args.Address) + value := state.GetStorage(args.Key) + *reply = NewSuccessRes(GetStorageAtRes{Address: args.Address, Key: args.Key, Value: value}) + return nil +} + +type GetBalanceArgs struct { + Address string +} + +func (a *GetBalanceArgs) requirements() error { + if a.Address == "" { + return NewErrorResponse("GetBalanceAt requires an 'address' value as argument") + } + return nil +} + +type BalanceRes struct { + Balance string `json:"balance"` + Address string `json:"address"` +} + +func (p *EthereumApi) GetBalanceAt(args *GetBalanceArgs, reply *string) error { + err := args.requirements() + if err != nil { + return err + } + state := p.ethp.GetStateObject(args.Address) + *reply = NewSuccessRes(BalanceRes{Balance: state.Value(), Address: args.Address}) + return nil +} + +type TestRes struct { + JsonResponse `json:"-"` + Answer int `json:"answer"` +} + +func (p *EthereumApi) Test(args *GetBlockArgs, reply *string) error { + *reply = NewSuccessRes(TestRes{Answer: 15}) + return nil +} diff --git a/ethrpc/server.go b/ethrpc/server.go new file mode 100644 index 000000000..40787fade --- /dev/null +++ b/ethrpc/server.go @@ -0,0 +1,62 @@ +package ethrpc + +import ( + "github.com/ethereum/eth-go/ethpub" + "github.com/ethereum/eth-go/ethutil" + "net" + "net/rpc" + "net/rpc/jsonrpc" +) + +type JsonRpcServer struct { + quit chan bool + listener net.Listener + ethp *ethpub.PEthereum +} + +func (s *JsonRpcServer) exitHandler() { +out: + for { + select { + case <-s.quit: + s.listener.Close() + break out + } + } + + ethutil.Config.Log.Infoln("[JSON] Shutdown JSON-RPC server") +} + +func (s *JsonRpcServer) Stop() { + close(s.quit) +} + +func (s *JsonRpcServer) Start() { + ethutil.Config.Log.Infoln("[JSON] Starting JSON-RPC server") + go s.exitHandler() + rpc.Register(&EthereumApi{ethp: s.ethp}) + rpc.HandleHTTP() + + for { + conn, err := s.listener.Accept() + if err != nil { + ethutil.Config.Log.Infoln("[JSON] Error starting JSON-RPC:", err) + break + } + ethutil.Config.Log.Debugln("[JSON] Incoming request.") + go jsonrpc.ServeConn(conn) + } +} + +func NewJsonRpcServer(ethp *ethpub.PEthereum) *JsonRpcServer { + l, err := net.Listen("tcp", ":30304") + if err != nil { + ethutil.Config.Log.Infoln("Error starting JSON-RPC") + } + + return &JsonRpcServer{ + listener: l, + quit: make(chan bool), + ethp: ethp, + } +} From 14a6e6a9cea48bcc7f6d5286e4f85b1a0cce84d8 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 5 May 2014 15:48:17 +0200 Subject: [PATCH 098/100] Added the ability to submit byte code for contracts instead of Mutan code. --- ethpub/pub.go | 23 +++++++++++++++++------ ethutil/bytes.go | 5 +++++ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/ethpub/pub.go b/ethpub/pub.go index 431f173a0..4ec9877b2 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -103,13 +103,24 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, in var tx *ethchain.Transaction // Compile and assemble the given data if contractCreation { - initScript, err := ethutil.Compile(initStr) - if err != nil { - return nil, err + var initScript, mainScript []byte + var err error + if ethutil.IsHex(initStr) { + initScript = ethutil.FromHex(initStr) + } else { + initScript, err = ethutil.Compile(initStr[2:]) + if err != nil { + return nil, err + } } - mainScript, err := ethutil.Compile(scriptStr) - if err != nil { - return nil, err + + if ethutil.IsHex(scriptStr) { + mainScript = ethutil.FromHex(scriptStr[2:]) + } else { + mainScript, err = ethutil.Compile(scriptStr) + if err != nil { + return nil, err + } } tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript) diff --git a/ethutil/bytes.go b/ethutil/bytes.go index 500368017..b298675a2 100644 --- a/ethutil/bytes.go +++ b/ethutil/bytes.go @@ -83,3 +83,8 @@ func CopyBytes(b []byte) (copiedBytes []byte) { return } + +func IsHex(str string) bool { + l := len(str) + return l >= 4 && l%2 == 0 && str[0:2] == "0x" +} From 78cb04cca3daafa178558a3dec5f4814f824dc95 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 5 May 2014 15:51:43 +0200 Subject: [PATCH 099/100] wrong string --- ethpub/pub.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ethpub/pub.go b/ethpub/pub.go index 4ec9877b2..5e7792a9f 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -106,9 +106,9 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, in var initScript, mainScript []byte var err error if ethutil.IsHex(initStr) { - initScript = ethutil.FromHex(initStr) + initScript = ethutil.FromHex(initStr[2:]) } else { - initScript, err = ethutil.Compile(initStr[2:]) + initScript, err = ethutil.Compile(initStr) if err != nil { return nil, err } From fedd4c906ff9f6139cb2d88e4f1adefbf6ea81a6 Mon Sep 17 00:00:00 2001 From: obscuren Date: Mon, 5 May 2014 15:55:26 +0200 Subject: [PATCH 100/100] bump --- README.md | 2 +- ethutil/config.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9d810643b..0a622fdcb 100644 --- a/README.md +++ b/README.md @@ -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 5.0". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). +of Concept 5.0 RC1". 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/ethutil/config.go b/ethutil/config.go index 609200649..382396ceb 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -51,7 +51,7 @@ func ReadConfig(base string) *config { } } - Config = &config{ExecPath: path, Debug: true, Ver: "0.5"} + Config = &config{ExecPath: path, Debug: true, Ver: "0.5 RC1"} Config.Log = NewLogger(LogFile|LogStd, LogLevelDebug) Config.SetClientString("/Ethereum(G)") }