Skip to content

Commit aa0e2a4

Browse files
authoredNov 19, 2021
Add ability to remove "Parent" transactions from the mempool (#582)
* add the new transaction wrapper * export and move DecodeChildTx * return a more precise type (Tx instead of []byte) for DecodeChildTx * add test and helper function * linter
1 parent 7828d21 commit aa0e2a4

File tree

6 files changed

+407
-113
lines changed

6 files changed

+407
-113
lines changed
 

‎buf.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
version: v1beta1
2+
13
build:
24
roots:
35
- proto

‎mempool/clist_mempool.go

+6
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,12 @@ func (mem *CListMempool) Update(
605605
// https://github.com/tendermint/tendermint/issues/3322.
606606
if e, ok := mem.txsMap.Load(TxKey(tx)); ok {
607607
mem.removeTx(tx, e.(*clist.CElement), false)
608+
// see if the transaction is a child transaction of a some parent
609+
// transaction that exists in the mempool
610+
} else if parentHash, _, isChild := types.DecodeChildTx(tx); isChild {
611+
var parentKey [TxKeySize]byte
612+
copy(parentKey[:], parentHash)
613+
mem.RemoveTxByKey(parentKey, false)
608614
}
609615
}
610616

‎mempool/clist_mempool_test.go

+23
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,29 @@ func TestMempoolUpdate(t *testing.T) {
214214
err = mempool.CheckTx([]byte{0x03}, nil, TxInfo{})
215215
require.NoError(t, err)
216216
}
217+
218+
// 4. Removes a parent transaction after receiving a child transaction in the update
219+
{
220+
mempool.Flush()
221+
parentTx := []byte{1, 2, 3, 4}
222+
childTx := []byte{1, 2}
223+
parentHash := sha256.Sum256(parentTx)
224+
225+
// create the wrapped child transaction
226+
wTx, err := types.WrapChildTx(parentHash[:], childTx)
227+
require.NoError(t, err)
228+
229+
// add the parent transaction to the mempool
230+
err = mempool.CheckTx(parentTx, nil, TxInfo{})
231+
require.NoError(t, err)
232+
233+
// remove the parent from the mempool using the wrapped child tx
234+
err = mempool.Update(1, []types.Tx{wTx}, abciResponses(1, abci.CodeTypeOK), nil, nil)
235+
require.NoError(t, err)
236+
237+
assert.Zero(t, mempool.Size())
238+
239+
}
217240
}
218241

219242
func TestMempool_KeepInvalidTxsInCache(t *testing.T) {

‎proto/tendermint/types/types.pb.go

+342-113
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎proto/tendermint/types/types.proto

+7
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,10 @@ message TxProof {
219219
bytes data = 2;
220220
tendermint.crypto.Proof proof = 3;
221221
}
222+
223+
// ChildTx wraps a transaction that was derived from a parent transaction. This
224+
// allows for removal of the parent transaction from the mempool.
225+
message ChildTx {
226+
bytes parent_tx_hash = 1;
227+
bytes tx = 2;
228+
}

‎types/tx.go

+27
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77

8+
"github.com/gogo/protobuf/proto"
89
"github.com/tendermint/tendermint/crypto/merkle"
910
"github.com/tendermint/tendermint/crypto/tmhash"
1011
tmbytes "github.com/tendermint/tendermint/libs/bytes"
@@ -159,3 +160,29 @@ func ComputeProtoSizeForTxs(txs []Tx) int64 {
159160
pdData := data.ToProto()
160161
return int64(pdData.Size())
161162
}
163+
164+
// DecodeChildTx attempts to unmarshal the provided transaction into a child
165+
// transaction wrapper, if this an be done, then it returns true. A child
166+
// transaction is a normal transaction that has been derived from a different
167+
// parent transaction. The returned hash is that of the parent transaction,
168+
// which allows us to remove the parent transaction from the mempool
169+
func DecodeChildTx(tx Tx) (hash []byte, unwrapped Tx, has bool) {
170+
// attempt to unmarshal into a a child transaction
171+
var childTx tmproto.ChildTx
172+
err := proto.Unmarshal(tx, &childTx)
173+
if err != nil {
174+
return nil, nil, false
175+
}
176+
return childTx.ParentTxHash, childTx.Tx, true
177+
}
178+
179+
// WrapChildTx creates a wrapped Tx that includes the parent transaction's hash
180+
// so that it can be easily removed from the mempool. note: must be unwrapped to
181+
// be a viable sdk.Tx
182+
func WrapChildTx(parentHash []byte, child Tx) (Tx, error) {
183+
wTx := tmproto.ChildTx{
184+
ParentTxHash: parentHash,
185+
Tx: child,
186+
}
187+
return proto.Marshal(&wTx)
188+
}

0 commit comments

Comments
 (0)
Please sign in to comment.