@@ -25,6 +25,7 @@ import {
2525 isP2PKHUnlocker ,
2626 isStandardUnlockableUtxo ,
2727 isUnlockableUtxo ,
28+ LibauthOutput ,
2829 Output ,
2930 StandardUnlockableUtxo ,
3031 Utxo ,
@@ -34,7 +35,7 @@ import { addressToLockScript, extendedStringify, zip } from '../utils.js';
3435import { TransactionBuilder } from '../TransactionBuilder.js' ;
3536import { deflate } from 'pako' ;
3637import MockNetworkProvider from '../network/MockNetworkProvider.js' ;
37- import { addHexPrefixExceptEmpty , DEFAULT_VM_TARGET , formatBytecodeForDebugging , formatParametersForDebugging , getLockScriptName , getSignatureAndPubkeyFromP2PKHInput , getUnlockScriptName , lockingBytecodeIsSetToSlot , serialiseTokenDetails } from './utils.js' ;
38+ import { addHexPrefixExceptEmpty , DEFAULT_VM_TARGET , formatBytecodeForDebugging , formatParametersForDebugging , getLockScriptName , getSignatureAndPubkeyFromP2PKHInput , getUnlockScriptName , serialiseTokenDetails } from './utils.js' ;
3839
3940// TODO: Add / improve descriptions throughout the template generation
4041
@@ -62,70 +63,6 @@ export const getLibauthTemplate = (
6263 scenarios : generateAllTemplateScenarios ( libauthTransaction , transactionBuilder ) ,
6364 } ;
6465
65- // TODO: Refactor the below code to not have deep reassignment of scenario.sourceOutputs and scenario.transaction.outputs
66-
67- // Initialize bytecode mappings, these will be used to map the locking and unlocking scripts and naming the scripts
68- const unlockingBytecodeToLockingBytecodeParams : Record < string , WalletTemplateScenarioBytecode > = { } ;
69- const lockingBytecodeToLockingBytecodeParams : Record < string , WalletTemplateScenarioBytecode > = { } ;
70-
71- // We can typecast this because we check that all inputs are standard unlockable at the top of this function
72- for ( const [ inputIndex , input ] of ( transactionBuilder . inputs as StandardUnlockableUtxo [ ] ) . entries ( ) ) {
73- if ( isContractUnlocker ( input . unlocker ) ) {
74- const lockScriptName = getLockScriptName ( input . unlocker . contract ) ;
75- if ( ! lockScriptName ) continue ;
76-
77- const lockingScriptParams = generateLockingScriptParams ( input . unlocker . contract , input , lockScriptName ) ;
78-
79- const unlockingBytecode = binToHex ( libauthTransaction . inputs [ inputIndex ] . unlockingBytecode ) ;
80- unlockingBytecodeToLockingBytecodeParams [ unlockingBytecode ] = lockingScriptParams ;
81-
82- const lockingBytecode = binToHex ( addressToLockScript ( input . unlocker . contract . address ) ) ;
83- lockingBytecodeToLockingBytecodeParams [ lockingBytecode ] = lockingScriptParams ;
84- }
85- }
86-
87- for ( const scenario of Object . values ( template . scenarios ! ) ) {
88- // For Inputs
89- for ( const [ idx , input ] of libauthTransaction . inputs . entries ( ) ) {
90- const unlockingBytecode = binToHex ( input . unlockingBytecode ) ;
91- const lockingBytecodeParams = unlockingBytecodeToLockingBytecodeParams [ unlockingBytecode ] ;
92-
93- // If lockingBytecodeParams is unknown, then it stays at default: {}
94- if ( ! lockingBytecodeParams ) continue ;
95-
96- // If locking bytecode is set to ['slot'] then this is being evaluated by the scenario, so we don't replace bytecode
97- if ( lockingBytecodeIsSetToSlot ( scenario ?. sourceOutputs ?. [ idx ] ?. lockingBytecode ) ) continue ;
98-
99- // If lockingBytecodeParams is known, and this input is not ['slot'] then assign a locking bytecode as source output
100- if ( scenario . sourceOutputs ?. [ idx ] ) {
101- scenario . sourceOutputs [ idx ] = {
102- ...scenario . sourceOutputs [ idx ] ,
103- lockingBytecode : lockingBytecodeParams ,
104- } ;
105- }
106- }
107-
108- // For Outputs
109- for ( const [ idx , output ] of libauthTransaction . outputs . entries ( ) ) {
110- const lockingBytecode = binToHex ( output . lockingBytecode ) ;
111- const lockingBytecodeParams = lockingBytecodeToLockingBytecodeParams [ lockingBytecode ] ;
112-
113- // If lockingBytecodeParams is unknown, then it stays at default: {}
114- if ( ! lockingBytecodeParams ) continue ;
115-
116- // If locking bytecode is set to ['slot'] then this is being evaluated by the scenario, so we don't replace bytecode
117- if ( lockingBytecodeIsSetToSlot ( scenario ?. transaction ?. outputs ?. [ idx ] ?. lockingBytecode ) ) continue ;
118-
119- // If lockingBytecodeParams is known, and this input is not ['slot'] then assign a locking bytecode as source output
120- if ( scenario ?. transaction ?. outputs ?. [ idx ] ) {
121- scenario . transaction . outputs [ idx ] = {
122- ...scenario . transaction . outputs [ idx ] ,
123- lockingBytecode : lockingBytecodeParams ,
124- } ;
125- }
126- }
127- }
128-
12966 return template ;
13067} ;
13168
@@ -188,6 +125,25 @@ const generateAllTemplateScripts = (
188125 return scripts . reduce ( ( acc , script ) => ( { ...acc , ...script } ) , { } ) ;
189126} ;
190127
128+ const generateLockingBytecodeParamsMapping = (
129+ transactionBuilder : TransactionBuilder ,
130+ ) : Record < string , WalletTemplateScenarioBytecode > => {
131+ // Initialize bytecode mapping, this will be used to map the locking bytecode to the locking bytecode params
132+ const mapping : Record < string , WalletTemplateScenarioBytecode > = { } ;
133+
134+ // We can typecast this because we check that all inputs are standard unlockable at the top of this function
135+ for ( const input of ( transactionBuilder . inputs as StandardUnlockableUtxo [ ] ) ) {
136+ if ( isContractUnlocker ( input . unlocker ) ) {
137+ const lockScriptName = getLockScriptName ( input . unlocker . contract ) ;
138+ const lockingScriptParams = generateLockingScriptParams ( input . unlocker . contract , input , lockScriptName ) ;
139+ const lockingBytecode = binToHex ( addressToLockScript ( input . unlocker . contract . address ) ) ;
140+ mapping [ lockingBytecode ] = lockingScriptParams ;
141+ }
142+ }
143+
144+ return mapping ;
145+ } ;
146+
191147const generateAllTemplateScenarios = (
192148 libauthTransaction : TransactionBch ,
193149 transactionBuilder : TransactionBuilder ,
@@ -451,6 +407,8 @@ const generateTemplateScenarioTransaction = (
451407 transactionBuilder : TransactionBuilder ,
452408 slotIndex : number ,
453409) : WalletTemplateScenario [ 'transaction' ] => {
410+ const lockingBytecodeParamsMapping = generateLockingBytecodeParamsMapping ( transactionBuilder ) ;
411+
454412 const zippedInputs = zip ( transactionBuilder . inputs , libauthTransaction . inputs ) ;
455413 const inputs = zippedInputs . map ( ( [ csInput , libauthInput ] , inputIndex ) => {
456414 return {
@@ -465,16 +423,10 @@ const generateTemplateScenarioTransaction = (
465423
466424 const zippedOutputs = zip ( transactionBuilder . outputs , libauthTransaction . outputs ) ;
467425 const outputs = zippedOutputs . map ( ( [ csOutput , libauthOutput ] ) => {
468- if ( csOutput && contract ) {
469- return {
470- lockingBytecode : generateTemplateScenarioTransactionOutputLockingBytecode ( csOutput , contract ) ,
471- token : serialiseTokenDetails ( libauthOutput . token ) ,
472- valueSatoshis : Number ( libauthOutput . valueSatoshis ) ,
473- } ;
474- }
475-
476426 return {
477- lockingBytecode : `${ binToHex ( libauthOutput . lockingBytecode ) } ` ,
427+ lockingBytecode : generateTemplateScenarioTransactionOutputLockingBytecode (
428+ csOutput , libauthOutput , contract , lockingBytecodeParamsMapping ,
429+ ) ,
478430 token : serialiseTokenDetails ( libauthOutput . token ) ,
479431 valueSatoshis : Number ( libauthOutput . valueSatoshis ) ,
480432 } ;
@@ -493,7 +445,7 @@ const generateTemplateScenarioSourceOutputs = (
493445 const zippedInputs = zip ( transactionBuilder . inputs , libauthTransaction . inputs ) ;
494446 return zippedInputs . map ( ( [ csInput , libauthInput ] , inputIndex ) => {
495447 return {
496- lockingBytecode : generateTemplateScenarioBytecode ( csInput , libauthInput , inputIndex , 'p2pkh_placeholder_lock' , inputIndex === slotIndex ) ,
448+ lockingBytecode : generateTemplateScenarioBytecodeForSourceOutputs ( csInput , libauthInput , inputIndex , 'p2pkh_placeholder_lock' , inputIndex === slotIndex ) ,
497449 valueSatoshis : Number ( csInput . satoshis ) ,
498450 token : serialiseTokenDetails ( csInput . token ) ,
499451 } ;
@@ -619,12 +571,45 @@ const generateTemplateScenarioBytecode = (
619571 return { } ;
620572} ;
621573
574+ const generateTemplateScenarioBytecodeForSourceOutputs = (
575+ input : Utxo ,
576+ libauthInput : Input ,
577+ inputIndex : number ,
578+ p2pkhScriptNameTemplate : string ,
579+ insertSlot ?: boolean ,
580+ ) : WalletTemplateScenarioBytecode | [ 'slot' ] => {
581+ if ( insertSlot ) return [ 'slot' ] ;
582+
583+ if ( isUnlockableUtxo ( input ) && isStandardUnlockableUtxo ( input ) ) {
584+ // If the input is a contract unlocker, we need to generate the locking bytecode params for the source outputs
585+ if ( isContractUnlocker ( input . unlocker ) ) {
586+ const lockScriptName = getLockScriptName ( input . unlocker . contract ) ;
587+ const lockingBytecodeParams = generateLockingScriptParams ( input . unlocker . contract , input , lockScriptName ) ;
588+ return lockingBytecodeParams ;
589+ }
590+
591+ // For a P2PKH unlocker, the sourceOutputs "locking bytecode params" are the same as the unlocking bytecode params
592+ return generateUnlockingScriptParams ( input , libauthInput , p2pkhScriptNameTemplate , inputIndex ) ;
593+ }
594+
595+ // 'slot' means that we are currently evaluating this specific input,
596+ // {} means that it is the same script type, but not being evaluated
597+ return { } ;
598+ } ;
599+
622600const generateTemplateScenarioTransactionOutputLockingBytecode = (
623601 csOutput : Output ,
624- contract : Contract ,
602+ libauthOutput : LibauthOutput ,
603+ contract : Contract | undefined ,
604+ lockingBytecodeParamsMapping : Record < string , WalletTemplateScenarioBytecode > ,
625605) : string | { } => {
606+ // If lockingBytecodeParams is known from the mapping, return it
607+ const lockingBytecode = binToHex ( libauthOutput . lockingBytecode ) ;
608+ const lockingBytecodeParams = lockingBytecodeParamsMapping [ lockingBytecode ] ;
609+ if ( lockingBytecodeParams ) return lockingBytecodeParams ;
610+
626611 if ( csOutput . to instanceof Uint8Array ) return binToHex ( csOutput . to ) ;
627- if ( [ contract . address , contract . tokenAddress ] . includes ( csOutput . to ) ) return { } ;
612+ if ( contract && [ contract . address , contract . tokenAddress ] . includes ( csOutput . to ) ) return { } ;
628613 return binToHex ( addressToLockScript ( csOutput . to ) ) ;
629614} ;
630615
0 commit comments