├── buf.work.yaml
├── x
├── btcbridge
│ ├── module
│ │ ├── module_simulation.go
│ │ ├── genesis.go
│ │ ├── genesis_test.go
│ │ ├── abci.go
│ │ └── module.go
│ ├── types
│ │ ├── utxo.go
│ │ ├── msg_update_params.go
│ │ ├── asset.go
│ │ ├── msg_submit_fee_rate.go
│ │ ├── msg_submit_headers.go
│ │ ├── msg_withdraw_to_bitcoin.go
│ │ ├── msg_submit_signatures.go
│ │ ├── merkle_proof.go
│ │ ├── msg_update_trusted_non_btc_relayers.go
│ │ ├── msg_update_trusted_fee_providers.go
│ │ ├── msg_submit_withdraw_transaction.go
│ │ ├── msg_submit_deposit_transaction.go
│ │ ├── msg_complete_dkg.go
│ │ ├── msg_transfer_vault.go
│ │ ├── msg_initiate_dkg.go
│ │ ├── msg_consolidate_vaults.go
│ │ ├── runes_test.go
│ │ ├── varint.go
│ │ ├── tss.go
│ │ ├── expected_keepers.go
│ │ ├── codec.go
│ │ ├── block_header.go
│ │ ├── genesis.go
│ │ ├── signature.go
│ │ ├── errors.go
│ │ ├── policy.go
│ │ ├── keys.go
│ │ └── runes.go
│ ├── keeper
│ │ ├── event.go
│ │ ├── hooks.go
│ │ ├── fee_rate.go
│ │ ├── params.go
│ │ └── consolidate.go
│ ├── codec
│ │ └── bech32_codec.go
│ └── client
│ │ └── cli
│ │ └── tx.go
└── incentive
│ ├── module
│ ├── module_simulation.go
│ ├── genesis.go
│ ├── genesis_test.go
│ └── module.go
│ ├── types
│ ├── errors.go
│ ├── genesis.go
│ ├── msg_update_params.go
│ ├── keys.go
│ ├── codec.go
│ ├── params.go
│ └── expected_keepers.go
│ ├── keeper
│ ├── msg_server.go
│ ├── queries.go
│ ├── keeper.go
│ ├── params.go
│ └── reward.go
│ └── client
│ └── cli
│ └── query.go
├── .gitignore
├── testutil
├── sample
│ └── sample.go
├── nullify
│ └── nullify.go
├── keeper
│ ├── incentive.go
│ └── btc_bridge.go
└── network
│ └── network.go
├── proto
├── side
│ ├── incentive
│ │ ├── genesis.proto
│ │ ├── params.proto
│ │ ├── incentive.proto
│ │ ├── tx.proto
│ │ └── query.proto
│ └── btcbridge
│ │ ├── genesis.proto
│ │ ├── params.proto
│ │ └── btcbridge.proto
├── buf.gen.swagger.yaml
├── buf.gen.sta.yaml
├── buf.gen.ts.yaml
├── buf.gen.gogo.yaml
├── buf.gen.pulsar.yaml
├── buf.yaml
└── buf.lock
├── cmd
└── sided
│ ├── main.go
│ └── cmd
│ ├── config.go
│ ├── root.go
│ └── commands.go
├── tools
└── tools.go
├── app
├── params
│ └── encoding.go
├── wasm.go
├── genesis.go
├── config.go
├── test_support.go
├── encoding.go
└── export.go
├── docs
├── docs.go
└── template
│ └── index.tpl
├── config.yml
├── README.md
├── .github
└── workflows
│ └── release.yml
├── third_node.sh
├── second_node.sh
└── api
└── side
└── incentive
├── tx_grpc.pb.go
└── query_grpc.pb.go
/buf.work.yaml:
--------------------------------------------------------------------------------
1 | version: v1
2 | directories:
3 | - proto
4 |
--------------------------------------------------------------------------------
/x/btcbridge/module/module_simulation.go:
--------------------------------------------------------------------------------
1 | package btcbridge
2 |
--------------------------------------------------------------------------------
/x/incentive/module/module_simulation.go:
--------------------------------------------------------------------------------
1 | package incentive
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | vue/node_modules
2 | vue/dist
3 | build/
4 | release/
5 | .idea/
6 | .vscode/
7 | .DS_Store
8 |
--------------------------------------------------------------------------------
/x/btcbridge/types/utxo.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | // UTXOIterator defines the interface of the iterator over the utxos
4 | type UTXOIterator interface {
5 | Valid() bool
6 | Next()
7 | Close() error
8 |
9 | GetUTXO() *UTXO
10 | GetMinimumUTXO() *UTXO
11 | }
12 |
--------------------------------------------------------------------------------
/testutil/sample/sample.go:
--------------------------------------------------------------------------------
1 | package sample
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | // AccAddress returns a sample account address
9 | func AccAddress() string {
10 | pk := ed25519.GenPrivKey().PubKey()
11 | addr := pk.Address()
12 | return sdk.AccAddress(addr).String()
13 | }
14 |
--------------------------------------------------------------------------------
/proto/side/incentive/genesis.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package side.incentive;
3 |
4 | import "gogoproto/gogo.proto";
5 | import "side/incentive/params.proto";
6 |
7 | option go_package = "github.com/sideprotocol/side/x/incentive/types";
8 |
9 | // GenesisState defines the incentive module's genesis state.
10 | message GenesisState {
11 | Params params = 1 [(gogoproto.nullable) = false];
12 | }
13 |
--------------------------------------------------------------------------------
/proto/buf.gen.swagger.yaml:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from Ignite. You can edit
2 | # the file content but do not change the file name or path.
3 | #
4 | # buf.gen.swagger.yaml
5 | #
6 | version: v1
7 | plugins:
8 | - name: openapiv2
9 | out: .
10 | opt:
11 | - logtostderr=true
12 | - openapi_naming_strategy=fqn
13 | - json_names_for_fields=false
14 | - generate_unbound_methods=true
--------------------------------------------------------------------------------
/proto/buf.gen.sta.yaml:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from Ignite. You can edit
2 | # the file content but do not change the file name or path.
3 | #
4 | # buf.gen.sta.yaml
5 | #
6 | version: v1
7 | plugins:
8 | - name: openapiv2
9 | out: .
10 | opt:
11 | - logtostderr=true
12 | - openapi_naming_strategy=simple
13 | - ignore_comments=true
14 | - simple_operation_ids=false
15 | - json_names_for_fields=false
16 |
--------------------------------------------------------------------------------
/cmd/sided/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "os"
5 |
6 | "cosmossdk.io/log"
7 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd"
8 |
9 | "github.com/sideprotocol/side/app"
10 | "github.com/sideprotocol/side/cmd/sided/cmd"
11 | )
12 |
13 | func main() {
14 | rootCmd := cmd.NewRootCmd()
15 | if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil {
16 | log.NewLogger(rootCmd.OutOrStderr()).Error("failure when running app", "err", err)
17 | os.Exit(1)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/x/incentive/types/errors.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | // DONTCOVER
4 |
5 | import (
6 | errorsmod "cosmossdk.io/errors"
7 | )
8 |
9 | // x/incentive module sentinel errors
10 | var (
11 | ErrDepositIncentiveNotEnabled = errorsmod.Register(ModuleName, 1001, "incentive not enabled for deposit")
12 | ErrWithdrawIncentiveNotEnabled = errorsmod.Register(ModuleName, 1002, "incentive not enabled for withdrawal")
13 | ErrInvalidParams = errorsmod.Register(ModuleName, 1003, "invalid params")
14 | )
15 |
--------------------------------------------------------------------------------
/proto/buf.gen.ts.yaml:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from Ignite. You can edit
2 | # the file content but do not change the file name or path.
3 | #
4 | # buf.gen.ts.yaml
5 | #
6 | version: v1
7 | managed:
8 | enabled: true
9 | plugins:
10 | - plugin: buf.build/community/stephenh-ts-proto
11 | out: .
12 | opt:
13 | - logtostderr=true
14 | - allow_merge=true
15 | - json_names_for_fields=false
16 | - ts_proto_opt=snakeToCamel=true
17 | - ts_proto_opt=esModuleInterop=true
18 | - ts_proto_out=.
19 |
--------------------------------------------------------------------------------
/tools/tools.go:
--------------------------------------------------------------------------------
1 | //go:build tools
2 |
3 | package tools
4 |
5 | import (
6 | _ "github.com/bufbuild/buf/cmd/buf"
7 | _ "github.com/cosmos/cosmos-proto/cmd/protoc-gen-go-pulsar"
8 | _ "github.com/cosmos/gogoproto/protoc-gen-gocosmos"
9 | _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway"
10 | _ "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2"
11 | _ "golang.org/x/tools/cmd/goimports"
12 | _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc"
13 | _ "google.golang.org/protobuf/cmd/protoc-gen-go"
14 | )
15 |
--------------------------------------------------------------------------------
/proto/buf.gen.gogo.yaml:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from Ignite. You can edit
2 | # the file content but do not change the file name or path.
3 | #
4 | # buf.gen.gogo.yaml
5 | #
6 | version: v1
7 | plugins:
8 | - name: gocosmos
9 | out: .
10 | opt:
11 | - plugins=grpc
12 | - Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types
13 | - Mcosmos/orm/v1/orm.proto=cosmossdk.io/orm
14 | - name: grpc-gateway
15 | out: .
16 | opt:
17 | - logtostderr=true
18 | - allow_colon_final_segments=true
19 |
--------------------------------------------------------------------------------
/app/params/encoding.go:
--------------------------------------------------------------------------------
1 | package params
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/client"
5 | "github.com/cosmos/cosmos-sdk/codec"
6 | "github.com/cosmos/cosmos-sdk/codec/types"
7 | )
8 |
9 | // EncodingConfig specifies the concrete encoding types to use for a given app.
10 | // This is provided for compatibility between protobuf and amino implementations.
11 | type EncodingConfig struct {
12 | InterfaceRegistry types.InterfaceRegistry
13 | Codec codec.Codec
14 | TxConfig client.TxConfig
15 | Amino *codec.LegacyAmino
16 | }
17 |
--------------------------------------------------------------------------------
/x/btcbridge/keeper/event.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 |
6 | "github.com/sideprotocol/side/x/btcbridge/types"
7 | )
8 |
9 | func (k Keeper) EmitEvent(ctx sdk.Context, sender string, attr ...sdk.Attribute) {
10 | headerAttr := []sdk.Attribute{
11 | {
12 | Key: "sender",
13 | Value: sender,
14 | },
15 | }
16 |
17 | headerAttr = append(headerAttr, attr...)
18 | ctx.EventManager().EmitEvent(
19 | sdk.NewEvent(
20 | types.ModuleName,
21 | // attr...,
22 | headerAttr...,
23 | ),
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_update_params.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgUpdateParams{}
9 |
10 | // ValidateBasic performs basic MsgUpdateParams message validation.
11 | func (m *MsgUpdateParams) ValidateBasic() error {
12 | if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil {
13 | return errorsmod.Wrap(err, "invalid authority address")
14 | }
15 |
16 | if err := m.Params.Validate(); err != nil {
17 | return err
18 | }
19 |
20 | return nil
21 | }
22 |
--------------------------------------------------------------------------------
/x/incentive/types/genesis.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | // DefaultGenesis returns the default genesis state
4 | func DefaultGenesis() *GenesisState {
5 | return &GenesisState{
6 | // this line is used by starport scaffolding # genesis/types/default
7 | Params: DefaultParams(),
8 | }
9 | }
10 |
11 | // Validate performs basic genesis state validation returning an error upon any
12 | // failure.
13 | func (gs GenesisState) Validate() error {
14 | // this line is used by starport scaffolding # genesis/types/validate
15 |
16 | // validate params
17 | return gs.Params.Validate()
18 | }
19 |
--------------------------------------------------------------------------------
/x/incentive/types/msg_update_params.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgUpdateParams{}
9 |
10 | // ValidateBasic performs basic MsgUpdateParams message validation.
11 | func (m *MsgUpdateParams) ValidateBasic() error {
12 | if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil {
13 | return errorsmod.Wrap(err, "invalid authority address")
14 | }
15 |
16 | if err := m.Params.Validate(); err != nil {
17 | return err
18 | }
19 |
20 | return nil
21 | }
22 |
--------------------------------------------------------------------------------
/proto/buf.gen.pulsar.yaml:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from Ignite. You can edit
2 | # the file content but do not change the file name or path.
3 | #
4 | # buf.gen.pulsar.yaml
5 | #
6 | version: v1
7 | managed:
8 | enabled: true
9 | go_package_prefix:
10 | default: cosmossdk.io/api
11 | except:
12 | - buf.build/googleapis/googleapis
13 | - buf.build/cosmos/gogo-proto
14 | - buf.build/cosmos/cosmos-proto
15 | override:
16 | plugins:
17 | - name: go-pulsar
18 | out: ./api
19 | opt: paths=source_relative
20 | - name: go-grpc
21 | out: ./api
22 | opt: paths=source_relative
23 |
--------------------------------------------------------------------------------
/proto/side/btcbridge/genesis.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package side.btcbridge;
3 |
4 | import "gogoproto/gogo.proto";
5 | import "side/btcbridge/params.proto";
6 | import "side/btcbridge/btcbridge.proto";
7 |
8 | option go_package = "github.com/sideprotocol/side/x/btcbridge/types";
9 |
10 | // GenesisState defines the btc bridge module's genesis state.
11 | message GenesisState {
12 | Params params = 1 [(gogoproto.nullable) = false];
13 | // the chain tip of the bitcoin chain
14 | BlockHeader best_block_header = 2;
15 | repeated BlockHeader block_headers = 3;
16 | repeated UTXO utxos = 4;
17 | DKGRequest dkg_request= 5;
18 | }
19 |
--------------------------------------------------------------------------------
/proto/side/incentive/params.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package side.incentive;
3 |
4 | import "gogoproto/gogo.proto";
5 | import "cosmos/base/v1beta1/coin.proto";
6 |
7 | option go_package = "github.com/sideprotocol/side/x/incentive/types";
8 |
9 | // Params defines the parameters for the module.
10 | message Params {
11 | // Indicates if the incentive mechanism is enabled
12 | bool enabled = 1;
13 | // Reward per deposit tx via btc bridge
14 | cosmos.base.v1beta1.Coin reward_per_deposit = 2 [(gogoproto.nullable) = false];
15 | // Reward per withdrawal tx via btc bridge
16 | cosmos.base.v1beta1.Coin reward_per_withdraw = 3 [(gogoproto.nullable) = false];
17 | }
18 |
--------------------------------------------------------------------------------
/x/btcbridge/types/asset.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | // AssetTypeFromDenom returns the asset type according to the denom
9 | func AssetTypeFromDenom(denom string, p Params) AssetType {
10 | if denom == p.BtcVoucherDenom {
11 | return AssetType_ASSET_TYPE_BTC
12 | }
13 |
14 | if strings.HasPrefix(denom, fmt.Sprintf("%s/", RunesProtocolName)) {
15 | return AssetType_ASSET_TYPE_RUNES
16 | }
17 |
18 | return AssetType_ASSET_TYPE_UNSPECIFIED
19 | }
20 |
21 | // SupportedAssetTypes returns the currently supported asset types
22 | func SupportedAssetTypes() []AssetType {
23 | return []AssetType{AssetType_ASSET_TYPE_BTC, AssetType_ASSET_TYPE_RUNES}
24 | }
25 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_submit_fee_rate.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgSubmitFeeRate{}
9 |
10 | func NewMsgSubmitFeeRate(
11 | sender string,
12 | feeRate int64,
13 | ) *MsgSubmitFeeRate {
14 | return &MsgSubmitFeeRate{
15 | Sender: sender,
16 | FeeRate: feeRate,
17 | }
18 | }
19 |
20 | func (msg *MsgSubmitFeeRate) ValidateBasic() error {
21 | _, err := sdk.AccAddressFromBech32(msg.Sender)
22 | if err != nil {
23 | return errorsmod.Wrapf(err, "invalid sender address (%s)", err)
24 | }
25 |
26 | if msg.FeeRate <= 0 {
27 | return ErrInvalidFeeRate
28 | }
29 |
30 | return nil
31 | }
32 |
--------------------------------------------------------------------------------
/x/incentive/types/keys.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | const (
4 | // ModuleName defines the module name
5 | ModuleName = "incentive"
6 |
7 | // StoreKey defines the primary module store key
8 | StoreKey = ModuleName
9 |
10 | // RouterKey defines the module's message routing key
11 | RouterKey = ModuleName
12 |
13 | // MemStoreKey defines the in-memory store key
14 | MemStoreKey = "mem_incentive"
15 | )
16 |
17 | var (
18 | ParamsStoreKey = []byte{0x1}
19 |
20 | RewardStatsKey = []byte{0x11} // key for total reward statistics
21 | RewardsKeyPrefix = []byte{0x12} // prefix for each key to the rewards
22 | )
23 |
24 | func RewardsKey(address string) []byte {
25 | return append(RewardsKeyPrefix, []byte(address)...)
26 | }
27 |
--------------------------------------------------------------------------------
/proto/buf.yaml:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from Ignite. You can edit
2 | # the file content but do not change the file name or path.
3 | #
4 | # buf.yaml
5 | #
6 | version: v1
7 | deps:
8 | - buf.build/protocolbuffers/wellknowntypes
9 | - buf.build/cosmos/cosmos-sdk
10 | - buf.build/cosmos/cosmos-proto
11 | - buf.build/cosmos/gogo-proto
12 | - buf.build/googleapis/googleapis
13 | - buf.build/cosmos/ics23
14 | breaking:
15 | use:
16 | - FILE
17 | lint:
18 | use:
19 | - DEFAULT
20 | - COMMENTS
21 | - FILE_LOWER_SNAKE_CASE
22 | except:
23 | - UNARY_RPC
24 | - COMMENT_FIELD
25 | - SERVICE_SUFFIX
26 | - PACKAGE_VERSION_SUFFIX
27 | - RPC_REQUEST_STANDARD_NAME
28 | ignore:
29 | - tendermint
30 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_submit_headers.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgSubmitBlockHeaders{}
9 |
10 | func NewMsgSubmitBlockHeaders(
11 | sender string,
12 | headers []*BlockHeader,
13 | ) *MsgSubmitBlockHeaders {
14 | return &MsgSubmitBlockHeaders{
15 | Sender: sender,
16 | BlockHeaders: headers,
17 | }
18 | }
19 |
20 | func (msg *MsgSubmitBlockHeaders) ValidateBasic() error {
21 | _, err := sdk.AccAddressFromBech32(msg.Sender)
22 | if err != nil {
23 | return errorsmod.Wrapf(err, "invalid sender address (%s)", err)
24 | }
25 |
26 | if err := BlockHeaders(msg.BlockHeaders).Validate(); err != nil {
27 | return err
28 | }
29 |
30 | return nil
31 | }
32 |
--------------------------------------------------------------------------------
/app/wasm.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
5 | )
6 |
7 | const (
8 | // DefaultInstanceCost is initially set the same as in wasmd
9 | DefaultInstanceCost uint64 = 60_000
10 | // DefaultCompileCost set to a large number for testing
11 | DefaultCompileCost uint64 = 100
12 | )
13 |
14 | // MunGasRegisterConfig is defaults plus a custom compile amount
15 | func GasRegisterConfig() wasmtypes.WasmGasRegisterConfig {
16 | gasConfig := wasmtypes.DefaultGasRegisterConfig()
17 | gasConfig.InstanceCost = DefaultInstanceCost
18 | gasConfig.CompileCost = DefaultCompileCost
19 |
20 | return gasConfig
21 | }
22 |
23 | func NewSideWasmGasRegister() wasmtypes.WasmGasRegister {
24 | return wasmtypes.NewWasmGasRegister(GasRegisterConfig())
25 | }
26 |
--------------------------------------------------------------------------------
/proto/side/incentive/incentive.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package side.incentive;
3 |
4 | import "gogoproto/gogo.proto";
5 | import "cosmos/base/v1beta1/coin.proto";
6 |
7 | option go_package = "github.com/sideprotocol/side/x/incentive/types";
8 |
9 | // Rewards
10 | message Rewards {
11 | string address = 1;
12 | uint64 deposit_count = 2;
13 | uint64 withdraw_count = 3;
14 | cosmos.base.v1beta1.Coin deposit_reward = 4 [(gogoproto.nullable) = false];
15 | cosmos.base.v1beta1.Coin withdraw_reward = 5 [(gogoproto.nullable) = false];
16 | cosmos.base.v1beta1.Coin total_amount = 6 [(gogoproto.nullable) = false];
17 | }
18 |
19 | // Reward Statistics
20 | message RewardStats {
21 | uint64 address_count = 1;
22 | uint64 tx_count = 2;
23 | cosmos.base.v1beta1.Coin total_reward_amount = 3 [(gogoproto.nullable) = false];
24 | }
25 |
--------------------------------------------------------------------------------
/app/genesis.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 |
6 | "github.com/cosmos/cosmos-sdk/codec"
7 | )
8 |
9 | // The genesis state of the blockchain is represented here as a map of raw json
10 | // messages key'd by a identifier string.
11 | // The identifier is used to determine which module genesis information belongs
12 | // to so it may be appropriately routed during init chain.
13 | // Within this application default genesis information is retrieved from
14 | // the ModuleBasicManager which populates json from each BasicModule
15 | // object provided to it during init.
16 | type GenesisState map[string]json.RawMessage
17 |
18 | // NewDefaultGenesisState generates the default state for the application.
19 | func NewDefaultGenesisState(cdc codec.JSONCodec) GenesisState {
20 | return ModuleBasics.DefaultGenesis(cdc)
21 | }
22 |
--------------------------------------------------------------------------------
/x/incentive/module/genesis.go:
--------------------------------------------------------------------------------
1 | package incentive
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 |
6 | "github.com/sideprotocol/side/x/incentive/keeper"
7 | "github.com/sideprotocol/side/x/incentive/types"
8 | )
9 |
10 | // InitGenesis initializes the module's state from a provided genesis state.
11 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) {
12 | // this line is used by starport scaffolding # genesis/module/init
13 | k.SetParams(ctx, genState.Params)
14 | }
15 |
16 | // ExportGenesis returns the module's exported genesis
17 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
18 | genesis := types.DefaultGenesis()
19 | genesis.Params = k.GetParams(ctx)
20 |
21 | // this line is used by starport scaffolding # genesis/module/export
22 |
23 | return genesis
24 | }
25 |
--------------------------------------------------------------------------------
/x/incentive/types/codec.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/codec"
5 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | "github.com/cosmos/cosmos-sdk/types/msgservice"
8 | )
9 |
10 | func RegisterCodec(cdc *codec.LegacyAmino) {
11 | cdc.RegisterConcrete(&MsgUpdateParams{}, "incentive/MsgUpdateParams", nil)
12 | // this line is used by starport scaffolding # 2
13 | }
14 |
15 | func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
16 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgUpdateParams{})
17 | // this line is used by starport scaffolding # 3
18 |
19 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
20 | }
21 |
22 | var (
23 | Amino = codec.NewLegacyAmino()
24 | ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry())
25 | )
26 |
--------------------------------------------------------------------------------
/app/config.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "github.com/btcsuite/btcd/chaincfg"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | )
8 |
9 | func init() {
10 | // Set prefixes
11 | accountPubKeyPrefix := AccountAddressPrefix + "pub"
12 | validatorAddressPrefix := AccountAddressPrefix + "valoper"
13 | validatorPubKeyPrefix := AccountAddressPrefix + "valoperpub"
14 | consNodeAddressPrefix := AccountAddressPrefix + "valcons"
15 | consNodePubKeyPrefix := AccountAddressPrefix + "valconspub"
16 |
17 | // Set and seal config
18 | config := sdk.GetConfig()
19 | config.SetBech32PrefixForAccount(AccountAddressPrefix, accountPubKeyPrefix)
20 | config.SetBech32PrefixForValidator(validatorAddressPrefix, validatorPubKeyPrefix)
21 | config.SetBech32PrefixForConsensusNode(consNodeAddressPrefix, consNodePubKeyPrefix)
22 | config.SetBtcChainCfg(&chaincfg.MainNetParams)
23 | config.Seal()
24 | }
25 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_withdraw_to_bitcoin.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgWithdrawToBitcoin{}
9 |
10 | func NewMsgWithdrawToBitcoin(
11 | sender string,
12 | amount string,
13 | ) *MsgWithdrawToBitcoin {
14 | return &MsgWithdrawToBitcoin{
15 | Sender: sender,
16 | Amount: amount,
17 | }
18 | }
19 |
20 | func (msg *MsgWithdrawToBitcoin) ValidateBasic() error {
21 | _, err := sdk.AccAddressFromBech32(msg.Sender)
22 | if err != nil {
23 | return errorsmod.Wrapf(err, "invalid sender address (%s)", err)
24 | }
25 |
26 | if !IsValidBtcAddress(msg.Sender) {
27 | return ErrInvalidBtcAddress
28 | }
29 |
30 | _, err = sdk.ParseCoinNormalized(msg.Amount)
31 | if err != nil {
32 | return errorsmod.Wrapf(err, "invalid withdrawal amount")
33 | }
34 |
35 | return nil
36 | }
37 |
--------------------------------------------------------------------------------
/proto/buf.lock:
--------------------------------------------------------------------------------
1 | # This file is auto-generated from Ignite.
2 | # DO NOT EDIT
3 | #
4 | # buf.lock
5 | #
6 | version: v1
7 | deps:
8 | - remote: buf.build
9 | owner: cosmos
10 | repository: cosmos-proto
11 | commit: 1935555c206d4afb9e94615dfd0fad31
12 | - remote: buf.build
13 | owner: cosmos
14 | repository: cosmos-sdk
15 | commit: 954f7b05f38440fc8250134b15adec47
16 | - remote: buf.build
17 | owner: cosmos
18 | repository: gogo-proto
19 | commit: 34d970b699f84aa382f3c29773a60836
20 | - remote: buf.build
21 | owner: cosmos
22 | repository: ics23
23 | commit: 3c44d8daa8b44059ac744cd17d4a49d7
24 | - remote: buf.build
25 | owner: googleapis
26 | repository: googleapis
27 | commit: 75b4300737fb4efca0831636be94e517
28 | - remote: buf.build
29 | owner: protocolbuffers
30 | repository: wellknowntypes
31 | commit: 44e83bc050a4497fa7b36b34d95ca156
32 |
--------------------------------------------------------------------------------
/x/btcbridge/keeper/hooks.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 | )
6 |
7 | // AfterDeposit performs the extended logic after deposit
8 | func (k Keeper) AfterDeposit(ctx sdk.Context, addr string) error {
9 | // distribute deposit reward
10 | if k.incentiveKeeper.DepositIncentiveEnabled(ctx) {
11 | _ = k.incentiveKeeper.DistributeDepositReward(ctx, addr)
12 | }
13 |
14 | return nil
15 | }
16 |
17 | // AfterWithdraw performs the extended logic after withdrawal
18 | func (k Keeper) AfterWithdraw(ctx sdk.Context, txHash string) error {
19 | // distribute rewards for all withdrawals
20 | if k.incentiveKeeper.WithdrawIncentiveEnabled(ctx) {
21 | withdrawRequests := k.GetWithdrawRequestsByTxHash(ctx, txHash)
22 | for _, req := range withdrawRequests {
23 | _ = k.incentiveKeeper.DistributeWithdrawReward(ctx, req.Address)
24 | }
25 | }
26 |
27 | return nil
28 | }
29 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_submit_signatures.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgSubmitSignatures{}
9 |
10 | func NewMsgSubmitSignatures(
11 | sender string,
12 | txid string,
13 | pbst string,
14 | ) *MsgSubmitSignatures {
15 | return &MsgSubmitSignatures{
16 | Sender: sender,
17 | Txid: txid,
18 | Psbt: pbst,
19 | }
20 | }
21 |
22 | func (msg *MsgSubmitSignatures) ValidateBasic() error {
23 | _, err := sdk.AccAddressFromBech32(msg.Sender)
24 | if err != nil {
25 | return errorsmod.Wrapf(err, "invalid sender address (%s)", err)
26 | }
27 |
28 | if len(msg.Txid) == 0 {
29 | return errorsmod.Wrap(ErrInvalidSignatures, "txid cannot be empty")
30 | }
31 |
32 | if len(msg.Psbt) == 0 {
33 | return errorsmod.Wrap(ErrInvalidSignatures, "psbt cannot be empty")
34 | }
35 |
36 | return nil
37 | }
38 |
--------------------------------------------------------------------------------
/docs/docs.go:
--------------------------------------------------------------------------------
1 | package docs
2 |
3 | import (
4 | "embed"
5 | httptemplate "html/template"
6 | "net/http"
7 |
8 | "github.com/gorilla/mux"
9 | )
10 |
11 | const (
12 | apiFile = "/static/openapi.yml"
13 | indexFile = "template/index.tpl"
14 | )
15 |
16 | //go:embed static
17 | var Static embed.FS
18 |
19 | //go:embed template
20 | var template embed.FS
21 |
22 | func RegisterOpenAPIService(appName string, rtr *mux.Router) {
23 | rtr.Handle(apiFile, http.FileServer(http.FS(Static)))
24 | rtr.HandleFunc("/", handler(appName))
25 | }
26 |
27 | // handler returns an http handler that servers OpenAPI console for an OpenAPI spec at specURL.
28 | func handler(title string) http.HandlerFunc {
29 | t, _ := httptemplate.ParseFS(template, indexFile)
30 |
31 | return func(w http.ResponseWriter, req *http.Request) {
32 | t.Execute(w, struct {
33 | Title string
34 | URL string
35 | }{
36 | title,
37 | apiFile,
38 | })
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/x/btcbridge/types/merkle_proof.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "encoding/base64"
5 |
6 | "github.com/btcsuite/btcd/blockchain"
7 | "github.com/btcsuite/btcd/chaincfg/chainhash"
8 | )
9 |
10 | // VerifyMerkleProof verifies the merkle proof
11 | func VerifyMerkleProof(proofs []string, txHash, root *chainhash.Hash) bool {
12 | current := txHash
13 | for _, proof := range proofs {
14 |
15 | bytes, err := base64.StdEncoding.DecodeString(proof)
16 | if err != nil {
17 | return false
18 | }
19 | position := bytes[0]
20 | p := current
21 | if len(bytes) > 1 {
22 | p, err = chainhash.NewHash(bytes[1:])
23 | if err != nil {
24 | return false
25 | }
26 | }
27 |
28 | var temp chainhash.Hash
29 | if position == 0 {
30 | temp = blockchain.HashMerkleBranches(current, p)
31 | } else {
32 | temp = blockchain.HashMerkleBranches(p, current)
33 | }
34 | current = &temp
35 | }
36 |
37 | return current.IsEqual(root)
38 | }
39 |
--------------------------------------------------------------------------------
/docs/template/index.tpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ .Title }}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
24 |
25 |
26 | Footer
27 | © 2022 GitHub, Inc.
28 | Footer navigation
29 |
--------------------------------------------------------------------------------
/x/incentive/module/genesis_test.go:
--------------------------------------------------------------------------------
1 | package incentive_test
2 |
3 | // Path: x/incentive/genesis_test.go
4 |
5 | import (
6 | "testing"
7 |
8 | "github.com/stretchr/testify/require"
9 |
10 | keepertest "github.com/sideprotocol/side/testutil/keeper"
11 | "github.com/sideprotocol/side/testutil/nullify"
12 | incentive "github.com/sideprotocol/side/x/incentive/module"
13 | "github.com/sideprotocol/side/x/incentive/types"
14 | )
15 |
16 | func TestGenesis(t *testing.T) {
17 | mnemonic := "sunny bamboo garlic fold reopen exile letter addict forest vessel square lunar shell number deliver cruise calm artist fire just kangaroo suit wheel extend"
18 | println(mnemonic)
19 |
20 | genesisState := types.DefaultGenesis()
21 |
22 | k, ctx := keepertest.IncentiveKeeper(t)
23 | incentive.InitGenesis(ctx, k, *genesisState)
24 | got := incentive.ExportGenesis(ctx, k)
25 | require.NotNil(t, got)
26 |
27 | nullify.Fill(&genesisState)
28 | nullify.Fill(got)
29 |
30 | // this line is used by starport scaffolding # genesis/test/assert
31 | }
32 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_update_trusted_non_btc_relayers.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgUpdateTrustedNonBtcRelayers{}
9 |
10 | func NewMsgUpdateTrustedNonBtcRelayers(
11 | sender string,
12 | relayers []string,
13 | ) *MsgUpdateTrustedNonBtcRelayers {
14 | return &MsgUpdateTrustedNonBtcRelayers{
15 | Sender: sender,
16 | Relayers: relayers,
17 | }
18 | }
19 |
20 | func (msg *MsgUpdateTrustedNonBtcRelayers) ValidateBasic() error {
21 | _, err := sdk.AccAddressFromBech32(msg.Sender)
22 | if err != nil {
23 | return errorsmod.Wrapf(err, "invalid sender address (%s)", err)
24 | }
25 |
26 | if len(msg.Relayers) == 0 {
27 | return errorsmod.Wrapf(ErrInvalidRelayers, "relayers can not be empty")
28 | }
29 |
30 | for _, relayer := range msg.Relayers {
31 | _, err := sdk.AccAddressFromBech32(relayer)
32 | if err != nil {
33 | return errorsmod.Wrapf(err, "invalid relayer address (%s)", err)
34 | }
35 | }
36 |
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_update_trusted_fee_providers.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgUpdateTrustedFeeProviders{}
9 |
10 | func NewMsgUpdateTrustedFeeProviders(
11 | sender string,
12 | feeProviders []string,
13 | ) *MsgUpdateTrustedFeeProviders {
14 | return &MsgUpdateTrustedFeeProviders{
15 | Sender: sender,
16 | FeeProviders: feeProviders,
17 | }
18 | }
19 |
20 | func (msg *MsgUpdateTrustedFeeProviders) ValidateBasic() error {
21 | _, err := sdk.AccAddressFromBech32(msg.Sender)
22 | if err != nil {
23 | return errorsmod.Wrapf(err, "invalid sender address (%s)", err)
24 | }
25 |
26 | if len(msg.FeeProviders) == 0 {
27 | return errorsmod.Wrapf(ErrInvalidFeeProviders, "fee providers can not be empty")
28 | }
29 |
30 | for _, provider := range msg.FeeProviders {
31 | _, err := sdk.AccAddressFromBech32(provider)
32 | if err != nil {
33 | return errorsmod.Wrapf(err, "invalid fee provider address (%s)", err)
34 | }
35 | }
36 |
37 | return nil
38 | }
39 |
--------------------------------------------------------------------------------
/x/btcbridge/keeper/fee_rate.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 |
6 | "github.com/sideprotocol/side/x/btcbridge/types"
7 | )
8 |
9 | // SetFeeRate sets the bitcoin network fee rate
10 | func (k Keeper) SetFeeRate(ctx sdk.Context, feeRate int64) {
11 | store := ctx.KVStore(k.storeKey)
12 |
13 | feeRateWithHeight := types.FeeRate{
14 | Value: feeRate,
15 | Height: ctx.BlockHeight(),
16 | }
17 |
18 | store.Set(types.BtcFeeRateKey, k.cdc.MustMarshal(&feeRateWithHeight))
19 | }
20 |
21 | // GetFeeRate gets the bitcoin network fee rate
22 | func (k Keeper) GetFeeRate(ctx sdk.Context) *types.FeeRate {
23 | store := ctx.KVStore(k.storeKey)
24 |
25 | var feeRate types.FeeRate
26 | bz := store.Get(types.BtcFeeRateKey)
27 | k.cdc.MustUnmarshal(bz, &feeRate)
28 |
29 | return &feeRate
30 | }
31 |
32 | // CheckFeeRate checks the given fee rate
33 | func (k Keeper) CheckFeeRate(ctx sdk.Context, feeRate *types.FeeRate) error {
34 | if feeRate.Value == 0 || ctx.BlockHeight()-feeRate.Height > k.GetParams(ctx).FeeRateValidityPeriod {
35 | return types.ErrInvalidFeeRate
36 | }
37 |
38 | return nil
39 | }
40 |
--------------------------------------------------------------------------------
/x/incentive/types/params.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var (
9 | // default reward per deposit tx via btc bridge
10 | DefaultRewardPerDeposit = sdk.NewInt64Coin("uside", 100000000) // 100SIDE
11 |
12 | // default reward per withdrawal tx via btc bridge
13 | DefaultRewardPerWithdraw = sdk.NewInt64Coin("uside", 100000000) // 100SIDE
14 | )
15 |
16 | // NewParams creates a new Params instance
17 | func NewParams() Params {
18 | return Params{
19 | Enabled: true,
20 | RewardPerDeposit: DefaultRewardPerDeposit,
21 | RewardPerWithdraw: DefaultRewardPerWithdraw,
22 | }
23 | }
24 |
25 | // DefaultParams returns a default set of parameters
26 | func DefaultParams() Params {
27 | return NewParams()
28 | }
29 |
30 | // Validate validates the set of params
31 | func (p Params) Validate() error {
32 | if !p.RewardPerDeposit.IsValid() {
33 | return errorsmod.Wrap(ErrInvalidParams, "invalid deposit reward")
34 | }
35 |
36 | if !p.RewardPerWithdraw.IsValid() {
37 | return errorsmod.Wrap(ErrInvalidParams, "invalid withdrawal reward")
38 | }
39 |
40 | return nil
41 | }
42 |
--------------------------------------------------------------------------------
/x/incentive/keeper/msg_server.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 |
6 | errorsmod "cosmossdk.io/errors"
7 | sdk "github.com/cosmos/cosmos-sdk/types"
8 | govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
9 |
10 | "github.com/sideprotocol/side/x/incentive/types"
11 | )
12 |
13 | type msgServer struct {
14 | Keeper
15 | }
16 |
17 | // UpdateParams updates the module params.
18 | func (m msgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) {
19 | if m.authority != msg.Authority {
20 | return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", m.authority, msg.Authority)
21 | }
22 |
23 | if err := msg.ValidateBasic(); err != nil {
24 | return nil, err
25 | }
26 |
27 | ctx := sdk.UnwrapSDKContext(goCtx)
28 | m.SetParams(ctx, msg.Params)
29 |
30 | return &types.MsgUpdateParamsResponse{}, nil
31 | }
32 |
33 | // NewMsgServerImpl returns an implementation of the MsgServer interface
34 | // for the provided Keeper.
35 | func NewMsgServerImpl(keeper Keeper) types.MsgServer {
36 | return &msgServer{Keeper: keeper}
37 | }
38 |
39 | var _ types.MsgServer = msgServer{}
40 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_submit_withdraw_transaction.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgSubmitWithdrawTransaction{}
9 |
10 | func NewMsgSubmitWithdrawTransaction(
11 | sender string,
12 | blockhash string,
13 | transaction string,
14 | proof []string,
15 | ) *MsgSubmitWithdrawTransaction {
16 | return &MsgSubmitWithdrawTransaction{
17 | Sender: sender,
18 | Blockhash: blockhash,
19 | TxBytes: transaction,
20 | Proof: proof,
21 | }
22 | }
23 |
24 | func (msg *MsgSubmitWithdrawTransaction) ValidateBasic() error {
25 | _, err := sdk.AccAddressFromBech32(msg.Sender)
26 | if err != nil {
27 | return errorsmod.Wrapf(err, "invalid sender address (%s)", err)
28 | }
29 |
30 | if len(msg.Blockhash) == 0 {
31 | return errorsmod.Wrap(ErrInvalidBtcTransaction, "blockhash cannot be empty")
32 | }
33 |
34 | if len(msg.TxBytes) == 0 {
35 | return errorsmod.Wrap(ErrInvalidBtcTransaction, "transaction cannot be empty")
36 | }
37 |
38 | if len(msg.Proof) == 0 {
39 | return errorsmod.Wrap(ErrInvalidBtcTransaction, "proof cannot be empty")
40 | }
41 |
42 | return nil
43 | }
44 |
--------------------------------------------------------------------------------
/app/test_support.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper"
5 | ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper"
6 |
7 | "github.com/cosmos/cosmos-sdk/baseapp"
8 | authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
9 | bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper"
10 | stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper"
11 |
12 | btcbridgekeeper "github.com/sideprotocol/side/x/btcbridge/keeper"
13 | )
14 |
15 | func (app *App) GetIBCKeeper() *ibckeeper.Keeper {
16 | return app.IBCKeeper
17 | }
18 |
19 | func (app *App) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper {
20 | return app.ScopedIBCKeeper
21 | }
22 |
23 | func (app *App) GetBaseApp() *baseapp.BaseApp {
24 | return app.BaseApp
25 | }
26 |
27 | func (app *App) GetBankKeeper() bankkeeper.Keeper {
28 | return app.BankKeeper
29 | }
30 |
31 | func (app *App) GetStakingKeeper() *stakingkeeper.Keeper {
32 | return app.StakingKeeper
33 | }
34 |
35 | func (app *App) GetAccountKeeper() authkeeper.AccountKeeper {
36 | return app.AccountKeeper
37 | }
38 |
39 | func (app *App) GetBtcBridgeKeeper() btcbridgekeeper.Keeper {
40 | return app.BtcBridgeKeeper
41 | }
42 |
--------------------------------------------------------------------------------
/app/encoding.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/codec"
5 | "github.com/cosmos/cosmos-sdk/codec/types"
6 | "github.com/cosmos/cosmos-sdk/std"
7 | "github.com/cosmos/cosmos-sdk/x/auth/tx"
8 |
9 | "github.com/sideprotocol/side/app/params"
10 | )
11 |
12 | // makeEncodingConfig creates an EncodingConfig for an amino based test configuration.
13 | func makeEncodingConfig() params.EncodingConfig {
14 | amino := codec.NewLegacyAmino()
15 | interfaceRegistry := types.NewInterfaceRegistry()
16 | marshaler := codec.NewProtoCodec(interfaceRegistry)
17 | txCfg := tx.NewTxConfig(marshaler, tx.DefaultSignModes)
18 |
19 | return params.EncodingConfig{
20 | InterfaceRegistry: interfaceRegistry,
21 | Codec: marshaler,
22 | TxConfig: txCfg,
23 | Amino: amino,
24 | }
25 | }
26 |
27 | // MakeEncodingConfig creates an EncodingConfig for testing
28 | func MakeEncodingConfig() params.EncodingConfig {
29 | encodingConfig := makeEncodingConfig()
30 | std.RegisterLegacyAminoCodec(encodingConfig.Amino)
31 | std.RegisterInterfaces(encodingConfig.InterfaceRegistry)
32 | ModuleBasics.RegisterLegacyAminoCodec(encodingConfig.Amino)
33 | ModuleBasics.RegisterInterfaces(encodingConfig.InterfaceRegistry)
34 | return encodingConfig
35 | }
36 |
--------------------------------------------------------------------------------
/x/incentive/types/expected_keepers.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "context"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | banktype "github.com/cosmos/cosmos-sdk/x/bank/types"
8 | )
9 |
10 | // BankKeeper defines the expected interface needed to retrieve account balances.
11 | type BankKeeper interface {
12 | SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins
13 | // Methods imported from bank should be defined here
14 |
15 | SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
16 |
17 | SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt sdk.Coins) error
18 | SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
19 | SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
20 | SetDenomMetaData(ctx context.Context, denomMetaData banktype.Metadata)
21 |
22 | MintCoins(ctx context.Context, moduleName string, amounts sdk.Coins) error
23 | BurnCoins(ctx context.Context, moduleName string, amounts sdk.Coins) error
24 |
25 | HasSupply(ctx context.Context, denom string) bool
26 | GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin
27 | }
28 |
--------------------------------------------------------------------------------
/proto/side/incentive/tx.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package side.incentive;
3 |
4 | import "cosmos/msg/v1/msg.proto";
5 | import "gogoproto/gogo.proto";
6 | import "side/incentive/params.proto";
7 |
8 | option go_package = "github.com/sideprotocol/side/x/incentive/types";
9 |
10 | // Msg defines the Msg service.
11 | service Msg {
12 | option (cosmos.msg.v1.service) = true;
13 |
14 | // UpdateParams defines a governance operation for updating the x/incentive module
15 | // parameters. The authority defaults to the x/gov module account.
16 | //
17 | // Since: cosmos-sdk 0.47
18 | rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
19 | }
20 |
21 | // MsgUpdateParams is the Msg/UpdateParams request type.
22 | //
23 | // Since: cosmos-sdk 0.47
24 | message MsgUpdateParams {
25 | option (cosmos.msg.v1.signer) = "authority";
26 |
27 | // authority is the address that controls the module (defaults to x/gov unless overwritten).
28 | string authority = 1;
29 |
30 | // params defines the x/incentive parameters to be updated.
31 | //
32 | // NOTE: All parameters must be supplied.
33 | Params params = 2 [(gogoproto.nullable) = false];
34 | }
35 |
36 | // MsgUpdateParamsResponse defines the Msg/UpdateParams response type.
37 | //
38 | // Since: cosmos-sdk 0.47
39 | message MsgUpdateParamsResponse {}
40 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_submit_deposit_transaction.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | errorsmod "cosmossdk.io/errors"
5 | sdk "github.com/cosmos/cosmos-sdk/types"
6 | )
7 |
8 | var _ sdk.Msg = &MsgSubmitDepositTransaction{}
9 |
10 | func NewMsgSubmitDepositTransaction(
11 | sender string,
12 | blockhash string,
13 | prevTx string,
14 | tx string,
15 | proof []string,
16 | ) *MsgSubmitDepositTransaction {
17 | return &MsgSubmitDepositTransaction{
18 | Sender: sender,
19 | Blockhash: blockhash,
20 | PrevTxBytes: prevTx,
21 | TxBytes: tx,
22 | Proof: proof,
23 | }
24 | }
25 |
26 | func (msg *MsgSubmitDepositTransaction) ValidateBasic() error {
27 | _, err := sdk.AccAddressFromBech32(msg.Sender)
28 | if err != nil {
29 | return errorsmod.Wrapf(err, "invalid sender address (%s)", err)
30 | }
31 |
32 | if len(msg.Blockhash) == 0 {
33 | return errorsmod.Wrap(ErrInvalidBtcTransaction, "blockhash cannot be empty")
34 | }
35 |
36 | if len(msg.PrevTxBytes) == 0 {
37 | return errorsmod.Wrap(ErrInvalidBtcTransaction, "previous transaction cannot be empty")
38 | }
39 |
40 | if len(msg.TxBytes) == 0 {
41 | return errorsmod.Wrap(ErrInvalidBtcTransaction, "transaction cannot be empty")
42 | }
43 |
44 | if len(msg.Proof) == 0 {
45 | return errorsmod.Wrap(ErrInvalidBtcTransaction, "proof cannot be empty")
46 | }
47 |
48 | return nil
49 | }
50 |
--------------------------------------------------------------------------------
/x/incentive/keeper/queries.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "context"
5 |
6 | "google.golang.org/grpc/codes"
7 | "google.golang.org/grpc/status"
8 |
9 | sdk "github.com/cosmos/cosmos-sdk/types"
10 |
11 | "github.com/sideprotocol/side/x/incentive/types"
12 | )
13 |
14 | var _ types.QueryServer = Keeper{}
15 |
16 | func (k Keeper) Params(goCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
17 | if req == nil {
18 | return nil, status.Error(codes.InvalidArgument, "invalid request")
19 | }
20 |
21 | ctx := sdk.UnwrapSDKContext(goCtx)
22 |
23 | return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil
24 | }
25 |
26 | func (k Keeper) Rewards(goCtx context.Context, req *types.QueryRewardsRequest) (*types.QueryRewardsResponse, error) {
27 | if req == nil {
28 | return nil, status.Error(codes.InvalidArgument, "invalid request")
29 | }
30 |
31 | ctx := sdk.UnwrapSDKContext(goCtx)
32 |
33 | return &types.QueryRewardsResponse{
34 | Rewards: k.GetRewards(ctx, req.Address),
35 | }, nil
36 | }
37 |
38 | func (k Keeper) RewardStats(goCtx context.Context, req *types.QueryRewardStatsRequest) (*types.QueryRewardStatsResponse, error) {
39 | if req == nil {
40 | return nil, status.Error(codes.InvalidArgument, "invalid request")
41 | }
42 |
43 | ctx := sdk.UnwrapSDKContext(goCtx)
44 |
45 | return &types.QueryRewardStatsResponse{
46 | RewardStats: k.GetRewardStats(ctx),
47 | }, nil
48 | }
49 |
--------------------------------------------------------------------------------
/x/incentive/keeper/keeper.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "cosmossdk.io/log"
5 | storetypes "cosmossdk.io/store/types"
6 | "github.com/cosmos/cosmos-sdk/codec"
7 | sdk "github.com/cosmos/cosmos-sdk/types"
8 |
9 | "github.com/sideprotocol/side/x/incentive/types"
10 | )
11 |
12 | type Keeper struct {
13 | cdc codec.BinaryCodec
14 | storeKey storetypes.StoreKey
15 | memKey storetypes.StoreKey
16 |
17 | bankKeeper types.BankKeeper
18 |
19 | authority string
20 | }
21 |
22 | func NewKeeper(
23 | cdc codec.BinaryCodec,
24 | storeKey,
25 | memKey storetypes.StoreKey,
26 | bankKeeper types.BankKeeper,
27 | authority string,
28 | ) Keeper {
29 | return Keeper{
30 | cdc: cdc,
31 | storeKey: storeKey,
32 | memKey: memKey,
33 | bankKeeper: bankKeeper,
34 | authority: authority,
35 | }
36 | }
37 |
38 | func (k Keeper) Logger(ctx sdk.Context) log.Logger {
39 | sdkCtx := sdk.UnwrapSDKContext(ctx)
40 | return sdkCtx.Logger().With("module", "x/"+types.ModuleName)
41 | }
42 |
43 | func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
44 | store := ctx.KVStore(k.storeKey)
45 |
46 | bz := k.cdc.MustMarshal(¶ms)
47 |
48 | store.Set(types.ParamsStoreKey, bz)
49 | }
50 |
51 | func (k Keeper) GetParams(ctx sdk.Context) types.Params {
52 | store := ctx.KVStore(k.storeKey)
53 |
54 | var params types.Params
55 | bz := store.Get(types.ParamsStoreKey)
56 | k.cdc.MustUnmarshal(bz, ¶ms)
57 |
58 | return params
59 | }
60 |
--------------------------------------------------------------------------------
/config.yml:
--------------------------------------------------------------------------------
1 | version: 1
2 | build:
3 | binary: sided
4 | proto:
5 | path: proto
6 | third_party_paths:
7 | - third_party/proto
8 | - proto_vendor
9 | accounts:
10 | - name: alice
11 | coins:
12 | - 10000000000000000000000000000000uside
13 | - 10000000000000000000000000000000uusdc
14 | - 10000000000000000000000000000000uusdt
15 |
16 | - name: bob
17 | mnemonic: "furnace wild gravity resist heavy beef bundle deliver design service cycle monkey"
18 | coins:
19 | - 10000000000000000000000000000000uside
20 | - 10000000000000000000000000000000uusdc
21 | - 10000000000000000000000000000000uusdt
22 | # faucet:
23 | # name: bob
24 | # coins:
25 | # - 10uside
26 | # host: 0.0.0.0:4500
27 | genesis:
28 | app_state:
29 | crisis:
30 | constant_fee:
31 | denom: uside
32 | gov:
33 | deposit_params:
34 | min_deposit:
35 | - amount: "10000000"
36 | denom: uside
37 | params:
38 | min_deposit:
39 | - amount: "10000000"
40 | denom: uside
41 | voting_period: "60s"
42 | mint:
43 | params:
44 | mint_denom: uside
45 | staking:
46 | params:
47 | bond_denom: uside
48 | chain_id: grimoria-testnet-1
49 | consensus_params:
50 | block:
51 | max_gas: "100000000"
52 | validators:
53 | - name: alice
54 | bonded: 10000000000000000000000uside
55 | home: $HOME/.side
56 | client:
57 | openapi:
58 | path: "docs/static/openapi.yml"
59 | prefix:
60 | address: "side"
61 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_complete_dkg.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "encoding/hex"
5 |
6 | errorsmod "cosmossdk.io/errors"
7 | "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
8 | sdk "github.com/cosmos/cosmos-sdk/types"
9 | )
10 |
11 | var _ sdk.Msg = &MsgCompleteDKG{}
12 |
13 | func NewMsgCompleteDKG(
14 | sender string,
15 | id uint64,
16 | vaults []string,
17 | consAddress string,
18 | signature string,
19 | ) *MsgCompleteDKG {
20 | return &MsgCompleteDKG{
21 | Sender: sender,
22 | Id: id,
23 | Vaults: vaults,
24 | ConsensusAddress: consAddress,
25 | Signature: signature,
26 | }
27 | }
28 |
29 | // ValidateBasic performs basic MsgCompleteDKG message validation.
30 | func (m *MsgCompleteDKG) ValidateBasic() error {
31 | if _, err := sdk.AccAddressFromBech32(m.Sender); err != nil {
32 | return errorsmod.Wrap(err, "invalid sender address")
33 | }
34 |
35 | if len(m.Vaults) == 0 {
36 | return ErrInvalidDKGCompletionRequest
37 | }
38 |
39 | vaults := make(map[string]bool)
40 | for _, v := range m.Vaults {
41 | _, err := sdk.AccAddressFromBech32(v)
42 | if err != nil || vaults[v] {
43 | return ErrInvalidDKGCompletionRequest
44 | }
45 |
46 | vaults[v] = true
47 | }
48 |
49 | if _, err := sdk.ConsAddressFromHex(m.ConsensusAddress); err != nil {
50 | return ErrInvalidDKGCompletionRequest
51 | }
52 |
53 | sigBytes, err := hex.DecodeString(m.Signature)
54 | if err != nil {
55 | return ErrInvalidDKGCompletionRequest
56 | }
57 |
58 | if len(sigBytes) != ed25519.SignatureSize {
59 | return ErrInvalidDKGCompletionRequest
60 | }
61 |
62 | return nil
63 | }
64 |
--------------------------------------------------------------------------------
/testutil/nullify/nullify.go:
--------------------------------------------------------------------------------
1 | // Package nullify provides methods to init nil values structs for test assertion.
2 | package nullify
3 |
4 | import (
5 | "reflect"
6 | "unsafe"
7 |
8 | sdk "github.com/cosmos/cosmos-sdk/types"
9 | )
10 |
11 | var (
12 | coinType = reflect.TypeOf(sdk.Coin{})
13 | coinsType = reflect.TypeOf(sdk.Coins{})
14 | )
15 |
16 | // Fill analyze all struct fields and slices with
17 | // reflection and initialize the nil and empty slices,
18 | // structs, and pointers.
19 | func Fill(x interface{}) interface{} {
20 | v := reflect.Indirect(reflect.ValueOf(x))
21 | switch v.Kind() {
22 | case reflect.Slice:
23 | for i := 0; i < v.Len(); i++ {
24 | obj := v.Index(i)
25 | objPt := reflect.NewAt(obj.Type(), unsafe.Pointer(obj.UnsafeAddr())).Interface()
26 | objPt = Fill(objPt)
27 | obj.Set(reflect.ValueOf(objPt))
28 | }
29 | case reflect.Struct:
30 | for i := 0; i < v.NumField(); i++ {
31 | f := reflect.Indirect(v.Field(i))
32 | if !f.CanSet() {
33 | continue
34 | }
35 | switch f.Kind() {
36 | case reflect.Slice:
37 | f.Set(reflect.MakeSlice(f.Type(), 0, 0))
38 | case reflect.Struct:
39 | switch f.Type() {
40 | case coinType:
41 | coin := reflect.New(coinType).Interface()
42 | s := reflect.ValueOf(coin).Elem()
43 | f.Set(s)
44 | case coinsType:
45 | coins := reflect.New(coinsType).Interface()
46 | s := reflect.ValueOf(coins).Elem()
47 | f.Set(s)
48 | default:
49 | objPt := reflect.NewAt(f.Type(), unsafe.Pointer(f.UnsafeAddr())).Interface()
50 | s := Fill(objPt)
51 | f.Set(reflect.ValueOf(s))
52 | }
53 | }
54 | }
55 | }
56 | return reflect.Indirect(v).Interface()
57 | }
58 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_transfer_vault.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "bytes"
5 |
6 | "github.com/btcsuite/btcd/btcutil/psbt"
7 |
8 | errorsmod "cosmossdk.io/errors"
9 | sdk "github.com/cosmos/cosmos-sdk/types"
10 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
11 | )
12 |
13 | var _ sdk.Msg = &MsgTransferVault{}
14 |
15 | // ValidateBasic performs basic MsgTransferVault message validation.
16 | func (m *MsgTransferVault) ValidateBasic() error {
17 | if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil {
18 | return errorsmod.Wrap(err, "invalid authority address")
19 | }
20 |
21 | if m.SourceVersion == m.DestVersion {
22 | return ErrInvalidVaultVersion
23 | }
24 |
25 | if m.AssetType == AssetType_ASSET_TYPE_UNSPECIFIED {
26 | return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "invalid asset type")
27 | }
28 |
29 | for _, p := range m.Psbts {
30 | packet, err := psbt.NewFromRawBytes(bytes.NewReader([]byte(p)), true)
31 | if err != nil {
32 | return err
33 | }
34 |
35 | if err := CheckTransactionWeight(packet.UnsignedTx, nil); err != nil {
36 | return err
37 | }
38 |
39 | for i, ti := range packet.UnsignedTx.TxIn {
40 | if ti.Sequence != MagicSequence {
41 | return ErrInvalidPsbt
42 | }
43 |
44 | if packet.Inputs[i].SighashType != DefaultSigHashType {
45 | return ErrInvalidPsbt
46 | }
47 | }
48 |
49 | for _, out := range packet.UnsignedTx.TxOut {
50 | if IsDustOut(out) {
51 | return ErrDustOutput
52 | }
53 | }
54 | }
55 |
56 | if len(m.Psbts) == 0 {
57 | if m.TargetUtxoNum == 0 {
58 | return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "target number of utxos must be greater than 0")
59 | }
60 | }
61 |
62 | return nil
63 | }
64 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_initiate_dkg.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "encoding/base64"
5 | "slices"
6 |
7 | errorsmod "cosmossdk.io/errors"
8 | "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519"
9 | sdk "github.com/cosmos/cosmos-sdk/types"
10 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
11 | )
12 |
13 | var _ sdk.Msg = &MsgInitiateDKG{}
14 |
15 | // ValidateBasic performs basic MsgInitiateDKG message validation.
16 | func (m *MsgInitiateDKG) ValidateBasic() error {
17 | if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil {
18 | return errorsmod.Wrap(err, "invalid authority address")
19 | }
20 |
21 | if len(m.Participants) == 0 || m.Threshold == 0 || m.Threshold > uint32(len(m.Participants)) {
22 | return ErrInvalidDKGParams
23 | }
24 |
25 | participants := make(map[string]bool)
26 |
27 | for _, p := range m.Participants {
28 | if len(p.Moniker) > stakingtypes.MaxMonikerLength {
29 | return ErrInvalidDKGParams
30 | }
31 |
32 | if _, err := sdk.ValAddressFromBech32(p.OperatorAddress); err != nil {
33 | return errorsmod.Wrap(err, "invalid operator address")
34 | }
35 |
36 | if pubKey, err := base64.StdEncoding.DecodeString(p.ConsensusPubkey); err != nil || len(pubKey) != ed25519.PubKeySize {
37 | return errorsmod.Wrap(err, "invalid consensus public key")
38 | }
39 |
40 | if participants[p.ConsensusPubkey] {
41 | return errorsmod.Wrap(ErrInvalidDKGParams, "duplicate participant")
42 | }
43 |
44 | participants[p.ConsensusPubkey] = true
45 | }
46 |
47 | if !slices.Equal(m.VaultTypes, SupportedAssetTypes()) {
48 | return errorsmod.Wrap(ErrInvalidDKGParams, "incorrect vault types")
49 | }
50 |
51 | if m.EnableTransfer {
52 | if m.TargetUtxoNum == 0 {
53 | return errorsmod.Wrap(ErrInvalidDKGParams, "target number of utxos must be greater than 0")
54 | }
55 | }
56 |
57 | return nil
58 | }
59 |
--------------------------------------------------------------------------------
/x/btcbridge/module/genesis.go:
--------------------------------------------------------------------------------
1 | package btcbridge
2 |
3 | import (
4 | "sort"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 |
8 | "github.com/sideprotocol/side/x/btcbridge/keeper"
9 | "github.com/sideprotocol/side/x/btcbridge/types"
10 | )
11 |
12 | // InitGenesis initializes the module's state from a provided genesis state.
13 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) {
14 | // this line is used by starport scaffolding # genesis/module/init
15 | k.SetParams(ctx, genState.Params)
16 |
17 | // set the best block header
18 | k.SetBestBlockHeader(ctx, genState.BestBlockHeader)
19 | k.SetBlockHeader(ctx, genState.BestBlockHeader)
20 |
21 | // set block headers
22 | for _, header := range genState.BlockHeaders {
23 | k.SetBlockHeader(ctx, header)
24 | }
25 |
26 | // set utxos
27 | for _, utxo := range genState.Utxos {
28 | k.SaveUTXO(ctx, utxo)
29 | }
30 |
31 | // set dkg request
32 | if genState.DkgRequest != nil {
33 | k.SetDKGRequest(ctx, genState.DkgRequest)
34 | k.SetDKGRequestID(ctx, genState.DkgRequest.Id)
35 | }
36 |
37 | // sort vaults and set the latest vault version
38 | if len(genState.Params.Vaults) > 0 {
39 | vaults := genState.Params.Vaults
40 | sort.Slice(vaults, func(i, j int) bool { return vaults[i].Version < vaults[j].Version })
41 |
42 | k.SetVaultVersion(ctx, vaults[len(vaults)-1].Version)
43 | }
44 | }
45 |
46 | // ExportGenesis returns the module's exported genesis
47 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {
48 | genesis := types.DefaultGenesis()
49 | genesis.Params = k.GetParams(ctx)
50 | genesis.BestBlockHeader = k.GetBestBlockHeader(ctx)
51 | genesis.BlockHeaders = k.GetAllBlockHeaders(ctx)
52 | genesis.Utxos = k.GetAllUTXOs(ctx)
53 |
54 | // this line is used by starport scaffolding # genesis/module/export
55 |
56 | return genesis
57 | }
58 |
--------------------------------------------------------------------------------
/x/btcbridge/types/msg_consolidate_vaults.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "lukechampine.com/uint128"
5 |
6 | errorsmod "cosmossdk.io/errors"
7 | sdk "github.com/cosmos/cosmos-sdk/types"
8 | )
9 |
10 | var _ sdk.Msg = &MsgConsolidateVaults{}
11 |
12 | // ValidateBasic performs basic MsgConsolidateVaults message validation.
13 | func (m *MsgConsolidateVaults) ValidateBasic() error {
14 | if _, err := sdk.AccAddressFromBech32(m.Authority); err != nil {
15 | return errorsmod.Wrap(err, "invalid authority address")
16 | }
17 |
18 | if m.BtcConsolidation == nil && len(m.RunesConsolidations) == 0 {
19 | return errorsmod.Wrap(ErrInvalidConsolidation, "neither btc nor runes consolidation provided")
20 | }
21 |
22 | if m.BtcConsolidation != nil {
23 | if err := ensureBtcConsolidation(m.BtcConsolidation); err != nil {
24 | return err
25 | }
26 | }
27 |
28 | if len(m.RunesConsolidations) != 0 {
29 | if err := ensureRunesConsolidations(m.RunesConsolidations); err != nil {
30 | return err
31 | }
32 | }
33 |
34 | return nil
35 | }
36 |
37 | // ensureBtcConsolidation checks the given btc consolidation
38 | func ensureBtcConsolidation(consolidation *BtcConsolidation) error {
39 | if consolidation.TargetThreshold <= 0 {
40 | return errorsmod.Wrap(ErrInvalidConsolidation, "btc target threshold must be greater than 0")
41 | }
42 |
43 | return nil
44 | }
45 |
46 | // ensureRunesConsolidations checks the given runes consolidations
47 | func ensureRunesConsolidations(consolidations []*RunesConsolidation) error {
48 | for _, c := range consolidations {
49 | var id RuneId
50 | err := id.FromString(c.RuneId)
51 | if err != nil {
52 | return err
53 | }
54 |
55 | threshold, err := uint128.FromString(c.TargetThreshold)
56 | if err != nil || threshold.IsZero() {
57 | return errorsmod.Wrap(ErrInvalidConsolidation, "invalid runes target threshold")
58 | }
59 | }
60 |
61 | return nil
62 | }
63 |
--------------------------------------------------------------------------------
/proto/side/incentive/query.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package side.incentive;
3 |
4 | import "gogoproto/gogo.proto";
5 | import "google/api/annotations.proto";
6 | import "side/incentive/incentive.proto";
7 | import "side/incentive/params.proto";
8 |
9 | option go_package = "github.com/sideprotocol/side/x/incentive/types";
10 |
11 | // Query defines the gRPC querier service.
12 | service Query {
13 | // Params queries the parameters of the module.
14 | rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
15 | option (google.api.http).get = "/side/incentive/params";
16 | }
17 | // Rewards queries the rewards of the given address.
18 | rpc Rewards(QueryRewardsRequest) returns (QueryRewardsResponse) {
19 | option (google.api.http).get = "/side/incentive/rewards";
20 | }
21 | // RewardStats queries total reward statistics.
22 | rpc RewardStats(QueryRewardStatsRequest) returns (QueryRewardStatsResponse) {
23 | option (google.api.http).get = "/side/incentive/rewards/stats";
24 | }
25 | }
26 |
27 | // QueryRewardsRequest is request type for the Query/Rewards RPC method.
28 | message QueryRewardsRequest {
29 | string address = 1;
30 | }
31 |
32 | // QueryRewardsResponse is response type for the Query/Rewards RPC method.
33 | message QueryRewardsResponse {
34 | Rewards rewards = 1;
35 | }
36 |
37 | // QueryRewardStatsRequest is request type for the Query/RewardStats RPC method.
38 | message QueryRewardStatsRequest {
39 | }
40 |
41 | // QueryRewardStatsResponse is response type for the Query/RewardStats RPC method.
42 | message QueryRewardStatsResponse {
43 | RewardStats reward_stats = 1;
44 | }
45 |
46 | // QueryParamsRequest is request type for the Query/Params RPC method.
47 | message QueryParamsRequest {}
48 |
49 | // QueryParamsResponse is response type for the Query/Params RPC method.
50 | message QueryParamsResponse {
51 | // params holds all the parameters of this module.
52 | Params params = 1 [(gogoproto.nullable) = false];
53 | }
54 |
--------------------------------------------------------------------------------
/testutil/keeper/incentive.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 |
8 | "cosmossdk.io/log"
9 | "cosmossdk.io/store"
10 | "cosmossdk.io/store/metrics"
11 | storetypes "cosmossdk.io/store/types"
12 | cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
13 | dbm "github.com/cosmos/cosmos-db"
14 | "github.com/cosmos/cosmos-sdk/codec"
15 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
16 | simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
17 | sdk "github.com/cosmos/cosmos-sdk/types"
18 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
19 | govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
20 |
21 | "github.com/sideprotocol/side/app"
22 | "github.com/sideprotocol/side/x/incentive/keeper"
23 | "github.com/sideprotocol/side/x/incentive/types"
24 | )
25 |
26 | func IncentiveKeeper(t testing.TB) (keeper.Keeper, sdk.Context) {
27 | db := dbm.NewMemDB()
28 |
29 | app := app.New(log.NewNopLogger(), db, nil, true, simtestutil.EmptyAppOptions{})
30 |
31 | storeKey := storetypes.NewKVStoreKey(types.StoreKey)
32 | memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey)
33 |
34 | stateStore := store.NewCommitMultiStore(db, log.NewNopLogger(), metrics.NewNoOpMetrics())
35 | stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db)
36 | stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil)
37 | require.NoError(t, stateStore.LoadLatestVersion())
38 |
39 | registry := codectypes.NewInterfaceRegistry()
40 | cdc := codec.NewProtoCodec(registry)
41 |
42 | authority := authtypes.NewModuleAddress(govtypes.ModuleName).String()
43 |
44 | k := keeper.NewKeeper(
45 | cdc,
46 | storeKey,
47 | memStoreKey,
48 | app.BankKeeper,
49 | authority,
50 | )
51 |
52 | ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger())
53 |
54 | // Initialize params
55 | k.SetParams(ctx, types.DefaultParams())
56 |
57 | return k, ctx
58 | }
59 |
--------------------------------------------------------------------------------
/testutil/keeper/btc_bridge.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/stretchr/testify/require"
7 |
8 | "cosmossdk.io/log"
9 | "cosmossdk.io/store"
10 | "cosmossdk.io/store/metrics"
11 | storetypes "cosmossdk.io/store/types"
12 | cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
13 | dbm "github.com/cosmos/cosmos-db"
14 | "github.com/cosmos/cosmos-sdk/codec"
15 | codectypes "github.com/cosmos/cosmos-sdk/codec/types"
16 | simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
17 | sdk "github.com/cosmos/cosmos-sdk/types"
18 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
19 | govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"
20 |
21 | "github.com/sideprotocol/side/app"
22 | "github.com/sideprotocol/side/x/btcbridge/keeper"
23 | "github.com/sideprotocol/side/x/btcbridge/types"
24 | )
25 |
26 | func BtcBridgeKeeper(t testing.TB) (*keeper.Keeper, sdk.Context) {
27 | db := dbm.NewMemDB()
28 |
29 | app := app.New(log.NewNopLogger(), db, nil, true, simtestutil.EmptyAppOptions{})
30 |
31 | storeKey := storetypes.NewKVStoreKey(types.StoreKey)
32 | memStoreKey := storetypes.NewMemoryStoreKey(types.MemStoreKey)
33 |
34 | stateStore := store.NewCommitMultiStore(db, log.NewNopLogger(), metrics.NewNoOpMetrics())
35 | stateStore.MountStoreWithDB(storeKey, storetypes.StoreTypeIAVL, db)
36 | stateStore.MountStoreWithDB(memStoreKey, storetypes.StoreTypeMemory, nil)
37 | require.NoError(t, stateStore.LoadLatestVersion())
38 |
39 | registry := codectypes.NewInterfaceRegistry()
40 | cdc := codec.NewProtoCodec(registry)
41 |
42 | authority := authtypes.NewModuleAddress(govtypes.ModuleName).String()
43 |
44 | k := keeper.NewKeeper(
45 | cdc,
46 | storeKey,
47 | memStoreKey,
48 | app.BankKeeper,
49 | app.StakingKeeper,
50 | app.IncentiveKeeper,
51 | authority,
52 | )
53 |
54 | ctx := sdk.NewContext(stateStore, cmtproto.Header{}, false, log.NewNopLogger())
55 |
56 | // Initialize params
57 | k.SetParams(ctx, types.DefaultParams())
58 |
59 | return k, ctx
60 | }
61 |
--------------------------------------------------------------------------------
/x/incentive/keeper/params.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
6 |
7 | "github.com/sideprotocol/side/x/incentive/types"
8 | )
9 |
10 | // IncentiveEnabled returns true if the incentive mechanism is enabled, false otherwise
11 | func (k Keeper) IncentiveEnabled(ctx sdk.Context) bool {
12 | return k.GetParams(ctx).Enabled && !k.bankKeeper.SpendableCoins(ctx, authtypes.NewModuleAddress(types.ModuleName)).IsZero()
13 | }
14 |
15 | // DepositIncentiveEnabled returns true if the incentive is enabled for deposit, false otherwise
16 | func (k Keeper) DepositIncentiveEnabled(ctx sdk.Context) bool {
17 | return k.IncentiveEnabled(ctx) && k.RewardPerDeposit(ctx).IsPositive() && k.bankKeeper.GetBalance(ctx, authtypes.NewModuleAddress(types.ModuleName), k.DepositRewardDenom(ctx)).IsPositive()
18 | }
19 |
20 | // WithdrawIncentiveEnabled returns true if the incentive is enabled for withdrawal, false otherwise
21 | func (k Keeper) WithdrawIncentiveEnabled(ctx sdk.Context) bool {
22 | return k.IncentiveEnabled(ctx) && k.RewardPerWithdraw(ctx).IsPositive() && k.bankKeeper.GetBalance(ctx, authtypes.NewModuleAddress(types.ModuleName), k.WithdrawRewardDenom(ctx)).IsPositive()
23 | }
24 |
25 | // RewardPerDeposit returns the reward amount for each deposit
26 | func (k Keeper) RewardPerDeposit(ctx sdk.Context) sdk.Coin {
27 | return k.GetParams(ctx).RewardPerDeposit
28 | }
29 |
30 | // RewardPerWithdraw returns the reward amount for each withdrawal
31 | func (k Keeper) RewardPerWithdraw(ctx sdk.Context) sdk.Coin {
32 | return k.GetParams(ctx).RewardPerWithdraw
33 | }
34 |
35 | // DepositRewardDenom returns the denom for deposit reward
36 | func (k Keeper) DepositRewardDenom(ctx sdk.Context) string {
37 | return k.GetParams(ctx).RewardPerDeposit.Denom
38 | }
39 |
40 | // WithdrawRewardDenom returns the denom for withdrawal reward
41 | func (k Keeper) WithdrawRewardDenom(ctx sdk.Context) string {
42 | return k.GetParams(ctx).RewardPerWithdraw.Denom
43 | }
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # sidechain
2 | **sidechain** is a blockchain built using Cosmos SDK and Tendermint and created with [Ignite CLI](https://ignite.com/cli).
3 |
4 | ## Get started
5 |
6 | ```
7 | ignite chain serve
8 | ```
9 | `serve` command installs dependencies, builds, initializes, and starts your blockchain in development.
10 |
11 | ### Requirements
12 |
13 | - `ignite version: 0.27.1`
14 | - `go version >= 1.20.x`
15 |
16 | ### Configure
17 |
18 | Your blockchain in development can be configured with `config.yml`. To learn more, see the [Ignite CLI docs](https://docs.ignite.com).
19 |
20 | ### Web Frontend
21 |
22 | Ignite CLI has scaffolded a Vue.js-based web app in the `vue` directory. Run the following commands to install dependencies and start the app:
23 |
24 | ```
25 | cd vue
26 | npm install
27 | npm run serve
28 | ```
29 |
30 | The frontend app is built using the `@starport/vue` and `@starport/vuex` packages. For details, see the [monorepo for Ignite front-end development](https://github.com/ignite/web).
31 |
32 | ## Release
33 | To release a new version of your blockchain, create and push a new tag with `v` prefix. A new draft release with the configured targets will be created.
34 |
35 | ```
36 | git tag v0.1
37 | git push origin v0.1
38 | ```
39 |
40 | After a draft release is created, make your final changes from the release page and publish it.
41 |
42 | ### Install
43 | To install the latest version of your blockchain node's binary, execute the following command on your machine:
44 |
45 | ```
46 | curl https://get.ignite.com/username/sidechain@latest! | sudo bash
47 | ```
48 | `username/sidechain` should match the `username` and `repo_name` of the Github repository to which the source code was pushed. Learn more about [the install process](https://github.com/allinbits/starport-installer).
49 |
50 | ## Learn more
51 |
52 | - [Ignite CLI](https://ignite.com/cli)
53 | - [Tutorials](https://docs.ignite.com/guide)
54 | - [Ignite CLI docs](https://docs.ignite.com)
55 | - [Cosmos SDK docs](https://docs.cosmos.network)
56 | - [Developer Chat](https://discord.gg/ignite)
57 |
--------------------------------------------------------------------------------
/x/btcbridge/types/runes_test.go:
--------------------------------------------------------------------------------
1 | package types_test
2 |
3 | import (
4 | "encoding/hex"
5 | "testing"
6 |
7 | "github.com/stretchr/testify/require"
8 |
9 | "github.com/btcsuite/btcd/wire"
10 |
11 | "github.com/sideprotocol/side/x/btcbridge/types"
12 | )
13 |
14 | func TestParseRunes(t *testing.T) {
15 | testCases := []struct {
16 | name string
17 | pkScriptHex string
18 | edicts []*types.Edict
19 | expectPass bool
20 | }{
21 | {
22 | name: "valid runes edict",
23 | pkScriptHex: "6a5d0b00c0a2330380cab5ee0101",
24 | edicts: []*types.Edict{
25 | {
26 | Id: &types.RuneId{Block: 840000, Tx: 3},
27 | Amount: "500000000",
28 | Output: 1,
29 | },
30 | },
31 | expectPass: true,
32 | },
33 | {
34 | name: "output index is out of range",
35 | pkScriptHex: "6a5d0b00c0a2330380cab5ee0102",
36 | expectPass: false,
37 | },
38 | {
39 | name: "no OP_RETURN",
40 | pkScriptHex: "615d0b00c0a2330380cab5ee0102",
41 | expectPass: true,
42 | edicts: nil,
43 | },
44 | {
45 | name: "no runes magic number",
46 | pkScriptHex: "6a5c0b00c0a2330380cab5ee0102",
47 | expectPass: true,
48 | edicts: nil,
49 | },
50 | {
51 | name: "non data push op",
52 | pkScriptHex: "6a5d4f00c0a2330380cab5ee0102",
53 | expectPass: false,
54 | },
55 | {
56 | name: "no tag body for edicts",
57 | pkScriptHex: "6a5d0b01c0a2330380cab5ee0102",
58 | expectPass: false,
59 | },
60 | {
61 | name: "invalid edict",
62 | pkScriptHex: "6a5d0b00c0a2330380cab5ee01",
63 | expectPass: false,
64 | },
65 | }
66 |
67 | for _, tc := range testCases {
68 | t.Run(tc.name, func(t *testing.T) {
69 | pkScript, err := hex.DecodeString(tc.pkScriptHex)
70 | require.NoError(t, err)
71 |
72 | tx := wire.NewMsgTx(types.TxVersion)
73 | tx.AddTxOut(wire.NewTxOut(0, pkScript))
74 |
75 | edicts, err := types.ParseRunes(tx)
76 | if tc.expectPass {
77 | require.NoError(t, err)
78 | require.EqualValues(t, tc.edicts, edicts)
79 | } else {
80 | require.NotNil(t, err)
81 | }
82 | })
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/x/btcbridge/types/varint.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "errors"
5 | "math/big"
6 |
7 | "lukechampine.com/uint128"
8 | )
9 |
10 | func EncodeUint32(n uint32) []byte {
11 | var result []byte
12 |
13 | for n >= 128 {
14 | result = append(result, byte(n&0x7F|0x80))
15 | n >>= 7
16 | }
17 |
18 | result = append(result, byte(n))
19 | return result
20 | }
21 |
22 | func EncodeUint64(n uint64) []byte {
23 | var result []byte
24 |
25 | for n >= 128 {
26 | result = append(result, byte(n&0x7F|0x80))
27 | n >>= 7
28 | }
29 |
30 | result = append(result, byte(n))
31 | return result
32 | }
33 |
34 | func EncodeUint128(n *uint128.Uint128) []byte {
35 | return EncodeBigInt(n.Big())
36 | }
37 |
38 | func EncodeBigInt(n *big.Int) []byte {
39 | var result []byte
40 |
41 | for n.Cmp(big.NewInt(128)) >= 0 {
42 | temp := new(big.Int).Set(n)
43 | last := temp.And(n, new(big.Int).SetUint64(0b0111_1111))
44 | result = append(result, last.Or(last, new(big.Int).SetUint64(0b1000_0000)).Bytes()[0])
45 | n.Rsh(n, 7)
46 | }
47 |
48 | if len(n.Bytes()) == 0 {
49 | result = append(result, 0)
50 | } else {
51 | result = append(result, n.Bytes()...)
52 | }
53 |
54 | return result
55 | }
56 |
57 | func Decode(bz []byte) (uint128.Uint128, int, error) {
58 | n := big.NewInt(0)
59 |
60 | for i, b := range bz {
61 | if i > 18 {
62 | return uint128.Zero, 0, errors.New("varint overflow")
63 | }
64 |
65 | value := uint64(b) & 0b0111_1111
66 | if i == 18 && value&0b0111_1100 != 0 {
67 | return uint128.Zero, 0, errors.New("varint too large")
68 | }
69 |
70 | temp := new(big.Int).SetUint64(value)
71 | n.Or(n, temp.Lsh(temp, uint(7*i)))
72 |
73 | if b&0b1000_0000 == 0 {
74 | return uint128.FromBig(n), i + 1, nil
75 | }
76 | }
77 |
78 | return uint128.Zero, 0, errors.New("varint too short")
79 | }
80 |
81 | func DecodeVec(payload []byte) ([]uint128.Uint128, error) {
82 | vec := make([]uint128.Uint128, 0)
83 | i := 0
84 |
85 | for i < len(payload) {
86 | value, length, err := Decode(payload[i:])
87 | if err != nil {
88 | return nil, err
89 | }
90 |
91 | vec = append(vec, value)
92 | i += length
93 | }
94 |
95 | return vec, nil
96 | }
97 |
--------------------------------------------------------------------------------
/x/btcbridge/types/tss.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "crypto/ed25519"
5 | "encoding/base64"
6 | "encoding/binary"
7 | "encoding/hex"
8 | "reflect"
9 | "strings"
10 |
11 | "github.com/cometbft/cometbft/crypto"
12 | "github.com/cometbft/cometbft/crypto/tmhash"
13 | )
14 |
15 | // MustGetConsensusAddr gets the hex-encoded consensus address from the given consensus public key
16 | // Panic if any error occurs
17 | func MustGetConsensusAddr(consPubKey string) string {
18 | pubKey, err := base64.StdEncoding.DecodeString(consPubKey)
19 | if err != nil {
20 | panic(err)
21 | }
22 |
23 | return hex.EncodeToString(tmhash.SumTruncated(pubKey))
24 | }
25 |
26 | // ParticipantExists returns true if the given address is a participant, false otherwise
27 | func ParticipantExists(participants []*DKGParticipant, consAddress string) bool {
28 | for _, p := range participants {
29 | if MustGetConsensusAddr(p.ConsensusPubkey) == strings.ToLower(consAddress) {
30 | return true
31 | }
32 | }
33 |
34 | return false
35 | }
36 |
37 | // CheckDKGCompletionRequests checks if the vaults of all the DKG completion requests are same
38 | func CheckDKGCompletionRequests(requests []*DKGCompletionRequest) bool {
39 | if len(requests) == 0 {
40 | return false
41 | }
42 |
43 | vaults := requests[0].Vaults
44 |
45 | for _, req := range requests[1:] {
46 | if !reflect.DeepEqual(req.Vaults, vaults) {
47 | return false
48 | }
49 | }
50 |
51 | return true
52 | }
53 |
54 | // VerifySignature verifies the given signature against the given DKG completion request
55 | func VerifySignature(signature string, pubKey []byte, req *DKGCompletionRequest) bool {
56 | sig, err := hex.DecodeString(signature)
57 | if err != nil {
58 | return false
59 | }
60 |
61 | sigMsg := GetSigMsgFromDKGCompletionReq(req)
62 |
63 | return ed25519.Verify(pubKey, sigMsg, sig)
64 | }
65 |
66 | // GetSigMsgFromDKGCompletionReq gets the msg to be signed from the given DKG completion request
67 | func GetSigMsgFromDKGCompletionReq(req *DKGCompletionRequest) []byte {
68 | rawMsg := make([]byte, 8)
69 | binary.BigEndian.PutUint64(rawMsg, req.Id)
70 |
71 | for _, v := range req.Vaults {
72 | rawMsg = append(rawMsg, []byte(v)...)
73 | }
74 |
75 | return crypto.Sha256(rawMsg)
76 | }
77 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # This workflow is useful if you want to automate the process of:
2 | #
3 | # a) Creating a new prelease when you push a new tag with a "v" prefix (version).
4 | #
5 | # This type of prerelease is meant to be used for production: alpha, beta, rc, etc. types of releases.
6 | # After the prerelease is created, you need to make your changes on the release page at the relevant
7 | # Github page and publish your release.
8 | #
9 | # b) Creating/updating the "latest" prerelease when you push to your default branch.
10 | #
11 | # This type of prelease is useful to make your bleeding-edge binaries available to advanced users.
12 | #
13 | # The workflow will not run if there is no tag pushed with a "v" prefix and no change pushed to your
14 | # default branch.
15 | on: push
16 |
17 | jobs:
18 | might_release:
19 | runs-on: ubuntu-latest
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v2
23 | with:
24 | fetch-depth: 0
25 |
26 | - name: Prepare Release Variables
27 | id: vars
28 | uses: ignite/cli/actions/release/vars@main
29 |
30 | - name: Issue Release Assets
31 | uses: ignite/cli/actions/cli@main
32 | if: ${{ steps.vars.outputs.should_release == 'true' }}
33 | with:
34 | args: chain build --release --release.prefix ${{ steps.vars.outputs.tarball_prefix }} -t linux:amd64 -t darwin:amd64 -t darwin:arm64
35 | env:
36 | DO_NOT_TRACK: 1
37 | GOFLAGS: "-buildvcs=false"
38 | continue-on-error: true
39 |
40 | - name: Delete the "latest" Release
41 | uses: dev-drprasad/delete-tag-and-release@v0.2.1
42 | if: ${{ steps.vars.outputs.is_release_type_latest == 'true' }}
43 | with:
44 | tag_name: ${{ steps.vars.outputs.tag_name }}
45 | delete_release: true
46 | env:
47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
48 |
49 | - name: Publish the Release
50 | uses: softprops/action-gh-release@v1
51 | if: ${{ steps.vars.outputs.should_release == 'true' }}
52 | with:
53 | tag_name: ${{ steps.vars.outputs.tag_name }}
54 | files: release/*
55 | prerelease: true
56 | env:
57 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
58 |
--------------------------------------------------------------------------------
/x/btcbridge/types/expected_keepers.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "context"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | "github.com/cosmos/cosmos-sdk/x/auth/types"
8 | banktype "github.com/cosmos/cosmos-sdk/x/bank/types"
9 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
10 | )
11 |
12 | // AccountKeeper defines the expected account keeper used for simulations (noalias)
13 | type AccountKeeper interface {
14 | GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI
15 | // Methods imported from account should be defined here
16 | }
17 |
18 | // BankKeeper defines the expected interface needed to retrieve account balances.
19 | type BankKeeper interface {
20 | SpendableCoins(ctx context.Context, addr sdk.AccAddress) sdk.Coins
21 | // Methods imported from bank should be defined here
22 |
23 | SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error
24 |
25 | SendCoinsFromModuleToModule(ctx context.Context, senderModule, recipientModule string, amt sdk.Coins) error
26 | SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error
27 | SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error
28 | SetDenomMetaData(ctx context.Context, denomMetaData banktype.Metadata)
29 |
30 | MintCoins(ctx context.Context, moduleName string, amounts sdk.Coins) error
31 | BurnCoins(ctx context.Context, moduleName string, amounts sdk.Coins) error
32 |
33 | HasSupply(ctx context.Context, denom string) bool
34 | GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin
35 | }
36 |
37 | // StakingKeeper defines the expected staking keeper used to retrieve validator (noalias)
38 | type StakingKeeper interface {
39 | GetValidator(ctx context.Context, addr sdk.ValAddress) (stakingtypes.Validator, error)
40 | GetValidatorByConsAddr(ctx context.Context, consAddr sdk.ConsAddress) (stakingtypes.Validator, error)
41 | }
42 |
43 | // IncentiveKeeper defines the expected incentive keeper
44 | type IncentiveKeeper interface {
45 | DepositIncentiveEnabled(ctx sdk.Context) bool
46 | WithdrawIncentiveEnabled(ctx sdk.Context) bool
47 |
48 | DistributeDepositReward(ctx sdk.Context, addr string) error
49 | DistributeWithdrawReward(ctx sdk.Context, addr string) error
50 | }
51 |
--------------------------------------------------------------------------------
/x/btcbridge/types/codec.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "github.com/cosmos/cosmos-sdk/codec"
5 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 | "github.com/cosmos/cosmos-sdk/types/msgservice"
8 | )
9 |
10 | func RegisterCodec(cdc *codec.LegacyAmino) {
11 | cdc.RegisterConcrete(&MsgSubmitBlockHeaders{}, "btcbridge/MsgSubmitBlockHeaders", nil)
12 | cdc.RegisterConcrete(&MsgSubmitDepositTransaction{}, "btcbridge/MsgSubmitDepositTransaction", nil)
13 | cdc.RegisterConcrete(&MsgSubmitWithdrawTransaction{}, "btcbridge/MsgSubmitWithdrawTransaction", nil)
14 | cdc.RegisterConcrete(&MsgSubmitFeeRate{}, "btcbridge/MsgSubmitFeeRate", nil)
15 | cdc.RegisterConcrete(&MsgUpdateTrustedNonBtcRelayers{}, "btcbridge/MsgUpdateTrustedNonBtcRelayers", nil)
16 | cdc.RegisterConcrete(&MsgUpdateTrustedFeeProviders{}, "btcbridge/MsgUpdateTrustedFeeProviders", nil)
17 | cdc.RegisterConcrete(&MsgWithdrawToBitcoin{}, "btcbridge/MsgWithdrawToBitcoin", nil)
18 | cdc.RegisterConcrete(&MsgSubmitSignatures{}, "btcbridge/MsgSubmitSignatures", nil)
19 | cdc.RegisterConcrete(&MsgCompleteDKG{}, "btcbridge/MsgCompleteDKG", nil)
20 | cdc.RegisterConcrete(&MsgUpdateParams{}, "btcbridge/MsgUpdateParams", nil)
21 | // this line is used by starport scaffolding # 2
22 | }
23 |
24 | func RegisterInterfaces(registry cdctypes.InterfaceRegistry) {
25 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSubmitBlockHeaders{})
26 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSubmitDepositTransaction{})
27 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSubmitWithdrawTransaction{})
28 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSubmitFeeRate{})
29 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgUpdateTrustedNonBtcRelayers{})
30 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgUpdateTrustedFeeProviders{})
31 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgWithdrawToBitcoin{})
32 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgSubmitSignatures{})
33 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgCompleteDKG{})
34 | registry.RegisterImplementations((*sdk.Msg)(nil), &MsgUpdateParams{})
35 | // this line is used by starport scaffolding # 3
36 |
37 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
38 | }
39 |
40 | var (
41 | Amino = codec.NewLegacyAmino()
42 | ModuleCdc = codec.NewProtoCodec(cdctypes.NewInterfaceRegistry())
43 | )
44 |
--------------------------------------------------------------------------------
/x/btcbridge/codec/bech32_codec.go:
--------------------------------------------------------------------------------
1 | package codec
2 |
3 | import (
4 | "errors"
5 | "strings"
6 |
7 | "cosmossdk.io/core/address"
8 | errorsmod "cosmossdk.io/errors"
9 |
10 | sdk "github.com/cosmos/cosmos-sdk/types"
11 | "github.com/cosmos/cosmos-sdk/types/bech32"
12 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
13 | )
14 |
15 | type bech32Codec struct {
16 | nativeBech32Prefix string
17 | bitcoinBech32Prefix string
18 | }
19 |
20 | var _ address.Codec = &bech32Codec{}
21 |
22 | // NewBech32Codec creates a new address codec
23 | func NewBech32Codec(nativeBech32Prefix string, bitcoinBech32Prefix string) address.Codec {
24 | return bech32Codec{nativeBech32Prefix, bitcoinBech32Prefix}
25 | }
26 |
27 | // StringToBytes encodes text to bytes
28 | func (bc bech32Codec) StringToBytes(text string) ([]byte, error) {
29 | if len(strings.TrimSpace(text)) == 0 {
30 | return []byte{}, errors.New("empty address string is not allowed")
31 | }
32 |
33 | hrp, bz, err := bech32.DecodeAndConvert(text)
34 | if err != nil {
35 | return nil, err
36 | }
37 |
38 | if hrp != bc.nativeBech32Prefix && hrp != bc.bitcoinBech32Prefix {
39 | return nil, errorsmod.Wrapf(sdkerrors.ErrLogic, "hrp does not match bech32 prefixes: expected '%s or %s' got '%s'", bc.nativeBech32Prefix, bc.bitcoinBech32Prefix, hrp)
40 | }
41 |
42 | if err := sdk.VerifyAddressFormat(bz); err != nil {
43 | return nil, err
44 | }
45 |
46 | return bz, nil
47 | }
48 |
49 | // BytesToString decodes bytes to text
50 | func (bc bech32Codec) BytesToString(bz []byte) (string, error) {
51 | bech32Prefix := bc.nativeBech32Prefix
52 | if isBitcoinAddress(bz) {
53 | bech32Prefix = bc.bitcoinBech32Prefix
54 | }
55 |
56 | text, err := bech32.ConvertAndEncode(bech32Prefix, bz)
57 | if err != nil {
58 | return "", err
59 | }
60 |
61 | return text, nil
62 | }
63 |
64 | // isBitcoinAddress returns true if the given address is segwit or taproot, false otherwise
65 | func isBitcoinAddress(address []byte) bool {
66 | return isSegwitAddress(address) || isTaprootAddress(address)
67 | }
68 |
69 | // isSegwitAddress returns true if the given address is segwit, false otherwise
70 | func isSegwitAddress(address []byte) bool {
71 | return len(address) == 33 // bech32 decoded address length
72 | }
73 |
74 | // isTaprootAddress returns true if the given address is taproot, false otherwise
75 | func isTaprootAddress(address []byte) bool {
76 | return len(address) == 32 // only with taproot output key
77 | }
78 |
--------------------------------------------------------------------------------
/cmd/sided/cmd/config.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | cmtcfg "github.com/cometbft/cometbft/config"
5 | serverconfig "github.com/cosmos/cosmos-sdk/server/config"
6 | )
7 |
8 | // initCometBFTConfig helps to override default CometBFT Config values.
9 | // return cmtcfg.DefaultConfig if no custom configuration is required for the application.
10 | func initCometBFTConfig() *cmtcfg.Config {
11 | cfg := cmtcfg.DefaultConfig()
12 |
13 | // these values put a higher strain on node memory
14 | // cfg.P2P.MaxNumInboundPeers = 100
15 | // cfg.P2P.MaxNumOutboundPeers = 40
16 |
17 | return cfg
18 | }
19 |
20 | // initAppConfig helps to override default appConfig template and configs.
21 | // return "", nil if no custom configuration is required for the application.
22 | func initAppConfig() (string, interface{}) {
23 | // The following code snippet is just for reference.
24 | type CustomAppConfig struct {
25 | serverconfig.Config `mapstructure:",squash"`
26 | }
27 |
28 | // Optionally allow the chain developer to overwrite the SDK's default
29 | // server config.
30 | srvCfg := serverconfig.DefaultConfig()
31 | // The SDK's default minimum gas price is set to "" (empty value) inside
32 | // app.toml. If left empty by validators, the node will halt on startup.
33 | // However, the chain developer can set a default app.toml value for their
34 | // validators here.
35 | //
36 | // In summary:
37 | // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their
38 | // own app.toml config,
39 | // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their
40 | // own app.toml to override, or use this default value.
41 | //
42 | // In tests, we set the min gas prices to 0.
43 | // srvCfg.MinGasPrices = "0stake"
44 | // srvCfg.BaseConfig.IAVLDisableFastNode = true // disable fastnode by default
45 |
46 | customAppConfig := CustomAppConfig{
47 | Config: *srvCfg,
48 | }
49 |
50 | customAppTemplate := serverconfig.DefaultConfigTemplate
51 | // Edit the default template file
52 | //
53 | // customAppTemplate := serverconfig.DefaultConfigTemplate + `
54 | // [wasm]
55 | // # This is the maximum sdk gas (wasm and storage) that we allow for any x/wasm "smart" queries
56 | // query_gas_limit = 300000
57 | // # This is the number of wasm vm instances we keep cached in memory for speed-up
58 | // # Warning: this is currently unstable and may lead to crashes, best to keep for 0 unless testing locally
59 | // lru_size = 0`
60 |
61 | return customAppTemplate, customAppConfig
62 | }
63 |
--------------------------------------------------------------------------------
/x/incentive/client/cli/query.go:
--------------------------------------------------------------------------------
1 | package cli
2 |
3 | import (
4 | "fmt"
5 |
6 | // "strings"
7 |
8 | "github.com/spf13/cobra"
9 |
10 | "github.com/cosmos/cosmos-sdk/client"
11 | "github.com/cosmos/cosmos-sdk/client/flags"
12 |
13 | "github.com/sideprotocol/side/x/incentive/types"
14 | )
15 |
16 | // GetQueryCmd returns the cli query commands for this module
17 | func GetQueryCmd(_ string) *cobra.Command {
18 | // Group yield queries under a subcommand
19 | cmd := &cobra.Command{
20 | Use: types.ModuleName,
21 | Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName),
22 | DisableFlagParsing: true,
23 | SuggestionsMinimumDistance: 2,
24 | RunE: client.ValidateCmd,
25 | }
26 |
27 | cmd.AddCommand(CmdQueryParams())
28 | cmd.AddCommand(CmdQueryRewards())
29 | cmd.AddCommand(CmdQueryRewardStats())
30 | // this line is used by starport scaffolding # 1
31 |
32 | return cmd
33 | }
34 |
35 | func CmdQueryParams() *cobra.Command {
36 | cmd := &cobra.Command{
37 | Use: "params",
38 | Short: "Query the parameters of the module",
39 | Args: cobra.NoArgs,
40 | RunE: func(cmd *cobra.Command, args []string) error {
41 | clientCtx, err := client.GetClientQueryContext(cmd)
42 | if err != nil {
43 | return err
44 | }
45 |
46 | queryClient := types.NewQueryClient(clientCtx)
47 |
48 | res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{})
49 | if err != nil {
50 | return err
51 | }
52 |
53 | return clientCtx.PrintProto(res)
54 | },
55 | }
56 |
57 | flags.AddQueryFlagsToCmd(cmd)
58 |
59 | return cmd
60 | }
61 |
62 | func CmdQueryRewards() *cobra.Command {
63 | cmd := &cobra.Command{
64 | Use: "rewards",
65 | Short: "Query the rewards of the given address",
66 | Args: cobra.ExactArgs(1),
67 | RunE: func(cmd *cobra.Command, args []string) error {
68 | clientCtx, err := client.GetClientQueryContext(cmd)
69 | if err != nil {
70 | return err
71 | }
72 |
73 | queryClient := types.NewQueryClient(clientCtx)
74 |
75 | res, err := queryClient.Rewards(cmd.Context(), &types.QueryRewardsRequest{
76 | Address: args[0],
77 | })
78 | if err != nil {
79 | return err
80 | }
81 |
82 | return clientCtx.PrintProto(res)
83 | },
84 | }
85 |
86 | flags.AddQueryFlagsToCmd(cmd)
87 |
88 | return cmd
89 | }
90 |
91 | func CmdQueryRewardStats() *cobra.Command {
92 | cmd := &cobra.Command{
93 | Use: "reward-stats",
94 | Short: "Query total reward statistics",
95 | Args: cobra.NoArgs,
96 | RunE: func(cmd *cobra.Command, args []string) error {
97 | clientCtx, err := client.GetClientQueryContext(cmd)
98 | if err != nil {
99 | return err
100 | }
101 |
102 | queryClient := types.NewQueryClient(clientCtx)
103 |
104 | res, err := queryClient.RewardStats(cmd.Context(), &types.QueryRewardStatsRequest{})
105 | if err != nil {
106 | return err
107 | }
108 |
109 | return clientCtx.PrintProto(res)
110 | },
111 | }
112 |
113 | flags.AddQueryFlagsToCmd(cmd)
114 |
115 | return cmd
116 | }
117 |
--------------------------------------------------------------------------------
/x/btcbridge/types/block_header.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "math/big"
5 | time "time"
6 |
7 | "github.com/btcsuite/btcd/blockchain"
8 | "github.com/btcsuite/btcd/chaincfg/chainhash"
9 | "github.com/btcsuite/btcd/wire"
10 |
11 | errorsmod "cosmossdk.io/errors"
12 | sdk "github.com/cosmos/cosmos-sdk/types"
13 | )
14 |
15 | // Validate validates the block header
16 | func (header *BlockHeader) Validate() error {
17 | wireHeader := header.ToWireHeader()
18 |
19 | if err := blockchain.CheckBlockHeaderSanity(
20 | wireHeader,
21 | sdk.GetConfig().GetBtcChainCfg().PowLimit,
22 | blockchain.NewMedianTime(),
23 | blockchain.BFNone,
24 | ); err != nil {
25 | return errorsmod.Wrapf(ErrInvalidBlockHeader, "check failed: %v", err)
26 | }
27 |
28 | if header.Hash != wireHeader.BlockHash().String() {
29 | return errorsmod.Wrap(ErrInvalidBlockHeader, "incorrect block hash")
30 | }
31 |
32 | return nil
33 | }
34 |
35 | // ToWireHeader converts the block header to wire.BlockHeader
36 | func (header *BlockHeader) ToWireHeader() *wire.BlockHeader {
37 | prevBlockHash, _ := chainhash.NewHashFromStr(header.PreviousBlockHash)
38 | merkleRoot, _ := chainhash.NewHashFromStr(header.MerkleRoot)
39 |
40 | bits := new(big.Int)
41 | bits.SetString(header.Bits, 16)
42 |
43 | return &wire.BlockHeader{
44 | Version: int32(header.Version),
45 | PrevBlock: *prevBlockHash,
46 | MerkleRoot: *merkleRoot,
47 | Timestamp: time.Unix(int64(header.Time), 0),
48 | Bits: uint32(bits.Uint64()),
49 | Nonce: uint32(header.Nonce),
50 | }
51 | }
52 |
53 | // GetWork gets the work of the block header
54 | func (header *BlockHeader) GetWork() *big.Int {
55 | return blockchain.CalcWork(BitsToTargetUint32(header.Bits))
56 | }
57 |
58 | // BlockHeaders defines a set of block headers which form a chain
59 | type BlockHeaders []*BlockHeader
60 |
61 | // Validate validates if each block header is valid and if the block headers form a chain
62 | func (headers BlockHeaders) Validate() error {
63 | if len(headers) == 0 {
64 | return errorsmod.Wrap(ErrInvalidBlockHeaders, "block headers can not be empty")
65 | }
66 |
67 | var lastHeight uint64
68 | var lastHash string
69 |
70 | for i, h := range headers {
71 | if err := h.Validate(); err != nil {
72 | return err
73 | }
74 |
75 | if i > 0 && h.Height != lastHeight+1 && h.PreviousBlockHash != lastHash {
76 | return errorsmod.Wrap(ErrInvalidBlockHeaders, "block headers can not form a chain")
77 | }
78 |
79 | lastHeight = h.Height
80 | lastHash = h.Hash
81 | }
82 |
83 | return nil
84 | }
85 |
86 | // GetTotalWork gets the total work of the block headers
87 | func (headers BlockHeaders) GetTotalWork() *big.Int {
88 | totalWork := new(big.Int)
89 |
90 | for _, h := range headers {
91 | work := h.GetWork()
92 | totalWork = new(big.Int).Add(totalWork, work)
93 | }
94 |
95 | return totalWork
96 | }
97 |
98 | func BitsToTarget(bits string) *big.Int {
99 | n := new(big.Int)
100 | n.SetString(bits, 16)
101 |
102 | return n
103 | }
104 |
105 | func BitsToTargetUint32(bits string) uint32 {
106 | return uint32(BitsToTarget(bits).Uint64())
107 | }
108 |
--------------------------------------------------------------------------------
/testutil/network/network.go:
--------------------------------------------------------------------------------
1 | package network
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | "time"
7 |
8 | pruningtypes "cosmossdk.io/store/pruning/types"
9 | tmrand "github.com/cometbft/cometbft/libs/rand"
10 | dbm "github.com/cosmos/cosmos-db"
11 | "github.com/cosmos/cosmos-sdk/baseapp"
12 | "github.com/cosmos/cosmos-sdk/crypto/hd"
13 | "github.com/cosmos/cosmos-sdk/crypto/keyring"
14 | servertypes "github.com/cosmos/cosmos-sdk/server/types"
15 | "github.com/cosmos/cosmos-sdk/testutil/network"
16 | simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
17 | sdk "github.com/cosmos/cosmos-sdk/types"
18 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
19 | "github.com/stretchr/testify/require"
20 |
21 | "github.com/sideprotocol/side/app"
22 | )
23 |
24 | type (
25 | Network = network.Network
26 | Config = network.Config
27 | )
28 |
29 | // New creates instance with fully configured cosmos network.
30 | // Accepts optional config, that will be used in place of the DefaultConfig() if provided.
31 | func New(t *testing.T, configs ...Config) *Network {
32 | if len(configs) > 1 {
33 | panic("at most one config should be provided")
34 | }
35 | var cfg network.Config
36 | if len(configs) == 0 {
37 | cfg = DefaultConfig()
38 | } else {
39 | cfg = configs[0]
40 | }
41 | net, err := network.New(t, t.TempDir(), cfg)
42 | require.NoError(t, err)
43 | _, err = net.WaitForHeight(1)
44 | require.NoError(t, err)
45 | t.Cleanup(net.Cleanup)
46 | return net
47 | }
48 |
49 | // DefaultConfig will initialize config for the network with custom application,
50 | // genesis and single validator. All other parameters are inherited from cosmos-sdk/testutil/network.DefaultConfig
51 | func DefaultConfig() network.Config {
52 | var (
53 | encoding = app.MakeEncodingConfig()
54 | chainID = "chain-" + tmrand.NewRand().Str(6)
55 | )
56 | return network.Config{
57 | Codec: encoding.Codec,
58 | TxConfig: encoding.TxConfig,
59 | LegacyAmino: encoding.Amino,
60 | InterfaceRegistry: encoding.InterfaceRegistry,
61 | AccountRetriever: authtypes.AccountRetriever{},
62 | AppConstructor: func(val network.ValidatorI) servertypes.Application {
63 | return app.New(
64 | val.GetCtx().Logger,
65 | dbm.NewMemDB(),
66 | nil,
67 | true,
68 | simtestutil.EmptyAppOptions{},
69 | baseapp.SetPruning(pruningtypes.NewPruningOptionsFromString(val.GetAppConfig().Pruning)),
70 | baseapp.SetMinGasPrices(val.GetAppConfig().MinGasPrices),
71 | baseapp.SetChainID(chainID),
72 | )
73 | },
74 | GenesisState: app.ModuleBasics.DefaultGenesis(encoding.Codec),
75 | TimeoutCommit: 2 * time.Second,
76 | ChainID: chainID,
77 | NumValidators: 1,
78 | BondDenom: sdk.DefaultBondDenom,
79 | MinGasPrices: fmt.Sprintf("0.000006%s", sdk.DefaultBondDenom),
80 | AccountTokens: sdk.TokensFromConsensusPower(1000, sdk.DefaultPowerReduction),
81 | StakingTokens: sdk.TokensFromConsensusPower(500, sdk.DefaultPowerReduction),
82 | BondedTokens: sdk.TokensFromConsensusPower(100, sdk.DefaultPowerReduction),
83 | PruningStrategy: pruningtypes.PruningOptionNothing,
84 | CleanupDir: true,
85 | SigningAlgo: string(hd.Secp256k1Type),
86 | KeyringOptions: []keyring.Option{},
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/third_node.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | KEYS=("dev0" "dev1")
4 | CHAINID="testnet"
5 | MONIKER="Side Labs"
6 | BINARY="$HOME/go/bin/sided"
7 | DENOM_STR="uside,uusdc"
8 |
9 | set -f
10 | IFS=,
11 | DENOMS=($DENOM_STR)
12 |
13 | IFS=";"
14 |
15 |
16 | INITIAL_SUPPLY="500000000000000"
17 | BLOCK_GAS=10000000
18 | MAX_GAS=10000000000
19 |
20 | # Remember to change to other types of keyring like 'file' in-case exposing to outside world,
21 | # otherwise your balance will be wiped quickly
22 | # The keyring test does not require private key to steal tokens from you
23 | KEYRING="test"
24 | #KEYALGO="secp256k1"
25 | KEYALGO="segwit"
26 | LOGLEVEL="info"
27 | # Set dedicated home directory for the $BINARY instance
28 | HOMEDIR="$HOME/.side3"
29 |
30 | # Path variables
31 | CONFIG=$HOMEDIR/config/config.toml
32 | APP_TOML=$HOMEDIR/config/app.toml
33 | GENESIS=$HOMEDIR/config/genesis.json
34 | TMP_GENESIS=$HOMEDIR/config/tmp_genesis.json
35 | PEER_ID="peer"
36 |
37 | # validate dependencies are installed
38 | command -v jq >/dev/null 2>&1 || {
39 | echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/"
40 | exit 1
41 | }
42 |
43 | # used to exit on first error (any non-zero exit code)
44 | set -e
45 |
46 | # Reinstall daemon
47 | # make install
48 |
49 | # User prompt if an existing local node configuration is found.
50 | if [ -d "$HOMEDIR" ]; then
51 | printf "\nAn existing folder at '%s' was found. You can choose to delete this folder and start a new local node with new keys from genesis. When declined, the existing local node is started. \n" "$HOMEDIR"
52 | echo "Overwrite the existing configuration and start a new local node? [y/n]"
53 | read -r overwrite
54 | else
55 | overwrite="Y"
56 | fi
57 |
58 |
59 | # Setup local node if overwrite is set to Yes, otherwise skip setup
60 | if [[ $overwrite == "y" || $overwrite == "Y" ]]; then
61 | # Remove the previous folder
62 | rm -rf "$HOMEDIR"
63 |
64 | $BINARY init $MONIKER -o --chain-id $CHAINID --home "$HOMEDIR"
65 |
66 | # Set client config
67 | cp -r ~/.side/config/genesis.json $HOMEDIR/config/genesis.json
68 | $BINARY config keyring-backend $KEYRING --home "$HOMEDIR"
69 | $BINARY config chain-id $CHAINID --home "$HOMEDIR"
70 |
71 | sed -i.bak 's/127.0.0.1:26657/0.0.0.0:36657/g' "$CONFIG"
72 | sed -i.bak 's/127.0.0.1:26658/0.0.0.0:36658/g' "$CONFIG"
73 | sed -i.bak 's/0.0.0.0:26656/0.0.0.0:36656/g' "$CONFIG"
74 | sed -i.bak 's/persistent_peers = ""/persistent_peers = "dfd3e3c99414aa850f6e269cf4a674a66062cd49@127.0.0.1:26656"/g' "$CONFIG"
75 | #sed -i 's/persistent_peers = "$PEERID"/g' "$CONFIG"
76 |
77 | sed -i.bak 's/swagger = false/swagger = true/g' $APP_TOML
78 | sed -i.bak 's/localhost:9090/localhost:8090/g' $APP_TOML
79 |
80 | $BINARY keys add secp256k1_key --key-type segwit --home "$HOMEDIR"
81 | $BINARY tx bank send dev1 $($BINARY keys show secp256k1_key -a --home "$HOMEDIR") 2000000000uside --chain-id $CHAINID --fees 200uside --yes
82 | sleep 6
83 | $BINARY keys add segwit_key --key-type segwit --home "$HOMEDIR"
84 | $BINARY tx bank send dev1 $($BINARY keys show segwit_key -a --home "$HOMEDIR") 2000000000uside --chain-id $CHAINID --fees 200uside --yes
85 |
86 |
87 | fi
88 |
89 | # Start the node (remove the --pruning=nothing flag if historical queries are not needed)
90 | $BINARY start --log_level info --minimum-gas-prices=0.0001${DENOMS[0]} --home "$HOMEDIR" --pruning="everything"
91 |
--------------------------------------------------------------------------------
/second_node.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | KEYS=("dev0" "dev1")
4 | CHAINID="testnet"
5 | MONIKER="Side Labs"
6 | BINARY="$HOME/go/bin/sided"
7 | DENOM_STR="uside,uusdc"
8 |
9 | set -f
10 | IFS=,
11 | DENOMS=($DENOM_STR)
12 |
13 | IFS=";"
14 |
15 |
16 | INITIAL_SUPPLY="500000000000000"
17 | BLOCK_GAS=10000000
18 | MAX_GAS=10000000000
19 |
20 | # Remember to change to other types of keyring like 'file' in-case exposing to outside world,
21 | # otherwise your balance will be wiped quickly
22 | # The keyring test does not require private key to steal tokens from you
23 | KEYRING="test"
24 | #KEYALGO="secp256k1"
25 | KEYALGO="segwit"
26 | LOGLEVEL="info"
27 | # Set dedicated home directory for the $BINARY instance
28 | HOMEDIR="$HOME/.side2"
29 |
30 | # Path variables
31 | CONFIG=$HOMEDIR/config/config.toml
32 | APP_TOML=$HOMEDIR/config/app.toml
33 | GENESIS=$HOMEDIR/config/genesis.json
34 | TMP_GENESIS=$HOMEDIR/config/tmp_genesis.json
35 | PEER_ID="peer"
36 |
37 | # validate dependencies are installed
38 | command -v jq >/dev/null 2>&1 || {
39 | echo >&2 "jq not installed. More info: https://stedolan.github.io/jq/download/"
40 | exit 1
41 | }
42 |
43 | # used to exit on first error (any non-zero exit code)
44 | set -e
45 |
46 | # Reinstall daemon
47 | # make install
48 |
49 | # User prompt if an existing local node configuration is found.
50 | if [ -d "$HOMEDIR" ]; then
51 | printf "\nAn existing folder at '%s' was found. You can choose to delete this folder and start a new local node with new keys from genesis. When declined, the existing local node is started. \n" "$HOMEDIR"
52 | echo "Overwrite the existing configuration and start a new local node? [y/n]"
53 | read -r overwrite
54 | else
55 | overwrite="Y"
56 | fi
57 |
58 |
59 | # Setup local node if overwrite is set to Yes, otherwise skip setup
60 | if [[ $overwrite == "y" || $overwrite == "Y" ]]; then
61 | # Remove the previous folder
62 | rm -rf "$HOMEDIR"
63 |
64 | $BINARY init $MONIKER -o --chain-id $CHAINID --home "$HOMEDIR"
65 |
66 | # Set client config
67 | cp -r ~/.side/config/genesis.json $HOMEDIR/config/genesis.json
68 | $BINARY config keyring-backend $KEYRING --home "$HOMEDIR"
69 | $BINARY config chain-id $CHAINID --home "$HOMEDIR"
70 |
71 | sed -i.bak 's/127.0.0.1:26657/0.0.0.0:16657/g' "$CONFIG"
72 | sed -i.bak 's/127.0.0.1:26658/0.0.0.0:16658/g' "$CONFIG"
73 | sed -i.bak 's/0.0.0.0:26656/0.0.0.0:16656/g' "$CONFIG"
74 | sed -i.bak 's/persistent_peers = ""/persistent_peers = "dfd3e3c99414aa850f6e269cf4a674a66062cd49@127.0.0.1:26656"/g' "$CONFIG"
75 | #sed -i 's/persistent_peers = "$PEERID"/g' "$CONFIG"
76 |
77 | sed -i.bak 's/swagger = false/swagger = true/g' $APP_TOML
78 | sed -i.bak 's/localhost:9090/localhost:7090/g' $APP_TOML
79 |
80 | $BINARY keys add secp256k1_key --key-type segwit --home "$HOMEDIR"
81 | $BINARY tx bank send dev1 $($BINARY keys show secp256k1_key -a --home "$HOMEDIR") 2000000000uside --chain-id $CHAINID --fees 200uside --yes
82 | sleep 6
83 | $BINARY keys add segwit_key --key-type segwit --home "$HOMEDIR"
84 | $BINARY tx bank send dev1 $($BINARY keys show segwit_key -a --home "$HOMEDIR") 2000000000uside --chain-id $CHAINID --fees 200uside --yes
85 |
86 |
87 | fi
88 |
89 | # Start the node (remove the --pruning=nothing flag if historical queries are not needed)
90 | $BINARY start --log_level info --minimum-gas-prices=0.0001${DENOMS[0]} --home "$HOMEDIR" --pruning="everything"
91 |
--------------------------------------------------------------------------------
/x/btcbridge/types/genesis.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "github.com/btcsuite/btcd/chaincfg"
5 |
6 | errorsmod "cosmossdk.io/errors"
7 | sdk "github.com/cosmos/cosmos-sdk/types"
8 | )
9 |
10 | // this line is used by starport scaffolding # genesis/types/import
11 |
12 | func DefaultBestBlockHeader() *BlockHeader {
13 | config := sdk.GetConfig().GetBtcChainCfg()
14 | switch config.Name {
15 | case chaincfg.MainNetParams.Name:
16 | return DefaultMainNetBestBlockHeader()
17 | case chaincfg.SigNetParams.Name:
18 | return DefaultSignetBestBlockHeader()
19 | }
20 | return DefaultTestnetBestBlockHeader()
21 | }
22 |
23 | func DefaultSignetBestBlockHeader() *BlockHeader {
24 | // testnet3 block 2815023
25 | return &BlockHeader{
26 | Version: 536870912,
27 | Hash: "0000017317a7dfa637773406765d308e93cb5a8e5e266bb21687e120bf0e13d3",
28 | Height: 1,
29 | PreviousBlockHash: "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6",
30 | MerkleRoot: "f1192075c6416b02df1487f1f302d925a875bec7e37cf38079e00b1cd831898a",
31 | Time: 1718707794,
32 | Bits: "1e0377ae",
33 | Nonce: 13603325,
34 | Ntx: 1,
35 | }
36 | }
37 |
38 | func DefaultTestnetBestBlockHeader() *BlockHeader {
39 | // testnet3 block 2815023
40 | return &BlockHeader{
41 | Version: 667459584,
42 | Hash: "0000000000000009fb68da72e8994f014fafb455c72978233b94580b12af778c",
43 | Height: 2815023,
44 | PreviousBlockHash: "0000000000000004a29c20eb32532718de8072665620edb4c657b22b4d463967",
45 | MerkleRoot: "9e219423eadce80e882cdff04b3026c9bbc994fd08a774f34a705ca3e710a332",
46 | Time: 1715566066,
47 | Bits: "191881b8",
48 | Nonce: 3913166971,
49 | Ntx: 6236,
50 | }
51 | }
52 |
53 | func DefaultMainNetBestBlockHeader() *BlockHeader {
54 | // testnet3 block 2815023
55 | return &BlockHeader{
56 | Version: 667459584,
57 | Hash: "0000000000000009fb68da72e8994f014fafb455c72978233b94580b12af778c",
58 | Height: 2815023,
59 | PreviousBlockHash: "0000000000000004a29c20eb32532718de8072665620edb4c657b22b4d463967",
60 | MerkleRoot: "9e219423eadce80e882cdff04b3026c9bbc994fd08a774f34a705ca3e710a332",
61 | Time: 1715566066,
62 | Bits: "191881b8",
63 | Nonce: 3913166971,
64 | Ntx: 6236,
65 | }
66 | }
67 |
68 | // DefaultGenesis returns the default genesis state
69 | func DefaultGenesis() *GenesisState {
70 | return &GenesisState{
71 | // this line is used by starport scaffolding # genesis/types/default
72 | Params: DefaultParams(),
73 | BestBlockHeader: DefaultBestBlockHeader(),
74 | BlockHeaders: []*BlockHeader{},
75 | Utxos: []*UTXO{},
76 | DkgRequest: nil,
77 | }
78 | }
79 |
80 | // Validate performs basic genesis state validation returning an error upon any
81 | // failure.
82 | func (gs GenesisState) Validate() error {
83 | // this line is used by starport scaffolding # genesis/types/validate
84 |
85 | // validate the best block header
86 | if gs.BestBlockHeader == nil {
87 | return errorsmod.Wrap(ErrInvalidBlockHeader, "best block header can not be empty")
88 | }
89 | if err := gs.BestBlockHeader.Validate(); err != nil {
90 | return err
91 | }
92 |
93 | // validate block headers
94 | if len(gs.BlockHeaders) != 0 {
95 | if err := BlockHeaders(gs.BlockHeaders).Validate(); err != nil {
96 | return err
97 | }
98 | }
99 |
100 | // validate params
101 | return gs.Params.Validate()
102 | }
103 |
--------------------------------------------------------------------------------
/x/btcbridge/types/signature.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | secp256k1 "github.com/btcsuite/btcd/btcec/v2"
5 | "github.com/btcsuite/btcd/btcec/v2/ecdsa"
6 | "github.com/btcsuite/btcd/btcec/v2/schnorr"
7 | "github.com/btcsuite/btcd/btcutil/psbt"
8 | "github.com/btcsuite/btcd/txscript"
9 | "github.com/btcsuite/btcd/wire"
10 | )
11 |
12 | // VerifyPsbtSignatures verifies the signatures of the given psbt
13 | // Note: assume that the psbt is finalized and all inputs are witness type
14 | func VerifyPsbtSignatures(p *psbt.Packet) bool {
15 | // extract signed tx
16 | signedTx, err := psbt.Extract(p)
17 | if err != nil {
18 | return false
19 | }
20 |
21 | // build previous output fetcher
22 | prevOutputFetcher := txscript.NewMultiPrevOutFetcher(nil)
23 |
24 | for i, txIn := range p.UnsignedTx.TxIn {
25 | prevOutput := p.Inputs[i].WitnessUtxo
26 | if prevOutput == nil {
27 | return false
28 | }
29 |
30 | prevOutputFetcher.AddPrevOut(txIn.PreviousOutPoint, prevOutput)
31 | }
32 |
33 | // verify signatures
34 | for i := range p.Inputs {
35 | prevOutput := p.Inputs[i].WitnessUtxo
36 | hashType := DefaultSigHashType
37 |
38 | switch {
39 | case txscript.IsPayToWitnessPubKeyHash(prevOutput.PkScript):
40 | if !verifyWitnessSignature(signedTx, i, prevOutput, prevOutputFetcher, hashType) {
41 | return false
42 | }
43 |
44 | case txscript.IsPayToTaproot(prevOutput.PkScript):
45 | if !verifyTaprootSignature(signedTx, i, prevOutput, prevOutputFetcher, hashType) {
46 | return false
47 | }
48 |
49 | default:
50 | return false
51 | }
52 | }
53 |
54 | return true
55 | }
56 |
57 | // verifyWitnessSignature verifies the signature of the witness v0 input
58 | func verifyWitnessSignature(tx *wire.MsgTx, idx int, prevOutput *wire.TxOut, prevOutputFetcher txscript.PrevOutputFetcher, hashType txscript.SigHashType) bool {
59 | witness := tx.TxIn[idx].Witness
60 | if len(witness) != 2 {
61 | return false
62 | }
63 |
64 | sigBytes := witness[0]
65 | pkBytes := witness[1]
66 |
67 | sig, err := ecdsa.ParseDERSignature(sigBytes)
68 | if err != nil {
69 | return false
70 | }
71 |
72 | pk, err := secp256k1.ParsePubKey(pkBytes)
73 | if err != nil {
74 | return false
75 | }
76 |
77 | if sigBytes[len(sigBytes)-1] != byte(hashType) {
78 | return false
79 | }
80 |
81 | sigHash, err := txscript.CalcWitnessSigHash(prevOutput.PkScript, txscript.NewTxSigHashes(tx, prevOutputFetcher),
82 | hashType, tx, idx, prevOutput.Value)
83 | if err != nil {
84 | return false
85 | }
86 |
87 | return sig.Verify(sigHash, pk)
88 | }
89 |
90 | // verifyTaprootSignature verifies the signature of the taproot input
91 | func verifyTaprootSignature(tx *wire.MsgTx, idx int, prevOutput *wire.TxOut, prevOutputFetcher txscript.PrevOutputFetcher, hashType txscript.SigHashType) bool {
92 | witness := tx.TxIn[idx].Witness
93 | if len(witness) != 1 || len(witness[0]) == 0 {
94 | return false
95 | }
96 |
97 | sigBytes := witness[0]
98 |
99 | if hashType != txscript.SigHashDefault {
100 | if sigBytes[len(sigBytes)-1] != byte(hashType) {
101 | return false
102 | }
103 |
104 | sigBytes = sigBytes[0 : len(sigBytes)-1]
105 | }
106 |
107 | sig, err := schnorr.ParseSignature(sigBytes)
108 | if err != nil {
109 | return false
110 | }
111 |
112 | pk, err := schnorr.ParsePubKey(prevOutput.PkScript[2:34])
113 | if err != nil {
114 | return false
115 | }
116 |
117 | sigHash, err := txscript.CalcTaprootSignatureHash(txscript.NewTxSigHashes(tx, prevOutputFetcher),
118 | hashType, tx, idx, prevOutputFetcher)
119 | if err != nil {
120 | return false
121 | }
122 |
123 | return sig.Verify(sigHash, pk)
124 | }
125 |
--------------------------------------------------------------------------------
/x/btcbridge/module/genesis_test.go:
--------------------------------------------------------------------------------
1 | package btcbridge_test
2 |
3 | // Path: x/btcbridge/genesis_test.go
4 |
5 | import (
6 | "bytes"
7 | "encoding/hex"
8 | "testing"
9 |
10 | "github.com/btcsuite/btcd/btcutil"
11 | "github.com/btcsuite/btcd/wire"
12 | keepertest "github.com/sideprotocol/side/testutil/keeper"
13 | "github.com/sideprotocol/side/testutil/nullify"
14 | btcbridge "github.com/sideprotocol/side/x/btcbridge/module"
15 | "github.com/sideprotocol/side/x/btcbridge/types"
16 | "github.com/stretchr/testify/require"
17 | )
18 |
19 | func TestGenesis(t *testing.T) {
20 | mnemonic := "sunny bamboo garlic fold reopen exile letter addict forest vessel square lunar shell number deliver cruise calm artist fire just kangaroo suit wheel extend"
21 | println(mnemonic)
22 |
23 | genesisState := types.DefaultGenesis()
24 |
25 | k, ctx := keepertest.BtcBridgeKeeper(t)
26 | btcbridge.InitGenesis(ctx, *k, *genesisState)
27 | got := btcbridge.ExportGenesis(ctx, *k)
28 | require.NotNil(t, got)
29 |
30 | nullify.Fill(&genesisState)
31 | nullify.Fill(got)
32 |
33 | // this line is used by starport scaffolding # genesis/test/assert
34 | }
35 |
36 | // TestSubmitTx tests the SubmitTx function
37 | // func TestSubmitTx(t *testing.T) {
38 |
39 | // // test tx: https://blockchain.info/tx/b657e22827039461a9493ede7bdf55b01579254c1630b0bfc9185ec564fc05ab?format=json
40 |
41 | // k, ctx := keepertest.BtcbridgeClientKeeper(t)
42 |
43 | // txHex := "02000000000101e5df234dbeff74d6da14754ebeea8ab0e2f60b1884566846cf6b36e8ceb5f5350100000000fdffffff02f0490200000000001600142ac13073e8d491428790481321a636696d00107681d7e205000000001600142bf3aa186cbdcbe88b70a67edcd5a32ce5e8e6d8024730440220081ee61d749ce8cedcf6eedde885579af2eb65ca67d29e6ae2c37109d81cbbb202203a1891ce45f91f159ccf04348ef37a3d1a12d89e5e01426e061326057e6c128d012103036bbdd77c9a932f37bd66175967c7fb7eb75ece06b87c1ad1716770cb3ca4ee79fc2a00"
44 | // prevTxHex := "0200000000010183372652f2af9ab34b3a003efada6b054c75583185ac130de72599dfdf4e462b0100000000fdffffff02f0490200000000001600142ac13073e8d491428790481321a636696d001076a64ee50500000000160014a03614eef338681373de94a2dc2574de55da1980024730440220274250f6036bea0947daf4455ab4976f81721257d163fd952fb5b0c70470edc602202fba816be260219bbc40a8983c459cf05cf2209bf1e62e7ccbf78aec54db607f0121031cee21ef69fe68b240c3032616fa310c6a60a856c0a7e0c1298815c92fb2c61788fb2a00"
45 |
46 | // msg := &types.MsgSubmitTransactionRequest{
47 | // Sender: "",
48 | // Blockhash: "000000000d73ecf25d3bf8e6ae65c35aa2a90e3271edff8bab90d87ed875f13b",
49 | // TxBytes: "0100000001b3f7",
50 | // PrevTxBytes: "0100000001b3f7",
51 | // Proof: []string{"0100000001b3f7"},
52 | // }
53 | // // this line is used by starport scaffolding # handler/test/submit
54 | // err := k.ProcessBitcoinDepositTransaction(ctx, msg)
55 | // require.NoError(t, err)
56 | // }
57 |
58 | // Decode transaction
59 | func TestDecodeTransaction(t *testing.T) {
60 | hexStr := "02000000000101e5df234dbeff74d6da14754ebeea8ab0e2f60b1884566846cf6b36e8ceb5f5350100000000fdffffff02f0490200000000001600142ac13073e8d491428790481321a636696d00107681d7e205000000001600142bf3aa186cbdcbe88b70a67edcd5a32ce5e8e6d8024730440220081ee61d749ce8cedcf6eedde885579af2eb65ca67d29e6ae2c37109d81cbbb202203a1891ce45f91f159ccf04348ef37a3d1a12d89e5e01426e061326057e6c128d012103036bbdd77c9a932f37bd66175967c7fb7eb75ece06b87c1ad1716770cb3ca4ee79fc2a00"
61 |
62 | // Decode the hex string to transaction
63 | txBytes, err := hex.DecodeString(hexStr)
64 | require.NoError(t, err)
65 |
66 | // Create a new transaction
67 | var tx wire.MsgTx
68 | err = tx.Deserialize(bytes.NewReader(txBytes))
69 | require.NoError(t, err)
70 |
71 | uTx := btcutil.NewTx(&tx)
72 |
73 | for _, input := range uTx.MsgTx().TxIn {
74 | t.Log(input.PreviousOutPoint.String())
75 | }
76 |
77 | require.GreaterOrEqual(t, len(uTx.MsgTx().TxIn), 1)
78 | }
79 |
--------------------------------------------------------------------------------
/proto/side/btcbridge/params.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package side.btcbridge;
3 |
4 | import "gogoproto/gogo.proto";
5 | import "google/protobuf/duration.proto";
6 | import "cosmos/base/v1beta1/coin.proto";
7 |
8 | option go_package = "github.com/sideprotocol/side/x/btcbridge/types";
9 |
10 | // Params defines the parameters for the module.
11 | message Params {
12 | // The minimum number of confirmations required for the deposit transactions
13 | int32 deposit_confirmation_depth = 1;
14 | // The minimum number of confirmations required for the withdrawal transactions
15 | int32 withdraw_confirmation_depth = 2;
16 | // The allowed maximum depth for bitcoin block reorganization
17 | int32 max_reorg_depth = 3;
18 | // Indicates the maximum depth or distance from the latest block up to which transactions are considered for acceptance.
19 | uint64 max_acceptable_block_depth = 4;
20 | // The denomination of the voucher
21 | string btc_voucher_denom = 5;
22 | // Indicates if deposit is enabled
23 | bool deposit_enabled = 6;
24 | // Indicates if withdrawal is enabled
25 | bool withdraw_enabled = 7;
26 | // Trusted relayers to submit bitcoin block headers
27 | repeated string trusted_btc_relayers = 8;
28 | // Trusted relayers for non-btc asset deposit
29 | repeated string trusted_non_btc_relayers = 9;
30 | // Trusted fee providers to submit bitcoin fee rate
31 | repeated string trusted_fee_providers = 10;
32 | // Period of validity for the fee rate
33 | int64 fee_rate_validity_period = 11;
34 | // Asset vaults
35 | repeated Vault vaults = 12;
36 | // Withdrawal params
37 | WithdrawParams withdraw_params = 13 [(gogoproto.nullable) = false];
38 | // Protocol limitations
39 | ProtocolLimits protocol_limits = 14 [(gogoproto.nullable) = false];
40 | // Protocol fees
41 | ProtocolFees protocol_fees = 15 [(gogoproto.nullable) = false];
42 | // TSS params
43 | TSSParams tss_params = 16 [(gogoproto.nullable) = false];
44 | }
45 |
46 | // AssetType defines the type of asset
47 | enum AssetType {
48 | // Unspecified asset type
49 | ASSET_TYPE_UNSPECIFIED = 0;
50 | // BTC
51 | ASSET_TYPE_BTC = 1;
52 | // BRC20: ordi, sats
53 | ASSET_TYPE_BRC20 = 2;
54 | // RUNE: dog•go•to•the•moon
55 | ASSET_TYPE_RUNES = 3;
56 | }
57 |
58 | // Vault defines the asset vault
59 | message Vault {
60 | // the vault address for deposit
61 | string address = 1;
62 | // public key of the vault
63 | string pub_key = 2;
64 | // the asset type supported by the vault
65 | AssetType asset_type = 3;
66 | // version
67 | uint64 version = 4;
68 | }
69 |
70 | message WithdrawParams {
71 | // Maximum number of utxos used to build the signing request; O means unlimited
72 | uint32 max_utxo_num = 1;
73 | // Period for handling btc withdrawal requests
74 | int64 btc_batch_withdraw_period = 2;
75 | // Maximum number of btc withdrawal requests to be handled per batch
76 | uint32 max_btc_batch_withdraw_num = 3;
77 | }
78 |
79 | // ProtocolLimits defines the params related to the the protocol limitations
80 | message ProtocolLimits {
81 | // The minimum deposit amount for btc in sat
82 | int64 btc_min_deposit = 1;
83 | // The minimum withdrawal amount for btc in sat
84 | int64 btc_min_withdraw = 2;
85 | // The maximum withdrawal amount for btc in sat
86 | int64 btc_max_withdraw = 3;
87 | }
88 |
89 | // ProtocolFees defines the params related to the protocol fees
90 | message ProtocolFees {
91 | // Protocol fee amount for deposit in sat
92 | int64 deposit_fee = 1;
93 | // Protocol fee amount for withdrawal in sat
94 | int64 withdraw_fee = 2;
95 | // Protocol fee collector
96 | string collector = 3;
97 | }
98 |
99 | // TSSParams defines the params related to TSS
100 | message TSSParams {
101 | // Timeout duration for DKG request
102 | google.protobuf.Duration dkg_timeout_period = 1 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
103 | // Transition period after which TSS participants update process is completed
104 | google.protobuf.Duration participant_update_transition_period = 2 [(gogoproto.nullable) = false, (gogoproto.stdduration) = true];
105 | }
106 |
--------------------------------------------------------------------------------
/x/btcbridge/keeper/params.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 |
6 | "github.com/sideprotocol/side/x/btcbridge/types"
7 | )
8 |
9 | // DepositConfirmationDepth gets the confirmation depth for deposit transactions
10 | func (k Keeper) DepositConfirmationDepth(ctx sdk.Context) int32 {
11 | return k.GetParams(ctx).DepositConfirmationDepth
12 | }
13 |
14 | // WithdrawConfirmationDepth gets the confirmation depth for withdrawal transactions
15 | func (k Keeper) WithdrawConfirmationDepth(ctx sdk.Context) int32 {
16 | return k.GetParams(ctx).WithdrawConfirmationDepth
17 | }
18 |
19 | // MaxReorgDepth gets the allowed maximum reorg depth
20 | func (k Keeper) MaxReorgDepth(ctx sdk.Context) int32 {
21 | return k.GetParams(ctx).MaxReorgDepth
22 | }
23 |
24 | // DepositEnabled returns true if deposit enabled, false otherwise
25 | func (k Keeper) DepositEnabled(ctx sdk.Context) bool {
26 | return k.GetParams(ctx).DepositEnabled
27 | }
28 |
29 | // WithdrawEnabled returns true if withdrawal enabled, false otherwise
30 | func (k Keeper) WithdrawEnabled(ctx sdk.Context) bool {
31 | return k.GetParams(ctx).WithdrawEnabled
32 | }
33 |
34 | // ProtocolDepositFeeEnabled returns true if the protocol fee is required for deposit, false otherwise
35 | func (k Keeper) ProtocolDepositFeeEnabled(ctx sdk.Context) bool {
36 | return k.GetParams(ctx).ProtocolFees.DepositFee > 0
37 | }
38 |
39 | // ProtocolWithdrawFeeEnabled returns true if the protocol fee is required for withdrawal, false otherwise
40 | func (k Keeper) ProtocolWithdrawFeeEnabled(ctx sdk.Context) bool {
41 | return k.GetParams(ctx).ProtocolFees.WithdrawFee > 0
42 | }
43 |
44 | // ProtocolFeeCollector gets the protocol fee collector
45 | func (k Keeper) ProtocolFeeCollector(ctx sdk.Context) string {
46 | return k.GetParams(ctx).ProtocolFees.Collector
47 | }
48 |
49 | // BtcDenom gets the btc denomination
50 | func (k Keeper) BtcDenom(ctx sdk.Context) string {
51 | return k.GetParams(ctx).BtcVoucherDenom
52 | }
53 |
54 | // IsTrustedBtcRelayer returns true if the given address is a trusted btc relayer, false otherwise
55 | func (k Keeper) IsTrustedBtcRelayer(ctx sdk.Context, addr string) bool {
56 | trustedBtcRelayers := k.GetParams(ctx).TrustedBtcRelayers
57 | if len(trustedBtcRelayers) == 0 {
58 | return true
59 | }
60 |
61 | for _, relayer := range trustedBtcRelayers {
62 | if relayer == addr {
63 | return true
64 | }
65 | }
66 |
67 | return false
68 | }
69 |
70 | // IsTrustedNonBtcRelayer returns true if the given address is a trusted non-btc relayer, false otherwise
71 | func (k Keeper) IsTrustedNonBtcRelayer(ctx sdk.Context, addr string) bool {
72 | for _, relayer := range k.GetParams(ctx).TrustedNonBtcRelayers {
73 | if relayer == addr {
74 | return true
75 | }
76 | }
77 |
78 | return false
79 | }
80 |
81 | // IsTrustedFeeProvider returns true if the given address is a trusted fee provider, false otherwise
82 | func (k Keeper) IsTrustedFeeProvider(ctx sdk.Context, addr string) bool {
83 | for _, provider := range k.GetParams(ctx).TrustedFeeProviders {
84 | if provider == addr {
85 | return true
86 | }
87 | }
88 |
89 | return false
90 | }
91 |
92 | // GetVaultByAssetTypeAndVersion gets the vault by the given asset type and version
93 | func (k Keeper) GetVaultByAssetTypeAndVersion(ctx sdk.Context, assetType types.AssetType, version uint64) *types.Vault {
94 | for _, v := range k.GetParams(ctx).Vaults {
95 | if v.AssetType == assetType && v.Version == version {
96 | return v
97 | }
98 | }
99 |
100 | return nil
101 | }
102 |
103 | // GetVaultVersionByAddress gets the vault version of the given address
104 | func (k Keeper) GetVaultVersionByAddress(ctx sdk.Context, address string) (uint64, bool) {
105 | for _, v := range k.GetParams(ctx).Vaults {
106 | if v.Address == address {
107 | return v.Version, true
108 | }
109 | }
110 |
111 | return 0, false
112 | }
113 |
114 | // GetMaxUtxoNum gets the maximum utxo number for the signing request
115 | func (k Keeper) GetMaxUtxoNum(ctx sdk.Context) int {
116 | params := k.GetParams(ctx)
117 |
118 | return int(params.WithdrawParams.MaxUtxoNum)
119 | }
120 |
--------------------------------------------------------------------------------
/api/side/incentive/tx_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 | // versions:
3 | // - protoc-gen-go-grpc v1.3.0
4 | // - protoc (unknown)
5 | // source: side/incentive/tx.proto
6 |
7 | package incentive
8 |
9 | import (
10 | context "context"
11 | grpc "google.golang.org/grpc"
12 | codes "google.golang.org/grpc/codes"
13 | status "google.golang.org/grpc/status"
14 | )
15 |
16 | // This is a compile-time assertion to ensure that this generated file
17 | // is compatible with the grpc package it is being compiled against.
18 | // Requires gRPC-Go v1.32.0 or later.
19 | const _ = grpc.SupportPackageIsVersion7
20 |
21 | const (
22 | Msg_UpdateParams_FullMethodName = "/side.incentive.Msg/UpdateParams"
23 | )
24 |
25 | // MsgClient is the client API for Msg service.
26 | //
27 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
28 | type MsgClient interface {
29 | // UpdateParams defines a governance operation for updating the x/incentive module
30 | // parameters. The authority defaults to the x/gov module account.
31 | //
32 | // Since: cosmos-sdk 0.47
33 | UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error)
34 | }
35 |
36 | type msgClient struct {
37 | cc grpc.ClientConnInterface
38 | }
39 |
40 | func NewMsgClient(cc grpc.ClientConnInterface) MsgClient {
41 | return &msgClient{cc}
42 | }
43 |
44 | func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) {
45 | out := new(MsgUpdateParamsResponse)
46 | err := c.cc.Invoke(ctx, Msg_UpdateParams_FullMethodName, in, out, opts...)
47 | if err != nil {
48 | return nil, err
49 | }
50 | return out, nil
51 | }
52 |
53 | // MsgServer is the server API for Msg service.
54 | // All implementations must embed UnimplementedMsgServer
55 | // for forward compatibility
56 | type MsgServer interface {
57 | // UpdateParams defines a governance operation for updating the x/incentive module
58 | // parameters. The authority defaults to the x/gov module account.
59 | //
60 | // Since: cosmos-sdk 0.47
61 | UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error)
62 | mustEmbedUnimplementedMsgServer()
63 | }
64 |
65 | // UnimplementedMsgServer must be embedded to have forward compatible implementations.
66 | type UnimplementedMsgServer struct {
67 | }
68 |
69 | func (UnimplementedMsgServer) UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) {
70 | return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented")
71 | }
72 | func (UnimplementedMsgServer) mustEmbedUnimplementedMsgServer() {}
73 |
74 | // UnsafeMsgServer may be embedded to opt out of forward compatibility for this service.
75 | // Use of this interface is not recommended, as added methods to MsgServer will
76 | // result in compilation errors.
77 | type UnsafeMsgServer interface {
78 | mustEmbedUnimplementedMsgServer()
79 | }
80 |
81 | func RegisterMsgServer(s grpc.ServiceRegistrar, srv MsgServer) {
82 | s.RegisterService(&Msg_ServiceDesc, srv)
83 | }
84 |
85 | func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
86 | in := new(MsgUpdateParams)
87 | if err := dec(in); err != nil {
88 | return nil, err
89 | }
90 | if interceptor == nil {
91 | return srv.(MsgServer).UpdateParams(ctx, in)
92 | }
93 | info := &grpc.UnaryServerInfo{
94 | Server: srv,
95 | FullMethod: Msg_UpdateParams_FullMethodName,
96 | }
97 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
98 | return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams))
99 | }
100 | return interceptor(ctx, in, info, handler)
101 | }
102 |
103 | // Msg_ServiceDesc is the grpc.ServiceDesc for Msg service.
104 | // It's only intended for direct use with grpc.RegisterService,
105 | // and not to be introspected or modified (even as a copy)
106 | var Msg_ServiceDesc = grpc.ServiceDesc{
107 | ServiceName: "side.incentive.Msg",
108 | HandlerType: (*MsgServer)(nil),
109 | Methods: []grpc.MethodDesc{
110 | {
111 | MethodName: "UpdateParams",
112 | Handler: _Msg_UpdateParams_Handler,
113 | },
114 | },
115 | Streams: []grpc.StreamDesc{},
116 | Metadata: "side/incentive/tx.proto",
117 | }
118 |
--------------------------------------------------------------------------------
/x/incentive/keeper/reward.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 |
6 | "github.com/sideprotocol/side/x/incentive/types"
7 | )
8 |
9 | // GetRewards gets the rewards of the given address
10 | func (k Keeper) GetRewards(ctx sdk.Context, address string) *types.Rewards {
11 | store := ctx.KVStore(k.storeKey)
12 |
13 | var rewards types.Rewards
14 | bz := store.Get(types.RewardsKey(address))
15 | k.cdc.MustUnmarshal(bz, &rewards)
16 |
17 | return &rewards
18 | }
19 |
20 | // HasRewards returns true if the given address has received rewards, false otherwise
21 | func (k Keeper) HasRewards(ctx sdk.Context, address string) bool {
22 | store := ctx.KVStore(k.storeKey)
23 |
24 | return store.Has(types.RewardsKey(address))
25 | }
26 |
27 | // SetRewards sets the given rewards
28 | func (k Keeper) SetRewards(ctx sdk.Context, rewards *types.Rewards) {
29 | store := ctx.KVStore(k.storeKey)
30 |
31 | bz := k.cdc.MustMarshal(rewards)
32 |
33 | store.Set(types.RewardsKey(rewards.Address), bz)
34 | }
35 |
36 | // AddDepositReward adds the deposit reward for the specified address by the given amount
37 | func (k Keeper) AddDepositReward(ctx sdk.Context, address string, amount sdk.Coin) {
38 | rewards := k.GetRewards(ctx, address)
39 |
40 | if len(rewards.Address) == 0 {
41 | rewards.Address = address
42 | rewards.DepositReward = amount
43 | rewards.TotalAmount = amount
44 | } else {
45 | rewards.DepositReward = amount.AddAmount(rewards.DepositReward.Amount)
46 | rewards.TotalAmount = amount.AddAmount(rewards.TotalAmount.Amount)
47 | }
48 |
49 | rewards.DepositCount += 1
50 |
51 | k.SetRewards(ctx, rewards)
52 | }
53 |
54 | // AddWithdrawReward adds the withdrawal reward for the specified address by the given amount
55 | func (k Keeper) AddWithdrawReward(ctx sdk.Context, address string, amount sdk.Coin) {
56 | rewards := k.GetRewards(ctx, address)
57 |
58 | if len(rewards.Address) == 0 {
59 | rewards.Address = address
60 | rewards.WithdrawReward = amount
61 | rewards.TotalAmount = amount
62 | } else {
63 | rewards.WithdrawReward = amount.AddAmount(rewards.WithdrawReward.Amount)
64 | rewards.TotalAmount = amount.AddAmount(rewards.TotalAmount.Amount)
65 | }
66 |
67 | rewards.WithdrawCount += 1
68 |
69 | k.SetRewards(ctx, rewards)
70 | }
71 |
72 | // GetRewardStats gets the reward statistics
73 | func (k Keeper) GetRewardStats(ctx sdk.Context) *types.RewardStats {
74 | store := ctx.KVStore(k.storeKey)
75 |
76 | var stats types.RewardStats
77 | bz := store.Get(types.RewardStatsKey)
78 | k.cdc.MustUnmarshal(bz, &stats)
79 |
80 | return &stats
81 | }
82 |
83 | // SetRewardStats sets the reward statistics
84 | func (k Keeper) SetRewardStats(ctx sdk.Context, rewardStats *types.RewardStats) {
85 | store := ctx.KVStore(k.storeKey)
86 |
87 | bz := k.cdc.MustMarshal(rewardStats)
88 |
89 | store.Set(types.RewardStatsKey, bz)
90 | }
91 |
92 | // UpdateRewardStats updates the reward statistics
93 | func (k Keeper) UpdateRewardStats(ctx sdk.Context, address string, reward sdk.Coin) {
94 | stats := k.GetRewardStats(ctx)
95 |
96 | if !k.HasRewards(ctx, address) {
97 | stats.AddressCount += 1
98 | }
99 |
100 | if stats.TxCount == 0 {
101 | stats.TotalRewardAmount = reward
102 | } else {
103 | stats.TotalRewardAmount = reward.AddAmount(stats.TotalRewardAmount.Amount)
104 | }
105 |
106 | stats.TxCount += 1
107 |
108 | k.SetRewardStats(ctx, stats)
109 | }
110 |
111 | // DistributeDepositReward distributes reward for deposit
112 | func (k Keeper) DistributeDepositReward(ctx sdk.Context, address string) error {
113 | if !k.DepositIncentiveEnabled(ctx) {
114 | return types.ErrDepositIncentiveNotEnabled
115 | }
116 |
117 | rewardAmount := k.RewardPerDeposit(ctx)
118 |
119 | if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sdk.MustAccAddressFromBech32(address), sdk.NewCoins(rewardAmount)); err != nil {
120 | return err
121 | }
122 |
123 | k.AddDepositReward(ctx, address, rewardAmount)
124 | k.UpdateRewardStats(ctx, address, rewardAmount)
125 |
126 | return nil
127 | }
128 |
129 | // DistributeWithdrawReward distributes reward for withdrawal
130 | func (k Keeper) DistributeWithdrawReward(ctx sdk.Context, address string) error {
131 | if !k.WithdrawIncentiveEnabled(ctx) {
132 | return types.ErrWithdrawIncentiveNotEnabled
133 | }
134 |
135 | rewardAmount := k.RewardPerWithdraw(ctx)
136 |
137 | if err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, sdk.MustAccAddressFromBech32(address), sdk.NewCoins(rewardAmount)); err != nil {
138 | return err
139 | }
140 |
141 | k.AddWithdrawReward(ctx, address, rewardAmount)
142 | k.UpdateRewardStats(ctx, address, rewardAmount)
143 |
144 | return nil
145 | }
146 |
--------------------------------------------------------------------------------
/x/btcbridge/types/errors.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | // DONTCOVER
4 |
5 | import (
6 | errorsmod "cosmossdk.io/errors"
7 | )
8 |
9 | // x/btcbridge module sentinel errors
10 | var (
11 | ErrInvalidBlockHeader = errorsmod.Register(ModuleName, 1100, "invalid block header")
12 | ErrInvalidBlockHeaders = errorsmod.Register(ModuleName, 1101, "invalid block headers")
13 | ErrInvalidReorgDepth = errorsmod.Register(ModuleName, 1102, "invalid reorg depth")
14 |
15 | ErrBlockNotFound = errorsmod.Register(ModuleName, 2101, "block not found")
16 | ErrTransactionNotIncluded = errorsmod.Register(ModuleName, 2102, "transaction not included in block")
17 | ErrNotConfirmed = errorsmod.Register(ModuleName, 2103, "transaction not confirmed")
18 | ErrExceedMaxAcceptanceDepth = errorsmod.Register(ModuleName, 2104, "exceed max acceptance block depth")
19 | ErrUnsupportedScriptType = errorsmod.Register(ModuleName, 2105, "unsupported script type")
20 | ErrInvalidBtcTransaction = errorsmod.Register(ModuleName, 2106, "invalid bitcoin transaction")
21 | ErrTransactionAlreadyMinted = errorsmod.Register(ModuleName, 2107, "transaction already minted")
22 | ErrInvalidDepositTransaction = errorsmod.Register(ModuleName, 2108, "invalid deposit transaction")
23 | ErrInvalidDepositAmount = errorsmod.Register(ModuleName, 2109, "invalid deposit amount")
24 | ErrDepositNotEnabled = errorsmod.Register(ModuleName, 2110, "deposit not enabled")
25 | ErrUntrustedBtcRelayer = errorsmod.Register(ModuleName, 2111, "untrusted btc relayer")
26 | ErrUntrustedNonBtcRelayer = errorsmod.Register(ModuleName, 2112, "untrusted non btc relayer")
27 | ErrUntrustedFeeProvider = errorsmod.Register(ModuleName, 2113, "untrusted fee provider")
28 |
29 | ErrInvalidWithdrawAmount = errorsmod.Register(ModuleName, 3100, "invalid withdrawal amount")
30 | ErrInvalidBtcAddress = errorsmod.Register(ModuleName, 3101, "invalid btc address")
31 | ErrAssetNotSupported = errorsmod.Register(ModuleName, 3102, "asset not supported")
32 | ErrInvalidFeeRate = errorsmod.Register(ModuleName, 3103, "invalid fee rate")
33 | ErrDustOutput = errorsmod.Register(ModuleName, 3104, "too small output amount")
34 | ErrInsufficientUTXOs = errorsmod.Register(ModuleName, 3105, "insufficient utxos")
35 | ErrMaxTransactionWeightExceeded = errorsmod.Register(ModuleName, 3106, "maximum transaction weight exceeded")
36 | ErrMaxUTXONumExceeded = errorsmod.Register(ModuleName, 3107, "maximum utxo number exceeded")
37 | ErrFailToSerializePsbt = errorsmod.Register(ModuleName, 3108, "failed to serialize psbt")
38 | ErrInvalidSignatures = errorsmod.Register(ModuleName, 3109, "invalid signatures")
39 | ErrSigningRequestDoesNotExist = errorsmod.Register(ModuleName, 3110, "signing request does not exist")
40 | ErrSigningRequestConfirmed = errorsmod.Register(ModuleName, 3111, "signing request has been confirmed")
41 | ErrWithdrawNotEnabled = errorsmod.Register(ModuleName, 3112, "withdrawal not enabled")
42 |
43 | ErrUTXODoesNotExist = errorsmod.Register(ModuleName, 4100, "utxo does not exist")
44 | ErrUTXOLocked = errorsmod.Register(ModuleName, 4101, "utxo locked")
45 | ErrUTXOUnlocked = errorsmod.Register(ModuleName, 4102, "utxo unlocked")
46 |
47 | ErrInvalidRunes = errorsmod.Register(ModuleName, 5100, "invalid runes")
48 | ErrInvalidRuneId = errorsmod.Register(ModuleName, 5101, "invalid rune id")
49 |
50 | ErrInvalidParams = errorsmod.Register(ModuleName, 6100, "invalid module params")
51 | ErrInvalidRelayers = errorsmod.Register(ModuleName, 6101, "invalid relayers")
52 | ErrInvalidFeeProviders = errorsmod.Register(ModuleName, 6102, "invalid fee providers")
53 |
54 | ErrInvalidDKGParams = errorsmod.Register(ModuleName, 7100, "invalid dkg params")
55 | ErrDKGRequestDoesNotExist = errorsmod.Register(ModuleName, 7101, "dkg request does not exist")
56 | ErrDKGCompletionRequestExists = errorsmod.Register(ModuleName, 7102, "dkg completion request already exists")
57 | ErrInvalidDKGCompletionRequest = errorsmod.Register(ModuleName, 7103, "invalid dkg completion request")
58 | ErrUnauthorizedDKGCompletionRequest = errorsmod.Register(ModuleName, 7104, "unauthorized dkg completion request")
59 | ErrInvalidVaultVersion = errorsmod.Register(ModuleName, 7105, "invalid vault version")
60 | ErrInvalidVault = errorsmod.Register(ModuleName, 7106, "invalid vault")
61 | ErrVaultDoesNotExist = errorsmod.Register(ModuleName, 7107, "vault does not exist")
62 | ErrInvalidPsbt = errorsmod.Register(ModuleName, 7108, "invalid psbt")
63 |
64 | ErrInvalidConsolidation = errorsmod.Register(ModuleName, 8100, "invalid consolidation")
65 | )
66 |
--------------------------------------------------------------------------------
/cmd/sided/cmd/root.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "os"
5 |
6 | "github.com/spf13/cobra"
7 | "github.com/spf13/pflag"
8 |
9 | "cosmossdk.io/log"
10 | dbm "github.com/cosmos/cosmos-db"
11 | "github.com/cosmos/cosmos-sdk/client"
12 | "github.com/cosmos/cosmos-sdk/client/config"
13 | "github.com/cosmos/cosmos-sdk/crypto/keyring"
14 | "github.com/cosmos/cosmos-sdk/server"
15 | simtestutil "github.com/cosmos/cosmos-sdk/testutil/sims"
16 | "github.com/cosmos/cosmos-sdk/types/tx/signing"
17 | "github.com/cosmos/cosmos-sdk/x/auth/tx"
18 | txmodule "github.com/cosmos/cosmos-sdk/x/auth/tx/config"
19 | "github.com/cosmos/cosmos-sdk/x/auth/types"
20 |
21 | // this line is used by starport scaffolding # root/moduleImport
22 |
23 | "github.com/sideprotocol/side/app"
24 | "github.com/sideprotocol/side/app/params"
25 | )
26 |
27 | // NewRootCmd creates a new root command for a Cosmos SDK application
28 | func NewRootCmd() *cobra.Command {
29 | // we "pre"-instantiate the application for getting the injected/configured encoding configuration
30 | // note, this is not necessary when using app wiring, as depinject can be directly used (see root_v2.go)
31 | tempApp := app.New(log.NewNopLogger(), dbm.NewMemDB(), nil, false, simtestutil.NewAppOptionsWithFlagHome(tempDir()))
32 | encodingConfig := params.EncodingConfig{
33 | InterfaceRegistry: tempApp.InterfaceRegistry(),
34 | Codec: tempApp.AppCodec(),
35 | TxConfig: tempApp.TxConfig(),
36 | Amino: tempApp.LegacyAmino(),
37 | }
38 |
39 | initClientCtx := client.Context{}.
40 | WithCodec(encodingConfig.Codec).
41 | WithInterfaceRegistry(encodingConfig.InterfaceRegistry).
42 | WithTxConfig(encodingConfig.TxConfig).
43 | WithLegacyAmino(encodingConfig.Amino).
44 | WithInput(os.Stdin).
45 | WithAccountRetriever(types.AccountRetriever{}).
46 | WithHomeDir(app.DefaultNodeHome).
47 | WithViper("")
48 |
49 | rootCmd := &cobra.Command{
50 | Use: app.Name + "d",
51 | Short: "Start side node",
52 | SilenceErrors: true,
53 | PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
54 | // set the default command outputs
55 | cmd.SetOut(cmd.OutOrStdout())
56 | cmd.SetErr(cmd.ErrOrStderr())
57 |
58 | initClientCtx = initClientCtx.WithCmdContext(cmd.Context())
59 | initClientCtx, err := client.ReadPersistentCommandFlags(initClientCtx, cmd.Flags())
60 | if err != nil {
61 | return err
62 | }
63 |
64 | initClientCtx, err = config.ReadFromClientConfig(initClientCtx)
65 | if err != nil {
66 | return err
67 | }
68 |
69 | // This needs to go after ReadFromClientConfig, as that function
70 | // sets the RPC client needed for SIGN_MODE_TEXTUAL. This sign mode
71 | // is only available if the client is online.
72 | if !initClientCtx.Offline {
73 | enabledSignModes := append(tx.DefaultSignModes, signing.SignMode_SIGN_MODE_TEXTUAL)
74 | txConfigOpts := tx.ConfigOptions{
75 | EnabledSignModes: enabledSignModes,
76 | TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx),
77 | }
78 | txConfig, err := tx.NewTxConfigWithOptions(
79 | initClientCtx.Codec,
80 | txConfigOpts,
81 | )
82 | if err != nil {
83 | return err
84 | }
85 |
86 | initClientCtx = initClientCtx.WithTxConfig(txConfig)
87 | }
88 |
89 | if err := client.SetCmdClientContextHandler(initClientCtx, cmd); err != nil {
90 | return err
91 | }
92 |
93 | customAppTemplate, customAppConfig := initAppConfig()
94 | customCMTConfig := initCometBFTConfig()
95 |
96 | return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customCMTConfig)
97 | },
98 | }
99 |
100 | initRootCmd(rootCmd, encodingConfig.TxConfig, tempApp.BasicModuleManager)
101 |
102 | // add keyring to autocli opts
103 | autoCliOpts := tempApp.AutoCliOpts()
104 | initClientCtx, _ = config.ReadFromClientConfig(initClientCtx)
105 | autoCliOpts.Keyring, _ = keyring.NewAutoCLIKeyring(initClientCtx.Keyring)
106 | autoCliOpts.ClientCtx = initClientCtx
107 | autoCliOpts.TxConfigOpts = tx.ConfigOptions{
108 | EnabledSignModes: tx.DefaultSignModes,
109 | TextualCoinMetadataQueryFn: txmodule.NewGRPCCoinMetadataQueryFn(initClientCtx),
110 | }
111 |
112 | if err := autoCliOpts.EnhanceRootCommand(rootCmd); err != nil {
113 | panic(err)
114 | }
115 |
116 | return rootCmd
117 | }
118 |
119 | func overwriteFlagDefaults(c *cobra.Command, defaults map[string]string) {
120 | set := func(s *pflag.FlagSet, key, val string) {
121 | if f := s.Lookup(key); f != nil {
122 | f.DefValue = val
123 | _ = f.Value.Set(val)
124 | }
125 | }
126 | for key, val := range defaults {
127 | set(c.Flags(), key, val)
128 | set(c.PersistentFlags(), key, val)
129 | }
130 | for _, c := range c.Commands() {
131 | overwriteFlagDefaults(c, defaults)
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/x/btcbridge/types/policy.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "github.com/btcsuite/btcd/btcutil"
5 | "github.com/btcsuite/btcd/chaincfg"
6 | "github.com/btcsuite/btcd/txscript"
7 | "github.com/btcsuite/btcd/wire"
8 | )
9 |
10 | const (
11 | // maximum allowed number of the non-vault outputs for the btc deposit transaction
12 | MaxNonVaultOutNum = 1
13 |
14 | // maximum allowed number of the non-vault outputs for the runes deposit transaction
15 | RunesMaxNonVaultOutNum = 3
16 |
17 | // allowed number of edicts in the runes payload for the runes deposit transaction
18 | RunesEdictNum = 1
19 |
20 | // transaction input sequence intended to identify the txs built by the bridge (for relayers' convenience)
21 | MagicSequence = 1<<31 + 0xde
22 | )
23 |
24 | // ExtractRecipientAddr extracts the recipient address for minting voucher token by the type of the asset to be deposited
25 | func ExtractRecipientAddr(tx *wire.MsgTx, prevTx *wire.MsgTx, vaults []*Vault, isRunes bool, chainCfg *chaincfg.Params) (btcutil.Address, error) {
26 | if isRunes {
27 | return ExtractRunesRecipientAddr(tx, prevTx, vaults, chainCfg)
28 | }
29 |
30 | return ExtractCommonRecipientAddr(tx, prevTx, vaults, chainCfg)
31 | }
32 |
33 | // ExtractCommonRecipientAddr extracts the recipient address for minting voucher token in the common case.
34 | // First, extract the recipient from the tx out which is a non-vault address;
35 | // Then fall back to the first input
36 | func ExtractCommonRecipientAddr(tx *wire.MsgTx, prevTx *wire.MsgTx, vaults []*Vault, chainCfg *chaincfg.Params) (btcutil.Address, error) {
37 | var recipient btcutil.Address
38 |
39 | nonVaultOutCount := 0
40 |
41 | // extract from the tx out which is a non-vault address
42 | for _, out := range tx.TxOut {
43 | pkScript, err := txscript.ParsePkScript(out.PkScript)
44 | if err != nil {
45 | return nil, err
46 | }
47 |
48 | addr, err := pkScript.Address(chainCfg)
49 | if err != nil {
50 | return nil, err
51 | }
52 |
53 | vault := SelectVaultByAddress(vaults, addr.EncodeAddress())
54 | if vault == nil {
55 | recipient = addr
56 | nonVaultOutCount++
57 | }
58 | }
59 |
60 | // exceed allowed non vault out number
61 | if nonVaultOutCount > MaxNonVaultOutNum {
62 | return nil, ErrInvalidDepositTransaction
63 | }
64 |
65 | if recipient != nil {
66 | return recipient, nil
67 | }
68 |
69 | // fall back to extract from the first input
70 | pkScript, err := txscript.ParsePkScript(prevTx.TxOut[tx.TxIn[0].PreviousOutPoint.Index].PkScript)
71 | if err != nil {
72 | return nil, err
73 | }
74 |
75 | return pkScript.Address(chainCfg)
76 | }
77 |
78 | // ExtractRunesRecipientAddr extracts the recipient address for minting runes voucher token.
79 | // First, extract the recipient from the tx out which is a non-vault and non-OP_RETURN output;
80 | // Then fall back to the first input
81 | func ExtractRunesRecipientAddr(tx *wire.MsgTx, prevTx *wire.MsgTx, vaults []*Vault, chainCfg *chaincfg.Params) (btcutil.Address, error) {
82 | var recipient btcutil.Address
83 |
84 | nonVaultOutCount := 0
85 |
86 | // extract from the tx out which is a non-vault and non-OP_RETURN output
87 | for _, out := range tx.TxOut {
88 | if IsOpReturnOutput(out) {
89 | nonVaultOutCount++
90 | continue
91 | }
92 |
93 | pkScript, err := txscript.ParsePkScript(out.PkScript)
94 | if err != nil {
95 | return nil, err
96 | }
97 |
98 | addr, err := pkScript.Address(chainCfg)
99 | if err != nil {
100 | return nil, err
101 | }
102 |
103 | vault := SelectVaultByAddress(vaults, addr.EncodeAddress())
104 | if vault == nil {
105 | recipient = addr
106 | nonVaultOutCount++
107 | }
108 | }
109 |
110 | // exceed allowed non vault out number
111 | if nonVaultOutCount > RunesMaxNonVaultOutNum {
112 | return nil, ErrInvalidDepositTransaction
113 | }
114 |
115 | if recipient != nil {
116 | return recipient, nil
117 | }
118 |
119 | // fall back to extract from the first input
120 | pkScript, err := txscript.ParsePkScript(prevTx.TxOut[tx.TxIn[0].PreviousOutPoint.Index].PkScript)
121 | if err != nil {
122 | return nil, err
123 | }
124 |
125 | return pkScript.Address(chainCfg)
126 | }
127 |
128 | // CheckRunesDepositTransaction checks if the given tx is valid runes deposit tx
129 | func CheckRunesDepositTransaction(tx *wire.MsgTx, vaults []*Vault) (*Edict, error) {
130 | edicts, err := ParseRunes(tx)
131 | if err != nil {
132 | return nil, ErrInvalidDepositTransaction
133 | }
134 |
135 | if len(edicts) == 0 {
136 | return nil, nil
137 | }
138 |
139 | if len(edicts) != RunesEdictNum {
140 | return nil, ErrInvalidDepositTransaction
141 | }
142 |
143 | // even split is not supported
144 | if edicts[0].Output == uint32(len(tx.TxOut)) {
145 | return nil, ErrInvalidDepositTransaction
146 | }
147 |
148 | vault := SelectVaultByPkScript(vaults, tx.TxOut[edicts[0].Output].PkScript)
149 | if vault == nil || vault.AssetType != AssetType_ASSET_TYPE_RUNES {
150 | return nil, ErrInvalidDepositTransaction
151 | }
152 |
153 | return edicts[0], nil
154 | }
155 |
--------------------------------------------------------------------------------
/x/btcbridge/keeper/consolidate.go:
--------------------------------------------------------------------------------
1 | package keeper
2 |
3 | import (
4 | "fmt"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 |
8 | "github.com/sideprotocol/side/x/btcbridge/types"
9 | )
10 |
11 | // ConsolidateVaults performs the UTXO consolidation for the given vaults
12 | func (k Keeper) ConsolidateVaults(ctx sdk.Context, vaultVersion uint64, btcConsolidation *types.BtcConsolidation, runesConsolidations []*types.RunesConsolidation) error {
13 | feeRate := k.GetFeeRate(ctx)
14 | if err := k.CheckFeeRate(ctx, feeRate); err != nil {
15 | return err
16 | }
17 |
18 | if btcConsolidation != nil {
19 | if err := k.handleBtcConsolidation(ctx, vaultVersion, btcConsolidation.TargetThreshold, btcConsolidation.MaxNum, feeRate.Value); err != nil {
20 | return err
21 | }
22 | }
23 |
24 | for _, rc := range runesConsolidations {
25 | if err := k.handleRunesConsolidation(ctx, vaultVersion, rc.RuneId, rc.TargetThreshold, rc.MaxNum, feeRate.Value); err != nil {
26 | return err
27 | }
28 | }
29 |
30 | return nil
31 | }
32 |
33 | // handleBtcConsolidation handles the given btc consolidation
34 | func (k Keeper) handleBtcConsolidation(ctx sdk.Context, vaultVersion uint64, targetThreshold int64, maxNum uint32, feeRate int64) error {
35 | vault := k.GetVaultByAssetTypeAndVersion(ctx, types.AssetType_ASSET_TYPE_BTC, vaultVersion)
36 | if vault == nil {
37 | return types.ErrVaultDoesNotExist
38 | }
39 |
40 | maxUtxoNum := uint32(k.GetMaxUtxoNum(ctx))
41 | if maxNum > maxUtxoNum {
42 | maxNum = maxUtxoNum
43 | }
44 |
45 | targetUTXOs := k.GetUnlockedUTXOsByAddrAndThreshold(ctx, vault.Address, targetThreshold, maxNum)
46 | if len(targetUTXOs) == 0 {
47 | return types.ErrInsufficientUTXOs
48 | }
49 |
50 | p, recipientUTXO, err := types.BuildTransferAllBtcPsbt(targetUTXOs, vault.Address, feeRate)
51 | if err != nil {
52 | return err
53 | }
54 |
55 | psbtB64, err := p.B64Encode()
56 | if err != nil {
57 | return types.ErrFailToSerializePsbt
58 | }
59 |
60 | txHash := p.UnsignedTx.TxHash().String()
61 |
62 | // spend the involved utxos
63 | _ = k.SpendUTXOs(ctx, targetUTXOs)
64 |
65 | // lock the recipient(change) utxo
66 | k.lockChangeUTXOs(ctx, txHash, recipientUTXO)
67 |
68 | // set signing request
69 | signingReq := &types.SigningRequest{
70 | Address: k.authority,
71 | Sequence: k.IncrementSigningRequestSequence(ctx),
72 | Type: types.AssetType_ASSET_TYPE_BTC,
73 | Txid: txHash,
74 | Psbt: psbtB64,
75 | CreationTime: ctx.BlockTime(),
76 | Status: types.SigningStatus_SIGNING_STATUS_PENDING,
77 | }
78 | k.SetSigningRequest(ctx, signingReq)
79 |
80 | // Emit events
81 | k.EmitEvent(ctx, k.authority,
82 | sdk.NewAttribute("sequence", fmt.Sprintf("%d", signingReq.Sequence)),
83 | sdk.NewAttribute("txid", signingReq.Txid),
84 | )
85 |
86 | return nil
87 | }
88 |
89 | // handleRunesConsolidation handles the given runes consolidation
90 | func (k Keeper) handleRunesConsolidation(ctx sdk.Context, vaultVersion uint64, runeId string, targetThreshold string, maxNum uint32, feeRate int64) error {
91 | vault := k.GetVaultByAssetTypeAndVersion(ctx, types.AssetType_ASSET_TYPE_RUNES, vaultVersion)
92 | if vault == nil {
93 | return types.ErrVaultDoesNotExist
94 | }
95 |
96 | btcVault := k.GetVaultByAssetTypeAndVersion(ctx, types.AssetType_ASSET_TYPE_BTC, vaultVersion)
97 | if btcVault == nil {
98 | return types.ErrVaultDoesNotExist
99 | }
100 |
101 | maxUtxoNum := uint32(k.GetMaxUtxoNum(ctx))
102 | if maxNum > maxUtxoNum {
103 | maxNum = maxUtxoNum
104 | }
105 |
106 | targetRunesUTXOs, runeBalances := k.GetTargetRunesUTXOsByAddrAndThreshold(ctx, vault.Address, runeId, types.RuneAmountFromString(targetThreshold), maxNum)
107 | if len(targetRunesUTXOs) == 0 {
108 | return types.ErrInsufficientUTXOs
109 | }
110 |
111 | btcUtxoIterator := k.GetUTXOIteratorByAddr(ctx, btcVault.Address)
112 |
113 | p, selectedUtxos, changeUtxo, runesRecipientUtxo, err := types.BuildTransferAllRunesPsbt(targetRunesUTXOs, btcUtxoIterator, vault.Address, runeBalances, feeRate, btcVault.Address, k.GetMaxUtxoNum(ctx))
114 | if err != nil {
115 | return err
116 | }
117 |
118 | psbtB64, err := p.B64Encode()
119 | if err != nil {
120 | return types.ErrFailToSerializePsbt
121 | }
122 |
123 | txHash := p.UnsignedTx.TxHash().String()
124 |
125 | // spend the involved utxos
126 | _ = k.SpendUTXOs(ctx, targetRunesUTXOs)
127 | _ = k.SpendUTXOs(ctx, selectedUtxos)
128 |
129 | // lock the change utxos
130 | k.lockChangeUTXOs(ctx, txHash, changeUtxo, runesRecipientUtxo)
131 |
132 | // set signing request
133 | signingReq := &types.SigningRequest{
134 | Address: k.authority,
135 | Sequence: k.IncrementSigningRequestSequence(ctx),
136 | Type: types.AssetType_ASSET_TYPE_RUNES,
137 | Txid: txHash,
138 | Psbt: psbtB64,
139 | CreationTime: ctx.BlockTime(),
140 | Status: types.SigningStatus_SIGNING_STATUS_PENDING,
141 | }
142 | k.SetSigningRequest(ctx, signingReq)
143 |
144 | // Emit events
145 | k.EmitEvent(ctx, k.authority,
146 | sdk.NewAttribute("sequence", fmt.Sprintf("%d", signingReq.Sequence)),
147 | sdk.NewAttribute("txid", signingReq.Txid),
148 | )
149 |
150 | return nil
151 | }
152 |
--------------------------------------------------------------------------------
/x/btcbridge/types/keys.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | sdk "github.com/cosmos/cosmos-sdk/types"
5 | )
6 |
7 | const (
8 | // ModuleName defines the module name
9 | ModuleName = "btcbridge"
10 |
11 | // StoreKey defines the primary module store key
12 | StoreKey = ModuleName
13 |
14 | // RouterKey defines the module's message routing key
15 | RouterKey = ModuleName
16 |
17 | // MemStoreKey defines the in-memory store key
18 | MemStoreKey = "mem_btcbridge"
19 | )
20 |
21 | var (
22 | ParamsStoreKey = []byte{0x1}
23 |
24 | BtcBlockHeaderHashPrefix = []byte{0x10} // prefix for each key to a block header, for a hash
25 | BtcBlockHeaderHeightPrefix = []byte{0x11} // prefix for each key to a block hash, for a height
26 | BtcBestBlockHeaderKey = []byte{0x12} // key for the best block height
27 | BtcFeeRateKey = []byte{0x13} // key for the bitcoin network fee rate
28 |
29 | BtcWithdrawRequestSequenceKey = []byte{0x20} // key for the withdrawal request sequence
30 | BtcWithdrawRequestKeyPrefix = []byte{0x21} // prefix for each key to a withdrawal request
31 | BtcWithdrawRequestByTxHashKeyPrefix = []byte{0x22} // prefix for each key to a withdrawal request by tx hash
32 | BtcWithdrawRequestQueueKeyPrefix = []byte{0x23} // prefix for each key to a pending btc withdrawal request
33 | BtcSigningRequestSequenceKey = []byte{0x24} // key for the signing request sequence
34 | BtcSigningRequestPrefix = []byte{0x25} // prefix for each key to a signing request
35 | BtcSigningRequestByTxHashPrefix = []byte{0x26} // prefix for each key to a signing request from tx hash
36 | BtcSigningRequestByStatusKeyPrefix = []byte{0x27} // prefix for each key to a signing request by status
37 | BtcMintedTxHashKeyPrefix = []byte{0x28} // prefix for each key to a minted tx hash
38 |
39 | BtcUtxoKeyPrefix = []byte{0x30} // prefix for each key to a utxo
40 | BtcOwnerUtxoKeyPrefix = []byte{0x31} // prefix for each key to an owned utxo
41 | BtcOwnerUtxoByAmountKeyPrefix = []byte{0x32} // prefix for each key to an owned utxo by amount
42 | BtcOwnerRunesUtxoKeyPrefix = []byte{0x33} // prefix for each key to an owned runes utxo
43 |
44 | DKGRequestIDKey = []byte{0x40} // key for the DKG request id
45 | DKGRequestKeyPrefix = []byte{0x41} // prefix for each key to a DKG request
46 | DKGCompletionRequestKeyPrefix = []byte{0x42} // prefix for each key to a DKG completion request
47 | VaultVersionKey = []byte{0x43} // key for vault version increased by 1 once updated
48 | )
49 |
50 | func BtcBlockHeaderHashKey(hash string) []byte {
51 | return append(BtcBlockHeaderHashPrefix, []byte(hash)...)
52 | }
53 |
54 | func BtcBlockHeaderHeightKey(height uint64) []byte {
55 | return append(BtcBlockHeaderHeightPrefix, sdk.Uint64ToBigEndian(height)...)
56 | }
57 |
58 | func BtcWithdrawRequestKey(sequence uint64) []byte {
59 | return append(BtcWithdrawRequestKeyPrefix, sdk.Uint64ToBigEndian(sequence)...)
60 | }
61 |
62 | func BtcWithdrawRequestByTxHashKey(txid string, sequence uint64) []byte {
63 | return append(append(BtcWithdrawRequestByTxHashKeyPrefix, []byte(txid)...), sdk.Uint64ToBigEndian(sequence)...)
64 | }
65 |
66 | func BtcWithdrawRequestQueueKey(sequence uint64) []byte {
67 | return append(BtcWithdrawRequestQueueKeyPrefix, sdk.Uint64ToBigEndian(sequence)...)
68 | }
69 |
70 | func BtcSigningRequestKey(sequence uint64) []byte {
71 | return append(BtcSigningRequestPrefix, sdk.Uint64ToBigEndian(sequence)...)
72 | }
73 |
74 | func BtcSigningRequestByTxHashKey(txid string) []byte {
75 | return append(BtcSigningRequestByTxHashPrefix, []byte(txid)...)
76 | }
77 |
78 | func BtcSigningRequestByStatusKey(status SigningStatus, sequence uint64) []byte {
79 | key := append(BtcSigningRequestByStatusKeyPrefix, sdk.Uint64ToBigEndian(uint64(status))...)
80 |
81 | return append(key, sdk.Uint64ToBigEndian(sequence)...)
82 | }
83 |
84 | func BtcMintedTxHashKey(hash string) []byte {
85 | return append(BtcMintedTxHashKeyPrefix, []byte(hash)...)
86 | }
87 |
88 | func BtcUtxoKey(hash string, vout uint64) []byte {
89 | return append(append(BtcUtxoKeyPrefix, []byte(hash)...), sdk.Uint64ToBigEndian(vout)...)
90 | }
91 |
92 | func BtcOwnerUtxoKey(owner string, hash string, vout uint64) []byte {
93 | key := append(append(BtcOwnerUtxoKeyPrefix, []byte(owner)...), []byte(hash)...)
94 | key = append(key, sdk.Uint64ToBigEndian(vout)...)
95 |
96 | return key
97 | }
98 |
99 | func BtcOwnerUtxoByAmountKey(owner string, amount uint64, hash string, vout uint64) []byte {
100 | key := append(append(BtcOwnerUtxoByAmountKeyPrefix, []byte(owner)...), sdk.Uint64ToBigEndian(amount)...)
101 | key = append(key, []byte(hash)...)
102 | key = append(key, sdk.Uint64ToBigEndian(vout)...)
103 |
104 | return key
105 | }
106 |
107 | func BtcOwnerRunesUtxoKey(owner string, id string, amount string, hash string, vout uint64) []byte {
108 | key := append(append(BtcOwnerRunesUtxoKeyPrefix, []byte(owner)...), MarshalRuneIdFromString(id)...)
109 | key = append(key, MarshalRuneAmountFromString(amount)...)
110 | key = append(key, []byte(hash)...)
111 | key = append(key, sdk.Uint64ToBigEndian(vout)...)
112 |
113 | return key
114 | }
115 |
116 | func DKGRequestKey(id uint64) []byte {
117 | return append(DKGRequestKeyPrefix, sdk.Uint64ToBigEndian(id)...)
118 | }
119 |
120 | func DKGCompletionRequestKey(id uint64, consAddress string) []byte {
121 | return append(append(DKGCompletionRequestKeyPrefix, sdk.Uint64ToBigEndian(id)...), []byte(consAddress)...)
122 | }
123 |
--------------------------------------------------------------------------------
/x/btcbridge/module/abci.go:
--------------------------------------------------------------------------------
1 | package btcbridge
2 |
3 | import (
4 | "fmt"
5 |
6 | sdk "github.com/cosmos/cosmos-sdk/types"
7 |
8 | "github.com/sideprotocol/side/x/btcbridge/keeper"
9 | "github.com/sideprotocol/side/x/btcbridge/types"
10 | )
11 |
12 | // EndBlocker called at every block
13 | func EndBlocker(ctx sdk.Context, k keeper.Keeper) {
14 | handleBtcWithdrawRequests(ctx, k)
15 | handleDKGRequests(ctx, k)
16 | handleVaultTransfer(ctx, k)
17 | }
18 |
19 | // handleBtcWithdrawRequests performs the batch btc withdrawal request handling
20 | func handleBtcWithdrawRequests(ctx sdk.Context, k keeper.Keeper) {
21 | p := k.GetParams(ctx)
22 |
23 | // check if withdrawal is enabled
24 | if !p.WithdrawEnabled {
25 | return
26 | }
27 |
28 | // check block height
29 | if ctx.BlockHeight()%p.WithdrawParams.BtcBatchWithdrawPeriod != 0 {
30 | return
31 | }
32 |
33 | // get the pending btc withdrawal request
34 | pendingWithdrawRequests := k.GetPendingBtcWithdrawRequests(ctx, p.WithdrawParams.MaxBtcBatchWithdrawNum)
35 | if len(pendingWithdrawRequests) == 0 {
36 | return
37 | }
38 |
39 | feeRate := k.GetFeeRate(ctx)
40 | if err := k.CheckFeeRate(ctx, feeRate); err != nil {
41 | k.Logger(ctx).Info("invalid fee rate", "value", feeRate.Value, "height", feeRate.Height)
42 | return
43 | }
44 |
45 | vault := types.SelectVaultByAssetType(p.Vaults, types.AssetType_ASSET_TYPE_BTC)
46 | if vault == nil {
47 | k.Logger(ctx).Info("btc vault does not exist")
48 | return
49 | }
50 |
51 | signingRequest, err := k.BuildBtcBatchWithdrawSigningRequest(ctx, pendingWithdrawRequests, feeRate.Value, vault.Address)
52 | if err != nil {
53 | k.Logger(ctx).Info("failed to build signing request", "err", err)
54 | return
55 | }
56 |
57 | for _, req := range pendingWithdrawRequests {
58 | // update withdrawal request
59 | req.Txid = signingRequest.Txid
60 | k.SetWithdrawRequest(ctx, req)
61 |
62 | // remove from the pending queue
63 | k.RemoveFromBtcWithdrawRequestQueue(ctx, req)
64 |
65 | // emit event
66 | k.EmitEvent(ctx, req.Address,
67 | sdk.NewAttribute("sequence", fmt.Sprintf("%d", req.Sequence)),
68 | sdk.NewAttribute("txid", req.Txid),
69 | )
70 | }
71 | }
72 |
73 | // handleDKGRequests performs the DKG request handling
74 | func handleDKGRequests(ctx sdk.Context, k keeper.Keeper) {
75 | pendingDKGRequests := k.GetPendingDKGRequests(ctx)
76 |
77 | for _, req := range pendingDKGRequests {
78 | // check if the DKG request expired
79 | if !ctx.BlockTime().Before(*req.Expiration) {
80 | req.Status = types.DKGRequestStatus_DKG_REQUEST_STATUS_TIMEDOUT
81 | k.SetDKGRequest(ctx, req)
82 |
83 | continue
84 | }
85 |
86 | // handle DKG completion requests
87 | completionRequests := k.GetDKGCompletionRequests(ctx, req.Id)
88 | if len(completionRequests) != len(req.Participants) {
89 | continue
90 | }
91 |
92 | // check if the DKG completion requests are valid
93 | if !types.CheckDKGCompletionRequests(completionRequests) {
94 | req.Status = types.DKGRequestStatus_DKG_REQUEST_STATUS_FAILED
95 | k.SetDKGRequest(ctx, req)
96 |
97 | continue
98 | }
99 |
100 | // update vaults
101 | k.UpdateVaults(ctx, completionRequests[0].Vaults, req.VaultTypes)
102 |
103 | // update status
104 | req.Status = types.DKGRequestStatus_DKG_REQUEST_STATUS_COMPLETED
105 | k.SetDKGRequest(ctx, req)
106 | }
107 | }
108 |
109 | // handleVaultTransfer performs the vault asset transfer
110 | func handleVaultTransfer(ctx sdk.Context, k keeper.Keeper) {
111 | completedDKGRequests := k.GetDKGRequests(ctx, types.DKGRequestStatus_DKG_REQUEST_STATUS_COMPLETED)
112 |
113 | for _, req := range completedDKGRequests {
114 | if req.EnableTransfer {
115 | completions := k.GetDKGCompletionRequests(ctx, req.Id)
116 | dkgVaultVersion, _ := k.GetVaultVersionByAddress(ctx, completions[0].Vaults[0])
117 |
118 | sourceVersion := dkgVaultVersion - 1
119 | destVersion := k.GetLatestVaultVersion(ctx)
120 |
121 | if k.VaultsTransferCompleted(ctx, sourceVersion) {
122 | continue
123 | }
124 |
125 | sourceBtcVault := k.GetVaultByAssetTypeAndVersion(ctx, types.AssetType_ASSET_TYPE_BTC, sourceVersion).Address
126 | sourceRunesVault := k.GetVaultByAssetTypeAndVersion(ctx, types.AssetType_ASSET_TYPE_RUNES, sourceVersion).Address
127 |
128 | // transfer runes
129 | if !k.VaultTransferCompleted(ctx, sourceRunesVault) {
130 | if err := k.TransferVault(ctx, sourceVersion, destVersion, types.AssetType_ASSET_TYPE_RUNES, nil, req.TargetUtxoNum); err != nil {
131 | k.Logger(ctx).Info("failed to transfer vault", "source version", sourceVersion, "destination version", destVersion, "asset type", types.AssetType_ASSET_TYPE_RUNES, "target utxo num", req.TargetUtxoNum, "err", err)
132 | continue
133 | }
134 | }
135 |
136 | // transfer btc only when runes transfer completed
137 | if k.VaultTransferCompleted(ctx, sourceRunesVault) && !k.VaultTransferCompleted(ctx, sourceBtcVault) {
138 | if err := k.TransferVault(ctx, sourceVersion, destVersion, types.AssetType_ASSET_TYPE_BTC, nil, req.TargetUtxoNum); err != nil {
139 | k.Logger(ctx).Info("failed to transfer vault", "source version", sourceVersion, "destination version", destVersion, "asset type", types.AssetType_ASSET_TYPE_BTC, "target utxo num", req.TargetUtxoNum, "err", err)
140 | continue
141 | }
142 | }
143 |
144 | if k.VaultsTransferCompleted(ctx, sourceVersion) {
145 | k.Logger(ctx).Info("vaults transfer completed", "source version", sourceVersion, "destination version", destVersion)
146 | }
147 | }
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/proto/side/btcbridge/btcbridge.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package side.btcbridge;
3 |
4 | import "gogoproto/gogo.proto";
5 | import "google/protobuf/any.proto";
6 | import "google/protobuf/timestamp.proto";
7 | import "cosmos/base/v1beta1/coin.proto";
8 | import "cosmos_proto/cosmos.proto";
9 | import "side/btcbridge/params.proto";
10 |
11 | option go_package = "github.com/sideprotocol/side/x/btcbridge/types";
12 |
13 | // Bitcoin Block Header
14 | message BlockHeader {
15 | uint64 version = 1;
16 | string hash = 2;
17 | uint64 height = 3;
18 | string previous_block_hash = 4;
19 | string merkle_root = 5;
20 | uint64 nonce = 6;
21 | string bits = 7;
22 | uint64 time = 8;
23 | uint64 ntx = 9;
24 | }
25 |
26 | // Fee rate
27 | message FeeRate {
28 | // fee rate
29 | int64 value = 1;
30 | // block height at which the fee rate is submitted
31 | int64 height = 2;
32 | }
33 |
34 | // Bitcoin Signing Status
35 | enum SigningStatus {
36 | // SIGNING_STATUS_UNSPECIFIED - Default value, should not be used
37 | SIGNING_STATUS_UNSPECIFIED = 0;
38 | // SIGNING_STATUS_PENDING - The signing request is pending
39 | SIGNING_STATUS_PENDING = 1;
40 | // SIGNING_STATUS_BROADCASTED - The signing request is broadcasted
41 | SIGNING_STATUS_BROADCASTED = 2;
42 | // SIGNING_STATUS_CONFIRMED - The signing request is confirmed
43 | SIGNING_STATUS_CONFIRMED = 3;
44 | // SIGNING_STATUS_FAILED - The signing request failed to be signed or broadcast due to unexpected exceptions
45 | SIGNING_STATUS_FAILED = 4;
46 | }
47 |
48 | // Bitcoin Signing Request
49 | message SigningRequest {
50 | string address = 1;
51 | uint64 sequence = 2;
52 | AssetType type = 3;
53 | string txid = 4;
54 | string psbt = 5;
55 | google.protobuf.Timestamp creation_time = 6 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
56 | SigningStatus status = 7;
57 | }
58 |
59 | // Withdrawal Request
60 | message WithdrawRequest {
61 | string address = 1;
62 | string amount = 2;
63 | uint64 sequence = 3;
64 | string txid = 4;
65 | }
66 |
67 | // Bitcoin UTXO
68 | message UTXO {
69 | string txid = 1;
70 | uint64 vout = 2;
71 | string address = 3;
72 | uint64 amount = 4;
73 | uint64 height = 5;
74 | bytes pub_key_script = 6;
75 | bool is_locked = 7;
76 | // rune balances associated with the UTXO
77 | repeated RuneBalance runes = 8;
78 | }
79 |
80 | // Rune Balance
81 | message RuneBalance {
82 | // serialized rune id
83 | string id = 1;
84 | // rune amount
85 | string amount = 2;
86 | }
87 |
88 | // Rune ID
89 | message RuneId {
90 | // block height
91 | uint64 block = 1;
92 | // tx index
93 | uint32 tx = 2;
94 | }
95 |
96 | // Rune Edict
97 | message Edict {
98 | RuneId id = 1;
99 | string amount = 2;
100 | uint32 output = 3;
101 | }
102 |
103 | // BTC UTXO Consolidation
104 | message BtcConsolidation {
105 | // maximum threshold of the btc value
106 | int64 target_threshold = 1;
107 | // maximum number of the utxos to be consolidated; 0 means all
108 | uint32 max_num = 2;
109 | }
110 |
111 | // Runes UTXO Consolidation
112 | message RunesConsolidation {
113 | // rune id
114 | string rune_id = 1;
115 | // maximum threshold of the corresponding rune balance
116 | string target_threshold = 2;
117 | // maximum number of the utxos to be consolidated; 0 means all
118 | uint32 max_num = 3;
119 | }
120 |
121 | // DKG Participant
122 | message DKGParticipant {
123 | // the moniker of the corresponding validator
124 | string moniker = 1;
125 | // the operator address of the corresponding validator
126 | string operator_address = 2;
127 | // the consensus public key of the corresponding validator
128 | string consensus_pubkey = 3;
129 | }
130 |
131 | enum DKGRequestStatus {
132 | // DKG_REQUEST_STATUS_UNSPECIFIED defines the unknown DKG request status
133 | DKG_REQUEST_STATUS_UNSPECIFIED = 0;
134 | // DKG_REQUEST_STATUS_PENDING defines the status of the DKG request which is pending
135 | DKG_REQUEST_STATUS_PENDING = 1;
136 | // DKG_REQUEST_STATUS_COMPLETED defines the status of the DKG request which is completed
137 | DKG_REQUEST_STATUS_COMPLETED = 2;
138 | // DKG_REQUEST_STATUS_FAILED defines the status of the DKG request which failed
139 | DKG_REQUEST_STATUS_FAILED = 3;
140 | // DKG_REQUEST_STATUS_TIMEDOUT defines the status of the DKG request which timed out
141 | DKG_REQUEST_STATUS_TIMEDOUT = 4;
142 | }
143 |
144 | // DKG Request
145 | message DKGRequest {
146 | // the unique request id
147 | uint64 id = 1;
148 | // participant set
149 | repeated DKGParticipant participants = 2;
150 | // threshold required to perform DKG
151 | uint32 threshold = 3;
152 | // asset types of vaults to be generated
153 | repeated AssetType vault_types = 4;
154 | // indicates if transferring assets to the newly generated vaults when the DKG request is completed
155 | bool enable_transfer = 5;
156 | // target number of the UTXOs to be transferred each time
157 | uint32 target_utxo_num = 6;
158 | // expiration time
159 | google.protobuf.Timestamp expiration = 7 [(gogoproto.stdtime) = true, (gogoproto.nullable) = true];
160 | // status
161 | DKGRequestStatus status = 8;
162 | }
163 |
164 | // DKG Completion Request
165 | message DKGCompletionRequest {
166 | // request id
167 | uint64 id = 1;
168 | // sender
169 | string sender = 2;
170 | // new vaults generated by DKG
171 | repeated string vaults = 3;
172 | // consensus address of the corresponding validator
173 | string consensus_address = 4;
174 | // hex encoded validator signature
175 | string signature = 5;
176 | }
177 |
--------------------------------------------------------------------------------
/cmd/sided/cmd/commands.go:
--------------------------------------------------------------------------------
1 | package cmd
2 |
3 | import (
4 | "errors"
5 | "io"
6 | "os"
7 |
8 | "github.com/spf13/cobra"
9 | "github.com/spf13/viper"
10 |
11 | "cosmossdk.io/log"
12 | confixcmd "cosmossdk.io/tools/confix/cmd"
13 | dbm "github.com/cosmos/cosmos-db"
14 | "github.com/cosmos/cosmos-sdk/client"
15 | "github.com/cosmos/cosmos-sdk/client/debug"
16 | "github.com/cosmos/cosmos-sdk/client/flags"
17 | "github.com/cosmos/cosmos-sdk/client/keys"
18 | "github.com/cosmos/cosmos-sdk/client/pruning"
19 | "github.com/cosmos/cosmos-sdk/client/rpc"
20 | "github.com/cosmos/cosmos-sdk/client/snapshot"
21 | "github.com/cosmos/cosmos-sdk/server"
22 | servertypes "github.com/cosmos/cosmos-sdk/server/types"
23 | "github.com/cosmos/cosmos-sdk/types/module"
24 | authcmd "github.com/cosmos/cosmos-sdk/x/auth/client/cli"
25 | "github.com/cosmos/cosmos-sdk/x/crisis"
26 | genutilcli "github.com/cosmos/cosmos-sdk/x/genutil/client/cli"
27 |
28 | "github.com/sideprotocol/side/app"
29 | )
30 |
31 | func initRootCmd(
32 | rootCmd *cobra.Command,
33 | txConfig client.TxConfig,
34 | basicManager module.BasicManager,
35 | ) {
36 | rootCmd.AddCommand(
37 | genutilcli.InitCmd(basicManager, app.DefaultNodeHome),
38 | NewInPlaceTestnetCmd(addModuleInitFlags),
39 | debug.Cmd(),
40 | confixcmd.ConfigCommand(),
41 | pruning.Cmd(newApp, app.DefaultNodeHome),
42 | snapshot.Cmd(newApp),
43 | )
44 |
45 | server.AddCommands(rootCmd, app.DefaultNodeHome, newApp, appExport, addModuleInitFlags)
46 |
47 | // add keybase, auxiliary RPC, query, genesis, and tx child commands
48 | rootCmd.AddCommand(
49 | server.StatusCommand(),
50 | genesisCommand(txConfig, basicManager),
51 | queryCommand(),
52 | txCommand(),
53 | keys.Commands(),
54 | )
55 | }
56 |
57 | func addModuleInitFlags(startCmd *cobra.Command) {
58 | crisis.AddModuleInitFlags(startCmd)
59 | }
60 |
61 | // genesisCommand builds genesis-related `sided genesis` command. Users may provide application specific commands as a parameter
62 | func genesisCommand(txConfig client.TxConfig, basicManager module.BasicManager, cmds ...*cobra.Command) *cobra.Command {
63 | cmd := genutilcli.Commands(txConfig, basicManager, app.DefaultNodeHome)
64 |
65 | for _, subCmd := range cmds {
66 | cmd.AddCommand(subCmd)
67 | }
68 | return cmd
69 | }
70 |
71 | func queryCommand() *cobra.Command {
72 | cmd := &cobra.Command{
73 | Use: "query",
74 | Aliases: []string{"q"},
75 | Short: "Querying subcommands",
76 | DisableFlagParsing: false,
77 | SuggestionsMinimumDistance: 2,
78 | RunE: client.ValidateCmd,
79 | }
80 |
81 | cmd.AddCommand(
82 | rpc.QueryEventForTxCmd(),
83 | rpc.ValidatorCommand(),
84 | server.QueryBlockCmd(),
85 | authcmd.QueryTxsByEventsCmd(),
86 | server.QueryBlocksCmd(),
87 | authcmd.QueryTxCmd(),
88 | server.QueryBlockResultsCmd(),
89 | )
90 | cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID")
91 |
92 | return cmd
93 | }
94 |
95 | func txCommand() *cobra.Command {
96 | cmd := &cobra.Command{
97 | Use: "tx",
98 | Short: "Transactions subcommands",
99 | DisableFlagParsing: false,
100 | SuggestionsMinimumDistance: 2,
101 | RunE: client.ValidateCmd,
102 | }
103 |
104 | cmd.AddCommand(
105 | authcmd.GetSignCommand(),
106 | authcmd.GetSignBatchCommand(),
107 | authcmd.GetMultiSignCommand(),
108 | authcmd.GetMultiSignBatchCmd(),
109 | authcmd.GetValidateSignaturesCommand(),
110 | flags.LineBreak,
111 | authcmd.GetBroadcastCommand(),
112 | authcmd.GetEncodeCommand(),
113 | authcmd.GetDecodeCommand(),
114 | authcmd.GetSimulateCmd(),
115 | )
116 | cmd.PersistentFlags().String(flags.FlagChainID, "", "The network chain ID")
117 |
118 | return cmd
119 | }
120 |
121 | // newApp creates the application
122 | func newApp(
123 | logger log.Logger,
124 | db dbm.DB,
125 | traceStore io.Writer,
126 | appOpts servertypes.AppOptions,
127 | ) servertypes.Application {
128 | baseappOptions := server.DefaultBaseappOptions(appOpts)
129 |
130 | app := app.New(
131 | logger, db, traceStore, true,
132 | appOpts,
133 | baseappOptions...,
134 | )
135 |
136 | return app
137 | }
138 |
139 | // appExport creates a new app (optionally at a given height) and exports state.
140 | func appExport(
141 | logger log.Logger,
142 | db dbm.DB,
143 | traceStore io.Writer,
144 | height int64,
145 | forZeroHeight bool,
146 | jailAllowedAddrs []string,
147 | appOpts servertypes.AppOptions,
148 | modulesToExport []string,
149 | ) (servertypes.ExportedApp, error) {
150 | var bApp *app.App
151 |
152 | // this check is necessary as we use the flag in x/upgrade.
153 | // we can exit more gracefully by checking the flag here.
154 | homePath, ok := appOpts.Get(flags.FlagHome).(string)
155 | if !ok || homePath == "" {
156 | return servertypes.ExportedApp{}, errors.New("application home not set")
157 | }
158 |
159 | viperAppOpts, ok := appOpts.(*viper.Viper)
160 | if !ok {
161 | return servertypes.ExportedApp{}, errors.New("appOpts is not viper.Viper")
162 | }
163 |
164 | // overwrite the FlagInvCheckPeriod
165 | viperAppOpts.Set(server.FlagInvCheckPeriod, 1)
166 | appOpts = viperAppOpts
167 |
168 | if height != -1 {
169 | bApp = app.New(logger, db, traceStore, false, appOpts)
170 |
171 | if err := bApp.LoadHeight(height); err != nil {
172 | return servertypes.ExportedApp{}, err
173 | }
174 | } else {
175 | bApp = app.New(logger, db, traceStore, true, appOpts)
176 | }
177 |
178 | return bApp.ExportAppStateAndValidators(forZeroHeight, jailAllowedAddrs, modulesToExport)
179 | }
180 |
181 | var tempDir = func() string {
182 | dir, err := os.MkdirTemp("", "wasmd")
183 | if err != nil {
184 | panic("failed to create temp dir: " + err.Error())
185 | }
186 | defer os.RemoveAll(dir)
187 |
188 | return dir
189 | }
190 |
--------------------------------------------------------------------------------
/x/incentive/module/module.go:
--------------------------------------------------------------------------------
1 | package incentive
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 |
8 | // this line is used by starport scaffolding # 1
9 |
10 | "github.com/grpc-ecosystem/grpc-gateway/runtime"
11 | "github.com/spf13/cobra"
12 |
13 | abci "github.com/cometbft/cometbft/abci/types"
14 |
15 | "github.com/cosmos/cosmos-sdk/client"
16 | "github.com/cosmos/cosmos-sdk/codec"
17 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
18 | sdk "github.com/cosmos/cosmos-sdk/types"
19 | "github.com/cosmos/cosmos-sdk/types/module"
20 |
21 | "github.com/sideprotocol/side/x/incentive/client/cli"
22 | "github.com/sideprotocol/side/x/incentive/keeper"
23 | "github.com/sideprotocol/side/x/incentive/types"
24 | )
25 |
26 | var (
27 | _ module.AppModule = AppModule{}
28 | _ module.AppModuleBasic = AppModuleBasic{}
29 | )
30 |
31 | // ----------------------------------------------------------------------------
32 | // AppModuleBasic
33 | // ----------------------------------------------------------------------------
34 |
35 | // AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement.
36 | type AppModuleBasic struct {
37 | cdc codec.BinaryCodec
38 | }
39 |
40 | func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic {
41 | return AppModuleBasic{cdc: cdc}
42 | }
43 |
44 | // Name returns the name of the module as a string
45 | func (AppModuleBasic) Name() string {
46 | return types.ModuleName
47 | }
48 |
49 | // RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore
50 | func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
51 | types.RegisterCodec(cdc)
52 | }
53 |
54 | // RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message
55 | func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) {
56 | types.RegisterInterfaces(reg)
57 | }
58 |
59 | // DefaultGenesis returns a default GenesisState for the module, marshaled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing
60 | func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
61 | return cdc.MustMarshalJSON(types.DefaultGenesis())
62 | }
63 |
64 | // ValidateGenesis used to validate the GenesisState, given in its json.RawMessage form
65 | func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
66 | var genState types.GenesisState
67 | if err := cdc.UnmarshalJSON(bz, &genState); err != nil {
68 | return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
69 | }
70 | return genState.Validate()
71 | }
72 |
73 | // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module
74 | func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
75 | if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil {
76 | panic(err)
77 | }
78 | }
79 |
80 | // GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module
81 | func (AppModuleBasic) GetQueryCmd() *cobra.Command {
82 | return cli.GetQueryCmd(types.StoreKey)
83 | }
84 |
85 | // ----------------------------------------------------------------------------
86 | // AppModule
87 | // ----------------------------------------------------------------------------
88 |
89 | // AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement
90 | type AppModule struct {
91 | AppModuleBasic
92 |
93 | keeper keeper.Keeper
94 | }
95 |
96 | func NewAppModule(
97 | cdc codec.Codec,
98 | keeper keeper.Keeper,
99 | ) AppModule {
100 | return AppModule{
101 | AppModuleBasic: NewAppModuleBasic(cdc),
102 | keeper: keeper,
103 | }
104 | }
105 |
106 | // IsOnePerModuleType implements the depinject.OnePerModuleType interface.
107 | func (am AppModule) IsOnePerModuleType() {}
108 |
109 | // IsAppModule implements the appmodule.AppModule interface.
110 | func (am AppModule) IsAppModule() {}
111 |
112 | // RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries
113 | func (am AppModule) RegisterServices(cfg module.Configurator) {
114 | types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
115 | types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
116 | }
117 |
118 | // RegisterInvariants registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted)
119 | func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
120 |
121 | // InitGenesis performs the module's genesis initialization. It returns no validator updates.
122 | func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate {
123 | var genState types.GenesisState
124 | // Initialize global index to index in genesis state
125 | cdc.MustUnmarshalJSON(gs, &genState)
126 |
127 | InitGenesis(ctx, am.keeper, genState)
128 |
129 | return []abci.ValidatorUpdate{}
130 | }
131 |
132 | // ExportGenesis returns the module's exported genesis state as raw JSON bytes.
133 | func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
134 | genState := ExportGenesis(ctx, am.keeper)
135 | return cdc.MustMarshalJSON(genState)
136 | }
137 |
138 | // ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1
139 | func (AppModule) ConsensusVersion() uint64 { return 1 }
140 |
141 | // BeginBlock contains the logic that is automatically triggered at the beginning of each block
142 | func (am AppModule) BeginBlock(_ context.Context) error {
143 | return nil
144 | }
145 |
146 | // EndBlock contains the logic that is automatically triggered at the end of each block
147 | func (am AppModule) EndBlock(_ context.Context) error {
148 | return nil
149 | }
150 |
--------------------------------------------------------------------------------
/x/btcbridge/module/module.go:
--------------------------------------------------------------------------------
1 | package btcbridge
2 |
3 | import (
4 | "context"
5 | "encoding/json"
6 | "fmt"
7 |
8 | // this line is used by starport scaffolding # 1
9 |
10 | "github.com/grpc-ecosystem/grpc-gateway/runtime"
11 | "github.com/spf13/cobra"
12 |
13 | abci "github.com/cometbft/cometbft/abci/types"
14 |
15 | "github.com/cosmos/cosmos-sdk/client"
16 | "github.com/cosmos/cosmos-sdk/codec"
17 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
18 | sdk "github.com/cosmos/cosmos-sdk/types"
19 | "github.com/cosmos/cosmos-sdk/types/module"
20 |
21 | "github.com/sideprotocol/side/x/btcbridge/client/cli"
22 | "github.com/sideprotocol/side/x/btcbridge/keeper"
23 | "github.com/sideprotocol/side/x/btcbridge/types"
24 | )
25 |
26 | var (
27 | _ module.AppModule = AppModule{}
28 | _ module.AppModuleBasic = AppModuleBasic{}
29 | )
30 |
31 | // ----------------------------------------------------------------------------
32 | // AppModuleBasic
33 | // ----------------------------------------------------------------------------
34 |
35 | // AppModuleBasic implements the AppModuleBasic interface that defines the independent methods a Cosmos SDK module needs to implement.
36 | type AppModuleBasic struct {
37 | cdc codec.BinaryCodec
38 | }
39 |
40 | func NewAppModuleBasic(cdc codec.BinaryCodec) AppModuleBasic {
41 | return AppModuleBasic{cdc: cdc}
42 | }
43 |
44 | // Name returns the name of the module as a string
45 | func (AppModuleBasic) Name() string {
46 | return types.ModuleName
47 | }
48 |
49 | // RegisterLegacyAminoCodec registers the amino codec for the module, which is used to marshal and unmarshal structs to/from []byte in order to persist them in the module's KVStore
50 | func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
51 | types.RegisterCodec(cdc)
52 | }
53 |
54 | // RegisterInterfaces registers a module's interface types and their concrete implementations as proto.Message
55 | func (a AppModuleBasic) RegisterInterfaces(reg cdctypes.InterfaceRegistry) {
56 | types.RegisterInterfaces(reg)
57 | }
58 |
59 | // DefaultGenesis returns a default GenesisState for the module, marshaled to json.RawMessage. The default GenesisState need to be defined by the module developer and is primarily used for testing
60 | func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
61 | return cdc.MustMarshalJSON(types.DefaultGenesis())
62 | }
63 |
64 | // ValidateGenesis used to validate the GenesisState, given in its json.RawMessage form
65 | func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
66 | var genState types.GenesisState
67 | if err := cdc.UnmarshalJSON(bz, &genState); err != nil {
68 | return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
69 | }
70 | return genState.Validate()
71 | }
72 |
73 | // RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the module
74 | func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *runtime.ServeMux) {
75 | if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil {
76 | panic(err)
77 | }
78 | }
79 |
80 | // GetTxCmd returns the root Tx command for the module. The subcommands of this root command are used by end-users to generate new transactions containing messages defined in the module
81 | func (a AppModuleBasic) GetTxCmd() *cobra.Command {
82 | return cli.GetTxCmd()
83 | }
84 |
85 | // GetQueryCmd returns the root query command for the module. The subcommands of this root command are used by end-users to generate new queries to the subset of the state defined by the module
86 | func (AppModuleBasic) GetQueryCmd() *cobra.Command {
87 | return cli.GetQueryCmd(types.StoreKey)
88 | }
89 |
90 | // ----------------------------------------------------------------------------
91 | // AppModule
92 | // ----------------------------------------------------------------------------
93 |
94 | // AppModule implements the AppModule interface that defines the inter-dependent methods that modules need to implement
95 | type AppModule struct {
96 | AppModuleBasic
97 |
98 | keeper keeper.Keeper
99 | }
100 |
101 | func NewAppModule(
102 | cdc codec.Codec,
103 | keeper keeper.Keeper,
104 | ) AppModule {
105 | return AppModule{
106 | AppModuleBasic: NewAppModuleBasic(cdc),
107 | keeper: keeper,
108 | }
109 | }
110 |
111 | // IsOnePerModuleType implements the depinject.OnePerModuleType interface.
112 | func (am AppModule) IsOnePerModuleType() {}
113 |
114 | // IsAppModule implements the appmodule.AppModule interface.
115 | func (am AppModule) IsAppModule() {}
116 |
117 | // RegisterServices registers a gRPC query service to respond to the module-specific gRPC queries
118 | func (am AppModule) RegisterServices(cfg module.Configurator) {
119 | types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
120 | types.RegisterQueryServer(cfg.QueryServer(), am.keeper)
121 | }
122 |
123 | // RegisterInvariants registers the invariants of the module. If an invariant deviates from its predicted value, the InvariantRegistry triggers appropriate logic (most often the chain will be halted)
124 | func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {}
125 |
126 | // InitGenesis performs the module's genesis initialization. It returns no validator updates.
127 | func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, gs json.RawMessage) []abci.ValidatorUpdate {
128 | var genState types.GenesisState
129 | // Initialize global index to index in genesis state
130 | cdc.MustUnmarshalJSON(gs, &genState)
131 |
132 | InitGenesis(ctx, am.keeper, genState)
133 |
134 | return []abci.ValidatorUpdate{}
135 | }
136 |
137 | // ExportGenesis returns the module's exported genesis state as raw JSON bytes.
138 | func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
139 | genState := ExportGenesis(ctx, am.keeper)
140 | return cdc.MustMarshalJSON(genState)
141 | }
142 |
143 | // ConsensusVersion is a sequence number for state-breaking change of the module. It should be incremented on each consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should be set to 1
144 | func (AppModule) ConsensusVersion() uint64 { return 1 }
145 |
146 | // BeginBlock contains the logic that is automatically triggered at the beginning of each block
147 | func (am AppModule) BeginBlock(_ context.Context) error {
148 | return nil
149 | }
150 |
151 | // EndBlock contains the logic that is automatically triggered at the end of each block
152 | func (am AppModule) EndBlock(ctx context.Context) error {
153 | c := sdk.UnwrapSDKContext(ctx)
154 | EndBlocker(c, am.keeper)
155 |
156 | return nil
157 | }
158 |
--------------------------------------------------------------------------------
/api/side/incentive/query_grpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go-grpc. DO NOT EDIT.
2 | // versions:
3 | // - protoc-gen-go-grpc v1.3.0
4 | // - protoc (unknown)
5 | // source: side/incentive/query.proto
6 |
7 | package incentive
8 |
9 | import (
10 | context "context"
11 | grpc "google.golang.org/grpc"
12 | codes "google.golang.org/grpc/codes"
13 | status "google.golang.org/grpc/status"
14 | )
15 |
16 | // This is a compile-time assertion to ensure that this generated file
17 | // is compatible with the grpc package it is being compiled against.
18 | // Requires gRPC-Go v1.32.0 or later.
19 | const _ = grpc.SupportPackageIsVersion7
20 |
21 | const (
22 | Query_Params_FullMethodName = "/side.incentive.Query/Params"
23 | Query_Rewards_FullMethodName = "/side.incentive.Query/Rewards"
24 | Query_RewardStats_FullMethodName = "/side.incentive.Query/RewardStats"
25 | )
26 |
27 | // QueryClient is the client API for Query service.
28 | //
29 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
30 | type QueryClient interface {
31 | // Params queries the parameters of the module.
32 | Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error)
33 | // Rewards queries the rewards of the given address.
34 | Rewards(ctx context.Context, in *QueryRewardsRequest, opts ...grpc.CallOption) (*QueryRewardsResponse, error)
35 | // RewardStats queries total reward statistics.
36 | RewardStats(ctx context.Context, in *QueryRewardStatsRequest, opts ...grpc.CallOption) (*QueryRewardStatsResponse, error)
37 | }
38 |
39 | type queryClient struct {
40 | cc grpc.ClientConnInterface
41 | }
42 |
43 | func NewQueryClient(cc grpc.ClientConnInterface) QueryClient {
44 | return &queryClient{cc}
45 | }
46 |
47 | func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) {
48 | out := new(QueryParamsResponse)
49 | err := c.cc.Invoke(ctx, Query_Params_FullMethodName, in, out, opts...)
50 | if err != nil {
51 | return nil, err
52 | }
53 | return out, nil
54 | }
55 |
56 | func (c *queryClient) Rewards(ctx context.Context, in *QueryRewardsRequest, opts ...grpc.CallOption) (*QueryRewardsResponse, error) {
57 | out := new(QueryRewardsResponse)
58 | err := c.cc.Invoke(ctx, Query_Rewards_FullMethodName, in, out, opts...)
59 | if err != nil {
60 | return nil, err
61 | }
62 | return out, nil
63 | }
64 |
65 | func (c *queryClient) RewardStats(ctx context.Context, in *QueryRewardStatsRequest, opts ...grpc.CallOption) (*QueryRewardStatsResponse, error) {
66 | out := new(QueryRewardStatsResponse)
67 | err := c.cc.Invoke(ctx, Query_RewardStats_FullMethodName, in, out, opts...)
68 | if err != nil {
69 | return nil, err
70 | }
71 | return out, nil
72 | }
73 |
74 | // QueryServer is the server API for Query service.
75 | // All implementations must embed UnimplementedQueryServer
76 | // for forward compatibility
77 | type QueryServer interface {
78 | // Params queries the parameters of the module.
79 | Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error)
80 | // Rewards queries the rewards of the given address.
81 | Rewards(context.Context, *QueryRewardsRequest) (*QueryRewardsResponse, error)
82 | // RewardStats queries total reward statistics.
83 | RewardStats(context.Context, *QueryRewardStatsRequest) (*QueryRewardStatsResponse, error)
84 | mustEmbedUnimplementedQueryServer()
85 | }
86 |
87 | // UnimplementedQueryServer must be embedded to have forward compatible implementations.
88 | type UnimplementedQueryServer struct {
89 | }
90 |
91 | func (UnimplementedQueryServer) Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) {
92 | return nil, status.Errorf(codes.Unimplemented, "method Params not implemented")
93 | }
94 | func (UnimplementedQueryServer) Rewards(context.Context, *QueryRewardsRequest) (*QueryRewardsResponse, error) {
95 | return nil, status.Errorf(codes.Unimplemented, "method Rewards not implemented")
96 | }
97 | func (UnimplementedQueryServer) RewardStats(context.Context, *QueryRewardStatsRequest) (*QueryRewardStatsResponse, error) {
98 | return nil, status.Errorf(codes.Unimplemented, "method RewardStats not implemented")
99 | }
100 | func (UnimplementedQueryServer) mustEmbedUnimplementedQueryServer() {}
101 |
102 | // UnsafeQueryServer may be embedded to opt out of forward compatibility for this service.
103 | // Use of this interface is not recommended, as added methods to QueryServer will
104 | // result in compilation errors.
105 | type UnsafeQueryServer interface {
106 | mustEmbedUnimplementedQueryServer()
107 | }
108 |
109 | func RegisterQueryServer(s grpc.ServiceRegistrar, srv QueryServer) {
110 | s.RegisterService(&Query_ServiceDesc, srv)
111 | }
112 |
113 | func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
114 | in := new(QueryParamsRequest)
115 | if err := dec(in); err != nil {
116 | return nil, err
117 | }
118 | if interceptor == nil {
119 | return srv.(QueryServer).Params(ctx, in)
120 | }
121 | info := &grpc.UnaryServerInfo{
122 | Server: srv,
123 | FullMethod: Query_Params_FullMethodName,
124 | }
125 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
126 | return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest))
127 | }
128 | return interceptor(ctx, in, info, handler)
129 | }
130 |
131 | func _Query_Rewards_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
132 | in := new(QueryRewardsRequest)
133 | if err := dec(in); err != nil {
134 | return nil, err
135 | }
136 | if interceptor == nil {
137 | return srv.(QueryServer).Rewards(ctx, in)
138 | }
139 | info := &grpc.UnaryServerInfo{
140 | Server: srv,
141 | FullMethod: Query_Rewards_FullMethodName,
142 | }
143 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
144 | return srv.(QueryServer).Rewards(ctx, req.(*QueryRewardsRequest))
145 | }
146 | return interceptor(ctx, in, info, handler)
147 | }
148 |
149 | func _Query_RewardStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
150 | in := new(QueryRewardStatsRequest)
151 | if err := dec(in); err != nil {
152 | return nil, err
153 | }
154 | if interceptor == nil {
155 | return srv.(QueryServer).RewardStats(ctx, in)
156 | }
157 | info := &grpc.UnaryServerInfo{
158 | Server: srv,
159 | FullMethod: Query_RewardStats_FullMethodName,
160 | }
161 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
162 | return srv.(QueryServer).RewardStats(ctx, req.(*QueryRewardStatsRequest))
163 | }
164 | return interceptor(ctx, in, info, handler)
165 | }
166 |
167 | // Query_ServiceDesc is the grpc.ServiceDesc for Query service.
168 | // It's only intended for direct use with grpc.RegisterService,
169 | // and not to be introspected or modified (even as a copy)
170 | var Query_ServiceDesc = grpc.ServiceDesc{
171 | ServiceName: "side.incentive.Query",
172 | HandlerType: (*QueryServer)(nil),
173 | Methods: []grpc.MethodDesc{
174 | {
175 | MethodName: "Params",
176 | Handler: _Query_Params_Handler,
177 | },
178 | {
179 | MethodName: "Rewards",
180 | Handler: _Query_Rewards_Handler,
181 | },
182 | {
183 | MethodName: "RewardStats",
184 | Handler: _Query_RewardStats_Handler,
185 | },
186 | },
187 | Streams: []grpc.StreamDesc{},
188 | Metadata: "side/incentive/query.proto",
189 | }
190 |
--------------------------------------------------------------------------------
/app/export.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "log"
7 |
8 | storetypes "cosmossdk.io/store/types"
9 | cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
10 | servertypes "github.com/cosmos/cosmos-sdk/server/types"
11 | sdk "github.com/cosmos/cosmos-sdk/types"
12 | slashingtypes "github.com/cosmos/cosmos-sdk/x/slashing/types"
13 | "github.com/cosmos/cosmos-sdk/x/staking"
14 | stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types"
15 | )
16 |
17 | // ExportAppStateAndValidators exports the state of the application for a genesis
18 | // file.
19 | func (app *App) ExportAppStateAndValidators(
20 | forZeroHeight bool,
21 | jailAllowedAddrs []string,
22 | modulesToExport []string,
23 | ) (servertypes.ExportedApp, error) {
24 | // as if they could withdraw from the start of the next block
25 | ctx := app.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()})
26 |
27 | // We export at last height + 1, because that's the height at which
28 | // Tendermint will start InitChain.
29 | height := app.LastBlockHeight() + 1
30 | if forZeroHeight {
31 | height = 0
32 | app.prepForZeroHeightGenesis(ctx, jailAllowedAddrs)
33 | }
34 |
35 | genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport)
36 | if err != nil {
37 | return servertypes.ExportedApp{}, err
38 | }
39 |
40 | appState, err := json.MarshalIndent(genState, "", " ")
41 | if err != nil {
42 | return servertypes.ExportedApp{}, err
43 | }
44 |
45 | validators, err := staking.WriteValidators(ctx, app.StakingKeeper)
46 | return servertypes.ExportedApp{
47 | AppState: appState,
48 | Validators: validators,
49 | Height: height,
50 | ConsensusParams: app.BaseApp.GetConsensusParams(ctx),
51 | }, err
52 | }
53 |
54 | // prepForZeroHeightGenesis prepares for a fresh genesis
55 | //
56 | // NOTE zero height genesis is a temporary feature which will be deprecated
57 | // in favour of export at a block height
58 | func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []string) {
59 | applyAllowedAddrs := false
60 |
61 | // check if there is a allowed address list
62 | if len(jailAllowedAddrs) > 0 {
63 | applyAllowedAddrs = true
64 | }
65 |
66 | allowedAddrsMap := make(map[string]bool)
67 |
68 | for _, addr := range jailAllowedAddrs {
69 | _, err := sdk.ValAddressFromBech32(addr)
70 | if err != nil {
71 | log.Fatal(err)
72 | }
73 | allowedAddrsMap[addr] = true
74 | }
75 |
76 | /* Just to be safe, assert the invariants on current state. */
77 | app.CrisisKeeper.AssertInvariants(ctx)
78 |
79 | /* Handle fee distribution state. */
80 |
81 | // withdraw all validator commission
82 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
83 | valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
84 | if err != nil {
85 | panic(err)
86 | }
87 |
88 | _, _ = app.DistrKeeper.WithdrawValidatorCommission(ctx, valBz)
89 | return false
90 | })
91 |
92 | // withdraw all delegator rewards
93 | dels, err := app.StakingKeeper.GetAllDelegations(ctx)
94 | if err != nil {
95 | panic(err)
96 | }
97 | for _, delegation := range dels {
98 | valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress)
99 | if err != nil {
100 | panic(err)
101 | }
102 |
103 | delAddr := sdk.MustAccAddressFromBech32(delegation.DelegatorAddress)
104 |
105 | _, _ = app.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr)
106 | }
107 |
108 | // clear validator slash events
109 | app.DistrKeeper.DeleteAllValidatorSlashEvents(ctx)
110 |
111 | // clear validator historical rewards
112 | app.DistrKeeper.DeleteAllValidatorHistoricalRewards(ctx)
113 |
114 | // set context height to zero
115 | height := ctx.BlockHeight()
116 | ctx = ctx.WithBlockHeight(0)
117 |
118 | // reinitialize all validators
119 | app.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) {
120 | valBz, err := app.StakingKeeper.ValidatorAddressCodec().StringToBytes(val.GetOperator())
121 | if err != nil {
122 | panic(err)
123 | }
124 | // donate any unwithdrawn outstanding reward fraction tokens to the community pool
125 | scraps, err := app.DistrKeeper.GetValidatorOutstandingRewardsCoins(ctx, valBz)
126 | if err != nil {
127 | panic(err)
128 | }
129 | feePool, err := app.DistrKeeper.FeePool.Get(ctx)
130 | if err != nil {
131 | panic(err)
132 | }
133 | feePool.CommunityPool = feePool.CommunityPool.Add(scraps...)
134 | if err := app.DistrKeeper.FeePool.Set(ctx, feePool); err != nil {
135 | panic(err)
136 | }
137 |
138 | if err := app.DistrKeeper.Hooks().AfterValidatorCreated(ctx, valBz); err != nil {
139 | panic(err)
140 | }
141 | return false
142 | })
143 |
144 | // reinitialize all delegations
145 | for _, del := range dels {
146 | valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress)
147 | if err != nil {
148 | panic(err)
149 | }
150 | delAddr := sdk.MustAccAddressFromBech32(del.DelegatorAddress)
151 |
152 | if err := app.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil {
153 | // never called as BeforeDelegationCreated always returns nil
154 | panic(fmt.Errorf("error while incrementing period: %w", err))
155 | }
156 |
157 | if err := app.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr); err != nil {
158 | // never called as AfterDelegationModified always returns nil
159 | panic(fmt.Errorf("error while creating a new delegation period record: %w", err))
160 | }
161 | }
162 |
163 | // reset context height
164 | ctx = ctx.WithBlockHeight(height)
165 |
166 | /* Handle staking state. */
167 |
168 | // iterate through redelegations, reset creation height
169 | app.StakingKeeper.IterateRedelegations(ctx, func(_ int64, red stakingtypes.Redelegation) (stop bool) {
170 | for i := range red.Entries {
171 | red.Entries[i].CreationHeight = 0
172 | }
173 | app.StakingKeeper.SetRedelegation(ctx, red)
174 | return false
175 | })
176 |
177 | // iterate through unbonding delegations, reset creation height
178 | app.StakingKeeper.IterateUnbondingDelegations(ctx, func(_ int64, ubd stakingtypes.UnbondingDelegation) (stop bool) {
179 | for i := range ubd.Entries {
180 | ubd.Entries[i].CreationHeight = 0
181 | }
182 | app.StakingKeeper.SetUnbondingDelegation(ctx, ubd)
183 | return false
184 | })
185 |
186 | // Iterate through validators by power descending, reset bond heights, and
187 | // update bond intra-tx counters.
188 | store := ctx.KVStore(app.GetKey(stakingtypes.StoreKey))
189 | iter := storetypes.KVStoreReversePrefixIterator(store, stakingtypes.ValidatorsKey)
190 | counter := int16(0)
191 |
192 | for ; iter.Valid(); iter.Next() {
193 | addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key()))
194 | validator, err := app.StakingKeeper.GetValidator(ctx, addr)
195 | if err != nil {
196 | panic(err)
197 | }
198 |
199 | validator.UnbondingHeight = 0
200 | if applyAllowedAddrs && !allowedAddrsMap[addr.String()] {
201 | validator.Jailed = true
202 | }
203 |
204 | app.StakingKeeper.SetValidator(ctx, validator)
205 | counter++
206 | }
207 |
208 | if err := iter.Close(); err != nil {
209 | app.Logger().Error("error while closing the key-value store reverse prefix iterator: ", err)
210 | return
211 | }
212 |
213 | _, err = app.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx)
214 | if err != nil {
215 | log.Fatal(err)
216 | }
217 |
218 | /* Handle slashing state. */
219 |
220 | // reset start height on signing infos
221 | app.SlashingKeeper.IterateValidatorSigningInfos(
222 | ctx,
223 | func(addr sdk.ConsAddress, info slashingtypes.ValidatorSigningInfo) (stop bool) {
224 | info.StartHeight = 0
225 | app.SlashingKeeper.SetValidatorSigningInfo(ctx, addr, info)
226 | return false
227 | },
228 | )
229 | }
230 |
--------------------------------------------------------------------------------
/x/btcbridge/client/cli/tx.go:
--------------------------------------------------------------------------------
1 | package cli
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "os"
7 | "strconv"
8 | "strings"
9 | "time"
10 |
11 | "github.com/spf13/cobra"
12 |
13 | "github.com/btcsuite/btcd/btcutil/psbt"
14 |
15 | "github.com/cosmos/cosmos-sdk/client"
16 | "github.com/cosmos/cosmos-sdk/client/flags"
17 | "github.com/cosmos/cosmos-sdk/client/tx"
18 | sdk "github.com/cosmos/cosmos-sdk/types"
19 |
20 | // "github.com/cosmos/cosmos-sdk/client/flags"
21 | "github.com/sideprotocol/side/x/btcbridge/types"
22 | )
23 |
24 | var DefaultRelativePacketTimeoutTimestamp = uint64((time.Duration(10) * time.Minute).Nanoseconds())
25 |
26 | const (
27 | // flagPacketTimeoutTimestamp = "packet-timeout-timestamp"
28 | listSeparator = ","
29 | )
30 |
31 | // GetTxCmd returns the transaction commands for this module
32 | func GetTxCmd() *cobra.Command {
33 | cmd := &cobra.Command{
34 | Use: types.ModuleName,
35 | Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName),
36 | DisableFlagParsing: true,
37 | SuggestionsMinimumDistance: 2,
38 | RunE: client.ValidateCmd,
39 | }
40 |
41 | cmd.AddCommand(CmdSubmitBlocks())
42 | cmd.AddCommand(CmdSubmitFeeRate())
43 | cmd.AddCommand(CmdUpdateNonBtcRelayers())
44 | cmd.AddCommand(CmdUpdateFeeProviders())
45 | cmd.AddCommand(CmdWithdrawToBitcoin())
46 | cmd.AddCommand(CmdSubmitSignatures())
47 | cmd.AddCommand(CmdCompleteDKG())
48 |
49 | return cmd
50 | }
51 |
52 | func CmdSubmitBlocks() *cobra.Command {
53 | cmd := &cobra.Command{
54 | Use: "submit-blocks [file-path-to-block-headers.json]",
55 | Short: "Submit Bitcoin block headers to the chain",
56 | Args: cobra.ExactArgs(1),
57 | RunE: func(cmd *cobra.Command, args []string) (err error) {
58 | clientCtx, err := client.GetClientTxContext(cmd)
59 | if err != nil {
60 | return err
61 | }
62 |
63 | // read the block headers from the file
64 | blockHeaders, err := readBlockHeadersFromFile(args[0])
65 | if err != nil {
66 | return err
67 | }
68 |
69 | msg := types.NewMsgSubmitBlockHeaders(
70 | clientCtx.GetFromAddress().String(),
71 | blockHeaders,
72 | )
73 | if err := msg.ValidateBasic(); err != nil {
74 | return err
75 | }
76 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
77 | },
78 | }
79 |
80 | flags.AddTxFlagsToCmd(cmd)
81 |
82 | return cmd
83 | }
84 |
85 | func CmdSubmitFeeRate() *cobra.Command {
86 | cmd := &cobra.Command{
87 | Use: "submit-fee-rate [fee rate]",
88 | Short: "Submit the latest fee rate of the bitcoin network",
89 | Args: cobra.ExactArgs(1),
90 | RunE: func(cmd *cobra.Command, args []string) (err error) {
91 | clientCtx, err := client.GetClientTxContext(cmd)
92 | if err != nil {
93 | return err
94 | }
95 |
96 | feeRate, err := strconv.ParseInt(args[0], 10, 64)
97 | if err != nil {
98 | return err
99 | }
100 |
101 | msg := types.NewMsgSubmitFeeRate(
102 | clientCtx.GetFromAddress().String(),
103 | feeRate,
104 | )
105 |
106 | if err := msg.ValidateBasic(); err != nil {
107 | return err
108 | }
109 |
110 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
111 | },
112 | }
113 |
114 | flags.AddTxFlagsToCmd(cmd)
115 |
116 | return cmd
117 | }
118 |
119 | func CmdUpdateNonBtcRelayers() *cobra.Command {
120 | cmd := &cobra.Command{
121 | Use: "update-non-btc-relayers [relayers]",
122 | Short: "Update trusted non-btc asset relayers",
123 | Args: cobra.ExactArgs(1),
124 | RunE: func(cmd *cobra.Command, args []string) (err error) {
125 | clientCtx, err := client.GetClientTxContext(cmd)
126 | if err != nil {
127 | return err
128 | }
129 |
130 | msg := types.NewMsgUpdateTrustedNonBtcRelayers(
131 | clientCtx.GetFromAddress().String(),
132 | strings.Split(args[0], listSeparator),
133 | )
134 |
135 | if err := msg.ValidateBasic(); err != nil {
136 | return err
137 | }
138 |
139 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
140 | },
141 | }
142 |
143 | flags.AddTxFlagsToCmd(cmd)
144 |
145 | return cmd
146 | }
147 |
148 | func CmdUpdateFeeProviders() *cobra.Command {
149 | cmd := &cobra.Command{
150 | Use: "update-fee-providers [fee providers]",
151 | Short: "Update trusted fee providers",
152 | Args: cobra.ExactArgs(1),
153 | RunE: func(cmd *cobra.Command, args []string) (err error) {
154 | clientCtx, err := client.GetClientTxContext(cmd)
155 | if err != nil {
156 | return err
157 | }
158 |
159 | msg := types.NewMsgUpdateTrustedFeeProviders(
160 | clientCtx.GetFromAddress().String(),
161 | strings.Split(args[0], listSeparator),
162 | )
163 |
164 | if err := msg.ValidateBasic(); err != nil {
165 | return err
166 | }
167 |
168 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
169 | },
170 | }
171 |
172 | flags.AddTxFlagsToCmd(cmd)
173 |
174 | return cmd
175 | }
176 |
177 | // Withdraw To Bitcoin
178 | func CmdWithdrawToBitcoin() *cobra.Command {
179 | cmd := &cobra.Command{
180 | Use: "withdraw [amount]",
181 | Short: "Withdraw bitcoin asset to the given sender",
182 | Args: cobra.ExactArgs(1),
183 | RunE: func(cmd *cobra.Command, args []string) (err error) {
184 | clientCtx, err := client.GetClientTxContext(cmd)
185 | if err != nil {
186 | return err
187 | }
188 |
189 | _, err = sdk.ParseCoinsNormalized(args[0])
190 | if err != nil {
191 | return fmt.Errorf("invalid amount")
192 | }
193 |
194 | msg := types.NewMsgWithdrawToBitcoin(
195 | clientCtx.GetFromAddress().String(),
196 | args[0],
197 | )
198 |
199 | if err := msg.ValidateBasic(); err != nil {
200 | return err
201 | }
202 |
203 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
204 | },
205 | }
206 |
207 | flags.AddTxFlagsToCmd(cmd)
208 |
209 | return cmd
210 | }
211 |
212 | func CmdSubmitSignatures() *cobra.Command {
213 | cmd := &cobra.Command{
214 | Use: "submit-signatures [psbt]",
215 | Short: "Submit the signed psbt",
216 | Args: cobra.ExactArgs(1),
217 | RunE: func(cmd *cobra.Command, args []string) (err error) {
218 | clientCtx, err := client.GetClientTxContext(cmd)
219 | if err != nil {
220 | return err
221 | }
222 |
223 | p, err := psbt.NewFromRawBytes(strings.NewReader(args[0]), true)
224 | if err != nil {
225 | return fmt.Errorf("invalid psbt")
226 | }
227 |
228 | signedTx, err := psbt.Extract(p)
229 | if err != nil {
230 | return fmt.Errorf("failed to extract tx from psbt")
231 | }
232 |
233 | msg := types.NewMsgSubmitSignatures(
234 | clientCtx.GetFromAddress().String(),
235 | signedTx.TxHash().String(),
236 | args[0],
237 | )
238 |
239 | if err := msg.ValidateBasic(); err != nil {
240 | return err
241 | }
242 |
243 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
244 | },
245 | }
246 |
247 | flags.AddTxFlagsToCmd(cmd)
248 |
249 | return cmd
250 | }
251 |
252 | // Complete DKG
253 | func CmdCompleteDKG() *cobra.Command {
254 | cmd := &cobra.Command{
255 | Use: "complete-dkg [id] [vaults] [validator-address] [signature]",
256 | Short: "Complete dkg request with new vaults",
257 | Args: cobra.ExactArgs(4),
258 | RunE: func(cmd *cobra.Command, args []string) (err error) {
259 | clientCtx, err := client.GetClientTxContext(cmd)
260 | if err != nil {
261 | return err
262 | }
263 |
264 | id, err := strconv.ParseUint(args[0], 10, 64)
265 | if err != nil {
266 | return err
267 | }
268 |
269 | vaults := strings.Split(args[1], listSeparator)
270 |
271 | msg := types.NewMsgCompleteDKG(
272 | clientCtx.GetFromAddress().String(),
273 | id,
274 | vaults,
275 | args[2],
276 | args[3],
277 | )
278 |
279 | if err := msg.ValidateBasic(); err != nil {
280 | return err
281 | }
282 |
283 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
284 | },
285 | }
286 |
287 | flags.AddTxFlagsToCmd(cmd)
288 |
289 | return cmd
290 | }
291 |
292 | // readBlockHeadersFromFile reads the block headers from the file
293 | func readBlockHeadersFromFile(filePath string) ([]*types.BlockHeader, error) {
294 | // read the file
295 | file, err := os.Open(filePath)
296 | if err != nil {
297 | return nil, err
298 | }
299 | defer file.Close()
300 |
301 | // read the block headers from the file
302 | var blockHeaders []*types.BlockHeader
303 | decoder := json.NewDecoder(file)
304 | if err := decoder.Decode(&blockHeaders); err != nil {
305 | return nil, err
306 | }
307 | return blockHeaders, nil
308 | }
309 |
--------------------------------------------------------------------------------
/x/btcbridge/types/runes.go:
--------------------------------------------------------------------------------
1 | package types
2 |
3 | import (
4 | "encoding/binary"
5 | "fmt"
6 | "strconv"
7 | "strings"
8 |
9 | "lukechampine.com/uint128"
10 |
11 | "github.com/btcsuite/btcd/txscript"
12 | "github.com/btcsuite/btcd/wire"
13 | )
14 |
15 | const (
16 | // runes protocol name
17 | RunesProtocolName = "runes"
18 |
19 | // runes magic number
20 | MagicNumber = txscript.OP_13
21 |
22 | // tag indicating that the following are edicts
23 | TagBody = 0
24 |
25 | // the number of components of each edict
26 | EdictLen = 4
27 |
28 | // sats in the runes output by default
29 | RunesOutValue = 546
30 | )
31 |
32 | // ParseRunes parses the potential runes protocol from the given tx;
33 | // If no OP_RETURN found, no error returned
34 | // Only support edicts for now
35 | func ParseRunes(tx *wire.MsgTx) ([]*Edict, error) {
36 | for _, out := range tx.TxOut {
37 | tokenizer := txscript.MakeScriptTokenizer(0, out.PkScript)
38 | if !tokenizer.Next() || tokenizer.Err() != nil || tokenizer.Opcode() != txscript.OP_RETURN {
39 | continue
40 | }
41 |
42 | if !tokenizer.Next() || tokenizer.Err() != nil || tokenizer.Opcode() != MagicNumber {
43 | continue
44 | }
45 |
46 | var payload []byte
47 |
48 | for tokenizer.Next() {
49 | if txscript.IsSmallInt(tokenizer.Opcode()) || tokenizer.Opcode() <= txscript.OP_PUSHDATA4 {
50 | payload = append(payload, tokenizer.Data()...)
51 | } else {
52 | return nil, ErrInvalidRunes
53 | }
54 | }
55 |
56 | if tokenizer.Err() != nil {
57 | return nil, ErrInvalidRunes
58 | }
59 |
60 | return ParseEdicts(tx, payload)
61 | }
62 |
63 | return nil, nil
64 | }
65 |
66 | // ParseEdicts parses the given payload to a set of edicts
67 | func ParseEdicts(tx *wire.MsgTx, payload []byte) ([]*Edict, error) {
68 | integers, err := DecodeVec(payload)
69 | if err != nil {
70 | return nil, err
71 | }
72 |
73 | if len(integers) < EdictLen+1 || len(integers[1:])%EdictLen != 0 || !integers[0].Equals(uint128.From64(TagBody)) {
74 | return nil, ErrInvalidRunes
75 | }
76 |
77 | integers = integers[1:]
78 |
79 | edicts := make([]*Edict, 0)
80 |
81 | for i := 0; i < len(integers); i = i + 4 {
82 | output := uint32(integers[i+3].Big().Uint64())
83 | if output > uint32(len(tx.TxOut)) {
84 | return nil, ErrInvalidRunes
85 | }
86 |
87 | // actually we only support one edict for now, so delta is unnecessary
88 | edict := Edict{
89 | Id: &RuneId{
90 | Block: integers[i].Big().Uint64(),
91 | Tx: uint32(integers[i+1].Big().Uint64()),
92 | },
93 | Amount: integers[i+2].String(),
94 | Output: output,
95 | }
96 |
97 | edicts = append(edicts, &edict)
98 | }
99 |
100 | return edicts, nil
101 | }
102 |
103 | // ParseEdict parses the given payload to edict
104 | func ParseEdict(payload []byte) (*Edict, error) {
105 | integers, err := DecodeVec(payload)
106 | if err != nil {
107 | return nil, err
108 | }
109 |
110 | if len(integers) != EdictLen+1 && !integers[0].Equals(uint128.From64(TagBody)) {
111 | return nil, ErrInvalidRunes
112 | }
113 |
114 | return &Edict{
115 | Id: &RuneId{
116 | Block: integers[1].Big().Uint64(),
117 | Tx: uint32(integers[2].Big().Uint64()),
118 | },
119 | Amount: integers[3].String(),
120 | Output: uint32(integers[4].Big().Uint64()),
121 | }, nil
122 | }
123 |
124 | // BuildEdictScript builds the edict script
125 | func BuildEdictScript(runeId string, amount uint128.Uint128, output uint32) ([]byte, error) {
126 | var id RuneId
127 | id.MustUnmarshalFromString(runeId)
128 |
129 | edict := Edict{
130 | Id: &id,
131 | Amount: amount.String(),
132 | Output: output,
133 | }
134 |
135 | payload := []byte{TagBody}
136 | payload = append(payload, edict.MustMarshalLEB128()...)
137 |
138 | scriptBuilder := txscript.NewScriptBuilder()
139 | scriptBuilder.AddOp(txscript.OP_RETURN).AddOp(MagicNumber).AddData(payload)
140 |
141 | return scriptBuilder.Script()
142 | }
143 |
144 | func (id *RuneId) ToString() string {
145 | return fmt.Sprintf("%d:%d", id.Block, id.Tx)
146 | }
147 |
148 | func (id *RuneId) FromString(idStr string) error {
149 | parts := strings.Split(idStr, ":")
150 | if len(parts) != 2 {
151 | return ErrInvalidRuneId
152 | }
153 |
154 | block, err := strconv.ParseUint(parts[0], 10, 64)
155 | if err != nil {
156 | return err
157 | }
158 |
159 | tx, err := strconv.ParseUint(parts[1], 10, 32)
160 | if err != nil {
161 | return err
162 | }
163 |
164 | id.Block = block
165 | id.Tx = uint32(tx)
166 |
167 | return nil
168 | }
169 |
170 | func (id *RuneId) MustUnmarshalFromString(s string) {
171 | err := id.FromString(s)
172 | if err != nil {
173 | panic(err)
174 | }
175 | }
176 |
177 | func (id *RuneId) MarshalToBytes() []byte {
178 | bz := make([]byte, 8+4)
179 |
180 | binary.LittleEndian.PutUint64(bz, id.Block)
181 | binary.LittleEndian.PutUint32(bz, id.Tx)
182 |
183 | return bz
184 | }
185 |
186 | func (id *RuneId) UnmarshalFromBytes(bz []byte) {
187 | id.Block = binary.LittleEndian.Uint64(bz[:8])
188 | id.Tx = binary.LittleEndian.Uint32(bz[8:])
189 | }
190 |
191 | // Denom returns the corresponding denom for the runes voucher token
192 | func (id *RuneId) Denom() string {
193 | return fmt.Sprintf("%s/%s", RunesProtocolName, id.ToString())
194 | }
195 |
196 | // FromDenom converts the denom to the rune id
197 | func (id *RuneId) FromDenom(denom string) {
198 | idStr := strings.TrimPrefix(denom, fmt.Sprintf("%s/", RunesProtocolName))
199 |
200 | id.MustUnmarshalFromString(idStr)
201 | }
202 |
203 | // MarshalRuneIdFromString marshals the given id string
204 | func MarshalRuneIdFromString(s string) []byte {
205 | var id RuneId
206 | id.MustUnmarshalFromString(s)
207 |
208 | return id.MarshalToBytes()
209 | }
210 |
211 | // UnmarshalRuneId unmarshals the given bytes to the rune id
212 | func UnmarshalRuneId(bz []byte) RuneId {
213 | var id RuneId
214 | id.UnmarshalFromBytes(bz)
215 |
216 | return id
217 | }
218 |
219 | func (e *Edict) MustMarshalLEB128() []byte {
220 | amount := RuneAmountFromString(e.Amount)
221 |
222 | payload := make([]byte, 0)
223 |
224 | payload = append(payload, EncodeUint64(e.Id.Block)...)
225 | payload = append(payload, EncodeUint32(e.Id.Tx)...)
226 | payload = append(payload, EncodeUint128(&amount)...)
227 | payload = append(payload, EncodeUint32(e.Output)...)
228 |
229 | return payload
230 | }
231 |
232 | // RuneBalances defines a set of rune balances
233 | type RuneBalances []*RuneBalance
234 |
235 | // GetBalance gets the rune balance by id
236 | func (rbs RuneBalances) GetBalance(id string) (int, uint128.Uint128) {
237 | for i, balance := range rbs {
238 | if balance.Id == id {
239 | return i, RuneAmountFromString(balance.Amount)
240 | }
241 | }
242 |
243 | return -1, uint128.Zero
244 | }
245 |
246 | // Merge merges the another rune balances
247 | func (rbs RuneBalances) Merge(other RuneBalances) RuneBalances {
248 | var result RuneBalances
249 | result = append(result, rbs...)
250 |
251 | for _, ob := range other {
252 | i, b := result.GetBalance(ob.Id)
253 | if !b.IsZero() {
254 | result[i].Amount = b.Add(RuneAmountFromString(ob.Amount)).String()
255 | } else {
256 | result = append(result, ob)
257 | }
258 | }
259 |
260 | return result
261 | }
262 |
263 | // Update updates the balance to the specified amount for the given rune id
264 | // The rune balance will be removed if the given amount is zero
265 | // Assume that the given RuneBalances is compact
266 | func (rbs RuneBalances) Update(id string, amount uint128.Uint128) RuneBalances {
267 | for i, balance := range rbs {
268 | if balance.Id == id {
269 | if !amount.IsZero() {
270 | rbs[i].Amount = amount.String()
271 | } else {
272 | rbs = append(rbs[:i], rbs[i+1:]...)
273 | }
274 |
275 | break
276 | }
277 | }
278 |
279 | return rbs
280 | }
281 |
282 | // RuneAmountFromString converts the given string to the rune amount
283 | // Panic if any error occurred
284 | func RuneAmountFromString(s string) uint128.Uint128 {
285 | amount, err := uint128.FromString(s)
286 | if err != nil {
287 | panic(err)
288 | }
289 |
290 | return amount
291 | }
292 |
293 | // MarshalRuneAmount marshals the given amount
294 | func MarshalRuneAmount(amount uint128.Uint128) []byte {
295 | bz := make([]byte, 16)
296 | amount.PutBytes(bz)
297 |
298 | return bz
299 | }
300 |
301 | // MarshalRuneAmountFromString marshals the given amount string
302 | func MarshalRuneAmountFromString(s string) []byte {
303 | amount := RuneAmountFromString(s)
304 |
305 | return MarshalRuneAmount(amount)
306 | }
307 |
308 | // UnmarshalRuneAmount unmarshals the given bytes to the rune amount
309 | func UnmarshalRuneAmount(bz []byte) uint128.Uint128 {
310 | return uint128.FromBytes(bz)
311 | }
312 |
--------------------------------------------------------------------------------