diff --git a/Cargo.lock b/Cargo.lock index 4a214504..8720cb6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5298,6 +5298,7 @@ dependencies = [ "bitcoin 0.32.2", "frame-support", "frame-system", + "hex-conservative 0.2.1", "log", "parity-scale-codec", "scale-info", diff --git a/crates/pallet-bitcoin/Cargo.toml b/crates/pallet-bitcoin/Cargo.toml index 8122f829..12488c2c 100644 --- a/crates/pallet-bitcoin/Cargo.toml +++ b/crates/pallet-bitcoin/Cargo.toml @@ -19,6 +19,9 @@ sp-runtime = { workspace = true, default-features = false } sp-std = { workspace = true, default-features = false } subcoin-runtime-primitives = { workspace = true, default-features = false } +[dev-dependencies] +hex = { package = "hex-conservative", version = "0.2.0", features = ["alloc"] } + [features] default = ["std"] std = [ diff --git a/crates/pallet-bitcoin/src/lib.rs b/crates/pallet-bitcoin/src/lib.rs index 0aea9afc..1268c115 100644 --- a/crates/pallet-bitcoin/src/lib.rs +++ b/crates/pallet-bitcoin/src/lib.rs @@ -12,14 +12,12 @@ #[cfg(test)] mod tests; +pub mod types; -use bitcoin::consensus::{Decodable, Encodable}; -use bitcoin::{OutPoint, Transaction as BitcoinTransaction}; -use codec::{Decode, Encode, MaxEncodedLen}; +use self::types::{OutPoint, Transaction, Txid}; +use bitcoin::consensus::Decodable; use frame_support::dispatch::DispatchResult; use frame_support::weights::Weight; -use scale_info::TypeInfo; -use sp_core::H256; use sp_runtime::traits::BlockNumberProvider; use sp_runtime::SaturatedConversion; use sp_std::prelude::*; @@ -30,55 +28,7 @@ use subcoin_runtime_primitives::Coin; pub use pallet::*; /// Transaction output index. -pub type Vout = u32; - -/// Wrapper type for Bitcoin txid in runtime as `bitcoin::Txid` does not implement codec. -#[derive(Clone, TypeInfo, Encode, Decode, MaxEncodedLen)] -pub struct Txid(H256); - -impl Txid { - fn from_bitcoin_txid(txid: bitcoin::Txid) -> Self { - let mut d = Vec::with_capacity(32); - txid.consensus_encode(&mut d) - .expect("txid must be encoded correctly; qed"); - - let d: [u8; 32] = d - .try_into() - .expect("Bitcoin txid is sha256 hash which must fit into [u8; 32]; qed"); - - Self(H256::from(d)) - } - - /// Converts the runtime [`Txid`] to a `bitcoin::Txid`. - pub fn into_bitcoin_txid(self) -> bitcoin::Txid { - bitcoin::consensus::Decodable::consensus_decode(&mut self.encode().as_slice()) - .expect("Decode must succeed as txid was ensured to be encoded correctly; qed") - } -} - -impl core::fmt::Debug for Txid { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - for byte in self.0.as_bytes().iter().rev() { - write!(f, "{:02x}", byte)?; - } - Ok(()) - } -} - -#[derive(Debug, TypeInfo, Encode, Decode, MaxEncodedLen)] -struct OutPointInner { - txid: Txid, - vout: Vout, -} - -impl From for OutPointInner { - fn from(out_point: OutPoint) -> Self { - Self { - txid: Txid::from_bitcoin_txid(out_point.txid), - vout: out_point.vout, - } - } -} +pub type OutputIndex = u32; #[frame_support::pallet] pub mod pallet { @@ -99,14 +49,13 @@ pub mod pallet { #[pallet::call(weight(::WeightInfo))] impl Pallet { - /// An internal unsigned extrinsic for including a Bitcoin transaction into the block. + /// An unsigned extrinsic for embedding a Bitcoin transaction into the Substrate block. #[pallet::call_index(0)] #[pallet::weight(Weight::zero())] - pub fn transact(origin: OriginFor, btc_tx: Vec) -> DispatchResult { + pub fn transact(origin: OriginFor, btc_tx: Transaction) -> DispatchResult { ensure_none(origin)?; - let bitcoin_transaction = Self::decode_transaction(btc_tx); - Self::process_bitcoin_transaction(bitcoin_transaction); + Self::process_bitcoin_transaction(btc_tx.into()); Ok(()) } @@ -147,7 +96,13 @@ pub mod pallet { #[pallet::genesis_build] impl BuildGenesisConfig for GenesisConfig { fn build(&self) { - let genesis_tx = Pallet::::decode_transaction(self.genesis_tx.clone()); + let genesis_tx = + bitcoin::Transaction::consensus_decode(&mut self.genesis_tx.clone().as_slice()) + .unwrap_or_else(|_| { + panic!( + "Transaction constructed internally must be decoded successfully; qed" + ) + }); let txid = Txid::from_bitcoin_txid(genesis_tx.compute_txid()); @@ -172,17 +127,18 @@ pub mod pallet { /// UTXO set. /// - /// (Txid, Vout, Coin) + /// (Txid, OutputIndex(vout), Coin) #[pallet::storage] - pub type Coins = StorageDoubleMap<_, Identity, Txid, Identity, Vout, Coin, OptionQuery>; + pub type Coins = + StorageDoubleMap<_, Identity, Txid, Identity, OutputIndex, Coin, OptionQuery>; } /// Returns the storage key for the referenced output. -pub fn coin_storage_key(bitcoin_txid: bitcoin::Txid, index: Vout) -> Vec { +pub fn coin_storage_key(bitcoin_txid: bitcoin::Txid, vout: OutputIndex) -> Vec { use frame_support::storage::generator::StorageDoubleMap; let txid = Txid::from_bitcoin_txid(bitcoin_txid); - Coins::::storage_double_map_final_key(txid, index) + Coins::::storage_double_map_final_key(txid, vout) } /// Returns the final storage prefix for the storage item `Coins`. @@ -193,20 +149,14 @@ pub fn coin_storage_prefix() -> [u8; 32] { } impl Pallet { - fn decode_transaction(btc_tx: Vec) -> BitcoinTransaction { - BitcoinTransaction::consensus_decode(&mut btc_tx.as_slice()).unwrap_or_else(|_| { - panic!("Transaction constructed internally must be decoded successfully; qed") - }) - } - - fn process_bitcoin_transaction(tx: BitcoinTransaction) { + fn process_bitcoin_transaction(tx: bitcoin::Transaction) { let txid = tx.compute_txid(); let is_coinbase = tx.is_coinbase(); let height = frame_system::Pallet::::current_block_number(); let new_coins = tx.output.into_iter().enumerate().map(|(index, txout)| { - let out_point = OutPoint { + let out_point = bitcoin::OutPoint { txid, vout: index as u32, }; @@ -221,8 +171,8 @@ impl Pallet { if is_coinbase { for (out_point, coin) in new_coins { - let OutPointInner { txid, vout } = OutPointInner::from(out_point); - Coins::::insert(txid, vout, coin); + let OutPoint { txid, output_index } = OutPoint::from(out_point); + Coins::::insert(txid, output_index, coin); } return; } @@ -230,8 +180,8 @@ impl Pallet { // Process inputs. for input in tx.input { let previous_output = input.previous_output; - let OutPointInner { txid, vout } = OutPointInner::from(previous_output); - if let Some(_spent) = Coins::::take(txid, vout) { + let OutPoint { txid, output_index } = OutPoint::from(previous_output); + if let Some(_spent) = Coins::::take(txid, output_index) { } else { panic!("Corruputed state, UTXO {previous_output:?} not found"); } @@ -239,8 +189,8 @@ impl Pallet { // Process outputs. for (out_point, coin) in new_coins { - let OutPointInner { txid, vout } = OutPointInner::from(out_point); - Coins::::insert(txid, vout, coin); + let OutPoint { txid, output_index } = OutPoint::from(out_point); + Coins::::insert(txid, output_index, coin); } } } diff --git a/crates/pallet-bitcoin/src/tests.rs b/crates/pallet-bitcoin/src/tests.rs index eb752a4d..e8e60359 100644 --- a/crates/pallet-bitcoin/src/tests.rs +++ b/crates/pallet-bitcoin/src/tests.rs @@ -1,4 +1,5 @@ -use bitcoin::consensus::Encodable; +use bitcoin::consensus::{deserialize, Encodable}; +use hex::test_hex_unwrap as hex; use sp_core::Encode; #[test] @@ -12,5 +13,22 @@ fn test_runtime_txid_type() { let mut d = Vec::new(); txid.consensus_encode(&mut d) .expect("txid must be encoded correctly; qed"); + d.reverse(); assert_eq!(d, runtime_txid.encode()); } + +#[test] +fn test_runtime_transaction_type() { + let tx_bytes = hex!( + "02000000000101595895ea20179de87052b4046dfe6fd515860505d6511a9004cf12a1f93cac7c01000000\ + 00ffffffff01deb807000000000017a9140f3444e271620c736808aa7b33e370bd87cb5a078702483045022\ + 100fb60dad8df4af2841adc0346638c16d0b8035f5e3f3753b88db122e70c79f9370220756e6633b17fd271\ + 0e626347d28d60b0a2d6cbb41de51740644b9fb3ba7751040121028fa937ca8cba2197a37c007176ed89410\ + 55d3bcb8627d085e94553e62f057dcc00000000" + ); + let tx: bitcoin::Transaction = deserialize(&tx_bytes).unwrap(); + + let runtime_tx: crate::types::Transaction = tx.clone().into(); + + assert_eq!(tx, bitcoin::Transaction::from(runtime_tx)); +} diff --git a/crates/pallet-bitcoin/src/types.rs b/crates/pallet-bitcoin/src/types.rs new file mode 100644 index 00000000..8781c247 --- /dev/null +++ b/crates/pallet-bitcoin/src/types.rs @@ -0,0 +1,271 @@ +use bitcoin::consensus::Encodable; +use bitcoin::locktime::absolute; +use codec::{Decode, Encode, MaxEncodedLen}; +use scale_info::TypeInfo; +use sp_core::H256; +use sp_std::vec::Vec; + +/// An absolute lock time value, representing either a block height or a UNIX timestamp (seconds +/// since epoch). +#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq)] +pub enum LockTime { + /// A block height lock time value. + Height(u32), + /// A UNIX timestamp lock time value. + /// + /// A UNIX timestamp, seconds since epoch, guaranteed to always contain a valid time value. + Time(u32), +} + +impl From for LockTime { + fn from(lock_time: absolute::LockTime) -> Self { + match lock_time { + absolute::LockTime::Blocks(n) => Self::Height(n.to_consensus_u32()), + absolute::LockTime::Seconds(n) => Self::Time(n.to_consensus_u32()), + } + } +} + +impl From for absolute::LockTime { + fn from(val: LockTime) -> Self { + match val { + LockTime::Height(n) => Self::Blocks( + absolute::Height::from_consensus(n).expect("Invalid height in LockTime"), + ), + LockTime::Time(n) => { + Self::Seconds(absolute::Time::from_consensus(n).expect("Invalid time in LockTime")) + } + } + } +} + +/// Wrapper type for representing [`bitcoin::Txid`] in runtime, stored in reversed byte order. +#[derive(Clone, TypeInfo, Encode, Decode, MaxEncodedLen, PartialEq)] +pub struct Txid(H256); + +impl Txid { + /// Converts a [`bitcoin::Txid`]` to a [`Txid`]. + pub fn from_bitcoin_txid(txid: bitcoin::Txid) -> Self { + let mut bytes = Vec::with_capacity(32); + txid.consensus_encode(&mut bytes) + .expect("txid must be encoded correctly; qed"); + + bytes.reverse(); + + let bytes: [u8; 32] = bytes + .try_into() + .expect("Bitcoin txid is sha256 hash which must fit into [u8; 32]; qed"); + + Self(H256::from(bytes)) + } + + /// Converts a [`Txid`] to a [`bitcoin::Txid`]. + pub fn into_bitcoin_txid(self) -> bitcoin::Txid { + let mut bytes = self.encode(); + bytes.reverse(); + bitcoin::consensus::Decodable::consensus_decode(&mut bytes.as_slice()) + .expect("Decode must succeed as txid was guaranteed to be encoded correctly; qed") + } +} + +impl core::fmt::Debug for Txid { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + for byte in self.0.as_bytes().iter() { + write!(f, "{:02x}", byte)?; + } + Ok(()) + } +} + +/// A reference to a transaction output. +#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq, MaxEncodedLen)] +pub struct OutPoint { + /// The transaction ID of the referenced output. + pub txid: Txid, + /// The index of the output within the referenced transaction. + pub output_index: u32, +} + +impl From for OutPoint { + fn from(out_point: bitcoin::OutPoint) -> Self { + Self { + txid: Txid::from_bitcoin_txid(out_point.txid), + output_index: out_point.vout, + } + } +} + +impl From for bitcoin::OutPoint { + fn from(val: OutPoint) -> Self { + bitcoin::OutPoint { + txid: val.txid.into_bitcoin_txid(), + vout: val.output_index, + } + } +} + +/// The Witness is the data used to unlock bitcoin since the [segwit upgrade]. +/// +/// Can be logically seen as an array of bytestrings, i.e. `Vec>`, and it is serialized on the wire +/// in that format. +/// +/// [segwit upgrade]: +#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq)] +pub struct Witness { + /// Contains the witness `Vec>` serialization without the initial varint indicating the + /// number of elements (which is stored in `witness_elements`). + content: Vec, + /// The number of elements in the witness. + /// + /// Stored separately (instead of as a VarInt in the initial part of content) so that methods + /// like [`Witness::push`] don't have to shift the entire array. + // usize + witness_elements: u64, + /// This is the valid index pointing to the beginning of the index area. This area is 4 * + /// stack_size bytes at the end of the content vector which stores the indices of each item. + indices_start: u64, +} + +/// Regular bitcoin transaction input. +/// +/// Structurely same with [`bitcoin::TxIn`]. +#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq)] +pub struct RegularTxIn { + /// The reference to the previous output that is being used as an input. + pub previous_output: OutPoint, + /// The unlocking script (scriptSig) that pushes values onto the stack, + /// enabling the referenced output's script to be satisfied. + pub unlocking_script: Vec, + /// The sequence number, which suggests to miners which of two + /// conflicting transactions should be preferred, or 0xFFFFFFFF + /// to ignore this feature. This is generally never used since + /// the miner behavior cannot be enforced. + pub sequence: u32, + /// Witness data: an array of byte-arrays. + /// Note that this field is *not* (de)serialized with the rest of the TxIn in + /// Encodable/Decodable, as it is (de)serialized at the end of the full + /// Transaction. It *is* (de)serialized with the rest of the TxIn in other + /// (de)serialization routines. + pub witness: Vec>, +} + +/// Bitcoin transaction input. +/// +/// This type is a wrapper around [`bitcoin::TxIn`], designed to provide a user-friendly representation +/// of transaction inputs (`TxIn`) in polkadot.js.org. It handles both coinbase (block reward) +/// transactions and standard transactions. +#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq)] +pub enum TxIn { + /// Input from a coinbase transaction, which does not reference any previous output. + Coinbase { + /// Arbitrary data used in the coinbase transaction, such as extra nonce or miner-specific information. + coinbase_data: Vec, + }, + /// Input from a regular transaction, which references a previous output (`OutPoint`). + Regular(RegularTxIn), +} + +impl From for TxIn { + fn from(txin: bitcoin::TxIn) -> Self { + if txin.previous_output.is_null() { + Self::Coinbase { + coinbase_data: txin.script_sig.into_bytes(), + } + } else { + Self::Regular(RegularTxIn { + previous_output: txin.previous_output.into(), + unlocking_script: txin.script_sig.into_bytes(), + sequence: txin.sequence.0, + witness: txin.witness.to_vec(), + }) + } + } +} + +impl From for bitcoin::TxIn { + fn from(val: TxIn) -> Self { + match val { + TxIn::Coinbase { coinbase_data } => bitcoin::TxIn { + previous_output: bitcoin::OutPoint::null(), + script_sig: bitcoin::ScriptBuf::from_bytes(coinbase_data), + sequence: bitcoin::Sequence::MAX, + witness: bitcoin::Witness::new(), + }, + TxIn::Regular(RegularTxIn { + previous_output, + unlocking_script, + sequence, + witness, + }) => bitcoin::TxIn { + previous_output: previous_output.into(), + script_sig: bitcoin::ScriptBuf::from_bytes(unlocking_script), + sequence: bitcoin::Sequence(sequence), + witness: witness.into(), + }, + } + } +} + +/// Bitcoin transaction output. +#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq)] +pub struct TxOut { + /// The value of the output, in satoshis. + pub value: u64, + /// The script which must be satisfied for the output to be spent. + pub script_pubkey: Vec, +} + +/// Bitcoin transaction. +#[derive(Clone, Debug, Encode, Decode, TypeInfo, PartialEq)] +pub struct Transaction { + /// The protocol version, is currently expected to be 1 or 2 (BIP 68). + pub version: i32, + /// Block height or timestamp. Transaction cannot be included in a block until this height/time. + pub lock_time: LockTime, + /// List of transaction inputs. + pub input: Vec, + /// List of transaction outputs. + pub output: Vec, +} + +impl From for bitcoin::Transaction { + fn from(val: Transaction) -> Self { + let Transaction { + version, + lock_time, + input, + output, + } = val; + + bitcoin::Transaction { + version: bitcoin::transaction::Version(version), + lock_time: lock_time.into(), + input: input.into_iter().map(Into::into).collect(), + output: output + .into_iter() + .map(|txout| bitcoin::TxOut { + value: bitcoin::Amount::from_sat(txout.value), + script_pubkey: bitcoin::ScriptBuf::from_bytes(txout.script_pubkey), + }) + .collect(), + } + } +} + +impl From for Transaction { + fn from(btc_tx: bitcoin::Transaction) -> Self { + Self { + version: btc_tx.version.0, + lock_time: btc_tx.lock_time.into(), + input: btc_tx.input.into_iter().map(Into::into).collect(), + output: btc_tx + .output + .into_iter() + .map(|txout| TxOut { + value: txout.value.to_sat(), + script_pubkey: txout.script_pubkey.into_bytes(), + }) + .collect(), + } + } +} diff --git a/crates/sc-consensus-nakamoto/src/block_import.rs b/crates/sc-consensus-nakamoto/src/block_import.rs index bbf3bffd..a10433d9 100644 --- a/crates/sc-consensus-nakamoto/src/block_import.rs +++ b/crates/sc-consensus-nakamoto/src/block_import.rs @@ -284,7 +284,8 @@ where let extrinsics = block .txdata - .iter() + .clone() + .into_iter() .map(TransactionAdapter::bitcoin_transaction_into_extrinsic) .collect::>(); diff --git a/crates/subcoin-node/src/commands/blockchain.rs b/crates/subcoin-node/src/commands/blockchain.rs index edd23ae5..36048606 100644 --- a/crates/subcoin-node/src/commands/blockchain.rs +++ b/crates/subcoin-node/src/commands/blockchain.rs @@ -280,7 +280,7 @@ fn fetch_utxo_set_at( client .storage_pairs(substrate_block_hash, Some(&storage_key), None)? .filter_map(|(key, value)| { - let (txid, vout) = <(pallet_bitcoin::Txid, u32)>::decode( + let (txid, vout) = <(pallet_bitcoin::types::Txid, u32)>::decode( &mut &key.0.as_slice()[FINAL_STORAGE_PREFIX_LEN..], ) .expect("UTXO key type must be correct; qed"); diff --git a/crates/subcoin-node/src/commands/tools.rs b/crates/subcoin-node/src/commands/tools.rs index 3914e8ee..686547f2 100644 --- a/crates/subcoin-node/src/commands/tools.rs +++ b/crates/subcoin-node/src/commands/tools.rs @@ -29,6 +29,8 @@ pub enum Tools { fn revert_sha256d(h256d: &str) -> sc_cli::Result { let hash: sha256d::Hash = h256d + .strip_prefix("0x") + .unwrap_or(h256d) .parse() .map_err(|err| sc_cli::Error::Input(format!("Invalid h256: {err}")))?; diff --git a/crates/subcoin-primitives/src/lib.rs b/crates/subcoin-primitives/src/lib.rs index 4accf993..b7acf739 100644 --- a/crates/subcoin-primitives/src/lib.rs +++ b/crates/subcoin-primitives/src/lib.rs @@ -72,7 +72,7 @@ pub trait BitcoinTransactionAdapter { fn extrinsic_to_bitcoin_transaction(extrinsics: &Block::Extrinsic) -> Transaction; /// Converts a Bitcoin transaction into a Substrate extrinsic. - fn bitcoin_transaction_into_extrinsic(btc_tx: &Transaction) -> Block::Extrinsic; + fn bitcoin_transaction_into_extrinsic(btc_tx: Transaction) -> Block::Extrinsic; } /// Trait for interfacing with the Bitcoin storage. diff --git a/crates/subcoin-service/src/genesis_block_builder.rs b/crates/subcoin-service/src/genesis_block_builder.rs index 6d2b6598..03d3bf12 100644 --- a/crates/subcoin-service/src/genesis_block_builder.rs +++ b/crates/subcoin-service/src/genesis_block_builder.rs @@ -78,7 +78,8 @@ where let extrinsics = block .txdata - .iter() + .clone() + .into_iter() .map(TransactionAdapter::bitcoin_transaction_into_extrinsic) .collect::>(); diff --git a/crates/subcoin-service/src/transaction_adapter.rs b/crates/subcoin-service/src/transaction_adapter.rs index 21f23ef0..a3bb14f0 100644 --- a/crates/subcoin-service/src/transaction_adapter.rs +++ b/crates/subcoin-service/src/transaction_adapter.rs @@ -1,4 +1,3 @@ -use bitcoin::consensus::{Decodable, Encodable}; use bitcoin::Transaction; use sp_core::{Decode, Encode}; use sp_runtime::traits::{Block as BlockT, Extrinsic}; @@ -30,27 +29,24 @@ impl BitcoinTransactionAdapter for TransactionAdapter { subcoin_runtime::Runtime, >::transact { btc_tx, - }) => Transaction::consensus_decode(&mut btc_tx.as_slice()) - .expect("Transaction decode must succeed otherwise the chain is broken; qed"), + }) => btc_tx.into(), _ => unreachable!("Transactions only exist in pallet-bitcoin; qed"), } } - fn bitcoin_transaction_into_extrinsic(btc_tx: &bitcoin::Transaction) -> Block::Extrinsic { - let mut data = Vec::new(); - btc_tx - .consensus_encode(&mut data) - .expect("Encoding bitcoin tx in a bitcoin block must succeed; qed"); - + fn bitcoin_transaction_into_extrinsic(btc_tx: bitcoin::Transaction) -> Block::Extrinsic { Decode::decode( &mut subcoin_runtime::UncheckedExtrinsic::new( - pallet_bitcoin::Call::::transact { btc_tx: data }.into(), + pallet_bitcoin::Call::::transact { + btc_tx: btc_tx.into(), + } + .into(), None, ) - .expect("Internally construct extrinsic must not fail; qed") + .expect("Extrinsic constructed internally must not fail; qed") .encode() .as_slice(), ) - .expect("Internally construct extrinsic must not fail; qed") + .expect("Extrinsic constructed internally must not fail; qed") } } diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 057e37f1..d537698d 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -8,7 +8,4 @@ # User Guide -- [Installation](installation.md) -- [Run a Node](run-a-node.md) - [Import Bitcoin Core Blocks](import-bitcoind-blocks.md) -- [Export Bitcoin State](dumptxoutset.md) diff --git a/docs/src/dumptxoutset.md b/docs/src/dumptxoutset.md deleted file mode 100644 index 6b194aab..00000000 --- a/docs/src/dumptxoutset.md +++ /dev/null @@ -1,17 +0,0 @@ -# Export Bitcoin State Compatible with Bitcoin Core - -## `dumptxoutset` Command - -Subcoin provides the ability to export the Bitcoin state (UTXO Set) into a binary file that is fully compatible with the output of Bitcoin Core's `dumptxoutset` command, allowing for seamless integration and compatibility with Bitcoin Core. - -To use this feature, check out this command: - -```bash -subcoin blockchain dumptxoutset --help -``` - -## Decentralized UTXO Set Snapshot Download (Coming Soon) - -You can download the Bitcoin state directly from the Subcoin P2P network in a fully decentralized manner, eliminating the need for trusted snapshot providers. This feature will allow you to retrieve the UTXO Set snapshot from the network, convert it into a format compatible with Bitcoin Core, and import it directly into a Bitcoin Core node. This makes Subcoin an ideal addition to the Bitcoin Core ecosystem, providing a decentralized, secure, and efficient method for syncing with Bitcoin’s UTXO set. - -Follow [Subcoin Issue #54](https://github.com/subcoin-project/subcoin/issues/54) for this complementary feature, which will enhance the functionality of Bitcoin Core by integrating decentralized state retrieval from Subcoin. diff --git a/docs/src/installation.md b/docs/src/installation.md deleted file mode 100644 index 7f613e19..00000000 --- a/docs/src/installation.md +++ /dev/null @@ -1,27 +0,0 @@ -# Installing Subcoin - -## Compile from source - -### Install prerequisites - -Subcoin is a Substrate-based chain, so you need to install the necessary development tools to compile the binary from source. Follow the full guide for installing the prerequisites at [Substrate Installation Guide](https://docs.substrate.io/install/). - -### Compile subcoin node binary - -After installing the required packages and Rust, proceed to compile the binary using the following command: - -```bash -cargo build --profile production --bin subcoin -``` - -Once the compilation is complete, the Subcoin node executable subcoin will be located at `target/production/subcoin`. - -## Docker - -You can pull the Docker image built from the latest commit of the main branch with the following command: - -```bash -docker pull ghcr.io/subcoin-project/subcoin:main -``` - -For a list of all available versions, refer to the [Subcoin Docker Container Versions](https://github.com/subcoin-project/subcoin/pkgs/container/subcoin/versions). diff --git a/docs/src/run-a-node.md b/docs/src/run-a-node.md deleted file mode 100644 index d84b1fdb..00000000 --- a/docs/src/run-a-node.md +++ /dev/null @@ -1,15 +0,0 @@ -# Run Subcoin Node - -## System Requirements - -TODO - -## Syncing the Bitcoin Network - -Run the following command to sync the Bitcoin blockchain from the Bitcoin P2P network. The `--log subcoin_network=debug` option -will enable debug-level logging to show detailed information about the syncing process. By default, the block will be fully verified. -You can use `--block-verification=none` to skip the block verification. Check out `subcoin run --help` for more options. - -```bash -subcoin run -d data --log subcoin_network=debug -```