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

Archive key material on wallet closure #3814

Merged
merged 17 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 7 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
30 changes: 30 additions & 0 deletions pkg/chain/ethereum/tbtc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,21 @@ func (tc *TbtcChain) PastNewWalletRegisteredEvents(
return convertedEvents, err
}

func (tc *TbtcChain) IsWalletRegistered(EcdsaWalletID [32]byte) (bool, error) {
isWalletRegistered, err := tc.walletRegistry.IsWalletRegistered(
EcdsaWalletID,
)
if err != nil {
return false, fmt.Errorf(
"cannot check if wallet with ECDSA ID [0x%x] is registered: [%v]",
EcdsaWalletID,
err,
)
}

return isWalletRegistered, nil
}

func (tc *TbtcChain) GetWallet(
walletPublicKeyHash [20]byte,
) (*tbtc.WalletChainData, error) {
Expand Down Expand Up @@ -1453,6 +1468,21 @@ func (tc *TbtcChain) GetWallet(
}, nil
}

func (tc *TbtcChain) OnWalletClosed(
handler func(event *tbtc.WalletClosedEvent),
) subscription.EventSubscription {
onEvent := func(
walletID [32]byte,
blockNumber uint64,
) {
handler(&tbtc.WalletClosedEvent{
WalletID: walletID,
BlockNumber: blockNumber,
})
}
return tc.walletRegistry.WalletClosedEvent(nil, nil).OnEvent(onEvent)
}

