用Go语言进行以太坊账户管理

转发请注明出处:https://blog.csdn.net/ahy231/article/details/114086566

网上关于 go 语言开发 DApp 的教程较少,因此我只能通过官方文档来系统学习 go 语言的 DApp 开发。这篇文章是我对 https://geth.ethereum.org/docs/dapp/native-accounts 这篇文档的翻译,如有不符之处,欢迎斧正。

译文

Go 语言账户管理

要为您的本机应用程序提供以太坊集成,您首先感兴趣的应该是账户管理。

尽管目前所有领先的以太坊实现都提供内置帐户管理,但将帐户保留在多个应用程序或多人共享的任何地方都是不明智的。同样,您不应该将您的登录凭据委托给您的ISP(毕竟是您进入internet的网关);您也不应该将您的凭据委托给以太坊节点(作为您进入以太坊网络的网关)。

在本机应用程序中处理用户帐户的正确方法是进行客户端帐户管理,所有内容都包含在您自己的应用程序中。通过这种方式,您可以确保在必要时对敏感数据具有细粒度(或粗粒度)的访问权限,而不依赖任何第三方应用程序的功能和/或漏洞。

为了支持这一点,go-ethereum 提供了一个简单而全面的 accounts 包,它为您提供了所有工具,通过加密的密钥库和受密码保护的帐户来进行适当的安全帐户管理。您可以利用 go 以太坊加密实现的所有安全性,同时在自己的应用程序中运行所有内容。

加密钥匙仓库

尽管在本地处理应用程序的帐户确实提供了某些安全保障,但以太坊帐户的访问密钥决不能以明文形式存在。因此,我们提供了一个加密的密钥库,它为您提供了适当的安全保证,而不需要您彻底了解相关的加密协议。

使用加密密钥库时需要知道的重要一点是,其中使用的加密协议可以在标准模式或轻型模式下运行。前者以增加计算负担和资源消耗为代价提供了更高级别的安全性:

  • 标准模式需要256MB内存和一个现代CPU通过1秒钟的处理来访问一个密钥
  • 轻型模式需要4MB的内存和一个现代CPU通过100毫秒的处理来访问一个密匙

因此,标准模式更适合于本机应用程序,但是您应该注意权衡,以防您针对的是资源受限的环境。

对于那些对加密或实现细节感兴趣的人,密钥存储使用 secp256k1 椭圆曲线,如高效加密标准中所定义,由 libsecp256k 库实现,并由 github.com/ethereum/go-ethereum/accounts 。帐户以 Web3 秘密存储格式存储在磁盘上。

Go 的密钥库

加密的密钥库被 github.com/ethereum/go-ethereum/accounts 包中的 accounts.Manager 结构体实现,其中还包含上述标准或轻型安全模式的配置常量。因此,要从 Go 执行客户端帐户管理,您只需将 accounts 包导入到代码中:

import "github.com/ethereum/go-ethereum/accounts"
import "github.com/ethereum/go-ethereum/accounts/keystore"
import "github.com/ethereum/go-ethereum/common"

之后,您可以通过以下方式创建新的加密帐户管理器:

ks := keystore.NewKeyStore("/path/to/keystore", keystore.StandardScryptN, keystore.StandardScryptP)
am := accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: false}, ks)

keystore 文件夹的路径必须是本地用户可写但其他系统用户不可读的位置(显然出于安全原因),因此我们建议将其放置在用户的主目录中,或者对于后端应用程序更为锁定。

keystore.NewKeyStore 的最后两个参数是定义密钥库加密的资源密集程度的加密参数。你可以选择 keystore.StandardScryptN, keystore.StandardScryptP, keystore.LightScryptN, keystore.LightScryptP 或者指定您自己的数字(请确保您了解这方面的基本密码)。我们建议使用标准版本。

账户生命周期

