diff --git a/README.md b/README.md index 4ac1280..0b071fd 100644 --- a/README.md +++ b/README.md @@ -113,7 +113,9 @@ plugins: ## Filtering -snek supports filtering events before they are output. +snek supports filtering events before they are output using multiple criteria. An event must match all configured filters to be emitted. +Each filter supports specifying multiple possible values separated by commas. When specifying multiple values for a filter, only one of +the values specified must match an event. You can get a list of all available filter options by using the `-h`/`-help` flag. @@ -172,6 +174,12 @@ Only output `chainsync.transaction` event types $ snek -filter-type chainsync.transaction ``` +Only output `chainsync.rollback` and `chainsync.block` event types + +```bash +$ snek -filter-type chainsync.transaction,chainsync.block +``` + #### Filtering on asset policy Only output transactions involving an asset with a particular policy ID diff --git a/filter/chainsync/chainsync.go b/filter/chainsync/chainsync.go index 2b3352f..c34931e 100644 --- a/filter/chainsync/chainsync.go +++ b/filter/chainsync/chainsync.go @@ -23,12 +23,12 @@ import ( ) type ChainSync struct { - errorChan chan error - inputChan chan event.Event - outputChan chan event.Event - filterAddress string - filterPolicyId string - filterAssetFingerprint string + errorChan chan error + inputChan chan event.Event + outputChan chan event.Event + filterAddresses []string + filterPolicyIds []string + filterAssetFingerprints []string } // New returns a new ChainSync object with the specified options applied @@ -57,74 +57,95 @@ func (c *ChainSync) Start() error { switch v := evt.Payload.(type) { case chainsync.TransactionEvent: // Check address filter - if c.filterAddress != "" { - isStakeAddress := false - if strings.HasPrefix(c.filterAddress, "stake") { - isStakeAddress = true - } - foundMatch := false - for _, output := range v.Outputs { - if output.Address().String() == c.filterAddress { - foundMatch = true - break - } - if isStakeAddress { - stakeAddr := output.Address().StakeAddress() - if stakeAddr == nil { - continue - } - if stakeAddr.String() == c.filterAddress { + if len(c.filterAddresses) > 0 { + filterMatched := false + for _, filterAddress := range c.filterAddresses { + isStakeAddress := strings.HasPrefix(filterAddress, "stake") + foundMatch := false + for _, output := range v.Outputs { + if output.Address().String() == filterAddress { foundMatch = true break } + if isStakeAddress { + stakeAddr := output.Address().StakeAddress() + if stakeAddr == nil { + continue + } + if stakeAddr.String() == filterAddress { + foundMatch = true + break + } + } + } + if foundMatch { + filterMatched = true + break } } - if !foundMatch { + // Skip the event if none of the filter values matched + if !filterMatched { continue } } // Check policy ID filter - if c.filterPolicyId != "" { - foundMatch := false - for _, output := range v.Outputs { - if output.Assets() != nil { - for _, policyId := range output.Assets().Policies() { - if policyId.String() == c.filterPolicyId { - foundMatch = true - break + if len(c.filterPolicyIds) > 0 { + filterMatched := false + for _, filterPolicyId := range c.filterPolicyIds { + foundMatch := false + for _, output := range v.Outputs { + if output.Assets() != nil { + for _, policyId := range output.Assets().Policies() { + if policyId.String() == filterPolicyId { + foundMatch = true + break + } } } + if foundMatch { + break + } } if foundMatch { + filterMatched = true break } } - if !foundMatch { + // Skip the event if none of the filter values matched + if !filterMatched { continue } } // Check asset fingerprint filter - if c.filterAssetFingerprint != "" { - foundMatch := false - for _, output := range v.Outputs { - if output.Assets() != nil { - for _, policyId := range output.Assets().Policies() { - for _, assetName := range output.Assets().Assets(policyId) { - assetFp := ledger.NewAssetFingerprint(policyId.Bytes(), assetName) - if assetFp.String() == c.filterAssetFingerprint { - foundMatch = true + if len(c.filterAssetFingerprints) > 0 { + filterMatched := false + for _, filterAssetFingerprint := range c.filterAssetFingerprints { + foundMatch := false + for _, output := range v.Outputs { + if output.Assets() != nil { + for _, policyId := range output.Assets().Policies() { + for _, assetName := range output.Assets().Assets(policyId) { + assetFp := ledger.NewAssetFingerprint(policyId.Bytes(), assetName) + if assetFp.String() == filterAssetFingerprint { + foundMatch = true + } + } + if foundMatch { + break } } if foundMatch { break } } - if foundMatch { - break - } + } + if foundMatch { + filterMatched = true + break } } - if !foundMatch { + // Skip the event if none of the filter values matched + if !filterMatched { continue } } diff --git a/filter/chainsync/option.go b/filter/chainsync/option.go index 2b01923..eccd09e 100644 --- a/filter/chainsync/option.go +++ b/filter/chainsync/option.go @@ -16,23 +16,23 @@ package chainsync type ChainSyncOptionFunc func(*ChainSync) -// WithAddress specfies the address to filter on -func WithAddress(address string) ChainSyncOptionFunc { +// WithAddresses specfies the address to filter on +func WithAddresses(addresses []string) ChainSyncOptionFunc { return func(c *ChainSync) { - c.filterAddress = address + c.filterAddresses = addresses[:] } } -// WithPolicy specfies the address to filter on -func WithPolicy(policyId string) ChainSyncOptionFunc { +// WithPolicies specfies the address to filter on +func WithPolicies(policyIds []string) ChainSyncOptionFunc { return func(c *ChainSync) { - c.filterPolicyId = policyId + c.filterPolicyIds = policyIds[:] } } -//WithAssetFingerprint specifies the asset fingerprint (asset1xxx) to filter on -func WithAssetFingerprint(assetFingerprint string) ChainSyncOptionFunc { +//WithAssetFingerprints specifies the asset fingerprint (asset1xxx) to filter on +func WithAssetFingerprints(assetFingerprints []string) ChainSyncOptionFunc { return func(c *ChainSync) { - c.filterAssetFingerprint = assetFingerprint + c.filterAssetFingerprints = assetFingerprints[:] } } diff --git a/filter/chainsync/plugin.go b/filter/chainsync/plugin.go index cc66d65..dd017db 100644 --- a/filter/chainsync/plugin.go +++ b/filter/chainsync/plugin.go @@ -15,6 +15,8 @@ package chainsync import ( + "strings" + "github.com/blinklabs-io/snek/plugin" ) @@ -62,10 +64,31 @@ func init() { } func NewFromCmdlineOptions() plugin.Plugin { - p := New( - WithAddress(cmdlineOptions.address), - WithPolicy(cmdlineOptions.policyId), - WithAssetFingerprint(cmdlineOptions.asset), - ) + pluginOptions := []ChainSyncOptionFunc{} + if cmdlineOptions.address != "" { + pluginOptions = append( + pluginOptions, + WithAddresses( + strings.Split(cmdlineOptions.address, ","), + ), + ) + } + if cmdlineOptions.policyId != "" { + pluginOptions = append( + pluginOptions, + WithPolicies( + strings.Split(cmdlineOptions.policyId, ","), + ), + ) + } + if cmdlineOptions.asset != "" { + pluginOptions = append( + pluginOptions, + WithAssetFingerprints( + strings.Split(cmdlineOptions.asset, ","), + ), + ) + } + p := New(pluginOptions...) return p } diff --git a/filter/event/event.go b/filter/event/event.go index d53babc..81d91f8 100644 --- a/filter/event/event.go +++ b/filter/event/event.go @@ -19,10 +19,10 @@ import ( ) type Event struct { - errorChan chan error - inputChan chan event.Event - outputChan chan event.Event - filterType string + errorChan chan error + inputChan chan event.Event + outputChan chan event.Event + filterTypes []string } // New returns a new Event object with the specified options applied @@ -49,8 +49,15 @@ func (e *Event) Start() error { return } // Drop events if we have a type filter configured and the event doesn't match - if e.filterType != "" { - if evt.Type != e.filterType { + if len(e.filterTypes) > 0 { + matched := false + for _, filterType := range e.filterTypes { + if evt.Type == filterType { + matched = true + break + } + } + if !matched { continue } } diff --git a/filter/event/option.go b/filter/event/option.go index 65931a0..35902a8 100644 --- a/filter/event/option.go +++ b/filter/event/option.go @@ -16,9 +16,9 @@ package event type EventOptionFunc func(*Event) -// WithType specfies the event type to filter on -func WithType(eventType string) EventOptionFunc { +// WithTypes specfies the event types to filter on +func WithTypes(eventTypes []string) EventOptionFunc { return func(e *Event) { - e.filterType = eventType + e.filterTypes = eventTypes[:] } } diff --git a/filter/event/plugin.go b/filter/event/plugin.go index b639895..c1b365c 100644 --- a/filter/event/plugin.go +++ b/filter/event/plugin.go @@ -15,6 +15,8 @@ package event import ( + "strings" + "github.com/blinklabs-io/snek/plugin" ) @@ -44,8 +46,15 @@ func init() { } func NewFromCmdlineOptions() plugin.Plugin { - p := New( - WithType(cmdlineOptions.eventType), - ) + pluginOptions := []EventOptionFunc{} + if cmdlineOptions.eventType != "" { + pluginOptions = append( + pluginOptions, + WithTypes( + strings.Split(cmdlineOptions.eventType, ","), + ), + ) + } + p := New(pluginOptions...) return p }