From 8bc032d49c0461ef83eeb2739cdd671e6db1490d Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Tue, 14 Jan 2025 18:54:02 -0300 Subject: [PATCH 01/11] chore: remove opt to remote signer, should always use remote signer --- cmd/covd/start.go | 33 +-- config/config.go | 21 +- covenant/covenant_test.go | 10 +- itest/babylon_node_handler.go | 2 + itest/test_manager.go | 483 ---------------------------------- 5 files changed, 20 insertions(+), 529 deletions(-) delete mode 100644 itest/test_manager.go diff --git a/cmd/covd/start.go b/cmd/covd/start.go index 446d8a8..e608d83 100644 --- a/cmd/covd/start.go +++ b/cmd/covd/start.go @@ -5,7 +5,6 @@ import ( "path/filepath" covcfg "github.com/babylonlabs-io/covenant-emulator/config" - "github.com/babylonlabs-io/covenant-emulator/keyring" "github.com/babylonlabs-io/covenant-emulator/log" "github.com/babylonlabs-io/covenant-emulator/remotesigner" "github.com/babylonlabs-io/covenant-emulator/util" @@ -23,11 +22,6 @@ var startCommand = cli.Command{ Usage: "Start the Covenant Emulator Daemon", Description: "Start the Covenant Emulator Daemon. Note that the Covenant key pair should be created beforehand", Flags: []cli.Flag{ - cli.StringFlag{ - Name: passphraseFlag, - Usage: "The pass phrase used to encrypt the keys", - Value: defaultPassphrase, - }, cli.StringFlag{ Name: homeFlag, Usage: "The path to the covenant home directory", @@ -59,20 +53,9 @@ func start(ctx *cli.Context) error { return fmt.Errorf("failed to create rpc client for the consumer chain: %w", err) } - pwd := ctx.String(passphraseFlag) - - var signer covenant.Signer - - if cfg.RemoteSignerEnabled { - signer, err = newRemoteSignerFromConfig(cfg) - if err != nil { - return fmt.Errorf("failed to create remote signer from config: %w", err) - } - } else { - signer, err = newSignerFromConfig(cfg, pwd) - if err != nil { - return fmt.Errorf("failed to create keyring signer from config: %w", err) - } + signer, err := newRemoteSignerFromConfig(cfg) + if err != nil { + return fmt.Errorf("failed to create remote signer from config: %w", err) } ce, err := covenant.NewCovenantEmulator(cfg, bbnClient, logger, signer) @@ -94,16 +77,6 @@ func start(ctx *cli.Context) error { return srv.RunUntilShutdown() } -func newSignerFromConfig(cfg *covcfg.Config, passphrase string) (covenant.Signer, error) { - return keyring.NewKeyringSigner( - cfg.BabylonConfig.ChainID, - cfg.BabylonConfig.Key, - cfg.BabylonConfig.KeyDirectory, - cfg.BabylonConfig.KeyringBackend, - passphrase, - ) -} - func newRemoteSignerFromConfig(cfg *covcfg.Config) (covenant.Signer, error) { return remotesigner.NewRemoteSigner(cfg.RemoteSigner), nil } diff --git a/config/config.go b/config/config.go index dc57336..b35c9a6 100644 --- a/config/config.go +++ b/config/config.go @@ -47,8 +47,6 @@ type Config struct { BabylonConfig *BBNConfig `group:"babylon" namespace:"babylon"` - RemoteSignerEnabled bool `long:"remote-signer-enabled" description:"if true, covenant will use the remote signer to sign transactions"` - RemoteSigner *RemoteSignerCfg `group:"remotesigner" namespace:"remotesigner"` } @@ -134,16 +132,15 @@ func DefaultConfigWithHomePath(homePath string) Config { metricsCfg := DefaultMetricsConfig() remoteSignerCfg := DefaultRemoteSignerConfig() cfg := Config{ - LogLevel: defaultLogLevel, - QueryInterval: defaultQueryInterval, - DelegationLimit: defaultDelegationLimit, - SigsBatchSize: defaultSigsBatchSize, - BitcoinNetwork: defaultBitcoinNetwork, - BTCNetParams: defaultBTCNetParams, - Metrics: &metricsCfg, - BabylonConfig: &bbnCfg, - RemoteSignerEnabled: false, - RemoteSigner: &remoteSignerCfg, + LogLevel: defaultLogLevel, + QueryInterval: defaultQueryInterval, + DelegationLimit: defaultDelegationLimit, + SigsBatchSize: defaultSigsBatchSize, + BitcoinNetwork: defaultBitcoinNetwork, + BTCNetParams: defaultBTCNetParams, + Metrics: &metricsCfg, + BabylonConfig: &bbnCfg, + RemoteSigner: &remoteSignerCfg, } if err := cfg.Validate(); err != nil { diff --git a/covenant/covenant_test.go b/covenant/covenant_test.go index fe7e265..5ab15c5 100644 --- a/covenant/covenant_test.go +++ b/covenant/covenant_test.go @@ -24,6 +24,7 @@ import ( covcfg "github.com/babylonlabs-io/covenant-emulator/config" "github.com/babylonlabs-io/covenant-emulator/covenant" "github.com/babylonlabs-io/covenant-emulator/keyring" + "github.com/babylonlabs-io/covenant-emulator/remotesigner" "github.com/babylonlabs-io/covenant-emulator/testutil" "github.com/babylonlabs-io/covenant-emulator/types" ) @@ -37,6 +38,10 @@ var net = &chaincfg.SimNetParams func FuzzAddCovenantSig(f *testing.F) { testutil.AddRandomSeedsToFuzzer(f, 10) + + // create a Covenant key pair in the keyring + covenantConfig := covcfg.DefaultConfig() + f.Fuzz(func(t *testing.T, seed int64) { t.Log("Seed", seed) r := rand.New(rand.NewSource(seed)) @@ -44,8 +49,6 @@ func FuzzAddCovenantSig(f *testing.F) { params := testutil.GenRandomParams(r, t) mockClientController := testutil.PrepareMockedClientController(t, params) - // create a Covenant key pair in the keyring - covenantConfig := covcfg.DefaultConfig() covenantConfig.BabylonConfig.KeyDirectory = t.TempDir() covKeyPair, err := keyring.CreateCovenantKey( @@ -58,8 +61,7 @@ func FuzzAddCovenantSig(f *testing.F) { ) require.NoError(t, err) - signer, err := keyring.NewKeyringSigner(covenantConfig.BabylonConfig.ChainID, covenantConfig.BabylonConfig.Key, covenantConfig.BabylonConfig.KeyDirectory, covenantConfig.BabylonConfig.KeyringBackend, passphrase) - require.NoError(t, err) + signer := remotesigner.NewRemoteSigner(covenantConfig.RemoteSigner) // create and start covenant emulator ce, err := covenant.NewCovenantEmulator(&covenantConfig, mockClientController, zap.NewNop(), signer) diff --git a/itest/babylon_node_handler.go b/itest/babylon_node_handler.go index 7f7e5bf..5b20a6e 100644 --- a/itest/babylon_node_handler.go +++ b/itest/babylon_node_handler.go @@ -19,6 +19,8 @@ import ( "github.com/stretchr/testify/require" ) +var chainID = "chain-test" + type babylonNode struct { cmd *exec.Cmd pidFile string diff --git a/itest/test_manager.go b/itest/test_manager.go deleted file mode 100644 index 7158277..0000000 --- a/itest/test_manager.go +++ /dev/null @@ -1,483 +0,0 @@ -package e2etest - -import ( - "context" - "math/rand" - "os" - "sync" - "testing" - "time" - - "github.com/babylonlabs-io/babylon/testutil/datagen" - bbntypes "github.com/babylonlabs-io/babylon/types" - btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" - btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" - bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" - covcc "github.com/babylonlabs-io/covenant-emulator/clientcontroller" - covcfg "github.com/babylonlabs-io/covenant-emulator/config" - "github.com/babylonlabs-io/covenant-emulator/covenant" - signerCfg "github.com/babylonlabs-io/covenant-emulator/covenant-signer/config" - "github.com/babylonlabs-io/covenant-emulator/covenant-signer/keystore/cosmos" - signerMetrics "github.com/babylonlabs-io/covenant-emulator/covenant-signer/observability/metrics" - signerApp "github.com/babylonlabs-io/covenant-emulator/covenant-signer/signerapp" - "github.com/babylonlabs-io/covenant-emulator/covenant-signer/signerservice" - signerService "github.com/babylonlabs-io/covenant-emulator/covenant-signer/signerservice" - covdkeyring "github.com/babylonlabs-io/covenant-emulator/keyring" - "github.com/babylonlabs-io/covenant-emulator/remotesigner" - "github.com/babylonlabs-io/covenant-emulator/testutil" - "github.com/babylonlabs-io/covenant-emulator/types" - "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/chaincfg" - "github.com/btcsuite/btcd/wire" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - "github.com/stretchr/testify/require" - "go.uber.org/zap" -) - -var ( - eventuallyWaitTimeOut = 1 * time.Minute - eventuallyPollTime = 500 * time.Millisecond - btcNetworkParams = &chaincfg.SimNetParams - - covenantKeyName = "covenant-key" - chainID = "chain-test" - passphrase = "testpass" - hdPath = "" -) - -type TestManager struct { - Wg sync.WaitGroup - BabylonHandler *BabylonNodeHandler - CovenantEmulator *covenant.CovenantEmulator - CovenanConfig *covcfg.Config - CovBBNClient *covcc.BabylonController - StakingParams *types.StakingParams - baseDir string -} - -type TestDelegationData struct { - DelegatorPrivKey *btcec.PrivateKey - DelegatorKey *btcec.PublicKey - SlashingTx *bstypes.BTCSlashingTx - StakingTx *wire.MsgTx - StakingTxInfo *btcctypes.TransactionInfo - DelegatorSig *bbntypes.BIP340Signature - FpPks []*btcec.PublicKey - - SlashingPkScript []byte - StakingTime uint16 - StakingAmount int64 -} - -type testFinalityProviderData struct { - BabylonAddress sdk.AccAddress - BtcPrivKey *btcec.PrivateKey - BtcKey *btcec.PublicKey - PoP *bstypes.ProofOfPossessionBTC -} - -func StartManager(t *testing.T, useRemoteSigner bool) *TestManager { - testDir, err := baseDir("cee2etest") - require.NoError(t, err) - - logger := zap.NewNop() - covenantConfig := defaultCovenantConfig(testDir) - err = covenantConfig.Validate() - require.NoError(t, err) - - // 1. prepare covenant key, which will be used as input of Babylon node - var signer covenant.Signer - var covPubKey *btcec.PublicKey - if useRemoteSigner { - covenantConfig.RemoteSignerEnabled = true - signerConfig := signerCfg.DefaultConfig() - signerConfig.KeyStore.CosmosKeyStore.ChainID = covenantConfig.BabylonConfig.ChainID - signerConfig.KeyStore.CosmosKeyStore.KeyName = covenantConfig.BabylonConfig.Key - signerConfig.KeyStore.CosmosKeyStore.KeyringBackend = covenantConfig.BabylonConfig.KeyringBackend - signerConfig.KeyStore.CosmosKeyStore.KeyDirectory = covenantConfig.BabylonConfig.KeyDirectory - keyRetriever, err := cosmos.NewCosmosKeyringRetriever(signerConfig.KeyStore.CosmosKeyStore) - require.NoError(t, err) - keyInfo, err := keyRetriever.Kr.CreateChainKey( - passphrase, - hdPath, - ) - require.NoError(t, err) - require.NotNil(t, keyInfo) - - app := signerApp.NewSignerApp( - keyRetriever, - ) - - met := signerMetrics.NewCovenantSignerMetrics() - parsedConfig, err := signerConfig.Parse() - require.NoError(t, err) - - server, err := signerService.New( - context.Background(), - parsedConfig, - app, - met, - ) - - require.NoError(t, err) - - signer = remotesigner.NewRemoteSigner(covenantConfig.RemoteSigner) - covPubKey = keyInfo.PublicKey - - go func() { - _ = server.Start() - }() - - // Give some time to launch server - time.Sleep(3 * time.Second) - - // unlock the signer before usage - err = signerservice.Unlock( - context.Background(), - covenantConfig.RemoteSigner.URL, - covenantConfig.RemoteSigner.Timeout, - passphrase, - ) - require.NoError(t, err) - - t.Cleanup(func() { - _ = server.Stop(context.TODO()) - }) - } else { - covKeyPair, err := covdkeyring.CreateCovenantKey(testDir, chainID, covenantKeyName, keyring.BackendTest, passphrase, hdPath) - require.NoError(t, err) - signer, err = covdkeyring.NewKeyringSigner( - covenantConfig.BabylonConfig.ChainID, - covenantConfig.BabylonConfig.Key, - covenantConfig.BabylonConfig.KeyDirectory, - covenantConfig.BabylonConfig.KeyringBackend, - passphrase, - ) - require.NoError(t, err) - covPubKey = covKeyPair.PublicKey - } - - // 2. prepare Babylon node - bh := NewBabylonNodeHandler(t, bbntypes.NewBIP340PubKeyFromBTCPK(covPubKey)) - err = bh.Start() - require.NoError(t, err) - - // 3. prepare covenant emulator - bbnCfg := defaultBBNConfigWithKey("test-spending-key", bh.GetNodeDataDir()) - covbc, err := covcc.NewBabylonController(bbnCfg, &covenantConfig.BTCNetParams, logger) - require.NoError(t, err) - - require.NoError(t, err) - - ce, err := covenant.NewCovenantEmulator(covenantConfig, covbc, logger, signer) - require.NoError(t, err) - err = ce.Start() - require.NoError(t, err) - - tm := &TestManager{ - BabylonHandler: bh, - CovenantEmulator: ce, - CovenanConfig: covenantConfig, - CovBBNClient: covbc, - baseDir: testDir, - } - - tm.WaitForServicesStart(t) - - return tm -} - -func (tm *TestManager) WaitForServicesStart(t *testing.T) { - // wait for Babylon node starts - require.Eventually(t, func() bool { - params, err := tm.CovBBNClient.QueryStakingParamsByVersion(0) - if err != nil { - return false - } - tm.StakingParams = params - return true - }, eventuallyWaitTimeOut, eventuallyPollTime) - - t.Logf("Babylon node is started") -} - -func StartManagerWithFinalityProvider(t *testing.T, n int, useRemoteSigner bool) (*TestManager, []*btcec.PublicKey) { - tm := StartManager(t, useRemoteSigner) - - var btcPks []*btcec.PublicKey - for i := 0; i < n; i++ { - fpData := genTestFinalityProviderData(t, tm.CovBBNClient.GetKeyAddress()) - btcPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpData.BtcKey) - _, err := tm.CovBBNClient.RegisterFinalityProvider( - btcPubKey, - &tm.StakingParams.MinComissionRate, - &stakingtypes.Description{ - Moniker: "tester", - }, - fpData.PoP, - ) - require.NoError(t, err) - - btcPks = append(btcPks, fpData.BtcKey) - } - - // check finality providers on Babylon side - require.Eventually(t, func() bool { - fps, err := tm.CovBBNClient.QueryFinalityProviders() - if err != nil { - t.Logf("failed to query finality providers from Babylon %s", err.Error()) - return false - } - - return len(fps) == n - }, eventuallyWaitTimeOut, eventuallyPollTime) - - t.Logf("the test manager is running with %v finality-provider(s)", n) - - return tm, btcPks -} - -func genTestFinalityProviderData(t *testing.T, babylonAddr sdk.AccAddress) *testFinalityProviderData { - finalityProviderEOTSPrivKey, err := btcec.NewPrivateKey() - require.NoError(t, err) - pop, err := bstypes.NewPoPBTC(babylonAddr, finalityProviderEOTSPrivKey) - require.NoError(t, err) - - return &testFinalityProviderData{ - BabylonAddress: babylonAddr, - BtcPrivKey: finalityProviderEOTSPrivKey, - BtcKey: finalityProviderEOTSPrivKey.PubKey(), - PoP: pop, - } -} - -func (tm *TestManager) Stop(t *testing.T) { - err := tm.CovenantEmulator.Stop() - require.NoError(t, err) - err = tm.BabylonHandler.Stop() - require.NoError(t, err) - err = os.RemoveAll(tm.baseDir) - require.NoError(t, err) -} - -func (tm *TestManager) WaitForNPendingDels(t *testing.T, n int) []*types.Delegation { - var ( - dels []*types.Delegation - err error - ) - require.Eventually(t, func() bool { - dels, err = tm.CovBBNClient.QueryPendingDelegations( - tm.CovenanConfig.DelegationLimit, - ) - if err != nil { - return false - } - return len(dels) == n - }, eventuallyWaitTimeOut, eventuallyPollTime) - - t.Logf("delegations are pending") - - return dels -} - -func (tm *TestManager) WaitForNActiveDels(t *testing.T, n int) []*types.Delegation { - var ( - dels []*types.Delegation - err error - ) - require.Eventually(t, func() bool { - dels, err = tm.CovBBNClient.QueryActiveDelegations( - tm.CovenanConfig.DelegationLimit, - ) - if err != nil { - return false - } - return len(dels) == n - }, eventuallyWaitTimeOut, eventuallyPollTime) - - t.Logf("delegations are active") - - return dels -} - -func (tm *TestManager) WaitForNVerifiedDels(t *testing.T, n int) []*types.Delegation { - var ( - dels []*types.Delegation - err error - ) - require.Eventually(t, func() bool { - dels, err = tm.CovBBNClient.QueryVerifiedDelegations( - tm.CovenanConfig.DelegationLimit, - ) - if err != nil { - return false - } - return len(dels) == n - }, eventuallyWaitTimeOut, eventuallyPollTime) - - t.Logf("delegations are verified") - - return dels -} - -// InsertBTCDelegation inserts a BTC delegation to Babylon -// isPreApproval indicates whether the delegation follows -// pre-approval flow, if so, the inclusion proof is nil -func (tm *TestManager) InsertBTCDelegation( - t *testing.T, - fpPks []*btcec.PublicKey, stakingTime uint16, stakingAmount int64, - isPreApproval bool, -) *TestDelegationData { - r := rand.New(rand.NewSource(time.Now().UnixNano())) - params := tm.StakingParams - - // delegator BTC key pairs, staking tx and slashing tx - delBtcPrivKey, delBtcPubKey, err := datagen.GenRandomBTCKeyPair(r) - require.NoError(t, err) - - unbondingTime := uint16(tm.StakingParams.UnbondingTimeBlocks) - testStakingInfo := testutil.GenBTCStakingSlashingInfo( - r, - t, - btcNetworkParams, - delBtcPrivKey, - fpPks, - params.CovenantPks, - params.CovenantQuorum, - stakingTime, - stakingAmount, - params.SlashingPkScript, - params.SlashingRate, - unbondingTime, - ) - - // proof-of-possession - pop, err := bstypes.NewPoPBTC(tm.CovBBNClient.GetKeyAddress(), delBtcPrivKey) - require.NoError(t, err) - - // create and insert BTC headers which include the staking tx to get staking tx info - currentBtcTipResp, err := tm.CovBBNClient.QueryBtcLightClientTip() - require.NoError(t, err) - tipHeader, err := bbntypes.NewBTCHeaderBytesFromHex(currentBtcTipResp.HeaderHex) - require.NoError(t, err) - blockWithStakingTx := datagen.CreateBlockWithTransaction(r, tipHeader.ToBlockHeader(), testStakingInfo.StakingTx) - accumulatedWork := btclctypes.CalcWork(&blockWithStakingTx.HeaderBytes) - accumulatedWork = btclctypes.CumulativeWork(accumulatedWork, currentBtcTipResp.Work) - parentBlockHeaderInfo := &btclctypes.BTCHeaderInfo{ - Header: &blockWithStakingTx.HeaderBytes, - Hash: blockWithStakingTx.HeaderBytes.Hash(), - Height: currentBtcTipResp.Height + 1, - Work: &accumulatedWork, - } - headers := make([]bbntypes.BTCHeaderBytes, 0) - headers = append(headers, blockWithStakingTx.HeaderBytes) - for i := 0; i < int(params.ComfirmationTimeBlocks); i++ { - headerInfo := datagen.GenRandomValidBTCHeaderInfoWithParent(r, *parentBlockHeaderInfo) - headers = append(headers, *headerInfo.Header) - parentBlockHeaderInfo = headerInfo - } - _, err = tm.CovBBNClient.InsertBtcBlockHeaders(headers) - require.NoError(t, err) - btcHeader := blockWithStakingTx.HeaderBytes - serializedStakingTx, err := bbntypes.SerializeBTCTx(testStakingInfo.StakingTx) - require.NoError(t, err) - txInfo := btcctypes.NewTransactionInfo(&btcctypes.TransactionKey{Index: 1, Hash: btcHeader.Hash()}, serializedStakingTx, blockWithStakingTx.SpvProof.MerkleNodes) - - slashingSpendInfo, err := testStakingInfo.StakingInfo.SlashingPathSpendInfo() - require.NoError(t, err) - - // delegator sig - delegatorSig, err := testStakingInfo.SlashingTx.Sign( - testStakingInfo.StakingTx, - 1, - slashingSpendInfo.GetPkScriptPath(), - delBtcPrivKey, - ) - require.NoError(t, err) - - unbondingValue := stakingAmount - 1000 - stakingTxHash := testStakingInfo.StakingTx.TxHash() - - testUnbondingInfo := datagen.GenBTCUnbondingSlashingInfo( - r, - t, - btcNetworkParams, - delBtcPrivKey, - fpPks, - params.CovenantPks, - params.CovenantQuorum, - wire.NewOutPoint(&stakingTxHash, 1), - unbondingTime, - unbondingValue, - params.SlashingPkScript, - params.SlashingRate, - unbondingTime, - ) - - unbondingTxMsg := testUnbondingInfo.UnbondingTx - - unbondingSlashingPathInfo, err := testUnbondingInfo.UnbondingInfo.SlashingPathSpendInfo() - require.NoError(t, err) - - unbondingSig, err := testUnbondingInfo.SlashingTx.Sign( - unbondingTxMsg, - 0, - unbondingSlashingPathInfo.GetPkScriptPath(), - delBtcPrivKey, - ) - require.NoError(t, err) - - serializedUnbondingTx, err := bbntypes.SerializeBTCTx(testUnbondingInfo.UnbondingTx) - require.NoError(t, err) - - // submit the BTC delegation to Babylon - _, err = tm.CovBBNClient.CreateBTCDelegation( - bbntypes.NewBIP340PubKeyFromBTCPK(delBtcPubKey), - fpPks, - pop, - uint32(stakingTime), - stakingAmount, - txInfo, - testStakingInfo.SlashingTx, - delegatorSig, - serializedUnbondingTx, - uint32(unbondingTime), - unbondingValue, - testUnbondingInfo.SlashingTx, - unbondingSig, - isPreApproval) - require.NoError(t, err) - - t.Log("successfully submitted a BTC delegation") - - return &TestDelegationData{ - DelegatorPrivKey: delBtcPrivKey, - DelegatorKey: delBtcPubKey, - FpPks: fpPks, - StakingTx: testStakingInfo.StakingTx, - SlashingTx: testStakingInfo.SlashingTx, - StakingTxInfo: txInfo, - DelegatorSig: delegatorSig, - SlashingPkScript: params.SlashingPkScript, - StakingTime: stakingTime, - StakingAmount: stakingAmount, - } -} - -func defaultBBNConfigWithKey(key, keydir string) *covcfg.BBNConfig { - bbnCfg := covcfg.DefaultBBNConfig() - bbnCfg.Key = key - bbnCfg.KeyDirectory = keydir - bbnCfg.GasAdjustment = 20 - - return &bbnCfg -} - -func defaultCovenantConfig(homeDir string) *covcfg.Config { - cfg := covcfg.DefaultConfigWithHomePath(homeDir) - cfg.BabylonConfig.KeyDirectory = homeDir - - return &cfg -} From e989e6eecf0e6780ad34edfec8b308a95b2dd096 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Tue, 14 Jan 2025 19:33:18 -0300 Subject: [PATCH 02/11] fix: test sign --- covenant/covenant_test.go | 75 +++++++++++++++++++++++++++------ docs/covenant-emulator-setup.md | 40 ++++++++---------- 2 files changed, 79 insertions(+), 36 deletions(-) diff --git a/covenant/covenant_test.go b/covenant/covenant_test.go index 5ab15c5..edb9cab 100644 --- a/covenant/covenant_test.go +++ b/covenant/covenant_test.go @@ -1,6 +1,7 @@ package covenant_test import ( + "context" "encoding/hex" "fmt" "math/rand" @@ -23,6 +24,11 @@ import ( covcfg "github.com/babylonlabs-io/covenant-emulator/config" "github.com/babylonlabs-io/covenant-emulator/covenant" + signerCfg "github.com/babylonlabs-io/covenant-emulator/covenant-signer/config" + "github.com/babylonlabs-io/covenant-emulator/covenant-signer/keystore/cosmos" + signerMetrics "github.com/babylonlabs-io/covenant-emulator/covenant-signer/observability/metrics" + signerApp "github.com/babylonlabs-io/covenant-emulator/covenant-signer/signerapp" + "github.com/babylonlabs-io/covenant-emulator/covenant-signer/signerservice" "github.com/babylonlabs-io/covenant-emulator/keyring" "github.com/babylonlabs-io/covenant-emulator/remotesigner" "github.com/babylonlabs-io/covenant-emulator/testutil" @@ -42,6 +48,61 @@ func FuzzAddCovenantSig(f *testing.F) { // create a Covenant key pair in the keyring covenantConfig := covcfg.DefaultConfig() + covenantConfig.BabylonConfig.KeyDirectory = f.TempDir() + + signerConfig := signerCfg.DefaultConfig() + signerConfig.KeyStore.CosmosKeyStore.ChainID = covenantConfig.BabylonConfig.ChainID + signerConfig.KeyStore.CosmosKeyStore.KeyName = covenantConfig.BabylonConfig.Key + signerConfig.KeyStore.CosmosKeyStore.KeyringBackend = covenantConfig.BabylonConfig.KeyringBackend + signerConfig.KeyStore.CosmosKeyStore.KeyDirectory = covenantConfig.BabylonConfig.KeyDirectory + keyRetriever, err := cosmos.NewCosmosKeyringRetriever(signerConfig.KeyStore.CosmosKeyStore) + require.NoError(f, err) + + covKeyPair, err := keyRetriever.Kr.CreateChainKey( + passphrase, + hdPath, + ) + require.NoError(f, err) + require.NotNil(f, covKeyPair) + + app := signerApp.NewSignerApp( + keyRetriever, + ) + + met := signerMetrics.NewCovenantSignerMetrics() + parsedConfig, err := signerConfig.Parse() + require.NoError(f, err) + + server, err := signerservice.New( + context.Background(), + parsedConfig, + app, + met, + ) + require.NoError(f, err) + + signer := remotesigner.NewRemoteSigner(covenantConfig.RemoteSigner) + + go func() { + _ = server.Start() + }() + + // Give some time to launch server + time.Sleep(time.Second) + + // unlock the signer before usage + err = signerservice.Unlock( + context.Background(), + covenantConfig.RemoteSigner.URL, + covenantConfig.RemoteSigner.Timeout, + passphrase, + ) + require.NoError(f, err) + + f.Cleanup(func() { + _ = server.Stop(context.TODO()) + }) + f.Fuzz(func(t *testing.T, seed int64) { t.Log("Seed", seed) r := rand.New(rand.NewSource(seed)) @@ -49,20 +110,6 @@ func FuzzAddCovenantSig(f *testing.F) { params := testutil.GenRandomParams(r, t) mockClientController := testutil.PrepareMockedClientController(t, params) - covenantConfig.BabylonConfig.KeyDirectory = t.TempDir() - - covKeyPair, err := keyring.CreateCovenantKey( - covenantConfig.BabylonConfig.KeyDirectory, - covenantConfig.BabylonConfig.ChainID, - covenantConfig.BabylonConfig.Key, - covenantConfig.BabylonConfig.KeyringBackend, - passphrase, - hdPath, - ) - require.NoError(t, err) - - signer := remotesigner.NewRemoteSigner(covenantConfig.RemoteSigner) - // create and start covenant emulator ce, err := covenant.NewCovenantEmulator(&covenantConfig, mockClientController, zap.NewNop(), signer) require.NoError(t, err) diff --git a/docs/covenant-emulator-setup.md b/docs/covenant-emulator-setup.md index 64f7df7..4bb92d6 100644 --- a/docs/covenant-emulator-setup.md +++ b/docs/covenant-emulator-setup.md @@ -3,7 +3,7 @@ This document outlines the setup of the covenant-emulator daemon program. -## Table of Contents +## Table of Contents 1. [Prerequesites](#1-prerequisites) 2. [Install Covenant Emulator Binary](#2-install-covenant-emulator-binary) @@ -17,20 +17,20 @@ daemon program. To successfully complete this guide, you will need: -1. A running instance of the [covenant signer](../covenant-signer) - with the url that you configured it to. Please follow the - [covenant signer setup guide](./covenant-signer-setup.md) to +1. A running instance of the [covenant signer](../covenant-signer) + with the url that you configured it to. Please follow the + [covenant signer setup guide](./covenant-signer-setup.md) to complete the setup of the covenant signer with your keys before proceeding. Note that the phase-2 covenant-signer program is a different one than the one used doing phase-1 -2. A connection to a Babylon node. To run your own node, please refer to the +2. A connection to a Babylon node. To run your own node, please refer to the [Babylon Node Setup Guide](https://github.com/babylonlabs-io/networks/blob/main/bbn-test-5/bbn-test-5/babylon-node/README.md). ## 2. Install covenant emulator binary If you haven't already, download [Golang 1.23](https://go.dev/dl). -Once installed run: +Once installed run: ```shell go version @@ -69,8 +69,8 @@ echo 'export PATH=$HOME/go/bin:$PATH' >> ~/.profile ### 3.1. Initialize directories -Next, we initialize the node and home directory. It should generate all of the -necessary files such as `covd.config`, these files will live in the `` +Next, we initialize the node and home directory. It should generate all of the +necessary files such as `covd.config`, these files will live in the `` that you set for the `--home` with the below command. ```shell @@ -87,8 +87,8 @@ $ ls ### 3.2. Configure the covenant emulator -As you have already set up the covenant signer, you can now configure the covenant -emulator to use it. +As you have already set up the covenant signer, you can now configure the covenant +emulator to use it. Use the following parameters to configure the `covd.conf` file. @@ -127,9 +127,6 @@ URL = http://127.0.0.1:9792 ; client when making requests to the remote signer Timeout = 2s - -; if true, covenant will use the remote signer to sign transactions -RemoteSignerEnabled = true ``` Below are brief explanations of the configuration entries: @@ -144,15 +141,14 @@ Below are brief explanations of the configuration entries: - `KeyringBackend` - Storage backend for the keyring (os, file, kwallet, pass, test, memory) - `URL` - Endpoint where the remote signing service is running - `Timeout` - Maximum time to wait for remote signer responses -- `RemoteSignerEnabled` - Whether to use the remote signing service -Ensure that the covenant signer is running and unlocked before proceeding +Ensure that the covenant signer is running and unlocked before proceeding otherwise you will be unable to run the emulator. ## 4. Generate key pairs -The covenant emulator daemon requires the existence of a Babylon keyring that -signs signatures and interacts with Babylon. Use the following command to generate +The covenant emulator daemon requires the existence of a Babylon keyring that +signs signatures and interacts with Babylon. Use the following command to generate the key: ```bash @@ -167,20 +163,20 @@ covd create-key --key-name --chain-id --keyring-backend Date: Tue, 14 Jan 2025 19:37:23 -0300 Subject: [PATCH 03/11] chore: removed unnecessary funcs --- keyring/keyring.go | 27 ------- keyring/keyringcontroller.go | 12 --- keyring/signer.go | 145 ----------------------------------- 3 files changed, 184 deletions(-) delete mode 100644 keyring/signer.go diff --git a/keyring/keyring.go b/keyring/keyring.go index efefdd8..cbcd7b2 100644 --- a/keyring/keyring.go +++ b/keyring/keyring.go @@ -1,42 +1,15 @@ package keyring import ( - "fmt" "os" "path" - "strings" "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/crypto/keyring" "github.com/babylonlabs-io/covenant-emulator/codec" "github.com/babylonlabs-io/covenant-emulator/types" ) -func CreateKeyring(keyringDir string, chainId string, backend string, input *strings.Reader) (keyring.Keyring, error) { - ctx, err := CreateClientCtx(keyringDir, chainId) - if err != nil { - return nil, err - } - - if backend == "" { - return nil, fmt.Errorf("the keyring backend should not be empty") - } - - kr, err := keyring.New( - ctx.ChainID, - backend, - ctx.KeyringDir, - input, - ctx.Codec, - ctx.KeyringOptions...) - if err != nil { - return nil, fmt.Errorf("failed to create keyring: %w", err) - } - - return kr, nil -} - func CreateClientCtx(keyringDir string, chainId string) (client.Context, error) { var err error var homeDir string diff --git a/keyring/keyringcontroller.go b/keyring/keyringcontroller.go index 5654116..78d7d06 100644 --- a/keyring/keyringcontroller.go +++ b/keyring/keyringcontroller.go @@ -53,18 +53,6 @@ func NewChainKeyringController(ctx client.Context, name, keyringBackend string) }, nil } -func NewChainKeyringControllerWithKeyring(kr keyring.Keyring, name string, input *strings.Reader) (*ChainKeyringController, error) { - if name == "" { - return nil, fmt.Errorf("the key name should not be empty") - } - - return &ChainKeyringController{ - kr: kr, - keyName: name, - input: input, - }, nil -} - func (kc *ChainKeyringController) GetKeyring() keyring.Keyring { return kc.kr } diff --git a/keyring/signer.go b/keyring/signer.go deleted file mode 100644 index a53aa6d..0000000 --- a/keyring/signer.go +++ /dev/null @@ -1,145 +0,0 @@ -package keyring - -import ( - "fmt" - "strings" - - "github.com/babylonlabs-io/babylon/btcstaking" - "github.com/babylonlabs-io/covenant-emulator/covenant" - - asig "github.com/babylonlabs-io/babylon/crypto/schnorr-adaptor-signature" - "github.com/btcsuite/btcd/btcec/v2" - "github.com/btcsuite/btcd/btcec/v2/schnorr" - secp "github.com/decred/dcrd/dcrec/secp256k1/v4" -) - -var _ covenant.Signer = KeyringSigner{} - -type KeyringSigner struct { - kc *ChainKeyringController - passphrase string -} - -func NewKeyringSigner(chainId, keyName, keyringDir, keyringBackend, passphrase string) (*KeyringSigner, error) { - input := strings.NewReader("") - kr, err := CreateKeyring(keyringDir, chainId, keyringBackend, input) - if err != nil { - return nil, fmt.Errorf("failed to create keyring: %w", err) - } - - kc, err := NewChainKeyringControllerWithKeyring(kr, keyName, input) - if err != nil { - return nil, err - } - - return &KeyringSigner{ - kc: kc, - passphrase: passphrase, - }, nil -} - -func (kcs KeyringSigner) PubKey() (*secp.PublicKey, error) { - record, err := kcs.kc.KeyRecord() - if err != nil { - return nil, err - } - - pubKey, err := record.GetPubKey() - if err != nil { - return nil, err - } - - return btcec.ParsePubKey(pubKey.Bytes()) -} - -// getPrivKey returns the keyring private key -// TODO: update btcstaking functions to avoid receiving private key as parameter -// and only sign it using the kcs.kc.GetKeyring().Sign() -func (kcs KeyringSigner) getPrivKey() (*btcec.PrivateKey, error) { - sdkPrivKey, err := kcs.kc.GetChainPrivKey(kcs.passphrase) - if err != nil { - return nil, err - } - - privKey, _ := btcec.PrivKeyFromBytes(sdkPrivKey.Key) - return privKey, nil -} - -// SignTransactions receives BTC delegation transactions to sign and returns all the signatures needed if nothing fails. -func (kcs KeyringSigner) SignTransactions(req covenant.SigningRequest) (*covenant.SignaturesResponse, error) { - covenantPrivKey, err := kcs.getPrivKey() - if err != nil { - return nil, fmt.Errorf("failed to get Covenant private key: %w", err) - } - - slashSigs := make([][]byte, 0, len(req.FpEncKeys)) - slashUnbondingSigs := make([][]byte, 0, len(req.FpEncKeys)) - for _, fpEncKey := range req.FpEncKeys { - slashSig, slashUnbondingSig, err := slashUnbondSig(covenantPrivKey, req, fpEncKey) - if err != nil { - return nil, err - } - - slashSigs = append(slashSigs, slashSig.MustMarshal()) - slashUnbondingSigs = append(slashUnbondingSigs, slashUnbondingSig.MustMarshal()) - } - - unbondingSig, err := unbondSig(covenantPrivKey, req) - if err != nil { - return nil, err - } - - return &covenant.SignaturesResponse{ - SlashSigs: slashSigs, - UnbondingSig: unbondingSig, - SlashUnbondingSigs: slashUnbondingSigs, - }, nil -} - -func slashUnbondSig( - covenantPrivKey *secp.PrivateKey, - signingTxReq covenant.SigningRequest, - fpEncKey *asig.EncryptionKey, -) (slashSig, slashUnbondingSig *asig.AdaptorSignature, err error) { - // creates slash sigs - slashSig, err = btcstaking.EncSignTxWithOneScriptSpendInputStrict( - signingTxReq.SlashingTx, - signingTxReq.StakingTx, - signingTxReq.StakingOutputIdx, - signingTxReq.SlashingPkScriptPath, - covenantPrivKey, - fpEncKey, - ) - if err != nil { - return nil, nil, fmt.Errorf("failed to sign adaptor slash signature with finality provider public key %s: %w", fpEncKey.ToBytes(), err) - } - - // creates slash unbonding sig - slashUnbondingSig, err = btcstaking.EncSignTxWithOneScriptSpendInputStrict( - signingTxReq.SlashUnbondingTx, - signingTxReq.UnbondingTx, - 0, // 0th output is always the unbonding script output - signingTxReq.UnbondingTxSlashingPkScriptPath, - covenantPrivKey, - fpEncKey, - ) - if err != nil { - return nil, nil, fmt.Errorf("failed to sign adaptor slash unbonding signature with finality provider public key %s: %w", fpEncKey.ToBytes(), err) - } - - return slashSig, slashUnbondingSig, nil -} - -func unbondSig(covenantPrivKey *secp.PrivateKey, signingTxReq covenant.SigningRequest) (*schnorr.Signature, error) { - unbondingSig, err := btcstaking.SignTxWithOneScriptSpendInputStrict( - signingTxReq.UnbondingTx, - signingTxReq.StakingTx, - signingTxReq.StakingOutputIdx, - signingTxReq.StakingTxUnbondingPkScriptPath, - covenantPrivKey, - ) - if err != nil { - return nil, fmt.Errorf("failed to sign unbonding tx: %w", err) - } - return unbondingSig, nil -} From 2d810a9be27d865eb3351a145acb0b4074df6afd Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Tue, 14 Jan 2025 19:38:49 -0300 Subject: [PATCH 04/11] chore: add #95 to changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a668a38..15ac9b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Improvements * [#83](https://github.com/babylonlabs-io/covenant-emulator/pull/83) covenant-signer: remove go.mod +* [#95](https://github.com/babylonlabs-io/covenant-emulator/pull/95) removed local signer option +as the covenant emulator should only connect to a remote signer ## v0.11.3 From 135811e8133ed8ffa71492dd0f829e25b4eda9ac Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Wed, 15 Jan 2025 08:56:41 -0300 Subject: [PATCH 05/11] chore: add back the TestManager --- itest/babylon_node_handler.go | 2 - itest/test_manager.go | 464 ++++++++++++++++++++++++++++++++++ 2 files changed, 464 insertions(+), 2 deletions(-) create mode 100644 itest/test_manager.go diff --git a/itest/babylon_node_handler.go b/itest/babylon_node_handler.go index 5b20a6e..7f7e5bf 100644 --- a/itest/babylon_node_handler.go +++ b/itest/babylon_node_handler.go @@ -19,8 +19,6 @@ import ( "github.com/stretchr/testify/require" ) -var chainID = "chain-test" - type babylonNode struct { cmd *exec.Cmd pidFile string diff --git a/itest/test_manager.go b/itest/test_manager.go new file mode 100644 index 0000000..706610c --- /dev/null +++ b/itest/test_manager.go @@ -0,0 +1,464 @@ +package e2etest + +import ( + "context" + "math/rand" + "os" + "sync" + "testing" + "time" + + "github.com/babylonlabs-io/babylon/testutil/datagen" + bbntypes "github.com/babylonlabs-io/babylon/types" + btcctypes "github.com/babylonlabs-io/babylon/x/btccheckpoint/types" + btclctypes "github.com/babylonlabs-io/babylon/x/btclightclient/types" + bstypes "github.com/babylonlabs-io/babylon/x/btcstaking/types" + covcc "github.com/babylonlabs-io/covenant-emulator/clientcontroller" + covcfg "github.com/babylonlabs-io/covenant-emulator/config" + "github.com/babylonlabs-io/covenant-emulator/covenant" + signerCfg "github.com/babylonlabs-io/covenant-emulator/covenant-signer/config" + "github.com/babylonlabs-io/covenant-emulator/covenant-signer/keystore/cosmos" + signerMetrics "github.com/babylonlabs-io/covenant-emulator/covenant-signer/observability/metrics" + signerApp "github.com/babylonlabs-io/covenant-emulator/covenant-signer/signerapp" + "github.com/babylonlabs-io/covenant-emulator/covenant-signer/signerservice" + signerService "github.com/babylonlabs-io/covenant-emulator/covenant-signer/signerservice" + "github.com/babylonlabs-io/covenant-emulator/remotesigner" + "github.com/babylonlabs-io/covenant-emulator/testutil" + "github.com/babylonlabs-io/covenant-emulator/types" + "github.com/btcsuite/btcd/btcec/v2" + "github.com/btcsuite/btcd/chaincfg" + "github.com/btcsuite/btcd/wire" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/stretchr/testify/require" + "go.uber.org/zap" +) + +var ( + eventuallyWaitTimeOut = 1 * time.Minute + eventuallyPollTime = 500 * time.Millisecond + btcNetworkParams = &chaincfg.SimNetParams + + covenantKeyName = "covenant-key" + chainID = "chain-test" + passphrase = "testpass" + hdPath = "" +) + +type TestManager struct { + Wg sync.WaitGroup + BabylonHandler *BabylonNodeHandler + CovenantEmulator *covenant.CovenantEmulator + CovenanConfig *covcfg.Config + CovBBNClient *covcc.BabylonController + StakingParams *types.StakingParams + baseDir string +} + +type TestDelegationData struct { + DelegatorPrivKey *btcec.PrivateKey + DelegatorKey *btcec.PublicKey + SlashingTx *bstypes.BTCSlashingTx + StakingTx *wire.MsgTx + StakingTxInfo *btcctypes.TransactionInfo + DelegatorSig *bbntypes.BIP340Signature + FpPks []*btcec.PublicKey + + SlashingPkScript []byte + StakingTime uint16 + StakingAmount int64 +} + +type testFinalityProviderData struct { + BabylonAddress sdk.AccAddress + BtcPrivKey *btcec.PrivateKey + BtcKey *btcec.PublicKey + PoP *bstypes.ProofOfPossessionBTC +} + +func StartManager(t *testing.T, useRemoteSigner bool) *TestManager { + testDir, err := baseDir("cee2etest") + require.NoError(t, err) + + logger := zap.NewNop() + covenantConfig := defaultCovenantConfig(testDir) + err = covenantConfig.Validate() + require.NoError(t, err) + + // 1. prepare covenant key, which will be used as input of Babylon node + signerConfig := signerCfg.DefaultConfig() + signerConfig.KeyStore.CosmosKeyStore.ChainID = covenantConfig.BabylonConfig.ChainID + signerConfig.KeyStore.CosmosKeyStore.KeyName = covenantConfig.BabylonConfig.Key + signerConfig.KeyStore.CosmosKeyStore.KeyringBackend = covenantConfig.BabylonConfig.KeyringBackend + signerConfig.KeyStore.CosmosKeyStore.KeyDirectory = covenantConfig.BabylonConfig.KeyDirectory + keyRetriever, err := cosmos.NewCosmosKeyringRetriever(signerConfig.KeyStore.CosmosKeyStore) + require.NoError(t, err) + keyInfo, err := keyRetriever.Kr.CreateChainKey( + passphrase, + hdPath, + ) + require.NoError(t, err) + require.NotNil(t, keyInfo) + + app := signerApp.NewSignerApp( + keyRetriever, + ) + + met := signerMetrics.NewCovenantSignerMetrics() + parsedConfig, err := signerConfig.Parse() + require.NoError(t, err) + + server, err := signerService.New( + context.Background(), + parsedConfig, + app, + met, + ) + + require.NoError(t, err) + + signer := remotesigner.NewRemoteSigner(covenantConfig.RemoteSigner) + covPubKey := keyInfo.PublicKey + + go func() { + _ = server.Start() + }() + + // Give some time to launch server + time.Sleep(3 * time.Second) + + // unlock the signer before usage + err = signerservice.Unlock( + context.Background(), + covenantConfig.RemoteSigner.URL, + covenantConfig.RemoteSigner.Timeout, + passphrase, + ) + require.NoError(t, err) + + t.Cleanup(func() { + _ = server.Stop(context.TODO()) + }) + + // 2. prepare Babylon node + bh := NewBabylonNodeHandler(t, bbntypes.NewBIP340PubKeyFromBTCPK(covPubKey)) + err = bh.Start() + require.NoError(t, err) + + // 3. prepare covenant emulator + bbnCfg := defaultBBNConfigWithKey("test-spending-key", bh.GetNodeDataDir()) + covbc, err := covcc.NewBabylonController(bbnCfg, &covenantConfig.BTCNetParams, logger) + require.NoError(t, err) + + require.NoError(t, err) + + ce, err := covenant.NewCovenantEmulator(covenantConfig, covbc, logger, signer) + require.NoError(t, err) + err = ce.Start() + require.NoError(t, err) + + tm := &TestManager{ + BabylonHandler: bh, + CovenantEmulator: ce, + CovenanConfig: covenantConfig, + CovBBNClient: covbc, + baseDir: testDir, + } + + tm.WaitForServicesStart(t) + + return tm +} + +func (tm *TestManager) WaitForServicesStart(t *testing.T) { + // wait for Babylon node starts + require.Eventually(t, func() bool { + params, err := tm.CovBBNClient.QueryStakingParamsByVersion(0) + if err != nil { + return false + } + tm.StakingParams = params + return true + }, eventuallyWaitTimeOut, eventuallyPollTime) + + t.Logf("Babylon node is started") +} + +func StartManagerWithFinalityProvider(t *testing.T, n int, useRemoteSigner bool) (*TestManager, []*btcec.PublicKey) { + tm := StartManager(t, useRemoteSigner) + + var btcPks []*btcec.PublicKey + for i := 0; i < n; i++ { + fpData := genTestFinalityProviderData(t, tm.CovBBNClient.GetKeyAddress()) + btcPubKey := bbntypes.NewBIP340PubKeyFromBTCPK(fpData.BtcKey) + _, err := tm.CovBBNClient.RegisterFinalityProvider( + btcPubKey, + &tm.StakingParams.MinComissionRate, + &stakingtypes.Description{ + Moniker: "tester", + }, + fpData.PoP, + ) + require.NoError(t, err) + + btcPks = append(btcPks, fpData.BtcKey) + } + + // check finality providers on Babylon side + require.Eventually(t, func() bool { + fps, err := tm.CovBBNClient.QueryFinalityProviders() + if err != nil { + t.Logf("failed to query finality providers from Babylon %s", err.Error()) + return false + } + + return len(fps) == n + }, eventuallyWaitTimeOut, eventuallyPollTime) + + t.Logf("the test manager is running with %v finality-provider(s)", n) + + return tm, btcPks +} + +func genTestFinalityProviderData(t *testing.T, babylonAddr sdk.AccAddress) *testFinalityProviderData { + finalityProviderEOTSPrivKey, err := btcec.NewPrivateKey() + require.NoError(t, err) + pop, err := bstypes.NewPoPBTC(babylonAddr, finalityProviderEOTSPrivKey) + require.NoError(t, err) + + return &testFinalityProviderData{ + BabylonAddress: babylonAddr, + BtcPrivKey: finalityProviderEOTSPrivKey, + BtcKey: finalityProviderEOTSPrivKey.PubKey(), + PoP: pop, + } +} + +func (tm *TestManager) Stop(t *testing.T) { + err := tm.CovenantEmulator.Stop() + require.NoError(t, err) + err = tm.BabylonHandler.Stop() + require.NoError(t, err) + err = os.RemoveAll(tm.baseDir) + require.NoError(t, err) +} + +func (tm *TestManager) WaitForNPendingDels(t *testing.T, n int) []*types.Delegation { + var ( + dels []*types.Delegation + err error + ) + require.Eventually(t, func() bool { + dels, err = tm.CovBBNClient.QueryPendingDelegations( + tm.CovenanConfig.DelegationLimit, + ) + if err != nil { + return false + } + return len(dels) == n + }, eventuallyWaitTimeOut, eventuallyPollTime) + + t.Logf("delegations are pending") + + return dels +} + +func (tm *TestManager) WaitForNActiveDels(t *testing.T, n int) []*types.Delegation { + var ( + dels []*types.Delegation + err error + ) + require.Eventually(t, func() bool { + dels, err = tm.CovBBNClient.QueryActiveDelegations( + tm.CovenanConfig.DelegationLimit, + ) + if err != nil { + return false + } + return len(dels) == n + }, eventuallyWaitTimeOut, eventuallyPollTime) + + t.Logf("delegations are active") + + return dels +} + +func (tm *TestManager) WaitForNVerifiedDels(t *testing.T, n int) []*types.Delegation { + var ( + dels []*types.Delegation + err error + ) + require.Eventually(t, func() bool { + dels, err = tm.CovBBNClient.QueryVerifiedDelegations( + tm.CovenanConfig.DelegationLimit, + ) + if err != nil { + return false + } + return len(dels) == n + }, eventuallyWaitTimeOut, eventuallyPollTime) + + t.Logf("delegations are verified") + + return dels +} + +// InsertBTCDelegation inserts a BTC delegation to Babylon +// isPreApproval indicates whether the delegation follows +// pre-approval flow, if so, the inclusion proof is nil +func (tm *TestManager) InsertBTCDelegation( + t *testing.T, + fpPks []*btcec.PublicKey, stakingTime uint16, stakingAmount int64, + isPreApproval bool, +) *TestDelegationData { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + params := tm.StakingParams + + // delegator BTC key pairs, staking tx and slashing tx + delBtcPrivKey, delBtcPubKey, err := datagen.GenRandomBTCKeyPair(r) + require.NoError(t, err) + + unbondingTime := uint16(tm.StakingParams.UnbondingTimeBlocks) + testStakingInfo := testutil.GenBTCStakingSlashingInfo( + r, + t, + btcNetworkParams, + delBtcPrivKey, + fpPks, + params.CovenantPks, + params.CovenantQuorum, + stakingTime, + stakingAmount, + params.SlashingPkScript, + params.SlashingRate, + unbondingTime, + ) + + // proof-of-possession + pop, err := bstypes.NewPoPBTC(tm.CovBBNClient.GetKeyAddress(), delBtcPrivKey) + require.NoError(t, err) + + // create and insert BTC headers which include the staking tx to get staking tx info + currentBtcTipResp, err := tm.CovBBNClient.QueryBtcLightClientTip() + require.NoError(t, err) + tipHeader, err := bbntypes.NewBTCHeaderBytesFromHex(currentBtcTipResp.HeaderHex) + require.NoError(t, err) + blockWithStakingTx := datagen.CreateBlockWithTransaction(r, tipHeader.ToBlockHeader(), testStakingInfo.StakingTx) + accumulatedWork := btclctypes.CalcWork(&blockWithStakingTx.HeaderBytes) + accumulatedWork = btclctypes.CumulativeWork(accumulatedWork, currentBtcTipResp.Work) + parentBlockHeaderInfo := &btclctypes.BTCHeaderInfo{ + Header: &blockWithStakingTx.HeaderBytes, + Hash: blockWithStakingTx.HeaderBytes.Hash(), + Height: currentBtcTipResp.Height + 1, + Work: &accumulatedWork, + } + headers := make([]bbntypes.BTCHeaderBytes, 0) + headers = append(headers, blockWithStakingTx.HeaderBytes) + for i := 0; i < int(params.ComfirmationTimeBlocks); i++ { + headerInfo := datagen.GenRandomValidBTCHeaderInfoWithParent(r, *parentBlockHeaderInfo) + headers = append(headers, *headerInfo.Header) + parentBlockHeaderInfo = headerInfo + } + _, err = tm.CovBBNClient.InsertBtcBlockHeaders(headers) + require.NoError(t, err) + btcHeader := blockWithStakingTx.HeaderBytes + serializedStakingTx, err := bbntypes.SerializeBTCTx(testStakingInfo.StakingTx) + require.NoError(t, err) + txInfo := btcctypes.NewTransactionInfo(&btcctypes.TransactionKey{Index: 1, Hash: btcHeader.Hash()}, serializedStakingTx, blockWithStakingTx.SpvProof.MerkleNodes) + + slashingSpendInfo, err := testStakingInfo.StakingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + + // delegator sig + delegatorSig, err := testStakingInfo.SlashingTx.Sign( + testStakingInfo.StakingTx, + 1, + slashingSpendInfo.GetPkScriptPath(), + delBtcPrivKey, + ) + require.NoError(t, err) + + unbondingValue := stakingAmount - 1000 + stakingTxHash := testStakingInfo.StakingTx.TxHash() + + testUnbondingInfo := datagen.GenBTCUnbondingSlashingInfo( + r, + t, + btcNetworkParams, + delBtcPrivKey, + fpPks, + params.CovenantPks, + params.CovenantQuorum, + wire.NewOutPoint(&stakingTxHash, 1), + unbondingTime, + unbondingValue, + params.SlashingPkScript, + params.SlashingRate, + unbondingTime, + ) + + unbondingTxMsg := testUnbondingInfo.UnbondingTx + + unbondingSlashingPathInfo, err := testUnbondingInfo.UnbondingInfo.SlashingPathSpendInfo() + require.NoError(t, err) + + unbondingSig, err := testUnbondingInfo.SlashingTx.Sign( + unbondingTxMsg, + 0, + unbondingSlashingPathInfo.GetPkScriptPath(), + delBtcPrivKey, + ) + require.NoError(t, err) + + serializedUnbondingTx, err := bbntypes.SerializeBTCTx(testUnbondingInfo.UnbondingTx) + require.NoError(t, err) + + // submit the BTC delegation to Babylon + _, err = tm.CovBBNClient.CreateBTCDelegation( + bbntypes.NewBIP340PubKeyFromBTCPK(delBtcPubKey), + fpPks, + pop, + uint32(stakingTime), + stakingAmount, + txInfo, + testStakingInfo.SlashingTx, + delegatorSig, + serializedUnbondingTx, + uint32(unbondingTime), + unbondingValue, + testUnbondingInfo.SlashingTx, + unbondingSig, + isPreApproval) + require.NoError(t, err) + + t.Log("successfully submitted a BTC delegation") + + return &TestDelegationData{ + DelegatorPrivKey: delBtcPrivKey, + DelegatorKey: delBtcPubKey, + FpPks: fpPks, + StakingTx: testStakingInfo.StakingTx, + SlashingTx: testStakingInfo.SlashingTx, + StakingTxInfo: txInfo, + DelegatorSig: delegatorSig, + SlashingPkScript: params.SlashingPkScript, + StakingTime: stakingTime, + StakingAmount: stakingAmount, + } +} + +func defaultBBNConfigWithKey(key, keydir string) *covcfg.BBNConfig { + bbnCfg := covcfg.DefaultBBNConfig() + bbnCfg.Key = key + bbnCfg.KeyDirectory = keydir + bbnCfg.GasAdjustment = 20 + + return &bbnCfg +} + +func defaultCovenantConfig(homeDir string) *covcfg.Config { + cfg := covcfg.DefaultConfigWithHomePath(homeDir) + cfg.BabylonConfig.KeyDirectory = homeDir + + return &cfg +} From 07e3d80d6b0fd66eb40166c52fc5b1bff8a7cda7 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Wed, 15 Jan 2025 08:58:26 -0300 Subject: [PATCH 06/11] fix: lint removed unused var --- itest/test_manager.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/itest/test_manager.go b/itest/test_manager.go index 706610c..9c94cde 100644 --- a/itest/test_manager.go +++ b/itest/test_manager.go @@ -39,10 +39,9 @@ var ( eventuallyPollTime = 500 * time.Millisecond btcNetworkParams = &chaincfg.SimNetParams - covenantKeyName = "covenant-key" - chainID = "chain-test" - passphrase = "testpass" - hdPath = "" + chainID = "chain-test" + passphrase = "testpass" + hdPath = "" ) type TestManager struct { From 66ec7db6dcd188ce655919d16d77d1bb357b1013 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Wed, 15 Jan 2025 09:40:07 -0300 Subject: [PATCH 07/11] chore: address pr comments --- docs/covenant-emulator-setup.md | 13 ++--- docs/covenant-signer-setup.md | 92 ++++++++++++++++----------------- 2 files changed, 51 insertions(+), 54 deletions(-) diff --git a/docs/covenant-emulator-setup.md b/docs/covenant-emulator-setup.md index 4bb92d6..653fcf3 100644 --- a/docs/covenant-emulator-setup.md +++ b/docs/covenant-emulator-setup.md @@ -30,7 +30,7 @@ To successfully complete this guide, you will need: If you haven't already, download [Golang 1.23](https://go.dev/dl). -Once installed run: +Once installed, run: ```shell go version @@ -69,8 +69,8 @@ echo 'export PATH=$HOME/go/bin:$PATH' >> ~/.profile ### 3.1. Initialize directories -Next, we initialize the node and home directory. It should generate all of the -necessary files such as `covd.config`, these files will live in the `` +Next, initialize the node and home directory by generating all of the +necessary files such as `covd.conf`. These files will live in the `` that you set for the `--home` with the below command. ```shell @@ -87,9 +87,6 @@ $ ls ### 3.2. Configure the covenant emulator -As you have already set up the covenant signer, you can now configure the covenant -emulator to use it. - Use the following parameters to configure the `covd.conf` file. ``` @@ -142,8 +139,8 @@ Below are brief explanations of the configuration entries: - `URL` - Endpoint where the remote signing service is running - `Timeout` - Maximum time to wait for remote signer responses -Ensure that the covenant signer is running and unlocked before proceeding -otherwise you will be unable to run the emulator. +Ensure that the covenant signer is running and unlocked before proceeding. +Otherwise, you will be unable to run the emulator. ## 4. Generate key pairs diff --git a/docs/covenant-signer-setup.md b/docs/covenant-signer-setup.md index 2c9dc42..65a2d9b 100644 --- a/docs/covenant-signer-setup.md +++ b/docs/covenant-signer-setup.md @@ -9,7 +9,7 @@ covenant committee member and producing the necessary cryptographic signatures. It prioritizes security through isolation, ensuring that private key handling -is confined to an instance with minimal connectivity and simpler application +is confined to an instance with minimal connectivity and simpler application logic. > **⚡ Note:** This program is a separate implementation from the @@ -40,20 +40,20 @@ Schnorr adaptor signatures required for covenant operations. This guide requires that: -1. You have a Bitcoin node setup to load your wallet and retrieve +1. You have a Bitcoin node setup to load your wallet and retrieve your master private key. 2. You have access to the private Bitcoin key you set up your covenant with. -3. A connection to a Babylon node. To run your own node, please refer to the +3. A connection to a Babylon node. To run your own node, please refer to the [Babylon Node Setup Guide](https://github.com/babylonlabs-io/networks/blob/main/bbn-test-5/babylon-node/README.md). -For a refresher on setting up the Bitcoin node, refer to the +For a refresher on setting up the Bitcoin node, refer to the [deployment guide of your phase-1 covenant signer setup](https://github.com/babylonlabs-io/covenant-signer/blob/main/docs/deployment.md#2-bitcoind-setup). ## 2. Shell Configuration -For security when entering sensitive commands, configure your shell to ignore +For security when entering sensitive commands, configure your shell to ignore commands that start with a space: For Bash users, please update if you are using a different shell. @@ -65,14 +65,14 @@ export HISTCONTROL=ignorespace # Then either restart your shell or run: source ~/.bashrc ``` -Please ensure that any commands that you wish to be hidden from your shell +Please ensure that any commands that you wish to be hidden from your shell history start with a space. ## 3. Installation If you haven't already, download [Golang 1.23](https://go.dev/dl). -Once installed run: +Once installed, run: ```shell go version @@ -150,16 +150,16 @@ Next, we are going to retrieve the `hdkeypath` of the Bitcoin address associated with our covenant key. We do this through the usage of the `getaddresssinfo` command -which takes your covenant Bitcoin address as a parameter. As mentioned above, +which takes your covenant Bitcoin address as a parameter. As mentioned above, you will need access to the Bitcoin key you set up your covenant with. ```shell - bitcoin-cli -datadir=./1/ getaddressinfo
| \ + bitcoin-cli -datadir=./1/ getaddressinfo
| \ jq '.hdkeypath | sub("^m/"; "") | sub("/[^/]+$"; "")' ``` -In the above command, we use the `jq` utility to extract only the relevant -`hdkeypath` information, which in this example is `84h/1h/0h/0/0` +In the above command, we use the `jq` utility to extract only the relevant +`hdkeypath` information, which in this example is `84h/1h/0h/0/0` (the initial `m/` can be ignored). Make note of the `hdkeypath` information - you'll need it later when @@ -168,19 +168,19 @@ deriving the covenant private key from the master key for verification purposes. #### Step 3: Retrieve the master private key In this step, -we are going to retrieve the **base58-encoded master private key** from the -Bitcoin wallet. This key will be used to derive the covenant private key, +we are going to retrieve the **base58-encoded master private key** from the +Bitcoin wallet. This key will be used to derive the covenant private key, which can then be imported directly into the Cosmos keyring. -The command below will list all descriptors in the wallet with private keys. -This will provide you with the descriptor needed to derive the private key. +The command below will list all descriptors in the wallet with private keys. +This will provide you with the descriptor needed to derive the private key. -Since Bitcoin wallets typically contain multiple descriptors +Since Bitcoin wallets typically contain multiple descriptors (usually 6 by default), we use `jq` to find the specific descriptor that -matches our previously saved `hdkeypath` in this example `(84h/1h/0h/0)` +matches our previously saved `hdkeypath` in this example `(84h/1h/0h/0)` and extract the master private key from it. -So, before you run this command you will need to replace the `` below +So, before you run this command you will need to replace the `` below with the one you retrieved in step 2. ```shell @@ -191,28 +191,28 @@ with the one you retrieved in step 2. ' ``` -The output will be: +The output will be: ```shell wpkh(tprv8ZgxMBicQKsPe9aCeUQgMEMy2YMZ6PHnn2iCuG12y5E8oYhYNEvUqUkNy6sJ7ViBmFUMicikHSK2LBUNPx5do5EDJBjG7puwd6azci2wEdq/84h/1h/0h/0/*)#sachkrde } ``` -As you can see above there is a concatenated string of your private key and +As you can see above there is a concatenated string of your private key and part of your `hdkeypath`. To extract the private key: 1. Remove everything outside the parentheses `wpkh(` and `)` -2. Remove the `hdkeypath` after the private key +2. Remove the `hdkeypath` after the private key (everything after and including `/`) -You'll be left with just the **base58-encoded master private key**, similar to +You'll be left with just the **base58-encoded master private key**, similar to below: ``` tprv8ZgxMBicQKsPe9aCeUQgMEMy2YMZ6PHnn2iCuG12y5E8oYhYNEvUqUkNy6sJ7ViBmFUMicikHSK2LBUNPx5do5EDJBjG7puwd6azci2wEdq ``` Now you have your **base58-encoded master private key**. -You can now pass the above information to the `covenant-signer` binary to +You can now pass the above information to the `covenant-signer` binary to derive the covenant private key from the master key using **BIP32 derivation**. Use the following command to derive the covenant private key: @@ -231,9 +231,9 @@ derived_public_key: 023a79b546c79d7f7c5ff20620d914b5cf7250631d12f6e26427ed9d3f98 ``` Parameters: -- ``: The base58-encoded master private key from your +- ``: The base58-encoded master private key from your Bitcoin wallet (first parameter) -- ``: The HD derivation path that specifies how to derive +- ``: The HD derivation path that specifies how to derive the child key (second parameter) To verify, you can execute the following: @@ -242,7 +242,7 @@ To verify, you can execute the following: bitcoin-cli getaddressinfo
| jq .publickey ``` -If the public key matches the `derived_public_key`s output from the +If the public key matches the `derived_public_key`s output from the `derive-child-key` command, the verification is successful. #### Step 4: Import the private key into a Cosmos Keyring @@ -267,9 +267,9 @@ to unlock the keyring. > **⚡ Note:** While both `os` and `file` backends are supported, the authors > of the docs have more thoroughly tested the `file` backend across > different environments. -> The `file` backend stores the private key in encrypted form -> on disk. When running `import-hex` with the `file` backend, you will be -> prompted for a passphrase. This passphrase will be required to unlock the +> The `file` backend stores the private key in encrypted form +> on disk. When running `import-hex` with the `file` backend, you will be +> prompted for a passphrase. This passphrase will be required to unlock the > signer later. To confirm that the import was successful, run: @@ -292,25 +292,25 @@ Congratulations! You have successfully imported your key. ## 5. Operation ### 5.1. Configuration -Next, we can return to the covenant signer directory -and create your own configuration file. Use the +Next, we can return to the covenant signer directory +and create your own configuration file. Use the following command to dump the configuration template: ```shell covenant-signer dump-cfg --config ``` -This will create a configuration file, from the example configuration, +This will create a configuration file, from the example configuration, in the specified path. -Replace the placeholder values with your own -configuration. This configuration can be placed directly in the +Replace the placeholder values with your own +configuration. This configuration can be placed directly in the `covenant-signer` directory. ```toml [keystore] -# Type of keystore to use for managing private keys. Currently only -# "cosmos" is supported, which uses the Cosmos SDK keyring system for +# Type of keystore to use for managing private keys. Currently only +# "cosmos" is supported, which uses the Cosmos SDK keyring system for # secure key storage. keystore-type = "cosmos" @@ -343,8 +343,8 @@ port = 2113 Below are brief explanations of the configuration entries: - `keystore-type`: Type of keystore used. Should be set to `"cosmos"` -- `key-directory`: Path where keys are stored. Do not include the keyring - backend type in the path (e.g., use `/path/to/keys` not +- `key-directory`: Path where keys are stored. Do not include the keyring + backend type in the path (e.g., use `/path/to/keys` not `/path/to/keys/keyring-file`). - `keyring-backend`: Backend system for key management, e.g., "file", "os". - `key-name`: Name of the key used for signing transactions. @@ -362,17 +362,17 @@ We will then run the following command to start the daemon: covenant-signer start --config ./path/to/config.toml ``` -The covenant signer must be run in a secure network and only accessible by the +The covenant signer must be run in a secure network and only accessible by the covenant emulator. -Once the covenant signer is set up and unlocked, you can configure the covenant +Once the covenant signer is set up and unlocked, you can configure the covenant emulator to use it. The URL of the covenant signer is configurable (`remotesigner` section) -but in this example we use the default value of +but in this example we use the default value of `http://127.0.0.1:9791`. ### 5.3. Unlocking the key -Before you can sign transactions with the covenant key, you must unlock the +Before you can sign transactions with the covenant key, you must unlock the keyring that stores it. This happens through a `POST` request on the `v1/unlock` endpoint with a payload containing the covenant keyring passphrase. @@ -381,8 +381,8 @@ the covenant keyring passphrase. curl -X POST http://127.0.0.1:9791/v1/unlock -d '{"passphrase": ""}' ``` -> ⚡ Note: Even if you provide the passphrase in the curl command to unlock the -> keyring, the CLI configuration for starting the service will still prompt you +> ⚡ Note: Even if you provide the passphrase in the curl command to unlock the +> keyring, the CLI configuration for starting the service will still prompt you > to enter the passphrase interactively. You can sign transactions by invoking the `v1/sign-transactions` endpoint, @@ -417,8 +417,8 @@ transactions and return it in JSON format. } ``` -These signatures can then be used to verify that the transactions were signed by +These signatures can then be used to verify that the transactions were signed by the covenant key. -Congratulations! You have successfully set up the covenant signer and are now able +Congratulations! You have successfully set up the covenant signer and are now able to sign transactions with the covenant key. From c49e0355c66770dafed4461bf705f820c4e5c0c0 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Wed, 15 Jan 2025 09:48:08 -0300 Subject: [PATCH 08/11] chore: add unlock wallet prior to send btc del --- itest/test_manager.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/itest/test_manager.go b/itest/test_manager.go index 9c94cde..1f29dd5 100644 --- a/itest/test_manager.go +++ b/itest/test_manager.go @@ -412,6 +412,14 @@ func (tm *TestManager) InsertBTCDelegation( serializedUnbondingTx, err := bbntypes.SerializeBTCTx(testUnbondingInfo.UnbondingTx) require.NoError(t, err) + err = signerservice.Unlock( + context.Background(), + tm.CovenanConfig.RemoteSigner.URL, + tm.CovenanConfig.RemoteSigner.Timeout, + passphrase, + ) + require.NoError(t, err) + // submit the BTC delegation to Babylon _, err = tm.CovBBNClient.CreateBTCDelegation( bbntypes.NewBIP340PubKeyFromBTCPK(delBtcPubKey), From 8636b7f831dd9ee6ec006bd93ebf62fdf6f37581 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Wed, 15 Jan 2025 10:26:16 -0300 Subject: [PATCH 09/11] chore: removed option to set remote signer --- itest/e2e_test.go | 4 ++-- itest/test_manager.go | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/itest/e2e_test.go b/itest/e2e_test.go index f575b7c..ed4ec74 100644 --- a/itest/e2e_test.go +++ b/itest/e2e_test.go @@ -18,7 +18,7 @@ var ( // TestCovenantEmulatorLifeCycle tests the whole life cycle of a covenant emulator // in two flows depending on whether the delegation is following pre-approval flow func TestCovenantEmulatorLifeCycle(t *testing.T) { - tm, btcPks := StartManagerWithFinalityProvider(t, 1, false) + tm, btcPks := StartManagerWithFinalityProvider(t, 1) defer tm.Stop(t) // send a BTC delegation that is not following pre-approval flow @@ -52,7 +52,7 @@ func TestCovenantEmulatorLifeCycle(t *testing.T) { } func TestCovenantEmulatorLifeCycleWithRemoteSigner(t *testing.T) { - tm, btcPks := StartManagerWithFinalityProvider(t, 1, true) + tm, btcPks := StartManagerWithFinalityProvider(t, 1) defer tm.Stop(t) // send a BTC delegation that is not following pre-approval flow diff --git a/itest/test_manager.go b/itest/test_manager.go index 1f29dd5..04b52ea 100644 --- a/itest/test_manager.go +++ b/itest/test_manager.go @@ -75,7 +75,7 @@ type testFinalityProviderData struct { PoP *bstypes.ProofOfPossessionBTC } -func StartManager(t *testing.T, useRemoteSigner bool) *TestManager { +func StartManager(t *testing.T) *TestManager { testDir, err := baseDir("cee2etest") require.NoError(t, err) @@ -183,8 +183,8 @@ func (tm *TestManager) WaitForServicesStart(t *testing.T) { t.Logf("Babylon node is started") } -func StartManagerWithFinalityProvider(t *testing.T, n int, useRemoteSigner bool) (*TestManager, []*btcec.PublicKey) { - tm := StartManager(t, useRemoteSigner) +func StartManagerWithFinalityProvider(t *testing.T, n int) (*TestManager, []*btcec.PublicKey) { + tm := StartManager(t) var btcPks []*btcec.PublicKey for i := 0; i < n; i++ { From 0ee1947b6cdb3db8e7829286a8b60787b6f5dc23 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Wed, 15 Jan 2025 10:34:23 -0300 Subject: [PATCH 10/11] chore: removed duplicated test with and without remote signer, all use the remote signer --- itest/e2e_test.go | 36 ------------------------------------ itest/test_manager.go | 8 -------- 2 files changed, 44 deletions(-) diff --git a/itest/e2e_test.go b/itest/e2e_test.go index ed4ec74..0ce387d 100644 --- a/itest/e2e_test.go +++ b/itest/e2e_test.go @@ -15,8 +15,6 @@ var ( stakingAmount = int64(20000) ) -// TestCovenantEmulatorLifeCycle tests the whole life cycle of a covenant emulator -// in two flows depending on whether the delegation is following pre-approval flow func TestCovenantEmulatorLifeCycle(t *testing.T) { tm, btcPks := StartManagerWithFinalityProvider(t, 1) defer tm.Stop(t) @@ -50,37 +48,3 @@ func TestCovenantEmulatorLifeCycle(t *testing.T) { require.NoError(t, err) require.Empty(t, res) } - -func TestCovenantEmulatorLifeCycleWithRemoteSigner(t *testing.T) { - tm, btcPks := StartManagerWithFinalityProvider(t, 1) - defer tm.Stop(t) - - // send a BTC delegation that is not following pre-approval flow - _ = tm.InsertBTCDelegation(t, btcPks, stakingTime, stakingAmount, false) - - // check the BTC delegation is pending - _ = tm.WaitForNPendingDels(t, 1) - - // check the BTC delegation is active - _ = tm.WaitForNActiveDels(t, 1) - - // send a BTC delegation that is following pre-approval flow - _ = tm.InsertBTCDelegation(t, btcPks, stakingTime, stakingAmount, true) - - // check the BTC delegation is pending - _ = tm.WaitForNPendingDels(t, 1) - - time.Sleep(10 * time.Second) - - // check the BTC delegation is verified - dels := tm.WaitForNVerifiedDels(t, 1) - - // test duplicate, should expect no error - // remove covenant sigs - dels[0].CovenantSigs = nil - dels[0].BtcUndelegation.CovenantSlashingSigs = nil - dels[0].BtcUndelegation.CovenantUnbondingSigs = nil - res, err := tm.CovenantEmulator.AddCovenantSignatures(dels) - require.NoError(t, err) - require.Empty(t, res) -} diff --git a/itest/test_manager.go b/itest/test_manager.go index 04b52ea..8fb3195 100644 --- a/itest/test_manager.go +++ b/itest/test_manager.go @@ -412,14 +412,6 @@ func (tm *TestManager) InsertBTCDelegation( serializedUnbondingTx, err := bbntypes.SerializeBTCTx(testUnbondingInfo.UnbondingTx) require.NoError(t, err) - err = signerservice.Unlock( - context.Background(), - tm.CovenanConfig.RemoteSigner.URL, - tm.CovenanConfig.RemoteSigner.Timeout, - passphrase, - ) - require.NoError(t, err) - // submit the BTC delegation to Babylon _, err = tm.CovBBNClient.CreateBTCDelegation( bbntypes.NewBIP340PubKeyFromBTCPK(delBtcPubKey), From c80f47979c6dd5be97a20761525f6eb9fed3d6c4 Mon Sep 17 00:00:00 2001 From: RafilxTenfen Date: Wed, 15 Jan 2025 11:40:42 -0300 Subject: [PATCH 11/11] chore: add func to get open port to start covenant emulator --- itest/test_manager.go | 6 ++++- itest/utils.go | 63 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/itest/test_manager.go b/itest/test_manager.go index 8fb3195..dd6b364 100644 --- a/itest/test_manager.go +++ b/itest/test_manager.go @@ -2,6 +2,7 @@ package e2etest import ( "context" + "fmt" "math/rand" "os" "sync" @@ -107,13 +108,16 @@ func StartManager(t *testing.T) *TestManager { parsedConfig, err := signerConfig.Parse() require.NoError(t, err) + remoteSignerPort, url := AllocateUniquePort(t) + parsedConfig.ServerConfig.Port = remoteSignerPort + covenantConfig.RemoteSigner.URL = fmt.Sprintf("http://%s", url) + server, err := signerService.New( context.Background(), parsedConfig, app, met, ) - require.NoError(t, err) signer := remotesigner.NewRemoteSigner(covenantConfig.RemoteSigner) diff --git a/itest/utils.go b/itest/utils.go index ee51188..17bc5db 100644 --- a/itest/utils.go +++ b/itest/utils.go @@ -1,6 +1,67 @@ package e2etest -import "os" +import ( + "fmt" + mrand "math/rand/v2" + "net" + "os" + "sync" + "testing" +) + +// Track allocated ports, protected by a mutex +var ( + allocatedPorts = make(map[int]struct{}) + portMutex sync.Mutex +) + +// AllocateUniquePort tries to find an available TCP port on the localhost +// by testing multiple random ports within a specified range. +func AllocateUniquePort(t *testing.T) (int, string) { + randPort := func(base, spread int) int { + return base + mrand.IntN(spread) + } + + // Base port and spread range for port selection + const ( + basePort = 40000 + portRange = 50000 + ) + + var url string + // Try up to 10 times to find an available port + for i := 0; i < 30; i++ { + port := randPort(basePort, portRange) + + // Lock the mutex to check and modify the shared map + portMutex.Lock() + if _, exists := allocatedPorts[port]; exists { + // Port already allocated, try another one + portMutex.Unlock() + continue + } + + url = fmt.Sprintf("127.0.0.1:%d", port) + listener, err := net.Listen("tcp", url) + if err != nil { + portMutex.Unlock() + continue + } + + allocatedPorts[port] = struct{}{} + portMutex.Unlock() + + if err := listener.Close(); err != nil { + continue + } + + return port, url + } + + // If no available port was found, fail the test + t.Fatalf("failed to find an available port in range %d-%d", basePort, basePort+portRange) + return 0, "" +} func baseDir(pattern string) (string, error) { tempPath := os.TempDir()