use super::{
wallet::{Wallet, WalletSnapshot},
Outcome, TernaryResult,
};
use crate::{Error, Result};
use log::debug;
#[cfg(feature = "simulated-payouts")]
use sn_data_types::Credit;
use sn_data_types::{
CreditAgreementProof, Debit, OwnerType, PublicKey, ReplicaEvent, Signature, SignedCredit,
SignedDebit, SignedTransfer, SignedTransferShare, Token, TransferAgreementProof,
TransferRegistered, TransferValidationProposed,
};
use std::collections::{BTreeMap, HashMap};
use std::fmt;
use threshold_crypto::{PublicKeySet, PublicKeyShare};
macro_rules! hashmap {
($( $key: expr => $val: expr ),*) => {{
let mut map = ::std::collections::HashMap::new();
$( let _ = map.insert($key, $val); )*
map
}}
}
#[derive(Clone, PartialEq, Eq)]
pub struct WalletReplica {
id: OwnerType,
replica_id: PublicKeyShare,
key_index: usize,
peer_replicas: PublicKeySet,
wallet: Wallet,
pending_proposals: HashMap<u64, HashMap<usize, TransferValidationProposed>>,
pending_debit: Option<u64>,
}
impl WalletReplica {
pub fn from_history(
id: OwnerType,
replica_id: PublicKeyShare,
key_index: usize,
peer_replicas: PublicKeySet,
events: Vec<ReplicaEvent>,
) -> Result<Self> {
let mut instance = Self::from_snapshot(
id.clone(),
replica_id,
key_index,
peer_replicas,
Wallet::new(id),
Default::default(),
None,
);
for e in events {
instance.apply(e)?;
}
Ok(instance)
}
pub fn from_snapshot(
id: OwnerType,
replica_id: PublicKeyShare,
key_index: usize,
peer_replicas: PublicKeySet,
wallet: Wallet,
pending_proposals: HashMap<u64, HashMap<usize, TransferValidationProposed>>,
pending_debit: Option<u64>,
) -> Self {
Self {
id,
replica_id,
key_index,
peer_replicas,
wallet,
pending_proposals,
pending_debit,
}
}
pub fn balance(&self) -> Token {
self.wallet.balance()
}
pub fn wallet(&self) -> Option<WalletSnapshot> {
let wallet = self.wallet.to_owned();
Some(wallet.into())
}
pub fn genesis<F: FnOnce() -> Result<PublicKey>>(
&self,
credit_proof: &CreditAgreementProof,
past_key: F,
) -> Outcome<()> {
if self.balance() != Token::zero() || self.pending_debit.is_some() {
return Err(Error::InvalidOperation);
}
self.receive_propagated(credit_proof, past_key)
}
pub fn test_validate_transfer(
&self,
signed_debit: &SignedDebit,
signed_credit: &SignedCredit,
) -> Outcome<()> {
if signed_debit.sender() == signed_credit.recipient() {
Err(Error::SameSenderAndRecipient)
} else if signed_credit.id() != &signed_debit.credit_id()? {
Err(Error::CreditDebitIdMismatch)
} else if signed_credit.amount() != signed_debit.amount() {
Err(Error::CreditDebitValueMismatch)
} else {
Outcome::success(())
}
}
pub fn validate(
&self,
signed_debit: &SignedDebit,
signed_credit: &SignedCredit,
) -> Outcome<()> {
let debit = &signed_debit.debit;
let credit = &signed_credit.credit;
if self
.verify_actor_signature(&signed_debit, &signed_credit)
.is_err()
{
return Outcome::rejected(Error::InvalidSignature);
} else if debit.sender() == credit.recipient() {
return Outcome::rejected(Error::SameSenderAndRecipient);
} else if credit.id() != &debit.credit_id()? {
return Outcome::rejected(Error::CreditDebitIdMismatch);
} else if credit.amount() != debit.amount() {
return Outcome::rejected(Error::CreditDebitValueMismatch);
} else if debit.amount() == Token::zero() {
return Outcome::rejected(Error::ZeroValueTransfer);
} else if self.wallet.id().public_key() != debit.sender() {
return Outcome::rejected(Error::NoSuchSender);
} else if self.pending_debit.is_none() && debit.id.counter != 0 {
return Outcome::rejected(Error::ShouldBeInitialOperation);
} else if let Some(counter) = self.pending_debit {
if debit.id.counter != (counter + 1) {
return Outcome::rejected(Error::OperationOutOfOrder(debit.id.counter, counter));
}
} else if debit.amount() > self.balance() {
return Outcome::rejected(Error::InsufficientBalance);
}
Outcome::success(())
}
pub fn register<F: FnOnce() -> Result<PublicKey>>(
&self,
transfer_proof: &TransferAgreementProof,
past_key: F,
) -> Outcome<TransferRegistered> {
debug!("Checking registered transfer");
if self
.verify_registered_proof(transfer_proof, past_key)
.is_err()
{
return Err(Error::InvalidSignature);
}
let debit = &transfer_proof.signed_debit.debit;
if self.wallet.next_debit() == debit.id().counter {
Outcome::success(TransferRegistered {
transfer_proof: transfer_proof.clone(),
})
} else {
Outcome::rejected(Error::InvalidOperation)
}
}
pub fn receive_propagated<F: FnOnce() -> Result<PublicKey>>(
&self,
credit_proof: &CreditAgreementProof,
past_key: F,
) -> Outcome<()> {
self.verify_propagated_proof(credit_proof, past_key)?;
if self.wallet.contains(&credit_proof.id()) {
Outcome::no_change()
} else {
Outcome::success(())
}
}
pub fn apply(&mut self, event: ReplicaEvent) -> Result<()> {
match event {
ReplicaEvent::TransferValidationProposed(e) => {
let debit = &e.signed_debit.debit;
let index = e.signed_debit.actor_signature.index;
if let Some(pending) = self.pending_proposals.get_mut(&debit.id.counter) {
let _ = pending.insert(index, e);
} else {
let _ = self
.pending_proposals
.insert(debit.id.counter, hashmap!(index => e));
};
Ok(())
}
ReplicaEvent::TransferValidated(e) => {
let debit = e.signed_debit.debit;
self.pending_debit = Some(debit.id.counter);
Ok(())
}
ReplicaEvent::TransferRegistered(e) => {
let debit = e.transfer_proof.signed_debit.debit;
self.wallet.apply_debit(Debit {
id: debit.id(),
amount: debit.amount(),
})
}
ReplicaEvent::TransferPropagated(e) => {
let credit = e.credit_proof.signed_credit.credit;
self.wallet.apply_credit(credit)
}
}
}
#[cfg(feature = "simulated-payouts")]
pub fn credit_without_proof(&mut self, credit: Credit) -> Result<()> {
self.wallet.simulated_credit(credit)
}
#[cfg(feature = "simulated-payouts")]
pub fn debit_without_proof(&mut self, debit: Debit) -> Result<()> {
self.wallet.simulated_debit(debit)
}
fn verify_actor_signature(
&self,
signed_debit: &SignedDebit,
signed_credit: &SignedCredit,
) -> Result<()> {
println!("Actor signature verification");
let debit = &signed_debit.debit;
let credit = &signed_credit.credit;
let debit_bytes = match bincode::serialize(&debit) {
Err(_) => return Err(Error::Serialisation("Could not serialise debit".into())),
Ok(bytes) => bytes,
};
let credit_bytes = match bincode::serialize(&credit) {
Err(_) => return Err(Error::Serialisation("Could not serialise credit".into())),
Ok(bytes) => bytes,
};
let valid_debit = signed_debit
.sender()
.verify(&signed_debit.actor_signature, debit_bytes)
.is_ok();
println!("Debit is valid?: {:?}", valid_debit);
let valid_credit = signed_debit
.sender()
.verify(&signed_credit.actor_signature, credit_bytes)
.is_ok();
println!("Credit is valid?: {:?}", valid_debit);
if valid_debit && valid_credit && credit.id() == &debit.credit_id()? {
Ok(())
} else {
Err(Error::InvalidSignature)
}
}
fn verify_registered_proof<F: FnOnce() -> Result<PublicKey>>(
&self,
proof: &TransferAgreementProof,
past_key: F,
) -> Result<()> {
if proof.signed_credit.id() != &proof.signed_debit.credit_id()? {
return Err(Error::CreditDebitValueMismatch);
}
let debit_bytes = match bincode::serialize(&proof.signed_debit) {
Ok(bytes) => bytes,
Err(_) => return Err(Error::Serialisation("Could not serialise transfer".into())),
};
let credit_bytes = match bincode::serialize(&proof.signed_credit) {
Ok(bytes) => bytes,
Err(_) => return Err(Error::Serialisation("Could not serialise transfer".into())),
};
let public_key = sn_data_types::PublicKey::Bls(self.peer_replicas.public_key());
let valid_debit = public_key.verify(&proof.debit_sig, &debit_bytes).is_ok();
let valid_credit = public_key.verify(&proof.credit_sig, &credit_bytes).is_ok();
if valid_debit && valid_credit {
return Ok(());
}
let public_key = past_key()?;
let valid_debit = public_key.verify(&proof.debit_sig, &debit_bytes).is_ok();
let valid_credit = public_key.verify(&proof.credit_sig, &credit_bytes).is_ok();
if valid_debit && valid_credit {
return Ok(());
}
Err(Error::InvalidSignature)
}
fn verify_propagated_proof<F: FnOnce() -> Result<PublicKey>>(
&self,
proof: &CreditAgreementProof,
past_key: F,
) -> Result<()> {
match bincode::serialize(&proof.signed_credit) {
Err(_) => Err(Error::Serialisation("Could not serialise transfer".into())),
Ok(credit_bytes) => {
let our_key = sn_data_types::PublicKey::Bls(self.peer_replicas.public_key());
if our_key
.verify(&proof.debiting_replicas_sig, &credit_bytes)
.is_ok()
{
return Ok(());
}
let public_key = past_key()?;
let valid_credit = public_key
.verify(&proof.debiting_replicas_sig, &credit_bytes)
.is_ok();
if valid_credit {
return Ok(());
}
Err(Error::InvalidSignature)
}
}
}
}
impl WalletReplica {
pub fn propose_validation(
&self,
signed_transfer: &SignedTransferShare,
) -> Outcome<TransferValidationProposed> {
let signed_debit = signed_transfer.debit();
let signed_credit = signed_transfer.credit();
let debit = &signed_debit.debit;
let credit = &signed_credit.credit;
if let Err(e) = self.verify_actor_signature_share(signed_transfer) {
println!("Failed verification of actor sig!");
return Outcome::rejected(e);
} else if debit.sender() == credit.recipient() {
return Outcome::rejected(Error::SameSenderAndRecipient);
} else if credit.id() != &debit.credit_id()? {
return Outcome::rejected(Error::CreditDebitIdMismatch);
} else if credit.amount() != debit.amount() {
return Outcome::rejected(Error::CreditDebitValueMismatch);
} else if debit.amount() == Token::zero() {
return Outcome::rejected(Error::ZeroValueTransfer);
} else if self.id.public_key() != debit.sender() {
return Outcome::rejected(Error::NoSuchSender);
} else if self.pending_debit.is_none() && debit.id.counter != 0 {
return Outcome::rejected(Error::ShouldBeInitialOperation);
} else if let Some(counter) = self.pending_debit {
if debit.id.counter != (counter + 1) {
return Outcome::rejected(Error::OperationOutOfOrder(debit.id.counter, counter));
}
}
debug!("Correct proposal.");
debug!("Accumulating transfer validation proposal..");
self.accumulate(TransferValidationProposed {
signed_credit: signed_credit.to_owned(),
signed_debit: signed_debit.to_owned(),
agreed_transfer: None,
})
}
fn accumulate(
&self,
proposal: TransferValidationProposed,
) -> Outcome<TransferValidationProposed> {
let actors = match &self.id {
OwnerType::Multi(actors) => actors,
OwnerType::Single(_) => return Outcome::rejected(Error::InvalidOwner),
};
let signed_debit = &proposal.signed_debit;
let signed_credit = &proposal.signed_credit;
let share_index = signed_debit.share_index();
let id = signed_debit.id();
let debit_counter = id.counter;
if let Some(map) = self.pending_proposals.get(&debit_counter) {
if map.contains_key(&share_index) {
return Outcome::no_change();
}
}
let map = HashMap::new();
let map = self.pending_proposals.get(&debit_counter).unwrap_or(&map);
let majority = map.len() + 1 > actors.threshold() && self.id.public_key() == id.actor;
if !majority {
debug!("No majority reached yet for proposal.");
return Outcome::success(proposal);
}
let debit_bytes = match bincode::serialize(&signed_debit.debit) {
Err(_) => {
return Err(Error::Serialisation(
"Could not serialise debit".to_string(),
))
}
Ok(data) => data,
};
let credit_bytes = match bincode::serialize(&signed_credit.credit) {
Err(_) => {
return Err(Error::Serialisation(
"Could not serialise credit".to_string(),
))
}
Ok(data) => data,
};
let debit_sig_shares: BTreeMap<_, _> = map
.values()
.chain(vec![&proposal])
.map(|v| v.signed_debit.actor_signature.clone())
.map(|s| (s.index, s.share))
.collect();
let credit_sig_shares: BTreeMap<_, _> = map
.values()
.chain(vec![&proposal])
.map(|v| v.signed_credit.actor_signature.clone())
.map(|s| (s.index, s.share))
.collect();
let debit_sig = actors
.combine_signatures(&debit_sig_shares)
.map_err(|_| Error::CannotAggregate)?;
let credit_sig = actors
.combine_signatures(&credit_sig_shares)
.map_err(|_| Error::CannotAggregate)?;
let valid_debit = actors.public_key().verify(&debit_sig, debit_bytes);
let valid_credit = actors.public_key().verify(&credit_sig, credit_bytes);
if valid_debit && valid_credit {
let mut proposal = proposal.clone();
proposal.agreed_transfer = Some(SignedTransfer {
debit: SignedDebit {
debit: signed_debit.debit.clone(),
actor_signature: Signature::Bls(debit_sig),
},
credit: SignedCredit {
credit: signed_credit.credit.clone(),
actor_signature: Signature::Bls(credit_sig),
},
});
Outcome::success(proposal)
} else {
Err(Error::UnexpectedOutcome)
}
}
fn verify_actor_signature_share(
&self,
signed_transfer_share: &SignedTransferShare,
) -> Result<()> {
debug!("Actor signature share verification");
let signed_debit = signed_transfer_share.debit();
let signed_credit = signed_transfer_share.credit();
let debit = &signed_debit.debit;
let credit = &signed_credit.credit;
let debit_bytes = match bincode::serialize(debit) {
Err(_) => return Err(Error::Serialisation("Could not serialise debit".into())),
Ok(bytes) => bytes,
};
let credit_bytes = match bincode::serialize(credit) {
Err(_) => return Err(Error::Serialisation("Could not serialise credit".into())),
Ok(bytes) => bytes,
};
let key_share = signed_transfer_share
.actors()
.public_key_share(signed_debit.actor_signature.index);
let valid_debit = key_share.verify(&signed_debit.actor_signature.share, &debit_bytes);
let valid_credit = key_share.verify(&signed_credit.actor_signature.share, &credit_bytes);
debug!("Debit is valid?: {:?}", valid_debit);
debug!("Credit is valid?: {:?}", valid_debit);
if credit.id() != &debit.credit_id()? {
return Err(Error::CreditDebitIdMismatch);
}
if valid_debit && valid_credit {
Ok(())
} else {
Err(Error::Unknown(format!(
"InvalidSignature! valid_debit: {}, valid_credit: {}",
valid_debit, valid_credit
)))
}
}
}
impl fmt::Debug for WalletReplica {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"WalletReplica {{ id: {:?}, replica_id: {:?}, key_index: {:?}, peer_replicas: PkSet {{ public_key: {:?} }}, wallet: {:?}, pending_proposals: {:?}, pending_debit: {:?} }}",
self.id,
self.replica_id,
self.key_index,
self.peer_replicas.public_key(),
self.wallet,
self.pending_proposals,
self.pending_debit
)
}
}