Skip to content

Commit

Permalink
chore: stop the loop if there is an error in sanitize delegations
Browse files Browse the repository at this point in the history
  • Loading branch information
RafilxTenfen committed Jan 13, 2025
1 parent 7614a28 commit cdab902
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 23 deletions.
33 changes: 17 additions & 16 deletions covenant/covenant.go
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ func (ce *CovenantEmulator) delegationsToBatches(dels []*types.Delegation) [][]*
func IsKeyInCommittee(paramCache ParamsGetter, covenantSerializedPk []byte, del *types.Delegation) (bool, error) {
stkParams, err := paramCache.Get(del.ParamsVersion)
if err != nil {
return false, err
return false, fmt.Errorf("unable to get the param version: %d, reason: %s", del.ParamsVersion, err.Error())
}

for _, pk := range stkParams.CovenantPks {
Expand All @@ -455,7 +455,7 @@ func IsKeyInCommittee(paramCache ParamsGetter, covenantSerializedPk []byte, del
return true, nil
}

return false, fmt.Errorf("serialized pub key is not in the list of covenants for the param version: %d", del.ParamsVersion)
return false, nil
}

// CovenantAlreadySigned returns true if the covenant already signed the BTC Delegation
Expand All @@ -473,18 +473,17 @@ func CovenantAlreadySigned(covenantSerializedPk []byte, del *types.Delegation) b

// sanitizeDelegations removes any delegations that have already been signed by the covenant and
// remove delegations that were not constructed with this covenant public key
func (ce *CovenantEmulator) sanitizeDelegations(dels []*types.Delegation) []*types.Delegation {
return SanitizeDelegations(ce.logger, ce.pk, ce.paramCache, dels)
func (ce *CovenantEmulator) sanitizeDelegations(dels []*types.Delegation) ([]*types.Delegation, error) {
return SanitizeDelegations(ce.pk, ce.paramCache, dels)
}

// SanitizeDelegations remove the delegations in which the covenant public key already signed
// or the delegation was not constructed with that covenant public key
func SanitizeDelegations(
logger *zap.Logger,
pk *btcec.PublicKey,
paramCache ParamsGetter,
dels []*types.Delegation,
) []*types.Delegation {
) ([]*types.Delegation, error) {
covenantSerializedPk := schnorr.SerializePubKey(pk)

sanitized := make([]*types.Delegation, 0, len(dels))
Expand All @@ -497,21 +496,15 @@ func SanitizeDelegations(
// 2. Remove delegations that were not constructed with this covenant public key
isInCommittee, err := IsKeyInCommittee(paramCache, covenantSerializedPk, del)
if err != nil {
logger.Error(
"invalid delegation",
zap.String("staker_pk", hex.EncodeToString(covenantSerializedPk)),
zap.String("staking_tx_hex", del.StakingTxHex),
zap.String("reason", "covenant key is not in committee"),
zap.Error(err),
)
return nil, fmt.Errorf("unable to verify if covenant key is in committee: %s", err.Error())
}
if !isInCommittee {
continue
}
sanitized = append(sanitized, del)
}

return sanitized
return sanitized, nil
}

// covenantSigSubmissionLoop is the reactor to submit Covenant signature for BTC delegations
Expand Down Expand Up @@ -540,10 +533,18 @@ func (ce *CovenantEmulator) covenantSigSubmissionLoop() {

if len(dels) == 0 {
ce.logger.Debug("no pending delegations are found")
continue
}
// 2. Remove delegations that do not need the covenant's signature
sanitizedDels := ce.sanitizeDelegations(dels)

// 2. Remove delegations that do not need the covenant's signature
sanitizedDels, err := ce.sanitizeDelegations(dels)
if err != nil {
ce.logger.Error(
"error sanitizing delegations",
zap.Error(err),
)
continue
}
// 3. Split delegations into batches for submission
batches := ce.delegationsToBatches(sanitizedDels)
for _, delBatch := range batches {
Expand Down
40 changes: 33 additions & 7 deletions covenant/covenant_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,9 @@ func TestDeduplicationWithOddKey(t *testing.T) {
})

// 4. After removing the already signed delegation, the list should have only one element
sanitized := covenant.SanitizeDelegations(zap.NewNop(), oddKeyPub, paramsGet, delegations)
sanitized, err := covenant.SanitizeDelegations(oddKeyPub, paramsGet, delegations)
require.Equal(t, 1, len(sanitized))
require.NoError(t, err)
}

func TestIsKeyInCommittee(t *testing.T) {
Expand Down Expand Up @@ -301,21 +302,23 @@ func TestIsKeyInCommittee(t *testing.T) {
pVersionWithCovenant: paramsWithCovenant,
})

logger := zap.NewNop()
// checks the case where the covenant is NOT in the committee
actual, err := covenant.IsKeyInCommittee(paramsGet, covenantSerializedPk, delNoCovenant)
require.False(t, actual)
require.EqualError(t, err, fmt.Errorf("serialized pub key is not in the list of covenants for the param version: %d", pVersionWithoutCovenant).Error())
emptyDels := covenant.SanitizeDelegations(logger, covKeyPair.PublicKey, paramsGet, []*types.Delegation{delNoCovenant, delNoCovenant})
require.NoError(t, err)
emptyDels, err := covenant.SanitizeDelegations(covKeyPair.PublicKey, paramsGet, []*types.Delegation{delNoCovenant, delNoCovenant})
require.NoError(t, err)
require.Len(t, emptyDels, 0)

// checks the case where the covenant is in the committee
actual, err = covenant.IsKeyInCommittee(paramsGet, covenantSerializedPk, delWithCovenant)
require.True(t, actual)
require.NoError(t, err)
dels := covenant.SanitizeDelegations(logger, covKeyPair.PublicKey, paramsGet, []*types.Delegation{delWithCovenant, delNoCovenant})
dels, err := covenant.SanitizeDelegations(covKeyPair.PublicKey, paramsGet, []*types.Delegation{delWithCovenant, delNoCovenant})
require.NoError(t, err)
require.Len(t, dels, 1)
dels = covenant.SanitizeDelegations(logger, covKeyPair.PublicKey, paramsGet, []*types.Delegation{delWithCovenant})
dels, err = covenant.SanitizeDelegations(covKeyPair.PublicKey, paramsGet, []*types.Delegation{delWithCovenant})
require.NoError(t, err)
require.Len(t, dels, 1)

amtSatFirst := btcutil.Amount(100)
Expand All @@ -338,11 +341,20 @@ func TestIsKeyInCommittee(t *testing.T) {
},
}

sanitizedDels := covenant.SanitizeDelegations(logger, covKeyPair.PublicKey, paramsGet, lastUnsanitizedDels)
sanitizedDels, err := covenant.SanitizeDelegations(covKeyPair.PublicKey, paramsGet, lastUnsanitizedDels)
require.NoError(t, err)
require.Len(t, sanitizedDels, 3)
require.Equal(t, amtSatFirst, sanitizedDels[0].TotalSat)
require.Equal(t, amtSatSecond, sanitizedDels[1].TotalSat)
require.Equal(t, amtSatThird, sanitizedDels[2].TotalSat)

errParamGet := fmt.Errorf("dumbErr")
sanitizedDels, err = covenant.SanitizeDelegations(covKeyPair.PublicKey, NewMockParamError(errParamGet), lastUnsanitizedDels)
require.Nil(t, sanitizedDels)

errKeyIsInCommittee := fmt.Errorf("unable to get the param version: %d, reason: %s", pVersionWithCovenant, errParamGet.Error())
expErr := fmt.Errorf("unable to verify if covenant key is in committee: %s", errKeyIsInCommittee.Error())
require.EqualError(t, err, expErr.Error())
}

type MockParamGetter struct {
Expand All @@ -359,3 +371,17 @@ func (m *MockParamGetter) Get(version uint32) (*types.StakingParams, error) {
p := m.paramsByVersion[version]
return p, nil
}

type MockParamError struct {
err error
}

func NewMockParamError(err error) *MockParamError {
return &MockParamError{
err: err,
}
}

func (m *MockParamError) Get(version uint32) (*types.StakingParams, error) {
return nil, m.err
}

0 comments on commit cdab902

Please sign in to comment.