Was passiert, wenn eine Transaktionsnonce zu hoch ist?

Aus der Design-Begründung :

Eine Schwäche des Kontoparadigmas besteht darin, dass jede Transaktion eine "Nonce" haben muss, um Replay-Angriffe zu verhindern, sodass das Konto die verwendeten Nonces verfolgt und eine Transaktion nur akzeptiert, wenn ihre Nonce nach der letzten verwendeten Nonce 1 ist.


Auf dieser Seite gab es einige Fragen zu zu niedrigen Transaktionsnonces. Was passiert, wenn eine Transaktionsnonce zu hoch ist?

Antworten (4)

Zusammenfassung

  • Transaktionen mit einer zu niedrigen Nonce werden sofort abgelehnt.
  • Transaktionen mit zu hoher Nonce werden in die Warteschlange des Transaktionspools gestellt.
  • Wenn Transaktionen mit Nonces gesendet werden, die die Lücke zwischen der letzten gültigen Nonce und der zu hohen Nonce füllen, und die Nonce-Sequenz vollständig ist, werden alle Transaktionen in der Sequenz verarbeitet und abgebaut.
  • Wenn die Geth-Instanzen heruntergefahren und neu gestartet werden, verschwinden Transaktionen in der Transaktionspool-Warteschlange.
  • Die Transaktionspoolwarteschlange enthält nur maximal 64 Transaktionen mit derselben From:Adresse mit Nonces außerhalb der Reihenfolge.
  • Die Geth-Instanz kann zum Absturz gebracht werden, indem die Transaktionspool-Warteschlange mit 64 Transaktionen mit derselben From:Adresse mit Nonces außerhalb der Reihenfolge gefüllt wird, beispielsweise durch:
    • Erstellen vieler Konten mit einer minimalen Menge an Ether (0,05 ETH in meinen Tests)
    • Senden von 64 Transaktionen von jedem Konto mit einer großen Datennutzlast
    • Für das 4-GB-Speicherlimit, das ich meiner Geth-Instanz auferlegt habe, würden 400 x 64 Transaktionen mit einer Nutzlast von etwa 4.500 Bytes die Geth-Instanz zum Absturz bringen (jedenfalls nach meinen begrenzten Tests).
    • Diese Transaktionen mit zu hoher Nonce werden NICHT an andere Knoten weitergegeben und bringen andere Knoten im Ethereum-Netzwerk zum Absturz.
  • Der Ethereum World Computer kann nicht mit Transaktionen mit zu hoher Nonce heruntergefahren werden. Gute Arbeit, Entwickler!


Details unten.



Was passiert, wenn die Transaktions-Nonce zu niedrig ist?

Ich habe zwei neue Konten in meinem privaten --dev-Netzwerk mit erstellt

geth -datadir ./data --dev account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: 
Repeat Passphrase: 
Address: {c18f2996c11ba48c7e14233710e5a8580c4fb9ee}

geth -datadir ./data --dev account new
Your new account is locked with a password. Please give a password. Do not forget this password.
Passphrase: 
Repeat Passphrase: 
Address: {e1fb110faa8850b4c6be5bdb3b7940d1ede87dfb}

Ich habe eine Mining-Geth-Instanz in einem separaten Fenster gestartet, sodass auf das erste Konto (Coinbase) mehr Geld eingezahlt wird, wenn neue Blöcke abgebaut werden.

geth --datadir ./data --dev --mine --minerthreads 1 console

In meinem Hauptfenster habe ich eine Geth-Instanz gestartet, die an die Mining-Instanz angehängt ist.

geth --datadir ./data --dev attach

Ich sende meine erste Transaktion von meinem ersten Konto an mein zweites Konto

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether")})
Unlock account c18f2996c11ba48c7e14233710e5a8580c4fb9ee
Passphrase: 
"0xd17510a4c9880155b0237cac58423e05b61ad3f2d5ee90f72d3db2b7d4ea2d47"

