Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >公链启动过程

公链启动过程

作者头像
Al1ex
发布于 2021-07-21 09:18:07
发布于 2021-07-21 09:18:07
1K00
代码可运行
举报
文章被收录于专栏:网络安全攻防网络安全攻防
运行总次数:0
代码可运行
源码下载

以太坊官方提供了Go、C++、Python各个版本的实现,具体可以在官方GitHub(https://github.com/ethereum)中进行查找:

本系列源码分析文章主要基于当前最新版本v 1.10.2版本进行分析:

https://github.com/ethereum/go-ethereum/releases

目录结构

以太坊源码目录结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
├─accounts                  账号相关
├─build                     编译生成的程序
├─cmd                       geth程序主体
├─common                    工具函数库
├─consensus                 共识算法
├─console                   交互式命令
├─contracts                 合约相关            
├─core                      以太坊核心部分
├─crypto                    加密函数库   
├─docs                      说明文档
├─eth                       以太坊协议
├─ethclient                 以太坊RPC客户端
├─ethdb                     底层存储
├─ethstats                  统计报告
├─event                     事件处理
├─graphql                   
├─internal                  RPC调用
├─les                       轻量级子协议
├─light                     轻客户端部分功能
├─log                       日志模块
├─metrics                   服务监控相关
├─miner                     挖矿相关
├─mobile                    Geth的移动端API
├─node                      接口节点
├─p2p                       p2p网络协议 
├─params                    一些预设参数值
├─rlp                       RLP系列化格式
├─rpc                       RPC接口
├─signer                    签名相关
├─swarm                     分布式存储
├─tests                     以太坊JSON测试
└─trie                      Merkle Patricia实现
启动分析

以太坊启动的入口位于go-ethereum-1.10.2\cmd\geth\main.go文件中的geth函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// geth is the main entry point into the system if no special subcommand is ran.
// It creates a default node based on the command line arguments and runs it in
// blocking mode, waiting for it to be shut down.
func geth(ctx *cli.Context) error {
  if args := ctx.Args(); len(args) > 0 {
    return fmt.Errorf("invalid command: %q", args[0])
  }

  prepare(ctx)
  stack, backend := makeFullNode(ctx)
  defer stack.Close()

  startNode(ctx, stack, backend)
  stack.Wait()
  return nil
}

在这里主要做了一下几件事情:

1、准备操作内存缓存余量和度量设置

2、加载配置

3、启动节点

4、启动守护进程

准备工作

在这里我们一步一步来看,在prepare函数中首先会根据传入的参数来匹配一些已知的全局启动参数并打印其log,之后根据轻节点和全节点来设置分配给内部缓存的大小,然后进行度量设置:

文件位置:go-ethereum-1.10.2\cmd\geth\main.go L263

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// prepare manipulates memory cache allowance and setups metric system.
// This function should be called before launching devp2p stack.
func prepare(ctx *cli.Context) {
  // If we're running a known preset, log it for convenience.
  switch {
  case ctx.GlobalIsSet(utils.RopstenFlag.Name):
    log.Info("Starting Geth on Ropsten testnet...")

  case ctx.GlobalIsSet(utils.RinkebyFlag.Name):
    log.Info("Starting Geth on Rinkeby testnet...")

  case ctx.GlobalIsSet(utils.GoerliFlag.Name):
    log.Info("Starting Geth on Görli testnet...")

  case ctx.GlobalIsSet(utils.YoloV3Flag.Name):
    log.Info("Starting Geth on YOLOv3 testnet...")

  case ctx.GlobalIsSet(utils.DeveloperFlag.Name):
    log.Info("Starting Geth in ephemeral dev mode...")

  case !ctx.GlobalIsSet(utils.NetworkIdFlag.Name):
    log.Info("Starting Geth on Ethereum mainnet...")
  }
  // If we're a full node on mainnet without --cache specified, bump default cache allowance
  if ctx.GlobalString(utils.SyncModeFlag.Name) != "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) && !ctx.GlobalIsSet(utils.NetworkIdFlag.Name) {
    // Make sure we're not on any supported preconfigured testnet either
    if !ctx.GlobalIsSet(utils.RopstenFlag.Name) && !ctx.GlobalIsSet(utils.RinkebyFlag.Name) && !ctx.GlobalIsSet(utils.GoerliFlag.Name) && !ctx.GlobalIsSet(utils.DeveloperFlag.Name) {
      // Nope, we're really on mainnet. Bump that cache up!
      log.Info("Bumping default cache on mainnet", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 4096)
      ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(4096))
    }
  }
  // If we're running a light client on any network, drop the cache to some meaningfully low amount
  if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" && !ctx.GlobalIsSet(utils.CacheFlag.Name) {
    log.Info("Dropping default light client cache", "provided", ctx.GlobalInt(utils.CacheFlag.Name), "updated", 128)
    ctx.GlobalSet(utils.CacheFlag.Name, strconv.Itoa(128))
  }

  // Start metrics export if enabled
  utils.SetupMetrics(ctx)

  // Start system runtime metrics collection
  go metrics.CollectProcessMetrics(3 * time.Second)
}

最后调用CollectProcessMetrics定期收集有关运行过程的各种度量