在为您的以太坊帐户创建了加密密钥库之后,您可以使用此帐户管理器满足本机应用程序的整个帐户生命周期要求。这包括创建新帐户和删除现有帐户的基本功能;以及更新访问凭证、导出现有帐户和在其他设备上导入这些帐户的更高级功能。

尽管密钥库定义了用于存储帐户的加密强度,但没有全局主密码可以授予对所有帐户的访问权限。相反,每个帐户都是单独维护的,并以其加密格式单独存储在磁盘上,从而确保凭证的分离更加干净和严格。

但是,这意味着任何需要访问帐户的操作都需要以密码短语的形式为该特定帐户提供必要的身份验证凭据:

  • 创建新帐户时,调用者必须提供密码短语以加密帐户。任何后续访问都需要此密码短语,缺少此密码短语将使新创建的帐户永久丢失。
  • 删除现有帐户时,调用者必须提供密码短语以验证帐户的所有权。这在密码上不是必要的,而是一种防止账户意外丢失的保护措施。
  • 更新现有帐户时,调用者必须同时提供当前密码和新密码。完成操作后,将无法再通过旧密码访问该帐户。
  • 导出现有帐户时,调用者必须提供用于解密帐户的当前密码短语,以及用于在将密钥文件返回给用户之前对其重新加密的导出密码短语。这是允许在计算机和应用程序之间移动帐户而不共享原始凭据所必需的。
  • 导入新帐户时,调用者必须提供要导入的密钥文件的加密密码短语,以及用于存储帐户的新密码短语。这是允许使用不同的凭据存储帐户所必需的,而不是用于移动它们。

请注意,密码短语丢失没有恢复机制。加密密钥库的加密属性(如果使用提供的参数)保证在任何有意义的时间内都不能强制使用帐户凭据。

Go 语言账户操作

一个以太坊帐户是被 github.com/ethereum/go-ethereum/accounts 包的 accounts.Account 结构体实现的。假设我们已经在代码中定义了一个 accounts.Manager 实例 am,我们可以通过少量的函数调用(省略错误处理)轻松地执行所描述的所有生命周期操作。

// Create a new account with the specified encryption passphrase.
newAcc, _ := ks.NewAccount("Creation password")
fmt.Println(newAcc)

// Export the newly created account with a different passphrase. The returned
// data from this method invocation is a JSON encoded, encrypted key-file.
jsonAcc, _ := ks.Export(newAcc, "Creation password", "Export password")

// Update the passphrase on the account created above inside the local keystore.
_ = ks.Update(newAcc, "Creation password", "Update password")

// Delete the account updated above from the local keystore.
_ = ks.Delete(newAcc, "Update password")

// Import back the account we've exported (and then deleted) above with yet
// again a fresh passphrase.
impAcc, _ := ks.Import(jsonAcc, "Export password", "Import password")

译者注:不知道为什么示例代码中没有 am 对象,可能是文档错把 ks 当成了 am

尽管 accounts.Account 实例可用于访问有关特定以太坊帐户的各种信息,它们不包含任何敏感数据(如密码短语或私钥),而仅作为客户端代码和密钥库的标识符。

数字签名

如上所述,account 对象并不持有相关以太坊帐户的敏感私钥,而仅仅是用以标识加密密钥的占位符。所有需要授权的操作(如交易签名)都由客户经理在授予其访问私钥的权限后执行。

