Skip to content

Commit

Permalink
[TODOs] refactor: proof path calculation (#659)
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanchriswhite authored Jul 15, 2024
1 parent 3f55936 commit bd42b13
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 66 deletions.
33 changes: 33 additions & 0 deletions pkg/crypto/protocol/proof_path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package protocol

import (
"crypto/sha256"

"github.com/pokt-network/smt"
)

// SMT specification used for the proof verification.
var (
newHasher = sha256.New
SmtSpec smt.TrieSpec
)

func init() {
// Use a spec that does not prehash values in the smst. This returns a nil value
// hasher for the proof verification in order to avoid hashing the value twice.
SmtSpec = smt.NewTrieSpec(
newHasher(), true,
smt.WithValueHasher(nil),
)
}

// GetPathForProof computes the path to be used for proof validation by hashing
// the block hash and session id.
func GetPathForProof(blockHash []byte, sessionId string) []byte {
hasher := newHasher()
if _, err := hasher.Write(append(blockHash, []byte(sessionId)...)); err != nil {
panic(err)
}

return hasher.Sum(nil)
}
8 changes: 6 additions & 2 deletions pkg/relayer/session/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import (
"fmt"

"github.com/pokt-network/poktroll/pkg/client"
"github.com/pokt-network/poktroll/pkg/crypto/protocol"
"github.com/pokt-network/poktroll/pkg/either"
"github.com/pokt-network/poktroll/pkg/observable"
"github.com/pokt-network/poktroll/pkg/observable/channel"
"github.com/pokt-network/poktroll/pkg/observable/filter"
"github.com/pokt-network/poktroll/pkg/observable/logging"
"github.com/pokt-network/poktroll/pkg/relayer"
proofkeeper "github.com/pokt-network/poktroll/x/proof/keeper"
"github.com/pokt-network/poktroll/x/shared"
)

Expand Down Expand Up @@ -229,6 +229,8 @@ func (rs *relayerSessionsManager) goProveClaims(
proofsGeneratedCh chan<- []relayer.SessionTree,
failSubmitProofsSessionsCh chan<- []relayer.SessionTree,
) {
logger := rs.logger.With("method", "goProveClaims")

// Separate the sessionTrees into those that failed to generate a proof
// and those that succeeded, then send them on their respective channels.
failedProofs := []relayer.SessionTree{}
Expand All @@ -241,13 +243,15 @@ func (rs *relayerSessionsManager) goProveClaims(
}
// Generate the proof path for the sessionTree using the previously committed
// sessionPathBlock hash.
path := proofkeeper.GetPathForProof(
path := protocol.GetPathForProof(
sessionPathBlock.Hash(),
sessionTree.GetSessionHeader().GetSessionId(),
)

// If the proof cannot be generated, add the sessionTree to the failedProofs.
if _, err := sessionTree.ProveClosest(path); err != nil {
logger.Error().Err(err).Msg("failed to generate proof")

failedProofs = append(failedProofs, sessionTree)
continue
}
Expand Down
16 changes: 11 additions & 5 deletions tests/integration/tokenomics/relay_mining_difficulty_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/pokt-network/poktroll/cmd/poktrolld/cmd"
"github.com/pokt-network/poktroll/pkg/crypto/protocol"
testutilevents "github.com/pokt-network/poktroll/testutil/events"
"github.com/pokt-network/poktroll/testutil/integration"
testutil "github.com/pokt-network/poktroll/testutil/integration"
Expand All @@ -28,7 +29,7 @@ func init() {
}

func TestUpdateRelayMiningDifficulty_NewServiceSeenForTheFirstTime(t *testing.T) {
var claimWindowOpenBlockHash, proofWindowOpenBlockHash []byte
var claimWindowOpenBlockHash, proofWindowOpenBlockHash, proofPathSeedBlockHash []byte

// Create a new integration app
integrationApp := integration.NewCompleteIntegrationApp(t)
Expand Down Expand Up @@ -89,7 +90,7 @@ func TestUpdateRelayMiningDifficulty_NewServiceSeenForTheFirstTime(t *testing.T)
createProofMsg := prooftypes.MsgSubmitProof{
SupplierAddress: integrationApp.DefaultSupplier.Address,
SessionHeader: session.Header,
Proof: getProof(t, trie),
Proof: getProof(t, trie, proofPathSeedBlockHash, session.GetHeader().GetSessionId()),
}
result = integrationApp.RunMsg(t,
&createProofMsg,
Expand Down Expand Up @@ -202,11 +203,16 @@ func prepareSMST(
// getProof returns a proof for the given session for the empty path.
// If there is only one relay in the trie, the proof will be for that single
// relay since it is "closest" to any path provided, empty or not.
func getProof(t *testing.T, trie *smt.SMST) []byte {
func getProof(
t *testing.T,
trie *smt.SMST,
pathSeedBlockHash []byte,
sessionId string,
) []byte {
t.Helper()

emptyPath := make([]byte, trie.PathHasherSize())
proof, err := trie.ProveClosest(emptyPath)
path := protocol.GetPathForProof(pathSeedBlockHash, sessionId)
proof, err := trie.ProveClosest(path)
require.NoError(t, err)

proofBz, err := proof.Marshal()
Expand Down
36 changes: 4 additions & 32 deletions x/proof/keeper/msg_server_submit_proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ package keeper
import (
"bytes"
"context"
"crypto/sha256"
"fmt"
"hash"

cosmoscryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
cosmostypes "github.com/cosmos/cosmos-sdk/types"
Expand All @@ -26,22 +24,6 @@ import (
sharedtypes "github.com/pokt-network/poktroll/x/shared/types"
)

// SMT specification used for the proof verification.
var (
hasher hash.Hash
SmtSpec smt.TrieSpec
)

func init() {
// Use a spec that does not prehash values in the smst. This returns a nil value
// hasher for the proof verification in order to to avoid hashing the value twice.
hasher = sha256.New()
SmtSpec = smt.NewTrieSpec(
hasher, true,
smt.WithValueHasher(nil),
)
}

// SubmitProof is the server handler to submit and store a proof on-chain.
// A proof that's stored on-chain is what leads to rewards (i.e. inflation)
// downstream, making the series of checks a critical part of the protocol.
Expand Down Expand Up @@ -165,7 +147,7 @@ func (k msgServer) SubmitProof(
// TODO_MAINNET(#427): Utilize smt.VerifyCompactClosestProof here to
// reduce on-chain storage requirements for proofs.
// Get the relay request and response from the proof.GetClosestMerkleProof.
relayBz := sparseMerkleClosestProof.GetValueHash(&SmtSpec)
relayBz := sparseMerkleClosestProof.GetValueHash(&protocol.SmtSpec)
relay := &servicetypes.Relay{}
if err = k.cdc.Unmarshal(relayBz, relay); err != nil {
return nil, status.Error(
Expand Down Expand Up @@ -454,7 +436,7 @@ func verifyClosestProof(
proof *smt.SparseMerkleClosestProof,
claimRootHash []byte,
) error {
valid, err := smt.VerifyClosestProof(proof, claimRootHash, &SmtSpec)
valid, err := smt.VerifyClosestProof(proof, claimRootHash, &protocol.SmtSpec)
if err != nil {
return err
}
Expand Down Expand Up @@ -528,24 +510,14 @@ func (k msgServer) validateClosestPath(
// error that may occur due to block height differing from the off-chain part.
k.logger.Info("E2E_DEBUG: height for block hash when verifying the proof", earliestSupplierProofCommitHeight, sessionHeader.GetSessionId())

expectedProofPath := GetPathForProof(proofPathSeedBlockHash, sessionHeader.GetSessionId())
expectedProofPath := protocol.GetPathForProof(proofPathSeedBlockHash, sessionHeader.GetSessionId())
if !bytes.Equal(proof.Path, expectedProofPath) {
return types.ErrProofInvalidProof.Wrapf(
"the proof for the path provided (%x) does not match one expected by the on-chain protocol (%x)",
"the path of the proof provided (%x) does not match one expected by the on-chain protocol (%x)",
proof.Path,
expectedProofPath,
)
}

return nil
}

func GetPathForProof(blockHash []byte, sessionId string) []byte {
// TODO_BLOCKER(@Olshansk): We need to replace the return
// statement below and change all relevant parts in the codebase.
// See the conversation in the following thread for more details: https://github.com/pokt-network/poktroll/pull/406#discussion_r1520790083
path := make([]byte, SmtSpec.PathHasherSize())
copy(path, blockHash)
return path
// return pathHasher.Sum(append(blockHash, []byte(sessionId)...))
}
88 changes: 64 additions & 24 deletions x/proof/keeper/msg_server_submit_proof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,10 @@ var (
func init() {
// The CometBFT header hash is 32 bytes: https://docs.cometbft.com/main/spec/core/data_structures
blockHeaderHash = make([]byte, 32)
expectedMerkleProofPath = keeper.GetPathForProof(blockHeaderHash, "TODO_BLOCKER_session_id_currently_unused")
expectedMerkleProofPath = protocol.GetPathForProof(blockHeaderHash, "TODO_BLOCKER_session_id_currently_unused")
}

func TestMsgServer_SubmitProof_Success(t *testing.T) {
var claimWindowOpenBlockHash []byte

tests := []struct {
desc string
getProofMsgHeight func(
Expand All @@ -75,7 +73,7 @@ func TestMsgServer_SubmitProof_Success(t *testing.T) {
return shared.GetEarliestSupplierProofCommitHeight(
sharedParams,
queryHeight,
claimWindowOpenBlockHash,
blockHeaderHash,
supplierAddr,
)
},
Expand Down Expand Up @@ -165,11 +163,10 @@ func TestMsgServer_SubmitProof_Success(t *testing.T) {
claimMsgHeight := shared.GetEarliestSupplierClaimCommitHeight(
&sharedParams,
sessionHeader.GetSessionEndBlockHeight(),
claimWindowOpenBlockHash,
blockHeaderHash,
supplierAddr,
)
sdkCtx = sdkCtx.WithBlockHeight(claimMsgHeight)
ctx = sdkCtx
ctx = keepertest.SetBlockHeight(ctx, claimMsgHeight)

// Create a valid claim.
claim := createClaimAndStoreBlockHash(
Expand All @@ -183,10 +180,25 @@ func TestMsgServer_SubmitProof_Success(t *testing.T) {
keepers,
)

// Advance the block height to the proof path seed height.
earliestSupplierProofCommitHeight := shared.GetEarliestSupplierProofCommitHeight(
&sharedParams,
sessionHeader.GetSessionEndBlockHeight(),
blockHeaderHash,
supplierAddr,
)
ctx = keepertest.SetBlockHeight(ctx, earliestSupplierProofCommitHeight-1)

// Store proof path seed block hash in the session keeper so that it can
// look it up during proof validation.
keepers.StoreBlockHash(ctx)

// Compute expected proof path.
expectedMerkleProofPath := protocol.GetPathForProof(blockHeaderHash, sessionHeader.GetSessionId())

// Advance the block height to the test proof msg height.
proofMsgHeight := test.getProofMsgHeight(&sharedParams, sessionHeader.GetSessionEndBlockHeight(), supplierAddr)
sdkCtx = sdkCtx.WithBlockHeight(proofMsgHeight)
ctx = sdkCtx
ctx = keepertest.SetBlockHeight(ctx, proofMsgHeight)

proofMsg := newTestProofMsg(t,
supplierAddr,
Expand Down Expand Up @@ -388,12 +400,10 @@ func TestMsgServer_SubmitProof_Error_OutsideOfWindow(t *testing.T) {
}

func TestMsgServer_SubmitProof_Error(t *testing.T) {
var claimWindowOpenBlockHash, proofCommitBlockHash []byte

opts := []keepertest.ProofKeepersOpt{
// Set block hash such that on-chain closest merkle proof validation
// uses the expected path.
keepertest.WithBlockHash(expectedMerkleProofPath),
keepertest.WithBlockHash(blockHeaderHash),
// Set block height to 1 so there is a valid session on-chain.
keepertest.WithBlockHeight(1),
}
Expand Down Expand Up @@ -507,7 +517,7 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) {
claimMsgHeight := shared.GetEarliestSupplierClaimCommitHeight(
&sharedParams,
validSessionHeader.GetSessionEndBlockHeight(),
claimWindowOpenBlockHash,
blockHeaderHash,
supplierAddr,
)
sdkCtx := cosmostypes.UnwrapSDKContext(ctx)
Expand Down Expand Up @@ -986,8 +996,7 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) {
require.NoError(t, err)

// Re-set the block height to the earliest claim commit height to create a new claim.
claimCtx := cosmostypes.UnwrapSDKContext(ctx)
claimCtx = claimCtx.WithBlockHeight(claimMsgHeight)
claimCtx := keepertest.SetBlockHeight(ctx, claimMsgHeight)

// Create a valid claim with the expected merkle root.
claimMsg := newTestClaimMsg(t,
Expand All @@ -1008,9 +1017,9 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) {
expectedErr: status.Error(
codes.FailedPrecondition,
prooftypes.ErrProofInvalidProof.Wrapf(
"the proof for the path provided (%x) does not match one expected by the on-chain protocol (%x)",
"the path of the proof provided (%x) does not match one expected by the on-chain protocol (%x)",
wrongClosestProofPath,
blockHeaderHash,
protocol.GetPathForProof(sdkCtx.HeaderHash(), validSessionHeader.GetSessionId()),
).Error(),
),
},
Expand Down Expand Up @@ -1074,6 +1083,12 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) {
_, err = unclaimedSessionTree.Flush()
require.NoError(t, err)

// Compute expected proof path for the unclaimed session.
expectedMerkleProofPath := protocol.GetPathForProof(
blockHeaderHash,
unclaimedSessionHeader.GetSessionId(),
)

// Construct new proof message using the supplier & session header
// from the session which is *not* expected to be claimed.
return newTestProofMsg(t,
Expand Down Expand Up @@ -1109,8 +1124,7 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) {
require.NoError(t, err)

// Re-set the block height to the earliest claim commit height to create a new claim.
claimCtx := cosmostypes.UnwrapSDKContext(ctx)
claimCtx = claimCtx.WithBlockHeight(claimMsgHeight)
claimCtx := keepertest.SetBlockHeight(ctx, claimMsgHeight)

// Create a claim with the incorrect Merkle root.
wrongMerkleRootClaimMsg := newTestClaimMsg(t,
Expand All @@ -1124,6 +1138,25 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) {
_, err = srv.CreateClaim(claimCtx, wrongMerkleRootClaimMsg)
require.NoError(t, err)

// Construct a valid session tree with 5 relays.
validSessionTree := newFilledSessionTree(
ctx, t,
uint(5),
supplierUid, supplierAddr,
validSessionHeader, validSessionHeader, validSessionHeader,
keyRing,
ringClient,
)

_, err = validSessionTree.Flush()
require.NoError(t, err)

// Compute expected proof path for the session.
expectedMerkleProofPath := protocol.GetPathForProof(
blockHeaderHash,
validSessionHeader.GetSessionId(),
)

return newTestProofMsg(t,
supplierAddr,
validSessionHeader,
Expand Down Expand Up @@ -1162,16 +1195,23 @@ func TestMsgServer_SubmitProof_Error(t *testing.T) {
// Submit the corresponding proof.
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
// Increment the block height to the test proof height.
proofMsg := test.newProofMsg(t)
proofMsgHeight := shared.GetEarliestSupplierProofCommitHeight(

// Advance the block height to the proof path seed height.
earliestSupplierProofCommitHeight := shared.GetEarliestSupplierProofCommitHeight(
&sharedParams,
proofMsg.GetSessionHeader().GetSessionEndBlockHeight(),
proofCommitBlockHash,
blockHeaderHash,
proofMsg.GetSupplierAddress(),
)
ctx = keepertest.SetBlockHeight(ctx, earliestSupplierProofCommitHeight-1)

// Store proof path seed block hash in the session keeper so that it can
// look it up during proof validation.
keepers.StoreBlockHash(ctx)

ctx = cosmostypes.UnwrapSDKContext(ctx).WithBlockHeight(proofMsgHeight)
// Advance the block height to the earliest proof commit height.
ctx = keepertest.SetBlockHeight(ctx, earliestSupplierProofCommitHeight)

submitProofRes, err := srv.SubmitProof(ctx, proofMsg)

Expand Down Expand Up @@ -1385,7 +1425,7 @@ func getClosestRelayDifficultyBits(

// Extract the Relay (containing the RelayResponse & RelayRequest) from the merkle proof.
relay := new(servicetypes.Relay)
relayBz := closestMerkleProof.GetValueHash(&keeper.SmtSpec)
relayBz := closestMerkleProof.GetValueHash(&protocol.SmtSpec)
err = relay.Unmarshal(relayBz)
require.NoError(t, err)

Expand Down
Loading

0 comments on commit bd42b13

Please sign in to comment.