Skip to content
Permalink
Browse files

Add utxo/account handling

  • Loading branch information...
vincenthz committed Mar 14, 2019
1 parent 035e185 commit 3ad0e5bd48a857f46423c13e4679dbe5ecc2878b
Showing with 208 additions and 16 deletions.
  1. +180 −5 chain-impl-mockchain/src/ledger.rs
  2. +28 −11 chain-impl-mockchain/src/state.rs
@@ -1,14 +1,11 @@
//! Mockchain ledger. Ledger exists in order to update the
//! current state and verify transactions.

use crate::error::*;
use crate::transaction::*;
use crate::value::*;
use crate::{account, block, leadership, setting, utxo};
use crate::{account, utxo};
use cardano::address::Addr as OldAddress;
use chain_addr::Address;
use chain_core::property;
use std::collections::HashMap;
use chain_addr::{Address, Kind};

/// Overall ledger structure.
///
@@ -24,6 +21,34 @@ pub struct Ledger {
pub(crate) accounts: account::Ledger,
}

#[derive(Debug, Clone)]
pub enum Error {
NotEnoughSignatures(usize, usize),
UtxoValueNotMatching(Value, Value),
UtxoError(utxo::Error),
UtxoInvalidSignature(UtxoPointer, Output<Address>, Witness),
AccountInvalidSignature(account::Identifier, Witness),
UtxoInputsTotal(ValueError),
UtxoOutputsTotal(ValueError),
Account(account::LedgerError),
NotBalanced(Value, Value),
ZeroOutput(Output<Address>),
ExpectingAccountWitness,
ExpectingUtxoWitness,
}

impl From<utxo::Error> for Error {
fn from(e: utxo::Error) -> Self {
Error::UtxoError(e)
}
}

impl From<account::LedgerError> for Error {
fn from(e: account::LedgerError) -> Self {
Error::Account(e)
}
}

