Skip to content

Commit

Permalink
add command to generate pop
Browse files Browse the repository at this point in the history
  • Loading branch information
KonradStaniec committed Dec 23, 2024
1 parent ddfabe3 commit 2ec161e
Show file tree
Hide file tree
Showing 6 changed files with 374 additions and 6 deletions.
16 changes: 16 additions & 0 deletions babylonclient/keyringcontroller/codec.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package keyringcontroller

import (
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
)

func MakeCodec() *codec.ProtoCodec {
ir := codectypes.NewInterfaceRegistry()
cdc := codec.NewProtoCodec(ir)

cryptocodec.RegisterInterfaces(ir)

return cdc
}
43 changes: 43 additions & 0 deletions babylonclient/keyringcontroller/keyring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package keyringcontroller

import (
"fmt"
"strings"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
)

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) {
if keyringDir == "" {
return client.Context{}, fmt.Errorf("the keyring directory should not be empty")
}
return client.Context{}.
WithChainID(chainID).
WithCodec(MakeCodec()).
WithKeyringDir(keyringDir), nil
}
136 changes: 136 additions & 0 deletions babylonclient/keyringcontroller/keyringcontroller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package keyringcontroller

import (
"fmt"
"strings"

"github.com/btcsuite/btcd/btcec/v2"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/crypto/keyring"
sdksecp256k1 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
"github.com/cosmos/go-bip39"
)

const (
secp256k1Type = "secp256k1"
mnemonicEntropySize = 256
)

type ChainKeyInfo struct {
Name string
Mnemonic string
PublicKey *btcec.PublicKey
PrivateKey *btcec.PrivateKey
}

type ChainKeyringController struct {
kr keyring.Keyring
keyName string
// input is to send passphrase to kr
input *strings.Reader
}

func NewChainKeyringController(ctx client.Context, name, keyringBackend string) (*ChainKeyringController, error) {
if name == "" {
return nil, fmt.Errorf("the key name should not be empty")
}

if keyringBackend == "" {
return nil, fmt.Errorf("the keyring backend should not be empty")
}

inputReader := strings.NewReader("")
kr, err := keyring.New(
ctx.ChainID,
keyringBackend,
ctx.KeyringDir,
inputReader,
ctx.Codec,
ctx.KeyringOptions...)
if err != nil {
return nil, fmt.Errorf("failed to create keyring: %w", err)
}

return &ChainKeyringController{
keyName: name,
kr: kr,
input: inputReader,
}, 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
}

func (kc *ChainKeyringController) CreateChainKey(passphrase, hdPath string) (*ChainKeyInfo, error) {
keyringAlgos, _ := kc.kr.SupportedAlgorithms()
algo, err := keyring.NewSigningAlgoFromString(secp256k1Type, keyringAlgos)
if err != nil {
return nil, err
}
// read entropy seed straight from tmcrypto.Rand and convert to mnemonic
entropySeed, err := bip39.NewEntropy(mnemonicEntropySize)
if err != nil {
return nil, err
}

mnemonic, err := bip39.NewMnemonic(entropySeed)
if err != nil {
return nil, err
}

// we need to repeat the passphrase to mock the reentry
kc.input.Reset(passphrase + "\n" + passphrase)
record, err := kc.kr.NewAccount(kc.keyName, mnemonic, passphrase, hdPath, algo)
if err != nil {
return nil, err
}

privKey := record.GetLocal().PrivKey.GetCachedValue()

switch v := privKey.(type) {
case *sdksecp256k1.PrivKey:
sk, pk := btcec.PrivKeyFromBytes(v.Key)
return &ChainKeyInfo{
Name: kc.keyName,
PublicKey: pk,
PrivateKey: sk,
Mnemonic: mnemonic,
}, nil
default:
return nil, fmt.Errorf("unsupported key type in keyring")
}
}

func (kc *ChainKeyringController) GetChainPrivKey(passphrase string) (*sdksecp256k1.PrivKey, error) {
kc.input.Reset(passphrase)
k, err := kc.kr.Key(kc.keyName)
if err != nil {
return nil, fmt.Errorf("failed to get private key: %w", err)
}

privKeyCached := k.GetLocal().PrivKey.GetCachedValue()

switch v := privKeyCached.(type) {
case *sdksecp256k1.PrivKey:
return v, nil
default:
return nil, fmt.Errorf("unsupported key type in keyring")
}
}

func (kc *ChainKeyringController) KeyRecord() (*keyring.Record, error) {
return kc.GetKeyring().Key(kc.keyName)
}
8 changes: 2 additions & 6 deletions cmd/stakercli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

