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

[Proof Validation] Move proof validation to session settlement #703

Merged
merged 38 commits into from
Jul 25, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
ffb6902
refactor: difficulty in terms of target hash
bryanchriswhite Jul 8, 2024
ceb283d
fix: e2e test
bryanchriswhite Jul 16, 2024
0ca9b98
fix: e2e test
bryanchriswhite Jul 16, 2024
38d1985
chore: review feedback improvements
bryanchriswhite Jul 17, 2024
1c66b4f
Merge remote-tracking branch 'pokt/main' into refactor/difficulty/tar…
bryanchriswhite Jul 17, 2024
5089b94
Merge branch 'main' into refactor/difficulty/target-hash
bryanchriswhite Jul 17, 2024
cc6468d
refactor: protocol.NewRelayHasher, .RelayHashSize
bryanchriswhite Jul 18, 2024
5675f81
fix: ComputeNewDifficultyTargetHash()
bryanchriswhite Jul 19, 2024
440ecc6
chore: review fee
bryanchriswhite Jul 19, 2024
ee59e0d
refactor: use big.Floats on-chain & cleanup
bryanchriswhite Jul 19, 2024
69d97d3
chore: review improvements
bryanchriswhite Jul 19, 2024
5a3cfd8
[TODO] chore: update `smt.MerkleRoot#Sum()` error handling (#672)
bryanchriswhite Jul 19, 2024
e2802ac
Merge remote-tracking branch 'pokt/main' into refactor/difficulty/tar…
bryanchriswhite Jul 19, 2024
a5f9b61
Last review of #690
Olshansk Jul 19, 2024
c365a4f
Self review
Olshansk Jul 20, 2024
09d4cb0
Fix flaky tests
Olshansk Jul 22, 2024
36d55a3
Minor nits
Olshansk Jul 22, 2024
9cf20eb
Move the verification layer
Olshansk Jul 23, 2024
cb166cd
Update testutil/network/network.go
Olshansk Jul 23, 2024
49ce8b7
Merge branch 'refactor/difficulty/target-hash-review' into pr_690_fol…
Olshansk Jul 23, 2024
a52a5e8
Working on one last test
Olshansk Jul 23, 2024
ac4a212
Add a TODO_MAINNET
Olshansk Jul 23, 2024
8e0d26c
Merge branch 'refactor/difficulty/target-hash-review' into pr_690_fol…
Olshansk Jul 23, 2024
f16ba64
WIP
Olshansk Jul 23, 2024
6dbbcd5
Not functioning but temp WIP
Olshansk Jul 23, 2024
58591c4
The test passes
Olshansk Jul 24, 2024
d21f9fc
About to do a self review
Olshansk Jul 24, 2024
99783a8
Review of #690 by @olshansk (#699)
Olshansk Jul 24, 2024
8fe0e87
git merge refactor/difficulty/target-hash
Olshansk Jul 24, 2024
0147e99
Finished self review
Olshansk Jul 24, 2024
f7b4e5c
Missing )
Olshansk Jul 24, 2024
54b0261
Merge with main
Olshansk Jul 24, 2024
ba2058c
Merge with main
Olshansk Jul 24, 2024
2d60b8d
Replied to some PR comments
Olshansk Jul 24, 2024
e2aa6fd
Reply to all review comments
Olshansk Jul 24, 2024
a4d28e3
Fix unit test
Olshansk Jul 24, 2024
6bf702e
Merge branch 'main' into pr_690_followups
Olshansk Jul 25, 2024
9e8e8e8
Omit nil check
Olshansk Jul 25, 2024
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
195 changes: 159 additions & 36 deletions api/poktroll/tokenomics/event.pulsar.go

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions pkg/client/query/accquerier.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ func (aq *accQuerier) GetPubKeyFromAddress(ctx context.Context, address string)
if err != nil {
return nil, err
}
if acc == nil {
return nil, ErrQueryAccountNotFound.Wrapf("address: %s", address)
}

