From 507d23ae5427dcf9fba172e819817ef6b7dba01a Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Wed, 24 Jan 2024 12:45:02 +0900 Subject: [PATCH 01/19] tx filter --- serve/filters/filter/tx.go | 40 ++++++++++++++++++++++++++++ serve/filters/filter/tx_test.go | 46 +++++++++++++++++++++++++++++++++ serve/filters/filter/types.go | 1 + 3 files changed, 87 insertions(+) create mode 100644 serve/filters/filter/tx.go create mode 100644 serve/filters/filter/tx_test.go diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go new file mode 100644 index 00000000..b81d980c --- /dev/null +++ b/serve/filters/filter/tx.go @@ -0,0 +1,40 @@ +package filter + +import ( + "github.com/gnolang/gno/tm2/pkg/bft/types" + // abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" +) + +type TxFilter struct { + *baseFilter + txr *types.TxResult +} + +func NewTxFilter() *TxFilter { + return &TxFilter{ + baseFilter: newBaseFilter(TxFilterType), + } +} + +func (tf *TxFilter) GetHash() []byte { + tf.Lock() + defer tf.Unlock() + + if tf.txr == nil { + return nil + } + + tx := tf.txr.Tx + if tx == nil { + return nil + } + + return tx.Hash() +} + +func (tf *TxFilter) UpdateWithTx(txr *types.TxResult) { + tf.Lock() + defer tf.Unlock() + + tf.txr = txr +} \ No newline at end of file diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go new file mode 100644 index 00000000..ffad08e4 --- /dev/null +++ b/serve/filters/filter/tx_test.go @@ -0,0 +1,46 @@ +package filter + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/gnolang/gno/tm2/pkg/bft/types" +) + +func TestGetHash(t *testing.T) { + t.Parallel() + + txs := []*types.TxResult { + { Tx: []byte(`c25dda249cdece9d908cc33adcd16aa05e20290f`) }, + { Tx: []byte(`71ac9eed6a76a285ae035fe84a251d56ae9485a4`) }, + { Tx: []byte(`356a192b7913b04c54574d18c28d46e6395428ab`) }, + { Tx: []byte(`da4b9237bacccdf19c0760cab7aec4a8359010b0`) }, + { Tx: []byte(`77de68daecd823babbb58edb1c8e14d7106e83bb`) }, + { Tx: []byte(`1b6453892473a467d07372d45eb05abc2031647a`) }, + { Tx: []byte(`ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4`) }, + { Tx: []byte(`c1dfd96eea8cc2b62785275bca38ac261256e278`) }, + } + + // create a new tx filter + f := NewTxFilter() + + // make sure the filter is of a correct type + assert.Equal(t, TxFilterType, f.GetType()) + + // update the tx filter with dummy txs + for _, tx := range txs { + tx := tx + + f.UpdateWithTx(tx) + + // get hash + hash := f.GetHash() + assert.Equal(t, tx.Tx.Hash(), hash) + } + + // change last tx to nil + f.UpdateWithTx(nil) + + hash := f.GetHash() + assert.Nil(t, hash) +} \ No newline at end of file diff --git a/serve/filters/filter/types.go b/serve/filters/filter/types.go index 4d6519c3..a3fc5c43 100644 --- a/serve/filters/filter/types.go +++ b/serve/filters/filter/types.go @@ -4,4 +4,5 @@ type Type string const ( BlockFilterType Type = "BlockFilter" + TxFilterType Type = "TxFilter" ) From 44c6806f336b7437c1425ef37a5398b9c247698f Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 25 Jan 2024 13:15:03 +0900 Subject: [PATCH 02/19] change TxFilter type --- serve/filters/filter/tx.go | 45 +++++++++++++++++++++++++-------- serve/filters/filter/tx_test.go | 27 ++++++++++---------- 2 files changed, 48 insertions(+), 24 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index b81d980c..262554af 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -2,39 +2,62 @@ package filter import ( "github.com/gnolang/gno/tm2/pkg/bft/types" - // abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" ) +// TxFilter holds a slice of transaction results. +// It provides methods to manipulate and query the transactions. type TxFilter struct { *baseFilter - txr *types.TxResult + txrs []*types.TxResult } +// NewTxFilter creates a new TxFilter object. func NewTxFilter() *TxFilter { return &TxFilter{ baseFilter: newBaseFilter(TxFilterType), + txrs: make([]*types.TxResult, 0), } } -func (tf *TxFilter) GetHash() []byte { +// GetHashes iterates over all transactions in the filter and returns their hashes. +// +// It appends `nil` to the result slice if the transaction or its content is `nil`. +// This ensures that the length og the returned slice matches the number of transactions in the filter. +func (tf *TxFilter) GetHashes() [][]byte { tf.Lock() defer tf.Unlock() - if tf.txr == nil { - return nil + hashes := make([][]byte, 0, len(tf.txrs)) + for _, txr := range tf.txrs { + if txr == nil || txr.Tx == nil { + hashes = append(hashes, nil) + continue + } + hashes = append(hashes, txr.Tx.Hash()) } - tx := tf.txr.Tx - if tx == nil { - return nil - } + return hashes +} + +// GetChanges retrieves and returns all the transactions in the filter. +// +// It also resets the transactions and prepare the filter for new transactions. +func (tf *TxFilter) GetChanges() any { + tf.Lock() + defer tf.Unlock() + + changes := make([]*types.TxResult, len(tf.txrs)) + copy(changes, tf.txrs) + + tf.txrs = tf.txrs[:0] // reset for new transactions - return tx.Hash() + return changes } +// UpdateWithTx adds a transaction to the filter. func (tf *TxFilter) UpdateWithTx(txr *types.TxResult) { tf.Lock() defer tf.Unlock() - tf.txr = txr + tf.txrs = append(tf.txrs, txr) } \ No newline at end of file diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index ffad08e4..57b8539f 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -3,8 +3,9 @@ package filter import ( "testing" - "github.com/stretchr/testify/assert" "github.com/gnolang/gno/tm2/pkg/bft/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestGetHash(t *testing.T) { @@ -23,24 +24,24 @@ func TestGetHash(t *testing.T) { // create a new tx filter f := NewTxFilter() - - // make sure the filter is of a correct type assert.Equal(t, TxFilterType, f.GetType()) - // update the tx filter with dummy txs for _, tx := range txs { - tx := tx - f.UpdateWithTx(tx) + } - // get hash - hash := f.GetHash() - assert.Equal(t, tx.Tx.Hash(), hash) + // get the hashes of the txs + hashes := f.GetHashes() + for i, tx := range txs { + assert.Equal(t, tx.Tx.Hash(), hashes[i]) } - // change last tx to nil - f.UpdateWithTx(nil) + // get the chages from the filter + changes := f.GetChanges().([]*types.TxResult) + require.Len(t, changes, len(txs)) + for i, tx := range txs { + assert.Equal(t, tx, changes[i]) + } - hash := f.GetHash() - assert.Nil(t, hash) + assert.Empty(t, f.txrs) } \ No newline at end of file From 38067d17e3455fce49f644e1fc5ccf6742e83350 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 25 Jan 2024 15:05:43 +0900 Subject: [PATCH 03/19] basic filters --- serve/filters/filter/tx.go | 75 ++++++++++++++++++- serve/filters/filter/tx_test.go | 126 +++++++++++++++++++++++++++++--- 2 files changed, 186 insertions(+), 15 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 262554af..112cadbf 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -8,14 +8,18 @@ import ( // It provides methods to manipulate and query the transactions. type TxFilter struct { *baseFilter - txrs []*types.TxResult + // txrs represents the transactions in the filter. + txrs []*types.TxResult + // conditions holds the filtering conditions. + conditions []func(*types.TxResult) bool } // NewTxFilter creates a new TxFilter object. func NewTxFilter() *TxFilter { return &TxFilter{ baseFilter: newBaseFilter(TxFilterType), - txrs: make([]*types.TxResult, 0), + txrs: make([]*types.TxResult, 0), + conditions: make([]func(*types.TxResult) bool, 0), } } @@ -49,7 +53,7 @@ func (tf *TxFilter) GetChanges() any { changes := make([]*types.TxResult, len(tf.txrs)) copy(changes, tf.txrs) - tf.txrs = tf.txrs[:0] // reset for new transactions + tf.txrs = tf.txrs[:0] // reset for new transactions return changes } @@ -60,4 +64,67 @@ func (tf *TxFilter) UpdateWithTx(txr *types.TxResult) { defer tf.Unlock() tf.txrs = append(tf.txrs, txr) -} \ No newline at end of file +} + +// ClearConditions resets the previously set conditions from the filter. +func (tf *TxFilter) ClearConditions() *TxFilter { + tf.conditions = nil + return tf +} + +// Height sets a filter for the height of the transactions. +// +// It appends a height-based condition to the conditions slice. +func (tf *TxFilter) Height(height int64) *TxFilter { + cond := func(txr *types.TxResult) bool { + return txr.Height == height + } + tf.conditions = append(tf.conditions, cond) + return tf +} + +// Index sets a filter for the index of the transactions. +func (tf *TxFilter) Index(index uint32) *TxFilter { + cond := func(txr *types.TxResult) bool { + return txr.Index == index + } + tf.conditions = append(tf.conditions, cond) + return tf +} + +// GasUsed sets a filter for the gas used by transactions. +func (tf *TxFilter) GasUsed(min, max int64) *TxFilter { + cond := func(txr *types.TxResult) bool { + return txr.Response.GasUsed >= min && txr.Response.GasUsed <= max + } + tf.conditions = append(tf.conditions, cond) + return tf +} + +// GasWanted sets a filter for the gas wanted by transactions. +func (tf *TxFilter) GasWanted(min, max int64) *TxFilter { + cond := func(txr *types.TxResult) bool { + return txr.Response.GasWanted >= min && txr.Response.GasWanted <= max + } + tf.conditions = append(tf.conditions, cond) + return tf +} + +// Apply applies all added conditions to the transactions in the filter. +// +// It returns a slice of `TxResult` that satisfy all the conditions. +func (tf *TxFilter) Apply() (filtered []*types.TxResult) { + for _, txr := range tf.txrs { + pass := true + for _, condition := range tf.conditions { + if !condition(txr) { + pass = false + break + } + } + if pass { + filtered = append(filtered, txr) + } + } + return filtered +} diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index 57b8539f..b1125b14 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -3,23 +3,24 @@ package filter import ( "testing" + abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/bft/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) -func TestGetHash(t *testing.T) { +func TestGetHashAndChanges(t *testing.T) { t.Parallel() - txs := []*types.TxResult { - { Tx: []byte(`c25dda249cdece9d908cc33adcd16aa05e20290f`) }, - { Tx: []byte(`71ac9eed6a76a285ae035fe84a251d56ae9485a4`) }, - { Tx: []byte(`356a192b7913b04c54574d18c28d46e6395428ab`) }, - { Tx: []byte(`da4b9237bacccdf19c0760cab7aec4a8359010b0`) }, - { Tx: []byte(`77de68daecd823babbb58edb1c8e14d7106e83bb`) }, - { Tx: []byte(`1b6453892473a467d07372d45eb05abc2031647a`) }, - { Tx: []byte(`ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4`) }, - { Tx: []byte(`c1dfd96eea8cc2b62785275bca38ac261256e278`) }, + txs := []*types.TxResult{ + {Tx: []byte(`c25dda249cdece9d908cc33adcd16aa05e20290f`)}, + {Tx: []byte(`71ac9eed6a76a285ae035fe84a251d56ae9485a4`)}, + {Tx: []byte(`356a192b7913b04c54574d18c28d46e6395428ab`)}, + {Tx: []byte(`da4b9237bacccdf19c0760cab7aec4a8359010b0`)}, + {Tx: []byte(`77de68daecd823babbb58edb1c8e14d7106e83bb`)}, + {Tx: []byte(`1b6453892473a467d07372d45eb05abc2031647a`)}, + {Tx: []byte(`ac3478d69a3c81fa62e60f5c3696165a4e5e6ac4`)}, + {Tx: []byte(`c1dfd96eea8cc2b62785275bca38ac261256e278`)}, } // create a new tx filter @@ -44,4 +45,107 @@ func TestGetHash(t *testing.T) { } assert.Empty(t, f.txrs) -} \ No newline at end of file +} + +func TestTxFilters(t *testing.T) { + txs := []*types.TxResult{ + { + Height: 100, + Index: 0, + Tx: []byte(`sampleTx0`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1000, + GasUsed: 900, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data0`), + }, + }, + }, + { + Height: 101, + Index: 1, + Tx: []byte(`sampleTx1`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1200, + GasUsed: 1100, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data1`), + }, + }, + }, + { + Height: 102, + Index: 2, + Tx: []byte(`sampleTx2`), + Response: abci.ResponseDeliverTx{ + GasWanted: 900, + GasUsed: 800, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data2`), + }, + }, + }, + { + Height: 103, + Index: 3, + Tx: []byte(`sampleTx3`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1300, + GasUsed: 1250, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data3`), + }, + }, + }, + { + Height: 104, + Index: 4, + Tx: []byte(`sampleTx4`), + Response: abci.ResponseDeliverTx{ + GasWanted: 800, + GasUsed: 700, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data4`), + }, + }, + }, + } + + f := NewTxFilter() + for _, tx := range txs { + f.UpdateWithTx(tx) + } + + height := f.Height(100).Apply() + require.Len(t, height, 1) + assert.Equal(t, txs[0], height[0]) + + f.ClearConditions() + + index := f.Index(1).Apply() + require.Len(t, index, 1) + assert.Equal(t, txs[1], index[0]) + + f.ClearConditions() + + gasUsed := f.GasUsed(800, 1000).Apply() + require.Len(t, gasUsed, 2) + assert.Equal(t, txs[0], gasUsed[0]) + assert.Equal(t, txs[2], gasUsed[1]) + + f.ClearConditions() + + gasWanted := f.GasWanted(1000, 1200).Apply() + require.Len(t, gasWanted, 2) + assert.Equal(t, txs[0], gasWanted[0]) + assert.Equal(t, txs[1], gasWanted[1]) + + f.ClearConditions() + + // query-like method chaining + query := f.Height(101).Index(1).GasUsed(1000, 1200).Apply() + require.Len(t, query, 1) + assert.Equal(t, txs[1], query[0]) + + f.ClearConditions() +} From 6b8aa0e9b9ffeb547f6c7049112bbe6735ccb8cc Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 25 Jan 2024 16:16:09 +0900 Subject: [PATCH 04/19] lint --- serve/filters/filter/tx.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 112cadbf..b5f82633 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -32,11 +32,14 @@ func (tf *TxFilter) GetHashes() [][]byte { defer tf.Unlock() hashes := make([][]byte, 0, len(tf.txrs)) + for _, txr := range tf.txrs { if txr == nil || txr.Tx == nil { hashes = append(hashes, nil) + continue } + hashes = append(hashes, txr.Tx.Hash()) } @@ -69,6 +72,7 @@ func (tf *TxFilter) UpdateWithTx(txr *types.TxResult) { // ClearConditions resets the previously set conditions from the filter. func (tf *TxFilter) ClearConditions() *TxFilter { tf.conditions = nil + return tf } @@ -80,6 +84,7 @@ func (tf *TxFilter) Height(height int64) *TxFilter { return txr.Height == height } tf.conditions = append(tf.conditions, cond) + return tf } @@ -89,6 +94,7 @@ func (tf *TxFilter) Index(index uint32) *TxFilter { return txr.Index == index } tf.conditions = append(tf.conditions, cond) + return tf } @@ -98,6 +104,7 @@ func (tf *TxFilter) GasUsed(min, max int64) *TxFilter { return txr.Response.GasUsed >= min && txr.Response.GasUsed <= max } tf.conditions = append(tf.conditions, cond) + return tf } @@ -107,6 +114,7 @@ func (tf *TxFilter) GasWanted(min, max int64) *TxFilter { return txr.Response.GasWanted >= min && txr.Response.GasWanted <= max } tf.conditions = append(tf.conditions, cond) + return tf } @@ -122,9 +130,11 @@ func (tf *TxFilter) Apply() (filtered []*types.TxResult) { break } } + if pass { filtered = append(filtered, txr) } } + return filtered } From aa7dc37ba535751137f4dfa603ea61236a8f6e53 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 25 Jan 2024 16:22:37 +0900 Subject: [PATCH 05/19] fixup --- serve/filters/filter/tx.go | 6 +++++- serve/filters/filter/tx_test.go | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index b5f82633..95dc5fb7 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -121,12 +121,16 @@ func (tf *TxFilter) GasWanted(min, max int64) *TxFilter { // Apply applies all added conditions to the transactions in the filter. // // It returns a slice of `TxResult` that satisfy all the conditions. -func (tf *TxFilter) Apply() (filtered []*types.TxResult) { +func (tf *TxFilter) Apply() []*types.TxResult { + var filtered []*types.TxResult + for _, txr := range tf.txrs { pass := true + for _, condition := range tf.conditions { if !condition(txr) { pass = false + break } } diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index b1125b14..34718f29 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -40,6 +40,7 @@ func TestGetHashAndChanges(t *testing.T) { // get the chages from the filter changes := f.GetChanges().([]*types.TxResult) require.Len(t, changes, len(txs)) + for i, tx := range txs { assert.Equal(t, tx, changes[i]) } From e7d66b72afa9aab05dbc418ae97b2d063045e3b0 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 25 Jan 2024 17:39:33 +0900 Subject: [PATCH 06/19] Missing lock acquisition --- serve/filters/filter/tx.go | 10 +++ serve/filters/filter/tx_test.go | 154 +++++++++++++++++++++----------- 2 files changed, 110 insertions(+), 54 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 95dc5fb7..463e7519 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -71,6 +71,9 @@ func (tf *TxFilter) UpdateWithTx(txr *types.TxResult) { // ClearConditions resets the previously set conditions from the filter. func (tf *TxFilter) ClearConditions() *TxFilter { + tf.Lock() + defer tf.Unlock() + tf.conditions = nil return tf @@ -122,6 +125,13 @@ func (tf *TxFilter) GasWanted(min, max int64) *TxFilter { // // It returns a slice of `TxResult` that satisfy all the conditions. func (tf *TxFilter) Apply() []*types.TxResult { + tf.Lock() + defer tf.Unlock() + + if tf.conditions == nil { + return tf.txrs + } + var filtered []*types.TxResult for _, txr := range tf.txrs { diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index 34718f29..0120bac0 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -1,6 +1,7 @@ package filter import ( + "sync" "testing" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" @@ -48,69 +49,71 @@ func TestGetHashAndChanges(t *testing.T) { assert.Empty(t, f.txrs) } -func TestTxFilters(t *testing.T) { - txs := []*types.TxResult{ - { - Height: 100, - Index: 0, - Tx: []byte(`sampleTx0`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1000, - GasUsed: 900, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data0`), - }, +var txs = []*types.TxResult{ + { + Height: 100, + Index: 0, + Tx: []byte(`sampleTx0`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1000, + GasUsed: 900, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data0`), }, }, - { - Height: 101, - Index: 1, - Tx: []byte(`sampleTx1`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1200, - GasUsed: 1100, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data1`), - }, + }, + { + Height: 101, + Index: 1, + Tx: []byte(`sampleTx1`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1200, + GasUsed: 1100, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data1`), }, }, - { - Height: 102, - Index: 2, - Tx: []byte(`sampleTx2`), - Response: abci.ResponseDeliverTx{ - GasWanted: 900, - GasUsed: 800, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data2`), - }, + }, + { + Height: 102, + Index: 2, + Tx: []byte(`sampleTx2`), + Response: abci.ResponseDeliverTx{ + GasWanted: 900, + GasUsed: 800, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data2`), }, }, - { - Height: 103, - Index: 3, - Tx: []byte(`sampleTx3`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1300, - GasUsed: 1250, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data3`), - }, + }, + { + Height: 103, + Index: 3, + Tx: []byte(`sampleTx3`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1300, + GasUsed: 1250, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data3`), }, }, - { - Height: 104, - Index: 4, - Tx: []byte(`sampleTx4`), - Response: abci.ResponseDeliverTx{ - GasWanted: 800, - GasUsed: 700, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data4`), - }, + }, + { + Height: 104, + Index: 4, + Tx: []byte(`sampleTx4`), + Response: abci.ResponseDeliverTx{ + GasWanted: 800, + GasUsed: 700, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data4`), }, }, - } + }, +} + +func TestTxFilters(t *testing.T) { + t.Parallel() f := NewTxFilter() for _, tx := range txs { @@ -143,10 +146,53 @@ func TestTxFilters(t *testing.T) { f.ClearConditions() - // query-like method chaining + // query-like method chaining. order of methods doesn't really matter (except `Apply`) query := f.Height(101).Index(1).GasUsed(1000, 1200).Apply() require.Len(t, query, 1) assert.Equal(t, txs[1], query[0]) f.ClearConditions() } + +func TestTxFilterWithNoConditionsShouldReturnsAllTxs(t *testing.T) { + t.Parallel() + + f := NewTxFilter() + for _, tx := range txs { + f.UpdateWithTx(tx) + } + + all := f.Apply() + require.Len(t, all, len(txs)) +} + +func TestApplyWithMultipleGoroutines(t *testing.T) { + t.Parallel() + + f := NewTxFilter() + + for _, tx := range txs { + f.UpdateWithTx(tx) + } + expected := f.Height(100).Apply() + + var wg sync.WaitGroup + + results := make([][]*types.TxResult, 10) + + for i := 0; i < 10; i++ { + wg.Add(1) + + go func(i int) { + defer wg.Done() + + results[i] = f.Height(100).Apply() + }(i) + } + + wg.Wait() + + for i := 0; i < 10; i++ { + assert.Equal(t, expected, results[i]) + } +} \ No newline at end of file From 49923b1563d05b4ec101717cf3e006c2c8bbefcf Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 25 Jan 2024 17:41:47 +0900 Subject: [PATCH 07/19] fixup --- serve/filters/filter/tx_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index 0120bac0..618622da 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -174,12 +174,12 @@ func TestApplyWithMultipleGoroutines(t *testing.T) { for _, tx := range txs { f.UpdateWithTx(tx) } + + results := make([][]*types.TxResult, 10) expected := f.Height(100).Apply() var wg sync.WaitGroup - results := make([][]*types.TxResult, 10) - for i := 0; i < 10; i++ { wg.Add(1) From 86eff859ba717a256f67b2584fa08489d2549351 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 25 Jan 2024 17:44:35 +0900 Subject: [PATCH 08/19] lint --- serve/filters/filter/tx_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index 618622da..f294e627 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -195,4 +195,4 @@ func TestApplyWithMultipleGoroutines(t *testing.T) { for i := 0; i < 10; i++ { assert.Equal(t, expected, results[i]) } -} \ No newline at end of file +} From 727c48ad2a1ded420f78f2496db5124b1a6a8c22 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Thu, 25 Jan 2024 17:52:29 +0900 Subject: [PATCH 09/19] fixup --- serve/filters/filter/tx.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 463e7519..4c426039 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -83,6 +83,9 @@ func (tf *TxFilter) ClearConditions() *TxFilter { // // It appends a height-based condition to the conditions slice. func (tf *TxFilter) Height(height int64) *TxFilter { + tf.Lock() + defer tf.Unlock() + cond := func(txr *types.TxResult) bool { return txr.Height == height } @@ -93,6 +96,9 @@ func (tf *TxFilter) Height(height int64) *TxFilter { // Index sets a filter for the index of the transactions. func (tf *TxFilter) Index(index uint32) *TxFilter { + tf.Lock() + defer tf.Unlock() + cond := func(txr *types.TxResult) bool { return txr.Index == index } @@ -103,6 +109,9 @@ func (tf *TxFilter) Index(index uint32) *TxFilter { // GasUsed sets a filter for the gas used by transactions. func (tf *TxFilter) GasUsed(min, max int64) *TxFilter { + tf.Lock() + defer tf.Unlock() + cond := func(txr *types.TxResult) bool { return txr.Response.GasUsed >= min && txr.Response.GasUsed <= max } @@ -113,6 +122,9 @@ func (tf *TxFilter) GasUsed(min, max int64) *TxFilter { // GasWanted sets a filter for the gas wanted by transactions. func (tf *TxFilter) GasWanted(min, max int64) *TxFilter { + tf.Lock() + defer tf.Unlock() + cond := func(txr *types.TxResult) bool { return txr.Response.GasWanted >= min && txr.Response.GasWanted <= max } From e66f58a1ded7a0da1fc6dba438aded27a76eb6b3 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Fri, 26 Jan 2024 17:30:19 +0900 Subject: [PATCH 10/19] add filter priority order --- serve/filters/filter/tx.go | 93 +++++++++++++++++++++++---------- serve/filters/filter/tx_test.go | 20 +++++++ 2 files changed, 85 insertions(+), 28 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 4c426039..47d333be 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -1,9 +1,29 @@ package filter import ( + "sort" + "github.com/gnolang/gno/tm2/pkg/bft/types" ) +// filterPriority defines the priority of a filter condition. +// The higher the priority, the earlier the condition is applied. +// +// This concept is borrowed from operator precedence. +type filterPriority int + +const ( + HeightPriority filterPriority = iota // highest priority + IndexPriority + GasUsedPriority + GasWantedPriority // lowest priority +) + +type condition struct { + filter func(*types.TxResult) bool + priority filterPriority +} + // TxFilter holds a slice of transaction results. // It provides methods to manipulate and query the transactions. type TxFilter struct { @@ -11,7 +31,7 @@ type TxFilter struct { // txrs represents the transactions in the filter. txrs []*types.TxResult // conditions holds the filtering conditions. - conditions []func(*types.TxResult) bool + conditions []condition } // NewTxFilter creates a new TxFilter object. @@ -19,7 +39,7 @@ func NewTxFilter() *TxFilter { return &TxFilter{ baseFilter: newBaseFilter(TxFilterType), txrs: make([]*types.TxResult, 0), - conditions: make([]func(*types.TxResult) bool, 0), + conditions: make([]condition, 0), } } @@ -83,52 +103,52 @@ func (tf *TxFilter) ClearConditions() *TxFilter { // // It appends a height-based condition to the conditions slice. func (tf *TxFilter) Height(height int64) *TxFilter { - tf.Lock() - defer tf.Unlock() - - cond := func(txr *types.TxResult) bool { - return txr.Height == height + cond := condition { + func(txr *types.TxResult) bool { + return txr.Height == height + }, + HeightPriority, } - tf.conditions = append(tf.conditions, cond) + tf.insertConditionInOrder(cond) return tf } // Index sets a filter for the index of the transactions. func (tf *TxFilter) Index(index uint32) *TxFilter { - tf.Lock() - defer tf.Unlock() - - cond := func(txr *types.TxResult) bool { - return txr.Index == index + cond := condition { + func(txr *types.TxResult) bool { + return txr.Index == index + }, + IndexPriority, } - tf.conditions = append(tf.conditions, cond) + tf.insertConditionInOrder(cond) return tf } // GasUsed sets a filter for the gas used by transactions. func (tf *TxFilter) GasUsed(min, max int64) *TxFilter { - tf.Lock() - defer tf.Unlock() - - cond := func(txr *types.TxResult) bool { - return txr.Response.GasUsed >= min && txr.Response.GasUsed <= max + cond := condition { + func(txr *types.TxResult) bool { + return txr.Response.GasUsed >= min && txr.Response.GasUsed <= max + }, + GasUsedPriority, } - tf.conditions = append(tf.conditions, cond) + tf.insertConditionInOrder(cond) return tf } // GasWanted sets a filter for the gas wanted by transactions. func (tf *TxFilter) GasWanted(min, max int64) *TxFilter { - tf.Lock() - defer tf.Unlock() - - cond := func(txr *types.TxResult) bool { - return txr.Response.GasWanted >= min && txr.Response.GasWanted <= max + cond := condition { + func(txr *types.TxResult) bool { + return txr.Response.GasWanted >= min && txr.Response.GasWanted <= max + }, + GasWantedPriority, } - tf.conditions = append(tf.conditions, cond) + tf.insertConditionInOrder(cond) return tf } @@ -140,7 +160,7 @@ func (tf *TxFilter) Apply() []*types.TxResult { tf.Lock() defer tf.Unlock() - if tf.conditions == nil { + if len(tf.conditions) == 0 { return tf.txrs } @@ -150,7 +170,7 @@ func (tf *TxFilter) Apply() []*types.TxResult { pass := true for _, condition := range tf.conditions { - if !condition(txr) { + if !condition.filter(txr) { pass = false break @@ -164,3 +184,20 @@ func (tf *TxFilter) Apply() []*types.TxResult { return filtered } + +// insertConditionInOrder adds a new condition to the conditions slice. +// +// It places the condition at the right position based on its priority, +// ensuring the slice is always ordered without needing to sort it entirely each time. +// +// complexity: O(log n) + O(n) +func (tf *TxFilter) insertConditionInOrder(cond condition) { + tf.Lock() + defer tf.Unlock() + + i := sort.Search(len(tf.conditions), func(i int) bool { + return tf.conditions[i].priority >= cond.priority + }) + + tf.conditions = append(tf.conditions[:i], append([]condition{cond}, tf.conditions[i:]...)...) +} diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index f294e627..e006ab00 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -196,3 +196,23 @@ func TestApplyWithMultipleGoroutines(t *testing.T) { assert.Equal(t, expected, results[i]) } } + +func TestTxFilterReOrderedByPriority(t *testing.T) { + t.Parallel() + + f := NewTxFilter() + + f.Index(1).GasUsed(800, 1200).Height(101) + + // checks the state of the conditions before prioritizing them correctly + require.Len(t, f.conditions, 3) + + // after `Apply`, check to see if the conditions (filters) are ordered by priority correctly + expectedOrder := []filterPriority{HeightPriority, IndexPriority, GasUsedPriority} + + for i, cond := range f.conditions { + if cond.priority != expectedOrder[i] { + t.Errorf("Condition at position %d has wrong priority before Apply. Got %v, want %v", i, cond.priority, expectedOrder[i]) + } + } +} From c64e00a6794351ad6bebac6ed84e6ff123c73466 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Mon, 29 Jan 2024 12:23:20 +0900 Subject: [PATCH 11/19] lint --- serve/filters/filter/tx.go | 16 ++++++++-------- serve/filters/filter/tx_test.go | 7 ++++++- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 47d333be..7ee0e1b4 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -13,15 +13,15 @@ import ( type filterPriority int const ( - HeightPriority filterPriority = iota // highest priority + HeightPriority filterPriority = iota // highest priority IndexPriority GasUsedPriority - GasWantedPriority // lowest priority + GasWantedPriority // lowest priority ) type condition struct { - filter func(*types.TxResult) bool - priority filterPriority + filter func(*types.TxResult) bool + priority filterPriority } // TxFilter holds a slice of transaction results. @@ -103,7 +103,7 @@ func (tf *TxFilter) ClearConditions() *TxFilter { // // It appends a height-based condition to the conditions slice. func (tf *TxFilter) Height(height int64) *TxFilter { - cond := condition { + cond := condition{ func(txr *types.TxResult) bool { return txr.Height == height }, @@ -116,7 +116,7 @@ func (tf *TxFilter) Height(height int64) *TxFilter { // Index sets a filter for the index of the transactions. func (tf *TxFilter) Index(index uint32) *TxFilter { - cond := condition { + cond := condition{ func(txr *types.TxResult) bool { return txr.Index == index }, @@ -129,7 +129,7 @@ func (tf *TxFilter) Index(index uint32) *TxFilter { // GasUsed sets a filter for the gas used by transactions. func (tf *TxFilter) GasUsed(min, max int64) *TxFilter { - cond := condition { + cond := condition{ func(txr *types.TxResult) bool { return txr.Response.GasUsed >= min && txr.Response.GasUsed <= max }, @@ -142,7 +142,7 @@ func (tf *TxFilter) GasUsed(min, max int64) *TxFilter { // GasWanted sets a filter for the gas wanted by transactions. func (tf *TxFilter) GasWanted(min, max int64) *TxFilter { - cond := condition { + cond := condition{ func(txr *types.TxResult) bool { return txr.Response.GasWanted >= min && txr.Response.GasWanted <= max }, diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index e006ab00..6f3e75e2 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -212,7 +212,12 @@ func TestTxFilterReOrderedByPriority(t *testing.T) { for i, cond := range f.conditions { if cond.priority != expectedOrder[i] { - t.Errorf("Condition at position %d has wrong priority before Apply. Got %v, want %v", i, cond.priority, expectedOrder[i]) + t.Errorf( + "Condition at position %d has wrong priority before Apply. Got %v, want %v", + i, + cond.priority, + expectedOrder[i], + ) } } } From 645eb41cf009e4194acab597aad902d39cc36af6 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Mon, 29 Jan 2024 12:26:29 +0900 Subject: [PATCH 12/19] fixup --- serve/filters/filter/tx_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index 6f3e75e2..2ee04f5b 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -213,7 +213,7 @@ func TestTxFilterReOrderedByPriority(t *testing.T) { for i, cond := range f.conditions { if cond.priority != expectedOrder[i] { t.Errorf( - "Condition at position %d has wrong priority before Apply. Got %v, want %v", + "Condition at position %d has wrong priority before Apply. Got %v, want %v", i, cond.priority, expectedOrder[i], From 1e56a53459168b338b955e9f8a32fa5d8137ca6a Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Tue, 26 Mar 2024 17:30:27 +0900 Subject: [PATCH 13/19] save: use insertion queue --- serve/filters/filter/tx.go | 67 ++++---- serve/filters/filter/tx_test.go | 289 +++++++++++++++++++------------- 2 files changed, 212 insertions(+), 144 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 7ee0e1b4..85f1010e 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -4,6 +4,7 @@ import ( "sort" "github.com/gnolang/gno/tm2/pkg/bft/types" + queue "github.com/madz-lab/insertion-queue" ) // filterPriority defines the priority of a filter condition. @@ -24,36 +25,45 @@ type condition struct { priority filterPriority } +func (c condition) Less(other queue.Item) bool { + otherCond, ok := other.(condition) + if !ok { + panic("invalid type") + } + + return c.priority < otherCond.priority +} + // TxFilter holds a slice of transaction results. // It provides methods to manipulate and query the transactions. type TxFilter struct { *baseFilter - // txrs represents the transactions in the filter. - txrs []*types.TxResult + // txs represents the transactions in the filter. + txs []*types.TxResult // conditions holds the filtering conditions. - conditions []condition + conditions queue.Queue } // NewTxFilter creates a new TxFilter object. func NewTxFilter() *TxFilter { return &TxFilter{ baseFilter: newBaseFilter(TxFilterType), - txrs: make([]*types.TxResult, 0), - conditions: make([]condition, 0), + txs: make([]*types.TxResult, 0), + conditions: queue.NewQueue(), } } // GetHashes iterates over all transactions in the filter and returns their hashes. // // It appends `nil` to the result slice if the transaction or its content is `nil`. -// This ensures that the length og the returned slice matches the number of transactions in the filter. +// This ensures that the length of the returned slice matches the number of transactions in the filter. func (tf *TxFilter) GetHashes() [][]byte { tf.Lock() defer tf.Unlock() - hashes := make([][]byte, 0, len(tf.txrs)) + hashes := make([][]byte, 0, len(tf.txs)) - for _, txr := range tf.txrs { + for _, txr := range tf.txs { if txr == nil || txr.Tx == nil { hashes = append(hashes, nil) @@ -73,10 +83,10 @@ func (tf *TxFilter) GetChanges() any { tf.Lock() defer tf.Unlock() - changes := make([]*types.TxResult, len(tf.txrs)) - copy(changes, tf.txrs) + changes := make([]*types.TxResult, len(tf.txs)) + copy(changes, tf.txs) - tf.txrs = tf.txrs[:0] // reset for new transactions + tf.txs = tf.txs[:0] // reset for new transactions return changes } @@ -86,17 +96,7 @@ func (tf *TxFilter) UpdateWithTx(txr *types.TxResult) { tf.Lock() defer tf.Unlock() - tf.txrs = append(tf.txrs, txr) -} - -// ClearConditions resets the previously set conditions from the filter. -func (tf *TxFilter) ClearConditions() *TxFilter { - tf.Lock() - defer tf.Unlock() - - tf.conditions = nil - - return tf + tf.txs = append(tf.txs, txr) } // Height sets a filter for the height of the transactions. @@ -109,7 +109,7 @@ func (tf *TxFilter) Height(height int64) *TxFilter { }, HeightPriority, } - tf.insertConditionInOrder(cond) + tf.conditions.Push(cond) return tf } @@ -161,18 +161,23 @@ func (tf *TxFilter) Apply() []*types.TxResult { defer tf.Unlock() if len(tf.conditions) == 0 { - return tf.txrs + return tf.txs } var filtered []*types.TxResult - for _, txr := range tf.txrs { + // Convert conditions queue to a slice to iterate in priority order. + condSlice := make([]condition, tf.conditions.Len()) + for i := 0; i < tf.conditions.Len(); i++ { + condSlice[i] = tf.conditions.Index(i).(condition) + } + + for _, txr := range tf.txs { pass := true - for _, condition := range tf.conditions { - if !condition.filter(txr) { + for _, cond := range condSlice { + if !cond.filter(txr) { pass = false - break } } @@ -195,9 +200,9 @@ func (tf *TxFilter) insertConditionInOrder(cond condition) { tf.Lock() defer tf.Unlock() - i := sort.Search(len(tf.conditions), func(i int) bool { - return tf.conditions[i].priority >= cond.priority + i := sort.Search(tf.conditions.Len(), func(i int) bool { + return !tf.conditions.Index(i).(condition).Less(cond) }) - tf.conditions = append(tf.conditions[:i], append([]condition{cond}, tf.conditions[i:]...)...) + tf.conditions = append(tf.conditions[:i], append([]queue.Item{cond}, tf.conditions[i:]...)...) } diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index 2ee04f5b..137affac 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -46,117 +46,117 @@ func TestGetHashAndChanges(t *testing.T) { assert.Equal(t, tx, changes[i]) } - assert.Empty(t, f.txrs) + assert.Empty(t, f.txs) } -var txs = []*types.TxResult{ - { - Height: 100, - Index: 0, - Tx: []byte(`sampleTx0`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1000, - GasUsed: 900, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data0`), - }, - }, - }, - { - Height: 101, - Index: 1, - Tx: []byte(`sampleTx1`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1200, - GasUsed: 1100, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data1`), - }, - }, - }, - { - Height: 102, - Index: 2, - Tx: []byte(`sampleTx2`), - Response: abci.ResponseDeliverTx{ - GasWanted: 900, - GasUsed: 800, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data2`), - }, - }, - }, - { - Height: 103, - Index: 3, - Tx: []byte(`sampleTx3`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1300, - GasUsed: 1250, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data3`), - }, - }, - }, - { - Height: 104, - Index: 4, - Tx: []byte(`sampleTx4`), - Response: abci.ResponseDeliverTx{ - GasWanted: 800, - GasUsed: 700, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data4`), - }, - }, - }, -} +// func TestTxFilters(t *testing.T) { +// t.Parallel() -func TestTxFilters(t *testing.T) { - t.Parallel() +// f := NewTxFilter() +// for _, tx := range txs { +// f.UpdateWithTx(tx) +// } - f := NewTxFilter() - for _, tx := range txs { - f.UpdateWithTx(tx) - } - - height := f.Height(100).Apply() - require.Len(t, height, 1) - assert.Equal(t, txs[0], height[0]) +// height := f.Height(100).Apply() +// require.Len(t, height, 1) +// assert.Equal(t, txs[0], height[0]) - f.ClearConditions() +// f.ClearConditions() - index := f.Index(1).Apply() - require.Len(t, index, 1) - assert.Equal(t, txs[1], index[0]) +// index := f.Index(1).Apply() +// require.Len(t, index, 1) +// assert.Equal(t, txs[1], index[0]) - f.ClearConditions() +// f.ClearConditions() - gasUsed := f.GasUsed(800, 1000).Apply() - require.Len(t, gasUsed, 2) - assert.Equal(t, txs[0], gasUsed[0]) - assert.Equal(t, txs[2], gasUsed[1]) +// gasUsed := f.GasUsed(800, 1000).Apply() +// require.Len(t, gasUsed, 2) +// assert.Equal(t, txs[0], gasUsed[0]) +// assert.Equal(t, txs[2], gasUsed[1]) - f.ClearConditions() +// f.ClearConditions() - gasWanted := f.GasWanted(1000, 1200).Apply() - require.Len(t, gasWanted, 2) - assert.Equal(t, txs[0], gasWanted[0]) - assert.Equal(t, txs[1], gasWanted[1]) +// gasWanted := f.GasWanted(1000, 1200).Apply() +// require.Len(t, gasWanted, 2) +// assert.Equal(t, txs[0], gasWanted[0]) +// assert.Equal(t, txs[1], gasWanted[1]) - f.ClearConditions() +// f.ClearConditions() - // query-like method chaining. order of methods doesn't really matter (except `Apply`) - query := f.Height(101).Index(1).GasUsed(1000, 1200).Apply() - require.Len(t, query, 1) - assert.Equal(t, txs[1], query[0]) +// // query-like method chaining. order of methods doesn't really matter (except `Apply`) +// query := f.Height(101).Index(1).GasUsed(1000, 1200).Apply() +// require.Len(t, query, 1) +// assert.Equal(t, txs[1], query[0]) - f.ClearConditions() -} +// f.ClearConditions() +// } func TestTxFilterWithNoConditionsShouldReturnsAllTxs(t *testing.T) { t.Parallel() + var txs = []*types.TxResult{ + { + Height: 100, + Index: 0, + Tx: []byte(`sampleTx0`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1000, + GasUsed: 900, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data0`), + }, + }, + }, + { + Height: 101, + Index: 1, + Tx: []byte(`sampleTx1`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1200, + GasUsed: 1100, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data1`), + }, + }, + }, + { + Height: 102, + Index: 2, + Tx: []byte(`sampleTx2`), + Response: abci.ResponseDeliverTx{ + GasWanted: 900, + GasUsed: 800, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data2`), + }, + }, + }, + { + Height: 103, + Index: 3, + Tx: []byte(`sampleTx3`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1300, + GasUsed: 1250, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data3`), + }, + }, + }, + { + Height: 104, + Index: 4, + Tx: []byte(`sampleTx4`), + Response: abci.ResponseDeliverTx{ + GasWanted: 800, + GasUsed: 700, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data4`), + }, + }, + }, + } + f := NewTxFilter() for _, tx := range txs { f.UpdateWithTx(tx) @@ -169,6 +169,69 @@ func TestTxFilterWithNoConditionsShouldReturnsAllTxs(t *testing.T) { func TestApplyWithMultipleGoroutines(t *testing.T) { t.Parallel() + var txs = []*types.TxResult{ + { + Height: 100, + Index: 0, + Tx: []byte(`sampleTx0`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1000, + GasUsed: 900, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data0`), + }, + }, + }, + { + Height: 101, + Index: 1, + Tx: []byte(`sampleTx1`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1200, + GasUsed: 1100, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data1`), + }, + }, + }, + { + Height: 102, + Index: 2, + Tx: []byte(`sampleTx2`), + Response: abci.ResponseDeliverTx{ + GasWanted: 900, + GasUsed: 800, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data2`), + }, + }, + }, + { + Height: 103, + Index: 3, + Tx: []byte(`sampleTx3`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1300, + GasUsed: 1250, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data3`), + }, + }, + }, + { + Height: 104, + Index: 4, + Tx: []byte(`sampleTx4`), + Response: abci.ResponseDeliverTx{ + GasWanted: 800, + GasUsed: 700, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data4`), + }, + }, + }, + } + f := NewTxFilter() for _, tx := range txs { @@ -197,27 +260,27 @@ func TestApplyWithMultipleGoroutines(t *testing.T) { } } -func TestTxFilterReOrderedByPriority(t *testing.T) { - t.Parallel() +// func TestTxFilterReOrderedByPriority(t *testing.T) { +// t.Parallel() - f := NewTxFilter() +// f := NewTxFilter() - f.Index(1).GasUsed(800, 1200).Height(101) +// f.Index(1).GasUsed(800, 1200).Height(101) - // checks the state of the conditions before prioritizing them correctly - require.Len(t, f.conditions, 3) +// // checks the state of the conditions before prioritizing them correctly +// require.Len(t, f.conditions, 3) - // after `Apply`, check to see if the conditions (filters) are ordered by priority correctly - expectedOrder := []filterPriority{HeightPriority, IndexPriority, GasUsedPriority} +// // after `Apply`, check to see if the conditions (filters) are ordered by priority correctly +// expectedOrder := []filterPriority{HeightPriority, IndexPriority, GasUsedPriority} - for i, cond := range f.conditions { - if cond.priority != expectedOrder[i] { - t.Errorf( - "Condition at position %d has wrong priority before Apply. Got %v, want %v", - i, - cond.priority, - expectedOrder[i], - ) - } - } -} +// for i, cond := range f.conditions { +// if cond.priority != expectedOrder[i] { +// t.Errorf( +// "Condition at position %d has wrong priority before Apply. Got %v, want %v", +// i, +// cond.priority, +// expectedOrder[i], +// ) +// } +// } +// } From 533ab0cb103905fe0f311a7473e626df0d2b1ffa Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Tue, 26 Mar 2024 20:26:26 +0900 Subject: [PATCH 14/19] filter-as-an-option --- serve/filters/filter/tx.go | 190 +++++--------------- serve/filters/filter/tx_test.go | 300 +++++++++++--------------------- 2 files changed, 147 insertions(+), 343 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 85f1010e..6b0d5a08 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -1,208 +1,100 @@ package filter import ( - "sort" - "github.com/gnolang/gno/tm2/pkg/bft/types" - queue "github.com/madz-lab/insertion-queue" -) - -// filterPriority defines the priority of a filter condition. -// The higher the priority, the earlier the condition is applied. -// -// This concept is borrowed from operator precedence. -type filterPriority int - -const ( - HeightPriority filterPriority = iota // highest priority - IndexPriority - GasUsedPriority - GasWantedPriority // lowest priority ) -type condition struct { - filter func(*types.TxResult) bool - priority filterPriority -} - -func (c condition) Less(other queue.Item) bool { - otherCond, ok := other.(condition) - if !ok { - panic("invalid type") - } - - return c.priority < otherCond.priority +type FilterOptions struct { + Height int64 + Index uint32 + GasUsed struct{ Min, Max int64 } + GasWanted struct{ Min, Max int64 } } // TxFilter holds a slice of transaction results. // It provides methods to manipulate and query the transactions. type TxFilter struct { *baseFilter - // txs represents the transactions in the filter. - txs []*types.TxResult - // conditions holds the filtering conditions. - conditions queue.Queue + txs []*types.TxResult + options FilterOptions } // NewTxFilter creates a new TxFilter object. -func NewTxFilter() *TxFilter { +func NewTxFilter(opts FilterOptions) *TxFilter { return &TxFilter{ baseFilter: newBaseFilter(TxFilterType), txs: make([]*types.TxResult, 0), - conditions: queue.NewQueue(), + options: opts, } } // GetHashes iterates over all transactions in the filter and returns their hashes. -// -// It appends `nil` to the result slice if the transaction or its content is `nil`. -// This ensures that the length of the returned slice matches the number of transactions in the filter. func (tf *TxFilter) GetHashes() [][]byte { tf.Lock() defer tf.Unlock() - hashes := make([][]byte, 0, len(tf.txs)) - + var hashes [][]byte for _, txr := range tf.txs { - if txr == nil || txr.Tx == nil { - hashes = append(hashes, nil) + var hash []byte - continue + if txr != nil && txr.Tx != nil { + hash = txr.Tx.Hash() } - hashes = append(hashes, txr.Tx.Hash()) + hashes = append(hashes, hash) } return hashes } -// GetChanges retrieves and returns all the transactions in the filter. -// -// It also resets the transactions and prepare the filter for new transactions. -func (tf *TxFilter) GetChanges() any { - tf.Lock() - defer tf.Unlock() - - changes := make([]*types.TxResult, len(tf.txs)) - copy(changes, tf.txs) - - tf.txs = tf.txs[:0] // reset for new transactions - - return changes -} - -// UpdateWithTx adds a transaction to the filter. -func (tf *TxFilter) UpdateWithTx(txr *types.TxResult) { +func (tf *TxFilter) UpdateWithTx(tx *types.TxResult) { tf.Lock() defer tf.Unlock() - tf.txs = append(tf.txs, txr) -} - -// Height sets a filter for the height of the transactions. -// -// It appends a height-based condition to the conditions slice. -func (tf *TxFilter) Height(height int64) *TxFilter { - cond := condition{ - func(txr *types.TxResult) bool { - return txr.Height == height - }, - HeightPriority, - } - tf.conditions.Push(cond) - - return tf -} - -// Index sets a filter for the index of the transactions. -func (tf *TxFilter) Index(index uint32) *TxFilter { - cond := condition{ - func(txr *types.TxResult) bool { - return txr.Index == index - }, - IndexPriority, - } - tf.insertConditionInOrder(cond) - - return tf -} - -// GasUsed sets a filter for the gas used by transactions. -func (tf *TxFilter) GasUsed(min, max int64) *TxFilter { - cond := condition{ - func(txr *types.TxResult) bool { - return txr.Response.GasUsed >= min && txr.Response.GasUsed <= max - }, - GasUsedPriority, - } - tf.insertConditionInOrder(cond) - - return tf -} - -// GasWanted sets a filter for the gas wanted by transactions. -func (tf *TxFilter) GasWanted(min, max int64) *TxFilter { - cond := condition{ - func(txr *types.TxResult) bool { - return txr.Response.GasWanted >= min && txr.Response.GasWanted <= max - }, - GasWantedPriority, - } - tf.insertConditionInOrder(cond) - - return tf + tf.txs = append(tf.txs, tx) } // Apply applies all added conditions to the transactions in the filter. // -// It returns a slice of `TxResult` that satisfy all the conditions. +// It returns a slice of `TxResult` that satisfy all the conditions. If no conditions are set, +// it returns all transactions in the filter. func (tf *TxFilter) Apply() []*types.TxResult { tf.Lock() defer tf.Unlock() - if len(tf.conditions) == 0 { - return tf.txs - } + return checkOpts(tf.txs, tf.options) +} +func checkOpts(txs []*types.TxResult, opts FilterOptions) []*types.TxResult { var filtered []*types.TxResult - // Convert conditions queue to a slice to iterate in priority order. - condSlice := make([]condition, tf.conditions.Len()) - for i := 0; i < tf.conditions.Len(); i++ { - condSlice[i] = tf.conditions.Index(i).(condition) - } + for _, tx := range txs { + if opts.Height != 0 && tx.Height != opts.Height { + continue + } - for _, txr := range tf.txs { - pass := true + if opts.Index != 0 && tx.Index != opts.Index { + continue + } - for _, cond := range condSlice { - if !cond.filter(txr) { - pass = false - break - } + if opts.GasUsed.Max != 0 && tx.Response.GasUsed > opts.GasUsed.Max { + continue } - if pass { - filtered = append(filtered, txr) + if opts.GasUsed.Min != 0 && tx.Response.GasUsed < opts.GasUsed.Min { + continue } - } - return filtered -} + if opts.GasWanted.Max != 0 && tx.Response.GasWanted > opts.GasWanted.Max { + continue + } -// insertConditionInOrder adds a new condition to the conditions slice. -// -// It places the condition at the right position based on its priority, -// ensuring the slice is always ordered without needing to sort it entirely each time. -// -// complexity: O(log n) + O(n) -func (tf *TxFilter) insertConditionInOrder(cond condition) { - tf.Lock() - defer tf.Unlock() + if opts.GasWanted.Min != 0 && tx.Response.GasWanted < opts.GasWanted.Min { + continue + } - i := sort.Search(tf.conditions.Len(), func(i int) bool { - return !tf.conditions.Index(i).(condition).Less(cond) - }) + filtered = append(filtered, tx) + } - tf.conditions = append(tf.conditions[:i], append([]queue.Item{cond}, tf.conditions[i:]...)...) + return filtered } diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index 137affac..2d831467 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -1,8 +1,9 @@ package filter import ( - "sync" + "fmt" "testing" + "time" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/bft/types" @@ -10,10 +11,10 @@ import ( "github.com/stretchr/testify/require" ) -func TestGetHashAndChanges(t *testing.T) { +func TestGetHashes(t *testing.T) { t.Parallel() - txs := []*types.TxResult{ + var txs = []*types.TxResult{ {Tx: []byte(`c25dda249cdece9d908cc33adcd16aa05e20290f`)}, {Tx: []byte(`71ac9eed6a76a285ae035fe84a251d56ae9485a4`)}, {Tx: []byte(`356a192b7913b04c54574d18c28d46e6395428ab`)}, @@ -24,74 +25,19 @@ func TestGetHashAndChanges(t *testing.T) { {Tx: []byte(`c1dfd96eea8cc2b62785275bca38ac261256e278`)}, } - // create a new tx filter - f := NewTxFilter() - assert.Equal(t, TxFilterType, f.GetType()) - + f := NewTxFilter(FilterOptions{}) for _, tx := range txs { f.UpdateWithTx(tx) } - // get the hashes of the txs hashes := f.GetHashes() - for i, tx := range txs { - assert.Equal(t, tx.Tx.Hash(), hashes[i]) - } - - // get the chages from the filter - changes := f.GetChanges().([]*types.TxResult) - require.Len(t, changes, len(txs)) - - for i, tx := range txs { - assert.Equal(t, tx, changes[i]) + require.Len(t, hashes, 8, fmt.Sprintf("There should be 8 hashes in the filter: %v", len(hashes))) + for i, hs := range hashes { + assert.Equal(t, txs[i].Tx.Hash(), hs, fmt.Sprintf("The hash should match the expected hash: %v", txs[i].Tx.Hash())) } - - assert.Empty(t, f.txs) } -// func TestTxFilters(t *testing.T) { -// t.Parallel() - -// f := NewTxFilter() -// for _, tx := range txs { -// f.UpdateWithTx(tx) -// } - -// height := f.Height(100).Apply() -// require.Len(t, height, 1) -// assert.Equal(t, txs[0], height[0]) - -// f.ClearConditions() - -// index := f.Index(1).Apply() -// require.Len(t, index, 1) -// assert.Equal(t, txs[1], index[0]) - -// f.ClearConditions() - -// gasUsed := f.GasUsed(800, 1000).Apply() -// require.Len(t, gasUsed, 2) -// assert.Equal(t, txs[0], gasUsed[0]) -// assert.Equal(t, txs[2], gasUsed[1]) - -// f.ClearConditions() - -// gasWanted := f.GasWanted(1000, 1200).Apply() -// require.Len(t, gasWanted, 2) -// assert.Equal(t, txs[0], gasWanted[0]) -// assert.Equal(t, txs[1], gasWanted[1]) - -// f.ClearConditions() - -// // query-like method chaining. order of methods doesn't really matter (except `Apply`) -// query := f.Height(101).Index(1).GasUsed(1000, 1200).Apply() -// require.Len(t, query, 1) -// assert.Equal(t, txs[1], query[0]) - -// f.ClearConditions() -// } - -func TestTxFilterWithNoConditionsShouldReturnsAllTxs(t *testing.T) { +func TestApplyFilters(t *testing.T) { t.Parallel() var txs = []*types.TxResult{ @@ -124,8 +70,8 @@ func TestTxFilterWithNoConditionsShouldReturnsAllTxs(t *testing.T) { Index: 2, Tx: []byte(`sampleTx2`), Response: abci.ResponseDeliverTx{ - GasWanted: 900, - GasUsed: 800, + GasWanted: 1000, + GasUsed: 1400, ResponseBase: abci.ResponseBase{ Data: []byte(`data2`), }, @@ -136,151 +82,117 @@ func TestTxFilterWithNoConditionsShouldReturnsAllTxs(t *testing.T) { Index: 3, Tx: []byte(`sampleTx3`), Response: abci.ResponseDeliverTx{ - GasWanted: 1300, - GasUsed: 1250, + GasWanted: 1200, + GasUsed: 900, ResponseBase: abci.ResponseBase{ Data: []byte(`data3`), }, }, }, - { - Height: 104, - Index: 4, - Tx: []byte(`sampleTx4`), - Response: abci.ResponseDeliverTx{ - GasWanted: 800, - GasUsed: 700, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data4`), - }, - }, - }, - } - - f := NewTxFilter() - for _, tx := range txs { - f.UpdateWithTx(tx) + { + Height: 104, + Index: 4, + Tx: []byte(`sampleTx4`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1100, + GasUsed: 1000, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data4`), + }, + }, + }, } - all := f.Apply() - require.Len(t, all, len(txs)) -} - -func TestApplyWithMultipleGoroutines(t *testing.T) { - t.Parallel() - - var txs = []*types.TxResult{ - { - Height: 100, - Index: 0, - Tx: []byte(`sampleTx0`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1000, - GasUsed: 900, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data0`), - }, - }, - }, + tests := []struct { + name string + options FilterOptions + expected []*types.TxResult + }{ { - Height: 101, - Index: 1, - Tx: []byte(`sampleTx1`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1200, - GasUsed: 1100, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data1`), - }, - }, + name: "no filter", + options: FilterOptions{}, + expected: txs, }, - { - Height: 102, - Index: 2, - Tx: []byte(`sampleTx2`), - Response: abci.ResponseDeliverTx{ - GasWanted: 900, - GasUsed: 800, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data2`), - }, + { // took 34.5µs + name: "filter by index", + options: FilterOptions{ + Index: 1, }, + expected: []*types.TxResult{txs[1]}, }, - { - Height: 103, - Index: 3, - Tx: []byte(`sampleTx3`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1300, - GasUsed: 1250, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data3`), - }, + { // took 29.583µs + name: "filter by height and gas used", + options: FilterOptions{ + Height: 100, + GasUsed: struct{ Min, Max int64 }{900, 1000}, }, + expected: []*types.TxResult{txs[0]}, }, - { - Height: 104, - Index: 4, - Tx: []byte(`sampleTx4`), - Response: abci.ResponseDeliverTx{ - GasWanted: 800, - GasUsed: 700, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data4`), - }, + { // took 37.292µs + name: "filter by gas wanted 1", + options: FilterOptions{ + GasWanted: struct{ Min, Max int64 }{1100, 1200}, }, + expected: []*types.TxResult{txs[1], txs[3], txs[4]}, }, + { // took 36.583µs + name: "filter by gas used 2", + options: FilterOptions{ + GasUsed: struct{ Min, Max int64 }{900, 1000}, + }, + expected: []*types.TxResult{txs[0], txs[3], txs[4]}, + }, + { // took 15.417µs + name: "filter by gas wanted is invalid", + options: FilterOptions{ + GasWanted: struct{ Min, Max int64 }{1200, 1100}, + }, + expected: []*types.TxResult{}, + }, + { // took 15.166µs + name: "gas used filter is invalid", + options: FilterOptions{ + GasUsed: struct{ Min, Max int64 }{1000, 900}, + }, + expected: []*types.TxResult{}, + }, + { // took 36.834µs + name: "use all filters", + options: FilterOptions{ + Height: 100, + Index: 0, + GasUsed: struct{ Min, Max int64 }{900, 1000}, + GasWanted: struct{ Min, Max int64 }{1000, 1100}, + }, + expected: []*types.TxResult{txs[0]}, + }, + { // took 27.167µs + name: "use all filters but sequence is flipped", + options: FilterOptions{ + GasWanted: struct{ Min, Max int64 }{1000, 1100}, + GasUsed: struct{ Min, Max int64 }{900, 1000}, + Index: 0, + Height: 100, + }, + expected: []*types.TxResult{txs[0]}, + }, } - f := NewTxFilter() - - for _, tx := range txs { - f.UpdateWithTx(tx) - } - - results := make([][]*types.TxResult, 10) - expected := f.Height(100).Apply() - - var wg sync.WaitGroup - - for i := 0; i < 10; i++ { - wg.Add(1) - - go func(i int) { - defer wg.Done() - - results[i] = f.Height(100).Apply() - }(i) - } - - wg.Wait() - - for i := 0; i < 10; i++ { - assert.Equal(t, expected, results[i]) + for _, tt := range tests { + start := time.Now() + t.Run(tt.name, func(t *testing.T) { + f := NewTxFilter(tt.options) + for _, tx := range txs { + f.UpdateWithTx(tx) + } + + filtered := f.Apply() + require.Len(t, filtered, len(tt.expected), fmt.Sprintf("There should be one transaction after applying filters: %v", len(tt.expected))) + for i, tx := range filtered { + assert.Equal(t, tt.expected[i], tx, fmt.Sprintf("The filtered transaction should match the expected transaction: %v", tt.expected[i])) + } + + fmt.Printf("took %v\n", time.Since(start)) + }) } } - -// func TestTxFilterReOrderedByPriority(t *testing.T) { -// t.Parallel() - -// f := NewTxFilter() - -// f.Index(1).GasUsed(800, 1200).Height(101) - -// // checks the state of the conditions before prioritizing them correctly -// require.Len(t, f.conditions, 3) - -// // after `Apply`, check to see if the conditions (filters) are ordered by priority correctly -// expectedOrder := []filterPriority{HeightPriority, IndexPriority, GasUsedPriority} - -// for i, cond := range f.conditions { -// if cond.priority != expectedOrder[i] { -// t.Errorf( -// "Condition at position %d has wrong priority before Apply. Got %v, want %v", -// i, -// cond.priority, -// expectedOrder[i], -// ) -// } -// } -// } From e62260a08f862605cfac56dafe37f92e001549e7 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Wed, 27 Mar 2024 10:16:46 +0900 Subject: [PATCH 15/19] fmt --- serve/filters/filter/tx.go | 8 +-- serve/filters/filter/tx_test.go | 114 ++++++++++++++++---------------- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 6b0d5a08..eca27ade 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -5,10 +5,10 @@ import ( ) type FilterOptions struct { - Height int64 - Index uint32 - GasUsed struct{ Min, Max int64 } - GasWanted struct{ Min, Max int64 } + Height int64 + Index uint32 + GasUsed struct{ Min, Max int64 } + GasWanted struct{ Min, Max int64 } } // TxFilter holds a slice of transaction results. diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index 2d831467..da10efd2 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -14,7 +14,7 @@ import ( func TestGetHashes(t *testing.T) { t.Parallel() - var txs = []*types.TxResult{ + txs := []*types.TxResult{ {Tx: []byte(`c25dda249cdece9d908cc33adcd16aa05e20290f`)}, {Tx: []byte(`71ac9eed6a76a285ae035fe84a251d56ae9485a4`)}, {Tx: []byte(`356a192b7913b04c54574d18c28d46e6395428ab`)}, @@ -40,7 +40,7 @@ func TestGetHashes(t *testing.T) { func TestApplyFilters(t *testing.T) { t.Parallel() - var txs = []*types.TxResult{ + txs := []*types.TxResult{ { Height: 100, Index: 0, @@ -89,18 +89,18 @@ func TestApplyFilters(t *testing.T) { }, }, }, - { - Height: 104, - Index: 4, - Tx: []byte(`sampleTx4`), - Response: abci.ResponseDeliverTx{ - GasWanted: 1100, - GasUsed: 1000, - ResponseBase: abci.ResponseBase{ - Data: []byte(`data4`), - }, - }, - }, + { + Height: 104, + Index: 4, + Tx: []byte(`sampleTx4`), + Response: abci.ResponseDeliverTx{ + GasWanted: 1100, + GasUsed: 1000, + ResponseBase: abci.ResponseBase{ + Data: []byte(`data4`), + }, + }, + }, } tests := []struct { @@ -113,14 +113,14 @@ func TestApplyFilters(t *testing.T) { options: FilterOptions{}, expected: txs, }, - { // took 34.5µs + { name: "filter by index", options: FilterOptions{ Index: 1, }, expected: []*types.TxResult{txs[1]}, }, - { // took 29.583µs + { name: "filter by height and gas used", options: FilterOptions{ Height: 100, @@ -128,54 +128,54 @@ func TestApplyFilters(t *testing.T) { }, expected: []*types.TxResult{txs[0]}, }, - { // took 37.292µs + { name: "filter by gas wanted 1", options: FilterOptions{ GasWanted: struct{ Min, Max int64 }{1100, 1200}, }, expected: []*types.TxResult{txs[1], txs[3], txs[4]}, }, - { // took 36.583µs - name: "filter by gas used 2", - options: FilterOptions{ - GasUsed: struct{ Min, Max int64 }{900, 1000}, - }, - expected: []*types.TxResult{txs[0], txs[3], txs[4]}, - }, - { // took 15.417µs - name: "filter by gas wanted is invalid", - options: FilterOptions{ - GasWanted: struct{ Min, Max int64 }{1200, 1100}, - }, - expected: []*types.TxResult{}, - }, - { // took 15.166µs - name: "gas used filter is invalid", - options: FilterOptions{ - GasUsed: struct{ Min, Max int64 }{1000, 900}, - }, - expected: []*types.TxResult{}, - }, - { // took 36.834µs - name: "use all filters", - options: FilterOptions{ - Height: 100, - Index: 0, - GasUsed: struct{ Min, Max int64 }{900, 1000}, - GasWanted: struct{ Min, Max int64 }{1000, 1100}, - }, - expected: []*types.TxResult{txs[0]}, - }, - { // took 27.167µs - name: "use all filters but sequence is flipped", - options: FilterOptions{ - GasWanted: struct{ Min, Max int64 }{1000, 1100}, - GasUsed: struct{ Min, Max int64 }{900, 1000}, - Index: 0, - Height: 100, - }, + { + name: "filter by gas used 2", + options: FilterOptions{ + GasUsed: struct{ Min, Max int64 }{900, 1000}, + }, + expected: []*types.TxResult{txs[0], txs[3], txs[4]}, + }, + { + name: "filter by gas wanted is invalid", + options: FilterOptions{ + GasWanted: struct{ Min, Max int64 }{1200, 1100}, + }, + expected: []*types.TxResult{}, + }, + { + name: "gas used filter is invalid", + options: FilterOptions{ + GasUsed: struct{ Min, Max int64 }{1000, 900}, + }, + expected: []*types.TxResult{}, + }, + { + name: "use all filters", + options: FilterOptions{ + Height: 100, + Index: 0, + GasUsed: struct{ Min, Max int64 }{900, 1000}, + GasWanted: struct{ Min, Max int64 }{1000, 1100}, + }, expected: []*types.TxResult{txs[0]}, - }, + }, + { + name: "use all filters but sequence is flipped", + options: FilterOptions{ + GasWanted: struct{ Min, Max int64 }{1000, 1100}, + GasUsed: struct{ Min, Max int64 }{900, 1000}, + Index: 0, + Height: 100, + }, + expected: []*types.TxResult{txs[0]}, + }, } for _, tt := range tests { From ef7cbf339c02f351934767d0b40f424de79a4865 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Wed, 27 Mar 2024 11:20:37 +0900 Subject: [PATCH 16/19] fixup --- serve/filters/filter/tx.go | 19 ++--- serve/filters/filter/tx_test.go | 128 +++++++++++++++++++++++++++----- 2 files changed, 120 insertions(+), 27 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index eca27ade..f7d0ce97 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -4,7 +4,7 @@ import ( "github.com/gnolang/gno/tm2/pkg/bft/types" ) -type FilterOptions struct { +type Options struct { Height int64 Index uint32 GasUsed struct{ Min, Max int64 } @@ -15,16 +15,16 @@ type FilterOptions struct { // It provides methods to manipulate and query the transactions. type TxFilter struct { *baseFilter - txs []*types.TxResult - options FilterOptions + txs []*types.TxResult + opts Options } // NewTxFilter creates a new TxFilter object. -func NewTxFilter(opts FilterOptions) *TxFilter { +func NewTxFilter(opts Options) *TxFilter { return &TxFilter{ baseFilter: newBaseFilter(TxFilterType), txs: make([]*types.TxResult, 0), - options: opts, + opts: opts, } } @@ -33,7 +33,8 @@ func (tf *TxFilter) GetHashes() [][]byte { tf.Lock() defer tf.Unlock() - var hashes [][]byte + hashes := make([][]byte, 0, len(tf.txs)) + for _, txr := range tf.txs { var hash []byte @@ -62,11 +63,11 @@ func (tf *TxFilter) Apply() []*types.TxResult { tf.Lock() defer tf.Unlock() - return checkOpts(tf.txs, tf.options) + return checkOpts(tf.txs, tf.opts) } -func checkOpts(txs []*types.TxResult, opts FilterOptions) []*types.TxResult { - var filtered []*types.TxResult +func checkOpts(txs []*types.TxResult, opts Options) []*types.TxResult { + filtered := make([]*types.TxResult, 0, len(txs)) for _, tx := range txs { if opts.Height != 0 && tx.Height != opts.Height { diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index da10efd2..bba71819 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -3,7 +3,6 @@ package filter import ( "fmt" "testing" - "time" abci "github.com/gnolang/gno/tm2/pkg/bft/abci/types" "github.com/gnolang/gno/tm2/pkg/bft/types" @@ -25,15 +24,23 @@ func TestGetHashes(t *testing.T) { {Tx: []byte(`c1dfd96eea8cc2b62785275bca38ac261256e278`)}, } - f := NewTxFilter(FilterOptions{}) + f := NewTxFilter(Options{}) + for _, tx := range txs { f.UpdateWithTx(tx) } hashes := f.GetHashes() - require.Len(t, hashes, 8, fmt.Sprintf("There should be 8 hashes in the filter: %v", len(hashes))) + require.Len( + t, hashes, 8, + fmt.Sprintf("There should be 8 hashes in the filter: %v", len(hashes)), + ) + for i, hs := range hashes { - assert.Equal(t, txs[i].Tx.Hash(), hs, fmt.Sprintf("The hash should match the expected hash: %v", txs[i].Tx.Hash())) + assert.Equal( + t, txs[i].Tx.Hash(), hs, + fmt.Sprintf("The hash should match the expected hash: %v", txs[i].Tx.Hash()), + ) } } @@ -105,24 +112,24 @@ func TestApplyFilters(t *testing.T) { tests := []struct { name string - options FilterOptions expected []*types.TxResult + options Options }{ { name: "no filter", - options: FilterOptions{}, + options: Options{}, expected: txs, }, { name: "filter by index", - options: FilterOptions{ + options: Options{ Index: 1, }, expected: []*types.TxResult{txs[1]}, }, { name: "filter by height and gas used", - options: FilterOptions{ + options: Options{ Height: 100, GasUsed: struct{ Min, Max int64 }{900, 1000}, }, @@ -130,35 +137,35 @@ func TestApplyFilters(t *testing.T) { }, { name: "filter by gas wanted 1", - options: FilterOptions{ + options: Options{ GasWanted: struct{ Min, Max int64 }{1100, 1200}, }, expected: []*types.TxResult{txs[1], txs[3], txs[4]}, }, { name: "filter by gas used 2", - options: FilterOptions{ + options: Options{ GasUsed: struct{ Min, Max int64 }{900, 1000}, }, expected: []*types.TxResult{txs[0], txs[3], txs[4]}, }, { name: "filter by gas wanted is invalid", - options: FilterOptions{ + options: Options{ GasWanted: struct{ Min, Max int64 }{1200, 1100}, }, expected: []*types.TxResult{}, }, { name: "gas used filter is invalid", - options: FilterOptions{ + options: Options{ GasUsed: struct{ Min, Max int64 }{1000, 900}, }, expected: []*types.TxResult{}, }, { name: "use all filters", - options: FilterOptions{ + options: Options{ Height: 100, Index: 0, GasUsed: struct{ Min, Max int64 }{900, 1000}, @@ -168,7 +175,7 @@ func TestApplyFilters(t *testing.T) { }, { name: "use all filters but sequence is flipped", - options: FilterOptions{ + options: Options{ GasWanted: struct{ Min, Max int64 }{1000, 1100}, GasUsed: struct{ Min, Max int64 }{900, 1000}, Index: 0, @@ -179,20 +186,105 @@ func TestApplyFilters(t *testing.T) { } for _, tt := range tests { - start := time.Now() + tt := tt t.Run(tt.name, func(t *testing.T) { + t.Parallel() + f := NewTxFilter(tt.options) + for _, tx := range txs { f.UpdateWithTx(tx) } filtered := f.Apply() - require.Len(t, filtered, len(tt.expected), fmt.Sprintf("There should be one transaction after applying filters: %v", len(tt.expected))) + require.Len( + t, filtered, len(tt.expected), + fmt.Sprintf( + "There should be one transaction after applying filters: %v", + len(tt.expected), + ), + ) + for i, tx := range filtered { - assert.Equal(t, tt.expected[i], tx, fmt.Sprintf("The filtered transaction should match the expected transaction: %v", tt.expected[i])) + assert.Equal( + t, tt.expected[i], tx, + fmt.Sprintf( + "The filtered transaction should match the expected transaction: %v", + tt.expected[i], + ), + ) + } + }) + } +} + +func TestApplyFiltersWithLargeData(t *testing.T) { + t.Parallel() + + const txCount = 100000 + + txs := make([]*types.TxResult, txCount) + + for i := 0; i < txCount; i++ { + txs[i] = &types.TxResult{ + Height: int64(i / 10000), + Index: uint32(i), + Tx: []byte(fmt.Sprintf("sampleTx%d", i)), + Response: abci.ResponseDeliverTx{ + GasWanted: int64(1000 + i%200), + GasUsed: int64(900 + i%100), + ResponseBase: abci.ResponseBase{ + Data: []byte(fmt.Sprintf("data%d", i)), + }, + }, + } + } + + tests := []struct { + name string + options Options + expected int + }{ + { + name: "no filter", + options: Options{}, + expected: txCount, + }, + { + name: "filter by height", + options: Options{ + Height: 5, + }, + expected: txCount / 10, + }, + { + name: "filter by gas used", + options: Options{ + GasUsed: struct{ Min, Max int64 }{950, 1000}, + }, + expected: txCount / 2, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + f := NewTxFilter(tt.options) + + for _, tx := range txs { + f.UpdateWithTx(tx) } - fmt.Printf("took %v\n", time.Since(start)) + filtered := f.Apply() + require.Len( + t, filtered, tt.expected, + fmt.Sprintf( + "There should be %d transactions after applying filters. got %d", + tt.expected, len(filtered), + ), + ) }) } } From b74d0ab579140890e2a96262f68d078e36397b30 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Mon, 8 Apr 2024 11:44:34 +0900 Subject: [PATCH 17/19] pointer filter --- serve/filters/filter/tx.go | 20 ++++----- serve/filters/filter/tx_test.go | 72 +++++++++++++++++++++++++-------- 2 files changed, 65 insertions(+), 27 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index f7d0ce97..48d02f59 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -5,10 +5,10 @@ import ( ) type Options struct { - Height int64 - Index uint32 - GasUsed struct{ Min, Max int64 } - GasWanted struct{ Min, Max int64 } + Height *int64 + Index *uint32 + GasUsed struct{ Min, Max *int64 } + GasWanted struct{ Min, Max *int64 } } // TxFilter holds a slice of transaction results. @@ -70,27 +70,27 @@ func checkOpts(txs []*types.TxResult, opts Options) []*types.TxResult { filtered := make([]*types.TxResult, 0, len(txs)) for _, tx := range txs { - if opts.Height != 0 && tx.Height != opts.Height { + if opts.Height != nil && tx.Height != *opts.Height { continue } - if opts.Index != 0 && tx.Index != opts.Index { + if opts.Index != nil && tx.Index != *opts.Index { continue } - if opts.GasUsed.Max != 0 && tx.Response.GasUsed > opts.GasUsed.Max { + if opts.GasUsed.Max != nil && tx.Response.GasUsed > *opts.GasUsed.Max { continue } - if opts.GasUsed.Min != 0 && tx.Response.GasUsed < opts.GasUsed.Min { + if opts.GasUsed.Min != nil && tx.Response.GasUsed < *opts.GasUsed.Min { continue } - if opts.GasWanted.Max != 0 && tx.Response.GasWanted > opts.GasWanted.Max { + if opts.GasWanted.Max != nil && tx.Response.GasWanted > *opts.GasWanted.Max { continue } - if opts.GasWanted.Min != 0 && tx.Response.GasWanted < opts.GasWanted.Min { + if opts.GasWanted.Min != nil && tx.Response.GasWanted < *opts.GasWanted.Min { continue } diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index bba71819..a1e81eab 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -123,63 +123,98 @@ func TestApplyFilters(t *testing.T) { { name: "filter by index", options: Options{ - Index: 1, + Index: uint32Ptr(1), }, expected: []*types.TxResult{txs[1]}, }, + { + name: "min gas used is 0", + options: Options{ + GasUsed: struct{ Min, Max *int64 }{int64Ptr(0), int64Ptr(1000)}, + }, + expected: []*types.TxResult{txs[0], txs[3], txs[4]}, + }, { name: "filter by height and gas used", options: Options{ - Height: 100, - GasUsed: struct{ Min, Max int64 }{900, 1000}, + Height: int64Ptr(100), + GasUsed: struct{ Min, Max *int64 }{int64Ptr(900), int64Ptr(1000)}, }, expected: []*types.TxResult{txs[0]}, }, + { + name: "invalid gas used", + options: Options{ + GasUsed: struct{ Min, Max *int64 }{int64Ptr(1000), int64Ptr(900)}, + }, + expected: []*types.TxResult{}, + }, { name: "filter by gas wanted 1", options: Options{ - GasWanted: struct{ Min, Max int64 }{1100, 1200}, + GasWanted: struct{ Min, Max *int64 }{int64Ptr(1100), int64Ptr(1200)}, }, expected: []*types.TxResult{txs[1], txs[3], txs[4]}, }, + { + name: "gas wanted min, max is same value", + options: Options{ + GasWanted: struct{ Min, Max *int64 }{int64Ptr(1000), int64Ptr(1000)}, + }, + expected: []*types.TxResult{txs[0], txs[2]}, + }, { name: "filter by gas used 2", options: Options{ - GasUsed: struct{ Min, Max int64 }{900, 1000}, + GasUsed: struct{ Min, Max *int64 }{int64Ptr(900), int64Ptr(1000)}, }, expected: []*types.TxResult{txs[0], txs[3], txs[4]}, }, + { + name: "gas used min, max is same value", + options: Options{ + GasUsed: struct{ Min, Max *int64 }{int64Ptr(1000), int64Ptr(1000)}, + }, + expected: []*types.TxResult{txs[4]}, + }, { name: "filter by gas wanted is invalid", options: Options{ - GasWanted: struct{ Min, Max int64 }{1200, 1100}, + GasWanted: struct{ Min, Max *int64 }{int64Ptr(1200), int64Ptr(1100)}, }, expected: []*types.TxResult{}, }, { name: "gas used filter is invalid", options: Options{ - GasUsed: struct{ Min, Max int64 }{1000, 900}, + GasUsed: struct{ Min, Max *int64 }{int64Ptr(1000), int64Ptr(900)}, }, expected: []*types.TxResult{}, }, + { + name: "gas used min is nil", + options: Options{ + GasUsed: struct{ Min, Max *int64 }{nil, int64Ptr(1000)}, + }, + expected: []*types.TxResult{txs[0], txs[3], txs[4]}, + }, { name: "use all filters", options: Options{ - Height: 100, - Index: 0, - GasUsed: struct{ Min, Max int64 }{900, 1000}, - GasWanted: struct{ Min, Max int64 }{1000, 1100}, + Height: int64Ptr(100), + Index: uint32Ptr(0), + GasUsed: struct{ Min, Max *int64 }{int64Ptr(900), int64Ptr(1000)}, + GasWanted: struct{ Min, Max *int64 }{int64Ptr(1000), int64Ptr(1100)}, }, expected: []*types.TxResult{txs[0]}, }, { name: "use all filters but sequence is flipped", options: Options{ - GasWanted: struct{ Min, Max int64 }{1000, 1100}, - GasUsed: struct{ Min, Max int64 }{900, 1000}, - Index: 0, - Height: 100, + GasWanted: struct{ Min, Max *int64 }{int64Ptr(1000), int64Ptr(1100)}, + GasUsed: struct{ Min, Max *int64 }{int64Ptr(900), int64Ptr(1000)}, + Index: uint32Ptr(0), + Height: int64Ptr(100), }, expected: []*types.TxResult{txs[0]}, }, @@ -253,14 +288,14 @@ func TestApplyFiltersWithLargeData(t *testing.T) { { name: "filter by height", options: Options{ - Height: 5, + Height: int64Ptr(5), }, expected: txCount / 10, }, { name: "filter by gas used", options: Options{ - GasUsed: struct{ Min, Max int64 }{950, 1000}, + GasUsed: struct{ Min, Max *int64 }{int64Ptr(950), int64Ptr(1000)}, }, expected: txCount / 2, }, @@ -288,3 +323,6 @@ func TestApplyFiltersWithLargeData(t *testing.T) { }) } } + +func int64Ptr(i int64) *int64 { return &i } +func uint32Ptr(i uint32) *uint32 { return &i } From 17455d4207694a13f818ff6209818d78c7cb2f87 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Mon, 8 Apr 2024 14:09:03 +0900 Subject: [PATCH 18/19] fixup --- serve/filters/filter/tx.go | 50 +++++++++++++----------- serve/filters/filter/tx_test.go | 68 +++++++++++++++------------------ 2 files changed, 58 insertions(+), 60 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index 48d02f59..e5d62fe2 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -5,10 +5,9 @@ import ( ) type Options struct { - Height *int64 - Index *uint32 GasUsed struct{ Min, Max *int64 } GasWanted struct{ Min, Max *int64 } + GasLimit struct{ Min, Max *int64 } } // TxFilter holds a slice of transaction results. @@ -58,7 +57,8 @@ func (tf *TxFilter) UpdateWithTx(tx *types.TxResult) { // Apply applies all added conditions to the transactions in the filter. // // It returns a slice of `TxResult` that satisfy all the conditions. If no conditions are set, -// it returns all transactions in the filter. +// it returns all transactions in the filter. Also, if the filter value is invalid filter will not +// be applied. func (tf *TxFilter) Apply() []*types.TxResult { tf.Lock() defer tf.Unlock() @@ -70,32 +70,38 @@ func checkOpts(txs []*types.TxResult, opts Options) []*types.TxResult { filtered := make([]*types.TxResult, 0, len(txs)) for _, tx := range txs { - if opts.Height != nil && tx.Height != *opts.Height { - continue + if checkFilterCondition(tx, opts) { + filtered = append(filtered, tx) } + } - if opts.Index != nil && tx.Index != *opts.Index { - continue - } + return filtered +} - if opts.GasUsed.Max != nil && tx.Response.GasUsed > *opts.GasUsed.Max { - continue - } +func checkFilterCondition(tx *types.TxResult, opts Options) bool { + if opts.GasLimit.Max != nil && tx.Response.GasUsed > *opts.GasLimit.Max { + return false + } - if opts.GasUsed.Min != nil && tx.Response.GasUsed < *opts.GasUsed.Min { - continue - } + if opts.GasLimit.Min != nil && tx.Response.GasUsed < *opts.GasLimit.Min { + return false + } - if opts.GasWanted.Max != nil && tx.Response.GasWanted > *opts.GasWanted.Max { - continue - } + if opts.GasUsed.Max != nil && tx.Response.GasUsed > *opts.GasUsed.Max { + return false + } - if opts.GasWanted.Min != nil && tx.Response.GasWanted < *opts.GasWanted.Min { - continue - } + if opts.GasUsed.Min != nil && tx.Response.GasUsed < *opts.GasUsed.Min { + return false + } - filtered = append(filtered, tx) + if opts.GasWanted.Max != nil && tx.Response.GasWanted > *opts.GasWanted.Max { + return false } - return filtered + if opts.GasWanted.Min != nil && tx.Response.GasWanted < *opts.GasWanted.Min { + return false + } + + return true } diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index a1e81eab..60f6667e 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -120,13 +120,6 @@ func TestApplyFilters(t *testing.T) { options: Options{}, expected: txs, }, - { - name: "filter by index", - options: Options{ - Index: uint32Ptr(1), - }, - expected: []*types.TxResult{txs[1]}, - }, { name: "min gas used is 0", options: Options{ @@ -134,14 +127,6 @@ func TestApplyFilters(t *testing.T) { }, expected: []*types.TxResult{txs[0], txs[3], txs[4]}, }, - { - name: "filter by height and gas used", - options: Options{ - Height: int64Ptr(100), - GasUsed: struct{ Min, Max *int64 }{int64Ptr(900), int64Ptr(1000)}, - }, - expected: []*types.TxResult{txs[0]}, - }, { name: "invalid gas used", options: Options{ @@ -192,31 +177,46 @@ func TestApplyFilters(t *testing.T) { expected: []*types.TxResult{}, }, { - name: "gas used min is nil", + name: "gas limit min value is nil", options: Options{ - GasUsed: struct{ Min, Max *int64 }{nil, int64Ptr(1000)}, + GasLimit: struct{ Min, Max *int64 }{nil, int64Ptr(1000)}, }, expected: []*types.TxResult{txs[0], txs[3], txs[4]}, }, { - name: "use all filters", + name: "gas limit max value is nil", + options: Options{ + GasLimit: struct{ Min, Max *int64 }{int64Ptr(1100), nil}, + }, + expected: []*types.TxResult{txs[1], txs[2]}, + }, + { + name: "gas limit range is valid", options: Options{ - Height: int64Ptr(100), - Index: uint32Ptr(0), - GasUsed: struct{ Min, Max *int64 }{int64Ptr(900), int64Ptr(1000)}, - GasWanted: struct{ Min, Max *int64 }{int64Ptr(1000), int64Ptr(1100)}, + GasLimit: struct{ Min, Max *int64 }{int64Ptr(900), int64Ptr(1000)}, }, - expected: []*types.TxResult{txs[0]}, + expected: []*types.TxResult{txs[0], txs[3], txs[4]}, }, { - name: "use all filters but sequence is flipped", + name: "gas limit both min and max are nil", options: Options{ - GasWanted: struct{ Min, Max *int64 }{int64Ptr(1000), int64Ptr(1100)}, - GasUsed: struct{ Min, Max *int64 }{int64Ptr(900), int64Ptr(1000)}, - Index: uint32Ptr(0), - Height: int64Ptr(100), + GasLimit: struct{ Min, Max *int64 }{nil, nil}, }, - expected: []*types.TxResult{txs[0]}, + expected: txs, + }, + { + name: "gas limit min is larger than max", + options: Options{ + GasLimit: struct{ Min, Max *int64 }{int64Ptr(1000), int64Ptr(900)}, + }, + expected: []*types.TxResult{}, + }, + { + name: "gas used min is nil", + options: Options{ + GasUsed: struct{ Min, Max *int64 }{nil, int64Ptr(1000)}, + }, + expected: []*types.TxResult{txs[0], txs[3], txs[4]}, }, } @@ -285,13 +285,6 @@ func TestApplyFiltersWithLargeData(t *testing.T) { options: Options{}, expected: txCount, }, - { - name: "filter by height", - options: Options{ - Height: int64Ptr(5), - }, - expected: txCount / 10, - }, { name: "filter by gas used", options: Options{ @@ -324,5 +317,4 @@ func TestApplyFiltersWithLargeData(t *testing.T) { } } -func int64Ptr(i int64) *int64 { return &i } -func uint32Ptr(i uint32) *uint32 { return &i } +func int64Ptr(i int64) *int64 { return &i } From 104f7f7158e85a35b63a7c7a027688828ea5e5c8 Mon Sep 17 00:00:00 2001 From: Lee ByeongJun Date: Mon, 8 Apr 2024 14:12:33 +0900 Subject: [PATCH 19/19] fieldalignment --- serve/filters/filter/tx.go | 4 ++-- serve/filters/filter/tx_test.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/serve/filters/filter/tx.go b/serve/filters/filter/tx.go index e5d62fe2..a740be98 100644 --- a/serve/filters/filter/tx.go +++ b/serve/filters/filter/tx.go @@ -13,9 +13,9 @@ type Options struct { // TxFilter holds a slice of transaction results. // It provides methods to manipulate and query the transactions. type TxFilter struct { - *baseFilter - txs []*types.TxResult opts Options + *baseFilter + txs []*types.TxResult } // NewTxFilter creates a new TxFilter object. diff --git a/serve/filters/filter/tx_test.go b/serve/filters/filter/tx_test.go index 60f6667e..0dc02015 100644 --- a/serve/filters/filter/tx_test.go +++ b/serve/filters/filter/tx_test.go @@ -111,9 +111,9 @@ func TestApplyFilters(t *testing.T) { } tests := []struct { + options Options name string expected []*types.TxResult - options Options }{ { name: "no filter", @@ -276,8 +276,8 @@ func TestApplyFiltersWithLargeData(t *testing.T) { } tests := []struct { - name string options Options + name string expected int }{ {