Merge pull request #58 from tamasblummer/libbitcoinconsenus_feature
Optional feature gives access to Bitcoin Core's own script validator
This commit is contained in:
commit
c8702ab55d
|
@ -15,6 +15,9 @@ readme = "README.md"
|
|||
name = "bitcoin"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
bitcoinconsenus = ["bitcoinconsensus"]
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.1"
|
||||
rand = "0.3"
|
||||
|
@ -22,8 +25,10 @@ rust-crypto = "0.2"
|
|||
rustc-serialize = "0.3"
|
||||
serde = "0.6"
|
||||
strason = "0.3"
|
||||
bitcoinconsensus = { version = "0.16", optional=true }
|
||||
|
||||
[dependencies.secp256k1]
|
||||
version = "0.8"
|
||||
features = [ "rand", "serde" ]
|
||||
|
||||
|
||||
|
|
21
README.md
21
README.md
|
@ -13,7 +13,6 @@ Supports (or should support)
|
|||
* De/serialization of Bitcoin protocol network messages
|
||||
* De/serialization of blocks and transactions
|
||||
* Script de/serialization
|
||||
* Blockchain validation
|
||||
* 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)
|
||||
|
||||
|
@ -27,7 +26,7 @@ To use rust-bitcoin, just add the following to your Cargo.toml.
|
|||
|
||||
```toml
|
||||
[dependencies]
|
||||
bitcoin = "0.10"
|
||||
bitcoin = "0.12"
|
||||
```
|
||||
|
||||
# Known limitations
|
||||
|
@ -35,17 +34,13 @@ bitcoin = "0.10"
|
|||
## Consensus
|
||||
|
||||
This library **must not** be used for consensus code (i.e. fully validating
|
||||
blockchain data). It technically supports doing this, using the feature-gated
|
||||
script parser, but doing so is very
|
||||
blockchain data). It technically supports doing this, but doing so is very
|
||||
ill-advised because there are many deviations, known and unknown, between
|
||||
this library and the Bitcoin Core reference implementation. In a consensus
|
||||
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
|
||||
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
|
||||
ever be fixed, and there are no plans to do so. Of course, patches to
|
||||
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
|
||||
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.
|
||||
|
||||
|
|
|
@ -35,6 +35,9 @@ use blockdata::opcodes;
|
|||
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
||||
use network::serialize::{SimpleDecoder, SimpleEncoder};
|
||||
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)]
|
||||
/// A Bitcoin script
|
||||
|
@ -150,6 +153,18 @@ pub enum Error {
|
|||
EarlyEndOfScript,
|
||||
/// Tried to read an array off the stack as a number when it was more than 4 bytes
|
||||
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 {
|
||||
|
@ -165,10 +180,26 @@ impl error::Error for Error {
|
|||
match *self {
|
||||
Error::EarlyEndOfScript => "unexpected end of script",
|
||||
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
|
||||
fn build_scriptint(n: i64) -> Vec<u8> {
|
||||
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 ||
|
||||
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 {
|
||||
|
@ -690,5 +731,14 @@ mod test {
|
|||
assert_eq!(redeem_script.to_v0_p2wsh(), expected_witout);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,9 +26,11 @@
|
|||
use byteorder::{LittleEndian, WriteBytesExt};
|
||||
use std::default::Default;
|
||||
use std::fmt;
|
||||
#[cfg(feature="bitcoinconsensus")] use std::collections::HashMap;
|
||||
use serde;
|
||||
|
||||
use util::hash::Sha256dHash;
|
||||
#[cfg(feature="bitcoinconsensus")] use blockdata::script;
|
||||
use blockdata::script::Script;
|
||||
use network::serialize::{serialize, BitcoinHash, SimpleEncoder, SimpleDecoder};
|
||||
use network::encodable::{ConsensusEncodable, ConsensusDecodable};
|
||||
|
@ -202,6 +204,28 @@ impl Transaction {
|
|||
raw_vec.write_u32::<LittleEndian>(sighash_u32).unwrap();
|
||||
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 {
|
||||
|
@ -783,5 +807,28 @@ mod tests {
|
|||
run_test_sighash("b3cad3a7041c2c17d90a2cd994f6c37307753fa3635e9ef05ab8b1ff121ca11239a0902e700300000009ab635300006aac5163ffffffffcec91722c7468156dce4664f3c783afef147f0e6f80739c83b5f09d5a09a57040200000004516a6552ffffffff969d1c6daf8ef53a70b7cdf1b4102fb3240055a8eaeaed2489617cd84cfd56cf020000000352ab53ffffffff46598b6579494a77b593681c33422a99559b9993d77ca2fa97833508b0c169f80200000009655300655365516351ffffffff04d7ddf800000000000853536a65ac6351ab09f3420300000000056aab65abac33589d04000000000952656a65655151acac944d6f0400000000006a8004ba", "005165", 1, 1035865506, "fe1dc9e8554deecf8f50c417c670b839cc9d650722ebaaf36572418756075d58");
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@ extern crate secp256k1;
|
|||
extern crate serde;
|
||||
extern crate strason;
|
||||
#[cfg(all(test, feature = "unstable"))] extern crate test;
|
||||
#[cfg(feature="bitcoinconsensus")] extern crate bitcoinconsensus;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
|
|
Loading…
Reference in New Issue