Skip to content

Commit

Permalink
Fix ordering for reinscriptions and show all reinscriptions for sat (o…
Browse files Browse the repository at this point in the history
  • Loading branch information
veryordinally authored Jul 17, 2023
1 parent cd215af commit fe82d4c
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 56 deletions.
169 changes: 131 additions & 38 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -536,17 +536,30 @@ impl Index {
self.client.get_block(&hash).into_option()
}

pub(crate) fn get_inscription_id_by_sat(&self, sat: Sat) -> Result<Option<InscriptionId>> {
Ok(
self
.database
.begin_read()?
.open_multimap_table(SAT_TO_INSCRIPTION_ID)?
.get(&sat.n())?
.next()
.and_then(|result| result.ok())
.map(|inscription_id| Entry::load(*inscription_id.value())),
)
pub(crate) fn get_inscription_ids_by_sat(&self, sat: Sat) -> Result<Vec<InscriptionId>> {
let rtx = &self.database.begin_read()?;

let mut ids = rtx
.open_multimap_table(SAT_TO_INSCRIPTION_ID)?
.get(&sat.n())?
.filter_map(|result| {
result
.ok()
.map(|inscription_id| InscriptionId::load(*inscription_id.value()))
})
.collect::<Vec<InscriptionId>>();

if ids.len() > 1 {
let re_id_to_seq_num = rtx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER)?;
ids.sort_by_key(
|inscription_id| match re_id_to_seq_num.get(&inscription_id.store()) {
Ok(Some(num)) => num.value() + 1, // remove at next index refactor
_ => 0,
},
);
}

Ok(ids)
}

pub(crate) fn get_inscription_id_by_inscription_number(
Expand Down Expand Up @@ -598,37 +611,30 @@ impl Index {
}))
}

pub(crate) fn get_inscriptions_on_output(
&self,
outpoint: OutPoint,
) -> Result<Vec<InscriptionId>> {
Ok(
Self::inscriptions_on_output(
&self
.database
.begin_read()?
.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?,
outpoint,
)?
.map(|(_satpoint, inscription_id)| inscription_id)
.collect(),
)
}

#[cfg(test)]
pub(crate) fn get_inscriptions_on_output_ordered(
pub(crate) fn get_inscriptions_on_output_with_satpoints(
&self,
outpoint: OutPoint,
) -> Result<Vec<(SatPoint, InscriptionId)>> {
let rtx = &self.database.begin_read()?;

let sat_to_id = rtx.open_multimap_table(SATPOINT_TO_INSCRIPTION_ID)?;

let re_id_to_seq_num = rtx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER)?;

Self::inscriptions_on_output_ordered(&re_id_to_seq_num, &sat_to_id, outpoint)
}

pub(crate) fn get_inscriptions_on_output(
&self,
outpoint: OutPoint,
) -> Result<Vec<InscriptionId>> {
Ok(
self
.get_inscriptions_on_output_with_satpoints(outpoint)?
.iter()
.map(|(_satpoint, inscription_id)| *inscription_id)
.collect(),
)
}