文件位置:go-ethereum-1.10.2\metrics\metrics.go L57

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// CollectProcessMetrics periodically collects various metrics about the running
// process.
func CollectProcessMetrics(refresh time.Duration) {
  // Short circuit if the metrics system is disabled
  if !Enabled {
    return
  }
  refreshFreq := int64(refresh / time.Second)

  // Create the various data collectors
  cpuStats := make([]*CPUStats, 2)
  memstats := make([]*runtime.MemStats, 2)
  diskstats := make([]*DiskStats, 2)
  for i := 0; i < len(memstats); i++ {
    cpuStats[i] = new(CPUStats)
    memstats[i] = new(runtime.MemStats)
    diskstats[i] = new(DiskStats)
  }
  // Define the various metrics to collect
  var (
    cpuSysLoad    = GetOrRegisterGauge("system/cpu/sysload", DefaultRegistry)
    cpuSysWait    = GetOrRegisterGauge("system/cpu/syswait", DefaultRegistry)
    cpuProcLoad   = GetOrRegisterGauge("system/cpu/procload", DefaultRegistry)
    cpuThreads    = GetOrRegisterGauge("system/cpu/threads", DefaultRegistry)
    cpuGoroutines = GetOrRegisterGauge("system/cpu/goroutines", DefaultRegistry)

    memPauses = GetOrRegisterMeter("system/memory/pauses", DefaultRegistry)
    memAllocs = GetOrRegisterMeter("system/memory/allocs", DefaultRegistry)
    memFrees  = GetOrRegisterMeter("system/memory/frees", DefaultRegistry)
    memHeld   = GetOrRegisterGauge("system/memory/held", DefaultRegistry)
    memUsed   = GetOrRegisterGauge("system/memory/used", DefaultRegistry)

    diskReads             = GetOrRegisterMeter("system/disk/readcount", DefaultRegistry)
    diskReadBytes         = GetOrRegisterMeter("system/disk/readdata", DefaultRegistry)
    diskReadBytesCounter  = GetOrRegisterCounter("system/disk/readbytes", DefaultRegistry)
    diskWrites            = GetOrRegisterMeter("system/disk/writecount", DefaultRegistry)
    diskWriteBytes        = GetOrRegisterMeter("system/disk/writedata", DefaultRegistry)
    diskWriteBytesCounter = GetOrRegisterCounter("system/disk/writebytes", DefaultRegistry)
  )
  // Iterate loading the different stats and updating the meters
  for i := 1; ; i++ {
    location1 := i % 2
    location2 := (i - 1) % 2

    ReadCPUStats(cpuStats[location1])
    cpuSysLoad.Update((cpuStats[location1].GlobalTime - cpuStats[location2].GlobalTime) / refreshFreq)
    cpuSysWait.Update((cpuStats[location1].GlobalWait - cpuStats[location2].GlobalWait) / refreshFreq)
    cpuProcLoad.Update((cpuStats[location1].LocalTime - cpuStats[location2].LocalTime) / refreshFreq)
    cpuThreads.Update(int64(threadCreateProfile.Count()))
    cpuGoroutines.Update(int64(runtime.NumGoroutine()))

    runtime.ReadMemStats(memstats[location1])
    memPauses.Mark(int64(memstats[location1].PauseTotalNs - memstats[location2].PauseTotalNs))
    memAllocs.Mark(int64(memstats[location1].Mallocs - memstats[location2].Mallocs))
    memFrees.Mark(int64(memstats[location1].Frees - memstats[location2].Frees))
    memHeld.Update(int64(memstats[location1].HeapSys - memstats[location1].HeapReleased))
    memUsed.Update(int64(memstats[location1].Alloc))

    if ReadDiskStats(diskstats[location1]) == nil {
      diskReads.Mark(diskstats[location1].ReadCount - diskstats[location2].ReadCount)
      diskReadBytes.Mark(diskstats[location1].ReadBytes - diskstats[location2].ReadBytes)
      diskWrites.Mark(diskstats[location1].WriteCount - diskstats[location2].WriteCount)
      diskWriteBytes.Mark(diskstats[location1].WriteBytes - diskstats[location2].WriteBytes)

      diskReadBytesCounter.Inc(diskstats[location1].ReadBytes - diskstats[location2].ReadBytes)
      diskWriteBytesCounter.Inc(diskstats[location1].WriteBytes - diskstats[location2].WriteBytes)
    }
    time.Sleep(refresh)
  }
}
加载配置

之后调用makeFullNode加载配置文件:

文件位置:go-ethereum-1.10.2\cmd\geth\config.go L141

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// makeFullNode loads geth configuration and creates the Ethereum backend.
func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
  stack, cfg := makeConfigNode(ctx)
  if ctx.GlobalIsSet(utils.OverrideBerlinFlag.Name) {
    cfg.Eth.OverrideBerlin = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideBerlinFlag.Name))
  }
  backend := utils.RegisterEthService(stack, &cfg.Eth)

  // Configure GraphQL if requested
  if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
    utils.RegisterGraphQLService(stack, backend, cfg.Node)
  }
  // Add the Ethereum Stats daemon if requested.
  if cfg.Ethstats.URL != "" {
    utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
  }
  return stack, backend
}

在这里会调用makeConfigNode来加载配置文件:

文件位置:go-ethereum-1.10.2\cmd\geth\config.go L109

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// makeConfigNode loads geth configuration and creates a blank node instance.
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
  // Load defaults.
  cfg := gethConfig{
    Eth:     ethconfig.Defaults,
    Node:    defaultNodeConfig(),
    Metrics: metrics.DefaultConfig,
  }

  // Load config file.
  if file := ctx.GlobalString(configFileFlag.Name); file != "" {
    if err := loadConfig(file, &cfg); err != nil {
      utils.Fatalf("%v", err)
    }
  }

  // Apply flags.
  utils.SetNodeConfig(ctx, &cfg.Node)
  stack, err := node.New(&cfg.Node)
  if err != nil {
    utils.Fatalf("Failed to create the protocol stack: %v", err)
  }
  utils.SetEthConfig(ctx, stack, &cfg.Eth)
  if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
    cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
  }
  applyMetricConfig(ctx, &cfg)

  return stack, cfg
}

从上面的代码中我们可以看到这里会加载默认设置,主要有以下几个:

  • ethconfig.Defaults:以太坊主网上使用的默认设置(缓存配置、数据库配置、网络ID配置、交易查询限制、Gas设置等)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Defaults contains default settings for use on the Ethereum main net.
var Defaults = Config{
  SyncMode: downloader.FastSync,
  Ethash: ethash.Config{
    CacheDir:         "ethash",
    CachesInMem:      2,
    CachesOnDisk:     3,
    CachesLockMmap:   false,
    DatasetsInMem:    1,
    DatasetsOnDisk:   2,
    DatasetsLockMmap: false,
  },
  NetworkId:               1,
  TxLookupLimit:           2350000,
  LightPeers:              100,
  UltraLightFraction:      75,
  DatabaseCache:           512,
  TrieCleanCache:          154,
  TrieCleanCacheJournal:   "triecache",
  TrieCleanCacheRejournal: 60 * time.Minute,
  TrieDirtyCache:          256,
  TrieTimeout:             60 * time.Minute,
  SnapshotCache:           102,
  Miner: miner.Config{
    GasFloor: 8000000,
    GasCeil:  8000000,
    GasPrice: big.NewInt(params.GWei),
    Recommit: 3 * time.Second,
  },
  TxPool:      core.DefaultTxPoolConfig,
  RPCGasCap:   25000000,
  GPO:         FullNodeGPO,
  RPCTxFeeCap: 1, // 1 ether
}

