Skip to content

Commit

Permalink
v1.2 upgrade (#3)
Browse files Browse the repository at this point in the history
* properly set new contract version in v1.1 upgrade

* validate keys

* v1.2 migrate

* put updates under feature flags

* some styling and code quality improvements

* bump version to 1.2.0

* move all dependencies to workspace

* update workspace profile

* do not use `k256` inside a contract

* determine `expected_version` by flag

* bump dependency versions

* add migrate entry point for nft contract

* remove features (unnecessary)
  • Loading branch information
larry0x authored Dec 7, 2022
1 parent dc485bd commit 964af0e
Show file tree
Hide file tree
Showing 23 changed files with 393 additions and 201 deletions.
215 changes: 117 additions & 98 deletions Cargo.lock

Large diffs are not rendered by default.

52 changes: 25 additions & 27 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
members = ["contracts/*", "packages/*"]

[workspace.package]
version = "1.1.0"
version = "1.2.0"
authors = ["larry <[email protected]>"]
edition = "2021"
homepage = "https://badges.fun"
Expand All @@ -11,34 +11,32 @@ license = "GPL-3.0-or-later"
keywords = ["nft", "cosmos", "cosmwasm", "stargaze"]

[workspace.dependencies]
cosmwasm-schema = "1.0"
cosmwasm-std = "1.0"
cw2 = "0.13"
cw721 = "0.13"
cw721-base = "0.13"
cw-storage-plus = "0.13"
cw-utils = "0.13"
cosmwasm-schema = "1.1"
cosmwasm-std = "1.1"
cw2 = "0.16"
cw721 = "0.16"
cw721-base = "0.16"
cw-item-set = { version = "0.7", default-features = false, features = ["iterator"] }
cw-storage-plus = "1.0"
# we can't use cw-utils v1.0 because sg1 still uses 0.16
cw-utils = "0.16"
hex = "0.4"
schemars = "0.8"
serde = { version = "1.0", default-features = false }
sg1 = "0.14"
sg721 = "0.14"
# Stargaze team forgot to upload sg721-base v0.14 to crates.io
sg721-base = { git = "https://github.com/public-awesome/launchpad", rev = "4183957" }
sg-metadata = "0.14"
sg-std = "0.14"

[profile.release.package.badge-hub]
codegen-units = 1
incremental = false

[profile.release.package.badge-nft]
codegen-units = 1
incremental = false
sg1 = "0.21"
sg721 = "0.21"
sg721-base = "0.21"
sg-metadata = "0.21"
sg-std = "0.21"
sha2 = "0.10"
thiserror = "1"

[profile.release]
rpath = false
lto = true
overflow-checks = true
opt-level = 3
debug = false
codegen-units = 1
debug = false
debug-assertions = false
incremental = false
lto = true
opt-level = 3
overflow-checks = true
rpath = false
11 changes: 6 additions & 5 deletions contracts/hub/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,20 @@ library = []
badges = { path = "../../packages/badges" }
cosmwasm-schema = { workspace = true }
cosmwasm-std = { workspace = true }
cw-item-set = "0.4"
cw-item-set = { workspace = true }
cw-storage-plus = { workspace = true }
cw-utils = { workspace = true }
cw2 = { workspace = true }
hex = "0.4"
cw721-base = { workspace = true, features = ["library"] }
hex = { workspace = true }
serde = { workspace = true }
sg1 = { workspace = true }
sg721 = { workspace = true }
sg-metadata = { workspace = true }
sg-std = { workspace = true }
sha2 = "0.10"
thiserror = "1"
sha2 = { workspace = true }
thiserror = { workspace = true }

[dev-dependencies]
k256 = "0.10"
k256 = "0.11"
rand = "0.8"
13 changes: 8 additions & 5 deletions contracts/hub/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ use cosmwasm_std::{
};
use sg_std::Response;

use badges::hub::{ExecuteMsg, InstantiateMsg, QueryMsg, SudoMsg};
use badges::Badge;
use badges::{
hub::{ExecuteMsg, InstantiateMsg, QueryMsg, SudoMsg},
Badge,
};

use crate::error::ContractError;
use crate::{execute, query, upgrades};
use crate::{error::ContractError, execute, query, upgrades};

pub const CONTRACT_NAME: &str = "crates.io:badge-hub";
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
Expand Down Expand Up @@ -139,9 +140,11 @@ pub fn migrate(deps: DepsMut, _env: Env, _msg: Empty) -> Result<Response, Contra
return Err(ContractError::incorrect_contract_name(CONTRACT_NAME, contract));
}

// in the previous v1.1 update, we forgot to set the contract version to `1.1.0`
// so for now it's still `1.0.0`
if version != "1.0.0" {
return Err(ContractError::incorrect_contract_version("1.0.0", version));
}

