Синтаксис для вызова методов изменения состояния контракта

Я немного боролся с вызовом методов, которые изменяют состояние контракта. Возьмем, к примеру, следующий контракт:

contract C {

     uint[] numbers;

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

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

После развертывания метод initNumbers() должен быть вызван/отправлен как транзакция:

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

из-за того, что он меняет состояние (выполняет операции записи) в цепочке блоков.

Как можно вызвать второй метод, который имеет аргумент, но также меняет состояние (сохраняет его в контракте)?

Я пробовал следующее, но получил исключение BigNumber :

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

Трассировка стека была:

/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)

Ответы (2)

Вот ваша функция, слегка измененная путем добавления public, чтобы numbersмы могли просматривать данные:

contract C {    
     uint[] public numbers;

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

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

Используя stripCrLfсценарий из раздела Как загрузить исходный файл Solidity в geth , я сгладил исходный код с помощью следующей команды:

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); }      }'

Я запускаю сеть разработчиков, используя следующую команду (у меня есть пароль passwordfile):

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

Я вставляю плоский код в gethкомандную строку:

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

И скомпилируйте код с помощью следующей команды:

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

path: /usr/bin/solc
undefined

Вот бинарный интерфейс приложения:

> 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"
}]

Разверните контракт на блокчейне:

> 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

Позвоните своему initNumbers():

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


Вызов stateChanger(...)- Способ 1

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


Вызов stateChanger(...)- Способ 2

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


Вызов stateChanger(...)- Способ 3

Ниже показано, как использовать sendTransaction(...)формат необработанных данных.

Найдите сигнатуру функции. Обратите внимание, что uintпараметр на самом деле является uint256(см. формат ABI выше):

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

Вот 5, закодированное в шестнадцатеричном формате и дополненное до 32 байтов:

0x0000000000000000000000000000000000000000000000000000000000000005

Соедините данные вместе, удалив 0xиз шестнадцатеричной (5) строки:

0x650607750000000000000000000000000000000000000000000000000000000000000005

Вот как это сделать sendTransaction(...). Параметр to:— адрес добытого контракта:

> 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


Бонус 1 -estimateGas(...)

Поскольку мы разработали данные для отправки sendTransaction(...)выше.

Вот газ, используемый sendTransaction(...)вызовом выше:

> eth.getTransactionReceipt(result).gasUsed
46888

Вы можете использовать тот же формат данных для оценки газа с помощью estimateGas(...)вызова:

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


Бонус 2 - Данные транзакции

Установите debug.verbosity(7)и наблюдайте, как транзакция удаляется из пула транзакций после ее выполнения:

> 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

Различные методы вызова ( c.stateChanger(3, {from:eth.accounts[0], gas: 400000})и c.stateChanger.sendTransaction(4, {from: eth.accounts[0], gas: 400000})) преобразуются в sendTransaction(...)формат.

Я собирался опубликовать ответ. Нашел методом проб и ошибок :)) . Вы всегда должны указывать кортеж отправителя/платы вместе с аргументами метода. Спасибо за stripCrLf.
@Sebi, давай, опубликуй свой ответ. Идея сетевых сайтов обмена стеками состоит в том, чтобы иметь более одного ответа, даже если они почти одинаковы. Таким образом, пользователи голосуют за лучших.
Сделанный. Я опубликовал свой ответ.
Я думаю, есть опечатка на 0x650607750000000000000000000000000000000000000000000000000000000000000004 должен быть 0x65060775000000000000000000000000000000000000000000000000000000005, верно?
Как это сделать с помощью Infura и web3@1.0?

Мой ответ похож на ответ Бокки:

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

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

где cContract — объект Javascript, определяющий контракт. Ключевым примечанием является убедиться, что учетная запись, вызывающая метод изменения состояния, всегда имеет достаточный баланс, иначе вызов метода будет прерван, а эфир будет потерян.