区块链应用的密钥管理

管理什么密钥?

在区块链应用的基础组件中通常有这样一种功能,需要持续不断的向区块链中发送交易,比如arbitrum的Sequencer需要持续不断的发送L2的区块,stark 需要发送单步证明/rBlock发布 的交易,chainlink需要定时发送datafeed交易。而这每一笔交易都需要L1上的账户做签名,如何安全的使用和管理这个密钥是值得关心的。

结论

就我所看到的一般有两种方式:

  • 通过配置文件配置私钥
  • 使用filekey的方式:
    • 注意file一般需要一个密码,密码是启动后在终端控制台输入

当然,密钥管理不只是简单的将密钥注入到程序里面,而是如何在程序里面安全的使用这些密钥,毕竟如果密钥发在一个可能被外部接口调用的接口中,可能会降低密钥的安全性。

密钥安全等级(依次递减,只考虑加密算法公开的情况):

  • 黑客无法得知任何明文&密文
  • 黑客可以得到密文
  • 黑客可以得到密文对应的明文
  • 黑客可以自行构造明文产生密文

所以在程序中也需要对密钥进行保护。

以太坊中每次使用完私钥会将私钥的地址还原成0地址,就是为了避免私钥在内存中泄漏。

私钥泄漏的原理大致是,geth程序在使用玩内存后会释放内存,而他释放内存并不会把内存值全部置0,而只是告诉操作系统,“这段内存我不用了,你可以分配给别的程序” 。而别的程序申请到这段内存之后,他是可以直接读取这段内存里的值,(经典案例就是 在c语言中如果你初始化一个变量,而不为赋值,那他的值不是0值,而是原先在这个值里面的内存的值)

arbitrum的处理方案

先从最底层的调用开始看

在单步证明的调用中可以看到,这笔交易的用户信息是保存在auth字段中的

