Skip to content

Commit 842d5dc

Browse files
committed
feat: add TxoCommitments softfork
1 parent 4b9c5f3 commit 842d5dc

File tree

29 files changed

+825
-178
lines changed

29 files changed

+825
-178
lines changed

Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

chain/src/chain.rs

+1
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,7 @@ impl ChainService {
787787
switch,
788788
Arc::clone(&txs_verify_cache),
789789
&mmr,
790+
&txn,
790791
);
791792
contextual_block_verifier.verify(&resolved, b)
792793
};

db-schema/src/lib.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/// Column families alias type
44
pub type Col = &'static str;
55
/// Total column number
6-
pub const COLUMNS: u32 = 19;
6+
pub const COLUMNS: u32 = 20;
77
/// Column store chain index
88
pub const COLUMN_INDEX: Col = "0";
99
/// Column store block's header
@@ -44,6 +44,8 @@ pub const COLUMN_CHAIN_ROOT_MMR: Col = "16";
4444
pub const COLUMN_BLOCK_FILTER: Col = "17";
4545
/// Column store filter data hash for client-side filtering
4646
pub const COLUMN_BLOCK_FILTER_HASH: Col = "18";
47+
/// Column store txo root MMR data
48+
pub const COLUMN_TXO_ROOT_MMR: Col = "19";
4749

4850
/// META_TIP_HEADER_KEY tracks the latest known best block header
4951
pub const META_TIP_HEADER_KEY: &[u8] = b"TIP_HEADER";
@@ -52,6 +54,13 @@ pub const META_CURRENT_EPOCH_KEY: &[u8] = b"CURRENT_EPOCH";
5254
/// META_FILTER_DATA_KEY tracks the latest built filter data block hash
5355
pub const META_LATEST_BUILT_FILTER_DATA_KEY: &[u8] = b"LATEST_BUILT_FILTER_DATA";
5456

57+
/// A key prefix records the txo root mmr size by block number
58+
pub const TXO_ROOT_MMR_SIZE_KEY_PREFIX: &[u8] = &[0];
59+
/// A key prefix records the status of out_point in txo root mmr
60+
pub const TXO_ROOT_MMR_STATUS_KEY_PREFIX: &[u8] = &[1];
61+
/// A key prefix records the txo root mmr element by position
62+
pub const TXO_ROOT_MMR_ELEMENT_KEY_PREFIX: &[u8] = &[2];
63+
5564
/// CHAIN_SPEC_HASH_KEY tracks the hash of chain spec which created current database
5665
pub const CHAIN_SPEC_HASH_KEY: &[u8] = b"chain-spec-hash";
5766
/// MIGRATION_VERSION_KEY tracks the current database version.

rpc/src/module/test.rs

+23-4
Original file line numberDiff line numberDiff line change
@@ -540,12 +540,31 @@ impl IntegrationTestRpc for IntegrationTestRpcImpl {
540540
Ok(tx_hash.unpack())
541541
}
542542

543-
fn generate_block_with_template(&self, block_template: BlockTemplate) -> Result<H256> {
543+
fn generate_block_with_template(&self, mut block_template: BlockTemplate) -> Result<H256> {
544544
let dao_field = self.calculate_dao_field(block_template.clone())?;
545545

546-
let mut update_dao_template = block_template;
547-
update_dao_template.dao = dao_field;
548-
let block = update_dao_template.into();
546+
let cellbase = packed::Transaction::from(block_template.cellbase.data.clone()).into_view();
547+
let txs = block_template
548+
.transactions
549+
.iter()
550+
.map(|t| packed::Transaction::from(t.data.clone()).into_view())
551+
.collect::<Vec<_>>();
552+
let extension = self
553+
.shared
554+
.snapshot()
555+
.build_extension(&cellbase, txs.iter())
556+
.map_err(|err| {
557+
error!(
558+
"build extension error when generating block \
559+
with block template, error: {:?}",
560+
err
561+
);
562+
RPCError::invalid_params(err.to_string())
563+
})?;
564+
565+
block_template.dao = dao_field;
566+
block_template.extension = extension.map(|e| e.into());
567+
let block = block_template.into();
549568
self.process_and_announce_block(block)
550569
}
551570

spec/src/lib.rs

+10
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,17 @@ impl ChainSpec {
532532
active_mode: ActiveMode::Always,
533533
threshold: TESTNET_ACTIVATION_THRESHOLD,
534534
};
535+
let txo_commitments = Deployment {
536+
bit: 2,
537+
start: 0,
538+
timeout: 0,
539+
min_activation_epoch: 0,
540+
period: 10,
541+
active_mode: ActiveMode::Always,
542+
threshold: TESTNET_ACTIVATION_THRESHOLD,
543+
};
535544
deployments.insert(DeploymentPos::LightClient, light_client);
545+
deployments.insert(DeploymentPos::TxoCommitments, txo_commitments);
536546
Some(deployments)
537547
}
538548
}

