Sintaxis para llamar a métodos de cambio de estado del contrato

He estado luchando un poco con los métodos de llamada que cambian el estado del contrato. Tomemos, por ejemplo, el siguiente contrato:

contract C {

     uint[] numbers;

     function initNumbers() {
         numbers.push(1);
         numbers.push(2);
     }

     function stateChanger(uint a) {
         numbers.push(a);
     }
}

Después de la implementación, el método initNumbers() debe llamarse/enviarse como una transacción:

c.initNumbers({from:eth.accounts[0],gas:400000});

debido al hecho de que cambia el estado (ejecuta operaciones de escritura) en la cadena de bloques.

¿Cómo es posible llamar al segundo método que tiene un argumento pero también cambia el estado (lo almacena en el contrato)?

Intenté lo siguiente pero obtuve una excepción BigNumber :

c.stateChanger({from:web3.eth.accounts[0],gas:400000}).call(3);

El seguimiento de la pila fue:

/usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:1209
            throw error;
            ^

BigNumber Error: new BigNumber() not a number: [object Object]
    at raise (/usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:1177:25)
    at /usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:1165:33
    at new BigNumber (/usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:193:67)
    at new BigNumber (/usr/lib/node_modules/web3/node_modules/bignumber.js/bignumber.js:203:25)
    at toBigNumber (/usr/lib/node_modules/web3/lib/utils/utils.js:367:12)
    at Object.toTwosComplement (/usr/lib/node_modules/web3/lib/utils/utils.js:378:21)
    at formatInputInt [as _inputFormatter] (/usr/lib/node_modules/web3/lib/solidity/formatters.js:40:38)
    at SolidityType.encode (/usr/lib/node_modules/web3/lib/solidity/type.js:179:17)
    at /usr/lib/node_modules/web3/lib/solidity/coder.js:86:29
    at Array.map (native)

Respuestas (2)

Aquí está su función ligeramente modificada agregando publicpara numbersque podamos ver los datos:

contract C {    
     uint[] public numbers;

     function initNumbers() {
         numbers.push(1);
         numbers.push(2);
     }

     function stateChanger(uint a) {
         numbers.push(a);
     }    
}

Usando el stripCrLfscript en Cómo cargar el archivo fuente de Solidity en geth , aplané el código fuente usando el siguiente comando:

user@Kumquat:~$ echo "var cSource='`stripCrLf C.sol`'"
var cSource='contract C { uint[] public numbers; function initNumbers() { numbers.push(1); numbers.push(2); } function stateChanger(uint a) { numbers.push(a); }      }'

Estoy ejecutando una red de desarrollo usando el siguiente comando (tengo mi contraseña en passwordfile):

user@Kumquat:~$ geth --datadir ~/devdata --dev --mine --minerthreads 1 --unlock 0 --password ~/passwordfile console

Pego el código aplanado en la gethlínea de comando:

> var cSource='contract C { uint[] public numbers; function initNumbers() { numbers.push(1); numbers.push(2); } function stateChanger(uint a) { numbers.push(a); }      }'
undefined

Y compila el código usando el siguiente comando:

> var cCompiled = web3.eth.compile.solidity(cSource);
Version: 0.3.5-0/RelWithDebInfo-Linux/g++/Interpreter

path: /usr/bin/solc
undefined

Aquí está la interfaz binaria de la aplicación:

> cCompiled.C.info.abiDefinition
[{
    constant: false,
    inputs: [],
    name: "initNumbers",
    outputs: [],
    type: "function"
}, {
    constant: false,
    inputs: [{
        name: "a",
        type: "uint256"
    }],
    name: "stateChanger",
    outputs: [],
    type: "function"
}, {
    constant: true,
    inputs: [{
        name: "",
        type: "uint256"
    }],
    name: "numbers",
    outputs: [{
        name: "",
        type: "uint256"
    }],
    type: "function"
}]

Implemente el contrato en la cadena de bloques:

> var cContract = web3.eth.contract(cCompiled.C.info.abiDefinition);
undefined
> var c = cContract.new({
    from:web3.eth.accounts[0], 
    data: cCompiled.C.code, gas: 400000}, 
    function(e, contract) {
      if (!e) {
        if (!contract.address) {
          console.log("Contract transaction send: TransactionHash: " + 
            contract.transactionHash + " waiting to be mined...");
        } else {
          console.log("Contract mined! Address: " + contract.address);
          console.log(contract);
        }
    }
})
Contract mined! Address: 0x672807a8c0f72a52d759942e86cfe33264e73934

Llame a su initNumbers():

> c.initNumbers({from:eth.accounts[0], gas: 400000})
"0x3f21e1cdb636a2cf291cd9296282a4e9c4f4ed57c65a13fedc937a97203c3a75"
...
> c.numbers(0)
1
> c.numbers(1)
2
> c.numbers(2)
0


Llamadas stateChanger(...)- Método 1

> c.stateChanger(3, {from:eth.accounts[0], gas: 400000})
"0x3ecf3aa6464f93d5b062ad01b76b5dbc4302c190baf70d157a4d7607d4c7c749"
...
> c.numbers(2)
3
> c.numbers(3)
0


