integration with bitcoinconsenus

This commit is contained in:
Tamas Blummer 2018-03-10 15:35:49 +01:00
parent cdeff991c5
commit 755fb454eb
5 changed files with 117 additions and 7 deletions

View File

@ -15,6 +15,9 @@ readme = "README.md"
name = "bitcoin" name = "bitcoin"
path = "src/lib.rs" path = "src/lib.rs"
[features]
bitcoinconsenus = ["bitcoinconsensus"]
[dependencies] [dependencies]
byteorder = "1.1" byteorder = "1.1"
rand = "0.3" rand = "0.3"
@ -22,8 +25,10 @@ rust-crypto = "0.2"
rustc-serialize = "0.3" rustc-serialize = "0.3"
serde = "0.6" serde = "0.6"
strason = "0.3" strason = "0.3"
bitcoinconsensus = { version = "0.16", optional=true }
[dependencies.secp256k1] [dependencies.secp256k1]
version = "0.8" version = "0.8"
features = [ "rand", "serde" ] features = [ "rand", "serde" ]

View File

@ -13,7 +13,6 @@ Supports (or should support)
* De/serialization of Bitcoin protocol network messages * De/serialization of Bitcoin protocol network messages
* De/serialization of blocks and transactions * De/serialization of blocks and transactions
* Script de/serialization * Script de/serialization
* Blockchain validation
* Private keys and address creation, de/serialization and validation (including full BIP32 support) * Private keys and address creation, de/serialization and validation (including full BIP32 support)
* Pay-to-contract support as in Appendix A of the [Blockstream sidechains whitepaper](https://www.blockstream.com/sidechains.pdf) * Pay-to-contract support as in Appendix A of the [Blockstream sidechains whitepaper](https://www.blockstream.com/sidechains.pdf)
@ -27,7 +26,7 @@ To use rust-bitcoin, just add the following to your Cargo.toml.
```toml ```toml
[dependencies] [dependencies]
bitcoin = "0.10" bitcoin = "0.12"
``` ```
# Known limitations # Known limitations
@ -35,17 +34,13 @@ bitcoin = "0.10"
## Consensus ## Consensus
This library **must not** be used for consensus code (i.e. fully validating This library **must not** be used for consensus code (i.e. fully validating
blockchain data). It technically supports doing this, using the feature-gated blockchain data). It technically supports doing this, but doing so is very
script parser, but doing so is very
ill-advised because there are many deviations, known and unknown, between ill-advised because there are many deviations, known and unknown, between
this library and the Bitcoin Core reference implementation. In a consensus this library and the Bitcoin Core reference implementation. In a consensus
based cryptocurrency such as Bitcoin it is critical that all parties are based cryptocurrency such as Bitcoin it is critical that all parties are
using the same rules to validate data, and this library is simply unable using the same rules to validate data, and this library is simply unable
to implement the same rules as Core. to implement the same rules as Core.
The script interpreter is now gated behind the `broken_consensus_code` flag
for this reason.
Given the complexity of both C++ and Rust, it is unlikely that this will Given the complexity of both C++ and Rust, it is unlikely that this will
ever be fixed, and there are no plans to do so. Of course, patches to ever be fixed, and there are no plans to do so. Of course, patches to
fix specific consensus incompatibilities are welcome. fix specific consensus incompatibilities are welcome.
@ -81,3 +76,15 @@ what they need to change.
Remove `num` dependency at Matt's request; agree this is obnoxious to require all Remove `num` dependency at Matt's request; agree this is obnoxious to require all
downstream users to also have a `num` dependency just so they can use `Uint256::from_u64`. downstream users to also have a `num` dependency just so they can use `Uint256::from_u64`.
### 0.12
* The in-memory blockchain was moved into a dedicated project rust-bitcoin-chain.
* Removed old script interpreter
* A new optional feature "bitcoinconsenus" lets this library use Bitcoin Core's native
script verifier, wrappend into Rust by the rust-bitcoinconsenus project.
See Transaction::verify and Script::verify methods.
* Replaced Base58 traits with encode_slice, check_encode_slice, from and from_check functions in the base58 module.

View File

@ -35,6 +35,9 @@ use blockdata::opcodes;
use network::encodable::{ConsensusDecodable, ConsensusEncodable}; use network::encodable::{ConsensusDecodable, ConsensusEncodable};
use network::serialize::{SimpleDecoder, SimpleEncoder}; use network::serialize::{SimpleDecoder, SimpleEncoder};
use util::hash::Hash160; use util::hash::Hash160;
#[cfg(feature="bitcoinconsensus")] use bitcoinconsensus;
#[cfg(feature="bitcoinconsensus")] use std::convert;
#[cfg(feature="bitcoinconsensus")] use util::hash::Sha256dHash;
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
/// A Bitcoin script /// A Bitcoin script
@ -150,6 +153,18 @@ pub enum Error {
EarlyEndOfScript, EarlyEndOfScript,
/// Tried to read an array off the stack as a number when it was more than 4 bytes /// Tried to read an array off the stack as a number when it was more than 4 bytes
NumericOverflow, NumericOverflow,
#[cfg(feature="bitcoinconsensus")]
/// Error validating the script with bitcoinconsensus library
BitcoinConsensus(bitcoinconsensus::Error),
#[cfg(feature="bitcoinconsensus")]
/// Can not find the spent transaction
UnknownSpentTransaction(Sha256dHash),
#[cfg(feature="bitcoinconsensus")]
/// The spent transaction does not have the referred output
WrongSpentOutputIndex(usize),
#[cfg(feature="bitcoinconsensus")]
/// Can not serialize the spending transaction
SerializationError
} }
impl fmt::Display for Error { impl fmt::Display for Error {
@ -165,10 +180,26 @@ impl error::Error for Error {
match *self { match *self {
Error::EarlyEndOfScript => "unexpected end of script", Error::EarlyEndOfScript => "unexpected end of script",
Error::NumericOverflow => "numeric overflow (number on stack larger than 4 bytes)", Error::NumericOverflow => "numeric overflow (number on stack larger than 4 bytes)",
#[cfg(feature="bitcoinconsensus")]
Error::BitcoinConsensus(ref _n) => "bitcoinconsenus verification failed",
#[cfg(feature="bitcoinconsensus")]
Error::UnknownSpentTransaction (ref _hash) => "unknown transaction referred in Transaction::verify()",
#[cfg(feature="bitcoinconsensus")]
Error::WrongSpentOutputIndex(ref _ix) => "unknown output index {} referred in Transaction::verify()",
#[cfg(feature="bitcoinconsensus")]
Error::SerializationError => "can not serialize the spending transaction in Transaction::verify()",
} }
} }
} }
#[cfg(feature="bitcoinconsensus")]
impl convert::From<bitcoinconsensus::Error> for Error {
fn from(err: bitcoinconsensus::Error) -> Error {
match err {
_ => Error::BitcoinConsensus(err)
}
}
}
/// Helper to encode an integer in script format /// Helper to encode an integer in script format
fn build_scriptint(n: i64) -> Vec<u8> { fn build_scriptint(n: i64) -> Vec<u8> {
if n == 0 { return vec![] } if n == 0 { return vec![] }
@ -310,6 +341,16 @@ impl Script {
!self.0.is_empty() && (opcodes::All::from(self.0[0]).classify() == opcodes::Class::ReturnOp || !self.0.is_empty() && (opcodes::All::from(self.0[0]).classify() == opcodes::Class::ReturnOp ||
opcodes::All::from(self.0[0]).classify() == opcodes::Class::IllegalOp) opcodes::All::from(self.0[0]).classify() == opcodes::Class::IllegalOp)
} }
#[cfg(feature="bitcoinconsensus")]
/// verify spend of an input script
/// # Parameters
/// * index - the index of the output holding this script in its own transaction
/// * amount - the amount this script guards
/// * spending - the transaction that attempts to spend the output holding this script
pub fn verify (&self, index: usize, amount: u64, spending: &[u8]) -> Result<(), Error> {
Ok(bitcoinconsensus::verify (&self.0[..], amount, spending, index)?)
}
} }
impl Default for Script { impl Default for Script {
@ -690,5 +731,14 @@ mod test {
assert_eq!(redeem_script.to_v0_p2wsh(), expected_witout); assert_eq!(redeem_script.to_v0_p2wsh(), expected_witout);
assert_eq!(redeem_script.to_v0_p2wsh().to_p2sh(), expected_out); assert_eq!(redeem_script.to_v0_p2wsh().to_p2sh(), expected_out);
} }
#[test]
#[cfg(feature="bitcoinconsensus")]
fn test_bitcoinconsensus () {
// a random segwit transaction from the blockchain using native segwit
let spent = Builder::from("0020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d".from_hex().unwrap()).into_script();
let spending = "010000000001011f97548fbbe7a0db7588a66e18d803d0089315aa7d4cc28360b6ec50ef36718a0100000000ffffffff02df1776000000000017a9146c002a686959067f4866b8fb493ad7970290ab728757d29f0000000000220020701a8d401c84fb13e6baf169d59684e17abd9fa216c8cc5b9fc63d622ff8c58d04004730440220565d170eed95ff95027a69b313758450ba84a01224e1f7f130dda46e94d13f8602207bdd20e307f062594022f12ed5017bbf4a055a06aea91c10110a0e3bb23117fc014730440220647d2dc5b15f60bc37dc42618a370b2a1490293f9e5c8464f53ec4fe1dfe067302203598773895b4b16d37485cbe21b337f4e4b650739880098c592553add7dd4355016952210375e00eb72e29da82b89367947f29ef34afb75e8654f6ea368e0acdfd92976b7c2103a1b26313f430c4b15bb1fdce663207659d8cac749a0e53d70eff01874496feff2103c96d495bfdd5ba4145e3e046fee45e84a8a48ad05bd8dbb395c011a32cf9f88053ae00000000".from_hex().unwrap();
spent.verify(0, 18393430, spending.as_slice()).unwrap();
}
} }

