Skip to content

Commit

Permalink
fix: invalidate cache when main chain scripts are different than in t…
Browse files Browse the repository at this point in the history
…he previous query
  • Loading branch information
LGLO authored Dec 17, 2024
1 parent f2e90ab commit 0cdac68
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 84 deletions.
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

0 comments on commit 0cdac68

Please sign in to comment.