Llamar stateChanger(...)- Método 2

> c.stateChanger.sendTransaction(4, {from: eth.accounts[0], gas: 400000})
"0xe1d9c57ca55dbdf446e55602570888ff5efce97a91ddc5c9575d10c7c9a1f0c8"
...
> c.numbers(3)
4
> c.numbers(4)
0


Llamar stateChanger(...)- Método 3

sendTransaction(...)Y lo siguiente es cómo usar el formato de datos sin procesar .

Encuentra la firma de la función. Tenga en cuenta que el uintparámetro es en realidad un uint256(consulte el formato ABI anterior):

> web3.sha3('stateChanger(uint256)').substr(0, 10)
"0x65060775"

Aquí hay 5 codificados en hexadecimal y rellenados a 32 bytes:

0x0000000000000000000000000000000000000000000000000000000000000005

Une los datos, eliminando el 0xde la cadena hexadecimal (5):

0x650607750000000000000000000000000000000000000000000000000000000000000005

Aquí está cómo sendTransaction(...). El to:parámetro es la dirección del contrato extraído:

> var result = web3.eth.sendTransaction({
    from: eth.accounts[0], 
    to: "0x672807a8c0f72a52d759942e86cfe33264e73934", 
    data: "0x650607750000000000000000000000000000000000000000000000000000000000000005", 
    gas: 400000}
)
undefined
> result
"0x420d4256718b4326967677da2e460a1d361a62f3ceee386e1313d25b12c0f610"
...
> c.numbers(4)
5
> c.numbers(5)
0


Bono 1 -estimateGas(...)

Ya que hemos elaborado los datos para enviar con sendTransaction(...)arriba.

Aquí está el gas utilizado por la sendTransaction(...)llamada anterior:

> eth.getTransactionReceipt(result).gasUsed
46888

Puede usar el mismo formato de datos para estimar el gas usando la estimateGas(...)llamada:

var result = web3.eth.estimateGas({
    from: eth.accounts[0], 
    to: "0x672807a8c0f72a52d759942e86cfe33264e73934", 
    data: "0x650607750000000000000000000000000000000000000000000000000000000000000005", 
    gas: 400000}
)
undefined
> result
46888


Bono 2 - Los datos de la transacción

Establezca debug.verbosity(7)y observe cómo se elimina la transacción del grupo de transacciones después de que se ejecute:

> debug.verbosity(7)
...
I0730 00:13:43.918828 core/tx_pool.go:547] removed tx (
    TX(07c3c717c87ce1e2b31ba144b6b751d66c90a8a69375478a5ef4e60a6f42996c)
    Contract: false
    From:     a7857047907d53a2e494d5f311b4b586dc6a96d2
    To:       672807a8c0f72a52d759942e86cfe33264e73934
    Nonce:    35
    GasPrice: 20000000000
    GasLimit  400000
    Value:    0
    Data:     0x650607750000000000000000000000000000000000000000000000000000000000000004
    V:        0x1c
    R:        0x75ff67a969670a0f35ae415f2fa541446a71fa9b7d8ad6247ad12fd7ae3986a
    S:        0x7150424ed1698792cdcf1253a79f2474e4df411610db97e36240bab50af6c80c
    Hex:      f889238504a817c80083061a8094672807a8c0f72a52d759942e86cfe33264e7393480a46506077500000000000000000000000000000000000000000000000000000000000000041ca0075ff67a969670a0f35ae415f2fa541446a71fa9b7d8ad6247ad12fd7ae3986aa07150424ed1698792cdcf1253a79f2474e4df411610db97e36240bab50af6c80c
) from pool: low tx nonce or out of funds

Los diferentes métodos de llamada ( c.stateChanger(3, {from:eth.accounts[0], gas: 400000})y c.stateChanger.sendTransaction(4, {from: eth.accounts[0], gas: 400000})) se convierten al sendTransaction(...)formato.

Estaba a punto de publicar la respuesta. Lo encontré por prueba y error :)) . Siempre debe especificar la tupla del remitente/tarifa junto con los argumentos del método. gracias stripCrLfpor
@Sebi sigue adelante y publica tu respuesta. La idea de los sitios de redes de intercambio de pilas es tener más de una respuesta, incluso si son casi iguales. De esta manera, los usuarios votan los mejores hasta la cima.
Hecho. He publicado mi respuesta.
Supongo que hay un error tipográfico en 0x650607750000000000000000000000000000000000000000000000000000000000000000000004 DEBE ser 0x650607500000000000000000000000000000000000000000000000000000000000005 PARA?
¿Cómo hacerlo usando Infura y web3@1.0?

Mi respuesta es similar a la de Bokky:

cContract = web3.eth.contract(cContractABI).at(cContractEthAddress);

cContract.initAddrs({from:web3.eth.accounts[0],gas:utils.DEFAULT_TX_COST});

donde cContract es un objeto Javascript que define un contrato. Una nota clave es asegurarse de que la cuenta que invoca el método de cambio de estado siempre tenga suficiente saldo; de lo contrario, la invocación del método se cancelará y Ether se perderá.