diff --git a/README.md b/README.md index 2b080273bb..081d9481e6 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 RC8". For build instructions see the [Wiki](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum(Go)). +of Concept 5.0 RC9". 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/block.go b/ethchain/block.go index 3401632b19..73e29f8783 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -1,6 +1,7 @@ package ethchain import ( + "bytes" "fmt" "github.com/ethereum/eth-go/ethutil" "math/big" @@ -55,6 +56,7 @@ type Block struct { Nonce []byte // List of transactions and/or contracts transactions []*Transaction + receipts []*Receipt TxSha []byte } @@ -84,24 +86,20 @@ func CreateBlock(root interface{}, base []byte, Difficulty *big.Int, Nonce []byte, - extra string, - txes []*Transaction) *Block { + extra string) *Block { block := &Block{ - // Slice of transactions to include in this block - transactions: txes, - PrevHash: prevHash, - Coinbase: base, - Difficulty: Difficulty, - Nonce: Nonce, - Time: time.Now().Unix(), - Extra: extra, - UncleSha: EmptyShaList, - GasUsed: new(big.Int), - MinGasPrice: new(big.Int), - GasLimit: new(big.Int), + PrevHash: prevHash, + Coinbase: base, + Difficulty: Difficulty, + Nonce: Nonce, + Time: time.Now().Unix(), + Extra: extra, + UncleSha: EmptyShaList, + GasUsed: new(big.Int), + MinGasPrice: new(big.Int), + GasLimit: new(big.Int), } - block.SetTransactions(txes) block.SetUncles([]*Block{}) block.state = NewState(ethutil.NewTrie(ethutil.Config.Db, root)) @@ -115,7 +113,10 @@ func (block *Block) Hash() []byte { } 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})) + return ethutil.Sha3Bin(ethutil.Encode([]interface{}{block.PrevHash, + block.UncleSha, block.Coinbase, block.state.trie.Root, + block.TxSha, block.Difficulty, block.Number, block.MinGasPrice, + block.GasLimit, block.GasUsed, block.Time, block.Extra})) } func (block *Block) State() *State { @@ -161,6 +162,16 @@ func (block *Block) BlockInfo() BlockInfo { return bi } +func (self *Block) GetTransaction(hash []byte) *Transaction { + for _, receipt := range self.receipts { + if bytes.Compare(receipt.Tx.Hash(), hash) == 0 { + return receipt.Tx + } + } + + return nil +} + // Sync the block's state and contract respectively func (block *Block) Sync() { block.state.Sync() @@ -172,15 +183,15 @@ func (block *Block) Undo() { } /////// Block Encoding -func (block *Block) rlpTxs() interface{} { +func (block *Block) rlpReceipts() interface{} { // Marshal the transactions of this block - encTx := make([]interface{}, len(block.transactions)) - for i, tx := range block.transactions { + encR := make([]interface{}, len(block.receipts)) + for i, r := range block.receipts { // Cast it to a string (safe) - encTx[i] = tx.RlpData() + encR[i] = r.RlpData() } - return encTx + return encR } func (block *Block) rlpUncles() interface{} { @@ -201,7 +212,12 @@ func (block *Block) SetUncles(uncles []*Block) { block.UncleSha = ethutil.Sha3Bin(ethutil.Encode(block.rlpUncles())) } -func (block *Block) SetTransactions(txs []*Transaction) { +func (self *Block) SetReceipts(receipts []*Receipt, txs []*Transaction) { + self.receipts = receipts + self.setTransactions(txs) +} + +func (block *Block) setTransactions(txs []*Transaction) { block.transactions = txs trie := ethutil.NewTrie(ethutil.Config.Db, "") @@ -221,7 +237,7 @@ func (block *Block) SetTransactions(txs []*Transaction) { } func (block *Block) Value() *ethutil.Value { - return ethutil.NewValue([]interface{}{block.header(), block.rlpTxs(), block.rlpUncles()}) + return ethutil.NewValue([]interface{}{block.header(), block.rlpReceipts(), block.rlpUncles()}) } func (block *Block) RlpEncode() []byte { @@ -245,6 +261,7 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) { block.TxSha = header.Get(4).Bytes() block.Difficulty = header.Get(5).BigInt() block.Number = header.Get(6).BigInt() + //fmt.Printf("#%v : %x\n", block.Number, block.Coinbase) block.MinGasPrice = header.Get(7).BigInt() block.GasLimit = header.Get(8).BigInt() block.GasUsed = header.Get(9).BigInt() @@ -255,12 +272,13 @@ func (block *Block) RlpValueDecode(decoder *ethutil.Value) { // Tx list might be empty if this is an uncle. Uncles only have their // header set. if decoder.Get(1).IsNil() == false { // Yes explicitness - txes := decoder.Get(1) - block.transactions = make([]*Transaction, txes.Len()) - for i := 0; i < txes.Len(); i++ { - tx := NewTransactionFromValue(txes.Get(i)) - - block.transactions[i] = tx + receipts := decoder.Get(1) + block.transactions = make([]*Transaction, receipts.Len()) + block.receipts = make([]*Receipt, receipts.Len()) + for i := 0; i < receipts.Len(); i++ { + receipt := NewRecieptFromValue(receipts.Get(i)) + block.transactions[i] = receipt.Tx + block.receipts[i] = receipt } } @@ -299,6 +317,10 @@ func (block *Block) GetRoot() interface{} { return block.state.trie.Root } +func (self *Block) Receipts() []*Receipt { + return self.receipts +} + func (block *Block) header() []interface{} { return []interface{}{ // Sha of the previous block @@ -346,6 +368,7 @@ func (block *Block) String() string { Time: %v Extra: %v Nonce: %x + NumTx: %v `, block.Hash(), block.PrevHash, @@ -360,5 +383,7 @@ func (block *Block) String() string { block.GasUsed, block.Time, block.Extra, - block.Nonce) + block.Nonce, + len(block.transactions), + ) } diff --git a/ethchain/block_chain.go b/ethchain/block_chain.go index b72c78cdb0..b45d254b5c 100644 --- a/ethchain/block_chain.go +++ b/ethchain/block_chain.go @@ -36,7 +36,7 @@ func (bc *BlockChain) Genesis() *Block { return bc.genesisBlock } -func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block { +func (bc *BlockChain) NewBlock(coinbase []byte) *Block { var root interface{} var lastBlockTime int64 hash := ZeroHash256 @@ -53,8 +53,7 @@ func (bc *BlockChain) NewBlock(coinbase []byte, txs []*Transaction) *Block { coinbase, ethutil.BigPow(2, 32), nil, - "", - txs) + "") if bc.CurrentBlock != nil { var mul *big.Int @@ -272,16 +271,18 @@ func (bc *BlockChain) GetChain(hash []byte, amount int) []*Block { func AddTestNetFunds(block *Block) { for _, addr := range []string{ - "8a40bfaa73256b60764c1bf40675a99083efb075", // Gavin - "e6716f9544a56c530d868e4bfbacb172315bdead", // Jeffrey - "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", // Vit - "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", // Alex - "2ef47100e0787b915105fd5e3f4ff6752079d5cb", // Maran - "cd2a3d9f938e13cd947ec05abc7fe734df8dd826", // Roman + "8a40bfaa73256b60764c1bf40675a99083efb075", + "e4157b34ea9615cfbde6b4fda419828124b70c78", + "1e12515ce3e0f817a4ddef9ca55788a1d66bd2df", + "6c386a4b26f73c802f34673f7248bb118f97424a", + "cd2a3d9f938e13cd947ec05abc7fe734df8dd826", + "2ef47100e0787b915105fd5e3f4ff6752079d5cb", + "e6716f9544a56c530d868e4bfbacb172315bdead", + "1a26338f0d905e295fccb71fa9ea849ffa12aaf4", } { codedAddr := ethutil.FromHex(addr) account := block.state.GetAccount(codedAddr) - account.Amount = ethutil.BigPow(2, 200) + account.Amount = ethutil.Big("1606938044258990275541962092341162602522202993782792835301376") //ethutil.BigPow(2, 200) block.state.UpdateStateObject(account) } log.Printf("%x\n", block.RlpEncode()) diff --git a/ethchain/block_chain_test.go b/ethchain/block_chain_test.go index 4e4bb9dd40..ecaf8ca894 100644 --- a/ethchain/block_chain_test.go +++ b/ethchain/block_chain_test.go @@ -50,7 +50,7 @@ func (tm *TestManager) Broadcast(msgType ethwire.MsgType, data []interface{}) { } func NewTestManager() *TestManager { - ethutil.ReadConfig(".ethtest", ethutil.LogStd) + ethutil.ReadConfig(".ethtest", ethutil.LogStd, "") db, err := ethdb.NewMemDatabase() if err != nil { @@ -74,7 +74,7 @@ func NewTestManager() *TestManager { func (tm *TestManager) AddFakeBlock(blk []byte) error { block := NewBlockFromBytes(blk) tm.Blocks = append(tm.Blocks, block) - err := tm.StateManager().ProcessBlock(tm.StateManager().CurrentState(), block, false) + err := tm.StateManager().Process(block, false) return err } func (tm *TestManager) CreateChain1() error { diff --git a/ethchain/genesis.go b/ethchain/genesis.go index b8f9f865a3..359c47c261 100644 --- a/ethchain/genesis.go +++ b/ethchain/genesis.go @@ -23,7 +23,7 @@ var GenesisHeader = []interface{}{ // Root state "", // tx sha - ZeroHash256, + "", // Difficulty ethutil.BigPow(2, 22), // Number diff --git a/ethchain/state.go b/ethchain/state.go index 6ec6916f46..e209e0e2ff 100644 --- a/ethchain/state.go +++ b/ethchain/state.go @@ -99,7 +99,21 @@ func (s *State) Cmp(other *State) bool { } func (s *State) Copy() *State { - return NewState(s.trie.Copy()) + state := NewState(s.trie.Copy()) + for k, subState := range s.states { + state.states[k] = subState.Copy() + } + + return state +} + +func (s *State) Snapshot() *State { + return s.Copy() +} + +func (s *State) Revert(snapshot *State) { + s.trie = snapshot.trie + s.states = snapshot.states } func (s *State) Put(key, object []byte) { diff --git a/ethchain/state_manager.go b/ethchain/state_manager.go index 8b56d65bb9..2d2a32e2fe 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -84,7 +84,7 @@ func (sm *StateManager) BlockChain() *BlockChain { return sm.bc } -func (sm *StateManager) MakeContract(state *State, tx *Transaction) *StateObject { +func (sm *StateManager) MakeStateObject(state *State, tx *Transaction) *StateObject { contract := MakeContract(tx, state) if contract != nil { state.states[string(tx.CreationAddress())] = contract.state @@ -97,40 +97,74 @@ func (sm *StateManager) MakeContract(state *State, tx *Transaction) *StateObject // Apply transactions uses the transaction passed to it and applies them onto // the current processing state. -func (sm *StateManager) ApplyTransactions(state *State, block *Block, txs []*Transaction) { +func (sm *StateManager) ApplyTransactions(state *State, block *Block, txs []*Transaction) ([]*Receipt, []*Transaction) { // Process each transaction/contract + var receipts []*Receipt + var validTxs []*Transaction + totalUsedGas := big.NewInt(0) for _, tx := range txs { - sm.ApplyTransaction(state, block, tx) + usedGas, err := sm.ApplyTransaction(state, block, tx) + if err != nil { + ethutil.Config.Log.Infoln(err) + continue + } + + accumelative := new(big.Int).Set(totalUsedGas.Add(totalUsedGas, usedGas)) + receipt := &Receipt{tx, ethutil.CopyBytes(state.Root().([]byte)), accumelative} + + receipts = append(receipts, receipt) + validTxs = append(validTxs, tx) } + + return receipts, txs } -func (sm *StateManager) ApplyTransaction(state *State, block *Block, tx *Transaction) error { - // 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() { - err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) +func (sm *StateManager) ApplyTransaction(state *State, block *Block, tx *Transaction) (*big.Int, error) { + /* + Applies transactions to the given state and creates new + state objects where needed. + + If said objects needs to be created + run the initialization script provided by the transaction and + assume there's a return value. The return value will be set to + the script section of the state object. + */ + totalGasUsed := big.NewInt(0) + // Apply the transaction to the current state + err := sm.Ethereum.TxPool().ProcessTransaction(tx, state, false) + if tx.CreatesContract() { if err == nil { - contract := sm.MakeContract(state, tx) + // Create a new state object and the transaction + // as it's data provider. + contract := sm.MakeStateObject(state, tx) if contract != nil { - sm.EvalScript(state, contract.Init(), contract, tx, block) + // Evaluate the initialization script + // and use the return value as the + // script section for the state object. + script, err := sm.EvalScript(state, contract.Init(), contract, tx, block) + if err != nil { + return nil, fmt.Errorf("[STATE] Error during init script run %v", err) + } + contract.script = script + state.UpdateStateObject(contract) } else { - return fmt.Errorf("[STATE] Unable to create contract") + return nil, fmt.Errorf("[STATE] Unable to create contract") } } else { - return fmt.Errorf("[STATE] contract create:", err) + return nil, fmt.Errorf("[STATE] contract creation tx:", err) } } else { - err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) - contract := state.GetStateObject(tx.Recipient) - if err == nil && contract != nil && len(contract.Script()) > 0 { - sm.EvalScript(state, contract.Script(), contract, tx, block) + // Find the state object at the "recipient" address. If + // there's an object attempt to run the script. + stateObject := state.GetStateObject(tx.Recipient) + if err == nil && stateObject != nil && len(stateObject.Script()) > 0 { + sm.EvalScript(state, stateObject.Script(), stateObject, tx, block) } else if err != nil { - return fmt.Errorf("[STATE] process:", err) + return nil, fmt.Errorf("[STATE] process:", err) } } - return nil + return totalGasUsed, nil } func (sm *StateManager) Process(block *Block, dontReact bool) error { @@ -276,6 +310,14 @@ func CalculateBlockReward(block *Block, uncleLength int) *big.Int { for i := 0; i < uncleLength; i++ { base.Add(base, UncleInclusionReward) } + + lastCumulGasUsed := big.NewInt(0) + for _, r := range block.Receipts() { + usedGas := new(big.Int).Sub(r.CumulativeGasUsed, lastCumulGasUsed) + usedGas.Add(usedGas, r.Tx.GasPrice) + base.Add(base, usedGas) + } + return base.Add(base, BlockReward) } @@ -307,10 +349,10 @@ func (sm *StateManager) Stop() { sm.bc.Stop() } -func (sm *StateManager) EvalScript(state *State, script []byte, object *StateObject, tx *Transaction, block *Block) { +func (sm *StateManager) EvalScript(state *State, script []byte, object *StateObject, tx *Transaction, block *Block) (ret []byte, err error) { account := state.GetAccount(tx.Sender()) - err := account.ConvertGas(tx.Gas, tx.GasPrice) + err = account.ConvertGas(tx.Gas, tx.GasPrice) if err != nil { ethutil.Config.Log.Debugln(err) return @@ -327,11 +369,13 @@ func (sm *StateManager) EvalScript(state *State, script []byte, object *StateObj Value: tx.Value, //Price: tx.GasPrice, }) - closure.Call(vm, tx.Data, nil) + ret, err = closure.Call(vm, tx.Data, nil) // Update the account (refunds) state.UpdateStateObject(account) state.UpdateStateObject(object) + + return } func (sm *StateManager) notifyChanges(state *State) { diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 8e059f3344..3e9c6df40b 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -28,8 +28,7 @@ func MakeContract(tx *Transaction, state *State) *StateObject { value := tx.Value contract := NewContract(addr, value, ZeroHash256) - contract.script = tx.Data - contract.initScript = tx.Init + contract.initScript = tx.Data state.UpdateStateObject(contract) @@ -82,12 +81,17 @@ func (c *StateObject) SetStorage(num *big.Int, val *ethutil.Value) { c.SetAddr(addr, val) } -func (c *StateObject) GetMem(num *big.Int) *ethutil.Value { +func (c *StateObject) GetStorage(num *big.Int) *ethutil.Value { nb := ethutil.BigToBytes(num, 256) return c.Addr(nb) } +/* DEPRECATED */ +func (c *StateObject) GetMem(num *big.Int) *ethutil.Value { + return c.GetStorage(num) +} + func (c *StateObject) GetInstr(pc *big.Int) *ethutil.Value { if int64(len(c.script)-1) < pc.Int64() { return ethutil.NewValue(0) @@ -146,7 +150,7 @@ func (c *StateObject) RlpEncode() []byte { if c.state != nil { root = c.state.trie.Root } else { - root = ZeroHash256 + root = "" } return ethutil.Encode([]interface{}{c.Amount, c.Nonce, root, ethutil.Sha3Bin(c.script)}) diff --git a/ethchain/state_object_test.go b/ethchain/state_object_test.go index e955acc56b..91ed7c0ddf 100644 --- a/ethchain/state_object_test.go +++ b/ethchain/state_object_test.go @@ -9,7 +9,7 @@ import ( ) func TestSync(t *testing.T) { - ethutil.ReadConfig("", ethutil.LogStd) + ethutil.ReadConfig("", ethutil.LogStd, "") db, _ := ethdb.NewMemDatabase() state := NewState(ethutil.NewTrie(db, "")) @@ -28,7 +28,7 @@ func TestSync(t *testing.T) { } func TestObjectGet(t *testing.T) { - ethutil.ReadConfig("", ethutil.LogStd) + ethutil.ReadConfig("", ethutil.LogStd, "") db, _ := ethdb.NewMemDatabase() ethutil.Config.Db = db diff --git a/ethchain/state_test.go b/ethchain/state_test.go new file mode 100644 index 0000000000..4cc3fdf75f --- /dev/null +++ b/ethchain/state_test.go @@ -0,0 +1,31 @@ +package ethchain + +import ( + "fmt" + "github.com/ethereum/eth-go/ethdb" + "github.com/ethereum/eth-go/ethutil" + "testing" +) + +func TestSnapshot(t *testing.T) { + ethutil.ReadConfig("", ethutil.LogStd, "") + + db, _ := ethdb.NewMemDatabase() + state := NewState(ethutil.NewTrie(db, "")) + + stateObject := NewContract([]byte("aa"), ethutil.Big1, ZeroHash256) + state.UpdateStateObject(stateObject) + stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(42)) + + snapshot := state.Snapshot() + + stateObject = state.GetStateObject([]byte("aa")) + stateObject.SetStorage(ethutil.Big("0"), ethutil.NewValue(43)) + + state.Revert(snapshot) + + stateObject = state.GetStateObject([]byte("aa")) + if !stateObject.GetStorage(ethutil.Big("0")).Cmp(ethutil.NewValue(42)) { + t.Error("Expected storage 0 to be 42") + } +} diff --git a/ethchain/transaction.go b/ethchain/transaction.go index bd7a0e424c..2c5615f991 100644 --- a/ethchain/transaction.go +++ b/ethchain/transaction.go @@ -1,6 +1,7 @@ package ethchain import ( + "fmt" "github.com/ethereum/eth-go/ethutil" "github.com/obscuren/secp256k1-go" "math/big" @@ -15,7 +16,6 @@ type Transaction struct { Gas *big.Int GasPrice *big.Int Data []byte - Init []byte v byte r, s []byte @@ -23,8 +23,8 @@ type Transaction struct { contractCreation bool } -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 NewContractCreationTx(value, gas, gasPrice *big.Int, script []byte) *Transaction { + return &Transaction{Value: value, Gas: gas, GasPrice: gasPrice, Data: script, contractCreation: true} } func NewTransactionMessage(to []byte, value, gas, gasPrice *big.Int, data []byte) *Transaction { @@ -46,19 +46,26 @@ func NewTransactionFromValue(val *ethutil.Value) *Transaction { } func (tx *Transaction) Hash() []byte { - data := []interface{}{tx.Nonce, tx.Value, tx.GasPrice, tx.Gas, tx.Recipient, tx.Data} + data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} - if tx.contractCreation { - data = append(data, tx.Init) - } + /* + if tx.contractCreation { + data = append(data, tx.Init) + } + */ return ethutil.Sha3Bin(ethutil.NewValue(data).Encode()) } -func (tx *Transaction) IsContract() bool { +func (tx *Transaction) CreatesContract() bool { return tx.contractCreation } +/* Depricated */ +func (tx *Transaction) IsContract() bool { + return tx.CreatesContract() +} + func (tx *Transaction) CreationAddress() []byte { return ethutil.Sha3Bin(ethutil.NewValue([]interface{}{tx.Sender(), tx.Nonce}).Encode())[12:] } @@ -112,10 +119,6 @@ func (tx *Transaction) Sign(privk []byte) error { func (tx *Transaction) RlpData() interface{} { data := []interface{}{tx.Nonce, tx.GasPrice, tx.Gas, tx.Recipient, tx.Value, tx.Data} - if tx.contractCreation { - data = append(data, tx.Init) - } - return append(data, tx.v, tx.r, tx.s) } @@ -138,18 +141,90 @@ func (tx *Transaction) RlpValueDecode(decoder *ethutil.Value) { tx.Recipient = decoder.Get(3).Bytes() tx.Value = decoder.Get(4).BigInt() tx.Data = decoder.Get(5).Bytes() - - // If the list is of length 10 it's a contract creation tx - if decoder.Len() == 10 { + tx.v = byte(decoder.Get(6).Uint()) + tx.r = decoder.Get(7).Bytes() + tx.s = decoder.Get(8).Bytes() + if len(tx.Recipient) == 0 { 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.v = byte(decoder.Get(6).Uint()) - tx.r = decoder.Get(7).Bytes() - tx.s = decoder.Get(8).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.v = byte(decoder.Get(6).Uint()) + tx.r = decoder.Get(7).Bytes() + tx.s = decoder.Get(8).Bytes() + } + */ +} + +func (tx *Transaction) String() string { + return fmt.Sprintf(` + TX(%x) + Contract: %v + From: %x + To: %x + Nonce: %v + GasPrice: %v + Gas: %v + Value: %v + Data: 0x%x + V: 0x%x + R: 0x%x + S: 0x%x + `, + tx.Hash(), + len(tx.Recipient) == 0, + tx.Sender(), + tx.Recipient, + tx.Nonce, + tx.GasPrice, + tx.Gas, + tx.Value, + tx.Data, + tx.v, + tx.r, + tx.s) +} + +type Receipt struct { + Tx *Transaction + PostState []byte + CumulativeGasUsed *big.Int +} + +func NewRecieptFromValue(val *ethutil.Value) *Receipt { + r := &Receipt{} + r.RlpValueDecode(val) + + return r +} + +func (self *Receipt) RlpValueDecode(decoder *ethutil.Value) { + self.Tx = NewTransactionFromValue(decoder.Get(0)) + self.PostState = decoder.Get(1).Bytes() + self.CumulativeGasUsed = decoder.Get(2).BigInt() +} + +func (self *Receipt) RlpData() interface{} { + return []interface{}{self.Tx.RlpData(), self.PostState, self.CumulativeGasUsed} +} + +func (self *Receipt) String() string { + return fmt.Sprintf(` + R + Tx:[ %v] + PostState: 0x%x + CumulativeGasUsed: %v + `, + self.Tx, + self.PostState, + self.CumulativeGasUsed) } diff --git a/ethchain/transaction_pool.go b/ethchain/transaction_pool.go index dcf0c4c31b..ee026ffdd9 100644 --- a/ethchain/transaction_pool.go +++ b/ethchain/transaction_pool.go @@ -91,15 +91,15 @@ 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) { +func (pool *TxPool) ProcessTransaction(tx *Transaction, state *State, toContract bool) (err error) { defer func() { if r := recover(); r != nil { - log.Println(r) + ethutil.Config.Log.Infoln(r) err = fmt.Errorf("%v", r) } }() // Get the sender - sender := block.state.GetAccount(tx.Sender()) + sender := 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) @@ -107,19 +107,21 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract // 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)) + //totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(TxFee, TxFeeRat)) + totAmount := new(big.Int).Add(tx.Value, new(big.Int).Mul(tx.Gas, tx.GasPrice)) if sender.Amount.Cmp(totAmount) < 0 { return fmt.Errorf("[TXPL] Insufficient amount in sender's (%x) account", tx.Sender()) } + //fmt.Println(tx) // 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.SubAmount(new(big.Int).Mul(TxFee, TxFeeRat)) + sender.SubAmount(new(big.Int).Mul(GasTx, tx.GasPrice)) } else { // Subtract the amount from the senders account sender.SubAmount(totAmount) @@ -127,10 +129,10 @@ func (pool *TxPool) ProcessTransaction(tx *Transaction, block *Block, toContract // Add the amount to receivers account which should conclude this transaction receiver.AddAmount(tx.Value) - block.state.UpdateStateObject(receiver) + state.UpdateStateObject(receiver) } - block.state.UpdateStateObject(sender) + state.UpdateStateObject(sender) ethutil.Config.Log.Infof("[TXPL] Processed Tx %x\n", tx.Hash()) diff --git a/ethchain/types.go b/ethchain/types.go index 9964bbe3bf..e0fdd5191c 100644 --- a/ethchain/types.go +++ b/ethchain/types.go @@ -322,3 +322,10 @@ func IsOpCode(s string) bool { } return false } + +func AppendScript(init, script []byte) []byte { + s := append(init, byte(oRETURN)) + s = append(s, script...) + + return s +} diff --git a/ethchain/vm.go b/ethchain/vm.go index d8254998e9..e025920f30 100644 --- a/ethchain/vm.go +++ b/ethchain/vm.go @@ -18,6 +18,7 @@ var ( GasCreate = big.NewInt(100) GasCall = big.NewInt(20) GasMemory = big.NewInt(1) + GasTx = big.NewInt(500) ) func CalculateTxGas(initSize, scriptSize *big.Int) *big.Int { @@ -425,6 +426,10 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro value := stack.Pop() size, offset := stack.Popn() + // Snapshot the current stack so we are able to + // revert back to it later. + snapshot := vm.state.Snapshot() + // Generate a new address addr := ethutil.CreateAddress(closure.callee.Address(), closure.callee.N()) // Create a new contract @@ -447,6 +452,9 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro closure.Script, err = closure.Call(vm, nil, hook) if err != nil { stack.Push(ethutil.BigFalse) + + // Revert the state as it was before. + vm.state.Revert(snapshot) } else { stack.Push(ethutil.BigD(addr)) @@ -472,6 +480,8 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro // Get the arguments from the memory args := mem.Get(inOffset.Int64(), inSize.Int64()) + snapshot := vm.state.Snapshot() + // Fetch the contract which will serve as the closure body contract := vm.state.GetStateObject(addr.Bytes()) @@ -494,14 +504,14 @@ func (vm *Vm) RunClosure(closure *Closure, hook DebugHook) (ret []byte, err erro if err != nil { stack.Push(ethutil.BigFalse) // Reset the changes applied this object - //contract.State().Reset() + vm.state.Revert(snapshot) } else { stack.Push(ethutil.BigTrue) + + vm.state.UpdateStateObject(contract) + + mem.Set(retOffset.Int64(), retSize.Int64(), ret) } - - vm.state.UpdateStateObject(contract) - - mem.Set(retOffset.Int64(), retSize.Int64(), ret) } else { ethutil.Config.Log.Debugf("Contract %x not found\n", addr.Bytes()) stack.Push(ethutil.BigFalse) diff --git a/ethchain/vm_test.go b/ethchain/vm_test.go index 2ec70536aa..520f9a2ed7 100644 --- a/ethchain/vm_test.go +++ b/ethchain/vm_test.go @@ -1,6 +1,5 @@ package ethchain -/* import ( _ "bytes" "fmt" @@ -13,60 +12,33 @@ import ( ) func TestRun4(t *testing.T) { - ethutil.ReadConfig("", ethutil.LogStd) + ethutil.ReadConfig("", ethutil.LogStd, "") db, _ := ethdb.NewMemDatabase() state := NewState(ethutil.NewTrie(db, "")) - script, err := mutan.Compile(strings.NewReader(` - int32 a = 10 - int32 b = 20 - if a > b { - int32 c = this.caller() - } - exit() - `), false) - tx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), script, nil) - tx.Sign(ContractAddr) - addr := tx.CreationAddress() - contract := MakeContract(tx, state) - state.UpdateStateObject(contract) - fmt.Printf("%x\n", addr) - callerScript, err := mutan.Compile(strings.NewReader(` - // Check if there's any cash in the initial store - if this.store[1000] == 0 { - this.store[1000] = 10**20 - } - - - this.store[1001] = this.value() * 20 - this.store[this.origin()] = this.store[this.origin()] + 1000 - - if this.store[1001] > 20 { - this.store[1001] = 10^50 - } - - int8 ret = 0 - int8 arg = 10 - 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 + this.store[this.origin()] = 10**20 + hello := "world" + + return lambda { + big to = this.data[0] + big from = this.origin() + big value = this.data[1] + + if this.store[from] >= value { + this.store[from] = this.store[from] - value + this.store[to] = this.store[to] + value } + } `), false) if err != nil { fmt.Println(err) } + fmt.Println(Disassemble(callerScript)) - callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript, nil) + callerTx := NewContractCreationTx(ethutil.Big("0"), ethutil.Big("1000"), ethutil.Big("100"), callerScript) + callerTx.Sign([]byte("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")) // Contract addr as test address gas := big.NewInt(1000) @@ -79,7 +51,7 @@ func TestRun4(t *testing.T) { fmt.Println(err) } fmt.Println("account.Amount =", account.Amount) - callerClosure := NewClosure(account, c, c.script, state, gas, gasPrice) + callerClosure := NewClosure(account, c, callerScript, state, gas, gasPrice) vm := NewVm(state, nil, RuntimeVars{ Origin: account.Address(), @@ -89,10 +61,10 @@ func TestRun4(t *testing.T) { Time: 1, Diff: big.NewInt(256), }) - _, e = callerClosure.Call(vm, nil, nil) + var ret []byte + ret, e = callerClosure.Call(vm, nil, nil) if e != nil { fmt.Println("error", e) } - fmt.Println("account.Amount =", account.Amount) + fmt.Println(ret) } -*/ diff --git a/ethminer/miner.go b/ethminer/miner.go index 166f7bc2f8..00e04cde2e 100644 --- a/ethminer/miner.go +++ b/ethminer/miner.go @@ -48,7 +48,7 @@ func NewDefaultMiner(coinbase []byte, ethereum ethchain.EthManager) Miner { // Insert initial TXs in our little miner 'pool' miner.txs = ethereum.TxPool().Flush() - miner.block = ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) + miner.block = ethereum.BlockChain().NewBlock(miner.coinbase) return miner } @@ -86,7 +86,7 @@ out: miner.txs = newtxs // Setup a fresh state to mine on - miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) + //miner.block = miner.ethereum.BlockChain().NewBlock(miner.coinbase, miner.txs) } else { if bytes.Compare(block.PrevHash, miner.ethereum.BlockChain().CurrentBlock.PrevHash) == 0 { @@ -125,7 +125,7 @@ func (self *Miner) Stop() { func (self *Miner) mineNewBlock() { stateManager := self.ethereum.StateManager() - self.block = self.ethereum.BlockChain().NewBlock(self.coinbase, self.txs) + self.block = self.ethereum.BlockChain().NewBlock(self.coinbase) // Apply uncles if len(self.uncles) > 0 { @@ -133,15 +133,10 @@ func (self *Miner) mineNewBlock() { } // Accumulate all valid transaction and apply them to the new state - var txs []*ethchain.Transaction - for _, tx := range self.txs { - if err := stateManager.ApplyTransaction(self.block.State(), self.block, tx); err == nil { - txs = append(txs, tx) - } - } + receipts, txs := stateManager.ApplyTransactions(self.block.State(), self.block, self.txs) self.txs = txs // Set the transactions to the block so the new SHA3 can be calculated - self.block.SetTransactions(self.txs) + self.block.SetReceipts(receipts, txs) // Accumulate the rewards included for this block stateManager.AccumelateRewards(self.block.State(), self.block) diff --git a/ethpub/pub.go b/ethpub/pub.go index cd002b5004..b75d3abc85 100644 --- a/ethpub/pub.go +++ b/ethpub/pub.go @@ -87,14 +87,14 @@ func (lib *PEthereum) SecretToAddress(key string) string { } func (lib *PEthereum) Transact(key, recipient, valueStr, gasStr, gasPriceStr, dataStr string) (*PReceipt, error) { - return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr, "") + return lib.createTx(key, recipient, valueStr, gasStr, gasPriceStr, dataStr) } -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) Create(key, valueStr, gasStr, gasPriceStr, script string) (*PReceipt, error) { + return lib.createTx(key, "", valueStr, gasStr, gasPriceStr, script) } -func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, initStr, scriptStr string) (*PReceipt, error) { +func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, scriptStr string) (*PReceipt, error) { var hash []byte var contractCreation bool if len(recipient) == 0 { @@ -121,33 +121,47 @@ func (lib *PEthereum) createTx(key, recipient, valueStr, gasStr, gasPriceStr, in var tx *ethchain.Transaction // Compile and assemble the given data if contractCreation { - var initScript, mainScript []byte + /* + var initScript, mainScript []byte + var err error + if ethutil.IsHex(initStr) { + initScript = ethutil.FromHex(initStr[2:]) + } else { + initScript, err = ethutil.Compile(initStr) + 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 + } + } + + script := ethchain.AppendScript(initScript, mainScript) + */ + var script []byte var err error - if ethutil.IsHex(initStr) { - initScript = ethutil.FromHex(initStr[2:]) - } else { - initScript, err = ethutil.Compile(initStr) - if err != nil { - return nil, err - } - } - if ethutil.IsHex(scriptStr) { - mainScript = ethutil.FromHex(scriptStr[2:]) + script = ethutil.FromHex(scriptStr) } else { - mainScript, err = ethutil.Compile(scriptStr) + script, err = ethutil.Compile(scriptStr) if err != nil { return nil, err } } - tx = ethchain.NewContractCreationTx(value, gas, gasPrice, mainScript, initScript) + tx = ethchain.NewContractCreationTx(value, gas, gasPrice, script) } else { // Just in case it was submitted as a 0x prefixed string - if len(initStr) > 0 && initStr[0:2] == "0x" { - initStr = initStr[2:len(initStr)] + if len(scriptStr) > 0 && scriptStr[0:2] == "0x" { + scriptStr = scriptStr[2:len(scriptStr)] } - tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, ethutil.FromHex(initStr)) + tx = ethchain.NewTransactionMessage(hash, value, gas, gasPrice, ethutil.FromHex(scriptStr)) } acc := lib.stateManager.TransState().GetStateObject(keyPair.Address()) diff --git a/ethpub/types.go b/ethpub/types.go index 7194de372e..e8a2164a78 100644 --- a/ethpub/types.go +++ b/ethpub/types.go @@ -31,7 +31,18 @@ func (self *PBlock) ToString() string { return "" } +func (self *PBlock) GetTransaction(hash string) *PTx { + tx := self.ref.GetTransaction(ethutil.FromHex(hash)) + if tx == nil { + return nil + } + + return NewPTx(tx) +} + type PTx struct { + ref *ethchain.Transaction + Value, Hash, Address string Contract bool } @@ -41,7 +52,11 @@ func NewPTx(tx *ethchain.Transaction) *PTx { sender := hex.EncodeToString(tx.Recipient) isContract := len(tx.Data) > 0 - return &PTx{Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender, Contract: isContract} + return &PTx{ref: tx, Hash: hash, Value: ethutil.CurrencyToString(tx.Value), Address: sender, Contract: isContract} +} + +func (self *PTx) ToString() string { + return self.ref.String() } type PKey struct { diff --git a/ethrpc/packages.go b/ethrpc/packages.go index 87cfc99b2d..1c4fb99f6f 100644 --- a/ethrpc/packages.go +++ b/ethrpc/packages.go @@ -137,7 +137,7 @@ func (p *EthereumApi) 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) + result, _ := p.ethp.Create(p.ethp.GetKey().PrivateKey, args.Value, args.Gas, args.GasPrice, args.Body) *reply = NewSuccessRes(result) return nil } diff --git a/ethutil/config.go b/ethutil/config.go index 1b41c46343..40ab3aa696 100644 --- a/ethutil/config.go +++ b/ethutil/config.go @@ -19,6 +19,7 @@ type config struct { Ver string ClientString string Pubkey []byte + Identifier string } var Config *config @@ -26,7 +27,7 @@ var Config *config // Read config // // Initialize the global Config variable with default settings -func ReadConfig(base string, logTypes LoggerType) *config { +func ReadConfig(base string, logTypes LoggerType, id string) *config { if Config == nil { usr, _ := user.Current() path := path.Join(usr.HomeDir, base) @@ -42,7 +43,8 @@ func ReadConfig(base string, logTypes LoggerType) *config { } } - Config = &config{ExecPath: path, Debug: true, Ver: "0.5.0 RC8"} + Config = &config{ExecPath: path, Debug: true, Ver: "0.5.0 RC9"} + Config.Identifier = id Config.Log = NewLogger(logTypes, LogLevelDebug) Config.SetClientString("/Ethereum(G)") } @@ -53,7 +55,11 @@ func ReadConfig(base string, logTypes LoggerType) *config { // Set client string // func (c *config) SetClientString(str string) { - Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, runtime.GOOS) + id := runtime.GOOS + if len(c.Identifier) > 0 { + id = c.Identifier + } + Config.ClientString = fmt.Sprintf("%s nv%s/%s", str, c.Ver, id) } type LoggerType byte diff --git a/ethutil/trie_test.go b/ethutil/trie_test.go index 0be512d9fc..d74d129ac3 100644 --- a/ethutil/trie_test.go +++ b/ethutil/trie_test.go @@ -1,7 +1,7 @@ package ethutil import ( - _ "fmt" + "fmt" "reflect" "testing" ) @@ -26,7 +26,6 @@ func (db *MemDatabase) Delete(key []byte) error { delete(db.db, string(key)) return nil } -func (db *MemDatabase) GetKeys() []*Key { return nil } func (db *MemDatabase) Print() {} func (db *MemDatabase) Close() {} func (db *MemDatabase) LastKnownTD() []byte { return nil } @@ -171,3 +170,17 @@ func TestTrieIterator(t *testing.T) { t.Errorf("Expected cached nodes to be deleted") } } + +func TestHashes(t *testing.T) { + _, trie := New() + trie.Update("cat", "dog") + trie.Update("ca", "dude") + trie.Update("doge", "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ") + trie.Update("dog", "test") + trie.Update("test", "1234567890abcdefghijklmnopqrstuvwxxzABCEFGHIJKLMNOPQRSTUVWXYZ") + fmt.Printf("%x\n", trie.Root) + trie.Delete("dog") + fmt.Printf("%x\n", trie.Root) + trie.Delete("test") + fmt.Printf("%x\n", trie.Root) +} diff --git a/ethutil/value.go b/ethutil/value.go index b7756f9b16..83600abc2c 100644 --- a/ethutil/value.go +++ b/ethutil/value.go @@ -16,7 +16,7 @@ type Value struct { } func (val *Value) String() string { - return fmt.Sprintf("%q", val.Val) + return fmt.Sprintf("%x", val.Val) } func NewValue(val interface{}) *Value { diff --git a/peer.go b/peer.go index 7f81489b60..7e505d6800 100644 --- a/peer.go +++ b/peer.go @@ -18,7 +18,7 @@ const ( // The size of the output buffer for writing messages outputBufferSize = 50 // Current protocol version - ProtocolVersion = 12 + ProtocolVersion = 17 ) type DiscReason byte @@ -119,7 +119,7 @@ type Peer struct { // this to prevent receiving false peers. requestedPeerList bool - host []interface{} + host []byte port uint16 caps Caps @@ -134,8 +134,7 @@ type Peer struct { } func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer { - data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) - pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes() + pubkey := ethutil.GetKeyRing().Get(0).PublicKey[1:] return &Peer{ outputQueue: make(chan *ethwire.Msg, outputBufferSize), @@ -342,6 +341,7 @@ 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.Debugln(block) } break } else { @@ -437,7 +437,7 @@ func (p *Peer) HandleInbound() { // If a parent is found send back a reply if parent != nil { - ethutil.Config.Log.Debugf("[PEER] Found conical block, returning chain from: %x ", parent.Hash()) + ethutil.Config.Log.Debugf("[PEER] Found canonical block, returning chain from: %x ", parent.Hash()) chain := p.ethereum.BlockChain().GetChainFromHash(parent.Hash(), amountOfBlocks) if len(chain) > 0 { ethutil.Config.Log.Debugf("[PEER] Returning %d blocks: %x ", len(chain), parent.Hash()) @@ -531,11 +531,10 @@ func (p *Peer) Stop() { } func (p *Peer) pushHandshake() error { - data, _ := ethutil.Config.Db.Get([]byte("KeyRing")) - pubkey := ethutil.NewValueFromBytes(data).Get(2).Bytes() + pubkey := ethutil.GetKeyRing().Get(0).PublicKey msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{ - uint32(ProtocolVersion), uint32(0), p.Version, byte(p.caps), p.port, pubkey, + uint32(ProtocolVersion), uint32(0), p.Version, byte(p.caps), p.port, pubkey[1:], }) p.QueueMessage(msg) @@ -667,23 +666,24 @@ func (p *Peer) RlpData() []interface{} { return []interface{}{p.host, p.port, p.pubkey} } -func packAddr(address, port string) ([]interface{}, uint16) { +func packAddr(address, port string) ([]byte, 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)} + host := []byte{byte(a), byte(b), byte(c), byte(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())) + byts := value.Bytes() + a := strconv.Itoa(int(byts[0])) + b := strconv.Itoa(int(byts[1])) + c := strconv.Itoa(int(byts[2])) + d := strconv.Itoa(int(byts[3])) host := strings.Join([]string{a, b, c, d}, ".") port := strconv.Itoa(int(p))