Skip to content
This repository has been archived by the owner on May 6, 2020. It is now read-only.

Add hints into proofs to help validation #6

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 36 additions & 5 deletions src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use bitcoin::blockdata::script::Script;
use bitcoin::network::encodable::ConsensusDecodable;
use bitcoin::network::encodable::ConsensusEncodable;
use bitcoin::network::serialize;
use bitcoin::network::serialize::serialize;
use bitcoin::network::serialize::RawEncoder;
use bitcoin::network::serialize::SimpleDecoder;
use bitcoin::network::serialize::SimpleEncoder;
use bitcoin::Transaction;
Expand All @@ -20,12 +20,14 @@ use traits::NeededTx;

#[derive(Clone, Debug)]
pub struct Contract {
pub version: u16,
pub title: String,
pub issuance_utxo: OutPoint,
pub initial_owner_utxo: OutPoint,
pub burn_address: Address,
pub network: Network,
pub total_supply: u32,
pub tx_committing_to_this: Option<Sha256dHash>,
}

impl Contract {
Expand All @@ -35,8 +37,21 @@ impl Contract {
}

impl BitcoinHash for Contract {
fn bitcoin_hash(&self) -> Sha256dHash { // all the fields
Sha256dHash::from_data(&serialize(self).unwrap())
fn bitcoin_hash(&self) -> Sha256dHash {
// skip tx_committing_to_this, not relevant for consensus

let encoded: Vec<u8> = Vec::new();
let mut enc = RawEncoder::new(encoded);

self.version.consensus_encode(&mut enc).unwrap();
self.title.consensus_encode(&mut enc).unwrap();
self.issuance_utxo.consensus_encode(&mut enc).unwrap();
self.initial_owner_utxo.consensus_encode(&mut enc).unwrap();
self.burn_address.to_string().consensus_encode(&mut enc).unwrap();
self.network.consensus_encode(&mut enc).unwrap();
self.total_supply.consensus_encode(&mut enc).unwrap();

enc.into_inner().bitcoin_hash()
}
}

Expand All @@ -46,7 +61,7 @@ impl Verify for Contract {
}

fn verify(&self, needed_txs: &HashMap<&NeededTx, Transaction>) -> bool {
let committing_tx = needed_txs.get(&NeededTx::WhichSpendsOutPoint(self.issuance_utxo)).unwrap();
let committing_tx = self.get_tx_committing_to_self(needed_txs).unwrap();
let expected = self.get_expected_script();

// Check the outputs
Expand All @@ -71,33 +86,49 @@ impl Verify for Contract {

burn_script_builder.into_script()
}

fn get_tx_committing_to_self<'m>(&self, needed_txs: &'m HashMap<&NeededTx, Transaction>) -> Option<&'m Transaction> {
match self.tx_committing_to_this {
Some(txid) => needed_txs.get(&NeededTx::FromTXID(txid)), // either by using the hint in the contract
None => needed_txs.get(&NeededTx::WhichSpendsOutPoint(self.issuance_utxo)) // or get the tx which spends the issuance_utxo
}
}

fn set_tx_committing_to_self(&mut self, tx: &Transaction) {
self.tx_committing_to_this = Some(tx.txid());
}
}

impl<S: SimpleEncoder> ConsensusEncodable<S> for Contract {
fn consensus_encode(&self, s: &mut S) -> Result<(), serialize::Error> {
self.version.consensus_encode(s)?;
self.title.consensus_encode(s)?;
self.issuance_utxo.consensus_encode(s)?;
self.initial_owner_utxo.consensus_encode(s)?;
self.burn_address.to_string().consensus_encode(s)?;
self.network.consensus_encode(s)?;
self.total_supply.consensus_encode(s)
self.total_supply.consensus_encode(s)?;
self.tx_committing_to_this.consensus_encode(s)
}
}

