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.
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.
kaki maestro del tiempo