1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
//! A peg-out (SPEC 7): a sidechain transaction paying a **burn output**,
//! `OP_RETURN pegout:<parent output script hex>` with a value of at least
//! `pegoutMin`. The value leaves the supply; the peg holders owe it to that
//! script on the parent. A port of the `pegout` branch of
//! `siding/lib/spend.mjs buildSpend` (`siding send --pegout`).
//!
//! The destination is a parent address (any prefix) or a script hex; only
//! the script matters, and a `bc1…` and a `tb1…` address with the same
//! program name the same script, so no network check applies here — the
//! peg holders' wallet on the parent decides what it can pay. The marker is
//! [`sidestr_core::marker::pegout_marker`], parsed back with
//! [`sidestr_core::marker::parse_pegout`] before the build goes on, so a
//! script the chain would refuse (under 2 or over 40 bytes) fails here.
//!
//! ```
//! use bitcoin::secp256k1::SecretKey;
//! use sidestr_core::document::ChainDocument;
//! use sidestr_core::marker::parse_pegout;
//! use sidestr_wallet::burn::{build_burn, BurnRequest};
//! use sidestr_wallet::coins::Coin;
//! use sidestr_wallet::{Error, PlainKey, Permissive};
//!
//! let chain = ChainDocument::from_json(r#"{"id":"sidestr:example","name":"example","parent":"tbtc4",
//! "challenge":"5120aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","powLimit":"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
//! "addressPrefix":"ex","genesisTime":1790000000,"pegs":[],"minFeeRate":1,"pegoutMin":10000}"#).unwrap();
//! let me = PlainKey::new(SecretKey::from_slice(&[9u8; 32]).unwrap());
//! let coins = vec![Coin { outpoint: format!("{}:0", "ab".repeat(32)).parse().unwrap(), value: 100_000, height: 5, coinbase: false }];
//! let parent = "tb1pvts4e2zcrujj9zey3kadyfgh2xs93v8va8ae9ldhukpxy2n3848qyqurhc";
//!
//! let req = BurnRequest { chain: &chain, coins: &coins, tip_height: 10, to: parent, amount: 25_000, fee: None };
//! let b = build_burn(&req, &me, &Permissive).unwrap();
//! let named = parse_pegout(&b.tx.output[0].script_pubkey).unwrap();
//! assert_eq!(named, sidestr_core::address::address_to_script(parent).unwrap().to_hex_string());
//! assert_eq!(b.tx.output[0].value.to_sat(), 25_000);
//! assert!(b.note.unwrap().starts_with("peg-out: 25000 sats burn here"));
//!
//! // below pegoutMin is refused before anything is signed
//! assert!(matches!(build_burn(&BurnRequest { amount: 9_999, ..req }, &me, &Permissive), Err(Error::BelowPegoutMin { min: 10000, .. })));
//! ```
use ChainDocument;
use ;
use crateCoin;
use crate;
use crateSpendSigner;
use crate;
use crate;
/// What a burn is built from.
/// Build and sign a peg-out burn. [`Error::BelowPegoutMin`] before any coin
/// is touched; then the checks of [`crate::spend::build_spend`], with the
/// policy told [`IntentKind::Burn`] and the parent script.