defaultNodeConfig:默认节点配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// fileDir:go-ethereum-1.10.2\cmd\geth\config.go  L99
func defaultNodeConfig() node.Config {
  cfg := node.DefaultConfig
  cfg.Name = clientIdentifier
  cfg.Version = params.VersionWithCommit(gitCommit, gitDate)
  cfg.HTTPModules = append(cfg.HTTPModules, "eth")
  cfg.WSModules = append(cfg.WSModules, "eth")
  cfg.IPCPath = "geth.ipc"
  return cfg
}

// filedir:go-ethereum-1.10.2\node\defaults.go  L39
// DefaultConfig contains reasonable default settings.
var DefaultConfig = Config{
  DataDir:             DefaultDataDir(),
  HTTPPort:            DefaultHTTPPort,
  HTTPModules:         []string{"net", "web3"},
  HTTPVirtualHosts:    []string{"localhost"},
  HTTPTimeouts:        rpc.DefaultHTTPTimeouts,
  WSPort:              DefaultWSPort,
  WSModules:           []string{"net", "web3"},
  GraphQLVirtualHosts: []string{"localhost"},
  P2P: p2p.Config{
    ListenAddr: ":30303",
    MaxPeers:   50,
    NAT:        nat.Any(),
  },
}

metrics.DefaultConfig:度量的默认配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\metrics\config.go
// DefaultConfig is the default config for metrics used in go-ethereum.
var DefaultConfig = Config{
  Enabled:          false,
  EnabledExpensive: false,
  HTTP:             "127.0.0.1",
  Port:             6060,
  EnableInfluxDB:   false,
  InfluxDBEndpoint: "http://localhost:8086",
  InfluxDBDatabase: "geth",
  InfluxDBUsername: "test",
  InfluxDBPassword: "test",
  InfluxDBTags:     "host=localhost",
}

之后加载用户自定义的配置:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\cmd\geth\config.go   L118
// Load config file.
  if file := ctx.GlobalString(configFileFlag.Name); file != "" {
    if err := loadConfig(file, &cfg); err != nil {
      utils.Fatalf("%v", err)
    }
  }

之后调用SetNodeConfig应用对应的配置参数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\cmd\geth\config.go  L126
// Apply flags.
  utils.SetNodeConfig(ctx, &cfg.Node)
  stack, err := node.New(&cfg.Node)
  if err != nil {
    utils.Fatalf("Failed to create the protocol stack: %v", err)
  }
  utils.SetEthConfig(ctx, stack, &cfg.Eth)
  if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
    cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
  }
  applyMetricConfig(ctx, &cfg)

  return stack, cfg

然后调用node.New方法来创建一个新的P2P节点:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\node\node.go
// New creates a new P2P node, ready for protocol registration.
func New(conf *Config) (*Node, error) {
  // Copy config and resolve the datadir so future changes to the current
  // working directory don't affect the node.
  confCopy := *conf
  conf = &confCopy
  if conf.DataDir != "" {
    absdatadir, err := filepath.Abs(conf.DataDir)
    if err != nil {
      return nil, err
    }
    conf.DataDir = absdatadir
  }
  if conf.Logger == nil {
    conf.Logger = log.New()
  }

  // Ensure that the instance name doesn't cause weird conflicts with
  // other files in the data directory.
  if strings.ContainsAny(conf.Name, `/\`) {
    return nil, errors.New(`Config.Name must not contain '/' or '\'`)
  }
  if conf.Name == datadirDefaultKeyStore {
    return nil, errors.New(`Config.Name cannot be "` + datadirDefaultKeyStore + `"`)
  }
  if strings.HasSuffix(conf.Name, ".ipc") {
    return nil, errors.New(`Config.Name cannot end in ".ipc"`)
  }

  node := &Node{
    config:        conf,
    inprocHandler: rpc.NewServer(),
    eventmux:      new(event.TypeMux),
    log:           conf.Logger,
    stop:          make(chan struct{}),
    server:        &p2p.Server{Config: conf.P2P},
    databases:     make(map[*closeTrackingDB]struct{}),
  }

  // Register built-in APIs.
  node.rpcAPIs = append(node.rpcAPIs, node.apis()...)

  // Acquire the instance directory lock.
  if err := node.openDataDir(); err != nil {
    return nil, err
  }
  // Ensure that the AccountManager method works before the node has started. We rely on
  // this in cmd/geth.
  am, ephemeralKeystore, err := makeAccountManager(conf)
  if err != nil {
    return nil, err
  }
  node.accman = am
  node.ephemKeystore = ephemeralKeystore

  // Initialize the p2p server. This creates the node key and discovery databases.
  node.server.Config.PrivateKey = node.config.NodeKey()
  node.server.Config.Name = node.config.NodeName()
  node.server.Config.Logger = node.log
  if node.server.Config.StaticNodes == nil {
    node.server.Config.StaticNodes = node.config.StaticNodes()
  }
  if node.server.Config.TrustedNodes == nil {
    node.server.Config.TrustedNodes = node.config.TrustedNodes()
  }
  if node.server.Config.NodeDatabase == "" {
    node.server.Config.NodeDatabase = node.config.NodeDB()
  }

  // Check HTTP/WS prefixes are valid.
  if err := validatePrefix("HTTP", conf.HTTPPathPrefix); err != nil {
    return nil, err
  }
  if err := validatePrefix("WebSocket", conf.WSPathPrefix); err != nil {
    return nil, err
  }

  // Configure RPC servers.
  node.http = newHTTPServer(node.log, conf.HTTPTimeouts)
  node.ws = newHTTPServer(node.log, rpc.DefaultHTTPTimeouts)
  node.ipc = newIPCServer(node.log, conf.IPCEndpoint())

  return node, nil
}

