Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TODO] chore: update smt.MerkleRoot#Sum() error handling #672

Merged
merged 18 commits into from
Jul 19, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ require (
// repo is the first obvious idea, but has to be carefully considered, automated, and is not
// a hard blocker.
github.com/pokt-network/shannon-sdk v0.0.0-20240628223057-7d2928722749
github.com/pokt-network/smt v0.11.1
github.com/pokt-network/smt v0.12.0
github.com/pokt-network/smt/kvstore/badger v0.0.0-20240109205447-868237978c0b
github.com/prometheus/client_golang v1.19.0
github.com/regen-network/gocuke v1.1.0
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -996,8 +996,10 @@ github.com/pokt-network/ring-go v0.1.0 h1:hF7mDR4VVCIqqDAsrloP8azM9y1mprc99YgnTj
github.com/pokt-network/ring-go v0.1.0/go.mod h1:8NHPH7H3EwrPX3XHfpyRI6bz4gApkE3+fd0XZRbMWP0=
github.com/pokt-network/shannon-sdk v0.0.0-20240628223057-7d2928722749 h1:V/3xzmykSABhAxRZLawWUoIPVlnp7EGCnCxFpLXD7R0=
github.com/pokt-network/shannon-sdk v0.0.0-20240628223057-7d2928722749/go.mod h1:MfoRhzPRlxiaY3xQyZo28B7ibDuhricA//TGGy48TwM=
github.com/pokt-network/smt v0.11.1 h1:ySN8PjrPDKyvzLcX0qTHR2s5ReaZnjq25z0B7p6AWl0=
github.com/pokt-network/smt v0.11.1/go.mod h1:S4Ho4OPkK2v2vUCHNtA49XDjqUC/OFYpBbynRVYmxvA=
github.com/pokt-network/smt v0.12.0 h1:uqru/0ykC4LnBoMacakobNOd1iRK69PlohqjMtLmYNA=
github.com/pokt-network/smt v0.12.0/go.mod h1:S4Ho4OPkK2v2vUCHNtA49XDjqUC/OFYpBbynRVYmxvA=
github.com/pokt-network/smt v1.0.0 h1:rEvpQjpN7N8yNSX84l5DclEWFphJPizuzTfkfE1jfQI=
github.com/pokt-network/smt v1.0.0/go.mod h1:S4Ho4OPkK2v2vUCHNtA49XDjqUC/OFYpBbynRVYmxvA=
github.com/pokt-network/smt/kvstore/badger v0.0.0-20240109205447-868237978c0b h1:TjfgV3vgW0zW47Br/OgUXD4M8iyR74EYanbFfN4ed8o=
github.com/pokt-network/smt/kvstore/badger v0.0.0-20240109205447-868237978c0b/go.mod h1:GbzcG5ebj8twKmBL1VzdPM4NS44okwYXBfQaVXT+6yU=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
Expand Down
32 changes: 20 additions & 12 deletions testutil/proof/fixture_generators.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package proof

import (
"crypto/sha256"
"encoding/binary"
"math/rand"
"testing"

"github.com/pokt-network/smt"
"github.com/stretchr/testify/require"

"github.com/pokt-network/smt"

testsession "github.com/pokt-network/poktroll/testutil/session"
prooftypes "github.com/pokt-network/poktroll/x/proof/types"
sessiontypes "github.com/pokt-network/poktroll/x/session/types"
sharedtypes "github.com/pokt-network/poktroll/x/shared/types"
)

const (
sumBytesSize = 8
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
countBytesSize = 8
Sha256SmstRootSize = sha256.Size + sumBytesSize + countBytesSize
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
)

// BaseClaim returns a base (default, example, etc..) claim with the given app
// address, supplier address, and sum that can be used for testing.
func BaseClaim(appAddr, supplierAddr string, sum uint64) prooftypes.Claim {
Expand Down Expand Up @@ -51,32 +59,32 @@ func ClaimWithRandomHash(t *testing.T, appAddr, supplierAddr string, sum uint64)
// TODO_MAINNET: Revisit if the SMT should be big or little Endian. Refs:
// https://github.com/pokt-network/smt/pull/46#discussion_r1636975124
// https://github.com/pokt-network/smt/blob/ea585c6c3bc31c804b6bafa83e985e473b275580/smst.go#L23C10-L23C76
func SmstRootWithSum(sum uint64) smt.MerkleRoot {
root := [smt.SmstRootSizeBytes]byte{}
func SmstRootWithSum(sum uint64) smt.MerkleSumRoot {
root := [Sha256SmstRootSize]byte{}
// Insert the sum into the root hash
binary.BigEndian.PutUint64(root[smt.SmtRootSizeBytes:], sum)
binary.BigEndian.PutUint64(root[sha256.Size:], sum)
// Insert the count into the root hash
// TODO_TECHDEBT: This is a hard-coded count of 1, but could be a parameter.
// TODO_TECHDEBT: We are assuming the sum takes up 8 bytes.
binary.BigEndian.PutUint64(root[smt.SmtRootSizeBytes+8:], 1)
return smt.MerkleRoot(root[:])
binary.BigEndian.PutUint64(root[sha256.Size+8:], 1)
return smt.MerkleSumRoot(root[:])
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
}

// RandSmstRootWithSum returns a randomized SMST root with the given sum that
// can be used for testing. Randomizing the root is a simple way to randomize
// test claim hashes for testing proof requirement cases.
func RandSmstRootWithSum(t *testing.T, sum uint64) smt.MerkleRoot {
func RandSmstRootWithSum(t *testing.T, sum uint64) smt.MerkleSumRoot {
t.Helper()

root := [smt.SmstRootSizeBytes]byte{}
root := [Sha256SmstRootSize]byte{}
// Only populate the first 32 bytes with random data, leave the last 8 bytes for the sum.
_, err := rand.Read(root[:smt.SmtRootSizeBytes]) //nolint:staticcheck // We need a deterministic pseudo-random source.
_, err := rand.Read(root[:sha256.Size]) //nolint:staticcheck // We need a deterministic pseudo-random source.
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)

binary.BigEndian.PutUint64(root[smt.SmtRootSizeBytes:], sum)
binary.BigEndian.PutUint64(root[sha256.Size:], sum)
// Insert the count into the root hash
// TODO_TECHDEBT: This is a hard-coded count of 1, but could be a parameter.
// TODO_TECHDEBT: We are assuming the sum takes up 8 bytes.
binary.BigEndian.PutUint64(root[smt.SmtRootSizeBytes+8:], 1)
return smt.MerkleRoot(root[:])
binary.BigEndian.PutUint64(root[sha256.Size+8:], 1)
return smt.MerkleSumRoot(root[:])
}
5 changes: 3 additions & 2 deletions x/proof/keeper/msg_server_create_claim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ import (

abci "github.com/cometbft/cometbft/abci/types"
cosmostypes "github.com/cosmos/cosmos-sdk/types"
"github.com/pokt-network/smt"
"github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/pokt-network/smt"

keepertest "github.com/pokt-network/poktroll/testutil/keeper"
testproof "github.com/pokt-network/poktroll/testutil/proof"
"github.com/pokt-network/poktroll/testutil/sample"
Expand Down Expand Up @@ -490,7 +491,7 @@ func newTestClaimMsg(
supplierAddr string,
appAddr string,
service *sharedtypes.Service,
merkleRoot smt.MerkleRoot,
merkleRoot smt.MerkleSumRoot,
) *types.MsgCreateClaim {
t.Helper()

Expand Down
29 changes: 2 additions & 27 deletions x/proof/types/claim.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package types

import (
"fmt"

"github.com/cometbft/cometbft/crypto"

"github.com/pokt-network/smt"
Expand All @@ -11,36 +9,13 @@ import (
// GetNumComputeUnits returns the number of compute units for a given claim
// as determined by the sum of the root hash.
func (claim *Claim) GetNumComputeUnits() (numComputeUnits uint64, err error) {
// NB: smt.MerkleRoot#Sum() will panic if the root hash is not valid.
// Convert this panic into an error return.
defer func() {
if r := recover(); r != nil {
numComputeUnits = 0
err = fmt.Errorf(
"unable to get sum of invalid merkle root: %x; error: %v",
claim.GetRootHash(), r,
)
}
}()

return smt.MerkleRoot(claim.GetRootHash()).Sum(), nil
return smt.MerkleSumRoot(claim.GetRootHash()).Sum()
}

// GetNumRelays returns the number of relays for a given claim
// as determined by the count of the root hash.
func (claim *Claim) GetNumRelays() (numRelays uint64, err error) {
// Convert this panic into an error return.
defer func() {
if r := recover(); r != nil {
numRelays = 0
err = fmt.Errorf(
"unable to get count of invalid merkle root: %x; error: %v",
claim.GetRootHash(), r,
)
}
}()

return smt.MerkleRoot(claim.GetRootHash()).Count(), nil
return smt.MerkleSumRoot(claim.GetRootHash()).Count()
}

// GetHash returns the SHA-256 hash of the serialized claim.
Expand Down
52 changes: 39 additions & 13 deletions x/tokenomics/keeper/settle_session_accounting.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@

import (
"context"
"crypto/sha256"
"fmt"

"cosmossdk.io/math"
cosmostypes "github.com/cosmos/cosmos-sdk/types"

"github.com/pokt-network/poktroll/app/volatile"
"github.com/pokt-network/smt"

"github.com/pokt-network/poktroll/telemetry"
Expand All @@ -26,15 +29,20 @@
func (k Keeper) SettleSessionAccounting(
ctx context.Context,
claim *prooftypes.Claim,
) error {
) (err error) {
logger := k.Logger().With("method", "SettleSessionAccounting")

settlementCoin := cosmostypes.NewCoin("upokt", math.NewInt(0))
isSuccessful := false
// This is emitted only when the function returns.
defer telemetry.EventSuccessCounter(
"settle_session_accounting",
func() float32 { return float32(settlementCoin.Amount.Int64()) },
func() float32 {
Olshansk marked this conversation as resolved.
Show resolved Hide resolved
if settlementCoin.Amount.BigInt() == nil {
return 0
}
return float32(settlementCoin.Amount.Int64())
},
func() bool { return isSuccessful },
)

Expand Down Expand Up @@ -66,15 +74,19 @@
}

// Retrieve the sum of the root as a proxy into the amount of work done
root := (smt.MerkleRoot)(claim.GetRootHash())
root := (smt.MerkleSumRoot)(claim.GetRootHash())

// TODO_BLOCKER(@Olshansk): This check should be the responsibility of the SMST package
// since it's used to get compute units from the root hash.
if root == nil || len(root) != smt.SmstRootSizeBytes {
logger.Error(fmt.Sprintf("received an invalid root hash of size: %d", len(root)))
return tokenomicstypes.ErrTokenomicsRootHashInvalid
if !root.HasDigestSize(sha256.Size) {
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
return types.ErrTokenomicsRootHashInvalid.Wrapf(

Check failure on line 80 in x/tokenomics/keeper/settle_session_accounting.go

View workflow job for this annotation

GitHub Actions / go-test

undefined: types) (typecheck)

Check failure on line 80 in x/tokenomics/keeper/settle_session_accounting.go

View workflow job for this annotation

GitHub Actions / go-test

undefined: types) (typecheck)

Check failure on line 80 in x/tokenomics/keeper/settle_session_accounting.go

View workflow job for this annotation

GitHub Actions / go-test

undefined: types) (typecheck)

Check failure on line 80 in x/tokenomics/keeper/settle_session_accounting.go

View workflow job for this annotation

GitHub Actions / go-test

undefined: types) (typecheck)

Check failure on line 80 in x/tokenomics/keeper/settle_session_accounting.go

View workflow job for this annotation

GitHub Actions / go-test

undefined: types) (typecheck)
bryanchriswhite marked this conversation as resolved.
Show resolved Hide resolved
"root hash has invalid digest size (%d), expected (%d)",
root.DigestSize(), sha256.Size,
)
}

claimComputeUnits, err := root.Sum()
if err != nil {
return tokenomicstypes.ErrTokenomicsRootHashInvalid.Wrapf("%v", err)
}
claimComputeUnits := root.Sum()

// Helpers for logging the same metadata throughout this function calls
logger = logger.With(
Expand All @@ -96,7 +108,11 @@
logger.Info(fmt.Sprintf("About to start settling claim for %d compute units", claimComputeUnits))

// Calculate the amount of tokens to mint & burn
settlementCoin = k.getCoinFromComputeUnits(ctx, root)
settlementCoin, err = k.getCoinFromComputeUnits(ctx, root)
if err != nil {
return err
}

settlementCoins := cosmostypes.NewCoins(settlementCoin)

logger.Info(fmt.Sprintf(
Expand Down Expand Up @@ -194,10 +210,20 @@
return nil
}

func (k Keeper) getCoinFromComputeUnits(ctx context.Context, root smt.MerkleRoot) cosmostypes.Coin {
func (k Keeper) getCoinFromComputeUnits(ctx context.Context, root smt.MerkleSumRoot) (cosmostypes.Coin, error) {
// Retrieve the existing tokenomics params
params := k.GetParams(ctx)

upokt := math.NewInt(int64(root.Sum() * params.ComputeUnitsToTokensMultiplier))
return cosmostypes.NewCoin("upokt", upokt)
sum, err := root.Sum()
if err != nil {
return cosmostypes.Coin{}, err
}

upokt := math.NewInt(int64(sum * params.ComputeUnitsToTokensMultiplier))

if upokt.IsNegative() {
return cosmostypes.Coin{}, tokenomicstypes.ErrTokenomicsRootHashInvalid.Wrap("sum * compute_units_to_tokens_multiplier is negative")
}

return cosmostypes.NewCoin(volatile.DenomuPOKT, upokt), nil
}
25 changes: 6 additions & 19 deletions x/tokenomics/keeper/settle_session_accounting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ import (
cosmostypes "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
"github.com/pokt-network/smt"
"github.com/stretchr/testify/require"

"github.com/pokt-network/smt"

testkeeper "github.com/pokt-network/poktroll/testutil/keeper"
testproof "github.com/pokt-network/poktroll/testutil/proof"
"github.com/pokt-network/poktroll/testutil/sample"
Expand Down Expand Up @@ -300,11 +301,11 @@ func TestSettleSessionAccounting_AppNotFound(t *testing.T) {
func TestSettleSessionAccounting_InvalidRoot(t *testing.T) {
keeper, ctx, appAddr, supplierAddr := testkeeper.TokenomicsKeeperWithActorAddrs(t)

rootHashSizeBytes := smt.SmstRootSizeBytes
rootHashSizeBytes := testproof.Sha256SmstRootSize
// Define test cases
tests := []struct {
desc string
root []byte // smst.MerkleRoot
root []byte // smst.MerkleSumRoot
errExpected bool
}{
{
Expand Down Expand Up @@ -350,26 +351,12 @@ func TestSettleSessionAccounting_InvalidRoot(t *testing.T) {
// Iterate over each test case
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
// Use defer-recover to catch any panic
defer func() {
if r := recover(); r != nil {
t.Errorf("Test panicked: %s", r)
}
}()

// Setup claim by copying the testproof.BaseClaim and updating the root
claim := testproof.BaseClaim(appAddr, supplierAddr, 0)
claim.RootHash = smt.MerkleRoot(test.root[:])
claim.RootHash = smt.MerkleSumRoot(test.root[:])

// Execute test function
err := func() (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("panic occurred: %v", r)
}
}()
return keeper.SettleSessionAccounting(ctx, &claim)
}()
err := keeper.SettleSessionAccounting(ctx, &claim)

// Assert the error
if test.errExpected {
Expand Down
Loading