use crate::{error::RpcError, rpc_trait::RpcFunctions, rpc_types::*};
use snarkos_consensus::{get_block_reward, memory_pool::Entry, ConsensusParameters, MemoryPool, MerkleTreeLedger};
use snarkos_network::{Node, Sync};
use snarkvm_dpc::base_dpc::{
instantiated::{Components, Tx},
parameters::PublicParameters,
};
use snarkvm_objects::{BlockHeaderHash, Storage, Transaction};
use snarkvm_utilities::{
bytes::{FromBytes, ToBytes},
to_bytes,
CanonicalSerialize,
};
use chrono::Utc;
use parking_lot::Mutex;
use std::{ops::Deref, sync::Arc};
#[derive(Derivative)]
#[derivative(Clone(bound = ""))]
pub struct RpcImpl<S: Storage>(Arc<RpcInner<S>>);
impl<S: Storage> Deref for RpcImpl<S> {
type Target = RpcInner<S>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct RpcInner<S: Storage> {
pub(crate) storage: Arc<MerkleTreeLedger<S>>,
pub(crate) credentials: Option<RpcCredentials>,
pub(crate) node: Node<S>,
}
impl<S: Storage + Send + core::marker::Sync + 'static> RpcImpl<S> {
pub fn new(storage: Arc<MerkleTreeLedger<S>>, credentials: Option<RpcCredentials>, node: Node<S>) -> Self {
Self(Arc::new(RpcInner {
storage,
credentials,
node,
}))
}
pub fn sync_handler(&self) -> Result<&Arc<Sync<S>>, RpcError> {
self.node.sync().ok_or(RpcError::NoConsensus)
}
pub fn consensus_parameters(&self) -> Result<&ConsensusParameters, RpcError> {
Ok(self.sync_handler()?.consensus_parameters())
}
pub fn dpc_parameters(&self) -> Result<&PublicParameters<Components>, RpcError> {
Ok(self.sync_handler()?.dpc_parameters())
}
pub fn memory_pool(&self) -> Result<&Mutex<MemoryPool<Tx>>, RpcError> {
Ok(self.sync_handler()?.memory_pool())
}
}
impl<S: Storage + Send + core::marker::Sync + 'static> RpcFunctions for RpcImpl<S> {
fn get_block(&self, block_hash_string: String) -> Result<BlockInfo, RpcError> {
let block_hash = hex::decode(&block_hash_string)?;
assert_eq!(block_hash.len(), 32);
let storage = &self.storage;
storage.catch_up_secondary(false)?;
let block_header_hash = BlockHeaderHash::new(block_hash);
let height = match storage.get_block_number(&block_header_hash) {
Ok(block_num) => match storage.is_canon(&block_header_hash) {
true => Some(block_num),
false => None,
},
Err(_) => None,
};
let confirmations = match height {
Some(block_height) => storage.get_current_block_height() - block_height,
None => 0,
};
if let Ok(block) = storage.get_block(&block_header_hash) {
let mut transactions = Vec::with_capacity(block.transactions.len());
for transaction in block.transactions.iter() {
transactions.push(hex::encode(&transaction.transaction_id()?));
}
Ok(BlockInfo {
hash: block_hash_string,
height,
confirmations,
size: block.serialize()?.len(),
previous_block_hash: block.header.previous_block_hash.to_string(),
merkle_root: block.header.merkle_root_hash.to_string(),
pedersen_merkle_root_hash: block.header.pedersen_merkle_root_hash.to_string(),
proof: block.header.proof.to_string(),
time: block.header.time,
difficulty_target: block.header.difficulty_target,
nonce: block.header.nonce,
transactions,
})
} else {
Err(RpcError::InvalidBlockHash(block_hash_string))
}
}
fn get_block_count(&self) -> Result<u32, RpcError> {
let storage = &self.storage;
storage.catch_up_secondary(false)?;
Ok(storage.get_block_count())
}
fn get_best_block_hash(&self) -> Result<String, RpcError> {
let storage = &self.storage;
storage.catch_up_secondary(false)?;
let best_block_hash = storage.get_block_hash(storage.get_current_block_height())?;
Ok(hex::encode(&best_block_hash.0))
}
fn get_block_hash(&self, block_height: u32) -> Result<String, RpcError> {
let storage = &self.storage;
storage.catch_up_secondary(false)?;
let block_hash = storage.get_block_hash(block_height)?;
Ok(hex::encode(&block_hash.0))
}
fn get_raw_transaction(&self, transaction_id: String) -> Result<String, RpcError> {
let storage = &self.storage;
storage.catch_up_secondary(false)?;
Ok(hex::encode(
&storage.get_transaction_bytes(&hex::decode(transaction_id)?)?,
))
}
fn get_transaction_info(&self, transaction_id: String) -> Result<TransactionInfo, RpcError> {
let transaction_bytes = self.get_raw_transaction(transaction_id)?;
self.decode_raw_transaction(transaction_bytes)
}
fn decode_raw_transaction(&self, transaction_bytes: String) -> Result<TransactionInfo, RpcError> {
self.storage.catch_up_secondary(false)?;
let transaction_bytes = hex::decode(transaction_bytes)?;
let transaction = Tx::read(&transaction_bytes[..])?;
let mut old_serial_numbers = Vec::with_capacity(transaction.old_serial_numbers().len());
for sn in transaction.old_serial_numbers() {
let mut serial_number: Vec<u8> = vec![];
CanonicalSerialize::serialize(sn, &mut serial_number).unwrap();
old_serial_numbers.push(hex::encode(serial_number));
}
let mut new_commitments = Vec::with_capacity(transaction.new_commitments().len());
for cm in transaction.new_commitments() {
new_commitments.push(hex::encode(to_bytes![cm]?));
}
let memo = hex::encode(to_bytes![transaction.memorandum()]?);
let mut signatures = Vec::with_capacity(transaction.signatures.len());
for sig in &transaction.signatures {
signatures.push(hex::encode(to_bytes![sig]?));
}
let mut encrypted_records = Vec::with_capacity(transaction.encrypted_records.len());
for encrypted_record in &transaction.encrypted_records {
encrypted_records.push(hex::encode(to_bytes![encrypted_record]?));
}
let transaction_id = transaction.transaction_id()?;
let storage = &self.storage;
let block_number = match storage.get_transaction_location(&transaction_id.to_vec())? {
Some(block_location) => Some(storage.get_block_number(&BlockHeaderHash(block_location.block_hash))?),
None => None,
};
let transaction_metadata = TransactionMetadata { block_number };
Ok(TransactionInfo {
txid: hex::encode(&transaction_id),
size: transaction_bytes.len(),
old_serial_numbers,
new_commitments,
memo,
network_id: transaction.network.id(),
digest: hex::encode(to_bytes![transaction.ledger_digest]?),
transaction_proof: hex::encode(to_bytes![transaction.transaction_proof]?),
program_commitment: hex::encode(to_bytes![transaction.program_commitment]?),
local_data_root: hex::encode(to_bytes![transaction.local_data_root]?),
value_balance: transaction.value_balance.0,
signatures,
encrypted_records,
transaction_metadata,
})
}
fn send_raw_transaction(&self, transaction_bytes: String) -> Result<String, RpcError> {
let transaction_bytes = hex::decode(transaction_bytes)?;
let transaction = Tx::read(&transaction_bytes[..])?;
let transaction_hex_id = hex::encode(transaction.transaction_id()?);
let storage = &self.storage;
storage.catch_up_secondary(false)?;
if !self.sync_handler()?.consensus.verify_transaction(&transaction)? {
return Ok("Transaction did not verify".into());
}
match !storage.transaction_conflicts(&transaction) {
true => {
let entry = Entry::<Tx> {
size_in_bytes: transaction_bytes.len(),
transaction,
};
if let Ok(inserted) = self.memory_pool()?.lock().insert(&storage, entry) {
if inserted.is_some() {
info!("Transaction added to the memory pool.");
}
}
Ok(transaction_hex_id)
}
false => Ok("Transaction contains spent records".into()),
}
}
fn validate_raw_transaction(&self, transaction_bytes: String) -> Result<bool, RpcError> {
let transaction_bytes = hex::decode(transaction_bytes)?;
let transaction = Tx::read(&transaction_bytes[..])?;
let storage = &self.storage;
storage.catch_up_secondary(false)?;
Ok(self.sync_handler()?.consensus.verify_transaction(&transaction)?)
}
fn get_connection_count(&self) -> Result<usize, RpcError> {
let number = self.node.peer_book.number_of_connected_peers();
Ok(number as usize)
}
fn get_peer_info(&self) -> Result<PeerInfo, RpcError> {
let peers = self.node.peer_book.connected_peers().keys().copied().collect();
Ok(PeerInfo { peers })
}
fn get_node_info(&self) -> Result<NodeInfo, RpcError> {
Ok(NodeInfo {
is_miner: self.sync_handler()?.is_miner(),
is_syncing: self.sync_handler()?.is_syncing_blocks(),
})
}
fn get_block_template(&self) -> Result<BlockTemplate, RpcError> {
let storage = &self.storage;
storage.catch_up_secondary(false)?;
let block_height = storage.get_current_block_height();
let block = storage.get_block_from_block_number(block_height)?;
let time = Utc::now().timestamp();
let full_transactions = self
.memory_pool()?
.lock()
.get_candidates(&storage, self.consensus_parameters()?.max_block_size)?;
let transaction_strings = full_transactions.serialize_as_str()?;
let mut coinbase_value = get_block_reward(block_height + 1);
for transaction in full_transactions.iter() {
coinbase_value = coinbase_value.add(transaction.value_balance())
}
Ok(BlockTemplate {
previous_block_hash: hex::encode(&block.header.get_hash().0),
block_height: block_height + 1,
time,
difficulty_target: self.consensus_parameters()?.get_block_difficulty(&block.header, time),
transactions: transaction_strings,
coinbase_value: coinbase_value.0 as u64,
})
}
}