- 默认会开启snapshot加速结构,首次生成过程会非常慢,并且(若节点性能较差)可能导致节点无法实时跟上最新区块高度。建议临时升级服务器配置
- 节点可能遭遇 “BAD BLOCK” 问题,并导致区块停止同步。此问题由以太坊 Geth v1.10.8 引入,并已有以太坊用户报告,详见这里 https://github.com/ethereum/go-ethereum/issues/23531 及这里 https://github.com/ethereum/go-ethereum/issues/23546 。
此问题跟首次生成snapshot这个过程有关,并且只是偶尔出现。主要有两种方案处理此问题:
(1)遇到此问题时,重启节点,重启后预期就可以恢复正常并可继续同步区块;
(2)若对此问题敏感,可以在节点启动时加入参数 “ --snapshot=false" 以禁用快照加速结构,直到此问题完全解决,再尝试开启。 - 新版本配置项有变更,DiscoveryURLs 已删除,取而代之的是两个配置项:EthDiscoveryURLs 和 SnapDiscoveryURLs。若之前有提供 DiscoveryURLs,建议可直接删掉该项。
HECO节点升级v1.2.0注意事项
验证BTC地址是否有效
validateaddress "address"
Return information about the given bitcoin address.
Arguments:
1. address (string, required) The bitcoin address to validate
Result:
{
"isvalid" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.
"address" : "address", (string) The bitcoin address validated
"scriptPubKey" : "hex", (string) The hex-encoded scriptPubKey generated by the address
"isscript" : true|false, (boolean) If the key is a script
"iswitness" : true|false, (boolean) If the address is a witness address
"witness_version" : version (numeric, optional) The version number of the witness program
"witness_program" : "hex" (string, optional) The hex value of the witness program
}
Examples:
> bitcoin-cli validateaddress "1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc"
> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id":"curltest", "method": "validateaddress", "params": ["1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc"] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/
https://bitcoincore.org/en/doc/0.18.0/rpc/util/validateaddress/
以太坊合约中是否可以获取交易hash?
答案:不能
txhash 是keccak256(signedTransaction).
此 keccak256 函数可用作 Solidity 函数 http://solidity.readthedocs.io/en/v0.4.21/units-and-global-variables.html
所以你需要构造,signedTransaction因为这个值没有暴露于可靠性,参见。https://stackoverflow.com/questions/49803424/how-can-we-access-rlp-encoded-signed-raw-transaction-in-solidity
signedTransaction 需要的参数是
- nonce
- gas price
- gas limit
- to
- value in wei
- data
- ecdsaV
- ecdsaR
- ecdsaS
值 3 不是直接可用的,但您可以在代码执行的任何时候获取当前剩余的 gas,并从中计算出执行开始后可用的气体量。值 1、7、8 和 9(nonce 和签名值)不能使用solidity,也不能使用汇编代码(可以在solidity 源代码文件中内联编写)。所以很遗憾该问题无法解决。
https://ethereum.stackexchange.com/questions/45648/how-to-calculate-the-assigned-txhash-of-a-transaction
https://github.com/ethereum/EIPs/issues/901
以太坊索引器通过 ETH 地址获取交易列表
https://github.com/Adamant-im/ETH-transactions-storage/blob/master/ethsync.py
# Indexer for Ethereum to get transaction list by ETH address
# https://github.com/Adamant-im/ETH-transactions-storage
# 2021 ADAMANT Foundation (devs@adamant.im), Francesco Bonanno (mibofra@parrotsec.org),
# Guénolé de Cadoudal (guenoledc@yahoo.fr), Drew Wells (drew.wells00@gmail.com)
# 2020-2021 ADAMANT Foundation (devs@adamant.im): Aleksei Lebedev
# 2017-2020 ADAMANT TECH LABS LP (pr@adamant.im): Artem Brunov, Aleksei Lebedev
# v2.0
from os import environ
from web3 import Web3
from web3.middleware import geth_poa_middleware
import psycopg2
import time
import sys
import logging
#from systemd.journal import JournalHandler
# Get env variables or set to default
dbname = environ.get("DB_NAME")
startBlock = environ.get("START_BLOCK") or "1"
confirmationBlocks = environ.get("CONFIRMATIONS_BLOCK") or "0"
nodeUrl = environ.get("ETH_URL")
pollingPeriod = environ.get("PERIOD") or "20"
if dbname == None:
print('Add postgre database in env var DB_NAME')
exit(2)
if nodeUrl == None:
print('Add eth url in env var ETH_URL')
exit(2)
# Connect to Ethereum node
if nodeUrl.startswith("http"):
web3 = Web3(Web3.HTTPProvider(nodeUrl))
if nodeUrl.startswith("ws"):
web3 = Web3(Web3.WebsocketProvider(nodeUrl)) # "ws://publicnode:8546"
if web3 == None:
web3 = Web3(Web3.IPCProvider(nodeUrl)) # "/home/parity/.local/share/openethereum/jsonrpc.ipc"
web3.middleware_onion.inject(geth_poa_middleware, layer=0)
# Start logger
#logger = logging.getLogger("EthIndexerLog")
logger = logging.getLogger("eth-sync")
logger.setLevel(logging.INFO)
# File logger
#lfh = logging.FileHandler("/var/log/ethindexer.log")
lfh = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
lfh.setFormatter(formatter)
logger.addHandler(lfh)
# Systemd logger, if we want to user journalctl logs
# Install systemd-python and
# decomment "#from systemd.journal import JournalHandler" up
#ljc = JournalHandler()
#formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
#ljc.setFormatter(formatter)
#logger.addHandler(ljc)
try:
logger.info("Trying to connect to "+ dbname)
conn = psycopg2.connect(dbname)
conn.autocommit = True
logger.info("Connected to the database")
except:
logger.error("Unable to connect to database")
exit(1)
# Delete last block as it may be not imparted in full
cur = conn.cursor()
cur.execute('DELETE FROM public.ethtxs WHERE block = (SELECT Max(block) from public.ethtxs)')
cur.close()
conn.close()
# Wait for the node to be in sync before indexing
logger.info("Waiting Ethereum node to be in sync...")
while web3.eth.syncing != False:
# Change with the time, in second, do you want to wait
# before cheking again, default is 5 minutes
time.sleep(300)
logger.info("Ethereum node is synced!")
# Adds all transactions from Ethereum block
def insertion(blockid, tr):
time = web3.eth.getBlock(blockid)['timestamp']
for x in range(0, tr):
trans = web3.eth.getTransactionByBlock(blockid, x)
# Save also transaction status, should be null if pre byzantium blocks
status = bool(web3.eth.get_transaction_receipt(trans['hash']).status)
txhash = trans['hash'].hex()
value = trans['value']
inputinfo = trans['input']
# Check if transaction is a contract transfer
if (value == 0 and not inputinfo.startswith('0xa9059cbb')):
continue
fr = trans['from']
to = trans['to']
gasprice = trans['gasPrice']
gas = web3.eth.getTransactionReceipt(trans['hash'])['gasUsed']
contract_to = ''
contract_value = ''
# Check if transaction is a contract transfer
if inputinfo.startswith('0xa9059cbb'):
contract_to = inputinfo[10:-64]
contract_value = inputinfo[74:]
# Correct contract transfer transaction represents '0x' + 4 bytes 'a9059cbb' + 32 bytes (64 chars) for contract address and 32 bytes for its value
# Some buggy txs can break up Indexer, so we'll filter it
if len(contract_to) > 128:
logger.info('Skipping ' + str(txhash) + ' tx. Incorrect contract_to length: ' + str(len(contract_to)))
contract_to = ''
contract_value = ''
cur.execute(
'INSERT INTO public.ethtxs(time, txfrom, txto, value, gas, gasprice, block, txhash, contract_to, contract_value, status) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)',
(time, fr, to, value, gas, gasprice, blockid, txhash, contract_to, contract_value, status))
# Fetch all of new (not in index) Ethereum blocks and add transactions to index
while True:
try:
conn = psycopg2.connect(dbname)
conn.autocommit = True
except:
logger.error("Unable to connect to database")
cur = conn.cursor()
cur.execute('SELECT Max(block) from public.ethtxs')
maxblockindb = cur.fetchone()[0]
# On first start, we index transactions from a block number you indicate
if maxblockindb is None:
maxblockindb = int(startBlock)
endblock = int(web3.eth.blockNumber) - int(confirmationBlocks)
logger.info('Current best block in index: ' + str(maxblockindb) + '; in Ethereum chain: ' + str(endblock))
for block in range(maxblockindb + 1, endblock):
transactions = web3.eth.getBlockTransactionCount(block)
if transactions > 0:
insertion(block, transactions)
else:
logger.debug('Block ' + str(block) + ' does not contain transactions')
cur.close()
conn.close()
time.sleep(int(pollingPeriod))
一种高效的以太币和代币余额扫描方案
开源地址:https://github.com/MyCryptoHQ/eth-scan
智能合约:
// SPDX-License-Identifier: MIT
pragma solidity 0.8.3;
/**
* @title An Ether or token balance scanner
* @author Maarten Zuidhoorn
* @author Luit Hollander
*/
contract BalanceScanner {
struct Result {
bool success;
bytes data;
}
/**
* @notice Get the Ether balance for all addresses specified
* @param addresses The addresses to get the Ether balance for
* @return results The Ether balance for all addresses in the same order as specified
*/
function etherBalances(address[] calldata addresses) external view returns (Result[] memory results) {
results = new Result[](addresses.length);
for (uint256 i = 0; i < addresses.length; i++) {
results[i] = Result(true, abi.encode(addresses[i].balance));
}
}
/**
* @notice Get the ERC-20 token balance of `token` for all addresses specified
* @dev This does not check if the `token` address specified is actually an ERC-20 token
* @param addresses The addresses to get the token balance for
* @param token The address of the ERC-20 token contract
* @return results The token balance for all addresses in the same order as specified
*/
function tokenBalances(address[] calldata addresses, address token) external view returns (Result[] memory results) {
results = new Result[](addresses.length);
for (uint256 i = 0; i < addresses.length; i++) {
bytes memory data = abi.encodeWithSignature("balanceOf(address)", addresses[i]);
results[i] = staticCall(token, data, 20000);
}
}
/**
* @notice Get the ERC-20 token balance from multiple contracts for a single owner
* @param owner The address of the token owner
* @param contracts The addresses of the ERC-20 token contracts
* @return results The token balances in the same order as the addresses specified
*/
function tokensBalance(address owner, address[] calldata contracts) external view returns (Result[] memory results) {
results = new Result[](contracts.length);
bytes memory data = abi.encodeWithSignature("balanceOf(address)", owner);
for (uint256 i = 0; i < contracts.length; i++) {
results[i] = staticCall(contracts[i], data, 20000);
}
}
/**
* @notice Call multiple contracts with the provided arbitrary data
* @param contracts The contracts to call
* @param data The data to call the contracts with
* @return results The raw result of the contract calls
*/
function call(address[] calldata contracts, bytes[] calldata data) external view returns (Result[] memory results) {
return call(contracts, data, gasleft());
}
/**
* @notice Call multiple contracts with the provided arbitrary data
* @param contracts The contracts to call
* @param data The data to call the contracts with
* @param gas The amount of gas to call the contracts with
* @return results The raw result of the contract calls
*/
function call(
address[] calldata contracts,
bytes[] calldata data,
uint256 gas
) public view returns (Result[] memory results) {
require(contracts.length == data.length, "Length must be equal");
results = new Result[](contracts.length);
for (uint256 i = 0; i < contracts.length; i++) {
results[i] = staticCall(contracts[i], data[i], gas);
}
}
/**
* @notice Static call a contract with the provided data
* @param target The address of the contract to call
* @param data The data to call the contract with
* @param gas The amount of gas to forward to the call
* @return result The result of the contract call
*/
function staticCall(
address target,
bytes memory data,
uint256 gas
) private view returns (Result memory) {
uint256 size = codeSize(target);
if (size > 0) {
(bool success, bytes memory result) = target.staticcall{ gas: gas }(data);
if (success) {
return Result(success, result);
}
}
return Result(false, "");
}
/**
* @notice Get code size of address
* @param _address The address to get code size from
* @return size Unsigned 256-bits integer
*/
function codeSize(address _address) private view returns (uint256 size) {
// solhint-disable-next-line no-inline-assembly
assembly {
size := extcodesize(_address)
}
}
}
测试例子
https://github.com/MyCryptoHQ/eth-scan/blob/master/tests/BalanceScanner.test.ts