Hier sind die Transaktionsdetails für die erste Transaktion. Für diese Transaktion wurde automatisch eine Nonce von 0 zugewiesen.

 > eth.getTransaction("0xd17510a4c9880155b0237cac58423e05b61ad3f2d5ee90f72d3db2b7d4ea2d47")
 {
   blockHash: "0x519ddf8c3d1a094933d2975bb7c9cdf3680c9d66b880ba22b26627f70d90bb54",
   blockNumber: 88,
   from: "0xc18f2996c11ba48c7e14233710e5a8580c4fb9ee",
   gas: 90000,
   gasPrice: 20000000000,
   hash: "0xd17510a4c9880155b0237cac58423e05b61ad3f2d5ee90f72d3db2b7d4ea2d47",
   input: "0x",
   nonce: 0,
   to: "0xe1fb110faa8850b4c6be5bdb3b7940d1ede87dfb",
   transactionIndex: 0,
   value: 1000000000000000000
 }

Ich sende eine zweite Transaktion von meinem ersten Konto an mein zweites Konto, gebe eine Nonce von 0 an und erhalte das erwartete Ergebnis von "Nonce too low".

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether"), nonce:0})
Nonce too low
    at InvalidResponse (<anonymous>:-81662:-106)
    at send (<anonymous>:-156322:-106)
    at sendTransaction (<anonymous>:-133322:-106)
    at <anonymous>:1:1



Was passiert, wenn die Transaktions-Nonce zu hoch ist?

Ich sende nun eine dritte Transaktion von meinem ersten Konto an mein zweites Konto, gebe eine Nonce von 10000 an und bekomme einen Transaktions-Hash zurück, dass die Transaktion an den Transaktionspool gesendet wurde.

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether"), nonce:10000})
"0x5b09270d6bcd33297527a1f6b08fa1528deec01e82a100c7e62ee93fbdcd1f7d"

Im Mining-Fenster zeigt eine Nachricht an, dass die Transaktion eingegangen ist. Die Transaktion wird jedoch nie abgebaut.

I0409 15:25:07.699859   10726 worker.go:569] commit new work on block 95 with 0 txs & 0 uncles. Took 289.587µs
I0409 15:25:08.493883   10726 xeth.go:1028] Tx(0x5b09270d6bcd33297527a1f6b08fa1528deec01e82a100c7e62ee93fbdcd1f7d) to: 0xe1fb110faa8850b4c6be5bdb3b7940d1ede87dfb
> I0409 15:26:13.472919   10726 worker.go:348] 🔨  Mined block (#95 / 7fe1ada0). Wait 5 blocks for confirmation
I0409 15:26:13.473634   10726 worker.go:569] commit new work on block 96 with 0 txs & 0 uncles. Took 630.605µs
I0409 15:26:13.473707   10726 worker.go:447] 🔨 🔗  Mined 5 blocks back: block #90
I0409 15:26:13.474252   10726 worker.go:569] commit new work on block 96 with 0 txs & 0 uncles. Took 447.451µs
I0409 15:26:18.921404   10726 worker.go:348] 🔨  Mined block (#96 / 760e117c). Wait 5 blocks for confirmation
I0409 15:26:18.922033   10726 worker.go:569] commit new work on block 97 with 0 txs & 0 uncles. Took 547.204µs
I0409 15:26:18.922096   10726 worker.go:447] 🔨 🔗  Mined 5 blocks back: block #91

Ich versuche, die Transaktionsdetails für meine dritte Transaktion abzurufen. Der blockHash und die blockNumber bleiben für immer null.

> eth.getTransaction("0x5b09270d6bcd33297527a1f6b08fa1528deec01e82a100c7e62ee93fbdcd1f7d")
{
  blockHash: null,
  blockNumber: null,
  from: "0xc18f2996c11ba48c7e14233710e5a8580c4fb9ee",
  gas: 90000,
  gasPrice: 20000000000,
  hash: "0x5b09270d6bcd33297527a1f6b08fa1528deec01e82a100c7e62ee93fbdcd1f7d",
  input: "0x",
  nonce: 10000,
  to: "0xe1fb110faa8850b4c6be5bdb3b7940d1ede87dfb",
  transactionIndex: null,
  value: 1000000000000000000
}

Ich überprüfe den Status des Transaktionspools und gehe davon aus, dass sich die Transaktion mit Nonce 10000 in der Warteschlange befindet.

