Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spv proof #1207

Merged
merged 82 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
82 commits
Select commit Hold shift + click to select a range
b53c03d
feat(unit_tests): add begin of unit test for spv proof
Milerius Jan 16, 2022
8b1ccc5
feat(utxo): continue test
Milerius Jan 17, 2022
b175b74
feat(unit_tests): very wip
Milerius Jan 18, 2022
3c122b1
feat(utxo): merkle tree verification now work; need full refactoring
Milerius Jan 18, 2022
971503a
feat(spv): start spv validation module
Milerius Jan 20, 2022
6570e07
feat(spv): add spv proof
Milerius Jan 20, 2022
da81a73
feat(spv): add merkle proof unit test for a single element
Milerius Jan 25, 2022
c888b42
feat(spv): add complex merkle proof inclusion unit test
Milerius Jan 25, 2022
a97f4d1
feat(spv): fix some cargo warnings
Milerius Jan 25, 2022
f9f9708
Merge branch 'dev' of https://github.com/KomodoPlatform/atomicDEX-API…
Milerius Feb 7, 2022
7f09afa
feat(spv): simplify error check for validate_spv_proof
Milerius Feb 7, 2022
b43aed6
feat(spv): complete the first step of the spv proof validation
Milerius Feb 7, 2022
8265073
feat(spv): complete the unit test for spv proof validation in utxo mo…
Milerius Feb 7, 2022
9176fd1
feat(spv_validation): add vin and vout check for spv proof validation
Milerius Feb 9, 2022
e80eafe
feat(spv_validation): match exact error from the spv - will customize…
Milerius Feb 9, 2022
a1940c1
Merge branch 'dev' of https://github.com/KomodoPlatform/atomicDEX-API…
Milerius Feb 14, 2022
1e405bb
Merge branch 'dev' of https://github.com/KomodoPlatform/atomicDEX-API…
Milerius Feb 16, 2022
db324f7
feat(utxo): start utxo block header storage + sync with dev
Milerius Feb 16, 2022
5fd7d6a
feat(utxo): continue block header storage interface
Milerius Feb 16, 2022
773b27b
feat(utxo): revert sled - will switch to sqllite
Milerius Feb 16, 2022
b3b8750
feat(utxo): start utxo sql block header storage trait implementation
Milerius Feb 16, 2022
f3728f8
feat(sql): implement partially add_block_headers_to_storage and the u…
Milerius Feb 17, 2022
5c408a8
feat(sql): add get_block header functions
Milerius Feb 17, 2022
d880ff8
feat(sql): add extra function for block insertion
Milerius Feb 18, 2022
b62ab5a
feat(lib_bitcoin): add raw header type
Milerius Feb 21, 2022
3710ea7
feat(spv): Integrate raw block header into spv validation
Milerius Feb 21, 2022
0a0eb47
feat(spv): remove old comments
Milerius Feb 21, 2022
deba4d0
feat(spv): Adding raw header into utxo_common spv validation
Milerius Feb 21, 2022
5028d08
Merge branch 'dev' of https://github.com/KomodoPlatform/atomicDEX-API…
Milerius Feb 21, 2022
6bd7c36
feat(spv): make slp great again
Milerius Feb 21, 2022
3f9dfb6
feat(spv): returning more concrete error types
Milerius Feb 21, 2022
4c88900
feat(toolchain): update to `nightly-2022-02-01` to fix osx compilation
Milerius Feb 21, 2022
2f25426
feat(cargo): fix warnings
Milerius Feb 21, 2022
2368209
feat(fmt): rust fmt
Milerius Feb 21, 2022
8914b76
feat(utxo_block_header): add a box dyn for block header storage to ut…
Milerius Feb 22, 2022
1def807
Revert "feat(utxo_block_header): add a box dyn for block header stora…
Milerius Feb 22, 2022
f75aee9
feat(utxo): add empty utxo_indexedb_block_header_storage.rs
Milerius Feb 22, 2022
40e3cf7
feat(utxo): start an empty loop for downloading headers
Milerius Feb 22, 2022
4dedf0b
feat(indexed_db_block_header): do not use todo to prevent crash at ru…
Milerius Feb 22, 2022
b69e401
feat(dev): sync with dev
Milerius Feb 22, 2022
aab5c80
feat(block_header_loop): add block header loop in arc builder
Milerius Feb 22, 2022
1db867d
feat(utxo): add a function to retrieve a storage from the ctx for blo…
Milerius Feb 22, 2022
03e937b
feat(utxo): start logic of block header downloading, very WIP
Milerius Feb 22, 2022
93a6505
feat(header_validation): add partial header validation + params for e…
Milerius Feb 23, 2022
de9478c
feat(header_validation): add unexpected difficulty change check
Milerius Feb 23, 2022
c36d9b0
feat(header_validation): add unit test for validate headers
Milerius Feb 23, 2022
78fd499
feat(header_validation): document the validate_headers function
Milerius Feb 23, 2022
db57ce1
feat(header_validation): use appropriate error for validate_headers
Milerius Feb 23, 2022
43b272a
feat(storage): rework storage to make it persistent
Milerius Feb 24, 2022
7262e3f
feat(storage): use the block header storage as optional and fix unit …
Milerius Feb 24, 2022
f0aeecb
feat(storage): make the conf into coins settings
Milerius Feb 24, 2022
6707b6a
feat(dev): sync with dev
Milerius Feb 25, 2022
7a4c689
feat(storage): implement header from storage or rpc + within validation
Milerius Feb 28, 2022
43a311c
feat(improvements): remove non-used function
Milerius Feb 28, 2022
4e44bc3
feat(storage): add to storage after validation in retrieve header fro…
Milerius Feb 28, 2022
fa9d3e6
feat(dev): fix conflict
Milerius Feb 28, 2022
2ccd242
feat(fix_review): first fixes batch
Milerius Mar 2, 2022
33877d2
feat(fix_review): sql fixes + rename `from_address` to `sender_address`
Milerius Mar 2, 2022
b63dfd6
feat(fix_review): simplify the way to get height, use into_iter + find
Milerius Mar 2, 2022
3fc93ec
feat(fix_review): add a get_tx_height function + remove more marketco…
Milerius Mar 2, 2022
676d30c
feat(fix_review): simplify download_loop with a `try_loop` macro
Milerius Mar 2, 2022
e4504b4
feat(fix_review): improve get_tx_height
Milerius Mar 3, 2022
900e62a
feat(fix_review): continue review improvements, move retrieve_last_he…
Milerius Mar 3, 2022
1bba0df
feat(fix_review): remove dead code comment
Milerius Mar 3, 2022
a341fed
feat(spv): next batch of fixes
Milerius Mar 4, 2022
b998591
feat(review): remove utxo_wrapper_block_header_storage.rs
Milerius Mar 4, 2022
36999e5
feat(review): next batche of fixes
Milerius Mar 4, 2022
dfd4bd4
feat(review): refactor block_header_from_storage_or_rpc
Milerius Mar 4, 2022
dfbfceb
feat(wasm): use explicitly instant wasm bindgen
Milerius Mar 9, 2022
291b76e
feat(spv): fix wasm tests
Milerius Mar 11, 2022
0e38525
feat(spv): fix unit test compilation
Milerius Mar 11, 2022
ed97f35
feat(eth): ignore polygon unit test (unstable)
Milerius Mar 11, 2022
6621d5b
feat(unit tests): increase to 6 seconds for the docker unit test
Milerius Mar 14, 2022
27a4e8b
feat(utxo): simplify block_headers_storage, add error
Milerius Mar 15, 2022
863fcc2
feat(review): batch of review fixes
Milerius Mar 18, 2022
0d09094
feat(review): another batch of review fixes
Milerius Mar 18, 2022
5d19724
feat(eth_test): remove the loop
Milerius Mar 18, 2022
b071fcd
feat(eth_test): remove the loop
Milerius Mar 18, 2022
3beaf24
feat(spv_proof): review fixes
Milerius Mar 21, 2022
9c50442
feat(dev): sync with dev
Milerius Mar 21, 2022
b5cadc5
feat(spv_proof): review fixes
Milerius Mar 22, 2022
4ff3f4f
feat(spv): fix wrong copy paste description
Milerius Mar 23, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
391 changes: 285 additions & 106 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ num-rational = { version = "0.2", features = ["serde", "bigint", "bigint-std"] }
num-traits = "0.2"
rpc = { path = "mm2src/mm2_bitcoin/rpc" }
rpc_task = { path = "mm2src/rpc_task" }
parking_lot = { version = "0.11", features = ["nightly"] }
parking_lot = { version = "0.12.0", features = ["nightly"] }
parity-util-mem = "0.9"
# AP: portfolio RPCs are not documented and not used as of now
# so the crate is disabled to speed up the entire removal of C code
Expand All @@ -110,6 +110,7 @@ ser_error = { path = "mm2src/derives/ser_error" }
ser_error_derive = { path = "mm2src/derives/ser_error_derive" }
serialization = { path = "mm2src/mm2_bitcoin/serialization" }
serialization_derive = { path = "mm2src/mm2_bitcoin/serialization_derive" }
spv_validation = { path = "mm2src/mm2_bitcoin/spv_validation" }
sp-runtime-interface = { version = "3.0.0", default-features = false, features = ["disable_target_static_assertions"] }
sp-trie = { version = "3.0", default-features = false }

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ The current state can be considered as very early alpha.
1. (Optional) OSX: run `LIBRARY_PATH=/usr/local/opt/openssl/lib`
1. Run
```
rustup install nightly-2021-12-16
rustup default nightly-2021-12-16
rustup install nightly-2022-02-01
rustup default nightly-2022-02-01
rustup component add rustfmt-preview
```
1. Run `cargo build` (or `cargo build -vv` to get verbose build output).
Expand Down
5 changes: 3 additions & 2 deletions mm2src/coins/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ doctest = false

