|
@@ -57,17 +57,17 @@ pub struct DifficultyIndex {
|
|
|
pub cumulative: u64,
|
|
pub cumulative: u64,
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+/// Structure holding all tapes in the database.
|
|
|
|
|
+pub struct TapesDatabase {
|
|
|
|
|
+ pub block_index: FixedSizedTape<BlockIndex>,
|
|
|
|
|
+ pub tx_index: FixedSizedTape<TxIndex>,
|
|
|
|
|
+ pub difficulty_index: FixedSizedTape<DifficultyIndex>,
|
|
|
|
|
+ pub blocks: BlobTape,
|
|
|
|
|
+ pub transactions: BlobTape,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
impl Explorer {
|
|
impl Explorer {
|
|
|
- pub fn open_tapes(
|
|
|
|
|
- db: &Tapes,
|
|
|
|
|
- options: &TapeOpenOptions,
|
|
|
|
|
- ) -> io::Result<(
|
|
|
|
|
- FixedSizedTape<BlockIndex>,
|
|
|
|
|
- FixedSizedTape<TxIndex>,
|
|
|
|
|
- FixedSizedTape<DifficultyIndex>,
|
|
|
|
|
- BlobTape,
|
|
|
|
|
- BlobTape,
|
|
|
|
|
- )> {
|
|
|
|
|
|
|
+ pub fn open_tapes(db: &Tapes, options: &TapeOpenOptions) -> io::Result<TapesDatabase> {
|
|
|
let mut tx = db.append();
|
|
let mut tx = db.append();
|
|
|
let block_index = tx.open_fixed_sized_tape("block_index", options)?;
|
|
let block_index = tx.open_fixed_sized_tape("block_index", options)?;
|
|
|
let tx_index = tx.open_fixed_sized_tape("tx_index", options)?;
|
|
let tx_index = tx.open_fixed_sized_tape("tx_index", options)?;
|
|
@@ -75,33 +75,33 @@ impl Explorer {
|
|
|
let blocks = tx.open_blob_tape("blocks", options)?;
|
|
let blocks = tx.open_blob_tape("blocks", options)?;
|
|
|
let transactions = tx.open_blob_tape("transactions", options)?;
|
|
let transactions = tx.open_blob_tape("transactions", options)?;
|
|
|
tx.commit(Persistence::Buffer)?;
|
|
tx.commit(Persistence::Buffer)?;
|
|
|
- Ok((block_index, tx_index, difficulty_index, blocks, transactions))
|
|
|
|
|
|
|
+ Ok(TapesDatabase { block_index, tx_index, difficulty_index, blocks, transactions })
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Append a new block
|
|
/// Append a new block
|
|
|
pub async fn append_block(&self, block: &BlockInfo, diff: &DifficultyIndex) -> io::Result<()> {
|
|
pub async fn append_block(&self, block: &BlockInfo, diff: &DifficultyIndex) -> io::Result<()> {
|
|
|
let mut tx = self.tapes_db.append();
|
|
let mut tx = self.tapes_db.append();
|
|
|
|
|
|
|
|
- let block_offset = tx.blob_tape_len(&self.blocks).unwrap_or(0);
|
|
|
|
|
- let tx_blob_offset = tx.blob_tape_len(&self.transactions).unwrap_or(0);
|
|
|
|
|
- let tx_start_idx = tx.fixed_sized_tape_len(&self.tx_index).unwrap_or(0);
|
|
|
|
|
|
|
+ let block_offset = tx.blob_tape_len(&self.database.blocks).unwrap_or(0);
|
|
|
|
|
+ let tx_blob_offset = tx.blob_tape_len(&self.database.transactions).unwrap_or(0);
|
|
|
|
|
+ let tx_start_idx = tx.fixed_sized_tape_len(&self.database.tx_index).unwrap_or(0);
|
|
|
|
|
|
|
|
// Append block header
|
|
// Append block header
|
|
|
let header_data = serialize_async(&block.header).await;
|
|
let header_data = serialize_async(&block.header).await;
|
|
|
- tx.append_bytes(&self.blocks, &header_data)?;
|
|
|
|
|
|
|
+ tx.append_bytes(&self.database.blocks, &header_data)?;
|
|
|
|
|
|
|
|
// Append all block transactions
|
|
// Append all block transactions
|
|
|
let mut current_tx_offset = tx_blob_offset;
|
|
let mut current_tx_offset = tx_blob_offset;
|
|
|
for transaction in &block.txs {
|
|
for transaction in &block.txs {
|
|
|
let tx_data = serialize_async(transaction).await;
|
|
let tx_data = serialize_async(transaction).await;
|
|
|
- tx.append_bytes(&self.transactions, &tx_data)?;
|
|
|
|
|
|
|
+ tx.append_bytes(&self.database.transactions, &tx_data)?;
|
|
|
|
|
|
|
|
let tx_idx = TxIndex {
|
|
let tx_idx = TxIndex {
|
|
|
offset: current_tx_offset,
|
|
offset: current_tx_offset,
|
|
|
length: tx_data.len() as u64,
|
|
length: tx_data.len() as u64,
|
|
|
block_height: block.header.height as u64,
|
|
block_height: block.header.height as u64,
|
|
|
};
|
|
};
|
|
|
- tx.append_entries(&self.tx_index, std::slice::from_ref(&tx_idx))?;
|
|
|
|
|
|
|
+ tx.append_entries(&self.database.tx_index, std::slice::from_ref(&tx_idx))?;
|
|
|
|
|
|
|
|
current_tx_offset += tx_data.len() as u64;
|
|
current_tx_offset += tx_data.len() as u64;
|
|
|
}
|
|
}
|
|
@@ -113,10 +113,10 @@ impl Explorer {
|
|
|
tx_count: block.txs.len() as u64,
|
|
tx_count: block.txs.len() as u64,
|
|
|
tx_start_idx,
|
|
tx_start_idx,
|
|
|
};
|
|
};
|
|
|
- tx.append_entries(&self.block_index, std::slice::from_ref(&block_idx))?;
|
|
|
|
|
|
|
+ tx.append_entries(&self.database.block_index, std::slice::from_ref(&block_idx))?;
|
|
|
|
|
|
|
|
// Append difficulty
|
|
// Append difficulty
|
|
|
- tx.append_entries(&self.difficulty_index, std::slice::from_ref(diff))?;
|
|
|
|
|
|
|
+ tx.append_entries(&self.database.difficulty_index, std::slice::from_ref(diff))?;
|
|
|
|
|
|
|
|
// sled stores transaction indices so we can reference them
|
|
// sled stores transaction indices so we can reference them
|
|
|
let mut batch = sled::Batch::default();
|
|
let mut batch = sled::Batch::default();
|
|
@@ -156,7 +156,7 @@ impl Explorer {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let reader = self.tapes_db.reader();
|
|
let reader = self.tapes_db.reader();
|
|
|
- let current_len = reader.fixed_sized_tape_len(&self.block_index).unwrap_or(0);
|
|
|
|
|
|
|
+ let current_len = reader.fixed_sized_tape_len(&self.database.block_index).unwrap_or(0);
|
|
|
|
|
|
|
|
if count > current_len {
|
|
if count > current_len {
|
|
|
return Err(io::Error::new(
|
|
return Err(io::Error::new(
|
|
@@ -166,7 +166,7 @@ impl Explorer {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
let new_block_count = current_len - count;
|
|
let new_block_count = current_len - count;
|
|
|
- let current_tx_idx_len = reader.fixed_sized_tape_len(&self.tx_index).unwrap_or(0);
|
|
|
|
|
|
|
+ let current_tx_idx_len = reader.fixed_sized_tape_len(&self.database.tx_index).unwrap_or(0);
|
|
|
|
|
|
|
|
// Collect data to remove from sled
|
|
// Collect data to remove from sled
|
|
|
let mut header_hashes_to_remove: Vec<Vec<u8>> = Vec::new();
|
|
let mut header_hashes_to_remove: Vec<Vec<u8>> = Vec::new();
|
|
@@ -175,23 +175,23 @@ impl Explorer {
|
|
|
for height in new_block_count..current_len {
|
|
for height in new_block_count..current_len {
|
|
|
// Get the block index to find transactions
|
|
// Get the block index to find transactions
|
|
|
let block_idx = reader
|
|
let block_idx = reader
|
|
|
- .read_entry(&self.block_index, height)?
|
|
|
|
|
|
|
+ .read_entry(&self.database.block_index, height)?
|
|
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "block index not found"))?;
|
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "block index not found"))?;
|
|
|
|
|
|
|
|
// Read header to get its hash
|
|
// Read header to get its hash
|
|
|
let mut header_data = vec![0u8; block_idx.length as usize];
|
|
let mut header_data = vec![0u8; block_idx.length as usize];
|
|
|
- reader.read_bytes(&self.blocks, block_idx.offset, &mut header_data)?;
|
|
|
|
|
|
|
+ reader.read_bytes(&self.database.blocks, block_idx.offset, &mut header_data)?;
|
|
|
let header: Header = deserialize_async(&header_data).await?;
|
|
let header: Header = deserialize_async(&header_data).await?;
|
|
|
header_hashes_to_remove.push(serialize_async(&header.hash()).await);
|
|
header_hashes_to_remove.push(serialize_async(&header.hash()).await);
|
|
|
|
|
|
|
|
// Read each tx to get its hash
|
|
// Read each tx to get its hash
|
|
|
for i in 0..block_idx.tx_count {
|
|
for i in 0..block_idx.tx_count {
|
|
|
let tx_idx = reader
|
|
let tx_idx = reader
|
|
|
- .read_entry(&self.tx_index, block_idx.tx_start_idx + i)?
|
|
|
|
|
|
|
+ .read_entry(&self.database.tx_index, block_idx.tx_start_idx + i)?
|
|
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "tx index not found"))?;
|
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "tx index not found"))?;
|
|
|
|
|
|
|
|
let mut tx_data = vec![0u8; tx_idx.length as usize];
|
|
let mut tx_data = vec![0u8; tx_idx.length as usize];
|
|
|
- reader.read_bytes(&self.transactions, tx_idx.offset, &mut tx_data)?;
|
|
|
|
|
|
|
+ reader.read_bytes(&self.database.transactions, tx_idx.offset, &mut tx_data)?;
|
|
|
let transaction: Transaction = deserialize_async(&tx_data).await?;
|
|
let transaction: Transaction = deserialize_async(&tx_data).await?;
|
|
|
tx_hashes_to_remove.push(transaction.hash().0.to_vec());
|
|
tx_hashes_to_remove.push(transaction.hash().0.to_vec());
|
|
|
}
|
|
}
|
|
@@ -201,7 +201,7 @@ impl Explorer {
|
|
|
(0, 0, 0)
|
|
(0, 0, 0)
|
|
|
} else {
|
|
} else {
|
|
|
let last_block_idx = reader
|
|
let last_block_idx = reader
|
|
|
- .read_entry(&self.block_index, new_block_count - 1)?
|
|
|
|
|
|
|
+ .read_entry(&self.database.block_index, new_block_count - 1)?
|
|
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "block index not found"))?;
|
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "block index not found"))?;
|
|
|
|
|
|
|
|
let new_block_blob_len = last_block_idx.offset + last_block_idx.length;
|
|
let new_block_blob_len = last_block_idx.offset + last_block_idx.length;
|
|
@@ -210,9 +210,10 @@ impl Explorer {
|
|
|
let new_tx_blob_len = if new_tx_idx_len == 0 {
|
|
let new_tx_blob_len = if new_tx_idx_len == 0 {
|
|
|
0
|
|
0
|
|
|
} else {
|
|
} else {
|
|
|
- let last_tx_idx = reader
|
|
|
|
|
- .read_entry(&self.tx_index, new_tx_idx_len - 1)?
|
|
|
|
|
- .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "tx index not found"))?;
|
|
|
|
|
|
|
+ let last_tx_idx =
|
|
|
|
|
+ reader.read_entry(&self.database.tx_index, new_tx_idx_len - 1)?.ok_or_else(
|
|
|
|
|
+ || io::Error::new(io::ErrorKind::NotFound, "tx index not found"),
|
|
|
|
|
+ )?;
|
|
|
last_tx_idx.offset + last_tx_idx.length
|
|
last_tx_idx.offset + last_tx_idx.length
|
|
|
};
|
|
};
|
|
|
|
|
|
|
@@ -223,14 +224,14 @@ impl Explorer {
|
|
|
drop(reader);
|
|
drop(reader);
|
|
|
|
|
|
|
|
let mut truncate_tx = self.tapes_db.truncate();
|
|
let mut truncate_tx = self.tapes_db.truncate();
|
|
|
- truncate_tx.drop_from_fixed_sized_tape(&self.block_index, count);
|
|
|
|
|
- truncate_tx.drop_from_fixed_sized_tape(&self.difficulty_index, count);
|
|
|
|
|
|
|
+ truncate_tx.drop_from_fixed_sized_tape(&self.database.block_index, count);
|
|
|
|
|
+ truncate_tx.drop_from_fixed_sized_tape(&self.database.difficulty_index, count);
|
|
|
|
|
|
|
|
let tx_idx_to_remove = current_tx_idx_len - new_tx_idx_len;
|
|
let tx_idx_to_remove = current_tx_idx_len - new_tx_idx_len;
|
|
|
- truncate_tx.drop_from_fixed_sized_tape(&self.tx_index, tx_idx_to_remove);
|
|
|
|
|
|
|
+ truncate_tx.drop_from_fixed_sized_tape(&self.database.tx_index, tx_idx_to_remove);
|
|
|
|
|
|
|
|
- truncate_tx.set_blob_tape_len(&self.blocks, new_block_blob_len);
|
|
|
|
|
- truncate_tx.set_blob_tape_len(&self.transactions, new_tx_blob_len);
|
|
|
|
|
|
|
+ truncate_tx.set_blob_tape_len(&self.database.blocks, new_block_blob_len);
|
|
|
|
|
+ truncate_tx.set_blob_tape_len(&self.database.transactions, new_tx_blob_len);
|
|
|
|
|
|
|
|
truncate_tx.commit(Persistence::SyncData)?;
|
|
truncate_tx.commit(Persistence::SyncData)?;
|
|
|
|
|
|
|
@@ -268,27 +269,27 @@ impl Explorer {
|
|
|
/// Get the current known blockchain height
|
|
/// Get the current known blockchain height
|
|
|
pub fn get_height(&self) -> io::Result<Option<u64>> {
|
|
pub fn get_height(&self) -> io::Result<Option<u64>> {
|
|
|
let reader = self.tapes_db.reader();
|
|
let reader = self.tapes_db.reader();
|
|
|
- let len = reader.fixed_sized_tape_len(&self.block_index);
|
|
|
|
|
|
|
+ let len = reader.fixed_sized_tape_len(&self.database.block_index);
|
|
|
Ok(len.filter(|&l| l > 0).map(|l| l - 1))
|
|
Ok(len.filter(|&l| l > 0).map(|l| l - 1))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Get the difficulty and cumulative difficulty for a block height
|
|
/// Get the difficulty and cumulative difficulty for a block height
|
|
|
pub fn get_difficulty(&self, height: u64) -> io::Result<Option<DifficultyIndex>> {
|
|
pub fn get_difficulty(&self, height: u64) -> io::Result<Option<DifficultyIndex>> {
|
|
|
let reader = self.tapes_db.reader();
|
|
let reader = self.tapes_db.reader();
|
|
|
- reader.read_entry(&self.difficulty_index, height)
|
|
|
|
|
|
|
+ reader.read_entry(&self.database.difficulty_index, height)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/// Get the block header for a height
|
|
/// Get the block header for a height
|
|
|
pub async fn get_header(&self, height: u64) -> io::Result<Option<Header>> {
|
|
pub async fn get_header(&self, height: u64) -> io::Result<Option<Header>> {
|
|
|
let reader = self.tapes_db.reader();
|
|
let reader = self.tapes_db.reader();
|
|
|
|
|
|
|
|
- let idx = match reader.read_entry(&self.block_index, height)? {
|
|
|
|
|
|
|
+ let idx = match reader.read_entry(&self.database.block_index, height)? {
|
|
|
Some(idx) => idx,
|
|
Some(idx) => idx,
|
|
|
None => return Ok(None),
|
|
None => return Ok(None),
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
let mut data = vec![0u8; idx.length as usize];
|
|
let mut data = vec![0u8; idx.length as usize];
|
|
|
- reader.read_bytes(&self.blocks, idx.offset, &mut data)?;
|
|
|
|
|
|
|
+ reader.read_bytes(&self.database.blocks, idx.offset, &mut data)?;
|
|
|
|
|
|
|
|
Ok(Some(deserialize_async(&data).await?))
|
|
Ok(Some(deserialize_async(&data).await?))
|
|
|
}
|
|
}
|
|
@@ -297,7 +298,7 @@ impl Explorer {
|
|
|
pub async fn get_block_txs(&self, height: u64) -> io::Result<Option<Vec<Transaction>>> {
|
|
pub async fn get_block_txs(&self, height: u64) -> io::Result<Option<Vec<Transaction>>> {
|
|
|
let reader = self.tapes_db.reader();
|
|
let reader = self.tapes_db.reader();
|
|
|
|
|
|
|
|
- let block_idx = match reader.read_entry(&self.block_index, height)? {
|
|
|
|
|
|
|
+ let block_idx = match reader.read_entry(&self.database.block_index, height)? {
|
|
|
Some(idx) => idx,
|
|
Some(idx) => idx,
|
|
|
None => return Ok(None),
|
|
None => return Ok(None),
|
|
|
};
|
|
};
|
|
@@ -309,11 +310,11 @@ impl Explorer {
|
|
|
let mut txs = Vec::with_capacity(block_idx.tx_count as usize);
|
|
let mut txs = Vec::with_capacity(block_idx.tx_count as usize);
|
|
|
for i in 0..block_idx.tx_count {
|
|
for i in 0..block_idx.tx_count {
|
|
|
let tx_idx = reader
|
|
let tx_idx = reader
|
|
|
- .read_entry(&self.tx_index, block_idx.tx_start_idx + i)?
|
|
|
|
|
|
|
+ .read_entry(&self.database.tx_index, block_idx.tx_start_idx + i)?
|
|
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "tx index not found"))?;
|
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "tx index not found"))?;
|
|
|
|
|
|
|
|
let mut data = vec![0u8; tx_idx.length as usize];
|
|
let mut data = vec![0u8; tx_idx.length as usize];
|
|
|
- reader.read_bytes(&self.transactions, tx_idx.offset, &mut data)?;
|
|
|
|
|
|
|
+ reader.read_bytes(&self.database.transactions, tx_idx.offset, &mut data)?;
|
|
|
txs.push(deserialize_async(&data).await?);
|
|
txs.push(deserialize_async(&data).await?);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -352,14 +353,14 @@ impl Explorer {
|
|
|
|
|
|
|
|
// Read the TxIndex from tapes
|
|
// Read the TxIndex from tapes
|
|
|
let reader = self.tapes_db.reader();
|
|
let reader = self.tapes_db.reader();
|
|
|
- let tx_idx = match reader.read_entry(&self.tx_index, tx_idx_pos)? {
|
|
|
|
|
|
|
+ let tx_idx = match reader.read_entry(&self.database.tx_index, tx_idx_pos)? {
|
|
|
Some(idx) => idx,
|
|
Some(idx) => idx,
|
|
|
None => return Ok(None),
|
|
None => return Ok(None),
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
// Read the transaction data from the blob tape
|
|
// Read the transaction data from the blob tape
|
|
|
let mut data = vec![0u8; tx_idx.length as usize];
|
|
let mut data = vec![0u8; tx_idx.length as usize];
|
|
|
- reader.read_bytes(&self.transactions, tx_idx.offset, &mut data)?;
|
|
|
|
|
|
|
+ reader.read_bytes(&self.database.transactions, tx_idx.offset, &mut data)?;
|
|
|
|
|
|
|
|
let transaction: Transaction = deserialize_async(&data).await?;
|
|
let transaction: Transaction = deserialize_async(&data).await?;
|
|
|
Ok(Some((transaction, tx_idx.block_height)))
|
|
Ok(Some((transaction, tx_idx.block_height)))
|