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

[r2r] spv storage optimization #1 #1585

Merged
merged 76 commits into from
Feb 13, 2023
Merged
Show file tree
Hide file tree
Changes from 66 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
a9e776e
save dev state — replaced enabled_spv_proof with SPVConf structure
borngraced Dec 14, 2022
47f76ad
save dev state
borngraced Dec 14, 2022
2731b25
impl get_total_block_headers_count_from_storage
borngraced Dec 16, 2022
275b5f3
save dev state — impl remove_block_headers_from_storage
borngraced Dec 16, 2022
64fb8c3
save dev state
borngraced Dec 17, 2022
2824d47
optimized spv block headers storage limit and starting block
borngraced Dec 17, 2022
c7f1afa
removed SPVConf from UtxoCoinField
borngraced Dec 17, 2022
866a0b5
save dev state — block_headers_limit_to_remove
borngraced Dec 17, 2022
f9ada57
save dev state — impl block_headers_limit_to_remove
borngraced Dec 18, 2022
462e3a6
save dev state — test_get_total_block_headers_from_storage unit test
borngraced Dec 19, 2022
7c56268
save dev state — test_remove_block_headers_from_storage unit test
borngraced Dec 19, 2022
08864ef
Merge remote-tracking branch 'origin/dev' into spv-optimizations
borngraced Dec 19, 2022
862553e
optimize spv storage
borngraced Dec 21, 2022
3ce2b23
minor changes to get_last_block_height
borngraced Dec 21, 2022
4b8a654
fix validate header fn
borngraced Dec 22, 2022
9584f86
fix tests
borngraced Dec 22, 2022
9c09e19
save dev state — headers difficulty validation
borngraced Dec 22, 2022
ff3cf63
save dev state — calculate_retarget_height and unit test
borngraced Dec 22, 2022
4ccf4ac
minor changes
borngraced Dec 22, 2022
828fad6
minor changes
borngraced Dec 23, 2022
c218712
fixed block header validation check
borngraced Dec 26, 2022
55bfce4
fix doc comments
borngraced Dec 26, 2022
265ef71
SPVVerificationFailed error kind
borngraced Dec 26, 2022
66c228c
UnableToDeleteHeaders error kind
borngraced Dec 27, 2022
0a7ae98
max_stored_block_headers must be greater than retarget_interval
borngraced Dec 27, 2022
526ef03
enhanced SPVConf and removed dup field
borngraced Dec 27, 2022
a7f6ddb
review changes and test_spv_conf_validate_starting_block_header
borngraced Dec 28, 2022
d558268
update doc comments
borngraced Dec 28, 2022
a054fb4
starting block header fixes
shamardy Dec 29, 2022
ad244e1
SPVConfNoVerification and SPVConfWithVerification
borngraced Jan 2, 2023
0ae51f7
spv_conf deserialization unit test
borngraced Jan 5, 2023
e5fc0a2
Merge remote-tracking branch 'origin/dev' into spv-optimizations
borngraced Jan 5, 2023
89b7aa2
remove Default impl
borngraced Jan 9, 2023
40bee1e
remove enable_spv_proof
shamardy Jan 9, 2023
174321c
Refactor SPVConf enum to a struct
shamardy Jan 9, 2023
300e63f
Merge remote-tracking branch 'origin/dev' into spv-optimizations
borngraced Jan 9, 2023
752a8a9
Merge remote-tracking branch 'origin/dev' into spv-optimizations
borngraced Jan 11, 2023
11855f1
fix test_block_header_utxo_loop
borngraced Jan 11, 2023
db01e59
fix review notes
borngraced Jan 11, 2023
575d228
StartingHeader -> StartingBlockHeader
borngraced Jan 12, 2023
d0b3465
validate_starting_block_header
borngraced Jan 12, 2023
315e24c
validate starting header based on difficulty algorithm
shamardy Jan 13, 2023
c8595a6
Merge remote-tracking branch 'origin/dev' into spv-optimizations
borngraced Jan 13, 2023
8efb2fe
fix test_block_header_utxo_loop
borngraced Jan 14, 2023
02a6c76
fix wasm
borngraced Jan 14, 2023
7798a5b
validate_spv_conf
borngraced Jan 14, 2023
35d72e1
fix todo
borngraced Jan 16, 2023
f6b1c0e
validate spv_conf in UtxoArcBuilder
borngraced Jan 16, 2023
8b6d3cb
Merge remote-tracking branch 'origin/dev' into spv-optimizations
borngraced Jan 16, 2023
d99c965
refactor spv_conf and added SPVVerificationHeader
borngraced Jan 18, 2023
a9d7445
validate_btc_starting_header_bits
borngraced Jan 18, 2023
6a9f92f
minor changes to SPVVerificationHeader
borngraced Jan 18, 2023
fb67253
Merge remote-tracking branch 'origin/dev' into spv-optimizations
borngraced Jan 18, 2023
c84db96
fix conflicts
borngraced Jan 18, 2023
c4af5a4
fix test_btc_block_header_sync test
borngraced Jan 19, 2023
3ff2893
replace BlockHeaderHashError
borngraced Jan 19, 2023
876611f
some changes to SPVVerificationHeader
borngraced Jan 20, 2023
22ab29b
fix wasm
borngraced Jan 20, 2023
2b24cf6
fix test_enable_coins_with_hd_account_id
borngraced Jan 20, 2023
c7847b1
rename remove_block_headers_from_storage to remove_headers_to_height
borngraced Jan 20, 2023
fe01f20
fix review notes on utxo_arc_builder
borngraced Jan 21, 2023
3048280
fix review notes on spv conf
borngraced Jan 22, 2023
94c2a30
impl get_verification_header
borngraced Jan 23, 2023
388876c
refactor block_header_status_channel
borngraced Jan 23, 2023
44224ff
fix review notes and refactoring
borngraced Jan 23, 2023
d380e5c
fix docs
borngraced Jan 24, 2023
b024b2e
removed retarget header from mainnet_next_block_bits params
borngraced Jan 24, 2023
649bfe1
test_spv_conf_with_verification unit test
borngraced Jan 24, 2023
1e9b0e1
impl validate starting block header from rpc!
borngraced Jan 24, 2023
f1e3e72
fix review notes
borngraced Jan 26, 2023
3e346eb
use correct bit for genesis block - btc testnet
borngraced Jan 26, 2023
f957ddc
refactor remove_headers_up_to_height_sql
borngraced Jan 26, 2023
e4d05df
fix review notes.
borngraced Jan 27, 2023
7239b0e
fix reveiw notes
borngraced Jan 28, 2023
3b41a0a
fix minor review notes
borngraced Jan 31, 2023
82d1bcb
use map_to_mm for error handling
borngraced Feb 1, 2023
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
4 changes: 2 additions & 2 deletions mm2src/coins/lightning/ln_platform.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ impl Platform {
},
});

