From 230aafbf66ba747fb3796810adf3b1680f368e73 Mon Sep 17 00:00:00 2001 From: obscuren Date: Thu, 22 May 2014 17:35:26 +0200 Subject: [PATCH] Working on interop * Receipts after each transaction * Fee structure * Applying fees to miners --- ethchain/block.go | 74 +++++++++++++++---------- ethchain/block_chain.go | 21 +++---- ethchain/genesis.go | 2 +- ethchain/state_manager.go | 42 +++++++++++--- ethchain/state_object.go | 2 +- ethchain/transaction.go | 104 +++++++++++++++++++++++++++++------ ethchain/transaction_pool.go | 18 +++--- ethchain/vm.go | 1 + ethminer/miner.go | 15 ++--- ethutil/config.go | 10 +++- ethutil/value.go | 2 +- 11 files changed, 203 insertions(+), 88 deletions(-) diff --git a/ethchain/block.go b/ethchain/block.go index 3401632b1..c846516f4 100644 --- a/ethchain/block.go +++ b/ethchain/block.go @@ -55,6 +55,7 @@ type Block struct { Nonce []byte // List of transactions and/or contracts transactions []*Transaction + receipts []*Receipt TxSha []byte } @@ -84,24 +85,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 +112,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 { @@ -172,15 +172,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 +201,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 +226,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 +250,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 +261,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 +306,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 +357,7 @@ func (block *Block) String() string { Time: %v Extra: %v Nonce: %x + NumTx: %v `, block.Hash(), block.PrevHash, @@ -360,5 +372,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 b72c78cdb..b45d254b5 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/genesis.go b/ethchain/genesis.go index b8f9f865a..359c47c26 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_manager.go b/ethchain/state_manager.go index 8b56d65bb..3b67381ea 100644 --- a/ethchain/state_manager.go +++ b/ethchain/state_manager.go @@ -97,40 +97,56 @@ 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 { +func (sm *StateManager) ApplyTransaction(state *State, block *Block, tx *Transaction) (*big.Int, 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. + totalGasUsed := big.NewInt(0) if tx.IsContract() { - err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) + err := sm.Ethereum.TxPool().ProcessTransaction(tx, state, false) if err == nil { contract := sm.MakeContract(state, tx) if contract != nil { sm.EvalScript(state, contract.Init(), contract, tx, block) } 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 create:", err) } } else { - err := sm.Ethereum.TxPool().ProcessTransaction(tx, block, false) + err := sm.Ethereum.TxPool().ProcessTransaction(tx, state, false) contract := state.GetStateObject(tx.Recipient) if err == nil && contract != nil && len(contract.Script()) > 0 { sm.EvalScript(state, contract.Script(), contract, 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 +292,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) } diff --git a/ethchain/state_object.go b/ethchain/state_object.go index 8e059f334..4046054db 100644 --- a/ethchain/state_object.go +++ b/ethchain/state_object.go @@ -146,7 +146,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/transaction.go b/ethchain/transaction.go index bd7a0e424..16ea312c0 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" @@ -46,11 +47,13 @@ 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()) } @@ -138,18 +141,87 @@ 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() + 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() + /* + // 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() - } + 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 + Nonce: %v + GasPrice: %v + Gas: %v + To: %x + Value: %v + Data: 0x%x + V: 0x%x + R: 0x%x + S: 0x%x + `, + tx.Hash(), + len(tx.Recipient) > 1, + tx.Sender(), + tx.Nonce, + tx.GasPrice, + tx.Gas, + tx.Recipient, + 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 dcf0c4c31..ee026ffdd 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/vm.go b/ethchain/vm.go index d8254998e..e067a9c96 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 { diff --git a/ethminer/miner.go b/ethminer/miner.go index 166f7bc2f..00e04cde2 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/ethutil/config.go b/ethutil/config.go index 1b41c4634..e6cbde4b6 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) @@ -43,6 +44,7 @@ func ReadConfig(base string, logTypes LoggerType) *config { } Config = &config{ExecPath: path, Debug: true, Ver: "0.5.0 RC8"} + 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/value.go b/ethutil/value.go index b7756f9b1..83600abc2 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 {