怎样判断以太坊地点类型?

打印 上一主题 下一主题

主题 1018|帖子 1018|积分 3054

怎样判断以太坊地点类型?

一、账户类型解释

2.1 以太坊外部账户(Externally Owned Account,EOA)

外部账户(EOA)是由私钥控制的账户,在以太坊网络中用来发送交易和执行其他操作。EOA 不是智能合约,它没有合约代码,只能用于发送交易(例如转账以太币)。其主要特点是:


  • 没有合约代码:外部账户没有摆设智能合约的代码。
  • 拥有私钥:外部账户由用户的私钥控制,只有拥有该私钥的人才能签订交易。
  • 交易类型:EOA 发起的交易仅限于转账以太币或调用合约。
外部账户的特征:


  • 账户的地点通常是 0x 开头的 40 个十六进制字符。
  • 它不会有与之相关联的合约代码。
2.2 以太坊合约地点(Contract Account)

合约地点是由智能合约创建的账户,它由合约的代码控制。智能合约是摆设在以太坊网络上的可编程代码,可以接受交易、存储数据、执行逻辑等。合约地点的主要特点是:


  • 拥有合约代码:合约地点包含了可执行的合约代码,它允许外部地点调用合约的函数。
  • 由合约代码控制:合约地点不能直接由私钥控制,它由智能合约的代码决定。
  • 交易类型:合约地点通过调用合约函数来执行操作,而不仅仅是进行转账。智能合约可以接收来自外部账户的交易并做出响应。
合约地点的特征:


  • 也以 0x 开头,而且长度为 40 个十六进制字符。
  • 它会与特定的智能合约代码相关联。
关于以太坊账户的详细解读,可以检察:以太坊账户详解
二、地点判断

2.1 判断方法


  • 检查地点的代码是否存在
在以太坊中,合约地点是有摆设在区块链上的智能合约代码的,而平常地点(外部账户)没有代码。
步骤:


  • 使用 eth_getCode RPC 调用来检查一个地点的代码。如果返回的是 0x,表现该地点是一个外部账户(平常地点)。如果返回的好坏空的代码,表现该地点是一个合约地点。
  1. curl https://eth.com/ \
  2.   -X POST \
  3.   -H "Content-Type: application/json" \
  4.   --data '{"method":"eth_getCode","params":["0xdac17f958d2ee523a2206206994597c13d831ec7","latest"],"id":1,"jsonrpc":"2.0"}'
复制代码
例如,若返回 "0x",则说明该地点是一个平常地点。如果返回一个非零的代码(例如:0x606060405260043610610196576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde031461019b5780630753c30c14610229578063095ea7b3146102625780630e136b191),表现这是一个合约地点。

  • 使用 Etherscan 等区块链浏览器
如果你有地点的相关信息,可以通过访问区块链浏览器(如 Etherscan)检察该地点是否被标志为合约地点。Etherscan 会显示“合约”标签,指示该地点是智能合约地点。

  • 通过交易历史


  • 平常地点通常只会执行简朴的交易(例如转账以太币)。
  • 合约地点会有摆设合约或调用合约函数的交易记录。如果通过交易记录可以看到该地点与智能合约的交互(如调用 transfer、mint 等合约函数),则可以确定该地点是合约地点。

  • 检查地点的运动
合约地点通常会有复杂的交易运动,如调用函数、创建交易等。而平常地点只是到场平常的转账交易,通常没有其他的复杂行为。可以通过检察该地点的交易历史来做一个大致的判断。
通过这些方法,你可以有效地判断一个以太坊地点是平常地点照旧合约地点。
2.2 代码示例

我们将使用第一种检查地点的代码是否存在的方法进行判断,而且使用golang的方法进行测试,以下是我们的代码示例
代码示例
  1. package main
  2. import (
  3.         "context"
  4.         "encoding/hex"
  5.         "fmt"
  6.         "github.com/ethereum/go-ethereum/common"
  7.         "github.com/ethereum/go-ethereum/ethclient"
  8.         "log"
  9. )
  10. func main() {
  11.         // 连接到以太坊节点
  12.         client, err := ethclient.Dial("https://rpc.ankr.com/eth")
  13.         if err != nil {
  14.                 log.Fatalf("Failed to connect to the Ethereum client: %v", err)
  15.         }
  16.         // 需要检查的以太坊地址
  17.         address := common.HexToAddress("0xdac17f958d2ee523a2206206994597c13d831ec7")
  18.         // 调用 eth_getCode 来检查地址是否是合约地址
  19.         code, err := client.CodeAt(context.Background(), address, nil) // 第二个参数为 nil,表示查询最新的状态
  20.         if err != nil {
  21.                 log.Fatalf("Failed to get code at address: %v", err)
  22.         }
  23.         // 将字节码转换为十六进制字符串,并以 '0x' 开头
  24.         codeHex := "0x" + hex.EncodeToString(code)
  25.         fmt.Println("code is:", codeHex)
  26.         // 如果返回的代码为空,说明是普通地址;如果有代码,说明是合约地址
  27.         if len(code) == 0 {
  28.                 fmt.Println("The address is a regular wallet address (EOA).")
  29.         } else {
  30.                 fmt.Println("The address is a smart contract address.")
  31.         }
  32. }