let is_spv_enabled = self.coin.as_ref().conf.enable_spv_proof;
let is_spv_enabled = self.coin.as_ref().conf.spv_conf.is_some();
let confirmed_transactions_futs = on_chain_txs
.map(|transaction| async move {
if is_spv_enabled {
Expand Down Expand Up @@ -419,7 +419,7 @@ impl Platform {
.any(|info| info.tx.hash() == output.spending_tx.hash())
});

let is_spv_enabled = self.coin.as_ref().conf.enable_spv_proof;
let is_spv_enabled = self.coin.as_ref().conf.spv_conf.is_some();
let confirmed_transactions_futs = spent_outputs_info
.into_iter()
.map(|output| async move {
Expand Down
10 changes: 4 additions & 6 deletions mm2src/coins/utxo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ 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, Error as SerError, SERIALIZE_TRANSACTION_WITNESS};
use spv_validation::helpers_validation::{BlockHeaderVerificationParams, SPVError};
use spv_validation::conf::SPVConf;
use spv_validation::helpers_validation::SPVError;
use spv_validation::storage::BlockHeaderStorageError;
use std::array::TryFromSliceError;
use std::collections::{HashMap, HashSet};
Expand Down Expand Up @@ -552,11 +553,8 @@ pub struct UtxoCoinConf {
/// The name of the coin with which Trezor wallet associates this asset.
pub trezor_coin: Option<String>,
/// Whether to verify swaps and lightning transactions using spv or not. When enabled, block headers will be retrieved, verified according
/// to block_headers_verification_params and stored in the DB. Can be false if the coin's RPC server is trusted.
pub enable_spv_proof: bool,
/// The parameters that specify how the coin block headers should be verified. If None and enable_spv_proof is true,
/// headers will be saved in DB without verification, can be none if the coin's RPC server is trusted.
pub block_headers_verification_params: Option<BlockHeaderVerificationParams>,
/// to [`SPVConf::validation_params`] and stored in the DB. Can be false if the coin's RPC server is trusted.
pub spv_conf: Option<SPVConf>,
shamardy marked this conversation as resolved.
Show resolved Hide resolved
/// Derivation path of the coin.
/// This derivation path consists of `purpose` and `coin_type` only
/// where the full `BIP44` address has the following structure:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ impl BlockHeaderStorageOps for IndexedDBBlockHeadersStorage {

async fn get_block_header_raw(&self, _height: u64) -> Result<Option<String>, BlockHeaderStorageError> { Ok(None) }

async fn get_last_block_height(&self) -> Result<u64, BlockHeaderStorageError> {
async fn get_last_block_height(&self) -> Result<Option<u64>, BlockHeaderStorageError> {
Err(BlockHeaderStorageError::Internal("Not implemented".into()))
}

async fn get_last_block_header_with_non_max_bits(&self) -> Result<Option<BlockHeader>, BlockHeaderStorageError> {
async fn get_last_block_header_with_non_max_bits(
&self,
_max_bits: u32,
) -> Result<Option<BlockHeader>, BlockHeaderStorageError> {
Ok(None)
}

async fn get_block_height_by_hash(&self, _hash: H256) -> Result<Option<i64>, BlockHeaderStorageError> { Ok(None) }

async fn remove_headers_to_height(&self, _height: u64) -> Result<(), BlockHeaderStorageError> { Ok(()) }
}
13 changes: 10 additions & 3 deletions mm2src/coins/utxo/utxo_block_header_storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,22 @@ impl BlockHeaderStorageOps for BlockHeaderStorage {
self.inner.get_block_header_raw(height).await
}

async fn get_last_block_height(&self) -> Result<u64, BlockHeaderStorageError> {
async fn get_last_block_height(&self) -> Result<Option<u64>, BlockHeaderStorageError> {
self.inner.get_last_block_height().await
}

async fn get_last_block_header_with_non_max_bits(&self) -> Result<Option<BlockHeader>, BlockHeaderStorageError> {
self.inner.get_last_block_header_with_non_max_bits().await
async fn get_last_block_header_with_non_max_bits(
&self,
max_bits: u32,
) -> Result<Option<BlockHeader>, BlockHeaderStorageError> {
self.inner.get_last_block_header_with_non_max_bits(max_bits).await
}

async fn get_block_height_by_hash(&self, hash: H256) -> Result<Option<i64>, BlockHeaderStorageError> {
self.inner.get_block_height_by_hash(hash).await
}

async fn remove_headers_to_height(&self, height: u64) -> Result<(), BlockHeaderStorageError> {
self.inner.remove_headers_to_height(height).await
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use db_common::{sqlite::rusqlite::Error as SqlError,
use primitives::hash::H256;
use serialization::Reader;
use spv_validation::storage::{BlockHeaderStorageError, BlockHeaderStorageOps};
use spv_validation::work::MAX_BITS_BTC;
use std::collections::HashMap;
use std::convert::TryInto;
use std::num::TryFromIntError;
Expand Down Expand Up @@ -68,11 +67,14 @@ fn get_last_block_height_sql(for_coin: &str) -> Result<String, BlockHeaderStorag
Ok(sql)
}

fn get_last_block_header_with_non_max_bits_sql(for_coin: &str) -> Result<String, BlockHeaderStorageError> {
fn get_last_block_header_with_non_max_bits_sql(
for_coin: &str,
max_bits: u32,
) -> Result<String, BlockHeaderStorageError> {
let table_name = get_table_name_and_validate(for_coin)?;
let sql = format!(
"SELECT hex FROM {} WHERE block_bits<>{} ORDER BY block_height DESC LIMIT 1;",
table_name, MAX_BITS_BTC
table_name, max_bits
);

Ok(sql)
Expand All @@ -85,6 +87,13 @@ fn get_block_height_by_hash(for_coin: &str) -> Result<String, BlockHeaderStorage
Ok(sql)
}

fn remove_headers_to_height_sql(for_coin: &str, height: u64) -> Result<String, BlockHeaderStorageError> {
let table_name = get_table_name_and_validate(for_coin)?;
let sql = format!("DELETE FROM {table_name} WHERE block_height <= {height};");

Ok(sql)
}

#[derive(Clone, Debug)]
pub struct SqliteBlockHeadersStorage {
pub ticker: String,
Expand Down Expand Up @@ -225,31 +234,34 @@ impl BlockHeaderStorageOps for SqliteBlockHeadersStorage {
})
}

async fn get_last_block_height(&self) -> Result<u64, BlockHeaderStorageError> {
async fn get_last_block_height(&self) -> Result<Option<u64>, BlockHeaderStorageError> {
let coin = self.ticker.clone();
let sql = get_last_block_height_sql(&coin)?;
let selfi = self.clone();

async_blocking(move || {
let conn = selfi.conn.lock().unwrap();
query_single_row(&conn, &sql, NO_PARAMS, |row| row.get(0))
query_single_row(&conn, &sql, NO_PARAMS, |row| row.get::<_, i64>(0))
})
.await
.map_err(|e| BlockHeaderStorageError::GetFromStorageError {
coin: coin.clone(),
reason: e.to_string(),
})?
.unwrap_or(0i64)
.try_into()
.map(|h| h.try_into())
.transpose()
.map_err(|e: TryFromIntError| BlockHeaderStorageError::DecodeError {
coin,
reason: e.to_string(),
}) // last_block_height is 0 if the database is empty
})
}

async fn get_last_block_header_with_non_max_bits(&self) -> Result<Option<BlockHeader>, BlockHeaderStorageError> {
async fn get_last_block_header_with_non_max_bits(
&self,
max_bits: u32,
) -> Result<Option<BlockHeader>, BlockHeaderStorageError> {
let coin = self.ticker.clone();
let sql = get_last_block_header_with_non_max_bits_sql(&coin)?;
let sql = get_last_block_header_with_non_max_bits_sql(&coin, max_bits)?;
let selfi = self.clone();

let maybe_header_raw = async_blocking(move || {
Expand All @@ -263,13 +275,12 @@ impl BlockHeaderStorageOps for SqliteBlockHeadersStorage {
})?;

if let Some(header_raw) = maybe_header_raw {
let header: BlockHeader =
header_raw
.try_into()
.map_err(|e: serialization::Error| BlockHeaderStorageError::DecodeError {
coin,
reason: e.to_string(),
})?;
let header = BlockHeader::try_from_string_with_coin_variant(header_raw, coin.as_str().into()).map_err(
|e: serialization::Error| BlockHeaderStorageError::DecodeError {
coin,
reason: e.to_string(),
},
)?;
return Ok(Some(header));
}
Ok(None)
Expand All @@ -291,6 +302,24 @@ impl BlockHeaderStorageOps for SqliteBlockHeadersStorage {
reason: e.to_string(),
})
}

async fn remove_headers_to_height(&self, height: u64) -> Result<(), BlockHeaderStorageError> {
let coin = self.ticker.clone();
let selfi = self.clone();
let sql = remove_headers_to_height_sql(&coin, height)?;

async_blocking(move || {
let conn = selfi.conn.lock().unwrap();
conn.execute(&sql, NO_PARAMS)
.map_err(|e| BlockHeaderStorageError::UnableToDeleteHeaders {
coin: coin.clone(),
height,
reason: e.to_string(),
})?;
Ok(())
})
.await
}
}

#[cfg(test)]
Expand All @@ -317,6 +346,7 @@ mod sql_block_headers_storage_tests {
use chain::BlockHeaderBits;
use common::block_on;
use primitives::hash::H256;
use spv_validation::work::MAX_BITS_BTC;

#[test]
fn test_init_collection() {
Expand Down Expand Up @@ -407,7 +437,7 @@ mod sql_block_headers_storage_tests {
block_on(storage.add_block_headers_to_storage(headers)).unwrap();
assert!(!storage.is_table_empty(&table));

let actual_block_header = block_on(storage.get_last_block_header_with_non_max_bits())
let actual_block_header = block_on(storage.get_last_block_header_with_non_max_bits(MAX_BITS_BTC))
.unwrap()
.unwrap();
assert_ne!(actual_block_header.bits, BlockHeaderBits::Compact(MAX_BITS_BTC.into()));
Expand Down Expand Up @@ -442,6 +472,47 @@ mod sql_block_headers_storage_tests {
assert!(!storage.is_table_empty(&table));

let last_block_height = block_on(storage.get_last_block_height()).unwrap();
assert_eq!(last_block_height, 201595);
assert_eq!(last_block_height.unwrap(), 201595);
}

#[test]
fn test_remove_headers_to_height() {
let for_coin = "get";
let storage = SqliteBlockHeadersStorage::in_memory(for_coin.into());
let table = block_headers_cache_table(for_coin);
block_on(storage.init()).unwrap();

let initialized = block_on(storage.is_initialized_for()).unwrap();
assert!(initialized);

let mut headers = HashMap::with_capacity(2);
shamardy marked this conversation as resolved.
Show resolved Hide resolved

// https://live.blockcypher.com/btc-testnet/block/00000000961a9d117feb57e516e17217207a849bf6cdfce529f31d9a96053530/
let block_header: BlockHeader = "02000000ea01a61a2d7420a1b23875e40eb5eb4ca18b378902c8e6384514ad0000000000c0c5a1ae80582b3fe319d8543307fa67befc2a734b8eddb84b1780dfdf11fa2b20e71353ffff001d00805fe0".into();
headers.insert(201595, block_header);

// https://live.blockcypher.com/btc-testnet/block/0000000000ad144538e6c80289378ba14cebb50ee47538b2a120742d1aa601ea/
let block_header: BlockHeader = "02000000cbed7fd98f1f06e85c47e13ff956533642056be45e7e6b532d4d768f00000000f2680982f333fcc9afa7f9a5e2a84dc54b7fe10605cd187362980b3aa882e9683be21353ab80011c813e1fc0".into();
headers.insert(201594, block_header);

// https://live.blockcypher.com/btc-testnet/block/0000000000ad144538e6c80289378ba14cebb50ee47538b2a120742d1aa601ea/
let block_header: BlockHeader = "020000001f38c8e30b30af912fbd4c3e781506713cfb43e73dff6250348e060000000000afa8f3eede276ccb4c4ee649ad9823fc181632f262848ca330733e7e7e541beb9be51353ffff001d00a63037".into();
headers.insert(201593, block_header);

block_on(storage.add_block_headers_to_storage(headers)).unwrap();
assert!(!storage.is_table_empty(&table));

// Remove 2 headers from storage.
block_on(storage.remove_headers_to_height(201594)).unwrap();

// Validate that blockers 201593..201594 are removed from storage.
for h in 201593..201594 {
let block_header = block_on(storage.get_block_header(h)).unwrap();
assert!(block_header.is_none());
}

// Last height should be 201595
let last_block_height = block_on(storage.get_last_block_height()).unwrap();
assert_eq!(last_block_height.unwrap(), 201595);
}
}
Loading