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

fix: invalidate cache when main chain scripts are different than in the previous query #335

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This changelog is based on [Keep A Changelog](https://keepachangelog.com/en/1.1.

## Fixed

* Cache returning invalid results when native token MainChainScripts has changed.
* Crash of parnter-chain-node smart-contracts command. Logging is now set independently.
* Renamed of argument 'ogmios-host' to 'ogmios-url' in smart-contracts subcommands.

Expand Down
59 changes: 30 additions & 29 deletions toolkit/mainchain-follower/db-sync-follower/src/native_token/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{DataSourceError, Result};
use derive_new::new;
use itertools::Itertools;
use sidechain_domain::*;
use sp_native_token_management::NativeTokenManagementDataSource;
use sp_native_token_management::{MainChainScripts, NativeTokenManagementDataSource};
use sqlx::PgPool;
use std::sync::{Arc, Mutex};

Expand All @@ -30,14 +30,12 @@ impl NativeTokenManagementDataSource for NativeTokenManagementDataSourceImpl {
&self,
after_block: Option<McBlockHash>,
to_block: McBlockHash,
policy_id: PolicyId,
asset_name: AssetName,
address: MainchainAddress,
scripts: MainChainScripts,
) -> std::result::Result<NativeTokenAmount, Box<dyn std::error::Error + Send + Sync>> {
if let Some(after_block) = after_block {
if after_block == to_block {
Ok(NativeTokenAmount(0))
} else if let Some(amount) = self.get_from_cache(&after_block, &to_block) {
} else if let Some(amount) = self.get_from_cache(&after_block, &to_block, &scripts) {
log::debug!(
"Illiquid supply transfers sum from cache after block '{:?}' to block '{:?}' is {}",
after_block, to_block, amount.0
Expand All @@ -46,7 +44,7 @@ impl NativeTokenManagementDataSource for NativeTokenManagementDataSourceImpl {
} else {
log::debug!("Illiquid supply transfers after block '{:?}' to block '{:?}' not found in cache.", after_block, to_block);
let block_to_amount = self
.get_data_to_cache(&after_block, &to_block, &policy_id, &asset_name, &address)
.get_data_to_cache(&after_block, &to_block, &scripts)
.await?;
log::debug!("Caching illiquid supply transfers from {} blocks", block_to_amount.len());

Expand All @@ -59,13 +57,13 @@ impl NativeTokenManagementDataSource for NativeTokenManagementDataSourceImpl {
log::debug!("Amount of illiquid supply transfers is {}", amount);

if let Ok(mut cache) = self.cache.lock() {
cache.update(block_to_amount)
cache.update(block_to_amount, scripts)
}
Ok(NativeTokenAmount(amount))
}
} else {
let amount = self
.query_transfers_from_genesis(&to_block, &policy_id, &asset_name, &address)
.query_transfers_from_genesis(&to_block, &scripts)
.await?;
log::debug!("Amount of illiquid supply transfers from genesis to {} is {}", to_block, amount.0);
Ok(amount)
Expand Down Expand Up @@ -95,9 +93,14 @@ impl NativeTokenManagementDataSourceImpl {
&self,
after_block: &McBlockHash,
to_block: &McBlockHash,
scripts: &MainChainScripts,
) -> Option<NativeTokenAmount> {
let cache = self.cache.lock().ok()?;
cache.get_sum_in_range(after_block, to_block).map(NativeTokenAmount)
if cache.scripts.as_ref() == Some(scripts) {
cache.get_sum_in_range(after_block, to_block).map(NativeTokenAmount)
} else {
None
}
}

// invariant: to_block is always a stable block
Expand All @@ -106,9 +109,7 @@ impl NativeTokenManagementDataSourceImpl {
&self,
from_block: &McBlockHash,
to_block: &McBlockHash,
policy_id: &PolicyId,
asset_name: &AssetName,
address: &MainchainAddress,
scripts: &MainChainScripts,
) -> Result<Vec<(McBlockHash, u128)>> {
let (from_block_no, to_block_no, latest_block) = futures::try_join!(
get_from_block_no(from_block, &self.pool),
Expand All @@ -124,22 +125,18 @@ impl NativeTokenManagementDataSourceImpl {
std::cmp::max(to_block_no.0, from_block_no.0.saturating_add(self.cache_size.into())),
));
// transfers starts with block having hash equal to after_block or genesis
let transfers = self
.query_db(from_block_no, cache_to_block_no, policy_id, asset_name, address)
.await?;
let transfers = self.query_db(from_block_no, cache_to_block_no, scripts).await?;
Ok(transfers.iter().map(|t| (McBlockHash(t.block_hash), t.amount.0)).collect())
}

async fn query_db(
&self,
from_block: BlockNumber,
to_block: BlockNumber,
native_token_policy_id: &PolicyId,
native_token_asset_name: &AssetName,
illiquid_supply_address: &MainchainAddress,
scripts: &MainChainScripts,
) -> Result<Vec<crate::db_model::BlockTokenAmount>> {
let address = illiquid_supply_address.clone().into();
let asset = to_db_asset(native_token_policy_id, native_token_asset_name);
let address = scripts.illiquid_supply_validator_address.clone().into();
let asset = to_db_asset(scripts);
Ok(crate::db_model::get_native_token_transfers(
&self.pool, from_block, to_block, asset, address,
)
Expand All @@ -149,26 +146,24 @@ impl NativeTokenManagementDataSourceImpl {
async fn query_transfers_from_genesis(
&self,
to_block: &McBlockHash,
policy_id: &PolicyId,
asset_name: &AssetName,
address: &MainchainAddress,
scripts: &MainChainScripts,
) -> Result<NativeTokenAmount> {
let to_block = get_to_block_no(to_block, &self.pool).await?;
Ok(crate::db_model::get_total_native_tokens_transfered(
&self.pool,
to_block,
to_db_asset(policy_id, asset_name),
address.clone().into(),
to_db_asset(scripts),
scripts.illiquid_supply_validator_address.clone().into(),
)
.await?
.into())
}
}

fn to_db_asset(policy_id: &PolicyId, asset_name: &AssetName) -> crate::db_model::Asset {
fn to_db_asset(scripts: &MainChainScripts) -> crate::db_model::Asset {
crate::db_model::Asset {
policy_id: policy_id.clone().into(),
asset_name: asset_name.clone().into(),
policy_id: scripts.native_token_policy_id.clone().into(),
asset_name: scripts.native_token_asset_name.clone().into(),
}
}

Expand Down Expand Up @@ -202,6 +197,7 @@ async fn get_latest_block(pool: &PgPool) -> Result<Block> {
pub(crate) struct Cache {
/// Continous blocks with their respective total native token transfer amount
block_hash_to_amount: Vec<(McBlockHash, u128)>,
pub(crate) scripts: Option<MainChainScripts>,
}

impl Cache {
Expand All @@ -215,7 +211,12 @@ impl Cache {
Some(after_to.iter().map(|(_, amount)| amount).sum())
}

pub fn update(&mut self, block_hash_to_amount: Vec<(McBlockHash, u128)>) {
pub fn update(
&mut self,
block_hash_to_amount: Vec<(McBlockHash, u128)>,
scripts: MainChainScripts,
) {
self.block_hash_to_amount = block_hash_to_amount;
self.scripts = Some(scripts);
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,38 @@
use super::NativeTokenManagementDataSourceImpl;
use sidechain_domain::{AssetName, MainchainAddress, McBlockHash, PolicyId};
use sp_native_token_management::NativeTokenManagementDataSource;
use sp_native_token_management::{MainChainScripts, NativeTokenManagementDataSource};
use sqlx::PgPool;
use std::str::FromStr;

fn native_token_policy_id() -> PolicyId {
PolicyId::from_hex_unsafe("6c969320597b755454ff3653ad09725d590c570827a129aeb4385526")
}
// Transfers in the migrations file are:
// block 1: 11 for scripts 1
// block 3: 12 for scripts 1
// block 5: 13 and 14 for scripts 1, 37 for scripts 2

fn native_token_asset_name() -> AssetName {
AssetName::from_hex_unsafe("546573744275647a507265766965775f3335")
fn scripts() -> MainChainScripts {
MainChainScripts {
native_token_policy_id: PolicyId::from_hex_unsafe(
"6c969320597b755454ff3653ad09725d590c570827a129aeb4385526",
),
native_token_asset_name: AssetName::from_hex_unsafe("546573744275647a507265766965775f3335"),
illiquid_supply_validator_address: MainchainAddress::from_str(
"addr_test1wrhvtvx3f0g9wv9rx8kfqc60jva3e07nqujk2cspekv4mqs9rjdvz",
)
.unwrap(),
}
}

fn illiquid_supply_address() -> MainchainAddress {
MainchainAddress::from_str("addr_test1wrhvtvx3f0g9wv9rx8kfqc60jva3e07nqujk2cspekv4mqs9rjdvz")
.unwrap()
fn scripts2() -> MainChainScripts {
MainChainScripts {
native_token_policy_id: PolicyId::from_hex_unsafe(
"aaaabbaa597b755454ff3653ad09725d590c570827a129aeb438ffff",
),
native_token_asset_name: AssetName::from_hex_unsafe("656565"),
illiquid_supply_validator_address: MainchainAddress::from_str(
"addr_test1aaaabbaaf0g9wv9rx8kfqc60jva3e07nqujk2cspekv4mqs9ffff",
)
.unwrap(),
}
}

fn block_hash(i: u32) -> McBlockHash {
Expand Down Expand Up @@ -69,15 +87,27 @@ async fn query_for_each_blocks_pair(pool: PgPool) {
assert_eq!(vec![r1, r2, r3, r4, r5], vec![11, 0, 12, 0, 13 + 14])
}

#[sqlx::test(migrations = "./testdata/native-token/migrations")]
async fn change_of_scripts_invalidates_cache(pool: PgPool) {
let source = make_source(pool.clone());
let scripts1_transfers = run_for_scripts(&source, Some(1), 3, scripts()).await;
assert_eq!(scripts1_transfers, 12);
let scripts2_transfers = run_for_scripts(&source, Some(4), 6, scripts2()).await;
assert_eq!(scripts2_transfers, 37);
}

async fn run(source: &NativeTokenManagementDataSourceImpl, from: Option<u32>, to: u32) -> u128 {
run_for_scripts(source, from, to, scripts()).await
}

async fn run_for_scripts(
source: &NativeTokenManagementDataSourceImpl,
from: Option<u32>,
to: u32,
scripts: MainChainScripts,
) -> u128 {
source
.get_total_native_token_transfer(
from.map(block_hash),
block_hash(to),
native_token_policy_id(),
native_token_asset_name(),
illiquid_supply_address(),
)
.get_total_native_token_transfer(from.map(block_hash), block_hash(to), scripts)
.await
.unwrap()
.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@ do $$
declare
policy_id integer := 1001;
policy hash28type := decode('6c969320597b755454ff3653ad09725d590c570827a129aeb4385526', 'hex');
policy_name asset32type := '\x546573744275647a507265766965775f3335';
asset_name asset32type := '\x546573744275647a507265766965775f3335';
validator_addr text := 'addr_test1wrhvtvx3f0g9wv9rx8kfqc60jva3e07nqujk2cspekv4mqs9rjdvz';

policy2_id integer := 1002;
policy2 hash28type := decode('aaaabbaa597b755454ff3653ad09725d590c570827a129aeb438ffff', 'hex');
asset2_name asset32type := '\x656565';
validator2_addr text := 'addr_test1aaaabbaaf0g9wv9rx8kfqc60jva3e07nqujk2cspekv4mqs9ffff';


genesis_hash hash32type := decode('b000000000000000000000000000000000000000000000000000000000000000','hex');
block_hash_1 hash32type := decode('b000000000000000000000000000000000000000000000000000000000000001','hex');
block_hash_2 hash32type := decode('b000000000000000000000000000000000000000000000000000000000000002','hex');
Expand All @@ -18,27 +24,31 @@ declare
irrelevant_tx_id integer := 3;
transfer_tx_id_3 integer := 4;
transfer_tx_id_4 integer := 5;
token2_transfer_tx_id integer := 6;

transfer_tx_hash_1 hash32type := decode('f000000000000000000000000000000000000000000000000000000000000001','hex');
transfer_tx_hash_2 hash32type := decode('f000000000000000000000000000000000000000000000000000000000000002','hex');
irrelevant_tx_hash hash32type := decode('f000000000000000000000000000000000000000000000000000000000000003','hex');
transfer_tx_hash_3 hash32type := decode('f000000000000000000000000000000000000000000000000000000000000004','hex');
transfer_tx_hash_4 hash32type := decode('f000000000000000000000000000000000000000000000000000000000000005','hex');
token2_transer_tx_hash hash32type := decode('f000000000000000000000000000000000000000000000000000000000000006','hex');

transfer_utxo_id_1 integer := 0;
transfer_utxo_id_2 integer := 1;
irrelevant_utxo_id_1 integer := 2;
irrelevant_utxo_id_2 integer := 3;
transfer_utxo_id_3 integer := 4;
transfer_utxo_id_4 integer := 5;
token2_transfer_utxo_id integer := 6;
begin

insert into multi_asset
(id , policy, "name", fingerprint)
values
(0 , '\xbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad01', '\xbadbadbadbadbadbadbadbadbadbadbad001', 'asset1thisassetshouldbeignoredbythequeries01'),
(policy_id, policy , policy_name , 'asset1yedvsfmkxu27zaaa37lw44pa8ql9favqlyclnm'),
(2 , '\xbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad02', '\xbadbadbadbadbadbadbadbadbadbadbad002', 'asset1thisassetshouldbeignoredbythequeries02')
(id , policy , "name" , fingerprint)
VALUES
(0 , '\xbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad01', '\xbadbadbadbadbadbadbadbadbadbadbad001', 'asset1thisassetshouldbeignoredbythequeries01'),
(policy_id , policy , asset_name , 'asset1yedvsfmkxu27zaaa37lw44pa8ql9favqlyclnm'),
(2 , '\xbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbadbad02', '\xbadbadbadbadbadbadbadbadbadbadbad002', 'asset1thisassetshouldbeignoredbythequeries02'),
(policy2_id, policy2 , asset2_name , 'asset1aaaabbaaxu27zaaa37lw44pa8ql9favqlyffff')
;

-- the integration test assume a securityParameter of 1
Expand All @@ -63,34 +73,37 @@ VALUES


INSERT INTO tx
( id , hash , block_id, block_index, out_sum, fee, deposit, size, invalid_before, invalid_hereafter, valid_contract, script_size )
( id , hash , block_id, block_index, out_sum, fee, deposit, size, invalid_before, invalid_hereafter, valid_contract, script_size )
VALUES
( transfer_tx_id_1 , transfer_tx_hash_1 , 1 , 0 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 ),
( transfer_tx_id_2 , transfer_tx_hash_2 , 3 , 0 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 ),
( irrelevant_tx_id , irrelevant_tx_hash , 3 , 1 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 ),
( transfer_tx_id_3 , transfer_tx_hash_3 , 5 , 0 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 ),
( transfer_tx_id_4 , transfer_tx_hash_4 , 5 , 1 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 )
( transfer_tx_id_1 , transfer_tx_hash_1 , 1 , 0 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 ),
( transfer_tx_id_2 , transfer_tx_hash_2 , 3 , 0 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 ),
( irrelevant_tx_id , irrelevant_tx_hash , 3 , 1 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 ),
( transfer_tx_id_3 , transfer_tx_hash_3 , 5 , 0 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 ),
( transfer_tx_id_4 , transfer_tx_hash_4 , 5 , 1 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 ),
( token2_transfer_tx_id , token2_transer_tx_hash , 5 , 2 , 0 , 0 , 0 , 1024, NULL , NULL , TRUE , 1024 )
;

INSERT INTO tx_out
( id , tx_id , index, address , address_raw, address_has_script, payment_cred, stake_address_id, value, data_hash )
( id , tx_id , index, address , address_raw, address_has_script, payment_cred, stake_address_id, value, data_hash )
VALUES
( transfer_utxo_id_1 , transfer_tx_id_1, 0 , validator_addr , '' , TRUE , NULL , NULL , 0 , NULL ),
( transfer_utxo_id_2 , transfer_tx_id_2, 1 , validator_addr , '' , TRUE , NULL , NULL , 0 , NULL ),
( irrelevant_utxo_id_1 , irrelevant_tx_id, 0 , 'other_addr' , '' , TRUE , NULL , NULL , 0 , NULL ),
( irrelevant_utxo_id_2 , irrelevant_tx_id, 1 , 'another_addr' , '' , TRUE , NULL , NULL , 0 , NULL ),
( transfer_utxo_id_3 , transfer_tx_id_3, 2 , validator_addr , '' , TRUE , NULL , NULL , 0 , NULL ),
( transfer_utxo_id_4 , transfer_tx_id_4, 0 , validator_addr , '' , TRUE , NULL , NULL , 0 , NULL )
( transfer_utxo_id_1 , transfer_tx_id_1 , 0 , validator_addr , '' , TRUE , NULL , NULL , 0 , NULL ),
( transfer_utxo_id_2 , transfer_tx_id_2 , 1 , validator_addr , '' , TRUE , NULL , NULL , 0 , NULL ),
( irrelevant_utxo_id_1 , irrelevant_tx_id , 0 , 'other_addr' , '' , TRUE , NULL , NULL , 0 , NULL ),
( irrelevant_utxo_id_2 , irrelevant_tx_id , 1 , 'another_addr' , '' , TRUE , NULL , NULL , 0 , NULL ),
( transfer_utxo_id_3 , transfer_tx_id_3 , 2 , validator_addr , '' , TRUE , NULL , NULL , 0 , NULL ),
( transfer_utxo_id_4 , transfer_tx_id_4 , 0 , validator_addr , '' , TRUE , NULL , NULL , 0 , NULL ),
( token2_transfer_utxo_id , token2_transfer_tx_id , 0 , validator2_addr , '' , TRUE , NULL , NULL , 0 , NULL )
;

INSERT INTO ma_tx_out
(id , quantity , tx_out_id , ident)
(id , quantity , tx_out_id , ident)
VALUES
(3001 , 11 , transfer_utxo_id_1 , policy_id),
(3002 , 12 , transfer_utxo_id_2 , policy_id),
(3003 , 13 , transfer_utxo_id_3 , policy_id),
(3004 , 14 , transfer_utxo_id_4 , policy_id),
(3005 , 100 , irrelevant_utxo_id_1 , 0)
(3005 , 100 , irrelevant_utxo_id_1 , 0),
(3006 , 37 , token2_transfer_utxo_id , policy2_id)
;

end $$;
6 changes: 2 additions & 4 deletions toolkit/mainchain-follower/mock/src/native_token.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::Result;
use async_trait::async_trait;
use sidechain_domain::*;
use sp_native_token_management::NativeTokenManagementDataSource;
use sp_native_token_management::{MainChainScripts, NativeTokenManagementDataSource};

pub struct NativeTokenDataSourceMock;

Expand All @@ -23,9 +23,7 @@ impl NativeTokenManagementDataSource for NativeTokenDataSourceMock {
&self,
_after_block: Option<McBlockHash>,
_to_block: McBlockHash,
_native_token_policy_id: PolicyId,
_native_token_asset_name: AssetName,
_illiquid_supply_address: MainchainAddress,
_scripts: MainChainScripts,
) -> Result<NativeTokenAmount> {
Ok(NativeTokenAmount(1000))
}
Expand Down
Loading
Loading