diff --git a/.github/workflows/integration-test.yml b/.github/workflows/integration-test.yml index c19d2f644..306c98e08 100644 --- a/.github/workflows/integration-test.yml +++ b/.github/workflows/integration-test.yml @@ -38,8 +38,9 @@ jobs: make docker-image sudo mkdir -p ./deployment/.zkbnb sudo cp /server/test.keyfile/* ./deployment/.zkbnb + source <(sudo cat /server/.env) blockNr=$(sudo bash ./deployment/tool/tool.sh blockHeight) - sudo bash ./deployment/tool/tool.sh all + sudo BSC_TESTNET_PRIVATE_KEY=${BSC_TESTNET_PRIVATE_KEY} bash ./deployment/tool/tool.sh all sudo bash ./deployment/docker-compose/docker-compose.sh down sudo bash ./deployment/docker-compose/docker-compose.sh up $blockNr echo "Contract addresses" @@ -68,7 +69,7 @@ jobs: go test -v -failfast -parallel 2 -run TestL.*Suite -timeout 30m - name: notification via slack - if: always() + if: cancelled() == false run: | export SLACK_WEBHOOK_URL=`sudo cat /home/ec2-user/actions-runner/slack-config.json | jq -r '.slack'` export JOB_STATUS=${{ job.status }} diff --git a/Makefile b/Makefile index 572c7676a..66c36c94f 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ IMAGE_NAME=ghcr.io/bnb-chain/zkbnb API_SERVER = ./service/apiserver api-server: - cd $(API_SERVER) && ${GOBIN}/goctl api go -api server.api -dir .; + cd $(API_SERVER) && goctl api go -api server.api -dir .; @echo "Done generate server api"; deploy: diff --git a/cmd/flags/flags.go b/cmd/flags/flags.go index 2e8e83e84..55a8b7728 100644 --- a/cmd/flags/flags.go +++ b/cmd/flags/flags.go @@ -41,4 +41,34 @@ var ( Value: 1000, Usage: "batch size for reading history record from the database", } + PProfEnabledFlag = &cli.BoolFlag{ + Name: "pprof", + Value: false, + Usage: "Enable the pprof HTTP server", + } + PProfPortFlag = &cli.IntFlag{ + Name: "pprof.port", + Usage: "pprof HTTP server listening port", + Value: 6060, + } + PProfAddrFlag = &cli.StringFlag{ + Name: "pprof.addr", + Usage: "pprof HTTP server listening interface", + Value: "127.0.0.1", + } + MetricsEnabledFlag = &cli.BoolFlag{ + Name: "metrics", + Value: false, + Usage: "Enable metrics collection and reporting", + } + MetricsHTTPFlag = &cli.StringFlag{ + Name: "metrics.addr", + Usage: "Enable stand-alone metrics HTTP server listening interface", + Value: "127.0.0.1", + } + MetricsPortFlag = &cli.IntFlag{ + Name: "metrics.port", + Usage: "Metrics HTTP server listening port", + Value: 6060, + } ) diff --git a/cmd/zkbnb/main.go b/cmd/zkbnb/main.go index c347e2532..ed0ed8dcd 100644 --- a/cmd/zkbnb/main.go +++ b/cmd/zkbnb/main.go @@ -6,16 +6,23 @@ import ( "runtime" "github.com/urfave/cli/v2" + "github.com/zeromicro/go-zero/core/logx" "github.com/bnb-chain/zkbnb/cmd/flags" + "github.com/bnb-chain/zkbnb/common/metrics" + pprofServer "github.com/bnb-chain/zkbnb/common/metrics/pprof" + prometheusServer "github.com/bnb-chain/zkbnb/common/metrics/prometheus" "github.com/bnb-chain/zkbnb/service/apiserver" "github.com/bnb-chain/zkbnb/service/committer" + "github.com/bnb-chain/zkbnb/service/fullnode" "github.com/bnb-chain/zkbnb/service/monitor" "github.com/bnb-chain/zkbnb/service/prover" "github.com/bnb-chain/zkbnb/service/sender" "github.com/bnb-chain/zkbnb/service/witness" "github.com/bnb-chain/zkbnb/tools/dbinitializer" "github.com/bnb-chain/zkbnb/tools/recovery" + + "net/http" ) // Build Info (set via linker flags) @@ -47,12 +54,18 @@ func main() { Usage: "Run prover service", Flags: []cli.Flag{ flags.ConfigFlag, + flags.MetricsEnabledFlag, + flags.MetricsHTTPFlag, + flags.MetricsPortFlag, + flags.PProfEnabledFlag, + flags.PProfAddrFlag, + flags.PProfPortFlag, }, Action: func(cCtx *cli.Context) error { if !cCtx.IsSet(flags.ConfigFlag.Name) { return cli.ShowSubcommandHelp(cCtx) } - + startMetricsServer(cCtx) return prover.Run(cCtx.String(flags.ConfigFlag.Name)) }, }, @@ -61,12 +74,18 @@ func main() { Usage: "Run witness service", Flags: []cli.Flag{ flags.ConfigFlag, + flags.MetricsEnabledFlag, + flags.MetricsHTTPFlag, + flags.MetricsPortFlag, + flags.PProfEnabledFlag, + flags.PProfAddrFlag, + flags.PProfPortFlag, }, Action: func(cCtx *cli.Context) error { if !cCtx.IsSet(flags.ConfigFlag.Name) { return cli.ShowSubcommandHelp(cCtx) } - + startMetricsServer(cCtx) return witness.Run(cCtx.String(flags.ConfigFlag.Name)) }, }, @@ -75,12 +94,18 @@ func main() { Usage: "Run monitor service", Flags: []cli.Flag{ flags.ConfigFlag, + flags.MetricsEnabledFlag, + flags.MetricsHTTPFlag, + flags.MetricsPortFlag, + flags.PProfEnabledFlag, + flags.PProfAddrFlag, + flags.PProfPortFlag, }, Action: func(cCtx *cli.Context) error { if !cCtx.IsSet(flags.ConfigFlag.Name) { return cli.ShowSubcommandHelp(cCtx) } - + startMetricsServer(cCtx) return monitor.Run(cCtx.String(flags.ConfigFlag.Name)) }, }, @@ -88,27 +113,53 @@ func main() { Name: "committer", Flags: []cli.Flag{ flags.ConfigFlag, + flags.MetricsEnabledFlag, + flags.MetricsHTTPFlag, + flags.MetricsPortFlag, + flags.PProfEnabledFlag, + flags.PProfAddrFlag, + flags.PProfPortFlag, }, Usage: "Run committer service", Action: func(cCtx *cli.Context) error { if !cCtx.IsSet(flags.ConfigFlag.Name) { return cli.ShowSubcommandHelp(cCtx) } - + startMetricsServer(cCtx) return committer.Run(cCtx.String(flags.ConfigFlag.Name)) }, }, + { + Name: "fullnode", + Flags: []cli.Flag{ + flags.ConfigFlag, + }, + Usage: "Run fullnode service", + Action: func(cCtx *cli.Context) error { + if !cCtx.IsSet(flags.ConfigFlag.Name) { + return cli.ShowSubcommandHelp(cCtx) + } + + return fullnode.Run(cCtx.String(flags.ConfigFlag.Name)) + }, + }, { Name: "sender", Usage: "Run sender service", Flags: []cli.Flag{ flags.ConfigFlag, + flags.MetricsEnabledFlag, + flags.MetricsHTTPFlag, + flags.MetricsPortFlag, + flags.PProfEnabledFlag, + flags.PProfAddrFlag, + flags.PProfPortFlag, }, Action: func(cCtx *cli.Context) error { if !cCtx.IsSet(flags.ConfigFlag.Name) { return cli.ShowSubcommandHelp(cCtx) } - + startMetricsServer(cCtx) return sender.Run(cCtx.String(flags.ConfigFlag.Name)) }, }, @@ -117,12 +168,18 @@ func main() { Usage: "Run apiserver service", Flags: []cli.Flag{ flags.ConfigFlag, + flags.MetricsEnabledFlag, + flags.MetricsHTTPFlag, + flags.MetricsPortFlag, + flags.PProfEnabledFlag, + flags.PProfAddrFlag, + flags.PProfPortFlag, }, Action: func(cCtx *cli.Context) error { if !cCtx.IsSet(flags.ConfigFlag.Name) { return cli.ShowSubcommandHelp(cCtx) } - + startMetricsServer(cCtx) return apiserver.Run(cCtx.String(flags.ConfigFlag.Name)) }, }, @@ -188,7 +245,51 @@ func main() { }, }, } + if err := app.Run(os.Args); err != nil { fmt.Println(err) } } + +func startMetricsServer(ctx *cli.Context) { + if !ctx.Bool(flags.PProfEnabledFlag.Name) && + !ctx.Bool(flags.MetricsEnabledFlag.Name) { + return + } + + pprofAddress := fmt.Sprintf("%s:%d", + ctx.String(flags.PProfAddrFlag.Name), + ctx.Int(flags.PProfPortFlag.Name)) + metricsAddress := fmt.Sprintf("%s:%d", + ctx.String(flags.MetricsHTTPFlag.Name), + ctx.Int(flags.MetricsPortFlag.Name)) + + pprofMux := metrics.NewRunOnceHttpMux(http.NewServeMux()) + metricsMux := metrics.NewRunOnceHttpMux(http.NewServeMux()) + if ctx.Bool(flags.PProfEnabledFlag.Name) && ctx.Bool(flags.MetricsEnabledFlag.Name) && + metricsAddress == pprofAddress { + // point to the same endpoint + pprofMux = metricsMux + } + + pprofServer := pprofServer.NewPProfServer(pprofMux, pprofAddress) + prometheusServer := prometheusServer.NewPrometheusServer(metricsMux, metricsAddress) + + if ctx.Bool(flags.MetricsEnabledFlag.Name) { + go func() { + logx.Info("Starting pprof server", "addr", fmt.Sprintf("http://%s/debug/pprof", pprofAddress)) + if err := pprofServer.Start(); err != nil { + logx.Error("Failure in running pprof server", "err", err) + } + }() + } + + if ctx.Bool(flags.MetricsEnabledFlag.Name) { + go func() { + logx.Info("Starting metrics server", "addr", fmt.Sprintf("http://%s/debug/metrics", metricsAddress)) + if err := prometheusServer.Start(); err != nil { + logx.Error("Failure in running metrics server", "err", err) + } + }() + } +} diff --git a/common/gopool/gopool.go b/common/gopool/gopool.go new file mode 100644 index 000000000..8fbbc7438 --- /dev/null +++ b/common/gopool/gopool.go @@ -0,0 +1,42 @@ +package gopool + +import ( + "time" + + "github.com/panjf2000/ants/v2" +) + +var ( + // Init a instance pool when importing ants. + defaultPool, _ = ants.NewPool(ants.DefaultAntsPoolSize, ants.WithExpiryDuration(10*time.Second)) +) + +// Submit submits a task to pool. +func Submit(task func()) error { + return defaultPool.Submit(task) +} + +// Running returns the number of the currently running goroutines. +func Running() int { + return defaultPool.Running() +} + +// Cap returns the capacity of this default pool. +func Cap() int { + return defaultPool.Cap() +} + +// Free returns the available goroutines to work. +func Free() int { + return defaultPool.Free() +} + +// Release Closes the default pool. +func Release() { + defaultPool.Release() +} + +// Reboot reboots the default pool. +func Reboot() { + defaultPool.Reboot() +} diff --git a/common/metrics/metrics.go b/common/metrics/metrics.go new file mode 100644 index 000000000..b4e913da2 --- /dev/null +++ b/common/metrics/metrics.go @@ -0,0 +1,31 @@ +package metrics + +import ( + "net/http" + "sync" +) + +func NewRunOnceHttpMux(mux *http.ServeMux) *RunOnceHttpMux { + return &RunOnceHttpMux{ + ServeMux: mux, + once: &sync.Once{}, + } +} + +type RunOnceHttpMux struct { + *http.ServeMux + once *sync.Once +} + +func (mux *RunOnceHttpMux) ListenAndServe(addr string) error { + var err error + mux.once.Do(func() { + err = http.ListenAndServe(addr, mux.ServeMux) + }) + + return err +} + +type MetricsServer interface { + Start() error +} diff --git a/common/metrics/pprof/pprof.go b/common/metrics/pprof/pprof.go new file mode 100644 index 000000000..b2ab3dbc6 --- /dev/null +++ b/common/metrics/pprof/pprof.go @@ -0,0 +1,31 @@ +package pprof + +import ( + "net/http/pprof" + + "github.com/bnb-chain/zkbnb/common/metrics" +) + +var _ metrics.MetricsServer = (*PProfServer)(nil) + +func NewPProfServer(srv *metrics.RunOnceHttpMux, addr string) metrics.MetricsServer { + srv.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine")) + srv.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate")) + srv.Handle("/debug/pprof/heap", pprof.Handler("heap")) + srv.Handle("/debug/pprof/allocs", pprof.Handler("allocs")) + srv.Handle("/debug/pprof/block", pprof.Handler("block")) + srv.Handle("/debug/pprof/mutex", pprof.Handler("mutex")) + return &PProfServer{ + srv: srv, + addr: addr, + } +} + +type PProfServer struct { + srv *metrics.RunOnceHttpMux + addr string +} + +func (server *PProfServer) Start() error { + return server.srv.ListenAndServe(server.addr) +} diff --git a/common/metrics/prometheus/prometheus.go b/common/metrics/prometheus/prometheus.go new file mode 100644 index 000000000..01ef2717b --- /dev/null +++ b/common/metrics/prometheus/prometheus.go @@ -0,0 +1,25 @@ +package prometheus + +import ( + "github.com/bnb-chain/zkbnb/common/metrics" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +var _ metrics.MetricsServer = (*PrometheusServer)(nil) + +func NewPrometheusServer(srv *metrics.RunOnceHttpMux, addr string) metrics.MetricsServer { + srv.Handle("/debug/metrics/prometheus", promhttp.Handler()) + return &PrometheusServer{ + srv: srv, + addr: addr, + } +} + +type PrometheusServer struct { + srv *metrics.RunOnceHttpMux + addr string +} + +func (server PrometheusServer) Start() error { + return server.srv.ListenAndServe(server.addr) +} diff --git a/common/prove/witness_helper.go b/common/prove/witness_helper.go index efe6b4703..397e56126 100644 --- a/common/prove/witness_helper.go +++ b/common/prove/witness_helper.go @@ -22,7 +22,6 @@ import ( "math/big" "github.com/ethereum/go-ethereum/common" - "github.com/zeromicro/go-zero/core/logx" "github.com/bnb-chain/zkbnb-crypto/circuit" cryptoTypes "github.com/bnb-chain/zkbnb-crypto/circuit/types" @@ -283,6 +282,15 @@ func (w *WitnessHelper) constructAccountWitness( } assetCount++ + + // cache gas account's assets + if accountKey == types.GasAccount { + w.gasAccountInfo.AssetInfo[nAsset.AssetId] = &types.AccountAsset{ + AssetId: nAsset.AssetId, + Balance: nAsset.Balance, + OfferCanceledOrFinalized: nAsset.OfferCanceledOrFinalized, + } + } } if err != nil { return accountRootBefore, accountsInfoBefore, merkleProofsAccountAssetsBefore, merkleProofsAccountBefore, err @@ -330,18 +338,11 @@ func (w *WitnessHelper) constructAccountWitness( return accountRootBefore, accountsInfoBefore, merkleProofsAccountAssetsBefore, merkleProofsAccountBefore, err } - // cache gas account + // cache gas account's nonce if accountKey == types.GasAccount { w.gasAccountInfo.Nonce = nonce w.gasAccountInfo.CollectionNonce = collectionNonce w.gasAccountInfo.AssetRoot = common.Bytes2Hex(w.assetTrees.Get(accountKey).Root()) - for i := 0; i < NbAccountAssetsPerAccount; i++ { - w.gasAccountInfo.AssetInfo[cryptoAccount.AssetsInfo[i].AssetId] = &types.AccountAsset{ - AssetId: cryptoAccount.AssetsInfo[i].AssetId, - Balance: cryptoAccount.AssetsInfo[i].Balance, - OfferCanceledOrFinalized: cryptoAccount.AssetsInfo[i].OfferCanceledOrFinalized, - } - } } // set account info before @@ -709,7 +710,6 @@ func (w *WitnessHelper) ConstructGasWitness(block *block.Block) (cryptoGas *GasW CollectionNonce: w.gasAccountInfo.CollectionNonce, AssetRoot: w.assetTrees.Get(gasAccountIndex).Root(), } - logx.Infof("old gas account asset root: %s", common.Bytes2Hex(w.assetTrees.Get(gasAccountIndex).Root())) accountMerkleProofs, err := w.accountTree.GetProof(uint64(gasAccountIndex)) if err != nil { @@ -753,9 +753,7 @@ func (w *WitnessHelper) ConstructGasWitness(block *block.Block) (cryptoGas *GasW if err != nil { return nil, err } - } - logx.Infof("new gas account asset root: %s", common.Bytes2Hex(w.assetTrees.Get(gasAccountIndex).Root())) nAccountHash, err := tree.ComputeAccountLeafHash( w.gasAccountInfo.AccountNameHash, diff --git a/common/prove/witness_test.go b/common/prove/witness_test.go index 84b9d1e89..102a35c63 100644 --- a/common/prove/witness_test.go +++ b/common/prove/witness_test.go @@ -29,7 +29,6 @@ import ( "gorm.io/gorm" "github.com/bnb-chain/zkbnb-crypto/circuit" - "github.com/bnb-chain/zkbnb-smt/database/memory" "github.com/bnb-chain/zkbnb/dao/account" "github.com/bnb-chain/zkbnb/dao/block" "github.com/bnb-chain/zkbnb/dao/blockwitness" @@ -79,9 +78,9 @@ func TestConstructWitness(t *testing.T) { } func getWitnessHelper(blockHeight int64) (*WitnessHelper, error) { - ctx := &tree.Context{ - Driver: tree.MemoryDB, - TreeDB: memory.NewMemoryDB(), + ctx, err := tree.NewContext("witness", tree.MemoryDB, false, 128, nil, nil) + if err != nil { + return nil, err } accountTree, accountAssetTrees, err := tree.InitAccountTree(accountModel, accountHistoryModel, blockHeight, ctx, assetTreeCacheSize) if err != nil { diff --git a/core/block_processor.go b/core/block_processor.go index 388311b86..e65f89da1 100644 --- a/core/block_processor.go +++ b/core/block_processor.go @@ -3,8 +3,10 @@ package core import ( "fmt" + "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" + "github.com/bnb-chain/zkbnb-crypto/wasm/txtypes" "github.com/bnb-chain/zkbnb/core/executor" "github.com/bnb-chain/zkbnb/dao/tx" "github.com/bnb-chain/zkbnb/types" @@ -83,12 +85,76 @@ func (p *APIProcessor) Process(tx *tx.Tx) error { err = executor.Prepare() if err != nil { logx.Error("fail to prepare:", err) - return types.AppErrInternal + return mappingPrepareErrors(err) } err = executor.VerifyInputs(false) if err != nil { - return types.AppErrInvalidTxField.RefineError(err.Error()) + return mappingVerifyInputsErrors(err) } return nil } + +func mappingPrepareErrors(err error) error { + switch e := errors.Cause(err).(type) { + case types.Error: + return e + default: + return types.AppErrInternal + } +} + +func mappingVerifyInputsErrors(err error) error { + e := errors.Cause(err) + switch e { + case txtypes.ErrAccountIndexTooLow, txtypes.ErrAccountIndexTooHigh, + txtypes.ErrCreatorAccountIndexTooLow, txtypes.ErrCreatorAccountIndexTooHigh, + txtypes.ErrFromAccountIndexTooLow, txtypes.ErrFromAccountIndexTooHigh, + txtypes.ErrToAccountIndexTooLow, txtypes.ErrToAccountIndexTooHigh: + return types.AppErrInvalidAccountIndex + case txtypes.ErrGasAccountIndexTooLow, txtypes.ErrGasAccountIndexTooHigh: + return types.AppErrInvalidGasFeeAccount + case txtypes.ErrGasFeeAssetIdTooLow, txtypes.ErrGasFeeAssetIdTooHigh: + return types.AppErrInvalidGasFeeAsset + case txtypes.ErrGasFeeAssetAmountTooLow, txtypes.ErrGasFeeAssetAmountTooHigh: + return types.AppErrInvalidGasFeeAmount + case txtypes.ErrNonceTooLow: + return types.AppErrInvalidNonce + case txtypes.ErrOfferTypeInvalid: + return types.AppErrInvalidOfferType + case txtypes.ErrOfferIdTooLow: + return types.AppErrInvalidOfferId + case txtypes.ErrNftIndexTooLow: + return types.AppErrInvalidNftIndex + case txtypes.ErrAssetIdTooLow, txtypes.ErrAssetIdTooHigh: + return types.AppErrInvalidAssetId + case txtypes.ErrAssetAmountTooLow, txtypes.ErrAssetAmountTooHigh: + return types.AppErrInvalidAssetAmount + case txtypes.ErrListedAtTooLow: + return types.AppErrInvalidListTime + case txtypes.ErrTreasuryRateTooLow, txtypes.ErrTreasuryRateTooHigh, + txtypes.ErrCreatorTreasuryRateTooLow, txtypes.ErrCreatorTreasuryRateTooHigh: + return types.AppErrInvalidTreasuryRate + case txtypes.ErrCollectionNameTooShort, txtypes.ErrCollectionNameTooLong: + return types.AppErrInvalidCollectionName + case txtypes.ErrIntroductionTooLong: + return types.AppErrInvalidIntroduction + case txtypes.ErrNftContentHashInvalid: + return types.AppErrInvalidNftContenthash + case txtypes.ErrNftCollectionIdTooLow, txtypes.ErrNftCollectionIdTooHigh: + return types.AppErrInvalidCollectionId + case txtypes.ErrCallDataHashInvalid: + return types.AppErrInvalidCallDataHash + case txtypes.ErrToAccountNameHashInvalid: + return types.AppErrInvalidToAccountNameHash + case txtypes.ErrToAddressInvalid: + return types.AppErrInvalidToAddress + case txtypes.ErrBuyOfferInvalid: + return types.AppErrInvalidBuyOffer + case txtypes.ErrSellOfferInvalid: + return types.AppErrInvalidSellOffer + + default: + return types.AppErrInvalidTxField.RefineError(err.Error()) + } +} diff --git a/core/blockchain.go b/core/blockchain.go index 88676ac27..b77568b1d 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -8,12 +8,13 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/panjf2000/ants/v2" + "github.com/prometheus/client_golang/prometheus" "github.com/zeromicro/go-zero/core/logx" "github.com/zeromicro/go-zero/core/stores/cache" "gorm.io/driver/postgres" "gorm.io/gorm" + bsmt "github.com/bnb-chain/zkbnb-smt" "github.com/bnb-chain/zkbnb/common/chain" "github.com/bnb-chain/zkbnb/core/statedb" sdb "github.com/bnb-chain/zkbnb/core/statedb" @@ -26,10 +27,22 @@ import ( "github.com/bnb-chain/zkbnb/dao/sysconfig" "github.com/bnb-chain/zkbnb/dao/tx" "github.com/bnb-chain/zkbnb/tree" + "github.com/bnb-chain/zkbnb/types" ) -const ( - defaultTaskPoolSize = 1000 +// metrics +var ( + updateTreeMetics = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "zkbnb", + Name: "update_smt", + Help: "update smt tree operation time", + }) + + commitTreeMetics = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "zkbnb", + Name: "commit_smt", + Help: "commit smt tree operation time", + }) ) type ChainConfig struct { @@ -44,7 +57,9 @@ type ChainConfig struct { //nolint:staticcheck LevelDBOption tree.LevelDBOption `json:",optional"` //nolint:staticcheck - RedisDBOption tree.RedisDBOption `json:",optional"` + RedisDBOption tree.RedisDBOption `json:",optional"` + //nolint:staticcheck + RoutinePoolSize int `json:",optional"` AssetTreeCacheSize int } } @@ -58,7 +73,6 @@ type BlockChain struct { currentBlock *block.Block processor Processor - taskPool *ants.Pool } func NewBlockChain(config *ChainConfig, moduleName string) (*BlockChain, error) { @@ -86,23 +100,25 @@ func NewBlockChain(config *ChainConfig, moduleName string) (*BlockChain, error) curHeight-- } redisCache := dbcache.NewRedisCache(config.CacheRedis[0].Host, config.CacheRedis[0].Pass, 15*time.Minute) - treeCtx := &tree.Context{ - Name: moduleName, - Driver: config.TreeDB.Driver, - LevelDBOption: &config.TreeDB.LevelDBOption, - RedisDBOption: &config.TreeDB.RedisDBOption, + treeCtx, err := tree.NewContext(moduleName, config.TreeDB.Driver, false, config.TreeDB.RoutinePoolSize, &config.TreeDB.LevelDBOption, &config.TreeDB.RedisDBOption) + if err != nil { + return nil, err } + treeCtx.SetOptions(bsmt.BatchSizeLimit(3 * 1024 * 1024)) bc.Statedb, err = sdb.NewStateDB(treeCtx, bc.ChainDB, redisCache, &config.CacheConfig, config.TreeDB.AssetTreeCacheSize, bc.currentBlock.StateRoot, curHeight) if err != nil { return nil, err } bc.processor = NewCommitProcessor(bc) - taskPool, err := ants.NewPool(defaultTaskPoolSize) - if err != nil { - return nil, err + + // register metrics + if err := prometheus.Register(updateTreeMetics); err != nil { + return nil, fmt.Errorf("prometheus.Register updateTreeMetics error: %v", err) + } + if err := prometheus.Register(commitTreeMetics); err != nil { + return nil, fmt.Errorf("prometheus.Register commitTreeMetics error: %v", err) } - bc.taskPool = taskPool return bc, nil } @@ -136,7 +152,7 @@ func (bc *BlockChain) ApplyTransaction(tx *tx.Tx) error { return bc.processor.Process(tx) } -func (bc *BlockChain) ProposeNewBlock() (*block.Block, error) { +func (bc *BlockChain) InitNewBlock() (*block.Block, error) { newBlock := &block.Block{ Model: gorm.Model{ // The block timestamp will be set when the first transaction executed. @@ -149,7 +165,8 @@ func (bc *BlockChain) ProposeNewBlock() (*block.Block, error) { bc.currentBlock = newBlock bc.Statedb.PurgeCache(bc.currentBlock.StateRoot) - return newBlock, nil + err := bc.Statedb.MarkGasAccountAsPending() + return newBlock, err } func (bc *BlockChain) CurrentBlock() *block.Block { @@ -164,10 +181,12 @@ func (bc *BlockChain) CommitNewBlock(blockSize int, createdAt int64) (*block.Blo currentHeight := bc.currentBlock.BlockHeight - err = tree.CommitTrees(bc.taskPool, uint64(currentHeight), bc.Statedb.AccountTree, bc.Statedb.AccountAssetTrees, bc.Statedb.NftTree) + start := time.Now() + err = tree.CommitTrees(uint64(currentHeight), bc.Statedb.AccountTree, bc.Statedb.AccountAssetTrees, bc.Statedb.NftTree) if err != nil { return nil, err } + commitTreeMetics.Set(float64(time.Since(start).Milliseconds())) pendingAccount, pendingAccountHistory, err := bc.Statedb.GetPendingAccount(currentHeight) if err != nil { @@ -207,11 +226,13 @@ func (bc *BlockChain) commitNewBlock(blockSize int, createdAt int64) (*block.Blo } } + start := time.Now() // Intermediate state root. err := s.IntermediateRoot(false) if err != nil { return nil, nil, err } + updateTreeMetics.Set(float64(time.Since(start).Milliseconds())) // Align pub data. s.AlignPubData(blockSize) @@ -258,11 +279,11 @@ func (bc *BlockChain) commitNewBlock(blockSize int, createdAt int64) (*block.Blo func (bc *BlockChain) VerifyExpiredAt(expiredAt int64) error { if !bc.dryRun { if expiredAt < bc.currentBlock.CreatedAt.UnixMilli() { - return errors.New("invalid expired time") + return types.AppErrInvalidExpireTime } } else { if expiredAt < time.Now().UnixMilli() { - return errors.New("invalid expired time") + return types.AppErrInvalidExpireTime } } return nil @@ -275,7 +296,7 @@ func (bc *BlockChain) VerifyNonce(accountIndex int64, nonce int64) error { return err } if nonce != expectNonce { - return errors.New("invalid nonce") + return types.AppErrInvalidNonce } } else { pendingNonce, err := bc.Statedb.GetPendingNonce(accountIndex) @@ -283,7 +304,7 @@ func (bc *BlockChain) VerifyNonce(accountIndex int64, nonce int64) error { return err } if pendingNonce != nonce { - return errors.New("invalid nonce") + return types.AppErrInvalidNonce } } return nil @@ -295,7 +316,7 @@ func (bc *BlockChain) VerifyGas(gasAccountIndex, gasFeeAssetId int64, txType int return err } if gasAccountIndex != cfgGasAccountIndex { - return errors.New("invalid gas fee account") + return types.AppErrInvalidGasFeeAccount } cfgGasFee, err := bc.Statedb.GetGasConfig() @@ -306,7 +327,7 @@ func (bc *BlockChain) VerifyGas(gasAccountIndex, gasFeeAssetId int64, txType int gasAsset, ok := cfgGasFee[uint32(gasFeeAssetId)] if !ok { logx.Errorf("cannot find gas config for asset id: %d", gasFeeAssetId) - return errors.New("invalid gas fee asset") + return types.AppErrInvalidGasFeeAsset } if !skipGasAmtChk { @@ -315,7 +336,7 @@ func (bc *BlockChain) VerifyGas(gasAccountIndex, gasFeeAssetId int64, txType int return errors.New("invalid tx type") } if gasFeeAmount.Cmp(big.NewInt(gasFee)) < 0 { - return errors.New("invalid gas fee amount") + return types.AppErrInvalidGasFeeAmount } } return nil diff --git a/core/executor/atomic_match_executor.go b/core/executor/atomic_match_executor.go index 95a1d5e28..acb61e971 100644 --- a/core/executor/atomic_match_executor.go +++ b/core/executor/atomic_match_executor.go @@ -51,7 +51,7 @@ func (e *AtomicMatchExecutor) Prepare() error { matchNft, err := e.bc.StateDB().PrepareNft(txInfo.SellOffer.NftIndex) if err != nil { logx.Errorf("prepare nft failed") - return errors.New("internal error") + return err } // Set the right treasury and creator treasury amount. @@ -79,16 +79,16 @@ func (e *AtomicMatchExecutor) VerifyInputs(skipGasAmtChk bool) error { if txInfo.BuyOffer.Type != types.BuyOfferType || txInfo.SellOffer.Type != types.SellOfferType { - return errors.New("invalid offer type") + return types.AppErrInvalidOfferType } if txInfo.BuyOffer.AccountIndex == txInfo.SellOffer.AccountIndex { - return errors.New("same buyer and seller") + return types.AppErrSameBuyerAndSeller } if txInfo.SellOffer.NftIndex != txInfo.BuyOffer.NftIndex || txInfo.SellOffer.AssetId != txInfo.BuyOffer.AssetId || txInfo.SellOffer.AssetAmount.String() != txInfo.BuyOffer.AssetAmount.String() || txInfo.SellOffer.TreasuryRate != txInfo.BuyOffer.TreasuryRate { - return errors.New("buy offer mismatches sell offer") + return types.AppErrBuyOfferMismatchSellOffer } // only gas assets are allowed for atomic match @@ -99,15 +99,15 @@ func (e *AtomicMatchExecutor) VerifyInputs(skipGasAmtChk bool) error { } } if !found { - return errors.New("invalid asset of offer") + return types.AppErrInvalidAssetOfOffer } // Check offer expired time. if err := e.bc.VerifyExpiredAt(txInfo.BuyOffer.ExpiredAt); err != nil { - return errors.New("invalid BuyOffer.ExpiredAt") + return types.AppErrInvalidBuyOfferExpireTime } if err := e.bc.VerifyExpiredAt(txInfo.SellOffer.ExpiredAt); err != nil { - return errors.New("invalid SellOffer.ExpiredAt") + return types.AppErrInvalidSellOfferExpireTime } fromAccount, err := bc.StateDB().GetFormatAccount(txInfo.AccountIndex) @@ -127,26 +127,26 @@ func (e *AtomicMatchExecutor) VerifyInputs(skipGasAmtChk bool) error { if txInfo.AccountIndex == txInfo.BuyOffer.AccountIndex && txInfo.GasFeeAssetId == txInfo.SellOffer.AssetId { totalBalance := ffmath.Add(txInfo.GasFeeAssetAmount, txInfo.BuyOffer.AssetAmount) if fromAccount.AssetInfo[txInfo.GasFeeAssetId].Balance.Cmp(totalBalance) < 0 { - return errors.New("sender balance is not enough") + return types.AppErrSellerBalanceNotEnough } } else { if fromAccount.AssetInfo[txInfo.GasFeeAssetId].Balance.Cmp(txInfo.GasFeeAssetAmount) < 0 { - return errors.New("sender balance is not enough") + return types.AppErrSellerBalanceNotEnough } if buyAccount.AssetInfo[txInfo.BuyOffer.AssetId].Balance.Cmp(txInfo.BuyOffer.AssetAmount) < 0 { - return errors.New("buy balance is not enough") + return types.AppErrBuyerBalanceNotEnough } } // Check offer canceled or finalized. sellOffer := sellAccount.AssetInfo[e.sellOfferAssetId].OfferCanceledOrFinalized if sellOffer.Bit(int(e.sellOfferIndex)) == 1 { - return errors.New("sell offer canceled or finalized") + return types.AppErrInvalidSellOfferState } buyOffer := buyAccount.AssetInfo[e.buyOfferAssetId].OfferCanceledOrFinalized if buyOffer.Bit(int(e.buyOfferIndex)) == 1 { - return errors.New("buy offer canceled or finalized") + return types.AppErrInvalidBuyOfferState } // Check the seller is the owner of the nft. @@ -155,7 +155,7 @@ func (e *AtomicMatchExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if nft.OwnerAccountIndex != txInfo.SellOffer.AccountIndex { - return errors.New("seller is not owner") + return types.AppErrSellerNotOwner } // Verify offer signature. @@ -220,8 +220,8 @@ func (e *AtomicMatchExecutor) ApplyTransaction() error { stateCache.SetPendingAccount(sellAccount.AccountIndex, sellAccount) stateCache.SetPendingAccount(creatorAccount.AccountIndex, creatorAccount) stateCache.SetPendingNft(matchNft.NftIndex, matchNft) - stateCache.SetPendingUpdateGas(txInfo.BuyOffer.AssetId, txInfo.TreasuryAmount) - stateCache.SetPendingUpdateGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) + stateCache.SetPendingGas(txInfo.BuyOffer.AssetId, txInfo.TreasuryAmount) + stateCache.SetPendingGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) return e.BaseExecutor.ApplyTransaction() } diff --git a/core/executor/base_executor.go b/core/executor/base_executor.go index a38fb7cf8..0d97c0d5f 100644 --- a/core/executor/base_executor.go +++ b/core/executor/base_executor.go @@ -3,7 +3,6 @@ package executor import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" "github.com/ethereum/go-ethereum/common" - "github.com/pkg/errors" "github.com/zeromicro/go-zero/core/logx" "github.com/bnb-chain/zkbnb-crypto/wasm/txtypes" @@ -55,7 +54,7 @@ func (e *BaseExecutor) Prepare() error { err := e.bc.StateDB().PrepareAccountsAndAssets(e.dirtyAccountsAndAssetsMap) if err != nil { logx.Errorf("prepare accounts and assets failed: %s", err.Error()) - return errors.New("internal error") + return err } return nil } diff --git a/core/executor/cancel_offer_executor.go b/core/executor/cancel_offer_executor.go index 3ab06ba4a..55e9e4422 100644 --- a/core/executor/cancel_offer_executor.go +++ b/core/executor/cancel_offer_executor.go @@ -57,7 +57,7 @@ func (e *CancelOfferExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if fromAccount.AssetInfo[txInfo.GasFeeAssetId].Balance.Cmp(txInfo.GasFeeAssetAmount) < 0 { - return errors.New("balance is not enough") + return types.AppErrBalanceNotEnough } offerAssetId := txInfo.OfferId / 128 @@ -66,7 +66,7 @@ func (e *CancelOfferExecutor) VerifyInputs(skipGasAmtChk bool) error { if offerAsset != nil && offerAsset.OfferCanceledOrFinalized != nil { xBit := offerAsset.OfferCanceledOrFinalized.Bit(int(offerIndex)) if xBit == 1 { - return errors.New("invalid offer id, already confirmed or canceled") + return types.AppErrInvalidOfferState } } @@ -94,7 +94,7 @@ func (e *CancelOfferExecutor) ApplyTransaction() error { stateCache := e.bc.StateDB() stateCache.SetPendingAccount(fromAccount.AccountIndex, fromAccount) - stateCache.SetPendingUpdateGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) + stateCache.SetPendingGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) return e.BaseExecutor.ApplyTransaction() } diff --git a/core/executor/create_collection_executor.go b/core/executor/create_collection_executor.go index 8fbf28464..09007e4ce 100644 --- a/core/executor/create_collection_executor.go +++ b/core/executor/create_collection_executor.go @@ -67,7 +67,7 @@ func (e *CreateCollectionExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if fromAccount.AssetInfo[txInfo.GasFeeAssetId].Balance.Cmp(txInfo.GasFeeAssetAmount) < 0 { - return errors.New("balance is not enough") + return types.AppErrBalanceNotEnough } return nil @@ -89,7 +89,7 @@ func (e *CreateCollectionExecutor) ApplyTransaction() error { stateCache := e.bc.StateDB() stateCache.SetPendingAccount(fromAccount.AccountIndex, fromAccount) - stateCache.SetPendingUpdateGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) + stateCache.SetPendingGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) return e.BaseExecutor.ApplyTransaction() } diff --git a/core/executor/deposit_executor.go b/core/executor/deposit_executor.go index 46ef44eed..50ba393f6 100644 --- a/core/executor/deposit_executor.go +++ b/core/executor/deposit_executor.go @@ -58,7 +58,7 @@ func (e *DepositExecutor) VerifyInputs(skipGasAmtChk bool) error { txInfo := e.txInfo if txInfo.AssetAmount.Cmp(types.ZeroBigInt) < 0 { - return errors.New("invalid asset amount") + return types.AppErrInvalidAssetAmount } return nil diff --git a/core/executor/deposit_nft_executor.go b/core/executor/deposit_nft_executor.go index c5b502f53..5ac33216c 100644 --- a/core/executor/deposit_nft_executor.go +++ b/core/executor/deposit_nft_executor.go @@ -57,7 +57,7 @@ func (e *DepositNftExecutor) Prepare() error { // Mark the tree states that would be affected in this executor. e.MarkNftDirty(txInfo.NftIndex) - e.MarkAccountAssetsDirty(txInfo.AccountIndex, []int64{0}) // Prepare asset 0 for generate an empty tx detail. + e.MarkAccountAssetsDirty(txInfo.AccountIndex, []int64{types.EmptyAccountAssetId}) // Prepare asset 0 for generate an empty tx detail. return e.BaseExecutor.Prepare() } @@ -70,7 +70,7 @@ func (e *DepositNftExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if nft.NftContentHash != types.EmptyNftContentHash { - return errors.New("invalid nft index, already exist") + return types.AppErrNftAlreadyExist } return nil @@ -149,14 +149,14 @@ func (e *DepositNftExecutor) GenerateTxDetails() ([]*tx.TxDetail, error) { // user info accountOrder := int64(0) order := int64(0) - baseBalance := depositAccount.AssetInfo[0] + baseBalance := depositAccount.AssetInfo[types.EmptyAccountAssetId] deltaBalance := &types.AccountAsset{ - AssetId: 0, + AssetId: types.EmptyAccountAssetId, Balance: big.NewInt(0), OfferCanceledOrFinalized: big.NewInt(0), } txDetails = append(txDetails, &tx.TxDetail{ - AssetId: 0, + AssetId: types.EmptyAccountAssetId, AssetType: types.FungibleAssetType, AccountIndex: txInfo.AccountIndex, AccountName: depositAccount.AccountName, diff --git a/core/executor/full_exit_executor.go b/core/executor/full_exit_executor.go index 1215b567b..cb78cf5aa 100644 --- a/core/executor/full_exit_executor.go +++ b/core/executor/full_exit_executor.go @@ -26,7 +26,7 @@ func NewFullExitExecutor(bc IBlockchain, tx *tx.Tx) (TxExecutor, error) { txInfo, err := types.ParseFullExitTxInfo(tx.TxInfo) if err != nil { logx.Errorf("parse full exit tx failed: %s", err.Error()) - return nil, errors.New("invalid tx info") + return nil, types.AppErrInvalidTxInfo } return &FullExitExecutor{ diff --git a/core/executor/full_exit_nft_executor.go b/core/executor/full_exit_nft_executor.go index 30586cdb3..8ddcc4852 100644 --- a/core/executor/full_exit_nft_executor.go +++ b/core/executor/full_exit_nft_executor.go @@ -81,7 +81,7 @@ func (e *FullExitNftExecutor) Prepare() error { // Mark the tree states that would be affected in this executor. e.MarkNftDirty(txInfo.NftIndex) - e.MarkAccountAssetsDirty(txInfo.AccountIndex, []int64{0}) // Prepare asset 0 for generate an empty tx detail. + e.MarkAccountAssetsDirty(txInfo.AccountIndex, []int64{types.EmptyAccountAssetId}) // Prepare asset 0 for generate an empty tx detail. err = e.BaseExecutor.Prepare() if err != nil { return err @@ -193,14 +193,14 @@ func (e *FullExitNftExecutor) GenerateTxDetails() ([]*tx.TxDetail, error) { // user info accountOrder := int64(0) order := int64(0) - baseBalance := exitAccount.AssetInfo[0] + baseBalance := exitAccount.AssetInfo[types.EmptyAccountAssetId] emptyDelta := &types.AccountAsset{ - AssetId: 0, + AssetId: types.EmptyAccountAssetId, Balance: big.NewInt(0), OfferCanceledOrFinalized: big.NewInt(0), } txDetails = append(txDetails, &tx.TxDetail{ - AssetId: 0, + AssetId: types.EmptyAccountAssetId, AssetType: types.FungibleAssetType, AccountIndex: txInfo.AccountIndex, AccountName: exitAccount.AccountName, diff --git a/core/executor/mint_nft_executor.go b/core/executor/mint_nft_executor.go index 5fd85c3da..df44f2b89 100644 --- a/core/executor/mint_nft_executor.go +++ b/core/executor/mint_nft_executor.go @@ -63,10 +63,10 @@ func (e *MintNftExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if creatorAccount.CollectionNonce <= txInfo.NftCollectionId { - return errors.New("nft collection id is not less than account collection nonce") + return types.AppErrInvalidCollectionId } if creatorAccount.AssetInfo[txInfo.GasFeeAssetId].Balance.Cmp(txInfo.GasFeeAssetAmount) < 0 { - return errors.New("balance is not enough") + return types.AppErrBalanceNotEnough } toAccount, err := e.bc.StateDB().GetFormatAccount(txInfo.ToAccountIndex) @@ -74,7 +74,7 @@ func (e *MintNftExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if txInfo.ToAccountNameHash != toAccount.AccountNameHash { - return errors.New("invalid ToAccountNameHash") + return types.AppErrInvalidToAccountNameHash } return nil @@ -105,7 +105,7 @@ func (e *MintNftExecutor) ApplyTransaction() error { CreatorTreasuryRate: txInfo.CreatorTreasuryRate, CollectionId: txInfo.NftCollectionId, }) - stateCache.SetPendingUpdateGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) + stateCache.SetPendingGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) return e.BaseExecutor.ApplyTransaction() } diff --git a/core/executor/register_zns_executor.go b/core/executor/register_zns_executor.go index a381119dd..10a73382c 100644 --- a/core/executor/register_zns_executor.go +++ b/core/executor/register_zns_executor.go @@ -53,11 +53,11 @@ func (e *RegisterZnsExecutor) VerifyInputs(skipGasAmtChk bool) error { _, err := bc.StateDB().GetAccountByName(txInfo.AccountName) if err == nil { - return errors.New("invalid account name, already registered") + return types.AppErrAccountNameAlreadyRegistered } if txInfo.AccountIndex != bc.StateDB().GetNextAccountIndex() { - return errors.New("invalid account index") + return types.AppErrInvalidAccountIndex } return nil diff --git a/core/executor/transfer_executor.go b/core/executor/transfer_executor.go index 887df5e5f..348da2a24 100644 --- a/core/executor/transfer_executor.go +++ b/core/executor/transfer_executor.go @@ -61,19 +61,19 @@ func (e *TransferExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if txInfo.ToAccountNameHash != toAccount.AccountNameHash { - return errors.New("invalid to account name hash") + return types.AppErrInvalidToAccountNameHash } if txInfo.GasFeeAssetId != txInfo.AssetId { if fromAccount.AssetInfo[txInfo.GasFeeAssetId].Balance.Cmp(txInfo.GasFeeAssetAmount) < 0 { - return errors.New("invalid gas asset amount") + return types.AppErrInvalidGasFeeAmount } if fromAccount.AssetInfo[txInfo.AssetId].Balance.Cmp(txInfo.AssetAmount) < 0 { - return errors.New("invalid asset amount") + return types.AppErrInvalidAssetAmount } } else { deltaBalance := ffmath.Add(txInfo.AssetAmount, txInfo.GasFeeAssetAmount) if fromAccount.AssetInfo[txInfo.AssetId].Balance.Cmp(deltaBalance) < 0 { - return errors.New("invalid asset amount") + return types.AppErrInvalidAssetAmount } } @@ -101,7 +101,7 @@ func (e *TransferExecutor) ApplyTransaction() error { stateCache := e.bc.StateDB() stateCache.SetPendingAccount(txInfo.FromAccountIndex, fromAccount) stateCache.SetPendingAccount(txInfo.ToAccountIndex, toAccount) - stateCache.SetPendingUpdateGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) + stateCache.SetPendingGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) return e.BaseExecutor.ApplyTransaction() } diff --git a/core/executor/transfer_nft_executor.go b/core/executor/transfer_nft_executor.go index 27a4fe157..235f5f033 100644 --- a/core/executor/transfer_nft_executor.go +++ b/core/executor/transfer_nft_executor.go @@ -39,13 +39,14 @@ func (e *TransferNftExecutor) Prepare() error { _, err := e.bc.StateDB().PrepareNft(txInfo.NftIndex) if err != nil { logx.Errorf("prepare nft failed") - return errors.New("internal error") + return err } // Mark the tree states that would be affected in this executor. e.MarkNftDirty(txInfo.NftIndex) e.MarkAccountAssetsDirty(txInfo.FromAccountIndex, []int64{txInfo.GasFeeAssetId}) - e.MarkAccountAssetsDirty(txInfo.ToAccountIndex, []int64{}) + // For empty tx details generation + e.MarkAccountAssetsDirty(txInfo.ToAccountIndex, []int64{types.EmptyAccountAssetId}) e.MarkAccountAssetsDirty(txInfo.GasAccountIndex, []int64{txInfo.GasFeeAssetId}) return e.BaseExecutor.Prepare() } @@ -63,7 +64,7 @@ func (e *TransferNftExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if fromAccount.AssetInfo[txInfo.GasFeeAssetId].Balance.Cmp(txInfo.GasFeeAssetAmount) < 0 { - return errors.New("balance is not enough") + return types.AppErrBalanceNotEnough } toAccount, err := e.bc.StateDB().GetFormatAccount(txInfo.ToAccountIndex) @@ -71,7 +72,7 @@ func (e *TransferNftExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if txInfo.ToAccountNameHash != toAccount.AccountNameHash { - return errors.New("invalid ToAccountNameHash") + return types.AppErrInvalidToAccountNameHash } nft, err := e.bc.StateDB().GetNft(txInfo.NftIndex) @@ -105,7 +106,7 @@ func (e *TransferNftExecutor) ApplyTransaction() error { stateCache := e.bc.StateDB() stateCache.SetPendingAccount(txInfo.FromAccountIndex, fromAccount) stateCache.SetPendingNft(txInfo.NftIndex, nft) - stateCache.SetPendingUpdateGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) + stateCache.SetPendingGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) return e.BaseExecutor.ApplyTransaction() } @@ -198,13 +199,13 @@ func (e *TransferNftExecutor) GenerateTxDetails() ([]*tx.TxDetail, error) { order++ accountOrder++ txDetails = append(txDetails, &tx.TxDetail{ - AssetId: txInfo.GasFeeAssetId, + AssetId: types.EmptyAccountAssetId, AssetType: types.FungibleAssetType, AccountIndex: txInfo.ToAccountIndex, AccountName: toAccount.AccountName, - Balance: toAccount.AssetInfo[txInfo.GasFeeAssetId].String(), + Balance: toAccount.AssetInfo[types.EmptyAccountAssetId].String(), BalanceDelta: types.ConstructAccountAsset( - txInfo.GasFeeAssetId, + types.EmptyAccountAssetId, types.ZeroBigInt, types.ZeroBigInt, ).String(), diff --git a/core/executor/withdraw_executor.go b/core/executor/withdraw_executor.go index 793218caf..6bd403792 100644 --- a/core/executor/withdraw_executor.go +++ b/core/executor/withdraw_executor.go @@ -56,15 +56,15 @@ func (e *WithdrawExecutor) VerifyInputs(skipGasAmtChk bool) error { } if txInfo.GasFeeAssetId != txInfo.AssetId { if fromAccount.AssetInfo[txInfo.AssetId].Balance.Cmp(txInfo.AssetAmount) < 0 { - return errors.New("invalid asset amount") + return types.AppErrInvalidGasFeeAccount } if fromAccount.AssetInfo[txInfo.GasFeeAssetId].Balance.Cmp(txInfo.GasFeeAssetAmount) < 0 { - return errors.New("invalid gas asset amount") + return types.AppErrInvalidAssetAmount } } else { deltaBalance := ffmath.Add(txInfo.AssetAmount, txInfo.GasFeeAssetAmount) if fromAccount.AssetInfo[txInfo.AssetId].Balance.Cmp(deltaBalance) < 0 { - return errors.New("invalid asset amount") + return types.AppErrInvalidAssetAmount } } @@ -87,7 +87,7 @@ func (e *WithdrawExecutor) ApplyTransaction() error { stateCache := e.bc.StateDB() stateCache.SetPendingAccount(txInfo.FromAccountIndex, fromAccount) - stateCache.SetPendingUpdateGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) + stateCache.SetPendingGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) return e.BaseExecutor.ApplyTransaction() } diff --git a/core/executor/withdraw_nft_executor.go b/core/executor/withdraw_nft_executor.go index dc62c1008..c2291407a 100644 --- a/core/executor/withdraw_nft_executor.go +++ b/core/executor/withdraw_nft_executor.go @@ -50,7 +50,7 @@ func (e *WithdrawNftExecutor) Prepare() error { e.MarkAccountAssetsDirty(txInfo.AccountIndex, []int64{txInfo.GasFeeAssetId}) e.MarkAccountAssetsDirty(txInfo.GasAccountIndex, []int64{txInfo.GasFeeAssetId}) if nftInfo.CreatorAccountIndex != types.NilAccountIndex { - e.MarkAccountAssetsDirty(nftInfo.CreatorAccountIndex, []int64{}) + e.MarkAccountAssetsDirty(nftInfo.CreatorAccountIndex, []int64{types.EmptyAccountAssetId}) } err = e.BaseExecutor.Prepare() if err != nil { @@ -89,7 +89,7 @@ func (e *WithdrawNftExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if fromAccount.AssetInfo[txInfo.GasFeeAssetId].Balance.Cmp(txInfo.GasFeeAssetAmount) < 0 { - return errors.New("balance is not enough") + return types.AppErrBalanceNotEnough } nftInfo, err := e.bc.StateDB().GetNft(txInfo.NftIndex) @@ -97,7 +97,7 @@ func (e *WithdrawNftExecutor) VerifyInputs(skipGasAmtChk bool) error { return err } if nftInfo.OwnerAccountIndex != txInfo.AccountIndex { - return errors.New("account is not owner of the nft") + return types.AppErrNotNftOwner } return nil @@ -134,7 +134,7 @@ func (e *WithdrawNftExecutor) ApplyTransaction() error { CreatorTreasuryRate: newNftInfo.CreatorTreasuryRate, CollectionId: newNftInfo.CollectionId, }) - stateCache.SetPendingUpdateGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) + stateCache.SetPendingGas(txInfo.GasFeeAssetId, txInfo.GasFeeAssetAmount) return e.BaseExecutor.ApplyTransaction() } @@ -264,13 +264,13 @@ func (e *WithdrawNftExecutor) GenerateTxDetails() ([]*tx.TxDetail, error) { order++ accountOrder++ txDetails = append(txDetails, &tx.TxDetail{ - AssetId: txInfo.GasFeeAssetId, + AssetId: types.EmptyAccountAssetId, AssetType: types.FungibleAssetType, AccountIndex: txInfo.CreatorAccountIndex, AccountName: creatorAccount.AccountName, - Balance: creatorAccount.AssetInfo[txInfo.GasFeeAssetId].String(), + Balance: creatorAccount.AssetInfo[types.EmptyAccountAssetId].String(), BalanceDelta: types.ConstructAccountAsset( - txInfo.GasFeeAssetId, + types.EmptyAccountAssetId, types.ZeroBigInt, types.ZeroBigInt, ).String(), diff --git a/core/statedb/state_cache.go b/core/statedb/state_cache.go index 8b28ece3f..6f698d0dd 100644 --- a/core/statedb/state_cache.go +++ b/core/statedb/state_cache.go @@ -103,14 +103,14 @@ func (c *StateCache) SetPendingNft(nftIndex int64, nft *nft.L2Nft) { c.PendingNftMap[nftIndex] = nft } -func (c *StateCache) GetPendingUpdateGas(assetId int64) *big.Int { +func (c *StateCache) GetPendingGas(assetId int64) *big.Int { if delta, ok := c.PendingGasMap[assetId]; ok { return delta } return types.ZeroBigInt } -func (c *StateCache) SetPendingUpdateGas(assetId int64, balanceDelta *big.Int) { +func (c *StateCache) SetPendingGas(assetId int64, balanceDelta *big.Int) { if _, ok := c.PendingGasMap[assetId]; !ok { c.PendingGasMap[assetId] = types.ZeroBigInt } diff --git a/core/statedb/statedb.go b/core/statedb/statedb.go index b1654211e..52717bde1 100644 --- a/core/statedb/statedb.go +++ b/core/statedb/statedb.go @@ -15,6 +15,7 @@ import ( "github.com/bnb-chain/zkbnb-crypto/ffmath" bsmt "github.com/bnb-chain/zkbnb-smt" "github.com/bnb-chain/zkbnb/common/chain" + "github.com/bnb-chain/zkbnb/common/gopool" "github.com/bnb-chain/zkbnb/dao/account" "github.com/bnb-chain/zkbnb/dao/dbcache" "github.com/bnb-chain/zkbnb/dao/nft" @@ -152,7 +153,9 @@ func (s *StateDB) GetFormatAccount(accountIndex int64) (*types.AccountInfo, erro } account, err := s.chainDb.AccountModel.GetAccountByIndex(accountIndex) - if err != nil { + if err == types.DbErrNotFound { + return nil, types.AppErrAccountNotFound + } else if err != nil { return nil, err } formatAccount, err := chain.ToFormatAccountInfo(account) @@ -252,13 +255,28 @@ func (s *StateDB) GetNft(nftIndex int64) (*nft.L2Nft, error) { return cached.(*nft.L2Nft), nil } nft, err := s.chainDb.L2NftModel.GetNft(nftIndex) - if err != nil { + if err == types.DbErrNotFound { + return nil, types.AppErrNftNotFound + } else if err != nil { return nil, err } s.NftCache.Add(nftIndex, nft) return nft, nil } +// MarkGasAccountAsPending will mark gas account as pending account. Putting gas account is pending +// account will unify many codes and remove some tricky logics. +func (s *StateDB) MarkGasAccountAsPending() error { + gasAccount, err := s.GetFormatAccount(types.GasAccount) + if err != nil && err != types.AppErrAccountNotFound { + return err + } + if err == nil { + s.PendingAccountMap[types.GasAccount] = gasAccount + } + return nil +} + func (s *StateDB) syncPendingAccount(pendingAccount map[int64]*types.AccountInfo) error { for index, formatAccount := range pendingAccount { account, err := chain.FromFormatAccountInfo(formatAccount) @@ -286,10 +304,9 @@ func (s *StateDB) syncPendingNft(pendingNft map[int64]*nft.L2Nft) error { return nil } -func (s *StateDB) SyncPendingGasAccount() error { +func (s *StateDB) SyncGasAccountToRedis() error { if cacheAccount, ok := s.AccountCache.Get(types.GasAccount); ok { formatAccount := cacheAccount.(*types.AccountInfo) - s.applyGasUpdate(formatAccount) account, err := chain.FromFormatAccountInfo(formatAccount) if err != nil { return err @@ -298,7 +315,6 @@ func (s *StateDB) SyncPendingGasAccount() error { if err != nil { return fmt.Errorf("cache to redis failed: %v", err) } - s.AccountCache.Add(account.AccountIndex, formatAccount) } return nil } @@ -325,48 +341,11 @@ func (s *StateDB) GetPendingAccount(blockHeight int64) ([]*account.Account, []*a pendingAccount := make([]*account.Account, 0) pendingAccountHistory := make([]*account.AccountHistory, 0) - gasChanged := false - for _, delta := range s.StateCache.PendingGasMap { - if delta.Cmp(types.ZeroBigInt) > 0 { - gasChanged = true - break - } - } - - handledGasAccount := false for _, formatAccount := range s.PendingAccountMap { - if formatAccount.AccountIndex == types.GasAccount && gasChanged { - handledGasAccount = true + if formatAccount.AccountIndex == types.GasAccount { s.applyGasUpdate(formatAccount) } - newAccount, err := chain.FromFormatAccountInfo(formatAccount) - if err != nil { - return nil, nil, err - } - pendingAccount = append(pendingAccount, newAccount) - pendingAccountHistory = append(pendingAccountHistory, &account.AccountHistory{ - AccountIndex: newAccount.AccountIndex, - Nonce: newAccount.Nonce, - CollectionNonce: newAccount.CollectionNonce, - AssetInfo: newAccount.AssetInfo, - AssetRoot: newAccount.AssetRoot, - L2BlockHeight: blockHeight, // TODO: ensure this should be the new block's height. - }) - } - - if !handledGasAccount && gasChanged { - gasAccount, err := s.GetAccount(types.GasAccount) - if err != nil { - return nil, nil, err - } - - formatAccount, err := chain.ToFormatAccountInfo(gasAccount) - if err != nil { - return nil, nil, err - } - s.applyGasUpdate(formatAccount) - newAccount, err := chain.FromFormatAccountInfo(formatAccount) if err != nil { return nil, nil, err @@ -488,7 +467,23 @@ func (s *StateDB) PrepareNft(nftIndex int64) (*nft.L2Nft, error) { return s.GetNft(nftIndex) } +const ( + accountTreeRole = "account" + nftTreeRole = "nft" +) + +type treeUpdateResp struct { + role string + index int64 + leaf []byte + err error +} + func (s *StateDB) IntermediateRoot(cleanDirty bool) error { + taskNum := 0 + resultChan := make(chan *treeUpdateResp, 1) + defer close(resultChan) + for accountIndex, assetsMap := range s.dirtyAccountsAndAssetsMap { assets := make([]int64, 0, len(assetsMap)) for assetIndex, isDirty := range assetsMap { @@ -497,8 +492,18 @@ func (s *StateDB) IntermediateRoot(cleanDirty bool) error { } assets = append(assets, assetIndex) } - - err := s.updateAccountTree(accountIndex, assets) + taskNum++ + err := func(accountIndex int64, assets []int64) error { + return gopool.Submit(func() { + index, leaf, err := s.updateAccountTree(accountIndex, assets) + resultChan <- &treeUpdateResp{ + role: accountTreeRole, + index: index, + leaf: leaf, + err: err, + } + }) + }(accountIndex, assets) if err != nil { return err } @@ -508,7 +513,18 @@ func (s *StateDB) IntermediateRoot(cleanDirty bool) error { if !isDirty { continue } - err := s.updateNftTree(nftIndex) + taskNum++ + err := func(nftIndex int64) error { + return gopool.Submit(func() { + index, leaf, err := s.updateNftTree(nftIndex) + resultChan <- &treeUpdateResp{ + role: nftTreeRole, + index: index, + leaf: leaf, + err: err, + } + }) + }(nftIndex) if err != nil { return err } @@ -519,6 +535,46 @@ func (s *StateDB) IntermediateRoot(cleanDirty bool) error { s.dirtyNftMap = make(map[int64]bool, 0) } + pendingAccountItem := make([]bsmt.Item, 0, len(s.dirtyAccountsAndAssetsMap)) + pendingNftItem := make([]bsmt.Item, 0, len(s.dirtyNftMap)) + for i := 0; i < taskNum; i++ { + result := <-resultChan + if result.err != nil { + return result.err + } + + switch result.role { + case accountTreeRole: + pendingAccountItem = append(pendingAccountItem, bsmt.Item{Key: uint64(result.index), Val: result.leaf}) + case nftTreeRole: + pendingNftItem = append(pendingNftItem, bsmt.Item{Key: uint64(result.index), Val: result.leaf}) + } + } + err := gopool.Submit(func() { + resultChan <- &treeUpdateResp{ + role: accountTreeRole, + err: s.AccountTree.MultiSet(pendingAccountItem), + } + }) + if err != nil { + return err + } + err = gopool.Submit(func() { + resultChan <- &treeUpdateResp{ + role: nftTreeRole, + err: s.NftTree.MultiSet(pendingNftItem), + } + }) + if err != nil { + return err + } + for i := 0; i < 2; i++ { + result := <-resultChan + if result.err != nil { + return fmt.Errorf("update %s tree failed, %v", result.role, result.err) + } + } + hFunc := mimc.NewMiMC() hFunc.Write(s.AccountTree.Root()) hFunc.Write(s.NftTree.Root()) @@ -526,12 +582,13 @@ func (s *StateDB) IntermediateRoot(cleanDirty bool) error { return nil } -func (s *StateDB) updateAccountTree(accountIndex int64, assets []int64) error { +func (s *StateDB) updateAccountTree(accountIndex int64, assets []int64) (int64, []byte, error) { account, err := s.GetFormatAccount(accountIndex) if err != nil { - return err + return accountIndex, nil, err } isGasAccount := accountIndex == types.GasAccount + pendingUpdateAssetItem := make([]bsmt.Item, 0, len(assets)) for _, assetId := range assets { isGasAsset := false if isGasAccount { @@ -544,19 +601,21 @@ func (s *StateDB) updateAccountTree(accountIndex int64, assets []int64) error { } balance := account.AssetInfo[assetId].Balance if isGasAsset { - balance = ffmath.Add(balance, s.GetPendingUpdateGas(assetId)) + balance = ffmath.Add(balance, s.GetPendingGas(assetId)) } assetLeaf, err := tree.ComputeAccountAssetLeafHash( balance.String(), account.AssetInfo[assetId].OfferCanceledOrFinalized.String(), ) if err != nil { - return fmt.Errorf("compute new account asset leaf failed: %v", err) - } - err = s.AccountAssetTrees.Get(accountIndex).Set(uint64(assetId), assetLeaf) - if err != nil { - return fmt.Errorf("update asset tree failed: %v", err) + return accountIndex, nil, fmt.Errorf("compute new account asset leaf failed: %v", err) } + pendingUpdateAssetItem = append(pendingUpdateAssetItem, bsmt.Item{Key: uint64(assetId), Val: assetLeaf}) + } + + err = s.AccountAssetTrees.Get(accountIndex).MultiSet(pendingUpdateAssetItem) + if err != nil { + return accountIndex, nil, fmt.Errorf("update asset tree failed: %v", err) } account.AssetRoot = common.Bytes2Hex(s.AccountAssetTrees.Get(accountIndex).Root()) @@ -568,20 +627,16 @@ func (s *StateDB) updateAccountTree(accountIndex int64, assets []int64) error { s.AccountAssetTrees.Get(accountIndex).Root(), ) if err != nil { - return fmt.Errorf("unable to compute account leaf: %v", err) - } - err = s.AccountTree.Set(uint64(accountIndex), nAccountLeafHash) - if err != nil { - return fmt.Errorf("unable to update account tree: %v", err) + return accountIndex, nil, fmt.Errorf("unable to compute account leaf: %v", err) } - return nil + return accountIndex, nAccountLeafHash, nil } -func (s *StateDB) updateNftTree(nftIndex int64) error { +func (s *StateDB) updateNftTree(nftIndex int64) (int64, []byte, error) { nft, err := s.GetNft(nftIndex) if err != nil { - return err + return nftIndex, nil, err } nftAssetLeaf, err := tree.ComputeNftAssetLeafHash( nft.CreatorAccountIndex, @@ -593,14 +648,10 @@ func (s *StateDB) updateNftTree(nftIndex int64) error { nft.CollectionId, ) if err != nil { - return fmt.Errorf("unable to compute nft leaf: %v", err) - } - err = s.NftTree.Set(uint64(nftIndex), nftAssetLeaf) - if err != nil { - return fmt.Errorf("unable to update nft tree: %v", err) + return nftIndex, nil, fmt.Errorf("unable to compute nft leaf: %v", err) } - return nil + return nftIndex, nftAssetLeaf, nil } func (s *StateDB) GetCommittedNonce(accountIndex int64) (int64, error) { @@ -680,6 +731,7 @@ func (s *StateDB) GetGasConfig() (map[uint32]map[int]int64, error) { return nil, errors.New("invalid gas fee asset") } gasFeeValue = cfgGasFee.Value + _ = s.redisCache.Set(context.Background(), dbcache.GasConfigKey, gasFeeValue) } m := make(map[uint32]map[int]int64) diff --git a/dao/compressedblock/compressed_block.go b/dao/compressedblock/compressed_block.go index e5b689c62..8aefb8753 100644 --- a/dao/compressedblock/compressed_block.go +++ b/dao/compressedblock/compressed_block.go @@ -71,7 +71,7 @@ func (m *defaultCompressedBlockModel) DropCompressedBlockTable() error { } func (m *defaultCompressedBlockModel) GetCompressedBlocksBetween(start, end int64) (blocksForCommit []*CompressedBlock, err error) { - dbTx := m.DB.Table(m.table).Where("block_height >= ? AND block_height <= ?", start, end).Find(&blocksForCommit) + dbTx := m.DB.Table(m.table).Where("block_height >= ? AND block_height <= ?", start, end).Order("block_height").Find(&blocksForCommit) if dbTx.Error != nil { return nil, types.DbErrSqlOperation } else if dbTx.RowsAffected == 0 { diff --git a/dao/tx/tx.go b/dao/tx/tx.go index 7be1ce3aa..85b6d8d47 100644 --- a/dao/tx/tx.go +++ b/dao/tx/tx.go @@ -38,14 +38,47 @@ const ( StatusVerified ) +type getTxOption struct { + Types []int64 + Statuses []int64 + FromHash string + WithDeleted bool +} + +type GetTxOptionFunc func(*getTxOption) + +func GetTxWithTypes(txTypes []int64) GetTxOptionFunc { + return func(o *getTxOption) { + o.Types = txTypes + } +} + +func GetTxWithStatuses(statuses []int64) GetTxOptionFunc { + return func(o *getTxOption) { + o.Statuses = statuses + } +} + +func GetTxWithFromHash(hash string) GetTxOptionFunc { + return func(o *getTxOption) { + o.FromHash = hash + } +} + +func GetTxWithDeleted() GetTxOptionFunc { + return func(o *getTxOption) { + o.WithDeleted = true + } +} + type ( TxModel interface { CreateTxTable() error DropTxTable() error - GetTxsTotalCount() (count int64, err error) - GetTxs(limit int64, offset int64) (txList []*Tx, err error) - GetTxsByAccountIndex(accountIndex int64, limit int64, offset int64) (txList []*Tx, err error) - GetTxsCountByAccountIndex(accountIndex int64) (count int64, err error) + GetTxsTotalCount(options ...GetTxOptionFunc) (count int64, err error) + GetTxs(limit int64, offset int64, options ...GetTxOptionFunc) (txList []*Tx, err error) + GetTxsByAccountIndex(accountIndex int64, limit int64, offset int64, options ...GetTxOptionFunc) (txList []*Tx, err error) + GetTxsCountByAccountIndex(accountIndex int64, options ...GetTxOptionFunc) (count int64, err error) GetTxByHash(txHash string) (tx *Tx, err error) GetTxsTotalCountBetween(from, to time.Time) (count int64, err error) GetDistinctAccountsCountBetween(from, to time.Time) (count int64, err error) @@ -106,8 +139,18 @@ func (m *defaultTxModel) DropTxTable() error { return m.DB.Migrator().DropTable(m.table) } -func (m *defaultTxModel) GetTxsTotalCount() (count int64, err error) { - dbTx := m.DB.Table(m.table).Where("deleted_at is NULL").Count(&count) +func (m *defaultTxModel) GetTxsTotalCount(options ...GetTxOptionFunc) (count int64, err error) { + opt := &getTxOption{} + for _, f := range options { + f(opt) + } + + dbTx := m.DB.Table(m.table) + if len(opt.Statuses) > 0 { + dbTx = dbTx.Where("tx_status IN ?", opt.Statuses) + } + + dbTx = dbTx.Where("deleted_at is NULL").Count(&count) if dbTx.Error != nil { if dbTx.Error == types.DbErrNotFound { return 0, nil @@ -117,8 +160,18 @@ func (m *defaultTxModel) GetTxsTotalCount() (count int64, err error) { return count, nil } -func (m *defaultTxModel) GetTxs(limit int64, offset int64) (txList []*Tx, err error) { - dbTx := m.DB.Table(m.table).Limit(int(limit)).Offset(int(offset)).Order("created_at desc").Find(&txList) +func (m *defaultTxModel) GetTxs(limit int64, offset int64, options ...GetTxOptionFunc) (txList []*Tx, err error) { + opt := &getTxOption{} + for _, f := range options { + f(opt) + } + + dbTx := m.DB.Table(m.table) + if len(opt.Statuses) > 0 { + dbTx = dbTx.Where("tx_status IN ?", opt.Statuses) + } + + dbTx = dbTx.Limit(int(limit)).Offset(int(offset)).Order("created_at desc").Find(&txList) if dbTx.Error != nil { return nil, types.DbErrSqlOperation } else if dbTx.RowsAffected == 0 { @@ -127,8 +180,18 @@ func (m *defaultTxModel) GetTxs(limit int64, offset int64) (txList []*Tx, err er return txList, nil } -func (m *defaultTxModel) GetTxsByAccountIndex(accountIndex int64, limit int64, offset int64) (txList []*Tx, err error) { - dbTx := m.DB.Table(m.table).Where("account_index = ?", accountIndex).Limit(int(limit)).Offset(int(offset)).Order("created_at desc").Find(&txList) +func (m *defaultTxModel) GetTxsByAccountIndex(accountIndex int64, limit int64, offset int64, options ...GetTxOptionFunc) (txList []*Tx, err error) { + opt := &getTxOption{} + for _, f := range options { + f(opt) + } + + dbTx := m.DB.Table(m.table).Where("account_index = ?", accountIndex) + if len(opt.Types) > 0 { + dbTx = dbTx.Where("tx_type IN ?", opt.Types) + } + + dbTx = dbTx.Limit(int(limit)).Offset(int(offset)).Order("created_at desc").Find(&txList) if dbTx.Error != nil { return nil, types.DbErrSqlOperation } else if dbTx.RowsAffected == 0 { @@ -137,8 +200,18 @@ func (m *defaultTxModel) GetTxsByAccountIndex(accountIndex int64, limit int64, o return txList, nil } -func (m *defaultTxModel) GetTxsCountByAccountIndex(accountIndex int64) (count int64, err error) { - dbTx := m.DB.Table(m.table).Where("account_index = ?", accountIndex).Count(&count) +func (m *defaultTxModel) GetTxsCountByAccountIndex(accountIndex int64, options ...GetTxOptionFunc) (count int64, err error) { + opt := &getTxOption{} + for _, f := range options { + f(opt) + } + + dbTx := m.DB.Table(m.table).Where("account_index = ?", accountIndex) + if len(opt.Types) > 0 { + dbTx = dbTx.Where("tx_type IN ?", opt.Types) + } + + dbTx = dbTx.Count(&count) if dbTx.Error != nil { return 0, types.DbErrSqlOperation } else if dbTx.RowsAffected == 0 { diff --git a/dao/tx/tx_pool.go b/dao/tx/tx_pool.go index 8d8866a6c..e44bddd68 100644 --- a/dao/tx/tx_pool.go +++ b/dao/tx/tx_pool.go @@ -31,12 +31,12 @@ type ( TxPoolModel interface { CreatePoolTxTable() error DropPoolTxTable() error - GetTxs(limit int64, offset int64) (txs []*Tx, err error) - GetTxsTotalCount() (count int64, err error) + GetTxs(limit int64, offset int64, options ...GetTxOptionFunc) (txs []*Tx, err error) + GetTxsTotalCount(options ...GetTxOptionFunc) (count int64, err error) GetTxByTxHash(hash string) (txs *Tx, err error) GetTxsByStatus(status int) (txs []*Tx, err error) CreateTxs(txs []*Tx) error - GetPendingTxsByAccountIndex(accountIndex int64) (txs []*Tx, err error) + GetPendingTxsByAccountIndex(accountIndex int64, options ...GetTxOptionFunc) (txs []*Tx, err error) GetMaxNonceByAccountIndex(accountIndex int64) (nonce int64, err error) CreateTxsInTransact(tx *gorm.DB, txs []*Tx) error UpdateTxsInTransact(tx *gorm.DB, txs []*Tx) error @@ -73,8 +73,28 @@ func (m *defaultTxPoolModel) DropPoolTxTable() error { return m.DB.Migrator().DropTable(m.table) } -func (m *defaultTxPoolModel) GetTxs(limit int64, offset int64) (txs []*Tx, err error) { - dbTx := m.DB.Table(m.table).Where("tx_status = ?", StatusPending).Limit(int(limit)).Offset(int(offset)).Order("created_at desc, id desc").Find(&txs) +func (m *defaultTxPoolModel) GetTxs(limit int64, offset int64, options ...GetTxOptionFunc) (txs []*Tx, err error) { + opt := &getTxOption{} + for _, f := range options { + f(opt) + } + + dbTx := m.DB.Table(m.table) + subTx := m.DB.Table(m.table) + + if opt.WithDeleted { + dbTx = dbTx.Unscoped() + subTx = subTx.Unscoped() + } + if len(opt.Statuses) > 0 { + dbTx = dbTx.Where("tx_status IN ?", opt.Statuses) + } + if len(opt.FromHash) > 0 { + subTx = subTx.Select("id").Where("tx_hash = ?", opt.FromHash).Limit(1) + dbTx = dbTx.Where("id > (?)", subTx) + } + + dbTx = dbTx.Limit(int(limit)).Offset(int(offset)).Order("created_at desc, id desc").Find(&txs) if dbTx.Error != nil { return nil, types.DbErrSqlOperation } @@ -89,8 +109,28 @@ func (m *defaultTxPoolModel) GetTxsByStatus(status int) (txs []*Tx, err error) { return txs, nil } -func (m *defaultTxPoolModel) GetTxsTotalCount() (count int64, err error) { - dbTx := m.DB.Table(m.table).Where("tx_status = ? and deleted_at is NULL", StatusPending).Count(&count) +func (m *defaultTxPoolModel) GetTxsTotalCount(options ...GetTxOptionFunc) (count int64, err error) { + opt := &getTxOption{} + for _, f := range options { + f(opt) + } + + dbTx := m.DB.Table(m.table) + subTx := m.DB.Table(m.table) + + if opt.WithDeleted { + dbTx = dbTx.Unscoped() + subTx = subTx.Unscoped() + } + if len(opt.Statuses) > 0 { + dbTx = dbTx.Where("tx_status IN ?", opt.Statuses) + } + if len(opt.FromHash) > 0 { + subTx = subTx.Select("id").Where("tx_hash = ?", opt.FromHash).Limit(1) + dbTx = dbTx.Where("id > (?)", subTx) + } + + dbTx = dbTx.Count(&count) if dbTx.Error != nil { return 0, types.DbErrSqlOperation } else if dbTx.RowsAffected == 0 { @@ -122,9 +162,18 @@ func (m *defaultTxPoolModel) CreateTxs(txs []*Tx) error { }) } -func (m *defaultTxPoolModel) GetPendingTxsByAccountIndex(accountIndex int64) (txs []*Tx, err error) { - dbTx := m.DB.Table(m.table).Where("tx_status = ? AND account_index = ?", StatusPending, accountIndex). - Order("created_at, id").Find(&txs) +func (m *defaultTxPoolModel) GetPendingTxsByAccountIndex(accountIndex int64, options ...GetTxOptionFunc) (txs []*Tx, err error) { + opt := &getTxOption{} + for _, f := range options { + f(opt) + } + + dbTx := m.DB.Table(m.table).Where("tx_status = ? AND account_index = ?", StatusPending, accountIndex) + if len(opt.Types) > 0 { + dbTx = dbTx.Where("tx_type IN ?", opt.Types) + } + + dbTx = dbTx.Order("created_at, id").Find(&txs) if dbTx.Error != nil { return nil, types.DbErrSqlOperation } else if dbTx.RowsAffected == 0 { diff --git a/deploy-local.sh b/deploy-local.sh index 5f091d864..9a2c7b500 100644 --- a/deploy-local.sh +++ b/deploy-local.sh @@ -47,7 +47,7 @@ fi echo '3. start verify_parse for ZkBNBVerifier' cd ${DEPLOY_PATH}/zkbnb/service/prover/ -python3 verifier_parse.py ${KEY_PATH}/ZkBNBVerifier1.sol,${KEY_PATH}/ZkBNBVerifier10.sol 1,10 ${DEPLOY_PATH}/zkbnb-contract/contracts/ZkBNBVerifier.sol +python3 verifier_parse.py ${KEY_PATH}/ZkBNBVerifier10.sol 10 ${DEPLOY_PATH}/zkbnb-contract/contracts/ZkBNBVerifier.sol @@ -60,7 +60,7 @@ echo 'latest block number = ' $blockNumber echo '4-2. deploy contracts, register and deposit on BSC Testnet' cd ${DEPLOY_PATH} -cd ./zkbnb-contract && echo "BSC_TESTNET_PRIVATE_KEY=${BSC_TESTNET_PRIVATE_KEY}" > .env && npm install +cd ./zkbnb-contract && echo "BSC_TESTNET_PRIVATE_KEY=${BSC_TESTNET_PRIVATE_KEY}" > .env && yarn install npx hardhat --network BSCTestnet run ./scripts/deploy-keccak256/deploy.js echo 'Recorded latest contract addresses into ${DEPLOY_PATH}/zkbnb-contract/info/addresses.json' @@ -78,23 +78,19 @@ sed -i -e "s/ZkBNBProxy: .*/ZkBNBProxy: ${ZkBNBContractAddr}/" ${DEPLOY_PATH}/zk GovernanceContractAddr=`cat ${DEPLOY_PATH}/zkbnb-contract/info/addresses.json | jq -r '.governance'` sed -i -e "s/Governance: .*/Governance: ${GovernanceContractAddr}/" ${DEPLOY_PATH}/zkbnb/tools/dbinitializer/contractaddr.yaml +BUSDContractAddr=`cat ${DEPLOY_PATH}/zkbnb-contract/info/addresses.json | jq -r '.BUSDToken'` +sed -i -e "s/BUSDToken: .*/BUSDToken: ${BUSDContractAddr}/" ${DEPLOY_PATH}/zkbnb/tools/dbinitializer/contractaddr.yaml -cd ${DEPLOY_PATH}/zkbnb/ -make api-server -cd ${DEPLOY_PATH}/zkbnb && go mod tidy +# cd ${DEPLOY_PATH}/zkbnb/ +# make api-server +cd ${DEPLOY_PATH}/zkbnb && go mod tidy echo "6. init tables on database" go run ./cmd/zkbnb/main.go db initialize --dsn "host=localhost user=postgres password=ZkBNB@123 dbname=zkbnb port=5432 sslmode=disable" --contractAddr ${DEPLOY_PATH}/zkbnb/tools/dbinitializer/contractaddr.yaml - -cd ${DEPLOY_PATH}/zkbnb/ -make api-server - - -sleep 30s - +sleep 10s echo "7. run prover" @@ -108,11 +104,11 @@ CacheRedis: Type: node KeyPath: - ProvingKeyPath: [${KEY_PATH}/zkbnb1.pk, ${KEY_PATH}/zkbnb10.pk] - VerifyingKeyPath: [${KEY_PATH}/zkbnb1.vk, ${KEY_PATH}/zkbnb10.vk] + ProvingKeyPath: [${KEY_PATH}/zkbnb10.pk] + VerifyingKeyPath: [${KEY_PATH}/zkbnb10.vk] BlockConfig: - OptionalBlockSizes: [1, 10] + OptionalBlockSizes: [10] TreeDB: Driver: memorydb @@ -120,7 +116,7 @@ TreeDB: " > ${DEPLOY_PATH}/zkbnb/service/prover/etc/config.yaml echo -e " -go run ./cmd/zkbnb/main.go prover --config ${DEPLOY_PATH}/zkbnb/service/prover/etc/config.yaml +go run ./cmd/zkbnb/main.go prover --config ${DEPLOY_PATH}/zkbnb/service/prover/etc/config.yaml --pprof --pprof.addr 0.0.0.0 --pprof.port 6060 --metrics --metrics.addr 0.0.0.0 --metrics.port 6060 " > run_prover.sh pm2 start --name prover "./run_prover.sh" @@ -146,7 +142,7 @@ TreeDB: " > ${DEPLOY_PATH}/zkbnb/service/witness/etc/config.yaml echo -e " -go run ./cmd/zkbnb/main.go witness --config ${DEPLOY_PATH}/zkbnb/service/witness/etc/config.yaml +go run ./cmd/zkbnb/main.go witness --config ${DEPLOY_PATH}/zkbnb/service/witness/etc/config.yaml --pprof --pprof.addr 0.0.0.0 --pprof.port 6061 --metrics --metrics.addr 0.0.0.0 --metrics.port 6061 " > run_witness.sh pm2 start --name witness "./run_witness.sh" @@ -177,7 +173,7 @@ TreeDB: " > ${DEPLOY_PATH}/zkbnb/service/monitor/etc/config.yaml echo -e " -go run ./cmd/zkbnb/main.go monitor --config ${DEPLOY_PATH}/zkbnb/service/monitor/etc/config.yaml +go run ./cmd/zkbnb/main.go monitor --config ${DEPLOY_PATH}/zkbnb/service/monitor/etc/config.yaml --pprof --pprof.addr 0.0.0.0 --pprof.port 6062 --metrics --metrics.addr 0.0.0.0 --metrics.port 6062 " > run_monitor.sh pm2 start --name monitor "./run_monitor.sh" @@ -195,7 +191,7 @@ CacheRedis: Type: node BlockConfig: - OptionalBlockSizes: [1, 10] + OptionalBlockSizes: [10] TreeDB: Driver: memorydb @@ -203,7 +199,7 @@ TreeDB: " > ${DEPLOY_PATH}/zkbnb/service/committer/etc/config.yaml echo -e " -go run ./cmd/zkbnb/main.go committer --config ${DEPLOY_PATH}/zkbnb/service/committer/etc/config.yaml +go run ./cmd/zkbnb/main.go committer --config ${DEPLOY_PATH}/zkbnb/service/committer/etc/config.yaml --pprof --pprof.addr 0.0.0.0 --pprof.port 6063 --metrics --metrics.addr 0.0.0.0 --metrics.port 6063 " > run_committer.sh pm2 start --name committer "./run_committer.sh" @@ -225,9 +221,9 @@ ChainConfig: #NetworkRPCSysConfigName: "LocalTestNetworkRpc" ConfirmBlocksCount: 0 MaxWaitingTime: 120 - MaxBlockCount: 3 - Sk: "107f9d2a50ce2d8337e0c5220574e9fcf2bf60002da5acf07718f4d531ea3faa" - GasLimit: 20000000 + MaxBlockCount: 4 + Sk: "${BSC_TESTNET_PRIVATE_KEY}" + GasLimit: 5000000 GasPrice: 0 TreeDB: @@ -236,7 +232,7 @@ TreeDB: " > ${DEPLOY_PATH}/zkbnb/service/sender/etc/config.yaml echo -e " -go run ./cmd/zkbnb/main.go sender --config ${DEPLOY_PATH}/zkbnb/service/sender/etc/config.yaml +go run ./cmd/zkbnb/main.go sender --config ${DEPLOY_PATH}/zkbnb/service/sender/etc/config.yaml --pprof --pprof.addr 0.0.0.0 --pprof.port 6064 --metrics --metrics.addr 0.0.0.0 --metrics.port 6064 " > run_sender.sh pm2 start --name sender "./run_sender.sh" @@ -251,13 +247,10 @@ Port: 8888 TxPool: MaxPendingTxCount: 10000 -Prometheus: - Host: 0.0.0.0 - Port: 9091 - Path: /metrics - Postgres: DataSource: host=127.0.0.1 user=postgres password=ZkBNB@123 dbname=zkbnb port=5432 sslmode=disable + MaxConn: 100 + MaxIdle: 10 CacheRedis: - Host: 127.0.0.1:6379 @@ -285,6 +278,6 @@ MemCache: " > ${DEPLOY_PATH}/zkbnb/service/apiserver/etc/config.yaml echo -e " -go run ./cmd/zkbnb/main.go apiserver --config ${DEPLOY_PATH}/zkbnb/service/apiserver/etc/config.yaml +go run ./cmd/zkbnb/main.go apiserver --config ${DEPLOY_PATH}/zkbnb/service/apiserver/etc/config.yaml --pprof --pprof.addr 0.0.0.0 --pprof.port 6065 --metrics --metrics.addr 0.0.0.0 --metrics.port 6065 " > run_apiserver.sh pm2 start --name apiserver "./run_apiserver.sh" diff --git a/deployment/docker-compose/docker-compose.sh b/deployment/docker-compose/docker-compose.sh index 206050781..b62bca7a5 100644 --- a/deployment/docker-compose/docker-compose.sh +++ b/deployment/docker-compose/docker-compose.sh @@ -145,13 +145,10 @@ Port: 8888 TxPool: MaxPendingTxCount: 10000 -Prometheus: - Host: 0.0.0.0 - Port: 9091 - Path: /metrics - Postgres: DataSource: host=database user=$DATABASE_USER password=$DATABASE_PASS dbname=$DATABASE_NAME port=5432 sslmode=disable + MaxConn: 100 + MaxIdle: 10 CacheRedis: - Host: redis:6379 diff --git a/deployment/helm/local-value/values.yaml b/deployment/helm/local-value/values.yaml index b3b948391..308c67bae 100644 --- a/deployment/helm/local-value/values.yaml +++ b/deployment/helm/local-value/values.yaml @@ -90,6 +90,8 @@ keyfileVolume: configs: postgres: DataSource: host=postgresql.postgres.svc.cluster.local user=postgres password=${POSTGRES_PASSWORD} dbname=zkbnb port=5432 sslmode=disable + MaxConn: 100 + MaxIdle: 10 redisCache: - Host: redis-master.redis.svc.cluster.local Pass: ${REDIS_PASSWORD} @@ -104,7 +106,8 @@ configs: gasLimit: 5000000 treedb: Driver: memorydb - SK: acbaa269bd7573ff12361be4b97201aef019776ea13384681d4e5ba6a88367d9 + AssetTreeCacheSize: 512000 + SK: acbaa26******************************a88367d9 cmcToken: cfce503f-fake-fake-fake-bbab5257dac8 cmcURL: https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol= memCache: diff --git a/deployment/helm/zkbnb/templates/configmap.yaml b/deployment/helm/zkbnb/templates/configmap.yaml index 74f55b551..d6fd6754a 100644 --- a/deployment/helm/zkbnb/templates/configmap.yaml +++ b/deployment/helm/zkbnb/templates/configmap.yaml @@ -81,12 +81,7 @@ data: Port: 8888 TxPool: - MaxPendingTxCount: 10000 - - Prometheus: - Host: 0.0.0.0 - Port: 9091 - Path: /metrics + MaxPendingTxCount: 10000 Postgres: {{- toYaml .Values.configs.postgres | nindent 6 }} diff --git a/deployment/helm/zkbnb/values.yaml b/deployment/helm/zkbnb/values.yaml index 3e2c9573d..e4ae8210b 100644 --- a/deployment/helm/zkbnb/values.yaml +++ b/deployment/helm/zkbnb/values.yaml @@ -90,6 +90,8 @@ keyfileVolume: configs: postgres: DataSource: host=postgres user=postgres password=*** dbname=zkbnb port=5432 sslmode=disable + MaxConn: 100 + MaxIdle: 10 redisCache: - Host: redis Type: node @@ -103,6 +105,7 @@ configs: gasLimit: 5000000 treedb: Driver: memorydb + AssetTreeCacheSize: 512000 SK: "" cmcToken: "" cmcURL: "" diff --git a/deployment/tool/tool.sh b/deployment/tool/tool.sh index e69c0b7b5..f8048b1d0 100644 --- a/deployment/tool/tool.sh +++ b/deployment/tool/tool.sh @@ -47,8 +47,8 @@ function getLatestBlockHeight() { function deployContracts() { echo 'deploy contracts, register and deposit on BSC Testnet' - cd ${WORKDIR}/dependency/zkbnb-contract && npm install - cp /server/.env ./ + cd ${WORKDIR}/dependency/zkbnb-contract && echo "BSC_TESTNET_PRIVATE_KEY=${BSC_TESTNET_PRIVATE_KEY}" > .env && npm install + npx hardhat --network BSCTestnet run ./scripts/deploy-keccak256/deploy.js echo "Recorded latest contract addresses into ${WORKDIR}/dependency/zkbnb-contract/info/addresses.json" npx hardhat --network BSCTestnet run ./scripts/deploy-keccak256/register.js @@ -63,6 +63,9 @@ function deployContracts() { GovernanceContractAddr=`cat ${WORKDIR}/dependency/zkbnb-contract/info/addresses.json | jq -r '.governance'` sed -i -e "s/Governance: .*/Governance: ${GovernanceContractAddr}/" ${WORKDIR}/configs/contractaddr.yaml + + BUSDContractAddr=`cat ${WORKDIR}/dependency/zkbnb-contract/info/addresses.json | jq -r '.BUSDToken'` + sed -i -e "s/BUSDToken: .*/BUSDToken: ${BUSDContractAddr}/" ${WORKDIR}/configs/contractaddr.yaml } CMD=$1 diff --git a/docs/api_reference.md b/docs/api_reference.md index b89ce7a2b..171259089 100644 --- a/docs/api_reference.md +++ b/docs/api_reference.md @@ -344,6 +344,29 @@ Get pending transactions | ---- | ----------- | ------ | | 200 | A successful response. | [Txs](#txs) | +### /api/v1/executedTxs + +#### GET + +##### Summary + +Get executed transactions + +##### Parameters + +| Name | Located in | Description | Required | Schema | +| ---- | ---------- | ----------- | -------- | ---- | +| offset | query | offset, min 0 and max 100000 | Yes | integer | +| limit | query | limit, min 1 and max 100 | Yes | integer | +| from_hash | query | start from the hash tx | No | string | + +##### Responses + +| Code | Description | Schema | +| ---- | ----------- | ------ | +| 200 | A successful response. | [Txs](#txs) | + + ### /api/v1/nextNonce #### GET diff --git a/go.mod b/go.mod index a199bc164..0719d2dc7 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,11 @@ module github.com/bnb-chain/zkbnb go 1.17 require ( + github.com/bnb-chain/zkbnb-go-sdk v1.0.4-0.20221012063144-3a6e84095b4d github.com/dgraph-io/ristretto v0.1.0 - github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d + github.com/hashicorp/golang-lru v0.5.5-0.20221011183528-d4900dc688bf github.com/panjf2000/ants/v2 v2.5.0 - github.com/prometheus/client_golang v1.12.2 + github.com/prometheus/client_golang v1.13.0 github.com/zeromicro/go-zero v1.3.4 gorm.io/gorm v1.23.4 ) @@ -57,8 +58,8 @@ require ( github.com/pelletier/go-toml/v2 v2.0.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.33.0 // indirect - github.com/prometheus/procfs v0.7.3 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect github.com/rjeczalik/notify v0.9.1 // indirect github.com/rs/zerolog v1.26.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect @@ -80,7 +81,6 @@ require ( go.uber.org/automaxprocs v1.5.1 // indirect golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 // indirect golang.org/x/text v0.3.7 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect @@ -90,9 +90,9 @@ require ( ) require ( - github.com/bnb-chain/zkbnb-crypto v0.0.7 + github.com/bnb-chain/zkbnb-crypto v0.0.8 github.com/bnb-chain/zkbnb-eth-rpc v0.0.2 - github.com/bnb-chain/zkbnb-smt v0.0.2-0.20220919093003-13980385d38f + github.com/bnb-chain/zkbnb-smt v0.0.2 github.com/consensys/gnark v0.7.0 github.com/consensys/gnark-crypto v0.7.0 github.com/eko/gocache/v2 v2.3.1 @@ -106,9 +106,9 @@ require ( github.com/yusufpapurcu/wmi v1.2.2 // indirect golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect - golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect + golang.org/x/sys v0.0.0-20220927170352-d9d178bc13c6 // indirect google.golang.org/grpc v1.46.2 // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/protobuf v1.28.1 // indirect gorm.io/driver/postgres v1.3.6 k8s.io/apimachinery v0.24.1 // indirect ) diff --git a/go.sum b/go.sum index b1f536444..89ac72fa6 100644 --- a/go.sum +++ b/go.sum @@ -117,12 +117,15 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= -github.com/bnb-chain/zkbnb-crypto v0.0.7 h1:IckShzkNdx/ee461Gd0kMC04phus8fcRdIeuzSz1PDE= -github.com/bnb-chain/zkbnb-crypto v0.0.7/go.mod h1:L1BEYSG945hwjJCtR5LUQ1pvRmydSsk9oU2d9YvoOrA= +github.com/bnb-chain/zkbnb-crypto v0.0.6-0.20220928090246-9a20106a2347/go.mod h1:L1BEYSG945hwjJCtR5LUQ1pvRmydSsk9oU2d9YvoOrA= +github.com/bnb-chain/zkbnb-crypto v0.0.8 h1:DZfq/HtCA91vaKZQNRPKuvB9Xk00M8BFw1Nkpf3Q2V0= +github.com/bnb-chain/zkbnb-crypto v0.0.8/go.mod h1:TSIDCYcRZCCFnx/VOXFd9jb9EDnn4xRxfERK1zGwrrU= github.com/bnb-chain/zkbnb-eth-rpc v0.0.2 h1:1rMa8XpplDNZaxeM1ifXMjSSeH/ucJyLUjHHEw1z4AA= github.com/bnb-chain/zkbnb-eth-rpc v0.0.2/go.mod h1:T69T8enicQ5kSRPIzyPJv/jhuvRMz1UxsijPXmlis+I= -github.com/bnb-chain/zkbnb-smt v0.0.2-0.20220919093003-13980385d38f h1:zBVWOWlH4w18O6wp0gZML4U2n1rxsFLb7KB7DFE8zcQ= -github.com/bnb-chain/zkbnb-smt v0.0.2-0.20220919093003-13980385d38f/go.mod h1:mGIAve72dt/VOVQ2wu7UjcjnMspnKczi5QACE1NGVec= +github.com/bnb-chain/zkbnb-go-sdk v1.0.4-0.20221012063144-3a6e84095b4d h1:2+S4/JdKoSPSJr3D7Z5I/Qmv8TkgVYB73vRP0OOunRY= +github.com/bnb-chain/zkbnb-go-sdk v1.0.4-0.20221012063144-3a6e84095b4d/go.mod h1:G27Jpl0yB6T/NKXw31bD2B2M91my0PM6SUEpbEGWGL8= +github.com/bnb-chain/zkbnb-smt v0.0.2 h1:r/iKBTQAVWdpzHVcYEGAkjwpzUiXgaZpegnTjw70JVE= +github.com/bnb-chain/zkbnb-smt v0.0.2/go.mod h1:QmQTsv2fxBsobFLFZkaiNmr3P0M/mm3W1AiwSU3/cGY= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw= github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA= @@ -386,8 +389,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -454,8 +458,9 @@ github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru v0.5.5-0.20221011183528-d4900dc688bf h1:BQyif+/dqmbIGXyGhe5bDx/3grIchislVu5pK7j/bMQ= +github.com/hashicorp/golang-lru v0.5.5-0.20221011183528-d4900dc688bf/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= @@ -745,8 +750,9 @@ github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34= github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= +github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -758,14 +764,16 @@ github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.33.0 h1:rHgav/0a6+uYgGdNt3jwz8FNSesO/Hsang3O0T9A5SE= github.com/prometheus/common v0.33.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= @@ -1107,6 +1115,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1199,9 +1208,8 @@ golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220429233432-b5fbb4746d32/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220927170352-d9d178bc13c6 h1:cy1ko5847T/lJ45eyg/7uLprIE/amW5IXxGtEnQdYMI= golang.org/x/sys v0.0.0-20220927170352-d9d178bc13c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM= -golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -1301,8 +1309,6 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.6.0/go.mod h1:9mxDZsDKxgMAuccQkewq682L+0eCu4dCN2yonUJTCLU= @@ -1422,8 +1428,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/service/apiserver/etc/server-api.yaml.example b/service/apiserver/etc/server-api.yaml.example index 5686bb1ee..2ea2c32ce 100644 --- a/service/apiserver/etc/server-api.yaml.example +++ b/service/apiserver/etc/server-api.yaml.example @@ -5,11 +5,6 @@ Port: 8888 TxPool: MaxPendingTxCount: 10000 -Prometheus: - Host: 0.0.0.0 - Port: 9091 - Path: /metrics - Postgres: DataSource: host=127.0.0.1 user=postgres password=pw dbname=zkbnb port=5432 sslmode=disable diff --git a/service/apiserver/internal/config/config.go b/service/apiserver/internal/config/config.go index bcc6315ba..edfe5fef3 100644 --- a/service/apiserver/internal/config/config.go +++ b/service/apiserver/internal/config/config.go @@ -10,6 +10,8 @@ type Config struct { rest.RestConf Postgres struct { DataSource string + MaxIdle int + MaxConn int } TxPool struct { MaxPendingTxCount int diff --git a/service/apiserver/internal/handler/routes.go b/service/apiserver/internal/handler/routes.go index 393256772..a241f1d8c 100644 --- a/service/apiserver/internal/handler/routes.go +++ b/service/apiserver/internal/handler/routes.go @@ -134,6 +134,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) { Path: "/api/v1/pendingTxs", Handler: transaction.GetPendingTxsHandler(serverCtx), }, + { + Method: http.MethodGet, + Path: "/api/v1/executedTxs", + Handler: transaction.GetExecutedTxsHandler(serverCtx), + }, { Method: http.MethodGet, Path: "/api/v1/accountPendingTxs", diff --git a/service/apiserver/internal/handler/transaction/getexecutedtxshandler.go b/service/apiserver/internal/handler/transaction/getexecutedtxshandler.go new file mode 100644 index 000000000..995a9a260 --- /dev/null +++ b/service/apiserver/internal/handler/transaction/getexecutedtxshandler.go @@ -0,0 +1,29 @@ +package transaction + +import ( + "net/http" + + "github.com/zeromicro/go-zero/rest/httpx" + + "github.com/bnb-chain/zkbnb/service/apiserver/internal/logic/transaction" + "github.com/bnb-chain/zkbnb/service/apiserver/internal/svc" + "github.com/bnb-chain/zkbnb/service/apiserver/internal/types" +) + +func GetExecutedTxsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var req types.ReqGetRangeWithFromHash + if err := httpx.Parse(r, &req); err != nil { + httpx.Error(w, err) + return + } + + l := transaction.NewGetExecutedTxsLogic(r.Context(), svcCtx) + resp, err := l.GetExecutedTxs(&req) + if err != nil { + httpx.Error(w, err) + } else { + httpx.OkJson(w, resp) + } + } +} diff --git a/service/apiserver/internal/logic/account/getaccountlogic.go b/service/apiserver/internal/logic/account/getaccountlogic.go index f953042d5..9ef78773c 100644 --- a/service/apiserver/internal/logic/account/getaccountlogic.go +++ b/service/apiserver/internal/logic/account/getaccountlogic.go @@ -39,7 +39,7 @@ func (l *GetAccountLogic) GetAccount(req *types.ReqGetAccount) (resp *types.Acco case queryByIndex: index, err = strconv.ParseInt(req.Value, 10, 64) if err != nil || index < 0 { - return nil, types2.AppErrInvalidParam.RefineError("invalid value for account index") + return nil, types2.AppErrInvalidAccountIndex } case queryByName: index, err = l.svcCtx.MemCache.GetAccountIndexByName(req.Value) @@ -51,7 +51,7 @@ func (l *GetAccountLogic) GetAccount(req *types.ReqGetAccount) (resp *types.Acco if err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrAccountNotFound } return nil, types2.AppErrInternal } @@ -59,7 +59,7 @@ func (l *GetAccountLogic) GetAccount(req *types.ReqGetAccount) (resp *types.Acco account, err := l.svcCtx.StateFetcher.GetLatestAccount(index) if err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrAccountNotFound } return nil, types2.AppErrInternal } diff --git a/service/apiserver/internal/logic/asset/getassetlogic.go b/service/apiserver/internal/logic/asset/getassetlogic.go index b903860d3..57fcfb08a 100644 --- a/service/apiserver/internal/logic/asset/getassetlogic.go +++ b/service/apiserver/internal/logic/asset/getassetlogic.go @@ -41,12 +41,12 @@ func (l *GetAssetLogic) GetAsset(req *types.ReqGetAsset) (resp *types.Asset, err case queryById: id, err := strconv.ParseInt(req.Value, 10, 64) if err != nil || id < 0 { - return nil, types2.AppErrInvalidParam.RefineError("invalid value for asset id") + return nil, types2.AppErrInvalidAssetId } symbol, err = l.svcCtx.MemCache.GetAssetSymbolById(id) if err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrAssetNotFound } return nil, types2.AppErrInternal } @@ -61,7 +61,7 @@ func (l *GetAssetLogic) GetAsset(req *types.ReqGetAsset) (resp *types.Asset, err }) if err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrAssetNotFound } return nil, types2.AppErrInternal } diff --git a/service/apiserver/internal/logic/block/getblocklogic.go b/service/apiserver/internal/logic/block/getblocklogic.go index 8b53238f8..0d8e2fa99 100644 --- a/service/apiserver/internal/logic/block/getblocklogic.go +++ b/service/apiserver/internal/logic/block/getblocklogic.go @@ -39,7 +39,7 @@ func (l *GetBlockLogic) GetBlock(req *types.ReqGetBlock) (resp *types.Block, err var height int64 height, err = strconv.ParseInt(req.Value, 10, 64) if err != nil || height < 0 { - return nil, types2.AppErrInvalidParam.RefineError("invalid value for block height") + return nil, types2.AppErrInvalidBlockHeight } block, err = l.svcCtx.MemCache.GetBlockByHeightWithFallback(height, func() (interface{}, error) { return l.svcCtx.BlockModel.GetBlockByHeight(height) @@ -51,10 +51,9 @@ func (l *GetBlockLogic) GetBlock(req *types.ReqGetBlock) (resp *types.Block, err default: return nil, types2.AppErrInvalidParam.RefineError("param by should be height|commitment") } - if err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrBlockNotFound } return nil, types2.AppErrInternal } diff --git a/service/apiserver/internal/logic/info/searchlogic.go b/service/apiserver/internal/logic/info/searchlogic.go index ec7554182..958c80c25 100644 --- a/service/apiserver/internal/logic/info/searchlogic.go +++ b/service/apiserver/internal/logic/info/searchlogic.go @@ -32,7 +32,7 @@ func (l *SearchLogic) Search(req *types.ReqSearch) (*types.Search, error) { if err == nil { if _, err = l.svcCtx.BlockModel.GetBlockByHeight(blockHeight); err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrBlockNotFound } return nil, types2.AppErrInternal } @@ -43,7 +43,7 @@ func (l *SearchLogic) Search(req *types.ReqSearch) (*types.Search, error) { if strings.Contains(req.Keyword, ".") { if _, err = l.svcCtx.MemCache.GetAccountIndexByName(req.Keyword); err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrAccountNotFound } return nil, types2.AppErrInternal } diff --git a/service/apiserver/internal/logic/nft/getaccountnftslogic.go b/service/apiserver/internal/logic/nft/getaccountnftslogic.go index 30ffca726..b8e1d0cb4 100644 --- a/service/apiserver/internal/logic/nft/getaccountnftslogic.go +++ b/service/apiserver/internal/logic/nft/getaccountnftslogic.go @@ -41,7 +41,7 @@ func (l *GetAccountNftsLogic) GetAccountNfts(req *types.ReqGetAccountNfts) (resp case queryByAccountIndex: accountIndex, err = strconv.ParseInt(req.Value, 10, 64) if err != nil || accountIndex < 0 { - return nil, types2.AppErrInvalidParam.RefineError("invalid value for account_index") + return nil, types2.AppErrInvalidAccountIndex } case queryByAccountName: accountIndex, err = l.svcCtx.MemCache.GetAccountIndexByName(req.Value) diff --git a/service/apiserver/internal/logic/nft/getmaxofferidlogic.go b/service/apiserver/internal/logic/nft/getmaxofferidlogic.go index 65146477e..5bad6d139 100644 --- a/service/apiserver/internal/logic/nft/getmaxofferidlogic.go +++ b/service/apiserver/internal/logic/nft/getmaxofferidlogic.go @@ -29,7 +29,7 @@ func (l *GetMaxOfferIdLogic) GetMaxOfferId(req *types.ReqGetMaxOfferId) (resp *t account, err := l.svcCtx.StateFetcher.GetLatestAccount(int64(req.AccountIndex)) if err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrAccountNotFound } return nil, types2.AppErrInternal } diff --git a/service/apiserver/internal/logic/transaction/getaccountpendingtxslogic.go b/service/apiserver/internal/logic/transaction/getaccountpendingtxslogic.go index 2d9aa7e21..713496624 100644 --- a/service/apiserver/internal/logic/transaction/getaccountpendingtxslogic.go +++ b/service/apiserver/internal/logic/transaction/getaccountpendingtxslogic.go @@ -6,6 +6,7 @@ import ( "github.com/zeromicro/go-zero/core/logx" + "github.com/bnb-chain/zkbnb/dao/tx" "github.com/bnb-chain/zkbnb/service/apiserver/internal/logic/utils" "github.com/bnb-chain/zkbnb/service/apiserver/internal/svc" "github.com/bnb-chain/zkbnb/service/apiserver/internal/types" @@ -36,7 +37,7 @@ func (l *GetAccountPendingTxsLogic) GetAccountPendingTxs(req *types.ReqGetAccoun case queryByAccountIndex: accountIndex, err = strconv.ParseInt(req.Value, 10, 64) if err != nil || accountIndex < 0 { - return nil, types2.AppErrInvalidParam.RefineError("invalid value for account_index") + return nil, types2.AppErrInvalidAccountIndex } case queryByAccountName: accountIndex, err = l.svcCtx.MemCache.GetAccountIndexByName(req.Value) @@ -53,7 +54,12 @@ func (l *GetAccountPendingTxsLogic) GetAccountPendingTxs(req *types.ReqGetAccoun return nil, types2.AppErrInternal } - poolTxs, err := l.svcCtx.TxPoolModel.GetPendingTxsByAccountIndex(accountIndex) + options := []tx.GetTxOptionFunc{} + if len(req.Types) > 0 { + options = append(options, tx.GetTxWithTypes(req.Types)) + } + + poolTxs, err := l.svcCtx.TxPoolModel.GetPendingTxsByAccountIndex(accountIndex, options...) if err != nil { if err != types2.DbErrNotFound { return nil, types2.AppErrInternal @@ -65,6 +71,9 @@ func (l *GetAccountPendingTxsLogic) GetAccountPendingTxs(req *types.ReqGetAccoun tx := utils.ConvertTx(poolTx) tx.AccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.AccountIndex) tx.AssetName, _ = l.svcCtx.MemCache.GetAssetNameById(tx.AssetId) + if tx.ToAccountIndex >= 0 { + tx.ToAccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.ToAccountIndex) + } resp.Txs = append(resp.Txs, tx) } return resp, nil diff --git a/service/apiserver/internal/logic/transaction/getaccounttxslogic.go b/service/apiserver/internal/logic/transaction/getaccounttxslogic.go index aed5d2b5a..2e3f2f603 100644 --- a/service/apiserver/internal/logic/transaction/getaccounttxslogic.go +++ b/service/apiserver/internal/logic/transaction/getaccounttxslogic.go @@ -6,6 +6,7 @@ import ( "github.com/zeromicro/go-zero/core/logx" + "github.com/bnb-chain/zkbnb/dao/tx" "github.com/bnb-chain/zkbnb/service/apiserver/internal/logic/utils" "github.com/bnb-chain/zkbnb/service/apiserver/internal/svc" "github.com/bnb-chain/zkbnb/service/apiserver/internal/types" @@ -42,7 +43,7 @@ func (l *GetAccountTxsLogic) GetAccountTxs(req *types.ReqGetAccountTxs) (resp *t case queryByAccountIndex: accountIndex, err = strconv.ParseInt(req.Value, 10, 64) if err != nil || accountIndex < 0 { - return nil, types2.AppErrInvalidParam.RefineError("invalid value for account_index") + return nil, types2.AppErrInvalidAccountIndex } case queryByAccountName: accountIndex, err = l.svcCtx.MemCache.GetAccountIndexByName(req.Value) @@ -59,11 +60,14 @@ func (l *GetAccountTxsLogic) GetAccountTxs(req *types.ReqGetAccountTxs) (resp *t return nil, types2.AppErrInternal } - total, err := l.svcCtx.TxModel.GetTxsCountByAccountIndex(accountIndex) + options := []tx.GetTxOptionFunc{} + if len(req.Types) > 0 { + options = append(options, tx.GetTxWithTypes(req.Types)) + } + + total, err := l.svcCtx.TxModel.GetTxsCountByAccountIndex(accountIndex, options...) if err != nil { - if err != types2.DbErrNotFound { - return nil, types2.AppErrInternal - } + return nil, types2.AppErrInternal } resp.Total = uint32(total) @@ -71,7 +75,7 @@ func (l *GetAccountTxsLogic) GetAccountTxs(req *types.ReqGetAccountTxs) (resp *t return resp, nil } - txs, err := l.svcCtx.TxModel.GetTxsByAccountIndex(accountIndex, int64(req.Limit), int64(req.Offset)) + txs, err := l.svcCtx.TxModel.GetTxsByAccountIndex(accountIndex, int64(req.Limit), int64(req.Offset), options...) if err != nil { return nil, types2.AppErrInternal } @@ -80,6 +84,9 @@ func (l *GetAccountTxsLogic) GetAccountTxs(req *types.ReqGetAccountTxs) (resp *t tx := utils.ConvertTx(dbTx) tx.AccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.AccountIndex) tx.AssetName, _ = l.svcCtx.MemCache.GetAssetNameById(tx.AssetId) + if tx.ToAccountIndex >= 0 { + tx.ToAccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.ToAccountIndex) + } resp.Txs = append(resp.Txs, tx) } return resp, nil diff --git a/service/apiserver/internal/logic/transaction/getblocktxslogic.go b/service/apiserver/internal/logic/transaction/getblocktxslogic.go index 7ad8e53c8..1dba66b2d 100644 --- a/service/apiserver/internal/logic/transaction/getblocktxslogic.go +++ b/service/apiserver/internal/logic/transaction/getblocktxslogic.go @@ -43,7 +43,7 @@ func (l *GetBlockTxsLogic) GetBlockTxs(req *types.ReqGetBlockTxs) (resp *types.T height := int64(0) height, err = strconv.ParseInt(req.Value, 10, 64) if err != nil || height < 0 { - return nil, types2.AppErrInvalidParam.RefineError("invalid value for block height") + return nil, types2.AppErrInvalidBlockHeight } block, err = l.svcCtx.MemCache.GetBlockByHeightWithFallback(height, func() (interface{}, error) { return l.svcCtx.BlockModel.GetBlockByHeight(height) @@ -68,6 +68,9 @@ func (l *GetBlockTxsLogic) GetBlockTxs(req *types.ReqGetBlockTxs) (resp *types.T tx := utils.ConvertTx(dbTx) tx.AccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.AccountIndex) tx.AssetName, _ = l.svcCtx.MemCache.GetAssetNameById(tx.AssetId) + if tx.ToAccountIndex >= 0 { + tx.ToAccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.ToAccountIndex) + } resp.Txs = append(resp.Txs, tx) } return resp, nil diff --git a/service/apiserver/internal/logic/transaction/getexecutedtxslogic.go b/service/apiserver/internal/logic/transaction/getexecutedtxslogic.go new file mode 100644 index 000000000..07d327122 --- /dev/null +++ b/service/apiserver/internal/logic/transaction/getexecutedtxslogic.go @@ -0,0 +1,68 @@ +package transaction + +import ( + "context" + + "github.com/zeromicro/go-zero/core/logx" + + "github.com/bnb-chain/zkbnb/dao/tx" + "github.com/bnb-chain/zkbnb/service/apiserver/internal/logic/utils" + "github.com/bnb-chain/zkbnb/service/apiserver/internal/svc" + "github.com/bnb-chain/zkbnb/service/apiserver/internal/types" + types2 "github.com/bnb-chain/zkbnb/types" +) + +type GetExecutedTxsLogic struct { + logx.Logger + ctx context.Context + svcCtx *svc.ServiceContext +} + +func NewGetExecutedTxsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetExecutedTxsLogic { + return &GetExecutedTxsLogic{ + Logger: logx.WithContext(ctx), + ctx: ctx, + svcCtx: svcCtx, + } +} + +func (l *GetExecutedTxsLogic) GetExecutedTxs(req *types.ReqGetRangeWithFromHash) (*types.Txs, error) { + + options := []tx.GetTxOptionFunc{ + tx.GetTxWithStatuses([]int64{tx.StatusExecuted}), + tx.GetTxWithDeleted(), + } + if len(req.FromHash) > 0 { + options = append(options, tx.GetTxWithFromHash(req.FromHash)) + } + + total, err := l.svcCtx.TxPoolModel.GetTxsTotalCount(options...) + if err != nil { + if err != types2.DbErrNotFound { + return nil, types2.AppErrInternal + } + } + + resp := &types.Txs{ + Txs: make([]*types.Tx, 0), + Total: uint32(total), + } + if total == 0 { + return resp, nil + } + + pendingTxs, err := l.svcCtx.TxPoolModel.GetTxs(int64(req.Limit), int64(req.Offset), options...) + if err != nil { + return nil, types2.AppErrInternal + } + for _, pendingTx := range pendingTxs { + tx := utils.ConvertTx(pendingTx) + tx.AccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.AccountIndex) + tx.AssetName, _ = l.svcCtx.MemCache.GetAssetNameById(tx.AssetId) + if tx.ToAccountIndex >= 0 { + tx.ToAccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.ToAccountIndex) + } + resp.Txs = append(resp.Txs, tx) + } + return resp, nil +} diff --git a/service/apiserver/internal/logic/transaction/getnextnoncelogic.go b/service/apiserver/internal/logic/transaction/getnextnoncelogic.go index 0f1e35f31..e91608fac 100644 --- a/service/apiserver/internal/logic/transaction/getnextnoncelogic.go +++ b/service/apiserver/internal/logic/transaction/getnextnoncelogic.go @@ -34,7 +34,7 @@ func (l *GetNextNonceLogic) GetNextNonce(req *types.ReqGetNextNonce) (*types.Nex nonce, err := bc.StateDB().GetPendingNonce(int64(req.AccountIndex)) if err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrAccountNonceNotFound } return nil, types2.AppErrInternal } diff --git a/service/apiserver/internal/logic/transaction/getpendingtxslogic.go b/service/apiserver/internal/logic/transaction/getpendingtxslogic.go index c8cff4d1e..6b4135da2 100644 --- a/service/apiserver/internal/logic/transaction/getpendingtxslogic.go +++ b/service/apiserver/internal/logic/transaction/getpendingtxslogic.go @@ -5,6 +5,7 @@ import ( "github.com/zeromicro/go-zero/core/logx" + "github.com/bnb-chain/zkbnb/dao/tx" "github.com/bnb-chain/zkbnb/service/apiserver/internal/logic/utils" "github.com/bnb-chain/zkbnb/service/apiserver/internal/svc" "github.com/bnb-chain/zkbnb/service/apiserver/internal/types" @@ -24,8 +25,12 @@ func NewGetPendingTxsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get svcCtx: svcCtx, } } + func (l *GetPendingTxsLogic) GetPendingTxs(req *types.ReqGetRange) (*types.Txs, error) { - total, err := l.svcCtx.TxPoolModel.GetTxsTotalCount() + + txStatuses := []int64{tx.StatusPending} + + total, err := l.svcCtx.TxPoolModel.GetTxsTotalCount(tx.GetTxWithStatuses(txStatuses)) if err != nil { if err != types2.DbErrNotFound { return nil, types2.AppErrInternal @@ -40,7 +45,7 @@ func (l *GetPendingTxsLogic) GetPendingTxs(req *types.ReqGetRange) (*types.Txs, return resp, nil } - pendingTxs, err := l.svcCtx.TxPoolModel.GetTxs(int64(req.Limit), int64(req.Offset)) + pendingTxs, err := l.svcCtx.TxPoolModel.GetTxs(int64(req.Limit), int64(req.Offset), tx.GetTxWithStatuses(txStatuses)) if err != nil { return nil, types2.AppErrInternal } @@ -48,6 +53,9 @@ func (l *GetPendingTxsLogic) GetPendingTxs(req *types.ReqGetRange) (*types.Txs, tx := utils.ConvertTx(pendingTx) tx.AccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.AccountIndex) tx.AssetName, _ = l.svcCtx.MemCache.GetAssetNameById(tx.AssetId) + if tx.ToAccountIndex >= 0 { + tx.ToAccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.ToAccountIndex) + } resp.Txs = append(resp.Txs, tx) } return resp, nil diff --git a/service/apiserver/internal/logic/transaction/gettxlogic.go b/service/apiserver/internal/logic/transaction/gettxlogic.go index 190791aa9..c4e202116 100644 --- a/service/apiserver/internal/logic/transaction/gettxlogic.go +++ b/service/apiserver/internal/logic/transaction/gettxlogic.go @@ -34,6 +34,9 @@ func (l *GetTxLogic) GetTx(req *types.ReqGetTx) (resp *types.EnrichedTx, err err resp.Tx = *utils.ConvertTx(tx) resp.Tx.AccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.AccountIndex) resp.Tx.AssetName, _ = l.svcCtx.MemCache.GetAssetNameById(tx.AssetId) + if resp.Tx.ToAccountIndex >= 0 { + resp.Tx.ToAccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(resp.Tx.ToAccountIndex) + } block, err := l.svcCtx.MemCache.GetBlockByHeightWithFallback(tx.BlockHeight, func() (interface{}, error) { return l.svcCtx.BlockModel.GetBlockByHeight(resp.Tx.BlockHeight) }) @@ -49,13 +52,16 @@ func (l *GetTxLogic) GetTx(req *types.ReqGetTx) (resp *types.EnrichedTx, err err poolTx, err := l.svcCtx.TxPoolModel.GetTxByTxHash(req.Hash) if err != nil { if err == types2.DbErrNotFound { - return nil, types2.AppErrNotFound + return nil, types2.AppErrPoolTxNotFound } return nil, types2.AppErrInternal } resp.Tx = *utils.ConvertTx(poolTx) resp.Tx.AccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(poolTx.AccountIndex) resp.Tx.AssetName, _ = l.svcCtx.MemCache.GetAssetNameById(poolTx.AssetId) + if resp.Tx.ToAccountIndex >= 0 { + resp.Tx.ToAccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(resp.Tx.ToAccountIndex) + } } return resp, nil diff --git a/service/apiserver/internal/logic/transaction/gettxslogic.go b/service/apiserver/internal/logic/transaction/gettxslogic.go index cbc856b68..43c553eb6 100644 --- a/service/apiserver/internal/logic/transaction/gettxslogic.go +++ b/service/apiserver/internal/logic/transaction/gettxslogic.go @@ -49,6 +49,9 @@ func (l *GetTxsLogic) GetTxs(req *types.ReqGetRange) (resp *types.Txs, err error tx := utils.ConvertTx(dbTx) tx.AccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.AccountIndex) tx.AssetName, _ = l.svcCtx.MemCache.GetAssetNameById(tx.AssetId) + if tx.ToAccountIndex >= 0 { + tx.ToAccountName, _ = l.svcCtx.MemCache.GetAccountNameByIndex(tx.ToAccountIndex) + } resp.Txs = append(resp.Txs, tx) } diff --git a/service/apiserver/internal/logic/utils/converter.go b/service/apiserver/internal/logic/utils/converter.go index fab43158d..9e413a072 100644 --- a/service/apiserver/internal/logic/utils/converter.go +++ b/service/apiserver/internal/logic/utils/converter.go @@ -1,30 +1,60 @@ package utils import ( + "github.com/zeromicro/go-zero/core/logx" + "github.com/bnb-chain/zkbnb/dao/tx" "github.com/bnb-chain/zkbnb/service/apiserver/internal/types" + types2 "github.com/bnb-chain/zkbnb/types" ) func ConvertTx(tx *tx.Tx) *types.Tx { + toAccountIndex := int64(-1) + + switch tx.TxType { + case types2.TxTypeMintNft: + txInfo, err := types2.ParseMintNftTxInfo(tx.TxInfo) + if err != nil { + logx.Errorf("parse mintNft tx failed: %s", err.Error()) + } else { + toAccountIndex = txInfo.ToAccountIndex + } + case types2.TxTypeTransfer: + txInfo, err := types2.ParseTransferTxInfo(tx.TxInfo) + if err != nil { + logx.Errorf("parse transfer tx failed: %s", err.Error()) + } else { + toAccountIndex = txInfo.ToAccountIndex + } + case types2.TxTypeTransferNft: + txInfo, err := types2.ParseTransferNftTxInfo(tx.TxInfo) + if err != nil { + logx.Errorf("parse transferNft tx failed: %s", err.Error()) + } else { + toAccountIndex = txInfo.ToAccountIndex + } + } + return &types.Tx{ - Hash: tx.TxHash, - Type: tx.TxType, - GasFee: tx.GasFee, - GasFeeAssetId: tx.GasFeeAssetId, - Status: int64(tx.TxStatus), - Index: tx.TxIndex, - BlockHeight: tx.BlockHeight, - NftIndex: tx.NftIndex, - CollectionId: tx.CollectionId, - AssetId: tx.AssetId, - Amount: tx.TxAmount, - NativeAddress: tx.NativeAddress, - Info: tx.TxInfo, - ExtraInfo: tx.ExtraInfo, - Memo: tx.Memo, - AccountIndex: tx.AccountIndex, - Nonce: tx.Nonce, - ExpiredAt: tx.ExpiredAt, - CreatedAt: tx.CreatedAt.Unix(), + Hash: tx.TxHash, + Type: tx.TxType, + GasFee: tx.GasFee, + GasFeeAssetId: tx.GasFeeAssetId, + Status: int64(tx.TxStatus), + Index: tx.TxIndex, + BlockHeight: tx.BlockHeight, + NftIndex: tx.NftIndex, + CollectionId: tx.CollectionId, + AssetId: tx.AssetId, + Amount: tx.TxAmount, + NativeAddress: tx.NativeAddress, + Info: tx.TxInfo, + ExtraInfo: tx.ExtraInfo, + Memo: tx.Memo, + AccountIndex: tx.AccountIndex, + Nonce: tx.Nonce, + ExpiredAt: tx.ExpiredAt, + CreatedAt: tx.CreatedAt.Unix(), + ToAccountIndex: toAccountIndex, } } diff --git a/service/apiserver/internal/svc/servicecontext.go b/service/apiserver/internal/svc/servicecontext.go index 4f327d548..432c88a2c 100644 --- a/service/apiserver/internal/svc/servicecontext.go +++ b/service/apiserver/internal/svc/servicecontext.go @@ -44,6 +44,14 @@ func NewServiceContext(c config.Config) *ServiceContext { if err != nil { logx.Must(err) } + + rawDB, err := db.DB() + if err != nil { + logx.Must(err) + } + rawDB.SetMaxOpenConns(c.Postgres.MaxConn) + rawDB.SetMaxIdleConns(c.Postgres.MaxIdle) + redisCache := dbcache.NewRedisCache(c.CacheRedis[0].Host, c.CacheRedis[0].Pass, 15*time.Minute) txPoolModel := tx.NewTxPoolModel(db) diff --git a/service/apiserver/server.api b/service/apiserver/server.api index 0c982d982..cdc32d381 100644 --- a/service/apiserver/server.api +++ b/service/apiserver/server.api @@ -30,6 +30,12 @@ type ReqGetRange { Limit uint32 `form:"limit,range=[1:100]"` } +type ReqGetRangeWithFromHash { + Offset uint32 `form:"offset,range=[0:100000]"` + Limit uint32 `form:"limit,range=[1:100]"` + FromHash string `form:"from_hash,optional"` +} + /* ========================= Account =========================*/ type ( @@ -256,28 +262,30 @@ service server-api { type ( Tx { - Hash string `json:"hash"` - Type int64 `json:"type,range=[1:64]"` - Amount string `json:"amount"` - Info string `json:"info"` - Status int64 `json:"status"` - Index int64 `json:"index"` - GasFeeAssetId int64 `json:"gas_fee_asset_id"` - GasFee string `json:"gas_fee"` - NftIndex int64 `json:"nft_index"` - CollectionId int64 `json:"collection_id"` - AssetId int64 `json:"asset_id"` - AssetName string `json:"asset_name"` - NativeAddress string `json:"native_address"` - ExtraInfo string `json:"extra_info"` - Memo string `json:"memo"` - AccountIndex int64 `json:"account_index"` - AccountName string `json:"account_name"` - Nonce int64 `json:"nonce"` - ExpiredAt int64 `json:"expire_at"` - BlockHeight int64 `json:"block_height"` - CreatedAt int64 `json:"created_at"` - StateRoot string `json:"state_root"` + Hash string `json:"hash"` + Type int64 `json:"type,range=[1:64]"` + Amount string `json:"amount"` + Info string `json:"info"` + Status int64 `json:"status"` + Index int64 `json:"index"` + GasFeeAssetId int64 `json:"gas_fee_asset_id"` + GasFee string `json:"gas_fee"` + NftIndex int64 `json:"nft_index"` + CollectionId int64 `json:"collection_id"` + AssetId int64 `json:"asset_id"` + AssetName string `json:"asset_name"` + NativeAddress string `json:"native_address"` + ExtraInfo string `json:"extra_info"` + Memo string `json:"memo"` + AccountIndex int64 `json:"account_index"` + AccountName string `json:"account_name"` + Nonce int64 `json:"nonce"` + ExpiredAt int64 `json:"expire_at"` + BlockHeight int64 `json:"block_height"` + CreatedAt int64 `json:"created_at"` + StateRoot string `json:"state_root"` + ToAccountIndex int64 `json:"to_account_index"` + ToAccountName string `json:"to_account_name"` } Txs { @@ -308,10 +316,11 @@ type ( } ReqGetAccountTxs { - By string `form:"by,options=account_index|account_name|account_pk"` - Value string `form:"value"` - Offset uint16 `form:"offset,range=[0:100000]"` - Limit uint16 `form:"limit,range=[1:100]"` + By string `form:"by,options=account_index|account_name|account_pk"` + Value string `form:"value"` + Types []int64 `form:"types,optional"` + Offset uint16 `form:"offset,range=[0:100000]"` + Limit uint16 `form:"limit,range=[1:100]"` } ReqGetTx { @@ -324,8 +333,9 @@ type ( } ReqGetAccountPendingTxs { - By string `form:"by,options=account_index|account_name|account_pk"` - Value string `form:"value"` + By string `form:"by,options=account_index|account_name|account_pk"` + Value string `form:"value"` + Types []int64 `form:"types,optional"` } ReqGetNextNonce { @@ -338,7 +348,7 @@ type ( ) service server-api { - @doc "Get transactions" + @doc "Get transactions which are already packed into blocks" @handler GetTxs get /api/v1/txs (ReqGetRange) returns (Txs) @@ -358,6 +368,10 @@ service server-api { @handler GetPendingTxs get /api/v1/pendingTxs (ReqGetRange) returns (Txs) + @doc "Get executed transactions which previously added to tx pool" + @handler GetExecutedTxs + get /api/v1/executedTxs (ReqGetRangeWithFromHash) returns (Txs) + @doc "Get pending transactions of a specific account" @handler GetAccountPendingTxs get /api/v1/accountPendingTxs (ReqGetAccountPendingTxs) returns (Txs) diff --git a/service/apiserver/test/getaccountpendingtxs_test.go b/service/apiserver/test/getaccountpendingtxs_test.go index d871077d6..a203df6ca 100644 --- a/service/apiserver/test/getaccountpendingtxs_test.go +++ b/service/apiserver/test/getaccountpendingtxs_test.go @@ -15,8 +15,9 @@ import ( func (s *ApiServerSuite) TestGetAccountPoolTxs() { type args struct { - by string - value string + by string + value string + txTypes []int64 } type testcase struct { @@ -26,25 +27,28 @@ func (s *ApiServerSuite) TestGetAccountPoolTxs() { } tests := []testcase{ - {"not found by index", args{"account_index", "9999999"}, 200}, - {"not found by name", args{"account_name", "notexists.legend"}, 200}, - {"not found by pk", args{"account_pk", "notexists"}, 200}, - {"invalidby", args{"invalidby", ""}, 400}, + {"not found by index", args{"account_index", "9999999", nil}, 200}, + {"not found by name", args{"account_name", "notexists.legend", nil}, 200}, + {"not found by pk", args{"account_pk", "notexists", nil}, 200}, + {"invalidby", args{"invalidby", "", nil}, 400}, } statusCode, txs := GetPendingTxs(s, 0, 100) if statusCode == http.StatusOK && len(txs.Txs) > 0 { - _, account := GetAccount(s, "name", txs.Txs[len(txs.Txs)-1].AccountName) + tx := txs.Txs[len(txs.Txs)-1] + _, account := GetAccount(s, "name", tx.AccountName) tests = append(tests, []testcase{ - {"found by index", args{"account_index", strconv.Itoa(int(account.Index))}, 200}, - {"found by name", args{"account_name", account.Name}, 200}, - {"found by pk", args{"account_pk", account.Pk}, 200}, + {"found by index", args{"account_index", strconv.Itoa(int(account.Index)), nil}, 200}, + {"found by name", args{"account_name", account.Name, nil}, 200}, + {"found by pk", args{"account_pk", account.Pk, nil}, 200}, + {"found by index and type", args{"account_index", strconv.Itoa(int(account.Index)), []int64{tx.Type}}, 200}, + {"not found by index and type", args{"account_index", strconv.Itoa(int(account.Index)), []int64{10000}}, 200}, }...) } for _, tt := range tests { s.T().Run(tt.name, func(t *testing.T) { - httpCode, result := GetAccountPendingTxs(s, tt.args.by, tt.args.value) + httpCode, result := GetAccountPendingTxs(s, tt.args.by, tt.args.value, tt.args.txTypes) assert.Equal(t, tt.httpCode, httpCode) if httpCode == http.StatusOK { if result.Total > 0 { @@ -63,8 +67,13 @@ func (s *ApiServerSuite) TestGetAccountPoolTxs() { } -func GetAccountPendingTxs(s *ApiServerSuite, by, value string) (int, *types.Txs) { - resp, err := http.Get(fmt.Sprintf("%s/api/v1/accountPendingTxs?by=%s&value=%s", s.url, by, value)) +func GetAccountPendingTxs(s *ApiServerSuite, by, value string, txTypes []int64) (int, *types.Txs) { + url := fmt.Sprintf("%s/api/v1/accountPendingTxs?by=%s&value=%s", s.url, by, value) + if len(txTypes) > 0 { + data, _ := json.Marshal(txTypes) + url += fmt.Sprintf("&types=%s", string(data)) + } + resp, err := http.Get(url) assert.NoError(s.T(), err) defer resp.Body.Close() diff --git a/service/apiserver/test/getaccounttxs_test.go b/service/apiserver/test/getaccounttxs_test.go index 33d9e1e5c..a2aede801 100644 --- a/service/apiserver/test/getaccounttxs_test.go +++ b/service/apiserver/test/getaccounttxs_test.go @@ -15,10 +15,11 @@ import ( func (s *ApiServerSuite) TestGetAccountTxs() { type args struct { - by string - value string - offset int - limit int + by string + value string + offset int + limit int + txTypes []int64 } type testcase struct { @@ -28,25 +29,28 @@ func (s *ApiServerSuite) TestGetAccountTxs() { } tests := []testcase{ - {"not found by index", args{"account_index", "99999999", 0, 10}, 200}, - {"not found by name", args{"account_name", "fakeaccount.legend", 0, 10}, 200}, - {"not found by pk", args{"account_pk", "fake8470d33c59a5cbf5e10df426eb97c2773ab890c3364f4162ba782a56ca998", 0, 10}, 200}, - {"invalid by", args{"invalidby", "fake8470d33c59a5cbf5e10df426eb97c2773ab890c3364f4162ba782a56ca998", 0, 10}, 400}, + {"not found by index", args{"account_index", "99999999", 0, 10, nil}, 200}, + {"not found by name", args{"account_name", "fakeaccount.legend", 0, 10, nil}, 200}, + {"not found by pk", args{"account_pk", "fake8470d33c59a5cbf5e10df426eb97c2773ab890c3364f4162ba782a56ca998", 0, 10, nil}, 200}, + {"invalid by", args{"invalidby", "fake8470d33c59a5cbf5e10df426eb97c2773ab890c3364f4162ba782a56ca998", 0, 10, nil}, 400}, } statusCode, txs := GetTxs(s, 0, 100) if statusCode == http.StatusOK && len(txs.Txs) > 0 { - _, account := GetAccount(s, "name", txs.Txs[len(txs.Txs)-1].AccountName) + tx := txs.Txs[len(txs.Txs)-1] + _, account := GetAccount(s, "name", tx.AccountName) tests = append(tests, []testcase{ - {"found by index", args{"account_index", strconv.Itoa(int(account.Index)), 0, 10}, 200}, - {"found by name", args{"account_name", account.Name, 0, 10}, 200}, - {"found by pk", args{"account_pk", account.Pk, 0, 10}, 200}, + {"found by index", args{"account_index", strconv.Itoa(int(account.Index)), 0, 10, nil}, 200}, + {"found by name", args{"account_name", account.Name, 0, 10, nil}, 200}, + {"found by pk", args{"account_pk", account.Pk, 0, 10, nil}, 200}, + {"found by index and type", args{"account_index", strconv.Itoa(int(account.Index)), 0, 10, []int64{tx.Type}}, 200}, + {"not found by index and type", args{"account_index", strconv.Itoa(int(account.Index)), 0, 10, []int64{10000}}, 200}, }...) } for _, tt := range tests { s.T().Run(tt.name, func(t *testing.T) { - httpCode, result := GetAccountTxs(s, tt.args.by, tt.args.value, tt.args.offset, tt.args.limit) + httpCode, result := GetAccountTxs(s, tt.args.by, tt.args.value, tt.args.offset, tt.args.limit, tt.args.txTypes) assert.Equal(t, tt.httpCode, httpCode) if httpCode == http.StatusOK { if tt.args.offset < int(result.Total) { @@ -65,8 +69,13 @@ func (s *ApiServerSuite) TestGetAccountTxs() { } -func GetAccountTxs(s *ApiServerSuite, by, value string, offset, limit int) (int, *types.Txs) { - resp, err := http.Get(fmt.Sprintf("%s/api/v1/accountTxs?by=%s&value=%s&offset=%d&limit=%d", s.url, by, value, offset, limit)) +func GetAccountTxs(s *ApiServerSuite, by, value string, offset, limit int, txTypes []int64) (int, *types.Txs) { + url := fmt.Sprintf("%s/api/v1/accountTxs?by=%s&value=%s&offset=%d&limit=%d", s.url, by, value, offset, limit) + if len(txTypes) > 0 { + data, _ := json.Marshal(txTypes) + url += fmt.Sprintf("&types=%s", string(data)) + } + resp, err := http.Get(url) assert.NoError(s.T(), err) defer resp.Body.Close() diff --git a/service/apiserver/test/getexecutedtxs_test.go b/service/apiserver/test/getexecutedtxs_test.go new file mode 100644 index 000000000..ee0564b05 --- /dev/null +++ b/service/apiserver/test/getexecutedtxs_test.go @@ -0,0 +1,71 @@ +package test + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/bnb-chain/zkbnb/service/apiserver/internal/types" +) + +func (s *ApiServerSuite) TestGetExecutedTxs() { + + type args struct { + offset int + limit int + fromHash string + } + tests := []struct { + name string + args args + httpCode int + }{ + {"found", args{0, 10, "hash"}, 200}, + {"found without hash", args{0, 10, ""}, 200}, + } + + for _, tt := range tests { + s.T().Run(tt.name, func(t *testing.T) { + httpCode, result := GetExecutedTxs(s, tt.args.offset, tt.args.limit, tt.args.fromHash) + assert.Equal(t, tt.httpCode, httpCode) + if httpCode == http.StatusOK { + if tt.args.offset < int(result.Total) { + assert.True(t, len(result.Txs) > 0) + assert.NotNil(t, result.Txs[0].BlockHeight) + assert.NotNil(t, result.Txs[0].Hash) + assert.NotNil(t, result.Txs[0].Type) + assert.NotNil(t, result.Txs[0].StateRoot) + assert.NotNil(t, result.Txs[0].Info) + assert.NotNil(t, result.Txs[0].Status) + } + fmt.Printf("result: %+v \n", result) + } + }) + } + +} + +func GetExecutedTxs(s *ApiServerSuite, offset, limit int, from_hash string) (int, *types.Txs) { + url := fmt.Sprintf("%s/api/v1/executedTxs?offset=%d&limit=%d", s.url, offset, limit) + if len(from_hash) > 0 { + url += fmt.Sprintf("&from_hash=%s", from_hash) + } + resp, err := http.Get(url) + assert.NoError(s.T(), err) + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + assert.NoError(s.T(), err) + + if resp.StatusCode != http.StatusOK { + return resp.StatusCode, nil + } + result := types.Txs{} + //nolint: errcheck + json.Unmarshal(body, &result) + return resp.StatusCode, &result +} diff --git a/service/apiserver/test/getpendingtxs_test.go b/service/apiserver/test/getpendingtxs_test.go index 41e96b0bf..35618a057 100644 --- a/service/apiserver/test/getpendingtxs_test.go +++ b/service/apiserver/test/getpendingtxs_test.go @@ -12,7 +12,7 @@ import ( "github.com/bnb-chain/zkbnb/service/apiserver/internal/types" ) -func (s *ApiServerSuite) TestGetPoolTxs() { +func (s *ApiServerSuite) TestGetPendingTxs() { type args struct { offset int diff --git a/service/apiserver/test/suite_test.go b/service/apiserver/test/suite_test.go index 8fc2673f4..9fd3d64d4 100644 --- a/service/apiserver/test/suite_test.go +++ b/service/apiserver/test/suite_test.go @@ -82,7 +82,15 @@ func (s *ApiServerSuite) SetupSuite() { MaxKeyNum int64 }{AccountExpiration: 10000, AssetExpiration: 10000, BlockExpiration: 10000, TxExpiration: 10000, PriceExpiration: 3600000, MaxCounterNum: 10000, MaxKeyNum: 10000}, } - c.Postgres = struct{ DataSource string }{DataSource: "host=127.0.0.1 user=postgres password=ZkBNB@123 dbname=zkbnb port=5433 sslmode=disable"} + c.Postgres = struct { + DataSource string + MaxIdle int + MaxConn int + }{ + DataSource: "host=127.0.0.1 user=postgres password=ZkBNB@123 dbname=zkbnb port=5433 sslmode=disable", + MaxIdle: 10, + MaxConn: 100, + } c.CacheRedis = cache.CacheConf{} c.CacheRedis = append(c.CacheRedis, cache.NodeConf{ RedisConf: redis.RedisConf{Host: "127.0.0.1"}, diff --git a/service/committer/committer/committer.go b/service/committer/committer/committer.go index 387bd9ad9..535c9f33b 100644 --- a/service/committer/committer/committer.go +++ b/service/committer/committer/committer.go @@ -30,6 +30,36 @@ var ( Name: "priority_operation_process_height", Help: "Priority operation height metrics.", }) + commitOperationMetics = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "zkbnb", + Name: "db_commit_time", + Help: "DB commit operation time", + }) + executeTxOperationMetrics = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "zkbnb", + Name: "exec_tx_time", + Help: "execute txs operation time", + }) + pendingTxNumMetrics = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "zkbnb", + Name: "pending_tx", + Help: "number of pending tx", + }) + stateDBOperationMetics = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "zkbnb", + Name: "state_db_time", + Help: "stateDB commit operation time", + }) + stateDBSyncOperationMetics = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "zkbnb", + Name: "state_sync_time", + Help: "stateDB sync operation time", + }) + sqlDBOperationMetics = prometheus.NewGauge(prometheus.GaugeOpts{ + Namespace: "zkbnb", + Name: "sql_db_time", + Help: "sql DB commit operation time", + }) ) type Config struct { @@ -66,6 +96,24 @@ func NewCommitter(config *Config) (*Committer, error) { if err := prometheus.Register(priorityOperationHeightMetric); err != nil { return nil, fmt.Errorf("prometheus.Register priorityOperationHeightMetric error: %v", err) } + if err := prometheus.Register(commitOperationMetics); err != nil { + return nil, fmt.Errorf("prometheus.Register commitOperationMetics error: %v", err) + } + if err := prometheus.Register(pendingTxNumMetrics); err != nil { + return nil, fmt.Errorf("prometheus.Register pendingTxNumMetrics error: %v", err) + } + if err := prometheus.Register(executeTxOperationMetrics); err != nil { + return nil, fmt.Errorf("prometheus.Register executeTxOperationMetrics error: %v", err) + } + if err := prometheus.Register(stateDBOperationMetics); err != nil { + return nil, fmt.Errorf("prometheus.Register stateDBOperationMetics error: %v", err) + } + if err := prometheus.Register(stateDBSyncOperationMetics); err != nil { + return nil, fmt.Errorf("prometheus.Register stateDBSyncOperationMetics error: %v", err) + } + if err := prometheus.Register(sqlDBOperationMetics); err != nil { + return nil, fmt.Errorf("prometheus.Register sqlDBOperationMetics error: %v", err) + } committer := &Committer{ running: true, @@ -95,7 +143,7 @@ func (c *Committer) Run() { break } if curBlock.BlockStatus > block.StatusProposing { - curBlock, err = c.bc.ProposeNewBlock() + curBlock, err = c.bc.InitNewBlock() if err != nil { panic("propose new block failed: " + err.Error()) } @@ -120,13 +168,14 @@ func (c *Committer) Run() { } } + pendingTxNumMetrics.Set(float64(len(pendingTxs))) pendingUpdatePoolTxs := make([]*tx.Tx, 0, len(pendingTxs)) pendingDeletePoolTxs := make([]*tx.Tx, 0, len(pendingTxs)) + start := time.Now() for _, poolTx := range pendingTxs { if c.shouldCommit(curBlock) { break } - logx.Infof("apply transaction, txHash=%s", poolTx.TxHash) err = c.bc.ApplyTransaction(poolTx) if err != nil { @@ -163,6 +212,7 @@ func (c *Committer) Run() { pendingUpdatePoolTxs = append(pendingUpdatePoolTxs, poolTx) } } + executeTxOperationMetrics.Set(float64(time.Since(start).Milliseconds())) err = c.bc.StateDB().SyncStateCacheToRedis() if err != nil { @@ -181,6 +231,7 @@ func (c *Committer) Run() { } if c.shouldCommit(curBlock) { + start := time.Now() logx.Infof("commit new block, height=%d, blockSize=%d", curBlock.BlockHeight, curBlock.BlockSize) curBlock, err = c.commitNewBlock(curBlock) logx.Infof("commit new block success") @@ -189,6 +240,7 @@ func (c *Committer) Run() { logx.Errorf("commit new block error, err=%s", err.Error()) panic("commit new block failed: " + err.Error()) } + commitOperationMetics.Set(float64(time.Since(start).Milliseconds())) } } } @@ -222,6 +274,9 @@ func (c *Committer) restoreExecutedTxs() (*block.Block, error) { return curBlock, nil } + if err := c.bc.StateDB().MarkGasAccountAsPending(); err != nil { + return nil, err + } for _, executedTx := range executedTxs { err = c.bc.ApplyTransaction(executedTx) if err != nil { @@ -255,16 +310,21 @@ func (c *Committer) shouldCommit(curBlock *block.Block) bool { func (c *Committer) commitNewBlock(curBlock *block.Block) (*block.Block, error) { blockSize := c.computeCurrentBlockSize() + start := time.Now() blockStates, err := c.bc.CommitNewBlock(blockSize, curBlock.CreatedAt.UnixMilli()) if err != nil { return nil, err } + stateDBOperationMetics.Set(float64(time.Since(start).Milliseconds())) - err = c.bc.Statedb.SyncPendingGasAccount() + start = time.Now() + err = c.bc.Statedb.SyncGasAccountToRedis() if err != nil { return nil, err } + stateDBSyncOperationMetics.Set(float64(time.Since(start).Milliseconds())) + start = time.Now() // update db err = c.bc.DB().DB.Transaction(func(tx *gorm.DB) error { // create block for commit @@ -314,6 +374,7 @@ func (c *Committer) commitNewBlock(curBlock *block.Block) (*block.Block, error) if err != nil { return nil, err } + sqlDBOperationMetics.Set(float64(time.Since(start).Milliseconds())) return blockStates.Block, nil } diff --git a/service/fullnode/etc/fullnode.yaml.example b/service/fullnode/etc/fullnode.yaml.example new file mode 100644 index 000000000..68e23321b --- /dev/null +++ b/service/fullnode/etc/fullnode.yaml.example @@ -0,0 +1,17 @@ +Name: fullnode + +Postgres: + DataSource: host=127.0.0.1 user=postgres password=ZkBNB@123 dbname=zkbnb port=5432 sslmode=disable + +CacheRedis: + - Host: 127.0.0.1:6379 + Type: node + +L2EndPoint: http://127.0.0.1:8888 + +# StatusPending: 1, StatusCommitted: 2, StatusVerifiedAndExecuted: 3 +SyncBlockStatus: 3 + +TreeDB: + Driver: memorydb + diff --git a/service/fullnode/fullnode.go b/service/fullnode/fullnode.go new file mode 100644 index 000000000..3d7016290 --- /dev/null +++ b/service/fullnode/fullnode.go @@ -0,0 +1,36 @@ +package fullnode + +import ( + "time" + + "github.com/zeromicro/go-zero/core/conf" + "github.com/zeromicro/go-zero/core/logx" + "github.com/zeromicro/go-zero/core/proc" + + "github.com/bnb-chain/zkbnb/service/fullnode/fullnode" +) + +const GracefulShutdownTimeout = 5 * time.Second + +func Run(configFile string) error { + var config fullnode.Config + conf.MustLoad(configFile, &config) + logx.MustSetup(config.LogConf) + logx.DisableStat() + + node, err := fullnode.NewFullnode(&config) + if err != nil { + logx.Error("new fullnode failed:", err) + return err + } + + proc.SetTimeToForceQuit(GracefulShutdownTimeout) + proc.AddShutdownListener(func() { + logx.Info("start to shutdown fullnode......") + node.Shutdown() + _ = logx.Close() + }) + logx.Info("fullnode is starting......") + node.Run() + return nil +} diff --git a/service/fullnode/fullnode/fullnode.go b/service/fullnode/fullnode/fullnode.go new file mode 100644 index 000000000..92bad6641 --- /dev/null +++ b/service/fullnode/fullnode/fullnode.go @@ -0,0 +1,226 @@ +package fullnode + +import ( + "fmt" + "time" + + "github.com/zeromicro/go-zero/core/logx" + "gorm.io/gorm" + + "github.com/bnb-chain/zkbnb-go-sdk/client" + "github.com/bnb-chain/zkbnb/core" + "github.com/bnb-chain/zkbnb/dao/block" + tx "github.com/bnb-chain/zkbnb/dao/tx" + "github.com/bnb-chain/zkbnb/types" +) + +const ( + DefaultL2EndPoint = "http://localhost:8888" + SyncInterval = 100 * time.Millisecond +) + +type Config struct { + core.ChainConfig + L2EndPoint string + SyncBlockStatus int64 + LogConf logx.LogConf +} + +type Fullnode struct { + config *Config + client client.ZkBNBClient + bc *core.BlockChain + + quitCh chan struct{} +} + +func NewFullnode(config *Config) (*Fullnode, error) { + bc, err := core.NewBlockChain(&config.ChainConfig, "fullnode") + if err != nil { + return nil, fmt.Errorf("new blockchain error: %v", err) + } + + l2EndPoint := config.L2EndPoint + if len(l2EndPoint) == 0 { + l2EndPoint = DefaultL2EndPoint + } + + if config.SyncBlockStatus <= block.StatusProposing || + config.SyncBlockStatus > block.StatusVerifiedAndExecuted { + config.SyncBlockStatus = block.StatusVerifiedAndExecuted + } + + fullnode := &Fullnode{ + config: config, + client: client.NewZkBNBClient(l2EndPoint), + bc: bc, + + quitCh: make(chan struct{}), + } + return fullnode, nil +} + +func (c *Fullnode) Run() { + curHeight, err := c.bc.BlockModel.GetCurrentBlockHeight() + if err != nil { + panic(fmt.Sprintf("get current block height failed, error: %v", err.Error())) + } + + curBlock, err := c.bc.BlockModel.GetBlockByHeight(curHeight) + if err != nil { + panic(fmt.Sprintf("get current block failed, height: %d, error: %v", curHeight, err.Error())) + } + + ticker := time.NewTicker(SyncInterval) + defer ticker.Stop() + + syncBlockStatus := c.config.SyncBlockStatus + for { + select { + case <-ticker.C: + + // if the latest block have been created + if curBlock.BlockStatus > block.StatusProposing { + // init new block, set curBlock.status to block.StatusProposing + curBlock, err = c.bc.InitNewBlock() + if err != nil { + panic(fmt.Sprintf("init new block failed, block height: %d, error: %v", curHeight, err.Error())) + } + + curHeight++ + } + + l2Block, err := c.client.GetBlockByHeight(curHeight) + if err != nil { + if err != types.DbErrNotFound { + logx.Errorf("get block failed, height: %d, err %v ", curHeight, err) + } + continue + } + + if l2Block.Status < syncBlockStatus { + continue + } + + // create time needs to be set, otherwise tx will fail if expire time is set + c.bc.CurrentBlock().CreatedAt = time.UnixMilli(l2Block.CommittedAt) + // set info + if l2Block.Status >= block.StatusCommitted { + c.bc.CurrentBlock().CommittedAt = l2Block.CommittedAt + c.bc.CurrentBlock().CommittedTxHash = l2Block.CommittedTxHash + c.bc.CurrentBlock().BlockCommitment = l2Block.Commitment + } + if l2Block.Status == block.StatusVerifiedAndExecuted { + c.bc.CurrentBlock().VerifiedAt = l2Block.VerifiedAt + c.bc.CurrentBlock().VerifiedTxHash = l2Block.VerifiedTxHash + } + + // clean cache + c.bc.Statedb.PurgeCache(curBlock.StateRoot) + + for _, blockTx := range l2Block.Txs { + newTx := &tx.Tx{ + TxHash: blockTx.Hash, // Would be computed in prepare method of executors. + TxType: blockTx.Type, + TxInfo: blockTx.Info, + } + + err = c.bc.ApplyTransaction(newTx) + if err != nil { + logx.Errorf("apply block tx ID: %d failed, err %v ", newTx.ID, err) + continue + } + } + + err = c.bc.Statedb.IntermediateRoot(true) + if err != nil { + panic(fmt.Sprint("calculate state root failed, err", err)) + } + + if c.bc.Statedb.StateRoot != l2Block.StateRoot { + panic(fmt.Sprintf("state root not matched between statedb and l2block: %d, local: %s, remote: %s", l2Block.Height, c.bc.Statedb.StateRoot, l2Block.StateRoot)) + } + + curBlock, err = c.processNewBlock(curBlock, int(l2Block.Size)) + if err != nil { + panic(fmt.Sprintf("new block failed, block height: %d, Error: %s", l2Block.Height, err.Error())) + } + logx.Infof("created new block on fullnode, height=%d, blockSize=%d", curBlock.BlockHeight, l2Block.Size) + case <-c.quitCh: + return + } + } +} + +func (c *Fullnode) Shutdown() { + close(c.quitCh) + c.bc.Statedb.Close() + c.bc.ChainDB.Close() +} + +func (c *Fullnode) processNewBlock(curBlock *block.Block, blockSize int) (*block.Block, error) { + blockStates, err := c.bc.CommitNewBlock(blockSize, curBlock.CreatedAt.UnixMilli()) + if err != nil { + return nil, err + } + blockStates.Block.BlockStatus = c.config.SyncBlockStatus + + // sync gas account + err = c.bc.Statedb.SyncGasAccountToRedis() + if err != nil { + return nil, err + } + + // sync pending value to caches + err = c.bc.Statedb.SyncStateCacheToRedis() + if err != nil { + panic("sync redis cache failed: " + err.Error()) + } + + // update db + err = c.bc.DB().DB.Transaction(func(tx *gorm.DB) error { + // create block for commit + if blockStates.CompressedBlock != nil { + err = c.bc.DB().CompressedBlockModel.CreateCompressedBlockInTransact(tx, blockStates.CompressedBlock) + if err != nil { + return err + } + } + // create or update account + if len(blockStates.PendingAccount) != 0 { + err = c.bc.DB().AccountModel.UpdateAccountsInTransact(tx, blockStates.PendingAccount) + if err != nil { + return err + } + } + // create account history + if len(blockStates.PendingAccountHistory) != 0 { + err = c.bc.DB().AccountHistoryModel.CreateAccountHistoriesInTransact(tx, blockStates.PendingAccountHistory) + if err != nil { + return err + } + } + // create or update nft + if len(blockStates.PendingNft) != 0 { + err = c.bc.DB().L2NftModel.UpdateNftsInTransact(tx, blockStates.PendingNft) + if err != nil { + return err + } + } + // create nft history + if len(blockStates.PendingNftHistory) != 0 { + err = c.bc.DB().L2NftHistoryModel.CreateNftHistoriesInTransact(tx, blockStates.PendingNftHistory) + if err != nil { + return err + } + } + + return c.bc.DB().BlockModel.CreateBlockInTransact(tx, blockStates.Block) + }) + + if err != nil { + return nil, err + } + + return blockStates.Block, nil +} diff --git a/service/witness/config/config.go b/service/witness/config/config.go index dfcef2a5f..418d8bab4 100644 --- a/service/witness/config/config.go +++ b/service/witness/config/config.go @@ -15,7 +15,9 @@ type Config struct { //nolint:staticcheck LevelDBOption tree.LevelDBOption `json:",optional"` //nolint:staticcheck - RedisDBOption tree.RedisDBOption `json:",optional"` + RedisDBOption tree.RedisDBOption `json:",optional"` + //nolint:staticcheck + RoutinePoolSize int `json:",optional"` AssetTreeCacheSize int } LogConf logx.LogConf diff --git a/service/witness/witness/witness.go b/service/witness/witness/witness.go index e770e55d2..98fe7d4b7 100644 --- a/service/witness/witness/witness.go +++ b/service/witness/witness/witness.go @@ -7,12 +7,12 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/panjf2000/ants/v2" "github.com/zeromicro/go-zero/core/logx" "gorm.io/driver/postgres" "gorm.io/gorm" "github.com/bnb-chain/zkbnb-crypto/circuit" + bsmt "github.com/bnb-chain/zkbnb-smt" smt "github.com/bnb-chain/zkbnb-smt" utils "github.com/bnb-chain/zkbnb/common/prove" "github.com/bnb-chain/zkbnb/dao/account" @@ -29,8 +29,6 @@ const ( UnprovedBlockWitnessTimeout = 10 * time.Minute BlockProcessDelta = 10 - - defaultTaskPoolSize = 1000 ) type Witness struct { @@ -43,7 +41,6 @@ type Witness struct { accountTree smt.SparseMerkleTree assetTrees *tree.AssetTreeCache nftTree smt.SparseMerkleTree - taskPool *ants.Pool // The data access object db *gorm.DB @@ -87,12 +84,12 @@ func (w *Witness) initState() error { } // dbinitializer tree database - treeCtx := &tree.Context{ - Name: "witness", - Driver: w.config.TreeDB.Driver, - LevelDBOption: &w.config.TreeDB.LevelDBOption, - RedisDBOption: &w.config.TreeDB.RedisDBOption, + treeCtx, err := tree.NewContext("witness", w.config.TreeDB.Driver, false, w.config.TreeDB.RoutinePoolSize, &w.config.TreeDB.LevelDBOption, &w.config.TreeDB.RedisDBOption) + if err != nil { + return err } + + treeCtx.SetOptions(bsmt.BatchSizeLimit(3 * 1024 * 1024)) err = tree.SetupTreeDB(treeCtx) if err != nil { return fmt.Errorf("init tree database failed %v", err) @@ -118,11 +115,6 @@ func (w *Witness) initState() error { if err != nil { return fmt.Errorf("initNftTree error: %v", err) } - taskPool, err := ants.NewPool(defaultTaskPoolSize) - if err != nil { - return err - } - w.taskPool = taskPool w.helper = utils.NewWitnessHelper(w.treeCtx, w.accountTree, w.nftTree, w.assetTrees, w.accountModel, w.accountHistoryModel) return nil } @@ -156,7 +148,7 @@ func (w *Witness) GenerateBlockWitness() (err error) { return fmt.Errorf("failed to construct block witness, block:%d, err: %v", block.BlockHeight, err) } // Step2: commit trees for witness - err = tree.CommitTrees(w.taskPool, uint64(latestVerifiedBlockNr), w.accountTree, w.assetTrees, w.nftTree) + err = tree.CommitTrees(uint64(latestVerifiedBlockNr), w.accountTree, w.assetTrees, w.nftTree) if err != nil { return fmt.Errorf("unable to commit trees after txs is executed, block:%d, error: %v", block.BlockHeight, err) } @@ -164,7 +156,7 @@ func (w *Witness) GenerateBlockWitness() (err error) { err = w.blockWitnessModel.CreateBlockWitness(blockWitness) if err != nil { // rollback trees - rollBackErr := tree.RollBackTrees(w.taskPool, uint64(block.BlockHeight)-1, w.accountTree, w.assetTrees, w.nftTree) + rollBackErr := tree.RollBackTrees(uint64(block.BlockHeight)-1, w.accountTree, w.assetTrees, w.nftTree) if rollBackErr != nil { logx.Errorf("unable to rollback trees %v", rollBackErr) } diff --git a/tools/dbinitializer/contractaddr.yaml b/tools/dbinitializer/contractaddr.yaml index 1055d137d..9bc1ecd04 100644 --- a/tools/dbinitializer/contractaddr.yaml +++ b/tools/dbinitializer/contractaddr.yaml @@ -5,6 +5,7 @@ ZnsControllerProxy: 0xa9702D6c08B353E120A882a266Cc8800d5A275a3 ZnsResolverProxy: 0x5735829b5B27f788Af29F93Ec883Ce8F7b02c808 ZkBNBProxy: 0x472236Cd542663AcDDF1fF8AC1269152BCE43826 UpgradeGateKeeper: 0x3B04DFD01a556BF9F44A30Be91A67A814e24eC99 +BUSDToken: 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56 LEGToken: 0xe121f36A3F3118dC58A53CA895049106f4c4BC05 REYToken: 0xf112f442B5cA7272B5e11eeC0672a5CB32BF8C70 ERC721: 0x916Abe7836BA8361bB62Dc33EDE60d85D778568E diff --git a/tools/dbinitializer/contractaddr.yaml.example b/tools/dbinitializer/contractaddr.yaml.example index d75481673..34e8a4e6d 100644 --- a/tools/dbinitializer/contractaddr.yaml.example +++ b/tools/dbinitializer/contractaddr.yaml.example @@ -5,6 +5,7 @@ ZnsControllerProxy: 0xa9702D6c08B353E120A882a266Cc8800d5A275a3 ZnsResolverProxy: 0x5735829b5B27f788Af29F93Ec883Ce8F7b02c808 ZkBNBProxy: 0x5A29E751E8C310849aEab8A274838929f29b75ca UpgradeGateKeeper: 0x3B04DFD01a556BF9F44A30Be91A67A814e24eC99 +BUSDToken: 0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56 LEGToken: 0xe121f36A3F3118dC58A53CA895049106f4c4BC05 REYToken: 0xf112f442B5cA7272B5e11eeC0672a5CB32BF8C70 ERC721: 0x916Abe7836BA8361bB62Dc33EDE60d85D778568E diff --git a/tools/dbinitializer/main.go b/tools/dbinitializer/main.go index 4c0568dfc..be4701b62 100644 --- a/tools/dbinitializer/main.go +++ b/tools/dbinitializer/main.go @@ -51,6 +51,7 @@ type contractAddr struct { ZnsResolverProxy string ZkBNBProxy string UpgradeGateKeeper string + BUSDToken string LEGToken string REYToken string ERC721 string @@ -129,8 +130,19 @@ func initSysConfig(svrConf *contractAddr, bscTestNetworkRPC, localTestNetworkRPC bnbGasFee[types.TxTypeCancelOffer] = 12000000000000 bnbGasFee[types.TxTypeWithdrawNft] = 20000000000000 + busdGasFee := make(map[int]int64) + busdGasFee[types.TxTypeTransfer] = 10000000000000 + busdGasFee[types.TxTypeWithdraw] = 20000000000000 + busdGasFee[types.TxTypeCreateCollection] = 10000000000000 + busdGasFee[types.TxTypeMintNft] = 10000000000000 + busdGasFee[types.TxTypeTransferNft] = 12000000000000 + busdGasFee[types.TxTypeAtomicMatch] = 18000000000000 + busdGasFee[types.TxTypeCancelOffer] = 12000000000000 + busdGasFee[types.TxTypeWithdrawNft] = 20000000000000 + gasFeeConfig := make(map[uint32]map[int]int64) // asset id -> (tx type -> gas fee value) gasFeeConfig[types.BNBAssetId] = bnbGasFee // bnb asset + gasFeeConfig[types.BUSDAssetId] = busdGasFee // busd asset gas, err := json.Marshal(gasFeeConfig) if err != nil { @@ -191,10 +203,10 @@ func initSysConfig(svrConf *contractAddr, bscTestNetworkRPC, localTestNetworkRPC } } -func initAssetsInfo() []*asset.Asset { +func initAssetsInfo(busdAddress string) []*asset.Asset { return []*asset.Asset{ { - AssetId: 0, + AssetId: types.BNBAssetId, L1Address: "0x00", AssetName: "BNB", AssetSymbol: "BNB", @@ -202,6 +214,15 @@ func initAssetsInfo() []*asset.Asset { Status: 0, IsGasAsset: asset.IsGasAsset, }, + { + AssetId: types.BUSDAssetId, + L1Address: busdAddress, + AssetName: "BUSD", + AssetSymbol: "BUSD", + Decimals: 18, + Status: 0, + IsGasAsset: asset.IsGasAsset, + }, } } @@ -241,7 +262,7 @@ func initTable(dao *dao, svrConf *contractAddr, bscTestNetworkRPC, localTestNetw assert.Nil(nil, dao.l1RollupTModel.CreateL1RollupTxTable()) assert.Nil(nil, dao.nftModel.CreateL2NftTable()) assert.Nil(nil, dao.nftHistoryModel.CreateL2NftHistoryTable()) - rowsAffected, err := dao.assetModel.CreateAssets(initAssetsInfo()) + rowsAffected, err := dao.assetModel.CreateAssets(initAssetsInfo(svrConf.BUSDToken)) if err != nil { panic(err) } diff --git a/tools/recovery/internal/config/config.go b/tools/recovery/internal/config/config.go index 64f646719..349acfd82 100644 --- a/tools/recovery/internal/config/config.go +++ b/tools/recovery/internal/config/config.go @@ -17,7 +17,9 @@ type Config struct { //nolint:staticcheck LevelDBOption tree.LevelDBOption `json:",optional"` //nolint:staticcheck - RedisDBOption tree.RedisDBOption `json:",optional"` + RedisDBOption tree.RedisDBOption `json:",optional"` + //nolint:staticcheck + RoutinePoolSize int `json:",optional"` AssetTreeCacheSize int } LogConf logx.LogConf diff --git a/tools/recovery/recovery.go b/tools/recovery/recovery.go index 776cb4c0c..e47db0ee4 100644 --- a/tools/recovery/recovery.go +++ b/tools/recovery/recovery.go @@ -27,16 +27,15 @@ func RecoveryTreeDB( }) // dbinitializer tree database - treeCtx := &tree.Context{ - Name: serviceName, - Driver: c.TreeDB.Driver, - LevelDBOption: &c.TreeDB.LevelDBOption, - RedisDBOption: &c.TreeDB.RedisDBOption, - Reload: true, + treeCtx, err := tree.NewContext(serviceName, c.TreeDB.Driver, true, c.TreeDB.RoutinePoolSize, &c.TreeDB.LevelDBOption, &c.TreeDB.RedisDBOption) + if err != nil { + logx.Errorf("Init tree database failed: %s", err) + return } + treeCtx.SetOptions(bsmt.InitializeVersion(bsmt.Version(blockHeight) - 1)) treeCtx.SetBatchReloadSize(batchSize) - err := tree.SetupTreeDB(treeCtx) + err = tree.SetupTreeDB(treeCtx) if err != nil { logx.Errorf("Init tree database failed: %s", err) return diff --git a/tree/account_tree.go b/tree/account_tree.go index 855ce7166..e377e8fff 100644 --- a/tree/account_tree.go +++ b/tree/account_tree.go @@ -19,6 +19,7 @@ package tree import ( "errors" + "hash" "strconv" "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" @@ -54,7 +55,7 @@ func InitAccountTree( // init account state trees accountAssetTrees = NewLazyTreeCache(assetCacheSize, accountNums-1, blockHeight, func(index, block int64) bsmt.SparseMerkleTree { - tree, err := bsmt.NewBASSparseMerkleTree(bsmt.NewHasher(mimc.NewMiMC()), + tree, err := bsmt.NewBASSparseMerkleTree(ctx.Hasher(), SetNamespace(ctx, accountAssetNamespace(index)), AssetTreeHeight, NilAccountAssetNodeHash, ctx.Options(block)...) if err != nil { @@ -63,7 +64,7 @@ func InitAccountTree( } return tree }) - accountTree, err = bsmt.NewBASSparseMerkleTree(bsmt.NewHasher(mimc.NewMiMC()), + accountTree, err = bsmt.NewBASSparseMerkleTree(ctx.Hasher(), SetNamespace(ctx, AccountPrefix), AccountTreeHeight, NilAccountNodeHash, opts...) if err != nil { @@ -256,6 +257,6 @@ func AccountToNode( } func NewMemAccountAssetTree() (tree bsmt.SparseMerkleTree, err error) { - return bsmt.NewBASSparseMerkleTree(bsmt.NewHasher(mimc.NewMiMC()), + return bsmt.NewBASSparseMerkleTree(bsmt.NewHasherPool(func() hash.Hash { return mimc.NewMiMC() }), memory.NewMemoryDB(), AssetTreeHeight, NilAccountAssetNodeHash) } diff --git a/tree/nft_tree.go b/tree/nft_tree.go index f4eddce6e..baa74b111 100644 --- a/tree/nft_tree.go +++ b/tree/nft_tree.go @@ -18,7 +18,6 @@ package tree import ( - "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" "github.com/zeromicro/go-zero/core/logx" bsmt "github.com/bnb-chain/zkbnb-smt" @@ -32,7 +31,7 @@ func InitNftTree( ) ( nftTree bsmt.SparseMerkleTree, err error, ) { - nftTree, err = bsmt.NewBASSparseMerkleTree(bsmt.NewHasher(mimc.NewMiMC()), + nftTree, err = bsmt.NewBASSparseMerkleTree(ctx.Hasher(), SetNamespace(ctx, NFTPrefix), NftTreeHeight, NilNftNodeHash, ctx.Options(blockHeight)...) if err != nil { diff --git a/tree/treedb.go b/tree/treedb.go index 3a728692e..23a447c41 100644 --- a/tree/treedb.go +++ b/tree/treedb.go @@ -3,6 +3,7 @@ package tree import ( "encoding/json" "errors" + "hash" "strings" "time" @@ -11,6 +12,8 @@ import ( "github.com/bnb-chain/zkbnb-smt/database/leveldb" "github.com/bnb-chain/zkbnb-smt/database/memory" "github.com/bnb-chain/zkbnb-smt/database/redis" + "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" + "github.com/panjf2000/ants/v2" ) const defaultBatchReloadSize = 1000 @@ -182,6 +185,35 @@ func SetNamespace( return context.TreeDB } +const ( + defaultTreeRoutinePoolSize = 10240 +) + +func NewContext( + name string, driver Driver, + reload bool, routineSize int, + levelDBOption *LevelDBOption, + redisDBOption *RedisDBOption) (*Context, error) { + + if routineSize <= 0 { + routineSize = defaultTreeRoutinePoolSize + } + pool, err := ants.NewPool(routineSize) + if err != nil { + return nil, err + } + return &Context{ + Name: name, + Driver: driver, + LevelDBOption: levelDBOption, + RedisDBOption: redisDBOption, + reload: reload, + routinePool: pool, + hasher: bsmt.NewHasherPool(func() hash.Hash { return mimc.NewMiMC() }), + defaultOptions: []bsmt.Option{bsmt.GoRoutinePool(pool)}, + }, nil +} + type Context struct { Name string Driver Driver @@ -190,12 +222,14 @@ type Context struct { TreeDB database.TreeDB defaultOptions []bsmt.Option - Reload bool + reload bool batchReloadSize int + routinePool *ants.Pool + hasher *bsmt.Hasher } func (ctx *Context) IsLoad() bool { - if ctx.Reload { + if ctx.reload { return true } return ctx.Driver == MemoryDB @@ -227,3 +261,11 @@ func (ctx *Context) BatchReloadSize() int { func (ctx *Context) SetBatchReloadSize(size int) { ctx.batchReloadSize = size } + +func (ctx *Context) RoutinePool() *ants.Pool { + return ctx.routinePool +} + +func (ctx *Context) Hasher() *bsmt.Hasher { + return ctx.hasher +} diff --git a/tree/util.go b/tree/util.go index 288b7d651..ab1f9e3ab 100644 --- a/tree/util.go +++ b/tree/util.go @@ -23,13 +23,14 @@ import ( "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" "github.com/ethereum/go-ethereum/common" - "github.com/panjf2000/ants/v2" "github.com/pkg/errors" curve "github.com/bnb-chain/zkbnb-crypto/ecc/ztwistededwards/tebn254" "github.com/bnb-chain/zkbnb-crypto/ffmath" bsmt "github.com/bnb-chain/zkbnb-smt" + common2 "github.com/bnb-chain/zkbnb/common" + "github.com/bnb-chain/zkbnb/common/gopool" ) func EmptyAccountNodeHash() []byte { @@ -87,7 +88,6 @@ func EmptyNftNodeHash() []byte { } func CommitTrees( - pool *ants.Pool, version uint64, accountTree bsmt.SparseMerkleTree, assetTrees *AssetTreeCache, @@ -100,7 +100,7 @@ func CommitTrees( errChan := make(chan error, totalTask) defer close(errChan) - err := pool.Submit(func() { + err := gopool.Submit(func() { accPrunedVersion := bsmt.Version(version) if accountTree.LatestVersion() < accPrunedVersion { accPrunedVersion = accountTree.LatestVersion() @@ -118,7 +118,7 @@ func CommitTrees( for _, idx := range assetTreeChanges { err := func(i int64) error { - return pool.Submit(func() { + return gopool.Submit(func() { asset := assetTrees.Get(i) version := asset.LatestVersion() ver, err := asset.Commit(&version) @@ -134,7 +134,7 @@ func CommitTrees( } } - err = pool.Submit(func() { + err = gopool.Submit(func() { nftPrunedVersion := bsmt.Version(version) if nftTree.LatestVersion() < nftPrunedVersion { nftPrunedVersion = nftTree.LatestVersion() @@ -161,7 +161,6 @@ func CommitTrees( } func RollBackTrees( - pool *ants.Pool, version uint64, accountTree bsmt.SparseMerkleTree, assetTrees *AssetTreeCache, @@ -174,7 +173,7 @@ func RollBackTrees( defer close(errChan) ver := bsmt.Version(version) - err := pool.Submit(func() { + err := gopool.Submit(func() { if accountTree.LatestVersion() > ver && !accountTree.IsEmpty() { err := accountTree.Rollback(ver) if err != nil { @@ -190,7 +189,7 @@ func RollBackTrees( for _, idx := range assetTreeChanges { err := func(i int64) error { - return pool.Submit(func() { + return gopool.Submit(func() { asset := assetTrees.Get(i) version := asset.RecentVersion() err := asset.Rollback(version) @@ -206,7 +205,7 @@ func RollBackTrees( } } - err = pool.Submit(func() { + err = gopool.Submit(func() { if nftTree.LatestVersion() > ver && !nftTree.IsEmpty() { err := nftTree.Rollback(ver) if err != nil { diff --git a/types/code.go b/types/code.go index b4d89b837..5ee9e86fa 100644 --- a/types/code.go +++ b/types/code.go @@ -48,8 +48,69 @@ var ( CmcNotListedErr = errors.New("cmc not listed") - AppErrInvalidParam = New(20001, "invalid param: ") - AppErrInvalidTxField = New(20002, "invalid tx field: ") + AppErrInvalidParam = New(20001, "invalid param: ") + AppErrInvalidTxField = New(20002, "invalid tx field: ") + + AppErrInvalidExpireTime = New(21000, "invalid expired time") + AppErrInvalidGasFeeAmount = New(21001, "invalid gas fee amount") + AppErrBalanceNotEnough = New(21002, "balance is not enough") + AppErrInvalidTreasuryRate = New(21003, "invalid treasury rate") + AppErrInvalidCallDataHash = New(21004, "invalid calldata hash") + AppErrInvalidToAddress = New(21005, "invalid toAddress") + + // Account + AppErrAccountNotFound = New(21100, "account not found") + AppErrAccountNonceNotFound = New(21101, "account nonce not found") + AppErrInvalidAccountIndex = New(21102, "invalid account index") + AppErrInvalidNonce = New(21103, "invalid nonce") + AppErrInvalidGasFeeAccount = New(21104, "invalid gas fee account") + AppErrInvalidToAccountNameHash = New(21105, "invalid ToAccountNameHash") + AppErrAccountNameAlreadyRegistered = New(21106, "invalid account name, already registered") + + // Asset + AppErrAssetNotFound = New(21200, "asset not found") + AppErrInvalidAssetId = New(21201, "invalid asset id") + AppErrInvalidGasFeeAsset = New(21202, "invalid gas fee asset") + AppErrInvalidAssetAmount = New(21203, "invalid asset amount") + + // Block + AppErrBlockNotFound = New(21300, "block not found") + AppErrInvalidBlockHeight = New(21301, "invalid block height") + + // Tx + AppErrPoolTxNotFound = New(21400, "pool tx not found") + AppErrInvalidTxInfo = New(21401, "invalid tx info") + + // Offer + AppErrInvalidOfferType = New(21500, "invalid offer type") + AppErrInvalidOfferState = New(21501, "invalid offer state, already canceled or finalized") + AppErrInvalidOfferId = New(21502, "invalid offer id") + AppErrInvalidListTime = New(21503, "invalid listed time") + AppErrInvalidBuyOffer = New(21504, "invalid buy offer") + AppErrInvalidSellOffer = New(21505, "invalid sell offer") + AppErrSameBuyerAndSeller = New(21506, "same buyer and seller") + AppErrBuyOfferMismatchSellOffer = New(21506, "buy offer mismatches sell offer") + AppErrInvalidBuyOfferExpireTime = New(21507, "invalid BuyOffer.ExpiredAt") + AppErrInvalidSellOfferExpireTime = New(21508, "invalid SellOffer.ExpiredAt") + AppErrSellerBalanceNotEnough = New(21508, "seller balance is not enough") + AppErrBuyerBalanceNotEnough = New(21509, "buyer balance is not enough") + AppErrSellerNotOwner = New(21510, "seller is not owner") + AppErrInvalidSellOfferState = New(21511, "invalid sell offer state, already canceled or finalized") + AppErrInvalidBuyOfferState = New(21512, "invalid buy offer state, already canceled or finalized") + AppErrInvalidAssetOfOffer = New(21513, "invalid asset of offer") + + // Nft + AppErrNftAlreadyExist = New(21600, "invalid nft index, already exist") + AppErrInvalidNftContenthash = New(21601, "invalid nft content hash") + AppErrNotNftOwner = New(21602, "account is not owner of the nft") + AppErrInvalidNftIndex = New(21603, "invalid nft index") + AppErrNftNotFound = New(21604, "nft not found") + + // Collection + AppErrInvalidCollectionId = New(21700, "invalid collection id") + AppErrInvalidCollectionName = New(21701, "invalid collection name") + AppErrInvalidIntroduction = New(21702, "invalid introduction") + AppErrInvalidGasAsset = New(25003, "invalid gas asset") AppErrInvalidTxType = New(25004, "invalid tx type") AppErrTooManyTxs = New(25005, "too many pending txs") diff --git a/types/constant.go b/types/constant.go index 6a9f375ff..d56e8fc04 100644 --- a/types/constant.go +++ b/types/constant.go @@ -45,8 +45,9 @@ const ( NilExpiredAt = math.MaxInt64 NilAssetAmount = "0" - GasAccount = int64(1) - BNBAssetId = 0 + GasAccount = int64(1) + BNBAssetId = 0 + BUSDAssetId = 1 ) var ( diff --git a/types/system.go b/types/system.go index 450aa135f..1cab8aef4 100644 --- a/types/system.go +++ b/types/system.go @@ -39,5 +39,6 @@ const ( ) var ( - ZeroBigInt = big.NewInt(0) + ZeroBigInt = big.NewInt(0) + EmptyAccountAssetId = int64(0) )