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:
    ACK cf129ad314
  apoelstra:
    ACK cf129ad314 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:
merge-script 2024-08-28 22:26:07 +00:00
commit 98252f36df
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
1 changed files with 42 additions and 24 deletions

View File

@ -13,6 +13,7 @@ use crate::bip32::{ChildNumber, Fingerprint, KeySource};
use crate::consensus::encode::{self, deserialize_partial, serialize, Decodable, Encodable};
use crate::crypto::key::PublicKey;
use crate::crypto::{ecdsa, taproot};
use crate::io::Write;
use crate::prelude::{DisplayHex, String, Vec};
use crate::psbt::{Error, Psbt};
use crate::script::ScriptBuf;
@ -42,40 +43,57 @@ impl Psbt {
/// Serialize as raw binary data
pub fn serialize(&self) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
// <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());
}
self.serialize_to_writer(&mut buf).expect("Writing to Vec can't fail");
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.
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";
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);
}
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);
}
let mut d = bytes.get(5..).ok_or(Error::NoMorePairs)?;
let mut global = Psbt::decode_global(&mut d)?;
let mut global = Psbt::decode_global(r)?;
global.unsigned_tx_checks()?;
let inputs: Vec<Input> = {
@ -84,7 +102,7 @@ impl Psbt {
let mut inputs: Vec<Input> = Vec::with_capacity(inputs_len);
for _ in 0..inputs_len {
inputs.push(Input::decode(&mut d)?);
inputs.push(Input::decode(r)?);
}
inputs
@ -96,7 +114,7 @@ impl Psbt {
let mut outputs: Vec<Output> = Vec::with_capacity(outputs_len);
for _ in 0..outputs_len {
outputs.push(Output::decode(&mut d)?);
outputs.push(Output::decode(r)?);
}
outputs