Skip to content

Commit

Permalink
Added function for getting wallets by ID
Browse files Browse the repository at this point in the history
  • Loading branch information
tomaszslabon committed Jun 11, 2024
1 parent 4429057 commit f0eca28
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 81 deletions.
8 changes: 0 additions & 8 deletions pkg/tbtc/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,14 +268,6 @@ type BridgeChain interface {
// 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
40 changes: 31 additions & 9 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 @@ -697,12 +697,6 @@ 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 @@ -856,7 +850,35 @@ func (lc *localChain) IsWalletRegistered(EcdsaWalletID [32]byte) (bool, error) {
func (lc *localChain) CalculateWalletID(
walletPublicKey *ecdsa.PublicKey,
) ([32]byte, error) {
panic("unsupported")
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) (
Expand Down
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
47 changes: 26 additions & 21 deletions pkg/tbtc/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,13 @@ func newNode(
proposalGenerator CoordinationProposalGenerator,
config Config,
) (*node, error) {
walletRegistry := newWalletRegistry(keyStorePersistance)
walletRegistry, err := newWalletRegistry(
keyStorePersistance,
chain.CalculateWalletID,
)
if err != nil {
return nil, fmt.Errorf("cannot create wallet registry: [%v]", err)
}

latch := generator.NewProtocolLatch()
scheduler.RegisterProtocol(latch)
Expand All @@ -146,7 +152,7 @@ func newNode(

// Archive any wallets that might have been closed or terminated while the
// client was turned off.
err := node.archiveClosedWallets()
err = node.archiveClosedWallets()
if err != nil {
return nil, fmt.Errorf("cannot archive closed wallets: [%v]", err)
}
Expand Down Expand Up @@ -1197,33 +1203,32 @@ func (n *node) handleWalletClosure(walletID [32]byte) error {
return fmt.Errorf("wallet closure not confirmed")
}

events, err := n.chain.PastNewWalletRegisteredEvents(
&NewWalletRegisteredEventFilter{
EcdsaWalletID: [][32]byte{walletID},
},
)
if err != nil {
return fmt.Errorf("could not get past new wallet registered events")
}

// There should be only one event returned and the ECDSA wallet ID should
// match the requested wallet ID. These errors should never happen, but
// check just in case.
if len(events) != 1 {
return fmt.Errorf("wrong number of past new wallet registered events")
}

if events[0].EcdsaWalletID != walletID {
return fmt.Errorf("wrong past new wallet registered event returned")
wallet, ok := n.walletRegistry.getWalletByID(walletID)
if !ok {
// Wallet was not found in the registry. The wallet is not controlled by
// this node.
logger.Infof(
"node does not control wallet with ID [0x%x]; quitting wallet "+
"archiving",
walletID,
)
return nil
}

walletPublicKeyHash := events[0].WalletPublicKeyHash
walletPublicKeyHash := bitcoin.PublicKeyHash(wallet.publicKey)

err = n.walletRegistry.archiveWallet(walletPublicKeyHash)
if err != nil {
return fmt.Errorf("failed to archive the wallet: [%v]", err)
}

logger.Infof(
"Successfully archived wallet with wallet ID [0x%x] and public key "+
"hash [0x%x]",
walletID,
walletPublicKeyHash,
)

return nil
}

Expand Down
67 changes: 55 additions & 12 deletions pkg/tbtc/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import (
"github.com/keep-network/keep-common/pkg/persistence"
)

// CalculateWalletIDFunc calculates the ECDSA wallet ID based on the provided
// wallet public key.
type CalculateWalletIdFunc func(walletPublicKey *ecdsa.PublicKey) ([32]byte, error)

// walletRegistry is the component that holds the data of the wallets managed
// by the given node. All functions of the registry are safe for concurrent use.
type walletRegistry struct {
Expand All @@ -26,18 +30,28 @@ type walletRegistry struct {
// walletStorage is the handle to the wallet storage responsible for
// wallet persistence.
walletStorage *walletStorage

// calculateWalletIdFunc calculates the ECDSA wallet ID based on the
// provided wallet public key.
calculateWalletIdFunc CalculateWalletIdFunc
}

type walletCacheValue struct {
// SHA-256+RIPEMD-160 hash computed over the compressed ECDSA public key of
// the wallet.
walletPublicKeyHash [20]byte
// ECDSA wallet ID calculated as the keccak256 of the 64-byte-long
// concatenation of the X and Y coordinates of the wallet's public key.
walletID [32]byte
// Array of wallet signers controlled by this node.
signers []*signer
}

// newWalletRegistry creates a new instance of the walletRegistry.
func newWalletRegistry(persistence persistence.ProtectedHandle) *walletRegistry {
func newWalletRegistry(
persistence persistence.ProtectedHandle,
calculateWalletIdFunc CalculateWalletIdFunc,
) (*walletRegistry, error) {
walletStorage := newWalletStorage(persistence)

// Pre-populate the wallet cache using the wallet storage.
Expand All @@ -53,9 +67,19 @@ func newWalletRegistry(persistence persistence.ProtectedHandle) *walletRegistry
// them.
wallet := signers[0].wallet
walletPublicKeyHash := bitcoin.PublicKeyHash(wallet.publicKey)
walletID, err := calculateWalletIdFunc(wallet.publicKey)
if err != nil {
return nil, fmt.Errorf(
"error while calculating wallet ID for wallet with public "+
"key hash [0x%x]: [%v]",
walletPublicKeyHash,
err,
)
}

walletCache[walletStorageKey] = &walletCacheValue{
walletPublicKeyHash: walletPublicKeyHash,
walletID: walletID,
signers: signers,
}

Expand All @@ -72,9 +96,10 @@ func newWalletRegistry(persistence persistence.ProtectedHandle) *walletRegistry
}

return &walletRegistry{
walletCache: walletCache,
walletStorage: walletStorage,
}
walletCache: walletCache,
walletStorage: walletStorage,
calculateWalletIdFunc: calculateWalletIdFunc,
}, nil
}

// getWalletsPublicKeys returns public keys of all registered wallets.
Expand Down Expand Up @@ -105,12 +130,18 @@ func (wr *walletRegistry) registerSigner(signer *signer) error {
walletStorageKey := getWalletStorageKey(signer.wallet.publicKey)

// If the wallet cache does not have the given entry yet, initialize
// the value and compute the wallet public key hash. This way, the hash
// is computed only once. No need to initialize signers slice as
// the value and compute the wallet ID and wallet public key hash. This way,
// the hashes are computed only once. No need to initialize signers slice as
// appending works with nil values.
if _, ok := wr.walletCache[walletStorageKey]; !ok {
walletID, err := wr.calculateWalletIdFunc(signer.wallet.publicKey)
if err != nil {
return fmt.Errorf("cannot calculate wallet ID: [%v]", err)
}

wr.walletCache[walletStorageKey] = &walletCacheValue{
walletPublicKeyHash: bitcoin.PublicKeyHash(signer.wallet.publicKey),
walletID: walletID,
}
}

Expand Down Expand Up @@ -156,6 +187,23 @@ func (wr *walletRegistry) getWalletByPublicKeyHash(
return wallet{}, false
}

// getWalletByID gets the given wallet by its 32-byte wallet ID. Second boolean
// return value denotes whether the wallet was found in the registry or not.
func (wr *walletRegistry) getWalletByID(walletID [32]byte) (wallet, bool) {
wr.mutex.Lock()
defer wr.mutex.Unlock()

for _, value := range wr.walletCache {
if value.walletID == walletID {
// All signers belong to one wallet. Take that wallet from the
// first signer.
return value.signers[0].wallet, true
}
}

return wallet{}, false
}

// archiveWallet archives the wallet with the given public key hash. The wallet
// data is removed from the wallet cache and the entire wallet storage directory
// is moved to the archive directory.
Expand All @@ -176,12 +224,7 @@ func (wr *walletRegistry) archiveWallet(
}

if walletPublicKey == nil {
logger.Infof(
"node does not control wallet with public key hash [0x%x]; "+
"quitting wallet archiving",
walletPublicKeyHash,
)
return nil
return fmt.Errorf("wallet not found in the wallet cache")
}

walletStorageKey := getWalletStorageKey(walletPublicKey)
Expand Down
Loading

0 comments on commit f0eca28

Please sign in to comment.