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 16 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
48 changes: 48 additions & 0 deletions pkg/chain/ethereum/tbtc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,39 @@ func (tc *TbtcChain) PastNewWalletRegisteredEvents(
return convertedEvents, err
}

func (tc *TbtcChain) CalculateWalletID(
walletPublicKey *ecdsa.PublicKey,
) ([32]byte, error) {
return calculateWalletID(walletPublicKey)
}

func calculateWalletID(walletPublicKey *ecdsa.PublicKey) ([32]byte, error) {
walletPublicKeyBytes, err := convertPubKeyToChainFormat(walletPublicKey)
if err != nil {
return [32]byte{}, fmt.Errorf(
"error while converting wallet public key to chain format: [%v]",
err,
)
}

return crypto.Keccak256Hash(walletPublicKeyBytes[:]), nil
}

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 +1486,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
44 changes: 44 additions & 0 deletions pkg/chain/ethereum/tbtc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/ethereum/go-ethereum/common"

"github.com/keep-network/keep-core/internal/testutils"
"github.com/keep-network/keep-core/pkg/chain/local_v1"
"github.com/keep-network/keep-core/pkg/protocol/group"
)

Expand Down Expand Up @@ -279,6 +280,49 @@ func TestCalculateInactivityClaimHash(t *testing.T) {
)
}

func TestCalculateWalletID(t *testing.T) {
hexToByte32 := func(hexStr string) [32]byte {
if len(hexStr) != 64 {
t.Fatal("hex string length incorrect")
}

decoded, err := hex.DecodeString(hexStr)
if err != nil {
t.Fatal(err)
}

var result [32]byte
copy(result[:], decoded)

return result
}

xBytes := hexToByte32(
"9a0544440cc47779235ccb76d669590c2cd20c7e431f97e17a1093faf03291c4",
)

yBytes := hexToByte32(
"73e661a208a8a565ca1e384059bd2ff7ff6886df081ff1229250099d388c83df",
)
lukasz-zimnoch marked this conversation as resolved.
Show resolved Hide resolved

walletPublicKey := &ecdsa.PublicKey{
Curve: local_v1.DefaultCurve,
X: new(big.Int).SetBytes(xBytes[:]),
Y: new(big.Int).SetBytes(yBytes[:]),
}

actualWalletID, err := calculateWalletID(walletPublicKey)
if err != nil {
t.Fatal(err)
}

expectedWalletID := hexToByte32(
"a6602e554b8cf7c23538fd040e4ff3520ec680e5e5ce9a075259e613a3e5aa79",
)

testutils.AssertBytesEqual(t, expectedWalletID[:], actualWalletID[:])
}

func TestParseDkgResultValidationOutcome(t *testing.T) {
isValid, err := parseDkgResultValidationOutcome(
&struct {
Expand Down
22 changes: 22 additions & 0 deletions pkg/tbtc/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,35 @@ type DKGParameters struct {
ApprovePrecedencePeriodBlocks uint64
}

// WalletClosedEvent represents a wallet closed event. 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 {
// CalculateWalletID calculates the wallet's ECDSA ID based on the provided
// wallet public key.
CalculateWalletID(walletPublicKey *ecdsa.PublicKey) ([32]byte, error)

// 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
Expand Down
48 changes: 46 additions & 2 deletions pkg/tbtc/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ import (
"sync"
"time"

"golang.org/x/crypto/sha3"

"github.com/ethereum/go-ethereum/crypto"
"github.com/keep-network/keep-core/pkg/bitcoin"
"github.com/keep-network/keep-core/pkg/chain"
"github.com/keep-network/keep-core/pkg/chain/local_v1"
"github.com/keep-network/keep-core/pkg/internal/byteutils"
"github.com/keep-network/keep-core/pkg/operator"
"github.com/keep-network/keep-core/pkg/protocol/group"
"github.com/keep-network/keep-core/pkg/protocol/inactivity"
"github.com/keep-network/keep-core/pkg/subscription"
"github.com/keep-network/keep-core/pkg/tecdsa/dkg"
"golang.org/x/crypto/sha3"
)

const (
Expand Down Expand Up @@ -843,6 +843,44 @@ func buildDepositRequestKey(
return sha256.Sum256(append(fundingTxHash[:], buffer...))
}

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

func (lc *localChain) CalculateWalletID(
walletPublicKey *ecdsa.PublicKey,
) ([32]byte, error) {
walletPublicKeyBytes, err := convertPubKeyToChainFormat(walletPublicKey)
if err != nil {
return [32]byte{}, fmt.Errorf(
"error while converting wallet public key to chain format: [%v]",
err,
)
}

return crypto.Keccak256Hash(walletPublicKeyBytes[:]), nil
}

func convertPubKeyToChainFormat(publicKey *ecdsa.PublicKey) ([64]byte, error) {
var serialized [64]byte

x, err := byteutils.LeftPadTo32Bytes(publicKey.X.Bytes())
if err != nil {
return serialized, err
}

y, err := byteutils.LeftPadTo32Bytes(publicKey.Y.Bytes())
if err != nil {
return serialized, err
}

serializedBytes := append(x, y...)

copy(serialized[:], serializedBytes)

return serialized, nil
}

func (lc *localChain) GetWallet(walletPublicKeyHash [20]byte) (
*WalletChainData,
error,
Expand All @@ -868,6 +906,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 ID of
// a closed wallet.
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")
}
}
9 changes: 8 additions & 1 deletion pkg/tbtc/dkg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,14 @@ func TestDkgExecutor_RegisterSigner(t *testing.T) {
for testName, test := range tests {
t.Run(testName, func(t *testing.T) {
persistenceHandle := &mockPersistenceHandle{}
walletRegistry := newWalletRegistry(persistenceHandle)
chain := Connect()
walletRegistry, err := newWalletRegistry(
persistenceHandle,
chain.CalculateWalletID,
)
if err != nil {
t.Fatal(err)
}

dkgExecutor := &dkgExecutor{
// setting only the fields really needed for this test
Expand Down
Loading
Loading