De la justificación del diseño :
Una debilidad del paradigma de la cuenta es que para evitar ataques de repetición, cada transacción debe tener un "nonce", de modo que la cuenta realiza un seguimiento de los nonces utilizados y solo acepta una transacción si su nonce es 1 después del último nonce utilizado.
Ha habido algunas preguntas en este sitio sobre los nonces de transacciones que son demasiado bajos. ¿Qué sucede cuando un nonce de transacción es demasiado alto?
Resumen
From:
dirección con nonces fuera de secuencia.From:
dirección con nonces fuera de secuencia, por ejemplo:
Detalles abajo.
¿Qué sucede cuando el Nonce de la transacción es demasiado bajo?
Creé dos cuentas nuevas en mi red privada --dev usando
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}
Comencé una instancia de geth de minería en una ventana separada, por lo que la primera cuenta (coinbase) se deposita con más fondos a medida que se extraen nuevos bloques.
geth --datadir ./data --dev --mine --minerthreads 1 console
En mi ventana principal, comencé una instancia de geth que se adjunta a la instancia de minería.
geth --datadir ./data --dev attach
Envío mi primera transacción desde mi primera cuenta a mi segunda cuenta
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether")})
Unlock account c18f2996c11ba48c7e14233710e5a8580c4fb9ee
Passphrase:
"0xd17510a4c9880155b0237cac58423e05b61ad3f2d5ee90f72d3db2b7d4ea2d47"
Aquí están los detalles de la transacción para la primera transacción. Se ha asignado automáticamente un nonce de 0 para esta transacción.
> 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
}
Envío una segunda transacción desde mi primera cuenta a mi segunda cuenta, especificando un nonce de 0, y obtengo el resultado esperado de "Nonce demasiado bajo".
> 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
¿Qué sucede cuando el Nonce de la transacción es demasiado alto?
Ahora envío una tercera transacción desde mi primera cuenta a mi segunda cuenta, especificando un nonce de 10000, y obtengo un hash de transacción que indica que la transacción se envió al grupo de transacciones.
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether"), nonce:10000})
"0x5b09270d6bcd33297527a1f6b08fa1528deec01e82a100c7e62ee93fbdcd1f7d"
En la ventana de minería, un mensaje muestra que se ha recibido la transacción. Sin embargo, la transacción nunca se extrae.
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
Intento recuperar los detalles de la transacción para mis terceras transacciones. blockHash y blockNumber permanecen nulos para siempre.
> 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
}
Verifico el estado del grupo de transacciones y asumo que la transacción con nonce 10000 está en la cola.
> txpool.status
{
pending: 0,
queued: 1
}
Intento enviar una cuarta transacción con un nonce de 1. La transacción se extrae.
> 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
}
Así que intento enviar una quinta transacción con un nonce de 3 (ahora hay una brecha ya que el último nonce válido es 1). La transacción entra en la cola del grupo de transacciones y no se extrae.
> 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
}
Envío una sexta transacción con un nonce de 2 (esto llena el espacio entre el último nonce válido de 1 y la transacción en cola con un nonce de 3).
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether"), nonce:2})
"0xea7a6350d6f7aa61a5f515452021de905917338d3b4d354e19fc53d8bd4982f4"
Ambas transacciones con los nonces de 2 y 3 ahora se minan.
> 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
}
Verifico el estado del grupo de transacciones y la transacción con nonce 10000 todavía está en la cola y permanecerá para siempre.
> txpool.status
{
pending: 0,
queued: 1
}
Cierro mi instancia geth de minería y mi instancia geth adjunta, y reinicio ambas. Ahora verifico el estado del grupo de transacciones y la transacción con nonce 10000 ha desaparecido.
> txpool.status
{
pending: 0,
queued: 0
}
Código fuente del grupo de transacciones
Mirando core/tx_pool.go (#48-50) , hay un tamaño máximo de cola de 64 transacciones para transacciones con secuencia nonce fuera de orden por dirección de envío.
const (
maxQueued = 64 // max limit of queued txs per address
)
Y core/tx_pool.go (#436-456) muestra el código que elimina las transacciones si la cola está demasiado llena:
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++
}
}
Crash Testing Geth con un Nonce demasiado alto
sudo swapoff -a
en Linux) y ejecutando otros programas de acaparamiento de memoria.si está usando hardhat + metamask y ve esto después de reiniciar el nodo, intente restablecer su cuenta en metamask: Configuración> Avanzado> Restablecer cuenta.
¿Qué sucede cuando un nonce de transacción es demasiado alto?
La transacción nonce "demasiado alta" podría agregarse a tx_pool y pasar la validación de la primera etapa, pero fallará en la validación de la segunda etapa durante la ejecución de la transición de estado.
Seguía estrictamente la razón de diseño: "solo acepta una transacción si su nonce es 1 después del último nonce utilizado".
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
}
En el intercambio Blockchain.info, al hacer uso de transferencias de cambio de forma entre dos criptos, por ejemplo, ethereum a bitcoin, este mensaje aparece si el estado de su primera transacción aún está pendiente.
Es para salvaguardar contra transacciones dobles. Espere hasta que se complete la primera transacción antes de intentar cambiar más.
tayvano
privacidadisahumanright.eth
q9f
privacidadisahumanright.eth
Tomas Clowes
alper
privacidadisahumanright.eth
swapoff -a
apaga el archivo de intercambio, por lo que mi memoria efectiva es de solo 4 Gb.sudo
Roberto Oschler
Ashwani Agarval