From 7f5c2795d6d581276119edf2becf4377d7c7df08 Mon Sep 17 00:00:00 2001 From: Dr Maxim Orlovsky Date: Fri, 18 Dec 2020 22:32:02 +0100 Subject: [PATCH] PSBT global xpub merging algorithm reworked --- src/util/psbt/map/global.rs | 64 ++++++++++++++++--------------------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/src/util/psbt/map/global.rs b/src/util/psbt/map/global.rs index e55775ff..15e9c945 100644 --- a/src/util/psbt/map/global.rs +++ b/src/util/psbt/map/global.rs @@ -15,7 +15,7 @@ use std::collections::BTreeMap; use std::collections::btree_map::Entry; use std::io::{self, Cursor, Read}; -use std::cmp::{self, Ordering}; +use std::cmp; use blockdata::transaction::Transaction; use consensus::{encode, Encodable, Decodable}; @@ -169,42 +169,34 @@ impl Map for Global { }, Entry::Occupied(mut entry) => { // Here in case of the conflict we select the version with algorithm: - // 1) if fingerprints are equal but derivations are not, we report an error; otherwise - // 2) choose longest unhardened derivation; if equal - // 3) choose longest derivation; if equal - // 4) choose the largest derivation using binary ordering; if equal - // 5) check that fingerprints are equal, otherwise fail; - // 6) if fingerprints are also equal we do nothing - let (fingerprint2, len2, normal_len2, deriv_cmp) = { - // weird scope needed for rustc 1.29 borrow checker - let (fp, deriv) = entry.get().clone(); - ( - fp, - deriv.len(), - deriv.into_iter().rposition(ChildNumber::is_normal), - derivation1.cmp(&deriv) - ) - }; - let len1 = derivation1.len(); - let normal_len1 = derivation1.into_iter().rposition(ChildNumber::is_normal); - match (normal_len1.cmp(&normal_len2), len1.cmp(&len2), deriv_cmp, fingerprint1.cmp(&fingerprint2)) { - (Ordering::Equal, Ordering::Equal, Ordering::Equal, Ordering::Equal) => {}, - (Ordering::Equal, Ordering::Equal, Ordering::Equal, _) => { - return Err(psbt::Error::MergeConflict(format!( - "global xpub {} has inconsistent key sources", xpub - ).to_owned())); - } - (Ordering::Greater, ..) - | (Ordering::Equal, Ordering::Greater, ..) - | (Ordering::Equal, Ordering::Equal, Ordering::Greater, _) => { - entry.insert((fingerprint1, derivation1)); - }, - (Ordering::Less, ..) - | (Ordering::Equal, Ordering::Less, ..) - | (Ordering::Equal, Ordering::Equal, Ordering::Less, _) => { - // do nothing here: we already own the proper data - } + // 1) if everything is equal we do nothing + // 2) report an error if + // - derivation paths are equal and fingerprints are not + // - derivation paths are of the same length, but not equal + // - derivation paths has different length, but the shorter one + // is not the strict suffix of the longer one + // 3) choose longest derivation otherwise + + let (fingerprint2, derivation2) = entry.get().clone(); + + if derivation1 == derivation2 && fingerprint1 == fingerprint2 + { + continue } + else if + derivation1.len() < derivation2.len() && + derivation1[..] == derivation2[derivation2.len() - derivation1.len()..] + { + continue + } + else if derivation2[..] == derivation1[derivation1.len() - derivation2.len()..] + { + entry.insert((fingerprint1, derivation1)); + continue + } + return Err(psbt::Error::MergeConflict(format!( + "global xpub {} has inconsistent key sources", xpub + ).to_owned())); } } }