然后调用SetEthConfig将eth相关命令行标志应用于配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\cmd\utils\flags.go
// SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
  // Avoid conflicting network flags
  CheckExclusive(ctx, MainnetFlag, DeveloperFlag, RopstenFlag, RinkebyFlag, GoerliFlag, YoloV3Flag)
  CheckExclusive(ctx, LightServeFlag, SyncModeFlag, "light")
  CheckExclusive(ctx, DeveloperFlag, ExternalSignerFlag) // Can't use both ephemeral unlocked and external signer
  if ctx.GlobalString(GCModeFlag.Name) == "archive" && ctx.GlobalUint64(TxLookupLimitFlag.Name) != 0 {
    ctx.GlobalSet(TxLookupLimitFlag.Name, "0")
    log.Warn("Disable transaction unindexing for archive node")
  }
  if ctx.GlobalIsSet(LightServeFlag.Name) && ctx.GlobalUint64(TxLookupLimitFlag.Name) != 0 {
    log.Warn("LES server cannot serve old transaction status and cannot connect below les/4 protocol version if transaction lookup index is limited")
  }
  var ks *keystore.KeyStore
  if keystores := stack.AccountManager().Backends(keystore.KeyStoreType); len(keystores) > 0 {
    ks = keystores[0].(*keystore.KeyStore)
  }
  setEtherbase(ctx, ks, cfg)
  setGPO(ctx, &cfg.GPO, ctx.GlobalString(SyncModeFlag.Name) == "light")
  setTxPool(ctx, &cfg.TxPool)
  setEthash(ctx, cfg)
  setMiner(ctx, &cfg.Miner)
  setWhitelist(ctx, cfg)
  setLes(ctx, cfg)

  // Cap the cache allowance and tune the garbage collector
  mem, err := gopsutil.VirtualMemory()
  if err == nil {
    if 32<<(^uintptr(0)>>63) == 32 && mem.Total > 2*1024*1024*1024 {
      log.Warn("Lowering memory allowance on 32bit arch", "available", mem.Total/1024/1024, "addressable", 2*1024)
      mem.Total = 2 * 1024 * 1024 * 1024
    }
    allowance := int(mem.Total / 1024 / 1024 / 3)
    if cache := ctx.GlobalInt(CacheFlag.Name); cache > allowance {
      log.Warn("Sanitizing cache to Go's GC limits", "provided", cache, "updated", allowance)
      ctx.GlobalSet(CacheFlag.Name, strconv.Itoa(allowance))
    }
  }
  // Ensure Go's GC ignores the database cache for trigger percentage
  cache := ctx.GlobalInt(CacheFlag.Name)
  gogc := math.Max(20, math.Min(100, 100/(float64(cache)/1024)))

  log.Debug("Sanitizing Go's GC trigger", "percent", int(gogc))
  godebug.SetGCPercent(int(gogc))

  if ctx.GlobalIsSet(SyncModeFlag.Name) {
    cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode)
  }
  if ctx.GlobalIsSet(NetworkIdFlag.Name) {
    cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
  }
  if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
    cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
  }
  cfg.DatabaseHandles = MakeDatabaseHandles()
  if ctx.GlobalIsSet(AncientFlag.Name) {
    cfg.DatabaseFreezer = ctx.GlobalString(AncientFlag.Name)
  }

  if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
    Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
  }
  if ctx.GlobalIsSet(GCModeFlag.Name) {
    cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive"
  }
  if ctx.GlobalIsSet(CacheNoPrefetchFlag.Name) {
    cfg.NoPrefetch = ctx.GlobalBool(CacheNoPrefetchFlag.Name)
  }
  // Read the value from the flag no matter if it's set or not.
  cfg.Preimages = ctx.GlobalBool(CachePreimagesFlag.Name)
  if cfg.NoPruning && !cfg.Preimages {
    cfg.Preimages = true
    log.Info("Enabling recording of key preimages since archive mode is used")
  }
  if ctx.GlobalIsSet(TxLookupLimitFlag.Name) {
    cfg.TxLookupLimit = ctx.GlobalUint64(TxLookupLimitFlag.Name)
  }
  if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheTrieFlag.Name) {
    cfg.TrieCleanCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheTrieFlag.Name) / 100
  }
  if ctx.GlobalIsSet(CacheTrieJournalFlag.Name) {
    cfg.TrieCleanCacheJournal = ctx.GlobalString(CacheTrieJournalFlag.Name)
  }
  if ctx.GlobalIsSet(CacheTrieRejournalFlag.Name) {
    cfg.TrieCleanCacheRejournal = ctx.GlobalDuration(CacheTrieRejournalFlag.Name)
  }
  if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
    cfg.TrieDirtyCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
  }
  if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheSnapshotFlag.Name) {
    cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100
  }
  if !ctx.GlobalBool(SnapshotFlag.Name) {
    // If snap-sync is requested, this flag is also required
    if cfg.SyncMode == downloader.SnapSync {
      log.Info("Snap sync requested, enabling --snapshot")
    } else {
      cfg.TrieCleanCache += cfg.SnapshotCache
      cfg.SnapshotCache = 0 // Disabled
    }
  }
  if ctx.GlobalIsSet(DocRootFlag.Name) {
    cfg.DocRoot = ctx.GlobalString(DocRootFlag.Name)
  }
  if ctx.GlobalIsSet(VMEnableDebugFlag.Name) {
    // TODO(fjl): force-enable this in --dev mode
    cfg.EnablePreimageRecording = ctx.GlobalBool(VMEnableDebugFlag.Name)
  }

  if ctx.GlobalIsSet(EWASMInterpreterFlag.Name) {
    cfg.EWASMInterpreter = ctx.GlobalString(EWASMInterpreterFlag.Name)
  }

  if ctx.GlobalIsSet(EVMInterpreterFlag.Name) {
    cfg.EVMInterpreter = ctx.GlobalString(EVMInterpreterFlag.Name)
  }
  if ctx.GlobalIsSet(RPCGlobalGasCapFlag.Name) {
    cfg.RPCGasCap = ctx.GlobalUint64(RPCGlobalGasCapFlag.Name)
  }
  if cfg.RPCGasCap != 0 {
    log.Info("Set global gas cap", "cap", cfg.RPCGasCap)
  } else {
    log.Info("Global gas cap disabled")
  }
  if ctx.GlobalIsSet(RPCGlobalTxFeeCapFlag.Name) {
    cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name)
  }
  if ctx.GlobalIsSet(NoDiscoverFlag.Name) {
    cfg.EthDiscoveryURLs, cfg.SnapDiscoveryURLs = []string{}, []string{}
  } else if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) {
    urls := ctx.GlobalString(DNSDiscoveryFlag.Name)
    if urls == "" {
      cfg.EthDiscoveryURLs = []string{}
    } else {
      cfg.EthDiscoveryURLs = SplitAndTrim(urls)
    }
  }
  // Override any default configs for hard coded networks.
  switch {
  case ctx.GlobalBool(MainnetFlag.Name):
    if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
      cfg.NetworkId = 1
    }
    cfg.Genesis = core.DefaultGenesisBlock()
    SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
  case ctx.GlobalBool(RopstenFlag.Name):
    if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
      cfg.NetworkId = 3
    }
    cfg.Genesis = core.DefaultRopstenGenesisBlock()
    SetDNSDiscoveryDefaults(cfg, params.RopstenGenesisHash)
  case ctx.GlobalBool(RinkebyFlag.Name):
    if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
      cfg.NetworkId = 4
    }
    cfg.Genesis = core.DefaultRinkebyGenesisBlock()
    SetDNSDiscoveryDefaults(cfg, params.RinkebyGenesisHash)
  case ctx.GlobalBool(GoerliFlag.Name):
    if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
      cfg.NetworkId = 5
    }
    cfg.Genesis = core.DefaultGoerliGenesisBlock()
    SetDNSDiscoveryDefaults(cfg, params.GoerliGenesisHash)
  case ctx.GlobalBool(YoloV3Flag.Name):
    if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
      cfg.NetworkId = new(big.Int).SetBytes([]byte("yolov3x")).Uint64() // "yolov3x"
    }
    cfg.Genesis = core.DefaultYoloV3GenesisBlock()
  case ctx.GlobalBool(DeveloperFlag.Name):
    if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
      cfg.NetworkId = 1337
    }
    // Create new developer account or reuse existing one
    var (
      developer  accounts.Account
      passphrase string
      err        error
    )
    if list := MakePasswordList(ctx); len(list) > 0 {
      // Just take the first value. Although the function returns a possible multiple values and
      // some usages iterate through them as attempts, that doesn't make sense in this setting,
      // when we're definitely concerned with only one account.
      passphrase = list[0]
    }
    // setEtherbase has been called above, configuring the miner address from command line flags.
    if cfg.Miner.Etherbase != (common.Address{}) {
      developer = accounts.Account{Address: cfg.Miner.Etherbase}
    } else if accs := ks.Accounts(); len(accs) > 0 {
      developer = ks.Accounts()[0]
    } else {
      developer, err = ks.NewAccount(passphrase)
      if err != nil {
        Fatalf("Failed to create developer account: %v", err)
      }
    }
    if err := ks.Unlock(developer, passphrase); err != nil {
      Fatalf("Failed to unlock developer account: %v", err)
    }
    log.Info("Using developer account", "address", developer.Address)

    // Create a new developer genesis block or reuse existing one
    cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address)
    if ctx.GlobalIsSet(DataDirFlag.Name) {
      // Check if we have an already initialized chain and fall back to
      // that if so. Otherwise we need to generate a new genesis spec.
      chaindb := MakeChainDatabase(ctx, stack, true)
      if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
        cfg.Genesis = nil // fallback to db content
      }
      chaindb.Close()
    }
    if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) {
      cfg.Miner.GasPrice = big.NewInt(1)
    }
  default:
    if cfg.NetworkId == 1 {
      SetDNSDiscoveryDefaults(cfg, params.MainnetGenesisHash)
    }
  }
}

