Enviar transacción firmada desde nodejs a SmartContract privado usando webjs en la red Quorum no funciona

Contexto

Tengo una red de quórum montada siguiendo el ejemplo de 7nodes . En el nodo 1 he implementado un contrato inteligente de forma privada, poniendo la clave pública de este ("BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo=") en forma privada.

Mi objetivo es hacer una transferencia a este contrato inteligente, para ejecutar una de sus funciones. Al ser un contrato privado la transferencia tiene que ser firmada.

Todo esto desde un servidor nodejs.

Código

El contrato inteligente es el siguiente:

pragma solidity ^0.4.19;

contract SimpleStorage {
  uint storedData;
  uint secondData;
  uint TData;
  function set(uint x) external {
    storedData = x;
  }
  function setSecond(uint x) external {
    secondData = x;
  }
  function setT(uint x) external {
    TData = x;
  }
  function get() external view returns (uint retVal) {
    return storedData;
  }
  function getSecond() external view returns (uint retVal) {
    return secondData;
  }
  function getT() external view returns (uint retVal) {
    return TData;
  }
}

El código en nodejs es el siguiente:

const Web3 = require('web3');
const EthereumTx = require('ethereumjs-tx');
const contract = require('truffle-contract');
const simpleStorageInterface = require("./contracts/SimpleStorage.json");
var web3 = new Web3(
    new Web3.providers.HttpProvider('http://172.10.4.159:22000')
);

var generalInfo = {
    contractAddress: "0x1932c48b2bf8102ba33b4a6b545c32236e342f34",
    account: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
    keystore: {"address":"ed9d02e382b34818e88b88a309c7fe71e65f419d","crypto":{"cipher":"aes-128-ctr","ciphertext":"4e77046ba3f699e744acb4a89c36a3ea1158a1bd90a076d36675f4c883864377","cipherparams":{"iv":"a8932af2a3c0225ee8e872bc0e462c11"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"8ca49552b3e92f79c51f2cd3d38dfc723412c212e702bd337a3724e8937aff0f"},"mac":"6d1354fef5aa0418389b1a5d1f5ee0050d7273292a1171c51fd02f9ecff55264"},"id":"a65d1ac3-db7e-445d-a1cc-b6c5eeaa05e0","version":3},
    bytecode: "0x608060405234801561001057600080fd5b506101ec806100206000396000f300608060405260043610610078576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680631b03316f1461007d57806322554f34146100a857806360fe47b1146100d35780636d4ce63c14610100578063b698c1291461012b578063f5f3194114610158575b600080fd5b34801561008957600080fd5b50610092610185565b6040518082815260200191505060405180910390f35b3480156100b457600080fd5b506100bd61018f565b6040518082815260200191505060405180910390f35b3480156100df57600080fd5b506100fe60048036038101908080359060200190929190505050610199565b005b34801561010c57600080fd5b506101156101a3565b6040518082815260200191505060405180910390f35b34801561013757600080fd5b50610156600480360381019080803590602001909291905050506101ac565b005b34801561016457600080fd5b50610183600480360381019080803590602001909291905050506101b6565b005b6000600154905090565b6000600254905090565b8060008190555050565b60008054905090565b8060018190555050565b80600281905550505600a165627a7a723058209cc3ec1ccb2383d74522ba2e5974347862883f4c4aa826bb69e6e1cfcc5bdd110029",
    interface: [{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"setSecond","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"setT","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getSecond","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getT","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}],
    privateFor: "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo="
}

web3.eth.getAccounts().then(async (_result) => {
    // Set web3 default account
    web3.eth.defaultAccount = _result[0];

    // Get truffle-contract
    let myContract = contract(simpleStorageInterface);
    // Set provider
    myContract.setProvider(web3.currentProvider);
    if (typeof myContract.currentProvider.sendAsync !== "function") {
        myContract.currentProvider.sendAsync = function() {
            return myContract.currentProvider.send.apply(
                myContract.currentProvider, arguments
            );
        };
    }

    // Instanciate the contract
    var contractInstance = new web3.eth.Contract(simpleStorageInterface.abi, generalInfo.contractAddress);

    // Function data
    let encodedABI = contractInstance.methods.set(123).encodeABI();
    let nonce = await web3.eth.getTransactionCount(web3.eth.defaultAccount);

    // Transaction
    let tx = {
        nonce: web3.utils.toHex(nonce),
        from: generalInfo.account,
        to: generalInfo.contractAddress,
        gas: 2000000,
        gasPrice: 0,
        data: encodedABI,
        privateFor: [generalInfo.privateFor]
    }
    let decrypt = web3.eth.accounts.decrypt(generalInfo.keystore, "");
    // Remove 0x from private key
    var privateKey = decryptKey.privateKey.substring(2);
    var bufferPK = new Buffer(privateK, 'hex');
    // Generate transaction using "ethereumjs-tx"
    var transaction = new EthereumTx(tx);
    // Sign transaction
    transaction.sign(bufferPK);

    // Send signed transaction
    web3.eth.sendSignedTransaction("0x" + transaction.serialize().toString('hex'), (_err, _res) => {
        if(_err){
            console.error("ERROR: ", _err);
        } else {
            console.log("Success: ", _res);
        }
    }).on('confirmation', (confirmationNumber, receipt) => {
        console.log('=> confirmation: ' + confirmationNumber);
    })
    .on('transactionHash', hash => {
        console.log('=> hash');
        console.log(hash);
    })
    .on('receipt', receipt => {
        console.log('=> reciept');
        console.log(receipt);
    })
    .on('error', console.error);
});