View File

@ -26,9 +26,11 @@
use byteorder::{LittleEndian, WriteBytesExt}; use byteorder::{LittleEndian, WriteBytesExt};
use std::default::Default; use std::default::Default;
use std::fmt; use std::fmt;
#[cfg(feature="bitcoinconsensus")] use std::collections::HashMap;
use serde; use serde;
use util::hash::Sha256dHash; use util::hash::Sha256dHash;
#[cfg(feature="bitcoinconsensus")] use blockdata::script;
use blockdata::script::Script; use blockdata::script::Script;
use network::serialize::{serialize, BitcoinHash, SimpleEncoder, SimpleDecoder}; use network::serialize::{serialize, BitcoinHash, SimpleEncoder, SimpleDecoder};
use network::encodable::{ConsensusEncodable, ConsensusDecodable}; use network::encodable::{ConsensusEncodable, ConsensusDecodable};
@ -202,6 +204,28 @@ impl Transaction {
raw_vec.write_u32::<LittleEndian>(sighash_u32).unwrap(); raw_vec.write_u32::<LittleEndian>(sighash_u32).unwrap();
Sha256dHash::from_data(&raw_vec) Sha256dHash::from_data(&raw_vec)
} }
#[cfg(feature="bitcoinconsensus")]
/// Verify that this transaction is able to spend some outputs of spent transactions
pub fn verify (&self, spent : &HashMap<Sha256dHash, Transaction>) -> Result<(), script::Error> {
if let Ok(tx) = serialize(&*self) {
for input in &self.input {
if let Some(ref s) = spent.get(&input.prev_hash) {
if let Some(ref output) = s.output.get(input.prev_index as usize) {
input.script_sig.verify(input.prev_index as usize, output.value, tx.as_slice())?;
} else {
return Err(script::Error::WrongSpentOutputIndex(input.prev_index as usize));
}
} else {
return Err(script::Error::UnknownSpentTransaction(input.prev_hash));
}
}
Ok(())
}
else {
Err(script::Error::SerializationError)
}
}
} }
impl BitcoinHash for Transaction { impl BitcoinHash for Transaction {
@ -783,5 +807,28 @@ mod tests {
run_test_sighash("b3cad3a7041c2c17d90a2cd994f6c37307753fa3635e9ef05ab8b1ff121ca11239a0902e700300000009ab635300006aac5163ffffffffcec91722c7468156dce4664f3c783afef147f0e6f80739c83b5f09d5a09a57040200000004516a6552ffffffff969d1c6daf8ef53a70b7cdf1b4102fb3240055a8eaeaed2489617cd84cfd56cf020000000352ab53ffffffff46598b6579494a77b593681c33422a99559b9993d77ca2fa97833508b0c169f80200000009655300655365516351ffffffff04d7ddf800000000000853536a65ac6351ab09f3420300000000056aab65abac33589d04000000000952656a65655151acac944d6f0400000000006a8004ba", "005165", 1, 1035865506, "fe1dc9e8554deecf8f50c417c670b839cc9d650722ebaaf36572418756075d58"); run_test_sighash("b3cad3a7041c2c17d90a2cd994f6c37307753fa3635e9ef05ab8b1ff121ca11239a0902e700300000009ab635300006aac5163ffffffffcec91722c7468156dce4664f3c783afef147f0e6f80739c83b5f09d5a09a57040200000004516a6552ffffffff969d1c6daf8ef53a70b7cdf1b4102fb3240055a8eaeaed2489617cd84cfd56cf020000000352ab53ffffffff46598b6579494a77b593681c33422a99559b9993d77ca2fa97833508b0c169f80200000009655300655365516351ffffffff04d7ddf800000000000853536a65ac6351ab09f3420300000000056aab65abac33589d04000000000952656a65655151acac944d6f0400000000006a8004ba", "005165", 1, 1035865506, "fe1dc9e8554deecf8f50c417c670b839cc9d650722ebaaf36572418756075d58");
run_test_sighash("cf781855040a755f5ba85eef93837236b34a5d3daeb2dbbdcf58bb811828d806ed05754ab8010000000351ac53ffffffffda1e264727cf55c67f06ebcc56dfe7fa12ac2a994fecd0180ce09ee15c480f7d00000000096351516a51acac00ab53dd49ff9f334befd6d6f87f1a832cddfd826a90b78fd8cf19a52cb8287788af94e939d6020000000700525251ac526310d54a7e8900ed633f0f6f0841145aae7ee0cbbb1e2a0cae724ee4558dbabfdc58ba6855010000000552536a53abfd1b101102c51f910500000000096300656a525252656a300bee010000000009ac52005263635151abe19235c9", "53005365", 2, 1422854188, "d5981bd4467817c1330da72ddb8760d6c2556cd809264b2d85e6d274609fc3a3"); run_test_sighash("cf781855040a755f5ba85eef93837236b34a5d3daeb2dbbdcf58bb811828d806ed05754ab8010000000351ac53ffffffffda1e264727cf55c67f06ebcc56dfe7fa12ac2a994fecd0180ce09ee15c480f7d00000000096351516a51acac00ab53dd49ff9f334befd6d6f87f1a832cddfd826a90b78fd8cf19a52cb8287788af94e939d6020000000700525251ac526310d54a7e8900ed633f0f6f0841145aae7ee0cbbb1e2a0cae724ee4558dbabfdc58ba6855010000000552536a53abfd1b101102c51f910500000000096300656a525252656a300bee010000000009ac52005263635151abe19235c9", "53005365", 2, 1422854188, "d5981bd4467817c1330da72ddb8760d6c2556cd809264b2d85e6d274609fc3a3");
} }
#[test]
#[cfg(feature="bitcoinconsensus")]
fn test_transaction_verify () {
use serialize::hex::FromHex;
use std::collections::HashMap;
// a random recent segwit transaction from blockchain using both old and segwit inputs
let spending: Transaction = deserialize("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700"
.from_hex().unwrap().as_slice()).unwrap();
let spent1: Transaction = deserialize("020000000001040aacd2c49f5f3c0968cfa8caf9d5761436d95385252e3abb4de8f5dcf8a582f20000000017160014bcadb2baea98af0d9a902e53a7e9adff43b191e9feffffff96cd3c93cac3db114aafe753122bd7d1afa5aa4155ae04b3256344ecca69d72001000000171600141d9984579ceb5c67ebfbfb47124f056662fe7adbfeffffffc878dd74d3a44072eae6178bb94b9253177db1a5aaa6d068eb0e4db7631762e20000000017160014df2a48cdc53dae1aba7aa71cb1f9de089d75aac3feffffffe49f99275bc8363f5f593f4eec371c51f62c34ff11cc6d8d778787d340d6896c0100000017160014229b3b297a0587e03375ab4174ef56eeb0968735feffffff03360d0f00000000001976a9149f44b06f6ee92ddbc4686f71afe528c09727a5c788ac24281b00000000001976a9140277b4f68ff20307a2a9f9b4487a38b501eb955888ac227c0000000000001976a9148020cd422f55eef8747a9d418f5441030f7c9c7788ac0247304402204aa3bd9682f9a8e101505f6358aacd1749ecf53a62b8370b97d59243b3d6984f02200384ad449870b0e6e89c92505880411285ecd41cf11e7439b973f13bad97e53901210205b392ffcb83124b1c7ce6dd594688198ef600d34500a7f3552d67947bbe392802473044022033dfd8d190a4ae36b9f60999b217c775b96eb10dee3a1ff50fb6a75325719106022005872e4e36d194e49ced2ebcf8bb9d843d842e7b7e0eb042f4028396088d292f012103c9d7cbf369410b090480de2aa15c6c73d91b9ffa7d88b90724614b70be41e98e0247304402207d952de9e59e4684efed069797e3e2d993e9f98ec8a9ccd599de43005fe3f713022076d190cc93d9513fc061b1ba565afac574e02027c9efbfa1d7b71ab8dbb21e0501210313ad44bc030cc6cb111798c2bf3d2139418d751c1e79ec4e837ce360cc03b97a024730440220029e75edb5e9413eb98d684d62a077b17fa5b7cc19349c1e8cc6c4733b7b7452022048d4b9cae594f03741029ff841e35996ef233701c1ea9aa55c301362ea2e2f68012103590657108a72feb8dc1dec022cf6a230bb23dc7aaa52f4032384853b9f8388baf9d20700"
.from_hex().unwrap().as_slice()).unwrap();
let spent2: Transaction = deserialize("0200000000010166c3d39490dc827a2594c7b17b7d37445e1f4b372179649cd2ce4475e3641bbb0100000017160014e69aa750e9bff1aca1e32e57328b641b611fc817fdffffff01e87c5d010000000017a914f3890da1b99e44cd3d52f7bcea6a1351658ea7be87024830450221009eb97597953dc288de30060ba02d4e91b2bde1af2ecf679c7f5ab5989549aa8002202a98f8c3bd1a5a31c0d72950dd6e2e3870c6c5819a6c3db740e91ebbbc5ef4800121023f3d3b8e74b807e32217dea2c75c8d0bd46b8665b3a2d9b3cb310959de52a09bc9d20700"
.from_hex().unwrap().as_slice()).unwrap();
let spent3: Transaction = deserialize("01000000027a1120a30cef95422638e8dab9dedf720ec614b1b21e451a4957a5969afb869d000000006a47304402200ecc318a829a6cad4aa9db152adbf09b0cd2de36f47b53f5dade3bc7ef086ca702205722cda7404edd6012eedd79b2d6f24c0a0c657df1a442d0a2166614fb164a4701210372f4b97b34e9c408741cd1fc97bcc7ffdda6941213ccfde1cb4075c0f17aab06ffffffffc23b43e5a18e5a66087c0d5e64d58e8e21fcf83ce3f5e4f7ecb902b0e80a7fb6010000006b483045022100f10076a0ea4b4cf8816ed27a1065883efca230933bf2ff81d5db6258691ff75202206b001ef87624e76244377f57f0c84bc5127d0dd3f6e0ef28b276f176badb223a01210309a3a61776afd39de4ed29b622cd399d99ecd942909c36a8696cfd22fc5b5a1affffffff0200127a000000000017a914f895e1dd9b29cb228e9b06a15204e3b57feaf7cc8769311d09000000001976a9144d00da12aaa51849d2583ae64525d4a06cd70fde88ac00000000"
.from_hex().unwrap().as_slice()).unwrap();
let mut spent = HashMap::new();
spent.insert(spent1.txid(), spent1);
spent.insert(spent2.txid(), spent2);
spent.insert(spent3.txid(), spent3);
spending.verify (&spent).unwrap();
}
} }

View File

@ -49,6 +49,7 @@ extern crate secp256k1;
extern crate serde; extern crate serde;
extern crate strason; extern crate strason;
#[cfg(all(test, feature = "unstable"))] extern crate test; #[cfg(all(test, feature = "unstable"))] extern crate test;
#[cfg(feature="bitcoinconsensus")] extern crate bitcoinconsensus;
#[cfg(test)] #[cfg(test)]
#[macro_use] #[macro_use]