> txpool.status
{
  pending: 0,
  queued: 1
}

Ich versuche, eine vierte Transaktion mit einer Nonce von 1 zu senden. Die Transaktion wird abgebaut.

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether"), nonce:1})
"0x545af0a0276e154a8669921373de8904a330b829318a7c83f5bd9f9771e71ff8"
> eth.getTransaction("0x545af0a0276e154a8669921373de8904a330b829318a7c83f5bd9f9771e71ff8")
{
  blockHash: "0xc125f5da96e36ac87728a35eae8ff8046bcc08c6242825daa4b6bb1e7b460a01",
  blockNumber: 101,
  from: "0xc18f2996c11ba48c7e14233710e5a8580c4fb9ee",
  gas: 90000,
  gasPrice: 20000000000,
  hash: "0x545af0a0276e154a8669921373de8904a330b829318a7c83f5bd9f9771e71ff8",
  input: "0x",
  nonce: 1,
  to: "0xe1fb110faa8850b4c6be5bdb3b7940d1ede87dfb",
  transactionIndex: 0,
  value: 1000000000000000000
}

Also versuche ich, eine fünfte Transaktion mit einer Nonce von 3 zu senden (es gibt jetzt eine Lücke, da die letzte gültige Nonce 1 ist). Die Transaktion geht in die Warteschlange des Transaktionspools und wird nicht abgebaut.

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether"), nonce:3})
"0x895ec329c3a1d53acf7a429721025f2ff01d5558feee0595daa0fa9c0282d461"
> eth.getTransaction("0x895ec329c3a1d53acf7a429721025f2ff01d5558feee0595daa0fa9c0282d461")
{
  blockHash: null,
  blockNumber: null,
  from: "0xc18f2996c11ba48c7e14233710e5a8580c4fb9ee",
  gas: 90000,
  gasPrice: 20000000000,
  hash: "0x895ec329c3a1d53acf7a429721025f2ff01d5558feee0595daa0fa9c0282d461",
  input: "0x",
  nonce: 3,
  to: "0xe1fb110faa8850b4c6be5bdb3b7940d1ede87dfb",
  transactionIndex: null,
  value: 1000000000000000000
}

Ich sende eine sechste Transaktion mit einer Nonce von 2 (dies füllt die Lücke zwischen der letzten gültigen Nonce von 1 und der Transaktion in der Warteschlange mit einer Nonce von 3).

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether"), nonce:2})
"0xea7a6350d6f7aa61a5f515452021de905917338d3b4d354e19fc53d8bd4982f4"

Beide Transaktionen mit den Nonces 2 und 3 werden jetzt abgebaut.

> eth.getTransaction("0xea7a6350d6f7aa61a5f515452021de905917338d3b4d354e19fc53d8bd4982f4")
{
  blockHash: "0xabcfea8140fdbe3d04bab05cb0232a8c73de4a6bc2307907ede9d45ad58d7107",
  blockNumber: 170,
  from: "0xc18f2996c11ba48c7e14233710e5a8580c4fb9ee",
  gas: 90000,
  gasPrice: 20000000000,
  hash: "0xea7a6350d6f7aa61a5f515452021de905917338d3b4d354e19fc53d8bd4982f4",
  input: "0x",
  nonce: 2,
  to: "0xe1fb110faa8850b4c6be5bdb3b7940d1ede87dfb",
  transactionIndex: 0,
  value: 1000000000000000000
}
> eth.getTransaction("0x895ec329c3a1d53acf7a429721025f2ff01d5558feee0595daa0fa9c0282d461")
{
  blockHash: "0xabcfea8140fdbe3d04bab05cb0232a8c73de4a6bc2307907ede9d45ad58d7107",
  blockNumber: 170,
  from: "0xc18f2996c11ba48c7e14233710e5a8580c4fb9ee",
  gas: 90000,
  gasPrice: 20000000000,
  hash: "0x895ec329c3a1d53acf7a429721025f2ff01d5558feee0595daa0fa9c0282d461",
  input: "0x",
  nonce: 3,
  to: "0xe1fb110faa8850b4c6be5bdb3b7940d1ede87dfb",
  transactionIndex: 1,
  value: 1000000000000000000
}

