@secux/app-btc
Version:
SecuX Hardware Wallet BTC API
18 lines (15 loc) • 15.4 kB
JavaScript
;
/*!
Copyright 2022 SecuX Technology Inc
Copyright Chen Wei-En
Copyright Wu Tsung-Yu
Licensed under the Apache License, Version 2.0 (the License);
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an AS IS BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/Object.defineProperty(exports,"__esModule",{value:!0}),exports.Psbtv2=void 0;const bip174_1=require("bip174"),convert=require("bip174/src/lib/converter"),bufferutils_1=require("./bufferutils");class Psbtv2 extends bip174_1.Psbt{static fromBuffer(buffer,txFromBuffer){const results=function(buffer,txGetter){if(1886610036!==buffer.readUInt32BE())throw Error("Format Error: Invalid Magic Number");if(255!==buffer.readUInt8(4))throw Error("Format Error: Magic Number must be followed by 0xff separator");const reader=new bufferutils_1.BufferReader(buffer);reader.offset+=5;const getKeyValue=()=>({key:reader.readVarSlice(),value:reader.readVarSlice()}),checkEndOfKeyValPairs=()=>{if(reader.offset>=buffer.length)throw Error("Format Error: Unexpected End of PSBT");const isEnd=0===buffer.readUInt8(reader.offset);return isEnd&&(reader.offset+=1),isEnd},globalMapKeyVals=[],globalKeyIndex={};for(;!checkEndOfKeyValPairs();){const keyVal=getKeyValue(),hexKey=keyVal.key.toString("hex");if(globalKeyIndex[hexKey])throw Error("Format Error: Keys must be unique for global keymap: key "+hexKey);globalKeyIndex[hexKey]=1,globalMapKeyVals.push(keyVal)}const unsignedTxMaps=globalMapKeyVals.filter((keyVal=>keyVal.key[0]===GlobalTypes.UNSIGNED_TX));if(1!==unsignedTxMaps.length)throw Error("Format Error: Only one UNSIGNED_TX allowed");const unsignedTx=txGetter(unsignedTxMaps[0].value),{inputCount,outputCount}=unsignedTx.getInputOutputCounts(),inputKeyVals=[],outputKeyVals=[];for(let index=0;index<inputCount;index++){const inputKeyIndex={},input=[];for(;!checkEndOfKeyValPairs();){const keyVal=getKeyValue(),hexKey=keyVal.key.toString("hex");if(inputKeyIndex[hexKey])throw Error(`Format Error: Keys must be unique, got "${hexKey}" from input#${index}`);inputKeyIndex[hexKey]=1,input.push(keyVal)}inputKeyVals.push(input)}for(let index=0;index<outputCount;index++){const outputKeyIndex={},output=[];for(;!checkEndOfKeyValPairs();){const keyVal=getKeyValue(),hexKey=keyVal.key.toString("hex");if(outputKeyIndex[hexKey])throw Error(`Format Error: Keys must be unique, got "${hexKey}" from output#${index}`);outputKeyIndex[hexKey]=1,output.push(keyVal)}outputKeyVals.push(output)}return function(unsignedTx,{globalMapKeyVals,inputKeyVals,outputKeyVals}){const globalMap={unsignedTx};let inputCount,outputCount,txCount=0;for(const keyVal of globalMapKeyVals){let reader;switch(keyVal.key[0]){case GlobalTypes.UNSIGNED_TX:if(checkKeyBuffer("global",keyVal.key,GlobalTypes.UNSIGNED_TX),txCount>0)throw new Error("Format Error: GlobalMap has multiple UNSIGNED_TX");txCount++;break;case GlobalTypes.GLOBAL_XPUB:void 0===globalMap.globalXpub&&(globalMap.globalXpub=[]),globalMap.globalXpub.push(convert.globals.globalXpub.decode(keyVal));break;case GlobalTypes.GLOBAL_TX_VERSION:if(checkKeyBuffer("global",keyVal.key,GlobalTypes.GLOBAL_TX_VERSION),4!==keyVal.value.length)throw Error("Value Error: Global transaction version is not 4 bytes");globalMap.txVersion=keyVal.value.readUInt32LE();break;case GlobalTypes.GLOBAL_FALLBACK_LOCKTIME:if(checkKeyBuffer("global",keyVal.key,GlobalTypes.GLOBAL_FALLBACK_LOCKTIME),4!==keyVal.value.length)throw Error("Value Error: Global fallback locktime is not 4 bytes");globalMap.fallbackLocktime=keyVal.value.readUInt32LE();break;case GlobalTypes.GLOBAL_INPUT_COUNT:checkKeyBuffer("global",keyVal.key,GlobalTypes.GLOBAL_INPUT_COUNT),reader=new bufferutils_1.BufferReader(keyVal.value),reader.readVarInt(),inputCount=reader.readVarInt();break;case GlobalTypes.GLOBAL_OUTPUT_COUNT:checkKeyBuffer("global",keyVal.key,GlobalTypes.GLOBAL_OUTPUT_COUNT),reader=new bufferutils_1.BufferReader(keyVal.value),reader.readVarInt(),outputCount=reader.readVarInt();break;case GlobalTypes.GLOBAL_TXMODIFIABLE:if(checkKeyBuffer("global",keyVal.key,GlobalTypes.GLOBAL_TXMODIFIABLE),1!==keyVal.value.length)throw Error("Value Error: Global tx modifiable flags is not 1 bytes");globalMap.txModifiable=!(1&~keyVal.value[0]);break;case GlobalTypes.GLOBAL_VERSION:if(checkKeyBuffer("global",keyVal.key,GlobalTypes.GLOBAL_VERSION),4!==keyVal.value.length)throw Error("Value Error: Global PSBT version is not 4 bytes");globalMap.version=keyVal.value.readUInt32LE();break;default:globalMap.unknownKeyVals||(globalMap.unknownKeyVals=[]),globalMap.unknownKeyVals.push(keyVal)}}inputCount=null!=inputCount?inputCount:inputKeyVals.length,outputCount=null!=outputCount?outputCount:outputKeyVals.length;const inputs=[],outputs=[];for(let index=0;index<inputCount;index++){const input={tapScriptSigs:{},tapScripts:{},tapBip32Paths:{}};for(const keyVal of inputKeyVals[index])switch(convert.inputs.checkPubkey(keyVal),keyVal.key[0]){case InputTypes.NON_WITNESS_UTXO:if(checkKeyBuffer("input",keyVal.key,InputTypes.NON_WITNESS_UTXO),void 0!==input.nonWitnessUtxo)throw Error("Format Error: Input has multiple NON_WITNESS_UTXO");input.nonWitnessUtxo=convert.inputs.nonWitnessUtxo.decode(keyVal);break;case InputTypes.WITNESS_UTXO:if(checkKeyBuffer("input",keyVal.key,InputTypes.WITNESS_UTXO),void 0!==input.witnessUtxo)throw Error("Format Error: Input has multiple WITNESS_UTXO");input.witnessUtxo=convert.inputs.witnessUtxo.decode(keyVal);break;case InputTypes.PARTIAL_SIG:void 0===input.partialSig&&(input.partialSig=[]),input.partialSig.push(convert.inputs.partialSig.decode(keyVal));break;case InputTypes.SIGHASH_TYPE:if(checkKeyBuffer("input",keyVal.key,InputTypes.SIGHASH_TYPE),void 0!==input.sighashType)throw Error("Format Error: Input has multiple SIGHASH_TYPE");input.sighashType=convert.inputs.sighashType.decode(keyVal);break;case InputTypes.REDEEM_SCRIPT:if(checkKeyBuffer("input",keyVal.key,InputTypes.REDEEM_SCRIPT),void 0!==input.redeemScript)throw Error("Format Error: Input has multiple REDEEM_SCRIPT");input.redeemScript=convert.inputs.redeemScript.decode(keyVal);break;case InputTypes.WITNESS_SCRIPT:if(checkKeyBuffer("input",keyVal.key,InputTypes.WITNESS_SCRIPT),void 0!==input.witnessScript)throw Error("Format Error: Input has multiple WITNESS_SCRIPT");input.witnessScript=convert.inputs.witnessScript.decode(keyVal);break;case InputTypes.BIP32_DERIVATION:void 0===input.bip32Derivation&&(input.bip32Derivation=[]),input.bip32Derivation.push(convert.inputs.bip32Derivation.decode(keyVal));break;case InputTypes.FINAL_SCRIPTSIG:checkKeyBuffer("input",keyVal.key,InputTypes.FINAL_SCRIPTSIG),input.finalScriptSig=convert.inputs.finalScriptSig.decode(keyVal);break;case InputTypes.FINAL_SCRIPTWITNESS:checkKeyBuffer("input",keyVal.key,InputTypes.FINAL_SCRIPTWITNESS),input.finalScriptWitness=convert.inputs.finalScriptWitness.decode(keyVal);break;case InputTypes.POR_COMMITMENT:checkKeyBuffer("input",keyVal.key,InputTypes.POR_COMMITMENT),input.porCommitment=convert.inputs.porCommitment.decode(keyVal);break;case InputTypes.PREVIOUS_TXID:if(checkKeyBuffer("input",keyVal.key,InputTypes.PREVIOUS_TXID),32!==keyVal.value.length)throw Error("Value Error: Previous txid is not 32 bytes");input.prevTXID=keyVal.value.toString("hex");break;case InputTypes.OUTPUT_INDEX:if(checkKeyBuffer("input",keyVal.key,InputTypes.OUTPUT_INDEX),4!==keyVal.value.length)throw Error("Value Error: Previous output index is not 4 bytes");input.prevOutputIndex=keyVal.value[0];break;case InputTypes.TAP_KEY_SIG:if(checkKeyBuffer("input",keyVal.key,InputTypes.TAP_KEY_SIG),keyVal.value.length<64)throw Error("Value Error: Input Taproot key path signature is shorter than 64 bytes");if(keyVal.value.length>65)throw Error("Value Error: Input Taproot key path signature is longer than 65 bytes");input.tapKeySig=keyVal.value;break;case InputTypes.TAP_SCRIPT_SIG:if(checkKeyBuffer("input",keyVal.key,InputTypes.TAP_SCRIPT_SIG),65!==keyVal.key.length)throw Error("Format Error: Input Taproot script signature key is not 65 bytes");if(keyVal.value.length<64)throw Error("Value Error: Input Taproot script path signature is shorter than 64 bytes");if(keyVal.value.length>65)throw Error("Value Error: Input Taproot script path signature is longer than 65 bytes");const scriptkey=keyVal.key.slice(1).toString("hex");input.tapScriptSigs[scriptkey]=keyVal.value;break;case InputTypes.TAP_LEAF_SCRIPT:if(checkKeyBuffer("input",keyVal.key,InputTypes.TAP_LEAF_SCRIPT),keyVal.key.length<34)throw Error("Format Error: Input Taproot leaf script key is not at least 34 bytes");if(keyVal.key.length%32!=2)throw Error("Format Error: Input Taproot leaf script key's control block is not valid");if(0===keyVal.value.length)throw Error("Value Error: Intput Taproot leaf script cannot be empty");const script=keyVal.value.slice(-1).toString("hex");input.tapScripts[script]||(input.tapScripts[script]=new Set),input.tapScripts[script].add(keyVal.key.slice(1).toString("hex"));break;case InputTypes.TAP_BIP32_DERIVATION:if(checkKeyBuffer("input",keyVal.key,InputTypes.TAP_BIP32_DERIVATION),33!==keyVal.key.length)throw Error("Format Error: Input Taproot BIP 32 keypath key is not 33 bytes");const xonly=keyVal.key.slice(1).toString("hex"),reader=new bufferutils_1.BufferReader(keyVal.value),num_hashs=reader.readVarInt(),leaf_hashes=new Set;for(let i=0;i<num_hashs;i++)leaf_hashes.add(reader.readSlice(32).toString("hex"));input.tapBip32Paths[xonly]={leafHashs:leaf_hashes,Bip32Derivation:keyVal.value.slice(reader.offset)};break;case InputTypes.TAP_INTERNAL_KEY:if(checkKeyBuffer("input",keyVal.key,InputTypes.TAP_INTERNAL_KEY),32!==keyVal.value.length)throw Error("Value Error: Input Taproot internal key is not 32 bytes");input.tapInternalKey=keyVal.value;break;case InputTypes.TAP_MERKLE_ROOT:if(checkKeyBuffer("input",keyVal.key,InputTypes.TAP_MERKLE_ROOT),32!==keyVal.value.length)throw Error("Value Error: Input Taproot merkle root is not 32 bytes");input.tapMarkleRoot=keyVal.value.toString("hex");break;default:input.unknownKeyVals||(input.unknownKeyVals=[]),input.unknownKeyVals.push(keyVal)}inputs.push(input)}for(let index=0;index<outputCount;index++){const output={tapBip32Paths:{}};for(const keyVal of outputKeyVals[index]){let reader;switch(convert.outputs.checkPubkey(keyVal),keyVal.key[0]){case OutputTypes.REDEEM_SCRIPT:if(checkKeyBuffer("output",keyVal.key,OutputTypes.REDEEM_SCRIPT),void 0!==output.redeemScript)throw Error("Format Error: Output has multiple REDEEM_SCRIPT");output.redeemScript=convert.outputs.redeemScript.decode(keyVal);break;case OutputTypes.WITNESS_SCRIPT:if(checkKeyBuffer("output",keyVal.key,OutputTypes.WITNESS_SCRIPT),void 0!==output.witnessScript)throw Error("Format Error: Output has multiple WITNESS_SCRIPT");output.witnessScript=convert.outputs.witnessScript.decode(keyVal);break;case OutputTypes.BIP32_DERIVATION:void 0===output.bip32Derivation&&(output.bip32Derivation=[]),output.bip32Derivation.push(convert.outputs.bip32Derivation.decode(keyVal));break;case OutputTypes.AMOUNT:if(checkKeyBuffer("output",keyVal.key,OutputTypes.AMOUNT),8!==keyVal.value.length)throw Error("Value Error: Output amount is not 8 bytes");reader=new bufferutils_1.BufferReader(keyVal.value),output.amount=reader.readUInt64();break;case OutputTypes.SCRIPT:checkKeyBuffer("output",keyVal.key,OutputTypes.SCRIPT),output.script=keyVal.value;break;case OutputTypes.TAP_INTERNAL_KEY:if(checkKeyBuffer("output",keyVal.key,OutputTypes.TAP_INTERNAL_KEY),32!==keyVal.value.length)throw Error("Value Error: Output Taproot internal key is not 32 bytes");output.tapInternalKey=keyVal.value;break;case OutputTypes.TAP_TREE:checkKeyBuffer("output",keyVal.key,OutputTypes.TAP_TREE),output.tapTree=keyVal.value;break;case OutputTypes.TAP_BIP32_DERIVATION:if(checkKeyBuffer("output",keyVal.key,OutputTypes.TAP_BIP32_DERIVATION),33!==keyVal.key.length)throw Error("Output Taproot BIP 32 keypath key is not 33 bytes");const xonly=keyVal.key.slice(1).toString("hex"),leafHashs=new Set;reader=new bufferutils_1.BufferReader(keyVal.value);const num_hashs=reader.readVarInt();for(let i=0;i<num_hashs;i++)leafHashs.add(reader.readSlice(32).toString("hex"));output.tapBip32Paths[xonly]={leafHashs,Bip32Derivation:keyVal.value.slice(reader.offset)};break;default:output.unknownKeyVals||(output.unknownKeyVals=[]),output.unknownKeyVals.push(keyVal)}}outputs.push(output)}return{globalMap,inputs,outputs}}(unsignedTx,{globalMapKeyVals,inputKeyVals,outputKeyVals})}(buffer,txFromBuffer),psbt=new this(results.globalMap.unsignedTx);return Object.assign(psbt,results),psbt}}function checkKeyBuffer(type,keyBuf,keyNum){if(!keyBuf.equals(Buffer.from([keyNum])))throw Error(`Format Error: Invalid ${type} key: ${keyBuf.toString("hex")}`)}var GlobalTypes,InputTypes,OutputTypes;exports.Psbtv2=Psbtv2,function(GlobalTypes){GlobalTypes[GlobalTypes.UNSIGNED_TX=0]="UNSIGNED_TX",GlobalTypes[GlobalTypes.GLOBAL_XPUB=1]="GLOBAL_XPUB",GlobalTypes[GlobalTypes.GLOBAL_TX_VERSION=2]="GLOBAL_TX_VERSION",GlobalTypes[GlobalTypes.GLOBAL_FALLBACK_LOCKTIME=3]="GLOBAL_FALLBACK_LOCKTIME",GlobalTypes[GlobalTypes.GLOBAL_INPUT_COUNT=4]="GLOBAL_INPUT_COUNT",GlobalTypes[GlobalTypes.GLOBAL_OUTPUT_COUNT=5]="GLOBAL_OUTPUT_COUNT",GlobalTypes[GlobalTypes.GLOBAL_TXMODIFIABLE=6]="GLOBAL_TXMODIFIABLE",GlobalTypes[GlobalTypes.GLOBAL_VERSION=251]="GLOBAL_VERSION"}(GlobalTypes||(GlobalTypes={})),function(InputTypes){InputTypes[InputTypes.NON_WITNESS_UTXO=0]="NON_WITNESS_UTXO",InputTypes[InputTypes.WITNESS_UTXO=1]="WITNESS_UTXO",InputTypes[InputTypes.PARTIAL_SIG=2]="PARTIAL_SIG",InputTypes[InputTypes.SIGHASH_TYPE=3]="SIGHASH_TYPE",InputTypes[InputTypes.REDEEM_SCRIPT=4]="REDEEM_SCRIPT",InputTypes[InputTypes.WITNESS_SCRIPT=5]="WITNESS_SCRIPT",InputTypes[InputTypes.BIP32_DERIVATION=6]="BIP32_DERIVATION",InputTypes[InputTypes.FINAL_SCRIPTSIG=7]="FINAL_SCRIPTSIG",InputTypes[InputTypes.FINAL_SCRIPTWITNESS=8]="FINAL_SCRIPTWITNESS",InputTypes[InputTypes.POR_COMMITMENT=9]="POR_COMMITMENT",InputTypes[InputTypes.PREVIOUS_TXID=14]="PREVIOUS_TXID",InputTypes[InputTypes.OUTPUT_INDEX=15]="OUTPUT_INDEX",InputTypes[InputTypes.SEQUENCE=16]="SEQUENCE",InputTypes[InputTypes.REQUIRED_TIME_LOCKTIME=17]="REQUIRED_TIME_LOCKTIME",InputTypes[InputTypes.REQUIRED_HEIGHT_LOCKTIME=18]="REQUIRED_HEIGHT_LOCKTIME",InputTypes[InputTypes.TAP_KEY_SIG=19]="TAP_KEY_SIG",InputTypes[InputTypes.TAP_SCRIPT_SIG=20]="TAP_SCRIPT_SIG",InputTypes[InputTypes.TAP_LEAF_SCRIPT=21]="TAP_LEAF_SCRIPT",InputTypes[InputTypes.TAP_BIP32_DERIVATION=22]="TAP_BIP32_DERIVATION",InputTypes[InputTypes.TAP_INTERNAL_KEY=23]="TAP_INTERNAL_KEY",InputTypes[InputTypes.TAP_MERKLE_ROOT=24]="TAP_MERKLE_ROOT"}(InputTypes||(InputTypes={})),function(OutputTypes){OutputTypes[OutputTypes.REDEEM_SCRIPT=0]="REDEEM_SCRIPT",OutputTypes[OutputTypes.WITNESS_SCRIPT=1]="WITNESS_SCRIPT",OutputTypes[OutputTypes.BIP32_DERIVATION=2]="BIP32_DERIVATION",OutputTypes[OutputTypes.AMOUNT=3]="AMOUNT",OutputTypes[OutputTypes.SCRIPT=4]="SCRIPT",OutputTypes[OutputTypes.TAP_INTERNAL_KEY=5]="TAP_INTERNAL_KEY",OutputTypes[OutputTypes.TAP_TREE=6]="TAP_TREE",OutputTypes[OutputTypes.TAP_BIP32_DERIVATION=7]="TAP_BIP32_DERIVATION"}(OutputTypes||(OutputTypes={}));