Error de respuesta JSON RPC no válido para sendTransaction en Infura + nodo Ropsten + consola Truffle

Las llamadas funcionan pero las transacciones arrojan el error :

Error: respuesta JSON RPC no válida: ""

Estoy usando web3 v0.19.0 y Truffle v3.4.9. Desplegando el contrato usando truffle,
truffle migrate --network ropsten
que proporciona con éxito la API y la dirección del contrato.

Mi proveedor web3 y la red ropsten (nodo infura) están definidos en truffle.js además del proyecto react-auth-box .

Abro truffle console --network ropsteny defino web3 -

var Web3 = require('web3')
let web3 = new Web3()
web3.setProvider(new web3.providers.HttpProvider('https://ropsten.infura.io/my_access_token_here')) 

No hay una cuenta predeterminada
web3.eth.defaultAccount(devuelve nulo)
web3.eth.accounts(devuelve [])

Establecer cuenta predeterminada,
web3.eth.defaultAccount = '0xpersonalaccount'

Definir instancia de contrato,
let contract = web3.eth.contract(abi).at(address)

Todo bien hasta ahora y las llamadas funcionan -
contract.checkIdExists.call(1, {'from': account, 'to': address})
(Devuelve '0x0000000000')

contract.fetchDataById.call(1, {'from': account, 'to': address})
(Devuelve '0x')

1. Las transacciones fallan -
contract.addRecord.sendTransaction(1, 'fjdnjsnkjnsd', '03:00:21 12-12-12', 'true', '', {'from': account, 'to': address})

Error: respuesta JSON RPC no válida: "" en Object.InvalidResponse (/var/www/html/react-auth-box/node_modules/web3/lib/web3/errors.js:38:16) en HttpProvider.send (/var /www/html/react-auth-box/node_modules/web3/lib/web3/httpprovider.js:91:22) en RequestManager.send (/var/www/html/react-auth-box/node_modules/web3/lib /web3/requestmanager.js:58:32) en Eth.send [como sendTransaction] (/var/www/html/react-auth-box/node_modules/web3/lib/web3/method.js:145:58) en SolidityFunction.sendTransaction (/var/www/html/react-auth-box/node_modules/web3/lib/web3/function.js:167:26) en evalmachine.:1:20 en ContextifyScript.Script.runInContext (vm.js :53:29) en Object.runInContext (vm.js:108:6) en TruffleInterpreter.interpret (/home/shivam/.npm-global/lib/node_modules/truffle/build/cli.bundled.js:213786:17 ) en el límite (dominio.js:301:14)

2. Me hace pensar que probablemente deba desbloquear la cuenta primero (¿o sí?)
web3.personal.unlockAccount(account, password)

Error: respuesta JSON RPC no válida: "" en Object.InvalidResponse (/var/www/html/react-auth-box/node_modules/web3/lib/web3/errors.js:38:16) en HttpProvider.send (/var /www/html/react-auth-box/node_modules/web3/lib/web3/httpprovider.js:91:22) en RequestManager.send (/var/www/html/react-auth-box/node_modules/web3/lib /web3/requestmanager.js:58:32) en Personal.send [como unlockAccount] (/var/www/html/react-auth-box/node_modules/web3/lib/web3/method.js:145:58) en evalmachine.:1:15 en ContextifyScript.Script.runInContext (vm.js:53:29) en Object.runInContext (vm.js:108:6) en TruffleInterpreter.interpret (/home/shivam/.npm-global/lib /node_modules/truffle/build/cli.bundled.js:213786:17) en el límite (dominio.js:301:14) en REPLServer.runBound [como evaluación] (dominio.js:314:12)

Corriendo bastante despistado por ahora. Cualquier apoyo será apreciado. ¡Gracias!

Infura no es compatible con sendTransaction, debe administrar las claves de su lado y enviar transacciones firmadas usando sendRawTransaction. Para firmar una transacción, puede usar ethereumjs-tx github.com/ethereumjs/ethereumjs-tx
Gracias Ismael, eso ayudó! Supongo que el nodo infura no tiene la cuenta predeterminada (o algo así), un poco confundido sobre el razonamiento exacto.
@ShivamD Creo que la razón de esto es que Infura no tiene sus claves privadas. Esto significa que no pueden firmar una transacción en su nombre. Consulte esta publicación para obtener más información: ethereum.stackexchange.com/questions/6905/…

Respuestas (3)

Permítanme publicar la respuesta completa aquí (Créditos a @Ismael).

Paquetes relevantes:
web3@0.18.2
ethereumjs-tx@1.3.3
crypto-js

const Web3 = require('web3')  
let web3 = new Web3()  
web3.providers.HttpProvider('https://ropsten.infura.io/my_access_token_here'))  
let contract = web3.eth.contract(abi).at(address)  
var coder = require('web3/lib/solidity/coder')  
var CryptoJS = require('crypto-js')  
var privateKey = new Buffer(myPrivateKey, 'hex')  

var functionName = 'addRecord'  
var types = ['uint','bytes32','bytes20','bytes5','bytes']  
var args = [1, 'fjdnjsnkjnsd', '03:00:21 12-12-12', 'true', '']  
var fullName = functionName + '(' + types.join() + ')'  
var signature = CryptoJS.SHA3(fullName,{outputLength:256}).toString(CryptoJS.enc.Hex).slice(0, 8)  
var dataHex = signature + coder.encodeParams(types, args)  
var data = '0x'+dataHex  

