vulpemventures / liquidjs-lib

A javascript Liquid library for node.js and browsers.
MIT License
27 stars 16 forks source link

Asset issuance not working as expected #108

Open Distance71 opened 11 months ago

Distance71 commented 11 months ago

Hello community,

I'm trying to create an asset with no success.

Everything looks well during the process of creation, but when I try to broadcast to the network I get this error:

sendrawtransaction RPC error: {"code":-26,"message":"mandatory-script-verify-flag-failed (Signature must be zero for failed CHECK(MULTI)SIG operation)"}

To broadcast, I used https://blockstream.info/liquidtestnet/api/tx, passing the transactionHex in the body as 'text'

I've used the same methods as described in the examples provided

This's the full code needed to reproduce. Any help would be appreciated :)

const ecc = require('tiny-secp256k1');
  const secp256k1 = require('@vulpemventures/secp256k1-zkp');
  const liquid = require('liquidjs-lib');
  const { ECPairFactory } = require('ecpair');

  const ECPair = ECPairFactory(ecc);
  const TESTNET = liquid.networks.testnet;

  const makeAddressOne = () => {
  const keyPair = ECPair.fromPrivateKey(
    Buffer.from('acbc28e59c0122f97d479d1e22806534f5bc4ff0bf127505be7dd8a8e11dc726', 'hex'),
  );

  const { address } = liquid.payments.p2pkh({
    pubkey: keyPair.publicKey,
    network: TESTNET,
  });

  const blindkey = keyPair.publicKey;

  const { confidentialAddress } = liquid.payments.p2pkh({
    address,
    blindkey,
    network: TESTNET,
  });

  return {
    address,
    confidentialAddress,
    keys: keyPair,
  };
};

const convertAddressToScript = address => liquid.address.toOutputScript(address, TESTNET);

