From 72b6be45b754c67f12d48267466686ad99d42e27 Mon Sep 17 00:00:00 2001 From: Yashk767 <76935991+Yashk767@users.noreply.github.com> Date: Thu, 17 Oct 2024 13:39:18 +0530 Subject: [PATCH] feat: added commitment verification layer for data during reveal after removing waitForBlockCompletion check for voting transactions (#1254) * refactor: added verification layer on commit data from memory * refactor: removed waitForBlockCompletion from propose and reveal * fix: returned err instead of nil in GetConfirmedBlock * reafctor: fixed tests * refactor: fixed lint errors * fix: returned supported struct type and added a condition to attempt confirmBlock when node just started voting * refactor: fixed tests after rebasing * refactor: removed commented unwanted tests * refactor: Fetched selected commit data first and than applied commitment verification * feat: saved commitment in the memory/file along with commit data and reused it in reveal --- cmd/commit.go | 84 ++++++++++---- cmd/commit_test.go | 102 +++++------------ cmd/interface.go | 2 +- cmd/mocks/utils_cmd_interface.go | 18 +-- cmd/propose.go | 52 ++++----- cmd/propose_test.go | 24 ---- cmd/vote.go | 176 +++++++++++------------------ cmd/vote_test.go | 176 ++++++++++++++++------------- core/types/block.go | 7 ++ core/types/vote.go | 1 + utils/block.go | 9 ++ utils/block_test.go | 66 +++++++++++ utils/common.go | 3 +- utils/common_test.go | 3 +- utils/interface.go | 7 +- utils/mocks/block_manager_utils.go | 82 +++++++++++--- utils/mocks/file_utils.go | 36 +++--- utils/mocks/utils.go | 24 ++++ utils/struct-utils.go | 15 +++ 19 files changed, 505 insertions(+), 382 deletions(-) diff --git a/cmd/commit.go b/cmd/commit.go index c2e3186fe..6927c9925 100644 --- a/cmd/commit.go +++ b/cmd/commit.go @@ -146,18 +146,12 @@ func (*UtilsStruct) HandleCommitState(ctx context.Context, client *ethclient.Cli /* Commit finally commits the data to the smart contract. It calculates the commitment to send using the merkle tree root and the seed. */ -func (*UtilsStruct) Commit(ctx context.Context, client *ethclient.Client, config types.Configurations, account types.Account, epoch uint32, latestHeader *Types.Header, stateBuffer uint64, seed []byte, values []*big.Int) (common.Hash, error) { +func (*UtilsStruct) Commit(ctx context.Context, client *ethclient.Client, config types.Configurations, account types.Account, epoch uint32, latestHeader *Types.Header, stateBuffer uint64, commitmentToSend [32]byte) (common.Hash, error) { if state, err := razorUtils.GetBufferedState(latestHeader, stateBuffer, config.BufferPercent); err != nil || state != 0 { log.Error("Not commit state") return core.NilHash, err } - commitmentToSend, err := CalculateCommitment(seed, values) - if err != nil { - log.Error("Error in getting commitment: ", err) - return core.NilHash, err - } - txnOpts := razorUtils.GetTxnOpts(ctx, types.TransactionOptions{ Client: client, ChainId: core.ChainId, @@ -215,7 +209,7 @@ func CalculateCommitment(seed []byte, values []*big.Int) ([32]byte, error) { return commitmentToSend, nil } -func VerifyCommitment(ctx context.Context, client *ethclient.Client, account types.Account, keystorePath string, epoch uint32, values []*big.Int) (bool, error) { +func VerifyCommitment(ctx context.Context, client *ethclient.Client, account types.Account, commitmentFetched [32]byte) (bool, error) { commitmentStruct, err := razorUtils.GetCommitment(ctx, client, account.Address) if err != nil { log.Error("Error in getting commitments: ", err) @@ -223,23 +217,71 @@ func VerifyCommitment(ctx context.Context, client *ethclient.Client, account typ } log.Debugf("VerifyCommitment: CommitmentStruct: %+v", commitmentStruct) - seed, err := CalculateSeed(ctx, client, account, keystorePath, epoch) - if err != nil { - log.Error("Error in calculating seed: ", err) - return false, err + if commitmentFetched == commitmentStruct.CommitmentHash { + log.Debug("VerifyCommitment: Commitment fetched from memory/file system for given values is EQUAL to commitment of the epoch") + return true, nil } + log.Debug("VerifyCommitment: Commitment fetched from memory/file system for given values DOES NOT MATCH with commitment in the epoch") + return false, nil +} + +func GetCommittedDataForEpoch(ctx context.Context, client *ethclient.Client, account types.Account, epoch uint32, rogueData types.Rogue) (types.CommitFileData, error) { + // Attempt to fetch global commit data from memory if epoch matches + if globalCommitDataStruct.Epoch == epoch { + log.Debugf("Epoch in global commit data is equal to current epoch %v. Fetching commit data from memory!", epoch) + } else { + // Fetch from file if memory data is outdated + log.Debugf("GetCommittedDataForEpoch: Global commit data epoch %v doesn't match current epoch %v. Fetching from file!", globalCommitDataStruct.Epoch, epoch) + log.Info("Getting the commit data from file...") + fileName, err := pathUtils.GetCommitDataFileName(account.Address) + if err != nil { + return types.CommitFileData{}, err + } + + log.Debug("GetCommittedDataForEpoch: Commit data file path: ", fileName) + commitDataFromFile, err := fileUtils.ReadFromCommitJsonFile(fileName) + if err != nil { + return types.CommitFileData{}, err + } - calculatedCommitment, err := CalculateCommitment(seed, values) + log.Debug("GetCommittedDataForEpoch: Committed data from file: ", commitDataFromFile) + if commitDataFromFile.Epoch != epoch { + log.Errorf("File %s doesn't contain latest committed data", fileName) + return types.CommitFileData{}, errors.New("commit data file doesn't contain latest committed data") + } + + // Update global commit data struct since the file data is valid + updateGlobalCommitDataStruct(types.CommitData{ + Leaves: commitDataFromFile.Leaves, + SeqAllottedCollections: commitDataFromFile.SeqAllottedCollections, + AssignedCollections: commitDataFromFile.AssignedCollections, + }, commitDataFromFile.Commitment, epoch) + } + + // Verify the final selected commit data + log.Debugf("Verifying commit data for epoch %v...", epoch) + isValid, err := VerifyCommitment(ctx, client, account, globalCommitDataStruct.Commitment) if err != nil { - log.Error("Error in calculating commitment for given committed values: ", err) - return false, err + return types.CommitFileData{}, err + } + if !isValid { + return types.CommitFileData{}, errors.New("commitment verification failed for selected commit data") } - log.Debug("VerifyCommitment: Calculated commitment: ", calculatedCommitment) - if calculatedCommitment == commitmentStruct.CommitmentHash { - log.Debug("VerifyCommitment: Calculated commitment for given values is EQUAL to commitment of the epoch") - return true, nil + // If rogue mode is enabled, alter the commitment data + if rogueData.IsRogue && utils.Contains(rogueData.RogueMode, "reveal") { + log.Warn("YOU ARE REVEALING VALUES IN ROGUE MODE, THIS CAN INCUR PENALTIES!") + globalCommitDataStruct.Leaves = generateRogueCommittedData(len(globalCommitDataStruct.Leaves)) + log.Debugf("Global Commit data struct in rogue mode: %+v", globalCommitDataStruct) } - log.Debug("VerifyCommitment: Calculated commitment for given values DOES NOT MATCH with commitment in the epoch") - return false, nil + + return globalCommitDataStruct, nil +} + +func generateRogueCommittedData(length int) []*big.Int { + var rogueCommittedData []*big.Int + for i := 0; i < length; i++ { + rogueCommittedData = append(rogueCommittedData, razorUtils.GetRogueRandomValue(10000000)) + } + return rogueCommittedData } diff --git a/cmd/commit_test.go b/cmd/commit_test.go index 36cfbc6fe..1945f0aca 100644 --- a/cmd/commit_test.go +++ b/cmd/commit_test.go @@ -26,12 +26,11 @@ func TestCommit(t *testing.T) { config types.Configurations latestHeader *Types.Header stateBuffer uint64 - seed []byte epoch uint32 + commitment [32]byte ) type args struct { - values []*big.Int state int64 stateErr error commitTxn *Types.Transaction @@ -47,7 +46,6 @@ func TestCommit(t *testing.T) { { name: "Test 1: When Commit function executes successfully", args: args{ - values: []*big.Int{big.NewInt(1)}, state: 0, stateErr: nil, commitTxn: &Types.Transaction{}, @@ -60,7 +58,6 @@ func TestCommit(t *testing.T) { { name: "Test 2: When there is an error in getting state", args: args{ - values: []*big.Int{big.NewInt(1)}, stateErr: errors.New("state error"), commitTxn: &Types.Transaction{}, commitErr: nil, @@ -72,7 +69,6 @@ func TestCommit(t *testing.T) { { name: "Test 3: When Commit transaction fails", args: args{ - values: []*big.Int{big.NewInt(1)}, state: 0, stateErr: nil, commitTxn: &Types.Transaction{}, @@ -82,29 +78,18 @@ func TestCommit(t *testing.T) { want: core.NilHash, wantErr: errors.New("commit error"), }, - { - name: "Test 4: When there is an error in getting commitmentHashString as values is nil", - args: args{ - values: []*big.Int{}, - }, - want: core.NilHash, - wantErr: errors.New("Error in getting merkle tree: values are nil, cannot create merkle tree"), - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { SetUpMockInterfaces() - utils.MerkleInterface = &utils.MerkleTreeStruct{} - merkleUtils = utils.MerkleInterface - utilsMock.On("GetBufferedState", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.state, tt.args.stateErr) utilsMock.On("GetTxnOpts", mock.Anything, mock.Anything).Return(TxnOpts) voteManagerMock.On("Commit", mock.AnythingOfType("*ethclient.Client"), mock.AnythingOfType("*bind.TransactOpts"), mock.AnythingOfType("uint32"), mock.Anything).Return(tt.args.commitTxn, tt.args.commitErr) transactionMock.On("Hash", mock.AnythingOfType("*types.Transaction")).Return(tt.args.hash) utils := &UtilsStruct{} - got, err := utils.Commit(context.Background(), client, config, account, epoch, latestHeader, stateBuffer, seed, tt.args.values) + got, err := utils.Commit(context.Background(), client, config, account, epoch, latestHeader, stateBuffer, commitment) if got != tt.want { t.Errorf("Txn hash for Commit function, got = %v, want = %v", got, tt.want) } @@ -492,18 +477,13 @@ func TestCalculateCommitment(t *testing.T) { func TestVerifyCommitment(t *testing.T) { var ( - client *ethclient.Client - account types.Account - keystorePath string - epoch uint32 + client *ethclient.Client + account types.Account ) type args struct { - values []*big.Int - commitmentHashString string - commitmentErr error - secret string - secretErr error - salt string + commitmentFetchedString string + commitmentHashString string + commitmentErr error } tests := []struct { name string @@ -512,82 +492,53 @@ func TestVerifyCommitment(t *testing.T) { wantErr bool }{ { - name: "Test 1: When commitmentHashString is verified successfully", + name: "Test 1: When commitmentFetched matches the commitment in the epoch", args: args{ - commitmentHashString: "22c9ba074e44d0009116b244a5cece9e9ade85af486e1f4f8db8e304e6605bea", - values: []*big.Int{big.NewInt(200), big.NewInt(100)}, - salt: "03bceb412a8c973dbb960f1353ba91cf6ca10dfde21c911054cf1e61f0d28e0b", - secret: "0f7f6290794dae00bf7c673d36fa2a5b447d2c8c60e9a4220b7ab65be80547a9", + commitmentHashString: "22c9ba074e44d0009116b244a5cece9e9ade85af486e1f4f8db8e304e6605bea", + commitmentFetchedString: "22c9ba074e44d0009116b244a5cece9e9ade85af486e1f4f8db8e304e6605bea", }, want: true, wantErr: false, }, { - name: "Test 2: When commitmentHashString is not verified successfully", + name: "Test 2: When commitmentFetched does not match the commitment in the epoch", args: args{ - commitmentHashString: "23cabb074e44d0009116b244a5cece9e9ade85af486e1f4f8db8e304e6605bea", - values: []*big.Int{big.NewInt(200), big.NewInt(100)}, - salt: "03bceb412a8c973dbb960f1353ba91cf6ca10dfde21c911054cf1e61f0d28e0b", - secret: "0f7f6290794dae00bf7c673d36fa2a5b447d2c8c60e9a4220b7ab65be80547a9", + commitmentHashString: "23cabb074e44d0009116b244a5cece9e9ade85af486e1f4f8db8e304e6605bea", + commitmentFetchedString: "22c9ba074e44d0009116b244a5cece9e9ade85af486e1f4f8db8e304e6605bea", }, want: false, wantErr: false, }, { - name: "Test 3: When there is an error in getting commitmentHashString", + name: "Test 3: When there is an error in fetching commitment from the blockchain", args: args{ commitmentErr: errors.New("getCommitment error"), }, want: false, wantErr: true, }, - { - name: "Test 4: When there is error in calculating commitmentHashString", - args: args{ - commitmentHashString: "22c9ba074e44d0009116b244a5cece9e9ade85af486e1f4f8db8e304e6605bea", - values: []*big.Int{}, - }, - want: false, - wantErr: true, - }, - { - name: "Test 5: When there is error in calculating seed", - args: args{ - commitmentHashString: "22c9ba074e44d0009116b244a5cece9e9ade85af486e1f4f8db8e304e6605bea", - values: []*big.Int{big.NewInt(200), big.NewInt(100)}, - secretErr: errors.New("secret error"), - }, - want: false, - wantErr: true, - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { var commitmentHash [32]byte - var salt [32]byte - var secret []byte + var commitmentFetched [32]byte - if tt.args.commitmentHashString != "" { + // Convert the commitmentFetchedString to a [32]byte format + if tt.args.commitmentFetchedString != "" { var err error - commitmentHash, err = convertStringToByte32(tt.args.commitmentHashString) + commitmentFetched, err = convertStringToByte32(tt.args.commitmentFetchedString) if err != nil { - t.Errorf("Error in decoding commitmentHashString: %v", err) + t.Errorf("Error in decoding commitmentFetchedString: %v", err) return } } - if tt.args.secret != "" { - var err error - secret, err = hex.DecodeString(tt.args.secret) - if err != nil { - t.Errorf("Error in decoding secret: %v", err) - return - } - } - if tt.args.salt != "" { + + // Convert the commitmentHashString to a [32]byte format + if tt.args.commitmentHashString != "" { var err error - salt, err = convertStringToByte32(tt.args.salt) + commitmentHash, err = convertStringToByte32(tt.args.commitmentHashString) if err != nil { - t.Errorf("Error in decoding salt: %v", err) + t.Errorf("Error in decoding commitmentHashString: %v", err) return } } @@ -598,9 +549,8 @@ func TestVerifyCommitment(t *testing.T) { merkleUtils = utils.MerkleInterface utilsMock.On("GetCommitment", mock.Anything, mock.Anything, mock.Anything).Return(types.Commitment{CommitmentHash: commitmentHash}, tt.args.commitmentErr) - cmdUtilsMock.On("GetSalt", mock.Anything, mock.Anything, mock.Anything).Return(salt, nil) - cmdUtilsMock.On("CalculateSecret", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil, secret, tt.args.secretErr) - got, err := VerifyCommitment(context.Background(), client, account, keystorePath, epoch, tt.args.values) + + got, err := VerifyCommitment(context.Background(), client, account, commitmentFetched) if (err != nil) != tt.wantErr { t.Errorf("VerifyCommitment() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/cmd/interface.go b/cmd/interface.go index 2481e3b15..3905c9ee9 100644 --- a/cmd/interface.go +++ b/cmd/interface.go @@ -166,7 +166,7 @@ type UtilsCmdInterface interface { ClaimBlockReward(ctx context.Context, options types.TransactionOptions) (common.Hash, error) GetSalt(ctx context.Context, client *ethclient.Client, epoch uint32) ([32]byte, error) HandleCommitState(ctx context.Context, client *ethclient.Client, epoch uint32, seed []byte, commitParams *types.CommitParams, rogueData types.Rogue) (types.CommitData, error) - Commit(ctx context.Context, client *ethclient.Client, config types.Configurations, account types.Account, epoch uint32, latestHeader *Types.Header, stateBuffer uint64, seed []byte, values []*big.Int) (common.Hash, error) + Commit(ctx context.Context, client *ethclient.Client, config types.Configurations, account types.Account, epoch uint32, latestHeader *Types.Header, stateBuffer uint64, commitment [32]byte) (common.Hash, error) ListAccounts() ([]accounts.Account, error) AssignAmountInWei(flagSet *pflag.FlagSet) (*big.Int, error) ExecuteTransfer(flagSet *pflag.FlagSet) diff --git a/cmd/mocks/utils_cmd_interface.go b/cmd/mocks/utils_cmd_interface.go index 1e59ba4f9..5827ad67f 100644 --- a/cmd/mocks/utils_cmd_interface.go +++ b/cmd/mocks/utils_cmd_interface.go @@ -298,25 +298,25 @@ func (_m *UtilsCmdInterface) ClaimCommission(flagSet *pflag.FlagSet) { _m.Called(flagSet) } -// Commit provides a mock function with given fields: ctx, client, config, account, epoch, latestHeader, stateBuffer, seed, values -func (_m *UtilsCmdInterface) Commit(ctx context.Context, client *ethclient.Client, config types.Configurations, account types.Account, epoch uint32, latestHeader *coretypes.Header, stateBuffer uint64, seed []byte, values []*big.Int) (common.Hash, error) { - ret := _m.Called(ctx, client, config, account, epoch, latestHeader, stateBuffer, seed, values) +// Commit provides a mock function with given fields: ctx, client, config, account, epoch, latestHeader, stateBuffer, commitment +func (_m *UtilsCmdInterface) Commit(ctx context.Context, client *ethclient.Client, config types.Configurations, account types.Account, epoch uint32, latestHeader *coretypes.Header, stateBuffer uint64, commitment [32]byte) (common.Hash, error) { + ret := _m.Called(ctx, client, config, account, epoch, latestHeader, stateBuffer, commitment) var r0 common.Hash var r1 error - if rf, ok := ret.Get(0).(func(context.Context, *ethclient.Client, types.Configurations, types.Account, uint32, *coretypes.Header, uint64, []byte, []*big.Int) (common.Hash, error)); ok { - return rf(ctx, client, config, account, epoch, latestHeader, stateBuffer, seed, values) + if rf, ok := ret.Get(0).(func(context.Context, *ethclient.Client, types.Configurations, types.Account, uint32, *coretypes.Header, uint64, [32]byte) (common.Hash, error)); ok { + return rf(ctx, client, config, account, epoch, latestHeader, stateBuffer, commitment) } - if rf, ok := ret.Get(0).(func(context.Context, *ethclient.Client, types.Configurations, types.Account, uint32, *coretypes.Header, uint64, []byte, []*big.Int) common.Hash); ok { - r0 = rf(ctx, client, config, account, epoch, latestHeader, stateBuffer, seed, values) + if rf, ok := ret.Get(0).(func(context.Context, *ethclient.Client, types.Configurations, types.Account, uint32, *coretypes.Header, uint64, [32]byte) common.Hash); ok { + r0 = rf(ctx, client, config, account, epoch, latestHeader, stateBuffer, commitment) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(common.Hash) } } - if rf, ok := ret.Get(1).(func(context.Context, *ethclient.Client, types.Configurations, types.Account, uint32, *coretypes.Header, uint64, []byte, []*big.Int) error); ok { - r1 = rf(ctx, client, config, account, epoch, latestHeader, stateBuffer, seed, values) + if rf, ok := ret.Get(1).(func(context.Context, *ethclient.Client, types.Configurations, types.Account, uint32, *coretypes.Header, uint64, [32]byte) error); ok { + r1 = rf(ctx, client, config, account, epoch, latestHeader, stateBuffer, commitment) } else { r1 = ret.Error(1) } diff --git a/cmd/propose.go b/cmd/propose.go index 686b99370..6bf71267c 100644 --- a/cmd/propose.go +++ b/cmd/propose.go @@ -159,37 +159,31 @@ func (*UtilsStruct) Propose(ctx context.Context, client *ethclient.Client, confi return err } proposeTxn := transactionUtils.Hash(txn) - log.Info("Txn Hash: ", proposeTxn.Hex()) + log.Info("Propose Transaction Hash: ", proposeTxn.Hex()) if proposeTxn != core.NilHash { - waitForBlockCompletionErr := razorUtils.WaitForBlockCompletion(client, proposeTxn.Hex()) - if waitForBlockCompletionErr != nil { - log.Error("Error in WaitForBlockCompletionErr for propose: ", waitForBlockCompletionErr) - return waitForBlockCompletionErr - } else { - // Saving proposed data after successful propose - log.Debug("Updating global propose data struct...") - updateGlobalProposedDataStruct(types.ProposeFileData{ - MediansData: medians, - RevealedDataMaps: revealedDataMaps, - RevealedCollectionIds: ids, - Epoch: epoch, - }) - log.Debugf("Propose: Global propose data struct: %+v", globalProposedDataStruct) - - log.Debug("Saving proposed data for recovery...") - fileName, err := pathUtils.GetProposeDataFileName(account.Address) - if err != nil { - log.Error("Error in getting file name to save median data: ", err) - return err - } - log.Debug("Propose: Propose data file path: ", fileName) - err = fileUtils.SaveDataToProposeJsonFile(fileName, globalProposedDataStruct) - if err != nil { - log.Errorf("Error in saving data to file %s: %v", fileName, err) - return err - } - log.Debug("Data saved!") + // Saving proposed data after getting the transaction hash + log.Debug("Updating global propose data struct...") + updateGlobalProposedDataStruct(types.ProposeFileData{ + MediansData: medians, + RevealedDataMaps: revealedDataMaps, + RevealedCollectionIds: ids, + Epoch: epoch, + }) + log.Debugf("Propose: Global propose data struct: %+v", globalProposedDataStruct) + + log.Debug("Saving proposed data for recovery...") + fileName, err := pathUtils.GetProposeDataFileName(account.Address) + if err != nil { + log.Error("Error in getting file name to save median data: ", err) + return err + } + log.Debug("Propose: Propose data file path: ", fileName) + err = fileUtils.SaveDataToProposeJsonFile(fileName, globalProposedDataStruct) + if err != nil { + log.Errorf("Error in saving data to file %s: %v", fileName, err) + return err } + log.Debug("Data saved!") } return nil diff --git a/cmd/propose_test.go b/cmd/propose_test.go index 2a358103b..0b1fbf442 100644 --- a/cmd/propose_test.go +++ b/cmd/propose_test.go @@ -76,7 +76,6 @@ func TestPropose(t *testing.T) { proposeTxn *Types.Transaction proposeErr error hash common.Hash - waitForBlockCompletionErr error } tests := []struct { name string @@ -480,28 +479,6 @@ func TestPropose(t *testing.T) { }, wantErr: errors.New("smallestStakerId error"), }, - { - name: "Test 19: When there is an error in waitForCompletion", - args: args{ - state: 2, - staker: bindings.StructsStaker{}, - numStakers: 5, - biggestStake: big.NewInt(1).Mul(big.NewInt(5356), big.NewInt(1e18)), - biggestStakerId: 2, - salt: saltBytes32, - iteration: 1, - numOfProposedBlocks: 3, - sortedProposedBlockIds: []uint32{2, 0, 1}, - maxAltBlocks: 4, - lastIteration: big.NewInt(5), - lastProposedBlockStruct: bindings.StructsBlock{}, - medians: []*big.Int{big.NewInt(6701548), big.NewInt(478307)}, - proposeTxn: &Types.Transaction{}, - hash: common.BigToHash(big.NewInt(1)), - waitForBlockCompletionErr: errors.New("waitForBlockCompletion error"), - }, - wantErr: errors.New("waitForBlockCompletion error"), - }, } for _, tt := range tests { SetUpMockInterfaces() @@ -526,7 +503,6 @@ func TestPropose(t *testing.T) { utilsMock.On("GetTxnOpts", mock.Anything, mock.Anything).Return(TxnOpts) blockManagerMock.On("Propose", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.args.proposeTxn, tt.args.proposeErr) transactionMock.On("Hash", mock.Anything).Return(tt.args.hash) - utilsMock.On("WaitForBlockCompletion", mock.AnythingOfType("*ethclient.Client"), mock.AnythingOfType("string")).Return(tt.args.waitForBlockCompletionErr) utils := &UtilsStruct{} t.Run(tt.name, func(t *testing.T) { diff --git a/cmd/vote.go b/cmd/vote.go index 7943fc4cb..2f7b41107 100644 --- a/cmd/vote.go +++ b/cmd/vote.go @@ -321,7 +321,24 @@ func (*UtilsStruct) HandleBlock(client *ethclient.Client, account types.Account, case 4: log.Debugf("Last verification: %d", lastVerification) log.Debugf("Block confirmed: %d", blockConfirmed) - if lastVerification == epoch && blockConfirmed < epoch { + + if blockConfirmed >= epoch { + log.Debug("Block is already confirmed for this epoch!") + break + } + + confirmedBlock, err := razorUtils.GetConfirmedBlocks(ctx, client, epoch) + if err != nil { + log.Error(err) + break + } + + if confirmedBlock.ProposerId != 0 { + log.Infof("Block is already confirmed, setting blockConfirmed (%d) to current epoch (%d)", blockConfirmed, epoch) + blockConfirmed = epoch + break + } + if (lastVerification == epoch || lastVerification == 0) && blockConfirmed < epoch { txn, err := cmdUtils.ClaimBlockReward(ctx, types.TransactionOptions{ Client: client, ChainId: core.ChainId, @@ -337,12 +354,7 @@ func (*UtilsStruct) HandleBlock(client *ethclient.Client, account types.Account, break } if txn != core.NilHash { - waitForBlockCompletionErr := razorUtils.WaitForBlockCompletion(client, txn.Hex()) - if waitForBlockCompletionErr != nil { - log.Error("Error in WaitForBlockCompletion for claimBlockReward: ", err) - break - } - blockConfirmed = epoch + log.Info("Confirm Transaction Hash: ", txn) } } case -1: @@ -364,7 +376,8 @@ func (*UtilsStruct) InitiateCommit(ctx context.Context, client *ethclient.Client log.Debug("InitiateCommit: Epoch last committed: ", lastCommit) if lastCommit >= epoch { - log.Debugf("Cannot commit in epoch %d because last committed epoch is %d", epoch, lastCommit) + // Clearing up the cache storing API results as the staker has already committed successfully + commitParams.LocalCache.ClearAll() return nil } @@ -411,11 +424,17 @@ func (*UtilsStruct) InitiateCommit(ctx context.Context, client *ethclient.Client } log.Debug("InitiateCommit: Commit Data: ", commitData) - commitTxn, err := cmdUtils.Commit(ctx, client, config, account, epoch, latestHeader, stateBuffer, seed, commitData.Leaves) + commitmentToSend, err := CalculateCommitment(seed, commitData.Leaves) + if err != nil { + log.Error("Error in getting commitment: ", err) + return err + } + + commitTxn, err := cmdUtils.Commit(ctx, client, config, account, epoch, latestHeader, stateBuffer, commitmentToSend) if err != nil { return errors.New("Error in committing data: " + err.Error()) } - log.Debug("InitiateCommit: Commit Transaction Hash: ", commitTxn) + log.Info("InitiateCommit: Commit Transaction Hash: ", commitTxn) if commitTxn != core.NilHash { log.Debug("Saving committed data for recovery...") fileName, err := pathUtils.GetCommitDataFileName(account.Address) @@ -424,24 +443,15 @@ func (*UtilsStruct) InitiateCommit(ctx context.Context, client *ethclient.Client } log.Debug("InitiateCommit: Commit data file path: ", fileName) - err = fileUtils.SaveDataToCommitJsonFile(fileName, epoch, commitData) + err = fileUtils.SaveDataToCommitJsonFile(fileName, epoch, commitData, commitmentToSend) if err != nil { return errors.New("Error in saving data to file" + fileName + ": " + err.Error()) } log.Debug("Data saved!") - log.Debug("Checking for commit transaction status with transaction hash: ", commitTxn) - waitForBlockCompletionErr := razorUtils.WaitForBlockCompletion(client, commitTxn.Hex()) - if waitForBlockCompletionErr != nil { - log.Error("Error in WaitForBlockCompletion for commit: ", waitForBlockCompletionErr) - return errors.New("error in sending commit transaction") - } log.Debug("Updating GlobalCommitDataStruct with latest commitData and epoch...") - updateGlobalCommitDataStruct(commitData, epoch) + updateGlobalCommitDataStruct(commitData, commitmentToSend, epoch) log.Debugf("InitiateCommit: Global commit data struct: %+v", globalCommitDataStruct) - - log.Debug("Clearing up the cache storing API results after successful commit...") - commitParams.LocalCache.ClearAll() } return nil } @@ -477,102 +487,41 @@ func (*UtilsStruct) InitiateReveal(ctx context.Context, client *ethclient.Client return err } - if globalCommitDataStruct.Epoch != epoch { - log.Debugf("InitiateReveal: Epoch in global commit data: %v is not equal to current epoch: %v", globalCommitDataStruct.Epoch, epoch) - log.Info("Getting the commit data from file...") - fileName, err := pathUtils.GetCommitDataFileName(account.Address) - if err != nil { - log.Error("Error in getting file name to save committed data: ", err) - return err - } - log.Debug("InitiateReveal: Commit data file path: ", fileName) - committedDataFromFile, err := fileUtils.ReadFromCommitJsonFile(fileName) - if err != nil { - log.Errorf("Error in getting committed data from file %s: %v", fileName, err) - return err - } - log.Debug("InitiateReveal: Committed data from file: ", committedDataFromFile) - if committedDataFromFile.Epoch != epoch { - log.Errorf("File %s doesn't contain latest committed data", fileName) - return errors.New("commit data file doesn't contain latest committed data") - } - log.Debug("Verifying commit data from file...") - razorPath, err := pathUtils.GetDefaultPath() - if err != nil { - return err - } - log.Debugf("InitiateReveal: .razor directory path: %s", razorPath) - keystorePath := filepath.Join(razorPath, "keystore_files") - log.Debugf("InitiateReveal: Keystore file path: %s", keystorePath) + razorPath, err := pathUtils.GetDefaultPath() + if err != nil { + return err + } + log.Debugf("InitiateReveal: .razor directory path: %s", razorPath) + keystorePath := filepath.Join(razorPath, "keystore_files") + log.Debugf("InitiateReveal: Keystore file path: %s", keystorePath) - log.Debugf("InitiateReveal: Calling VerifyCommitment() for address %v with arguments epoch = %v, values = %v", account.Address, epoch, committedDataFromFile.Leaves) - isCommittedDataFromFileValid, err := VerifyCommitment(ctx, client, account, keystorePath, epoch, committedDataFromFile.Leaves) - if err != nil { - log.Error("Error in verifying commitment for commit data from file: ", err) - return err - } - if !isCommittedDataFromFileValid { - log.Infof("Not using data from file! as commitment calculated for data from commit data file is not equal to staker's commitment for this epoch.") - return errors.New("commitment verification for commit file data failed") - } - log.Debug("Updating global commit data struct...") - updateGlobalCommitDataStruct(types.CommitData{ - Leaves: committedDataFromFile.Leaves, - SeqAllottedCollections: committedDataFromFile.SeqAllottedCollections, - AssignedCollections: committedDataFromFile.AssignedCollections, - }, epoch) - log.Debugf("InitiateReveal: Global Commit data struct: %+v", globalCommitDataStruct) - } - if rogueData.IsRogue && utils.Contains(rogueData.RogueMode, "reveal") { - log.Warn("YOU ARE REVEALING VALUES IN ROGUE MODE, THIS CAN INCUR PENALTIES!") - var rogueCommittedData []*big.Int - for i := 0; i < len(globalCommitDataStruct.Leaves); i++ { - rogueCommittedData = append(rogueCommittedData, razorUtils.GetRogueRandomValue(10000000)) - } - globalCommitDataStruct.Leaves = rogueCommittedData - log.Debugf("InitiateReveal: Global Commit data struct in rogue mode: %+v", globalCommitDataStruct) + // Consolidated commitment verification for commit data being fetched from memory or file + commitData, err := GetCommittedDataForEpoch(ctx, client, account, epoch, rogueData) + if err != nil { + return err } - if globalCommitDataStruct.Epoch == epoch { - razorPath, err := pathUtils.GetDefaultPath() - if err != nil { - return err - } - log.Debug("InitiateReveal: .razor directory path: ", razorPath) - keystorePath := filepath.Join(razorPath, "keystore_files") - log.Debug("InitiateReveal: Keystore file path: ", keystorePath) + log.Debugf("InitiateReveal: Calling CalculateSecret() with argument epoch = %d, keystorePath = %s, chainId = %s", epoch, keystorePath, core.ChainId) + signature, _, err := cmdUtils.CalculateSecret(account, epoch, keystorePath, core.ChainId) + if err != nil { + return err + } + log.Debug("InitiateReveal: Signature: ", signature) + log.Debug("InitiateReveal: Assigned Collections: ", commitData.AssignedCollections) + log.Debug("InitiateReveal: SeqAllottedCollections: ", commitData.SeqAllottedCollections) + log.Debug("InitiateReveal: Leaves: ", commitData.Leaves) - log.Debugf("InitiateReveal: Calling CalculateSecret() with argument epoch = %d, keystorePath = %s, chainId = %s", epoch, keystorePath, core.ChainId) - signature, _, err := cmdUtils.CalculateSecret(account, epoch, keystorePath, core.ChainId) - if err != nil { - return err - } - log.Debug("InitiateReveal: Signature: ", signature) - log.Debug("InitiateReveal: Assigned Collections: ", globalCommitDataStruct.AssignedCollections) - log.Debug("InitiateReveal: SeqAllottedCollections: ", globalCommitDataStruct.SeqAllottedCollections) - log.Debug("InitiateReveal: Leaves: ", globalCommitDataStruct.Leaves) - - commitDataToSend := types.CommitData{ - Leaves: globalCommitDataStruct.Leaves, - AssignedCollections: globalCommitDataStruct.AssignedCollections, - SeqAllottedCollections: globalCommitDataStruct.SeqAllottedCollections, - } - log.Debugf("InitiateReveal: Calling Reveal() with arguments epoch = %d, commitDataToSend = %+v, signature = %v", epoch, commitDataToSend, signature) - revealTxn, err := cmdUtils.Reveal(ctx, client, config, account, epoch, latestHeader, stateBuffer, commitDataToSend, signature) - if err != nil { - return errors.New("Reveal error: " + err.Error()) - } - if revealTxn != core.NilHash { - waitForBlockCompletionErr := razorUtils.WaitForBlockCompletion(client, revealTxn.Hex()) - if waitForBlockCompletionErr != nil { - log.Error("Error in WaitForBlockCompletionErr for reveal: ", waitForBlockCompletionErr) - return err - } - } - } else { - log.Error("The commit data is outdated, does not match with the latest epoch") - return errors.New("outdated commit data") + commitDataToSend := types.CommitData{ + Leaves: commitData.Leaves, + AssignedCollections: commitData.AssignedCollections, + SeqAllottedCollections: commitData.SeqAllottedCollections, + } + log.Debugf("InitiateReveal: Calling Reveal() with arguments epoch = %d, commitDataToSend = %+v, signature = %v", epoch, commitDataToSend, signature) + revealTxn, err := cmdUtils.Reveal(ctx, client, config, account, epoch, latestHeader, stateBuffer, commitDataToSend, signature) + if err != nil { + return errors.New("Reveal error: " + err.Error()) } + log.Info("InitiateReveal: Reveal Transaction Hash: ", revealTxn) return nil } @@ -651,11 +600,12 @@ func (*UtilsStruct) CalculateSecret(account types.Account, epoch uint32, keystor return signedData, secret, nil } -func updateGlobalCommitDataStruct(commitData types.CommitData, epoch uint32) types.CommitFileData { +func updateGlobalCommitDataStruct(commitData types.CommitData, commitment [32]byte, epoch uint32) types.CommitFileData { globalCommitDataStruct.Leaves = commitData.Leaves globalCommitDataStruct.AssignedCollections = commitData.AssignedCollections globalCommitDataStruct.SeqAllottedCollections = commitData.SeqAllottedCollections globalCommitDataStruct.Epoch = epoch + globalCommitDataStruct.Commitment = commitment return globalCommitDataStruct } diff --git a/cmd/vote_test.go b/cmd/vote_test.go index 07549319b..023e7b388 100644 --- a/cmd/vote_test.go +++ b/cmd/vote_test.go @@ -353,28 +353,27 @@ func TestInitiateCommit(t *testing.T) { } type args struct { - staker bindings.StructsStaker - stakerErr error - minStakeAmount *big.Int - minStakeAmountErr error - epoch uint32 - lastCommit uint32 - lastCommitErr error - secret []byte - secretErr error - signature []byte - salt [32]byte - saltErr error - path string - pathErr error - commitData types.CommitData - commitDataErr error - commitTxn common.Hash - commitTxnErr error - waitForBlockCompletionErr error - fileName string - fileNameErr error - saveErr error + staker bindings.StructsStaker + stakerErr error + minStakeAmount *big.Int + minStakeAmountErr error + epoch uint32 + lastCommit uint32 + lastCommitErr error + secret []byte + secretErr error + signature []byte + salt [32]byte + saltErr error + path string + pathErr error + commitData types.CommitData + commitDataErr error + commitTxn common.Hash + commitTxnErr error + fileName string + fileNameErr error + saveErr error } tests := []struct { name string @@ -394,7 +393,7 @@ func TestInitiateCommit(t *testing.T) { commitData: types.CommitData{ AssignedCollections: nil, SeqAllottedCollections: nil, - Leaves: nil, + Leaves: []*big.Int{big.NewInt(100)}, }, commitTxn: common.BigToHash(big.NewInt(1)), fileName: "", @@ -520,28 +519,29 @@ func TestInitiateCommit(t *testing.T) { wantErr: true, }, { - name: "Test 12: When there is an error in sending commit transaction", + name: "Test 12: When there is an error in getting path", args: args{ epoch: 5, lastCommit: 2, - secret: []byte{1}, - salt: [32]byte{}, - commitData: types.CommitData{ - AssignedCollections: nil, - SeqAllottedCollections: nil, - Leaves: nil, - }, - commitTxn: common.BigToHash(big.NewInt(1)), - waitForBlockCompletionErr: errors.New("transaction mining unsuccessful"), + pathErr: errors.New("path error"), }, wantErr: true, }, { - name: "Test 13: When there is an error in getting path", + name: "Test 13: When there is an error in getting commitment as values is nil", args: args{ - epoch: 5, - lastCommit: 2, - pathErr: errors.New("path error"), + staker: bindings.StructsStaker{Id: 1, Stake: big.NewInt(10000)}, + minStakeAmount: big.NewInt(100), + epoch: 5, + lastCommit: 2, + signature: []byte{2}, + secret: []byte{1}, + salt: [32]byte{}, + commitData: types.CommitData{ + AssignedCollections: nil, + SeqAllottedCollections: nil, + Leaves: []*big.Int{}, + }, }, wantErr: true, }, @@ -550,6 +550,9 @@ func TestInitiateCommit(t *testing.T) { t.Run(tt.name, func(t *testing.T) { SetUpMockInterfaces() + utils.MerkleInterface = &utils.MerkleTreeStruct{} + merkleUtils = utils.MerkleInterface + utilsMock.On("GetStaker", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.staker, tt.args.stakerErr) utilsMock.On("GetMinStakeAmount", mock.Anything, mock.Anything).Return(tt.args.minStakeAmount, tt.args.minStakeAmountErr) utilsMock.On("GetEpochLastCommitted", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.lastCommit, tt.args.lastCommitErr) @@ -558,9 +561,8 @@ func TestInitiateCommit(t *testing.T) { cmdUtilsMock.On("HandleCommitState", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.args.commitData, tt.args.commitDataErr) pathMock.On("GetDefaultPath").Return(tt.args.path, tt.args.pathErr) cmdUtilsMock.On("Commit", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.args.commitTxn, tt.args.commitTxnErr) - utilsMock.On("WaitForBlockCompletion", mock.AnythingOfType("*ethclient.Client"), mock.AnythingOfType("string")).Return(tt.args.waitForBlockCompletionErr) pathMock.On("GetCommitDataFileName", mock.AnythingOfType("string")).Return(tt.args.fileName, tt.args.fileNameErr) - fileUtilsMock.On("SaveDataToCommitJsonFile", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.saveErr) + fileUtilsMock.On("SaveDataToCommitJsonFile", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.args.saveErr) clientUtilsMock.On("GetLatestBlockWithRetry", mock.Anything, mock.Anything).Return(&Types.Header{Number: big.NewInt(100)}, nil) clientUtilsMock.On("FilterLogsWithRetry", mock.Anything, mock.Anything, mock.Anything).Return([]Types.Log{}, nil) ut := &UtilsStruct{} @@ -580,8 +582,14 @@ func TestInitiateReveal(t *testing.T) { stateBuffer uint64 ) + decodedCommitment, _ := hex.DecodeString("f3955999458f88a8440026a24e53c0761b67475e742556bf55bbe3bbdf5028ed") + var decodedCommitment32 [32]byte + copy(decodedCommitment32[:], decodedCommitment) + randomNum := big.NewInt(1111) globalCommitDataStruct.Epoch = 5 + globalCommitDataStruct.Leaves = []*big.Int{big.NewInt(100), big.NewInt(101)} + globalCommitDataStruct.Commitment = decodedCommitment32 type args struct { staker bindings.StructsStaker @@ -612,15 +620,13 @@ func TestInitiateReveal(t *testing.T) { { name: "Test 1: When InitiateReveal executes successfully", args: args{ - staker: bindings.StructsStaker{Id: 1, Stake: big.NewInt(10000)}, - minStakeAmount: big.NewInt(100), - epoch: 5, - lastReveal: 2, - fileName: "", - committedDataFromFile: types.CommitFileData{Epoch: 5}, - signature: []byte{1}, - secret: []byte{}, - revealTxn: common.BigToHash(big.NewInt(1)), + staker: bindings.StructsStaker{Id: 1, Stake: big.NewInt(10000)}, + minStakeAmount: big.NewInt(100), + epoch: 5, + lastReveal: 2, + signature: []byte{1}, + secret: []byte{}, + revealTxn: common.BigToHash(big.NewInt(1)), }, wantErr: false, }, @@ -735,8 +741,9 @@ func TestInitiateReveal(t *testing.T) { lastReveal: 2, fileName: "", committedDataFromFile: types.CommitFileData{ - Epoch: 5, - Leaves: []*big.Int{big.NewInt(1), big.NewInt(2)}, + Epoch: 5, + Leaves: []*big.Int{big.NewInt(1), big.NewInt(2)}, + Commitment: decodedCommitment32, }, secret: []byte{}, revealTxn: common.BigToHash(big.NewInt(1)), @@ -782,7 +789,7 @@ func TestInitiateReveal(t *testing.T) { pathMock.On("GetDefaultPath").Return(tt.args.path, tt.args.pathErr) cmdUtilsMock.On("CalculateSecret", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.args.signature, tt.args.secret, tt.args.secretErr) cmdUtilsMock.On("GetSalt", mock.Anything, mock.Anything, mock.Anything).Return([32]byte{}, nil) - utilsMock.On("GetCommitment", mock.Anything, mock.Anything, mock.Anything).Return(types.Commitment{}, nil) + utilsMock.On("GetCommitment", mock.Anything, mock.Anything, mock.Anything).Return(types.Commitment{CommitmentHash: decodedCommitment32}, nil) cmdUtilsMock.On("Reveal", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.args.revealTxn, tt.args.revealTxnErr) utilsMock.On("WaitForBlockCompletion", mock.AnythingOfType("*ethclient.Client"), mock.AnythingOfType("string")).Return(nil) ut := &UtilsStruct{} @@ -954,6 +961,8 @@ func TestHandleBlock(t *testing.T) { lastVerification uint32 isFlagPassed bool handleClaimBountyErr error + confirmedBlock types.ConfirmedBlock + confirmedBlockErr error } tests := []struct { name string @@ -1167,33 +1176,38 @@ func TestHandleBlock(t *testing.T) { { name: "Test 19: When claimBlockReward executes successfully in confirm state", args: args{ - state: 4, - epoch: 1, - stateName: "confirm", - lastVerification: 1, - staker: bindings.StructsStaker{Id: 1, Stake: big.NewInt(10000)}, - ethBalance: big.NewInt(1000), - actualStake: big.NewFloat(10000), - actualBalance: big.NewFloat(1000), - sRZRBalance: big.NewInt(10000), - sRZRInEth: big.NewFloat(100), + state: 4, + epoch: 1, + stateName: "confirm", + lastVerification: 1, + staker: bindings.StructsStaker{Id: 1, Stake: big.NewInt(10000)}, + ethBalance: big.NewInt(1000), + actualStake: big.NewFloat(10000), + actualBalance: big.NewFloat(1000), + sRZRBalance: big.NewInt(10000), + sRZRInEth: big.NewFloat(100), + confirmedBlock: types.ConfirmedBlock{ + ProposerId: 0, + }, claimBlockRewardTxn: common.BigToHash(big.NewInt(1)), }, }, { - name: "Test 20: When there is an error in claimBlockReward", + name: "Test 20: When in confirm state and the block is already confirmed", args: args{ - state: 4, - epoch: 2, - stateName: "confirm", - lastVerification: 1, - staker: bindings.StructsStaker{Id: 2, Stake: big.NewInt(10000)}, - ethBalance: big.NewInt(1000), - actualStake: big.NewFloat(10000), - actualBalance: big.NewFloat(1000), - sRZRBalance: big.NewInt(10000), - sRZRInEth: big.NewFloat(100), - claimBlockRewardErr: errors.New("error in claimBlockReward"), + state: 4, + epoch: 2, + stateName: "confirm", + lastVerification: 1, + staker: bindings.StructsStaker{Id: 2, Stake: big.NewInt(10000)}, + ethBalance: big.NewInt(1000), + actualStake: big.NewFloat(10000), + actualBalance: big.NewFloat(1000), + sRZRBalance: big.NewInt(10000), + sRZRInEth: big.NewFloat(100), + confirmedBlock: types.ConfirmedBlock{ + ProposerId: 1, + }, }, }, { @@ -1234,9 +1248,19 @@ func TestHandleBlock(t *testing.T) { }, }, { - name: "Test 24: When there is an error in getting state remaining time", + name: "Test 24: When in confirm state and there is an error in getting confirmed block for the epoch", args: args{ - stateRemainingTimeErr: errors.New("state remaining time error"), + state: 4, + epoch: 2, + stateName: "confirm", + lastVerification: 1, + staker: bindings.StructsStaker{Id: 2, Stake: big.NewInt(10000)}, + ethBalance: big.NewInt(1000), + actualStake: big.NewFloat(10000), + actualBalance: big.NewFloat(1000), + sRZRBalance: big.NewInt(10000), + sRZRInEth: big.NewFloat(100), + confirmedBlockErr: errors.New("blocks error"), }, }, } @@ -1258,8 +1282,8 @@ func TestHandleBlock(t *testing.T) { cmdUtilsMock.On("HandleDispute", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(tt.args.handleDisputeErr) utilsMock.On("IsFlagPassed", mock.AnythingOfType("string")).Return(tt.args.isFlagPassed) cmdUtilsMock.On("HandleClaimBounty", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.handleClaimBountyErr) + utilsMock.On("GetConfirmedBlocks", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.confirmedBlock, tt.args.confirmedBlockErr) cmdUtilsMock.On("ClaimBlockReward", mock.Anything, mock.Anything).Return(tt.args.claimBlockRewardTxn, tt.args.claimBlockRewardErr) - utilsMock.On("WaitForBlockCompletion", mock.AnythingOfType("*ethclient.Client"), mock.Anything).Return(nil) timeMock.On("Sleep", mock.Anything).Return() utilsMock.On("WaitTillNextNSecs", mock.AnythingOfType("int32")).Return() lastVerification = tt.args.lastVerification diff --git a/core/types/block.go b/core/types/block.go index 2fa61fd82..cc059af6e 100644 --- a/core/types/block.go +++ b/core/types/block.go @@ -16,3 +16,10 @@ type DisputesStruct struct { AccWeight *big.Int Median *big.Int } + +type ConfirmedBlock struct { + Valid bool + ProposerId uint32 + Iteration *big.Int + BiggestStake *big.Int +} diff --git a/core/types/vote.go b/core/types/vote.go index f1fb7b743..0ec230387 100644 --- a/core/types/vote.go +++ b/core/types/vote.go @@ -58,6 +58,7 @@ type CommitFileData struct { AssignedCollections map[int]bool SeqAllottedCollections []*big.Int Leaves []*big.Int + Commitment [32]byte } type ProposeFileData struct { diff --git a/utils/block.go b/utils/block.go index 53544e78b..af86f14c0 100644 --- a/utils/block.go +++ b/utils/block.go @@ -4,6 +4,7 @@ import ( "context" "errors" "math/big" + Types "razor/core/types" "razor/pkg/bindings" "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -115,3 +116,11 @@ func (*UtilsStruct) GetEpochLastProposed(ctx context.Context, client *ethclient. } return returnedValues[0].Interface().(uint32), nil } + +func (*UtilsStruct) GetConfirmedBlocks(ctx context.Context, client *ethclient.Client, epoch uint32) (Types.ConfirmedBlock, error) { + returnedValues, err := InvokeFunctionWithRetryAttempts(ctx, BlockManagerInterface, "GetConfirmedBlocks", client, epoch) + if err != nil { + return Types.ConfirmedBlock{}, err + } + return returnedValues[0].Interface().(Types.ConfirmedBlock), nil +} diff --git a/utils/block_test.go b/utils/block_test.go index ba1d2c73b..370dee4c8 100644 --- a/utils/block_test.go +++ b/utils/block_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "math/big" + "razor/core/types" "razor/pkg/bindings" "razor/utils/mocks" "reflect" @@ -708,3 +709,68 @@ func TestGetEpochLastProposed(t *testing.T) { }) } } + +func TestGetConfirmedBlocks(t *testing.T) { + var client *ethclient.Client + var callOpts bind.CallOpts + var epoch uint32 + + type args struct { + confirmedBlock types.ConfirmedBlock + confirmedBlockErr error + } + tests := []struct { + name string + args args + want types.ConfirmedBlock + wantErr bool + }{ + { + name: "Test 1: When GetConfirmedBlocks() executes successfully", + args: args{ + confirmedBlock: types.ConfirmedBlock{ + ProposerId: 1, + }, + }, + want: types.ConfirmedBlock{ + ProposerId: 1, + }, + wantErr: false, + }, + { + name: "Test 2: When there is an error in getting confirmedBlock", + args: args{ + confirmedBlockErr: errors.New("confirmedBlock error"), + }, + want: types.ConfirmedBlock{}, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + retryMock := new(mocks.RetryUtils) + utilsMock := new(mocks.Utils) + blockManagerMock := new(mocks.BlockManagerUtils) + + optionsPackageStruct := OptionsPackageStruct{ + RetryInterface: retryMock, + UtilsInterface: utilsMock, + BlockManagerInterface: blockManagerMock, + } + utils := StartRazor(optionsPackageStruct) + + utilsMock.On("GetOptions").Return(callOpts) + blockManagerMock.On("GetConfirmedBlocks", mock.Anything, mock.Anything).Return(tt.args.confirmedBlock, tt.args.confirmedBlockErr) + retryMock.On("RetryAttempts", mock.Anything).Return(retry.Attempts(1)) + + got, err := utils.GetConfirmedBlocks(context.Background(), client, epoch) + if (err != nil) != tt.wantErr { + t.Errorf("GetConfirmedBlocks() error = %v, wantErr %v", err, tt.wantErr) + return + } + if got != tt.want { + t.Errorf("GetConfirmedBlocks() got = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/utils/common.go b/utils/common.go index 6dc589de6..c442021e8 100644 --- a/utils/common.go +++ b/utils/common.go @@ -219,13 +219,14 @@ func (*UtilsStruct) EstimateBlockNumberAtEpochBeginning(client *ethclient.Client } -func (*FileStruct) SaveDataToCommitJsonFile(filePath string, epoch uint32, commitData types.CommitData) error { +func (*FileStruct) SaveDataToCommitJsonFile(filePath string, epoch uint32, commitData types.CommitData, commitment [32]byte) error { var data types.CommitFileData data.Epoch = epoch data.AssignedCollections = commitData.AssignedCollections data.SeqAllottedCollections = commitData.SeqAllottedCollections data.Leaves = commitData.Leaves + data.Commitment = commitment jsonData, err := JsonInterface.Marshal(data) if err != nil { diff --git a/utils/common_test.go b/utils/common_test.go index ba01a3feb..3cc05fd02 100644 --- a/utils/common_test.go +++ b/utils/common_test.go @@ -1070,6 +1070,7 @@ func TestSaveDataToCommitJsonFile(t *testing.T) { filePath string epoch uint32 commitData Types.CommitData + commitment [32]byte ) type args struct { jsonData []byte @@ -1119,7 +1120,7 @@ func TestSaveDataToCommitJsonFile(t *testing.T) { osMock.On("WriteFile", mock.Anything, mock.Anything, mock.Anything).Return(tt.args.writeFileErr) fileUtils := FileStruct{} - if err := fileUtils.SaveDataToCommitJsonFile(filePath, epoch, commitData); (err != nil) != tt.wantErr { + if err := fileUtils.SaveDataToCommitJsonFile(filePath, epoch, commitData, commitment); (err != nil) != tt.wantErr { t.Errorf("SaveDataToCommitJsonFile() error = %v, wantErr %v", err, tt.wantErr) } }) diff --git a/utils/interface.go b/utils/interface.go index f6786721f..f90001279 100644 --- a/utils/interface.go +++ b/utils/interface.go @@ -3,7 +3,6 @@ package utils import ( "context" "crypto/ecdsa" - "github.com/ethereum/go-ethereum/rpc" "io" "io/fs" "math/big" @@ -13,6 +12,8 @@ import ( "razor/pkg/bindings" "time" + "github.com/ethereum/go-ethereum/rpc" + "github.com/avast/retry-go" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" @@ -149,6 +150,7 @@ type Utils interface { SecondsToReadableTime(input int) string EstimateBlockNumberAtEpochBeginning(client *ethclient.Client, currentBlockNumber *big.Int) (*big.Int, error) GetEpochLastProposed(ctx context.Context, client *ethclient.Client, stakerId uint32) (uint32, error) + GetConfirmedBlocks(ctx context.Context, client *ethclient.Client, epoch uint32) (types.ConfirmedBlock, error) CheckAmountAndBalance(amountInWei *big.Int, balance *big.Int) *big.Int PasswordPrompt() string AssignPassword(flagSet *pflag.FlagSet) string @@ -231,6 +233,7 @@ type BlockManagerUtils interface { SortedProposedBlockIds(client *ethclient.Client, arg0 uint32, arg1 *big.Int) (uint32, error) GetBlockIndexToBeConfirmed(client *ethclient.Client) (int8, error) GetEpochLastProposed(client *ethclient.Client, stakerId uint32) (uint32, error) + GetConfirmedBlocks(client *ethclient.Client, epoch uint32) (types.ConfirmedBlock, error) } type StakeManagerUtils interface { @@ -296,7 +299,7 @@ type FlagSetUtils interface { } type FileUtils interface { - SaveDataToCommitJsonFile(filePath string, epoch uint32, commitData types.CommitData) error + SaveDataToCommitJsonFile(filePath string, epoch uint32, commitData types.CommitData, commitment [32]byte) error ReadFromCommitJsonFile(filePath string) (types.CommitFileData, error) SaveDataToProposeJsonFile(filePath string, proposeData types.ProposeFileData) error ReadFromProposeJsonFile(filePath string) (types.ProposeFileData, error) diff --git a/utils/mocks/block_manager_utils.go b/utils/mocks/block_manager_utils.go index 072fb5432..d61b0c566 100644 --- a/utils/mocks/block_manager_utils.go +++ b/utils/mocks/block_manager_utils.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.30.1. DO NOT EDIT. package mocks @@ -9,6 +9,8 @@ import ( ethclient "github.com/ethereum/go-ethereum/ethclient" mock "github.com/stretchr/testify/mock" + + types "razor/core/types" ) // BlockManagerUtils is an autogenerated mock type for the BlockManagerUtils type @@ -21,13 +23,16 @@ func (_m *BlockManagerUtils) GetBlock(client *ethclient.Client, epoch uint32) (b ret := _m.Called(client, epoch) var r0 bindings.StructsBlock + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32) (bindings.StructsBlock, error)); ok { + return rf(client, epoch) + } if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32) bindings.StructsBlock); ok { r0 = rf(client, epoch) } else { r0 = ret.Get(0).(bindings.StructsBlock) } - var r1 error if rf, ok := ret.Get(1).(func(*ethclient.Client, uint32) error); ok { r1 = rf(client, epoch) } else { @@ -42,13 +47,16 @@ func (_m *BlockManagerUtils) GetBlockIndexToBeConfirmed(client *ethclient.Client ret := _m.Called(client) var r0 int8 + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client) (int8, error)); ok { + return rf(client) + } if rf, ok := ret.Get(0).(func(*ethclient.Client) int8); ok { r0 = rf(client) } else { r0 = ret.Get(0).(int8) } - var r1 error if rf, ok := ret.Get(1).(func(*ethclient.Client) error); ok { r1 = rf(client) } else { @@ -58,18 +66,45 @@ func (_m *BlockManagerUtils) GetBlockIndexToBeConfirmed(client *ethclient.Client return r0, r1 } +// GetConfirmedBlocks provides a mock function with given fields: client, epoch +func (_m *BlockManagerUtils) GetConfirmedBlocks(client *ethclient.Client, epoch uint32) (types.ConfirmedBlock, error) { + ret := _m.Called(client, epoch) + + var r0 types.ConfirmedBlock + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32) (types.ConfirmedBlock, error)); ok { + return rf(client, epoch) + } + if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32) types.ConfirmedBlock); ok { + r0 = rf(client, epoch) + } else { + r0 = ret.Get(0).(types.ConfirmedBlock) + } + + if rf, ok := ret.Get(1).(func(*ethclient.Client, uint32) error); ok { + r1 = rf(client, epoch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetEpochLastProposed provides a mock function with given fields: client, stakerId func (_m *BlockManagerUtils) GetEpochLastProposed(client *ethclient.Client, stakerId uint32) (uint32, error) { ret := _m.Called(client, stakerId) var r0 uint32 + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32) (uint32, error)); ok { + return rf(client, stakerId) + } if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32) uint32); ok { r0 = rf(client, stakerId) } else { r0 = ret.Get(0).(uint32) } - var r1 error if rf, ok := ret.Get(1).(func(*ethclient.Client, uint32) error); ok { r1 = rf(client, stakerId) } else { @@ -84,13 +119,16 @@ func (_m *BlockManagerUtils) GetNumProposedBlocks(client *ethclient.Client, epoc ret := _m.Called(client, epoch) var r0 uint8 + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32) (uint8, error)); ok { + return rf(client, epoch) + } if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32) uint8); ok { r0 = rf(client, epoch) } else { r0 = ret.Get(0).(uint8) } - var r1 error if rf, ok := ret.Get(1).(func(*ethclient.Client, uint32) error); ok { r1 = rf(client, epoch) } else { @@ -105,13 +143,16 @@ func (_m *BlockManagerUtils) GetProposedBlock(client *ethclient.Client, epoch ui ret := _m.Called(client, epoch, proposedBlock) var r0 bindings.StructsBlock + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32, uint32) (bindings.StructsBlock, error)); ok { + return rf(client, epoch, proposedBlock) + } if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32, uint32) bindings.StructsBlock); ok { r0 = rf(client, epoch, proposedBlock) } else { r0 = ret.Get(0).(bindings.StructsBlock) } - var r1 error if rf, ok := ret.Get(1).(func(*ethclient.Client, uint32, uint32) error); ok { r1 = rf(client, epoch, proposedBlock) } else { @@ -126,13 +167,16 @@ func (_m *BlockManagerUtils) MaxAltBlocks(client *ethclient.Client) (uint8, erro ret := _m.Called(client) var r0 uint8 + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client) (uint8, error)); ok { + return rf(client) + } if rf, ok := ret.Get(0).(func(*ethclient.Client) uint8); ok { r0 = rf(client) } else { r0 = ret.Get(0).(uint8) } - var r1 error if rf, ok := ret.Get(1).(func(*ethclient.Client) error); ok { r1 = rf(client) } else { @@ -147,6 +191,10 @@ func (_m *BlockManagerUtils) MinStake(client *ethclient.Client) (*big.Int, error ret := _m.Called(client) var r0 *big.Int + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client) (*big.Int, error)); ok { + return rf(client) + } if rf, ok := ret.Get(0).(func(*ethclient.Client) *big.Int); ok { r0 = rf(client) } else { @@ -155,7 +203,6 @@ func (_m *BlockManagerUtils) MinStake(client *ethclient.Client) (*big.Int, error } } - var r1 error if rf, ok := ret.Get(1).(func(*ethclient.Client) error); ok { r1 = rf(client) } else { @@ -170,13 +217,16 @@ func (_m *BlockManagerUtils) SortedProposedBlockIds(client *ethclient.Client, ar ret := _m.Called(client, arg0, arg1) var r0 uint32 + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32, *big.Int) (uint32, error)); ok { + return rf(client, arg0, arg1) + } if rf, ok := ret.Get(0).(func(*ethclient.Client, uint32, *big.Int) uint32); ok { r0 = rf(client, arg0, arg1) } else { r0 = ret.Get(0).(uint32) } - var r1 error if rf, ok := ret.Get(1).(func(*ethclient.Client, uint32, *big.Int) error); ok { r1 = rf(client, arg0, arg1) } else { @@ -191,13 +241,16 @@ func (_m *BlockManagerUtils) StateBuffer(client *ethclient.Client) (uint8, error ret := _m.Called(client) var r0 uint8 + var r1 error + if rf, ok := ret.Get(0).(func(*ethclient.Client) (uint8, error)); ok { + return rf(client) + } if rf, ok := ret.Get(0).(func(*ethclient.Client) uint8); ok { r0 = rf(client) } else { r0 = ret.Get(0).(uint8) } - var r1 error if rf, ok := ret.Get(1).(func(*ethclient.Client) error); ok { r1 = rf(client) } else { @@ -207,13 +260,12 @@ func (_m *BlockManagerUtils) StateBuffer(client *ethclient.Client) (uint8, error return r0, r1 } -type mockConstructorTestingTNewBlockManagerUtils interface { +// NewBlockManagerUtils creates a new instance of BlockManagerUtils. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBlockManagerUtils(t interface { mock.TestingT Cleanup(func()) -} - -// NewBlockManagerUtils creates a new instance of BlockManagerUtils. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewBlockManagerUtils(t mockConstructorTestingTNewBlockManagerUtils) *BlockManagerUtils { +}) *BlockManagerUtils { mock := &BlockManagerUtils{} mock.Mock.Test(t) diff --git a/utils/mocks/file_utils.go b/utils/mocks/file_utils.go index d107eadac..56c64175b 100644 --- a/utils/mocks/file_utils.go +++ b/utils/mocks/file_utils.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.14.0. DO NOT EDIT. +// Code generated by mockery v2.30.1. DO NOT EDIT. package mocks @@ -24,13 +24,16 @@ func (_m *FileUtils) ReadFromCommitJsonFile(filePath string) (types.CommitFileDa ret := _m.Called(filePath) var r0 types.CommitFileData + var r1 error + if rf, ok := ret.Get(0).(func(string) (types.CommitFileData, error)); ok { + return rf(filePath) + } if rf, ok := ret.Get(0).(func(string) types.CommitFileData); ok { r0 = rf(filePath) } else { r0 = ret.Get(0).(types.CommitFileData) } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(filePath) } else { @@ -45,13 +48,16 @@ func (_m *FileUtils) ReadFromDisputeJsonFile(filePath string) (types.DisputeFile ret := _m.Called(filePath) var r0 types.DisputeFileData + var r1 error + if rf, ok := ret.Get(0).(func(string) (types.DisputeFileData, error)); ok { + return rf(filePath) + } if rf, ok := ret.Get(0).(func(string) types.DisputeFileData); ok { r0 = rf(filePath) } else { r0 = ret.Get(0).(types.DisputeFileData) } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(filePath) } else { @@ -66,13 +72,16 @@ func (_m *FileUtils) ReadFromProposeJsonFile(filePath string) (types.ProposeFile ret := _m.Called(filePath) var r0 types.ProposeFileData + var r1 error + if rf, ok := ret.Get(0).(func(string) (types.ProposeFileData, error)); ok { + return rf(filePath) + } if rf, ok := ret.Get(0).(func(string) types.ProposeFileData); ok { r0 = rf(filePath) } else { r0 = ret.Get(0).(types.ProposeFileData) } - var r1 error if rf, ok := ret.Get(1).(func(string) error); ok { r1 = rf(filePath) } else { @@ -82,13 +91,13 @@ func (_m *FileUtils) ReadFromProposeJsonFile(filePath string) (types.ProposeFile return r0, r1 } -// SaveDataToCommitJsonFile provides a mock function with given fields: filePath, epoch, commitData -func (_m *FileUtils) SaveDataToCommitJsonFile(filePath string, epoch uint32, commitData types.CommitData) error { - ret := _m.Called(filePath, epoch, commitData) +// SaveDataToCommitJsonFile provides a mock function with given fields: filePath, epoch, commitData, commitment +func (_m *FileUtils) SaveDataToCommitJsonFile(filePath string, epoch uint32, commitData types.CommitData, commitment [32]byte) error { + ret := _m.Called(filePath, epoch, commitData, commitment) var r0 error - if rf, ok := ret.Get(0).(func(string, uint32, types.CommitData) error); ok { - r0 = rf(filePath, epoch, commitData) + if rf, ok := ret.Get(0).(func(string, uint32, types.CommitData, [32]byte) error); ok { + r0 = rf(filePath, epoch, commitData, commitment) } else { r0 = ret.Error(0) } @@ -124,13 +133,12 @@ func (_m *FileUtils) SaveDataToProposeJsonFile(filePath string, proposeData type return r0 } -type mockConstructorTestingTNewFileUtils interface { +// NewFileUtils creates a new instance of FileUtils. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewFileUtils(t interface { mock.TestingT Cleanup(func()) -} - -// NewFileUtils creates a new instance of FileUtils. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewFileUtils(t mockConstructorTestingTNewFileUtils) *FileUtils { +}) *FileUtils { mock := &FileUtils{} mock.Mock.Test(t) diff --git a/utils/mocks/utils.go b/utils/mocks/utils.go index 646ad8354..51e0457c3 100644 --- a/utils/mocks/utils.go +++ b/utils/mocks/utils.go @@ -734,6 +734,30 @@ func (_m *Utils) GetCommitment(ctx context.Context, client *ethclient.Client, ad return r0, r1 } +// GetConfirmedBlocks provides a mock function with given fields: ctx, client, epoch +func (_m *Utils) GetConfirmedBlocks(ctx context.Context, client *ethclient.Client, epoch uint32) (types.ConfirmedBlock, error) { + ret := _m.Called(ctx, client, epoch) + + var r0 types.ConfirmedBlock + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, *ethclient.Client, uint32) (types.ConfirmedBlock, error)); ok { + return rf(ctx, client, epoch) + } + if rf, ok := ret.Get(0).(func(context.Context, *ethclient.Client, uint32) types.ConfirmedBlock); ok { + r0 = rf(ctx, client, epoch) + } else { + r0 = ret.Get(0).(types.ConfirmedBlock) + } + + if rf, ok := ret.Get(1).(func(context.Context, *ethclient.Client, uint32) error); ok { + r1 = rf(ctx, client, epoch) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetDataToCommitFromJob provides a mock function with given fields: job, commitParams func (_m *Utils) GetDataToCommitFromJob(job bindings.StructsJob, commitParams *types.CommitParams) (*big.Int, error) { ret := _m.Called(job, commitParams) diff --git a/utils/struct-utils.go b/utils/struct-utils.go index 5b25dc2f7..216e85114 100644 --- a/utils/struct-utils.go +++ b/utils/struct-utils.go @@ -342,6 +342,21 @@ func (b BlockManagerStruct) GetEpochLastProposed(client *ethclient.Client, stake return returnedValues[0].Interface().(uint32), nil } +func (b BlockManagerStruct) GetConfirmedBlocks(client *ethclient.Client, epoch uint32) (coretypes.ConfirmedBlock, error) { + blockManager, opts := UtilsInterface.GetBlockManagerWithOpts(client) + returnedValues := InvokeFunctionWithTimeout(blockManager, "Blocks", &opts, epoch) + returnedError := CheckIfAnyError(returnedValues) + if returnedError != nil { + return coretypes.ConfirmedBlock{}, returnedError + } + return returnedValues[0].Interface().(struct { + Valid bool + ProposerId uint32 + Iteration *big.Int + BiggestStake *big.Int + }), nil +} + func (s StakeManagerStruct) GetStakerId(client *ethclient.Client, address common.Address) (uint32, error) { stakeManager, opts := UtilsInterface.GetStakeManagerWithOpts(client) returnedValues := InvokeFunctionWithTimeout(stakeManager, "GetStakerId", &opts, address)