From 086e5da8d014e27e65386368cae7247da9d9c9ef Mon Sep 17 00:00:00 2001 From: Sunny Gleason Date: Sun, 17 Nov 2019 11:26:01 -0500 Subject: [PATCH] feat: add TransactionStatus column family and test (#6958) --- ledger/src/blocktree.rs | 63 ++++++++++++++++++++++++++++++++++++++ ledger/src/blocktree_db.rs | 44 +++++++++++++++++++++++++- 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/ledger/src/blocktree.rs b/ledger/src/blocktree.rs index 1770b8990..d34eb5473 100644 --- a/ledger/src/blocktree.rs +++ b/ledger/src/blocktree.rs @@ -68,6 +68,7 @@ pub struct Blocktree { index_cf: LedgerColumn, data_shred_cf: LedgerColumn, code_shred_cf: LedgerColumn, + transaction_status_cf: LedgerColumn, last_root: Arc>, insert_shreds_lock: Arc>, pub new_shreds_signals: Vec>, @@ -171,6 +172,7 @@ impl Blocktree { let data_shred_cf = db.column(); let code_shred_cf = db.column(); + let transaction_status_cf = db.column(); let db = Arc::new(db); @@ -192,6 +194,7 @@ impl Blocktree { index_cf, data_shred_cf, code_shred_cf, + transaction_status_cf, new_shreds_signals: vec![], completed_slots_senders: vec![], insert_shreds_lock: Arc::new(Mutex::new(())), @@ -283,6 +286,10 @@ impl Blocktree { .code_shred_cf .delete_slot(&mut write_batch, from_slot, batch_end) .unwrap_or(false) + && self + .transaction_status_cf + .delete_slot(&mut write_batch, from_slot, batch_end) + .unwrap_or(false) && self .orphans_cf .delete_slot(&mut write_batch, from_slot, batch_end) @@ -1985,6 +1992,7 @@ pub mod tests { use rand::{seq::SliceRandom, thread_rng}; use solana_sdk::{ hash::Hash, instruction::CompiledInstruction, packet::PACKET_DATA_SIZE, pubkey::Pubkey, + signature::Signature, transaction::TransactionError, }; use std::{iter::FromIterator, time::Duration}; @@ -4071,4 +4079,59 @@ pub mod tests { drop(ledger); Blocktree::destroy(&ledger_path).expect("Expected successful database destruction"); } + + #[test] + pub fn test_persist_transaction_status() { + let blocktree_path = get_tmp_ledger_path!(); + { + let blocktree = Blocktree::open(&blocktree_path).unwrap(); + let transaction_status_cf = blocktree.db.column::(); + + // result not found + assert!(transaction_status_cf + .get((0, Signature::default())) + .unwrap() + .is_none()); + + // insert value + assert!(transaction_status_cf + .put( + (0, Signature::default()), + &( + solana_sdk::transaction::Result::<()>::Err( + TransactionError::AccountNotFound + ), + 5u64 + ) + ) + .is_ok()); + + // result found + let (status, fee) = transaction_status_cf + .get((0, Signature::default())) + .unwrap() + .unwrap(); + assert_eq!(status, Err(TransactionError::AccountNotFound)); + assert_eq!(fee, 5u64); + + // insert value + assert!(transaction_status_cf + .put( + (9, Signature::default()), + &(solana_sdk::transaction::Result::<()>::Ok(()), 9u64) + ) + .is_ok()); + + // result found + let (status, fee) = transaction_status_cf + .get((9, Signature::default())) + .unwrap() + .unwrap(); + + // deserialize + assert_eq!(status, Ok(())); + assert_eq!(fee, 9u64); + } + Blocktree::destroy(&blocktree_path).expect("Expected successful database destruction"); + } } diff --git a/ledger/src/blocktree_db.rs b/ledger/src/blocktree_db.rs index ada3b530e..a48d0e5cf 100644 --- a/ledger/src/blocktree_db.rs +++ b/ledger/src/blocktree_db.rs @@ -9,7 +9,7 @@ use rocksdb::{ }; use serde::de::DeserializeOwned; use serde::Serialize; -use solana_sdk::clock::Slot; +use solana_sdk::{clock::Slot, signature::Signature}; use std::collections::HashMap; use std::fs; use std::marker::PhantomData; @@ -35,6 +35,8 @@ const INDEX_CF: &str = "index"; const DATA_SHRED_CF: &str = "data_shred"; /// Column family for Code Shreds const CODE_SHRED_CF: &str = "code_shred"; +/// Column family for Transaction Status +const TRANSACTION_STATUS_CF: &str = "transaction_status"; #[derive(Debug)] pub enum BlocktreeError { @@ -111,6 +113,10 @@ pub mod columns { #[derive(Debug)] /// The shred erasure code column pub struct ShredCode; + + #[derive(Debug)] + /// The transaction status column + pub struct TransactionStatus; } #[derive(Debug)] @@ -120,6 +126,7 @@ impl Rocks { fn open(path: &Path) -> Result { use columns::{ DeadSlots, ErasureMeta, Index, Orphans, Root, ShredCode, ShredData, SlotMeta, + TransactionStatus, }; fs::create_dir_all(&path)?; @@ -140,6 +147,8 @@ impl Rocks { ColumnFamilyDescriptor::new(ShredData::NAME, get_cf_options()); let shred_code_cf_descriptor = ColumnFamilyDescriptor::new(ShredCode::NAME, get_cf_options()); + let transaction_status_cf_descriptor = + ColumnFamilyDescriptor::new(TransactionStatus::NAME, get_cf_options()); let cfs = vec![ meta_cf_descriptor, @@ -150,6 +159,7 @@ impl Rocks { index_cf_descriptor, shred_data_cf_descriptor, shred_code_cf_descriptor, + transaction_status_cf_descriptor, ]; // Open the database @@ -161,6 +171,7 @@ impl Rocks { fn columns(&self) -> Vec<&'static str> { use columns::{ DeadSlots, ErasureMeta, Index, Orphans, Root, ShredCode, ShredData, SlotMeta, + TransactionStatus, }; vec![ @@ -172,6 +183,7 @@ impl Rocks { SlotMeta::NAME, ShredData::NAME, ShredCode::NAME, + TransactionStatus::NAME, ] } @@ -248,6 +260,36 @@ pub trait TypedColumn: Column { type Type: Serialize + DeserializeOwned; } +impl TypedColumn for columns::TransactionStatus { + type Type = (solana_sdk::transaction::Result<()>, u64); +} + +impl Column for columns::TransactionStatus { + const NAME: &'static str = TRANSACTION_STATUS_CF; + type Index = (Slot, Signature); + + fn key((slot, index): (Slot, Signature)) -> Vec { + let mut key = vec![0; 8 + 64]; + BigEndian::write_u64(&mut key[..8], slot); + key[8..72].clone_from_slice(&index.as_ref()[0..64]); + key + } + + fn index<'a>(key: &[u8]) -> (Slot, Signature) { + let slot = BigEndian::read_u64(&key[..8]); + let index = Signature::new(&key[8..72]); + (slot, index) + } + + fn slot(index: Self::Index) -> Slot { + index.0 + } + + fn as_index(slot: Slot) -> Self::Index { + (slot, Signature::default()) + } +} + impl Column for columns::ShredCode { const NAME: &'static str = CODE_SHRED_CF; type Index = (u64, u64);