Я немного боролся с вызовом методов, которые изменяют состояние контракта. Возьмем, к примеру, следующий контракт:
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)
Вот ваша функция, слегка измененная путем добавления 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
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
Установите 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(...)
формат.
Мой ответ похож на ответ Бокки:
cContract = web3.eth.contract(cContractABI).at(cContractEthAddress);
cContract.initAddrs({from:web3.eth.accounts[0],gas:utils.DEFAULT_TX_COST});
где cContract — объект Javascript, определяющий контракт. Ключевым примечанием является убедиться, что учетная запись, вызывающая метод изменения состояния, всегда имеет достаточный баланс, иначе вызов метода будет прерван, а эфир будет потерян.
Себи
stripCrLf
.q9f
Себи
Хуан Игнасио Перес Сакристан
кицунэ