3
3
binToHex ,
4
4
decodeCashAddress ,
5
5
hexToBin ,
6
+ Input ,
6
7
isHex ,
7
8
TransactionBch ,
8
9
utf8ToBin ,
@@ -30,10 +31,10 @@ import { Contract } from './Contract.js';
30
31
import { DebugResults , debugTemplate } from './debugging.js' ;
31
32
import {
32
33
HashType ,
34
+ isContractUnlocker ,
33
35
isP2PKHUnlocker ,
34
36
isStandardUnlockableUtxo ,
35
37
isUnlockableUtxo ,
36
- isUtxoP2PKH ,
37
38
LibauthTokenDetails ,
38
39
Output ,
39
40
SignatureAlgorithm ,
@@ -43,11 +44,10 @@ import {
43
44
Utxo ,
44
45
} from './interfaces.js' ;
45
46
import SignatureTemplate from './SignatureTemplate.js' ;
46
- import { addressToLockScript , extendedStringify , zip } from './utils.js' ;
47
+ import { addressToLockScript , extendedStringify , getSignatureAndPubkeyFromP2PKHInput , zip } from './utils.js' ;
47
48
import { TransactionBuilder } from './TransactionBuilder.js' ;
48
49
import { deflate } from 'pako' ;
49
50
50
-
51
51
/**
52
52
* Generates template entities for P2PKH (Pay to Public Key Hash) placeholder scripts.
53
53
*
@@ -61,16 +61,22 @@ export const generateTemplateEntitiesP2PKH = (
61
61
const lockScriptName = `p2pkh_placeholder_lock_${ inputIndex } ` ;
62
62
const unlockScriptName = `p2pkh_placeholder_unlock_${ inputIndex } ` ;
63
63
64
+ // TODO: Add descriptions
64
65
return {
65
66
[ `signer_${ inputIndex } ` ] : {
66
67
scripts : [ lockScriptName , unlockScriptName ] ,
67
- description : `placeholder_key_ ${ inputIndex } ` ,
68
+ description : `P2PKH data for input ${ inputIndex } ` ,
68
69
name : `P2PKH Signer (input #${ inputIndex } )` ,
69
70
variables : {
70
- [ `placeholder_key_${ inputIndex } ` ] : {
71
+ [ `signature_${ inputIndex } ` ] : {
72
+ description : '' ,
73
+ name : `P2PKH Signature (input #${ inputIndex } )` ,
74
+ type : 'WalletData' ,
75
+ } ,
76
+ [ `public_key_${ inputIndex } ` ] : {
71
77
description : '' ,
72
- name : `P2PKH Placeholder Key (input #${ inputIndex } )` ,
73
- type : 'Key ' ,
78
+ name : `P2PKH public key (input #${ inputIndex } )` ,
79
+ type : 'WalletData ' ,
74
80
} ,
75
81
} ,
76
82
} ,
@@ -152,30 +158,29 @@ const createWalletTemplateVariables = (
152
158
*
153
159
*/
154
160
export const generateTemplateScriptsP2PKH = (
155
- template : SignatureTemplate ,
156
161
inputIndex : number ,
157
162
) : WalletTemplate [ 'scripts' ] => {
158
163
const scripts : WalletTemplate [ 'scripts' ] = { } ;
159
164
const lockScriptName = `p2pkh_placeholder_lock_${ inputIndex } ` ;
160
165
const unlockScriptName = `p2pkh_placeholder_unlock_${ inputIndex } ` ;
161
- const placeholderKeyName = `placeholder_key_${ inputIndex } ` ;
162
166
163
- const signatureAlgorithmName = getSignatureAlgorithmName ( template . getSignatureAlgorithm ( ) ) ;
164
- const hashtypeName = getHashTypeName ( template . getHashType ( false ) ) ;
165
- const signatureString = ` ${ placeholderKeyName } . ${ signatureAlgorithmName } . ${ hashtypeName } ` ;
167
+ const signatureString = `signature_ ${ inputIndex } ` ;
168
+ const publicKeyString = `public_key_ ${ inputIndex } ` ;
169
+
166
170
// add extra unlocking and locking script for P2PKH inputs spent alongside our contract
167
171
// this is needed for correct cross-references in the template
168
172
scripts [ unlockScriptName ] = {
173
+ passes : [ `P2PKH_spend_input${ inputIndex } _evaluate` ] ,
169
174
name : `P2PKH Unlock (input #${ inputIndex } )` ,
170
175
script :
171
- `<${ signatureString } >\n<${ placeholderKeyName } .public_key >` ,
176
+ `<${ signatureString } >\n<${ publicKeyString } >` ,
172
177
unlocks : lockScriptName ,
173
178
} ;
174
179
scripts [ lockScriptName ] = {
175
180
lockingType : 'standard' ,
176
181
name : `P2PKH Lock (input #${ inputIndex } )` ,
177
182
script :
178
- `OP_DUP\nOP_HASH160 <$(<${ placeholderKeyName } .public_key > OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG` ,
183
+ `OP_DUP\nOP_HASH160 <$(<${ publicKeyString } > OP_HASH160\n)> OP_EQUALVERIFY\nOP_CHECKSIG` ,
179
184
} ;
180
185
181
186
return scripts ;
@@ -275,6 +280,7 @@ export const generateTemplateScenarios = (
275
280
const encodedConstructorArgs = contract . encodedConstructorArgs ;
276
281
const scenarioIdentifier = `${ artifact . contractName } _${ abiFunction . name } _input${ inputIndex } _evaluate` ;
277
282
283
+ // TODO: Update scenario descriptions
278
284
const scenarios = {
279
285
// single scenario to spend out transaction under test given the CashScript parameters provided
280
286
[ scenarioIdentifier ] : {
@@ -288,12 +294,13 @@ export const generateTemplateScenarios = (
288
294
} ,
289
295
currentBlockHeight : 2 ,
290
296
currentBlockTime : Math . round ( + new Date ( ) / 1000 ) ,
297
+ // TODO: remove usage of private keys in P2SH scenarios as well
291
298
keys : {
292
299
privateKeys : generateTemplateScenarioKeys ( abiFunction . inputs , encodedFunctionArgs ) ,
293
300
} ,
294
301
} ,
295
302
transaction : generateTemplateScenarioTransaction ( contract , libauthTransaction , csTransaction , inputIndex ) ,
296
- sourceOutputs : generateTemplateScenarioSourceOutputs ( csTransaction , inputIndex ) ,
303
+ sourceOutputs : generateTemplateScenarioSourceOutputs ( csTransaction , libauthTransaction , inputIndex ) ,
297
304
} ,
298
305
} ;
299
306
@@ -308,20 +315,53 @@ export const generateTemplateScenarios = (
308
315
return scenarios ;
309
316
} ;
310
317
318
+ export const generateTemplateScenariosP2PKH = (
319
+ libauthTransaction : TransactionBch ,
320
+ csTransaction : TransactionType ,
321
+ inputIndex : number ,
322
+ ) : WalletTemplate [ 'scenarios' ] => {
323
+ const scenarioIdentifier = `P2PKH_spend_input${ inputIndex } _evaluate` ;
324
+
325
+ const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput ( libauthTransaction . inputs [ inputIndex ] ) ;
326
+
327
+ // TODO: Update scenario descriptions
328
+ const scenarios = {
329
+ // single scenario to spend out transaction under test given the CashScript parameters provided
330
+ [ scenarioIdentifier ] : {
331
+ name : `Evaluate P2PKH spend (input #${ inputIndex } )` ,
332
+ description : 'An example evaluation where this script execution passes.' ,
333
+ data : {
334
+ // encode values for the variables defined above in `entities` property
335
+ bytecode : {
336
+ [ `signature_${ inputIndex } ` ] : `0x${ binToHex ( signature ) } ` ,
337
+ [ `public_key_${ inputIndex } ` ] : `0x${ binToHex ( publicKey ) } ` ,
338
+ } ,
339
+ currentBlockHeight : 2 ,
340
+ currentBlockTime : Math . round ( + new Date ( ) / 1000 ) ,
341
+ } ,
342
+ transaction : generateTemplateScenarioTransaction ( undefined , libauthTransaction , csTransaction , inputIndex ) ,
343
+ sourceOutputs : generateTemplateScenarioSourceOutputs ( csTransaction , libauthTransaction , inputIndex ) ,
344
+ } ,
345
+ } ;
346
+
347
+ return scenarios ;
348
+ } ;
349
+
311
350
const generateTemplateScenarioTransaction = (
312
- contract : Contract ,
351
+ contract : Contract | undefined ,
313
352
libauthTransaction : TransactionBch ,
314
353
csTransaction : TransactionType ,
315
354
slotIndex : number ,
316
355
) : WalletTemplateScenario [ 'transaction' ] => {
317
356
const inputs = libauthTransaction . inputs . map ( ( input , inputIndex ) => {
318
357
const csInput = csTransaction . inputs [ inputIndex ] as Utxo ;
358
+ const libauthInput = libauthTransaction . inputs [ inputIndex ] ;
319
359
320
360
return {
321
361
outpointIndex : input . outpointIndex ,
322
362
outpointTransactionHash : binToHex ( input . outpointTransactionHash ) ,
323
363
sequenceNumber : input . sequenceNumber ,
324
- unlockingBytecode : generateTemplateScenarioBytecode ( csInput , inputIndex , 'p2pkh_placeholder_unlock' , slotIndex === inputIndex ) ,
364
+ unlockingBytecode : generateTemplateScenarioBytecode ( csInput , libauthInput , inputIndex , 'p2pkh_placeholder_unlock' , slotIndex === inputIndex ) ,
325
365
} as WalletTemplateScenarioInput ;
326
366
} ) ;
327
367
@@ -330,8 +370,16 @@ const generateTemplateScenarioTransaction = (
330
370
const outputs = libauthTransaction . outputs . map ( ( output , index ) => {
331
371
const csOutput = csTransaction . outputs [ index ] ;
332
372
373
+ if ( csOutput && contract ) {
374
+ return {
375
+ lockingBytecode : generateTemplateScenarioTransactionOutputLockingBytecode ( csOutput , contract ) ,
376
+ token : serialiseTokenDetails ( output . token ) ,
377
+ valueSatoshis : Number ( output . valueSatoshis ) ,
378
+ } as WalletTemplateScenarioTransactionOutput ;
379
+ }
380
+
333
381
return {
334
- lockingBytecode : generateTemplateScenarioTransactionOutputLockingBytecode ( csOutput , contract ) ,
382
+ lockingBytecode : ` ${ binToHex ( output . lockingBytecode ) } ` ,
335
383
token : serialiseTokenDetails ( output . token ) ,
336
384
valueSatoshis : Number ( output . valueSatoshis ) ,
337
385
} as WalletTemplateScenarioTransactionOutput ;
@@ -344,11 +392,14 @@ const generateTemplateScenarioTransaction = (
344
392
345
393
const generateTemplateScenarioSourceOutputs = (
346
394
csTransaction : TransactionType ,
395
+ libauthTransaction : TransactionBch ,
347
396
slotIndex : number ,
348
397
) : Array < WalletTemplateScenarioOutput < true > > => {
349
398
return csTransaction . inputs . map ( ( input , inputIndex ) => {
399
+ const libauthInput = libauthTransaction . inputs [ inputIndex ] ;
400
+
350
401
return {
351
- lockingBytecode : generateTemplateScenarioBytecode ( input , inputIndex , 'p2pkh_placeholder_lock' , inputIndex === slotIndex ) ,
402
+ lockingBytecode : generateTemplateScenarioBytecode ( input , libauthInput , inputIndex , 'p2pkh_placeholder_lock' , inputIndex === slotIndex ) ,
352
403
valueSatoshis : Number ( input . satoshis ) ,
353
404
token : serialiseTokenDetails ( input . token ) ,
354
405
} ;
@@ -409,18 +460,14 @@ export const getLibauthTemplates = (
409
460
410
461
// We can typecast this because we check that all inputs are standard unlockable at the top of this function
411
462
for ( const [ inputIndex , input ] of ( txn . inputs as StandardUnlockableUtxo [ ] ) . entries ( ) ) {
412
- // If template exists on the input, it indicates this is a P2PKH (Pay to Public Key Hash) input
413
- if ( 'template' in input . unlocker ) {
414
- // @ts -ignore TODO: Remove UtxoP2PKH type and only use UnlockableUtxo in Libauth Template generation
415
- input . template = input . unlocker ?. template ; // Added to support P2PKH inputs in buildTemplate
463
+ if ( isP2PKHUnlocker ( input . unlocker ) ) {
416
464
Object . assign ( p2pkhEntities , generateTemplateEntitiesP2PKH ( inputIndex ) ) ;
417
- Object . assign ( p2pkhScripts , generateTemplateScriptsP2PKH ( input . unlocker . template , inputIndex ) ) ;
418
-
465
+ Object . assign ( p2pkhScripts , generateTemplateScriptsP2PKH ( inputIndex ) ) ;
466
+ Object . assign ( scenarios , generateTemplateScenariosP2PKH ( libauthTransaction , csTransaction , inputIndex ) ) ;
419
467
continue ;
420
468
}
421
469
422
- // If contract exists on the input, it indicates this is a contract input
423
- if ( 'contract' in input . unlocker ) {
470
+ if ( isContractUnlocker ( input . unlocker ) ) {
424
471
const contract = input . unlocker ?. contract ;
425
472
const abiFunction = input . unlocker ?. abiFunction ;
426
473
@@ -537,7 +584,7 @@ export const getLibauthTemplates = (
537
584
538
585
export const debugLibauthTemplate = ( template : WalletTemplate , transaction : TransactionBuilder ) : DebugResults => {
539
586
const allArtifacts = transaction . inputs
540
- . map ( input => 'contract' in input . unlocker ? input . unlocker . contract : undefined )
587
+ . map ( input => isContractUnlocker ( input . unlocker ) ? input . unlocker . contract : undefined )
541
588
. filter ( ( contract ) : contract is Contract => Boolean ( contract ) )
542
589
. map ( contract => contract . artifact ) ;
543
590
@@ -575,17 +622,19 @@ const generateLockingScriptParams = (
575
622
576
623
export const generateUnlockingScriptParams = (
577
624
csInput : StandardUnlockableUtxo ,
625
+ libauthInput : Input ,
578
626
p2pkhScriptNameTemplate : string ,
579
627
inputIndex : number ,
580
628
) : WalletTemplateScenarioBytecode => {
581
629
if ( isP2PKHUnlocker ( csInput . unlocker ) ) {
630
+ const { signature, publicKey } = getSignatureAndPubkeyFromP2PKHInput ( libauthInput ) ;
631
+
582
632
return {
583
633
script : `${ p2pkhScriptNameTemplate } _${ inputIndex } ` ,
584
634
overrides : {
585
- keys : {
586
- privateKeys : {
587
- [ `placeholder_key_${ inputIndex } ` ] : binToHex ( csInput . unlocker . template . privateKey ) ,
588
- } ,
635
+ bytecode : {
636
+ [ `signature_${ inputIndex } ` ] : `0x${ binToHex ( signature ) } ` ,
637
+ [ `public_key_${ inputIndex } ` ] : `0x${ binToHex ( publicKey ) } ` ,
589
638
} ,
590
639
} ,
591
640
} ;
@@ -604,6 +653,7 @@ export const generateUnlockingScriptParams = (
604
653
...generateTemplateScenarioParametersValues ( abiFunction . inputs , encodedFunctionArgs ) ,
605
654
...generateTemplateScenarioParametersValues ( contract . artifact . constructorInputs , contract . encodedConstructorArgs ) ,
606
655
} ,
656
+ // TODO: remove usage of private keys in P2SH scenarios as well
607
657
keys : {
608
658
privateKeys : generateTemplateScenarioKeys ( abiFunction . inputs , encodedFunctionArgs ) ,
609
659
} ,
@@ -745,29 +795,16 @@ export const generateTemplateScenarioKeys = (
745
795
746
796
// Used for generating the locking / unlocking bytecode for source outputs and inputs
747
797
export const generateTemplateScenarioBytecode = (
748
- input : Utxo , inputIndex : number , p2pkhScriptNameTemplate : string , insertSlot ?: boolean ,
798
+ input : Utxo ,
799
+ libauthInput : Input ,
800
+ inputIndex : number ,
801
+ p2pkhScriptNameTemplate : string ,
802
+ insertSlot ?: boolean ,
749
803
) : WalletTemplateScenarioBytecode | [ 'slot' ] => {
750
804
if ( insertSlot ) return [ 'slot' ] ;
751
805
752
- const p2pkhScriptName = `${ p2pkhScriptNameTemplate } _${ inputIndex } ` ;
753
- const placeholderKeyName = `placeholder_key_${ inputIndex } ` ;
754
-
755
- // This is for P2PKH inputs in the old transaction builder (TODO: remove when we remove old transaction builder)
756
- if ( isUtxoP2PKH ( input ) ) {
757
- return {
758
- script : p2pkhScriptName ,
759
- overrides : {
760
- keys : {
761
- privateKeys : {
762
- [ placeholderKeyName ] : binToHex ( input . template . privateKey ) ,
763
- } ,
764
- } ,
765
- } ,
766
- } ;
767
- }
768
-
769
806
if ( isUnlockableUtxo ( input ) && isStandardUnlockableUtxo ( input ) ) {
770
- return generateUnlockingScriptParams ( input , p2pkhScriptNameTemplate , inputIndex ) ;
807
+ return generateUnlockingScriptParams ( input , libauthInput , p2pkhScriptNameTemplate , inputIndex ) ;
771
808
}
772
809
773
810
// 'slot' means that we are currently evaluating this specific input,
0 commit comments