Skip to content

Commit

Permalink
Mint runes with wallet (ordinals#3298)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphjaph authored Mar 17, 2024
1 parent 6864023 commit c6b27f7
Show file tree
Hide file tree
Showing 17 changed files with 559 additions and 103 deletions.
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

0 comments on commit c6b27f7

Please sign in to comment.