Skip to content

Commit

Permalink
feat: utilities for instantiating Babylon contracts (#66)
Browse files Browse the repository at this point in the history
This PR provides a message `MsgInstantiateBabylonContracts` for
instantiating all Babylon contracts. This simplifies the Cosmos
integration: one can first store code of 3 contracts, and then submit
this message for instantiating everything.

With this message as baseline, we can then change the handler logic to
improve the instantiation flow, e.g., instantiate 3 contracts one by one
in this handler, rather than allowing contracts instantiating each
other. This will be done in a subsequent PR.

The PR contains the following:

- [x] function/message for instantiating Babylon contracts
- [x] CLIs for the messages
- [x] fuzz tests
- [x] e2e tests
  • Loading branch information
SebastianElvis authored Dec 9, 2024
1 parent 411d4ac commit b6bbc02
Show file tree
Hide file tree
Showing 21 changed files with 1,631 additions and 140 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,5 @@ jobs:
secrets: inherit
with:
publish: false
dockerfile: ./Dockerfile
dockerfile: ./contrib/images/local-bcd/Dockerfile
repoName: babylon-sdk
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,5 @@ jobs:
secrets: inherit
with:
publish: true
dockerfile: ./Dockerfile
dockerfile: ./contrib/images/local-bcd/Dockerfile
repoName: babylon-sdk
6 changes: 1 addition & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ build-linux-static:
$(MAKE) -C demo build-linux-static

build-docker:
$(DOCKER) build --tag babylonlabs-io/bcd -f contrib/images/bcd/Dockerfile .

# A local bcd image with users and funds for testing / development / integration purposes
build-bcd:
$(DOCKER) build --tag babylonlabs-io/local-bcd -f contrib/images/local-bcd/Dockerfile .
$(DOCKER) build --tag babylonlabs-io/bcd -f contrib/images/local-bcd/Dockerfile .

########################################
### Testing
Expand Down
96 changes: 94 additions & 2 deletions demo/app/app_test.go
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package app

import (
"encoding/json"
"fmt"
"testing"
"time"

"cosmossdk.io/log"
"github.com/CosmWasm/wasmd/x/wasm"
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
babylonkeeper "github.com/babylonlabs-io/babylon-sdk/x/babylon/keeper"
"github.com/babylonlabs-io/babylon-sdk/x/babylon/types"
abci "github.com/cometbft/cometbft/abci/types"
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
dbm "github.com/cosmos/cosmos-db"
"github.com/stretchr/testify/require"

simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)

var emptyWasmOpts []wasm.Option
Expand Down Expand Up @@ -61,3 +67,89 @@ func TestGetMaccPerms(t *testing.T) {
dup := GetMaccPerms()
require.Equal(t, maccPerms, dup, "duplicated module account permissions differed from actual module account permissions")
}

const (
TestDataPath = "../../tests/testdata"
BabylonContractCodePath = TestDataPath + "/babylon_contract.wasm"
BtcStakingContractCodePath = TestDataPath + "/btc_staking.wasm"
BtcFinalityContractCodePath = TestDataPath + "/btc_finality.wasm"
)

func GetGZippedContractCodes() ([]byte, []byte, []byte) {
babylonContractCode, err := types.GetGZippedContractCode(BabylonContractCodePath)
if err != nil {
panic(err)
}
btcStakingContractCode, err := types.GetGZippedContractCode(BtcStakingContractCodePath)
if err != nil {
panic(err)
}
btcFinalityContractCode, err := types.GetGZippedContractCode(BtcFinalityContractCodePath)
if err != nil {
panic(err)
}

return babylonContractCode, btcStakingContractCode, btcFinalityContractCode
}

func TestInstantiateBabylonContracts(t *testing.T) {
consumerApp := Setup(t)
ctx := consumerApp.NewContext(false)
ctx = ctx.WithBlockHeader(cmtproto.Header{Time: time.Now()})
babylonKeeper := consumerApp.BabylonKeeper
babylonMsgServer := babylonkeeper.NewMsgServer(babylonKeeper)
wasmKeeper := consumerApp.WasmKeeper
wasmMsgServer := wasmkeeper.NewMsgServerImpl(&wasmKeeper)

// store Babylon contract codes
babylonContractCode, btcStakingContractCode, btcFinalityContractCode := GetGZippedContractCodes()
resp, err := wasmMsgServer.StoreCode(ctx, &wasmtypes.MsgStoreCode{
Sender: consumerApp.BabylonKeeper.GetAuthority(),
WASMByteCode: babylonContractCode,
})
babylonContractCodeID := resp.CodeID
require.NoError(t, err)
resp, err = wasmMsgServer.StoreCode(ctx, &wasmtypes.MsgStoreCode{
Sender: consumerApp.BabylonKeeper.GetAuthority(),
WASMByteCode: btcStakingContractCode,
})
btcStakingContractCodeID := resp.CodeID
require.NoError(t, err)
resp, err = wasmMsgServer.StoreCode(ctx, &wasmtypes.MsgStoreCode{
Sender: consumerApp.BabylonKeeper.GetAuthority(),
WASMByteCode: btcFinalityContractCode,
})
btcFinalityContractCodeID := resp.CodeID
require.NoError(t, err)

// BTC staking init message
btcStakingInitMsg := map[string]interface{}{
"admin": consumerApp.BabylonKeeper.GetAuthority(),
}
btcStakingInitMsgBytes, err := json.Marshal(btcStakingInitMsg)
require.NoError(t, err)
// BTC finality init message
btcFinalityInitMsg := map[string]interface{}{
"admin": consumerApp.BabylonKeeper.GetAuthority(),
}
btcFinalityInitMsgBytes, err := json.Marshal(btcFinalityInitMsg)
require.NoError(t, err)

// instantiate Babylon contract
_, err = babylonMsgServer.InstantiateBabylonContracts(ctx, &types.MsgInstantiateBabylonContracts{
Network: "regtest",
BabylonContractCodeId: babylonContractCodeID,
BtcStakingContractCodeId: btcStakingContractCodeID,
BtcFinalityContractCodeId: btcFinalityContractCodeID,
BabylonTag: "01020304",
BtcConfirmationDepth: 1,
CheckpointFinalizationTimeout: 2,
NotifyCosmosZone: false,
BtcStakingMsg: btcStakingInitMsgBytes,
BtcFinalityMsg: btcFinalityInitMsgBytes,
ConsumerName: "test-consumer",
ConsumerDescription: "test-consumer-description",
Admin: babylonKeeper.GetAuthority(),
})
require.NoError(t, err)
}
46 changes: 46 additions & 0 deletions docs/proto/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
- [Query](#babylonlabs.babylon.v1beta1.Query)

- [babylonlabs/babylon/v1beta1/tx.proto](#babylonlabs/babylon/v1beta1/tx.proto)
- [MsgInstantiateBabylonContracts](#babylonlabs.babylon.v1beta1.MsgInstantiateBabylonContracts)
- [MsgInstantiateBabylonContractsResponse](#babylonlabs.babylon.v1beta1.MsgInstantiateBabylonContractsResponse)
- [MsgUpdateParams](#babylonlabs.babylon.v1beta1.MsgUpdateParams)
- [MsgUpdateParamsResponse](#babylonlabs.babylon.v1beta1.MsgUpdateParamsResponse)

Expand All @@ -41,6 +43,9 @@ Params defines the parameters for the x/babylon module.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `babylon_contract_code_id` | [uint64](#uint64) | | babylon_contract_code_id is the code ID of the Babylon contract |
| `btc_staking_contract_code_id` | [uint64](#uint64) | | btc_staking_contract_code_id is the code ID of the BTC staking contract |
| `btc_finality_contract_code_id` | [uint64](#uint64) | | btc_finality_contract_code_id is the code ID of the BTC finality contract |
| `babylon_contract_address` | [string](#string) | | babylon_contract_address is the address of the Babylon contract |
| `btc_staking_contract_address` | [string](#string) | | btc_staking_contract_address is the address of the BTC staking contract |
| `btc_finality_contract_address` | [string](#string) | | btc_finality_contract_address is the address of the BTC finality contract |
Expand Down Expand Up @@ -151,6 +156,46 @@ Query provides defines the gRPC querier service



<a name="babylonlabs.babylon.v1beta1.MsgInstantiateBabylonContracts"></a>

### MsgInstantiateBabylonContracts
MsgInstantiateBabylonContracts is the Msg/InstantiateBabylonContracts request
type.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `signer` | [string](#string) | | signer is the address who submits the message. |
| `babylon_contract_code_id` | [uint64](#uint64) | | babylon_contract_code_id is the code ID for the Babylon contract. |
| `btc_staking_contract_code_id` | [uint64](#uint64) | | btc_staking_contract_code_id is the code ID for the BTC staking contract. |
| `btc_finality_contract_code_id` | [uint64](#uint64) | | btc_finality_contract_code_id is the code ID for the BTC finality contract. |
| `network` | [string](#string) | | network is the Bitcoin network to connect to (e.g. "regtest", "testnet", "mainnet") |
| `babylon_tag` | [string](#string) | | babylon_tag is a unique identifier for this Babylon instance |
| `btc_confirmation_depth` | [uint32](#uint32) | | btc_confirmation_depth is the number of confirmations required for Bitcoin transactions |
| `checkpoint_finalization_timeout` | [uint32](#uint32) | | checkpoint_finalization_timeout is the timeout in blocks for checkpoint finalization |
| `notify_cosmos_zone` | [bool](#bool) | | notify_cosmos_zone indicates whether to notify the Cosmos zone of events |
| `btc_staking_msg` | [bytes](#bytes) | | btc_staking_msg is the initialization message for the BTC staking contract |
| `btc_finality_msg` | [bytes](#bytes) | | btc_finality_msg is the initialization message for the BTC finality contract |
| `consumer_name` | [string](#string) | | consumer_name is the name of this consumer chain |
| `consumer_description` | [string](#string) | | consumer_description is a description of this consumer chain |
| `admin` | [string](#string) | | admin is the address that controls the Babylon module |






<a name="babylonlabs.babylon.v1beta1.MsgInstantiateBabylonContractsResponse"></a>

### MsgInstantiateBabylonContractsResponse
MsgInstantiateBabylonContractsResponse is the Msg/InstantiateBabylonContracts
response type.






<a name="babylonlabs.babylon.v1beta1.MsgUpdateParams"></a>

### MsgUpdateParams
Expand Down Expand Up @@ -193,6 +238,7 @@ Msg defines the wasm Msg service.

| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `InstantiateBabylonContracts` | [MsgInstantiateBabylonContracts](#babylonlabs.babylon.v1beta1.MsgInstantiateBabylonContracts) | [MsgInstantiateBabylonContractsResponse](#babylonlabs.babylon.v1beta1.MsgInstantiateBabylonContractsResponse) | InstantiateBabylonContracts defines an operation for instantiating the Babylon contracts. | |
| `UpdateParams` | [MsgUpdateParams](#babylonlabs.babylon.v1beta1.MsgUpdateParams) | [MsgUpdateParamsResponse](#babylonlabs.babylon.v1beta1.MsgUpdateParamsResponse) | UpdateParams defines a (governance) operation for updating the x/auth module parameters. The authority defaults to the x/gov module account. | |

<!-- end services -->
Expand Down
16 changes: 11 additions & 5 deletions proto/babylonlabs/babylon/v1beta1/babylon.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,22 @@ option (gogoproto.equal_all) = false;
message Params {
option (gogoproto.equal) = true;

// babylon_contract_code_id is the code ID of the Babylon contract
uint64 babylon_contract_code_id = 1;
// btc_staking_contract_code_id is the code ID of the BTC staking contract
uint64 btc_staking_contract_code_id = 2;
// btc_finality_contract_code_id is the code ID of the BTC finality contract
uint64 btc_finality_contract_code_id = 3;
// babylon_contract_address is the address of the Babylon contract
string babylon_contract_address = 1
string babylon_contract_address = 4
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// btc_staking_contract_address is the address of the BTC staking contract
string btc_staking_contract_address = 2
string btc_staking_contract_address = 5
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// btc_finality_contract_address is the address of the BTC finality contract
string btc_finality_contract_address = 3
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
string btc_finality_contract_address = 6
[ (cosmos_proto.scalar) = "cosmos.AddressString" ];
// max_gas_begin_blocker defines the maximum gas that can be spent in a
// contract sudo callback
uint32 max_gas_begin_blocker = 4;
uint32 max_gas_begin_blocker = 7;
}
50 changes: 50 additions & 0 deletions proto/babylonlabs/babylon/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,61 @@ option (gogoproto.goproto_getters_all) = false;
service Msg {
option (cosmos.msg.v1.service) = true;

// InstantiateBabylonContracts defines an operation for instantiating the
// Babylon contracts.
rpc InstantiateBabylonContracts(MsgInstantiateBabylonContracts)
returns (MsgInstantiateBabylonContractsResponse);

// UpdateParams defines a (governance) operation for updating the x/auth
// module parameters. The authority defaults to the x/gov module account.
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
}

// MsgInstantiateBabylonContracts is the Msg/InstantiateBabylonContracts request
// type.
message MsgInstantiateBabylonContracts {
option (cosmos.msg.v1.signer) = "signer";

// signer is the address who submits the message.
string signer = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];

// babylon_contract_code_id is the code ID for the Babylon contract.
uint64 babylon_contract_code_id = 2;
// btc_staking_contract_code_id is the code ID for the BTC staking contract.
uint64 btc_staking_contract_code_id = 3;
// btc_finality_contract_code_id is the code ID for the BTC finality contract.
uint64 btc_finality_contract_code_id = 4;

// network is the Bitcoin network to connect to (e.g. "regtest", "testnet",
// "mainnet")
string network = 5;
// babylon_tag is a unique identifier for this Babylon instance
string babylon_tag = 6;
// btc_confirmation_depth is the number of confirmations required for Bitcoin
// transactions
uint32 btc_confirmation_depth = 7;
// checkpoint_finalization_timeout is the timeout in blocks for checkpoint
// finalization
uint32 checkpoint_finalization_timeout = 8;
// notify_cosmos_zone indicates whether to notify the Cosmos zone of events
bool notify_cosmos_zone = 9;
// btc_staking_msg is the initialization message for the BTC staking contract
bytes btc_staking_msg = 10;
// btc_finality_msg is the initialization message for the BTC finality
// contract
bytes btc_finality_msg = 11;
// consumer_name is the name of this consumer chain
string consumer_name = 12;
// consumer_description is a description of this consumer chain
string consumer_description = 13;
// admin is the address that controls the Babylon module
string admin = 14 [ (cosmos_proto.scalar) = "cosmos.AddressString" ];
}

// MsgInstantiateBabylonContractsResponse is the Msg/InstantiateBabylonContracts
// response type.
message MsgInstantiateBabylonContractsResponse {}

// MsgUpdateParams is the Msg/UpdateParams request type.
message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";
Expand Down
51 changes: 24 additions & 27 deletions tests/e2e/test_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/CosmWasm/wasmd/x/wasm/ibctesting"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/babylonlabs-io/babylon-sdk/demo/app"
"github.com/babylonlabs-io/babylon-sdk/x/babylon/types"
bbntypes "github.com/babylonlabs-io/babylon-sdk/x/babylon/types"
abci "github.com/cometbft/cometbft/abci/types"
sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -120,43 +121,39 @@ func (p *TestConsumerClient) BootstrapContracts() (*ConsumerContract, error) {
if err != nil {
return nil, err
}
initMsg := map[string]interface{}{
"network": "regtest",
"babylon_tag": "01020304",
"btc_confirmation_depth": 1,
"checkpoint_finalization_timeout": 2,
"notify_cosmos_zone": false,
"btc_staking_code_id": btcStakingContractWasmId,
"btc_staking_msg": btcStakingInitMsgBytes,
"consumer_name": "test-consumer",
"consumer_description": "test-consumer-description",
"btc_finality_code_id": btcFinalityContractWasmId,
"btc_finality_msg": btcFinalityInitMsgBytes,
"admin": p.GetSender().String(),

// instantiate Babylon contract
msgInstantiate := types.MsgInstantiateBabylonContracts{
Signer: p.GetSender().String(),
BabylonContractCodeId: babylonContractWasmId,
BtcStakingContractCodeId: btcStakingContractWasmId,
BtcFinalityContractCodeId: btcFinalityContractWasmId,
Network: "regtest",
BabylonTag: "01020304",
BtcConfirmationDepth: 1,
CheckpointFinalizationTimeout: 2,
NotifyCosmosZone: false,
BtcStakingMsg: btcStakingInitMsgBytes,
BtcFinalityMsg: btcFinalityInitMsgBytes,
ConsumerName: "test-consumer",
ConsumerDescription: "test-consumer-description",
Admin: p.GetSender().String(),
}
initMsgBytes, err := json.Marshal(initMsg)
_, err = p.Chain.SendMsgs(&msgInstantiate)
if err != nil {
return nil, err
}

babylonContractAddr := InstantiateContract(p.t, p.Chain, babylonContractWasmId, initMsgBytes)
res, err := p.Query(babylonContractAddr, Query{"config": {}})
params := p.App.BabylonKeeper.GetParams(p.Chain.GetContext())
babylonAddr, btcStakingAddr, btcFinalityAddr, err := params.GetContractAddresses()
if err != nil {
return nil, err
}
btcStakingContractAddr, ok := res["btc_staking"]
if !ok {
return nil, fmt.Errorf("failed to instantiate BTC staking contract")
}
btcFinalityContractAddr, ok := res["btc_finality"]
if !ok {
return nil, fmt.Errorf("failed to instantiate BTC finality contract")
}

r := ConsumerContract{
Babylon: babylonContractAddr,
BTCStaking: sdk.MustAccAddressFromBech32(btcStakingContractAddr.(string)),
BTCFinality: sdk.MustAccAddressFromBech32(btcFinalityContractAddr.(string)),
Babylon: babylonAddr,
BTCStaking: btcStakingAddr,
BTCFinality: btcFinalityAddr,
}
p.Contracts = r
return &r, nil
Expand Down
Loading

0 comments on commit b6bbc02

Please sign in to comment.