Список транзакций в блоке
Список транзакций в блоке

В прошлых статьях (первая и вторая) я рассказывал вам, как подключиться к локальному блокчейну. Мы использовали Ganache, это ethereum-блокчейн, состоящий всего из одной ноды. Все транзакции происходили внутри нашего локального компьютера. Ganache идеально подходит для тестирования и отладки, так как уже после старта мы получаем предустановленные аккаунты со средствами на балансе, а результаты выполнения транзакций получаем моментально. Кроме того, это изолированный блокчейн и никто другой не может вмешаться в процесс автономного тестирования.

Помимо локальных тестовых блокчейнов существуют ещё и тестовые ethereum-сети, которые практически ничем не отличается от основной сети Mainnet. Здесь уже мы имеем дело с настоящей распределённой средой со множеством нод и независимыми аккаунтами. Транзакции здесь проходят аналогичный с основной сетью путь: распределение по нодам, валидация, включение в блок, достижение консенсуса между нодами относительного этого блока, и наконец, финализация - то есть приобретение транзакцией статуса неизменяемой и неотменяемой в блокчейне. Для записи транзакции в блокчейн требуется некоторое время.

Есть несколько тестовых ethereum сетей, вот некоторые из них:

  • Sepolia

  • Goerli

  • Mumbai

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

Что мы будем делать?

  1. Создадим окружение, зайдём в консоль и проверим подключение

  2. Создадим аккаунты отправителя и получателя

  3. Пополним аккаунт отправителя через Fauсet

  4. Отправим Ether с одного аккаунта на другой

  5. Зайдём на Etherscan и убедимся, что всё прошло корректно

Итак, приступим!

Шаг 1. Создаём окружение, заходим в консоль, проверяем подключение

Создадим рабочий каталог и установим в него web3.js

$ mkdir testnet-demo
$ cd testnet-demo
$ npm install web3

Зайдём в папку проекта и запустим node.js

$ node

Теперь установим RPC подключение к Sepolia testnet. Адрес подключения я взял отсюда.

> const Web3 = require("web3");
> const web3 = new Web3("https://rpc2.sepolia.org");

Проверим подключение:

> web3.currentProvider

Вывод:

HttpProvider {
  withCredentials: undefined,
  timeout: 0,
  headers: undefined,
  agent: undefined,
  connected: false,
  host: 'https://rpc2.sepolia.org',
  httpsAgent: Agent {
    _events: [Object: null prototype] {
      free: [Function (anonymous)],
      newListener: [Function: maybeEnableKeylog]
    },
    _eventsCount: 2,
    _maxListeners: undefined,
    defaultPort: 443,
    protocol: 'https:',
    options: [Object: null prototype] {
      keepAlive: true,
      noDelay: true,
      path: null
    },
    requests: [Object: null prototype] {},
    sockets: [Object: null prototype] {},
    freeSockets: [Object: null prototype] {},
    keepAliveMsecs: 1000,
    keepAlive: true,
    maxSockets: Infinity,
    maxFreeSockets: 256,
    scheduling: 'lifo',
    maxTotalSockets: Infinity,
    totalSocketCount: 0,
    maxCachedSessions: 100,
    _sessionCache: { map: {}, list: [] },
    [Symbol(kCapture)]: false
  }
}

Для интереса можем запросить текущую стоимость одной единицы Gas:

> await web3.eth.getGasPrice();

// Out: '1000000006'

Отлично, соединение установлено, теперь создадим аккаунты участников транзакции.

Шаг 2. Создаём аккаунты отправителя и получателя

Создадим аккаунт отправителя:

> web3.eth.accounts.create();

Вывод:

{
  address: '0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474',
  privateKey: '0x3a...e8',
  signTransaction: [Function: signTransaction],
  sign: [Function: sign],
  encrypt: [Function: encrypt]
}

Теперь убедимся, что аккаунт действительно создан. Для этого зайдём на главную страницу тестовой сети, и в строке введём адрес аккаунта:

Главный экран тестовой сети со строкой поиска
Главный экран тестовой сети со строкой поиска

После этого мы попадем на страницу нашего аккаунта:

Созданный нами аккаунт отправителя
Созданный нами аккаунт отправителя

Аналогично создадим аккаунт получателя:

> web3.eth.accounts.create();

Вывод:

{
  address: '0xdA6e32D495c0E70AeD5F9e1E0D7Dea8543fb37e0',
  privateKey: '0xab...80',
  signTransaction: [Function: signTransaction],
  sign: [Function: sign],
  encrypt: [Function: encrypt]
}

