Diese Frage entstand aus der Beantwortung des Transaktionsstatus .
Im folgenden Beispiel sende ich Gas in Höhe von 21.000 (der Betrag, der für eine reguläre Transaktion erforderlich ist). In dieser Situation gas == gasUsed
kann und so nicht verwendet werden, um festzustellen, ob der Vertrag eine Ausnahme ausgelöst hat?
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1.2345, "ether"), gas: 21000})
"0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3"
> eth.getTransaction("0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3")
{
blockHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
blockNumber: null,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gas: 21000,
gasPrice: 20000000000,
hash: "0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3",
input: "0x",
nonce: 55,
to: "0x4d5bbe7fbc80933ffa90ece988a764e41ee6d018",
transactionIndex: null,
value: 1234500000000000000
}
> eth.getTransactionReceipt("0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3")
{
blockHash: "0xf0af8236ceec7ad1839d67c9934ab062a8d95fa1f88b06139f97dbdfbd1cd842",
blockNumber: 2234,
contractAddress: null,
cumulativeGasUsed: 21000,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gasUsed: 21000,
logs: [],
root: "3280f47a0de1149ad5c5fda421faaf95f303da8a77e83c8ec6ac2b3d8ca27abc",
to: "0x4d5bbe7fbc80933ffa90ece988a764e41ee6d018",
transactionHash: "0xc7c63b67747c0c825229ce3d36d226423adb8cab6bebe12b6d5001e0dc3f79b3",
transactionIndex: 0
}
Eine Transaktion kann so eingestellt werden, dass das Gas genau das gleiche ist wie das Gas, das verwendet wird, indem die web3.eth.estimateGas- Funktion verwendet wird, um zunächst das erforderliche Gas zu schätzen.
Wie kann also der Transaktionsstatus aus einem ausgelösten Fehler erkannt werden, wenn das Gas genau dasselbe sein kann wie das Gas, das für eine erfolgreiche Transaktion verwendet wird?
BEARBEITEN 15.06.2016 – Erklären der Unterschiede zu „ Woher weiß ich, wann mir das Benzin ausgegangen ist?“
Bei dieser Frage WISSEN wir bereits, wie wir programmatisch feststellen können, dass uns das Benzin ausgegangen ist. Und wir wissen bereits, dass diese Methode zum Erkennen einer fehlerhaften Transaktion nicht zuverlässig ist, ebenso wie gas == gasUsed
eine gültige Bedingung für eine nicht fehlerhafte Transaktion.
Diese Frage stellt sich die Frage, welche zuverlässigen Alternativen es gibt, um eine fehlerhafte Transaktion zu erkennen, wenn die Situation „kein Benzin mehr“ nicht zuverlässig ist.
Ich habe eine Testtransaktion (#1) gesendet, um zunächst herauszufinden, wie viel Gas eine erfolgreiche Smart-Contract-Transaktion verbraucht. Aus den Ergebnissen dieser Transaktion ergibt sich, dass das für eine erfolgreiche Transaktion erforderliche Gas 26747 beträgt.
Ich habe dann diese 26747 Gasmenge in meiner voraussichtlich erfolgreichen Transaktion (Nr. 2) verwendet. Und gasUsed == gas
.
Ich verwende dann dieselbe Gasmenge von 26747 in meiner Transaktion, die erfolglos bleiben wird (Nr. 3). Und gasUsed == gas
.
Die einzige zuverlässige Möglichkeit, die ich bisher gefunden habe, um einfach zu überprüfen, ob eine Smart-Contract-Transaktion erfolgreich ist, besteht darin debug.traceTransaction
, die letzte Fehlermeldung zu verwenden und zu überprüfen. Wenn dies der Fall ist ""
, ist kein Fehler aufgetreten. Wenn dies "Out of gas"
oder ist "invalid jump destination (PUSH1) 2"
, ist ein Fehler aufgetreten.
Und hier ist ein kurzer Code, um den Status Ihrer Transaktion zu bestimmen.
> var status = debug.traceTransaction("0xd23219e2ea10528b245deb9e47993cae2ffd3ffe9fc27aeb808e94fc4e75d37b")
undefined
> if (status.structLogs.length > 0) {
console.log(status.structLogs[status.structLogs.length-1].error)
}
"invalid jump destination (PUSH1) 2"
Der obige Rückgabewert ist, ""
wenn keine Fehler vorliegen oder "Out of gas"
wenn Ihnen das Benzin ausgeht.
UPDATE 29.07.2016 - Der geth --fast
Blockchain-Download enthält nicht die Informationen für debug.traceTransaction(...)
die Anzeige.
Ich verwende das folgende Beispiel für einen Smart Contract, der eine Ausnahme auslöst, wenn _value < 12345:
contract TestStatus {
uint public value;
function setValue(uint256 _value) {
value = _value;
if (_value < 12345) {
throw;
}
}
}
Ich habe die Quelle reduziert auf:
var testStatusSource='contract TestStatus { uint public value; function setValue(uint256 _value) { value = _value; if (_value < 12345) { throw; } }}'
Ich habe den Vertrag kompiliert und in die Blockchain eingefügt:
var testStatusCompiled = web3.eth.compile.solidity(testStatusSource);
var testStatusContract = web3.eth.contract(testStatusCompiled.TestStatus.info.abiDefinition);
var testStatus = testStatusContract.new({
from:web3.eth.accounts[0],
data: testStatusCompiled.TestStatus.code, gas: 1000000},
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 transaction send: TransactionHash: 0x4de11cc54484333036f45a2441563d6badae43d2e2789e0b113b6703a582a879 waiting to be mined...
undefined
...
Contract mined! Address: 0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c
[object Object]
Ich habe eine Transaktion gesendet, die keinen Fehler bei der Bestimmung des erforderlichen Gases verursacht:
# Not an error
testStatus.setValue(123456, eth.accounts[0], {
from:web3.eth.accounts[0],
data: testStatusCompiled.TestStatus.code,
gas: 41747
});
...
> eth.getTransactionReceipt("0x727cb3c6846bbb05c8437e97aa32db39a252426b09ed837a76bb364748ce73c4")
{
blockHash: "0x293b4d357d72615c409ba37a6430253d64b4646e0366ac987d1364a258cf81fa",
blockNumber: 2465,
contractAddress: null,
cumulativeGasUsed: 26747,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gasUsed: 26747,
logs: [],
root: "fbb29f9eff2340aa8d7df7feec74671c2b693fb9e7b6ec3b710ca6d64813da0e",
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionHash: "0x727cb3c6846bbb05c8437e97aa32db39a252426b09ed837a76bb364748ce73c4",
transactionIndex: 0
}
Das benötigte Gas ist 26747.
Ich habe eine Transaktion gesendet value=123456
, die NICHT fehlschlägt, und gas==gasUsed
:
testStatus.setValue(123456, eth.accounts[0], {
from:web3.eth.accounts[0],
data: testStatusCompiled.TestStatus.code,
gas: 26747
});
0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1
Ich verwende debug.traceTransaction
um zu bestätigen, dass bei dieser Transaktion kein Fehler aufgetreten ist.
> var status = debug.traceTransaction("0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1")
undefined
> status.structLogs[status.structLogs.length-1].error
""
Und in dieser Situationgas(26747) == gasUsed(26747)
eth.getTransaction("0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1")
{
blockHash: "0x961277e2bebfe8332a23d240672b2658eced493b70fd145c99991c3c8651adcc",
blockNumber: 2475,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gas: 26747,
gasPrice: 20000000000,
hash: "0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1",
input: "0x55241077000000000000000000000000000000000000000000000000000000000001e240",
nonce: 66,
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionIndex: 0,
value: 0
}
eth.getTransactionReceipt("0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1")
{
blockHash: "0x961277e2bebfe8332a23d240672b2658eced493b70fd145c99991c3c8651adcc",
blockNumber: 2475,
contractAddress: null,
cumulativeGasUsed: 26747,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gasUsed: 26747,
logs: [],
root: "f850e1e5c99352f47e3409e4e9f966a49c13152e63aaa7bb3765f0b37bfe09e5",
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionHash: "0xc0ab94adfe473811d055f98528a625dbebf66026b7ec5ad8eab939de862bc2d1",
transactionIndex: 0
}
Ich habe dann eine Transaktion gesendet, bei value=123
der das fehlgeschlagen ist, und gas==gasUsed
:
testStatus.setValue(123, eth.accounts[0], {
from:web3.eth.accounts[0],
data: testStatusCompiled.TestStatus.code,
gas: 26747
});
0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a
> var status = debug.traceTransaction("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
undefined
> status.structLogs[status.structLogs.length-1].error
"invalid jump destination (PUSH1) 2"
> eth.getTransaction("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
{
blockHash: "0x22fb5fcaef27dd017efcde8c3f78df7f5168c505210f5d08872a9c2877146044",
blockNumber: 2484,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gas: 26747,
gasPrice: 20000000000,
hash: "0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a",
input: "0x55241077000000000000000000000000000000000000000000000000000000000000007b",
nonce: 67,
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionIndex: 0,
value: 0
}
> eth.getTransactionReceipt("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
{
blockHash: "0x22fb5fcaef27dd017efcde8c3f78df7f5168c505210f5d08872a9c2877146044",
blockNumber: 2484,
contractAddress: null,
cumulativeGasUsed: 26747,
from: "0xa7857047907d53a2e494d5f311b4b586dc6a96d2",
gasUsed: 26747,
logs: [],
root: "ab51ab81c19eb8da7f8d0216d6c2f1d8946e88842b758ff0af13c28ff7e181b4",
to: "0x87847eb0f944fbb6d5c5a4891e3b103a63cee45c",
transactionHash: "0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a",
transactionIndex: 0
}
Der einzige einfache und zuverlässige Weg, den ich gefunden habe, um festzustellen, ob eine intelligente Vertragstransaktion erfolgreich war oder fehlgeschlagen ist, ist die Verwendung von debug.traceTransaction
.
Hier ist die Ausgabe debug.traceTransaction
für die letzte fehlgeschlagene Transaktion.
debug.traceTransaction("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
{
gas: 26747,
returnValue: "",
structLogs: [{
depth: 1,
error: "",
gas: 5280,
gasCost: 3,
memory: null,
op: "PUSH1",
pc: 0,
stack: [],
storage: {}
}, {
depth: 1,
error: "",
gas: 5277,
gasCost: 3,
memory: null,
op: "PUSH1",
pc: 2,
stack: ["0000000000000000000000000000000000000000000000000000000000000060"],
storage: {}
}, {
...
}, {
depth: 1,
error: "invalid jump destination (PUSH1) 2",
gas: 129,
gasCost: 8,
memory: ["0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000060"],
op: "JUMP",
pc: 66,
stack: ["0000000000000000000000000000000000000000000000000000000055241077", "0000000000000000000000000000000000000000000000000000000000000022", "000000000000000000000000000000000000000000000000000000000000007b"],
storage: {
0000000000000000000000000000000000000000000000000000000000000000: "000000000000000000000000000000000000000000000000000000000000007b"
}
}]
}
Und hier ist ein kurzes Skript, um zu überprüfen, ob die Transaktion erfolgreich war oder fehlgeschlagen ist:
> var status = debug.traceTransaction("0x9ee86a200528de32a695f1e2dd0d94a3871fefc7e49c5fd24a4a37eab1b99f7a")
undefined
> status.structLogs[status.structLogs.length-1].error
"invalid jump destination (PUSH1) 2"
debug.traceTransaction
.debug.traceTransaction(...)
Informationen nicht aus dem schnell synchronisierten Teil der Blockchain abrufen können – der Overhead besteht aus Speicherplatz und der Zeit für die Synchronisierung.status.structLogs[status.structLogs.length-1].error
zurück: "{}"
. Kann ich daraus schließen, dass die Transaktion bestanden wurde? @BokkyPooBahdebug.traceTransaction
können sich geändert haben. Hier ist ein TX mit einem Fehler während des TokenCard ICO. var status = debug.traceTransaction("0xe686a5fd36ec7921c847b9285c9ea09724bc6065c7374462a448f0d384adad82");
. status.structLogs[status.structLogs.length-1].error
kehrt zurück {}
. Aber status.structLogs[status.structLogs.length-1].op
kehrt zurück"Missing opcode 0xfd"
status.structLogs[status.structLogs.length-1].op returns
=> "Missing opcode 0xfd"
könnte ich also daraus schließen, dass die Transaktion ausgelöst wird? Ich möchte nur eine Kennung haben, um zu überprüfen, ob die Transaktion ausgelöst wird. @BokkyPooBahop:JUMP and error: {}
.. bedeutet es auch Transaktionswurf? @BokkyPooBahop:RETURN and error: null
ist eine gültige Transaktion? @BokkyPooBahstatus
Kennzeichen hinzugefügt.eth.getTransactionReceipt(transactionHash)
gibt ein status
Feld zurück, das einen Wert hat, 0
wann eine Transaktion fehlgeschlagen ist und 1
wann die Transaktion erfolgreich war.
Der Quittungsstatus ist null, und um festzustellen, dass kein Gas mehr vorhanden ist, muss die Transaktion über das EVM verarbeitet werden, was einige Block-Explorer tun .
Out of Gas kann im Allgemeinen aufgrund der Implikationen des Halteproblems nicht statisch (ohne Ausführung der Transaktion) erkannt werden .
Selbst bei der einfachsten Transaktion, die genau 21.000 Gas hat und verbraucht, muss die Transaktion ausgeführt werden, da der Empfänger den ihm zugesandten Ether durch eine Fallback-Funktion ablehnen könnte. Im Allgemeinen muss der Code in einer Fallback-Funktion ausgeführt werden, um zu bestimmen, ob er eine „Out of Gas“-Ausnahme vermeidet.
gas == gasUsed
ist eine gute praktische Heuristik für Gasmangel, da es sicherer ist, eine angemessene Menge Gas mehr zu liefern, als web3.eth.estimateGas
ungenutztes Gas zurückerstattet wird.
Hier ist mein Python-Code, um dies mit Populus und web3.py zu überprüfen :
from web3 import Web3
from populus.utils.transactions import wait_for_transaction_receipt
class TransactionConfirmationError(Exception):
"""A transaction was not correctly included in blockchain."""
def confirm_transaction(web3: Web3, txid: str, timeout=60) -> dict:
"""Make sure a transaction was correctly performed.
Confirm that
* The transaction has been mined in blockchain
* The transaction did not throw an error (used up all its gas)
http://ethereum.stackexchange.com/q/6007/620
:raise TransactionConfirmationError: If we did not get it confirmed in time
:return: Transaction receipt
"""
try:
receipt = wait_for_transaction_receipt(web3, txid, timeout)
except Timeout as e:
raise TransactionConfirmationError("Could not confirm tx {} within timeout {}".format(txid, timeout)) from e
tx = web3.eth.getTransaction(txid)
if tx["gas"] == receipt["gasUsed"]:
raise TransactionConfirmationError("Transaction failed (out of gas, thrown): {}".format(txid))
return receipt
debug.traceTransaction(...)
Überprüfung nicht zu Ihrem Code hinzu - Sie erhalten dann den genauen Fehler, und Sie können auch Nicht-Fehlerbedingungen erkennen, bei denen gas==gasUsed ?Persönlich habe ich eine andere Problemumgehung als die Methode traceTransaction verwendet. Es besteht einfach darin, dass am Ende der Transaktionen (Vertragsfunktion) ein Ereignisprotokoll (z. B. Completed()) aufgerufen wird. Der Ablauf ist dann:
Es ist komplexer, verwendet aber nicht traceTransaction, das ich als Debugging-Funktion und nicht als Produktionsumgebung betrachte.
Ismael
Datenschutz ist ein Menschenrecht.eth