spec/src/versionbits/convert.rs

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ impl From<DeploymentPos> for JsonDeploymentPos {
4848
match pos {
4949
DeploymentPos::Testdummy => JsonDeploymentPos::Testdummy,
5050
DeploymentPos::LightClient => JsonDeploymentPos::LightClient,
51+
DeploymentPos::TxoCommitments => JsonDeploymentPos::TxoCommitments,
5152
}
5253
}
5354
}

spec/src/versionbits/mod.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,10 @@ pub enum ActiveMode {
5959
pub enum DeploymentPos {
6060
/// Dummy
6161
Testdummy,
62-
/// light client protocol
62+
/// Light client protocol
6363
LightClient,
64+
/// Tranaction output commitments
65+
TxoCommitments,
6466
}
6567

6668
/// VersionbitsIndexer

store/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ ckb-app-config = { path = "../util/app-config", version = "= 0.112.0-pre" }
2020
ckb-db-schema = { path = "../db-schema", version = "= 0.112.0-pre" }
2121
ckb-freezer = { path = "../freezer", version = "= 0.112.0-pre" }
2222
ckb-merkle-mountain-range = { git = "https://github.com/quake/merkle-mountain-range", branch = "quake/updatable" }
23+
ckb-logger = { path = "../util/logger", version = "= 0.112.0-pre" }
2324

2425
[dev-dependencies]
2526
tempfile.workspace = true

store/src/cell.rs

+80-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
use crate::{ChainStore, StoreTransaction};
2-
use ckb_error::Error;
3-
use ckb_types::{core::BlockView, packed, prelude::*};
2+
use ckb_error::{Error, InternalErrorKind};
3+
use ckb_types::{
4+
core::{BlockNumber, BlockView},
5+
packed,
6+
prelude::*,
7+
utilities::merkle_mountain_range::{hash_out_point_and_status, TxoStatus},
8+
};
49
use std::collections::HashMap;
510

611
/**
@@ -26,6 +31,39 @@ use std::collections::HashMap;
2631
pub fn attach_block_cell(txn: &StoreTransaction, block: &BlockView) -> Result<(), Error> {
2732
let transactions = block.transactions();
2833

34+
// update txo root mmr
35+
let block_number = block.header().number();
36+
let mut txo_root_mmr = txn.txo_root_mmr(block_number);
37+
for tx in transactions.iter() {
38+
for input in tx.inputs().into_iter() {
39+
let out_point = input.previous_output();
40+
// cellbase and genesis block's tx may not have previous output
41+
if let Some(mut txo_status) = txn.get_txo_mmr_status(&out_point) {
42+
txo_root_mmr
43+
.update(
44+
txo_status.mmr_position,
45+
hash_out_point_and_status(&out_point, txo_status.created_by, block_number),
46+
)
47+
.map_err(|e| InternalErrorKind::MMR.other(e))?;
48+
txo_status.mark_as_consumed(block_number);
49+
txn.insert_txo_root_mmr_status(&out_point, &txo_status)?;
50+
}
51+
}
52+
53+
for out_point in tx.output_pts().into_iter() {
54+
let hash = hash_out_point_and_status(&out_point, block_number, BlockNumber::MAX);
55+
let mmr_position = txo_root_mmr
56+
.push(hash)
57+
.map_err(|e| InternalErrorKind::MMR.other(e))?;
58+
let txo_status = TxoStatus::new(mmr_position, block_number);
59+
txn.insert_txo_root_mmr_status(&out_point, &txo_status)?;
60+
}
61+
}
62+
txo_root_mmr
63+
.commit()
64+
.map_err(|e| InternalErrorKind::MMR.other(e))?;
65+
txn.insert_txo_root_mmr_size(block_number, txo_root_mmr.mmr_size())?;
66+
2967
// add new live cells
3068
let new_cells = transactions
3169
.iter()
@@ -84,6 +122,46 @@ pub fn attach_block_cell(txn: &StoreTransaction, block: &BlockView) -> Result<()
84122
/// Undoes the effects of this block on the live cell set.
85123
pub fn detach_block_cell(txn: &StoreTransaction, block: &BlockView) -> Result<(), Error> {
86124
let transactions = block.transactions();
125+
126+
// undo txo root mmr updates
127+
let block_number = block.header().number();
128+
let mut txo_root_mmr = txn.txo_root_mmr(block_number);
129+
130+
for tx in transactions.iter() {
131+
for input in tx.inputs().into_iter() {
132+
let out_point = input.previous_output();
133+
if let Some(mut txo_status) = txn.get_txo_mmr_status(&out_point) {
134+
txo_root_mmr
135+
.update(
136+
txo_status.mmr_position,
137+
hash_out_point_and_status(
138+
&out_point,
139+
txo_status.created_by,
140+
BlockNumber::MAX,
141+
),
142+
)
143+
.map_err(|e| InternalErrorKind::MMR.other(e))?;
144+
txo_status.mark_as_live();
145+
txn.insert_txo_root_mmr_status(&out_point, &txo_status)?;
146+
}
147+
}
148+
149+
for out_point in tx.output_pts().into_iter() {
150+
txn.delete_txo_root_mmr_status(&out_point)?;
151+
}
152+
}
153+
txo_root_mmr
154+
.commit()
155+
.map_err(|e| InternalErrorKind::MMR.other(e))?;
156+
157+
let current_mmr_size = txn.get_txo_mmr_size(block_number);
158+
let pre_mmr_size = txn.get_txo_mmr_size(block_number - 1);
159+
for pos in pre_mmr_size..current_mmr_size {
160+
txn.delete_txo_root_mmr_element(pos, block_number)?;
161+
}
162+
txn.delete_txo_root_mmr_size(block_number)?;
163+
164+
// restore inputs
87165
let mut input_pts = HashMap::with_capacity(transactions.len());
88166

89167
for tx in transactions.iter().skip(1) {
@@ -95,7 +173,6 @@ pub fn detach_block_cell(txn: &StoreTransaction, block: &BlockView) -> Result<()
95173
}
96174
}
97175

98-
// restore inputs
99176
// skip cellbase
100177
let undo_deads = input_pts
101178
.iter()

store/src/store.rs

+43-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ use ckb_db_schema::{
99
COLUMN_BLOCK_FILTER, COLUMN_BLOCK_FILTER_HASH, COLUMN_BLOCK_HEADER, COLUMN_BLOCK_PROPOSAL_IDS,
1010
COLUMN_BLOCK_UNCLE, COLUMN_CELL, COLUMN_CELL_DATA, COLUMN_CELL_DATA_HASH,
1111
COLUMN_CHAIN_ROOT_MMR, COLUMN_EPOCH, COLUMN_INDEX, COLUMN_META, COLUMN_TRANSACTION_INFO,
12-
COLUMN_UNCLES, META_CURRENT_EPOCH_KEY, META_LATEST_BUILT_FILTER_DATA_KEY, META_TIP_HEADER_KEY,
12+
COLUMN_TXO_ROOT_MMR, COLUMN_UNCLES, META_CURRENT_EPOCH_KEY, META_LATEST_BUILT_FILTER_DATA_KEY,
13+
META_TIP_HEADER_KEY, TXO_ROOT_MMR_ELEMENT_KEY_PREFIX, TXO_ROOT_MMR_SIZE_KEY_PREFIX,
14+
TXO_ROOT_MMR_STATUS_KEY_PREFIX,
1315
};
1416
use ckb_freezer::Freezer;
1517
use ckb_types::{
@@ -20,6 +22,8 @@ use ckb_types::{
2022
},
2123
packed::{self, OutPoint},
2224
prelude::*,
25+
utilities::merkle_mountain_range::TxoStatus,
26+
H256,
2327
};
2428

2529
/// The `ChainStore` trait provides chain data store interface
@@ -576,6 +580,44 @@ pub trait ChainStore: Send + Sync + Sized {
576580
})
577581
}
578582

583+
/// Gets txo mmr size by block number
584+
fn get_txo_mmr_size(&self, block_number: BlockNumber) -> u64 {
585+
let start_key = [TXO_ROOT_MMR_SIZE_KEY_PREFIX, &block_number.to_be_bytes()].concat();
586+
self.get_iter(
587+
COLUMN_TXO_ROOT_MMR,
588+
IteratorMode::From(&start_key, Direction::Reverse),
589+
)
590+
.take_while(|(key, _)| key.starts_with(TXO_ROOT_MMR_SIZE_KEY_PREFIX))
591+
.next()
592+
.map(|(_key, value)| u64::from_le_bytes(value.as_ref().try_into().expect("stored u64")))
593+
.unwrap_or_default()
594+
}
595+
596+
/// Gets txo mmr status by out point
597+
fn get_txo_mmr_status(&self, out_point: &OutPoint) -> Option<TxoStatus> {
598+
let key = [TXO_ROOT_MMR_STATUS_KEY_PREFIX, out_point.as_slice()].concat();
599+
self.get(COLUMN_TXO_ROOT_MMR, &key)
600+
.map(|slice| TxoStatus::new_unchecked(slice.as_ref()))
601+
}
602+
603+
/// Gets txo mmr element by position and block number
604+
fn get_txo_mmr_element(&self, position: u64, block_number: BlockNumber) -> Option<H256> {
605+
// block_number is stored as big endian for prefix search in lexicographical order
606+
let start_key = [
607+
TXO_ROOT_MMR_ELEMENT_KEY_PREFIX,
608+
&position.to_le_bytes(),
609+
&block_number.to_be_bytes(),
610+
]
611+
.concat();
612+
self.get_iter(
613+
COLUMN_TXO_ROOT_MMR,
614+
IteratorMode::From(&start_key, Direction::Reverse),
615+
)
616+
.take_while(|(key, _)| key.starts_with(&start_key[0..9]))
617+
.next()
618+
.map(|(_key, value)| H256::from_slice(value.as_ref()).expect("stored H256"))
619+
}
620+
579621
/// Gets ancestor block header by a base block hash and number
580622
fn get_ancestor(&self, base: &packed::Byte32, number: BlockNumber) -> Option<HeaderView> {
581623
let header = self.get_block_header(base)?;

0 commit comments

Comments
 (0)