Отлично, мы создали наших участников и убедились, что они отображаются в тестовой сети. Теперь давайте пополним баланс отправителя.

Шаг 3. Пополняем аккаунт отправителя через Fauсet

Для тестовых сетей существует и тестовая криптовалюта, получить её можно на ресурсах, называемых Faucet, или по-русски кран. Для каждой тестовой сети есть свой Faucet. Так как мы находимся в сети Sepolia, то нам сюда.

Перед выполнением операции вам скорее всего потребуется создать свою учётную запись. После того как вы залогинились, берём наш адрес отправителя:

0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474

И копируем его в строку. Подтверждаем, что вы человек и нажимаем Send Me ETH

Sepolia Faucet для пополнения балансов участников тестовой сети
Sepolia Faucet для пополнения балансов участников тестовой сети

После появится заставка, закрываем её и готово.

Вы удачно пополнили баланс аккаунта тестовыми ETH
Вы удачно пополнили баланс аккаунта тестовыми ETH

Мы зачислили себе 0.5 Ether. На момент написания статьи на Sepolia Faucet стояло ограничение в 0.5 Ether в день. Несмотря на кажущееся маленькое значение, это довольно-таки крупная сумма, и её вполне хватит для проведения тестовых транзакций.

Проверим наш аккаунт:

Аккаунт отправителя после пополнения
Аккаунт отправителя после пополнения

Замечательно! На балансе 0.5 Ether, а в списке транзакций видим перевод этих средств с Faucet на наш аккаунт.

Можно зайти и в саму транзакцию:

Транзакция с переводом средств из Faucet на наш аккаунт
Транзакция с переводом средств из Faucet на наш аккаунт

Здесь мы видим, что средства перевелись с аккаунта-донора на наш аккаунт. Если мы зайдём на аккаунт-донор, то увидим множество транзакций по 0.5 Ether. Это другие участники сети получают тестовые Ether.

Итак, у нас есть два участника, средства на балансе, а значит всё готово для отправки транзакции.

Шаг 4. Отправляем Ether с одного аккаунта на другой

Перед отправкой транзакции добавляем приватный ключ отправителя в наш локальный кошелёк, чтобы метод sendTransaction() смог найти его и подписать транзакцию. Для этого выполним в консоли следующую команду:

> web3.eth.accounts.wallet.add('0x3a...e8');

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

Замечательно. Теперь у нас всё готово для вызова метода sendTransaction(), которой подпишет нашу транзакцию и передаст её в тестовую сеть Sepolia. В теле транзакции я добавил ещё поле gasLimit с минимально необходимым значением 21000. Если этого не сделать, то поле будет установлено в 0, а тестовая сеть отклонит транзакцию, так как нода-валидатор не сможет снять пошлину за обработку транзакции.

Отправляем транзакцию:

> await web3.eth.sendTransaction({from: '0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474', to: '0xdA6e32D495c0E70AeD5F9e1E0D7Dea8543fb37e0', value: web3.utils.toWei('0.01', 'ether'), gasLimit: 21000});

В ответ получаем квитанцию о проведённой транзакции:

{
  blockHash: '0x6e9469710983d3b7be0e136cbff4d2dfa92e2c10fe926f502f0a1f67ad1f7de2',
  blockNumber: 3601461,
  contractAddress: null,
  cumulativeGasUsed: 425343,
  effectiveGasPrice: 2500000009,
  from: '0xc2325b2b3a8425ccb7c048ffa8f64e1d035ae474',
  gasUsed: 21000,
  logs: [],
  logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
  status: true,
  to: '0xda6e32d495c0e70aed5f9e1e0d7dea8543fb37e0',
  transactionHash: '0x510ef9140eac78893c2f1f7aae4a4e3cf841a55bfd8e19fbe911fbd07ddbe2e4',
  transactionIndex: 9,
  type: '0x2'
}

В квитанции видим хэш транзакции и номер блока в который она попала.

Проверим из консоли балансы участников тарнзакции:

> await web3.eth.getBalance('0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474');

// Out: '489947499999811000'

> await web3.eth.getBalance('0xdA6e32D495c0E70AeD5F9e1E0D7Dea8543fb37e0');

// Out: '10000000000000000'

Транзакция прошла успешно, и мы подошли к нашему финальному шагу - окончательной проверке полученных результатов.

Шаг 5. Заходим на Etherscan и убеждаемся, что всё прошло корректно