pregunta y problema

La transacción se ejecuta con éxito, pero el nuevo valor "123" no se modifica en el contrato inteligente. Solo si desbloqueo la cuenta antes de que la transacción await web3.eth.personal.unlockAccount (account, "")funcione realmente, pero no quiero estar bloqueando y desbloqueando la cuenta por razones de seguridad.

Una transferencia correcta sería la siguiente:

{
  blockHash: "0xe5a2df3f592392c71f9995d697721c046f60c81d2988418b0c7b929cb17a0cee",
  blockNumber: 2190,
  from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
  gas: 90000,
  gasPrice: 0,
  hash: "0x24df9e01d9fdb7acc7a2842dcdd1d93a37e7be7885ef469a262d8a690f7143f3",
  input: "0xb0101ef545cf42bb490bca4fac873ea06d989abf1fbdc89f7dfd09014c085f163c371efa5acac2b43c9dec5cb20bd11e853069a99f4bcb938d6fdcd6f2918333",
  nonce: 31,
  r: "0x9851006a766b4bd75051cdba9c06b6d251125d68894983eee3a4c1a53a03d77a",
  s: "0x1696039cedf14a82147c858bc17896664c9c74bda313307dbf9386b7d6893938",
  to: "0x1932c48b2bf8102ba33b4a6b545c32236e342f34",
  transactionIndex: 0,
  v: "0x25",
  value: 0
}

El mío se ve así:

{
  blockHash: "0x6a2aacceabe984b2c368fa9ca7c245065924dd6d88e30f81311e2a5a7e2aeab8",
  blockNumber: 2119,
  from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
  gas: 2000000,
  gasPrice: 0,
  hash: "0x4a918c1641478c1f229e7cdfff93669a6e08b37555eafe871fc3b05717cbcb79",
  input: "0x60fe47b1000000000000000000000000000000000000000000000000000000000000007b",
  nonce: 30,
  r: "0xac64a34de4bf0c2d3f1d8da6af3d38ee12be38846f744004eeef460ad94b528e",
  s: "0x10e642925665877c4e2571a2f835af68c025417574462ffc4864e6128e4a4deb",
  to: "0x1932c48b2bf8102ba33b4a6b545c32236e342f34",
  transactionIndex: 0,
  v: "0x1c",
  value: 0
}

La diferencia es la propiedad "v", el valor que se esta fijando en mi transferencia no es correcto, tiene que ser 0x25 o 0x26 para identificarlas como transferencias privadas. No entiendo por qué estos valores no están configurando bien, ayúdenme por favor.

Sus preguntas son confusas, por favor haga sus preguntas mucho más claras. sobre el bloqueo y desbloqueo, es muy habitual y necesario para realizar cualquier transacción. si maneja las billeteras ethereum como lo hace, tendrá que desbloquear, realizar transacciones y bloquear la cuenta.

Respuestas (1)

Por el momento, Quorum no admite transacciones autofirmadas o firmadas externamente con privateFor/privateFrom (las cosas que habilitan la privacidad). sincroniza fuera de la cadena con partes privadas y, en el paso final de este proceso, escribe un txn completamente diferente en el bloque: sin sus claves en el nodo, el nodo no puede volver a firmar. Próximamente habrá un parche para esto, pero será un proceso diferente al que tiene en su código.

Se agregó esta función --> github.com/jpmorganchase/quorum/pull/591
Eso es correcto. La función que habilita esta función se ha comprometido con tessera y Quorum, pero aún no se ha lanzado. El lanzamiento también es inminente.