async function transfer(
  tokenId,
  receiverPublicKey,
  originalCommitmentSalt,
  newCommitmentSalt,
  senderSecretKey,
  commitment,
  commitmentIndex,
  vkId,
  blockchainOptions,
  zokratesOptions,
) {
  const { nfTokenShieldJson, nfTokenShieldAddress } = blockchainOptions;
  const account = utils.ensure0x(blockchainOptions.account);

  const {
    codePath,
    outputDirectory,
    witnessName = 'witness',
    pkPath,
    provingScheme = 'gm17',
    createProofJson = true,
    proofName = 'proof.json',
  } = zokratesOptions;

  console.group('\nIN TRANSFER...');

  console.log('Finding the relevant Shield and Verifier contracts');
  const nfTokenShield = contract(nfTokenShieldJson);
  nfTokenShield.setProvider(Web3.connect());
  const nfTokenShieldInstance = await nfTokenShield.at(nfTokenShieldAddress);
  const verifier = await Verifier.at(await nfTokenShieldInstance.getVerifier.call());
  const verifier_registry = await Verifier_Registry.at(await verifier.getRegistry.call());
  console.log('NFTokenShield contract address:', nfTokenShieldInstance.address);
  console.log('Verifier contract address:', verifier.address);
  console.log('Verifier_Registry contract address:', verifier_registry.address);

  // Calculate new arguments for the proof:
  const nullifier = utils.concatenateThenHash(originalCommitmentSalt, senderSecretKey);
  const outputCommitment = utils.concatenateThenHash(
    utils.strip0x(tokenId).slice(-config.LEAF_HASHLENGTH * 2),
    receiverPublicKey,
    newCommitmentSalt,
  );

  // Get the sibling-path from the token commitment (leaf) to the root. Express each node as an Element class.
  const siblingPath = await getSiblingPath(
    account,
    nfTokenShieldInstance,
    commitment,
    commitmentIndex,
  );

  const root = siblingPath[0];
  // TODO: checkRoot() is not essential. It's only useful for debugging as we make iterative improvements to nightfall's zokrates files. Possibly delete in future.
  checkRoot(commitment, commitmentIndex, siblingPath, root);

  const siblingPathElements = siblingPath.map(
    nodeValue => new Element(nodeValue, 'field', config.NODE_HASHLENGTH * 8, 1),
  ); // we truncate to 216 bits - sending the whole 256 bits will overflow the prime field

  // Summarise values in the console:
  console.group('Existing Proof Variables:');
  const p = config.ZOKRATES_PACKING_SIZE;
  const pt = Math.ceil((config.LEAF_HASHLENGTH * 8) / config.ZOKRATES_PACKING_SIZE);
  console.log('tokenId: ', tokenId, ' : ', utils.hexToFieldPreserve(tokenId, p, pt));
  console.log(
    'originalCommitmentSalt:',
    originalCommitmentSalt,
    ' : ',
    utils.hexToFieldPreserve(originalCommitmentSalt, p, pt),
  );
  console.log(
    'newCommitmentSalt:',
    newCommitmentSalt,
    ':',
    utils.hexToFieldPreserve(newCommitmentSalt, p, pt),
  );
  console.log(
    'senderSecretKey:',
    senderSecretKey,
    ':',
    utils.hexToFieldPreserve(senderSecretKey, p, pt),
  );
  console.log(
    'receiverPublicKey:',
    receiverPublicKey,
    ':',
    utils.hexToFieldPreserve(receiverPublicKey, p, pt),
  );
  console.log('inputCommitment:', commitment, ':', utils.hexToFieldPreserve(commitment, p, pt));
  console.groupEnd();

  console.group('New Proof Variables:');
  console.log('nullifier:', nullifier, ':', utils.hexToFieldPreserve(nullifier, p, pt));
  console.log(
    'outputCommitment:',
    outputCommitment,
    ':',
    utils.hexToFieldPreserve(outputCommitment, p, pt),
  );
  console.log('root:', root, ':', utils.hexToFieldPreserve(root, p));
  console.log(`siblingPath:`, siblingPath);
  console.log(`commitmentIndex:`, commitmentIndex);
  console.groupEnd();

  const publicInputHash = utils.concatenateThenHash(root, nullifier, outputCommitment);
  console.log('publicInputHash:', publicInputHash);

  const allInputs = formatInputsForZkSnark([
    new Element(publicInputHash, 'field', 248, 1),
    new Element(tokenId, 'field'),
    ...siblingPathElements.slice(1),
    new Element(commitmentIndex, 'field', 128, 1), // the binary decomposition of a leafIndex gives its path's 'left-right' positions up the tree. The decomposition is done inside the circuit.
    new Element(nullifier, 'field'),
    new Element(receiverPublicKey, 'field'),
    new Element(originalCommitmentSalt, 'field'),
    new Element(newCommitmentSalt, 'field'),
    new Element(senderSecretKey, 'field'),
    new Element(root, 'field'),
    new Element(outputCommitment, 'field'),
  ]);

  await zokrates.computeWitness(codePath, outputDirectory, witnessName, allInputs);

  await zokrates.generateProof(pkPath, codePath, `${outputDirectory}/witness`, provingScheme, {
    createFile: createProofJson,
    directory: outputDirectory,
    fileName: proofName,
  });

  let { proof } = JSON.parse(fs.readFileSync(`${outputDirectory}/${proofName}`));

  proof = Object.values(proof);
  // convert to flattened array:
  proof = utils.flattenDeep(proof);
  // convert to decimal, as the solidity functions expect uints
  proof = proof.map(el => utils.hexToDec(el));
  console.groupEnd();

  console.group('Transferring within the Shield contract');

  const publicInputs = formatInputsForZkSnark([new Element(publicInputHash, 'field', 248, 1)]);

  console.log('proof:');
  console.log(proof);
  console.log('publicInputs:');
  console.log(publicInputs);
  console.log(`vkId: ${vkId}`);

  const txReceipt = await nfTokenShieldInstance.transfer(
    proof,
    publicInputs,
    vkId,
    root,
    nullifier,
    outputCommitment,
    {
      from: account,
      gas: 6500000,
      gasPrice: config.GASPRICE,
    },
  );
  gasUsedStats(txReceipt, 'transfer');

  const newLeafLog = txReceipt.logs.filter(log => {
    return log.event === 'NewLeaf';
  });
  const outputCommitmentIndex = newLeafLog[0].args.leafIndex;
  console.groupEnd();

  console.log('TRANSFER COMPLETE\n');
  console.groupEnd();

  return {
    outputCommitment,
    outputCommitmentIndex,
    txReceipt,
  };
}
/**
 * This function actually transfers a token, assuming that we have a proof.
 * @param {String} tokenId - the token's unique id (this is a full 256 bits)
 * @param {String} receiverPublicKey
 * @param {String} originalCommitmentSalt
 * @param {String} newCommitmentSalt
 * @param {String} senderSecretKey
 * @param {String} commitment - Commitment of token being sent
 * @param {Integer} commitmentIndex - the position of commitment in the on-chain Merkle Tree
 * @param {String} vkId
 * @param {Object} blockchainOptions
 * @param {String} blockchainOptions.nfTokenShieldJson - ABI of nfTokenShield
 * @param {String} blockchainOptions.nfTokenShieldAddress - Address of deployed nfTokenShieldContract
 * @param {String} blockchainOptions.account - Account that is sending these transactions
 * @returns {String} outputCommitment - New commitment
 * @returns {Number} outputCommitmentIndex - the index of the token within the Merkle Tree.  This is required for later transfers/joins so that Alice knows which 'chunks' of the Merkle Tree she needs to 'get' from the NFTokenShield contract in order to calculate a path.
 * @returns {Object} txObj - a promise of a blockchain transaction
 */
