Skip to content

Commit

Permalink
feat: added commitment verification layer for data during reveal afte…
Browse files Browse the repository at this point in the history
…r 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
  • Loading branch information
Yashk767 authored Oct 17, 2024
1 parent 6917916 commit 72b6be4
Show file tree
Hide file tree
Showing 19 changed files with 505 additions and 382 deletions.
84 changes: 63 additions & 21 deletions cmd/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -215,31 +209,79 @@ 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)
return false, err
}
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
}
102 changes: 26 additions & 76 deletions cmd/commit_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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{},
Expand All @@ -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,
Expand All @@ -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{},
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}
}
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion cmd/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
18 changes: 9 additions & 9 deletions cmd/mocks/utils_cmd_interface.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 72b6be4

Please sign in to comment.