如何监听以太坊内存池(附源码)

  • stirlingx
  • 更新于 2021-12-06 14:22
  • 阅读 9518

我们知道以太坊是目前最大的公链,它是一个去中心化的数据库,与传统数据库有一个很大的区别是,以太坊的数据并没有一个主动写入的过程。

以太坊内存池是什么?

我们知道以太坊是目前最大的公链,它是一个去中心化的数据库,与传统数据库有一个很大的区别是,以太坊的数据并没有一个主动写入的过程。所谓数据上链,其实是用户发起一笔交易,并携带相关信息。这些相关信息可以是调用智能合约某些函数和相关参数。然后这笔交易会广播到全网节点,其他节点收到之后会将他保存到自己的内存池中(mempool,其实翻译成交易池更合适)。如果节点开启了挖矿功能,我们把它叫做矿工节点。矿工节点会根据内存池中所有交易的gasPrice从大到小排序,打包进区块。当然每一笔交易只能被一个矿工打包,这个通过共识决定。一旦发现某一笔交易已经上链,就将其从内存池中删除。

科学家和抢跑机器人

玩过DeFi的小伙伴可能遇到过这样的情况,当你要买入一个商品的时候,有人在你之前抢先成交了。其实这些都是机器人发起的交易。我们把这类机器人叫抢跑机器人,开发这些机器人的人叫 “科学家”。抢跑机器的基本原理就是通过监听内存池中的交易,分析交易内容,然后发起一笔gasPrice更高的交易,强行插队。上面我们说过矿工是根据gasPrice的大小来决定哪笔交易先打包的。

如何监听内存池?

监听内存池需要一个全节点,可以自己部署,也可以使用第三方的节点。比如我们这里用 infura.io, 先去上面申请一个key,拿到接口地址。以太坊官方代码已经实现了go的客户端,直接使用即可。代码如下

package main

import (
    "context"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/ethclient"
    "github.com/ethereum/go-ethereum/ethclient/gethclient"
    "github.com/ethereum/go-ethereum/rpc"
    "log"
    "os"
    "os/signal"
    "syscall"
)

const (
    url = "https://mainnet.infura.io/v3/b4c05366e4c14e8a8304f0690aeae0e8"
    wss = "wss://mainnet.infura.io/ws/v3/b4c05366e4c14e8a8304f0690aeae0e8"
)

func watch() {
    backend, err := ethclient.Dial(url)
    if err != nil {
        log.Printf("failed to dial: %v", err)
        return
    }

    rpcCli, err := rpc.Dial(wss)
    if err != nil {
        log.Printf("failed to dial: %v", err)
        return
    }
    gcli := gethclient.New(rpcCli)

    txch := make(chan common.Hash, 100)
    _, err = gcli.SubscribePendingTransactions(context.Background(), txch)
    if err != nil {
        log.Printf("failed to SubscribePendingTransactions: %v", err)
        return
    }
    for {
        select {
        case txhash := <-txch:
            tx, _, err := backend.TransactionByHash(context.Background(), txhash)
            if err != nil {
                continue
            }
            data, _ := tx.MarshalJSON()
            log.Printf("tx: %v", string(data))
        }
    }
}

func main() {
    go watch()
    signalChan := make(chan os.Signal, 1)
    signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
    <-signalChan
}

点赞 7
收藏 14
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

10 条评论

请先 登录 后评论
stirlingx
stirlingx
0x7690...f836
江湖只有他的大名,没有他的介绍。