func (m *ChallengeManager) IssueOneStepProof(ctx context.Context,oldState *ChallengeState,startSegment int,
) (*types.Transaction, error) {position := oldState.Segments[startSegment].Positionproof, err := m.executionChallengeBackend.GetProofAt(ctx, position)if err != nil {return nil, fmt.Errorf("error getting OSP from challenge %v backend at step %v: %w", m.challengeIndex, position, err)}return m.challengeCore.con.OneStepProveExecution(m.challengeCore.auth,   // 用户信息保存在这个字段m.challengeCore.challengeIndex,challengegen.ChallengeLibSegmentSelection{OldSegmentsStart:  oldState.Start,OldSegmentsLength: new(big.Int).Sub(oldState.End, oldState.Start),OldSegments:       oldState.RawSegments,ChallengePosition: big.NewInt(int64(startSegment)),},proof,)
}

具体如何使用可以继续点进去看,最终是auth中包含一个变量(函数类型的变量),由这个变量进行签名,(我们需要找到的这个函数的生命周期,也就是密钥的生命周期)。

那么继续往上看,看这个challengeManager的构造方法

func NewChallengeManager(ctx context.Context,l1client bind.ContractBackend,auth *bind.TransactOpts,fromAddr common.Address,challengeManagerAddr common.Address,challengeIndex uint64,val *StatelessBlockValidator,startL1Block uint64,confirmationBlocks int64,
) (*ChallengeManager, error) {...return &ChallengeManager{challengeCore: &challengeCore{con:                  con,challengeManagerAddr: challengeManagerAddr,challengeIndex:       challengeIndex,client:               l1client,auth:                 auth,   // 也就是上面的authactingAs:             fromAddr,startL1Block:         new(big.Int).SetUint64(startL1Block),confirmationBlocks:   confirmationBlocks,},blockChallengeBackend: backend,validator:             val,wasmModuleRoot:        challengeInfo.WasmModuleRoot,maxBatchesRead:        challengeInfo.MaxInboxMessages,}, nil
}

可以看到auth是上面传递过来的bind.ContractOpts

继续往上面看,auth来自与Builder这个结构,好在这个结构的构造函数只被调用过一次(我们及假设唯一的构造得到的auth就是我们要找的auth,中间没有发生更改)

func NewBuilder(wallet ValidatorWalletInterface) (*Builder, error) {randKey, err := crypto.GenerateKey()if err != nil {return nil, err}builderAuth := wallet.AuthIfEoa()var isAuthFake boolif builderAuth == nil {// Make a fake auth so we have txs to give to the smart contract walletbuilderAuth, err = bind.NewKeyedTransactorWithChainID(randKey, big.NewInt(9999999))if err != nil {return nil, err}isAuthFake = true}return &Builder{builderAuth: builderAuth,wallet:      wallet,L1Interface: wallet.L1Client(),isAuthFake:  isAuthFake,}, nil
}

builder的auth有两种途径,一种是 AuthIfEoa 也就是从eoa中解析私钥,一种是自己生成私钥

那么关键在与这里的wallet是什么(也就是现在从跟踪auth转移到跟踪wallet)

最终发现wallet在creatNoteImpl方法里面构造的

var wallet staker.ValidatorWalletInterface = validatorwallet.NewNoOp(l1client, deployInfo.Rollup)
if !strings.EqualFold(config.Staker.Strategy, "watchtower") {if config.Staker.UseSmartContractWallet || (txOptsValidator == nil && config.Staker.DataPoster.ExternalSigner.URL == "") {// 合约账户var existingWalletAddress *common.Addressif len(config.Staker.ContractWalletAddress) > 0 {if !common.IsHexAddress(config.Staker.ContractWalletAddress) {log.Error("invalid validator smart contract wallet", "addr", config.Staker.ContractWalletAddress)return nil, errors.New("invalid validator smart contract wallet address")}tmpAddress := common.HexToAddress(config.Staker.ContractWalletAddress)existingWalletAddress = &tmpAddress}wallet, err = validatorwallet.NewContract(dp, existingWalletAddress, deployInfo.ValidatorWalletCreator, deployInfo.Rollup, l1Reader, txOptsValidator, int64(deployInfo.DeployedAt), func(common.Address) {}, getExtraGas)if err != nil {return nil, err}} else {if len(config.Staker.ContractWalletAddress) > 0 {return nil, errors.New("validator contract wallet specified but flag to use a smart contract wallet was not specified")}wallet, err = validatorwallet.NewEOA(dp, deployInfo.Rollup, l1client, getExtraGas)if err != nil {return nil, err}}
}

继续跟踪我们得到wellet中的验证方法是由txOptsValidator提供的

向上继续找txOptsValidator

最重找到mainImpl

	if sequencerNeedsKey || nodeConfig.Node.BatchPoster.ParentChainWallet.OnlyCreateKey {l1TransactionOptsBatchPoster, dataSigner, err = util.OpenWallet("l1-batch-poster", &nodeConfig.Node.BatchPoster.ParentChainWallet, new(big.Int).SetUint64(nodeConfig.ParentChain.ID))if err != nil {flag.Usage()log.Crit("error opening Batch poster parent chain wallet", "path", nodeConfig.Node.BatchPoster.ParentChainWallet.Pathname, "account", nodeConfig.Node.BatchPoster.ParentChainWallet.Account, "err", err)}if nodeConfig.Node.BatchPoster.ParentChainWallet.OnlyCreateKey {return 0}}if validatorNeedsKey || nodeConfig.Node.Staker.ParentChainWallet.OnlyCreateKey {l1TransactionOptsValidator, _, err = util.OpenWallet("l1-validator", &nodeConfig.Node.Staker.ParentChainWallet, new(big.Int).SetUint64(nodeConfig.ParentChain.ID))if err != nil {flag.Usage()log.Crit("error opening Validator parent chain wallet", "path", nodeConfig.Node.Staker.ParentChainWallet.Pathname, "account", nodeConfig.Node.Staker.ParentChainWallet.Account, "err", err)}if nodeConfig.Node.Staker.ParentChainWallet.OnlyCreateKey {return 0}}

我们得到l1TransactionOptsValidator是使用nodeConfig.Node.Staker.ParentChainWallet 这个配置项得到的.

最终的数据结构张这个样子

type WalletConfig struct {Pathname      string `koanf:"pathname"`Password      string `koanf:"password"`PrivateKey    string `koanf:"private-key"`Account       string `koanf:"account"`OnlyCreateKey bool   `koanf:"only-create-key"`
}

继续点到OpenWallet可以看到他是如何处理这些配置项的

在有私钥的情况下最终会走到这个方法

func NewKeyedTransactorWithChainID(key *ecdsa.PrivateKey, chainID *big.Int) (*TransactOpts, error) {keyAddr := crypto.PubkeyToAddress(key.PublicKey)if chainID == nil {return nil, ErrNoChainID}signer := types.LatestSignerForChainID(chainID)return &TransactOpts{From: keyAddr,Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {  // signer就是我们一直再找的在发送交易时使用到的签名方法if address != keyAddr {return nil, ErrNotAuthorized}signature, err := crypto.Sign(signer.Hash(tx).Bytes(), key)if err != nil {return nil, err}return tx.WithSignature(signer, signature)},Context: context.Background(),}, nil
}

从这里看出来,私钥始终保存在signer这个方法中,在整个使用过程中没有将私钥作为参数传递的情况。

如果使用的是filekey+密码的情况会进入到这个方法

func NewKeyStoreTransactor(keystore *keystore.KeyStore, account accounts.Account) (*TransactOpts, error) {log.Warn("WARNING: NewKeyStoreTransactor has been deprecated in favour of NewTransactorWithChainID")signer := types.HomesteadSigner{}return &TransactOpts{From: account.Address,Signer: func(address common.Address, tx *types.Transaction) (*types.Transaction, error) {if address != account.Address {return nil, ErrNotAuthorized}signature, err := keystore.SignHash(account, signer.Hash(tx).Bytes())if err != nil {return nil, err}return tx.WithSignature(signer, signature)},Context: context.Background(),}, nil
}

程序会根据filekey构造一个keystore,后续签名都是在keystore中签名

注意filekey的密码是在终端控制台输入的,其中的readPass函数如下

func readPass() (string, error) {bytePassword, err := term.ReadPassword(syscall.Stdin)if err != nil {return "", err}passphrase := string(bytePassword)passphrase = strings.TrimSpace(passphrase)return passphrase, nil
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/794536.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【可视化+编程】快速掌握seaborn的基础用法

https://mp.weixin.qq.com/s/gRkFrLbChEJ5kHsGokLA6A?poc_token=HNGw3majZY6E6VuU_gYN_ODo66iKNCpKzodh8_3T

密码正则表达式

String regex = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*(){}+=]).{9,}$";

centos7.9安装mysql8.0.39

1. 添加 MySQL Yum 仓库 首先,需要下载并安装 MySQL Yum 仓库 RPM 包:sudo rpm -Uvh https://repo.mysql.com/mysql80-community-release-el7-3.noarch.rpm2. 更新 Yum 缓存 安装完 Yum 仓库后,更新 Yum 缓存:sudo yum clean all sudo yum makecache3. 安装 MySQL 现在可以…

在stable diffussion中完美修复AI图片

无论您的提示和模型有多好,一次性获得完美图像的情况很少见。修复小缺陷的不可或缺的方法是图像修复(inpainting)无论您的提示和模型有多好,一次性获得完美图像的情况很少见。 修复小缺陷的不可或缺的方法是图像修复(inpainting)。在这篇文章中,我将通过一些基本示例来介…

51nod 1020 逆序排列

51nod 1020 逆序排列 学习笔记 其实要预处理,但唐的我非要每次都求一遍。 设状态为 \(dp[i][j]\) 选了 i 个数逆序对数为 j 的排序种类数。 首先初始化 \(dp[i][0]=1\) 即没有逆序对,转移方程 \(dp[i][j]=dp[i-1][j]+dp[i-1][j-1]+……+dp[i-1][j-i]\) 这是显然的(放上这个数…

2181.合并零之间的节点

问题描述: 给你一个链表的头节点 head ,该链表包含由 0 分隔开的一连串整数。链表的 开端 和 末尾 的节点都满足 Node.val == 0 。 对于每两个相邻的 0 ,请你将它们之间的所有节点合并成一个节点,其值是所有已合并节点的值之和。然后将所有 0 移除,修改后的链表不应该含有…

调谐半导体吸收光谱(TDLAS)技术简介

一、调谐半导体吸收光谱(TDLAS)技术简介 TDLAS是Tunable Diode Laser Absorption Spectroscopy的简称,该技术主要是利用可调谐半导体激光器的窄线宽和波长随注入电流改变的特性,通过调制激光器的波长,使激光器的波长扫描过被测气体分子的吸收峰,从而基于比尔朗伯定律,使气…

极狐GitLab 新一代容器镜像仓库正式上线啦!

从极狐GitLab 17.3 开始,私有化部署实例也可以使用新一代容器镜像仓库啦!新一代容器镜像仓库具有更高效的零宕机垃圾收集功能和其他优势。从去年开始,极狐GitLab 就启动了重构容器镜像仓库的计划,用以构建具有更强功能的镜像仓库,比如零宕机垃圾收集。自从将此功能成功迁移…

集合底层学习笔记

集合的底层原理 数据结构中有 数组 和 链表 来实现对数据的存储,但这两者基本上就是两个极端。数组:数组存储区间是连续的,占用内存严重,故空间复杂度很大。但数组的二分查找时间复杂度很小,为O(1);数组的特点是:寻址容易,插入和删除困难。 链表:链表存储区间不连续,占…

2.HDFS

HDFS一.HDFS概述1.HDFS的产生背景和定义(1)HDFS产生背景随着数据量越来越大,在一个操作系统存不下所有的数据,那么就分配到 更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系 统来管理多台机器上的文件,这就是分布式管理系统.HDFS只是分布式文 件管理系统中的一…

编译hello world

新建一个文件夹存放代码 在文件夹内新建一个java文件。hello.java 在文件内编写代码public class hello{public static void main(String[] args){System.out.print("hello world!");} }编译java文件,在文件目录下打开命令提示符窗口,输入:javac hello.java编译ja…

海外合规|新加坡 【数据保护新风向】你的DPO注册了吗?

数据安全已经成为了我们不可忽视的重要议题。新加坡个人数据保护委员会(PDPC)提醒,2024年9月30日之前,根据新加坡的个人资料保护法(PDPA),每个组织都必须指定至少一名数据保护官(DPO)来确保数据的合规使用。 DPO注册相关问题: 1、是否必须通过BizFile+注册我组织的DP…