Skip to content

Commit

Permalink
Implemented inactivity claim submitting
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaszslabon committed Apr 26, 2024
1 parent 17df0d8 commit bd181a9
Show file tree
Hide file tree
Showing 7 changed files with 365 additions and 22 deletions.
92 changes: 80 additions & 12 deletions pkg/chain/ethereum/tbtc.go
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,78 @@ func (tc *TbtcChain) DKGParameters() (*tbtc.DKGParameters, error) {
}, nil
}

func (tc *TbtcChain) AssembleInactivityClaim(
walletID [32]byte,
inactiveMembersIndices []group.MemberIndex,
signatures map[group.MemberIndex][]byte,
heartbeatFailed bool,
) (
*tbtc.InactivityChainClaim,
error,
) {
signingMemberIndices, signatureBytes, err := convertSignaturesToChainFormat(
signatures,
)
if err != nil {
return nil, fmt.Errorf(
"could not convert signatures to chain format: [%v]",
err,
)
}

// Sort inactiveMembersIndices slice in ascending order as expected by the
// on-chain contract.
sort.Slice(inactiveMembersIndices[:], func(i, j int) bool {
return inactiveMembersIndices[i] < inactiveMembersIndices[j]
})

return &tbtc.InactivityChainClaim{
WalletID: walletID,
InactiveMembersIndices: inactiveMembersIndices,
HeartbeatFailed: heartbeatFailed,
Signatures: signatureBytes,
SigningMembersIndices: signingMemberIndices,
}, nil
}

// convertInactivityClaimToAbiType converts the TBTC-specific inactivity claim
// to the format applicable for the WalletRegistry ABI.
func convertInactivityClaimToAbiType(
claim *tbtc.InactivityChainClaim,
) ecdsaabi.EcdsaInactivityClaim {
inactiveMembersIndices := make([]*big.Int, len(claim.InactiveMembersIndices))
for i, memberIndex := range claim.InactiveMembersIndices {
inactiveMembersIndices[i] = big.NewInt(int64(memberIndex))
}

signingMembersIndices := make([]*big.Int, len(claim.SigningMembersIndices))
for i, memberIndex := range claim.SigningMembersIndices {
signingMembersIndices[i] = big.NewInt(int64(memberIndex))
}

return ecdsaabi.EcdsaInactivityClaim{
WalletID: claim.WalletID,
InactiveMembersIndices: inactiveMembersIndices,
HeartbeatFailed: claim.HeartbeatFailed,
Signatures: claim.Signatures,
SigningMembersIndices: signingMembersIndices,
}
}

func (tc *TbtcChain) SubmitInactivityClaim(
claim *tbtc.InactivityChainClaim,
nonce *big.Int,
groupMembers []uint32,
) error {
_, err := tc.walletRegistry.NotifyOperatorInactivity(
convertInactivityClaimToAbiType(claim),
nonce,
groupMembers,
)

return err
}