最后调用applyMetricConfig进行全局设置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\cmd\geth\config.go
func applyMetricConfig(ctx *cli.Context, cfg *gethConfig) {
  if ctx.GlobalIsSet(utils.MetricsEnabledFlag.Name) {
    cfg.Metrics.Enabled = ctx.GlobalBool(utils.MetricsEnabledFlag.Name)
  }
  if ctx.GlobalIsSet(utils.MetricsEnabledExpensiveFlag.Name) {
    cfg.Metrics.EnabledExpensive = ctx.GlobalBool(utils.MetricsEnabledExpensiveFlag.Name)
  }
  if ctx.GlobalIsSet(utils.MetricsHTTPFlag.Name) {
    cfg.Metrics.HTTP = ctx.GlobalString(utils.MetricsHTTPFlag.Name)
  }
  if ctx.GlobalIsSet(utils.MetricsPortFlag.Name) {
    cfg.Metrics.Port = ctx.GlobalInt(utils.MetricsPortFlag.Name)
  }
  if ctx.GlobalIsSet(utils.MetricsEnableInfluxDBFlag.Name) {
    cfg.Metrics.EnableInfluxDB = ctx.GlobalBool(utils.MetricsEnableInfluxDBFlag.Name)
  }
  if ctx.GlobalIsSet(utils.MetricsInfluxDBEndpointFlag.Name) {
    cfg.Metrics.InfluxDBEndpoint = ctx.GlobalString(utils.MetricsInfluxDBEndpointFlag.Name)
  }
  if ctx.GlobalIsSet(utils.MetricsInfluxDBDatabaseFlag.Name) {
    cfg.Metrics.InfluxDBDatabase = ctx.GlobalString(utils.MetricsInfluxDBDatabaseFlag.Name)
  }
  if ctx.GlobalIsSet(utils.MetricsInfluxDBUsernameFlag.Name) {
    cfg.Metrics.InfluxDBUsername = ctx.GlobalString(utils.MetricsInfluxDBUsernameFlag.Name)
  }
  if ctx.GlobalIsSet(utils.MetricsInfluxDBPasswordFlag.Name) {
    cfg.Metrics.InfluxDBPassword = ctx.GlobalString(utils.MetricsInfluxDBPasswordFlag.Name)
  }
  if ctx.GlobalIsSet(utils.MetricsInfluxDBTagsFlag.Name) {
    cfg.Metrics.InfluxDBTags = ctx.GlobalString(utils.MetricsInfluxDBTagsFlag.Name)
  }
启动节点

之后调用startNode启动节点:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir: go-ethereum-1.10.2\cmd\geth\main.go  L328
// startNode boots up the system node and all registered protocols, after which
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner.
func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) {
  debug.Memsize.Add("node", stack)

  // Start up the node itself
  utils.StartNode(ctx, stack)

  // Unlock any account specifically requested
  unlockAccounts(ctx, stack)

  // Register wallet event handlers to open and auto-derive wallets
  events := make(chan accounts.WalletEvent, 16)
  stack.AccountManager().Subscribe(events)

  // Create a client to interact with local geth node.
  rpcClient, err := stack.Attach()
  if err != nil {
    utils.Fatalf("Failed to attach to self: %v", err)
  }
  ethClient := ethclient.NewClient(rpcClient)

  go func() {
    // Open any wallets already attached
    for _, wallet := range stack.AccountManager().Wallets() {
      if err := wallet.Open(""); err != nil {
        log.Warn("Failed to open wallet", "url", wallet.URL(), "err", err)
      }
    }
    // Listen for wallet event till termination
    for event := range events {
      switch event.Kind {
      case accounts.WalletArrived:
        if err := event.Wallet.Open(""); err != nil {
          log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
        }
      case accounts.WalletOpened:
        status, _ := event.Wallet.Status()
        log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)

        var derivationPaths []accounts.DerivationPath
        if event.Wallet.URL().Scheme == "ledger" {
          derivationPaths = append(derivationPaths, accounts.LegacyLedgerBaseDerivationPath)
        }
        derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath)

        event.Wallet.SelfDerive(derivationPaths, ethClient)

      case accounts.WalletDropped:
        log.Info("Old wallet dropped", "url", event.Wallet.URL())
        event.Wallet.Close()
      }
    }
  }()

  // Spawn a standalone goroutine for status synchronization monitoring,
  // close the node when synchronization is complete if user required.
  if ctx.GlobalBool(utils.ExitWhenSyncedFlag.Name) {
    go func() {
      sub := stack.EventMux().Subscribe(downloader.DoneEvent{})
      defer sub.Unsubscribe()
      for {
        event := <-sub.Chan()
        if event == nil {
          continue
        }
        done, ok := event.Data.(downloader.DoneEvent)
        if !ok {
          continue
        }
        if timestamp := time.Unix(int64(done.Latest.Time), 0); time.Since(timestamp) < 10*time.Minute {
          log.Info("Synchronisation completed", "latestnum", done.Latest.Number, "latesthash", done.Latest.Hash(),
            "age", common.PrettyAge(timestamp))
          stack.Close()
        }
      }
    }()
  }

  // Start auxiliary services if enabled
  if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) {
    // Mining only makes sense if a full Ethereum node is running
    if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
      utils.Fatalf("Light clients do not support mining")
    }
    ethBackend, ok := backend.(*eth.EthAPIBackend)
    if !ok {
      utils.Fatalf("Ethereum service not running: %v", err)
    }
    // Set the gas price to the limits from the CLI and start mining
    gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
    ethBackend.TxPool().SetGasPrice(gasprice)
    // start mining
    threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name)
    if err := ethBackend.StartMining(threads); err != nil {
      utils.Fatalf("Failed to start mining: %v", err)
    }
  }
}

