From 11553e84344021cf45705bed63ed390d8cefd9c1 Mon Sep 17 00:00:00 2001 From: Zakhar Petukhov Date: Mon, 11 Sep 2023 20:51:43 +0700 Subject: [PATCH] create jetton spam detector --- go.mod | 2 +- go.sum | 6 ++++-- pkg/addressbook/addressbook.go | 3 ++- pkg/api/account_handlers.go | 10 ++++++++++ pkg/api/event_converters.go | 6 +++--- pkg/api/handler.go | 22 +++++++++------------- pkg/api/interfaces.go | 6 ++++++ pkg/core/attached_account.go | 5 +++-- pkg/spam/spam.go | 26 ++++++++++++++++++++++++++ 9 files changed, 64 insertions(+), 22 deletions(-) create mode 100644 pkg/spam/spam.go diff --git a/go.mod b/go.mod index aa5665c2..f63e34e4 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/shurcooL/graphql v0.0.0-20220606043923-3cf50f8a0a29 github.com/sourcegraph/conc v0.3.0 github.com/stretchr/testify v1.8.4 - github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230413094040-a66ab71fb269 + github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230911133119-702402af5714 github.com/tonkeeper/tongo v1.2.3-0.20230906163250-fcf4e9b56841 go.opentelemetry.io/otel v1.16.0 go.opentelemetry.io/otel/metric v1.16.0 diff --git a/go.sum b/go.sum index e04dbcbf..b17b5337 100644 --- a/go.sum +++ b/go.sum @@ -264,8 +264,10 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230413094040-a66ab71fb269 h1:roJPVpmFnUZX6YCUqzoD5+0+Z2ruuuXmxWKS+IH3O2s= -github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230413094040-a66ab71fb269/go.mod h1:VGp8QednbWkKHcpQVlWyO0XSqAA0cR6d9wEdrDmHbbA= +github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230911103049-ddd89c5c0c4e h1:rePeUIT9A9FrJH3X6usFgdxvrnRPU+pmJ1R2HqBmhp4= +github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230911103049-ddd89c5c0c4e/go.mod h1:JS7CStRKXjl+nZtRk+f0PReL+mVityMP4O3HupHfyfg= +github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230911133119-702402af5714 h1:HwtfwtwZK6OyeqvTCqFTxiY8p7gPSS23pV8nir/tKmk= +github.com/tonkeeper/scam_backoffice_rules v0.0.0-20230911133119-702402af5714/go.mod h1:JS7CStRKXjl+nZtRk+f0PReL+mVityMP4O3HupHfyfg= github.com/tonkeeper/tongo v1.2.3-0.20230906163250-fcf4e9b56841 h1:ls2rVf+DV45opFCm/fLSfztjx3JeeDYza5rrOnh1kBw= github.com/tonkeeper/tongo v1.2.3-0.20230906163250-fcf4e9b56841/go.mod h1:LdOBjpUz6vLp1EdX3E0XLNks9YI5XMSqaQahfOMrBEY= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= diff --git a/pkg/addressbook/addressbook.go b/pkg/addressbook/addressbook.go index d0beea57..0c43bd62 100644 --- a/pkg/addressbook/addressbook.go +++ b/pkg/addressbook/addressbook.go @@ -32,7 +32,8 @@ type AttachedAccount struct { Name string `json:"name"` Preview string `json:"preview"` Wallet string `json:"wallet"` - Normalized string + Symbol string `json:"-"` + Normalized string `json:"-"` } type JettonVerificationType string diff --git a/pkg/api/account_handlers.go b/pkg/api/account_handlers.go index f6f2595c..493df710 100644 --- a/pkg/api/account_handlers.go +++ b/pkg/api/account_handlers.go @@ -8,6 +8,7 @@ import ( "net/http" "sort" + rules "github.com/tonkeeper/scam_backoffice_rules" "github.com/tonkeeper/tongo/code" "github.com/tonkeeper/tongo/utils" @@ -180,6 +181,15 @@ func (h Handler) SearchAccounts(ctx context.Context, params oas.SearchAccountsPa accounts := h.addressBook.SearchAttachedAccountsByPrefix(params.Name) var response oas.FoundAccounts for _, account := range accounts { + if account.Symbol != "" { + accountID, err := tongo.ParseAccountID(account.Wallet) + if err != nil { + continue + } + if h.spamFilter.CheckJettonAction(accountID, account.Symbol) == rules.Drop { + continue + } + } response.Addresses = append(response.Addresses, oas.FoundAccountsAddressesItem{ Address: account.Wallet, Name: account.Name, diff --git a/pkg/api/event_converters.go b/pkg/api/event_converters.go index a567ae32..c79dabd8 100644 --- a/pkg/api/event_converters.go +++ b/pkg/api/event_converters.go @@ -118,7 +118,7 @@ func signedValue(value string, viewer *tongo.AccountID, source, destination tong func (h Handler) convertActionTonTransfer(t *bath.TonTransferAction, acceptLanguage string, viewer *tongo.AccountID) (oas.OptTonTransferAction, oas.ActionSimplePreview, bool) { var spamDetected bool if t.Amount < int64(ton.OneTON) && t.Comment != nil { - if spamAction := rules.CheckAction(h.spamRules(), *t.Comment); spamAction == rules.Drop { + if spamAction := rules.CheckAction(h.spamFilter.GetRules(), *t.Comment); spamAction == rules.Drop { *t.Comment = "" spamDetected = true } @@ -158,7 +158,7 @@ func (h Handler) convertActionTonTransfer(t *bath.TonTransferAction, acceptLangu func (h Handler) convertActionNftTransfer(t *bath.NftTransferAction, acceptLanguage string, viewer *tongo.AccountID) (oas.OptNftItemTransferAction, oas.ActionSimplePreview, bool) { var spamDetected bool if t.Comment != nil { - if spamAction := rules.CheckAction(h.spamRules(), *t.Comment); spamAction == rules.Drop { + if spamAction := rules.CheckAction(h.spamFilter.GetRules(), *t.Comment); spamAction == rules.Drop { spamDetected = true } } @@ -187,7 +187,7 @@ func (h Handler) convertActionNftTransfer(t *bath.NftTransferAction, acceptLangu func (h Handler) convertActionJettonTransfer(ctx context.Context, t *bath.JettonTransferAction, acceptLanguage string, viewer *tongo.AccountID) (oas.OptJettonTransferAction, oas.ActionSimplePreview, bool) { var spamDetected bool if t.Comment != nil { - if spamAction := rules.CheckAction(h.spamRules(), *t.Comment); spamAction == rules.Drop { + if spamAction := rules.CheckAction(h.spamFilter.GetRules(), *t.Comment); spamAction == rules.Drop { spamDetected = true } } diff --git a/pkg/api/handler.go b/pkg/api/handler.go index 8b213914..a4445595 100644 --- a/pkg/api/handler.go +++ b/pkg/api/handler.go @@ -4,12 +4,11 @@ import ( "context" "fmt" - "github.com/tonkeeper/opentonapi/pkg/chainstate" - "github.com/go-faster/errors" + "github.com/tonkeeper/opentonapi/pkg/chainstate" "github.com/tonkeeper/opentonapi/pkg/core" "github.com/tonkeeper/opentonapi/pkg/rates" - rules "github.com/tonkeeper/scam_backoffice_rules" + "github.com/tonkeeper/opentonapi/pkg/spam" "github.com/tonkeeper/tongo" "github.com/tonkeeper/tongo/contract/dns" "github.com/tonkeeper/tongo/tep64" @@ -35,7 +34,7 @@ type Handler struct { executor executor dns *dns.DNS limits Limits - spamRules func() rules.Rules + spamFilter spamFilter ratesSource ratesSource metaCache metadataCache mempoolEmulate mempoolEmulate @@ -50,7 +49,7 @@ type Options struct { msgSender messageSender executor executor limits Limits - spamRules func() rules.Rules + spamFilter spamFilter ratesSource ratesSource tonConnectSecret string } @@ -92,9 +91,9 @@ func WithLimits(limits Limits) Option { } } -func WithSpamRules(spamRules func() rules.Rules) Option { +func WithSpamFilter(spamFilter spamFilter) Option { return func(o *Options) { - o.spamRules = spamRules + o.spamFilter = spamFilter } } @@ -128,11 +127,8 @@ func NewHandler(logger *zap.Logger, opts ...Option) (*Handler, error) { if options.chainState == nil { options.chainState = chainstate.NewChainState(options.storage) } - if options.spamRules == nil { - options.spamRules = func() rules.Rules { - defaultRules := rules.GetDefaultRules() - return defaultRules - } + if options.spamFilter == nil { + options.spamFilter = spam.NewSpamFilter() } if options.ratesSource == nil { options.ratesSource = rates.Mock{Storage: options.storage} @@ -154,7 +150,7 @@ func NewHandler(logger *zap.Logger, opts ...Option) (*Handler, error) { executor: options.executor, dns: dnsClient, limits: options.limits, - spamRules: options.spamRules, + spamFilter: options.spamFilter, ratesSource: rates.InitCalculator(options.ratesSource), metaCache: metadataCache{ collectionsCache: cache.NewLRUCache[tongo.AccountID, tep64.Metadata](10000, "nft_metadata_cache"), diff --git a/pkg/api/interfaces.go b/pkg/api/interfaces.go index 20fd1374..57dfaacb 100644 --- a/pkg/api/interfaces.go +++ b/pkg/api/interfaces.go @@ -5,6 +5,7 @@ import ( "crypto/ed25519" "sync" + rules "github.com/tonkeeper/scam_backoffice_rules" "github.com/tonkeeper/tongo/boc" "github.com/tonkeeper/tongo" @@ -148,6 +149,11 @@ type ratesSource interface { GetRatesChart(token string, currency string, startDate *int64, endDate *int64) ([][]any, error) } +type spamFilter interface { + GetRules() rules.Rules + CheckJettonAction(address tongo.AccountID, symbol string) rules.TypeOfAction +} + type metadataCache struct { collectionsCache cache.Cache[tongo.AccountID, tep64.Metadata] jettonsCache cache.Cache[tongo.AccountID, tep64.Metadata] diff --git a/pkg/core/attached_account.go b/pkg/core/attached_account.go index fe854d13..2831cc3a 100644 --- a/pkg/core/attached_account.go +++ b/pkg/core/attached_account.go @@ -1,6 +1,7 @@ package core type AttachedAccount struct { - Name string - Image string + Name string + Symbol string + Image string } diff --git a/pkg/spam/spam.go b/pkg/spam/spam.go new file mode 100644 index 00000000..6538582e --- /dev/null +++ b/pkg/spam/spam.go @@ -0,0 +1,26 @@ +package spam + +import ( + rules "github.com/tonkeeper/scam_backoffice_rules" + "github.com/tonkeeper/tongo" +) + +type SpamFilter struct { + Rules rules.Rules + JettonEvaluate *rules.JettonEvaluate +} + +func NewSpamFilter() *SpamFilter { + return &SpamFilter{ + Rules: rules.GetDefaultRules(), + JettonEvaluate: rules.NewJettonEvaluate(), + } +} + +func (s *SpamFilter) GetRules() rules.Rules { + return s.Rules +} + +func (s *SpamFilter) CheckJettonAction(address tongo.AccountID, symbol string) rules.TypeOfAction { + return s.JettonEvaluate.SearchAction(address, symbol) +}