Skip to content

Commit 145b8af

Browse files
Tommytrgaesedepece
authored andcommitted
feat(data_structures): add support for UnstakeTransaction
1 parent 0011473 commit 145b8af

File tree

11 files changed

+468
-22
lines changed

11 files changed

+468
-22
lines changed

data_structures/src/chain/mod.rs

Lines changed: 159 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ use crate::{
4747
transaction::{
4848
CommitTransaction, DRTransaction, DRTransactionBody, Memoized, MintTransaction,
4949
RevealTransaction, StakeTransaction, TallyTransaction, Transaction, TxInclusionProof,
50-
VTTransaction,
50+
UnstakeTransaction, VTTransaction,
5151
},
5252
transaction::{
5353
MemoHash, MemoizedHashable, BETA, COMMIT_WEIGHT, OUTPUT_SIZE, REVEAL_WEIGHT, TALLY_WEIGHT,
@@ -419,6 +419,8 @@ pub struct BlockTransactions {
419419
pub tally_txns: Vec<TallyTransaction>,
420420
/// A list of signed stake transactions
421421
pub stake_txns: Vec<StakeTransaction>,
422+
/// A list of signed unstake transactions
423+
pub unstake_txns: Vec<UnstakeTransaction>,
422424
}
423425

424426
impl Block {
@@ -448,6 +450,7 @@ impl Block {
448450
reveal_txns: vec![],
449451
tally_txns: vec![],
450452
stake_txns: vec![],
453+
unstake_txns: vec![],
451454
};
452455

453456
/// Function to calculate a merkle tree from a transaction vector
@@ -473,6 +476,7 @@ impl Block {
473476
reveal_hash_merkle_root: merkle_tree_root(&txns.reveal_txns),
474477
tally_hash_merkle_root: merkle_tree_root(&txns.tally_txns),
475478
stake_hash_merkle_root: merkle_tree_root(&txns.stake_txns),
479+
unstake_hash_merkle_root: merkle_tree_root(&txns.unstake_txns),
476480
};
477481

478482
Block::new(
@@ -515,8 +519,16 @@ impl Block {
515519
st_weight
516520
}
517521

522+
pub fn ut_weight(&self) -> u32 {
523+
let mut ut_weight = 0;
524+
for ut_txn in self.txns.unstake_txns.iter() {
525+
ut_weight += ut_txn.weight();
526+
}
527+
ut_weight
528+
}
529+
518530
pub fn weight(&self) -> u32 {
519-
self.dr_weight() + self.vt_weight() + self.st_weight()
531+
self.dr_weight() + self.vt_weight() + self.st_weight() + self.ut_weight()
520532
}
521533
}
522534

@@ -531,6 +543,7 @@ impl BlockTransactions {
531543
+ self.reveal_txns.len()
532544
+ self.tally_txns.len()
533545
+ self.stake_txns.len()
546+
+ self.unstake_txns.len()
534547
}
535548

536549
/// Returns true if this block contains no transactions
@@ -543,6 +556,7 @@ impl BlockTransactions {
543556
&& self.reveal_txns.is_empty()
544557
&& self.tally_txns.is_empty()
545558
&& self.stake_txns.is_empty()
559+
&& self.unstake_txns.is_empty()
546560
}
547561

548562
/// Get a transaction given the `TransactionPointer`
@@ -579,6 +593,11 @@ impl BlockTransactions {
579593
.get(i as usize)
580594
.cloned()
581595
.map(Transaction::Stake),
596+
TransactionPointer::Unstake(i) => self
597+
.unstake_txns
598+
.get(i as usize)
599+
.cloned()
600+
.map(Transaction::Unstake),
582601
}
583602
}
584603

@@ -626,6 +645,11 @@ impl BlockTransactions {
626645
TransactionPointer::Stake(u32::try_from(i).unwrap());
627646
items_to_add.push((tx.hash(), pointer_to_block.clone()));
628647
}
648+
for (i, tx) in self.unstake_txns.iter().enumerate() {
649+
pointer_to_block.transaction_index =
650+
TransactionPointer::Unstake(u32::try_from(i).unwrap());
651+
items_to_add.push((tx.hash(), pointer_to_block.clone()));
652+
}
629653

630654
items_to_add
631655
}
@@ -709,6 +733,8 @@ pub struct BlockMerkleRoots {
709733
pub tally_hash_merkle_root: Hash,
710734
/// A 256-bit hash based on all of the stake transactions committed to this block
711735
pub stake_hash_merkle_root: Hash,
736+
/// A 256-bit hash based on all of the unstake transactions committed to this block
737+
pub unstake_hash_merkle_root: Hash,
712738
}
713739

714740
/// Function to calculate a merkle tree from a transaction vector
@@ -738,6 +764,7 @@ impl BlockMerkleRoots {
738764
reveal_hash_merkle_root: merkle_tree_root(&txns.reveal_txns),
739765
tally_hash_merkle_root: merkle_tree_root(&txns.tally_txns),
740766
stake_hash_merkle_root: merkle_tree_root(&txns.stake_txns),
767+
unstake_hash_merkle_root: merkle_tree_root(&txns.unstake_txns),
741768
}
742769
}
743770
}
@@ -2026,6 +2053,7 @@ type PrioritizedHash = (OrderedFloat<f64>, Hash);
20262053
type PrioritizedVTTransaction = (OrderedFloat<f64>, VTTransaction);
20272054
type PrioritizedDRTransaction = (OrderedFloat<f64>, DRTransaction);
20282055
type PrioritizedStakeTransaction = (OrderedFloat<f64>, StakeTransaction);
2056+
type PrioritizedUnstakeTransaction = (OrderedFloat<f64>, UnstakeTransaction);
20292057

