tendermint/mempool/mempool.go

101 lines
2.6 KiB
Go
Raw Normal View History

2014-09-10 02:43:16 -07:00
/*
Mempool receives new transactions and applies them to the latest committed state.
If the transaction is acceptable, then it broadcasts a fingerprint to peers.
The transaction fingerprint is a short sequence of bytes (shorter than a full hash).
Each peer connection uses a different algorithm for turning the tx hash into a
fingerprint in order to prevent transaction blocking attacks. Upon inspecting a
tx fingerprint, the receiver may query the source for the full tx bytes.
When this node happens to be the next proposer, it simply takes the recently
modified state (and the associated transactions) and use that as the proposal.
*/
package mempool
import (
"sync"
2014-09-11 11:17:59 -07:00
. "github.com/tendermint/tendermint/binary"
. "github.com/tendermint/tendermint/blocks"
. "github.com/tendermint/tendermint/state"
)
2014-09-10 02:43:16 -07:00
type Mempool struct {
mtx sync.Mutex
lastBlock *Block
state *State
txs []Tx
2014-09-10 02:43:16 -07:00
}
func NewMempool(lastBlock *Block, state *State) *Mempool {
2014-09-10 02:43:16 -07:00
return &Mempool{
lastBlock: lastBlock,
state: state,
2014-09-10 02:43:16 -07:00
}
}
// Apply tx to the state and remember it.
func (mem *Mempool) AddTx(tx Tx) (err error) {
2014-09-10 02:43:16 -07:00
mem.mtx.Lock()
defer mem.mtx.Unlock()
err = mem.state.CommitTx(tx)
if err != nil {
return err
2014-09-10 02:43:16 -07:00
} else {
mem.txs = append(mem.txs, tx)
return nil
2014-09-10 02:43:16 -07:00
}
}
// Returns a new block from the current state and associated transactions.
// The block's Validation is empty, and some parts of the header too.
func (mem *Mempool) MakeProposal() (*Block, *State) {
mem.mtx.Lock()
defer mem.mtx.Unlock()
nextBlock := mem.lastBlock.MakeNextBlock()
nextBlock.Data.Txs = mem.txs
return nextBlock, mem.state
}
// Txs that are present in block are discarded from mempool.
// Txs that have become invalid in the new state are also discarded.
func (mem *Mempool) ResetForBlockandState(block *Block, state *State) {
mem.mtx.Lock()
defer mem.mtx.Unlock()
mem.lastBlock = block
mem.state = state.Copy()
// First, create a lookup map of txns in new block.
blockTxsMap := make(map[string]struct{})
for _, tx := range block.Data.Txs {
txHash := BinaryHash(tx)
blockTxsMap[string(txHash)] = struct{}{}
}
// Next, filter all txs from mem.txs that are in blockTxsMap
txs := []Tx{}
for _, tx := range mem.txs {
txHash := BinaryHash(tx)
if _, ok := blockTxsMap[string(txHash)]; ok {
continue
} else {
txs = append(txs, tx)
}
}
// Next, filter all txs that aren't valid given new state.
validTxs := []Tx{}
for _, tx := range txs {
err := mem.state.CommitTx(tx)
if err != nil {
validTxs = append(validTxs, tx)
} else {
// tx is no longer valid.
}
}
// We're done!
mem.txs = validTxs
}