diff --git a/Makefile b/Makefile index 28cb8b955..f62c5d2d0 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,7 @@ go_clean_deps: ## Runs `go mod tidy` && `go mod vendor` .PHONY: go_lint go_lint: ## Run all linters that are triggered by the CI pipeline - golangci-lint run ./... + docker run -t --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.51.1 golangci-lint run -v .PHONY: gofmt gofmt: ## Format all the .go files in the project in place. @@ -222,7 +222,7 @@ mockgen: clean_mocks ## Use `mockgen` to generate mocks used for testing purpose $(eval modules_dir = "shared/modules") go generate ./${modules_dir} echo "Mocks generated in ${modules_dir}/mocks" - + $(eval DIRS = p2p persistence) for dir in $(DIRS); do \ echo "Processing $$dir mocks..."; \ @@ -230,7 +230,7 @@ mockgen: clean_mocks ## Use `mockgen` to generate mocks used for testing purpose go generate ./${dir_name}/...; \ echo "$$dir mocks generated in $$dir/types/mocks"; \ done - + # TODO(team): Tested locally with `protoc` version `libprotoc 3.19.4`. In the near future, only the Dockerfiles will be used to compile protos. .PHONY: protogen_show @@ -248,9 +248,10 @@ PROTOC_SHARED = $(PROTOC) -I=./shared .PHONY: protogen_local protogen_local: go_protoc-go-inject-tag ## Generate go structures for all of the protobufs # Shared - $(PROTOC) -I=./shared/core/types/proto --go_out=./shared/core/types ./shared/core/types/proto/*.proto - $(PROTOC) -I=./shared/messaging/proto --go_out=./shared/messaging ./shared/messaging/proto/*.proto - $(PROTOC) -I=./shared/codec/proto --go_out=./shared/codec ./shared/codec/proto/*.proto + $(PROTOC) -I=./shared/core/types/proto --go_out=./shared/core/types ./shared/core/types/proto/*.proto + $(PROTOC) -I=./shared/modules/types/proto --go_out=./shared/modules/types ./shared/modules/types/proto/*.proto + $(PROTOC) -I=./shared/messaging/proto --go_out=./shared/messaging ./shared/messaging/proto/*.proto + $(PROTOC) -I=./shared/codec/proto --go_out=./shared/codec ./shared/codec/proto/*.proto # Runtime $(PROTOC) -I=./runtime/configs/types/proto --go_out=./runtime/configs/types ./runtime/configs/types/proto/*.proto @@ -260,8 +261,6 @@ protogen_local: go_protoc-go-inject-tag ## Generate go structures for all of the # Persistence $(PROTOC_SHARED) -I=./persistence/indexer/proto --go_out=./persistence/indexer ./persistence/indexer/proto/*.proto - $(PROTOC_SHARED) -I=./persistence/proto --go_out=./persistence/types ./persistence/proto/*.proto - protoc-go-inject-tag -input="./persistence/types/*.pb.go" # Utility $(PROTOC_SHARED) -I=./utility/types/proto --go_out=./utility/types ./utility/types/proto/*.proto @@ -497,3 +496,7 @@ check_cross_module_imports: ## Lists cross-module imports echo "-----------------------" echo "runtime:\n" grep ${exclude_common} --exclude-dir=runtime -r "github.com/pokt-network/pocket/runtime" || echo "✅ OK!" + +.PHONY: send_local_tx +send_local_tx: ## A hardcoded send tx to make LocalNet debugging easier + p1 --path_to_private_key_file=build/pkeys/val1.json Account Send 6f66574e1f50f0ef72dff748c3f11b9e0e89d32a 67eb3f0a50ae459fecf666be0e93176e92441317 1000 \ No newline at end of file diff --git a/app/client/cli/utils.go b/app/client/cli/utils.go index b379e9c5b..a891d616f 100644 --- a/app/client/cli/utils.go +++ b/app/client/cli/utils.go @@ -94,7 +94,7 @@ func prepareTxBytes(msg typesUtil.Message, pk crypto.PrivateKey) ([]byte, error) Nonce: fmt.Sprintf("%d", crypto.GetNonce()), } - signBytes, err := tx.SignBytes() + signBytes, err := tx.SignableBytes() if err != nil { return nil, err } @@ -151,7 +151,7 @@ func validateStakeAmount(amount string) error { } sr := big.NewInt(stakingRecommendationAmount) - if typesUtil.BigIntLessThan(am, sr) { + if converters.BigIntLessThan(am, sr) { fmt.Printf("The amount you are staking for is below the recommendation of %d POKT, would you still like to continue? y|n\n", sr.Div(sr, oneMillion).Int64()) if !confirmation(pwd) { return fmt.Errorf("aborted") diff --git a/app/client/doc/CHANGELOG.md b/app/client/doc/CHANGELOG.md index b3030f879..0ce6b73a4 100644 --- a/app/client/doc/CHANGELOG.md +++ b/app/client/doc/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.0.0.10] - 2023-02-07 -- Added GITHUB_WIKI tags where it was missing +- Added GH_WIKI tags where it was missing ## [0.0.0.9] - 2023-02-06 diff --git a/docs/demos/iteration_3_end_to_end_tx.md b/docs/demos/iteration_3_end_to_end_tx.md index e2c252c36..ff99977c4 100644 --- a/docs/demos/iteration_3_end_to_end_tx.md +++ b/docs/demos/iteration_3_end_to_end_tx.md @@ -19,7 +19,7 @@ The first video of this demo can be accessed [here](https://drive.google.com/file/d/1IOrzq-XJP04BJjyqPPpPu873aSfwrnur/view?usp=sharing). -Screenshot 2022-12-05 at 9 02 28 PM +![Demo Goals](https://user-images.githubusercontent.com/1892194/205820691-26e801e4-ff79-4132-a7a1-358860ca2335.png) ### Features @@ -91,7 +91,7 @@ select * from pool; ### Available Commands -Show all the commands available in the CLI: +Show all the commands available in the CLI by running `p1` or: ```bash go run app/client/*.go diff --git a/docs/development/README.md b/docs/development/README.md index e1f2d11bc..333f8f246 100644 --- a/docs/development/README.md +++ b/docs/development/README.md @@ -12,6 +12,7 @@ Please note that this repository is under very active development and breaking c - [Running LocalNet](#running-localnet) - [\[Advanced\] Kubernetes](#advanced-kubernetes) - [\[Basic\] Docker Compose](#basic-docker-compose) + - [TODO: Improvements to be added by the core team](#todo-improvements-to-be-added-by-the-core-team) - [Profiling](#profiling) - [Code Organization](#code-organization) - [Maintaining Documentation](#maintaining-documentation) @@ -217,6 +218,22 @@ make client_start && make client_connect ✔ TriggerNextView # Let it rip! ``` +9. Send a transaction (and trigger the next view) + +```bash + make send_local_tx +``` + +### TODO: Improvements to be added by the core team + +A lot of features have been added since this doc was first added. See `docs/demo`. We should update it to: + +1. Show k8s LocalNet +2. Add more details related to transactions +3. Add details related to the keybase +4. Add state sync tooling +5. Add P2P tooling + ### Profiling If you need to profile the node for CPU and/or memory usage, you can use the `pprof` tool. diff --git a/persistence/actor.go b/persistence/actor.go index 41e09fc8e..d6fc24245 100644 --- a/persistence/actor.go +++ b/persistence/actor.go @@ -6,8 +6,8 @@ import ( ) // TODO (#399): All of the functions below following a structure similar to `GetAll` -// can easily be refactored and condensed into a single function using a generic type or a common -// interface. +// can easily be refactored and condensed into a single function using a generic type or a common +// interface. func (p *PostgresContext) GetAllApps(height int64) (apps []*coreTypes.Actor, err error) { ctx, tx := p.getCtxAndTx() rows, err := tx.Query(ctx, types.ApplicationActor.GetAllQuery(height)) diff --git a/persistence/actor_shared_sql.go b/persistence/actor_shared_sql.go index 7776362a0..e22bcc372 100644 --- a/persistence/actor_shared_sql.go +++ b/persistence/actor_shared_sql.go @@ -8,12 +8,12 @@ import ( "github.com/jackc/pgx/v5" "github.com/pokt-network/pocket/persistence/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/modules" + moduleTypes "github.com/pokt-network/pocket/shared/modules/types" ) // IMPROVE(team): Move this into a proto enum. We are not using `iota` for the time being // for the purpose of being explicit: https://github.com/pokt-network/pocket/pull/140#discussion_r939731342 -// TODO Cleanup with #149 +// TODO: Consolidate with proto enum in the utility module const ( UndefinedStakingStatus = int32(0) UnstakingStatus = int32(1) @@ -174,7 +174,7 @@ func (p *PostgresContext) UpdateActor(actorSchema types.ProtocolActorSchema, act return nil } -func (p *PostgresContext) GetActorsReadyToUnstake(actorSchema types.ProtocolActorSchema, height int64) (actors []modules.IUnstakingActor, err error) { +func (p *PostgresContext) GetActorsReadyToUnstake(actorSchema types.ProtocolActorSchema, height int64) (actors []*moduleTypes.UnstakingActor, err error) { ctx, tx := p.getCtxAndTx() rows, err := tx.Query(ctx, actorSchema.GetReadyToUnstakeQuery(height)) @@ -184,15 +184,11 @@ func (p *PostgresContext) GetActorsReadyToUnstake(actorSchema types.ProtocolActo defer rows.Close() for rows.Next() { - unstakingActor := &types.UnstakingActor{} - var addr, output, stakeAmount string - if err = rows.Scan(&addr, &stakeAmount, &output); err != nil { + actor := &moduleTypes.UnstakingActor{} + if err = rows.Scan(&actor.Address, &actor.StakeAmount, &actor.OutputAddress); err != nil { return } - unstakingActor.SetAddress(addr) - unstakingActor.SetStakeAmount(stakeAmount) - unstakingActor.SetOutputAddress(output) - actors = append(actors, unstakingActor) + actors = append(actors, actor) } return } @@ -244,7 +240,6 @@ func (p *PostgresContext) SetActorStatusAndUnstakingHeightIfPausedBefore(actorSc if err != nil { return err } - _, err = tx.Exec(ctx, actorSchema.UpdateUnstakedHeightIfPausedBeforeQuery(pausedBeforeHeight, unstakingHeight, currentHeight)) return err } diff --git a/persistence/application.go b/persistence/application.go index 11a37bb54..97608da85 100644 --- a/persistence/application.go +++ b/persistence/application.go @@ -5,7 +5,7 @@ import ( "github.com/pokt-network/pocket/persistence/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/modules" + moduleTypes "github.com/pokt-network/pocket/shared/modules/types" ) func (p *PostgresContext) GetAppExists(address []byte, height int64) (exists bool, err error) { @@ -61,7 +61,7 @@ func (p *PostgresContext) SetAppStakeAmount(address []byte, stakeAmount string) return p.setActorStakeAmount(types.ApplicationActor, address, stakeAmount) } -func (p *PostgresContext) GetAppsReadyToUnstake(height int64, _ int32) ([]modules.IUnstakingActor, error) { +func (p *PostgresContext) GetAppsReadyToUnstake(height int64, _ int32) ([]*moduleTypes.UnstakingActor, error) { return p.GetActorsReadyToUnstake(types.ApplicationActor, height) } diff --git a/persistence/block.go b/persistence/block.go index 297cee1e5..88bd3aff8 100644 --- a/persistence/block.go +++ b/persistence/block.go @@ -30,7 +30,7 @@ func (p *persistenceModule) TransactionExists(transactionHash string) (bool, err func (p *PostgresContext) GetMinimumBlockHeight() (latestHeight uint64, err error) { ctx, tx := p.getCtxAndTx() - err = tx.QueryRow(ctx, types.GetMinimumlockHeightQuery()).Scan(&latestHeight) + err = tx.QueryRow(ctx, types.GetMinimumBlockHeightQuery()).Scan(&latestHeight) return } diff --git a/persistence/docs/CHANGELOG.md b/persistence/docs/CHANGELOG.md index d96cf4c32..764f26c16 100644 --- a/persistence/docs/CHANGELOG.md +++ b/persistence/docs/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.34] - 2023-02-14 + +- Remove `IUnstakingActor` and use `UnstakingActor` directly; guideline for removing future unnecessary types (e.g. TxResult) +- Typo in `GetMinimumBlockHeightQuery` +- Reduce unnecessary `string` <-> `[]byte` conversion in a few places +- Fix bug in `updateUnstakedHeightIfPausedBefore` that was unstaking all actors + ## [0.0.0.33] - 2023-02-09 - Added mock generation to the `kvstore/kvstore.go`. diff --git a/persistence/fisherman.go b/persistence/fisherman.go index c96b5c047..9664d4694 100644 --- a/persistence/fisherman.go +++ b/persistence/fisherman.go @@ -5,7 +5,7 @@ import ( "github.com/pokt-network/pocket/persistence/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/modules" + moduleTypes "github.com/pokt-network/pocket/shared/modules/types" ) func (p *PostgresContext) GetFishermanExists(address []byte, height int64) (exists bool, err error) { @@ -58,7 +58,7 @@ func (p *PostgresContext) SetFishermanStakeAmount(address []byte, stakeAmount st return p.setActorStakeAmount(types.FishermanActor, address, stakeAmount) } -func (p *PostgresContext) GetFishermenReadyToUnstake(height int64, status int32) ([]modules.IUnstakingActor, error) { +func (p *PostgresContext) GetFishermenReadyToUnstake(height int64, status int32) ([]*moduleTypes.UnstakingActor, error) { return p.GetActorsReadyToUnstake(types.FishermanActor, height) } diff --git a/persistence/indexer/indexer_test.go b/persistence/indexer/indexer_test.go index 4783ab30e..f09f84d31 100644 --- a/persistence/indexer/indexer_test.go +++ b/persistence/indexer/indexer_test.go @@ -34,7 +34,7 @@ func FuzzTxIndexer(f *testing.F) { f.Fuzz(func(t *testing.T, op string) { // seed random - rand.Seed(int64(time.Now().Nanosecond())) + rand.Seed(int64(time.Now().Nanosecond())) //nolint:staticcheck // G404 - Weak random source is okay here // set height ordering to descending 50% of time isDescending := rand.Intn(2) == 0 //nolint:gosec // G404 - Weak random source is okay in unit tests // select a height 0 - 9 to index @@ -266,7 +266,7 @@ func randomErr() (code int32, err string) { //nolint:gosec // G404 - Weak random source is okay in unit tests func randLetterBytes() []byte { randBytes := make([]byte, 50) - rand.Read(randBytes) + rand.Read(randBytes) //nolint:staticcheck // G404 - Weak random source is okay here return randBytes } diff --git a/persistence/proto/unstaking.proto b/persistence/proto/unstaking.proto deleted file mode 100644 index 42848b3d8..000000000 --- a/persistence/proto/unstaking.proto +++ /dev/null @@ -1,10 +0,0 @@ -syntax = "proto3"; -package persistence; - -option go_package = "github.com/pokt-network/pocket/persistence/types"; - -message UnstakingActor { - bytes address = 1; - string stake_amount = 2; - bytes output_address = 3; -} \ No newline at end of file diff --git a/persistence/service_node.go b/persistence/service_node.go index 65275d3c9..4756e6198 100644 --- a/persistence/service_node.go +++ b/persistence/service_node.go @@ -5,7 +5,7 @@ import ( "github.com/pokt-network/pocket/persistence/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/modules" + moduleTypes "github.com/pokt-network/pocket/shared/modules/types" ) func (p *PostgresContext) GetServiceNodeExists(address []byte, height int64) (exists bool, err error) { @@ -62,7 +62,7 @@ func (p *PostgresContext) GetServiceNodeCount(chain string, height int64) (int, panic("GetServiceNodeCount not implemented") } -func (p *PostgresContext) GetServiceNodesReadyToUnstake(height int64, status int32) ([]modules.IUnstakingActor, error) { +func (p *PostgresContext) GetServiceNodesReadyToUnstake(height int64, status int32) ([]*moduleTypes.UnstakingActor, error) { return p.GetActorsReadyToUnstake(types.ServiceNodeActor, height) } diff --git a/persistence/test/application_test.go b/persistence/test/application_test.go index 3c0bd28fd..38fa56841 100644 --- a/persistence/test/application_test.go +++ b/persistence/test/application_test.go @@ -122,13 +122,13 @@ func TestGetAppsReadyToUnstake(t *testing.T) { unstakingApps, err := db.GetAppsReadyToUnstake(0, persistence.UnstakingStatus) require.NoError(t, err) require.Equal(t, 1, len(unstakingApps), "wrong number of actors ready to unstake at height 0") - require.Equal(t, app.Address, hex.EncodeToString(unstakingApps[0].GetAddress()), "unexpected application actor returned") + require.Equal(t, app.Address, unstakingApps[0].GetAddress(), "unexpected application actor returned") // Check unstaking apps at height 1 unstakingApps, err = db.GetAppsReadyToUnstake(1, persistence.UnstakingStatus) require.NoError(t, err) require.Equal(t, 2, len(unstakingApps), "wrong number of actors ready to unstake at height 1") - require.ElementsMatch(t, [][]byte{addrBz2, addrBz3}, [][]byte{unstakingApps[0].GetAddress(), unstakingApps[1].GetAddress()}) + require.ElementsMatch(t, []string{app2.Address, app3.Address}, []string{unstakingApps[0].Address, unstakingApps[1].Address}) } func TestGetAppStatus(t *testing.T) { diff --git a/persistence/test/fisherman_test.go b/persistence/test/fisherman_test.go index 057b62a9d..0f513ac30 100644 --- a/persistence/test/fisherman_test.go +++ b/persistence/test/fisherman_test.go @@ -130,13 +130,13 @@ func TestGetFishermenReadyToUnstake(t *testing.T) { unstakingFishermen, err := db.GetFishermenReadyToUnstake(0, persistence.UnstakingStatus) require.NoError(t, err) require.Equal(t, 1, len(unstakingFishermen), "wrong number of actors ready to unstake at height 0") - require.Equal(t, fisherman.Address, hex.EncodeToString(unstakingFishermen[0].GetAddress()), "unexpected fishermanlication actor returned") + require.Equal(t, fisherman.Address, unstakingFishermen[0].GetAddress(), "unexpected fishermanlication actor returned") // Check unstaking fishermans at height 1 unstakingFishermen, err = db.GetFishermenReadyToUnstake(1, persistence.UnstakingStatus) require.NoError(t, err) require.Equal(t, 2, len(unstakingFishermen), "wrong number of actors ready to unstake at height 1") - require.ElementsMatch(t, [][]byte{addrBz2, addrBz3}, [][]byte{unstakingFishermen[0].GetAddress(), unstakingFishermen[1].GetAddress()}) + require.ElementsMatch(t, []string{fisherman2.Address, fisherman3.Address}, []string{unstakingFishermen[0].Address, unstakingFishermen[1].Address}) } func TestGetFishermanStatus(t *testing.T) { diff --git a/persistence/test/service_node_test.go b/persistence/test/service_node_test.go index 98b483f02..8eb8e7d38 100644 --- a/persistence/test/service_node_test.go +++ b/persistence/test/service_node_test.go @@ -130,13 +130,13 @@ func TestGetServiceNodesReadyToUnstake(t *testing.T) { unstakingServiceNodes, err := db.GetServiceNodesReadyToUnstake(0, persistence.UnstakingStatus) require.NoError(t, err) require.Equal(t, 1, len(unstakingServiceNodes), "wrong number of actors ready to unstake at height 0") - require.Equal(t, serviceNode.Address, hex.EncodeToString(unstakingServiceNodes[0].GetAddress()), "unexpected serviceNodelication actor returned") + require.Equal(t, serviceNode.Address, unstakingServiceNodes[0].Address, "unexpected serviceNodelication actor returned") // Check unstaking serviceNodes at height 1 unstakingServiceNodes, err = db.GetServiceNodesReadyToUnstake(1, persistence.UnstakingStatus) require.NoError(t, err) require.Equal(t, 2, len(unstakingServiceNodes), "wrong number of actors ready to unstake at height 1") - require.ElementsMatch(t, [][]byte{addrBz2, addrBz3}, [][]byte{unstakingServiceNodes[0].GetAddress(), unstakingServiceNodes[1].GetAddress()}) + require.ElementsMatch(t, []string{serviceNode2.Address, serviceNode3.Address}, []string{unstakingServiceNodes[0].Address, unstakingServiceNodes[1].Address}) } func TestGetServiceNodeStatus(t *testing.T) { diff --git a/persistence/test/setup_test.go b/persistence/test/setup_test.go index 3671374cb..7c80f916f 100644 --- a/persistence/test/setup_test.go +++ b/persistence/test/setup_test.go @@ -21,6 +21,7 @@ import ( coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/messaging" "github.com/pokt-network/pocket/shared/modules" + moduleTypes "github.com/pokt-network/pocket/shared/modules/types" "github.com/stretchr/testify/require" "golang.org/x/exp/slices" ) @@ -42,8 +43,8 @@ var ( StakeToUpdate = converters.BigIntToString((&big.Int{}).Add(DefaultStakeBig, DefaultDeltaBig)) DefaultStakeStatus = int32(persistence.StakedStatus) - DefaultPauseHeight = int64(-1) - DefaultUnstakingHeight = int64(-1) + DefaultPauseHeight = int64(-1) // pauseHeight=-1 means not paused + DefaultUnstakingHeight = int64(-1) // pauseHeight=-1 means not unstaking OlshanskyURL = "https://olshansky.info" OlshanskyChains = []string{"OLSH"} @@ -235,8 +236,8 @@ func fuzzSingleProtocolActor( if originalActor.UnstakingHeight != db.Height { // Not ready to unstake require.Nil(t, unstakingActors) } else { - idx := slices.IndexFunc(unstakingActors, func(a modules.IUnstakingActor) bool { - return originalActor.Address == hex.EncodeToString(a.GetAddress()) + idx := slices.IndexFunc(unstakingActors, func(a *moduleTypes.UnstakingActor) bool { + return originalActor.Address == a.Address }) require.NotEqual(t, idx, -1, fmt.Sprintf("actor that is unstaking was not found %+v", originalActor)) } @@ -285,8 +286,8 @@ func fuzzSingleProtocolActor( newActor, err := getTestActor(db, originalActor.Address) require.NoError(t, err) - if db.Height > originalActor.PausedHeight { // isPausedAndReadyToUnstake - require.Equal(t, newActor.UnstakingHeight, newUnstakingHeight, "setPausedToUnstaking") + if db.Height > originalActor.PausedHeight && originalActor.PausedHeight != DefaultPauseHeight { // isPausedAndReadyToUnstake + require.Equal(t, newUnstakingHeight, newActor.UnstakingHeight, "setPausedToUnstaking") } case "GetActorOutputAddr": outputAddr, err := db.GetActorOutputAddress(protocolActorSchema, addr, db.Height) diff --git a/persistence/test/validator_test.go b/persistence/test/validator_test.go index 10584ad36..d5df1f97a 100644 --- a/persistence/test/validator_test.go +++ b/persistence/test/validator_test.go @@ -127,13 +127,14 @@ func TestGetValidatorsReadyToUnstake(t *testing.T) { unstakingValidators, err := db.GetValidatorsReadyToUnstake(0, persistence.UnstakingStatus) require.NoError(t, err) require.Equal(t, 1, len(unstakingValidators), "wrong number of actors ready to unstake at height 0") - require.Equal(t, validator.Address, hex.EncodeToString(unstakingValidators[0].GetAddress()), "unexpected validatorlication actor returned") + + require.Equal(t, validator.Address, unstakingValidators[0].GetAddress(), "unexpected unstaking validator returned") // Check unstaking validators at height 1 unstakingValidators, err = db.GetValidatorsReadyToUnstake(1, persistence.UnstakingStatus) require.NoError(t, err) require.Equal(t, 2, len(unstakingValidators), "wrong number of actors ready to unstake at height 1") - require.ElementsMatch(t, [][]byte{addrBz2, addrBz3}, [][]byte{unstakingValidators[0].GetAddress(), unstakingValidators[1].GetAddress()}) + require.ElementsMatch(t, []string{validator2.Address, validator3.Address}, []string{unstakingValidators[0].Address, unstakingValidators[1].Address}) } func TestGetValidatorStatus(t *testing.T) { diff --git a/persistence/types/actor_shared_sql.go b/persistence/types/actor_shared_sql.go index b785bc191..958051179 100644 --- a/persistence/types/actor_shared_sql.go +++ b/persistence/types/actor_shared_sql.go @@ -225,7 +225,7 @@ func updateUnstakedHeightIfPausedBefore(actorSpecificParam string, unstakingHeig INSERT INTO %s (address, public_key, staked_tokens, %s, output_address, paused_height, unstaking_height, height) ( SELECT address, public_key, staked_tokens, %s, output_address, paused_height, %d, %d - FROM %s WHERE paused_height<%d + FROM %s WHERE paused_height<%d AND paused_height>=0 AND (height,address) IN (SELECT MAX(height),address from %s GROUP BY address) ) ON CONFLICT ON CONSTRAINT %s diff --git a/persistence/types/block.go b/persistence/types/block.go index f96951d27..5f85f4ce5 100644 --- a/persistence/types/block.go +++ b/persistence/types/block.go @@ -29,7 +29,7 @@ func GetMaximumBlockHeightQuery() string { return fmt.Sprintf(`SELECT MAX(height) FROM %s`, BlockTableName) } -func GetMinimumlockHeightQuery() string { +func GetMinimumBlockHeightQuery() string { return fmt.Sprintf(`SELECT MIN(height) FROM %s`, BlockTableName) } diff --git a/persistence/types/unstaking.go b/persistence/types/unstaking.go deleted file mode 100644 index 9ba09358e..000000000 --- a/persistence/types/unstaking.go +++ /dev/null @@ -1,30 +0,0 @@ -package types - -import ( - "encoding/hex" - "log" - - shared "github.com/pokt-network/pocket/shared/modules" -) - -var _ shared.IUnstakingActor = &UnstakingActor{} - -func (x *UnstakingActor) SetAddress(address string) { // TODO (team) convert address to string #149 - s, err := hex.DecodeString(address) - if err != nil { - log.Fatal(err) - } - x.Address = s -} - -func (x *UnstakingActor) SetStakeAmount(stakeAmount string) { - x.StakeAmount = stakeAmount -} - -func (x *UnstakingActor) SetOutputAddress(address string) { - s, err := hex.DecodeString(address) - if err != nil { - log.Fatal(err) - } - x.OutputAddress = s -} diff --git a/persistence/validator.go b/persistence/validator.go index 60c988e2d..356659872 100644 --- a/persistence/validator.go +++ b/persistence/validator.go @@ -5,7 +5,7 @@ import ( "github.com/pokt-network/pocket/persistence/types" coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/modules" + moduleTypes "github.com/pokt-network/pocket/shared/modules/types" ) func (p *PostgresContext) GetValidatorExists(address []byte, height int64) (exists bool, err error) { @@ -55,7 +55,7 @@ func (p *PostgresContext) SetValidatorStakeAmount(address []byte, stakeAmount st return p.setActorStakeAmount(types.ValidatorActor, address, stakeAmount) } -func (p *PostgresContext) GetValidatorsReadyToUnstake(height int64, status int32) ([]modules.IUnstakingActor, error) { +func (p *PostgresContext) GetValidatorsReadyToUnstake(height int64, status int32) ([]*moduleTypes.UnstakingActor, error) { return p.GetActorsReadyToUnstake(types.ValidatorActor, height) } diff --git a/runtime/docs/CHANGELOG.md b/runtime/docs/CHANGELOG.md index 016460003..076a76b85 100644 --- a/runtime/docs/CHANGELOG.md +++ b/runtime/docs/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.17] - 2023-02-14 + +- Move shared utils (e.g. `BitIngToString`) to the `converters` package +- Remove `CleanupTest` + ## [0.0.0.16] - 2023-02-09 - Update runtime consensus config with bool server mode variable diff --git a/runtime/test_artifacts/gov.go b/runtime/test_artifacts/gov.go index 77b088daa..a6e09f105 100644 --- a/runtime/test_artifacts/gov.go +++ b/runtime/test_artifacts/gov.go @@ -4,8 +4,8 @@ import ( "math/big" "github.com/pokt-network/pocket/runtime/genesis" - "github.com/pokt-network/pocket/utility/types" + "github.com/pokt-network/pocket/shared/converters" "github.com/pokt-network/pocket/shared/crypto" ) @@ -16,25 +16,25 @@ var DefaultParamsOwner, _ = crypto.NewPrivateKey("ff538589deb7f28bbce1ba68b37d2e func DefaultParams() *genesis.Params { return &genesis.Params{ BlocksPerSession: 4, - AppMinimumStake: types.BigIntToString(big.NewInt(15000000000)), + AppMinimumStake: converters.BigIntToString(big.NewInt(15000000000)), AppMaxChains: 15, AppBaselineStakeRate: 100, AppStakingAdjustment: 0, AppUnstakingBlocks: 2016, AppMinimumPauseBlocks: 4, AppMaxPauseBlocks: 672, - ServiceNodeMinimumStake: types.BigIntToString(big.NewInt(15000000000)), + ServiceNodeMinimumStake: converters.BigIntToString(big.NewInt(15000000000)), ServiceNodeMaxChains: 15, ServiceNodeUnstakingBlocks: 2016, ServiceNodeMinimumPauseBlocks: 4, ServiceNodeMaxPauseBlocks: 672, ServiceNodesPerSession: 24, - FishermanMinimumStake: types.BigIntToString(big.NewInt(15000000000)), + FishermanMinimumStake: converters.BigIntToString(big.NewInt(15000000000)), FishermanMaxChains: 15, FishermanUnstakingBlocks: 2016, FishermanMinimumPauseBlocks: 4, FishermanMaxPauseBlocks: 672, - ValidatorMinimumStake: types.BigIntToString(big.NewInt(15000000000)), + ValidatorMinimumStake: converters.BigIntToString(big.NewInt(15000000000)), ValidatorUnstakingBlocks: 2016, ValidatorMinimumPauseBlocks: 4, ValidatorMaxPauseBlocks: 672, @@ -43,32 +43,32 @@ func DefaultParams() *genesis.Params { ProposerPercentageOfFees: 10, MissedBlocksBurnPercentage: 1, DoubleSignBurnPercentage: 5, - MessageDoubleSignFee: types.BigIntToString(big.NewInt(10000)), - MessageSendFee: types.BigIntToString(big.NewInt(10000)), - MessageStakeFishermanFee: types.BigIntToString(big.NewInt(10000)), - MessageEditStakeFishermanFee: types.BigIntToString(big.NewInt(10000)), - MessageUnstakeFishermanFee: types.BigIntToString(big.NewInt(10000)), - MessagePauseFishermanFee: types.BigIntToString(big.NewInt(10000)), - MessageUnpauseFishermanFee: types.BigIntToString(big.NewInt(10000)), - MessageFishermanPauseServiceNodeFee: types.BigIntToString(big.NewInt(10000)), - MessageTestScoreFee: types.BigIntToString(big.NewInt(10000)), - MessageProveTestScoreFee: types.BigIntToString(big.NewInt(10000)), - MessageStakeAppFee: types.BigIntToString(big.NewInt(10000)), - MessageEditStakeAppFee: types.BigIntToString(big.NewInt(10000)), - MessageUnstakeAppFee: types.BigIntToString(big.NewInt(10000)), - MessagePauseAppFee: types.BigIntToString(big.NewInt(10000)), - MessageUnpauseAppFee: types.BigIntToString(big.NewInt(10000)), - MessageStakeValidatorFee: types.BigIntToString(big.NewInt(10000)), - MessageEditStakeValidatorFee: types.BigIntToString(big.NewInt(10000)), - MessageUnstakeValidatorFee: types.BigIntToString(big.NewInt(10000)), - MessagePauseValidatorFee: types.BigIntToString(big.NewInt(10000)), - MessageUnpauseValidatorFee: types.BigIntToString(big.NewInt(10000)), - MessageStakeServiceNodeFee: types.BigIntToString(big.NewInt(10000)), - MessageEditStakeServiceNodeFee: types.BigIntToString(big.NewInt(10000)), - MessageUnstakeServiceNodeFee: types.BigIntToString(big.NewInt(10000)), - MessagePauseServiceNodeFee: types.BigIntToString(big.NewInt(10000)), - MessageUnpauseServiceNodeFee: types.BigIntToString(big.NewInt(10000)), - MessageChangeParameterFee: types.BigIntToString(big.NewInt(10000)), + MessageDoubleSignFee: converters.BigIntToString(big.NewInt(10000)), + MessageSendFee: converters.BigIntToString(big.NewInt(10000)), + MessageStakeFishermanFee: converters.BigIntToString(big.NewInt(10000)), + MessageEditStakeFishermanFee: converters.BigIntToString(big.NewInt(10000)), + MessageUnstakeFishermanFee: converters.BigIntToString(big.NewInt(10000)), + MessagePauseFishermanFee: converters.BigIntToString(big.NewInt(10000)), + MessageUnpauseFishermanFee: converters.BigIntToString(big.NewInt(10000)), + MessageFishermanPauseServiceNodeFee: converters.BigIntToString(big.NewInt(10000)), + MessageTestScoreFee: converters.BigIntToString(big.NewInt(10000)), + MessageProveTestScoreFee: converters.BigIntToString(big.NewInt(10000)), + MessageStakeAppFee: converters.BigIntToString(big.NewInt(10000)), + MessageEditStakeAppFee: converters.BigIntToString(big.NewInt(10000)), + MessageUnstakeAppFee: converters.BigIntToString(big.NewInt(10000)), + MessagePauseAppFee: converters.BigIntToString(big.NewInt(10000)), + MessageUnpauseAppFee: converters.BigIntToString(big.NewInt(10000)), + MessageStakeValidatorFee: converters.BigIntToString(big.NewInt(10000)), + MessageEditStakeValidatorFee: converters.BigIntToString(big.NewInt(10000)), + MessageUnstakeValidatorFee: converters.BigIntToString(big.NewInt(10000)), + MessagePauseValidatorFee: converters.BigIntToString(big.NewInt(10000)), + MessageUnpauseValidatorFee: converters.BigIntToString(big.NewInt(10000)), + MessageStakeServiceNodeFee: converters.BigIntToString(big.NewInt(10000)), + MessageEditStakeServiceNodeFee: converters.BigIntToString(big.NewInt(10000)), + MessageUnstakeServiceNodeFee: converters.BigIntToString(big.NewInt(10000)), + MessagePauseServiceNodeFee: converters.BigIntToString(big.NewInt(10000)), + MessageUnpauseServiceNodeFee: converters.BigIntToString(big.NewInt(10000)), + MessageChangeParameterFee: converters.BigIntToString(big.NewInt(10000)), AclOwner: DefaultParamsOwner.Address().String(), BlocksPerSessionOwner: DefaultParamsOwner.Address().String(), AppMinimumStakeOwner: DefaultParamsOwner.Address().String(), diff --git a/runtime/test_artifacts/keygenerator/keygen.go b/runtime/test_artifacts/keygenerator/keygen.go index 98634fb6f..c55377051 100644 --- a/runtime/test_artifacts/keygenerator/keygen.go +++ b/runtime/test_artifacts/keygenerator/keygen.go @@ -24,8 +24,8 @@ func GetInstance() *keyGenerator { } func (k *keyGenerator) reset() { - rand.Seed(timestamppb.Now().Seconds) - k.privateKeySeed = rand.Int() //nolint:gosec // G404 - Weak random source is okay here + rand.Seed(timestamppb.Now().Seconds) //nolint:staticcheck // G404 - Weak random source is okay here + k.privateKeySeed = rand.Int() //nolint:gosec // G404 - Weak random source is okay here } func (k *keyGenerator) SetSeed(seed int) (teardown func()) { diff --git a/runtime/test_artifacts/util.go b/runtime/test_artifacts/util.go index 2d80dae5a..f1c46669f 100644 --- a/runtime/test_artifacts/util.go +++ b/runtime/test_artifacts/util.go @@ -12,7 +12,6 @@ import ( "github.com/jackc/pgx/v5" "github.com/ory/dockertest" "github.com/ory/dockertest/docker" - "github.com/pokt-network/pocket/utility" ) const ( @@ -97,6 +96,3 @@ func CleanupPostgresDocker(_ *testing.M, pool *dockertest.Pool, resource *docker log.Fatalf("could not purge resource: %s", err) } } - -// CLEANUP: Remove this since it's no longer used or necessary but make sure remote tests are still passing -func CleanupTest(u *utility.UtilityContext) {} diff --git a/shared/CHANGELOG.md b/shared/CHANGELOG.md index 57d7e9b13..93a9bd97e 100644 --- a/shared/CHANGELOG.md +++ b/shared/CHANGELOG.md @@ -7,9 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.25] - 2023-02-14 + +- Remove shared `ActorTypes` array and use the enum directly +- Reduce the code footprint of the `codec` package & add some TODOs +- Added `UnstakingActor` proto to remove deduplication across modules; adding TECHDEBT to remove altogether one day +- Added clarifying comments to the utility module interface + ## [0.0.0.24] - 2023-02-09 - - Add `ConsensusStateSync` interface that is implemented by the consensus module +- Add `ConsensusStateSync` interface that is implemented by the consensus module ## [0.0.0.23] - 2023-02-07 diff --git a/shared/codec/codec.go b/shared/codec/codec.go index 258858adc..a0f505b31 100644 --- a/shared/codec/codec.go +++ b/shared/codec/codec.go @@ -5,56 +5,43 @@ import ( "google.golang.org/protobuf/types/known/anypb" ) -// TODO: Use generics in place of `proto.Message` in the interface below -// so every caller does not need to do in place casting. -type Codec interface { // TODO (Team) move to shared. Possibly rename +// CONSIDERATION: Use generics in place of `proto.Message` in the interface below so +// every caller does not need to do in place casting. +type Codec interface { Marshal(proto.Message) ([]byte, error) Unmarshal([]byte, proto.Message) error ToAny(proto.Message) (*anypb.Any, error) FromAny(*anypb.Any) (proto.Message, error) + Clone(proto.Message) proto.Message } -var _ Codec = &ProtoCodec{} +var _ Codec = &protoCodec{} -// TODO: Need to define a type like `type ProtoAny anypb.Any` so that we are -// mixing protobufs and a centralized codec structure throughout the codebase. -type ProtoCodec struct{} +// IMPROVE: Need to define a type similar to `type ProtoAny anypb.Any` so that we are +// referencing protobuf specific types (e.g. anypb.Any) anywhere in the codebase. +type protoCodec struct{} -func (p *ProtoCodec) Marshal(message proto.Message) ([]byte, error) { - bz, err := proto.MarshalOptions{Deterministic: true}.Marshal(message) - if err != nil { - return nil, err - } - return bz, nil +func GetCodec() Codec { + return &protoCodec{} } -func (p *ProtoCodec) Unmarshal(bz []byte, message proto.Message) error { - err := proto.Unmarshal(bz, message) - if err != nil { - return err - } - return nil +// IMPROVE: If/when we move Pocket's `Error` type into a separate package, we can return `ErrProtoMarshal` here +func (p *protoCodec) Marshal(msg proto.Message) ([]byte, error) { + return proto.MarshalOptions{Deterministic: true}.Marshal(msg) } -func (p *ProtoCodec) ToAny(message proto.Message) (*anypb.Any, error) { - any, err := anypb.New(message) - if err != nil { - return nil, err - } - return any, nil +func (p *protoCodec) Unmarshal(bz []byte, msg proto.Message) error { + return proto.Unmarshal(bz, msg) } -func (p *ProtoCodec) FromAny(any *anypb.Any) (proto.Message, error) { - msg, err := anypb.UnmarshalNew(any, proto.UnmarshalOptions{}) - if err != nil { - return nil, err - } - return msg, nil +func (p *protoCodec) ToAny(msg proto.Message) (*anypb.Any, error) { + return anypb.New(msg) } -// DISCUSS: Retrieve this from the utility module via the application specific bus? -// There are some parts of the code that does not have access to the bus; -// Example: txIndexer -func GetCodec() Codec { - return &ProtoCodec{} +func (p *protoCodec) FromAny(any *anypb.Any) (proto.Message, error) { + return anypb.UnmarshalNew(any, proto.UnmarshalOptions{}) +} + +func (p *protoCodec) Clone(msg proto.Message) proto.Message { + return proto.Clone(msg) } diff --git a/utility/types/util_test.go b/shared/converters/num_utils_test.go similarity index 73% rename from utility/types/util_test.go rename to shared/converters/num_utils_test.go index d17900c70..9f8ea4851 100644 --- a/utility/types/util_test.go +++ b/shared/converters/num_utils_test.go @@ -1,4 +1,4 @@ -package types +package converters import ( "math/big" @@ -12,7 +12,5 @@ func TestBigIntToString(t *testing.T) { bigIntString := BigIntToString(bigOriginal) bigIntAfter, err := StringToBigInt(bigIntString) require.NoError(t, err) - if bigIntAfter.Cmp(bigOriginal) != 0 { - t.Fatal("unequal after conversion") - } + require.Zero(t, bigIntAfter.Cmp(bigOriginal), "unequal after conversion") } diff --git a/shared/converters/util.go b/shared/converters/util.go index 30a0bb062..666595316 100644 --- a/shared/converters/util.go +++ b/shared/converters/util.go @@ -7,12 +7,12 @@ import ( ) const ( - DefaultDenomination = 10 + defaultDenomination = 10 ) func StringToBigInt(s string) (*big.Int, error) { b := big.Int{} - i, ok := b.SetString(s, DefaultDenomination) + i, ok := b.SetString(s, defaultDenomination) if !ok { return nil, fmt.Errorf("unable to SetString() with base 10") } @@ -20,7 +20,11 @@ func StringToBigInt(s string) (*big.Int, error) { } func BigIntToString(b *big.Int) string { - return b.Text(DefaultDenomination) + return b.Text(defaultDenomination) +} + +func BigIntLessThan(a, b *big.Int) bool { + return a.Cmp(b) == -1 } func HeightFromBytes(heightBz []byte) uint64 { diff --git a/shared/core/types/README.md b/shared/core/types/README.md new file mode 100644 index 000000000..56310b46e --- /dev/null +++ b/shared/core/types/README.md @@ -0,0 +1,7 @@ +# Core Types + +The types defined in this package are responsible for state commitment. + +The field types, values and order of serialization is critical in ensuring the integrity of the blockchain. + +See the [Persistence Specification](https://github.com/pokt-network/pocket-network-protocol/tree/main/persistence) for full details. diff --git a/shared/core/types/actor.go b/shared/core/types/actor.go index 9fabc6176..79fa21999 100644 --- a/shared/core/types/actor.go +++ b/shared/core/types/actor.go @@ -1,5 +1,7 @@ package types +// REFACTOR: Evaluate a way to define an interface for the actors which would enable to easily +// remove all the `switch actorType` statements in the codebase. func (a ActorType) GetName() string { return ActorType_name[int32(a)] } diff --git a/shared/core/types/proto/actor.proto b/shared/core/types/proto/actor.proto index 15bfecdaa..4d8f28c3c 100644 --- a/shared/core/types/proto/actor.proto +++ b/shared/core/types/proto/actor.proto @@ -7,7 +7,7 @@ option go_package = "github.com/pokt-network/pocket/shared/core/types"; enum ActorType { ACTOR_TYPE_UNSPECIFIED = 0; ACTOR_TYPE_APP = 1; - ACTOR_TYPE_SERVICENODE = 2;// TODO: Consider renaming to `servicers` throughout the codebase. + ACTOR_TYPE_SERVICENODE = 2; // TODO: Rename to `servicers` throughout the codebase. ACTOR_TYPE_FISH = 3; ACTOR_TYPE_VAL = 4; } diff --git a/shared/crypto/sha3.go b/shared/crypto/sha3.go index d54cca15b..dd83edc6f 100644 --- a/shared/crypto/sha3.go +++ b/shared/crypto/sha3.go @@ -17,9 +17,7 @@ func SHA3Hash(bz []byte) []byte { return hasher.Sum(nil) } -// GetHashStringFromBytes returns the hexadecimal encoding in string format of the SHA3Hash of the bytes passed in as argument -// -// Typically used to compute a TransactionHash +// GetHashStringFromBytes returns hex(SHA3Hash(bytesArgument)); typically used to compute a TransactionHash func GetHashStringFromBytes(bytes []byte) string { return hex.EncodeToString(SHA3Hash(bytes)) } diff --git a/shared/modules/persistence_module.go b/shared/modules/persistence_module.go index c1b74730d..a5a7b8eb0 100644 --- a/shared/modules/persistence_module.go +++ b/shared/modules/persistence_module.go @@ -7,6 +7,7 @@ import ( "github.com/pokt-network/pocket/runtime/genesis" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/messaging" + moduleTypes "github.com/pokt-network/pocket/shared/modules/types" ) const PersistenceModuleName = "persistence" @@ -105,6 +106,7 @@ type PersistenceWriteContext interface { InsertValidator(address []byte, publicKey []byte, output []byte, paused bool, status int32, serviceURL string, stakedTokens string, pausedHeight int64, unstakingHeight int64) error UpdateValidator(address []byte, serviceURL string, amount string) error SetValidatorStakeAmount(address []byte, stakeAmount string) error + // IMPROVE: Decouple and/or rename these functions SetValidatorUnstakingHeightAndStatus(address []byte, unstakingHeight int64, status int32) error SetValidatorsStatusAndUnstakingHeightIfPausedBefore(pausedBeforeHeight, unstakingHeight int64, status int32) error SetValidatorPauseHeight(address []byte, height int64) error @@ -147,7 +149,7 @@ type PersistenceReadContext interface { GetAllApps(height int64) ([]*coreTypes.Actor, error) GetAppExists(address []byte, height int64) (exists bool, err error) GetAppStakeAmount(height int64, address []byte) (string, error) - GetAppsReadyToUnstake(height int64, status int32) (apps []IUnstakingActor, err error) + GetAppsReadyToUnstake(height int64, status int32) (apps []*moduleTypes.UnstakingActor, err error) GetAppStatus(address []byte, height int64) (status int32, err error) GetAppPauseHeightIfExists(address []byte, height int64) (int64, error) GetAppOutputAddress(operator []byte, height int64) (output []byte, err error) @@ -156,7 +158,7 @@ type PersistenceReadContext interface { GetAllServiceNodes(height int64) ([]*coreTypes.Actor, error) GetServiceNodeExists(address []byte, height int64) (exists bool, err error) GetServiceNodeStakeAmount(height int64, address []byte) (string, error) - GetServiceNodesReadyToUnstake(height int64, status int32) (serviceNodes []IUnstakingActor, err error) + GetServiceNodesReadyToUnstake(height int64, status int32) (serviceNodes []*moduleTypes.UnstakingActor, err error) GetServiceNodeStatus(address []byte, height int64) (status int32, err error) GetServiceNodePauseHeightIfExists(address []byte, height int64) (int64, error) GetServiceNodeOutputAddress(operator []byte, height int64) (output []byte, err error) @@ -166,7 +168,7 @@ type PersistenceReadContext interface { GetAllFishermen(height int64) ([]*coreTypes.Actor, error) GetFishermanExists(address []byte, height int64) (exists bool, err error) GetFishermanStakeAmount(height int64, address []byte) (string, error) - GetFishermenReadyToUnstake(height int64, status int32) (fishermen []IUnstakingActor, err error) + GetFishermenReadyToUnstake(height int64, status int32) (fishermen []*moduleTypes.UnstakingActor, err error) GetFishermanStatus(address []byte, height int64) (status int32, err error) GetFishermanPauseHeightIfExists(address []byte, height int64) (int64, error) GetFishermanOutputAddress(operator []byte, height int64) (output []byte, err error) @@ -175,7 +177,7 @@ type PersistenceReadContext interface { GetAllValidators(height int64) ([]*coreTypes.Actor, error) GetValidatorExists(address []byte, height int64) (exists bool, err error) GetValidatorStakeAmount(height int64, address []byte) (string, error) - GetValidatorsReadyToUnstake(height int64, status int32) (validators []IUnstakingActor, err error) + GetValidatorsReadyToUnstake(height int64, status int32) (validators []*moduleTypes.UnstakingActor, err error) GetValidatorStatus(address []byte, height int64) (status int32, err error) GetValidatorPauseHeightIfExists(address []byte, height int64) (int64, error) GetValidatorOutputAddress(operator []byte, height int64) (output []byte, err error) diff --git a/shared/modules/types.go b/shared/modules/types.go index 1bd74aa55..9b4c984f5 100644 --- a/shared/modules/types.go +++ b/shared/modules/types.go @@ -1,15 +1,7 @@ package modules -type IUnstakingActor interface { - GetAddress() []byte - SetAddress(address string) - GetStakeAmount() string - SetStakeAmount(address string) - GetOutputAddress() []byte - SetOutputAddress(address string) -} - // The result of executing a transaction against the blockchain state so that it is included in the block + type TxResult interface { GetTx() []byte // the transaction object primitive GetHeight() int64 // the height at which the tx was applied diff --git a/shared/modules/types/README.md b/shared/modules/types/README.md new file mode 100644 index 000000000..cd1235082 --- /dev/null +++ b/shared/modules/types/README.md @@ -0,0 +1,5 @@ +# Modules Types + +The types defined in this package are used for cross-module communication. + +The serialized byte representation of these types does not affect the state hash, but defines the "contract" of what one module expects of another. diff --git a/shared/modules/types/proto/unstaking_actor.proto b/shared/modules/types/proto/unstaking_actor.proto new file mode 100644 index 000000000..33f2b95f2 --- /dev/null +++ b/shared/modules/types/proto/unstaking_actor.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package modules; + +option go_package = "github.com/pokt-network/pocket/shared/modules/types"; + +// TECHDEBT: Identify if the codebase can be simplified by removing this protobuf altogether in +// exchange for using actor.proto. If not, move it into coreTypes. +message UnstakingActor { + string address = 1; + string stake_amount = 2; + string output_address = 3; +} \ No newline at end of file diff --git a/shared/modules/utility_module.go b/shared/modules/utility_module.go index 697fecafb..464fbb4cc 100644 --- a/shared/modules/utility_module.go +++ b/shared/modules/utility_module.go @@ -12,50 +12,52 @@ const UtilityModuleName = "utility" type UtilityModule interface { Module + // General purpose handler of utility specific messages that are not externalized in shared directories HandleMessage(*anypb.Any) error - // Creates a utilityContext with an underlying read-write persistenceContext; only 1 can exist at a time + // Creates a `utilityContext` with an underlying read-write `persistenceContext`; only 1 of which can exist at a time NewContext(height int64) (UtilityContext, error) - // Basic Transaction validation. SIDE EFFECT: Adds the transaction to the mempool if valid. + // Basic Transaction validation. + // SIDE EFFECT: Transaction is added to the utility's module mempool if valid to be repeated in the future; not obvious from the functional name. CheckTransaction(tx []byte) error GetMempool() mempool.TXMempool } -// Interface defining the context within which the node can operate with the utility layer. -// Operations in the context of a UtilityContext are isolated from other operations and -// other utility contexts until committed and released, enabling parallelizability along other -// operations. +// TECHDEBT: `CreateAndApplyProposalBlock` and `ApplyBlock` should be be refactored into a +// `GetProposalBlock` and `ApplyProposalBlock` functions + +// The context within which the node can operate with the utility layer. type UtilityContext interface { // Block operations - // This function is intended to be called by any type of node during state transitions. For example, - // both block proposers and verifiers will use it to create a context (before finalizing it) during consensus, - // and all verifiers will call it during state sync. - SetProposalBlock(blockHash string, proposerAddr []byte, transactions [][]byte) error + // This function is intended to be called by any type of node during state transitions. + // For example, both block proposers and replicas/verifiers will use it to create a + // context (before finalizing it) during consensus, and all verifiers will call it during state sync. + // TODO: Replace []byte with semantic type + SetProposalBlock(blockHash string, proposerAddr []byte, txs [][]byte) error // Reaps the mempool for transactions to be proposed in a new block, and applies them to this - // context; intended to be used by the block proposer. - CreateAndApplyProposalBlock(proposer []byte, maxTransactionBytes int) (stateHash string, transactions [][]byte, err error) - // Applies the proposed local state (i.e. the transactions in the current context); intended to be used by - // the block verifiers (i.e. non proposers).. + // context. + // Only intended to be used by the block proposer. + // TODO: Replace []byte with semantic type + CreateAndApplyProposalBlock(proposer []byte, maxTxBytes int) (stateHash string, txs [][]byte, err error) + // Applies the proposed local state (i.e. the transactions in the current context). + // Only intended to be used by the block verifiers (i.e. replicas). ApplyBlock() (stateHash string, err error) - // TECHDEBT: `CreateAndApplyProposalBlock` and `ApplyBlock` should be be refactored into a - // `GetProposalBlock` and `ApplyProposalBlock` functions - // Context operations // Releases the utility context and any underlying contexts it references Release() error - // State commitment of the current context + // Commit the current utility context (along with its underlying persistence context) to disk Commit(quorumCert []byte) error // Returns the read-write persistence context initialized by this utility context GetPersistenceContext() PersistenceRWContext } +// TECHDEBT: Remove this interface from `shared/modules` type UnstakingActor interface { GetAddress() []byte - GetStakeAmount() string GetOutputAddress() []byte } diff --git a/utility/account.go b/utility/account.go index 111b0c1d0..50ba875d4 100644 --- a/utility/account.go +++ b/utility/account.go @@ -3,109 +3,92 @@ package utility import ( "math/big" + "github.com/pokt-network/pocket/shared/converters" "github.com/pokt-network/pocket/utility/types" + typesUtil "github.com/pokt-network/pocket/utility/types" ) -// 'Accounts' are structures in the utility module that closely resemble currency holding vehicles: like a bank account. -// Accounts enable the 'ownership' or 'custody' over uPOKT tokens. These structures are fundamental to enabling -// the utility economy. +// 'Accounts' are utility module structures that resemble currency holding vehicles; e.g. a bank account. -func (u *UtilityContext) GetAccountAmount(address []byte) (*big.Int, types.Error) { - store, height, er := u.GetStoreAndHeight() - if er != nil { - return nil, er +func (u *utilityContext) getAccountAmount(address []byte) (*big.Int, types.Error) { + store, height, err := u.getStoreAndHeight() + if err != nil { + return nil, typesUtil.ErrGetHeight(err) + } + amountStr, err := store.GetAccountAmount(address, height) + if err != nil { + return nil, typesUtil.ErrGetAccountAmount(err) } - amount, err := store.GetAccountAmount(address, height) + amount, err := converters.StringToBigInt(amountStr) if err != nil { - return nil, types.ErrGetAccountAmount(err) + return nil, typesUtil.ErrStringToBigInt(err) } - return types.StringToBigInt(amount) + return amount, nil } -func (u *UtilityContext) AddAccountAmount(address []byte, amountToAdd *big.Int) types.Error { - store := u.Store() - if err := store.AddAccountAmount(address, types.BigIntToString(amountToAdd)); err != nil { +func (u *utilityContext) addAccountAmount(address []byte, amountToAdd *big.Int) types.Error { + if err := u.Store().AddAccountAmount(address, converters.BigIntToString(amountToAdd)); err != nil { return types.ErrAddAccountAmount(err) } return nil } -func (u *UtilityContext) AddAccountAmountString(address []byte, amountToAdd string) types.Error { - store := u.Store() - if err := store.AddAccountAmount(address, amountToAdd); err != nil { - return types.ErrAddAccountAmount(err) +func (u *utilityContext) subtractAccountAmount(address []byte, amountToSubtract *big.Int) types.Error { + if err := u.Store().SubtractAccountAmount(address, converters.BigIntToString(amountToSubtract)); err != nil { + return types.ErrSetAccountAmount(err) } return nil } -func (u *UtilityContext) AddPoolAmount(name string, amountToAdd *big.Int) types.Error { - store := u.Store() - if err := store.AddPoolAmount(name, types.BigIntToString(amountToAdd)); err != nil { - return types.ErrAddPoolAmount(name, err) +func (u *utilityContext) setAccountAmount(address []byte, amount *big.Int) types.Error { + if err := u.Store().SetAccountAmount(address, converters.BigIntToString(amount)); err != nil { + return types.ErrSetAccountAmount(err) } return nil } -func (u *UtilityContext) SubPoolAmount(name, amountToSub string) types.Error { - store := u.Store() - if err := store.SubtractPoolAmount(name, amountToSub); err != nil { - return types.ErrSubPoolAmount(name, err) +// 'Pools' are autonomous accounts owned by the protocol; e.g. an account for a fee pool that gets distributed + +func (u *utilityContext) insertPool(name string, amount *big.Int) types.Error { + if err := u.Store().InsertPool(name, converters.BigIntToString(amount)); err != nil { + return types.ErrSetPool(name, err) } return nil } -func (u *UtilityContext) GetPoolAmount(name string) (*big.Int, types.Error) { - store, height, err := u.GetStoreAndHeight() +func (u *utilityContext) getPoolAmount(name string) (*big.Int, types.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return nil, err + return nil, typesUtil.ErrGetHeight(err) } - tokens, er := store.GetPoolAmount(name, height) - if er != nil { - return nil, types.ErrGetPoolAmount(name, er) + amountStr, err := store.GetPoolAmount(name, height) + if err != nil { + return nil, types.ErrGetPoolAmount(name, err) } - amount, err := types.StringToBigInt(tokens) + amount, err := converters.StringToBigInt(amountStr) if err != nil { - return nil, err + return nil, types.ErrStringToBigInt(err) } return amount, nil } -func (u *UtilityContext) InsertPool(name string, address []byte, amount string) types.Error { - store := u.Store() - if err := store.InsertPool(name, amount); err != nil { - return types.ErrSetPool(name, err) - } - return nil -} - -func (u *UtilityContext) SetPoolAmount(name string, amount *big.Int) types.Error { - store := u.Store() - if err := store.SetPoolAmount(name, types.BigIntToString(amount)); err != nil { - return types.ErrSetPoolAmount(name, err) - } - return nil -} - -func (u *UtilityContext) SetAccountWithAmountString(address []byte, amount string) types.Error { - store := u.Store() - if err := store.SetAccountAmount(address, amount); err != nil { - return types.ErrSetAccountAmount(err) +func (u *utilityContext) addPoolAmount(name string, amountToAdd *big.Int) types.Error { + if err := u.Store().AddPoolAmount(name, converters.BigIntToString(amountToAdd)); err != nil { + return types.ErrAddPoolAmount(name, err) } return nil } -func (u *UtilityContext) SetAccountAmount(address []byte, amount *big.Int) types.Error { - store := u.Store() - if err := store.SetAccountAmount(address, types.BigIntToString(amount)); err != nil { - return types.ErrSetAccountAmount(err) +func (u *utilityContext) subPoolAmount(name string, amountToSub *big.Int) types.Error { + if err := u.Store().SubtractPoolAmount(name, converters.BigIntToString(amountToSub)); err != nil { + return types.ErrSubPoolAmount(name, err) } return nil } -func (u *UtilityContext) SubtractAccountAmount(address []byte, amountToSubtract *big.Int) types.Error { - store := u.Store() - if err := store.SubtractAccountAmount(address, types.BigIntToString(amountToSubtract)); err != nil { - return types.ErrSetAccountAmount(err) +func (u *utilityContext) setPoolAmount(name string, amount *big.Int) types.Error { + if err := u.Store().SetPoolAmount(name, converters.BigIntToString(amount)); err != nil { + return types.ErrSetPoolAmount(name, err) } return nil } diff --git a/utility/account_test.go b/utility/account_test.go new file mode 100644 index 000000000..6a06edc91 --- /dev/null +++ b/utility/account_test.go @@ -0,0 +1,158 @@ +package utility + +import ( + "encoding/hex" + "math/big" + "sort" + "testing" + + "github.com/pokt-network/pocket/shared/converters" + coreTypes "github.com/pokt-network/pocket/shared/core/types" + "github.com/pokt-network/pocket/shared/crypto" + "github.com/stretchr/testify/require" +) + +func TestUtilityContext_AddAccountAmount(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + acc := getFirstTestingAccount(t, ctx) + + initialAmount, err := converters.StringToBigInt(acc.GetAmount()) + require.NoError(t, err) + + addAmount := big.NewInt(1) + addrBz, err := hex.DecodeString(acc.GetAddress()) + require.NoError(t, err) + require.NoError(t, ctx.addAccountAmount(addrBz, addAmount), "add account amount") + + afterAmount, err := ctx.getAccountAmount(addrBz) + require.NoError(t, err) + + expected := initialAmount.Add(initialAmount, addAmount) + require.Equal(t, expected, afterAmount) +} + +func TestUtilityContext_SubtractAccountAmount(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + acc := getFirstTestingAccount(t, ctx) + + beforeAmount, err := converters.StringToBigInt(acc.GetAmount()) + require.NoError(t, err) + + subAmount := big.NewInt(100) + addrBz, er := hex.DecodeString(acc.GetAddress()) + require.NoError(t, er) + require.NoError(t, ctx.subtractAccountAmount(addrBz, subAmount), "sub account amount") + + amount, err := ctx.getAccountAmount(addrBz) + require.NoError(t, err) + require.NotEqual(t, beforeAmount, amount) + + expected := beforeAmount.Sub(beforeAmount, subAmount) + require.Equal(t, expected, amount) +} + +func TestUtilityContext_SetAccountAmount(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + + addr, err := crypto.GenerateAddress() + require.NoError(t, err) + + amount := big.NewInt(100) + require.NoError(t, ctx.setAccountAmount(addr, amount), "set account amount") + + gotAmount, err := ctx.getAccountAmount(addr) + require.NoError(t, err) + require.Equal(t, amount, gotAmount) +} + +func TestUtilityContext_AddPoolAmount(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + pool := getFirstTestingPool(t, ctx) + + initialAmount, err := converters.StringToBigInt(pool.GetAmount()) + require.NoError(t, err) + + addAmount := big.NewInt(1) + require.NoError(t, ctx.addPoolAmount(pool.GetAddress(), addAmount), "add pool amount") + + afterAmount, err := ctx.getPoolAmount(pool.GetAddress()) + require.NoError(t, err) + + expected := initialAmount.Add(initialAmount, addAmount) + require.Equal(t, expected, afterAmount) +} + +func TestUtilityContext_InsertPool(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + testPoolName := "TEST_POOL" + + amount := big.NewInt(1000) + err := ctx.insertPool(testPoolName, amount) + require.NoError(t, err, "insert pool") + + poolAmount, err := ctx.getPoolAmount(testPoolName) + require.NoError(t, err) + require.Equal(t, amount, poolAmount) +} + +func TestUtilityContext_SetPoolAmount(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + pool := getFirstTestingPool(t, ctx) + + beforeAmount, err := converters.StringToBigInt(pool.GetAmount()) + require.NoError(t, err) + + expectedAfterAmount := big.NewInt(100) + require.NoError(t, ctx.setPoolAmount(pool.GetAddress(), expectedAfterAmount), "set pool amount") + + amount, err := ctx.getPoolAmount(pool.GetAddress()) + require.NoError(t, err) + require.NotEqual(t, beforeAmount, amount) + require.Equal(t, amount, expectedAfterAmount) +} + +func TestUtilityContext_SubPoolAmount(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + pool := getFirstTestingPool(t, ctx) + + beforeAmountBig := big.NewInt(1000000000000000) + require.NoError(t, ctx.setPoolAmount(pool.GetAddress(), beforeAmountBig)) + + subAmount := big.NewInt(100) + require.NoError(t, ctx.subPoolAmount(pool.GetAddress(), subAmount), "sub pool amount") + + amount, err := ctx.getPoolAmount(pool.GetAddress()) + require.NoError(t, err) + require.NotEqual(t, beforeAmountBig, amount) + + expected := beforeAmountBig.Sub(beforeAmountBig, subAmount) + require.Equal(t, expected, amount) +} + +func getAllTestingAccounts(t *testing.T, ctx *utilityContext) []*coreTypes.Account { + accs, err := (ctx.persistenceContext).GetAllAccounts(0) + require.NoError(t, err) + + sort.Slice(accs, func(i, j int) bool { + return accs[i].GetAddress() < accs[j].GetAddress() + }) + return accs +} + +func getFirstTestingAccount(t *testing.T, ctx *utilityContext) *coreTypes.Account { + return getAllTestingAccounts(t, ctx)[0] +} + +func getAllTestingPools(t *testing.T, ctx *utilityContext) []*coreTypes.Account { + pools, err := (ctx.persistenceContext).GetAllPools(0) + require.NoError(t, err) + + sort.Slice(pools, func(i, j int) bool { + return pools[i].GetAddress() < pools[j].GetAddress() + }) + return pools +} + +func getFirstTestingPool(t *testing.T, ctx *utilityContext) *coreTypes.Account { + return getAllTestingPools(t, ctx)[0] +} diff --git a/utility/actor.go b/utility/actor.go index 1ac51391f..1ce8017fa 100644 --- a/utility/actor.go +++ b/utility/actor.go @@ -1,88 +1,76 @@ package utility import ( - "math" "math/big" + "github.com/pokt-network/pocket/shared/converters" coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/crypto" typesUtil "github.com/pokt-network/pocket/utility/types" ) -/* - `Actor` is the consolidated term for common functionality among the following network actors: app, fish, node, val. +// `Actor` is the consolidated term for common functionality among the following network actors: application, fisherman, servicer, validator, etc. - This file contains all the state based CRUD operations shared between these abstractions. - - The ideology of the separation of the actors is based on the expectation of actor divergence in the near future. - The current implementation attempts to simplify code footprint and complexity while enabling future divergence. - It is important to note, that as production approaches, the idea is to attempt more consolidation at an architectural - multi-module level. Until then, it's a fine line to walk. -*/ - -// setters - -func (u *UtilityContext) SetActorStakedTokens(actorType coreTypes.ActorType, tokens *big.Int, address []byte) typesUtil.Error { +func (u *utilityContext) setActorStakeAmount(actorType coreTypes.ActorType, addr []byte, amount *big.Int) typesUtil.Error { store := u.Store() + amountStr := converters.BigIntToString(amount) - var er error + var err error switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - er = store.SetAppStakeAmount(address, typesUtil.BigIntToString(tokens)) + err = store.SetAppStakeAmount(addr, amountStr) case coreTypes.ActorType_ACTOR_TYPE_FISH: - er = store.SetFishermanStakeAmount(address, typesUtil.BigIntToString(tokens)) + err = store.SetFishermanStakeAmount(addr, amountStr) case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - er = store.SetServiceNodeStakeAmount(address, typesUtil.BigIntToString(tokens)) + err = store.SetServiceNodeStakeAmount(addr, amountStr) case coreTypes.ActorType_ACTOR_TYPE_VAL: - er = store.SetValidatorStakeAmount(address, typesUtil.BigIntToString(tokens)) + err = store.SetValidatorStakeAmount(addr, amountStr) default: - er = typesUtil.ErrUnknownActorType(actorType.String()) + err = typesUtil.ErrUnknownActorType(actorType.String()) } - if er != nil { - return typesUtil.ErrSetValidatorStakedTokens(er) + if err != nil { + return typesUtil.ErrSetValidatorStakedAmount(err) } - return nil } -func (u *UtilityContext) SetActorUnstaking(actorType coreTypes.ActorType, unstakingHeight int64, address []byte) typesUtil.Error { +func (u *utilityContext) setActorUnstakingHeight(actorType coreTypes.ActorType, addr []byte, height int64) typesUtil.Error { store := u.Store() + unstakingStatus := int32(typesUtil.StakeStatus_Unstaking) - var er error + var err error switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - er = store.SetAppUnstakingHeightAndStatus(address, unstakingHeight, int32(typesUtil.StakeStatus_Unstaking)) + err = store.SetAppUnstakingHeightAndStatus(addr, height, unstakingStatus) case coreTypes.ActorType_ACTOR_TYPE_FISH: - er = store.SetFishermanUnstakingHeightAndStatus(address, unstakingHeight, int32(typesUtil.StakeStatus_Unstaking)) + err = store.SetFishermanUnstakingHeightAndStatus(addr, height, unstakingStatus) case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - er = store.SetServiceNodeUnstakingHeightAndStatus(address, unstakingHeight, int32(typesUtil.StakeStatus_Unstaking)) + err = store.SetServiceNodeUnstakingHeightAndStatus(addr, height, unstakingStatus) case coreTypes.ActorType_ACTOR_TYPE_VAL: - er = store.SetValidatorUnstakingHeightAndStatus(address, unstakingHeight, int32(typesUtil.StakeStatus_Unstaking)) + err = store.SetValidatorUnstakingHeightAndStatus(addr, height, unstakingStatus) default: - er = typesUtil.ErrUnknownActorType(actorType.String()) + err = typesUtil.ErrUnknownActorType(actorType.String()) } - if er != nil { - return typesUtil.ErrSetUnstakingHeightAndStatus(er) + if err != nil { + return typesUtil.ErrSetUnstakingHeightAndStatus(err) } - return nil } -func (u *UtilityContext) SetActorPauseHeight(actorType coreTypes.ActorType, address []byte, height int64) typesUtil.Error { +func (u *utilityContext) setActorPausedHeight(actorType coreTypes.ActorType, addr []byte, height int64) typesUtil.Error { store := u.Store() var err error switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - err = store.SetAppPauseHeight(address, height) + err = store.SetAppPauseHeight(addr, height) case coreTypes.ActorType_ACTOR_TYPE_FISH: - err = store.SetFishermanPauseHeight(address, height) + err = store.SetFishermanPauseHeight(addr, height) case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - err = store.SetServiceNodePauseHeight(address, height) + err = store.SetServiceNodePauseHeight(addr, height) case coreTypes.ActorType_ACTOR_TYPE_VAL: - err = store.SetValidatorPauseHeight(address, height) + err = store.SetValidatorPauseHeight(addr, height) default: err = typesUtil.ErrUnknownActorType(actorType.String()) } @@ -90,49 +78,45 @@ func (u *UtilityContext) SetActorPauseHeight(actorType coreTypes.ActorType, addr if err != nil { return typesUtil.ErrSetPauseHeight(err) } - return nil } -// getters - -func (u *UtilityContext) GetActorStakedTokens(actorType coreTypes.ActorType, address []byte) (*big.Int, typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +func (u *utilityContext) getActorStakeAmount(actorType coreTypes.ActorType, addr []byte) (*big.Int, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return nil, err + return nil, typesUtil.ErrGetHeight(err) } - var stakedTokens string - var er error + var stakeAmount string switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - stakedTokens, er = store.GetAppStakeAmount(height, address) + stakeAmount, err = store.GetAppStakeAmount(height, addr) case coreTypes.ActorType_ACTOR_TYPE_FISH: - stakedTokens, er = store.GetFishermanStakeAmount(height, address) + stakeAmount, err = store.GetFishermanStakeAmount(height, addr) case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - stakedTokens, er = store.GetServiceNodeStakeAmount(height, address) + stakeAmount, err = store.GetServiceNodeStakeAmount(height, addr) case coreTypes.ActorType_ACTOR_TYPE_VAL: - stakedTokens, er = store.GetValidatorStakeAmount(height, address) + stakeAmount, err = store.GetValidatorStakeAmount(height, addr) default: - er = typesUtil.ErrUnknownActorType(actorType.String()) + err = typesUtil.ErrUnknownActorType(actorType.String()) } - if er != nil { - return nil, typesUtil.ErrGetStakedTokens(er) + if err != nil { + return nil, typesUtil.ErrGetStakeAmount(err) } - i, err := typesUtil.StringToBigInt(stakedTokens) + amount, err := converters.StringToBigInt(stakeAmount) if err != nil { - return nil, err + return nil, typesUtil.ErrStringToBigInt(err) } - return i, nil + return amount, nil } -func (u *UtilityContext) GetMaxPausedBlocks(actorType coreTypes.ActorType) (maxPausedBlocks int, err typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +func (u *utilityContext) getMaxAllowedPausedBlocks(actorType coreTypes.ActorType) (int, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return 0, err + return 0, typesUtil.ErrGetHeight(err) } var paramName string @@ -149,19 +133,18 @@ func (u *UtilityContext) GetMaxPausedBlocks(actorType coreTypes.ActorType) (maxP return 0, typesUtil.ErrUnknownActorType(actorType.String()) } - var er error - maxPausedBlocks, er = store.GetIntParam(paramName, height) - if er != nil { - return typesUtil.ZeroInt, typesUtil.ErrGetParam(paramName, er) + maxPausedBlocks, err := store.GetIntParam(paramName, height) + if err != nil { + return typesUtil.ZeroInt, typesUtil.ErrGetParam(paramName, err) } - return + return maxPausedBlocks, nil } -func (u *UtilityContext) GetMinimumPauseBlocks(actorType coreTypes.ActorType) (minPauseBlocks int, err typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +func (u *utilityContext) getMinRequiredPausedBlocks(actorType coreTypes.ActorType) (int, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return 0, err + return 0, typesUtil.ErrGetHeight(err) } var paramName string @@ -178,72 +161,75 @@ func (u *UtilityContext) GetMinimumPauseBlocks(actorType coreTypes.ActorType) (m return 0, typesUtil.ErrUnknownActorType(actorType.String()) } - minPauseBlocks, er := store.GetIntParam(paramName, height) + minPausedBlocks, er := store.GetIntParam(paramName, height) if er != nil { return typesUtil.ZeroInt, typesUtil.ErrGetParam(paramName, er) } - - return + return minPausedBlocks, nil } -func (u *UtilityContext) GetPauseHeight(actorType coreTypes.ActorType, address []byte) (pauseHeight int64, err typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +func (u *utilityContext) getPausedHeightIfExists(actorType coreTypes.ActorType, addr []byte) (int64, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return 0, err + return 0, typesUtil.ErrGetHeight(err) } - var er error + var pauseHeight int64 switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - pauseHeight, er = store.GetAppPauseHeightIfExists(address, height) + pauseHeight, err = store.GetAppPauseHeightIfExists(addr, height) case coreTypes.ActorType_ACTOR_TYPE_FISH: - pauseHeight, er = store.GetFishermanPauseHeightIfExists(address, height) + pauseHeight, err = store.GetFishermanPauseHeightIfExists(addr, height) case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - pauseHeight, er = store.GetServiceNodePauseHeightIfExists(address, height) + pauseHeight, err = store.GetServiceNodePauseHeightIfExists(addr, height) case coreTypes.ActorType_ACTOR_TYPE_VAL: - pauseHeight, er = store.GetValidatorPauseHeightIfExists(address, height) + pauseHeight, err = store.GetValidatorPauseHeightIfExists(addr, height) default: - er = typesUtil.ErrUnknownActorType(actorType.String()) + err = typesUtil.ErrUnknownActorType(actorType.String()) } - if er != nil { - return typesUtil.ZeroInt, typesUtil.ErrGetPauseHeight(er) + if err != nil { + return typesUtil.ZeroInt, typesUtil.ErrGetPauseHeight(err) } - return + return pauseHeight, nil } -func (u *UtilityContext) GetActorStatus(actorType coreTypes.ActorType, address []byte) (status int32, err typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +func (u *utilityContext) getActorStatus(actorType coreTypes.ActorType, addr []byte) (typesUtil.StakeStatus, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return 0, err + return 0, typesUtil.ErrGetHeight(err) } - var er error + var status int32 switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - status, er = store.GetAppStatus(address, height) + status, err = store.GetAppStatus(addr, height) case coreTypes.ActorType_ACTOR_TYPE_FISH: - status, er = store.GetFishermanStatus(address, height) + status, err = store.GetFishermanStatus(addr, height) case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - status, er = store.GetServiceNodeStatus(address, height) + status, err = store.GetServiceNodeStatus(addr, height) case coreTypes.ActorType_ACTOR_TYPE_VAL: - status, er = store.GetValidatorStatus(address, height) + status, err = store.GetValidatorStatus(addr, height) default: - er = typesUtil.ErrUnknownActorType(actorType.String()) + err = typesUtil.ErrUnknownActorType(actorType.String()) } - if er != nil { - return typesUtil.ZeroInt, typesUtil.ErrGetStatus(er) + if err != nil { + return typesUtil.ZeroInt, typesUtil.ErrGetStatus(err) + } + + if _, ok := typesUtil.StakeStatus_name[status]; !ok { + return typesUtil.ZeroInt, typesUtil.ErrUnknownStatus(status) } - return status, nil + return typesUtil.StakeStatus(status), nil } -func (u *UtilityContext) GetMinimumStake(actorType coreTypes.ActorType) (*big.Int, typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +func (u *utilityContext) getMinRequiredStakeAmount(actorType coreTypes.ActorType) (*big.Int, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return nil, err + return nil, typesUtil.ErrGetHeight(err) } var paramName string @@ -265,45 +251,21 @@ func (u *UtilityContext) GetMinimumStake(actorType coreTypes.ActorType) (*big.In return nil, typesUtil.ErrGetParam(paramName, er) } - return typesUtil.StringToBigInt(minStake) -} - -func (u *UtilityContext) GetStakeAmount(actorType coreTypes.ActorType, address []byte) (*big.Int, typesUtil.Error) { - var stakeAmount string - store, height, er := u.GetStoreAndHeight() - if er != nil { - return nil, er - } - - var err error - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_APP: - stakeAmount, err = store.GetAppStakeAmount(height, address) - case coreTypes.ActorType_ACTOR_TYPE_FISH: - stakeAmount, err = store.GetFishermanStakeAmount(height, address) - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - stakeAmount, err = store.GetServiceNodeStakeAmount(height, address) - case coreTypes.ActorType_ACTOR_TYPE_VAL: - stakeAmount, err = store.GetValidatorStakeAmount(height, address) - default: - err = typesUtil.ErrUnknownActorType(actorType.String()) - } - + amount, err := converters.StringToBigInt(minStake) if err != nil { - return nil, typesUtil.ErrGetStakeAmount(err) + return nil, typesUtil.ErrStringToBigInt(err) } - - return typesUtil.StringToBigInt(stakeAmount) + return amount, nil } -func (u *UtilityContext) GetUnstakingHeight(actorType coreTypes.ActorType) (unstakingHeight int64, err typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +func (u *utilityContext) getUnbondingHeight(actorType coreTypes.ActorType) (int64, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return 0, err + return 0, typesUtil.ErrGetHeight(err) } var paramName string - var unstakingBlocks int + var unstakingBlocksPeriod int switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: paramName = typesUtil.AppUnstakingBlocksParamName @@ -317,19 +279,18 @@ func (u *UtilityContext) GetUnstakingHeight(actorType coreTypes.ActorType) (unst return 0, typesUtil.ErrUnknownActorType(actorType.String()) } - var er error - unstakingBlocks, er = store.GetIntParam(paramName, height) - if er != nil { - return typesUtil.ZeroInt, typesUtil.ErrGetParam(paramName, er) + unstakingBlocksPeriod, err = store.GetIntParam(paramName, height) + if err != nil { + return typesUtil.ZeroInt, typesUtil.ErrGetParam(paramName, err) } - return u.CalculateUnstakingHeight(int64(unstakingBlocks)) + return u.height + int64(unstakingBlocksPeriod), nil } -func (u *UtilityContext) GetMaxChains(actorType coreTypes.ActorType) (maxChains int, err typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +func (u *utilityContext) getMaxAllowedChains(actorType coreTypes.ActorType) (int, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return 0, err + return 0, typesUtil.ErrGetHeight(err) } var paramName string @@ -344,32 +305,30 @@ func (u *UtilityContext) GetMaxChains(actorType coreTypes.ActorType) (maxChains return 0, typesUtil.ErrUnknownActorType(actorType.String()) } - var er error - maxChains, er = store.GetIntParam(paramName, height) - if er != nil { - return 0, typesUtil.ErrGetParam(paramName, er) + maxChains, err := store.GetIntParam(paramName, height) + if err != nil { + return 0, typesUtil.ErrGetParam(paramName, err) } - return + return maxChains, nil } -func (u *UtilityContext) GetActorExists(actorType coreTypes.ActorType, address []byte) (bool, typesUtil.Error) { - store, height, er := u.GetStoreAndHeight() - if er != nil { - return false, er +func (u *utilityContext) getActorExists(actorType coreTypes.ActorType, addr []byte) (bool, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() + if err != nil { + return false, typesUtil.ErrGetHeight(err) } var exists bool - var err error switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - exists, err = store.GetAppExists(address, height) + exists, err = store.GetAppExists(addr, height) case coreTypes.ActorType_ACTOR_TYPE_FISH: - exists, err = store.GetFishermanExists(address, height) + exists, err = store.GetFishermanExists(addr, height) case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - exists, err = store.GetServiceNodeExists(address, height) + exists, err = store.GetServiceNodeExists(addr, height) case coreTypes.ActorType_ACTOR_TYPE_VAL: - exists, err = store.GetValidatorExists(address, height) + exists, err = store.GetValidatorExists(addr, height) default: return false, typesUtil.ErrUnknownActorType(actorType.String()) } @@ -381,164 +340,31 @@ func (u *UtilityContext) GetActorExists(actorType coreTypes.ActorType, address [ return exists, nil } -func (u *UtilityContext) GetActorOutputAddress(actorType coreTypes.ActorType, operator []byte) (output []byte, err typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +// IMPROVE: Need to re-evaluate the design of `Output Address` to support things like "rev-share" +// and multiple output addresses. +func (u *utilityContext) getActorOutputAddress(actorType coreTypes.ActorType, operator []byte) ([]byte, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return nil, err + return nil, typesUtil.ErrGetHeight(err) } - var er error + var outputAddr []byte switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - output, er = store.GetAppOutputAddress(operator, height) + outputAddr, err = store.GetAppOutputAddress(operator, height) case coreTypes.ActorType_ACTOR_TYPE_FISH: - output, er = store.GetFishermanOutputAddress(operator, height) + outputAddr, err = store.GetFishermanOutputAddress(operator, height) case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - output, er = store.GetServiceNodeOutputAddress(operator, height) + outputAddr, err = store.GetServiceNodeOutputAddress(operator, height) case coreTypes.ActorType_ACTOR_TYPE_VAL: - output, er = store.GetValidatorOutputAddress(operator, height) + outputAddr, err = store.GetValidatorOutputAddress(operator, height) default: - er = typesUtil.ErrUnknownActorType(actorType.String()) - } - - if er != nil { - return nil, typesUtil.ErrGetOutputAddress(operator, er) - - } - return output, nil -} - -// calculators - -func (u *UtilityContext) BurnActor(actorType coreTypes.ActorType, percentage int, address []byte) typesUtil.Error { - tokens, err := u.GetActorStakedTokens(actorType, address) - if err != nil { - return err - } - zeroBigInt := big.NewInt(0) - tokensFloat := new(big.Float).SetInt(tokens) - tokensFloat.Mul(tokensFloat, big.NewFloat(float64(percentage))) - tokensFloat.Quo(tokensFloat, big.NewFloat(100)) - truncatedTokens, _ := tokensFloat.Int(nil) - if truncatedTokens.Cmp(zeroBigInt) == -1 { - truncatedTokens = zeroBigInt - } - newTokensAfterBurn := big.NewInt(0).Sub(tokens, truncatedTokens) - // remove from pool - if err := u.SubPoolAmount(coreTypes.Pools_POOLS_VALIDATOR_STAKE.FriendlyName(), typesUtil.BigIntToString(truncatedTokens)); err != nil { - return err - } - // remove from actor - if err := u.SetActorStakedTokens(actorType, newTokensAfterBurn, address); err != nil { - return err - } - // check to see if they fell below minimum stake - minStake, err := u.GetValidatorMinimumStake() - if err != nil { - return err - } - // fell below minimum stake - if minStake.Cmp(truncatedTokens) == 1 { - unstakingHeight, err := u.GetUnstakingHeight(actorType) - if err != nil { - return err - } - if err := u.SetActorUnstaking(actorType, unstakingHeight, address); err != nil { - return err - } - } - return nil -} - -func (u *UtilityContext) CalculateAppRelays(stakedTokens string) (string, typesUtil.Error) { - tokens, err := typesUtil.StringToBigInt(stakedTokens) - if err != nil { - return typesUtil.EmptyString, err - } - // The constant integer adjustment that the DAO may use to move the stake. The DAO may manually - // adjust an application's MaxRelays at the time of staking to correct for short-term fluctuations - // in the price of POKT, which may not be reflected in ParticipationRate - // When this parameter is set to 0, no adjustment is being made. - stabilityAdjustment, err := u.GetStabilityAdjustment() - if err != nil { - return typesUtil.EmptyString, err - } - baseRate, err := u.GetBaselineAppStakeRate() - if err != nil { - return typesUtil.EmptyString, err - } - // convert tokens to float64 - tokensFloat64 := big.NewFloat(float64(tokens.Int64())) - // get the percentage of the baseline stake rate (can be over 100%) - basePercentage := big.NewFloat(float64(baseRate) / float64(100)) - // multiply the two - // DISCUSS evaluate whether or not we should use micro denomination or not - baselineThroughput := basePercentage.Mul(basePercentage, tokensFloat64) - // adjust for uPOKT - baselineThroughput.Quo(baselineThroughput, big.NewFloat(typesUtil.MillionInt)) - // add staking adjustment (can be negative) - adjusted := baselineThroughput.Add(baselineThroughput, big.NewFloat(float64(stabilityAdjustment))) - // truncate the integer - result, _ := adjusted.Int(nil) - // bounding Max Amount of relays to maxint64 - max := big.NewInt(math.MaxInt64) - if i := result.Cmp(max); i < -1 { - result = max - } - return typesUtil.BigIntToString(result), nil -} - -func (u *UtilityContext) CheckAboveMinStake(actorType coreTypes.ActorType, amount string) (a *big.Int, err typesUtil.Error) { - minStake, er := u.GetMinimumStake(actorType) - if er != nil { - return nil, er - } - a, err = typesUtil.StringToBigInt(amount) - if err != nil { - return nil, err - } - if typesUtil.BigIntLessThan(a, minStake) { - return nil, typesUtil.ErrMinimumStake() - } - return // for convenience this returns amount as a big.Int -} - -func (u *UtilityContext) CheckBelowMaxChains(actorType coreTypes.ActorType, chains []string) typesUtil.Error { - // validators don't have chains field - - if actorType == coreTypes.ActorType_ACTOR_TYPE_VAL { - return nil - } - - maxChains, err := u.GetMaxChains(actorType) - if err != nil { - return err - } - if len(chains) > maxChains { - return typesUtil.ErrMaxChains(maxChains) + err = typesUtil.ErrUnknownActorType(actorType.String()) } - return nil -} -func (u *UtilityContext) GetLastBlockByzantineValidators() ([][]byte, error) { - // TODO(#271): Need to retrieve byzantine validators from the persistence module - return nil, nil -} - -func (u *UtilityContext) CalculateUnstakingHeight(unstakingBlocks int64) (int64, typesUtil.Error) { - latestHeight, err := u.GetLatestBlockHeight() if err != nil { - return typesUtil.ZeroInt, err - } - return unstakingBlocks + latestHeight, nil -} - -// util + return nil, typesUtil.ErrGetOutputAddress(operator, err) -func (u *UtilityContext) BytesToPublicKey(publicKey []byte) (crypto.PublicKey, typesUtil.Error) { - pk, er := crypto.NewPublicKeyFromBytes(publicKey) - if er != nil { - return nil, typesUtil.ErrNewPublicKeyFromBytes(er) } - return pk, nil + return outputAddr, nil } diff --git a/utility/actor_test.go b/utility/actor_test.go new file mode 100644 index 000000000..6a6eb33a0 --- /dev/null +++ b/utility/actor_test.go @@ -0,0 +1,713 @@ +package utility + +import ( + "encoding/hex" + "fmt" + "math/big" + "sort" + "testing" + + "github.com/pokt-network/pocket/runtime/test_artifacts" + "github.com/pokt-network/pocket/shared/codec" + "github.com/pokt-network/pocket/shared/converters" + coreTypes "github.com/pokt-network/pocket/shared/core/types" + "github.com/pokt-network/pocket/shared/crypto" + typesUtil "github.com/pokt-network/pocket/utility/types" + "github.com/stretchr/testify/require" + "golang.org/x/exp/slices" +) + +func TestUtilityContext_HandleMessageStake(t *testing.T) { + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.HandleMessageStake", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + + pubKey, err := crypto.GeneratePublicKey() + require.NoError(t, err) + + outputAddress, err := crypto.GenerateAddress() + require.NoError(t, err) + + err = ctx.setAccountAmount(outputAddress, test_artifacts.DefaultAccountAmount) + require.NoError(t, err, "error setting account amount error") + + msg := &typesUtil.MessageStake{ + PublicKey: pubKey.Bytes(), + Chains: test_artifacts.DefaultChains, + Amount: test_artifacts.DefaultStakeAmountString, + ServiceUrl: test_artifacts.DefaultServiceURL, + OutputAddress: outputAddress, + Signer: outputAddress, + ActorType: actorType, + } + + err = ctx.handleStakeMessage(msg) + require.NoError(t, err) + + actor := getActorByAddr(t, ctx, actorType, pubKey.Address().String()) + require.Equal(t, actor.GetAddress(), pubKey.Address().String(), "incorrect actor address") + require.Equal(t, typesUtil.HeightNotUsed, actor.GetPausedHeight(), "incorrect actorpaused height") + require.Equal(t, test_artifacts.DefaultStakeAmountString, actor.GetStakedAmount(), "incorrect actor stake amount") + require.Equal(t, outputAddress.String(), actor.GetOutput(), "incorrect actor output address") + if actorType != coreTypes.ActorType_ACTOR_TYPE_VAL { + require.Equal(t, msg.Chains, actor.GetChains(), "incorrect actor chains") + } + }) + } +} + +func TestUtilityContext_HandleMessageEditStake(t *testing.T) { + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.HandleMessageEditStake", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + actor := getFirstActor(t, ctx, actorType) + + addr := actor.GetAddress() + addrBz, err := hex.DecodeString(addr) + require.NoError(t, err) + + // Edit the staked chains + msg := &typesUtil.MessageEditStake{ + Address: addrBz, + Chains: test_artifacts.DefaultChains, + Amount: test_artifacts.DefaultStakeAmountString, + Signer: addrBz, + ActorType: actorType, + } + msgChainsEdited := codec.GetCodec().Clone(msg).(*typesUtil.MessageEditStake) + msgChainsEdited.Chains = []string{"0002"} + require.NotEqual(t, msgChainsEdited.Chains, test_artifacts.DefaultChains) // sanity check to make sure the test makes sense + + err = ctx.handleEditStakeMessage(msgChainsEdited) + require.NoError(t, err) + + // Verify the chains were edited + actor = getActorByAddr(t, ctx, actorType, addr) + if actorType != coreTypes.ActorType_ACTOR_TYPE_VAL { + require.NotEqual(t, test_artifacts.DefaultChains, actor.GetChains(), "incorrect edited chains") + require.Equal(t, msgChainsEdited.Chains, actor.GetChains(), "incorrect edited chains") + } + + // Edit the staked amount + amountEdited := test_artifacts.DefaultAccountAmount.Add(test_artifacts.DefaultAccountAmount, big.NewInt(1)) + amountEditedString := converters.BigIntToString(amountEdited) + + msgAmountEdited := codec.GetCodec().Clone(msg).(*typesUtil.MessageEditStake) + msgAmountEdited.Amount = amountEditedString + + // Verify the staked amount was edited + err = ctx.handleEditStakeMessage(msgAmountEdited) + require.NoError(t, err, "handle edit stake message") + + actor = getActorByAddr(t, ctx, actorType, addr) + require.NotEqual(t, test_artifacts.DefaultStakeAmountString, actor.GetStakedAmount(), "incorrect edited amount staked") + require.Equal(t, amountEditedString, actor.StakedAmount, "incorrect edited amount staked") + }) + } +} + +func TestUtilityContext_HandleMessageUnstake(t *testing.T) { + // The gov param for each actor will be set to this value + numUnstakingBlocks := 5 + + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.HandleMessageUnstake", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 1) + + var paramName string + switch actorType { + case coreTypes.ActorType_ACTOR_TYPE_APP: + paramName = typesUtil.AppUnstakingBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_FISH: + paramName = typesUtil.FishermanUnstakingBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: + paramName = typesUtil.ServiceNodeUnstakingBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_VAL: + paramName = typesUtil.ValidatorUnstakingBlocksParamName + default: + t.Fatalf("unexpected actor type %s", actorType.String()) + } + err := ctx.persistenceContext.SetParam(paramName, numUnstakingBlocks) + require.NoError(t, err, "error setting minimum pause blocks") + + actor := getFirstActor(t, ctx, actorType) + addr := actor.GetAddress() + addrBz, err := hex.DecodeString(addr) + require.NoError(t, err) + + msg := &typesUtil.MessageUnstake{ + Address: addrBz, + Signer: addrBz, + ActorType: actorType, + } + + // Unstake the actor + err = ctx.handleUnstakeMessage(msg) + require.NoError(t, err, "handle unstake message") + + // Verify the unstaking height is correct + actor = getActorByAddr(t, ctx, actorType, addr) + require.Equal(t, int64(numUnstakingBlocks)+1, actor.GetUnstakingHeight(), "actor should be unstaking") + }) + } +} + +func TestUtilityContext_HandleMessageUnpause(t *testing.T) { + // The gov param for each actor will be set to this value + minPauseBlocksNumber := 5 + + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.HandleMessageUnpause", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 1) + + var paramName string + switch actorType { + case coreTypes.ActorType_ACTOR_TYPE_APP: + paramName = typesUtil.AppMinimumPauseBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_FISH: + paramName = typesUtil.FishermanMinimumPauseBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: + paramName = typesUtil.ServiceNodeMinimumPauseBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_VAL: + paramName = typesUtil.ValidatorMinimumPauseBlocksParamName + default: + t.Fatalf("unexpected actor type %s", actorType.String()) + } + err := ctx.persistenceContext.SetParam(paramName, minPauseBlocksNumber) + require.NoError(t, err, "error setting minimum pause blocks") + + actor := getFirstActor(t, ctx, actorType) + addr := actor.GetAddress() + addrBz, err := hex.DecodeString(addr) + require.NoError(t, err) + + // Pause the actor + err = ctx.setActorPausedHeight(actorType, addrBz, 1) + require.NoError(t, err, "error setting pause height") + + // Verify the actor is paused + actor = getActorByAddr(t, ctx, actorType, addr) + require.Equal(t, int64(1), actor.GetPausedHeight()) + + // Try to unpause the actor and verify that it fails + msgUnpauseActor := &typesUtil.MessageUnpause{ + Address: addrBz, + Signer: addrBz, + ActorType: actorType, + } + err = ctx.handleUnpauseMessage(msgUnpauseActor) + require.Error(t, err) + require.ErrorContains(t, err, "minimum number of blocks hasn't passed since pausing") + + // Start a new context when the actor still cannot be unpaused + require.NoError(t, ctx.Commit([]byte("empty qc"))) + require.NoError(t, ctx.Release()) + ctx = newTestingUtilityContext(t, int64(minPauseBlocksNumber)-1) + + // Try to unpause the actor + err = ctx.handleUnpauseMessage(msgUnpauseActor) + require.Error(t, err) + require.ErrorContains(t, err, "minimum number of blocks hasn't passed since pausing") + + // Verify the actor is still paused + actor = getActorByAddr(t, ctx, actorType, addr) + require.NotEqual(t, typesUtil.HeightNotUsed, actor.GetPausedHeight()) + + // Start a new context when the actor can be unpaused + require.Error(t, ctx.Commit([]byte("empty qc"))) // Nothing to commit so we expect an error + require.NoError(t, ctx.Release()) + ctx = newTestingUtilityContext(t, int64(minPauseBlocksNumber)+1) + + // Try to unpause the actor + err = ctx.handleUnpauseMessage(msgUnpauseActor) + require.NoError(t, err) + + // Verify the actor is still paused + actor = getActorByAddr(t, ctx, actorType, addr) + require.Equal(t, typesUtil.HeightNotUsed, actor.GetPausedHeight()) + }) + } +} + +func TestUtilityContext_GetUnbondingHeight(t *testing.T) { + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.CalculateUnstakingHeight", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + + var unstakingBlocks int64 + var err error + switch actorType { + case coreTypes.ActorType_ACTOR_TYPE_APP: + unstakingBlocks, err = ctx.getAppUnstakingBlocks() + case coreTypes.ActorType_ACTOR_TYPE_FISH: + unstakingBlocks, err = ctx.getFishermanUnstakingBlocks() + case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: + unstakingBlocks, err = ctx.getServiceNodeUnstakingBlocks() + case coreTypes.ActorType_ACTOR_TYPE_VAL: + unstakingBlocks, err = ctx.getValidatorUnstakingBlocks() + default: + t.Fatalf("unexpected actor type %s", actorType.String()) + } + require.NoError(t, err, "error getting unstaking blocks") + + unbondingHeight, err := ctx.getUnbondingHeight(actorType) + require.NoError(t, err) + require.Equal(t, unstakingBlocks, unbondingHeight, "unexpected unstaking height") + }) + } +} + +func TestUtilityContext_BeginUnstakingMaxPausedActors(t *testing.T) { + // The gov param for each actor will be set to this value + maxPausedBlocks := 5 + + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.BeginUnstakingMaxPausedActors", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 1) + + var paramName string + switch actorType { + case coreTypes.ActorType_ACTOR_TYPE_APP: + paramName = typesUtil.AppMaxPauseBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_FISH: + paramName = typesUtil.FishermanMaxPauseBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: + paramName = typesUtil.ServiceNodeMaxPauseBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_VAL: + paramName = typesUtil.ValidatorMaxPausedBlocksParamName + default: + t.Fatalf("unexpected actor type %s", actorType.String()) + } + err := ctx.persistenceContext.SetParam(paramName, maxPausedBlocks) + require.NoError(t, err) + + actor := getFirstActor(t, ctx, actorType) + addr := actor.GetAddress() + addrBz, err := hex.DecodeString(addr) + require.NoError(t, err) + + // Pause the actor at height 0 + err = ctx.setActorPausedHeight(actorType, addrBz, 1) + require.NoError(t, err, "error setting actor pause height") + + // Start unstaking paused actors at the current height + err = ctx.beginUnstakingMaxPausedActors() + require.NoError(t, err, "error beginning unstaking max paused actors") + + // Verify that the actor is still staked + status, err := ctx.getActorStatus(actorType, addrBz) + require.NoError(t, err) + require.Equal(t, typesUtil.StakeStatus_Staked, status, "actor should be staked") + + // Start a new context when the actor still shouldn't be unstaked + require.NoError(t, ctx.Commit([]byte("empty qc"))) + require.NoError(t, ctx.Release()) + ctx = newTestingUtilityContext(t, int64(maxPausedBlocks)-1) + + // Start unstaking paused actors at the current height + err = ctx.beginUnstakingMaxPausedActors() + require.NoError(t, err, "error beginning unstaking max paused actors") + + // Verify that the actor is still staked + status, err = ctx.getActorStatus(actorType, addrBz) + require.NoError(t, err) + require.Equal(t, typesUtil.StakeStatus_Staked, status, "actor should be staked") + + // Start a new context when the actor should be unstaked + require.NoError(t, ctx.Release()) + ctx = newTestingUtilityContext(t, int64(maxPausedBlocks)+2) + + // Start unstaking paused actors at the current height + err = ctx.beginUnstakingMaxPausedActors() + require.NoError(t, err, "error beginning unstaking max paused actors") + + // Verify that the actor is still staked + status, err = ctx.getActorStatus(actorType, addrBz) + require.NoError(t, err) + require.Equal(t, typesUtil.StakeStatus_Unstaking, status, "actor should be staked") + }) + } +} + +func TestUtilityContext_BeginUnstakingActorsPausedBefore_UnbondUnstakingActors(t *testing.T) { + // The gov param for each actor will be set to this value + maxPausedBlocks := 5 + unstakingBlocks := 10 + + poolInitAMount := big.NewInt(10000000000000) + pauseHeight := int64(2) + + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.BeginUnstakingActorsPausedBefore", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 1) + + var poolName string + var paramName1 string + var paramName2 string + switch actorType { + case coreTypes.ActorType_ACTOR_TYPE_APP: + poolName = coreTypes.Pools_POOLS_APP_STAKE.FriendlyName() + paramName1 = typesUtil.AppMaxPauseBlocksParamName + paramName2 = typesUtil.AppUnstakingBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_FISH: + poolName = coreTypes.Pools_POOLS_FISHERMAN_STAKE.FriendlyName() + paramName1 = typesUtil.FishermanMaxPauseBlocksParamName + paramName2 = typesUtil.FishermanUnstakingBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: + poolName = coreTypes.Pools_POOLS_SERVICE_NODE_STAKE.FriendlyName() + paramName1 = typesUtil.ServiceNodeMaxPauseBlocksParamName + paramName2 = typesUtil.ServiceNodeUnstakingBlocksParamName + case coreTypes.ActorType_ACTOR_TYPE_VAL: + poolName = coreTypes.Pools_POOLS_VALIDATOR_STAKE.FriendlyName() + paramName1 = typesUtil.ValidatorMaxPausedBlocksParamName + paramName2 = typesUtil.ValidatorUnstakingBlocksParamName + default: + t.Fatalf("unexpected actor type %s", actorType.String()) + } + + er := ctx.persistenceContext.SetParam(paramName1, maxPausedBlocks) + require.NoError(t, er, "error setting max paused blocks") + er = ctx.persistenceContext.SetParam(paramName2, unstakingBlocks) + require.NoError(t, er, "error setting max paused blocks") + er = ctx.setPoolAmount(poolName, poolInitAMount) + require.NoError(t, er) + + // Validate the actor is not unstaking + actor := getFirstActor(t, ctx, actorType) + addr := actor.GetAddress() + require.Equal(t, typesUtil.HeightNotUsed, actor.GetUnstakingHeight(), "wrong starting status") + + addrBz, err := hex.DecodeString(addr) + require.NoError(t, err) + + // Set the actor to be paused at height 1 + err = ctx.setActorPausedHeight(actorType, addrBz, pauseHeight) + require.NoError(t, err, "error setting actor pause height") + + // Check that the actor is still not unstaking + actor = getActorByAddr(t, ctx, actorType, addr) + require.Equal(t, typesUtil.HeightNotUsed, actor.GetUnstakingHeight(), "incorrect unstaking height") + + // Verify that the actor is still not unstaking + err = ctx.beginUnstakingActorsPausedBefore(pauseHeight-1, actorType) + require.NoError(t, err, "error unstaking actor pause before height 0") + actor = getActorByAddr(t, ctx, actorType, addr) + require.Equal(t, typesUtil.HeightNotUsed, actor.GetUnstakingHeight(), "incorrect unstaking height") + + unbondingHeight := pauseHeight - 1 + int64(unstakingBlocks) + + // Verify that the actor is now unstaking + err = ctx.beginUnstakingActorsPausedBefore(pauseHeight+1, actorType) + require.NoError(t, err, "error unstaking actor pause before height 1") + actor = getActorByAddr(t, ctx, actorType, addr) + require.Equal(t, unbondingHeight, actor.GetUnstakingHeight(), "incorrect unstaking height") + + status, err := ctx.getActorStatus(actorType, addrBz) + require.NoError(t, err) + require.Equal(t, typesUtil.StakeStatus_Unstaking, status, "actor should be unstaking") + + // Commit the context and start a new one while the actor is still unstaking + require.NoError(t, ctx.Commit([]byte("empty QC"))) + ctx = newTestingUtilityContext(t, unbondingHeight-1) + + status, err = ctx.getActorStatus(actorType, addrBz) + require.NoError(t, err) + require.Equal(t, typesUtil.StakeStatus_Unstaking, status, "actor should be unstaking") + + // Release the context since there's nothing to commit and start a new one where the actors can be unbound + require.NoError(t, ctx.Release()) + ctx = newTestingUtilityContext(t, unbondingHeight) + + // Before unbonding, the pool amount should be unchanged + amount, err := ctx.getPoolAmount(poolName) + require.NoError(t, err) + require.Equal(t, poolInitAMount, amount, "pool amount should be unchanged") + + err = ctx.unbondUnstakingActors() + require.NoError(t, err) + + // Before unbonding, the money from the staked actor should go to the pool + amount, err = ctx.getPoolAmount(poolName) + require.NoError(t, err) + + stakedAmount, err := converters.StringToBigInt(actor.StakedAmount) + require.NoError(t, err) + expectedAmount := big.NewInt(0).Sub(poolInitAMount, stakedAmount) + require.Equalf(t, expectedAmount, amount, "pool amount should be unchanged for %s", poolName) + + // Status should be changed from Unstaking to Unstaked + status, err = ctx.getActorStatus(actorType, addrBz) + require.NoError(t, err) + require.Equal(t, typesUtil.StakeStatus_Unstaked, status, "actor should be unstaking") + }) + } +} + +func TestUtilityContext_GetExists(t *testing.T) { + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.GetExists", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + + actor := getFirstActor(t, ctx, actorType) + randAddr, err := crypto.GenerateAddress() + require.NoError(t, err) + + addrBz, err := hex.DecodeString(actor.GetAddress()) + require.NoError(t, err) + + exists, err := ctx.getActorExists(actorType, addrBz) + require.NoError(t, err) + require.True(t, exists, "actor that should exist does not") + + exists, err = ctx.getActorExists(actorType, randAddr) + require.NoError(t, err) + require.False(t, exists, "actor that shouldn't exist does") + }) + } +} + +func TestUtilityContext_GetOutputAddress(t *testing.T) { + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.GetOutputAddress", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + + actor := getFirstActor(t, ctx, actorType) + addrBz, err := hex.DecodeString(actor.GetAddress()) + require.NoError(t, err) + + outputAddress, err := ctx.getActorOutputAddress(actorType, addrBz) + require.NoError(t, err) + require.Equal(t, actor.GetOutput(), hex.EncodeToString(outputAddress), "unexpected output address") + }) + } +} + +func TestUtilityContext_GetPauseHeightIfExists(t *testing.T) { + pauseHeight := int64(100) + + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.GetPauseHeightIfExists", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + actor := getFirstActor(t, ctx, actorType) + + addrBz, err := hex.DecodeString(actor.GetAddress()) + require.NoError(t, err) + + // Paused height should not exist here + gotPauseHeight, err := ctx.getPausedHeightIfExists(actorType, addrBz) + require.NoError(t, err) + require.Equal(t, typesUtil.HeightNotUsed, gotPauseHeight) + + // Paused height should be set after this + err = ctx.setActorPausedHeight(actorType, addrBz, pauseHeight) + require.NoError(t, err, "error setting actor pause height") + + gotPauseHeight, err = ctx.getPausedHeightIfExists(actorType, addrBz) + require.NoError(t, err) + require.Equal(t, pauseHeight, gotPauseHeight, "unable to get pause height from the actor") + + // Random address shouldn't have a paused height + randAddr, er := crypto.GenerateAddress() + require.NoError(t, er) + + _, err = ctx.getPausedHeightIfExists(actorType, randAddr) + require.Error(t, err, "non existent actor should error") + }) + } +} +func TestUtilityContext_GetMessageEditStakeSignerCandidates(t *testing.T) { + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.GetMessageEditStakeSignerCandidates", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + actor := getFirstActor(t, ctx, actorType) + + addrBz, err := hex.DecodeString(actor.GetAddress()) + require.NoError(t, err) + + msgEditStake := &typesUtil.MessageEditStake{ + Address: addrBz, + Chains: test_artifacts.DefaultChains, + Amount: test_artifacts.DefaultStakeAmountString, + ActorType: actorType, + } + candidates, err := ctx.getMessageEditStakeSignerCandidates(msgEditStake) + require.NoError(t, err) + + require.Equal(t, 2, len(candidates), "unexpected number of candidates") + require.Equal(t, actor.GetOutput(), hex.EncodeToString(candidates[0]), "incorrect output candidate") + require.Equal(t, actor.GetAddress(), hex.EncodeToString(candidates[1]), "incorrect addr candidate") + }) + } +} + +func TestUtilityContext_GetMessageUnpauseSignerCandidates(t *testing.T) { + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.GetMessageUnpauseSignerCandidates", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + actor := getFirstActor(t, ctx, actorType) + + addrBz, err := hex.DecodeString(actor.GetAddress()) + require.NoError(t, err) + + msg := &typesUtil.MessageUnpause{ + Address: addrBz, + ActorType: actorType, + } + candidates, err := ctx.getMessageUnpauseSignerCandidates(msg) + require.NoError(t, err) + + require.Equal(t, 2, len(candidates), "unexpected number of candidates") + require.Equal(t, actor.GetOutput(), hex.EncodeToString(candidates[0]), "incorrect output candidate") + require.Equal(t, actor.GetAddress(), hex.EncodeToString(candidates[1]), "incorrect addr candidate") + }) + } +} + +func TestUtilityContext_GetMessageUnstakeSignerCandidates(t *testing.T) { + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + t.Run(fmt.Sprintf("%s.GetMessageUnstakeSignerCandidates", actorType.String()), func(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + actor := getFirstActor(t, ctx, actorType) + + addrBz, err := hex.DecodeString(actor.GetAddress()) + require.NoError(t, err) + + msg := &typesUtil.MessageUnstake{ + Address: addrBz, + ActorType: actorType, + } + candidates, err := ctx.getMessageUnstakeSignerCandidates(msg) + require.NoError(t, err) + + require.Equal(t, 2, len(candidates), "unexpected number of candidates") + require.Equal(t, actor.GetOutput(), hex.EncodeToString(candidates[0]), "incorrect output candidate") + require.Equal(t, actor.GetAddress(), hex.EncodeToString(candidates[1]), "incorrect addr candidate") + }) + } +} + +// Helpers + +func getAllTestingActors(t *testing.T, ctx *utilityContext, actorType coreTypes.ActorType) (actors []*coreTypes.Actor) { + actors = make([]*coreTypes.Actor, 0) + switch actorType { + case coreTypes.ActorType_ACTOR_TYPE_APP: + apps := getAllTestingApps(t, ctx) + actors = append(actors, apps...) + case coreTypes.ActorType_ACTOR_TYPE_FISH: + fish := getAllTestingFish(t, ctx) + actors = append(actors, fish...) + case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: + nodes := getAllTestingServicers(t, ctx) + actors = append(actors, nodes...) + case coreTypes.ActorType_ACTOR_TYPE_VAL: + vals := getAllTestingValidators(t, ctx) + actors = append(actors, vals...) + default: + t.Fatalf("unexpected actor type %s", actorType.String()) + } + + return +} + +func getFirstActor(t *testing.T, ctx *utilityContext, actorType coreTypes.ActorType) *coreTypes.Actor { + return getAllTestingActors(t, ctx, actorType)[0] +} + +func getActorByAddr(t *testing.T, ctx *utilityContext, actorType coreTypes.ActorType, addr string) (actor *coreTypes.Actor) { + actors := getAllTestingActors(t, ctx, actorType) + idx := slices.IndexFunc(actors, func(a *coreTypes.Actor) bool { return a.GetAddress() == addr }) + return actors[idx] +} + +func getAllTestingApps(t *testing.T, ctx *utilityContext) []*coreTypes.Actor { + actors, err := (ctx.persistenceContext).GetAllApps(ctx.height) + require.NoError(t, err) + return actors +} + +func getAllTestingValidators(t *testing.T, ctx *utilityContext) []*coreTypes.Actor { + actors, err := (ctx.persistenceContext).GetAllValidators(ctx.height) + require.NoError(t, err) + sort.Slice(actors, func(i, j int) bool { + return actors[i].GetAddress() < actors[j].GetAddress() + }) + return actors +} + +func getAllTestingFish(t *testing.T, ctx *utilityContext) []*coreTypes.Actor { + actors, err := (ctx.persistenceContext).GetAllFishermen(ctx.height) + require.NoError(t, err) + return actors +} + +func getAllTestingServicers(t *testing.T, ctx *utilityContext) []*coreTypes.Actor { + actors, err := (ctx.persistenceContext).GetAllServiceNodes(ctx.height) + require.NoError(t, err) + return actors +} diff --git a/utility/application.go b/utility/application.go new file mode 100644 index 000000000..0b3d8330e --- /dev/null +++ b/utility/application.go @@ -0,0 +1,61 @@ +package utility + +import ( + "math" + "math/big" + + "github.com/pokt-network/pocket/shared/converters" + typesUtil "github.com/pokt-network/pocket/utility/types" +) + +var ( + // TECHDEBT: Re-evalute the denomination of tokens used throughout the codebase. `MillionInt` is + // currently used to convert POKT to uPOKT but this is not clear throughout the codebase. + MillionInt = big.NewFloat(1e6) +) + +// TODO(M3): Re-evaluate the implementation in this function when implementing the Application Protocol +// and rate limiting +func (u *utilityContext) calculateMaxAppRelays(appStakeStr string) (string, typesUtil.Error) { + appStakeBigInt, er := converters.StringToBigInt(appStakeStr) + if er != nil { + return typesUtil.EmptyString, typesUtil.ErrStringToBigInt(er) + } + + stabilityAdjustment, err := u.getStabilityAdjustment() + if err != nil { + return typesUtil.EmptyString, err + } + + // INVESTIGATE: Need to understand what `baseline adjustment` is + baseRate, err := u.getBaselineAppStakeRate() + if err != nil { + return typesUtil.EmptyString, err + } + + // convert amount to float64 + appStake := big.NewFloat(float64(appStakeBigInt.Int64())) + + // get the percentage of the baseline stake rate; can be over 100% + basePercentage := big.NewFloat(float64(baseRate) / float64(100)) + + // multiply the two + baselineThroughput := basePercentage.Mul(basePercentage, appStake) + + // Convert POKT to uPOKT + baselineThroughput.Quo(baselineThroughput, MillionInt) + + // add staking adjustment; can be -ve + adjusted := baselineThroughput.Add(baselineThroughput, big.NewFloat(float64(stabilityAdjustment))) + + // truncate the integer + result, _ := adjusted.Int(nil) + + // bounding Max Amount of relays to maxint64 + max := big.NewInt(math.MaxInt64) + if i := result.Cmp(max); i < -1 { + result = max + } + + return converters.BigIntToString(result), nil +} diff --git a/utility/application_test.go b/utility/application_test.go new file mode 100644 index 000000000..7cbb926d5 --- /dev/null +++ b/utility/application_test.go @@ -0,0 +1,16 @@ +package utility + +import ( + "testing" + + coreTypes "github.com/pokt-network/pocket/shared/core/types" + "github.com/stretchr/testify/require" +) + +func TestUtilityContext_CalculateMaxAppRelays(t *testing.T) { + ctx := newTestingUtilityContext(t, 1) + actor := getFirstActor(t, ctx, coreTypes.ActorType_ACTOR_TYPE_APP) + newMaxRelays, err := ctx.calculateMaxAppRelays(actor.GetStakedAmount()) + require.NoError(t, err) + require.Equal(t, actor.GetGenericParam(), newMaxRelays) +} diff --git a/utility/block.go b/utility/block.go index 71f5c0993..c3d94cbaf 100644 --- a/utility/block.go +++ b/utility/block.go @@ -1,104 +1,106 @@ package utility import ( + "encoding/hex" + "fmt" "math/big" + "github.com/pokt-network/pocket/shared/converters" coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/modules" + moduleTypes "github.com/pokt-network/pocket/shared/modules/types" typesUtil "github.com/pokt-network/pocket/utility/types" ) -/* - This 'block' file contains all the lifecycle block operations. +// This 'block' file contains all the lifecycle block operations. - The ApplyBlock function is the 'main' operation that executes a 'block' object against the state. +// The ApplyBlock function is the 'main' operation that executes a 'block' object against the state. - Pocket Network adopt a Tendermint-like lifecycle of BeginBlock -> DeliverTx -> EndBlock in that - order. Like the name suggests, BeginBlock is an autonomous state operation that executes at the - beginning of every block DeliverTx individually applies each transaction against the state and - rolls it back (not yet implemented) if fails. Like BeginBlock, EndBlock is an autonomous state - operation that executes at the end of every block. -*/ +// Pocket Network adopt a Tendermint-like lifecycle of BeginBlock -> DeliverTx -> EndBlock in that +// order. Like the name suggests, BeginBlock is an autonomous state operation that executes at the +// beginning of every block DeliverTx individually applies each transaction against the state and +// rolls it back (not yet implemented) if fails. Like BeginBlock, EndBlock is an autonomous state +// operation that executes at the end of every block. // TODO: Make sure to call `utility.CheckTransaction`, which calls `persistence.TransactionExists` -func (u *UtilityContext) CreateAndApplyProposalBlock(proposer []byte, maxTransactionBytes int) (stateHash string, transactions [][]byte, err error) { - lastBlockByzantineVals, err := u.GetLastBlockByzantineValidators() +func (u *utilityContext) CreateAndApplyProposalBlock(proposer []byte, maxTransactionBytes int) (stateHash string, txs [][]byte, err error) { + lastBlockByzantineVals, err := u.getLastBlockByzantineValidators() if err != nil { return "", nil, err } // begin block lifecycle phase - if err := u.BeginBlock(lastBlockByzantineVals); err != nil { + if err := u.beginBlock(lastBlockByzantineVals); err != nil { return "", nil, err } - transactions = make([][]byte, 0) + txs = make([][]byte, 0) totalTxsSizeInBytes := 0 txIndex := 0 - mempool := u.GetBus().GetUtilityModule().GetMempool() + + mempool := u.getBus().GetUtilityModule().GetMempool() for !mempool.IsEmpty() { txBytes, err := mempool.PopTx() if err != nil { return "", nil, err } - transaction, err := typesUtil.TransactionFromBytes(txBytes) + tx, err := typesUtil.TxFromBytes(txBytes) if err != nil { return "", nil, err } txTxsSizeInBytes := len(txBytes) totalTxsSizeInBytes += txTxsSizeInBytes if totalTxsSizeInBytes >= maxTransactionBytes { - // Add back popped transaction to be applied in a future block + // Add back popped tx to be applied in a future block err := mempool.AddTx(txBytes) if err != nil { return "", nil, err } break // we've reached our max } - txResult, err := u.ApplyTransaction(txIndex, transaction) + txResult, err := u.applyTx(txIndex, tx) if err != nil { // TODO(#327): Properly implement 'unhappy path' for save points - if err := u.RevertLastSavePoint(); err != nil { + if err := u.revertLastSavePoint(); err != nil { return "", nil, err } totalTxsSizeInBytes -= txTxsSizeInBytes continue } - if err := u.Context.IndexTransaction(txResult); err != nil { - u.logger.Fatal().Err(err).Msg("TODO(#327): We can apply the transaction but not index it. Crash the process for now") + if err := u.persistenceContext.IndexTransaction(txResult); err != nil { + u.logger.Fatal().Err(err).Msgf("TODO(#327): We can apply the transaction but not index it. Crash the process for now: %v\n", err) } - transactions = append(transactions, txBytes) + txs = append(txs, txBytes) txIndex++ } - if err := u.EndBlock(proposer); err != nil { + if err := u.endBlock(proposer); err != nil { return "", nil, err } // return the app hash (consensus module will get the validator set directly) - stateHash, err = u.Context.ComputeStateHash() + stateHash, err = u.persistenceContext.ComputeStateHash() if err != nil { u.logger.Fatal().Err(err).Msg("Updating the app hash failed. TODO: Look into roll-backing the entire commit...") } u.logger.Info().Msgf("CreateAndApplyProposalBlock - computed state hash: %s", stateHash) - return stateHash, transactions, err + return stateHash, txs, err } // TODO: Make sure to call `utility.CheckTransaction`, which calls `persistence.TransactionExists` // CLEANUP: code re-use ApplyBlock() for CreateAndApplyBlock() -func (u *UtilityContext) ApplyBlock() (string, error) { - lastByzantineValidators, err := u.GetLastBlockByzantineValidators() +func (u *utilityContext) ApplyBlock() (string, error) { + lastByzantineValidators, err := u.getLastBlockByzantineValidators() if err != nil { return "", err } // begin block lifecycle phase - if err := u.BeginBlock(lastByzantineValidators); err != nil { + if err := u.beginBlock(lastByzantineValidators); err != nil { return "", err } // deliver txs lifecycle phase - for index, transactionProtoBytes := range u.proposalBlockTxs { - tx, err := typesUtil.TransactionFromBytes(transactionProtoBytes) + for index, txProtoBytes := range u.proposalBlockTxs { + tx, err := typesUtil.TxFromBytes(txProtoBytes) if err != nil { return "", err } @@ -110,28 +112,28 @@ func (u *UtilityContext) ApplyBlock() (string, error) { // Or wait until the entire lifecycle is over to evaluate an 'invalid' block // Validate and apply the transaction to the Postgres database - txResult, err := u.ApplyTransaction(index, tx) + txResult, err := u.applyTx(index, tx) if err != nil { return "", err } - if err := u.Context.IndexTransaction(txResult); err != nil { - u.logger.Fatal().Err(err).Msg("TODO(#327): We can apply the transaction but not index it. Crash the process for now") + if err := u.persistenceContext.IndexTransaction(txResult); err != nil { + u.logger.Fatal().Err(err).Msgf("TODO(#327): We can apply the transaction but not index it. Crash the process for now: %v\n", err) } // TODO: if found, remove transaction from mempool. // DISCUSS: What if the context is rolled back or cancelled. Do we add it back to the mempool? - // if err := u.Mempool.RemoveTransaction(transaction); err != nil { + // if err := u.Mempool.RemoveTx(tx); err != nil { // return nil, err // } } // end block lifecycle phase - if err := u.EndBlock(u.proposalProposerAddr); err != nil { + if err := u.endBlock(u.proposalProposerAddr); err != nil { return "", err } // return the app hash (consensus module will get the validator set directly) - stateHash, err := u.Context.ComputeStateHash() + stateHash, err := u.persistenceContext.ComputeStateHash() if err != nil { u.logger.Fatal().Err(err).Msg("Updating the app hash failed. TODO: Look into roll-backing the entire commit...") return "", typesUtil.ErrAppHash(err) @@ -142,41 +144,37 @@ func (u *UtilityContext) ApplyBlock() (string, error) { return stateHash, nil } -func (u *UtilityContext) BeginBlock(previousBlockByzantineValidators [][]byte) typesUtil.Error { - if err := u.HandleByzantineValidators(previousBlockByzantineValidators); err != nil { +func (u *utilityContext) beginBlock(previousBlockByzantineValidators [][]byte) typesUtil.Error { + if err := u.handleByzantineValidators(previousBlockByzantineValidators); err != nil { return err } return nil } -func (u *UtilityContext) EndBlock(proposer []byte) typesUtil.Error { +func (u *utilityContext) endBlock(proposer []byte) typesUtil.Error { // reward the block proposer - if err := u.HandleProposalRewards(proposer); err != nil { + if err := u.handleProposerRewards(proposer); err != nil { return err } // unstake actors that have been 'unstaking' for the UnstakingBlocks - if err := u.UnstakeActorsThatAreReady(); err != nil { + if err := u.unbondUnstakingActors(); err != nil { return err } // begin unstaking the actors who have been paused for MaxPauseBlocks - if err := u.BeginUnstakingMaxPaused(); err != nil { + if err := u.beginUnstakingMaxPausedActors(); err != nil { return err } return nil } -// HandleByzantineValidators handles the validators who either didn't sign at all or disagreed with the 2/3+ majority -func (u *UtilityContext) HandleByzantineValidators(lastBlockByzantineValidators [][]byte) typesUtil.Error { - latestBlockHeight, err := u.GetLatestBlockHeight() - if err != nil { - return err - } - maxMissedBlocks, err := u.GetValidatorMaxMissedBlocks() +// handleByzantineValidators handles the validators who either didn't sign at all or disagreed with the 2/3+ majority +func (u *utilityContext) handleByzantineValidators(lastBlockByzantineValidators [][]byte) typesUtil.Error { + maxMissedBlocks, err := u.getValidatorMaxMissedBlocks() if err != nil { return err } for _, address := range lastBlockByzantineValidators { - numberOfMissedBlocks, err := u.GetValidatorMissedBlocks(address) + numberOfMissedBlocks, err := u.getValidatorMissedBlocks(address) if err != nil { return err } @@ -185,47 +183,47 @@ func (u *UtilityContext) HandleByzantineValidators(lastBlockByzantineValidators // handle if over the threshold if numberOfMissedBlocks >= maxMissedBlocks { // pause the validator and reset missed blocks - if err := u.PauseValidatorAndSetMissedBlocks(address, latestBlockHeight, int(typesUtil.HeightNotUsed)); err != nil { + if err := u.pauseValidatorAndSetMissedBlocks(address, u.height, int(typesUtil.HeightNotUsed)); err != nil { return err } // burn validator for missing blocks - burnPercentage, err := u.GetMissedBlocksBurnPercentage() + burnPercentage, err := u.getMissedBlocksBurnPercentage() if err != nil { return err } - if err := u.BurnActor(coreTypes.ActorType_ACTOR_TYPE_VAL, burnPercentage, address); err != nil { + if err := u.burnValidator(burnPercentage, address); err != nil { return err } - } else if err := u.SetValidatorMissedBlocks(address, numberOfMissedBlocks); err != nil { + } else if err := u.setValidatorMissedBlocks(address, numberOfMissedBlocks); err != nil { return err } } return nil } -func (u *UtilityContext) UnstakeActorsThatAreReady() (err typesUtil.Error) { +func (u *utilityContext) unbondUnstakingActors() (err typesUtil.Error) { var er error store := u.Store() - latestHeight, err := u.GetLatestBlockHeight() - if err != nil { - return err - } - for _, actorTypeInt32 := range coreTypes.ActorType_value { - var readyToUnstake []modules.IUnstakingActor - actorType := coreTypes.ActorType(actorTypeInt32) + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + + var readyToUnstake []*moduleTypes.UnstakingActor var poolName string switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - readyToUnstake, er = store.GetAppsReadyToUnstake(latestHeight, int32(typesUtil.StakeStatus_Unstaking)) + readyToUnstake, er = store.GetAppsReadyToUnstake(u.height, int32(typesUtil.StakeStatus_Unstaking)) poolName = coreTypes.Pools_POOLS_APP_STAKE.FriendlyName() case coreTypes.ActorType_ACTOR_TYPE_FISH: - readyToUnstake, er = store.GetFishermenReadyToUnstake(latestHeight, int32(typesUtil.StakeStatus_Unstaking)) + readyToUnstake, er = store.GetFishermenReadyToUnstake(u.height, int32(typesUtil.StakeStatus_Unstaking)) poolName = coreTypes.Pools_POOLS_FISHERMAN_STAKE.FriendlyName() case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - readyToUnstake, er = store.GetServiceNodesReadyToUnstake(latestHeight, int32(typesUtil.StakeStatus_Unstaking)) + readyToUnstake, er = store.GetServiceNodesReadyToUnstake(u.height, int32(typesUtil.StakeStatus_Unstaking)) poolName = coreTypes.Pools_POOLS_SERVICE_NODE_STAKE.FriendlyName() case coreTypes.ActorType_ACTOR_TYPE_VAL: - readyToUnstake, er = store.GetValidatorsReadyToUnstake(latestHeight, int32(typesUtil.StakeStatus_Unstaking)) + readyToUnstake, er = store.GetValidatorsReadyToUnstake(u.height, int32(typesUtil.StakeStatus_Unstaking)) poolName = coreTypes.Pools_POOLS_VALIDATOR_STAKE.FriendlyName() case coreTypes.ActorType_ACTOR_TYPE_UNSPECIFIED: continue @@ -234,10 +232,22 @@ func (u *UtilityContext) UnstakeActorsThatAreReady() (err typesUtil.Error) { return typesUtil.ErrGetReadyToUnstake(er) } for _, actor := range readyToUnstake { - if err := u.SubPoolAmount(poolName, actor.GetStakeAmount()); err != nil { + if poolName == coreTypes.Pools_POOLS_VALIDATOR_STAKE.FriendlyName() { + fmt.Println("unstaking validator", actor.StakeAmount) + } + stakeAmount, er := converters.StringToBigInt(actor.StakeAmount) + if er != nil { + return typesUtil.ErrStringToBigInt(er) + } + outputAddrBz, er := hex.DecodeString(actor.OutputAddress) + if er != nil { + return typesUtil.ErrHexDecodeFromString(er) + } + + if err := u.subPoolAmount(poolName, stakeAmount); err != nil { return err } - if err := u.AddAccountAmountString(actor.GetOutputAddress(), actor.GetStakeAmount()); err != nil { + if err := u.addAccountAmount(outputAddrBz, stakeAmount); err != nil { return err } } @@ -245,48 +255,47 @@ func (u *UtilityContext) UnstakeActorsThatAreReady() (err typesUtil.Error) { return nil } -func (u *UtilityContext) BeginUnstakingMaxPaused() (err typesUtil.Error) { - latestHeight, err := u.GetLatestBlockHeight() - if err != nil { - return err - } - for _, actorTypeInt32 := range coreTypes.ActorType_value { - actorType := coreTypes.ActorType(actorTypeInt32) +func (u *utilityContext) beginUnstakingMaxPausedActors() (err typesUtil.Error) { + for actorTypeNum := range coreTypes.ActorType_name { + if actorTypeNum == 0 { // ACTOR_TYPE_UNSPECIFIED + continue + } + actorType := coreTypes.ActorType(actorTypeNum) + if actorType == coreTypes.ActorType_ACTOR_TYPE_UNSPECIFIED { continue } - maxPausedBlocks, err := u.GetMaxPausedBlocks(actorType) + maxPausedBlocks, err := u.getMaxAllowedPausedBlocks(actorType) if err != nil { return err } - beforeHeight := latestHeight - int64(maxPausedBlocks) - // genesis edge case - if beforeHeight < 0 { - beforeHeight = 0 + maxPauseHeight := u.height - int64(maxPausedBlocks) + if maxPauseHeight < 0 { // genesis edge case + maxPauseHeight = 0 } - if err := u.UnstakeActorPausedBefore(beforeHeight, actorType); err != nil { + if err := u.beginUnstakingActorsPausedBefore(maxPauseHeight, actorType); err != nil { return err } } return nil } -func (u *UtilityContext) UnstakeActorPausedBefore(pausedBeforeHeight int64, actorType coreTypes.ActorType) (err typesUtil.Error) { +func (u *utilityContext) beginUnstakingActorsPausedBefore(pausedBeforeHeight int64, actorType coreTypes.ActorType) (err typesUtil.Error) { var er error store := u.Store() - unstakingHeight, err := u.GetUnstakingHeight(actorType) + unbondingHeight, err := u.getUnbondingHeight(actorType) if err != nil { return err } switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - er = store.SetAppStatusAndUnstakingHeightIfPausedBefore(pausedBeforeHeight, unstakingHeight, int32(typesUtil.StakeStatus_Unstaking)) + er = store.SetAppStatusAndUnstakingHeightIfPausedBefore(pausedBeforeHeight, unbondingHeight, int32(typesUtil.StakeStatus_Unstaking)) case coreTypes.ActorType_ACTOR_TYPE_FISH: - er = store.SetFishermanStatusAndUnstakingHeightIfPausedBefore(pausedBeforeHeight, unstakingHeight, int32(typesUtil.StakeStatus_Unstaking)) + er = store.SetFishermanStatusAndUnstakingHeightIfPausedBefore(pausedBeforeHeight, unbondingHeight, int32(typesUtil.StakeStatus_Unstaking)) case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - er = store.SetServiceNodeStatusAndUnstakingHeightIfPausedBefore(pausedBeforeHeight, unstakingHeight, int32(typesUtil.StakeStatus_Unstaking)) + er = store.SetServiceNodeStatusAndUnstakingHeightIfPausedBefore(pausedBeforeHeight, unbondingHeight, int32(typesUtil.StakeStatus_Unstaking)) case coreTypes.ActorType_ACTOR_TYPE_VAL: - er = store.SetValidatorsStatusAndUnstakingHeightIfPausedBefore(pausedBeforeHeight, unstakingHeight, int32(typesUtil.StakeStatus_Unstaking)) + er = store.SetValidatorsStatusAndUnstakingHeightIfPausedBefore(pausedBeforeHeight, unbondingHeight, int32(typesUtil.StakeStatus_Unstaking)) } if er != nil { return typesUtil.ErrSetStatusPausedBefore(er, pausedBeforeHeight) @@ -294,16 +303,16 @@ func (u *UtilityContext) UnstakeActorPausedBefore(pausedBeforeHeight int64, acto return nil } -func (u *UtilityContext) HandleProposalRewards(proposer []byte) typesUtil.Error { +func (u *utilityContext) handleProposerRewards(proposer []byte) typesUtil.Error { feePoolName := coreTypes.Pools_POOLS_FEE_COLLECTOR.FriendlyName() - feesAndRewardsCollected, err := u.GetPoolAmount(feePoolName) + feesAndRewardsCollected, err := u.getPoolAmount(feePoolName) if err != nil { return err } - if err := u.SetPoolAmount(feePoolName, big.NewInt(0)); err != nil { + if err := u.setPoolAmount(feePoolName, big.NewInt(0)); err != nil { return err } - proposerCutPercentage, err := u.GetProposerPercentageOfFees() + proposerCutPercentage, err := u.getProposerPercentageOfFees() if err != nil { return err } @@ -316,20 +325,20 @@ func (u *UtilityContext) HandleProposalRewards(proposer []byte) typesUtil.Error amountToProposerFloat.Quo(amountToProposerFloat, big.NewFloat(100)) amountToProposer, _ := amountToProposerFloat.Int(nil) amountToDAO := feesAndRewardsCollected.Sub(feesAndRewardsCollected, amountToProposer) - if err := u.AddAccountAmount(proposer, amountToProposer); err != nil { + if err := u.addAccountAmount(proposer, amountToProposer); err != nil { return err } - if err := u.AddPoolAmount(coreTypes.Pools_POOLS_DAO.FriendlyName(), amountToDAO); err != nil { + if err := u.addPoolAmount(coreTypes.Pools_POOLS_DAO.FriendlyName(), amountToDAO); err != nil { return err } return nil } -// GetValidatorMissedBlocks gets the total blocks that a validator has not signed a certain window of time denominated by blocks -func (u *UtilityContext) GetValidatorMissedBlocks(address []byte) (int, typesUtil.Error) { - store, height, err := u.GetStoreAndHeight() +// TODO: Need to design & document this business logic. +func (u *utilityContext) getValidatorMissedBlocks(address []byte) (int, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() if err != nil { - return 0, err + return 0, typesUtil.ErrGetHeight(err) } missedBlocks, er := store.GetValidatorMissedBlocks(address, height) if er != nil { @@ -338,7 +347,8 @@ func (u *UtilityContext) GetValidatorMissedBlocks(address []byte) (int, typesUti return missedBlocks, nil } -func (u *UtilityContext) PauseValidatorAndSetMissedBlocks(address []byte, pauseHeight int64, missedBlocks int) typesUtil.Error { +// TODO: Need to design & document this business logic. +func (u *utilityContext) pauseValidatorAndSetMissedBlocks(address []byte, pauseHeight int64, missedBlocks int) typesUtil.Error { store := u.Store() if err := store.SetValidatorPauseHeightAndMissedBlocks(address, pauseHeight, missedBlocks); err != nil { return typesUtil.ErrSetPauseHeight(err) @@ -346,7 +356,8 @@ func (u *UtilityContext) PauseValidatorAndSetMissedBlocks(address []byte, pauseH return nil } -func (u *UtilityContext) SetValidatorMissedBlocks(address []byte, missedBlocks int) typesUtil.Error { +// TODO: Need to design & document this business logic. +func (u *utilityContext) setValidatorMissedBlocks(address []byte, missedBlocks int) typesUtil.Error { store := u.Store() er := store.SetValidatorMissedBlocks(address, missedBlocks) if er != nil { @@ -354,3 +365,8 @@ func (u *UtilityContext) SetValidatorMissedBlocks(address []byte, missedBlocks i } return nil } + +// TODO: Need to design & document this business logic. +func (u *utilityContext) getLastBlockByzantineValidators() ([][]byte, error) { + return nil, nil +} diff --git a/utility/test/block_test.go b/utility/block_test.go similarity index 78% rename from utility/test/block_test.go rename to utility/block_test.go index 043cc3d66..17759ddf9 100644 --- a/utility/test/block_test.go +++ b/utility/block_test.go @@ -1,17 +1,16 @@ -package test +package utility import ( "encoding/hex" "math/big" "testing" - "github.com/pokt-network/pocket/runtime/test_artifacts" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/stretchr/testify/require" ) func TestUtilityContext_ApplyBlock(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) tx, startingBalance, amountSent, signer := newTestingTransaction(t, ctx) txBz, er := tx.Bytes() @@ -22,7 +21,7 @@ func TestUtilityContext_ApplyBlock(t *testing.T) { addrBz, err := hex.DecodeString(proposer.GetAddress()) require.NoError(t, err) - proposerBeforeBalance, err := ctx.GetAccountAmount(addrBz) + proposerBeforeBalance, err := ctx.getAccountAmount(addrBz) require.NoError(t, err) err = ctx.SetProposalBlock("", addrBz, [][]byte{txBz}) @@ -34,20 +33,20 @@ func TestUtilityContext_ApplyBlock(t *testing.T) { // // TODO: Uncomment this once `GetValidatorMissedBlocks` is implemented. // beginBlock logic verify - // missed, err := ctx.GetValidatorMissedBlocks(byzantine.Address) + // missed, err := ctx.getValidatorMissedBlocks(byzantine.Address) // require.NoError(t, err) // require.Equal(t, missed, 1) - feeBig, err := ctx.GetMessageSendFee() + feeBig, err := ctx.getMessageSendFee() require.NoError(t, err) expectedAmountSubtracted := big.NewInt(0).Add(amountSent, feeBig) expectedAfterBalance := big.NewInt(0).Sub(startingBalance, expectedAmountSubtracted) - amountAfter, err := ctx.GetAccountAmount(signer.Address()) + amountAfter, err := ctx.getAccountAmount(signer.Address()) require.NoError(t, err) require.Equal(t, expectedAfterBalance, amountAfter, "unexpected after balance; expected %v got %v", expectedAfterBalance, amountAfter) - proposerCutPercentage, err := ctx.GetProposerPercentageOfFees() + proposerCutPercentage, err := ctx.getProposerPercentageOfFees() require.NoError(t, err) feesAndRewardsCollectedFloat := new(big.Float).SetInt(feeBig) @@ -55,17 +54,16 @@ func TestUtilityContext_ApplyBlock(t *testing.T) { feesAndRewardsCollectedFloat.Quo(feesAndRewardsCollectedFloat, big.NewFloat(100)) expectedProposerBalanceDifference, _ := feesAndRewardsCollectedFloat.Int(nil) - proposerAfterBalance, err := ctx.GetAccountAmount(addrBz) + proposerAfterBalance, err := ctx.getAccountAmount(addrBz) require.NoError(t, err) proposerBalanceDifference := big.NewInt(0).Sub(proposerAfterBalance, proposerBeforeBalance) require.Equal(t, expectedProposerBalanceDifference, proposerBalanceDifference, "unexpected before / after balance difference") - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_BeginBlock(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) tx, _, _, _ := newTestingTransaction(t, ctx) proposer := getFirstActor(t, ctx, coreTypes.ActorType_ACTOR_TYPE_VAL) @@ -84,15 +82,14 @@ func TestUtilityContext_BeginBlock(t *testing.T) { // // TODO: Uncomment this once `GetValidatorMissedBlocks` is implemented. // beginBlock logic verify - // missed, err := ctx.GetValidatorMissedBlocks(byzantine.Address) + // missed, err := ctx.getValidatorMissedBlocks(byzantine.Address) // require.NoError(t, err) // require.Equal(t, missed, 1) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_EndBlock(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) tx, _, _, _ := newTestingTransaction(t, ctx) proposer := getFirstActor(t, ctx, coreTypes.ActorType_ACTOR_TYPE_VAL) @@ -103,7 +100,7 @@ func TestUtilityContext_EndBlock(t *testing.T) { addrBz, er := hex.DecodeString(proposer.GetAddress()) require.NoError(t, er) - proposerBeforeBalance, err := ctx.GetAccountAmount(addrBz) + proposerBeforeBalance, err := ctx.getAccountAmount(addrBz) require.NoError(t, err) er = ctx.SetProposalBlock("", addrBz, [][]byte{txBz}) @@ -112,21 +109,20 @@ func TestUtilityContext_EndBlock(t *testing.T) { _, er = ctx.ApplyBlock() require.NoError(t, er) - feeBig, err := ctx.GetMessageSendFee() + feeBig, err := ctx.getMessageSendFee() require.NoError(t, err) - proposerCutPercentage, err := ctx.GetProposerPercentageOfFees() + proposerCutPercentage, err := ctx.getProposerPercentageOfFees() require.NoError(t, err) feesAndRewardsCollectedFloat := new(big.Float).SetInt(feeBig) feesAndRewardsCollectedFloat.Mul(feesAndRewardsCollectedFloat, big.NewFloat(float64(proposerCutPercentage))) feesAndRewardsCollectedFloat.Quo(feesAndRewardsCollectedFloat, big.NewFloat(100)) expectedProposerBalanceDifference, _ := feesAndRewardsCollectedFloat.Int(nil) - proposerAfterBalance, err := ctx.GetAccountAmount(addrBz) + proposerAfterBalance, err := ctx.getAccountAmount(addrBz) require.NoError(t, err) proposerBalanceDifference := big.NewInt(0).Sub(proposerAfterBalance, proposerBeforeBalance) require.Equal(t, expectedProposerBalanceDifference, proposerBalanceDifference) - test_artifacts.CleanupTest(ctx) } diff --git a/utility/context.go b/utility/context.go index feb22f68a..354fb0b32 100644 --- a/utility/context.go +++ b/utility/context.go @@ -3,147 +3,123 @@ package utility import ( "encoding/hex" - "github.com/pokt-network/pocket/shared/codec" "github.com/pokt-network/pocket/shared/modules" typesUtil "github.com/pokt-network/pocket/utility/types" ) -// TODO: The implementation of `UtilityContext` should not be exposed. -type UtilityContext struct { - bus modules.Bus - Height int64 - Context *Context // IMPROVE: Rename to `persistenceContext` or `storeContext` or `reversibleContext`? +type utilityContext struct { + bus modules.Bus + height int64 - // Data related to the Block being proposed - // TECHDEBT: When we consolidate everything to have a single `Block` object (a struct backed by a protobuf), - // this can be simplified to just point to that object. + persistenceContext modules.PersistenceRWContext + savePointsSet map[string]struct{} + savePointsList [][]byte logger modules.Logger + // TECHDEBT: Consolidate all these types with the shared Protobuf struct and create a `proposalBlock` proposalProposerAddr []byte proposalStateHash string proposalBlockTxs [][]byte } -// IMPROVE: Consider renaming to `persistenceContext` or `storeContext`? -type Context struct { - // CLEANUP: Since `Context` embeds `PersistenceRWContext`, we don't need to do `u.Context.PersistenceRWContext`, but can call `u.Context` directly - modules.PersistenceRWContext - // TODO(#327): `SavePoints`` have not been implemented yet - SavePointsM map[string]struct{} - SavePoints [][]byte -} - func (u *utilityModule) NewContext(height int64) (modules.UtilityContext, error) { ctx, err := u.GetBus().GetPersistenceModule().NewRWContext(height) if err != nil { return nil, typesUtil.ErrNewPersistenceContext(err) } - return &UtilityContext{ - bus: u.GetBus(), - Height: height, - logger: u.logger, - Context: &Context{ - PersistenceRWContext: ctx, - SavePoints: make([][]byte, 0), - SavePointsM: make(map[string]struct{}), - }, + return &utilityContext{ + bus: u.GetBus(), + height: height, + logger: u.logger, + persistenceContext: ctx, + savePointsList: make([][]byte, 0), + savePointsSet: make(map[string]struct{}), }, nil } -func (p *UtilityContext) SetProposalBlock(blockHash string, proposerAddr []byte, transactions [][]byte) error { +func (p *utilityContext) SetProposalBlock(blockHash string, proposerAddr []byte, txs [][]byte) error { p.proposalProposerAddr = proposerAddr p.proposalStateHash = blockHash - p.proposalBlockTxs = transactions + p.proposalBlockTxs = txs return nil } -func (u *UtilityContext) Store() *Context { - return u.Context +func (u *utilityContext) Store() modules.PersistenceRWContext { + return u.persistenceContext } -func (u *UtilityContext) GetPersistenceContext() modules.PersistenceRWContext { - return u.Context.PersistenceRWContext +func (u *utilityContext) GetPersistenceContext() modules.PersistenceRWContext { + return u.persistenceContext } -func (u *UtilityContext) Commit(quorumCert []byte) error { - if err := u.Context.PersistenceRWContext.Commit(u.proposalProposerAddr, quorumCert); err != nil { +func (u *utilityContext) Commit(quorumCert []byte) error { + if err := u.persistenceContext.Commit(u.proposalProposerAddr, quorumCert); err != nil { return err } - u.Context = nil + u.persistenceContext = nil return nil } -func (u *UtilityContext) Release() error { - if u.Context == nil { +func (u *utilityContext) Release() error { + if u.persistenceContext == nil { return nil } - if err := u.Context.Release(); err != nil { + if err := u.persistenceContext.Release(); err != nil { return err } - u.Context = nil + u.persistenceContext = nil return nil } -func (u *UtilityContext) GetLatestBlockHeight() (int64, typesUtil.Error) { - height, er := u.Store().GetHeight() - if er != nil { - return 0, typesUtil.ErrGetHeight(er) - } - return height, nil -} - -func (u *UtilityContext) GetStoreAndHeight() (*Context, int64, typesUtil.Error) { +// TECHDEBT: We should be using the height of the context and shouldn't need to be retrieving +// the height from the store either for "current height" operations. +func (u *utilityContext) getStoreAndHeight() (modules.PersistenceRWContext, int64, error) { store := u.Store() - height, er := store.GetHeight() - if er != nil { - return nil, 0, typesUtil.ErrGetHeight(er) - } - return store, height, nil -} - -func (u *UtilityContext) Codec() codec.Codec { - return codec.GetCodec() + height, err := store.GetHeight() + return store, height, err } -func (u *UtilityContext) RevertLastSavePoint() typesUtil.Error { - if len(u.Context.SavePointsM) == typesUtil.ZeroInt { +// TODO: This has not been tested or investigated in detail +func (u *utilityContext) revertLastSavePoint() typesUtil.Error { + if len(u.savePointsSet) == typesUtil.ZeroInt { return typesUtil.ErrEmptySavePoints() } var key []byte - popIndex := len(u.Context.SavePoints) - 1 - key, u.Context.SavePoints = u.Context.SavePoints[popIndex], u.Context.SavePoints[:popIndex] - delete(u.Context.SavePointsM, hex.EncodeToString(key)) - if err := u.Context.PersistenceRWContext.RollbackToSavePoint(key); err != nil { + popIndex := len(u.savePointsList) - 1 + key, u.savePointsList = u.savePointsList[popIndex], u.savePointsList[:popIndex] + delete(u.savePointsSet, hex.EncodeToString(key)) + if err := u.persistenceContext.RollbackToSavePoint(key); err != nil { return typesUtil.ErrRollbackSavePoint(err) } return nil } -func (u *UtilityContext) NewSavePoint(transactionHash []byte) typesUtil.Error { - if err := u.Context.PersistenceRWContext.NewSavePoint(transactionHash); err != nil { +//nolint:unused // TODO: This has not been tested or investigated in detail +func (u *utilityContext) newSavePoint(txHashBz []byte) typesUtil.Error { + if err := u.persistenceContext.NewSavePoint(txHashBz); err != nil { return typesUtil.ErrNewSavePoint(err) } - txHash := hex.EncodeToString(transactionHash) - if _, exists := u.Context.SavePointsM[txHash]; exists { + txHash := hex.EncodeToString(txHashBz) + if _, exists := u.savePointsSet[txHash]; exists { return typesUtil.ErrDuplicateSavePoint() } - u.Context.SavePoints = append(u.Context.SavePoints, transactionHash) - u.Context.SavePointsM[txHash] = struct{}{} + u.savePointsList = append(u.savePointsList, txHashBz) + u.savePointsSet[txHash] = struct{}{} return nil } -func (u *UtilityContext) GetBus() modules.Bus { +func (u *utilityContext) getBus() modules.Bus { return u.bus } -func (u *UtilityContext) WithBus(bus modules.Bus) *UtilityContext { +func (u *utilityContext) setBus(bus modules.Bus) *utilityContext { u.bus = bus return u } -func (c *Context) Reset() typesUtil.Error { - if err := c.PersistenceRWContext.Release(); err != nil { +func (c *utilityContext) Reset() typesUtil.Error { + if err := c.persistenceContext.Release(); err != nil { return typesUtil.ErrResetContext(err) } return nil diff --git a/utility/doc/CHANGELOG.md b/utility/doc/CHANGELOG.md index 03b22a5d5..036ea583e 100644 --- a/utility/doc/CHANGELOG.md +++ b/utility/doc/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.0.0.27] - 2023-02-14 + +- Added a `Validatable` type for basic validation +- Split business logic specific to certain actors (e.g. validator reward, app relays, message handling) into separate files +- Reduced the scope of functions and types that shouldn’t be exposed +- Upgraded the actors tests - a lot went here to help with understanding what’s going on but it’s still just a start +- Remove the `Context` struct; unnecessary abstraction +- Added comments and guidance on message, transaction and signature validation +- Added `ITransaction`, an interface for the `Transaction` protocol to help capture the functionality it adds to the core type +- DOC: Delineate between unstaking & unbonding in a few places throughout the codebase +- BUG: `tx.Equals` was comparing the same transaction against itself (major bug) +- BUG: `StakingStatus` enums in utility did not reflect the same protocol as in persistence (needs to be consolidated) + ## [0.0.0.26] - 2023-02-07 - Documentation update diff --git a/utility/doc/PROTOCOL_RELAY.md b/utility/doc/PROTOCOL_RELAY.md index b54eaa171..ceedbd77c 100644 --- a/utility/doc/PROTOCOL_RELAY.md +++ b/utility/doc/PROTOCOL_RELAY.md @@ -1,10 +1,10 @@ # Relay Protocol -### Background +## Background The Relay Protocol is a fundamental sequence that makes up the building blocks of Pocket Network's Utility. -In Pocket Network, a Relay is a Read or Write API request operation to a 3rd party `RelayChain`. +In Pocket Network, a `Relay` is a Read / Write API request operation to a 3rd party `RelayChain`. The Relay Protocol is the servicing lifecycle that poises staked ServiceNodes to be able to complete Relays on behalf of the network. @@ -19,27 +19,26 @@ The foundational lifecycle of the Relay Protocol is: ```mermaid sequenceDiagram - title Steps 1 to 3 - autonumber - actor App - actor Client - actor Service Node - participant Internal State - participant Internal Storage - participant External Relay Chain - App->>Client: Provision(AppAuthToken) - loop Repeats Throughout Session Duration - Client->>Client: Sign(Relay) - Client->>Service Node: Send(Relay) - Service Node->>Internal State: Validate(Relay) - Internal State->>Service Node: IsValid(Relay) - Service Node->>Internal Storage: IfValid(Relay) -> Persist(Relay) - Service Node->>External Relay Chain: Execute(Relay, RelayChainURL) - External Relay Chain->>Service Node: RelayResponse = GetResponse(RelayChain) - Service Node->>Service Node: Sign(RelayResponse) - Service Node ->> Client: Send(RelayResponse) - - end + title Steps 1 to 3 + autonumber + actor App + actor Client + actor Service Node + participant Internal State + participant Internal Storage + participant External Relay Chain + App->>Client: Provision(AppAuthToken) + loop Repeats Throughout Session Duration + Client->>Client: Sign(Relay) + Client->>Service Node: Send(Relay) + Service Node->>Internal State: Validate(Relay) + Internal State->>Service Node: IsValid(Relay) + Service Node->>Internal Storage: IfValid(Relay) -> Persist(Relay) + Service Node->>External Relay Chain: Execute(Relay, RelayChainURL) + External Relay Chain->>Service Node: RelayResponse = GetResponse(RelayChain) + Service Node->>Service Node: Sign(RelayResponse) + Service Node ->> Client: Send(RelayResponse) + end ``` 4. Wait for `Session` end / secret key to be revealed @@ -172,10 +171,10 @@ This algorithm is not yet documented anywhere, so the following links can act as - Twitter Thread: https://twitter.com/o_rourke/status/1263847357122326530 - Plasma core Merkle Sum Tree: https://plasma-core.readthedocs.io/en/latest/specs/sum-tree.html -**Source code references:** +**V0 Source Code References:** -- Merkle: https://github.com/pokt-network/pocket-core/blob/staging/x/pocketcore/types/merkle.go -- Claim: https://github.com/pokt-network/pocket-core/blob/staging/x/pocketcore/keeper/claim.go -- Proof: https://github.com/pokt-network/pocket-core/blob/staging/x/pocketcore/keeper/proof.go +- Merkle: [pocketcore/types/merkle.go](https://github.com/pokt-network/pocket-core/blob/staging/x/pocketcore/types/merkle.go) +- Claim: [pocketcore/keeper/claim.go](https://github.com/pokt-network/pocket-core/blob/staging/x/pocketcore/keeper/claim.go) +- Proof: [pocketcore/keeper/proof.go](https://github.com/pokt-network/pocket-core/blob/staging/x/pocketcore/keeper/proof.go) diff --git a/utility/doc/README.md b/utility/doc/README.md index 968af25f9..c388491ba 100644 --- a/utility/doc/README.md +++ b/utility/doc/README.md @@ -2,11 +2,9 @@ This document is meant to be a placeholder to serve as a living representation of the parent 'Utility Module' codebase until the code matches the [1.0 Utility Module specification](https://github.com/pokt-network/pocket-network-protocol/tree/main/utility). -The spirit of the documentation is to continuously update and inform the reader of the general scope of the Utility Module as breaking, rapid development occurs. - _This document will be archived once the implementation is synonymous with the current 1.0 Utility specification, ._ -# Origin Document +## Origin Document Currently, the Utility Module minimally implements the first iteration of an account based, state machine, blockchain protocol. @@ -152,8 +150,8 @@ And minimally satisfy the following interface: ```go CheckTransaction(tx []byte) error -GetProposalTransactions(proposer []byte, maxTransactionBytes int, lastBlockByzantineValidators [][]byte) (transactions [][]byte, err error) -ApplyBlock(Height int64, proposer []byte, transactions [][]byte, lastBlockByzantineValidators [][]byte) (appHash []byte, err error) +GetProposalTransactions(proposer []byte, maxTransactionBytes int, lastBlockByzantineValidators [][]byte) (txs [][]byte, err error) +ApplyBlock(Height int64, proposer []byte, txs [][]byte, lastBlockByzantineValidators [][]byte) (appHash []byte, err error) ``` ## How to build diff --git a/utility/gov.go b/utility/gov.go index 1a683ab05..5f3da81bf 100644 --- a/utility/gov.go +++ b/utility/gov.go @@ -3,12 +3,13 @@ package utility import ( "math/big" + "github.com/pokt-network/pocket/shared/converters" coreTypes "github.com/pokt-network/pocket/shared/core/types" typesUtil "github.com/pokt-network/pocket/utility/types" "google.golang.org/protobuf/types/known/wrapperspb" ) -func (u *UtilityContext) UpdateParam(paramName string, value any) typesUtil.Error { +func (u *utilityContext) updateParam(paramName string, value any) typesUtil.Error { store := u.Store() switch t := value.(type) { case *wrapperspb.Int32Value: @@ -33,227 +34,227 @@ func (u *UtilityContext) UpdateParam(paramName string, value any) typesUtil.Erro return typesUtil.ErrUnknownParam(paramName) } -func (u *UtilityContext) GetParameter(paramName string, height int64) (any, error) { +func (u *utilityContext) getParameter(paramName string, height int64) (any, error) { return u.Store().GetParameter(paramName, height) } -func (u *UtilityContext) GetAppMinimumStake() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getAppMinimumStake() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.AppMinimumStakeParamName) } -func (u *UtilityContext) GetAppMaxChains() (int, typesUtil.Error) { +func (u *utilityContext) getAppMaxChains() (int, typesUtil.Error) { return u.getIntParam(typesUtil.AppMaxChainsParamName) } -func (u *UtilityContext) GetBaselineAppStakeRate() (int, typesUtil.Error) { +func (u *utilityContext) getBaselineAppStakeRate() (int, typesUtil.Error) { return u.getIntParam(typesUtil.AppBaselineStakeRateParamName) } -func (u *UtilityContext) GetStabilityAdjustment() (int, typesUtil.Error) { +func (u *utilityContext) getStabilityAdjustment() (int, typesUtil.Error) { return u.getIntParam(typesUtil.AppStakingAdjustmentParamName) } -func (u *UtilityContext) GetAppUnstakingBlocks() (int64, typesUtil.Error) { +func (u *utilityContext) getAppUnstakingBlocks() (int64, typesUtil.Error) { return u.getInt64Param(typesUtil.AppUnstakingBlocksParamName) } -func (u *UtilityContext) GetAppMinimumPauseBlocks() (int, typesUtil.Error) { +func (u *utilityContext) getAppMinimumPauseBlocks() (int, typesUtil.Error) { return u.getIntParam(typesUtil.AppMinimumPauseBlocksParamName) } -func (u *UtilityContext) GetAppMaxPausedBlocks() (maxPausedBlocks int, err typesUtil.Error) { +func (u *utilityContext) getAppMaxPausedBlocks() (maxPausedBlocks int, err typesUtil.Error) { return u.getIntParam(typesUtil.AppMaxPauseBlocksParamName) } -func (u *UtilityContext) GetServiceNodeMinimumStake() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getServiceNodeMinimumStake() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.ServiceNodeMinimumStakeParamName) } -func (u *UtilityContext) GetServiceNodeMaxChains() (int, typesUtil.Error) { +func (u *utilityContext) getServiceNodeMaxChains() (int, typesUtil.Error) { return u.getIntParam(typesUtil.ServiceNodeMaxChainsParamName) } -func (u *UtilityContext) GetServiceNodeUnstakingBlocks() (int64, typesUtil.Error) { +func (u *utilityContext) getServiceNodeUnstakingBlocks() (int64, typesUtil.Error) { return u.getInt64Param(typesUtil.ServiceNodeUnstakingBlocksParamName) } -func (u *UtilityContext) GetServiceNodeMinimumPauseBlocks() (int, typesUtil.Error) { +func (u *utilityContext) getServiceNodeMinimumPauseBlocks() (int, typesUtil.Error) { return u.getIntParam(typesUtil.ServiceNodeMinimumPauseBlocksParamName) } -func (u *UtilityContext) GetServiceNodeMaxPausedBlocks() (maxPausedBlocks int, err typesUtil.Error) { +func (u *utilityContext) getServiceNodeMaxPausedBlocks() (maxPausedBlocks int, err typesUtil.Error) { return u.getIntParam(typesUtil.ServiceNodeMaxPauseBlocksParamName) } -func (u *UtilityContext) GetValidatorMinimumStake() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getValidatorMinimumStake() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.ValidatorMinimumStakeParamName) } -func (u *UtilityContext) GetValidatorUnstakingBlocks() (int64, typesUtil.Error) { +func (u *utilityContext) getValidatorUnstakingBlocks() (int64, typesUtil.Error) { return u.getInt64Param(typesUtil.ValidatorUnstakingBlocksParamName) } -func (u *UtilityContext) GetValidatorMinimumPauseBlocks() (int, typesUtil.Error) { +func (u *utilityContext) getValidatorMinimumPauseBlocks() (int, typesUtil.Error) { return u.getIntParam(typesUtil.ValidatorMinimumPauseBlocksParamName) } -func (u *UtilityContext) GetValidatorMaxPausedBlocks() (maxPausedBlocks int, err typesUtil.Error) { +func (u *utilityContext) getValidatorMaxPausedBlocks() (maxPausedBlocks int, err typesUtil.Error) { return u.getIntParam(typesUtil.ValidatorMaxPausedBlocksParamName) } -func (u *UtilityContext) GetProposerPercentageOfFees() (proposerPercentage int, err typesUtil.Error) { +func (u *utilityContext) getProposerPercentageOfFees() (proposerPercentage int, err typesUtil.Error) { return u.getIntParam(typesUtil.ProposerPercentageOfFeesParamName) } -func (u *UtilityContext) GetValidatorMaxMissedBlocks() (maxMissedBlocks int, err typesUtil.Error) { +func (u *utilityContext) getValidatorMaxMissedBlocks() (maxMissedBlocks int, err typesUtil.Error) { return u.getIntParam(typesUtil.ValidatorMaximumMissedBlocksParamName) } -func (u *UtilityContext) GetMaxEvidenceAgeInBlocks() (maxMissedBlocks int, err typesUtil.Error) { +func (u *utilityContext) getMaxEvidenceAgeInBlocks() (maxMissedBlocks int, err typesUtil.Error) { return u.getIntParam(typesUtil.ValidatorMaxEvidenceAgeInBlocksParamName) } -func (u *UtilityContext) GetDoubleSignBurnPercentage() (burnPercentage int, err typesUtil.Error) { +func (u *utilityContext) getDoubleSignBurnPercentage() (burnPercentage int, err typesUtil.Error) { return u.getIntParam(typesUtil.DoubleSignBurnPercentageParamName) } -func (u *UtilityContext) GetMissedBlocksBurnPercentage() (burnPercentage int, err typesUtil.Error) { +func (u *utilityContext) getMissedBlocksBurnPercentage() (burnPercentage int, err typesUtil.Error) { return u.getIntParam(typesUtil.MissedBlocksBurnPercentageParamName) } -func (u *UtilityContext) GetFishermanMinimumStake() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getFishermanMinimumStake() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.FishermanMinimumStakeParamName) } -func (u *UtilityContext) GetFishermanMaxChains() (int, typesUtil.Error) { +func (u *utilityContext) getFishermanMaxChains() (int, typesUtil.Error) { return u.getIntParam(typesUtil.FishermanMaxChainsParamName) } -func (u *UtilityContext) GetFishermanUnstakingBlocks() (int64, typesUtil.Error) { +func (u *utilityContext) getFishermanUnstakingBlocks() (int64, typesUtil.Error) { return u.getInt64Param(typesUtil.FishermanUnstakingBlocksParamName) } -func (u *UtilityContext) GetFishermanMinimumPauseBlocks() (int, typesUtil.Error) { +func (u *utilityContext) getFishermanMinimumPauseBlocks() (int, typesUtil.Error) { return u.getIntParam(typesUtil.FishermanMinimumPauseBlocksParamName) } -func (u *UtilityContext) GetFishermanMaxPausedBlocks() (maxPausedBlocks int, err typesUtil.Error) { +func (u *utilityContext) getFishermanMaxPausedBlocks() (maxPausedBlocks int, err typesUtil.Error) { return u.getIntParam(typesUtil.FishermanMaxPauseBlocksParamName) } -func (u *UtilityContext) GetMessageDoubleSignFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageDoubleSignFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageDoubleSignFee) } -func (u *UtilityContext) GetMessageSendFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageSendFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageSendFee) } -func (u *UtilityContext) GetMessageStakeFishermanFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageStakeFishermanFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageStakeFishermanFee) } -func (u *UtilityContext) GetMessageEditStakeFishermanFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageEditStakeFishermanFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageEditStakeFishermanFee) } -func (u *UtilityContext) GetMessageUnstakeFishermanFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageUnstakeFishermanFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageUnstakeFishermanFee) } -func (u *UtilityContext) GetMessagePauseFishermanFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessagePauseFishermanFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessagePauseFishermanFee) } -func (u *UtilityContext) GetMessageUnpauseFishermanFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageUnpauseFishermanFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageUnpauseFishermanFee) } -func (u *UtilityContext) GetMessageFishermanPauseServiceNodeFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageFishermanPauseServiceNodeFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageFishermanPauseServiceNodeFee) } -func (u *UtilityContext) GetMessageTestScoreFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageTestScoreFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageTestScoreFee) } -func (u *UtilityContext) GetMessageProveTestScoreFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageProveTestScoreFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageProveTestScoreFee) } -func (u *UtilityContext) GetMessageStakeAppFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageStakeAppFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageStakeAppFee) } -func (u *UtilityContext) GetMessageEditStakeAppFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageEditStakeAppFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageEditStakeAppFee) } -func (u *UtilityContext) GetMessageUnstakeAppFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageUnstakeAppFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageUnstakeAppFee) } -func (u *UtilityContext) GetMessagePauseAppFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessagePauseAppFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessagePauseAppFee) } -func (u *UtilityContext) GetMessageUnpauseAppFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageUnpauseAppFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageUnpauseAppFee) } -func (u *UtilityContext) GetMessageStakeValidatorFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageStakeValidatorFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageStakeValidatorFee) } -func (u *UtilityContext) GetMessageEditStakeValidatorFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageEditStakeValidatorFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageEditStakeValidatorFee) } -func (u *UtilityContext) GetMessageUnstakeValidatorFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageUnstakeValidatorFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageUnstakeValidatorFee) } -func (u *UtilityContext) GetMessagePauseValidatorFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessagePauseValidatorFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessagePauseValidatorFee) } -func (u *UtilityContext) GetMessageUnpauseValidatorFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageUnpauseValidatorFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageUnpauseValidatorFee) } -func (u *UtilityContext) GetMessageStakeServiceNodeFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageStakeServiceNodeFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageStakeServiceNodeFee) } -func (u *UtilityContext) GetMessageEditStakeServiceNodeFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageEditStakeServiceNodeFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageEditStakeServiceNodeFee) } -func (u *UtilityContext) GetMessageUnstakeServiceNodeFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageUnstakeServiceNodeFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageUnstakeServiceNodeFee) } -func (u *UtilityContext) GetMessagePauseServiceNodeFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessagePauseServiceNodeFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessagePauseServiceNodeFee) } -func (u *UtilityContext) GetMessageUnpauseServiceNodeFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageUnpauseServiceNodeFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageUnpauseServiceNodeFee) } -func (u *UtilityContext) GetMessageChangeParameterFee() (*big.Int, typesUtil.Error) { +func (u *utilityContext) getMessageChangeParameterFee() (*big.Int, typesUtil.Error) { return u.getBigIntParam(typesUtil.MessageChangeParameterFee) } -func (u *UtilityContext) GetDoubleSignFeeOwner() (owner []byte, err typesUtil.Error) { +func (u *utilityContext) getDoubleSignFeeOwner() (owner []byte, err typesUtil.Error) { return u.getByteArrayParam(typesUtil.MessageDoubleSignFeeOwner) } -func (u *UtilityContext) GetParamOwner(paramName string) ([]byte, error) { +func (u *utilityContext) getParamOwner(paramName string) ([]byte, error) { // DISCUSS (@deblasis): here we could potentially leverage the struct tags in gov.proto by specifying an `owner` key // eg: `app_minimum_stake` could have `pokt:"owner=app_minimum_stake_owner"` // in here we would use that map to point to the owner, removing this switch, centralizing the logic and making it declarative - store, height, er := u.GetStoreAndHeight() + store, height, er := u.getStoreAndHeight() if er != nil { return nil, er } @@ -479,96 +480,98 @@ func (u *UtilityContext) GetParamOwner(paramName string) ([]byte, error) { } } -func (u *UtilityContext) GetFee(msg typesUtil.Message, actorType coreTypes.ActorType) (amount *big.Int, err typesUtil.Error) { +func (u *utilityContext) getFee(msg typesUtil.Message, actorType coreTypes.ActorType) (amount *big.Int, err typesUtil.Error) { switch x := msg.(type) { - case *typesUtil.MessageDoubleSign: - return u.GetMessageDoubleSignFee() case *typesUtil.MessageSend: - return u.GetMessageSendFee() + return u.getMessageSendFee() case *typesUtil.MessageStake: switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - return u.GetMessageStakeAppFee() + return u.getMessageStakeAppFee() case coreTypes.ActorType_ACTOR_TYPE_FISH: - return u.GetMessageStakeFishermanFee() + return u.getMessageStakeFishermanFee() case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - return u.GetMessageStakeServiceNodeFee() + return u.getMessageStakeServiceNodeFee() case coreTypes.ActorType_ACTOR_TYPE_VAL: - return u.GetMessageStakeValidatorFee() + return u.getMessageStakeValidatorFee() default: return nil, typesUtil.ErrUnknownActorType(actorType.String()) } case *typesUtil.MessageEditStake: switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - return u.GetMessageEditStakeAppFee() + return u.getMessageEditStakeAppFee() case coreTypes.ActorType_ACTOR_TYPE_FISH: - return u.GetMessageEditStakeFishermanFee() + return u.getMessageEditStakeFishermanFee() case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - return u.GetMessageEditStakeServiceNodeFee() + return u.getMessageEditStakeServiceNodeFee() case coreTypes.ActorType_ACTOR_TYPE_VAL: - return u.GetMessageEditStakeValidatorFee() + return u.getMessageEditStakeValidatorFee() default: return nil, typesUtil.ErrUnknownActorType(actorType.String()) } case *typesUtil.MessageUnstake: switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - return u.GetMessageUnstakeAppFee() + return u.getMessageUnstakeAppFee() case coreTypes.ActorType_ACTOR_TYPE_FISH: - return u.GetMessageUnstakeFishermanFee() + return u.getMessageUnstakeFishermanFee() case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - return u.GetMessageUnstakeServiceNodeFee() + return u.getMessageUnstakeServiceNodeFee() case coreTypes.ActorType_ACTOR_TYPE_VAL: - return u.GetMessageUnstakeValidatorFee() + return u.getMessageUnstakeValidatorFee() default: return nil, typesUtil.ErrUnknownActorType(actorType.String()) } case *typesUtil.MessageUnpause: switch actorType { case coreTypes.ActorType_ACTOR_TYPE_APP: - return u.GetMessageUnpauseAppFee() + return u.getMessageUnpauseAppFee() case coreTypes.ActorType_ACTOR_TYPE_FISH: - return u.GetMessageUnpauseFishermanFee() + return u.getMessageUnpauseFishermanFee() case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - return u.GetMessageUnpauseServiceNodeFee() + return u.getMessageUnpauseServiceNodeFee() case coreTypes.ActorType_ACTOR_TYPE_VAL: - return u.GetMessageUnpauseValidatorFee() + return u.getMessageUnpauseValidatorFee() default: return nil, typesUtil.ErrUnknownActorType(actorType.String()) } case *typesUtil.MessageChangeParameter: - return u.GetMessageChangeParameterFee() + return u.getMessageChangeParameterFee() default: return nil, typesUtil.ErrUnknownMessage(x) } } -func (u *UtilityContext) GetMessageChangeParameterSignerCandidates(msg *typesUtil.MessageChangeParameter) ([][]byte, typesUtil.Error) { - owner, err := u.GetParamOwner(msg.ParameterKey) +func (u *utilityContext) getMessageChangeParameterSignerCandidates(msg *typesUtil.MessageChangeParameter) ([][]byte, typesUtil.Error) { + owner, err := u.getParamOwner(msg.ParameterKey) if err != nil { return nil, typesUtil.ErrGetParam(msg.ParameterKey, err) } return [][]byte{owner}, nil } -func (u *UtilityContext) getBigIntParam(paramName string) (*big.Int, typesUtil.Error) { - store, height, er := u.GetStoreAndHeight() - if er != nil { - return nil, er +func (u *utilityContext) getBigIntParam(paramName string) (*big.Int, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() + if err != nil { + return nil, typesUtil.ErrGetHeight(err) } value, err := store.GetStringParam(paramName, height) if err != nil { u.logger.Err(err) return nil, typesUtil.ErrGetParam(paramName, err) } - return typesUtil.StringToBigInt(value) + amount, err := converters.StringToBigInt(value) + if err != nil { + return nil, typesUtil.ErrStringToBigInt(err) + } + return amount, nil } -func (u *UtilityContext) getIntParam(paramName string) (int, typesUtil.Error) { - store, height, er := u.GetStoreAndHeight() - if er != nil { - return 0, er +func (u *utilityContext) getIntParam(paramName string) (int, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() + if err != nil { + return 0, typesUtil.ErrGetHeight(err) } value, err := store.GetIntParam(paramName, height) if err != nil { @@ -577,10 +580,10 @@ func (u *UtilityContext) getIntParam(paramName string) (int, typesUtil.Error) { return value, nil } -func (u *UtilityContext) getInt64Param(paramName string) (int64, typesUtil.Error) { - store, height, er := u.GetStoreAndHeight() - if er != nil { - return 0, er +func (u *utilityContext) getInt64Param(paramName string) (int64, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() + if err != nil { + return 0, typesUtil.ErrGetHeight(err) } value, err := store.GetIntParam(paramName, height) if err != nil { @@ -589,10 +592,10 @@ func (u *UtilityContext) getInt64Param(paramName string) (int64, typesUtil.Error return int64(value), nil } -func (u *UtilityContext) getByteArrayParam(paramName string) ([]byte, typesUtil.Error) { - store, height, er := u.GetStoreAndHeight() - if er != nil { - return nil, er +func (u *utilityContext) getByteArrayParam(paramName string) ([]byte, typesUtil.Error) { + store, height, err := u.getStoreAndHeight() + if err != nil { + return nil, typesUtil.ErrGetHeight(err) } value, err := store.GetBytesParam(paramName, height) if err != nil { diff --git a/utility/test/gov_test.go b/utility/gov_test.go similarity index 65% rename from utility/test/gov_test.go rename to utility/gov_test.go index b986d7661..d10959f74 100644 --- a/utility/test/gov_test.go +++ b/utility/gov_test.go @@ -1,4 +1,4 @@ -package test +package utility import ( "encoding/hex" @@ -7,6 +7,7 @@ import ( "github.com/pokt-network/pocket/runtime/genesis" "github.com/pokt-network/pocket/runtime/test_artifacts" "github.com/pokt-network/pocket/shared/codec" + "github.com/pokt-network/pocket/shared/converters" typesUtil "github.com/pokt-network/pocket/utility/types" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/wrapperspb" @@ -20,96 +21,86 @@ func DefaultTestingParams(_ *testing.T) *genesis.Params { } func TestUtilityContext_GetAppMaxChains(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) - maxChains, err := ctx.GetAppMaxChains() + maxChains, err := ctx.getAppMaxChains() require.NoError(t, err) require.Equal(t, int(defaultParams.GetAppMaxChains()), maxChains) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetAppMaxPausedBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) - gotParam, err := ctx.GetAppMaxPausedBlocks() + gotParam, err := ctx.getAppMaxPausedBlocks() require.NoError(t, err) require.Equal(t, int(defaultParams.GetAppMaxPauseBlocks()), gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetAppMinimumPauseBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetAppMinimumPauseBlocks()) - gotParam, err := ctx.GetAppMinimumPauseBlocks() + gotParam, err := ctx.getAppMinimumPauseBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetAppMinimumStake(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetAppMinimumStake() - gotParam, err := ctx.GetAppMinimumStake() + gotParam, err := ctx.getAppMinimumStake() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetAppUnstakingBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int64(defaultParams.GetAppUnstakingBlocks()) - gotParam, err := ctx.GetAppUnstakingBlocks() + gotParam, err := ctx.getAppUnstakingBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetBaselineAppStakeRate(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetAppBaselineStakeRate()) - gotParam, err := ctx.GetBaselineAppStakeRate() + gotParam, err := ctx.getBaselineAppStakeRate() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetBlocksPerSession(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetBlocksPerSession()) - gotParam, err := ctx.GetParameter(typesUtil.BlocksPerSessionParamName, 0) + gotParam, err := ctx.getParameter(typesUtil.BlocksPerSessionParamName, 0) require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetDoubleSignBurnPercentage(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetDoubleSignBurnPercentage()) - gotParam, err := ctx.GetDoubleSignBurnPercentage() + gotParam, err := ctx.getDoubleSignBurnPercentage() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetDoubleSignFeeOwner(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageDoubleSignFeeOwner() - gotParam, err := ctx.GetDoubleSignFeeOwner() + gotParam, err := ctx.getDoubleSignFeeOwner() require.NoError(t, err) defaultParamTx, er := hex.DecodeString(defaultParam) @@ -117,510 +108,431 @@ func TestUtilityContext_GetDoubleSignFeeOwner(t *testing.T) { require.Equal(t, defaultParamTx, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetFishermanMaxChains(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetFishermanMaxChains()) - gotParam, err := ctx.GetFishermanMaxChains() + gotParam, err := ctx.getFishermanMaxChains() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetFishermanMaxPausedBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetFishermanMaxPauseBlocks()) - gotParam, err := ctx.GetFishermanMaxPausedBlocks() + gotParam, err := ctx.getFishermanMaxPausedBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetFishermanMinimumPauseBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetFishermanMinimumPauseBlocks()) - gotParam, err := ctx.GetFishermanMinimumPauseBlocks() + gotParam, err := ctx.getFishermanMinimumPauseBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetFishermanMinimumStake(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetFishermanMinimumStake() - gotParam, err := ctx.GetFishermanMinimumStake() + gotParam, err := ctx.getFishermanMinimumStake() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetFishermanUnstakingBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int64(defaultParams.GetFishermanUnstakingBlocks()) - gotParam, err := ctx.GetFishermanUnstakingBlocks() + gotParam, err := ctx.getFishermanUnstakingBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetMaxEvidenceAgeInBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetValidatorMaxEvidenceAgeInBlocks()) - gotParam, err := ctx.GetMaxEvidenceAgeInBlocks() + gotParam, err := ctx.getMaxEvidenceAgeInBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetMessageChangeParameterFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageChangeParameterFee() - gotParam, err := ctx.GetMessageChangeParameterFee() + gotParam, err := ctx.getMessageChangeParameterFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetMessageDoubleSignFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageDoubleSignFee() - gotParam, err := ctx.GetMessageDoubleSignFee() + gotParam, err := ctx.getMessageDoubleSignFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetMessageEditStakeAppFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageEditStakeAppFee() - gotParam, err := ctx.GetMessageEditStakeAppFee() + gotParam, err := ctx.getMessageEditStakeAppFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageEditStakeFishermanFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageEditStakeFishermanFee() - gotParam, err := ctx.GetMessageEditStakeFishermanFee() + gotParam, err := ctx.getMessageEditStakeFishermanFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageEditStakeServiceNodeFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageEditStakeServiceNodeFee() - gotParam, err := ctx.GetMessageEditStakeServiceNodeFee() + gotParam, err := ctx.getMessageEditStakeServiceNodeFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageEditStakeValidatorFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageEditStakeValidatorFee() - gotParam, err := ctx.GetMessageEditStakeValidatorFee() + gotParam, err := ctx.getMessageEditStakeValidatorFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageFishermanPauseServiceNodeFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageFishermanPauseServiceNodeFee() - gotParam, err := ctx.GetMessageFishermanPauseServiceNodeFee() + gotParam, err := ctx.getMessageFishermanPauseServiceNodeFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessagePauseAppFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessagePauseAppFee() - gotParam, err := ctx.GetMessagePauseAppFee() + gotParam, err := ctx.getMessagePauseAppFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessagePauseFishermanFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessagePauseFishermanFee() - gotParam, err := ctx.GetMessagePauseFishermanFee() + gotParam, err := ctx.getMessagePauseFishermanFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessagePauseServiceNodeFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessagePauseServiceNodeFee() - gotParam, err := ctx.GetMessagePauseServiceNodeFee() + gotParam, err := ctx.getMessagePauseServiceNodeFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessagePauseValidatorFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessagePauseValidatorFee() - gotParam, err := ctx.GetMessagePauseValidatorFee() + gotParam, err := ctx.getMessagePauseValidatorFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageProveTestScoreFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageProveTestScoreFee() - gotParam, err := ctx.GetMessageProveTestScoreFee() + gotParam, err := ctx.getMessageProveTestScoreFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetMessageSendFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageSendFee() - gotParam, err := ctx.GetMessageSendFee() + gotParam, err := ctx.getMessageSendFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageStakeAppFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageStakeAppFee() - gotParam, err := ctx.GetMessageStakeAppFee() + gotParam, err := ctx.getMessageStakeAppFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageStakeFishermanFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageStakeFishermanFee() - gotParam, err := ctx.GetMessageStakeFishermanFee() + gotParam, err := ctx.getMessageStakeFishermanFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetMessageStakeServiceNodeFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageStakeServiceNodeFee() - gotParam, err := ctx.GetMessageStakeServiceNodeFee() + gotParam, err := ctx.getMessageStakeServiceNodeFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageStakeValidatorFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageStakeValidatorFee() - gotParam, err := ctx.GetMessageStakeValidatorFee() + gotParam, err := ctx.getMessageStakeValidatorFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageTestScoreFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageTestScoreFee() - gotParam, err := ctx.GetMessageTestScoreFee() + gotParam, err := ctx.getMessageTestScoreFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageUnpauseAppFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageUnpauseAppFee() - gotParam, err := ctx.GetMessageUnpauseAppFee() + gotParam, err := ctx.getMessageUnpauseAppFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageUnpauseFishermanFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageUnpauseFishermanFee() - gotParam, err := ctx.GetMessageUnpauseFishermanFee() + gotParam, err := ctx.getMessageUnpauseFishermanFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageUnpauseServiceNodeFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageUnpauseServiceNodeFee() - gotParam, err := ctx.GetMessageUnpauseServiceNodeFee() + gotParam, err := ctx.getMessageUnpauseServiceNodeFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageUnpauseValidatorFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageUnpauseValidatorFee() - gotParam, err := ctx.GetMessageUnpauseValidatorFee() + gotParam, err := ctx.getMessageUnpauseValidatorFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetMessageUnstakeAppFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageUnstakeAppFee() - gotParam, err := ctx.GetMessageUnstakeAppFee() + gotParam, err := ctx.getMessageUnstakeAppFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetMessageUnstakeFishermanFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageUnstakeFishermanFee() - gotParam, err := ctx.GetMessageUnstakeFishermanFee() + gotParam, err := ctx.getMessageUnstakeFishermanFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageUnstakeServiceNodeFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageUnstakeServiceNodeFee() - gotParam, err := ctx.GetMessageUnstakeServiceNodeFee() + gotParam, err := ctx.getMessageUnstakeServiceNodeFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMessageUnstakeValidatorFee(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetMessageUnstakeValidatorFee() - gotParam, err := ctx.GetMessageUnstakeValidatorFee() + gotParam, err := ctx.getMessageUnstakeValidatorFee() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetMissedBlocksBurnPercentage(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetMissedBlocksBurnPercentage()) - gotParam, err := ctx.GetMissedBlocksBurnPercentage() + gotParam, err := ctx.getMissedBlocksBurnPercentage() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetProposerPercentageOfFees(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetProposerPercentageOfFees()) - gotParam, err := ctx.GetProposerPercentageOfFees() + gotParam, err := ctx.getProposerPercentageOfFees() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetServiceNodeMaxChains(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetServiceNodeMaxChains()) - gotParam, err := ctx.GetServiceNodeMaxChains() + gotParam, err := ctx.getServiceNodeMaxChains() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetServiceNodeMaxPausedBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetServiceNodeMaxPauseBlocks()) - gotParam, err := ctx.GetServiceNodeMaxPausedBlocks() + gotParam, err := ctx.getServiceNodeMaxPausedBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetServiceNodeMinimumPauseBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetServiceNodeMinimumPauseBlocks()) - gotParam, err := ctx.GetServiceNodeMinimumPauseBlocks() + gotParam, err := ctx.getServiceNodeMinimumPauseBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetServiceNodeMinimumStake(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetServiceNodeMinimumStake() - gotParam, err := ctx.GetServiceNodeMinimumStake() + gotParam, err := ctx.getServiceNodeMinimumStake() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetServiceNodeUnstakingBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int64(defaultParams.GetServiceNodeUnstakingBlocks()) - gotParam, err := ctx.GetServiceNodeUnstakingBlocks() + gotParam, err := ctx.getServiceNodeUnstakingBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetStakingAdjustment(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetAppStakingAdjustment()) - gotParam, err := ctx.GetStabilityAdjustment() + gotParam, err := ctx.getStabilityAdjustment() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetValidatorMaxMissedBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetValidatorMaximumMissedBlocks()) - gotParam, err := ctx.GetValidatorMaxMissedBlocks() + gotParam, err := ctx.getValidatorMaxMissedBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetValidatorMaxPausedBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetValidatorMaxPauseBlocks()) - gotParam, err := ctx.GetValidatorMaxPausedBlocks() + gotParam, err := ctx.getValidatorMaxPausedBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetValidatorMinimumPauseBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetValidatorMinimumPauseBlocks()) - gotParam, err := ctx.GetValidatorMinimumPauseBlocks() + gotParam, err := ctx.getValidatorMinimumPauseBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetValidatorMinimumStake(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetValidatorMinimumStake() - gotParam, err := ctx.GetValidatorMinimumStake() + gotParam, err := ctx.getValidatorMinimumStake() require.NoError(t, err) - require.Equal(t, defaultParam, typesUtil.BigIntToString(gotParam)) - - test_artifacts.CleanupTest(ctx) + require.Equal(t, defaultParam, converters.BigIntToString(gotParam)) } func TestUtilityContext_GetValidatorUnstakingBlocks(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int64(defaultParams.GetValidatorUnstakingBlocks()) - gotParam, err := ctx.GetValidatorUnstakingBlocks() + gotParam, err := ctx.getValidatorUnstakingBlocks() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_HandleMessageChangeParameter(t *testing.T) { cdc := codec.GetCodec() - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := int(defaultParams.GetMissedBlocksBurnPercentage()) - gotParam, err := ctx.GetMissedBlocksBurnPercentage() + gotParam, err := ctx.getMissedBlocksBurnPercentage() require.NoError(t, err) require.Equal(t, defaultParam, gotParam) newParamValue := int32(2) @@ -634,452 +546,449 @@ func TestUtilityContext_HandleMessageChangeParameter(t *testing.T) { ParameterKey: typesUtil.MissedBlocksBurnPercentageParamName, ParameterValue: any, } - require.NoError(t, ctx.HandleMessageChangeParameter(msg), "handle message change param") - gotParam, err = ctx.GetMissedBlocksBurnPercentage() + require.NoError(t, ctx.handleMessageChangeParameter(msg), "handle message change param") + gotParam, err = ctx.getMissedBlocksBurnPercentage() require.NoError(t, err) require.Equal(t, int(newParamValue), gotParam) - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetParamOwner(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) defaultParams := DefaultTestingParams(t) defaultParam := defaultParams.GetAclOwner() - gotParam, err := ctx.GetParamOwner(typesUtil.AclOwner) + gotParam, err := ctx.getParamOwner(typesUtil.AclOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetBlocksPerSessionOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.BlocksPerSessionParamName) + gotParam, err = ctx.getParamOwner(typesUtil.BlocksPerSessionParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAppMaxChainsOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppMaxChainsParamName) + gotParam, err = ctx.getParamOwner(typesUtil.AppMaxChainsParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAppMinimumStakeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppMinimumStakeParamName) + gotParam, err = ctx.getParamOwner(typesUtil.AppMinimumStakeParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAppBaselineStakeRateOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppBaselineStakeRateParamName) + gotParam, err = ctx.getParamOwner(typesUtil.AppBaselineStakeRateParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAppStakingAdjustmentOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppStakingAdjustmentOwner) + gotParam, err = ctx.getParamOwner(typesUtil.AppStakingAdjustmentOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAppUnstakingBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppUnstakingBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.AppUnstakingBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAppMinimumPauseBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppMinimumPauseBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.AppMinimumPauseBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAppMaxPausedBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppMaxPauseBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.AppMaxPauseBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetServiceNodesPerSessionOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodesPerSessionParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodesPerSessionParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetServiceNodeMinimumStakeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeMinimumStakeParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeMinimumStakeParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetServiceNodeMaxChainsOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeMaxChainsParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeMaxChainsParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetServiceNodeUnstakingBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeUnstakingBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeUnstakingBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetServiceNodeMinimumPauseBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeMinimumPauseBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeMinimumPauseBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetServiceNodeMaxPausedBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeMaxPauseBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeMaxPauseBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetFishermanMinimumStakeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.FishermanMinimumStakeParamName) + gotParam, err = ctx.getParamOwner(typesUtil.FishermanMinimumStakeParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetServiceNodeMaxChainsOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeMaxPauseBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeMaxPauseBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetFishermanUnstakingBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.FishermanUnstakingBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.FishermanUnstakingBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetFishermanMinimumPauseBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.FishermanMinimumPauseBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.FishermanMinimumPauseBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetFishermanMaxPausedBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.FishermanMaxPauseBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.FishermanMaxPauseBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetValidatorMinimumStakeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMinimumStakeParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMinimumStakeParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetValidatorUnstakingBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorUnstakingBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorUnstakingBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetValidatorMinimumPauseBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMinimumPauseBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMinimumPauseBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetValidatorMaxPausedBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMaxPausedBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMaxPausedBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetValidatorMaximumMissedBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMaximumMissedBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMaximumMissedBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetProposerPercentageOfFeesOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ProposerPercentageOfFeesParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ProposerPercentageOfFeesParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetValidatorMaxEvidenceAgeInBlocksOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMaxEvidenceAgeInBlocksParamName) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMaxEvidenceAgeInBlocksParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMissedBlocksBurnPercentageOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MissedBlocksBurnPercentageParamName) + gotParam, err = ctx.getParamOwner(typesUtil.MissedBlocksBurnPercentageParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetDoubleSignBurnPercentageOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.DoubleSignBurnPercentageParamName) + gotParam, err = ctx.getParamOwner(typesUtil.DoubleSignBurnPercentageParamName) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageDoubleSignFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageDoubleSignFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageDoubleSignFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageSendFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageSendFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageSendFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageStakeFishermanFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageStakeFishermanFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageStakeFishermanFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageEditStakeFishermanFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageEditStakeFishermanFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageEditStakeFishermanFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageUnstakeFishermanFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnstakeFishermanFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnstakeFishermanFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessagePauseFishermanFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessagePauseFishermanFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessagePauseFishermanFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageUnpauseFishermanFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnpauseFishermanFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnpauseFishermanFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageTestScoreFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageTestScoreFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageTestScoreFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageFishermanPauseServiceNodeFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageFishermanPauseServiceNodeFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageFishermanPauseServiceNodeFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageProveTestScoreFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageProveTestScoreFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageProveTestScoreFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageStakeAppFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageStakeAppFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageStakeAppFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageEditStakeAppFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageEditStakeAppFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageEditStakeAppFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageUnstakeAppFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnstakeAppFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnstakeAppFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessagePauseAppFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessagePauseAppFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessagePauseAppFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageUnpauseAppFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnpauseAppFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnpauseAppFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageStakeValidatorFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageStakeValidatorFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageStakeValidatorFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageEditStakeValidatorFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageEditStakeValidatorFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageEditStakeValidatorFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageUnstakeValidatorFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnstakeValidatorFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnstakeValidatorFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessagePauseValidatorFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessagePauseValidatorFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessagePauseValidatorFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageUnpauseValidatorFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnpauseValidatorFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnpauseValidatorFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageStakeServiceNodeFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageStakeServiceNodeFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageStakeServiceNodeFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageEditStakeServiceNodeFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageEditStakeServiceNodeFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageEditStakeServiceNodeFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageUnstakeServiceNodeFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnstakeServiceNodeFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnstakeServiceNodeFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessagePauseServiceNodeFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessagePauseServiceNodeFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessagePauseServiceNodeFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageUnpauseServiceNodeFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnpauseServiceNodeFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnpauseServiceNodeFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetMessageChangeParameterFeeOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageChangeParameterFee) + gotParam, err = ctx.getParamOwner(typesUtil.MessageChangeParameterFee) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) // owners defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.BlocksPerSessionOwner) + gotParam, err = ctx.getParamOwner(typesUtil.BlocksPerSessionOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppMaxChainsOwner) + gotParam, err = ctx.getParamOwner(typesUtil.AppMaxChainsOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppMinimumStakeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.AppMinimumStakeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppBaselineStakeRateOwner) + gotParam, err = ctx.getParamOwner(typesUtil.AppBaselineStakeRateOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppStakingAdjustmentOwner) + gotParam, err = ctx.getParamOwner(typesUtil.AppStakingAdjustmentOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppUnstakingBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.AppUnstakingBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppMinimumPauseBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.AppMinimumPauseBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.AppMaxPausedBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.AppMaxPausedBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeMinimumPauseBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeMinimumPauseBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeMaxChainsOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeMaxChainsOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeUnstakingBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeUnstakingBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeMinimumStakeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeMinimumStakeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodeMaxPausedBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodeMaxPausedBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ServiceNodesPerSessionOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ServiceNodesPerSessionOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.FishermanMinimumStakeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.FishermanMinimumStakeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.FishermanMaxChainsOwner) + gotParam, err = ctx.getParamOwner(typesUtil.FishermanMaxChainsOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.FishermanUnstakingBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.FishermanUnstakingBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.FishermanMinimumPauseBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.FishermanMinimumPauseBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.FishermanMaxPausedBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.FishermanMaxPausedBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMinimumStakeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMinimumStakeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorUnstakingBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorUnstakingBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMinimumPauseBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMinimumPauseBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMaxPausedBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMaxPausedBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMaxPausedBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMaxPausedBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ProposerPercentageOfFeesOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ProposerPercentageOfFeesOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.ValidatorMaxEvidenceAgeInBlocksOwner) + gotParam, err = ctx.getParamOwner(typesUtil.ValidatorMaxEvidenceAgeInBlocksOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MissedBlocksBurnPercentageOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MissedBlocksBurnPercentageOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.DoubleSignBurnPercentageOwner) + gotParam, err = ctx.getParamOwner(typesUtil.DoubleSignBurnPercentageOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageSendFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageSendFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageStakeFishermanFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageStakeFishermanFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageEditStakeFishermanFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageEditStakeFishermanFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnstakeFishermanFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnstakeFishermanFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessagePauseFishermanFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessagePauseFishermanFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnpauseFishermanFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnpauseFishermanFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageFishermanPauseServiceNodeFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageFishermanPauseServiceNodeFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageTestScoreFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageTestScoreFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageProveTestScoreFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageProveTestScoreFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageStakeAppFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageStakeAppFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageEditStakeAppFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageEditStakeAppFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnstakeAppFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnstakeAppFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessagePauseAppFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessagePauseAppFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnpauseAppFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnpauseAppFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageStakeValidatorFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageStakeValidatorFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageEditStakeValidatorFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageEditStakeValidatorFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnstakeValidatorFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnstakeValidatorFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessagePauseValidatorFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessagePauseValidatorFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnpauseValidatorFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnpauseValidatorFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageStakeServiceNodeFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageStakeServiceNodeFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageEditStakeServiceNodeFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageEditStakeServiceNodeFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnstakeServiceNodeFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnstakeServiceNodeFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessagePauseServiceNodeFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessagePauseServiceNodeFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageUnpauseServiceNodeFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageUnpauseServiceNodeFeeOwner) require.NoError(t, err) require.Equal(t, defaultParam, hex.EncodeToString(gotParam)) defaultParam = defaultParams.GetAclOwner() - gotParam, err = ctx.GetParamOwner(typesUtil.MessageChangeParameterFeeOwner) + gotParam, err = ctx.getParamOwner(typesUtil.MessageChangeParameterFeeOwner) require.NoError(t, err) defaultParamBz, err := hex.DecodeString(defaultParam) require.NoError(t, err) require.Equal(t, defaultParamBz, gotParam) - - test_artifacts.CleanupTest(ctx) } diff --git a/utility/message_handler.go b/utility/message_handler.go new file mode 100644 index 000000000..88387c34c --- /dev/null +++ b/utility/message_handler.go @@ -0,0 +1,354 @@ +package utility + +import ( + "encoding/hex" + "fmt" + "log" + "math/big" + + "github.com/pokt-network/pocket/shared/codec" + "github.com/pokt-network/pocket/shared/converters" + coreTypes "github.com/pokt-network/pocket/shared/core/types" + "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/utility/types" + typesUtil "github.com/pokt-network/pocket/utility/types" + "google.golang.org/protobuf/types/known/anypb" +) + +const ( + TransactionGossipMessageContentType = "utility.TransactionGossipMessage" +) + +func (u *utilityModule) HandleMessage(message *anypb.Any) error { + switch message.MessageName() { + case TransactionGossipMessageContentType: + msg, err := codec.GetCodec().FromAny(message) + if err != nil { + return err + } + + if txGossipMsg, ok := msg.(*types.TransactionGossipMessage); !ok { + return fmt.Errorf("failed to cast message to UtilityMessage") + } else if err := u.CheckTransaction(txGossipMsg.Tx); err != nil { + return err + } + + log.Println("MEMPOOL: Successfully added a new message to the mempool!") + + default: + return types.ErrUnknownMessageType(message.MessageName()) + } + + return nil +} + +func (u *utilityContext) handleMessage(msg typesUtil.Message) (err typesUtil.Error) { + switch x := msg.(type) { + case *typesUtil.MessageSend: + return u.handleMessageSend(x) + case *typesUtil.MessageStake: + return u.handleStakeMessage(x) + case *typesUtil.MessageEditStake: + return u.handleEditStakeMessage(x) + case *typesUtil.MessageUnstake: + return u.handleUnstakeMessage(x) + case *typesUtil.MessageUnpause: + return u.handleUnpauseMessage(x) + case *typesUtil.MessageChangeParameter: + return u.handleMessageChangeParameter(x) + default: + return typesUtil.ErrUnknownMessage(x) + } +} + +func (u *utilityContext) handleMessageSend(message *typesUtil.MessageSend) typesUtil.Error { + // convert the amount to big.Int + amount, er := converters.StringToBigInt(message.Amount) + if er != nil { + return typesUtil.ErrStringToBigInt(er) + } + // get the sender's account amount + fromAccountAmount, err := u.getAccountAmount(message.FromAddress) + if err != nil { + return err + } + // subtract that amount from the sender + fromAccountAmount.Sub(fromAccountAmount, amount) + // if they go negative, they don't have sufficient funds + // NOTE: we don't use the u.SubtractAccountAmount() function because Utility needs to do this check + if fromAccountAmount.Sign() == -1 { + return typesUtil.ErrInsufficientAmount(hex.EncodeToString(message.FromAddress)) + } + // add the amount to the recipient's account + if err := u.addAccountAmount(message.ToAddress, amount); err != nil { + return err + } + // set the sender's account amount + if err := u.setAccountAmount(message.FromAddress, fromAccountAmount); err != nil { + return err + } + return nil +} + +func (u *utilityContext) handleStakeMessage(message *typesUtil.MessageStake) typesUtil.Error { + publicKey, er := crypto.NewPublicKeyFromBytes(message.PublicKey) + if er != nil { + return typesUtil.ErrNewPublicKeyFromBytes(er) + } + // ensure above minimum stake + amount, err := u.checkAboveMinStake(message.ActorType, message.Amount) + if err != nil { + return err + } + // ensure signer has sufficient funding for the stake + signerAccountAmount, err := u.getAccountAmount(message.Signer) + if err != nil { + return err + } + // calculate new signer account amount + signerAccountAmount.Sub(signerAccountAmount, amount) + if signerAccountAmount.Sign() == -1 { + return typesUtil.ErrInsufficientAmount(hex.EncodeToString(message.Signer)) + } + // validators don't have chains field + if err := u.checkBelowMaxChains(message.ActorType, message.Chains); err != nil { + return err + } + // ensure actor doesn't already exist + if exists, err := u.getActorExists(message.ActorType, publicKey.Address()); err != nil || exists { + if exists { + return typesUtil.ErrAlreadyExists() + } + return err + } + // update account amount + if err := u.setAccountAmount(message.Signer, signerAccountAmount); err != nil { + return err + } + // move funds from account to pool + if err := u.addPoolAmount(coreTypes.Pools_POOLS_APP_STAKE.FriendlyName(), amount); err != nil { + return err + } + + store := u.Store() + // insert actor + switch message.ActorType { + case coreTypes.ActorType_ACTOR_TYPE_APP: + maxRelays, err := u.calculateMaxAppRelays(message.Amount) + if err != nil { + return err + } + er = store.InsertApp(publicKey.Address(), publicKey.Bytes(), message.OutputAddress, false, int32(typesUtil.StakeStatus_Staked), maxRelays, message.Amount, message.Chains, typesUtil.HeightNotUsed, typesUtil.HeightNotUsed) + case coreTypes.ActorType_ACTOR_TYPE_FISH: + er = store.InsertFisherman(publicKey.Address(), publicKey.Bytes(), message.OutputAddress, false, int32(typesUtil.StakeStatus_Staked), message.ServiceUrl, message.Amount, message.Chains, typesUtil.HeightNotUsed, typesUtil.HeightNotUsed) + case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: + er = store.InsertServiceNode(publicKey.Address(), publicKey.Bytes(), message.OutputAddress, false, int32(typesUtil.StakeStatus_Staked), message.ServiceUrl, message.Amount, message.Chains, typesUtil.HeightNotUsed, typesUtil.HeightNotUsed) + case coreTypes.ActorType_ACTOR_TYPE_VAL: + er = store.InsertValidator(publicKey.Address(), publicKey.Bytes(), message.OutputAddress, false, int32(typesUtil.StakeStatus_Staked), message.ServiceUrl, message.Amount, typesUtil.HeightNotUsed, typesUtil.HeightNotUsed) + } + if er != nil { + return typesUtil.ErrInsert(er) + } + return nil +} + +func (u *utilityContext) handleEditStakeMessage(message *typesUtil.MessageEditStake) typesUtil.Error { + // ensure actor exists + if exists, err := u.getActorExists(message.ActorType, message.Address); err != nil || !exists { + if !exists { + return typesUtil.ErrNotExists() + } + return err + } + currentStakeAmount, err := u.getActorStakeAmount(message.ActorType, message.Address) + if err != nil { + return err + } + amount, er := converters.StringToBigInt(message.Amount) + if er != nil { + return typesUtil.ErrStringToBigInt(err) + } + // ensure new stake >= current stake + amount.Sub(amount, currentStakeAmount) + if amount.Sign() == -1 { + return typesUtil.ErrStakeLess() + } + // ensure signer has sufficient funding for the stake + signerAccountAmount, err := u.getAccountAmount(message.Signer) + if err != nil { + return err + } + signerAccountAmount.Sub(signerAccountAmount, amount) + if signerAccountAmount.Sign() == -1 { + return typesUtil.ErrInsufficientAmount(hex.EncodeToString(message.Signer)) + } + if err := u.checkBelowMaxChains(message.ActorType, message.Chains); err != nil { + return err + } + // update account amount + if err := u.setAccountAmount(message.Signer, signerAccountAmount); err != nil { + return err + } + // move funds from account to pool + if err := u.addPoolAmount(coreTypes.Pools_POOLS_APP_STAKE.FriendlyName(), amount); err != nil { + return err + } + store := u.Store() + switch message.ActorType { + case coreTypes.ActorType_ACTOR_TYPE_APP: + maxRelays, err := u.calculateMaxAppRelays(message.Amount) + if err != nil { + return err + } + er = store.UpdateApp(message.Address, maxRelays, message.Amount, message.Chains) + case coreTypes.ActorType_ACTOR_TYPE_FISH: + er = store.UpdateFisherman(message.Address, message.ServiceUrl, message.Amount, message.Chains) + case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: + er = store.UpdateServiceNode(message.Address, message.ServiceUrl, message.Amount, message.Chains) + case coreTypes.ActorType_ACTOR_TYPE_VAL: + er = store.UpdateValidator(message.Address, message.ServiceUrl, message.Amount) + } + if er != nil { + return typesUtil.ErrInsert(er) + } + return nil +} + +func (u *utilityContext) handleUnstakeMessage(message *typesUtil.MessageUnstake) typesUtil.Error { + if status, err := u.getActorStatus(message.ActorType, message.Address); err != nil || status != typesUtil.StakeStatus_Staked { + if status != typesUtil.StakeStatus_Staked { + return typesUtil.ErrInvalidStatus(status, typesUtil.StakeStatus_Staked) + } + return err + } + unbondingHeight, err := u.getUnbondingHeight(message.ActorType) + if err != nil { + return err + } + if err := u.setActorUnstakingHeight(message.ActorType, message.Address, unbondingHeight); err != nil { + return err + } + return nil +} + +func (u *utilityContext) handleUnpauseMessage(message *typesUtil.MessageUnpause) typesUtil.Error { + pausedHeight, err := u.getPausedHeightIfExists(message.ActorType, message.Address) + if err != nil { + return err + } + if pausedHeight == typesUtil.HeightNotUsed { + return typesUtil.ErrNotPaused() + } + minPauseBlocks, err := u.getMinRequiredPausedBlocks(message.ActorType) + if err != nil { + return err + } + if u.height < int64(minPauseBlocks)+pausedHeight { + return typesUtil.ErrNotReadyToUnpause() + } + if err := u.setActorPausedHeight(message.ActorType, message.Address, typesUtil.HeightNotUsed); err != nil { + return err + } + return nil +} + +func (u *utilityContext) handleMessageChangeParameter(message *typesUtil.MessageChangeParameter) typesUtil.Error { + v, err := codec.GetCodec().FromAny(message.ParameterValue) + if err != nil { + return typesUtil.ErrProtoFromAny(err) + } + return u.updateParam(message.ParameterKey, v) +} + +// REFACTOR: This can be moved over into utility/types/message.go +func (u *utilityContext) getSignerCandidates(msg typesUtil.Message) ([][]byte, typesUtil.Error) { + switch x := msg.(type) { + case *typesUtil.MessageSend: + return u.getMessageSendSignerCandidates(x) + case *typesUtil.MessageStake: + return u.getMessageStakeSignerCandidates(x) + case *typesUtil.MessageUnstake: + return u.getMessageUnstakeSignerCandidates(x) + case *typesUtil.MessageUnpause: + return u.getMessageUnpauseSignerCandidates(x) + case *typesUtil.MessageChangeParameter: + return u.getMessageChangeParameterSignerCandidates(x) + default: + return nil, typesUtil.ErrUnknownMessage(x) + } +} + +func (u *utilityContext) getMessageStakeSignerCandidates(msg *typesUtil.MessageStake) ([][]byte, typesUtil.Error) { + pk, er := crypto.NewPublicKeyFromBytes(msg.PublicKey) + if er != nil { + return nil, typesUtil.ErrNewPublicKeyFromBytes(er) + } + candidates := make([][]byte, 0) + candidates = append(candidates, msg.OutputAddress, pk.Address()) + return candidates, nil +} + +func (u *utilityContext) getMessageEditStakeSignerCandidates(msg *typesUtil.MessageEditStake) ([][]byte, typesUtil.Error) { + output, err := u.getActorOutputAddress(msg.ActorType, msg.Address) + if err != nil { + return nil, err + } + candidates := make([][]byte, 0) + candidates = append(candidates, output, msg.Address) + return candidates, nil +} + +func (u *utilityContext) getMessageUnstakeSignerCandidates(msg *typesUtil.MessageUnstake) ([][]byte, typesUtil.Error) { + output, err := u.getActorOutputAddress(msg.ActorType, msg.Address) + if err != nil { + return nil, err + } + candidates := make([][]byte, 0) + candidates = append(candidates, output, msg.Address) + return candidates, nil +} + +func (u *utilityContext) getMessageUnpauseSignerCandidates(msg *typesUtil.MessageUnpause) ([][]byte, typesUtil.Error) { + output, err := u.getActorOutputAddress(msg.ActorType, msg.Address) + if err != nil { + return nil, err + } + candidates := make([][]byte, 0) + candidates = append(candidates, output, msg.Address) + return candidates, nil +} + +func (u *utilityContext) getMessageSendSignerCandidates(msg *typesUtil.MessageSend) ([][]byte, typesUtil.Error) { + return [][]byte{msg.FromAddress}, nil +} + +func (u *utilityContext) checkBelowMaxChains(actorType coreTypes.ActorType, chains []string) typesUtil.Error { + // validators don't have chains field + if actorType == coreTypes.ActorType_ACTOR_TYPE_VAL { + return nil + } + + maxChains, err := u.getMaxAllowedChains(actorType) + if err != nil { + return err + } + if len(chains) > maxChains { + return typesUtil.ErrMaxChains(maxChains) + } + return nil +} + +func (u *utilityContext) checkAboveMinStake(actorType coreTypes.ActorType, amountStr string) (*big.Int, typesUtil.Error) { + minStake, err := u.getMinRequiredStakeAmount(actorType) + if err != nil { + return nil, err + } + amount, er := converters.StringToBigInt(amountStr) + if er != nil { + return nil, typesUtil.ErrStringToBigInt(err) + } + if converters.BigIntLessThan(amount, minStake) { + return nil, typesUtil.ErrMinimumStake() + } + return amount, nil +} diff --git a/utility/message_test.go b/utility/message_test.go new file mode 100644 index 000000000..358942426 --- /dev/null +++ b/utility/message_test.go @@ -0,0 +1,71 @@ +package utility + +import ( + "encoding/hex" + "math/big" + "testing" + + "github.com/pokt-network/pocket/shared/converters" + "github.com/pokt-network/pocket/utility/types" + "github.com/stretchr/testify/require" +) + +func TestUtilityContext_HandleMessageSend(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + accs := getAllTestingAccounts(t, ctx) + + sendAmount := big.NewInt(1000000) + sendAmountString := converters.BigIntToString(sendAmount) + senderBalanceBefore, err := converters.StringToBigInt(accs[0].GetAmount()) + require.NoError(t, err) + + recipientBalanceBefore, err := converters.StringToBigInt(accs[1].GetAmount()) + require.NoError(t, err) + + addrBz, er := hex.DecodeString(accs[0].GetAddress()) + require.NoError(t, er) + + addrBz2, er := hex.DecodeString(accs[1].GetAddress()) + require.NoError(t, er) + + msg := NewTestingSendMessage(t, addrBz, addrBz2, sendAmountString) + err = ctx.handleMessageSend(&msg) + require.NoError(t, err, "handle message send") + + accs = getAllTestingAccounts(t, ctx) + senderBalanceAfter, err := converters.StringToBigInt(accs[0].GetAmount()) + require.NoError(t, err) + + recipientBalanceAfter, err := converters.StringToBigInt(accs[1].GetAmount()) + require.NoError(t, err) + require.Equal(t, sendAmount, big.NewInt(0).Sub(senderBalanceBefore, senderBalanceAfter)) + require.Equal(t, sendAmount, big.NewInt(0).Sub(recipientBalanceAfter, recipientBalanceBefore)) +} + +func TestUtilityContext_GetMessageSendSignerCandidates(t *testing.T) { + ctx := newTestingUtilityContext(t, 0) + accs := getAllTestingAccounts(t, ctx) + + sendAmount := big.NewInt(1000000) + sendAmountString := converters.BigIntToString(sendAmount) + + addrBz, er := hex.DecodeString(accs[0].GetAddress()) + require.NoError(t, er) + + addrBz2, er := hex.DecodeString(accs[1].GetAddress()) + require.NoError(t, er) + + msg := NewTestingSendMessage(t, addrBz, addrBz2, sendAmountString) + candidates, err := ctx.getMessageSendSignerCandidates(&msg) + require.NoError(t, err) + require.Equal(t, 1, len(candidates)) + require.Equal(t, addrBz, candidates[0]) +} + +func NewTestingSendMessage(_ *testing.T, fromAddress, toAddress []byte, amount string) types.MessageSend { + return types.MessageSend{ + FromAddress: fromAddress, + ToAddress: toAddress, + Amount: amount, + } +} diff --git a/utility/module.go b/utility/module.go index 173dfc1ca..24c7d64d5 100644 --- a/utility/module.go +++ b/utility/module.go @@ -1,15 +1,11 @@ package utility import ( - "fmt" - "github.com/pokt-network/pocket/logger" "github.com/pokt-network/pocket/runtime/configs" - "github.com/pokt-network/pocket/shared/codec" "github.com/pokt-network/pocket/shared/mempool" "github.com/pokt-network/pocket/shared/modules" "github.com/pokt-network/pocket/utility/types" - "google.golang.org/protobuf/types/known/anypb" ) var ( @@ -21,14 +17,10 @@ type utilityModule struct { bus modules.Bus config *configs.UtilityConfig - logger modules.Logger + logger modules.Logger mempool mempool.TXMempool } -const ( - TransactionGossipMessageContentType = "utility.TransactionGossipMessage" -) - func Create(bus modules.Bus) (modules.Module, error) { return new(utilityModule).Create(bus) } @@ -74,27 +66,6 @@ func (u *utilityModule) GetBus() modules.Bus { return u.bus } -func (u *utilityModule) HandleMessage(message *anypb.Any) error { - switch message.MessageName() { - case TransactionGossipMessageContentType: - msg, err := codec.GetCodec().FromAny(message) - if err != nil { - return err - } - transactionGossipMsg, ok := msg.(*types.TransactionGossipMessage) - if !ok { - return fmt.Errorf("failed to cast message to UtilityMessage") - } - if err := u.CheckTransaction(transactionGossipMsg.Tx); err != nil { - return err - } - u.logger.Info().Str("source", "MEMPOOL").Msg("Successfully added a new message to the mempool!") - default: - return types.ErrUnknownMessageType(message.MessageName()) - } - return nil -} - func (u *utilityModule) GetMempool() mempool.TXMempool { return u.mempool } diff --git a/utility/test/module_test.go b/utility/module_test.go similarity index 79% rename from utility/test/module_test.go rename to utility/module_test.go index 4477ea33d..6e8d47846 100644 --- a/utility/test/module_test.go +++ b/utility/module_test.go @@ -1,4 +1,4 @@ -package test +package utility import ( "log" @@ -10,12 +10,10 @@ import ( "github.com/pokt-network/pocket/runtime" "github.com/pokt-network/pocket/runtime/configs" "github.com/pokt-network/pocket/runtime/test_artifacts" - coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/mempool" "github.com/pokt-network/pocket/shared/messaging" "github.com/pokt-network/pocket/shared/modules" mockModules "github.com/pokt-network/pocket/shared/modules/mocks" - "github.com/pokt-network/pocket/utility" utilTypes "github.com/pokt-network/pocket/utility/types" "github.com/stretchr/testify/require" ) @@ -25,33 +23,25 @@ const ( testingServiceNodeCount = 1 testingApplicationCount = 1 testingFishermenCount = 1 -) - -var ( - defaultTestingChainsEdited = []string{"0002"} - - defaultUnstaking = int64(2017) testNonce = "defaultNonceString" testSchema = "test_schema" ) -var testPersistenceMod modules.PersistenceModule // initialized in TestMain -var testUtilityMod modules.UtilityModule // initialized in TestMain - -var actorTypes = []coreTypes.ActorType{ - coreTypes.ActorType_ACTOR_TYPE_APP, - coreTypes.ActorType_ACTOR_TYPE_SERVICENODE, - coreTypes.ActorType_ACTOR_TYPE_FISH, - coreTypes.ActorType_ACTOR_TYPE_VAL, -} +var ( + // Initialized in TestMain + testPersistenceMod modules.PersistenceModule + testUtilityMod modules.UtilityModule +) func NewTestingMempool(_ *testing.T) mempool.TXMempool { return utilTypes.NewTxFIFOMempool(1000000, 1000) } func TestMain(m *testing.M) { + // TODO(#261): Utility module tests should have no dependencies on a postgres container pool, resource, dbUrl := test_artifacts.SetupPostgresDocker() + runtimeCfg := newTestRuntimeConfig(dbUrl) bus, err := runtime.CreateBus(runtimeCfg) if err != nil { @@ -66,7 +56,7 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } -func NewTestingUtilityContext(t *testing.T, height int64) *utility.UtilityContext { +func newTestingUtilityContext(t *testing.T, height int64) *utilityContext { persistenceContext, err := testPersistenceMod.NewRWContext(height) require.NoError(t, err) @@ -82,16 +72,14 @@ func NewTestingUtilityContext(t *testing.T, height int64) *utility.UtilityContex testUtilityMod.GetMempool().Clear() }) - uc := &utility.UtilityContext{ - Height: height, - Context: &utility.Context{ - PersistenceRWContext: persistenceContext, - SavePointsM: make(map[string]struct{}), - SavePoints: make([][]byte, 0), - }, + uc := &utilityContext{ + height: height, + persistenceContext: persistenceContext, + savePointsSet: make(map[string]struct{}), + savePointsList: make([][]byte, 0), } - return uc.WithBus(testUtilityMod.GetBus()) + return uc.setBus(testUtilityMod.GetBus()) } func newTestRuntimeConfig(databaseUrl string) *runtime.Manager { @@ -124,13 +112,22 @@ func newTestRuntimeConfig(databaseUrl string) *runtime.Manager { } func newTestUtilityModule(bus modules.Bus) modules.UtilityModule { - utilityMod, err := utility.Create(bus) + utilityMod, err := Create(bus) if err != nil { log.Fatalf("Error creating persistence module: %s", err) } return utilityMod.(modules.UtilityModule) } +// TODO(#261): Utility module tests should have no dependencies on the persistence module +func newTestPersistenceModule(bus modules.Bus) modules.PersistenceModule { + persistenceMod, err := persistence.Create(bus) + if err != nil { + log.Fatalf("Error creating persistence module: %s", err) + } + return persistenceMod.(modules.PersistenceModule) +} + // IMPROVE: Not part of `TestMain` because a mock requires `testing.T` to be initialized. // We are trying to only initialize one `testPersistenceModule` in all the tests, so when the // utility module tests are no longer dependant on the persistence module explicitly, this @@ -149,12 +146,3 @@ func mockBusInTestModules(t *testing.T) { testPersistenceMod.SetBus(nil) }) } - -// TODO(#290): Mock the persistence module so the utility module is not dependant on it. -func newTestPersistenceModule(bus modules.Bus) modules.PersistenceModule { - persistenceMod, err := persistence.Create(bus) - if err != nil { - log.Fatalf("Error creating persistence module: %s", err) - } - return persistenceMod.(modules.PersistenceModule) -} diff --git a/utility/session.go b/utility/session.go index c48c44109..6f9d29e9a 100644 --- a/utility/session.go +++ b/utility/session.go @@ -1,5 +1,8 @@ package utility +// IMPORTANT: The interface and implementation defined in this file are for illustrative purposes only +// and need to be revisited before any implementation commences. + import ( "encoding/binary" "encoding/hex" @@ -9,16 +12,6 @@ import ( "github.com/pokt-network/pocket/utility/types" ) -type Session interface { - NewSession(sessionHeight int64, blockHash string, geoZone GeoZone, relayChain RelayChain, application *coreTypes.Actor) (Session, types.Error) - GetServiceNodes() []*coreTypes.Actor // the ServiceNodes providing Web3 to the application - GetFishermen() []*coreTypes.Actor // the Fishermen monitoring the serviceNodes - GetApplication() *coreTypes.Actor // the Application consuming the web3 access - GetRelayChain() RelayChain // the chain identifier of the web3 - GetGeoZone() GeoZone // the geolocation zone where all are registered - GetSessionHeight() int64 // the block height when the session started -} - type RelayChain Identifier type GeoZone Identifier @@ -28,6 +21,16 @@ type Identifier interface { Bytes() []byte } +type Session interface { + NewSession(sessionHeight int64, blockHash string, geoZone GeoZone, relayChain RelayChain, application *coreTypes.Actor) (Session, types.Error) + GetServiceNodes() []*coreTypes.Actor // the ServiceNodes providing Web3 to the application + GetFishermen() []*coreTypes.Actor // the Fishermen monitoring the serviceNodes + GetApplication() *coreTypes.Actor // the Application consuming the web3 access + GetRelayChain() RelayChain // the chain identifier of the web3 + GetGeoZone() GeoZone // the geo-location zone where all are registered + GetSessionHeight() int64 // the block height when the session started +} + var _ Session = &session{} type session struct { diff --git a/utility/test/account_test.go b/utility/test/account_test.go deleted file mode 100644 index db3a41705..000000000 --- a/utility/test/account_test.go +++ /dev/null @@ -1,239 +0,0 @@ -package test - -import ( - "encoding/hex" - "math/big" - "sort" - "testing" - - "github.com/pokt-network/pocket/runtime/test_artifacts" - coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/crypto" - "github.com/pokt-network/pocket/utility" - "github.com/pokt-network/pocket/utility/types" - "github.com/stretchr/testify/require" -) - -func TestUtilityContext_AddAccountAmount(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - acc := GetAllTestingAccounts(t, ctx)[0] - - initialAmount, err := types.StringToBigInt(acc.GetAmount()) - require.NoError(t, err) - - addAmount := big.NewInt(1) - addrBz, er := hex.DecodeString(acc.GetAddress()) - require.NoError(t, er) - require.NoError(t, ctx.AddAccountAmount(addrBz, addAmount), "add account amount") - afterAmount, err := ctx.GetAccountAmount(addrBz) - require.NoError(t, err) - - expected := initialAmount.Add(initialAmount, addAmount) - require.Equal(t, expected, afterAmount) - // RESEARCH a golang specific solution for after test teardown - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_AddAccountAmountString(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - acc := GetAllTestingAccounts(t, ctx)[0] - - initialAmount, err := types.StringToBigInt(acc.GetAmount()) - require.NoError(t, err) - - addAmount := big.NewInt(1) - addAmountString := types.BigIntToString(addAmount) - addrBz, er := hex.DecodeString(acc.GetAddress()) - require.NoError(t, er) - require.NoError(t, ctx.AddAccountAmountString(addrBz, addAmountString), "add account amount string") - afterAmount, err := ctx.GetAccountAmount(addrBz) - require.NoError(t, err) - - expected := initialAmount.Add(initialAmount, addAmount) - require.Equal(t, expected, afterAmount) - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_AddPoolAmount(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - pool := GetAllTestingPools(t, ctx)[0] - - initialAmount, err := types.StringToBigInt(pool.GetAmount()) - require.NoError(t, err) - - addAmount := big.NewInt(1) - require.NoError(t, ctx.AddPoolAmount(pool.GetAddress(), addAmount), "add pool amount") - afterAmount, err := ctx.GetPoolAmount(pool.GetAddress()) - require.NoError(t, err) - - expected := initialAmount.Add(initialAmount, addAmount) - require.Equal(t, expected, afterAmount, "amounts are not equal") - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_HandleMessageSend(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - accs := GetAllTestingAccounts(t, ctx) - - sendAmount := big.NewInt(1000000) - sendAmountString := types.BigIntToString(sendAmount) - senderBalanceBefore, err := types.StringToBigInt(accs[0].GetAmount()) - require.NoError(t, err) - - recipientBalanceBefore, err := types.StringToBigInt(accs[1].GetAmount()) - require.NoError(t, err) - addrBz, er := hex.DecodeString(accs[0].GetAddress()) - require.NoError(t, er) - addrBz2, er := hex.DecodeString(accs[1].GetAddress()) - require.NoError(t, er) - msg := NewTestingSendMessage(t, addrBz, addrBz2, sendAmountString) - err = ctx.HandleMessageSend(&msg) - require.NoError(t, err, "handle message send") - - accs = GetAllTestingAccounts(t, ctx) - senderBalanceAfter, err := types.StringToBigInt(accs[0].GetAmount()) - require.NoError(t, err) - - recipientBalanceAfter, err := types.StringToBigInt(accs[1].GetAmount()) - require.NoError(t, err) - require.Equal(t, sendAmount, big.NewInt(0).Sub(senderBalanceBefore, senderBalanceAfter)) - require.Equal(t, sendAmount, big.NewInt(0).Sub(recipientBalanceAfter, recipientBalanceBefore)) - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_GetMessageSendSignerCandidates(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - accs := GetAllTestingAccounts(t, ctx) - - sendAmount := big.NewInt(1000000) - sendAmountString := types.BigIntToString(sendAmount) - addrBz, er := hex.DecodeString(accs[0].GetAddress()) - require.NoError(t, er) - addrBz2, er := hex.DecodeString(accs[1].GetAddress()) - require.NoError(t, er) - msg := NewTestingSendMessage(t, addrBz, addrBz2, sendAmountString) - candidates, err := ctx.GetMessageSendSignerCandidates(&msg) - require.NoError(t, err) - require.Equal(t, 1, len(candidates)) - require.Equal(t, addrBz, candidates[0]) - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_InsertPool(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - testPoolName := "TEST_POOL" - - addr, err := crypto.GenerateAddress() - require.NoError(t, err) - - amount := types.BigIntToString(big.NewInt(1000)) - err = ctx.InsertPool(testPoolName, addr, amount) - require.NoError(t, err, "insert pool") - gotAmount, err := ctx.GetPoolAmount(testPoolName) - require.NoError(t, err) - - gotAmountString := types.BigIntToString(gotAmount) - require.Equal(t, amount, gotAmountString) - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_SetAccountAmount(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - - addr, err := crypto.GenerateAddress() - require.NoError(t, err) - - amount := big.NewInt(100) - require.NoError(t, ctx.SetAccountAmount(addr, amount), "set account amount") - gotAmount, err := ctx.GetAccountAmount(addr) - require.NoError(t, err) - require.Equal(t, amount, gotAmount) - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_SetAccountWithAmountString(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - - addr, err := crypto.GenerateAddress() - require.NoError(t, err) - - amount := big.NewInt(100) - amountString := types.BigIntToString(amount) - require.NoError(t, ctx.SetAccountWithAmountString(addr, amountString), "set account amount string") - gotAmount, err := ctx.GetAccountAmount(addr) - require.NoError(t, err) - require.Equal(t, amount, gotAmount) - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_SetPoolAmount(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - pool := GetAllTestingPools(t, ctx)[0] - beforeAmount := pool.GetAmount() - beforeAmountBig, err := types.StringToBigInt(beforeAmount) - require.NoError(t, err) - - expectedAfterAmount := big.NewInt(100) - require.NoError(t, ctx.SetPoolAmount(pool.GetAddress(), expectedAfterAmount), "set pool amount") - amount, err := ctx.GetPoolAmount(pool.GetAddress()) - require.NoError(t, err) - require.NotEqual(t, beforeAmountBig, amount) - require.Equal(t, expectedAfterAmount, amount) - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_SubPoolAmount(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - pool := GetAllTestingPools(t, ctx)[0] - - beforeAmountBig := big.NewInt(1000000000000000) - err := ctx.SetPoolAmount(pool.GetAddress(), beforeAmountBig) - require.NoError(t, err) - subAmountBig := big.NewInt(100) - subAmount := types.BigIntToString(subAmountBig) - require.NoError(t, ctx.SubPoolAmount(pool.GetAddress(), subAmount), "sub pool amount") - amount, err := ctx.GetPoolAmount(pool.GetAddress()) - require.NoError(t, err) - require.NotEqual(t, beforeAmountBig, amount) - expected := beforeAmountBig.Sub(beforeAmountBig, subAmountBig) - require.Equal(t, expected, amount) - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_SubtractAccountAmount(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - acc := GetAllTestingAccounts(t, ctx)[0] - - beforeAmount := acc.GetAmount() - beforeAmountBig, err := types.StringToBigInt(beforeAmount) - require.NoError(t, err) - - subAmountBig := big.NewInt(100) - addrBz, er := hex.DecodeString(acc.GetAddress()) - require.NoError(t, er) - require.NoError(t, ctx.SubtractAccountAmount(addrBz, subAmountBig), "sub account amount") - amount, err := ctx.GetAccountAmount(addrBz) - require.NoError(t, err) - require.NotEqual(t, beforeAmountBig, amount) - expected := beforeAmountBig.Sub(beforeAmountBig, subAmountBig) - require.Equal(t, expected, amount) - test_artifacts.CleanupTest(ctx) -} - -func GetAllTestingAccounts(t *testing.T, ctx *utility.UtilityContext) []*coreTypes.Account { - accs, err := (ctx.Context.PersistenceRWContext).GetAllAccounts(0) - require.NoError(t, err) - sort.Slice(accs, func(i, j int) bool { - return accs[i].GetAddress() < accs[j].GetAddress() - }) - return accs -} - -func GetAllTestingPools(t *testing.T, ctx *utility.UtilityContext) []*coreTypes.Account { - accs, err := (ctx.Context.PersistenceRWContext).GetAllPools(0) - require.NoError(t, err) - sort.Slice(accs, func(i, j int) bool { - return accs[i].GetAddress() < accs[j].GetAddress() - }) - return accs -} diff --git a/utility/test/actor_test.go b/utility/test/actor_test.go deleted file mode 100644 index 71f8b11db..000000000 --- a/utility/test/actor_test.go +++ /dev/null @@ -1,633 +0,0 @@ -package test - -import ( - "encoding/hex" - "fmt" - "math" - "math/big" - "sort" - "testing" - - "github.com/pokt-network/pocket/runtime/test_artifacts" - coreTypes "github.com/pokt-network/pocket/shared/core/types" - "github.com/pokt-network/pocket/shared/crypto" - "github.com/pokt-network/pocket/utility" - typesUtil "github.com/pokt-network/pocket/utility/types" - "github.com/stretchr/testify/require" - "golang.org/x/exp/slices" - "google.golang.org/protobuf/proto" -) - -// CLEANUP: Move `App` specific tests to `app_test.go` - -func TestUtilityContext_HandleMessageStake(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.HandleMessageStake", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - - pubKey, err := crypto.GeneratePublicKey() - require.NoError(t, err) - - outputAddress, err := crypto.GenerateAddress() - require.NoError(t, err) - - err = ctx.SetAccountAmount(outputAddress, test_artifacts.DefaultAccountAmount) - require.NoError(t, err, "error setting account amount error") - - msg := &typesUtil.MessageStake{ - PublicKey: pubKey.Bytes(), - Chains: test_artifacts.DefaultChains, - Amount: test_artifacts.DefaultStakeAmountString, - ServiceUrl: "https://localhost.com", - OutputAddress: outputAddress, - Signer: outputAddress, - ActorType: actorType, - } - - er := ctx.HandleStakeMessage(msg) - require.NoError(t, er, "handle stake message") - - actor := getActorByAddr(t, ctx, actorType, pubKey.Address().String()) - - require.Equal(t, actor.GetAddress(), pubKey.Address().String(), "incorrect actor address") - if actorType != coreTypes.ActorType_ACTOR_TYPE_VAL { - require.Equal(t, msg.Chains, actor.GetChains(), "incorrect actor chains") - } - require.Equal(t, typesUtil.HeightNotUsed, actor.GetPausedHeight(), "incorrect actor height") - require.Equal(t, test_artifacts.DefaultStakeAmountString, actor.GetStakedAmount(), "incorrect actor stake amount") - require.Equal(t, typesUtil.HeightNotUsed, actor.GetUnstakingHeight(), "incorrect actor unstaking height") - require.Equal(t, outputAddress.String(), actor.GetOutput(), "incorrect actor output address") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_HandleMessageEditStake(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.HandleMessageEditStake", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - actor := getFirstActor(t, ctx, actorType) - - addr := actor.GetAddress() - addrBz, err := hex.DecodeString(addr) - require.NoError(t, err) - - msg := &typesUtil.MessageEditStake{ - Address: addrBz, - Chains: test_artifacts.DefaultChains, - Amount: test_artifacts.DefaultStakeAmountString, - Signer: addrBz, - ActorType: actorType, - } - msgChainsEdited := proto.Clone(msg).(*typesUtil.MessageEditStake) - msgChainsEdited.Chains = defaultTestingChainsEdited - - err = ctx.HandleEditStakeMessage(msgChainsEdited) - require.NoError(t, err, "handle edit stake message") - - actor = getActorByAddr(t, ctx, actorType, addr) - if actorType != coreTypes.ActorType_ACTOR_TYPE_VAL { - require.Equal(t, msgChainsEdited.Chains, actor.GetChains(), "incorrect edited chains") - } - require.Equal(t, test_artifacts.DefaultStakeAmountString, actor.GetStakedAmount(), "incorrect staked tokens") - require.Equal(t, typesUtil.HeightNotUsed, actor.GetUnstakingHeight(), "incorrect unstaking height") - - amountEdited := test_artifacts.DefaultAccountAmount.Add(test_artifacts.DefaultAccountAmount, big.NewInt(1)) - amountEditedString := typesUtil.BigIntToString(amountEdited) - msgAmountEdited := proto.Clone(msg).(*typesUtil.MessageEditStake) - msgAmountEdited.Amount = amountEditedString - - err = ctx.HandleEditStakeMessage(msgAmountEdited) - require.NoError(t, err, "handle edit stake message") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_HandleMessageUnpause(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.HandleMessageUnpause", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 1) - - var err error - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_VAL: - err = ctx.Context.SetParam(typesUtil.ValidatorMinimumPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - err = ctx.Context.SetParam(typesUtil.ServiceNodeMinimumPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_APP: - err = ctx.Context.SetParam(typesUtil.AppMinimumPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_FISH: - err = ctx.Context.SetParam(typesUtil.FishermanMinimumPauseBlocksParamName, 0) - default: - t.Fatalf("unexpected actor type %s", actorType.String()) - } - require.NoError(t, err, "error setting minimum pause blocks") - - actor := getFirstActor(t, ctx, actorType) - addr := actor.GetAddress() - addrBz, err := hex.DecodeString(addr) - require.NoError(t, err) - - err = ctx.SetActorPauseHeight(actorType, addrBz, 1) - require.NoError(t, err, "error setting pause height") - - actor = getActorByAddr(t, ctx, actorType, addr) - require.Equal(t, int64(1), actor.GetPausedHeight()) - - msgUnpauseActor := &typesUtil.MessageUnpause{ - Address: addrBz, - Signer: addrBz, - ActorType: actorType, - } - - err = ctx.HandleUnpauseMessage(msgUnpauseActor) - require.NoError(t, err, "handle unpause message") - - actor = getActorByAddr(t, ctx, actorType, addr) - require.Equal(t, int64(-1), actor.GetPausedHeight()) - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_HandleMessageUnstake(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.HandleMessageUnstake", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 1) - - var err error - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_APP: - err = ctx.Context.SetParam(typesUtil.AppMinimumPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_VAL: - err = ctx.Context.SetParam(typesUtil.ValidatorMinimumPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_FISH: - err = ctx.Context.SetParam(typesUtil.FishermanMinimumPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - err = ctx.Context.SetParam(typesUtil.ServiceNodeMinimumPauseBlocksParamName, 0) - default: - t.Fatalf("unexpected actor type %s", actorType.String()) - } - require.NoError(t, err, "error setting minimum pause blocks") - - actor := getFirstActor(t, ctx, actorType) - addr := actor.GetAddress() - addrBz, err := hex.DecodeString(actor.GetAddress()) - require.NoError(t, err) - - msg := &typesUtil.MessageUnstake{ - Address: addrBz, - Signer: addrBz, - ActorType: actorType, - } - - err = ctx.HandleUnstakeMessage(msg) - require.NoError(t, err, "handle unstake message") - - actor = getActorByAddr(t, ctx, actorType, addr) - require.Equal(t, defaultUnstaking, actor.GetUnstakingHeight(), "actor should be unstaking") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_BeginUnstakingMaxPaused(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.BeginUnstakingMaxPaused", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 1) - actor := getFirstActor(t, ctx, actorType) - - addrBz, err := hex.DecodeString(actor.GetAddress()) - require.NoError(t, err) - - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_APP: - err = ctx.Context.SetParam(typesUtil.AppMaxPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_VAL: - err = ctx.Context.SetParam(typesUtil.ValidatorMaxPausedBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_FISH: - err = ctx.Context.SetParam(typesUtil.FishermanMaxPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - err = ctx.Context.SetParam(typesUtil.ServiceNodeMaxPauseBlocksParamName, 0) - default: - t.Fatalf("unexpected actor type %s", actorType.String()) - } - require.NoError(t, err) - - err = ctx.SetActorPauseHeight(actorType, addrBz, 0) - require.NoError(t, err, "error setting actor pause height") - - err = ctx.BeginUnstakingMaxPaused() - require.NoError(t, err, "error beginning unstaking max paused actors") - - status, err := ctx.GetActorStatus(actorType, addrBz) - require.NoError(t, err) - require.Equal(t, int32(typesUtil.StakeStatus_Unstaking), status, "actor should be unstaking") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_CalculateMaxAppRelays(t *testing.T) { - ctx := NewTestingUtilityContext(t, 1) - actor := getFirstActor(t, ctx, coreTypes.ActorType_ACTOR_TYPE_APP) - newMaxRelays, err := ctx.CalculateAppRelays(actor.GetStakedAmount()) - require.NoError(t, err) - require.Equal(t, actor.GetGenericParam(), newMaxRelays) - test_artifacts.CleanupTest(ctx) -} - -func TestUtilityContext_CalculateUnstakingHeight(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.CalculateUnstakingHeight", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - var unstakingBlocks int64 - var err error - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_VAL: - unstakingBlocks, err = ctx.GetValidatorUnstakingBlocks() - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - unstakingBlocks, err = ctx.GetServiceNodeUnstakingBlocks() - case coreTypes.ActorType_ACTOR_TYPE_APP: - unstakingBlocks, err = ctx.GetAppUnstakingBlocks() - case coreTypes.ActorType_ACTOR_TYPE_FISH: - unstakingBlocks, err = ctx.GetFishermanUnstakingBlocks() - default: - t.Fatalf("unexpected actor type %s", actorType.String()) - } - require.NoError(t, err, "error getting unstaking blocks") - - unstakingHeight, err := ctx.GetUnstakingHeight(actorType) - require.NoError(t, err) - require.Equal(t, unstakingBlocks, unstakingHeight, "unexpected unstaking height") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_GetExists(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.GetExists", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - - actor := getFirstActor(t, ctx, actorType) - randAddr, err := crypto.GenerateAddress() - require.NoError(t, err) - - addrBz, err := hex.DecodeString(actor.GetAddress()) - require.NoError(t, err) - - exists, err := ctx.GetActorExists(actorType, addrBz) - require.NoError(t, err) - require.True(t, exists, "actor that should exist does not") - - exists, err = ctx.GetActorExists(actorType, randAddr) - require.NoError(t, err) - require.False(t, exists, "actor that shouldn't exist does") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_GetOutputAddress(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.GetOutputAddress", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - - actor := getFirstActor(t, ctx, actorType) - addrBz, err := hex.DecodeString(actor.GetAddress()) - require.NoError(t, err) - - outputAddress, err := ctx.GetActorOutputAddress(actorType, addrBz) - require.NoError(t, err) - require.Equal(t, actor.GetOutput(), hex.EncodeToString(outputAddress), "unexpected output address") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_GetPauseHeightIfExists(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.GetPauseHeightIfExists", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - pauseHeight := int64(100) - actor := getFirstActor(t, ctx, actorType) - - addrBz, err := hex.DecodeString(actor.GetAddress()) - require.NoError(t, err) - - err = ctx.SetActorPauseHeight(actorType, addrBz, pauseHeight) - require.NoError(t, err, "error setting actor pause height") - - gotPauseHeight, err := ctx.GetPauseHeight(actorType, addrBz) - require.NoError(t, err) - require.Equal(t, pauseHeight, gotPauseHeight, "unable to get pause height from the actor") - - randAddr, er := crypto.GenerateAddress() - require.NoError(t, er) - - _, err = ctx.GetPauseHeight(actorType, randAddr) - require.Error(t, err, "non existent actor should error") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_GetMessageEditStakeSignerCandidates(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.GetMessageEditStakeSignerCandidates", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - actor := getFirstActor(t, ctx, actorType) - - addrBz, err := hex.DecodeString(actor.GetAddress()) - require.NoError(t, err) - - msgEditStake := &typesUtil.MessageEditStake{ - Address: addrBz, - Chains: test_artifacts.DefaultChains, - Amount: test_artifacts.DefaultStakeAmountString, - ActorType: actorType, - } - candidates, err := ctx.GetMessageEditStakeSignerCandidates(msgEditStake) - require.NoError(t, err) - - require.Equal(t, 2, len(candidates), "unexpected number of candidates") - require.Equal(t, actor.GetOutput(), hex.EncodeToString(candidates[0]), "incorrect output candidate") - require.Equal(t, actor.GetAddress(), hex.EncodeToString(candidates[1]), "incorrect addr candidate") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_GetMessageUnpauseSignerCandidates(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.GetMessageUnpauseSignerCandidates", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - actor := getFirstActor(t, ctx, actorType) - - addrBz, err := hex.DecodeString(actor.GetAddress()) - require.NoError(t, err) - - msg := &typesUtil.MessageUnpause{ - Address: addrBz, - ActorType: actorType, - } - candidates, err := ctx.GetMessageUnpauseSignerCandidates(msg) - require.NoError(t, err) - - require.Equal(t, 2, len(candidates), "unexpected number of candidates") - require.Equal(t, actor.GetOutput(), hex.EncodeToString(candidates[0]), "incorrect output candidate") - require.Equal(t, actor.GetAddress(), hex.EncodeToString(candidates[1]), "incorrect addr candidate") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_GetMessageUnstakeSignerCandidates(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.GetMessageUnstakeSignerCandidates", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - actor := getFirstActor(t, ctx, actorType) - - addrBz, err := hex.DecodeString(actor.GetAddress()) - require.NoError(t, err) - - msg := &typesUtil.MessageUnstake{ - Address: addrBz, - ActorType: actorType, - } - candidates, err := ctx.GetMessageUnstakeSignerCandidates(msg) - require.NoError(t, err) - - require.Equal(t, 2, len(candidates), "unexpected number of candidates") - require.Equal(t, actor.GetOutput(), hex.EncodeToString(candidates[0]), "incorrect output candidate") - require.Equal(t, actor.GetAddress(), hex.EncodeToString(candidates[1]), "incorrect addr candidate") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_UnstakePausedBefore(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.UnstakePausedBefore", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 1) - - actor := getFirstActor(t, ctx, actorType) - require.Equal(t, int64(-1), actor.GetUnstakingHeight(), "wrong starting status") - - addr := actor.GetAddress() - addrBz, err := hex.DecodeString(addr) - require.NoError(t, err) - - err = ctx.SetActorPauseHeight(actorType, addrBz, 0) - require.NoError(t, err, "error setting actor pause height") - - var er error - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_APP: - er = ctx.Context.SetParam(typesUtil.AppMaxPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_VAL: - er = ctx.Context.SetParam(typesUtil.ValidatorMaxPausedBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_FISH: - er = ctx.Context.SetParam(typesUtil.FishermanMaxPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - er = ctx.Context.SetParam(typesUtil.ServiceNodeMaxPauseBlocksParamName, 0) - default: - t.Fatalf("unexpected actor type %s", actorType.String()) - } - require.NoError(t, er, "error setting max paused blocks") - - err = ctx.UnstakeActorPausedBefore(0, actorType) - require.NoError(t, err, "error unstaking actor pause before") - - err = ctx.UnstakeActorPausedBefore(1, actorType) - require.NoError(t, err, "error unstaking actor pause before height 1") - - actor = getActorByAddr(t, ctx, actorType, addr) - require.Equal(t, defaultUnstaking, actor.GetUnstakingHeight(), "status does not equal unstaking") - - var unstakingBlocks int64 - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_VAL: - unstakingBlocks, err = ctx.GetValidatorUnstakingBlocks() - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - unstakingBlocks, err = ctx.GetServiceNodeUnstakingBlocks() - case coreTypes.ActorType_ACTOR_TYPE_APP: - unstakingBlocks, err = ctx.GetAppUnstakingBlocks() - case coreTypes.ActorType_ACTOR_TYPE_FISH: - unstakingBlocks, err = ctx.GetFishermanUnstakingBlocks() - default: - t.Fatalf("unexpected actor type %s", actorType.String()) - } - require.NoError(t, err, "error getting unstaking blocks") - require.Equal(t, unstakingBlocks+1, actor.GetUnstakingHeight(), "incorrect unstaking height") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_UnstakeActorsThatAreReady(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.UnstakeActorsThatAreReady", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 1) - - var poolName string - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_APP: - poolName = coreTypes.Pools_POOLS_APP_STAKE.FriendlyName() - case coreTypes.ActorType_ACTOR_TYPE_VAL: - poolName = coreTypes.Pools_POOLS_VALIDATOR_STAKE.FriendlyName() - case coreTypes.ActorType_ACTOR_TYPE_FISH: - poolName = coreTypes.Pools_POOLS_FISHERMAN_STAKE.FriendlyName() - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - poolName = coreTypes.Pools_POOLS_SERVICE_NODE_STAKE.FriendlyName() - default: - t.Fatalf("unexpected actor type %s", actorType.String()) - } - - er := ctx.SetPoolAmount(poolName, big.NewInt(math.MaxInt64)) - require.NoError(t, er) - - err := ctx.Context.SetParam(typesUtil.AppUnstakingBlocksParamName, 0) - require.NoError(t, err) - - err = ctx.Context.SetParam(typesUtil.AppMaxPauseBlocksParamName, 0) - require.NoError(t, err) - - actors := getAllTestingActors(t, ctx, actorType) - for _, actor := range actors { - addrBz, er := hex.DecodeString(actor.GetAddress()) - require.NoError(t, er) - er = ctx.SetActorPauseHeight(actorType, addrBz, 1) - require.NoError(t, er) - } - - err = ctx.UnstakeActorPausedBefore(2, actorType) - require.NoError(t, err) - - err = ctx.UnstakeActorsThatAreReady() - require.NoError(t, err) - - actors = getAllTestingActors(t, ctx, actorType) - require.NotEqual(t, actors[0].GetUnstakingHeight(), -1, "validators still exists after unstake that are ready() call") - - // TODO: We need to better define what 'deleted' really is in the postgres world. - // We might not need to 'unstakeActorsThatAreReady' if we are already filtering by unstakingHeight - test_artifacts.CleanupTest(ctx) - }) - } -} - -func TestUtilityContext_BeginUnstakingMaxPausedActors(t *testing.T) { - for _, actorType := range actorTypes { - t.Run(fmt.Sprintf("%s.BeginUnstakingMaxPausedActors", actorType.String()), func(t *testing.T) { - ctx := NewTestingUtilityContext(t, 1) - actor := getFirstActor(t, ctx, actorType) - - var err error - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_APP: - err = ctx.Context.SetParam(typesUtil.AppMaxPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_VAL: - err = ctx.Context.SetParam(typesUtil.ValidatorMaxPausedBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_FISH: - err = ctx.Context.SetParam(typesUtil.FishermanMaxPauseBlocksParamName, 0) - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - err = ctx.Context.SetParam(typesUtil.ServiceNodeMaxPauseBlocksParamName, 0) - default: - t.Fatalf("unexpected actor type %s", actorType.String()) - } - require.NoError(t, err) - - addrBz, er := hex.DecodeString(actor.GetAddress()) - require.NoError(t, er) - - err = ctx.SetActorPauseHeight(actorType, addrBz, 0) - require.NoError(t, err) - - err = ctx.BeginUnstakingMaxPaused() - require.NoError(t, err) - - status, err := ctx.GetActorStatus(actorType, addrBz) - require.NoError(t, err) - require.Equal(t, int32(typesUtil.StakeStatus_Unstaking), status, "incorrect status") - - test_artifacts.CleanupTest(ctx) - }) - } -} - -// Helpers - -func getAllTestingActors(t *testing.T, ctx *utility.UtilityContext, actorType coreTypes.ActorType) (actors []*coreTypes.Actor) { - actors = make([]*coreTypes.Actor, 0) - switch actorType { - case coreTypes.ActorType_ACTOR_TYPE_APP: - apps := getAllTestingApps(t, ctx) - actors = append(actors, apps...) - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - nodes := getAllTestingNodes(t, ctx) - actors = append(actors, nodes...) - case coreTypes.ActorType_ACTOR_TYPE_VAL: - vals := getAllTestingValidators(t, ctx) - actors = append(actors, vals...) - case coreTypes.ActorType_ACTOR_TYPE_FISH: - fish := getAllTestingFish(t, ctx) - actors = append(actors, fish...) - default: - t.Fatalf("unexpected actor type %s", actorType.String()) - } - - return -} - -func getFirstActor(t *testing.T, ctx *utility.UtilityContext, actorType coreTypes.ActorType) *coreTypes.Actor { - return getAllTestingActors(t, ctx, actorType)[0] -} - -func getActorByAddr(t *testing.T, ctx *utility.UtilityContext, actorType coreTypes.ActorType, addr string) (actor *coreTypes.Actor) { - actors := getAllTestingActors(t, ctx, actorType) - idx := slices.IndexFunc(actors, func(a *coreTypes.Actor) bool { return a.GetAddress() == addr }) - return actors[idx] -} - -func getAllTestingApps(t *testing.T, ctx *utility.UtilityContext) []*coreTypes.Actor { - actors, err := (ctx.Context.PersistenceRWContext).GetAllApps(ctx.Height) - require.NoError(t, err) - return actors -} - -func getAllTestingValidators(t *testing.T, ctx *utility.UtilityContext) []*coreTypes.Actor { - actors, err := (ctx.Context.PersistenceRWContext).GetAllValidators(ctx.Height) - require.NoError(t, err) - sort.Slice(actors, func(i, j int) bool { - return actors[i].GetAddress() < actors[j].GetAddress() - }) - return actors -} - -func getAllTestingFish(t *testing.T, ctx *utility.UtilityContext) []*coreTypes.Actor { - actors, err := (ctx.Context.PersistenceRWContext).GetAllFishermen(ctx.Height) - require.NoError(t, err) - return actors -} - -func getAllTestingNodes(t *testing.T, ctx *utility.UtilityContext) []*coreTypes.Actor { - actors, err := (ctx.Context.PersistenceRWContext).GetAllServiceNodes(ctx.Height) - require.NoError(t, err) - return actors -} diff --git a/utility/test/message_test.go b/utility/test/message_test.go deleted file mode 100644 index 571b9ee25..000000000 --- a/utility/test/message_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package test - -import ( - "github.com/pokt-network/pocket/utility/types" - "testing" -) - -func NewTestingSendMessage(_ *testing.T, fromAddress, toAddress []byte, amount string) types.MessageSend { - return types.MessageSend{ - FromAddress: fromAddress, - ToAddress: toAddress, - Amount: amount, - } -} diff --git a/utility/transaction.go b/utility/transaction.go index 2ea4d5912..eaabf9879 100644 --- a/utility/transaction.go +++ b/utility/transaction.go @@ -13,28 +13,26 @@ import ( func (u *utilityModule) CheckTransaction(txProtoBytes []byte) error { // Is the tx already in the mempool (in memory)? - txHash := typesUtil.TransactionHash(txProtoBytes) + txHash := typesUtil.TxHash(txProtoBytes) if u.mempool.Contains(txHash) { return typesUtil.ErrDuplicateTransaction() } - // Is the tx already indexed (on disk)? - persistenceModule := u.GetBus().GetPersistenceModule() - if txExists, err := persistenceModule.TransactionExists(txHash); err != nil { + // Is the tx already committed & indexed (on disk)? + if txExists, err := u.GetBus().GetPersistenceModule().TransactionExists(txHash); err != nil { return err } else if txExists { - // TODO: non-ordered nonce requires non-pruned tx indexer return typesUtil.ErrTransactionAlreadyCommitted() } - // Can the tx bytes be decoded as a protobuf? - transaction := &typesUtil.Transaction{} - if err := codec.GetCodec().Unmarshal(txProtoBytes, transaction); err != nil { + // Can the tx be decoded? + tx := &typesUtil.Transaction{} + if err := codec.GetCodec().Unmarshal(txProtoBytes, tx); err != nil { return typesUtil.ErrProtoUnmarshal(err) } // Does the tx pass basic validation? - if err := transaction.ValidateBasic(); err != nil { + if err := tx.ValidateBasic(); err != nil { return err } @@ -42,21 +40,20 @@ func (u *utilityModule) CheckTransaction(txProtoBytes []byte) error { return u.mempool.AddTx(txProtoBytes) } -func (u *UtilityContext) ApplyTransaction(index int, tx *typesUtil.Transaction) (modules.TxResult, typesUtil.Error) { - msg, signer, err := u.AnteHandleMessage(tx) +func (u *utilityContext) applyTx(index int, tx *typesUtil.Transaction) (modules.TxResult, typesUtil.Error) { + msg, signer, err := u.anteHandleMessage(tx) if err != nil { return nil, err } - return tx.ToTxResult(u.Height, index, signer, msg.GetMessageRecipient(), msg.GetMessageName(), u.HandleMessage(msg)) + return tx.ToTxResult(u.height, index, signer, msg.GetMessageRecipient(), msg.GetMessageName(), u.handleMessage(msg)) } -// CLEANUP: Exposed for testing purposes only -func (u *UtilityContext) AnteHandleMessage(tx *typesUtil.Transaction) (msg typesUtil.Message, signer string, err typesUtil.Error) { - msg, err = tx.Message() +func (u *utilityContext) anteHandleMessage(tx *typesUtil.Transaction) (msg typesUtil.Message, signer string, err typesUtil.Error) { + msg, err = tx.GetMessage() if err != nil { return nil, "", err } - fee, err := u.GetFee(msg, msg.GetActorType()) + fee, err := u.getFee(msg, msg.GetActorType()) if err != nil { return nil, "", err } @@ -65,7 +62,7 @@ func (u *UtilityContext) AnteHandleMessage(tx *typesUtil.Transaction) (msg types return nil, "", typesUtil.ErrNewPublicKeyFromBytes(er) } address := pubKey.Address() - accountAmount, err := u.GetAccountAmount(address) + accountAmount, err := u.getAccountAmount(address) if err != nil { return nil, "", typesUtil.ErrGetAccountAmount(err) } @@ -73,7 +70,7 @@ func (u *UtilityContext) AnteHandleMessage(tx *typesUtil.Transaction) (msg types if accountAmount.Sign() == -1 { return nil, "", typesUtil.ErrInsufficientAmount(address.String()) } - signerCandidates, err := u.GetSignerCandidates(msg) + signerCandidates, err := u.getSignerCandidates(msg) if err != nil { return nil, "", err } @@ -88,334 +85,12 @@ func (u *UtilityContext) AnteHandleMessage(tx *typesUtil.Transaction) (msg types if !isValidSigner { return nil, signer, typesUtil.ErrInvalidSigner() } - if err := u.SetAccountAmount(address, accountAmount); err != nil { + if err := u.setAccountAmount(address, accountAmount); err != nil { return nil, signer, err } - if err := u.AddPoolAmount(coreTypes.Pools_POOLS_FEE_COLLECTOR.FriendlyName(), fee); err != nil { + if err := u.addPoolAmount(coreTypes.Pools_POOLS_FEE_COLLECTOR.FriendlyName(), fee); err != nil { return nil, "", err } msg.SetSigner(address) return msg, signer, nil } - -func (u *UtilityContext) HandleMessage(msg typesUtil.Message) (err typesUtil.Error) { - switch x := msg.(type) { - case *typesUtil.MessageDoubleSign: - return u.HandleMessageDoubleSign(x) - case *typesUtil.MessageSend: - return u.HandleMessageSend(x) - case *typesUtil.MessageStake: - return u.HandleStakeMessage(x) - case *typesUtil.MessageEditStake: - return u.HandleEditStakeMessage(x) - case *typesUtil.MessageUnstake: - return u.HandleUnstakeMessage(x) - case *typesUtil.MessageUnpause: - return u.HandleUnpauseMessage(x) - case *typesUtil.MessageChangeParameter: - return u.HandleMessageChangeParameter(x) - default: - return typesUtil.ErrUnknownMessage(x) - } -} - -func (u *UtilityContext) HandleMessageSend(message *typesUtil.MessageSend) typesUtil.Error { - // convert the amount to big.Int - amount, err := typesUtil.StringToBigInt(message.Amount) - if err != nil { - return err - } - // get the sender's account amount - fromAccountAmount, err := u.GetAccountAmount(message.FromAddress) - if err != nil { - return err - } - // subtract that amount from the sender - fromAccountAmount.Sub(fromAccountAmount, amount) - // if they go negative, they don't have sufficient funds - // NOTE: we don't use the u.SubtractAccountAmount() function because Utility needs to do this check - if fromAccountAmount.Sign() == -1 { - return typesUtil.ErrInsufficientAmount(hex.EncodeToString(message.FromAddress)) - } - // add the amount to the recipient's account - if err := u.AddAccountAmount(message.ToAddress, amount); err != nil { - return err - } - // set the sender's account amount - if err := u.SetAccountAmount(message.FromAddress, fromAccountAmount); err != nil { - return err - } - return nil -} - -func (u *UtilityContext) HandleStakeMessage(message *typesUtil.MessageStake) typesUtil.Error { - publicKey, err := u.BytesToPublicKey(message.PublicKey) - if err != nil { - return err - } - // ensure above minimum stake - amount, err := u.CheckAboveMinStake(message.ActorType, message.Amount) - if err != nil { - return err - } - // ensure signer has sufficient funding for the stake - signerAccountAmount, err := u.GetAccountAmount(message.Signer) - if err != nil { - return err - } - // calculate new signer account amount - signerAccountAmount.Sub(signerAccountAmount, amount) - if signerAccountAmount.Sign() == -1 { - return typesUtil.ErrInsufficientAmount(hex.EncodeToString(message.Signer)) - } - // validators don't have chains field - if err := u.CheckBelowMaxChains(message.ActorType, message.Chains); err != nil { - return err - } - // ensure actor doesn't already exist - if exists, err := u.GetActorExists(message.ActorType, publicKey.Address()); err != nil || exists { - if exists { - return typesUtil.ErrAlreadyExists() - } - return err - } - // update account amount - if err := u.SetAccountAmount(message.Signer, signerAccountAmount); err != nil { - return err - } - // move funds from account to pool - if err := u.AddPoolAmount(coreTypes.Pools_POOLS_APP_STAKE.FriendlyName(), amount); err != nil { - return err - } - var er error - store := u.Store() - // insert actor - switch message.ActorType { - case coreTypes.ActorType_ACTOR_TYPE_APP: - maxRelays, err := u.CalculateAppRelays(message.Amount) - if err != nil { - return err - } - er = store.InsertApp(publicKey.Address(), publicKey.Bytes(), message.OutputAddress, false, int32(typesUtil.StakeStatus_Staked), maxRelays, message.Amount, message.Chains, typesUtil.HeightNotUsed, typesUtil.HeightNotUsed) - case coreTypes.ActorType_ACTOR_TYPE_FISH: - er = store.InsertFisherman(publicKey.Address(), publicKey.Bytes(), message.OutputAddress, false, int32(typesUtil.StakeStatus_Staked), message.ServiceUrl, message.Amount, message.Chains, typesUtil.HeightNotUsed, typesUtil.HeightNotUsed) - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - er = store.InsertServiceNode(publicKey.Address(), publicKey.Bytes(), message.OutputAddress, false, int32(typesUtil.StakeStatus_Staked), message.ServiceUrl, message.Amount, message.Chains, typesUtil.HeightNotUsed, typesUtil.HeightNotUsed) - case coreTypes.ActorType_ACTOR_TYPE_VAL: - er = store.InsertValidator(publicKey.Address(), publicKey.Bytes(), message.OutputAddress, false, int32(typesUtil.StakeStatus_Staked), message.ServiceUrl, message.Amount, typesUtil.HeightNotUsed, typesUtil.HeightNotUsed) - } - if er != nil { - return typesUtil.ErrInsert(er) - } - return nil -} - -func (u *UtilityContext) HandleEditStakeMessage(message *typesUtil.MessageEditStake) typesUtil.Error { - // ensure actor exists - if exists, err := u.GetActorExists(message.ActorType, message.Address); err != nil || !exists { - if !exists { - return typesUtil.ErrNotExists() - } - return err - } - currentStakeAmount, err := u.GetStakeAmount(message.ActorType, message.Address) - if err != nil { - return err - } - amount, err := typesUtil.StringToBigInt(message.Amount) - if err != nil { - return err - } - // ensure new stake >= current stake - amount.Sub(amount, currentStakeAmount) - if amount.Sign() == -1 { - return typesUtil.ErrStakeLess() - } - // ensure signer has sufficient funding for the stake - signerAccountAmount, err := u.GetAccountAmount(message.Signer) - if err != nil { - return err - } - signerAccountAmount.Sub(signerAccountAmount, amount) - if signerAccountAmount.Sign() == -1 { - return typesUtil.ErrInsufficientAmount(hex.EncodeToString(message.Signer)) - } - if err := u.CheckBelowMaxChains(message.ActorType, message.Chains); err != nil { - return err - } - // update account amount - if err := u.SetAccountAmount(message.Signer, signerAccountAmount); err != nil { - return err - } - // move funds from account to pool - if err := u.AddPoolAmount(coreTypes.Pools_POOLS_APP_STAKE.FriendlyName(), amount); err != nil { - return err - } - store := u.Store() - var er error - switch message.ActorType { - case coreTypes.ActorType_ACTOR_TYPE_APP: - maxRelays, err := u.CalculateAppRelays(message.Amount) - if err != nil { - return err - } - er = store.UpdateApp(message.Address, maxRelays, message.Amount, message.Chains) - case coreTypes.ActorType_ACTOR_TYPE_FISH: - er = store.UpdateFisherman(message.Address, message.ServiceUrl, message.Amount, message.Chains) - case coreTypes.ActorType_ACTOR_TYPE_SERVICENODE: - er = store.UpdateServiceNode(message.Address, message.ServiceUrl, message.Amount, message.Chains) - case coreTypes.ActorType_ACTOR_TYPE_VAL: - er = store.UpdateValidator(message.Address, message.ServiceUrl, message.Amount) - } - if er != nil { - return typesUtil.ErrInsert(er) - } - return nil -} - -func (u *UtilityContext) HandleUnstakeMessage(message *typesUtil.MessageUnstake) typesUtil.Error { - if status, err := u.GetActorStatus(message.ActorType, message.Address); err != nil || status != int32(typesUtil.StakeStatus_Staked) { - if status != int32(typesUtil.StakeStatus_Staked) { - return typesUtil.ErrInvalidStatus(status, int32(typesUtil.StakeStatus_Staked)) - } - return err - } - unstakingHeight, err := u.GetUnstakingHeight(message.ActorType) - if err != nil { - return err - } - if err := u.SetActorUnstaking(message.ActorType, unstakingHeight, message.Address); err != nil { - return err - } - return nil -} - -func (u *UtilityContext) HandleUnpauseMessage(message *typesUtil.MessageUnpause) typesUtil.Error { - pausedHeight, err := u.GetPauseHeight(message.ActorType, message.Address) - if err != nil { - return err - } - if pausedHeight == typesUtil.HeightNotUsed { - return typesUtil.ErrNotPaused() - } - minPauseBlocks, err := u.GetMinimumPauseBlocks(message.ActorType) - if err != nil { - return err - } - latestHeight, err := u.GetLatestBlockHeight() - if err != nil { - return err - } - if latestHeight < int64(minPauseBlocks)+pausedHeight { - return typesUtil.ErrNotReadyToUnpause() - } - if err := u.SetActorPauseHeight(message.ActorType, message.Address, typesUtil.HeightNotUsed); err != nil { - return err - } - return nil -} - -func (u *UtilityContext) HandleMessageDoubleSign(message *typesUtil.MessageDoubleSign) typesUtil.Error { - latestHeight, err := u.GetLatestBlockHeight() - if err != nil { - return err - } - evidenceAge := latestHeight - message.VoteA.Height - maxEvidenceAge, err := u.GetMaxEvidenceAgeInBlocks() - if err != nil { - return err - } - if evidenceAge > int64(maxEvidenceAge) { - return typesUtil.ErrMaxEvidenceAge() - } - pk, er := crypto.NewPublicKeyFromBytes(message.VoteB.PublicKey) - if er != nil { - return typesUtil.ErrNewPublicKeyFromBytes(er) - } - doubleSigner := pk.Address() - // burn validator for double signing blocks - burnPercentage, err := u.GetDoubleSignBurnPercentage() - if err != nil { - return err - } - if err := u.BurnActor(coreTypes.ActorType_ACTOR_TYPE_VAL, burnPercentage, doubleSigner); err != nil { - return err - } - return nil -} - -func (u *UtilityContext) HandleMessageChangeParameter(message *typesUtil.MessageChangeParameter) typesUtil.Error { - cdc := u.Codec() - v, err := cdc.FromAny(message.ParameterValue) - if err != nil { - return typesUtil.ErrProtoFromAny(err) - } - return u.UpdateParam(message.ParameterKey, v) -} - -func (u *UtilityContext) GetSignerCandidates(msg typesUtil.Message) ([][]byte, typesUtil.Error) { - switch x := msg.(type) { - case *typesUtil.MessageDoubleSign: - return u.GetMessageDoubleSignSignerCandidates(x) - case *typesUtil.MessageSend: - return u.GetMessageSendSignerCandidates(x) - case *typesUtil.MessageStake: - return u.GetMessageStakeSignerCandidates(x) - case *typesUtil.MessageUnstake: - return u.GetMessageUnstakeSignerCandidates(x) - case *typesUtil.MessageUnpause: - return u.GetMessageUnpauseSignerCandidates(x) - case *typesUtil.MessageChangeParameter: - return u.GetMessageChangeParameterSignerCandidates(x) - default: - return nil, typesUtil.ErrUnknownMessage(x) - } -} - -func (u *UtilityContext) GetMessageStakeSignerCandidates(msg *typesUtil.MessageStake) ([][]byte, typesUtil.Error) { - pk, er := crypto.NewPublicKeyFromBytes(msg.PublicKey) - if er != nil { - return nil, typesUtil.ErrNewPublicKeyFromBytes(er) - } - candidates := make([][]byte, 0) - candidates = append(candidates, msg.OutputAddress, pk.Address()) - return candidates, nil -} - -func (u *UtilityContext) GetMessageEditStakeSignerCandidates(msg *typesUtil.MessageEditStake) ([][]byte, typesUtil.Error) { - output, err := u.GetActorOutputAddress(msg.ActorType, msg.Address) - if err != nil { - return nil, err - } - candidates := make([][]byte, 0) - candidates = append(candidates, output, msg.Address) - return candidates, nil -} - -func (u *UtilityContext) GetMessageUnstakeSignerCandidates(msg *typesUtil.MessageUnstake) ([][]byte, typesUtil.Error) { - output, err := u.GetActorOutputAddress(msg.ActorType, msg.Address) - if err != nil { - return nil, err - } - candidates := make([][]byte, 0) - candidates = append(candidates, output, msg.Address) - return candidates, nil -} - -func (u *UtilityContext) GetMessageUnpauseSignerCandidates(msg *typesUtil.MessageUnpause) ([][]byte, typesUtil.Error) { - output, err := u.GetActorOutputAddress(msg.ActorType, msg.Address) - if err != nil { - return nil, err - } - candidates := make([][]byte, 0) - candidates = append(candidates, output, msg.Address) - return candidates, nil -} - -func (u *UtilityContext) GetMessageSendSignerCandidates(msg *typesUtil.MessageSend) ([][]byte, typesUtil.Error) { - return [][]byte{msg.FromAddress}, nil -} - -func (u *UtilityContext) GetMessageDoubleSignSignerCandidates(msg *typesUtil.MessageDoubleSign) ([][]byte, typesUtil.Error) { - return [][]byte{msg.ReporterAddress}, nil -} diff --git a/utility/test/transaction_test.go b/utility/transaction_test.go similarity index 69% rename from utility/test/transaction_test.go rename to utility/transaction_test.go index 6ba8327d4..15130f40f 100644 --- a/utility/test/transaction_test.go +++ b/utility/transaction_test.go @@ -1,4 +1,4 @@ -package test +package utility import ( "encoding/hex" @@ -7,11 +7,10 @@ import ( "github.com/pokt-network/pocket/runtime/test_artifacts" "github.com/pokt-network/pocket/shared/codec" + "github.com/pokt-network/pocket/shared/converters" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/crypto" - "github.com/pokt-network/pocket/utility" typesUtil "github.com/pokt-network/pocket/utility/types" - utilTypes "github.com/pokt-network/pocket/utility/types" "github.com/stretchr/testify/require" ) @@ -20,47 +19,43 @@ var ( ) func TestUtilityContext_AnteHandleMessage(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) tx, startingBalance, _, signer := newTestingTransaction(t, ctx) - _, signerString, err := ctx.AnteHandleMessage(tx) + _, signerString, err := ctx.anteHandleMessage(tx) require.NoError(t, err) require.Equal(t, signer.Address().String(), signerString) - feeBig, err := ctx.GetMessageSendFee() + feeBig, err := ctx.getMessageSendFee() require.NoError(t, err) expectedAfterBalance := big.NewInt(0).Sub(startingBalance, feeBig) - amount, err := ctx.GetAccountAmount(signer.Address()) + amount, err := ctx.getAccountAmount(signer.Address()) require.NoError(t, err) require.Equal(t, expectedAfterBalance, amount, "unexpected after balance") - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_ApplyTransaction(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) tx, startingBalance, amount, signer := newTestingTransaction(t, ctx) - txResult, err := ctx.ApplyTransaction(0, tx) + txResult, err := ctx.applyTx(0, tx) require.NoError(t, err) require.Equal(t, int32(0), txResult.GetResultCode()) require.Equal(t, "", txResult.GetError()) - feeBig, err := ctx.GetMessageSendFee() + feeBig, err := ctx.getMessageSendFee() require.NoError(t, err) expectedAmountSubtracted := amount.Add(amount, feeBig) expectedAfterBalance := big.NewInt(0).Sub(startingBalance, expectedAmountSubtracted) - amount, err = ctx.GetAccountAmount(signer.Address()) + amount, err = ctx.getAccountAmount(signer.Address()) require.NoError(t, err) require.Equal(t, expectedAfterBalance, amount, "unexpected after balance") - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_CheckTransaction(t *testing.T) { mockBusInTestModules(t) - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) tx, _, _, _ := newTestingTransaction(t, ctx) txBz, err := tx.Bytes() @@ -71,34 +66,30 @@ func TestUtilityContext_CheckTransaction(t *testing.T) { require.NoError(t, err) require.True(t, testUtilityMod.GetMempool().Contains(hash)) require.Equal(t, testUtilityMod.CheckTransaction(txBz).Error(), typesUtil.ErrDuplicateTransaction().Error()) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_GetSignerCandidates(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - accs := GetAllTestingAccounts(t, ctx) + ctx := newTestingUtilityContext(t, 0) + accs := getAllTestingAccounts(t, ctx) sendAmount := big.NewInt(1000000) - sendAmountString := typesUtil.BigIntToString(sendAmount) + sendAmountString := converters.BigIntToString(sendAmount) addrBz, er := hex.DecodeString(accs[0].GetAddress()) require.NoError(t, er) addrBz2, er := hex.DecodeString(accs[1].GetAddress()) require.NoError(t, er) msg := NewTestingSendMessage(t, addrBz, addrBz2, sendAmountString) - candidates, err := ctx.GetSignerCandidates(&msg) + candidates, err := ctx.getSignerCandidates(&msg) require.NoError(t, err) require.Equal(t, 1, len(candidates), "wrong number of candidates") require.Equal(t, accs[0].GetAddress(), hex.EncodeToString(candidates[0]), "unexpected signer candidate") - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_CreateAndApplyBlock(t *testing.T) { mockBusInTestModules(t) - ctx := NewTestingUtilityContext(t, 0) + ctx := newTestingUtilityContext(t, 0) tx, _, _, _ := newTestingTransaction(t, ctx) proposer := getFirstActor(t, ctx, coreTypes.ActorType_ACTOR_TYPE_VAL) @@ -111,41 +102,37 @@ func TestUtilityContext_CreateAndApplyBlock(t *testing.T) { require.NotEmpty(t, appHash) require.Equal(t, 1, len(txs)) require.Equal(t, txs[0], txBz) - - test_artifacts.CleanupTest(ctx) } func TestUtilityContext_HandleMessage(t *testing.T) { - ctx := NewTestingUtilityContext(t, 0) - accs := GetAllTestingAccounts(t, ctx) + ctx := newTestingUtilityContext(t, 0) + accs := getAllTestingAccounts(t, ctx) sendAmount := big.NewInt(1000000) - sendAmountString := typesUtil.BigIntToString(sendAmount) - senderBalanceBefore, err := typesUtil.StringToBigInt(accs[0].GetAmount()) + sendAmountString := converters.BigIntToString(sendAmount) + senderBalanceBefore, err := converters.StringToBigInt(accs[0].GetAmount()) require.NoError(t, err) - recipientBalanceBefore, err := typesUtil.StringToBigInt(accs[1].GetAmount()) + recipientBalanceBefore, err := converters.StringToBigInt(accs[1].GetAmount()) require.NoError(t, err) addrBz, er := hex.DecodeString(accs[0].GetAddress()) require.NoError(t, er) addrBz2, er := hex.DecodeString(accs[1].GetAddress()) require.NoError(t, er) msg := NewTestingSendMessage(t, addrBz, addrBz2, sendAmountString) - require.NoError(t, ctx.HandleMessageSend(&msg)) - accs = GetAllTestingAccounts(t, ctx) - senderBalanceAfter, err := typesUtil.StringToBigInt(accs[0].GetAmount()) + require.NoError(t, ctx.handleMessageSend(&msg)) + accs = getAllTestingAccounts(t, ctx) + senderBalanceAfter, err := converters.StringToBigInt(accs[0].GetAmount()) require.NoError(t, err) - recipientBalanceAfter, err := typesUtil.StringToBigInt(accs[1].GetAmount()) + recipientBalanceAfter, err := converters.StringToBigInt(accs[1].GetAmount()) require.NoError(t, err) require.Equal(t, sendAmount, big.NewInt(0).Sub(senderBalanceBefore, senderBalanceAfter), "unexpected sender balance") require.Equal(t, sendAmount, big.NewInt(0).Sub(recipientBalanceAfter, recipientBalanceBefore), "unexpected recipient balance") - - test_artifacts.CleanupTest(ctx) } -func newTestingTransaction(t *testing.T, ctx *utility.UtilityContext) (transaction *typesUtil.Transaction, startingBalance, amountSent *big.Int, signer crypto.PrivateKey) { +func newTestingTransaction(t *testing.T, ctx *utilityContext) (tx *typesUtil.Transaction, startingBalance, amountSent *big.Int, signer crypto.PrivateKey) { amountSent = new(big.Int).Set(defaultSendAmount) startingBalance = new(big.Int).Set(test_artifacts.DefaultAccountAmount) @@ -156,17 +143,17 @@ func newTestingTransaction(t *testing.T, ctx *utility.UtilityContext) (transacti require.NoError(t, err) signerAddr := signer.Address() - require.NoError(t, ctx.SetAccountAmount(signerAddr, startingBalance)) + require.NoError(t, ctx.setAccountAmount(signerAddr, startingBalance)) - msg := NewTestingSendMessage(t, signerAddr, recipientAddr.Bytes(), utilTypes.BigIntToString(amountSent)) + msg := NewTestingSendMessage(t, signerAddr, recipientAddr.Bytes(), converters.BigIntToString(amountSent)) any, err := codec.GetCodec().ToAny(&msg) require.NoError(t, err) - transaction = &typesUtil.Transaction{ + tx = &typesUtil.Transaction{ Msg: any, Nonce: testNonce, } - require.NoError(t, transaction.Sign(signer)) + require.NoError(t, tx.Sign(signer)) return } diff --git a/utility/types/constants.go b/utility/types/constants.go new file mode 100644 index 000000000..bfa459503 --- /dev/null +++ b/utility/types/constants.go @@ -0,0 +1,9 @@ +package types + +// CLEANUP: Consider moving these into a shared location or eliminating altogether +const ( + ZeroInt = 0 + // IMPROVE: -1 is returned when retrieving the paused height of an unpaused actor. Consider a more user friendly and semantic way of managing this. + HeightNotUsed = int64(-1) + EmptyString = "" +) diff --git a/utility/types/error.go b/utility/types/error.go index 29a7e02e3..48f55869d 100644 --- a/utility/types/error.go +++ b/utility/types/error.go @@ -1,9 +1,14 @@ package types +// DISCUSS(M5): Evaluate how Pocket specific errors should be managed and returned to the client +// TECHDEBT: Remove reference to the term `Proto`; it's why we created a codec package + import ( "encoding/hex" "errors" "fmt" + + cryptoPocket "github.com/pokt-network/pocket/shared/crypto" ) type Error interface { @@ -11,21 +16,23 @@ type Error interface { error } -type StdErr struct { +var _ Error = &stdErr{} + +type stdErr struct { CodeError Code error } -func (se StdErr) Error() string { +func (se *stdErr) Error() string { return fmt.Sprintf("CODE: %v, ERROR: %s", se.Code(), se.error.Error()) } -func (se StdErr) Code() Code { +func (se *stdErr) Code() Code { return se.CodeError } func NewError(code Code, msg string) Error { - return StdErr{ + return &stdErr{ CodeError: code, error: errors.New(msg), } @@ -94,7 +101,7 @@ const ( CodeAlreadyExistsError Code = 60 CodeGetExistsError Code = 61 CodeGetLatestHeightError Code = 62 - + // DEPRECATED Code = 63 CodeGetPauseHeightError Code = 64 CodeAlreadyPausedError Code = 65 CodeSetPauseHeightError Code = 66 @@ -111,8 +118,8 @@ const ( CodeEqualVotesError Code = 77 CodeUnequalRoundsError Code = 78 CodeMaxEvidenceAgeError Code = 79 - CodeGetStakedTokensError Code = 80 - CodeSetValidatorStakedTokensError Code = 81 + CodeGetStakedAmountError Code = 80 + CodeSetValidatorStakedAmountError Code = 81 CodeSetPoolAmountError Code = 82 CodeGetPoolAmountError Code = 83 CodeInvalidProposerCutPercentageError Code = 84 @@ -163,9 +170,11 @@ const ( CodeGetHeightError Code = 129 CodeUnknownActorType Code = 130 CodeUnknownMessageType Code = 131 +) - GetStakedTokensError = "an error occurred getting the validator staked tokens" - SetValidatorStakedTokensError = "an error occurred setting the validator staked tokens" +const ( + GetStakedAmountsError = "an error occurred getting the validator's amount staked" + SetValidatorStakedAmountError = "an error occurred setting the validator' amount staked" EqualVotesError = "the votes are identical and not equivocating" UnequalRoundsError = "the round numbers are not equal" UnequalVoteTypesError = "the vote types are not equal" @@ -258,7 +267,7 @@ const ( PayloadTooBigError = "socket error: payload size is too big. " SocketIOStartFailedError = "socket error: failed to start socket reading/writing (io)" EmptyTransactionError = "the transaction is empty" - StringToBigIntError = "an error occurred converting the string primitive to big.Int, the conversion was unsuccessful with base 10" + StringToBigIntError = "error converting string to big int" GetAllValidatorsError = "an error occurred getting all validators from the state" InvalidAmountError = "the amount field is invalid; cannot be converted to big.Int" InvalidAddressLenError = "the length of the address is not valid" @@ -353,11 +362,11 @@ func ErrGetMissedBlocks(err error) Error { } func ErrGetStakedTokens(err error) Error { - return NewError(CodeGetStakedTokensError, GetStakedTokensError) + return NewError(CodeGetStakedAmountError, GetStakedAmountsError) } -func ErrSetValidatorStakedTokens(err error) Error { - return NewError(CodeSetValidatorStakedTokensError, SetValidatorStakedTokensError) +func ErrSetValidatorStakedAmount(err error) Error { + return NewError(CodeSetValidatorStakedAmountError, SetValidatorStakedAmountError) } func ErrGetExists(err error) Error { @@ -436,7 +445,11 @@ func ErrNotReadyToUnpause() Error { return NewError(CodeNotReadyToUnpauseError, NotReadyToUnpauseError) } -func ErrInvalidStatus(got, expected int32) Error { +func ErrUnknownStatus(status int32) Error { + return NewError(CodeInvalidStatusError, fmt.Sprintf("%s: unknown status %d", InvalidStatusError, status)) +} + +func ErrInvalidStatus(got, expected StakeStatus) Error { return NewError(CodeInvalidStatusError, fmt.Sprintf("%s: %d expected %d", InvalidStatusError, got, expected)) } @@ -516,8 +529,8 @@ func ErrGetBlockHash(err error) Error { return NewError(CodeGetBlockHashError, fmt.Sprintf("%s: %s", GetBlockHashError, err.Error())) } -func ErrInvalidPublicKeyLen(err error) Error { - return NewError(CodeInvalidPublicKeyLenError, fmt.Sprintf("%s: %s", InvalidPublicKeyLenError, err.Error())) +func ErrInvalidPublicKeyLen(pubKeyLen int) Error { + return NewError(CodeInvalidPublicKeyLenError, fmt.Sprintf("%s: %s", InvalidPublicKeyLenError, cryptoPocket.ErrInvalidPublicKeyLen(pubKeyLen))) } func ErrInvalidNonce() Error { @@ -669,8 +682,8 @@ func ErrDuplicateTransaction() Error { return NewError(CodeDuplicateTransactionError, DuplicateTransactionError) } -func ErrStringToBigInt() Error { - return NewError(CodeStringToBigIntError, StringToBigIntError) +func ErrStringToBigInt(err error) Error { + return NewError(CodeStringToBigIntError, fmt.Sprintf("%s: %s", StringToBigIntError, err.Error())) } func ErrInsufficientAmount(address string) Error { @@ -757,8 +770,8 @@ func ErrInvalidTransactionCount() Error { return NewError(CodeInvalidTransactionCountError, InvalidTransactionCountError) } -func ErrInvalidHashLength(err error) Error { - return NewError(CodeInvalidHashLengthError, fmt.Sprintf("%s: %s", InvalidHashLengthError, err.Error())) +func ErrInvalidHashLength(hashLen int) Error { + return NewError(CodeInvalidHashLengthError, fmt.Sprintf("%s: %s", InvalidHashLengthError, cryptoPocket.ErrInvalidHashLen(hashLen))) } func ErrNilQuorumCertificate() Error { @@ -769,6 +782,7 @@ func ErrNewAddressFromBytes(err error) Error { return NewError(CodeNewAddressFromBytesError, fmt.Sprintf("%s: %s", NewAddressFromBytesError, err.Error())) } +// CONSIDERATION: Moving this into the `codec` library could reduce some code bloat func ErrProtoMarshal(err error) Error { return NewError(CodeProtoMarshalError, fmt.Sprintf("%s: %s", ProtoMarshalError, err.Error())) } diff --git a/utility/types/gov.go b/utility/types/gov.go index b1be4c89a..497cf39b1 100644 --- a/utility/types/gov.go +++ b/utility/types/gov.go @@ -1,16 +1,26 @@ package types +// IMPROVE: Rename `UnstakingBlocks` to `UnbondingPeriod` or `UnstakingBlocksUnbondingPeriod` +// IMPROVE: Create a mapping from ActorType to the gov params that are relevant to that actor type. + const ( + // Session gov params BlocksPerSessionParamName = "blocks_per_session" + // Application actor gov params AppMinimumStakeParamName = "app_minimum_stake" AppMaxChainsParamName = "app_max_chains" AppBaselineStakeRateParamName = "app_baseline_stake_rate" - AppStakingAdjustmentParamName = "app_staking_adjustment" AppUnstakingBlocksParamName = "app_unstaking_blocks" AppMinimumPauseBlocksParamName = "app_minimum_pause_blocks" AppMaxPauseBlocksParamName = "app_max_pause_blocks" + // The constant integer adjustment that the DAO may use to move the stake. The DAO may manually + // adjust an application's MaxRelays at the time of staking to correct for short-term fluctuations + // in the price of POKT, which may not be reflected in ParticipationRate + // When this parameter is set to 0, no adjustment is being made. + AppStakingAdjustmentParamName = "app_staking_adjustment" // IMPROVE: Document & explain the purpose of this parameter in more detail. + // Servicer actor gov params ServiceNodeMinimumStakeParamName = "service_node_minimum_stake" ServiceNodeMaxChainsParamName = "service_node_max_chains" ServiceNodeUnstakingBlocksParamName = "service_node_unstaking_blocks" @@ -18,27 +28,27 @@ const ( ServiceNodeMaxPauseBlocksParamName = "service_node_max_pause_blocks" ServiceNodesPerSessionParamName = "service_nodes_per_session" - FishermanMinimumStakeParamName = "fisherman_minimum_stake" - // DISCUSS(github.com/pokt-network/pocket-network-protocol/issues/18): Once the spec is updated, - // determine if fish should be chain based. + // Fisherman actor gov params + FishermanMinimumStakeParamName = "fisherman_minimum_stake" FishermanMaxChainsParamName = "fisherman_max_chains" FishermanUnstakingBlocksParamName = "fisherman_unstaking_blocks" FishermanMinimumPauseBlocksParamName = "fisherman_minimum_pause_blocks" FishermanMaxPauseBlocksParamName = "fisherman_max_pause_blocks" + // Validator actor gov params ValidatorMinimumStakeParamName = "validator_minimum_stake" ValidatorUnstakingBlocksParamName = "validator_unstaking_blocks" ValidatorMinimumPauseBlocksParamName = "validator_minimum_pause_blocks" ValidatorMaxPausedBlocksParamName = "validator_max_pause_blocks" ValidatorMaximumMissedBlocksParamName = "validator_maximum_missed_blocks" + // Validator (complex) actor gov params ValidatorMaxEvidenceAgeInBlocksParamName = "validator_max_evidence_age_in_blocks" ProposerPercentageOfFeesParamName = "proposer_percentage_of_fees" MissedBlocksBurnPercentageParamName = "missed_blocks_burn_percentage" DoubleSignBurnPercentageParamName = "double_sign_burn_percentage" - MessageDoubleSignFee = "message_double_sign_fee" - MessageSendFee = "message_send_fee" + // Pocket specific message gov params MessageStakeFishermanFee = "message_stake_fisherman_fee" MessageEditStakeFishermanFee = "message_edit_stake_fisherman_fee" MessageUnstakeFishermanFee = "message_unstake_fisherman_fee" @@ -47,54 +57,76 @@ const ( MessageFishermanPauseServiceNodeFee = "message_fisherman_pause_service_node_fee" MessageTestScoreFee = "message_test_score_fee" MessageProveTestScoreFee = "message_prove_test_score_fee" - MessageStakeAppFee = "message_stake_app_fee" - MessageEditStakeAppFee = "message_edit_stake_app_fee" - MessageUnstakeAppFee = "message_unstake_app_fee" - MessagePauseAppFee = "message_pause_app_fee" - MessageUnpauseAppFee = "message_unpause_app_fee" - MessageStakeValidatorFee = "message_stake_validator_fee" - MessageEditStakeValidatorFee = "message_edit_stake_validator_fee" - MessageUnstakeValidatorFee = "message_unstake_validator_fee" - MessagePauseValidatorFee = "message_pause_validator_fee" - MessageUnpauseValidatorFee = "message_unpause_validator_fee" - MessageStakeServiceNodeFee = "message_stake_service_node_fee" - MessageEditStakeServiceNodeFee = "message_edit_stake_service_node_fee" - MessageUnstakeServiceNodeFee = "message_unstake_service_node_fee" - MessagePauseServiceNodeFee = "message_pause_service_node_fee" - MessageUnpauseServiceNodeFee = "message_unpause_service_node_fee" - MessageChangeParameterFee = "message_change_parameter_fee" - - AclOwner = "acl_owner" - BlocksPerSessionOwner = "blocks_per_session_owner" - AppMinimumStakeOwner = "app_minimum_stake_owner" - AppMaxChainsOwner = "app_max_chains_owner" - AppBaselineStakeRateOwner = "app_baseline_stake_rate_owner" - AppStakingAdjustmentOwner = "app_staking_adjustment_owner" - AppUnstakingBlocksOwner = "app_unstaking_blocks_owner" - AppMinimumPauseBlocksOwner = "app_minimum_pause_blocks_owner" - AppMaxPausedBlocksOwner = "app_max_paused_blocks_owner" - ServiceNodeMinimumStakeOwner = "service_node_minimum_stake_owner" - ServiceNodeMaxChainsOwner = "service_node_max_chains_owner" - ServiceNodeUnstakingBlocksOwner = "service_node_unstaking_blocks_owner" - ServiceNodeMinimumPauseBlocksOwner = "service_node_minimum_pause_blocks_owner" - ServiceNodeMaxPausedBlocksOwner = "service_node_max_paused_blocks_owner" - ServiceNodesPerSessionOwner = "service_nodes_per_session_owner" - FishermanMinimumStakeOwner = "fisherman_minimum_stake_owner" - FishermanMaxChainsOwner = "fisherman_max_chains_owner" - FishermanUnstakingBlocksOwner = "fisherman_unstaking_blocks_owner" - FishermanMinimumPauseBlocksOwner = "fisherman_minimum_pause_blocks_owner" - FishermanMaxPausedBlocksOwner = "fisherman_max_paused_blocks_owner" - ValidatorMinimumStakeOwner = "validator_minimum_stake_owner" - ValidatorUnstakingBlocksOwner = "validator_unstaking_blocks_owner" - ValidatorMinimumPauseBlocksOwner = "validator_minimum_pause_blocks_owner" - ValidatorMaxPausedBlocksOwner = "validator_max_paused_blocks_owner" - ValidatorMaximumMissedBlocksOwner = "validator_maximum_missed_blocks_owner" - ValidatorMaxEvidenceAgeInBlocksOwner = "validator_max_evidence_age_in_blocks_owner" - ProposerPercentageOfFeesOwner = "proposer_percentage_of_fees_owner" - MissedBlocksBurnPercentageOwner = "missed_blocks_burn_percentage_owner" - DoubleSignBurnPercentageOwner = "double_sign_burn_percentage_owner" - MessageDoubleSignFeeOwner = "message_double_sign_fee_owner" - MessageSendFeeOwner = "message_send_fee_owner" + + // Proof-of-stake message gov params + MessageDoubleSignFee = "message_double_sign_fee" + MessageSendFee = "message_send_fee" + MessageStakeAppFee = "message_stake_app_fee" + MessageEditStakeAppFee = "message_edit_stake_app_fee" + MessageUnstakeAppFee = "message_unstake_app_fee" + MessagePauseAppFee = "message_pause_app_fee" + MessageUnpauseAppFee = "message_unpause_app_fee" + + // Validator message gov params + MessageStakeValidatorFee = "message_stake_validator_fee" + MessageEditStakeValidatorFee = "message_edit_stake_validator_fee" + MessageUnstakeValidatorFee = "message_unstake_validator_fee" + MessagePauseValidatorFee = "message_pause_validator_fee" + MessageUnpauseValidatorFee = "message_unpause_validator_fee" + + // Servicer message gov params + MessageStakeServiceNodeFee = "message_stake_service_node_fee" + MessageEditStakeServiceNodeFee = "message_edit_stake_service_node_fee" + MessageUnstakeServiceNodeFee = "message_unstake_service_node_fee" + MessagePauseServiceNodeFee = "message_pause_service_node_fee" + MessageUnpauseServiceNodeFee = "message_unpause_service_node_fee" + + // Parameter / flags gov params + MessageChangeParameterFee = "message_change_parameter_fee" +) + +// TECHDEBT: The parameters below are equivalent to the list above with the suffix `_owner`. There +// is likely a clean way to better organize this code. This will also involve finding +// discrepancies between the two lists from missing / duplicate types or owners. +const ( + AclOwner = "acl_owner" + + BlocksPerSessionOwner = "blocks_per_session_owner" + + AppMinimumStakeOwner = "app_minimum_stake_owner" + AppMaxChainsOwner = "app_max_chains_owner" + AppBaselineStakeRateOwner = "app_baseline_stake_rate_owner" + AppStakingAdjustmentOwner = "app_staking_adjustment_owner" + AppUnstakingBlocksOwner = "app_unstaking_blocks_owner" + AppMinimumPauseBlocksOwner = "app_minimum_pause_blocks_owner" + AppMaxPausedBlocksOwner = "app_max_paused_blocks_owner" + + ServiceNodeMinimumStakeOwner = "service_node_minimum_stake_owner" + ServiceNodeMaxChainsOwner = "service_node_max_chains_owner" + ServiceNodeUnstakingBlocksOwner = "service_node_unstaking_blocks_owner" + ServiceNodeMinimumPauseBlocksOwner = "service_node_minimum_pause_blocks_owner" + ServiceNodeMaxPausedBlocksOwner = "service_node_max_paused_blocks_owner" + ServiceNodesPerSessionOwner = "service_nodes_per_session_owner" + + FishermanMinimumStakeOwner = "fisherman_minimum_stake_owner" + FishermanMaxChainsOwner = "fisherman_max_chains_owner" + FishermanUnstakingBlocksOwner = "fisherman_unstaking_blocks_owner" + FishermanMinimumPauseBlocksOwner = "fisherman_minimum_pause_blocks_owner" + FishermanMaxPausedBlocksOwner = "fisherman_max_paused_blocks_owner" + + ValidatorMinimumStakeOwner = "validator_minimum_stake_owner" + ValidatorUnstakingBlocksOwner = "validator_unstaking_blocks_owner" + ValidatorMinimumPauseBlocksOwner = "validator_minimum_pause_blocks_owner" + ValidatorMaxPausedBlocksOwner = "validator_max_paused_blocks_owner" + ValidatorMaximumMissedBlocksOwner = "validator_maximum_missed_blocks_owner" + ValidatorMaxEvidenceAgeInBlocksOwner = "validator_max_evidence_age_in_blocks_owner" + + ProposerPercentageOfFeesOwner = "proposer_percentage_of_fees_owner" + MissedBlocksBurnPercentageOwner = "missed_blocks_burn_percentage_owner" + DoubleSignBurnPercentageOwner = "double_sign_burn_percentage_owner" + MessageDoubleSignFeeOwner = "message_double_sign_fee_owner" + MessageSendFeeOwner = "message_send_fee_owner" + MessageStakeFishermanFeeOwner = "message_stake_fisherman_fee_owner" MessageEditStakeFishermanFeeOwner = "message_edit_stake_fisherman_fee_owner" MessageUnstakeFishermanFeeOwner = "message_unstake_fisherman_fee_owner" @@ -118,5 +150,6 @@ const ( MessageUnstakeServiceNodeFeeOwner = "message_unstake_service_node_fee_owner" MessagePauseServiceNodeFeeOwner = "message_pause_service_node_fee_owner" MessageUnpauseServiceNodeFeeOwner = "message_unpause_service_node_fee_owner" - MessageChangeParameterFeeOwner = "message_change_parameter_fee_owner" + + MessageChangeParameterFeeOwner = "message_change_parameter_fee_owner" ) diff --git a/utility/types/message.go b/utility/types/message.go index 0a7c3779f..b1feb3e7e 100644 --- a/utility/types/message.go +++ b/utility/types/message.go @@ -1,12 +1,8 @@ package types import ( - "bytes" "encoding/hex" "log" - "net/url" - "strconv" - "strings" "github.com/pokt-network/pocket/shared/codec" coreTypes "github.com/pokt-network/pocket/shared/core/types" @@ -14,40 +10,18 @@ import ( "google.golang.org/protobuf/proto" ) -/* -`message.go`` contains `ValidateBasic` and `SetSigner`` logic for all message types. - -`ValidateBasic` is a **stateless** validation check that should encapsulate all -validations possible before even checking the state storage layer. -*/ - -// CLEANUP: Move these to a better shared location or inline the vars. -const ( - MillionInt = 1000000 - ZeroInt = 0 - HeightNotUsed = int64(-1) - EmptyString = "" - HttpsPrefix = "https://" - HttpPrefix = "http://" - Colon = ":" - Period = "." - InvalidURLPrefix = "the url must start with http:// or https://" - PortRequired = "a port is required" - NonNumberPort = "invalid port, cant convert to integer" - PortOutOfRange = "invalid port, out of valid port range" - NoPeriod = "must contain one '.'" - MaxPort = 65535 -) +// A message is a component of a transaction (excluding metadata such as the signature) +// defining the action driving the state transition type Message interface { - proto.Message + proto.Message // TECHDEBT: Still making direct `proto` reference even with a central `codec` package + Validatable - SetSigner(signer []byte) - ValidateBasic() Error - GetCanonicalBytes() []byte - GetActorType() coreTypes.ActorType GetMessageName() string GetMessageRecipient() string + SetSigner(signer []byte) + GetActorType() coreTypes.ActorType + GetCanonicalBytes() []byte } var ( @@ -57,68 +31,41 @@ var ( _ Message = &MessageUnstake{} _ Message = &MessageUnpause{} _ Message = &MessageChangeParameter{} - _ Message = &MessageDoubleSign{} ) -func (msg *MessageSend) GetActorType() coreTypes.ActorType { - return coreTypes.ActorType_ACTOR_TYPE_UNSPECIFIED // there's no actor type for message send, so return zero to allow fee retrieval -} - -func (msg *MessageStake) ValidateBasic() Error { - if err := ValidatePublicKey(msg.GetPublicKey()); err != nil { - return err - } - if err := ValidateOutputAddress(msg.GetOutputAddress()); err != nil { - return err - } - return ValidateStaker(msg) -} - -func (msg *MessageEditStake) ValidateBasic() Error { - if err := ValidateAddress(msg.GetAddress()); err != nil { +func (msg *MessageSend) ValidateBasic() Error { + if err := validateAddress(msg.FromAddress); err != nil { return err } - return ValidateStaker(msg) -} - -func (msg *MessageDoubleSign) ValidateBasic() Error { - if err := msg.VoteA.ValidateBasic(); err != nil { + if err := validateAddress(msg.ToAddress); err != nil { return err } - if err := msg.VoteB.ValidateBasic(); err != nil { + if err := validateAmount(msg.Amount); err != nil { return err } - if !bytes.Equal(msg.VoteA.PublicKey, msg.VoteB.PublicKey) { - return ErrUnequalPublicKeys() - } - if msg.VoteA.Type != msg.VoteB.Type { - return ErrUnequalVoteTypes() - } - if msg.VoteA.Height != msg.VoteB.Height { - return ErrUnequalHeights() - } - if msg.VoteA.Round != msg.VoteB.Round { - return ErrUnequalRounds() - } - if bytes.Equal(msg.VoteA.BlockHash, msg.VoteB.BlockHash) { - return ErrEqualVotes() - } return nil } - -func (msg *MessageSend) ValidateBasic() Error { - if err := ValidateAddress(msg.FromAddress); err != nil { +func (msg *MessageStake) ValidateBasic() Error { + if err := validatePublicKey(msg.PublicKey); err != nil { return err } - if err := ValidateAddress(msg.ToAddress); err != nil { + if err := validateOutputAddress(msg.OutputAddress); err != nil { return err } - if err := ValidateAmount(msg.Amount); err != nil { + return validateStaker(msg) +} +func (msg *MessageUnstake) ValidateBasic() Error { + return validateAddress(msg.Address) +} +func (msg *MessageUnpause) ValidateBasic() Error { + return validateAddress(msg.Address) +} +func (msg *MessageEditStake) ValidateBasic() Error { + if err := validateAddress(msg.Address); err != nil { return err } - return nil + return validateStaker(msg) } - func (msg *MessageChangeParameter) ValidateBasic() Error { if msg.ParameterKey == "" { return ErrEmptyParamKey() @@ -126,52 +73,52 @@ func (msg *MessageChangeParameter) ValidateBasic() Error { if msg.ParameterValue == nil { return ErrEmptyParamValue() } - if err := ValidateAddress(msg.Owner); err != nil { + if err := validateAddress(msg.Owner); err != nil { return err } return nil } func (msg *MessageSend) GetMessageName() string { return getMessageType(msg) } +func (msg *MessageStake) GetMessageName() string { return getMessageType(msg) } +func (msg *MessageEditStake) GetMessageName() string { return getMessageType(msg) } func (msg *MessageUnstake) GetMessageName() string { return getMessageType(msg) } func (msg *MessageUnpause) GetMessageName() string { return getMessageType(msg) } -func (msg *MessageEditStake) GetMessageName() string { return getMessageType(msg) } -func (msg *MessageStake) GetMessageName() string { return getMessageType(msg) } func (msg *MessageChangeParameter) GetMessageName() string { return getMessageType(msg) } -func (msg *MessageDoubleSign) GetMessageName() string { return getMessageType(msg) } func (msg *MessageSend) GetMessageRecipient() string { return hex.EncodeToString(msg.ToAddress) } +func (msg *MessageStake) GetMessageRecipient() string { return "" } +func (msg *MessageEditStake) GetMessageRecipient() string { return "" } func (msg *MessageUnstake) GetMessageRecipient() string { return "" } func (msg *MessageUnpause) GetMessageRecipient() string { return "" } -func (msg *MessageEditStake) GetMessageRecipient() string { return "" } -func (msg *MessageStake) GetMessageRecipient() string { return "" } func (msg *MessageChangeParameter) GetMessageRecipient() string { return "" } -func (msg *MessageDoubleSign) GetMessageRecipient() string { return "" } -func (msg *MessageUnstake) ValidateBasic() Error { return ValidateAddress(msg.Address) } -func (msg *MessageUnpause) ValidateBasic() Error { return ValidateAddress(msg.Address) } +func (msg *MessageSend) SetSigner(signer []byte) { /*no op*/ } +func (msg *MessageStake) SetSigner(signer []byte) { msg.Signer = signer } +func (msg *MessageEditStake) SetSigner(signer []byte) { msg.Signer = signer } +func (msg *MessageUnstake) SetSigner(signer []byte) { msg.Signer = signer } +func (msg *MessageUnpause) SetSigner(signer []byte) { msg.Signer = signer } +func (msg *MessageChangeParameter) SetSigner(signer []byte) { msg.Signer = signer } -func (msg *MessageStake) SetSigner(signer []byte) { msg.Signer = signer } -func (msg *MessageEditStake) SetSigner(signer []byte) { msg.Signer = signer } -func (msg *MessageUnstake) SetSigner(signer []byte) { msg.Signer = signer } -func (msg *MessageUnpause) SetSigner(signer []byte) { msg.Signer = signer } -func (msg *MessageDoubleSign) SetSigner(signer []byte) { msg.ReporterAddress = signer } -func (msg *MessageSend) SetSigner(signer []byte) { /*no op*/ } -func (msg *MessageChangeParameter) SetSigner(signer []byte) { msg.Signer = signer } -func (x *MessageChangeParameter) GetActorType() coreTypes.ActorType { return -1 } -func (x *MessageDoubleSign) GetActorType() coreTypes.ActorType { return -1 } +func (msg *MessageSend) GetActorType() coreTypes.ActorType { + return coreTypes.ActorType_ACTOR_TYPE_UNSPECIFIED // there's no actor type for message send, so return zero to allow fee retrieval +} +func (msg *MessageChangeParameter) GetActorType() coreTypes.ActorType { + return -1 // CONSIDERATION: Should we create an actor for the DAO or ACLed addresses? +} +func (msg *MessageSend) GetCanonicalBytes() []byte { return getCanonicalBytes(msg) } func (msg *MessageStake) GetCanonicalBytes() []byte { return getCanonicalBytes(msg) } func (msg *MessageEditStake) GetCanonicalBytes() []byte { return getCanonicalBytes(msg) } -func (msg *MessageDoubleSign) GetCanonicalBytes() []byte { return getCanonicalBytes(msg) } -func (msg *MessageSend) GetCanonicalBytes() []byte { return getCanonicalBytes(msg) } -func (msg *MessageChangeParameter) GetCanonicalBytes() []byte { return getCanonicalBytes(msg) } func (msg *MessageUnstake) GetCanonicalBytes() []byte { return getCanonicalBytes(msg) } func (msg *MessageUnpause) GetCanonicalBytes() []byte { return getCanonicalBytes(msg) } +func (msg *MessageChangeParameter) GetCanonicalBytes() []byte { return getCanonicalBytes(msg) } -// helpers +// Helpers -func ValidateAddress(address []byte) Error { +// CONSIDERATION: If the protobufs contain semantic types (e.g. Address is an interface), we could +// potentially leverage a shared `address.ValidateBasic()` throughout the codebase. +func validateAddress(address []byte) Error { if address == nil { return ErrEmptyAddress() } @@ -182,7 +129,8 @@ func ValidateAddress(address []byte) Error { return nil } -func ValidateOutputAddress(address []byte) Error { +// CONSIDERATION: Consolidate with `validateAddress`? The only difference is the error message. +func validateOutputAddress(address []byte) Error { if address == nil { return ErrNilOutputAddress() } @@ -193,135 +141,51 @@ func ValidateOutputAddress(address []byte) Error { return nil } -func ValidatePublicKey(publicKey []byte) Error { +// CONSIDERATION: If the protobufs contain semantic types, we could potentially leverage +// a shared `address.ValidateBasic()` throughout the codebase.s +func validatePublicKey(publicKey []byte) Error { if publicKey == nil { return ErrEmptyPublicKey() } pubKeyLen := len(publicKey) if pubKeyLen != cryptoPocket.PublicKeyLen { - return ErrInvalidPublicKeyLen(cryptoPocket.ErrInvalidPublicKeyLen(pubKeyLen)) + return ErrInvalidPublicKeyLen(pubKeyLen) } return nil } -func ValidateHash(hash []byte) Error { +//nolint:unused // TODO: need to figure out why this function was added and never used +func validateHash(hash []byte) Error { if hash == nil { return ErrEmptyHash() } hashLen := len(hash) if hashLen != cryptoPocket.SHA3HashLen { - return ErrInvalidHashLength(cryptoPocket.ErrInvalidHashLen(hashLen)) + return ErrInvalidHashLength(hashLen) } return nil } -func ValidateRelayChains(chains []string) Error { +func validateRelayChains(chains []string) Error { if chains == nil { return ErrEmptyRelayChains() } for _, chain := range chains { - relayChain := RelayChain(chain) - if err := relayChain.Validate(); err != nil { + if err := relayChain(chain).ValidateBasic(); err != nil { return err } } return nil } -func ValidateAmount(amount string) Error { - if amount == "" { - return ErrEmptyAmount() - } - if _, err := StringToBigInt(amount); err != nil { - return err - } - return nil -} - -func ValidateActorType(_ coreTypes.ActorType) Error { - // TODO (team) not sure if there's anything we can do here - return nil -} - -func ValidateServiceUrl(actorType coreTypes.ActorType, uri string) Error { - if actorType == coreTypes.ActorType_ACTOR_TYPE_APP { - return nil - } - uri = strings.ToLower(uri) - _, err := url.ParseRequestURI(uri) - if err != nil { - return ErrInvalidServiceUrl(err.Error()) - } - if !(uri[:8] == HttpsPrefix || uri[:7] == HttpPrefix) { - return ErrInvalidServiceUrl(InvalidURLPrefix) - } - temp := strings.Split(uri, Colon) - if len(temp) != 3 { - return ErrInvalidServiceUrl(PortRequired) - } - port, err := strconv.Atoi(temp[2]) - if err != nil { - return ErrInvalidServiceUrl(NonNumberPort) - } - if port > MaxPort || port < 0 { - return ErrInvalidServiceUrl(PortOutOfRange) - } - if !strings.Contains(uri, Period) { - return ErrInvalidServiceUrl(NoPeriod) - } - return nil -} - func getMessageType(msg Message) string { return string(msg.ProtoReflect().Descriptor().Name()) } -// CLEANUP: Figure out where these other types should be defined. -// It's a bit weird that they are hidden at the bottom of the file. - -const ( - RelayChainLength = 4 // pre-determined length that strikes a balance between combination possibilities & storage -) - -type RelayChain string - -// TODO: Consider adding a governance parameter for a list of valid relay chains -func (rc *RelayChain) Validate() Error { - if rc == nil || *rc == "" { - return ErrEmptyRelayChain() - } - rcLen := len(*rc) - if rcLen != RelayChainLength { - return ErrInvalidRelayChainLength(rcLen, RelayChainLength) - } - return nil -} - -type MessageStaker interface { - GetActorType() coreTypes.ActorType - GetAmount() string - GetChains() []string - GetServiceUrl() string -} - -func ValidateStaker(msg MessageStaker) Error { - if err := ValidateActorType(msg.GetActorType()); err != nil { - return err - } - if err := ValidateAmount(msg.GetAmount()); err != nil { - return err - } - if err := ValidateRelayChains(msg.GetChains()); err != nil { - return err - } - return ValidateServiceUrl(msg.GetActorType(), msg.GetServiceUrl()) -} - func getCanonicalBytes(msg Message) []byte { bz, err := codec.GetCodec().Marshal(msg) if err != nil { log.Fatalf("must marshal %v", err) } - // DISCUSS(#142): should we also sort the JSON like in V0? - return bz + return bz // DISCUSS(#142): should we also sort the JSON like in V0? } diff --git a/utility/types/message_staking.go b/utility/types/message_staking.go new file mode 100644 index 000000000..4d85f48b1 --- /dev/null +++ b/utility/types/message_staking.go @@ -0,0 +1,96 @@ +package types + +import ( + "net/url" + "strconv" + "strings" + + "github.com/pokt-network/pocket/shared/converters" + coreTypes "github.com/pokt-network/pocket/shared/core/types" +) + +// This file captures basic logic common across all the actors that need to stake regardless of their responsibility. + +// CLEANUP: Cleanup these strings. Either move them to a shared location or use them in place, but having +// them as constants in this file only feels very incorrect. +const ( + httpsPrefix = "https://" + httpPrefix = "http://" + colon = ":" + period = "." + invalidURLPrefix = "the url must start with http:// or https://" + portRequired = "a port is required" + nonNumberPort = "invalid port, cant convert to integer" + portOutOfRange = "invalid port, out of valid port range" + noPeriod = "must contain one '.'" + maxPort = 65535 +) + +// This interface is useful in validating stake related messages and is not intended to be used outside of this package +type stakingMessage interface { + GetActorType() coreTypes.ActorType + GetAmount() string + GetChains() []string + GetServiceUrl() string +} + +func validateStaker(msg stakingMessage) Error { + if err := validateActorType(msg.GetActorType()); err != nil { + return err + } + if err := validateAmount(msg.GetAmount()); err != nil { + return err + } + if err := validateRelayChains(msg.GetChains()); err != nil { + return err + } + return validateServiceUrl(msg.GetActorType(), msg.GetServiceUrl()) +} + +func validateActorType(actorType coreTypes.ActorType) Error { + if actorType == coreTypes.ActorType_ACTOR_TYPE_UNSPECIFIED { + return ErrUnknownActorType(string(actorType)) + } + return nil +} + +func validateAmount(amount string) Error { + if amount == "" { + return ErrEmptyAmount() + } + if _, err := converters.StringToBigInt(amount); err != nil { + return ErrStringToBigInt(err) + } + return nil +} + +func validateServiceUrl(actorType coreTypes.ActorType, uri string) Error { + if actorType == coreTypes.ActorType_ACTOR_TYPE_APP { + return nil + } + + uri = strings.ToLower(uri) + _, err := url.ParseRequestURI(uri) + if err != nil { + return ErrInvalidServiceUrl(err.Error()) + } + if !(uri[:8] == httpsPrefix || uri[:7] == httpPrefix) { + return ErrInvalidServiceUrl(invalidURLPrefix) + } + + urlParts := strings.Split(uri, colon) + if len(urlParts) != 3 { // protocol:host:port + return ErrInvalidServiceUrl(portRequired) + } + port, err := strconv.Atoi(urlParts[2]) + if err != nil { + return ErrInvalidServiceUrl(nonNumberPort) + } + if port > maxPort || port < 0 { + return ErrInvalidServiceUrl(portOutOfRange) + } + if !strings.Contains(uri, period) { + return ErrInvalidServiceUrl(noPeriod) + } + return nil +} diff --git a/utility/types/message_test.go b/utility/types/message_test.go index 5dfdbeb29..585e4f36f 100644 --- a/utility/types/message_test.go +++ b/utility/types/message_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/pokt-network/pocket/shared/codec" + "github.com/pokt-network/pocket/shared/converters" coreTypes "github.com/pokt-network/pocket/shared/core/types" "github.com/pokt-network/pocket/shared/crypto" "github.com/stretchr/testify/require" @@ -15,7 +16,7 @@ import ( var ( defaultTestingChains = []string{"0001"} defaultAmountBig = big.NewInt(1000000) - defaultAmount = BigIntToString(defaultAmountBig) + defaultAmount = converters.BigIntToString(defaultAmountBig) defaultUnusedLength = -1 ) @@ -50,66 +51,6 @@ func TestMessage_ChangeParameter_ValidateBasic(t *testing.T) { require.Equal(t, ErrEmptyParamValue().Code(), msgMissingParamValue.ValidateBasic().Code()) } -func TestMessage_DoubleSign_ValidateBasic(t *testing.T) { - pk, err := crypto.GeneratePublicKey() - require.NoError(t, err) - - hashA := crypto.SHA3Hash(pk.Bytes()) - hashB := crypto.SHA3Hash(pk.Address()) - voteA := &LegacyVote{ - PublicKey: pk.Bytes(), - Height: 1, - Round: 2, - Type: DoubleSignEvidenceType, - BlockHash: hashA, - } - voteB := &LegacyVote{ - PublicKey: pk.Bytes(), - Height: 1, - Round: 2, - Type: DoubleSignEvidenceType, - BlockHash: hashB, - } - reporter, _ := crypto.GenerateAddress() - msg := &MessageDoubleSign{ - VoteA: voteA, - VoteB: voteB, - ReporterAddress: reporter, - } - er := msg.ValidateBasic() - require.NoError(t, er) - - pk2, err := crypto.GeneratePublicKey() - require.NoError(t, err) - msgUnequalPubKeys := new(MessageDoubleSign) - msgUnequalPubKeys.VoteA = proto.Clone(msg.VoteA).(*LegacyVote) - msgUnequalPubKeys.VoteB = proto.Clone(msg.VoteB).(*LegacyVote) - msgUnequalPubKeys.VoteA.PublicKey = pk2.Bytes() - er = msgUnequalPubKeys.ValidateBasic() - require.Equal(t, ErrUnequalPublicKeys().Code(), er.Code()) - - msgUnequalHeights := new(MessageDoubleSign) - msgUnequalHeights.VoteA = proto.Clone(msg.VoteA).(*LegacyVote) - msgUnequalHeights.VoteB = proto.Clone(msg.VoteB).(*LegacyVote) - msgUnequalHeights.VoteA.Height = 2 - er = msgUnequalHeights.ValidateBasic() - require.Equal(t, ErrUnequalHeights().Code(), er.Code()) - - msgUnequalRounds := new(MessageDoubleSign) - msgUnequalRounds.VoteA = proto.Clone(msg.VoteA).(*LegacyVote) - msgUnequalRounds.VoteB = proto.Clone(msg.VoteB).(*LegacyVote) - msgUnequalRounds.VoteA.Round = 1 - er = msgUnequalRounds.ValidateBasic() - require.Equal(t, ErrUnequalRounds().Code(), er.Code()) - - msgEqualVoteHash := new(MessageDoubleSign) - msgEqualVoteHash.VoteA = proto.Clone(msg.VoteA).(*LegacyVote) - msgEqualVoteHash.VoteB = proto.Clone(msg.VoteB).(*LegacyVote) - msgEqualVoteHash.VoteB.BlockHash = hashA - er = msgEqualVoteHash.ValidateBasic() - require.Equal(t, ErrEqualVotes().Code(), er.Code()) -} - func TestMessage_EditStake_ValidateBasic(t *testing.T) { addr, err := crypto.GenerateAddress() require.NoError(t, err) @@ -131,7 +72,7 @@ func TestMessage_EditStake_ValidateBasic(t *testing.T) { msgInvalidAmount := proto.Clone(&msg).(*MessageEditStake) msgInvalidAmount.Amount = "sdk" er = msgInvalidAmount.ValidateBasic() - require.Equal(t, ErrStringToBigInt().Code(), er.Code()) + require.Equal(t, ErrStringToBigInt(er).Code(), er.Code()) msgEmptyAddress := proto.Clone(&msg).(*MessageEditStake) msgEmptyAddress.Address = nil @@ -152,11 +93,11 @@ func TestMessage_EditStake_ValidateBasic(t *testing.T) { msgInvalidRelayChains := proto.Clone(&msg).(*MessageEditStake) msgInvalidRelayChains.Chains = []string{"notAValidRelayChain"} er = msgInvalidRelayChains.ValidateBasic() - expectedErr = ErrInvalidRelayChainLength(0, RelayChainLength) + expectedErr = ErrInvalidRelayChainLength(0, relayChainLength) require.Equal(t, expectedErr.Code(), er.Code()) } -func TestMessageSend_ValidateBasic(t *testing.T) { +func TestMessage_Send_ValidateBasic(t *testing.T) { addr1, err := crypto.GenerateAddress() require.NoError(t, err) @@ -192,7 +133,7 @@ func TestMessageSend_ValidateBasic(t *testing.T) { require.Equal(t, ErrEmptyAmount().Code(), er.Code()) } -func TestMessageStake_ValidateBasic(t *testing.T) { +func TestMessage_Stake_ValidateBasic(t *testing.T) { pk, err := crypto.GeneratePublicKey() require.NoError(t, err) @@ -228,7 +169,7 @@ func TestMessageStake_ValidateBasic(t *testing.T) { require.Equal(t, ErrNilOutputAddress().Code(), er.Code()) } -func TestMessageUnstake_ValidateBasic(t *testing.T) { +func TestMessage_Unstake_ValidateBasic(t *testing.T) { addr, err := crypto.GenerateAddress() require.NoError(t, err) @@ -244,7 +185,7 @@ func TestMessageUnstake_ValidateBasic(t *testing.T) { require.Equal(t, ErrEmptyAddress().Code(), er.Code()) } -func TestMessageUnpause_ValidateBasic(t *testing.T) { +func TestMessage_Unpause_ValidateBasic(t *testing.T) { addr, err := crypto.GenerateAddress() require.NoError(t, err) @@ -259,19 +200,3 @@ func TestMessageUnpause_ValidateBasic(t *testing.T) { er = msgMissingAddress.ValidateBasic() require.Equal(t, ErrEmptyAddress().Code(), er.Code()) } - -func TestRelayChain_Validate(t *testing.T) { - relayChainValid := RelayChain("0001") - err := relayChainValid.Validate() - require.NoError(t, err) - - relayChainInvalidLength := RelayChain("001") - expectedError := ErrInvalidRelayChainLength(0, RelayChainLength) - err = relayChainInvalidLength.Validate() - require.Equal(t, expectedError.Code(), err.Code()) - - relayChainEmpty := RelayChain("") - expectedError = ErrEmptyRelayChain() - err = relayChainEmpty.Validate() - require.Equal(t, expectedError.Code(), err.Code()) -} diff --git a/utility/types/proto/message.proto b/utility/types/proto/message.proto index d61529807..af27b92b0 100644 --- a/utility/types/proto/message.proto +++ b/utility/types/proto/message.proto @@ -1,4 +1,5 @@ syntax = "proto3"; + package utility; option go_package = "github.com/pokt-network/pocket/utility/types"; @@ -6,12 +7,13 @@ option go_package = "github.com/pokt-network/pocket/utility/types"; import "google/protobuf/any.proto"; import "core/types/proto/actor.proto"; +// Send funds from one address to another message MessageSend { bytes from_address = 1; bytes to_address = 2; string amount = 3; } - +// Stake on behalf of a protocol actor message MessageStake { core.ActorType actor_type = 1; bytes public_key = 2; @@ -49,18 +51,3 @@ message MessageChangeParameter { string parameter_key = 3; google.protobuf.Any parameter_value = 4; } - -message MessageDoubleSign { - utility.LegacyVote vote_a = 1; - utility.LegacyVote vote_b = 2; - optional bytes reporter_address = 3; -} - -// TECHDEBT: Consolidate this with consensus -message LegacyVote { - bytes public_key = 1; - int64 height = 2; - uint32 round = 3; - uint32 type = 4; - bytes block_hash = 5; -} \ No newline at end of file diff --git a/utility/types/proto/stake_status.proto b/utility/types/proto/stake_status.proto index 4ed646fd4..fb6fc40af 100644 --- a/utility/types/proto/stake_status.proto +++ b/utility/types/proto/stake_status.proto @@ -4,10 +4,10 @@ package utility; option go_package = "github.com/pokt-network/pocket/utility/types"; -// DISCUSS: *Design Decision* deprecating StakeStatus -// REFACTOR(#258): Rename the protobuf types to follow best practices +// REFACTOR(#258): Consolidate with persistence and rename the protobuf types to follow best practices. enum StakeStatus { UnknownStatus = 0; Unstaking = 1; Staked = 2; + Unstaked = 3; } diff --git a/utility/types/proto/transaction.proto b/utility/types/proto/transaction.proto index 082c02516..03dcd9fde 100644 --- a/utility/types/proto/transaction.proto +++ b/utility/types/proto/transaction.proto @@ -1,27 +1,20 @@ syntax = "proto3"; + package utility; option go_package = "github.com/pokt-network/pocket/utility/types"; import "google/protobuf/any.proto"; -// TECHDEBT: Consolidate this with consensus +// `Transaction` is used to name the type for clarity & verbosity, but `tx` is used method signatures +// and variable names to be concise. https://github.com/pokt-network/pocket/pull/503 message Transaction { - google.protobuf.Any msg = 1; - Signature signature = 2; + google.protobuf.Any msg = 1; // CONSOLIDATE: Should be a oneof {} message + Signature signature = 2; // CONSOLIDATE: should use a shared crypto package type string nonce = 3; } -message TransactionResult { - uint32 code = 1; - bytes signer = 2; - bytes recipient = 3; - string message_type = 4; - int64 height = 5; - uint32 index = 6; - Transaction transaction = 7; -} - +// REFACTOR: Consolidate with other signature types throughout the codebase message Signature { bytes public_key = 1; bytes signature = 2; diff --git a/utility/types/proto/tx_result.proto b/utility/types/proto/tx_result.proto index 915afb5e1..b0c5b08a2 100644 --- a/utility/types/proto/tx_result.proto +++ b/utility/types/proto/tx_result.proto @@ -1,8 +1,24 @@ syntax = "proto3"; -package shared; + +package utility; option go_package = "github.com/pokt-network/pocket/utility/types"; +import "transaction.proto"; + +// INVESTIGATE: Look into a way of removing this type altogether or from shared interfaces. + +message TxResult { + uint32 code = 1; + bytes signer = 2; + bytes recipient = 3; + string message_type = 4; + int64 height = 5; + uint32 index = 6; + utility.Transaction tx = 7; +} + +// TECHDEBT: Re-evaluate the need for this type altogether message DefaultTxResult { bytes tx = 1; // The bytes of the indexed transaction int64 height = 2; // The block height at which the transaction was included diff --git a/utility/types/proto/util_module.proto b/utility/types/proto/util_module.proto deleted file mode 100644 index 68ed4afc8..000000000 --- a/utility/types/proto/util_module.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto3"; -package utility; - -option go_package = "github.com/pokt-network/pocket/utility/types"; - -message TransactionGossipMessage { - bytes tx = 1; -} \ No newline at end of file diff --git a/utility/types/proto/utility_messages.proto b/utility/types/proto/utility_messages.proto new file mode 100644 index 000000000..e93c94998 --- /dev/null +++ b/utility/types/proto/utility_messages.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package utility; + +option go_package = "github.com/pokt-network/pocket/utility/types"; + +// Messages that are sent between nodes (e.g. Evidence) for node specific business logic but are +// not intended to be stored on the blockchain or drive state transitions. + +message TransactionGossipMessage { + bytes tx = 1; +} \ No newline at end of file diff --git a/utility/types/relay_chain.go b/utility/types/relay_chain.go new file mode 100644 index 000000000..899e9b3ca --- /dev/null +++ b/utility/types/relay_chain.go @@ -0,0 +1,23 @@ +package types + +const ( + // DISCUSS: Should this be a governance parameter or moved to a shared file? + relayChainLength = 4 // pre-determined length that strikes a balance between combination possibilities & storage +) + +type relayChain string + +// No need for a relayChain interface abstraction for the time being +var _ Validatable = relayChain("") + +// TODO: Consider adding a governance parameter for a list of valid relay chains +func (rc relayChain) ValidateBasic() Error { + if rc == "" { + return ErrEmptyRelayChain() + } + rcLen := len(rc) + if rcLen != relayChainLength { + return ErrInvalidRelayChainLength(rcLen, relayChainLength) + } + return nil +} diff --git a/utility/types/relay_chain_test.go b/utility/types/relay_chain_test.go new file mode 100644 index 000000000..125ba9ba8 --- /dev/null +++ b/utility/types/relay_chain_test.go @@ -0,0 +1,23 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func Test_RelayChain_Validate(t *testing.T) { + relayChainValid := relayChain("0001") + err := relayChainValid.ValidateBasic() + require.NoError(t, err) + + relayChainInvalidLength := relayChain("001") + expectedError := ErrInvalidRelayChainLength(0, relayChainLength) + err = relayChainInvalidLength.ValidateBasic() + require.Equal(t, expectedError.Code(), err.Code()) + + relayChainEmpty := relayChain("") + expectedError = ErrEmptyRelayChain() + err = relayChainEmpty.ValidateBasic() + require.Equal(t, expectedError.Code(), err.Code()) +} diff --git a/utility/types/signature.go b/utility/types/signature.go new file mode 100644 index 000000000..dde49d2e1 --- /dev/null +++ b/utility/types/signature.go @@ -0,0 +1,14 @@ +package types + +// No need for a Signature interface abstraction for the time being +var _ Validatable = &Signature{} + +func (s *Signature) ValidateBasic() Error { + if s.Signature == nil { + return ErrEmptySignature() + } + if s.PublicKey == nil { + return ErrEmptyPublicKey() + } + return nil +} diff --git a/utility/types/transaction.go b/utility/types/transaction.go index c7ed4fa2b..f80601043 100644 --- a/utility/types/transaction.go +++ b/utility/types/transaction.go @@ -5,52 +5,76 @@ import ( "github.com/pokt-network/pocket/shared/codec" "github.com/pokt-network/pocket/shared/crypto" - "github.com/pokt-network/pocket/shared/modules" - "google.golang.org/protobuf/proto" ) -func TransactionFromBytes(transaction []byte) (*Transaction, Error) { +func TxFromBytes(txProtoBytes []byte) (*Transaction, Error) { tx := &Transaction{} - if err := codec.GetCodec().Unmarshal(transaction, tx); err != nil { + if err := codec.GetCodec().Unmarshal(txProtoBytes, tx); err != nil { return nil, ErrUnmarshalTransaction(err) } return tx, nil } +func TxHash(txProtoBytes []byte) string { + return crypto.GetHashStringFromBytes(txProtoBytes) +} + +var ( + _ Validatable = &Transaction{} + _ ITransaction = &Transaction{} +) + +// `ITransaction` is an interface that helps capture the functions added to the `Transaction` data structure. +// It is unlikely for there to be multiple implementations of this interface in prod. +type ITransaction interface { + GetMessage() (Message, Error) + Sign(privateKey crypto.PrivateKey) Error + Hash() (string, Error) + SignableBytes() ([]byte, error) + Bytes() ([]byte, error) + Equals(tx2 ITransaction) bool +} + func (tx *Transaction) ValidateBasic() Error { + // Nonce cannot be empty to avoid transaction replays if tx.Nonce == "" { return ErrEmptyNonce() } - if _, err := codec.GetCodec().FromAny(tx.Msg); err != nil { - return ErrProtoFromAny(err) - } - if tx.Signature == nil || tx.Signature.Signature == nil { + + // Is there a signature we can verify? + if tx.Signature == nil { return ErrEmptySignature() } - if tx.Signature.PublicKey == nil { - return ErrEmptyPublicKey() + if err := tx.Signature.ValidateBasic(); err != nil { + return err } + + // Does the transaction have a valid key? publicKey, err := crypto.NewPublicKeyFromBytes(tx.Signature.PublicKey) if err != nil { return ErrNewPublicKeyFromBytes(err) } - signBytes, err := tx.SignBytes() + + // Is there a valid msg that can be decoded? + if _, err := tx.GetMessage(); err != nil { + return err + } + + signBytes, err := tx.SignableBytes() if err != nil { return ErrProtoMarshal(err) } if ok := publicKey.Verify(signBytes, tx.Signature.Signature); !ok { return ErrSignatureVerificationFailed() } - if _, err := tx.Message(); err != nil { - return err - } + return nil } -func (tx *Transaction) Message() (Message, Error) { - msg, er := codec.GetCodec().FromAny(tx.Msg) - if er != nil { - return nil, ErrProtoMarshal(er) +func (tx *Transaction) GetMessage() (Message, Error) { + msg, err := codec.GetCodec().FromAny(tx.Msg) + if err != nil { + return nil, ErrProtoFromAny(err) } message, ok := msg.(Message) if !ok { @@ -60,107 +84,44 @@ func (tx *Transaction) Message() (Message, Error) { } func (tx *Transaction) Sign(privateKey crypto.PrivateKey) Error { - publicKey := privateKey.PublicKey() - bz, err := tx.SignBytes() + txSignableBz, err := tx.SignableBytes() if err != nil { - return err + return ErrProtoMarshal(err) } - signature, er := privateKey.Sign(bz) + signature, er := privateKey.Sign(txSignableBz) if er != nil { return ErrTransactionSign(er) } tx.Signature = &Signature{ - PublicKey: publicKey.Bytes(), + PublicKey: privateKey.PublicKey().Bytes(), Signature: signature, } return nil } func (tx *Transaction) Hash() (string, Error) { - b, err := tx.Bytes() + txProtoBz, err := tx.Bytes() if err != nil { return "", ErrProtoMarshal(err) } - return TransactionHash(b), nil + return TxHash(txProtoBz), nil } -func (tx *Transaction) SignBytes() ([]byte, Error) { - sig := tx.Signature // Backup signature - tx.Signature = nil - bz, err := codec.GetCodec().Marshal(tx) - if err != nil { - return nil, ErrProtoMarshal(err) - } - tx.Signature = sig // Restore signature - return bz, nil +// The bytes of the transaction that should have been signed. +func (tx *Transaction) SignableBytes() ([]byte, error) { + // This is not simply `tx.Message().GetCanonicalBytes()` because the txCopy also contains + // other metadata such as the nonce which has to be part signed as well. + txCopy := codec.GetCodec().Clone(tx).(*Transaction) + txCopy.Signature = nil + return codec.GetCodec().Marshal(txCopy) } -func (tx *Transaction) Bytes() ([]byte, Error) { - bz, err := codec.GetCodec().Marshal(tx) - if err != nil { - return nil, ErrProtoMarshal(err) - } - return bz, nil -} - -func (tx *Transaction) Equals(tx2 *Transaction) bool { - b, _ := tx2.Bytes() - b1, _ := tx2.Bytes() - return bytes.Equal(b, b1) -} - -var _ modules.TxResult = &DefaultTxResult{} - -func (x *DefaultTxResult) Bytes() ([]byte, error) { - return codec.GetCodec().Marshal(x) -} - -func (*DefaultTxResult) FromBytes(bz []byte) (modules.TxResult, error) { - result := new(DefaultTxResult) - if err := codec.GetCodec().Unmarshal(bz, result); err != nil { - return nil, err - } - return result, nil -} - -func (x *DefaultTxResult) Hash() ([]byte, error) { - bz, err := x.Bytes() - if err != nil { - return nil, err - } - return x.HashFromBytes(bz) -} - -func (x *DefaultTxResult) HashFromBytes(bz []byte) ([]byte, error) { - return crypto.SHA3Hash(bz), nil -} - -func (tx *Transaction) ToTxResult(height int64, index int, signer, recipient, msgType string, er Error) (*DefaultTxResult, Error) { - txBytes, err := tx.Bytes() - if err != nil { - return nil, err - } - code, errString := int32(0), "" - if er != nil { - code = int32(er.Code()) - errString = err.Error() - } - return &DefaultTxResult{ - Tx: txBytes, - Height: height, - Index: int32(index), - ResultCode: code, - Error: errString, - SignerAddr: signer, - RecipientAddr: recipient, - MessageType: msgType, - }, nil +func (tx *Transaction) Bytes() ([]byte, error) { + return codec.GetCodec().Marshal(tx) } -func (tx *Transaction) GetMessage() (proto.Message, error) { - return codec.GetCodec().FromAny(tx.Msg) -} - -func TransactionHash(transactionProtoBytes []byte) string { - return crypto.GetHashStringFromBytes(transactionProtoBytes) +func (tx *Transaction) Equals(tx2 ITransaction) bool { + b, err := tx.Bytes() + b2, err2 := tx2.Bytes() + return err != nil && err2 != nil && bytes.Equal(b, b2) } diff --git a/utility/types/transaction_test.go b/utility/types/transaction_test.go index 8212b0cf1..6f5c9ec0b 100644 --- a/utility/types/transaction_test.go +++ b/utility/types/transaction_test.go @@ -1,10 +1,10 @@ package types import ( + "fmt" "testing" "github.com/pokt-network/pocket/shared/codec" - "github.com/pokt-network/pocket/shared/crypto" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -17,32 +17,12 @@ var ( testingToAddr, _ = crypto.GenerateAddress() ) -func NewTestingMsg(_ *testing.T) Message { - return &MessageSend{ - FromAddress: testingSenderAddr, - ToAddress: testingToAddr, - Amount: defaultAmount, - } -} - -func NewUnsignedTestingTransaction(t *testing.T) Transaction { - msg := NewTestingMsg(t) - - anyMsg, err := codec.GetCodec().ToAny(msg) - require.NoError(t, err) - - return Transaction{ - Msg: anyMsg, - Nonce: BigIntToString(RandBigInt()), - } -} - -func TestTransactionBytesAndFromBytes(t *testing.T) { - tx := NewUnsignedTestingTransaction(t) +func TestTransaction_BytesAndFromBytes(t *testing.T) { + tx := newUnsignedTestingTransaction(t) bz, err := tx.Bytes() require.NoError(t, err) - tx2, err := TransactionFromBytes(bz) + tx2, err := TxFromBytes(bz) require.NoError(t, err) hash1, err := tx.Hash() @@ -55,37 +35,37 @@ func TestTransactionBytesAndFromBytes(t *testing.T) { require.Equal(t, proto.Clone(&tx), proto.Clone(tx2), "transaction mismatch") } -func TestTransaction_Message(t *testing.T) { - tx := NewUnsignedTestingTransaction(t) - msg, err := tx.Message() +func TestTransaction_GetMessage(t *testing.T) { + tx := newUnsignedTestingTransaction(t) + msg, err := tx.GetMessage() require.NoError(t, err) - expected := NewTestingMsg(t) + expected := newTestingMsgSend(t) require.NotEqual(t, expected, msg) require.Equal(t, msg.ProtoReflect().Type(), expected.ProtoReflect().Type()) - message := msg.(*MessageSend) - expectedMessage := expected.(*MessageSend) - require.Equal(t, message.Amount, expectedMessage.Amount, "unequal messages") - require.Equal(t, message.FromAddress, expectedMessage.FromAddress, "unequal messages") - require.Equal(t, message.ToAddress, expectedMessage.ToAddress, "unequal messages") + messageSend := msg.(*MessageSend) + expectedMessageSend := expected.(*MessageSend) + require.Equal(t, messageSend.Amount, expectedMessageSend.Amount, "unequal messages") + require.Equal(t, messageSend.FromAddress, expectedMessageSend.FromAddress, "unequal messages") + require.Equal(t, messageSend.ToAddress, expectedMessageSend.ToAddress, "unequal messages") } func TestTransaction_Sign(t *testing.T) { - tx := NewUnsignedTestingTransaction(t) + tx := newUnsignedTestingTransaction(t) err := tx.Sign(testingSenderPrivateKey) require.NoError(t, err) - msg, err := tx.SignBytes() - require.NoError(t, err) + msg, er := tx.SignableBytes() + require.NoError(t, er) verified := testingSenderPublicKey.Verify(msg, tx.Signature.Signature) require.True(t, verified, "signature should be verified") } func TestTransaction_ValidateBasic(t *testing.T) { - tx := NewUnsignedTestingTransaction(t) + tx := newUnsignedTestingTransaction(t) err := tx.Sign(testingSenderPrivateKey) require.NoError(t, err) @@ -122,5 +102,24 @@ func TestTransaction_ValidateBasic(t *testing.T) { txInvalidSignature.Signature.Signature = []byte("signature") er = txInvalidSignature.ValidateBasic() require.Equal(t, ErrSignatureVerificationFailed().Code(), er.Code()) +} +func newTestingMsgSend(_ *testing.T) Message { + return &MessageSend{ + FromAddress: testingSenderAddr, + ToAddress: testingToAddr, + Amount: defaultAmount, + } +} + +func newUnsignedTestingTransaction(t *testing.T) Transaction { + msg := newTestingMsgSend(t) + + anyMsg, err := codec.GetCodec().ToAny(msg) + require.NoError(t, err) + + return Transaction{ + Msg: anyMsg, + Nonce: fmt.Sprint(crypto.GetNonce()), + } } diff --git a/utility/types/tx_result.go b/utility/types/tx_result.go new file mode 100644 index 000000000..12aedd91a --- /dev/null +++ b/utility/types/tx_result.go @@ -0,0 +1,53 @@ +package types + +import ( + "github.com/pokt-network/pocket/shared/codec" + "github.com/pokt-network/pocket/shared/crypto" + "github.com/pokt-network/pocket/shared/modules" +) + +// INVESTIGATE: Look into a way of removing this type altogether or from shared interfaces. + +var _ modules.TxResult = &DefaultTxResult{} + +func (txr *DefaultTxResult) Bytes() ([]byte, error) { + return codec.GetCodec().Marshal(txr) +} + +func (*DefaultTxResult) FromBytes(bz []byte) (modules.TxResult, error) { + result := new(DefaultTxResult) + if err := codec.GetCodec().Unmarshal(bz, result); err != nil { + return nil, err + } + return result, nil +} + +func (txr *DefaultTxResult) Hash() ([]byte, error) { + bz, err := txr.Bytes() + if err != nil { + return nil, err + } + return txr.HashFromBytes(bz) +} + +func (txr *DefaultTxResult) HashFromBytes(bz []byte) ([]byte, error) { + return crypto.SHA3Hash(bz), nil +} + +func (tx *Transaction) ToTxResult(height int64, index int, signer, recipient, msgType string, err Error) (*DefaultTxResult, Error) { + txBytes, er := tx.Bytes() + if er != nil { + return nil, ErrProtoMarshal(er) + } + code, errString := int32(0), "" + return &DefaultTxResult{ + Tx: txBytes, + Height: height, + Index: int32(index), + ResultCode: code, + Error: errString, + SignerAddr: signer, + RecipientAddr: recipient, + MessageType: msgType, + }, nil +} diff --git a/utility/types/util.go b/utility/types/util.go deleted file mode 100644 index 1a275e8eb..000000000 --- a/utility/types/util.go +++ /dev/null @@ -1,39 +0,0 @@ -package types - -import ( - "crypto/rand" - "math/big" -) - -const ( - DefaultDenomination = 10 -) - -var max *big.Int - -func init() { - max = new(big.Int) - max.Exp(big.NewInt(2), big.NewInt(256), nil).Sub(max, big.NewInt(1)) -} - -func RandBigInt() *big.Int { - n, _ := rand.Int(rand.Reader, max) - return n -} - -func StringToBigInt(s string) (*big.Int, Error) { - b := big.Int{} - i, ok := b.SetString(s, DefaultDenomination) - if !ok { - return nil, ErrStringToBigInt() - } - return i, nil -} - -func BigIntToString(b *big.Int) string { - return b.Text(DefaultDenomination) -} - -func BigIntLessThan(a, b *big.Int) bool { - return a.Cmp(b) == -1 -} diff --git a/utility/types/validatable.go b/utility/types/validatable.go new file mode 100644 index 000000000..15747d729 --- /dev/null +++ b/utility/types/validatable.go @@ -0,0 +1,7 @@ +package types + +type Validatable interface { + // ValidateBasic` is a stateless validation check that should encapsulate all + // validations possible prior to interacting with the storage layer. + ValidateBasic() Error +} diff --git a/utility/types/vote.go b/utility/types/vote.go deleted file mode 100644 index 4b8e9fe53..000000000 --- a/utility/types/vote.go +++ /dev/null @@ -1,23 +0,0 @@ -package types - -const ( - DoubleSignEvidenceType = 1 -) - -// NOTE: there's no signature validation on the vote because we are unsure the current mode of vote signing -// TODO *Needs to add signatures to vote structure* -func (v *LegacyVote) ValidateBasic() Error { - if err := ValidatePublicKey(v.PublicKey); err != nil { - return err - } - if err := ValidateHash(v.BlockHash); err != nil { - return err - } - if v.Height < 0 { - return ErrInvalidBlockHeight() - } - if v.Type != DoubleSignEvidenceType { - return ErrInvalidEvidenceType() - } - return nil -} diff --git a/utility/types/vote_test.go b/utility/types/vote_test.go deleted file mode 100644 index 5649fb60d..000000000 --- a/utility/types/vote_test.go +++ /dev/null @@ -1,47 +0,0 @@ -package types - -import ( - "testing" - - "github.com/pokt-network/pocket/shared/crypto" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/proto" -) - -func TestVoteValidateBasic(t *testing.T) { - publicKey, err := crypto.GeneratePublicKey() - require.NoError(t, err) - testHash := crypto.SHA3Hash([]byte("fake_hash")) - v := &LegacyVote{ - PublicKey: publicKey.Bytes(), - Height: 1, - Round: 2, - Type: DoubleSignEvidenceType, - BlockHash: testHash, - } - require.NoError(t, v.ValidateBasic()) - // bad public key - v2 := proto.Clone(v).(*LegacyVote) - v2.PublicKey = []byte("not_a_valid_key") - badPkLen := len(v2.PublicKey) - require.Equal(t, v2.ValidateBasic(), ErrInvalidPublicKeyLen(crypto.ErrInvalidPublicKeyLen(badPkLen))) - // no public key - v2.PublicKey = nil - require.Equal(t, v2.ValidateBasic(), ErrEmptyPublicKey()) - // bad hash - v3 := proto.Clone(v).(*LegacyVote) - v3.BlockHash = []byte("not_a_hash") - badBlockHashLen := len(v3.BlockHash) - require.Equal(t, v3.ValidateBasic(), ErrInvalidHashLength(crypto.ErrInvalidHashLen(badBlockHashLen))) - // no hash - v3.BlockHash = nil - require.Equal(t, v3.ValidateBasic(), ErrEmptyHash()) - // negative height - v4 := proto.Clone(v).(*LegacyVote) - v4.Height = -1 - require.Equal(t, v4.ValidateBasic(), ErrInvalidBlockHeight()) - // bad type - v5 := proto.Clone(v).(*LegacyVote) - v5.Type = 0 - require.Equal(t, v5.ValidateBasic(), ErrInvalidEvidenceType()) -} diff --git a/utility/validator.go b/utility/validator.go new file mode 100644 index 000000000..3c71a5345 --- /dev/null +++ b/utility/validator.go @@ -0,0 +1,63 @@ +package utility + +import ( + "math/big" + + coreTypes "github.com/pokt-network/pocket/shared/core/types" + typesUtil "github.com/pokt-network/pocket/utility/types" +) + +// ADDTEST: There are no good tests for this functionality, which MUST be added. +func (u *utilityContext) burnValidator(burnPercent int, addr []byte) typesUtil.Error { + // TODO: Will need to extend this to support burning from other actors types & pools when the logic is implemented + validatorActorType := coreTypes.ActorType_ACTOR_TYPE_VAL + validatorPool := coreTypes.Pools_POOLS_VALIDATOR_STAKE + + stakeAmount, err := u.getActorStakeAmount(validatorActorType, addr) + if err != nil { + return err + } + + // stake after burn = current take * newStake = currentStake * burnPercent / 100 + burnAmount := new(big.Float).SetInt(stakeAmount) + burnAmount.Mul(burnAmount, big.NewFloat(float64(burnPercent))) + burnAmount.Quo(burnAmount, big.NewFloat(100)) + burnAmountTruncated, _ := burnAmount.Int(nil) + + // Round up to 0 if -ve + zeroBigInt := big.NewInt(0) + if burnAmountTruncated.Cmp(zeroBigInt) == -1 { + burnAmountTruncated = zeroBigInt + } + + newAmountAfterBurn := big.NewInt(0).Sub(stakeAmount, burnAmountTruncated) + + // remove from pool + if err := u.subPoolAmount(validatorPool.FriendlyName(), burnAmountTruncated); err != nil { + return err + } + + // remove from actor + if err := u.setActorStakeAmount(validatorActorType, addr, newAmountAfterBurn); err != nil { + return err + } + + // Need to check if new stake is below min required stake + minStake, err := u.getValidatorMinimumStake() + if err != nil { + return err + } + + // Check if amount after burn is below the min required stake + if minStake.Cmp(newAmountAfterBurn) == -1 { + unbondingHeight, err := u.getUnbondingHeight(validatorActorType) + if err != nil { + return err + } + if err := u.setActorUnstakingHeight(validatorActorType, addr, unbondingHeight); err != nil { + return err + } + } + + return nil +}