func (tc *TbtcChain) ComputeMainUtxoHash(
mainUtxo *bitcoin.UnspentTransactionOutput,
) [32]byte {
Expand Down
26 changes: 26 additions & 0 deletions pkg/tbtc/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,17 +235,43 @@ type DKGParameters struct {
ApprovePrecedencePeriodBlocks uint64
}

// WalletClosedEvent represents a wallet closed. It is emitted when the wallet
// is closed in the wallet registry.
type WalletClosedEvent struct {
WalletID [32]byte
BlockNumber uint64
}

// BridgeChain defines the subset of the TBTC chain interface that pertains
// specifically to the tBTC Bridge operations.
type BridgeChain interface {
// IsWalletRegistered checks whether the given wallet is registered in the
// ECDSA wallet registry.
IsWalletRegistered(EcdsaWalletID [32]byte) (bool, error)

// GetWallet gets the on-chain data for the given wallet. Returns an error
// if the wallet was not found.
GetWallet(walletPublicKeyHash [20]byte) (*WalletChainData, error)

// OnWalletClosed registers a callback that is invoked when an on-chain
// notification of the wallet closed is seen. The notification occurs when
// the wallet is closed or terminated.
OnWalletClosed(
func(event *WalletClosedEvent),
) subscription.EventSubscription

// ComputeMainUtxoHash computes the hash of the provided main UTXO
// according to the on-chain Bridge rules.
ComputeMainUtxoHash(mainUtxo *bitcoin.UnspentTransactionOutput) [32]byte

// PastNewWalletRegisteredEvents fetches past new wallet registered events
// according to the provided filter or unfiltered if the filter is nil.
// Returned events are sorted by the block number in the ascending order,
// i.e. the latest event is at the end of the slice.
PastNewWalletRegisteredEvents(
filter *NewWalletRegisteredEventFilter,
) ([]*NewWalletRegisteredEvent, error)

// PastDepositRevealedEvents fetches past deposit reveal events according
// to the provided filter or unfiltered if the filter is nil. Returned
// events are sorted by the block number in the ascending order, i.e. the
Expand Down
16 changes: 16 additions & 0 deletions pkg/tbtc/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,12 @@ func (lc *localChain) GetInactivityClaimNonce(walletID [32]byte) (*big.Int, erro
return big.NewInt(int64(nonce)), nil
}

func (lc *localChain) PastNewWalletRegisteredEvents(
filter *NewWalletRegisteredEventFilter,
) ([]*NewWalletRegisteredEvent, error) {
panic("unsupported")
}

func (lc *localChain) PastDepositRevealedEvents(
filter *DepositRevealedEventFilter,
) ([]*DepositRevealedEvent, error) {
Expand Down Expand Up @@ -843,6 +849,10 @@ func buildDepositRequestKey(
return sha256.Sum256(append(fundingTxHash[:], buffer...))
}

func (lc *localChain) IsWalletRegistered(EcdsaWalletID [32]byte) (bool, error) {
panic("unsupported")
}

func (lc *localChain) GetWallet(walletPublicKeyHash [20]byte) (
*WalletChainData,
error,
Expand All @@ -868,6 +878,12 @@ func (lc *localChain) setWallet(
lc.wallets[walletPublicKeyHash] = walletChainData
}

func (lc *localChain) OnWalletClosed(
handler func(event *WalletClosedEvent),
) subscription.EventSubscription {
panic("unsupported")
}

func (lc *localChain) ComputeMainUtxoHash(
mainUtxo *bitcoin.UnspentTransactionOutput,
) [32]byte {
Expand Down
26 changes: 26 additions & 0 deletions pkg/tbtc/deduplicator.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const (
// DKGResultHashCachePeriod is the time period the cache maintains
// the given DKG result hash.
DKGResultHashCachePeriod = 7 * 24 * time.Hour
// WalletClosedCachePeriod is the time period the cache maintains
// the given wallet closed hash.
WalletClosedCachePeriod = 7 * 24 * time.Hour
)

// deduplicator decides whether the given event should be handled by the
Expand All @@ -31,15 +34,18 @@ const (
// Those events are supported:
// - DKG started
// - DKG result submitted
// - Wallet closed
type deduplicator struct {
dkgSeedCache *cache.TimeCache
dkgResultHashCache *cache.TimeCache
walletClosedCache *cache.TimeCache
}

func newDeduplicator() *deduplicator {
return &deduplicator{
dkgSeedCache: cache.NewTimeCache(DKGSeedCachePeriod),
dkgResultHashCache: cache.NewTimeCache(DKGResultHashCachePeriod),
walletClosedCache: cache.NewTimeCache(WalletClosedCachePeriod),
}
}

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

func (d *deduplicator) notifyWalletClosed(
WalletID [32]byte,
) bool {
d.walletClosedCache.Sweep()

// Use wallet ID converted to string as the cache key.
cacheKey := hex.EncodeToString(WalletID[:])

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

// Otherwise, the wallet closure is a duplicate and the client should not
// proceed with the execution.
return false
}
43 changes: 41 additions & 2 deletions pkg/tbtc/deduplicator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import (
"github.com/keep-network/keep-common/pkg/cache"
)

const testDKGSeedCachePeriod = 1 * time.Second
const testDKGResultHashCachePeriod = 1 * time.Second
const (
testDKGSeedCachePeriod = 1 * time.Second
testDKGResultHashCachePeriod = 1 * time.Second
testWalletClosedCachePeriod = 1 * time.Second
)

func TestNotifyDKGStarted(t *testing.T) {
deduplicator := deduplicator{
Expand Down Expand Up @@ -112,3 +115,39 @@ func TestNotifyDKGResultSubmitted(t *testing.T) {
t.Fatal("should be allowed to process")
}
}

func TestNotifyWalletClosed(t *testing.T) {
deduplicator := deduplicator{
walletClosedCache: cache.NewTimeCache(testWalletClosedCachePeriod),
}

wallet1 := [32]byte{1}
wallet2 := [32]byte{2}

// Add the first wallet ID.
canProcess := deduplicator.notifyWalletClosed(wallet1)
if !canProcess {
t.Fatal("should be allowed to process")
}

// Add the second wallet ID.
canProcess = deduplicator.notifyWalletClosed(wallet2)
if !canProcess {
t.Fatal("should be allowed to process")
}

// Add the first wallet ID before caching period elapses.
canProcess = deduplicator.notifyWalletClosed(wallet1)
if canProcess {
t.Fatal("should not be allowed to process")
}

// Wait until caching period elapses.
time.Sleep(testWalletClosedCachePeriod)

// Add the first wallet ID again.
canProcess = deduplicator.notifyWalletClosed(wallet1)
if !canProcess {
t.Fatal("should be allowed to process")
}
}
Loading
Loading