startNode的具体实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\cmd\utils\cmd.go
func StartNode(ctx *cli.Context, stack *node.Node) {
  if err := stack.Start(); err != nil {
    Fatalf("Error starting protocol stack: %v", err)
  }
  go func() {
    sigc := make(chan os.Signal, 1)
    signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
    defer signal.Stop(sigc)

    minFreeDiskSpace := ethconfig.Defaults.TrieDirtyCache
    if ctx.GlobalIsSet(MinFreeDiskSpaceFlag.Name) {
      minFreeDiskSpace = ctx.GlobalInt(MinFreeDiskSpaceFlag.Name)
    } else if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
      minFreeDiskSpace = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
    }
    if minFreeDiskSpace > 0 {
      go monitorFreeDiskSpace(sigc, stack.InstanceDir(), uint64(minFreeDiskSpace)*1024*1024)
    }

    <-sigc
    log.Info("Got interrupt, shutting down...")
    go stack.Close()
    for i := 10; i > 0; i-- {
      <-sigc
      if i > 1 {
        log.Warn("Already shutting down, interrupt more to panic.", "times", i-1)
      }
    }
    debug.Exit() // ensure trace and CPU profile data is flushed.
    debug.LoudPanic("boom")
  }()
}

在这里会调用node的start方法启动所有注册的生命周期、RPC服务和P2P网络:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\node\node.go
// Start starts all registered lifecycles, RPC services and p2p networking.
// Node can only be started once.
func (n *Node) Start() error {
  n.startStopLock.Lock()
  defer n.startStopLock.Unlock()

  n.lock.Lock()
  switch n.state {
  case runningState:
    n.lock.Unlock()
    return ErrNodeRunning
  case closedState:
    n.lock.Unlock()
    return ErrNodeStopped
  }
  n.state = runningState
  // open networking and RPC endpoints
  err := n.openEndpoints()
  lifecycles := make([]Lifecycle, len(n.lifecycles))
  copy(lifecycles, n.lifecycles)
  n.lock.Unlock()

  // Check if endpoint startup failed.
  if err != nil {
    n.doClose(nil)
    return err
  }
  // Start all registered lifecycles.
  var started []Lifecycle
  for _, lifecycle := range lifecycles {
    if err = lifecycle.Start(); err != nil {
      break
    }
    started = append(started, lifecycle)
  }
  // Check if any lifecycle failed to start.
  if err != nil {
    n.stopServices(started)
    n.doClose(nil)
  }
  return err
}

之后调用unlockAccounts来解锁账户:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\cmd\geth\main.go   L426
// unlockAccounts unlocks any account specifically requested.
func unlockAccounts(ctx *cli.Context, stack *node.Node) {
  var unlocks []string
  inputs := strings.Split(ctx.GlobalString(utils.UnlockedAccountFlag.Name), ",")
  for _, input := range inputs {
    if trimmed := strings.TrimSpace(input); trimmed != "" {
      unlocks = append(unlocks, trimmed)
    }
  }
  // Short circuit if there is no account to unlock.
  if len(unlocks) == 0 {
    return
  }
  // If insecure account unlocking is not allowed if node's APIs are exposed to external.
  // Print warning log to user and skip unlocking.
  if !stack.Config().InsecureUnlockAllowed && stack.Config().ExtRPCEnabled() {
    utils.Fatalf("Account unlock with HTTP access is forbidden!")
  }
  ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
  passwords := utils.MakePasswordList(ctx)
  for i, account := range unlocks {
    unlockAccount(ks, account, i, passwords)
  }
}

之后注册钱包事件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  // Register wallet event handlers to open and auto-derive wallets
  events := make(chan accounts.WalletEvent, 16)
  stack.AccountManager().Subscribe(events)

