![Список транзакций в блоке Список транзакций в блоке](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/83b/355/0c7/83b3550c7f7c89656805d117231c7565.png)
В прошлых статьях (первая и вторая) я рассказывал вам, как подключиться к локальному блокчейну. Мы использовали Ganache, это ethereum-блокчейн, состоящий всего из одной ноды. Все транзакции происходили внутри нашего локального компьютера. Ganache идеально подходит для тестирования и отладки, так как уже после старта мы получаем предустановленные аккаунты со средствами на балансе, а результаты выполнения транзакций получаем моментально. Кроме того, это изолированный блокчейн и никто другой не может вмешаться в процесс автономного тестирования.
Помимо локальных тестовых блокчейнов существуют ещё и тестовые ethereum-сети, которые практически ничем не отличается от основной сети Mainnet. Здесь уже мы имеем дело с настоящей распределённой средой со множеством нод и независимыми аккаунтами. Транзакции здесь проходят аналогичный с основной сетью путь: распределение по нодам, валидация, включение в блок, достижение консенсуса между нодами относительного этого блока, и наконец, финализация - то есть приобретение транзакцией статуса неизменяемой и неотменяемой в блокчейне. Для записи транзакции в блокчейн требуется некоторое время.
Есть несколько тестовых ethereum сетей, вот некоторые из них:
Sepolia
Goerli
Mumbai
В данной статье я подключусь к Sepolia testnet, но в дальнейшем вы может использовать и другие сети.
Что мы будем делать?
Создадим окружение, зайдём в консоль и проверим подключение
Создадим аккаунты отправителя и получателя
Пополним аккаунт отправителя через Fauсet
Отправим Ether с одного аккаунта на другой
Зайдём на 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]
}
Теперь убедимся, что аккаунт действительно создан. Для этого зайдём на главную страницу тестовой сети, и в строке введём адрес аккаунта:
![Главный экран тестовой сети со строкой поиска Главный экран тестовой сети со строкой поиска](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/1f6/5c9/dac/1f65c9dac95850e18758c36e0f8f44fd.png)
После этого мы попадем на страницу нашего аккаунта:
![Созданный нами аккаунт отправителя Созданный нами аккаунт отправителя](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/f1b/7bf/8f2/f1b7bf8f2d58b52b322efb0bd02b5387.png)
Аналогично создадим аккаунт получателя:
> 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 для пополнения балансов участников тестовой сети](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/7af/b3c/5cc/7afb3c5cc356213f2770e98a3ae1cc1f.png)
После появится заставка, закрываем её и готово.
![Вы удачно пополнили баланс аккаунта тестовыми ETH Вы удачно пополнили баланс аккаунта тестовыми ETH](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/8fd/ef5/685/8fdef56857799e8bbe89d0d4a65fded3.png)
Мы зачислили себе 0.5 Ether. На момент написания статьи на Sepolia Faucet стояло ограничение в 0.5 Ether в день. Несмотря на кажущееся маленькое значение, это довольно-таки крупная сумма, и её вполне хватит для проведения тестовых транзакций.
Проверим наш аккаунт:
![Аккаунт отправителя после пополнения Аккаунт отправителя после пополнения](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/ca9/d6b/63a/ca9d6b63a1ef6ef1651ea9d1516c14c1.png)
Замечательно! На балансе 0.5 Ether, а в списке транзакций видим перевод этих средств с Faucet на наш аккаунт.
Можно зайти и в саму транзакцию:
![Транзакция с переводом средств из Faucet на наш аккаунт Транзакция с переводом средств из Faucet на наш аккаунт](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/c9e/f4d/37e/c9ef4d37e45b3fdf9ceb532de94d9a6b.png)
Здесь мы видим, что средства перевелись с аккаунта-донора на наш аккаунт. Если мы зайдём на аккаунт-донор, то увидим множество транзакций по 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 получателю](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/876/e40/f5e/876e40f5ecc8be587c0a109e43785585.png)
![Получатель приобрёл 0.01 ETH Получатель приобрёл 0.01 ETH](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/383/f2f/3d2/383f2f3d21e619e8d00f3338bd9c16a4.png)
Отлично. Мы видим, что у наших участников изменились балансы, а в разделе транзакции появилось по одной новой транзакции, притом последняя транзакция имеет один и тот же хэш, так как она одна на двоих.
Мы можем зайти и в саму транзакцию, кликнув на её хэш в списке:
![Транзакция между нашими участниками Транзакция между нашими участниками](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/9dc/099/ede/9dc099ede38117fb53f11173ae547f29.png)
Из транзакции мы можем провалиться в блок, в который попала эта транзакция:
![Блок в который попала наша транзакция Блок в который попала наша транзакция](https://webcf.waybackmachine.org/web/20230616141222/https://habrastorage.org/getpro/habr/upload_files/b14/8de/0db/b148de0dbff676db7799a4d502b5fff2.png)
Ту же информацию мы можем получить и из консоли. Вот например информация о транзакции:
> 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-файл и выполнять уже более сложные операции. Надеюсь информация была полезной :)