-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.rs
168 lines (143 loc) · 5.63 KB
/
main.rs
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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use bitcoin_cash::{
encode_bitcoin_code, Address, ByteArray, Hashed, Opcode::*, Prefix, Pubkey, Script,
Sha256d, SigHashFlags, Signatory, TxBuilder, TxOutpoint, TxOutput, TxPreimage, UnsignedTxInput,
ECC, MAX_SIGNATURE_SIZE,
};
use bitcoin_cash_ecc::init_ecc;
use iguana_ws::run_iguana_ws;
/// Struct of all the contract construction parameters
/// You can put literally anything in here, even with a lifetime.
pub struct P2ReversePkParams {
pk: Pubkey,
}
/// bitcoin-cash script macro. Will create two things:
/// - function script_p2_reverse_pk (instantiates the contract given a &P2ReversePkParams)
/// - struct P2ReversePkInputs (struct containing all the stuff that needs to be provided to be
/// able to spend the contract, aka inputs)
#[bitcoin_cash::script(P2ReversePkInputs)]
pub fn script_p2_reverse_pk(
// The first fn parameter is special, it's always the constructor params
params: &P2ReversePkParams,
// All other fn paramters are the inputs for spending the contract.
// They need to implement `BitcoinDataType`
sig: ByteArray,
) {
let rev_pubkey = {
// push reversed pubkey
params
.pk
.as_slice()
.iter()
.rev()
.cloned()
.collect::<Vec<u8>>()
};
// reverse pubkey using OP_REVERSEBYTES
let pubkey = OP_REVERSEBYTES(rev_pubkey);
// check signature
OP_CHECKSIG(sig, pubkey);
}
/// A signatory knows how to spend a certain contract, here P2ReversePk.
/// It contains all the data required for building the input script except
/// the signatures. (Which in this case is nothing.)
pub struct P2ReversePkSignatory;
/// Implementation of Signatory for P2ReversePkSignatory.
impl Signatory for P2ReversePkSignatory {
/// The struct with the input script (generated by the script_p2_reverse_pk macro)
type Script = P2ReversePkInputs;
/// The required signatures. Here, just a tuple with one entry, but could be anything.
type Signatures = (ByteArray,);
/// The sig hash flags for the signature. SigHashFlags::DEFAULT for FORKID|ALL
fn sig_hash_flags(&self) -> Vec<SigHashFlags> {
vec![SigHashFlags::DEFAULT]
}
/// Generate placeholder signatures for fee estimation. This should always be the
/// largest possible signature.
fn placeholder_signatures(&self) -> Self::Signatures {
(vec![0; MAX_SIGNATURE_SIZE].into(),)
}
/// This actually builds the input script (here, ).
fn build_script(
&self,
// One preimage for each entry in sig_hash_flags
_tx_preimages: &[TxPreimage],
// Used TxBuilder
_unsigned_tx: &TxBuilder,
// Signatures for the input script
sigs: (ByteArray,),
// The lock script (aka redeem script, generated by calling script_p2_reverse_pk)
_lock_script: &Script,
// The output of the transaction (overlaps with TxBuilder)
_tx_outputs: &[TxOutput],
) -> Self::Script {
P2ReversePkInputs {
// input script only requires a single signature
sig: sigs.0.concat(vec![self.sig_hash_flags()[0].bits() as u8]),
}
}
}
fn main() {
run_iguana_ws(|| {
let ecc = init_ecc();
let sk = [0x37; 32]; // super secure private key
let pk = ecc.derive_pubkey(&sk)?; // derive pubkey
// outpoint of the contract
let prev_out_hash = "21e8c0172050469425f2d699b96bbf638db8143e9223a257ef4032dc9816ac62";
let prev_out_idx = 0;
let prev_out_value = 10_000;
// instantiate contract
let script = script_p2_reverse_pk(&P2ReversePkParams { pk });
// print out contract address for funding
println!(
"{}",
Address::from_redeem_script(Prefix::default(), script.clone().into())?.cash_addr()
);
// address to send to
let recipient =
Address::from_cash_addr("bitcoincash:qzjxz2yajgc9fe0nxm6k7w0yynxpqpp2eqrzcp548t")?;
// Step 1: instantiate normal transaction (version 1, no locktime)
let mut tx_build = TxBuilder::new_simple();
// add input, keep backlink in `input1`
let input1 = tx_build.add_input(
UnsignedTxInput {
prev_out: TxOutpoint {
tx_hash: Sha256d::from_hex_le(prev_out_hash)?,
vout: prev_out_idx,
},
sequence: 0xffff_ffff,
value: prev_out_value,
},
script,
P2ReversePkSignatory,
);
let fee_per_kb = 1000;
let lower_bound = 0;
let upper_bound = std::u64::MAX;
let precedence = 0;
tx_build.add_leftover_output(
fee_per_kb,
lower_bound,
upper_bound,
precedence,
recipient.into(),
);
// Step 2: build unsigned transaction (makes tx_build unchangable)
let mut unsigned_tx = tx_build.build()?;
// Step 3: get preimages for transaction
// for each input there is a vector of preimages (one for each item in sig_hash_flags)
let preimages = unsigned_tx.preimages();
// Step 4: sign preimage
let preimage = &preimages[0][0];
let sig = ecc.sign(&sk, Sha256d::digest(preimage.to_byte_array()))?;
// Step 5: feed signature to input
unsigned_tx.sign_input(input1, (sig,));
// Step 6: complete transaction
let tx = unsigned_tx.complete_tx();
// serialize transaction
let tx_ser = encode_bitcoin_code(&tx)?;
let tx_hex = hex::encode(&tx_ser);
println!("tx hex: {}", tx_hex);
// return tx to ws
Ok(tx)
});
}