upgrades::v1_1::migrate(deps).map_err(ContractError::from)
upgrades::v1_2::migrate(deps).map_err(ContractError::from)
}
3 changes: 3 additions & 0 deletions contracts/hub/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ pub enum ContractError {
#[error("invalid reply id {0}; must be 1")]
InvalidReplyId(u64),

#[error("not a valid secp256k1 public key")]
InvalidPubkey,

#[error("signature verification failed")]
InvalidSignature,

Expand Down
47 changes: 26 additions & 21 deletions contracts/hub/src/execute.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use std::collections::BTreeSet;

use cosmwasm_std::{to_binary, Addr, DepsMut, Empty, Env, MessageInfo, StdResult, WasmMsg};
use sg721::MintMsg;
use cw721_base::msg::MintMsg;
use sg_metadata::Metadata;
use sg_std::Response;
use badges::{Badge, FeeRate, MintRule};

use crate::error::ContractError;
use crate::fee::handle_fee;
use crate::helpers::*;
use crate::state::*;
use crate::query;
use badges::{Badge, FeeRate, MintRule};

pub const DEFAULT_LIMIT: u32 = 10;
pub const MAX_LIMIT: u32 = 30;
use crate::{
error::ContractError,
fee::handle_fee,
helpers::*,
query,
state::*,
};

pub fn init(deps: DepsMut, developer: Addr, fee_rate: FeeRate) -> StdResult<Response> {
DEVELOPER.save(deps.storage, &developer)?;
Expand All @@ -40,11 +40,6 @@ pub fn set_nft(deps: DepsMut, sender_addr: Addr, nft: &str) -> Result<Response,
NFT.save(deps.storage, &nft_addr)?;

Ok(Response::new()
.add_message(WasmMsg::Execute {
contract_addr: nft_addr.into(),
msg: to_binary(&badges::nft::ExecuteMsg::_Ready {})?,
funds: vec![],
})
.add_attribute("action", "badges/hub/set_nft")
.add_attribute("nft", nft))
}
Expand Down Expand Up @@ -77,6 +72,13 @@ pub fn create_badge(
fee_rate.metadata,
)?;

// if the badge uses "by key" mint rule, the key must be a valid secp256k1
// public key
if let MintRule::ByKey(key) = &badge.rule {
let bytes = hex::decode(key)?;
assert_valid_secp256k1_pubkey(&bytes)?;
}

let id = BADGE_COUNT.update(deps.storage, |id| StdResult::Ok(id + 1))?;
BADGES.save(deps.storage, id, &badge)?;

Expand Down Expand Up @@ -154,7 +156,10 @@ pub fn add_keys(
// save the keys
keys.iter().try_for_each(|key| -> Result<_, ContractError> {
// key must be a of valid hex encoding
hex::decode(key)?;
let bytes = hex::decode(key)?;

// key must be a valid secp256k1 public key
assert_valid_secp256k1_pubkey(&bytes)?;

// the key must not already exist
if KEYS.insert(deps.storage, (id, key))? {
Expand Down Expand Up @@ -186,7 +191,7 @@ pub fn purge_keys(
// because of how Rust works
let res = query::keys(deps.as_ref(), id, None, limit)?;
for key in &res.keys {
KEYS.remove(deps.storage, (id, key));
KEYS.remove(deps.storage, (id, key))?;
};

Ok(Response::new()
Expand All @@ -210,7 +215,7 @@ pub fn purge_owners(
// them because of how Rust works
let res = query::owners(deps.as_ref(), id, None, limit)?;
for owner in &res.owners {
OWNERS.remove(deps.storage, (id, owner));
OWNERS.remove(deps.storage, (id, owner))?;
};

Ok(Response::new()
Expand Down Expand Up @@ -245,7 +250,7 @@ pub fn mint_by_minter(
let serial = start_serial + (idx as u64);
Ok(WasmMsg::Execute {
contract_addr: nft_addr.to_string(),
msg: to_binary(&sg721::ExecuteMsg::Mint(MintMsg::<Option<Empty>> {
msg: to_binary(&sg721::ExecuteMsg::<_, Empty>::Mint(MintMsg::<Option<Empty>> {
token_id: token_id(id, serial),
owner,
token_uri: None,
Expand Down Expand Up @@ -285,7 +290,7 @@ pub fn mint_by_key(
Ok(Response::new()
.add_message(WasmMsg::Execute {
contract_addr: nft_addr.to_string(),
msg: to_binary(&sg721::ExecuteMsg::Mint(MintMsg::<Option<Empty>> {
msg: to_binary(&sg721::ExecuteMsg::<_, Empty>::Mint(MintMsg::<Option<Empty>> {
token_id: token_id(id, badge.current_supply),
// NOTE: it's possible to avoid cloning and save a liiiittle bit of gas here, simply
// by moving this `add_message` after the one `add_attribute` that uses `owner`.
Expand Down Expand Up @@ -321,13 +326,13 @@ pub fn mint_by_keys(
badge.current_supply += 1;
BADGES.save(deps.storage, id, &badge)?;

KEYS.remove(deps.storage, (id, &pubkey));
KEYS.remove(deps.storage, (id, &pubkey))?;
OWNERS.insert(deps.storage, (id, &owner))?;

Ok(Response::new()
.add_message(WasmMsg::Execute {
contract_addr: nft_addr.to_string(),
msg: to_binary(&sg721::ExecuteMsg::Mint(MintMsg::<Option<Empty>> {
msg: to_binary(&sg721::ExecuteMsg::<_, Empty>::Mint(MintMsg::<Option<Empty>> {
token_id: token_id(id, badge.current_supply),
owner: owner.clone(),
token_uri: None,
Expand Down
3 changes: 1 addition & 2 deletions contracts/hub/src/fee.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use cosmwasm_std::{to_binary, MessageInfo, Storage, Uint128, Decimal};
use sg_std::Response;

use crate::error::ContractError;
use crate::state::DEVELOPER;
use crate::{error::ContractError, state::DEVELOPER};

// TODO: add docs
pub fn handle_fee<T: serde::Serialize>(
Expand Down
31 changes: 29 additions & 2 deletions contracts/hub/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@ use sha2::{Digest, Sha256};

use badges::{Badge, MintRule};

use crate::error::ContractError;
use crate::state::{KEYS, OWNERS};
use crate::{
error::ContractError,
state::{KEYS, OWNERS},
};

/// Length of a serialized compressed public key
const ECDSA_COMPRESSED_PUBKEY_LEN: usize = 33;
/// Length of a serialized uncompressed public key
const ECDSA_UNCOMPRESSED_PUBKEY_LEN: usize = 65;

/// Each NFT's token id is simply the badge id and the serial separated by a pipe.
pub fn token_id(id: u64, serial: u64) -> String {
Expand Down Expand Up @@ -168,3 +175,23 @@ pub fn assert_can_mint_by_keys(

Ok(())
}

/// Assert that a byte array is a valid secp256k1 public key.
///
/// Copied from cosmwasm-crypto:
/// https://github.com/CosmWasm/cosmwasm/blob/v1.1.9/packages/crypto/src/secp256k1.rs#L140-L151
///
/// Previously I attempted to use the `k256` library for pubkey validation.
/// But it did not work because `rand` is a non-optional dependency for `k256`.
pub fn assert_valid_secp256k1_pubkey(bytes: &[u8]) -> Result<(), ContractError> {
let ok = match bytes.first() {
Some(0x02) | Some(0x03) => bytes.len() == ECDSA_COMPRESSED_PUBKEY_LEN,
Some(0x04) => bytes.len() == ECDSA_UNCOMPRESSED_PUBKEY_LEN,
_ => false,
};
if ok {
Ok(())
} else {
Err(ContractError::InvalidPubkey)
}
}
3 changes: 0 additions & 3 deletions contracts/hub/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ use badges::hub::{

use crate::state::*;

pub const CONTRACT_NAME: &str = "crates.io:badge-hub";
pub const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");

pub const DEFAULT_LIMIT: u32 = 10;
pub const MAX_LIMIT: u32 = 30;

Expand Down
1 change: 1 addition & 0 deletions contracts/hub/src/upgrades/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pub mod v1_1;
pub mod v1_2;
8 changes: 7 additions & 1 deletion contracts/hub/src/upgrades/v1_1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use sg_std::Response;

use badges::FeeRate;

use crate::state::{BADGES, FEE_RATE};
use crate::{
contract::{CONTRACT_NAME, CONTRACT_VERSION},
state::{BADGES, FEE_RATE},
};

const LEGACY_FEE_PER_BYTE: Item<Decimal> = Item::new("fee_per_byte");

Expand All @@ -28,6 +31,9 @@ pub fn migrate(deps: DepsMut) -> StdResult<Response> {
// extend the minting deadline for badge 3
update_badge_3_expiry(deps.storage)?;

// set the contract version to v1.1.0
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

Ok(Response::new()
.add_attribute("action", "badges/hub/migrate")
.add_attribute("from_version", "1.0.0")
Expand Down
33 changes: 33 additions & 0 deletions contracts/hub/src/upgrades/v1_2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use cosmwasm_std::{DepsMut, StdResult, Storage};
use sg_std::Response;

use badges::MintRule;

use crate::{
contract::{CONTRACT_NAME, CONTRACT_VERSION},
state::BADGES,
};

const NEW_BADGE_17_KEY: &str = "036986114808be5b9f9009754014bdf5ae210cc17c93f4e1d010164be74b8653f4";

pub fn migrate(deps: DepsMut) -> StdResult<Response> {
// correct the claim key of badge 17
update_badge_17_key(deps.storage)?;

// set the contract version to v1.2.0
cw2::set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;

Ok(Response::new()
.add_attribute("action", "badges/hub/migrate")
.add_attribute("from_version", "1.1.0")
.add_attribute("to_version", "1.2.0"))
}

fn update_badge_17_key(store: &mut dyn Storage) -> StdResult<()> {
BADGES.update(store, 17, |opt| -> StdResult<_> {
let mut badge = opt.unwrap();
badge.rule = MintRule::ByKey(NEW_BADGE_17_KEY.into());
Ok(badge)
})?;
Ok(())
}
Loading

0 comments on commit 964af0e

Please sign in to comment.