Ich überprüfe den Status des Transaktionspools und die Transaktion mit Nonce 10000 befindet sich immer noch in der Warteschlange und wird für immer bleiben.

> txpool.status
{
  pending: 0,
  queued: 1
}

Ich fahre meine Mining-Geth-Instanz und meine angehängte Geth-Instanz herunter und starte beide neu. Ich überprüfe jetzt den Status des Transaktionspools und die Transaktion mit Nonce 10000 ist verschwunden.

> txpool.status
{
  pending: 0,
  queued: 0
}



Quellcode des Transaktionspools

Betrachtet man core/tx_pool.go (#48-50) , gibt es eine maximale Warteschlangengröße von 64 Transaktionen für Transaktionen mit Out-of-Order-Nonce-Sequenz pro Sendeadresse.

const (
    maxQueued = 64 // max limit of queued txs per address
)

Und core/tx_pool.go (#436-456) zeigt den Code, der Transaktionen entfernt, wenn die Warteschlange zu voll ist:

for i, entry := range promote {
    // If we reached a gap in the nonces, enforce transaction limit and stop
    if entry.Nonce() > guessedNonce {
        if len(promote)-i > maxQueued {
            if glog.V(logger.Debug) {
                glog.Infof("Queued tx limit exceeded for %s. Tx %s removed\n", common.PP(address[:]), common.PP(entry.hash[:]))
            }
            for _, drop := range promote[i+maxQueued:] {
                delete(txs, drop.hash)
            }
        }
        break
    }
    // Otherwise promote the transaction and move the guess nonce if needed
    pool.addTx(entry.hash, address, entry.Transaction)
    delete(txs, entry.hash)

    if entry.Nonce() == guessedNonce {
        guessedNonce++
    }
}



Crashtest von Geth mit zu hoher Nonce

  • Für meine Tests habe ich eine Mining-Geth-Instanz mit einer Peer-to-Peer-verbundenen Nicht-Mining-Geth-Instanz in einem privaten Entwicklernetzwerk erstellt.
  • Ich habe die Mining-Geth-Instanz auf 4 GB begrenzt, indem ich meine Auslagerungsdatei (die sudo swapoff -aunter Linux läuft) abschalte und andere speicherraubende Programme ausführe.
  • Ich habe ein Perl-Skript erstellt, um iterativ (1 .. 20.000) neue Konten auf meiner Mining-Geth-Instanz zu erstellen und 0,05 ETH von meiner Coinbase in die neuen Konten zu übertragen.
  • Ich habe ein weiteres Perl-Skript erstellt, um iterativ (1 .. 20.000) jedes neue Konto in der Mining-Geth-Instanz zu entsperren und 64-Transaktionen mit zu hoch eingestelltem Nonce und mit einer Datennutzlast von ~ 4.500 Bytes zu senden.
  • Die Transaktionen mit einer zu hohen Nonce füllten schließlich die Transaktionspool-Warteschlange der Mining-Geth-Instanz und brachten Geth bei etwa der 400. Iteration zum Absturz. Mein Computer ist auch heruntergefahren.
  • Die Nicht-Mining-Geth-Instanz hat keine der Transaktionen mit einer zu hohen Nonce erhalten.
  • Um zu bestätigen, dass sich die Transaktion mit zu hoher Nonce nicht von Knoten zu Knoten ausbreitet, habe ich auch manuell eine Transaktion mit zu hoher Nonce auf der Nicht-Mining-Geth-Instanz erstellt und nur die Transaktionspool-Warteschlange der Nicht-Mining-Geth-Instanz gefüllt .
Welche Intelligenz-/Frage-Antwort-Droge Sie auch immer nehmen, ich will welche. Episches Werk. 🌟
Ich habe dies aus Neugier getan und einen potenziellen Angriffsvektor im Ethereum-Netzwerk gefunden – vielleicht auch nicht. Ist aber zu schwer zum Testen. Danke.
10 weitere Benutzer mögen Bokky, und wir sind bereit für Shanghai . <3
Informationen zur Zusammenfassung hinzugefügt und neuer Abschnitt Crash Testing Geth hinzugefügt. Der Ethereum World Computer ist sicher vor Angriffen mit Transaktionen mit einer zu hohen Nonce - zumindest nach meinen begrenzten Tests.
Um diesen Kommentaren hinzuzufügen ... diese Antwort ist hervorragend. Vielen Dank, dass Sie sich die Zeit genommen haben, darauf zu antworten.
Haben Sie vor ` sudo swapoff -a ` irgendetwas getan, um 4 GB festzulegen? Was war der Vorteil von Swapoff? @BokkyPooBah
Mein physischer Speicher war 4 GB. Darüber hinaus hatte ich eine Auslagerungsdatei von 4 GB, sodass der gesamte effektive Speicher 8 GB betrug. swapoff -aschaltet die Auslagerungsdatei aus, sodass mein effektiver Speicher nur 4 GB beträgt.
Warum lassen Nodes keine zu hohen Nonces zu? Es wäre schön, Transaktionen schnell übermitteln zu können, ohne prüfen zu müssen, ob jede erfolgreich geschürft wurde. Wenn Sie dies tun, besteht derzeit die Gefahr, dass Sie eine Nonce-Lücke haben.
@TheOfficiousBokkyPooBah Was passiert, wenn zwei verschiedene dApps, vollständig isoliert, Transaktionen für dasselbe Benutzerkonto einreichen? Da sie nicht über die Nonce-Auswahl kommunizieren, scheint dies ein großes Problem zu sein? Oder übersehe ich etwas? Ich dachte, dass Nonce für ein bestimmtes Konto zusammenhängend sein müssten (mit Ausnahme von Duplikaten mit höherem Gas). Wenn ein Benutzer Transaktionen auf zwei verschiedenen dApps initiiert oder zwei dApps im Namen des Benutzers übermitteln, wie um alles in der Welt könnten Sie mit einem zusammenhängenden Nonce-Stream enden?
Wenn ich eine beste Antwort auf StackExchange auswählen muss, werde ich diese auswählen.

Wenn Sie Hardhat + Metamask verwenden und dies nach dem Neustart des Knotens sehen, versuchen Sie, Ihr Konto auf Metamask zurückzusetzen: Einstellungen > Erweitert > Konto zurücksetzen.

Was passiert, wenn eine Transaktionsnonce zu hoch ist?

Eine „zu hohe“ Nonce-Transaktion könnte in tx_pool hinzugefügt werden und die Validierung der ersten Stufe bestehen, aber sie wird in der Validierung der zweiten Stufe während der Zustandsübergangsausführung fehlschlagen.
Es folgte strikt der Design-Rationale: "Akzeptiert eine Transaktion nur, wenn ihre Nonce 1 nach der letzten verwendeten Nonce ist."

go-ethereum/core/state_transition.go

func (self *StateTransition) preCheck() (err error) {
    msg := self.msg
    sender := self.from()

    // Make sure this transaction's nonce is correct
    if msg.CheckNonce() {
        if n := self.state.GetNonce(sender.Address()); n != msg.Nonce() {
            return NonceError(msg.Nonce(), n)
        }
    }

    // Pre-pay gas
    if err = self.buyGas(); err != nil {
        if IsGasLimitErr(err) {
            return err
        }
        return InvalidTxError(err)
    }

    return nil
}
Ich bin ziemlich erstaunt, dass nur +1 einmalig akzeptiert wird. Das fügt meiner Meinung nach unnötige Komplexität und Synchronisierungsanforderungen zwischen 2 Instanzen hinzu, die dasselbe Konto verwenden. Auch bei Offline-Knoten oder leichten Knoten müssen sie den Netzwerkstatus kennen, um eine Transaktion zu signieren. Ich verstehe, warum Nonce benötigt wird, aber warum ist es so streng, dass es nur +1 zulässt?

Beim Blockchain.info-Austausch, bei dem Shapeshift-Transfers zwischen zwei Kryptos, z. B. Ethereum zu Bitcoin, verwendet werden, wird diese Meldung angezeigt, wenn Ihr erster Transaktionsstatus noch aussteht.

Es soll vor Doppeltransaktionen schützen. Warten Sie, bis die erste Transaktion abgeschlossen ist, bevor Sie versuchen, weitere zu verschieben.