Add a cosigning protocol to ensure finalizations are unique (#433)
* Add a function to deterministically decide which Serai blocks should be co-signed
Has a 5 minute latency between co-signs, also used as the maximal latency
before a co-sign is started.
* Get all active tributaries we're in at a specific block
* Add and route CosignSubstrateBlock, a new provided TX
* Split queued cosigns per network
* Rename BatchSignId to SubstrateSignId
* Add SubstrateSignableId, a meta-type for either Batch or Block, and modularize around it
* Handle the CosignSubstrateBlock provided TX
* Revert substrate_signer.rs to develop (and patch to still work)
Due to SubstrateSigner moving when the prior multisig closes, yet cosigning
occurring with the most recent key, a single SubstrateSigner can be reused.
We could manage multiple SubstrateSigners, yet considering the much lower
specifications for cosigning, I'd rather treat it distinctly.
* Route cosigning through the processor
* Add note to rename SubstrateSigner post-PR
I don't want to do so now in order to preserve the diff's clarity.
* Implement cosign evaluation into the coordinator
* Get tests to compile
* Bug fixes, mark blocks without cosigners available as cosigned
* Correct the ID Batch preprocesses are saved under, add log statements
* Create a dedicated function to handle cosigns
* Correct the flow around Batch verification/queueing
Verifying `Batch`s could stall when a `Batch` was signed before its
predecessors/before the block it's contained in was cosigned (the latter being
inevitable as we can't sign a block containing a signed batch before signing
the batch).
Now, Batch verification happens on a distinct async task in order to not block
the handling of processor messages. This task is the sole caller of verify in
order to ensure last_verified_batch isn't unexpectedly mutated.
When the processor message handler needs to access it, or needs to queue a
Batch, it associates the DB TXN with a lock preventing the other task from
doing so.
This lock, as currently implemented, is a poor and inefficient design. It
should be modified to the pattern used for cosign management. Additionally, a
new primitive of a DB-backed channel may be immensely valuable.
Fixes a standing potential deadlock and a deadlock introduced with the
cosigning protocol.
* Working full-stack tests
After the last commit, this only required extending a timeout.
* Replace "co-sign" with "cosign" to make finding text easier
* Update the coordinator tests to support cosigning
* Inline prior_batch calculation to prevent panic on rotation
Noticed when doing a final review of the branch.
2023-11-15 16:57:21 -05:00
|
|
|
use std::sync::{OnceLock, MutexGuard, Mutex};
|
|
|
|
|
|
Start moving Coordinator to a multi-Tributary model
Prior, we only supported a single Tributary per network, and spawned a task to
handled Processor messages per Tributary. Now, we handle Processor messages per
network, yet we still only supported a single Tributary in that handling
function.
Now, when we handle a message, we load the Tributary which is relevant. Once we
know it, we ensure we have it (preventing race conditions), and then proceed.
We do need work to check if we should have a Tributary, or if we're not
participating. We also need to check if a Tributary has been retired, meaning
we shouldn't handle any transactions related to them, and to clean up retired
Tributaries.
2023-09-27 18:20:36 -04:00
|
|
|
use scale::{Encode, Decode};
|
|
|
|
|
|
2023-04-20 05:04:08 -04:00
|
|
|
pub use serai_db::*;
|
|
|
|
|
|
2023-09-29 03:51:01 -04:00
|
|
|
use serai_client::{
|
|
|
|
|
primitives::NetworkId,
|
Add a cosigning protocol to ensure finalizations are unique (#433)
* Add a function to deterministically decide which Serai blocks should be co-signed
Has a 5 minute latency between co-signs, also used as the maximal latency
before a co-sign is started.
* Get all active tributaries we're in at a specific block
* Add and route CosignSubstrateBlock, a new provided TX
* Split queued cosigns per network
* Rename BatchSignId to SubstrateSignId
* Add SubstrateSignableId, a meta-type for either Batch or Block, and modularize around it
* Handle the CosignSubstrateBlock provided TX
* Revert substrate_signer.rs to develop (and patch to still work)
Due to SubstrateSigner moving when the prior multisig closes, yet cosigning
occurring with the most recent key, a single SubstrateSigner can be reused.
We could manage multiple SubstrateSigners, yet considering the much lower
specifications for cosigning, I'd rather treat it distinctly.
* Route cosigning through the processor
* Add note to rename SubstrateSigner post-PR
I don't want to do so now in order to preserve the diff's clarity.
* Implement cosign evaluation into the coordinator
* Get tests to compile
* Bug fixes, mark blocks without cosigners available as cosigned
* Correct the ID Batch preprocesses are saved under, add log statements
* Create a dedicated function to handle cosigns
* Correct the flow around Batch verification/queueing
Verifying `Batch`s could stall when a `Batch` was signed before its
predecessors/before the block it's contained in was cosigned (the latter being
inevitable as we can't sign a block containing a signed batch before signing
the batch).
Now, Batch verification happens on a distinct async task in order to not block
the handling of processor messages. This task is the sole caller of verify in
order to ensure last_verified_batch isn't unexpectedly mutated.
When the processor message handler needs to access it, or needs to queue a
Batch, it associates the DB TXN with a lock preventing the other task from
doing so.
This lock, as currently implemented, is a poor and inefficient design. It
should be modified to the pattern used for cosign management. Additionally, a
new primitive of a DB-backed channel may be immensely valuable.
Fixes a standing potential deadlock and a deadlock introduced with the
cosigning protocol.
* Working full-stack tests
After the last commit, this only required extending a timeout.
* Replace "co-sign" with "cosign" to make finding text easier
* Update the coordinator tests to support cosigning
* Inline prior_batch calculation to prevent panic on rotation
Noticed when doing a final review of the branch.
2023-11-15 16:57:21 -05:00
|
|
|
validator_sets::primitives::{Session, ValidatorSet, KeyPair},
|
2023-09-29 03:51:01 -04:00
|
|
|
};
|
Start moving Coordinator to a multi-Tributary model
Prior, we only supported a single Tributary per network, and spawned a task to
handled Processor messages per Tributary. Now, we handle Processor messages per
network, yet we still only supported a single Tributary in that handling
function.
Now, when we handle a message, we load the Tributary which is relevant. Once we
know it, we ensure we have it (preventing race conditions), and then proceed.
We do need work to check if we should have a Tributary, or if we're not
participating. We also need to check if a Tributary has been retired, meaning
we shouldn't handle any transactions related to them, and to clean up retired
Tributaries.
2023-09-27 18:20:36 -04:00
|
|
|
|
Add a cosigning protocol to ensure finalizations are unique (#433)
* Add a function to deterministically decide which Serai blocks should be co-signed
Has a 5 minute latency between co-signs, also used as the maximal latency
before a co-sign is started.
* Get all active tributaries we're in at a specific block
* Add and route CosignSubstrateBlock, a new provided TX
* Split queued cosigns per network
* Rename BatchSignId to SubstrateSignId
* Add SubstrateSignableId, a meta-type for either Batch or Block, and modularize around it
* Handle the CosignSubstrateBlock provided TX
* Revert substrate_signer.rs to develop (and patch to still work)
Due to SubstrateSigner moving when the prior multisig closes, yet cosigning
occurring with the most recent key, a single SubstrateSigner can be reused.
We could manage multiple SubstrateSigners, yet considering the much lower
specifications for cosigning, I'd rather treat it distinctly.
* Route cosigning through the processor
* Add note to rename SubstrateSigner post-PR
I don't want to do so now in order to preserve the diff's clarity.
* Implement cosign evaluation into the coordinator
* Get tests to compile
* Bug fixes, mark blocks without cosigners available as cosigned
* Correct the ID Batch preprocesses are saved under, add log statements
* Create a dedicated function to handle cosigns
* Correct the flow around Batch verification/queueing
Verifying `Batch`s could stall when a `Batch` was signed before its
predecessors/before the block it's contained in was cosigned (the latter being
inevitable as we can't sign a block containing a signed batch before signing
the batch).
Now, Batch verification happens on a distinct async task in order to not block
the handling of processor messages. This task is the sole caller of verify in
order to ensure last_verified_batch isn't unexpectedly mutated.
When the processor message handler needs to access it, or needs to queue a
Batch, it associates the DB TXN with a lock preventing the other task from
doing so.
This lock, as currently implemented, is a poor and inefficient design. It
should be modified to the pattern used for cosign management. Additionally, a
new primitive of a DB-backed channel may be immensely valuable.
Fixes a standing potential deadlock and a deadlock introduced with the
cosigning protocol.
* Working full-stack tests
After the last commit, this only required extending a timeout.
* Replace "co-sign" with "cosign" to make finding text easier
* Update the coordinator tests to support cosigning
* Inline prior_batch calculation to prevent panic on rotation
Noticed when doing a final review of the branch.
2023-11-15 16:57:21 -05:00
|
|
|
create_db! {
|
|
|
|
|
NewSubstrateDb {
|
|
|
|
|
CosignTriggered: () -> (),
|
|
|
|
|
IntendedCosign: () -> (u64, Option<u64>),
|
|
|
|
|
BlockHasEvents: (block: u64) -> u8,
|
|
|
|
|
CosignTransactions: (network: NetworkId) -> Vec<(Session, u64, [u8; 32])>
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl IntendedCosign {
|
|
|
|
|
pub fn set_intended_cosign(txn: &mut impl DbTxn, intended: u64) {
|
|
|
|
|
Self::set(txn, &(intended, None::<u64>));
|
|
|
|
|
}
|
|
|
|
|
pub fn set_skipped_cosign(txn: &mut impl DbTxn, skipped: u64) {
|
|
|
|
|
let (intended, prior_skipped) = Self::get(txn).unwrap();
|
|
|
|
|
assert!(prior_skipped.is_none());
|
|
|
|
|
Self::set(txn, &(intended, Some(skipped)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This guarantees:
|
|
|
|
|
// 1) Appended transactions are appended
|
|
|
|
|
// 2) Taking cosigns does not clear any TXs which weren't taken
|
|
|
|
|
// 3) Taking does actually clear the set
|
|
|
|
|
static COSIGN_LOCK: OnceLock<Mutex<()>> = OnceLock::new();
|
|
|
|
|
pub struct CosignTxn<T: DbTxn>(T, MutexGuard<'static, ()>);
|
|
|
|
|
impl<T: DbTxn> CosignTxn<T> {
|
|
|
|
|
pub fn new(txn: T) -> Self {
|
|
|
|
|
Self(txn, COSIGN_LOCK.get_or_init(|| Mutex::new(())).lock().unwrap())
|
|
|
|
|
}
|
|
|
|
|
pub fn commit(self) {
|
|
|
|
|
self.0.commit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
impl CosignTransactions {
|
|
|
|
|
// Append a cosign transaction.
|
|
|
|
|
pub fn append_cosign<T: DbTxn>(
|
|
|
|
|
txn: &mut CosignTxn<T>,
|
|
|
|
|
set: ValidatorSet,
|
|
|
|
|
number: u64,
|
|
|
|
|
hash: [u8; 32],
|
|
|
|
|
) {
|
|
|
|
|
#[allow(clippy::unwrap_or_default)]
|
|
|
|
|
let mut txs = CosignTransactions::get(&txn.0, set.network).unwrap_or(vec![]);
|
|
|
|
|
txs.push((set.session, number, hash));
|
|
|
|
|
CosignTransactions::set(&mut txn.0, set.network, &txs);
|
|
|
|
|
}
|
|
|
|
|
// Peek at the next cosign transaction.
|
|
|
|
|
pub fn peek_cosign(getter: &impl Get, network: NetworkId) -> Option<(Session, u64, [u8; 32])> {
|
|
|
|
|
let mut to_cosign = CosignTransactions::get(getter, network)?;
|
|
|
|
|
if to_cosign.is_empty() {
|
|
|
|
|
None?
|
|
|
|
|
}
|
|
|
|
|
Some(to_cosign.swap_remove(0))
|
|
|
|
|
}
|
|
|
|
|
// Take the next transaction, panicking if it doesn't exist.
|
|
|
|
|
pub fn take_cosign(mut txn: impl DbTxn, network: NetworkId) {
|
|
|
|
|
let _lock = COSIGN_LOCK.get_or_init(|| Mutex::new(())).lock().unwrap();
|
|
|
|
|
let mut txs = CosignTransactions::get(&txn, network).unwrap();
|
|
|
|
|
txs.remove(0);
|
|
|
|
|
CosignTransactions::set(&mut txn, network, &txs);
|
|
|
|
|
txn.commit();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-20 05:04:08 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct SubstrateDb<D: Db>(pub D);
|
|
|
|
|
impl<D: Db> SubstrateDb<D> {
|
|
|
|
|
pub fn new(db: D) -> Self {
|
|
|
|
|
Self(db)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn substrate_key(dst: &'static [u8], key: impl AsRef<[u8]>) -> Vec<u8> {
|
2023-09-01 01:00:24 -04:00
|
|
|
D::key(b"coordinator_substrate", dst, key)
|
2023-04-20 05:04:08 -04:00
|
|
|
}
|
|
|
|
|
|
Add a cosigning protocol to ensure finalizations are unique (#433)
* Add a function to deterministically decide which Serai blocks should be co-signed
Has a 5 minute latency between co-signs, also used as the maximal latency
before a co-sign is started.
* Get all active tributaries we're in at a specific block
* Add and route CosignSubstrateBlock, a new provided TX
* Split queued cosigns per network
* Rename BatchSignId to SubstrateSignId
* Add SubstrateSignableId, a meta-type for either Batch or Block, and modularize around it
* Handle the CosignSubstrateBlock provided TX
* Revert substrate_signer.rs to develop (and patch to still work)
Due to SubstrateSigner moving when the prior multisig closes, yet cosigning
occurring with the most recent key, a single SubstrateSigner can be reused.
We could manage multiple SubstrateSigners, yet considering the much lower
specifications for cosigning, I'd rather treat it distinctly.
* Route cosigning through the processor
* Add note to rename SubstrateSigner post-PR
I don't want to do so now in order to preserve the diff's clarity.
* Implement cosign evaluation into the coordinator
* Get tests to compile
* Bug fixes, mark blocks without cosigners available as cosigned
* Correct the ID Batch preprocesses are saved under, add log statements
* Create a dedicated function to handle cosigns
* Correct the flow around Batch verification/queueing
Verifying `Batch`s could stall when a `Batch` was signed before its
predecessors/before the block it's contained in was cosigned (the latter being
inevitable as we can't sign a block containing a signed batch before signing
the batch).
Now, Batch verification happens on a distinct async task in order to not block
the handling of processor messages. This task is the sole caller of verify in
order to ensure last_verified_batch isn't unexpectedly mutated.
When the processor message handler needs to access it, or needs to queue a
Batch, it associates the DB TXN with a lock preventing the other task from
doing so.
This lock, as currently implemented, is a poor and inefficient design. It
should be modified to the pattern used for cosign management. Additionally, a
new primitive of a DB-backed channel may be immensely valuable.
Fixes a standing potential deadlock and a deadlock introduced with the
cosigning protocol.
* Working full-stack tests
After the last commit, this only required extending a timeout.
* Replace "co-sign" with "cosign" to make finding text easier
* Update the coordinator tests to support cosigning
* Inline prior_batch calculation to prevent panic on rotation
Noticed when doing a final review of the branch.
2023-11-15 16:57:21 -05:00
|
|
|
fn next_block_key() -> Vec<u8> {
|
|
|
|
|
Self::substrate_key(b"next_block", [])
|
2023-04-20 05:04:08 -04:00
|
|
|
}
|
2023-08-02 12:18:50 -04:00
|
|
|
pub fn set_next_block(&mut self, block: u64) {
|
2023-04-20 05:04:08 -04:00
|
|
|
let mut txn = self.0.txn();
|
Add a cosigning protocol to ensure finalizations are unique (#433)
* Add a function to deterministically decide which Serai blocks should be co-signed
Has a 5 minute latency between co-signs, also used as the maximal latency
before a co-sign is started.
* Get all active tributaries we're in at a specific block
* Add and route CosignSubstrateBlock, a new provided TX
* Split queued cosigns per network
* Rename BatchSignId to SubstrateSignId
* Add SubstrateSignableId, a meta-type for either Batch or Block, and modularize around it
* Handle the CosignSubstrateBlock provided TX
* Revert substrate_signer.rs to develop (and patch to still work)
Due to SubstrateSigner moving when the prior multisig closes, yet cosigning
occurring with the most recent key, a single SubstrateSigner can be reused.
We could manage multiple SubstrateSigners, yet considering the much lower
specifications for cosigning, I'd rather treat it distinctly.
* Route cosigning through the processor
* Add note to rename SubstrateSigner post-PR
I don't want to do so now in order to preserve the diff's clarity.
* Implement cosign evaluation into the coordinator
* Get tests to compile
* Bug fixes, mark blocks without cosigners available as cosigned
* Correct the ID Batch preprocesses are saved under, add log statements
* Create a dedicated function to handle cosigns
* Correct the flow around Batch verification/queueing
Verifying `Batch`s could stall when a `Batch` was signed before its
predecessors/before the block it's contained in was cosigned (the latter being
inevitable as we can't sign a block containing a signed batch before signing
the batch).
Now, Batch verification happens on a distinct async task in order to not block
the handling of processor messages. This task is the sole caller of verify in
order to ensure last_verified_batch isn't unexpectedly mutated.
When the processor message handler needs to access it, or needs to queue a
Batch, it associates the DB TXN with a lock preventing the other task from
doing so.
This lock, as currently implemented, is a poor and inefficient design. It
should be modified to the pattern used for cosign management. Additionally, a
new primitive of a DB-backed channel may be immensely valuable.
Fixes a standing potential deadlock and a deadlock introduced with the
cosigning protocol.
* Working full-stack tests
After the last commit, this only required extending a timeout.
* Replace "co-sign" with "cosign" to make finding text easier
* Update the coordinator tests to support cosigning
* Inline prior_batch calculation to prevent panic on rotation
Noticed when doing a final review of the branch.
2023-11-15 16:57:21 -05:00
|
|
|
txn.put(Self::next_block_key(), block.to_le_bytes());
|
2023-04-20 05:04:08 -04:00
|
|
|
txn.commit();
|
|
|
|
|
}
|
2023-08-02 12:18:50 -04:00
|
|
|
pub fn next_block(&self) -> u64 {
|
Add a cosigning protocol to ensure finalizations are unique (#433)
* Add a function to deterministically decide which Serai blocks should be co-signed
Has a 5 minute latency between co-signs, also used as the maximal latency
before a co-sign is started.
* Get all active tributaries we're in at a specific block
* Add and route CosignSubstrateBlock, a new provided TX
* Split queued cosigns per network
* Rename BatchSignId to SubstrateSignId
* Add SubstrateSignableId, a meta-type for either Batch or Block, and modularize around it
* Handle the CosignSubstrateBlock provided TX
* Revert substrate_signer.rs to develop (and patch to still work)
Due to SubstrateSigner moving when the prior multisig closes, yet cosigning
occurring with the most recent key, a single SubstrateSigner can be reused.
We could manage multiple SubstrateSigners, yet considering the much lower
specifications for cosigning, I'd rather treat it distinctly.
* Route cosigning through the processor
* Add note to rename SubstrateSigner post-PR
I don't want to do so now in order to preserve the diff's clarity.
* Implement cosign evaluation into the coordinator
* Get tests to compile
* Bug fixes, mark blocks without cosigners available as cosigned
* Correct the ID Batch preprocesses are saved under, add log statements
* Create a dedicated function to handle cosigns
* Correct the flow around Batch verification/queueing
Verifying `Batch`s could stall when a `Batch` was signed before its
predecessors/before the block it's contained in was cosigned (the latter being
inevitable as we can't sign a block containing a signed batch before signing
the batch).
Now, Batch verification happens on a distinct async task in order to not block
the handling of processor messages. This task is the sole caller of verify in
order to ensure last_verified_batch isn't unexpectedly mutated.
When the processor message handler needs to access it, or needs to queue a
Batch, it associates the DB TXN with a lock preventing the other task from
doing so.
This lock, as currently implemented, is a poor and inefficient design. It
should be modified to the pattern used for cosign management. Additionally, a
new primitive of a DB-backed channel may be immensely valuable.
Fixes a standing potential deadlock and a deadlock introduced with the
cosigning protocol.
* Working full-stack tests
After the last commit, this only required extending a timeout.
* Replace "co-sign" with "cosign" to make finding text easier
* Update the coordinator tests to support cosigning
* Inline prior_batch calculation to prevent panic on rotation
Noticed when doing a final review of the branch.
2023-11-15 16:57:21 -05:00
|
|
|
u64::from_le_bytes(self.0.get(Self::next_block_key()).unwrap_or(vec![0; 8]).try_into().unwrap())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn latest_cosigned_block_key() -> Vec<u8> {
|
|
|
|
|
Self::substrate_key(b"latest_cosigned_block", [])
|
|
|
|
|
}
|
|
|
|
|
pub fn set_latest_cosigned_block(txn: &mut D::Transaction<'_>, latest_cosigned_block: u64) {
|
|
|
|
|
txn.put(Self::latest_cosigned_block_key(), latest_cosigned_block.to_le_bytes());
|
|
|
|
|
}
|
|
|
|
|
pub fn latest_cosigned_block<G: Get>(getter: &G) -> u64 {
|
|
|
|
|
let db = u64::from_le_bytes(
|
|
|
|
|
getter.get(Self::latest_cosigned_block_key()).unwrap_or(vec![0; 8]).try_into().unwrap(),
|
|
|
|
|
);
|
|
|
|
|
// Mark the genesis as cosigned
|
|
|
|
|
db.max(1)
|
2023-04-20 05:04:08 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn event_key(id: &[u8], index: u32) -> Vec<u8> {
|
|
|
|
|
Self::substrate_key(b"event", [id, index.to_le_bytes().as_ref()].concat())
|
|
|
|
|
}
|
|
|
|
|
pub fn handled_event<G: Get>(getter: &G, id: [u8; 32], index: u32) -> bool {
|
|
|
|
|
getter.get(Self::event_key(&id, index)).is_some()
|
|
|
|
|
}
|
|
|
|
|
pub fn handle_event(txn: &mut D::Transaction<'_>, id: [u8; 32], index: u32) {
|
|
|
|
|
assert!(!Self::handled_event(txn, id, index));
|
|
|
|
|
txn.put(Self::event_key(&id, index), []);
|
|
|
|
|
}
|
Start moving Coordinator to a multi-Tributary model
Prior, we only supported a single Tributary per network, and spawned a task to
handled Processor messages per Tributary. Now, we handle Processor messages per
network, yet we still only supported a single Tributary in that handling
function.
Now, when we handle a message, we load the Tributary which is relevant. Once we
know it, we ensure we have it (preventing race conditions), and then proceed.
We do need work to check if we should have a Tributary, or if we're not
participating. We also need to check if a Tributary has been retired, meaning
we shouldn't handle any transactions related to them, and to clean up retired
Tributaries.
2023-09-27 18:20:36 -04:00
|
|
|
|
|
|
|
|
fn session_key(key: &[u8]) -> Vec<u8> {
|
|
|
|
|
Self::substrate_key(b"session", key)
|
|
|
|
|
}
|
|
|
|
|
pub fn session_for_key<G: Get>(getter: &G, key: &[u8]) -> Option<Session> {
|
|
|
|
|
getter.get(Self::session_key(key)).map(|bytes| Session::decode(&mut bytes.as_ref()).unwrap())
|
|
|
|
|
}
|
|
|
|
|
pub fn save_session_for_keys(txn: &mut D::Transaction<'_>, key_pair: &KeyPair, session: Session) {
|
|
|
|
|
let session = session.encode();
|
|
|
|
|
let key_0 = Self::session_key(&key_pair.0);
|
|
|
|
|
let existing = txn.get(&key_0);
|
|
|
|
|
// This may trigger if 100% of a DKG are malicious, and they create a key equivalent to a prior
|
|
|
|
|
// key. Since it requires 100% maliciousness, not just 67% maliciousness, this will only assert
|
|
|
|
|
// in a modified-to-be-malicious stack, making it safe
|
|
|
|
|
assert!(existing.is_none() || (existing.as_ref() == Some(&session)));
|
|
|
|
|
txn.put(key_0, session.clone());
|
|
|
|
|
txn.put(Self::session_key(&key_pair.1), session);
|
|
|
|
|
}
|
2023-09-29 03:51:01 -04:00
|
|
|
|
|
|
|
|
fn batch_instructions_key(network: NetworkId, id: u32) -> Vec<u8> {
|
|
|
|
|
Self::substrate_key(b"batch", (network, id).encode())
|
|
|
|
|
}
|
|
|
|
|
pub fn batch_instructions_hash<G: Get>(
|
|
|
|
|
getter: &G,
|
|
|
|
|
network: NetworkId,
|
|
|
|
|
id: u32,
|
|
|
|
|
) -> Option<[u8; 32]> {
|
|
|
|
|
getter.get(Self::batch_instructions_key(network, id)).map(|bytes| bytes.try_into().unwrap())
|
|
|
|
|
}
|
|
|
|
|
pub fn save_batch_instructions_hash(
|
|
|
|
|
txn: &mut D::Transaction<'_>,
|
|
|
|
|
network: NetworkId,
|
|
|
|
|
id: u32,
|
|
|
|
|
hash: [u8; 32],
|
|
|
|
|
) {
|
|
|
|
|
txn.put(Self::batch_instructions_key(network, id), hash);
|
|
|
|
|
}
|
2023-04-20 05:04:08 -04:00
|
|
|
}
|