// If the account's public key is nil, then return an error.
pubKey := acc.GetPubKey()
Expand Down
1 change: 1 addition & 0 deletions pkg/crypto/protocol/difficulty.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var (
// GetDifficultyFromHash returns the "difficulty" of the given hash, with respect
// to the "highest" (easiest) target hash, BaseRelayDifficultyHash.
// The resultant value is not used for any business logic but is simplify there to have a human-readable version of the hash.
// TODO_MAINNET: Can this cause an integer overflow?
func GetDifficultyFromHash(hashBz [RelayHasherSize]byte) int64 {
baseRelayDifficultyHashInt := new(big.Int).SetBytes(BaseRelayDifficultyHashBz)
hashInt := new(big.Int).SetBytes(hashBz[:])
Expand Down
2 changes: 1 addition & 1 deletion proto/poktroll/shared/service.proto
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ enum RPCType {
WEBSOCKET = 2; // WebSocket
JSON_RPC = 3; // JSON-RPC
REST = 4; // REST
// Add new RPC types here as needed
// Add new RPC types here as needed
}

// Enum to define configuration options
Expand Down
8 changes: 8 additions & 0 deletions proto/poktroll/tokenomics/event.proto
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,21 @@ import "cosmos/base/v1beta1/coin.proto";
import "poktroll/proof/claim.proto";
import "poktroll/proof/requirement.proto";

enum ClaimExpirationReason {
EXPIRATION_REASON_UNSPECIFIED = 0; // Default value, means may be valid
PROOF_MISSING = 1;
PROOF_INVALID = 2;
}

// EventClaimExpired is an event emitted during settlement whenever a claim requiring
// an on-chain proof doesn't have one. The claim cannot be settled, leading to that work
// never being rewarded.
message EventClaimExpired {
poktroll.proof.Claim claim = 1 [(gogoproto.jsontag) = "claim"];
// TODO_MAINNET: Shold we include the proof here too?
uint64 num_relays = 2 [(gogoproto.jsontag) = "num_relays"];
uint64 num_compute_units = 3 [(gogoproto.jsontag) = "num_compute_units"];
ClaimExpirationReason expiration_reason = 4 [(gogoproto.jsontag) = "expiration_reason"];
}

// EventClaimSettled is an event emitted whenever a claim is settled.
Expand Down
2 changes: 2 additions & 0 deletions testutil/keeper/tokenomics.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ type TokenomicsModuleKeepers struct {
tokenomicstypes.SupplierKeeper
tokenomicstypes.ProofKeeper
tokenomicstypes.SharedKeeper
tokenomicstypes.SessionKeeper

Codec *codec.ProtoCodec
}
Expand Down Expand Up @@ -373,6 +374,7 @@ func NewTokenomicsModuleKeepers(
SupplierKeeper: &supplierKeeper,
ProofKeeper: &proofKeeper,
SharedKeeper: &sharedKeeper,
SessionKeeper: &sessionKeeper,

Codec: cdc,
}
Expand Down
162 changes: 162 additions & 0 deletions testutil/testtree/tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package testtree

import (
"context"
"os"
"testing"

"github.com/cosmos/cosmos-sdk/crypto/keyring"
cosmostypes "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"

"github.com/pokt-network/poktroll/pkg/crypto"
"github.com/pokt-network/poktroll/pkg/relayer"
"github.com/pokt-network/poktroll/pkg/relayer/session"
"github.com/pokt-network/poktroll/testutil/testrelayer"
prooftypes "github.com/pokt-network/poktroll/x/proof/types"
sessiontypes "github.com/pokt-network/poktroll/x/session/types"
)

// NewFilledSessionTree creates a new session tree with numRelays of relays
// filled out using the request and response headers provided where every
// relay is signed by the supplier and application respectively.
func NewFilledSessionTree(
ctx context.Context, t *testing.T,
numRelays uint,
supplierKeyUid, supplierAddr string,
sessionTreeHeader, reqHeader, resHeader *sessiontypes.SessionHeader,
keyRing keyring.Keyring,
ringClient crypto.RingClient,
) relayer.SessionTree {
t.Helper()

// Initialize an empty session tree with the given session header.
sessionTree := NewEmptySessionTree(t, sessionTreeHeader, supplierAddr)

// Add numRelays of relays to the session tree.
FillSessionTree(
ctx, t,
sessionTree, numRelays,
supplierKeyUid, supplierAddr,
reqHeader, resHeader,
keyRing,
ringClient,
)

return sessionTree
}

// NewEmptySessionTree creates a new empty session tree with for given session.
func NewEmptySessionTree(
t *testing.T,
sessionTreeHeader *sessiontypes.SessionHeader,
supplierAddr string,
) relayer.SessionTree {
t.Helper()

// Create a temporary session tree store directory for persistence.
testSessionTreeStoreDir, err := os.MkdirTemp("", "session_tree_store_dir")
require.NoError(t, err)

// Delete the temporary session tree store directory after the test completes.
t.Cleanup(func() {
_ = os.RemoveAll(testSessionTreeStoreDir)
})

accAddress := cosmostypes.MustAccAddressFromBech32(supplierAddr)

// Construct a session tree to add relays to and generate a proof from.
sessionTree, err := session.NewSessionTree(
sessionTreeHeader,
&accAddress,
testSessionTreeStoreDir,
)
require.NoError(t, err)

return sessionTree
}

// FillSessionTree fills the session tree with valid signed relays.
// A total of numRelays relays are added to the session tree with
// increasing weights (relay 1 has weight 1, relay 2 has weight 2, etc.).
func FillSessionTree(
ctx context.Context, t *testing.T,
sessionTree relayer.SessionTree,
numRelays uint,
supplierKeyUid, supplierAddr string,
reqHeader, resHeader *sessiontypes.SessionHeader,
keyRing keyring.Keyring,
ringClient crypto.RingClient,
) {
t.Helper()

for i := 0; i < int(numRelays); i++ {
relay := testrelayer.NewSignedEmptyRelay(
ctx, t,
supplierKeyUid, supplierAddr,
reqHeader, resHeader,
keyRing,
ringClient,
)
relayBz, err := relay.Marshal()
require.NoError(t, err)

relayKey, err := relay.GetHash()
require.NoError(t, err)

// See FillSessionTreeExpectedComputeUnits below for explanation.
relayWeight := uint64(i)

err = sessionTree.Update(relayKey[:], relayBz, relayWeight)
require.NoError(t, err)
}
}

// FillSessionTreeExpectedComputeUnits returns the number of expected compute units
// to covert numRelays (in a test scenario) whereby every subsequent relay costs
// an addition compute unit.
// This is basic random approach selected for testing purposes. Don't think too
// deeply about it.
func FillSessionTreeExpectedComputeUnits(numRelays uint) uint64 {
return uint64(numRelays * (numRelays - 1) / 2)
}

// NewProof creates a new proof structure.
func NewProof(
t *testing.T,
supplierAddr string,
sessionHeader *sessiontypes.SessionHeader,
sessionTree relayer.SessionTree,
closestProofPath []byte,
) *prooftypes.Proof {
t.Helper()

// Generate a closest proof from the session tree using closestProofPath.
merkleProof, err := sessionTree.ProveClosest(closestProofPath)
require.NoError(t, err)
require.NotNil(t, merkleProof)

// Serialize the closest merkle proof.
merkleProofBz, err := merkleProof.Marshal()
require.NoError(t, err)

return &prooftypes.Proof{
SupplierAddress: supplierAddr,
SessionHeader: sessionHeader,
ClosestMerkleProof: merkleProofBz,
}
}

func NewClaim(
t *testing.T,
supplierAddr string,
sessionHeader *sessiontypes.SessionHeader,
rootHash []byte,
) *prooftypes.Claim {
// Create a new claim.
return &prooftypes.Claim{
SupplierAddress: supplierAddr,
SessionHeader: sessionHeader,
RootHash: rootHash,
}
}
2 changes: 2 additions & 0 deletions x/proof/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type (

sessionKeeper types.SessionKeeper
applicationKeeper types.ApplicationKeeper
accountKeeper types.AccountKeeper
sharedKeeper types.SharedKeeper

ringClient crypto.RingClient
Expand Down Expand Up @@ -88,6 +89,7 @@ func NewKeeper(

sessionKeeper: sessionKeeper,
applicationKeeper: applicationKeeper,
accountKeeper: accountKeeper,
sharedKeeper: sharedKeeper,

ringClient: ringKeeperClient,
Expand Down
51 changes: 21 additions & 30 deletions x/proof/keeper/msg_server_create_claim.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (

"github.com/pokt-network/poktroll/telemetry"
"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"
)

Expand All @@ -37,53 +36,43 @@ func (k msgServer) CreateClaim(
}()

logger := k.Logger().With("method", "CreateClaim")
sdkCtx := cosmostypes.UnwrapSDKContext(ctx)
logger.Info("creating claim")

// Basic validation of the CreateClaim message.
if err = msg.ValidateBasic(); err != nil {
return nil, err
}
logger.Info("validated the createClaim message")

// Compare msg session header w/ on-chain session header.
var session *sessiontypes.Session
session, err = k.queryAndValidateSessionHeader(ctx, msg)
session, err := k.queryAndValidateSessionHeader(ctx, msg.GetSessionHeader(), msg.GetSupplierAddress())
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}

// Use the session header from the on-chain hydrated session.
sessionHeader := session.GetHeader()

// Set the session header to the on-chain hydrated session header.
msg.SessionHeader = sessionHeader

// Validate claim message commit height is within the respective session's
// claim creation window using the on-chain session header.
if err = k.validateClaimWindow(ctx, msg); err != nil {
return nil, status.Error(codes.FailedPrecondition, err.Error())
// Construct and insert claim
claim = types.Claim{
SupplierAddress: msg.GetSupplierAddress(),
SessionHeader: session.GetHeader(),
RootHash: msg.GetRootHash(),
}

// Helpers for logging the same metadata throughout this function calls
logger = logger.
With(
"session_id", session.GetSessionId(),
"session_end_height", sessionHeader.GetSessionEndBlockHeight(),
"session_end_height", claim.SessionHeader.GetSessionEndBlockHeight(),
"supplier", msg.GetSupplierAddress(),
)

logger.Info("validated claim")

// Assign and upsert claim after all validation.
claim = types.Claim{
SupplierAddress: msg.GetSupplierAddress(),
SessionHeader: sessionHeader,
RootHash: msg.GetRootHash(),
// Validate claim message commit height is within the respective session's
// claim creation window using the on-chain session header.
if err = k.validateClaimWindow(ctx, claim.SessionHeader, claim.SupplierAddress); err != nil {
return nil, status.Error(codes.FailedPrecondition, err.Error())
}

_, isExistingClaim = k.Keeper.GetClaim(ctx, claim.GetSessionHeader().GetSessionId(), claim.GetSupplierAddress())

k.Keeper.UpsertClaim(ctx, claim)

logger.Info("created new claim")

// Get metadata for the event we want to emit
numRelays, err = claim.GetNumRelays()
if err != nil {
return nil, status.Error(codes.Internal, types.ErrProofInvalidClaimRootHash.Wrap(err.Error()).Error())
Expand All @@ -92,6 +81,11 @@ func (k msgServer) CreateClaim(
if err != nil {
return nil, status.Error(codes.Internal, types.ErrProofInvalidClaimRootHash.Wrap(err.Error()).Error())
}
_, isExistingClaim = k.Keeper.GetClaim(ctx, claim.GetSessionHeader().GetSessionId(), claim.GetSupplierAddress())

// Upsert the claim
k.Keeper.UpsertClaim(ctx, claim)
logger.Info("successfully upserted the claim")

// Emit the appropriate event based on whether the claim was created or updated.
var claimUpsertEvent proto.Message
Expand All @@ -113,8 +107,6 @@ func (k msgServer) CreateClaim(
},
)
}

sdkCtx := cosmostypes.UnwrapSDKContext(ctx)
if err = sdkCtx.EventManager().EmitTypedEvent(claimUpsertEvent); err != nil {
return nil, status.Error(
codes.Internal,
Expand All @@ -126,7 +118,6 @@ func (k msgServer) CreateClaim(
)
}

// TODO_BETA: return the claim in the response.
return &types.MsgCreateClaimResponse{
Claim: &claim,
}, nil
Expand Down
5 changes: 2 additions & 3 deletions x/proof/keeper/msg_server_create_claim_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ 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 @@ -140,7 +139,7 @@ func TestMsgServer_CreateClaim_Success(t *testing.T) {
events := sdkCtx.EventManager().Events()
require.Equal(t, 1, len(events))

require.Equal(t, events[0].Type, "poktroll.proof.EventClaimCreated")
require.Equal(t, "poktroll.proof.EventClaimCreated", events[0].Type)

event, err := cosmostypes.ParseTypedEvent(abci.Event(events[0]))
require.NoError(t, err)
Expand Down
Loading
Loading