use std::{collections::HashMap, sync::Mutex};
use crate::PublicKey;
use crate::types::AccountId;
type Network = String;
pub struct NonceManager {
nonces: Mutex<HashMap<(Network, AccountId, PublicKey), u64>>,
}
impl Default for NonceManager {
fn default() -> Self {
Self::new()
}
}
impl NonceManager {
pub fn new() -> Self {
Self {
nonces: Mutex::new(HashMap::new()),
}
}
pub fn next(
&self,
network: impl Into<Network>,
account_id: AccountId,
public_key: PublicKey,
blockchain_nonce: u64,
) -> u64 {
let mut nonces = self.nonces.lock().unwrap();
let nonce = nonces
.entry((network.into(), account_id, public_key))
.and_modify(|n| *n = (*n).max(blockchain_nonce))
.or_insert(blockchain_nonce);
*nonce += 1;
*nonce
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_nonce_manager_caching() {
let manager = NonceManager::new();
let nonce1 = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
10,
);
assert_eq!(nonce1, 11);
let nonce2 = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
10,
);
assert_eq!(nonce2, 12);
let nonce3 = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
10,
);
assert_eq!(nonce3, 13);
}
#[test]
fn test_chain_catches_up() {
let manager = NonceManager::new();
let nonce1 = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
10,
);
assert_eq!(nonce1, 11);
let nonce2 = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
15,
);
assert_eq!(nonce2, 16); }
#[test]
fn test_different_keys() {
let manager = NonceManager::new();
let nonce_alice = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
10,
);
assert_eq!(nonce_alice, 11);
let nonce_bob = manager.next(
"testnet",
"bob.testnet".parse().unwrap(),
"ed25519:22skMptHjFWNyuEWY22ftn2AbLPSYpmYwGJRGwpNHbTV"
.parse()
.unwrap(),
20,
);
assert_eq!(nonce_bob, 21);
let nonce_alice2 = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
10,
);
assert_eq!(nonce_alice2, 12);
}
#[test]
fn test_different_networks() {
let manager = NonceManager::new();
let nonce_testnet = manager.next(
"testnet",
"alice.near".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
10,
);
assert_eq!(nonce_testnet, 11);
let nonce_mainnet = manager.next(
"mainnet",
"alice.near".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
50,
);
assert_eq!(nonce_mainnet, 51);
let nonce_testnet2 = manager.next(
"testnet",
"alice.near".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
10,
);
assert_eq!(nonce_testnet2, 12);
}
#[test]
fn test_ak_nonce_from_error() {
let manager = NonceManager::new();
let nonce1 = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
10,
);
assert_eq!(nonce1, 11);
let nonce2 = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
100,
);
assert_eq!(nonce2, 101);
let nonce3 = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
100,
);
assert_eq!(nonce3, 102);
}
#[test]
fn test_lower_ak_nonce_uses_cache() {
let manager = NonceManager::new();
assert_eq!(
manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
100,
),
101
);
assert_eq!(
manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
100,
),
102
);
assert_eq!(
manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
100,
),
103
);
let nonce = manager.next(
"testnet",
"alice.testnet".parse().unwrap(),
"ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp"
.parse()
.unwrap(),
100,
);
assert_eq!(nonce, 104); }
}