From 725c18c0ff897e709844e5dca2bdb8155f08ba6d Mon Sep 17 00:00:00 2001 From: Mohammed Sohail Date: Mon, 7 Oct 2024 15:14:24 +0300 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 05d142664d08ef5fe5b2e0c79b447dc589daab51 Author: Mohamed Sohail 天明 Date: Mon Oct 7 15:12:58 2024 +0300 feat: handle contract creation (#43) * feat: add contract creation handler * fix: process contract creations * fix: redis keys name commit 4b2ad3daf93bf2bc26c202a1265f1dc3d57d9529 Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon Oct 7 15:12:15 2024 +0300 build(deps): bump github.com/knadh/koanf/providers/env (#37) Bumps [github.com/knadh/koanf/providers/env](https://github.com/knadh/koanf) from 0.1.0 to 1.0.0. - [Release notes](https://github.com/knadh/koanf/releases) - [Commits](https://github.com/knadh/koanf/compare/v0.1.0...v1.0.0) --- updated-dependencies: - dependency-name: github.com/knadh/koanf/providers/env dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> commit f1086fcdc135c2ca98b6636e4f4592c06c9b6f98 Author: Mohamed Sohail 天明 Date: Mon Oct 7 10:07:11 2024 +0300 feat: optimize exists to check multiple keys in one call (#40) * closes #32 commit fd59d286f5e31e87a378c81de15bb6ad16c5ff05 Author: Mohammed Sohail Date: Mon Oct 7 09:49:01 2024 +0300 feat: add custodial registration proxy handler --- cmd/bootstrap/main.go | 4 +- cmd/service/router.go | 4 ++ go.mod | 2 +- go.sum | 4 +- internal/cache/cache.go | 2 +- internal/cache/redis.go | 4 +- internal/cache/xmap.go | 11 +++- internal/handler/contract_creation.go | 28 +++++++++ internal/handler/custodial_registration.go | 66 ++++++++++++++++++++++ internal/handler/token_transfer.go | 10 +--- internal/processor/processor.go | 65 ++++++++++++++++++++- pkg/router/router.go | 36 +++++++++--- 12 files changed, 206 insertions(+), 30 deletions(-) create mode 100644 internal/handler/contract_creation.go create mode 100644 internal/handler/custodial_registration.go diff --git a/cmd/bootstrap/main.go b/cmd/bootstrap/main.go index f664537..974e5f4 100644 --- a/cmd/bootstrap/main.go +++ b/cmd/bootstrap/main.go @@ -230,12 +230,12 @@ func bootstrapCache() error { } for _, address := range ko.MustStrings("bootstrap.watchlist") { - if err := cache.Add(ctx, address); err != nil { + if err := cache.Add(ctx, ethutils.HexToAddress(address).Hex()); err != nil { return err } } for _, address := range ko.MustStrings("bootstrap.blacklist") { - if err := cache.Remove(ctx, address); err != nil { + if err := cache.Remove(ctx, ethutils.HexToAddress(address).Hex()); err != nil { return err } } diff --git a/cmd/service/router.go b/cmd/service/router.go index 90ff053..b0d7599 100644 --- a/cmd/service/router.go +++ b/cmd/service/router.go @@ -11,6 +11,8 @@ func bootstrapEventRouter(cacheProvider cache.Cache, pubCB router.Callback) *rou handlerContainer := handler.New(cacheProvider) router := router.New(pubCB) + router.RegisterContractCreationHandler(handler.HandleContractCreation()) + router.RegisterLogRoute(w3.H("0x26162814817e23ec5035d6a2edc6c422da2da2119e27cfca6be65cc2dc55ca4c"), handler.HandleFaucetGiveLog()) router.RegisterLogRoute(w3.H("0xa226db3f664042183ee0281230bba26cbf7b5057e50aee7f25a175ff45ce4d7f"), handler.HandleIndexAddLog(handlerContainer)) router.RegisterLogRoute(w3.H("0x24a12366c02e13fe4a9e03d86a8952e85bb74a456c16e4a18b6d8295700b74bb"), handler.HandleIndexRemoveLog(handlerContainer)) @@ -21,6 +23,7 @@ func bootstrapEventRouter(cacheProvider cache.Cache, pubCB router.Callback) *rou router.RegisterLogRoute(w3.H("0x6b7e2e653f93b645d4ed7292d6429f96637084363e477c8aaea1a43ed13c284e"), handler.HandleSealStateChangeLog()) router.RegisterLogRoute(w3.H("0xcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5"), handler.HandleTokenBurnLog()) router.RegisterLogRoute(w3.H("0xab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f8"), handler.HandleTokenMintLog()) + router.RegisterLogRoute(w3.H("0x894e56e1dac400b4475c83d8af0f0aa44de17c62764bd82f6e768a504e242461"), handler.HandleCustodialRegistrationLog()) router.RegisterLogRoute(w3.H("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"), handler.HandleTokenTransferLog(handlerContainer)) router.RegisterInputDataRoute("63e4bff4", handler.HandleFaucetGiveInputData()) @@ -35,6 +38,7 @@ func bootstrapEventRouter(cacheProvider cache.Cache, pubCB router.Callback) *rou router.RegisterInputDataRoute("86fe212d", handler.HandleSealStateChangeInputData()) router.RegisterInputDataRoute("42966c68", handler.HandleTokenBurnInputData()) router.RegisterInputDataRoute("449a52f8", handler.HandleTokenMintInputData()) + router.RegisterInputDataRoute("4420e486", handler.HandleCustodialRegistrationInputData()) router.RegisterInputDataRoute("a9059cbb", handler.HandleTokenTransferInputData(handlerContainer)) router.RegisterInputDataRoute("23b872dd", handler.HandleTokenTransferInputData(handlerContainer)) diff --git a/go.mod b/go.mod index 74fc614..3cd32b8 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/grassrootseconomics/ethutils v1.3.0 github.com/kamikazechaser/common v0.2.0 github.com/knadh/koanf/parsers/toml v0.1.0 - github.com/knadh/koanf/providers/env v0.1.0 + github.com/knadh/koanf/providers/env v1.0.0 github.com/knadh/koanf/providers/file v1.1.0 github.com/knadh/koanf/v2 v2.1.1 github.com/lmittmann/w3 v0.17.0 diff --git a/go.sum b/go.sum index 37f70d1..cdd6600 100644 --- a/go.sum +++ b/go.sum @@ -112,8 +112,8 @@ github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NI github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= github.com/knadh/koanf/parsers/toml v0.1.0 h1:S2hLqS4TgWZYj4/7mI5m1CQQcWurxUz6ODgOub/6LCI= github.com/knadh/koanf/parsers/toml v0.1.0/go.mod h1:yUprhq6eo3GbyVXFFMdbfZSo928ksS+uo0FFqNMnO18= -github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg= -github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ= +github.com/knadh/koanf/providers/env v1.0.0 h1:ufePaI9BnWH+ajuxGGiJ8pdTG0uLEUWC7/HDDPGLah0= +github.com/knadh/koanf/providers/env v1.0.0/go.mod h1:mzFyRZueYhb37oPmC1HAv/oGEEuyvJDA98r3XAa8Gak= github.com/knadh/koanf/providers/file v1.1.0 h1:MTjA+gRrVl1zqgetEAIaXHqYje0XSosxSiMD4/7kz0o= github.com/knadh/koanf/providers/file v1.1.0/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI= github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= diff --git a/internal/cache/cache.go b/internal/cache/cache.go index 7166208..96a5077 100644 --- a/internal/cache/cache.go +++ b/internal/cache/cache.go @@ -9,7 +9,7 @@ type ( Cache interface { Add(context.Context, string) error Remove(context.Context, string) error - Exists(context.Context, string) (bool, error) + Exists(context.Context, ...string) (bool, error) Size(context.Context) (int64, error) } diff --git a/internal/cache/redis.go b/internal/cache/redis.go index 616578f..5424162 100644 --- a/internal/cache/redis.go +++ b/internal/cache/redis.go @@ -40,8 +40,8 @@ func (c *redisCache) Remove(ctx context.Context, key string) error { return c.client.Do(ctx, cmd).Error() } -func (c *redisCache) Exists(ctx context.Context, key string) (bool, error) { - cmd := c.client.B().Exists().Key(key).Build() +func (c *redisCache) Exists(ctx context.Context, keys ...string) (bool, error) { + cmd := c.client.B().Exists().Key(keys...).Build() res, err := c.client.Do(ctx, cmd).AsBool() if err != nil { return false, err diff --git a/internal/cache/xmap.go b/internal/cache/xmap.go index 49ba5a8..3a10bf2 100644 --- a/internal/cache/xmap.go +++ b/internal/cache/xmap.go @@ -26,9 +26,14 @@ func (c *mapCache) Remove(_ context.Context, key string) error { return nil } -func (c *mapCache) Exists(_ context.Context, key string) (bool, error) { - _, ok := c.xmap.Load(key) - return ok, nil +func (c *mapCache) Exists(_ context.Context, key ...string) (bool, error) { + for _, v := range key { + _, ok := c.xmap.Load(v) + if ok { + return true, nil + } + } + return false, nil } func (c *mapCache) Size(_ context.Context) (int64, error) { diff --git a/internal/handler/contract_creation.go b/internal/handler/contract_creation.go new file mode 100644 index 0000000..0c44695 --- /dev/null +++ b/internal/handler/contract_creation.go @@ -0,0 +1,28 @@ +package handler + +import ( + "context" + + "github.com/grassrootseconomics/eth-tracker/pkg/event" + "github.com/grassrootseconomics/eth-tracker/pkg/router" +) + +const contractCreationEventName = "CONTRACT_CREATION" + +func HandleContractCreation() router.ContractCreationHandlerFunc { + return func(ctx context.Context, ccp router.ContractCreationPayload, c router.Callback) error { + contractCreationEvent := event.Event{ + Block: ccp.Block, + ContractAddress: ccp.ContractAddress, + Success: ccp.Success, + Timestamp: ccp.Timestamp, + TxHash: ccp.TxHash, + TxType: contractCreationEventName, + Payload: map[string]any{ + "from": ccp.From, + }, + } + + return c(ctx, contractCreationEvent) + } +} diff --git a/internal/handler/custodial_registration.go b/internal/handler/custodial_registration.go new file mode 100644 index 0000000..f7af4e8 --- /dev/null +++ b/internal/handler/custodial_registration.go @@ -0,0 +1,66 @@ +package handler + +import ( + "context" + + "github.com/ethereum/go-ethereum/common" + "github.com/grassrootseconomics/eth-tracker/pkg/event" + "github.com/grassrootseconomics/eth-tracker/pkg/router" + "github.com/lmittmann/w3" +) + +const custodialRegistrationEventName = "CUSTODIAL_REGISTRATION" + +var ( + custodialRegistrationEvent = w3.MustNewEvent("NewRegistration(address indexed subject)") + custodialRegistrationSig = w3.MustNewFunc("register(address)", "") +) + +func HandleCustodialRegistrationLog() router.LogHandlerFunc { + return func(ctx context.Context, lp router.LogPayload, c router.Callback) error { + var account common.Address + + if err := custodialRegistrationEvent.DecodeArgs(lp.Log, &account); err != nil { + return err + } + + custodialRegistrationEvent := event.Event{ + Index: lp.Log.Index, + Block: lp.Log.BlockNumber, + ContractAddress: lp.Log.Address.Hex(), + Success: true, + Timestamp: lp.Timestamp, + TxHash: lp.Log.TxHash.Hex(), + TxType: custodialRegistrationEventName, + Payload: map[string]any{ + "account": account.Hex(), + }, + } + + return c(ctx, custodialRegistrationEvent) + } +} + +func HandleCustodialRegistrationInputData() router.InputDataHandlerFunc { + return func(ctx context.Context, idp router.InputDataPayload, c router.Callback) error { + var account common.Address + + if err := custodialRegistrationSig.DecodeArgs(w3.B(idp.InputData), &account); err != nil { + return err + } + + custodialRegistrationEvent := event.Event{ + Block: idp.Block, + ContractAddress: idp.ContractAddress, + Success: false, + Timestamp: idp.Timestamp, + TxHash: idp.TxHash, + TxType: custodialRegistrationEventName, + Payload: map[string]any{ + "account": account.Hex(), + }, + } + + return c(ctx, custodialRegistrationEvent) + } +} diff --git a/internal/handler/token_transfer.go b/internal/handler/token_transfer.go index 182e00f..7ae360a 100644 --- a/internal/handler/token_transfer.go +++ b/internal/handler/token_transfer.go @@ -147,16 +147,10 @@ func (hc *HandlerContainer) checkStables(ctx context.Context, from string, to st return true, nil } - // TODO: Pipeline this check on Redis with a new method - fromExists, err := hc.cache.Exists(ctx, from) + exists, err := hc.cache.Exists(ctx, from, to) if err != nil { return false, err } - toExists, err := hc.cache.Exists(ctx, to) - if err != nil { - return false, err - } - - return fromExists || toExists, nil + return exists, nil } diff --git a/internal/processor/processor.go b/internal/processor/processor.go index 14376c8..271a108 100644 --- a/internal/processor/processor.go +++ b/internal/processor/processor.go @@ -54,7 +54,7 @@ func (p *Processor) ProcessBlock(ctx context.Context, blockNumber uint64) error } for _, receipt := range receipts { - if receipt.Status > 0 { + if receipt.Status == 1 { for _, log := range receipt.Logs { exists, err := p.cache.Exists(ctx, log.Address.Hex()) if err != nil { @@ -72,13 +72,72 @@ func (p *Processor) ProcessBlock(ctx context.Context, blockNumber uint64) error } } } - } else { + + if receipt.ContractAddress != (common.Address{}) { + tx, err := p.chain.GetTransaction(ctx, receipt.TxHash) + if err != nil && !errors.Is(err, context.Canceled) { + return fmt.Errorf("get transaction error: tx %s: %v", receipt.TxHash.Hex(), err) + } + + from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx) + if err != nil { + return fmt.Errorf("transaction decode error: tx %s: %v", receipt.TxHash.Hex(), err) + } + + exists, err := p.cache.Exists(ctx, from.Hex()) + if err != nil { + return err + } + if exists { + if err := p.router.ProcessContractCreation( + ctx, + router.ContractCreationPayload{ + From: from.Hex(), + Block: blockNumber, + ContractAddress: receipt.ContractAddress.Hex(), + Timestamp: block.Time(), + TxHash: receipt.TxHash.Hex(), + Success: true, + }, + ); err != nil && !errors.Is(err, context.Canceled) { + return fmt.Errorf("route success contract creation error: tx %s: %v", receipt.TxHash.Hex(), err) + } + } + } + } + + if receipt.Status == 0 { tx, err := p.chain.GetTransaction(ctx, receipt.TxHash) if err != nil && !errors.Is(err, context.Canceled) { return fmt.Errorf("get transaction error: tx %s: %v", receipt.TxHash.Hex(), err) } + if tx.To() == nil { + from, err := types.Sender(types.LatestSignerForChainID(tx.ChainId()), tx) + if err != nil { + return fmt.Errorf("transaction decode error: tx %s: %v", receipt.TxHash.Hex(), err) + } - if tx.To() != nil { + exists, err := p.cache.Exists(ctx, from.Hex()) + if err != nil { + return err + } + + if exists { + if err := p.router.ProcessContractCreation( + ctx, + router.ContractCreationPayload{ + From: from.Hex(), + Block: blockNumber, + ContractAddress: receipt.ContractAddress.Hex(), + Timestamp: block.Time(), + TxHash: receipt.TxHash.Hex(), + Success: false, + }, + ); err != nil && !errors.Is(err, context.Canceled) { + return fmt.Errorf("route reverted contract creation error: tx %s: %v", receipt.TxHash.Hex(), err) + } + } + } else { exists, err := p.cache.Exists(ctx, tx.To().Hex()) if err != nil { return err diff --git a/pkg/router/router.go b/pkg/router/router.go index 6f5e940..61696bc 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -25,8 +25,18 @@ type ( TxHash string } - LogHandlerFunc func(context.Context, LogPayload, Callback) error - InputDataHandlerFunc func(context.Context, InputDataPayload, Callback) error + ContractCreationPayload struct { + From string + ContractAddress string + Block uint64 + Timestamp uint64 + TxHash string + Success bool + } + + LogHandlerFunc func(context.Context, LogPayload, Callback) error + InputDataHandlerFunc func(context.Context, InputDataPayload, Callback) error + ContractCreationHandlerFunc func(context.Context, ContractCreationPayload, Callback) error LogRouteEntry struct { Signature common.Hash @@ -39,17 +49,19 @@ type ( } Router struct { - callbackFn Callback - logHandlers map[common.Hash]LogRouteEntry - inputDataHandlers map[string]InputDataEntry + callbackFn Callback + logHandlers map[common.Hash]LogRouteEntry + inputDataHandlers map[string]InputDataEntry + contractCreationHandler ContractCreationHandlerFunc } ) func New(callbackFn Callback) *Router { return &Router{ - callbackFn: callbackFn, - logHandlers: make(map[common.Hash]LogRouteEntry), - inputDataHandlers: make(map[string]InputDataEntry), + callbackFn: callbackFn, + logHandlers: make(map[common.Hash]LogRouteEntry), + inputDataHandlers: make(map[string]InputDataEntry), + contractCreationHandler: nil, } } @@ -67,6 +79,10 @@ func (r *Router) RegisterInputDataRoute(signature string, handlerFunc InputDataH } } +func (r *Router) RegisterContractCreationHandler(handlerFunc ContractCreationHandlerFunc) { + r.contractCreationHandler = handlerFunc +} + func (r *Router) ProcessLog(ctx context.Context, payload LogPayload) error { handler, ok := r.logHandlers[payload.Log.Topics[0]] if ok { @@ -88,3 +104,7 @@ func (r *Router) ProcessInputData(ctx context.Context, payload InputDataPayload) return nil } + +func (r *Router) ProcessContractCreation(ctx context.Context, payload ContractCreationPayload) error { + return r.contractCreationHandler(ctx, payload, r.callbackFn) +}