pub(crate) fn get_transaction(&self, txid: Txid) -> Result<Option<Transaction>> {
if txid == self.genesis_block_coinbase_txid {
Ok(Some(self.genesis_block_coinbase_transaction.clone()))
Expand Down Expand Up @@ -778,7 +784,7 @@ impl Index {
let id = id_result?;
result.insert(Entry::load(*satpoint.value()), Entry::load(*id.value()));
}
if result.len() == n.unwrap_or(usize::MAX) {
if result.len() >= n.unwrap_or(usize::MAX) {
break;
}
}
Expand Down Expand Up @@ -946,7 +952,7 @@ impl Index {
}
}

fn inscriptions_on_output<'a: 'tx, 'tx>(
fn inscriptions_on_output_unordered<'a: 'tx, 'tx>(
satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>,
outpoint: OutPoint,
) -> Result<impl Iterator<Item = (SatPoint, InscriptionId)> + 'tx> {
Expand Down Expand Up @@ -980,7 +986,7 @@ impl Index {
satpoint_to_id: &'a impl ReadableMultimapTable<&'static SatPointValue, &'static InscriptionIdValue>,
outpoint: OutPoint,
) -> Result<Vec<(SatPoint, InscriptionId)>> {
let mut result = Self::inscriptions_on_output(satpoint_to_id, outpoint)?
let mut result = Self::inscriptions_on_output_unordered(satpoint_to_id, outpoint)?
.collect::<Vec<(SatPoint, InscriptionId)>>();

if result.len() <= 1 {
Expand All @@ -989,7 +995,7 @@ impl Index {

result.sort_by_key(|(_satpoint, inscription_id)| {
match re_id_to_seq_num.get(&inscription_id.store()) {
Ok(Some(num)) => num.value(),
Ok(Some(num)) => num.value() + 1, // remove at next index refactor
Ok(None) => 0,
_ => 0,
}
Expand Down Expand Up @@ -3003,7 +3009,7 @@ mod tests {
],
context
.index
.get_inscriptions_on_output_ordered(OutPoint { txid, vout: 0 })
.get_inscriptions_on_output_with_satpoints(OutPoint { txid, vout: 0 })
.unwrap()
.iter()
.map(|(_satpoint, inscription_id)| *inscription_id)
Expand Down Expand Up @@ -3055,7 +3061,94 @@ mod tests {
vec![(location, first), (location, second), (location, third)],
context
.index
.get_inscriptions_on_output_ordered(OutPoint { txid, vout: 0 })
.get_inscriptions_on_output_with_satpoints(OutPoint { txid, vout: 0 })
.unwrap()
)
}
}

#[test]
fn reinscriptions_sequence_numbers_are_tracked_correctly() {
for context in Context::configurations() {
context.mine_blocks(1);

let mut inscription_ids = vec![];
for i in 1..=5 {
let txid = context.rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(i, if i == 1 { 0 } else { 1 }, 0)], // for the first inscription use coinbase, otherwise use the previous tx
witness: inscription("text/plain;charset=utf-8", &format!("hello {}", i)).to_witness(),
..Default::default()
});

inscription_ids.push(InscriptionId { txid, index: 0 });

context.mine_blocks(1);
}

let rtx = context.index.database.begin_read().unwrap();
let re_id_to_seq_num = rtx.open_table(REINSCRIPTION_ID_TO_SEQUENCE_NUMBER).unwrap();

for (index, id) in inscription_ids.iter().enumerate() {
match re_id_to_seq_num.get(&id.store()) {
Ok(Some(num)) => {
let sequence_number = num.value() + 1; // remove at next index refactor
assert_eq!(
index as u64, sequence_number,
"sequence number mismatch for {:?}",
id
);
}
Ok(None) => assert_eq!(
index, 0,
"only first inscription should not have sequence number for {:?}",
id
),
Err(error) => panic!("Error retrieving sequence number: {}", error),
}
}
}
}