function signTransaction(pset, signers, sighashType, ecclib = ecc) {
  const signer = new liquid.Signer(pset);

  signers.forEach((keyPairs, i) => {
    const preimage = pset.getInputPreimage(i, sighashType);
    const partialSig = {
      partialSig: {
        pubkey: keyPairs.publicKey,
        signature: liquid.script.signature.encode(keyPairs.sign(preimage), sighashType),
      },
    };
    signer.addSignature(i, partialSig, liquid.Pset.ECDSASigValidator(ecclib));
  });

  if (!pset.validateAllSignatures(liquid.Pset.ECDSASigValidator(ecclib))) {
    throw new Error('Failed to sign pset');
  }

  const finalizer = new liquid.Finalizer(pset);
  finalizer.finalize();
  return liquid.Extractor.extract(pset);
}

  const addressAlice = makeAddressOne();
  const lbtc = liquid.networks.testnet.assetHash;

  const unspentConfidential = {
    txid: '337b9c5c8a4e83a56875d53b1ad9e7caa1c0862537bf040e4165b717560d9b38',
    vout: 1,
    txHex:
      '020000000101a16e6cfaecf3132c6b993862efd7ca882609850cf16332bc9ba48a729a2afed5010000006a473044022056a5198901f4b0d0e6d9cde19d0865ddd2028aece8b3b6740ba02e7d4aed85cc022050079be073d30b3e2cc5e2823e74e6ac139d48ea46c5601d433e8d69084ff2d701210382375b3986feb6f33d96f86c4bc5e09f53d7b3e4eb5b90eeca6d487b7eb40a65ffffffff0301499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000003e8001976a914539cc65863ecfbfce0dd163e9353cb29ea222c0a88ac0b6821f161c20add66bd7a4c3d4a85caf0dd69fcef2fc95a7a15485343e93e61ba093159ac773433fb919b9831f65dfddd250ce1e9afa9f00150a4fba2d79a868a4d033f3ba42f7268beebd479da75e97cd5bb6f791fec5dc545cd0db294110cbb38cc1976a9146db5192bb9ef2b8ba234d8e5b9399d4219419f9588ac01499a818545f6bae39fc03b637f2a4e1e64e590cac1bc3a6f6d71aa4443654c140100000000000003e800000000000000000000000043010001f59a88d5ac142a8dc48b088ad822e0e00305c1ded821d36e3dd2545e6c3dbdf9767d040e34a1d794e61ef558875435d3c953a1631fcc9c072b60eb64adf2691afd4e1060330000000000000001f5f06c009fc8e7035dc105984b6e61199fbac515d51ca3a0d8480675d927918f363b4b97cdd6ad2be78493dbdb268454c752df01c4cbba762ece262ba78e4a8bf014172304bc39b687269fa60354620178c11fd2f12261ab9e38d111d6d64214f7424b90ec96eef050d9fab83954d889d964d4607f8dab2939353d3f0d11ca3736e9ecf92cb94fe9b8ab3c56ddad2ec153ab226487d996f2debe0e247fa0448841a3ddaf09801ced01157d3876a82c6b6a9e3b610e529c33d5cbde73ff8a040a266c874182dc5f685e4bca2ba6b0b6cc03f328887c5457e65c600fd09027abb7c61231b71099fc5c8f386ad86313e3f096a89edfc18f52a151d8ec4627e418241dd040807c753d91e9c7feced79f80572e9cb01ad5dd4b63d1c0ba020d5371f15b0951d11873d277ba0959b88ffd50b7dc1abb1b7d046bfc754d629f477fc9bae94273763dd87a83dfd2dc599c99beef32163e900b0d678c589890d7b8d746e4dff39311919bc2add316acb35d2fcda02a8f428c50bead5d8784f323f490db4d5704b3d2a32edb0a46df72f69345aa3ff90c5e440df0f527ffdf3751763b9b00ee749d46d65672ffbcf880d274b08a74ea526c51db93bc45e22d4329ca7eef60084a26d1daf3a376721a2185d8c119518931d8d7e4fcf9799156e1e71c1bc65cfb6048062787655f67f8b3e51bfd2eb65612f0e86e547480b9890385e8101f0a8d566c71d5263da2257009f78a65aa51fa8dd8c5210f37f7ab7709e36e501937699a871f86ea08913cb14afbddc58bfd987b983bedab6aec29416b373fb84f6ec857f4959a215b403f0553966a4b9a90a99603c9d478fc18331d07cbcd4e59e911404152cc2526e25f7bc00d3e47f85c290047a4c88cd60190d2e432d34abeb6f6ac5852cb30ca12faf4d084ccf7f48e8a1144c640bcbcda5f4257e213e38cb17e174f6cbedc2f44c141bdafcfe4f3cdafcfa8abba1a25b168c405711a365a46145dafc65fdeff276138e822f4145bdf81f09eefb2cde5407a768dc2d460700327630e760625daf720e3f4ead348fea8651d20619ef05b45176ea4b97f45a946b742b7e52e3babc9d0a08cd6aeb7a06774302be3781d34e1d376d3a40315c02008a22da5037bd0adec9a9e0ec3f390c0325315b20aafa98736e184eb0ff3463660c8df6257ecfa4d27bb6c7019ca3649cf705401e02f5d9ef67b22abda8affc68becf290cb62da5fa4070a6c2aba2e76ac2d5c2347acec0458d6f53c446afe45867cdc81efb111c5da8b5b30ca2c0cb74b8c99a023737a9e6e4b0a1854056b69d662073764ef6dfd4f41f3bebda52e222528cd7758b09acf16bd253a1be01e55c4609b600ce148cd9daedd083d532ef200b4f5ebcffbd450e8f565ea5cba568670c7c80ab6104f4934c9d93d96ee5fe56bc2ec24f3915324e680cab7a1f46f79ddfdca13ca4d6fbb2fe2444261d2b6c4568b3cd0a555c29b97b483c645e7b40a478d4f8a1081cc34655860d05568a98dac98d00871bce511e1f062fa1f9d1a919327f6138f04307a618a8002c1dc42ff531ef985bc7a39e64d0a8da9a3aa23c639525d9ba05e3c0bfcc959413e0975ec6d67958016de07f9d5d782511546c3d8f22fbf21556e9b866181d101fe5367f4f5d0df3db30c289211f6adf88b0a186d08fad31b7672607117769045321d8b891e1c798fe1a14305f9fc47d820b2c36cddc106d1aa89ec21567f9f4cba037651276c820b3c410af61f52b5cb19499a1bc25b9e6edf3ba7dbaf8ecd302e40a20a6d411ceb717d25ce6e5f3a31c4afd0dd28b1317fc6edf7ddd28b5a6d44ec9ce47555120f3e76431f9133c477ad192c7df4d800f53ec7c9dc9b999c7d670f9d83c7849d33977f915296a0eefac14881e482a1b2c1d388769c0f5e47d77ffc765f4f74d152389246491e3a2545ece18f715b3afcf3dcd612f6902395ba69eeae2dd5540c74ff88516f19535579fb038f3e492286c10472a74f308a1d73e63aff33470a5c50401b3f671118c1146e0e671a737b12adb6d12a73d360bab1b4915a56265703c85921640502417498db9cd73f6a18c91b8d7c7412c359b1ab79f5e1a631ac7a1dc39567f03617b476b8f6fbb28eeb309c505eb01ed218ed687eedc41a55437763b71bb810ff2b2dd1332cd56346a99bf92e0cef1801afed09c5ee6a9112c40e29c854bfbeb0bd7988689a7f23c6363b4080d62934c8d6fa39485410e0dc54ddd78a231528cfdb2cf2465f9464a9b5f3a6ac5a821dfda6511a569fd488c434c3b5cc9cc88f25dcdea17fd0a7114793cc4b49800115059fdfb9b52781db0bb1e76e6260f927a1698154bebb701bb8f3e33c44f94cae3605e1627fafe392738e908a78da8115ca99382bda27fe6a184e5e865715de0092904cbde1f897ff63857e14e8390bcf965f7057ace53fb32388dd824ad622507c3e0f3df25e31f7ba0af33943d44630c6b15a0f78e1b54760905d23a481c3eedeeb3443009f76e5f9597f83c4ee3f4e376cf6a98bad707530038fe584eac28543dd3fcaccfefdd6b23a03a9a274d9f90fb8f06aae766a675e596cb279433477a9b41e60c9505d11e7335a4e116951cb2305b9cbf3a1ecc2521b66ed1cb111aaad5faaee99b6dbbaa2e12cb191658dae9abcab51ee273b384865b0fcc55c608ca6e4aa97eca8837cce737589a7370770681c9661159923bd12f36d73b0dbfb516628380357b8c50176d7cac52eb4118d2435e1f6386f796f7f152bba3c3b397ba32c2f0a2f7ab57e2df71301928e09971f1e9ce8859c00f46a98a02cf5633729d05838cb14aadab2dfe3a7510b1fce146d387f48f95b97e5177483a38902f98f771248e4b65ed23617db24c17ecd9733b675144cff0f8dd5a5a910355a94af5aa5bd8e718be1b22975853a4b4f9d8cbb69ae189fd4dda571b274484aed920a716295e1b637c5dd9c465fe89e5ccf8c6986e0a0defcf429145b86d40cefd03dc284c0dd8b8dfcc2e99ec7c0b7fd4b8fdf88b558ac0355cce913a254bdb427632ae032f91e3d82c1ee9bb7f08a3171d4fd01121f3b8e9c9941de5543de29309f6de0ef5113a88d66420b9d1926d1e42a2209f991aa5b58b2757f15fe3024bece53f490436ac91b0d6d9dcaa9c0116d5da4707f69d84c0b8bfeba931e8ba767d4f3b3f74854dbbe0799fb03671d4c1836677fa32df079a68e21e36e9dc877d06c1ce7e3da0f368094d32fc15f72e1d859acf37904d5cc86c16e829901893ca9e84c54764b883c885a4110083fce6db292af304f7e7f905cbdb12d519951758060b53c8c0f7e5bfba0664564e37cee01915d4f629ad1772537d5b1d33db5a09ed0ee0fd425502355f42c5d0906c2269405f6617e13552f619c03e3fd700ec61958fcd80cc209f15155c5bb16c4831a4759027a22e78389146818862209c503f6459df2d2db4228217b0e21f9f12b329e8d22841b044dbe2767bd407a2b033160680b928838097f89d63ed19f1091afe467f7589ae927373d56c82f9ea926ecc0238cb474676d22fe0aa3c1bd858570497fc467dd021c3764b27d84562f7420f320a8feea1d542e277280f5f65c637b8282c5bb8a1a1d566cc106ff26227f99fdb91df11bb7f98fc6313555ce21ea43c2492894329523473e58c74371cba40a0fec683cfb990c158f09eb2bf9166da0028b525f053c59ec00e4c5f79b014ce15a5accb1e0a02f60e6f416b53448f451cf44a7e892fdd4052abd92d30965f7716bb03b418e8b133f1a9116ba9fdd88621a1aa84932c67b5e0538f0980a6981bc946b4337c92fd91cd60ec4d2f116ad1ed7ea06cead4257255c9321c36fa6f53fc4d18df23f5f94c2662492982f93e1fa0277ac162fe145ebaf3c9c123e1c496bbaeca45b135b0c30f2b56d0a7692588bf8ec26406cf983e91f167f2a9bd8e8ffac9fcc704d8814352ec491e6cf00dbb388870dfcd8a126a50021dbb4b76da75d57277bf13d0b38c387bd4f84a333d0ad1e07420be83c6be593c23e34d3c7a626f90e11714cb98f4e4dc23daad01cd1a41580fba7c7f89dec325f240baaf9799d4ac99e983f9657219d011121e29b07e88dec8b1aea7b79466abd90518230dd4256fd8f3bf56ae1bf526daa41de9f3b4c82d5ff9efccc7a074c510d76748dd087431ebf9e96e7b626ef66a89a8c23383cd35f7b5d08e87fea79354431e30c30ce10ff9dc5acc6db3e084384343903aee579204182f061baa646f258ff3aa62d22172eb5ddabe45a17168db2fe5a051b6385967eab9cff2d4d065dcdc2142938e23caa54bf27b70f9a0710ae7499d817bd0937468e01130f874f93c988cb237b8c378bb0ec5d4a9b4466452e951a0a542cd736d28f9000368040ee7d53ac25651c1053508f145f92bef1a06c5301a702cc5aa0c1943089879878863f5413f18410942cb1517e1396d4dfa3f7b532566e3ac80612f422b2c9d5fed25381f786fa0ea79280921e532b997efcc1e5e13ffff5de30501eaea228c50004d90ad6f8e5604cf47e24d025558675ce07f007be3207ad9f7d4deeb89eb1f5267190baa3f7c663cb05d71c6a869976a259c2c2b70938dc922c87781bfebeb850d30b9a62a007ffca68b9bdc165565e3b2c14a9b36f09e46bc583ab14b5a8140cd1a6ec4d5c71115ceb3e37e82d803c3ad11bb134eb63899c33d6c98b0992ef6b7b9a552f3ddd812d467fdbf1f3af167c9c492d4360b425fc2ae2a8ab9997efb077174ac280770e72752bf16f7db0a2d6842387b392950185789f748130a9d67797fdc6d26cfac556ea08a3e14e084efe0a1df703eb8084994bf372939b8c9f80cc70b90f1627280653811e1253612b7bf0bed43dfd74636195f9a94066a17906b98fbb0979cd5d64d24a98618eab2254fb8eae726fb58a184ae6fdcd536709dd3338f3537e3ea1122c8b61518643c0f80c02fc15778efd297e36cf2a83fef59b2af9bea6d90a0057cc23773d28b2fd42a9122679aa858294f4739695be481b0ea5354b146119ad2a5b6ce3626c1447ee7768133afe7eb929a08613a1ca50614c247ce091d5f5b87a4702b8674e7ae850b6c26155d31c009c608dde266854a801bd4c73f68ee97d52c2475c4c5636e0e83863a2672e0a4f95d6875d0dcc10395d77dd11a6b448330ebd65dce4ec66cb430a55848bdc3c06738eb2c27aa24ef55722175a38ac2ef599ed346016a5db3c85a7c791607acc4e8bdca219032ed224648a3dddbab450deec0acb8ea7a2313afb5d40e386de05695cbd2a6e51e2fe4fb02ce8e4f41b5d2a4128cabd448c023e88a4fc1826cffd5d837c7236b6eb3b60353b1557e8ce7e7cdb5e7f8238a5db490ea5639e93a46778a6d64b4792ccad63f6b0aa3b05ff7e1347810a0ce553f58867b278bab4be3089be6fdae1eaf89e7d6e4b4141fcd33b87e0f3461aad9bb619791845c7bddcf9224b170ad3583b870b53634074e7f40524451925d21c39a35d7ba0c6a4c3c6d3f9fbcfbce04c3fd300cf91b63d981b6eceed8dfe26503395e075a9bfaf18b4e5e6725c187e86a338d9f149ed6b80d7d7b81b4f9ed108fa4843de62b685ea6162e8dd45b64c48d5ab6a9ae5c37142a077f1102e45d1941e4f506f0da83460d91dc3e12deef9bcb4945247cca64bd79780449475012b3084e6a6d442667df14199a2cc6eee54e6f39766087244c0d829aa2b1e83ac98f347910c5b106fea8fd9b4de3ea7954e5fafde3362414deaa1f864d24403c92be0d0db3504dd749445a9e5c345be9274a2cbc8a32a7c0839c6c1d5f30e0770cf048fd915d6dfa4212e2bbc1a56d93810a371b491fec13d46c13013dea5dcb3aca37cd193d79d8055613ddf3cca72f041799c282c584b0206fded0000',
    amount: 100000,
    asset: '144c654344aa716d6f3abcc1ca90e5641e4e2a7f633bc09fe3baf64585819a49',
  };

  const alice = {
    payment: {
      output: Buffer.from(convertAddressToScript(addressAlice.address), 'hex'),
      blindkey: addressAlice.keys.publicKey,
    },
    keys: addressAlice.keys,
    blindingKeys: [addressAlice.keys.privateKey],
  };

  const aliceInputData = {
    hash: Buffer.from(unspentConfidential.txid, 'hex').reverse(),
    index: unspentConfidential.vout, // index of utxo
    nonWitnessUtxo: unspentConfidential.txHex,
  };

  const inputs = [aliceInputData].map(({ hash, index }) => {
    const txid = hash
      .slice()
      .reverse()
      .toString('hex');
    return new liquid.CreatorInput(txid, index);
  });

  const outputs = [
    new liquid.CreatorOutput(
      unspentConfidential.asset,
      97000,
      alice.payment.output,
      alice.payment.blindkey,
      0,
    ),
    new liquid.CreatorOutput(lbtc, 1000),
  ];

  const zkpLib = await secp256k1();

  const pset = liquid.Creator.newPset({ inputs, outputs });
  const updater = new liquid.Updater(pset);

  updater.addInNonWitnessUtxo(0, liquid.Transaction.fromHex(aliceInputData.nonWitnessUtxo));
  updater.addInSighashType(0, liquid.Transaction.SIGHASH_ALL);

  const zkpValidator = new liquid.ZKPValidator(zkpLib);
  const zkpGenerator = new liquid.ZKPGenerator(
    zkpLib,
    liquid.ZKPGenerator.WithBlindingKeysOfInputs(alice.blindingKeys),
  );

  updater.addInIssuance(0, {
    assetAmount: 1000,
    assetAddress: addressAlice.confidentialAddress,
    blindedIssuance: true,
    contract: {
      entity: {
        domain: 'www.testbtset.com',
      },
      issuer_pubkey: addressAlice.confidentialAddress,
      name: 'TESTBTSET',
      precision: 0, // NFT
      ticker: 'TESTBTSET',
      version: 1.0,
      collection: 'test-btset-collection',
    },
  });

  const issuanceBlindingArgs = zkpGenerator.blindIssuances(pset, {
    0: alice.blindingKeys[0],
  });

  const ownedInputs = zkpGenerator.unblindInputs(pset);
  const outputBlindingArgs = zkpGenerator.blindOutputs(pset, liquid.Pset.ECCKeysGenerator(ecc));
  const blinder = new liquid.Blinder(pset, ownedInputs, zkpValidator, zkpGenerator);
  blinder.blindLast({ issuanceBlindingArgs, outputBlindingArgs });
  const rawTx = signTransaction(pset, [alice.keys], liquid.Transaction.SIGHASH_ALL);
altafan commented 11 months ago

The error suggests that something's wrong with the input signature.

One thing I noticed is that you hardcoded the amount of the LBTC input to 100000, while the outputs add up to 98000. After unblinding the conf output, I made sure that the correct input amount is 98000 but, still, the same error persisted.

Distance71 commented 11 months ago

@altafan What may be wrong in the signature? I used the same method shown in the examples @louisinger May you have some more background on this?

altafan commented 11 months ago

@Distance71 we were able to reproduce your error, we're now debugging to understand where's the bug.

In the meantime, you could move all your funds to a p2wpkh address. We have working test cases that demonstrate how to make a successful issuance with this kind of addresses.

Distance71 commented 8 months ago

@altafan Any updates on this?