Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: utilities for instantiating Babylon contracts #66

Merged
merged 7 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think providing a message for instantiation is not without merit, but why not just instantiate the contracts during bootstrapping?

Since the contracts are fundamental for the SDK operation, I don't see the need to gate their instantiation through a specific protobuf endpoint.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just instantiate the contracts during bootstrapping?

I was thinking about this as well, but decided to not do this (at least for this PR):

  • This makes it mandatory to have these wasm binaries in hand when executiong babylond to start the chain. Otherwise we would need to somehow wire the wasm binaries into the babylond binary, which is not ideal.
  • It's possible that one wants to invoke MsgInstantiateBabylonContracts after the chain has been running for a while, e.g.,
    • the chain has just passed a gov prop for integrating with Babylon;
    • the chain does not want the old babylon contracts anymore, and wants to deploy a new set of babylon contracts, e.g., due to corrupted DB state
  • Even if we want to instantiate all contracts upon starting the chain, this could be done in a separate PR. This separate PR basically executes the same logic of MsgInstantiateBabylonContracts handler upon starting the chain. Other things are not affected.

Wdyt?

Copy link
Contributor

@maurolacy maurolacy Dec 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. But please notice that regarding

This makes it mandatory to have these wasm binaries in hand when executiong babylond to start the chain.

these contracts are on the Consumer side, and so, they are not mandatory for Babylon.

Moreover, there's a way to embed the contracts inside the binary, which makes it pretty much stand-alone. See

https://github.com/confio/tgrade/blob/1bc8463f3760bf9549389a02a9e48eda482a59fe/x/poe/bootstrap.go#L19-L40

for reference / as an example.

- [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;
maurolacy marked this conversation as resolved.
Show resolved Hide resolved
// 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;
maurolacy marked this conversation as resolved.
Show resolved Hide resolved

// 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
maurolacy marked this conversation as resolved.
Show resolved Hide resolved
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
Loading