Почему sendSignedTransaction возвращает хэш tx, НО не отправляет сообщения в сеть Rinkeby (React, Metamask, web3, infura)

Я разрабатываю очень простое приложение React, в котором пользователь нажимает кнопку, и некоторое количество эфира отправляется с одной учетной записи на другую. В настоящее время функция onClick sendEther создает правильно подписанную сериализованную транзакцию и (часто) возвращает хэш транзакции. Однако на самом деле транзакция никогда не проходит через тестовую сеть Rinkeby (доступ к которой осуществляется через Etherscan).

Я подозреваю, что я неправильно использую infura (которую я настроил в своем событии DOMContentLoaded, см. ниже).

//1. Настраивать

document.addEventListener('DOMContentLoaded', () => {
   const endpoint = 'https://rinkeby.infura.io/APIKEY';
   window.web3 = new Web3(new Web3.providers.HttpProvider(endpoint));
}); 

//2. Компонент интерфейса React.

sendEther() {
    const fromAccount = **acct1**;
    const toAccount   = **acct2**;

    const rawTransaction    = this.makeRawTransaction(fromAccount, toAccount);
    const signedTransaction = this.makeSignedTransaction(rawTransaction);
    const serializedTransaction = `0x${signedTransaction.serialize().toString('hex')}`;

    window.web3.eth.sendSignedTransaction(serializedTransaction, (error, result) => {
        if(!error) {
          console.log(`Transaction hash is: ${result}`);
          this.setState({
            etherscanUrl: `https://rinkeby.etherscan.io/tx/${result}`,
            error: null
          });

        } else {
          this.setState({ error: error.message })
          console.error(error);
        }
    });
  }

  makeSignedTransaction(rawTransaction) {
    const privateKey   = '**************';
    const privateKeyX  = new Buffer(privateKey, 'hex');
    const transaction  = new EthTx(rawTransaction);
    transaction.sign(privateKeyX);

    return transaction;
  }

  makeRawTransaction(fromAccount, toAccount) {
    const { exchangeRate } = this.props;
    const amount = (1 / exchangeRate) * 5;

    return ({
      nonce: window.web3.utils.toHex(window.web3.eth.getTransactionCount(fromAccount)),
      to: toAccount,
      gasPrice: window.web3.utils.toHex(100000000000),
      gasLimit: window.web3.utils.toHex(100000),
      value: window.web3.utils.toHex(window.web3.utils.toWei(`${amount}`, 'ether')),
      data: ''
    });
  }
Просто примечание: Etherscan не всемогущ. Даже если что-то не отображается в Etherscan, это не значит, что это не удалось. Может Etherscan его просто не видит?

Ответы (2)

Эта линия...

nonce: window.web3.utils.toHex(window.web3.eth.getTransactionCount(fromAccount)),

... опирается на надежный подсчет транзакций от Infura. Проблема в том, что это не так надежно (или даже близко), как вам нужно. Как вы заметили, этот метод будет давать ненадежные и противоречивые результаты.

Некоторая предыстория поможет объяснить, что происходит и как вы можете решить эту проблему.

Ethereum использует nonceдля каждой учетной записи для защиты от повторных атак. Есть некоторые тонкие детали реализации.

  1. Транзакции с одного и того же аккаунта наверняка будут майнить по nonceпорядку.
  2. Если транзакция не может быть добыта (потеряна, газ слишком низок для включения в блок), то все транзакции с более высоким значением nonceостановятся. Застрявшую транзакцию обычно можно отменить (см. № 4).
  3. Ожидается, что программные клиенты (узлы, кошельки) будут отслеживать одноразовый номер и прикреплять его к транзакциям. Таким образом, узлы будут знать правильный порядок выполнения даже в том случае, если сплетни поступают не по порядку.
  4. Транзакция может быть отменена другой транзакцией с таким же nonceили более высоким gasзначением, если замена будет добыта первой. Распространенным методом отмены транзакции является повторение nonceи отправка 0ETH со счета на себя.
  5. В лучшем getTransactionCount()случае не учитываются транзакции, которые могут находиться в очереди ожидания.
  6. Ожидающая очередь является «неофициальной», если такое можно сказать о чем-либо в распределенном консенсусе. Узлы могут сообщать только об ожидающих транзакциях , о которых они знают . Вы можете и будете получать разные мнения от разных узлов по мере прохождения предлагаемых транзакций по сети. Майнинг блоков — это процесс, который разрушает неоднозначность.
  7. Infura обладает высокой степенью параллелизма, поэтому, даже если вы работаете с одной конечной точкой, ответ может исходить от любого внешнего узла на основе балансировки нагрузки.

Все это говорит о том, что существуют известные причины, по которым ваш процесс ненадежен.

Обычный подход состоит в том, чтобы сделать программный клиент менеджером nonce. Самый простой сценарий для описания — это случай, когда ожидающих транзакций нет, потому что учетная запись была закрыта или это новая учетная запись.

Начиная с 0, или счетчика транзакций, увеличивайте одноразовый номер по мере продвижения, не полагаясь на узлы. Вы должны доверять своему собственному счету больше, чем любому сигналу из сети.

Дополнительную информацию можно найти здесь: Шаблоны параллелизма для одноразового номера учетной записи .

Надеюсь, это поможет.

Как объяснил Роб Хитченс, архитектура Infura построена на основе балансировщика нагрузки с несколькими узлами за ним. Это означает, что два одинаковых запроса к Infura могут привести к разным ответам, поскольку каждый запрос может попасть на другой узел. Иногда вы будете получать ответы от устаревших блоков, узлов с другими транзакциями в своих мемпулах или узлов, которые не содержат новейший блок. Альтернативой этой проблеме является использование такого сервиса, как Алхимия .

Добро пожаловать на биржу стека Ethereum! Это было помечено как спам. Пожалуйста, проверьте meta.stackexchange.com/questions/57497/… , потому что в ваших ответах часто упоминается алхимия, и это следует раскрывать, если вы работаете на них.
Можете ли вы объяснить, почему Алхимия лучше?