Skip to content

Commit c859cec

Browse files
committed
Merge rust-bitcoin#222: Pretty print a policy
e829434 Implemented Policy Diff (sanket1729) 4d23b79 Pretty print a policy (sanket1729) Pull request description: Adds methods to pretty print policies similar to directory tree printing in Linux. Prints diff in colors which highlights the differences between the two policies. Our representation of Semantic policies in only in Thresh, so we combine `or(pk,or(pk,pk))` into `thresh(1,pk,pk,pk)`. And so we use the latter in comparing two policies that complicate some of the implementation but is still fairly small. Top commit has no ACKs. Tree-SHA512: 8efd11b2c2582a4e26455ccb718d1ad2016ead3507746bd98a16240c7c72cf411df7fa01d817aee3a42c138b8ff7b887e7104d29d15f7a57a61620eee20a9d2b
1 parent e78dd15 commit c859cec

File tree

1 file changed

+131
-13
lines changed

1 file changed

+131
-13
lines changed

src/policy/semantic.rs

Lines changed: 131 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
//! Abstract Policies
1616
17+
use std::collections::HashSet;
1718
use std::str::FromStr;
1819
use std::{fmt, str};
1920

@@ -60,7 +61,7 @@ pub enum Policy<Pk: MiniscriptKey> {
6061
/// Compute the Policy difference between two policies.
6162
/// This is useful when trying to find out the conditions
6263
/// under which the two policies are different.
63-
#[derive(Debug, Clone)]
64+
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
6465
pub struct PolicyDiff<Pk: MiniscriptKey> {
6566
/// The first policy
6667
pub a: Vec<Policy<Pk>>,
@@ -85,21 +86,49 @@ impl<Pk: MiniscriptKey> PolicyDiff<Pk> {
8586

8687
/// Create a new PolicyDiff in the
8788
pub fn new(a: Policy<Pk>, b: Policy<Pk>) -> Self {
88-
match (a.normalized(), b.normalized()) {
89-
(x, y) if x == y => Self::_new(vec![], vec![]),
90-
(Policy::Threshold(k1, subs1), Policy::Threshold(k2, subs2))
91-
if k1 == k2 && subs1.len() == subs2.len() =>
92-
{
93-
let mut a = Self::_new(vec![], vec![]);
94-
95-
for (sub_a, sub_b) in subs1.into_iter().zip(subs2.into_iter()) {
96-
let sub_diff = Self::_new(vec![sub_a], vec![sub_b]);
97-
a.combine(sub_diff);
89+
pub fn new_helper<Pk: MiniscriptKey>(a: Policy<Pk>, b: Policy<Pk>) -> PolicyDiff<Pk> {
90+
match (a, b) {
91+
(ref x, ref y) if x == y => PolicyDiff::_new(vec![], vec![]),
92+
(Policy::Threshold(k1, subs1), Policy::Threshold(k2, subs2)) => {
93+
if k1 == k2 && subs1.len() == subs2.len() {
94+
let mut ind_a = HashSet::new();
95+
let mut ind_b = HashSet::new();
96+
for i in 0..subs1.len() {
97+
let sub_a = &subs1[i];
98+
let b_pos = subs2.iter().position(|sub_b| sub_a == sub_b);
99+
match b_pos {
100+
Some(j) => {
101+
ind_a.insert(i);
102+
ind_b.insert(j);
103+
}
104+
None => {}
105+
}
106+
}
107+
let diff_a: Vec<_> = subs1
108+
.into_iter()
109+
.enumerate()
110+
.filter(|(i, _x)| !ind_a.contains(i))
111+
.map(|(_i, p)| p)
112+
.collect();
113+
let diff_b = subs2
114+
.into_iter()
115+
.enumerate()
116+
.filter(|(i, _x)| !ind_b.contains(i))
117+
.map(|(_i, p)| p)
118+
.collect::<Vec<_>>();
119+
PolicyDiff::_new(diff_a, diff_b)
120+
} else {
121+
PolicyDiff::_new(
122+
vec![Policy::Threshold(k1, subs1)],
123+
vec![Policy::Threshold(k2, subs2)],
124+
)
125+
}
98126
}
99-
a
127+
(x, y) => PolicyDiff::_new(vec![x], vec![y]),
100128
}
101-
(x, y) => Self::_new(vec![x], vec![y]),
102129
}
130+
131+
new_helper(a.normalized(), b.normalized())
103132
}
104133
}
105134

@@ -214,6 +243,67 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
214243
}
215244
}
216245

246+
fn _pprint_diff(&self, other: &Policy<Pk>, prefix: String, last: bool) {
247+
match (self, other) {
248+
(x, y) if x == y => x._pprint_tree(prefix, last),
249+
(Policy::Threshold(k1, subs1), Policy::Threshold(k2, subs2))
250+
if k1 == k2 && subs1.len() == subs2.len() =>
251+
{
252+
let prefix_current = if last { "`-- " } else { "|-- " };
253+
254+
println!("{}{}{}", prefix, prefix_current, self.term_name());
255+
256+
let prefix_child = if last { " " } else { "| " };
257+
let prefix = prefix + prefix_child;
258+
259+
let last_child = subs1.len() - 1;
260+
261+
// Hashsets to maintain which children of subs are matched
262+
// onto children from others
263+
let mut ind_a = HashSet::new();
264+
let mut ind_b = HashSet::new();
265+
for i in 0..subs1.len() {
266+
let sub_a = &subs1[i];
267+
let b_pos = subs2.iter().position(|sub_b| sub_a == sub_b);
268+
match b_pos {
269+
Some(j) => {
270+
ind_a.insert(i);
271+
ind_b.insert(j);
272+
}
273+
None => {}
274+
}
275+
}
276+
let mut j = 0;
277+
for (i, sub_a) in subs1.iter().enumerate() {
278+
// The position in subs2
279+
if ind_a.contains(&i) {
280+
sub_a._pprint_tree(prefix.to_string(), last);
281+
} else {
282+
while ind_b.contains(&j) {
283+
j = j + 1;
284+
}
285+
sub_a._pprint_diff(&subs2[j], prefix.to_string(), i == last_child);
286+
}
287+
}
288+
}
289+
(x, y) => {
290+
let red = "\x1b[0;31m";
291+
let nc = "\x1b[0m";
292+
let green = "\x1b[0;32m";
293+
print!("{}", green);
294+
x._pprint_tree(prefix.clone(), last);
295+
print!("{}{}", nc, red);
296+
y._pprint_tree(prefix, last);
297+
print!("{}", nc);
298+
}
299+
}
300+
}
301+
302+
/// Pretty Print a tree
303+
pub fn pprint_diff(&self, other: &Policy<Pk>) {
304+
self._pprint_diff(other, "".to_string(), true);
305+
}
306+
217307
/// This function computes whether the current policy entails the second one.
218308
/// A |- B means every satisfaction of A is also a satisfaction of B.
219309
/// This implementation will run slow for larger policies but should be sufficient for
@@ -863,6 +953,34 @@ mod tests {
863953
assert_eq!(policy.minimum_n_keys(), Some(0));
864954
}
865955

956+
#[test]
957+
fn policy_diff() {
958+
let pol1 = StringPolicy::from_str("or(pkh(A),pkh(C))").unwrap();
959+
let pol2 = StringPolicy::from_str("or(pkh(B),pkh(C))").unwrap();
960+
let diff = PolicyDiff::new(pol1.clone(), pol2.clone());
961+
assert_eq!(
962+
diff,
963+
PolicyDiff::new(
964+
StringPolicy::from_str("pkh(A)").unwrap(),
965+
StringPolicy::from_str("pkh(B)").unwrap()
966+
)
967+
);
968+
// Uncomment for pprint
969+
// pol1.pprint_diff(&pol2);
970+
971+
let pol1 = StringPolicy::from_str("or(pkh(A),pkh(C))").unwrap();
972+
// change the order
973+
let pol2 = StringPolicy::from_str("or(pkh(C),and(pkh(B),older(9)))").unwrap();
974+
let diff = PolicyDiff::new(pol1.clone(), pol2.clone());
975+
assert_eq!(
976+
diff,
977+
PolicyDiff::new(
978+
StringPolicy::from_str("pkh(A)").unwrap(),
979+
StringPolicy::from_str("and(pkh(B),older(9))").unwrap()
980+
)
981+
);
982+
}
983+
866984
#[test]
867985
fn entailment_liquid_test() {
868986
//liquid policy

0 commit comments

Comments
 (0)