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

RFC 12 Implementation: Coordination result processing #3750

Merged
merged 4 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 4 additions & 135 deletions pkg/tbtc/deduplicator.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package tbtc

import (
"bytes"
"crypto/sha256"
"encoding/binary"
"encoding/hex"
"math/big"
"strconv"
Expand All @@ -19,15 +16,6 @@ const (
// DKGResultHashCachePeriod is the time period the cache maintains
// the given DKG result hash.
DKGResultHashCachePeriod = 7 * 24 * time.Hour
// HeartbeatRequestCachePeriod is the time period the cache maintains
// the given heartbeat request.
HeartbeatRequestCachePeriod = 24 * time.Hour
// DepositSweepProposalCachePeriod is the time period the cache maintains
// the given deposit sweep proposal.
DepositSweepProposalCachePeriod = 7 * 24 * time.Hour
// RedemptionProposalCachePeriod is the time period the cache maintains
// the given redemption proposal.
RedemptionProposalCachePeriod = 24 * time.Hour
)

// deduplicator decides whether the given event should be handled by the
Expand All @@ -43,24 +31,15 @@ const (
// Those events are supported:
// - DKG started
// - DKG result submitted
// - Heartbeat request submission
// - Deposit sweep proposal submission
// - Redemption proposal submission
type deduplicator struct {
dkgSeedCache *cache.TimeCache
dkgResultHashCache *cache.TimeCache
heartbeatRequestCache *cache.TimeCache
depositSweepProposalCache *cache.TimeCache
redemptionProposalCache *cache.TimeCache
dkgSeedCache *cache.TimeCache
dkgResultHashCache *cache.TimeCache
}

func newDeduplicator() *deduplicator {
return &deduplicator{
dkgSeedCache: cache.NewTimeCache(DKGSeedCachePeriod),
dkgResultHashCache: cache.NewTimeCache(DKGResultHashCachePeriod),
heartbeatRequestCache: cache.NewTimeCache(HeartbeatRequestCachePeriod),
depositSweepProposalCache: cache.NewTimeCache(DepositSweepProposalCachePeriod),
redemptionProposalCache: cache.NewTimeCache(RedemptionProposalCachePeriod),
dkgSeedCache: cache.NewTimeCache(DKGSeedCachePeriod),
dkgResultHashCache: cache.NewTimeCache(DKGResultHashCachePeriod),
}
}

Expand Down Expand Up @@ -111,113 +90,3 @@ func (d *deduplicator) notifyDKGResultSubmitted(
// proceed with the execution.
return false
}

// notifyHeartbeatRequestSubmitted notifies the client wants to start some
// actions upon the heartbeat request submitted. It returns boolean indicating
// whether the client should proceed with the actions or ignore the event as
// a duplicate.
func (d *deduplicator) notifyHeartbeatRequestSubmitted(
walletPublicKeyHash [20]byte,
message []byte,
) bool {
d.heartbeatRequestCache.Sweep()

var buffer bytes.Buffer
buffer.Write(walletPublicKeyHash[:])
buffer.Write(message)

bufferSha256 := sha256.Sum256(buffer.Bytes())
cacheKey := hex.EncodeToString(bufferSha256[:])

// If the key is not in the cache, that means the request was not handled
// yet and the client should proceed with the execution.
if !d.heartbeatRequestCache.Has(cacheKey) {
d.heartbeatRequestCache.Add(cacheKey)
return true
}

// Otherwise, the request is a duplicate and the client should not
// proceed with the execution.
return false
}

// notifyDepositSweepProposalSubmitted notifies the client wants to start some
// actions upon the deposit sweep proposal submission. It returns boolean
// indicating whether the client should proceed with the actions or ignore the
// event as a duplicate.
func (d *deduplicator) notifyDepositSweepProposalSubmitted(
newProposal *DepositSweepProposal,
) bool {
d.depositSweepProposalCache.Sweep()

// We build the cache key by hashing the concatenation of relevant fields
// of the proposal. It may be tempting to extract that code into a general
// "hash code" function exposed by the DepositSweepProposal type but this
// is not necessarily a good idea. The deduplicator is responsible for
// detecting duplicates and construction of cache keys is part of that job.
// Extracting this logic outside would push that responsibility out of the
// deduplicator control. That is dangerous as deduplication logic could
// be implicitly changeable from the outside and lead to serious bugs.
var buffer bytes.Buffer
buffer.Write(newProposal.WalletPublicKeyHash[:])
for _, depositKey := range newProposal.DepositsKeys {
buffer.Write(depositKey.FundingTxHash[:])
fundingOutputIndex := make([]byte, 4)
binary.BigEndian.PutUint32(fundingOutputIndex, depositKey.FundingOutputIndex)
buffer.Write(fundingOutputIndex)
}
buffer.Write(newProposal.SweepTxFee.Bytes())

bufferSha256 := sha256.Sum256(buffer.Bytes())
cacheKey := hex.EncodeToString(bufferSha256[:])

// If the key is not in the cache, that means the proposal was not handled
// yet and the client should proceed with the execution.
if !d.depositSweepProposalCache.Has(cacheKey) {
d.depositSweepProposalCache.Add(cacheKey)
return true
}

// Otherwise, the proposal is a duplicate and the client should not
// proceed with the execution.
return false
}

// notifyRedemptionProposalSubmitted notifies the client wants to start some
// actions upon the redemption proposal submission. It returns boolean
// indicating whether the client should proceed with the actions or ignore the
// event as a duplicate.
func (d *deduplicator) notifyRedemptionProposalSubmitted(
newProposal *RedemptionProposal,
) bool {
d.redemptionProposalCache.Sweep()

// We build the cache key by hashing the concatenation of relevant fields
// of the proposal. It may be tempting to extract that code into a general
// "hash code" function exposed by the RedemptionProposal type but this
// is not necessarily a good idea. The deduplicator is responsible for
// detecting duplicates and construction of cache keys is part of that job.
// Extracting this logic outside would push that responsibility out of the
// deduplicator control. That is dangerous as deduplication logic could
// be implicitly changeable from the outside and lead to serious bugs.
var buffer bytes.Buffer
buffer.Write(newProposal.WalletPublicKeyHash[:])
for _, script := range newProposal.RedeemersOutputScripts {
buffer.Write(script)
}
buffer.Write(newProposal.RedemptionTxFee.Bytes())

bufferSha256 := sha256.Sum256(buffer.Bytes())
cacheKey := hex.EncodeToString(bufferSha256[:])

// If the key is not in the cache, that means the proposal was not handled
// yet and the client should proceed with the execution.
if !d.redemptionProposalCache.Has(cacheKey) {
d.redemptionProposalCache.Add(cacheKey)
return true
}

// Otherwise, the proposal is a duplicate and the client should not
// proceed with the execution.
return false
}
Loading