PSBT: basic support for proprietary keys
This commit is contained in:
parent
a6264cfca6
commit
c3024c3ebb
|
@ -32,6 +32,8 @@ const PSBT_GLOBAL_UNSIGNED_TX: u8 = 0x00;
|
||||||
const PSBT_GLOBAL_XPUB: u8 = 0x01;
|
const PSBT_GLOBAL_XPUB: u8 = 0x01;
|
||||||
/// Type: Version Number PSBT_GLOBAL_VERSION = 0xFB
|
/// Type: Version Number PSBT_GLOBAL_VERSION = 0xFB
|
||||||
const PSBT_GLOBAL_VERSION: u8 = 0xFB;
|
const PSBT_GLOBAL_VERSION: u8 = 0xFB;
|
||||||
|
/// Type: Proprietary Use Type PSBT_GLOBAL_PROPRIETARY = 0xFC
|
||||||
|
const PSBT_GLOBAL_PROPRIETARY: u8 = 0xFC;
|
||||||
|
|
||||||
/// A key-value map for global data.
|
/// A key-value map for global data.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
@ -44,10 +46,12 @@ pub struct Global {
|
||||||
/// A global map from extended public keys to the used key fingerprint and
|
/// A global map from extended public keys to the used key fingerprint and
|
||||||
/// derivation path as defined by BIP 32
|
/// derivation path as defined by BIP 32
|
||||||
pub xpub: BTreeMap<ExtendedPubKey, KeySource>,
|
pub xpub: BTreeMap<ExtendedPubKey, KeySource>,
|
||||||
|
/// Global proprietary key-value pairs.
|
||||||
|
pub proprietary: BTreeMap<raw::Key, Vec<u8>>,
|
||||||
/// Unknown global key-value pairs.
|
/// Unknown global key-value pairs.
|
||||||
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
||||||
}
|
}
|
||||||
serde_struct_impl!(Global, unsigned_tx, version, xpub, unknown);
|
serde_struct_impl!(Global, unsigned_tx, version, xpub, proprietary, unknown);
|
||||||
|
|
||||||
impl Global {
|
impl Global {
|
||||||
/// Create a Global from an unsigned transaction, error if not unsigned
|
/// Create a Global from an unsigned transaction, error if not unsigned
|
||||||
|
@ -66,6 +70,7 @@ impl Global {
|
||||||
unsigned_tx: tx,
|
unsigned_tx: tx,
|
||||||
xpub: Default::default(),
|
xpub: Default::default(),
|
||||||
version: 0,
|
version: 0,
|
||||||
|
proprietary: Default::default(),
|
||||||
unknown: Default::default(),
|
unknown: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -80,6 +85,10 @@ impl Map for Global {
|
||||||
|
|
||||||
match raw_key.type_value {
|
match raw_key.type_value {
|
||||||
PSBT_GLOBAL_UNSIGNED_TX => return Err(Error::DuplicateKey(raw_key).into()),
|
PSBT_GLOBAL_UNSIGNED_TX => return Err(Error::DuplicateKey(raw_key).into()),
|
||||||
|
PSBT_GLOBAL_PROPRIETARY => match self.proprietary.entry(raw_key) {
|
||||||
|
Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
||||||
|
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
||||||
|
}
|
||||||
_ => match self.unknown.entry(raw_key) {
|
_ => match self.unknown.entry(raw_key) {
|
||||||
Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
||||||
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
||||||
|
@ -135,6 +144,13 @@ impl Map for Global {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (key, value) in self.proprietary.iter() {
|
||||||
|
rv.push(raw::Pair {
|
||||||
|
key: key.clone(),
|
||||||
|
value: value.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (key, value) in self.unknown.iter() {
|
for (key, value) in self.unknown.iter() {
|
||||||
rv.push(raw::Pair {
|
rv.push(raw::Pair {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
|
@ -201,6 +217,7 @@ impl Map for Global {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.proprietary.extend(other.proprietary);
|
||||||
self.unknown.extend(other.unknown);
|
self.unknown.extend(other.unknown);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -215,6 +232,7 @@ impl Decodable for Global {
|
||||||
let mut version: Option<u32> = None;
|
let mut version: Option<u32> = None;
|
||||||
let mut unknowns: BTreeMap<raw::Key, Vec<u8>> = Default::default();
|
let mut unknowns: BTreeMap<raw::Key, Vec<u8>> = Default::default();
|
||||||
let mut xpub_map: BTreeMap<ExtendedPubKey, (Fingerprint, DerivationPath)> = Default::default();
|
let mut xpub_map: BTreeMap<ExtendedPubKey, (Fingerprint, DerivationPath)> = Default::default();
|
||||||
|
let mut proprietary: BTreeMap<raw::Key, Vec<u8>> = Default::default();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match raw::Pair::consensus_decode(&mut d) {
|
match raw::Pair::consensus_decode(&mut d) {
|
||||||
|
@ -299,6 +317,10 @@ impl Decodable for Global {
|
||||||
return Err(Error::InvalidKey(pair.key).into())
|
return Err(Error::InvalidKey(pair.key).into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PSBT_GLOBAL_PROPRIETARY => match proprietary.entry(pair.key) {
|
||||||
|
Entry::Vacant(empty_key) => {empty_key.insert(pair.value);},
|
||||||
|
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
||||||
|
}
|
||||||
_ => match unknowns.entry(pair.key) {
|
_ => match unknowns.entry(pair.key) {
|
||||||
Entry::Vacant(empty_key) => {empty_key.insert(pair.value);},
|
Entry::Vacant(empty_key) => {empty_key.insert(pair.value);},
|
||||||
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
||||||
|
|
|
@ -52,6 +52,8 @@ const PSBT_IN_SHA256: u8 = 0x0b;
|
||||||
const PSBT_IN_HASH160: u8 = 0x0c;
|
const PSBT_IN_HASH160: u8 = 0x0c;
|
||||||
/// Type: HASH256 preimage PSBT_IN_HASH256 = 0x0d
|
/// Type: HASH256 preimage PSBT_IN_HASH256 = 0x0d
|
||||||
const PSBT_IN_HASH256: u8 = 0x0d;
|
const PSBT_IN_HASH256: u8 = 0x0d;
|
||||||
|
/// Type: Proprietary Use Type PSBT_IN_PROPRIETARY = 0xFC
|
||||||
|
const PSBT_IN_PROPRIETARY: u8 = 0xFC;
|
||||||
|
|
||||||
/// A key-value map for an input of the corresponding index in the unsigned
|
/// A key-value map for an input of the corresponding index in the unsigned
|
||||||
/// transaction.
|
/// transaction.
|
||||||
|
@ -93,6 +95,8 @@ pub struct Input {
|
||||||
pub hash160_preimages: BTreeMap<hash160::Hash, Vec<u8>>,
|
pub hash160_preimages: BTreeMap<hash160::Hash, Vec<u8>>,
|
||||||
/// HAS256 hash to preimage map
|
/// HAS256 hash to preimage map
|
||||||
pub hash256_preimages: BTreeMap<sha256d::Hash, Vec<u8>>,
|
pub hash256_preimages: BTreeMap<sha256d::Hash, Vec<u8>>,
|
||||||
|
/// Proprietary key-value pairs for this input.
|
||||||
|
pub proprietary: BTreeMap<raw::Key, Vec<u8>>,
|
||||||
/// Unknown key-value pairs for this input.
|
/// Unknown key-value pairs for this input.
|
||||||
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
@ -101,7 +105,7 @@ serde_struct_impl!(
|
||||||
sighash_type, redeem_script, witness_script, bip32_derivation,
|
sighash_type, redeem_script, witness_script, bip32_derivation,
|
||||||
final_script_sig, final_script_witness,
|
final_script_sig, final_script_witness,
|
||||||
ripemd160_preimages, sha256_preimages, hash160_preimages, hash256_preimages,
|
ripemd160_preimages, sha256_preimages, hash160_preimages, hash256_preimages,
|
||||||
unknown
|
proprietary, unknown
|
||||||
);
|
);
|
||||||
|
|
||||||
impl Map for Input {
|
impl Map for Input {
|
||||||
|
@ -169,6 +173,10 @@ impl Map for Input {
|
||||||
PSBT_IN_HASH256 => {
|
PSBT_IN_HASH256 => {
|
||||||
psbt_insert_hash_pair(&mut self.hash256_preimages, raw_key, raw_value, error::PsbtHash::Hash256)?;
|
psbt_insert_hash_pair(&mut self.hash256_preimages, raw_key, raw_value, error::PsbtHash::Hash256)?;
|
||||||
}
|
}
|
||||||
|
PSBT_IN_PROPRIETARY => match self.proprietary.entry(raw_key) {
|
||||||
|
::std::collections::btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
||||||
|
::std::collections::btree_map::Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
||||||
|
}
|
||||||
_ => match self.unknown.entry(raw_key) {
|
_ => match self.unknown.entry(raw_key) {
|
||||||
::std::collections::btree_map::Entry::Vacant(empty_key) => {
|
::std::collections::btree_map::Entry::Vacant(empty_key) => {
|
||||||
empty_key.insert(raw_value);
|
empty_key.insert(raw_value);
|
||||||
|
@ -237,6 +245,13 @@ impl Map for Input {
|
||||||
rv.push(self.hash256_preimages as <PSBT_IN_HASH256, sha256d::Hash>|<Vec<u8>>)
|
rv.push(self.hash256_preimages as <PSBT_IN_HASH256, sha256d::Hash>|<Vec<u8>>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (key, value) in self.proprietary.iter() {
|
||||||
|
rv.push(raw::Pair {
|
||||||
|
key: key.clone(),
|
||||||
|
value: value.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (key, value) in self.unknown.iter() {
|
for (key, value) in self.unknown.iter() {
|
||||||
rv.push(raw::Pair {
|
rv.push(raw::Pair {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
|
@ -261,6 +276,7 @@ impl Map for Input {
|
||||||
self.sha256_preimages.extend(other.sha256_preimages);
|
self.sha256_preimages.extend(other.sha256_preimages);
|
||||||
self.hash160_preimages.extend(other.hash160_preimages);
|
self.hash160_preimages.extend(other.hash160_preimages);
|
||||||
self.hash256_preimages.extend(other.hash256_preimages);
|
self.hash256_preimages.extend(other.hash256_preimages);
|
||||||
|
self.proprietary.extend(other.proprietary);
|
||||||
self.unknown.extend(other.unknown);
|
self.unknown.extend(other.unknown);
|
||||||
|
|
||||||
merge!(redeem_script, self, other);
|
merge!(redeem_script, self, other);
|
||||||
|
|
|
@ -30,6 +30,8 @@ const PSBT_OUT_REDEEM_SCRIPT: u8 = 0x00;
|
||||||
const PSBT_OUT_WITNESS_SCRIPT: u8 = 0x01;
|
const PSBT_OUT_WITNESS_SCRIPT: u8 = 0x01;
|
||||||
/// Type: BIP 32 Derivation Path PSBT_OUT_BIP32_DERIVATION = 0x02
|
/// Type: BIP 32 Derivation Path PSBT_OUT_BIP32_DERIVATION = 0x02
|
||||||
const PSBT_OUT_BIP32_DERIVATION: u8 = 0x02;
|
const PSBT_OUT_BIP32_DERIVATION: u8 = 0x02;
|
||||||
|
/// Type: Proprietary Use Type PSBT_IN_PROPRIETARY = 0xFC
|
||||||
|
const PSBT_OUT_PROPRIETARY: u8 = 0xFC;
|
||||||
|
|
||||||
/// A key-value map for an output of the corresponding index in the unsigned
|
/// A key-value map for an output of the corresponding index in the unsigned
|
||||||
/// transaction.
|
/// transaction.
|
||||||
|
@ -42,11 +44,13 @@ pub struct Output {
|
||||||
/// A map from public keys needed to spend this output to their
|
/// A map from public keys needed to spend this output to their
|
||||||
/// corresponding master key fingerprints and derivation paths.
|
/// corresponding master key fingerprints and derivation paths.
|
||||||
pub bip32_derivation: BTreeMap<PublicKey, KeySource>,
|
pub bip32_derivation: BTreeMap<PublicKey, KeySource>,
|
||||||
|
/// Proprietary key-value pairs for this output.
|
||||||
|
pub proprietary: BTreeMap<raw::Key, Vec<u8>>,
|
||||||
/// Unknown key-value pairs for this output.
|
/// Unknown key-value pairs for this output.
|
||||||
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
||||||
}
|
}
|
||||||
serde_struct_impl!(
|
serde_struct_impl!(
|
||||||
Output, redeem_script, witness_script, bip32_derivation, unknown
|
Output, redeem_script, witness_script, bip32_derivation, proprietary, unknown
|
||||||
);
|
);
|
||||||
|
|
||||||
impl Map for Output {
|
impl Map for Output {
|
||||||
|
@ -72,6 +76,10 @@ impl Map for Output {
|
||||||
self.bip32_derivation <= <raw_key: PublicKey>|<raw_value: KeySource>
|
self.bip32_derivation <= <raw_key: PublicKey>|<raw_value: KeySource>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PSBT_OUT_PROPRIETARY => match self.proprietary.entry(raw_key) {
|
||||||
|
Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
||||||
|
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
||||||
|
}
|
||||||
_ => match self.unknown.entry(raw_key) {
|
_ => match self.unknown.entry(raw_key) {
|
||||||
Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
||||||
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
||||||
|
@ -96,6 +104,13 @@ impl Map for Output {
|
||||||
rv.push(self.bip32_derivation as <PSBT_OUT_BIP32_DERIVATION, PublicKey>|<KeySource>)
|
rv.push(self.bip32_derivation as <PSBT_OUT_BIP32_DERIVATION, PublicKey>|<KeySource>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (key, value) in self.proprietary.iter() {
|
||||||
|
rv.push(raw::Pair {
|
||||||
|
key: key.clone(),
|
||||||
|
value: value.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
for (key, value) in self.unknown.iter() {
|
for (key, value) in self.unknown.iter() {
|
||||||
rv.push(raw::Pair {
|
rv.push(raw::Pair {
|
||||||
key: key.clone(),
|
key: key.clone(),
|
||||||
|
@ -108,6 +123,7 @@ impl Map for Output {
|
||||||
|
|
||||||
fn merge(&mut self, other: Self) -> Result<(), psbt::Error> {
|
fn merge(&mut self, other: Self) -> Result<(), psbt::Error> {
|
||||||
self.bip32_derivation.extend(other.bip32_derivation);
|
self.bip32_derivation.extend(other.bip32_derivation);
|
||||||
|
self.proprietary.extend(other.proprietary);
|
||||||
self.unknown.extend(other.unknown);
|
self.unknown.extend(other.unknown);
|
||||||
|
|
||||||
merge!(redeem_script, self, other);
|
merge!(redeem_script, self, other);
|
||||||
|
|
|
@ -194,6 +194,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
xpub: Default::default(),
|
xpub: Default::default(),
|
||||||
version: 0,
|
version: 0,
|
||||||
|
proprietary: BTreeMap::new(),
|
||||||
unknown: BTreeMap::new(),
|
unknown: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
inputs: vec![],
|
inputs: vec![],
|
||||||
|
@ -283,6 +284,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
xpub: Default::default(),
|
xpub: Default::default(),
|
||||||
version: 0,
|
version: 0,
|
||||||
|
proprietary: Default::default(),
|
||||||
unknown: Default::default(),
|
unknown: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -387,6 +389,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
xpub: Default::default(),
|
xpub: Default::default(),
|
||||||
version: 0,
|
version: 0,
|
||||||
|
proprietary: BTreeMap::new(),
|
||||||
unknown: BTreeMap::new(),
|
unknown: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
inputs: vec![Input {
|
inputs: vec![Input {
|
||||||
|
@ -612,6 +615,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
version: 0,
|
version: 0,
|
||||||
xpub: Default::default(),
|
xpub: Default::default(),
|
||||||
|
proprietary: Default::default(),
|
||||||
unknown: BTreeMap::new(),
|
unknown: BTreeMap::new(),
|
||||||
},
|
},
|
||||||
inputs: vec![Input {
|
inputs: vec![Input {
|
||||||
|
|
Loading…
Reference in New Issue