From e3253b5d5e65bfb6944ddaabd3c79400fbe06ef8 Mon Sep 17 00:00:00 2001
From: obscuren <geffobscura@gmail.com>
Date: Fri, 22 May 2015 22:44:51 +0200
Subject: core: fixed an issue with storing receipts

---
 cmd/mist/assets/examples/coin.html |  2 +-
 core/block_processor.go            | 73 +++++++++++++++++++++++++-------------
 core/block_processor_test.go       | 32 +++++++++++++++++
 core/state/log.go                  | 21 +++++++----
 core/types/receipt.go              | 29 +++++++++++++++
 5 files changed, 124 insertions(+), 33 deletions(-)

diff --git a/cmd/mist/assets/examples/coin.html b/cmd/mist/assets/examples/coin.html
index e6baf4579..4fe8e7fa2 100644
--- a/cmd/mist/assets/examples/coin.html
+++ b/cmd/mist/assets/examples/coin.html
@@ -102,7 +102,7 @@ window.filter = filter;
 		var amount = parseInt( value.value );
 		console.log("transact: ", to.value, " => ", amount)
 
-		contract.sendTransaction({from: eth.accounts[0]}).send( to.value, amount );
+		contract.send.sendTransaction(to.value, amount ,{from: eth.accounts[0]});
 
 		to.value = "";
 		value.value = "";
diff --git a/core/block_processor.go b/core/block_processor.go
index 6cd1c8aa3..ca205ee86 100644
--- a/core/block_processor.go
+++ b/core/block_processor.go
@@ -40,11 +40,6 @@ type BlockProcessor struct {
 
 	txpool *TxPool
 
-	// The last attempted block is mainly used for debugging purposes
-	// This does not have to be a valid block and will be set during
-	// 'Process' & canonical validation.
-	lastAttemptedBlock *types.Block
-
 	events event.Subscription
 
 	eventMux *event.TypeMux
@@ -188,8 +183,6 @@ func (sm *BlockProcessor) Process(block *types.Block) (logs state.Logs, err erro
 }
 
 func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs state.Logs, err error) {
-	sm.lastAttemptedBlock = block
-
 	// Create a new state based on the parent's root (e.g., create copy)
 	state := state.New(parent.Root(), sm.db)
 
@@ -255,6 +248,12 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
 		return
 	}
 
+	// store the receipts
+	err = putReceipts(sm.extraDb, block.Hash(), receipts)
+	if err != nil {
+		return nil, err
+	}
+
 	// Calculate the td for this block
 	//td = CalculateTD(block, parent)
 	// Sync the current block's state to the database
@@ -268,23 +267,9 @@ func (sm *BlockProcessor) processWithParent(block, parent *types.Block) (logs st
 		putTx(sm.extraDb, tx, block, uint64(i))
 	}
 
-	receiptsRlp := block.Receipts().RlpEncode()
-	sm.extraDb.Put(append(receiptsPre, block.Hash().Bytes()...), receiptsRlp)
-
 	return state.Logs(), nil
 }
 