impl Ledger {
pub fn new() -> Self {
Ledger {
@@ -32,6 +57,156 @@ impl Ledger {
accounts: account::Ledger::new(),
}
}

pub fn apply_transaction(
&mut self,
signed_tx: &SignedTransaction<Address>,
) -> Result<Self, Error> {
let mut ledger = self.clone();
let transaction_id = signed_tx.transaction.hash();
ledger = internal_apply_transaction(
ledger,
&transaction_id,
&signed_tx.transaction.inputs[..],
&signed_tx.transaction.outputs[..],
&signed_tx.witnesses[..],
)?;
Ok(ledger)
}
}

/// Apply the transaction
fn internal_apply_transaction(
mut ledger: Ledger,
transaction_id: &TransactionId,
inputs: &[Input],
outputs: &[Output<Address>],
witnesses: &[Witness],
) -> Result<Ledger, Error> {
assert!(inputs.len() < 255);
assert!(outputs.len() < 255);
assert!(witnesses.len() < 255);

// 1. verify that number of signatures matches number of
// transactions
if inputs.len() != witnesses.len() {
return Err(Error::NotEnoughSignatures(inputs.len(), witnesses.len()));
}

// 2. validate inputs of transaction by gathering what we know of it,
// then verifying the associated witness
for (input, witness) in inputs.iter().zip(witnesses.iter()) {
match input.to_enum() {
InputEnum::UtxoInput(utxo) => {
ledger = input_utxo_verify(ledger, transaction_id, &utxo, witness)?
}
InputEnum::AccountInput(account_id, value) => {
ledger.accounts = input_account_verify(
ledger.accounts,
transaction_id,
&account_id,
value,
witness,
)?
}
}
}

// 3. verify that transaction sum is zero.
// TODO: with fees this will change
let total_input =
Value::sum(inputs.iter().map(|i| i.value)).map_err(|e| Error::UtxoInputsTotal(e))?;
let total_output =
Value::sum(inputs.iter().map(|i| i.value)).map_err(|e| Error::UtxoOutputsTotal(e))?;
if total_input != total_output {
return Err(Error::NotBalanced(total_input, total_output));
}

// 4. add the new outputs
let mut new_utxos = Vec::new();
for (index, output) in outputs.iter().enumerate() {
// Reject zero-valued outputs.
if output.value == Value::zero() {
return Err(Error::ZeroOutput(output.clone()));
}
match output.address.kind() {
Kind::Single(_) | Kind::Group(_, _) => {
new_utxos.push((index as u8, output.clone()));
}
Kind::Account(identifier) => {
// don't have a way to make a newtype ref from the ref so .clone()
let account = identifier.clone().into();
ledger.accounts = ledger.accounts.add_value(&account, output.value)?;
}
}
}

ledger.utxos = ledger.utxos.add(transaction_id, &new_utxos)?;

Ok(ledger)
}

fn input_utxo_verify(
mut ledger: Ledger,
transaction_id: &TransactionId,
utxo: &UtxoPointer,
witness: &Witness,
) -> Result<Ledger, Error> {
match witness {
Witness::Account(_) => return Err(Error::ExpectingUtxoWitness),
Witness::Utxo(signature) => {
let (new_utxos, associated_output) = ledger
.utxos
.remove(&utxo.transaction_id, utxo.output_index)?;
ledger.utxos = new_utxos;
if utxo.value != associated_output.value {
return Err(Error::UtxoValueNotMatching(
utxo.value,
associated_output.value,
));
}

let verified = signature.verify(
&associated_output.address.public_key().unwrap(),
&transaction_id,
);
if verified == chain_crypto::Verification::Failed {
return Err(Error::UtxoInvalidSignature(
utxo.clone(),
associated_output.clone(),
witness.clone(),
));
};
Ok(ledger)
}
}
}

fn input_account_verify(
mut ledger: account::Ledger,
transaction_id: &TransactionId,
account: &account::Identifier,
value: Value,
witness: &Witness,
) -> Result<account::Ledger, Error> {
// check if there's enough value
let (new_ledger, spending_counter) = ledger.remove_value(account, value)?;
ledger = new_ledger;

match witness {
Witness::Utxo(_) => return Err(Error::ExpectingAccountWitness),
Witness::Account(sig) => {
let tidsc = TransactionIdSpendingCounter::new(transaction_id, &spending_counter);
let verified = sig.verify(&account.clone().into(), &tidsc);
if verified == chain_crypto::Verification::Failed {
return Err(Error::AccountInvalidSignature(
account.clone(),
witness.clone(),
));
};
Ok(ledger)
}
}
}

/*
@@ -3,14 +3,17 @@

use crate::block::{BlockContents, Message};
use crate::ledger::Ledger;
use crate::{account, block, leadership, setting, utxo};
use crate::{block, leadership, ledger, setting};
use chain_core::property;
use std::sync::Arc;

pub(crate) type Leadership = Box<
dyn property::LeaderSelection<
Block = block::Block,
Error = leadership::Error,
LeaderId = leadership::PublicLeader,
pub(crate) type Leadership = Arc<
Box<
dyn property::LeaderSelection<
Block = block::Block,
Error = leadership::Error,
LeaderId = leadership::PublicLeader,
>,
>,
>;

@@ -20,20 +23,34 @@ pub struct State {
pub(crate) leadership: Leadership,
}

type Error = ();
pub enum Error {
LedgerError(ledger::Error),
}
impl From<ledger::Error> for Error {
fn from(e: ledger::Error) -> Self {
Error::LedgerError(e)
}
}

impl State {
pub fn apply(&self, contents: BlockContents) -> Result<State, Error> {
// for now we just clone ledger, since leadership is still inside the state.
let mut newst = self.ledger.clone();
let mut new_ledger = self.ledger.clone();
for content in contents.iter() {
match content {
Message::Transaction(signed_transaction) => {}
Message::Transaction(signed_transaction) => {
let ledger = new_ledger.apply_transaction(signed_transaction)?;
new_ledger = ledger;
}
Message::Update(update_proposal) => {}
_ => {}
}
}
unimplemented!();
Ok(State {
ledger: new_ledger,
settings: self.settings.clone(),
leadership: self.leadership.clone(),
})
}
}

@@ -42,7 +59,7 @@ impl State {
State {
ledger: Ledger::new(),
settings: setting::Settings::new(),
leadership: Box::new(leadership::none::NoLeadership),
leadership: Arc::new(Box::new(leadership::none::NoLeadership)),
}
}
}

0 comments on commit 3ad0e5b

Please sign in to comment.
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.