mirror of
https://github.com/d0zingcat/ore.git
synced 2026-06-06 23:26:46 +00:00
Move to new supply model
This commit is contained in:
121
Cargo.lock
generated
121
Cargo.lock
generated
@@ -2,21 +2,6 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "addr2line"
|
|
||||||
version = "0.21.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
|
|
||||||
dependencies = [
|
|
||||||
"gimli",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "adler"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aead"
|
name = "aead"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -388,17 +373,6 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
|
checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-trait"
|
|
||||||
version = "0.1.76"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.43",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@@ -416,21 +390,6 @@ version = "1.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "backtrace"
|
|
||||||
version = "0.3.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
|
|
||||||
dependencies = [
|
|
||||||
"addr2line",
|
|
||||||
"cc",
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"miniz_oxide",
|
|
||||||
"object",
|
|
||||||
"rustc-demangle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
@@ -521,12 +480,6 @@ version = "0.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bnum"
|
|
||||||
version = "0.10.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "56953345e39537a3e18bdaeba4cb0c58a78c1f61f361dc0fa7c5c7340ae87c5f"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "borsh"
|
name = "borsh"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@@ -674,12 +627,6 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bytes"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.83"
|
version = "1.0.83"
|
||||||
@@ -1010,12 +957,6 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gimli"
|
|
||||||
version = "0.28.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
@@ -1058,12 +999,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hex"
|
|
||||||
version = "0.4.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hmac"
|
name = "hmac"
|
||||||
version = "0.8.1"
|
version = "0.8.1"
|
||||||
@@ -1298,15 +1233,6 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "miniz_oxide"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
|
||||||
dependencies = [
|
|
||||||
"adler",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-bigint"
|
name = "num-bigint"
|
||||||
version = "0.4.4"
|
version = "0.4.4"
|
||||||
@@ -1401,15 +1327,6 @@ dependencies = [
|
|||||||
"syn 2.0.43",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "object"
|
|
||||||
version = "0.32.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "once_cell"
|
name = "once_cell"
|
||||||
version = "1.19.0"
|
version = "1.19.0"
|
||||||
@@ -1428,9 +1345,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anchor-lang",
|
"anchor-lang",
|
||||||
"anchor-spl",
|
"anchor-spl",
|
||||||
"bnum",
|
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"sha256",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1486,12 +1401,6 @@ version = "2.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.2.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polyval"
|
name = "polyval"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -1715,12 +1624,6 @@ version = "0.8.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustc-demangle"
|
|
||||||
version = "0.1.23"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-hash"
|
name = "rustc-hash"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
@@ -1846,19 +1749,6 @@ dependencies = [
|
|||||||
"digest 0.10.7",
|
"digest 0.10.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sha256"
|
|
||||||
version = "1.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7895c8ae88588ccead14ff438b939b0c569cd619116f14b4d13fdff7b8333386"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"bytes",
|
|
||||||
"hex",
|
|
||||||
"sha2 0.10.8",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha3"
|
name = "sha3"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
@@ -2468,17 +2358,6 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio"
|
|
||||||
version = "1.35.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
|
|
||||||
dependencies = [
|
|
||||||
"backtrace",
|
|
||||||
"bytes",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.5.11"
|
version = "0.5.11"
|
||||||
|
|||||||
@@ -24,8 +24,6 @@ default = []
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
anchor-lang = "0.29.0"
|
anchor-lang = "0.29.0"
|
||||||
anchor-spl = { version = "0.29.0", features = ["token"] }
|
anchor-spl = { version = "0.29.0", features = ["token"] }
|
||||||
bnum = "0.10.0"
|
|
||||||
sha256 = "1.1.3"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
|||||||
@@ -1,103 +1,183 @@
|
|||||||
|
use std::mem::size_of;
|
||||||
|
|
||||||
use anchor_lang::{
|
use anchor_lang::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
solana_program::{system_program, sysvar},
|
solana_program::{
|
||||||
|
hash::{hashv, Hash},
|
||||||
|
system_program, sysvar,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use anchor_spl::token::{self, Mint, MintTo, TokenAccount};
|
use anchor_spl::token::{self, Mint, MintTo, TokenAccount};
|
||||||
use bnum::types::U256;
|
|
||||||
use sha256;
|
|
||||||
|
|
||||||
declare_id!("CeJShZEAzBLwtcLQvbZc7UT38e4nUTn63Za5UFyYYDTS");
|
declare_id!("CeJShZEAzBLwtcLQvbZc7UT38e4nUTn63Za5UFyYYDTS");
|
||||||
|
|
||||||
/// The number of hashes per epoch (~24 hours).
|
// TODO Set this to a reasonable value.
|
||||||
pub const EPOCH_HEIGHT: u64 = 8640;
|
pub const DIFFICULTY: Hash = Hash::new_from_array([
|
||||||
|
0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
/// The target average duration in seconds between each valid hash.
|
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||||
pub const HASH_TIME: u64 = 10;
|
]);
|
||||||
|
|
||||||
/// The seed of the genesis hash.
|
|
||||||
pub const GENESIS: &str = "42";
|
|
||||||
|
|
||||||
/// The seed of the Metadata program derived address.
|
|
||||||
pub const METADATA: &[u8] = b"metadata";
|
|
||||||
|
|
||||||
/// The decimal precision of the Ore token.
|
|
||||||
pub const TOKEN_DECIMALS: u8 = 8;
|
|
||||||
|
|
||||||
/// The radix of U256 string encodings.
|
|
||||||
pub const RADIX: u32 = 16;
|
|
||||||
|
|
||||||
/// The coefficient of the supply function.
|
|
||||||
pub const SUPPLY_COEFFICIENT: u64 = 48484;
|
|
||||||
|
|
||||||
/// The exponent of the supply function.
|
|
||||||
pub const SUPPLY_EXPONENT: f64 = 0.618;
|
|
||||||
|
|
||||||
/// The smoothing factor for difficulty adjustments.
|
|
||||||
pub const SMOOTHING_FACTOR: U256 = U256::from_digit(4);
|
|
||||||
|
|
||||||
// TODO Set this before deployment
|
// TODO Set this before deployment
|
||||||
/// The time after which mining can happen.
|
/// The time after which mining can happen.
|
||||||
pub const START_AT: i64 = 0;
|
pub const START_AT: i64 = 0;
|
||||||
|
|
||||||
|
pub const EPOCH_DURATION: i64 = 60;
|
||||||
|
|
||||||
|
pub const EXPECTED_EPOCH_REWARDS: u64 = 10u64.pow(TOKEN_DECIMALS as u32); // 1 ORE / epoch
|
||||||
|
|
||||||
|
pub const SMOOTHING_FACTOR: u64 = 256;
|
||||||
|
|
||||||
|
/// The decimal precision of the Ore token.
|
||||||
|
// If we use a decimal precision of 16, we can fit 10_000_000_000_000_000 (10 quadrillion) hashes in each minute.
|
||||||
|
// This is sufficiently far beyond what Solana is capable of processing.
|
||||||
|
// Max supply would still be very large and take millions of years to reach at a rate of 1 ORE / minute.
|
||||||
|
// We will not have to implement a variable difficulty to maintain the 1 ORE / min average.
|
||||||
|
// If token decimals were only 8, we likely would need a variable difficulty at some point.
|
||||||
|
pub const TOKEN_DECIMALS: u8 = 9;
|
||||||
|
|
||||||
|
pub const BUS_COUNT: u64 = 8;
|
||||||
|
|
||||||
|
// TODO Use 8,9,or 10 decimals. Test and run math to see which one makes the most sense.
|
||||||
|
// TODO Track number of successful hashes per bus and add limit to # hashes/bus exceeding EXPECTED_EPOCH_REWARDS/NUMBER_OF_BUSSES.
|
||||||
|
// Rewards per epoch can exceed target limit. But hashes cannot exceed theoretical maximum.
|
||||||
|
// Eg. scenario where reward_rate = 1, cannot go lower, and the network is submitting enough hashes to push issuance rate above 1 ORE / epoch.
|
||||||
|
// In this case, each valid hash is earning the smallest reward possible.
|
||||||
|
// By hard limitting the number of hashes per bus per epoch, we prevent the 1 ORE/epoch limit from being fundamentally broken.
|
||||||
|
|
||||||
#[program]
|
#[program]
|
||||||
mod ore {
|
mod ore {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// Initializes the program. Can only be executed once.
|
/// Initializes the program. Can only be executed once.
|
||||||
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
|
pub fn initialize_metadata(ctx: Context<InitializeMetadata>) -> Result<()> {
|
||||||
let metadata = &mut ctx.accounts.metadata;
|
ctx.accounts.metadata.bump = ctx.bumps.metadata;
|
||||||
metadata.bump = ctx.bumps.metadata;
|
ctx.accounts.metadata.reward_rate = 10u64.pow(TOKEN_DECIMALS.saturating_div(2) as u32);
|
||||||
metadata.difficulty = U256::MAX.to_str_radix(RADIX);
|
ctx.accounts.metadata.mint = ctx.accounts.mint.key();
|
||||||
metadata.hash = sha256::digest(GENESIS.to_string());
|
|
||||||
metadata.height = 0;
|
|
||||||
metadata.mint = ctx.accounts.mint.key();
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mints new Ore to the beneficiary if a valid hash is provided.
|
/// Initializes the program. Can only be executed once.
|
||||||
pub fn mine(ctx: Context<Mine>, hash: String, nonce: u64) -> Result<()> {
|
pub fn initialize_busses(ctx: Context<InitializeBusses>) -> Result<()> {
|
||||||
// Validate clock.
|
ctx.accounts.bus_0.bump = ctx.bumps.bus_0;
|
||||||
let clock = Clock::get().unwrap();
|
ctx.accounts.bus_0.id = 0;
|
||||||
require!(clock.unix_timestamp.ge(&START_AT), ProgramError::NotStarted);
|
ctx.accounts.bus_1.bump = ctx.bumps.bus_1;
|
||||||
|
ctx.accounts.bus_1.id = 1;
|
||||||
|
ctx.accounts.bus_2.bump = ctx.bumps.bus_2;
|
||||||
|
ctx.accounts.bus_2.id = 2;
|
||||||
|
ctx.accounts.bus_3.bump = ctx.bumps.bus_3;
|
||||||
|
ctx.accounts.bus_3.id = 3;
|
||||||
|
ctx.accounts.bus_4.bump = ctx.bumps.bus_4;
|
||||||
|
ctx.accounts.bus_4.id = 4;
|
||||||
|
ctx.accounts.bus_5.bump = ctx.bumps.bus_5;
|
||||||
|
ctx.accounts.bus_5.id = 5;
|
||||||
|
ctx.accounts.bus_6.bump = ctx.bumps.bus_6;
|
||||||
|
ctx.accounts.bus_6.id = 6;
|
||||||
|
ctx.accounts.bus_7.bump = ctx.bumps.bus_7;
|
||||||
|
ctx.accounts.bus_7.id = 7;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// Log request.
|
pub fn register_miner(ctx: Context<RegisterMiner>) -> Result<()> {
|
||||||
|
let miner = &mut ctx.accounts.miner;
|
||||||
|
miner.authority = ctx.accounts.signer.key();
|
||||||
|
miner.bump = ctx.bumps.miner;
|
||||||
|
miner.hash = hashv(&[&ctx.accounts.signer.key().to_bytes()]);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_epoch(ctx: Context<StartEpoch>) -> Result<()> {
|
||||||
|
// Validate epoch has ended.
|
||||||
|
let clock = Clock::get().unwrap();
|
||||||
let metadata = &mut ctx.accounts.metadata;
|
let metadata = &mut ctx.accounts.metadata;
|
||||||
msg!("Difficulty: {}", metadata.difficulty);
|
let epoch_end_at = metadata.epoch_start_at.saturating_add(EPOCH_DURATION);
|
||||||
msg!("Hash: {}", hash);
|
require!(
|
||||||
msg!("Nonce: {}", nonce);
|
clock.unix_timestamp.ge(&epoch_end_at),
|
||||||
|
ProgramError::ClockInvalid
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calculate total rewards issued during the epoch.
|
||||||
|
let bus_0 = &mut ctx.accounts.bus_0;
|
||||||
|
let bus_1 = &mut ctx.accounts.bus_1;
|
||||||
|
let bus_2 = &mut ctx.accounts.bus_2;
|
||||||
|
let bus_3 = &mut ctx.accounts.bus_3;
|
||||||
|
let bus_4 = &mut ctx.accounts.bus_4;
|
||||||
|
let bus_5 = &mut ctx.accounts.bus_5;
|
||||||
|
let bus_6 = &mut ctx.accounts.bus_6;
|
||||||
|
let bus_7 = &mut ctx.accounts.bus_7;
|
||||||
|
let total_epoch_rewards = bus_0
|
||||||
|
.rewards
|
||||||
|
.saturating_add(bus_1.rewards)
|
||||||
|
.saturating_add(bus_2.rewards)
|
||||||
|
.saturating_add(bus_3.rewards)
|
||||||
|
.saturating_add(bus_4.rewards)
|
||||||
|
.saturating_add(bus_5.rewards)
|
||||||
|
.saturating_add(bus_6.rewards)
|
||||||
|
.saturating_add(bus_7.rewards);
|
||||||
|
|
||||||
|
// Update the reward amount for the next epoch.
|
||||||
|
metadata.reward_rate = calculate_new_reward_rate(metadata.reward_rate, total_epoch_rewards);
|
||||||
|
|
||||||
|
// Reset state for new epoch.
|
||||||
|
bus_0.hashes = 0;
|
||||||
|
bus_1.hashes = 0;
|
||||||
|
bus_2.hashes = 0;
|
||||||
|
bus_3.hashes = 0;
|
||||||
|
bus_4.hashes = 0;
|
||||||
|
bus_5.hashes = 0;
|
||||||
|
bus_6.hashes = 0;
|
||||||
|
bus_7.hashes = 0;
|
||||||
|
bus_0.hashes = 0;
|
||||||
|
bus_1.rewards = 0;
|
||||||
|
bus_2.rewards = 0;
|
||||||
|
bus_3.rewards = 0;
|
||||||
|
bus_4.rewards = 0;
|
||||||
|
bus_5.rewards = 0;
|
||||||
|
bus_6.rewards = 0;
|
||||||
|
bus_7.rewards = 0;
|
||||||
|
metadata.epoch_start_at = clock.unix_timestamp;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mine(ctx: Context<Mine>, hash: Hash, nonce: u64) -> Result<()> {
|
||||||
|
// Validate epoch is active.
|
||||||
|
let clock = Clock::get().unwrap();
|
||||||
|
let metadata = &mut ctx.accounts.metadata;
|
||||||
|
let epoch_end_at = metadata.epoch_start_at.saturating_add(EPOCH_DURATION);
|
||||||
|
require!(
|
||||||
|
clock.unix_timestamp.lt(&epoch_end_at),
|
||||||
|
ProgramError::EpochNotActive
|
||||||
|
);
|
||||||
|
|
||||||
// Validate hash.
|
// Validate hash.
|
||||||
let difficulty = U256::parse_str_radix(&metadata.difficulty, RADIX);
|
let miner = &mut ctx.accounts.miner;
|
||||||
validate_hash(
|
validate_hash(
|
||||||
metadata.hash.clone(),
|
miner.hash.clone(),
|
||||||
hash.clone(),
|
hash.clone(),
|
||||||
ctx.accounts.signer.key(),
|
ctx.accounts.signer.key(),
|
||||||
nonce,
|
nonce,
|
||||||
difficulty,
|
DIFFICULTY,
|
||||||
)?;
|
)?;
|
||||||
msg!("Hash is valid");
|
|
||||||
|
|
||||||
// Update metadata.
|
// Update state.
|
||||||
metadata.hash = hash.clone();
|
ctx.accounts.bus.hashes = ctx.accounts.bus.hashes.saturating_add(1);
|
||||||
metadata.height = metadata.height.checked_add(1).unwrap();
|
ctx.accounts.bus.rewards = ctx
|
||||||
if metadata.height.eq(&1) {
|
.accounts
|
||||||
metadata.epoch_start_at = clock.unix_timestamp;
|
.bus
|
||||||
}
|
.rewards
|
||||||
|
.saturating_add(metadata.reward_rate);
|
||||||
|
miner.hash = hash.clone();
|
||||||
|
|
||||||
// Update difficulty, if new epoch.
|
// Error if this bus has already processed its quota of hashes for the epoch.
|
||||||
if metadata.height % (EPOCH_HEIGHT as u128) == 0 {
|
require!(
|
||||||
metadata.difficulty =
|
ctx.accounts
|
||||||
calculate_new_difficulty(metadata.epoch_start_at, clock.unix_timestamp, difficulty)
|
.bus
|
||||||
.expect("Failed to calculate new difficulty")
|
.hashes
|
||||||
.to_str_radix(RADIX);
|
.le(&EXPECTED_EPOCH_REWARDS.saturating_div(BUS_COUNT)),
|
||||||
metadata.epoch_start_at = clock.unix_timestamp;
|
// TODO Needs a dedicated error
|
||||||
}
|
ProgramError::HashInvalid
|
||||||
|
);
|
||||||
|
|
||||||
// Mint reward to beneficiary.
|
// Mint reward to beneficiary.
|
||||||
let supply = calculate_supply(metadata.height);
|
|
||||||
let reward = supply
|
|
||||||
.checked_sub(ctx.accounts.mint.supply)
|
|
||||||
.expect("Failed to calculate reward amount");
|
|
||||||
token::mint_to(
|
token::mint_to(
|
||||||
CpiContext::new_with_signer(
|
CpiContext::new_with_signer(
|
||||||
ctx.accounts.token_program.to_account_info(),
|
ctx.accounts.token_program.to_account_info(),
|
||||||
@@ -108,105 +188,127 @@ mod ore {
|
|||||||
},
|
},
|
||||||
&[&[METADATA, &[metadata.bump]]],
|
&[&[METADATA, &[metadata.bump]]],
|
||||||
),
|
),
|
||||||
reward,
|
metadata.reward_rate,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Log result.
|
|
||||||
msg!("Height: {}", metadata.height);
|
|
||||||
msg!("Reward: {}", reward);
|
|
||||||
msg!("Supply: {}", supply);
|
|
||||||
emit!(MineEvent {
|
|
||||||
signer: ctx.accounts.signer.key(),
|
|
||||||
beneficiary: ctx.accounts.beneficiary.key(),
|
|
||||||
height: metadata.height,
|
|
||||||
reward,
|
|
||||||
supply,
|
|
||||||
hash,
|
|
||||||
nonce,
|
|
||||||
difficulty: metadata.difficulty.clone(),
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_hash(
|
fn validate_hash(
|
||||||
current_hash: String,
|
current_hash: Hash,
|
||||||
hash: String,
|
hash: Hash,
|
||||||
signer: Pubkey,
|
signer: Pubkey,
|
||||||
nonce: u64,
|
nonce: u64,
|
||||||
difficulty: U256,
|
difficulty: Hash,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Validate hash correctness.
|
// Validate hash correctness.
|
||||||
let msg = format!("{}-{}-{}", current_hash, signer, nonce);
|
let bytes = [
|
||||||
require!(sha256::digest(msg).eq(&hash), ProgramError::HashInvalid);
|
current_hash.to_bytes().as_slice(),
|
||||||
|
signer.to_bytes().as_slice(),
|
||||||
|
nonce.to_be_bytes().as_slice(),
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
let hash_ = hashv(&[&bytes]);
|
||||||
|
require!(hash.eq(&hash_), ProgramError::HashInvalid);
|
||||||
|
|
||||||
// Validate hash difficulty.
|
// Validate hash difficulty.
|
||||||
let hash_u256 = U256::parse_str_radix(&hash, RADIX);
|
require!(hash.le(&difficulty), ProgramError::HashInvalid);
|
||||||
require!(hash_u256.le(&difficulty), ProgramError::HashInvalid);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_new_difficulty(t1: i64, t2: i64, difficulty: U256) -> Result<U256> {
|
fn calculate_new_reward_rate(current_rate: u64, epoch_rewards: u64) -> u64 {
|
||||||
// Calculate time ratio.
|
// Avoid division by zero. Leave the reward rate unchanged.
|
||||||
require!(t2.gt(&t1), ProgramError::ClockInvalid);
|
if epoch_rewards.eq(&0) {
|
||||||
let actual_time = t2.saturating_sub(t1) as f64;
|
return current_rate;
|
||||||
let expected_time = EPOCH_HEIGHT.saturating_mul(HASH_TIME) as f64;
|
}
|
||||||
let time_ratio = actual_time / expected_time;
|
|
||||||
|
|
||||||
// Scale time ratio for integer arithmetic.
|
// Calculate new rate.
|
||||||
const SCALE_FACTOR: f64 = 1000f64;
|
msg!("Current rate: {}", current_rate);
|
||||||
let time_ratio_scaled = U256::from_digit((time_ratio * SCALE_FACTOR) as u64);
|
msg!("Epoch rewards: {}", epoch_rewards);
|
||||||
|
msg!("Expected rewards: {}", EXPECTED_EPOCH_REWARDS);
|
||||||
|
let new_rate = (current_rate as u128)
|
||||||
|
.saturating_mul(EXPECTED_EPOCH_REWARDS as u128)
|
||||||
|
.saturating_div(epoch_rewards as u128) as u64;
|
||||||
|
msg!("New rate: {}", new_rate);
|
||||||
|
|
||||||
// Calculate new difficulty.
|
// Smooth reward rate to not change by more than a constant factor from one epoch to the next.
|
||||||
const SCALE_FACTOR_U256: U256 = U256::from_digit(SCALE_FACTOR as u64);
|
let new_rate_min = current_rate.saturating_div(SMOOTHING_FACTOR);
|
||||||
let new_difficulty_scaled = difficulty.saturating_mul(time_ratio_scaled);
|
let new_rate_max = current_rate.saturating_mul(SMOOTHING_FACTOR);
|
||||||
let new_difficulty = new_difficulty_scaled.saturating_div(SCALE_FACTOR_U256);
|
let new_rate_smoothed = new_rate_min.max(new_rate_max.min(new_rate));
|
||||||
|
msg!("New rate min: {}", new_rate_min);
|
||||||
|
msg!("New rate max: {}", new_rate_max);
|
||||||
|
msg!("New rate smoothed: {}", new_rate_smoothed);
|
||||||
|
|
||||||
// Smooth new difficulty to a min/max multiple of the old difficulty.
|
// Prevent new reward from reaching 0 and return.
|
||||||
let new_difficulty_min = difficulty.saturating_div(SMOOTHING_FACTOR);
|
new_rate_smoothed.max(1)
|
||||||
let new_difficulty_max = difficulty.saturating_mul(SMOOTHING_FACTOR);
|
|
||||||
let new_difficulty_smoothed = new_difficulty_min.max(new_difficulty_max.min(new_difficulty));
|
|
||||||
Ok(new_difficulty_smoothed)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calculate_supply(height: u128) -> u64 {
|
/// The seed of the Bus account PDA.
|
||||||
((SUPPLY_COEFFICIENT as f64 * (height as f64).powf(SUPPLY_EXPONENT))
|
pub const BUS: &[u8] = b"bus";
|
||||||
* 10f64.powf(TOKEN_DECIMALS as f64)) as u64
|
|
||||||
}
|
/// The seed of the Metadata account PDA.
|
||||||
|
pub const METADATA: &[u8] = b"metadata";
|
||||||
|
|
||||||
|
/// The seed of the Miner account PDA.
|
||||||
|
pub const MINER: &[u8] = b"miner";
|
||||||
|
|
||||||
#[account]
|
#[account]
|
||||||
#[derive(Debug, InitSpace)]
|
#[derive(Debug)]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
/// The bump of the metadata account address.
|
/// The bump of the metadata PDA.
|
||||||
pub bump: u8,
|
pub bump: u8,
|
||||||
|
|
||||||
/// The current mining difficulty.
|
|
||||||
#[max_len(256)]
|
|
||||||
pub difficulty: String,
|
|
||||||
|
|
||||||
/// The current hash.
|
|
||||||
#[max_len(256)]
|
|
||||||
pub hash: String,
|
|
||||||
|
|
||||||
/// The current height of the hash chain.
|
|
||||||
pub height: u128,
|
|
||||||
|
|
||||||
/// The mint address of the Ore token.
|
/// The mint address of the Ore token.
|
||||||
pub mint: Pubkey,
|
pub mint: Pubkey,
|
||||||
|
|
||||||
/// The timestamp of the start of the current epoch.
|
/// The timestamp of the start of the current epoch.
|
||||||
pub epoch_start_at: i64,
|
pub epoch_start_at: i64,
|
||||||
|
|
||||||
|
/// Reweard
|
||||||
|
pub reward_rate: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[account]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Miner {
|
||||||
|
/// The bump of the miner PDA.
|
||||||
|
pub bump: u8,
|
||||||
|
|
||||||
|
/// The account authorized to hash this chain.
|
||||||
|
pub authority: Pubkey,
|
||||||
|
|
||||||
|
/// The miner's current hash.
|
||||||
|
pub hash: Hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Bus is an account used to track rewards issued during an epoch.
|
||||||
|
/// There are 8 bus accounts to provide parallelism and reduce write lock contention.
|
||||||
|
#[account]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Bus {
|
||||||
|
/// The bump of the counter PDA.
|
||||||
|
pub bump: u8,
|
||||||
|
|
||||||
|
/// The ID of this counter account.
|
||||||
|
pub id: u8,
|
||||||
|
|
||||||
|
/// The count of rewards issued this epoch.
|
||||||
|
pub rewards: u64,
|
||||||
|
|
||||||
|
/// The count of valid hashes that have been submitted on this bus this epoch.
|
||||||
|
pub hashes: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct Initialize<'info> {
|
pub struct InitializeMetadata<'info> {
|
||||||
/// The signer of the transaction.
|
/// The signer of the transaction.
|
||||||
#[account(mut)]
|
#[account(mut)]
|
||||||
pub signer: Signer<'info>,
|
pub signer: Signer<'info>,
|
||||||
|
|
||||||
/// The metadata account.
|
/// The metadata account.
|
||||||
#[account(init, seeds = [METADATA], bump, payer = signer, space = 8 + Metadata::INIT_SPACE)]
|
#[account(init, seeds = [METADATA], bump, payer = signer, space = 8 + size_of::<Metadata>())]
|
||||||
pub metadata: Account<'info, Metadata>,
|
pub metadata: Account<'info, Metadata>,
|
||||||
|
|
||||||
/// The Ore token mint.
|
/// The Ore token mint.
|
||||||
@@ -227,7 +329,110 @@ pub struct Initialize<'info> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
#[instruction(hash: String, nonce: u64)]
|
pub struct InitializeBusses<'info> {
|
||||||
|
/// The signer of the transaction.
|
||||||
|
#[account(mut)]
|
||||||
|
pub signer: Signer<'info>,
|
||||||
|
|
||||||
|
/// Bus account 0.
|
||||||
|
#[account(init, seeds = [BUS, &[0]], bump, payer = signer, space = 8 + size_of::<Bus>())]
|
||||||
|
pub bus_0: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 1.
|
||||||
|
#[account(init, seeds = [BUS, &[1]], bump, payer = signer, space = 8 + size_of::<Bus>())]
|
||||||
|
pub bus_1: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 2.
|
||||||
|
#[account(init, seeds = [BUS, &[2]], bump, payer = signer, space = 8 + size_of::<Bus>())]
|
||||||
|
pub bus_2: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 3.
|
||||||
|
#[account(init, seeds = [BUS, &[3]], bump, payer = signer, space = 8 + size_of::<Bus>())]
|
||||||
|
pub bus_3: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 4.
|
||||||
|
#[account(init, seeds = [BUS, &[4]], bump, payer = signer, space = 8 + size_of::<Bus>())]
|
||||||
|
pub bus_4: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 5.
|
||||||
|
#[account(init, seeds = [BUS, &[5]], bump, payer = signer, space = 8 + size_of::<Bus>())]
|
||||||
|
pub bus_5: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 6.
|
||||||
|
#[account(init, seeds = [BUS, &[6]], bump, payer = signer, space = 8 + size_of::<Bus>())]
|
||||||
|
pub bus_6: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 7.
|
||||||
|
#[account(init, seeds = [BUS, &[7]], bump, payer = signer, space = 8 + size_of::<Bus>())]
|
||||||
|
pub bus_7: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// The Solana system program.
|
||||||
|
#[account(address = system_program::ID)]
|
||||||
|
pub system_program: Program<'info, System>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// RegisterMiner registers a new miner with the Ore program and starts a new hash chain for them to mine.
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct RegisterMiner<'info> {
|
||||||
|
/// The signer of the transaction.
|
||||||
|
#[account(mut)]
|
||||||
|
pub signer: Signer<'info>,
|
||||||
|
|
||||||
|
/// The miner account.
|
||||||
|
#[account(init, seeds = [MINER, signer.key().as_ref()], bump, payer = signer, space = 8 + size_of::<Miner>())]
|
||||||
|
pub miner: Account<'info, Miner>,
|
||||||
|
|
||||||
|
/// The Solana system program.
|
||||||
|
#[account(address = system_program::ID)]
|
||||||
|
pub system_program: Program<'info, System>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Accounts)]
|
||||||
|
pub struct StartEpoch<'info> {
|
||||||
|
/// The signer of the transaction.
|
||||||
|
#[account(mut)]
|
||||||
|
pub signer: Signer<'info>,
|
||||||
|
|
||||||
|
/// Counter account 0.
|
||||||
|
#[account(mut, seeds = [BUS, &[0]], bump)]
|
||||||
|
pub bus_0: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 1.
|
||||||
|
#[account(mut, seeds = [BUS, &[1]], bump)]
|
||||||
|
pub bus_1: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 2.
|
||||||
|
#[account(mut, seeds = [BUS, &[2]], bump)]
|
||||||
|
pub bus_2: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 3.
|
||||||
|
#[account(mut, seeds = [BUS, &[3]], bump)]
|
||||||
|
pub bus_3: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 4.
|
||||||
|
#[account(mut, seeds = [BUS, &[4]], bump)]
|
||||||
|
pub bus_4: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 5.
|
||||||
|
#[account(mut, seeds = [BUS, &[5]], bump)]
|
||||||
|
pub bus_5: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 6.
|
||||||
|
#[account(mut, seeds = [BUS, &[6]], bump)]
|
||||||
|
pub bus_6: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// Bus account 7.
|
||||||
|
#[account(mut, seeds = [BUS, &[7]], bump)]
|
||||||
|
pub bus_7: Account<'info, Bus>,
|
||||||
|
|
||||||
|
/// The metadata account.
|
||||||
|
#[account(mut, seeds = [METADATA], bump)]
|
||||||
|
pub metadata: Account<'info, Metadata>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Bytes, not strings
|
||||||
|
#[derive(Accounts)]
|
||||||
|
#[instruction(hash: Hash, nonce: u64)]
|
||||||
pub struct Mine<'info> {
|
pub struct Mine<'info> {
|
||||||
/// The signer of the transaction (i.e. the miner).
|
/// The signer of the transaction (i.e. the miner).
|
||||||
#[account(mut)]
|
#[account(mut)]
|
||||||
@@ -237,11 +442,19 @@ pub struct Mine<'info> {
|
|||||||
#[account(mut, token::mint = mint)]
|
#[account(mut, token::mint = mint)]
|
||||||
pub beneficiary: Account<'info, TokenAccount>,
|
pub beneficiary: Account<'info, TokenAccount>,
|
||||||
|
|
||||||
|
/// A bus account for tracking epoch rewards.
|
||||||
|
#[account(mut)]
|
||||||
|
pub bus: Account<'info, Bus>,
|
||||||
|
|
||||||
/// The metadata account.
|
/// The metadata account.
|
||||||
#[account(mut, seeds = [METADATA], bump = metadata.bump, has_one = mint)]
|
#[account(seeds = [METADATA], bump = metadata.bump, has_one = mint)]
|
||||||
pub metadata: Account<'info, Metadata>,
|
pub metadata: Account<'info, Metadata>,
|
||||||
|
|
||||||
/// The Ore token mint.
|
/// The metadata account.
|
||||||
|
#[account(mut, seeds = [MINER, signer.key().as_ref()], bump = miner.bump, constraint = signer.key().eq(&miner.authority))]
|
||||||
|
pub miner: Account<'info, Miner>,
|
||||||
|
|
||||||
|
/// The Ore token mint account.
|
||||||
#[account(mut)]
|
#[account(mut)]
|
||||||
pub mint: Account<'info, Mint>,
|
pub mint: Account<'info, Mint>,
|
||||||
|
|
||||||
@@ -260,9 +473,6 @@ pub struct MineEvent {
|
|||||||
/// The beneficiary token account to which rewards were minted.
|
/// The beneficiary token account to which rewards were minted.
|
||||||
pub beneficiary: Pubkey,
|
pub beneficiary: Pubkey,
|
||||||
|
|
||||||
/// The updated height of the program's hash chain.
|
|
||||||
pub height: u128,
|
|
||||||
|
|
||||||
/// The quantity of new Ore tokens that were mined.
|
/// The quantity of new Ore tokens that were mined.
|
||||||
pub reward: u64,
|
pub reward: u64,
|
||||||
|
|
||||||
@@ -270,13 +480,13 @@ pub struct MineEvent {
|
|||||||
pub supply: u64,
|
pub supply: u64,
|
||||||
|
|
||||||
/// The valid hash provided by the signer.
|
/// The valid hash provided by the signer.
|
||||||
pub hash: String,
|
pub hash: Hash,
|
||||||
|
|
||||||
|
/// The current mining difficulty.
|
||||||
|
pub difficulty: Hash,
|
||||||
|
|
||||||
/// The nonce provided by the signer.
|
/// The nonce provided by the signer.
|
||||||
pub nonce: u64,
|
pub nonce: u64,
|
||||||
|
|
||||||
/// The current mining difficulty.
|
|
||||||
pub difficulty: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[error_code]
|
#[error_code]
|
||||||
@@ -287,167 +497,132 @@ pub enum ProgramError {
|
|||||||
HashInvalid,
|
HashInvalid,
|
||||||
#[msg("Mining has not started yet")]
|
#[msg("Mining has not started yet")]
|
||||||
NotStarted,
|
NotStarted,
|
||||||
|
#[msg("The epoch has ended and needs to be reset")]
|
||||||
|
EpochNotActive,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use anchor_lang::prelude::Pubkey;
|
// use anchor_lang::prelude::Pubkey;
|
||||||
use bnum::types::U256;
|
// use bnum::types::U256;
|
||||||
use rand::prelude::*;
|
// use rand::prelude::*;
|
||||||
|
|
||||||
use crate::{calculate_new_difficulty, calculate_supply, validate_hash};
|
// use crate::validate_hash;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_validate_hash_pass() {
|
fn test_validate_hash_pass() {
|
||||||
let h1 = sha256::digest("Seed");
|
// let h1 = sha256::digest("Seed");
|
||||||
let signer = Pubkey::new_unique();
|
// let signer = Pubkey::new_unique();
|
||||||
let nonce = 10;
|
// let nonce = 10;
|
||||||
let h2 = sha256::digest(format!("{}-{}-{}", h1, signer, nonce));
|
// let h2 = sha256::digest(format!("{}-{}-{}", h1, signer, nonce));
|
||||||
let res = validate_hash(h1, h2, signer, nonce, U256::MAX);
|
// let res = validate_hash(h1, h2, signer, nonce, U256::MAX);
|
||||||
assert!(res.is_ok());
|
// assert!(res.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_validate_hash_fail() {
|
fn test_validate_hash_fail() {
|
||||||
let h1 = sha256::digest("Seed");
|
// let h1 = sha256::digest("Seed");
|
||||||
let signer = Pubkey::new_unique();
|
// let signer = Pubkey::new_unique();
|
||||||
let nonce = 10;
|
// let nonce = 10;
|
||||||
let h2 = String::from("Invalid hash");
|
// let h2 = String::from("Invalid hash");
|
||||||
let res = validate_hash(h1, h2, signer, nonce, U256::MAX);
|
// let res = validate_hash(h1, h2, signer, nonce, U256::MAX);
|
||||||
assert!(res.is_err());
|
// assert!(res.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_validate_hash_fail_difficulty() {
|
fn test_validate_hash_fail_difficulty() {
|
||||||
let h1 = sha256::digest("Seed");
|
// let h1 = sha256::digest("Seed");
|
||||||
let signer = Pubkey::new_unique();
|
// let signer = Pubkey::new_unique();
|
||||||
let nonce = 10;
|
// let nonce = 10;
|
||||||
let h2 = sha256::digest(format!("{}-{}-{}", h1, signer, nonce));
|
// let h2 = sha256::digest(format!("{}-{}-{}", h1, signer, nonce));
|
||||||
let res = validate_hash(h1, h2, signer, nonce, U256::MIN);
|
// let res = validate_hash(h1, h2, signer, nonce, U256::MIN);
|
||||||
assert!(res.is_err());
|
// assert!(res.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_validate_hash_fuzz() {
|
fn test_validate_hash_fuzz() {
|
||||||
let h1 = sha256::digest("Seed");
|
// let h1 = sha256::digest("Seed");
|
||||||
let signer = Pubkey::new_unique();
|
// let signer = Pubkey::new_unique();
|
||||||
let mut rng = rand::thread_rng();
|
// let mut rng = rand::thread_rng();
|
||||||
for i in 0..10_000 {
|
// for i in 0..10_000 {
|
||||||
let nonce = rng.gen::<u64>();
|
// let nonce = rng.gen::<u64>();
|
||||||
let h2 = sha256::digest(i.to_string());
|
// let h2 = sha256::digest(i.to_string());
|
||||||
let res = validate_hash(h1.clone(), h2, signer, nonce, U256::MAX);
|
// let res = validate_hash(h1.clone(), h2, signer, nonce, U256::MAX);
|
||||||
assert!(res.is_err());
|
// assert!(res.is_err());
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_calculate_new_difficulty_stable() {
|
// fn test_calculate_new_difficulty_stable() {
|
||||||
let t1 = 0i64;
|
// let t1 = 0i64;
|
||||||
let t2 = 86_400i64;
|
// let t2 = 86_400i64;
|
||||||
let difficulty = U256::from_digit(100);
|
// let difficulty = U256::from_digit(100);
|
||||||
let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
// let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
||||||
assert!(new_difficulty.is_ok());
|
// assert!(new_difficulty.is_ok());
|
||||||
let x = new_difficulty.unwrap();
|
// let x = new_difficulty.unwrap();
|
||||||
assert!(x.eq(&U256::from_digit(100)));
|
// assert!(x.eq(&U256::from_digit(100)));
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_calculate_new_difficulty_higher() {
|
// fn test_calculate_new_difficulty_higher() {
|
||||||
let t1 = 0i64;
|
// let t1 = 0i64;
|
||||||
let t2 = 172_800i64;
|
// let t2 = 172_800i64;
|
||||||
let difficulty = U256::from_digit(100);
|
// let difficulty = U256::from_digit(100);
|
||||||
let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
// let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
||||||
assert!(new_difficulty.is_ok());
|
// assert!(new_difficulty.is_ok());
|
||||||
let x = new_difficulty.unwrap();
|
// let x = new_difficulty.unwrap();
|
||||||
assert!(x.eq(&U256::from_digit(200)));
|
// assert!(x.eq(&U256::from_digit(200)));
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_calculate_new_difficulty_lower() {
|
// fn test_calculate_new_difficulty_lower() {
|
||||||
let t1 = 0i64;
|
// let t1 = 0i64;
|
||||||
let t2 = 43_200i64;
|
// let t2 = 43_200i64;
|
||||||
let difficulty = U256::from_digit(100);
|
// let difficulty = U256::from_digit(100);
|
||||||
let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
// let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
||||||
assert!(new_difficulty.is_ok());
|
// assert!(new_difficulty.is_ok());
|
||||||
let x = new_difficulty.unwrap();
|
// let x = new_difficulty.unwrap();
|
||||||
assert!(x.eq(&U256::from_digit(50)));
|
// assert!(x.eq(&U256::from_digit(50)));
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_calculate_new_difficulty_max() {
|
// fn test_calculate_new_difficulty_max() {
|
||||||
let t1 = 0i64;
|
// let t1 = 0i64;
|
||||||
let t2 = 1_000_000i64;
|
// let t2 = 1_000_000i64;
|
||||||
let difficulty = U256::from_digit(100);
|
// let difficulty = U256::from_digit(100);
|
||||||
let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
// let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
||||||
assert!(new_difficulty.is_ok());
|
// assert!(new_difficulty.is_ok());
|
||||||
let x = new_difficulty.unwrap();
|
// let x = new_difficulty.unwrap();
|
||||||
assert!(x.eq(&U256::from_digit(400)));
|
// assert!(x.eq(&U256::from_digit(400)));
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_calculate_new_difficulty_min() {
|
// fn test_calculate_new_difficulty_min() {
|
||||||
let t1 = 0i64;
|
// let t1 = 0i64;
|
||||||
let t2 = 1i64;
|
// let t2 = 1i64;
|
||||||
let difficulty = U256::from_digit(100);
|
// let difficulty = U256::from_digit(100);
|
||||||
let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
// let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
||||||
assert!(new_difficulty.is_ok());
|
// assert!(new_difficulty.is_ok());
|
||||||
let x = new_difficulty.unwrap();
|
// let x = new_difficulty.unwrap();
|
||||||
assert!(x.eq(&U256::from_digit(25)));
|
// assert!(x.eq(&U256::from_digit(25)));
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_calculate_new_difficulty_err() {
|
// fn test_calculate_new_difficulty_err() {
|
||||||
let t1 = 10i64;
|
// let t1 = 10i64;
|
||||||
let t2 = 5i64;
|
// let t2 = 5i64;
|
||||||
let difficulty = U256::from_digit(100);
|
// let difficulty = U256::from_digit(100);
|
||||||
let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
// let new_difficulty = calculate_new_difficulty(t1, t2, difficulty);
|
||||||
assert!(new_difficulty.is_err());
|
// assert!(new_difficulty.is_err());
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[test]
|
// #[test]
|
||||||
fn test_calculate_supply() {
|
// fn test_calculate_supply() {
|
||||||
let s1 = calculate_supply(1);
|
// let s1 = calculate_supply(1);
|
||||||
let s10 = calculate_supply(10);
|
// let s10 = calculate_supply(10);
|
||||||
let s100 = calculate_supply(100);
|
// let s100 = calculate_supply(100);
|
||||||
assert!(s1.eq(&4_848_400_000_000));
|
// assert!(s1.eq(&4_848_400_000_000));
|
||||||
assert!(s10.eq(&20_118_631_803_084));
|
// assert!(s10.eq(&20_118_631_803_084));
|
||||||
assert!(s100.eq(&83_483_075_989_621));
|
// assert!(s100.eq(&83_483_075_989_621));
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
fn estimate_size(x: u32) -> u32 {
|
|
||||||
assert!(x < 4096);
|
|
||||||
|
|
||||||
if x < 256 {
|
|
||||||
if x < 128 {
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
} else if x < 1024 {
|
|
||||||
if x > 1022 {
|
|
||||||
return 4;
|
|
||||||
} else {
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if x < 2048 {
|
|
||||||
return 7;
|
|
||||||
} else {
|
|
||||||
return 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(kani)]
|
|
||||||
mod verification {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[kani::proof]
|
|
||||||
pub fn check() {
|
|
||||||
// let x: u32 = kani::any();
|
|
||||||
// kani::assume(x < 4096);
|
|
||||||
// let y = estimate_size(x);
|
|
||||||
// assert!(y < 10);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user