Skip to content

Commit

Permalink
Add optional deadline to open etchings (ordinals#2875)
Browse files Browse the repository at this point in the history
  • Loading branch information
casey authored Dec 18, 2023
1 parent 0073caf commit d856b05
Show file tree
Hide file tree
Showing 11 changed files with 277 additions and 29 deletions.
48 changes: 34 additions & 14 deletions src/index/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ impl Entry for BlockHash {
#[derive(Debug, PartialEq, Copy, Clone)]
pub(crate) struct RuneEntry {
pub(crate) burned: u128,
pub(crate) deadline: Option<u32>,
pub(crate) divisibility: u8,
pub(crate) end: Option<u32>,
pub(crate) etching: Txid,
Expand All @@ -39,6 +40,7 @@ pub(crate) struct RuneEntry {

pub(super) type RuneEntryValue = (
u128, // burned
Option<u32>, // deadline
u8, // divisibility
Option<u32>, // end
(u128, u128), // etching
Expand All @@ -64,6 +66,7 @@ impl Default for RuneEntry {
fn default() -> Self {
Self {
burned: 0,
deadline: None,
divisibility: 0,
end: None,
etching: Txid::all_zeros(),
Expand All @@ -82,10 +85,24 @@ impl Entry for RuneEntry {
type Value = RuneEntryValue;

fn load(
(burned, divisibility, end, etching, limit, number, rune, spacers, supply, symbol, timestamp): RuneEntryValue,
(
burned,
deadline,
divisibility,
end,
etching,
limit,
number,
rune,
spacers,
supply,
symbol,
timestamp,
): RuneEntryValue,
) -> Self {
Self {
burned,
deadline,
divisibility,
end,
etching: {
Expand All @@ -111,6 +128,7 @@ impl Entry for RuneEntry {
fn store(self) -> Self::Value {
(
self.burned,
self.deadline,
self.divisibility,
self.end,
{
Expand Down Expand Up @@ -404,37 +422,39 @@ mod tests {
fn rune_entry() {
let entry = RuneEntry {
burned: 1,
divisibility: 2,
end: Some(3),
deadline: Some(2),
divisibility: 3,
end: Some(4),
etching: Txid::from_byte_array([
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D,
0x1E, 0x1F,
]),
limit: Some(4),
number: 5,
rune: Rune(6),
spacers: 7,
supply: 8,
limit: Some(5),
number: 6,
rune: Rune(7),
spacers: 8,
supply: 9,
symbol: Some('a'),
timestamp: 9,
timestamp: 10,
};

let value = (
1,
2,
Some(3),
Some(2),
3,
Some(4),
(
0x0F0E0D0C0B0A09080706050403020100,
0x1F1E1D1C1B1A19181716151413121110,
),
Some(4),
5,
Some(5),
6,
7,
8,
Some('a'),
9,
Some('a'),
10,
);

assert_eq!(entry.store(), value);
Expand Down
13 changes: 11 additions & 2 deletions src/index/updater/rune_updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ fn claim(id: u128) -> Option<u128> {

struct Allocation {
balance: u128,
deadline: Option<u32>,
divisibility: u8,
end: Option<u32>,
id: u128,
Expand Down Expand Up @@ -118,6 +119,7 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
} else {
u128::max_value()
},
deadline: etching.deadline,
divisibility: etching.divisibility,
end: term.map(|term| term + self.height),
id: u128::from(self.height) << 16 | u128::from(index),
Expand Down Expand Up @@ -153,6 +155,11 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
continue;
}
}
if let Some(deadline) = entry.deadline {
if self.timestamp >= deadline {
continue;
}
}
mintable.insert(id, limit);
}
}
Expand Down Expand Up @@ -250,6 +257,7 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {

if let Some(Allocation {
balance,
deadline,
divisibility,
end,
id,
Expand All @@ -271,6 +279,7 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
id.store(),
RuneEntry {
burned: 0,
deadline: deadline.and_then(|deadline| (!burn).then_some(deadline)),
divisibility,
etching: txid,
number,
Expand All @@ -285,9 +294,9 @@ impl<'a, 'db, 'tx> RuneUpdater<'a, 'db, 'tx> {
} else {
u128::max_value()
} - balance,
end,
end: end.and_then(|end| (!burn).then_some(end)),
symbol,
limit,
limit: limit.and_then(|limit| (!burn).then_some(limit)),
timestamp: self.timestamp,
}
.store(),
Expand Down
186 changes: 186 additions & 0 deletions src/runes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -920,6 +920,66 @@ mod tests {
);
}

#[test]
fn etched_rune_open_etching_parameters_are_unset_for_burned_runestone() {
let context = Context::builder().arg("--index-runes").build();

context.mine_blocks(1);

let txid0 = context.rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, Witness::new())],
op_return: Some(
Runestone {
edicts: vec![Edict {
id: 0,
amount: u128::max_value(),
output: 0,
}],
etching: Some(Etching {
rune: Some(Rune(RUNE)),
deadline: Some(1),
divisibility: 1,
limit: Some(1),
symbol: Some('$'),
term: Some(1),
spacers: 1,
}),
burn: true,
}
.encipher(),
),
..Default::default()
});

context.mine_blocks(1);

let id = RuneId {
height: 2,
index: 1,
};

context.assert_runes(
[(
id,
RuneEntry {
etching: txid0,
rune: Rune(RUNE),
timestamp: 2,
divisibility: 1,
limit: None,
symbol: Some('$'),
end: None,
spacers: 1,
burned: 0,
deadline: None,
number: 0,
supply: 0,
},
)],
[],
);
}

#[test]
fn etched_reserved_rune_is_allocated_with_zero_supply_for_burned_runestone() {
let context = Context::builder().arg("--index-runes").build();
Expand Down Expand Up @@ -4257,6 +4317,132 @@ mod tests {
);
}

#[test]
fn open_etchings_can_be_limited_to_deadline() {
let context = Context::builder().arg("--index-runes").build();

context.mine_blocks(1);

let txid0 = context.rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(1, 0, 0, Witness::new())],
op_return: Some(
Runestone {
etching: Some(Etching {
rune: Some(Rune(RUNE)),
limit: Some(1000),
deadline: Some(4),
..Default::default()
}),
..Default::default()
}
.encipher(),
),
..Default::default()
});

context.mine_blocks(1);

let id = RuneId {
height: 2,
index: 1,
};

context.assert_runes(
[(
id,
RuneEntry {
deadline: Some(4),
etching: txid0,
limit: Some(1000),
rune: Rune(RUNE),
timestamp: 2,
..Default::default()
},
)],
[],
);

let txid1 = context.rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(2, 0, 0, Witness::new())],
op_return: Some(
Runestone {
edicts: vec![Edict {
id: u128::from(id) | CLAIM_BIT,
amount: 1000,
output: 0,
}],
..Default::default()
}
.encipher(),
),
..Default::default()
});

context.mine_blocks(1);

context.assert_runes(
[(
id,
RuneEntry {
deadline: Some(4),
etching: txid0,
limit: Some(1000),
rune: Rune(RUNE),
supply: 1000,
timestamp: 2,
..Default::default()
},
)],
[(
OutPoint {
txid: txid1,
vout: 0,
},
vec![(id, 1000)],
)],
);

context.rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(3, 0, 0, Witness::new())],
op_return: Some(
Runestone {
edicts: vec![Edict {
id: u128::from(id) | CLAIM_BIT,
amount: 1000,
output: 0,
}],
..Default::default()
}
.encipher(),
),
..Default::default()
});

context.mine_blocks(1);

context.assert_runes(
[(
id,
RuneEntry {
etching: txid0,
rune: Rune(RUNE),
limit: Some(1000),
supply: 1000,
deadline: Some(4),
timestamp: 2,
..Default::default()
},
)],
[(
OutPoint {
txid: txid1,
vout: 0,
},
vec![(id, 1000)],
)],
);
}

#[test]
fn open_etching_claims_can_use_split() {
let context = Context::builder().arg("--index-runes").build();
Expand Down
1 change: 1 addition & 0 deletions src/runes/etching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use super::*;

#[derive(Default, Serialize, Debug, PartialEq, Copy, Clone)]
pub struct Etching {
pub deadline: Option<u32>,
pub divisibility: u8,
pub limit: Option<u128>,
pub rune: Option<Rune>,
Expand Down
Loading

0 comments on commit d856b05

Please sign in to comment.