@@ -136,87 +136,242 @@ pub fn script(tokens: TokenStream) -> TokenStream {
136
136
#[ proc_macro]
137
137
pub fn define_pushable ( _: TokenStream ) -> TokenStream {
138
138
quote ! (
139
- pub mod pushable {
140
-
141
- use bitcoin:: blockdata:: opcodes:: Opcode ;
142
- use bitcoin:: blockdata:: script:: Builder ;
143
- use bitcoin:: blockdata:: script:: PushBytesBuf ;
144
- use std:: convert:: TryFrom ;
145
-
146
- // We split up the bitcoin_script_push function to allow pushing a single u8 value as
147
- // an integer (i64), Vec<u8> as raw data and Vec<T> for any T: Pushable object that is
148
- // not a u8. Otherwise the Vec<u8> and Vec<T: Pushable> definitions conflict.
149
- trait NotU8Pushable {
150
- fn bitcoin_script_push( self , builder: Builder ) -> Builder ;
151
- }
152
- impl NotU8Pushable for Opcode {
153
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
154
- builder. push_opcode( self )
139
+ pub mod pushable {
140
+
141
+ use bitcoin:: blockdata:: opcodes:: { all:: * , Opcode } ;
142
+ use bitcoin:: blockdata:: script:: Builder as BitcoinBuilder ;
143
+ use bitcoin:: blockdata:: script:: { PushBytesBuf , Script } ;
144
+ use std:: convert:: TryFrom ;
145
+
146
+ pub struct Builder ( pub BitcoinBuilder ) ;
147
+
148
+ pub fn check_optimality( opcode: Opcode , next_opcode: Opcode ) {
149
+ match ( opcode, next_opcode) {
150
+ ( OP_PUSHNUM_1 , OP_ADD ) => eprintln!( "Script can be optimized: 1 OP_ADD => OP_1ADD" ) ,
151
+ ( OP_PUSHNUM_1 , OP_SUB ) => eprintln!( "Script can be optimized: 1 OP_SUB => OP_1SUB" ) ,
152
+ ( OP_DROP , OP_DROP ) => {
153
+ eprintln!( "Script can be optimized: OP_DROP OP_DROP => OP_2DROP" )
155
154
}
156
- }
157
- impl NotU8Pushable for i64 {
158
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
159
- builder. push_int( self )
155
+ ( OP_PUSHBYTES_0 , OP_ROLL ) => eprintln!( "Script can be optimized: Remove 0 OP_ROLL" ) ,
156
+ ( OP_PUSHNUM_1 , OP_ROLL ) => {
157
+ eprintln!( "Script can be optimized: 1 OP_ROLL => OP_SWAP" )
160
158
}
161
- }
162
- impl NotU8Pushable for i32 {
163
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
164
- builder. push_int( self as i64 )
159
+ ( OP_PUSHNUM_2 , OP_ROLL ) => {
160
+ eprintln!( "Script can be optimized: 2 OP_ROLL => OP_ROT" )
165
161
}
166
- }
167
- impl NotU8Pushable for u32 {
168
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
169
- builder. push_int( self as i64 )
162
+ ( OP_PUSHBYTES_0 , OP_PICK ) => {
163
+ eprintln!( "Script can be optimized: 0 OP_PICK => OP_DUP" )
170
164
}
171
- }
172
- impl NotU8Pushable for usize {
173
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
174
- builder. push_int(
175
- i64 :: try_from( self ) . unwrap_or_else( |_| panic!( "Usize does not fit in i64" ) ) ,
176
- )
165
+ ( OP_PUSHBYTES_1 , OP_PICK ) => {
166
+ eprintln!( "Script can be optimized: 1 OP_PICK => OP_OVER" )
177
167
}
168
+ ( OP_IF , OP_ELSE ) => eprintln!( "Script can be optimized: OP_IF OP_ELSE => OP_NOTIF" ) ,
169
+ ( _, _) => ( ) ,
178
170
}
179
- impl NotU8Pushable for Vec <u8 > {
180
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
181
- builder. push_slice( PushBytesBuf :: try_from( self ) . unwrap( ) )
182
- }
171
+ }
172
+
173
+ impl Builder {
174
+ pub fn new( ) -> Self {
175
+ let builder = BitcoinBuilder :: new( ) ;
176
+ Builder ( builder)
183
177
}
184
- impl NotU8Pushable for :: bitcoin:: PublicKey {
185
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
186
- builder. push_key( & self )
187
- }
178
+
179
+ pub fn as_bytes( & self ) -> & [ u8 ] {
180
+ self . 0 . as_bytes( )
188
181
}
189
- impl NotU8Pushable for :: bitcoin:: ScriptBuf {
190
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
191
- let mut script_vec = vec![ ] ;
192
- script_vec. extend_from_slice( builder. as_bytes( ) ) ;
193
- script_vec. extend_from_slice( self . as_bytes( ) ) ;
194
- Builder :: from( script_vec)
195
- }
182
+
183
+ pub fn as_script( & self ) -> & Script {
184
+ self . 0 . as_script( )
196
185
}
197
- impl <T : NotU8Pushable > NotU8Pushable for Vec <T > {
198
- fn bitcoin_script_push( self , mut builder: Builder ) -> Builder {
199
- for pushable in self {
200
- builder = pushable. bitcoin_script_push( builder) ;
201
- }
202
- builder
203
- }
186
+
187
+ pub fn push_opcode( mut self , opcode: Opcode ) -> Builder {
188
+ match self . as_script( ) . instructions_minimal( ) . last( ) {
189
+ Some ( instr_result) => match instr_result {
190
+ Ok ( instr) => match instr {
191
+ bitcoin:: script:: Instruction :: PushBytes ( push_bytes) => {
192
+ if push_bytes. as_bytes( ) == [ ] {
193
+ check_optimality( :: bitcoin:: opcodes:: all:: OP_PUSHBYTES_0 , opcode)
194
+ }
195
+ } ,
196
+ bitcoin:: script:: Instruction :: Op ( previous_opcode) => {
197
+ check_optimality( previous_opcode, opcode)
198
+ }
199
+ } ,
200
+ Err ( _) => eprintln!( "Script includes non-minimal pushes." ) ,
201
+ } ,
202
+ None => ( ) ,
203
+ } ;
204
+ self . 0 = self . 0 . push_opcode( opcode) ;
205
+ self
206
+ }
207
+
208
+ pub fn push_int( mut self , int: i64 ) -> Builder {
209
+ self . 0 = self . 0 . push_int( int) ;
210
+ self
204
211
}
205
- pub trait Pushable {
206
- fn bitcoin_script_push( self , builder: Builder ) -> Builder ;
212
+
213
+ pub fn push_slice( mut self , slice: PushBytesBuf ) -> Builder {
214
+ self . 0 = self . 0 . push_slice( slice) ;
215
+ self
216
+ }
217
+
218
+ pub fn push_key( mut self , pub_key: & :: bitcoin:: PublicKey ) -> Builder {
219
+ self . 0 = self . 0 . push_key( pub_key) ;
220
+ self
207
221
}
208
- impl <T : NotU8Pushable > Pushable for T {
209
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
210
- NotU8Pushable :: bitcoin_script_push( self , builder)
222
+
223
+ pub fn push_expression<T : Pushable >( self , expression: T ) -> Builder {
224
+ let last_opcode_index = match self . as_script( ) . instruction_indices_minimal( ) . last( )
225
+ {
226
+ Some ( instr_result) => match instr_result {
227
+ Ok ( ( index, instr) ) => match instr {
228
+ bitcoin:: script:: Instruction :: PushBytes ( push_bytes) => {
229
+ // Seperately handle OP_0 because it is turned into a PushBytes
230
+ // struct in the Script instruction
231
+ if push_bytes. as_bytes( ) == [ ] {
232
+ Some ( ( index, :: bitcoin:: opcodes:: all:: OP_PUSHBYTES_0 ) )
233
+ } else {
234
+ None
235
+ }
236
+ } ,
237
+ bitcoin:: script:: Instruction :: Op ( opcode) => Some ( ( index, opcode) ) ,
238
+ } ,
239
+ Err ( _) => {
240
+ eprintln!( "Script includes non-minimal pushes." ) ;
241
+ None
242
+ }
243
+ } ,
244
+ None => None ,
245
+ } ;
246
+ let builder = expression. bitcoin_script_push( self ) ;
247
+ if let Some ( ( last_index, previous_opcode) ) = last_opcode_index {
248
+ match builder
249
+ . as_script( )
250
+ . instructions_minimal( )
251
+ . skip( last_index + 1 )
252
+ . next( )
253
+ {
254
+ Some ( instr_result) => match instr_result {
255
+ Ok ( instr) => match instr {
256
+ bitcoin:: script:: Instruction :: PushBytes ( _) => ( ) ,
257
+ bitcoin:: script:: Instruction :: Op ( opcode) => {
258
+ check_optimality( previous_opcode, opcode)
259
+ }
260
+ } ,
261
+ Err ( _) => eprintln!( "Script includes non-minimal pushes." ) ,
262
+ } ,
263
+ None => eprintln!( "Script extends an empty script!" ) ,
264
+ } ;
211
265
}
266
+ builder
212
267
}
268
+ }
213
269
214
- impl Pushable for u8 {
215
- fn bitcoin_script_push( self , builder: Builder ) -> Builder {
216
- builder. push_int( self as i64 )
270
+ impl From <Vec <u8 >> for Builder {
271
+ fn from( v: Vec <u8 >) -> Builder {
272
+ let builder = BitcoinBuilder :: from( v) ;
273
+ Builder ( builder)
274
+ }
275
+ }
276
+ // We split up the bitcoin_script_push function to allow pushing a single u8 value as
277
+ // an integer (i64), Vec<u8> as raw data and Vec<T> for any T: Pushable object that is
278
+ // not a u8. Otherwise the Vec<u8> and Vec<T: Pushable> definitions conflict.
279
+ trait NotU8Pushable {
280
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder ;
281
+ }
282
+ impl NotU8Pushable for i64 {
283
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder {
284
+ builder. push_int( self )
285
+ }
286
+ }
287
+ impl NotU8Pushable for i32 {
288
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder {
289
+ builder. push_int( self as i64 )
290
+ }
291
+ }
292
+ impl NotU8Pushable for u32 {
293
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder {
294
+ builder. push_int( self as i64 )
295
+ }
296
+ }
297
+ impl NotU8Pushable for usize {
298
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder {
299
+ builder. push_int(
300
+ i64 :: try_from( self ) . unwrap_or_else( |_| panic!( "Usize does not fit in i64" ) ) ,
301
+ )
302
+ }
303
+ }
304
+ impl NotU8Pushable for Vec <u8 > {
305
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder {
306
+ builder. push_slice( PushBytesBuf :: try_from( self ) . unwrap( ) )
307
+ }
308
+ }
309
+ impl NotU8Pushable for :: bitcoin:: PublicKey {
310
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder {
311
+ builder. push_key( & self )
312
+ }
313
+ }
314
+ impl NotU8Pushable for :: bitcoin:: ScriptBuf {
315
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder {
316
+ let previous_opcode = match self . as_script( ) . instructions_minimal( ) . last( ) {
317
+ Some ( instr_result) => match instr_result {
318
+ Ok ( instr) => match instr {
319
+ bitcoin:: script:: Instruction :: PushBytes ( _) => None ,
320
+ bitcoin:: script:: Instruction :: Op ( previous_opcode) => {
321
+ Some ( previous_opcode)
322
+ }
323
+ } ,
324
+ Err ( _) => {
325
+ eprintln!( "Script includes non-minimal pushes." ) ;
326
+ None
327
+ }
328
+ } ,
329
+ None => None ,
330
+ } ;
331
+
332
+ if let Some ( previous_opcode) = previous_opcode {
333
+ match self . as_script( ) . instructions_minimal( ) . last( ) {
334
+ Some ( instr_result) => match instr_result {
335
+ Ok ( instr) => match instr {
336
+ bitcoin:: script:: Instruction :: PushBytes ( _) => ( ) ,
337
+ bitcoin:: script:: Instruction :: Op ( opcode) => {
338
+ check_optimality( previous_opcode, opcode)
339
+ }
340
+ } ,
341
+ Err ( _) => eprintln!( "Script includes non-minimal pushes." ) ,
342
+ } ,
343
+ None => ( ) ,
344
+ }
345
+ } ;
346
+ let mut script_vec = vec![ ] ;
347
+ script_vec. extend_from_slice( builder. as_bytes( ) ) ;
348
+ script_vec. extend_from_slice( self . as_bytes( ) ) ;
349
+ Builder :: from( script_vec)
350
+ }
351
+ }
352
+ impl <T : NotU8Pushable > NotU8Pushable for Vec <T > {
353
+ fn bitcoin_script_push( self , mut builder: Builder ) -> Builder {
354
+ for pushable in self {
355
+ builder = pushable. bitcoin_script_push( builder) ;
217
356
}
357
+ builder
358
+ }
359
+ }
360
+ pub trait Pushable {
361
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder ;
362
+ }
363
+ impl <T : NotU8Pushable > Pushable for T {
364
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder {
365
+ NotU8Pushable :: bitcoin_script_push( self , builder)
366
+ }
367
+ }
368
+
369
+ impl Pushable for u8 {
370
+ fn bitcoin_script_push( self , builder: Builder ) -> Builder {
371
+ builder. push_int( self as i64 )
218
372
}
219
373
}
374
+ }
220
375
)
221
376
. into ( )
222
377
}
0 commit comments