cmdadmin "github.com/babylonlabs-io/btc-staker/cmd/stakercli/admin"
cmddaemon "github.com/babylonlabs-io/btc-staker/cmd/stakercli/daemon"
cmdpop "github.com/babylonlabs-io/btc-staker/cmd/stakercli/pop"

Check failure on line 9 in cmd/stakercli/main.go

View workflow job for this annotation

GitHub Actions / lint_test / integration-tests

no required module provides package github.com/babylonlabs-io/btc-staker/cmd/stakercli/pop; to add it:

Check failure on line 9 in cmd/stakercli/main.go

View workflow job for this annotation

GitHub Actions / lint_test / unit-tests

no required module provides package github.com/babylonlabs-io/btc-staker/cmd/stakercli/pop; to add it:
cmdtx "github.com/babylonlabs-io/btc-staker/cmd/stakercli/transaction"
"github.com/urfave/cli"
)
Expand All @@ -21,7 +22,6 @@ const (
btcWalletRPCUserFlag = "btc-wallet-rpc-user"
btcWalletRPCPassFlag = "btc-wallet-rpc-pass"
btcWalletPassphraseFlag = "btc-wallet-passphrase"
btcWalletBackendFlag = "btc-wallet-backend"
)

func main() {
Expand Down Expand Up @@ -53,16 +53,12 @@ func main() {
Name: btcWalletPassphraseFlag,
Usage: "Bitcoin wallet passphrase",
},
cli.StringFlag{
Name: btcWalletBackendFlag,
Usage: "Bitcoin backend (btcwallet|bitcoind)",
Value: "btcd",
},
}

app.Commands = append(app.Commands, cmddaemon.DaemonCommands...)
app.Commands = append(app.Commands, cmdadmin.AdminCommands...)
app.Commands = append(app.Commands, cmdtx.TransactionCommands...)
app.Commands = append(app.Commands, cmdpop.PopCommands...)

if err := app.Run(os.Args); err != nil {
fatal(err)
Expand Down
54 changes: 54 additions & 0 deletions itest/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"testing"
"time"

"github.com/babylonlabs-io/btc-staker/babylonclient/keyringcontroller"
"github.com/babylonlabs-io/btc-staker/itest/containers"
"github.com/babylonlabs-io/btc-staker/itest/testutil"
"github.com/babylonlabs-io/networks/parameters/parser"
Expand All @@ -27,6 +28,7 @@ import (
"github.com/babylonlabs-io/btc-staker/stakercfg"
"github.com/babylonlabs-io/btc-staker/types"
"github.com/babylonlabs-io/btc-staker/walletcontroller"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcec/v2/schnorr"
"github.com/btcsuite/btcd/btcjson"
"github.com/btcsuite/btcd/btcutil"
Expand Down Expand Up @@ -983,3 +985,55 @@ func TestStakeFromPhase1(t *testing.T) {
require.NoError(t, err)
require.True(t, delInfo.Active)
}

func TestTestPopCreation(t *testing.T) {
t.Parallel()
manager, err := containers.NewManager(t)
require.NoError(t, err)
h := NewBitcoindHandler(t, manager)
bitcoind := h.Start()
passphrase := "pass"
walletName := "test-wallet"
_ = h.CreateWallet(walletName, passphrase)

rpcHost := fmt.Sprintf("127.0.0.1:%s", bitcoind.GetPort("18443/tcp"))
cfg, c := defaultStakerConfigAndBtc(t, walletName, passphrase, rpcHost)

segwitAddress, err := c.GetNewAddress("")
require.NoError(t, err)

controller, err := walletcontroller.NewRPCWalletController(cfg)
require.NoError(t, err)

keyring, err := keyringcontroller.CreateKeyring(
// does not matter for memory keyring
"/",
"babylon",
"memory",
nil,
)
require.NoError(t, err)

randomKey, _ := btcec.NewPrivateKey()
require.NoError(t, err)

keyName := "test"
err = keyring.ImportPrivKeyHex(keyName, hex.EncodeToString(randomKey.Serialize()), "secp256k1")
require.NoError(t, err)

record, err := keyring.Key(keyName)
require.NoError(t, err)

address, err := record.GetAddress()
require.NoError(t, err)

popCreator := staker.NewPopCreator(controller, keyring)
require.NotNil(t, popCreator)

err = controller.UnlockWallet(30)
require.NoError(t, err)

popResponse, err := popCreator.CreatePop(segwitAddress, "bbn", address)
require.NoError(t, err)
require.NotNil(t, popResponse)
}
Loading

0 comments on commit 2ec161e

Please sign in to comment.