20302058
#[derive(Debug, Clone, Default)]
20312059
struct UnconfirmedTransactions {
@@ -2084,6 +2112,8 @@ pub struct TransactionsPool {
20842112
total_dr_weight: u64,
20852113
// Total size of all stake transactions inside the pool in weight units
20862114
total_st_weight: u64,
2115+
// Total size of all unstake transactions inside the pool in weight units
2116+
total_ut_weight: u64,
20872117
// TransactionsPool size limit in weight units
20882118
weight_limit: u64,
20892119
// Ratio of value transfer transaction to data request transaction that should be in the
@@ -2109,9 +2139,14 @@ pub struct TransactionsPool {
21092139
// first query the index for the hash, and then using the hash to find the actual data)
21102140
st_transactions: HashMap<Hash, PrioritizedStakeTransaction>,
21112141
sorted_st_index: BTreeSet<PrioritizedHash>,
2142+
ut_transactions: HashMap<Hash, PrioritizedUnstakeTransaction>,
2143+
sorted_ut_index: BTreeSet<PrioritizedHash>,
21122144
// Minimum fee required to include a Stake Transaction into a block. We check for this fee in the
21132145
// TransactionPool so we can choose not to insert a transaction we will not mine anyway.
21142146
minimum_st_fee: u64,
2147+
// Minimum fee required to include a Unstake Transaction into a block. We check for this fee in the
2148+
// TransactionPool so we can choose not to insert a transaction we will not mine anyway.
2149+
minimum_ut_fee: u64,
21152150
}
21162151

21172152
impl Default for TransactionsPool {
@@ -2129,6 +2164,7 @@ impl Default for TransactionsPool {
21292164
total_vt_weight: 0,
21302165
total_dr_weight: 0,
21312166
total_st_weight: 0,
2167+
total_ut_weight: 0,
21322168
// Unlimited by default
21332169
weight_limit: u64::MAX,
21342170
// Try to keep the same amount of value transfer weight and data request weight
@@ -2137,13 +2173,17 @@ impl Default for TransactionsPool {
21372173
minimum_vtt_fee: 0,
21382174
// Default is to include all transactions into the pool and blocks
21392175
minimum_st_fee: 0,
2176+
// Default is to include all transactions into the pool and blocks
2177+
minimum_ut_fee: 0,
21402178
// Collateral minimum from consensus constants
21412179
collateral_minimum: 0,
21422180
// Required minimum reward to collateral percentage is defined as a consensus constant
21432181
required_reward_collateral_ratio: u64::MAX,
21442182
unconfirmed_transactions: Default::default(),
21452183
st_transactions: Default::default(),
21462184
sorted_st_index: Default::default(),
2185+
ut_transactions: Default::default(),
2186+
sorted_ut_index: Default::default(),
21472187
}
21482188
}
21492189
}
@@ -2216,6 +2256,7 @@ impl TransactionsPool {
22162256
&& self.co_transactions.is_empty()
22172257
&& self.re_transactions.is_empty()
22182258
&& self.st_transactions.is_empty()
2259+
&& self.ut_transactions.is_empty()
22192260
}
22202261

22212262
/// Remove all the transactions but keep the allocated memory for reuse.
@@ -2233,15 +2274,19 @@ impl TransactionsPool {
22332274
total_vt_weight,
22342275
total_dr_weight,
22352276
total_st_weight,
2277+
total_ut_weight,
22362278
weight_limit: _,
22372279
vt_to_dr_factor: _,
22382280
minimum_vtt_fee: _,
22392281
minimum_st_fee: _,
2282+
minimum_ut_fee: _,
22402283
collateral_minimum: _,
22412284
required_reward_collateral_ratio: _,
22422285
unconfirmed_transactions,
22432286
st_transactions,
22442287
sorted_st_index,
2288+
ut_transactions,
2289+
sorted_ut_index,
22452290
} = self;
22462291

22472292
vt_transactions.clear();
@@ -2256,9 +2301,12 @@ impl TransactionsPool {
22562301
*total_vt_weight = 0;
22572302
*total_dr_weight = 0;
22582303
*total_st_weight = 0;
2304+
*total_ut_weight = 0;
22592305
unconfirmed_transactions.clear();
22602306
st_transactions.clear();
22612307
sorted_st_index.clear();
2308+
ut_transactions.clear();
2309+
sorted_ut_index.clear();
22622310
}
22632311

22642312
/// Returns the number of value transfer transactions in the pool.
@@ -2324,6 +2372,27 @@ impl TransactionsPool {
23242372
self.st_transactions.len()
23252373
}
23262374

2375+
/// Returns the number of unstake transactions in the pool.
2376+
///
2377+
/// # Examples:
2378+
///
2379+
/// ```
2380+
/// # use witnet_data_structures::chain::{TransactionsPool, Hash};
2381+
/// # use witnet_data_structures::transaction::{Transaction, StakeTransaction};
2382+
/// let mut pool = TransactionsPool::new();
2383+
///
2384+
/// let transaction = Transaction::Stake(StakeTransaction::default());
2385+
///
2386+
/// assert_eq!(pool.st_len(), 0);
2387+
///
2388+
/// pool.insert(transaction, 0);
2389+
///
2390+
/// assert_eq!(pool.st_len(), 1);
2391+
/// ```
2392+
pub fn ut_len(&self) -> usize {
2393+
self.ut_transactions.len()
2394+
}
2395+
23272396
/// Clear commit transactions in TransactionsPool
23282397
pub fn clear_commits(&mut self) {
23292398
self.co_transactions.clear();
@@ -2365,7 +2434,8 @@ impl TransactionsPool {
23652434
// be impossible for nodes to broadcast these kinds of transactions.
23662435
Transaction::Tally(_tt) => Err(TransactionError::NotValidTransaction),
23672436
Transaction::Mint(_mt) => Err(TransactionError::NotValidTransaction),
2368-
Transaction::Stake(_mt) => Ok(self.st_contains(&tx_hash)),
2437+
Transaction::Stake(_st) => Ok(self.st_contains(&tx_hash)),
2438+
Transaction::Unstake(_ut) => Ok(self.ut_contains(&tx_hash)),
23692439
}
23702440
}
23712441

@@ -2481,6 +2551,30 @@ impl TransactionsPool {
24812551
self.st_transactions.contains_key(key)
24822552
}
24832553

2554+
/// Returns `true` if the pool contains an unstake transaction for the
2555+
/// specified hash.
2556+
///
2557+
/// The `key` may be any borrowed form of the hash, but `Hash` and
2558+
/// `Eq` on the borrowed form must match those for the key type.
2559+
///
2560+
/// # Examples:
2561+
/// ```
2562+
/// # use witnet_data_structures::chain::{TransactionsPool, Hash, Hashable};
2563+
/// # use witnet_data_structures::transaction::{Transaction, UnstakeTransaction};
2564+
/// let mut pool = TransactionsPool::new();
2565+
///
2566+
/// let transaction = Transaction::Stake(UnstakeTransaction::default());
2567+
/// let hash = transaction.hash();
2568+
/// assert!(!pool.ut_contains(&hash));
2569+
///
2570+
/// pool.insert(transaction, 0);
2571+
///
2572+
/// assert!(pool.t_contains(&hash));
2573+
/// ```
2574+
pub fn ut_contains(&self, key: &Hash) -> bool {
2575+
self.ut_transactions.contains_key(key)
2576+
}
2577+
24842578
/// Remove a value transfer transaction from the pool and make sure that other transactions
24852579
/// that may try to spend the same UTXOs are also removed.
24862580
/// This should be used to remove transactions that got included in a consolidated block.
@@ -2725,13 +2819,12 @@ impl TransactionsPool {
27252819
transaction
27262820
}
27272821

2728-
/// Remove a stake transaction from the pool but do not remove other transactions that
2729-
/// may try to spend the same UTXOs.
2822+
/// Remove a stake from the pool but do not remove other transactions that may try to spend the
2823+
/// same UTXOs.
27302824
/// This should be used to remove transactions that did not get included in a consolidated
27312825
/// block.
27322826
/// If the transaction did get included in a consolidated block, use `st_remove` instead.
27332827
fn st_remove_inner(&mut self, key: &Hash, consolidated: bool) -> Option<StakeTransaction> {
2734-
// TODO: is this taking into account the change and the stake output?
27352828
self.st_transactions
27362829
.remove(key)
27372830
.map(|(weight, transaction)| {
@@ -2744,6 +2837,21 @@ impl TransactionsPool {
27442837
})
27452838
}
27462839

2840+
/// Remove an unstake transaction from the pool but do not remove other transactions that
2841+
/// may try to spend the same UTXOs, because this kind of transactions spend no UTXOs.
2842+
/// This should be used to remove transactions that did not get included in a consolidated
2843+
/// block.
2844+
fn ut_remove_inner(&mut self, key: &Hash) -> Option<UnstakeTransaction> {
2845+
self.ut_transactions
2846+
.remove(key)
2847+
.map(|(weight, transaction)| {
2848+
self.sorted_ut_index.remove(&(weight, *key));
2849+
self.total_ut_weight -= u64::from(transaction.weight());
2850+
2851+
transaction
2852+
})
2853+
}
2854+
27472855
/// Returns a tuple with a vector of reveal transactions and the value
27482856
/// of all the fees obtained with those reveals
27492857
pub fn get_reveals(&self, dr_pool: &DataRequestPool) -> (Vec<&RevealTransaction>, u64) {
@@ -2957,6 +3065,27 @@ impl TransactionsPool {
29573065
self.sorted_st_index.insert((priority, key));
29583066
}
29593067
}
3068+
Transaction::Unstake(ut_tx) => {
3069+
let weight = f64::from(ut_tx.weight());
3070+
let priority = OrderedFloat(fee as f64 / weight);
3071+
3072+
if fee < self.minimum_ut_fee {
3073+
return vec![Transaction::Unstake(ut_tx)];
3074+
} else {
3075+
self.total_st_weight += u64::from(ut_tx.weight());
3076+
3077+
// TODO
3078+
// for input in &ut_tx.body.inputs {
3079+
// self.output_pointer_map
3080+
// .entry(input.output_pointer)
3081+
// .or_insert_with(Vec::new)
3082+
// .push(ut_tx.hash());
3083+
// }
3084+
3085+
self.ut_transactions.insert(key, (priority, ut_tx));
3086+
self.sorted_ut_index.insert((priority, key));
3087+
}
3088+
}
29603089
tx => {
29613090
panic!(
29623091
"Transaction kind not supported by TransactionsPool: {:?}",
@@ -3014,6 +3143,15 @@ impl TransactionsPool {
30143143
.filter_map(move |(_, h)| self.st_transactions.get(h).map(|(_, t)| t))
30153144
}
30163145

3146+
/// An iterator visiting all the unstake transactions
3147+
/// in the pool
3148+
pub fn ut_iter(&self) -> impl Iterator<Item = &UnstakeTransaction> {
3149+
self.sorted_ut_index
3150+
.iter()
3151+
.rev()
3152+
.filter_map(move |(_, h)| self.ut_transactions.get(h).map(|(_, t)| t))
3153+
}
3154+
30173155
/// Returns a reference to the value corresponding to the key.
30183156
///
30193157
/// Examples:
@@ -3108,11 +3246,16 @@ impl TransactionsPool {
31083246
self.re_hash_index
31093247
.get(hash)
31103248
.map(|rt| Transaction::Reveal(rt.clone()))
3111-
.or_else(|| {
3112-
self.st_transactions
3113-
.get(hash)
3114-
.map(|(_, st)| Transaction::Stake(st.clone()))
3115-
})
3249+
})
3250+
.or_else(|| {
3251+
self.st_transactions
3252+
.get(hash)
3253+
.map(|(_, st)| Transaction::Stake(st.clone()))
3254+
})
3255+
.or_else(|| {
3256+
self.ut_transactions
3257+
.get(hash)
3258+
.map(|(_, ut)| Transaction::Unstake(ut.clone()))
31163259
})
31173260
}
31183261

@@ -3141,6 +3284,9 @@ impl TransactionsPool {
31413284
Transaction::Stake(_) => {
31423285
let _x = self.st_remove_inner(&hash, false);
31433286
}
3287+
Transaction::Unstake(_) => {
3288+
let _x = self.ut_remove_inner(&hash);
3289+
}
31443290
_ => continue,
31453291
}
31463292

@@ -3253,6 +3399,8 @@ pub enum TransactionPointer {
32533399
Mint,
32543400
// Stake
32553401
Stake(u32),
3402+
// Unstake
3403+
Unstake(u32),
32563404
}
32573405

32583406
/// This is how transactions are stored in the database: hash of the containing block, plus index

0 commit comments

Comments
 (0)