之后监听钱包事件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
     // Listen for wallet event till termination
    for event := range events {
      switch event.Kind {
      case accounts.WalletArrived:
        if err := event.Wallet.Open(""); err != nil {
          log.Warn("New wallet appeared, failed to open", "url", event.Wallet.URL(), "err", err)
        }
      case accounts.WalletOpened:
        status, _ := event.Wallet.Status()
        log.Info("New wallet appeared", "url", event.Wallet.URL(), "status", status)

        var derivationPaths []accounts.DerivationPath
        if event.Wallet.URL().Scheme == "ledger" {
          derivationPaths = append(derivationPaths, accounts.LegacyLedgerBaseDerivationPath)
        }
        derivationPaths = append(derivationPaths, accounts.DefaultBaseDerivationPath)

        event.Wallet.SelfDerive(derivationPaths, ethClient)

      case accounts.WalletDropped:
        log.Info("Old wallet dropped", "url", event.Wallet.URL())
        event.Wallet.Close()
      }
    }
  }

生成用于状态同步监视的独立goroutine

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  // Spawn a standalone goroutine for status synchronization monitoring,
  // close the node when synchronization is complete if user required.
  if ctx.GlobalBool(utils.ExitWhenSyncedFlag.Name) {
    go func() {
      sub := stack.EventMux().Subscribe(downloader.DoneEvent{})
      defer sub.Unsubscribe()
      for {
        event := <-sub.Chan()
        if event == nil {
          continue
        }
        done, ok := event.Data.(downloader.DoneEvent)
        if !ok {
          continue
        }
        if timestamp := time.Unix(int64(done.Latest.Time), 0); time.Since(timestamp) < 10*time.Minute {
          log.Info("Synchronisation completed", "latestnum", done.Latest.Number, "latesthash", done.Latest.Hash(),
            "age", common.PrettyAge(timestamp))
          stack.Close()
        }
      }
    }()
  }

之后启动辅助服务(例如:挖矿):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  // Start auxiliary services if enabled
  if ctx.GlobalBool(utils.MiningEnabledFlag.Name) || ctx.GlobalBool(utils.DeveloperFlag.Name) {
    // Mining only makes sense if a full Ethereum node is running
    if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
      utils.Fatalf("Light clients do not support mining")
    }
    ethBackend, ok := backend.(*eth.EthAPIBackend)
    if !ok {
      utils.Fatalf("Ethereum service not running: %v", err)
    }
    // Set the gas price to the limits from the CLI and start mining
    gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
    ethBackend.TxPool().SetGasPrice(gasprice)
    // start mining
    threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name)
    if err := ethBackend.StartMining(threads); err != nil {
      utils.Fatalf("Failed to start mining: %v", err)
    }
  }

挖矿函数的具体逻辑代码如下,这里首先会检查矿工是否运行、配置本地挖矿奖励地址、最后执行挖矿:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// filedir:go-ethereum-1.10.2\eth\backend.go
// StartMining starts the miner with the given number of CPU threads. If mining
// is already running, this method adjust the number of threads allowed to use
// and updates the minimum price required by the transaction pool.
func (s *Ethereum) StartMining(threads int) error {
  // Update the thread count within the consensus engine
  type threaded interface {
    SetThreads(threads int)
  }
  if th, ok := s.engine.(threaded); ok {
    log.Info("Updated mining threads", "threads", threads)
    if threads == 0 {
      threads = -1 // Disable the miner from within
    }
    th.SetThreads(threads)
  }
  // If the miner was not running, initialize it
  if !s.IsMining() {
    // Propagate the initial price point to the transaction pool
    s.lock.RLock()
    price := s.gasPrice
    s.lock.RUnlock()
    s.txPool.SetGasPrice(price)

    // Configure the local mining address
    eb, err := s.Etherbase()
    if err != nil {
      log.Error("Cannot start mining without etherbase", "err", err)
      return fmt.Errorf("etherbase missing: %v", err)
    }
    if clique, ok := s.engine.(*clique.Clique); ok {
      wallet, err := s.accountManager.Find(accounts.Account{Address: eb})
      if wallet == nil || err != nil {
        log.Error("Etherbase account unavailable locally", "err", err)
        return fmt.Errorf("signer missing: %v", err)
      }
      clique.Authorize(eb, wallet.SignData)
    }
    // If mining is started, we can disable the transaction rejection mechanism
    // introduced to speed sync times.
    atomic.StoreUint32(&s.handler.acceptTxs, 1)

    go s.miner.Start(eb)
  }
  return nil
}

之后等待节点关闭:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Wait blocks until the node is closed.
func (n *Node) Wait() {
  <-n.stop
}
流程图示

根据上面的启动流程分析,我们可以简单的绘制一个启动流程图(这里主要以一些关键操作为主,忽略了一些if...else..条件判断语句,较为简化)

启动公链

以太坊官方提供了编译打包好的二进制文件,我们可以直接下载对应的二进制文件来启动公链(你有可以使用docker来搭建):

https://geth.ethereum.org/downloads/

