core, core/types: readd transactions after chain re-org
Added a `Difference` method to `types.Transactions` which sets the receiver to the difference of a to b (NOTE: not a **and** b). Transaction pool subscribes to RemovedTransactionEvent adding back to those potential missing from the chain. When a chain re-org occurs remove any transactions that were removed from the canonical chain during the re-org as well as the receipts that were generated in the process. Closes #1746
This commit is contained in:
		
							parent
							
								
									12c0afe4fe
								
							
						
					
					
						commit
						eaa4473dbd
					
				| @ -92,7 +92,7 @@ func testREPL(t *testing.T, config func(*eth.Config)) (string, *testjethre, *eth | ||||
| 
 | ||||
| 	db, _ := ethdb.NewMemDatabase() | ||||
| 
 | ||||
| 	core.WriteGenesisBlockForTesting(db, common.HexToAddress(testAddress), common.String2Big(testBalance)) | ||||
| 	core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)}) | ||||
| 	ks := crypto.NewKeyStorePlain(filepath.Join(tmp, "keystore")) | ||||
| 	am := accounts.NewManager(ks) | ||||
| 	conf := ð.Config{ | ||||
|  | ||||
| @ -134,7 +134,7 @@ func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) { | ||||
| 
 | ||||
| 	db, _ := ethdb.NewMemDatabase() | ||||
| 	// set up mock genesis with balance on the testAddress
 | ||||
| 	core.WriteGenesisBlockForTesting(db, common.HexToAddress(testAddress), common.String2Big(testBalance)) | ||||
| 	core.WriteGenesisBlockForTesting(db, core.GenesisAccount{common.HexToAddress(testAddress), common.String2Big(testBalance)}) | ||||
| 
 | ||||
| 	// only use minimalistic stack with no networking
 | ||||
| 	ethereum, err = eth.New(ð.Config{ | ||||
|  | ||||
| @ -162,7 +162,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) { | ||||
| 
 | ||||
| 	// Generate a chain of b.N blocks using the supplied block
 | ||||
| 	// generator function.
 | ||||
| 	genesis := WriteGenesisBlockForTesting(db, benchRootAddr, benchRootFunds) | ||||
| 	genesis := WriteGenesisBlockForTesting(db, GenesisAccount{benchRootAddr, benchRootFunds}) | ||||
| 	chain := GenerateChain(genesis, db, b.N, gen) | ||||
| 
 | ||||
| 	// Time the insertion of the new chain.
 | ||||
|  | ||||
| @ -42,7 +42,7 @@ func ExampleGenerateChain() { | ||||
| 	) | ||||
| 
 | ||||
| 	// Ensure that key1 has some funds in the genesis block.
 | ||||
| 	genesis := WriteGenesisBlockForTesting(db, addr1, big.NewInt(1000000)) | ||||
| 	genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(1000000)}) | ||||
| 
 | ||||
| 	// This call generates a chain of 5 blocks. The function runs for
 | ||||
| 	// each block and adds different features to gen based on the
 | ||||
|  | ||||
| @ -569,18 +569,17 @@ func (self *ChainManager) WriteBlock(block *types.Block) (status writeStatus, er | ||||
| 		// chain fork
 | ||||
| 		if block.ParentHash() != cblock.Hash() { | ||||
| 			// during split we merge two different chains and create the new canonical chain
 | ||||
| 			err := self.merge(cblock, block) | ||||
| 			err := self.reorg(cblock, block) | ||||
| 			if err != nil { | ||||
| 				return NonStatTy, err | ||||
| 			} | ||||
| 			status = SplitStatTy | ||||
| 		} | ||||
| 		status = CanonStatTy | ||||
| 
 | ||||
| 		self.mu.Lock() | ||||
| 		self.setTotalDifficulty(td) | ||||
| 		self.insert(block) | ||||
| 		self.mu.Unlock() | ||||
| 
 | ||||
| 		status = CanonStatTy | ||||
| 	} else { | ||||
| 		status = SideStatTy | ||||
| 	} | ||||
| @ -681,9 +680,11 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { | ||||
| 
 | ||||
| 			return i, err | ||||
| 		} | ||||
| 		if err := PutBlockReceipts(self.chainDb, block, receipts); err != nil { | ||||
| 			glog.V(logger.Warn).Infoln("error writing block receipts:", err) | ||||
| 		} | ||||
| 
 | ||||
| 		txcount += len(block.Transactions()) | ||||
| 
 | ||||
| 		// write the block to the chain and get the status
 | ||||
| 		status, err := self.WriteBlock(block) | ||||
| 		if err != nil { | ||||
| @ -711,10 +712,6 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { | ||||
| 			queue[i] = ChainSplitEvent{block, logs} | ||||
| 			queueEvent.splitCount++ | ||||
| 		} | ||||
| 		if err := PutBlockReceipts(self.chainDb, block, receipts); err != nil { | ||||
| 			glog.V(logger.Warn).Infoln("error writing block receipts:", err) | ||||
| 		} | ||||
| 
 | ||||
| 		stats.processed++ | ||||
| 	} | ||||
| 
 | ||||
| @ -729,20 +726,26 @@ func (self *ChainManager) InsertChain(chain types.Blocks) (int, error) { | ||||
| 	return 0, nil | ||||
| } | ||||
| 
 | ||||
| // diff takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
 | ||||
| // to be part of the new canonical chain.
 | ||||
| func (self *ChainManager) diff(oldBlock, newBlock *types.Block) (types.Blocks, error) { | ||||
| // reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
 | ||||
| // to be part of the new canonical chain and accumulates potential missing transactions and post an
 | ||||
| // event about them
 | ||||
| func (self *ChainManager) reorg(oldBlock, newBlock *types.Block) error { | ||||
| 	self.mu.Lock() | ||||
| 	defer self.mu.Unlock() | ||||
| 
 | ||||
| 	var ( | ||||
| 		newChain    types.Blocks | ||||
| 		commonBlock *types.Block | ||||
| 		oldStart    = oldBlock | ||||
| 		newStart    = newBlock | ||||
| 		deletedTxs  types.Transactions | ||||
| 	) | ||||
| 
 | ||||
| 	// first reduce whoever is higher bound
 | ||||
| 	if oldBlock.NumberU64() > newBlock.NumberU64() { | ||||
| 		// reduce old chain
 | ||||
| 		for oldBlock = oldBlock; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = self.GetBlock(oldBlock.ParentHash()) { | ||||
| 			deletedTxs = append(deletedTxs, oldBlock.Transactions()...) | ||||
| 		} | ||||
| 	} else { | ||||
| 		// reduce new chain and append new chain blocks for inserting later on
 | ||||
| @ -751,10 +754,10 @@ func (self *ChainManager) diff(oldBlock, newBlock *types.Block) (types.Blocks, e | ||||
| 		} | ||||
| 	} | ||||
| 	if oldBlock == nil { | ||||
| 		return nil, fmt.Errorf("Invalid old chain") | ||||
| 		return fmt.Errorf("Invalid old chain") | ||||
| 	} | ||||
| 	if newBlock == nil { | ||||
| 		return nil, fmt.Errorf("Invalid new chain") | ||||
| 		return fmt.Errorf("Invalid new chain") | ||||
| 	} | ||||
| 
 | ||||
| 	numSplit := newBlock.Number() | ||||
| @ -764,13 +767,14 @@ func (self *ChainManager) diff(oldBlock, newBlock *types.Block) (types.Blocks, e | ||||
| 			break | ||||
| 		} | ||||
| 		newChain = append(newChain, newBlock) | ||||
| 		deletedTxs = append(deletedTxs, oldBlock.Transactions()...) | ||||
| 
 | ||||
| 		oldBlock, newBlock = self.GetBlock(oldBlock.ParentHash()), self.GetBlock(newBlock.ParentHash()) | ||||
| 		if oldBlock == nil { | ||||
| 			return nil, fmt.Errorf("Invalid old chain") | ||||
| 			return fmt.Errorf("Invalid old chain") | ||||
| 		} | ||||
| 		if newBlock == nil { | ||||
| 			return nil, fmt.Errorf("Invalid new chain") | ||||
| 			return fmt.Errorf("Invalid new chain") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @ -779,18 +783,8 @@ func (self *ChainManager) diff(oldBlock, newBlock *types.Block) (types.Blocks, e | ||||
| 		glog.Infof("Chain split detected @ %x. Reorganising chain from #%v %x to %x", commonHash[:4], numSplit, oldStart.Hash().Bytes()[:4], newStart.Hash().Bytes()[:4]) | ||||
| 	} | ||||
| 
 | ||||
| 	return newChain, nil | ||||
| } | ||||
| 
 | ||||
| // merge merges two different chain to the new canonical chain
 | ||||
| func (self *ChainManager) merge(oldBlock, newBlock *types.Block) error { | ||||
| 	newChain, err := self.diff(oldBlock, newBlock) | ||||
| 	if err != nil { | ||||
| 		return fmt.Errorf("chain reorg failed: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	var addedTxs types.Transactions | ||||
| 	// insert blocks. Order does not matter. Last block will be written in ImportChain itself which creates the new head properly
 | ||||
| 	self.mu.Lock() | ||||
| 	for _, block := range newChain { | ||||
| 		// insert the block in the canonical way, re-writing history
 | ||||
| 		self.insert(block) | ||||
| @ -798,8 +792,18 @@ func (self *ChainManager) merge(oldBlock, newBlock *types.Block) error { | ||||
| 		PutTransactions(self.chainDb, block, block.Transactions()) | ||||
| 		PutReceipts(self.chainDb, GetBlockReceipts(self.chainDb, block.Hash())) | ||||
| 
 | ||||
| 		addedTxs = append(addedTxs, block.Transactions()...) | ||||
| 	} | ||||
| 	self.mu.Unlock() | ||||
| 
 | ||||
| 	// calculate the difference between deleted and added transactions
 | ||||
| 	diff := types.TxDifference(deletedTxs, addedTxs) | ||||
| 	// When transactions get deleted from the database that means the
 | ||||
| 	// receipts that were created in the fork must also be deleted
 | ||||
| 	for _, tx := range diff { | ||||
| 		DeleteReceipt(self.chainDb, tx.Hash()) | ||||
| 		DeleteTransaction(self.chainDb, tx.Hash()) | ||||
| 	} | ||||
| 	self.eventMux.Post(RemovedTransactionEvent{diff}) | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| @ -30,8 +30,10 @@ import ( | ||||
| 	"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/crypto" | ||||
| 	"github.com/ethereum/go-ethereum/ethdb" | ||||
| 	"github.com/ethereum/go-ethereum/event" | ||||
| 	"github.com/ethereum/go-ethereum/params" | ||||
| 	"github.com/ethereum/go-ethereum/pow" | ||||
| 	"github.com/ethereum/go-ethereum/rlp" | ||||
| 	"github.com/hashicorp/golang-lru" | ||||
| @ -483,19 +485,115 @@ func TestInsertNonceError(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| func TestGenesisMismatch(t *testing.T) { | ||||
| 	db, _ := ethdb.NewMemDatabase() | ||||
| 	var mux event.TypeMux | ||||
| 	genesis := GenesisBlock(0, db) | ||||
| 	_, err := NewChainManager(genesis, db, db, db, thePow(), &mux) | ||||
| 	if err != nil { | ||||
| 		t.Error(err) | ||||
| // Tests that chain reorganizations handle transaction removals and reinsertions.
 | ||||
| func TestChainTxReorgs(t *testing.T) { | ||||
| 	params.MinGasLimit = big.NewInt(125000)      // Minimum the gas limit may ever be.
 | ||||
| 	params.GenesisGasLimit = big.NewInt(3141592) // Gas limit of the Genesis block.
 | ||||
| 
 | ||||
| 	var ( | ||||
| 		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") | ||||
| 		key2, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") | ||||
| 		key3, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") | ||||
| 		addr1   = crypto.PubkeyToAddress(key1.PublicKey) | ||||
| 		addr2   = crypto.PubkeyToAddress(key2.PublicKey) | ||||
| 		addr3   = crypto.PubkeyToAddress(key3.PublicKey) | ||||
| 		db, _   = ethdb.NewMemDatabase() | ||||
| 	) | ||||
| 	genesis := WriteGenesisBlockForTesting(db, | ||||
| 		GenesisAccount{addr1, big.NewInt(1000000)}, | ||||
| 		GenesisAccount{addr2, big.NewInt(1000000)}, | ||||
| 		GenesisAccount{addr3, big.NewInt(1000000)}, | ||||
| 	) | ||||
| 	// Create two transactions shared between the chains:
 | ||||
| 	//  - postponed: transaction included at a later block in the forked chain
 | ||||
| 	//  - swapped: transaction included at the same block number in the forked chain
 | ||||
| 	postponed, _ := types.NewTransaction(0, addr1, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key1) | ||||
| 	swapped, _ := types.NewTransaction(1, addr1, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key1) | ||||
| 
 | ||||
| 	// Create two transactions that will be dropped by the forked chain:
 | ||||
| 	//  - pastDrop: transaction dropped retroactively from a past block
 | ||||
| 	//  - freshDrop: transaction dropped exactly at the block where the reorg is detected
 | ||||
| 	var pastDrop, freshDrop *types.Transaction | ||||
| 
 | ||||
| 	// Create three transactions that will be added in the forked chain:
 | ||||
| 	//  - pastAdd:   transaction added before the reorganiztion is detected
 | ||||
| 	//  - freshAdd:  transaction added at the exact block the reorg is detected
 | ||||
| 	//  - futureAdd: transaction added after the reorg has already finished
 | ||||
| 	var pastAdd, freshAdd, futureAdd *types.Transaction | ||||
| 
 | ||||
| 	chain := GenerateChain(genesis, db, 3, func(i int, gen *BlockGen) { | ||||
| 		switch i { | ||||
| 		case 0: | ||||
| 			pastDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2) | ||||
| 
 | ||||
| 			gen.AddTx(pastDrop)  // This transaction will be dropped in the fork from below the split point
 | ||||
| 			gen.AddTx(postponed) // This transaction will be postponed till block #3 in the fork
 | ||||
| 
 | ||||
| 		case 2: | ||||
| 			freshDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2) | ||||
| 
 | ||||
| 			gen.AddTx(freshDrop) // This transaction will be dropped in the fork from exactly at the split point
 | ||||
| 			gen.AddTx(swapped)   // This transaction will be swapped out at the exact height
 | ||||
| 
 | ||||
| 			gen.OffsetTime(9) // Lower the block difficulty to simulate a weaker chain
 | ||||
| 		} | ||||
| 	}) | ||||
| 	// Import the chain. This runs all block validation rules.
 | ||||
| 	evmux := &event.TypeMux{} | ||||
| 	chainman, _ := NewChainManager(db, FakePow{}, evmux) | ||||
| 	chainman.SetProcessor(NewBlockProcessor(db, FakePow{}, chainman, evmux)) | ||||
| 	if i, err := chainman.InsertChain(chain); err != nil { | ||||
| 		t.Fatalf("failed to insert original chain[%d]: %v", i, err) | ||||
| 	} | ||||
| 
 | ||||
| 	// overwrite the old chain
 | ||||
| 	chain = GenerateChain(genesis, db, 5, func(i int, gen *BlockGen) { | ||||
| 		switch i { | ||||
| 		case 0: | ||||
| 			pastAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3) | ||||
| 			gen.AddTx(pastAdd) // This transaction needs to be injected during reorg
 | ||||
| 
 | ||||
| 		case 2: | ||||
| 			gen.AddTx(postponed) // This transaction was postponed from block #1 in the original chain
 | ||||
| 			gen.AddTx(swapped)   // This transaction was swapped from the exact current spot in the original chain
 | ||||
| 
 | ||||
| 			freshAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3) | ||||
| 			gen.AddTx(freshAdd) // This transaction will be added exactly at reorg time
 | ||||
| 
 | ||||
| 		case 3: | ||||
| 			futureAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3) | ||||
| 			gen.AddTx(futureAdd) // This transaction will be added after a full reorg
 | ||||
| 		} | ||||
| 	}) | ||||
| 	if _, err := chainman.InsertChain(chain); err != nil { | ||||
| 		t.Fatalf("failed to insert forked chain: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// removed tx
 | ||||
| 	for i, tx := range (types.Transactions{pastDrop, freshDrop}) { | ||||
| 		if GetTransaction(db, tx.Hash()) != nil { | ||||
| 			t.Errorf("drop %d: tx found while shouldn't have been", i) | ||||
| 		} | ||||
| 		if GetReceipt(db, tx.Hash()) != nil { | ||||
| 			t.Errorf("drop %d: receipt found while shouldn't have been", i) | ||||
| 		} | ||||
| 	} | ||||
| 	// added tx
 | ||||
| 	for i, tx := range (types.Transactions{pastAdd, freshAdd, futureAdd}) { | ||||
| 		if GetTransaction(db, tx.Hash()) == nil { | ||||
| 			t.Errorf("add %d: expected tx to be found", i) | ||||
| 		} | ||||
| 		if GetReceipt(db, tx.Hash()) == nil { | ||||
| 			t.Errorf("add %d: expected receipt to be found", i) | ||||
| 		} | ||||
| 	} | ||||
| 	// shared tx
 | ||||
| 	for i, tx := range (types.Transactions{postponed, swapped}) { | ||||
| 		if GetTransaction(db, tx.Hash()) == nil { | ||||
| 			t.Errorf("share %d: expected tx to be found", i) | ||||
| 		} | ||||
| 		if GetReceipt(db, tx.Hash()) == nil { | ||||
| 			t.Errorf("share %d: expected receipt to be found", i) | ||||
| 		} | ||||
| 	genesis = GenesisBlock(1, db) | ||||
| 	_, err = NewChainManager(genesis, db, db, db, thePow(), &mux) | ||||
| 	if err == nil { | ||||
| 		t.Error("expected genesis mismatch error") | ||||
| 	} | ||||
| } | ||||
| */ | ||||
|  | ||||
| @ -36,6 +36,9 @@ type NewBlockEvent struct{ Block *types.Block } | ||||
| // NewMinedBlockEvent is posted when a block has been imported.
 | ||||
| type NewMinedBlockEvent struct{ Block *types.Block } | ||||
| 
 | ||||
| // RemovedTransactionEvent is posted when a reorg happens
 | ||||
| type RemovedTransactionEvent struct{ Txs types.Transactions } | ||||
| 
 | ||||
| // ChainSplit is posted when a new head is detected
 | ||||
| type ChainSplitEvent struct { | ||||
| 	Block *types.Block | ||||
|  | ||||
| @ -125,15 +125,27 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big | ||||
| 	return block | ||||
| } | ||||
| 
 | ||||
| func WriteGenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big.Int) *types.Block { | ||||
| type GenesisAccount struct { | ||||
| 	Address common.Address | ||||
| 	Balance *big.Int | ||||
| } | ||||
| 
 | ||||
| func WriteGenesisBlockForTesting(db ethdb.Database, accounts ...GenesisAccount) *types.Block { | ||||
| 	accountJson := "{" | ||||
| 	for i, account := range accounts { | ||||
| 		if i != 0 { | ||||
| 			accountJson += "," | ||||
| 		} | ||||
| 		accountJson += fmt.Sprintf(`"0x%x":{"balance":"0x%x"}`, account.Address, account.Balance.Bytes()) | ||||
| 	} | ||||
| 	accountJson += "}" | ||||
| 
 | ||||
| 	testGenesis := fmt.Sprintf(`{ | ||||
| 	"nonce":"0x%x", | ||||
| 	"gasLimit":"0x%x", | ||||
| 	"difficulty":"0x%x", | ||||
| 	"alloc": { | ||||
| 		"0x%x":{"balance":"0x%x"} | ||||
| 	} | ||||
| }`, types.EncodeNonce(0), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes(), addr, balance.Bytes()) | ||||
| 	"alloc": %s | ||||
| }`, types.EncodeNonce(0), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes(), accountJson) | ||||
| 	block, _ := WriteGenesisBlock(db, strings.NewReader(testGenesis)) | ||||
| 	return block | ||||
| } | ||||
|  | ||||
| @ -81,7 +81,7 @@ func NewTxPool(eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func( | ||||
| 		gasLimit:     gasLimitFn, | ||||
| 		minGasPrice:  new(big.Int), | ||||
| 		pendingState: state.ManageState(currentStateFn()), | ||||
| 		events:       eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}), | ||||
| 		events:       eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}), | ||||
| 	} | ||||
| 	go pool.eventLoop() | ||||
| 
 | ||||
| @ -93,16 +93,18 @@ func (pool *TxPool) eventLoop() { | ||||
| 	// we need to know the new state. The new state will help us determine
 | ||||
| 	// the nonces in the managed state
 | ||||
| 	for ev := range pool.events.Chan() { | ||||
| 		pool.mu.Lock() | ||||
| 
 | ||||
| 		switch ev := ev.(type) { | ||||
| 		case ChainHeadEvent: | ||||
| 			pool.mu.Lock() | ||||
| 			pool.resetState() | ||||
| 		case GasPriceChanged: | ||||
| 			pool.minGasPrice = ev.Price | ||||
| 		} | ||||
| 
 | ||||
| 			pool.mu.Unlock() | ||||
| 		case GasPriceChanged: | ||||
| 			pool.mu.Lock() | ||||
| 			pool.minGasPrice = ev.Price | ||||
| 			pool.mu.Unlock() | ||||
| 		case RemovedTransactionEvent: | ||||
| 			pool.AddTransactions(ev.Txs) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -238,3 +238,15 @@ func TestNonceRecovery(t *testing.T) { | ||||
| 		t.Errorf("expected nonce to be %d, got %d", n+1, fn) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestRemovedTxEvent(t *testing.T) { | ||||
| 	pool, key := setupTxPool() | ||||
| 	tx := transaction(0, big.NewInt(1000000), key) | ||||
| 	from, _ := tx.From() | ||||
| 	pool.currentState().AddBalance(from, big.NewInt(1000000000000)) | ||||
| 	pool.eventMux.Post(RemovedTransactionEvent{types.Transactions{tx}}) | ||||
| 	pool.eventMux.Post(ChainHeadEvent{nil}) | ||||
| 	if len(pool.pending) != 1 { | ||||
| 		t.Error("expected 1 pending tx, got", len(pool.pending)) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @ -77,6 +77,22 @@ func PutTransactions(db ethdb.Database, block *types.Block, txs types.Transactio | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func DeleteTransaction(db ethdb.Database, txHash common.Hash) { | ||||
| 	db.Delete(txHash[:]) | ||||
| } | ||||
| 
 | ||||
| func GetTransaction(db ethdb.Database, txhash common.Hash) *types.Transaction { | ||||
| 	data, _ := db.Get(txhash[:]) | ||||
| 	if len(data) != 0 { | ||||
| 		var tx types.Transaction | ||||
| 		if err := rlp.DecodeBytes(data, &tx); err != nil { | ||||
| 			return nil | ||||
| 		} | ||||
| 		return &tx | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // PutReceipts stores the receipts in the current database
 | ||||
| func PutReceipts(db ethdb.Database, receipts types.Receipts) error { | ||||
| 	batch := new(leveldb.Batch) | ||||
| @ -107,6 +123,11 @@ func PutReceipts(db ethdb.Database, receipts types.Receipts) error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| // Delete a receipts from the database
 | ||||
| func DeleteReceipt(db ethdb.Database, txHash common.Hash) { | ||||
| 	db.Delete(append(receiptsPre, txHash[:]...)) | ||||
| } | ||||
| 
 | ||||
| // GetReceipt returns a receipt by hash
 | ||||
| func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt { | ||||
| 	data, _ := db.Get(append(receiptsPre, txHash[:]...)) | ||||
|  | ||||
| @ -272,14 +272,36 @@ func (tx *Transaction) String() string { | ||||
| // Transaction slice type for basic sorting.
 | ||||
| type Transactions []*Transaction | ||||
| 
 | ||||
| // Len returns the length of s
 | ||||
| func (s Transactions) Len() int { return len(s) } | ||||
| 
 | ||||
| // Swap swaps the i'th and the j'th element in s
 | ||||
| func (s Transactions) Swap(i, j int) { s[i], s[j] = s[j], s[i] } | ||||
| 
 | ||||
| // GetRlp implements Rlpable and returns the i'th element of s in rlp
 | ||||
| func (s Transactions) GetRlp(i int) []byte { | ||||
| 	enc, _ := rlp.EncodeToBytes(s[i]) | ||||
| 	return enc | ||||
| } | ||||
| 
 | ||||
| // Returns a new set t which is the difference between a to b
 | ||||
| func TxDifference(a, b Transactions) (keep Transactions) { | ||||
| 	keep = make(Transactions, 0, len(a)) | ||||
| 
 | ||||
| 	remove := make(map[common.Hash]struct{}) | ||||
| 	for _, tx := range b { | ||||
| 		remove[tx.Hash()] = struct{}{} | ||||
| 	} | ||||
| 
 | ||||
| 	for _, tx := range a { | ||||
| 		if _, ok := remove[tx.Hash()]; !ok { | ||||
| 			keep = append(keep, tx) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return keep | ||||
| } | ||||
| 
 | ||||
| type TxByNonce struct{ Transactions } | ||||
| 
 | ||||
| func (s TxByNonce) Less(i, j int) bool { | ||||
|  | ||||
| @ -33,7 +33,7 @@ func newTestProtocolManager(blocks int, generator func(int, *core.BlockGen), new | ||||
| 		evmux       = new(event.TypeMux) | ||||
| 		pow         = new(core.FakePow) | ||||
| 		db, _       = ethdb.NewMemDatabase() | ||||
| 		genesis     = core.WriteGenesisBlockForTesting(db, testBankAddress, testBankFunds) | ||||
| 		genesis     = core.WriteGenesisBlockForTesting(db, core.GenesisAccount{testBankAddress, testBankFunds}) | ||||
| 		chainman, _ = core.NewChainManager(db, pow, evmux) | ||||
| 		blockproc   = core.NewBlockProcessor(db, pow, chainman, evmux) | ||||
| 	) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user