func (tc *TbtcChain) CalculateInactivityClaimSignatureHash(
claim *inactivity.Claim,
) (inactivity.ClaimSignatureHash, error) {
Expand All @@ -1006,21 +1078,17 @@ func (tc *TbtcChain) CalculateInactivityClaimSignatureHash(
// expects an unprefixed 64-byte public key,
unprefixedGroupPublicKeyBytes := walletPublicKeyBytes[1:]

// Inactive members indexes should be sorted in the ascending order. As the
// claim object may possibly be shared between concurrent code, it is
// safer to copy the indexes into a new slice. Additionally, the type
// representing inactive member index should be `big.Int` as the smart
// contract reading the calculated hash uses `uint256` for inactive member
// indexes.
inactiveMembersIndexes := make([]*big.Int, len(claim.InactiveMembersIndexes))
for i, index := range claim.InactiveMembersIndexes {
// The indexes are already sorted.
sortedIndexes := claim.GetInactiveMembersIndexes()

// The type representing inactive member index should be `big.Int` as the
// smart contract reading the calculated hash uses `uint256` for inactive
// member indexes.
inactiveMembersIndexes := make([]*big.Int, len(sortedIndexes))
for i, index := range sortedIndexes {
inactiveMembersIndexes[i] = big.NewInt(int64(index))
}

sort.Slice(inactiveMembersIndexes, func(i, j int) bool {
return inactiveMembersIndexes[i].Cmp(inactiveMembersIndexes[j]) < 0
})

return calculateInactivityClaimSignatureHash(
tc.chainID,
claim.Nonce,
Expand Down
25 changes: 25 additions & 0 deletions pkg/tbtc/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,32 @@ type DistributedKeyGenerationChain interface {
DKGParameters() (*DKGParameters, error)
}

// InactivityChainClaim represents an inactivity claim submitted to the chain.
type InactivityChainClaim struct {
WalletID [32]byte
InactiveMembersIndices []group.MemberIndex
HeartbeatFailed bool
Signatures []byte
SigningMembersIndices []group.MemberIndex
}

type InactivityClaimChain interface {
// AssembleDKGResult assembles the inactivity chain claim according to the
// rules expected by the given chain.
AssembleInactivityClaim(
walletID [32]byte,
inactiveMembersIndices []group.MemberIndex,
signatures map[group.MemberIndex][]byte,
heartbeatFailed bool,
) (*InactivityChainClaim, error)

// SubmitInactivityClaim submits the inactivity claim to the chain.
SubmitInactivityClaim(
claim *InactivityChainClaim,
nonce *big.Int,
groupMembers []uint32,
) error

// CalculateInactivityClaimSignatureHash calculates hash for the given
// inactivity claim.
CalculateInactivityClaimSignatureHash(
Expand Down
20 changes: 20 additions & 0 deletions pkg/tbtc/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,26 @@ func (lc *localChain) DKGParameters() (*DKGParameters, error) {
}, nil
}

func (lc *localChain) AssembleInactivityClaim(
walletID [32]byte,
inactiveMembersIndices []group.MemberIndex,
signatures map[group.MemberIndex][]byte,
heartbeatFailed bool,
) (
*InactivityChainClaim,
error,
) {
panic("unsupported")
}

func (lc *localChain) SubmitInactivityClaim(
claim *InactivityChainClaim,
nonce *big.Int,
groupMembers []uint32,
) error {
panic("unsupported")
}

func (lc *localChain) CalculateInactivityClaimSignatureHash(
claim *inactivity.Claim,
) (inactivity.ClaimSignatureHash, error) {
Expand Down
52 changes: 50 additions & 2 deletions pkg/tbtc/inactivity.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,21 @@ import (
"golang.org/x/sync/semaphore"

"github.com/keep-network/keep-core/pkg/bitcoin"
"github.com/keep-network/keep-core/pkg/chain"
"github.com/keep-network/keep-core/pkg/generator"
"github.com/keep-network/keep-core/pkg/net"
"github.com/keep-network/keep-core/pkg/protocol/group"
"github.com/keep-network/keep-core/pkg/tecdsa/inactivity"
)

const (
// inactivityClaimSubmissionDelayStepBlocks determines the delay step in blocks
// that is used to calculate the submission delay period that should be respected
// by the given member to avoid all members submitting the same inactivity claim
// at the same time.
inactivityClaimSubmissionDelayStepBlocks = 3
)

// errInactivityClaimExecutorBusy is an error returned when the inactivity claim
// executor cannot execute the inactivity claim due to another inactivity claim
// execution in progress.
Expand All @@ -30,7 +39,7 @@ type inactivityClaimExecutor struct {
broadcastChannel net.BroadcastChannel
membershipValidator *group.MembershipValidator
groupParameters *GroupParameters
protocolLatch *generator.ProtocolLatch
protocolLatch *generator.ProtocolLatch

waitForBlockFn waitForBlockFn
}
Expand Down Expand Up @@ -98,6 +107,11 @@ func (ice *inactivityClaimExecutor) publishClaim(
HeartbeatFailed: heartbeatFailed,
}

groupMembers, err := ice.getWalletMembersInfo()
if err != nil {
return fmt.Errorf("could not get wallet members info: [%v]", err)
}

wg := sync.WaitGroup{}
wg.Add(len(ice.signers))

Expand Down Expand Up @@ -126,6 +140,7 @@ func (ice *inactivityClaimExecutor) publishClaim(
wallet.groupDishonestThreshold(
ice.groupParameters.HonestThreshold,
),
groupMembers,
ice.membershipValidator,
claim,
)
Expand All @@ -139,13 +154,40 @@ func (ice *inactivityClaimExecutor) publishClaim(
return nil
}

func (ice *inactivityClaimExecutor) getWalletMembersInfo() ([]uint32, error) {
// Cache mapping operator addresses to their wallet member IDs. It helps to
// limit the number of calls to the ETH client if some operator addresses
// occur on the list multiple times.
operatorIDCache := make(map[chain.Address]uint32)

walletMemberIDs := make([]uint32, 0)

for _, operatorAddress := range ice.wallet().signingGroupOperators {
// Search for the operator address in the cache. Store the operator
// address in the cache if it's not there.
if operatorID, found := operatorIDCache[operatorAddress]; !found {
fetchedOperatorID, err := ice.chain.GetOperatorID(operatorAddress)
if err != nil {
return nil, fmt.Errorf("could not get operator ID: [%w]", err)
}
operatorIDCache[operatorAddress] = fetchedOperatorID
walletMemberIDs = append(walletMemberIDs, fetchedOperatorID)
} else {
walletMemberIDs = append(walletMemberIDs, operatorID)
}
}

return walletMemberIDs, nil
}

func (ice *inactivityClaimExecutor) publish(
ctx context.Context,
inactivityLogger log.StandardLogger,
seed *big.Int,
memberIndex group.MemberIndex,
groupSize int,
dishonestThreshold int,
groupMembers []uint32,
membershipValidator *group.MembershipValidator,
inactivityClaim *inactivity.Claim,
) error {
Expand All @@ -159,7 +201,13 @@ func (ice *inactivityClaimExecutor) publish(
dishonestThreshold,
membershipValidator,
newInactivityClaimSigner(ice.chain),
newInactivityClaimSubmitter(),
newInactivityClaimSubmitter(
inactivityLogger,
ice.chain,
ice.groupParameters,
groupMembers,
ice.waitForBlockFn,
),
inactivityClaim,
)
}
Expand Down
Loading

0 comments on commit bd181a9

Please sign in to comment.