Skip to content

Commit 159df16

Browse files
committed
Refactor block index and internal structures. See #18
1 parent a73e958 commit 159df16

File tree

13 files changed

+566
-688
lines changed

13 files changed

+566
-688
lines changed

node/src/bin/space-cli.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use jsonrpsee::{
1010
};
1111
use protocol::{
1212
bitcoin::{Amount, FeeRate, OutPoint, Txid},
13-
hasher::{KeyHasher, SpaceHash},
13+
hasher::{KeyHasher, SpaceKey},
1414
opcodes::OP_SETALL,
1515
sname::{NameLike, SName},
1616
Covenant, FullSpaceOut,
@@ -372,7 +372,7 @@ async fn main() -> anyhow::Result<()> {
372372
fn space_hash(spaceish: &str) -> anyhow::Result<String> {
373373
let space = normalize_space(&spaceish);
374374
let sname = SName::from_str(&space)?;
375-
let spacehash = SpaceHash::from(Sha256::hash(sname.to_bytes()));
375+
let spacehash = SpaceKey::from(Sha256::hash(sname.to_bytes()));
376376
Ok(hex::encode(spacehash.as_slice()))
377377
}
378378

@@ -394,7 +394,13 @@ async fn handle_commands(
394394

395395
if let Some(outpoint) = outpoint {
396396
if let Some(spaceout) = cli.client.get_spaceout(outpoint).await? {
397-
spaceouts.push((priority, FullSpaceOut { outpoint, spaceout }));
397+
spaceouts.push((
398+
priority,
399+
FullSpaceOut {
400+
txid: outpoint.txid,
401+
spaceout,
402+
},
403+
));
398404
}
399405
}
400406
}

node/src/node.rs

Lines changed: 99 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@ use bincode::{Decode, Encode};
88
use protocol::{
99
bitcoin::{Amount, Block, BlockHash, OutPoint},
1010
constants::{ChainAnchor, ROLLOUT_BATCH_SIZE, ROLLOUT_BLOCK_INTERVAL},
11-
hasher::{BidHash, KeyHasher, OutpointHash, SpaceHash},
12-
prepare::PreparedTransaction,
11+
hasher::{BidKey, KeyHasher, OutpointKey, SpaceKey},
12+
prepare::TxContext,
1313
sname::NameLike,
14-
validate::{ErrorOut, MetaOutKind, TxInKind, TxOutKind, ValidatedTransaction, Validator},
14+
validate::{UpdateKind, TxChangeSet, Validator},
1515
Covenant, FullSpaceOut, RevokeReason, SpaceOut,
1616
};
1717
use serde::{Deserialize, Serialize};
18+
use wallet::bitcoin::Transaction;
1819

1920
use crate::{
2021
source::BitcoinRpcError,
@@ -34,11 +35,11 @@ pub struct Node {
3435
validator: Validator,
3536
}
3637

37-
/// A block structure containing validated transactions
38+
/// A block structure containing validated transaction metadata
3839
/// relevant to the Spaces protocol
3940
#[derive(Clone, Serialize, Deserialize, Encode, Decode)]
40-
pub struct ValidatedBlock {
41-
tx_data: Vec<ValidatedTransaction>,
41+
pub struct BlockMeta {
42+
tx_meta: Vec<TxChangeSet>,
4243
}
4344

4445
#[derive(Debug)]
@@ -73,7 +74,7 @@ impl Node {
7374
block_hash: BlockHash,
7475
block: Block,
7576
get_block_data: bool,
76-
) -> Result<Option<ValidatedBlock>> {
77+
) -> Result<Option<BlockMeta>> {
7778
{
7879
let tip = chain.state.tip.read().expect("read tip");
7980
if tip.hash != block.header.prev_blockhash || tip.height + 1 != height {
@@ -85,7 +86,7 @@ impl Node {
8586
}
8687
}
8788

88-
let mut block_data = ValidatedBlock { tx_data: vec![] };
89+
let mut block_data = BlockMeta { tx_meta: vec![] };
8990

9091
if (height - 1) % ROLLOUT_BLOCK_INTERVAL == 0 {
9192
let batch = Self::get_rollout_batch(ROLLOUT_BATCH_SIZE, chain)?;
@@ -94,155 +95,135 @@ impl Node {
9495
.expect("expected a coinbase tx to be present in the block")
9596
.clone();
9697

97-
let validated = self.validator.rollout(height, coinbase, batch);
98+
let validated = self.validator.rollout(height, &coinbase, batch);
9899
if get_block_data {
99-
block_data.tx_data.push(validated.clone());
100+
block_data.tx_meta.push(validated.clone());
100101
}
101102

102-
self.apply_tx(&mut chain.state, validated);
103+
self.apply_tx(&mut chain.state, &coinbase, validated);
103104
}
104105

105106
for tx in block.txdata {
106107
let prepared_tx =
107-
{ PreparedTransaction::from_tx::<LiveSnapshot, Sha256>(&mut chain.state, tx)? };
108+
{ TxContext::from_tx::<LiveSnapshot, Sha256>(&mut chain.state, &tx)? };
108109

109110
if let Some(prepared_tx) = prepared_tx {
110-
let validated_tx = self.validator.process(height, prepared_tx);
111+
let validated_tx = self.validator.process(height, &tx, prepared_tx);
112+
111113
if get_block_data {
112-
block_data.tx_data.push(validated_tx.clone());
114+
block_data.tx_meta.push(validated_tx.clone());
113115
}
114-
self.apply_tx(&mut chain.state, validated_tx);
116+
self.apply_tx(&mut chain.state, &tx, validated_tx);
115117
}
116118
}
117119
let mut tip = chain.state.tip.write().expect("write tip");
118120
tip.height = height;
119121
tip.hash = block_hash;
120122

121-
if get_block_data && !block_data.tx_data.is_empty() {
123+
if get_block_data && !block_data.tx_meta.is_empty() {
122124
return Ok(Some(block_data));
123125
}
124126
Ok(None)
125127
}
126128

127-
fn apply_tx(&self, state: &mut LiveSnapshot, changeset: ValidatedTransaction) {
129+
fn apply_tx(&self, state: &mut LiveSnapshot, tx: &Transaction, changeset: TxChangeSet) {
128130
// Remove spends
129-
for input in changeset.input {
130-
match input {
131-
TxInKind::CoinIn(_) => {
132-
// not relevant to spaces
133-
}
134-
TxInKind::SpaceIn(spacein) => {
135-
// remove spend
136-
let spend = OutpointHash::from_outpoint::<Sha256>(spacein.txin.previous_output);
137-
state.remove(spend);
138-
}
139-
}
131+
for spend in changeset.spends.into_iter() {
132+
let previous = tx.input[spend.n].previous_output;
133+
let spend = OutpointKey::from_outpoint::<Sha256>(previous);
134+
state.remove(spend);
140135
}
141136

142137
// Apply outputs
143-
for (index, output) in changeset.output.into_iter().enumerate() {
144-
match output {
145-
TxOutKind::CoinOut(_) => {
146-
// not relevant to spaces
147-
}
148-
TxOutKind::SpaceOut(spaceout) => {
149-
if let Some(space) = spaceout.space.as_ref() {
150-
assert!(
151-
!matches!(space.covenant, Covenant::Bid { .. }),
152-
"bid unexpected"
153-
);
154-
}
155-
let outpoint = OutPoint {
156-
txid: changeset.txid,
157-
vout: index as u32,
158-
};
159-
160-
// Space => Outpoint
161-
if let Some(space) = spaceout.space.as_ref() {
162-
let space_key = SpaceHash::from(Sha256::hash(space.name.to_bytes()));
163-
state.insert_space(space_key, outpoint.into());
164-
}
165-
// Outpoint => SpaceOut
166-
let outpoint_key = OutpointHash::from_outpoint::<Sha256>(outpoint);
167-
state.insert_spaceout(outpoint_key, spaceout);
168-
}
138+
for create in changeset.creates.into_iter() {
139+
if let Some(space) = create.space.as_ref() {
140+
assert!(
141+
!matches!(space.covenant, Covenant::Bid { .. }),
142+
"bid unexpected"
143+
);
144+
}
145+
let outpoint = OutPoint {
146+
txid: changeset.txid,
147+
vout: create.n as u32,
148+
};
149+
150+
// Space => Outpoint
151+
if let Some(space) = create.space.as_ref() {
152+
let space_key = SpaceKey::from(Sha256::hash(space.name.to_bytes()));
153+
state.insert_space(space_key, outpoint.into());
169154
}
155+
// Outpoint => SpaceOut
156+
let outpoint_key = OutpointKey::from_outpoint::<Sha256>(outpoint);
157+
state.insert_spaceout(outpoint_key, create);
170158
}
171159

172160
// Apply meta outputs
173-
for meta_output in changeset.meta_output {
174-
match meta_output {
175-
MetaOutKind::ErrorOut(errrout) => {
176-
match errrout {
177-
ErrorOut::Reject(_) => {
178-
// no state changes as it doesn't
179-
// modify any existing spaces
180-
}
181-
ErrorOut::Revoke(params) => {
182-
match params.reason {
183-
RevokeReason::BidPsbt(_)
184-
| RevokeReason::PrematureClaim
185-
| RevokeReason::BadSpend => {
186-
// Since these are caused by spends
187-
// Outpoint -> Spaceout mapping is already removed,
188-
let space = params.spaceout.spaceout.space.unwrap();
189-
let base_hash = Sha256::hash(space.name.to_bytes());
190-
191-
// Remove Space -> Outpoint
192-
let space_key = SpaceHash::from(base_hash);
193-
state.remove(space_key);
194-
195-
// Remove any bids from pre-auction pool
196-
match space.covenant {
197-
Covenant::Bid {
198-
total_burned,
199-
claim_height,
200-
..
201-
} => {
202-
if claim_height.is_none() {
203-
let bid_key =
204-
BidHash::from_bid(total_burned, base_hash);
205-
state.remove(bid_key);
206-
}
207-
}
208-
_ => {}
161+
for update in changeset.updates {
162+
match update.kind {
163+
UpdateKind::Revoke(params) => {
164+
match params {
165+
RevokeReason::BidPsbt(_)
166+
| RevokeReason::PrematureClaim
167+
| RevokeReason::BadSpend => {
168+
// Since these are caused by spends
169+
// Outpoint -> Spaceout mapping is already removed,
170+
let space = update.output.spaceout.space.unwrap();
171+
let base_hash = Sha256::hash(space.name.to_bytes());
172+
173+
// Remove Space -> Outpoint
174+
let space_key = SpaceKey::from(base_hash);
175+
state.remove(space_key);
176+
177+
// Remove any bids from pre-auction pool
178+
match space.covenant {
179+
Covenant::Bid {
180+
total_burned,
181+
claim_height,
182+
..
183+
} => {
184+
if claim_height.is_none() {
185+
let bid_key = BidKey::from_bid(total_burned, base_hash);
186+
state.remove(bid_key);
209187
}
210188
}
211-
RevokeReason::Expired => {
212-
// Space => Outpoint mapping will be removed
213-
// since this type of revocation only happens when an
214-
// expired space is being re-opened for auction.
215-
// No bids here so only remove Outpoint -> Spaceout
216-
let hash = OutpointHash::from_outpoint::<Sha256>(
217-
params.spaceout.outpoint,
218-
);
219-
state.remove(hash);
220-
}
189+
_ => {}
221190
}
222191
}
192+
RevokeReason::Expired => {
193+
// Space => Outpoint mapping will be removed
194+
// since this type of revocation only happens when an
195+
// expired space is being re-opened for auction.
196+
// No bids here so only remove Outpoint -> Spaceout
197+
let hash =
198+
OutpointKey::from_outpoint::<Sha256>(update.output.outpoint());
199+
state.remove(hash);
200+
}
223201
}
224202
}
225-
MetaOutKind::RolloutOut(rollout) => {
203+
UpdateKind::Rollout(rollout) => {
226204
let base_hash = Sha256::hash(
227-
rollout
205+
update
206+
.output
228207
.spaceout
229208
.space
230209
.as_ref()
231210
.expect("a space in rollout")
232211
.name
233212
.to_bytes(),
234213
);
235-
let bid_key = BidHash::from_bid(rollout.bid_value, base_hash);
214+
let bid_key = BidKey::from_bid(rollout.priority, base_hash);
236215

237-
let outpoint_key = OutpointHash::from_outpoint::<Sha256>(rollout.outpoint);
216+
let outpoint_key =
217+
OutpointKey::from_outpoint::<Sha256>(update.output.outpoint());
238218

239219
state.remove(bid_key);
240-
state.insert_spaceout(outpoint_key, rollout.spaceout);
220+
state.insert_spaceout(outpoint_key, update.output.spaceout);
241221
}
242-
MetaOutKind::SpaceOut(carried) => {
222+
UpdateKind::Bid => {
243223
// Only bids are expected in meta outputs
244224
let base_hash = Sha256::hash(
245-
carried
225+
update
226+
.output
246227
.spaceout
247228
.space
248229
.as_ref()
@@ -251,25 +232,27 @@ impl Node {
251232
.to_bytes(),
252233
);
253234

254-
let (bid_value, previous_bid) = unwrap_bid_value(&carried.spaceout);
235+
let (bid_value, previous_bid) =
236+
unwrap_bid_value(&update.output.spaceout);
255237

256-
let bid_hash = BidHash::from_bid(bid_value, base_hash);
257-
let space_key = SpaceHash::from(base_hash);
238+
let bid_hash = BidKey::from_bid(bid_value, base_hash);
239+
let space_key = SpaceKey::from(base_hash);
258240

259-
match carried.spaceout.space.as_ref().expect("space").covenant {
241+
match update.output.spaceout.space.as_ref().expect("space").covenant {
260242
Covenant::Bid { claim_height, .. } => {
261243
if claim_height.is_none() {
262-
let prev_bid_hash = BidHash::from_bid(previous_bid, base_hash);
244+
let prev_bid_hash = BidKey::from_bid(previous_bid, base_hash);
263245
state.update_bid(Some(prev_bid_hash), bid_hash, space_key);
264246
}
265247
}
266248
_ => panic!("expected bid"),
267249
}
268250

269-
state.insert_space(space_key, carried.outpoint.into());
251+
let carried_outpoint = update.output.outpoint();
252+
state.insert_space(space_key, carried_outpoint.into());
270253

271-
let outpoint_key = OutpointHash::from_outpoint::<Sha256>(carried.outpoint);
272-
state.insert_spaceout(outpoint_key, carried.spaceout);
254+
let outpoint_key = OutpointKey::from_outpoint::<Sha256>(carried_outpoint);
255+
state.insert_spaceout(outpoint_key, update.output.spaceout);
273256
}
274257
}
275258
}
@@ -291,7 +274,7 @@ impl Node {
291274
let mut hash = [0u8; 32];
292275
hash.copy_from_slice(raw_hash.as_slice());
293276

294-
let space_hash = SpaceHash::from_raw(hash)?;
277+
let space_hash = SpaceKey::from_raw(hash)?;
295278
let full = chain.state.get_space_info(&space_hash)?;
296279

297280
if let Some(full) = full {

0 commit comments

Comments
 (0)