This function actually transfers a token, assuming that we have a proof.
This function actually transfers a token assuming that we have a proof .
async function transfer(
  tokenId,
  receiverPublicKey,
  originalCommitmentSalt,
  newCommitmentSalt,
  senderSecretKey,
  commitment,
  commitmentIndex,
  vkId,
  blockchainOptions,
  zokratesOptions,
) {
  const { nfTokenShieldJson, nfTokenShieldAddress } = blockchainOptions;
  const account = utils.ensure0x(blockchainOptions.account);
  const {
    codePath,
    outputDirectory,
    witnessName = 'witness',
    pkPath,
    provingScheme = 'gm17',
    createProofJson = true,
    proofName = 'proof.json',
  } = zokratesOptions;
  console.group('\nIN TRANSFER...');
  console.log('Finding the relevant Shield and Verifier contracts');
  const nfTokenShield = contract(nfTokenShieldJson);
  nfTokenShield.setProvider(Web3.connect());
  const nfTokenShieldInstance = await nfTokenShield.at(nfTokenShieldAddress);
  const verifier = await Verifier.at(await nfTokenShieldInstance.getVerifier.call());
  const verifier_registry = await Verifier_Registry.at(await verifier.getRegistry.call());
  console.log('NFTokenShield contract address:', nfTokenShieldInstance.address);
  console.log('Verifier contract address:', verifier.address);
  console.log('Verifier_Registry contract address:', verifier_registry.address);
  const nullifier = utils.concatenateThenHash(originalCommitmentSalt, senderSecretKey);
  const outputCommitment = utils.concatenateThenHash(
    utils.strip0x(tokenId).slice(-config.LEAF_HASHLENGTH * 2),
    receiverPublicKey,
    newCommitmentSalt,
  );
  const siblingPath = await getSiblingPath(
    account,
    nfTokenShieldInstance,
    commitment,
    commitmentIndex,
  );
  const root = siblingPath[0];
  checkRoot(commitment, commitmentIndex, siblingPath, root);
  const siblingPathElements = siblingPath.map(
    nodeValue => new Element(nodeValue, 'field', config.NODE_HASHLENGTH * 8, 1),
  ); 
  console.group('Existing Proof Variables:');
  const p = config.ZOKRATES_PACKING_SIZE;
  const pt = Math.ceil((config.LEAF_HASHLENGTH * 8) / config.ZOKRATES_PACKING_SIZE);
  console.log('tokenId: ', tokenId, ' : ', utils.hexToFieldPreserve(tokenId, p, pt));
  console.log(
    'originalCommitmentSalt:',
    originalCommitmentSalt,
    ' : ',
    utils.hexToFieldPreserve(originalCommitmentSalt, p, pt),
  );
  console.log(
    'newCommitmentSalt:',
    newCommitmentSalt,
    ':',
    utils.hexToFieldPreserve(newCommitmentSalt, p, pt),
  );
  console.log(
    'senderSecretKey:',
    senderSecretKey,
    ':',
    utils.hexToFieldPreserve(senderSecretKey, p, pt),
  );
  console.log(
    'receiverPublicKey:',
    receiverPublicKey,
    ':',
    utils.hexToFieldPreserve(receiverPublicKey, p, pt),
  );
  console.log('inputCommitment:', commitment, ':', utils.hexToFieldPreserve(commitment, p, pt));
  console.groupEnd();
  console.group('New Proof Variables:');
  console.log('nullifier:', nullifier, ':', utils.hexToFieldPreserve(nullifier, p, pt));
  console.log(
    'outputCommitment:',
    outputCommitment,
    ':',
    utils.hexToFieldPreserve(outputCommitment, p, pt),
  );
  console.log('root:', root, ':', utils.hexToFieldPreserve(root, p));
  console.log(`siblingPath:`, siblingPath);
  console.log(`commitmentIndex:`, commitmentIndex);
  console.groupEnd();
  const publicInputHash = utils.concatenateThenHash(root, nullifier, outputCommitment);
  console.log('publicInputHash:', publicInputHash);
  const allInputs = formatInputsForZkSnark([
    new Element(publicInputHash, 'field', 248, 1),
    new Element(tokenId, 'field'),
    ...siblingPathElements.slice(1),
    new Element(commitmentIndex, 'field', 128, 1), 
    new Element(nullifier, 'field'),
    new Element(receiverPublicKey, 'field'),
    new Element(originalCommitmentSalt, 'field'),
    new Element(newCommitmentSalt, 'field'),
    new Element(senderSecretKey, 'field'),
    new Element(root, 'field'),
    new Element(outputCommitment, 'field'),
  ]);
  await zokrates.computeWitness(codePath, outputDirectory, witnessName, allInputs);
  await zokrates.generateProof(pkPath, codePath, `${outputDirectory}/witness`, provingScheme, {
    createFile: createProofJson,
    directory: outputDirectory,
    fileName: proofName,
  });
  let { proof } = JSON.parse(fs.readFileSync(`${outputDirectory}/${proofName}`));
  proof = Object.values(proof);
  proof = utils.flattenDeep(proof);
  proof = proof.map(el => utils.hexToDec(el));
  console.groupEnd();
  console.group('Transferring within the Shield contract');
  const publicInputs = formatInputsForZkSnark([new Element(publicInputHash, 'field', 248, 1)]);
  console.log('proof:');
  console.log(proof);
  console.log('publicInputs:');
  console.log(publicInputs);
  console.log(`vkId: ${vkId}`);
  const txReceipt = await nfTokenShieldInstance.transfer(
    proof,
    publicInputs,
    vkId,
    root,
    nullifier,
    outputCommitment,
    {
      from: account,
      gas: 6500000,
      gasPrice: config.GASPRICE,
    },
  );
  gasUsedStats(txReceipt, 'transfer');
  const newLeafLog = txReceipt.logs.filter(log => {
    return log.event === 'NewLeaf';
  });
  const outputCommitmentIndex = newLeafLog[0].args.leafIndex;
  console.groupEnd();
  console.log('TRANSFER COMPLETE\n');
  console.groupEnd();
  return {
    outputCommitment,
    outputCommitmentIndex,
    txReceipt,
  };
}
async function transfer ( tokenId , receiverPublicKey , originalCommitmentSalt , newCommitmentSalt , senderSecretKey , commitment , commitmentIndex , vkId , blockchainOptions , zokratesOptions , ) { const { nfTokenShieldJson , nfTokenShieldAddress } = blockchainOptions ; const account = utils . ensure0x ( blockchainOptions . account ) ; const { codePath , outputDirectory , witnessName = 'witness' , pkPath , provingScheme = 'gm17' , createProofJson = true , proofName = 'proof.json' , } = zokratesOptions ; console . group ( '\nIN TRANSFER...' ) ; console . log ( 'Finding the relevant Shield and Verifier contracts' ) ; const nfTokenShield = contract ( nfTokenShieldJson ) ; nfTokenShield . setProvider ( Web3 . connect ( ) ) ; const nfTokenShieldInstance = await nfTokenShield . at ( nfTokenShieldAddress ) ; const verifier = await Verifier . at ( await nfTokenShieldInstance . getVerifier . call ( ) ) ; const verifier_registry = await Verifier_Registry . at ( await verifier . getRegistry . call ( ) ) ; console . log ( 'NFTokenShield contract address:' , nfTokenShieldInstance . address ) ; console . log ( 'Verifier contract address:' , verifier . address ) ; console . log ( 'Verifier_Registry contract address:' , verifier_registry . address ) ; const nullifier = utils . concatenateThenHash ( originalCommitmentSalt , senderSecretKey ) ; const outputCommitment = utils . concatenateThenHash ( utils . strip0x ( tokenId ) . slice ( - config . LEAF_HASHLENGTH * 2 ) , receiverPublicKey , newCommitmentSalt , ) ; const siblingPath = await getSiblingPath ( account , nfTokenShieldInstance , commitment , commitmentIndex , ) ; const root = siblingPath [ 0 ] ; checkRoot ( commitment , commitmentIndex , siblingPath , root ) ; const siblingPathElements = siblingPath . map ( nodeValue => new Element ( nodeValue , 'field' , config . NODE_HASHLENGTH * 8 , 1 ) , ) ; console . group ( 'Existing Proof Variables:' ) ; const p = config . ZOKRATES_PACKING_SIZE ; const pt = Math . ceil ( ( config . LEAF_HASHLENGTH * 8 ) / config . ZOKRATES_PACKING_SIZE ) ; console . log ( 'tokenId: ' , tokenId , ' : ' , utils . hexToFieldPreserve ( tokenId , p , pt ) ) ; console . log ( 'originalCommitmentSalt:' , originalCommitmentSalt , ' : ' , utils . hexToFieldPreserve ( originalCommitmentSalt , p , pt ) , ) ; console . log ( 'newCommitmentSalt:' , newCommitmentSalt , ':' , utils . hexToFieldPreserve ( newCommitmentSalt , p , pt ) , ) ; console . log ( 'senderSecretKey:' , senderSecretKey , ':' , utils . hexToFieldPreserve ( senderSecretKey , p , pt ) , ) ; console . log ( 'receiverPublicKey:' , receiverPublicKey , ':' , utils . hexToFieldPreserve ( receiverPublicKey , p , pt ) , ) ; console . log ( 'inputCommitment:' , commitment , ':' , utils . hexToFieldPreserve ( commitment , p , pt ) ) ; console . groupEnd ( ) ; console . group ( 'New Proof Variables:' ) ; console . log ( 'nullifier:' , nullifier , ':' , utils . hexToFieldPreserve ( nullifier , p , pt ) ) ; console . log ( 'outputCommitment:' , outputCommitment , ':' , utils . hexToFieldPreserve ( outputCommitment , p , pt ) , ) ; console . log ( 'root:' , root , ':' , utils . hexToFieldPreserve ( root , p ) ) ; console . log ( ` ` , siblingPath ) ; console . log ( ` ` , commitmentIndex ) ; console . groupEnd ( ) ; const publicInputHash = utils . concatenateThenHash ( root , nullifier , outputCommitment ) ; console . log ( 'publicInputHash:' , publicInputHash ) ; const allInputs = formatInputsForZkSnark ( [ new Element ( publicInputHash , 'field' , 248 , 1 ) , new Element ( tokenId , 'field' ) , ... siblingPathElements . slice ( 1 ) , new Element ( commitmentIndex , 'field' , 128 , 1 ) , new Element ( nullifier , 'field' ) , new Element ( receiverPublicKey , 'field' ) , new Element ( originalCommitmentSalt , 'field' ) , new Element ( newCommitmentSalt , 'field' ) , new Element ( senderSecretKey , 'field' ) , new Element ( root , 'field' ) , new Element ( outputCommitment , 'field' ) , ] ) ; await zokrates . computeWitness ( codePath , outputDirectory , witnessName , allInputs ) ; await zokrates . generateProof ( pkPath , codePath , ` ${ outputDirectory } ` , provingScheme , { createFile : createProofJson , directory : outputDirectory , fileName : proofName , } ) ; let { proof } = JSON . parse ( fs . readFileSync ( ` ${ outputDirectory } ${ proofName } ` ) ) ; proof = Object . values ( proof ) ; proof = utils . flattenDeep ( proof ) ; proof = proof . map ( el => utils . hexToDec ( el ) ) ; console . groupEnd ( ) ; console . group ( 'Transferring within the Shield contract' ) ; const publicInputs = formatInputsForZkSnark ( [ new Element ( publicInputHash , 'field' , 248 , 1 ) ] ) ; console . log ( 'proof:' ) ; console . log ( proof ) ; console . log ( 'publicInputs:' ) ; console . log ( publicInputs ) ; console . log ( ` ${ vkId } ` ) ; const txReceipt = await nfTokenShieldInstance . transfer ( proof , publicInputs , vkId , root , nullifier , outputCommitment , { from : account , gas : 6500000 , gasPrice : config . GASPRICE , } , ) ; gasUsedStats ( txReceipt , 'transfer' ) ; const newLeafLog = txReceipt . logs . filter ( log => { return log . event === 'NewLeaf' ; } ) ; const outputCommitmentIndex = newLeafLog [ 0 ] . args . leafIndex ; console . groupEnd ( ) ; console . log ( 'TRANSFER COMPLETE\n' ) ; console . groupEnd ( ) ; return { outputCommitment , outputCommitmentIndex , txReceipt , } ; }
This function actually transfers a token, assuming that we have a proof.
This function actually transfers a token assuming that we have a proof .
// Calculate new arguments for the proof: // Get the sibling-path from the token commitment (leaf) to the root. Express each node as an Element class. // TODO: checkRoot() is not essential. It's only useful for debugging as we make iterative improvements to nightfall's zokrates files. Possibly delete in future. // we truncate to 216 bits - sending the whole 256 bits will overflow the prime field // Summarise values in the console: // the binary decomposition of a leafIndex gives its path's 'left-right' positions up the tree. The decomposition is done inside the circuit. // convert to flattened array: // convert to decimal, as the solidity functions expect uints