Skip to content

Commit 7a13e67

Browse files
authored
Merge pull request #1834 from mintlayer/fix/api-server-owning-block-id
Fix missing owning block for transaction in api server
2 parents afc10c0 + 6bf3a2b commit 7a13e67

File tree

13 files changed

+197
-475
lines changed

13 files changed

+197
-475
lines changed

api-server/api-server-common/src/storage/impls/in_memory/mod.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ struct ApiServerInMemoryStorage {
4949
delegation_table: BTreeMap<DelegationId, BTreeMap<BlockHeight, Delegation>>,
5050
main_chain_blocks_table: BTreeMap<BlockHeight, Id<Block>>,
5151
pool_data_table: BTreeMap<PoolId, BTreeMap<BlockHeight, PoolData>>,
52-
transaction_table: BTreeMap<Id<Transaction>, (Option<Id<Block>>, TransactionInfo)>,
52+
transaction_table: BTreeMap<Id<Transaction>, (Id<Block>, TransactionInfo)>,
5353
utxo_table: BTreeMap<UtxoOutPoint, BTreeMap<BlockHeight, Utxo>>,
5454
address_utxos: BTreeMap<String, BTreeSet<UtxoOutPoint>>,
5555
locked_utxo_table: BTreeMap<UtxoOutPoint, BTreeMap<BlockHeight, LockedUtxo>>,
@@ -201,7 +201,7 @@ impl ApiServerInMemoryStorage {
201201
};
202202

203203
Ok(Some((
204-
block_id.and_then(|block_id| self.block_aux_data_table.get(&block_id)).cloned(),
204+
self.block_aux_data_table.get(block_id).cloned(),
205205
tx.clone(),
206206
)))
207207
}
@@ -210,7 +210,7 @@ impl ApiServerInMemoryStorage {
210210
fn get_transaction(
211211
&self,
212212
transaction_id: Id<Transaction>,
213-
) -> Result<Option<(Option<Id<Block>>, TransactionInfo)>, ApiServerStorageError> {
213+
) -> Result<Option<(Id<Block>, TransactionInfo)>, ApiServerStorageError> {
214214
let transaction_result = self.transaction_table.get(&transaction_id);
215215
let tx = match transaction_result {
216216
Some(tx) => tx,
@@ -824,9 +824,16 @@ impl ApiServerInMemoryStorage {
824824
fn set_transaction(
825825
&mut self,
826826
transaction_id: Id<Transaction>,
827-
owning_block: Option<Id<Block>>,
827+
owning_block: Id<Block>,
828828
transaction: &TransactionInfo,
829829
) -> Result<(), ApiServerStorageError> {
830+
// Emulate the behavior of real db where foreign key must be present
831+
if !self.block_table.contains_key(&owning_block) {
832+
return Err(ApiServerStorageError::LowLevelStorageError(
833+
"Owning block must exist in block table".to_string(),
834+
));
835+
}
836+
830837
self.transaction_table
831838
.insert(transaction_id, (owning_block, transaction.clone()));
832839
Ok(())

api-server/api-server-common/src/storage/impls/in_memory/transactional/read.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ impl<'t> ApiServerStorageRead for ApiServerInMemoryStorageTransactionalRo<'t> {
132132
async fn get_transaction(
133133
&self,
134134
transaction_id: Id<Transaction>,
135-
) -> Result<Option<(Option<Id<Block>>, TransactionInfo)>, ApiServerStorageError> {
135+
) -> Result<Option<(Id<Block>, TransactionInfo)>, ApiServerStorageError> {
136136
self.transaction.get_transaction(transaction_id)
137137
}
138138

api-server/api-server-common/src/storage/impls/in_memory/transactional/write.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ impl<'t> ApiServerStorageWrite for ApiServerInMemoryStorageTransactionalRw<'t> {
126126
async fn set_transaction(
127127
&mut self,
128128
transaction_id: Id<Transaction>,
129-
owning_block: Option<Id<Block>>,
129+
owning_block: Id<Block>,
130130
transaction: &TransactionInfo,
131131
) -> Result<(), ApiServerStorageError> {
132132
self.transaction.set_transaction(transaction_id, owning_block, transaction)
@@ -406,7 +406,7 @@ impl<'t> ApiServerStorageRead for ApiServerInMemoryStorageTransactionalRw<'t> {
406406
async fn get_transaction(
407407
&self,
408408
transaction_id: Id<Transaction>,
409-
) -> Result<Option<(Option<Id<Block>>, TransactionInfo)>, ApiServerStorageError> {
409+
) -> Result<Option<(Id<Block>, TransactionInfo)>, ApiServerStorageError> {
410410
self.transaction.get_transaction(transaction_id)
411411
}
412412

api-server/api-server-common/src/storage/impls/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16-
pub const CURRENT_STORAGE_VERSION: u32 = 16;
16+
pub const CURRENT_STORAGE_VERSION: u32 = 17;
1717

1818
pub mod in_memory;
1919
pub mod postgres;

api-server/api-server-common/src/storage/impls/postgres/queries.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ impl<'a, 'b> QueryFromConnection<'a, 'b> {
506506
self.just_execute(
507507
"CREATE TABLE ml.transactions (
508508
transaction_id bytea PRIMARY KEY,
509-
owning_block_id bytea REFERENCES ml.blocks(block_id),
509+
owning_block_id bytea NOT NULL REFERENCES ml.blocks(block_id),
510510
transaction_data bytea NOT NULL
511511
);", // block_id can be null if the transaction is not in the main chain
512512
)
@@ -1342,7 +1342,7 @@ impl<'a, 'b> QueryFromConnection<'a, 'b> {
13421342
pub async fn get_transaction(
13431343
&mut self,
13441344
transaction_id: Id<Transaction>,
1345-
) -> Result<Option<(Option<Id<Block>>, TransactionInfo)>, ApiServerStorageError> {
1345+
) -> Result<Option<(Id<Block>, TransactionInfo)>, ApiServerStorageError> {
13461346
let row = self
13471347
.tx
13481348
.query_opt(
@@ -1360,12 +1360,11 @@ impl<'a, 'b> QueryFromConnection<'a, 'b> {
13601360
None => return Ok(None),
13611361
};
13621362

1363-
let block_id_data: Option<Vec<u8>> = data.get(0);
1363+
let block_id_data: Vec<u8> = data.get(0);
13641364
let transaction_data: Vec<u8> = data.get(1);
13651365

13661366
let block_id = {
1367-
let deserialized_block_id =
1368-
block_id_data.map(|d| Id::<Block>::decode_all(&mut d.as_slice())).transpose();
1367+
let deserialized_block_id = Id::<Block>::decode_all(&mut block_id_data.as_slice());
13691368
deserialized_block_id.map_err(|e| {
13701369
ApiServerStorageError::DeserializationError(format!(
13711370
"Block deserialization failed: {}",
@@ -1497,7 +1496,7 @@ impl<'a, 'b> QueryFromConnection<'a, 'b> {
14971496
pub async fn set_transaction(
14981497
&mut self,
14991498
transaction_id: Id<Transaction>,
1500-
owning_block: Option<Id<Block>>,
1499+
owning_block: Id<Block>,
15011500
transaction: &TransactionInfo,
15021501
) -> Result<(), ApiServerStorageError> {
15031502
logging::log::debug!(
@@ -1509,7 +1508,7 @@ impl<'a, 'b> QueryFromConnection<'a, 'b> {
15091508
self.tx.execute(
15101509
"INSERT INTO ml.transactions (transaction_id, owning_block_id, transaction_data) VALUES ($1, $2, $3)
15111510
ON CONFLICT (transaction_id) DO UPDATE
1512-
SET owning_block_id = $2, transaction_data = $3;", &[&transaction_id.encode(), &owning_block.map(|v|v.encode()), &transaction.encode()]
1511+
SET owning_block_id = $2, transaction_data = $3;", &[&transaction_id.encode(), &owning_block.encode(), &transaction.encode()]
15131512
).await
15141513
.map_err(|e| ApiServerStorageError::LowLevelStorageError(e.to_string()))?;
15151514

api-server/api-server-common/src/storage/impls/postgres/transactional/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ impl<'a> ApiServerPostgresTransactionalRo<'a> {
129129
pub async fn get_transaction(
130130
&mut self,
131131
transaction_id: Id<Transaction>,
132-
) -> Result<Option<(Option<Id<Block>>, TransactionInfo)>, ApiServerStorageError> {
132+
) -> Result<Option<(Id<Block>, TransactionInfo)>, ApiServerStorageError> {
133133
let mut conn = QueryFromConnection::new(self.connection.as_ref().expect(CONN_ERR));
134134
let res = conn.get_transaction(transaction_id).await?;
135135

api-server/api-server-common/src/storage/impls/postgres/transactional/read.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -233,8 +233,7 @@ impl<'a> ApiServerStorageRead for ApiServerPostgresTransactionalRo<'a> {
233233
async fn get_transaction(
234234
&self,
235235
transaction_id: Id<common::chain::Transaction>,
236-
) -> Result<Option<(Option<Id<common::chain::Block>>, TransactionInfo)>, ApiServerStorageError>
237-
{
236+
) -> Result<Option<(Id<common::chain::Block>, TransactionInfo)>, ApiServerStorageError> {
238237
let mut conn = QueryFromConnection::new(self.connection.as_ref().expect(CONN_ERR));
239238
let res = conn.get_transaction(transaction_id).await?;
240239

api-server/api-server-common/src/storage/impls/postgres/transactional/write.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ impl<'a> ApiServerStorageWrite for ApiServerPostgresTransactionalRw<'a> {
148148
async fn set_transaction(
149149
&mut self,
150150
transaction_id: Id<Transaction>,
151-
owning_block: Option<Id<Block>>,
151+
owning_block: Id<Block>,
152152
transaction: &TransactionInfo,
153153
) -> Result<(), ApiServerStorageError> {
154154
let mut conn = QueryFromConnection::new(self.connection.as_ref().expect(CONN_ERR));
@@ -541,7 +541,7 @@ impl<'a> ApiServerStorageRead for ApiServerPostgresTransactionalRw<'a> {
541541
async fn get_transaction(
542542
&self,
543543
transaction_id: Id<Transaction>,
544-
) -> Result<Option<(Option<Id<Block>>, TransactionInfo)>, ApiServerStorageError> {
544+
) -> Result<Option<(Id<Block>, TransactionInfo)>, ApiServerStorageError> {
545545
let mut conn = QueryFromConnection::new(self.connection.as_ref().expect(CONN_ERR));
546546
let res = conn.get_transaction(transaction_id).await?;
547547

api-server/api-server-common/src/storage/storage_api/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use self::block_aux_data::{BlockAuxData, BlockWithExtraData};
4040
pub mod block_aux_data;
4141

4242
#[allow(dead_code)]
43-
#[derive(Debug, thiserror::Error)]
43+
#[derive(Debug, Eq, PartialEq, thiserror::Error)]
4444
pub enum ApiServerStorageError {
4545
#[error("Low level storage error: {0}")]
4646
LowLevelStorageError(String),
@@ -530,7 +530,7 @@ pub trait ApiServerStorageRead: Sync {
530530
async fn get_transaction(
531531
&self,
532532
transaction_id: Id<Transaction>,
533-
) -> Result<Option<(Option<Id<Block>>, TransactionInfo)>, ApiServerStorageError>;
533+
) -> Result<Option<(Id<Block>, TransactionInfo)>, ApiServerStorageError>;
534534

535535
async fn get_transactions_with_block(
536536
&self,
@@ -666,7 +666,7 @@ pub trait ApiServerStorageWrite: ApiServerStorageRead {
666666
async fn set_transaction(
667667
&mut self,
668668
transaction_id: Id<Transaction>,
669-
owning_block: Option<Id<Block>>,
669+
owning_block: Id<Block>,
670670
transaction: &TransactionInfo,
671671
) -> Result<(), ApiServerStorageError>;
672672

api-server/scanner-lib/src/blockchain_state/mod.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,19 @@ impl<S: ApiServerStorage + Send + Sync> LocalBlockchainState for BlockchainState
137137
logging::log::info!("Connected block: ({}, {:x})", block_height, block.get_id());
138138

139139
let mut total_fees = AccumulatedFee::new();
140+
let mut transactions = Vec::with_capacity(block.transactions().len());
140141
let mut tx_additional_infos = Vec::with_capacity(block.transactions().len());
141142

143+
// The order in which data is processed and flushed to the storage is very specific.
144+
//
145+
// First, the tx fee is calculated and the tables dependant on tx info are updated. This is done
146+
// per tx because calculating fee requires up to date utxo and orders info.
147+
//
148+
// Second, total fee is accumulated and block is flushed to the db.
149+
//
150+
// Third, txs are flushed to the db AFTER the block.
151+
// This is done because transaction table has FOREIGN key `owning_block_id` referring block table.
152+
142153
for tx in block.transactions().iter() {
143154
let (tx_fee, tx_additional_info) = calculate_tx_fee_and_collect_token_info(
144155
&self.chain_config,
@@ -165,10 +176,7 @@ impl<S: ApiServerStorage + Send + Sync> LocalBlockchainState for BlockchainState
165176
tx: tx.clone(),
166177
additional_info: tx_additional_info,
167178
};
168-
db_tx
169-
.set_transaction(tx.transaction().get_id(), Some(block.get_id()), &tx_info)
170-
.await
171-
.expect("Unable to set transaction");
179+
transactions.push(tx_info);
172180
}
173181

174182
let block_id = block.get_id();
@@ -181,6 +189,13 @@ impl<S: ApiServerStorage + Send + Sync> LocalBlockchainState for BlockchainState
181189
.await
182190
.expect("Unable to set block");
183191

192+
for tx_info in transactions {
193+
db_tx
194+
.set_transaction(tx_info.tx.transaction().get_id(), block_id, &tx_info)
195+
.await
196+
.expect("Unable to set transaction");
197+
}
198+
184199
db_tx
185200
.set_block_aux_data(
186201
block_id,

0 commit comments

Comments
 (0)