Открываем в Etherscan адреса отправителя и получателя

Отправитель

Аккаунт отправителя после отправки 0.01 ETH получателю
Аккаунт отправителя после отправки 0.01 ETH получателю

Получатель:

Получатель приобрёл 0.01 ETH
Получатель приобрёл 0.01 ETH

Отлично. Мы видим, что у наших участников изменились балансы, а в разделе транзакции появилось по одной новой транзакции, притом последняя транзакция имеет один и тот же хэш, так как она одна на двоих.

Мы можем зайти и в саму транзакцию, кликнув на её хэш в списке:

Транзакция между нашими участниками
Транзакция между нашими участниками

Из транзакции мы можем провалиться в блок, в который попала эта транзакция:

Блок в который попала наша транзакция
Блок в который попала наша транзакция

Ту же информацию мы можем получить и из консоли. Вот например информация о транзакции:

> await web3.eth.getTransaction('0x510ef9140eac78893c2f1f7aae4a4e3cf841a55bfd8e19fbe911fbd07ddbe2e4');

Вывод:

{
  blockHash: '0x6e9469710983d3b7be0e136cbff4d2dfa92e2c10fe926f502f0a1f67ad1f7de2',
  blockNumber: 3601461,
  from: '0xc2325B2b3a8425CCB7C048FfA8f64e1D035Ae474',
  gas: 21000,
  gasPrice: '2500000009',
  maxFeePerGas: '2500000018',
  maxPriorityFeePerGas: '2500000000',
  hash: '0x510ef9140eac78893c2f1f7aae4a4e3cf841a55bfd8e19fbe911fbd07ddbe2e4',
  input: '0x',
  nonce: 0,
  to: '0xdA6e32D495c0E70AeD5F9e1E0D7Dea8543fb37e0',
  transactionIndex: 9,
  value: '10000000000000000',
  type: 2,
  accessList: [],
  chainId: '0xaa36a7',
  v: '0x1',
  r: '0x2272a39cbfa9e75dac16d6488926b60090bf19d7a019246cf6d429ebd220a47a',
  s: '0x7369a5b91a4775c19e7e76d387c1e46e8ec8f44aa3b6992043b369f411da9452'
}

А это информация о блоке:

> await web3.eth.getBlock('3601461');

Вывод:

{
  baseFeePerGas: 9,
  difficulty: '0',
  extraData: '0x476f65726c69205365706f6c69612d4265706f6c696120513966',
  gasLimit: 30000000,
  gasUsed: 4924680,
  hash: '0x6e9469710983d3b7be0e136cbff4d2dfa92e2c10fe926f502f0a1f67ad1f7de2',
  logsBloom: '0x0001010002000000010080202010200601010008020000020c80010020806010000a00022020808000000010100008002000400000000000020040002024000802020002000020000c000009000102010031009050840000280202a000000400040080060602120000009004200009080a08880008480a0020040010408000510000400000000000210010000000080140020041000040400000020004400400a2880400002040100400400840080000104000400000000040102040000020140800000200200002020004200020102000000028400000088000000020806000041011080010002000a000200200002004000450040600015204c00000105084',
  miner: '0xFf58d746A67C2E42bCC07d6B3F58406E8837E883',
  mixHash: '0x3cec292e094271cfa845168c92c864a0ce630b8eee86adf2b26edba4cfb6da13',
  nonce: '0x0000000000000000',
  number: 3601461,
  parentHash: '0x4fdae93676cf69ac57bf3d70005f3f8cb7544364e15ccacfe9a48dcec2b10c97',
  receiptsRoot: '0xf90116292dfdcdb96e8d46b829744674d91eb8bc08413738735c9622180a806b',
  sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
  size: 23359,
  stateRoot: '0x0b07d5b2d444bc2c9f960c9fc5b40754489826038a89665d9268137b6b16d95b',
  timestamp: 1685610816,
  totalDifficulty: '17000018015853232',
  transactions: [
    '0xf0d3fc86a598811b1bff6452426eae007d8294c1b525974c07417ad479a64da2',
    '0x472fe3a00739eb596802ddd1f66f8ada2ebe9ba930390f98864d38e2b5045773',
    '0xd7c6479a2a08d25963532818e64e4ea64fe07c7d2b4b3eb5a80be0bcdba7acd7',
    '0xe32d2cb4f4c668773bc23e7d03552143ca3f35b14a9b099b6679c522da36adee',
    '0x200c8ce51b05490a192ed3b53f675f836d59f2dc714d1ca971b669ca90ef621a',
    '0xc334be29073dd4e181d7d4c481bf2f8b3b1206683876191277e6bc392e0642ac',
    '0xefc42a3b155790f9b64ffa4c663be4048e3226eea756fb530bedbb3c0272dd7d',
    '0x7fb223e9e809299b2bbf2a4dffa841cff32185a11d9dbf8e7dfeae6c8cd9b9a6',
    '0x8d2af3c0a4db0160539e8dec1790a893d993a65c355749d1917d9a2aac09a3e0',
    '0x510ef9140eac78893c2f1f7aae4a4e3cf841a55bfd8e19fbe911fbd07ddbe2e4',
    '0xb6dfc9c9e9457cf81a8e0c990a8a1c16ef58b25f43d6d314c00ac58ea4a77ebe',
    '0x90ca55b8f73394593ea92343dd53bc2228abc732009540a629895a8cb38a07fd',
    '0x661c5ab4e3f854ce286f2fa0ee278596e03b1dea46854e18ca388b9c22c532c7',
    '0x25c649d23a5a5f9bf545c415c1cbfb256e3fac7fcb85f0913e1cd601e32abc0f',
    '0x28b0f7765b8c6897ddae5c32b92b34c216b7149d3b8a71725c300b3c752d71a6',
    '0x530160bed0a1f4ab410f1a20879b0663b99ec84fac34bcc5f22be6068070aecc',
    '0xa64aea5c59dd462131c941a85b0c7c2bbfabdf60b815b7535a8b5bef5bbeb74e',
    '0xb75f2cc6766ccb8bcb3aeab241e57cf4e330d8d9a50fb9bea463155552ce31b5',
    '0xe2af47f0d3b4ead18cef2693ecfbfeeec57004e57252be4c198165c3a4dcc411',
    '0x1f6d31ca35b033b4aae431a1b212b5d222844a35311b54f28a148394f6032be9',
    '0xe1bfe48f3f6f9cf1b2b491bb7a6193a308eb38d5095c229cb82a7e0ca96d7e1f',
    '0xf627b541b16e275de5f48aee01af49e6f9a69405bab08df1566845fb643c7807',
    '0xe52dc798e3ce48242b52046eee555654a84b8462161acb6e1f5223dfa9f5a135',
    '0x8eab69c265c28af2022d09d81e4587e1936d51ed0f8879f92fe469caeb79eec2',
    '0x698665b27b3b1b796d76e2d546cb3ba54fc7f716f63f1dee5524a7c57b2e2878',
    '0xe627ed697178499240fef79c49ffc17fa074c81323f35ce1fd67ae25dd71cfe2',
    '0xa846655a9e27a86c29da227f819124cd033078a51b0ac9155f38d1fb6a24e3a7',
    '0xd70ad44740445384004fc2f39d469fd809ffa2453f6e4c539b0d457499320655'
  ],
  transactionsRoot: '0x2df7c4fce57e1c56cf10d3c4b6c84c795c395821bb1c6e1f41810368f3a5b05f',
  uncles: [],
  withdrawals: [
    {
      index: '0x94fa81',
      validatorIndex: '0x3b5',
      address: '0xe276bc378a527a8792b353cdca5b5e53263dfb9e',
      amount: '0x244aa'
    },
    
    // ... Большой список ...
    
    {
      index: '0x94fa90',
      validatorIndex: '0x3c4',
      address: '0xe276bc378a527a8792b353cdca5b5e53263dfb9e',
      amount: '0x2f6aa'
    }
  ],
  withdrawalsRoot: '0x25c3a945376fba5742d93a83c818c0baccd6887164b93425a04fc926782cf937'
}

Среди множества полей блока обратим внимание на поле transactions - это транзакции других участников, которые были добавлены в этот блок наряду с нашей транзакцией. Есть ещё интересное поле miner - это адрес ноды-валидатора, которая была выбрана для сборки блока. Если вы посмотрите этот адрес, то увидите сколько блоков этот валидатор добавил в блокчейн.

На этом всё. Далее вы можете самостоятельно поэкспериментировать с тестовой сетью, отправив в неё и другие вызовы из библиотеки web3

Поздравляю! Сегодня мы познакомились с тестовыми сетями, и научились взаимодействовать с ними посредством RPC-вызовов с помощью библиотеки web3.js и терминала. Все эти команды в дальнейшем вы можете объединить в JavaScript-файл и выполнять уже более сложные операции. Надеюсь информация была полезной :)

Комментарии (0)