From 602eb81552e942737502c418475b62fcb69e28fe Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Tue, 16 Apr 2024 12:10:03 +0300 Subject: [PATCH 01/19] Fix copying mutex, go mod tidy --- go.mod | 17 +++-------------- go.sum | 31 ------------------------------- internal/cli/main.go | 2 +- internal/cli/migrate.go | 4 ++-- internal/config/main.go | 4 ++-- internal/service/router.go | 2 +- 6 files changed, 9 insertions(+), 51 deletions(-) diff --git a/go.mod b/go.mod index be5561c..71b73a3 100644 --- a/go.mod +++ b/go.mod @@ -6,22 +6,14 @@ require ( github.com/Masterminds/squirrel v1.4.0 github.com/alecthomas/kingpin v2.2.6+incompatible github.com/cosmos/cosmos-sdk v0.46.12 - github.com/ethereum/go-ethereum v1.13.11 github.com/go-chi/chi v4.1.2+incompatible - github.com/go-co-op/gocron/v2 v2.2.2 github.com/go-ozzo/ozzo-validation/v4 v4.3.0 - github.com/google/jsonapi v1.0.0 - github.com/iden3/go-iden3-core/v2 v2.0.4 - github.com/rarimo/auth-svc v1.0.0-rc2.0.20240311143312-de1e2258f175 github.com/rarimo/saver-grpc-lib v1.0.0 github.com/rubenv/sql-migrate v1.6.1 gitlab.com/distributed_lab/ape v1.7.1 gitlab.com/distributed_lab/figure/v3 v3.1.3 - gitlab.com/distributed_lab/json-api-connector v0.2.7 gitlab.com/distributed_lab/kit v1.11.2 gitlab.com/distributed_lab/logan v3.8.1+incompatible - gitlab.com/distributed_lab/running v0.0.0-20200706131153-4af0e83eb96c - gitlab.com/distributed_lab/urlval/v4 v4.0.3 ) require ( @@ -64,7 +56,7 @@ require ( github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect - github.com/fatih/structs v1.1.0 // indirect + github.com/ethereum/go-ethereum v1.13.11 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getsentry/raven-go v0.2.0 // indirect github.com/getsentry/sentry-go v0.26.0 // indirect @@ -75,11 +67,11 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/gogo/protobuf v1.3.3 // indirect - github.com/golang-jwt/jwt/v5 v5.2.0 // indirect github.com/golang/glog v1.1.2 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect github.com/google/btree v1.1.2 // indirect + github.com/google/jsonapi v1.0.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect @@ -92,11 +84,9 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect github.com/holiman/uint256 v1.2.4 // indirect - github.com/iden3/go-iden3-crypto v0.0.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/jmoiron/sqlx v1.2.0 // indirect - github.com/jonboulle/clockwork v0.4.0 // indirect github.com/klauspost/compress v1.17.0 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect @@ -108,7 +98,6 @@ require ( github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect - github.com/mr-tron/base58 v1.2.0 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/oklog/ulid v1.3.1 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect @@ -121,7 +110,6 @@ require ( github.com/prometheus/procfs v0.9.0 // indirect github.com/rarimo/broadcaster-svc v1.0.2 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect @@ -148,6 +136,7 @@ require ( github.com/zondax/ledger-go v0.14.1 // indirect gitlab.com/distributed_lab/figure v2.1.2+incompatible // indirect gitlab.com/distributed_lab/lorem v0.2.1 // indirect + gitlab.com/distributed_lab/running v0.0.0-20200706131153-4af0e83eb96c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.18.0 // indirect diff --git a/go.sum b/go.sum index de20375..9ec52d9 100644 --- a/go.sum +++ b/go.sum @@ -616,8 +616,6 @@ github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91 github.com/certifi/gocertifi v0.0.0-20200211180108-c7c1fbc02894/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d h1:S2NE3iHSwP0XV47EEXL8mWmRdEfGscSJ+7EgePNgt0s= github.com/certifi/gocertifi v0.0.0-20210507211836-431795d63e8d/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA= -github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= -github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -735,7 +733,6 @@ github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqL github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk= github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8= -github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= @@ -746,7 +743,6 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/ github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= -github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -769,8 +765,6 @@ github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/ github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-co-op/gocron/v2 v2.2.2 h1:XgQIiocDrdSX/B2AICR0FWk4ZeyJzeK5LVmLRoou3F0= -github.com/go-co-op/gocron/v2 v2.2.2/go.mod h1:igssOwzZkfcnu3m2kwnCf/mYj4SmhP9ecSgmYjCOHkk= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= @@ -821,8 +815,6 @@ github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0= github.com/gogo/gateway v1.1.0/go.mod h1:S7rR8FRQyG3QFESeSv4l2WnsyzlCLG0CzBbUUo/mbic= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= -github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= @@ -1007,10 +999,6 @@ github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/iden3/go-iden3-core/v2 v2.0.4 h1:ggzC2zgOWgJAAcuG9X8bQG1r4gAoHZWqY7aLV8b1qgc= -github.com/iden3/go-iden3-core/v2 v2.0.4/go.mod h1:L9PxhWPvoS9qTb3inEkZBm1RpjHBt+VTwvxssdzbAdw= -github.com/iden3/go-iden3-crypto v0.0.15 h1:4MJYlrot1l31Fzlo2sF56u7EVFeHHJkxGXXZCtESgK4= -github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= @@ -1030,8 +1018,6 @@ github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= -github.com/jonboulle/clockwork v0.4.0 h1:p4Cf1aMWXnXAUh8lVfewRBx1zaTSYKrKMF2g3ST4RZ4= -github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -1145,8 +1131,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ= -github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= -github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -1217,8 +1201,6 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= -github.com/rarimo/auth-svc v1.0.0-rc2.0.20240311143312-de1e2258f175 h1:LWfw+633hf9J0unWMA528CEf+taYdzhhoqJVFj3po6Y= -github.com/rarimo/auth-svc v1.0.0-rc2.0.20240311143312-de1e2258f175/go.mod h1:XtPIuJABJ3QNmhcpmJRlQrxuagPfpIWwFbY0j5SakFg= github.com/rarimo/broadcaster-svc v1.0.2 h1:ExQcjjWCRP5+POLDlZHrTD1ffUsBH+Dgv5FAgcP3BXc= github.com/rarimo/broadcaster-svc v1.0.2/go.mod h1:lYIHy+X4IqQt4eBdtMN/V352H3EV0/gO8G+32SFwUWI= github.com/rarimo/cosmos-sdk v0.46.7 h1:jU2PiWzc+19SF02cXM0O0puKPeH1C6Q6t2lzJ9s1ejc= @@ -1234,8 +1216,6 @@ github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= -github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -1282,7 +1262,6 @@ github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcD github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= @@ -1371,7 +1350,6 @@ github.com/zondax/hid v0.9.1 h1:gQe66rtmyZ8VeGFcOpbuH3r7erYtNEAezCAYu8LdkJo= github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM= github.com/zondax/ledger-go v0.14.1 h1:Pip65OOl4iJ84WTpA4BKChvOufMhhbxED3BaihoZN4c= github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320= -gitlab.com/distributed_lab/ape v1.6.1/go.mod h1:Qy9Y2arL0hmZIpVpctGEFhdrVsjWtyVJ5G+bZWcFT4s= gitlab.com/distributed_lab/ape v1.7.1 h1:LpTmZgG7Lvx6ulopQbH2aWI3s8ey9FsKVjbic3ZQIy4= gitlab.com/distributed_lab/ape v1.7.1/go.mod h1:Qy9Y2arL0hmZIpVpctGEFhdrVsjWtyVJ5G+bZWcFT4s= gitlab.com/distributed_lab/figure v2.1.0+incompatible/go.mod h1:tk+aPBohT49MGPLy5+eVbE1HpD/CaC5drBHfVpRI8eE= @@ -1379,12 +1357,9 @@ gitlab.com/distributed_lab/figure v2.1.2+incompatible h1:xO1KCYPK9KFx6OUBOaJ62d8 gitlab.com/distributed_lab/figure v2.1.2+incompatible/go.mod h1:tk+aPBohT49MGPLy5+eVbE1HpD/CaC5drBHfVpRI8eE= gitlab.com/distributed_lab/figure/v3 v3.1.3 h1:gCHplT1Ih8B1s4eYTeAhRZyto3gIWoUCUj3yYfNM4r8= gitlab.com/distributed_lab/figure/v3 v3.1.3/go.mod h1:gYbCEdQBQCVEg+ap0zrpjY56BU95k9H8ELebL1ChONo= -gitlab.com/distributed_lab/json-api-connector v0.2.7 h1:cwKDOxY/WLNFUJqpj90gGwnrdOZctQPD6RiTEJ7rNw4= -gitlab.com/distributed_lab/json-api-connector v0.2.7/go.mod h1:/jNqcDl22LxF06EOYsU8DvLpYwB5okFvesDotsj4ClA= gitlab.com/distributed_lab/kit v1.11.2 h1:3GYAVe/ih5fvFuM/44zIorv9mUyD3JBQe/5v+GL7x+k= gitlab.com/distributed_lab/kit v1.11.2/go.mod h1:MZj5Vb71YBWJ2wLAb9fDvlCYKewmNDNVWjAiERwgbdA= gitlab.com/distributed_lab/logan v3.7.2+incompatible/go.mod h1:25oL/FPFXmyYzWeA6vahMvnFJV8P7mOx0jZhRP7nhlc= -gitlab.com/distributed_lab/logan v3.8.0+incompatible/go.mod h1:25oL/FPFXmyYzWeA6vahMvnFJV8P7mOx0jZhRP7nhlc= gitlab.com/distributed_lab/logan v3.8.1+incompatible h1:bYiP3P0AA0cpAL/fyOYWGq1aiKw16vZFoJz+nwbqdvU= gitlab.com/distributed_lab/logan v3.8.1+incompatible/go.mod h1:25oL/FPFXmyYzWeA6vahMvnFJV8P7mOx0jZhRP7nhlc= gitlab.com/distributed_lab/lorem v0.2.0/go.mod h1:wkzrGoB1L/yUBu56SfoJ/vNiPqiHZcg75AnBkWNcjhQ= @@ -1392,8 +1367,6 @@ gitlab.com/distributed_lab/lorem v0.2.1 h1:A1QoiEDRN3vlPrwsXJmPlENanQwu3FxpDl5vE gitlab.com/distributed_lab/lorem v0.2.1/go.mod h1:wkzrGoB1L/yUBu56SfoJ/vNiPqiHZcg75AnBkWNcjhQ= gitlab.com/distributed_lab/running v0.0.0-20200706131153-4af0e83eb96c h1:cpIjV8C//7sLVvMcBaFGopI8sMcCw8Za7T0HIf52esU= gitlab.com/distributed_lab/running v0.0.0-20200706131153-4af0e83eb96c/go.mod h1:4TnADX84dQjQMRHKIMPCVL0L97rD/Jxv0xDbrN6aKzk= -gitlab.com/distributed_lab/urlval/v4 v4.0.3 h1:ZgdSBcvaoHBYmgze/u0bYfvq5Xx47pGTdfFmMYxn27s= -gitlab.com/distributed_lab/urlval/v4 v4.0.3/go.mod h1:IdRM8gOyzpXNoAkIKWVwN+dChh6+1TioS/SVhTGvRFA= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= @@ -1408,8 +1381,6 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= @@ -2061,8 +2032,6 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/cli/main.go b/internal/cli/main.go index 387e5e4..63b4a69 100644 --- a/internal/cli/main.go +++ b/internal/cli/main.go @@ -42,7 +42,7 @@ func Run(args []string) bool { defer stop() var wg sync.WaitGroup - run := func(f func(context.Context, config.Config)) { + run := func(f func(context.Context, *config.Config)) { wg.Add(1) go func() { f(ctx, cfg) diff --git a/internal/cli/migrate.go b/internal/cli/migrate.go index e7b9d2b..906a0fc 100644 --- a/internal/cli/migrate.go +++ b/internal/cli/migrate.go @@ -12,7 +12,7 @@ var migrations = &migrate.EmbedFileSystemMigrationSource{ Root: "migrations", } -func MigrateUp(cfg config.Config) error { +func MigrateUp(cfg *config.Config) error { applied, err := migrate.Exec(cfg.DB().RawDB(), "postgres", migrations, migrate.Up) if err != nil { return errors.Wrap(err, "failed to apply migrations") @@ -21,7 +21,7 @@ func MigrateUp(cfg config.Config) error { return nil } -func MigrateDown(cfg config.Config) error { +func MigrateDown(cfg *config.Config) error { applied, err := migrate.Exec(cfg.DB().RawDB(), "postgres", migrations, migrate.Down) if err != nil { return errors.Wrap(err, "failed to apply migrations") diff --git a/internal/config/main.go b/internal/config/main.go index 862925e..7f4bb39 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -17,8 +17,8 @@ type Config struct { getter kv.Getter } -func New(getter kv.Getter) Config { - return Config{ +func New(getter kv.Getter) *Config { + return &Config{ getter: getter, Databaser: pgdb.NewDatabaser(getter), Listenerer: comfig.NewListenerer(getter), diff --git a/internal/service/router.go b/internal/service/router.go index 6e440bd..b2c70a5 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -9,7 +9,7 @@ import ( "gitlab.com/distributed_lab/ape" ) -func Run(ctx context.Context, cfg config.Config) { +func Run(ctx context.Context, cfg *config.Config) { r := chi.NewRouter() r.Use( From a88a9f83cb6a806366c3dc3c6ab5b3df5d62be98 Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Tue, 16 Apr 2024 18:33:13 +0300 Subject: [PATCH 02/19] Add passport proof verification for airdrop --- config.yaml | 7 + .../components/schemas/CreateAirdrop.yaml | 7 +- go.mod | 3 + go.sum | 6 + internal/config/main.go | 5 +- internal/config/verifier.go | 54 +++ internal/service/handlers/create_airdrop.go | 320 ++++++++++++++++++ internal/service/handlers/ctx.go | 12 + internal/service/requests/create_airdrop.go | 24 +- internal/service/requests/hex_rule.go | 24 ++ internal/service/router.go | 1 + resources/model_create_airdrop_attributes.go | 4 +- resources/passport_data.go | 64 ++++ 13 files changed, 518 insertions(+), 13 deletions(-) create mode 100644 internal/config/verifier.go create mode 100644 internal/service/requests/hex_rule.go create mode 100644 resources/passport_data.go diff --git a/config.yaml b/config.yaml index c5f3b85..fec5819 100644 --- a/config.yaml +++ b/config.yaml @@ -12,5 +12,12 @@ broadcaster: addr: broadcaster sender_account: "rarimo15hcd6tv7pe8hk2re7hu0zg0aphqdm2dtjrs0ds" +verifier: + verification_keys_paths: + sha1: "./sha1_verification_key.json" + sha256: "./sha256_verification_key.json" + master_certs_path: "./masterList.dev.pem" + allowed_age: 18 + airdrop: amount: 100000 # urmo diff --git a/docs/spec/components/schemas/CreateAirdrop.yaml b/docs/spec/components/schemas/CreateAirdrop.yaml index e2da382..7fd4ba1 100644 --- a/docs/spec/components/schemas/CreateAirdrop.yaml +++ b/docs/spec/components/schemas/CreateAirdrop.yaml @@ -9,13 +9,14 @@ allOf: type: object required: - address - - proof + - passport_data properties: address: type: string description: Destination address for the airdrop example: "rarimo1qlyq3ej7j7rrkw6sluz658pzne88ymf66vjcap" - proof: + passport_data: type: string - description: Placeholder for the proof + format: PassportData + description: All passport-related data required to verify passport-based ZKP example: "{}" diff --git a/go.mod b/go.mod index 71b73a3..ce3d4ea 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,8 @@ require ( github.com/cosmos/cosmos-sdk v0.46.12 github.com/go-chi/chi v4.1.2+incompatible github.com/go-ozzo/ozzo-validation/v4 v4.3.0 + github.com/iden3/go-rapidsnark/types v0.0.3 + github.com/iden3/go-rapidsnark/verifier v0.0.5 github.com/rarimo/saver-grpc-lib v1.0.0 github.com/rubenv/sql-migrate v1.6.1 gitlab.com/distributed_lab/ape v1.7.1 @@ -84,6 +86,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect github.com/holiman/uint256 v1.2.4 // indirect + github.com/iden3/go-iden3-crypto v0.0.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/jmoiron/sqlx v1.2.0 // indirect diff --git a/go.sum b/go.sum index 9ec52d9..bd3be12 100644 --- a/go.sum +++ b/go.sum @@ -999,6 +999,12 @@ github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFck github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/iden3/go-iden3-crypto v0.0.15 h1:4MJYlrot1l31Fzlo2sF56u7EVFeHHJkxGXXZCtESgK4= +github.com/iden3/go-iden3-crypto v0.0.15/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= +github.com/iden3/go-rapidsnark/types v0.0.3 h1:f0s1Qdut1qHe1O67+m+xUVRBPwSXnq5j0xSrBi0jqM4= +github.com/iden3/go-rapidsnark/types v0.0.3/go.mod h1:ApgcaUxKIgSRA6fAeFxK7p+lgXXfG4oA2HN5DhFlfF4= +github.com/iden3/go-rapidsnark/verifier v0.0.5 h1:J7y0ovrEjDQoWtZmlrp4tgGng1A9faMeYsQH4igAEqA= +github.com/iden3/go-rapidsnark/verifier v0.0.5/go.mod h1:KgL3Yr9NehlFDI4EIWVLE3UDUi8ulyjbp7HcXSBfiGI= github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA= github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ= github.com/improbable-eng/grpc-web v0.15.0/go.mod h1:1sy9HKV4Jt9aEs9JSnkWlRJPuPtwNr0l57L4f878wP8= diff --git a/internal/config/main.go b/internal/config/main.go index 7f4bb39..782690d 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -13,8 +13,9 @@ type Config struct { comfig.Listenerer broadcaster.Broadcasterer - airdrop comfig.Once - getter kv.Getter + airdrop comfig.Once + verifier comfig.Once + getter kv.Getter } func New(getter kv.Getter) *Config { diff --git a/internal/config/verifier.go b/internal/config/verifier.go new file mode 100644 index 0000000..229f5bd --- /dev/null +++ b/internal/config/verifier.go @@ -0,0 +1,54 @@ +package config + +import ( + "os" + + "gitlab.com/distributed_lab/figure/v3" + "gitlab.com/distributed_lab/kit/kv" +) + +type VerifierConfig struct { + VerificationKeys map[string][]byte + MasterCerts []byte + AllowedAge int +} + +func (c *Config) Verifier() *VerifierConfig { + return c.verifier.Do(func() interface{} { + var cfg struct { + VerificationKeysPaths map[string]string `fig:"verification_keys_paths,required"` + MasterCertsPath string `fig:"master_certs_path,required"` + AllowedAge int `fig:"allowed_age,required"` + } + + err := figure. + Out(&cfg). + With(figure.BaseHooks). + From(kv.MustGetStringMap(c.getter, "verifier")). + Please() + if err != nil { + panic(err) + } + + verificationKeys := make(map[string][]byte) + for algo, path := range cfg.VerificationKeysPaths { + verificationKey, err := os.ReadFile(path) + if err != nil { + panic(err) + } + + verificationKeys[algo] = verificationKey + } + + masterCerts, err := os.ReadFile(cfg.MasterCertsPath) + if err != nil { + panic(err) + } + + return &VerifierConfig{ + VerificationKeys: verificationKeys, + MasterCerts: masterCerts, + AllowedAge: cfg.AllowedAge, + } + }).(*VerifierConfig) +} diff --git a/internal/service/handlers/create_airdrop.go b/internal/service/handlers/create_airdrop.go index d1944e7..55a7a29 100644 --- a/internal/service/handlers/create_airdrop.go +++ b/internal/service/handlers/create_airdrop.go @@ -1,17 +1,49 @@ package handlers import ( + "bytes" + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "crypto/sha1" + "crypto/sha256" + "crypto/x509" + "encoding/asn1" + "encoding/hex" + "encoding/pem" + "errors" "fmt" + "hash" + "math/big" "net/http" + "strconv" + "strings" + "time" cosmos "github.com/cosmos/cosmos-sdk/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" + validation "github.com/go-ozzo/ozzo-validation/v4" + "github.com/iden3/go-rapidsnark/types" + "github.com/iden3/go-rapidsnark/verifier" + "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/internal/service/requests" "github.com/rarimo/airdrop-svc/resources" "gitlab.com/distributed_lab/ape" "gitlab.com/distributed_lab/ape/problems" ) +// Full list of the OpenSSL signature algorithms and hash-functions is provided here: +// https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set1_sigalgs_list.html + +const ( + SHA1 = "sha1" + SHA256 = "sha256" + + SHA256withRSA = "SHA256withRSA" + SHA1withECDSA = "SHA1withECDSA" + SHA256withECDSA = "SHA256withECDSA" +) + func CreateAirdrop(w http.ResponseWriter, r *http.Request) { req, err := requests.NewCreateAirdrop(r) if err != nil { @@ -30,6 +62,14 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { return } + if err = verifyPassportData(req.Data.Attributes.PassportData, Verifier(r)); err != nil { + Log(r).WithError(err).Info("Invalid passport data") + ape.RenderErr(w, problems.BadRequest(validation.Errors{ + "data/attributes/passport_data": err, + })...) + return + } + err = ParticipantsQ(r).Transaction(func() error { err = ParticipantsQ(r).Insert(req.Data.ID, req.Data.Attributes.Address) if err != nil { @@ -47,6 +87,286 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +func verifyPassportData(passport resources.PassportData, cfg *config.VerifierConfig) error { + algorithm := signatureAlgorithm(passport.DocumentSOD.Algorithm) + if algorithm == "" { + return fmt.Errorf("invalid signature algorithm: %s", passport.DocumentSOD.Algorithm) + } + + signedAttributes, _ := hex.DecodeString(passport.DocumentSOD.SignedAttributes) + encapsulatedContent, _ := hex.DecodeString(passport.DocumentSOD.EncapsulatedContent) + + if err := validateSignedAttributes(passport.DocumentSOD, algorithm); err != nil { + return fmt.Errorf("invalid signed attributes: %w", err) + } + + cert, err := parseCertificate([]byte(passport.DocumentSOD.PemFile)) + if err != nil { + return fmt.Errorf("parse certificate: %w", err) + } + + if err = verifySignature(passport, cert, signedAttributes, algorithm); err != nil { + return fmt.Errorf("invalid signature: %w", err) + } + + var key []byte + switch algorithm { + case SHA1withECDSA: + key = cfg.VerificationKeys[SHA1] + case SHA256withRSA, SHA256withECDSA: + key = cfg.VerificationKeys[SHA256] + } + + if err = verifier.VerifyGroth16(passport.ZKProof, key); err != nil { + return fmt.Errorf("verify groth16: %w", err) + } + + var encapsulatedData resources.EncapsulatedData + if _, err = asn1.Unmarshal(encapsulatedContent, &encapsulatedData); err != nil { + return fmt.Errorf("unmarshal raw encapsulated content: %w", err) + } + + if err = validatePubSignals(cfg, passport.ZKProof, encapsulatedData.PrivateKey.El1.OctetStr.Bytes); err != nil { + return fmt.Errorf("invalid pub signals: %w", err) + } + + if err = validateCert(cert, cfg.MasterCerts); err != nil { + return fmt.Errorf("invalid certificate: %w", err) + } + + return nil +} + +var algorithmsMap = map[string]map[string]string{ + "SHA1": { + "ECDSA": SHA1withECDSA, + }, + "SHA256": { + "RSA": SHA256withRSA, + "ECDSA": SHA256withECDSA, + }, +} + +func signatureAlgorithm(passedAlgorithm string) string { + if passedAlgorithm == "rsaEncryption" { + return SHA256withRSA + } + + if strings.Contains(strings.ToUpper(passedAlgorithm), "PSS") { + return "" // RSA-PSS is not currently supported + } + + for hashFunc, signatureAlgorithms := range algorithmsMap { + if !strings.Contains(strings.ToUpper(passedAlgorithm), hashFunc) { + continue + } + + for signatureAlgo, algorithmName := range signatureAlgorithms { + if strings.Contains(strings.ToUpper(passedAlgorithm), signatureAlgo) { + return algorithmName + } + } + } + + return "" +} + +func validateSignedAttributes(sod resources.DocumentSOD, algorithm string) error { + signedAttributes, _ := hex.DecodeString(sod.SignedAttributes) + signedAttributesASN1 := make([]asn1.RawValue, 0) + if _, err := asn1.UnmarshalWithParams(signedAttributes, &signedAttributesASN1, "set"); err != nil { + return fmt.Errorf("unmarshal signed attributes to ASN1 with params: %w", err) + } + if len(signedAttributesASN1) == 0 { + return errors.New("signed attributes count is 0") + } + + digestAttr := resources.DigestAttribute{} + if _, err := asn1.Unmarshal(signedAttributesASN1[len(signedAttributesASN1)-1].FullBytes, &digestAttr); err != nil { + return fmt.Errorf("unmarshal ASN1 signed attributes to digest attribute: %w", err) + } + if len(digestAttr.Digest) == 0 { + return errors.New("signed attributes digest values count is 0") + } + + encapsulatedContent, _ := hex.DecodeString(sod.EncapsulatedContent) + d := messageDigest(encapsulatedContent, algorithm) + + if !bytes.Equal(digestAttr.Digest[0].Bytes, d) { + return errors.New("digest signed attribute is not equal to encapsulated content hash") + } + + return nil +} + +func parseCertificate(pemFile []byte) (*x509.Certificate, error) { + block, _ := pem.Decode(pemFile) + if block == nil { + return nil, errors.New("invalid PEM certificate") + } + + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, fmt.Errorf("parse X.509 certificate: %w", err) + } + + return cert, nil +} + +func verifySignature(req resources.PassportData, cert *x509.Certificate, signedAttributes []byte, algo string) error { + signature, err := hex.DecodeString(req.DocumentSOD.Signature) + if err != nil { + return fmt.Errorf("invalid signature hex: %w", err) + } + + digest := messageDigest(signedAttributes, algo) + + switch algo { + case SHA256withRSA: + pubKey := cert.PublicKey.(*rsa.PublicKey) + if err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, digest, signature); err != nil { + return fmt.Errorf("invalid RSA signature: %w", err) + } + case SHA1withECDSA, SHA256withECDSA: + pubKey := cert.PublicKey.(*ecdsa.PublicKey) + if !ecdsa.VerifyASN1(pubKey, digest, signature) { + return errors.New("invalid ECDSA signature") + } + } + + return nil +} + +func messageDigest(data []byte, algo string) []byte { + var h hash.Hash + switch algo { + case SHA1: + h = sha1.New() + case SHA256: + h = sha256.New() + default: + return nil + } + + h.Write(data) + return h.Sum(nil) +} + +func validatePubSignals(cfg *config.VerifierConfig, zkp types.ZKProof, dg1 []byte) error { + if err := validatePubSignalsDG1Hash(dg1, zkp.PubSignals); err != nil { + return fmt.Errorf("invalid dg1 hash: %w", err) + } + if err := validatePubSignalsCurrentDate(zkp.PubSignals); err != nil { + return fmt.Errorf("invalid current date: %w", err) + } + if err := validatePubSignalsAge(cfg, zkp.PubSignals[9]); err != nil { + return fmt.Errorf("invalid age: %w", err) + } + return nil +} + +func validatePubSignalsDG1Hash(dg1 []byte, pubSignals []string) error { + ints, err := stringsToArrayBigInt([]string{pubSignals[0], pubSignals[1]}) + if err != nil { + return fmt.Errorf("convert pub signals to big ints: %w", err) + } + + hashBytes := make([]byte, 0) + hashBytes = append(hashBytes, ints[0].Bytes()...) + hashBytes = append(hashBytes, ints[1].Bytes()...) + + if !bytes.Equal(dg1, hashBytes) { + return errors.New("encapsulated data and proof pub signals hashes are different") + } + + return nil +} + +func validatePubSignalsCurrentDate(pubSignals []string) error { + year, err := strconv.Atoi(pubSignals[3]) + if err != nil { + return fmt.Errorf("invalid year: %w", err) + } + + month, err := strconv.Atoi(pubSignals[4]) + if err != nil { + return fmt.Errorf("invalid month: %w", err) + } + + day, err := strconv.Atoi(pubSignals[5]) + if err != nil { + return fmt.Errorf("invalid day: %w", err) + } + + currentTime := time.Now().UTC() + + if currentTime.Year() != (2000 + year) { + return fmt.Errorf("invalid year, expected %d, got %d", currentTime.Year(), 2000+year) + } + + if currentTime.Month() != time.Month(month) { + return fmt.Errorf("invalid month, expected %d, got %d", currentTime.Month(), month) + } + + if currentTime.Day() != day { + return fmt.Errorf("invalid day, expected %d, got %d", currentTime.Day(), day) + } + + return nil +} + +func validatePubSignalsAge(cfg *config.VerifierConfig, agePubSignal string) error { + age, err := strconv.Atoi(agePubSignal) + if err != nil { + return fmt.Errorf("age is not int: %w", err) + } + if age < cfg.AllowedAge { + return errors.New("invalid age") + } + return nil +} + +func validateCert(cert *x509.Certificate, masterCertsPem []byte) error { + roots := x509.NewCertPool() + roots.AppendCertsFromPEM(masterCertsPem) + + foundCerts, err := cert.Verify(x509.VerifyOptions{Roots: roots}) + if err != nil { + return fmt.Errorf("invalid certificate: %w", err) + } + + if len(foundCerts) == 0 { + return fmt.Errorf("invalid certificate: not ") + } + + return nil +} + +func stringsToArrayBigInt(publicSignals []string) ([]*big.Int, error) { + p := make([]*big.Int, 0, len(publicSignals)) + for _, s := range publicSignals { + sb, err := stringToBigInt(s) + if err != nil { + return nil, err + } + p = append(p, sb) + } + return p, nil +} + +func stringToBigInt(s string) (*big.Int, error) { + base := 10 + if bytes.HasPrefix([]byte(s), []byte("0x")) { + base = 16 + s = strings.TrimPrefix(s, "0x") + } + n, ok := new(big.Int).SetString(s, base) + if !ok { + return nil, fmt.Errorf("cannot parse string to *big.Int: %s (base=%d)", s, base) + } + return n, nil +} + func broadcastWithdrawalTx(req resources.CreateAirdropRequest, r *http.Request) error { urmo := AirdropAmount(r) tx := &bank.MsgSend{ diff --git a/internal/service/handlers/ctx.go b/internal/service/handlers/ctx.go index 15491e8..e7f8f7f 100644 --- a/internal/service/handlers/ctx.go +++ b/internal/service/handlers/ctx.go @@ -4,6 +4,7 @@ import ( "context" "net/http" + "github.com/rarimo/airdrop-svc/internal/config" data "github.com/rarimo/airdrop-svc/internal/data" "github.com/rarimo/saver-grpc-lib/broadcaster" "gitlab.com/distributed_lab/logan/v3" @@ -16,6 +17,7 @@ const ( participantsQCtxKey airdropAmountCtxKey broadcasterCtxKey + verifierCtxKey ) func CtxLog(entry *logan.Entry) func(context.Context) context.Context { @@ -57,3 +59,13 @@ func CtxBroadcaster(broadcaster broadcaster.Broadcaster) func(context.Context) c func Broadcaster(r *http.Request) broadcaster.Broadcaster { return r.Context().Value(broadcasterCtxKey).(broadcaster.Broadcaster) } + +func CtxVerifier(entry *config.VerifierConfig) func(context.Context) context.Context { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, verifierCtxKey, entry) + } +} + +func Verifier(r *http.Request) *config.VerifierConfig { + return r.Context().Value(verifierCtxKey).(*config.VerifierConfig) +} diff --git a/internal/service/requests/create_airdrop.go b/internal/service/requests/create_airdrop.go index f83c9a6..cd253c4 100644 --- a/internal/service/requests/create_airdrop.go +++ b/internal/service/requests/create_airdrop.go @@ -5,7 +5,7 @@ import ( "fmt" "net/http" - validation "github.com/go-ozzo/ozzo-validation/v4" + val "github.com/go-ozzo/ozzo-validation/v4" "github.com/rarimo/airdrop-svc/resources" ) @@ -15,15 +15,27 @@ func NewCreateAirdrop(r *http.Request) (req resources.CreateAirdropRequest, err return } - return req, validation.Errors{ - "data/id": validation.Validate(req.Data.ID, validation.Required), - "data/type": validation.Validate(req.Data.Type, validation.Required, validation.In(resources.CREATE_AIRDROP)), - "data/attributes/address": validation.Validate(req.Data.Attributes.Address, validation.Required), + passport := req.Data.Attributes.PassportData + sod := passport.DocumentSOD + + return req, val.Errors{ + "data/id": val.Validate(req.Data.ID, val.Required, isHex), + "data/type": val.Validate(req.Data.Type, val.Required, val.In(resources.CREATE_AIRDROP)), + "data/attributes/address": val.Validate(req.Data.Attributes.Address, val.Required, isHex), + + "data/attributes/passport_data/zkproof/proof": val.Validate(passport.ZKProof.Proof, val.Required), + "data/attributes/passport_data/zkproof/pub_signals": val.Validate(passport.ZKProof.PubSignals, val.Required), + + "data/attributes/passport_data/document_sod/algorithm": val.Validate(sod.Algorithm, val.Required), + "data/attributes/passport_data/document_sod/signed_attributes": val.Validate(sod.SignedAttributes, val.Required, isHex), + "data/attributes/passport_data/document_sod/encapsulated_content": val.Validate(sod.EncapsulatedContent, val.Required, isHex), + "data/attributes/passport_data/document_sod/signature": val.Validate(sod.Signature, val.Required, isHex), + "data/attributes/passport_data/document_sod/pem_file": val.Validate(sod.PemFile, val.Required), }.Filter() } func newDecodeError(what string, err error) error { - return validation.Errors{ + return val.Errors{ what: fmt.Errorf("decode request %s: %w", what, err), } } diff --git a/internal/service/requests/hex_rule.go b/internal/service/requests/hex_rule.go new file mode 100644 index 0000000..14fe06c --- /dev/null +++ b/internal/service/requests/hex_rule.go @@ -0,0 +1,24 @@ +package requests + +import ( + "encoding/hex" + "fmt" +) + +var isHex hexRule + +type hexRule struct{} + +func (r hexRule) Validate(data interface{}) error { + str, ok := data.(string) + if !ok { + return fmt.Errorf("invalid type: %T, expected string", data) + } + + _, err := hex.DecodeString(str) + if err != nil { + return fmt.Errorf("invalid hex string: %w", err) + } + + return nil +} diff --git a/internal/service/router.go b/internal/service/router.go index b2c70a5..3e325ea 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -19,6 +19,7 @@ func Run(ctx context.Context, cfg *config.Config) { handlers.CtxLog(cfg.Log()), handlers.CtxAirdropAmount(cfg.AirdropAmount()), handlers.CtxBroadcaster(cfg.Broadcaster()), + handlers.CtxVerifier(cfg.Verifier()), ), handlers.DBCloneMiddleware(cfg.DB()), ) diff --git a/resources/model_create_airdrop_attributes.go b/resources/model_create_airdrop_attributes.go index 3364483..c922076 100644 --- a/resources/model_create_airdrop_attributes.go +++ b/resources/model_create_airdrop_attributes.go @@ -7,6 +7,6 @@ package resources type CreateAirdropAttributes struct { // Destination address for the airdrop Address string `json:"address"` - // Placeholder for the proof - Proof string `json:"proof"` + // All passport-related data required to verify passport-based ZKP + PassportData PassportData `json:"passport_data"` } diff --git a/resources/passport_data.go b/resources/passport_data.go new file mode 100644 index 0000000..f848363 --- /dev/null +++ b/resources/passport_data.go @@ -0,0 +1,64 @@ +package resources + +import ( + "encoding/asn1" + + "github.com/iden3/go-rapidsnark/types" +) + +type PassportData struct { + ZKProof types.ZKProof `json:"zkproof"` + DocumentSOD DocumentSOD `json:"document_sod"` +} + +type DocumentSOD struct { + SignedAttributes string `json:"signed_attributes"` + Algorithm string `json:"algorithm"` + Signature string `json:"signature"` + PemFile string `json:"pem_file"` + EncapsulatedContent string `json:"encapsulated_content"` +} + +type DigestAttribute struct { + ID asn1.ObjectIdentifier + Digest []asn1.RawValue `asn1:"set"` +} + +type EncapsulatedData struct { + Version int + PrivateKeyAlgorithm asn1.RawValue + PrivateKey struct { + El1 struct { + Integer int + OctetStr asn1.RawValue + } + El2 struct { + Integer int + OctetStr asn1.RawValue + } + El3 struct { + Integer int + OctetStr asn1.RawValue + } + El4 struct { + Integer int + OctetStr asn1.RawValue + } + El5 struct { + Integer int + OctetStr asn1.RawValue + } + El6 struct { + Integer int + OctetStr asn1.RawValue + } + El7 struct { + Integer int + OctetStr asn1.RawValue + } + El8 struct { + Integer int + OctetStr asn1.RawValue + } + } +} From b6d62f3a7a1d1b99c039d861b55dad3488a2539c Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Wed, 17 Apr 2024 14:51:26 +0300 Subject: [PATCH 03/19] Drop invalid passport-related logic, set valid pub signals indexes --- config.yaml | 2 +- .../components/schemas/CreateAirdrop.yaml | 13 +- .../components/schemas/CreateAirdropKey.yaml | 5 - internal/config/verifier.go | 26 +-- internal/service/handlers/create_airdrop.go | 176 ++---------------- internal/service/requests/create_airdrop.go | 77 ++++++-- internal/service/requests/hex_rule.go | 24 --- internal/service/requests/validation.go | 68 +++++++ resources/model_create_airdrop_attributes.go | 8 +- resources/passport_data.go | 64 ------- 10 files changed, 170 insertions(+), 293 deletions(-) delete mode 100644 internal/service/requests/hex_rule.go create mode 100644 internal/service/requests/validation.go delete mode 100644 resources/passport_data.go diff --git a/config.yaml b/config.yaml index fec5819..76c7bcd 100644 --- a/config.yaml +++ b/config.yaml @@ -16,8 +16,8 @@ verifier: verification_keys_paths: sha1: "./sha1_verification_key.json" sha256: "./sha256_verification_key.json" - master_certs_path: "./masterList.dev.pem" allowed_age: 18 + allowed_citizenships: ["UKR"] airdrop: amount: 100000 # urmo diff --git a/docs/spec/components/schemas/CreateAirdrop.yaml b/docs/spec/components/schemas/CreateAirdrop.yaml index 7fd4ba1..c77abf4 100644 --- a/docs/spec/components/schemas/CreateAirdrop.yaml +++ b/docs/spec/components/schemas/CreateAirdrop.yaml @@ -9,14 +9,19 @@ allOf: type: object required: - address - - passport_data + - algorithm + - zk_proof properties: address: type: string description: Destination address for the airdrop example: "rarimo1qlyq3ej7j7rrkw6sluz658pzne88ymf66vjcap" - passport_data: + algorithm: type: string - format: PassportData - description: All passport-related data required to verify passport-based ZKP + description: Signing algorithm used in proof. The value from passport document SOD is assumed. + example: "sha256_ecdsa" + zk_proof: + type: string + format: types.ZKProof + description: ZK-proof of the passport data example: "{}" diff --git a/docs/spec/components/schemas/CreateAirdropKey.yaml b/docs/spec/components/schemas/CreateAirdropKey.yaml index 2737204..637132a 100644 --- a/docs/spec/components/schemas/CreateAirdropKey.yaml +++ b/docs/spec/components/schemas/CreateAirdropKey.yaml @@ -1,12 +1,7 @@ type: object required: - - id - type properties: - id: - type: string - description: Nullifier - example: "0x04a32216f2425dc7343031352de3d62a7b0d3b4bf7a66d6c8c2aa8c9f4f2632b" type: type: string enum: [ create_airdrop ] diff --git a/internal/config/verifier.go b/internal/config/verifier.go index 229f5bd..1f6b573 100644 --- a/internal/config/verifier.go +++ b/internal/config/verifier.go @@ -1,6 +1,7 @@ package config import ( + "fmt" "os" "gitlab.com/distributed_lab/figure/v3" @@ -8,17 +9,17 @@ import ( ) type VerifierConfig struct { - VerificationKeys map[string][]byte - MasterCerts []byte - AllowedAge int + VerificationKeys map[string][]byte + AllowedAge int + AllowedCitizenships []interface{} // more convenient to use for validation, replace on need } func (c *Config) Verifier() *VerifierConfig { return c.verifier.Do(func() interface{} { var cfg struct { VerificationKeysPaths map[string]string `fig:"verification_keys_paths,required"` - MasterCertsPath string `fig:"master_certs_path,required"` AllowedAge int `fig:"allowed_age,required"` + AllowedCitizenships []string `fig:"allowed_citizenships,required"` } err := figure. @@ -27,28 +28,27 @@ func (c *Config) Verifier() *VerifierConfig { From(kv.MustGetStringMap(c.getter, "verifier")). Please() if err != nil { - panic(err) + panic(fmt.Errorf("failed to figure out verifier: %w", err)) } verificationKeys := make(map[string][]byte) for algo, path := range cfg.VerificationKeysPaths { verificationKey, err := os.ReadFile(path) if err != nil { - panic(err) + panic(fmt.Errorf("failed to read verification key file: %w", err)) } - verificationKeys[algo] = verificationKey } - masterCerts, err := os.ReadFile(cfg.MasterCertsPath) - if err != nil { - panic(err) + citizenships := make([]interface{}, len(cfg.AllowedCitizenships)) + for i, ctz := range cfg.AllowedCitizenships { + citizenships[i] = ctz } return &VerifierConfig{ - VerificationKeys: verificationKeys, - MasterCerts: masterCerts, - AllowedAge: cfg.AllowedAge, + VerificationKeys: verificationKeys, + AllowedAge: cfg.AllowedAge, + AllowedCitizenships: citizenships, } }).(*VerifierConfig) } diff --git a/internal/service/handlers/create_airdrop.go b/internal/service/handlers/create_airdrop.go index 55a7a29..c6cdb8c 100644 --- a/internal/service/handlers/create_airdrop.go +++ b/internal/service/handlers/create_airdrop.go @@ -2,18 +2,9 @@ package handlers import ( "bytes" - "crypto" - "crypto/ecdsa" - "crypto/rsa" - "crypto/sha1" - "crypto/sha256" "crypto/x509" - "encoding/asn1" - "encoding/hex" - "encoding/pem" "errors" "fmt" - "hash" "math/big" "net/http" "strconv" @@ -23,7 +14,6 @@ import ( cosmos "github.com/cosmos/cosmos-sdk/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" validation "github.com/go-ozzo/ozzo-validation/v4" - "github.com/iden3/go-rapidsnark/types" "github.com/iden3/go-rapidsnark/verifier" "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/internal/service/requests" @@ -45,7 +35,7 @@ const ( ) func CreateAirdrop(w http.ResponseWriter, r *http.Request) { - req, err := requests.NewCreateAirdrop(r) + req, err := requests.NewCreateAirdrop(r, Verifier(r)) if err != nil { ape.RenderErr(w, problems.BadRequest(err)...) return @@ -62,16 +52,17 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { return } - if err = verifyPassportData(req.Data.Attributes.PassportData, Verifier(r)); err != nil { - Log(r).WithError(err).Info("Invalid passport data") + if err = verifyProof(req, Verifier(r)); err != nil { + Log(r).WithError(err).Info("Invalid proof") ape.RenderErr(w, problems.BadRequest(validation.Errors{ - "data/attributes/passport_data": err, + "data/attributes/zk_proof": err, })...) return } + nullifier := req.Data.Attributes.ZkProof.PubSignals[requests.PubSignalNullifier] err = ParticipantsQ(r).Transaction(func() error { - err = ParticipantsQ(r).Insert(req.Data.ID, req.Data.Attributes.Address) + err = ParticipantsQ(r).Insert(nullifier, req.Data.Attributes.Address) if err != nil { return fmt.Errorf("insert participant: %w", err) } @@ -87,53 +78,23 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } -func verifyPassportData(passport resources.PassportData, cfg *config.VerifierConfig) error { - algorithm := signatureAlgorithm(passport.DocumentSOD.Algorithm) - if algorithm == "" { - return fmt.Errorf("invalid signature algorithm: %s", passport.DocumentSOD.Algorithm) - } - - signedAttributes, _ := hex.DecodeString(passport.DocumentSOD.SignedAttributes) - encapsulatedContent, _ := hex.DecodeString(passport.DocumentSOD.EncapsulatedContent) - - if err := validateSignedAttributes(passport.DocumentSOD, algorithm); err != nil { - return fmt.Errorf("invalid signed attributes: %w", err) - } - - cert, err := parseCertificate([]byte(passport.DocumentSOD.PemFile)) - if err != nil { - return fmt.Errorf("parse certificate: %w", err) - } - - if err = verifySignature(passport, cert, signedAttributes, algorithm); err != nil { - return fmt.Errorf("invalid signature: %w", err) - } - +func verifyProof(req resources.CreateAirdropRequest, cfg *config.VerifierConfig) error { var key []byte + algorithm := signatureAlgorithm(req.Data.Attributes.Algorithm) switch algorithm { case SHA1withECDSA: key = cfg.VerificationKeys[SHA1] case SHA256withRSA, SHA256withECDSA: key = cfg.VerificationKeys[SHA256] + default: + return fmt.Errorf("unsupported algorithm: %s", req.Data.Attributes.Algorithm) } - if err = verifier.VerifyGroth16(passport.ZKProof, key); err != nil { + proof := req.Data.Attributes.ZkProof + if err := verifier.VerifyGroth16(proof, key); err != nil { return fmt.Errorf("verify groth16: %w", err) } - var encapsulatedData resources.EncapsulatedData - if _, err = asn1.Unmarshal(encapsulatedContent, &encapsulatedData); err != nil { - return fmt.Errorf("unmarshal raw encapsulated content: %w", err) - } - - if err = validatePubSignals(cfg, passport.ZKProof, encapsulatedData.PrivateKey.El1.OctetStr.Bytes); err != nil { - return fmt.Errorf("invalid pub signals: %w", err) - } - - if err = validateCert(cert, cfg.MasterCerts); err != nil { - return fmt.Errorf("invalid certificate: %w", err) - } - return nil } @@ -171,118 +132,7 @@ func signatureAlgorithm(passedAlgorithm string) string { return "" } -func validateSignedAttributes(sod resources.DocumentSOD, algorithm string) error { - signedAttributes, _ := hex.DecodeString(sod.SignedAttributes) - signedAttributesASN1 := make([]asn1.RawValue, 0) - if _, err := asn1.UnmarshalWithParams(signedAttributes, &signedAttributesASN1, "set"); err != nil { - return fmt.Errorf("unmarshal signed attributes to ASN1 with params: %w", err) - } - if len(signedAttributesASN1) == 0 { - return errors.New("signed attributes count is 0") - } - - digestAttr := resources.DigestAttribute{} - if _, err := asn1.Unmarshal(signedAttributesASN1[len(signedAttributesASN1)-1].FullBytes, &digestAttr); err != nil { - return fmt.Errorf("unmarshal ASN1 signed attributes to digest attribute: %w", err) - } - if len(digestAttr.Digest) == 0 { - return errors.New("signed attributes digest values count is 0") - } - - encapsulatedContent, _ := hex.DecodeString(sod.EncapsulatedContent) - d := messageDigest(encapsulatedContent, algorithm) - - if !bytes.Equal(digestAttr.Digest[0].Bytes, d) { - return errors.New("digest signed attribute is not equal to encapsulated content hash") - } - - return nil -} - -func parseCertificate(pemFile []byte) (*x509.Certificate, error) { - block, _ := pem.Decode(pemFile) - if block == nil { - return nil, errors.New("invalid PEM certificate") - } - - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - return nil, fmt.Errorf("parse X.509 certificate: %w", err) - } - - return cert, nil -} - -func verifySignature(req resources.PassportData, cert *x509.Certificate, signedAttributes []byte, algo string) error { - signature, err := hex.DecodeString(req.DocumentSOD.Signature) - if err != nil { - return fmt.Errorf("invalid signature hex: %w", err) - } - - digest := messageDigest(signedAttributes, algo) - - switch algo { - case SHA256withRSA: - pubKey := cert.PublicKey.(*rsa.PublicKey) - if err = rsa.VerifyPKCS1v15(pubKey, crypto.SHA256, digest, signature); err != nil { - return fmt.Errorf("invalid RSA signature: %w", err) - } - case SHA1withECDSA, SHA256withECDSA: - pubKey := cert.PublicKey.(*ecdsa.PublicKey) - if !ecdsa.VerifyASN1(pubKey, digest, signature) { - return errors.New("invalid ECDSA signature") - } - } - - return nil -} - -func messageDigest(data []byte, algo string) []byte { - var h hash.Hash - switch algo { - case SHA1: - h = sha1.New() - case SHA256: - h = sha256.New() - default: - return nil - } - - h.Write(data) - return h.Sum(nil) -} - -func validatePubSignals(cfg *config.VerifierConfig, zkp types.ZKProof, dg1 []byte) error { - if err := validatePubSignalsDG1Hash(dg1, zkp.PubSignals); err != nil { - return fmt.Errorf("invalid dg1 hash: %w", err) - } - if err := validatePubSignalsCurrentDate(zkp.PubSignals); err != nil { - return fmt.Errorf("invalid current date: %w", err) - } - if err := validatePubSignalsAge(cfg, zkp.PubSignals[9]); err != nil { - return fmt.Errorf("invalid age: %w", err) - } - return nil -} - -func validatePubSignalsDG1Hash(dg1 []byte, pubSignals []string) error { - ints, err := stringsToArrayBigInt([]string{pubSignals[0], pubSignals[1]}) - if err != nil { - return fmt.Errorf("convert pub signals to big ints: %w", err) - } - - hashBytes := make([]byte, 0) - hashBytes = append(hashBytes, ints[0].Bytes()...) - hashBytes = append(hashBytes, ints[1].Bytes()...) - - if !bytes.Equal(dg1, hashBytes) { - return errors.New("encapsulated data and proof pub signals hashes are different") - } - - return nil -} - -func validatePubSignalsCurrentDate(pubSignals []string) error { +func validateCurrentDate(pubSignals []string) error { year, err := strconv.Atoi(pubSignals[3]) if err != nil { return fmt.Errorf("invalid year: %w", err) diff --git a/internal/service/requests/create_airdrop.go b/internal/service/requests/create_airdrop.go index cd253c4..2aa2219 100644 --- a/internal/service/requests/create_airdrop.go +++ b/internal/service/requests/create_airdrop.go @@ -3,37 +3,80 @@ package requests import ( "encoding/json" "fmt" + "math/big" "net/http" + "time" + "github.com/cosmos/cosmos-sdk/types" val "github.com/go-ozzo/ozzo-validation/v4" + "github.com/iden3/go-iden3-crypto/poseidon" + "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/resources" ) -func NewCreateAirdrop(r *http.Request) (req resources.CreateAirdropRequest, err error) { +const ( + PubSignalNullifier = iota + pubSignalBirthDate + pubSignalExpirationDate + pubSignalCitizenship = 6 + pubSignalEventData = 10 + pubSignalSelector = 12 + + proofSelectorValue = "placeholder" // todo configure +) + +func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resources.CreateAirdropRequest, err error) { if err = json.NewDecoder(r.Body).Decode(&req); err != nil { - err = newDecodeError("body", err) - return + return req, newDecodeError("body", err) + } + + var ( + attr = req.Data.Attributes + signals = attr.ZkProof.PubSignals + olderThanDate = time.Now().UTC().AddDate(-cfg.AllowedAge, 0, 0) + ) + + err = val.Errors{ + "data/type": val.Validate(req.Data.Type, val.Required, val.In(resources.CREATE_AIRDROP)), + "data/attributes/address": val.Validate(attr.Address, val.Required, isHex), + "data/attributes/algorithm": val.Validate(attr.Algorithm, val.Required), + "data/attributes/zk_proof/proof": val.Validate(attr.ZkProof.Proof, val.Required), + "data/attributes/zk_proof/pub_signals": val.Validate(signals, val.Required, val.Length(14, 14)), + }.Filter() + if err != nil { + return req, err } - passport := req.Data.Attributes.PassportData - sod := passport.DocumentSOD + addrHash, err := poseidonHash(attr.Address) + if err != nil { + return req, val.Errors{"data/attributes/address": err} + } return req, val.Errors{ - "data/id": val.Validate(req.Data.ID, val.Required, isHex), - "data/type": val.Validate(req.Data.Type, val.Required, val.In(resources.CREATE_AIRDROP)), - "data/attributes/address": val.Validate(req.Data.Attributes.Address, val.Required, isHex), - - "data/attributes/passport_data/zkproof/proof": val.Validate(passport.ZKProof.Proof, val.Required), - "data/attributes/passport_data/zkproof/pub_signals": val.Validate(passport.ZKProof.PubSignals, val.Required), - - "data/attributes/passport_data/document_sod/algorithm": val.Validate(sod.Algorithm, val.Required), - "data/attributes/passport_data/document_sod/signed_attributes": val.Validate(sod.SignedAttributes, val.Required, isHex), - "data/attributes/passport_data/document_sod/encapsulated_content": val.Validate(sod.EncapsulatedContent, val.Required, isHex), - "data/attributes/passport_data/document_sod/signature": val.Validate(sod.Signature, val.Required, isHex), - "data/attributes/passport_data/document_sod/pem_file": val.Validate(sod.PemFile, val.Required), + "pub_signals/nullifier": val.Validate(signals[PubSignalNullifier], val.Required), + "pub_signals/selector": val.Validate(signals[pubSignalSelector], val.Required, val.In(proofSelectorValue)), + "pub_signals/expiration_date": val.Validate(signals[pubSignalExpirationDate], val.Required, afterDate(time.Now().UTC())), + "pub_signals/birth_date": val.Validate(signals[pubSignalBirthDate], val.Required, beforeDate(olderThanDate)), + "pub_signals/citizenship": val.Validate(signals[pubSignalCitizenship], val.Required, val.In(cfg.AllowedCitizenships...)), + "pub_signals/event_data": val.Validate(signals[pubSignalEventData], val.Required, val.In(addrHash, "0x"+addrHash)), }.Filter() } +func poseidonHash(addr string) (string, error) { + bytes, err := types.AccAddressFromBech32(addr) + if err != nil { + return "", fmt.Errorf("invalid address: %w", err) + } + bigAddr := new(big.Int).SetBytes(bytes) + + h, err := poseidon.Hash([]*big.Int{bigAddr}) + if err != nil { + return "", fmt.Errorf("hash address: %w", err) + } + + return h.Text(16), nil +} + func newDecodeError(what string, err error) error { return val.Errors{ what: fmt.Errorf("decode request %s: %w", what, err), diff --git a/internal/service/requests/hex_rule.go b/internal/service/requests/hex_rule.go deleted file mode 100644 index 14fe06c..0000000 --- a/internal/service/requests/hex_rule.go +++ /dev/null @@ -1,24 +0,0 @@ -package requests - -import ( - "encoding/hex" - "fmt" -) - -var isHex hexRule - -type hexRule struct{} - -func (r hexRule) Validate(data interface{}) error { - str, ok := data.(string) - if !ok { - return fmt.Errorf("invalid type: %T, expected string", data) - } - - _, err := hex.DecodeString(str) - if err != nil { - return fmt.Errorf("invalid hex string: %w", err) - } - - return nil -} diff --git a/internal/service/requests/validation.go b/internal/service/requests/validation.go new file mode 100644 index 0000000..fc01c48 --- /dev/null +++ b/internal/service/requests/validation.go @@ -0,0 +1,68 @@ +package requests + +import ( + "encoding/hex" + "errors" + "fmt" + "time" +) + +var isHex hexRule + +type ( + hexRule struct{} + timeRule struct { + point time.Time + isBefore bool + } +) + +func (r hexRule) Validate(data interface{}) error { + str, ok := data.(string) + if !ok { + return fmt.Errorf("invalid type: %T, expected string", data) + } + + _, err := hex.DecodeString(str) + if err != nil { + return fmt.Errorf("invalid hex string: %w", err) + } + + return nil +} + +func (r timeRule) Validate(date interface{}) error { + str, ok := date.(string) + if !ok { + return fmt.Errorf("invalid type: %T, expected string", date) + } + + parsed, err := time.Parse("060102", str) + if err != nil { + return fmt.Errorf("invalid date string: %w", err) + } + + if r.isBefore && parsed.After(r.point) { + return errors.New("date is too late") + } + + if !r.isBefore && parsed.Before(r.point) { + return errors.New("date is too early") + } + + return nil +} + +func beforeDate(point time.Time) timeRule { + return timeRule{ + point: point, + isBefore: false, + } +} + +func afterDate(point time.Time) timeRule { + return timeRule{ + point: point, + isBefore: false, + } +} diff --git a/resources/model_create_airdrop_attributes.go b/resources/model_create_airdrop_attributes.go index c922076..6dfb49e 100644 --- a/resources/model_create_airdrop_attributes.go +++ b/resources/model_create_airdrop_attributes.go @@ -4,9 +4,13 @@ package resources +import "github.com/iden3/go-rapidsnark/types" + type CreateAirdropAttributes struct { // Destination address for the airdrop Address string `json:"address"` - // All passport-related data required to verify passport-based ZKP - PassportData PassportData `json:"passport_data"` + // Signing algorithm used in proof. The value from passport document SOD is assumed. + Algorithm string `json:"algorithm"` + // ZK-proof of the passport data + ZkProof types.ZKProof `json:"zk_proof"` } diff --git a/resources/passport_data.go b/resources/passport_data.go deleted file mode 100644 index f848363..0000000 --- a/resources/passport_data.go +++ /dev/null @@ -1,64 +0,0 @@ -package resources - -import ( - "encoding/asn1" - - "github.com/iden3/go-rapidsnark/types" -) - -type PassportData struct { - ZKProof types.ZKProof `json:"zkproof"` - DocumentSOD DocumentSOD `json:"document_sod"` -} - -type DocumentSOD struct { - SignedAttributes string `json:"signed_attributes"` - Algorithm string `json:"algorithm"` - Signature string `json:"signature"` - PemFile string `json:"pem_file"` - EncapsulatedContent string `json:"encapsulated_content"` -} - -type DigestAttribute struct { - ID asn1.ObjectIdentifier - Digest []asn1.RawValue `asn1:"set"` -} - -type EncapsulatedData struct { - Version int - PrivateKeyAlgorithm asn1.RawValue - PrivateKey struct { - El1 struct { - Integer int - OctetStr asn1.RawValue - } - El2 struct { - Integer int - OctetStr asn1.RawValue - } - El3 struct { - Integer int - OctetStr asn1.RawValue - } - El4 struct { - Integer int - OctetStr asn1.RawValue - } - El5 struct { - Integer int - OctetStr asn1.RawValue - } - El6 struct { - Integer int - OctetStr asn1.RawValue - } - El7 struct { - Integer int - OctetStr asn1.RawValue - } - El8 struct { - Integer int - OctetStr asn1.RawValue - } - } -} From 5bc921992c13c479a90cd7a222e00ae2e479e603 Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Wed, 17 Apr 2024 15:56:45 +0300 Subject: [PATCH 04/19] Fix bugs with base validation, add key samples --- internal/service/requests/create_airdrop.go | 2 +- internal/service/requests/validation.go | 15 ++- internal/service/router.go | 10 ++ sha1_verification_key.json | 139 ++++++++++++++++++++ sha256_verification_key.json | 139 ++++++++++++++++++++ 5 files changed, 297 insertions(+), 8 deletions(-) create mode 100644 sha1_verification_key.json create mode 100644 sha256_verification_key.json diff --git a/internal/service/requests/create_airdrop.go b/internal/service/requests/create_airdrop.go index 2aa2219..73fb3ed 100644 --- a/internal/service/requests/create_airdrop.go +++ b/internal/service/requests/create_airdrop.go @@ -38,7 +38,7 @@ func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resource err = val.Errors{ "data/type": val.Validate(req.Data.Type, val.Required, val.In(resources.CREATE_AIRDROP)), - "data/attributes/address": val.Validate(attr.Address, val.Required, isHex), + "data/attributes/address": val.Validate(attr.Address, val.Required, isRarimoAddr), "data/attributes/algorithm": val.Validate(attr.Algorithm, val.Required), "data/attributes/zk_proof/proof": val.Validate(attr.ZkProof.Proof, val.Required), "data/attributes/zk_proof/pub_signals": val.Validate(signals, val.Required, val.Length(14, 14)), diff --git a/internal/service/requests/validation.go b/internal/service/requests/validation.go index fc01c48..7835d8e 100644 --- a/internal/service/requests/validation.go +++ b/internal/service/requests/validation.go @@ -1,31 +1,32 @@ package requests import ( - "encoding/hex" "errors" "fmt" "time" + + "github.com/cosmos/cosmos-sdk/types" ) -var isHex hexRule +var isRarimoAddr addressRule type ( - hexRule struct{} - timeRule struct { + addressRule struct{} + timeRule struct { point time.Time isBefore bool } ) -func (r hexRule) Validate(data interface{}) error { +func (r addressRule) Validate(data interface{}) error { str, ok := data.(string) if !ok { return fmt.Errorf("invalid type: %T, expected string", data) } - _, err := hex.DecodeString(str) + _, err := types.AccAddressFromBech32(str) if err != nil { - return fmt.Errorf("invalid hex string: %w", err) + return fmt.Errorf("invalid bech32 address: %w", err) } return nil diff --git a/internal/service/router.go b/internal/service/router.go index 3e325ea..e7df866 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -3,6 +3,7 @@ package service import ( "context" + "github.com/cosmos/cosmos-sdk/types" "github.com/go-chi/chi" "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/internal/service/handlers" @@ -10,6 +11,7 @@ import ( ) func Run(ctx context.Context, cfg *config.Config) { + setBech32Prefixes() r := chi.NewRouter() r.Use( @@ -30,3 +32,11 @@ func Run(ctx context.Context, cfg *config.Config) { cfg.Log().Info("Service started") ape.Serve(ctx, r, cfg, ape.ServeOpts{}) } + +func setBech32Prefixes() { + c := types.GetConfig() + c.SetBech32PrefixForAccount("rarimo", "rarimopub") + c.SetBech32PrefixForValidator("rarimovaloper", "rarimovaloperpub") + c.SetBech32PrefixForConsensusNode("rarimovalcons", "rarimovalconspub") + c.Seal() +} diff --git a/sha1_verification_key.json b/sha1_verification_key.json new file mode 100644 index 0000000..e245fc1 --- /dev/null +++ b/sha1_verification_key.json @@ -0,0 +1,139 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 10, + "vk_alpha_1": [ + "20491192805390485299153009773594534940189261866228447918068658471970481763042", + "9383485363053290200918347156157836566562967994039712273449902621266178545958", + "1" + ], + "vk_beta_2": [ + [ + "6375614351688725206403948262868962793625744043794305715222011528459656738731", + "4252822878758300859123897981450591353533073413197771768651442665752259397132" + ], + [ + "10505242626370262277552901082094356697409835680220590971873171140371331206856", + "21847035105528745403288232691147584728191162732299865338377159692350059136679" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "13335603151114257085544796181936358705000717656954191293817584669435541903997", + "855044178118616772310849467209193664762752654954052490163498625045180193756" + ], + [ + "6302336779246085612303849712824706487368936725589359697885301708175792844786", + "3784551810894943508748993329302756088278319564314930030748154195034083326587" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "2029413683389138792403550203267699914886160938906632433982220835551125967885", + "21072700047562757817161031222997517981543347628379360635925549008442030252106" + ], + [ + "5940354580057074848093997050200682056184807770593307860589430076672439820312", + "12156638873931618554171829126792193045421052652279363021382169897324752428276" + ], + [ + "7898200236362823042373859371574133993780991612861777490112507062703164551277", + "7074218545237549455313236346927434013100842096812539264420499035217050630853" + ] + ], + [ + [ + "7077479683546002997211712695946002074877511277312570035766170199895071832130", + "10093483419865920389913245021038182291233451549023025229112148274109565435465" + ], + [ + "4595479056700221319381530156280926371456704509942304414423590385166031118820", + "19831328484489333784475432780421641293929726139240675179672856274388269393268" + ], + [ + "11934129596455521040620786944827826205713621633706285934057045369193958244500", + "8037395052364110730298837004334506829870972346962140206007064471173334027475" + ] + ] + ], + "IC": [ + [ + "19766712073108394055778654604653161828599986192416085027770686939657537474394", + "12642379111858059083801753438279635767167548213546287271697232928586495515699", + "1" + ], + [ + "584762057455664547744476471213046814865611820077786283125673808213893818755", + "2912432121204396054706954371886894328141865251111756656662249761743998496191", + "1" + ], + [ + "19451045650299307320044727951276477304372732815205278053357027625954057505564", + "7100217652478779412031569841873451007978853870973219012300086874189787375785", + "1" + ], + [ + "11970078222384517632961845147934946655612526886659849027950134923294732029165", + "6527685793983467597201811761846003066122316061526297927932748528606993248636", + "1" + ], + [ + "2660457081731774785128097336270231248352700431474148108139412388195283182232", + "10488555146741721756573736739665847987061152580772800352624573820935951088344", + "1" + ], + [ + "4893978980811664218839438027979908249454919893355746208690065504815606996262", + "19502969513500282812422680455994826923834050704271610731578081635166186993501", + "1" + ], + [ + "18439384519127139362275927514462884403559719228548124935120397729435517213503", + "7048874182435608031065423155085957060967730132845918622494815786952189070741", + "1" + ], + [ + "11059240975576033930164464853226216820462476497935609277365762403945847356460", + "2881196981032565074325013910354406643711256837091689775171647854359302526073", + "1" + ], + [ + "18135724539352256541449998224099007381748021073153872484479250984089064579622", + "7992354498594564483034548722246985239588388054478138294348024661383582846574", + "1" + ], + [ + "5053719041764043456087341180127062799902390878534108335055115084702857369676", + "20142380894288460545079211888023687300985182521228248118922317151986740129783", + "1" + ], + [ + "13509562356188107648974321344583644995728503613402077845936593792883627805260", + "7794092723380352848845619773297911712349571618205916500009692182982271988232", + "1" + ] + ] +} \ No newline at end of file diff --git a/sha256_verification_key.json b/sha256_verification_key.json new file mode 100644 index 0000000..476b7d8 --- /dev/null +++ b/sha256_verification_key.json @@ -0,0 +1,139 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 10, + "vk_alpha_1": [ + "20491192805390485299153009773594534940189261866228447918068658471970481763042", + "9383485363053290200918347156157836566562967994039712273449902621266178545958", + "1" + ], + "vk_beta_2": [ + [ + "6375614351688725206403948262868962793625744043794305715222011528459656738731", + "4252822878758300859123897981450591353533073413197771768651442665752259397132" + ], + [ + "10505242626370262277552901082094356697409835680220590971873171140371331206856", + "21847035105528745403288232691147584728191162732299865338377159692350059136679" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "8767943132255908659621448864311181023075456751167467131291557114128374509934", + "5877101292351415218299489783347327985138326881578758780380449942726943140276" + ], + [ + "12680584410820365890672996430300519834411616530112645757178056454416163820692", + "19774966158762863707082168545275433613524686193433871664759865203431034905947" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "2029413683389138792403550203267699914886160938906632433982220835551125967885", + "21072700047562757817161031222997517981543347628379360635925549008442030252106" + ], + [ + "5940354580057074848093997050200682056184807770593307860589430076672439820312", + "12156638873931618554171829126792193045421052652279363021382169897324752428276" + ], + [ + "7898200236362823042373859371574133993780991612861777490112507062703164551277", + "7074218545237549455313236346927434013100842096812539264420499035217050630853" + ] + ], + [ + [ + "7077479683546002997211712695946002074877511277312570035766170199895071832130", + "10093483419865920389913245021038182291233451549023025229112148274109565435465" + ], + [ + "4595479056700221319381530156280926371456704509942304414423590385166031118820", + "19831328484489333784475432780421641293929726139240675179672856274388269393268" + ], + [ + "11934129596455521040620786944827826205713621633706285934057045369193958244500", + "8037395052364110730298837004334506829870972346962140206007064471173334027475" + ] + ] + ], + "IC": [ + [ + "15173812305309831757708639027315020189449057594488219448543959207607673084279", + "4814820718200744916511464835021856303255833249952873915686759979417600552947", + "1" + ], + [ + "3444252994063267366080312721417215139713916628122723523087529286045650776409", + "4361919222828239648312602078120666110225297919827173779067050201452455249930", + "1" + ], + [ + "19300808472721615667034958542809903725324875358810540778846814364169650800414", + "20426091859060010655343410540159901955165826321293328056909481802779225131221", + "1" + ], + [ + "8329495446876201996995806231175162591746860221198959263467553007658725162143", + "17508461337160224747297796306311768894282268327661620636214629260705532549624", + "1" + ], + [ + "20040123241747600892666259227496013749215236854660587785663857812228012673762", + "5729443332333637593770762404117272066047057113326960028413991477297974183903", + "1" + ], + [ + "3024259430970802219526870158524046712149294065997344281422276885118044200553", + "9801065014450203161439146571266682567573487164679976344521031094443632198026", + "1" + ], + [ + "15667469284259607099267228672405726445698960078994075111208469718859122323637", + "10787447162480273994046848927763585296663244037416113612572074251856207793682", + "1" + ], + [ + "9820074783160412985187460372522436631200468609751139914172627821323239779064", + "12972559272391447938575941241577431744095602567340658045147691923715011520072", + "1" + ], + [ + "5419213375484459883306350143506463889901576856899122960678862270845991915096", + "9261974966733576467250263770968551812519600034135314844726635880580498504025", + "1" + ], + [ + "11290445513758753115012850630733665404002511428601534527806086290324782552840", + "1659743188610609462703631354829204020986657725865076762217376006074675596814", + "1" + ], + [ + "14821705212209839031788750598029676874964794243356605498494182067162800708956", + "1840975772780294955842758105215237594901536551454124194460024363325959212487", + "1" + ] + ] +} \ No newline at end of file From de39879a53ff260c9dfef4cf51d2258d4480d33e Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Wed, 17 Apr 2024 18:21:56 +0300 Subject: [PATCH 05/19] Fix parsing int pub signals, change key, fix handler --- internal/data/participants.go | 5 +- internal/service/handlers/create_airdrop.go | 96 +----------- internal/service/requests/create_airdrop.go | 30 +--- internal/service/requests/validation.go | 16 +- verification_key.json | 159 ++++++++++++++++++++ 5 files changed, 185 insertions(+), 121 deletions(-) create mode 100644 verification_key.json diff --git a/internal/data/participants.go b/internal/data/participants.go index b219e6d..93c4507 100644 --- a/internal/data/participants.go +++ b/internal/data/participants.go @@ -35,7 +35,10 @@ func (q *ParticipantsQ) New() *ParticipantsQ { } func (q *ParticipantsQ) Insert(nullifier, address string) error { - stmt := squirrel.Insert(participantsTable).Columns("nullifier").Values(nullifier) + stmt := squirrel.Insert(participantsTable).SetMap(map[string]interface{}{ + "nullifier": nullifier, + "address": address, + }) if err := q.db.Exec(stmt); err != nil { return fmt.Errorf("insert participant %s: %w", nullifier, err) diff --git a/internal/service/handlers/create_airdrop.go b/internal/service/handlers/create_airdrop.go index c6cdb8c..96920eb 100644 --- a/internal/service/handlers/create_airdrop.go +++ b/internal/service/handlers/create_airdrop.go @@ -1,15 +1,9 @@ package handlers import ( - "bytes" - "crypto/x509" - "errors" "fmt" - "math/big" "net/http" - "strconv" "strings" - "time" cosmos "github.com/cosmos/cosmos-sdk/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -41,7 +35,9 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { return } - participant, err := ParticipantsQ(r).Get(req.Data.ID) + nullifier := req.Data.Attributes.ZkProof.PubSignals[requests.PubSignalNullifier] + + participant, err := ParticipantsQ(r).Get(nullifier) if err != nil { Log(r).WithError(err).Error("Failed to get participant by ID") ape.RenderErr(w, problems.InternalError()) @@ -59,7 +55,6 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { })...) return } - nullifier := req.Data.Attributes.ZkProof.PubSignals[requests.PubSignalNullifier] err = ParticipantsQ(r).Transaction(func() error { err = ParticipantsQ(r).Insert(nullifier, req.Data.Attributes.Address) @@ -132,91 +127,6 @@ func signatureAlgorithm(passedAlgorithm string) string { return "" } -func validateCurrentDate(pubSignals []string) error { - year, err := strconv.Atoi(pubSignals[3]) - if err != nil { - return fmt.Errorf("invalid year: %w", err) - } - - month, err := strconv.Atoi(pubSignals[4]) - if err != nil { - return fmt.Errorf("invalid month: %w", err) - } - - day, err := strconv.Atoi(pubSignals[5]) - if err != nil { - return fmt.Errorf("invalid day: %w", err) - } - - currentTime := time.Now().UTC() - - if currentTime.Year() != (2000 + year) { - return fmt.Errorf("invalid year, expected %d, got %d", currentTime.Year(), 2000+year) - } - - if currentTime.Month() != time.Month(month) { - return fmt.Errorf("invalid month, expected %d, got %d", currentTime.Month(), month) - } - - if currentTime.Day() != day { - return fmt.Errorf("invalid day, expected %d, got %d", currentTime.Day(), day) - } - - return nil -} - -func validatePubSignalsAge(cfg *config.VerifierConfig, agePubSignal string) error { - age, err := strconv.Atoi(agePubSignal) - if err != nil { - return fmt.Errorf("age is not int: %w", err) - } - if age < cfg.AllowedAge { - return errors.New("invalid age") - } - return nil -} - -func validateCert(cert *x509.Certificate, masterCertsPem []byte) error { - roots := x509.NewCertPool() - roots.AppendCertsFromPEM(masterCertsPem) - - foundCerts, err := cert.Verify(x509.VerifyOptions{Roots: roots}) - if err != nil { - return fmt.Errorf("invalid certificate: %w", err) - } - - if len(foundCerts) == 0 { - return fmt.Errorf("invalid certificate: not ") - } - - return nil -} - -func stringsToArrayBigInt(publicSignals []string) ([]*big.Int, error) { - p := make([]*big.Int, 0, len(publicSignals)) - for _, s := range publicSignals { - sb, err := stringToBigInt(s) - if err != nil { - return nil, err - } - p = append(p, sb) - } - return p, nil -} - -func stringToBigInt(s string) (*big.Int, error) { - base := 10 - if bytes.HasPrefix([]byte(s), []byte("0x")) { - base = 16 - s = strings.TrimPrefix(s, "0x") - } - n, ok := new(big.Int).SetString(s, base) - if !ok { - return nil, fmt.Errorf("cannot parse string to *big.Int: %s (base=%d)", s, base) - } - return n, nil -} - func broadcastWithdrawalTx(req resources.CreateAirdropRequest, r *http.Request) error { urmo := AirdropAmount(r) tx := &bank.MsgSend{ diff --git a/internal/service/requests/create_airdrop.go b/internal/service/requests/create_airdrop.go index 73fb3ed..6e2c8fe 100644 --- a/internal/service/requests/create_airdrop.go +++ b/internal/service/requests/create_airdrop.go @@ -3,13 +3,11 @@ package requests import ( "encoding/json" "fmt" - "math/big" "net/http" "time" "github.com/cosmos/cosmos-sdk/types" val "github.com/go-ozzo/ozzo-validation/v4" - "github.com/iden3/go-iden3-crypto/poseidon" "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/resources" ) @@ -22,7 +20,7 @@ const ( pubSignalEventData = 10 pubSignalSelector = 12 - proofSelectorValue = "placeholder" // todo configure + proofSelectorValue = "39" ) func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resources.CreateAirdropRequest, err error) { @@ -47,36 +45,20 @@ func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resource return req, err } - addrHash, err := poseidonHash(attr.Address) - if err != nil { - return req, val.Errors{"data/attributes/address": err} - } + addrBytes, _ := types.AccAddressFromBech32(attr.Address) + addrDec := encodeInt(addrBytes) + citizenship := decodeInt(signals[pubSignalCitizenship]) return req, val.Errors{ "pub_signals/nullifier": val.Validate(signals[PubSignalNullifier], val.Required), "pub_signals/selector": val.Validate(signals[pubSignalSelector], val.Required, val.In(proofSelectorValue)), "pub_signals/expiration_date": val.Validate(signals[pubSignalExpirationDate], val.Required, afterDate(time.Now().UTC())), "pub_signals/birth_date": val.Validate(signals[pubSignalBirthDate], val.Required, beforeDate(olderThanDate)), - "pub_signals/citizenship": val.Validate(signals[pubSignalCitizenship], val.Required, val.In(cfg.AllowedCitizenships...)), - "pub_signals/event_data": val.Validate(signals[pubSignalEventData], val.Required, val.In(addrHash, "0x"+addrHash)), + "pub_signals/citizenship": val.Validate(citizenship, val.Required, val.In(cfg.AllowedCitizenships...)), + "pub_signals/event_data": val.Validate(signals[pubSignalEventData], val.Required, val.In(addrDec, "0x"+addrDec)), }.Filter() } -func poseidonHash(addr string) (string, error) { - bytes, err := types.AccAddressFromBech32(addr) - if err != nil { - return "", fmt.Errorf("invalid address: %w", err) - } - bigAddr := new(big.Int).SetBytes(bytes) - - h, err := poseidon.Hash([]*big.Int{bigAddr}) - if err != nil { - return "", fmt.Errorf("hash address: %w", err) - } - - return h.Text(16), nil -} - func newDecodeError(what string, err error) error { return val.Errors{ what: fmt.Errorf("decode request %s: %w", what, err), diff --git a/internal/service/requests/validation.go b/internal/service/requests/validation.go index 7835d8e..592aacb 100644 --- a/internal/service/requests/validation.go +++ b/internal/service/requests/validation.go @@ -3,6 +3,7 @@ package requests import ( "errors" "fmt" + "math/big" "time" "github.com/cosmos/cosmos-sdk/types" @@ -33,12 +34,12 @@ func (r addressRule) Validate(data interface{}) error { } func (r timeRule) Validate(date interface{}) error { - str, ok := date.(string) + raw, ok := date.(string) if !ok { return fmt.Errorf("invalid type: %T, expected string", date) } - parsed, err := time.Parse("060102", str) + parsed, err := time.Parse("060102", decodeInt(raw)) if err != nil { return fmt.Errorf("invalid date string: %w", err) } @@ -57,7 +58,7 @@ func (r timeRule) Validate(date interface{}) error { func beforeDate(point time.Time) timeRule { return timeRule{ point: point, - isBefore: false, + isBefore: true, } } @@ -67,3 +68,12 @@ func afterDate(point time.Time) timeRule { isBefore: false, } } + +func encodeInt(b []byte) string { + return new(big.Int).SetBytes(b).String() +} + +func decodeInt(s string) string { + b, _ := new(big.Int).SetString(s, 10) + return string(b.Bytes()) +} diff --git a/verification_key.json b/verification_key.json new file mode 100644 index 0000000..dc96554 --- /dev/null +++ b/verification_key.json @@ -0,0 +1,159 @@ +{ + "protocol": "groth16", + "curve": "bn128", + "nPublic": 14, + "vk_alpha_1": [ + "20491192805390485299153009773594534940189261866228447918068658471970481763042", + "9383485363053290200918347156157836566562967994039712273449902621266178545958", + "1" + ], + "vk_beta_2": [ + [ + "6375614351688725206403948262868962793625744043794305715222011528459656738731", + "4252822878758300859123897981450591353533073413197771768651442665752259397132" + ], + [ + "10505242626370262277552901082094356697409835680220590971873171140371331206856", + "21847035105528745403288232691147584728191162732299865338377159692350059136679" + ], + [ + "1", + "0" + ] + ], + "vk_gamma_2": [ + [ + "10857046999023057135944570762232829481370756359578518086990519993285655852781", + "11559732032986387107991004021392285783925812861821192530917403151452391805634" + ], + [ + "8495653923123431417604973247489272438418190587263600148770280649306958101930", + "4082367875863433681332203403145435568316851327593401208105741076214120093531" + ], + [ + "1", + "0" + ] + ], + "vk_delta_2": [ + [ + "5881319498150167833454896747787115330082788999657353758350492637177203947132", + "9949214289812961365419380854693741624922638872034090021107822121477584145602" + ], + [ + "13195969254141148782483366563506880643405989331574131612110776671098325700438", + "3140221224938908807371141614047390878656651401496414581045172752286020285545" + ], + [ + "1", + "0" + ] + ], + "vk_alphabeta_12": [ + [ + [ + "2029413683389138792403550203267699914886160938906632433982220835551125967885", + "21072700047562757817161031222997517981543347628379360635925549008442030252106" + ], + [ + "5940354580057074848093997050200682056184807770593307860589430076672439820312", + "12156638873931618554171829126792193045421052652279363021382169897324752428276" + ], + [ + "7898200236362823042373859371574133993780991612861777490112507062703164551277", + "7074218545237549455313236346927434013100842096812539264420499035217050630853" + ] + ], + [ + [ + "7077479683546002997211712695946002074877511277312570035766170199895071832130", + "10093483419865920389913245021038182291233451549023025229112148274109565435465" + ], + [ + "4595479056700221319381530156280926371456704509942304414423590385166031118820", + "19831328484489333784475432780421641293929726139240675179672856274388269393268" + ], + [ + "11934129596455521040620786944827826205713621633706285934057045369193958244500", + "8037395052364110730298837004334506829870972346962140206007064471173334027475" + ] + ] + ], + "IC": [ + [ + "12640767553758245571267967352956172513976635016328091875889036061925791282378", + "18732056297563962620109112333764314209492109938603835829173063247081375497439", + "1" + ], + [ + "2494919605304897926546183709099895513944731758494800807871975987669137634406", + "15319608514195286442622609343041384682083637925017891703643083047941728650668", + "1" + ], + [ + "8910422356346062943614643891886220187015862875112071538363529460670454777682", + "14618180742662556112131658829158389395067692590402280788260924293481640653473", + "1" + ], + [ + "19649048006010522156164472603049612558215121134518792501589245882823988917803", + "2349243816810645250175224780832927045947724268646606088804858066190604890021", + "1" + ], + [ + "14074442618061182801862966793976530545437554989615804060753292488605235183301", + "20368551231344541123225358218889716154836901194183555434677738965733937086061", + "1" + ], + [ + "5150576700996284953710396536473066789151317398606062595087643123971106352498", + "18232257501865673565432479283352491374524329731381845228409273872798447682236", + "1" + ], + [ + "21311517897314194539621734333014206250327883496788141608534039491212570001694", + "3838029699226248534807784233114682848228944168375144232445250326068580233137", + "1" + ], + [ + "14351295920862885909045665845773355644923619480551637034403844565821112815603", + "18522367453390404353311291065773187955405331825491954087982279003316325467681", + "1" + ], + [ + "21386056948398347489860984459590141351296043198494333993406230188552765792174", + "12464372699317577949310118769190377561559055486636889866035875177931300350874", + "1" + ], + [ + "16293395202365799425472518057576675542114191917985992944794923290742387826367", + "21217507504085635215218729688124117403558875939633985860855454361513510890281", + "1" + ], + [ + "18985303574682807776482328658032455672856878624166140049602365947913054228268", + "10752468031888529607812301145831140946904486457737655907753544712563010546940", + "1" + ], + [ + "18492138447341963206451724810362765515310324533737009366631918038963848666415", + "3030936806725576419715380208693070848643446286460103249347153498626008308933", + "1" + ], + [ + "19792046990382870225562847472407083843208530026385439779373043371729702786352", + "14388571168733148745378638293267709207329261368385603526101893692467301458645", + "1" + ], + [ + "14958366088677790089811699001015396001314145299675889137280520991095114469461", + "12025358820373539495713807327200635709154220687063080641907359529111628373091", + "1" + ], + [ + "11641273402670490069515430284510043153510641052674964094482479558624101882781", + "4235042202733196648476041501000770632001069584743823089844373725827746734223", + "1" + ] + ] +} \ No newline at end of file From b0dad695d0745866c2d404e6ae19ed0c1991e91a Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Wed, 17 Apr 2024 18:46:30 +0300 Subject: [PATCH 06/19] Add verification key into docker image --- config.yaml | 4 +- sha1_verification_key.json | 139 ----------------------------------- sha256_verification_key.json | 139 ----------------------------------- werf.yaml | 6 +- 4 files changed, 7 insertions(+), 281 deletions(-) delete mode 100644 sha1_verification_key.json delete mode 100644 sha256_verification_key.json diff --git a/config.yaml b/config.yaml index 76c7bcd..f4168ca 100644 --- a/config.yaml +++ b/config.yaml @@ -14,8 +14,8 @@ broadcaster: verifier: verification_keys_paths: - sha1: "./sha1_verification_key.json" - sha256: "./sha256_verification_key.json" + sha1: "./verification_key.json" + sha256: "./verification_key.json" allowed_age: 18 allowed_citizenships: ["UKR"] diff --git a/sha1_verification_key.json b/sha1_verification_key.json deleted file mode 100644 index e245fc1..0000000 --- a/sha1_verification_key.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "protocol": "groth16", - "curve": "bn128", - "nPublic": 10, - "vk_alpha_1": [ - "20491192805390485299153009773594534940189261866228447918068658471970481763042", - "9383485363053290200918347156157836566562967994039712273449902621266178545958", - "1" - ], - "vk_beta_2": [ - [ - "6375614351688725206403948262868962793625744043794305715222011528459656738731", - "4252822878758300859123897981450591353533073413197771768651442665752259397132" - ], - [ - "10505242626370262277552901082094356697409835680220590971873171140371331206856", - "21847035105528745403288232691147584728191162732299865338377159692350059136679" - ], - [ - "1", - "0" - ] - ], - "vk_gamma_2": [ - [ - "10857046999023057135944570762232829481370756359578518086990519993285655852781", - "11559732032986387107991004021392285783925812861821192530917403151452391805634" - ], - [ - "8495653923123431417604973247489272438418190587263600148770280649306958101930", - "4082367875863433681332203403145435568316851327593401208105741076214120093531" - ], - [ - "1", - "0" - ] - ], - "vk_delta_2": [ - [ - "13335603151114257085544796181936358705000717656954191293817584669435541903997", - "855044178118616772310849467209193664762752654954052490163498625045180193756" - ], - [ - "6302336779246085612303849712824706487368936725589359697885301708175792844786", - "3784551810894943508748993329302756088278319564314930030748154195034083326587" - ], - [ - "1", - "0" - ] - ], - "vk_alphabeta_12": [ - [ - [ - "2029413683389138792403550203267699914886160938906632433982220835551125967885", - "21072700047562757817161031222997517981543347628379360635925549008442030252106" - ], - [ - "5940354580057074848093997050200682056184807770593307860589430076672439820312", - "12156638873931618554171829126792193045421052652279363021382169897324752428276" - ], - [ - "7898200236362823042373859371574133993780991612861777490112507062703164551277", - "7074218545237549455313236346927434013100842096812539264420499035217050630853" - ] - ], - [ - [ - "7077479683546002997211712695946002074877511277312570035766170199895071832130", - "10093483419865920389913245021038182291233451549023025229112148274109565435465" - ], - [ - "4595479056700221319381530156280926371456704509942304414423590385166031118820", - "19831328484489333784475432780421641293929726139240675179672856274388269393268" - ], - [ - "11934129596455521040620786944827826205713621633706285934057045369193958244500", - "8037395052364110730298837004334506829870972346962140206007064471173334027475" - ] - ] - ], - "IC": [ - [ - "19766712073108394055778654604653161828599986192416085027770686939657537474394", - "12642379111858059083801753438279635767167548213546287271697232928586495515699", - "1" - ], - [ - "584762057455664547744476471213046814865611820077786283125673808213893818755", - "2912432121204396054706954371886894328141865251111756656662249761743998496191", - "1" - ], - [ - "19451045650299307320044727951276477304372732815205278053357027625954057505564", - "7100217652478779412031569841873451007978853870973219012300086874189787375785", - "1" - ], - [ - "11970078222384517632961845147934946655612526886659849027950134923294732029165", - "6527685793983467597201811761846003066122316061526297927932748528606993248636", - "1" - ], - [ - "2660457081731774785128097336270231248352700431474148108139412388195283182232", - "10488555146741721756573736739665847987061152580772800352624573820935951088344", - "1" - ], - [ - "4893978980811664218839438027979908249454919893355746208690065504815606996262", - "19502969513500282812422680455994826923834050704271610731578081635166186993501", - "1" - ], - [ - "18439384519127139362275927514462884403559719228548124935120397729435517213503", - "7048874182435608031065423155085957060967730132845918622494815786952189070741", - "1" - ], - [ - "11059240975576033930164464853226216820462476497935609277365762403945847356460", - "2881196981032565074325013910354406643711256837091689775171647854359302526073", - "1" - ], - [ - "18135724539352256541449998224099007381748021073153872484479250984089064579622", - "7992354498594564483034548722246985239588388054478138294348024661383582846574", - "1" - ], - [ - "5053719041764043456087341180127062799902390878534108335055115084702857369676", - "20142380894288460545079211888023687300985182521228248118922317151986740129783", - "1" - ], - [ - "13509562356188107648974321344583644995728503613402077845936593792883627805260", - "7794092723380352848845619773297911712349571618205916500009692182982271988232", - "1" - ] - ] -} \ No newline at end of file diff --git a/sha256_verification_key.json b/sha256_verification_key.json deleted file mode 100644 index 476b7d8..0000000 --- a/sha256_verification_key.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "protocol": "groth16", - "curve": "bn128", - "nPublic": 10, - "vk_alpha_1": [ - "20491192805390485299153009773594534940189261866228447918068658471970481763042", - "9383485363053290200918347156157836566562967994039712273449902621266178545958", - "1" - ], - "vk_beta_2": [ - [ - "6375614351688725206403948262868962793625744043794305715222011528459656738731", - "4252822878758300859123897981450591353533073413197771768651442665752259397132" - ], - [ - "10505242626370262277552901082094356697409835680220590971873171140371331206856", - "21847035105528745403288232691147584728191162732299865338377159692350059136679" - ], - [ - "1", - "0" - ] - ], - "vk_gamma_2": [ - [ - "10857046999023057135944570762232829481370756359578518086990519993285655852781", - "11559732032986387107991004021392285783925812861821192530917403151452391805634" - ], - [ - "8495653923123431417604973247489272438418190587263600148770280649306958101930", - "4082367875863433681332203403145435568316851327593401208105741076214120093531" - ], - [ - "1", - "0" - ] - ], - "vk_delta_2": [ - [ - "8767943132255908659621448864311181023075456751167467131291557114128374509934", - "5877101292351415218299489783347327985138326881578758780380449942726943140276" - ], - [ - "12680584410820365890672996430300519834411616530112645757178056454416163820692", - "19774966158762863707082168545275433613524686193433871664759865203431034905947" - ], - [ - "1", - "0" - ] - ], - "vk_alphabeta_12": [ - [ - [ - "2029413683389138792403550203267699914886160938906632433982220835551125967885", - "21072700047562757817161031222997517981543347628379360635925549008442030252106" - ], - [ - "5940354580057074848093997050200682056184807770593307860589430076672439820312", - "12156638873931618554171829126792193045421052652279363021382169897324752428276" - ], - [ - "7898200236362823042373859371574133993780991612861777490112507062703164551277", - "7074218545237549455313236346927434013100842096812539264420499035217050630853" - ] - ], - [ - [ - "7077479683546002997211712695946002074877511277312570035766170199895071832130", - "10093483419865920389913245021038182291233451549023025229112148274109565435465" - ], - [ - "4595479056700221319381530156280926371456704509942304414423590385166031118820", - "19831328484489333784475432780421641293929726139240675179672856274388269393268" - ], - [ - "11934129596455521040620786944827826205713621633706285934057045369193958244500", - "8037395052364110730298837004334506829870972346962140206007064471173334027475" - ] - ] - ], - "IC": [ - [ - "15173812305309831757708639027315020189449057594488219448543959207607673084279", - "4814820718200744916511464835021856303255833249952873915686759979417600552947", - "1" - ], - [ - "3444252994063267366080312721417215139713916628122723523087529286045650776409", - "4361919222828239648312602078120666110225297919827173779067050201452455249930", - "1" - ], - [ - "19300808472721615667034958542809903725324875358810540778846814364169650800414", - "20426091859060010655343410540159901955165826321293328056909481802779225131221", - "1" - ], - [ - "8329495446876201996995806231175162591746860221198959263467553007658725162143", - "17508461337160224747297796306311768894282268327661620636214629260705532549624", - "1" - ], - [ - "20040123241747600892666259227496013749215236854660587785663857812228012673762", - "5729443332333637593770762404117272066047057113326960028413991477297974183903", - "1" - ], - [ - "3024259430970802219526870158524046712149294065997344281422276885118044200553", - "9801065014450203161439146571266682567573487164679976344521031094443632198026", - "1" - ], - [ - "15667469284259607099267228672405726445698960078994075111208469718859122323637", - "10787447162480273994046848927763585296663244037416113612572074251856207793682", - "1" - ], - [ - "9820074783160412985187460372522436631200468609751139914172627821323239779064", - "12972559272391447938575941241577431744095602567340658045147691923715011520072", - "1" - ], - [ - "5419213375484459883306350143506463889901576856899122960678862270845991915096", - "9261974966733576467250263770968551812519600034135314844726635880580498504025", - "1" - ], - [ - "11290445513758753115012850630733665404002511428601534527806086290324782552840", - "1659743188610609462703631354829204020986657725865076762217376006074675596814", - "1" - ], - [ - "14821705212209839031788750598029676874964794243356605498494182067162800708956", - "1840975772780294955842758105215237594901536551454124194460024363325959212487", - "1" - ] - ] -} \ No newline at end of file diff --git a/werf.yaml b/werf.yaml index b4bcb4a..93ea40c 100644 --- a/werf.yaml +++ b/werf.yaml @@ -42,4 +42,8 @@ import: - image: builder add: /usr/local/bin/airdrop-svc to: /usr/local/bin/airdrop-svc - after: setup \ No newline at end of file + after: setup + - image: builder + add: /go/src/github.com/rarimo/airdrop-svc/verification_key.json + to: /verification_key.json + after: setup From 56be93c99a0c95fe88407214c21c3be684e0e297 Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Fri, 19 Apr 2024 12:40:55 +0300 Subject: [PATCH 07/19] Add: eventID check, endpoint to get airdrop, new fields for tx handling --- .../components/responses/invalidAuth.yaml | 5 -- docs/spec/components/schemas/Airdrop.yaml | 32 +++++++++++ docs/spec/components/schemas/AirdropKey.yaml | 12 +++++ .../integrations@airdrop-svc@airdrops.yaml | 19 ++++--- ...ntegrations@airdrop-svc@airdrops@{id}.yaml | 37 +++++++++++++ internal/assets/migrations/001_initial.sql | 7 ++- internal/data/participants.go | 23 +++++--- internal/service/handlers/create_airdrop.go | 34 ++++++------ internal/service/handlers/get_airdrop.go | 53 +++++++++++++++++++ internal/service/requests/create_airdrop.go | 15 ++++-- internal/service/router.go | 5 +- resources/model_airdrop.go | 43 +++++++++++++++ resources/model_airdrop_attributes.go | 18 +++++++ resources/model_resource_type.go | 1 + 14 files changed, 263 insertions(+), 41 deletions(-) delete mode 100644 docs/spec/components/responses/invalidAuth.yaml create mode 100644 docs/spec/components/schemas/Airdrop.yaml create mode 100644 docs/spec/components/schemas/AirdropKey.yaml create mode 100644 docs/spec/paths/integrations@airdrop-svc@airdrops@{id}.yaml create mode 100644 internal/service/handlers/get_airdrop.go create mode 100644 resources/model_airdrop.go create mode 100644 resources/model_airdrop_attributes.go diff --git a/docs/spec/components/responses/invalidAuth.yaml b/docs/spec/components/responses/invalidAuth.yaml deleted file mode 100644 index 07a81f5..0000000 --- a/docs/spec/components/responses/invalidAuth.yaml +++ /dev/null @@ -1,5 +0,0 @@ -description: You must provide a valid authorization params. -content: - application/vnd.api+json: - schema: - $ref: '#/components/schemas/Errors' \ No newline at end of file diff --git a/docs/spec/components/schemas/Airdrop.yaml b/docs/spec/components/schemas/Airdrop.yaml new file mode 100644 index 0000000..55308d1 --- /dev/null +++ b/docs/spec/components/schemas/Airdrop.yaml @@ -0,0 +1,32 @@ +allOf: + - $ref: '#/components/schemas/AirdropKey' + - type: object + required: + - attributes + properties: + attributes: + type: object + required: + - address + - status + - created_at + - updated_at + properties: + address: + type: string + description: Destination address for the airdrop + example: "rarimo1qlyq3ej7j7rrkw6sluz658pzne88ymf66vjcap" + status: + type: string + description: Status of the airdrop transaction + enum: [ pending, completed ] + created_at: + type: string + format: time.Time + description: RFC3339 UTC timestamp of the airdrop creation + example: "2021-09-01T00:00:00Z" + updated_at: + type: string + format: time.Time + description: RFC3339 UTC timestamp of the airdrop successful tx + example: "2021-09-01T00:00:00Z" diff --git a/docs/spec/components/schemas/AirdropKey.yaml b/docs/spec/components/schemas/AirdropKey.yaml new file mode 100644 index 0000000..9ec9254 --- /dev/null +++ b/docs/spec/components/schemas/AirdropKey.yaml @@ -0,0 +1,12 @@ +type: object +required: + - id + - type +properties: + id: + type: string + description: User nullifier + example: "0x04a32216f2425dc7343031352de3d62a7b0d3b4bf7a66d6c8c2aa8c9f4f2632b" + type: + type: string + enum: [ airdrop ] diff --git a/docs/spec/paths/integrations@airdrop-svc@airdrops.yaml b/docs/spec/paths/integrations@airdrop-svc@airdrops.yaml index 3e551a6..758f973 100644 --- a/docs/spec/paths/integrations@airdrop-svc@airdrops.yaml +++ b/docs/spec/paths/integrations@airdrop-svc@airdrops.yaml @@ -15,12 +15,19 @@ post: data: $ref: '#/components/schemas/CreateAirdrop' responses: - 204: - description: No content - 401: - $ref: '#/components/responses/invalidAuth' - 404: - $ref: '#/components/responses/notFound' + 201: + description: Airdrop was created, transaction was queued + content: + application/vnd.api+json: + schema: + type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/Airdrop' + 400: + $ref: '#/components/responses/invalidParameter' 409: description: Airdrop was already done content: diff --git a/docs/spec/paths/integrations@airdrop-svc@airdrops@{id}.yaml b/docs/spec/paths/integrations@airdrop-svc@airdrops@{id}.yaml new file mode 100644 index 0000000..6d4d4fc --- /dev/null +++ b/docs/spec/paths/integrations@airdrop-svc@airdrops@{id}.yaml @@ -0,0 +1,37 @@ +get: + tags: + - Airdrop + summary: Create airdrop + description: Create an airdrop for unique user. The proof will be verified. + operationId: createAirdrop + parameters: + - in: path + name: id + description: User nullifier + required: true + schema: + type: string + example: "0x04a32216f2425dc7343031352de3d62a7b0d3b4bf7a66d6c8c2aa8c9f4f2632b" + responses: + 200: + content: + application/vnd.api+json: + schema: + type: object + required: + - data + properties: + data: + $ref: '#/components/schemas/Airdrop' + 400: + $ref: '#/components/responses/invalidParameter' + 404: + $ref: '#/components/responses/notFound' + 409: + description: Airdrop was already done + content: + application/vnd.api+json: + schema: + $ref: '#/components/schemas/Errors' + 500: + $ref: '#/components/responses/internalError' diff --git a/internal/assets/migrations/001_initial.sql b/internal/assets/migrations/001_initial.sql index 6d116dd..8f4b260 100644 --- a/internal/assets/migrations/001_initial.sql +++ b/internal/assets/migrations/001_initial.sql @@ -1,10 +1,15 @@ -- +migrate Up +CREATE TYPE tx_status AS ENUM ('pending', 'completed'); + CREATE TABLE participants ( nullifier text PRIMARY KEY, address text NOT NULL, - created_at timestamp without time zone NOT NULL default NOW() + status tx_status NOT NULL, + created_at timestamp without time zone NOT NULL default NOW(), + updated_at timestamp without time zone NOT NULL default NOW() ); -- +migrate Down DROP TABLE participants; +DROP TYPE tx_status; diff --git a/internal/data/participants.go b/internal/data/participants.go index 93c4507..3176160 100644 --- a/internal/data/participants.go +++ b/internal/data/participants.go @@ -10,12 +10,19 @@ import ( "gitlab.com/distributed_lab/kit/pgdb" ) +const ( + TxStatusPending = "pending" + TxStatusCompleted = "completed" +) + const participantsTable = "participants" type Participant struct { Nullifier string `db:"nullifier"` Address string `db:"address"` + Status string `db:"status"` CreatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"created_at"` } type ParticipantsQ struct { @@ -34,17 +41,19 @@ func (q *ParticipantsQ) New() *ParticipantsQ { return NewParticipantsQ(q.db) } -func (q *ParticipantsQ) Insert(nullifier, address string) error { +func (q *ParticipantsQ) Insert(p Participant) (*Participant, error) { + var res Participant stmt := squirrel.Insert(participantsTable).SetMap(map[string]interface{}{ - "nullifier": nullifier, - "address": address, - }) + "nullifier": p.Nullifier, + "address": p.Address, + "status": p.Status, + }).Suffix("RETURNING *") - if err := q.db.Exec(stmt); err != nil { - return fmt.Errorf("insert participant %s: %w", nullifier, err) + if err := q.db.Get(&res, stmt); err != nil { + return nil, fmt.Errorf("insert participant %+v: %w", p, err) } - return nil + return &res, nil } func (q *ParticipantsQ) Transaction(fn func() error) error { diff --git a/internal/service/handlers/create_airdrop.go b/internal/service/handlers/create_airdrop.go index 96920eb..69ccc42 100644 --- a/internal/service/handlers/create_airdrop.go +++ b/internal/service/handlers/create_airdrop.go @@ -10,6 +10,7 @@ import ( validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/iden3/go-rapidsnark/verifier" "github.com/rarimo/airdrop-svc/internal/config" + data "github.com/rarimo/airdrop-svc/internal/data" "github.com/rarimo/airdrop-svc/internal/service/requests" "github.com/rarimo/airdrop-svc/resources" "gitlab.com/distributed_lab/ape" @@ -20,12 +21,9 @@ import ( // https://www.openssl.org/docs/man1.1.1/man3/SSL_CTX_set1_sigalgs_list.html const ( - SHA1 = "sha1" - SHA256 = "sha256" - - SHA256withRSA = "SHA256withRSA" - SHA1withECDSA = "SHA1withECDSA" - SHA256withECDSA = "SHA256withECDSA" + sha256rsa = "SHA256withRSA" + sha1ecdsa = "SHA1withECDSA" + sha256ecdsa = "SHA256withECDSA" ) func CreateAirdrop(w http.ResponseWriter, r *http.Request) { @@ -57,7 +55,11 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { } err = ParticipantsQ(r).Transaction(func() error { - err = ParticipantsQ(r).Insert(nullifier, req.Data.Attributes.Address) + participant, err = ParticipantsQ(r).Insert(data.Participant{ + Nullifier: nullifier, + Address: req.Data.Attributes.Address, + Status: data.TxStatusPending, + }) if err != nil { return fmt.Errorf("insert participant: %w", err) } @@ -70,17 +72,17 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { return } - w.WriteHeader(http.StatusNoContent) + ape.Render(w, toAirdropResponse(*participant)) } func verifyProof(req resources.CreateAirdropRequest, cfg *config.VerifierConfig) error { var key []byte algorithm := signatureAlgorithm(req.Data.Attributes.Algorithm) switch algorithm { - case SHA1withECDSA: - key = cfg.VerificationKeys[SHA1] - case SHA256withRSA, SHA256withECDSA: - key = cfg.VerificationKeys[SHA256] + case sha1ecdsa: + key = cfg.VerificationKeys["sha1"] + case sha256rsa, sha256ecdsa: + key = cfg.VerificationKeys["sha256"] default: return fmt.Errorf("unsupported algorithm: %s", req.Data.Attributes.Algorithm) } @@ -95,17 +97,17 @@ func verifyProof(req resources.CreateAirdropRequest, cfg *config.VerifierConfig) var algorithmsMap = map[string]map[string]string{ "SHA1": { - "ECDSA": SHA1withECDSA, + "ECDSA": sha1ecdsa, }, "SHA256": { - "RSA": SHA256withRSA, - "ECDSA": SHA256withECDSA, + "RSA": sha256rsa, + "ECDSA": sha256ecdsa, }, } func signatureAlgorithm(passedAlgorithm string) string { if passedAlgorithm == "rsaEncryption" { - return SHA256withRSA + return sha256rsa } if strings.Contains(strings.ToUpper(passedAlgorithm), "PSS") { diff --git a/internal/service/handlers/get_airdrop.go b/internal/service/handlers/get_airdrop.go new file mode 100644 index 0000000..41a81da --- /dev/null +++ b/internal/service/handlers/get_airdrop.go @@ -0,0 +1,53 @@ +package handlers + +import ( + "net/http" + + "github.com/go-chi/chi" + validation "github.com/go-ozzo/ozzo-validation/v4" + data "github.com/rarimo/airdrop-svc/internal/data" + "github.com/rarimo/airdrop-svc/resources" + "gitlab.com/distributed_lab/ape" + "gitlab.com/distributed_lab/ape/problems" +) + +func GetAirdrop(w http.ResponseWriter, r *http.Request) { + var ( + id = chi.URLParam(r, "id") + err error = validation.Errors{"{id}": validation.Validate(id, validation.Required)} + ) + if err != nil { + ape.RenderErr(w, problems.BadRequest(err)...) + return + } + + participant, err := ParticipantsQ(r).Get(id) + if err != nil { + Log(r).WithError(err).Error("Failed to get participant by ID") + ape.RenderErr(w, problems.InternalError()) + return + } + if participant == nil { + ape.RenderErr(w, problems.NotFound()) + return + } + + ape.Render(w, toAirdropResponse(*participant)) +} + +func toAirdropResponse(p data.Participant) resources.AirdropResponse { + return resources.AirdropResponse{ + Data: resources.Airdrop{ + Key: resources.Key{ + ID: p.Nullifier, + Type: resources.AIRDROP, + }, + Attributes: resources.AirdropAttributes{ + Address: p.Address, + Status: p.Status, + CreatedAt: p.CreatedAt, + UpdatedAt: p.UpdatedAt, + }, + }, + } +} diff --git a/internal/service/requests/create_airdrop.go b/internal/service/requests/create_airdrop.go index 6e2c8fe..1a441a7 100644 --- a/internal/service/requests/create_airdrop.go +++ b/internal/service/requests/create_airdrop.go @@ -3,6 +3,7 @@ package requests import ( "encoding/json" "fmt" + "math/big" "net/http" "time" @@ -17,10 +18,12 @@ const ( pubSignalBirthDate pubSignalExpirationDate pubSignalCitizenship = 6 + pubSignalEventID = 9 pubSignalEventData = 10 pubSignalSelector = 12 proofSelectorValue = "39" + proofEventIDValue = "ac42d1a986804618c7a793fbe814d9b31e47be51e082806363dca6958f3062" ) func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resources.CreateAirdropRequest, err error) { @@ -45,9 +48,12 @@ func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resource return req, err } - addrBytes, _ := types.AccAddressFromBech32(attr.Address) - addrDec := encodeInt(addrBytes) - citizenship := decodeInt(signals[pubSignalCitizenship]) + var ( + addrBytes, _ = types.AccAddressFromBech32(attr.Address) + addrDec = encodeInt(addrBytes) + citizenship = decodeInt(signals[pubSignalCitizenship]) + eventID, _ = new(big.Int).SetString(signals[pubSignalEventID], 10) + ) return req, val.Errors{ "pub_signals/nullifier": val.Validate(signals[PubSignalNullifier], val.Required), @@ -55,7 +61,8 @@ func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resource "pub_signals/expiration_date": val.Validate(signals[pubSignalExpirationDate], val.Required, afterDate(time.Now().UTC())), "pub_signals/birth_date": val.Validate(signals[pubSignalBirthDate], val.Required, beforeDate(olderThanDate)), "pub_signals/citizenship": val.Validate(citizenship, val.Required, val.In(cfg.AllowedCitizenships...)), - "pub_signals/event_data": val.Validate(signals[pubSignalEventData], val.Required, val.In(addrDec, "0x"+addrDec)), + "pub_signals/event_id": val.Validate(eventID.Text(16), val.Required, val.In(proofEventIDValue)), + "pub_signals/event_data": val.Validate(signals[pubSignalEventData], val.Required, val.In(addrDec)), }.Filter() } diff --git a/internal/service/router.go b/internal/service/router.go index e7df866..bf5809e 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -25,8 +25,9 @@ func Run(ctx context.Context, cfg *config.Config) { ), handlers.DBCloneMiddleware(cfg.DB()), ) - r.Route("/integrations/airdrop-svc", func(r chi.Router) { - r.Post("/airdrops", handlers.CreateAirdrop) + r.Route("/integrations/airdrop-svc/airdrops", func(r chi.Router) { + r.Post("/", handlers.CreateAirdrop) + r.Get("/{id}", handlers.GetAirdrop) }) cfg.Log().Info("Service started") diff --git a/resources/model_airdrop.go b/resources/model_airdrop.go new file mode 100644 index 0000000..5fd7d5f --- /dev/null +++ b/resources/model_airdrop.go @@ -0,0 +1,43 @@ +/* + * GENERATED. Do not modify. Your changes might be overwritten! + */ + +package resources + +import "encoding/json" + +type Airdrop struct { + Key + Attributes AirdropAttributes `json:"attributes"` +} +type AirdropResponse struct { + Data Airdrop `json:"data"` + Included Included `json:"included"` +} + +type AirdropListResponse struct { + Data []Airdrop `json:"data"` + Included Included `json:"included"` + Links *Links `json:"links"` + Meta json.RawMessage `json:"meta,omitempty"` +} + +func (r *AirdropListResponse) PutMeta(v interface{}) (err error) { + r.Meta, err = json.Marshal(v) + return err +} + +func (r *AirdropListResponse) GetMeta(out interface{}) error { + return json.Unmarshal(r.Meta, out) +} + +// MustAirdrop - returns Airdrop from include collection. +// if entry with specified key does not exist - returns nil +// if entry with specified key exists but type or ID mismatches - panics +func (c *Included) MustAirdrop(key Key) *Airdrop { + var airdrop Airdrop + if c.tryFindEntry(key, &airdrop) { + return &airdrop + } + return nil +} diff --git a/resources/model_airdrop_attributes.go b/resources/model_airdrop_attributes.go new file mode 100644 index 0000000..9fa2d59 --- /dev/null +++ b/resources/model_airdrop_attributes.go @@ -0,0 +1,18 @@ +/* + * GENERATED. Do not modify. Your changes might be overwritten! + */ + +package resources + +import "time" + +type AirdropAttributes struct { + // Destination address for the airdrop + Address string `json:"address"` + // RFC3339 UTC timestamp of the airdrop creation + CreatedAt time.Time `json:"created_at"` + // Status of the airdrop transaction + Status string `json:"status"` + // RFC3339 UTC timestamp of the airdrop successful tx + UpdatedAt time.Time `json:"updated_at"` +} diff --git a/resources/model_resource_type.go b/resources/model_resource_type.go index 69376c0..badfc92 100644 --- a/resources/model_resource_type.go +++ b/resources/model_resource_type.go @@ -8,5 +8,6 @@ type ResourceType string // List of ResourceType const ( + AIRDROP ResourceType = "airdrop" CREATE_AIRDROP ResourceType = "create_airdrop" ) From 5c8ebbcd14218c30eb54abdc532bdf1614b7bc4d Mon Sep 17 00:00:00 2001 From: violog <51th.apprent1ce.f0rce@gmail.com> Date: Fri, 19 Apr 2024 19:46:13 +0300 Subject: [PATCH 08/19] Add draft built-in tx broadcaster --- go.mod | 7 +- go.sum | 13 +- internal/broadcaster/broadcaster.go | 193 ++++++++++++++++++++ internal/broadcaster/config.go | 119 ++++++++++++ internal/data/participants.go | 32 +++- internal/service/handlers/create_airdrop.go | 3 +- internal/service/handlers/ctx.go | 2 +- internal/service/handlers/get_airdrop.go | 4 +- internal/service/handlers/middleware.go | 2 +- 9 files changed, 365 insertions(+), 10 deletions(-) create mode 100644 internal/broadcaster/broadcaster.go create mode 100644 internal/broadcaster/config.go diff --git a/go.mod b/go.mod index ce3d4ea..a1cbe89 100644 --- a/go.mod +++ b/go.mod @@ -6,16 +6,20 @@ require ( github.com/Masterminds/squirrel v1.4.0 github.com/alecthomas/kingpin v2.2.6+incompatible github.com/cosmos/cosmos-sdk v0.46.12 + github.com/ethereum/go-ethereum v1.13.11 github.com/go-chi/chi v4.1.2+incompatible github.com/go-ozzo/ozzo-validation/v4 v4.3.0 github.com/iden3/go-rapidsnark/types v0.0.3 github.com/iden3/go-rapidsnark/verifier v0.0.5 + github.com/rarimo/rarimo-core v0.0.0-20231004143803-6b209428ecbf github.com/rarimo/saver-grpc-lib v1.0.0 github.com/rubenv/sql-migrate v1.6.1 gitlab.com/distributed_lab/ape v1.7.1 gitlab.com/distributed_lab/figure/v3 v3.1.3 gitlab.com/distributed_lab/kit v1.11.2 gitlab.com/distributed_lab/logan v3.8.1+incompatible + gitlab.com/distributed_lab/running v0.0.0-20200706131153-4af0e83eb96c + google.golang.org/grpc v1.59.0 ) require ( @@ -58,7 +62,6 @@ require ( github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac // indirect github.com/dvsekhvalnov/jose2go v1.5.0 // indirect github.com/ethereum/c-kzg-4844 v0.4.0 // indirect - github.com/ethereum/go-ethereum v1.13.11 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/getsentry/raven-go v0.2.0 // indirect github.com/getsentry/sentry-go v0.26.0 // indirect @@ -139,7 +142,6 @@ require ( github.com/zondax/ledger-go v0.14.1 // indirect gitlab.com/distributed_lab/figure v2.1.2+incompatible // indirect gitlab.com/distributed_lab/lorem v0.2.1 // indirect - gitlab.com/distributed_lab/running v0.0.0-20200706131153-4af0e83eb96c // indirect go.etcd.io/bbolt v1.3.6 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.18.0 // indirect @@ -154,7 +156,6 @@ require ( google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect - google.golang.org/grpc v1.59.0 // indirect google.golang.org/protobuf v1.31.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index bd3be12..516877e 100644 --- a/go.sum +++ b/go.sum @@ -1157,8 +1157,9 @@ github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.11.0 h1:+CqWgvj0OZycCaqclBD1pxKHAU+tOkHmQIWvDHq2aug= +github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= @@ -1211,6 +1212,8 @@ github.com/rarimo/broadcaster-svc v1.0.2 h1:ExQcjjWCRP5+POLDlZHrTD1ffUsBH+Dgv5FA github.com/rarimo/broadcaster-svc v1.0.2/go.mod h1:lYIHy+X4IqQt4eBdtMN/V352H3EV0/gO8G+32SFwUWI= github.com/rarimo/cosmos-sdk v0.46.7 h1:jU2PiWzc+19SF02cXM0O0puKPeH1C6Q6t2lzJ9s1ejc= github.com/rarimo/cosmos-sdk v0.46.7/go.mod h1:fqKqz39U5IlEFb4nbQ72951myztsDzFKKDtffYJ63nk= +github.com/rarimo/rarimo-core v0.0.0-20231004143803-6b209428ecbf h1:NvYhOErW0d7ohn2YzGxQYKssrgVrKOvjrKL1OBQgCB4= +github.com/rarimo/rarimo-core v0.0.0-20231004143803-6b209428ecbf/go.mod h1:Onkd0EJP94hw4dT/2KH7QXRwDG4eIGeaMffSjA1i6/s= github.com/rarimo/saver-grpc-lib v1.0.0 h1:MGUVjYg7unmodYczVsLqlqZNkT4CIgKqdo6aQtL1qdE= github.com/rarimo/saver-grpc-lib v1.0.0/go.mod h1:DpugWK5B7Hi0bdC3MPe/9FD2zCxaRwsyykdwxtF1Zgg= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= @@ -1313,6 +1316,14 @@ github.com/tendermint/tendermint v0.34.24 h1:879MKKJWYYPJEMMKME+DWUTY4V9f/FBpnZD github.com/tendermint/tendermint v0.34.24/go.mod h1:rXVrl4OYzmIa1I91av3iLv2HS0fGSiucyW9J4aMTpKI= github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8= github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I= +github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= +github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= diff --git a/internal/broadcaster/broadcaster.go b/internal/broadcaster/broadcaster.go new file mode 100644 index 0000000..d9a6abb --- /dev/null +++ b/internal/broadcaster/broadcaster.go @@ -0,0 +1,193 @@ +// Package broadcaster provides the functionality to broadcast transactions to +// the blockchain. It is similar to https://github.com/rarimo/broadcaster-svc, +// but is integrated into airdrop-svc purposely. The mentioned broadcaster does +// not allow you to track even successful transaction submission. +// +// The reason of broadcasting implementation is the same: account sequence +// (nonce) must be strictly incrementing in Cosmos. +package broadcaster + +import ( + "context" + "fmt" + "time" + + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/rarimo/airdrop-svc/internal/data" + "gitlab.com/distributed_lab/logan/v3" + + clienttx "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/cosmos/cosmos-sdk/types" + client "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + ethermint "github.com/rarimo/rarimo-core/ethermint/types" + "gitlab.com/distributed_lab/running" +) + +const txCodeSuccess = 0 + +type Runner struct { + log *logan.Entry + participants *data.ParticipantsQ + Config +} + +// TODO: run in CLI +func Run(ctx context.Context, cfg config) { + log := cfg.Log().WithField("service", "builtin-broadcaster") + log.Info("Starting service") + + r := &Runner{ + log: log, + participants: data.NewParticipantsQ(cfg.DB().Clone()), + Config: cfg.Broadcaster(), + } + + running.WithBackOff(ctx, r.log, "builtin-broadcaster", r.run, 5*time.Second, 5*time.Second, 5*time.Second) +} + +func (r *Runner) run(ctx context.Context) error { + participants, err := r.participants.New().Limit(r.queryLimit).Select() + if err != nil { + return fmt.Errorf("select participants: %w", err) + } + if len(participants) == 0 { + return nil + } + r.log.Debugf("Got %d participants to broadcast airdrop transactions", len(participants)) + + // TODO: handle errors: whether we should delete the participant or assign a failed status (hard) + for _, participant := range participants { + log := r.log.WithField("participant_nullifier", participant.Nullifier) + + tx, err := r.genTx(ctx, 0, participant) + if err != nil { + log.WithError(err).Error("Failed to generate tx") + continue + } + + gasUsed, err := r.simulateTx(ctx, tx) + if err != nil { + log.WithError(err).Error("Failed to simulate tx") + continue + } + + tx, err = r.genTx(ctx, gasUsed*3, participant) + if err != nil { + log.WithError(err).Error("Failed to generate tx after simulation") + continue + } + + if err = r.broadcastTx(ctx, tx); err != nil { + log.WithError(err).Error("Failed to broadcast tx") + continue + } + + if err = r.participants.New().Delete(participant.Nullifier); err != nil { + log.WithError(err).Error("Failed to delete successful tx") + continue + } + } + + return nil +} + +func (r *Runner) genTx(ctx context.Context, gasLimit uint64, p data.Participant) ([]byte, error) { + tx, err := r.buildTransferTx(p) + if err != nil { + return nil, fmt.Errorf("build transfer tx: %w", err) + } + + builder, err := r.txConfig.WrapTxBuilder(tx) + if err != nil { + return nil, fmt.Errorf("wrap tx with builder: %w", err) + } + builder.SetGasLimit(gasLimit) + // there are no fees on the mainnet now, and applies fees requires a lot of work + builder.SetFeeAmount(types.Coins{types.NewInt64Coin("urmo", 0)}) + + resp, err := r.auth.Account(ctx, &authtypes.QueryAccountRequest{Address: r.senderAddress}) + if err != nil { + return nil, fmt.Errorf("get sender account: %w", err) + } + + var account ethermint.EthAccount + if err = account.Unmarshal(resp.Account.Value); err != nil { + return nil, fmt.Errorf("unmarshal sender account: %w", err) + } + + err = builder.SetSignatures(signing.SignatureV2{ + PubKey: r.sender.PubKey(), + Data: &signing.SingleSignatureData{ + SignMode: r.txConfig.SignModeHandler().DefaultMode(), + Signature: nil, + }, + Sequence: account.Sequence, + }) + if err != nil { + return nil, fmt.Errorf("set signatures to tx: %w", err) + } + + signerData := xauthsigning.SignerData{ + ChainID: r.chainID, + AccountNumber: account.AccountNumber, + Sequence: account.Sequence, + } + sigV2, err := clienttx.SignWithPrivKey( + r.txConfig.SignModeHandler().DefaultMode(), signerData, + builder, r.sender, r.txConfig, account.Sequence, + ) + if err != nil { + return nil, fmt.Errorf("sign with private key: %w", err) + } + + if err = builder.SetSignatures(sigV2); err != nil { + return nil, fmt.Errorf("set signatures V2: %w", err) + } + + return r.txConfig.TxEncoder()(builder.GetTx()) +} + +func (r *Runner) simulateTx(ctx context.Context, tx []byte) (gasUsed uint64, err error) { + sim, err := r.txClient.Simulate(ctx, &client.SimulateRequest{TxBytes: tx}) + if err != nil { + return 0, fmt.Errorf("simulate tx: %w", err) + } + + r.log.Debugf("Gas wanted: %d; gas used in simulation: %d", sim.GasInfo.GasWanted, sim.GasInfo.GasUsed) + return sim.GasInfo.GasUsed, nil +} + +func (r *Runner) broadcastTx(ctx context.Context, tx []byte) error { + grpcRes, err := r.txClient.BroadcastTx(ctx, &client.BroadcastTxRequest{ + Mode: client.BroadcastMode_BROADCAST_MODE_BLOCK, + TxBytes: tx, + }) + if err != nil { + return fmt.Errorf("send tx: %w", err) + } + r.log.Debugf("Submitted transaction to the core: %s", grpcRes.TxResponse.TxHash) + + if grpcRes.TxResponse.Code != txCodeSuccess { + return fmt.Errorf("got error code: %d, info: %s, log: %s", grpcRes.TxResponse.Code, grpcRes.TxResponse.Info, grpcRes.TxResponse.RawLog) + } + + return nil +} + +func (r *Runner) buildTransferTx(p data.Participant) (types.Tx, error) { + tx := &bank.MsgSend{ + FromAddress: r.senderAddress, + ToAddress: p.Address, + Amount: r.airdropCoins, + } + + builder := r.txConfig.NewTxBuilder() + if err := builder.SetMsgs(tx); err != nil { + return nil, fmt.Errorf("set messages: %w", err) + } + + return builder.GetTx(), nil +} diff --git a/internal/broadcaster/config.go b/internal/broadcaster/config.go new file mode 100644 index 0000000..7741908 --- /dev/null +++ b/internal/broadcaster/config.go @@ -0,0 +1,119 @@ +package broadcaster + +import ( + "fmt" + "time" + + sdkclient "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" + cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/bech32" + txclient "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/ethereum/go-ethereum/common/hexutil" + "gitlab.com/distributed_lab/figure/v3" + "gitlab.com/distributed_lab/kit/comfig" + "gitlab.com/distributed_lab/kit/kv" + "gitlab.com/distributed_lab/kit/pgdb" + "google.golang.org/grpc" + "google.golang.org/grpc/keepalive" +) + +const accountPrefix = "rarimo" + +type config interface { + comfig.Logger + pgdb.Databaser + Broadcasterer +} + +type Config struct { + sender cryptotypes.PrivKey + senderAddress string + chainID string + txConfig sdkclient.TxConfig + txClient txclient.ServiceClient + auth authtypes.QueryClient + airdropCoins types.Coins + queryLimit uint64 +} + +type Broadcasterer interface { + Broadcaster() Config +} + +type broadcasterer struct { + getter kv.Getter + once comfig.Once +} + +func New(getter kv.Getter) Broadcasterer { + return &broadcasterer{ + getter: getter, + } +} + +func (b *broadcasterer) Broadcaster() Config { + return b.once.Do(func() interface{} { + var cfg struct { + AirdropAmount string `fig:"airdrop_amount,required"` + CosmosRPC string `fig:"cosmos_rpc,required"` + ChainID string `fig:"chain_id,required"` + SenderPrivateKey string `fig:"sender_private_key,required"` + QueryLimit uint64 `fig:"query_limit"` + } + + err := figure.Out(&cfg).From(kv.MustGetStringMap(b.getter, "broadcaster")).Please() + if err != nil { + panic(fmt.Errorf("failed to figure out broadcaster: %w", err)) + } + + amount, err := types.ParseCoinsNormalized(cfg.AirdropAmount) + if err != nil { + panic(fmt.Errorf("broadcaster: invalid airdrop amount: %w", err)) + } + + cosmosRPC, err := grpc.Dial(cfg.CosmosRPC, grpc.WithInsecure(), grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 10 * time.Second, // wait time before ping if no activity + Timeout: 20 * time.Second, // ping timeout + })) + if err != nil { + panic(fmt.Errorf("broadcaster: failed to dial cosmos core rpc: %w", err)) + } + + privateKey, err := hexutil.Decode(cfg.SenderPrivateKey) + if err != nil { + panic(fmt.Errorf("broadcaster: sender private key is not a hex string: %w", err)) + } + + sender := &secp256k1.PrivKey{Key: privateKey} + address, err := bech32.ConvertAndEncode(accountPrefix, sender.PubKey().Address().Bytes()) + if err != nil { + panic(fmt.Errorf("failed to convert and encode sender address: %w", err)) + } + + queryLimit := uint64(100) + if cfg.QueryLimit > 0 { + queryLimit = cfg.QueryLimit + } + + return Config{ + sender: sender, + senderAddress: address, + chainID: cfg.ChainID, + txConfig: authtx.NewTxConfig( + codec.NewProtoCodec(codectypes.NewInterfaceRegistry()), + []signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT}, + ), + txClient: txclient.NewServiceClient(cosmosRPC), + auth: authtypes.NewQueryClient(cosmosRPC), + airdropCoins: amount, + queryLimit: queryLimit, + } + }).(Config) +} diff --git a/internal/data/participants.go b/internal/data/participants.go index 3176160..4c21916 100644 --- a/internal/data/participants.go +++ b/internal/data/participants.go @@ -1,4 +1,4 @@ -package pg +package data import ( "database/sql" @@ -56,6 +56,26 @@ func (q *ParticipantsQ) Insert(p Participant) (*Participant, error) { return &res, nil } +func (q *ParticipantsQ) UpdateStatus(nullifier, status string) error { + stmt := squirrel.Update(participantsTable).Set("status", status).Where(squirrel.Eq{"nullifier": nullifier}) + + if err := q.db.Exec(stmt); err != nil { + return fmt.Errorf("update participant status [nullifier=%s newStatus=%s]: %w", nullifier, status, err) + } + + return nil +} + +func (q *ParticipantsQ) Delete(nullifier string) error { + stmt := squirrel.Delete(participantsTable).Where(squirrel.Eq{"nullifier": nullifier}) + + if err := q.db.Exec(stmt); err != nil { + return fmt.Errorf("delete participant [nullifier=%s]: %w", nullifier, err) + } + + return nil +} + func (q *ParticipantsQ) Transaction(fn func() error) error { return q.db.Transaction(fn) } @@ -83,3 +103,13 @@ func (q *ParticipantsQ) Get(nullifier string) (*Participant, error) { return &res, nil } + +func (q *ParticipantsQ) Limit(limit uint64) *ParticipantsQ { + q.selector = q.selector.Limit(limit) + return q +} + +func (q *ParticipantsQ) FilterByStatus(status string) *ParticipantsQ { + q.selector = q.selector.Where(squirrel.Eq{"status": status}) + return q +} diff --git a/internal/service/handlers/create_airdrop.go b/internal/service/handlers/create_airdrop.go index 69ccc42..7310bae 100644 --- a/internal/service/handlers/create_airdrop.go +++ b/internal/service/handlers/create_airdrop.go @@ -10,7 +10,7 @@ import ( validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/iden3/go-rapidsnark/verifier" "github.com/rarimo/airdrop-svc/internal/config" - data "github.com/rarimo/airdrop-svc/internal/data" + "github.com/rarimo/airdrop-svc/internal/data" "github.com/rarimo/airdrop-svc/internal/service/requests" "github.com/rarimo/airdrop-svc/resources" "gitlab.com/distributed_lab/ape" @@ -63,6 +63,7 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { if err != nil { return fmt.Errorf("insert participant: %w", err) } + // TODO: do not broadcast return broadcastWithdrawalTx(req, r) }) diff --git a/internal/service/handlers/ctx.go b/internal/service/handlers/ctx.go index e7f8f7f..f462372 100644 --- a/internal/service/handlers/ctx.go +++ b/internal/service/handlers/ctx.go @@ -5,7 +5,7 @@ import ( "net/http" "github.com/rarimo/airdrop-svc/internal/config" - data "github.com/rarimo/airdrop-svc/internal/data" + "github.com/rarimo/airdrop-svc/internal/data" "github.com/rarimo/saver-grpc-lib/broadcaster" "gitlab.com/distributed_lab/logan/v3" ) diff --git a/internal/service/handlers/get_airdrop.go b/internal/service/handlers/get_airdrop.go index 41a81da..afcccbb 100644 --- a/internal/service/handlers/get_airdrop.go +++ b/internal/service/handlers/get_airdrop.go @@ -13,8 +13,8 @@ import ( func GetAirdrop(w http.ResponseWriter, r *http.Request) { var ( - id = chi.URLParam(r, "id") - err error = validation.Errors{"{id}": validation.Validate(id, validation.Required)} + id = chi.URLParam(r, "id") + err = validation.Errors{"{id}": validation.Validate(id, validation.Required)}.Filter() ) if err != nil { ape.RenderErr(w, problems.BadRequest(err)...) diff --git a/internal/service/handlers/middleware.go b/internal/service/handlers/middleware.go index 3325fd2..322091e 100644 --- a/internal/service/handlers/middleware.go +++ b/internal/service/handlers/middleware.go @@ -4,7 +4,7 @@ import ( "context" "net/http" - data "github.com/rarimo/airdrop-svc/internal/data" + "github.com/rarimo/airdrop-svc/internal/data" "gitlab.com/distributed_lab/kit/pgdb" ) From 579bb8a8d0c05b4346346f8402c79303c7bd6282 Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Mon, 22 Apr 2024 14:17:13 +0300 Subject: [PATCH 09/19] update: verification_key file --- verification_key.json | 68 +++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/verification_key.json b/verification_key.json index dc96554..e5feb38 100644 --- a/verification_key.json +++ b/verification_key.json @@ -37,12 +37,12 @@ ], "vk_delta_2": [ [ - "5881319498150167833454896747787115330082788999657353758350492637177203947132", - "9949214289812961365419380854693741624922638872034090021107822121477584145602" + "18902490647006052583680684949147035157755711876364177649770849967939344884538", + "19432663951169219714199180832722185001550181884984488837398124950285484557723" ], [ - "13195969254141148782483366563506880643405989331574131612110776671098325700438", - "3140221224938908807371141614047390878656651401496414581045172752286020285545" + "14591094483196085995359195833411906307448492969324321979804892684379441339558", + "5999628724148110316059290465418237180160096173074925497597305915922821774275" ], [ "1", @@ -81,78 +81,78 @@ ], "IC": [ [ - "12640767553758245571267967352956172513976635016328091875889036061925791282378", - "18732056297563962620109112333764314209492109938603835829173063247081375497439", + "21327673885005299961818505230691163479042042144639778148003692955425200736186", + "20233959945660576453804329261660391125548013335600690858049840634649293268283", "1" ], [ - "2494919605304897926546183709099895513944731758494800807871975987669137634406", - "15319608514195286442622609343041384682083637925017891703643083047941728650668", + "5903376938520018326778583809880270521491177172275283176042261261236098034824", + "15805384632440663692369401736980130251786386583587437616948123682857972322532", "1" ], [ - "8910422356346062943614643891886220187015862875112071538363529460670454777682", - "14618180742662556112131658829158389395067692590402280788260924293481640653473", + "9326220291848943201059535996242761688950495688802773201752943264311010614902", + "2003297437436365064109892185222923126758724737940241242622558954340106805287", "1" ], [ - "19649048006010522156164472603049612558215121134518792501589245882823988917803", - "2349243816810645250175224780832927045947724268646606088804858066190604890021", + "5366098896536007408381906435441844593345991512005050024745852263243878409037", + "20740059460552597602943382207610363544017420726626979303837290789685920991121", "1" ], [ - "14074442618061182801862966793976530545437554989615804060753292488605235183301", - "20368551231344541123225358218889716154836901194183555434677738965733937086061", + "20404569792849015449375865709121251498645251974471736641306215763339626800609", + "14489212707900410939196283629471436416376431939568850129009863111996688419491", "1" ], [ - "5150576700996284953710396536473066789151317398606062595087643123971106352498", - "18232257501865673565432479283352491374524329731381845228409273872798447682236", + "20251029915052268400121048730174983570679491326509635444509076172427695565733", + "8121503923205860425007604133667740884774435904318668933445724290643798160756", "1" ], [ - "21311517897314194539621734333014206250327883496788141608534039491212570001694", - "3838029699226248534807784233114682848228944168375144232445250326068580233137", + "5660476746545600816022831155597033244613491613266191153995188957757724232729", + "20927590518031292652210500445169357147761075380271057265732882151587454669838", "1" ], [ - "14351295920862885909045665845773355644923619480551637034403844565821112815603", - "18522367453390404353311291065773187955405331825491954087982279003316325467681", + "15225248180906883935064509815159151438282815605390395070094474255232393078622", + "4555050821274533518001959087157774898981791805098919625857641300660114716183", "1" ], [ - "21386056948398347489860984459590141351296043198494333993406230188552765792174", - "12464372699317577949310118769190377561559055486636889866035875177931300350874", + "14715365108364930387879568300526707316984035933059473476586781788323469412350", + "12192870055063999123137717117305345559566777164496051945164400177781475641733", "1" ], [ - "16293395202365799425472518057576675542114191917985992944794923290742387826367", - "21217507504085635215218729688124117403558875939633985860855454361513510890281", + "2082848515453217463677495129326473847336490112872029650204359132614367908122", + "2173997466818478897370776479935485571774315439446720778925042738415651328109", "1" ], [ - "18985303574682807776482328658032455672856878624166140049602365947913054228268", - "10752468031888529607812301145831140946904486457737655907753544712563010546940", + "6646023268191686713660275902219940056013913801689619995927418974603183614887", + "1231806158510607488813861836702722689673636443726119736095369434042057333372", "1" ], [ - "18492138447341963206451724810362765515310324533737009366631918038963848666415", - "3030936806725576419715380208693070848643446286460103249347153498626008308933", + "4377359631235333289892095991643823095145640220475443425003268667238699140586", + "9374374232144989733270988720515633221305302580281530317206103302242955523396", "1" ], [ - "19792046990382870225562847472407083843208530026385439779373043371729702786352", - "14388571168733148745378638293267709207329261368385603526101893692467301458645", + "8110399313240184355618312860633022154554813418871633597599517326739014864140", + "11676550680226129473408430325024673042473466539753500775196687152830088242971", "1" ], [ - "14958366088677790089811699001015396001314145299675889137280520991095114469461", - "12025358820373539495713807327200635709154220687063080641907359529111628373091", + "7024062754746500814827176673987775644012152972941215368751859266417069107618", + "2787628166343310828586499689603219957481834488889340575677676565749708186563", "1" ], [ - "11641273402670490069515430284510043153510641052674964094482479558624101882781", - "4235042202733196648476041501000770632001069584743823089844373725827746734223", + "6887254711132875200665861464020284080991043892867693682461427028944957149365", + "1644391122832138403227281593066810887181390293587245530100267365785376294120", "1" ] ] From 0aaee24c9602323ae16e2b2eecb392046f9a1aef Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Mon, 22 Apr 2024 16:43:49 +0300 Subject: [PATCH 10/19] fix: use same decoding as mobile devs, handle errors --- internal/service/requests/create_airdrop.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/internal/service/requests/create_airdrop.go b/internal/service/requests/create_airdrop.go index 1a441a7..2f4a3bc 100644 --- a/internal/service/requests/create_airdrop.go +++ b/internal/service/requests/create_airdrop.go @@ -7,7 +7,7 @@ import ( "net/http" "time" - "github.com/cosmos/cosmos-sdk/types" + "github.com/btcsuite/btcutil/bech32" val "github.com/go-ozzo/ozzo-validation/v4" "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/resources" @@ -48,11 +48,22 @@ func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resource return req, err } + _, addrBytes, err := bech32.DecodeToBase256(attr.Address) + if err != nil { + return req, newDecodeError("data/attributes/address", err) + } + + eventID, ok := new(big.Int).SetString(signals[pubSignalEventID], 10) + if !ok { + return req, newDecodeError( + "pub_signals/event_id", + fmt.Errorf("setting string %s", signals[pubSignalEventID]), + ) + } + var ( - addrBytes, _ = types.AccAddressFromBech32(attr.Address) - addrDec = encodeInt(addrBytes) - citizenship = decodeInt(signals[pubSignalCitizenship]) - eventID, _ = new(big.Int).SetString(signals[pubSignalEventID], 10) + addrDec = encodeInt(addrBytes) + citizenship = decodeInt(signals[pubSignalCitizenship]) ) return req, val.Errors{ From 5f0789724849dd6feeee67ea47c9fb14b712ba83 Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Mon, 22 Apr 2024 17:07:58 +0300 Subject: [PATCH 11/19] fix: use local tx broadcaster instead of external service --- config.yaml | 10 +++--- internal/broadcaster/broadcaster.go | 17 +++++----- internal/cli/main.go | 2 ++ internal/config/main.go | 2 +- internal/data/participants.go | 2 +- internal/service/handlers/create_airdrop.go | 36 +++------------------ internal/service/handlers/ctx.go | 23 ------------- internal/service/router.go | 2 -- 8 files changed, 22 insertions(+), 72 deletions(-) diff --git a/config.yaml b/config.yaml index f4168ca..cb60add 100644 --- a/config.yaml +++ b/config.yaml @@ -9,8 +9,11 @@ listener: addr: localhost:8000 broadcaster: - addr: broadcaster - sender_account: "rarimo15hcd6tv7pe8hk2re7hu0zg0aphqdm2dtjrs0ds" + airdrop_amount: 100urmo + cosmos_rpc: rpc_url + chain_id: chain_id + sender_private_key: priv_key + query_limit: 10 verifier: verification_keys_paths: @@ -18,6 +21,3 @@ verifier: sha256: "./verification_key.json" allowed_age: 18 allowed_citizenships: ["UKR"] - -airdrop: - amount: 100000 # urmo diff --git a/internal/broadcaster/broadcaster.go b/internal/broadcaster/broadcaster.go index d9a6abb..d558ba1 100644 --- a/internal/broadcaster/broadcaster.go +++ b/internal/broadcaster/broadcaster.go @@ -12,17 +12,16 @@ import ( "fmt" "time" - bank "github.com/cosmos/cosmos-sdk/x/bank/types" - "github.com/rarimo/airdrop-svc/internal/data" - "gitlab.com/distributed_lab/logan/v3" - clienttx "github.com/cosmos/cosmos-sdk/client/tx" "github.com/cosmos/cosmos-sdk/types" client "github.com/cosmos/cosmos-sdk/types/tx" "github.com/cosmos/cosmos-sdk/types/tx/signing" xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + bank "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/rarimo/airdrop-svc/internal/data" ethermint "github.com/rarimo/rarimo-core/ethermint/types" + "gitlab.com/distributed_lab/logan/v3" "gitlab.com/distributed_lab/running" ) @@ -34,7 +33,6 @@ type Runner struct { Config } -// TODO: run in CLI func Run(ctx context.Context, cfg config) { log := cfg.Log().WithField("service", "builtin-broadcaster") log.Info("Starting service") @@ -49,7 +47,7 @@ func Run(ctx context.Context, cfg config) { } func (r *Runner) run(ctx context.Context) error { - participants, err := r.participants.New().Limit(r.queryLimit).Select() + participants, err := r.participants.New().FilterByStatus(data.TxStatusPending).Limit(r.queryLimit).Select() if err != nil { return fmt.Errorf("select participants: %w", err) } @@ -58,7 +56,6 @@ func (r *Runner) run(ctx context.Context) error { } r.log.Debugf("Got %d participants to broadcast airdrop transactions", len(participants)) - // TODO: handle errors: whether we should delete the participant or assign a failed status (hard) for _, participant := range participants { log := r.log.WithField("participant_nullifier", participant.Nullifier) @@ -80,11 +77,13 @@ func (r *Runner) run(ctx context.Context) error { continue } - if err = r.broadcastTx(ctx, tx); err != nil { - log.WithError(err).Error("Failed to broadcast tx") + if err = r.broadcastTx(ctx, tx); err == nil { continue } + log.WithError(err).Error("Failed to broadcast tx") + + // TODO: handle errors: whether we should delete the participant or assign a failed status (hard) if err = r.participants.New().Delete(participant.Nullifier); err != nil { log.WithError(err).Error("Failed to delete successful tx") continue diff --git a/internal/cli/main.go b/internal/cli/main.go index 63b4a69..ad23b18 100644 --- a/internal/cli/main.go +++ b/internal/cli/main.go @@ -8,6 +8,7 @@ import ( "syscall" "github.com/alecthomas/kingpin" + "github.com/rarimo/airdrop-svc/internal/broadcaster" "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/internal/service" "gitlab.com/distributed_lab/kit/kv" @@ -53,6 +54,7 @@ func Run(args []string) bool { switch cmd { case serviceCmd.FullCommand(): run(service.Run) + run(func(context.Context, *config.Config) { broadcaster.Run(ctx, cfg) }) case migrateUpCmd.FullCommand(): err = MigrateUp(cfg) case migrateDownCmd.FullCommand(): diff --git a/internal/config/main.go b/internal/config/main.go index 782690d..964f6e9 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -1,7 +1,7 @@ package config import ( - "github.com/rarimo/saver-grpc-lib/broadcaster" + "github.com/rarimo/airdrop-svc/internal/broadcaster" "gitlab.com/distributed_lab/kit/comfig" "gitlab.com/distributed_lab/kit/kv" "gitlab.com/distributed_lab/kit/pgdb" diff --git a/internal/data/participants.go b/internal/data/participants.go index 4c21916..9b0a7c6 100644 --- a/internal/data/participants.go +++ b/internal/data/participants.go @@ -22,7 +22,7 @@ type Participant struct { Address string `db:"address"` Status string `db:"status"` CreatedAt time.Time `db:"created_at"` - UpdatedAt time.Time `db:"created_at"` + UpdatedAt time.Time `db:"updated_at"` } type ParticipantsQ struct { diff --git a/internal/service/handlers/create_airdrop.go b/internal/service/handlers/create_airdrop.go index 7310bae..4cd9d57 100644 --- a/internal/service/handlers/create_airdrop.go +++ b/internal/service/handlers/create_airdrop.go @@ -5,8 +5,6 @@ import ( "net/http" "strings" - cosmos "github.com/cosmos/cosmos-sdk/types" - bank "github.com/cosmos/cosmos-sdk/x/bank/types" validation "github.com/go-ozzo/ozzo-validation/v4" "github.com/iden3/go-rapidsnark/verifier" "github.com/rarimo/airdrop-svc/internal/config" @@ -54,21 +52,13 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { return } - err = ParticipantsQ(r).Transaction(func() error { - participant, err = ParticipantsQ(r).Insert(data.Participant{ - Nullifier: nullifier, - Address: req.Data.Attributes.Address, - Status: data.TxStatusPending, - }) - if err != nil { - return fmt.Errorf("insert participant: %w", err) - } - // TODO: do not broadcast - return broadcastWithdrawalTx(req, r) + participant, err = ParticipantsQ(r).Insert(data.Participant{ + Nullifier: nullifier, + Address: req.Data.Attributes.Address, + Status: data.TxStatusPending, }) - if err != nil { - Log(r).WithError(err).Error("Failed to save and perform airdrop") + Log(r).WithError(err).WithField("nullifier", nullifier).Errorf("Failed to insert participant") ape.RenderErr(w, problems.InternalError()) return } @@ -129,19 +119,3 @@ func signatureAlgorithm(passedAlgorithm string) string { return "" } - -func broadcastWithdrawalTx(req resources.CreateAirdropRequest, r *http.Request) error { - urmo := AirdropAmount(r) - tx := &bank.MsgSend{ - FromAddress: Broadcaster(r).Sender(), - ToAddress: req.Data.Attributes.Address, - Amount: cosmos.NewCoins(cosmos.NewInt64Coin("urmo", urmo)), - } - - err := Broadcaster(r).BroadcastTx(r.Context(), tx) - if err != nil { - return fmt.Errorf("broadcast withdrawal tx: %w", err) - } - - return nil -} diff --git a/internal/service/handlers/ctx.go b/internal/service/handlers/ctx.go index f462372..e87a99a 100644 --- a/internal/service/handlers/ctx.go +++ b/internal/service/handlers/ctx.go @@ -6,7 +6,6 @@ import ( "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/internal/data" - "github.com/rarimo/saver-grpc-lib/broadcaster" "gitlab.com/distributed_lab/logan/v3" ) @@ -15,8 +14,6 @@ type ctxKey int const ( logCtxKey ctxKey = iota participantsQCtxKey - airdropAmountCtxKey - broadcasterCtxKey verifierCtxKey ) @@ -40,26 +37,6 @@ func ParticipantsQ(r *http.Request) *data.ParticipantsQ { return r.Context().Value(participantsQCtxKey).(*data.ParticipantsQ).New() } -func CtxAirdropAmount(amount int64) func(context.Context) context.Context { - return func(ctx context.Context) context.Context { - return context.WithValue(ctx, airdropAmountCtxKey, amount) - } -} - -func AirdropAmount(r *http.Request) int64 { - return r.Context().Value(airdropAmountCtxKey).(int64) -} - -func CtxBroadcaster(broadcaster broadcaster.Broadcaster) func(context.Context) context.Context { - return func(ctx context.Context) context.Context { - return context.WithValue(ctx, broadcasterCtxKey, broadcaster) - } -} - -func Broadcaster(r *http.Request) broadcaster.Broadcaster { - return r.Context().Value(broadcasterCtxKey).(broadcaster.Broadcaster) -} - func CtxVerifier(entry *config.VerifierConfig) func(context.Context) context.Context { return func(ctx context.Context) context.Context { return context.WithValue(ctx, verifierCtxKey, entry) diff --git a/internal/service/router.go b/internal/service/router.go index bf5809e..0fce084 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -19,8 +19,6 @@ func Run(ctx context.Context, cfg *config.Config) { ape.LoganMiddleware(cfg.Log()), ape.CtxMiddleware( handlers.CtxLog(cfg.Log()), - handlers.CtxAirdropAmount(cfg.AirdropAmount()), - handlers.CtxBroadcaster(cfg.Broadcaster()), handlers.CtxVerifier(cfg.Verifier()), ), handlers.DBCloneMiddleware(cfg.DB()), From 84f7d7687f9ad23c6d170e96ba68cea91613d72f Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Mon, 22 Apr 2024 17:39:46 +0300 Subject: [PATCH 12/19] fix: broken migrations in stage; add: tx info in participants table --- internal/assets/migrations/001_initial.sql | 9 ++------- internal/assets/migrations/002_tx_info.sql | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 internal/assets/migrations/002_tx_info.sql diff --git a/internal/assets/migrations/001_initial.sql b/internal/assets/migrations/001_initial.sql index 8f4b260..c195cf4 100644 --- a/internal/assets/migrations/001_initial.sql +++ b/internal/assets/migrations/001_initial.sql @@ -1,15 +1,10 @@ -- +migrate Up -CREATE TYPE tx_status AS ENUM ('pending', 'completed'); - CREATE TABLE participants ( nullifier text PRIMARY KEY, address text NOT NULL, - status tx_status NOT NULL, - created_at timestamp without time zone NOT NULL default NOW(), - updated_at timestamp without time zone NOT NULL default NOW() + created_at timestamp without time zone NOT NULL default NOW() ); -- +migrate Down -DROP TABLE participants; -DROP TYPE tx_status; +DROP TABLE participants; \ No newline at end of file diff --git a/internal/assets/migrations/002_tx_info.sql b/internal/assets/migrations/002_tx_info.sql new file mode 100644 index 0000000..1f941bd --- /dev/null +++ b/internal/assets/migrations/002_tx_info.sql @@ -0,0 +1,17 @@ +-- +migrate Up +CREATE TYPE tx_status_enum AS ENUM ('pending', 'completed', 'failed'); + +ALTER TABLE participants + ADD COLUMN status tx_status_enum NOT NULL DEFAULT 'completed', + ADD COLUMN updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT NOW(), + ADD COLUMN tx_hash VARCHAR(64) NOT NULL DEFAULT '', + ADD COLUMN amount TEXT NOT NULL DEFAULT '0urmo'; + +-- +migrate Down +ALTER TABLE participants + DROP COLUMN status, + DROP COLUMN updated_at, + DROP COLUMN tx_hash, + DROP COLUMN amount; + +DROP TYPE IF EXISTS tx_status_enum; From 80cfcc562408d49f109b679f21bea87f46b7df50 Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Mon, 22 Apr 2024 17:41:28 +0300 Subject: [PATCH 13/19] fix: use cosmos-sdk method (again) --- .gitignore | 1 + go.mod | 6 +++--- go.sum | 4 ---- internal/service/requests/create_airdrop.go | 12 ++++-------- 4 files changed, 8 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 07831c8..4ec632f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ docker-compose.yaml docs/node_modules docs/web_deploy vendor/ +airdrop-svc diff --git a/go.mod b/go.mod index a1cbe89..2f7c79a 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,15 @@ go 1.22 require ( github.com/Masterminds/squirrel v1.4.0 github.com/alecthomas/kingpin v2.2.6+incompatible + github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce github.com/cosmos/cosmos-sdk v0.46.12 github.com/ethereum/go-ethereum v1.13.11 github.com/go-chi/chi v4.1.2+incompatible github.com/go-ozzo/ozzo-validation/v4 v4.3.0 + github.com/iden3/go-iden3-crypto v0.0.15 github.com/iden3/go-rapidsnark/types v0.0.3 github.com/iden3/go-rapidsnark/verifier v0.0.5 github.com/rarimo/rarimo-core v0.0.0-20231004143803-6b209428ecbf - github.com/rarimo/saver-grpc-lib v1.0.0 github.com/rubenv/sql-migrate v1.6.1 gitlab.com/distributed_lab/ape v1.7.1 gitlab.com/distributed_lab/figure/v3 v3.1.3 @@ -89,7 +90,6 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect github.com/holiman/uint256 v1.2.4 // indirect - github.com/iden3/go-iden3-crypto v0.0.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/jmoiron/sqlx v1.2.0 // indirect @@ -102,6 +102,7 @@ require ( github.com/mattn/go-isatty v0.0.17 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect + github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mtibben/percent v0.2.1 // indirect @@ -114,7 +115,6 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.39.0 // indirect github.com/prometheus/procfs v0.9.0 // indirect - github.com/rarimo/broadcaster-svc v1.0.2 // indirect github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect diff --git a/go.sum b/go.sum index 516877e..fdb302c 100644 --- a/go.sum +++ b/go.sum @@ -1208,14 +1208,10 @@ github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJf github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= -github.com/rarimo/broadcaster-svc v1.0.2 h1:ExQcjjWCRP5+POLDlZHrTD1ffUsBH+Dgv5FAgcP3BXc= -github.com/rarimo/broadcaster-svc v1.0.2/go.mod h1:lYIHy+X4IqQt4eBdtMN/V352H3EV0/gO8G+32SFwUWI= github.com/rarimo/cosmos-sdk v0.46.7 h1:jU2PiWzc+19SF02cXM0O0puKPeH1C6Q6t2lzJ9s1ejc= github.com/rarimo/cosmos-sdk v0.46.7/go.mod h1:fqKqz39U5IlEFb4nbQ72951myztsDzFKKDtffYJ63nk= github.com/rarimo/rarimo-core v0.0.0-20231004143803-6b209428ecbf h1:NvYhOErW0d7ohn2YzGxQYKssrgVrKOvjrKL1OBQgCB4= github.com/rarimo/rarimo-core v0.0.0-20231004143803-6b209428ecbf/go.mod h1:Onkd0EJP94hw4dT/2KH7QXRwDG4eIGeaMffSjA1i6/s= -github.com/rarimo/saver-grpc-lib v1.0.0 h1:MGUVjYg7unmodYczVsLqlqZNkT4CIgKqdo6aQtL1qdE= -github.com/rarimo/saver-grpc-lib v1.0.0/go.mod h1:DpugWK5B7Hi0bdC3MPe/9FD2zCxaRwsyykdwxtF1Zgg= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzywPxOvwMdxcg= diff --git a/internal/service/requests/create_airdrop.go b/internal/service/requests/create_airdrop.go index 2f4a3bc..3383e6f 100644 --- a/internal/service/requests/create_airdrop.go +++ b/internal/service/requests/create_airdrop.go @@ -7,7 +7,7 @@ import ( "net/http" "time" - "github.com/btcsuite/btcutil/bech32" + "github.com/cosmos/cosmos-sdk/types" val "github.com/go-ozzo/ozzo-validation/v4" "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/resources" @@ -48,11 +48,6 @@ func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resource return req, err } - _, addrBytes, err := bech32.DecodeToBase256(attr.Address) - if err != nil { - return req, newDecodeError("data/attributes/address", err) - } - eventID, ok := new(big.Int).SetString(signals[pubSignalEventID], 10) if !ok { return req, newDecodeError( @@ -62,8 +57,9 @@ func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resource } var ( - addrDec = encodeInt(addrBytes) - citizenship = decodeInt(signals[pubSignalCitizenship]) + addrBytes, _ = types.AccAddressFromBech32(attr.Address) + addrDec = encodeInt(addrBytes) + citizenship = decodeInt(signals[pubSignalCitizenship]) ) return req, val.Errors{ From 354c330666ae34a5d385c0c6567d5f05e05c385b Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Mon, 22 Apr 2024 18:50:52 +0300 Subject: [PATCH 14/19] add: use of internal broadcasted, updating transaction info in participants DB, airdrop amount in api ctx --- config.yaml | 2 +- internal/broadcaster/broadcaster.go | 70 +++++++++++++-------- internal/broadcaster/config.go | 5 +- internal/data/participants.go | 12 +++- internal/service/handlers/create_airdrop.go | 1 + internal/service/handlers/ctx.go | 11 ++++ internal/service/router.go | 1 + 7 files changed, 71 insertions(+), 31 deletions(-) diff --git a/config.yaml b/config.yaml index cb60add..c895ccc 100644 --- a/config.yaml +++ b/config.yaml @@ -9,7 +9,7 @@ listener: addr: localhost:8000 broadcaster: - airdrop_amount: 100urmo + airdrop_amount: 100stake cosmos_rpc: rpc_url chain_id: chain_id sender_private_key: priv_key diff --git a/internal/broadcaster/broadcaster.go b/internal/broadcaster/broadcaster.go index d558ba1..6f562b3 100644 --- a/internal/broadcaster/broadcaster.go +++ b/internal/broadcaster/broadcaster.go @@ -59,40 +59,58 @@ func (r *Runner) run(ctx context.Context) error { for _, participant := range participants { log := r.log.WithField("participant_nullifier", participant.Nullifier) - tx, err := r.genTx(ctx, 0, participant) - if err != nil { - log.WithError(err).Error("Failed to generate tx") + if err := r.handleParticipant(ctx, participant); err != nil { + log.WithError(err).Error("Failed to handle participant") continue } + } - gasUsed, err := r.simulateTx(ctx, tx) - if err != nil { - log.WithError(err).Error("Failed to simulate tx") - continue - } + return nil +} - tx, err = r.genTx(ctx, gasUsed*3, participant) - if err != nil { - log.WithError(err).Error("Failed to generate tx after simulation") - continue - } +func (r *Runner) handleParticipant(ctx context.Context, participant data.Participant) error { + tx, err := r.createAirdropTx(ctx, participant) + if err != nil { + return fmt.Errorf("creating airdrop tx: %w", err) + } - if err = r.broadcastTx(ctx, tx); err == nil { - continue + txHash, err := r.broadcastTx(ctx, tx) + if err != nil { + err = r.participants.New().UpdateStatus(participant.Nullifier, txHash, data.TxStatusFailed) + if err != nil { + return fmt.Errorf("update participant failed tx status: %w", err) } - log.WithError(err).Error("Failed to broadcast tx") + return fmt.Errorf("broadcast tx: %w", err) + } - // TODO: handle errors: whether we should delete the participant or assign a failed status (hard) - if err = r.participants.New().Delete(participant.Nullifier); err != nil { - log.WithError(err).Error("Failed to delete successful tx") - continue - } + err = r.participants.New().UpdateStatus(participant.Nullifier, txHash, data.TxStatusCompleted) + if err != nil { + return fmt.Errorf("update participant completed tx status: %w", err) } return nil } +func (r *Runner) createAirdropTx(ctx context.Context, participant data.Participant) ([]byte, error) { + tx, err := r.genTx(ctx, 0, participant) + if err != nil { + return nil, fmt.Errorf("failed to generate tx: %w", err) + } + + gasUsed, err := r.simulateTx(ctx, tx) + if err != nil { + return nil, fmt.Errorf("failed to simulate tx: %w", err) + } + + tx, err = r.genTx(ctx, gasUsed*3, participant) + if err != nil { + return nil, fmt.Errorf("failed to generate tx after simulation: %w", err) + } + + return tx, nil +} + func (r *Runner) genTx(ctx context.Context, gasLimit uint64, p data.Participant) ([]byte, error) { tx, err := r.buildTransferTx(p) if err != nil { @@ -159,28 +177,28 @@ func (r *Runner) simulateTx(ctx context.Context, tx []byte) (gasUsed uint64, err return sim.GasInfo.GasUsed, nil } -func (r *Runner) broadcastTx(ctx context.Context, tx []byte) error { +func (r *Runner) broadcastTx(ctx context.Context, tx []byte) (string, error) { grpcRes, err := r.txClient.BroadcastTx(ctx, &client.BroadcastTxRequest{ Mode: client.BroadcastMode_BROADCAST_MODE_BLOCK, TxBytes: tx, }) if err != nil { - return fmt.Errorf("send tx: %w", err) + return "", fmt.Errorf("send tx: %w", err) } r.log.Debugf("Submitted transaction to the core: %s", grpcRes.TxResponse.TxHash) if grpcRes.TxResponse.Code != txCodeSuccess { - return fmt.Errorf("got error code: %d, info: %s, log: %s", grpcRes.TxResponse.Code, grpcRes.TxResponse.Info, grpcRes.TxResponse.RawLog) + return grpcRes.TxResponse.TxHash, fmt.Errorf("got error code: %d, info: %s, log: %s", grpcRes.TxResponse.Code, grpcRes.TxResponse.Info, grpcRes.TxResponse.RawLog) } - return nil + return grpcRes.TxResponse.TxHash, nil } func (r *Runner) buildTransferTx(p data.Participant) (types.Tx, error) { tx := &bank.MsgSend{ FromAddress: r.senderAddress, ToAddress: p.Address, - Amount: r.airdropCoins, + Amount: r.AirdropCoins, } builder := r.txConfig.NewTxBuilder() diff --git a/internal/broadcaster/config.go b/internal/broadcaster/config.go index 7741908..42cedea 100644 --- a/internal/broadcaster/config.go +++ b/internal/broadcaster/config.go @@ -33,13 +33,14 @@ type config interface { } type Config struct { + AirdropCoins types.Coins + sender cryptotypes.PrivKey senderAddress string chainID string txConfig sdkclient.TxConfig txClient txclient.ServiceClient auth authtypes.QueryClient - airdropCoins types.Coins queryLimit uint64 } @@ -112,7 +113,7 @@ func (b *broadcasterer) Broadcaster() Config { ), txClient: txclient.NewServiceClient(cosmosRPC), auth: authtypes.NewQueryClient(cosmosRPC), - airdropCoins: amount, + AirdropCoins: amount, queryLimit: queryLimit, } }).(Config) diff --git a/internal/data/participants.go b/internal/data/participants.go index 9b0a7c6..fac2790 100644 --- a/internal/data/participants.go +++ b/internal/data/participants.go @@ -13,6 +13,7 @@ import ( const ( TxStatusPending = "pending" TxStatusCompleted = "completed" + TxStatusFailed = "failed" ) const participantsTable = "participants" @@ -21,6 +22,8 @@ type Participant struct { Nullifier string `db:"nullifier"` Address string `db:"address"` Status string `db:"status"` + TxHash string `db:"tx_hash"` + Amount string `db:"amount"` CreatedAt time.Time `db:"created_at"` UpdatedAt time.Time `db:"updated_at"` } @@ -47,6 +50,8 @@ func (q *ParticipantsQ) Insert(p Participant) (*Participant, error) { "nullifier": p.Nullifier, "address": p.Address, "status": p.Status, + "tx_hash": p.TxHash, + "amount": p.Amount, }).Suffix("RETURNING *") if err := q.db.Get(&res, stmt); err != nil { @@ -56,8 +61,11 @@ func (q *ParticipantsQ) Insert(p Participant) (*Participant, error) { return &res, nil } -func (q *ParticipantsQ) UpdateStatus(nullifier, status string) error { - stmt := squirrel.Update(participantsTable).Set("status", status).Where(squirrel.Eq{"nullifier": nullifier}) +func (q *ParticipantsQ) UpdateStatus(nullifier, txHash, status string) error { + stmt := squirrel.Update(participantsTable). + Set("status", status). + Set("tx_hash", txHash). + Where(squirrel.Eq{"nullifier": nullifier}) if err := q.db.Exec(stmt); err != nil { return fmt.Errorf("update participant status [nullifier=%s newStatus=%s]: %w", nullifier, status, err) diff --git a/internal/service/handlers/create_airdrop.go b/internal/service/handlers/create_airdrop.go index 4cd9d57..61acff8 100644 --- a/internal/service/handlers/create_airdrop.go +++ b/internal/service/handlers/create_airdrop.go @@ -56,6 +56,7 @@ func CreateAirdrop(w http.ResponseWriter, r *http.Request) { Nullifier: nullifier, Address: req.Data.Attributes.Address, Status: data.TxStatusPending, + Amount: AirdropAmount(r), }) if err != nil { Log(r).WithError(err).WithField("nullifier", nullifier).Errorf("Failed to insert participant") diff --git a/internal/service/handlers/ctx.go b/internal/service/handlers/ctx.go index e87a99a..5cbbe19 100644 --- a/internal/service/handlers/ctx.go +++ b/internal/service/handlers/ctx.go @@ -14,6 +14,7 @@ type ctxKey int const ( logCtxKey ctxKey = iota participantsQCtxKey + airdropAmountCtxKey verifierCtxKey ) @@ -37,6 +38,16 @@ func ParticipantsQ(r *http.Request) *data.ParticipantsQ { return r.Context().Value(participantsQCtxKey).(*data.ParticipantsQ).New() } +func CtxAirdropAmount(amount string) func(context.Context) context.Context { + return func(ctx context.Context) context.Context { + return context.WithValue(ctx, airdropAmountCtxKey, amount) + } +} + +func AirdropAmount(r *http.Request) string { + return r.Context().Value(airdropAmountCtxKey).(string) +} + func CtxVerifier(entry *config.VerifierConfig) func(context.Context) context.Context { return func(ctx context.Context) context.Context { return context.WithValue(ctx, verifierCtxKey, entry) diff --git a/internal/service/router.go b/internal/service/router.go index 0fce084..4b6d443 100644 --- a/internal/service/router.go +++ b/internal/service/router.go @@ -20,6 +20,7 @@ func Run(ctx context.Context, cfg *config.Config) { ape.CtxMiddleware( handlers.CtxLog(cfg.Log()), handlers.CtxVerifier(cfg.Verifier()), + handlers.CtxAirdropAmount(cfg.Broadcaster().AirdropCoins.String()), ), handlers.DBCloneMiddleware(cfg.DB()), ) From 3f7336c331cfce56b01865e1d9a44584bd258081 Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Mon, 22 Apr 2024 20:04:19 +0300 Subject: [PATCH 15/19] fix: docs --- docs/spec/components/schemas/Airdrop.yaml | 10 ++++++++++ .../paths/integrations@airdrop-svc@airdrops@{id}.yaml | 4 ++-- internal/service/handlers/get_airdrop.go | 2 ++ resources/model_airdrop_attributes.go | 4 ++++ 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/docs/spec/components/schemas/Airdrop.yaml b/docs/spec/components/schemas/Airdrop.yaml index 55308d1..4599f1e 100644 --- a/docs/spec/components/schemas/Airdrop.yaml +++ b/docs/spec/components/schemas/Airdrop.yaml @@ -9,6 +9,8 @@ allOf: required: - address - status + - amount + - tx_hash - created_at - updated_at properties: @@ -30,3 +32,11 @@ allOf: format: time.Time description: RFC3339 UTC timestamp of the airdrop successful tx example: "2021-09-01T00:00:00Z" + amount: + type: string + description: Amount of airdropped coins + example: "100stake" + tx_hash: + type: string + description: Hash of the airdrop transaction + example: "F1CC0E80E151A67F75E41F2CDBF07920C29C9A3CDB6131B2A23A7C9D1964AD0B" diff --git a/docs/spec/paths/integrations@airdrop-svc@airdrops@{id}.yaml b/docs/spec/paths/integrations@airdrop-svc@airdrops@{id}.yaml index 6d4d4fc..ef42277 100644 --- a/docs/spec/paths/integrations@airdrop-svc@airdrops@{id}.yaml +++ b/docs/spec/paths/integrations@airdrop-svc@airdrops@{id}.yaml @@ -1,8 +1,8 @@ get: tags: - Airdrop - summary: Create airdrop - description: Create an airdrop for unique user. The proof will be verified. + summary: Get an airdrop + description: Get an airdrop for unique user. operationId: createAirdrop parameters: - in: path diff --git a/internal/service/handlers/get_airdrop.go b/internal/service/handlers/get_airdrop.go index afcccbb..3049d46 100644 --- a/internal/service/handlers/get_airdrop.go +++ b/internal/service/handlers/get_airdrop.go @@ -47,6 +47,8 @@ func toAirdropResponse(p data.Participant) resources.AirdropResponse { Status: p.Status, CreatedAt: p.CreatedAt, UpdatedAt: p.UpdatedAt, + Amount: p.Amount, + TxHash: p.TxHash, }, }, } diff --git a/resources/model_airdrop_attributes.go b/resources/model_airdrop_attributes.go index 9fa2d59..04d00c3 100644 --- a/resources/model_airdrop_attributes.go +++ b/resources/model_airdrop_attributes.go @@ -9,10 +9,14 @@ import "time" type AirdropAttributes struct { // Destination address for the airdrop Address string `json:"address"` + // Amount of airdropped coins + Amount string `json:"amount"` // RFC3339 UTC timestamp of the airdrop creation CreatedAt time.Time `json:"created_at"` // Status of the airdrop transaction Status string `json:"status"` + // Hash of the airdrop transaction + TxHash string `json:"tx_hash"` // RFC3339 UTC timestamp of the airdrop successful tx UpdatedAt time.Time `json:"updated_at"` } From a552e8586dec4a072f99a45324172536b38b8133 Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Tue, 23 Apr 2024 11:46:58 +0300 Subject: [PATCH 16/19] fix: ignore DS_Store --- .DS_Store | Bin 6148 -> 0 bytes .gitignore | 1 + 2 files changed, 1 insertion(+) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index a4a83d9fe24c8b2433cd4ddd10c54e493a08b8b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHK%}T>S5Z<-XrW7Fug&r5Y7HqW?itraOfn+JF~TC7gozBnelxMZ z4*2aBi<=_`4&ox+T)a8t;nPD6=bM<)PYIg7omCm@Uk$Pf)7+7YYrcDRW|5Ny7RzC8V zOUNPyh=G5`0Jnzz(1S&pv-R8Z@T?WkZlR%IUWp0_=u4LXFmNAfsi2Mv)FIC`SZc&k T(67n?=^~&Ap^g~%1qMC Date: Thu, 25 Apr 2024 15:38:08 +0300 Subject: [PATCH 17/19] fix: use exactly the same bech32 lib that mobile devs use --- go.mod | 4 ++-- go.sum | 2 ++ internal/service/requests/create_airdrop.go | 12 ++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 2f7c79a..3043569 100644 --- a/go.mod +++ b/go.mod @@ -5,12 +5,11 @@ go 1.22 require ( github.com/Masterminds/squirrel v1.4.0 github.com/alecthomas/kingpin v2.2.6+incompatible - github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce github.com/cosmos/cosmos-sdk v0.46.12 + github.com/decred/dcrd/bech32 v1.1.3 github.com/ethereum/go-ethereum v1.13.11 github.com/go-chi/chi v4.1.2+incompatible github.com/go-ozzo/ozzo-validation/v4 v4.3.0 - github.com/iden3/go-iden3-crypto v0.0.15 github.com/iden3/go-rapidsnark/types v0.0.3 github.com/iden3/go-rapidsnark/verifier v0.0.5 github.com/rarimo/rarimo-core v0.0.0-20231004143803-6b209428ecbf @@ -90,6 +89,7 @@ require ( github.com/hashicorp/hcl v1.0.0 // indirect github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect github.com/holiman/uint256 v1.2.4 // indirect + github.com/iden3/go-iden3-crypto v0.0.15 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/jmoiron/sqlx v1.2.0 // indirect diff --git a/go.sum b/go.sum index fdb302c..2a99728 100644 --- a/go.sum +++ b/go.sum @@ -692,6 +692,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/bech32 v1.1.3 h1:EeipVC1dO4zkjTjyqvrWt6JT2Ajr1EHZt+BAmWN864s= +github.com/decred/dcrd/bech32 v1.1.3/go.mod h1:jliqHZmCbVfT06Lh1mQywEKFVidRclbBJIUmwdoKhu0= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= diff --git a/internal/service/requests/create_airdrop.go b/internal/service/requests/create_airdrop.go index 3383e6f..fc224e7 100644 --- a/internal/service/requests/create_airdrop.go +++ b/internal/service/requests/create_airdrop.go @@ -7,7 +7,7 @@ import ( "net/http" "time" - "github.com/cosmos/cosmos-sdk/types" + "github.com/decred/dcrd/bech32" val "github.com/go-ozzo/ozzo-validation/v4" "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/resources" @@ -56,10 +56,14 @@ func NewCreateAirdrop(r *http.Request, cfg *config.VerifierConfig) (req resource ) } + _, addrBytes, err := bech32.Decode(attr.Address) + if err != nil { + return req, newDecodeError("data/attributes/address", err) + } + var ( - addrBytes, _ = types.AccAddressFromBech32(attr.Address) - addrDec = encodeInt(addrBytes) - citizenship = decodeInt(signals[pubSignalCitizenship]) + addrDec = encodeInt(addrBytes) + citizenship = decodeInt(signals[pubSignalCitizenship]) ) return req, val.Errors{ From 6436a02bcd5bd84b07101ce5e6b743772a0e4178 Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Sun, 28 Apr 2024 14:56:03 +0300 Subject: [PATCH 18/19] fix: broadcaster running from wrapped func --- internal/broadcaster/broadcaster.go | 3 ++- internal/cli/main.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/broadcaster/broadcaster.go b/internal/broadcaster/broadcaster.go index 6f562b3..84bbcb1 100644 --- a/internal/broadcaster/broadcaster.go +++ b/internal/broadcaster/broadcaster.go @@ -19,6 +19,7 @@ import ( xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" + cfg "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/internal/data" ethermint "github.com/rarimo/rarimo-core/ethermint/types" "gitlab.com/distributed_lab/logan/v3" @@ -33,7 +34,7 @@ type Runner struct { Config } -func Run(ctx context.Context, cfg config) { +func Run(ctx context.Context, cfg *cfg.Config) { log := cfg.Log().WithField("service", "builtin-broadcaster") log.Info("Starting service") diff --git a/internal/cli/main.go b/internal/cli/main.go index ad23b18..5bf39cf 100644 --- a/internal/cli/main.go +++ b/internal/cli/main.go @@ -54,7 +54,7 @@ func Run(args []string) bool { switch cmd { case serviceCmd.FullCommand(): run(service.Run) - run(func(context.Context, *config.Config) { broadcaster.Run(ctx, cfg) }) + run(broadcaster.Run) case migrateUpCmd.FullCommand(): err = MigrateUp(cfg) case migrateDownCmd.FullCommand(): From 4b8f0db8da6f7c66ec3f8894eba71bf54df2e5e7 Mon Sep 17 00:00:00 2001 From: Maksym Hrynenko Date: Sun, 28 Apr 2024 22:21:40 +0300 Subject: [PATCH 19/19] refactor: move broadcaster config to the config package; fix: import cycle --- internal/broadcaster/broadcaster.go | 34 ++++++------ .../config.go => config/broadcaster.go} | 52 ++++++++----------- internal/config/main.go | 5 +- 3 files changed, 41 insertions(+), 50 deletions(-) rename internal/{broadcaster/config.go => config/broadcaster.go} (77%) diff --git a/internal/broadcaster/broadcaster.go b/internal/broadcaster/broadcaster.go index 84bbcb1..d44b3f6 100644 --- a/internal/broadcaster/broadcaster.go +++ b/internal/broadcaster/broadcaster.go @@ -19,7 +19,7 @@ import ( xauthsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" - cfg "github.com/rarimo/airdrop-svc/internal/config" + "github.com/rarimo/airdrop-svc/internal/config" "github.com/rarimo/airdrop-svc/internal/data" ethermint "github.com/rarimo/rarimo-core/ethermint/types" "gitlab.com/distributed_lab/logan/v3" @@ -31,24 +31,24 @@ const txCodeSuccess = 0 type Runner struct { log *logan.Entry participants *data.ParticipantsQ - Config + config.Broadcaster } -func Run(ctx context.Context, cfg *cfg.Config) { +func Run(ctx context.Context, cfg *config.Config) { log := cfg.Log().WithField("service", "builtin-broadcaster") log.Info("Starting service") r := &Runner{ log: log, participants: data.NewParticipantsQ(cfg.DB().Clone()), - Config: cfg.Broadcaster(), + Broadcaster: cfg.Broadcaster(), } running.WithBackOff(ctx, r.log, "builtin-broadcaster", r.run, 5*time.Second, 5*time.Second, 5*time.Second) } func (r *Runner) run(ctx context.Context) error { - participants, err := r.participants.New().FilterByStatus(data.TxStatusPending).Limit(r.queryLimit).Select() + participants, err := r.participants.New().FilterByStatus(data.TxStatusPending).Limit(r.QueryLimit).Select() if err != nil { return fmt.Errorf("select participants: %w", err) } @@ -118,7 +118,7 @@ func (r *Runner) genTx(ctx context.Context, gasLimit uint64, p data.Participant) return nil, fmt.Errorf("build transfer tx: %w", err) } - builder, err := r.txConfig.WrapTxBuilder(tx) + builder, err := r.TxConfig.WrapTxBuilder(tx) if err != nil { return nil, fmt.Errorf("wrap tx with builder: %w", err) } @@ -126,7 +126,7 @@ func (r *Runner) genTx(ctx context.Context, gasLimit uint64, p data.Participant) // there are no fees on the mainnet now, and applies fees requires a lot of work builder.SetFeeAmount(types.Coins{types.NewInt64Coin("urmo", 0)}) - resp, err := r.auth.Account(ctx, &authtypes.QueryAccountRequest{Address: r.senderAddress}) + resp, err := r.Auth.Account(ctx, &authtypes.QueryAccountRequest{Address: r.SenderAddress}) if err != nil { return nil, fmt.Errorf("get sender account: %w", err) } @@ -137,9 +137,9 @@ func (r *Runner) genTx(ctx context.Context, gasLimit uint64, p data.Participant) } err = builder.SetSignatures(signing.SignatureV2{ - PubKey: r.sender.PubKey(), + PubKey: r.Sender.PubKey(), Data: &signing.SingleSignatureData{ - SignMode: r.txConfig.SignModeHandler().DefaultMode(), + SignMode: r.TxConfig.SignModeHandler().DefaultMode(), Signature: nil, }, Sequence: account.Sequence, @@ -149,13 +149,13 @@ func (r *Runner) genTx(ctx context.Context, gasLimit uint64, p data.Participant) } signerData := xauthsigning.SignerData{ - ChainID: r.chainID, + ChainID: r.ChainID, AccountNumber: account.AccountNumber, Sequence: account.Sequence, } sigV2, err := clienttx.SignWithPrivKey( - r.txConfig.SignModeHandler().DefaultMode(), signerData, - builder, r.sender, r.txConfig, account.Sequence, + r.TxConfig.SignModeHandler().DefaultMode(), signerData, + builder, r.Sender, r.TxConfig, account.Sequence, ) if err != nil { return nil, fmt.Errorf("sign with private key: %w", err) @@ -165,11 +165,11 @@ func (r *Runner) genTx(ctx context.Context, gasLimit uint64, p data.Participant) return nil, fmt.Errorf("set signatures V2: %w", err) } - return r.txConfig.TxEncoder()(builder.GetTx()) + return r.TxConfig.TxEncoder()(builder.GetTx()) } func (r *Runner) simulateTx(ctx context.Context, tx []byte) (gasUsed uint64, err error) { - sim, err := r.txClient.Simulate(ctx, &client.SimulateRequest{TxBytes: tx}) + sim, err := r.TxClient.Simulate(ctx, &client.SimulateRequest{TxBytes: tx}) if err != nil { return 0, fmt.Errorf("simulate tx: %w", err) } @@ -179,7 +179,7 @@ func (r *Runner) simulateTx(ctx context.Context, tx []byte) (gasUsed uint64, err } func (r *Runner) broadcastTx(ctx context.Context, tx []byte) (string, error) { - grpcRes, err := r.txClient.BroadcastTx(ctx, &client.BroadcastTxRequest{ + grpcRes, err := r.TxClient.BroadcastTx(ctx, &client.BroadcastTxRequest{ Mode: client.BroadcastMode_BROADCAST_MODE_BLOCK, TxBytes: tx, }) @@ -197,12 +197,12 @@ func (r *Runner) broadcastTx(ctx context.Context, tx []byte) (string, error) { func (r *Runner) buildTransferTx(p data.Participant) (types.Tx, error) { tx := &bank.MsgSend{ - FromAddress: r.senderAddress, + FromAddress: r.SenderAddress, ToAddress: p.Address, Amount: r.AirdropCoins, } - builder := r.txConfig.NewTxBuilder() + builder := r.TxConfig.NewTxBuilder() if err := builder.SetMsgs(tx); err != nil { return nil, fmt.Errorf("set messages: %w", err) } diff --git a/internal/broadcaster/config.go b/internal/config/broadcaster.go similarity index 77% rename from internal/broadcaster/config.go rename to internal/config/broadcaster.go index 42cedea..37f8cb9 100644 --- a/internal/broadcaster/config.go +++ b/internal/config/broadcaster.go @@ -1,4 +1,4 @@ -package broadcaster +package config import ( "fmt" @@ -19,33 +19,25 @@ import ( "gitlab.com/distributed_lab/figure/v3" "gitlab.com/distributed_lab/kit/comfig" "gitlab.com/distributed_lab/kit/kv" - "gitlab.com/distributed_lab/kit/pgdb" "google.golang.org/grpc" "google.golang.org/grpc/keepalive" ) const accountPrefix = "rarimo" -type config interface { - comfig.Logger - pgdb.Databaser - Broadcasterer -} - -type Config struct { - AirdropCoins types.Coins - - sender cryptotypes.PrivKey - senderAddress string - chainID string - txConfig sdkclient.TxConfig - txClient txclient.ServiceClient - auth authtypes.QueryClient - queryLimit uint64 +type Broadcaster struct { + AirdropCoins types.Coins + Sender cryptotypes.PrivKey + SenderAddress string + ChainID string + TxConfig sdkclient.TxConfig + TxClient txclient.ServiceClient + Auth authtypes.QueryClient + QueryLimit uint64 } type Broadcasterer interface { - Broadcaster() Config + Broadcaster() Broadcaster } type broadcasterer struct { @@ -53,13 +45,13 @@ type broadcasterer struct { once comfig.Once } -func New(getter kv.Getter) Broadcasterer { +func NewBroadcaster(getter kv.Getter) Broadcasterer { return &broadcasterer{ getter: getter, } } -func (b *broadcasterer) Broadcaster() Config { +func (b *broadcasterer) Broadcaster() Broadcaster { return b.once.Do(func() interface{} { var cfg struct { AirdropAmount string `fig:"airdrop_amount,required"` @@ -103,18 +95,18 @@ func (b *broadcasterer) Broadcaster() Config { queryLimit = cfg.QueryLimit } - return Config{ - sender: sender, - senderAddress: address, - chainID: cfg.ChainID, - txConfig: authtx.NewTxConfig( + return Broadcaster{ + Sender: sender, + SenderAddress: address, + ChainID: cfg.ChainID, + TxConfig: authtx.NewTxConfig( codec.NewProtoCodec(codectypes.NewInterfaceRegistry()), []signing.SignMode{signing.SignMode_SIGN_MODE_DIRECT}, ), - txClient: txclient.NewServiceClient(cosmosRPC), - auth: authtypes.NewQueryClient(cosmosRPC), + TxClient: txclient.NewServiceClient(cosmosRPC), + Auth: authtypes.NewQueryClient(cosmosRPC), AirdropCoins: amount, - queryLimit: queryLimit, + QueryLimit: queryLimit, } - }).(Config) + }).(Broadcaster) } diff --git a/internal/config/main.go b/internal/config/main.go index 964f6e9..66a7913 100644 --- a/internal/config/main.go +++ b/internal/config/main.go @@ -1,7 +1,6 @@ package config import ( - "github.com/rarimo/airdrop-svc/internal/broadcaster" "gitlab.com/distributed_lab/kit/comfig" "gitlab.com/distributed_lab/kit/kv" "gitlab.com/distributed_lab/kit/pgdb" @@ -11,7 +10,7 @@ type Config struct { comfig.Logger pgdb.Databaser comfig.Listenerer - broadcaster.Broadcasterer + Broadcasterer airdrop comfig.Once verifier comfig.Once @@ -24,6 +23,6 @@ func New(getter kv.Getter) *Config { Databaser: pgdb.NewDatabaser(getter), Listenerer: comfig.NewListenerer(getter), Logger: comfig.NewLogger(getter, comfig.LoggerOpts{}), - Broadcasterer: broadcaster.New(getter), + Broadcasterer: NewBroadcaster(getter), } }