[dependencies]
async-std = { version = "1.5", features = ["unstable"] }
async-trait = "0.1"
async-trait = "0.1.52"
base64 = "0.10.0"
bigdecimal = { version = "0.1.0", features = ["serde"] }
bip32 = { version = "0.2.2", default-features = false, features = ["alloc", "secp256k1-ffi"] }
Expand Down Expand Up @@ -52,7 +52,7 @@ lightning-invoice = "0.12.0"
metrics = "0.12"
mocktopus = "0.7.0"
num-traits = "0.2"
parking_lot = { version = "0.11", features = ["nightly"] }
parking_lot = { version = "0.12.0", features = ["nightly"] }
primitives = { path = "../mm2_bitcoin/primitives" }
prost = "0.8"
rand = { version = "0.7", features = ["std", "small_rng"] }
Expand All @@ -69,6 +69,7 @@ serde_derive = "1.0"
serde_json = { version = "1.0", features = ["preserve_order", "raw_value"] }
serialization = { path = "../mm2_bitcoin/serialization" }
serialization_derive = { path = "../mm2_bitcoin/serialization_derive" }
spv_validation = { path = "../mm2_bitcoin/spv_validation" }
sha2 = "0.8"
sha3 = "0.8"
utxo_signer = { path = "utxo_signer" }
Expand Down
2 changes: 1 addition & 1 deletion mm2src/coins/lightning_persister/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ bitcoin = "0.27.1"
common = { path = "../../common" }
lightning = "0.0.104"
libc = "0.2"
parking_lot = { version = "0.11", features = ["nightly"] }
parking_lot = { version = "0.12.0", features = ["nightly"] }
secp256k1 = { version = "0.20" }
serde_json = "1.0"

