From f4e91e53324aca5ac2bcbec904893b93b82f2953 Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Sun, 7 Oct 2018 14:11:04 +0900 Subject: [PATCH 1/3] Add optional hints to txs committing to proofs/contracts --- src/contract.rs | 37 ++++++++++++++++++++++++++++++++----- src/proof.rs | 39 +++++++++++++++++++++++++++++++-------- src/traits.rs | 1 + 3 files changed, 64 insertions(+), 13 deletions(-) diff --git a/src/contract.rs b/src/contract.rs index a6cf277..817bb0a 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -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; @@ -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, } impl Contract { @@ -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 = 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() } } @@ -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 @@ -71,33 +86,45 @@ 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 + } + } } impl ConsensusEncodable 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 ConsensusDecodable for Contract { fn consensus_decode(d: &mut D) -> Result { + 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)? }) } } \ No newline at end of file diff --git a/src/proof.rs b/src/proof.rs index e481264..139401e 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -40,9 +40,11 @@ impl OutputEntry { #[derive(Clone, Debug)] pub struct Proof { + pub version: u16, pub bind_to: Vec, pub input: Vec, pub output: Vec, + pub tx_committing_to_this: Option, pub contract: Option>, // Only needed for root proofs } @@ -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 } } @@ -138,15 +142,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; } // --------------------------------- @@ -232,6 +242,13 @@ 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 + } + } } impl PartialEq for Proof { @@ -265,19 +282,25 @@ impl ConsensusDecodable for OutputEntry { impl ConsensusEncodable 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 ConsensusDecodable for Proof { fn consensus_decode(d: &mut D) -> Result { + 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)?, }) } diff --git a/src/traits.rs b/src/traits.rs index 2145b3a..2c11767 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -14,4 +14,5 @@ pub trait Verify { fn get_needed_txs(&self) -> Vec; 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>; } \ No newline at end of file From b889f981e715c0ea070d430b5cf956f4482500ef Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Sun, 7 Oct 2018 14:37:57 +0900 Subject: [PATCH 2/3] Use hints to verify proofs --- src/proof.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/proof.rs b/src/proof.rs index 139401e..e789f09 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -67,9 +67,8 @@ impl Proof { } fn get_entries_for_us(&self, test_proof: &Proof, needed_txs: &HashMap<&NeededTx, Transaction>) -> Vec { - // 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(); From 1344b320891ba0fafc5d8f385cb9c9071ea72bc1 Mon Sep 17 00:00:00 2001 From: Alekos Filini Date: Sun, 7 Oct 2018 21:29:32 +0900 Subject: [PATCH 3/3] Add setters for validation hints --- src/contract.rs | 4 ++++ src/proof.rs | 4 ++++ src/traits.rs | 1 + 3 files changed, 9 insertions(+) diff --git a/src/contract.rs b/src/contract.rs index 817bb0a..b6a2388 100644 --- a/src/contract.rs +++ b/src/contract.rs @@ -93,6 +93,10 @@ impl Verify for 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 ConsensusEncodable for Contract { diff --git a/src/proof.rs b/src/proof.rs index e789f09..aa859a6 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -248,6 +248,10 @@ impl Verify for 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 { diff --git a/src/traits.rs b/src/traits.rs index 2c11767..bbb02f3 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -15,4 +15,5 @@ pub trait Verify { 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); } \ No newline at end of file