var nonce = web3.toHex(web3.eth.getTransactionCount(account))  
var gasPrice = web3.toHex(web3.eth.gasPrice)  
var gasLimitHex = web3.toHex(300000) (user defined)  
var rawTx = { 'nonce': nonce, 'gasPrice': gasPrice, 'gasLimit': gasLimitHex, 'from': account, 'to': address, 'data': data}  
var tx = new Tx(rawTx)  
tx.sign(privateKey)  
var serializedTx = '0x'+tx.serialize().toString('hex')  
web3.eth.sendRawTransaction(serializedTx, function(err, txHash){ console.log(err, txHash) })   

(Devuelve '0xf802614fd6a53cb372752634630265063d0b48fec12ea8f5ed363de1d4bd372d')

web3.eth.getTransaction('0xf802614fd6a53cb372752634630265063d0b48fec12ea8f5ed363de1d4bd372d', console.log)

(Imprime los datos de la transacción)
(Consulte aquí )

donde myPrivateKeynormalmente se almacena en ubuntu cuando se hace con geth

Mayor que el cambio web1.0.

var myPrivateKey = "xxxxxx";
var privateKey = new Buffer(myPrivateKey, 'hex')

var functionName = 'add token';
var types = ['uint', 'bytes32', 'string', 'bool', 'bytes'];
var args = [123, '0xdf3234', '03:00:21 12-12-12', true, '0xdf3234'];
var fullName = functionName + '(' + types.join() + ')';
var signature = CryptoJS.SHA3(fullName, { outputLength: 256 }).toString(CryptoJS.enc.Hex).slice(0, 8)
var dataHex = signature + Web3.eth.abi.encodeParameters(types, args)
var data = '0x' + dataHex;
// console.log(99, dataHex)

var rawTx = {
    nonce: Web3.utils.toHex(await Web3.eth.getTransactionCount(cfg.addr.accountA).then(data => data)),
    gasPrice: Web3.utils.toHex(await Web3.eth.getGasPrice().then(data => data)),
    gasLimit: Web3.utils.toHex(300000), // Web3.toHex(300000)
    // from: '',
    to: cfg.addr.accountB,
    value: Web3.utils.toHex(10 ** 16),
    data
}

var tx = new Tx(rawTx)
tx.sign(privateKey)
var serializedTx = '0x' + tx.serialize().toString('hex')

const res = await Web3.eth.sendSignedTransaction(serializedTx)
    .on('transactionHash', function (hash) {
        console.log(100, hash)
    })
    .on('receipt', function (receipt) {
        console.log(101, receipt)
        return receipt;
    })
    .on('confirmation', function (confirmationNumber, receipt) {
        // console.log(102, confirmationNumber, receipt)
    })
    .on('error', (e) => {
        console.log(103, e)
    })

Editar: Sí, al enviar una transacción al nodo Infura, debe crearse y firmarse de antemano, ya que solo admite el eth_sendRawTransactionmétodo JSON RPC.

Ya no es necesario usar la biblioteca ethereumjs-tx para realizar transacciones, puede hacerlo solo con core web3.js 1.0, no necesita más,

Firmar transacciones automáticamente usando solo web3.js 1.0, usando web3.eth.sendTransaction()la clave privada proporcionada de la cuenta de envío:

Cuando especifique web3.eth.defaultAccount, agregue su clave privada en web3.eth.accounts.wallet (también conocido como almacén de claves).

...   
web3.eth.wallet.add('The private key of the sending account')
...

De lo contrario, la API de web3.js no tiene forma de firmar una transacción porque no sabe dónde está la clave privada defautlAccount y lo hará:

"Error: Returned error: The method eth_sendTransaction does not exist/is not available"

Nota: también debe considerar las implicaciones de seguridad de almacenar la clave privada del usuario en el almacenamiento de su navegador local.

Entonces, por ejemplo, si usara un TestRPC local con cuentas bloqueadas, para hacer lo mismo, podría usar unlockAccountla función en la cuenta que desea usar para firmar y luego firmar una transacción, pero en la variante explicada anteriormente ( agregando una 'billetera') todo lo que necesita hacer es

...
web3.eth.defaultAccount = '0xpersonalaccount'
web3.eth.wallet.add('The private key of your personal account')
...

y ahora puede usarlo web3.eth.sendTransaction()incluso sin especificar una from:propiedad y firmarla después, ya que web3.js lo firmará localmente usando la clave privada de esa cuenta predeterminada y enviará la transacción web3.eth.sendSignedTransaction()automáticamente.

Esto no funciona cuando se llama a un método de contrato a través sendde ocall
Puedo aprobar el comentario de @PaulBerg. Estoy usando methods.myMethod.send(ver web3js.readthedocs.io/en/1.0/… ) y falla con"Error: Returned error: The method eth_sendTransaction does not exist/is not available"
@Ronin, @Paul, no puede usar contract.methods.myMethod.send(), tendrá que usar web3.eth.sendTransaction()o web3.eth.sendSignedTransaction()para crear y firmar la transacción antes de enviarla, como describí en mi respuesta original. Para usar el eth_callno necesita hacer nada relacionado con la administración de la cuenta, excepto especificar el remitente, ya que se usa solo para llamar a funciones de solo lectura (ver) y no modifica el estado en la cadena de bloques (básicamente no es una transacción).