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

Mint runes with wallet #3298

Merged
merged 20 commits into from
Mar 17, 2024
3 changes: 3 additions & 0 deletions crates/test-bitcoincore-rpc/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ pub trait Api {
#[rpc(name = "getbalances")]
fn get_balances(&self) -> Result<GetBalancesResult, jsonrpc_core::Error>;

#[rpc(name = "getbestblockhash")]
fn get_best_block_hash(&self) -> Result<bitcoin::BlockHash, jsonrpc_core::Error>;

#[rpc(name = "getblockhash")]
fn get_block_hash(&self, height: usize) -> Result<BlockHash, jsonrpc_core::Error>;

Expand Down
11 changes: 10 additions & 1 deletion crates/test-bitcoincore-rpc/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ impl Api for Server {
})
}

fn get_best_block_hash(&self) -> Result<bitcoin::BlockHash, jsonrpc_core::Error> {
match self.state().hashes.last() {
Some(block_hash) => Ok(*block_hash),
None => Err(Self::not_found()),
}
}

fn get_blockchain_info(&self) -> Result<GetBlockchainInfoResult, jsonrpc_core::Error> {
Ok(GetBlockchainInfoResult {
chain: String::from(match self.network {
Expand Down Expand Up @@ -373,7 +380,9 @@ impl Api for Server {
let (additional_input_value, outpoint) = utxos
.iter()
.find(|(value, outpoint)| value.to_sat() >= shortfall && !state.locked.contains(outpoint))
.ok_or_else(Self::not_found)?;
.ok_or_else(|| {
jsonrpc_core::Error::new(jsonrpc_core::types::error::ErrorCode::ServerError(-6))
})?;

transaction.input.push(TxIn {
previous_output: *outpoint,
Expand Down
1 change: 1 addition & 0 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use {
},
super::*,
crate::{
runes::MintError,
subcommand::{find::FindRangeOutput, server::query},
templates::StatusHtml,
},
Expand Down
28 changes: 25 additions & 3 deletions src/index/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,28 @@ pub struct RuneEntry {
pub timestamp: u32,
}

impl RuneEntry {
pub fn mintable(&self, block_height: Height, block_time: u32) -> Result<u128, MintError> {
let Some(mint) = self.mint else {
return Err(MintError::Unmintable(self.rune));
};

if let Some(end) = mint.end {
if block_height.0 >= end {
return Err(MintError::End((self.rune, end)));
}
}

if let Some(deadline) = mint.deadline {
if block_time >= deadline {
return Err(MintError::Deadline((self.rune, deadline)));
}
}

Ok(mint.limit.unwrap_or(runes::MAX_LIMIT))
}
}

pub(super) type RuneEntryValue = (
u128, // burned
u8, // divisibility
Expand All @@ -59,9 +81,9 @@ pub(super) type RuneEntryValue = (

#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize, Default)]
pub struct MintEntry {
pub deadline: Option<u32>,
pub end: Option<u32>,
pub limit: Option<u128>,
pub deadline: Option<u32>, // unix timestamp
pub end: Option<u32>, // block height
pub limit: Option<u128>, // claim amount
}

type MintEntryValue = (
Expand Down
2 changes: 1 addition & 1 deletion src/index/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -600,7 +600,7 @@ impl<'index> Updater<'index> {
runes,
sequence_number_to_rune_id: &mut sequence_number_to_rune_id,
statistic_to_count: &mut statistic_to_count,
timestamp: block.header.time,
block_time: block.header.time,
transaction_id_to_rune: &mut transaction_id_to_rune,
updates: HashMap::new(),
};
Expand Down
42 changes: 10 additions & 32 deletions src/index/updater/rune_updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use {
};

struct Claim {
id: u128,
id: RuneId,
limit: u128,
}

Expand Down Expand Up @@ -35,7 +35,7 @@ pub(super) struct RuneUpdater<'a, 'db, 'tx> {
pub(super) runes: u64,
pub(super) sequence_number_to_rune_id: &'a mut Table<'db, 'tx, u32, RuneIdValue>,
pub(super) statistic_to_count: &'a mut Table<'db, 'tx, u64, u64>,
pub(super) timestamp: u32,
pub(super) block_time: u32,
pub(super) transaction_id_to_rune: &'a mut Table<'db, 'tx, &'static TxidValue, u128>,
pub(super) updates: HashMap<RuneId, RuneUpdate>,
}
Expand Down Expand Up @@ -65,12 +65,9 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
.and_then(|id| self.claim(id).transpose())
.transpose()?
{
*unallocated.entry(claim.id).or_default() += claim.limit;
*unallocated.entry(claim.id.into()).or_default() += claim.limit;

let update = self
.updates
.entry(RuneId::try_from(claim.id).unwrap())
.or_default();
let update = self.updates.entry(claim.id).or_default();

update.mints += 1;
update.supply += claim.limit;
Expand Down Expand Up @@ -283,7 +280,7 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
u128::MAX
} - balance,
symbol,
timestamp: self.timestamp,
timestamp: self.block_time,
}
.store(),
)?;
Expand Down Expand Up @@ -367,37 +364,18 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
}))
}

fn claim(&self, id: u128) -> Result<Option<Claim>> {
let Ok(key) = RuneId::try_from(id) else {
return Ok(None);
};

let Some(entry) = self.id_to_entry.get(&key.store())? else {
fn claim(&self, id: RuneId) -> Result<Option<Claim>> {
let Some(entry) = self.id_to_entry.get(&id.store())? else {
return Ok(None);
};

let entry = RuneEntry::load(entry.value());
let rune_entry = RuneEntry::load(entry.value());

let Some(mint) = entry.mint else {
let Ok(limit) = rune_entry.mintable(Height(self.height), self.block_time) else {
return Ok(None);
};

if let Some(end) = mint.end {
if self.height >= end {
return Ok(None);
}
}

if let Some(deadline) = mint.deadline {
if self.timestamp >= deadline {
return Ok(None);
}
}

Ok(Some(Claim {
id,
limit: mint.limit.unwrap_or(runes::MAX_LIMIT),
}))
Ok(Some(Claim { id, limit }))
}

fn unallocated(&mut self, tx: &Transaction) -> Result<HashMap<u128, u128>> {
Expand Down
14 changes: 13 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,19 @@ fn fund_raw_transaction(
..Default::default()
}),
Some(false),
)?
)
.map_err(|err| {
if matches!(
err,
bitcoincore_rpc::Error::JsonRpc(bitcoincore_rpc::jsonrpc::Error::Rpc(
bitcoincore_rpc::jsonrpc::error::RpcError { code: -6, .. }
))
) {
anyhow!("not enough cardinal utxos")
} else {
err.into()
}
})?
.hex,
)
}
Expand Down
49 changes: 34 additions & 15 deletions src/runes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ use {

pub use {edict::Edict, rune::Rune, rune_id::RuneId, runestone::Runestone};

pub(crate) use {etching::Etching, mint::Mint, pile::Pile, spaced_rune::SpacedRune};
pub use {etching::Etching, mint::Mint, pile::Pile, spaced_rune::SpacedRune};

pub const MAX_DIVISIBILITY: u8 = 38;
pub(crate) const MAX_LIMIT: u128 = 1 << 64;
pub const MAX_LIMIT: u128 = 1 << 64;
const RESERVED: u128 = 6402364363415443603228541259936211926;

mod edict;
Expand All @@ -25,6 +25,25 @@ pub mod varint;

type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug)]
pub enum MintError {
Deadline((Rune, u32)),
End((Rune, u32)),
Unmintable(Rune),
}

impl fmt::Display for MintError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MintError::Deadline((rune, deadline)) => {
write!(f, "rune {rune} mint ended at {deadline}")
}
MintError::End((rune, end)) => write!(f, "rune {rune} mint ended on block {end}"),
MintError::Unmintable(rune) => write!(f, "rune {rune} not mintable"),
}
}
}

