mod error;
use casper_types::{
bytesrepr::ToBytes, Deploy, DeployHash, DeployHeader, Digest, ExecutableDeployItem,
InitiatorAddr, PublicKey, SecretKey, TimeDiff, Timestamp, TransferTarget, URef, U512,
};
pub use error::DeployBuilderError;
use itertools::Itertools;
use crate::types::InitiatorAddrAndSecretKey;
pub struct DeployBuilder<'a> {
account: Option<PublicKey>,
secret_key: Option<&'a SecretKey>,
timestamp: Timestamp,
ttl: TimeDiff,
gas_price: u64,
dependencies: Vec<DeployHash>,
chain_name: String,
payment: Option<ExecutableDeployItem>,
session: ExecutableDeployItem,
}
impl<'a> DeployBuilder<'a> {
pub const DEFAULT_TTL: TimeDiff = TimeDiff::from_millis(30 * 60 * 1_000);
pub const DEFAULT_GAS_PRICE: u64 = 1;
pub fn new<C: Into<String>>(chain_name: C, session: ExecutableDeployItem) -> Self {
#[cfg(any(feature = "std-fs-io", test))]
let timestamp = Timestamp::now();
#[cfg(not(any(feature = "std-fs-io", test)))]
let timestamp = Timestamp::zero();
DeployBuilder {
account: None,
secret_key: None,
timestamp,
ttl: Self::DEFAULT_TTL,
gas_price: Self::DEFAULT_GAS_PRICE,
dependencies: vec![],
chain_name: chain_name.into(),
payment: None,
session,
}
}
pub fn new_transfer<C: Into<String>, A: Into<U512>, T: Into<TransferTarget>>(
chain_name: C,
amount: A,
maybe_source: Option<URef>,
target: T,
maybe_transfer_id: Option<u64>,
) -> Self {
let session =
ExecutableDeployItem::new_transfer(amount, maybe_source, target, maybe_transfer_id);
DeployBuilder::new(chain_name, session)
}
pub fn with_account(mut self, account: PublicKey) -> Self {
self.account = Some(account);
self
}
pub fn with_gas_price(mut self, gas_price: u64) -> Self {
self.gas_price = gas_price;
self
}
pub fn with_secret_key(mut self, secret_key: &'a SecretKey) -> Self {
self.secret_key = Some(secret_key);
self
}
pub fn with_payment(mut self, payment: ExecutableDeployItem) -> Self {
self.payment = Some(payment);
self
}
pub fn with_timestamp(mut self, timestamp: Timestamp) -> Self {
self.timestamp = timestamp;
self
}
pub fn with_ttl(mut self, ttl: TimeDiff) -> Self {
self.ttl = ttl;
self
}
#[allow(clippy::too_many_arguments)]
fn build_deploy_inner(
timestamp: Timestamp,
ttl: TimeDiff,
gas_price: u64,
dependencies: Vec<DeployHash>,
chain_name: String,
payment: ExecutableDeployItem,
session: ExecutableDeployItem,
initiator_addr_and_secret_key: InitiatorAddrAndSecretKey,
) -> Deploy {
let serialized_body = serialize_body(&payment, &session);
let body_hash = Digest::hash(serialized_body);
let account = match initiator_addr_and_secret_key.initiator_addr() {
InitiatorAddr::PublicKey(public_key) => public_key,
InitiatorAddr::AccountHash(_) => unreachable!(),
};
let dependencies = dependencies.into_iter().unique().collect();
let header = DeployHeader::new(
account,
timestamp,
ttl,
gas_price,
body_hash,
dependencies,
chain_name,
);
let serialized_header = serialize_header(&header);
let hash = DeployHash::new(Digest::hash(serialized_header));
let mut deploy = Deploy::new(hash, header, payment, session);
if let Some(secret_key) = initiator_addr_and_secret_key.secret_key() {
deploy.sign(secret_key);
}
deploy
}
pub fn build(self) -> Result<Deploy, DeployBuilderError> {
let initiator_addr_and_secret_key = match (self.account, self.secret_key) {
(Some(account), Some(secret_key)) => InitiatorAddrAndSecretKey::Both {
initiator_addr: InitiatorAddr::PublicKey(account),
secret_key,
},
(Some(account), None) => {
InitiatorAddrAndSecretKey::InitiatorAddr(InitiatorAddr::PublicKey(account))
}
(None, Some(secret_key)) => InitiatorAddrAndSecretKey::SecretKey(secret_key),
(None, None) => return Err(DeployBuilderError::DeployMissingSessionAccount),
};
let payment = self
.payment
.ok_or(DeployBuilderError::DeployMissingPaymentCode)?;
let deploy = Self::build_deploy_inner(
self.timestamp,
self.ttl,
self.gas_price,
self.dependencies,
self.chain_name,
payment,
self.session,
initiator_addr_and_secret_key,
);
Ok(deploy)
}
}
fn serialize_header(header: &DeployHeader) -> Vec<u8> {
header
.to_bytes()
.unwrap_or_else(|error| panic!("should serialize deploy header: {}", error))
}
fn serialize_body(payment: &ExecutableDeployItem, session: &ExecutableDeployItem) -> Vec<u8> {
let mut buffer = Vec::with_capacity(payment.serialized_length() + session.serialized_length());
payment
.write_bytes(&mut buffer)
.unwrap_or_else(|error| panic!("should serialize payment code: {}", error));
session
.write_bytes(&mut buffer)
.unwrap_or_else(|error| panic!("should serialize session code: {}", error));
buffer
}