复制代码
2.3 拓展:数据库中查询

通常,我们可能需要从数据库中获取地点,然后并发判断地点类型,以下是我们的golang代码示例
  1. package main
  2. import (
  3.         "context"
  4.         "database/sql"
  5.         "fmt"
  6.         "log"
  7.         "sync"
  8.         "github.com/ethereum/go-ethereum/common"
  9.         "github.com/ethereum/go-ethereum/ethclient"
  10.         _ "github.com/go-sql-driver/mysql"
  11. )
  12. // 检查地址是否为合约地址的函数
  13. func checkIfContract(client *ethclient.Client, address string, wg *sync.WaitGroup, results chan<- string) {
  14.         defer wg.Done()
  15.         // 将地址转换为eth的地址格式
  16.         ethAddress := common.HexToAddress(address)
  17.         // 查询地址的代码
  18.         code, err := client.CodeAt(context.Background(), ethAddress, nil)
  19.         if err != nil {
  20.                 results <- fmt.Sprintf("Error checking address %s: %v", address, err)
  21.                 return
  22.         }
  23.         // 判断代码是否为空
  24.         if len(code) == 0 {
  25.                 results <- fmt.Sprintf("Address %s is a regular wallet address (EOA).", address)
  26.         } else {
  27.                 results <- fmt.Sprintf("Address %s is a smart contract address.", address)
  28.         }
  29. }
  30. // 从数据库中获取以太坊地址
  31. func getEthereumAddresses(db *sql.DB) ([]string, error) {
  32.         var addresses []string
  33.         // 执行查询(假设表名为 'eth_addresses',列名为 'address')
  34.         rows, err := db.Query("SELECT address FROM eth_addresses")
  35.         if err != nil {
  36.                 return nil, fmt.Errorf("failed to execute query: %w", err)
  37.         }
  38.         defer rows.Close()
  39.         // 遍历查询结果并将地址加入到切片中
  40.         for rows.Next() {
  41.                 var address string
  42.                 if err := rows.Scan(&address); err != nil {
  43.                         return nil, fmt.Errorf("failed to scan row: %w", err)
  44.                 }
  45.                 addresses = append(addresses, address)
  46.         }
  47.         // 检查是否有错误发生
  48.         if err := rows.Err(); err != nil {
  49.                 return nil, fmt.Errorf("error while iterating rows: %w", err)
  50.         }
  51.         return addresses, nil
  52. }
  53. func main() {
  54.         // 连接到MySQL数据库,使用连接池
  55.         dsn := "username:password@tcp(localhost:3306)/your_database_name"
  56.         db, err := sql.Open("mysql", dsn)
  57.         if err != nil {
  58.                 log.Fatalf("Failed to connect to the database: %v", err)
  59.         }
  60.         defer db.Close()
  61.         // 检查数据库连接是否可用
  62.         if err := db.Ping(); err != nil {
  63.                 log.Fatalf("Failed to ping the database: %v", err)
  64.         }
  65.         // 获取所有以太坊地址
  66.         addresses, err := getEthereumAddresses(db)
  67.         if err != nil {
  68.                 log.Fatalf("Failed to get Ethereum addresses from database: %v", err)
  69.         }
  70.         // 连接到以太坊节点(使用Infura或Geth节点)
  71.         client, err := ethclient.Dial("https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID")
  72.         if err != nil {
  73.                 log.Fatalf("Failed to connect to the Ethereum client: %v", err)
  74.         }
  75.         // 并发查询每个地址
  76.         var wg sync.WaitGroup
  77.         results := make(chan string, len(addresses)) // 设置一个缓冲 channel 来存储结果
  78.         // 启动多个 goroutine 并发查询
  79.         for _, address := range addresses {
  80.                 wg.Add(1)
  81.                 go checkIfContract(client, address, &wg, results)
  82.         }
  83.         // 等待所有查询完成
  84.         wg.Wait()
  85.         close(results)
  86.         // 打印所有结果
  87.         for result := range results {
  88.                 fmt.Println(result)
  89.         }
  90. }
复制代码
以上代码的优化点;
主要优化点:

  • 数据库连接池使用:通过 sql.Open 创建数据库连接并使用连接池(db.Ping() 用于确保数据库连接可用)。不需要每次查询时都打开新连接。
  • 错误处置惩罚改进:通过 fmt.Errorf 包装错误信息,使其更具可读性,并提供详细的上下文。
  • sync.WaitGroup 和 chan 并发控制:这些部分没有变革,但确保 goroutines 的执行是并发的且高效的。
  • 日志:改进了错误日志,提供更清晰的错误上下文。
  • CodeAt 查询上下文:确保查询时使用 context.Background(),而不是 nil,保持划一性。
  • db.Ping():用于检查数据库连接是否正常,防止在查询时出现连接问题。
性能思量:


  • 使用缓冲通道(results)来存储查询结果,避免了在多个 goroutine 中频繁地发生壅闭。
  • sync.WaitGroup 确保全部的并发任务在步伐退出之前都已完成。
这种优化方式使得代码更坚固,易于维护,并进步了处置惩罚并发任务时的性能。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

圆咕噜咕噜

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表