有几种不同的方法可以授权客户经理执行签名操作,每种方法都有其优点和缺点。由于不同的方法具有完全不同的安全保证,因此必须明确每种方法的工作原理:

  • 单一授权:通过帐户管理器对事务进行签名的最简单方法是,每次需要签名时提供帐户的密码短语,这将临时解密私钥,执行签名操作并立即丢弃解密的密钥。缺点是每次都需要从用户那里查询密码短语,如果经常这样做,可能会变得烦人;或者应用程序需要将密码短语保存在内存中,如果做得不好,可能会产生安全后果;并且取决于密钥库的配置强度,不断解密密钥会导致不可忽略的资源需求。
  • 多重授权:通过帐户管理器对事务进行签名的一种更为复杂的方式是通过其密码短语对帐户进行一次解锁,并允许帐户管理器缓存解密的私钥,从而使所有后续签名请求在没有密码短语的情况下完成。可以手动(通过显式锁定帐户备份)或自动(通过在解锁期间提供超时)管理缓存私钥的生存期。这种机制对于用户可能需要签署许多事务或应用程序需要签署而不需要用户输入的情况非常有用。需要记住的关键方面是,任何有权访问帐户管理器的人都可以在特定帐户解锁时签署交易(例如,运行不受信任代码的应用程序)。

注意,在这里,创建事务超出了叙述范围,因此本节的其余部分将假设我们已经有了要签名的事务哈希,并且将只关注于创建授权它的加密签名。稍后将介绍如何创建实际事务并将授权签名注入其中。

Go 语言签名

假设我们已经在代码中定义 accounts.Manager 实例 am,我们可以创建一个新帐户,通过已经演示过的 NewAccount 方法来签署交易;为了避免现在进入交易创建的部分,我们可以指定一个随机 common.Hash 作为签名。

// Create a new account to sign transactions with
signer, _ := ks.NewAccount("Signer password")
txHash := common.HexToHash("0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")

在文件实例结束后,我们现在可以使用上面描述的授权机制对事务进行签名:

// Sign a transaction with a single authorization
signature, _ := ks.SignHashWithPassphrase(signer, "Signer password", txHash.Bytes())

// Sign a transaction with multiple manually cancelled authorizations
_ = ks.Unlock(signer, "Signer password")
signature, _ = ks.SignHash(signer, txHash.Bytes())
_ = ks.Lock(signer.Address)

// Sign a transaction with multiple automatically cancelled authorizations
_ = ks.TimedUnlock(signer, "Signer password", time.Second)
signature, _ = ks.SignHash(signer, txHash.Bytes())

你可能想知道为什么 SignWithPassphrase 函数需要 accounts.Account 作为签名者,而签名只需要 commom.Address 原因是 accounts.Account 对象还可能包含自定义密钥路径,允许 SignWithPassphrase 函数使用密钥库之外的帐户进行签名;但是,Sign 依赖于密钥库中已解锁的帐户,因此它无法指定自定义路径。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!为了与以太坊网络进行交互,并且不需要自己搭建节点,您可以使用Infura来连接以太坊网络。Infura是一个以太坊的基础设施提供商,它提供了一个简便的方式来访问以太坊网络。 要使用Infura,您需要首先注册一个账户并创建一个项目。然后,您将获得一个用于访问以太坊网络的API密钥。在您的代码中,您可以使用这个API密钥来连接到Infura的节点,并发送以太坊交易或查询区块链数据。 在Go语言中使用Infura连接以太坊网络的示例代码如下: ```go package main import ( "context" "fmt" "log" "math/big" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/rpc" ) func main() { client, err := rpc.Dial("https://mainnet.infura.io/v3/YOUR_INFURA_API_KEY") if err != nil { log.Fatal(err) } blockNumber := big.NewInt(10000000) header, err := client.HeaderByNumber(context.Background(), blockNumber) if err != nil { log.Fatal(err) } fmt.Println("Block number:", header.Number.String()) fmt.Println("Block time:", header.Time.String()) account := common.HexToAddress("YOUR_ETH_ADDRESS") balance, err := client.BalanceAt(context.Background(), account, nil) if err != nil { log.Fatal(err) } fmt.Println("Account balance:", balance.String()) } ``` 请注意,您需要将代码中的`YOUR_INFURA_API_KEY`替换为您在Infura上创建项目时获得的API密钥,并将`YOUR_ETH_ADDRESS`替换为您要查询余额的以太坊地址。 这只是一个简单的示例,您可以根据自己的需求进行更多操作。希望对您有帮助!如有任何疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值