diff --git a/changelog.md b/changelog.md index a10154ccb..42d1f8ce8 100644 --- a/changelog.md +++ b/changelog.md @@ -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. diff --git a/toolkit/mainchain-follower/db-sync-follower/src/native_token/mod.rs b/toolkit/mainchain-follower/db-sync-follower/src/native_token/mod.rs index 76d7a39be..51e0e3db9 100644 --- a/toolkit/mainchain-follower/db-sync-follower/src/native_token/mod.rs +++ b/toolkit/mainchain-follower/db-sync-follower/src/native_token/mod.rs @@ -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}; @@ -30,14 +30,12 @@ impl NativeTokenManagementDataSource for NativeTokenManagementDataSourceImpl { &self, after_block: Option, to_block: McBlockHash, - policy_id: PolicyId, - asset_name: AssetName, - address: MainchainAddress, + scripts: MainChainScripts, ) -> std::result::Result> { 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 @@ -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()); @@ -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) @@ -95,9 +93,14 @@ impl NativeTokenManagementDataSourceImpl { &self, after_block: &McBlockHash, to_block: &McBlockHash, + scripts: &MainChainScripts, ) -> Option { 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 @@ -106,9 +109,7 @@ impl NativeTokenManagementDataSourceImpl { &self, from_block: &McBlockHash, to_block: &McBlockHash, - policy_id: &PolicyId, - asset_name: &AssetName, - address: &MainchainAddress, + scripts: &MainChainScripts, ) -> Result> { let (from_block_no, to_block_no, latest_block) = futures::try_join!( get_from_block_no(from_block, &self.pool), @@ -124,9 +125,7 @@ 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()) } @@ -134,12 +133,10 @@ impl NativeTokenManagementDataSourceImpl { &self, from_block: BlockNumber, to_block: BlockNumber, - native_token_policy_id: &PolicyId, - native_token_asset_name: &AssetName, - illiquid_supply_address: &MainchainAddress, + scripts: &MainChainScripts, ) -> Result> { - 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, ) @@ -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 { 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(), } } @@ -202,6 +197,7 @@ async fn get_latest_block(pool: &PgPool) -> Result { 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, } impl Cache { @@ -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); } } diff --git a/toolkit/mainchain-follower/db-sync-follower/src/native_token/tests.rs b/toolkit/mainchain-follower/db-sync-follower/src/native_token/tests.rs index aa658d93c..086fb15c5 100644 --- a/toolkit/mainchain-follower/db-sync-follower/src/native_token/tests.rs +++ b/toolkit/mainchain-follower/db-sync-follower/src/native_token/tests.rs @@ -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 { @@ -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, to: u32) -> u128 { + run_for_scripts(source, from, to, scripts()).await +} + +async fn run_for_scripts( + source: &NativeTokenManagementDataSourceImpl, + from: Option, + 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 diff --git a/toolkit/mainchain-follower/db-sync-follower/testdata/native-token/migrations/2_data.sql b/toolkit/mainchain-follower/db-sync-follower/testdata/native-token/migrations/2_data.sql index c91e10b6e..41d2bd72a 100644 --- a/toolkit/mainchain-follower/db-sync-follower/testdata/native-token/migrations/2_data.sql +++ b/toolkit/mainchain-follower/db-sync-follower/testdata/native-token/migrations/2_data.sql @@ -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'); @@ -18,12 +24,14 @@ 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; @@ -31,14 +39,16 @@ declare 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 @@ -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 $$; diff --git a/toolkit/mainchain-follower/mock/src/native_token.rs b/toolkit/mainchain-follower/mock/src/native_token.rs index cd9c06a89..c1469f56e 100644 --- a/toolkit/mainchain-follower/mock/src/native_token.rs +++ b/toolkit/mainchain-follower/mock/src/native_token.rs @@ -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; @@ -23,9 +23,7 @@ impl NativeTokenManagementDataSource for NativeTokenDataSourceMock { &self, _after_block: Option, _to_block: McBlockHash, - _native_token_policy_id: PolicyId, - _native_token_asset_name: AssetName, - _illiquid_supply_address: MainchainAddress, + _scripts: MainChainScripts, ) -> Result { Ok(NativeTokenAmount(1000)) } diff --git a/toolkit/primitives/native-token-management/src/lib.rs b/toolkit/primitives/native-token-management/src/lib.rs index de6188edc..1599ab4cb 100644 --- a/toolkit/primitives/native-token-management/src/lib.rs +++ b/toolkit/primitives/native-token-management/src/lib.rs @@ -105,9 +105,7 @@ mod inherent_provider { &self, after_block: Option, to_block: McBlockHash, - native_token_policy_id: PolicyId, - native_token_asset_name: AssetName, - illiquid_supply_address: MainchainAddress, + scripts: MainChainScripts, ) -> Result>; } @@ -178,13 +176,7 @@ mod inherent_provider { None }; let token_amount = data_source - .get_total_native_token_transfer( - parent_mc_hash, - mc_hash, - scripts.native_token_policy_id, - scripts.native_token_asset_name, - scripts.illiquid_supply_validator_address, - ) + .get_total_native_token_transfer(parent_mc_hash, mc_hash, scripts) .await .map_err(IDPCreationError::DataSourceError)?; @@ -224,7 +216,7 @@ mod inherent_provider { #[cfg(any(test, feature = "mock"))] pub mod mock { - use crate::NativeTokenManagementDataSource; + use crate::{MainChainScripts, NativeTokenManagementDataSource}; use async_trait::async_trait; use derive_new::new; use sidechain_domain::*; @@ -241,9 +233,7 @@ mod inherent_provider { &self, after_block: Option, to_block: McBlockHash, - _native_token_policy_id: PolicyId, - _native_token_asset_name: AssetName, - _illiquid_supply_address: MainchainAddress, + _scripts: MainChainScripts, ) -> Result> { Ok(self.transfers.get(&(after_block, to_block)).cloned().unwrap_or_default()) }