Merge rust-bitcoin/rust-bitcoin#3255: fix: re-implement `Psbt` (de)serialization from/to readers/writers
cf129ad314
fix: re-implement (de)serialization from/to readers/writers (elsirion) Pull request description: Fixes #3250. The serialization is less than ideal and still allocates a lot. I can understand not wanting to (ab)use the consensus encoding traits, but they have a pretty good interface, copying it and creating some `EncodePsbt` and `DecodePsbt` traits with similar interfaces would have been nice imo. ACKs for top commit: Kixunil: ACKcf129ad314
apoelstra: ACKcf129ad314
successfully ran local tests; LGTM -- I believe this is non-breaking, as does cargo-semver-checks, so we can backport this to 0.32 Tree-SHA512: d7f218164d772db3a9fb4436953c3b5fd3677b92078d0843233197629df7d852d80615a3ff38c5b70771381ba1aeb30defdc98ee63653e570bb75dc553400cad
This commit is contained in:
commit
98252f36df
|
@ -13,6 +13,7 @@ use crate::bip32::{ChildNumber, Fingerprint, KeySource};
|
||||||
use crate::consensus::encode::{self, deserialize_partial, serialize, Decodable, Encodable};
|
use crate::consensus::encode::{self, deserialize_partial, serialize, Decodable, Encodable};
|
||||||
use crate::crypto::key::PublicKey;
|
use crate::crypto::key::PublicKey;
|
||||||
use crate::crypto::{ecdsa, taproot};
|
use crate::crypto::{ecdsa, taproot};
|
||||||
|
use crate::io::Write;
|
||||||
use crate::prelude::{DisplayHex, String, Vec};
|
use crate::prelude::{DisplayHex, String, Vec};
|
||||||
use crate::psbt::{Error, Psbt};
|
use crate::psbt::{Error, Psbt};
|
||||||
use crate::script::ScriptBuf;
|
use crate::script::ScriptBuf;
|
||||||
|
@ -42,40 +43,57 @@ impl Psbt {
|
||||||
/// Serialize as raw binary data
|
/// Serialize as raw binary data
|
||||||
pub fn serialize(&self) -> Vec<u8> {
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
let mut buf: Vec<u8> = Vec::new();
|
let mut buf: Vec<u8> = Vec::new();
|
||||||
|
self.serialize_to_writer(&mut buf).expect("Writing to Vec can't fail");
|
||||||
// <magic>
|
|
||||||
buf.extend_from_slice(b"psbt");
|
|
||||||
|
|
||||||
buf.push(0xff_u8);
|
|
||||||
|
|
||||||
buf.extend(self.serialize_map());
|
|
||||||
|
|
||||||
for i in &self.inputs {
|
|
||||||
buf.extend(i.serialize_map());
|
|
||||||
}
|
|
||||||
|
|
||||||
for i in &self.outputs {
|
|
||||||
buf.extend(i.serialize_map());
|
|
||||||
}
|
|
||||||
|
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Serialize the PSBT into a writer.
|
||||||
|
pub fn serialize_to_writer(&self, w: &mut impl Write) -> io::Result<usize> {
|
||||||
|
let mut written_len = 0;
|
||||||
|
|
||||||
|
fn write_all(w: &mut impl Write, data: &[u8]) -> io::Result<usize> {
|
||||||
|
w.write_all(data).map(|_| data.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
// magic
|
||||||
|
written_len += write_all(w, b"psbt")?;
|
||||||
|
// separator
|
||||||
|
written_len += write_all(w, &[0xff])?;
|
||||||
|
|
||||||
|
written_len += write_all(w, &self.serialize_map())?;
|
||||||
|
|
||||||
|
for i in &self.inputs {
|
||||||
|
written_len += write_all(w, &i.serialize_map())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in &self.outputs {
|
||||||
|
written_len += write_all(w, &i.serialize_map())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(written_len)
|
||||||
|
}
|
||||||
|
|
||||||
/// Deserialize a value from raw binary data.
|
/// Deserialize a value from raw binary data.
|
||||||
pub fn deserialize(bytes: &[u8]) -> Result<Self, Error> {
|
pub fn deserialize(mut bytes: &[u8]) -> Result<Self, Error> {
|
||||||
|
Self::deserialize_from_reader(&mut bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deserialize a value from raw binary data read from a `BufRead` object.
|
||||||
|
pub fn deserialize_from_reader<R: io::BufRead>(r: &mut R) -> Result<Self, Error> {
|
||||||
const MAGIC_BYTES: &[u8] = b"psbt";
|
const MAGIC_BYTES: &[u8] = b"psbt";
|
||||||
if bytes.get(0..MAGIC_BYTES.len()) != Some(MAGIC_BYTES) {
|
|
||||||
|
let magic: [u8; 4] = Decodable::consensus_decode(r)?;
|
||||||
|
if magic != MAGIC_BYTES {
|
||||||
return Err(Error::InvalidMagic);
|
return Err(Error::InvalidMagic);
|
||||||
}
|
}
|
||||||
|
|
||||||
const PSBT_SERPARATOR: u8 = 0xff_u8;
|
const PSBT_SERPARATOR: u8 = 0xff_u8;
|
||||||
if bytes.get(MAGIC_BYTES.len()) != Some(&PSBT_SERPARATOR) {
|
let separator: u8 = Decodable::consensus_decode(r)?;
|
||||||
|
if separator != PSBT_SERPARATOR {
|
||||||
return Err(Error::InvalidSeparator);
|
return Err(Error::InvalidSeparator);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut d = bytes.get(5..).ok_or(Error::NoMorePairs)?;
|
let mut global = Psbt::decode_global(r)?;
|
||||||
|
|
||||||
let mut global = Psbt::decode_global(&mut d)?;
|
|
||||||
global.unsigned_tx_checks()?;
|
global.unsigned_tx_checks()?;
|
||||||
|
|
||||||
let inputs: Vec<Input> = {
|
let inputs: Vec<Input> = {
|
||||||
|
@ -84,7 +102,7 @@ impl Psbt {
|
||||||
let mut inputs: Vec<Input> = Vec::with_capacity(inputs_len);
|
let mut inputs: Vec<Input> = Vec::with_capacity(inputs_len);
|
||||||
|
|
||||||
for _ in 0..inputs_len {
|
for _ in 0..inputs_len {
|
||||||
inputs.push(Input::decode(&mut d)?);
|
inputs.push(Input::decode(r)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs
|
inputs
|
||||||
|
@ -96,7 +114,7 @@ impl Psbt {
|
||||||
let mut outputs: Vec<Output> = Vec::with_capacity(outputs_len);
|
let mut outputs: Vec<Output> = Vec::with_capacity(outputs_len);
|
||||||
|
|
||||||
for _ in 0..outputs_len {
|
for _ in 0..outputs_len {
|
||||||
outputs.push(Output::decode(&mut d)?);
|
outputs.push(Output::decode(r)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
outputs
|
outputs
|
||||||
|
|
Loading…
Reference in New Issue