├── img ├── readme.md ├── 111.png ├── 1231.png ├── 640.jpg ├── 641.png ├── EOS1.png ├── eth60.png ├── gas.png ├── mpc1.jpg ├── mpc2.png ├── mpc3.png ├── mpc4.jpg ├── shang.png ├── sui.png ├── bnbsdk.png ├── ethSign.png ├── weixin.png ├── ERC20Sign.png ├── hdwallet1.jpg ├── hdwallet10.jpg ├── hdwallet11.jpg ├── hdwallet12.jpg ├── hdwallet13.jpg ├── hdwallet14.jpg ├── hdwallet15.jpg ├── hdwallet16.jpg ├── hdwallet2.png ├── hdwallet3.png ├── hdwallet4.png ├── hdwallet5.png ├── hdwallet6.png ├── hdwallet7.png ├── hdwallet8.png ├── hdwallet9.png ├── mnemonic.png ├── privateKey.png ├── staking1.png ├── staking2.jpg ├── staking3.jpg ├── staking4.jpg ├── staking5.jpg ├── PubKeyToAddr.jpg ├── PubKeyToAddr.png ├── coinExample.png ├── keystoreone.png ├── mnemonicfive.png ├── mnemonicfour.png ├── mnemonicone.png ├── mnemonictwo.png ├── preface_3.1.png ├── preface_4.1.png ├── preface_4.2.png ├── preface_5.1.png ├── preface_6.1.png ├── preface_8.1.png ├── preface_8.2.png ├── 1541492232(1).png ├── mnemonicthree.png ├── preface_3.2.1.png ├── preface_3.2.2.png ├── preface_3.2.3.png ├── preface_3.2.4.png ├── web3j_network.png ├── 1785959-77bbcea7174071c3.png ├── 1785959-7ce3000da8239b74.png ├── 1785959-be390ca284ed5f40.png └── 597d0a10-3de6-11e9-83a7-c387b6fac45c ├── walletBasic ├── .gitignore ├── resource ├── README.md └── readme.md ├── web3j └── README.md ├── bitcoinj └── README.md ├── contract └── README.md ├── projectTwo └── README.md ├── openWallet └── README.md ├── mnemonic.png ├── keystoreone.png ├── 1541492232(1).png ├── .github └── FUNDING.yml ├── savour-labs └── readme.md ├── iris └── readme.md ├── xrp └── README.md ├── README.md ├── chapter └── readme.md ├── projectOne ├── README.md └── EnReadme.md ├── mnemonic └── readme.md ├── Ripple └── README.md ├── ERC20 └── README.md ├── preface └── readme.md ├── basicWallet └── readme.md ├── Bitcoin ├── BitcoinWalletOfflineAddressGenerationAndCignatureCombat.md ├── BitcoinWalletSchnorrOfflineAddressGenerationAndSignature.md └── BitcoinWalletDevelopmentProcess.md ├── projectPractice ├── MPC Asset Management System.md ├── ETH centralized wallets.md ├── Solana centralized wallets.md └── Realize the staking function.md ├── tron └── README.md ├── biphd └── README.md ├── bnbPythonSdk └── README.md ├── EOS └── EOS README.md ├── dot └── README.md └── stacks └── readme.md /img/readme.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /walletBasic: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /resource/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /resource/readme.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /web3j/README.md: -------------------------------------------------------------------------------- 1 | # 第九章:web3j 2 | -------------------------------------------------------------------------------- /bitcoinj/README.md: -------------------------------------------------------------------------------- 1 | # 第十章:bitcoinj 2 | -------------------------------------------------------------------------------- /contract/README.md: -------------------------------------------------------------------------------- 1 | # 一文带你读懂合约审计 2 | -------------------------------------------------------------------------------- /projectTwo/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 第十章:钱包项目实战二 3 | -------------------------------------------------------------------------------- /openWallet/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 第十一章:开源钱包源码分析 3 | -------------------------------------------------------------------------------- /img/111.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/111.png -------------------------------------------------------------------------------- /img/1231.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/1231.png -------------------------------------------------------------------------------- /img/640.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/640.jpg -------------------------------------------------------------------------------- /img/641.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/641.png -------------------------------------------------------------------------------- /img/EOS1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/EOS1.png -------------------------------------------------------------------------------- /img/eth60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/eth60.png -------------------------------------------------------------------------------- /img/gas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/gas.png -------------------------------------------------------------------------------- /img/mpc1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mpc1.jpg -------------------------------------------------------------------------------- /img/mpc2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mpc2.png -------------------------------------------------------------------------------- /img/mpc3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mpc3.png -------------------------------------------------------------------------------- /img/mpc4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mpc4.jpg -------------------------------------------------------------------------------- /img/shang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/shang.png -------------------------------------------------------------------------------- /img/sui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/sui.png -------------------------------------------------------------------------------- /mnemonic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/mnemonic.png -------------------------------------------------------------------------------- /img/bnbsdk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/bnbsdk.png -------------------------------------------------------------------------------- /img/ethSign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/ethSign.png -------------------------------------------------------------------------------- /img/weixin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/weixin.png -------------------------------------------------------------------------------- /keystoreone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/keystoreone.png -------------------------------------------------------------------------------- /1541492232(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/1541492232(1).png -------------------------------------------------------------------------------- /img/ERC20Sign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/ERC20Sign.png -------------------------------------------------------------------------------- /img/hdwallet1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet1.jpg -------------------------------------------------------------------------------- /img/hdwallet10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet10.jpg -------------------------------------------------------------------------------- /img/hdwallet11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet11.jpg -------------------------------------------------------------------------------- /img/hdwallet12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet12.jpg -------------------------------------------------------------------------------- /img/hdwallet13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet13.jpg -------------------------------------------------------------------------------- /img/hdwallet14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet14.jpg -------------------------------------------------------------------------------- /img/hdwallet15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet15.jpg -------------------------------------------------------------------------------- /img/hdwallet16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet16.jpg -------------------------------------------------------------------------------- /img/hdwallet2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet2.png -------------------------------------------------------------------------------- /img/hdwallet3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet3.png -------------------------------------------------------------------------------- /img/hdwallet4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet4.png -------------------------------------------------------------------------------- /img/hdwallet5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet5.png -------------------------------------------------------------------------------- /img/hdwallet6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet6.png -------------------------------------------------------------------------------- /img/hdwallet7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet7.png -------------------------------------------------------------------------------- /img/hdwallet8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet8.png -------------------------------------------------------------------------------- /img/hdwallet9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/hdwallet9.png -------------------------------------------------------------------------------- /img/mnemonic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mnemonic.png -------------------------------------------------------------------------------- /img/privateKey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/privateKey.png -------------------------------------------------------------------------------- /img/staking1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/staking1.png -------------------------------------------------------------------------------- /img/staking2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/staking2.jpg -------------------------------------------------------------------------------- /img/staking3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/staking3.jpg -------------------------------------------------------------------------------- /img/staking4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/staking4.jpg -------------------------------------------------------------------------------- /img/staking5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/staking5.jpg -------------------------------------------------------------------------------- /img/PubKeyToAddr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/PubKeyToAddr.jpg -------------------------------------------------------------------------------- /img/PubKeyToAddr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/PubKeyToAddr.png -------------------------------------------------------------------------------- /img/coinExample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/coinExample.png -------------------------------------------------------------------------------- /img/keystoreone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/keystoreone.png -------------------------------------------------------------------------------- /img/mnemonicfive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mnemonicfive.png -------------------------------------------------------------------------------- /img/mnemonicfour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mnemonicfour.png -------------------------------------------------------------------------------- /img/mnemonicone.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mnemonicone.png -------------------------------------------------------------------------------- /img/mnemonictwo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mnemonictwo.png -------------------------------------------------------------------------------- /img/preface_3.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_3.1.png -------------------------------------------------------------------------------- /img/preface_4.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_4.1.png -------------------------------------------------------------------------------- /img/preface_4.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_4.2.png -------------------------------------------------------------------------------- /img/preface_5.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_5.1.png -------------------------------------------------------------------------------- /img/preface_6.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_6.1.png -------------------------------------------------------------------------------- /img/preface_8.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_8.1.png -------------------------------------------------------------------------------- /img/preface_8.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_8.2.png -------------------------------------------------------------------------------- /img/1541492232(1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/1541492232(1).png -------------------------------------------------------------------------------- /img/mnemonicthree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/mnemonicthree.png -------------------------------------------------------------------------------- /img/preface_3.2.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_3.2.1.png -------------------------------------------------------------------------------- /img/preface_3.2.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_3.2.2.png -------------------------------------------------------------------------------- /img/preface_3.2.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_3.2.3.png -------------------------------------------------------------------------------- /img/preface_3.2.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/preface_3.2.4.png -------------------------------------------------------------------------------- /img/web3j_network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/web3j_network.png -------------------------------------------------------------------------------- /img/1785959-77bbcea7174071c3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/1785959-77bbcea7174071c3.png -------------------------------------------------------------------------------- /img/1785959-7ce3000da8239b74.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/1785959-7ce3000da8239b74.png -------------------------------------------------------------------------------- /img/1785959-be390ca284ed5f40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/1785959-be390ca284ed5f40.png -------------------------------------------------------------------------------- /img/597d0a10-3de6-11e9-83a7-c387b6fac45c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/blockchain-wallet/HEAD/img/597d0a10-3de6-11e9-83a7-c387b6fac45c -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [ guoshijiang ] 4 | custom: ['https://www.wenwoha.com/', 'https://chaineye.info/', 'https://gingernet.vip/'] 5 | -------------------------------------------------------------------------------- /savour-labs/readme.md: -------------------------------------------------------------------------------- 1 | # SavourLabs 钱包项目 2 | 3 | - 后端对接链的服务:https://github.com/savour-labs/savour-hd 4 | - 钱包 Sdk: https://github.com/savour-labs/savour-snow 5 | - 社交恢复组件:https://github.com/savour-labs/key-locker 6 | - 社交恢复组件密码学算法: https://github.com/savour-labs/savour-secret 7 | -------------------------------------------------------------------------------- /iris/readme.md: -------------------------------------------------------------------------------- 1 | # IRIS 主链钱包开发 2 | 3 | ### 一. IRIS 简介 4 | 5 | IRISnet是边界智能与Cosmos团队以及万云合作打造的区块链跨链网络,将是Cosmos生态中的第一个区域性枢纽(Hub),并专注于为分布式商业应用提供基础设施和协议。 6 | 7 | IRISnet将面向服务的基础设施融入到Cosmos网络中,支持对包括公链、联盟链以及现有传统商业系统的集成从而实现互联互通。通过对Cosmos 的跨链协议进行创新扩展,IRISnet允许数据及复杂计算跨异构网络被调用。就像忠实地在人间和天堂传递信息的希腊彩虹女神Iris,IRISnet 的目标是成为链接数字经济和实体经济,支持构建复杂分布式商业应用的下一代公链。 8 | 9 | ### 二. IRIS 主网节点搭建 10 | 11 | -------------------------------------------------------------------------------- /xrp/README.md: -------------------------------------------------------------------------------- 1 | # 第十章:Ripple钱包开发 2 | 3 | ### 1. nodeJS 生成 ripple 地址 4 | 5 | 6 | const RippleAPI = require('ripple-lib').RippleAPI; 7 | var test_server = 'wss://s2.ripple.com'; 8 | var keypairs = require('ripple-keypairs'); 9 | const api = new RippleAPI({ 10 | server: test_server 11 | }); 12 | 13 | api.connect().then(() => { 14 | return api.generateAddress(); 15 | }).then(address_info => { 16 | console.log("Secret: " + address_info.secret); 17 | console.log("Address: " + address_info.address); 18 | var keypair = keypairs.deriveKeypair(address_info.secret); 19 | var privateKey = keypair.privateKey; 20 | console.log("Private key: " + privateKey); 21 | var publicKey = keypair.publicKey; 22 | console.log("Public key: " + publicKey); 23 | var address = keypairs.deriveAddress(keypair.publicKey); 24 | console.log("Address: " + address); 25 | }).then(() => { 26 | return api.disconnect(); 27 | }).then(() => { 28 | console.log('done and disconnected.'); 29 | }).catch(console.error); 30 | 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 区块链钱包技术指南 2 | 3 | 本教程由 The Web3 社区出品, DappLink 赞助 4 | 5 | [![DappLink](https://raw.githubusercontent.com/eniac-x-labs/.github/main/profile/dapplink.jpeg)](https://www.dapplink.xyz/zh) 6 | 7 | 随着区块链技术的发展,伴随而来的巨大经济效益也是日益突显,区块链技术离我们的生活也越来越近,近期兴起的币市风暴,想必了解区块链的人都略知一二。我们都知道,目前的区块链技术主要有两大阵营,币圈和链圈,币圈主要以公链技术、钱包技术延伸出一系列的公司和产品,而链圈主要的联盟链技术。我们此书讲解的内容主要还是属于币圈的技术,书中会涉及到公链的一些技术,但书的主要内容还区块链钱包技术。 8 | 9 | 10 | 本书所要讲授的内容就是目前最流行的技术之一的区块链钱包技术,我们将会从基础知识开始讲授,一直深入到钱包技术的核心,最后的章节将做两个笔者主导开发的区块链钱包技术实战的讲授。虽说我写本书的时候会从最基本的内容讲,但是编程语言层面的东西本书不会过多的讲解。希望阅读本书的读者可以自己把本书涉及到的编程语言、网络协议和加密算法提前了解一下。本书针对区块链钱包的开发者和对区块链钱包技术感兴趣的人。 11 | 12 | 13 | 阅读本书之前,你可能需要对下面的技术和编程语言有所了解: 14 | 15 | * 公链技术(尤其目前公链支持的账户体系及时,当然本书对这部分的内容会做详细的介绍) 16 | * 加密算法 17 | * Golang(目前区块链技术中使用最广泛的编程语言之一) 18 | * Java(承载钱包的业务逻辑) 19 | * Nodejs(钱包本地账户,交易签名) 20 | * electron(前端桌面框架,在项目实战中会用到) 21 | * Vue(前端框架,项目实战中将会用到) 22 | * Nginx 23 | * 数据库技术 24 | * 网络通信技术 25 | * 点对点网络通信 26 | * IOS和Android 27 | 28 | 涉及到的技术远远不只上面这些,上面只是钱包技术中使用到的比较重要的技术。 29 | 30 | 31 | 笔者之所以写这本书,是为了广大的区块链爱好者可以更好的学习块链钱包技术,目前网络上讲解区块链钱包的文章,可谓少之又少,而且质量也是参差不齐。影响广大区块链爱好者的理解与学习。现在掌握区块链钱包技术的人才并不多,市场上也没有一本把区块链钱包技术介绍透彻的书,当然,笔者也不一定能把这个技术介绍得很透彻,笔者的水平有限,如果本书中有什么错误的地方,欢迎大家交流指正。 32 | 33 | 34 | [点击开始阅读本书](https://github.com/guoshijiang/blockchain-wallet/blob/master/chapter/readme.md) 35 | 36 | 37 | ### 有兴趣的朋友可以加笔者的微信,我们一起交流区块链技术,区块链钱包技术 38 | 39 | .: 40 | ![.: 41 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/weixin.png) 42 | 43 | 44 | ### 关于本书 45 | 46 | 目前本书在写作阶段,书籍的版本暂设为V1.0.0版,本书将一直跟随区块链钱包技术的发展,随时更新,本书的内容将会在笔者的博客上同步更新,可能内容上不太一致,希望读者多多包涵。 47 | 48 | 49 | ### The Web3 社区简介 50 | The Web3 是一个专注 Web3 技术解决方案设计与开发、技术教程设计与开发、Web3 项目投研分析和 Web3 项目孵化,旨在将开发者,创业者,投资者和项目方联系在一起的社区。 51 | 52 | #### The web3 业务范围 53 | 54 | - 技术服务:提供交易所钱包,HD 钱包,硬件钱包,MPC 托管钱包,Dapps, 质押协议,L1,L2 ,L3 公链,数据可用层(DA)和中心化交易所技术开发服务。 55 | - 技术培训:提供个人技术成长和企业技术培训服务 56 | - 开发者活动承接:各种线下线上黑客松和开发者 meetup 活动承接 57 | - 除此之外,我们还和 "磐石安全实验室" 深入合作,开展去中心化安全审计服务 58 | 59 | ### The Web3 社区官方链接 60 | - github: https://github.com/the-web3 61 | - X: https://twitter.com/0xtheweb3cn 62 | - telegram: https://t.me/+pmoh3D4uTAFjNWM1 63 | - discord: https://discord.gg/muhuXRsK 64 | - the web3 官网:https://thewebthree.xyz/ 65 | - the web3 技术服务网站:https://web.thewebthree.xyz/ 66 | 67 | -------------------------------------------------------------------------------- /chapter/readme.md: -------------------------------------------------------------------------------- 1 | 2 | ## 本书目录结构 3 | 4 | ### [第一章:前言](https://github.com/guoshijiang/blockchain-wallet/blob/master/preface/readme.md) 5 | 6 | ### [第二章:钱包基础知识](https://github.com/guoshijiang/blockchain-wallet/tree/master/basicWallet) 7 | 8 | ### [第三章:助记词](https://github.com/guoshijiang/blockchain-wallet/tree/master/mnemonic) 9 | 10 | ### [第四章:BIP钱包分层协议](https://github.com/guoshijiang/blockchain-wallet/tree/master/biphd) 11 | 12 | ### [第五章:以太坊钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/Ethereum) 13 | 14 | ### [第六章:比特币钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/Bitcoin) 15 | 16 | ### [第七章:EOS钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/EOS) 17 | 18 | ### [第八章:ERC20代币钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/ERC20) 19 | 20 | ### [第九章:omni-USDT钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/Omni) 21 | 22 | ### [第十章:Ripple钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/xrp) 23 | 24 | ### [第十一章:BNB 钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/bnb) 25 | 26 | ### [第十二章:IRIS 主链钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/iris) 27 | 28 | ### [第十三章:COSMOS 主链钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/cosmos) 29 | 30 | ### [第十三章:BTM 主链钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/cosmos) 31 | 32 | ### [第十三章:TRX 主链钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/cosmos) 33 | 34 | ### [第十三章:Grin 主链钱包开发](https://github.com/guoshijiang/blockchain-wallet/tree/master/cosmos) 35 | 36 | ### [第十四章:防止钱包私钥丢失](https://github.com/guoshijiang/blockchain-wallet/tree/master/Ripple) 37 | 38 | ### [第十五章:blockchain-wallet-sdk使用和代码详解](https://github.com/guoshijiang/blockchain-wallet/tree/master/biwork) 39 | 40 | ### [第十六章:钱包项目实战一](https://github.com/guoshijiang/blockchain-wallet/tree/master/projectOne) 41 | 42 | ### [第十七章:钱包项目实战二](https://github.com/guoshijiang/blockchain-wallet/tree/master/projectTwo) 43 | 44 | ### [第十八章:开源钱包源码分析](https://github.com/guoshijiang/blockchain-wallet/tree/master/openWallet) 45 | 46 | ### [第十九章:BNB python-sdk 源码分析](https://github.com/guoshijiang/blockchain-wallet/tree/master/bnbPythonSdk) 47 | 48 | -------------------------------------------------------------------------------- /projectOne/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 第九章:钱包项目实战一 3 | 4 | ## linkeye钱包 5 | 6 | Linkeye 钱包是笔者独立开发的一个项目,对接的是 Linkeye 的公链项目。这个项目比较小巧,而且代码量也不大。下面笔者就以 Linkeye 为例给讲解项目想的开发流程,Linkeye 是一个非确定性钱包,这个钱包没有遵循 Bip44 协议。 7 | 8 | ## linkeye钱包项目架构分析 9 | 10 | ### 一.简述 11 | 12 | linkeye钱包是对接linkeye公链的钱包项目,不能对接其他的公链。linkeye钱包是nodejs, electron和vue开发的本地桌面钱包,整个账户体系的信息都在用户自己的设备上。nodeJs是一个本地的服务,vue是渲染进程,nodejs本地服务和vue渲染进程之间通过electron的主进程和渲染进程通信机制通信。用户的账户体系数据存储在本地sqlite3数据库中。整个项目分为创建账户生成keystore模块,导出私钥模块,导入私钥模块,账户体系模块,转账记录模块,转账模块,转账确认模块。 13 | 14 | 15 | linkeye-wallet架构图: 16 | ![linkeye-wallet架构图: 17 | ](https://github.com/guoshijiang/go-ethereum-code-analysis/blob/master/wallet/linkeye-wallet/img/linkeye-wallet.png "linkeye-wallet架构图: 18 | ") 19 | 20 | 本项目中把IPC通信的机制封装了,渲染进程的封装代码如下: 21 | 22 | 23 | const { ipcRenderer } = require('electron') 24 | const { 25 | CLIENT_NORMAL_MSG, 26 | CRAWLER_NORMAL_MSG, 27 | } = require('./../../constants/constants') 28 | 29 | const ipcService = Object.create(null) 30 | const callbackCache = [] 31 | 32 | ipcService.install = Vue => { 33 | Vue.prototype.$ipcRenderer = { 34 | send: (msgType, msgData) => { 35 | ipcRenderer.send(CLIENT_NORMAL_MSG, { 36 | type: msgType, 37 | data: msgData, 38 | }) 39 | }, 40 | on: (type, callback) => { 41 | callbackCache.push({ 42 | type, 43 | callback, 44 | }) 45 | }, 46 | detach: type => { 47 | const idx = callbackCache.findIndex(v => v.type === type) 48 | idx > -1 ? callbackCache.splice(idx, 1) : console.error(`error type ${type}`) 49 | }, 50 | } 51 | ipcRenderer.on(CRAWLER_NORMAL_MSG, (sender, msg) => { 52 | callbackCache.forEach(cache => { 53 | if (cache.type === msg.type) { 54 | cache.callback && cache.callback(msg.data) 55 | } 56 | }) 57 | }) 58 | } 59 | 60 | export default ipcService 61 | 62 | 63 | 以上代码中将调用的事件放到一个事件队列中(其实是一个数组),当事件调用完成后,detach一下,就可以把事件从数组中删除了。 64 | 65 | 在node服务,每个接口请求linkeye钱包针对请求封装了一个IPC通信的类,代码都比较类似,我们这里就讲其中的一个IPC类 66 | 67 | import queryBlock from '../block/queryBlock' 68 | 69 | const {CLIENT_NORMAL_MSG, CRAWLER_NORMAL_MSG,} = require('../../constants/constants') 70 | 71 | export default class queryBlockIpc { 72 | constructor(listener, sender) { 73 | this.listener = listener 74 | this.sender = sender 75 | this.addListener(CLIENT_NORMAL_MSG, this.handleFn.bind(this)) 76 | this.handlerQueryBlockIpc = queryBlock(this) 77 | } 78 | 79 | handleFn(event, data) { 80 | try { 81 | this.handlerQueryBlockIpc[data.type](event, data.data) 82 | } catch (error) { 83 | console.error('handler event error:' + error.message) 84 | } 85 | } 86 | 87 | addListener(chanel, cb) { 88 | this.listener.on(chanel, cb) 89 | } 90 | 91 | _sendMsg(chanel, msgBody) { 92 | this.sender.send(chanel, msgBody) 93 | } 94 | 95 | sendToClient(type, data) { 96 | this._sendMsg(CRAWLER_NORMAL_MSG, { 97 | type, 98 | data, 99 | }) 100 | } 101 | } 102 | 103 | 以上代码是扫块的IPC类,sendToClient是将处理好的消息发送给渲染进程,addListener将处理过的事件放到监听器 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /projectOne/EnReadme.md: -------------------------------------------------------------------------------- 1 | # Chapter 9: Wallet Project Practice 1 2 | 3 | ## linkeye wallet 4 | 5 | Linkeye wallet is a project independently developed by the author, and it is connected to Linkeye's public chain project. This project is relatively small and the amount of code is not large. Below, the author will use Linkeye as an example to explain the development process of the project. Linkeye is a non-deterministic wallet that does not follow the Bip44 protocol. 6 | 7 | ## Linkeye wallet project architecture analysis 8 | 9 | ### 1. Brief description 10 | 11 | Linkeye wallet is a wallet project that connects to the Linkeye public chain and cannot connect to other public chains. Linkeye wallet is a local desktop wallet developed by nodejs, electron and vue. The information of the entire account system is on the user's own device. nodeJs is a local service, and vue is the rendering process. The nodejs local service and the vue rendering process communicate through the main process and rendering process communication mechanism of electron. The user's account system data is stored in a local sqlite3 database. The entire project is divided into account creation keystore module, private key export module, private key import module, account system module, transfer record module, transfer module, and transfer confirmation module. 12 | 13 | 14 | linkeye-wallet architecture diagram: 15 | ![linkeye-wallet architecture diagram: 16 | ](https://github.com/guoshijiang/go-ethereum-code-analysis/blob/master/wallet/linkeye-wallet/img/linkeye-wallet.png "linkeye-wallet architecture diagram: 17 | ") 18 | 19 | In this project, the IPC communication mechanism is encapsulated. The encapsulation code of the rendering process is as follows: 20 | 21 | 22 | const { ipcRenderer } = require('electron') 23 | const { 24 | CLIENT_NORMAL_MSG, 25 | CRAWLER_NORMAL_MSG, 26 | } = require('./../../constants/constants') 27 | 28 | const ipcService = Object.create(null) 29 | const callbackCache = [] 30 | 31 | ipcService.install = Vue => { 32 | Vue.prototype.$ipcRenderer = { 33 | send: (msgType, msgData) => { 34 | ipcRenderer.send(CLIENT_NORMAL_MSG, { 35 | type: msgType, 36 | data: msgData, 37 | }) 38 | }, 39 | on: (type, callback) => { 40 | callbackCache.push({ 41 | type, 42 | callback, 43 | }) 44 | }, 45 | detach: type => { 46 | const idx = callbackCache.findIndex(v => v.type === type) 47 | idx > -1 ? callbackCache.splice(idx, 1) : console.error(`error type ${type}`) 48 | }, 49 | } 50 | ipcRenderer.on(CRAWLER_NORMAL_MSG, (sender, msg) => { 51 | callbackCache.forEach(cache => { 52 | if (cache.type === msg.type) { 53 | cache.callback && cache.callback(msg.data) 54 | } 55 | }) 56 | }) 57 | } 58 | 59 | export default ipcService 60 | 61 | 62 | In the above code, the called event is placed in an event queue (actually an array). When the event call is completed, detach it and the event can be deleted from the array. 63 | 64 | In the node service, each interface request linkeye wallet encapsulates an IPC communication class for the request. The codes are relatively similar. We will talk about one of the IPC classes here. 65 | 66 | import queryBlock from '../block/queryBlock' 67 | 68 | const {CLIENT_NORMAL_MSG, CRAWLER_NORMAL_MSG,} = require('../../constants/constants') 69 | 70 | export default class queryBlockIpc { 71 | constructor(listener, sender) { 72 | this.listener = listener 73 | this.sender = sender 74 | this.addListener(CLIENT_NORMAL_MSG, this.handleFn.bind(this)) 75 | this.handlerQueryBlockIpc = queryBlock(this) 76 | } 77 | 78 | handleFn(event, data) { 79 | try { 80 | this.handlerQueryBlockIpc[data.type](event, data.data) 81 | } catch (error) { 82 | console.error('handler event error:' + error.message) 83 | } 84 | } 85 | 86 | addListener(chanel, cb) { 87 | this.listener.on(chanel, cb) 88 | } 89 | 90 | _sendMsg(chanel, msgBody) { 91 | this.sender.send(chanel, msgBody) 92 | } 93 | 94 | sendToClient(type, data) { 95 | this._sendMsg(CRAWLER_NORMAL_MSG, { 96 | type, 97 | data, 98 | }) 99 | } 100 | } 101 | 102 | The above code is the IPC class for scanning blocks. sendToClient sends the processed message to the rendering process, and addListener puts the processed event into the listener. 103 | -------------------------------------------------------------------------------- /mnemonic/readme.md: -------------------------------------------------------------------------------- 1 | 2 | # 第三章:助记词 3 | 4 | 现在区块链市场上的大部分钱包,都是通过助记词来备份钱包的。当然也有不少的钱包是通过私钥来备份钱包的。不管是通过助记词备份钱包,还是痛私钥备份钱包,其实在原理上都是大同小异。一般的钱包都是通过助记词生成随机数种子,然后再通过随机数种子生成根私钥,再通过BIP分层协议生成各个币种账户的私钥。因而助记词是一个钱包的起始段,也是一个钱包的重要技术组成部分。本章咱们将详细地讲解助记词相关的知识。 5 | 6 | ## 一.助记词原理 7 | 8 | 助记词并不是一个新概念,早在很多行业,就出现了助记词的概念。发音为“ne-manik”的最纯粹形式的助记符是一种字母,单词或关联模式,可让您轻松记住信息,并已被人类使用了数千年。换句话说,它可以是一个非常有用的工具,帮助我们记住我们需要记住的重要信息。 9 | 10 | 在比特币中,助记符采用相同的基本原则,即使用一组单词生成一个独特的钱包短语,为您提供一个人类可读的单词格式,以备份您的钱包进行恢复。用于生成这些短语的助记符代码在2013年通过比特币改进提案或BIP引入比特币。BIP描述了助记符代码或助记句的实现,一组容易记忆的单词,用于生成确定性钱包。它由两部分组成:生成助记符,并将其转换为二进制种子。 该种子可以稍后用于使用BIP-0032或类似方法生成确定性钱包。 11 | 12 | 一般来说私钥都有256位,以64个字母数字构成的16进制字符串表示。备份私钥的时候直接抄录64个字符显然是很容易产生错误的,而且易读性也很差,用户体验度显然就会降低,而助记词的出现刚好解决了这个尴尬的局面,助记词是明文私钥的另一种表现形式。助记词出现的目的是为了帮助用户记忆复杂的私钥 (64位的哈希值)。助记词一般由12、15、18、21个单词构成, 这些单词都取自一个固定词库, 其生成顺序也是按照一定算法而来, 所以用户没必要担心随便输入12个单词就会生成一个地址。虽然助记词和 Keystore 都可以作为私钥的另一种表现形式, 但与 Keystore 不同的是, 助记词是未经加密的私钥, 没有任何安全性可言, 任何人得到了你的助记词, 可以不费吹灰之力的夺走你的资产。 13 | 14 | 目前钱包分为两种:非确定性钱包和确定性钱包,非确定钱包就是随机生成多个私钥,钱包管理这些私钥。这种钱包私钥不易于管理。但如果设计钱包的人水平过高的话,也可以设计得很好。确定性钱包是通过种子来生成不同账户的私钥,我们只要记住助记词就行,用助记词生成种子,再去生成钱包相关的地址和私钥。 15 | 16 | 下面我们看看种子生成的流程 17 | 18 | .: 19 | ![.: 20 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/mnemonic.png) 21 | 22 | 23 | ### 1.从熵到助记词 24 | 25 | 26 | 随机生成一个128到258位的数字,叫做熵; 熵通过SHA256哈希得一个值,取前面的几位(熵长/32),记为y; 熵和y组成一个新的序列,将新序列以11位为一部分,已经预先定义2048个单词的字典做对应; 生成的有顺序的单词组就是助记词 27 | 28 | 如下图 29 | 30 | .: 31 | ![.: 32 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/mnemonicone.png) 33 | 34 | ### 2.从助记词生成种子 35 | 36 | 助记词表示长度为128至256位的熵。 通过使用密钥延伸函数PBKDF2,熵被用于导出较长的(512位)种子。 37 | PBKDF2的基本原理是通过一个伪随机函数(例如HMAC函数),把明文和一个盐值作为输入参数,然后重复进行运算,并最终产生密钥。如果重复的次数足够大,破解的成本就会变得很高。而盐值的添加也会增加“彩虹表”攻击的难度。 38 | 比特币钱包中,PBKDF2函数的第一个参数是助记词,第二个参数盐,由字符串常数“助记词”与可选的用户提供的密码字符串连接组成。使用HMAC-SHA512算法,使用2048次哈希来延伸助记符和盐参数,产生一个512位的值作为其最终输出。 这个512位的值就是种子。如图 39 | 40 | .: 41 | ![.: 42 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/mnemonictwo.png) 43 | 44 | ### 3.从种子到母密钥 45 | 46 | 512位分成平均分成两部分,左边的256位为母私钥,右边的256位为链码。母私钥、链码和索引号,CKD(child key derivation)函数去从母密钥衍生出子密钥。如图 47 | 48 | .: 49 | ![.: 50 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/mnemonicthree.png) 51 | 52 | 53 | ### 4.从母密钥到子密钥 54 | 55 | 母密钥、链码、索引合并在一起并且用HMAC-SHA512函数散列之后可以产生512位的散列。所得的散列可被拆分为两部分。散列右半部分的256位产出可以给子链当链码。左半部分256位散列以及索引码被加载在母私钥上来衍生子私钥。在图中,我们看到这个说明——索引集被设为0去生产母密钥的第0个子密钥(第一个通过索 引)。 56 | 57 | .: 58 | ![.: 59 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/mnemonicfour.png) 60 | 61 | ### 5.扩展密钥 62 | 63 | 母密钥和链码结合叫做扩展密钥,拥有扩展私钥可以推导出子私钥,扩展公钥可以推导出子公钥。拥有扩展公钥就可以推导出子公钥,在服务器不需要母私钥也可以,这样就更安全更方便。但是还有一个问题,那就是扩展公钥包含有链码,如果子私钥被知道或者被泄漏的话,链码就可以被用来衍生所有的其他子私钥。简单地泄露的私钥以及一个母链码,可以暴露所有的子密钥。更糟糕的是,子私钥与母链码可以用来推断母私钥。 64 | 为此,HD钱包使用一种叫做硬化衍生(hardened derivation)的替代衍生函数。这就“打破”了母公钥以及子链码之间的关系。这个硬化衍生函数使用了母私钥去推导子链码,而不是母公钥。这就在母/子顺序中创造了一道“防火墙”——有链码但并不能够用来推算子链码或者姊妹私钥。强化衍生函数看起来几乎与一般的衍生的子私钥相同,不同的是母私钥被用来输入散列函数中而不是母公钥。如图 65 | 66 | .: 67 | ![.: 68 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/mnemonicfive.png) 69 | 70 | 71 | ## 二.生成助记词 72 | 73 | ### 1.生成12个助记词 74 | 75 | var bip39 = require('bip39') 76 | var mnemonic = bip39.generateMnemonic() 77 | console.log(mnemonic); 78 | 79 | var bip39 = require('bip39') 80 | var mnemonic = bip39.generateMnemonic(128) 81 | console.log(mnemonic); 82 | 83 | ### 2.生成15个助记词 84 | 85 | var bip39 = require('bip39') 86 | var mnemonic = bip39.generateMnemonic(160) 87 | console.log(mnemonic); 88 | 89 | ### 3.生成18个助记词 90 | 91 | var bip39 = require('bip39') 92 | var mnemonic = bip39.generateMnemonic(192) 93 | console.log(mnemonic); 94 | 95 | ### 4.生成21个助记词 96 | 97 | var bip39 = require('bip39') 98 | var mnemonic = bip39.generateMnemonic(224) 99 | console.log(mnemonic); 100 | 101 | ### 5.生成24个助记词 102 | 103 | var bip39 = require('bip39') 104 | var mnemonic = bip39.generateMnemonic(256) 105 | console.log(mnemonic); 106 | 107 | ### 6.生成中文助记词 108 | 109 | var mnemonicS = bip39.generateMnemonic(128, null, bip39.wordlists.chinese_simplified); 110 | console.log(mnemonicS); 111 | 以上根据传值的的大小限定助记词的个数 112 | 113 | 目前助记词库能够生成“简体中文”,“繁体中文”,“英文”,“法文”,“意大利文”,“日文”,“韩文”和西班牙文。 114 | 115 | ## 三.助记词编解码 116 | 117 | ### 1.编码助记词 118 | 119 | var bip39 = require('bip39') 120 | var mnemonic = bip39.generateMnemonic() 121 | var encrytMnemonic = bip39.mnemonicToEntropy(mnemonic) 122 | console.log(encrytMnemonic); 123 | 124 | ### 2.解码助记词 125 | 126 | var bip39 = require('bip39') 127 | var mnemonic = bip39.generateMnemonic() 128 | console.log(mnemonic); 129 | var encrytMnemonic = bip39.mnemonicToEntropy(mnemonic) 130 | var word = bip39.entropyToMnemonic(encrytMnemonic) 131 | console.log(word); 132 | 133 | ## 四.生成随机数种子 134 | 135 | var mnemonicE = bip39.generateMnemonic() 136 | console.log(mnemonicE); 137 | var Seed= bip39.mnemonicToSeed(mnemonicE) 138 | var seedHex= bip39.mnemonicToSeedHex(mnemonicE) 139 | console.log(Seed); 140 | console.log(seedHex); 141 | 142 | ## 五.验证助记词 143 | 144 | var mnemonic = bip39.generateMnemonic() 145 | console.log(mnemonic); 146 | var bool = bip39.validateMnemonic(mnemonic) 147 | 148 | ## 六.助记词开源库 149 | 150 | https://github.com/bitcoinjs/bip39 151 | 152 | 153 | -------------------------------------------------------------------------------- /Ripple/README.md: -------------------------------------------------------------------------------- 1 | # 第十章:防止钱包私钥丢失 2 | 3 | ## 一.关于钱包的私钥 4 | 5 | 现在市场上的大部分数字货币钱包,如果你把助记词和钱包私钥搞丢了,整个钱包基本上算是丢失了。笔者就遇到过这样的问题,笔者使用biwork钱包,钱包里面有10000LET、3个EOS和0.45个以太坊,biwork钱包是一个去中心化的钱包,它的私钥和助记词编码串是存储在客户端。后来我不小心把手机的内屏和外屏的砸碎,修手机的费用已经超过我钱包中币的价值。当时我备份的助记词找不到了,到现在为止我这个币也没有拿出来。 6 | 7 | 做区块链钱包开发的开发者应该都知道,目前最流行的是HD钱包(确定性分层钱包),它支持多钱包,多币种,多账户。对于每个钱包,最核心的部分是助记词,助记词在客户端存储方式一个编码串;通过这个编码串可以恢复助记词,通过助记词又恢复整个账户。对于每个钱包,它还有一个主私钥可以恢复这个钱包;而对于每个账户,都拥有自己的私钥,通过私钥是完全能找回这个账户的。也可以转走这个钱包中的数字资产。 8 | 9 | 不管是助记词编码串,钱包私钥,还是每个账户的私钥,它都是一个十六进制的一个字符串。对于这个字符串呢,我们有很多种处理方式。 10 | 11 | ## 二.抗丢失的钱包助记词、钱包私钥、钱包账户私钥存储方案 12 | 13 | 去中心化的钱包,不管是助记词,钱包私钥,还是钱包账户的私钥,都是存储在客户端。一旦我们的备份存储助记词,钱包私钥,钱包账户的私钥的文件丢失,我们客户端的钱包损坏,对于加密数字资产持有者是致命的,这将意味着数字资产持有者将会损失所有的加密货币。 14 | 15 | 我们做一个产品的原则是让用户使用起来更简单,现在我们市场的数字资产钱包总是喜欢让用户自己去备份助记词或者私钥,实际上这种做法是比较愚蠢的。为什么这样说呢,对于一个非技术人员来说,助记词,私钥这些东西,在用户的心里面是没有什么概念的。你在钱包内部发出的提示语很多用户可能都懒得看一眼。总的一句话来说,目前市场的数字资产钱包使用是有门槛的。 16 | 17 | ### 1.从硬件角度解决这个问题 18 | 19 | 助记词(助记词可编码存储)或密钥在本地客户端生成之后,使用蓝牙的通信方式将密钥下发到小型硬件设备。 20 | 21 | .: 22 | ![.: 23 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/privateKey.png) 24 | 25 | 这种方式的优点是保证了私钥和助记词编码串存储的私密行,当客户端彻底崩溃的时候,助记词或私钥还可以通过小型的硬件设备来找回。助记词或私钥不再强烈依靠客户端,也不再依靠用户的备份。在这里面,硬件只是做了一个助记词或私钥的备份功能,钱包只有在助记词或密钥找回的时候才会依赖于小型硬件设备。其他的任何时候钱包是可以独立使用的。 26 | 27 | 当然,事情总是有对立面,解决问题的同时它也带来来一些问题。使用这种小型硬件会使钱包的造价和使用成本偏高。 28 | 29 | 5G 时代的到来将会使得物联网的高速发展,物联网的发展也将给区块链钱包私钥的存储带来很好的解决方案,将来你的私钥可以存储在很多自己家里的设备中,当你私钥丢失时,只要将家里的设备接入网络,你就可以找回私钥。当然,物联网中的“物”是处在网络环境中的,这也是有安全性问题的。 30 | 31 | #### 1.1.小型硬件解决密钥丢失方案详细设计 32 | 33 | 下面的图是小型硬件设备存储密钥的一个简单的逻辑设计图,数据传输的方式采用蓝牙传输,包括密钥的备份和恢复两个过程。 34 | 35 | .: 36 | ![.: 37 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/1231.png) 38 | 39 | 40 | 41 | ##### 密钥的备份过程 42 | 43 | 钱包通过助记词算法产生助记词,将助记词编码,并将编码串使用密码加密,然后通过蓝牙通信的方式将加密的助记词编码串传输到小型硬件设备;或者钱包生成私钥之后,用密码将私钥加密,然后通过蓝牙通信方式传输到小型硬件设备。这个过程称为密钥的备份过程。 44 | 45 | 46 | ##### 密钥的恢复过程 47 | 48 | 当用的承载钱包的设备坏,并且忘记助记词的情况下,就需要启动密钥的恢复过程。正常小型硬件设备里面会存储一些验证用户的数据,当小型硬件设备验证用户通过之后,就可以发起蓝牙连接。蓝牙连接完成后,硬件设备将加密的私钥或者加密的助记词编码串发送到钱包,钱包接到加密串之后,让用户输入密码解密编码串或者私钥。解密之后用户就可以使用助记词和私钥恢复自己的钱包了。 49 | 50 | 51 | ### 2.密钥共享托管 52 | 53 | 秘密的共享是信息安全研究的一个重要的分支,密码主要是为了对重要信息进行加密,保证信息的安全新,而秘密共享却可以保证密钥的安全性,它为密码学的发展提供了一个崭新的思路。秘密共享的原理是将秘密分成若干份,交给不同的人去保管,设定管理秘密的一定数量的人贡献出自己持有的秘密,就可以恢复秘密了。 54 | 55 | 秘密共享的概念最早由著名密码学家 Shamir 和 Blakley 于 1979 年分别独立提出并给出了各自的方案。Shamir 的( t,n) 门限方案基于 Lagrnage ( 拉格朗日) 插值法来实现,Blakley 的( t,n) 门限方案是利用多维空间点的性质来建立的。 56 | 57 | 秘密共享的概念一经提出,受到专家学者们的极大的关注与研究。 这些年取得的成果颇丰。大致可以分为如下几类。 58 | 59 | * 门限密钥共享 60 | 61 | 在( t,n) 门限秘密共享方案中,任何包含至 少 t 个参与者的集合都是授权子集,而包含 t-1 或更少参与者的集合都是非授权子集,实现( t, n) 门限秘密共享的方法除了 Shamir 和 Blakley 的方案外,还有基于中国剩余定理的 Asmuth-Bloom 法以及使用矩阵乘法的 Karnin-Greene Hellman 方法等。 62 | 63 | * 一般访问结构上秘密共享方案 64 | 65 | 门限方案是实现门限访问结构的秘密共享方案,对于其它更广泛的访问结构存在局限性, 如在 甲 、乙 、丙 、丁 四 个成员中共享秘 密 ,使甲和丁或乙和丙合作能恢复秘密,门限秘密共享方案就不能解决这样的情况. 针对这类问题,1987 年密码学研究人士提出了一般访问结构上的秘密共享方案。1988 年有人又提出了一个更简单有效的方法—单调电路构造法,并且证明了任何访问结构都能够通过完备的秘密共享方案加以实现。 66 | 67 | * 多重秘密共享方案 68 | 69 | 只需保护一个子秘密就可以实现多个秘密的共享,在多重秘密共享方案中每个参与者的子秘密可以使用多次,但是一次秘密共享过程只能共享一个秘密。 70 | 71 | * 多秘密共享方案 72 | 73 | 多重秘密共享解决了参与者的子秘密重用 的问题,但其在一次秘密共享过程中只能共享一个秘密。 74 | 75 | * 可验证秘密共享方案 76 | 77 | 参与秘密共享的成员可以通过公开变量验证自己所拥有的子秘密的正确性,从而有效地防止了分发者与参与者,以及参与者之间的相互欺骗的问题。可验证秘密共享方案分为交互式和非交互式两种。交互式可验证的秘密共享方案是指各个参与者在验证秘密份额的正确性时需要相互之间交换信息;非交互式可验证的秘密共享是指各个参与者在验证秘密份额的正确性时不需要相互之间交换信息。在应用方面,非交互式可验证秘密共享可以减少网络通信费用,降低秘密泄漏的机会,因此应用领域也更加广泛。 78 | 79 | * 动态秘密共享方案 80 | 81 | 动态秘密共享方案是 1990 年提出,它具有很好的安全性与灵活性,它允许新增或删除参与者、定期或不定期更新参与者的子秘密以及在不同的时间恢复不同的秘密等等. 以上是几种经典的秘密共享方案. 需要说明的是,一个具体的秘密共享方案往往是几个类型的集合体。 82 | 83 | * 其他秘密共享 84 | 85 | 量子秘密共享、可视秘密共享、 基于多分辨滤波的秘密共享、基于广义自缩序列的秘密共享。 86 | 87 | #### 2.1.经典秘密共享方案介绍 88 | 89 | ##### 2.1.1.shamir 门限秘密共享方案 90 | 91 | Shamir 的( t,n) 门限方案基于 Lagrnage ( 拉格朗日) 插值法来实现,简单地说,设秘密通过秘密共享算法分发给个成员共享,每一个成员持有一个子密钥也称为 shadow 或 Secret debris,如果满足: 92 | 93 | 任何不少于 t 个的有效成员使用他持有的正确的碎片都可以恢复秘密。 94 | 任何 t 个以下的成员集都无法恢复秘密。 95 | 我们称这种方案为 (t ,n) 门限秘密共享方案,简称为门限方案,t 称为方案的门限值。 96 | 97 | 作为各种秘密共享方案中最简单实用的门限秘密共享方案,(k,n) 门限秘密共享方案也是这些方案中最具有代表性和广泛应用的方案。 98 | 99 | 下面简单介绍一下这个方案。 100 | 101 | * 系统参数 102 | 103 | 假定 n 是参与者的数目,n 是门限值,p 是一个大素数要求 p > n 并且大于 p 秘密 s 的可能的最大取值;秘密空间与份额空间均为有限域 GF(p)。 104 | 105 | * 秘密分发 106 | 107 | 秘密分发者 D 给 n 个参与者 Pi(0 ≤ i ≤ n) 分配份额的过程,即方案的分配算法如下: 108 | 109 | 随机选择一个 GF(p) 上的 k - 1 次多项式 使得 f(0) = a0 = s要在个参与者中分享的秘密 D 对 f(x) 保密。 110 | 111 | D 在 Zp 中选择 n 个互不相同的非零元素 x1, x2, …, xn,计算 (0≤ i ≤ n)。 112 | 113 | 将 ( xi , yi ) 分配给参与者 Pi( 0 ≤ i ≤ n),值 xi 是公开的,yi 作为的秘密份额,不公开。 114 | 115 | * 秘密重构 116 | 117 | 给定任何 t 个点,不妨设为前 t 个点(x1, y1),(x2, y2),…, (xt, yt)。由插值公式: 118 | 119 | 120 | .: 121 | ![.: 122 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/111.png) 123 | 124 | 125 | Shamir 方案作为一种被广泛选用的门限方案,具有以下优点: 126 | 127 | 1. t 个秘密份额可以确定出整个多项式,并可计算出其他的秘密份额。 128 | 129 | 2. 在原有分享者的秘密份额保持不变的情况下,可以增加新的分享者,只要增加后分享者的总数不超过。 130 | 131 | 3. 还可以在原有共享密钥未暴露之前,通过构造常数项仍为共享密钥的具有新系数的次多项式,重新计算新一轮分享者的秘密份额,从而使得分享者原有的秘密份额作废。 132 | 133 | 但同时方案存在以下问题 134 | 135 | 1. 在秘密分发阶段,不诚实的秘密分发者可分发无效的秘密份额给参与者 136 | 137 | 2. 在秘密重构阶段,某些参与者可能提交无效的秘密份额使得无法恢复正确秘。 138 | 139 | 3. 秘密分发者与参与者之间需点对点安全通道。 140 | 141 | ##### 2.1.2.基于中国剩余定理的秘密共享方案 142 | 143 | 中国剩余定理最早出现在我国古代数学名著《孙子算经》中,因而又被称为孙子定理,中国剩余定理在密码学中被广泛的使用。 144 | 145 | 令 p1, p2,...,pm 表示 m 个两两互素的正整数。给定任意 m 个整数,k1,k2,k3,...,km,存在唯一的一个整数 K 属于 GF(p1p2 ..., pm)满足: 146 | 147 | K = K1 mod p1 148 | K = K2 mod P2 149 | ...... 150 | K = Km mod Pm 151 | 1983 年,密码学专家提出两一种基于中国剩余定理的秘密共享方案,整体思路如下。 152 | 153 | * 初始化阶段 154 | 155 | 设 P = {p1, p2, ... , Pn} 为参与者的集合。{p, d1, d2, ... dn} 是一组满足下列条件的整数。 156 | 157 | s < p, s 是需要共享的密明。 158 | d1 < d2 < ... dn。 159 | 对i = 1,2, ... , n, 有 gcd(di, p) = 1,其中 gcd(x, y) 表示 x 和 y 的最大公约数。 160 | 对于所有 i 不等于 j,有 gcd(di, dj) = 1。 161 | d1d2...dk > pdn-t+2dn-k+3 ... dn。 162 | 其中第 5 条意味着最小的 k 个 di 的乘积要大于 p 和 k—1 个最大的di之积。 163 | 164 | * 密明分发阶段 165 | 166 | 令 N = d1d2...dt,则 N/p 大于任意的 k-1 个 di 之积。令 r 是 [0, N/p-1] 中一个随机数。为了将密明 s 分割为 n 份,密朗分发者 D 计算 s' = s + rp,这里将 s' 限制在 [0, N - 1] 中,则每个参与者 Pi 的子密钥 xi 为: 167 | 168 | xi = s' mod (di) 169 | 170 | * 密钥重构阶段 171 | 172 | 任意 k 个参与者,不妨假设为 P1, P2, ... Pk 拿出他们的 k 个子密钥 x1, x2, ... xk,由中国剩余定理可得到对应的模: 173 | 174 | N1 = d1d2 ... dk 175 | 因为 N1 大于等于 N,可由中国剩余定理唯一的确定 s'。而后可由 s', r, p 计算出密钥: 176 | 177 | s = s' - rp 178 | 如果仅仅知道 k-1 个子密朗,虽然由N2可知s', 179 | 180 | N2 = d1d2 ... dk-1 181 | 182 | 但由于 N/N2 > p,且gcd(N2, p) = 1,所以使 x 大于等于 N 和 x=s'mod(p) 的数 x 在模 p 下的同余类上均匀分布,因此没有足够的信息决定s'。在该方案中,密钢恢复算法的时间复杂度为O(k),空间复杂度为O(n)。 183 | 184 | 185 | #### 2.2.密钥抗丢失方案 186 | 187 | 关于的密钥的共享,咱们就说上面这些,基于上面的这些理论知识,我想大家已经知道了怎么去对私钥进行保存了,那就是密钥共享。根据门限共享理论可知,秘密可以被切割为 n 份,其中的 k(k < n)份就可以恢复出秘密。这样的话我们就可以把我们的私钥切割为 n 份,交 n 个人或者设备去管理并设置其中 k 份秘密可以帮助我们恢复密钥。当我们的密钥丢失了之后,我们可以发起恢复,只要那到其中的 k 份秘密之后,我们就可以恢复私钥了。具体你的秘密是交给人存储还是交给设备去存储由你的业务决定。 188 | 189 | 190 | ### 3.去中心化的密钥存储方案 191 | 192 | 基于 2 中的秘密拆分,我们可以将拆分的秘密存储到区块链上去。 193 | 194 | -------------------------------------------------------------------------------- /ERC20/README.md: -------------------------------------------------------------------------------- 1 | # 第八章:ERC20代币钱包开发 2 | 3 | ## 一.什么是ERC20 4 | 5 | ERC-20是一门使用智能合约在代太坊区块链实现Tokens的技术,20是分配给此请求的号码。 在以太坊区块链上发行的绝大部分代币均符合ERC-20标准。 根据Etherscan.io 显示,截至目前为止,在以太坊主网络上共发现了147762个ERC-20代币Token。 6 | 7 | ERC-20为更大的以太坊生态系统中的以太坊Token定义了一个通用的规则列表,允许开发人员准确预测令牌之间的交互。 这些规则包括如何在地址之间传输令牌以及如何访问每个令牌中的数据。 8 | 9 | 目前,Ether不符合ERC-20标准。 需要ERC-20合规交易的协议已经创建了包裹的以太币Token作为ETH的占位符。 这些“WETH”代币存放在一个单独的智能合约中,并以1:1的方式与以太网挂钩。 10 | 11 | ### 1.ERC-20的历史由来 12 | 13 | ERC-20于2015年11月19日由Fabian Vogelsteller提出。 它定义了以太坊Token必须实现的常见规则列表,使开发人员能够编程新Token在以太坊生态系统中的运作方式。 ERC-20令牌标准因为部署简单,以及与其他以太坊Token标准互操作的潜力,开始受到从事数字货币产品(ICO)众筹公司的欢迎, 14 | 15 | ### 2.应用 16 | 17 | 截至目前,有超过147762份ERC-20Token合约在以太坊区块链网络上运行。最成功的ERC20Token销售包括EOS,Filecoin,Bancor,Qash和Bankex,已经筹集到大量的资金。 18 | 19 | ### 3.函数 20 | 21 | ERC-20令牌具有以下与方法相关的功能: 22 | 23 | 获取Token代币总量 24 | 25 | totalSupply() public view returns (uint256 totalSupply) 26 | 27 | 获取具有相应合约地址上的的帐户的帐户余额 28 | 29 | balanceOf(address _owner) public view returns (uint256 balance) 30 | 31 | 将一定数量的Token代币发送到地址给其他接收转账地址 32 | 33 | transfer(address _to, uint256 _value) public returns (bool success) 34 | 35 | 从地址_from到地址_to发送_value数量的Token代币 36 | 37 | transferFrom(address _from, address _to, uint256 _value) public returns (bool success) 38 | 39 | 允许_spender多次退出您的帐户,最多为_value金额。 如果再次调用此函数,它将使用_value覆盖当前允许值 40 | 41 | approve(address _spender, uint256 _value) public returns (bool success) 42 | 43 | 返回仍然允许_spender从_owner中退出的数量 44 | 45 | allowance(address _owner, address _spender) public view returns (uint256 remaining) 46 | 47 | #### 事件格式 48 | 49 | 转移Token时触发 50 | 51 | Transfer(address indexed _from, address indexed _to, uint256 _value) 52 | 53 | 每当批准(地址_spender,uint256 _value)被调用时触发 54 | 55 | Approval(address indexed _owner, address indexed _spender, uint256 _value) 56 | 57 | ### 二.ERC20代币账户地址生成 58 | 59 | 由于现在很多代币都没有在BIP44上注册相应的序号,因此现在的大多数钱包开发ERC20代币生成地址时,都使用以太坊的账户地址方式生成ERC20代币的地址。我们这里和正式钱包开发不一样,咱们将用严格的地址生成方式生成ERC20代币的地址。有关BIP44协议部分内容请看面的章节,这里不再做重复的介绍。 60 | 61 | 下面是封装好的一个生成ERC20地址的函数 62 | 63 | function erc20Address(seed, bipNumber, number, coinMark) { 64 | if(!seed || !number) { 65 | console.log("input param seed, coinNumber and number is null") 66 | return paramsErr; 67 | } 68 | var rootMasterKey = hdkey.fromMasterSeed(seed); 69 | var childKey = rootMasterKey.derivePath("m/44'/" + bipNumber + "'/0'/0/" + number + ""); 70 | var address = util.pubToAddress(childKey._hdkey._publicKey, true).toString('hex'); 71 | var privateKey = childKey._hdkey._privateKey.toString('hex'); 72 | var erc20Data = {coinMark:coinMark, privateKey:privateKey, address:"0x" + address} 73 | return erc20Data; 74 | } 75 | 76 | 函数说明:`seed`是随机种子的Buffer流,`bipNumber`对BIP44协议上规定的代号,`number`对应第几个账户,`0`表示第一个服务器,`coinMark`币的标识,例如`LET`。 77 | 78 | ### 三.ERC20代币转账签名 79 | 80 | 以下是封装好的一个ERC20交易签名,上述代码中包含了单个ERC20交易签名和批量ERC20交易签名,代码中使用到了ethereumjs-tx这个开源库。 81 | 82 | 83 | const transaction = require( 'ethereumjs-tx'); 84 | 85 | const paramsErr = {code:1000, message:"input params is null"}; 86 | 87 | var libErc29Sign = {}; 88 | 89 | function addPreZero(num){ 90 | var t = (num+'').length, 91 | s = ''; 92 | for(var i=0; i<64-t; i++){ 93 | s += '0'; 94 | } 95 | return s+num; 96 | } 97 | 98 | 这个函数是单个ERC20代币交易签名函数,`privateKey`是私钥;`nonce`交易nonce,标识每一交易;`currentAccount`当前要转账的账户地址;`contractAddress`代币的合约地址;`toAddress`数字货币转入地址;gasPrice和gasLimit俩个相乘代表手续费;`totalAmount`转账金额, `decimal`代币单位换算。 99 | 100 | function ethereumErc20CoinSign(privateKey, nonce, currentAccount, contractAddress, toAddress, gasPrice, gasLimit, totalAmount , decimal) { 101 | if(!privateKey || !nonce || !currentAccount || !contractAddress || !toAddress || !gasPrice || !gasLimit || !totalAmount || !decimal) { 102 | console.log("one of param is null, please give a valid param"); 103 | return paramsErr; 104 | } 105 | var transactionNonce = parseInt(nonce).toString(16); 106 | var gasLimits = parseInt(90000).toString(16); 107 | var gasPrices = parseFloat(gasPrice).toString(16); 108 | var txboPrice = parseFloat(totalAmount*(10**decimal)).toString(16) 109 | var txData = { 110 | nonce: '0x'+ transactionNonce, 111 | gasLimit: '0x' + gasLimits, 112 | gasPrice: '0x' +gasPrices, 113 | to: contractAddress, 114 | from: currentAccount, 115 | value: '0x00', 116 | data: '0x' + 'a9059cbb' + addPreZero(toAddress.substr(2)) + addPreZero(txboPrice) 117 | } 118 | var tx = new transaction(txData); 119 | const privateKey1 = new Buffer(privateKey, 'hex'); 120 | tx.sign(privateKey1); 121 | var serializedTx = tx.serialize().toString('hex'); 122 | return '0x'+serializedTx; 123 | }; 124 | 125 | 下面函数是批量ERC20交易签名,下面函数将参数JSON化,参数的意义和上面的函数一致。 126 | 127 | function MultiEthereumErc20CoinSign(erc20SignData) { 128 | var outErc20Data = []; 129 | if(erc20SignData === null) { 130 | console.log("erc30SignData param is null, please give a valid param"); 131 | return paramsErr; 132 | } 133 | var calcNonce = Number(erc20SignData.nonce); 134 | for (var i = 0; i < erc20SignData.signDta.length; i++){ 135 | var transactionNonce = parseInt(calcNonce).toString(16); 136 | var gasLimit = parseInt(120000).toString(16); 137 | var gasPrice = parseFloat(erc20SignData.gasPrice).toString(16); 138 | var totx = parseFloat((erc20SignData.signDta[i].totalAmount)*(10**(erc20SignData.decimal))).toString(16); 139 | var txData = { 140 | nonce: '0x'+ transactionNonce, 141 | gasLimit: '0x' + gasLimit, 142 | gasPrice: '0x' +gasPrice, 143 | to: erc20SignData.contractAddress, 144 | from: erc20SignData.currentAccount, 145 | value: '0x00', 146 | data: '0x' + 'a9059cbb' + addPreZero((erc20SignData.signDta[i].toAddress).substr(2)) + addPreZero(totx) 147 | } 148 | var tx = new transaction(txData); 149 | const privateKey1 = new Buffer(erc20SignData.privateKey, 'hex'); 150 | tx.sign(privateKey1); 151 | var serializedTx = '0x' + tx.serialize().toString('hex'); 152 | outErc20Data = outErc20Data.concat(serializedTx) 153 | calcNonce = calcNonce + 1; 154 | } 155 | return { signCoin:"ERC20", signDataArr:outErc20Data} 156 | }; 157 | 158 | 159 | ### 四.发送交易到区块链网络 160 | 161 | 此处发送交易到区块链网络和以太坊的一致,这里不再做过多的介绍。 162 | -------------------------------------------------------------------------------- /preface/readme.md: -------------------------------------------------------------------------------- 1 | # 钱包教程 1--探索不同类别的 Web3 钱包底层奥秘 2 | 3 | # **1.概述** 4 | 5 | 熟悉区块链的人都知道,区块链的钱包之所以被分成不同的类别,其本质其实就是私钥的管理方式不同。 6 | 7 | # **2. 钱包类别** 8 | 9 | - 中心化钱包:钱包私钥一般管理在中心化服务器上,代表项目为交易所钱包;例如 Binance 交易所钱包,OKX 交易所钱包和 Bybit 交易所钱包等。 10 | - 去中心化钱包(确定性分层钱包):钱包私钥一般管理在用户的设备上,代表项目为 TP, ImToken, MetaMask 等; 11 | - 硬件钱包:钱包私钥管理在离线的硬件设备上,代表项目 ledger, onekey 等。 12 | - 交易所 Web3 钱包:一般的交易所 Web3 钱包集成了,中心化钱包,去中心化钱包(确定性分层钱包)和硬件钱包的功能。 13 | - 托管钱包:一般为 MPC 算法,每个节点有一个密钥片,网络中没有完整的私钥出现过, N-M 的签名方式,总节点为 N 个,M 个节点签名交易即有效。 14 | - 多签钱包:EVM 链一般使用 gnosis safe 多签,其他使用传统密码学方式多签 15 | - 社交恢复钱包: 密钥分片备份和守护者恢复 16 | - EVM 链 AA 钱包:ERC4337 协议独有特性 17 | 18 | 19 | 20 | # 3. 中心化钱包 21 | 22 | ## 3.1. 中心化钱包架构图 23 | 24 | ![图像](../img/preface_3.1.png) 25 | 交易所使用的钱包一般都称为中心化钱包,原因是交易所钱包私钥一般管理在中心化服务器上,不同的交易所的私钥管理方式也不一样 26 | 27 | 最简单的方式莫过去将私钥做一层 DES 加密之后存储在数据库或者 waller.data数据文件中,其次是使用 KMS 环境保存加密的私钥,不管是使用数据文件或者 KMS 保存私钥,交易签名的时候都需要解密私钥之后就行签名,私钥暴露的风险最大,团队成员作恶也非常容易。 28 | 29 | 其次是 Tee 环境,Tee 环境虽然是一个安全的执行环境,但是私钥也是加密之后存储在这个环境的数据库或者文件里面,每次签名也要在这个环境中解密私钥进行签名,私钥暴露的风险也很大,团队成员想拿到私钥也很容易,只需要签名前传人私钥是打一个日志就行。 30 | 31 | 最安全和最专业的做法是使用 cloadHSM 或者多节点备份的小型签名机,私钥不会离开设备,需要签名的时候,传入待签名的报文,cloadHSM 或者小型签名机签名结束之后返回签名报文,然后整理签名交易报文发送到区块链网络。 32 | 33 | 不管是使用数据库、数据文件、 KMS、TEE,还是 CloadHSM,安全级别也是相对的,有一句话说得好: "**外贼易挡,家贼难防**",这句话用在钱包管理上真的是太正确了。不管用什么方案,即使是 CloadHSM 或者小型机方案,"家贼也是很难防",家贼完全可以模拟一笔合法的交易让 CloadHSM 或小型机方案签名,然后把钱盗走。为了防止家贼,正常的交易所钱包都要做链路风控体系。 34 | 35 | 从上面的架构图中,我们可以看到,对于交易所钱包来说,通常有这些业务流程 36 | 37 | - 批量地址生成 38 | - 充值 39 | - 提现 40 | - 归集 41 | - 热转冷 42 | - 冷转热 43 | - 链路风控 44 | 45 | 很多公司钱包开发,一般就是一个人负责整条链钱包的调研(离线地址生成,离线交易签名和扫块出入金),开发(含充值,提现,归集,热转冷等功能开发,甚至含链路风控的一部分代码)和测试。当然,也有的公司分为三到四团队,调研和离线签是一个团队,扫链出入金,归集和转冷等功能是一个团队,链路风控是一个团队,测试是一个团队。不管怎么分,运维和开发是隔离的,这样做才能做大限度做到人为风控。 46 | 47 | ## 3.2. 中心化钱包业务细节流程 48 | 49 | - 批量地址生成:交易所钱包为了性能和快速响应用户,所有交易所钱包都会有一个地址池,地址池里面每次生成成千上万的地址,当用户注册到交易所的时直接给用户分配地址,而不是去生成;当地址池里面的地址少于一定数量时就会去新生成一批。 50 | 51 | ![图像](../img/preface_3.2.1.png) 52 | 53 | - 充值:扫链服务扫区块链,解析区块里面的交易数据,当交易数据里面的 to 地址是系统里的用户地址时,就说明有用户充值;充值这个过程除了钱包充值扫块之外,还会和风控一起联合,只有风控系统和钱包都认可这笔交易,才会通知业务层;解析的时候需要注意有人构造恶意交易,有的链是可以构造交易攻击的。 54 | 55 | ![图像](../img/preface_3.2.2.png) 56 | 57 | - 提现: 客户端用户输入要提现的地址,金额,链;这些数据将进入交易所业务层,业务层提交数据给钱包,并建立签名参数风控;钱包拿到交易数据之后先验证是否能过风控,没有问题的去链上获取签名参数,组织交易之后生成待签名的交易报文,将交易报文递给签名机,签名机签名之后返回签名的交易报文,钱包服务组织交易之后发送到区块链网络。交易发送完成之后,扫链服务扫到这笔交易之后,验证风控,通过之后通知业务层提现成功,上报交易 Hash 等信息。 58 | 59 | ![图像](../img/preface_3.2.3.png) 60 | 61 | - 归集:归集功能是把用户地址里面的资金全部归集到一个规整地址里面去,归集是一个批量转账的过程,和提现的逻辑比较类似,就是业务类别不一样,这里就不再画流程图了。 62 | - 转冷:将归集地址里面的资金转到一个冷钱包,归集是一个单笔转账的过程,和提现的逻辑比较类似,就是业务类别不一样,这里就不再画流程图了。 63 | - 冷转热:从冷钱包将资金转到热钱包,手动操作的过程 64 | - 交易回滚:交易回滚是指区块链网络的区块回滚或者重组,需要将钱包系统中的交易处理掉的过程,比方说:现在有一条链区块一下从 1000 个块回滚到 500 个块,那么钱包系统里面存储的 500-1000 个块的交易就是无效交易了,需要处理在这个块范围的所用用户的交易。 65 | 66 | ![图像](../img/preface_3.2.4.png) 67 | 68 | - 钱包风控:钱包风控在整个项目中占据重要的角色,不管是充值,提现,归集,转冷,冷转热中的每一笔交易,业务层和钱包底层都应该建立完善的风控体系。 69 | - 对账系统: 钱包系统与业务层对账,钱包系统实时资产负债表等。 70 | 71 | # 4. 去中心化钱包(HD 钱包) 72 | 73 | ## 4.1. 去中心化钱包架构图 74 | 75 | ![图像](../img/preface_4.1.png) 76 | 77 | 去中心化钱包的私钥是管理在用户设备中的,除了用户本人和牛逼的黑客,没有人接触得到。一般的去中心化钱包都是确定性分层钱包;先生成助记词,助记词导出主私钥,主私钥扩展子私钥和公钥匙的方式,公钥再导出地址。 78 | 79 | 去中心化钱包的私钥一般是加密之后存储在本地设备 sqlite 或者数据文件里面,当要签名交易的时候,需要用户输入密码解密私钥之后再签名。 80 | 81 | 去中心化钱包一般有以下几个业务流程: 82 | 83 | - 收款 84 | - 转账 85 | - 转账记录 86 | - 闪兑 87 | - Dapp 浏览器 88 | 89 | 去中心化钱包比较出名的有 Tp,ImToken,MetaMask 等,我们项目实战中将会带大家用 RN 开发一款类似 TP 的钱包。 90 | 91 | ## 4.2.去中心化钱包细节业务流程 92 | 93 | - 收款:查询本地设备数据库把地址展示到界面上。 94 | - 转账 95 | 96 | ![图像](../img/preface_4.2.png) 97 | 98 | - 转账记录:根据地址查询交易记录和根据 Hash 查询交易详情 99 | - 闪兑:钱包里面的闪兑一般对接 [1inch](https://app.1inch.io/#/1/simple/swap/ETH) 或者其他的 `aggregator` 实现兑换功能 100 | - Dapp浏览器:Dapp浏览器是指可以运行Dapp的应用程序,能够正常的和钱包进行交互,有以下几种主流的实现方式 Web View 中包裹 Dapp, 通过 JavaScript 注入 window 对象的方式通信,一般钱包开发直接使用这种方式 websocket 进行 Dapp 和钱包之间的通信 101 | 102 | # 5. 硬件钱包 103 | 104 | ## 5.1. 硬件钱包架构图 105 | 106 | ![图像](../img/preface_5.1.png) 107 | 108 | 硬件钱包主要是把私钥管理在离线的硬件设备中,在硬件设备中集成钱包签名算法,一般使用蓝牙,NFC 或者串口通信进行通信。 109 | 110 | 硬件钱包一般有以下几个业务流程: 111 | 112 | - 地址生成:硬件中集成 BIP,生成助记词和密钥对,使用公钥导出钱包地址; 113 | - 离线签名:组织交易生成 32 位的 Msg Hash, 将签名消息摘要传给硬件,硬件签名之后返回,组织交易发送到区块链网络。 114 | 115 | ## 5.2. 硬件钱包细节业务流程 116 | 117 | 硬件钱包和去中心化钱包相比,最不相同是私有的管理方式,硬件钱包私钥和助记词管理在硬件中,下面以离线地址生成和离线签名为例说明: 118 | 119 | - 离线地址生成:发布地址生成的指令给硬件,硬件会生成助记词和私钥等信息,并将私钥和助记词编码加密之后存储在硬件中,吐出公钥,钱包界面端用公钥生成地址。 120 | - 离线签名:用户端组织编码交易报文,发送给硬件签名,签名回来的交易报文再次组织,然后发送到区块链网络。 121 | 122 | # 6. MPC 托管钱包 123 | 124 | ## 6.1. 托管钱包架构图 125 | 126 | ![图像](../img/preface_6.1.png) 127 | 128 | ## 6.2. 托管钱包业务流程图 129 | 130 | 托管钱包与中心化钱包的主要区别在于密钥的管理方式。托管系统中通常使用MPC(多方计算)网络,该网络由M个节点组成,当有N个节点对交易进行签名时,交易即被视为有效。每个节点掌握一个密钥片,整个网络中从未出现过完整的密钥。以下以地址生成和交易签名为例进行说明: 131 | 132 | - 地址生成:业务端发出 Keygen 指令,MPC 网络经过多轮共识之后各自产生密钥片,并吐出聚合公钥,业务端使用公钥导出地址。 133 | - 离线签名:业务端发出Sign指令,并携带待签名的交易报文到MPC网络。MPC网络经过多轮共识后生成签名,并将签名串返回给业务端。业务端随后将签名后的交易发送到区块链网络。 134 | 135 | # 7. 多签钱包 136 | 137 | 多签钱包的工作机制与MPC钱包有些相似,但本质上完全不同。多签钱包涉及多个密钥以及M-of-N签名的概念。在M-of-N的设置中,只有当N个密钥中的M个对交易进行了签名,交易才能被授权。例如,在2-of-3多签钱包中,共存在三个私钥,至少需要两个私钥才能授权交易。 138 | 139 | 关于Gnosis Safe多签钱包的详细内容,我们将在后续的合约课程中深入讲解,在本章节中暂不展开。 140 | 141 | # 8. 社交恢复钱包 142 | 143 | 社交恢复钱包现在是属于不流行阶段,其使用门槛稍微高了一些,而且现有的社交恢复的技术解决方案不够完美,这也是社交恢复钱包直到今天为止做得不太好的原因。 144 | 145 | ## 8.1. 守护者恢复 146 | 147 | 守护者恢复是使用合约的方式来实现的,用户的钱包是一个合约钱包,由一个 EOA 地址控制;用户可以给这个钱包设置 N 个守护者,当用户的私钥丢失时,可以让这些守护者联合签名一个交易替换这个合约钱包的 owner。 148 | 149 | ![图像](../img/preface_8.1.png) 150 | 151 | ## 8.2. 密钥分片回复 152 | 153 | 密钥分片社交恢复钱包是使用门限秘密共享的密码学技术方案对钱包的助记词或私钥进行切片备份管理,当用户的助记词或私钥丢失,可以发起社交恢复的方式进行助记词和私钥的恢复,在密钥分片社交恢复钱包中,可以将助记词和私钥屏蔽,不在需要用户自己去备份助记词和私钥,而是通过社交的方式进行助记词和私钥的备份。 154 | 155 | ![图像](../img/preface_8.2.png) 156 | 157 | 在密钥分片钱包中,我们整个社交恢复的流程如下: 158 | 159 | - 用户的私钥或者助记词和一个大随机数做一次异或算法,得到一个新的秘密值,我们把大随机数叫做 head, 秘密值叫做 body,head 加密之后上传到 savour wallet 云端。 160 | - 将 body 做门限共享秘密算法,拆分成 N 份 shadow, 设置 K 份可以恢复 body 161 | - shadow-1 加密之后存储在本地,使用 AES 加密,密码Hash之后做为加密 Key, 162 | - shadow-2 加密之后存储到savour wallet 云端,使用 AES 加密,密码和设备 ID 做为加密 Key 163 | - shadow-3 ... n 加密之后存储到密钥柜和社交圈的好友 164 | - 助记词或者私钥丢失发起恢复,获取密钥柜里面或者好友处的 k - 2 份私钥,再获取云端的 shadow-2 和 header。 165 | - 逆门限共享秘密算法通过 K 份 shadow 恢复出 body 166 | - body 和 head 做逆异或算法得到助记词和私钥。 关于密钥柜: 密码柜是一个密钥托管服务,用户可以自行运行密钥柜服务存储自己的密钥 shadow,也可以将密钥 shadow 加密之后保存到我们官方密钥柜,密钥柜对接的是各大区块链存储平台,我们将把密钥进行深层次加密之后把密钥上传到各大区块链存储平台,即使有一天密钥柜不运行了,用户也可以从各大区块链存储平台获取到自己加密的密钥分片,根据加密规则进行解密就可以得到明文的密钥 shadow,同时根据上面的设计图用户还可以通过隐私社交的方式备份自己的密钥 shadow 167 | 168 | # 9. EVM 链 AA 钱包 169 | 170 | EIP4337 账户抽象钱包不仅仅是普通的钱包,它们本身就是智能合约, 主要有安全性高,gas 代付等特点,目前很多公司和项目方都已经或者正在集成账户抽象钱包,但是 AA 钱包目前不是一个很好的赛道,实际应用的公司并不多。 171 | 172 | AA 钱包和 Gnosis safe 多签钱包一样,很多都是智能合约,故而我也在智能合约章节中仔细讲解它, 这里不做过多的赘述。 173 | 174 | 如果有兴趣,更多详情可以参阅:https://eips.ethereum.org/EIPS/eip-4337 175 | 176 | # 10. 总结 177 | 178 | 对于钱包来说,大家需要更好的掌握交易所钱包,去中心化钱包,硬件钱包和 MPC 托管钱包,目前这四种钱包应用最广泛,工作岗位最多。关于 gnosis safe 多签钱包, AA 钱包等,了解即可,需要使用的时候再学习就行了。话虽然这样说,但是关于 gnosis safe 多签钱包和 AA 钱包,我们在智能合约阶段的课程中也会给大家详细讲解。 179 | 180 | 值得一说的是,整个钱包课程使用总-分-总的形势铺开,先整体理解钱包的类别,架构以及各模块的实现逻辑,然后再深入讲解钱包的知识细节,最后通过项目实战的形势收回到这篇文章。当然,整个钱包课程也是根据这篇文章展开的,这篇文章的内容也是我们钱包课程第一节课的内容,希望大家有时间好好阅读,深入体会。 -------------------------------------------------------------------------------- /basicWallet/readme.md: -------------------------------------------------------------------------------- 1 | # 第二章:钱包基础知识 2 | 3 | ## 一.钱包相关的术语 4 | 5 | * 助记词:通过算法从词库中随机选择 12 ~ 24 个单词,目前支持多种语言。 6 | 7 | * 随机种子:使用助记词通过算法产生的一个随机字符串。 8 | 9 | * 私钥:钱包中最重要的东西,钱包实际上就是对私钥进行管理的工具,私钥有主私钥和派生私钥的说法。 10 | 11 | * 主私钥:钱包私钥,由助记词产生的随机种子生成,主私钥被盗的话,你的整个钱包的钱将全部被盗。 12 | 13 | * 派生私钥:由主私钥通过分层确定性协议生成的私钥,派生私钥丢失,只会丢失单一账户上的数字资产。 14 | 15 | * 备份钱包:也叫做备份助记词,现在的钱包基本上都是用助记词恢复的。 16 | 17 | * 备份私钥:对钱包的主私钥或者派生私钥进行备份,通过主私钥可以找回钱包中所有的账户,通过派生私钥只能找回其中的一个账户。 18 | 19 | * 导入钱包:通过导入助记词或者私钥的方式恢复生成以前的钱包。 20 | 21 | * 导入私钥:通过导入私钥生成多链多账户钱包,或者单链单账户钱包。 22 | 23 | * 单链钱包:只支持一条链的钱包,比如说比特币钱包。 24 | 25 | * 多链钱包:支持所有币种的钱包。 26 | 27 | * 单账户钱包:每种币只支持一个账户。 28 | 29 | * 多账户钱包:每种币支持多个账户。 30 | 31 | * 地址:类似于银行卡账户,通过地址可以查询到目前账户上的余额。 32 | 33 | * 公钥:对所有人公开的秘钥。 34 | 35 | * 签名:在发起数字资产转账时,所有的币种都要对转账的数据进行签名,签名的数据对于不同的币种是不一样的。 36 | 37 | * 空投:就是批量打币。 38 | 39 | * bip协议簇:HD分层钱包中的一个重要的规定,是数字货币发展的必然产物。 40 | 41 | * 非确定性钱包:每个账户对应一个私钥,私钥不好管理。 42 | 43 | * 确定性钱包:有主私钥,通过主私钥可以生产其他所有币种的私钥,单不能支持多账户。 44 | 45 | * 确定性分层钱包:有主私钥,通过主私钥可以生产其他所有币种的私钥,支持多账户。 46 | 47 | ### 关于钱包 48 | 49 | 关于钱包的一个常见误解是它们存储Token。 事实上,钱包内并不直接存储Token。钱包的作用是将私钥存储在加密文件中并签署交易。 50 | 51 | 用户通常通过接口构建事务对象,将该对象发送到要签名的钱包,然后钱包返回具有签名的该事务对象,然后该签名被广播到网络。 如果网络确认交易有效,则将其包含在区块链的块中。 52 | 53 | ## 二.目前市场的钱包简介 54 | 55 | ### 1.以太坊的Mist钱包 56 | 57 | 说到以太坊钱包,第一个要说的当然就是Ethereum官方钱包+浏览器Mist。Mist是一个全节点钱包(全节点钱包通俗的来说就是同步了全部的以太坊区块信息的钱包)。也就是说打开钱包后,电脑会自动同步全部的以太坊区块信息,但是同步现有以太坊公链数据的时间比较长。Mist在开发的时候也可以在你的私链上使用,关于Mist详细内容,后面我们将详细介绍。 58 | 59 | ### 2.Parity 60 | 61 | Parity的目标是成为最快,最轻,最安全的以太坊客户端。 使用最尖端的Rust编程语言来开发Parity。 Parity获得GPLv3许可,可用于以太坊的钱包需求。原以太坊基金会部分成员,开发的钱包。功能强大,也是一个全节点钱包。 62 | 63 | ### 3.MyEtherWallet 64 | 65 | MyEtherWallet 作为一个轻钱包,上手难道不大,无需下载,在直接在网页上就可以完成所有的操作。在MyEtherWallet上生成的私钥由用户自我保管,平台方并无备份。 66 | 67 | ### 4.imToken 68 | 69 | 移动端钱包,操作简便,容易上手,功能齐全,在imToken上生成的钱包私钥保存在手机本地,平台方并无备份。 70 | 71 | 72 | ### 5.MetaMask 73 | 74 | MetaMask是一款在谷歌浏览器Chrome上使用的插件类型的以太坊钱包,该钱包不需要下载,只需要在谷歌浏览器添加对应的扩展程序即可,非常轻量级,使用起来也非常方便。 75 | 76 | 77 | ### 6.ledger 78 | Ledger是一个硬件钱包,是一个安全存储私钥的硬件设备,查看钱包和发送交易时,硬件钱包需要与软件钱包配合才能使用。你既可以使用Ledger自己开发的软件钱包,也可以使用其他团队开发的软件钱包。以以太币为例,你可以配合以太币网页钱包myetherwallet.com或者Parity钱包使用Ledger。 79 | 80 | ### 7.kcash 81 | 82 | 移动端钱包,操作简便,支持单链,多链钱包。 83 | 84 | ### 8.biwrok 85 | 86 | 一款企业级应用钱包。 87 | 88 | ### 9.linkeye钱包 89 | 90 | 与Linkeye公链配合使用的钱包。 91 | 92 | ### 10.番茄钱包 93 | 94 | 95 | 如果大家知道还有什么的钱包,可以提供作者,作者进行补充。开源钱包也提供给作者,作者将在后面对钱包的源码进行分析 96 | 97 | ## 三.钱包的发展历史 98 | 99 | 钱包只含有密钥,而不包含钱币。 每个用户有一个包含多个密钥的钱包。 钱包只包含私钥/公钥对的密钥链。因此不要误解为钱包里面包含的是钱,钱包只是私钥的集合,你的钱币存储中区块链的区块中。 100 | 101 | ### 1.非确定性钱包 102 | 103 | 在最早的一批区块链钱包中,钱包只是私钥的集合,这些私钥之间相互独立,没有任何关联,这种类型的钱包咱们把他称为非确定性钱包。举个例子,比特币核心客户端预先生成100个随机私钥,从最开始就生成足够多的私钥并且每个密钥只使用一次。这种钱包现在正在被确定性钱包替换,因为它们难以管理、 备份以及导入。随机密钥的缺点就是如果你生成很多私钥,你必须保存它们所有的副本。这就意味着这个钱包必须被经常性地备份。每一个密钥都必须备份,否则一旦钱包不可访问时,钱包所控制的资金就付之东流。这种情况直接与避免地址重复使用的原则相冲突——每个比特币地址只能用一次交易。地址重复使用将多个交易和地址关联在一起,这会减少隐私。当你想避免重复使用地址时,零型非确定性钱包并不是好的选择,因为你要创造过多的私钥并且要保存它们。虽然比特币核心客户端包含零型钱包,但比特币的核心开发者并不鼓励大家使用。 104 | 105 | .: 106 | ![.: 107 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/1785959-77bbcea7174071c3.png) 108 | 109 | 110 | ### 2.确定性钱包和确定性分层钱包 111 | 112 | 其中所有的密钥都是从一个主密钥派生出来,这个主密钥即为种子(seed)。该类型钱包中所有密钥都相互关联,如果有原始种子,则可以再次生成全部密钥。确定性钱包中使用了许多不同的密钥推导方法。最常用的推导方法是使用树状结构,称为分级确定性钱包或HD钱包 113 | 114 | 115 | #### 2.1.确定性钱包 116 | 117 | 确定性,或者“种子”钱包包含通过使用单项离散函数而可从公共的种子生成的私钥。种子是随机生成的数字。这个数字也含有比如索引号码或者可生成私钥的“链码”(参见“ 分层确定性钱包(BIP0032/BIP0044)”一节)。在确定性钱包中,种子足够恢复所有的已经产生的私钥,所以只用在初始创建时的一个简单备份就足以搞定。并且种子也足够让钱包导入或者导出。这就很容易允许使用者的私钥在钱包之间轻松转移。 118 | 119 | .: 120 | ![.: 121 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/1785959-be390ca284ed5f40.png) 122 | 123 | 124 | #### 2.2.分层确定性钱包 125 | 126 | 确定性钱包被开发成更容易从单个“种子”中生成许多密钥。确定性钱包的最高级形式是通过BIP0032标准定义的HD钱包。HD钱包包含以树状结构衍生的密钥,使得父密钥可以衍生一系列子密钥,每个子密钥又可以衍生出一系列孙密钥,以此类推,无限衍生。 127 | 128 | .: 129 | ![.: 130 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/1785959-7ce3000da8239b74.png) 131 | 132 | 133 | 134 | 相比较随机(不确定性)密钥,HD钱包有两个主要的优势。第一,树状结构可以被用来表达额外的组织含义。比如当一个特定分支的子密钥被用来接收交易收入并且有另一个分支的子密钥用来负责支付花费。不同分支的密钥都可以被用在企业环境中,这就可以支配不同的分支部门、子公司、具体功能以及会计类别。 135 | 136 | HD钱包的第二个好处就是它可以允许让使用者去建立一个公共密钥的序列而不需要访问相对应的私钥。这可允许HD钱包在不安全的服务器中使用或者在每笔交易中发行不同的公共钥匙。公共钥匙不需要被预先加载或者提前衍生,而在服务器中不需要可用来支付的私钥。 137 | 138 | ## 四.钱包的账户体系 139 | 140 | 目前的区块链技术中,记账的方式主要有两种形式,以比特币为代表的UTXO模型,UTXO是该记账模式的缩写,它的英文全拼为Unspent Transaction Output,汉语翻译为“未花费的交易输出”;对于“未花费的交易输出”这个名词对于大多数人来说,应该会比较陌生。如果你觉得陌生的话,请接着文章下面的分解。另一种是以以太坊为代表的基于账户/余额的记账模式,基于账户/余额的这种记账模式比较简单了,和现在的银行卡记账方式一样,大多数人都能够很清晰地理解这种模型。 141 | 142 | ### 1.UTXO模型 143 | 144 | #### 1.1.模型解说 145 | 在解说UTXO模型之前,我想先说说一个名词,叫做Transaction,即交易,Transaction和UTXO是相辅相成的,下面先来举个例子: 146 | 147 | 张三:通过挖矿得到了120个比特币,现在张三饿了,想要花费2个比特币去购买一个面包。这是只是举例而已,以现在比特币的价格,1个比特币就可以买到一大堆面包了。 148 | 149 | 现在我们假设,李四是面包点营业主 150 | 151 | 这样的话,在比特币中,张三付给李四将一次性付给李四120块,李四给张三找零118块,这里其实产生了两笔交易,咱们可以这样理解,张三的钱被分成了两份,其中118打给了自己,剩下的2块打给了李四。 152 | 153 | 交易与交易之间组成了网状关系,1个交易的输出,成为了下1个交易的输入;下1个交易的输出,又成了下下1个交易的输入。所有的钱,在这个网络中流动,每1笔钱的去向、来源,都是可追溯的,而这也是区块链网络的一个重要特点。 154 | 155 | 上面的讲解可能大多数人都比较懵逼,接下来咱们用比较通俗的方式来说明UTXO和Transaction到底是什么 156 | 157 | 在现实生活中,一笔转账对应的事一个付款人和一个收款人,而在比特币种,一笔转账对应的事多个转账人和多个收款人。 158 | 159 | 现在咱们仔细分析一下上面的这个例子,对于张三买面包这个案列 160 | 161 | 付款人:张三 120块 收款人:张三 118块,李四 2块 162 | 163 | 张三的120,转118块给自己,转2块给李四,对应到交易里面,就是这笔交易有1个输入,2个输出! 164 | 165 | 下面来一个多输入,多输出的案列 166 | 167 | 考虑如下场景:用户A和用户B之间发生了一个交易T3,A向B转100元。 A的100元,来自T1:C向A转的80元 + T2:D向A转的30元(共110元,但A只转了100元给B,10元找零返回给A的账号)。 同理,C向A转的这80元,来自用户E、F的某次交易...... D向A转的这30元,来自用户E的某次交易...... 168 | 169 | 这个交易就有2个输入,2个输出: 2个输入(也就是2个UTXO): T1: C向A转的80元 T2:D向A转的30元 170 | 171 | 2个输出: B:100元 A:10元(找零) 172 | 173 | 当你理解上面的例子时,我们再来说一下UTXO,理解上面的例子对你理解UTXO会特别有帮助。 174 | 175 | 1.比特币的交易中不是通过账户的增减来实现的,而是一笔笔关联的输入/输出交易事务。 176 | 177 | 2.每一笔的交易都要花费“输入”,然后产生“输出”,这个产生的“输出”就是所谓的“未花费过的交易输出”,也就是UTXO。每一笔交易事务都有一个唯一的编号,称为交易事务ID,这是通过哈希算法计算而来的,当需要引用某一笔交易事务中的“输出”时,主要提供交易事务ID和所处“输出”列表中的序号就可以了。 178 | 179 | 3.由于没有账户的概念,因此当“输入”部分的金额大于所需的“输出”时,必须给自己找零,这个找零也是作为交易的一部分包含在“输出”中。 180 | 181 | 4.旧的UTXO不断消亡,新的UTXO不断产生。所有的UTXO,组成了UTXO Set 的数据库,存在于每个节点 182 | 183 | 5.任何1笔UTXO,有且仅可能被1个交易花费1次 184 | 185 | 6.1个UTXO,具有如下的表达形式: 1个UTXO = 1个Transaction ID + Output Index 186 | 187 | #### 1.2.UTXO模型的区块链钱包余额形式 188 | 深刻理解了UTXO的概念,钱包就很容易理解了,某个人的钱包的余额 = 属于他的UTXO的总和;在这里,你会发现一个不同于现实世界的“银行”里的一个概念,在银行里,会存储每个账号剩余多少钱。但这里,我们存储的并不是每个账号的余额,而存的是1笔笔的交易,也就是1笔笔的UTXO,每个账户的余额是通过UTXO计算出来的,而不是直接存储余额。 189 | 190 | ### 2.账户余额模型 191 | 账户余额模型和当今的银行卡一样,当我们需要花费钱的时候,会去先检查我们的余额是否足够。转账的整个业务流程和银行卡一样的,基于账户的余额。 192 | 193 | ## 四.多种签名 194 | 195 | ### 1.多重签名概述 196 | 197 | 多重签名就是由多方共同签名发布一个事务的方式,多方中规定的任何一方没有签名都说明该事务无效。只用多方中规定要签名的任何一方签名了该事务才是有效的。多重签名的好处分割了权力,使得拥有该事务的人都能行使自己的权力,确保自己拥有的事务的安全性。 198 | 199 | 下面给出多重签名的相关的定义。 200 | 201 | * 签名系统:在一个多重签名体制中,所有参与签名的相对独立而又按一定规则关联的实体的集合,我们称为一个签名系统。签名子系统就是所有签名者的一个子集合。 202 | * 签名结构:在一个多重签名体制中,签名系统中的任何一个子系统的各成员按照特定的承接关系对某个文件进行签名,这个承接关系就称为这个签名系统的一个签名结构。可以用有向图来表示签名结构,其中顶点表示参与签名的各实体,有向边表示承接关系,即数据的流向。 203 | 204 | 发展初期,按照签名结构的不同,多重数字签名分为两类:有序多重签名,即签名者之间的签名次序是一种串行的顺序,和广播多重签名,即签名者之间的签名次序是一种并行的顺序。后来,提出了具有更一般化签名结构的签名方案:结构化多重签名。在结构化签名方案中,各成员按照事先指定的签名结构进行签名。 205 | 206 | * 多重签名就是多个用户对同一个消息进行数字签名。 207 | * 根据签名过程的不同,多重签名方案可以分为两类:有序多重数字签名方案和广播多重数字签名方案。 208 | * 每种方案都有三个过程:系统初始化、产生签名和验证签名。 209 | * 每种方案都包含三个对象:消息发送者、消息签名者和签名验证者。广播多重数字签名方案中还包含签名收集者。 210 | 211 | 212 | 213 | ### 2.比特币多重签名的流程 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | -------------------------------------------------------------------------------- /Bitcoin/BitcoinWalletOfflineAddressGenerationAndCignatureCombat.md: -------------------------------------------------------------------------------- 1 | # Bitcoin 钱包离线地址生成和签名实战 2 | 3 | # 一. Bitcoin 概览 4 | 5 | ## 1. Bitcoin 简介 6 | 7 | 比特币(Bitcoin)是由一个或一群化名为中本聪(Satoshi Nakamoto)的人在 2008 年提出,并于 2009 年开始发布的去中心化数字货币。它的出现标志着一种新型金融体系的诞生,具有以下主要特点: 8 | 9 | **去中心化** 10 | 11 | 💡💡比特币网络没有中央管理机构或中介机构,它通过一个称为区块链的分布式账本来记录所有交易。 12 | 13 | 💡💡区块链技术确保了所有参与者都可以查看和验证交易,提高了透明度和安全性。 14 | 15 | 16 | 17 | **有限供应** 18 | 19 | 💡💡比特币的总供应量被固定在2100万枚,这通过代码内的机制控制。 20 | 21 | 💡💡这一机制使比特币具备了抗通胀的特点。 22 | 23 | 24 | 25 | **点对点交易** 26 | 27 | 💡💡比特币允许用户之间直接进行交易,不需要通过银行或支付处理机构。 28 | 29 | 💡💡这种点对点交易减少了交易成本和处理时间。 30 | 31 | 32 | 33 | **安全性** 34 | 35 | 💡💡比特币使用加密技术确保交易的安全性和隐私性。 36 | 37 | 💡💡每一笔交易都需要通过复杂的数学算法进行验证,从而确保系统的完整性。 38 | 39 | 40 | 41 | **不可逆交易** 42 | 43 | 💡💡一旦比特币交易被确认,它就无法被撤销。这减少了欺诈的风险,但也意味着用户需要谨慎操作。 44 | 45 | 46 | 47 | **挖矿机制** 48 | 49 | 💡💡新的比特币通过一个称为挖矿的过程产生。矿工们使用计算能力来解决复杂的数学问题,成功解决问题的矿工将获得比特币奖励。 50 | 51 | 💡💡挖矿不仅生成新币,还维护和保护比特币网络的安全。 52 | 53 | 54 | 55 | **全球流通** 56 | 57 | 💡💡比特币可以在全球范围内进行交易,不受地域和国界的限制。 58 | 59 | 💡💡它为跨国支付提供了一种高效、低成本的替代方案。 60 | 61 | 62 | 63 | **隐私和匿名性** 64 | 65 | 💡💡虽然比特币交易是公开记录的,但用户身份是匿名的。交易是通过地址(类似于账号)进行的,而不是通过真实身份。 66 | 67 | 68 | 69 | ## 2.Bitcoin 的升级次数介绍 70 | 71 | 比特币自 2009 年发布以来,经历了多次重要升级。这些升级旨在提高比特币网络的安全性、效率和功能性。 72 | 73 | 并且比特币的 Taproot 升级给比特币带来更多的可能性,Taproot 升级带动了 BRC20 和 Bitcoin-Layer2 的发展,这里我们不在做过多的介绍,在未来的 Layer2. 的课程中我们会深入讲解这部分的内容 74 | 75 | **P2SH(Pay-to-Script-Hash)** 76 | 77 | 💡💡时间:2012年 78 | 79 | 💡💡BIP(Bitcoin Improvement Proposal):BIP-0016 80 | 81 | 💡💡内容:允许更复杂的交易脚本,支持多重签名地址。这种升级使得比特币交易更加灵活和安全 82 | 83 | 84 | 85 | **比特币改进提案 BIP 66** 86 | 87 | 💡💡时间:2015年 88 | 89 | 💡💡BIP:BIP-0066 90 | 91 | 💡💡内容:规范了交易中DER格式的签名,解决了交易签名的一致性问题,增强了网络的安全性。 92 | 93 | 94 | 95 | **CheckSequenceVerify(CSV)** 96 | 97 | 💡💡时间:2016年 98 | 99 | 💡💡BIP:BIP-0112 100 | 101 | 💡💡内容:增加了相对时间锁功能,使得交易可以在指定的时间或块高度之后才生效,这为更复杂的支付通道铺平了道路。 102 | 103 | 104 | 105 | **Segregated Witness(SegWit)** 106 | 107 | 💡💡时间:2017年 108 | 109 | 💡💡BIP:BIP-0141, BIP-0143, BIP-0144 110 | 111 | 💡💡内容:分离交易签名数据,提高了区块的有效容量,减小了交易体积,降低了交易费用。还解决了交易的可塑性问题,使得闪电网络等侧链解决方案成为可能。 112 | 113 | 114 | 115 | **隔离见证2x(SegWit2x)** 116 | 117 | 💡💡时间:2017年 118 | 119 | 💡💡内容:这次升级是对 SegWit 的后续提案,旨在进一步增加区块大小。然而,由于社区共识未达成,这次升级最终未能实现。 120 | 121 | 122 | 123 | **Taproot** 124 | 125 | 💡💡时间:2021年 126 | 127 | 💡💡BIP:BIP-0340, BIP-0341, BIP-0342 128 | 129 | 💡💡内容:引入了 Schnorr 签名和默克尔化抽象语法树(MAST),增强了隐私性和执行脚本验证的功能,进一步提高了交易的灵活性和效率。这是自2017年 SegWit 以来最重要的一次升级。 130 | 131 | 132 | 133 | 💡💡💡💡**MAST(Merkelized Abstract Syntax Trees)** 134 | 135 | 💡💡💡💡💡💡时间:2021年 136 | 137 | 💡💡💡💡💡💡BIP:作为 Taproot 的一部分 138 | 139 | 💡💡💡💡💡💡内容:允许将多种条件的智能合约合并为一个,使得只有满足条件的部分才被公开,提高了隐私性和效率。 140 | 141 | 142 | 143 | 💡💡💡💡**Schnorr Signatures** 144 | 145 | 💡💡💡💡💡💡时间:2021年 146 | 147 | 💡💡💡💡💡💡BIP:作为 Taproot 的一部分 148 | 149 | 💡💡💡💡💡💡内容:提供了一种更高效和安全的签名算法,允许多重签名聚合,提高了交易的隐私性和可扩展性。 150 | 151 | 152 | 153 | ## 3.UTXO 介绍 154 | 155 | UTXO(Unspent Transaction Output,未花费交易输出)模型是比特币及其他一些加密货币使用的一种记账方法。它与账户模型不同,通过追踪未花费的交易输出来记录每个地址的余额。以下是UTXO模型的详细介绍: 156 | 157 | 158 | 159 | **模型解说** 160 | 161 | 在解说 UTXO 模型之前,我想先说说一个名词,叫做 Transaction,即交易,Transaction 和 UTXO 是相辅相成的,下面先来举个例子: 162 | 163 | 张三:通过挖矿得到了120个比特币,现在张三饿了,想要花费2个比特币去购买一个面包。这是只是举例而已,以现在比特币的价格,1个比特币就可以买到一大堆面包了。 164 | 165 | 现在我们假设,李四是面包店营业主 166 | 167 | 这样的话,在比特币中,张三付给李四将一次性付给李四 120 块,李四给张三找零 118 块,这里其实产生了两笔交易,咱们可以这样理解,张三的钱被分成了两份,其中 118 打给了自己,剩下的 2 块打给了李四。 168 | 169 | 交易与交易之间组成了网状关系,1 个交易的输出,成为了下 1 个交易的输入;下 1 个交易的输出,又成了下下 1 个交易的输入。所有的钱,在这个网络中流动,每 1 笔钱的去向、来源,都是可追溯的,而这也是区块链网络的一个重要特点。 170 | 171 | 上面的讲解可能大多数人都比较懵逼,接下来咱们用比较通俗的方式来说明 UTXO 和 Transaction 到底是什么 172 | 173 | 在现实生活中,一笔转账对应的事一个付款人和一个收款人,而在比特币种,一笔转账对应的事多个转账人和多个收款人。 174 | 175 | 现在咱们仔细分析一下上面的这个例子,对于张三买面包这个案列 176 | 177 | - 付款人:张三 120块 178 | - 收款人:张三 118块,李四 2块 179 | 180 | 张三的120,转118块给自己,转2块给李四,对应到交易里面,就是这笔交易有1个输入,2个输出! 181 | 182 | 下面来一个多输入,多输出的案例 183 | 184 | 考虑如下场景:用户A和用户B之间发生了一个交易T3,A 向 B 转 100 元。 A的100元,来自T1:C向A转的80元 + T2:D向A转的30元(共110元,但A只转了100元给B,10元找零返回给A的账号)。 同理,C向A转的这80元,来自用户E、F的某次交易...... D向A转的这30元,来自用户E的某次交易...... 185 | 186 | 这个交易就有2个输入,2个输出: 2个输入(也就是2个UTXO): T1: C向A转的80元 T2:D向A转的30元 187 | 188 | 2个输出: B:100元 A:10元(找零) 189 | 190 | 当你理解上面的例子时,我们再来说一下UTXO,理解上面的例子对你理解UTXO会特别有帮助。 191 | 192 | - 比特币的交易中不是通过账户的增减来实现的,而是一笔笔关联的输入/输出交易事务。 193 | 194 | - 每一笔的交易都要花费“输入”,然后产生“输出”,这个产生的“输出”就是所谓的“未花费过的交易输出”,也就是UTXO。每一笔交易事务都有一个唯一的编号,称为交易事务ID,这是通过哈希算法计算而来的,当需要引用某一笔交易事务中的“输出”时,主要提供交易事务ID和所处“输出”列表中的序号就可以了。 195 | 196 | - 由于没有账户的概念,因此当“输入”部分的金额大于所需的“输出”时,必须给自己找零,这个找零也是作为交易的一部分包含在“输出”中。 197 | 198 | - 旧的 UTXO不断消亡,新的UTXO不断产生。所有的UTXO,组成了UTXO Set 的数据库,存在于每个节点 199 | 200 | - 任何1笔 UTXO,有且仅可能被1个交易花费1次 201 | 202 | - 1 个 UTXO,具有如下的表达形式: 1 个 UTXO = 1个Transaction ID + Output Index 203 | 204 | 205 | 206 | **2. UTXO模型的区块链钱包余额形式** 207 | 208 | 深刻理解了UTXO的概念,钱包就很容易理解了,某个人的钱包的余额 = 属于他的UTXO的总和;在这里,你会发现一个不同于现实世界的“银行”里的一个概念,在银行里,会存储每个账号剩余多少钱。但这里,我们存储的并不是每个账号的余额,而存的是1笔笔的交易,也就是1 笔笔的UTXO,每个账户的余额是通过UTXO计算出来的,而不是直接存储余额。 209 | 210 | 211 | 212 | # 二. Bitcoin 钱包地址类型 213 | 214 | Bitcoin 钱包地址有几种不同的类型,每种类型都有其特定的用途和特点。主要的几种类型包括: 215 | 216 | 217 | 218 | **P2PKH(Pay-to-PubKeyHash)地址** 219 | 220 | 💡💡格式:以1开头,例如,1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa。 221 | 222 | 💡💡特点:这是最传统和最常见的地址类型,广泛用于比特币的早期交易。 223 | 224 | 💡💡优点:兼容性好,几乎所有钱包和交易所都支持。 225 | 226 | 💡💡缺点:随着时间的推移,这种地址类型的使用效率较低,交易费用可能会较高。 227 | 228 | 229 | 230 | **P2SH(Pay-to-Script-Hash)地址** 231 | 232 | 💡💡格式:以3开头,例如,3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy。 233 | 234 | 💡💡特点:这种地址允许更复杂的交易脚本,例如多重签名地址。 235 | 236 | 💡💡优点:支持更复杂的交易和脚本,安全性更高。 237 | 238 | 💡💡缺点:创建和管理比P2PKH地址更复杂。 239 | 240 | 241 | 242 | **Bech32(SegWit)地址** 243 | 244 | 💡💡格式:以 bc1 开头,例如,bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwfvenl。 245 | 246 | 💡💡特点:这是比特币改进提案BIP-0173中引入的新地址格式,旨在提高交易效率和减少费用。 247 | 248 | 💡💡优点:交易费用更低,处理速度更快,且有助于减少交易体积。 249 | 250 | 💡💡缺点:并非所有的钱包和交易所都支持这种地址类型,尽管支持率在逐步增加。 251 | 252 | 每种地址类型都有其特定的应用场景和优缺点,用户可以根据自己的需求选择合适的地址类型来存储和交易比特币 253 | 254 | 255 | 256 | # 三. Bitcoin 离线地址生成代码 257 | 258 | ## NodeJs 代码 259 | 260 | ```javascript 261 | export function createAddress (params: any): any { 262 | const {seedHex, receiveOrChange, addressIndex, network, method } = params 263 | const root = bip32.fromSeed(Buffer.from(seedHex, 'hex')); 264 | let path = "m/44'/0'/0'/0/" + addressIndex + ''; 265 | if (receiveOrChange === '1') { 266 | path = "m/44'/0'/0'/1/" + addressIndex + ''; 267 | } 268 | const child = root.derivePath(path); 269 | let address: string 270 | switch(method) { 271 | case "p2pkh": 272 | const p2pkhAddress = bitcoin.payments.p2pkh({ 273 | pubkey: child.publicKey, 274 | network: bitcoin.networks[network] 275 | }); 276 | address = p2pkhAddress.address 277 | break 278 | case "p2wpkh": 279 | const p2wpkhAddress = bitcoin.payments.p2wpkh({ 280 | pubkey: child.publicKey, 281 | network: bitcoin.networks[network] 282 | }); 283 | address = p2wpkhAddress.address 284 | break 285 | case "p2sh": 286 | const p2shAddress = bitcoin.payments.p2sh({ 287 | redeem: bitcoin.payments.p2wpkh({ 288 | pubkey: child.publicKey, 289 | network: bitcoin.networks[network] 290 | }), 291 | }); 292 | address = p2shAddress.address 293 | break 294 | default: 295 | console.log("This way can not support") 296 | } 297 | 298 | return { 299 | privateKey: Buffer.from(child.privateKey).toString('hex'), 300 | publicKey: Buffer.from(child.publicKey).toString('hex'), 301 | address 302 | }; 303 | } 304 | ``` 305 | 306 | 代码中通过 method 来控制生成的地址类别 307 | 308 | 309 | 310 | # 四. Bitcoin 离线签名代码 311 | 312 | ```javascript 313 | export function buildAndSignTx (params: { privateKey: string; signObj: any; network: string; }): string { 314 | const { privateKey, signObj, network } = params; 315 | const net = bitcore.Networks[network]; 316 | const inputs = signObj.inputs.map(input => { 317 | return { 318 | address: input.address, 319 | txId: input.txid, 320 | outputIndex: input.vout, 321 | script: new bitcore.Script.fromAddress(input.address).toHex(), 322 | satoshis: input.amount 323 | } 324 | }); 325 | const outputs = signObj.outputs.map(output => { 326 | return { 327 | address: output.address, 328 | satoshis: output.amount 329 | }; 330 | }); 331 | const transaction = new bitcore.Transaction(net).from(inputs).to(outputs); 332 | transaction.version = 2; 333 | transaction.sign(privateKey); 334 | return transaction.toString(); 335 | } 336 | ``` 337 | -------------------------------------------------------------------------------- /projectPractice/MPC Asset Management System.md: -------------------------------------------------------------------------------- 1 | # The Web3 通用型 MPC 资管系统项目设计方案 2 | 3 | # 一. 概述 4 | 5 | 目前市场上有很多公司在做 MPC 资管系统,也有很多公司使用 MPC 资管平台; 当然,现在市场上也有很多自托管的 MPC 钱包,其中不乏用户量比较大的钱包。因此,不管是从开发者角度,还是从 MPC 托管钱包系统使用者角度,理解整个系统的运行都是非常有必要的。对于技术开发者来说,学会该项技术,将能为你未来的职业生涯增加另一份可能性;对于使用者来说,深入理解该系统的运行机制才能选择到更好的托管产品,为自己的资金服务。 6 | 7 | ## 1.什么 MPC 8 | 9 | 安全多方计算(Secure Multi-Party Computation,SMPC 或 MPC)是一种密码学技术,允许多个参与方在没有信任第三方的情况下,协同计算某个函数的结果,同时确保每个参与方的输入数据对其他参与方保密,也就是说 MPC 各参与方持有的密钥只有自己知道。同时 MPC 签名的特点是网络中有 M 个节点,N 个节点参与签名交易就有效(M >= N)。 10 | 11 | 1.1. 安全多方计算的基本概念 12 | 13 | - **多方参与**:有多个参与方,每个参与方拥有私密输入数据。 14 | - **联合计算**:参与方希望共同计算一个公共函数的输出,但不希望泄露各自的私密输入。 15 | - **隐私保护**:在计算过程中,确保每个参与方的输入数据对其他参与方是保密的。 16 | 17 | 1.2.安全多方计算的主要特性 18 | 19 | - **输入隐私性**:保证各方输入数据的隐私性,即使在计算过程中也不会泄露给其他参与方。 20 | - **正确性**:保证计算结果的正确性,确保输出结果是按照协议正确计算的。 21 | - **公平性**:所有参与方同时得到计算结果,没有任何一方能够提前获得或影响结果。 22 | - **安全性**:即使在某些参与方恶意行为或尝试获取额外信息的情况下,协议仍然能够保障输入数据的隐私性和计算结果的正确性。 23 | 24 | 1.3.安全多方计算的基础 25 | 26 | - **秘密分享(Secret Sharing)**:将数据分割成多份,使得单个分片不泄露任何信息,只有足够多的分片集合才能重建原始数据。典型的有 Shamir 秘密分享方案。 27 | - **混淆电路(Garbled Circuits)**:一种基于电路的计算方式,使用混淆技术对布尔电路进行加密,使得参与方能够在不知道具体电路结构的情况下进行计算。 28 | - **同态加密(Homomorphic Encryption)**:一种加密方法,允许在加密数据上直接执行特定的运算,而不需要解密。常见的同态加密方案包括 Paillier 加密和 Gentry 的全同态加密方案。 29 | - **可信执行环境(Trusted Execution Environment,TEE)**:一种硬件和软件结合的技术,提供隔离的执行环境来确保代码和数据的机密性和完整性。 30 | 31 | ## 2.什么是托管系统 32 | 33 | MPC 托管系统(MPC Custody)是一种基于安全多方计算(Secure Multi-Party Computation,MPC)技术的资产托管解决方案。它的整个网络中没有出现过完整的私钥,节点之间通过多轮计算得到聚合公钥,各个私钥托管方密钥片是私密的,密钥片分布在不同的托管方之间,从而提高了数字资产的安全性和管理灵活性。 34 | 35 | 2.1.MPC 托管系统的基本原理 36 | 37 | - **密钥分片**:私钥被分割成多个密钥片(key shares),这些密钥片分别存储在不同的服务器或设备上。单个密钥片本身不包含任何有用的信息,只有在足够数量的密钥片聚合时,才能恢复出完整的私钥。 38 | - **分布式签名**:在需要进行数字签名时,MPC 托管系统会通过多方计算协议,使用各自持有的密钥片来联合生成签名。这个过程中,私钥本身不会被重构或暴露给任何单一方。 39 | - **无信任第三方**:每个托管方之间可以相互不信任,但通过 MPC 协议,仍然可以保证计算的正确性和密钥的安全性。 40 | 41 | 2.2.MPC 托管系统的主要特性 42 | 43 | - **增强的安全性**:由于密钥分布在多个地方,即使一个托管方遭受攻击,攻击者也无法获得完整的私钥,从而大大提高了系统的安全性。 44 | - **去中心化管理**:消除了单点故障的风险,提高了系统的鲁棒性和可靠性。 45 | - **灵活的访问控制**:可以设置多种策略(如多签名、多因子认证)来控制对密钥的访问和使用。 46 | - **隐私保护**:私钥不会在任何时刻被完整地重建,确保了密钥的隐私性。 47 | 48 | ## 3.MPC 钱包类别 49 | 50 | - 自托管钱包:密钥分片由用户和钱包服务商控制 51 | - 企业托管钱包:密钥分片有不同的机构节点控制 52 | 53 | # 二.项目架构 54 | 55 | ![图像](../img/mpc1.jpg) 56 | 57 | ## 1.架构要点说明: 58 | 59 | - Keygen 过程:用户向系统发起托管地址申领请求,钱包服务接收到这个请求之后,想 MPC 网络发起 keygen 过程,MPC 网络通过多轮交互之后,个节点产生了账户对应的密钥片,同时产生聚合公钥,用聚合公钥生成地址,这个地址即为用户地址。 60 | - Sign 过程:用户发起转账请求,钱包服务收到转账请求之后,构建待签名的交易,将该交易 Hash 成待签名的消息摘要,将消息摘要递给 MPC 网络进行签名,假定 MPC 节点为 M,N 个节点签名有效(M >= N), N 个节点签名完成之后返回 signature。 61 | - 交易发送到区块链网络:用签名完成返回来 signature 和交易信息,公钥等构造完整的交易,将完整的交易发送到区块链网络。 62 | 63 | ## 2.重要说明 64 | 65 | 很多 MPC 自托管钱包的 MPC 密钥片一般会给到用户,我们假定三片密钥,用户本地设备里面有一片,剩下的两片一般会让用户备份到云端,谷歌或者其他云盘里面;大家使用 MPC 自托管钱包的时候应该都会遇到让你备份密钥的过程。 66 | 67 | # 三.功能模块 68 | 69 | ## 1.账户生成 70 | 71 | ![图像](../img/mpc2.png) 72 | 73 | ## 2.交易签名并且发送交易到区块链网络 74 | 75 | ![图像](../img/mpc3.png) 76 | 77 | # 四. 基于 ZenGo-X 的 multi-party-ecdsa 实现 Ethereum MPC 钱包 MVP 78 | 79 | 本节我们将按照上面的流程用 ZenGo-X 的 multi-party-ecdsa 实现一个 Ethereum MPC 托管钱包 MPC,这里由于是 MVP, 我们将不给大家展示完整的源码,只讲一个简单的实现流程和代码片段,**如果您想获得 MPC 托管钱包的实战代码,请联系 The Web3 社区获取,代码目前这个阶段并不开源,可能需要付一定费用才能拿到实战的源码。** 80 | 81 | 下面我们基于 multi-party-ecdsa 的 example,并结合以太坊签名交易特征完成项目的 MVP 练习。 82 | 83 | ## 1.生成账户地址 84 | 85 | 将 multi-party-ecdsa 的代码库编译构建之后,启动 gg20 manager, 命令如下: 86 | 87 | ```text 88 | ./gg20_sm_manager 89 | ``` 90 | 91 | 模拟三个节点 keygen, 命令如下: 92 | 93 | ```text 94 | ./gg20_keygen -t 1 -n 3 -i 1 --output local-share1.json 95 | ./gg20_keygen -t 1 -n 3 -i 2 --output local-share2.json 96 | ./gg20_keygen -t 1 -n 3 -i 3 --output local-share3.json 97 | ``` 98 | 99 | 生成的 keygen 文件里面有一个叫 y_sum_s 的字段,这里面的 point 对应的就是三个节点掌管的私钥对应的公钥,通过下面代码可以生成地址,代码如下: 100 | 101 | ```text 102 | const y_sum_s = [ 103 | 3, 51, 74, 205, 71, 16, 34, 12, 190, 49, 191, 131, 104 | 245, 158, 114, 173, 238, 162, 120, 125, 221, 191, 105 | 128, 106, 146, 177, 243, 86, 18, 254, 233, 50, 118 106 | ]; 107 | const address = ethers.utils.computeAddress("0x" + Buffer.from(y_sum_s).toString("hex")) 108 | console.log("wallet address:", address); 109 | ``` 110 | 111 | ## 2.交易签名 112 | 113 | 构建交易,可以构建任意类型的交易,构建 *Legacy 类型* 114 | 115 | ```rust 116 | let tx = TransactionRequest { 117 | chain_id: Option::from(U64::from(10)), 118 | to: Some(NameOrAddress::from(wallet.address())), 119 | value: Some(U256::from(100000000000u64)), // 1 ETH 120 | gas: Some(U256::from(21000)), 121 | gas_price: Some(U256::from(10000000000u64)), // 20 Gwei 122 | nonce: Some(U256::from(0)), 123 | ..Default::default() 124 | }; 125 | ``` 126 | 127 | 如果想构建 EIP1559 交易,使用下面代码 128 | 129 | ```rust 130 | let tx1559 = Eip1559TransactionRequest { 131 | chain_id: Option::from(U64::from(10)), 132 | to: Some(NameOrAddress::from(wallet.address())), 133 | value: Some(U256::from(100000000000u64)), // 1 ETH 134 | gas: Some(U256::from(21000)), 135 | max_fee_per_gas: Some(U256::from(10000000000u64)), // 20 Gwei 136 | max_priority_fee_per_gas: Some(U256::from(10000000000u64)), // 20 Gwei 137 | nonce: Some(U256::from(0)), 138 | ..Default::default() 139 | }; 140 | ``` 141 | 142 | 构建待签名的消息摘要, 代码如下: 143 | 144 | ```rust 145 | let chain_id = tx.chain_id().map(|id| id.as_u64()).unwrap_or(self.chain_id); 146 | let mut tx = tx.clone(); 147 | tx.set_chain_id(chain_id); 148 | 149 | let sighash = tx.sighash(); 150 | 151 | println!("Tx Sign Hash: {:?}", sighash); 152 | ``` 153 | 154 | 将 sign hash 拿去给 MPC 网络签名 155 | 156 | ```text 157 | ./gg20_signing -p 1,2 -d "sighash" -l local-share1.json 158 | ./gg20_signing -p 1,2 -d "sighash" -l local-share2.json 159 | ``` 160 | 161 | 使用 r, s 构建完整的交易 162 | 163 | ```rust 164 | let signed_tx = Transaction { 165 | hash: Default::default(), 166 | nonce: U256::from(0), 167 | block_hash: None, 168 | block_number: None, 169 | transaction_index: None, 170 | gas_price: Option::from(U256::from(10000000000u64)), 171 | gas: U256::from(21000), 172 | to: Option::from(wallet.address()), 173 | value: U256::from(100000000000u64), 174 | v: U64::from(signature.v), 175 | r: signature.r, 176 | s: signature.s, 177 | transaction_type: None, 178 | access_list: None, 179 | max_priority_fee_per_gas: None, 180 | max_fee_per_gas: None, 181 | chain_id: None, 182 | from: Default::default(), 183 | input: Default::default(), 184 | other: Default::default(), 185 | }; 186 | 187 | println!("{:?}", signed_tx.rlp()); 188 | ``` 189 | 190 | 从签名信息里面恢复地址代码如下: 191 | 192 | ```js 193 | const convertHashToHex = (value) => { 194 | return value.map(v => v.toString(16).padStart(2, '0')).join(''); 195 | } 196 | 197 | const r = "0x" + convertHashToHex([38,197,206,18,56,101,23,224,63,131,52,210,1,129,193,227,250,26,43,168,54,154,241,18,51,125,21,186,218,126,144,249]) 198 | const s = "0x" + convertHashToHex([33,26,215,163,100,30,208,235,66,20,231,175,68,154,183,100,230,211,218,117,115,71,118,238,183,162,169,117,76,61,103,179]) 199 | const recid = 0 200 | const expandedSig = { 201 | r: r, 202 | s: s, 203 | v: recid 204 | } 205 | const recoveredAddress = ethers.utils.recoverAddress(Buffer.from("f87fde81fd3fa0e152252a45467b32faeac8377b4e6580c9ac06cc6ee82240bb"), expandedSig) 206 | console.log(recoveredAddress); 207 | ``` 208 | 209 | ## 3.发送交易到区块链网络 210 | 211 | 将上面签名得到的消息发送到区块链网络 212 | 213 | 请求示范 214 | 215 | ```text 216 | curl --location 'https://eth-mainnet.g.alchemy.com/v2/XZw9s8EsSyUtwDOjtVvzwL8N0T96Zxt0' \ 217 | --header 'Content-Type: application/json' \ 218 | --header 'Cookie: _cfuvid=A7vae8DmfdNdKLQ37u_mDw17rqRDDuKqXFrJuWD1ccA-1716725090138-0.0.1.1-604800000' \ 219 | --data '{ 220 | "jsonrpc":"2.0", 221 | "method":"eth_sendRawTransaction", 222 | "params":["0x"], 223 | "id":1 224 | }' 225 | ``` 226 | 227 | 返回值 228 | 229 | ```text 230 | { 231 | "jsonrpc": "2.0", 232 | "id": 1, 233 | "error": { 234 | "code": -32000, 235 | "message": "typed transaction too short" 236 | } 237 | } 238 | ``` 239 | 240 | 返回值为交易 Hash 241 | 242 | # 五. DappLink Layer3 托管模块解决方案 243 | 244 | 上面我们讲解了目前市场上存储的托管模块的解决方案,从实际情况出发,上面的 MPC 托管钱包是存在安全风险, 由于 MPC keygen 和 Sign 的过程都涉及到多轮计算,这个网络节点过于庞大的话,整个 keygen 和 sign 的效率就越低。所以,目前的 MPC 托管钱包的节点群都很小,而且节点都是掌握在不同项目方的手里,用户有可能持有一些节点,有可能没有持有节点。这种情况下若节点运营商联合作恶,是完全可以窃取用户的资产的。 245 | 246 | DappLink 考虑到了上述这些情况,我设计了 L3 托管 AppChain, 提高安全的同时不影响 MPC 签名的效率,DappLink 主要做了一下改进 247 | 248 | - 节点运行商必须在 DappLink 的 LinkLayer 多重质押协议质押一定量的 ETH 才能运行 MPC 托管节点,节点做恶,质押的 ETH 将会被罚没 249 | - 每次签名的节点动态选出,参与签名的节点需提交承诺签名信息到 L2, 动态委员信息,交易信息等将生成 zk proof 提交到 L2, 这个过程中若发现做恶节点,罚没质押。 250 | 251 | 架构图如下: 252 | 253 | ![图像](../img/mpc4.jpg) 254 | 255 | # 六. 资料链接 256 | 257 | - ZenGo-X ECDSA 多方计算库: https://github.com/ZenGo-X/multi-party-ecdsa 258 | - ZenGo-X BLS 多方计算库:https://github.com/ZenGo-X/multi-party-bls 259 | - ZenGo-X EDDSA多方计算代码库:https://github.com/ZenGo-X/multi-party-eddsa 260 | - BNB Chain 门限签名代码库: https://github.com/bnb-chain/tss-lib 261 | - DappLink Github: https://github.com/eniac-x-labs 262 | - DappLink X: https://x.com/0xdapplink -------------------------------------------------------------------------------- /tron/README.md: -------------------------------------------------------------------------------- 1 | # Tron 钱包开发详细教程 2 | 3 | # 一. Tron 简介 4 | 5 | Tron(波场) 是一个兼容 EVM 的区块链平台, Tron的技术架构由三层组成: 6 | 7 | - **存储层**:包括区块存储和状态存储,支持多种存储机制。 8 | - **核心层**:实现了智能合约、账户管理和共识机制(目前使用的是DPoS,即委托权益证明)。 9 | - **应用层**:为开发者提供API和SDK,支持开发去中心化应用(DApps)。 10 | 11 | ## **1.主要组件和功能** 12 | 13 | - **Tron虚拟机(TVM)**:兼容以太坊虚拟机(EVM),使开发者可以轻松将以太坊上的应用迁移到Tron上。 14 | - **智能合约**:支持Solidity语言编写的智能合约。 15 | - **TRC-20和TRC-721标准**:TRC-20是Tron的代币标准,类似于以太坊的ERC-20;TRC-721则是非同质化代币标准,类似于ERC-721。 16 | 17 | ## **2.共识机制** 18 | 19 | Tron采用委托权益证明(DPoS)机制。TRX持有者可以投票选举超级代表(Super Representatives,SR),这些SR负责验证交易和维护网络的安全。 20 | 21 | ## **3.生态系统** 22 | 23 | Tron生态系统包含多个重要组件和项目: 24 | 25 | - **TronLink**:官方钱包,支持TRX和TRC-20代币的存储和交易。 26 | - **JustSwap**:去中心化交易所,支持TRC-20代币的交易。 27 | - **Sun Network**:侧链扩展方案,旨在提升Tron主网的扩展性。 28 | - **BitTorrent**:Tron收购的去中心化文件共享协议,与Tron网络深度集成。 29 | - **DApps**:大量去中心化应用在 Tron 平台上运行。 30 | 31 | ## **4. 代币(TRX)** 32 | 33 | Tron的原生代币是TRX,主要用于以下几个方面: 34 | 35 | - **交易费用**:支付网络上的交易费用。 36 | - **Staking和投票**:TRX持有者可以质押代币并参与超级代表的选举。 37 | - **DApp支付**:在Tron平台上的去中心化应用中进行支付和交易。 38 | 39 | # 二. 离线地址生成 40 | 41 | ```javascript 42 | export function createTrxAddress (seedHex: string, addressIndex: string): string { 43 | const node = bip32.fromSeed(Buffer.from(seedHex, 'hex')); 44 | const child = node.derivePath("m/44'/195'/0'/0/" + addressIndex + ''); 45 | const privateKey = child.privateKey.toString('hex'); 46 | const publickKey = child.publicKey.toString('hex'); 47 | const address = pkToAddress(privateKey).toString('hex'); 48 | return JSON.stringify({ 49 | privateKey, 50 | publickKey, 51 | address 52 | }); 53 | } 54 | ``` 55 | 56 | # 三. 离线签名 57 | 58 | ```javascript 59 | export async function signTrxTransaction (params: any): Promise { 60 | const { privateKey, from, to, amount, energyLimit, energyPrice, refBlock, tokenAddress, tokenTRC10, expiration } = params; 61 | const time = Date.now(); 62 | const feeLimit = energyLimit * energyPrice; // SUN 63 | const rawTx = buildTronTransaction({ 64 | from, 65 | to, 66 | amount, 67 | feeLimit, 68 | refBlock, 69 | tokenAddress: tokenAddress || NULLTOKENADDR, 70 | tokenTRC10, 71 | expiration, 72 | permissionId: 0, 73 | time 74 | }); 75 | const signedTx: any = signTx(privateKey, rawTx); 76 | return signedTx.hex; 77 | } 78 | ``` 79 | 80 | **注意:完整代码可以联系 The Web3 社区帮助您,联系方式 github 上和 The Web3Dao 公众号上面有。** 81 | 82 | # 四. Tron 钱包开发相关接口 83 | 84 | ## 1.获取账户信息 85 | 86 | - 请求示范 87 | 88 | ```bash 89 | curl --location 'https://api.trongrid.io/wallet/getaccount' \ 90 | --header 'Content-Type: application/json' \ 91 | --data '{ 92 | "address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g", 93 | "visible": true 94 | }' 95 | ``` 96 | 97 | - 返回值 98 | 99 | ```json 100 | { 101 | "address": "TZ4UXDV5ZhNW7fb2AMSbgfAEZ7hWsnYS2g", 102 | "balance": 68623166, 103 | "create_time": 1675582485000, 104 | "latest_consume_time": 1712481906000, 105 | "net_window_size": 28800000, 106 | "net_window_optimized": true, 107 | "account_resource": { 108 | "latest_consume_time_for_energy": 1714295640000, 109 | "energy_window_size": 28800000, 110 | "energy_window_optimized": true 111 | }, 112 | "owner_permission": {}, 113 | "active_permission": [], 114 | "frozenV2": [ 115 | {}, 116 | { 117 | "type": "ENERGY" 118 | }, 119 | { 120 | "type": "TRON_POWER" 121 | } 122 | ], 123 | "assetV2": [ 124 | { 125 | "key": "1004977", 126 | "value": 8888880000 127 | }, 128 | { 129 | "key": "1005026", 130 | "value": 8888880000 131 | } 132 | ], 133 | "free_asset_net_usageV2": [ 134 | { 135 | "key": "1004977", 136 | "value": 0 137 | }, 138 | { 139 | "key": "1005026", 140 | "value": 0 141 | } 142 | ], 143 | "asset_optimized": true 144 | } 145 | ``` 146 | 147 | - balance: 账户余额 148 | - account_resource:账户资源 149 | 150 | ## 2. 根据账户地址获取交易信息 151 | 152 | - https://developers.tron.network/reference/get-transaction-info-by-account-address 153 | - https://developers.tron.network/reference/get-trc20-transaction-info-by-account-address 154 | 155 | ## 3. 获取 energyPrice 156 | 157 | - 请求示范 158 | 159 | ```bash 160 | curl --location 'https://api.trongrid.io/wallet/getenergyprices' --data '' 161 | ``` 162 | 163 | - 返回值 164 | 165 | ```json 166 | { 167 | "prices": "0:100,1542607200000:20,1544724000000:10,1606240800000:40,1613044800000:140,1635422400000:280,1670133600000:420" 168 | } 169 | ``` 170 | 171 | - prices - 字符串:所有历史能源单价信息。每次单价变化由逗号分隔。冒号前是毫秒时间戳,冒号后是以 sun 为单位的能源单价。 172 | 173 | ## 4. 获取最新块高 174 | 175 | - 请求示范 176 | 177 | ```bash 178 | curl --location 'https://api.trongrid.io/walletsolidity/getblock' --data '' 179 | ``` 180 | 181 | - 返回值 182 | 183 | ```json 184 | { 185 | "blockID": "0000000003b6b891043be54ac0ae1ef579fad5e6e1732cb0ac4237a873b8453e", 186 | "block_header": { 187 | "raw_data": { 188 | "number": 62306449, 189 | "txTrieRoot": "7cb6ddff8ada79c91baf6667cc8ead757b39205a8dc8ae097298dbb0587bddb7", 190 | "witness_address": "4114f2c09d3de3fe82a71960da65d4935a30b24e1f", 191 | "parentHash": "0000000003b6b890d8e1d886520dcad1820f4313a435ad593a80c549ea883054", 192 | "version": 30, 193 | "timestamp": 1717563117000 194 | }, 195 | "witness_signature": "82cd722206df1bd86be854605514f5a45f4914dba2097de6fd59b8c45a07721d5af9a60ec567ecdd48921aec5d458fa26f359315b6561b3d98bedd11910f777601" 196 | } 197 | } 198 | ``` 199 | 200 | - number:为最新块高,也可以使用 curl --location 'https://api.trongrid.io/wallet/getnowblock' --data '' 201 | 202 | ## 4. 根据区块号获取里面的交易 203 | 204 | - https://developers.tron.network/reference/gettransactioninfobyblocknum-1 205 | 206 | - https://developers.tron.network/reference/gettransactioncountbyblocknum 207 | 208 | 获取交易笔数 209 | 210 | ## 5.根据交易 Hash 获取交易详情 211 | 212 | - https://developers.tron.network/reference/gettransactionbyid 213 | - https://developers.tron.network/reference/gettransactioninfobyid-1 214 | 215 | ## 6. 广播交易 216 | 217 | - 请求示范 218 | 219 | ```bash 220 | curl --location 'https://api.trongrid.io/wallet/broadcasthex' \ 221 | --header 'Content-Type: application/json' \ 222 | --data '{ 223 | "transaction": "0A83010A0261B72208C487C467E75A3B8E40A4B7D099FE315A65080112610A2D747970652E676F6F676C65617069732E636F6D2F70726F746F636F6C2E5472616E73666572436F6E747261637412300A1541CF55FB918FB606E00B521868D06B02B261CF18E0121541EDFE83892A43773E7FF65440B72EB3A6AFE37A9B180170A4FD9896FE31124194602BD38AECC0E0AA8F4E417B5C969C840D6D9A4B2310E35A0162EBB7BFED4238AF68321B82C48EE0DCE0F42675DE477A388DA45E2DF9ABFF3A324D5B1E377C00" 224 | }' 225 | ``` 226 | 227 | - 返回值 228 | 229 | ```javascript 230 | { 231 | "result": false, 232 | "code": "TRANSACTION_EXPIRATION_ERROR", 233 | "txid": "cfc03ca018cb79aff19bafe67a81221f2295b2de3402ca4b92293ff3c78e46fe", 234 | "message": "Transaction expired", 235 | "transaction": "{\"raw_data\": {\"ref_block_bytes\": \"61b7\",\"ref_block_hash\": \"c487c467e75a3b8e\",\"expiration\": 1717503794084,\"contract\": [{\"type\": \"TransferContract\",\"parameter\": {\"type_url\": \"type.googleapis.com/protocol.TransferContract\",\"value\": \"0a1541cf55fb918fb606e00b521868d06b02b261cf18e0121541edfe83892a43773e7ff65440b72eb3a6afe37a9b1801\"}}],\"timestamp\": 1717496594084},\"signature\": [\"94602bd38aecc0e0aa8f4e417b5c969c840d6d9a4b2310e35a0162ebb7bfed4238af68321b82c48ee0dce0f42675de477a388da45e2df9abff3a324d5b1e377c00\"]}" 236 | } 237 | ``` 238 | 239 | - txid: 交易 hash 240 | 241 | # **五. 中心化钱包开发** 242 | 243 | ## **1. 离线地址生成** 244 | 245 | - 调度签名机生成密钥对,签名机吐出公钥 246 | - 使用公钥匙导出地址 247 | 248 | ## **2.充值逻辑** 249 | 250 | - 获得最新块高;更新到数据库 251 | - 从数据库中获取上次解析交易的块高做为起始块高,最新块高为截止块高,如果数据库中没有记录,说明需要从头开始扫,起始块高为 0; 252 | - 解析区块里面的交易,to 地址是系统内部的用户地址,说明用户充值,更新交易到数据库中,将交易的状态设置为待确认。 253 | - 所在块的交易过了确认位,将交易状态更新位充值成功并通知业务层。 254 | 255 | ## **3. 提现逻辑** 256 | 257 | - 获取离线签名需要的参数,给合适的手续费 258 | - 构建未签名的交易消息摘要,将消息摘要递给签名机签名 259 | - 构建完整的交易并进行序列化 260 | - 发送交易到区块链网络 261 | - 扫链获取到交易之后更新交易状态并上报业务层 262 | 263 | ## **4.归集逻辑** 264 | 265 | - 将用户地址上的资金转到归集地址,签名流程类似提现 266 | - 发送交易到区块链网络 267 | - 扫链获取到交易之后更新交易状态 268 | 269 | ## **5. 转冷逻辑** 270 | 271 | - 将热钱包地址上的资金转到冷钱包地址,签名流程类似提现 272 | - 发送交易到区块链网络 273 | - 扫链获取到交易之后更新交易状态 274 | 275 | ## **6.冷转热逻辑** 276 | 277 | - 手动操作转账到热钱包地址 278 | - 扫链获取到交易之后更新交易状态 279 | 280 | # **六 HD 钱包开发** 281 | 282 | ## **1.离线地生成和离线签名** 283 | 284 | 参考上面的代码 285 | 286 | ## **2.和链上交互的接口** 287 | 288 | - 获取账户余额 289 | - 获取 nonce 290 | - 根据地址获取交易记录 291 | - 获取预估手续费 292 | 293 | # **七. 总结** 294 | 295 | HD 钱包和交易所钱包不同之处有以下几点 296 | 297 | ## **1.密钥管理方式不同** 298 | 299 | - HD 钱包私钥在本地设备,私钥用户自己控制 300 | - 交易所钱包中心化服务器(CloadHSM, TEE 等),私钥项目方控制 301 | 302 | ## **2.资金存在方式不同** 303 | 304 | - HD 资金在用户钱包地址 305 | - 交易所钱包资金在交易所热钱包或者冷钱包里面, 用户提现的时候从交易所热钱包提取 306 | 307 | ## **3.业务逻辑不一致** 308 | 309 | - 中心化钱包:实时不断扫链更新交易数据和状态 310 | - HD 钱包:根据用户的操作通过请求接口实现业务逻辑 311 | 312 | # **八.附录** 313 | 314 | - Official Website: [https://www.tron.network](https://www.tron.network/) 315 | - Github: https://github.com/tronprotocol 316 | - Tron docs: https://developers.tron.network/ 317 | - Tron 接口文档: https://developers.tron.network/reference/background 318 | - Tron 浏览器: https://tronscan.org/ 319 | - 关于 Tron 的问题列表:https://developers.tron.network/docs/faq -------------------------------------------------------------------------------- /Bitcoin/BitcoinWalletSchnorrOfflineAddressGenerationAndSignature.md: -------------------------------------------------------------------------------- 1 | # Bitcoin 钱包 Schnorr 离线地址生成与签名 2 | 3 | # **1. Schnorr 签名** 4 | 5 | Schnorr 签名是一种数字签名方案,以其简洁性、高效性和安全性著称,并作为比特币的 Taproot 升级的一部分被采用。以下是 Schnorr 签名的详细流程和特点。 6 | 7 | ## 1.1. 签名流程 8 | 9 | Schnorr 签名算法由以下步骤组成: 10 | 11 | **密钥生成** 12 | 13 | - 私钥 𝑘*k* 是一个随机选择的整数,范围为 [1,𝑛−1],其中 𝑛 是椭圆曲线的阶。 14 | - 公钥 𝑃*P* 是通过将私钥乘以椭圆曲线的基点 𝐺*G* 得到的,即 𝑃=𝑘⋅𝐺。 15 | 16 | 17 | 18 | **签名生成** 19 | 20 | 假设消息为 𝑚。 21 | 22 | - 生成随机数 𝑟:选择一个随机数 𝑟*r* 作为临时私钥,范围为 [1,𝑛−1]。 23 | - 计算非交互式挑战:计算 𝑅=𝑟⋅𝐺,其中 𝐺 是椭圆曲线的基点。 24 | - 计算哈希值:计算哈希值 𝑒: 25 | 26 | 𝑒=H(𝑅∥𝑃∥𝑚) 27 | 28 | - 其中 H 是一个哈希函数,如 SHA-256,𝑅∥𝑃∥𝑚 表示 𝑅、公钥 𝑃 和消息 𝑚 的连接。 29 | - 计算签名:计算签名 𝑠: 30 | 31 | 𝑠=𝑟+𝑒⋅𝑘(mod𝑛) 32 | 33 | - 其中 𝑘 是私钥,𝑒 是哈希值,𝑛*n*是椭圆曲线的阶。 34 | 35 | 签名由 (𝑅,𝑠)组成。 36 | 37 | 38 | 39 | **签名验证** 40 | 41 | - 计算哈希值:重新计算哈希值 𝑒: 42 | 43 | 𝑒=H(𝑅∥𝑃∥𝑚) 44 | 45 | - 验证等式:验证以下等式是否成立: 46 | 47 | 𝑠⋅𝐺=𝑅+𝑒⋅𝑃 48 | 49 | - 如果等式成立,签名是有效的;否则无效。 50 | 51 | 52 | 53 | ## 1.2.签名特点 54 | 55 | **简洁性** 56 | 57 | - Schnorr 签名算法结构简单,签名过程和验证过程都较为直观。这种简洁性有助于实现和分析安全性。 58 | 59 | 60 | 61 | **安全性** 62 | 63 | - 不可伪造:基于离散对数问题的困难性,Schnorr 签名在当前已知的计算能力下被认为是安全的。 64 | - 欧氏证明:Schnorr 签名有严格的欧氏安全性证明。 65 | 66 | 67 | 68 | **高效性** 69 | 70 | - 计算效率:签名生成和验证的计算效率较高。尤其在批量签名验证中,Schnorr 签名具有显著的性能优势。 71 | - 签名长度:Schnorr 签名的长度较短,相比于 ECDSA 签名,Schnorr 签名占用的存储空间更少。 72 | 73 | 74 | 75 | **扩展性和兼容性** 76 | 77 | Schnorr 签名支持多种扩展功能,如多重签名(MuSig)和聚合签名。这些扩展功能在增强隐私性和可扩展性方面表现出色。 78 | 79 | - 多重签名(MuSig):Schnorr 签名允许多个签名者的公钥和签名聚合为单一的公钥和签名,这显著提高了多重签名的效率和隐私性。 80 | - 兼容性:Schnorr 签名与现有的椭圆曲线加密标准兼容,便于在现有系统中实现和部署。 81 | 82 | 83 | 84 | ## 1.3. 签名算法 Python 代码实现 85 | 86 | **SchnorrSignObj 类** 87 | 88 | ```python 89 | import hashlib 90 | from ecdsa import SECP256k1, SigningKey, VerifyingKey 91 | from ecdsa.curves import Curve 92 | 93 | 94 | class SchnorrSignObj: 95 | curve: Curve 96 | def __init__(self): 97 | # 椭圆曲线参数 98 | self.curve = SECP256k1 99 | 100 | def schnorr_sign(self, private_key, message): 101 | # 生成私钥和公钥 102 | signing_key = SigningKey.from_string(private_key, curve=self.curve) 103 | verifying_key = signing_key.get_verifying_key() 104 | 105 | # 生成随机数 r 106 | r = SigningKey.generate(curve=self.curve).privkey.secret_multiplier 107 | R = VerifyingKey.from_public_point(r * self.curve.generator, curve=self.curve) 108 | 109 | # 计算哈希 e 110 | e = hashlib.sha256(R.to_string() + verifying_key.to_string() + message).digest() 111 | e = int.from_bytes(e, 'big') 112 | 113 | # 计算签名 s 114 | s = (r + e * signing_key.privkey.secret_multiplier) % self.curve.order 115 | return R.to_string(), s.to_bytes(32, 'big') 116 | 117 | def schnorr_verify(self, public_key, message, signature): 118 | # 解析签名 119 | R = VerifyingKey.from_string(signature[0], curve=self.curve) 120 | s = int.from_bytes(signature[1], 'big') 121 | 122 | # 计算哈希 e 123 | e = hashlib.sha256(signature[0] + public_key.to_string() + message).digest() 124 | e = int.from_bytes(e, 'big') 125 | 126 | # 验证签名 127 | sG = VerifyingKey.from_public_point(s * self.curve.generator, curve=self.curve) 128 | ReP = VerifyingKey.from_public_point(R.pubkey.point + e * public_key.pubkey.point, curve=self.curve) 129 | return sG.to_string() == ReP.to_string() 130 | ``` 131 | 132 | 133 | 134 | **测试代码** 135 | 136 | ```python 137 | import schnorr 138 | from ecdsa import SECP256k1, SigningKey, VerifyingKey 139 | 140 | # 初始化类 141 | schnorr_test = schnorr.SchnorrSignObj() 142 | 143 | # 产生密钥并对交易签名 144 | private_key = SigningKey.generate(curve=SECP256k1).to_string() 145 | message = b"Hello, Schnorr!" 146 | signature = schnorr_test.schnorr_sign(private_key, message) 147 | print("Signature:", signature) 148 | 149 | # 验证交易签名 150 | public_key = VerifyingKey.from_string(SigningKey.from_string(private_key, curve=SECP256k1).get_verifying_key().to_string(), curve=SECP256k1) 151 | is_valid = schnorr_test.schnorr_verify(public_key, message, signature) 152 | 153 | print("Signature valid:", is_valid) 154 | ``` 155 | 156 | ## 2. BIP86 协议 157 | 158 | **2.1 结构路径** BIP86 是一种专门为生成 Taproot 地址的路径标准,简化并优化了生成单密钥 Taproot 地址的过程。BIP86 的路径结构如下: 159 | 160 | ``` 161 | m / 86' / coin_type' / account' / change / address_index 162 | ``` 163 | 164 | - Purpose: 固定为 86',表示遵循 BIP86 标准。 165 | - Coin Type: 定义特定加密货币的类型,如 0' 表示比特币。 166 | - Account: 账户索引,允许钱包管理多个独立账户。 167 | - Change: 0 表示外部链(收款地址),1 表示内部链(找零地址)。 168 | - Address Index: 用于生成具体地址的索引。 169 | 170 | 171 | 172 | **2.2.地址生成方法** 173 | 174 | BIP86 使用新的路径结构生成 Taproot 地址,通过椭圆曲线算法派生公钥,并将其编码为 Bech32 格式的地址。生成的 Taproot 地址具有更高的隐私性、安全性和扩展性。 175 | 176 | 177 | 178 | **2.3.BIP86 的优势** 179 | 180 | - 简洁性:BIP86 提供了一种简洁而直观的路径结构,用于生成 Taproot 地址。 181 | - 安全性:BIP86 使用椭圆曲线算法生成公钥,具有与比特币的现有地址生成方法相当的安全性。 182 | - 可扩展性:BIP86 支持不同的币种和账户,具有良好的可扩展性。 183 | 184 | ## 3. BIP44 与 BIP86 的区别与联系 185 | 186 | **3.1.用途上的区别** 187 | 188 | - BIP44: 通用的多币种、多账户管理,适用于生成多种类型的地址(P2PKH, P2SH, P2WPKH)。 189 | - BIP86: 专门为生成单密钥 Taproot 地址设计,简化了路径和使用场景。 190 | 191 | 192 | 193 | **3.2. 路径结构区别** 194 | 195 | - BIP44: m/44'/coin_type'/account'/change/address_index,包含多币种、多账户和多地址类型。 196 | - BIP86: m/86'/coin_type'/account'/change/address_index,专注于单密钥 Taproot 地址生成。 197 | 198 | 199 | 200 | **3.3.复杂性区别** 201 | 202 | - BIP44: 适用于更复杂的多币种和多账户需求,路径层次更丰富。 203 | - BIP86: 专注于单一用途(Taproot 地址),路径层次简化。 204 | 205 | 206 | 207 | **3.4.标准化区别** 208 | 209 | - BIP44: 广泛应用于各种钱包和加密货币管理系统,已经成为生成确定性钱包路径的标准。 210 | - BIP86: 主要用于比特币的 Taproot 地址生成,随着 Taproot 的采用而变得更加流行。 211 | 212 | 213 | 214 | **3.5.总结** 215 | 216 | - BIP44 适用于多种类型地址生成,适合更复杂的钱包管理需求。 217 | - BIP86 专注于生成比特币的 Taproot 地址,路径简化,专门为 Taproot 设计。 218 | 219 | ## 4.Taproot 为什么需要 BIP86 220 | 221 | **4.1. 专注于单密钥 Taproot 地址** BIP86 专门设计用于生成单密钥 Taproot 地址。Taproot 是比特币的一项重要升级,旨在提高隐私性、可扩展性和智能合约功能。 222 | 223 | 224 | 225 | **4.2.符合新标准和优化** BIP86 引入了一些新的优化和改进,以支持比特币协议的新特性,如 Taproot 和 Schnorr 签名。Taproot 和 Schnorr 签名提供了更高的安全性和更好的隐私特性。BIP86 的设计是为了最大限度地利用这些新特性。 226 | 227 | 228 | 229 | **4.3.专注于比特币和未来扩展** 230 | 231 | BIP44 是一个通用的多币种和多账户路径标准,设计用于管理各种加密货币的地址。这种通用性虽然提供了很大的灵活性,但在特定的优化和新特性支持上可能不如专门设计的标准有效。BIP86 专门为比特币设计,更好地支持比特币的未来扩展和改进。 232 | 233 | 234 | 235 | **4.4.简化路径和提高效率** 236 | 237 | BIP86 路径简化,专注于单一用途,使其更适合于生成和管理比特币的 Taproot 地址。这种简化可以减少实现和使用中的复杂性,提高效率和安全性。 238 | 239 | 240 | 241 | **4.5.避免混淆和错误** 242 | 243 | BIP86 的专用路径减少了使用过程中可能出现的混淆和错误。由于它专门设计用于比特币的 Taproot 地址,开发者和用户不必在多个标准之间切换,减少了出错的可能性。 244 | 245 | 246 | 247 | **4.6.代码比较** 248 | 249 | BIP44 代码示范 250 | 251 | ```python 252 | def create_address(mnemonic): 253 | seed = bip39.phrase_to_seed(mnemonic) 254 | bip32_root_key = BIP32Key.fromEntropy(seed) 255 | path = "m/44'/0'/0'/0/0" 256 | key = bip32_root_key 257 | for level in path.split('/')[1:]: 258 | if level.endswith("'"): 259 | index = BIP32_HARDEN + int(level[:-1]) 260 | else: 261 | index = int(level) 262 | key = key.ChildKey(index) 263 | address = key.Address() 264 | return address 265 | ``` 266 | 267 | 268 | 269 | BIP86 代码示例 270 | 271 | ```python 272 | import bip39 273 | import bech32 274 | from bip32 import BIP32, HARDENED_INDEX 275 | from hashlib import sha256 276 | from ecdsa import SECP256k1, SigningKey 277 | 278 | def generate_taproot_address(mnemonic): 279 | seed = bip39.phrase_to_seed(mnemonic) 280 | bip32 = BIP32.from_seed(seed) 281 | path = "m/86'/0'/0'/0/0" 282 | child_key = bip32.get_privkey_from_path(path) 283 | private_key = SigningKey.from_string(child_key, curve=SECP256k1) 284 | public_key = private_key.get_verifying_key().to_string("compressed") 285 | tweak = sha256(b'TapTweak' + public_key[1:]).digest() 286 | tweaked_pubkey = bytearray(public_key) 287 | for i in range(32): 288 | tweaked_pubkey[1 + i] ^= tweak[i] 289 | witver = 1 # witness version for Taproot 290 | witprog = tweaked_pubkey[1:33] 291 | address = bech32.encode('bc', witver, witprog) 292 | return address 293 | ``` 294 | 295 | 296 | 297 | # 5. Taproot 格式地址离线生成 298 | 299 | ```python 300 | import bip39 301 | import bech32 302 | from bip32 import BIP32, HARDENED_INDEX 303 | from hashlib import sha256 304 | from ecdsa import SECP256k1, SigningKey 305 | 306 | def generate_taproot_address(mnemonic): 307 | seed = bip39.phrase_to_seed(mnemonic) 308 | bip32 = BIP32.from_seed(seed) 309 | path = "m/86'/0'/0'/0/0" 310 | child_key = bip32.get_privkey_from_path(path) 311 | private_key = SigningKey.from_string(child_key, curve=SECP256k1) 312 | public_key = private_key.get_verifying_key().to_string("compressed") 313 | tweak = sha256(b'TapTweak' + public_key[1:]).digest() 314 | tweaked_pubkey = bytearray(public_key) 315 | for i in range(32): 316 | tweaked_pubkey[1 + i] ^= tweak[i] 317 | witver = 1 # witness version for Taproot 318 | witprog = tweaked_pubkey[1:33] 319 | address = bech32.encode('bc', witver, witprog) 320 | return address 321 | ``` 322 | 323 | 324 | 325 | # 6. 离线签名 326 | 327 | - 构建交易: 创建并填充交易输入和输出。 328 | - 生成交易摘要: 使用交易数据生成 Taproot 交易摘要。 329 | - 签名交易: 使用私钥对交易摘要进行签名。 330 | - 构建完整交易: 将签名附加到交易中并生成最终的交易。 331 | 332 | ```python 333 | def buildAndsignTx(input_list, output_list, private_key, taproot_pubkey): 334 | tx = Transaction() 335 | for input in input_list: 336 | tx.add_input(input['previous_txid'], input['index']) 337 | for output in output_list: 338 | tx.add_output(output["address"], output["value"]) 339 | tx.version = 2 340 | tx.locktime = 0 341 | sighash = tx.signature_hash() 342 | 343 | tweaked_privkey = taproot_tweak_seckey(private_key, taproot_pubkey) 344 | signature = schnorr.sign(tweaked_privkey, sighash) 345 | 346 | witness = TaprootWitness(taproot_pubkey, signature) 347 | tx.inputs[0].witness = witness 348 | 349 | signed_tx = tx.serialize() 350 | 351 | return signed_tx 352 | ``` 353 | -------------------------------------------------------------------------------- /biphd/README.md: -------------------------------------------------------------------------------- 1 | 2 | # 第四章:BIP钱包分层协议 3 | 4 | ## 一.为什么需要分层协议 5 | 6 | ### 1.非确定钱包生成以太坊地址 7 | 8 | var keythereum = require("keythereum"); 9 | var params = { keyBytes: 32, ivBytes: 16 }; 10 | 11 | keythereum.create(params, function (dk) { 12 | var options = { 13 | kdf: "pbkdf2", 14 | cipher: "aes-128-ctr", 15 | kdfparams: { 16 | c: 262144, 17 | dklen: 32, 18 | prf: "hmac-sha256" 19 | } 20 | }; 21 | var password = "wheethereum"; 22 | var kdf = "pbkdf2"; 23 | keythereum.dump(password, dk.privateKey, dk.salt, dk.iv, options, function (keyObject) { 24 | console.log(keyObject.address) 25 | keythereum.exportToFile(keyObject); 26 | }); 27 | }); 28 | 29 | 想要运行上面这段段代码,你需要nodejs的环境并且安装keythereum库。上面这段代码是生成以太坊keystore的代码。keystore里面包含了地址、加密的私钥和MAC地址等信息,dk.privateKey是明文的私钥。上面这段代码中并没有助记词的概念。 30 | 31 | ### 2.确定性分层钱包以太坊地址生成 32 | 33 | var bip39 = require('bip39') 34 | var hdkey = require('ethereumjs-wallet/hdkey') 35 | var util = require('ethereumjs-util') 36 | 37 | var mnemonic = bip39.generateMnemonic() 38 | var seed = bip39.mnemonicToSeed(mnemonic) 39 | var hdWallet = hdkey.fromMasterSeed(seed) 40 | 41 | var key1 = hdWallet.derivePath("m/44'/60'/0'/0/0") 42 | console.log(key1._hdkey._privateKey.toString('hex')) 43 | var address1 = util.pubToAddress(key1._hdkey._publicKey, true).toString('hex') 44 | 45 | 以上代码是通过严格的分层钱包机制生成的以太坊地址和私钥,此处的私钥是明文的,如果你需要密文的私钥,可以自己写一个编码私钥的代码对私钥进行编码,下面是一段编码私钥的代码: 46 | 47 | var encrypt = function (str, pwd) { 48 | if(pwd == null || pwd.length <= 0) { 49 | console.log("Please enter a password with which to encrypt the message"); 50 | return null; 51 | } 52 | var prand = ""; 53 | for(var i=0; i 10) { 67 | prand = (parseInt(prand.substring(0, 10)) + parseInt(prand.substring(10, prand.length))).toString(); 68 | } 69 | prand = (mult * prand + incr) % modu; 70 | var enc_chr = ""; 71 | var enc_str = ""; 72 | for(var i=0; i 10) { 106 | prand = (parseInt(prand.substring(0, 10)) + parseInt(prand.substring(10, prand.length))).toString(); 107 | } 108 | prand = (mult * prand + incr) % modu; 109 | var enc_chr = ""; 110 | var enc_str = ""; 111 | for(var i=0; i生成随机数种子->导出主私钥->根据分层规范导出子私钥->导出地址。单单看上面的这两个案列,我们可能看不出不确定性钱包和确定钱包的优势和逆势。但是如果我们的钱包中有很多个账户,这时用不确定性钱包私钥就很难管理,如果用分层确定性钱包,我们只需要记住助记词就行。下面问将分析为什么确定性钱包只需要记住助记词就行,而分层钱包需要记住和管理很多私钥。 122 | 123 | ## 二. SLIPS项目 124 | 125 | 项目需要一种技术来记录其技术决策和功能的方法,SatoshiLabs项目提供了这么一种机制来管理比特币和加密货币。SLIP就是其项目的实现,SLIP存储库是比特币改进提案(BIP)流程的扩展,包含不适合提交给BIP存储库的文档。 126 | 127 | 每个SLIP应提供该功能的简明技术规范和该功能的基本原理。 128 | 129 | | 标号 | 标题 | 类型 | 状态 | 130 | |---------------|----------------------------------------------|--------------|-------------------- | 131 | | SLIP-0000 | SLIP模板 | 信息化 | 公认 | 132 | | SLIP-0010 | 从主私钥派生通用私钥 | 标准 | 草案 | 133 | |   SLIP-0011  |   使用确定性层次结构对键值对的对称加密   |   标准       |       草案         |   134 | | SLIP-0012 | 使用确定性等级的公钥加密 | 标准 | 草案 | 135 | | SLIP-0013 | 使用确定性等级认证 | 标准 | 草案 | 136 | |   SLIP-0014  |   压力测试确定性钱包         |    信息化     |        草案         | 137 | |   SLIP-0015  |   比特币元数据的格式及其在高清钱包中的加密 |    标准       |       草案        | 138 | |   SLIP-0016  |   密码存储格式及其加密   |     标准       |       草案         |   139 | |   SLIP-0017  |   确定性层次结构Elliptic Curve Diffie-Hellman   |     标准       |       草案         |   140 | |   SLIP-0018  |   保留(CoSi)     |     标准       |       草案         |   141 | |   SLIP-0032  |   BIP-32钱包的扩展序列化格式         |     标准       |       草案         |   142 | |   SLIP-0039  |   沙米尔的助记码秘密共享     |     标准       |       草案         |   143 | | SLIP-0044 | BIP-0044注册的币的类别 | 标准 | 草案 | 144 | |   SLIP-0048  |   基于石墨烯的网络的确定性密钥层次结构     |     标准       |       草案         |   145 | |   SLIP-0132  |   BIP-0032的已注册HD版本字节 |     标准       |       草案         |   146 | |   SLIP-0173  |   BIP-0173的已注册人类可读部件     |     标准       |       草案         |   147 | 148 | ### 三.各个组件介绍 149 | 150 | 强制使用公共派生密钥对于支持私钥导出的任何软件都是不安全的,并且会导致(导致)资金损失。 缺少用于版本控制的钩子对于将来的可扩展性来说是很差的(尽管这可以在更高的层次上处理)。 通常建议不要执行此操作,硬件钱包除外。 151 | 152 | 第二节中介绍到的这些组件中,运用最广泛的是SLIP-0044,严格的分层确定性钱包都会用到SLIP-0044分层协议的规定。当然,实际上的钱包开发中,由于ERC20代币比较混乱,大多数币都是空气币,很多项目方都不知道有这个协议的存在,故而在实际的钱包开发中,ERC20代币的地址生成协议我们都会选择使用以太坊的BIP44规定的序号。下面我们首先介绍一下这个组件。 153 | 154 | 要说BIP44,先得从BIP32说起,BIP32是HD钱包的核心提案,通过种子来生成主私钥,然后派生海量的子私钥和地址,但是种子是一串很长的随机数,不利于记录,所以我们用算法将种子转化为一串助记词,方便保存记录,这就是BIP39,它扩展了HD钱包种子的生成算法。BIP43对BIP32树结构增加了子索引标识purpose的扩展`m/purpose'/*`。 BIP44是在 BIP43和BIP32的基础上增加多币种,通过HD钱包派生多个地址,BIP44提出5层的路径建议,如下: 155 | `m/purpse’/coin_type’/account’/change/address_index` 156 | BIP44的规则使得HD钱包非常强大,用户只需要保存一个种子,就能控制所有币种,所有账户的钱包。 157 | 158 | #### 1.BIP44 159 | 160 | BIP44基于BIP-0032(从现在开始的BIP32)和BIP-0043(从现在开始的BIP43)中描述的目的方案中描述的算法定义确定性钱包的逻辑层级。BIP44是BIP43的特定应用。BIP44的层次结构非常全面。 它允许处理多个硬币,多个帐户,每个帐户的外部和内部链以及每个链数百万个地址。 161 | 162 | ##### 1.1.路径级别 163 | 164 | 在BIP32中定义了5种路径级别: 165 | 166 | m / purpose' / coin_type' / account' / change / address_index 167 | 168 | 路径中的撇号表示使用BIP32硬化衍生物。上面的每个路径的级别都有其特殊的意义,我们下面将做详细的说明。 169 | 170 | ##### 1.2.purpose 171 | 172 | Purpose是在BIP43建议之后将常数设置为44'(或0x8000002C)。 它表示根据本规范使用该节点的子树。 173 | 174 | 在此级别使用强化派生。 175 | 176 | ##### 1.3.coin_type 177 | 178 | 一个主节点(种子)可用于无限数量的独立加密币,如比特币,Litecoin或Namecoin。 但是,为各种加密币共享相同的空间有一些缺点。 179 | 180 | 此级别为每个加密币创建一个单独的子树,避免重用加密链中的地址并改善隐私问题。 181 | 182 | 硬币类型是一个常量,为每个加密币设置。 Cryptocoin开发人员可能会要求为他们的项目注册未使用的号码。 183 | 184 | 已分配硬币类型的列表在下面的“已注册硬币类型”一章中。 185 | 186 | 在此级别使用强化派生。 187 | 188 | ##### 1.4. Account 189 | 190 | 此级别将密钥空间拆分为独立的用户身份,因此钱包永远不会将硬币混合在不同的帐户中。 191 | 192 | 用户可以使用这些账户以与银行账户相同的方式组织资金; 用于捐赠目的(所有地址都被视为公共地址),用于保存目的,共同费用等。 193 | 194 | 帐户按顺序递增的方式从索引0开始编号。 此数字在BIP32派生中用作子索引。 195 | 196 | 在此级别使用强化派生。 197 | 198 | 如果先前的帐户没有交易历史记录(意味着之前没有使用过任何地址),则软件应该阻止创建帐户。 199 | 200 | 从外部源导入种子后,软件需要发现所有使用过的帐户。 “帐户发现”一章中描述了这种算法。 201 | 202 | ##### 1.5.Change 203 | 204 | 常量0用于外部链,常量1用于内部链(也称为找零地址)。 外部链用于在钱包外部可见的地址(例如,用于接收付款)。 内部链用于地址,这些地址并不意味着在钱包外可见,并用于返回交易更改。 205 | 206 | 在这个级别使用公共派生。 207 | 208 | ##### 1.6.Index 209 | 210 | 地址按顺序递增的方式从索引0开始编号。 此数字在BIP32派生中用作子索引。 211 | 212 | 在这个级别使用公共派生。 213 | 214 | #### 2.账户发现机制 215 | 216 | 从外部源导入主种子时,软件应开始以下列方式发现帐户: 217 | 218 | * 派生第一个帐户的节点(索引= 0) 219 | * 派生此帐户的外部链节点 220 | * 扫描外链的地址; 遵循下面描述的差距限制 221 | * 如果外部链上未找到任何交易,请停止发现 222 | * 如果有某些交易,请增加帐户索引并转到步骤1 223 | 224 | 此算法是成功的,因为如果上一个帐户没有交易历史记录,程序应该禁止创建新帐户,如上面“帐户”一章所述。 225 | 226 | 请注意,该算法适用于交易历史记录,而不是帐户余额,因此您可以拥有一个总共0个硬币的帐户,算法仍将继续发现。 227 | 228 | ##### 2.1.地址差距限制 229 | 230 | 地址间隙限制当前设置为20.如果程序连续命中20个未使用的地址,则预计在此之后没有使用过的地址并停止搜索地址链。 我们只扫描外部链,因为内部链仅接收来自相关外部链的货币。 231 | 232 | 当用户试图通过生成新地址来超过外部链的间隙限制时,钱包程序应该发出警告。 233 | 234 | #### 3.注册的币种 235 | 236 | 注册的币种是“数字货币类型”;描述的是BIP44第2级使用的默认注册硬币类型。所有这些常量都用作硬化推导。 237 | 238 | | 索引 | 十六进制 | 币 | 239 | |------|--------------|----------------| 240 | | 0 | 0x80000000 | 比特币 | 241 | | 1 | 0x80000001 | 比特币测试网 | 242 | 243 | 此BIP不是注册货币类型的中央目录,请访问SatoshiLabs,其中包含完整列表: 244 | 245 | 要注册新的硬币类型,需要实现该标准的现有钱包,并且应创建对上述文件的拉取请求。 246 | 247 | #### 例子 248 | 249 | .: 250 | ![.: 251 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/coinExample.png) 252 | 253 | -------------------------------------------------------------------------------- /projectPractice/ETH centralized wallets.md: -------------------------------------------------------------------------------- 1 | # 以太坊中心化钱包项目实战 2 | 3 | # 一. 以太坊中心化钱包概述 4 | 5 | 我们都知道,中心化钱包主要用在交易所使用,中心化钱包功能主要包含:批量地址生成,充值,提现,归集,热转冷,冷转热等功能。 6 | 7 | ## **1. 离线地址生成** 8 | 9 | - 调度签名机生成密钥对,签名机吐出公钥 10 | - 使用公钥导出地址 11 | 12 | ## **2.充值逻辑** 13 | 14 | - 获取链上最新块高,从数据库中获取上次解析交易的块高做为起始块高,如果链上的最新块和数据内部的块高一致,则等到下一个块出现了之后再开始去扫块;如果数据库中没有记录,则按照配置的块高开始扫块,起始块高配置默认为 0,如果用户没有配置,那就从 0 开始扫块。 15 | - 获取块里面的交易,判断交易是否为代币转账,如果 to 地址是 Token Address, 则说明为 Token 转账,真正的 To 地址需要从 data 字段里面解出来;如果 To 地址为空,则为合约创建交易,直接跳过。 16 | - 第二步中处理过的交易里面 to 地址是系统内部的用户地址,from 地址是外部地址,说明用户充值,更新交易到数据库中,并更新改地址的余额(注意,如果用户充值金额小于最小充值金额,不给入账),将交易的状态设置为待确认。 17 | - 交易所在块的过了确认位,将交易状态更新位充值成功并通知业务层。 18 | 19 | ## **3.归集逻辑** 20 | 21 | - 将用户地址上资金全部转到归集地址,这里需要注意的是,有的项目方的归集地址也是热钱包地址 22 | - 使用用户地址构建一笔待签名的交易,将交易编码成 32 位的 Msg, 将 Msg 递交给签名机进行签名,签名成功之后返回 signature 23 | - 使用返回的 signature 解析出来 r, s, v; 使用 r,s,v和交易体构建出完整的交易并将交易发送到区块链网络,将用户地址上的资金所动,余额减掉。 24 | - 扫链获取到交易之后更新交易状态:如果交易成功,更新交易状态到成功,并把用户地址锁定的资金减掉,将资金加到归集地址上;如果交易失败,将用户地址锁定的资金返还到余额,更新交易状态为失败,等待下一次重新归集。 25 | 26 | ## **4. 提现逻辑** 27 | 28 | - 用户在业务层发起提现,业务层把该笔提交到钱包层的提现表,钱包层使用热钱包地址签名一笔交易并发送到区块链网络,并且更新提现表里面的交易位已发送,将交易也同时放到 transaction 表, 将热钱包地址里面提现的资金锁定。 29 | 30 | - 扫链扫到这笔之后,通过 TxHash 查到这笔交易,然后匹配 from 地址和 to 地址, 提现的 from 地址是系统内部热钱包地址,to 地址系统外部地址,根据扫链交易状体更新数据库表的状态。 31 | 32 | 💡成功:更新 transaction 和 withdraw 表的交易状态为成功并且将热钱包地址锁定资金减掉 33 | 34 | 💡失败:更新 transaction 表的交易状态为失败并且将热钱包地址锁定资金退回到热钱地址里重新发起提现过程 35 | 36 | ## **5. 热转冷逻辑** 37 | 38 | - 将热钱包地址上的资金转到冷钱包地址,签名流程类似归集,交易签名完成之后将交易发送到区块链网络 39 | 40 | - 扫链进程扫到这笔之后,通过 TxHash 查到这笔交易,然后匹配 from 地址和 to 地址, 热转冷的 from 地址是系统内部热钱包地址,to 系统内配置的冷钱包地址,根据扫链交易状体更新数据库表的状态。 41 | 42 | 💡成功:更新 transaction 表的交易状态为成功并且将热钱包地址锁定资金减掉 43 | 44 | 💡失败:更新 transaction 表的交易状态为失败并且将热钱包地址锁定资金退回到热钱地址里重新发起热转冷的过程 45 | 46 | ## **6.冷转热逻辑** 47 | 48 | - 手动操作转账到热钱包地址 49 | 50 | - 扫链进程扫到这笔交易之后,匹配 from 和 to 地址, 冷转热的 from 地址是冷钱包,to 地址系统内配置的热钱包地址,根据扫链交易状体更新数据库表的状态。 51 | 52 | 💡成功:更新 transaction 表的交易状态为成功,并将余额加到热钱包地址上去 53 | 54 | 💡失败:更新 transaction 表的交易状态为失败,不用处理热钱包余额 55 | 56 | # 二.以太坊钱包代码实战 57 | 58 | - 项目代码:https://github.com/the-web3/eth-wallet 59 | 60 | ## 1.代码简介 61 | 62 | - 表结构定义:https://github.com/the-web3/eth-wallet/blob/main/migrations/00001_create_schema.sql 63 | - 充值逻辑:https://github.com/the-web3/eth-wallet/blob/main/wallet/deposit.go 64 | - 提现逻辑: https://github.com/the-web3/eth-wallet/blob/main/wallet/withdraw.go 65 | - 归集转冷逻辑:https://github.com/the-web3/eth-wallet/blob/main/wallet/collection_cold.go 66 | 67 | ## 2.必要依赖和代码构建 68 | 69 | **2.1.必要依赖** 70 | 71 | - 数据库:postgres 72 | - Golang: [Go 1.21+](https://golang.org/dl/) 73 | 74 | **2.2.构建代码** 75 | 76 | ```text 77 | make eth-wallet 78 | ``` 79 | 80 | **2.3.构建效果展示** 81 | 82 | ```text 83 | guoshijiang@192 eth-wallet % ./eth-wallet 84 | NAME: 85 | eth-wallet - A new cli application 86 | 87 | USAGE: 88 | eth-wallet [global options] command [command options] [arguments...] 89 | 90 | VERSION: 91 | 1.14.6-stable-8b3f4433 92 | 93 | DESCRIPTION: 94 | An exchange wallet scanner services with rpc and rest api server 95 | 96 | COMMANDS: 97 | api 98 | rpc 99 | generate-address 100 | wallet 101 | migrate 102 | version 103 | help, h Shows a list of commands or help for one command 104 | 105 | GLOBAL OPTIONS: 106 | --help, -h show help 107 | --version, -v print the version 108 | ``` 109 | 110 | - api: 启动 http 服务 111 | - rpc: 启动 rpc 服务 112 | - generate-address: 批量生成地址命令 113 | - migrate: 数据库 migrate 114 | - wallet: 启动充值,提现,归集与转冷等进程 115 | 116 | ## **3.运行代码** 117 | 118 | 3.1.**配置环境** 119 | 120 | ```text 121 | ETH_WALLET_MIGRATIONS_DIR="./migrations" 122 | 123 | ETH_WALLET_CHAIN_ID=17000 124 | ETH_WALLET_RPC_RUL="please type your ethereum rpc url" 125 | ETH_WALLET_STARTING_HEIGHT=1960574 126 | ETH_WALLET_CONFIRMATIONS=32 127 | ETH_WALLET_DEPOSIT_INTERVAL=5s 128 | ETH_WALLET_WITHDRAW_INTERVAL=5s 129 | ETH_WALLET_COLLECT_INTERVAL=5s 130 | ETH_WALLET_BLOCKS_STEP=5 131 | 132 | ETH_WALLET_HTTP_PORT=8989 133 | ETH_WALLET_HTTP_HOST="127.0.0.1" 134 | ETH_WALLET_RPC_PORT=8980 135 | ETH_WALLET_RPC_HOST="127.0.0.1" 136 | ETH_WALLET_METRICS_PORT=8990 137 | ETH_WALLET_METRICS_HOST="127.0.0.1" 138 | 139 | ETH_WALLET_SLAVE_DB_ENABLE=false 140 | 141 | ETH_WALLET_MASTER_DB_HOST="127.0.0.1" 142 | ETH_WALLET_MASTER_DB_PORT=5432 143 | ETH_WALLET_MASTER_DB_USER="guoshijiang" 144 | ETH_WALLET_MASTER_DB_PASSWORD="" 145 | ETH_WALLET_MASTER_DB_NAME="eth_wallet" 146 | 147 | ETH_WALLET_SLAVE_DB_HOST="127.0.0.1" 148 | ETH_WALLET_SLAVE_DB_PORT=5432 149 | ETH_WALLET_SLAVE_DB_USER="guoshijiang" 150 | ETH_WALLET_SLAVE_DB_PASSWORD="" 151 | ETH_WALLET_SLAVE_DB_NAME="eth_wallet" 152 | 153 | ETH_WALLET_API_CACHE_LIST_SIZE=0 154 | ETH_WALLET_API_CACHE_LIST_DETAIL=0 155 | ETH_WALLET_API_CACHE_LIST_EXPIRE_TIME=0 156 | ETH_WALLET_API_CACHE_DETAIL_EXPIRE_TIME=0 157 | ``` 158 | 159 | **3.2.创建数据库** 160 | 161 | ```text 162 | create database eth_wallet; 163 | ``` 164 | 165 | **3.3.迁移数据库** 166 | 167 | ```text 168 | ./eth-wallet migrate 169 | ``` 170 | 171 | 执行结果 172 | 173 | ```text 174 | postgres=# \c eth_wallet 175 | 您现在已经连接到数据库 "eth_wallet",用户 "guoshijiang". 176 | eth_wallet=# \d 177 | 关联列表 178 | 架构模式 | 名称 | 类型 | 拥有者 179 | ----------+--------------+--------+------------- 180 | public | addresses | 数据表 | guoshijiang 181 | public | balances | 数据表 | guoshijiang 182 | public | blocks | 数据表 | guoshijiang 183 | public | deposits | 数据表 | guoshijiang 184 | public | tokens | 数据表 | guoshijiang 185 | public | transactions | 数据表 | guoshijiang 186 | public | withdraws | 数据表 | guoshijiang 187 | (7 行记录) 188 | 189 | eth_wallet=# 190 | ``` 191 | 192 | **3.4.批量地址生成** 193 | 194 | ```text 195 | ./eth-wallet generate-address 196 | ``` 197 | 198 | 查看效果 199 | 200 | ```text 201 | eth_wallet=# select count(*) from addresses; 202 | count 203 | ------- 204 | 100 205 | (1 行记录) 206 | 207 | eth_wallet=# 208 | ``` 209 | 210 | 3.5.启动钱包充值,提现,归集,转冷等命令 211 | 212 | ```text 213 | ./eth-wallet wallet 214 | ``` 215 | 216 | 启动效果如下: 217 | 218 | ```text 219 | INFO [07-20|16:26:52.255] exec wallet sync 220 | INFO [07-20|16:26:52.255] loaded chain config config="{ChainID:17000 RpcUrl: StartingHeight:1964509 Confirmations:64 DepositInterval:5000 WithdrawInterval:500 CollectInterval:500 ColdInterval:500 BlocksStep:5}" 221 | 222 | 2024/07/20 16:26:52 /Users/guoshijiang/theweb3/eth-wallet/database/blocks.go:56 record not found 223 | [5.827ms] [rows:0] SELECT * FROM "blocks" ORDER BY number DESC LIMIT 1 224 | INFO [07-20|16:26:52.758] no sync indexed state starting from supplied ethereum height height=1,964,509 225 | INFO [07-20|16:26:53.471] start deposit...... 226 | INFO [07-20|16:26:53.472] start withdraw...... 227 | ``` 228 | 229 | ## 4.API 和 RPC 服务 230 | 231 | **4.1. RPC** 232 | 233 | 如果修改wallet.proto代码,需要执行[proto.sh](https://proto.sh/)将其编译为 golang 语言。 234 | 235 | ```text 236 | ./proto.sh 237 | ``` 238 | 239 | 启动 rpc 服务 240 | 241 | ```text 242 | ./eth-wallet rpc 243 | ``` 244 | 245 | 使用grpcui作为工具来请求rpc服务器 246 | 247 | ```text 248 | grpcui -plaintext 127.0.0.1:8089 249 | ``` 250 | 251 | 调用 rpc 提交提现信息 252 | 253 | - 请求示例 254 | 255 | ```text 256 | grpcurl -plaintext -d '{ 257 | "requestId": "11111", 258 | "chainId": "11", 259 | "fromAddress": "0xc144779fa97544872879ec162fcd7367141db17f", 260 | "toAddress": "0xc144779fa97544872879ec162fcd7367141db171", 261 | "tokenAddress": "0x00", 262 | "amount": "10000000000000000000" 263 | }' 127.0.0.1:8980 services.thewebthree.wallet.WalletService.submitWithdrawInfo 264 | ``` 265 | 266 | - 结果 267 | 268 | ```text 269 | { 270 | "code": "2000", 271 | "msg": "submit withdraw success", 272 | "hash": "0x0000000000000000000000000000000000000000000000000000000000000000" 273 | } 274 | ``` 275 | 276 | 4.2.**API** 277 | 278 | 启动 API 服务 279 | 280 | ```text 281 | ./eth-wallet api 282 | ``` 283 | 284 | 获取充值列表 285 | 286 | - 请求示例 287 | 288 | ```text 289 | curl --location --request GET 'http://127.0.0.1:8989/api/v1/deposits?address=0xc144779fa97544872879ec162fcd7367141db17f&page=1&pageSize=10' \ 290 | --form 'address="0x62a58ec98bbc1a1b348554a19996305edc224e32"' \ 291 | --form 'page="1"' \ 292 | --form 'pageSize="10"' 293 | ``` 294 | 295 | - 返回值 296 | 297 | ```text 298 | { 299 | "Current": 1, 300 | "Size": 10, 301 | "Total": 2, 302 | "Records": [ 303 | { 304 | "guid": "e6277fd2-4672-11ef-a12f-0e6e6b1f0bae", 305 | "block_hash": "0x2515a19d875ffdacae62d20a0f6eddfd3a35206aa07b5cfdc0c49abe7148a9cf", 306 | "BlockNumber": 1964544, 307 | "hash": "0x19a97821f6337789294eae9f8ce21455aa05b9e31737e3f48394413e3d0d36a1", 308 | "from_address": "0x72ffaa289993bcada2e01612995e5c75dd81cdbc", 309 | "to_address": "0xc144779fa97544872879ec162fcd7367141db17f", 310 | "token_address": "0x0000000000000000000000000000000000000000", 311 | "Fee": 176820000, 312 | "Amount": 100000000000000000, 313 | "status": 1, 314 | "TransactionIndex": 59, 315 | "Timestamp": 1721464476 316 | }, 317 | { 318 | "guid": "3835ecd8-4672-11ef-a12f-0e6e6b1f0bae", 319 | "block_hash": "0xcd8835a2f98bd51f3f88be4b248e52534c251b86aaa6bf0ed2785280d67c2f81", 320 | "BlockNumber": 1964522, 321 | "hash": "0x6841a22be3ae7b1c138057ae522017f652f49b579b6c5a26b8727e1f1ce899c8", 322 | "from_address": "0x72ffaa289993bcada2e01612995e5c75dd81cdbc", 323 | "to_address": "0xc144779fa97544872879ec162fcd7367141db17f", 324 | "token_address": "0x0000000000000000000000000000000000000000", 325 | "Fee": 182301000, 326 | "Amount": 10000000000000000, 327 | "status": 1, 328 | "TransactionIndex": 26, 329 | "Timestamp": 1721464185 330 | } 331 | ] 332 | } 333 | ``` 334 | 335 | 获取提现列表 336 | 337 | - 请求示例 338 | 339 | ```text 340 | curl --location --request GET 'http://127.0.0.1:8989/api/v1/withdrawals?address=0x62a58ec98bbc1a1b348554a19996305edc224e32&page=1&pageSize=10' \ 341 | --form 'address="0x62a58ec98bbc1a1b348554a19996305edc224e32"' \ 342 | --form 'page="1"' \ 343 | --form 'pageSize="10"' 344 | ``` 345 | 346 | - 结果 347 | 348 | ```text 349 | { 350 | "Current": 1, 351 | "Size": 10, 352 | "Total": 1, 353 | "Records": [ 354 | { 355 | "guid": "803fd471-e74a-403e-a934-1b917e801518", 356 | "block_hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 357 | "BlockNumber": 1, 358 | "hash": "0x0000000000000000000000000000000000000000000000000000000000000000", 359 | "from_address": "0x62a58ec98bbc1a1b348554a19996305edc224e32", 360 | "to_address": "0x62a58ec98bbc1a1b348554a19996305edc224e32", 361 | "token_address": "0x62a58ec98bbc1a1b348554a19996305edc224e32", 362 | "Fee": 1, 363 | "Amount": 1000000000000000000, 364 | "status": 0, 365 | "TransactionIndex": 1, 366 | "tx_sign_hex": "", 367 | "Timestamp": 1721466415 368 | } 369 | ] 370 | } 371 | ``` 372 | 373 | 提交提现交易 374 | 375 | - 请求示例 376 | 377 | ```text 378 | curl --location --request POST 'http://127.0.0.1:8989/api/v1/submit/withdrawals?fromAddress=0x62a58ec98bbc1a1b348554a19996305edc224e32&toAddress=0x62a58ec98bbc1a1b348554a19996305edc224e32&tokenAddress=0x62a58ec98bbc1a1b348554a19996305edc224e32&amount=1000000000000000000' 379 | ``` 380 | 381 | - 结果 382 | 383 | ```text 384 | { 385 | "code": 2000, 386 | "msg": "submit transaction success" 387 | } 388 | ``` 389 | 390 | # 三. 总结 391 | 392 | 本代码改造之后是可以做为生产代码来用的,可以根据自己的需求改造和业务层的对接,本项目中归集地址和热钱包是共用一个地址,这里可能需要根据具体的业务需求进行改造成多归集地址,多热钱包地址之类的处理。有的项目中可能还设置温钱包这样的业务,也需要根据业务调整一些细节的代码。 -------------------------------------------------------------------------------- /bnbPythonSdk/README.md: -------------------------------------------------------------------------------- 1 | # 第十九章:BNB python-sdk 源码分析 2 | 3 | ## 一. binance-chain 和 Binance DEX 4 | 5 | Binance Chain是Binance及其社区开发的区块链软件系统。 Binance DEX指的是在Binance Chain之上开发的去中心化交易所。 6 | 7 | ## 二. binance-chain 的 python-sdk 安装与使用 8 | 9 | ### 1.安装 10 | 11 | 要求 python 版本为 3.6 以上,安装命令很简单, 12 | 13 | pip install python-binance-chain 14 | 15 | 如果安装过程中出现问题,请按照 sec256k1-py, 此处提供一种安装 sec256k1-py 失败的解决方案​。 16 | 17 | #### 错误1:pkg-config包安装不成功 18 | 19 | 这个错误是由于pkg-config缺失引起的,手动安装这个包 20 | 21 | #### 错误2:secp256k1包安装不成功 22 | 23 | 安装这个包之前必须安装好这几个东西个,automake,pkg-config,libtool,libffi,gmp。 24 | 25 | ### 2.使用 26 | 27 | #### 2.1 生成钱包私钥,公钥和地址 28 | 29 | from binance_chain.wallet import Wallet 30 | from binance_chain.environment import BinanceEnvironment 31 | 32 | product_env = BinanceEnvironment.get_production_env() 33 | wallet = Wallet.create_random_wallet(env=product_env) 34 | print(wallet.address) 35 | print(wallet.private_key) 36 | print(wallet.public_key) 37 | 38 | 代码比较简单,这里就不再多做解释了,下面看一下执行结果: 39 | 40 | bnb1fx64c76dpew4pqqsrmvdg0l8xe8ud04h44yxsy 41 | b88d4cc333e02d44a2e2426425d3ec4956e3793ef00a0d77a8c3338f127aa58d 42 | b'\x02d\r\x84\x1f|\xd9J\x90\x85\xc7\x01P\xb8\xcc0\xca\xb2\x9d\xd3\x11?#\xec\xca\x19\\\xd8@\x8d\x91\xc9q' 43 | 44 | 上面的结果依次是地址,私钥和经过处理的公钥,申明一下原始的公钥不是这样的 45 | 46 | #### 2.2 HttpApiClient 的使用 47 | 48 | from binance_chain.http import HttpApiClient 49 | from binance_chain.environment import BinanceEnvironment 50 | testnet_env = BinanceEnvironment.get_production_env() 51 | client = HttpApiClient(env=testnet_env) 52 | prod_client = HttpApiClient() 53 | 54 | account = client.get_account('bnb1acecavtwz6s4fat6540a5dz3vcq25lrsccxapp') 55 | print(account) 56 | 57 | account_seq = client.get_account_sequence('bnb1acecavtwz6s4fat6540a5dz3vcq25lrsccxapp') 58 | print(account_seq) 59 | 60 | fees = client.get_fees() 61 | print(fees) 62 | 63 | 代码中第一个是获取 `bnb1acecavtwz6s4fat6540a5dz3vcq25lrsccxapp`的账户信息,第二个获取`bnb1acecavtwz6s4fat6540a5dz3vcq25lrsccxapp`交易序号,第三个获取费用。 64 | 65 | 执行结果如下: 66 | 67 | {'account_number': 198155, 'address': 'bnb1acecavtwz6s4fat6540a5dz3vcq25lrsccxapp', 'balances': [{'free': '0.46438600', 'frozen': '0.00000000', 'locked': '0.00000000', 'symbol': 'BNB'}], 'flags': 0, 'public_key': [3, 142, 211, 241, 139, 162, 23, 200, 94, 195, 109, 117, 254, 214, 166, 254, 154, 61, 151, 18, 33, 59, 253, 59, 34, 38, 241, 210, 157, 129, 125, 240, 25], 'sequence': 19} 68 | 69 | 70 | {'sequence': 19} 71 | 72 | 73 | [{'msg_type': 'submit_proposal', 'fee': 500000000, 'fee_for': 1}, {'msg_type': 'deposit', 'fee': 62500, 'fee_for': 1}, {'msg_type': 'vote', 'fee': 0, 'fee_for': 3}, {'msg_type': 'create_validator', 'fee': 1000000000, 'fee_for': 1}, {'msg_type': 'remove_validator', 'fee': 100000000, 'fee_for': 1}, {'msg_type': 'dexList', 'fee': 100000000000, 'fee_for': 2}, {'msg_type': 'orderNew', 'fee': 0, 'fee_for': 3}, {'msg_type': 'orderCancel', 'fee': 0, 'fee_for': 3}, {'msg_type': 'issueMsg', 'fee': 50000000000, 'fee_for': 2}, {'msg_type': 'mintMsg', 'fee': 500000000, 'fee_for': 2}, {'msg_type': 'tokensBurn', 'fee': 50000000, 'fee_for': 1}, {'msg_type': 'tokensFreeze', 'fee': 500000, 'fee_for': 1}, {'fixed_fee_params': {'msg_type': 'send', 'fee': 37500, 'fee_for': 1}, 'multi_transfer_fee': 30000, 'lower_limit_as_multi': 2}, {'dex_fee_fields': [{'fee_name': 'ExpireFee', 'fee_value': 25000}, {'fee_name': 'ExpireFeeNative', 'fee_value': 5000}, {'fee_name': 'CancelFee', 'fee_value': 25000}, {'fee_name': 'CancelFeeNative', 'fee_value': 5000}, {'fee_name': 'FeeRate', 'fee_value': 1000}, {'fee_name': 'FeeRateNative', 'fee_value': 400}, {'fee_name': 'IOCExpireFee', 'fee_value': 10000}, {'fee_name': 'IOCExpireFeeNative', 'fee_value': 2500}]}, {'msg_type': 'timeLock', 'fee': 1000000, 'fee_for': 1}, {'msg_type': 'timeUnlock', 'fee': 1000000, 'fee_for': 1}, {'msg_type': 'timeRelock', 'fee': 1000000, 'fee_for': 1}, {'msg_type': 'setAccountFlags', 'fee': 100000000, 'fee_for': 1}] 74 | 75 | 上面这些我们不做过多的解释,关于 BNB 钱包开发详细情况请查看作者的另一篇文章 76 | 77 | #### 2.3 交易签名的使用 78 | 79 | from binance_chain.messages import TransferMsg, StdTxMsg, Signature 80 | from binance_chain.wallet import Wallet 81 | 82 | wallet = Wallet('私钥') 83 | wallet._account_number=198155 84 | wallet._sequence=18 85 | wallet._chain_id='Binance-Chain-Tigris' 86 | transfer_msg = TransferMsg( 87 | wallet=wallet, 88 | symbol='BNB', 89 | amount=0.01, 90 | to_address='bnb1k3ekzgy2ana55s52ckj5pf02yypj6kcr6rme6f', 91 | memo='57891114' 92 | ) 93 | data = transfer_msg.to_hex_data().decode() 94 | print(data) 95 | 96 | 上面的私钥替换成自己的就行,这是 BNB 转账交易签名,这里简单说一下 BNB 的交易特征,首先签名的时候 account_number 和 sequence 一个是账户号一个是交易序号,BNB 交易是带 memo, 他的交易可以一对一,一对多,多对一,多对多转账,BNB 交易有 Input 和 Output, 但他上层调用没有找零的概念。 97 | 98 | #### 2.4 导入私钥 99 | 100 | from binance_chain.wallet import Wallet 101 | from binance_chain.environment import BinanceEnvironment 102 | 103 | testnet_env = BinanceEnvironment.get_production_env() 104 | wallet = Wallet('私钥', env=testnet_env) 105 | print(wallet.address) 106 | print(wallet.private_key) 107 | print(wallet.public_key_hex) 108 | 109 | 代码比较简单,直接看执行结果 110 | 111 | bnb1k3ekzgy2ana55s52ckj5pf02yypj6kcr6rme6f 112 | 882ddb71dc3d2edb54472ecf60d55b8585257639a47b425fdadb223f6698a203 113 | b'023fce18eda7b4599367b9af64771693ebdc9a2897ab63d50dd4f77586830cdd42' 114 | 115 | #### 2.5 获取交易并解析交易 116 | 117 | import binascii 118 | from io import BytesIO 119 | import typing 120 | import base64 121 | from binance_chain import messages 122 | from binance_chain.protobuf import dex_pb2 123 | from binance_chain.utils import segwit_addr 124 | from binance_chain.http import HttpApiClient 125 | from binance_chain.node_rpc.http import HttpRpcClient 126 | from binance_chain.environment import BinanceEnvironment 127 | 128 | 129 | def extract_send_msg_addresses(send_msg: dex_pb2.Send) -> typing.Tuple[str]: 130 | from_address = segwit_addr.encode('bnb', send_msg.inputs[0].address) 131 | to_address = segwit_addr.encode('bnb', send_msg.outputs[0].address) 132 | return from_address, to_address 133 | 134 | def is_bnb_transaction(send_msg: dex_pb2.Send): 135 | for inp in send_msg.inputs: 136 | for coin in inp.coins: 137 | if coin.denom != 'BNB': 138 | return False 139 | for out in send_msg.outputs: 140 | for coin in out.coins: 141 | if coin.denom != 'BNB': 142 | return False 143 | return True 144 | 145 | def parse_msg( msg_bytes: bytes) -> dex_pb2.StdTx: 146 | msg_type = msg_bytes[:4] 147 | msg_class = amino_msg_type_to_class(msg_type) 148 | msg_pb2 = msg_class().FromString(msg_bytes[4:]) 149 | return msg_pb2 150 | 151 | def amino_msg_type_to_class(msg_type: bytes): 152 | return { 153 | messages.NewOrderMsg.AMINO_MESSAGE_TYPE: dex_pb2.NewOrder, 154 | messages.CancelOrderMsg.AMINO_MESSAGE_TYPE: dex_pb2.CancelOrder, 155 | messages.FreezeMsg.AMINO_MESSAGE_TYPE: dex_pb2.TokenFreeze, 156 | messages.UnFreezeMsg.AMINO_MESSAGE_TYPE: dex_pb2.TokenUnfreeze, 157 | messages.TransferMsg.AMINO_MESSAGE_TYPE: dex_pb2.Send, 158 | messages.VoteMsg.AMINO_MESSAGE_TYPE: dex_pb2.Vote, 159 | }.get(binascii.hexlify(msg_type).upper()) 160 | 161 | 162 | def parse_stdtx(tx_bytes: bytes) -> dex_pb2.StdTx: 163 | varint_length = decode_bytes(tx_bytes[:2]) 164 | msg_type = tx_bytes[2:6] 165 | assert varint_length == len(tx_bytes) - 2 166 | assert (binascii.hexlify(msg_type).decode('utf-8').lower() == 167 | messages.StdTxMsg.AMINO_MESSAGE_TYPE.decode('utf-8').lower()) 168 | stdtx_pb2 = dex_pb2.StdTx().FromString(tx_bytes[6:]) 169 | return stdtx_pb2 170 | 171 | 172 | def decode_bytes(buf): 173 | return decode_stream(BytesIO(buf)) 174 | 175 | def decode_stream(stream): 176 | shift = 0 177 | result = 0 178 | while True: 179 | i = read_one(stream) 180 | result |= (i & 0x7f) << shift 181 | shift += 7 182 | if not (i & 0x80): 183 | break 184 | return result 185 | 186 | def read_one(stream): 187 | c = stream.read(1) 188 | if c == b'': 189 | raise EOFError("Unexpected EOF while reading bytes") 190 | return ord(c) 191 | 192 | 193 | httpapiclient = HttpApiClient() 194 | listen_addr ='https://dataseed3.ninicoin.io/' 195 | rpc_client = HttpRpcClient(listen_addr) 196 | 197 | h = 30060722 198 | block = rpc_client.get_block(30060722) 199 | txs = block['block']['data']['txs'] 200 | print(txs) 201 | if txs != None: 202 | for tx_str in txs: 203 | tx_bytes = base64.b64decode(tx_str) 204 | stdtx_pb2 = parse_stdtx(tx_bytes) 205 | 206 | msg = stdtx_pb2.msgs[0] 207 | msg_pb2 = parse_msg(msg) 208 | 209 | # 只支持转账类型,其他类型直接抛弃 210 | if type(msg_pb2) != dex_pb2.Send: 211 | continue 212 | 213 | # 只支持 BNB 转账类型, 代币直接抛弃 214 | if not is_bnb_transaction(msg_pb2): 215 | continue 216 | 217 | from_address, to_address = extract_send_msg_addresses(msg_pb2) 218 | 219 | product_env = BinanceEnvironment.get_production_env() 220 | client = HttpApiClient(env=product_env) 221 | 222 | queried_txs = client.get_transactions(address=from_address, height=h)['tx'] 223 | print(queried_txs) 224 | tx_dict = list(filter(lambda t: t['toAddr'] == to_address, queried_txs))[0] 225 | print(tx_dict) 226 | 227 | 这里我们也不多解释,如果看不懂这个代码,请联系作者买视频教程,下面是执行结果: 228 | 229 | 第一次打印 230 | 231 | ['0QHwYl3uClYWbmgbChS7ldSfPDJKi4D9MXmzzQ49BOWlARILRE9TLTEyMF9CTkIaLUJCOTVENDlGM0MzMjRBOEI4MEZEMzE3OUIzQ0QwRTNEMDRFNUE1MDEtODc5MhJxCibrWumHIQMOKyrvTAm/lmfD6MTtDOY/YOR5N6UWii88fzT7RbikOxJA8PlOxc73ShBQ0tXv6pXTomEDyYGYzXZIvNRF5G0+YxRalvnMurFpaJ3oUJoGFK5Dkcej+o/uIXy3ftG6LUzfkxjnqw8g2EQgAQ==', 'ygHwYl3uCkgqLIf6CiAKFLRzYSCK7PtKQorFpUCl6iEDLVsDEggKA0JOQhDEXhIgChTuM46xbhahVPV6pV/aNFFmAKp8cBIICgNCTkIQxF4ScAom61rphyECP84Y7ae0WZNnua9kdxaT69yaKJerY9UN1Pd1hoMM3UISQCmg1tAoOQPkktIOZ36QuZD5zTrVUbd0FYo/SW7c3NxADqeRhgO46xixNprsW+Fhzf+6/yuc8NmyuLE7gtw679MYubwNIBwaCDQ5NDA5Njgz'] 232 | 233 | 第二次打印 234 | 235 | 236 | [{'txHash': 'FEEDE276687D8B98315711A0ACD019241C4BA5F8B6101E496F4B8A0DA52B3FD8', 'blockHeight': 30060722, 'txType': 'TRANSFER', 'timeStamp': '2019-08-26T11:53:26.752Z', 'fromAddr': 'bnb1k3ekzgy2ana55s52ckj5pf02yypj6kcr6rme6f', 'toAddr': 'bnb1acecavtwz6s4fat6540a5dz3vcq25lrsccxapp', 'value': '0.00012100', 'txAsset': 'BNB', 'txFee': '0.00037500', 'proposalId': None, 'txAge': 39626, 'orderId': None, 'code': 0, 'data': None, 'confirmBlocks': 0, 'memo': '49409683', 'source': 0, 'sequence': 28}] 237 | 238 | 239 | {'txHash': 'FEEDE276687D8B98315711A0ACD019241C4BA5F8B6101E496F4B8A0DA52B3FD8', 'blockHeight': 30060722, 'txType': 'TRANSFER', 'timeStamp': '2019-08-26T11:53:26.752Z', 'fromAddr': 'bnb1k3ekzgy2ana55s52ckj5pf02yypj6kcr6rme6f', 'toAddr': 'bnb1acecavtwz6s4fat6540a5dz3vcq25lrsccxapp', 'value': '0.00012100', 'txAsset': 'BNB', 'txFee': '0.00037500', 'proposalId': None, 'txAge': 39626, 'orderId': None, 'code': 0, 'data': None, 'confirmBlocks': 0, 'memo': '49409683', 'source': 0, 'sequence': 28} 240 | 241 | 第二次打印的结果是解析出来的交易 242 | 243 | 好了,sdk 的使用我们就先讲到这里,接下来我们解释一下源码 244 | 245 | ### 3.源码解析 246 | 247 | .: 248 | ![.: 249 | ](https://github.com/guoshijiang/blockchain-wallet/blob/master/img/bnbsdk.png 250 | ) 251 | 252 | 上图是 BNB pyhon-sdk 的目录结构图,图中包含了 BNB 的主要的目录结构,咱们先简单的对目录结构做分析,接下来选择 2 到 3 个比较重要的模块说明一下,我相信,只要我简单说明之后,大家对 sdk 的理解会更加深入。 253 | 254 | #### 3.1 SDK 目录解析 255 | 256 | #### 3.2 transfer整个转账过程源码解析 257 | 258 | ##### 3.2.1 仿照 SDK 实现整个签名的流程 259 | 260 | ##### 3.2.2.源码解析 261 | 262 | 263 | #### 3.3 钱包相关的源码解析(含私钥->公钥->地址的整个过程) 264 | 265 | ##### 3.2.1 仿照 SDK 实现整个地址生成流程 266 | 267 | ##### 3.2.2.源码解析 268 | 269 | ### 4. 关于本文 270 | 271 | 本文是作者推出的 BNB 系列课程的一部分,大家如果觉能可以,可以联系作者买 BNB 相关的课程哦,包含 Java, Python, ,PHP, NodeJs 和 Go 等语言相关的课程,价格实惠。 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | -------------------------------------------------------------------------------- /EOS/EOS README.md: -------------------------------------------------------------------------------- 1 | # EOS 系列钱包开发详细教程 2 | 3 | # 一. EOS 简介 4 | 5 | ## 1.概述 6 | 7 | EOS(Enterprise Operating System)是一种开源的区块链平台,旨在提供一个可扩展的去中心化应用程序(DApp)开发环境。它采用了类似操作系统的架构,具有账户、验证器、数据库、消息传递等核心功能,为开发者提供了一个强大而灵活的平台,以构建各种区块链应用。 8 | 9 | 以下是一些 EOS 的关键特性和组成部分: 10 | 11 | - **智能合约平台**:EOS 提供了一个智能合约平台,开发者可以在上面部署和执行智能合约。这些合约可以编写使用 C++ 或其他支持的语言,并通过 EOSIO 软件进行部署。 12 | - **高性能**:EOS 致力于实现高吞吐量和低延迟的区块链交易处理。它使用了一种名为“委托权益证明(Delegated Proof of Stake,DPoS)”的共识机制,这种机制通过选举一组验证者来验证交易,并将交易打包进区块中。 13 | - **低成本**:EOS 设计的目标之一是降低区块链应用程序的开发和运行成本。它使用的资源模型允许用户根据其持有的代币来获取资源,而不需要支付每笔交易的费用。 14 | - **水平扩展性**:EOS 被设计为可以轻松地水平扩展,以满足不断增长的用户需求。其架构允许多个并行的链运行,并且可以通过网络链接来扩展其功能。 15 | - **治理模型**:EOS 采用了一种基于代币持有者的治理模型,使持币者能够对网络的发展和升级做出决策。这种模型通过投票来选择验证者和制定网络规则。 16 | 17 | ## 2. 共识算法 18 | 19 | EOS 使用的共识算法是委托权益证明(Delegated Proof of Stake,DPoS)。DPoS 是一种共识机制,旨在通过选举一组验证者来验证交易并产生新的区块。与传统的 PoW(Proof of Work,工作量证明)不同,DPoS 不需要大量的计算能力,因此能够实现更高的交易吞吐量和更低的延迟。 20 | 21 | EOS 的 DPoS 共识算法的基本工作原理如下: 22 | 23 | - **验证者选举**:在 EOS 中,持有代币的持有者有权利选举一组称为“区块生产者(Block Producers)”的验证者。这些验证者负责验证交易并打包它们到区块中,并有权获得相应的奖励。 24 | - **轮流产生区块**:通过一种轮流的方式,选举产生的验证者依次产生新的区块。每个验证者在其轮到产生区块时,负责验证之前的交易,并将其打包成一个新的区块,然后将该区块添加到区块链上。 25 | - **投票权重**:在 DPoS 中,验证者的权重是根据他们得到的选票数量来确定的。持有更多代币的用户拥有更多的选票权重,因此他们对验证者的选举结果有更大的影响力。 26 | - **奖励机制**:作为对验证者的激励,他们会获得相应数量的 EOS 代币作为区块奖励。这些奖励既来自新发行的代币,也来自交易费用的分配。 27 | 28 | 通过 DPoS 共识算法,EOS 实现了高吞吐量、低延迟的交易处理能力,并且能够通过投票机制实现网络的去中心化治理。这种机制使得 EOS 能够适用于各种规模的应用场景,并且具有较高的扩展性和灵活性。 29 | 30 | 尽管 EOS 主要采用 DPoS,但某些版本的 EOSIO 软件中可能会使用 PBFT 或类似的拜占庭容错算法来增强网络的安全性和稳定性。这种混合使用的方法可能会结合 DPoS 的高吞吐量和 PBFT 的安全性,以提供更强大的区块链网络。 31 | 32 | ## 3. EOS 的 PowerUp 机制 33 | 34 | EOS 的 PowerUp 机制是一种新的资源模型,旨在改善网络的资源管理和使用效率。在之前的资源模型中,EOS 网络的用户需要持有一定数量的 EOS 代币来获取网络资源(如 CPU、NET 和 RAM),这种资源模型可能会导致一些问题,比如资源浪费和网络拥堵。 35 | 36 | PowerUp 机制的主要思想是让用户可以直接购买和使用网络资源,而不必持有 EOS 代币。具体来说,PowerUp 机制包含以下几个关键点: 37 | 38 | - **资源购买**:用户可以通过使用信用卡或其他支付方式直接购买网络资源,而不需要事先持有 EOS 代币。他们可以购买所需的 CPU、NET 和 RAM,以满足其在 EOS 网络上的需求。 39 | - **资源使用**:一旦用户购买了资源,他们就可以立即开始使用它们,而不必等待资源的释放或解冻。这种即时的资源使用方式可以帮助用户更灵活地管理他们的网络资源。 40 | - **自动续费**:PowerUp 机制还支持资源的自动续费功能,用户可以选择在资源用尽之前自动续费资源,以确保他们的应用程序不会因为资源不足而中断。 41 | - **弹性定价**:PowerUp 机制采用了一种基于市场供需关系的动态定价模型,资源的价格会根据市场需求的变化而变化。这种弹性定价机制可以帮助保持资源的供需平衡,同时避免资源的浪费和滥用。 42 | 43 | 总的来说,PowerUp 机制旨在改善 EOS 网络的资源管理和使用效率,使用户可以更灵活地获取和使用网络资源,同时帮助 EOS 生态系统实现更加健康和可持续的发展。 44 | 45 | # 二. WAXP 简介 46 | 47 | WAXP(WAX Protocol Token)是 WAX(Worldwide Asset eXchange)区块链平台的原生代币。WAX 是一个专注于虚拟商品交易的区块链网络,旨在为游戏玩家、收藏家和虚拟商品交易者提供一个去中心化的市场和交易平台。 48 | 49 | 以下是关于 WAXP 的详细介绍: 50 | 51 | - **原生代币**:WAXP 是 WAX 平台的原生代币,作为网络中的货币和经济激励手段。持有 WAXP 的用户可以用它来支付交易费用、购买虚拟商品和服务,以及参与网络的治理。 52 | - **区块链基础设施**:WAXP 是构建在 WAX 区块链上的,这个区块链专门设计用于支持虚拟商品的交易和交换。WAX 区块链基于 DPoS 共识机制,旨在提供高吞吐量、低延迟的交易处理能力。 53 | - **虚拟商品交易平台**:WAX 平台为游戏开发商、虚拟商品发行者和收藏家提供了一个去中心化的市场和交易平台。通过 WAX 区块链,用户可以安全地买卖虚拟商品,包括游戏物品、数字艺术品、虚拟地产等。 54 | - **智能合约支持**:WAX 区块链支持智能合约技术,使开发者能够部署和执行自定义的智能合约。这些合约可以用于管理虚拟商品的所有权、交易规则和分配方案等。 55 | - **NFT 生态系统**:WAXP 也是支持 NFT(Non-Fungible Token,非同质化代币)的生态系统中的重要组成部分。在 WAX 平台上,开发者和发行者可以创建和交易各种 NFT,包括游戏道具、数字艺术品、收藏品等。 56 | 57 | WAXP 是 fork EOS 的代码改造的一个项目,故而很多行为都和 EOS 是一样。 58 | 59 | # 二. 地址生成与激活 60 | 61 | ## 1.准备账户,生成 EOS 密钥对 62 | 63 | ```javascript 64 | test('create Key', async ()=>{ 65 | const privteKey = "Ecc privateKey" 66 | const wifKey = eosEcc.PrivateKey.fromHex(privteKey) 67 | console.log(wifKey.toWif()) 68 | console.log(wifKey.toPublic().toString()); 69 | }); 70 | ``` 71 | 72 | 2. 创建新账号 73 | 74 | ```javascript 75 | test('json to abi one', async ()=>{ 76 | const abiCreateData = { 77 | code: "eosio", 78 | action: "newaccount", 79 | args: { 80 | creator: "ox4x5zhltd4t", // 支付创建新用户费用的基账户(用户名) 81 | name: "theweb3test", // 新账户的用户名 仅支持字母和1-5数字 82 | owner: { 83 | threshold: 1, 84 | keys: [ 85 | { 86 | key: "EOS5rKs3ADoAdjrxkhTSjRNmCHqghDVFcrXgbjnjG5SjVNZi8ww95", // 新账户的 Owner Public Key 87 | weight: 1, 88 | }, 89 | ], 90 | accounts: [], 91 | waits: [], 92 | }, 93 | active: { 94 | threshold: 1, 95 | keys: [ 96 | { 97 | key: "EOS7vFFPoJvpYsYJ92AdGwVPRiFTA2JMJGmzz71k1zTMUTHJZYuqp", // Active Public Key 98 | weight: 1, 99 | }, 100 | ], 101 | accounts: [], 102 | waits: [], 103 | }, 104 | }, 105 | }; 106 | console.log(JSON.stringify(abiCreateData)) 107 | }); 108 | ``` 109 | 110 | 数据的 json 格式的数据转换成 16 进制字符串数据,使用 https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/abi_json_to_bin 这个接口,或者实现类似的代码编码数据。 111 | 112 | ## 3. 账户 buyrambytes 113 | 114 | ```javascript 115 | test('json to abi two', async ()=>{ 116 | const abiRawData = { 117 | code: "eosio", 118 | action: "buyrambytes", 119 | args: { 120 | payer: "ox4x5zhltd4t", //付款账户 121 | receiver: "theweb3test", // 收款账户 122 | bytes: 2996, // 数量 123 | }, 124 | }; 125 | console.log(JSON.stringify(abiRawData)); 126 | }); 127 | ``` 128 | 129 | 数据的 json 格式的数据转换成 16 进制字符串数据,使用 https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/abi_json_to_bin 这个接口,或者实现类似的代码编码数据。 130 | 131 | ## 4. 抵押 CPU 和 NET 132 | 133 | ```javascript 134 | test('di ya cpu', async ()=>{ 135 | const abiDelegateData = { 136 | code: "eosio", 137 | action: "delegatebw", 138 | args: { 139 | from: "ox4x5zhltd4t", 140 | receiver: "theweb3test", 141 | stake_net_quantity: "0.200000 EOS", 142 | stake_cpu_quantity: "0.200000 EOS", 143 | transfer: 0, 144 | }, 145 | }; 146 | console.log(JSON.stringify(abiDelegateData)); 147 | }); 148 | ``` 149 | 150 | 数据的 json 格式的数据转换成 16 进制字符串数据,使用 https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/abi_json_to_bin 这个接口,或者实现类似的代码编码数据。 151 | 152 | ## 6. 离线构建创建新账号的交易 153 | 154 | ```javascript 155 | test('create account', async ()=>{ 156 | const timeStamp = timePointSecToDate( 157 | dateToTimePointSec("2022-04-20T11:40:36.000") + 600 158 | ); 159 | const serdata = { 160 | max_net_usage_words: 0, 161 | max_cpu_usage_ms: 0, 162 | delay_sec: 0, 163 | context_free_actions: [], 164 | actions: [ 165 | { 166 | account: "eosio", 167 | name: "newaccount", 168 | authorization: [{ actor: "aaaashiiang", permission: "owner" }], 169 | data: "c0a671aee1ec8e3f00000857612db64a01000000010002c9fdc9a4df56c7bce22fabc6f31fb844293459fa7b53192ff8cac611ea36b3fc0100000001000000010002c9fdc9a4df56c7bce22fabc6f31fb844293459fa7b53192ff8cac611ea36b3fc01000000", 170 | }, 171 | { 172 | account: "eosio", 173 | name: "buyrambytes", 174 | authorization: [{ actor: "aaaashiiang", permission: "owner" }], 175 | data: "c0a671aee1ec8e3f00000857612db64ab40b0000", 176 | }, 177 | { 178 | account: "eosio", 179 | name: "delegatebw", 180 | authorization: [{ actor: "aaaashiiang", permission: "owner" }], 181 | data: "c0a671aee1ec8e3f00000857612db64a002d3101000000000857415800000000002d310100000000085741580000000000", 182 | }, 183 | ], 184 | transaction_extensions: [], 185 | expiration: timeStamp, 186 | ref_block_num: 177994685 & 0xffff, 187 | ref_block_prefix: 2207165085, 188 | }; 189 | let buffer = new Serialize.SerialBuffer({ 190 | textEncoder: TextEncoder, 191 | textDecoder: TextDecoder, 192 | }); 193 | transactionTypes.get("transaction").serialize(buffer, serdata); 194 | const privateKey = "PrivateKeyEOS"; 195 | const signatureProvider = new JsSignatureProvider([privateKey]); 196 | const signature = await signatureProvider.sign({ 197 | chainId: chainId, 198 | requiredKeys: ["publicKey"], 199 | serializedTransaction: buffer.asUint8Array(), 200 | }); 201 | const res = { 202 | signatures: signature, 203 | compression: 0, 204 | packed_context_free_data: "", 205 | packed_trx: Serialize.arrayToHex(buffer.asUint8Array()), 206 | }; 207 | console.log(JSON.stringify(res)); 208 | }) 209 | }); 210 | ``` 211 | 212 | - 把交易发送到区块链网络激活账户 213 | 214 | # 三. 交易离线签名 215 | 216 | ## 1.普通签名 217 | 218 | ```javascript 219 | describe('Sign Transaction', ()=>{ 220 | test('waxp sign tx', async ()=>{ 221 | const privateKey = "eccPrivateKey" 222 | const timeStamp = timePointSecToDate( 223 | dateToTimePointSec("2022-03-23T06:44:56.000") + 600 224 | ); 225 | const params = { 226 | txObj: { 227 | block: 173121671, // 最新区块:getInfo.head_block_num 228 | prefix: 1239139801, // 最新区块后缀:getBlock.ref_block_prefix 229 | expiration: timeStamp, // 交易过期时间戳:getBlock.timestamp 230 | chainId: "1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4", 231 | from: "aaaashiiang", 232 | to: "lrn1zg5umbqp", 233 | quantity: "1.00000000", 234 | memo: "1222" 235 | }, 236 | network: 'main_net', 237 | privs: [{ key: privateKey }] 238 | } 239 | const tx_msg = await signTransaction(params) 240 | console.log("tx_msg===", tx_msg) 241 | }) 242 | }) 243 | ``` 244 | 245 | ## 2. Powerup 签名 246 | 247 | ```javascript 248 | describe('Sign powerUp Transaction', ()=>{ 249 | test('waxp power tx', async ()=>{ 250 | const privateKey = "eccPrivateKey" 251 | const timeStamp = timePointSecToDate( 252 | dateToTimePointSec("2022-03-31T07:00:59.500") + 600 253 | ); 254 | console.log(timeStamp) 255 | const params = { 256 | txObj: { 257 | block: 173268750, // 最新区块:getInfo.head_block_num 258 | prefix: 2304335082, // 最新区块后缀:getBlock.ref_block_prefix 259 | expiration: timeStamp, // 交易过期时间戳:getBlock.timestamp 260 | chainId: "1064487b3cd1a897ce03ae5b6a865651747e2e152090f99c1d19d44e01aea5a4", 261 | from: "aaaashiiang", 262 | to: "lrn1zg5umbqp", 263 | quantity: "1.00000000", 264 | memo: "1222", 265 | max_payment: "5.00000000", 266 | net_frac: 1000000, 267 | cpu_frac: 1000000, 268 | }, 269 | network: 'main_net', 270 | privs: [{ key: privateKey }] 271 | } 272 | const tx_msg = await prepareAccount(params) 273 | console.log("tx_msg===", tx_msg) 274 | }) 275 | }) 276 | ``` 277 | 278 | 四. EOS 和 WAXP 钱包使用的 RPC 接口 279 | 280 | ## 1.获取链信息和状态 281 | 282 | - https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/get_info 283 | 284 | ## 2. 获取账户信息 285 | 286 | - https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/get_account 287 | 288 | ## 3. 获取账户余额 289 | 290 | - https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/get_currency_balance 291 | 292 | ## 4. 根据区块获取区块信息 293 | 294 | - https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/get_block_info 295 | 296 | ## 5. 根据 block 获取交易 297 | 298 | - https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/get_block 299 | 300 | ## 6. Json To Bin 编码接口 301 | 302 | - https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/abi_json_to_bin 303 | 304 | ## 7. 广播交易 305 | 306 | - https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/push_transaction 307 | - https://developers.eos.io/manuals/eos/v2.2/nodeos/plugins/chain_api_plugin/api-reference/index#operation/send_transaction 308 | 309 | # **五. 中心化钱包开发** 310 | 311 | ## **1. 离线地址生成** 312 | 313 | - 调度签名机生成密钥对,签名机吐出公钥 314 | - 使用公钥匙导出地址 315 | 316 | ## **2.充值逻辑** 317 | 318 | - 获得最新块高;更新到数据库 319 | - 从数据库中获取上次解析交易的块高做为起始块高,最新块高为截止块高,如果数据库中没有记录,说明需要从头开始扫,起始块高为 0; 320 | - 解析区块里面的交易,to 地址是系统内部的用户地址,说明用户充值,更新交易到数据库中,将交易的状态设置为待确认。 321 | - 所在块的交易过了确认位,将交易状态更新位充值成功并通知业务层。 322 | 323 | ## **3. 提现逻辑** 324 | 325 | - 获取离线签名需要的参数,给合适的手续费 326 | - 构建未签名的交易消息摘要,将消息摘要递给签名机签名 327 | - 构建完整的交易并进行序列化 328 | - 发送交易到区块链网络 329 | - 扫链获取到交易之后更新交易状态并上报业务层 330 | 331 | ## **4.归集逻辑** 332 | 333 | - 将用户地址上的资金转到归集地址,签名流程类似提现 334 | - 发送交易到区块链网络 335 | - 扫链获取到交易之后更新交易状态 336 | 337 | ## **5. 转冷逻辑** 338 | 339 | - 将热钱包地址上的资金转到冷钱包地址,签名流程类似提现 340 | - 发送交易到区块链网络 341 | - 扫链获取到交易之后更新交易状态 342 | 343 | ## **6.冷转热逻辑** 344 | 345 | - 手动操作转账到热钱包地址 346 | - 扫链获取到交易之后更新交易状态 347 | 348 | # **六 HD 钱包开发** 349 | 350 | ## **1.离线地生成和离线签名** 351 | 352 | 参考上面的代码 353 | 354 | ## **2.和链上交互的接口** 355 | 356 | - 获取账户余额 357 | - 获取 nonce 358 | - 根据地址获取交易记录 359 | - 获取预估手续费 360 | 361 | # **七. 总结** 362 | 363 | HD 钱包和交易所钱包不同之处有以下几点 364 | 365 | ## **1.密钥管理方式不同** 366 | 367 | - HD 钱包私钥在本地设备,私钥用户自己控制 368 | - 交易所钱包中心化服务器(CloadHSM, TEE 等),私钥项目方控制 369 | 370 | ## **2.资金存在方式不同** 371 | 372 | - HD 资金在用户钱包地址 373 | - 交易所钱包资金在交易所热钱包或者冷钱包里面, 用户提现的时候从交易所热钱包提取 374 | 375 | ## **3.业务逻辑不一致** 376 | 377 | - 中心化钱包:实时不断扫链更新交易数据和状态 378 | - HD 钱包:根据用户的操作通过请求接口实现业务逻辑 -------------------------------------------------------------------------------- /projectPractice/Solana centralized wallets.md: -------------------------------------------------------------------------------- 1 | # Solana 中心化钱包项目实战 2 | 3 | # 一. Solana 中心化钱包概述 4 | 5 | 我们都知道,中心化钱包主要用在交易所使用,中心化钱包功能主要包含:批量地址生成,充值,提现,归集,热转冷,冷转热等功能。 6 | 7 | ## **1. 离线地址生成** 8 | 9 | - 调度签名机生成密钥对,签名机吐出公钥, 签名算法是:Ed25519 10 | - 使用公钥导出地址 11 | 12 | ## **2.充值逻辑** 13 | 14 | - 获取链上最新块高,从数据库中获取上次解析交易的块高做为起始块高,如果链上的最新块和数据内部的块高一致,则等到下一个块出现了之后再开始去扫块;如果数据库中没有记录,则按照配置的块高开始扫块,起始块高配置默认为 0,如果用户没有配置,那就从 0 开始扫块。 15 | 16 | - 获取块里面的交易,判断是 native token 或者是代币充值 17 | 18 | 💡native token:主链币充值 program 等于 system 并且 type 为 transfer 19 | 20 | 💡Token:代币充值 program 等于 spl-token 并且 type 为 transfer 或者 transferChecked。 21 | 22 | 💡Nft: source 和 destination 里面的 mint 为合约地址 23 | 24 | 25 | 26 | ```js 27 | for(let i=0; i{ 31 | // tokenAddress 对应 owner 32 | obj[accountKeys[item.accountIndex].pubkey] = {owner: item.owner, mint: item.mint}; 33 | }); 34 | if(instruction.parsed && instruction.program){ 35 | const {parsed:{type, info}, program} = instruction; 36 | if(program==="system" && type==="transfer"){ 37 | fromAddresses.push(info.source); 38 | toAddresses.push(info.destination); 39 | amounts.push(info.lamports); 40 | }else if(program==="spl-token" && (type==="transfer" || type==="transferChecked") && obj[info.source].mint === contractAddr && obj[info.destination].mint === contractAddr){ 41 | fromAddresses.push(obj[info.source].owner || info.authority || info.multisigAuthority); 42 | let toAddr = obj[info.destination].owner; 43 | if(!toAddr){ 44 | const toAddrObj = instructions.find(ele => { 45 | return ele.program === "spl-associated-token-account" 46 | && ele.parsed.type === "create" 47 | && ele.parsed.info.account === info.destination 48 | }); 49 | toAddr = toAddrObj.parsed.info.wallet; 50 | } 51 | toAddresses.push(toAddr); 52 | amounts.push(info.amount || info.tokenAmount.amount); 53 | } 54 | } 55 | ``` 56 | 57 | 本次实战课程中只实现主币充值和代币充值,不实现 NFT 充值 58 | 59 | - 交易里面 to 地址是系统内部的用户地址,from 地址是外部地址,说明用户充值,更新交易到数据库中,并更新改地址的余额(注意,如果用户充值金额小于最小充值金额,不给入账),将交易的状态设置为待确认。 60 | - 交易所在块的过了确认位,将交易状态更新位充值成功并通知业务层,solana 的确认位 60 个。 61 | 62 | ## **3.归集** 63 | 64 | 功能:每隔一段时间将用户地址里面资金归集到归集地址 65 | 66 | 流程: 67 | 68 | - 使用用户的地址发起一笔交易,构建完交易之后生成待签名 Msg, 将签名 Msg 递交给签名机进行签名,签名机签名完成之后返回 signature, 将 signature 与交易一起构建成完整的交易,然后将交易发送到区块链网络,并且将这笔交易记录到 transaction 表,将用户地址里面资金锁定。 69 | 70 | - 扫链扫到这笔之后,通过 TxHash 查到这笔交易,然后匹配 from 和 to, 归集的交易 from 是系统的用户地址,to 地址交易所的归集地址,根据扫链交易状体更新数据库表的状态。 71 | 72 | 💡成功:更新 transaction 表的交易状态为成功并且将用户地址锁定资金清 0 73 | 74 | 💡失败:更新 transaction 表的交易状态为失败并且将用户地址锁定资金退回到用户地址里等待下一次归集 75 | 76 | ## **4.提现** 77 | 78 | 功能:将热钱包地址里面资金提现到用户的外部地址 79 | 80 | 流程: 81 | 82 | - 用户在业务层发起提现,业务层把该笔提交到钱包层的提现表,钱包层使用热钱包地址签名一笔交易并发送到区块链网络,并且更新提现表里面的交易位已发送,将交易也同时放到 transaction 表, 将热钱包地址里面提现的资金锁定。 83 | 84 | - 扫链扫到这笔之后,通过 TxHash 查到这笔交易,然后匹配 from 和 to, 提现的 from 是系统内部热钱包地址,to系统外部地址,根据扫链交易状体更新数据库表的状态。 85 | 86 | 💡成功:更新 transaction 和 withdraw 表的交易状态为成功并且将热钱包地址锁定资金减掉 87 | 88 | 💡失败:更新 transaction 表的交易状态为失败并且将热钱包地址锁定资金退回到热钱地址里重新发起提现过程 89 | 90 | # 二. 签名机服务 91 | 92 | ## 1.签名机器功能概述 93 | 94 | Solana 钱包实战中我们用 NodeJs 写了一个签名机服务, 主要实现密钥对和地址的生成,nonceAccount 的签名和交易签名。如果整个项目在生成环境中实施,需要把这个服务部署到 TEE 环境; 整个项目的功能如下: 95 | 96 | - 地址生成:接口请求地址,签名机会根据传入的参数需要生成多少个地址生成对应的地址,目前接口中会返回私钥,正常的生产环境中实施的话,需要把私钥留在 TEE 环境里面,并且进行加密存储。 97 | - Nonce 账户创建:做一笔 Nonce 账户创建的交易,目前接口需要传入私钥进行进行签名,实际生产中需要改造,私钥留在 TEE 环境里面,从 TEE 环境里面取加密的私钥解密之后对交易进行签名。 98 | - 交易签名:离线交易签名,目前接口需要传入私钥进行进行签名,实际生产中需要改造,私钥留在 TEE 环境里面,从 TEE 环境里面取加密的私钥解密之后对交易进行签名。 99 | 100 | ## 2.代码介绍 101 | 102 | - Solana 地址生成和签名代码封装: https://github.com/the-web3/solana-sign-service/blob/main/src/solana/solana.service.ts 103 | - 地址生成接口代码: https://github.com/the-web3/solana-sign-service/blob/42b6538ea663d5a0c8d0f732b6d2cd3b423f88e1/src/solana/solana.controller.ts#L8 104 | - Nonce 账户创建代码:https://github.com/the-web3/solana-sign-service/blob/42b6538ea663d5a0c8d0f732b6d2cd3b423f88e1/src/solana/solana.controller.ts#L48 105 | - 交易签名的代码:https://github.com/the-web3/solana-sign-service/blob/42b6538ea663d5a0c8d0f732b6d2cd3b423f88e1/src/solana/solana.controller.ts#L21 106 | 107 | 目前代码里面集成数据库,但是没有完全搞好,也欢迎小伙伴来提 PR 108 | 109 | # 三. Solana 交易所钱包代码实战 110 | 111 | - 项目代码:https://github.com/the-web3/sol-wallet 112 | 113 | ## 1.代码简介 114 | 115 | - 表结构定义:https://github.com/the-web3/sol-wallet/blob/main/migrations/00001_create_schema.sql 116 | - 充值逻辑:https://github.com/the-web3/sol-wallet/blob/main/wallet/deposit.go 117 | - 提现逻辑: https://github.com/the-web3/sol-wallet/blob/main/wallet/withdraw.go 118 | - 归集转冷逻辑:https://github.com/the-web3/sol-wallet/blob/main/wallet/collection_cold.go 119 | 120 | ## 2.必要依赖和代码构建 121 | 122 | **2.1.必要依赖** 123 | 124 | - 数据库:postgres 125 | - Golang: [Go 1.21+](https://golang.org/dl/) 126 | 127 | **2.2.构建代码** 128 | 129 | ```text 130 | make sol-wallet 131 | ``` 132 | 133 | **2.3.构建效果展示** 134 | 135 | ```text 136 | guoshijiang@192 sol-wallet % ./sol-wallet 137 | NAME: 138 | eth-wallet - A new cli application 139 | 140 | USAGE: 141 | sol-wallet [global options] command [command options] [arguments...] 142 | 143 | VERSION: 144 | 1.14.6-stable-8b3f4433 145 | 146 | DESCRIPTION: 147 | An exchange wallet scanner services with rpc and rest api server 148 | 149 | COMMANDS: 150 | api 151 | rpc 152 | generate-address 153 | wallet 154 | migrate 155 | version 156 | help, h Shows a list of commands or help for one command 157 | 158 | GLOBAL OPTIONS: 159 | --help, -h show help 160 | --version, -v print the version 161 | ``` 162 | 163 | - api: 启动 http 服务 164 | - rpc: 启动 rpc 服务 165 | - generate-address: 批量生成地址命令 166 | - migrate: 数据库 migrate 167 | - wallet: 启动充值,提现,归集与转冷等进程 168 | 169 | ## **3.运行代码** 170 | 171 | 3.1.**配置环境** 172 | 173 | ```text 174 | export SOL_WALLET_MIGRATIONS_DIR="./migrations" 175 | 176 | export SOL_WALLET_CHAIN_ID=1 177 | export SOL_WALLET_RPC_RUL="https://docs-demo.solana-mainnet.quiknode.pro" 178 | export SOL_WALLET_STARTING_HEIGHT=1950545 179 | export SOL_WALLET_CONFIRMATIONS=64 180 | export SOL_WALLET_DEPOSIT_INTERVAL=5s 181 | export SOL_WALLET_WITHDRAW_INTERVAL=5s 182 | export SOL_WALLET_COLLECT_INTERVAL=5s 183 | export SOL_WALLET_BLOCKS_STEP=1 184 | 185 | export SOL_WALLET_HTTP_PORT=8989 186 | export SOL_WALLET_HTTP_HOST="127.0.0.1" 187 | export SOL_WALLET_RPC_PORT=8980 188 | export SOL_WALLET_RPC_HOST="127.0.0.1" 189 | export SOL_WALLET_METRICS_PORT=8990 190 | export SOL_WALLET_METRICS_HOST="127.0.0.1" 191 | 192 | export SOL_WALLET_SLAVE_DB_ENABLE=false 193 | 194 | export SOL_WALLET_MASTER_DB_HOST="127.0.0.1" 195 | export SOL_WALLET_MASTER_DB_PORT=5432 196 | export SOL_WALLET_MASTER_DB_USER="guoshijiang" 197 | export SOL_WALLET_MASTER_DB_PASSWORD="" 198 | export SOL_WALLET_MASTER_DB_NAME="sol_wallet" 199 | 200 | export SOL_WALLET_SLAVE_DB_HOST="127.0.0.1" 201 | export SOL_WALLET_SLAVE_DB_PORT=5432 202 | export SOL_WALLET_SLAVE_DB_USER="guoshijiang" 203 | export SOL_WALLET_SLAVE_DB_PASSWORD="" 204 | export SOL_WALLET_SLAVE_DB_NAME="sol_wallet" 205 | 206 | export SOL_WALLET_API_CACHE_LIST_SIZE=0 207 | export SOL_WALLET_API_CACHE_LIST_DETAIL=0 208 | export SOL_WALLET_API_CACHE_LIST_EXPIRE_TIME=0 209 | export SOL_WALLET_API_CACHE_DETAIL_EXPIRE_TIME=0 210 | ``` 211 | 212 | **3.2.创建数据库** 213 | 214 | ```text 215 | create database sol_wallet; 216 | ``` 217 | 218 | **3.3.迁移数据库** 219 | 220 | ```text 221 | ./sol-wallet migrate 222 | ``` 223 | 224 | 执行结果 225 | 226 | ```text 227 | postgres=# \c sol_wallet 228 | 您现在已经连接到数据库 "sol_wallet",用户 "guoshijiang". 229 | sol_wallet=# \d 230 | 关联列表 231 | 架构模式 | 名称 | 类型 | 拥有者 232 | ----------+--------------+--------+------------- 233 | public | addresses | 数据表 | guoshijiang 234 | public | balances | 数据表 | guoshijiang 235 | public | blocks | 数据表 | guoshijiang 236 | public | deposits | 数据表 | guoshijiang 237 | public | tokens | 数据表 | guoshijiang 238 | public | transactions | 数据表 | guoshijiang 239 | public | withdraws | 数据表 | guoshijiang 240 | (7 行记录) 241 | ``` 242 | 243 | **3.4.批量地址生成** 244 | 245 | ```text 246 | ./sol-wallet generate-address 247 | ``` 248 | 249 | 查看效果 250 | 251 | ```text 252 | sol_wallet=# select count(*) from addresses; 253 | count 254 | ------- 255 | 100 256 | (1 行记录) 257 | ``` 258 | 259 | **3.5.启动钱包充值,提现,归集,转冷等命令** 260 | 261 | ```text 262 | ./sol-wallet wallet 263 | ``` 264 | 265 | 启动效果如下: 266 | 267 | ```text 268 | INFO [07-20|16:26:52.255] exec wallet sync 269 | INFO [07-20|16:26:52.255] loaded chain config config="{ChainID:17000 RpcUrl:https://eth-holesky.g.alchemy.com/v2/BvSZ5ZfdIwB-5SDXMz8PfGcbICYQqwrl StartingHeight:1964509 Confirmations:64 DepositInterval:5000 WithdrawInterval:500 CollectInterval:500 ColdInterval:500 BlocksStep:5}" 270 | 271 | 2024/07/20 16:26:52 /Users/guoshijiang/theweb3/sol-wallet/database/blocks.go:56 record not found 272 | [5.827ms] [rows:0] SELECT * FROM "blocks" ORDER BY number DESC LIMIT 1 273 | INFO [07-20|16:26:52.758] no sync indexed state starting from supplied solana height height=1,964,509 274 | INFO [07-20|16:26:53.471] start deposit...... 275 | INFO [07-20|16:26:53.472] start withdraw...... 276 | ``` 277 | 278 | ## 4.API 和 RPC 服务 279 | 280 | **4.1. RPC** 281 | 282 | 如果修改wallet.proto代码,需要执行[proto.sh](https://proto.sh/)将其编译为 golang 语言。 283 | 284 | ```text 285 | ./proto.sh 286 | ``` 287 | 288 | 启动 rpc 服务 289 | 290 | ```text 291 | ./sol-wallet rpc 292 | ``` 293 | 294 | 使用grpcui作为工具来请求rpc服务器 295 | 296 | ```text 297 | grpcui -plaintext 127.0.0.1:8089 298 | ``` 299 | 300 | 调用 rpc 提交提现信息 301 | 302 | - 请求示例 303 | 304 | ```text 305 | grpcurl -plaintext -d '{ 306 | "requestId": "11111", 307 | "chainId": "11", 308 | "fromAddress": "AqHAt2nQZNbHYjnSUyYkPdzSAybwcFYGjFb3XuQVm6YU", 309 | "toAddress": "Dx7VEjFSVS4F1LiUJRqUXjZbYryRWNqdEU8dTrna4YWS", 310 | "tokenAddress": "0x00", 311 | "amount": "10000000000000000000" 312 | }' 127.0.0.1:8980 services.thewebthree.wallet.WalletService.submitWithdrawInfo 313 | ``` 314 | 315 | - 结果 316 | 317 | ```text 318 | { 319 | "code": "2000", 320 | "msg": "submit withdraw success", 321 | "hash": "000000" 322 | } 323 | ``` 324 | 325 | **4.2.API** 326 | 327 | 启动 API 服务 328 | 329 | ```text 330 | ./sol-wallet api 331 | ``` 332 | 333 | 获取充值列表 334 | 335 | - 请求示例 336 | 337 | ```text 338 | curl --location --request GET 'http://127.0.0.1:8989/api/v1/deposits?address=AqHAt2nQZNbHYjnSUyYkPdzSAybwcFYGjFb3XuQVm6YU&page=1&pageSize=10' \ 339 | --form 'address="AqHAt2nQZNbHYjnSUyYkPdzSAybwcFYGjFb3XuQVm6YU"' \ 340 | --form 'page="1"' \ 341 | --form 'pageSize="10"' 342 | ``` 343 | 344 | - 返回值 345 | 346 | ```text 347 | { 348 | "Current": 1, 349 | "Size": 10, 350 | "Total": 1, 351 | "Records": [ 352 | { 353 | "guid": "803fd471-e74a-403e-a934-1b917e801518", 354 | "block_hash": "7UYTQjPtvoHoa9v5rd22LqbUMMi3Ne7SzscwFtzoXcfs", 355 | "BlockNumber": 1, 356 | "hash": "48mrFicPuHXjmdbzvNxjGPgasoP6eQbvpQPYFoMnZXfTYTgw7oJAhtgoVpdwu4ep3ZCpf6FxYYgVqYqBicBUFqJK", 357 | "from_address": "Dx7VEjFSVS4F1LiUJRqUXjZbYryRWNqdEU8dTrna4YWS", 358 | "to_address": "AqHAt2nQZNbHYjnSUyYkPdzSAybwcFYGjFb3XuQVm6YU", 359 | "token_address": "519p6BrurRFL3WBZPkGwCikHEoPhtf24H4kryLdspEXd", 360 | "Fee": 1, 361 | "Amount": 1000000000000000000, 362 | "status": 0, 363 | "tx_sign_hex": "", 364 | "Timestamp": 1721466415 365 | } 366 | ] 367 | } 368 | ``` 369 | 370 | 获取提现列表 371 | 372 | - 请求示例 373 | 374 | ```text 375 | curl --location --request GET 'http://127.0.0.1:8989/api/v1/withdrawals?address=Dx7VEjFSVS4F1LiUJRqUXjZbYryRWNqdEU8dTrna4YWS&page=1&pageSize=10' \ 376 | --form 'address="Dx7VEjFSVS4F1LiUJRqUXjZbYryRWNqdEU8dTrna4YWS"' \ 377 | --form 'page="1"' \ 378 | --form 'pageSize="10"' 379 | ``` 380 | 381 | - 结果 382 | 383 | ```text 384 | { 385 | "Current": 1, 386 | "Size": 10, 387 | "Total": 1, 388 | "Records": [ 389 | { 390 | "guid": "803fd471-e74a-403e-a934-1b917e801518", 391 | "block_hash": "7UYTQjPtvoHoa9v5rd22LqbUMMi3Ne7SzscwFtzoXcfs", 392 | "BlockNumber": 1, 393 | "hash": "48mrFicPuHXjmdbzvNxjGPgasoP6eQbvpQPYFoMnZXfTYTgw7oJAhtgoVpdwu4ep3ZCpf6FxYYgVqYqBicBUFqJK", 394 | "from_address": "Dx7VEjFSVS4F1LiUJRqUXjZbYryRWNqdEU8dTrna4YWS", 395 | "to_address": "AqHAt2nQZNbHYjnSUyYkPdzSAybwcFYGjFb3XuQVm6YU", 396 | "token_address": "519p6BrurRFL3WBZPkGwCikHEoPhtf24H4kryLdspEXd", 397 | "Fee": 1, 398 | "Amount": 1000000000000000000, 399 | "status": 0, 400 | "tx_sign_hex": "", 401 | "Timestamp": 1721466415 402 | } 403 | ] 404 | } 405 | ``` 406 | 407 | 提交提现交易 408 | 409 | - 请求示例 410 | 411 | ```text 412 | curl --location --request POST 'http://127.0.0.1:8989/api/v1/submit/withdrawals?fromAddress=Dx7VEjFSVS4F1LiUJRqUXjZbYryRWNqdEU8dTrna4YWS&toAddress=Dx7VEjFSVS4F1LiUJRqUXjZbYryRWNqdEU8dTrna4YWS&tokenAddress=Dx7VEjFSVS4F1LiUJRqUXjZbYryRWNqdEU8dTrna4YWS&amount=1000000000000000000' 413 | ``` 414 | 415 | - 结果 416 | 417 | ```text 418 | { 419 | "code": 2000, 420 | "msg": "submit transaction success" 421 | } 422 | ``` 423 | 424 | # 四.总结 425 | 426 | 本代码改造之后是可以做为生产代码来用的,可以根据自己的需求改造和业务层的对接,本项目中归集地址和热钱包是共用一个地址,这里可能需要根据具体的业务需求进行改造成多归集地址,多热钱包地址之类的处理。有的项目中可能还设置温钱包这样的业务,也需要根据业务调整一些细节的代码。 -------------------------------------------------------------------------------- /dot/README.md: -------------------------------------------------------------------------------- 1 | # DOT 钱包开发详细教程 2 | 3 | # 一. Polkadot 简介 4 | 5 | Polkadot 是一个由 Web3 基金会支持的开源多链区块链平台,旨在解决现有区块链技术的可扩展性、互操作性和升级困难等问题。其核心目标是创建一个完全去中心化的互联网,让独立区块链可以在共享的安全环境中无缝协作和通信。Polkadot 由以太坊联合创始人 Gavin Wood 创建。其目标是通过一个创新的多链架构,解决当前区块链生态系统的孤立性问题,促进不同区块链之间的互操作性和数据共享,从而实现真正的 Web3.0。 6 | 7 | ## 1.核心组件 8 | 9 | Polkadot 网络由多个关键组件构成,每个组件都在整个网络中扮演着重要角色: 10 | 11 | **1.1. Relay Chain(中继链)** 12 | 13 | 中继链是 Polkadot 的核心,负责网络的安全性、共识机制和跨链交易处理。中继链本身不处理智能合约,这样可以专注于提高性能和安全性。 14 | 15 | **1.2.Parachains(平行链)** 16 | 17 | 平行链是独立的区块链,可以拥有自己的共识机制和功能。平行链通过中继链与其他区块链实现互操作性,并共享中继链的安全保障。 18 | 19 | **1.3.Parathreads(平行线程)** 20 | 21 | 平行线程类似于平行链,但连接方式更加灵活。它们按需连接到中继链,适合那些不需要持续运行但又需要周期性连接的区块链应用。 22 | 23 | **1.4.Bridges(桥)** 24 | 25 | 桥是 Polkadot 网络与其他区块链(如以太坊、比特币等)之间的连接,使得不同区块链之间可以进行数据和资产的互通。 26 | 27 | ## **2. Polkadot 的 Nominated Proof-of-Stake (NPoS) 共识机制详解** 28 | 29 | Nominated Proof-of-Stake (NPoS) 是 Polkadot 网络采用的一种共识机制。它结合了传统的委托权益证明(DPoS)和权益证明(PoS)的优点,通过结合验证者和提名者的角色,采用优化的选举算法和严格的奖励与惩罚机制,成功地实现了高效、安全和去中心化的区块链网络。NPoS 机制不仅确保了 Polkadot 网络的运行稳定和安全,Nominated Proof-of-Stake (NPoS) 作为 Polkadot 的共识机制,,也为其他区块链项目提供了一个创新的共识机制范例。Polkadot 理念是旨在通过一个更去中心化且安全的方式来选择区块链网络的验证者,从而维护网络的安全性和高效性。 30 | 31 | **验证者(Validators):**验证者是负责验证交易、打包区块并维护网络安全的节点。他们通过质押一定数量的 DOT 代币来保证其诚实性。验证者需要运行完整节点,具备高性能和高可用性的服务器资源。 32 | 33 | **提名者(Nominators):**提名者是 DOT 代币持有者,他们通过质押 DOT 来支持一个或多个验证者。提名者与验证者的关系类似于选民和候选人,提名者的 DOT 代币被用作验证者的质押的一部分,从而共享验证者的区块奖励和惩罚。 34 | 35 | **2.1.机制详解** 36 | 37 | - **验证者选举:** 验证者的选举是通过 Phragmén 算法进行的,这是一种基于数学优化的公平选举算法。Phragmén 算法的目标是最大化网络的去中心化,并确保质押的分布均匀。 38 | - **提名者选择**:提名者选择他们信任的验证者,并将 DOT 代币质押给这些验证者。 39 | - **算法分配**:Phragmén 算法根据提名者的选择和质押的数量,公平地分配验证者的席位。 40 | 41 | **2.2. 验证者职责** 42 | 43 | 验证者在 Polkadot 网络中的主要职责包括: 44 | 45 | - **区块生成**:验证者轮流生成新区块,并将其添加到区块链上。 46 | - **交易验证**:验证交易的合法性和有效性,确保没有双花和其他恶意行为。 47 | - **共识协议**:参与网络的共识协议,确保区块链的一致性和安全性。 48 | 49 | **2.3. 奖励与惩罚** 50 | 51 | NPoS 机制通过奖励和惩罚来激励诚实的行为和惩罚不良行为。 52 | 53 | - **奖励**:验证者和提名者会根据他们质押的 DOT 代币比例获得区块奖励。奖励的分配取决于验证者的表现和提名者的贡献。 54 | - **惩罚**:如果验证者表现不诚实或未能履行职责,他们的质押 DOT 代币会被削减(slashing)。削减的程度取决于违规的严重性,提名者也会相应受到影响。 55 | 56 | **2.4. NPoS 的优势** 57 | 58 | - **去中心化**:NPoS 机制鼓励更多的验证者参与,从而提高网络的去中心化程度。 59 | - **安全性**:通过质押和削减机制,确保验证者的行为诚实并有效防止攻击。 60 | - **灵活性**:提名者可以根据表现和信誉随时更换支持的验证者,从而提高网络的灵活性和弹性。 61 | - **公平性**:Phragmén 算法确保质押分布的公平性,避免过度集中。 62 | 63 | ## **3. DOT 代币** 64 | 65 | DOT 是 Polkadot 的原生代币,具有以下几个主要用途: 66 | 67 | - **治理**:持有者可以参与 Polkadot 网络的治理,包括投票、提案和协议升级。 68 | - **质押**:用于网络的共识机制,通过质押获得奖励。 69 | - **绑定(Bonding)**:用于将新的平行链连接到中继链。 70 | 71 | ## **4. 治理机制** 72 | 73 | Polkadot 采用链上治理模式,允许代币持有者直接参与网络决策: 74 | 75 | - **提案**:任何持有 DOT 代币的用户可以提出网络改进提案。 76 | - **投票**:持有者可以对提案进行投票。 77 | - **技术委员会**:由技术专家组成,处理紧急决策。 78 | 79 | ## **5. 开发与生态系统** 80 | 81 | Polkadot 提供了 Substrate 框架,使开发者可以快速构建和部署区块链项目。Substrate 的模块化设计简化了区块链的开发过程,开发者可以根据需要定制自己的区块链。 82 | 83 | # 二. 离线地址生成 84 | 85 | ```js 86 | function createDotAddress (seedHex, addressIndex, chain, network) { 87 | const { key } = derivePath("m/44'/354'/0'/0'/" + addressIndex + "'", seedHex); 88 | const keyword = `dot_main_net`; 89 | const pubKey = getPublicKey(key, false).toString('hex'); 90 | console.log("publicKey==", pubKey) 91 | let publicKey = pubKey; 92 | if(typeof publicKey === "string" && publicKey.substring(0,2)!=="0x"){ 93 | publicKey = "0x" + publicKey; 94 | } 95 | const address = polkdot.encodeAddress(publicKey,0) 96 | return { 97 | "privateKey": key.toString('hex') + publicKey, 98 | "publicKey": publicKey, 99 | "address": address 100 | } 101 | } 102 | 103 | 104 | const mnemonic = ""; 105 | const seed = bip39.mnemonicToSeedSync(mnemonic) 106 | const account = createDotAddress(seed, 0, "dot", "main_net") 107 | console.log(account) 108 | ``` 109 | 110 | # 三. 离线签名 111 | 112 | - https://wiki.polkadot.network/docs/build-transaction-construction 113 | - 代币精度:10 114 | 115 | ```js 116 | const metadataRpc= "调用 state_getMetadata 返回的数据填充到这里" 117 | 118 | const {Keyring} = require("@polkadot/api"); 119 | const { methods, getRegistry, construct} = require("@substrate/txwrapper-polkadot"); 120 | 121 | 122 | function createKeypair(privateKey) { 123 | const privKey = "0x" + privateKey.substring(0, 64); 124 | const keyring = new Keyring({ type: "ed25519" }); 125 | return keyring.addFromUri(privKey); 126 | } 127 | 128 | const registry = getRegistry({ 129 | chainName: "Polkadot", 130 | specName: "polkadot", 131 | specVersion: "1002000", 132 | metadataRpc, 133 | userExtensions:{ SetEvmOrigin: { payload: {}, extrinsic: {} } }, 134 | }); 135 | 136 | 137 | const unsigned = methods.balances.transferKeepAlive( 138 | { 139 | dest: "15vrtLsCQFG3qRYUcaEeeEih4JwepocNJHkpsrqojqnZPc2y", 140 | value: 5000000000, 141 | }, 142 | { 143 | address: "16EEnWeJZkzBiLgZnz8R5NiE4Zo7S67PGvVyGWD6L538e2bB", // from address 144 | blockHash: "0x7c8dc11dff851d5d7c94ebd3072a45202100257d3f8a4fa39156fbe3d5e18642", 145 | blockNumber: 21037668, 146 | genesisHash: "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3", 147 | metadataRpc, // must import from client RPC call state_getMetadata 148 | nonce: 0, 149 | specVersion: 1002000, 150 | tip: 0, 151 | eraPeriod: 64, // number of blocks from checkpoint that transaction is valid 152 | transactionVersion: 1, 153 | }, 154 | { 155 | metadataRpc, 156 | registry, // Type registry 157 | } 158 | ); 159 | 160 | const extrinsicPayload = registry.createType( 161 | "ExtrinsicPayload", 162 | unsigned, 163 | { 164 | version: unsigned.version, 165 | } 166 | ); 167 | 168 | 169 | const keypair = createKeypair("privatekey"); 170 | const signature = extrinsicPayload.sign(keypair).signature; 171 | 172 | const signTx = construct.signedTx( 173 | unsigned, 174 | signature, 175 | { metadataRpc, registry, userExtensions: {SetEvmOrigin: {payload: {}, extrinsic: {}}},} 176 | ); 177 | 178 | console.log(signTx) 179 | ``` 180 | 181 | # 四. Polkadot 钱包相关的 RPC 接口 182 | 183 | ## 1.获取 metadatarpc 数据 184 | 185 | - 接口作用:获取签名需要用到的 metadata 186 | 187 | ```json 188 | { 189 | "jsonrpc": "2.0", 190 | "result": "0x......", 191 | "id": 1 192 | } 193 | ``` 194 | 195 | - 接口参数:无 196 | - 请求示范 197 | - 返回值 198 | 199 | ```bash 200 | curl --location 'https://rpc.polkadot.io' \ 201 | --header 'Content-Type: application/json' \ 202 | --data '{ 203 | "jsonrpc": "2.0", 204 | "method": "state_getMetadata", 205 | "params": [], 206 | "id": 1 207 | }' 208 | ``` 209 | 210 | - result 就是我们要的 metadata 211 | 212 | ## 2. 获取链的名字 213 | 214 | - 接口作用:获取签名需要用到的 chainName 215 | - 接口参数:无 216 | - 请求示范 217 | 218 | ```bash 219 | curl --location 'https://rpc.polkadot.io' \ 220 | --header 'Content-Type: application/json' \ 221 | --data '{ 222 | "method":"system_chain", 223 | "params":[],"id":1, 224 | "jsonrpc":"2.0" 225 | }' 226 | ``` 227 | 228 | - 返回值 229 | 230 | ```json 231 | { 232 | "jsonrpc": "2.0", 233 | "result": "Polkadot", 234 | "id": 1 235 | } 236 | ``` 237 | 238 | - result 返回的链名字 239 | 240 | ## 3. 获取 SpecName 和 SpecVersion 241 | 242 | - 接口作用:获取签名需要用到的 SpecName 和 SpecVersion 243 | - 接口参数:无 244 | - 请求示范 245 | 246 | ```bash 247 | curl --location 'https://rpc.polkadot.io' \ 248 | --header 'Content-Type: application/json' \ 249 | --data '{ 250 | "jsonrpc": "2.0", 251 | "method": "state_getRuntimeVersion", 252 | "params": [], 253 | "id": 1 254 | }' 255 | ``` 256 | 257 | - 返回值 258 | 259 | ```json 260 | { 261 | "jsonrpc": "2.0", 262 | "result": { 263 | "specName": "polkadot", 264 | "implName": "parity-polkadot", 265 | "authoringVersion": 0, 266 | "specVersion": 1002000, 267 | "implVersion": 0, 268 | "apis": [ 269 | [ 270 | "0xdf6acb689907609b", 271 | 4 272 | ] 273 | ], 274 | "transactionVersion": 25, 275 | "stateVersion": 1 276 | }, 277 | "id": 1 278 | } 279 | ``` 280 | 281 | - specName 运行时 spec 的名字 282 | - specVersion:运行时 spec 版本 283 | 284 | ## 4. 获取签名需要的 nonce 285 | 286 | - 接口作用:获取签名需要用到的 nonce 287 | - 接口参数:无 288 | - 请求示范 289 | 290 | ```bash 291 | curl --location 'https://rpc.polkadot.io' \ 292 | --header 'Content-Type: application/json' \ 293 | --data '{ 294 | "jsonrpc": "2.0", 295 | "method": "system_accountNextIndex", 296 | "params": ["16EEnWeJZkzBiLgZnz8R5NiE4Zo7S67PGvVyGWD6L538e2bB"], 297 | "id": 1 298 | }' 299 | ``` 300 | 301 | - 返回值 302 | 303 | ```json 304 | { 305 | "jsonrpc": "2.0", 306 | "result": 0, 307 | "id": 1 308 | } 309 | ``` 310 | 311 | - result 即为签名需要用的 noce 312 | 313 | ## 5.获取创世块的 Hash 314 | 315 | - 接口作用:获取签名需要使用的 genesisHash 316 | - 接口参数:0 为创世块 317 | - 请求示范 318 | 319 | ```bash 320 | curl --location 'https://rpc.polkadot.io' \ 321 | --header 'Content-Type: application/json' \ 322 | --data '{ 323 | "jsonrpc": "2.0", 324 | "method": "chain_getBlockHash", 325 | "params": [0], 326 | "id": 1 327 | }' 328 | ``` 329 | 330 | - 返回值 331 | 332 | ```json 333 | { 334 | "jsonrpc": "2.0", 335 | "result": "0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3", 336 | "id": 1 337 | } 338 | ``` 339 | 340 | - 返回的 result 就是 genesisHash 341 | 342 | ## 6. 获取区块相关信息 343 | 344 | - 接口作用: 不传入参数:返回最新区块 传入 block number 或者 block Hash, 返回对应区块数据 345 | - 接口参数:参考接口作用 346 | - 请求示范 347 | 348 | ```bash 349 | curl --location 'https://rpc.polkadot.io' \ 350 | --header 'Content-Type: application/json' \ 351 | --data '{ 352 | "jsonrpc": "2.0", 353 | "method": "chain_getBlock", 354 | "params": ["0x7c8dc11dff851d5d7c94ebd3072a45202100257d3f8a4fa39156fbe3d5e18642"], 355 | "id": 1 356 | }' 357 | ``` 358 | 359 | - 返回值 360 | 361 | ```json 362 | { 363 | "jsonrpc": "2.0", 364 | "result": { 365 | "block": { 366 | "header": { 367 | "parentHash": "0xa3d4aa9e1c54b967d638efd7f26c2df67fb5ced4d54294c8f167fb6fadefc8b8", 368 | "number": "0x1410264", 369 | "stateRoot": "0x23bf1b985d0df7c8891eb4ed44f7fa05bb2242b86888b43567f187cf0b452b35", 370 | "extrinsicsRoot": "0x069caf68e4ce5e2ad0219de0d55c44fce1c9c0ca5b2c86a0a5d036348fe44804", 371 | "digest": { 372 | "logs": [ 373 | "0x0642414245b50101b400000061570f11000000001c6cfeea11570a6aaac35569f0ad48c81a7d648a4b17a2e16e768ddf8ea5ea5e52268f317726ce51e4dfa05aba1b53927dccefb1e507ca7ff6ccad9fd7741e0d3a1410a37070a3955f888be2540c0c8f0e54f8c12cf7432e10247506b9fd0503", 374 | "0x044245454684035446b837cd2f2eb527bf0190c9ffb62421967a473abcfb55ebe5065dbad95a2f", 375 | "0x0542414245010136b9d0fd76acf3eaac298b8ea2f67a996259f55cfb331cc1dbeac6b22f1f9c76442eb5c87dc40f213b90a1c58cf2dcc554f9edb41c63efa912da4db93025ad86" 376 | ] 377 | } 378 | }, 379 | "extrinsics": [ 380 | "0x280403000b71f18fd78f01", 381 | "0x0000000000000.........." 382 | ] 383 | }, 384 | "justifications": null 385 | }, 386 | "id": 1 387 | } 388 | ``` 389 | 390 | - 将里面的交易解码就得到交易的 json 格式,可以是 polkdot js 解码 391 | 392 | ## 7. 发送交易到区块链网络 393 | 394 | - 接口作用:发送交易 395 | - 接口参数:参考接口作用 396 | - 请求示范 397 | 398 | ```bash 399 | curl --location 'https://rpc.polkadot.io' \ 400 | --header 'Content-Type: application/json' \ 401 | --data '{ 402 | "jsonrpc": "2.0", 403 | "method": "author_submitExtrinsic", 404 | "params": ["0x41028400e745c2c869615d270bf5eaf86e3efe4c857594628e67ddf8010e32aa8cb9b8a6000c665c8a601c5f1b04821741ad4b59098e0807c430121fdadb9ab50f506ec24e17c1f8602b727e5bb7675c86e6b08cc6db242ba3a2f738925f2ce052724dba0945020000050300da04de6cd781c98acf0693dfb97c11011938ad22fcc476ed0089ac5aec3fe2430700f2052a01"], 405 | "id": 1 406 | }' 407 | ``` 408 | 409 | - 返回值 410 | 411 | ```json 412 | { 413 | "jsonrpc": "2.0", 414 | "error": { 415 | "code": 1010, 416 | "message": "Invalid Transaction", 417 | "data": "Transaction has a bad signature" 418 | }, 419 | "id": 1 420 | } 421 | ``` 422 | 423 | - 成功返回交易 Hash, 失败返回失败信息 424 | 425 | # **五. 中心化钱包开发** 426 | 427 | ## **1. 离线地址生成** 428 | 429 | - 调度签名机生成密钥对,签名机吐出公钥 430 | - 使用公钥匙导出地址 431 | 432 | ## **2.充值逻辑** 433 | 434 | - 获得最新块高;更新到数据库 435 | - 从数据库中获取上次解析交易的块高做为起始块高,最新块高为截止块高,如果数据库中没有记录,说明需要从头开始扫,起始块高为 0; 436 | - 解析区块里面的交易,to 地址是系统内部的用户地址,说明用户充值,更新交易到数据库中,将交易的状态设置为待确认。 437 | - 所在块的交易过了确认位,将交易状态更新位充值成功并通知业务层。 438 | 439 | ## **3. 提现逻辑** 440 | 441 | - 获取离线签名需要的参数,给合适的手续费 442 | - 构建未签名的交易消息摘要,将消息摘要递给签名机签名 443 | - 构建完整的交易并进行序列化 444 | - 发送交易到区块链网络 445 | - 扫链获取到交易之后更新交易状态并上报业务层 446 | 447 | ## **4.归集逻辑** 448 | 449 | - 将用户地址上的资金转到归集地址,签名流程类似提现 450 | - 发送交易到区块链网络 451 | - 扫链获取到交易之后更新交易状态 452 | 453 | ## **5. 转冷逻辑** 454 | 455 | - 将热钱包地址上的资金转到冷钱包地址,签名流程类似提现 456 | - 发送交易到区块链网络 457 | - 扫链获取到交易之后更新交易状态 458 | 459 | ## **6.冷转热逻辑** 460 | 461 | - 手动操作转账到热钱包地址 462 | - 扫链获取到交易之后更新交易状态 463 | 464 | # **六 HD 钱包开发** 465 | 466 | ## **1.离线地生成和离线签名** 467 | 468 | 参考上面的代码 469 | 470 | ## **2.和链上交互的接口** 471 | 472 | - 获取账户余额 473 | - 获取 nonce 474 | - 根据地址获取交易记录 475 | - 获取预估手续费 476 | 477 | # **七. 总结** 478 | 479 | HD 钱包和交易所钱包不同之处有以下几点 480 | 481 | ## **1.密钥管理方式不同** 482 | 483 | - HD 钱包私钥在本地设备,私钥用户自己控制 484 | - 交易所钱包中心化服务器(CloadHSM, TEE 等),私钥项目方控制 485 | 486 | ## **2.资金存在方式不同** 487 | 488 | - HD 资金在用户钱包地址 489 | - 交易所钱包资金在交易所热钱包或者冷钱包里面, 用户提现的时候从交易所热钱包提取 490 | 491 | ## **3.业务逻辑不一致** 492 | 493 | - 中心化钱包:实时不断扫链更新交易数据和状态 494 | - HD 钱包:根据用户的操作通过请求接口实现业务逻辑 495 | 496 | # **八.附录** 497 | 498 | - PolkDot 官网:https://polkadot.network/ 499 | - Github: https://github.com/paritytech/polkadot-sdk 500 | - Dot docs: https://polkadot.network/development/docs/ 501 | - RPC 接口文档:https://www.quicknode.com/docs/polkadot/chain_getBlockHash 502 | - 浏览器: https://polkadot.subscan.io/ 503 | - PolkadotJs: https://github.com/polkadot-js -------------------------------------------------------------------------------- /Bitcoin/BitcoinWalletDevelopmentProcess.md: -------------------------------------------------------------------------------- 1 | # Bitcoin 钱包开发流程 2 | 3 | # 1.地址生成 4 | 5 | ```javascript 6 | import * as bitcoin from 'bitcoinjs-lib'; 7 | import * as ecc from 'tiny-secp256k1'; 8 | const { BIP32Factory } = require('bip32'); 9 | const bip32 = BIP32Factory(ecc); 10 | 11 | export function createAddress (params: any): any { 12 | const { seedHex, receiveOrChange, addressIndex, network, method } = params; 13 | const root = bip32.fromSeed(Buffer.from(seedHex, 'hex')); 14 | let path = "m/44'/0'/0'/0/" + addressIndex + ''; 15 | if (receiveOrChange === '1') { 16 | path = "m/44'/0'/0'/1/" + addressIndex + ''; 17 | } 18 | const child = root.derivePath(path); 19 | let address: string; 20 | switch (method) { 21 | case 'p2pkh': 22 | // eslint-disable-next-line no-case-declarations 23 | const p2pkhAddress = bitcoin.payments.p2pkh({ 24 | pubkey: child.publicKey, 25 | network: bitcoin.networks[network] 26 | }); 27 | address = p2pkhAddress.address; 28 | break; 29 | case 'p2wpkh': 30 | // eslint-disable-next-line no-case-declarations 31 | const p2wpkhAddress = bitcoin.payments.p2wpkh({ 32 | pubkey: child.publicKey, 33 | network: bitcoin.networks[network] 34 | }); 35 | address = p2wpkhAddress.address; 36 | break; 37 | case 'p2sh': 38 | // eslint-disable-next-line no-case-declarations 39 | const p2shAddress = bitcoin.payments.p2sh({ 40 | redeem: bitcoin.payments.p2wpkh({ 41 | pubkey: child.publicKey, 42 | network: bitcoin.networks[network] 43 | }) 44 | }); 45 | address = p2shAddress.address; 46 | break; 47 | default: 48 | console.log('This way can not support'); 49 | } 50 | 51 | return { 52 | privateKey: Buffer.from(child.privateKey).toString('hex'), 53 | publicKey: Buffer.from(child.publicKey).toString('hex'), 54 | address 55 | }; 56 | } 57 | 58 | export function createMultiSignAddress (params: any): string { 59 | const { pubkeys, network, method, threshold } = params; 60 | switch (method) { 61 | case 'p2pkh': 62 | return bitcoin.payments.p2sh({ 63 | redeem: bitcoin.payments.p2ms({ 64 | m: threshold, 65 | network: bitcoin.networks[network], 66 | pubkeys 67 | }) 68 | }).address; 69 | case 'p2wpkh': 70 | return bitcoin.payments.p2wsh({ 71 | redeem: bitcoin.payments.p2ms({ 72 | m: threshold, 73 | network: bitcoin.networks[network], 74 | pubkeys 75 | }) 76 | }).address; 77 | case 'p2sh': 78 | return bitcoin.payments.p2sh({ 79 | redeem: bitcoin.payments.p2wsh({ 80 | redeem: bitcoin.payments.p2ms({ 81 | m: threshold, 82 | network: bitcoin.networks[network], 83 | pubkeys 84 | }) 85 | }) 86 | }).address; 87 | default: 88 | console.log('This way can not support'); 89 | return '0x00'; 90 | } 91 | } 92 | 93 | export function createSchnorrAddress (params: any): any { 94 | bitcoin.initEccLib(ecc); 95 | 96 | const { seedHex, receiveOrChange, addressIndex } = params; 97 | const root = bip32.fromSeed(Buffer.from(seedHex, 'hex')); 98 | let path = "m/44'/0'/0'/0/" + addressIndex + ''; 99 | if (receiveOrChange === '1') { 100 | path = "m/44'/0'/0'/1/" + addressIndex + ''; 101 | } 102 | const childKey = root.derivePath(path); 103 | const privateKey = childKey.privateKey; 104 | if (!privateKey) throw new Error('No private key found'); 105 | 106 | const publicKey = childKey.publicKey; 107 | 108 | const tweak = bitcoin.crypto.taggedHash('TapTweak', publicKey.slice(1, 33)); 109 | const tweakedPublicKey = Buffer.from(publicKey); 110 | for (let i = 0; i < 32; ++i) { 111 | tweakedPublicKey[1 + i] ^= tweak[i]; 112 | } 113 | 114 | const { address } = bitcoin.payments.p2tr({ 115 | internalPubkey: tweakedPublicKey.slice(1, 33) 116 | }); 117 | 118 | return { 119 | privateKey: Buffer.from(childKey.privateKey).toString('hex'), 120 | publicKey: Buffer.from(childKey.publicKey).toString('hex'), 121 | address 122 | }; 123 | } 124 | ``` 125 | 126 | # 2. 离线签名 127 | 128 | ```javascript 129 | const ecc = require('tiny-secp256k1'); 130 | const { BIP32Factory } = require('bip32'); 131 | BIP32Factory(ecc); 132 | const bitcoin = require('bitcoinjs-lib'); 133 | const bitcore = require('bitcore-lib'); 134 | 135 | /** 136 | * @returns 137 | * @param params 138 | */ 139 | export function buildAndSignTx (params: { privateKey: string; signObj: any; network: string; }): string { 140 | const { privateKey, signObj, network } = params; 141 | const net = bitcore.Networks[network]; 142 | const inputs = signObj.inputs.map(input => { 143 | return { 144 | address: input.address, 145 | txId: input.txid, 146 | outputIndex: input.vout, 147 | // eslint-disable-next-line new-cap 148 | script: new bitcore.Script.fromAddress(input.address).toHex(), 149 | satoshis: input.amount 150 | }; 151 | }); 152 | const outputs = signObj.outputs.map(output => { 153 | return { 154 | address: output.address, 155 | satoshis: output.amount 156 | }; 157 | }); 158 | const transaction = new bitcore.Transaction(net).from(inputs).to(outputs); 159 | transaction.version = 2; 160 | transaction.sign(privateKey); 161 | return transaction.toString(); 162 | } 163 | 164 | export function buildUnsignTxAndSign (params) { 165 | const { keyPair, signObj, network } = params; 166 | const psbt = new bitcoin.Psbt({ network }); 167 | const inputs = signObj.inputs.map(input => { 168 | return { 169 | address: input.address, 170 | txId: input.txid, 171 | outputIndex: input.vout, 172 | // eslint-disable-next-line new-cap 173 | script: new bitcore.Script.fromAddress(input.address).toHex(), 174 | satoshis: input.amount 175 | }; 176 | }); 177 | psbt.addInput(inputs); 178 | 179 | const outputs = signObj.outputs.map(output => { 180 | return { 181 | address: output.address, 182 | satoshis: output.amount 183 | }; 184 | }); 185 | psbt.addOutput(outputs); 186 | psbt.toBase64(); 187 | 188 | psbt.signInput(0, keyPair); 189 | psbt.finalizeAllInputs(); 190 | 191 | const signedTransaction = psbt.extractTransaction().toHex(); 192 | console.log('signedTransaction==', signedTransaction); 193 | } 194 | ``` 195 | 196 | # 3. 扫链接口 197 | 198 | ## 3.1.原生 Bitcoin 接口 199 | 200 | **获取活跃的最新区块** 201 | 202 | 请求参数 203 | 204 | ```bash 205 | curl --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getchaintips", "params": []}' -H 'content-type: text/plain;' https://thrilling-spring-bush.btc.quiknode.pro/c0d9254cfb049224abd0ece400635e62b791a388/ 206 | ``` 207 | 208 | 返回值 209 | 210 | ```json 211 | { 212 | "result":[ 213 | { 214 | "height":845198, 215 | "hash":"00000000000000000000301d584ec5f1c16e89487c05baf035f01875cb763d75", 216 | "branchlen":0, 217 | "status":"active" 218 | }, 219 | { 220 | "height":841424, 221 | "hash":"000000000000000000010998fc2714f8ae10ffb73f1986eecc58f5afc457ee07", 222 | "branchlen":1, 223 | "status":"valid-headers" 224 | }, 225 | { 226 | "height":838792, 227 | "hash":"00000000000000000002af7214c8796e102b0e9074a5d469266d7afe5af2f087", 228 | "branchlen":1, 229 | "status":"headers-only" 230 | }, 231 | { 232 | "height":816358, 233 | "hash":"00000000000000000001d5f92e2dbbfcbc1e859873117e7983dd574857da5e14", 234 | "branchlen":1, 235 | "status":"valid-headers" 236 | }, 237 | { 238 | "height":815202, 239 | "hash":"0000000000000000000093917031004a140b6db5c6adec217f814db98d7f0bde", 240 | "branchlen":1, 241 | "status":"valid-fork" 242 | }, 243 | ], 244 | "error":null, 245 | "id":"curltest" 246 | } 247 | ``` 248 | 249 | - “invalid” 该分支至少包含一个无效块 250 | - “headers-only” 并非该分支的所有块都可用,但 headers 有效 251 | - “valid-headers”所有块都可用于此分支,但它们从未经过完全验证 252 | - “valid-fork” 该分支不是活动链的一部分,但经过充分验证 253 | - “active”这是活跃主链的提示,这当然有效 254 | 255 | **获取区块信息** 256 | 257 | 请求示范 258 | 259 | ```bash 260 | curl --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockchaininfo", "params": []}' -H 'content-type: text/plain;' https://thrilling-spring-bush.btc.quiknode.pro/c0d9254cfb049224abd0ece400635e62b791a388/ 261 | ``` 262 | 263 | 返回值 264 | 265 | ```json 266 | { 267 | "result":{ 268 | "chain":"main", 269 | "blocks":845200, 270 | "headers":845200, 271 | "bestblockhash":"000000000000000000027a970865a12b12e4da473011e2033eeca871c957a747", 272 | "difficulty":84381461788831.34, 273 | "time":1716706327, 274 | "mediantime":1716703878, 275 | "verificationprogress":0.999998974207445, 276 | "initialblockdownload":false, 277 | "chainwork":"00000000000000000000000000000000000000007b695dedb46255cb840f5cb6", 278 | "size_on_disk":652535688171, 279 | "pruned":false, 280 | "warnings":"" 281 | }, 282 | "error":null, 283 | "id":"curltest" 284 | } 285 | ``` 286 | 287 | **列出未花费的输入输出** 288 | 289 | 请求示范 290 | 291 | ```bash 292 | curl --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "listunspent", "params": [845000, 845200, [] , true, { "minimumAmount": 0.005 } ]}' -H 'content-type: text/plain;' https://thrilling-spring-bush.btc.quiknode.pro/c0d9254cfb049224abd0ece400635e62b791a388/ 293 | ``` 294 | 295 | 返回值 296 | 297 | ```json 298 | [ 299 | { 300 | "txid" : "", 301 | "vout" : 1, 302 | "address" : "str", 303 | "label" : "str", 304 | "scriptPubKey" : "str", 305 | "amount" : 10000, 306 | "confirmations" : 12, 307 | "redeemScript" : "hex", 308 | "witnessScript" : "str", 309 | "spendable" : false, 310 | "solvable" : false, 311 | "reused" : false, 312 | "desc" : "str", 313 | "safe" : true 314 | },{ 315 | "txid" : "", 316 | "vout" : 1, 317 | "address" : "str", 318 | "label" : "str", 319 | "scriptPubKey" : "str", 320 | "amount" : 10000, 321 | "confirmations" : 12, 322 | "redeemScript" : "hex", 323 | "witnessScript" : "str", 324 | "spendable" : false, 325 | "solvable" : false, 326 | "reused" : false, 327 | "desc" : "str", 328 | "safe" : true 329 | }, 330 | ] 331 | ``` 332 | 333 | **发送交易到区块链网络** 334 | 335 | 请求参数 336 | 337 | ```bash 338 | curl --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "sendrawtransaction", "params": ["signedhex"]}' -H 'content-type: text/plain;' https://thrilling-spring-bush.btc.quiknode.pro/c0d9254cfb049224abd0ece400635e62b791a388/ 339 | ``` 340 | 341 | 返回值 342 | 343 | ``` 344 | 成功返回交易 Hash 345 | ``` 346 | 347 | ## 3.2.Rosetta Api 348 | 349 | Bitcoin Rosetta API 是由 Coinbase 提出的 Rosetta 标准的一部分,旨在为区块链和钱包提供一个统一的接口标准。这个标准化的接口使得与各种区块链的交互更加容易和一致,无论是对交易数据的读取还是写入。目前已经支持很多链,包含比特币,以太坊等主流链,也包含像 IoTex 和 Oasis 这样的非主流链。 350 | 351 | **3.2.1.Rosetta API 概述** 352 | 353 | Rosetta API 分为两部分: 354 | 355 | - Data API:用于读取区块链数据。 356 | - Construction API:用于构建和提交交易。 357 | 358 | **3.2.2. Data API** 359 | 360 | Data API 提供了一组端点,用于检索区块链数据,如区块、交易、余额等。主要端点包括: 361 | 362 | - /network/list:返回支持的网络列表。 363 | - /network/status:返回当前网络的状态信息。 364 | - /network/options:返回支持的网络选项和版本信息。 365 | - /block:返回指定区块的数据。 366 | - /block/transaction:返回指定交易的数据。 367 | - /account/balance:返回指定账户的余额。 368 | - /mempool:返回当前未确认的交易池。 369 | - /mempool/transaction:返回指定未确认交易的数据。 370 | 371 | **3.2.3. Construction API** 372 | 373 | Construction API 提供了一组端点,用于创建、签名和提交交易。主要端点包括: 374 | 375 | - /construction/preprocess:分析交易需求并返回交易所需的元数据。 376 | - /construction/metadata:返回构建交易所需的元数据。 377 | - /construction/payloads:生成待签名的交易有效载荷。 378 | - /construction/parse:解析交易并返回其操作。 379 | - /construction/combine:将签名与待签名交易合并。 380 | - /construction/hash:返回交易的唯一标识符(哈希)。 381 | - /construction/submit:提交签名后的交易。 382 | 383 | **3.2.4.开发 BTC 钱包使用到的 Rosetta Api** 384 | 385 | 为了具体实现 Rosetta API,开发者需要遵循 Rosetta 标准并根据比特币区块链的特性进行适配。以下是一些具体实现细节 386 | 387 | 数据结构: 388 | 389 | - 区块:包含区块哈希、前一个区块哈希、区块高度、时间戳、交易列表等。 390 | - 交易:包含交易哈希、输入输出列表、金额、地址等。 391 | - 账户:包含账户地址和余额信息。 392 | 393 | **用到的接口** 394 | 395 | - /network/list:返回比特币主网和测试网信息。 396 | - /network/status:返回当前最新区块、已同步区块高度、区块链处理器的状态等。 397 | - /block 和 /block/transaction:返回区块和交易的详细信息,包括交易的输入输出、金额、地址等。 398 | - /account/balance:通过查询比特币节点,返回指定地址的余额。 399 | 400 | **发送交易到区块链网络** 401 | 402 | - /construction/submit:通过比特币节点提交签名后的交易。 403 | 404 | ## 3.3. 文档资料 405 | 406 | - 比特币开发文档:https://developer.bitcoin.org/reference/rpc/ 407 | 408 | - Rosetta 开发文档:https://docs.cdp.coinbase.com/rosetta/reference/networklist/ 409 | 410 | - Rosetta 开发文档:https://github.com/coinbase/mesh-ecosystem/blob/master/implementations.md 411 | 412 | - 浏览器:https://btc.com/zh-CN 413 | 414 | 415 | # 4.中心化钱包开发 416 | 417 | ## 4.1.离线地址生成 418 | 419 | - 调度签名机生成密钥对,签名机吐出公钥 420 | - 使用公钥匙导出地址 421 | 422 | ## 4.2.充值逻辑 423 | 424 | - 获得最新块高;更新到数据库 425 | - 从数据库中获取上次解析交易的块高做为起始块高,最新块高为截止块高,如果数据库中没有记录,说明需要从头开始扫,起始块高为 0; 426 | - 解析区块里面的交易,to 地址是系统内部的用户地址,说明用户充值,更新交易到数据库中,将交易的状态设置为待确认。 427 | - 所在块的交易过了确认位,将交易状态更新位充值成功并通知业务层。 428 | - 解析到的充值交易需要在钱包的数据库里面维护 UTXO 429 | 430 | ## **4.2.**提现逻辑 431 | 432 | - 获取离线签名需要的参数,给合适的手续费 433 | - 构建未签名的交易消息摘要,将消息摘要递给签名机签名 434 | - 构建完整的交易并进行序列化 435 | - 发送交易到区块链网络 436 | - 扫链获取到交易之后更新交易状态并上报业务层 437 | 438 | ## **4.3.**归集逻辑 439 | 440 | - 将用户地址上的资金转到归集地址,签名流程类似提现 441 | - 发送交易到区块链网络 442 | - 扫链获取到交易之后更新交易状态 443 | 444 | ## **4.4.**转冷逻辑 445 | 446 | - 将热钱包地址上的资金转到冷钱包地址,签名流程类似提现 447 | - 发送交易到区块链网络 448 | - 扫链获取到交易之后更新交易状态 449 | 450 | ## **4.5.**冷转热逻辑 451 | 452 | - 手动操作转账到热钱包地址 453 | - 扫链获取到交易之后更新交易状态 454 | 455 | **注意:交费的学员需要完整的项目实战代码可寻求 The Web3 社区索取** 456 | 457 | # **5.HD 钱包开发** 458 | 459 | ## 5.1. 离线地生成和离线签名 460 | 461 | 参考上面的代码 462 | 463 | ## 5.2. 和链上交互的接口 464 | 465 | - 获取账户余额 466 | 467 | - 根据地址获取交易记录 468 | 469 | - 根据交易 Hash 获取交易详情 470 | 471 | - 获取未花费的输入输出 472 | 473 | - 获取交易手续费 474 | 475 | - 以上接口请参考代码库:https://github.com/the-web3/wallet-chain-node/tree/develop/wallet/bitcoin 476 | 477 | 478 | # **6.总结** 479 | 480 | HD 钱包和交易所钱包不同之处有以下几点 481 | 482 | ## **6.1.密钥管理方式不同** 483 | 484 | - HD 钱包私钥在本地设备,私钥用户自己控制 485 | - 交易所钱包中心化服务器(CloadHSM, TEE 等),私钥项目方控制 486 | 487 | ## 6.2.资金存在方式不同 488 | 489 | - HD 资金在用户钱包地址 490 | - 交易所钱包资金在交易所热钱包或者冷钱包里面 491 | 492 | ## 6.3.业务逻辑不一致 493 | 494 | - 中心化钱包:实时不断扫链更新交易数据和状态 495 | - HD 钱包:根据用户的操作通过请求接口实现业务逻辑 496 | -------------------------------------------------------------------------------- /projectPractice/Realize the staking function.md: -------------------------------------------------------------------------------- 1 | # 如何在钱包里面实现 Staking 功能 2 | 3 | # 一. Staking 功能概述 4 | 5 | Staking 是一种数字货币领域中常见的机制,允许持有者通过将其加密货币锁定在特定的区块链网络中,从而获得奖励。这种机制通常用于支持和维护区块链网络的运行和安全。 6 | 7 | ## 1.基本概念 8 | 9 | - **定义**:Staking 是指持有者将其加密货币存入一个指定的钱包或智能合约中,并锁定一段时间,以支持区块链网络的操作。 10 | - **目的**:通过 staking,持有者可以帮助区块链网络进行交易验证和达成共识,从而确保网络的安全和稳定。 11 | 12 | ## 2.**主要特点** 13 | 14 | - **收益**:参与 staking 的持有者可以获得奖励,通常以同种加密货币或其他形式的收益发放。这些奖励可能是固定的,也可能是根据网络的具体规则动态变化的。 15 | - **锁定期**:在 staking 期间,加密货币将被锁定一段时间,期间持有者无法转移或使用这些加密货币。锁定期长短因区块链网络的不同而有所差异。 16 | - **最低持有量**:有些区块链网络对参与 staking 的持有量有最低要求,持有者必须至少持有一定数量的加密货币才能参与 staking。 17 | 18 | ## 3.质押类别 19 | 20 | - **权益证明(Proof of Stake, PoS)**:这是最基本的 staking 机制,节点通过持有和锁定加密货币来参与网络共识 💡ETH2.0 21 | 22 | 💡Cosmos(ATOM) 23 | 24 | 💡Solanan(SOL) 25 | 26 | - **委托权益证明(Delegated Proof of Stake, DPoS)**:持有者将其加密货币委托给可信任的节点(代表),由代表进行验证和共识操作。 27 | 28 | 💡Tezos(XTZ) 29 | 30 | 💡EOSIO(EOS) 31 | 32 | 💡TRON(TRX) 33 | 34 | - **混合机制**:一些区块链网络使用混合机制,例如混合 PoW/PoS(工作量证明和权益证明结合),以平衡网络的安全性和去中心化特性。 35 | 36 | - **智能合约**:通过智能合约实现的质押模型 37 | 38 | 💡EigenLayr 39 | 40 | 💡DappLink 多重质押协议 LinkLayer 41 | 42 | 💡Compond 43 | 44 | ## 4.**工作原理** 45 | 46 | - **验证节点**:在使用权益证明(Proof of Stake,PoS),其变种的区块链和合约实现的质押协议网络中,验证节点(或称为 staker)通过锁定其加密货币来获得验证交易和创建新区块的权利。 47 | - **选取机制**:验证节点的选取通常是随机的,但选取概率与节点所持有的加密货币数量成正比,一般持有越多的加密货币,成为验证节点的概率越大, ETH2.0 是比较特殊的,持有 32 个 ETH 成为验证人,大于 32 个 ETH 部分不会获得更多的奖励。 48 | - **奖励机制**:验证节点成功验证交易并创建新区块后,会获得相应的奖励。这些奖励的来源可以是区块链网络生成的新币或交易费用。 49 | 50 | ## 5.**优缺点分析** 51 | 52 | **优点** 53 | 54 | - **收益来源**:持有者可以通过 staking 获得被动收入。 55 | - **网络安全**:通过 staking,更多的持有者参与网络共识,提高了区块链网络的安全性和去中心化程度。 56 | - **资源效率**:相比于工作量证明(Proof of Work, PoW),staking 更加节能和环保,不需要大量的计算资源。 57 | 58 | **缺点** 59 | 60 | - **流动性风险**:由于加密货币在 staking 期间被锁定,持有者可能面临流动性不足的问题。 61 | - **市场风险**:加密货币市场价格波动较大,持有者的 staking 收益可能无法抵消价格下跌带来的损失。 62 | - **集中化风险**:在某些情况下,持有大量加密货币的大户可能控制网络,导致集中化问题。 63 | 64 | # 二. 课程说明 65 | 66 | 本课程将围绕 ETH2.0, Solana 和 Tezos 展开,由于这里讲解的内容和 staking 的底层实现逻辑并没有太大的关系,因此本课程不会深入讲解 Staking 的底层实现机制,而是仅仅讲解钱包层面和 staking 相关的内容。接下来我们将围绕着 Ethereum2.0, Solana 和 Tezos 展开本课程的内容。 67 | 68 | # 二. ETH2.0 staking 69 | 70 | 以太坊 2.0(Ethereum 2.0),也被称为 ETH2 或 Serenity,是以太坊区块链的一个重大升级,旨在改善可扩展性、安全性和可持续性。ETH2的核心目标是通过引入新的共识机制(称为权益证明)以及分阶段的部署来解决当前以太坊网络面临的一系列问题。 71 | 72 | 以下是ETH2的一些主要特性和改进: 73 | 74 | - **权益证明(PoS)机制**:ETH2将从以太坊1.0的工作量证明(PoW)共识机制转变为权益证明(PoS)共识机制。在PoS中,验证者通过抵押一定数量的ETH来参与网络的验证和区块生产,而不是像PoW中那样依赖大量的计算资源来解决数学难题。 75 | - **分阶段部署**:ETH2的升级是分阶段进行的。第一个阶段称为Beacon Chain,它引入了PoS共识机制,并与现有的以太坊1.0链平行运行。随后的阶段将逐步引入区块链合并、分片技术等功能,以进一步提高网络的性能和可扩展性。 76 | - **分片技术**:在ETH2中,分片是将整个区块链状态和交易处理分成小片段的技术。这样做可以提高网络的吞吐量,允许更多的交易并行处理,从而提高整个系统的性能。 77 | - **网络安全和可持续性**:通过引入PoS共识机制,ETH2旨在提高网络的安全性,并降低对能源的消耗。相较于PoW,PoS能够降低对能源的需求,并且更加环保。 78 | - **奖励和激励机制**:参与ETH2网络的验证者(即持有者)可以通过提供安全性和参与区块生产来获得奖励。这些奖励主要是通过区块的交易费和新铸造的ETH来实现的。 79 | 80 | 总的来说,以太坊2.0的目标是建立一个更加可扩展、安全和可持续的区块链网络。通过引入PoS共识机制、分片技术等创新,ETH2旨在解决以太坊1.0中存在的各种问题,并为未来的去中心化应用提供更好的基础设施。 81 | 82 | ## 1.ETH 2.0 质押介绍 83 | 84 | 在 ETH2.0 中,共识算法是 POS,信标链负责共识模块,每质押 32 个 ETH 可以启动一个信标链节点, 现在有很多 LSD 协议专门做 ETH 质押的,例如:lido, swell, mantle-lsd 等项目,也有从二层质押回去,1 层的模式类似这些 LSD 项目的,这里以 DappLink 的 LinkLayr 多重质押协议为例给大家讲解整个流程。 85 | 86 | **1.1.正常 LSD 质押流程** 87 | 88 | ![图像](../img/staking1.png) 89 | 90 | **1.2.DappLink 的 LinkLayer 质押协议** 91 | 92 | ![图像](../img/staking2.jpg) 93 | 94 | **1.3.LinkLayer 一层质押模型角色** 95 | 96 | **质押流程** 97 | 98 | ![图像](../img/staking3.jpg) 99 | 100 | **资金归集** 101 | 102 | ![图像](../img/staking4.jpg) 103 | 104 | **退出节点** 105 | 106 | ![图像](../img/staking5.jpg) 107 | 108 | **1.4代码实战** 109 | 110 | - https://github.com/eniac-x-labs/linklayer 111 | - https://github.com/eniac-x-labs/dapplink-services 112 | 113 | ## 2.钱包里面实现 ETH 质押功能 114 | 115 | 钱包只需要找对应的 LSD 项目合作,通过 call 合约的方式把资金丢到 LSD 项目里面就可以 116 | 117 | # 三. Solana(SOL) Staking 118 | 119 | ## 1.Solana 质押概述 120 | 121 | Solana 区块链上的质押是指将一定数量的 SOL 代币锁定在网络上,以支持网络的安全性和稳定性,并获得相应的奖励。以下是 Solana 质押的一些概述: 122 | 123 | - **质押奖励**:通过在 Solana 网络上质押 SOL 代币,有机会获得相应的奖励。这些奖励主要来自于网络的通胀机制和交易费用。 124 | - **网络安全**:质押 SOL 代币有助于确保 Solana 网络的安全性和去中心化。质押者通过参与网络的验证和共识过程来保护网络免受恶意攻击和攻击者的影响。 125 | - **质押池**:Solana 网络通常会有多个质押池(staking pool)可供选择,质押者可以选择将其 SOL 代币放入这些池子中,而不必自行运行验证节点。质押池通常提供更简单的质押体验,并可能提供更广泛的奖励分配方式;各大钱包公司都提供了可供选择的质押节点。 126 | - **质押门槛**:参与 Solana 质押通常需要达到一定的门槛,即需要锁定一定数量的 SOL 代币才能参与质押。质押门槛的具体要求可能会因质押池或网络协议而异。 127 | - **退出质押**:质押者可以随时选择退出质押并解锁其质押的 SOL 代币。但在某些情况下,可能需要一定的解锁期限或过程才能完全退出质押并取回所有的代币。 128 | 129 | 总的来说,Solana 质押是参与 Solana 网络并为其安全性和稳定性做出贡献的一种方式,同时也是一种获取奖励的机会。质押者应该对网络的运行机制和质押奖励等方面有一定的了解,并在决定参与质押之前进行适当的研究和考虑。 130 | 131 | ## 2.Solana 质押离线签名 132 | 133 | **2.1.创建质押账号并质押资金** 134 | 135 | ```js 136 | export async function createAccount() { 137 | let tx = new Transaction(); 138 | const priv = "privateKey" 139 | const fromPublicKey = Keypair.fromSecretKey(new Uint8Array(Buffer.from(priv, "hex"))); 140 | let stakeAccount = Keypair.generate(); 141 | const secretKey = stakeAccount.secretKey 142 | let secretKeyHex = Buffer.from(secretKey).toString('hex'); 143 | let authorizedAccount = fromPublicKey; 144 | let lamportsForStakeAccount = 12388800; 145 | let createAccountTransaction = StakeProgram.createAccount({ 146 | fromPubkey: fromPublicKey.publicKey, 147 | authorized: new Authorized(authorizedAccount.publicKey, authorizedAccount.publicKey), 148 | lamports: lamportsForStakeAccount, 149 | lockup: new Lockup(0, 0, fromPublicKey.publicKey), 150 | stakePubkey: stakeAccount.publicKey 151 | }); 152 | tx.add(createAccountTransaction) 153 | tx.recentBlockhash = "F7i9vw8taJvVJntB7nV8CMf4tNskmxwnLJSCFosGgofZ"; 154 | tx.sign(fromPublicKey, stakeAccount); 155 | const serializeMsg = tx.serialize().toString("base64"); 156 | console.log("serializeMsg====", serializeMsg) 157 | } 158 | ``` 159 | 160 | **2.2.转移质押投票权** 161 | 162 | ```js 163 | export async function delegateStake() { 164 | let tx = new Transaction() 165 | const priv1 = "privateKey-1" 166 | const authorizedAccount = Keypair.fromSecretKey(new Uint8Array(Buffer.from(priv1, "hex"))); 167 | const priv2 = "privateKey-2" 168 | const stakeAccount = Keypair.fromSecretKey(new Uint8Array(Buffer.from(priv2, "hex"))); 169 | let votePubkey = new PublicKey("FKsC411dik9ktS6xPADxs4Fk2SCENvAiuccQHLAPndvk"); 170 | let delegateTransaction = StakeProgram.delegate({ 171 | stakePubkey: stakeAccount.publicKey, 172 | authorizedPubkey: authorizedAccount.publicKey, 173 | votePubkey: votePubkey, 174 | }); 175 | tx.add(delegateTransaction) 176 | tx.recentBlockhash = "Hq6d9jBrs6RHtAbFFy4beYPjx6fggVKETeeahC9jBPGK"; 177 | tx.sign(authorizedAccount, stakeAccount); 178 | const serializeMsg = tx.serialize().toString("base64"); 179 | console.log("serializeMsg====", serializeMsg) 180 | } 181 | ``` 182 | 183 | 2.3.解除账号活跃状态 184 | 185 | ```js 186 | export function deactivateStake() { 187 | let tx = new Transaction() 188 | const priv1 = "privateKey-1" 189 | const authorizedAccount = Keypair.fromSecretKey(new Uint8Array(Buffer.from(priv1, "hex"))); 190 | const priv2 = "privateKey-2" 191 | const stakeAccount = Keypair.fromSecretKey(new Uint8Array(Buffer.from(priv2, "hex"))); 192 | let deactivateTransaction = StakeProgram.deactivate({ 193 | stakePubkey: stakeAccount.publicKey, 194 | authorizedPubkey: authorizedAccount.publicKey, 195 | }); 196 | tx.add(deactivateTransaction) 197 | tx.recentBlockhash = "AScJ4TVPF26EF82mLtv5R5SJYgCB3FhaHnLVEAdjKtx7"; 198 | tx.sign(authorizedAccount, stakeAccount); 199 | const serializeMsg = tx.serialize().toString("base64"); 200 | console.log("serializeMsg====", serializeMsg) 201 | } 202 | ``` 203 | 204 | 2.4.提取质押的资金 205 | 206 | ```js 207 | export function withdrawFunds() { 208 | let tx = new Transaction() 209 | const priv1 = "privateKey-1" 210 | const authorizedAccount = Keypair.fromSecretKey(new Uint8Array(Buffer.from(priv1, "hex"))); 211 | const priv2 = "privateKey-2" 212 | const stakeAccount = Keypair.fromSecretKey(new Uint8Array(Buffer.from(priv2, "hex"))); 213 | const priv = "privateKey-1" 214 | const fromPublicKey = Keypair.fromSecretKey(new Uint8Array(Buffer.from(priv, "hex"))); 215 | const stakeBalance = 1010592 216 | let withdrawTransaction = StakeProgram.withdraw({ 217 | stakePubkey: stakeAccount.publicKey, 218 | authorizedPubkey: authorizedAccount.publicKey, 219 | toPubkey: fromPublicKey.publicKey, 220 | lamports: stakeBalance, 221 | }); 222 | tx.add(withdrawTransaction) 223 | tx.recentBlockhash = "2453A3Kep5d1kmoNP5p5i9NPhmhZANe7q6rDU8TUPRqj"; 224 | tx.sign(authorizedAccount, stakeAccount); 225 | const serializeMsg = tx.serialize().toString("base64"); 226 | console.log("serializeMsg====", serializeMsg) 227 | } 228 | ``` 229 | 230 | ## 3.Solana 质押相关的 RPC 接口 231 | 232 | **3.1.获取可以进行质押的节点** 233 | 234 | - 请求示范 235 | 236 | ```text 237 | curl --location 'https://sly-yolo-dinghy.solana-mainnet.quiknode.pro/2ac2af5b8c2e5e9e74c7906e949f1976314aa996' \ 238 | --header 'Content-Type: application/json' \ 239 | --data ' { 240 | "jsonrpc": "2.0", 241 | "id": 1, 242 | "method": "getClusterNodes" 243 | }' 244 | ``` 245 | 246 | - 返回值: 247 | 248 | ```text 249 | { 250 | "jsonrpc": "2.0", 251 | "result": [ 252 | { 253 | "featureSet": 3746964731, 254 | "gossip": "216.144.245.62:8001", 255 | "pubkey": "BmR3ANrUw8aKEnh9a7wBMXAoT1MKtXYsYqWQxQ7sdHmL", 256 | "pubsub": null, 257 | "rpc": null, 258 | "shredVersion": 50093, 259 | "tpu": "216.144.245.62:8004", 260 | "tpuQuic": "216.144.245.62:8010", 261 | "version": "1.17.34" 262 | } 263 | ], 264 | "id": 1 265 | } 266 | ``` 267 | 268 | - pubkey: 节点公钥,质押 delegate 的时候签名需要用 269 | 270 | **3.2.获取 Epoch 信息** 271 | 272 | - 请求示范 273 | 274 | ```text 275 | curl --location 'https://sly-yolo-dinghy.solana-mainnet.quiknode.pro/2ac2af5b8c2e5e9e74c7906e949f1976314aa996' \ 276 | --header 'Content-Type: application/json' \ 277 | --data ' { 278 | "jsonrpc": "2.0", 279 | "id": 1, 280 | "method": "getEpochInfo" 281 | }' 282 | ``` 283 | 284 | - 返回值 285 | 286 | ```text 287 | { 288 | "jsonrpc": "2.0", 289 | "result": { 290 | "absoluteSlot": 271176477, 291 | "blockHeight": 250804509, 292 | "epoch": 627, 293 | "slotIndex": 312477, 294 | "slotsInEpoch": 432000, 295 | "transactionCount": 294818991055 296 | }, 297 | "id": 1 298 | } 299 | ``` 300 | 301 | - epoch:为当前时期 302 | 303 | **3.3.发送交易到区块链网络** 304 | 305 | - 请求示范 306 | 307 | ```text 308 | curl --location 'https://sly-yolo-dinghy.solana-mainnet.quiknode.pro/2ac2af5b8c2e5e9e74c7906e949f1976314aa996' \ 309 | --header 'Content-Type: application/json' \ 310 | --data '{ 311 | "jsonrpc": "2.0", 312 | "id": 1, 313 | "method": "sendTransaction", 314 | "params": [ 315 | "ASG4jVQEsZhgMGnkXIYEvyhvGMYBfwL8lFd40mPz4AjpeWqZlVM122Vx8iCZIUNmbGdqDcdplm0xMUGri4WiCAIBAAEDOns4dLpGe+a4HqNh49dFOvi4HIiu3SS1Ax/doLxxrTJa8yfsHKLzR/zaYPv8xS5VGKfAp4PkO8pQN4Jn396IRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKy6/vKD5x9Dmbz1bBJ85fYZK94CfRhjYwMBlQyb4mz8BAgIAAQwCAAAAQEIPAAAAAAA=", 316 | { 317 | "encoding":"base64" 318 | } 319 | 320 | ] 321 | }' 322 | ``` 323 | 324 | - 返回值 325 | 326 | ```text 327 | { 328 | "jsonrpc": "2.0", 329 | "result": "g6yMJUd16jAcPiQfi7QK1M5tN7yheeKAC7NbUh3BQCSfav3pgv74ovCSGuhgApzma1s6ew8WEmX1Bzfk6sCfg9P", 330 | "id": 1 331 | } 332 | ``` 333 | 334 | # 四. Tezos(XTZ) Staking 335 | 336 | Tezos质押(Staking)是一种通过参与Tezos区块链共识机制来获得奖励的方式。质押的过程包括锁定一定数量的Tezos代币(XTZ)以支持网络的安全和操作,并获得相应的回报。以下是关于Tezos质押的一些关键点: 337 | 338 | ## 1.**Tezos质押** 339 | 340 | Tezos质押是指持有者将其XTZ代币锁定在网络中,以参与区块链的共识机制。Tezos使用一种称为“Liquid Proof-of-Stake”(LPoS)的共识算法,这种算法允许代币持有者通过质押来参与网络治理和区块验证。 341 | 342 | **1.1. 参与方式** 343 | 344 | 质押Tezos可以通过两种主要方式进行: 345 | 346 | - **自我质押(Self-Baking)**:用户自己运行一个Tezos节点,并直接参与区块验证。这种方式需要一定的技术知识和硬件支持。 347 | - **委托质押(Delegation)**:用户将他们的XTZ代币委托给一个专业的Baker(质押服务提供者),由后者代表他们参与区块验证。用户仍然保有其代币的所有权,只是将其质押权委托出去。 348 | 349 | **1.2. 质押的好处** 350 | 351 | - **获得奖励**:参与质押可以获得XTZ代币奖励,奖励的数量取决于质押的数量和时间。 352 | - **网络安全**:质押帮助增强Tezos区块链的安全性和稳定性。 353 | - **参与治理**:质押者可以参与Tezos网络的治理,投票决定网络升级和其他重要事项。 354 | 355 | **1.3. 风险与考虑** 356 | 357 | - **技术要求**:自我质押需要一定的技术能力和设备维护。 358 | - **托管风险**:委托质押涉及将代币委托给第三方,有一定的托管风险。 359 | - **市场波动**:XTZ代币的市场价格波动可能影响质押奖励的实际价值。 360 | 361 | **1.4.质押流程** 362 | 363 | - **自我质押**: 设置并运行一个Tezos节点。 锁定一定数量的XTZ代币作为质押。 参与区块验证并获得奖励。 364 | - **委托质押**: 选择一个可信赖的Baker。 在钱包中将XTZ代币委托给该Baker。 等待奖励分配。 365 | 366 | ## 2.Tezos 质押离线签名 367 | 368 | ```js 369 | export async function StakingSign({ txObj, privs }): Promise { 370 | const {from, delegate, counter, branch, fee, gasLimit, storageLimit } = txObj; 371 | const fromAddress = from; 372 | if (!privs[0] || !privs[0].key || privs[0].key === "") { 373 | throw new Error(`交易签名缺少私钥`); 374 | } 375 | const privKey: string = privs[0].key.substring(0, 64); 376 | const encodedPrivKey = encodePrivKey(privKey); 377 | const signer = new InMemorySigner(encodedPrivKey); 378 | var rpcOperations: RPCDelegateOperation[] = []; 379 | var revealOp: RPCDelegateOperation; 380 | revealOp = { 381 | kind: OpKind.DELEGATION, 382 | source: fromAddress, 383 | fee: fee, 384 | gas_limit: gasLimit, 385 | storage_limit: storageLimit, 386 | delegate: delegate 387 | } 388 | rpcOperations.push(revealOp); 389 | const preparedOp = prepareOp(branch, counter, { 390 | operation: rpcOperations, 391 | source: fromAddress, 392 | }); 393 | const opObLst:PreparedOperation = { 394 | opOb: preparedOp.opOb, 395 | counter: counter 396 | } 397 | const forgedBytes = await forge(opObLst); 398 | const signed = await sign(forgedBytes.opbytes, signer); 399 | console.log(signed.prefixSig) 400 | return signed.sbytes; 401 | } 402 | ``` 403 | 404 | ## 3.Tezos 质押相关的 RPC 接口 405 | 406 | **3.1.获取烘焙师列表** 407 | 408 | - 请求示范 409 | 410 | ```text 411 | curl --location 'https://rpc.tzbeta.net/chains/main/blocks/head/helpers/baking_rights' 412 | ``` 413 | 414 | - 返回值 415 | 416 | ```text 417 | [ 418 | { 419 | "level": 5780818, 420 | "delegate": "tz1aRoaRhSpRYvFdyvgWLL6TGyRoGF51wDjM", 421 | "round": 0, 422 | "estimated_time": "2024-06-11T09:48:30Z", 423 | "consensus_key": "tz1aRoaRhSpRYvFdyvgWLL6TGyRoGF51wDjM" 424 | },{ 425 | "level": 5780818, 426 | "delegate": "tz2FCNBrERXtaTtNX6iimR1UJ5JSDxvdHM93", 427 | "round": 60, 428 | "estimated_time": "2024-06-11T12:26:00Z", 429 | "consensus_key": "tz2FCNBrERXtaTtNX6iimR1UJ5JSDxvdHM93" 430 | } 431 | ] 432 | ``` 433 | 434 | - delegate:接受投票权限的烘焙师的地址 435 | 436 | 3.2.发送交易 437 | 438 | - 请求示范 439 | 440 | ```text 441 | curl -X POST "http://localhost:8732/injection/operation?async=false&chain=main" \ 442 | -H "Content-Type: application/octet-stream" \ 443 | --data-binary "6d5b0460a74cbca8b5bb4f97fd0cf6a0282c1ff8ded5180f3a98b1303bea6e846e00c42c5e588c3c6685e78ed2cc287ba1561698440d8c03a8aeb71dcc0800ff00fe9ceee394b26880e978fd409967f8c0d84c923a2c02be2874309f9105ef0aa31ef3c74f57735bf71ec6e0e9cbdc2c068a5093f7afd37f459871a75fd8e000836158ba4dbe08d3632f864df093c877c9fee05f03" 444 | ``` 445 | 446 | - 返回值 447 | 448 | 💡💡交易 Hash 449 | 450 | # 五. 总结 451 | 452 | 我们都知道,在交易所(如 Binance, Bybit 和 Okx)还有很多 HD 钱包(如 Metamask 和 phantom )里面都集成了 staking 的功能,由此看出,整个 staking 的在钱包开发中是比较重要的一环;熟悉 staking 的开发流程,也将帮助你更了解 staking 的一些本质性的东西。我们整个课程由 Eth2.0, Solana 和 Tezos 质押展开, ETH2.0 质押产品 LSD 开发的复杂度是比较高的 ,但是对于钱包接入来说,只需要对接 LSD 合约就行,开发起来是比较简单的, 当然,Solana 和 Tezos 质押在钱包开发中也比较简单,其他链的开发也和他们大同小异,因此我们本课程中也不再展开说明了。 453 | 454 | -------------------------------------------------------------------------------- /stacks/readme.md: -------------------------------------------------------------------------------- 1 | # Stacks 钱包开发详细教程 2 | 3 | ## 一. Stacks 简介 4 | 5 | Stacks 是一个开源项目,旨在通过将智能合约和去中心化应用(dApps)引入比特币区块链,从而扩展比特币的功能。它通过名为 "Proof of Transfer" (PoX) 的共识机制,将新一代的区块链与比特币连接起来。 6 | 7 | ## **1.核心组件** 8 | 9 | **Stacks 区块链:**Stacks 区块链是一个独立的区块链,与比特币紧密耦合。它通过 PoX 共识机制利用比特币的安全性和稳定性来实现自身的去中心化和安全性。 10 | 11 | **Proof of Transfer (PoX):** PoX 是一种独特的共识机制,允许 Stacks 区块链重用比特币的算力。矿工通过在比特币网络上支出 BTC 来竞选新的 Stacks 区块的创建权,从而在 Stacks 网络上获取奖励。 12 | 13 | **Clarity 智能合约语言:**Clarity 是 Stacks 网络上使用的智能合约语言,专为安全和可预测性而设计。它是一种非图灵完备的语言,这意味着它在执行时的行为是可预测的,并且可以在执行前完全分析其运行结果。 14 | 15 | ## 2.POX 共识算法详解 16 | 17 | Proof of Transfer (PoX) 是一种创新的共识机制,旨在利用现有区块链(如比特币)的安全性来保护新兴区块链(如 Stacks)的网络。PoX 通过让矿工在比特币区块链上支出 BTC 来竞选新块的生成权,并将这些 BTC 奖励给参与者,从而实现新旧区块链的连接和安全共享。 18 | 19 | **2.1.参与者** 20 | 21 | - **矿工(Miners)**:在 PoX 机制中,矿工通过支出比特币来竞争 Stacks 区块的创建权。 22 | - **堆叠者(Stackers)**:持有 STX 代币并愿意参与 Stacking 的用户。他们通过锁定 STX 代币来支持网络的稳定性和安全性,并获得 BTC 奖励。 23 | 24 | **2.2.工作流程** 25 | 26 | - **矿工提交 BTC**:矿工将一定数量的比特币(BTC)发送到指定的比特币地址。这些比特币被称为“竞选比特币”。 27 | - **选择区块创建者**:使用加权随机选择算法,从所有提交了竞选比特币的矿工中选出一个矿工来创建下一个 Stacks 区块。 28 | - **分发 BTC 奖励**:选出的矿工创建新的 Stacks 区块,且之前提交的 BTC 奖励会分发给参与 Stacking 的堆叠者。 29 | 30 | **2.3.主要功能** 31 | 32 | - **智能合约:** 通过 Clarity 编写和执行智能合约,使开发者能够创建复杂的 dApps,同时利用比特币的安全性。 33 | - **去中心化应用(dApps):**开发者可以在 Stacks 平台上创建和部署去中心化应用,这些应用可以与比特币直接交互,并使用 BTC 作为主要货币。 34 | - **Stacking:**Stacks 代币持有者可以通过参与 Stacking 来帮助保护网络并获得 BTC 奖励。Stacking 类似于质押,但它通过锁定 STX 代币并参与 PoX 机制来实现。 35 | 36 | ## **3.Stacks 代币(STX)** 37 | 38 | STX 是 Stacks 区块链的原生代币,用于支付交易费用、执行智能合约以及参与 Stacking。 39 | 40 | ## **4. 实际应用** 41 | 42 | Stacks 已经有多个实际应用和项目在其平台上运行,包括去中心化金融(DeFi)、NFT 市场以及社交媒体平台。这些应用展示了 Stacks 如何利用比特币的安全性和 Clarity 智能合约的灵活性来创建创新的区块链解决方案。 43 | 44 | # 二. 离线地址生成 45 | 46 | ```js 47 | export function createAddress(params: any): any { 48 | const { seedHex, addressIndex } = params; 49 | const node = bip32.fromSeed(Buffer.from(seedHex, 'hex')); 50 | const childKey = node.derivePath("m/44'/5757'/0'/0/" + addressIndex + ''); 51 | 52 | const derivePubK = childKey.publicKey.toString('hex'); 53 | const publicKey = Buffer.from(derivePubK, 'hex'); 54 | 55 | let uncompressedPublicKey = new Uint8Array(65); 56 | secp256k1.publicKeyConvert(new Uint8Array(publicKey), false, uncompressedPublicKey); 57 | uncompressedPublicKey = Buffer.from(uncompressedPublicKey); 58 | // @ts-ignore 59 | const address = getAddressFromPrivateKey(Buffer.from(childKey.privateKey).toString('hex') + "01", TransactionVersion.Mainnet ) 60 | return { 61 | privateKey: Buffer.from(childKey.privateKey).toString('hex') + "01", 62 | address: address 63 | } 64 | } 65 | ``` 66 | 67 | # 三. 离线签名 68 | 69 | ```js 70 | export async function signTransaction(params){ 71 | const { 72 | to, amount, fee, nonce, memo, decimal, privKey, network 73 | } = params; 74 | const privateKey = privKey; 75 | const calcAmount = new BigNumber(amount).times(new BigNumber(10).pow(decimal)).toNumber(); 76 | if (calcAmount % 1 !== 0) throw new Error("decimal 无效"); 77 | const stacksNet = network === "main_net" ? new StacksMainnet() : new StacksTestnet(); 78 | const txOptions = { 79 | recipient: to, 80 | amount: calcAmount, 81 | senderKey: privateKey, 82 | network: stacksNet, 83 | memo: memo, 84 | nonce: nonce, 85 | fee: fee, 86 | anchorMode: AnchorMode.Any 87 | }; 88 | const transaction = await makeSTXTokenTransfer(txOptions); 89 | return transaction.serialize().toString('hex') 90 | } 91 | ``` 92 | 93 | # 四. STX 钱包开发相关的 RPC 接口 94 | 95 | ## 1.获取最新块高 96 | 97 | - 接口作用:获取最新块高和检查公链同步状态 98 | - 接口参数:无 99 | - 请求示范 100 | 101 | ``` 102 | curl --location 'https://api.mainnet.hiro.so/extended/v2/blocks/' 103 | ``` 104 | 105 | - 返回值 106 | 107 | ```json 108 | { 109 | "limit": 20, 110 | "offset": 0, 111 | "total": 152310, 112 | "results": [ 113 | { 114 | "canonical": true, 115 | "height": 152310, 116 | "hash": "0xf24fc095620160c54389b881a9b3c5d8b298d94c6ca0e17e48b8367824248d74", 117 | "block_time": 1717240458, 118 | "block_time_iso": "2024-06-01T11:14:18.000Z", 119 | "index_block_hash": "0x1d5058157c3207cb06d3d4e8507ff1f081353da8946d479bb20913e50d214803", 120 | "parent_block_hash": "0x66400b855151626f2643fc4c1b58d0a873d03803045967d4bee5de976704b0a8", 121 | "burn_block_time": 1717240458, 122 | "burn_block_time_iso": "2024-06-01T11:14:18.000Z", 123 | "burn_block_hash": "0x0000000000000000000028e67320af6ad8d2ae6f1c7b95f98eb6f9177920e526", 124 | "burn_block_height": 846050, 125 | "miner_txid": "0x3b6bfb369e8a0cc4eca3580e3d47eb01caca71ff7c5f31f3a6d98d9a5701d058", 126 | "parent_microblock_hash": "", 127 | "parent_microblock_sequence": -1, 128 | "txs": [ 129 | "0x2a3e8ba75e6571d8d7e4bc0f3bb0d0d5e624a319a8bf1534eb0bf8ec502c951b", 130 | ], 131 | "microblocks_accepted": [], 132 | "microblocks_streamed": [], 133 | "execution_cost_read_count": 428, 134 | "execution_cost_read_length": 91935, 135 | "execution_cost_runtime": 7901829, 136 | "execution_cost_write_count": 10, 137 | "execution_cost_write_length": 418, 138 | "microblock_tx_count": {} 139 | } 140 | ] 141 | } 142 | ``` 143 | 144 | - height 第一项为最新块高 145 | 146 | ## 2. 获取账户签名交易的 Nonce 和账户余额 147 | 148 | - 接口作用:获取交易签名的 nonce 149 | - 接口参数: 需要获取 Nonce 的账户地址 150 | - 请求示范 151 | 152 | ``` 153 | curl --location 'https://stacks-node-api.mainnet.stacks.co/v2/accounts/SP11RPVAKGYF7N7EM9XWH926SF8ZTY7FPXGWCVWC1' 154 | ``` 155 | 156 | - 返回值 157 | 158 | ```json 159 | { 160 | "balance":"0x000000000000000000000000004ac0b8", 161 | "locked":"0x00000000000000000000000000000000", 162 | "unlock_height":0, 163 | "nonce":1, 164 | "balance_proof":"", 165 | "nonce_proof":"" 166 | } 167 | ``` 168 | 169 | - balance:账户余额 170 | - nonce:签名交易体参数 171 | 172 | ## 3. 根据区块号获取交易 173 | 174 | - 接口作用:根据区块号获取区块链信息 175 | - 接口参数: 区块高度 176 | - 请求示范 177 | 178 | ``` 179 | curl --location 'https://api.mainnet.hiro.so/extended/v1/block/by_height/152290' 180 | ``` 181 | 182 | - 返回值 183 | 184 | ```json 185 | { 186 | "canonical": true, 187 | "height": 152290, 188 | "hash": "0x19a26d0a71cdb24fdfae40132dd611dd9a03c5b8fe1b6a5b63316b3ac8adff63", 189 | "block_time": 1717227375, 190 | "block_time_iso": "2024-06-01T07:36:15.000Z", 191 | "index_block_hash": "0x2de372e030c5c2c0b5ae03859bcb6d17225c40fd86807473e69911d52841ec72", 192 | "parent_block_hash": "0x8fed97eb49751646e95edd9f06a1ff0e0f30d76d112eb60572c8086c8a4d6dff", 193 | "burn_block_time": 1717227375, 194 | "burn_block_time_iso": "2024-06-01T07:36:15.000Z", 195 | "burn_block_hash": "0x00000000000000000001726c4f29eb9e01f96db8e12bc9813fcdb4e3a4dff9c2", 196 | "burn_block_height": 846029, 197 | "miner_txid": "0x91e4ef60e9f1382bb55003bfbc86c9e56fdb6cd353aade048dcb1960d53dbe8e", 198 | "parent_microblock_hash": "", 199 | "parent_microblock_sequence": -1, 200 | "txs": [ 201 | "0x118c2b5e431051bf83319c378a1ee8d7f3ff22c744b372e250f86f36a482710f", 202 | "0xa226fb79920a8d4d90d23fac29249aa87b45daaeb4480518cf9faba6e8f843d0", 203 | "0x62f2cf634fa9b53b7efa04dccb544de1e07cb5c4eaf53b71da18d0447c967a2d", 204 | "0x9e5b3caf459258904b2b7ac016d37110c8d6eaec384286c5f8768a968bea2671", 205 | "0x7003ec9ec330b9cbb45e7d4611cc5c6d791fd93274b9641a21a807fe44061b96", 206 | "0xdbd9037d227d05aa88e7fc07f3b8486f849e6b1fce757ad7dab7ab3700b48ba0", 207 | "0x7c5b73206cada60ec1e6c96a6b3de47590eb3479db23ee6d115b20af4ce20a0c", 208 | "0xf14c72d1a06b13df90c2297db7e9ec7ac79095366035479c59f4fac25e9cdfd9", 209 | "0xfad9c58dbd4d2b7ab591e9f062ad49e75812308a8bc3c4e4d858f4bae9e854cf", 210 | "0x9f8ccbef3cbd3aea5542de52d2e362483dc5909696b82e213073c060906bc757", 211 | "0x34975a203ba98944bb5d3446802b04ab3a4190a58fb0cae411ce85a0b984f115", 212 | "0xdfaca9eaefe4f8627646b5594a62eb68d3107ea96e00e2f4a4197888845e08fa", 213 | "0x97aa470e533d3db74f903031d4dbfa7802eba60894386c1de9c7e26c6ce8279d", 214 | "0xec7e1a43a52b6cc2d6f42040cb4304998d2746fc7bbc585e9e8238645bef0047", 215 | "0x7706b87bd2d034020ab45cfefaf1c3e310d836d0e1d62d74ab59fc6c69651a50", 216 | "0x9b99dda174b35143685cf5dfe1e6af36a5ed7adc65b0d9b38eba4c37b654ed2b", 217 | "0x7612542575cecd6067882a9245bf63d69ddb62d32ca8cb821dbd64821d998e97", 218 | "0x21e0a53755a1fff42a49ac1a86f681a9ddd3143a1e8655b784e245ca76d98e42", 219 | "0xd63a7d74c59969856e2eb6328fe3baab6ae142b704ac1625d458b24782592de9" 220 | ], 221 | "microblocks_accepted": [], 222 | "microblocks_streamed": [], 223 | "execution_cost_read_count": 1123, 224 | "execution_cost_read_length": 5070876, 225 | "execution_cost_runtime": 31806543, 226 | "execution_cost_write_count": 53, 227 | "execution_cost_write_length": 9326, 228 | "microblock_tx_count": {} 229 | } 230 | ``` 231 | 232 | - txs:交易列表,里面是交易 Hash 233 | 234 | ## 4.根据交易 Hash 获取交易 235 | 236 | - 接口作用:根据交易 Hash 获取交易 237 | - 接口参数: 交易Hash 238 | - 请求示范 239 | 240 | ``` 241 | curl --location 'https://api.mainnet.hiro.so/extended/v1/tx/ed917ad07e01653ea17644299f1ef4f2cfe6e46d887979d9ab7a6369ad883918' 242 | ``` 243 | 244 | - 返回值 245 | 246 | ```json 247 | { 248 | "tx_id": "0xed917ad07e01653ea17644299f1ef4f2cfe6e46d887979d9ab7a6369ad883918", 249 | "nonce": 1, 250 | "fee_rate": "21700", 251 | "sender_address": "SP11RPVAKGYF7N7EM9XWH926SF8ZTY7FPXGWCVWC1", 252 | "sponsored": false, 253 | "post_condition_mode": "deny", 254 | "post_conditions": [], 255 | "anchor_mode": "any", 256 | "tx_status": "pending", 257 | "receipt_time": 1717241359, 258 | "receipt_time_iso": "2024-06-01T11:29:19.000Z", 259 | "tx_type": "token_transfer", 260 | "token_transfer": { 261 | "recipient_address": "SP2J1YDB3A9KYJQ7EZ70PXJKSQY9H78K5Q6CGH8VX", 262 | "amount": "300000", 263 | "memo": "0x746865207765623320636f7572736500000000000000000000000000000000000000" 264 | } 265 | } 266 | ``` 267 | 268 | - sender_address: 转出地址 269 | - recipient_address:转入地址 270 | - amount:转账金额 271 | - memo:转账备注 272 | - tx_type: 转账类型 273 | 274 | ## 5.根据地址获取交易列表 275 | 276 | - 接口作用:根据地址获取交易列表 277 | - 接口参数: 地址 278 | - 请求示范 279 | 280 | ``` 281 | curl --location 'https://api.mainnet.hiro.so/extended/v2/addresses/SP11RPVAKGYF7N7EM9XWH926SF8ZTY7FPXGWCVWC1/transactions' 282 | ``` 283 | 284 | - 返回值 285 | 286 | ```json 287 | { 288 | "limit": 20, 289 | "offset": 0, 290 | "total": 2, 291 | "results": [ 292 | { 293 | "tx": { 294 | "tx_id": "0xd825bfc0559c2de86e93b587747682b7aba3555fce092fc729fe4a7b29f5fe01", 295 | "nonce": 0, 296 | "fee_rate": "1000", 297 | "sender_address": "SP11RPVAKGYF7N7EM9XWH926SF8ZTY7FPXGWCVWC1", 298 | "tx_type": "token_transfer", 299 | "token_transfer": { 300 | "recipient_address": "SP2J1YDB3A9KYJQ7EZ70PXJKSQY9H78K5Q6CGH8VX", 301 | "amount": "100000", 302 | "memo": "0x6d656d6f000000000000000000000000000000000000000000000000000000000000" 303 | } 304 | }, 305 | "stx_sent": "101000", 306 | "stx_received": "0", 307 | "events": { 308 | "stx": { 309 | "transfer": 1, 310 | "mint": 0, 311 | "burn": 0 312 | }, 313 | "ft": { 314 | "transfer": 0, 315 | "mint": 0, 316 | "burn": 0 317 | }, 318 | "nft": { 319 | "transfer": 0, 320 | "mint": 0, 321 | "burn": 0 322 | } 323 | } 324 | }, 325 | { 326 | "tx": { 327 | "tx_id": "0xfc9f84ba538e1b63643655fac219d343fffc845d47cb9b4cb9cba90a406e4654", 328 | "nonce": 20350, 329 | "fee_rate": "300000", 330 | "sender_address": "SP2ADT4TAV92SPBNP3WE9GPJ12F6CKGWMEJ1H1BB9", 331 | "tx_type": "token_transfer", 332 | "token_transfer": { 333 | "recipient_address": "SP11RPVAKGYF7N7EM9XWH926SF8ZTY7FPXGWCVWC1", 334 | "amount": "5000000", 335 | "memo": "0x31313030000000000000000000000000000000000000000000000000000000000000" 336 | } 337 | }, 338 | "stx_sent": "300000", 339 | "stx_received": "5000000", 340 | "events": { 341 | "stx": { 342 | "transfer": 1, 343 | "mint": 0, 344 | "burn": 0 345 | }, 346 | "ft": { 347 | "transfer": 0, 348 | "mint": 0, 349 | "burn": 0 350 | }, 351 | "nft": { 352 | "transfer": 0, 353 | "mint": 0, 354 | "burn": 0 355 | } 356 | } 357 | } 358 | ] 359 | } 360 | ``` 361 | 362 | - sender_address: 转出地址 363 | - recipient_address:转入地址 364 | - amount:转账金额 365 | - memo:转账备注 366 | - tx_type: 转账类型 367 | 368 | ## 6.发送交易到区块链网络 369 | 370 | - 接口作用:发送交易到区块链网络 371 | - 接口参数: 签名的交易体 372 | - 请求示范 373 | 374 | ``` 375 | curl --location 'https://stacks-node-api.mainnet.stacks.co/v2/transactions' \ 376 | --header 'Content-Type: application/json' \ 377 | --data '{ 378 | "tx":"00000000010400438b6d53879e7a9dd44f791488d97a3faf1df6ec000000000000000100000000000054c40001707bd733bac6db46e2c6dccda2fd00da2959cbe3c3d0237c62c2bc85ff231d056685498b5b6354636517625ebcd18fbcbc59746b133656471b2e74b43367f025030200000000000516a41f35635267e95ceef9c16eca79bf9313a265b900000000000493e0746865207765623320636f7572736500000000000000000000000000000000000000" 379 | }' 380 | ``` 381 | 382 | - 返回值 383 | 384 | ``` 385 | "ed917ad07e01653ea17644299f1ef4f2cfe6e46d887979d9ab7a6369ad883918" 386 | ``` 387 | 388 | # **五. 中心化钱包开发** 389 | 390 | ## **1. 离线地址生成** 391 | 392 | - 调度签名机生成密钥对,签名机吐出公钥 393 | - 使用公钥匙导出地址 394 | 395 | ## **2.充值逻辑** 396 | 397 | - 获得最新块高;更新到数据库 398 | - 从数据库中获取上次解析交易的块高做为起始块高,最新块高为截止块高,如果数据库中没有记录,说明需要从头开始扫,起始块高为 0; 399 | - 解析区块里面的交易,to 地址是系统内部的用户地址,说明用户充值,更新交易到数据库中,将交易的状态设置为待确认。 400 | - 所在块的交易过了确认位,将交易状态更新位充值成功并通知业务层。 401 | 402 | ## **3. 提现逻辑** 403 | 404 | - 获取离线签名需要的参数,给合适的手续费 405 | - 构建未签名的交易消息摘要,将消息摘要递给签名机签名 406 | - 构建完整的交易并进行序列化 407 | - 发送交易到区块链网络 408 | - 扫链获取到交易之后更新交易状态并上报业务层 409 | 410 | ## **4.归集逻辑** 411 | 412 | - 将用户地址上的资金转到归集地址,签名流程类似提现 413 | - 发送交易到区块链网络 414 | - 扫链获取到交易之后更新交易状态 415 | 416 | ## **5. 转冷逻辑** 417 | 418 | - 将热钱包地址上的资金转到冷钱包地址,签名流程类似提现 419 | - 发送交易到区块链网络 420 | - 扫链获取到交易之后更新交易状态 421 | 422 | ## **6.冷转热逻辑** 423 | 424 | - 手动操作转账到热钱包地址 425 | - 扫链获取到交易之后更新交易状态 426 | 427 | # **六 HD 钱包开发** 428 | 429 | ## **1.离线地生成和离线签名** 430 | 431 | 参考上面的代码 432 | 433 | ## **2.和链上交互的接口** 434 | 435 | - 获取账户余额 436 | - 获取 nonce 437 | - 根据地址获取交易记录 438 | - 获取预估手续费 439 | 440 | # **七. 总结** 441 | 442 | HD 钱包和交易所钱包不同之处有以下几点 443 | 444 | ## **1.密钥管理方式不同** 445 | 446 | - HD 钱包私钥在本地设备,私钥用户自己控制 447 | - 交易所钱包中心化服务器(CloadHSM, TEE 等),私钥项目方控制 448 | 449 | ## **2.资金存在方式不同** 450 | 451 | - HD 资金在用户钱包地址 452 | - 交易所钱包资金在交易所热钱包或者冷钱包里面, 用户提现的时候从交易所热钱包提取 453 | 454 | ## **3.业务逻辑不一致** 455 | 456 | - 中心化钱包:实时不断扫链更新交易数据和状态 457 | - HD 钱包:根据用户的操作通过请求接口实现业务逻辑 458 | 459 | # **八.附录** 460 | 461 | - Github: https://github.com/stacks-network 462 | 463 | - 官方文档: https://wdocs.stacks.co/ 464 | 465 | - 浏览器: https://explorer.hiro.so/ 466 | 467 | - Stackjs: https://github.com/stacksjs 468 | 469 | - 官方接口文档: 470 | 471 | https://docs.stacks.co/stacks-101/api 472 | 473 | https://docs.hiro.so/api/ --------------------------------------------------------------------------------