impl<D: SimpleDecoder> ConsensusDecodable<D> for Contract {
fn consensus_decode(d: &mut D) -> Result<Contract, serialize::Error> {
let version: u16 = ConsensusDecodable::consensus_decode(d)?;
let title: String = ConsensusDecodable::consensus_decode(d)?;
let issuance_utxo: OutPoint = ConsensusDecodable::consensus_decode(d)?;
let initial_owner_utxo: OutPoint = ConsensusDecodable::consensus_decode(d)?;
let burn_address_str: String = ConsensusDecodable::consensus_decode(d)?;

Ok(Contract {
version,
title,
issuance_utxo,
initial_owner_utxo,
burn_address: Address::from_str(burn_address_str.as_str()).unwrap(),
network: ConsensusDecodable::consensus_decode(d)?,
total_supply: ConsensusDecodable::consensus_decode(d)?,
tx_committing_to_this: ConsensusDecodable::consensus_decode(d)?
})
}
}
48 changes: 37 additions & 11 deletions src/proof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,11 @@ impl OutputEntry {

#[derive(Clone, Debug)]
pub struct Proof {
pub version: u16,
pub bind_to: Vec<OutPoint>,
pub input: Vec<Proof>,
pub output: Vec<OutputEntry>,
pub tx_committing_to_this: Option<Sha256dHash>,
pub contract: Option<Box<Contract>>, // Only needed for root proofs
}

Expand All @@ -51,10 +53,12 @@ impl Proof {
let contract = if contract.is_some() { Some(Box::new(contract.unwrap().clone())) } else { None };

Proof {
version: 1,
bind_to,
input,
output,
contract,
tx_committing_to_this: None
}
}

Expand All @@ -63,9 +67,8 @@ impl Proof {
}

fn get_entries_for_us(&self, test_proof: &Proof, needed_txs: &HashMap<&NeededTx, Transaction>) -> Vec<OutputEntry> {
// We know that [0] is equal to all others (checked in verify)
let committing_tx_this = needed_txs.get(&NeededTx::WhichSpendsOutPoint(self.bind_to[0])).unwrap();
let committing_tx_test = needed_txs.get(&NeededTx::WhichSpendsOutPoint(test_proof.bind_to[0])).unwrap();
let committing_tx_this = self.get_tx_committing_to_self(needed_txs).unwrap();
let committing_tx_test = test_proof.get_tx_committing_to_self(needed_txs).unwrap();

let mut ans = Vec::new();

Expand Down Expand Up @@ -138,15 +141,21 @@ impl Verify for Proof {
fn verify(&self, needed_txs: &HashMap<&NeededTx, Transaction>) -> bool {
// Make sure that all the outpoints we are binding to are spent in the same tx

let committing_tx = needed_txs.get(&NeededTx::WhichSpendsOutPoint(self.bind_to[0])).unwrap(); // Take the first one
for out_point in &self.bind_to {
// And compare it to all the others
let this_committing_tx = needed_txs.get(&NeededTx::WhichSpendsOutPoint(out_point.clone())).unwrap();

if committing_tx.txid() != this_committing_tx.txid() {
println!("not all the outpoints in bind_to are spent in the same tx {:?}", committing_tx.txid());
return false;
// Take the tx committing to this
let committing_tx = self.get_tx_committing_to_self(&needed_txs).unwrap();
let all_spent = self.bind_to.iter().all(|&op| { // And check all the bind_to
for input in &committing_tx.input {
if input.previous_output == op {
return true;
}
}

false
});

if !all_spent {
println!("not all the outpoints in bind_to are spent in the same tx {:?}", committing_tx.txid());
return false;
}

// ---------------------------------
Expand Down Expand Up @@ -232,6 +241,17 @@ impl Verify for Proof {

burn_script_builder.into_script()
}

fn get_tx_committing_to_self<'m>(&self, needed_txs: &'m HashMap<&NeededTx, Transaction>) -> Option<&'m Transaction> {
match self.tx_committing_to_this {
Some(txid) => needed_txs.get(&NeededTx::FromTXID(txid)), // either by using the hint in the proof
None => needed_txs.get(&NeededTx::WhichSpendsOutPoint(self.bind_to[0])) // or get the tx which spends one of the bind_to
}
}

fn set_tx_committing_to_self(&mut self, tx: &Transaction) {
self.tx_committing_to_this = Some(tx.txid());
}
}

impl PartialEq for Proof {
Expand Down Expand Up @@ -265,19 +285,25 @@ impl<D: SimpleDecoder> ConsensusDecodable<D> for OutputEntry {

impl<S: SimpleEncoder> ConsensusEncodable<S> for Proof {
fn consensus_encode(&self, s: &mut S) -> Result<(), serialize::Error> {
self.version.consensus_encode(s)?;
self.bind_to.consensus_encode(s)?;
self.input.consensus_encode(s)?;
self.output.consensus_encode(s)?;
self.tx_committing_to_this.consensus_encode(s)?;
self.contract.consensus_encode(s)
}
}

impl<D: SimpleDecoder> ConsensusDecodable<D> for Proof {
fn consensus_decode(d: &mut D) -> Result<Proof, serialize::Error> {
let version: u16 = ConsensusDecodable::consensus_decode(d)?;

Ok(Proof {
version,
bind_to: ConsensusDecodable::consensus_decode(d)?,
input: ConsensusDecodable::consensus_decode(d)?,
output: ConsensusDecodable::consensus_decode(d)?,
tx_committing_to_this: ConsensusDecodable::consensus_decode(d)?,
contract: ConsensusDecodable::consensus_decode(d)?,
})
}
Expand Down
2 changes: 2 additions & 0 deletions src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ pub trait Verify {
fn get_needed_txs(&self) -> Vec<NeededTx>;
fn verify(&self, needed_txs: &HashMap<&NeededTx, Transaction>) -> bool;
fn get_expected_script(&self) -> Script;
fn get_tx_committing_to_self<'m>(&self, needed_txs: &'m HashMap<&NeededTx, Transaction>) -> Option<&'m Transaction>;
fn set_tx_committing_to_self(&mut self, tx: &Transaction);
}