Expand Down
45 changes: 45 additions & 0 deletions mm2src/coins/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,19 @@ mod bchd_pb;
pub mod qtum;
pub mod rpc_clients;
pub mod slp;
pub mod utxo_block_header_storage;
pub mod utxo_builder;
pub mod utxo_common;
pub mod utxo_standard;
pub mod utxo_withdraw;

#[cfg(not(target_arch = "wasm32"))] pub mod tx_cache;
#[cfg(target_arch = "wasm32")]
pub mod utxo_indexedb_block_header_storage;
#[cfg(not(target_arch = "wasm32"))]
pub mod utxo_sql_block_header_storage;
pub mod utxo_wrapper_block_header_storage;
use utxo_wrapper_block_header_storage::BlockHeaderStorage;

use async_trait::async_trait;
use bigdecimal::BigDecimal;
Expand Down Expand Up @@ -69,6 +76,7 @@ use rpc::v1::types::{Bytes as BytesJson, Transaction as RpcTransaction, H256 as
use script::{Builder, Script, SignatureVersion, TransactionInputSigner};
use serde_json::{self as json, Value as Json};
use serialization::{serialize, serialize_with_flags, SERIALIZE_TRANSACTION_WITNESS};
use spv_validation::types::SPVError;
use std::array::TryFromSliceError;
use std::collections::{HashMap, HashSet};
use std::convert::TryInto;
Expand All @@ -94,6 +102,7 @@ use super::{BalanceError, BalanceFut, BalanceResult, CoinsContext, DerivationMet
Transaction, TransactionDetails, TransactionEnum, TransactionFut, WithdrawError, WithdrawRequest};
use crate::coin_balance::HDAddressBalanceChecker;
use crate::hd_wallet::{HDAccountOps, HDAccountsMutex, HDAddress, HDWalletCoinOps, HDWalletOps, InvalidBip44ChainError};
use crate::utxo::utxo_block_header_storage::BlockHeaderStorageError;

#[cfg(test)] pub mod utxo_tests;
#[cfg(target_arch = "wasm32")] pub mod utxo_wasm_tests;
Expand Down Expand Up @@ -471,6 +480,7 @@ pub struct UtxoCoinConf {
/// Block count for median time past calculation
pub mtp_block_count: NonZeroU64,
pub estimate_fee_mode: Option<EstimateFeeMode>,
pub block_header_storage_params: Option<UtxoBlockHeaderVerificationParams>,
artemii235 marked this conversation as resolved.
Show resolved Hide resolved
/// The minimum number of confirmations at which a transaction is considered mature
pub mature_confirmations: u32,
/// The number of blocks used for estimate_fee/estimate_smart_fee RPC calls
Expand Down Expand Up @@ -501,6 +511,7 @@ pub struct UtxoCoinFields {
pub history_sync_state: Mutex<HistorySyncState>,
/// Path to the TX cache directory
pub tx_cache_directory: Option<PathBuf>,
pub block_headers_storage: Option<BlockHeaderStorage>,
artemii235 marked this conversation as resolved.
Show resolved Hide resolved
/// The cache of recently send transactions used to track the spent UTXOs and replace them with new outputs
/// The daemon needs some time to update the listunspent list for address which makes it return already spent UTXOs
/// This cache helps to prevent UTXO reuse in such cases
Expand Down Expand Up @@ -536,6 +547,31 @@ impl From<UnsupportedAddr> for WithdrawError {
fn from(e: UnsupportedAddr) -> Self { WithdrawError::InvalidAddress(e.to_string()) }
}

#[derive(Debug)]
pub enum GetBlockHeaderError {
StorageError(BlockHeaderStorageError),
RpcError(JsonRpcError),
SerializationError(serialization::Error),
SPVError(SPVError),
NativeNotSupported(String),
}

impl From<JsonRpcError> for GetBlockHeaderError {
fn from(err: JsonRpcError) -> Self { GetBlockHeaderError::RpcError(err) }
}

impl From<SPVError> for GetBlockHeaderError {
fn from(e: SPVError) -> Self { GetBlockHeaderError::SPVError(e) }
}

impl From<serialization::Error> for GetBlockHeaderError {
fn from(err: serialization::Error) -> Self { GetBlockHeaderError::SerializationError(err) }
}

impl From<BlockHeaderStorageError> for GetBlockHeaderError {
fn from(err: BlockHeaderStorageError) -> Self { GetBlockHeaderError::StorageError(err) }
}

impl UtxoCoinFields {
pub fn transaction_preimage(&self) -> TransactionInputSigner {
let lock_time = if self.conf.ticker == "KMD" {
Expand Down Expand Up @@ -1024,6 +1060,14 @@ pub struct UtxoMergeParams {
pub max_merge_at_once: usize,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct UtxoBlockHeaderVerificationParams {
pub difficulty_check: bool,
pub constant_difficulty: bool,
pub blocks_limit_to_check: u64,
pub check_every: f64,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct UtxoActivationParams {
pub mode: UtxoRpcMode,
Expand All @@ -1044,6 +1088,7 @@ pub enum UtxoFromLegacyReqErr {
UnexpectedMethod,
InvalidElectrumServers(json::Error),
InvalidMergeParams(json::Error),
InvalidBlockHeaderVerificationParams(json::Error),
InvalidRequiredConfs(json::Error),
InvalidRequiresNota(json::Error),
InvalidAddressFormat(json::Error),
Expand Down
30 changes: 14 additions & 16 deletions mm2src/coins/utxo/qtum_delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,14 +124,13 @@ impl QtumCoin {
let delegation_output = self.remove_delegation_output(QRC20_GAS_LIMIT_DEFAULT, QRC20_GAS_PRICE_DEFAULT)?;
let outputs = vec![delegation_output];
let my_address = self.my_address().map_to_mm(DelegationError::InternalError)?;
Ok(self
.generate_delegation_transaction(
outputs,
my_address,
QRC20_GAS_LIMIT_DEFAULT,
TransactionType::RemoveDelegation,
)
.await?)
self.generate_delegation_transaction(
outputs,
my_address,
QRC20_GAS_LIMIT_DEFAULT,
TransactionType::RemoveDelegation,
)
.await
}

async fn am_i_currently_staking(&self) -> Result<Option<String>, MmError<StakingInfosError>> {
Expand Down Expand Up @@ -252,14 +251,13 @@ impl QtumCoin {

let outputs = vec![delegation_output];
let my_address = self.my_address().map_to_mm(DelegationError::InternalError)?;
Ok(self
.generate_delegation_transaction(
outputs,
my_address,
QRC20_GAS_LIMIT_DELEGATION,
TransactionType::StakingDelegation,
)
.await?)
self.generate_delegation_transaction(
outputs,
my_address,
QRC20_GAS_LIMIT_DELEGATION,
TransactionType::StakingDelegation,
)
.await
}

async fn generate_delegation_transaction(
Expand Down
18 changes: 14 additions & 4 deletions mm2src/coins/utxo/rpc_clients.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1025,7 +1025,7 @@ impl Into<BlockHeaderNonce> for ElectrumNonce {

#[derive(Debug, Deserialize)]
pub struct ElectrumBlockHeadersRes {
count: u64,
pub count: u64,
pub hex: BytesJson,
#[allow(dead_code)]
max: u64,
Expand All @@ -1044,8 +1044,8 @@ pub struct ElectrumBlockHeaderV12 {
}

impl ElectrumBlockHeaderV12 {
pub fn hash(&self) -> H256Json {
let block_header = BlockHeader {
fn as_block_header(&self) -> BlockHeader {
BlockHeader {
version: self.version as u32,
previous_header_hash: self.prev_block_hash.into(),
merkle_root_hash: self.merkle_root.into(),
Expand All @@ -1064,7 +1064,17 @@ impl ElectrumBlockHeaderV12 {
n_height: None,
n_nonce_u64: None,
mix_hash: None,
};
}
}

pub fn as_hex(&self) -> String {
let block_header = self.as_block_header();
let serialized = serialize(&block_header);
hex::encode(serialized)
}

pub fn hash(&self) -> H256Json {
let block_header = self.as_block_header();
BlockHeader::hash(&block_header).into()
}
}
Expand Down
77 changes: 77 additions & 0 deletions mm2src/coins/utxo/utxo_block_header_storage.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use crate::utxo::rpc_clients::ElectrumBlockHeader;
use async_trait::async_trait;
use chain::BlockHeader;
use common::mm_error::MmError;
use derive_more::Display;
use std::collections::HashMap;

#[derive(Debug, Deserialize, Display, Serialize, SerializeErrorType)]
#[serde(tag = "error_type", content = "error_data")]
pub enum BlockHeaderStorageError {
artemii235 marked this conversation as resolved.
Show resolved Hide resolved
#[display(fmt = "The storage cannot be initialized for {}", _0)]
InitializationError(String),
sergeyboyko0791 marked this conversation as resolved.
Show resolved Hide resolved
#[display(fmt = "The storage is not initialized for {} - reason: {}", ticker, reason)]
NotInitializedError { ticker: String, reason: String },
#[display(fmt = "Can't add to the storage for {} - reason: {}", ticker, reason)]
AddToStorageError { ticker: String, reason: String },
#[display(fmt = "Can't add to the storage for {} - reason: {}", ticker, reason)]
sergeyboyko0791 marked this conversation as resolved.
Show resolved Hide resolved
GetFromStorageError { ticker: String, reason: String },
#[display(
fmt = "Can't retrieve the table from the storage for {} - reason: {}",
ticker,
reason
)]
CantRetrieveTableError { ticker: String, reason: String },
#[display(fmt = "Can't query from the storage - query: {} - reason: {}", query, reason)]
QueryError { query: String, reason: String },
#[display(fmt = "Can't retrieve string from row - reason: {}", reason)]
StringRowError { reason: String },
#[display(fmt = "Can't execute query from storage for {} - reason: {}", ticker, reason)]
ExecutionError { ticker: String, reason: String },
#[display(fmt = "Can't start a transaction from storage for {} - reason: {}", ticker, reason)]
TransactionError { ticker: String, reason: String },
#[display(fmt = "Can't commit a transaction from storage for {} - reason: {}", ticker, reason)]
CommitError { ticker: String, reason: String },
#[display(fmt = "Can't decode/deserialize from storage for {} - reason: {}", ticker, reason)]
DecodeError { ticker: String, reason: String },
}

#[async_trait]
pub trait BlockHeaderStorageOps: Send + Sync + 'static {
/// Initializes collection/tables in storage for a specified coin
async fn init(&self, for_coin: &str) -> Result<(), MmError<BlockHeaderStorageError>>;

async fn is_initialized_for(&self, for_coin: &str) -> Result<bool, MmError<BlockHeaderStorageError>>;

// Adds multiple block headers to the selected coin's header storage
// Should store it as `TICKER_HEIGHT=hex_string`
// use this function for headers that comes from `blockchain_headers_subscribe`
async fn add_electrum_block_headers_to_storage(
&self,
for_coin: &str,
headers: Vec<ElectrumBlockHeader>,
) -> Result<(), MmError<BlockHeaderStorageError>>;

// Adds multiple block headers to the selected coin's header storage
// Should store it as `TICKER_HEIGHT=hex_string`
// use this function for headers that comes from `blockchain_block_headers`
async fn add_block_headers_to_storage(
&self,
for_coin: &str,
headers: HashMap<u64, BlockHeader>,
) -> Result<(), MmError<BlockHeaderStorageError>>;

/// Gets the block header by height from the selected coin's storage as BlockHeader
async fn get_block_header(
&self,
for_coin: &str,
height: u64,
) -> Result<Option<BlockHeader>, MmError<BlockHeaderStorageError>>;

/// Gets the block header by height from the selected coin's storage as hex
async fn get_block_header_raw(
&self,
for_coin: &str,
height: u64,
) -> Result<Option<String>, MmError<BlockHeaderStorageError>>;
}
Loading