-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.json
1 lines (1 loc) · 181 KB
/
search.json
1
[{"title":"cosmpy-sdk","url":"/blog/2024/05/19/3-cosmos/300-cosmpy-sdk/","content":"1.cosmpy源码解读1.1 项目简介基于 Cosmos-SDK 用于与区块链交互的 Python 客户端库 cosmpy-项目地址\n官方下也有一些示例文档和教程 可参考:https://fetch.ai/docs\n1.2 下载源码并安装依赖\npoetry 的使用\n# 安装brew install poetry # 这里是mac电脑下安装方式,win可自行查找# 进入项目目录下执行下面命令poetry install # 将根据项目根目录中的pyproject.toml文件中定义的依赖项列表,下载并安装所有必需的依赖项\n\n1.3 调试代码和模块解析aerial模块\n\nconfig.py:\n\n\t\t核心逻辑是NetworkConfig 和 NetworkConfigError 两个类\n\t\tNetworkConfig 主要配置 不同网络环境对应的成员数据\n\t\tvalidator函数主要检查classdata对象成员是否符合规范\n\n\naerial/init.py\nLedgerClient 根据config中的URL类型建立一个客户端\n\n\n\n1.4 更改配置后调试测试用例项目测试用例中有一个简单示例:\ncosmospy/tests/integration/test_tx.py::test_faucet_transaction_balance\n\n更改相关配置主要是代币名称,节点地址等,先保证此用例通过\n2.cosmpy实践2.1 找到proto源文件并导入相关依赖改造 cosmos-sdk 项目的 proto 源文件路径一般在根目录下的 proto 文件夹中\n\n需注意 py-proto 是最后生成的Python调用文件,至于其他三个文件都可通过Google下载\n2.2 编译proto文件\n编译命令, 需进入proto路径下,并提前创建py-proto文件夹\n–proto_path=./proto # 指定proto文件的路径\n–python_out=./proto/py-proto –grpc_python_out=./proto/py-proto # 指定Python调用文件路径\n$(find ./ ( -path “/proto/“ ) -type f -name “*.proto”) # 查找proto文件夹下所有 .proto结尾文件\npython -m grpc_tools.protoc --proto_path=./proto --python_out=./proto/py-proto --grpc_python_out=./proto/py-proto $(find ./ \\( -path "*/proto/*" \\) -type f -name "*.proto") \n\n2.3 生成的文件可能没有包含__init__文件,需要循环创建\n若已经有则可跳过此步骤\n\n\n将 py-proto 文件夹导入cosmpy项目, 重命名为 proto 此时会覆盖旧项目 proto\n\n在proto文件夹下创建__init__.py 文件\n\n循环遍历每个文件夹并创建__init__.py 文件\nfind ./cosmpy/proto/amino -type d -not -name '.*' -exec sh -c 'if [ ! -f "{}/__init__.py" ]; then touch "{}/__init__.py"; fi' \\;find ./cosmpy/proto/cosmos -type d -not -name '.*' -exec sh -c 'if [ ! -f "{}/__init__.py" ]; then touch "{}/__init__.py"; fi' \\;\n\n其他文件夹操作方式类似,操作完毕后将得到完整可用的Python客户端调用文件\n\n\n2.4 按proto文件编写对应客户端代码\n","categories":["blockchain"],"tags":["python","cosmos"]},{"title":"Bitcoin地址规则","url":"/blog/2024/06/28/1-bitcoin/100-btc%E5%9C%B0%E5%9D%80%E8%A7%84%E5%88%99/","content":"1.私钥生成规则\n私钥由 24 组数字组成,每 11 个数字为一组,共计 264 个二进制数字(24×11=264)\n\n\n私钥的组成 = 随机二进制数 + 校验和\n私钥有一部分是随机生成的,\n最后 8 位叫作校验和(checksum)是以前面的随机部分作为输入通过一个公式计算得到\n\n会随机生成23组二进制数据,每11位一组,最后第24组只会生成3位\n23*11+3 = 256 位随机数 + 8位校验和 = 264位【最终私钥的二进制】\n在 BIP39 标准下,一个单词需要 11 位数字表达,这里有 264/11 = 24个单词\n生成的私钥组合后 就是一个 264位的二进制数据 【缺点是不方便人类记忆】\n1.1校验和随机熵 256位 + 8位校验和 = 264位二进制数 / 11个二进制一组 = 24组数字,对应24个单词【助记词】\n随机熵 224位 + 7位校验和 = 231位二进制 / 11个二进制一组 = 21组数字,对应21个单词\n随机熵 192位 + 6位校验和 = 198位二进制 / 11个二进制一组 = 18组数字,对应18个单词\n随机熵 160位 + 5位校验和 = 165位二进制 / 11个二进制一组 = 15组数字,对应15个单词\n随机熵 128位 + 4位校验和 = 132位二进制 / 11个二进制一组 = 12组数字,对应12个单词\n// 校验随机熵,128, 160 , 192, 224, 256func validateEntropyBitSize(bitSize int) error { if (bitSize%32) != 0 || bitSize < 128 || bitSize > 256 { return ErrEntropyLengthInvalid } return nil}// 256/8 = 32个 bytefunc NewEntropy(bitSize int) ([]byte, error) { err := validateEntropyBitSize(bitSize) if err != nil { return nil, err } entropy := make([]byte, bitSize/8) _, err = rand.Read(entropy) return entropy, err}// 创建助记词/*case1: 随机熵256位二进制数据时,校验和为 256/32 = 8 entropyBitLength = 256 checksumBitLength = 256/32 case2: 随机熵128位二进制数据时,校验和为 128/32 = 4 entropyBitLength = 128 checksumBitLength = 128/32 case3: 随机熵224位二进制数据时,校验和为 224/32 = 7 entropyBitLength = 224 checksumBitLength = 224/32 */func NewMnemonic(entropy []byte) (string, error) { // Compute some lengths for convenience. entropyBitLength := len(entropy) * 8 checksumBitLength := entropyBitLength / 32 sentenceLength := (entropyBitLength + checksumBitLength) / 11\n\n1.2私钥转换每11个二进制数一组,一共24组。如果都用十进制来表示:可表示的十进制数范围是 “0” 至 “2047”,一组最多可以表示2048个十进制数,[ 00000000000 至 11111111111 ]\n转换为24组十进制的数据,每组取值范围都是 0~2047 【还是不利于记忆】\nBIP 39 单词表共包含 2048 个单词,按照字母顺序排列。\n每个单词都代表 “0” 至 “2047” 之间的某个数字。私钥中的每组十进制数都可以被写成一个单词。\n协议制定的,用这个单词就代表用了对应数字。\n十进制数 543 对应的是dumb,原因是github上是从1开始,所以行号为544\nbips/bip-0039/english.txt at 22660ad3078ee9bd106e64d44662a59a1967c4bd · bitcoin/bips\n2.HD钱包为什么要有一个规定?因为这是不同加密社区正式商定的推导路径。\n我可以按照我想要的任何制定标准创建自己的树,并从公钥/私钥创建和签署有效交易。\n因为有了协议标准,其他钱包也能重现这种结构,不会导致用户的钱被锁定在完全任意的私钥/公钥树中\n\nHD 钱包树由到第一个地址节点的派生路径表示。\n例如,以太坊的默认值是 m/44’/60’/0’/0。该路径中的每个数字代表上面树中的某个级别。\nm / purpose' / coin_type' / account' / chain / address_index\n\n路径 m/44'/60'/0'/0 解构\n\n44 标准协议\n60 以太坊 coin_type 解决兼容不同链的问题\n0 账户 account 解决不同身份地址管理,比如财务和人事部门 使用不同的Account\n0 链 chain 0 是外部地址,对于btc中 1 是内部地址或找零地址\n\n\n【扩展私钥无法签署交易,只能使用子地址中对应的私钥】\n注:钱包本身不保存资产,资产是在记录比特币网络账本中的(通常称为保存在链上),钱包实际是一个管理私钥(生成、存储、签名)的工具\n钱包为了显示你的比特币 “余额”,钱包软件必须在比特币区块链上查询所有由你的私钥控制的 UTXO,然后将这些 UTXO 的值相加,并显示最终余额。\n3.地址生成逻辑\n首先生成一个随机数作为私钥\n然后根据椭圆曲线算法(ECDSA)计算出公钥\n然后在根据哈希运算及校验编码得到比特币地址\n\n公钥及地址是公开的,私钥这是保密的,私钥推导地址的过程也是单向的,无法通过地址反推到公钥及私钥。\n\n4.地址类型4.1 传统的 P2PKH 地址全称:支付到公钥哈希值(Pay-to-Public-Key-Hash)\n地址格式:以 1 开头\n字符长度:32 个字符\n说明:这种地址类型是比特币最早的地址格式,用户通过公钥哈希来接收比特币。它的安全性依赖于公钥的私钥。\n4.2 兼容隔离见证 P2SH-P2WPKH 地址全称:支付到脚本哈希值(Pay-to-Script-Hash)\n地址格式:以 3 开头\n字符长度:32 个字符\n说明:这种地址类型也称为嵌套隔离见证地址,允许用户在不完全支持隔离见证的环境中使用隔离见证的优势。它通过脚本哈希来接收比特币。\n4.3 原生隔离见证 P2WPKH 地址全称:支付到原生公钥哈希值(Pay-to-Witness-Public-Key-Hash)\n地址格式:以 bc1 开头\n字符长度:42 个字符\n说明:这种地址类型是比特币的原生隔离见证地址,使用 Bech32 编码,提供更高的交易效率和更低的手续费。\n4.4 Taproot 地址 P2TR全称:支付到 Taproot 地址(Pay-to-Taproot)\n地址格式:以 bc 开头\n字符长度:62 个字符\n说明:Taproot 是比特币协议的一个重要升级,旨在提高隐私性和智能合约的灵活性。它允许更复杂的交易结构,同时保持简单的支付方式。\n5.UTXO集所有 UTXO 的集合被称为 UTXO 集。比特币节点会追踪 UTXO 集,确定哪些代币未被花费哪些可以花费。\nUTXO 其实是包含\n\n一定数量的比特币(以 “聪(satoshi)” 为单位)\n以及花费这些比特币时所需满足的条件 叫作 锁定脚本(locking script)\n\n当我们要使用一个 UTXO 时,就是用私钥对 UTXO 进行解锁(签名),以便使用其中的比特币。\n手续费: Input - out = fee\nout中: PubKey Script 字段(简称为 “scriptPubKey”)就是我们所说的 “锁定脚本”, 别人的公钥锁定\nInput中:Signature Script 字段(简称为 “scriptSig”)也就是所谓的 “解锁脚本”,自己的私钥解锁\n5.1 coinbaseCoinbase 交易是一种特殊类型的交易,通过创建新的比特币来奖励找到区块的矿工。\n由于创造了新的比特币,coinbase 交易没有输入,但是会产生一个或多个输出。\n就像所有正常输出一样,coinbase 交易的输出是新的 UTXO。\n6.交易过程6.1 发起交易及交易签名\n交易输入: 他们指向之前的交易创建的 UTXO,通常钱包会收集当前可用的 UTXO 集合作为交易的输入;\n交易输出:表明多少比特币会锁定到哪些地址,即生成新的 UTXO 。\n签名: 用私钥来解锁交易输入的UTXO。\n\n创建比特币交易是在钱包内完成的,而不是在节点上,因此可以在离线的情况下创建交易,交易创建后,通过比特币节点发送到比特币网络中。\n6.2 节点验证交易有效性无论交易来自哪里,交易就得先被节点的交易池(mempool)接受。交易池就是未确认交易的缓存,以便矿工从中挑选出手续费率最高的交易、打包到区块中。\n当然矿工有首先检查交易是否有效,例如检查:\n\n交易输出是否 小于 0 或者大于 2100 万 BTC\n交易不是一笔 coinbase 交易 ,因为区块之外无法存在任何 coinbase 交易。\n交易的 “重量” 不超过 400000 单位 。这么大体积的交易可能在共识上是有效的,但会占据太多的交易池空间。防止攻击者尝试使用体积非常大但永远不会被挖出的交易来塞爆交易池。\n签名验证确定 UTXO 的有效性(UTXO 脚本检查)\n\n所有有效的交易会传播给网络中的所有节点。\n6.3 使用工作量证明挖掘新区块矿工从从交易池中找出最优的一组交易(在一个区块限制下,手续费收益最大)\n给这组交易创建merkle树,然后不断的执行暴力哈希运算,以求解出满足一下 Hash 目标值的nonce值\nSHA256(SHA256(version + prev_hash + merkle_root + ntime + nbits + nonce )) < HASH 目标值\n\nversion: block的版本 \nprev_hash: 上一个block的hash值 \nmerkle_root: 需要写入的交易记录的hash树的值 \nntime: 更新时间 \nnbits: 当前难度\n7.隔离见证隔离见证(SegWit)是一个由多个 BIP(141、142、143、144 和 145)描述的软分叉\n其主要用意是优化比特币交易和区块的结构,将交易的签名(也叫 “脚本签名(scriptSig)”、“witness” 或 “解锁脚本”)从交易中移到一个独立的结构中\nSegWit解决了什么问题?SegWit 的首要目标不是节省区块空间,而是修复交易的不定形漏洞。\n在 SegWit 启用之前,尚未上链确认的交易的 ID (txid)可能会因为所纳入的脚本或者签名本身的变化而发生变化。\nSegWit 将脚本签名(ScriptSig)转移到了交易的一个新部分 “witness”(该部分不用来计算 txid)中,交易的不定形漏洞得以修复,交易 ID 也变成了未确认交易可以依赖的标识符。\n这一更改让交易的 ID 变成可以依赖的数据,但将脚本和签名数据移到 Witness 字段中使得开发者必须提出一种新的交易费计量方法,根据 “block weight”[不同类型的数据设计了数据量的乘数] 而非 “block size”[数据量的大小] 计算交易费\n在 SegWit 启用之前,区块大小是 100 万字节(约 1 MB)\n而在 SegWit 启用之后,区块大小上限变成了 400 万 weight,换算过来就是平均每区块 1.5 ~ 2.0 MB 左右(具体视区块中包含的交易量而定),但是最多可容纳 4 MB 的数据。\n这是因为 witness 数据和交易中其它数据的 weight 比是 1:4,区块中可以塞入更多交易,连带会让手续费水平降低。\nSegWit 升级见证了广泛的用户群体站出来与贪婪的矿工对抗,通过拒绝非 SegWit 的区块来迫使矿工升级。\n它利用了比特币的博弈经济学来降低矿工的动力 — 如果他们的区块会被拒绝,就不能从中收获区块奖励和手续费\n","categories":["blockchain"],"tags":["bitcoin"]},{"title":"python基础数据类型","url":"/blog/2023/03/04/tools/0-python%E5%9F%BA%E7%A1%80%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B/","content":"1.基础数据类型1.1 变量赋值和引用\n每定义一个新变量时,就会在内存中开辟一块空间用于数据存储。不同的变量,内存地址是不同的。\n使用 id() 获取内存地址,使用 is 判断变量内存地址是否相同。\n\n\n把一个变量的内存地址同时关联到另一个变量上,称为引用。两个变量对应同一块内存地址\na = 100b = aassert id(a) == id(b)assert a is b\n\n不可变类型变量和可变类型变量在引用时的区别\n常用int、字符串都是不可变类型, 字典、集合、列表都属于可变类型的变量\n# b引用a所指向的数据a = 100b = aassert id(a) == id(b)assert a is ba = 100b = aa = 200print(a, b) # 200 100assert a is not bassert id(a) != id(b)a1 = "str"b1 = a1a1 = "string"print(a1, b1) # string stra = [1, 2, 3, 4]b = aa[0] = 100print(a[0], b[0]) # 100, 100assert a is bassert id(a) == id(b)\n\n1.2 小整数池和字符串驻留Python中存在一个小整数池,范围通常在-5到256之间。在这个范围内的整数会被提前创建并缓存,以便节省内存。 \n字符串驻留范围:英文字母、数字、下划线\n以下代码在Ipthon中执行,使用Vscode和PythonCharm执行结果可能不同\n# 在ipython中执行a = 100b = 100print(a is b) # Truea = 500b = 500print(id(a) == id(b)) # Falsea, b = 500, 500print(a is b) # True 一行定义两个相同值的变量,解释器会优化,a、b是同一内存地址a = "abc"b = "abc"print(a is b) # Truea = "abc!"b = "abc!"print(a is b) # False\n\n\n\n1.3 数据类型数值: int float bool complex(复数)序列: str list tuple散列: set dict \n\n常见基础面试题\n\n列表去重方案\n# 方案一 set() 利用集合去重的特性original_list = [1, 2, 3, 4, 3, 2, 1]deduplicated_list = list(set(original_list))print(deduplicated_list) # 输出: [1, 2, 3, 4]# 方案二 使用enumerate 找到列表下标判断元素是否存在deduplicated_list = [x for i, x in enumerate(original_list) if x not in original_list[:i]]print(deduplicated_list) # 输出: [1, 2, 3, 4]# 方案三 使用 dict.fromkeys():利用字典的键的唯一性deduplicated_list = list(dict.fromkeys(original_list))print(deduplicated_list) # 输出: [1, 2, 3, 4]# 方案四 使用 Counter 是 collections 模块提供的一个计数器对象,可以用来统计元素出现的次数deduplicated_list = list(Counter(original_list)) # Counter({1: 2, 2: 2, 3: 2, 4: 1})print(deduplicated_list) # 输出: [1, 2, 3, 4]\n\n字符串反转\nprint("第一种方式:", "".join(reversed(data)))print("第二种方式:", data[::-1])\n\n\n推导式\nlist推导式\nresult = ["data{}".format(i) for i in range(0, 100) if i % 2 == 0]\n\ndict 推导式\nprint({f"data{(i + 1)}": i + 1 for i in range(3)}) # {'data1': 1, 'data2': 2, 'data3': 3}\n\nset 推导式\n{ expression for item in Sequence if conditional }\n\ntuple 推导式 (生成器表达式)\na = (x for x in range(1,10)) # <generator object <genexpr> at 0x7faf6ee20a50> 生成器对象tuple(a) # 使用 tuple() 函数,可以直接将生成器对象转换成元组\n\n1.4 collections\n 这个模块实现了一些专门化的容器,提供了对 Python 的通用内建容器 dict、list、set 和 tuple 的补充。\n\nfrom collections import namedtuple, deque, ChainMap# namedtuple 命名元祖Student = namedtuple('Students', ["name", "age", 'index'])tu1 = Student(name="张三", age=18, index=1)print(tu1.name) # 张三print(isinstance(tu1, tuple)) # True 也是元祖类型数据print(type(tu1)) # tu1数据类型 <class '__main__.Students'> Student对象# deque 双端队列d = deque('ghi')d.extendleft('123', )d.appendleft('4')print(d) # deque(['4', '1', '2', '3', 'g', 'h', 'i'])# ChainMap 将多个字典或者其他映射组合在一起,创建一个单独的可更新的视图dict1 = {'music': 'bach', 'art': 'rembrandt'}dict2 = {'art': 'van gogh', 'opera': 'carmen'}dict3 = {'opera': 'dict3', 'test': 'dict3'}c = ChainMap(dict1, dict2, dict3)# 获取map中的key和其传入参数有关系, 迭代顺序是通过从后往前扫描print(c.get('art'), c.get('opera')) # rembrandt, carmen# 如果要实现dict.update功能,可以使用update()c.update(dict3)print(c.get('art'), c.get('opera')) # rembrandt, dict3\n\n\n\n1.5 iterable可迭代对象: 可被 for 遍历都是可迭代对象 \n\n实现了 iter 方法,并且该方法返回一个迭代器对象。 \n实现了 getitem 方法,并且可以通过索引访问元素。\n\n\nclass collections.abc.Iterable\n提供了 __iter__() 方法的抽象基类。\n使用 isinstance(obj, Iterable) 可以检测一个类是否已经注册到了 Iterable 或者实现了 __iter__() 函数,但是无法检测这个类是否能够使用 __getitem__() 方法进行迭代。检测一个对象是否是 iterable 的唯一可信赖的方法是调用 iter(obj)。\n\n示例1: 实现 __iter__ 但是返回一个list 非迭代器对象 \nclass Iterable1: def __init__(self): self.data = [1, 2, 3, 4] def __iter__(self): # 返回的是一个列表,而不是一个迭代器对象 return self.dataobj1 = Iterable1()assert isinstance(obj1, Iterable) # Trueassert iter(obj1) # iter() returned non-iterator of type 'list'\n\n示例2: 实现 __iter__ 返回一个迭代器对象 \nclass Iterable2: def __init__(self): self.data = [1, 2, 3, 4] def __iter__(self): # 返回的是一个迭代器对象 return iter(self.data)obj2 = Iterable2() # obj2 是可以被for遍历的对象print(iter(obj2)) # <list_iterator object at 0x1043bb6d0>\n\n示例3: 实现 __getitem__ 访问元素 \nclass Iterable3: def __init__(self): self.data = [1, 2, 3, 4, 5] def __getitem__(self, index): # 通过索引访问元素,实现迭代行为 return self.data[index]my_iterable = Iterable3()print(iter(my_iterable))\n\n\n\n1.6 iterator迭代器:必须要同时拥有 __iter__ 和 __next__ 方法才是迭代器\n\n迭代器调用 __next__ 方法会调用迭代器中的下一个值\n\n示例1:通过 iter(iterable) 得到迭代器\nmy_iterator = iter(["1", "2", "3"])# hasattr 判断某个对象是否包含某个属性信息print(hasattr(my_iterator, "__iter__")) # Trueprint(hasattr(my_iterator, "__next__")) # True\n\n示例2:实现一个迭代器,必须要实现 __next__ 和 __iter__ 方法\n\n示例中并没有手动实现 __iter__ 会使用父类的 __iter__ \n\nfrom typing import Iteratorclass Students(Iterator): def __init__(self): self.students = ["张三", "李四", "王五"] self.index = 0 def __next__(self): if self.index >= len(self.students): raise StopIteration self.index += 1 return self.students[self.index - 1]print(isinstance(Students(), Iterator)) # Trueprint(my_iterator.__iter__()) # <__main__.Students object at 0x1009a7950>for item in Students(): print(item) \n\n示例3:实现一个迭代器,自己实现 __init__ 方法\nclass Students2(Iterator): def __init__(self): self.students = ["1", "2"] self.index = 0 def __iter__(self): return iter(self.students) def __next__(self): if self.index >= len(self.students): raise StopIteration self.index += 1 return self.students[self.index - 1]my_iterator2 = Students2()print(my_iterator2.__iter__()) # <list_iterator object at 0x104f37fd0>print(isinstance(my_iterator2, Iterator)) # Trueprint(next(my_iterator2)) # 1print(next(my_iterator2)) # 2print(next(my_iterator2)) # raise StopIteration\n\n示例4:实现一个 range 迭代器\n\n\nrange 方法的签名 start、stop 两个参数\n__iter__ 方法要求返回值必须是一个”迭代器“ (或者返回值必须要有 __next__ 方法)\n\n\nclass Next: def __init__(self, stop, start=-1): self.start = start self.stop = stop def __next__(self): if self.start >= self.stop - 1: raise StopIteration self.start += 1 return self.startclass MyRange: def __init__(self, stop): self.stop = stop def __iter__(self): return Next(self.stop)my_range = MyRange(5) # <__main__.MyRange object at 0x1045029d0># False 断言它不是一个迭代器,但是它可以被for遍历,所以__iter__返回值有__next__方法也可以print(isinstance(my_range, Iterator)) for item in my_range: print(item) # 也可以通过 for 遍历\n\n示例5: 基于MyRange使用while实现for \n\nfor 会自动调用 __iter__ , __next__ 方法,但是while不会,需要手动调用\n\n# 基于MyRange使用while实现fordef my_while(): start, stop = 0, 5 my_range = MyRange(stop) numbers = my_range.__iter__() # 手动调用__iter__方法 while start < stop: print(numbers.__next__()) start += 1my_while()\n\n\n\n1.7 generator生成器(高效):生成器是特殊的迭代器,迭代器是特殊的可迭代对象,那么生成器必定是可迭代对象\n\n使用yield关键字返回一个生成器对象\n\nfrom typing import Iterable, Iteratordef g_func2(): my_list = range(3) for i in my_list: yield i * ig = g_func2()print(isinstance(g, Iterable)) # Trueprint(g.__iter__()) # <generator object g_func1 at 0x10271fc10>print(next(g))print(next(g))print(next(g))print(hasattr(g, "__iter__")) # Trueprint(hasattr(g, "__next__")) # Trueprint(isinstance(g, Iterator)) # True\n\n\n\n1.8 for循环的本质\n\n调用iter(),将numbers转化为迭代器numbers_iterator\n调用next(numbers_iterator),返回出numbers的第一个元素\n循环步骤2,迭代完numbers内所有数据,捕获异常\n\n\n# while + iteratornumbers = [1, 2, 3, 4]numbers_iterator = iter(numbers)while True: try: print(next(numbers_iterator)) except StopIteration: # 捕捉异常终止循环 break# for循环for i in numbers: print(i)\n\n\n\n1.9 itertools\n为高效循环而创建迭代器的函数\n\niterable = itertools.chain(["A", "B", "C"], ["D", "E", "F"])for i in iterable: print(i) # --> A B C D E Ffrom_iterable = itertools.chain.from_iterable(['ABC', 'DEF'])for i in from_iterable: print(i)r = itertools.combinations("ABCD", 2)for i in r: print(i) # --> AB AC AD BC BD CD\n\n\n\n1.10 lambda、map、zip\nlamdba 处理简单业务逻辑\ny: any = lambda x: x + 1print(y(10))Students = [ {"name": "a", "age": 18}, {"name": "c", "age": 20}, {"name": "b", "age": 19}, {"name": "ca", "age": 19}, {"name": "cb", "age": 19}]# 根据age排序,age一致时根据name排序print(sorted(Students, key=lambda student: (student["age"], student["name"])))\n\nmap(func, *iterables) –> map object\n# map 可迭代对象元素合并 返回新的map对象,按最短的对象合并a = [1, 2, 3]b = [4, 5, ]# map(func, *iterables)# func --> lambda a1, b1: (a1, b1)# *iterables --> a, bnum1 = map(lambda a1, b1: (a1, b1), a, b) # <map object at 0x109efed60>for i in num1: print(i) # (1, 4), (2, 5)\n\nreduce 求和\nfrom functools import reduceprint(reduce(lambda x, y: x + y, range(1, 101))) # 5050\n\nfilter 过滤\nprint(list(filter(lambda x: x > 5, range(10)))) # <filter object at 0x106ad6c40>\n\nzip\na = [1, 2, 3]b = [4, 5, 6, 7, 8]print(list(zip(a, b))) # [(1, 4), (2, 5), (3, 6)] # 元素个数与最短的列表一致\n\n1.11 namespace\n一般有三种命名空间:\n\n内置名称(built-in names), Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。\n全局名称(global names),模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。\n局部名称(local names),函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)\n\n\nPython的作用域一共有4种【规则顺序: L –> E –> G –> gt; B】\n\nL(Local):最内层,包含局部变量,比如一个函数/方法内部。\n\nE(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。\n比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。\n\nG(Global):当前脚本的最外层,比如当前模块的全局变量。\n\nB(Built-in): 包含了内建的变量/关键字等。最后被搜索\n\n\n\n闭包\n\n1.在一个函数内部定义了另一个函数2.内部函数引用了外部函数的变量\n\ng_count = 0 # 全局作用域def outer(): o_count = 1 # 闭包函数外,函数中 print(f"Enclosing: {o_count}") def inner(): i_count = 2 # 局部作用域 print(f"Local: {i_count}") nonlocal o_count # 外层作用域 o_count += 5 print(f"Enclosing: {o_count}") return inner # 返回函数名称 可以被调用print(f"Global: {g_count}") # Global: 0func = outer() # Enclosing: 1func() # Local: 2 Enclosing: 6func() # Local: 2 Enclosing: 11\n\n1.12 private_name# import 私有变量# 1. __name它不会被导入到导入模块的命名空间中# 2. _name会被导入到导入模块的命名空间中class MyClass: def __init__(self): self.__name = "Private Name" # 私有变量 __name self._name = "Conventionally Private Name" # 约定上的私有变量 _name def get_private_name(self): return self.__name def get_conventionally_private_name(self): return self._nameobj = MyClass()# 访问私有变量 __nameprint(obj.get_private_name()) # 输出: Private Name# print(obj.__name) # 错误,在类外部,无法直接访问私有变量,会引发 AttributeError 错误print(obj._MyClass__name) # 输出: Private Name,通过名称重整方式访问私有变量# 访问约定上的私有变量 _nameprint(obj.get_conventionally_private_name()) # 输出: Conventionally Private Nameprint(obj._name) # 输出: Conventionally Private Name,可以直接访问约定上的私有变量\n","tags":["python"]},{"title":"1-装饰器","url":"/blog/2023/03/05/tools/1-%E8%A3%85%E9%A5%B0%E5%99%A8/","content":"2.装饰器(decorator)\n本质函数当作参数传递,利用闭包特性实现\n\n2.1 最原始的装饰器def add(*args): return sum(args)# 把函数当做参数,传递给另外一个函数def new_add(func, *args): return f"对原函数进行装饰 遵循开放封闭原则: {func(*args)}"print(new_add(add, 1, 2, 3))\n\n\n\n2.2 常见使用方式\n编写一个记录日志 和 统计函数执行耗时的装饰器\n\nimport timedef loger(func): def wrapper(*args): print("1 记录日志的代码...") result = func(*args) print("2 日志分析的代码...") return result return wrapper# 编写一个计算方法执行耗时的装饰器def timer(func): def wrapper(*args, **kwargs): print("3 计算耗时开始") start_time = time.time() result = func(*args) end_time = time.time() print(f"{func.__name__}: 耗时: {end_time - start_time}") print("4 计算耗时结束") return result return wrapper@loger@timerdef add(*args, **kwargs): return sum(args)print(add(11, 1)) # 执行顺序: 1 -> 3 -> func.__name__耗时 -> 4 -> 2 -> func执行结果\n\n\n\n2.3 装饰器带参数# 编写一个带参数的装饰器,用于验证用户登录def login_verify(is_login=False): # 这里接收装饰器的参数 def inner(func): # 接收被装饰的函数 def wrapper(*args, **kwargs): # 这里接收被装饰函数的参数 if is_login: # 装饰器的参数在这里使用,用于判断 print("被装饰函数执行前") result = func(*args) print("被装饰函数执行后") return result else: return None return wrapper # 返回函数的包装器 return inner@login_verify(is_login=True)def add(*args, **kwargs): return sum(args)print(add(11, 22))\n\n\n\n2.4 给类添加一个装饰器def class_name(cls): cls.name = "小明" return cls# 给类添加装饰器@class_nameclass A(object): passprint(A.name)\n\n\n\n2.5 使用类编写装饰器class A: def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): print("__call__ is running ...") return self.func(*args)@A # 本质 A(add)def add(*args): return sum(args)print(add(1, 2))\n\n\n\n2.6 使用类编写装饰器带参数class S: def __init__(self, func, name): self.func = func self.name = name def __call__(self, *args, **kwargs): print("类装饰器转入的参数", self.name) print("1 装饰函数执行之前") result = self.func(*args) print("2 装饰函数执行之后") return resultdef add(*args): return sum(args)s = S(add, "hello")print(s(1, 3))\n\n","tags":["python"]},{"title":"2-面向对象","url":"/blog/2023/03/06/tools/2-%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1/","content":"3.面向对象3.1 三大特性\n面向对象三大特性:继承 封装 多态\n\n# 面向对象三大特性:继承 封装 多态class Base(object): def __init__(self): self.leg = "4" def func1(self): print(f"Base 有 {self.leg} 条腿...")class Cat(Base): def func1(self): print(f"我是cat,有{self.leg}条腿...") print("我会上树")class Dog(Base): def func1(self): print(f"我是dog,有{self.leg}条腿...") print("我跑得快")class Table(Base): def func1(self): print(f"我是一个餐桌,也有{self.leg}条腿,但我不会跑...")def func(arg): arg.func1()func(Base())func(Dog())func(Cat())func(Table())\n\n\n\n3.2 类方法和静态方法\nclassmethod 给类定义的方法staticmethod 目的只是封装在一起,内聚\n\nclass Person(object): def __init__(self, name): self.name = name @classmethod def name(cls, name): return cls(name) @staticmethod def age(age: int): return age def __repr__(self): return self.namea = Person(name="张三")print(a)b = Person.name("李四")print(b)print(a.age(18)) # 对象可以调用print(Person.age(20)) # 类也可以调用\n\n\n\n3.3 property装饰器\n1.将函数属性伪装成数据属性2.统一数据属性的查、改、删操作\n\nclass Person: def __init__(self, name): self.__name = name @property def name(self): return self.__name # 当name 遇到赋值操作, 即 = 时触发被property.setter装饰的函数的执行 @name.setter def name(self, value): self.__name = value # 当name 遇到删除操作,即 del 时触发property.deleter装饰的函数的执行 @name.deleter def name(self): print('deleter')obj1 = Person('abc')print(obj1.name)obj1.name = 'aaa'print(obj1.name)del obj1.name\n\n\n\n3.4 cached_property\n相比 property 增加缓存功能,针对不可变的高计算资源消耗的实例特征属性\n\nfrom functools import cached_property # 内置 3.8版本才加入的cached_propertypip3 install cached-property # 第三方包 支持asyncio\n\n\n\n3.5 属性查找顺序\n对象 —> 父类 —> 继承类, 依次类推,找不到则报错\n\n3.6 多继承\n多继承的优点:同时继承多个父类属性和方法,功能强大。\n多继承缺点:代码可读性变差。\n通过类的mro()方法查看多继承的查找顺序。\n__bases__ 可以查看类继承的所有父类\n\nprint(C.mro())# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class 'object'>]print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B'>)\n\n\n\n3.7 广度优先和深度优先# [<class '__main__.C'>, <class '__main__.A'>, <class '__main__.A1'>, <class '__main__.B'>, <class 'object'>]# python3中全部默认继承object,所以都是新式类- object类提供了一些常用内置方法的实现,如用来在打印对象时返回字符串的内置方法__str__新式类:广度优先obj -> <class '__main__.C'> -> <class '__main__.A1'> ... -> <class 'object'>\n\n\n\n3.8 抽象基类\n1.抽象类本身不能实例化\n2.子类必须实现其定义接口\n\nimport abc# 指定metaclass属性将类设置为抽象类,抽象类本身不能实例化class Animal(metaclass=abc.ABCMeta): @abc.abstractmethod # 该装饰器限制子类必须定义有一个名为talk的方法 def talk(self): # 抽象方法中无需实现具体的功能 passclass Cat(Animal): # 但凡继承Animal的子类都必须遵循Animal规定的标准 def talk(self): # 必须定义talk方法\t\t\t\t\t\t passcat = Cat() # 若子类中没有一个定义talk的方法则会抛出异常TypeError,无法实例化\n\n\n\n3.9 isinstance 和 issubclassprint(isinstance(a, int)) # 断言类型print(issubclass(People, Animal)) # 断言是否其子类\n\n\n\n3.10 动态获取对象信息(反射)hasattr(obj, 'x')\t # 判断对象是否有一个属性,返回布尔值getattr(object, name, default=None)\t # 获取对象的name属性,name属性不存在的返回Nonesetattr(x, 'y', 'v')\t # 更新x对象 y属性的值, 等价于 x.y = 'v',当y不存在的新增delattr(x, 'y')\t\t # 删除x对象 y属性, 等价于 del x.y 属性y不存在则报错\n","tags":["python"]},{"title":"3-魔法方法","url":"/blog/2023/03/07/tools/3-%E9%AD%94%E6%B3%95%E6%96%B9%E6%B3%95/","content":"4.魔法函数4.1 __new__ 、__init__ 、__call __、__del__\n__new__ 实例化对象(1.创建对象 2.分配内存)__init__ 构造方法,实例化对象时自动调用(1.可以没有 2.如果有方法必须返回None,默认不写return语句)__call __ 对象可以被调用时触发执行__del__ 析构方法,当对象被回收时触发执行(程序结束、对象引用计数为零称为垃圾时)\n\nclass MyClass(object): def __init__(self): print("__init__ is running...") def __new__(cls): print("__new__ is running...") return super().__new__(cls) # 创建对象 分配内存 def __call__(self, *args, **kwargs): print("__call__ is running...") def __del__(self): print("__del__ is running...")MyClass() # 匿名对象程序并未使用到,执行完后就销毁了print("----------------------")a = MyClass() # 这里会先执行__new__ 在执行 __init__assert hasattr(a, "__del__") # Trueprint(callable(a)) # True 可以被调用时结果为True,对象如果没有__call__ 属性则是Falseassert hasattr(lambda x, y: x + y, "__call__") # Trueprint(callable(lambda x, y: x + y)) # True\n\n\n\n4.2 __str__ 和 __repr__\n两个方法都只是为了自定义对象的打印信息 \n对象被打印时执行,一般默认先找str, str没有则使用repr\n\nclass A(object): def __init__(self): self.name = "李四" def __str__(self): print("__str__ is running ...") return "str" def __repr__(self): print("__repr__ is running ...") return ""print(A()) # 默认为 <__main__.A object at 0x1043aa710>\n\n\n\n4.3 compare系列class Student: def __init__(self, age): self.age = age def __eq__(self, other): print("__eq__ is running ... 可自定义比较逻辑") if isinstance(other, Student): return self.age == other.age # 返回布尔值 return Falseprint(Student(18) == Student(18))print(Student(18) != 18) # nq, 不相等的逻辑。如果没有实现,则默认是eq的结果取反。print(dir(Student(18))) # __lt__、__gt__、__le__、__ge__ 分别表示小于、大于、小于等于和大于等于。\n\n\n\n4.4 attr系列class MyClass(object): def __init__(self, name, age): self.name = name self.age = age def __getattr__(self, item): print("getattr 获取不存在的对象属性时触发") # super().__delattr__(item) # 'MyClass' object has no attribute 'id' return self.__dict__.get(item) def __setattr__(self, key, value): print("setattr 设置修改对象属性时触发") super().__setattr__(key, value) def __delattr__(self, item): print("delattr 删除对象属性时触发") if item == "name": # 属性是name时抛出异常,或者不进行删除操作 # raise AttributeError("name 属性不让删除...") pass else: super().__delattr__(item) def __getattribute__(self, name): # 访问任何属性(包括存在的和不存在的属性)时都会调用 __getattribute__ 方法 print("__getattribute__ called") return super().__getattribute__(name)a = MyClass("李四", 18) # 每一次给属性赋值 都会执行setattr方法print(a.id)del a.age # 触发delattr方法print(f"查看对象属性:{a.__dict__}")\n\n\n\n4.5 item系列# 一个普通对象通过[] 操作取值时会触发 __getitem__class Person(object): def __setitem__(self, key, value): print("setitem []设置值时触发") setattr(self, key, value) def __getitem__(self, item): print("getitem []取值时触发") return getattr(self, item) def __delitem__(self, key): print("delitem del p[key]时触发", key)p = Person()p['id'] = 1 # 触发setitem方法print(p['id']) # 触发getitem方法del p['id'] # 触发delitem方法\n\n\n\n4.6 __enter__ 和 __exit__\n上下文管理器: 支持”上下文管理协议”的对象,包含 enter() 和 exit() 方法with 可以操作一个 支持上下文管理协议的对象\n\nclass MyOpen: def __init__(self, file_name: str, mode="r"): self.file = open(file_name, mode) def __enter__(self): print("进入with语句块时触发") return self.file # 返回值赋值给 as后面的接收值 def __exit__(self, exc_type, exc_val, exc_tb): print("退出with语句块时触发,不论with语句块是否有异常报错,__exit__都会被执行") self.file.close()with MyOpen("test", "w") as f: f.write("hello world")\n\n\n\n4.7 __slots__\n该类实例只能创建__slots__中声明的属性,否则报错, 具体作用就是节省内存\n\nfrom memory_profiler import profileclass Test(object): __slots__ = ['a', 'name'] def __init__(self, name): self.name = nameTest.c = 3 # 类属性仍然可以自由添加t = Test("xx")t.a = 1print(t.c) # 绕过限制就是给类添加属性# t.b = 2 # AttributeError: 'Test' object has no attribute 'b'class TestA(object): __slots__ = ['a', 'b', 'c'] def __init__(self, a, b, c): self.a = a self.b = b self.c = cclass TestB(object): def __init__(self, a, b, c): self.a = a self.b = b self.c = c@profiledef func_02(): temp = [TestA(i, i + 1, i + 2) for i in range(10000)] del temp temp = [TestB(i, i + 1, i + 2) for i in range(10000)] del tempfunc_02()\n\n\n\n4.8 __add__、 __dict__、 __bases__、 __all__\n__add__: 手动实现相加操作__dict__: 获取对象的属性__bases__: 获取类继承的元素__all__: 当其它文件以“from 模块名 import *”的形式导入该模块时,该文件中只能使用 __all__ 列表中指定的成员\n\nclass MyClass(object): def __init__(self, value): self.value = value def __add__(self, other): # other这里传入的是第二个对象 obj2 obj2.value ==》 __init__ 初始化中传入的value return self.value + other.valuea = MyClass(10)print(a + MyClass(20))print(MyClass.__dict__)# __bases__ 这是一个元祖,里面的元素是继承的类class A(object): passprint(A.__bases__)# 当其它文件以“from 模块名 import *”的形式导入该模块时,该文件中只能使用 `__all__` 列表中指定的成员__all__ = ["MyClass"]\n\n","tags":["python"]},{"title":"4-路径导入","url":"/blog/2023/03/08/tools/4-%E8%B7%AF%E5%BE%84%E5%AF%BC%E5%85%A5/","content":"python文件运行时路径导入的问题\n 错误提示:ModuleNotFoundError: No module named ‘xxxx’\n\n解决这个问题首先要明白Python运行时,Python解释器会按照一定的搜索路径来查找模块和包。\n可以通过sys.path查看具体信息\nimport sysprint(sys.path)\n\n['/Users/jw/Documents/github/abcd/b', '/opt/homebrew/Cellar/[email protected]/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python312.zip', '/opt/homebrew/Cellar/[email protected]/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12', '/opt/homebrew/Cellar/[email protected]/3.12.3/Frameworks/Python.framework/Versions/3.12/lib/python3.12/lib-dynload', '/opt/homebrew/lib/python3.12/site-packages', '/Users/jw/Documents/github/abcd']\n\n\n当前工作目录:即列表下标为0的元素,解释器会首先在当前目录中搜索模块,当前工作目录是指在运行Python脚本时所在的目录\nPYTHONPATH环境变量:PYTHONPATH环境变量可以用来指定额外的模块搜索路径。它是一个包含多个目录路径的列表,Python解释器会按照列表中的顺序进行搜索。\n\n# 可以通过以下命令在终端查看echo $PYTHONPATH \n\n这种方式比较隐蔽,不常用,需要操作者知情才会去查看此环境变量\n\n系统默认路径:Python解释器会在一组默认的系统路径中搜索模块。这些路径包括Python标准库的位置以及安装的第三方库的位置。【参考上述path路径中展示,除最后一个元素和第一个元素外】\n\n解决方案abcd/├── a/│ ├── init.py│ └── m.py # m.py中有一个变量 aa = 11└── b/├── init.py└── mb.py # 在此路径下运行 mb.py 文件 并打印出变量 aa\n\n# mb.pyfrom a.m import aaprint(aa)File "/Users/jw/Documents/github/abcd/b/mb.py", line 29, in <module> from a.m import aaModuleNotFoundError: No module named 'a'\n\n将模块a添加至path路径中\nimport sysfrom pathlib import Pathroot_path = Path(__file__).resolve().parent.parentsys.path.append(str(root_path)) # 将root_path添加到PATH,并会在此模块下搜索print(sys.path) # 此处会输出path的所有内容from a.m import aaprint(aa) # 成功输出变量信息\n\n\n\n类似场景abcd/├── a/│ ├── init.py│ └── m.py # m.py中有一个变量 aa = 11└── b/├── init.py└── mb.py # 在此路径下运行 mb.py 文件 并打印出变量 aa└ main.py # 程序主入口\n\n# mian.pyfrom a.m import aaprint(aa)\n\nmain.py 无需添加至PATH即可成功输出aa变量的原因现在知道了吗?\n根据上述结论推导:\nmain.py文件在运行时,会在当前工作目录下搜索包,即能找到a模块也可以找到b模块,无需添加\n\n","tags":["python"]},{"title":"5-重复逻辑优化","url":"/blog/2023/08/21/tools/5-%E9%87%8D%E5%A4%8D%E9%80%BB%E8%BE%91%E4%BC%98%E5%8C%96/","content":"1.解决api请求变更,URL和参数更新维护等问题?引入客户端代码生成工具,根据接口文档自动生成代码,从而减少由于接口变更的维护成本\n官网地址: https://swagger.io/tools/swagger-codegen/\n\n下载swagger codegen \n生成对应语言客户端代码 [-i 指定api接口路径,-lang 指定语言,-o 指定生成代码文件路径]\n\n阅读生成项目的readme,提供了使用教程\t\t\nswagger-codegen generate -i http://ip:port/static/openapi.yml --lang python -o ./me_api_clientswagger-codegen generate -i http://ip:port/static/openapi.yml --lang go -o ./me_api_client\n\n\n\n\n2. 项目结构分析\n客户端需要连接到指定节点,能提供多节点测试\n客户端需要提供命令生成器 和 执行器\n操作每个模块都需要自定义其模块方法,成本较高,并且大致逻辑一致\n\nNode模块:\n\n初始化node实例提供连接节点功能\nnode实例默认提供对应 交易的一些常用命令设置\nnode都绑定有执行器(executor) 和 命令生成器(generate_xxx)\n\nimport inspectfrom typing import Callablefrom loguru import loggerfrom config.config import app_chainfrom ssh import Client, Resultclass Node: ssh_client = Client(ip=app_chain.Host.ip, port=app_chain.Host.port, username=app_chain.Host.username, password=app_chain.Host.password) config = app_chain def __init__(self, node: str): super().__init__() if "--node" not in node: node = f"--node={node}" self.config.Flags.node = node self.superadmin = self.__get_superadmin_addr() self.__init_instance_config() def update_config(self, attr: str, key: str, value: str): """ If key exists in the attr object, replace the value. If no, add the value :param attr: 'ApplicationChain' object must have attr :param key: :param value: :return: """ sub_cfg_gen = getattr(self.config, attr) found_key = False for i in sub_cfg_gen: if i[0] == key: setattr(sub_cfg_gen, key, value) found_key = True break if not found_key: setattr(sub_cfg_gen, key, value) def __init_instance_config(self): self.update_config("Flags", "fees", "--fees=100umec") self.update_config("Flags", "gas", "--gas=200000") @property def base_cmd(self): return f"{self.config.Host.chain_work_path} " def __get_superadmin_addr(self): get_superadmin_cmd = f"{self.base_cmd} keys show superadmin -a {self.config.Flags.keyring_backend}" return self.ssh_client.exec_cmd(get_superadmin_cmd) def generate_query_cmd(self, cmd: str): query_cmd = self.base_cmd + f"{self.config.Flags.node} {self.config.GlobalFlags.chain_id} " return query_cmd + cmd def generate_tx_cmd(self, cmd: str): tx_cmd = self.base_cmd + (f"{self.config.Flags.fees} {self.config.Flags.gas} " f"{self.config.Flags.yes} {self.config.Flags.keyring_backend} " f"{self.config.Flags.node} {self.config.GlobalFlags.chain_id} ") return tx_cmd + cmd def generate_keys_cmd(self, cmd: str): keys_cmd = self.base_cmd + f"{self.config.Flags.keyring_backend} " return keys_cmd + cmd def executor(self, cmd): logger.info(f"{inspect.stack()[0][3]}: {cmd}") if "keys add" in cmd: _ = self.ssh_client.channel.send(cmd + "\\n") resp_info = self.ssh_client.Interactive.read_channel_data(self.ssh_client.channel) if "existing" in resp_info: resp_info = self.ssh_client.Interactive.input_yes_or_no(self.ssh_client.channel) assert "**Important**" in resp_info return resp_info resp_info = self.ssh_client.exec_cmd(cmd, strip=False) if resp_info.failed: logger.info(f"resp_info.stderr: {resp_info.stderr}") return resp_info.stderr return Result.yaml_to_dict(resp_info.stdout)\n\n\n\nMeta元类:\n为每个类动态生成方法,解决其定义冗余\nclass Meta(type): def __init__(cls, name, bases, attrs): cls.module = name.lower() # 创建类module = 其类名称小写 super().__init__(name, bases, attrs) sub_module = attrs.get('sub_module', []) parent_module = attrs.get('parent_module', '') if isinstance(sub_module, list): for module in sub_module: \t# 添加类方法,名称为其子模块 method = cls.generate_method(parent_module, module) setattr(cls, module, classmethod(method)) elif isinstance(sub_module, dict): for k, module in sub_module.items(): method = cls.generate_method(parent_module, module) setattr(cls, k, classmethod(method)) else: raise f"sub_module type error: {type(sub_module)}, expect list or dict" @staticmethod def generate_method(parent_module, sub_module) -> Callable[..., str]: # 利用闭包特性,生成的类方法只接受 *args,**kwargs 参数 def method(cls, *args, **kwargs): return cls.build_command(parent_module, sub_module, *args, **kwargs) return method def build_command(cls, parent_module, sub_module, *args, **kwargs): args_str = " ".join(map(str, args)) kwargs_str = " ".join([f"--{key}={value}" for key, value in kwargs.items() if value != ""]) return f"{parent_module} {cls.module} {sub_module} {args_str} {kwargs_str} " def __getattr__(cls, attr): raise AttributeError(f"'{cls.__name__}' class has no attribute '{attr}'") def help(cls): for attr_name in cls.sub_module: attr = getattr(cls, attr_name) if not callable(attr): raise TypeError(f"attribute '{attr_name}' is not callable") print(f"Available methods: {list(cls.sub_module)}") print(f"Example usage: {cls.__name__}.{list(cls.sub_module)[0]}('argument')")\n\n\n\n","tags":["python","tools"]},{"title":"move入门与交互","url":"/blog/2024/07/01/5-sui/500-move%E5%85%A5%E9%97%A8%E4%B8%8E%E4%BA%A4%E4%BA%92/","content":"move基础课\nmove中文社区\n黑客松\n\nTask1环境安装\nInstall 文档已经很齐全了,直接开整\n01-hello-world 建议查看并熟悉\n\n任务概览\nfork这个仓库 letsmove\n复制 mover/001 并重命名为自己github名称,例子:\n\n\n\n查看task1的任务列表 task1的任务明细 , 交作业的参考文章 \n在项目letsmove/mover/0x-wen 目录下更新readme.md\n在readme.md 中需要填写sui地址,建议使用sui插件钱包中地址,或者okx中sui地址\n在letsmove/mover/0x-wen路径下创建 images文件夹,并上传两个图片,分别为钱包截图和浏览器搜索截图\n完成后提交本地代码,同步远程最新代码后,全部操作完成后提交PR\n\n操作步骤\n完成Sui CLI安装, 根据官方文档安装\n# 安装完成后先生成一个地址# 查看,首次则一直回车,如果提示加密方式就选择 0 sui client addresses # 创建 {ed25519 | secp256k1 | secp256r1} 不同的加密方式sui client new-address ed25519 \n\n\n\n完成获取测试网络SUI学习,主要是领取测试网代币\ncurl --location --request POST 'https://faucet.testnet.sui.io/gas' \\--header 'Content-Type: application/json' \\--data-raw '{ "FixedAmountRequest": { "recipient": "替换成本地地址" }}'\t\n\n\n\nHello Move 合约必须包含自己github id 输出内容是 github id\n# 切换到项目路径:letsmove/mover/0x-wen/code # 创建一个项目sui move new hello# 项目结构$ tree hellohello├── Move.toml├── sources│ └── hello.move└── tests └── hello_tests.move\n\n// 更改hello.move 代码module hello::hello { use std::ascii::{String, string}; use sui::object::{Self,UID}; use sui::transfer::transfer; use sui::tx_context::{TxContext, sender}; public struct Hello has key{ id:UID, say: String } fun init(ctx: &mut TxContext) { let hello_move = Hello { id:object::new(ctx), say: string(b"0x-wen"), // 替换github }; transfer(hello_move, sender(ctx)); }}\n\n\n\n执行部署命令\n$ sui move build $ sui client publish # 部署合约\n\n\n\n提交Hello Move合约发布package id\nPublished Objects: \tPackageID: 0xdecabd09762d6a7ed908127b170ce358f80b2d6b1932cbf0845b8731d1802033 \tVersion: 1 \tDigest: DaUN8sMqky89FvzZvsrJVdVYmGabwkw4KdnkyF1bdXvF \tModules: hello \n\n\n\n拿着PackageID 去测试网浏览器【注意切换网络】 搜索一下并截图\n\n提交安装好浏览器钱包的截图到 images 目录下\n\n\ncreate-dapp\n创建一个可以和合约交互的前端应用\n\n通过手脚架生成项目结构# 路径: 0x-wen/code/npm create @mysten/dapp# 选择 e2e-counter# 创建一个名称为 hello_dapp\n\n更改相关配置后部署# 更新默认生成的toml文件vim 0x-wen/code/hello_dapp/move/counter/Move.toml# 将依赖分支更改为测试分支rev = "framework/testnet" # 执行部署cd 0x-wen/code/hello_dapp/move/countersui client publish\n\n替换部署成功后的package-id// 0x-wen/code/hello_dapp/src/constants.tsexport const TESTNET_COUNTER_PACKAGE_ID = "0x54fae9c956cc1a2ef3f1436e1fa008a7230d7f79070c58bfc070c5d893315483"; // 替换部署成功后的package\n\n下载依赖并本地启动# 路径:0x-wen/code/hello_dappnpm install # 本地启动npm run dev# 先在sui-wallet中切换到测试网络,在打开本地页面选择连接wallet,然后使用页面完成交互和发送交易\n\n\n\n新建项目sui move new my_project\n\n编译项目Move 是一种编译语言,需要将源文件编译为 Move Bytecode,它仅包含有关模块、其成员和类型的必要信息,不包括注释和一些标识符(例如常量)。\nMove Compiler 会创建一个文件夹,其中放置所有获取和编译的依赖项以及当前包模块的字节码。生成的文件应该被添加到.gitignore\nsui move build \n\n运行测试sui move testsui move test test_hello # 指定运行某个或多个测试\n\n部署脚本todo-list 复制链接中的代码,并按操作部署到测试网\nsui client publish --json\n\n合约交互# 找到合约部署后packageId"type": "published","packageId": "0x102f24e8218fa6fb837137ada5a0ba30bac96e30b085bd6de99a11727b794a79",# 准备变量export PACKAGE_ID=0x102f24e8218fa6fb837137ada5a0ba30bac96e30b085bd6de99a11727b794a79export MY_ADDRESS=$(sui client active-address)# 调用合约sui client ptb \\--gas-budget 100000000 \\--assign sender @$MY_ADDRESS \\--move-call $PACKAGE_ID::todo_list::new \\# 将new函数的返回值存储至 list 中--assign list \\# 将 list 对象传输给 sender--transfer-objects "[list]" sender# 执行成功后得到交互的响应信息,ObjectID类似一个TodoList的实例,拥有其所有权并可以调用其相关方法Created Objects: ObjectID: 0x3ac554c410b36c309313eced79e6d86e251637b80ab54180c4d44b86c0538064 Sender: 0x7074c1cfb8327dcefcf6f44d14d1747bd24859df02a8ba2f31eaf6504385cf79 Owner: Account Address ( 0x7074c1cfb8327dcefcf6f44d14d1747bd24859df02a8ba2f31eaf6504385cf79 ) ObjectType: 0x102f24e8218fa6fb837137ada5a0ba30bac96e30b085bd6de99a11727b794a79::todo_list::TodoList # 配置LIST_IDexport LIST_ID=0x3ac554c410b36c309313eced79e6d86e251637b80ab54180c4d44b86c0538064# 添加一个任务sui client ptb \\--gas-budget 100000000 \\--move-call $PACKAGE_ID::todo_list::add @$LIST_ID "'0'"# 查看sui client object $LIST_ID# 添加三个并删除两个, 验证同一个事务按下标删除sui client ptb \\--gas-budget 100000000 \\--move-call $PACKAGE_ID::todo_list::add @$LIST_ID "'1'" \\--move-call $PACKAGE_ID::todo_list::add @$LIST_ID "'2'" \\--move-call $PACKAGE_ID::todo_list::add @$LIST_ID "'3'" \\--move-call $PACKAGE_ID::todo_list::remove @$LIST_ID 0 \\--move-call $PACKAGE_ID::todo_list::remove @$LIST_ID 0 # 删除多个的场景下会有错误,目前还没有找到啥原因# 复现步骤:# 1.单独添加0 2.单独添加1 3.合并 删除0和1sui client ptb \\--gas-budget 100000000 \\--move-call $PACKAGE_ID::todo_list::remove @$LIST_ID 0 \\--move-call $PACKAGE_ID::todo_list::remove @$LIST_ID 1 \\--move-call $PACKAGE_ID::todo_list::remove @$LIST_ID 2 \\--move-call $PACKAGE_ID::todo_list::remove @$LIST_ID 3 # Error executing transaction: Failure error: "MoveAbort(MoveLocation { module: ModuleId { address: 0000000000000000000000000000000000000000000000000000000000000001, name: Identifier(\\"vector\\") }, function: 14, instruction: 11, function_name: Some(\\"remove\\") }, 131072) in command 1"\n\n\n相关概念包包由模块组成,包发布到区块链上并由地址标识。\n\n调用其函数的事务来与已发包的包交互\n可以作为其他包的依赖,例如:自己编写的包可以依赖官方包\n\n已发布的包\n开发过程中包没有地址,一般需要设置为 0x0 \n已发布的包会有一个链上唯一地址,其中包含其模块的字节码,已发布的包不可变,并且可以通过发送交易与其交互\n\n模块\n模块包含函数、类型、常量、变量等\n模块可以引入其他依赖包, 以todolist为例:\n\nmodule todo_list :: todo_list定义模块 包/address :: 模块名称# 引入其他依赖包, std包/string模块/String类型 use std::string::String;# 定义结构体public struct TodoList has key, store { id: UID, items: vector<String>}# 定义函数public fun new(ctx: &mut TxContext): TodoList\n\nMove.toml\n[package]: 包信息\nname: 导入时的包名称\nversion: 包的版本号\nedition: Move的版本\n\n\n[dependencies]: 依赖包\noverride = true, 解决版本冲突问题\ngit 指定github仓库地址, local = path 指定本地路径\nsubdir = “remote/path”\nrev = “main” 指定分支\n\n\n[addresses]: 地址\n部分用于添加地址的别名,可以在此指定任意地址,然后在代码中用作别名\n\n\n\nAddress\n地址是区块链上位置的唯一标识符。它用于识别包、帐户和对象, 地址的固定大小为32字节,通常使用16进制表示\n\n长度为 64 个字符(32 个字节),并以 0x 为前缀\n>>> len("0x04bde040cca2b5fec3c47a30cdeb3e11f011395c49886c91629b7dbd4713b319")66>>> len("04bde040cca2b5fec3c47a30cdeb3e11f011395c49886c91629b7dbd4713b319")64 \n\n标准库的地址是 0x1 。小于 32 字节的地址在左侧用零填充。\n0x1 = 0x0000000000000000000000000000000000000000000000000000000000000001 # Sui 标准库的地址(别名 std )0x2 - Sui 框架的地址(别名 sui )0x5 - Sui System 对象的地址0x6 - 系统 Clock 对象的地址0x8 - 系统 Random 对象的地址0x403 - DenyList 系统对象的地址\n\nAccount\n账户是识别用户的一种方式,账户由私钥生成并由地址标识。\n账户可以拥有对象,并且可以发送交易,每笔交易都有一个发送者(地址)\nSui支持多种密码算法生成账户,ed25519, secp256k1, zklogin\n\nTransaction\n与链交互的唯一方式,发起交易用于改变区块链的状态。\n\n\n交易结构\n\nsender: 交互的操作者是谁\nlist (or a chain) of commands: 要执行的操作\ncommand inputs: 简单类型值也可以是object\ngas object: 用于支付gas费用对象\ngas price and budget: 预估交易成本\n\n\ncommands\n\n交易由多个命令组成,并按照它们列出的顺序执行。并且可以使用之前命令的结果\n交易整体作为一个事务,要么全部成功,要么失败 Inputs:- sender = 0xa11ceCommands:- payment = SplitCoins(Gas, [ 1000 ])- item = MoveCall(0xAAA::market::purchase, [ payment ])- TransferObjects(item, sender)\n\n\nSplitCoins - 一个内置命令,用于从传递的对象(在本例中为 Gas 对象)中分离出新硬币\nMoveCall - 使用给定参数调用包 0xAAA 、模块 market 中的函数 purchase 的命令 - 给定参数 payment object\nTransferObjects - 将对象传输给接收者的内置命令\n\n\n交易结果\n\n\n交易对区块链状态的改变,可以通过以下方式更改状态:\n\n使用 gas object 来支付交易\n创建、更新和删除对象\n记录事件\n\n交易结果的组成:\n\nTransaction Digest: 交易摘要 - 交易的哈希值,用于识别交易\nTransaction Data: 交易数据 - 交易中使用的输入、命令和气体对象\nTransaction Effects: 交易效果 - 交易的状态和“效果”,更具体地说:交易的状态、对象及其新版本的更新、使用的气体对象、交易的气体成本以及交易发出的事件\nEvents: 事件 - 交易发出的自定义事件\nObject Changes: 对象更改 - 对对象所做的更改,包括所有权的更改\nBalance Changes: 余额变化 - 交易涉及的账户总余额的变化\n\n基础类型变量// 格式如下:let <variable_name>[: <type>] = <expression>;let mut <variable_name>[: <type>] = <expression>;let x: bool = true;let mut y: u8 = 42;y = 43;let x: u8 = 42; // shadowed\n\n布尔值// 无需显式指定类型 - 编译器可以从值中推断出类型let x = true;let y = false;\n\n整数类型let x: u8 = 42;let y: u16 = 42;// ...let z: u256 = 42;// Both are equivalentlet x: u8 = 42;let x = 42u8;\n\n整数溢出let x: u8 = 255;let y: u8 = 255;let z: u16 = (x as u16) + ((y as u16) * 2);let x = 255u8;let y = 1u8;// This will raise an errorlet z = x + y;\n\n运算符// 加法、减法、乘法、除法和余数// + | - | * | / | %\n\n地址类型// address 类型。它是一个 32 字节的值,可用于表示区块链上的任何地址。// 地址以两种语法形式使用:前缀为 0x 的十六进制地址和命名地址。// address literallet value: address = @0x1;// named address registered in Move.tomllet value = @std;let other = @sui;\n\n地址文字以 @ 符号开头,后跟十六进制数字或标识符。十六进制数被解释为 32 字节值。\n地址转换由于地址类型是 32 字节值,因此可以将其转换为 u256 类型,反之亦然。它还可以与 vector<u8> 类型相互转换。\n\n示例:将地址转换为 u256 类型并返回。\n\nuse sui::address;let addr_as_u256: u256 = address::to_u256(@0x1);let addr = address::from_u256(addr_as_u256);\n\n\n示例:将地址转换为 vector<u8> 类型并返回。\n\nuse sui::address;let addr_as_u8: vector<u8> = address::to_bytes(@0x1);let addr = address::from_bytes(addr_as_u8);\n\n\n示例:将地址转换为字符串。\n\nuse sui::address;use std::string::String;let addr_as_string: String = address::to_string(@0x1);\n\n语句与表达式最后一行是表达式将用于赋值,是语句将返回一个默认单元类型 () \n// block with an empty expression, however, the compiler will// insert an empty expression automatically: `let none = { () }`// let none = {};// block with let statements and an expression.let sum = { let a = 1; let b = 2; a + b // last expression is the value of the block};// block is an expression, so it can be used in an expression and// doesn't have to be assigned to a variable.{ let a = 1; let b = 2; a + b; // not returned - semicolon. // compiler automatically inserts an empty expression `()`};\n\n结构体数据合集,用什么来表示长方形的长和宽。Move 不支持递归结构,这意味着结构不能将自身包含为字段。\npublic struct Artist { name: String, \tage: u8,}\n\n\n\n函数调用add 的返回值是一个表达式 a+b,返回类型是u8\nfun add(a: u8, b: u8): u8 { a + b}#[test]fun some_other() { let sum = add(1, 2); // add(1, 2) is an expression with type u8}\n\n流程控制它们也是表达式,因此它们返回一个值。\n// if is an expression, so it returns a value; if there are 2 branches,// the types of the branches must match.if (bool_expr) expr1 else expr2;// while is an expression, but it returns `()`.while (bool_expr) { expr; };// loop is an expression, but returns `()` as well.loop { expr; break };\n\n\n\n\n\n\n\n\n\n\n\n","categories":["blockchain"],"tags":["move"]},{"title":"区块链基础","url":"/blog/2024/05/08/0-wallet/00-%E5%8C%BA%E5%9D%97%E9%93%BE%E5%9F%BA%E7%A1%80/","content":"\n文章将使用问答的形式进行区块链基础知识的讲解,同时满足学习和面试需求\n\n1.什么是区块链?区块链是一种分布式的、去中心化的数字账本技术,用于记录和验证交易与数据。区块链的核心概念是将交易和数据记录在一个被称为”区块”的数据结构中,并通过加密和共识算法保证数据的安全性和一致性。每个区块包含了一批交易的信息,以及与之前区块的链接,形成了一个链式结构。\n2.区块链有什么特点?在区块链中,参与者通过网络共享和维护这个账本的副本,而不是依赖于中央机构来验证交易。这种去中心化的特性使得区块链具有以下特点:\n\n透明性:区块链中的交易和数据是公开可见的,任何人都可以查看账本的副本。这提供了一种透明的方式来验证交易和监督系统的运作。\n安全性:区块链使用密码学技术对数据进行加密和验证,从而确保数据的安全性和完整性。由于数据存储在多个节点上,篡改数据变得非常困难。\n去中心化:区块链没有中央机构或控制权,参与者通过共识算法来达成对账本的一致认可。这使得区块链具有更高的可信度和抗攻击能力。\n不可篡改性:一旦数据被记录在区块链上,由于每个区块都包含了前一个区块的哈希值,任何对数据的篡改都将被其他节点检测到。\n\n3.区块链的运作方式3.1 哈希函数理想的加密哈希函数具备5个主要属性:\n\n确定性:相同的消息总是产生相同的哈希值。\n快速:快速计算任何给定消息的哈希值。\n抵抗性:除非尝试所有可能的消息,否则从其哈希值生成消息是不可行的。\n不相关:对消息的微小更改会极大地改变哈希值,以至于新值与旧值没有关系。\n抗碰撞:不可能找到具有相同哈希值的两个不同消息。\n\n3.2 区块、链\n有效区块是一组有序的交易。\n每个块都包含前一个块的哈希值。 \n区块的哈希值具有易证明难伪造。\n\n区块链接方式如下图示例:\n\n\n4.交易的运作方式4.1 加密钱包\n钱包并不存储加密资产,它们只存储私钥\n\n先理解三个重要概念:\n\n私钥、公钥、地址:公私钥通过非对称加密生成,公钥用于分发,私钥用于个人保留,地址通过公钥推导\n加密与解密:消息传递通过公钥加密、私钥解密\n签名与验证:交易信息通过私钥签名、公钥验证签名\n\n\n\n4.2 以BTC为例,讲解一笔交易流程\n用户B转账 1 bitcoin 至用户A\n\n\n\n\n用户B使用自己的私钥签名完成交易信息【确认用户B有足够的代币】\n将交易信息发送至节点,节点使用公钥验证交易信息,并存储在内存池中。\n节点将交易分组在区块中,并广播到其他节点。\n共识成功并出块后,用户A才能获取到1 bitcoin。\n\n5.共识5.1 工作量证明(PoW)\n去中心化程度高,消耗资源高在 PoW 共识中,用户必须完成任意难度的任务,当与块中的有序交易结合时,会产生符合特定标准的哈希函数结果。\n\n\n称为“矿工”的节点通过暴力竞争来解决数学问题(找到特定规则的哈希值)\n第一个解决问题的矿工可以创建一个区块\n其他节点检查该块是否有效。如果是,矿工将获得加密货币奖励。如果不是,矿工就浪费了时间和精力。\n所有的节点都将新块添加到其区块链副本中。\n\n5.2 股权证明 (PoS)\n去中心化的一种妥协方案,更像选择代表人参与网络,相比工作量证明大大降低资源消耗\n\n\n称为验证者节点会抵押一些加密货币。投入一定数量的加密货币来参与和分配出块奖励。\n验证者节点所占的权重越高更有可能被选择来处理交易并创建区块。\n其他验证者节点检查该块是否有效。如果是,所有参与的验证者都会赚取收益。如果不是,创建该块的验证者可能会失去其权益或者获得惩罚。\n\n","categories":["wallet"],"tags":["blockchain"]},{"title":"分布式基础理论","url":"/blog/2024/05/13/0-wallet/01-%E5%88%86%E5%B8%83%E5%BC%8F%E7%B3%BB%E7%BB%9F%E5%9F%BA%E7%A1%80%E7%90%86%E8%AE%BA/","content":"1.分布式基础理论1.1 CAP理论是分布式系统的基石Consistency (一致性):\n“all nodes see the same data at the same time”,即更新操作成功并返回客户端,所有节点在同一时间的数据完全一致,一致性的问题在并发系统中不可避免。\n对于客户端,一致性指的是并发访问时更新过的数据如何获取。\n对于服务端,则是更新如何复制分布到整个系统,以保证数据一致。\nAvailability (可用性):\n“Reads and writes always succeed”,即服务一直可用,而且是正常响应时间。良好的可用性是指系统能够很好的为用户服务,不出现用户操作失败或者访问超时等用户体验不好的情况。\nPartition Tolerance (分区容错性):\n即分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。\n比如现在的分布式系统中有某一个或者几个机器宕掉了,其他剩下的机器还能够正常运转满足系统需求,对于用户而言并没有什么体验上的影响。\n1.2 三者只能取其二\nCA without P: 如果不要求P(不允许分区),则保留C(强一致性)和A(可用性),但放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,这是有违背分布式系统的设计初衷。\nCP without A: 如果不要求A(可用),强调各服务之间数据强一致性,而P(分区)会导致同步时间无限延长(等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验。\nAP wihtout C: 要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,这样会导致全局数据的不一致性。\n\n1.3 Base理论BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写\nBASE是对CAP中一致性和可用性权衡的结果,核心思想是:即使无法做到强一致性,采用适当的方式使系统达到最终一致性。\nBasically Available: 基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性,但不等价于系统不可用。\n\n秒杀的场景下用户数量暴增,为保护系统稳定性,有部分用户可能被引导至一个降级页面\n\nSoft state: 允许系统中的数据存在中间状态,并认为中间状态不会影响系统整体可用性,即允许系统在不用节点之间数据同步存在延时\nEventually consistent: 强调所有的数据副本在经过一段时间同步后,最终能够达成一致的状态。最终保证一致,而不是实时保证系统一致性。\nBASE理论面向的是大型高可用可扩展的分布式系统,和传统的事物ACID特性是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。但同时,在实际的分布式场景中,不同业务单元和组件对数据一致性的要求是不同的,因此在具体的分布式系统架构设计过程中,ACID特性和BASE理论往往又会结合在一起。\n2.区块链不可能三角区块链通常在安全性、去中心化和可扩展性之间进行权衡:\n\n比特币:重点放在安全性和去中心化上,因此导致比特币的可扩展性受限,因为比特币的区块大小和区块时间设定限制了它的交易处理能力。在需求高的时候就会导致交易延迟和手续费上涨。\n以太坊:重点也放在安全性和去中心化上,并作为智能合约平台,它需要确保用户的资产和协议是安全的。因此以太坊的可扩展性较差,尤其当DeFi和NFT的应用量增大时,交易延迟与费用上涨的问题尤其突出。\nSolana:主要关注可扩展性因此在处理速度和吞吐量上都非常高效,为了达到这样的性能,在网络的中心化程度作出了一些妥协,例如限制验证者的数量。\n\n3.概率最终性和逐块最终性概率最终性:在比特币网络中,由于采用了Proof-of-Work的共识算法。尽管一个区块被添加到区块链上,但仍然存在一定的概率(尽管很小),在后续的区块中被另一个竞争者的区块所取代。因此,对于比特币网络来说,一个最新的区块在一段时间后可以被认为具有概率最终性,即随着更多的区块被添加到区块链上,该区块被替代的概率会逐渐减小。\n逐块最终性:对于Cosmos网络,它采用了Tendermint共识算法,该算法通过验证人的投票来确定下一个区块的生成者。一旦在Cosmos网络中的一个区块被验证人成功生成并广播出去,它被认为是最终的结果,不会被后续的区块替代。因此,可以说Cosmos网络具有逐块最终性,即一旦一个区块在网络中被验证人确认,它就成为最终的结果。\n","categories":["wallet"],"tags":["blockchain"]},{"title":"UTXO和Account模型对比","url":"/blog/2024/05/21/0-wallet/03-UTXO%E5%92%8CAccount%E6%A8%A1%E5%9E%8B%E5%AF%B9%E6%AF%94/","content":"在区块链中常见的两种记录模式:UTXO(Unspent Transaction Output,未花费的交易输出)模型和Account(账户)模型\n1.UTXO模型:区块链中的现金类比在基于 UTXO 的区块链中,加密货币账户余额只是拥有的 UTXO 的总和,如实体钱包中拥有不同面额的钞票一样。\n没有身份的概念,只有关联到不同钱包地址的 UTXO 或者说 “未花费的代币”。\n比特币协议不追踪用户的余额,而是追踪 UTXO 以及这些 UTXO 分别属于哪些地址。\n2.采用UTXO模型的交易步骤\n确定交易金额:发送方确定要发送的加密货币金额。\n\n选择UTXO:发送方的钱包查找足够覆盖交易金额的UTXO。这些UTXO是之前收到但尚未花费的交易输出。\n\n创建交易:钱包创建一笔新的交易,这笔交易包含以下部分:\n\n输入(Inputs):选择的UTXO列表,这些将被用于支付交易金额。\n输出(Outputs):至少包含两个输出,一个是支付给接收方的金额,另一个是找零(如果原始UTXO的总和大于所需支付的金额)。\n交易费(Transaction Fee):为了激励矿工将交易纳入区块链,发送方需要支付一定的交易费。\n\n\n签署交易:发送方使用与其钱包地址相关联的私钥对交易进行数字签名。这个签名证明了交易是由拥有私钥的用户发起的。\n\n广播交易:签署后的交易被广播到区块链网络中,网络中的节点会接收这笔交易。\n\n节点验证:网络中的节点接收到交易后,会进行验证,确保:\n\n交易格式正确。\n输入的UTXO是有效的,且未被花费过。\n签名与UTXO的公钥匹配。\n交易费用合理。\n\n\n交易池(Mempool):验证通过的交易会被放入节点的交易池(mempool)中,等待被矿工打包进区块。\n\n矿工打包交易:矿工从交易池中选择交易进行打包,创建一个新的区块。\n\n区块链更新:当新区块被成功挖出并添加到区块链上时,交易被认为是确认的。此时,UTXO集会更新:\n\n交易中使用的输入UTXO从UTXO集中移除。\n新创建的输出成为新的UTXO,添加到UTXO集中。\n\n\n交易确认:随着区块被添加到区块链上,交易得到网络的确认。\n通常需要多个后续区块(即几个确认)来增加交易的安全性。\n\n1个确认:交易已经被打包进最新的区块,但仍然存在被回滚(reorg)的风险,尤其是当矿工算力非常集中时。\n3个确认:交易被认为相对安全,因为它需要重新计算3个区块的工作量证明才能被更改。\n6个确认:这通常被认为是一个较高的安全标准,需要更多的时间和资源来回滚交易。\n\n在一些高价值交易或对安全性有更高要求的场景中,可能会等待更多的确认来确保交易不可逆。\n\n接收方确认:\n\n接收方或其钱包会检查区块链,确认交易已经被纳入区块并且有足够的确认。\n\n\n\n3.UTXO模型优缺点优点:\n提高隐私性:由于每次交易都可以使用新的地址,UTXO模型提供了较高的隐私保护,使得追踪用户的资金流向更加困难。\n支持并行处理:UTXO模型允许区块链网络并行处理交易,因为每个交易的验证不依赖于其他交易,这有助于提高交易吞吐量。\n无状态性:UTXO模型是无状态的,意味着节点不需要维护一个全局的状态表,这使得系统更加简洁和易于维护。\n\n缺点:\n缺乏可编程性:UTXO模型本身不支持复杂的脚本和智能合约功能,限制了区块链的可编程性和灵活性。\n交易灵活性降低:在UTXO模型中,每笔交易都需要精确地花费一个或多个UTXO,并产生找零,这增加了交易的复杂性。\n\n4.Account模型:区块链中的银行账户类比最初由以太坊采用,类似于传统银行账户的处理方式。在这种模型中,交易直接反映在用户账户的余额变化上。\n在账户余额上做加减操作,由网络节点来维护用户余额的分布式账本,存在身份的概念通常于与账户或者地址关联\n5.采用Account模型的交易步骤\n创建交易:发送方决定交易的接收方地址和要发送的金额。\n交易签名:发送方使用他们的私钥对交易进行签名,以证明身份并授权这笔交易。\n交易广播:签名后的交易被发送到区块链网络中的节点。\n交易验证:网络中的节点接收到交易后,会进行一系列验证,包括检查签名的有效性、确保发送方账户有足够的余额以及交易是否符合其他网络规则。\n交易费用计算:根据交易的复杂性和大小,计算所需的交易费用。\n交易池存储:验证通过的交易被存储在节点的交易池(mempool)中,等待被打包进区块。\n区块打包:矿工或验证者从交易池中选择交易,并将其打包进新的区块。\n共识机制:通过工作量证明(Proof of Work)或权益证明(Proof of Stake)等共识机制,新区块被添加到区块链中。\n状态更新:当新区块被确认后,账户的状态(余额和非货币属性)会根据交易内容进行更新。\n交易确认:交易被网络中的其他节点确认,并且随着区块的增加,交易的确认深度也会增加,从而提高交易的安全性。\n交易完成:一旦交易被确认并且相应的状态变更被记录在区块链上,交易就被认为是完成的。\n\n6.Account模型优缺点优点:\n易于理解:Account模型类似于传统的银行账户系统,用户容易理解账户余额和交易的概念。\n\n可编程性:Account模型通常支持图灵完备的智能合约,允许开发者编写复杂的逻辑和去中心化应用。\n\n简化的交易结构:交易通常只需要包含发送者、接收者、金额和数据,结构相对简单。\n\n\n缺点:\n状态存储需求:Account模型需要存储每个账户的状态,随着用户数量的增加,状态数据可能会迅速增长。\n隐私性:由于账户地址与所有交易直接关联,可能更容易分析和追踪用户的行为模式。\n网络拥堵:Account模型通常需要按顺序处理交易,这在高需求时可能导致网络拥堵。\n\n","categories":["wallet"],"tags":["blockchain"]},{"title":"探讨事务一致性","url":"/blog/2024/05/16/0-wallet/02-%E6%8E%A2%E8%AE%A8%E4%BA%8B%E5%8A%A1%E4%B8%80%E8%87%B4%E6%80%A7/","content":"1.事务的ACID特性在数据库中,“事务”是由多个操作构成的序列。广义上的事务一致性具化到了原子性、一致性、隔离性和持久性这4个方面。\n\n原子性(Atomicity):指事务是一个不可分割的操作单元,要么全部执行成功,要么全部失败回滚。如果事务中的任何一个操作失败,整个事务将回滚到最初的状态。\n一致性(Consistency):指事务将数据库从一个一致状态转变为另一个一致状态。在事务开始和结束时,数据库必须满足预定义的一致性规则,以保持数据的有效性和完整性。\n隔离性(Isolation):指每个事务的执行应该与其他事务的执行相互隔离,以防止彼此干扰。隔离级别定义了事务之间的隔离程度,包括已提交读、可重复读、快照隔离和可串行化。\n持久性(Durability):指一旦事务提交,其结果就应该永久保存在数据库中,即使系统发生故障也不会丢失。数据库通过将事务日志持久化到磁盘来实现持久性。\n\n\n1.1 原子性和一致性的差异原子性关注事务内部状态,要不全部成功,要不全部失败\n一致性关注数据可见性,中间状态的数据对外不可见,只有最初状态和最终状态的数据对外可见\n1.2 持久性核心思想即应对系统故障\n目的都是保证在本地节点之外,至少有一份完整的日志可用于数据恢复。\n\n存储硬件无损、可恢复故障: 主要依托于预写日志(Write Ahead Log, WAL)保证第一时间存储数据。WAL采用顺序写入的方式,可以保证数据库的低延时响应。\n存储硬件损坏、不可恢复的故障: 需要用到日志复制技术,将本地日志及时同步到其他节点。\n\n单体数据库自带的同步或半同步的方式,其中半同步方式具有一定的容错能力\n日志存储到共享存储系统上,后者会通过冗余存储保证日志的安全性\n基于Paxos/Raft的共识算法同步日志数据,在分布式数据库中被广泛使用\n\n1.3 隔离性分为多个隔离级别\n较低的隔离级别就是在正确性上做妥协,将一些异常现象交给应用系统的开发人员去解决,从而获得更好的性能。\n\n最早、最正式的对隔离级别的定义,是ANSI SQL-92(简称SQL-92),它定义的隔离级别和异常现象如下所示:\n\n1.4 更严谨的隔离级别参考论文“A Critique of ANSI SQL Isolation Levels”(简称Critique)\n定义了六种隔离级别和八种异常现象\n\n\nSQL-92中可重复读与可串行化两个隔离级别的主要差别是对幻读的处理,是不是解决了幻读就是可串行化\nCritique的发表,快照隔离能解决幻读的问题,但却无法处理写倾斜(Write Skew)问题,也不符合可串行化要求\n\n对比总结到今天,使用最广泛的隔离级别为四种:已提交读、可重复读、快照隔离、可串行化\n1.5 异常现象不可重复读和幻读的差异在语义上接近:都是在一个事务内用相同的条件查询两次,但两次的结果不一样\n不可重复读:第二次的结果集相对第一次,有些记录被修改(Update)或删除(Delete)了\n幻读: 第二次结果集里出现了第一次结果集没有的记录(Insert),幻读是在第一次结果集的记录“间隙”中增加了新的记录\n1.6 快照隔离 && 可重复读\n两者在强度上并列,已提交读弱于这两者,大多数数据库支持的隔离级别就在这三者之中\n\n快照隔离的实现基础则是多版本并发控制(MVCC),用于更高效地实现乐观或悲观并发控制。\n快照隔离存在写倾斜的问题:\n\n两个事务T1、T2同时针对10个变量赋值\nT1将10个变量赋值A,T2将10个变量赋值B,在多事务并行下得到结果将是5个A和B\n\n根据可串行化的定义,“多事务并行执行所得到的结果,与串行执行(一个接一个)完全相同”。\n实现可串行化:先执行T1再执行T2,10个变量都会变成A;调换T1与T2的顺序,则10个变量都是B。\n1.7 数据一致性和事务一致性\n数据一致性关注的是单对象、单操作在多副本上的一致性\n事务一致性则是关注多对象、多操作在单副本上的一致性\n\n分布式数据库的一致性是数据一致性与事务一致性的融合。\n分布式数据“强一致性”的含义,意味着要实现严格串行化,同时也带来性能上的损失。\n降低隔离级别则是在正确性上做妥协,将异常给应用开发处理,从而获得更好的性能。\n","categories":["wallet"],"tags":["blockchain"]},{"title":"位、字节、字符与编码","url":"/blog/2024/07/01/0-wallet/04-%E4%BD%8D%E3%80%81%E5%AD%97%E8%8A%82%E3%80%81%E5%AD%97%E7%AC%A6%E4%B8%8E%E7%BC%96%E7%A0%81/","content":"位(Bit)、字节(Byte)、字符(Character)和编码(Encoding)基础概念讲解位(Bit):\n位是计算机存储和处理信息的最小单位,表示一个二进制数字,可以是0或1。\n位是构建更大数据单位的基础。\n\n字节(Byte):\n字节是由8个位组成的数据单位,是计算机存储的基本单位。\n1字节 = 8位。\n\n字符(Character):\n字符是文本的基本单位,代表字母、数字、标点符号等。\n字符的存储大小取决于所使用的编码系统。例如,在ASCII编码中,一个英文字符占用1个字节;而在UTF-8编码中,英文字符占用1个字节,而中文字符可能占用3个字节。\n\n编码(Encoding):\n编码是将字符映射到字节序列的规则或标准。\n不同的编码方式可以支持不同数量的字符和语言。例如:\nASCII编码:使用7位(实际上是8位,但最高位未使用)来表示字符,可以表示128个不同的字符,主要支持英语字符。\nUTF-8编码:是一种变长编码方式,可以使用1到4个字节来表示一个字符,支持世界上几乎所有的书写系统。\n\n\n\n","categories":["wallet"],"tags":["blockchain"]},{"title":"密码学基础与常用算法","url":"/blog/2024/05/28/0-wallet/05-%E5%AF%86%E7%A0%81%E5%AD%A6%E5%9F%BA%E7%A1%80%E4%B8%8E%E5%B8%B8%E7%94%A8%E7%AE%97%E6%B3%95/","content":"对称加密\n定义:使用同一把密钥进行数据的加密和解密。\n优点:\n加密和解密速度快。\n算法公开,计算量小。\n适合大量数据的加密。\n\n\n缺点:\n密钥分发问题,需要安全地分配给通信双方。\n密钥管理负担,多人协作每次通信可能需要不同的密钥。\n\n\n\n常用算法:DES、3DES、AES、TDEA、Blowfish\nDES (Data Encryption Standard):\n早期广泛使用的对称加密标准。\n由于其较短的密钥长度(56位有效密钥长度),目前已被认为是不安全的,容易受到暴力破解攻击。\n\n\n3DES (Triple DES):\nDES的改进版,通过三次应用DES加密算法来提高安全性。\n有两种常见的密钥长度:112位(使用两个密钥)和168位(使用三个独立的密钥)。\n尽管仍然被一些旧系统使用,但随着计算能力的提升,3DES的安全性也在逐渐降低。\n\n\nAES (Advanced Encryption Standard):\n目前最广泛使用的对称加密标准,于2001年被选为新的加密标准。\n提供128、192和256位的密钥长度。\n被认为非常安全,至今没有被实际破解。\n\n\nTDEA (Triple Data Encryption Algorithm):\n与3DES类似,也是通过三次应用加密算法来提高安全性。\n相较于3DES,TDEA使用的是不同的算法和密钥调度,但安全性和3DES相似。\n\n\nBlowfish:\n一种可变长度的块加密算法,设计用于替代DES。\n密钥长度可变,最大支持448位。\n目前被认为是安全的。\n\n\n\n安全性总结:\nDES 已被破解,不再安全。\n\n3DES 和 TDEA 的安全性较低,容易受到现代计算能力的攻击。\n\nAES、 Blowfish 目前被认为是安全的,广泛用于保护数据。\n对于需要长期安全性的应用,建议使用AES-256,因为它提供了最高的密钥长度和强大的安全性保证。\n\n应用场景:适用于数据加密和消息认证,当通信双方都可信赖且密钥分发安全时。\n\n\n非对称加密\n定义:使用一对密钥,使用方式一 公钥加密私钥解密,使用方式二私钥签名公钥验签。\n优点:\n解决了密钥分发问题,公钥可以公开。\n支持数字签名和身份验证。\n安全性更高,即使公钥被获取也无法解密数据。\n\n\n缺点:\n加密和解密速度慢。\n算法复杂,计算量大。\n\n\n\n常用算法:RSA、ECC(椭圆曲线加密算法)、Rabin、D-H\nRSA:\nRSA是目前最广泛使用的非对称加密算法之一,它的安全性基于大数分解的难题\n随着量子计算的发展,RSA的安全性可能会受到威胁,但目前对于经典计算机来说仍然是安全的。\n密钥长度至少为2048位被认为是安全的。\n\n\nECC (Elliptic Curve Cryptography):\n基于椭圆曲线数学的密码学,提供了与RSA相同的安全级别,但需要较短的密钥长度。\n适用于移动设备和高安全环境中,因为计算需求和性能开销较低。\n目前没有已知的有效的量子算法可以在合理时间内破解ECC。\n\n\nRabin:\nRSA的一个变种,也基于大数分解问题。\n加密和解密过程比RSA更简单,但加密后的密文更长。\n与RSA具有相似的安全性。\n\n\nD-H (Diffie-Hellman):\n一种密钥交换协议,而非加密算法。\n允许两个通信方在不安全的通道上生成共享密钥。\n基于离散对数问题的困难性。\nD-H本身不用于加密数据,常与对称加密算法结合使用。\n\n\n\n安全性总结:\nRSA 和 Rabin 的安全性依赖于大数分解问题,目前对于经典计算机来说是安全的,但可能受到量子计算的威胁。\nECC 提供了与RSA相当的安全性,但需要较短的密钥长度,对量子攻击的抵抗力也较强。\nD-H 主要用于密钥交换,需要与对称加密结合来保证数据的机密性。\n\n区别:\n密钥长度:ECC可以用较短的密钥提供与RSA相同级别的安全。\n\n性能:RSA和Rabin在加密和解密操作上相对成熟和高效,ECC在签名操作上更高效。\n\n应用场景:D-H主要用于密钥交换,而RSA、ECC用于加密和数字签名。\n\n抗量子能力:ECC被认为在抵抗量子计算攻击方面比RSA有优势。\n\n应用场景:适用于开放网络环境中的通信,特别是需要身份验证和安全认证的场景。\n\n\nECDSA和EDDSA的区别\nECDSA: 使用Weierstrass 形式的椭圆曲线\n安全性:ECDSA 的安全性基于椭圆曲线离散对数问题(ECDLP),即在椭圆曲线上求解一个点的离散对数是计算上困难的。\n应用:ECDSA 被广泛应用于各种安全协议中,包括 TLS/SSL、SSH、PGP 以及多种区块链技术(例如比特币)。\n签名过程:ECDSA 签名过程涉及计算随机数、椭圆曲线上的点加运算以及模 n 的逆元运算。\n验证过程:ECDSA 的签名验证需要进行点加运算和模 n 运算。\n\nEDDSA: 使用 Edwards 曲线,提供了一些特殊的属性,如抵抗侧信道攻击。\n安全性:EDDSA 的安全性基于哈希到点(Hash-to-Curve)问题,这使得它在理论上更难以被量子计算机攻击。\n应用:EDDSA 被设计为高效且安全,适用于需要快速签名验证的场景,如加密货币(例如 Ed25519 被用于比特币以外的许多其他加密货币)。\n签名过程:EDDSA 的签名过程相对简单,不需要像 ECDSA 那样的随机数生成,这简化了实现并减少了错误和攻击面。\n验证过程:EDDSA 的签名验证同样高效,利用了 Edwards 曲线的特殊性质来快速验证签名。\n\n\n主要区别\n曲线模型:ECDSA 使用 Weierstrass 曲线,而 EDDSA 使用 Edwards 曲线。\n安全性:EDDSA 被认为在某些方面比 ECDSA 更安全,尤其是在抵抗量子攻击方面。\n性能:EDDSA 通常提供更快的签名验证速度,且实现更简单。\n随机性:ECDSA 需要安全的随机数生成器来产生每次签名的随机数 k,而 EDDSA 不需要随机数。\n侧信道攻击:EDDSA 设计上更能抵抗侧信道攻击。\n\nSecp256k1 和 Ed25519 的区别和优势Secp256k1\n曲线类型:Secp256k1 是一种基于 Koblitz 曲线的椭圆曲线,特别是 secp256r1 的一个变种。属于 ECDSA 算法家族。\n应用:Secp256k1 最著名的应用是在比特币(Bitcoin)中作为其公钥加密和数字签名的曲线。它也被以太坊(Ethereum)和其他许多区块链平台采用。\n安全性:Secp256k1 的安全性基于椭圆曲线离散对数问题(ECDLP),目前被认为是安全的,但随着量子计算技术的发展,未来可能会面临风险。\n特点:Secp256k1 的基点和曲线参数是预定义的,且该曲线提供了一个相对较高的安全级别。\n\nEd25519\n曲线类型:Ed25519 是基于 Edwards 曲线的数字签名方案,属于 EdDSA 算法家族。\n应用:Ed25519 被用于多种安全应用,包括加密货币(如 Zcash、波卡)、安全通信协议(如 Noise Protocol Framework)以及一些操作系统和软件项目(如 OpenSSH)。\n安全性:Ed25519 的安全性基于哈希到曲线问题,它对量子计算机攻击具有较高的抵抗力。\n特点:Ed25519 提供了快速、简洁和安全的签名机制,不需要随机数生成器,简化了实现并减少了潜在的漏洞。\n\n区别和优势Secp256k1:\n\n优势:\n广泛的行业接受度和使用,特别是在加密货币领域。\n经过时间检验的安全模型。\n支持多种高级加密技术和智能合约。\n\n\n考虑:\n相对于 Ed25519,可能面临更大的量子计算风险。\n实现和优化可能更复杂。\n\n\n\nEd25519:\n\n优势:\n高效且快速的签名验证过程,抵抗量子攻击,具有更好的未来安全性。\n实现简单,减少了实现错误和安全漏洞。\n不需要随机数生成,简化了签名过程。\n\n\n考虑:\n相对较新的算法,虽然已被广泛审查,但可能不如 Secp256k1 那样经过长期实践的检验。\n在某些需要与现有系统兼容的场景中可能不是首选。\n\n\n\n混合加密\n概念:结合对称加密和非对称加密的优点。\n\n优点:\n\n利用非对称加密安全地分发对称密钥。\n利用对称加密的高效率进行数据加密。\n\n\n应用场景:\n安全通信:在线通信协议(如SSL/TLS)使用混合加密来保护网络连接。非对称加密用于安全地交换对称密钥,然后使用对称加密来加密实际传输的数据。\n文件加密:在文件加密工具中,用户可以使用自己的私钥加密文件,然后使用接收者的公钥加密对称密钥,确保只有拥有私钥的接收者能解密文件。\n数字签名:在需要验证文档或软件的真实性和完整性的场景中,数字签名使用非对称加密来签署数据,使用对称加密来加密数据内容。\n\n\n混合加密是结合了两种加密方法的优势:非对称加密的密钥分发安全性和对称加密的高效率。可以在确保数据安全的同时,提高系统的整体性能。\n特定算法概述\nAES: 目前安全的对称加密算法,加密和解密速度快。\nRSA:基于大数分解的难题,适用于数据加密和数字签名。\nECC:基于椭圆曲线的离散对数问题,提供“短密钥”优势,适用于移动设备和高安全环境。\nECDSA:基于椭圆曲线的数字签名算法,广泛应用于TLS/SSL、SSH等。\nEdDSA:基于Edwards曲线,提供更好的安全性,抵抗侧信道攻击。\n\n结论\n对称加密和非对称加密各有适用场景,混合加密结合了两者的优点。\n在设计安全系统时,应根据具体需求和环境选择合适的加密技术。\n对称加密适合内部安全和大量数据加密,非对称加密适合开放网络和需要身份验证的场景。\n\n","categories":["wallet"],"tags":["blockchain"]},{"title":"Schnorr和BLS与哈希算法讲解","url":"/blog/2024/05/28/0-wallet/06-Schnorr%E5%92%8CBLS%E4%B8%8E%E5%93%88%E5%B8%8C%E7%AE%97%E6%B3%95%E8%AE%B2%E8%A7%A3/","content":"Schnorr签名算法\nSchnorr签名算法是一种基于离散对数问题的数字签名算法,由克劳斯·施诺尔(Claus-Peter Schnorr)于1989年提出。\n\n重点任务:\n\n理解Schnorr签名算法的工作原理。\n掌握算法参数的选择和密钥生成过程。\n学习如何生成和验证Schnorr签名。\n\n优点\n高效性:Schnorr签名在计算上更加高效,特别是在签名生成和验证过程中。\n短签名:Schnorr签名长度较短,有助于节省带宽和存储空间。\n安全性:基于离散对数问题的安全性,难以被破解。\n\n应用Schnorr签名广泛应用于区块链和加密货币领域,例如比特币的Taproot升级中使用了基于Schnorr签名的聚合签名技术。\n结论Schnorr签名算法以其高效和安全的特点,在对资源要求较高的应用场景中具有显著优势。\n基于有限单群的Schnorr多重数字签名算法\n结合了Schnorr签名的效率和有限单群的数学复杂性,以提供一种安全的多重签名方案。\n\n重点任务:\n\n理解有限单群的概念及其在密码学中的应用。\n掌握基于有限单群的Schnorr签名算法的参数选择和密钥生成。\n学习如何生成和验证基于有限单群的Schnorr多重签名。\n\n优点\n增强的安全性:有限单群的引入为签名方案提供了额外的保护层,使得攻击者更难以找到有效的签名。\n\n高效的签名验证:尽管基于更复杂的数学结构,该算法仍然保持了Schnorr签名的高效性,特别是在验证过程中。\n\n签名聚合:多重签名可以被聚合为单个签名,这在处理大量交易或需要多个签名者的场景中非常有用。\n\n灵活性和可扩展性:算法支持多个签名者,可以根据需要轻松扩展签名者的数量。\n\n节省空间:聚合签名减少了存储和传输所需的空间,这对于大规模部署尤为重要。\n\n抗串谋性:由于签名的生成和验证机制,即使部分签名者串谋,也难以对系统安全造成威胁。\n\n\n应用\n区块链技术:在区块链中,多重签名可以用于实现多重签名钱包,要求多个签名者中的一些同意才能进行交易。\n\n金融服务:在需要多个授权人批准交易的场景中,如大额支付或资产管理。\n\n安全通信:在需要验证消息来源和完整性的加密通信系统中。\n\n\n结论基于有限单群的Schnorr多重数字签名算法是一种强大的密码学工具,它通过结合Schnorr签名的效率和有限单群的安全性,为需要多重认证的场景提供了一种有效的解决方案。\nBLS签名算法\nBLS签名算法是一种基于双线性配对的密码学技术,以其简洁、短签名和强安全性著称。\n\n特点:\n基于双线性配对:BLS签名利用了双线性配对的性质,这是一种在两个不同的椭圆曲线群之间定义的函数,满足双线性、非退化和有效性。\n短签名:BLS签名生成的签名相对较短,这对于带宽和存储效率至关重要。\n签名聚合:BLS签名允许将多个签名聚合为一个单一的签名,这在区块链等分布式系统中非常有用,可以显著减少交易数据的大小。\n安全性:BLS签名的安全性基于计算双线性Diffie-Hellman问题的困难性。\n\nBLS签名算法步骤\n参数生成:选择适当的椭圆曲线和相应的群,以及群的生成元。\n密钥生成:为每个用户生成一个私钥(一个随机数)和相应的公钥(私钥的哈希值)。\n签名生成:签名者使用其私钥和消息的哈希值来生成签名。\n签名验证:使用签名、公钥和消息来验证签名的有效性。\n\nGo语言实现示例package mainimport (\t"crypto/rand"\t"crypto/sha256"\t"golang.org/x/crypto/bn256"\t"golang.org/x/crypto/ecdsa")// 生成密钥对func GenerateKeyPair() (*ecdsa.PrivateKey, *ecdsa.PublicKey) {\tprivateKey, err := ecdsa.GenerateKey(bn256.P256(), rand.Reader)\tif err != nil {\t\tpanic(err)\t}\treturn privateKey, &privateKey.PublicKey}// 哈希消息func HashMessage(message []byte) []byte {\thash := sha256.Sum256(message)\treturn hash[:]}// 签名消息func Sign(privateKey *ecdsa.PrivateKey, message []byte) (*bn256.G1, error) {\thash := HashMessage(message)\tr := bn256.ScalarBaseMult(privateKey.D, hash[:])\ts := bn256.ScalarBaseMult(privateKey.D, bn256.G1.Gen().ScalarMult(bn256.ScalarFromBytes(hash)).Add(r))\treturn s, nil}// 验证签名func Verify(publicKey *ecdsa.PublicKey, message []byte, signature *bn256.G1) bool {\thash := HashMessage(message)\tleft := bn256.Pairing(signature, bn256.G2.Gen().ScalarMult(bn256.ScalarFromBytes(hash)))\tright := bn256.Pairing(publicKey.X, bn256.G2.Gen().ScalarMult(bn256.ScalarFromBytes(hash)))\treturn left.Equal(right)}func main() {\tprivateKey, publicKey := GenerateKeyPair()\tmessage := []byte("Hello, BLS!")\tsignature, err := Sign(privateKey, message)\tif err != nil {\t\tpanic(err)\t}\tisValid := Verify(publicKey, message, signature)\tfmt.Printf("Signature is valid: %v\\n", isValid)}\n\n注意:\n\n这个示例使用了bn256包,它实现了BN256曲线的双线性配对。\n签名生成和验证过程都进行了简化,没有包含异常处理和完整的安全特性。\n在实际应用中,需要对私钥进行安全存储,并确保随机数生成器的安全性。\n\n单向散列函数\n单向散列函数是一种将任意长度的输入转换成固定长度输出的函数,输出称为散列值。它具备单向性,即从输出难以反推出输入。\n\nMD5 算法讲解\nMD5(Message Digest Algorithm 5,消息摘要算法第五版)\n\n特点\n固定长度输出:无论输入数据的大小,输出的哈希值长度始终固定为128位。\n单向性:从哈希值几乎不可能反推出原始数据。\n抗篡改性:输入数据的微小变化会导致哈希值的巨大变化,这被称为“雪崩效应”。\n快速计算:MD5算法计算速度快,适用于需要快速哈希的场景。\n\n流程\n填充:对输入数据进行填充,使得数据长度满足对512位整数倍的要求。填充规则是在数据末尾添加一个1,然后添加足够数量的0,最后添加64位的数据原始长度信息。\n初始化缓冲区:算法开始前,初始化四个32位的变量(A, B, C, D)\n处理消息块:将填充后的数据分成512位的消息块,每个块进一步被划分为16个32位的子块。\n主循环:对每个消息块执行以下步骤:\n将A, B, C, D的值复制到工作变量a, b, c, d中。\n对于每个子块,通过一系列复杂的函数和运算更新a, b, c, d的值。\n这些函数包括位与(AND)、位或(OR)、异或(XOR)和位非(NOT)操作。\n完成所有子块的处理后,将a, b, c, d的值与A, B, C, D相加,更新这些变量。\n\n\n输出哈希值:重复上述过程直到所有消息块都被处理完毕,最终得到的A, B, C, D的值即为MD5哈希值。\n\n安全性\n已破解:MD5算法已被证明存在安全漏洞,如碰撞攻击,即找到两个不同的输入产生相同的输出。\n不推荐使用:由于安全问题,MD5不再推荐用于需要高安全性的场合,如数字签名、密码存储等。\n\n应用\n数据完整性:尽管存在安全漏洞,MD5仍然有时被用于检测数据是否被篡改,因为它可以快速生成数据摘要。\n其他:在一些不需要高安全性的场合,MD5仍可能被用作一种快速的哈希手段。\n\n结论MD5算法因其快速和简单而被广泛使用,但由于已知的安全问题,它不再适合用于安全性要求高的场合。在需要确保数据安全的应用中,推荐使用更安全的哈希算法,如SHA-256或SHA-3。\nSHA 算法讲解\nSHA(Secure Hash Algorithm,安全散列算法)是一系列密码散列函数,设计用于确保信息传输的完整性和安全性。\n\nSHA算法概述\n设计目的:生成消息摘要,用于数据完整性校验和安全认证。\n单向性:易于从数据生成摘要,但从摘要几乎不可能反推出原始数据。\n抗篡改性:输入数据的微小变化会导致生成的消息摘要发生显著变化。\n\nSHA算法家族\nSHA-0:最初的版本,很快被发现存在安全问题。\nSHA-1:SHA-0的后续版本,产生160位的摘要。已发现安全漏洞,不推荐用于高安全需求。\nSHA-2:包括SHA-224、SHA-256、SHA-384和SHA-512。提供不同长度的摘要,是目前广泛使用的版本。\nSHA-3:基于Keccak算法,提供与SHA-2相似的安全级别,但具有不同的结构。\n\nSHA-1算法详解\n消息填充:将消息扩展到长度为512位倍数。\n初始化缓冲区:初始化五个32位的变量。\n处理消息块:将填充后的消息分为512位的块,对每个块进行处理。\n主循环:对每个块进行80次迭代,使用不同的函数和常量。\n更新缓冲区:将每轮的结果累加到初始缓冲区变量。\n输出哈希值:最终的缓冲区变量连接起来形成160位的哈希值。\n\nSHA-2算法详解\n消息填充:类似于SHA-1,但细节有所不同。\n初始化缓冲区:根据算法版本(SHA-224、SHA-256、SHA-384、SHA-512)初始化不同的32位或64位缓冲区变量。\n处理消息块:将填充后的消息分为512位(对于SHA-256和SHA-224)或1024位(对于SHA-512和SHA-384)的块。\n主循环:进行64或80次迭代,使用更复杂的函数。\n更新缓冲区:将每轮的结果累加到初始缓冲区变量。\n输出哈希值:最终的缓冲区变量连接起来形成相应的哈希值。\n\nSHA-3算法详解\n基于Keccak算法:SHA-3采用了一种称为海绵结构的设计。\n初始化:初始化一个固定大小的状态数组。\n吸收阶段:将输入消息分块,与状态的一部分进行异或操作,然后更新整个状态。\n挤出阶段:从状态中提取输出哈希值。\nKeccak置换函数:包括多个步骤,如θ(Theta)、ρ(Rho)、π(Pi)、χ(Chi)和ι(Iota),用于混合状态。\n\n安全性\nSHA-1和SHA-0:已知存在安全漏洞,不建议用于安全性要求高的场合。\nSHA-2:目前被认为安全,但建议使用SHA-256或更高版本的算法。\nSHA-3:提供与SHA-2类似的安全级别,具有不同的结构,被认为是安全的。\n\n结论SHA算法是密码学中重要的组成部分,用于确保数据的完整性和安全性。随着密码学技术的发展,新的SHA版本不断被开发以应对新的安全挑战。SHA-3作为最新的版本,提供了额外的结构和安全性优势。在选择SHA算法时,应考虑当前的安全需求和算法的成熟度。\nBlake 和 Blake2 算法讲解BLAKE算法\nBLAKE是一种密码散列函数,参与了NIST的SHA-3竞赛并成为最终的五个决赛选手之一。它由Jean-Philippe Aumasson等人设计,旨在提供高安全性和高性能。\n\n特点\n高安全性:BLAKE算法设计考虑了多种已知的密码学攻击,增强了对这些攻击的抵抗力。\n高性能:BLAKE算法在多种平台上表现出了优秀的性能。\n灵活性:BLAKE算法支持不同长度的输出,适用于不同的安全需求。\n\n版本\nBLAKE-224:输出长度为224位。\nBLAKE-256:输出长度为256位。\nBLAKE-384:输出长度为384位。\nBLAKE-512:输出长度为512位。\n\n算法流程\n初始化:设置算法的参数和初始状态。\n填充:对输入数据进行填充,使其长度满足算法的要求。\n处理消息块:将填充后的数据分成多个块进行处理。\n最终化:在所有数据块处理完毕后,进行最终化步骤,输出散列值。\n\nBLAKE2算法详解\nBLAKE2是基于BLAKE算法的改进版本,BLAKE2在安全性、性能和灵活性方面进行了优化。\n\n特点\n安全性:BLAKE2提供了与BLAKE相当的安全性。\n高性能:BLAKE2在多种处理器架构上进行了优化,提供了更高的性能。\n灵活性:BLAKE2支持多种配置选项,如密钥哈希、盐值哈希等。\n\n版本\nBLAKE2b:适用于64位平台,支持最长64字节的摘要。\nBLAKE2s:适用于8到32位平台,支持最长32字节的摘要。\nBLAKE2bp 和 BLAKE2sp:分别是BLAKE2b和BLAKE2s的并行版本。\n\n算法流程\n参数配置:根据应用需求配置算法参数。\n初始化:设置算法的初始状态和参数。\n填充:对输入数据进行填充,准备进行散列计算。\n处理消息块:将填充后的数据分成多个块,并进行处理。\n最终化:在所有数据块处理完毕后,进行最终化步骤,输出散列值。\n\n应用场景\n密码学应用:数字签名、消息认证码、随机数生成等。\n数据完整性验证:文件校验、数据备份验证等。\n区块链和加密货币:作为哈希函数,用于交易和区块的安全性保护。\n\nPoseidon算法讲解\nPoseidon是一种密码学哈希函数,专为区块链和零知识证明(Zero-Knowledge Proofs, ZKPs)等应用设计。旨在提供高效率和高安全性,尤其适合于需要大量哈希计算的场景。\n\n特点\n针对零知识证明优化:Poseidon设计时考虑了在零知识证明系统中的应用,优化了哈希计算的效率。\n基于加密结构:Poseidon采用Sponge结构,这种结构在密码学中广泛应用于构建哈希函数和伪随机数生成器。\n高效性:Poseidon的设计特别注重计算效率,适合在资源受限的环境中运行,如智能合约。\n灵活性:Poseidon可以根据不同的应用需求进行配置,支持不同的安全参数和性能优化。\n安全性:Poseidon提供了较高的安全性,能够抵抗碰撞攻击、预映射攻击和第二预映射攻击。\n\n具体实现\n状态初始化:Poseidon初始化一个状态向量,状态的大小通常取决于安全参数和哈希输出的长度。\n非线性层(S-Box):Poseidon在状态向量的每个元素上应用一个非线性变换,这种变换称为S-Box。\n线性层:Poseidon应用一个线性变换,将状态向量的元素混合。通常,这个线性变换是通过一个矩阵乘法实现的。\n轮函数(Round Function):非线性层和线性层组合成一个轮函数。Poseidon多次应用轮函数,将输入数据充分混合。\nSponge结构:Poseidon使用Sponge结构处理输入数据。首先在吸收阶段将输入数据分块处理并更新状态向量,然后在挤出阶段从状态向量中提取哈希值。\n\n应用\n区块链:因其高效性和安全性,被认为是区块链系统中实现零知识证明的理想哈希函数。\n隐私保护:可以用于保护数据隐私,通过零知识证明确保数据的完整性和保密性而无需透露实际数据内容。\n智能合约:可以集成到区块链智能合约中,提供高效的哈希计算和验证功能,增强智能合约的安全性和性能。\n\n","categories":["wallet"],"tags":["blockchain"]},{"title":"共享门限秘密算法","url":"/blog/2024/05/28/0-wallet/07-%E5%85%B1%E4%BA%AB%E9%97%A8%E9%99%90%E7%A7%98%E5%AF%86%E7%AE%97%E6%B3%95/","content":"门限共享秘密算法(Threshold Secret Sharing,TSS)重点知识点\n密钥分片:门限共享秘密算法将一个秘密(如加密密钥或钱包的助记词)分割成多个部分,称为份额(shares)。\n门限值:确定一个门限值(K),只有当收集到至少K个份额时,才能恢复出原始的秘密。\n安全性:即使部分份额丢失或被攻击者获取,只要不超过门限值,秘密仍然是安全的。\n秘密恢复:只有当拥有足够数量的份额时,才能通过某种算法恢复原始的秘密。\n可扩展性:算法可以设计成支持任意数量的份额和门限值,以适应不同的安全需求。\n分布式存储:份额可以分布式存储在不同的物理位置或服务器上,增强了系统的容错性和抗攻击能力。\n\n应用场景\n多签名钱包:在加密货币钱包中,使用TSS可以允许多个用户共同管理资产,只有当一定数量的用户同意时才能进行交易。\n分布式密钥管理:在企业或组织中,TSS可以用于安全地管理和访问敏感信息,如服务器的私钥。\n云服务提供商:云服务可以利用TSS来提供更高级别的数据安全性,确保客户数据的安全存储和访问。\n区块链治理:在区块链网络中,TSS可以用于实现多签名的治理机制,确保关键决策需要多个参与者的共识。\n\n技术难点\n秘密分割算法:设计一个既安全又高效的分割算法是一个挑战,需要确保分割后的份额无法被单独解密。\n恢复算法的安全性:恢复算法必须能够抵御各种攻击,包括内部和外部的攻击。\n性能问题:在处理大量数据或高频率的访问请求时,保持高性能是一个技术挑战。\n实际部署的复杂性:在实际部署中,需要考虑如何安全地生成、存储和传输份额。\n参与者的信任问题:在分布式系统中,如何确保参与者之间的信任和合作是一个问题。\n容错和备份:需要设计有效的容错和备份机制,以防止份额的丢失或损坏。\n\n门限秘密共享方案Shamir的(t, n)门限方案:\n该方案基于多项式插值的原理,特别是拉格朗日插值法。需要n个份额中的任意t个份额来恢复秘密。\n\n基本概念\n秘密(Secret, s):需要被共享的原始数据或密钥。\n份额(Shares):秘密分割成的多个部分,每个份额称为一个份额。\n门限值(Threshold, t):恢复原始秘密所需的最小份额数。\n参与者数(Total Participants, n):总共分割出的份额数量。\n\n工作原理\n秘密分割:\n秘密分发者选择一个大素数p(通常远大于秘密s)。\n在模p的有限域GF(p)中,秘密分发者构造一个t-1次的多项式f(x),使得f(0) = s(秘密)。\n\n\n生成份额:\n秘密分发者随机选择n个不同的非零值x1, x2, …, xn作为多项式的根。\n对于每个根xi,计算多项式的值yi = f(xi)。\n将每个根xi和对应的计算值yi作为一对份额(xi, yi)分发给n个参与者。\n\n\n秘密恢复:\n任何t个或更多的份额持有者可以合作恢复原始秘密s。\n通过拉格朗日插值法,利用t个已知的份额(xi, yi)计算出多项式f(x)在x=0处的值,即秘密s。\n\n\n\n安全性分析\n安全性前提:至少t-1个份额持有者是诚实的,没有串通攻击者。\n抵抗攻击:少于t个份额无法恢复多项式f(x),因此无法得知秘密s。\n\n优点\n灵活性:可以在不改变其他份额的情况下,增加新的份额持有者或更新现有份额。\n可扩展性:适用于各种规模的参与者集合。\n去中心化:不需要可信的中心服务器来管理份额。\n\n缺点\n存储需求:每个参与者需要存储一个份额对(xi, yi),随着参与者数量的增加,存储需求也会增加。\n通信复杂性:在秘密恢复时,需要t个份额持有者之间的通信。\n\nBlakley的(t, n)门限方案:\n该方案基于几何概念,特别是基于多维空间中的点的性质。\n\n基本概念\n秘密(Secret, s):需要被共享的数据或密钥。\n份额(Shares):秘密分割成的多个部分,每个份额是一个点的坐标。\n门限值(Threshold, t):恢复原始秘密所需的最小份额数。\n参与者数(Total Participants, n):总共分割出的份额数量。\n\n工作原理\n选择空间:\nBlakley方案首先在多维空间中选择一个点,该点的坐标表示秘密。\n\n\n生成份额:\n在一个足够高维的空间(通常是t维)中,秘密分发者选择一个点(秘密点),这个点的坐标是秘密s。\n然后,秘密分发者在通过秘密点的超平面上选择n-1个不同的点,这些点与秘密点一起定义了一个超平面。\n\n\n分配份额:\n每个参与者获得一个份额,即超平面上的一个点的坐标。\n为了恢复秘密,需要t个不共线的点(因为一个t维空间中的t个不共线的点可以确定一个超平面)。\n\n\n秘密恢复:\n当收集到t个份额时,这t个点的坐标可以用来确定包含秘密点的超平面。\n通过解这组线性方程,可以找到秘密点的坐标,即原始秘密s。\n\n\n\n安全性分析\n安全性前提:至少需要t个份额才能确定超平面,从而恢复秘密。\n抵抗攻击:少于t个份额无法确定超平面,因此无法得知秘密。\n\n优点\n安全性:只有达到门限值的份额才能恢复秘密,提供了良好的安全性。\n直观性:基于几何概念,易于直观理解。\n\n缺点\n复杂性:相比于多项式基的方案,Blakley方案在计算和实现上可能更复杂。\n扩展性:添加或删除参与者可能比较复杂。\n\n还有一些其他的解决方案,待需要时继续学习:\n\nKarnin-Green-Hellman方案:使用矩阵乘法来实现秘密共享, 允许灵活地设置访问结构和门限值。\n一般访问结构上的秘密共享方案:不限于特定的门限值,支持更复杂的访问结构。允许为不同的参与者组合定义不同的访问权限。\n多重秘密共享方案:允许每个参与者的子秘密在多次秘密共享过程中被重用。\n多秘密共享方案:在一次秘密共享过程中可以共享多个秘密。\n可验证秘密共享方案:允许参与者验证他们收到的子秘密的正确性。分为交互式和非交互式两种。\n动态秘密共享方案:允许在不改变秘密的情况下,周期性地更新子秘密。提供了对秘密在生命周期内的动态保护。\n量子秘密共享:利用量子力学原理来实现秘密共享。\n可视秘密共享:通过图像处理技术,将秘密编码在图像中。\n基于多分辨滤波的秘密共享:利用多分辨滤波技术来分割和共享秘密。\n基于广义自缩序列的秘密共享:使用广义自缩序列的数学特性来实现秘密共享。\n\n","categories":["wallet"],"tags":["blockchain"]},{"title":"钱包类别","url":"/blog/2024/05/17/0-wallet/30-%E9%92%B1%E5%8C%85%E7%B1%BB%E5%88%AB/","content":"钱包类别\nWeb3钱包的核心差异在于私钥的管理方式。私钥是用户访问和控制其加密货币资产的关键。\n\n\n中心化钱包:私钥由中心服务器管理,如交易所钱包(Binance、okx)\n去中心化钱包:私钥存储在用户设备上,如ImToken、MetaMask。采用确定性分层钱包结构,提供更高的个人控制权。\n硬件钱包:私钥存储在离线硬件设备中,代表产品如Ledger、OneKey,通过蓝牙、NFC或串口与用户设备通信,实现安全签名。\n交易所Web3钱包:集成了中心化、去中心化和硬件钱包的功能。\n托管钱包:托管钱包通常指的是由第三方服务提供商管理私钥的钱包。\n多签钱包:基于M-of-N的签名机制,要求一定数量的签名才能授权交易,如Gnosis Safe。\nMPC钱包:采用秘密共享技术来管理和生成加密货币私钥的钱包。\n社交恢复钱包:两种主要的恢复机制:守护者恢复和私钥分片恢复。\nEVM链AA钱包:遵循ERC4337协议,提供账户抽象功能,实现智能合约级别的钱包服务。\n\n中心化钱包架构和工作原理\n私钥存储:中心化钱包的私钥通常存储在中心服务器上,由服务提供者管理。用户不直接控制私钥,而是通过账户和密码来访问自己的资产。\n加密技术:为了保护私钥,中心化钱包服务提供者会采用各种加密技术,如DES加密、AES加密等,将私钥加密后存储。\n签名机制:当用户发起交易时,钱包服务会解密私钥,使用私钥对交易进行签名,然后将签名后的交易发送到区块链网络。\n\n业务流程\n批量地址生成:为了提高性能和响应速度,中心化钱包会预先生成大量的地址,存放在地址池中,用户注册时直接分配地址。\n充值:用户向钱包地址发送加密货币,钱包服务通过扫描区块链来确认交易,并更新用户的账户余额。\n提现:用户提交提现请求,钱包服务验证请求后,使用私钥签名交易,并将加密货币发送到用户指定的地址。\n归集:将分散在多个地址中的资金归集到一个或几个地址中,以便于管理和使用。\n热转冷:将热钱包中的资金转移到冷钱包中,以提高安全性。\n冷转热:从冷钱包中转移资金到热钱包,以便用户进行交易。\n链路风控:实施风险控制措施,确保交易的安全性,防止欺诈和非法活动。\n\n安全性\n内部风险:由于私钥由中心化服务管理,存在内部人员滥用权限的风险。\n外部攻击:中心化钱包可能成为黑客攻击的目标,一旦被攻破,用户资产将面临巨大风险。\n合规性:中心化钱包通常需要遵守严格的监管要求,包括KYC(了解你的客户)和AML(反洗钱)政策。\n\n用户体验\n便捷性:用户不需要管理私钥,只需记住账户和密码,使用起来相对简单。\n服务依赖:用户的资产安全和服务可用性高度依赖于中心化钱包的运营状况。\n\n优势与劣势\n优势:操作简便,适合新手用户;交易速度快,服务响应及时。\n劣势:用户对资产的控制权较低;存在中心化服务的风险,如内部欺诈、黑客攻击等。\n\n去中心化钱包\n也称为非托管钱包,它允许用户完全控制自己的私钥和资金,而不需要依赖于第三方服务\n\n核心特点\n私钥控制:用户完全控制自己的私钥,这是访问和转移加密货币的关键。\n非托管:没有中心化的实体持有用户资产或私钥,用户对自己的资产有完全的控制权。\n确定性分层钱包(HD Wallet):支持分层确定性钱包结构,可以从一个主种子(通常是助记词)生成多个地址。\n\n架构和工作原理\n本地存储:私钥通常存储在用户的设备上,如电脑、手机或专用硬件设备。\n加密保护:私钥在本地以加密形式存储,需要用户设置的密码或PIN码来解锁。\n签名交易:用户发起交易时,交易会在本地设备上使用私钥签名,然后广播到区块链网络。\n\n业务流程\n生成钱包:用户创建新钱包时,系统会生成一个私钥和相应的公钥和地址。\n备份助记词:生成一组助记词(通常为12或24个单词),用于恢复钱包。\n接收资金:用户可以将加密货币发送到钱包的公钥地址。\n发送交易:用户发起交易时,需要输入密码或PIN码来解锁钱包并签名交易。\n查看余额和交易:用户可以查看钱包的余额和交易历史。\n\n安全性\n私钥安全:私钥的安全性完全取决于用户,需要妥善保管。\n加密保护:私钥在本地加密存储,增加了安全性。\n多签名:支持多签名钱包,需要多个私钥共同签名才能进行交易,提高了安全性。\n\n用户体验\n控制权:用户对自己的资产有完全的控制权。\n隐私性:交易和余额信息不需要与任何中心化实体共享。\n技术门槛:相对于中心化钱包,去中心化钱包可能需要用户具备一定的技术知识。\n\n优势与劣势\n优势:用户完全控制自己的资产;提高了安全性和隐私性;支持多种区块链资产。\n劣势:用户需要自己管理私钥,增加了责任;可能需要更高的技术知识;交易速度可能较慢。\n\n硬件钱包\n提供高安全性的加密货币存储解决方案,专门设计的物理设备,用于生成、存储和管理私钥,以确保用户资产的安全。\n\n核心特点\n离线存储:硬件钱包将私钥存储在设备内部,与互联网隔离,只在需要时与设备交互,减少了在线黑客攻击的风险。\n私钥生成:私钥在设备内部生成,确保了私钥的随机性和安全性。\n用户控制:用户完全控制硬件钱包中的私钥,没有第三方介入。\n\n架构和工作原理\n嵌入式系统:硬件钱包通常基于嵌入式系统,具有有限的硬件资源,专注于安全功能。\n安全元件:使用专门的安全元件(如安全处理器或加密协处理器)来保护私钥。\n用户界面:配备有小屏幕和按钮,用于显示交易信息和用户操作确认。\n通信接口:通过USB、蓝牙、NFC等接口与用户设备通信。\n\n主要功能\n地址生成:硬件钱包可以生成多个地址,通常遵循BIP32、BIP39等标准。\n交易签名:硬件钱包在设备内部对交易进行签名,确保私钥不会暴露给用户设备。\n备份与恢复:支持助记词或种子短语备份,允许用户恢复钱包。\n多签名支持:一些硬件钱包支持多签名交易,增加交易的安全性。\n\n操作流程\n初始化:用户设置硬件钱包,生成新的种子短语,并妥善保存。\n接收资金:用户通过硬件钱包提供的公钥地址接收资金。\n发送交易:\n用户在计算机或手机上创建交易请求。\n将交易详情传输到硬件钱包。\n硬件钱包显示交易信息,用户确认无误后,通过按钮确认签名。\n硬件钱包对交易进行签名,并将签名后的交易返回给用户设备。\n用户将签名后的交易广播到区块链网络。\n\n\n备份与恢复:在需要时,用户可以使用种子短语恢复钱包。\n\n安全性\n抵御网络攻击:由于私钥不接触网络,硬件钱包能有效抵御网络攻击。\n物理安全:硬件钱包的物理安全性取决于其构建质量和用户如何保管设备。\n抗胁迫功能:一些硬件钱包提供虚假的PIN码或助记词,以防止在胁迫情况下泄露真实信息。\n\n用户体验\n高安全性:为用户提供银行级别的资产保护。\n易用性:尽管设置了安全措施,但大多数硬件钱包仍设计为用户友好。\n成本:硬件钱包需要购买,存在一定的成本。\n\n优势与劣势\n优势:极高的安全性;私钥不触网;支持多种加密货币;易于使用。\n劣势:需要购买硬件设备;交易时需要物理设备;可能存在设备损坏或丢失的风险。\n\n交易所Web3钱包核心特点\n集成性:作为交易所平台的一部分,用户可以直接在交易所内进行资产的存储、转账和交易。\n多功能:除了基本的钱包功能,还可能提供去中心化应用(DApp)的交互、代币交换、借贷等高级功能。\n用户界面:提供用户友好的界面,便于用户管理和使用其加密资产。\n\n架构和工作原理\n中心化管理:用户的私钥由交易所管理,这与中心化钱包相似,便于交易所进行快速的交易处理。\n去中心化交互:支持与区块链上的智能合约和DApp的交互,允许用户进行去中心化的交易和操作。\n热钱包与冷钱包:结合热钱包(在线钱包)和冷钱包(离线钱包)的使用,以提供安全性和便捷性的平衡。\n\n主要功能\n资产管理:用户可以在钱包中查看和管理自己的加密货币资产。\n交易执行:用户可以直接在钱包界面上执行买卖订单。\n智能合约交互:支持与智能合约的交互,使用户能够参与到去中心化金融(DeFi)项目中。\n代币收发:用户可以发送和接收各种加密货币代币。\n多签支持:可能支持多签名钱包功能,增加交易的安全性。\n\n操作流程\n账户创建:用户在交易所注册账户,并创建Web3钱包。\n私钥管理:私钥由交易所安全地管理,用户通过账户验证来访问钱包功能。\n资金存入:用户可以通过转账将加密货币存入交易所钱包。\n交易和投资:用户利用钱包内的资金进行市场交易或参与DeFi项目。\n资金提取:用户可以将资金从钱包提取到个人地址或其他钱包。\n\n安全性\n中心化风险:由于私钥由交易所管理,用户需要信任交易所的安全性和信誉。\n多重签名:一些交易所可能提供多重签名保护,增加交易的安全性。\n冷存储:交易所通常会将大部分资产存储在冷钱包中,以抵御网络攻击。\n\n用户体验\n便捷性:用户可以在一个平台上完成交易和资产管理,无需在多个钱包间转移资金。\n功能丰富:提供多种金融功能,满足用户的不同需求。\n安全性:虽然不如硬件钱包安全,但通过多重签名和冷存储等措施提高了安全性。\n\n优势与劣势\n优势:操作便捷,功能集成;用户界面友好;快速交易执行。\n劣势:中心化管理可能带来安全风险;用户对资产的控制权较低。\n\n托管钱包\n提供一种高度安全的方式来存储和管理私钥。通常由一个可信的第三方机构管理,如专业的托管服务提供商或金融机构。\n\n核心特点\n第三方管理:私钥由第三方机构管理,而不是由用户直接控制。\n安全性高:通过专业的安全措施和协议来保护私钥,适用于存储大量资产。\n合规性:托管钱包服务提供商通常需要遵守严格的监管要求,包括KYC和AML政策。\n\n架构和工作原理\n多签名技术:托管钱包通常采用多签名技术,即需要多个签名者中的一定数量同意才能授权交易。\n分层确定性钱包结构:支持分层确定性钱包(HD Wallet),可以从一个种子短语生成多个地址。\n冷存储:私钥存储在离线环境中,如硬件安全模块(HSM)或冷库,以提高安全性。\n\n主要功能\n资产保管:为用户提供加密货币资产的保管服务。\n交易执行:用户可以发起交易请求,由托管服务执行实际的交易。\n审计和报告:提供资产审计和交易报告服务,有助于合规性和透明度。\n风险管理:通过专业的风控措施,帮助用户管理资产风险。\n\n操作流程\n注册和验证:用户在托管服务提供商处注册账户,并完成必要的身份验证。\n资产存入:用户将加密货币发送到托管钱包提供的地址。\n交易请求:用户通过托管服务的用户界面或API发起交易请求。\n交易执行:托管服务在验证用户请求后,执行交易并将结果通知用户。\n资产提取:用户可以请求将资产从托管钱包提取到指定地址。\n\n安全性\n专业安全措施:托管服务提供商采用高级的安全技术和协议,如SSL加密、多因素认证等。\n保险:一些托管服务为存储的资产提供保险,以覆盖潜在的安全事件造成的损失。\n合规性:遵循监管要求,定期进行安全审计和合规检查。\n\n用户体验\n高安全性:为用户提供银行级别的资产保护。\n专业服务:提供专业的资产管理和交易执行服务。\n监管合规:帮助用户满足监管要求,降低合规风险。\n\n优势与劣势\n优势:提供高安全性的资产保管;专业的管理和服务;帮助用户满足监管要求。\n劣势:用户需要支付服务费用;对资产的控制权较低;需要信任第三方机构的信誉和安全性。\n\n多签钱包\n多签钱包(Multi-signature Wallets),是一种需要多个私钥中的一定数量(M个中的N个,即M-of-N)共同签署才能授权交易的加密货币钱包\n\n核心特点\n多重签名:交易需要多个私钥中的至少M个签名才能被授权。\n提高安全性:减少了单点故障的风险,即使部分私钥泄露,资产仍然安全。\n灵活性:用户可以根据需要设置M和N的值,以及哪些私钥参与签名。\n\n架构和工作原理\n私钥生成:每个参与方生成自己的私钥,并保留好。\n公钥组合:所有参与方的公钥被组合在一起,用于生成钱包地址。\n交易签署:当需要发起交易时,必须收集到足够的签名(至少M个)才能进行。\n智能合约支持:多签钱包通常基于智能合约实现,特别是在以太坊等支持智能合约的区块链上。\n\n主要功能\n资产共享管理:多个用户可以共同管理钱包中的资产。\n交易授权:交易需要多个签名,增加了交易的安全性。\n权限分配:可以根据需要分配不同的权限给不同的私钥持有者。\n审计和追踪:所有交易都需要多个签名,易于审计和追踪。\n\n操作流程\n创建钱包:创建多签钱包时,需要确定M和N的值,以及参与方的公钥。\n生成私钥:每个参与方生成自己的私钥,并妥善保管。\n发起交易:一个参与方发起交易请求,但交易尚未最终授权。\n收集签名:交易请求被发送给其他参与方,他们用自己的私钥签署交易。\n交易执行:收集到足够的签名后,交易被广播到区块链网络并执行。\n\n安全性\n抗胁迫:即使一个私钥持有者受到胁迫,也无法单独授权交易。\n抗攻击:需要多个签名才能交易,增加了攻击者盗取资产的难度。\n私钥分散:私钥分散在多个参与方手中,减少了私钥集中泄露的风险。\n\n用户体验\n安全性高:适合需要高安全级别的用户和组织。\n操作复杂性:相比于单签名钱包,多签钱包的操作更为复杂。\n协作需求:需要参与方之间的协作来签署和执行交易。\n\n优势与劣势\n优势:提高了安全性和抗风险能力;适合共同管理资产;易于审计和追踪。\n劣势:操作过程可能较为繁琐;需要多个参与方的协作;可能存在协调困难。\n\nMPC钱包\n(MPC,Multi-Party Computation),一个完整的私钥被分割多个部分,每个部分由不同的参与方持有。\n\n核心特点\n去中心化控制:私钥以分割的形式存在,没有单一实体能够单独控制私钥。\n增强安全性:私钥的每个部分单独不具有价值,只有组合一定数量的部分才能恢复私钥。\n多方授权:需要多个参与者的份额共同协作才能授权和签署交易。\n\n架构和工作原理\n秘密分割:私钥通过秘密共享算法被分割成多个份额,每个份额包含私钥的一部分信息。\n份额分布:这些份额被分发给不同的参与者,可以是不同的个人、服务器或地理位置。\n阈值设置:设置一个阈值(通常是N个中的M个),只有收集到M个份额时才能恢复私钥。\n交易签署:当需要签署交易时,所有参与者必须协作,提供他们的份额以生成签名。\n\n主要功能\n资产保护:即使部分份额丢失或被盗,只要没有达到阈值,资产仍然安全。\n抗胁迫:单个参与者无法被胁迫透露私钥信息,因为他们不拥有完整的私钥。\n容错性:即使一些份额丢失或损坏,只要有足够的份额,私钥仍然可以被恢复。\n审计和合规:所有交易都需要多个签名,便于审计和满足合规要求。\n\n操作流程\n初始化钱包:创建MPC钱包时,私钥被分割成多个份额,并分配给不同的参与者。\n份额存储:每个参与者安全地存储他们的份额,可能使用硬件安全模块(HSM)或其他安全措施。\n交易请求:用户发起交易请求,需要多个份额来授权。\n份额收集:协调者或智能合约收集足够的份额以重构私钥。\n签名交易:使用重构的私钥对交易进行签名。\n执行交易:将签名后的交易广播到区块链网络。\n\n安全性\n抗盗取:没有单一实体拥有完整的私钥,增加了盗取资产的难度。\n抗内部欺诈:需要多个参与者的协作才能进行交易,减少了内部欺诈的风险。\n抗胁迫:单个参与者即使在胁迫下也无法泄露完整私钥。\n\n用户体验\n高安全性:用户可以放心地保管资产,因为私钥的安全性得到了显著提升。\n操作复杂性:相比于传统钱包,MPC钱包的操作可能更加复杂,需要协调多个参与者。\n信任和协作:需要在参与者之间建立信任和协作机制。\n\n优势与劣势\n优势:提供了极高的安全性;适合需要严格合规的场合;增强了对资产的保护。\n劣势:操作过程可能较为复杂;需要多个参与者的协作;可能涉及较高的协调成本。\n\n社交恢复钱包守护者恢复机制在守护者恢复机制中,用户可以指定一组信任的联系人(守护者),他们可以在用户丢失私钥时帮助恢复钱包。这个过程通常涉及以下步骤:\n\n设置守护者:用户在钱包中设置一组信任的联系人(守护者),通常需要他们的公钥或钱包地址。\n私钥丢失:当用户丢失了对钱包的访问权限,例如忘记了密码或丢失了硬件钱包。\n请求恢复:用户向守护者发出帮助恢复的请求。\n多签名交易:守护者们需要按照预设的规则(例如,需要一定数量的守护者同意)签署一个特殊的多签名交易,以证明他们的身份和同意恢复操作。\n恢复钱包:一旦收集到足够数量的签名,就可以通过智能合约或其他机制来恢复钱包的访问权限。\n\n私钥分片恢复机制私钥分片恢复机制则是一种基于秘密共享算法的方法,它将私钥分割成多个部分(分片),并将这些分片分散存储在不同的地点或人那里。以下是私钥分片恢复的一般步骤:\n\n私钥分片:用户的私钥被分割成多个分片,每个分片都通过某种加密方法进行保护。\n分片存储:这些分片可以存储在用户的多个设备上、云端服务、或者交给信任的朋友保管。\n分片恢复:当用户需要恢复私钥时,他们需要收集一定数量的分片(根据设定的阈值,例如5个分片中的3个)。\n重组私钥:收集到足够的分片后,用户可以通过一个解密过程重组原始的私钥。\n访问钱包:一旦私钥被重组,用户就可以重新访问他们的钱包。\n\n社交恢复钱包的优势和挑战\n优势:提供了一种不依赖于单一备份的私钥恢复方式,增加了私钥管理的灵活性和安全性。\n挑战:需要用户信任守护者不会滥用他们的私钥信息,同时也要求用户在社交网络中有一定的信任基础。\n\nEVM链AA钱包核心特点\n智能合约钱包:AA钱包本身就是部署在区块链上的智能合约。\n内置安全性:通过智能合约的代码可以实施复杂的安全策略。\nGas代付:允许用户在执行交易时由第三方代付Gas费用。\n\n架构和工作原理\n账户抽象:与传统钱包不同,AA钱包将账户和交易的发送者抽象化,允许更灵活的交易发起和签名方式。\n交易构造:交易的构造可以在链上完成,用户或第三方可以代表AA钱包构造交易。\n签名机制:AA钱包可以使用多种签名机制,包括但不限于传统的ECDSA签名。\n\n主要功能\n代付交易:支持Gas代付功能,允许第三方支付交易费用。\n自定义交易:用户可以自定义交易属性,如nonce、gas限制等。\n多签交易:可以集成多签名机制,提高交易的安全性。\n智能合约交互:作为智能合约,AA钱包可以与其他智能合约进行交互。\n\n操作流程\n部署钱包:在以太坊链上部署AA钱包智能合约。\n资金存入:用户将资金存入AA钱包合约地址。\n交易构造:用户或第三方构造交易,包括确定接收者、金额和数据。\n交易签名:根据AA钱包的配置,可能需要用户或其他参与方的签名。\n交易执行:将构造好的交易发送到区块链网络,由网络中的节点验证和执行。\n\n安全性\n智能合约安全:安全性依赖于智能合约代码的健壮性,需要经过严格的安全审计。\n多签支持:如果集成多签名机制,可以增加交易的安全性。\n自定义安全策略:可以通过智能合约实现自定义的安全策略和检查。\n\n用户体验\n灵活性:提供了高度灵活的交易构造和签名方式。\n降低成本:通过Gas代付功能,可以降低用户的交易成本。\n复杂性:相比于传统钱包,AA钱包的设置和使用可能更复杂。\n\n优势与劣势\n优势:提供更高的安全性;支持Gas代付;允许灵活的交易构造和签名方式。\n劣势:需要用户理解智能合约的概念;可能存在智能合约安全风险。\n\n","categories":["wallet"],"tags":["blockchain"]},{"title":"Bitcoin钱包开发","url":"/blog/2024/05/31/0-wallet/32-Bitcoin%E9%92%B1%E5%8C%85%E5%BC%80%E5%8F%91/","content":"Bitcoin 钱包地址类型P2PKH(Pay-to-PubKeyHash)普通地址\n格式:以1开头,例如 1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa。\n特点:最传统和最常见的地址类型,广泛用于比特币早期交易,直接将比特币发送给一个特定的公钥哈希(即地址)\n优点:兼容性好,几乎所有钱包和交易所都支持。\n缺点:每笔交易都需要公开公钥,从而略微降低了隐私性,并且由于数据结构的限制,增加了交易的大小和费用。\n\nP2SH(Pay-to-Script-Hash)地址\n格式:以3开头,例如 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy。\n特点:允许更复杂的交易脚本,如多重签名地址。\n优点:支持复杂交易和脚本,提供更高安全性。\n缺点:创建和管理比P2PKH地址更复杂。\n\nBech32编码地址,比特币隔离见证(SegWit)的引入新增了几种地址类型:\nPay-to-Witness-Pubkey-Hash (P2WPKH): P2PKH的SegWit版本,使用Bech32编码。\n\n**Pay-to-Witness-Script-Hash (P2WSH)**:这是P2SH的SegWit版本,使用Bech32编码。\n\n**Pay-to-Taproot (P2TR)**:这是Taproot更新引入的地址类型,用于表示Taproot结构的输出,也使用Bech32编码\n\n格式:以 bc1 开头,例如 bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwfvenl。\n\n特点:具有更高的隐私性、安全性和扩展性。\n\n优点:通过利用Schnorr签名和Merkle化的脚本树(MAST),它能够提供更高的效率和隐私,更低的交易费用。\n\n缺点:不是所有的钱包和交易所都支持,尽管支持率在逐步增加。\n\n\n不同类型的地址之间能相互转账吗?在比特币网络中,不同类型的地址主要涉及到如何编码和处理交易的细节,但它们都遵循相同的底层比特币协议。\n比特币网络根据交易使用的地址类型不同,比特币网络中的节点和矿工会采用不同的处理方法来验证和确认交易。\n从任何一种地址类型向另一种类型的地址发送比特币都是可以的,地址类型的不同只是传输的数据格式和方法不同,并不影响发送和接受比特币。\n注意事项一个私钥可以同时生成多个不同类型的地址,并且各地址之间是相互隔离的,类比同一个银行拥有不用的账号,每个账号中的钱是独立的。\n钱包开发实践主要待完成: \n\n离线地址生成\n签名发送交易\n查询交易状态\n\n可参考资料: https://github.com/btcsuite/btcd \n代码 btcd/btcutil/address.go 下提供了具体的地址生成方式:\nNewAddressPubKeyHashNewAddressScriptHashNewAddressWitnessPubKeyHashNewAddressWitnessScriptHashNewAddressTaproot\n\n离线地址生成func createPrivkey() {\t// secp256k1 生成私钥\tprivKey, err := btcec.NewPrivateKey()\tif err != nil {\t\tpanic(err)\t}\tprivKeyHex := hex.EncodeToString(privKey.Serialize())\tfmt.Println("Private Key:", privKeyHex)\t// 创建一个新的WIF编码的私钥\twif, err := btcutil.NewWIF(privKey, &chaincfg.MainNetParams, true) // true 表示压缩公钥\tif err != nil {\t\tlog.Fatalf("Error creating WIF: %v", err)\t}\t// 打印WIF格式的私钥\tfmt.Println("WIF Private Key:", wif.String())\t// 从私钥获取公钥\tpubKey := privKey.PubKey()\tpubKeyHash := btcutil.Hash160(pubKey.SerializeCompressed())\t// 使用公钥和主网络参数创建一个P2PKH地址\taddr, err := btcutil.NewAddressPubKeyHash(pubKeyHash, &chaincfg.MainNetParams)\tif err != nil {\t\tlog.Fatalf("Unable to create address: %v", err)\t}\tfmt.Println("P2PKH Address:", addr.EncodeAddress())}\n\n确定性钱包地址生成const (\tLEGACY = "legacy" // p2pkh bip44\tSEGWIT_NESTED = "segwit_nested" // p2sh bip49\tSEGWIT_NATIVE = "segwit_native" // p2wpkh bip84\tTAPROOT = "taproot" // p2tr bip86)func GenerateExtendedKey() (*hdkeychain.ExtendedKey, error) {\t// 方式一: 创建一个新的HD钱包种子\t// seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)\t// if err != nil {\t// \tlog.Fatalf("Unable to generate seed: %v", err)\t// }\tentropy, _ := bip39.NewEntropy(128)\tmnemonic, _ := bip39.NewMnemonic(entropy)\tfmt.Println("助记词:", mnemonic)\t// 由助记词生成种子(Seed), password为空可兼容其他钱包\tseed := bip39.NewSeed(mnemonic, "")\t// 使用种子创建一个新的主私钥\tmasterPrivKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams)\tif err != nil {\t\tlog.Fatalf("Unable to create master private key: %v", err)\t}\t// Derive according to BIP44 path m/44'/0'/0'/0/0\tpurposeIndex := hdkeychain.HardenedKeyStart + 44\tcoinTypeIndex := hdkeychain.HardenedKeyStart + 0 // Bitcoin\taccountIndex := hdkeychain.HardenedKeyStart + 0 // Account 0\tchangeIndex := 0 // External address (receiving)\taddressIndex := 0 // First address\t// Correct usage of Derive method\tpurposeKey, err := masterPrivKey.Derive(uint32(purposeIndex))\tif err != nil {\t\tlog.Fatalf("Unable to derive purpose key: %v", err)\t}\tcoinTypeKey, err := purposeKey.Derive(uint32(coinTypeIndex))\tif err != nil {\t\tlog.Fatalf("Unable to derive coin type key: %v", err)\t}\taccountKey, err := coinTypeKey.Derive(uint32(accountIndex))\tif err != nil {\t\tlog.Fatalf("Unable to derive account key: %v", err)\t}\texternalKey, err := accountKey.Derive(uint32(changeIndex))\tif err != nil {\t\tlog.Fatalf("Unable to derive external chain key: %v", err)\t}\tchildKey, err := externalKey.Derive(uint32(addressIndex))\tif err != nil {\t\tlog.Fatalf("Unable to derive receive address key: %v", err)\t}\treturn childKey, nil}func HDWallet() {\tchildKey, _ := GenerateExtendedKey()\t// 获取子私钥\tprivKey, err := childKey.ECPrivKey()\tif err != nil {\t\tlog.Fatalf("Unable to get private key: %v", err)\t}\tprivKeyHex := hex.EncodeToString(privKey.Serialize())\tfmt.Println("Hex Private Key:", privKeyHex)\t// 创建一个新的WIF编码的私钥\twif, err := btcutil.NewWIF(privKey, &chaincfg.MainNetParams, true) // true 表示压缩公钥\tif err != nil {\t\tlog.Fatalf("Error creating WIF: %v", err)\t}\t// 打印WIF格式的私钥\tfmt.Println("WIF Private Key:", wif.String())\t// 从私钥获取公钥\tpubKey := privKey.PubKey()\t// 使用公钥和主网络参数创建一个P2PKH地址\taddr, err := btcutil.NewAddressPubKeyHash(btcutil.Hash160(pubKey.SerializeCompressed()), &chaincfg.MainNetParams)\tif err != nil {\t\tlog.Fatalf("Unable to create address: %v", err)\t}\tfmt.Println("1 - P2PKH Address:", addr.EncodeAddress())\tp2pkh, err := btcutil.NewAddressPubKey(pubKey.SerializeCompressed(), &chaincfg.MainNetParams)\tif err != nil {\t\tlog.Fatalf("Unable to create address: %v", err)\t}\tfmt.Println("2 - P2PKH Address:", p2pkh.EncodeAddress())\t// pay-to-witness-pubkey-hash (P2WPKH) btcutil.NewAddressWitnessScriptHash\tp2wpkh, err := btcutil.NewAddressWitnessPubKeyHash(btcutil.Hash160(pubKey.SerializeCompressed()), &chaincfg.MainNetParams)\tif err != nil {\t\tlog.Fatalf("Unable to create address: %v", err)\t}\tfmt.Printf("3 - P2WPKH Address: %s\\n", p2wpkh.EncodeAddress())\t// pay-to-script-hash (P2SH) btcutil.NewAddressScriptHash\tredeemScript, err := txscript.PayToAddrScript(p2wpkh)\tif err != nil {\t\tlog.Fatalf("Unable to create script: %v", err)\t}\tp2sh, err := btcutil.NewAddressScriptHash(redeemScript, &chaincfg.MainNetParams)\tif err != nil {\t\tlog.Fatalf("Unable to create address: %v", err)\t}\tfmt.Printf("4 - P2SH Address: %s\\n", p2sh.EncodeAddress())\t// pay-to-taproot (P2TR) btcutil.NewAddressTaproot\tinternalKey, _ := btcec.ParsePubKey(pubKey.SerializeUncompressed())\tp2tr, _ := btcutil.NewAddressTaproot(txscript.ComputeTaprootKeyNoScript(internalKey).SerializeCompressed()[1:], &chaincfg.MainNetParams)\tfmt.Printf("5 - P2TR Address: %s\\n", p2tr.EncodeAddress())}\n","categories":["wallet"],"tags":["bitcoin","blockchain"]},{"title":"HD钱包助记词生成","url":"/blog/2024/05/28/0-wallet/31-HD%E9%92%B1%E5%8C%85%E5%8A%A9%E8%AE%B0%E8%AF%8D%E7%94%9F%E6%88%90/","content":"助记词生成概念讲解助记词(Mnemonic Phrase) 是一种用于加密货币钱包中生成和管理私钥的方法,基于 BIP-39 标准。\n生成原理\n随机熵生成:创建一段随机熵,长度为 128 到 256 位,通常是 32 的倍数。\n计算校验和:对熵进行 SHA-256 哈希计算,取哈希值的前几位作为校验和。\n组合熵和校验:将校验和附加到熵的末尾,形成一个新的二进制序列。\n分割为助记词索引:将序列分割成 11 位一组,每个数字作为索引。\n映射为助记词:使用索引从预定义的 2048 个助记词列表中提取助记词。\n\n验证原理\n检查单词数量:助记词通常包含12、15、18、21或24个单词,如果单词数量不符合上述标准,则助记词无效。\n检查单词是否在词汇表中:每个单词必须在BIP-39标准的2048个单词的词汇表中。\n将助记词转化成位串:将每个单词转换为其在词汇表中的索引,每个索引是一个11位的二进制数,所有索引的二进制数连接起来,形成一个位串。\n提取种子和校验和:位串的长度应为单词数量乘以11位,12个单词的助记词将产生132位的位串。位串的前128位用于提取种子,最后4位用于校验和。\n计算校验和:使用SHA-256哈希函数对提取的种子(前128位)进行哈希计算,从哈希值中取前4位,作为计算得到的校验和。\n验证校验和:将从位串中提取的校验和与计算得到的校验和进行比较,如果两者匹配,助记词有效。\n\n代码实现import hashlibimport osimport bip39def generate_mnemonic(): # 1. 生成 128 位随机熵 (16 字节) entropy = os.urandom(16) # 2. 计算校验和 (SHA-256) hash_bytes = hashlib.sha256(entropy).digest() checksum_bits = bin(hash_bytes[0])[2:].zfill(8)[:4] # 取前 4 位 # 3. 组合熵和校验和 entropy_bits = ''.join([bin(byte)[2:].zfill(8) for byte in entropy]) combined_bits = entropy_bits + checksum_bits # 4. 分割为助记词索引 indices = [int(combined_bits[i:i + 11], 2) for i in range(0, len(combined_bits), 11)] # 5. 映射为助记词 wordlist = bip39.INDEX_TO_WORD_TABLE mnemonic = ' '.join([wordlist[index] for index in indices]) return mnemonicif __name__ == '__main__': # 生成并打印助记词 mnemonic_phrase = generate_mnemonic() print(f"Generated mnemonic phrase: {mnemonic_phrase}")\n\n推导关系:熵 → 助记词:有了熵,可以通过BIP39算法生成助记词。助记词 → 熵:根据助记词,可以反向推导出原始的熵。熵 或 助记词 → 种子【不可逆】:有了熵或助记词,可以使用BIP39过程生成种子。种子 → 私钥 和 公钥【不可逆】:种子通过加密算法如BIP32生成HD钱包的主私钥和主公钥,然后可以进一步生成任意数量的子私钥和对应的公钥。 \n优点:\n易用性:助记词以单词形式存在,方便用户记忆和记录。\n安全性:基于强随机熵和校验和机制,提供了较高的安全性。\n标准化:遵循 BIP-39 标准,实现跨钱包和应用的兼容性。\n\nBIP-32、BIP-39、BIP-44 和 BIP-86 分别解决了什么问题?BIP-32: 分层确定性钱包(HD Wallet)\n解决的问题:如何从单一种子生成一个钱包树结构,以便高效管理大量地址和密钥。\n功能:允许创建主密钥(root key)和从该主密钥派生出无限数量的子密钥。\n应用:用于生成钱包地址的层次结构,便于管理和备份。\n\nBIP-39: 助记词标准\n解决的问题:提供一个人性化且易于记忆的方式来备份和恢复钱包种子。\n功能:定义了一个由2048个单词组成的列表,用于生成助记词短语,这些短语可以转换成种子。\n应用:允许用户通过记忆一串单词来备份他们的钱包,简化了钱包的恢复过程。\n\nBIP-44: 多重账户确定性钱包结构\n解决的问题:如何组织和管理具有多个账户和货币的钱包。\n功能:定义了一个钱包结构标准,允许钱包同时管理多个账户,每个账户可以包含多个地址。\n应用:用于多币种、多账户的钱包管理,提高了钱包的灵活性和可用性。\n\nBIP-86: Schnorr签名\n解决的问题:提高比特币签名的效率和安全性。\n功能:引入Schnorr签名算法,允许多个签名聚合为一个,减少了签名数据的大小。\n应用:旨在提高交易的可扩展性和隐私性,同时减少交易验证的数据量。\n\n总结\nBIP-32 解决了密钥管理的可扩展性和效率问题,允许从单一种子生成多个密钥。\nBIP-39 解决了用户备份和恢复钱包的问题,提供了一种易于记忆和记录的方法。\nBIP-44 解决了多币种、多账户钱包管理的问题,提供了一种清晰的组织结构。\nBIP-86 解决了提高交易签名效率和安全性的问题,通过引入Schnorr签名算法。\n\n\nbip44 支持的完整币种列表: https://github.com/satoshilabs/slips/blob/master/slip-0044.md\n\n","categories":["wallet"],"tags":["blockchain"]},{"title":"Cosmos钱包开发","url":"/blog/2024/06/13/0-wallet/34-Cosmos%E9%92%B1%E5%8C%85%E5%BC%80%E5%8F%91/","content":"cosmos地址生成非确定性钱包地址生成\nsecp256k1.GenPrivKey() 生成私钥\n通过私钥->公钥->cmtcrypto.Address->bytes\n转换为cosmos中 baseAccount 账户模型并获取地址\n\nfunc createPrivKey() {\tprivKey := secp256k1.GenPrivKey()\tfmt.Println("privKey:", hex.EncodeToString(privKey.Bytes()))\tvar privKeyBytes sdk.AccAddress = privKey.PubKey().Address().Bytes()\tbaseAccount := authtypes.NewBaseAccount(privKeyBytes, privKey.PubKey(), 0, 0)\tfmt.Println("address:", baseAccount.Address)}\n\n\n\n确定性钱包生成\tfunc NewHDWallet() {\t// bip39 -> 助记词\tentropy, _ := bip39.NewEntropy(128)\tmnemonic, _ := bip39.NewMnemonic(entropy)\tfmt.Println("助记词:", mnemonic)\t// 由助记词生成种子(Seed), password为空可兼容其他钱包\tseed := bip39.NewSeed(mnemonic, "")\tmasterKey, ch := hd.ComputeMastersFromSeed(seed)\tpriv, _ := hd.DerivePrivateKeyForPath(masterKey, ch, "m/44'/118'/0'/0/0")\tprivKey := &secp256k1.PrivKey{Key: priv}\tfmt.Println("privKey:", hex.EncodeToString(privKey.Bytes()))\taccAddr, err := bech32.ConvertAndEncode(sdk.Bech32MainPrefix, privKey.PubKey().Address())\tif err != nil {\t\tpanic(err)\t}\tfmt.Println("accAddr:", accAddr)}\n\n","categories":["wallet"],"tags":["cosmos","blockchain"]},{"title":"Ethereum钱包开发","url":"/blog/2024/06/04/0-wallet/33-Ethereum%E9%92%B1%E5%8C%85%E5%BC%80%E5%8F%91/","content":"Ethereum钱包开发\n基于go实现以太坊离线钱包\n\n主要依赖仓库列表:\n github.com/ethereum/go-ethereum v1.14.4 github.com/tyler-smith/go-bip32 v1.0.0 github.com/tyler-smith/go-bip39 v1.1.0\n\n\n\n1.非确定性钱包\n地址独立,无关联性,但需要备份所有地址的私钥\n\n\n第一步生成私钥 GenerateKey -> ecdsa.PrivateKey \n\n第二步通过私钥获取公钥 privKey.PublicKey\n\n第三步公钥通过Keccak256算法得到地址\n\n\nfunc GeneratePrivKey() {\t// 生成随机私钥\tprivKey, _ := crypto.GenerateKey()\t// 将私钥转换为字节序列\tprivKeyBytes := privKey.D.Bytes()\t// 将私钥字节序列转换为十六进制字符串\tprivKeyHex := hex.EncodeToString(privKeyBytes)\t// 打印私钥的十六进制表示\tfmt.Println(privKeyHex)\t// 提取私钥对应的公钥\tPublicKey := privKey.PublicKey\t// 将公钥转换为以太坊地址\taddr := crypto.PubkeyToAddress(PublicKey)\tfmt.Println(addr)}\n\n\n\n通过secp256k1算法生成公私钥func createPrivKey() {\tcurve := secp256k1.S256()\tb := make([]byte, curve.Params().N.BitLen()/8)\tio.ReadFull(rand.Reader, b)\tkey := new(big.Int).SetBytes(b)\tfmt.Println("key:", len(key.Bytes()))\tfmt.Println("key:", hex.EncodeToString(key.Bytes()))\t// 使用私钥计算公钥,X和Y是公钥的坐标\tx, y := curve.ScalarBaseMult(key.Bytes())\t// 获取公钥的字节表示形式\tpublicKey := ecdsa.PublicKey{Curve: curve, X: x, Y: y}\tpubKeyBytes := crypto.FromECDSAPub(&publicKey)\t// 输出公钥\tfmt.Println("pubKey:", hex.EncodeToString(pubKeyBytes))\tcompressPubKey := crypto.Keccak256(pubKeyBytes[1:])\taddr := common.BytesToAddress(compressPubKey[12:])\t// 输出以太坊地址\tfmt.Println("addr:", addr.String())}\n\n\n\n2.分层确定性钱包生成一个BIP39兼容的助记词func bip39Mnemonic() (seed []byte) {\t// 生成256位的随机熵,用于创建BIP39助记词。即 128 个 16 进制字符\tentropy, _ := bip39.NewEntropy(128)\tmnemonic, _ := bip39.NewMnemonic(entropy)\tfmt.Println("助记词:", mnemonic)\t// 由助记词生成种子(Seed), password为空可兼容其他钱包\tseed = bip39.NewSeed(mnemonic, "")\tfmt.Println("New seed:", seed)\treturn}\n\n\n\n根据BIP39随机种子字节生成BIP32钱包\n通过种子得到MasterKey\n通过主私钥派生子私钥 masterKey.NewChildKey\n\nfunc bip32HD(seed []byte) {\t// 由种子生成主账户扩展私钥(私钥和链码)\tmasterKey, _ := bip32.NewMasterKey(seed) //从0开始,主账户密钥 masterKey 序号是 0,这个就叫做索引号(32 位)\tfmt.Println("masterKey:", masterKey)\tchildKey1, _ := masterKey.NewChildKey(1)\t// 用主账户公钥 派生 子账户公钥\tpublicKey := masterKey.PublicKey()\tchildPubKey1, _ := publicKey.NewChildKey(1)\t// 第一种: 通过扩展公钥派生出子账户公钥,扩展私钥派生出子账户私钥,实现公私钥解耦\t// 第二种: 强化派生限制父公钥派生子公钥,严格通过 扩展私钥 -> 子私钥 -> 子公钥\tpubK1, _ := childPubKey1.Serialize()\tpubK2, _ := childKey1.PublicKey().Serialize()\t// 当前通过 扩展公钥 -> 子公钥[1] == 对应子私钥[1] -> 子公钥, [1]表示 childIdx 一致\tif !bytes.Equal(pubK1, pubK2) {\t\tfmt.Println("pubK1 != pubK2")\t}\t// 强化派生\t// 索引号在 0 和 2^31–1(0x0 to 0x7FFFFFFF)之间的只用于常规派生。\t// 索引号在 2^31 和 2^32– 1(0x80000000 to 0xFFFFFFFF)之间的只用于强化派生。\tchildKeyPro, _ := masterKey.NewChildKey(bip32.FirstHardenedChild)\tpubKPro, _ := childKeyPro.PublicKey().Serialize()\tfmt.Println("pubKPro:", pubKPro)\tchildPubKeyPro, _ := publicKey.NewChildKey(bip32.FirstHardenedChild)\tfmt.Println("childPubKeyPro:", childPubKeyPro) // childPubKeyPro: <nil>}\n\n\n\n结合BIP44创建钱包\nmasterKey 兼容BIP44协议创建eth钱包\n\nfunc createWallet(seed []byte) {\t// 创建子账户, 为了保护主私钥安全,所有主私钥派生的第一级账户,都采用强化派生。\tmasterKey, _ := bip32.NewMasterKey(seed)\t// 以太坊的币种类型是60, 以路径(path: "m/44'/60'/0'/0/0")为例\tkey, _ := masterKey.NewChildKey(bip32.FirstHardenedChild + 44) // 强化派生 对应 purpose'\tkey, _ = key.NewChildKey(bip32.FirstHardenedChild + uint32(60)) // 强化派生 对应 coin_type'\tkey, _ = key.NewChildKey(bip32.FirstHardenedChild + uint32(0)) // 强化派生 对应 account'\tkey, _ = key.NewChildKey(uint32(0)) // 常规派生 对应 change\tkey, _ = key.NewChildKey(uint32(0)) // 常规派生 对应 address_index\tfmt.Println("privKey:", key.String())\t// 将key转换为十六进制字符串\thexKey := fmt.Sprintf("%x", key.Key)\tfmt.Println("Hex Key:", hexKey)\t// 生成地址\tpubKey, _ := crypto.DecompressPubkey(key.PublicKey().Key)\taddress := crypto.PubkeyToAddress(*pubKey).Hex()\tfmt.Println("address:", address)}\n\n\n\n发送交易与查询1.查询链上数据\n创建一个rpc节点客户端,通过客户端提供的方法进行查询\n\n// client的代码去 https://dashboard.alchemy.com/ 点击APIKEY后复制func newClient() (*ethclient.Client, error) {\t// client, err := ethclient.Dial("https://eth-mainnet.g.alchemy.com/v2/k7J02LbbJiACCe52gTgZ64sY-sj-AZux")\tclient, err := ethclient.Dial("https://eth-sepolia.g.alchemy.com/v2/k7J02LbbJiACCe52gTgZ64sY-sj-AZux")\tif err != nil {\t\tlog.Fatal(err)\t}\t// Get the balance of an account\taccount := common.HexToAddress(formAddr)\tbalance, err := client.BalanceAt(context.Background(), account, nil)\tif err != nil {\t\tlog.Fatal(err)\t}\tfmt.Printf("Account balance: %d\\n", balance)\t// Get the latest known block\tblock, err := client.BlockByNumber(context.Background(), nil)\tif err != nil {\t\tlog.Fatal(err)\t}\tfmt.Printf("Latest block: %d\\n", block.Number().Uint64())\treturn client, nil}\n\n\n\n2.发送交易至链上(SendTransaction)\n构造交易 types.NewTransaction\n使用私钥对交易进行签名 types.SignTx\n广播交易至节点 cli.SendTransaction\n\nfunc transaction() {\tprivKey, _ := crypto.HexToECDSA(privKeyHex)\tformAddr := crypto.PubkeyToAddress(privKey.PublicKey)\tfmt.Println("formAddr:", formAddr.Hex())\tcli, _ := newClient()\t// 构造交易数据\tnonce, err := cli.NonceAt(context.Background(), formAddr, nil)\tif err != nil {\t\tlog.Fatal(err)\t}\tfmt.Println("nonce:", nonce)\ttoAddress := common.HexToAddress(toAddr)\tamount := big.NewInt(1000000000000000)\tgasLimit := uint64(21000) // 标准交易的 gas 限制\tgasPrice := big.NewInt(200000000000) // gas 价格,你需要根据网络情况调整\t// gasPrice, err := cli.SuggestGasTipCap(context.Background())\t// if err != nil {\t// \tlog.Fatal(err)\t// }\tfmt.Println("gasPrice:", gasPrice)\ttx := types.NewTransaction(nonce, toAddress, amount, gasLimit, gasPrice, nil)\tchainID, err := cli.NetworkID(context.Background())\tif err != nil {\t\tlog.Fatal(err)\t}\t// 对交易进行签名\tsignedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privKey)\tif err != nil {\t\tlog.Fatal(err)\t}\t// 将签名后的数据通过clp编码为字节数组,前端eth_sendRawTransaction接口中params参数\tencodedTx, err := rlp.EncodeToBytes(signedTx)\tif err != nil {\t\tlog.Fatal(err)\t}\tparams := hex.EncodeToString(encodedTx)\tfmt.Printf("eth_sendRawTransaction Params: %s\\n", params)\t// 广播交易到测试节点\tif err = cli.SendTransaction(context.Background(), signedTx); err != nil {\t\tlog.Fatal(err)\t}\tfmt.Printf("Transaction Hash: 0x%x\\n", signedTx.Hash())}\n\n\n\nAPI相关接口参考api文档: https://docs.alchemy.com/reference/eth-gettransactionbyhash\n发送交易:params参数获取可参考transaction方法curl --request POST \\ --url https://eth-sepolia.g.alchemy.com/v2/k7J02LbbJiACCe52gTgZ64sY-sj-AZux \\ --header 'accept: application/json' \\ --header 'content-type: application/json' \\ --data ' { "id": 1, "jsonrpc": "2.0", "params": [ "0xf86f04852e90edd0008252089475751bf3a86ea2f19660229c112df7dad84b8c0287038d7ea4c68000808401546d72a0d7618c5a377ae77c40c0e092c05545bbb24aacfff02569d74a1ab3bf27620d0ba02b5e3fe241f53d44a00691084d9e0d08475ce136b92da53b8b0b9b4fed285167" ], "method": "eth_sendRawTransaction" }'\n\n\n\n根据txhash查询交易信息curl --request POST \\ --url https://eth-sepolia.g.alchemy.com/v2/k7J02LbbJiACCe52gTgZ64sY-sj-AZux \\ --header 'Content-Type: application/json' \\ --header 'Cookie: _cfuvid=wq9cOWHZrQJC_tuTXZLmlWkdI8GPYot1N2.FsyNsrbw-1716723624624-0.0.1.1-604800000' \\ --data ' { "id": 1, "jsonrpc": "2.0", "method": "eth_getTransactionByHash", "params": [ "0x18fb59de0ac1ab62401e08f6260c0fd838a35f113da467671c894c65d1b1ccc6" ] }'\n\n","categories":["wallet"],"tags":["blockchain","ethereum"]},{"title":"MPC钱包01","url":"/blog/2024/06/25/0-wallet/40-MPC%E9%92%B1%E5%8C%8501/","content":"探索MPC钱包:数字资产安全的革命性创新\n随着数字货币的普及,资产安全成为用户关注的焦点。传统的钱包依赖于单一私钥,存在单点故障的风险。MPC钱包的出现,以其独特的多方计算技术,为数字资产的安全存储和交易提供了新的解决方案。\n\nMPC钱包的核心原理MPC钱包的工作原理基于一个简单的概念:将私钥分割成多个碎片,每个碎片由不同的参与者持有。只有当足够数量的参与者共同参与计算时,才能恢复出完整的私钥,进而授权交易。\n实现MPC钱包的关键技术\n私钥分片:使用特定的算法,如Shamir’s Secret Sharing,将私钥分割成多个份额,每个份额本身不包含任何有关原始私钥的信息。\n阈值签名:定义一个阈值,只有当达到或超过这个阈值数量的份额共同参与时,才能生成有效的签名。\n多方计算协议:确保在不泄露各自份额的前提下,多个参与者可以协同计算出私钥的完整形式。\n\nMPC钱包的优势\n提高安全性:由于私钥的分散存储,即使部分份额被泄露,也不会导致资产损失。\n降低用户门槛:用户无需管理复杂的私钥或助记词,简化了使用流程。\n适应性强:MPC钱包适用于需要高度安全性的场景,如大额资产存储和金融交易。\n\nokx开源库\n参考链接:okx-threshold-lib,提供了基于阈值签名方案的多方计算(MPC)实现。这个库是用 Go 语言编写的,支持 ECDSA 和 Ed25519 签名算法的阈值签名。\n\n产品逻辑\n结合okx-MPC钱包,总体将私钥分为三方: 用户设备、用户云盘、交易所,其中任意两方即可完成消息签名\n紧急情况下可以通过用户云盘将私钥导出\n\n算法实现\nECDSA 阈值签名:使用 Feldman’s VSS(Verifiable Secret Sharing,可验证秘密共享)算法生成密钥份额,并使用 Lindell 17 协议实现两方签名。\nEd25519 阈值签名:为 Ed25519 签名方案实现阈值签名功能。\nBip32 密钥派生:支持密钥份额的非硬化派生,链码(chaincode)由多方共同生成。\n密钥份额刷新:当一方的密钥份额丢失或有新的参与者加入时,支持密钥份额的刷新。\n\n实现步骤\n初始化:设置阈值参数和参与方数量。\n密钥生成:使用秘密共享算法生成私钥的多个份额,并分配给不同的参与方。\n密钥份额刷新:在密钥份额丢失或有新参与方加入时,重新分配或生成新的密钥份额,并更新现有份额。\n聚合签名:在达到预设的阈值数量后,聚合各参与方的中间值,生成最终的签名。\n签名验证:使用相应的公钥验证生成的签名是否有效。\n\n代码实践\n初始化:设置加密类型、阈值参数和参与方数量。\n\n参考官方测试代码\n支持的加密类型\n\n// curve 决定采用哪种椭圆曲线来生成对应密钥setUp1 := dkg.NewSetUp(1, 3, curve) \n\n密钥生成:使用秘密共享算法生成私钥的多个份额,并分配给不同的参与方。\n// KeyStep3Data 即 私钥的分片,示例是生成3个分片func (k *keys) GenKey() (mpcKeys []*tss.KeyStep3Data, err error) {\tsetUp1 := dkg.NewSetUp(1, 3, k.curve)\tsetUp2 := dkg.NewSetUp(2, 3, k.curve)\tsetUp3 := dkg.NewSetUp(3, 3, k.curve)\tmsgs1_1, err := setUp1.DKGStep1()\tif err != nil {\t\treturn nil, err\t}\tmsgs2_1, err := setUp2.DKGStep1()\tif err != nil {\t\treturn nil, err\t}\tmsgs3_1, err := setUp3.DKGStep1()\tif err != nil {\t\treturn nil, err\t}\tmsgs1_2_in := []*tss.Message{msgs2_1[1], msgs3_1[1]}\tmsgs2_2_in := []*tss.Message{msgs1_1[2], msgs3_1[2]}\tmsgs3_2_in := []*tss.Message{msgs1_1[3], msgs2_1[3]}\tmsgs1_2, err := setUp1.DKGStep2(msgs1_2_in)\tif err != nil {\t\treturn nil, err\t}\tmsgs2_2, err := setUp2.DKGStep2(msgs2_2_in)\tif err != nil {\t\treturn nil, err\t}\tmsgs3_2, err := setUp3.DKGStep2(msgs3_2_in)\tif err != nil {\t\treturn nil, err\t}\tmsgs1_3_in := []*tss.Message{msgs2_2[1], msgs3_2[1]}\tmsgs2_3_in := []*tss.Message{msgs1_2[2], msgs3_2[2]}\tmsgs3_3_in := []*tss.Message{msgs1_2[3], msgs2_2[3]}\tp1SaveData, err := setUp1.DKGStep3(msgs1_3_in)\tif err != nil {\t\treturn nil, err\t}\tp2SaveData, err := setUp2.DKGStep3(msgs2_3_in)\tif err != nil {\t\treturn nil, err\t}\tp3SaveData, err := setUp3.DKGStep3(msgs3_3_in)\tif err != nil {\t\treturn nil, err\t}\tmpcKeys = append(mpcKeys, p1SaveData, p2SaveData, p3SaveData)\treturn mpcKeys, nil}\n\n密钥份额刷新:在密钥份额丢失或有新参与方加入时,重新分配或生成新的密钥份额,并更新现有份额。\nfunc (k *keys) Refresh(key1, key2 *tss.KeyStep3Data) (mpcKeys []*tss.KeyStep3Data, err error) {\tif key1.Id == key2.Id {\t\treturn nil, errors.New("key1 and key2 must be different")\t}\tdevoteList := [2]int{key1.Id, key2.Id}\toldKeys := make([]*tss.KeyStep3Data, 3)\tfor i := 1; i <= 3; i++ {\t\tif i == key1.Id {\t\t\toldKeys[i-1] = key1\t\t} else if i == key2.Id {\t\t\toldKeys[i-1] = key2\t\t} else {\t\t\toldKeys[i-1] = &tss.KeyStep3Data{\t\t\t\tId: i,\t\t\t\tShareI: nil,\t\t\t\tPublicKey: key1.PublicKey,\t\t\t\tChainCode: key1.ChainCode,\t\t\t\tSharePubKeyMap: nil,\t\t\t}\t\t}\t}\trefresh1 := reshare.NewRefresh(1, 3, devoteList, oldKeys[0].ShareI, oldKeys[0].PublicKey)\trefresh2 := reshare.NewRefresh(2, 3, devoteList, oldKeys[1].ShareI, oldKeys[1].PublicKey)\trefresh3 := reshare.NewRefresh(3, 3, devoteList, oldKeys[2].ShareI, oldKeys[2].PublicKey)\tmsgs1_1, err := refresh1.DKGStep1()\tif err != nil {\t\treturn nil, err\t}\tmsgs2_1, err := refresh2.DKGStep1()\tif err != nil {\t\treturn nil, err\t}\tmsgs3_1, err := refresh3.DKGStep1()\tif err != nil {\t\treturn nil, err\t}\tmsgs1_2_in := []*tss.Message{msgs2_1[1], msgs3_1[1]}\tmsgs2_2_in := []*tss.Message{msgs1_1[2], msgs3_1[2]}\tmsgs3_2_in := []*tss.Message{msgs1_1[3], msgs2_1[3]}\tmsgs1_2, err := refresh1.DKGStep2(msgs1_2_in)\tif err != nil {\t\treturn nil, err\t}\tmsgs2_2, err := refresh2.DKGStep2(msgs2_2_in)\tif err != nil {\t\treturn nil, err\t}\tmsgs3_2, err := refresh3.DKGStep2(msgs3_2_in)\tif err != nil {\t\treturn nil, err\t}\tmsgs1_3_in := []*tss.Message{msgs2_2[1], msgs3_2[1]}\tmsgs2_3_in := []*tss.Message{msgs1_2[2], msgs3_2[2]}\tmsgs3_3_in := []*tss.Message{msgs1_2[3], msgs2_2[3]}\tp1SaveData, err := refresh1.DKGStep3(msgs1_3_in)\tif err != nil {\t\treturn nil, err\t}\tp2SaveData, err := refresh2.DKGStep3(msgs2_3_in)\tif err != nil {\t\treturn nil, err\t}\tp3SaveData, err := refresh3.DKGStep3(msgs3_3_in)\tif err != nil {\t\treturn nil, err\t}\tmpcKeys = append(mpcKeys, p1SaveData, p2SaveData, p3SaveData)\tfor i := range mpcKeys {\t\tmpcKeys[i].ChainCode = key1.ChainCode\t}\treturn mpcKeys, nil}\n\n聚合签名:在达到预设的阈值数量后,聚合各参与方的中间值,生成最终的签名。\n// ECDSA签名示例func (k *keys) Sign(key1, key2 *tss.KeyStep3Data, msg []byte, pariPrivate, preParams string) (string, string, error) {\tif key1 == nil || key2 == nil {\t\treturn "", "", fmt.Errorf("key1 or key2 is nil")\t}\tif key1.Id == key2.Id {\t\treturn "", "", fmt.Errorf("key1.Id == key2.Id")\t}\tif key1.Id > key2.Id {\t\tkey1.Id = key2.Id\t\tkey2.Id = key1.Id\t}\tmsgHash, err := Sha256Hash(msg)\tif err != nil {\t\treturn "", "", fmt.Errorf("Sha256Hash error.err = %s", err.Error())\t} r, s, err := signMsgECDSA(key1, key2, msgHash, pariPrivate, preParams, k.curve) if err != nil { return "", "", fmt.Errorf("signMsgECDSA error.err = %s", err.Error()) }\treturn hex.EncodeToString(r.Bytes()), hex.EncodeToString(s.Bytes()), nil}func Sha256Hash(msg []byte) ([]byte, error) {\thash := sha256.New()\t_, err := hash.Write(msg)\tif err != nil {\t\treturn nil, fmt.Errorf("hash.Write(msgs) error, err = %s ", err.Error())\t}\treturn hash.Sum(nil), nil}// signMsgECDSA 使用ECDSA算法签名消息。// key1, key2: TSS密钥的第三步数据。// msgHash: 需要签名的消息的哈希值。// strPaillerKeys: Paillier公私钥对的字符串表示。// strPreParams: 预计算参数的字符串表示。// curve: 用于ECDSA的椭圆曲线。// 返回签名的r和s值,以及可能的错误。func signMsgECDSA(key1, key2 *tss.KeyStep3Data, msgHash []byte, strPaillerKeys, strPreParams string, curve elliptic.Curve) (*big.Int, *big.Int, error) {\t// 根据传入的预计算参数字符串生成预计算参数对象。\tvar param *keygen.PreParams\tif strPreParams == "" {\t\tparam = keygen.GeneratePreParams()\t} else {\t\tparam = &keygen.PreParams{}\t\tif err := json.Unmarshal([]byte(strPreParams), param); err != nil {\t\t\treturn nil, nil, fmt.Errorf("preParams Unmarshal error, err = %s ", err.Error())\t\t}\t}\t// 根据传入的Paillier密钥字符串生成Paillier私钥对象。\tvar paiPrivateKey *paillier.PrivateKey\tvar err error\tif strPaillerKeys == "" {\t\tpaiPrivateKey, _, err = paillier.NewKeyPair(8)\t\tif err != nil {\t\t\treturn nil, nil, fmt.Errorf("paillier.NewKeyPair error, err = %s ", err.Error())\t\t}\t} else {\t\tif err := json.Unmarshal([]byte(strPaillerKeys), paiPrivateKey); err != nil {\t\t\treturn nil, nil, fmt.Errorf("parillerKey Unmarshal error, err = %s ", err.Error())\t\t}\t}\t// 执行P1步骤,生成P1输出数据。\tp1Dto, errP := keygen.P1(key1.ShareI, paiPrivateKey, key1.Id, key2.Id, param)\tif errP != nil {\t\treturn nil, nil, fmt.Errorf("keygen.P1 error, err = %s ", errP.Error())\t}\t// 将key2的公钥转换为EC点对象。\tpublicKey, err := curves.NewECPoint(curve, key2.PublicKey.X, key2.PublicKey.Y)\tif err != nil {\t\treturn nil, nil, fmt.Errorf(" curves.NewECPoint pubKey error, err = %s ", err.Error())\t}\t// 执行P2步骤,生成P2保存数据。\tp2SaveData, err := keygen.P2(key2.ShareI, publicKey, p1Dto, key1.Id, key2.Id)\tif err != nil {\t\treturn nil, nil, fmt.Errorf("keygen.P2 error, err = %s ", err.Error())\t}\t//生成TSS密钥。 这部分应该是主钥\ttssKey, err := bip32.NewTssKey(p2SaveData.X2, key2.PublicKey, key2.ChainCode)\tif err != nil {\t\treturn nil, nil, fmt.Errorf("NewTssKey error, err = %s ", err.Error())\t}\t// 从TSS密钥中获取分享I值。\tx2 := tssKey.ShareI()\t// 创建ECDSA公钥对象。\ttssPubKey := &ecdsa.PublicKey{Curve: curve, X: tssKey.PublicKey().X, Y: tssKey.PublicKey().Y}\t// 初始化P1和P2签名阶段。\tp1 := ecdsa_sign.NewP1(tssPubKey, hex.EncodeToString(msgHash), paiPrivateKey)\tp2 := ecdsa_sign.NewP2(x2, p2SaveData.E_x1, tssPubKey, p2SaveData.PaiPubKey, hex.EncodeToString(msgHash))\t// 执行P1和P2的Step1步骤。\tcommit, _ := p1.Step1()\tbobProof, R2, _ := p2.Step1(commit)\t// 执行P1的Step2步骤,使用P2的Step1输出。\tproof, cmtD, _ := p1.Step2(bobProof, R2)\tE_k2_h_xr, _ := p2.Step2(cmtD, proof)\t// 执行P1的最后一步,生成签名的r和s值。\tr, s, _ := p1.Step3(E_k2_h_xr)\treturn r, s, nil}\n\n签名验证:使用相应的公钥验证生成的签名是否有效。\n// test.gokeys, _ := tt.args.GenKey()reKeys, _ := tt.args.Refresh(keys[0], keys[1])got, got1, err := tt.args.Sign(reKeys[1], reKeys[2], []byte("hello"), "", "")if (err != nil) != tt.wantErr { t.Errorf("keys.Sign() error = %v, wantErr %v", err, tt.wantErr) return}msgHash, _ := Sha256Hash([]byte("hello"))r, _ := hex.DecodeString(got)s, _ := hex.DecodeString(got1)// verify ecdsapublicKey := &ecdsa.PublicKey{Curve: reKeys[0].PublicKey.Curve, X: reKeys[0].PublicKey.X, Y: reKeys[0].PublicKey.Y}if ok := ecdsa.Verify(publicKey, msgHash, new(big.Int).SetBytes(r), new(big.Int).SetBytes(s)); !ok { t.Errorf("verify failed")}\n\n","categories":["wallet"],"tags":["blockchain","go","mpc"]}]