Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

feat: added commitment verification layer for data during reveal after removing waitForBlockCompletion check for voting transactions #1254

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
Loading