下面我们使用Geth来启动一个节点:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
./geth --ropsten --rpc --rpcaddr 192.168.174.212 --rpcport 8989 
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 七芒星实验室 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
多链支持,真没你想的那么简单
总算是空闲下来可以整理一下代码了,Web3项目接入多链钱包连接功能,主要涉及 Ethereum、Polygon、BSC 和 Solana。乍一听好像只是“多做几套兼容逻辑”的事,但真正落地后才发现,很多东西其实没想得那么简单。
阿珏
2025/07/06
540
以太坊交互工具
以太坊提供了Geth客户端用于管理API,我们可以在终端输入geth help查看其具体使用方法:
Al1ex
2021/07/21
1.9K0
以太坊交互工具
以太坊网络架构解析
区块链的火热程度一直以直线上升,其中以区块链 2.0 —— 以太坊为代表,不断的为传统行业带来革新,同时也推动区块链技术发展。
Seebug漏洞平台
2018/07/12
1.8K0
Derek解读Bytom源码-启动与停止
Gitee地址:https://gitee.com/BytomBlockchain/bytom
比原链Bytom
2018/08/30
5320
Derek解读Bytom源码-启动与停止
知道创宇区块链安全实验室|深入理解以太坊交易处理机制
区块链是一个以"去中心化"、"去信任化"方式集体维护的分布式账本,这里的"分布式"不仅体现在数据的分布式存储,也体现在数据的分布式记录,即由系统参与者共同维护,作为"账本"的区块链自然少不了记账,而交易自然而然的成为了重中之重。
Al1ex
2021/07/21
1.7K0
知道创宇区块链安全实验室|深入理解以太坊交易处理机制
理解以太坊: Go-Ethereum 源码剖析(0): Geth Start
Geth[2] 是基于 Go 语言开发以太坊的客户端,它实现了 Ethereum 协议(黄皮书)中所有需要的实现的功能模块,包括状态管理,挖矿,P2P 网络通信,密码学,数据库,EVM 解释器等。我们可以通过启动 Geth 来运行一个 Ethereum 的节点。Go-ethereum 是包含了 Geth 在内的一个代码库,它包含了 Geth,以及编译 Geth 所需要的其他代码。在本系列中,我们会深入 Go-ethereum 代码库,从 High-level 的 API 接口出发,沿着 Ethereum 主 Workflow,从而理解 Ethereum 具体实现的细节。
Tiny熊
2022/05/25
2.3K0
聊聊kingbus的binlog_server_handler.go
kingbus的binlog_server_handler提供了StartBinlogServer、StopBinlogServer、GetBinlogServerStatus,他们均委托给了server.go的对应方法
code4it
2020/06/18
3610
NFT+DeFi流动性挖矿系统开发策划细节
其简洁是得力于 Geth 使用了 gopkg.in/urfave/cli.v1 扩展包,该扩展包用于管理程序的启动,以及命令行解析,其中 app 是该扩展包的一个实例。
KFZ433
2022/06/17
3860
以太坊虚拟机(下篇)
区块链上的虚拟机(Virtual Machine)是指建立在去中心化的区块链上的代码运行环境,目前市面上比较主流的便是以太坊虚拟机(Ethereum Virtual Machine,EVM)和类以太坊虚拟机,它基于Account账户模型将智能合约代码以对外完全隔离的方式在内部运行,实现了图灵完备的智能合约体系,本篇文章将从源码角度对其工作原理进行简要分析~
Al1ex
2021/07/21
7260
以太坊RPC机制
RPC(remote process call),即远程过程调用,意思就是两台物理位置不同的服务器,其中一台服务器的应用想调用另一台服务器上某个应用的函数或者方法,由于不在同一个内存空间不能直接调用,因此需要通过网络来表达语义以及传入的参数,RPC是跨操作系统,跨编程语言的网络通信方式。
Al1ex
2021/07/21
3.8K0
以太坊RPC机制
以太坊多节点私有链部署
https://g2ex.github.io/2017/09/12/ethereum-guidance/
飞狗
2018/09/10
1.5K0
以太坊虚拟机(上篇)
区块链上的虚拟机(Virtual Machine)是指建立在去中心化的区块链上的代码运行环境,目前市面上比较主流的便是以太坊虚拟机(Ethereum Virtual Machine,EVM)和类以太坊虚拟机,它基于Account账户模型将智能合约代码以对外完全隔离的方式在内部运行,实现了图灵完备的智能合约体系,本篇文章将从源码角度对其工作原理进行简要分析~
Al1ex
2021/07/21
1.6K0
以太坊虚拟机(上篇)
交易所对接以太坊钱包服务设计与实现
交易所钱包服务是加密货币交易所系统中的重要组成部分,它负责与各种不同的区块链的交互,实现用户地址生成、充值与提现等功能。本文以对接以太坊区块链的钱包服务为例,介绍交易所系统平台中钱包管理服务的设计与实现。
用户1408045
2019/08/09
2.9K0
聊聊kingbus的starRaft
starRaft方法先通过rafthttp.NewRoundTripper创建http.RoundTripper,之后通过storage.NewDiskStorage创建DiskStorage,之后根据logExist及cfg.NewCluster做不同处理;若二者都为false则更新membership.RaftCluster的id为存在的cluster的id,然后执行startEtcdRaftNode;若cfg.NewCluster为true则使用cl.MemberIDs()来执行startEtcdRaftNode;若logExist为true则执行restartEtcdNode;最后创建rafthttp.Transport,执行tr.Start()、tr.AddRemote、tr.AddPeer
code4it
2020/06/13
4670
聊聊kingbus的starRaft
区块链开发(三)以太坊客户端命令行选项汇总
本篇博客的内容可通过以下命令在客户端中进行查看: geth --help 也可以访问github上的wiki文档查看,地址为: https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options 具体内容 NAME: geth - go-ethereum命令行接口 USAGE: geth [options] command [command options] [arguments...] VERSION: 1.4.11-s
程序新视界
2022/05/06
5020
go-kit 微服务 整合Promtheus解决监控告警问题
Prometheus基于中央化的规则计算、统一分析和告警的新模型, 完美地解决了传统监控模型的痛点。所以说其对传统监控系统的测试和告警模型进行了彻底的颠覆。
Johns
2021/04/08
1.4K0
go-kit 微服务 整合Promtheus解决监控告警问题
golang源码分析:mysql同步工具gravity(2)
在分析完gravity的原理和如何使用以后,我们开始分析下它的源码。gravity有5个入口,代表了5个工具。
golangLeetcode
2023/09/06
3950
golang源码分析:mysql同步工具gravity(2)
Bytom侧链Vapor源码浅析-节点出块过程
在这篇文章中,作者将从Vapor节点的创建开始,进而拓展讲解Vapor节点出块过程中所涉及的源码。
比原链Bytom
2020/08/07
6210
以太坊区块设计
区块链是由包含交易的区块按照时间先后顺序依次连接起来的数据结构,这种数据结构是一个形象的链表结构,所有数据有序地链接在同一条区块链上,每个区块通过一个hash指针指向前一个区块,hash指针其实是前一个区块头进行SHA256哈希计算得到的,通过这个哈希值,可以唯一的识别一个区块,然后将每个区块连接到其区块头中前一个区块哈希值代币的区块后面,从而构建出一条完整的区块链。
Al1ex
2023/05/26
2.2K0
以太坊区块设计
以太坊win平台和Linux 私链搭建,交易,添加多节点
先上一篇的以太坊的私有搭建,交易,节点连接,结合搭建的步骤对以太坊进行深入介绍。本文介绍的方式在win10 和ubuntu16.04 的方式测试过。 第一步:geth的安装,请直接移步:https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum 或者百度其他博客。为了方便操作,windows 平台在安装完成geth后 ,最好将geth.exe 所在的目录添加到Path环境变量中以便于开发。linux 的方式推荐使用ppa的方式安装。如果不是也推荐奖geth 添加到环境变量里面。 第二步:创建创世块 先不要想太多直接复制我的创世块用: 新建一个文件夹名为mynode,然后在mynode 文件夹里面创建一个文件
地球流浪猫
2018/08/02
6140
推荐阅读
相关推荐
多链支持,真没你想的那么简单
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档