mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Restore block_hash to Batch
It's not only helpful (to easily check where Serai's view of the external network is) but it's necessary in case of a non-trivial chain fork to determine which blockchain Serai considers canonical.
This commit is contained in:
@@ -160,6 +160,7 @@ impl<D: Db> ContinuallyRan for CanonicalEventStream<D> {
|
|||||||
network: batch_network,
|
network: batch_network,
|
||||||
publishing_session,
|
publishing_session,
|
||||||
id,
|
id,
|
||||||
|
external_network_block_hash,
|
||||||
in_instructions_hash,
|
in_instructions_hash,
|
||||||
in_instruction_results,
|
in_instruction_results,
|
||||||
} = this_batch
|
} = this_batch
|
||||||
@@ -173,6 +174,7 @@ impl<D: Db> ContinuallyRan for CanonicalEventStream<D> {
|
|||||||
batch = Some(ExecutedBatch {
|
batch = Some(ExecutedBatch {
|
||||||
id: *id,
|
id: *id,
|
||||||
publisher: *publishing_session,
|
publisher: *publishing_session,
|
||||||
|
external_network_block_hash: *external_network_block_hash,
|
||||||
in_instructions_hash: *in_instructions_hash,
|
in_instructions_hash: *in_instructions_hash,
|
||||||
in_instruction_results: in_instruction_results
|
in_instruction_results: in_instruction_results
|
||||||
.iter()
|
.iter()
|
||||||
|
|||||||
@@ -277,23 +277,14 @@ pub async fn main_loop<
|
|||||||
} => {
|
} => {
|
||||||
let scanner = scanner.as_mut().unwrap();
|
let scanner = scanner.as_mut().unwrap();
|
||||||
|
|
||||||
if let Some(messages::substrate::ExecutedBatch {
|
if let Some(batch) = batch {
|
||||||
id,
|
|
||||||
publisher,
|
|
||||||
in_instructions_hash,
|
|
||||||
in_instruction_results,
|
|
||||||
}) = batch
|
|
||||||
{
|
|
||||||
let key_to_activate =
|
let key_to_activate =
|
||||||
KeyToActivate::<KeyFor<S>>::try_recv(txn.as_mut().unwrap()).map(|key| key.0);
|
KeyToActivate::<KeyFor<S>>::try_recv(txn.as_mut().unwrap()).map(|key| key.0);
|
||||||
|
|
||||||
// This is a cheap call as it internally just queues this to be done later
|
// This is a cheap call as it internally just queues this to be done later
|
||||||
let _: () = scanner.acknowledge_batch(
|
let _: () = scanner.acknowledge_batch(
|
||||||
txn.take().unwrap(),
|
txn.take().unwrap(),
|
||||||
id,
|
batch,
|
||||||
publisher,
|
|
||||||
in_instructions_hash,
|
|
||||||
in_instruction_results,
|
|
||||||
/*
|
/*
|
||||||
`acknowledge_batch` takes burns to optimize handling returns with standard
|
`acknowledge_batch` takes burns to optimize handling returns with standard
|
||||||
payments. That's why handling these with a Batch (and not waiting until the
|
payments. That's why handling these with a Batch (and not waiting until the
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ pub mod substrate {
|
|||||||
pub struct ExecutedBatch {
|
pub struct ExecutedBatch {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub publisher: Session,
|
pub publisher: Session,
|
||||||
|
pub external_network_block_hash: [u8; 32],
|
||||||
pub in_instructions_hash: [u8; 32],
|
pub in_instructions_hash: [u8; 32],
|
||||||
pub in_instruction_results: Vec<InInstructionResult>,
|
pub in_instruction_results: Vec<InInstructionResult>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use serai_in_instructions_primitives::{MAX_BATCH_SIZE, Batch};
|
|||||||
use primitives::{EncodableG, task::ContinuallyRan};
|
use primitives::{EncodableG, task::ContinuallyRan};
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{Returnable, ScannerGlobalDb, InInstructionData, ScanToBatchDb, BatchData, BatchToReportDb},
|
db::{Returnable, ScannerGlobalDb, InInstructionData, ScanToBatchDb, BatchData, BatchToReportDb},
|
||||||
|
index,
|
||||||
scan::next_to_scan_for_outputs_block,
|
scan::next_to_scan_for_outputs_block,
|
||||||
ScannerFeed, KeyFor,
|
ScannerFeed, KeyFor,
|
||||||
};
|
};
|
||||||
@@ -100,10 +101,16 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for BatchTask<D, S> {
|
|||||||
// If this block is notable, create the Batch(s) for it
|
// If this block is notable, create the Batch(s) for it
|
||||||
if notable {
|
if notable {
|
||||||
let network = S::NETWORK;
|
let network = S::NETWORK;
|
||||||
|
let external_network_block_hash = index::block_id(&txn, block_number);
|
||||||
let mut batch_id = BatchDb::<S>::acquire_batch_id(&mut txn);
|
let mut batch_id = BatchDb::<S>::acquire_batch_id(&mut txn);
|
||||||
|
|
||||||
// start with empty batch
|
// start with empty batch
|
||||||
let mut batches = vec![Batch { network, id: batch_id, instructions: vec![] }];
|
let mut batches = vec![Batch {
|
||||||
|
network,
|
||||||
|
id: batch_id,
|
||||||
|
external_network_block_hash,
|
||||||
|
instructions: vec![],
|
||||||
|
}];
|
||||||
// We also track the return information for the InInstructions within a Batch in case
|
// We also track the return information for the InInstructions within a Batch in case
|
||||||
// they error
|
// they error
|
||||||
let mut return_information = vec![vec![]];
|
let mut return_information = vec![vec![]];
|
||||||
@@ -123,7 +130,12 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for BatchTask<D, S> {
|
|||||||
batch_id = BatchDb::<S>::acquire_batch_id(&mut txn);
|
batch_id = BatchDb::<S>::acquire_batch_id(&mut txn);
|
||||||
|
|
||||||
// make a new batch with this instruction included
|
// make a new batch with this instruction included
|
||||||
batches.push(Batch { network, id: batch_id, instructions: vec![in_instruction] });
|
batches.push(Batch {
|
||||||
|
network,
|
||||||
|
id: batch_id,
|
||||||
|
external_network_block_hash,
|
||||||
|
instructions: vec![in_instruction],
|
||||||
|
});
|
||||||
// Since we're allocating a new batch, allocate a new set of return addresses for it
|
// Since we're allocating a new batch, allocate a new set of return addresses for it
|
||||||
return_information.push(vec![]);
|
return_information.push(vec![]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ use borsh::{BorshSerialize, BorshDeserialize};
|
|||||||
use serai_db::{Get, DbTxn, Db};
|
use serai_db::{Get, DbTxn, Db};
|
||||||
|
|
||||||
use serai_primitives::{NetworkId, Coin, Amount};
|
use serai_primitives::{NetworkId, Coin, Amount};
|
||||||
use serai_validator_sets_primitives::Session;
|
|
||||||
use serai_coins_primitives::OutInstructionWithBalance;
|
use serai_coins_primitives::OutInstructionWithBalance;
|
||||||
|
|
||||||
|
use messages::substrate::ExecutedBatch;
|
||||||
use primitives::{task::*, Address, ReceivedOutput, Block, Payment};
|
use primitives::{task::*, Address, ReceivedOutput, Block, Payment};
|
||||||
|
|
||||||
// Logic for deciding where in its lifetime a multisig is.
|
// Logic for deciding where in its lifetime a multisig is.
|
||||||
@@ -444,29 +444,17 @@ impl<S: ScannerFeed> Scanner<S> {
|
|||||||
/// `queue_burns`. Doing so will cause them to be executed multiple times.
|
/// `queue_burns`. Doing so will cause them to be executed multiple times.
|
||||||
///
|
///
|
||||||
/// The calls to this function must be ordered with regards to `queue_burns`.
|
/// The calls to this function must be ordered with regards to `queue_burns`.
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn acknowledge_batch(
|
pub fn acknowledge_batch(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut txn: impl DbTxn,
|
mut txn: impl DbTxn,
|
||||||
batch_id: u32,
|
batch: ExecutedBatch,
|
||||||
publisher: Session,
|
|
||||||
in_instructions_hash: [u8; 32],
|
|
||||||
in_instruction_results: Vec<messages::substrate::InInstructionResult>,
|
|
||||||
burns: Vec<OutInstructionWithBalance>,
|
burns: Vec<OutInstructionWithBalance>,
|
||||||
key_to_activate: Option<KeyFor<S>>,
|
key_to_activate: Option<KeyFor<S>>,
|
||||||
) {
|
) {
|
||||||
log::info!("acknowledging batch {batch_id}");
|
log::info!("acknowledging batch {}", batch.id);
|
||||||
|
|
||||||
// Queue acknowledging this block via the Substrate task
|
// Queue acknowledging this block via the Substrate task
|
||||||
substrate::queue_acknowledge_batch::<S>(
|
substrate::queue_acknowledge_batch::<S>(&mut txn, batch, burns, key_to_activate);
|
||||||
&mut txn,
|
|
||||||
batch_id,
|
|
||||||
publisher,
|
|
||||||
in_instructions_hash,
|
|
||||||
in_instruction_results,
|
|
||||||
burns,
|
|
||||||
key_to_activate,
|
|
||||||
);
|
|
||||||
// Commit this txn so this data is flushed
|
// Commit this txn so this data is flushed
|
||||||
txn.commit();
|
txn.commit();
|
||||||
// Then run the Substrate task
|
// Then run the Substrate task
|
||||||
|
|||||||
@@ -6,16 +6,14 @@ use borsh::{BorshSerialize, BorshDeserialize};
|
|||||||
use serai_db::{Get, DbTxn, create_db, db_channel};
|
use serai_db::{Get, DbTxn, create_db, db_channel};
|
||||||
|
|
||||||
use serai_coins_primitives::OutInstructionWithBalance;
|
use serai_coins_primitives::OutInstructionWithBalance;
|
||||||
use serai_validator_sets_primitives::Session;
|
|
||||||
|
use messages::substrate::ExecutedBatch;
|
||||||
|
|
||||||
use crate::{ScannerFeed, KeyFor};
|
use crate::{ScannerFeed, KeyFor};
|
||||||
|
|
||||||
#[derive(BorshSerialize, BorshDeserialize)]
|
#[derive(BorshSerialize, BorshDeserialize)]
|
||||||
struct AcknowledgeBatchEncodable {
|
struct AcknowledgeBatchEncodable {
|
||||||
batch_id: u32,
|
batch: ExecutedBatch,
|
||||||
publisher: Session,
|
|
||||||
in_instructions_hash: [u8; 32],
|
|
||||||
in_instruction_results: Vec<messages::substrate::InInstructionResult>,
|
|
||||||
burns: Vec<OutInstructionWithBalance>,
|
burns: Vec<OutInstructionWithBalance>,
|
||||||
key_to_activate: Option<Vec<u8>>,
|
key_to_activate: Option<Vec<u8>>,
|
||||||
}
|
}
|
||||||
@@ -27,10 +25,7 @@ enum ActionEncodable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct AcknowledgeBatch<S: ScannerFeed> {
|
pub(crate) struct AcknowledgeBatch<S: ScannerFeed> {
|
||||||
pub(crate) batch_id: u32,
|
pub(crate) batch: ExecutedBatch,
|
||||||
pub(crate) publisher: Session,
|
|
||||||
pub(crate) in_instructions_hash: [u8; 32],
|
|
||||||
pub(crate) in_instruction_results: Vec<messages::substrate::InInstructionResult>,
|
|
||||||
pub(crate) burns: Vec<OutInstructionWithBalance>,
|
pub(crate) burns: Vec<OutInstructionWithBalance>,
|
||||||
pub(crate) key_to_activate: Option<KeyFor<S>>,
|
pub(crate) key_to_activate: Option<KeyFor<S>>,
|
||||||
}
|
}
|
||||||
@@ -64,20 +59,14 @@ impl<S: ScannerFeed> SubstrateDb<S> {
|
|||||||
|
|
||||||
pub(crate) fn queue_acknowledge_batch(
|
pub(crate) fn queue_acknowledge_batch(
|
||||||
txn: &mut impl DbTxn,
|
txn: &mut impl DbTxn,
|
||||||
batch_id: u32,
|
batch: ExecutedBatch,
|
||||||
publisher: Session,
|
|
||||||
in_instructions_hash: [u8; 32],
|
|
||||||
in_instruction_results: Vec<messages::substrate::InInstructionResult>,
|
|
||||||
burns: Vec<OutInstructionWithBalance>,
|
burns: Vec<OutInstructionWithBalance>,
|
||||||
key_to_activate: Option<KeyFor<S>>,
|
key_to_activate: Option<KeyFor<S>>,
|
||||||
) {
|
) {
|
||||||
Actions::send(
|
Actions::send(
|
||||||
txn,
|
txn,
|
||||||
&ActionEncodable::AcknowledgeBatch(AcknowledgeBatchEncodable {
|
&ActionEncodable::AcknowledgeBatch(AcknowledgeBatchEncodable {
|
||||||
batch_id,
|
batch,
|
||||||
publisher,
|
|
||||||
in_instructions_hash,
|
|
||||||
in_instruction_results,
|
|
||||||
burns,
|
burns,
|
||||||
key_to_activate: key_to_activate.map(|key| key.to_bytes().as_ref().to_vec()),
|
key_to_activate: key_to_activate.map(|key| key.to_bytes().as_ref().to_vec()),
|
||||||
}),
|
}),
|
||||||
@@ -91,17 +80,11 @@ impl<S: ScannerFeed> SubstrateDb<S> {
|
|||||||
let action_encodable = Actions::try_recv(txn)?;
|
let action_encodable = Actions::try_recv(txn)?;
|
||||||
Some(match action_encodable {
|
Some(match action_encodable {
|
||||||
ActionEncodable::AcknowledgeBatch(AcknowledgeBatchEncodable {
|
ActionEncodable::AcknowledgeBatch(AcknowledgeBatchEncodable {
|
||||||
batch_id,
|
batch,
|
||||||
publisher,
|
|
||||||
in_instructions_hash,
|
|
||||||
in_instruction_results,
|
|
||||||
burns,
|
burns,
|
||||||
key_to_activate,
|
key_to_activate,
|
||||||
}) => Action::AcknowledgeBatch(AcknowledgeBatch {
|
}) => Action::AcknowledgeBatch(AcknowledgeBatch {
|
||||||
batch_id,
|
batch,
|
||||||
publisher,
|
|
||||||
in_instructions_hash,
|
|
||||||
in_instruction_results,
|
|
||||||
burns,
|
burns,
|
||||||
key_to_activate: key_to_activate.map(|key| {
|
key_to_activate: key_to_activate.map(|key| {
|
||||||
let mut repr = <KeyFor<S> as GroupEncoding>::Repr::default();
|
let mut repr = <KeyFor<S> as GroupEncoding>::Repr::default();
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ use core::{marker::PhantomData, future::Future};
|
|||||||
use serai_db::{Get, DbTxn, Db};
|
use serai_db::{Get, DbTxn, Db};
|
||||||
|
|
||||||
use serai_coins_primitives::{OutInstruction, OutInstructionWithBalance};
|
use serai_coins_primitives::{OutInstruction, OutInstructionWithBalance};
|
||||||
use serai_validator_sets_primitives::Session;
|
|
||||||
|
|
||||||
|
use messages::substrate::ExecutedBatch;
|
||||||
use primitives::task::ContinuallyRan;
|
use primitives::task::ContinuallyRan;
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{ScannerGlobalDb, SubstrateToEventualityDb, AcknowledgedBatches},
|
db::{ScannerGlobalDb, SubstrateToEventualityDb, AcknowledgedBatches},
|
||||||
batch, ScannerFeed, KeyFor,
|
index, batch, ScannerFeed, KeyFor,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod db;
|
mod db;
|
||||||
@@ -19,22 +19,11 @@ pub(crate) fn last_acknowledged_batch<S: ScannerFeed>(getter: &impl Get) -> Opti
|
|||||||
}
|
}
|
||||||
pub(crate) fn queue_acknowledge_batch<S: ScannerFeed>(
|
pub(crate) fn queue_acknowledge_batch<S: ScannerFeed>(
|
||||||
txn: &mut impl DbTxn,
|
txn: &mut impl DbTxn,
|
||||||
batch_id: u32,
|
batch: ExecutedBatch,
|
||||||
publisher: Session,
|
|
||||||
in_instructions_hash: [u8; 32],
|
|
||||||
in_instruction_results: Vec<messages::substrate::InInstructionResult>,
|
|
||||||
burns: Vec<OutInstructionWithBalance>,
|
burns: Vec<OutInstructionWithBalance>,
|
||||||
key_to_activate: Option<KeyFor<S>>,
|
key_to_activate: Option<KeyFor<S>>,
|
||||||
) {
|
) {
|
||||||
SubstrateDb::<S>::queue_acknowledge_batch(
|
SubstrateDb::<S>::queue_acknowledge_batch(txn, batch, burns, key_to_activate)
|
||||||
txn,
|
|
||||||
batch_id,
|
|
||||||
publisher,
|
|
||||||
in_instructions_hash,
|
|
||||||
in_instruction_results,
|
|
||||||
burns,
|
|
||||||
key_to_activate,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
pub(crate) fn queue_queue_burns<S: ScannerFeed>(
|
pub(crate) fn queue_queue_burns<S: ScannerFeed>(
|
||||||
txn: &mut impl DbTxn,
|
txn: &mut impl DbTxn,
|
||||||
@@ -73,40 +62,38 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for SubstrateTask<D, S> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match action {
|
match action {
|
||||||
Action::AcknowledgeBatch(AcknowledgeBatch {
|
Action::AcknowledgeBatch(AcknowledgeBatch { batch, mut burns, key_to_activate }) => {
|
||||||
batch_id,
|
|
||||||
publisher,
|
|
||||||
in_instructions_hash,
|
|
||||||
in_instruction_results,
|
|
||||||
mut burns,
|
|
||||||
key_to_activate,
|
|
||||||
}) => {
|
|
||||||
// Check if we have the information for this batch
|
// Check if we have the information for this batch
|
||||||
let Some(batch::BatchInfo {
|
let Some(batch::BatchInfo {
|
||||||
block_number,
|
block_number,
|
||||||
session_to_sign_batch,
|
session_to_sign_batch,
|
||||||
external_key_for_session_to_sign_batch,
|
external_key_for_session_to_sign_batch,
|
||||||
in_instructions_hash: expected_in_instructions_hash,
|
in_instructions_hash,
|
||||||
}) = batch::take_info_for_batch::<S>(&mut txn, batch_id)
|
}) = batch::take_info_for_batch::<S>(&mut txn, batch.id)
|
||||||
else {
|
else {
|
||||||
// If we don't, drop this txn (restoring the action to the database)
|
// If we don't, drop this txn (restoring the action to the database)
|
||||||
drop(txn);
|
drop(txn);
|
||||||
return Ok(made_progress);
|
return Ok(made_progress);
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
publisher, session_to_sign_batch,
|
batch.publisher, session_to_sign_batch,
|
||||||
"batch acknowledged on-chain was acknowledged by an unexpected publisher"
|
"batch acknowledged on-chain was acknowledged by an unexpected publisher"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
in_instructions_hash, expected_in_instructions_hash,
|
batch.external_network_block_hash,
|
||||||
"batch acknowledged on-chain was distinct"
|
index::block_id(&txn, block_number),
|
||||||
|
"batch acknowledged on-chain was for a distinct block"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
batch.in_instructions_hash, in_instructions_hash,
|
||||||
|
"batch acknowledged on-chain had distinct InInstructions"
|
||||||
);
|
);
|
||||||
|
|
||||||
SubstrateDb::<S>::set_last_acknowledged_batch(&mut txn, batch_id);
|
SubstrateDb::<S>::set_last_acknowledged_batch(&mut txn, batch.id);
|
||||||
AcknowledgedBatches::send(
|
AcknowledgedBatches::send(
|
||||||
&mut txn,
|
&mut txn,
|
||||||
&external_key_for_session_to_sign_batch.0,
|
&external_key_for_session_to_sign_batch.0,
|
||||||
batch_id,
|
batch.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Mark we made progress and handle this
|
// Mark we made progress and handle this
|
||||||
@@ -143,17 +130,17 @@ impl<D: Db, S: ScannerFeed> ContinuallyRan for SubstrateTask<D, S> {
|
|||||||
|
|
||||||
// Return the balances for any InInstructions which failed to execute
|
// Return the balances for any InInstructions which failed to execute
|
||||||
{
|
{
|
||||||
let return_information = batch::take_return_information::<S>(&mut txn, batch_id)
|
let return_information = batch::take_return_information::<S>(&mut txn, batch.id)
|
||||||
.expect("didn't save the return information for Batch we published");
|
.expect("didn't save the return information for Batch we published");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
in_instruction_results.len(),
|
batch.in_instruction_results.len(),
|
||||||
return_information.len(),
|
return_information.len(),
|
||||||
"amount of InInstruction succeededs differed from amount of return information saved"
|
"amount of InInstruction succeededs differed from amount of return information saved"
|
||||||
);
|
);
|
||||||
|
|
||||||
// We map these into standard Burns
|
// We map these into standard Burns
|
||||||
for (result, return_information) in
|
for (result, return_information) in
|
||||||
in_instruction_results.into_iter().zip(return_information)
|
batch.in_instruction_results.into_iter().zip(return_information)
|
||||||
{
|
{
|
||||||
if result == messages::substrate::InInstructionResult::Succeeded {
|
if result == messages::substrate::InInstructionResult::Succeeded {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ pub enum Event {
|
|||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
publishing_session: Session,
|
publishing_session: Session,
|
||||||
id: u32,
|
id: u32,
|
||||||
|
external_network_block_hash: [u8; 32],
|
||||||
in_instructions_hash: [u8; 32],
|
in_instructions_hash: [u8; 32],
|
||||||
in_instruction_results: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
in_instruction_results: bitvec::vec::BitVec<u8, bitvec::order::Lsb0>,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ pub mod pallet {
|
|||||||
Batch {
|
Batch {
|
||||||
network: NetworkId,
|
network: NetworkId,
|
||||||
publishing_session: Session,
|
publishing_session: Session,
|
||||||
|
external_network_block_hash: [u8; 32],
|
||||||
id: u32,
|
id: u32,
|
||||||
in_instructions_hash: [u8; 32],
|
in_instructions_hash: [u8; 32],
|
||||||
in_instruction_results: BitVec<u8, Lsb0>,
|
in_instruction_results: BitVec<u8, Lsb0>,
|
||||||
@@ -356,6 +357,7 @@ pub mod pallet {
|
|||||||
network: batch.network,
|
network: batch.network,
|
||||||
publishing_session: if valid_by_prior { prior_session } else { current_session },
|
publishing_session: if valid_by_prior { prior_session } else { current_session },
|
||||||
id: batch.id,
|
id: batch.id,
|
||||||
|
external_network_block_hash: batch.external_network_block_hash,
|
||||||
in_instructions_hash,
|
in_instructions_hash,
|
||||||
in_instruction_results,
|
in_instruction_results,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ pub struct InInstructionWithBalance {
|
|||||||
pub struct Batch {
|
pub struct Batch {
|
||||||
pub network: NetworkId,
|
pub network: NetworkId,
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
|
pub external_network_block_hash: [u8; 32],
|
||||||
pub instructions: Vec<InInstructionWithBalance>,
|
pub instructions: Vec<InInstructionWithBalance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user