#[cfg(test)]
mod tests {
use {super::*, crate::index::testing::Context};
Expand Down Expand Up @@ -4273,7 +4292,7 @@ mod tests {
inputs: &[(2, 0, 0, Witness::new())],
op_return: Some(
Runestone {
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -4370,7 +4389,7 @@ mod tests {
amount: 1000,
output: 0,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -4415,7 +4434,7 @@ mod tests {
amount: 1000,
output: 0,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -4465,7 +4484,7 @@ mod tests {
op_return: Some(
Runestone {
burn: true,
claim: Some(u128::from(id)),
claim: Some(id),
edicts: vec![Edict {
id: u128::from(id),
amount: 1000,
Expand Down Expand Up @@ -4576,7 +4595,7 @@ mod tests {
amount: 1000,
output: 0,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -4621,7 +4640,7 @@ mod tests {
amount: 1000,
output: 0,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -4724,7 +4743,7 @@ mod tests {
amount: 1,
output: 3,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -4813,7 +4832,7 @@ mod tests {
amount: 1000,
output: 0,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -4858,7 +4877,7 @@ mod tests {
amount: 1000,
output: 0,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -4954,7 +4973,7 @@ mod tests {
amount: 0,
output: 3,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -5114,7 +5133,7 @@ mod tests {
amount: MAX_LIMIT + 1,
output: 0,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -5266,7 +5285,7 @@ mod tests {
amount: 2000,
output: 0,
}],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down Expand Up @@ -5386,7 +5405,7 @@ mod tests {
output: 0,
},
],
claim: Some(u128::from(id)),
claim: Some(id),
..Default::default()
}
.encipher(),
Expand Down
6 changes: 3 additions & 3 deletions src/runes/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::*;

#[derive(Default, Serialize, Debug, PartialEq, Copy, Clone)]
pub struct Mint {
pub deadline: Option<u32>,
pub limit: Option<u128>,
pub term: Option<u32>,
pub deadline: Option<u32>, // unix timestamp
pub limit: Option<u128>, // claim amount
pub term: Option<u32>, // relative block height
}
Loading
Loading