#[test]
fn reinscriptions_are_ordered_correctly_for_many_outpoints() {
for context in Context::configurations() {
context.mine_blocks(1);

let mut inscription_ids = vec![];
for i in 1..=21 {
let txid = context.rpc_server.broadcast_tx(TransactionTemplate {
inputs: &[(i, if i == 1 { 0 } else { 1 }, 0)], // for the first inscription use coinbase, otherwise use the previous tx
witness: inscription("text/plain;charset=utf-8", &format!("hello {}", i)).to_witness(),
..Default::default()
});

inscription_ids.push(InscriptionId { txid, index: 0 });

context.mine_blocks(1);
}

let final_txid = inscription_ids.last().unwrap().txid;
let location = SatPoint {
outpoint: OutPoint {
txid: final_txid,
vout: 0,
},
offset: 0,
};

let expected_result = inscription_ids
.iter()
.map(|id| (location, *id))
.collect::<Vec<(SatPoint, InscriptionId)>>();

assert_eq!(
expected_result,
context
.index
.get_inscriptions_on_output_with_satpoints(OutPoint {
txid: final_txid,
vout: 0
})
.unwrap()
)
}
Expand Down
8 changes: 1 addition & 7 deletions src/index/updater/inscription_updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,7 @@ impl<'a, 'db, 'tx> InscriptionUpdater<'a, 'db, 'tx> {
} else if inscription.tx_in_offset != 0 {
Some(Curse::NotAtOffsetZero)
} else if inscribed_offsets.contains_key(&offset) {
let seq_num = self
.reinscription_id_to_seq_num
.iter()?
.next_back()
.and_then(|result| result.ok())
.map(|(_id, number)| number.value() + 1)
.unwrap_or(0);
let seq_num = self.reinscription_id_to_seq_num.len()?;

let sat = Self::calculate_sat(input_sat_ranges, offset);
log::info!("processing reinscription {inscription_id} on sat {:?}: sequence number {seq_num}, inscribed offsets {:?}", sat, inscribed_offsets);
Expand Down
2 changes: 1 addition & 1 deletion src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ impl Server {
sat,
satpoint,
blocktime: index.block_time(sat.height())?,
inscription: index.get_inscription_id_by_sat(sat)?,
inscriptions: index.get_inscription_ids_by_sat(sat)?,
}
.page(page_config, index.has_sat_index()?),
)
Expand Down
46 changes: 38 additions & 8 deletions src/templates/sat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub(crate) struct SatHtml {
pub(crate) sat: Sat,
pub(crate) satpoint: Option<SatPoint>,
pub(crate) blocktime: Blocktime,
pub(crate) inscription: Option<InscriptionId>,
pub(crate) inscriptions: Vec<InscriptionId>,
}

impl PageContent for SatHtml {
Expand All @@ -25,7 +25,7 @@ mod tests {
sat: Sat(0),
satpoint: None,
blocktime: Blocktime::confirmed(0),
inscription: None,
inscriptions: Vec::new(),
},
"
<h1>Sat 0</h1>
Expand Down Expand Up @@ -58,7 +58,7 @@ mod tests {
sat: Sat(2099999997689999),
satpoint: None,
blocktime: Blocktime::confirmed(0),
inscription: None,
inscriptions: Vec::new(),
},
"
<h1>Sat 2099999997689999</h1>
Expand Down Expand Up @@ -91,7 +91,7 @@ mod tests {
sat: Sat(1),
satpoint: None,
blocktime: Blocktime::confirmed(0),
inscription: None,
inscriptions: Vec::new(),
},
r"<h1>Sat 1</h1>.*<a class=prev href=/sat/0>prev</a>\n<a class=next href=/sat/2>next</a>.*",
);
Expand All @@ -104,9 +104,39 @@ mod tests {
sat: Sat(0),
satpoint: None,
blocktime: Blocktime::confirmed(0),
inscription: Some(inscription_id(1)),
inscriptions: vec![inscription_id(1)],
},
r"<h1>Sat 0</h1>.*<dt>inscription</dt><dd class=thumbnails><a href=/inscription/1{64}i1>.*</a></dd>.*",
"
<h1>Sat 0</h1>
.*
<dt>inscriptions</dt>
<dd class=thumbnails>
<a href=/inscription/1{64}i1>.*</a>
</dd>
.*"
.unindent(),
);
}

#[test]
fn sat_with_reinscription() {
assert_regex_match!(
SatHtml {
sat: Sat(0),
satpoint: None,
blocktime: Blocktime::confirmed(0),
inscriptions: vec![inscription_id(1), inscription_id(2)],
},
"
<h1>Sat 0</h1>
.*
<dt>inscriptions</dt>
<dd class=thumbnails>
<a href=/inscription/1{64}i1>.*</a>
<a href=/inscription/2{64}i2>.*</a>
</dd>
.*"
.unindent(),
);
}

Expand All @@ -117,7 +147,7 @@ mod tests {
sat: Sat::LAST,
satpoint: None,
blocktime: Blocktime::confirmed(0),
inscription: None,
inscriptions: Vec::new(),
},
r"<h1>Sat 2099999997689999</h1>.*<a class=prev href=/sat/2099999997689998>prev</a>\nnext.*",
);
Expand All @@ -130,7 +160,7 @@ mod tests {
sat: Sat(0),
satpoint: Some(satpoint(1, 0)),
blocktime: Blocktime::confirmed(0),
inscription: None,
inscriptions: Vec::new(),
},
"<h1>Sat 0</h1>.*<dt>location</dt><dd class=monospace>1{64}:1:0</dd>.*",
);
Expand Down
9 changes: 7 additions & 2 deletions templates/sat.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ <h1>Sat {{ self.sat.n() }}</h1>
<dt>offset</dt><dd>{{ self.sat.third() }}</dd>
<dt>rarity</dt><dd><span class={{self.sat.rarity()}}>{{ self.sat.rarity() }}</span></dd>
<dt>timestamp</dt><dd><time>{{self.blocktime.timestamp()}}</time>{{self.blocktime.suffix()}}</dd>
%% if let Some((inscription)) = &self.inscription {
<dt>inscription</dt><dd class=thumbnails>{{ Iframe::thumbnail(*inscription) }}</dd>
%% if !self.inscriptions.is_empty() {
<dt>inscriptions</dt>
<dd class=thumbnails>
%% for inscription in &self.inscriptions {
{{Iframe::thumbnail(*inscription)}}
%% }
</dd>
%% }
%% if let Some(satpoint) = self.satpoint {
<dt>location</dt><dd class=monospace>{{ satpoint }}</dd>
Expand Down

0 comments on commit fe82d4c

Please sign in to comment.