From e19be03607542ced4e4c5c587f3d9c3faaa25b61 Mon Sep 17 00:00:00 2001 From: Ares <75481906+ice-ares@users.noreply.github.com> Date: Thu, 30 Nov 2023 16:10:19 +0200 Subject: [PATCH] added coin-distribution structure --- cmd/freezer-coin-distributer/Dockerfile | 28 ++++++++++ .../freezer_coin_distributer.go | 50 +++++++++++++++++ coin-distribution/.testdata/application.yaml | 18 ++++++ coin-distribution/DDL.sql | 23 ++++++++ coin-distribution/coin_distribution.go | 55 +++++++++++++++++++ coin-distribution/contract.go | 48 ++++++++++++++++ go.mod | 4 +- go.sum | 8 +-- 8 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 cmd/freezer-coin-distributer/Dockerfile create mode 100644 cmd/freezer-coin-distributer/freezer_coin_distributer.go create mode 100644 coin-distribution/.testdata/application.yaml create mode 100644 coin-distribution/DDL.sql create mode 100644 coin-distribution/coin_distribution.go create mode 100644 coin-distribution/contract.go diff --git a/cmd/freezer-coin-distributer/Dockerfile b/cmd/freezer-coin-distributer/Dockerfile new file mode 100644 index 0000000..2d317f0 --- /dev/null +++ b/cmd/freezer-coin-distributer/Dockerfile @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: ice License 1.0 + +FROM golang:latest AS build +ARG SERVICE_NAME +ARG TARGETOS +ARG TARGETARCH + +WORKDIR /app/ +COPY . /app/ + +ENV CGO_ENABLED=0 +ENV GOOS=$TARGETOS +ENV GOARCH=$TARGETARCH + +RUN env SERVICE_NAME=$SERVICE_NAME make dockerfile +RUN cp cmd/$SERVICE_NAME/bin bin + +FROM gcr.io/distroless/base-debian11:latest +ARG TARGETOS +ARG TARGETARCH +ARG PORT=443 +LABEL os=$TARGETOS +LABEL arch=$TARGETARCH +COPY --from=build /app/bin app +#You might need to expose more ports. Just add more separated by space +#I.E. EXPOSE 8080 8081 8082 8083 +EXPOSE $PORT +ENTRYPOINT ["/app"] diff --git a/cmd/freezer-coin-distributer/freezer_coin_distributer.go b/cmd/freezer-coin-distributer/freezer_coin_distributer.go new file mode 100644 index 0000000..20fbb8a --- /dev/null +++ b/cmd/freezer-coin-distributer/freezer_coin_distributer.go @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: ice License 1.0 + +package main + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + + coindistribution "github.com/ice-blockchain/freezer/coin-distribution" + appCfg "github.com/ice-blockchain/wintr/config" + "github.com/ice-blockchain/wintr/log" + "github.com/ice-blockchain/wintr/server" +) + +func main() { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + const pkgName = "cmd/freezer-coin-distributer" + + var cfg struct{ Version string } + appCfg.MustLoadFromKey(pkgName, &cfg) + + log.Info(fmt.Sprintf("starting version `%v`...", cfg.Version)) + + server.New(new(service), pkgName, "").ListenAndServe(ctx, cancel) +} + +type ( + // | service implements server.State and is responsible for managing the state and lifecycle of the package. + service struct{ coinDistributer coindistribution.Client } +) + +func (s *service) RegisterRoutes(_ *server.Router) {} + +func (s *service) Init(ctx context.Context, cancel context.CancelFunc) { + s.coinDistributer = coindistribution.MustStartCoinDistribution(ctx, cancel) +} + +func (s *service) Close(_ context.Context) error { + return errors.Wrap(s.coinDistributer.Close(), "could not close service") +} + +func (s *service) CheckHealth(ctx context.Context) error { + log.Debug("checking health...", "package", "coin-distribution") + + return errors.Wrap(s.coinDistributer.CheckHealth(ctx), "failed to check coin distributer's health") +} diff --git a/coin-distribution/.testdata/application.yaml b/coin-distribution/.testdata/application.yaml new file mode 100644 index 0000000..37f82df --- /dev/null +++ b/coin-distribution/.testdata/application.yaml @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: ice License 1.0 + +development: true +logger: + encoder: console + level: debug +coin-distribution: + development: true + workers: 10 + batchSize: 100 + wintr/connectors/storage/v2: + runDDL: true + primaryURL: postgresql://root:pass@localhost:5433/freezer + credentials: + user: root + password: pass + replicaURLs: + - postgresql://root:pass@localhost:5433/freezer \ No newline at end of file diff --git a/coin-distribution/DDL.sql b/coin-distribution/DDL.sql new file mode 100644 index 0000000..8289650 --- /dev/null +++ b/coin-distribution/DDL.sql @@ -0,0 +1,23 @@ +-- SPDX-License-Identifier: ice License 1.0 +CREATE TABLE IF NOT EXISTS pending_coin_distributions ( + created_at timestamp NOT NULL, + internal_id bigint NOT NULL, + iceflakes bigint NOT NULL, + user_id text NOT NULL PRIMARY KEY, + eth_address text NOT NULL); + +CREATE INDEX IF NOT EXISTS pending_coin_distributions_worker_number_ix ON pending_coin_distributions ((internal_id % 10), created_at ASC); + +--- Flow: +--infinite loop: -- with 30 sec sleep between iterations if 0 rows returned +--do in transaction: +--1. SELECT * +-- FROM pending_coin_distributions +-- WHERE internal_id % 10 = $1 +-- ORDER BY created_at ASC +-- LIMIT $2 +-- FOR UPDATE +--2. call ERC-20 smart contract method to airdrop coins +--3. delete from pending_coin_distributions WHERE user_id = ANY($1) +-- if transaction fails you retry infinitely +-- log.info every successful transaction, log every error diff --git a/coin-distribution/coin_distribution.go b/coin-distribution/coin_distribution.go new file mode 100644 index 0000000..cffdf46 --- /dev/null +++ b/coin-distribution/coin_distribution.go @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: ice License 1.0 + +package coindistribution + +import ( + "context" + "sync" + + "github.com/hashicorp/go-multierror" + "github.com/pkg/errors" + + appCfg "github.com/ice-blockchain/wintr/config" + "github.com/ice-blockchain/wintr/connectors/storage/v2" +) + +func init() { + appCfg.MustLoadFromKey(applicationYamlKey, &cfg) +} + +func MustStartCoinDistribution(ctx context.Context, cancel context.CancelFunc) Client { + cd := &coinDistributer{ + db: storage.MustConnect(context.Background(), ddl, applicationYamlKey), + wg: new(sync.WaitGroup), + } + cd.wg.Add(int(cfg.Workers)) + cd.cancel = cancel + + for workerNumber := int64(0); workerNumber < cfg.Workers; workerNumber++ { + go func(wn int64) { + defer cd.wg.Done() + cd.distributeCoins(ctx, wn) + }(workerNumber) + } + + return cd +} + +func (cd *coinDistributer) Close() error { + cd.cancel() + cd.wg.Wait() + + return multierror.Append( + errors.Wrap(cd.db.Close(), "failed to close db"), + ).ErrorOrNil() +} + +func (cd *coinDistributer) CheckHealth(ctx context.Context) error { + return errors.Wrap(cd.db.Ping(ctx), "[health-check] failed to ping DB") +} + +func (cd *coinDistributer) distributeCoins(ctx context.Context, workerNumber int64) { + for ctx.Err() == nil { + println(workerNumber) + } +} diff --git a/coin-distribution/contract.go b/coin-distribution/contract.go new file mode 100644 index 0000000..0d19ff5 --- /dev/null +++ b/coin-distribution/contract.go @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: ice License 1.0 + +package coindistribution + +import ( + "context" + _ "embed" + "io" + "sync" + + "github.com/ice-blockchain/wintr/connectors/storage/v2" +) + +// Public API. + +type ( + Client interface { + io.Closer + CheckHealth(context.Context) error + } +) + +// Private API. + +const ( + applicationYamlKey = "coin-distribution" +) + +// . +var ( + //nolint:gochecknoglobals // Singleton & global config mounted only during bootstrap. + cfg config + //go:embed DDL.sql + ddl string +) + +type ( + coinDistributer struct { + db *storage.DB + cancel context.CancelFunc + wg *sync.WaitGroup + } + config struct { + Workers int64 `yaml:"workers"` + BatchSize int64 `yaml:"batchSize"` + Development bool `yaml:"development"` + } +) diff --git a/go.mod b/go.mod index f0931ee..6e2c414 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/cenkalti/backoff/v4 v4.2.1 github.com/goccy/go-json v0.10.2 github.com/hashicorp/go-multierror v1.1.1 - github.com/ice-blockchain/eskimo v1.190.0 + github.com/ice-blockchain/eskimo v1.193.0 github.com/ice-blockchain/go-tarantool-client v0.0.0-20230327200757-4fc71fa3f7bb github.com/ice-blockchain/wintr v1.125.0 github.com/imroc/req/v3 v3.42.2 @@ -137,7 +137,7 @@ require ( github.com/twmb/franz-go v1.15.2 // indirect github.com/twmb/franz-go/pkg/kadm v1.10.0 // indirect github.com/twmb/franz-go/pkg/kmsg v1.7.0 // indirect - github.com/ugorji/go/codec v1.2.11 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect go.opencensus.io v0.24.0 // indirect diff --git a/go.sum b/go.sum index 8bbf981..4e3cd0e 100644 --- a/go.sum +++ b/go.sum @@ -212,8 +212,8 @@ github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mO github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/ice-blockchain/eskimo v1.190.0 h1:1pU/fZvoJYywuQ8A9zmRzjiXkjWWaZ1b3EWqf0XXac4= -github.com/ice-blockchain/eskimo v1.190.0/go.mod h1:zvDU+xukTh7ZmCLi4wFsnrl8LlcTg+9mYrEoyL2f7hw= +github.com/ice-blockchain/eskimo v1.193.0 h1:0U1E7NOpoKvY2y8OvNDKggHlciuEwYsPJ4xrFu7FXHI= +github.com/ice-blockchain/eskimo v1.193.0/go.mod h1:3hnl49K1NMs8WY+W2MXTw5rJjrvj8WxOaVQnqXMgSiA= github.com/ice-blockchain/go-tarantool-client v0.0.0-20230327200757-4fc71fa3f7bb h1:8TnFP3mc7O+tc44kv2e0/TpZKnEVUaKH+UstwfBwRkk= github.com/ice-blockchain/go-tarantool-client v0.0.0-20230327200757-4fc71fa3f7bb/go.mod h1:ZsQU7i3mxhgBBu43Oev7WPFbIjP4TniN/b1UPNGbrq8= github.com/ice-blockchain/wintr v1.125.0 h1:pk/SVyztstUF19+JDCufJRMXJeNpchVA4O26xp47l3Y= @@ -374,8 +374,8 @@ github.com/twmb/franz-go/pkg/kadm v1.10.0 h1:3oYKNP+e3HGo4GYadrDeRxOaAIsOXmX6LBV github.com/twmb/franz-go/pkg/kadm v1.10.0/go.mod h1:hUMoV4SRho+2ij/S9cL39JaLsr+XINjn0ZkCdBY2DXc= github.com/twmb/franz-go/pkg/kmsg v1.7.0 h1:a457IbvezYfA5UkiBvyV3zj0Is3y1i8EJgqjJYoij2E= github.com/twmb/franz-go/pkg/kmsg v1.7.0/go.mod h1:se9Mjdt0Nwzc9lnjJ0HyDtLyBnaBDAd7pCje47OhSyw= -github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= -github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8= github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok= github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=