Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Allow_all_drand_rounds_issue_248 #249

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions contracts/nois-drand/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ backtraces = ["cosmwasm-std/backtraces"]

[dependencies]
nois.workspace = true

drand-common = { path = "../../packages/drand-common" }
cosmwasm-std = { version = "1.2.5", features = ["iterator", "ibc3"] }
cosmwasm-schema = { version = "1.2.5" }
Expand Down
61 changes: 38 additions & 23 deletions contracts/nois-drand/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
use cosmwasm_std::{
ensure_eq, entry_point, to_binary, Attribute, BankMsg, Coin, CosmosMsg, Deps, DepsMut, Empty,
Env, HexBinary, MessageInfo, Order, QueryResponse, Response, StdError, StdResult, Uint128,
WasmMsg,
WasmMsg, WasmQuery,
};
use cw_storage_plus::Bound;
use drand_common::{is_valid, DRAND_MAINNET2_PUBKEY};
use drand_common::{is_valid, DrandJobStats, DrandJobStatsResponse, DRAND_MAINNET2_PUBKEY};
use drand_verify::{derive_randomness, G2Pubkey, Pubkey};

use crate::attributes::{
Expand Down Expand Up @@ -359,7 +359,7 @@ fn execute_add_round(
Attribute::new(ATTR_BOT, info.sender.to_string()),
];

// Execute the callback jobs and incentivise the drand bot based on howmany jobs they process
// Execute the callback jobs and incentivise the drand bot based on how many jobs they process

let mut out_msgs = Vec::<CosmosMsg>::new();
if let Some(gateway) = config.gateway {
Expand All @@ -386,7 +386,11 @@ fn execute_add_round(

let correct_group = Some(group(&info.sender)) == eligible_group(round);

let is_eligible = correct_group && is_registered && is_allowlisted && reward_points != 0; // Allowed and registered bot that gathered reward points get incentives
let is_eligible = correct_group
&& is_registered
&& is_allowlisted
&& reward_points != 0
&& is_round_requested_by_job(round, deps.as_ref());

if !is_eligible {
reward_points = 0;
Expand Down Expand Up @@ -448,6 +452,27 @@ fn execute_add_round(
.add_attributes(attributes))
}

// incentivise on modulo_10 and requested rounds

fn is_round_requested_by_job(round: u64, deps: Deps) -> bool {
// TODO handle unsafe unwrap
get_drand_job_response(deps, round).unwrap().unprocessed > 0
|| get_drand_job_response(deps, round).unwrap().processed > 0
}

fn get_drand_job_response(deps: Deps, round: u64) -> StdResult<DrandJobStatsResponse> {
// Loading here config twice, a more optimised but less readable way is to pass the gateway info all along these functions as param to avoid calling the state twice
let config = CONFIG.load(deps.storage)?;
let msg = DrandJobStats { round };
let wasm = WasmQuery::Smart {
// TODO handle this unsafe unwrap
contract_addr: config.gateway.unwrap().into_string(),
msg: to_binary(&msg)?,
};
let drand_job_response: DrandJobStatsResponse = deps.querier.query(&wasm.into())?;
Ok(drand_job_response)
}

fn execute_set_config(
deps: DepsMut,
info: MessageInfo,
Expand Down Expand Up @@ -610,25 +635,6 @@ mod tests {
);
}

#[test]
fn add_round_fails_when_round_invalid() {
let mut deps = mock_dependencies();

let msg = InstantiateMsg {
manager: TESTING_MANAGER.to_string(),
min_round: TESTING_MIN_ROUND,
incentive_point_price: Uint128::new(20_000),
incentive_denom: "unois".to_string(),
};
let info = mock_info("creator", &[]);
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(0, res.messages.len());

let msg = make_add_round_msg(8);
let err = execute(deps.as_mut(), mock_env(), mock_info("anyone", &[]), msg).unwrap_err();
assert!(matches!(err, ContractError::RoundInvalid { round: 8 }));
}

#[test]
fn add_round_fails_when_round_too_low() {
let mut deps = mock_dependencies();
Expand Down Expand Up @@ -1911,4 +1917,13 @@ mod tests {
}
);
}

// TODO
#[test]
fn only_incentivising_rounds_give_incentives() {

// Assert modulo_10_rounds_give_incentives
// Assert requested_rounds_are_incentivised
// rounds that are not modulo_10 and are not requested are not incentivised
}
}
5 changes: 3 additions & 2 deletions contracts/nois-gateway/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use cosmwasm_std::{
WasmQuery,
};
use cw_storage_plus::Bound;
use drand_common::DrandJobStatsResponse;
use nois_protocol::{
check_order, check_version, InPacket, InPacketAck, OutPacket, OutPacketAck, StdAck,
BEACON_PRICE_PACKET_LIFETIME, IBC_APP_VERSION, WELCOME_PACKET_LIFETIME,
Expand All @@ -17,8 +18,8 @@ use sha2::{Digest, Sha256};
use crate::error::ContractError;
use crate::job_id::validate_origin;
use crate::msg::{
ConfigResponse, CustomerResponse, CustomersResponse, DrandJobStatsResponse, ExecuteMsg,
InstantiateMsg, QueriedCustomer, QueryMsg,
ConfigResponse, CustomerResponse, CustomersResponse, ExecuteMsg, InstantiateMsg,
QueriedCustomer, QueryMsg,
};
use crate::request_router::{NewDrand, RequestRouter, RoutingReceipt};
use crate::state::{
Expand Down
10 changes: 1 addition & 9 deletions contracts/nois-gateway/src/msg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use cosmwasm_schema::{cw_serde, QueryResponses};
use cosmwasm_std::{Addr, Coin, HexBinary};
use drand_common::DrandJobStatsResponse;

use crate::state::{Config, Customer};

Expand Down Expand Up @@ -59,15 +60,6 @@ pub enum QueryMsg {
// We define a custom struct for each query response
pub type ConfigResponse = Config;

#[cw_serde]
pub struct DrandJobStatsResponse {
pub round: u64,
/// Number of unprocessed jobs
pub unprocessed: u32,
/// Number of processed jobs
pub processed: u32,
}

#[cw_serde]
pub struct QueriedCustomer {
pub channel_id: String,
Expand Down
22 changes: 11 additions & 11 deletions contracts/nois-gateway/src/request_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use cosmwasm_std::{
to_binary, Binary, CosmosMsg, DepsMut, Env, HexBinary, IbcMsg, StdError, StdResult, Timestamp,
};
use drand_common::{time_of_round, valid_round_after, DRAND_CHAIN_HASH};
use drand_common::{round_after, time_of_round, DRAND_CHAIN_HASH};
use nois_protocol::{InPacketAck, OutPacket, StdAck, DELIVER_BEACON_PACKET_LIFETIME};

use crate::{
Expand Down Expand Up @@ -160,7 +160,7 @@ fn create_deliver_beacon_ibc_message(

/// Calculates the next round in the future, i.e. publish time > base time.
fn commit_to_drand_round(after: Timestamp) -> (u64, String) {
let round = valid_round_after(after);
let round = round_after(after);
let source_id = format!("drand:{}:{}", DRAND_CHAIN_HASH, round);
(round, source_id)
}
Expand All @@ -173,42 +173,42 @@ mod tests {
fn commit_to_drand_round_works() {
// UNIX epoch
let (round, source) = commit_to_drand_round(Timestamp::from_seconds(0));
assert_eq!(round, 10);
assert_eq!(round, 1);
assert_eq!(
source,
"drand:dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493:10"
"drand:dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493:1"
);

// Before Drand genesis (https://api3.drand.sh/dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493/info)
let (round, source) =
commit_to_drand_round(Timestamp::from_seconds(1677685200).minus_nanos(1));
assert_eq!(round, 10);
assert_eq!(round, 1);
assert_eq!(
source,
"drand:dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493:10"
"drand:dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493:1"
);

// At Drand genesis (https://api3.drand.sh/dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493/info)
let (round, source) = commit_to_drand_round(Timestamp::from_seconds(1677685200));
assert_eq!(round, 10);
assert_eq!(round, 2);
assert_eq!(
source,
"drand:dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493:10"
"drand:dbd506d6ef76e5f386f41c651dcb808c5bcbd75471cc4eafa3f4df7ad4e4c493:2"
);

// After Drand genesis
let (round, _) = commit_to_drand_round(Timestamp::from_seconds(1677685200).plus_nanos(1));
assert_eq!(round, 10);
assert_eq!(round, 2);

// Drand genesis +29s/30s/31s
let (round, _) =
commit_to_drand_round(Timestamp::from_seconds(1677685200).plus_seconds(26));
assert_eq!(round, 10);
let (round, _) =
commit_to_drand_round(Timestamp::from_seconds(1677685200).plus_seconds(27));
assert_eq!(round, 20);
assert_eq!(round, 11);
let (round, _) =
commit_to_drand_round(Timestamp::from_seconds(1677685200).plus_seconds(28));
assert_eq!(round, 20);
assert_eq!(round, 11);
}
}
1 change: 1 addition & 0 deletions packages/drand-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ edition = "2021"

[dependencies]
cosmwasm-std = { version = "1.2.5", features = ["iterator", "ibc3"] }
cosmwasm-schema = { version = "1.2.5" }

[dev-dependencies]
15 changes: 15 additions & 0 deletions packages/drand-common/src/jobs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use cosmwasm_schema::cw_serde;

#[cw_serde]
pub struct DrandJobStatsResponse {
pub round: u64,
/// Number of unprocessed jobs
pub unprocessed: u32,
/// Number of processed jobs
pub processed: u32,
}

#[cw_serde]
pub struct DrandJobStats {
pub round: u64,
}
4 changes: 3 additions & 1 deletion packages/drand-common/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod jobs;
mod rounds;
pub mod testing;

pub use rounds::{is_valid, time_of_round, valid_round_after};
pub use jobs::{DrandJobStats, DrandJobStatsResponse};
pub use rounds::{is_valid, round_after, time_of_round};

use cosmwasm_std::Timestamp;

Expand Down
71 changes: 11 additions & 60 deletions packages/drand-common/src/rounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub fn time_of_round(round: u64) -> Timestamp {
DRAND_GENESIS.plus_nanos((round - 1) * DRAND_ROUND_LENGTH)
}

fn round_after(base: Timestamp) -> u64 {
pub fn round_after(base: Timestamp) -> u64 {
// Losely ported from https://github.com/drand/drand/blob/eb36ba81e3f28c966f95bcd602f60e7ff8ef4c35/chain/time.go#L49-L63
if base < DRAND_GENESIS {
1
Expand All @@ -19,28 +19,13 @@ fn round_after(base: Timestamp) -> u64 {
}
}

/// Returns the next round after the timestamp which can be divided by the divisor.
fn round_after_divisor(base: Timestamp, divisor: u64) -> u64 {
let round = round_after(base);
let remainder = round % divisor;
if remainder != 0 {
round + divisor - remainder
} else {
round
}
}

pub fn valid_round_after(base: Timestamp) -> u64 {
round_after_divisor(base, 10)
}

/// Returns true if and only if the round number is valid for Nois.
/// For mainnet launch, every 10th round is considered valid.
///
/// If round is 0, this returns false because there is no 0 round in drand.
#[inline]
pub fn is_valid(round: u64) -> bool {
round != 0 && round % 10 == 0
round != 0
}

#[cfg(test)]
Expand Down Expand Up @@ -91,61 +76,27 @@ mod tests {
fn round_after_divisor_works() {
// Before Drand genesis
let before = Timestamp::from_seconds(1677685200).minus_seconds(1);
assert_eq!(round_after_divisor(before, 1), 1);
assert_eq!(round_after_divisor(before, 2), 2);
assert_eq!(round_after_divisor(before, 10), 10);
assert_eq!(round_after_divisor(before, 700), 700);
assert_eq!(round_after(before), 1);

// At Drand genesis
let genesis = Timestamp::from_seconds(1677685200);
assert_eq!(round_after_divisor(genesis, 1), 2);
assert_eq!(round_after_divisor(genesis, 2), 2);
assert_eq!(round_after_divisor(genesis, 10), 10);
assert_eq!(round_after_divisor(genesis, 700), 700);
assert_eq!(round_after(genesis), 2);

let after5 = genesis.plus_seconds(5);
assert_eq!(round_after_divisor(after5, 1), 3);
assert_eq!(round_after_divisor(after5, 3), 3);
assert_eq!(round_after_divisor(after5, 10), 10);
assert_eq!(round_after(after5,), 3);

let after8 = genesis.plus_seconds(8);
assert_eq!(round_after(after8,), 4);

let later = genesis.plus_seconds(299);
assert_eq!(round_after_divisor(later, 1), 101);
assert_eq!(round_after_divisor(later, 10), 110);
assert_eq!(round_after(later,), 101);
}

#[test]
fn is_valid_works() {
assert!(!is_valid(0)); // no 0 round exists in drand
assert!(!is_valid(1));
assert!(!is_valid(2));
assert!(!is_valid(3));
assert!(!is_valid(4));
assert!(!is_valid(5));
assert!(!is_valid(6));
assert!(!is_valid(7));
assert!(!is_valid(8));
assert!(!is_valid(9));
assert!(is_valid(10));
assert!(!is_valid(11));
assert!(!is_valid(12));
assert!(!is_valid(13));
assert!(!is_valid(14));
assert!(!is_valid(15));
assert!(!is_valid(16));
assert!(!is_valid(17));
assert!(!is_valid(18));
assert!(!is_valid(19));
assert!(is_valid(20));
assert!(!is_valid(21));
assert!(!is_valid(22));
assert!(!is_valid(23));
assert!(!is_valid(24));
assert!(!is_valid(25));
assert!(!is_valid(26));
assert!(!is_valid(27));
assert!(!is_valid(28));
assert!(!is_valid(29));
assert!(is_valid(1));
assert!(is_valid(30));
assert!(!is_valid(31));
assert!(is_valid(31));
}
}