Skip to content

Commit

Permalink
staking: verify staking/slashing tx relationship in staking requests (#…
Browse files Browse the repository at this point in the history
…46)

First step of #7 

This PR adds verification on the relationship between staking/slashing
tx in staking requests using
`babylon_btcstaking::tx_verify::check_transactions`. This also involves
further refactoring of datagen library, and a bug fix in taproot pk
script generation where we mistakenly used secp256k1 FFI that bloats
contract size.

NOTE: this PR still results in a wasm contract slightly bigger than the
standard 800 KB. There are two ways to go ahead before optimisation is
in place:

1. merge it into a feature branch for full validation for now, optimise
in subsequent PRs, or
2. merge it into `main`, but before that add a Rust feature to allow to
enable/disable the full validation

I'm inclined to 2 but open to other ideas cc @maurolacy @gusin13 

---

TODOs before ready:

- [x] fixing size of BTC staking contract (current it's 1.9MB)
- [x] making all tests use corresponding parameters in
[datagen](https://github.com/babylonlabs-io/babylon-contract/blob/647b1f4f16a8b37f3d5a11a543cdb093f2d7d830/datagen/utils/btcstaking.go)
  • Loading branch information
SebastianElvis authored Sep 5, 2024
1 parent a36f194 commit 187839b
Show file tree
Hide file tree
Showing 44 changed files with 491 additions and 301 deletions.
2 changes: 1 addition & 1 deletion .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[alias]
unit-test = "test --lib"
unit-test = "test --lib --features full-validation"
integration = "run-script integration"
gen-data = "run-script gen-data"
gen-proto = "run-script gen-proto"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/local-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ jobs:
image: rust:1.78.0
steps:
- uses: actions/checkout@v4.1.0
- name: Build contracts, check formats, and run unit tests
run: cargo test --lib
- name: Build contracts, check formats, and run unit tests (with full validation)
run: cargo test --lib --features full-validation
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 18 additions & 6 deletions contracts/btc-staking/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[package.metadata.optimizer]
default-build = true
builds = [
{ name = "full-validation", features = [
"full-validation",
], default-features = false }
]

[lib]
crate-type = ["cdylib", "rlib"]
doctest = false
Expand All @@ -25,14 +33,18 @@ default = []
cranelift = ["cosmwasm-vm/cranelift"]
# for quicker tests, cargo test --lib
library = []
# feature for enabling the full validation
full-validation = []

[dependencies]
babylon-apis = { path = "../../packages/apis" }
babylon-bindings = { path = "../../packages/bindings" }
babylon-contract = { path = "../babylon", features = [ "library" ] }
babylon-merkle = { path = "../../packages/merkle" }
babylon-proto = { path = "../../packages/proto" }
eots = { path = "../../packages/eots" }
babylon-apis = { path = "../../packages/apis" }
babylon-bindings = { path = "../../packages/bindings" }
babylon-contract = { path = "../babylon", features = [ "library" ] }
babylon-merkle = { path = "../../packages/merkle" }
babylon-proto = { path = "../../packages/proto" }
babylon-btcstaking = { path = "../../packages/btcstaking" }
babylon-bitcoin = { path = "../../packages/bitcoin" }
eots = { path = "../../packages/eots" }

bitcoin = { workspace = true }
cosmwasm-schema = { workspace = true }
Expand Down
22 changes: 21 additions & 1 deletion contracts/btc-staking/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,12 +279,16 @@ pub(crate) mod tests {

use super::*;

use crate::state::config::Params;
use babylon_apis::btc_staking_api::{
ActiveBtcDelegation, BtcUndelegationInfo, CovenantAdaptorSignatures,
FinalityProviderDescription, NewFinalityProvider, ProofOfPossessionBtc,
};
use babylon_apis::finality_api::PubRandCommit;
use babylon_proto::babylon::btcstaking::v1::{BtcDelegation, FinalityProvider};
use babylon_bitcoin::chain_params::Network;
use babylon_proto::babylon::btcstaking::v1::{
BtcDelegation, FinalityProvider, Params as ProtoParams,
};
use cosmwasm_std::{
from_json,
testing::{message_info, mock_dependencies, mock_env},
Expand All @@ -298,6 +302,22 @@ pub(crate) mod tests {
pub(crate) const INIT_ADMIN: &str = "initial_admin";
const NEW_ADMIN: &str = "new_admin";

fn new_params(params: ProtoParams) -> Params {
Params {
btc_network: Network::Regtest, // TODO: fix this
max_active_finality_providers: params.max_active_finality_providers,
min_pub_rand: 10, // TODO: fix this
slashing_address: params.slashing_address,
min_slashing_tx_fee_sat: params.min_slashing_tx_fee_sat as u64,
slashing_rate: "0.01".to_string(), // TODO: fix this
}
}

pub(crate) fn get_params() -> Params {
let proto_params = test_utils::get_params();
new_params(proto_params)
}

fn new_finality_provider(fp: FinalityProvider) -> NewFinalityProvider {
NewFinalityProvider {
addr: fp.addr,
Expand Down
10 changes: 4 additions & 6 deletions contracts/btc-staking/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub enum ContractError {
Std(#[from] StdError),
#[error("{0}")]
Payment(#[from] PaymentError),
#[error("{0}")]
BTCStaking(#[from] babylon_btcstaking::error::Error),
#[error("error converting from hex to array: {0}")]
HexArrayError(#[from] HexToArrayError),
#[error("{0}")]
Expand Down Expand Up @@ -48,16 +50,12 @@ pub enum ContractError {
DelegationAlreadyExists(String),
#[error("Invalid Btc tx: {0}")]
InvalidBtcTx(String),
#[error("Empty unbonding tx")]
EmptyUnbondingTx,
#[error("Empty Slashing tx")]
EmptySlashingTx,
#[error("Empty signature from the delegator")]
EmptySignature,
#[error("Invalid lock type: seconds")]
ErrInvalidLockType,
#[error("Invalid lock time blocks: {0}, max: {1}")]
ErrInvalidLockTime(u32, u32),
#[error("Empty signature from the delegator")]
EmptySignature,
#[error("The finality provider {0} does not have voting power at height {1}")]
NoVotingPower(String, u64),
#[error("The chain has not reached the given height yet")]
Expand Down
28 changes: 20 additions & 8 deletions contracts/btc-staking/src/finality.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,10 +515,11 @@ pub(crate) mod tests {
use test_utils::{get_add_finality_sig, get_add_finality_sig_2, get_pub_rand_value};

use crate::contract::tests::{
create_new_finality_provider, get_public_randomness_commitment, CREATOR,
create_new_finality_provider, get_params, get_public_randomness_commitment, CREATOR,
};
use crate::contract::{execute, instantiate};
use crate::msg::{ExecuteMsg, FinalitySignatureResponse, InstantiateMsg};
use crate::queries;

pub(crate) fn mock_env_height(height: u64) -> Env {
let mut env = mock_env();
Expand Down Expand Up @@ -555,7 +556,7 @@ pub(crate) mod tests {
height: u64,
) -> Result<Response<BabylonMsg>, crate::error::ContractError> {
let env = mock_env_height(height);
// Hash is not used in the begin-block handler
// Hash is not used in the end-block handler
let hash_hex = "deadbeef".to_string();
let app_hash_hex = app_hash.encode_hex();

Expand Down Expand Up @@ -636,7 +637,7 @@ pub(crate) mod tests {
initial_env.clone(),
info.clone(),
InstantiateMsg {
params: None,
params: Some(get_params()),
admin: None,
},
)
Expand Down Expand Up @@ -774,7 +775,7 @@ pub(crate) mod tests {
initial_env.clone(),
info.clone(),
InstantiateMsg {
params: None,
params: Some(get_params()),
admin: None,
},
)
Expand All @@ -791,7 +792,7 @@ pub(crate) mod tests {
unbonded_del: vec![],
};

let _res = execute(deps.as_mut(), initial_env.clone(), info.clone(), msg).unwrap();
execute(deps.as_mut(), initial_env.clone(), info.clone(), msg).unwrap();

// Add a delegation, so that the finality provider has some power
let mut del1 = crate::contract::tests::get_derived_btc_delegation(1, &[1]);
Expand All @@ -803,9 +804,14 @@ pub(crate) mod tests {
slashed_del: vec![],
unbonded_del: vec![],
};

execute(deps.as_mut(), initial_env, info.clone(), msg).unwrap();

// Check that the finality provider power has been updated
let fp_info =
queries::finality_provider_info(deps.as_ref(), new_fp.btc_pk_hex.clone(), None)
.unwrap();
assert_eq!(fp_info.power, del1.total_sat);

// Submit public randomness commitment for the FP and the involved heights
let msg = ExecuteMsg::CommitPublicRandomness {
fp_pubkey_hex: pk_hex.clone(),
Expand Down Expand Up @@ -862,7 +868,7 @@ pub(crate) mod tests {
// 2. The FP has consolidated power at such height
// 3. There are no more pending / future blocks to process
let submit_env = mock_env_height(submit_height);
let _res = execute(deps.as_mut(), submit_env.clone(), info.clone(), msg).unwrap();
execute(deps.as_mut(), submit_env.clone(), info.clone(), msg).unwrap();

// Call the begin blocker, to compute the active FP set
let res = call_begin_block(
Expand Down Expand Up @@ -930,7 +936,7 @@ pub(crate) mod tests {
initial_env.clone(),
info.clone(),
InstantiateMsg {
params: None,
params: Some(get_params()),
admin: None,
},
)
Expand Down Expand Up @@ -962,6 +968,12 @@ pub(crate) mod tests {

execute(deps.as_mut(), initial_env.clone(), info.clone(), msg).unwrap();

// Check that the finality provider power has been updated
let fp_info =
queries::finality_provider_info(deps.as_ref(), new_fp.btc_pk_hex.clone(), None)
.unwrap();
assert_eq!(fp_info.power, del1.total_sat);

// Submit public randomness commitment for the FP and the involved heights
let msg = ExecuteMsg::CommitPublicRandomness {
fp_pubkey_hex: pk_hex.clone(),
Expand Down
27 changes: 26 additions & 1 deletion contracts/btc-staking/src/queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,12 +246,13 @@ mod tests {

use babylon_apis::btc_staking_api::{FinalityProvider, UnbondedBtcDelegation};

use crate::contract::tests::create_new_finality_provider;
use crate::contract::tests::{create_new_finality_provider, get_params};
use crate::contract::{execute, instantiate};
use crate::error::ContractError;
use crate::finality::tests::mock_env_height;
use crate::msg::{ExecuteMsg, FinalityProviderInfo, InstantiateMsg};
use crate::staking::tests::staking_tx_hash;
use crate::state::config::PARAMS;
use crate::state::staking::{BtcDelegation, FinalityProviderState, FP_STATE_KEY};

const CREATOR: &str = "creator";
Expand Down Expand Up @@ -279,6 +280,9 @@ mod tests {
)
.unwrap();

let params = get_params();
PARAMS.save(deps.as_mut().storage, &params).unwrap();

// Add a couple finality providers
let new_fp1 = create_new_finality_provider(1);
let new_fp2 = create_new_finality_provider(2);
Expand Down Expand Up @@ -337,6 +341,9 @@ mod tests {
)
.unwrap();

let params = get_params();
PARAMS.save(deps.as_mut().storage, &params).unwrap();

// Add a couple finality providers
let new_fp1 = create_new_finality_provider(1);
let new_fp2 = create_new_finality_provider(2);
Expand Down Expand Up @@ -409,6 +416,9 @@ mod tests {
)
.unwrap();

let params = get_params();
PARAMS.save(deps.as_mut().storage, &params).unwrap();

// Add a finality provider
let new_fp1 = create_new_finality_provider(1);

Expand Down Expand Up @@ -494,6 +504,9 @@ mod tests {
)
.unwrap();

let params = get_params();
PARAMS.save(deps.as_mut().storage, &params).unwrap();

// Add a couple finality providers
let new_fp1 = create_new_finality_provider(1);
let fp1_pk = new_fp1.btc_pk_hex.clone();
Expand Down Expand Up @@ -552,6 +565,9 @@ mod tests {
)
.unwrap();

let params = get_params();
PARAMS.save(deps.as_mut().storage, &params).unwrap();

// Add a finality provider
let new_fp1 = create_new_finality_provider(1);
let fp1_pk = new_fp1.btc_pk_hex.clone();
Expand Down Expand Up @@ -642,6 +658,9 @@ mod tests {
)
.unwrap();

let params = get_params();
PARAMS.save(deps.as_mut().storage, &params).unwrap();

// Add a finality provider
let new_fp1 = create_new_finality_provider(1);
let fp1_pk = new_fp1.btc_pk_hex.clone();
Expand Down Expand Up @@ -733,6 +752,9 @@ mod tests {
)
.unwrap();

let params = get_params();
PARAMS.save(deps.as_mut().storage, &params).unwrap();

// Add a finality provider
let new_fp1 = create_new_finality_provider(1);
let fp1_pk = new_fp1.btc_pk_hex.clone();
Expand Down Expand Up @@ -786,6 +808,9 @@ mod tests {
)
.unwrap();

let params = get_params();
PARAMS.save(deps.as_mut().storage, &params).unwrap();

// Add a couple finality providers
let new_fp1 = create_new_finality_provider(1);
let fp1_pk = new_fp1.btc_pk_hex.clone();
Expand Down
Loading

0 comments on commit 187839b

Please sign in to comment.