-func (self *BlockProcessor) GetBlockReceipts(bhash common.Hash) (receipts types.Receipts, err error) {
-	var rdata []byte
-	rdata, err = self.extraDb.Get(append(receiptsPre, bhash[:]...))
-
-	if err == nil {
-		err = rlp.DecodeBytes(rdata, &receipts)
-	}
-	return
-
-}
-
 // See YP section 4.3.4. "Block Header Validity"
 // Validates a block. Returns an error if the block is invalid.
 func (sm *BlockProcessor) ValidateHeader(block, parent *types.Header, checkPow bool) error {
@@ -391,13 +376,25 @@ func (sm *BlockProcessor) VerifyUncles(statedb *state.StateDB, block, parent *ty
 	return nil
 }
 
+// GetBlockReceipts returns the receipts beloniging to the block hash
+func (sm *BlockProcessor) GetBlockReceipts(bhash common.Hash) (receipts types.Receipts, err error) {
+	return getBlockReceipts(sm.extraDb, bhash)
+}
+
+// GetLogs returns the logs of the given block. This method is using a two step approach
+// where it tries to get it from the (updated) method which gets them from the receipts or
+// the depricated way by re-processing the block.
 func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err error) {
-	if !sm.bc.HasBlock(block.Header().ParentHash) {
-		return nil, ParentError(block.Header().ParentHash)
+	receipts, err := sm.GetBlockReceipts(block.Hash())
+	if err == nil && len(receipts) > 0 {
+		// coalesce logs
+		for _, receipt := range receipts {
+			logs = append(logs, receipt.Logs()...)
+		}
+		return
 	}
 
-	sm.lastAttemptedBlock = block
-
+	// TODO: remove backward compatibility
 	var (
 		parent = sm.bc.GetBlock(block.Header().ParentHash)
 		state  = state.New(parent.Root(), sm.db)
@@ -408,6 +405,16 @@ func (sm *BlockProcessor) GetLogs(block *types.Block) (logs state.Logs, err erro
 	return state.Logs(), nil
 }
 
+func getBlockReceipts(db common.Database, bhash common.Hash) (receipts types.Receipts, err error) {
+	var rdata []byte
+	rdata, err = db.Get(append(receiptsPre, bhash[:]...))
+
+	if err == nil {
+		err = rlp.DecodeBytes(rdata, &receipts)
+	}
+	return
+}
+
 func putTx(db common.Database, tx *types.Transaction, block *types.Block, i uint64) {
 	rlpEnc, err := rlp.EncodeToBytes(tx)
 	if err != nil {
@@ -431,3 +438,19 @@ func putTx(db common.Database, tx *types.Transaction, block *types.Block, i uint
 	}
 	db.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta)
 }
+
+func putReceipts(db common.Database, hash common.Hash, receipts types.Receipts) error {
+	storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
+	for i, receipt := range receipts {
+		storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
+	}
+
+	bytes, err := rlp.EncodeToBytes(storageReceipts)
+	if err != nil {
+		return err
+	}
+
+	db.Put(append(receiptsPre, hash[:]...), bytes)
+
+	return nil
+}
diff --git a/core/block_processor_test.go b/core/block_processor_test.go
index e0aa5fb4c..72b173a71 100644
--- a/core/block_processor_test.go
+++ b/core/block_processor_test.go
@@ -5,6 +5,8 @@ import (
 	"testing"
 
 	"github.com/ethereum/go-ethereum/common"
+	"github.com/ethereum/go-ethereum/core/state"
+	"github.com/ethereum/go-ethereum/core/types"
 	"github.com/ethereum/go-ethereum/ethdb"
 	"github.com/ethereum/go-ethereum/event"
 	"github.com/ethereum/go-ethereum/pow/ezp"
@@ -35,3 +37,33 @@ func TestNumber(t *testing.T) {
 		t.Errorf("didn't expect block number error")
 	}
 }
+
+func TestPutReceipt(t *testing.T) {
+	db, _ := ethdb.NewMemDatabase()
+
+	var addr common.Address
+	addr[0] = 1
+	var hash common.Hash
+	hash[0] = 2
+
+	receipt := new(types.Receipt)
+	receipt.SetLogs(state.Logs{&state.Log{
+		Address:   addr,
+		Topics:    []common.Hash{hash},
+		Data:      []byte("hi"),
+		Number:    42,
+		TxHash:    hash,
+		TxIndex:   0,
+		BlockHash: hash,
+		Index:     0,
+	}})
+
+	putReceipts(db, hash, types.Receipts{receipt})
+	receipts, err := getBlockReceipts(db, hash)
+	if err != nil {
+		t.Error("got err:", err)
+	}
+	if len(receipts) != 1 {
+		t.Error("expected to get 1 receipt, got", len(receipts))
+	}
+}
diff --git a/core/state/log.go b/core/state/log.go
index a7aa784e2..882977061 100644
--- a/core/state/log.go
+++ b/core/state/log.go
@@ -29,15 +29,22 @@ func (self *Log) EncodeRLP(w io.Writer) error {
 }
 
 func (self *Log) String() string {
-	return fmt.Sprintf(`log: %x %x %x`, self.Address, self.Topics, self.Data)
+	return fmt.Sprintf(`log: %x %x %x %x %d %x %d`, self.Address, self.Topics, self.Data, self.TxHash, self.TxIndex, self.BlockHash, self.Index)
 }
 
 type Logs []*Log
 
-func (self Logs) String() (ret string) {
-	for _, log := range self {
-		ret += fmt.Sprintf("%v", log)
-	}
-
-	return "[" + ret + "]"
+type LogForStorage Log
+
+func (self *LogForStorage) EncodeRLP(w io.Writer) error {
+	return rlp.Encode(w, []interface{}{
+		self.Address,
+		self.Topics,
+		self.Data,
+		self.Number,
+		self.TxHash,
+		self.TxIndex,
+		self.BlockHash,
+		self.Index,
+	})
 }
diff --git a/core/types/receipt.go b/core/types/receipt.go
index 414e4d364..6b4024ada 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -26,10 +26,39 @@ func (self *Receipt) SetLogs(logs state.Logs) {
 	self.logs = logs
 }
 
+func (self *Receipt) Logs() state.Logs {
+	return self.logs
+}
+
 func (self *Receipt) EncodeRLP(w io.Writer) error {
 	return rlp.Encode(w, []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs})
 }
 
+func (self *Receipt) DecodeRLP(s *rlp.Stream) error {
+	var r struct {
+		PostState         []byte
+		CumulativeGasUsed *big.Int
+		Bloom             Bloom
+		Logs              state.Logs
+	}
+	if err := s.Decode(&r); err != nil {
+		return err
+	}
+	self.PostState, self.CumulativeGasUsed, self.Bloom, self.logs = r.PostState, r.CumulativeGasUsed, r.Bloom, r.Logs
+
+	return nil
+}
+
+type ReceiptForStorage Receipt
+
+func (self *ReceiptForStorage) EncodeRLP(w io.Writer) error {
+	storageLogs := make([]*state.LogForStorage, len(self.logs))
+	for i, log := range self.logs {
+		storageLogs[i] = (*state.LogForStorage)(log)
+	}
+	return rlp.Encode(w, []interface{}{self.PostState, self.CumulativeGasUsed, self.Bloom, storageLogs})
+}
+
 func (self *Receipt) RlpEncode() []byte {
 	bytes, err := rlp.EncodeToBytes(self)
 	if err != nil {
-- 
cgit v1.2.3