From 1c872ddf4b1db51847a5d9d020e13d432e847f52 Mon Sep 17 00:00:00 2001 From: obscuren Date: Wed, 8 Apr 2015 17:14:58 +0200 Subject: [PATCH] Changed how logs are being recorded Logs are now recorded per transactions instead of tossing them out after each transaction. This should also fix an issue with `eth_getFilterLogs` (#629) Also now implemented are the `transactionHash, blockHash, transactionIndex, logIndex` on logs. Closes #654. --- core/block_processor.go | 11 ++++-- core/filter.go | 6 +-- core/state/log.go | 88 ++++++++--------------------------------- core/state/statedb.go | 42 +++++++++++++++----- core/types/bloom9.go | 8 ++-- core/types/receipt.go | 19 +-------- core/vm/environment.go | 2 +- core/vm/vm.go | 3 +- core/vm_env.go | 2 +- miner/worker.go | 2 + rpc/responses.go | 16 +++++--- xeth/xeth.go | 6 +-- 12 files changed, 83 insertions(+), 122 deletions(-) diff --git a/core/block_processor.go b/core/block_processor.go index 39134c63e..7aded346a 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -73,7 +73,7 @@ func (sm *BlockProcessor) TransitionState(statedb *state.StateDB, parent, block func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, statedb *state.StateDB, block *types.Block, tx *types.Transaction, usedGas *big.Int, transientProcess bool) (*types.Receipt, *big.Int, error) { // If we are mining this block and validating we want to set the logs back to 0 - statedb.EmptyLogs() + //statedb.EmptyLogs() cb := statedb.GetStateObject(coinbase.Address()) _, gas, err := ApplyMessage(NewEnv(statedb, self.bc, tx, block), tx, cb) @@ -89,7 +89,9 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated cumulative := new(big.Int).Set(usedGas.Add(usedGas, gas)) receipt := types.NewReceipt(statedb.Root().Bytes(), cumulative) - receipt.SetLogs(statedb.Logs()) + + logs := statedb.GetLogs(tx.Hash()) + receipt.SetLogs(logs) receipt.Bloom = types.CreateBloom(types.Receipts{receipt}) glog.V(logger.Debug).Infoln(receipt) @@ -97,7 +99,6 @@ func (self *BlockProcessor) ApplyTransaction(coinbase *state.StateObject, stated // Notify all subscribers if !transientProcess { go self.eventMux.Post(TxPostEvent{tx}) - logs := statedb.Logs() go self.eventMux.Post(logs) } @@ -115,7 +116,9 @@ func (self *BlockProcessor) ApplyTransactions(coinbase *state.StateObject, state cumulativeSum = new(big.Int) ) - for _, tx := range txs { + for i, tx := range txs { + statedb.StartRecord(tx.Hash(), block.Hash(), i) + receipt, txGas, err := self.ApplyTransaction(coinbase, statedb, block, tx, totalUsedGas, transientProcess) if err != nil && (IsNonceErr(err) || state.IsGasLimitErr(err) || IsInvalidTxErr(err)) { return nil, err diff --git a/core/filter.go b/core/filter.go index 1dca5501d..dd15db27d 100644 --- a/core/filter.go +++ b/core/filter.go @@ -124,17 +124,17 @@ func (self *Filter) FilterLogs(logs state.Logs) state.Logs { // Filter the logs for interesting stuff Logs: for _, log := range logs { - if len(self.address) > 0 && !includes(self.address, log.Address()) { + if len(self.address) > 0 && !includes(self.address, log.Address) { continue } logTopics := make([]common.Hash, len(self.topics)) - copy(logTopics, log.Topics()) + copy(logTopics, log.Topics) for i, topics := range self.topics { for _, topic := range topics { var match bool - if log.Topics()[i] == topic { + if log.Topics[i] == topic { match = true } if !match { diff --git a/core/state/log.go b/core/state/log.go index f8aa4c08c..a7aa784e2 100644 --- a/core/state/log.go +++ b/core/state/log.go @@ -8,87 +8,31 @@ import ( "github.com/ethereum/go-ethereum/rlp" ) -type Log interface { - Address() common.Address - Topics() []common.Hash - Data() []byte +type Log struct { + Address common.Address + Topics []common.Hash + Data []byte + Number uint64 - Number() uint64 + TxHash common.Hash + TxIndex uint + BlockHash common.Hash + Index uint } -type StateLog struct { - address common.Address - topics []common.Hash - data []byte - number uint64 +func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log { + return &Log{Address: address, Topics: topics, Data: data, Number: number} } -func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *StateLog { - return &StateLog{address, topics, data, number} +func (self *Log) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, []interface{}{self.Address, self.Topics, self.Data}) } -func (self *StateLog) Address() common.Address { - return self.address +func (self *Log) String() string { + return fmt.Sprintf(`log: %x %x %x`, self.Address, self.Topics, self.Data) } -func (self *StateLog) Topics() []common.Hash { - return self.topics -} - -func (self *StateLog) Data() []byte { - return self.data -} - -func (self *StateLog) Number() uint64 { - return self.number -} - -/* -func NewLogFromValue(decoder *common.Value) *StateLog { - var extlog struct { - - } - - log := &StateLog{ - address: decoder.Get(0).Bytes(), - data: decoder.Get(2).Bytes(), - } - - it := decoder.Get(1).NewIterator() - for it.Next() { - log.topics = append(log.topics, it.Value().Bytes()) - } - - return log -} -*/ - -func (self *StateLog) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, []interface{}{self.address, self.topics, self.data}) -} - -/* -func (self *StateLog) RlpData() interface{} { - return []interface{}{self.address, common.ByteSliceToInterface(self.topics), self.data} -} -*/ - -func (self *StateLog) String() string { - return fmt.Sprintf(`log: %x %x %x`, self.address, self.topics, self.data) -} - -type Logs []Log - -/* -func (self Logs) RlpData() interface{} { - data := make([]interface{}, len(self)) - for i, log := range self { - data[i] = log.RlpData() - } - - return data -} -*/ +type Logs []*Log func (self Logs) String() (ret string) { for _, log := range self { diff --git a/core/state/statedb.go b/core/state/statedb.go index 065cbd607..0651365f0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -23,30 +23,53 @@ type StateDB struct { refund map[string]*big.Int - logs Logs + thash, bhash common.Hash + txIndex int + logs map[common.Hash]Logs } // Create a new state from a given trie func New(root common.Hash, db common.Database) *StateDB { trie := trie.NewSecure(root[:], db) - return &StateDB{db: db, trie: trie, stateObjects: make(map[string]*StateObject), refund: make(map[string]*big.Int)} + return &StateDB{db: db, trie: trie, stateObjects: make(map[string]*StateObject), refund: make(map[string]*big.Int), logs: make(map[common.Hash]Logs)} } func (self *StateDB) PrintRoot() { self.trie.Trie.PrintRoot() } -func (self *StateDB) EmptyLogs() { - self.logs = nil +func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) { + self.thash = thash + self.bhash = bhash + self.txIndex = ti } -func (self *StateDB) AddLog(log Log) { - self.logs = append(self.logs, log) +func (self *StateDB) AddLog(log *Log) { + log.TxHash = self.thash + log.BlockHash = self.bhash + log.TxIndex = uint(self.txIndex) + self.logs[self.thash] = append(self.logs[self.thash], log) +} + +func (self *StateDB) GetLogs(hash common.Hash) Logs { + return self.logs[hash] } func (self *StateDB) Logs() Logs { + var logs Logs + for _, lgs := range self.logs { + logs = append(logs, lgs...) + } + return logs +} + +/* +func (self *StateDB) Logs(txHash, blockHash common.Hash, txIndex uint) Logs { + self.logs.SetInfo(txHash, blockHash, txIndex) + return self.logs } +*/ func (self *StateDB) Refund(address common.Address, gas *big.Int) { addr := address.Str() @@ -253,9 +276,10 @@ func (self *StateDB) Copy() *StateDB { state.refund[addr] = new(big.Int).Set(refund) } - logs := make(Logs, len(self.logs)) - copy(logs, self.logs) - state.logs = logs + for hash, logs := range self.logs { + state.logs[hash] = make(Logs, len(logs)) + copy(state.logs[hash], logs) + } return state } diff --git a/core/types/bloom9.go b/core/types/bloom9.go index af90679ce..0d37cb19f 100644 --- a/core/types/bloom9.go +++ b/core/types/bloom9.go @@ -4,8 +4,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" ) func CreateBloom(receipts Receipts) Bloom { @@ -20,10 +20,10 @@ func CreateBloom(receipts Receipts) Bloom { func LogsBloom(logs state.Logs) *big.Int { bin := new(big.Int) for _, log := range logs { - data := make([]common.Hash, len(log.Topics())) - bin.Or(bin, bloom9(log.Address().Bytes())) + data := make([]common.Hash, len(log.Topics)) + bin.Or(bin, bloom9(log.Address.Bytes())) - for i, topic := range log.Topics() { + for i, topic := range log.Topics { data[i] = topic } diff --git a/core/types/receipt.go b/core/types/receipt.go index 83c981f93..414e4d364 100644 --- a/core/types/receipt.go +++ b/core/types/receipt.go @@ -7,8 +7,8 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/rlp" ) type Receipt struct { @@ -30,12 +30,6 @@ func (self *Receipt) EncodeRLP(w io.Writer) error { return rlp.Encode(w, []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs}) } -/* -func (self *Receipt) RlpData() interface{} { - return []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs.RlpData()} -} -*/ - func (self *Receipt) RlpEncode() []byte { bytes, err := rlp.EncodeToBytes(self) if err != nil { @@ -58,17 +52,6 @@ func (self *Receipt) String() string { type Receipts []*Receipt -/* -func (self Receipts) RlpData() interface{} { - data := make([]interface{}, len(self)) - for i, receipt := range self { - data[i] = receipt.RlpData() - } - - return data -} -*/ - func (self Receipts) RlpEncode() []byte { bytes, err := rlp.EncodeToBytes(self) if err != nil { diff --git a/core/vm/environment.go b/core/vm/environment.go index 72e18c353..cc9570fc8 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -22,7 +22,7 @@ type Environment interface { Difficulty() *big.Int GasLimit() *big.Int Transfer(from, to Account, amount *big.Int) error - AddLog(state.Log) + AddLog(*state.Log) VmType() Type diff --git a/core/vm/vm.go b/core/vm/vm.go index 118f60076..927b67293 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -557,7 +557,8 @@ func (self *Vm) Run(context *Context, callData []byte) (ret []byte, err error) { } data := mem.Get(mStart.Int64(), mSize.Int64()) - log := &Log{context.Address(), topics, data, self.env.BlockNumber().Uint64()} + log := state.NewLog(context.Address(), topics, data, self.env.BlockNumber().Uint64()) + //log := &Log{context.Address(), topics, data, self.env.BlockNumber().Uint64()} self.env.AddLog(log) self.Printf(" => %v", log) diff --git a/core/vm_env.go b/core/vm_env.go index 6a604fccd..c439d2946 100644 --- a/core/vm_env.go +++ b/core/vm_env.go @@ -47,7 +47,7 @@ func (self *VMEnv) GetHash(n uint64) common.Hash { return common.Hash{} } -func (self *VMEnv) AddLog(log state.Log) { +func (self *VMEnv) AddLog(log *state.Log) { self.state.AddLog(log) } func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) error { diff --git a/miner/worker.go b/miner/worker.go index 7f2728f9c..25ea95347 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -266,6 +266,8 @@ func (self *worker) commitNewWork() { ) gasLimit: for i, tx := range transactions { + self.current.state.StartRecord(tx.Hash(), common.Hash{}, 0) + err := self.commitTransaction(tx) switch { case core.IsNonceErr(err) || core.IsInvalidTxErr(err): diff --git a/rpc/responses.go b/rpc/responses.go index 52a2f714c..3620f643e 100644 --- a/rpc/responses.go +++ b/rpc/responses.go @@ -284,15 +284,19 @@ type LogRes struct { TransactionIndex *hexnum `json:"transactionIndex"` } -func NewLogRes(log state.Log) LogRes { +func NewLogRes(log *state.Log) LogRes { var l LogRes - l.Topics = make([]*hexdata, len(log.Topics())) - for j, topic := range log.Topics() { + l.Topics = make([]*hexdata, len(log.Topics)) + for j, topic := range log.Topics { l.Topics[j] = newHexData(topic) } - l.Address = newHexData(log.Address()) - l.Data = newHexData(log.Data()) - l.BlockNumber = newHexNum(log.Number()) + l.Address = newHexData(log.Address) + l.Data = newHexData(log.Data) + l.BlockNumber = newHexNum(log.Number) + l.LogIndex = newHexNum(log.Index) + l.TransactionHash = newHexData(log.TxHash) + l.TransactionIndex = newHexNum(log.TxIndex) + l.BlockHash = newHexData(log.BlockHash) return l } diff --git a/xeth/xeth.go b/xeth/xeth.go index b8d9ecb08..94014763a 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -393,7 +393,7 @@ func (self *XEth) NewFilterString(word string) int { self.logMut.Lock() defer self.logMut.Unlock() - self.logs[id].add(&state.StateLog{}) + self.logs[id].add(&state.Log{}) } case "latest": filter.BlockCallback = func(block *types.Block, logs state.Logs) { @@ -403,7 +403,7 @@ func (self *XEth) NewFilterString(word string) int { for _, log := range logs { self.logs[id].add(log) } - self.logs[id].add(&state.StateLog{}) + self.logs[id].add(&state.Log{}) } } @@ -729,7 +729,7 @@ type logFilter struct { id int } -func (l *logFilter) add(logs ...state.Log) { +func (l *logFilter) add(logs ...*state.Log) { l.logs = append(l.logs, logs...) }