diff --git a/issuance-mixed-reissuance.sh b/issuance-mixed-reissuance.sh new file mode 100644 index 000000000..931ad2f52 --- /dev/null +++ b/issuance-mixed-reissuance.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +# change with yor own elements-cli config +ecli="nigiri rpc --liquid" + +conf_addr_for_reissuance_token=`$ecli getnewaddress` +unconf_addr_random="ert1qtz89cfq54fdgekhu9m759pn3xpr64ek7mmm5ds" + +# Issue an asset with supply 0 +issued_asset=`$ecli issueasset 0 0.00000001 false` +txid=`echo $issued_asset | jq -r .txid` +asset=`echo $issued_asset | jq -r .asset` +token=`echo $issued_asset | jq -r .token` +vin=`echo $issued_asset | jq -r .vin` +entropy=`echo $issued_asset | jq -r .entropy` +echo "txid: $txid" +echo "vin: $vin" +echo "asset: $asset" +echo "token: $token" +echo "entropy: $entropy" +issaunce_tx=`$ecli gettransaction $txid` +assetblinder=`echo $issaunce_tx | jq -r '.details | .[0] | .assetblinder'` +echo "assetblinder: $assetblinder" + +$ecli generatetoaddress 1 $conf_addr_for_reissuance_token + + +# create raw transaction that sends 0.0015 BTC unblinded to someone, attaching the reissuance token already +# the vout 1 is always the reissaunce token (most of the time) +empty_tx=`$ecli createrawtransaction '[{"txid": "'$txid'", "vout": 1}]' '[{"'$conf_addr_for_reissuance_token'":0.00000001, "asset": "'$token'"}, {"'$unconf_addr_random'": 0.0015}]'` + + +# fund the btc input +result_funded_tx=`$ecli fundrawtransaction "$empty_tx"` +funded_tx=`echo $result_funded_tx | jq -r .hex` + + +# attach reissuance stuff +result_reissue_tx=`$ecli rawreissueasset "$funded_tx" '[{"asset_amount":0.0025,"asset_address":"'$unconf_addr_random'", "input_index":0, "asset_blinder":"'$assetblinder'", "entropy":"'$entropy'"}]'` +reissue_tx=`echo $result_reissue_tx | jq -r .hex` + + +# attach NFT issuance stuff +result_issue_tx=`$ecli rawissueasset "$reissue_tx" '[{"asset_amount":0.00000001,"asset_address":"'$conf_addr_for_reissuance_token'"}]'` +issue_tx=`echo $result_issue_tx | jq -r '.[0] | .hex'` + + +# blind +blind_tx=`$ecli blindrawtransaction "$issue_tx"` + +# sign with wallet +result_signed_tx=`$ecli signrawtransactionwithwallet "$blind_tx"` +signed_tx=`echo $result_signed_tx | jq -r .hex` + +echo "signed_tx: $signed_tx" + +# broadcast +$ecli sendrawtransaction "$signed_tx" \ No newline at end of file diff --git a/test/integration/issuances.spec.ts b/test/integration/issuances.spec.ts index 8cf96e11f..9898acbaf 100644 --- a/test/integration/issuances.spec.ts +++ b/test/integration/issuances.spec.ts @@ -224,6 +224,142 @@ describe('liquidjs-lib (issuances transactions with psbt)', () => { await broadcast(hex); }); + it.only('can create an unblinded issuance together with an unblinded reissuance', async () => { + const alice = createPayment('p2wpkh', undefined, undefined, true); + const aliceBlindingPrivkeys = alice.blindingKeys; + // btc utxo + const inputData = await getInputData(alice.payment, true, 'noredeem'); + + // const reissuableAssetPay = createPayment('p2wpkh', undefined, undefined, true); // unconfidential + + // 1. issue the reissubale asset first + const issuePsbt = new Psbt(); + issuePsbt.addInput(inputData); + issuePsbt.addIssuance({ + assetAddress: alice.payment.confidentialAddress, + tokenAddress: alice.payment.confidentialAddress, + assetAmount: 0.00000001, + tokenAmount: 0.00000001, + precision: 8, + blindedIssuance: false, + }); + issuePsbt.addOutputs([ + { + nonce, + asset, + value: confidential.satoshiToConfidentialValue(99999500), + script: alice.payment.output, + }, + { + nonce, + asset, + value: confidential.satoshiToConfidentialValue(500), + script: Buffer.alloc(0), + }, + ]); + // blind the token output + await issuePsbt.blindOutputsByIndex( + Psbt.ECCKeysGenerator(ecc), + new Map().set(0, aliceBlindingPrivkeys[0]), + // blind only the token output + new Map().set( + 1, + fromConfidential(alice.payment.confidentialAddress).blindingKey, + ), + ); + // sign the issuance + issuePsbt.signAllInputs(alice.keys[0]); + const valid = issuePsbt.validateSignaturesOfAllInputs( + Psbt.ECDSASigValidator(ecc), + ); + strictEqual(valid, true); + + issuePsbt.finalizeAllInputs(); + const hex = issuePsbt.extractTransaction().toHex(); + console.log(hex) + await broadcast(hex); + // 2. reissue the asset together with an issuance + const issuanceTx = Transaction.fromHex(hex); + const issuanceInput = issuanceTx.ins[0]; + + if (!issuanceInput.issuance) { + throw new Error('no issuance in issuance input'); + } + + const entropy = issuanceEntropyFromInput(issuanceInput); + + const tokenOutput = issuanceTx.outs[1]; + const changeOutput = issuanceTx.outs[2]; + + const unblindedTokenOutput = await confidential.unblindOutputWithKey( + tokenOutput, + aliceBlindingPrivkeys[0], + ); + + const tokenBlinder = unblindedTokenOutput.assetBlindingFactor; + + const reissuancePsbt = new Psbt() + .addInput({ + hash: issuanceTx.getId(), + index: 2, + witnessUtxo: changeOutput, + }) + .addOutput({ + nonce, + asset, + value: confidential.satoshiToConfidentialValue(99998500), + script: alice.payment.output, + }) + .addReissuance({ + entropy, + tokenPrevout: { txHash: issuanceTx.getHash(false), vout: 1 }, + prevoutBlinder: tokenBlinder, + witnessUtxo: tokenOutput, + assetAmount: 0.005, + tokenAmount: 0.00000001, + assetAddress: alice.payment.confidentialAddress, + tokenAddress: alice.payment.confidentialAddress, + precision: 8, + blindedIssuance: false + }) + .addIssuance({ + assetAddress: alice.payment.confidentialAddress, + assetAmount: 1, + tokenAmount: 0, + precision: 0, + blindedIssuance: false, + }) + .addOutput({ + nonce, + asset, + value: confidential.satoshiToConfidentialValue(1000), + script: Buffer.alloc(0), + }); + + // blind the token output + await reissuancePsbt.blindOutputsByIndex( + Psbt.ECCKeysGenerator(ecc), + new Map().set(1, aliceBlindingPrivkeys[0]), + // blind only the token output + new Map() + .set( + 2, + fromConfidential(alice.payment.confidentialAddress).blindingKey, + ) + ); + // sign the issuance + reissuancePsbt.signAllInputs(alice.keys[0]); + const validReissuance = reissuancePsbt.validateSignaturesOfAllInputs( + Psbt.ECDSASigValidator(ecc), + ); + strictEqual(validReissuance, true); + + reissuancePsbt.finalizeAllInputs(); + const hexReissuance = reissuancePsbt.extractTransaction().toHex(); + console.log(hexReissuance); + //await broadcast(hexReissuance); + }); + it('can create a confidential reissuance transaction from confidential issuance transaction', async () => { // Issuance const alice = createPayment('p2wpkh', undefined, undefined, true);