From 6d3e2daedebe731f33ec3c856ab54c0d28c73100 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Thu, 22 Oct 2020 13:59:38 +0200 Subject: [PATCH 01/31] Stub of the provide redemption signature action Added a stub of the provide redemption signature monitoring process. Also, implemented an events log which will be needed at later stage. --- pkg/chain/ethereum/tbtc.go | 48 ++++++++++++++++ pkg/chain/local/tbtc.go | 16 ++++++ pkg/extensions/tbtc/chain.go | 29 ++++++++++ pkg/extensions/tbtc/events_log.go | 49 ++++++++++++++++ pkg/extensions/tbtc/tbtc.go | 96 +++++++++++++++++++++++++++++-- pkg/extensions/tbtc/tbtc_test.go | 14 ++--- 6 files changed, 241 insertions(+), 11 deletions(-) create mode 100644 pkg/extensions/tbtc/events_log.go diff --git a/pkg/chain/ethereum/tbtc.go b/pkg/chain/ethereum/tbtc.go index a61798240..297d9b9b6 100644 --- a/pkg/chain/ethereum/tbtc.go +++ b/pkg/chain/ethereum/tbtc.go @@ -89,6 +89,54 @@ func (tec *TBTCEthereumChain) OnDepositRegisteredPubkey( ) } +// OnDepositRedemptionRequested installs a callback that is invoked when an +// on-chain notification of a new deposit redemption request is seen. +func (tec *TBTCEthereumChain) OnDepositRedemptionRequested( + handler func( + depositAddress string, + requesterAddress string, + digest [32]uint8, + utxoValue *big.Int, + redeemerOutputScript []uint8, + requestedFee *big.Int, + outpoint []uint8, + blockNumber uint64, + ), +) (subscription.EventSubscription, error) { + return tec.tbtcSystemContract.WatchRedemptionRequested( + func( + DepositContractAddress common.Address, + Requester common.Address, + Digest [32]uint8, + UtxoValue *big.Int, + RedeemerOutputScript []uint8, + RequestedFee *big.Int, + Outpoint []uint8, + blockNumber uint64, + ) { + handler( + DepositContractAddress.Hex(), + Requester.Hex(), + Digest, + UtxoValue, + RedeemerOutputScript, + RequestedFee, + Outpoint, + blockNumber, + ) + }, + func(err error) error { + return fmt.Errorf( + "watch deposit redemption requested failed: [%v]", + err, + ) + }, + nil, + nil, + nil, + ) +} + // KeepAddress returns the underlying keep address for the // provided deposit. func (tec *TBTCEthereumChain) KeepAddress( diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index c3e6d2198..dfe0b00b3 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -3,6 +3,7 @@ package local import ( "bytes" "fmt" + "math/big" "sync" "github.com/ethereum/go-ethereum/common" @@ -106,6 +107,21 @@ func (tlc *TBTCLocalChain) OnDepositRegisteredPubkey( }), nil } +func (tlc *TBTCLocalChain) OnDepositRedemptionRequested( + handler func( + depositAddress string, + requesterAddress string, + digest [32]uint8, + utxoValue *big.Int, + redeemerOutputScript []uint8, + requestedFee *big.Int, + outpoint []uint8, + blockNumber uint64, + ), +) (subscription.EventSubscription, error) { + panic("not implemented") // TODO: Implementation for unit testing purposes. +} + func (tlc *TBTCLocalChain) KeepAddress(depositAddress string) (string, error) { tlc.mutex.Lock() defer tlc.mutex.Unlock() diff --git a/pkg/extensions/tbtc/chain.go b/pkg/extensions/tbtc/chain.go index 598814880..078370f76 100644 --- a/pkg/extensions/tbtc/chain.go +++ b/pkg/extensions/tbtc/chain.go @@ -1,6 +1,8 @@ package tbtc import ( + "math/big" + "github.com/keep-network/keep-common/pkg/subscription" chain "github.com/keep-network/keep-ecdsa/pkg/chain" ) @@ -39,4 +41,31 @@ type TBTCSystem interface { OnDepositRegisteredPubkey( handler func(depositAddress string), ) (subscription.EventSubscription, error) + + // OnDepositRedemptionRequested installs a callback that is invoked when an + // on-chain notification of a new deposit redemption request is seen. + OnDepositRedemptionRequested( + handler func( + depositAddress string, + requesterAddress string, + digest [32]uint8, + utxoValue *big.Int, + redeemerOutputScript []uint8, + requestedFee *big.Int, + outpoint []uint8, + blockNumber uint64, + ), + ) (subscription.EventSubscription, error) +} + +// DepositRedemptionRequestedEvent represents a deposit redemption requested event. +type DepositRedemptionRequestedEvent struct { + DepositAddress string + RequesterAddress string + Digest [32]uint8 + UtxoValue *big.Int + RedeemerOutputScript []uint8 + RequestedFee *big.Int + Outpoint []uint8 + BlockNumber uint64 } diff --git a/pkg/extensions/tbtc/events_log.go b/pkg/extensions/tbtc/events_log.go new file mode 100644 index 000000000..62f5ccb95 --- /dev/null +++ b/pkg/extensions/tbtc/events_log.go @@ -0,0 +1,49 @@ +package tbtc + +import ( + "sync" +) + +type tbtcEventsLog struct { + depositRedemptionRequestedEventStorage *eventsStorage +} + +func newTBTCEventsLog() *tbtcEventsLog { + return &tbtcEventsLog{ + depositRedemptionRequestedEventStorage: newEventsStorage(), + } +} + +func (tel *tbtcEventsLog) logDepositRedemptionRequestedEvent( + deposit string, + event *DepositRedemptionRequestedEvent, +) { + tel.depositRedemptionRequestedEventStorage.storeEvent(deposit, event) +} + +type eventsStorage struct { + storage map[string][]interface{} // + storageMutex sync.Mutex +} + +func newEventsStorage() *eventsStorage { + return &eventsStorage{ + storage: make(map[string][]interface{}), + } +} + +func (ms *eventsStorage) storeEvent(deposit string, event interface{}) { + ms.storageMutex.Lock() + defer ms.storageMutex.Unlock() + + ms.storage[deposit] = append(ms.storage[deposit], event) +} + +func (ms *eventsStorage) getEvents(deposit string) []interface{} { + ms.storageMutex.Lock() + defer ms.storageMutex.Unlock() + + events := ms.storage[deposit] + + return events +} diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index 7bb201fef..ffe4daf5e 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math" + "math/big" "math/rand" "time" @@ -19,15 +20,15 @@ var logger = log.Logger("tbtc-extension") const maxActionAttempts = 3 // Initialize initializes extension specific to the TBTC application. -func Initialize(ctx context.Context, handle Handle) error { +func Initialize(ctx context.Context, chain Handle) error { logger.Infof("initializing tbtc extension") - tbtc := &tbtc{handle} + tbtc := newTBTC(chain) err := tbtc.monitorRetrievePubKey( ctx, exponentialBackoff, - 150*time.Minute, + 150*time.Minute, // 30 minutes before the 3 hours on-chain timeout ) if err != nil { return fmt.Errorf( @@ -36,6 +37,19 @@ func Initialize(ctx context.Context, handle Handle) error { ) } + err = tbtc.monitorProvideRedemptionSignature( + ctx, + exponentialBackoff, + 105*time.Minute, // 15 minutes before the 2 hours on-chain timeout + ) + if err != nil { + return fmt.Errorf( + "could not initialize provide redemption "+ + "signature monitoring: [%v]", + err, + ) + } + logger.Infof("tbtc extension has been initialized") return nil @@ -58,7 +72,15 @@ type submitDepositTxFn func(deposit string) error type backoffFn func(iteration int) time.Duration type tbtc struct { - chain Handle + chain Handle + eventsLog *tbtcEventsLog +} + +func newTBTC(chain Handle) *tbtc { + return &tbtc{ + chain: chain, + eventsLog: newTBTCEventsLog(), + } } func (t *tbtc) monitorRetrievePubKey( @@ -97,6 +119,72 @@ func (t *tbtc) monitorRetrievePubKey( return nil } +func (t *tbtc) monitorProvideRedemptionSignature( + ctx context.Context, + actBackoffFn backoffFn, + timeout time.Duration, +) error { + monitoringStartFn := func( + handler depositEventHandler, + ) (subscription.EventSubscription, error) { + return t.chain.OnDepositRedemptionRequested( + func( + depositAddress string, + requesterAddress string, + digest [32]uint8, + utxoValue *big.Int, + redeemerOutputScript []uint8, + requestedFee *big.Int, + outpoint []uint8, + blockNumber uint64, + ) { + t.eventsLog.logDepositRedemptionRequestedEvent( + depositAddress, + &DepositRedemptionRequestedEvent{ + DepositAddress: depositAddress, + RequesterAddress: requesterAddress, + Digest: digest, + UtxoValue: utxoValue, + RedeemerOutputScript: redeemerOutputScript, + RequestedFee: requestedFee, + Outpoint: outpoint, + BlockNumber: blockNumber, + }, + ) + handler(depositAddress) + }, + ) + } + + startEventSubscription, err := t.monitorAndAct( + ctx, + "provide redemption signature", + monitoringStartFn, + func(handler depositEventHandler) (subscription.EventSubscription, error) { + panic("not implemented") // TODO: Implementation + }, + t.watchKeepClosed, + func(deposit string) error { + panic("not implemented") // TODO: Implementation + }, + actBackoffFn, + timeout, + ) + if err != nil { + return err + } + + go func() { + <-ctx.Done() + startEventSubscription.Unsubscribe() + logger.Infof("provide redemption signature monitoring disabled") + }() + + logger.Infof("provide redemption signature monitoring initialized") + + return nil +} + // TODO: // 1. Filter incoming events by operator interest. // 2. Incoming events deduplication. diff --git a/pkg/extensions/tbtc/tbtc_test.go b/pkg/extensions/tbtc/tbtc_test.go index 729f922d5..dd4099549 100644 --- a/pkg/extensions/tbtc/tbtc_test.go +++ b/pkg/extensions/tbtc/tbtc_test.go @@ -21,7 +21,7 @@ const ( func TestRetrievePubkey_TimeoutElapsed(t *testing.T) { ctx := context.Background() tbtcChain := local.NewTBTCLocalChain() - tbtc := &tbtc{tbtcChain} + tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( ctx, @@ -74,7 +74,7 @@ func TestRetrievePubkey_TimeoutElapsed(t *testing.T) { func TestRetrievePubkey_StopEventOccurred(t *testing.T) { ctx := context.Background() tbtcChain := local.NewTBTCLocalChain() - tbtc := &tbtc{tbtcChain} + tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( ctx, @@ -136,7 +136,7 @@ func TestRetrievePubkey_StopEventOccurred(t *testing.T) { func TestRetrievePubkey_KeepClosedEventOccurred(t *testing.T) { ctx := context.Background() tbtcChain := local.NewTBTCLocalChain() - tbtc := &tbtc{tbtcChain} + tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( ctx, @@ -196,7 +196,7 @@ func TestRetrievePubkey_KeepClosedEventOccurred(t *testing.T) { func TestRetrievePubkey_KeepTerminatedEventOccurred(t *testing.T) { ctx := context.Background() tbtcChain := local.NewTBTCLocalChain() - tbtc := &tbtc{tbtcChain} + tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( ctx, @@ -256,7 +256,7 @@ func TestRetrievePubkey_KeepTerminatedEventOccurred(t *testing.T) { func TestRetrievePubkey_ActionFailed(t *testing.T) { ctx := context.Background() tbtcChain := local.NewTBTCLocalChain() - tbtc := &tbtc{tbtcChain} + tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( ctx, @@ -292,7 +292,7 @@ func TestRetrievePubkey_ActionFailed(t *testing.T) { func TestRetrievePubkey_ContextCancelled_WithoutWorkingMonitoring(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) tbtcChain := local.NewTBTCLocalChain() - tbtc := &tbtc{tbtcChain} + tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( ctx, @@ -328,7 +328,7 @@ func TestRetrievePubkey_ContextCancelled_WithoutWorkingMonitoring(t *testing.T) func TestRetrievePubkey_ContextCancelled_WithWorkingMonitoring(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) tbtcChain := local.NewTBTCLocalChain() - tbtc := &tbtc{tbtcChain} + tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( ctx, From 2fa23da00e1752042c542330a6e79123aa1a284d Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Thu, 22 Oct 2020 18:01:09 +0200 Subject: [PATCH 02/31] Provide redemption signature action Implemented the main part of the redemption signature action. --- pkg/chain/chain.go | 7 ++ pkg/chain/ethereum/ethereum.go | 48 ++++++++ pkg/chain/ethereum/tbtc.go | 112 ++++++++++++++++- pkg/chain/event.go | 8 ++ pkg/chain/local/local.go | 7 ++ pkg/chain/local/tbtc.go | 29 +++++ pkg/extensions/tbtc/chain.go | 42 +++++-- pkg/extensions/tbtc/events_log.go | 2 +- pkg/extensions/tbtc/tbtc.go | 193 +++++++++++++++++++++++------- 9 files changed, 388 insertions(+), 60 deletions(-) diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go index e80dbac26..3c8f46817 100644 --- a/pkg/chain/chain.go +++ b/pkg/chain/chain.go @@ -143,4 +143,11 @@ type BondedECDSAKeep interface { // GetOpenedTimestamp returns timestamp when the keep was created. GetOpenedTimestamp(keepAddress common.Address) (time.Time, error) + + // PastSignatureSubmittedEvents returns all signature submitted events + // for the given keep which occurred after the provided start block. + PastSignatureSubmittedEvents( + keepAddress string, + startBlock uint64, + ) ([]*SignatureSubmittedEvent, error) } diff --git a/pkg/chain/ethereum/ethereum.go b/pkg/chain/ethereum/ethereum.go index 92cc1311e..7a5dbc6ec 100644 --- a/pkg/chain/ethereum/ethereum.go +++ b/pkg/chain/ethereum/ethereum.go @@ -6,6 +6,9 @@ import ( "math/big" "time" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/keep-network/keep-ecdsa/pkg/chain/gen/abi" + "github.com/ethereum/go-ethereum/common" "github.com/ipfs/go-log" @@ -507,3 +510,48 @@ func (ec *EthereumChain) GetOpenedTimestamp(keepAddress common.Address) (time.Ti return keepOpenTime, nil } + +// PastSignatureSubmittedEvents returns all signature submitted events +// for the given keep which occurred after the provided start block. +func (ec *EthereumChain) PastSignatureSubmittedEvents( + keepAddress string, + startBlock uint64, +) ([]*eth.SignatureSubmittedEvent, error) { + if !common.IsHexAddress(keepAddress) { + return nil, fmt.Errorf("invalid keep address: [%v]", keepAddress) + } + + keepContractAbi, err := abi.NewBondedECDSAKeep( + common.HexToAddress(keepAddress), + ec.client, + ) + if err != nil { + return nil, err + } + + iterator, err := keepContractAbi.FilterSignatureSubmitted( + &bind.FilterOpts{Start: startBlock}, + nil, + ) + if err != nil { + return nil, err + } + + result := make([]*eth.SignatureSubmittedEvent, 0) + + hasNext := true + for hasNext { + hasNext = iterator.Next() + if hasNext { + event := iterator.Event + result = append(result, ð.SignatureSubmittedEvent{ + Digest: event.Digest, + R: event.R, + S: event.S, + RecoveryID: event.RecoveryID, + }) + } + } + + return result, nil +} diff --git a/pkg/chain/ethereum/tbtc.go b/pkg/chain/ethereum/tbtc.go index 297d9b9b6..f25886889 100644 --- a/pkg/chain/ethereum/tbtc.go +++ b/pkg/chain/ethereum/tbtc.go @@ -83,14 +83,17 @@ func (tec *TBTCEthereumChain) OnDepositRegisteredPubkey( handler(DepositContractAddress.Hex()) }, func(err error) error { - return fmt.Errorf("watch deposit created failed: [%v]", err) + return fmt.Errorf( + "watch deposit registered pubkey failed: [%v]", + err, + ) }, nil, ) } // OnDepositRedemptionRequested installs a callback that is invoked when an -// on-chain notification of a new deposit redemption request is seen. +// on-chain notification of a deposit redemption request is seen. func (tec *TBTCEthereumChain) OnDepositRedemptionRequested( handler func( depositAddress string, @@ -137,6 +140,58 @@ func (tec *TBTCEthereumChain) OnDepositRedemptionRequested( ) } +// OnDepositGotRedemptionSignature installs a callback that is invoked when an +// on-chain notification of a deposit receiving a redemption signature is seen. +func (tec *TBTCEthereumChain) OnDepositGotRedemptionSignature( + handler func(depositAddress string), +) (subscription.EventSubscription, error) { + return tec.tbtcSystemContract.WatchGotRedemptionSignature( + func( + DepositContractAddress common.Address, + Digest [32]uint8, + R [32]uint8, + S [32]uint8, + Timestamp *big.Int, + blockNumber uint64, + ) { + handler(DepositContractAddress.Hex()) + }, + func(err error) error { + return fmt.Errorf( + "watch deposit got redemption signature failed: [%v]", + err, + ) + }, + nil, + nil, + ) +} + +// OnDepositRedeemed installs a callback that is invoked when an +// on-chain notification of a deposit redemption is seen. +func (tec *TBTCEthereumChain) OnDepositRedeemed( + handler func(depositAddress string), +) (subscription.EventSubscription, error) { + return tec.tbtcSystemContract.WatchRedeemed( + func( + DepositContractAddress common.Address, + Txid [32]uint8, + Timestamp *big.Int, + blockNumber uint64, + ) { + handler(DepositContractAddress.Hex()) + }, + func(err error) error { + return fmt.Errorf( + "watch deposit redeemed failed: [%v]", + err, + ) + }, + nil, + nil, + ) +} + // KeepAddress returns the underlying keep address for the // provided deposit. func (tec *TBTCEthereumChain) KeepAddress( @@ -178,6 +233,59 @@ func (tec *TBTCEthereumChain) RetrieveSignerPubkey( return nil } +// ProvideRedemptionSignature provides the redemption signature for the +// provided deposit. +func (tec *TBTCEthereumChain) ProvideRedemptionSignature( + depositAddress string, + v uint8, + r [32]uint8, + s [32]uint8, +) error { + deposit, err := tec.getDepositContract(depositAddress) + if err != nil { + return err + } + + transaction, err := deposit.ProvideRedemptionSignature(v, r, s) + if err != nil { + return err + } + + logger.Debugf( + "submitted ProvideRedemptionSignature transaction with hash: [%x]", + transaction.Hash(), + ) + + return nil +} + +// IncreaseRedemptionFee increases the redemption fee for the provided deposit. +func (tec *TBTCEthereumChain) IncreaseRedemptionFee( + depositAddress string, + previousOutputValueBytes [8]uint8, + newOutputValueBytes [8]uint8, +) error { + deposit, err := tec.getDepositContract(depositAddress) + if err != nil { + return err + } + + transaction, err := deposit.IncreaseRedemptionFee( + previousOutputValueBytes, + newOutputValueBytes, + ) + if err != nil { + return err + } + + logger.Debugf( + "submitted IncreaseRedemptionFee transaction with hash: [%x]", + transaction.Hash(), + ) + + return nil +} + func (tec *TBTCEthereumChain) getDepositContract( address string, ) (*contract.Deposit, error) { diff --git a/pkg/chain/event.go b/pkg/chain/event.go index adf7e3f93..b6772c904 100644 --- a/pkg/chain/event.go +++ b/pkg/chain/event.go @@ -42,6 +42,14 @@ type KeepTerminatedEvent struct { BlockNumber uint64 } +// SignatureSubmittedEvent is an event emitted when a keep submits a signature. +type SignatureSubmittedEvent struct { + Digest [32]byte + R [32]byte + S [32]byte + RecoveryID uint8 +} + // IsMember checks if list of members contains the given address. func (e *BondedECDSAKeepCreatedEvent) IsMember(address common.Address) bool { for _, member := range e.Members { diff --git a/pkg/chain/local/local.go b/pkg/chain/local/local.go index fbd1aa563..ac64c054e 100644 --- a/pkg/chain/local/local.go +++ b/pkg/chain/local/local.go @@ -351,6 +351,13 @@ func (lc *localChain) GetOpenedTimestamp(keepAddress common.Address) (time.Time, panic("implement") } +func (lc *localChain) PastSignatureSubmittedEvents( + keepAddress string, + startBlock uint64, +) ([]*eth.SignatureSubmittedEvent, error) { + panic("implement") +} + func generateHandlerID() int { // #nosec G404 (insecure random number source (rand)) // Local chain implementation doesn't require secure randomness. diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index dfe0b00b3..b84eb1b26 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -122,6 +122,18 @@ func (tlc *TBTCLocalChain) OnDepositRedemptionRequested( panic("not implemented") // TODO: Implementation for unit testing purposes. } +func (tlc *TBTCLocalChain) OnDepositGotRedemptionSignature( + handler func(depositAddress string), +) (subscription.EventSubscription, error) { + panic("not implemented") // TODO: Implementation for unit testing purposes. +} + +func (tlc *TBTCLocalChain) OnDepositRedeemed( + handler func(depositAddress string), +) (subscription.EventSubscription, error) { + panic("not implemented") // TODO: Implementation for unit testing purposes. +} + func (tlc *TBTCLocalChain) KeepAddress(depositAddress string) (string, error) { tlc.mutex.Lock() defer tlc.mutex.Unlock() @@ -183,6 +195,23 @@ func (tlc *TBTCLocalChain) RetrieveSignerPubkey(depositAddress string) error { return nil } +func (tlc *TBTCLocalChain) ProvideRedemptionSignature( + depositAddress string, + v uint8, + r [32]uint8, + s [32]uint8, +) error { + panic("not implemented") // TODO: Implementation for unit testing purposes. +} + +func (tlc *TBTCLocalChain) IncreaseRedemptionFee( + depositAddress string, + previousOutputValueBytes [8]uint8, + newOutputValueBytes [8]uint8, +) error { + panic("not implemented") // TODO: Implementation for unit testing purposes. +} + func (tlc *TBTCLocalChain) DepositPubkey( depositAddress string, ) ([]byte, error) { diff --git a/pkg/extensions/tbtc/chain.go b/pkg/extensions/tbtc/chain.go index 078370f76..651cd1521 100644 --- a/pkg/extensions/tbtc/chain.go +++ b/pkg/extensions/tbtc/chain.go @@ -25,6 +25,23 @@ type Deposit interface { // RetrieveSignerPubkey retrieves the signer public key for the // provided deposit. RetrieveSignerPubkey(depositAddress string) error + + // ProvideRedemptionSignature provides the redemption signature for the + // provided deposit. + ProvideRedemptionSignature( + depositAddress string, + v uint8, + r [32]uint8, + s [32]uint8, + ) error + + // IncreaseRedemptionFee increases the redemption fee for the + // provided deposit. + IncreaseRedemptionFee( + depositAddress string, + previousOutputValueBytes [8]uint8, + newOutputValueBytes [8]uint8, + ) error } // TBTCSystem is an interface that provides ability to interact @@ -43,7 +60,7 @@ type TBTCSystem interface { ) (subscription.EventSubscription, error) // OnDepositRedemptionRequested installs a callback that is invoked when an - // on-chain notification of a new deposit redemption request is seen. + // on-chain notification of a deposit redemption request is seen. OnDepositRedemptionRequested( handler func( depositAddress string, @@ -56,16 +73,17 @@ type TBTCSystem interface { blockNumber uint64, ), ) (subscription.EventSubscription, error) -} -// DepositRedemptionRequestedEvent represents a deposit redemption requested event. -type DepositRedemptionRequestedEvent struct { - DepositAddress string - RequesterAddress string - Digest [32]uint8 - UtxoValue *big.Int - RedeemerOutputScript []uint8 - RequestedFee *big.Int - Outpoint []uint8 - BlockNumber uint64 + // OnDepositGotRedemptionSignature installs a callback that is invoked + // when an on-chain notification of a deposit receiving a redemption + // signature is seen. + OnDepositGotRedemptionSignature( + handler func(depositAddress string), + ) (subscription.EventSubscription, error) + + // OnDepositRedeemed installs a callback that is invoked when an + // on-chain notification of a deposit redemption is seen. + OnDepositRedeemed( + handler func(depositAddress string), + ) (subscription.EventSubscription, error) } diff --git a/pkg/extensions/tbtc/events_log.go b/pkg/extensions/tbtc/events_log.go index 62f5ccb95..9de547826 100644 --- a/pkg/extensions/tbtc/events_log.go +++ b/pkg/extensions/tbtc/events_log.go @@ -16,7 +16,7 @@ func newTBTCEventsLog() *tbtcEventsLog { func (tel *tbtcEventsLog) logDepositRedemptionRequestedEvent( deposit string, - event *DepositRedemptionRequestedEvent, + event *depositRedemptionRequestedEvent, ) { tel.depositRedemptionRequestedEventStorage.storeEvent(deposit, event) } diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index ffe4daf5e..ae8d38a60 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -1,6 +1,7 @@ package tbtc import ( + "bytes" "context" "fmt" "math" @@ -55,19 +56,42 @@ func Initialize(ctx context.Context, chain Handle) error { return nil } -type depositEventHandler func(deposit string) +type depositEvent interface { + getDepositAddress() string +} + +type baseDepositEvent struct { + depositAddress string +} + +func (bdd *baseDepositEvent) getDepositAddress() string { + return bdd.depositAddress +} + +type depositRedemptionRequestedEvent struct { + *baseDepositEvent + + digest [32]uint8 + blockNumber uint64 +} + +type depositEventHandler func(depositEvent) type watchDepositEventFn func( handler depositEventHandler, ) (subscription.EventSubscription, error) -type watchKeepClosedFn func(deposit string) ( +type watchKeepClosedFn func(depositAddress string) ( keepClosedChan chan struct{}, unsubscribe func(), err error, ) -type submitDepositTxFn func(deposit string) error +type depositMonitoringContext struct { + startEvent depositEvent +} + +type submitDepositTxFn func(*depositMonitoringContext) error type backoffFn func(iteration int) time.Duration @@ -92,14 +116,20 @@ func (t *tbtc) monitorRetrievePubKey( ctx, "retrieve pubkey", func(handler depositEventHandler) (subscription.EventSubscription, error) { - return t.chain.OnDepositCreated(handler) + return t.chain.OnDepositCreated(func(depositAddress string) { + handler(&baseDepositEvent{depositAddress}) + }) }, func(handler depositEventHandler) (subscription.EventSubscription, error) { - return t.chain.OnDepositRegisteredPubkey(handler) + return t.chain.OnDepositRegisteredPubkey(func(depositAddress string) { + handler(&baseDepositEvent{depositAddress}) + }) }, t.watchKeepClosed, - func(deposit string) error { - return t.chain.RetrieveSignerPubkey(deposit) + func(monitoringContext *depositMonitoringContext) error { + return t.chain.RetrieveSignerPubkey( + monitoringContext.startEvent.getDepositAddress(), + ) }, actBackoffFn, timeout, @@ -138,35 +168,106 @@ func (t *tbtc) monitorProvideRedemptionSignature( outpoint []uint8, blockNumber uint64, ) { + event := &depositRedemptionRequestedEvent{ + &baseDepositEvent{depositAddress}, + digest, + blockNumber, + } + + // Log the event to make it available for future provide + // redemption proof monitoring process. t.eventsLog.logDepositRedemptionRequestedEvent( depositAddress, - &DepositRedemptionRequestedEvent{ - DepositAddress: depositAddress, - RequesterAddress: requesterAddress, - Digest: digest, - UtxoValue: utxoValue, - RedeemerOutputScript: redeemerOutputScript, - RequestedFee: requestedFee, - Outpoint: outpoint, - BlockNumber: blockNumber, - }, + event, ) - handler(depositAddress) + + handler(event) }, ) } - startEventSubscription, err := t.monitorAndAct( + monitoringStopFn := func( + handler depositEventHandler, + ) (subscription.EventSubscription, error) { + signatureSubscription, err := t.chain.OnDepositGotRedemptionSignature( + func(depositAddress string) { + handler(&baseDepositEvent{depositAddress}) + }, + ) + if err != nil { + return nil, err + } + + redeemedSubscription, err := t.chain.OnDepositRedeemed( + func(depositAddress string) { + handler(&baseDepositEvent{depositAddress}) + }, + ) + if err != nil { + return nil, err + } + + return subscription.NewEventSubscription( + func() { + signatureSubscription.Unsubscribe() + redeemedSubscription.Unsubscribe() + }, + ), nil + } + + actFn := func(monitoringContext *depositMonitoringContext) error { + depositRedemptionRequestedEvent, ok := monitoringContext.startEvent.(*depositRedemptionRequestedEvent) + if !ok { + return fmt.Errorf( + "monitoring context contains unexpected type of start event", + ) + } + + depositAddress := depositRedemptionRequestedEvent.depositAddress + + keepAddress, err := t.chain.KeepAddress(depositAddress) + if err != nil { + return err + } + + signatureSubmittedEvents, err := t.chain.PastSignatureSubmittedEvents( + keepAddress, + depositRedemptionRequestedEvent.blockNumber, + ) + if err != nil { + return err + } + + depositDigest := depositRedemptionRequestedEvent.digest + for _, signatureSubmittedEvent := range signatureSubmittedEvents { + if bytes.Equal(signatureSubmittedEvent.Digest[:], depositDigest[:]) { + // We add 27 to the recovery ID to align it with ethereum and + // bitcoin protocols where 27 is added to recovery ID to + // indicate usage of uncompressed public keys. + v := 27 + signatureSubmittedEvent.RecoveryID + + return t.chain.ProvideRedemptionSignature( + depositAddress, + v, + signatureSubmittedEvent.R, + signatureSubmittedEvent.S, + ) + } + } + + return fmt.Errorf( + "could not find signature for digest: [%v]", + depositDigest, + ) + } + + monitoringSubscription, err := t.monitorAndAct( ctx, "provide redemption signature", monitoringStartFn, - func(handler depositEventHandler) (subscription.EventSubscription, error) { - panic("not implemented") // TODO: Implementation - }, + monitoringStopFn, t.watchKeepClosed, - func(deposit string) error { - panic("not implemented") // TODO: Implementation - }, + actFn, actBackoffFn, timeout, ) @@ -176,7 +277,7 @@ func (t *tbtc) monitorProvideRedemptionSignature( go func() { <-ctx.Done() - startEventSubscription.Unsubscribe() + monitoringSubscription.Unsubscribe() logger.Infof("provide redemption signature monitoring disabled") }() @@ -199,18 +300,18 @@ func (t *tbtc) monitorAndAct( actBackoffFn backoffFn, timeout time.Duration, ) (subscription.EventSubscription, error) { - handleStartEvent := func(deposit string) { + handleStartEvent := func(startEvent depositEvent) { logger.Infof( "starting [%v] monitoring for deposit [%v]", monitoringName, - deposit, + startEvent.getDepositAddress(), ) stopEventChan := make(chan struct{}) stopEventSubscription, err := monitoringStopFn( - func(stopEventDeposit string) { - if deposit == stopEventDeposit { + func(stopEvent depositEvent) { + if startEvent.getDepositAddress() == stopEvent.getDepositAddress() { stopEventChan <- struct{}{} } }, @@ -220,20 +321,22 @@ func (t *tbtc) monitorAndAct( "could not setup stop event handler for [%v] "+ "monitoring for deposit [%v]: [%v]", monitoringName, - deposit, + startEvent.getDepositAddress(), err, ) return } defer stopEventSubscription.Unsubscribe() - keepClosedChan, keepClosedUnsubscribe, err := keepClosedFn(deposit) + keepClosedChan, keepClosedUnsubscribe, err := keepClosedFn( + startEvent.getDepositAddress(), + ) if err != nil { logger.Errorf( "could not setup keep closed handler for [%v] "+ "monitoring for deposit [%v]: [%v]", monitoringName, - deposit, + startEvent.getDepositAddress(), err, ) return @@ -252,7 +355,7 @@ func (t *tbtc) monitorAndAct( "context is done for [%v] "+ "monitoring for deposit [%v]", monitoringName, - deposit, + startEvent.getDepositAddress(), ) break monitoring case <-stopEventChan: @@ -260,7 +363,7 @@ func (t *tbtc) monitorAndAct( "stop event occurred for [%v] "+ "monitoring for deposit [%v]", monitoringName, - deposit, + startEvent.getDepositAddress(), ) break monitoring case <-keepClosedChan: @@ -268,11 +371,11 @@ func (t *tbtc) monitorAndAct( "keep closed event occurred for [%v] "+ "monitoring for deposit [%v]", monitoringName, - deposit, + startEvent.getDepositAddress(), ) break monitoring case <-timeoutChan: - err := actFn(deposit) + err := actFn(&depositMonitoringContext{startEvent}) if err != nil { if actionAttempt == maxActionAttempts { logger.Errorf( @@ -280,7 +383,7 @@ func (t *tbtc) monitorAndAct( "for [%v] monitoring for deposit [%v]: [%v]; "+ "last attempt failed", monitoringName, - deposit, + startEvent.getDepositAddress(), err, ) break monitoring @@ -293,7 +396,7 @@ func (t *tbtc) monitorAndAct( "for [%v] monitoring for deposit [%v]: [%v]; "+ "retrying after: [%v]", monitoringName, - deposit, + startEvent.getDepositAddress(), err, backoff, ) @@ -305,7 +408,7 @@ func (t *tbtc) monitorAndAct( "action for [%v] monitoring for "+ "deposit [%v] has been performed successfully", monitoringName, - deposit, + startEvent.getDepositAddress(), ) break monitoring } @@ -316,23 +419,23 @@ func (t *tbtc) monitorAndAct( "[%v] monitoring for deposit [%v] "+ "has been completed", monitoringName, - deposit, + startEvent.getDepositAddress(), ) } return monitoringStartFn( - func(deposit string) { - go handleStartEvent(deposit) + func(startEvent depositEvent) { + go handleStartEvent(startEvent) }, ) } func (t *tbtc) watchKeepClosed( - deposit string, + depositAddress string, ) (chan struct{}, func(), error) { signalChan := make(chan struct{}) - keepAddress, err := t.chain.KeepAddress(deposit) + keepAddress, err := t.chain.KeepAddress(depositAddress) if err != nil { return nil, nil, err } From 43292b43f94f4858d00df6a10c91ab931a66ad8b Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Fri, 23 Oct 2020 11:33:00 +0200 Subject: [PATCH 03/31] Simplify the iterator of past signature events --- pkg/chain/ethereum/ethereum.go | 22 +++++++++++----------- pkg/extensions/tbtc/tbtc.go | 1 + 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/pkg/chain/ethereum/ethereum.go b/pkg/chain/ethereum/ethereum.go index 7a5dbc6ec..468d190c6 100644 --- a/pkg/chain/ethereum/ethereum.go +++ b/pkg/chain/ethereum/ethereum.go @@ -539,18 +539,18 @@ func (ec *EthereumChain) PastSignatureSubmittedEvents( result := make([]*eth.SignatureSubmittedEvent, 0) - hasNext := true - for hasNext { - hasNext = iterator.Next() - if hasNext { - event := iterator.Event - result = append(result, ð.SignatureSubmittedEvent{ - Digest: event.Digest, - R: event.R, - S: event.S, - RecoveryID: event.RecoveryID, - }) + for { + if !iterator.Next() { + break } + + event := iterator.Event + result = append(result, ð.SignatureSubmittedEvent{ + Digest: event.Digest, + R: event.R, + S: event.S, + RecoveryID: event.RecoveryID, + }) } return result, nil diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index 7a937dabc..c7cffb436 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -239,6 +239,7 @@ func (t *tbtc) monitorProvideRedemptionSignature( } depositDigest := depositRedemptionRequestedEvent.digest + for _, signatureSubmittedEvent := range signatureSubmittedEvents { if bytes.Equal(signatureSubmittedEvent.Digest[:], depositDigest[:]) { // We add 27 to the recovery ID to align it with ethereum and From b13b76f7bfe9e6fdc507ffb1b5246a2936f91cd3 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Fri, 23 Oct 2020 12:05:53 +0200 Subject: [PATCH 04/31] Remove the direct dependency on `gen/abi` --- pkg/chain/ethereum/ethereum.go | 17 ++--- .../gen/filterer/BondedECDSAKeepFilterer.go | 69 +++++++++++++++++++ pkg/extensions/tbtc/tbtc.go | 4 +- 3 files changed, 75 insertions(+), 15 deletions(-) create mode 100644 pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go diff --git a/pkg/chain/ethereum/ethereum.go b/pkg/chain/ethereum/ethereum.go index 468d190c6..309b3f5fa 100644 --- a/pkg/chain/ethereum/ethereum.go +++ b/pkg/chain/ethereum/ethereum.go @@ -6,8 +6,7 @@ import ( "math/big" "time" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/keep-network/keep-ecdsa/pkg/chain/gen/abi" + "github.com/keep-network/keep-ecdsa/pkg/chain/gen/filterer" "github.com/ethereum/go-ethereum/common" @@ -521,7 +520,7 @@ func (ec *EthereumChain) PastSignatureSubmittedEvents( return nil, fmt.Errorf("invalid keep address: [%v]", keepAddress) } - keepContractAbi, err := abi.NewBondedECDSAKeep( + keepContractFilterer, err := filterer.NewBondedECDSAKeepFilterer( common.HexToAddress(keepAddress), ec.client, ) @@ -529,22 +528,14 @@ func (ec *EthereumChain) PastSignatureSubmittedEvents( return nil, err } - iterator, err := keepContractAbi.FilterSignatureSubmitted( - &bind.FilterOpts{Start: startBlock}, - nil, - ) + events, err := keepContractFilterer.FilterSignatureSubmitted(startBlock, nil) if err != nil { return nil, err } result := make([]*eth.SignatureSubmittedEvent, 0) - for { - if !iterator.Next() { - break - } - - event := iterator.Event + for _, event := range events { result = append(result, ð.SignatureSubmittedEvent{ Digest: event.Digest, R: event.R, diff --git a/pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go b/pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go new file mode 100644 index 000000000..f37d6a391 --- /dev/null +++ b/pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go @@ -0,0 +1,69 @@ +package filterer + +import ( + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/keep-network/keep-ecdsa/pkg/chain/gen/abi" +) + +// FIXME: This is a temporary structure allowing to access past events +// occurred emitted by the `BondedECDSAKeep` contract. This structure is +// here because the generated contract wrappers from `gen/contract` +// don't support `Filter*` methods yet. When the contract generator +// will support those methods, the below structure can be removed. +type BondedECDSAKeepFilterer struct { + contract *abi.BondedECDSAKeep +} + +func NewBondedECDSAKeepFilterer( + contractAddress common.Address, + backend bind.ContractBackend, +) (*BondedECDSAKeepFilterer, error) { + contract, err := abi.NewBondedECDSAKeep(contractAddress, backend) + if err != nil { + return nil, err + } + + return &BondedECDSAKeepFilterer{contract}, nil +} + +type BondedECDSAKeepSignatureSubmitted struct { + Digest [32]byte + R [32]byte + S [32]byte + RecoveryID uint8 +} + +func (bekf *BondedECDSAKeepFilterer) FilterSignatureSubmitted( + startBlock uint64, + endBlock *uint64, +) ([]*BondedECDSAKeepSignatureSubmitted, error) { + iterator, err := bekf.contract.FilterSignatureSubmitted( + &bind.FilterOpts{ + Start: startBlock, + End: endBlock, + }, + nil, + ) + if err != nil { + return nil, err + } + + events := make([]*BondedECDSAKeepSignatureSubmitted, 0) + + for { + if !iterator.Next() { + break + } + + event := iterator.Event + events = append(events, &BondedECDSAKeepSignatureSubmitted{ + Digest: event.Digest, + R: event.R, + S: event.S, + RecoveryID: event.RecoveryID, + }) + } + + return events, nil +} diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index c7cffb436..9f11300c3 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -64,8 +64,8 @@ type baseDepositEvent struct { depositAddress string } -func (bdd *baseDepositEvent) getDepositAddress() string { - return bdd.depositAddress +func (bde *baseDepositEvent) getDepositAddress() string { + return bde.depositAddress } type depositRedemptionRequestedEvent struct { From c12019625d4b4c6c0a7ebbb4f687125cd3819b3a Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Fri, 23 Oct 2020 12:21:38 +0200 Subject: [PATCH 05/31] Add line break on start event casting --- pkg/extensions/tbtc/tbtc.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index 9f11300c3..fe06480b6 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -216,7 +216,8 @@ func (t *tbtc) monitorProvideRedemptionSignature( } actFn := func(monitoringContext *depositMonitoringContext) error { - depositRedemptionRequestedEvent, ok := monitoringContext.startEvent.(*depositRedemptionRequestedEvent) + depositRedemptionRequestedEvent, ok := monitoringContext. + startEvent.(*depositRedemptionRequestedEvent) if !ok { return fmt.Errorf( "monitoring context contains unexpected type of start event", From b5621ba9ba28f9c3b97143e250f002544dd9473e Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Fri, 23 Oct 2020 15:44:00 +0200 Subject: [PATCH 06/31] Implementation of the redemption proof action Implemented almost all elements of the provide redemption proof action. Also, some code refactoring has been done by the way. --- go.mod | 2 +- go.sum | 4 +- pkg/chain/chain.go | 2 + pkg/chain/ethereum/connect.go | 19 ++ pkg/chain/ethereum/ethereum.go | 5 + pkg/chain/ethereum/tbtc.go | 81 +++-- pkg/chain/event.go | 18 ++ .../gen/filterer/BondedECDSAKeepFilterer.go | 2 +- pkg/chain/local/local.go | 6 +- pkg/chain/local/tbtc.go | 21 +- pkg/extensions/tbtc/chain.go | 22 +- pkg/extensions/tbtc/events_log.go | 49 --- pkg/extensions/tbtc/tbtc.go | 281 +++++++++++------- 13 files changed, 308 insertions(+), 204 deletions(-) delete mode 100644 pkg/extensions/tbtc/events_log.go diff --git a/go.mod b/go.mod index 46c3db432..a0a3220cf 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/ipfs/go-log v1.0.4 github.com/keep-network/keep-common v1.2.1-0.20201020114759-19c123cbd4f4 github.com/keep-network/keep-core v1.3.0 - github.com/keep-network/tbtc v1.1.1-0.20201020115551-5f9077c74826 + github.com/keep-network/tbtc v1.1.1-0.20201023121153-c4c21e8404c5 github.com/pkg/errors v0.9.1 github.com/urfave/cli v1.22.1 ) diff --git a/go.sum b/go.sum index e89cf4f9e..dcc162532 100644 --- a/go.sum +++ b/go.sum @@ -337,8 +337,8 @@ github.com/keep-network/keep-common v1.2.1-0.20201020114759-19c123cbd4f4 h1:Civu github.com/keep-network/keep-common v1.2.1-0.20201020114759-19c123cbd4f4/go.mod h1:emxogTbBdey7M3jOzfxZOdfn139kN2mI2b2wA6AHKKo= github.com/keep-network/keep-core v1.3.0 h1:7Tb33EmO/ntHOEbOiYciRlBhqu5Ln6KemWCaYK0Z6LA= github.com/keep-network/keep-core v1.3.0/go.mod h1:1KsSSTQoN754TrFLW7kLy50pOG2CQ4BOfnJqdvEG7FA= -github.com/keep-network/tbtc v1.1.1-0.20201020115551-5f9077c74826 h1:ijlpSs+mEtur4F1DQA8450Ubuhdk4lGjIoPZr3yf7vc= -github.com/keep-network/tbtc v1.1.1-0.20201020115551-5f9077c74826/go.mod h1:igBF2MPTFkzOdZ3gcwt8h0Zb5pZaHnij/iPZoMB9IKM= +github.com/keep-network/tbtc v1.1.1-0.20201023121153-c4c21e8404c5 h1:eETyOKkTyO51URqSoXsaMe0VYB1AiQGBnlBEXX/XHOY= +github.com/keep-network/tbtc v1.1.1-0.20201023121153-c4c21e8404c5/go.mod h1:igBF2MPTFkzOdZ3gcwt8h0Zb5pZaHnij/iPZoMB9IKM= github.com/keep-network/toml v0.3.0 h1:G+NJwWR/ZiORqeLBsDXDchYoL29PXHdxOPcCueA7ctE= github.com/keep-network/toml v0.3.0/go.mod h1:Zeyd3lxbIlMYLREho3UK1dMP2xjqt2gLkQ5E5vM6K38= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go index 3c8f46817..dde8ecb07 100644 --- a/pkg/chain/chain.go +++ b/pkg/chain/chain.go @@ -20,6 +20,8 @@ type Handle interface { StakeMonitor() (chain.StakeMonitor, error) // BlockCounter returns a block counter. BlockCounter() chain.BlockCounter + // BlockTimestamp returns given block's timestamp. + BlockTimestamp(blockNumber *big.Int) (uint64, error) BondedECDSAKeepFactory BondedECDSAKeep diff --git a/pkg/chain/ethereum/connect.go b/pkg/chain/ethereum/connect.go index 5a7e059f2..e8cb6d11d 100644 --- a/pkg/chain/ethereum/connect.go +++ b/pkg/chain/ethereum/connect.go @@ -1,6 +1,7 @@ package ethereum import ( + "context" "fmt" "math/big" "sync" @@ -41,6 +42,7 @@ type EthereumChain struct { blockCounter *blockcounter.EthereumBlockCounter miningWaiter *ethutil.MiningWaiter nonceManager *ethutil.NonceManager + blockTimestampFn blockTimestampFn // transactionMutex allows interested parties to forcibly serialize // transaction submission. @@ -120,6 +122,7 @@ func Connect(accountKey *keystore.Key, config *ethereum.Config) (*EthereumChain, nonceManager: nonceManager, miningWaiter: miningWaiter, transactionMutex: transactionMutex, + blockTimestampFn: createBlockTimestampFn(client), }, nil } @@ -149,3 +152,19 @@ func addClientWrappers( return loggingBackend } + +type blockTimestampFn func(blockNumber *big.Int) (uint64, error) + +func createBlockTimestampFn(client *ethclient.Client) blockTimestampFn { + return func(blockNumber *big.Int) (uint64, error) { + ctx, cancelCtx := context.WithTimeout(context.Background(), 1*time.Minute) + defer cancelCtx() + + header, err := client.HeaderByNumber(ctx, blockNumber) + if err != nil { + return 0, err + } + + return header.Time, nil + } +} diff --git a/pkg/chain/ethereum/ethereum.go b/pkg/chain/ethereum/ethereum.go index 309b3f5fa..40a954542 100644 --- a/pkg/chain/ethereum/ethereum.go +++ b/pkg/chain/ethereum/ethereum.go @@ -546,3 +546,8 @@ func (ec *EthereumChain) PastSignatureSubmittedEvents( return result, nil } + +// BlockTimestamp returns given block's timestamp. +func (ec *EthereumChain) BlockTimestamp(blockNumber *big.Int) (uint64, error) { + return ec.blockTimestampFn(blockNumber) +} diff --git a/pkg/chain/ethereum/tbtc.go b/pkg/chain/ethereum/tbtc.go index f25886889..34d7588c6 100644 --- a/pkg/chain/ethereum/tbtc.go +++ b/pkg/chain/ethereum/tbtc.go @@ -3,10 +3,14 @@ package ethereum import ( "fmt" "math/big" + "sort" + + eth "github.com/keep-network/keep-ecdsa/pkg/chain" "github.com/ethereum/go-ethereum/common" "github.com/keep-network/keep-common/pkg/subscription" "github.com/keep-network/tbtc/pkg/chain/ethereum/gen/contract" + "github.com/keep-network/tbtc/pkg/chain/ethereum/gen/filterer" ) // TBTCEthereumChain represents an Ethereum chain handle with @@ -15,6 +19,7 @@ type TBTCEthereumChain struct { *EthereumChain tbtcSystemContract *contract.TBTCSystem + tbtcSystemFilterer *filterer.TBTCSystemFilterer } // WithTBTCExtension extends the Ethereum chain handle with @@ -39,9 +44,18 @@ func WithTBTCExtension( return nil, err } + tbtcSystemFilterer, err := filterer.NewTBTCSystemFilterer( + common.HexToAddress(tbtcSystemContractAddress), + ethereumChain.client, + ) + if err != nil { + return nil, err + } + return &TBTCEthereumChain{ EthereumChain: ethereumChain, tbtcSystemContract: tbtcSystemContract, + tbtcSystemFilterer: tbtcSystemFilterer, }, nil } @@ -95,16 +109,7 @@ func (tec *TBTCEthereumChain) OnDepositRegisteredPubkey( // OnDepositRedemptionRequested installs a callback that is invoked when an // on-chain notification of a deposit redemption request is seen. func (tec *TBTCEthereumChain) OnDepositRedemptionRequested( - handler func( - depositAddress string, - requesterAddress string, - digest [32]uint8, - utxoValue *big.Int, - redeemerOutputScript []uint8, - requestedFee *big.Int, - outpoint []uint8, - blockNumber uint64, - ), + handler func(depositAddress string), ) (subscription.EventSubscription, error) { return tec.tbtcSystemContract.WatchRedemptionRequested( func( @@ -117,16 +122,7 @@ func (tec *TBTCEthereumChain) OnDepositRedemptionRequested( Outpoint []uint8, blockNumber uint64, ) { - handler( - DepositContractAddress.Hex(), - Requester.Hex(), - Digest, - UtxoValue, - RedeemerOutputScript, - RequestedFee, - Outpoint, - blockNumber, - ) + handler(DepositContractAddress.Hex()) }, func(err error) error { return fmt.Errorf( @@ -192,6 +188,51 @@ func (tec *TBTCEthereumChain) OnDepositRedeemed( ) } +// PastDepositRedemptionRequestedEvents returns all redemption requested +// events for the given deposit which occurred after the provided start block. +// Returned events are sorted by the block number in the ascending order. +func (tec *TBTCEthereumChain) PastDepositRedemptionRequestedEvents( + depositAddress string, + startBlock uint64, +) ([]*eth.DepositRedemptionRequestedEvent, error) { + if !common.IsHexAddress(depositAddress) { + return nil, fmt.Errorf("incorrect deposit contract address") + } + + events, err := tec.tbtcSystemFilterer.FilterRedemptionRequested( + []common.Address{ + common.HexToAddress(depositAddress), + }, + startBlock, + nil, + ) + if err != nil { + return nil, err + } + + result := make([]*eth.DepositRedemptionRequestedEvent, 0) + + for _, event := range events { + result = append(result, ð.DepositRedemptionRequestedEvent{ + DepositAddress: event.DepositContractAddress.Hex(), + RequesterAddress: event.Requester.Hex(), + Digest: event.Digest, + UtxoValue: event.UtxoValue, + RedeemerOutputScript: event.RedeemerOutputScript, + RequestedFee: event.RequestedFee, + Outpoint: event.Outpoint, + BlockNumber: event.BlockNumber, + }) + } + + // Make sure events are sorted by block number in ascending order. + sort.SliceStable(result, func(i, j int) bool { + return result[i].BlockNumber < result[j].BlockNumber + }) + + return result, nil +} + // KeepAddress returns the underlying keep address for the // provided deposit. func (tec *TBTCEthereumChain) KeepAddress( diff --git a/pkg/chain/event.go b/pkg/chain/event.go index b6772c904..c881cbb10 100644 --- a/pkg/chain/event.go +++ b/pkg/chain/event.go @@ -1,6 +1,8 @@ package eth import ( + "math/big" + "github.com/ethereum/go-ethereum/common" ) @@ -59,3 +61,19 @@ func (e *BondedECDSAKeepCreatedEvent) IsMember(address common.Address) bool { } return false } + +// DepositRedemptionRequestedEvent is an event emitted when a deposit +// redemption has been requested or the redemption fee has been increased. +// FIXME: This event should be placed in `extensions/tbtc` package but +// this cause an import cycle. For now, there are no better option +// than place it right here. +type DepositRedemptionRequestedEvent struct { + DepositAddress string + RequesterAddress string + Digest [32]byte + UtxoValue *big.Int + RedeemerOutputScript []byte + RequestedFee *big.Int + Outpoint []byte + BlockNumber uint64 +} diff --git a/pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go b/pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go index f37d6a391..d4c0ffa63 100644 --- a/pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go +++ b/pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go @@ -7,7 +7,7 @@ import ( ) // FIXME: This is a temporary structure allowing to access past events -// occurred emitted by the `BondedECDSAKeep` contract. This structure is +// emitted by the `BondedECDSAKeep` contract. This structure is // here because the generated contract wrappers from `gen/contract` // don't support `Filter*` methods yet. When the contract generator // will support those methods, the below structure can be removed. diff --git a/pkg/chain/local/local.go b/pkg/chain/local/local.go index dc9b83b3b..1c9a497f6 100644 --- a/pkg/chain/local/local.go +++ b/pkg/chain/local/local.go @@ -355,7 +355,11 @@ func (lc *localChain) PastSignatureSubmittedEvents( keepAddress string, startBlock uint64, ) ([]*eth.SignatureSubmittedEvent, error) { - panic("implement") + panic("not implemented") // TODO: Implementation for unit testing purposes. +} + +func (lc *localChain) BlockTimestamp(blockNumber *big.Int) (uint64, error) { + panic("not implemented") // TODO: Implementation for unit testing purposes. } func generateHandlerID() int { diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index b84eb1b26..621f6b033 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -3,9 +3,10 @@ package local import ( "bytes" "fmt" - "math/big" "sync" + eth "github.com/keep-network/keep-ecdsa/pkg/chain" + "github.com/ethereum/go-ethereum/common" "github.com/keep-network/keep-common/pkg/subscription" ) @@ -108,16 +109,7 @@ func (tlc *TBTCLocalChain) OnDepositRegisteredPubkey( } func (tlc *TBTCLocalChain) OnDepositRedemptionRequested( - handler func( - depositAddress string, - requesterAddress string, - digest [32]uint8, - utxoValue *big.Int, - redeemerOutputScript []uint8, - requestedFee *big.Int, - outpoint []uint8, - blockNumber uint64, - ), + handler func(depositAddress string), ) (subscription.EventSubscription, error) { panic("not implemented") // TODO: Implementation for unit testing purposes. } @@ -134,6 +126,13 @@ func (tlc *TBTCLocalChain) OnDepositRedeemed( panic("not implemented") // TODO: Implementation for unit testing purposes. } +func (tlc *TBTCLocalChain) PastDepositRedemptionRequestedEvents( + depositAddress string, + startBlock uint64, +) ([]*eth.DepositRedemptionRequestedEvent, error) { + panic("not implemented") // TODO: Implementation for unit testing purposes. +} + func (tlc *TBTCLocalChain) KeepAddress(depositAddress string) (string, error) { tlc.mutex.Lock() defer tlc.mutex.Unlock() diff --git a/pkg/extensions/tbtc/chain.go b/pkg/extensions/tbtc/chain.go index 651cd1521..3a70bbfec 100644 --- a/pkg/extensions/tbtc/chain.go +++ b/pkg/extensions/tbtc/chain.go @@ -1,8 +1,6 @@ package tbtc import ( - "math/big" - "github.com/keep-network/keep-common/pkg/subscription" chain "github.com/keep-network/keep-ecdsa/pkg/chain" ) @@ -62,16 +60,7 @@ type TBTCSystem interface { // OnDepositRedemptionRequested installs a callback that is invoked when an // on-chain notification of a deposit redemption request is seen. OnDepositRedemptionRequested( - handler func( - depositAddress string, - requesterAddress string, - digest [32]uint8, - utxoValue *big.Int, - redeemerOutputScript []uint8, - requestedFee *big.Int, - outpoint []uint8, - blockNumber uint64, - ), + handler func(depositAddress string), ) (subscription.EventSubscription, error) // OnDepositGotRedemptionSignature installs a callback that is invoked @@ -86,4 +75,13 @@ type TBTCSystem interface { OnDepositRedeemed( handler func(depositAddress string), ) (subscription.EventSubscription, error) + + // PastDepositRedemptionRequestedEvents returns all redemption requested + // events for the given deposit which occurred after the provided start block. + // All implementations should returns those events sorted by the + // block number in the ascending order. + PastDepositRedemptionRequestedEvents( + depositAddress string, + startBlock uint64, + ) ([]*chain.DepositRedemptionRequestedEvent, error) } diff --git a/pkg/extensions/tbtc/events_log.go b/pkg/extensions/tbtc/events_log.go deleted file mode 100644 index 9de547826..000000000 --- a/pkg/extensions/tbtc/events_log.go +++ /dev/null @@ -1,49 +0,0 @@ -package tbtc - -import ( - "sync" -) - -type tbtcEventsLog struct { - depositRedemptionRequestedEventStorage *eventsStorage -} - -func newTBTCEventsLog() *tbtcEventsLog { - return &tbtcEventsLog{ - depositRedemptionRequestedEventStorage: newEventsStorage(), - } -} - -func (tel *tbtcEventsLog) logDepositRedemptionRequestedEvent( - deposit string, - event *depositRedemptionRequestedEvent, -) { - tel.depositRedemptionRequestedEventStorage.storeEvent(deposit, event) -} - -type eventsStorage struct { - storage map[string][]interface{} // - storageMutex sync.Mutex -} - -func newEventsStorage() *eventsStorage { - return &eventsStorage{ - storage: make(map[string][]interface{}), - } -} - -func (ms *eventsStorage) storeEvent(deposit string, event interface{}) { - ms.storageMutex.Lock() - defer ms.storageMutex.Unlock() - - ms.storage[deposit] = append(ms.storage[deposit], event) -} - -func (ms *eventsStorage) getEvents(deposit string) []interface{} { - ms.storageMutex.Lock() - defer ms.storageMutex.Unlock() - - events := ms.storage[deposit] - - return events -} diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index fe06480b6..e255d006c 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -3,6 +3,7 @@ package tbtc import ( "bytes" "context" + "encoding/binary" "fmt" "math" "math/big" @@ -29,7 +30,7 @@ func Initialize(ctx context.Context, chain Handle) error { err := tbtc.monitorRetrievePubKey( ctx, exponentialBackoff, - 150*time.Minute, // 30 minutes before the 3 hours on-chain timeout + 165*time.Minute, // 15 minutes before the 3 hours on-chain timeout ) if err != nil { return fmt.Errorf( @@ -51,31 +52,25 @@ func Initialize(ctx context.Context, chain Handle) error { ) } + err = tbtc.monitorProvideRedemptionProof( + ctx, + exponentialBackoff, + 345*time.Minute, // 15 minutes before the 6 hours on-chain timeout + ) + if err != nil { + return fmt.Errorf( + "could not initialize provide redemption "+ + "proof monitoring: [%v]", + err, + ) + } + logger.Infof("tbtc extension has been initialized") return nil } -type depositEvent interface { - getDepositAddress() string -} - -type baseDepositEvent struct { - depositAddress string -} - -func (bde *baseDepositEvent) getDepositAddress() string { - return bde.depositAddress -} - -type depositRedemptionRequestedEvent struct { - *baseDepositEvent - - digest [32]uint8 - blockNumber uint64 -} - -type depositEventHandler func(depositEvent) +type depositEventHandler func(depositAddress string) type watchDepositEventFn func( handler depositEventHandler, @@ -87,23 +82,17 @@ type watchKeepClosedFn func(depositAddress string) ( err error, ) -type depositMonitoringContext struct { - startEvent depositEvent -} - -type submitDepositTxFn func(*depositMonitoringContext) error +type submitDepositTxFn func(depositAddress string) error type backoffFn func(iteration int) time.Duration type tbtc struct { - chain Handle - eventsLog *tbtcEventsLog + chain Handle } func newTBTC(chain Handle) *tbtc { return &tbtc{ - chain: chain, - eventsLog: newTBTCEventsLog(), + chain: chain, } } @@ -116,21 +105,13 @@ func (t *tbtc) monitorRetrievePubKey( ctx, "retrieve pubkey", func(handler depositEventHandler) (subscription.EventSubscription, error) { - return t.chain.OnDepositCreated(func(depositAddress string) { - handler(&baseDepositEvent{depositAddress}) - }) + return t.chain.OnDepositCreated(handler) }, func(handler depositEventHandler) (subscription.EventSubscription, error) { - return t.chain.OnDepositRegisteredPubkey(func(depositAddress string) { - handler(&baseDepositEvent{depositAddress}) - }) + return t.chain.OnDepositRegisteredPubkey(handler) }, t.watchKeepClosed, - func(monitoringContext *depositMonitoringContext) error { - return t.chain.RetrieveSignerPubkey( - monitoringContext.startEvent.getDepositAddress(), - ) - }, + t.chain.RetrieveSignerPubkey, actBackoffFn, timeout, ) @@ -157,52 +138,22 @@ func (t *tbtc) monitorProvideRedemptionSignature( monitoringStartFn := func( handler depositEventHandler, ) (subscription.EventSubscription, error) { - return t.chain.OnDepositRedemptionRequested( - func( - depositAddress string, - requesterAddress string, - digest [32]uint8, - utxoValue *big.Int, - redeemerOutputScript []uint8, - requestedFee *big.Int, - outpoint []uint8, - blockNumber uint64, - ) { - event := &depositRedemptionRequestedEvent{ - &baseDepositEvent{depositAddress}, - digest, - blockNumber, - } - - // Log the event to make it available for future provide - // redemption proof monitoring process. - t.eventsLog.logDepositRedemptionRequestedEvent( - depositAddress, - event, - ) - - handler(event) - }, - ) + // Start right after a redemption has been requested or the redemption + // fee has been increased. + return t.chain.OnDepositRedemptionRequested(handler) } monitoringStopFn := func( handler depositEventHandler, ) (subscription.EventSubscription, error) { - signatureSubscription, err := t.chain.OnDepositGotRedemptionSignature( - func(depositAddress string) { - handler(&baseDepositEvent{depositAddress}) - }, - ) + // Stop in case the redemption signature has been provided by someone else. + signatureSubscription, err := t.chain.OnDepositGotRedemptionSignature(handler) if err != nil { return nil, err } - redeemedSubscription, err := t.chain.OnDepositRedeemed( - func(depositAddress string) { - handler(&baseDepositEvent{depositAddress}) - }, - ) + // Stop in case the redemption proof has been provided by someone else. + redeemedSubscription, err := t.chain.OnDepositRedeemed(handler) if err != nil { return nil, err } @@ -215,31 +166,39 @@ func (t *tbtc) monitorProvideRedemptionSignature( ), nil } - actFn := func(monitoringContext *depositMonitoringContext) error { - depositRedemptionRequestedEvent, ok := monitoringContext. - startEvent.(*depositRedemptionRequestedEvent) - if !ok { - return fmt.Errorf( - "monitoring context contains unexpected type of start event", - ) + actFn := func(depositAddress string) error { + keepAddress, err := t.chain.KeepAddress(depositAddress) + if err != nil { + return err } - depositAddress := depositRedemptionRequestedEvent.depositAddress - - keepAddress, err := t.chain.KeepAddress(depositAddress) + redemptionRequestedEvents, err := t.chain.PastDepositRedemptionRequestedEvents( + depositAddress, + 0, // TODO: Should be something better that 0 + ) if err != nil { return err } + if len(redemptionRequestedEvents) == 0 { + return fmt.Errorf( + "no redemption requested events found for deposit: [%v]", + depositAddress, + ) + } + + latestRedemptionRequestedEvent := + redemptionRequestedEvents[len(redemptionRequestedEvents)-1] + signatureSubmittedEvents, err := t.chain.PastSignatureSubmittedEvents( keepAddress, - depositRedemptionRequestedEvent.blockNumber, + latestRedemptionRequestedEvent.BlockNumber, ) if err != nil { return err } - depositDigest := depositRedemptionRequestedEvent.digest + depositDigest := latestRedemptionRequestedEvent.Digest for _, signatureSubmittedEvent := range signatureSubmittedEvents { if bytes.Equal(signatureSubmittedEvent.Digest[:], depositDigest[:]) { @@ -288,6 +247,108 @@ func (t *tbtc) monitorProvideRedemptionSignature( return nil } +func (t *tbtc) monitorProvideRedemptionProof( + ctx context.Context, + actBackoffFn backoffFn, + timeout time.Duration, +) error { + monitoringStartFn := func( + handler depositEventHandler, + ) (subscription.EventSubscription, error) { + // Start right after a redemption signature has been provided. + return t.chain.OnDepositGotRedemptionSignature(handler) + } + + monitoringStopFn := func( + handler depositEventHandler, + ) (subscription.EventSubscription, error) { + // Stop in case the redemption fee has been increased by someone else. + redemptionRequestedSubscription, err := t.chain.OnDepositRedemptionRequested( + handler, + ) + if err != nil { + return nil, err + } + + // Stop in case the redemption proof has been provided by someone else. + redeemedSubscription, err := t.chain.OnDepositRedeemed(handler) + if err != nil { + return nil, err + } + + return subscription.NewEventSubscription( + func() { + redemptionRequestedSubscription.Unsubscribe() + redeemedSubscription.Unsubscribe() + }, + ), nil + } + + actFn := func(depositAddress string) error { + redemptionRequestedEvents, err := t.chain.PastDepositRedemptionRequestedEvents( + depositAddress, + 0, // TODO: Should be something better that 0 + ) + if err != nil { + return err + } + + if len(redemptionRequestedEvents) == 0 { + return fmt.Errorf( + "no redemption requested events found for deposit: [%v]", + depositAddress, + ) + } + + // TODO: Check whether the redemption proof can be submitted by + // interacting with the BTC chain. If yes, construct and submit + // the proof. If not, try to increase the redemption fee. + + latestRedemptionRequestedEvent := + redemptionRequestedEvents[len(redemptionRequestedEvents)-1] + + previousOutputValue := new(big.Int).Sub( + latestRedemptionRequestedEvent.UtxoValue, + latestRedemptionRequestedEvent.RequestedFee, + ) + + newOutputValue := new(big.Int).Sub( + previousOutputValue, + redemptionRequestedEvents[0].RequestedFee, // initial fee + ) + + return t.chain.IncreaseRedemptionFee( + depositAddress, + toLittleEndianBytes(previousOutputValue), + toLittleEndianBytes(newOutputValue), + ) + } + + monitoringSubscription, err := t.monitorAndAct( + ctx, + "provide redemption proof", + monitoringStartFn, + monitoringStopFn, + t.watchKeepClosed, + actFn, + actBackoffFn, + timeout, // TODO: Timeout must be counted from the `RedemptionRequested` event not from the monitoring start event `GotRedemptionSignature` + ) + if err != nil { + return err + } + + go func() { + <-ctx.Done() + monitoringSubscription.Unsubscribe() + logger.Infof("provide redemption proof monitoring disabled") + }() + + logger.Infof("provide redemption proof monitoring initialized") + + return nil +} + // TODO: // 1. Filter incoming events by operator interest. // 2. Incoming events deduplication. @@ -302,18 +363,18 @@ func (t *tbtc) monitorAndAct( actBackoffFn backoffFn, timeout time.Duration, ) (subscription.EventSubscription, error) { - handleStartEvent := func(startEvent depositEvent) { + handleStartEvent := func(depositAddress string) { logger.Infof( "starting [%v] monitoring for deposit [%v]", monitoringName, - startEvent.getDepositAddress(), + depositAddress, ) stopEventChan := make(chan struct{}) stopEventSubscription, err := monitoringStopFn( - func(stopEvent depositEvent) { - if startEvent.getDepositAddress() == stopEvent.getDepositAddress() { + func(stopEventDepositAddress string) { + if depositAddress == stopEventDepositAddress { stopEventChan <- struct{}{} } }, @@ -323,7 +384,7 @@ func (t *tbtc) monitorAndAct( "could not setup stop event handler for [%v] "+ "monitoring for deposit [%v]: [%v]", monitoringName, - startEvent.getDepositAddress(), + depositAddress, err, ) return @@ -331,14 +392,14 @@ func (t *tbtc) monitorAndAct( defer stopEventSubscription.Unsubscribe() keepClosedChan, keepClosedUnsubscribe, err := keepClosedFn( - startEvent.getDepositAddress(), + depositAddress, ) if err != nil { logger.Errorf( "could not setup keep closed handler for [%v] "+ "monitoring for deposit [%v]: [%v]", monitoringName, - startEvent.getDepositAddress(), + depositAddress, err, ) return @@ -357,7 +418,7 @@ func (t *tbtc) monitorAndAct( "context is done for [%v] "+ "monitoring for deposit [%v]", monitoringName, - startEvent.getDepositAddress(), + depositAddress, ) break monitoring case <-stopEventChan: @@ -365,7 +426,7 @@ func (t *tbtc) monitorAndAct( "stop event occurred for [%v] "+ "monitoring for deposit [%v]", monitoringName, - startEvent.getDepositAddress(), + depositAddress, ) break monitoring case <-keepClosedChan: @@ -373,7 +434,7 @@ func (t *tbtc) monitorAndAct( "keep closed event occurred for [%v] "+ "monitoring for deposit [%v]", monitoringName, - startEvent.getDepositAddress(), + depositAddress, ) break monitoring case <-timeoutChan: @@ -381,10 +442,10 @@ func (t *tbtc) monitorAndAct( "[%v] not performed in the expected time frame "+ "for deposit [%v]; performing the action", monitoringName, - startEvent.getDepositAddress(), + depositAddress, ) - err := actFn(&depositMonitoringContext{startEvent}) + err := actFn(depositAddress) if err != nil { if actionAttempt == maxActAttempts { logger.Errorf( @@ -392,7 +453,7 @@ func (t *tbtc) monitorAndAct( "for [%v] monitoring for deposit [%v]: [%v]; "+ "the maximum number of attempts reached", monitoringName, - startEvent.getDepositAddress(), + depositAddress, err, ) break monitoring @@ -405,7 +466,7 @@ func (t *tbtc) monitorAndAct( "for [%v] monitoring for deposit [%v]: [%v]; "+ "retrying after: [%v]", monitoringName, - startEvent.getDepositAddress(), + depositAddress, err, backoff, ) @@ -421,13 +482,13 @@ func (t *tbtc) monitorAndAct( logger.Infof( "stopped [%v] monitoring for deposit [%v]", monitoringName, - startEvent.getDepositAddress(), + depositAddress, ) } return monitoringStartFn( - func(startEvent depositEvent) { - go handleStartEvent(startEvent) + func(depositAddress string) { + go handleStartEvent(depositAddress) }, ) } @@ -483,3 +544,9 @@ func exponentialBackoff(iteration int) time.Duration { jitterMillis := rand.Intn(100) return time.Duration(int(backoffMillis)+jitterMillis) * time.Millisecond } + +func toLittleEndianBytes(value *big.Int) [8]byte { + var valueBytes [8]byte + binary.LittleEndian.PutUint64(valueBytes[:], value.Uint64()) + return valueBytes +} From 8bf484121e7df571b2e38757cc9559e6ea04e9a3 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Fri, 23 Oct 2020 16:32:24 +0200 Subject: [PATCH 07/31] Set the right timeout for redemption proof action --- pkg/extensions/tbtc/tbtc.go | 69 ++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index e255d006c..5b5926035 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -86,6 +86,8 @@ type submitDepositTxFn func(depositAddress string) error type backoffFn func(iteration int) time.Duration +type timeoutFn func(depositAddress string) (time.Duration, error) + type tbtc struct { chain Handle } @@ -113,7 +115,9 @@ func (t *tbtc) monitorRetrievePubKey( t.watchKeepClosed, t.chain.RetrieveSignerPubkey, actBackoffFn, - timeout, + func(_ string) (time.Duration, error) { + return timeout, nil + }, ) if err != nil { return err @@ -230,7 +234,9 @@ func (t *tbtc) monitorProvideRedemptionSignature( t.watchKeepClosed, actFn, actBackoffFn, - timeout, + func(_ string) (time.Duration, error) { + return timeout, nil + }, ) if err != nil { return err @@ -324,6 +330,49 @@ func (t *tbtc) monitorProvideRedemptionProof( ) } + timeoutFn := func(depositAddress string) (time.Duration, error) { + // Get the seconds timestamp in the moment when this function is + // invoked. This is when the monitoring starts in response of + // the `GotRedemptionSignature` event. + gotRedemptionSignatureTimestamp := uint64(time.Now().Unix()) + + redemptionRequestedEvents, err := t.chain.PastDepositRedemptionRequestedEvents( + depositAddress, + 0, // TODO: Should be something better that 0 + ) + if err != nil { + return 0, err + } + + if len(redemptionRequestedEvents) == 0 { + return 0, fmt.Errorf( + "no redemption requested events found for deposit: [%v]", + depositAddress, + ) + } + + latestRedemptionRequestedEvent := + redemptionRequestedEvents[len(redemptionRequestedEvents)-1] + + // Get the seconds timestamp for the latest redemption request. + redemptionRequestedTimestamp, err := t.chain.BlockTimestamp( + new(big.Int).SetUint64(latestRedemptionRequestedEvent.BlockNumber), + ) + if err != nil { + return 0, err + } + + // We must shift the constant timeout value by subtracting the time + // elapsed between the redemption request and the redemption signature. + // This way we obtain a value close to the redemption proof timeout + // and it doesn't matter when the redemption signature arrives. + timeoutShift := time.Duration( + gotRedemptionSignatureTimestamp-redemptionRequestedTimestamp, + ) * time.Second + + return timeout - timeoutShift, nil + } + monitoringSubscription, err := t.monitorAndAct( ctx, "provide redemption proof", @@ -332,7 +381,7 @@ func (t *tbtc) monitorProvideRedemptionProof( t.watchKeepClosed, actFn, actBackoffFn, - timeout, // TODO: Timeout must be counted from the `RedemptionRequested` event not from the monitoring start event `GotRedemptionSignature` + timeoutFn, ) if err != nil { return err @@ -361,7 +410,7 @@ func (t *tbtc) monitorAndAct( keepClosedFn watchKeepClosedFn, actFn submitDepositTxFn, actBackoffFn backoffFn, - timeout time.Duration, + timeoutFn timeoutFn, ) (subscription.EventSubscription, error) { handleStartEvent := func(depositAddress string) { logger.Infof( @@ -406,6 +455,18 @@ func (t *tbtc) monitorAndAct( } defer keepClosedUnsubscribe() + timeout, err := timeoutFn(depositAddress) + if err != nil { + logger.Errorf( + "could determine timeout value for [%v] "+ + "monitoring for deposit [%v]: [%v]", + monitoringName, + depositAddress, + err, + ) + return + } + timeoutChan := time.After(timeout) actionAttempt := 1 From db1536fcd40790ad18def547a18934944fa07845 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 26 Oct 2020 10:16:23 +0100 Subject: [PATCH 08/31] Move auxiliary types above `monitorAndAct` --- pkg/extensions/tbtc/tbtc.go | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index 5b5926035..573e90172 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -70,24 +70,6 @@ func Initialize(ctx context.Context, chain Handle) error { return nil } -type depositEventHandler func(depositAddress string) - -type watchDepositEventFn func( - handler depositEventHandler, -) (subscription.EventSubscription, error) - -type watchKeepClosedFn func(depositAddress string) ( - keepClosedChan chan struct{}, - unsubscribe func(), - err error, -) - -type submitDepositTxFn func(depositAddress string) error - -type backoffFn func(iteration int) time.Duration - -type timeoutFn func(depositAddress string) (time.Duration, error) - type tbtc struct { chain Handle } @@ -398,6 +380,24 @@ func (t *tbtc) monitorProvideRedemptionProof( return nil } +type depositEventHandler func(depositAddress string) + +type watchDepositEventFn func( + handler depositEventHandler, +) (subscription.EventSubscription, error) + +type watchKeepClosedFn func(depositAddress string) ( + keepClosedChan chan struct{}, + unsubscribe func(), + err error, +) + +type submitDepositTxFn func(depositAddress string) error + +type backoffFn func(iteration int) time.Duration + +type timeoutFn func(depositAddress string) (time.Duration, error) + // TODO: // 1. Filter incoming events by operator interest. // 2. Incoming events deduplication. From 3396b774325a825ec4d625c0bb9d3b5dd5f177b0 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 26 Oct 2020 10:22:49 +0100 Subject: [PATCH 09/31] Change alias of the `pkg/chain` package --- pkg/chain/ethereum/tbtc.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/chain/ethereum/tbtc.go b/pkg/chain/ethereum/tbtc.go index 34d7588c6..47d0f51cc 100644 --- a/pkg/chain/ethereum/tbtc.go +++ b/pkg/chain/ethereum/tbtc.go @@ -5,7 +5,7 @@ import ( "math/big" "sort" - eth "github.com/keep-network/keep-ecdsa/pkg/chain" + chain "github.com/keep-network/keep-ecdsa/pkg/chain" "github.com/ethereum/go-ethereum/common" "github.com/keep-network/keep-common/pkg/subscription" @@ -194,7 +194,7 @@ func (tec *TBTCEthereumChain) OnDepositRedeemed( func (tec *TBTCEthereumChain) PastDepositRedemptionRequestedEvents( depositAddress string, startBlock uint64, -) ([]*eth.DepositRedemptionRequestedEvent, error) { +) ([]*chain.DepositRedemptionRequestedEvent, error) { if !common.IsHexAddress(depositAddress) { return nil, fmt.Errorf("incorrect deposit contract address") } @@ -210,10 +210,10 @@ func (tec *TBTCEthereumChain) PastDepositRedemptionRequestedEvents( return nil, err } - result := make([]*eth.DepositRedemptionRequestedEvent, 0) + result := make([]*chain.DepositRedemptionRequestedEvent, 0) for _, event := range events { - result = append(result, ð.DepositRedemptionRequestedEvent{ + result = append(result, &chain.DepositRedemptionRequestedEvent{ DepositAddress: event.DepositContractAddress.Hex(), RequesterAddress: event.Requester.Hex(), Digest: event.Digest, From 2307ccdfdd97d82067a454c4efd49a12a67e16bc Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 26 Oct 2020 10:40:35 +0100 Subject: [PATCH 10/31] Update tbtc and use the `TBTCSystemEventLog` --- go.mod | 2 +- go.sum | 2 ++ pkg/chain/ethereum/tbtc.go | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index a0a3220cf..139d4d6e7 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/ipfs/go-log v1.0.4 github.com/keep-network/keep-common v1.2.1-0.20201020114759-19c123cbd4f4 github.com/keep-network/keep-core v1.3.0 - github.com/keep-network/tbtc v1.1.1-0.20201023121153-c4c21e8404c5 + github.com/keep-network/tbtc v1.1.1-0.20201026093513-cb9246987718 github.com/pkg/errors v0.9.1 github.com/urfave/cli v1.22.1 ) diff --git a/go.sum b/go.sum index dcc162532..af86097a1 100644 --- a/go.sum +++ b/go.sum @@ -339,6 +339,8 @@ github.com/keep-network/keep-core v1.3.0 h1:7Tb33EmO/ntHOEbOiYciRlBhqu5Ln6KemWCa github.com/keep-network/keep-core v1.3.0/go.mod h1:1KsSSTQoN754TrFLW7kLy50pOG2CQ4BOfnJqdvEG7FA= github.com/keep-network/tbtc v1.1.1-0.20201023121153-c4c21e8404c5 h1:eETyOKkTyO51URqSoXsaMe0VYB1AiQGBnlBEXX/XHOY= github.com/keep-network/tbtc v1.1.1-0.20201023121153-c4c21e8404c5/go.mod h1:igBF2MPTFkzOdZ3gcwt8h0Zb5pZaHnij/iPZoMB9IKM= +github.com/keep-network/tbtc v1.1.1-0.20201026093513-cb9246987718 h1:/ZNMBY7y6hfzCYA8mgtHnspGO26OmWV3sDehyGnqRyY= +github.com/keep-network/tbtc v1.1.1-0.20201026093513-cb9246987718/go.mod h1:igBF2MPTFkzOdZ3gcwt8h0Zb5pZaHnij/iPZoMB9IKM= github.com/keep-network/toml v0.3.0 h1:G+NJwWR/ZiORqeLBsDXDchYoL29PXHdxOPcCueA7ctE= github.com/keep-network/toml v0.3.0/go.mod h1:Zeyd3lxbIlMYLREho3UK1dMP2xjqt2gLkQ5E5vM6K38= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= diff --git a/pkg/chain/ethereum/tbtc.go b/pkg/chain/ethereum/tbtc.go index 47d0f51cc..2242e3ec2 100644 --- a/pkg/chain/ethereum/tbtc.go +++ b/pkg/chain/ethereum/tbtc.go @@ -10,7 +10,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/keep-network/keep-common/pkg/subscription" "github.com/keep-network/tbtc/pkg/chain/ethereum/gen/contract" - "github.com/keep-network/tbtc/pkg/chain/ethereum/gen/filterer" + "github.com/keep-network/tbtc/pkg/chain/ethereum/gen/eventlog" ) // TBTCEthereumChain represents an Ethereum chain handle with @@ -19,7 +19,7 @@ type TBTCEthereumChain struct { *EthereumChain tbtcSystemContract *contract.TBTCSystem - tbtcSystemFilterer *filterer.TBTCSystemFilterer + tbtcSystemEventLog *eventlog.TBTCSystemEventLog } // WithTBTCExtension extends the Ethereum chain handle with @@ -44,7 +44,7 @@ func WithTBTCExtension( return nil, err } - tbtcSystemFilterer, err := filterer.NewTBTCSystemFilterer( + tbtcSystemEventLog, err := eventlog.NewTBTCSystemEventLog( common.HexToAddress(tbtcSystemContractAddress), ethereumChain.client, ) @@ -55,7 +55,7 @@ func WithTBTCExtension( return &TBTCEthereumChain{ EthereumChain: ethereumChain, tbtcSystemContract: tbtcSystemContract, - tbtcSystemFilterer: tbtcSystemFilterer, + tbtcSystemEventLog: tbtcSystemEventLog, }, nil } @@ -199,7 +199,7 @@ func (tec *TBTCEthereumChain) PastDepositRedemptionRequestedEvents( return nil, fmt.Errorf("incorrect deposit contract address") } - events, err := tec.tbtcSystemFilterer.FilterRedemptionRequested( + events, err := tec.tbtcSystemEventLog.PastRedemptionRequestedEvents( []common.Address{ common.HexToAddress(depositAddress), }, From 4482cbd0770f70c9181eb6655a30338d15d055d3 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 26 Oct 2020 10:48:01 +0100 Subject: [PATCH 11/31] Rename `BondedECDSAKeepFilterer` --- pkg/chain/ethereum/ethereum.go | 9 ++++++--- .../BondedECDSAKeepEventLog.go} | 14 +++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) rename pkg/chain/gen/{filterer/BondedECDSAKeepFilterer.go => eventlog/BondedECDSAKeepEventLog.go} (82%) diff --git a/pkg/chain/ethereum/ethereum.go b/pkg/chain/ethereum/ethereum.go index 40a954542..3e4a05de9 100644 --- a/pkg/chain/ethereum/ethereum.go +++ b/pkg/chain/ethereum/ethereum.go @@ -6,7 +6,7 @@ import ( "math/big" "time" - "github.com/keep-network/keep-ecdsa/pkg/chain/gen/filterer" + "github.com/keep-network/keep-ecdsa/pkg/chain/gen/eventlog" "github.com/ethereum/go-ethereum/common" @@ -520,7 +520,7 @@ func (ec *EthereumChain) PastSignatureSubmittedEvents( return nil, fmt.Errorf("invalid keep address: [%v]", keepAddress) } - keepContractFilterer, err := filterer.NewBondedECDSAKeepFilterer( + keepContractEventLog, err := eventlog.NewBondedECDSAKeepEventLog( common.HexToAddress(keepAddress), ec.client, ) @@ -528,7 +528,10 @@ func (ec *EthereumChain) PastSignatureSubmittedEvents( return nil, err } - events, err := keepContractFilterer.FilterSignatureSubmitted(startBlock, nil) + events, err := keepContractEventLog.PastSignatureSubmittedEvents( + startBlock, + nil, + ) if err != nil { return nil, err } diff --git a/pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go b/pkg/chain/gen/eventlog/BondedECDSAKeepEventLog.go similarity index 82% rename from pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go rename to pkg/chain/gen/eventlog/BondedECDSAKeepEventLog.go index d4c0ffa63..29a30ebd3 100644 --- a/pkg/chain/gen/filterer/BondedECDSAKeepFilterer.go +++ b/pkg/chain/gen/eventlog/BondedECDSAKeepEventLog.go @@ -1,4 +1,4 @@ -package filterer +package eventlog import ( "github.com/ethereum/go-ethereum/accounts/abi/bind" @@ -11,20 +11,20 @@ import ( // here because the generated contract wrappers from `gen/contract` // don't support `Filter*` methods yet. When the contract generator // will support those methods, the below structure can be removed. -type BondedECDSAKeepFilterer struct { +type BondedECDSAKeepEventLog struct { contract *abi.BondedECDSAKeep } -func NewBondedECDSAKeepFilterer( +func NewBondedECDSAKeepEventLog( contractAddress common.Address, backend bind.ContractBackend, -) (*BondedECDSAKeepFilterer, error) { +) (*BondedECDSAKeepEventLog, error) { contract, err := abi.NewBondedECDSAKeep(contractAddress, backend) if err != nil { return nil, err } - return &BondedECDSAKeepFilterer{contract}, nil + return &BondedECDSAKeepEventLog{contract}, nil } type BondedECDSAKeepSignatureSubmitted struct { @@ -34,11 +34,11 @@ type BondedECDSAKeepSignatureSubmitted struct { RecoveryID uint8 } -func (bekf *BondedECDSAKeepFilterer) FilterSignatureSubmitted( +func (bekel *BondedECDSAKeepEventLog) PastSignatureSubmittedEvents( startBlock uint64, endBlock *uint64, ) ([]*BondedECDSAKeepSignatureSubmitted, error) { - iterator, err := bekf.contract.FilterSignatureSubmitted( + iterator, err := bekel.contract.FilterSignatureSubmitted( &bind.FilterOpts{ Start: startBlock, End: endBlock, From 513cc8f155aa29d08006315b4bf64b4fb2c83e28 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 26 Oct 2020 10:55:11 +0100 Subject: [PATCH 12/31] Add TODO about chain reorgs --- pkg/extensions/tbtc/tbtc.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index 573e90172..2fc124061 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -402,6 +402,7 @@ type timeoutFn func(depositAddress string) (time.Duration, error) // 1. Filter incoming events by operator interest. // 2. Incoming events deduplication. // 3. Resume monitoring after client restart. +// 4. Handle chain reorgs (keep-ecdsa/pull/585#discussion_r511760283) func (t *tbtc) monitorAndAct( ctx context.Context, monitoringName string, From 01696f39d9b9a80eac81fced14d5171212d9906d Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 26 Oct 2020 11:17:05 +0100 Subject: [PATCH 13/31] Change signature lookup during action execution --- pkg/chain/chain.go | 2 ++ pkg/chain/ethereum/ethereum.go | 16 ++++++++++++---- pkg/chain/event.go | 9 +++++---- .../gen/eventlog/BondedECDSAKeepEventLog.go | 18 ++++++++++-------- pkg/extensions/tbtc/tbtc.go | 5 ++++- 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go index dde8ecb07..d5118854a 100644 --- a/pkg/chain/chain.go +++ b/pkg/chain/chain.go @@ -148,6 +148,8 @@ type BondedECDSAKeep interface { // PastSignatureSubmittedEvents returns all signature submitted events // for the given keep which occurred after the provided start block. + // All implementations should returns those events sorted by the + // block number in the ascending order. PastSignatureSubmittedEvents( keepAddress string, startBlock uint64, diff --git a/pkg/chain/ethereum/ethereum.go b/pkg/chain/ethereum/ethereum.go index 3e4a05de9..0e1db077a 100644 --- a/pkg/chain/ethereum/ethereum.go +++ b/pkg/chain/ethereum/ethereum.go @@ -4,6 +4,7 @@ package ethereum import ( "fmt" "math/big" + "sort" "time" "github.com/keep-network/keep-ecdsa/pkg/chain/gen/eventlog" @@ -512,6 +513,7 @@ func (ec *EthereumChain) GetOpenedTimestamp(keepAddress common.Address) (time.Ti // PastSignatureSubmittedEvents returns all signature submitted events // for the given keep which occurred after the provided start block. +// Returned events are sorted by the block number in the ascending order. func (ec *EthereumChain) PastSignatureSubmittedEvents( keepAddress string, startBlock uint64, @@ -540,13 +542,19 @@ func (ec *EthereumChain) PastSignatureSubmittedEvents( for _, event := range events { result = append(result, ð.SignatureSubmittedEvent{ - Digest: event.Digest, - R: event.R, - S: event.S, - RecoveryID: event.RecoveryID, + Digest: event.Digest, + R: event.R, + S: event.S, + RecoveryID: event.RecoveryID, + BlockNumber: event.BlockNumber, }) } + // Make sure events are sorted by block number in ascending order. + sort.SliceStable(result, func(i, j int) bool { + return result[i].BlockNumber < result[j].BlockNumber + }) + return result, nil } diff --git a/pkg/chain/event.go b/pkg/chain/event.go index c881cbb10..01a44010f 100644 --- a/pkg/chain/event.go +++ b/pkg/chain/event.go @@ -46,10 +46,11 @@ type KeepTerminatedEvent struct { // SignatureSubmittedEvent is an event emitted when a keep submits a signature. type SignatureSubmittedEvent struct { - Digest [32]byte - R [32]byte - S [32]byte - RecoveryID uint8 + Digest [32]byte + R [32]byte + S [32]byte + RecoveryID uint8 + BlockNumber uint64 } // IsMember checks if list of members contains the given address. diff --git a/pkg/chain/gen/eventlog/BondedECDSAKeepEventLog.go b/pkg/chain/gen/eventlog/BondedECDSAKeepEventLog.go index 29a30ebd3..1c34454d3 100644 --- a/pkg/chain/gen/eventlog/BondedECDSAKeepEventLog.go +++ b/pkg/chain/gen/eventlog/BondedECDSAKeepEventLog.go @@ -28,10 +28,11 @@ func NewBondedECDSAKeepEventLog( } type BondedECDSAKeepSignatureSubmitted struct { - Digest [32]byte - R [32]byte - S [32]byte - RecoveryID uint8 + Digest [32]byte + R [32]byte + S [32]byte + RecoveryID uint8 + BlockNumber uint64 } func (bekel *BondedECDSAKeepEventLog) PastSignatureSubmittedEvents( @@ -58,10 +59,11 @@ func (bekel *BondedECDSAKeepEventLog) PastSignatureSubmittedEvents( event := iterator.Event events = append(events, &BondedECDSAKeepSignatureSubmitted{ - Digest: event.Digest, - R: event.R, - S: event.S, - RecoveryID: event.RecoveryID, + Digest: event.Digest, + R: event.R, + S: event.S, + RecoveryID: event.RecoveryID, + BlockNumber: event.Raw.BlockNumber, }) } diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index 2fc124061..1c00bab15 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -186,7 +186,10 @@ func (t *tbtc) monitorProvideRedemptionSignature( depositDigest := latestRedemptionRequestedEvent.Digest - for _, signatureSubmittedEvent := range signatureSubmittedEvents { + // Start iterating from the latest event. + for i := len(signatureSubmittedEvents) - 1; i >= 0; i-- { + signatureSubmittedEvent := signatureSubmittedEvents[i] + if bytes.Equal(signatureSubmittedEvent.Digest[:], depositDigest[:]) { // We add 27 to the recovery ID to align it with ethereum and // bitcoin protocols where 27 is added to recovery ID to From 6c03e6b7f7d285164919e0b4acf459da3671b91d Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 26 Oct 2020 14:36:16 +0100 Subject: [PATCH 14/31] Optimize the start block when fetching past events --- pkg/extensions/tbtc/tbtc.go | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index 1c00bab15..31ae1590c 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -19,7 +19,10 @@ import ( var logger = log.Logger("tbtc-extension") -const maxActAttempts = 3 +const ( + maxActAttempts = 3 + pastEventsLookbackBlocks = 10000 +) // Initialize initializes extension specific to the TBTC application. func Initialize(ctx context.Context, chain Handle) error { @@ -160,7 +163,7 @@ func (t *tbtc) monitorProvideRedemptionSignature( redemptionRequestedEvents, err := t.chain.PastDepositRedemptionRequestedEvents( depositAddress, - 0, // TODO: Should be something better that 0 + t.pastEventsLookupStartBlock(), ) if err != nil { return err @@ -278,7 +281,7 @@ func (t *tbtc) monitorProvideRedemptionProof( actFn := func(depositAddress string) error { redemptionRequestedEvents, err := t.chain.PastDepositRedemptionRequestedEvents( depositAddress, - 0, // TODO: Should be something better that 0 + t.pastEventsLookupStartBlock(), ) if err != nil { return err @@ -323,7 +326,7 @@ func (t *tbtc) monitorProvideRedemptionProof( redemptionRequestedEvents, err := t.chain.PastDepositRedemptionRequestedEvents( depositAddress, - 0, // TODO: Should be something better that 0 + t.pastEventsLookupStartBlock(), ) if err != nil { return 0, err @@ -596,6 +599,19 @@ func (t *tbtc) watchKeepClosed( return signalChan, unsubscribe, nil } +func (t *tbtc) pastEventsLookupStartBlock() uint64 { + currentBlock, err := t.chain.BlockCounter().CurrentBlock() + if err != nil { + return 0 // if something went wrong, start from block `0` + } + + if currentBlock <= pastEventsLookbackBlocks { + return 0 + } + + return currentBlock - pastEventsLookbackBlocks +} + // Computes the exponential backoff value for given iteration. // For each iteration the result value will be in range: // - iteration 1: [2000ms, 2100ms) From 1c9aa0947d9bbc7fef5d624db6b80f987f972724 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Tue, 27 Oct 2020 10:50:42 +0100 Subject: [PATCH 15/31] Happy path test for provide redemption signature --- pkg/chain/local/bonded_ecdsa_keep.go | 11 +- pkg/chain/local/bonded_ecdsa_keep_factory.go | 1 + pkg/chain/local/local.go | 64 +++++- pkg/chain/local/tbtc.go | 206 +++++++++++++++++-- pkg/extensions/tbtc/tbtc_test.go | 129 ++++++++++++ 5 files changed, 389 insertions(+), 22 deletions(-) diff --git a/pkg/chain/local/bonded_ecdsa_keep.go b/pkg/chain/local/bonded_ecdsa_keep.go index 73c9e94cc..74151b359 100644 --- a/pkg/chain/local/bonded_ecdsa_keep.go +++ b/pkg/chain/local/bonded_ecdsa_keep.go @@ -16,14 +16,17 @@ const ( ) type localKeep struct { - publicKey [64]byte - members []common.Address - status keepStatus + publicKey [64]byte + members []common.Address + status keepStatus + latestDigest [32]byte signatureRequestedHandlers map[int]func(event *eth.SignatureRequestedEvent) keepClosedHandlers map[int]func(event *eth.KeepClosedEvent) keepTerminatedHandlers map[int]func(event *eth.KeepTerminatedEvent) + + signatureSubmittedEvents []*eth.SignatureSubmittedEvent } func (c *localChain) requestSignature(keepAddress common.Address, digest [32]byte) error { @@ -38,6 +41,8 @@ func (c *localChain) requestSignature(keepAddress common.Address, digest [32]byt ) } + keep.latestDigest = digest + signatureRequestedEvent := ð.SignatureRequestedEvent{ Digest: digest, } diff --git a/pkg/chain/local/bonded_ecdsa_keep_factory.go b/pkg/chain/local/bonded_ecdsa_keep_factory.go index e0c756069..939cae91e 100644 --- a/pkg/chain/local/bonded_ecdsa_keep_factory.go +++ b/pkg/chain/local/bonded_ecdsa_keep_factory.go @@ -31,6 +31,7 @@ func (c *localChain) createKeepWithMembers( signatureRequestedHandlers: make(map[int]func(event *chain.SignatureRequestedEvent)), keepClosedHandlers: make(map[int]func(event *chain.KeepClosedEvent)), keepTerminatedHandlers: make(map[int]func(event *chain.KeepTerminatedEvent)), + signatureSubmittedEvents: make([]*chain.SignatureSubmittedEvent, 0), } c.keeps[keepAddress] = localKeep diff --git a/pkg/chain/local/local.go b/pkg/chain/local/local.go index 1c9a497f6..d36cba696 100644 --- a/pkg/chain/local/local.go +++ b/pkg/chain/local/local.go @@ -7,6 +7,9 @@ import ( "sync" "time" + "github.com/keep-network/keep-core/pkg/chain/local" + "github.com/keep-network/keep-ecdsa/pkg/utils/byteutils" + "github.com/ethereum/go-ethereum/common" "github.com/keep-network/keep-common/pkg/subscription" "github.com/keep-network/keep-core/pkg/chain" @@ -32,6 +35,8 @@ type Chain interface { type localChain struct { handlerMutex sync.Mutex + blockCounter chain.BlockCounter + keepAddresses []common.Address keeps map[common.Address]*localKeep @@ -45,7 +50,13 @@ type localChain struct { // Connect performs initialization for communication with Ethereum blockchain // based on provided config. func Connect() Chain { + blockCounter, err := local.BlockCounter() + if err != nil { + panic(err) // should never happen + } + return &localChain{ + blockCounter: blockCounter, keeps: make(map[common.Address]*localKeep), keepCreatedHandlers: make(map[int]func(event *eth.BondedECDSAKeepCreatedEvent)), clientAddress: common.HexToAddress("6299496199d99941193Fdd2d717ef585F431eA05"), @@ -145,6 +156,9 @@ func (lc *localChain) SubmitKeepPublicKey( keepAddress common.Address, publicKey [64]byte, ) error { + lc.handlerMutex.Lock() + defer lc.handlerMutex.Unlock() + keep, ok := lc.keeps[keepAddress] if !ok { return fmt.Errorf( @@ -171,6 +185,44 @@ func (lc *localChain) SubmitSignature( keepAddress common.Address, signature *ecdsa.Signature, ) error { + lc.handlerMutex.Lock() + defer lc.handlerMutex.Unlock() + + keep, ok := lc.keeps[keepAddress] + if !ok { + return fmt.Errorf( + "failed to find keep with address: [%s]", + keepAddress.String(), + ) + } + + if keep.publicKey == [64]byte{} { + return fmt.Errorf( + "keep [%s] has no public key", + keepAddress.String(), + ) + } + + rBytes, err := byteutils.BytesTo32Byte(signature.R.Bytes()) + if err != nil { + return err + } + + sBytes, err := byteutils.BytesTo32Byte(signature.S.Bytes()) + if err != nil { + return err + } + + keep.signatureSubmittedEvents = append( + keep.signatureSubmittedEvents, + ð.SignatureSubmittedEvent{ + Digest: keep.latestDigest, + R: rBytes, + S: sBytes, + RecoveryID: uint8(signature.RecoveryID), + }, + ) + return nil } @@ -197,7 +249,7 @@ func (lc *localChain) IsActive(keepAddress common.Address) (bool, error) { } func (lc *localChain) BlockCounter() chain.BlockCounter { - panic("implement") + return lc.blockCounter } func (lc *localChain) IsRegisteredForApplication(application common.Address) (bool, error) { @@ -355,7 +407,15 @@ func (lc *localChain) PastSignatureSubmittedEvents( keepAddress string, startBlock uint64, ) ([]*eth.SignatureSubmittedEvent, error) { - panic("not implemented") // TODO: Implementation for unit testing purposes. + lc.handlerMutex.Lock() + defer lc.handlerMutex.Unlock() + + keep, ok := lc.keeps[common.HexToAddress(keepAddress)] + if !ok { + return nil, fmt.Errorf("no keep with address [%v]", keepAddress) + } + + return keep.signatureSubmittedEvents, nil } func (lc *localChain) BlockTimestamp(blockNumber *big.Int) (uint64, error) { diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index 621f6b033..4453a0c97 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -3,9 +3,10 @@ package local import ( "bytes" "fmt" + "math/rand" "sync" - eth "github.com/keep-network/keep-ecdsa/pkg/chain" + chain "github.com/keep-network/keep-ecdsa/pkg/chain" "github.com/ethereum/go-ethereum/common" "github.com/keep-network/keep-common/pkg/subscription" @@ -14,10 +15,21 @@ import ( type localDeposit struct { keepAddress string pubkey []byte + digest [32]byte + signature *Signature + + redemptionRequestedEvents []*chain.DepositRedemptionRequestedEvent +} + +type Signature struct { + V uint8 + R [32]uint8 + S [32]uint8 } type localChainLogger struct { - retrieveSignerPubkeyCalls int + retrieveSignerPubkeyCalls int + provideRedemptionSignatureCalls int } func (lcl *localChainLogger) logRetrieveSignerPubkeyCall() { @@ -28,6 +40,14 @@ func (lcl *localChainLogger) RetrieveSignerPubkeyCalls() int { return lcl.retrieveSignerPubkeyCalls } +func (lcl *localChainLogger) logProvideRedemptionSignatureCall() { + lcl.provideRedemptionSignatureCalls++ +} + +func (lcl *localChainLogger) ProvideRedemptionSignatureCalls() int { + return lcl.provideRedemptionSignatureCalls +} + type TBTCLocalChain struct { *localChain @@ -35,18 +55,24 @@ type TBTCLocalChain struct { logger *localChainLogger - deposits map[string]*localDeposit - depositCreatedHandlers map[int]func(depositAddress string) - depositRegisteredPubkeyHandlers map[int]func(depositAddress string) + deposits map[string]*localDeposit + depositCreatedHandlers map[int]func(depositAddress string) + depositRegisteredPubkeyHandlers map[int]func(depositAddress string) + depositRedemptionRequestedHandlers map[int]func(depositAddress string) + depositGotRedemptionSignatureHandlers map[int]func(depositAddress string) + depositRedeemedHandlers map[int]func(depositAddress string) } func NewTBTCLocalChain() *TBTCLocalChain { return &TBTCLocalChain{ - localChain: Connect().(*localChain), - logger: &localChainLogger{}, - deposits: make(map[string]*localDeposit), - depositCreatedHandlers: make(map[int]func(depositAddress string)), - depositRegisteredPubkeyHandlers: make(map[int]func(depositAddress string)), + localChain: Connect().(*localChain), + logger: &localChainLogger{}, + deposits: make(map[string]*localDeposit), + depositCreatedHandlers: make(map[int]func(depositAddress string)), + depositRegisteredPubkeyHandlers: make(map[int]func(depositAddress string)), + depositRedemptionRequestedHandlers: make(map[int]func(depositAddress string)), + depositGotRedemptionSignatureHandlers: make(map[int]func(depositAddress string)), + depositRedeemedHandlers: make(map[int]func(depositAddress string)), } } @@ -62,7 +88,8 @@ func (tlc *TBTCLocalChain) CreateDeposit(depositAddress string) { }) tlc.deposits[depositAddress] = &localDeposit{ - keepAddress: keepAddress.Hex(), + keepAddress: keepAddress.Hex(), + redemptionRequestedEvents: make([]*chain.DepositRedemptionRequestedEvent, 0), } for _, handler := range tlc.depositCreatedHandlers { @@ -108,29 +135,124 @@ func (tlc *TBTCLocalChain) OnDepositRegisteredPubkey( }), nil } +func (tlc *TBTCLocalChain) RedeemDeposit(depositAddress string) error { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + deposit, ok := tlc.deposits[depositAddress] + if !ok { + return fmt.Errorf("no deposit with address [%v]", depositAddress) + } + + if !bytes.Equal(deposit.digest[:], make([]byte, len(deposit.digest))) { + return fmt.Errorf( + "redemption of deposit [%v] already requested", + depositAddress, + ) + } + + var digest [32]byte + rand.Read(digest[:]) + + deposit.digest = digest + + err := tlc.requestSignature( + common.HexToAddress(deposit.keepAddress), + deposit.digest, + ) + if err != nil { + return err + } + + for _, handler := range tlc.depositRedemptionRequestedHandlers { + go func(handler func(depositAddress string), depositAddress string) { + handler(depositAddress) + }(handler, depositAddress) + } + + deposit.redemptionRequestedEvents = append( + deposit.redemptionRequestedEvents, + &chain.DepositRedemptionRequestedEvent{ + DepositAddress: depositAddress, + Digest: deposit.digest, + UtxoValue: nil, + RedeemerOutputScript: nil, + RequestedFee: nil, + Outpoint: nil, + BlockNumber: 0, + }, + ) + + return nil +} + func (tlc *TBTCLocalChain) OnDepositRedemptionRequested( handler func(depositAddress string), ) (subscription.EventSubscription, error) { - panic("not implemented") // TODO: Implementation for unit testing purposes. + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + handlerID := generateHandlerID() + + tlc.depositRedemptionRequestedHandlers[handlerID] = handler + + return subscription.NewEventSubscription(func() { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + delete(tlc.depositRedemptionRequestedHandlers, handlerID) + }), nil } func (tlc *TBTCLocalChain) OnDepositGotRedemptionSignature( handler func(depositAddress string), ) (subscription.EventSubscription, error) { - panic("not implemented") // TODO: Implementation for unit testing purposes. + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + handlerID := generateHandlerID() + + tlc.depositGotRedemptionSignatureHandlers[handlerID] = handler + + return subscription.NewEventSubscription(func() { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + delete(tlc.depositGotRedemptionSignatureHandlers, handlerID) + }), nil } func (tlc *TBTCLocalChain) OnDepositRedeemed( handler func(depositAddress string), ) (subscription.EventSubscription, error) { - panic("not implemented") // TODO: Implementation for unit testing purposes. + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + handlerID := generateHandlerID() + + tlc.depositRedeemedHandlers[handlerID] = handler + + return subscription.NewEventSubscription(func() { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + delete(tlc.depositRedeemedHandlers, handlerID) + }), nil } func (tlc *TBTCLocalChain) PastDepositRedemptionRequestedEvents( depositAddress string, startBlock uint64, -) ([]*eth.DepositRedemptionRequestedEvent, error) { - panic("not implemented") // TODO: Implementation for unit testing purposes. +) ([]*chain.DepositRedemptionRequestedEvent, error) { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + deposit, ok := tlc.deposits[depositAddress] + if !ok { + return nil, fmt.Errorf("no deposit with address [%v]", depositAddress) + } + + return deposit.redemptionRequestedEvents, nil } func (tlc *TBTCLocalChain) KeepAddress(depositAddress string) (string, error) { @@ -200,7 +322,36 @@ func (tlc *TBTCLocalChain) ProvideRedemptionSignature( r [32]uint8, s [32]uint8, ) error { - panic("not implemented") // TODO: Implementation for unit testing purposes. + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + tlc.logger.logProvideRedemptionSignatureCall() + + deposit, ok := tlc.deposits[depositAddress] + if !ok { + return fmt.Errorf("no deposit with address [%v]", depositAddress) + } + + if deposit.signature != nil { + return fmt.Errorf( + "redemption signature for deposit [%v] already provided", + depositAddress, + ) + } + + deposit.signature = &Signature{ + V: v, + R: r, + S: s, + } + + for _, handler := range tlc.depositGotRedemptionSignatureHandlers { + go func(handler func(depositAddress string), depositAddress string) { + handler(depositAddress) + }(handler, depositAddress) + } + + return nil } func (tlc *TBTCLocalChain) IncreaseRedemptionFee( @@ -232,6 +383,27 @@ func (tlc *TBTCLocalChain) DepositPubkey( return deposit.pubkey, nil } +func (tlc *TBTCLocalChain) DepositSignature( + depositAddress string, +) (*Signature, error) { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + deposit, ok := tlc.deposits[depositAddress] + if !ok { + return nil, fmt.Errorf("no deposit with address [%v]", depositAddress) + } + + if deposit.signature == nil { + return nil, fmt.Errorf( + "no signature for deposit [%v]", + depositAddress, + ) + } + + return deposit.signature, nil +} + func (tlc *TBTCLocalChain) Logger() *localChainLogger { return tlc.logger } diff --git a/pkg/extensions/tbtc/tbtc_test.go b/pkg/extensions/tbtc/tbtc_test.go index d8b5a9abf..133fd4775 100644 --- a/pkg/extensions/tbtc/tbtc_test.go +++ b/pkg/extensions/tbtc/tbtc_test.go @@ -4,11 +4,15 @@ import ( "bytes" "context" "fmt" + "math/big" "math/rand" "reflect" "testing" "time" + "github.com/keep-network/keep-ecdsa/pkg/ecdsa" + "github.com/keep-network/keep-ecdsa/pkg/utils/byteutils" + "github.com/ethereum/go-ethereum/common" "github.com/keep-network/keep-ecdsa/pkg/chain/local" ) @@ -367,6 +371,69 @@ func TestRetrievePubkey_ContextCancelled_WithWorkingMonitoring(t *testing.T) { } } +func TestProvideRedemptionSignature_TimeoutElapsed(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionSignature( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedProvideRedemptionSignatureCalls := 1 + actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + t.Errorf( + "unexpected number of ProvideRedemptionSignature calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedProvideRedemptionSignatureCalls, + actualProvideRedemptionSignatureCalls, + ) + } + + depositSignature, err := tbtcChain.DepositSignature(depositAddress) + if err != nil { + t.Errorf("unexpected error while fetching deposit pubkey: [%v]", err) + } + + if !areChainSignaturesEqual(keepSignature, depositSignature) { + t.Errorf( + "unexpected signature\n"+ + "expected: [%+v]\n"+ + "actual: [%+v]", + keepSignature, + depositSignature, + ) + } +} + func submitKeepPublicKey( depositAddress string, tbtcChain *local.TBTCLocalChain, @@ -390,6 +457,68 @@ func submitKeepPublicKey( return keepPubkey, nil } +func submitKeepSignature( + depositAddress string, + tbtcChain *local.TBTCLocalChain, +) (*local.Signature, error) { + keepAddress, err := tbtcChain.KeepAddress(depositAddress) + if err != nil { + return nil, err + } + + signature := &ecdsa.Signature{ + R: new(big.Int).SetUint64(rand.Uint64()), + S: new(big.Int).SetUint64(rand.Uint64()), + RecoveryID: rand.Intn(4), + } + + err = tbtcChain.SubmitSignature( + common.HexToAddress(keepAddress), + signature, + ) + if err != nil { + return nil, err + } + + return toChainSignature(signature) +} + +func toChainSignature(signature *ecdsa.Signature) (*local.Signature, error) { + v := uint8(27 + signature.RecoveryID) + + r, err := byteutils.BytesTo32Byte(signature.R.Bytes()) + if err != nil { + return nil, err + } + + s, err := byteutils.BytesTo32Byte(signature.S.Bytes()) + if err != nil { + return nil, err + } + + return &local.Signature{ + V: v, + R: r, + S: s, + }, nil +} + +func areChainSignaturesEqual(signature1, signature2 *local.Signature) bool { + if signature1.V != signature2.V { + return false + } + + if !bytes.Equal(signature1.R[:], signature2.R[:]) { + return false + } + + if !bytes.Equal(signature1.S[:], signature2.S[:]) { + return false + } + + return true +} + func closeKeep( depositAddress string, tbtcChain *local.TBTCLocalChain, From 1d4bcc19932518e4239cf223ee9ca918ade29874 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Tue, 27 Oct 2020 10:57:07 +0100 Subject: [PATCH 16/31] Add GoSec exception for local chain randomness --- pkg/chain/local/tbtc.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index 4453a0c97..d74c1ffb0 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -152,11 +152,16 @@ func (tlc *TBTCLocalChain) RedeemDeposit(depositAddress string) error { } var digest [32]byte - rand.Read(digest[:]) + // #nosec G404 (insecure random number source (rand)) + // Local chain implementation doesn't require secure randomness. + _, err := rand.Read(digest[:]) + if err != nil { + return err + } deposit.digest = digest - err := tlc.requestSignature( + err = tlc.requestSignature( common.HexToAddress(deposit.keepAddress), deposit.digest, ) From 073fcf08ba04cd2cda97b5f42d0ed4e30fac37ef Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Tue, 27 Oct 2020 12:20:16 +0100 Subject: [PATCH 17/31] Add remaining tests for provide signature action --- pkg/chain/ethereum/tbtc.go | 37 +++ pkg/chain/local/bonded_ecdsa_keep.go | 8 + pkg/chain/local/local.go | 5 +- pkg/chain/local/tbtc.go | 109 ++++++- pkg/extensions/tbtc/chain.go | 15 + pkg/extensions/tbtc/tbtc_test.go | 452 ++++++++++++++++++++++++++- 6 files changed, 608 insertions(+), 18 deletions(-) diff --git a/pkg/chain/ethereum/tbtc.go b/pkg/chain/ethereum/tbtc.go index 2242e3ec2..e681c3c7a 100644 --- a/pkg/chain/ethereum/tbtc.go +++ b/pkg/chain/ethereum/tbtc.go @@ -327,6 +327,43 @@ func (tec *TBTCEthereumChain) IncreaseRedemptionFee( return nil } +// ProvideRedemptionProof provides the redemption proof for the provided deposit. +func (tec *TBTCEthereumChain) ProvideRedemptionProof( + depositAddress string, + txVersion [4]uint8, + txInputVector []uint8, + txOutputVector []uint8, + txLocktime [4]uint8, + merkleProof []uint8, + txIndexInBlock *big.Int, + bitcoinHeaders []uint8, +) error { + deposit, err := tec.getDepositContract(depositAddress) + if err != nil { + return err + } + + transaction, err := deposit.ProvideRedemptionProof( + txVersion, + txInputVector, + txOutputVector, + txLocktime, + merkleProof, + txIndexInBlock, + bitcoinHeaders, + ) + if err != nil { + return err + } + + logger.Debugf( + "submitted ProvideRedemptionProof transaction with hash: [%x]", + transaction.Hash(), + ) + + return nil +} + func (tec *TBTCEthereumChain) getDepositContract( address string, ) (*contract.Deposit, error) { diff --git a/pkg/chain/local/bonded_ecdsa_keep.go b/pkg/chain/local/bonded_ecdsa_keep.go index 74151b359..13b604ad4 100644 --- a/pkg/chain/local/bonded_ecdsa_keep.go +++ b/pkg/chain/local/bonded_ecdsa_keep.go @@ -41,6 +41,14 @@ func (c *localChain) requestSignature(keepAddress common.Address, digest [32]byt ) } + // force the right workflow sequence + if keep.publicKey == [64]byte{} { + return fmt.Errorf( + "public key for keep [%s] is not set", + keepAddress.String(), + ) + } + keep.latestDigest = digest signatureRequestedEvent := ð.SignatureRequestedEvent{ diff --git a/pkg/chain/local/local.go b/pkg/chain/local/local.go index d36cba696..89b406222 100644 --- a/pkg/chain/local/local.go +++ b/pkg/chain/local/local.go @@ -196,9 +196,10 @@ func (lc *localChain) SubmitSignature( ) } - if keep.publicKey == [64]byte{} { + // force the right workflow sequence + if keep.latestDigest == [32]byte{} { return fmt.Errorf( - "keep [%s] has no public key", + "keep [%s] is not awaiting for a signature", keepAddress.String(), ) } diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index d74c1ffb0..683b363e6 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -3,6 +3,7 @@ package local import ( "bytes" "fmt" + "math/big" "math/rand" "sync" @@ -15,8 +16,10 @@ import ( type localDeposit struct { keepAddress string pubkey []byte - digest [32]byte - signature *Signature + + redemptionDigest [32]byte + redemptionSignature *Signature + redemptionProof *TxProof redemptionRequestedEvents []*chain.DepositRedemptionRequestedEvent } @@ -27,6 +30,8 @@ type Signature struct { S [32]uint8 } +type TxProof struct{} + type localChainLogger struct { retrieveSignerPubkeyCalls int provideRedemptionSignatureCalls int @@ -55,6 +60,8 @@ type TBTCLocalChain struct { logger *localChainLogger + alwaysFailingTransactions map[string]bool + deposits map[string]*localDeposit depositCreatedHandlers map[int]func(depositAddress string) depositRegisteredPubkeyHandlers map[int]func(depositAddress string) @@ -67,6 +74,7 @@ func NewTBTCLocalChain() *TBTCLocalChain { return &TBTCLocalChain{ localChain: Connect().(*localChain), logger: &localChainLogger{}, + alwaysFailingTransactions: make(map[string]bool), deposits: make(map[string]*localDeposit), depositCreatedHandlers: make(map[int]func(depositAddress string)), depositRegisteredPubkeyHandlers: make(map[int]func(depositAddress string)), @@ -144,26 +152,29 @@ func (tlc *TBTCLocalChain) RedeemDeposit(depositAddress string) error { return fmt.Errorf("no deposit with address [%v]", depositAddress) } - if !bytes.Equal(deposit.digest[:], make([]byte, len(deposit.digest))) { + if !bytes.Equal( + deposit.redemptionDigest[:], + make([]byte, len(deposit.redemptionDigest)), + ) { return fmt.Errorf( "redemption of deposit [%v] already requested", depositAddress, ) } - var digest [32]byte + var randomDigest [32]byte // #nosec G404 (insecure random number source (rand)) // Local chain implementation doesn't require secure randomness. - _, err := rand.Read(digest[:]) + _, err := rand.Read(randomDigest[:]) if err != nil { return err } - deposit.digest = digest + deposit.redemptionDigest = randomDigest err = tlc.requestSignature( common.HexToAddress(deposit.keepAddress), - deposit.digest, + deposit.redemptionDigest, ) if err != nil { return err @@ -179,7 +190,7 @@ func (tlc *TBTCLocalChain) RedeemDeposit(depositAddress string) error { deposit.redemptionRequestedEvents, &chain.DepositRedemptionRequestedEvent{ DepositAddress: depositAddress, - Digest: deposit.digest, + Digest: deposit.redemptionDigest, UtxoValue: nil, RedeemerOutputScript: nil, RequestedFee: nil, @@ -332,19 +343,23 @@ func (tlc *TBTCLocalChain) ProvideRedemptionSignature( tlc.logger.logProvideRedemptionSignatureCall() + if _, exists := tlc.alwaysFailingTransactions["ProvideRedemptionSignature"]; exists { + return fmt.Errorf("always failing transaction") + } + deposit, ok := tlc.deposits[depositAddress] if !ok { return fmt.Errorf("no deposit with address [%v]", depositAddress) } - if deposit.signature != nil { + if deposit.redemptionSignature != nil { return fmt.Errorf( "redemption signature for deposit [%v] already provided", depositAddress, ) } - deposit.signature = &Signature{ + deposit.redemptionSignature = &Signature{ V: v, R: r, S: s, @@ -367,6 +382,42 @@ func (tlc *TBTCLocalChain) IncreaseRedemptionFee( panic("not implemented") // TODO: Implementation for unit testing purposes. } +func (tlc *TBTCLocalChain) ProvideRedemptionProof( + depositAddress string, + txVersion [4]uint8, + txInputVector []uint8, + txOutputVector []uint8, + txLocktime [4]uint8, + merkleProof []uint8, + txIndexInBlock *big.Int, + bitcoinHeaders []uint8, +) error { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + deposit, ok := tlc.deposits[depositAddress] + if !ok { + return fmt.Errorf("no deposit with address [%v]", depositAddress) + } + + if deposit.redemptionProof != nil { + return fmt.Errorf( + "redemption proof for deposit [%v] already provided", + depositAddress, + ) + } + + deposit.redemptionProof = &TxProof{} + + for _, handler := range tlc.depositRedeemedHandlers { + go func(handler func(depositAddress string), depositAddress string) { + handler(depositAddress) + }(handler, depositAddress) + } + + return nil +} + func (tlc *TBTCLocalChain) DepositPubkey( depositAddress string, ) ([]byte, error) { @@ -388,7 +439,7 @@ func (tlc *TBTCLocalChain) DepositPubkey( return deposit.pubkey, nil } -func (tlc *TBTCLocalChain) DepositSignature( +func (tlc *TBTCLocalChain) DepositRedemptionSignature( depositAddress string, ) (*Signature, error) { tlc.mutex.Lock() @@ -399,14 +450,44 @@ func (tlc *TBTCLocalChain) DepositSignature( return nil, fmt.Errorf("no deposit with address [%v]", depositAddress) } - if deposit.signature == nil { + if deposit.redemptionSignature == nil { return nil, fmt.Errorf( - "no signature for deposit [%v]", + "no redemption signature for deposit [%v]", depositAddress, ) } - return deposit.signature, nil + return deposit.redemptionSignature, nil +} + +func (tlc *TBTCLocalChain) DepositRedemptionProof( + depositAddress string, +) (*TxProof, error) { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + deposit, ok := tlc.deposits[depositAddress] + if !ok { + return nil, fmt.Errorf("no deposit with address [%v]", depositAddress) + } + + if deposit.redemptionProof == nil { + return nil, fmt.Errorf( + "no redemption proof for deposit [%v]", + depositAddress, + ) + } + + return deposit.redemptionProof, nil +} + +func (tlc *TBTCLocalChain) SetAlwaysFailingTransactions(transactions ...string) { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + for _, tx := range transactions { + tlc.alwaysFailingTransactions[tx] = true + } } func (tlc *TBTCLocalChain) Logger() *localChainLogger { diff --git a/pkg/extensions/tbtc/chain.go b/pkg/extensions/tbtc/chain.go index 3a70bbfec..51b7a77d4 100644 --- a/pkg/extensions/tbtc/chain.go +++ b/pkg/extensions/tbtc/chain.go @@ -1,6 +1,8 @@ package tbtc import ( + "math/big" + "github.com/keep-network/keep-common/pkg/subscription" chain "github.com/keep-network/keep-ecdsa/pkg/chain" ) @@ -40,6 +42,19 @@ type Deposit interface { previousOutputValueBytes [8]uint8, newOutputValueBytes [8]uint8, ) error + + // ProvideRedemptionProof provides the redemption proof for the + // provided deposit. + ProvideRedemptionProof( + depositAddress string, + txVersion [4]uint8, + txInputVector []uint8, + txOutputVector []uint8, + txLocktime [4]uint8, + merkleProof []uint8, + txIndexInBlock *big.Int, + bitcoinHeaders []uint8, + ) error } // TBTCSystem is an interface that provides ability to interact diff --git a/pkg/extensions/tbtc/tbtc_test.go b/pkg/extensions/tbtc/tbtc_test.go index 133fd4775..648c421dc 100644 --- a/pkg/extensions/tbtc/tbtc_test.go +++ b/pkg/extensions/tbtc/tbtc_test.go @@ -418,9 +418,9 @@ func TestProvideRedemptionSignature_TimeoutElapsed(t *testing.T) { ) } - depositSignature, err := tbtcChain.DepositSignature(depositAddress) + depositSignature, err := tbtcChain.DepositRedemptionSignature(depositAddress) if err != nil { - t.Errorf("unexpected error while fetching deposit pubkey: [%v]", err) + t.Errorf("unexpected error while fetching deposit signature: [%v]", err) } if !areChainSignaturesEqual(keepSignature, depositSignature) { @@ -434,6 +434,454 @@ func TestProvideRedemptionSignature_TimeoutElapsed(t *testing.T) { } } +func TestProvideRedemptionSignature_StopEventOccurred_DepositGotRedemptionSignature(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionSignature( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // wait a while before triggering the stop event because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + // invoke the action which will trigger the stop event in result + err = tbtcChain.ProvideRedemptionSignature( + depositAddress, + keepSignature.V, + keepSignature.R, + keepSignature.S, + ) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedProvideRedemptionSignatureCalls := 1 + actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + t.Errorf( + "unexpected number of ProvideRedemptionSignature calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedProvideRedemptionSignatureCalls, + actualProvideRedemptionSignatureCalls, + ) + } + + depositSignature, err := tbtcChain.DepositRedemptionSignature(depositAddress) + if err != nil { + t.Errorf("unexpected error while fetching deposit signature: [%v]", err) + } + + if !areChainSignaturesEqual(keepSignature, depositSignature) { + t.Errorf( + "unexpected signature\n"+ + "expected: [%+v]\n"+ + "actual: [%+v]", + keepSignature, + depositSignature, + ) + } +} + +func TestProvideRedemptionSignature_StopEventOccurred_DepositRedeemed(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionSignature( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + _, err = submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // wait a while before triggering the stop event because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + // invoke the action which will trigger the stop event in result + err = tbtcChain.ProvideRedemptionProof( + depositAddress, + [4]uint8{}, + nil, + nil, + [4]uint8{}, + nil, + nil, + nil, + ) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedProvideRedemptionSignatureCalls := 0 + actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + t.Errorf( + "unexpected number of ProvideRedemptionSignature calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedProvideRedemptionSignatureCalls, + actualProvideRedemptionSignatureCalls, + ) + } + + depositProof, err := tbtcChain.DepositRedemptionProof(depositAddress) + if err != nil { + t.Errorf("unexpected error while fetching deposit proof: [%v]", err) + } + + if depositProof == nil { + t.Errorf("deposit proof should be provided") + } +} + +func TestProvideRedemptionSignature_KeepClosedEventOccurred(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionSignature( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + _, err = submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // wait a while before triggering the keep closed event because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + err = closeKeep(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedProvideRedemptionSignatureCalls := 0 + actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + t.Errorf( + "unexpected number of ProvideRedemptionSignature calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedProvideRedemptionSignatureCalls, + actualProvideRedemptionSignatureCalls, + ) + } + + _, err = tbtcChain.DepositRedemptionSignature(depositAddress) + + expectedError := fmt.Errorf( + "no redemption signature for deposit [%v]", + depositAddress, + ) + if !reflect.DeepEqual(expectedError, err) { + t.Errorf( + "unexpected error\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedError, + err, + ) + } +} + +func TestProvideRedemptionSignature_KeepTerminatedEventOccurred(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionSignature( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + _, err = submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // wait a while before triggering the keep terminated event because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + err = terminateKeep(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedProvideRedemptionSignatureCalls := 0 + actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + t.Errorf( + "unexpected number of ProvideRedemptionSignature calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedProvideRedemptionSignatureCalls, + actualProvideRedemptionSignatureCalls, + ) + } + + _, err = tbtcChain.DepositRedemptionSignature(depositAddress) + + expectedError := fmt.Errorf( + "no redemption signature for deposit [%v]", + depositAddress, + ) + if !reflect.DeepEqual(expectedError, err) { + t.Errorf( + "unexpected error\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedError, + err, + ) + } +} + +func TestProvideRedemptionSignature_ActionFailed(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionSignature( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + _, err = submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // simulate a situation when `ProvideRedemptionSignature` fails on-chain + tbtcChain.SetAlwaysFailingTransactions("ProvideRedemptionSignature") + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedProvideRedemptionSignatureCalls := 3 + actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + t.Errorf( + "unexpected number of ProvideRedemptionSignature calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedProvideRedemptionSignatureCalls, + actualProvideRedemptionSignatureCalls, + ) + } +} + +func TestProvideRedemptionSignature_ContextCancelled_WithoutWorkingMonitoring(t *testing.T) { + ctx, cancelCtx := context.WithCancel(context.Background()) + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionSignature( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + // cancel the context before any start event occurs + cancelCtx() + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedProvideRedemptionSignatureCalls := 0 + actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + t.Errorf( + "unexpected number of ProvideRedemptionSignature calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedProvideRedemptionSignatureCalls, + actualProvideRedemptionSignatureCalls, + ) + } +} + +func TestProvideRedemptionSignature_ContextCancelled_WithWorkingMonitoring(t *testing.T) { + ctx, cancelCtx := context.WithCancel(context.Background()) + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionSignature( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + // wait a while before cancelling the context because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + // cancel the context once the start event is handled and + // the monitoring process is running + cancelCtx() + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedProvideRedemptionSignatureCalls := 0 + actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + t.Errorf( + "unexpected number of ProvideRedemptionSignature calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedProvideRedemptionSignatureCalls, + actualProvideRedemptionSignatureCalls, + ) + } +} + func submitKeepPublicKey( depositAddress string, tbtcChain *local.TBTCLocalChain, From 5c55f3c371ed8eda82a6cba459494c65392668cb Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Tue, 27 Oct 2020 13:28:16 +0100 Subject: [PATCH 18/31] Happy path test for provide redemption proof --- pkg/chain/local/local.go | 37 +++++++- pkg/chain/local/tbtc.go | 140 ++++++++++++++++++++++++++++++- pkg/extensions/tbtc/tbtc_test.go | 83 ++++++++++++++++++ 3 files changed, 253 insertions(+), 7 deletions(-) diff --git a/pkg/chain/local/local.go b/pkg/chain/local/local.go index 89b406222..1531c7e26 100644 --- a/pkg/chain/local/local.go +++ b/pkg/chain/local/local.go @@ -1,6 +1,7 @@ package local import ( + "context" "fmt" "math/big" "math/rand" @@ -35,7 +36,9 @@ type Chain interface { type localChain struct { handlerMutex sync.Mutex - blockCounter chain.BlockCounter + blockCounter chain.BlockCounter + blocksTimestamps map[uint64]uint64 + blocksTimestampsMutex sync.RWMutex keepAddresses []common.Address keeps map[common.Address]*localKeep @@ -55,13 +58,33 @@ func Connect() Chain { panic(err) // should never happen } - return &localChain{ + localChain := &localChain{ blockCounter: blockCounter, + blocksTimestamps: map[uint64]uint64{0: uint64(time.Now().Unix())}, keeps: make(map[common.Address]*localKeep), keepCreatedHandlers: make(map[int]func(event *eth.BondedECDSAKeepCreatedEvent)), clientAddress: common.HexToAddress("6299496199d99941193Fdd2d717ef585F431eA05"), authorizations: make(map[common.Address]bool), } + + go localChain.observeBlocksTimestamps(context.Background()) + + return localChain +} + +func (lc *localChain) observeBlocksTimestamps(ctx context.Context) { + blockChan := lc.BlockCounter().WatchBlocks(ctx) + + for { + select { + case blockNumber := <-blockChan: + lc.blocksTimestampsMutex.Lock() + lc.blocksTimestamps[blockNumber] = uint64(time.Now().Unix()) + lc.blocksTimestampsMutex.Unlock() + case <-ctx.Done(): + return + } + } } func (lc *localChain) OpenKeep(keepAddress common.Address, members []common.Address) { @@ -420,7 +443,15 @@ func (lc *localChain) PastSignatureSubmittedEvents( } func (lc *localChain) BlockTimestamp(blockNumber *big.Int) (uint64, error) { - panic("not implemented") // TODO: Implementation for unit testing purposes. + lc.blocksTimestampsMutex.RLock() + defer lc.blocksTimestampsMutex.RUnlock() + + blockTimestamp, ok := lc.blocksTimestamps[blockNumber.Uint64()] + if !ok { + return 0, fmt.Errorf("no timestamp for block [%v]", blockNumber) + } + + return blockTimestamp, nil } func generateHandlerID() int { diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index 683b363e6..362296eab 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -2,6 +2,7 @@ package local import ( "bytes" + "encoding/binary" "fmt" "math/big" "math/rand" @@ -13,11 +14,18 @@ import ( "github.com/keep-network/keep-common/pkg/subscription" ) +const ( + defaultUTXOValue = 1000 + defaultInitialRedemptionFee = 10 +) + type localDeposit struct { keepAddress string pubkey []byte + utxoValue *big.Int redemptionDigest [32]byte + redemptionFee *big.Int redemptionSignature *Signature redemptionProof *TxProof @@ -35,6 +43,7 @@ type TxProof struct{} type localChainLogger struct { retrieveSignerPubkeyCalls int provideRedemptionSignatureCalls int + increaseRedemptionFeeCalls int } func (lcl *localChainLogger) logRetrieveSignerPubkeyCall() { @@ -53,6 +62,14 @@ func (lcl *localChainLogger) ProvideRedemptionSignatureCalls() int { return lcl.provideRedemptionSignatureCalls } +func (lcl *localChainLogger) logIncreaseRedemptionFeeCalls() { + lcl.increaseRedemptionFeeCalls++ +} + +func (lcl *localChainLogger) IncreaseRedemptionFeeCalls() int { + return lcl.increaseRedemptionFeeCalls +} + type TBTCLocalChain struct { *localChain @@ -97,6 +114,7 @@ func (tlc *TBTCLocalChain) CreateDeposit(depositAddress string) { tlc.deposits[depositAddress] = &localDeposit{ keepAddress: keepAddress.Hex(), + utxoValue: big.NewInt(defaultUTXOValue), redemptionRequestedEvents: make([]*chain.DepositRedemptionRequestedEvent, 0), } @@ -171,6 +189,7 @@ func (tlc *TBTCLocalChain) RedeemDeposit(depositAddress string) error { } deposit.redemptionDigest = randomDigest + deposit.redemptionFee = big.NewInt(defaultInitialRedemptionFee) err = tlc.requestSignature( common.HexToAddress(deposit.keepAddress), @@ -186,16 +205,21 @@ func (tlc *TBTCLocalChain) RedeemDeposit(depositAddress string) error { }(handler, depositAddress) } + currentBlock, err := tlc.BlockCounter().CurrentBlock() + if err != nil { + return err + } + deposit.redemptionRequestedEvents = append( deposit.redemptionRequestedEvents, &chain.DepositRedemptionRequestedEvent{ DepositAddress: depositAddress, Digest: deposit.redemptionDigest, - UtxoValue: nil, + UtxoValue: deposit.utxoValue, RedeemerOutputScript: nil, - RequestedFee: nil, + RequestedFee: deposit.redemptionFee, Outpoint: nil, - BlockNumber: 0, + BlockNumber: currentBlock, }, ) @@ -352,6 +376,10 @@ func (tlc *TBTCLocalChain) ProvideRedemptionSignature( return fmt.Errorf("no deposit with address [%v]", depositAddress) } + if deposit.redemptionDigest == [32]byte{} { + return fmt.Errorf("deposit [%v] is not in redemption", depositAddress) + } + if deposit.redemptionSignature != nil { return fmt.Errorf( "redemption signature for deposit [%v] already provided", @@ -379,7 +407,86 @@ func (tlc *TBTCLocalChain) IncreaseRedemptionFee( previousOutputValueBytes [8]uint8, newOutputValueBytes [8]uint8, ) error { - panic("not implemented") // TODO: Implementation for unit testing purposes. + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + tlc.logger.logIncreaseRedemptionFeeCalls() + + deposit, ok := tlc.deposits[depositAddress] + if !ok { + return fmt.Errorf("no deposit with address [%v]", depositAddress) + } + + if deposit.redemptionSignature == nil { + return fmt.Errorf( + "no redemption signature for deposit [%v]; could not increase fee", + depositAddress, + ) + } + + previousOutputValue := fromLittleEndianBytes(previousOutputValueBytes) + expectedPreviousOutputValue := new(big.Int).Sub( + deposit.utxoValue, + deposit.redemptionFee, + ) + + if expectedPreviousOutputValue.Cmp(previousOutputValue) != 0 { + return fmt.Errorf("wrong previous output value") + } + + newOutputValue := fromLittleEndianBytes(newOutputValueBytes) + + if new(big.Int).Sub(previousOutputValue, newOutputValue).Cmp( + big.NewInt(defaultInitialRedemptionFee), + ) != 0 { + return fmt.Errorf("wrong increase fee step") + } + + var randomDigest [32]byte + // #nosec G404 (insecure random number source (rand)) + // Local chain implementation doesn't require secure randomness. + _, err := rand.Read(randomDigest[:]) + if err != nil { + return err + } + + deposit.redemptionDigest = randomDigest + deposit.redemptionFee = new(big.Int).Sub(deposit.utxoValue, newOutputValue) + deposit.redemptionSignature = nil + + err = tlc.requestSignature( + common.HexToAddress(deposit.keepAddress), + deposit.redemptionDigest, + ) + if err != nil { + return err + } + + for _, handler := range tlc.depositRedemptionRequestedHandlers { + go func(handler func(depositAddress string), depositAddress string) { + handler(depositAddress) + }(handler, depositAddress) + } + + currentBlock, err := tlc.BlockCounter().CurrentBlock() + if err != nil { + return err + } + + deposit.redemptionRequestedEvents = append( + deposit.redemptionRequestedEvents, + &chain.DepositRedemptionRequestedEvent{ + DepositAddress: depositAddress, + Digest: deposit.redemptionDigest, + UtxoValue: deposit.utxoValue, + RedeemerOutputScript: nil, + RequestedFee: deposit.redemptionFee, + Outpoint: nil, + BlockNumber: currentBlock, + }, + ) + + return nil } func (tlc *TBTCLocalChain) ProvideRedemptionProof( @@ -481,6 +588,27 @@ func (tlc *TBTCLocalChain) DepositRedemptionProof( return deposit.redemptionProof, nil } +func (tlc *TBTCLocalChain) DepositRedemptionFee( + depositAddress string, +) (*big.Int, error) { + tlc.mutex.Lock() + defer tlc.mutex.Unlock() + + deposit, ok := tlc.deposits[depositAddress] + if !ok { + return nil, fmt.Errorf("no deposit with address [%v]", depositAddress) + } + + if deposit.redemptionFee == nil { + return nil, fmt.Errorf( + "no redemption fee for deposit [%v]", + depositAddress, + ) + } + + return deposit.redemptionFee, nil +} + func (tlc *TBTCLocalChain) SetAlwaysFailingTransactions(transactions ...string) { tlc.mutex.Lock() defer tlc.mutex.Unlock() @@ -493,3 +621,7 @@ func (tlc *TBTCLocalChain) SetAlwaysFailingTransactions(transactions ...string) func (tlc *TBTCLocalChain) Logger() *localChainLogger { return tlc.logger } + +func fromLittleEndianBytes(bytes [8]byte) *big.Int { + return new(big.Int).SetUint64(binary.LittleEndian.Uint64(bytes[:])) +} diff --git a/pkg/extensions/tbtc/tbtc_test.go b/pkg/extensions/tbtc/tbtc_test.go index 648c421dc..59705e586 100644 --- a/pkg/extensions/tbtc/tbtc_test.go +++ b/pkg/extensions/tbtc/tbtc_test.go @@ -882,6 +882,89 @@ func TestProvideRedemptionSignature_ContextCancelled_WithWorkingMonitoring(t *te } } +func TestProvideRedemptionProof_TimeoutElapsed(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionProof( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + initialDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee(depositAddress) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.ProvideRedemptionSignature( + depositAddress, + keepSignature.V, + keepSignature.R, + keepSignature.S, + ) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedIncreaseRedemptionFeeCalls := 1 + actualIncreaseRedemptionFeeCalls := tbtcChain.Logger().IncreaseRedemptionFeeCalls() + if expectedIncreaseRedemptionFeeCalls != actualIncreaseRedemptionFeeCalls { + t.Errorf( + "unexpected number of IncreaseRedemptionFee calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedIncreaseRedemptionFeeCalls, + actualIncreaseRedemptionFeeCalls, + ) + } + + expectedDepositRedemptionFee := new(big.Int).Mul( + big.NewInt(2), + initialDepositRedemptionFee, + ) + + actualDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee(depositAddress) + if err != nil { + t.Fatal(err) + } + + if expectedDepositRedemptionFee.Cmp(actualDepositRedemptionFee) != 0 { + t.Errorf( + "unexpected redemption fee value\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedDepositRedemptionFee.Text(10), + actualDepositRedemptionFee.Text(10), + ) + } +} + func submitKeepPublicKey( depositAddress string, tbtcChain *local.TBTCLocalChain, From 46e1780dd9cfdeab22089abccf3d97f57442b59c Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Tue, 27 Oct 2020 13:48:28 +0100 Subject: [PATCH 19/31] Fix failing unit tests --- pkg/chain/local/bonded_ecdsa_keep_test.go | 20 +++++++++++++++++++- pkg/chain/local/local_test.go | 11 ++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/pkg/chain/local/bonded_ecdsa_keep_test.go b/pkg/chain/local/bonded_ecdsa_keep_test.go index 40ec15b96..8ed9a5862 100644 --- a/pkg/chain/local/bonded_ecdsa_keep_test.go +++ b/pkg/chain/local/bonded_ecdsa_keep_test.go @@ -4,12 +4,13 @@ import ( "bytes" "context" "fmt" + "math/rand" "reflect" "testing" "time" "github.com/ethereum/go-ethereum/common" - "github.com/keep-network/keep-ecdsa/pkg/chain" + eth "github.com/keep-network/keep-ecdsa/pkg/chain" ) func TestRequestSignatureNonexistentKeep(t *testing.T) { @@ -39,6 +40,14 @@ func TestRequestSignatureNoHandler(t *testing.T) { t.Fatal(err) } + var keepPubkey [64]byte + rand.Read(keepPubkey[:]) + + err = chain.SubmitKeepPublicKey(keepAddress, keepPubkey) + if err != nil { + t.Fatal(err) + } + err = chain.requestSignature(keepAddress, digest) if err != nil { t.Fatal(err) @@ -61,6 +70,15 @@ func TestRequestSignature(t *testing.T) { if err != nil { t.Fatal(err) } + + var keepPubkey [64]byte + rand.Read(keepPubkey[:]) + + err = chain.SubmitKeepPublicKey(keepAddress, keepPubkey) + if err != nil { + t.Fatal(err) + } + chain.keeps[keepAddress].signatureRequestedHandlers[0] = handler err = chain.requestSignature(keepAddress, digest) diff --git a/pkg/chain/local/local_test.go b/pkg/chain/local/local_test.go index f6e1a36a5..7c2646f4f 100644 --- a/pkg/chain/local/local_test.go +++ b/pkg/chain/local/local_test.go @@ -3,12 +3,13 @@ package local import ( "context" "fmt" + "math/rand" "reflect" "testing" "time" "github.com/ethereum/go-ethereum/common" - "github.com/keep-network/keep-ecdsa/pkg/chain" + eth "github.com/keep-network/keep-ecdsa/pkg/chain" ) func TestOnBondedECDSAKeepCreated(t *testing.T) { @@ -62,6 +63,14 @@ func TestOnSignatureRequested(t *testing.T) { t.Fatal(err) } + var keepPubkey [64]byte + rand.Read(keepPubkey[:]) + + err = chain.SubmitKeepPublicKey(keepAddress, keepPubkey) + if err != nil { + t.Fatal(err) + } + subscription, err := chain.OnSignatureRequested( keepAddress, func(event *eth.SignatureRequestedEvent) { From fa9e0341bc67b176defdcd43233624c3cff9bb25 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Tue, 27 Oct 2020 14:14:38 +0100 Subject: [PATCH 20/31] Add remaining tests for provide proof action --- pkg/chain/local/tbtc.go | 6 +- pkg/extensions/tbtc/tbtc_test.go | 709 +++++++++++++++++++++++++++++-- 2 files changed, 675 insertions(+), 40 deletions(-) diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index 362296eab..ae1504ea3 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -22,8 +22,8 @@ const ( type localDeposit struct { keepAddress string pubkey []byte - utxoValue *big.Int + utxoValue *big.Int redemptionDigest [32]byte redemptionFee *big.Int redemptionSignature *Signature @@ -412,6 +412,10 @@ func (tlc *TBTCLocalChain) IncreaseRedemptionFee( tlc.logger.logIncreaseRedemptionFeeCalls() + if _, exists := tlc.alwaysFailingTransactions["IncreaseRedemptionFee"]; exists { + return fmt.Errorf("always failing transaction") + } + deposit, ok := tlc.deposits[depositAddress] if !ok { return fmt.Errorf("no deposit with address [%v]", depositAddress) diff --git a/pkg/extensions/tbtc/tbtc_test.go b/pkg/extensions/tbtc/tbtc_test.go index 59705e586..432e7ade0 100644 --- a/pkg/extensions/tbtc/tbtc_test.go +++ b/pkg/extensions/tbtc/tbtc_test.go @@ -48,7 +48,8 @@ func TestRetrievePubkey_TimeoutElapsed(t *testing.T) { time.Sleep(2 * timeout) expectedRetrieveSignerPubkeyCalls := 1 - actualRetrieveSignerPubkeyCalls := tbtcChain.Logger().RetrieveSignerPubkeyCalls() + actualRetrieveSignerPubkeyCalls := tbtcChain.Logger(). + RetrieveSignerPubkeyCalls() if expectedRetrieveSignerPubkeyCalls != actualRetrieveSignerPubkeyCalls { t.Errorf( "unexpected number of RetrieveSignerPubkey calls\n"+ @@ -61,7 +62,10 @@ func TestRetrievePubkey_TimeoutElapsed(t *testing.T) { depositPubkey, err := tbtcChain.DepositPubkey(depositAddress) if err != nil { - t.Errorf("unexpected error while fetching deposit pubkey: [%v]", err) + t.Errorf( + "unexpected error while fetching deposit pubkey: [%v]", + err, + ) } if !bytes.Equal(keepPubkey[:], depositPubkey) { @@ -111,7 +115,8 @@ func TestRetrievePubkey_StopEventOccurred(t *testing.T) { time.Sleep(2 * timeout) expectedRetrieveSignerPubkeyCalls := 1 - actualRetrieveSignerPubkeyCalls := tbtcChain.Logger().RetrieveSignerPubkeyCalls() + actualRetrieveSignerPubkeyCalls := tbtcChain.Logger(). + RetrieveSignerPubkeyCalls() if expectedRetrieveSignerPubkeyCalls != actualRetrieveSignerPubkeyCalls { t.Errorf( "unexpected number of RetrieveSignerPubkey calls\n"+ @@ -124,7 +129,10 @@ func TestRetrievePubkey_StopEventOccurred(t *testing.T) { depositPubkey, err := tbtcChain.DepositPubkey(depositAddress) if err != nil { - t.Errorf("unexpected error while fetching deposit pubkey: [%v]", err) + t.Errorf( + "unexpected error while fetching deposit pubkey: [%v]", + err, + ) } if !bytes.Equal(keepPubkey[:], depositPubkey) { @@ -173,7 +181,8 @@ func TestRetrievePubkey_KeepClosedEventOccurred(t *testing.T) { time.Sleep(2 * timeout) expectedRetrieveSignerPubkeyCalls := 0 - actualRetrieveSignerPubkeyCalls := tbtcChain.Logger().RetrieveSignerPubkeyCalls() + actualRetrieveSignerPubkeyCalls := tbtcChain.Logger(). + RetrieveSignerPubkeyCalls() if expectedRetrieveSignerPubkeyCalls != actualRetrieveSignerPubkeyCalls { t.Errorf( "unexpected number of RetrieveSignerPubkey calls\n"+ @@ -186,7 +195,10 @@ func TestRetrievePubkey_KeepClosedEventOccurred(t *testing.T) { _, err = tbtcChain.DepositPubkey(depositAddress) - expectedError := fmt.Errorf("no pubkey for deposit [%v]", depositAddress) + expectedError := fmt.Errorf( + "no pubkey for deposit [%v]", + depositAddress, + ) if !reflect.DeepEqual(expectedError, err) { t.Errorf( "unexpected error\n"+ @@ -233,7 +245,8 @@ func TestRetrievePubkey_KeepTerminatedEventOccurred(t *testing.T) { time.Sleep(2 * timeout) expectedRetrieveSignerPubkeyCalls := 0 - actualRetrieveSignerPubkeyCalls := tbtcChain.Logger().RetrieveSignerPubkeyCalls() + actualRetrieveSignerPubkeyCalls := tbtcChain.Logger(). + RetrieveSignerPubkeyCalls() if expectedRetrieveSignerPubkeyCalls != actualRetrieveSignerPubkeyCalls { t.Errorf( "unexpected number of RetrieveSignerPubkey calls\n"+ @@ -246,7 +259,10 @@ func TestRetrievePubkey_KeepTerminatedEventOccurred(t *testing.T) { _, err = tbtcChain.DepositPubkey(depositAddress) - expectedError := fmt.Errorf("no pubkey for deposit [%v]", depositAddress) + expectedError := fmt.Errorf( + "no pubkey for deposit [%v]", + depositAddress, + ) if !reflect.DeepEqual(expectedError, err) { t.Errorf( "unexpected error\n"+ @@ -282,7 +298,8 @@ func TestRetrievePubkey_ActionFailed(t *testing.T) { time.Sleep(2 * timeout) expectedRetrieveSignerPubkeyCalls := 3 - actualRetrieveSignerPubkeyCalls := tbtcChain.Logger().RetrieveSignerPubkeyCalls() + actualRetrieveSignerPubkeyCalls := tbtcChain.Logger(). + RetrieveSignerPubkeyCalls() if expectedRetrieveSignerPubkeyCalls != actualRetrieveSignerPubkeyCalls { t.Errorf( "unexpected number of RetrieveSignerPubkey calls\n"+ @@ -318,7 +335,8 @@ func TestRetrievePubkey_ContextCancelled_WithoutWorkingMonitoring(t *testing.T) time.Sleep(2 * timeout) expectedRetrieveSignerPubkeyCalls := 0 - actualRetrieveSignerPubkeyCalls := tbtcChain.Logger().RetrieveSignerPubkeyCalls() + actualRetrieveSignerPubkeyCalls := tbtcChain.Logger(). + RetrieveSignerPubkeyCalls() if expectedRetrieveSignerPubkeyCalls != actualRetrieveSignerPubkeyCalls { t.Errorf( "unexpected number of RetrieveSignerPubkey calls\n"+ @@ -359,8 +377,10 @@ func TestRetrievePubkey_ContextCancelled_WithWorkingMonitoring(t *testing.T) { time.Sleep(2 * timeout) expectedRetrieveSignerPubkeyCalls := 0 - actualRetrieveSignerPubkeyCalls := tbtcChain.Logger().RetrieveSignerPubkeyCalls() - if expectedRetrieveSignerPubkeyCalls != actualRetrieveSignerPubkeyCalls { + actualRetrieveSignerPubkeyCalls := tbtcChain.Logger(). + RetrieveSignerPubkeyCalls() + if expectedRetrieveSignerPubkeyCalls != + actualRetrieveSignerPubkeyCalls { t.Errorf( "unexpected number of RetrieveSignerPubkey calls\n"+ "expected: [%v]\n"+ @@ -407,8 +427,10 @@ func TestProvideRedemptionSignature_TimeoutElapsed(t *testing.T) { time.Sleep(2 * timeout) expectedProvideRedemptionSignatureCalls := 1 - actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() - if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + actualProvideRedemptionSignatureCalls := tbtcChain.Logger(). + ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != + actualProvideRedemptionSignatureCalls { t.Errorf( "unexpected number of ProvideRedemptionSignature calls\n"+ "expected: [%v]\n"+ @@ -418,9 +440,14 @@ func TestProvideRedemptionSignature_TimeoutElapsed(t *testing.T) { ) } - depositSignature, err := tbtcChain.DepositRedemptionSignature(depositAddress) + depositSignature, err := tbtcChain.DepositRedemptionSignature( + depositAddress, + ) if err != nil { - t.Errorf("unexpected error while fetching deposit signature: [%v]", err) + t.Errorf( + "unexpected error while fetching deposit signature: [%v]", + err, + ) } if !areChainSignaturesEqual(keepSignature, depositSignature) { @@ -434,7 +461,9 @@ func TestProvideRedemptionSignature_TimeoutElapsed(t *testing.T) { } } -func TestProvideRedemptionSignature_StopEventOccurred_DepositGotRedemptionSignature(t *testing.T) { +func TestProvideRedemptionSignature_StopEventOccurred_DepositGotRedemptionSignature( + t *testing.T, +) { ctx := context.Background() tbtcChain := local.NewTBTCLocalChain() tbtc := newTBTC(tbtcChain) @@ -485,8 +514,10 @@ func TestProvideRedemptionSignature_StopEventOccurred_DepositGotRedemptionSignat time.Sleep(2 * timeout) expectedProvideRedemptionSignatureCalls := 1 - actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() - if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + actualProvideRedemptionSignatureCalls := tbtcChain.Logger(). + ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != + actualProvideRedemptionSignatureCalls { t.Errorf( "unexpected number of ProvideRedemptionSignature calls\n"+ "expected: [%v]\n"+ @@ -496,9 +527,14 @@ func TestProvideRedemptionSignature_StopEventOccurred_DepositGotRedemptionSignat ) } - depositSignature, err := tbtcChain.DepositRedemptionSignature(depositAddress) + depositSignature, err := tbtcChain.DepositRedemptionSignature( + depositAddress, + ) if err != nil { - t.Errorf("unexpected error while fetching deposit signature: [%v]", err) + t.Errorf( + "unexpected error while fetching deposit signature: [%v]", + err, + ) } if !areChainSignaturesEqual(keepSignature, depositSignature) { @@ -512,7 +548,9 @@ func TestProvideRedemptionSignature_StopEventOccurred_DepositGotRedemptionSignat } } -func TestProvideRedemptionSignature_StopEventOccurred_DepositRedeemed(t *testing.T) { +func TestProvideRedemptionSignature_StopEventOccurred_DepositRedeemed( + t *testing.T, +) { ctx := context.Background() tbtcChain := local.NewTBTCLocalChain() tbtc := newTBTC(tbtcChain) @@ -567,8 +605,10 @@ func TestProvideRedemptionSignature_StopEventOccurred_DepositRedeemed(t *testing time.Sleep(2 * timeout) expectedProvideRedemptionSignatureCalls := 0 - actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() - if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + actualProvideRedemptionSignatureCalls := tbtcChain.Logger(). + ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != + actualProvideRedemptionSignatureCalls { t.Errorf( "unexpected number of ProvideRedemptionSignature calls\n"+ "expected: [%v]\n"+ @@ -633,8 +673,10 @@ func TestProvideRedemptionSignature_KeepClosedEventOccurred(t *testing.T) { time.Sleep(2 * timeout) expectedProvideRedemptionSignatureCalls := 0 - actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() - if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + actualProvideRedemptionSignatureCalls := tbtcChain.Logger(). + ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != + actualProvideRedemptionSignatureCalls { t.Errorf( "unexpected number of ProvideRedemptionSignature calls\n"+ "expected: [%v]\n"+ @@ -706,8 +748,10 @@ func TestProvideRedemptionSignature_KeepTerminatedEventOccurred(t *testing.T) { time.Sleep(2 * timeout) expectedProvideRedemptionSignatureCalls := 0 - actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() - if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + actualProvideRedemptionSignatureCalls := tbtcChain.Logger(). + ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != + actualProvideRedemptionSignatureCalls { t.Errorf( "unexpected number of ProvideRedemptionSignature calls\n"+ "expected: [%v]\n"+ @@ -773,8 +817,10 @@ func TestProvideRedemptionSignature_ActionFailed(t *testing.T) { time.Sleep(2 * timeout) expectedProvideRedemptionSignatureCalls := 3 - actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() - if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + actualProvideRedemptionSignatureCalls := tbtcChain.Logger(). + ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != + actualProvideRedemptionSignatureCalls { t.Errorf( "unexpected number of ProvideRedemptionSignature calls\n"+ "expected: [%v]\n"+ @@ -785,7 +831,9 @@ func TestProvideRedemptionSignature_ActionFailed(t *testing.T) { } } -func TestProvideRedemptionSignature_ContextCancelled_WithoutWorkingMonitoring(t *testing.T) { +func TestProvideRedemptionSignature_ContextCancelled_WithoutWorkingMonitoring( + t *testing.T, +) { ctx, cancelCtx := context.WithCancel(context.Background()) tbtcChain := local.NewTBTCLocalChain() tbtc := newTBTC(tbtcChain) @@ -819,8 +867,10 @@ func TestProvideRedemptionSignature_ContextCancelled_WithoutWorkingMonitoring(t time.Sleep(2 * timeout) expectedProvideRedemptionSignatureCalls := 0 - actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() - if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + actualProvideRedemptionSignatureCalls := tbtcChain.Logger(). + ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != + actualProvideRedemptionSignatureCalls { t.Errorf( "unexpected number of ProvideRedemptionSignature calls\n"+ "expected: [%v]\n"+ @@ -831,7 +881,9 @@ func TestProvideRedemptionSignature_ContextCancelled_WithoutWorkingMonitoring(t } } -func TestProvideRedemptionSignature_ContextCancelled_WithWorkingMonitoring(t *testing.T) { +func TestProvideRedemptionSignature_ContextCancelled_WithWorkingMonitoring( + t *testing.T, +) { ctx, cancelCtx := context.WithCancel(context.Background()) tbtcChain := local.NewTBTCLocalChain() tbtc := newTBTC(tbtcChain) @@ -870,8 +922,10 @@ func TestProvideRedemptionSignature_ContextCancelled_WithWorkingMonitoring(t *te time.Sleep(2 * timeout) expectedProvideRedemptionSignatureCalls := 0 - actualProvideRedemptionSignatureCalls := tbtcChain.Logger().ProvideRedemptionSignatureCalls() - if expectedProvideRedemptionSignatureCalls != actualProvideRedemptionSignatureCalls { + actualProvideRedemptionSignatureCalls := tbtcChain.Logger(). + ProvideRedemptionSignatureCalls() + if expectedProvideRedemptionSignatureCalls != + actualProvideRedemptionSignatureCalls { t.Errorf( "unexpected number of ProvideRedemptionSignature calls\n"+ "expected: [%v]\n"+ @@ -908,7 +962,99 @@ func TestProvideRedemptionProof_TimeoutElapsed(t *testing.T) { t.Fatal(err) } - initialDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee(depositAddress) + initialDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee( + depositAddress, + ) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.ProvideRedemptionSignature( + depositAddress, + keepSignature.V, + keepSignature.R, + keepSignature.S, + ) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedIncreaseRedemptionFeeCalls := 1 + actualIncreaseRedemptionFeeCalls := tbtcChain.Logger(). + IncreaseRedemptionFeeCalls() + if expectedIncreaseRedemptionFeeCalls != actualIncreaseRedemptionFeeCalls { + t.Errorf( + "unexpected number of IncreaseRedemptionFee calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedIncreaseRedemptionFeeCalls, + actualIncreaseRedemptionFeeCalls, + ) + } + + expectedDepositRedemptionFee := new(big.Int).Mul( + big.NewInt(2), + initialDepositRedemptionFee, + ) + + actualDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee( + depositAddress, + ) + if err != nil { + t.Fatal(err) + } + + if expectedDepositRedemptionFee.Cmp(actualDepositRedemptionFee) != 0 { + t.Errorf( + "unexpected redemption fee value\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedDepositRedemptionFee.Text(10), + actualDepositRedemptionFee.Text(10), + ) + } +} + +func TestProvideRedemptionProof_StopEventOccurred_DepositRedemptionRequested( + t *testing.T, +) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionProof( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + initialDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee( + depositAddress, + ) if err != nil { t.Fatal(err) } @@ -928,12 +1074,27 @@ func TestProvideRedemptionProof_TimeoutElapsed(t *testing.T) { t.Fatal(err) } + // wait a while before triggering the stop event because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + // invoke the action which will trigger the stop event in result + err = tbtcChain.IncreaseRedemptionFee( + depositAddress, + toLittleEndianBytes(big.NewInt(990)), + toLittleEndianBytes(big.NewInt(980)), + ) + if err != nil { + t.Fatal(err) + } + // wait a bit longer than the monitoring timeout // to make sure the potential transaction completes time.Sleep(2 * timeout) expectedIncreaseRedemptionFeeCalls := 1 - actualIncreaseRedemptionFeeCalls := tbtcChain.Logger().IncreaseRedemptionFeeCalls() + actualIncreaseRedemptionFeeCalls := tbtcChain.Logger(). + IncreaseRedemptionFeeCalls() if expectedIncreaseRedemptionFeeCalls != actualIncreaseRedemptionFeeCalls { t.Errorf( "unexpected number of IncreaseRedemptionFee calls\n"+ @@ -949,7 +1110,9 @@ func TestProvideRedemptionProof_TimeoutElapsed(t *testing.T) { initialDepositRedemptionFee, ) - actualDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee(depositAddress) + actualDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee( + depositAddress, + ) if err != nil { t.Fatal(err) } @@ -965,6 +1128,474 @@ func TestProvideRedemptionProof_TimeoutElapsed(t *testing.T) { } } +func TestProvideRedemptionProof_StopEventOccurred_DepositRedeemed( + t *testing.T, +) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionProof( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.ProvideRedemptionSignature( + depositAddress, + keepSignature.V, + keepSignature.R, + keepSignature.S, + ) + if err != nil { + t.Fatal(err) + } + + // wait a while before triggering the stop event because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + // invoke the action which will trigger the stop event in result + err = tbtcChain.ProvideRedemptionProof( + depositAddress, + [4]uint8{}, + nil, + nil, + [4]uint8{}, + nil, + nil, + nil, + ) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedIncreaseRedemptionFeeCalls := 0 + actualIncreaseRedemptionFeeCalls := tbtcChain.Logger(). + IncreaseRedemptionFeeCalls() + if expectedIncreaseRedemptionFeeCalls != actualIncreaseRedemptionFeeCalls { + t.Errorf( + "unexpected number of IncreaseRedemptionFee calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedIncreaseRedemptionFeeCalls, + actualIncreaseRedemptionFeeCalls, + ) + } + + depositProof, err := tbtcChain.DepositRedemptionProof(depositAddress) + if err != nil { + t.Errorf("unexpected error while fetching deposit proof: [%v]", err) + } + + if depositProof == nil { + t.Errorf("deposit proof should be provided") + } +} + +func TestProvideRedemptionProof_KeepClosedEventOccurred(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionProof( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + initialDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee( + depositAddress, + ) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.ProvideRedemptionSignature( + depositAddress, + keepSignature.V, + keepSignature.R, + keepSignature.S, + ) + if err != nil { + t.Fatal(err) + } + + // wait a while before triggering the keep closed event because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + err = closeKeep(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedIncreaseRedemptionFeeCalls := 0 + actualIncreaseRedemptionFeeCalls := tbtcChain.Logger(). + IncreaseRedemptionFeeCalls() + if expectedIncreaseRedemptionFeeCalls != actualIncreaseRedemptionFeeCalls { + t.Errorf( + "unexpected number of IncreaseRedemptionFee calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedIncreaseRedemptionFeeCalls, + actualIncreaseRedemptionFeeCalls, + ) + } + + actualDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee( + depositAddress, + ) + if err != nil { + t.Fatal(err) + } + + if initialDepositRedemptionFee.Cmp(actualDepositRedemptionFee) != 0 { + t.Errorf( + "unexpected redemption fee value\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + initialDepositRedemptionFee.Text(10), + actualDepositRedemptionFee.Text(10), + ) + } +} + +func TestProvideRedemptionProof_KeepTerminatedEventOccurred(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionProof( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + initialDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee( + depositAddress, + ) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.ProvideRedemptionSignature( + depositAddress, + keepSignature.V, + keepSignature.R, + keepSignature.S, + ) + if err != nil { + t.Fatal(err) + } + + // wait a while before triggering the keep terminated event because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + err = terminateKeep(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedIncreaseRedemptionFeeCalls := 0 + actualIncreaseRedemptionFeeCalls := tbtcChain.Logger(). + IncreaseRedemptionFeeCalls() + if expectedIncreaseRedemptionFeeCalls != actualIncreaseRedemptionFeeCalls { + t.Errorf( + "unexpected number of IncreaseRedemptionFee calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedIncreaseRedemptionFeeCalls, + actualIncreaseRedemptionFeeCalls, + ) + } + + actualDepositRedemptionFee, err := tbtcChain.DepositRedemptionFee( + depositAddress, + ) + if err != nil { + t.Fatal(err) + } + + if initialDepositRedemptionFee.Cmp(actualDepositRedemptionFee) != 0 { + t.Errorf( + "unexpected redemption fee value\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + initialDepositRedemptionFee.Text(10), + actualDepositRedemptionFee.Text(10), + ) + } +} + +func TestProvideRedemptionProof_ActionFailed(t *testing.T) { + ctx := context.Background() + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionProof( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.ProvideRedemptionSignature( + depositAddress, + keepSignature.V, + keepSignature.R, + keepSignature.S, + ) + if err != nil { + t.Fatal(err) + } + + // simulate a situation when `IncreaseRedemptionFee` fails on-chain + tbtcChain.SetAlwaysFailingTransactions("IncreaseRedemptionFee") + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedIncreaseRedemptionFeeCalls := 3 + actualIncreaseRedemptionFeeCalls := tbtcChain.Logger(). + IncreaseRedemptionFeeCalls() + if expectedIncreaseRedemptionFeeCalls != actualIncreaseRedemptionFeeCalls { + t.Errorf( + "unexpected number of IncreaseRedemptionFee calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedIncreaseRedemptionFeeCalls, + actualIncreaseRedemptionFeeCalls, + ) + } +} + +func TestProvideRedemptionProof_ContextCancelled_WithoutWorkingMonitoring( + t *testing.T, +) { + ctx, cancelCtx := context.WithCancel(context.Background()) + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionProof( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + // cancel the context before any start event occurs + cancelCtx() + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.ProvideRedemptionSignature( + depositAddress, + keepSignature.V, + keepSignature.R, + keepSignature.S, + ) + if err != nil { + t.Fatal(err) + } + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedIncreaseRedemptionFeeCalls := 0 + actualIncreaseRedemptionFeeCalls := tbtcChain.Logger(). + IncreaseRedemptionFeeCalls() + if expectedIncreaseRedemptionFeeCalls != actualIncreaseRedemptionFeeCalls { + t.Errorf( + "unexpected number of IncreaseRedemptionFee calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedIncreaseRedemptionFeeCalls, + actualIncreaseRedemptionFeeCalls, + ) + } +} + +func TestProvideRedemptionProof_ContextCancelled_WithWorkingMonitoring( + t *testing.T, +) { + ctx, cancelCtx := context.WithCancel(context.Background()) + tbtcChain := local.NewTBTCLocalChain() + tbtc := newTBTC(tbtcChain) + + err := tbtc.monitorProvideRedemptionProof( + ctx, + constantBackoff, + timeout, + ) + if err != nil { + t.Fatal(err) + } + + tbtcChain.CreateDeposit(depositAddress) + + _, err = submitKeepPublicKey(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.RedeemDeposit(depositAddress) + if err != nil { + t.Fatal(err) + } + + keepSignature, err := submitKeepSignature(depositAddress, tbtcChain) + if err != nil { + t.Fatal(err) + } + + err = tbtcChain.ProvideRedemptionSignature( + depositAddress, + keepSignature.V, + keepSignature.R, + keepSignature.S, + ) + if err != nil { + t.Fatal(err) + } + + // wait a while before cancelling the context because the + // extension must have time to handle the start event + time.Sleep(100 * time.Millisecond) + + // cancel the context once the start event is handled and + // the monitoring process is running + cancelCtx() + + // wait a bit longer than the monitoring timeout + // to make sure the potential transaction completes + time.Sleep(2 * timeout) + + expectedIncreaseRedemptionFeeCalls := 0 + actualIncreaseRedemptionFeeCalls := tbtcChain.Logger(). + IncreaseRedemptionFeeCalls() + if expectedIncreaseRedemptionFeeCalls != actualIncreaseRedemptionFeeCalls { + t.Errorf( + "unexpected number of IncreaseRedemptionFee calls\n"+ + "expected: [%v]\n"+ + "actual: [%v]", + expectedIncreaseRedemptionFeeCalls, + actualIncreaseRedemptionFeeCalls, + ) + } +} + func submitKeepPublicKey( depositAddress string, tbtcChain *local.TBTCLocalChain, From a9a5a8d0d223cfe95be1339fab078cc65b3c86b3 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Thu, 29 Oct 2020 11:02:39 +0100 Subject: [PATCH 21/31] Tidy the go.sum file --- go.sum | 2 -- 1 file changed, 2 deletions(-) diff --git a/go.sum b/go.sum index af86097a1..226695f3d 100644 --- a/go.sum +++ b/go.sum @@ -337,8 +337,6 @@ github.com/keep-network/keep-common v1.2.1-0.20201020114759-19c123cbd4f4 h1:Civu github.com/keep-network/keep-common v1.2.1-0.20201020114759-19c123cbd4f4/go.mod h1:emxogTbBdey7M3jOzfxZOdfn139kN2mI2b2wA6AHKKo= github.com/keep-network/keep-core v1.3.0 h1:7Tb33EmO/ntHOEbOiYciRlBhqu5Ln6KemWCaYK0Z6LA= github.com/keep-network/keep-core v1.3.0/go.mod h1:1KsSSTQoN754TrFLW7kLy50pOG2CQ4BOfnJqdvEG7FA= -github.com/keep-network/tbtc v1.1.1-0.20201023121153-c4c21e8404c5 h1:eETyOKkTyO51URqSoXsaMe0VYB1AiQGBnlBEXX/XHOY= -github.com/keep-network/tbtc v1.1.1-0.20201023121153-c4c21e8404c5/go.mod h1:igBF2MPTFkzOdZ3gcwt8h0Zb5pZaHnij/iPZoMB9IKM= github.com/keep-network/tbtc v1.1.1-0.20201026093513-cb9246987718 h1:/ZNMBY7y6hfzCYA8mgtHnspGO26OmWV3sDehyGnqRyY= github.com/keep-network/tbtc v1.1.1-0.20201026093513-cb9246987718/go.mod h1:igBF2MPTFkzOdZ3gcwt8h0Zb5pZaHnij/iPZoMB9IKM= github.com/keep-network/toml v0.3.0 h1:G+NJwWR/ZiORqeLBsDXDchYoL29PXHdxOPcCueA7ctE= From 909ea901b614bd66ef8d017c17ea1dd3f7b0d71e Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Thu, 29 Oct 2020 11:04:13 +0100 Subject: [PATCH 22/31] Add comment to `BlockTimestamp` method --- pkg/chain/chain.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/chain/chain.go b/pkg/chain/chain.go index d5118854a..5036c7bd4 100644 --- a/pkg/chain/chain.go +++ b/pkg/chain/chain.go @@ -21,6 +21,7 @@ type Handle interface { // BlockCounter returns a block counter. BlockCounter() chain.BlockCounter // BlockTimestamp returns given block's timestamp. + // In case the block is not yet mined, an error should be returned. BlockTimestamp(blockNumber *big.Int) (uint64, error) BondedECDSAKeepFactory From d507bf58bff99cd04b1cd27e5af0e8e3187c9d80 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Thu, 29 Oct 2020 11:32:48 +0100 Subject: [PATCH 23/31] Add TODO about rate limiter --- pkg/chain/ethereum/connect.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/chain/ethereum/connect.go b/pkg/chain/ethereum/connect.go index e8cb6d11d..830216269 100644 --- a/pkg/chain/ethereum/connect.go +++ b/pkg/chain/ethereum/connect.go @@ -67,6 +67,10 @@ func Connect(accountKey *keystore.Key, config *ethereum.Config) (*EthereumChain, return nil, err } + // TODO: Add rate limiting to (keep-ecdsa/pull/585#discussion_r513351032): + // - `createBlockTimestampFn` + // - `blockCounter` + // - `miningWaiter` wrappedClient := addClientWrappers(config, client) transactionMutex := &sync.Mutex{} From 10f673d1683d1d538ae7ae7bee38049631878dbc Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Thu, 29 Oct 2020 11:52:12 +0100 Subject: [PATCH 24/31] Move TBTC chain interface to `pkg/chain` --- pkg/chain/event.go | 18 -------------- .../tbtc/chain.go => chain/tbtc.go} | 24 ++++++++++++++----- pkg/extensions/tbtc/tbtc.go | 6 ++--- 3 files changed, 21 insertions(+), 27 deletions(-) rename pkg/{extensions/tbtc/chain.go => chain/tbtc.go} (82%) diff --git a/pkg/chain/event.go b/pkg/chain/event.go index 01a44010f..abc7ad675 100644 --- a/pkg/chain/event.go +++ b/pkg/chain/event.go @@ -1,8 +1,6 @@ package eth import ( - "math/big" - "github.com/ethereum/go-ethereum/common" ) @@ -62,19 +60,3 @@ func (e *BondedECDSAKeepCreatedEvent) IsMember(address common.Address) bool { } return false } - -// DepositRedemptionRequestedEvent is an event emitted when a deposit -// redemption has been requested or the redemption fee has been increased. -// FIXME: This event should be placed in `extensions/tbtc` package but -// this cause an import cycle. For now, there are no better option -// than place it right here. -type DepositRedemptionRequestedEvent struct { - DepositAddress string - RequesterAddress string - Digest [32]byte - UtxoValue *big.Int - RedeemerOutputScript []byte - RequestedFee *big.Int - Outpoint []byte - BlockNumber uint64 -} diff --git a/pkg/extensions/tbtc/chain.go b/pkg/chain/tbtc.go similarity index 82% rename from pkg/extensions/tbtc/chain.go rename to pkg/chain/tbtc.go index 51b7a77d4..ee248a1c7 100644 --- a/pkg/extensions/tbtc/chain.go +++ b/pkg/chain/tbtc.go @@ -1,15 +1,14 @@ -package tbtc +package eth import ( "math/big" "github.com/keep-network/keep-common/pkg/subscription" - chain "github.com/keep-network/keep-ecdsa/pkg/chain" ) -// Handle represents a chain handle extended with TBTC-specific capabilities. -type Handle interface { - chain.Handle +// TBTCHandle represents a chain handle extended with TBTC-specific capabilities. +type TBTCHandle interface { + Handle Deposit TBTCSystem @@ -98,5 +97,18 @@ type TBTCSystem interface { PastDepositRedemptionRequestedEvents( depositAddress string, startBlock uint64, - ) ([]*chain.DepositRedemptionRequestedEvent, error) + ) ([]*DepositRedemptionRequestedEvent, error) +} + +// DepositRedemptionRequestedEvent is an event emitted when a deposit +// redemption has been requested or the redemption fee has been increased. +type DepositRedemptionRequestedEvent struct { + DepositAddress string + RequesterAddress string + Digest [32]byte + UtxoValue *big.Int + RedeemerOutputScript []byte + RequestedFee *big.Int + Outpoint []byte + BlockNumber uint64 } diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index 31ae1590c..fb72d68cb 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -25,7 +25,7 @@ const ( ) // Initialize initializes extension specific to the TBTC application. -func Initialize(ctx context.Context, chain Handle) error { +func Initialize(ctx context.Context, chain chain.TBTCHandle) error { logger.Infof("initializing tbtc extension") tbtc := newTBTC(chain) @@ -74,10 +74,10 @@ func Initialize(ctx context.Context, chain Handle) error { } type tbtc struct { - chain Handle + chain chain.TBTCHandle } -func newTBTC(chain Handle) *tbtc { +func newTBTC(chain chain.TBTCHandle) *tbtc { return &tbtc{ chain: chain, } From 2f774164220e1862811d08770a3b39321393e435 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Thu, 29 Oct 2020 12:00:58 +0100 Subject: [PATCH 25/31] Fix import alias --- pkg/chain/local/bonded_ecdsa_keep_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/chain/local/bonded_ecdsa_keep_test.go b/pkg/chain/local/bonded_ecdsa_keep_test.go index 8ed9a5862..e74705d64 100644 --- a/pkg/chain/local/bonded_ecdsa_keep_test.go +++ b/pkg/chain/local/bonded_ecdsa_keep_test.go @@ -10,7 +10,7 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - eth "github.com/keep-network/keep-ecdsa/pkg/chain" + "github.com/keep-network/keep-ecdsa/pkg/chain" ) func TestRequestSignatureNonexistentKeep(t *testing.T) { From 2f38d37c2a3b9c54106290a0eae05900b62d66eb Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Thu, 29 Oct 2020 12:15:32 +0100 Subject: [PATCH 26/31] Add discussion link to the TODOs section --- pkg/extensions/tbtc/tbtc.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index fb72d68cb..434a7e99a 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -404,7 +404,7 @@ type backoffFn func(iteration int) time.Duration type timeoutFn func(depositAddress string) (time.Duration, error) -// TODO: +// TODO (keep-ecdsa/pull/585#discussion_r513447505): // 1. Filter incoming events by operator interest. // 2. Incoming events deduplication. // 3. Resume monitoring after client restart. From c3df0ec143b5ddb711833400829008113805d20b Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 9 Nov 2020 11:09:14 +0100 Subject: [PATCH 27/31] Remove signature submitted event lookup loop --- pkg/extensions/tbtc/tbtc.go | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/pkg/extensions/tbtc/tbtc.go b/pkg/extensions/tbtc/tbtc.go index 434a7e99a..5052617dc 100644 --- a/pkg/extensions/tbtc/tbtc.go +++ b/pkg/extensions/tbtc/tbtc.go @@ -187,25 +187,30 @@ func (t *tbtc) monitorProvideRedemptionSignature( return err } - depositDigest := latestRedemptionRequestedEvent.Digest + if len(signatureSubmittedEvents) == 0 { + return fmt.Errorf( + "no signature submitted events found for deposit: [%v]", + depositAddress, + ) + } + + latestSignatureSubmittedEvent := + signatureSubmittedEvents[len(signatureSubmittedEvents)-1] - // Start iterating from the latest event. - for i := len(signatureSubmittedEvents) - 1; i >= 0; i-- { - signatureSubmittedEvent := signatureSubmittedEvents[i] + depositDigest := latestRedemptionRequestedEvent.Digest - if bytes.Equal(signatureSubmittedEvent.Digest[:], depositDigest[:]) { - // We add 27 to the recovery ID to align it with ethereum and - // bitcoin protocols where 27 is added to recovery ID to - // indicate usage of uncompressed public keys. - v := 27 + signatureSubmittedEvent.RecoveryID + if bytes.Equal(latestSignatureSubmittedEvent.Digest[:], depositDigest[:]) { + // We add 27 to the recovery ID to align it with ethereum and + // bitcoin protocols where 27 is added to recovery ID to + // indicate usage of uncompressed public keys. + v := 27 + latestSignatureSubmittedEvent.RecoveryID - return t.chain.ProvideRedemptionSignature( - depositAddress, - v, - signatureSubmittedEvent.R, - signatureSubmittedEvent.S, - ) - } + return t.chain.ProvideRedemptionSignature( + depositAddress, + v, + latestSignatureSubmittedEvent.R, + latestSignatureSubmittedEvent.S, + ) } return fmt.Errorf( From 5d17df5b40962860f1dbff5e3bf350f66e65008a Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 9 Nov 2020 11:50:35 +0100 Subject: [PATCH 28/31] Align mutex names in local chain implementations --- pkg/chain/local/bonded_ecdsa_keep.go | 12 +-- pkg/chain/local/bonded_ecdsa_keep_factory.go | 4 +- pkg/chain/local/local.go | 90 +++++++++--------- pkg/chain/local/tbtc.go | 98 ++++++++++---------- 4 files changed, 100 insertions(+), 104 deletions(-) diff --git a/pkg/chain/local/bonded_ecdsa_keep.go b/pkg/chain/local/bonded_ecdsa_keep.go index 13b604ad4..e20a1b68e 100644 --- a/pkg/chain/local/bonded_ecdsa_keep.go +++ b/pkg/chain/local/bonded_ecdsa_keep.go @@ -30,8 +30,8 @@ type localKeep struct { } func (c *localChain) requestSignature(keepAddress common.Address, digest [32]byte) error { - c.handlerMutex.Lock() - defer c.handlerMutex.Unlock() + c.localChainMutex.Lock() + defer c.localChainMutex.Unlock() keep, ok := c.keeps[keepAddress] if !ok { @@ -65,8 +65,8 @@ func (c *localChain) requestSignature(keepAddress common.Address, digest [32]byt } func (c *localChain) closeKeep(keepAddress common.Address) error { - c.handlerMutex.Lock() - defer c.handlerMutex.Unlock() + c.localChainMutex.Lock() + defer c.localChainMutex.Unlock() keep, ok := c.keeps[keepAddress] if !ok { @@ -97,8 +97,8 @@ func (c *localChain) closeKeep(keepAddress common.Address) error { } func (c *localChain) terminateKeep(keepAddress common.Address) error { - c.handlerMutex.Lock() - defer c.handlerMutex.Unlock() + c.localChainMutex.Lock() + defer c.localChainMutex.Unlock() keep, ok := c.keeps[keepAddress] if !ok { diff --git a/pkg/chain/local/bonded_ecdsa_keep_factory.go b/pkg/chain/local/bonded_ecdsa_keep_factory.go index 939cae91e..b3eea1862 100644 --- a/pkg/chain/local/bonded_ecdsa_keep_factory.go +++ b/pkg/chain/local/bonded_ecdsa_keep_factory.go @@ -15,8 +15,8 @@ func (c *localChain) createKeepWithMembers( keepAddress common.Address, members []common.Address, ) error { - c.handlerMutex.Lock() - defer c.handlerMutex.Unlock() + c.localChainMutex.Lock() + defer c.localChainMutex.Unlock() if _, ok := c.keeps[keepAddress]; ok { return fmt.Errorf( diff --git a/pkg/chain/local/local.go b/pkg/chain/local/local.go index 1531c7e26..55bcd12a4 100644 --- a/pkg/chain/local/local.go +++ b/pkg/chain/local/local.go @@ -34,11 +34,10 @@ type Chain interface { // It mocks the behaviour of a real blockchain, without the complexity of deployments, // accounts, async transactions and so on. For use in tests ONLY. type localChain struct { - handlerMutex sync.Mutex + localChainMutex sync.Mutex - blockCounter chain.BlockCounter - blocksTimestamps map[uint64]uint64 - blocksTimestampsMutex sync.RWMutex + blockCounter chain.BlockCounter + blocksTimestamps sync.Map keepAddresses []common.Address keeps map[common.Address]*localKeep @@ -60,13 +59,15 @@ func Connect() Chain { localChain := &localChain{ blockCounter: blockCounter, - blocksTimestamps: map[uint64]uint64{0: uint64(time.Now().Unix())}, keeps: make(map[common.Address]*localKeep), keepCreatedHandlers: make(map[int]func(event *eth.BondedECDSAKeepCreatedEvent)), clientAddress: common.HexToAddress("6299496199d99941193Fdd2d717ef585F431eA05"), authorizations: make(map[common.Address]bool), } + // block 0 must be stored manually as it is not delivered by the block counter + localChain.blocksTimestamps.Store(uint64(0), uint64(time.Now().Unix())) + go localChain.observeBlocksTimestamps(context.Background()) return localChain @@ -78,9 +79,7 @@ func (lc *localChain) observeBlocksTimestamps(ctx context.Context) { for { select { case blockNumber := <-blockChan: - lc.blocksTimestampsMutex.Lock() - lc.blocksTimestamps[blockNumber] = uint64(time.Now().Unix()) - lc.blocksTimestampsMutex.Unlock() + lc.blocksTimestamps.Store(blockNumber, uint64(time.Now().Unix())) case <-ctx.Done(): return } @@ -103,8 +102,8 @@ func (lc *localChain) TerminateKeep(keepAddress common.Address) error { } func (lc *localChain) AuthorizeOperator(operator common.Address) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() lc.authorizations[operator] = true } @@ -129,16 +128,16 @@ func (lc *localChain) RegisterAsMemberCandidate(application common.Address) erro func (lc *localChain) OnBondedECDSAKeepCreated( handler func(event *eth.BondedECDSAKeepCreatedEvent), ) subscription.EventSubscription { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() handlerID := generateHandlerID() lc.keepCreatedHandlers[handlerID] = handler return subscription.NewEventSubscription(func() { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() delete(lc.keepCreatedHandlers, handlerID) }) @@ -150,8 +149,8 @@ func (lc *localChain) OnSignatureRequested( keepAddress common.Address, handler func(event *eth.SignatureRequestedEvent), ) (subscription.EventSubscription, error) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() handlerID := generateHandlerID() @@ -166,8 +165,8 @@ func (lc *localChain) OnSignatureRequested( keep.signatureRequestedHandlers[handlerID] = handler return subscription.NewEventSubscription(func() { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() delete(keep.signatureRequestedHandlers, handlerID) }), nil @@ -179,8 +178,8 @@ func (lc *localChain) SubmitKeepPublicKey( keepAddress common.Address, publicKey [64]byte, ) error { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() keep, ok := lc.keeps[keepAddress] if !ok { @@ -208,8 +207,8 @@ func (lc *localChain) SubmitSignature( keepAddress common.Address, signature *ecdsa.Signature, ) error { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() keep, ok := lc.keeps[keepAddress] if !ok { @@ -261,8 +260,8 @@ func (lc *localChain) IsAwaitingSignature( // IsActive checks for current state of a keep on-chain. func (lc *localChain) IsActive(keepAddress common.Address) (bool, error) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() keep, ok := lc.keeps[keepAddress] if !ok { @@ -293,15 +292,15 @@ func (lc *localChain) UpdateStatusForApplication(application common.Address) err } func (lc *localChain) IsOperatorAuthorized(operator common.Address) (bool, error) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() return lc.authorizations[operator], nil } func (lc *localChain) GetKeepCount() (*big.Int, error) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() return big.NewInt(int64(len(lc.keeps))), nil } @@ -309,8 +308,8 @@ func (lc *localChain) GetKeepCount() (*big.Int, error) { func (lc *localChain) GetKeepAtIndex( keepIndex *big.Int, ) (common.Address, error) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() index := int(keepIndex.Uint64()) @@ -325,8 +324,8 @@ func (lc *localChain) OnKeepClosed( keepAddress common.Address, handler func(event *eth.KeepClosedEvent), ) (subscription.EventSubscription, error) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() handlerID := generateHandlerID() @@ -341,8 +340,8 @@ func (lc *localChain) OnKeepClosed( keep.keepClosedHandlers[handlerID] = handler return subscription.NewEventSubscription(func() { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() delete(keep.keepClosedHandlers, handlerID) }), nil @@ -352,8 +351,8 @@ func (lc *localChain) OnKeepTerminated( keepAddress common.Address, handler func(event *eth.KeepTerminatedEvent), ) (subscription.EventSubscription, error) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() handlerID := generateHandlerID() @@ -368,8 +367,8 @@ func (lc *localChain) OnKeepTerminated( keep.keepTerminatedHandlers[handlerID] = handler return subscription.NewEventSubscription(func() { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() delete(keep.keepTerminatedHandlers, handlerID) }), nil @@ -407,8 +406,8 @@ func (lc *localChain) GetPublicKey(keepAddress common.Address) ([]uint8, error) func (lc *localChain) GetMembers( keepAddress common.Address, ) ([]common.Address, error) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() keep, ok := lc.keeps[keepAddress] if !ok { @@ -431,8 +430,8 @@ func (lc *localChain) PastSignatureSubmittedEvents( keepAddress string, startBlock uint64, ) ([]*eth.SignatureSubmittedEvent, error) { - lc.handlerMutex.Lock() - defer lc.handlerMutex.Unlock() + lc.localChainMutex.Lock() + defer lc.localChainMutex.Unlock() keep, ok := lc.keeps[common.HexToAddress(keepAddress)] if !ok { @@ -443,15 +442,12 @@ func (lc *localChain) PastSignatureSubmittedEvents( } func (lc *localChain) BlockTimestamp(blockNumber *big.Int) (uint64, error) { - lc.blocksTimestampsMutex.RLock() - defer lc.blocksTimestampsMutex.RUnlock() - - blockTimestamp, ok := lc.blocksTimestamps[blockNumber.Uint64()] + blockTimestamp, ok := lc.blocksTimestamps.Load(blockNumber.Uint64()) if !ok { return 0, fmt.Errorf("no timestamp for block [%v]", blockNumber) } - return blockTimestamp, nil + return blockTimestamp.(uint64), nil } func generateHandlerID() int { diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index ae1504ea3..f668ad366 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -73,7 +73,7 @@ func (lcl *localChainLogger) IncreaseRedemptionFeeCalls() int { type TBTCLocalChain struct { *localChain - mutex sync.Mutex + tbtcLocalChainMutex sync.Mutex logger *localChainLogger @@ -102,8 +102,8 @@ func NewTBTCLocalChain() *TBTCLocalChain { } func (tlc *TBTCLocalChain) CreateDeposit(depositAddress string) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() keepAddress := generateAddress() tlc.OpenKeep(keepAddress, []common.Address{ @@ -128,16 +128,16 @@ func (tlc *TBTCLocalChain) CreateDeposit(depositAddress string) { func (tlc *TBTCLocalChain) OnDepositCreated( handler func(depositAddress string), ) (subscription.EventSubscription, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() handlerID := generateHandlerID() tlc.depositCreatedHandlers[handlerID] = handler return subscription.NewEventSubscription(func() { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() delete(tlc.depositCreatedHandlers, handlerID) }), nil @@ -146,24 +146,24 @@ func (tlc *TBTCLocalChain) OnDepositCreated( func (tlc *TBTCLocalChain) OnDepositRegisteredPubkey( handler func(depositAddress string), ) (subscription.EventSubscription, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() handlerID := generateHandlerID() tlc.depositRegisteredPubkeyHandlers[handlerID] = handler return subscription.NewEventSubscription(func() { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() delete(tlc.depositRegisteredPubkeyHandlers, handlerID) }), nil } func (tlc *TBTCLocalChain) RedeemDeposit(depositAddress string) error { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() deposit, ok := tlc.deposits[depositAddress] if !ok { @@ -229,16 +229,16 @@ func (tlc *TBTCLocalChain) RedeemDeposit(depositAddress string) error { func (tlc *TBTCLocalChain) OnDepositRedemptionRequested( handler func(depositAddress string), ) (subscription.EventSubscription, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() handlerID := generateHandlerID() tlc.depositRedemptionRequestedHandlers[handlerID] = handler return subscription.NewEventSubscription(func() { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() delete(tlc.depositRedemptionRequestedHandlers, handlerID) }), nil @@ -247,16 +247,16 @@ func (tlc *TBTCLocalChain) OnDepositRedemptionRequested( func (tlc *TBTCLocalChain) OnDepositGotRedemptionSignature( handler func(depositAddress string), ) (subscription.EventSubscription, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() handlerID := generateHandlerID() tlc.depositGotRedemptionSignatureHandlers[handlerID] = handler return subscription.NewEventSubscription(func() { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() delete(tlc.depositGotRedemptionSignatureHandlers, handlerID) }), nil @@ -265,16 +265,16 @@ func (tlc *TBTCLocalChain) OnDepositGotRedemptionSignature( func (tlc *TBTCLocalChain) OnDepositRedeemed( handler func(depositAddress string), ) (subscription.EventSubscription, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() handlerID := generateHandlerID() tlc.depositRedeemedHandlers[handlerID] = handler return subscription.NewEventSubscription(func() { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() delete(tlc.depositRedeemedHandlers, handlerID) }), nil @@ -284,8 +284,8 @@ func (tlc *TBTCLocalChain) PastDepositRedemptionRequestedEvents( depositAddress string, startBlock uint64, ) ([]*chain.DepositRedemptionRequestedEvent, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() deposit, ok := tlc.deposits[depositAddress] if !ok { @@ -296,8 +296,8 @@ func (tlc *TBTCLocalChain) PastDepositRedemptionRequestedEvents( } func (tlc *TBTCLocalChain) KeepAddress(depositAddress string) (string, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() deposit, ok := tlc.deposits[depositAddress] if !ok { @@ -308,8 +308,8 @@ func (tlc *TBTCLocalChain) KeepAddress(depositAddress string) (string, error) { } func (tlc *TBTCLocalChain) RetrieveSignerPubkey(depositAddress string) error { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() tlc.logger.logRetrieveSignerPubkeyCall() @@ -326,8 +326,8 @@ func (tlc *TBTCLocalChain) RetrieveSignerPubkey(depositAddress string) error { } // lock upstream mutex to access `keeps` map safely - tlc.handlerMutex.Lock() - defer tlc.handlerMutex.Unlock() + tlc.localChainMutex.Lock() + defer tlc.localChainMutex.Unlock() keep, ok := tlc.keeps[common.HexToAddress(deposit.keepAddress)] if !ok { @@ -362,8 +362,8 @@ func (tlc *TBTCLocalChain) ProvideRedemptionSignature( r [32]uint8, s [32]uint8, ) error { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() tlc.logger.logProvideRedemptionSignatureCall() @@ -407,8 +407,8 @@ func (tlc *TBTCLocalChain) IncreaseRedemptionFee( previousOutputValueBytes [8]uint8, newOutputValueBytes [8]uint8, ) error { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() tlc.logger.logIncreaseRedemptionFeeCalls() @@ -503,8 +503,8 @@ func (tlc *TBTCLocalChain) ProvideRedemptionProof( txIndexInBlock *big.Int, bitcoinHeaders []uint8, ) error { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() deposit, ok := tlc.deposits[depositAddress] if !ok { @@ -532,8 +532,8 @@ func (tlc *TBTCLocalChain) ProvideRedemptionProof( func (tlc *TBTCLocalChain) DepositPubkey( depositAddress string, ) ([]byte, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() deposit, ok := tlc.deposits[depositAddress] if !ok { @@ -553,8 +553,8 @@ func (tlc *TBTCLocalChain) DepositPubkey( func (tlc *TBTCLocalChain) DepositRedemptionSignature( depositAddress string, ) (*Signature, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() deposit, ok := tlc.deposits[depositAddress] if !ok { @@ -574,8 +574,8 @@ func (tlc *TBTCLocalChain) DepositRedemptionSignature( func (tlc *TBTCLocalChain) DepositRedemptionProof( depositAddress string, ) (*TxProof, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() deposit, ok := tlc.deposits[depositAddress] if !ok { @@ -595,8 +595,8 @@ func (tlc *TBTCLocalChain) DepositRedemptionProof( func (tlc *TBTCLocalChain) DepositRedemptionFee( depositAddress string, ) (*big.Int, error) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() deposit, ok := tlc.deposits[depositAddress] if !ok { @@ -614,8 +614,8 @@ func (tlc *TBTCLocalChain) DepositRedemptionFee( } func (tlc *TBTCLocalChain) SetAlwaysFailingTransactions(transactions ...string) { - tlc.mutex.Lock() - defer tlc.mutex.Unlock() + tlc.tbtcLocalChainMutex.Lock() + defer tlc.tbtcLocalChainMutex.Unlock() for _, tx := range transactions { tlc.alwaysFailingTransactions[tx] = true From d869e2df5f7c5a04eda521bf1fb04838ebecba54 Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 9 Nov 2020 12:22:49 +0100 Subject: [PATCH 29/31] Add ctx param to local chain constructor --- .../local/bonded_ecdsa_keep_factory_test.go | 11 +- pkg/chain/local/bonded_ecdsa_keep_test.go | 18 ++- pkg/chain/local/local.go | 4 +- pkg/chain/local/local_test.go | 21 +-- pkg/chain/local/tbtc.go | 5 +- pkg/extensions/tbtc/tbtc_test.go | 126 ++++++++++++------ pkg/firewall/firewall_test.go | 76 ++++++++--- 7 files changed, 185 insertions(+), 76 deletions(-) diff --git a/pkg/chain/local/bonded_ecdsa_keep_factory_test.go b/pkg/chain/local/bonded_ecdsa_keep_factory_test.go index 1e79452de..def995c3b 100644 --- a/pkg/chain/local/bonded_ecdsa_keep_factory_test.go +++ b/pkg/chain/local/bonded_ecdsa_keep_factory_test.go @@ -2,6 +2,7 @@ package local import ( "bytes" + "context" "fmt" "reflect" "testing" @@ -10,7 +11,10 @@ import ( ) func TestCreateKeepDuplicate(t *testing.T) { - chain := initializeLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := initializeLocalChain(ctx) keepAddress := common.Address([20]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) expectedError := fmt.Errorf("keep already exists for address [0x0000000000000000000000000000000000000001]") @@ -30,7 +34,10 @@ func TestCreateKeepDuplicate(t *testing.T) { } func TestCreateKeep(t *testing.T) { - chain := initializeLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := initializeLocalChain(ctx) keepAddress := common.Address([20]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) expectedPublicKey := [64]byte{} diff --git a/pkg/chain/local/bonded_ecdsa_keep_test.go b/pkg/chain/local/bonded_ecdsa_keep_test.go index e74705d64..30dd51c8f 100644 --- a/pkg/chain/local/bonded_ecdsa_keep_test.go +++ b/pkg/chain/local/bonded_ecdsa_keep_test.go @@ -10,11 +10,14 @@ import ( "time" "github.com/ethereum/go-ethereum/common" - "github.com/keep-network/keep-ecdsa/pkg/chain" + eth "github.com/keep-network/keep-ecdsa/pkg/chain" ) func TestRequestSignatureNonexistentKeep(t *testing.T) { - chain := initializeLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := initializeLocalChain(ctx) keepAddress := common.Address([20]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) digest := [32]byte{1} expectedError := fmt.Errorf("failed to find keep with address: [0x0000000000000000000000000000000000000001]") @@ -31,7 +34,10 @@ func TestRequestSignatureNonexistentKeep(t *testing.T) { } func TestRequestSignatureNoHandler(t *testing.T) { - chain := initializeLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := initializeLocalChain(ctx) keepAddress := common.Address([20]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) digest := [32]byte{1} @@ -55,10 +61,10 @@ func TestRequestSignatureNoHandler(t *testing.T) { } func TestRequestSignature(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() + ctx, cancelCtx := context.WithTimeout(context.Background(), 1*time.Second) + defer cancelCtx() - chain := initializeLocalChain() + chain := initializeLocalChain(ctx) keepAddress := common.Address([20]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) digest := [32]byte{1} eventEmitted := make(chan *eth.SignatureRequestedEvent) diff --git a/pkg/chain/local/local.go b/pkg/chain/local/local.go index 55bcd12a4..a679e4091 100644 --- a/pkg/chain/local/local.go +++ b/pkg/chain/local/local.go @@ -51,7 +51,7 @@ type localChain struct { // Connect performs initialization for communication with Ethereum blockchain // based on provided config. -func Connect() Chain { +func Connect(ctx context.Context) Chain { blockCounter, err := local.BlockCounter() if err != nil { panic(err) // should never happen @@ -68,7 +68,7 @@ func Connect() Chain { // block 0 must be stored manually as it is not delivered by the block counter localChain.blocksTimestamps.Store(uint64(0), uint64(time.Now().Unix())) - go localChain.observeBlocksTimestamps(context.Background()) + go localChain.observeBlocksTimestamps(ctx) return localChain } diff --git a/pkg/chain/local/local_test.go b/pkg/chain/local/local_test.go index 7c2646f4f..66c00b92e 100644 --- a/pkg/chain/local/local_test.go +++ b/pkg/chain/local/local_test.go @@ -13,10 +13,10 @@ import ( ) func TestOnBondedECDSAKeepCreated(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() + ctx, cancelCtx := context.WithTimeout(context.Background(), 1*time.Second) + defer cancelCtx() - chain := initializeLocalChain() + chain := initializeLocalChain(ctx) eventFired := make(chan *eth.BondedECDSAKeepCreatedEvent) keepAddress := common.Address([20]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) expectedEvent := ð.BondedECDSAKeepCreatedEvent{ @@ -50,10 +50,10 @@ func TestOnBondedECDSAKeepCreated(t *testing.T) { } func TestOnSignatureRequested(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() + ctx, cancelCtx := context.WithTimeout(context.Background(), 1*time.Second) + defer cancelCtx() - chain := initializeLocalChain() + chain := initializeLocalChain(ctx) eventFired := make(chan *eth.SignatureRequestedEvent) keepAddress := common.Address([20]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}) digest := [32]byte{1} @@ -106,7 +106,10 @@ func TestOnSignatureRequested(t *testing.T) { } func TestSubmitKeepPublicKey(t *testing.T) { - chain := initializeLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := initializeLocalChain(ctx) keepAddress := common.HexToAddress("0x41048F9B90290A2e96D07f537F3A7E97620E9e47") keepPublicKey := [64]byte{11, 12, 13, 14, 15, 16} expectedDuplicationError := fmt.Errorf( @@ -148,6 +151,6 @@ func TestSubmitKeepPublicKey(t *testing.T) { } } -func initializeLocalChain() *localChain { - return Connect().(*localChain) +func initializeLocalChain(ctx context.Context) *localChain { + return Connect(ctx).(*localChain) } diff --git a/pkg/chain/local/tbtc.go b/pkg/chain/local/tbtc.go index f668ad366..c0d951bff 100644 --- a/pkg/chain/local/tbtc.go +++ b/pkg/chain/local/tbtc.go @@ -2,6 +2,7 @@ package local import ( "bytes" + "context" "encoding/binary" "fmt" "math/big" @@ -87,9 +88,9 @@ type TBTCLocalChain struct { depositRedeemedHandlers map[int]func(depositAddress string) } -func NewTBTCLocalChain() *TBTCLocalChain { +func NewTBTCLocalChain(ctx context.Context) *TBTCLocalChain { return &TBTCLocalChain{ - localChain: Connect().(*localChain), + localChain: Connect(ctx).(*localChain), logger: &localChainLogger{}, alwaysFailingTransactions: make(map[string]bool), deposits: make(map[string]*localDeposit), diff --git a/pkg/extensions/tbtc/tbtc_test.go b/pkg/extensions/tbtc/tbtc_test.go index 432e7ade0..d168aa2d3 100644 --- a/pkg/extensions/tbtc/tbtc_test.go +++ b/pkg/extensions/tbtc/tbtc_test.go @@ -23,8 +23,10 @@ const ( ) func TestRetrievePubkey_TimeoutElapsed(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( @@ -80,8 +82,10 @@ func TestRetrievePubkey_TimeoutElapsed(t *testing.T) { } func TestRetrievePubkey_StopEventOccurred(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( @@ -147,8 +151,10 @@ func TestRetrievePubkey_StopEventOccurred(t *testing.T) { } func TestRetrievePubkey_KeepClosedEventOccurred(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( @@ -211,8 +217,10 @@ func TestRetrievePubkey_KeepClosedEventOccurred(t *testing.T) { } func TestRetrievePubkey_KeepTerminatedEventOccurred(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( @@ -275,8 +283,10 @@ func TestRetrievePubkey_KeepTerminatedEventOccurred(t *testing.T) { } func TestRetrievePubkey_ActionFailed(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( @@ -313,7 +323,9 @@ func TestRetrievePubkey_ActionFailed(t *testing.T) { func TestRetrievePubkey_ContextCancelled_WithoutWorkingMonitoring(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) - tbtcChain := local.NewTBTCLocalChain() + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( @@ -350,7 +362,9 @@ func TestRetrievePubkey_ContextCancelled_WithoutWorkingMonitoring(t *testing.T) func TestRetrievePubkey_ContextCancelled_WithWorkingMonitoring(t *testing.T) { ctx, cancelCtx := context.WithCancel(context.Background()) - tbtcChain := local.NewTBTCLocalChain() + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorRetrievePubKey( @@ -392,8 +406,10 @@ func TestRetrievePubkey_ContextCancelled_WithWorkingMonitoring(t *testing.T) { } func TestProvideRedemptionSignature_TimeoutElapsed(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionSignature( @@ -464,8 +480,10 @@ func TestProvideRedemptionSignature_TimeoutElapsed(t *testing.T) { func TestProvideRedemptionSignature_StopEventOccurred_DepositGotRedemptionSignature( t *testing.T, ) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionSignature( @@ -551,8 +569,10 @@ func TestProvideRedemptionSignature_StopEventOccurred_DepositGotRedemptionSignat func TestProvideRedemptionSignature_StopEventOccurred_DepositRedeemed( t *testing.T, ) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionSignature( @@ -629,8 +649,10 @@ func TestProvideRedemptionSignature_StopEventOccurred_DepositRedeemed( } func TestProvideRedemptionSignature_KeepClosedEventOccurred(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionSignature( @@ -704,8 +726,10 @@ func TestProvideRedemptionSignature_KeepClosedEventOccurred(t *testing.T) { } func TestProvideRedemptionSignature_KeepTerminatedEventOccurred(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionSignature( @@ -779,8 +803,10 @@ func TestProvideRedemptionSignature_KeepTerminatedEventOccurred(t *testing.T) { } func TestProvideRedemptionSignature_ActionFailed(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionSignature( @@ -835,7 +861,9 @@ func TestProvideRedemptionSignature_ContextCancelled_WithoutWorkingMonitoring( t *testing.T, ) { ctx, cancelCtx := context.WithCancel(context.Background()) - tbtcChain := local.NewTBTCLocalChain() + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionSignature( @@ -885,7 +913,9 @@ func TestProvideRedemptionSignature_ContextCancelled_WithWorkingMonitoring( t *testing.T, ) { ctx, cancelCtx := context.WithCancel(context.Background()) - tbtcChain := local.NewTBTCLocalChain() + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionSignature( @@ -937,8 +967,10 @@ func TestProvideRedemptionSignature_ContextCancelled_WithWorkingMonitoring( } func TestProvideRedemptionProof_TimeoutElapsed(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionProof( @@ -1027,8 +1059,10 @@ func TestProvideRedemptionProof_TimeoutElapsed(t *testing.T) { func TestProvideRedemptionProof_StopEventOccurred_DepositRedemptionRequested( t *testing.T, ) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionProof( @@ -1131,8 +1165,10 @@ func TestProvideRedemptionProof_StopEventOccurred_DepositRedemptionRequested( func TestProvideRedemptionProof_StopEventOccurred_DepositRedeemed( t *testing.T, ) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionProof( @@ -1218,8 +1254,10 @@ func TestProvideRedemptionProof_StopEventOccurred_DepositRedeemed( } func TestProvideRedemptionProof_KeepClosedEventOccurred(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionProof( @@ -1310,8 +1348,10 @@ func TestProvideRedemptionProof_KeepClosedEventOccurred(t *testing.T) { } func TestProvideRedemptionProof_KeepTerminatedEventOccurred(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionProof( @@ -1402,8 +1442,10 @@ func TestProvideRedemptionProof_KeepTerminatedEventOccurred(t *testing.T) { } func TestProvideRedemptionProof_ActionFailed(t *testing.T) { - ctx := context.Background() - tbtcChain := local.NewTBTCLocalChain() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionProof( @@ -1467,7 +1509,9 @@ func TestProvideRedemptionProof_ContextCancelled_WithoutWorkingMonitoring( t *testing.T, ) { ctx, cancelCtx := context.WithCancel(context.Background()) - tbtcChain := local.NewTBTCLocalChain() + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionProof( @@ -1531,7 +1575,9 @@ func TestProvideRedemptionProof_ContextCancelled_WithWorkingMonitoring( t *testing.T, ) { ctx, cancelCtx := context.WithCancel(context.Background()) - tbtcChain := local.NewTBTCLocalChain() + defer cancelCtx() + + tbtcChain := local.NewTBTCLocalChain(ctx) tbtc := newTBTC(tbtcChain) err := tbtc.monitorProvideRedemptionProof( diff --git a/pkg/firewall/firewall_test.go b/pkg/firewall/firewall_test.go index bb25f6a54..da77ac876 100644 --- a/pkg/firewall/firewall_test.go +++ b/pkg/firewall/firewall_test.go @@ -1,6 +1,7 @@ package firewall import ( + "context" "crypto/ecdsa" "fmt" "reflect" @@ -20,7 +21,10 @@ var cacheLifeTime = time.Second // Has minimum stake. // Should allow to connect. func TestHasMinimumStake(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -42,7 +46,10 @@ func TestHasMinimumStake(t *testing.T) { // Has no authorization. // Should NOT allow to connect. func TestNoAuthorization(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -66,7 +73,10 @@ func TestNoAuthorization(t *testing.T) { // Has no authorization // Should cache the information operator is not authorized func TestCachesNotAuthorizedOperators(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -93,7 +103,10 @@ func TestCachesNotAuthorizedOperators(t *testing.T) { // Has authorization // Should cache the information operator is authorized. func TestCachesAuthorizedOperators(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -119,7 +132,10 @@ func TestCachesAuthorizedOperators(t *testing.T) { } func TestConsultsAuthorizedOperatorsCache(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -160,7 +176,10 @@ func TestConsultsAuthorizedOperatorsCache(t *testing.T) { // No keeps exist. // Should NOT allow to connect. func TestNoMinimumStakeNoKeepsExist(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -189,7 +208,10 @@ func TestNoMinimumStakeNoKeepsExist(t *testing.T) { // It not a member of a keep. // Should NOT allow to connect. func TestNoMinimumStakeIsNotKeepMember(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -226,7 +248,10 @@ func TestNoMinimumStakeIsNotKeepMember(t *testing.T) { // Is a member of an active keep // Should allow to connect. func TestNoMinimumStakeIsActiveKeepMember(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -259,7 +284,10 @@ func TestNoMinimumStakeIsActiveKeepMember(t *testing.T) { // Is a member of a closed keep // Should NOT allow to connect. func TestNoMinimumStakeIsClosedKeepMember(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -301,7 +329,10 @@ func TestNoMinimumStakeIsClosedKeepMember(t *testing.T) { // Is a member of an active keep // Should allow to connect. func TestNoMinimumStakeMultipleKeepsMember(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -348,7 +379,10 @@ func TestNoMinimumStakeMultipleKeepsMember(t *testing.T) { // Is not a member of an active keep. // Should NOT allow to connect but should cache all active keep members in-memory. func TestCachesAllActiveKeepMembers(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -425,7 +459,10 @@ func TestCachesAllActiveKeepMembers(t *testing.T) { // After some time, the keep gets closed. // It should no longer allow to connect. func TestSweepsActiveKeepMembersCache(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -485,7 +522,10 @@ func TestSweepsActiveKeepMembersCache(t *testing.T) { // Shortly after that, the minimum stake drops below the required minimum but // the membership in an active keep remains. func TestSweepsNoActiveKeepMembersCache(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -538,7 +578,10 @@ func TestSweepsNoActiveKeepMembersCache(t *testing.T) { } func TestIsKeepActiveCaching(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) @@ -611,7 +654,10 @@ func TestIsKeepActiveCaching(t *testing.T) { } func TestGetKeepMembersCaching(t *testing.T) { - chain := local.Connect() + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := local.Connect(ctx) coreFirewall := newMockCoreFirewall() policy := createNewPolicy(chain, coreFirewall) From 2dca954ca4aa9f09877a0164bd308c5cfb6b970b Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 9 Nov 2020 12:41:06 +0100 Subject: [PATCH 30/31] Add test for local `SubmitSignature` method --- pkg/chain/local/local_test.go | 74 +++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/pkg/chain/local/local_test.go b/pkg/chain/local/local_test.go index 66c00b92e..0728b6a3a 100644 --- a/pkg/chain/local/local_test.go +++ b/pkg/chain/local/local_test.go @@ -3,11 +3,15 @@ package local import ( "context" "fmt" + "math/big" "math/rand" "reflect" "testing" "time" + "github.com/keep-network/keep-ecdsa/pkg/ecdsa" + "github.com/keep-network/keep-ecdsa/pkg/utils/byteutils" + "github.com/ethereum/go-ethereum/common" eth "github.com/keep-network/keep-ecdsa/pkg/chain" ) @@ -151,6 +155,76 @@ func TestSubmitKeepPublicKey(t *testing.T) { } } +func TestSubmitSignature(t *testing.T) { + ctx, cancelCtx := context.WithCancel(context.Background()) + defer cancelCtx() + + chain := initializeLocalChain(ctx) + + keepAddress := common.HexToAddress("0x41048F9B90290A2e96D07f537F3A7E97620E9e47") + keepPublicKey := [64]byte{11, 12, 13, 14, 15, 16} + + err := chain.createKeep(keepAddress) + if err != nil { + t.Fatal(err) + } + + err = chain.SubmitKeepPublicKey( + keepAddress, + keepPublicKey, + ) + if err != nil { + t.Fatal(err) + } + + digest := [32]byte{17, 18} + + err = chain.requestSignature(keepAddress, digest) + if err != nil { + t.Fatal(err) + } + + signature := &ecdsa.Signature{ + R: big.NewInt(10), + S: big.NewInt(11), + RecoveryID: 1, + } + + err = chain.SubmitSignature(keepAddress, signature) + if err != nil { + t.Fatal(err) + } + + events, err := chain.PastSignatureSubmittedEvents(keepAddress.Hex(), 0) + if err != nil { + t.Fatal(err) + } + + if len(events) != 1 { + t.Errorf("there should be one signature submitted event") + } + + expectedRBytes, _ := byteutils.BytesTo32Byte(signature.R.Bytes()) + expectedSBytes, _ := byteutils.BytesTo32Byte(signature.S.Bytes()) + expectedEvent := ð.SignatureSubmittedEvent{ + Digest: digest, + R: expectedRBytes, + S: expectedSBytes, + RecoveryID: 1, + BlockNumber: 0, + } + + lastEvent := events[len(events)-1] + + if !reflect.DeepEqual(expectedEvent, lastEvent) { + t.Fatalf( + "unexpected signature submitted event\nexpected: [%+v]\nactual: [%+v]", + expectedEvent, + lastEvent, + ) + } +} + func initializeLocalChain(ctx context.Context) *localChain { return Connect(ctx).(*localChain) } From 9f67bfa30dc0bab1e63a30c038cde3d438e7f8ed Mon Sep 17 00:00:00 2001 From: Lukasz Zimnoch Date: Mon, 9 Nov 2020 14:03:45 +0100 Subject: [PATCH 31/31] Add comment about `IncreaseRedemptionFee` call --- pkg/extensions/tbtc/tbtc_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/extensions/tbtc/tbtc_test.go b/pkg/extensions/tbtc/tbtc_test.go index d168aa2d3..ec041665b 100644 --- a/pkg/extensions/tbtc/tbtc_test.go +++ b/pkg/extensions/tbtc/tbtc_test.go @@ -1126,6 +1126,9 @@ func TestProvideRedemptionProof_StopEventOccurred_DepositRedemptionRequested( // to make sure the potential transaction completes time.Sleep(2 * timeout) + // Expect exactly one call of `IncreaseRedemptionFee` coming from the + // explicit invocation placed above. The monitoring routine is not expected + // to do any calls. expectedIncreaseRedemptionFeeCalls := 1 actualIncreaseRedemptionFeeCalls := tbtcChain.Logger(). IncreaseRedemptionFeeCalls()