EOS 资源汇总 A curated list of EOS Ecosystem by SuperONE. EOS 资源汇总。提交 PR 参考 contributing，提交 issue 点击 此处。 中文 | English EOS 主网 超级节点 https://eos.live，EOS 竞选节点列表，by SuperONE https://www.imeos.one/block-producer，EOS 竞选节点列表，by IMEOS https://eosnodes.privex.io，privex.io 出品 GitHub https://github.com/EOSIO，官方源码 https://github.com/EOS-Mainnet，主网源码 主网启动相关 https://github.com/eoscanada/eos-bios，Orchestrator for a decentralized EOS.IO blockchain network boot, by EOS Canad Ghostbusters-Testnet，EOS Core-an EOS mainnet launch solution, by HKEOS and EOSRio https://github.com/winlin/eostoolkit，EOS 工具，EOSBixin 出品 https://github.com/EOSArgentina/firestarter，验证工具 by EOSArgentina https://github.com/eosnewyork/eospy，验证工具 by EOS NewYork https://scholar.eosnation.io，EOSIO Scholar Testnet https://bp.eosgo.io，EOS Block Producer Candidates 展示，EOS Go 出品 https://imeos.one/block-producer，EOS Block Producer Candidates 展示，IMEOS 出品 http://dev.cryptolions.io，EOS Jungle Testnet http://superhero3monitor.eosgreen.io，EOS Superhero Testnet https://github.com/eostea/EOS-Boot-Steps-dawn4，EOSIO - 主网启动演练 https://github.com/eosauthority/genesis，EOS Snapshot Generator eos-bp-nodes-security-checklist，EOS 超级节点安全执行指南，慢雾团队出品，致敬 eos-bp-nodes-security-checklist/audit.md，超级节点安全审计方案，慢雾团队出品，致敬 https://github.com/consenlabs/eos-bp-profile，EOS Block Producer Profile，imToken 收集 https://github.com/onechainone/eos-bp-profile，EOS Block Producer Profile，OneChain 收集 主网启动状态 https://eosnation.io/eos-launch-tracker，EOS Nation 出品 https://eoscountdown.com，EOS 倒计时工具 https://status.producer.vote，可以查看 EOS 主网启动流程图 映射查询 https://www.eoslaomao.com/eos/verify，EOSLaoMao 出品 https://eoscountdown.com，同时提供映射检查功能 区块浏览器 http://eostracker.io，较早开源的 EOS 区块链浏览器 GitHub 账户信息 区块信息 交易信息 Action 信息 超级节点 https://explorer.eoseco.com，EOS 生态区出品 账户信息 区块信息 交易信息 公钥信息 投票 发送交易 映射查询 RAM(内存) 价格 网络计算成本 计算资源成本 RAM(内存) 占用率 https://eosblock.co，EOSpace 出品 http://scaneos.io，ScanEOS 是全球首个专注于 EOS 生态的专业区块链浏览器和数据分析平台 账户信息 区块信息 交易信息 Action 信息 Token 信息 账号竞拍 投票进度 超级节点 http://eosflare.io，搜索账号、公钥、交易 账户信息 交易信息 公钥信息 账号竞拍 投票者排名 https://eospark.com，腾讯团队出品 账户信息 区块信息 交易信息 公钥信息 账号竞拍 投票进度 超级节点 https://m.eosnode.info，EOSTEA 出品 https://eosmonitor.io，同样是 EOSTEA 的产品 账户信息 区块信息 交易信息 Token 信息 超级节点 Ram 持有账号排名 (Top 200) http://eosnetworkmonitor.io，CryptoLions 出品 账户信息 区块信息 交易信息 超级节点 主网投票状态 http://eosportal.io/chain/12/producers，较早的投票工具 http://eos.host/mainnet，ONO 出品 http://eosnetworkmonitor.io，CryptoLions 出品 https://eosauthority.com/voting，EOS Authority 团队作品 https://explorer.eoseco.com/voting，实时观看投票最新排名，EOSEco 出品 https://eosquery.com，EOS Canada 出品 https://votetracker.eosmedi.com，EOS Voter Tracker，EOS Mediterranean 出品 EOS Block Producer Votes Summary，来自 dapptools.info 超级节点投票占比多样性统计，来自 dapptools.info 主网投票 https://github.com/EOSLaoMao/eos-local-voter，老猫团队出品的投票工具 https://github.com/EOSLaoMao/eos-local-voter-desktop，目前只有 Mac 客户端 https://github.com/EOSLaoMao/eos-full-node，cleos 命令行投票指引 http://eosportal.io，较早的投票工具 http://vote.libertyblock.io，LibertyBlock 作品 https://tokenika.github.io/secure-bp-voting，展示页有待优化 https://github.com/greymass/eos-voter，开源的投票工具 http://vote.liquideos.com，做得相对完善，开源，有 Mac 和 Windows 客户端 https://toolkit.genereos.io/tools，EOS Toolkit by GenerEOS https://github.com/genereos/eostoolkit，GitHub 源码 EOS 离线投票工具，EOSeco 出品 https://vote.eosasia.one，EOS Asia 出品 http://eosvoter.eosphere.io/vote，EOSPhere 出品 https://token.im/download，imToken 2.0 支持 EOS 超级节点投票 https://www.mytokenpocket.vip，Token Pocket 团队出品 http://app.onechain.one/appstart.html，ONEChain 出品 https://halowallet.io，EOS 引力区出品 https://bepal.pro/Download，Bepal 出品 https://eos.live，EOS LIVE 由 SuperONE 团队开发，支持 EOS 超级节点投票、委托投票、账号注册等功能，SuperONE 团队会于近期发布，尽请期待 https://github.com/eoscanada/eosc，eosc 是一个跨平台的命令行工具 (支持 Windows, Mac 和 Linux)，你可以通过这个工具实现与 EOS.IO 区块链的交互，EOS Canada 出品 https://github.com/eoscannon/EosCannon-Offline-Tools，EOS 离线投票，by EOS Cannon DevOps https://github.com/BlockMatrixNetwork/eos-bp-failover，EOS 出块节点切换脚本，by Block Matrix https://github.com/eossv/bp-monitor，通过 head_block_time 检测节点出块状态，支持 email 提醒，也可配置电话/短信/自定义提醒，by EOS Silicon Valley eos-claimer，获取奖励工具，by EOS Canada https://github.com/EOSpace/eos-sync，同步 EOS 区块链数据到 MongoDB 数据库，by EOSpace 文档 https://github.com/EOSIO/Documentation，EOSIO Documents TechnicalWhitePaper.md，技术白皮书 https://github.com/EOSIO/eos/wiki，官方 Wiki https://eosio.github.io/eos，EOS 官方文档 https://eos.readthedocs.io/zh_CN/latest，EOS.IO 中文文档 https://www.eosdocs.io，由 EOS New York 和 EOS Nation 维护 https://github.com/EOS-Mainnet/Community-Launch-Docs，Community-Launch-Docs，by EOS-Mainnet EOSIO Resources https://github.com/EOS-Nation/Awesome-EOS，by EOS Nation 开发 https://github.com/EOSIO/eosjs, Javascript eos-go，Go https://github.com/ProChain/SwiftyEOS，Swift https://hex.pm/packages/eosrpc，Elixir lang https://tbfleming.github.io/cib/eos.html，C++ -> WASM compiler https://github.com/OracleChain/EOSDevHelper，EOSDevHelper 是一个跨平台 PC 端 EOS 钱包工具，由 OracleChain 团队研发 http://eosfactory.io，基于 Python 的智能合约开发和测试框架（Win, Mac, Linux） https://fibos.io，为 EOS 而生的 JavaScript 工具链 https://developers.eos.io 概述 快速起步 进阶指南 构建 EOSIO 插件 智能合约 cleos keosd nodeos C/C++ API HTTP API https://github.com/EOS-BP-Developers/EOS-Test-Cave, EOS 校验自动化测试工具 安全 https://www.slowmist.com，专注区块链生态安全 https://peckshield.com, 派盾科技（PeckShield）由前 360 首席科学家、美国北卡州立大学终身教授蒋旭宪博士于 2018 年创办 Debunking Claimed Vulnerability in EOSIO Smart Contract System Prevent DDoS with AWS Network Load Balancer EOS Node Remote Code Execution Vulnerability Block.one one hackerone HOW TO GENERATE YOUR FALLBACK EOS KEY BASED ON YOUR ETHEREUM PRIVATE KEY 资源 社区 https://eosio.stackexchange.com，EOS.IO Stackexchange https://forums.eosgo.io，EOS Go - Blockchain Community https://forums.eosgo.io/categories/chinese，EOS Go 中文 https://forums.eosgo.io/categories/developers，EOS Developers https://www.reddit.com/r/EOSDev，EOS on Reddit https://eosfans.io，EOS 中文社区 https://eosforum.org，EOS Community Forum https://eos.live，EOS 社区首个产品与资讯聚合平台 https://www.eos.top，EOS 论坛 https://eostalk.io/forums, The EOS Community, beta version, powered by chainbb.com https://www.helloeos.one，HelloEOS 聚集了国内最强的石墨烯开发力量和最资深的社区参与者 http://www.eoschannel.com，EOS Channel，专注于 EOS 资讯与技术 https://www.huobipool.com/#!/community，火币矿池超级社区 资讯 https://imeos.one/app，EOS 生态资讯门户 https://www.jinse.com，金色财经 博客 https://medium.com/eosio，EOS 官方博客 https://medium.com/@bytemaster，BM 博客 http://blog.eosdata.io，EOS 区块链开发指南 https://medium.com/blockproducers，EOS 区块链矿工和出块节点背后的故事 https://bihu.com/people/9457，EOS Cannon 技术总监 Bean 币乎首页 https://bihu.com/people/5402，荆凯币乎首页 https://www.jianshu.com/u/29f865fe3598 http://www.cnblogs.com/Evsward，醒者呆博客 https://blog.eos42.io，EOS42 博客 https://medium.com/@thomas.cox_39839，Thomas Cox https://medium.com/@eosiosg，EOSIO.SG http://www.8btc.com/author/48198，极简主义 https://www.jianshu.com/u/a7e82863b3e2，Lochaiching https://bihu.com/people/484685, animaeos 空投 https://www.eosdrops.io，空投列表集合 https://imeos.one/airdrop，IMEOS 出品的空投列表 导航 https://index.eoscannon.io，EOS 导航，by EOS Cannon https://tp-lab.github.io/EOS-Navigator，EOS 导航，by TP-Lab 下载 https://github.com/EOSIO/eos/releases，EOSIO 官方版本 https://github.com/EOS-Mainnet/eos/releases，EOS-Mainnet 社区维护版本 EOS.IO Technical White Paper v2，EOS 白皮书 视频 火币 - EOS20 问系列 nsjames - YouTube CodingWithCrypto - YouTube Jackson Kelley - YouTube Building Distributed Apps With EOS.IO Blockchain - YouTube 工具 账号相关工具 https://github.com/webdigi/EOS-Offline-Private-key-check，EOS 离线公私钥配对检测工具 http://meet.one/eos.html，EOS Generate and Mapping，MEET.ONE 出品 https://nadejde.github.io/eos-token-sale，Key Gen https://eosfans.io/tools/generate，一键生成私钥/公钥，EOSTEA 出品 get-eoskey-from-ethkey，用你的以太坊私钥找回丢失的 EOS，访问 http://getfallbackeos.mytokenpocket.vip https://eostoolkit.io，GenerEOS 出品 Account/PublicKey 查询 创建账户 发送 EOS 抵押 EOS 获取带宽 / CPU 取消抵押 EOS 释放带宽 / CPU 退回抵押的 EOS 到账户 购买 RAM 出售 RAM 权限管理 投票 成为代理 设置代理 账号竞拍 BP 声明收益 https://www.myeoskit.com，EOS Asia 出品 Account/PublicKey/Tx 查询 创建账户 成为代理 设置代理 投票 账号竞拍 https://eosauthority.com/alerts，EOS 账号监控工具 https://github.com/breakstring/savemyeosram，释放掉发行的 EOS 空气代币所占用的内存 EOSRescuer，拯救高风险的 EOS 账户 浏览器插件 EOS KIT，EOS 工具，by MEET.ONE EOS Price Ticker，EOS 价格跟踪 其他工具 http://eosflare.io/account/eosio.names，查看短账号注册情况 https://github.com/tokenbankteam/eos-bp-profile-parser，EOS BP Profile 解析工具 https://github.com/EOSIO/patroneos，RPC Checkpoint for EOS nodes https://github.com/OracleChain/EOSBenchTool，EOS 性能测试工具 http://eosdemo.bec.com，EOS 钱包 DEMO 版，美图贝客钱包技术支持 https://github.com/activeeos/eos-helm，Kubernetes Helm charts for the EOS smart contracts platform https://github.com/eosdetroit/bp-playbook, Ansible playbook to dynamically deploy Dockerized nodeos https://github.com/eoseoul/eos-ramcost, Adjusting supply of a token and connector balance of bancor during final phase of boot RAM 价格趋势 RAM 价格计算器 RAM、带宽、CPU 价格 / 成本计算 RAM 价格、投票数、未支付区块数统计 EOS 主网 24 小时 / 7 天内 Action 数据统计 超级节点（BP）的世界分布 EOS 350 期众筹情况统计 EOS 事件日历 https://github.com/ukarlsson/eos-clion-example，使用 CLion 的示例代码 https://eos.feexplorer.io，EOS RAM 历史价格 eosDataConditionSearch https://github.com/wangweiX/eosram-trading-robot，EOS RAM 交易机器人 EOS 钱包 硬件钱包 https://bepal.pro/bepal-pro-s，Bepal Pro S 硬件钱包，首款支持 EOS 主网生态的硬件钱包（Bepal 轻钱包同步推出） https://bepal.pro/Download，冷钱包是冷热交互的，购买冷钱包的同时需要热钱包下载交互，当然同步推出的 EOS 轻钱包，只需下载 App 即可 https://cybex.io，Cybex 出品的 EOS 硬件钱包，宣传视频 点击此处 移动端 https://meet.one/pomelo，MeetOne 出品 http://halowallet.io，EOS 引力区出品 https://www.mytokenpocket.vip，Token Pocket 团队出品 https://token.im，知名以太坊钱包，支持 EOS 主网投票 http://www.medishares.org/wallet/cn，麦子钱包 http://bitpie.com，比特派，老牌比特币钱包 http://www.eosbixin.com，Bixin 出品 https://pocketeos.com，PocketEOS，OracleChain 出品 http://app.onechain.one，ONE，支持 EOS 超级节点投票、领取 EOS 全部糖果、支持 EOS 系所有代币 https://www.bitportal.io/zh-hans，币通，一款安全易用的 EOS 钱包，BitPortal 出品 http://www.unlimitedip.io，未来版权 Uni-Wallet，宣传视频 点击此处 http://eostoken.im，安卓版和 iOS 版均可下载 https://more.top，IMEOS 出品的 MORE 钱包 https://eos.live/download，EOS LIVE 由 SuperONE 团队开发，支持 EOS 超级节点投票、委托投票、账号注册、RAM 买卖等功能 https://cobo.com，Cobo Wallet，支持 EOS 账号注册 https://kkwallet.com，KKWallet，支持免费创建 EOS 账户，转账、EOS 主流合约代币、质押及 RAM 交易等功能 Web 钱包 https://eoswalletpro.com，Web 钱包 https://wallet.starteos.io，Starteos 出品 https://eostea.github.io/eos-wallet，EOSTEA 出品，VUE + JS，开源 浏览器插件钱包 https://scatter-eos.com，Chrome 插件钱包，类似 MetaMask 开源钱包 https://github.com/EOSPortal，GitHub 开源 https://github.com/EOSEssentials/EOSWallet，较早开源的钱包 https://github.com/espritblock/eos4j，GitHub 开源 https://github.com/eostoken/wallet，GitHub 开源 https://github.com/espritblock/react-native-eos，React https://github.com/OracleChain/PocketEOS-IOS，PocketEOS iOS 版 https://github.com/OracleChain/PocketEOS-Android，PocketEOS 安卓版 https://github.com/plactal/EosCommander，GitHub 开源 https://github.com/eostea/eos-wallet，EOSTEA 出品，VUE + JS，开源 交易所 中心化交易所 畅思，专注 EOS 生态的区块链资产精品交易平台 鲸交所，全球首个基于 EOS 主链开发、支持跨链的高流动性去中心化交易所 BigONE，BigONE 是一家数字资产交易及托管平台，帮助用户寻找全球优质区块链资产，提供便捷的交易服务，以顶尖的技术和安全控制流程保障用户数字资产安全，帮助用户进行资产管理。BigONE 团队汇集了来自世界各地的区块链专家、极客和爱好者 HADEX，火币自主数字资产交易所 EOSFINEX，全球首个高性能去中心化交易所 ZBG，Pos+Pow 交易挖矿，社区化自治的交易平台 去中心化交易所 Newdex，全球首家基于 EOS 的去中心化交易所，支持 SimpleWallet 协议 SouthEX，基于 EOS 的去中心化交易所 DEXEOS，全球首家基于 EOS 的去中心化交易所 Dapp Dapp 展示 https://eosindex.io，Directory of Projects based around EOS https://imeos.one，IMEOS 团队出品 https://eos.live，SuperONE 团队出品 DApp 生态 EOS LIVE，EOS 社区首个产品与资讯聚合平台 AWOO!，允许用户合作完成共建更好的社会，目标是带来和平，繁荣和丰富 AZARUS，一个公平，可靠，透明的平台，用于玩令牌和下注。专门针对视频游戏行业而构建。 Billionaire Token，致力于成为首个在 EOS 区块链网络上运行的博彩娱乐场。代币通缩机制，通过分布式抽奖和智能合约每周销毁 XBL 代币。 Cards & Tokens.，可收藏的数字卡平台，用户可以上传内容并赚钱 Carmel.io，开源教育平台，可帮助有抱负和经验丰富的软件开发人员通过 token 化同行挑战来增长他们的技能并跟踪他们的成长 Chaince，专注 EOS 生态的区块链资产精品交易平台，支持糖果空投，主打严选项目，不收上币费 Chintai，Chintai 是一个由 EOS 节点候选者 EOS42 团队推出的 EOS 代币租赁平台，EOS 持币者就像是那些持有商业房产的投资客，而开发者是需要租用场地办公定期付租金的公司，双方各取所需，盘活了整个地产市场，而 Chintai 则是不收中介费的链家，这是一门相当有前景的生意 DEOS Games，赌场的边缘，赌场的平台 emanate，艺术家之间合作的音乐技术平台。 凭借智能合约，emanate 允许艺术家通过合作获利 EOS Bet，赌博系统，投资者可以支持房屋并获得股息 EOXCommerceA，分散的电子商务平台，允许使用基于 EOS 的加密货币和加密资产 EOSfinex，著名的交易所 Bitfinex 开发一个高性能、去中心化的交易所 EOS Gems，EOS 区块链上的收藏宝石游戏 Everipedia，一个众包的 wiki 平台，在线百科全书和知识库 HorusPay，分散的全球薪资门户网站，允许公司与国际薪资供应商交换私密的加密数据 HireVibes，一个众包型的去中心化求职招聘平台，解决中心化招聘平台收费高匹配度低的痛点 Insights Network，基于 EOS 的数据交易市场，用户可以直接出售他们的数据而不需要通过第三方 Karma，KARMA 旨在激励用户在世界上做得很好，并接受 KARMA 帮助人类 IRYO，去中心化的医疗数据，病人可以控制谁可以使用它并可以随身携带 Lab Ledger, A social media, crowdfunding website for scientists. Onepay，帮助商家接收加密货币的支付钱包，传统的支付系统依靠中央银行和信贷机构，ONEPAY 的去中心化 POS 系统运行在 EOS 区块链上，只需付出一小部分成本即可闪电般交易 Oracle Chain，让世界与区块链互联 ONO，一个年轻人泛社交 App，定义了注意力价值，鼓励自由表达。目前社区已经初步形成 Plactal，Plactal 瞄准的是电子游戏市场。它通过收集每个玩家的信息做出分析，然后提供给游戏公司精准的广告投放信息 Scatter，用于保存身份和密钥安全的钱包。 项目的区块链部分是用于在应用程序和用户中建立信任 Sense，是一个 P2P 聊天机器人平台，便于任何两个用户通过各种不同的聊天工具实时对话，Sensay 利用人工智能匹配算法找出互相配对的用户 Seyara，一个开放的协议，以建立真正分散的点对点市场 SFEOS，玩家可以自己创建的，高沉浸式的体验游戏 Starteos，智能数字钱包，支持 EOS Tokena，首个基于 EOS 的去中心化交易所 Trade Stuf，一个建立在 EOS 区块链上的高效，去中心化教育生态系统 TXT，支付观众的去中心化广告经济。 旨在激励观众观看所有人都获利的广告 Uncloak，将自己描述为世界上第一个基于区块链技术的网络威胁解决方案，通过一个完全自动化的区块链系统来解决网络威胁的弱点 Unico，去中心化数字收藏 Zimbra X，下一代 Zimbra 电子邮件和协作平台 EOS 超级矿工，EOS Asia 孵化的一款游戏：EOS 超级旷工，不发币，直接使用 EOS，奖池的 90% 左右的 EOS 全部返还给玩家，排行榜动态设计，保证后来的玩家的公平，总之我玩了几天感觉上手容易，既公平，也有趣味性，很快就上线，预约地址 http://eosminer.vip 参考：EOS dApp Ecosystem. License This list is released into the public domain. SuperONE, robin 转载自 ：https://github.com/superoneio/awesome-eos
简介 本篇将为大家介绍eosio.msig的源码实现，合约代码库详见：eosio.msig。eosio.msig主要有propose、approve、unapprove、cancel、exec、invalidate这几种方法，下面会详细逐一介绍每种方法的功能和实现细节。 主要合约方法 eosio.msig合约，在eosio.msig.hpp头文件中，主要定义了以下六个合约方法： propose：提出提案 approve：通过提案 unapprove：不通过提案 cancel：取消提案 exec：执行提案 invalidate：撤回对之前所有该账户通过、但未被最终执行的提案的通过授权 eosio.msig合约头文件 namespace eosio { class multisig : public contract { public: multisig( account_name self ):contract(self){} void propose(); void approve( account_name proposer, name proposal_name, permission_level level ); void unapprove( account_name proposer, name proposal_name, permission_level level ); void cancel( account_name proposer, name proposal_name, account_name canceler ); void exec( account_name proposer, name proposal_name, account_name executer ); void invalidate( account_name account ); propose方法 propose方法主要功能是提出提案，对应上篇提到的 cleos multisig propose 命令，传参如下： proposer：提案账户 proposal_name：提案名 requested：提案通过所需权限 trx：提案具体执行的交易内容 为了节省资源开销，propose方法并不会根据 cleos multisig propose 传入的参数一一做解析，而是直接解析input data /* propose function manually parses input data (instead of taking parsed arguments from dispatcher) because parsing data in the dispatcher uses too much CPU in case if proposed transaction is big If we use dispatcher the function signature should be: void multisig::propose( account_name proposer, name proposal_name, vector<permission_level> requested, transaction trx) */ void multisig::propose() { constexpr size_t max_stack_buffer_size = 512; size_t size = action_data_size(); char* buffer = (char*)( max_stack_buffer_size < size ? malloc(size) : alloca(size) ); read_action_data( buffer, size ); 之后，会做一系列前置检验工作：proposer提案账户授权是否正确、交易是否超时、propose_name是否存在、提案通过所需权限是否正确等 require_auth( proposer ); eosio_assert( trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" ); //eosio_assert( trx_header.actions.size() > 0, "transaction must have at least one action" ); proposals proptable( _self, proposer ); eosio_assert( proptable.find( proposal_name ) == proptable.end(), "proposal with the same name exists" ); bytes packed_requested = pack(requested); auto res = ::check_transaction_authorization( buffer+trx_pos, size-trx_pos, (const char*)0, 0, packed_requested.data(), packed_requested.size() ); eosio_assert( res > 0, "transaction authorization failed" ); 之后，将提案和提案合约的内容存表，将提案通过所需的权限存入requested_approvals表中，我们上篇文章所提到 cleos get table eosio.msig <proposer account> approvals 命令查询的就是这张表 proptable.emplace( proposer, [&]( auto& prop ) { prop.proposal_name = proposal_name; prop.packed_transaction = bytes( buffer+trx_pos, buffer+size ); }); approvals apptable( _self, proposer ); apptable.emplace( proposer, [&]( auto& a ) { a.proposal_name = proposal_name; a.requested_approvals.reserve( requested.size() ); for ( auto& level : requested ) { a.requested_approvals.push_back( approval{ level, time_point{ microseconds{0} } } ); } }); approve方法 approve方法的主要功能是通过提案，对应上篇提到的 cleos multisig approve 命令，传参如下 proposer：提案人 proposal_name：提案名 permissions：使用哪个权限批准这个提案 首先，系统会查找提案合约内容，查找 requested_approvals 表中需要通过的权限中，是否有和传入permission匹配项。若有匹配项，将此权限加入 provided_approvals 表，即表示该权限通过此提案，并从 requested_approvals 表中移除该权限。 void multisig::approve( account_name proposer, name proposal_name, permission_level level ) { require_auth( level ); approvals apptable( _self, proposer ); auto apps_it = apptable.find( proposal_name ); if ( apps_it != apptable.end() ) { auto itr = std::find_if( apps_it->requested_approvals.begin(), apps_it->requested_approvals.end(), [&](const approval& a) { return a.level == level; } ); eosio_assert( itr != apps_it->requested_approvals.end(), "approval is not on the list of requested approvals" ); apptable.modify( apps_it, proposer, [&]( auto& a ) { a.provided_approvals.push_back( approval{ level, current_time_point() } ); a.requested_approvals.erase( itr ); }); } else { old_approvals old_apptable( _self, proposer ); auto& apps = old_apptable.get( proposal_name, "proposal not found" ); auto itr = std::find( apps.requested_approvals.begin(), apps.requested_approvals.end(), level ); eosio_assert( itr != apps.requested_approvals.end(), "approval is not on the list of requested approvals" ); old_apptable.modify( apps, proposer, [&]( auto& a ) { a.provided_approvals.push_back( level ); a.requested_approvals.erase( itr ); }); } } unapprove方法 unapprove方法的主要功能是不通过提案，对应上篇提到的 cleos multisig unapprove 命令，传参如下： proposer：提案人 proposal_name：提案名 permissions：使用哪个权限拒绝这个提案 首先，系统会查找提案合约内容，查找 provided_approvals 表中通过的权限中，是否有和传入permission匹配项。若有匹配项，将此权限加入requested_approvals 表，即表示该权限还没通过此提案，并从 provided_approvals 表中移除该权限。 void multisig::unapprove( account_name proposer, name proposal_name, permission_level level ) { require_auth( level ); approvals apptable( _self, proposer ); auto apps_it = apptable.find( proposal_name ); if ( apps_it != apptable.end() ) { auto itr = std::find_if( apps_it->provided_approvals.begin(), apps_it->provided_approvals.end(), [&](const approval& a) { return a.level == level; } ); eosio_assert( itr != apps_it->provided_approvals.end(), "no approval previously granted" ); apptable.modify( apps_it, proposer, [&]( auto& a ) { a.requested_approvals.push_back( approval{ level, current_time_point() } ); a.provided_approvals.erase( itr ); }); } else { old_approvals old_apptable( _self, proposer ); auto& apps = old_apptable.get( proposal_name, "proposal not found" ); auto itr = std::find( apps.provided_approvals.begin(), apps.provided_approvals.end(), level ); eosio_assert( itr != apps.provided_approvals.end(), "no approval previously granted" ); old_apptable.modify( apps, proposer, [&]( auto& a ) { a.requested_approvals.push_back( level ); a.provided_approvals.erase( itr ); }); } } cancel方法 cancel方法的主要功能是取消提案，对应上篇提到的 cleos multisig cancel 命令，传参如下： proposer：提案账户 proposal_name：提案名 canceler：取消账户 首先，先查找表获取提案内容。如果canceler账户和提案账户不同，则在提案交易过期之前，canceler都不能取消提案。若能取消，将提案从表中移除。 void multisig::cancel( account_name proposer, name proposal_name, account_name canceler ) { require_auth( canceler ); proposals proptable( _self, proposer ); auto& prop = proptable.get( proposal_name, "proposal not found" ); if( canceler != proposer ) { eosio_assert( unpack<transaction_header>( prop.packed_transaction ).expiration < eosio::time_point_sec(current_time_point()), "cannot cancel until expiration" ); } proptable.erase(prop); //remove from new table approvals apptable( _self, proposer ); auto apps_it = apptable.find( proposal_name ); if ( apps_it != apptable.end() ) { apptable.erase(apps_it); } else { old_approvals old_apptable( _self, proposer ); auto apps_it = old_apptable.find( proposal_name ); eosio_assert( apps_it != old_apptable.end(), "proposal not found" ); old_apptable.erase(apps_it); } } exec方法 exec方法的主要功能是执行提案，对应上篇提到的 cleos multisig exec 命令，传参如下： proposer：提案账户 proposal_name：提案名 executer：执行账户 void multisig::exec( account_name proposer, name proposal_name, account_name executer ) { require_auth( executer ); proposals proptable( _self, proposer ); auto& prop = proptable.get( proposal_name, "proposal not found" ); transaction_header trx_header; datastream<const char*> ds( prop.packed_transaction.data(), prop.packed_transaction.size() ); ds >> trx_header; //首先，需要做前置检查，检查交易是否过期 eosio_assert( trx_header.expiration >= eosio::time_point_sec(current_time_point()), "transaction expired" ); approvals apptable( _self, proposer ); auto apps_it = apptable.find( proposal_name ); vector<permission_level> approvals; //然后，查 provided_approvals 表获取通过提案交易的权限们，对比 inv_table 表，如果权限不在 inv_table 表中或者 last_invalidation_time //已经小于当前时间，代表权限有效，放入approvals表中。inv_table 表的用途在下一个invalidate方法中介绍 invalidations inv_table( _self, _self ); if ( apps_it != apptable.end() ) { approvals.reserve( apps_it->provided_approvals.size() ); for ( auto& p : apps_it->provided_approvals ) { auto it = inv_table.find( p.level.actor ); if ( it == inv_table.end() || it->last_invalidation_time < p.time ) { approvals.push_back(p.level); } } apptable.erase(apps_it); } else { old_approvals old_apptable( _self, proposer ); auto& apps = old_apptable.get( proposal_name, "proposal not found" ); for ( auto& level : apps.provided_approvals ) { auto it = inv_table.find( level.actor ); if ( it == inv_table.end() ) { approvals.push_back( level ); } } old_apptable.erase(apps); } //最后，执行提案。如果交易执行权限检验无误，会发起一个defer延迟合约，去执行提案交易。如果执行成功， //`cleos get actions <executer account>` 会产生两条actions，一条是exec的交易，一条是提案执行的交易。 bytes packed_provided_approvals = pack(approvals); auto res = ::check_transaction_authorization( prop.packed_transaction.data(), prop.packed_transaction.size(), (const char*)0, 0, packed_provided_approvals.data(), packed_provided_approvals.size() ); eosio_assert( res > 0, "transaction authorization failed" ); send_deferred( (uint128_t(proposer) << 64) | proposal_name, executer, prop.packed_transaction.data(), prop.packed_transaction.size() ); proptable.erase(prop); } invalidate方法 invalidate方法的主要功能是：如果account之前通过的提案还未执行，就可以使用该方法将提案一键设置为无效。这个方法主要是解决：账户权限变更时，之前通过但未执行的提案一旦执行会盗取账户权限的问题，详见issue。该方法传参如下： account：提案的批准账户 该功能的实现非常简单，首先，inv_table 是用来存放权限的，它的两个字段 account 和last_invalidation_time 分别是账户名和账户权限最近失效时间。last_invalidation_time 时间之前，account的提案批准权限都不可用，在该时间之后account的提案批准权限才能生效。 因此，如果想使account之前审批通过的所有提案都失效的话，就将 last_invalidation_time 设置为当前时间即可。exec方法在执行之前会检查 inv_table，则包含在 inv_table 中的account，即便批准了该提案，该批准也会作废 void multisig::invalidate( account_name account ) { require_auth( account ); invalidations inv_table( _self, _self ); auto it = inv_table.find( account ); if ( it == inv_table.end() ) { inv_table.emplace( account, [&](auto& i) { i.account = account; i.last_invalidation_time = current_time_point(); }); } else { inv_table.modify( it, account, [&](auto& i) { i.last_invalidation_time = current_time_point(); }); } } 转载自简书
简介 本篇将为大家介绍eos另一个系统合约eos.msig的主要功能和源码实现细节。eos.msig是eos的提案合约，同样也是cleos multisig命令调用的系统合约，可用于提案、通过/不通提案、执行多重签名交易等功能。由于涉及内容较多，介绍将分为上下两篇，上篇将围绕eos账户权限、cleos multisig命令的使用这几个方面进行介绍，下篇则会为大家介绍eosio.msig的源码实现 Eos账户权限 回顾一下之前的文章，一个账户最基本的权限owner和active是由公私钥对控制的，然而，eos丰富的权限控制方式，还允许我们将一个账户的权限，下放给其他的账户。比如下面的经典例子： 账户popo的权限情况 permissio account weight threshold owner 2 @user1 1 @user2 1 active 1 @user1 1 @user2 1 我们将一步一步，通过命令行操作的方式来为大家介绍eos权限的应用。 1.我们分别创建三个账户popo11111111、zoin11111111、zoin22222222，每个账户使用不同的公私钥对。 分别创建6对公私钥，分别用于三个账号的owner和active权限 cleos create key --to-console | 账号 | 权限|公钥 |私钥 | | ------------ | ------------ | ------------ | | popo11111111 | owner | EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB | 5JAxr3bM6DwwtcKJ7aVydpoaSA2oyiZ94PjqTpW6uXDZ41VuK22| | popo11111111 | active | EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T |5KRCeqsHAx1UNTtz7BH5N4SSMe75XMCj3j93TqQRDrYCwbjccoP | | zoin11111111 | owner | EOS5N3qMPXjtwZpMyPeiYXSmBQbr7Lt5Ro2W8pLqyAzFYQPknoUvZ |5KWYcdZcJ1E8mWtg1Qoq5s1fYXJxMQkLupozfZEUEiu6vn6QNzx | | zoin11111111 | active | EOS6BAfK8mZZjMaa74zxkT3evWEtWo4AnRQYmnpGWkNJYKVjcrpao | 5Jwy6VbCwpkmczjYgMNVtr9Nd77aenZcPE8DtvPwtYqztbgMzu4| | zoin22222222 | owner | EOS6hBy5yioHpcH4SxSe91aH8vCGAhhEifKtp1jZEy7PS2rhJFSER |5Hz3u44Avpf4NCVDQdwZGSVXpbpEdRFczo2Wm9FC14pqcx8mUwM | | zoin22222222 | active | EOS5TeY2C9bdewrbS58SZnrp5azF5Cq7AXraNJihxaW4hbGeWSaeE |5JFoGdAc6zPG9KdHCHPHFL5XbpEnFmDq61y53Bw3Hm3VuFy7Stk | 2.下面开始创建三个账号 cleos system newaccount 已有账号 将要创建的账号 ownerkey activekey --stake-net '0.1 EOS' --stake-cpu '0.1 EOS' --buy-ram-kbytes 4000 利用我们上面文章《以传统的WEB开发方式，来举例理解Dapp开发》中已经在麒麟测试网创建的账号cryptokylinq，并在麒麟测试网来创建其他账号。 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com system newaccount cryptokylinq popo11111111 EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T --stake-net '0.1 EOS' --stake-cpu '0.1 EOS' --buy-ram-kbytes 4000 executed transaction: ada14edaa2dc356f02f8f19515f3227669887a5a840601d28f8d59238e1c7890 336 bytes 4284 us # eosio <= eosio::newaccount {"creator":"cryptokylinq","name":"popo11111111","owner":{"threshold":1,"keys":[{"key":"EOS81WjiHefR6... # eosio <= eosio::buyrambytes {"payer":"cryptokylinq","receiver":"popo11111111","bytes":4096000} # eosio.token <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8497 EOS","memo":"buy ram"} # cryptokylinq <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8497 EOS","memo":"buy ram"} # eosio.ram <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8497 EOS","memo":"buy ram"} # eosio.token <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5018 EOS","memo":"ram fee"} # cryptokylinq <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5018 EOS","memo":"ram fee"} # eosio.ramfee <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5018 EOS","memo":"ram fee"} # eosio <= eosio::delegatebw {"from":"cryptokylinq","receiver":"popo11111111","stake_net_quantity":"0.1000 EOS","stake_cpu_quanti... # eosio.token <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"} # cryptokylinq <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"} # eosio.stake <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"} 2018-09-25T04:07:44.999 thread-0 main.cpp:458 print_result warning: transaction executed locally, but may not be confirmed by the network yet suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com system newaccount cryptokylinq zoin11111111 EOS5N3qMPXjtwZpMyPeiYXSmBQbr7Lt5Ro2W8pLqyAzFYQPknoUvZ EOS6BAfK8mZZjMaa74zxkT3evWEtWo4AnRQYmnpGWkNJYKVjcrpao --stake-net '0.1 EOS' --stake-cpu '0.1 EOS' --buy-ram-kbytes 4000 executed transaction: daf4b102f68d7161022c0ac8635d91137a0d2d24072d49fdad13a608e2e03072 336 bytes 5102 us # eosio <= eosio::newaccount {"creator":"cryptokylinq","name":"zoin11111111","owner":{"threshold":1,"keys":[{"key":"EOS5N3qMPXjtw... # eosio <= eosio::buyrambytes {"payer":"cryptokylinq","receiver":"zoin11111111","bytes":4096000} # eosio.token <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8589 EOS","memo":"buy ram"} # cryptokylinq <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8589 EOS","memo":"buy ram"} # eosio.ram <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8589 EOS","memo":"buy ram"} # eosio.token <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"} # cryptokylinq <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"} # eosio.ramfee <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"} # eosio <= eosio::delegatebw {"from":"cryptokylinq","receiver":"zoin11111111","stake_net_quantity":"0.1000 EOS","stake_cpu_quanti... # eosio.token <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"} # cryptokylinq <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"} # eosio.stake <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"} 2018-09-25T04:11:22.091 thread-0 main.cpp:458 print_result warning: transaction executed locally, but may not be confirmed by the network yet suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com system newaccount cryptokylinq zoin22222222 EOS6hBy5yioHpcH4SxSe91aH8vCGAhhEifKtp1jZEy7PS2rhJFSER EOS5TeY2C9bdewrbS58SZnrp5azF5Cq7AXraNJihxaW4hbGeWSaeE --stake-net '0.1 EOS' --stake-cpu '0.1 EOS' --buy-ram-kbytes 4000 executed transaction: 7b184ccebaeb2cd2b6cece49ec46abd86b20a1d20b29722d21029dd1abc958f0 336 bytes 2667 us # eosio <= eosio::newaccount {"creator":"cryptokylinq","name":"zoin22222222","owner":{"threshold":1,"keys":[{"key":"EOS6hBy5yioHp... # eosio <= eosio::buyrambytes {"payer":"cryptokylinq","receiver":"zoin22222222","bytes":4096000} # eosio.token <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8723 EOS","memo":"buy ram"} # cryptokylinq <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8723 EOS","memo":"buy ram"} # eosio.ram <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ram","quantity":"99.8723 EOS","memo":"buy ram"} # eosio.token <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"} # cryptokylinq <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"} # eosio.ramfee <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.ramfee","quantity":"0.5019 EOS","memo":"ram fee"} # eosio <= eosio::delegatebw {"from":"cryptokylinq","receiver":"zoin22222222","stake_net_quantity":"0.1000 EOS","stake_cpu_quanti... # eosio.token <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"} # cryptokylinq <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"} # eosio.stake <= eosio.token::transfer {"from":"cryptokylinq","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"} 2018-09-25T04:13:52.457 thread-0 main.cpp:458 print_result warning: transaction executed locally, but may not be confirmed by the network yet 3.将popo11111111的owner权限交出，由zoin11111111和zoin22222222的owner权限控制。每个权限的权重weight是1，而门槛threshold是2，即使用popo11111111的owner权限发送交易，需要zoin11111111和zoin22222222进行多重签名才行 先将popo11111111的owner私钥导入钱包 suroudeMacBook-Pro:~ surou$ cleos wallet import private key: imported private key for: EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB 查看下popo11111111信息 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account popo11111111 permissions: owner 1: 1 EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB active 1: 1 EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T 将popo11111111的owner权限交出 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com set account permission popo11111111 owner '{"threshold":2, "keys":[{"key": "EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB"}], "accounts": [{"permission": {"actor": "zoin11111111", "permission": "owner"}, "weight": 1}, {"permission": {"actor": "zoin22222222", "permission": "owner"}, "weight": 1}]}' -p popo11111111@owner executed transaction: 38317f76ad1eb90601ad9bd2c6720e322196aebbb8bed5afacfd4f7c5098b111 200 bytes 759 us # eosio <= eosio::updateauth {"account":"popo11111111","permission":"owner","parent":"","auth":{"threshold":2,"keys":[{"key":"EOS... 再次查看popo11111111账户 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account popo11111111 permissions: owner 2: 60450 EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB1 zoin11111111@owner, 1 zoin22222222@owner, active 1: 1 EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T 已将权限owner转交给了 zoin11111111@owner, 1 zoin22222222@owner 4.将popo11111111的active权限交出，由zoin11111111和zoin22222222的active权限控制。每个权限的权重weight是1，而门槛threshold是1，即使用popo11111111的active权限发送交易，zoin11111111和zoin22222222任意一个账户签名即可 先将popo11111111的owner私钥导入钱包 suroudeMacBook-Pro:~ surou$ cleos wallet import private key: imported private key for: EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T 将popo11111111的active权限交出 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com set account permission popo11111111 active '{"threshold":1, "keys":[{"key": "EOS6L7pr3AaKekTs1dbratDq1PutoSdmBWJFwbLcStsnKBbJtNUws"}], "accounts": [{"permission": {"actor": "zoin11111111", "permission": "active"}, "weight": 1}, {"permission": {"actor": "zoin22222222", "permission": "active"}, "weight": 1}]}' -p popo11111111@active executed transaction: c51dede340204a6561b9ad511efcf1f194075319a97f54e7fce1dbee8b20eb18 200 bytes 748 us # eosio <= eosio::updateauth {"account":"popo11111111","permission":"active","parent":"owner","auth":{"threshold":1,"keys":[{"key... 再次查看popo11111111账户 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account popo11111111 permissions: owner 2: 60450 EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB1 zoin11111111@owner, 1 zoin22222222@owner, active 1: 31778 EOS6L7pr3AaKekTs1dbratDq1PutoSdmBWJFwbLcStsnKBbJtNUws1 zoin11111111@active, 1 zoin22222222@active, 已经权限active转交给1 zoin11111111@active, 1 zoin22222222@active cleos multisig命令 EOS账户这一节，我们将popo11111111账户的owner和active权限交给了zoin11111111和zoin22222222账户，则popo11111111账户原来的公私钥对就失效了，交易的发送需要zoin11111111和zoin22222222账户授权。 额外提一句，主网启动后，超级节点们会将eosio账户的权限交给eosio.prods账户，而eosio.prods账户是由21个超级节点的账户联合控制的，需要15个节点联合签名才能使用eosio账户，可以避免拿管理员权限作恶的现象 主网eosio账户权限suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account eosio privileged: true permissions: owner 1: 1 eosio.prods@active, active 1: 1 eosio.prods@active, 主网eosio.prods账户权限suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get account eosio.prods permissions: owner 1: active 15: 1 acryptotitan@active, 1 alohaeostest@active, 1 blockmatrix2@active, 1 eosargentina@active, 1 eosasia11111@active, 1 eosbeijingbp@active, 1 eoscanadacom@active, 1 eosecoeoseco@active, 1 eoshuobipool@active, 1 eosiomeetone@active, 1 eosiosg11111@active, 1 eoslaomaocom@active, 1 eosnodeonebp@active, 1 eospaceioeos@active, 1 eosriobrazil@active, 1 eosstorebest@active, 1 eossv12eossv@active, 1 eosswedenorg@active, 1 eoszb1111111@active, 1 helloeoschbp@active, 1 superoneiobp@active, prod.major 11: 1 acryptotitan@active, 1 alohaeostest@active, 1 blockmatrix2@active, 1 eosargentina@active, 1 eosasia11111@active, 1 eosbeijingbp@active, 1 eoscanadacom@active, 1 eosecoeoseco@active, 1 eoshuobipool@active, 1 eosiomeetone@active, 1 eosiosg11111@active, 1 eoslaomaocom@active, 1 eosnodeonebp@active, 1 eospaceioeos@active, 1 eosriobrazil@active, 1 eosstorebest@active, 1 eossv12eossv@active, 1 eosswedenorg@active, 1 eoszb1111111@active, 1 helloeoschbp@active, 1 superoneiobp@active, prod.minor 8: 1 acryptotitan@active, 1 alohaeostest@active, 1 blockmatrix2@active, 1 eosargentina@active, 1 eosasia11111@active, 1 eosbeijingbp@active, 1 eoscanadacom@active, 1 eosecoeoseco@active, 1 eoshuobipool@active, 1 eosiomeetone@active, 1 eosiosg11111@active, 1 eoslaomaocom@active, 1 eosnodeonebp@active, 1 eospaceioeos@active, 1 eosriobrazil@active, 1 eosstorebest@active, 1 eossv12eossv@active, 1 eosswedenorg@active, 1 eoszb1111111@active, 1 helloeoschbp@active, 1 superoneiobp@active, 下面，我们就要介绍cleos multisig命令如何调用eosio.msig合约进行提案、发送多重签名的交易。 1.zoin11111111发起一个转账提案，提案名是transferpopo，将popo11111111账户中的10EOS转给cryptokylinq账户，这之前需要保证popo11111111账户有币。 先给popo11111111转入点EOS cleos -u http://kylin.fn.eosbixin.com push action eosio.token transfer '["cryptokylinq", "popo11111111","10.0000 EOS","test"]' -p cryptokylinq 再导入zoin11111111的active的私钥 suroudeMacBook-Pro:~ surou$ cleos wallet import private key: imported private key for: EOS6BAfK8mZZjMaa74zxkT3evWEtWo4AnRQYmnpGWkNJYKVjcrpao 发起提案 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig propose transferpopo '[{"actor": "zoin11111111", "permission": "active"}, {"actor": "zoin22222222", "permission": "active"}]' '[{"actor": "popo11111111", "permission":"active"}]' eosio.token transfer '{"from": "popo11111111", "to": "cryptokylinq", "quantity":"10.0000 EOS", "memo": "test multisig"}' -p zoin11111111@active executed transaction: 83cde06310cd6cd74dfb180aa38fa4f0a5d950b6f39a4f963237a5ea6d2c39aa 240 bytes 810 us # eosio.msig <= eosio.msig::propose {"proposer":"zoin11111111","proposal_name":"transferpopo","requested":[{"actor":"zoin11111111","perm... 详细分析下cleos multisig propose命令参数 - proposal_name：提案名 requested_permissions：提案审批通过需要的权限，这里需要zoin11111111和zoin22222222的active权限 trx_permission：提案执行需要的权限，需要popo11111111的active权限就能发起转账 contract：提案调用的合约账户，转账使用eosio.token账户合约 action：提案调用的合约方法，转账使用transfer方法 data：具体数据 proposer：提案发起人，zoin11111111发起 - proposal_expiration：提案的有效时间 2.查看提案交易cleos multisig review <proposer> <proposal_name> suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig review zoin11111111 transferpopo { "proposal_name": "transferpopo", "packed_transaction": "4224ab5b000000000000000000000100a6823403ea3055000000572d3ccdcd011042082184402bad00000000a8ed32322e1042082184402bad60a78b1ed25cfd45a08601000000000004454f53000000000d74657374206d756c746973696700", "transaction": { "expiration": "2018-09-26T06:16:34", "ref_block_num": 0, "ref_block_prefix": 0, "max_net_usage_words": 0, "max_cpu_usage_ms": 0, "delay_sec": 0, "context_free_actions": [], "actions": [{ "account": "eosio.token", "name": "transfer", "authorization": [{ "actor": "popo11111111", "permission": "active" } ], "data": { "from": "popo11111111", "to": "cryptokylinq", "quantity": "10.0000 EOS", "memo": "test multisig" }, "hex_data": "1042082184402bad60a78b1ed25cfd45a08601000000000004454f53000000000d74657374206d756c7469736967" } ], "transaction_extensions": [] } } 3.查看提案审批情况，provided_approvals为空表示尚未审批，request_approvals表示需要哪些权限进行审批 cleos get table eosio.msig <proposer> approvals suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get table eosio.msig zoin11111111 approvals { "rows": [{ "proposal_name": "transferpopo", "requested_approvals": [{ "actor": "zoin11111111", "permission": "active" },{ "actor": "zoin22222222", "permission": "active" } ], "provided_approvals": [] } ], "more": false } 4.通过提案 cleos multisig approve 提案人 提案 权限 zoin11111111通过提案suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig approve zoin11111111 transferpopo '{"actor": "zoin11111111", "permission": "active"}' -p zoin11111111@active executed transaction: bcc0495b88d7b897853af11ace59029a32f16f1727de04e4c1fa7fc85e7df1dc 128 bytes 1091 us # eosio.msig <= eosio.msig::approve {"proposer":"zoin11111111","proposal_name":"transferpopo","level":{"actor":"zoin11111111","permissio... 查看提案审批情况 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get table eosio.msig zoin11111111 approvals { "rows": [{ "proposal_name": "transferpopo", "requested_approvals": [{ "actor": "zoin22222222", "permission": "active" } ], "provided_approvals": [{ "actor": "zoin11111111", "permission": "active" } ] } ], "more": false } zoin22222222通过提案suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig approve zoin11111111 transferpopo '{"actor": "zoin22222222", "permission": "active"}' -p zoin22222222@active executed transaction: 62ef04a0ee345ac604ca9fe7be034a0cb69524b1ed146e22c44a806a03337919 128 bytes 633 us # eosio.msig <= eosio.msig::approve {"proposer":"zoin11111111","proposal_name":"transferpopo","level":{"actor":"zoin22222222","permissio... 查看提案审批情况 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get table eosio.msig zoin11111111 approvals { "rows": [{ "proposal_name": "transferpopo", "requested_approvals": [], "provided_approvals": [{ "actor": "zoin11111111", "permission": "active" },{ "actor": "zoin22222222", "permission": "active" } ] } ], "more": false } 5.执行提案cleos multisig exec <proposer> <proposal_name> -p 谁想执行都可以 执行前查看账户 cryptokylinq 余额4,148.3135 EOS popo11111111 余额 10 EOS suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com multisig exec zoin11111111 transferpopo -p zoin11111111 executed transaction: caaa900536463207713f6af031ee315125d993f0af56ea91aafff3dcdf8b1353 160 bytes 1098 us # eosio.msig <= eosio.msig::exec {"proposer":"zoin11111111","proposal_name":"transferpopo","executer":"zoin11111111"} 执行完成后查看账户 cryptokylinq 余额4,158.3135 EOS popo11111111 余额 0 EOS 且提案将被自动删除 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com get table eosio.msig zoin11111111 approvals { "rows": [], "more": false } 并注意到，该笔转账没有交易记录actions 额，是不是在某些情况，需要隐藏交易记录时，可以通过此方法。。。 使用场景 某个账户发起一个提案后，需事先明确指定哪些账户/权限，必须全部通过审核后，合约动作才能被执行。 常见问题 提案操作很消耗CPU Error 3080004: Transaction exceeded the current CPU usage limit imposed on the transaction cleos -u http://kylin.fn.eosbixin.com system delegatebw cryptokylinq zoin11111111 "0 EOS" "10 EOS" 执行提案时，可能出现下面未知错误 需要部署eosio.system系统合约，并开启提案合约的功能即可解决（具体原理会在介绍eos.system合约的时候介绍） cleos push action eosio setpriv '["eosio.msig", 1]' -p eosio 后记 本篇为大家介绍了eos的账户权限，以及eosio.msig提案合约如何通过cleos multisig调用使用，如何发起提案、审批提案等。别走开，下一篇将深入源码，分析eosio.msig提案合约是怎么写的。 参考自简书
Error 3050003: eosio_assert_message assertion failure
创建账号时Error 3050003 suroudeMacBook-Pro:~ surou$ cleos -u http://kylin.fn.eosbixin.com system newaccount cryptokylinq popo EOS81WjiHefR6c5VKYjvdSat68RDJ3qGPgCckJqG3pXbDbVjZR5QB EOS7ob62mdBfiRt1a8tQrQGbhnU1gEAvWyQDFHs99HzYEh8wxeQ9T --stake-net '0.1 EOS' --stake-cpu '0.1 EOS' --buy-ram-kbytes 4000 Error 3050003: eosio_assert_message assertion failure 如果nodeos启动时开始错误显示 nodeos --verbose-http-errors Error 3050003: eosio_assert_message assertion failure Error Details: assertion failure with message: no active bid for name 根据Details就能看出，由于创建的账户名不属于常规的账户名“1-5，a-z”12位。是需要事先投标购买成功的。
我们知道目前EOS DApp运行，目前分为两种方式 App提供DApp运行环境。App内置浏览器控件，注入js，DApp运行时用注入的js与App内置的钱包交互，DApp需要私钥签名时，将需要签名的数据通过注入的js将数据传给外部的钱包，使用钱包当前选中的私钥将数据签名，然后将数据返回给DApp。 浏览器扩展提供支持。目前支持EOS链的扩展主要是以Scatter，或者基于此二次开发为主。 本篇我们主要讲下DApp对Scatter的接入，大多的App提供的运行环境也是支持Scatter的，比如tp钱包等，并且Scatter在EOS链的绝对地位（不低于Metamask对于以太坊），所以我们开发DApp最少要支持Scatter的支持。 先来个主角Scatter介绍 Scatter 是一个去中心化的签名、身份、私人数据与身份验证解决方案。它在用户的计算机本地运行，采用非对称加密技术，令用户可以在不访问集中式服务器的情况下通过网站进行身份验证。Scatter 主要包含两个部分：钱包与 RIDL (Reputation & Identity Layer) 系统。 Scatter 钱包目前支持 EOS 和 ETH 钱包，未来会支持更多钱包。Scatter 已推出 Chrome 浏览器插件版本和桌面 Beta 版本，之后会推出移动端版本，其中浏览器插件版与以太坊轻钱包 MetaMask 类似。Scatter 钱包作为 EOS 应用程序与用户信息之间的桥梁，能让用户安全地向应用程序签名和提供私人数据，并保证在与EOS 区块链通信的 Web 应用程序进行交互时不会暴露密钥和其他不必要的信息。Scatter 不需要用户提交私钥，只需对交易签名，它是本地的应用，不会给用户发送需要解密或加密私钥的请求。而用户如果使用直接手动将私钥输入到应用程序的网页表单中，会存在资产被盗取的严重风险。 Scatter 的 RIDL (Reputation & Identity Layer) 系统包含了声望与身份系统。用户可以创建多个身份，每个身份都包含用户身份名称、EOS 账户/密钥对、姓名、电话和地址等个人信息。用户只需设置一次身份， Dapp 就可以经过用户授权后从 Scatter 接入用户身份信息。因此用户不必在多个 Dapp 重复填写个人信息，并避免个人信息泄漏。而在 Scatter 声望系统当中，用户和 Dapp 可以互相对彼此的声望进行评分。这一系统允许应用程序评估用户做出某种行为的可能性，比如偿还贷款。反之，它也允许用户衡量应用程序的质量，例如安全性与可用性。 Scatter 项目代码 现在Scatter 分为ScatterWebExtension（浏览器扩展）和ScatterDesktop（桌面客户端）两个版本。 ScatterWebExtension https://github.com/GetScatter/ScatterWebExtension ScatterDesktop https://github.com/GetScatter/ScatterDesktop 目前浏览器版本已经逐步废弃，主推桌面版本。 编写一个支持Scatter的DApp例子 //TODO ScatterDesktop使用步骤，并与上面例子交互 //TODO 中秋快乐，稍后继续完善本文。节后会紧跟几篇，详细针对Scatter接口方面的讲解。
version返回版本信息 返回EOS客户端版本信息 $cleos version client Build version: cc9decff create在区块链上和区域外创建数据 create key 创建密钥 命令格式：$ cleos create key 为了创建一个帐户，你需要两个新的密钥：所有者和激活密钥。你可以要求cleos为你创建一些钥匙： 这将是你的Owner Key， $ cleos create key --to-console Private key: 5HtimtY36YN8mB5MRKh2FDkdtUg98dZ5HrnC9hhCkf5KsZvXR11 Public key: EOS86qf6z2CuNfYDADMMysy2Gfs6p3GVK5k71BbGovmtmkBwvMxPq 再次执行，生成active key $ cleos create key --to-console Private key: 5K4SVxGkSrUWJVSsxtyJx7nqwVoNwgEZ77KxwVXaB2yzCf2ri3q Public key: EOS5aC4xP3rf1sJVfNudJaa4CikUpmAYqXuiCxfL8nQT3tFDys9X1 注意：cleos不会把生成的私钥自动导入钱包，需手动导入cleos wallet import create account 创建账号 命令格式：$ cleos create account ${control_account} ${account_name} ${owner_public_key} ${active_public_key} Usage: ./cleos create account [OPTIONS] creator name OwnerKey ActiveKey Positionals: creator TEXT 创建新帐户的帐户的名称 name TEXT 新帐户的名称 OwnerKey TEXT 新帐户的所有者公钥 ActiveKey TEXT 新帐户的激活公钥 Options: -x,--expiration 在交易到期之前以秒为单位设置时间，默认值为30s。 -f,--force-unique 强制使交易独一无二，这将消耗额外的带宽，并消除对多次发布同一交易的任何保护。 -s,--skip-sign 指定未锁定钱包密钥是否用于签署交易。即跳过签署直接交易。 -d,--dont-broadcast 不要向网络广播交易（只需打印到stdout）。 -p,--permission TEXT ... 授权的帐户和权限级别，如“帐户@权限”(默认值'creator@active') 待续
怎么给超级节点发工资：EOS的奖励分配规则
最近几篇文章，持续在聊EOS的资源分配模型。今天节后上班第一天，我们来聊个五块钱的天，看看EOS节点的收入问题：收益有多少，是怎么计算出的。 当下一次有人问你，EOS的通胀比率是多少，节点的奖励比例是多少，怎么分配的时候，也许这篇文章可以帮你回答这些问题。 主要用到的网站： https://eos.host/mainnet， 可以直观的查看节点的投票和出块情况 https://votetracker.eosmedi.com/ 可以看到每个节点的投票者的信息 -https://eosflare.io EOS网络浏览器 超级节点/备用节点/BP是什么？ 在许多文章里面你会看到BP这一缩写。这是Block Producer的简称，即出块节点。 实际上，超级节点，这是中文里面才会有的称呼，似乎不加上超级，就显得不够霸气似的。英文语境里面，只是称呼EOS的节点为BP(Block Producer)。 节点分为两类：出块节点，跟备用节点(Block Producer Candidate)。在EOS主网之中，现在是设定为前21个节点为出块节点，其他的节点为备用节点。 备用节点也有另外的名称，叫做候选节点，两个名字都是说的同一回事。 你如果愿意，也可以调用EOS的命令，将自己启动的节点注册为节点，只是，未必有收益罢了。 节点收益知多少？ 通过https://eos.host/mainnet这一网络，可以非常直观的查看各个节点的收益情况。 上图列出了前30个节点的得票率占比，橙色为出块节点，灰色为备用节点。 21个出块节点的收益 通过第二张图，可以看到，有一栏是列出来了节点的预估收益。 我们再仔细看一下。排名第一的佳能节点(恭喜佳能！)，总计出块34895，预估得到收益为： 1,248.86 EOS 备用节点的收益 除了出块节点之外，也有部分备用节点是有收益的。从图中可以看出来，排名59的备选节点hexlantttttt，预计也能够得到100多的EOS作为回报。 你可能会好奇：出块节点的收益跟备用节点的收益是怎么算的呢？我们从EOS的增发开始说起。 算算节点的收益分配 分配的流程 每次有节点发起指令要领取奖励的时候，系统会计算： 新增发的EOS总数有多少； 按照比例，将新增发的EOS分配到不同的奖励池之中(下文会详述)； 按照节点的实际情况，计算节点应该得到的收益，并分配这部分收益给节点； 对于备用节点来说，如果领取的奖励不足100个EOS，则无法领取奖励。 简单概括就是，先计算各部分奖励池应该分配多少EOS，即新注入多少EOS；然后，根据规则，分配这部分新增的奖励，分配给对应的人节点。 EOS通胀率为5% EOS是温和通胀设计的系统，每年增发的EOS比率为5%左右，按照总供应量10亿来计算，就是五千万EOS。 而节点得到的奖励，只是通胀的一小部分，只是总EOS数量的1%。 新增EOS的用途 实际上，EOS的增发，是持续进行的，而并非是每年一次性新增加EOS。每次EOS的节点发起领取奖励的命令，就会计算新增EOS的量。 通胀的EOS，有两个用途： 通胀EOS的20%, 用于给节点的奖励，即，如果新增了五千万EOS，那么会有1千万EOS分配给节点作为奖励，包括给出块节点和备用节点； 通胀EOS的80%， 用于EOS基金池，这部分会在未来用于Worker Proposal的奖励发放。所谓的Worker Proposal，是指的社区福利应用或者对EOS系统有所帮助的项目。 Worker Proposal这部分的奖励，现在已经开始累积，但是没有发放。 后续会部署新的智能合约到系统之中，经过持票人投票选出来认为对社区有益的DAPP，来获得这部分奖金。 可以看得出来，EOS设计之中对于生态的重视，Worker Proposal(可以称为：工作提案？)这部分每年足足有四千万左右的EOS奖励。(此处进行了简化，实际上随着EOS通胀，奖励的EOS数量会越来越多，比率为总流通量的4%) 如果你觉得自己的创意够牛逼，对于推动社区进步有贡献，那后续，光凭藉Worker Proposal的这部分奖励，应该就足够你赚到钱的了。 出块节点和备用节点的收益 具体的节点奖励的这1%，分为两部分：出块奖励(0.25%)和得票奖励(0.75%)。 出块奖励只是分配给出块的前21个节点，而得票奖励呢，则是用作所有节点(包括出块节点和备用节点)的奖励。 出块奖励占据EOS总量的0.25%，这部分前21个节点平分。 假设总量是10亿，那么，出块奖励部分，则是两百五十万 EOS，这部分21个节点平分。 得票奖励， 占0.75%，这部分由所有节点按照投票权重占比来分配的。 例如，在某个时刻，如果未分配的得票奖励部分是一万2千个EOS，那么，假设 EOS Cannon节点的投票权重比例为10%，则佳能可以分得10%的奖励，即两千五百个EOS。 另外，佳能作为出块节点，还可以因为出块得到奖励。 假设未分配的出块奖励为两千一百个EOS，那么，佳能得到的奖励就是一百个，因为出块奖励是21个节点平分的。 总结 如果某个节点是前21个节点之一，那么，其奖励分为：出块奖励部分 + 得票奖励部分； 如果某个节点是备用节点，那么，它可能得到的收益属于得票奖励之中的一部分，比如总共投票权重为100，该节点所得到的投票权重为5，则可以分到得票奖励的5%。 如果某个备用节点的得票奖励不足100个EOS，则无法得到奖励。 转载自：https://www.jianshu.com/p/8fb3531d055c
EOS每年增发5%，1/5作为节点奖励，剩下4/5居然都给了它？
一、EOS通胀及奖励怎么分？ 众所周知，EOS采取每年增发5%代币的通胀模式。这是因为EOS采用了DPOS共识机制，而且没有任何交易手续费（明面上的），所以它通过增发代币的方式来鼓励节点提供计算资源和记账。 但实际上，这些增发的EOS并不完全用于节点奖励，而是分为两部分，其中节点奖励占五分之一，即1%的EOS增发量；剩下的归WPS提案基金（WPF），约4%。 其中节点（包括超级节点和备用节点）奖励的四分之一，即0.25%的EOS增发量为超级节点的出块奖励，剩下的0.75%为所有节点的得票率奖励。备用节点则无出块奖励，只有得票率奖励且要求在100个EOS以上，否则便不算备用节点，无法领取奖励。 而归提案基金（WPF）所有的4%的增发奖励则服务于WPS（工作者提案系统），它就是为了奖励给对社区有益的工作提案，旨在成为社区促进创新的资金来源，以帮助发展生态系统。 二、WPS新提案流程 通过上面的介绍，我们大致了解了WPS是什么。那么在WPS中，我们又该如何将提案提交公众批准呢？（内心OS：其实我就是冲着瓜分4%的增发奖励去的） 具体流程是这样的：EOS持有者发起提案，上链，经由紧急事务委员会进行紧急事务标准评估，满足标准就进行社区投票。如果投票不通过，根据反馈意见重新修改提交，如果投票通过，则获得第一阶段的基金分配 需要提醒的是，这个流程中提到的紧急事务委员会，是属于第一阶段公投时候所产生的，所处理的事务为紧急类别的事务，目前紧急类别的事务包括：Github存储库管理、安全测试、Bug赏金计划以及WPS监督委员会。 三、WPS备受争议 从WPS的设计上看，它将激励社区开发人去提供有效和可拓展的改进所需的补丁和升级，使得EOS.IO能够满足社区不断变化的需求。它无疑将填补目前EOS社区治理的部分空白，对于EOS生态发展起着一定促进作用。 虽然WPS有着诸多优点，但我们注意到这样一个问题，即4%作为提案基金是不是太高了。EOS增发的4%，这可是一笔数额不菲的资金，WPS允许将这部分资金用于由社区批准的项目，如果使用得当固然是幸事一件；但如果没用好，对EOS系统就可能会是一场打击。因此，BM也于前几周在电报群中表达了对选民投票积极性以及对WPS正常运转的忧虑，认为应该取消。 虽然BM这一观点得到了一部分社区成员的认同，但我个人认为：WPS的存在是值得肯定和支持的，但4%这个比例有点高，可以改为一年0.5-1.5%的比例进行试验，然后在试验中进行调整优化。 另外，核心WPS工作组也于上周发布了声明，表示当前核心工作组正在努力构建一套工具和一套拟议的规则和指南，以实现WPS的设计目标。同时，他们将会在9月至10月，执行第一次公投。第一次公投将会决定是否同意将100万EOS通证从eosio.saving帐户转移到新帐户eosio.wps，用做第一阶段的基金，用于紧急事务。（更多内容可查看：https://bihu.com/article/1163451） 而在第一阶段之后，将会逐渐替换掉紧急基金会，而且如下图所示，会有监管、社区、基础设施等更多其他类别的提案产生。 所以，现在对WPS盖棺定论还为时尚早，不如让WPS“再飞一会”，待第一次公投后再看它，说不定它会成长为EOS生态中的另一个奇迹。 转载自：https://www.jianshu.com/p/4ad8e49910a9
eosjs简明使用手册（最新版） 作者：古千峰 2018-7-15 以下教程用于EOS正式版，已在DEV开发链和EOS1.0.5-1.0.7版本的主链上测试通过。 官方文档因为版本的原因，内容多有错误，仅供参考。 eosjs的API与http-RPC调用一致，如rpc使用get-block，则在eosjs中使用getBlock()方法。即去横岗，首字符大写 eosjs几个容易出错的地方 asset类型，表达方式为字符串，如：100.0000 EOS，千万注意小数点后四位，少一位都不行 与链连接时需要指定chainId，可以通过cleos get info获得 1- 安装eosjs eosjs用于对交易签名、交易等操作。 eosjs-api用于读取链上数据，只读，如果只需要读取链上数据的话，只需要使用eosjs-api。 在nodejs中安装： npm install eosjs npm install eosjs-api 注意nodejs需要最新版，如安装过程发生错误，请用node -v和npm -v查看版本。 2- 建立eosjs与链的连接 Eos = require('eosjs') // Optional configuration.. config = { keyProvider: ['PrivateKeys...'], // 配置私钥字符串 httpEndpoint: 'http://178.62.196.196:8888', //DEV开发链url与端口 chainId: "0b08e71a2f8caaccc2dc13244b788f5eba29462ecd5d5dea1ad8cbe9581e885a", // 通过cleos get info可以获取chainId mockTransactions: () => null, // 如果要广播，需要设为null transactionHeaders: (expireInSeconds, callback) => { callback(null/*error*/, headers) //手动设置交易记录头，该方法中的callback回调函数每次交易都会被调用 }, expireInSeconds: 60, broadcast: true, debug: false, sign: true, authorization: null // 该参数用于在多签名情况下，识别签名帐号与权限,格式如：account@permission } eos = Eos(config) 3- 建立eosjs-api与链的连接 如果加载了eosjs后，默认加载eosjs-api，无需单独链接eosjs-api与链 EosApi = require('eosjs-api') // everything is optional options = { httpEndpoint: 'http://178.62.196.196:8888', verbose: false, // API logging logger: { // Default logging functions //log: config.verbose ? console.log : '', error: console.error }, fetchConfiguration: {} } eos = EosApi(options) 4- 获取帮助 不添加如何参数，可以获取该方法的使用说明，如： eos.getBlock() 5- 获取链上最新出块信息 eos.getInfo({}).then(result => { console.log(result) }) 返回： { server_version: 'b195012b', chain_id: '0b08e71a2f8caaccc2dc13244b788f5eba29462ecd5d5dea1ad8cbe9581e885a', head_block_num: 209307, last_irreversible_block_num: 209267, last_irreversible_block_id: '00033173a9ccd4bdd60a92d257e9354023b0457b134797be472a236cd908bc31', head_block_id: '0003319ba8ddc60d80c3cd0c7a70695cfd951f51ace9a798c913384cfbae659c', head_block_time: '2018-07-15T01:51:07.500', head_block_producer: 'eoshackathon', virtual_block_cpu_limit: 100000000, virtual_block_net_limit: 1048576000, block_cpu_limit: 99900, block_net_limit: 1048576 } 如果需要拿到某一个数据值，比如：head_block_producer，则使用： eos.getInfo({}).then(result => { console.log(result.head_block_producer) //以对象属性方式获取head_block_producer }) 6- 获取指定区块信息 eos.getBlock(200000).then(result => { console.log(result) }) //获取第200000个区块 或者： eos.getBlock({block_num_or_id: 200000}).then(result => { console.log(result) }) //获取第200000个区块 或者： eos.getBlock('00030d4011a6744857533a6e6d907037a94c27a2dc006b4d28125f76bed2b355').then(result => { console.log(result) }) //根据id获取区块 或者： eos.getBlock({block_num_or_id: '00030d4011a6744857533a6e6d907037a94c27a2dc006b4d28125f76bed2b355'}).then(result => { console.log(result) }) //根据id获取区块 或者将返回值传到回调函数callback中处理： callback = (err, res) => { err ? console.error(err) : console.log(res) } eos.getBlock(200000, callback) 7- 获取账户余额 eos.getCurrencyBalance({ code: "eosio.token", account: "eosio", symbol: "DEV" }).then(result => console.log(result)) 以上命令相当于 cleos get currency balance eosio.token eosio DEV 8- 获取某代币的信息 eos.getCurrencyStats({code: "eosio.token", symbol: "DEV"}, callback) //用上面出现的callback，下同 9- 获取账户信息 eos.getAccount({account_name: "eoshackathon"}, callback) //获取eoshackathon账户的信息 相当于： cleos get account eoshackathon 返回： { account_name: 'eoshackathon', head_block_num: 219997, head_block_time: '2018-07-15T03:20:12.500', privileged: false, last_code_update: '1970-01-01T00:00:00.000', created: '2018-07-13T20:54:28.000', ram_quota: 8148, net_weight: 500000, cpu_weight: 500000, net_limit: { used: 145, available: 120795486, max: 120795631 }, cpu_limit: { used: 1511, available: 11518458, max: 11519969 }, ram_usage: 3414, permissions: [ { perm_name: 'active', parent: 'owner', required_auth: [Object] }, { perm_name: 'owner', parent: '', required_auth: [Object] } ], total_resources: { owner: 'eoshackathon', net_weight: '50.0000 DEV', cpu_weight: '50.0000 DEV', ram_bytes: 8148 }, self_delegated_bandwidth: null, refund_request: null, voter_info: null } 10- 获取智能合约代码 eos.getCode({ account_name: "eosio"}, callback) 获取eosio账户的所有合约代码 11- 获取智能合约ABI eos.getAbi({ account_name: "eosio"}, callback) 12- 获取Table行数据 不成熟 13- 获取账户的Actions列表 eos.getActions({account_name: "eoshackathon", pos: 0, offset: 15}, callback) //pos和offset是指：从第pos条记录开始获取offset条Actions 14- 获取公钥对应的账户 eos.getKeyAccounts({ public_key: 公钥字符串}, callback) 如果查找到帐号，则返回[]，如果该公钥没有对应帐号，则报错。 相当于： cleos get accounts 公钥 15- 获取主账号控制的其他帐号 eos.getControlledAccounts({ controlling_account: "eoshackathon"}, callback) 16- 获取transaction交易细节 该指令有bug，慎用！ eos.getTransaction({id: "xxxx"}, callback) 17- 转账交易 首先，在链接配置config中，keyProvider: [发送方私钥] 其次，可以设置options参数如下： options = { authorization: '发送方帐号@active', broadcast: true, sign: true } 方式1： eos.transfer('发送方帐号', '接收方帐号', '0.3000 DEV','memo') 如果需要对返回值处理： eos.transfer('发送方帐号', '接收方帐号', '0.3000 DEV','memo', callback) 如果有options参数，则： eos.transfer('发送方帐号', '接收方帐号', '0.3000 DEV','memo', options, callback) 方式2： 使用对象： eos.transfer({ from: '发送方帐号', to: '接收方帐号', quantity: '0.1000 DEV', memo: '备注', callback }) 如果不想广播交易，可以使用以下简便指令： eos.transfer('发送方帐号', '接收方帐号', '0.3000 DEV','memo', false) //在最后加上false，不广播 方式3： 使用eos.transaction，构造对象执行 eos.transaction( { // ...headers, actions: [ { account: 'eosio.token', name: 'transfer', authorization: [{ actor: '发送方帐号', permission: 'active' }], data: { from: '发送方帐号', to: '接收方帐号', quantity: '0.3000 DEV', memo: '备注' } } ] } // options -- example: {broadcast: false} ) 以上命令与以下cleos相同： cleos push action eosio.token transfer '[ "发送方帐号", "接收方帐号", "0.3000 DEV", "备注" ]' -p 发送方帐号 18- 新建帐号 creatoraccount = "testtesttest" //主账号 newaccount = "testtest1113" //新账号 newaccount_pubkey = "EOS5LUYdLZAd3uHF5XGAeE61aTeSXWqvqxBSUq3uhqYH7kY15Drjf" //新账号的公钥 //构建transaction对象 eos.transaction(tr => { //新建账号 tr.newaccount({ creator: creatoraccount, name: newaccount, owner: newaccount_pubkey, active: newaccount_pubkey }) //为新账号充值RAM tr.buyrambytes({ payer: creatoraccount, receiver: newaccount, bytes: 8192 }) //为新账号抵押CPU和NET资源 tr.delegatebw({ from: creatoraccount, receiver: newaccount, stake_net_quantity: '1.0000 DEV', stake_cpu_quantity: '1.0000 DEV', transfer: 0 }) }) 19- 购买RAM creatoraccount = "testtesttest" //主账号 newaccount = "testtest1113" //新账号 eos.transaction(tr => { tr.buyrambytes({ payer: creatoraccount, receiver: newaccount, bytes: 8192 }) 或者 eos.transaction(tr => { tr.buyram({ payer: creatoraccount, receiver: newaccount, quant: 8 //以k为单位的内存，8k=8192字节 }) 20- 出售RAM eos.transaction(tr => { tr.sellram({ account: 'testtesttest', bytes: 1024 //出售1k内存 }) }) 21- 竞拍账号名 eos.transaction(tr => { tr.bidname ({ bidder: "testtesttest", newname: "竞拍的名字", bid: 价格 }) }) 22- 抵押CPU和NET eos.transaction(tr => { tr.delegatebw({ from: "testtesttest", receiver: "testtesttest", //testtesttest账户为自己抵押 stake_net_quantity: "1.0000 DEV", stake_cpu_quantity: "1.0000 DEV", transfer: 0 }) }) 23- 取消抵押（赎回）CPU和NET eos.transaction(tr => { tr.undelegatebw({ from: "testtesttest", receiver: "testtesttest", unstake_net_quantity: "0.1000 DEV", //赎回0.1个DEV unstake_cpu_quantity: "0.1000 DEV" }) }) 24- 智能合约部署 如果是加载wasm合约，不用使用binaryen，如果加载wast合约，需要安装并使用binaryen，如下： npm install binaryen@39.0.0 并用以下方式导入到js binaryen = require('binaryen') eos = Eos({keyProvider, binaryen}) 部署合约 以官方自带的hello合约为例 fs = require('fs') wasm = fs.readFileSync(`contracts/hello/hello.wasm`) //这个文件在客户端上，而不是在服务器上 abi = fs.readFileSync(`contracts/hello/hello.abi`) eos.setcode('contract_name', 0, 0, wasm) // contract_name 为合约名 eos.setabi('contract_name', JSON.parse(abi)) // @returns {Promise} 25- 智能合约的执行 方法一： eos.contract("contract_name").then(hello => { //hello随便起的变量名 hello.hi('axay', { //hi是方法名, 'axay'是该hello合约hi方法的参数 authorization: ['testtesttest'] //testtesttest是建立该合约的用户 }).then(result => { console.log(result); }); }); 方法二： eos.transaction( { actions: [ { account: 'contract_name', //合约名 name: 'hi', //方法名，该方法在官方的hello合约中有 authorization: [{ actor: 'testtesttest', permission: 'active' }], data: { user: 'axay' } } ] } // options -- example: {broadcast: false} ).then(result => console.log(result)) 26- 通过eosjs发行一个代币 发行代币有两种方式，一种是通过cleos，参考这里，但前提是必须要安装好EOS系统。 另一种方式是通过eosjs，无需在本机安装EOS系统。 注意： 此操作需要以eosio.token账户进行操作，因此需将eosio.token帐号的私钥导入到keyProvider数组中。但如果出于安全原因，不允许将eosio.token账户私钥加到程序中的话，则可以由发币的用户先部署eosio.token合约，然后再做接下去的操作。 第一步：创建代币 eos.transaction( { // ...headers, actions: [ { account: 'eosio.token', //合约名 name: 'create', //调用创建代币的方法 authorization: [{ actor: 'eosio.token', //必须是eosio.token permission: 'active' }], data: { issuer: 'testtesttest', //代币发行方 maximum_supply: '10000.0000 AAA', //代币总量与名称 can_freeze: 0, can_recall: 0, can_whitelist: 0 } } ] } // options -- example: {broadcast: false} ).then(result => console.log(result)) 第二步：发行代币 eos.transaction( { // ...headers, actions: [ { account: 'eosio.token', //合约名 name: 'issue', //调用发行代币的方法 authorization: [{ actor: 'testtesttest', //必须是代币的发行方 permission: 'active' }], data: { to: 'testtesttest', //收到代币的帐号 quantity: '1000.0000 AAA', memo: "testtesttest issue 1000 AAA" } } ] } // options -- example: {broadcast: false} ).then(result => console.log(result)) 也可以将以上两步合在一起操作，如： eos.transaction( { // ...headers, actions: [ { account: 'eosio.token', //合约 name: 'create', //方法 authorization: [{ actor: 'eosio.token', permission: 'active' }], data: { issuer: 'testtesttest', maximum_supply: '10000000.0000 GAT', can_freeze: 0, can_recall: 0, can_whitelist: 0 } }, { account: 'eosio.token', //合约 name: 'issue', //方法 authorization: [{ actor: 'testtesttest', permission: 'active' }], data: { to: 'testtest1111', quantity: '1000.0000 GAT', memo: "testtesttest issue 1000 GAT to testtest1111" } } ] } // options -- example: {broadcast: false} ).then(result => console.log(result)) https://github.com/eoshackathon/eos_dapp_development_cn/edit/master/docs/eosjs_manual.md
EOS 智能合约最佳安全开发指南 这篇文档旨在为 EOS 智能合约开发人员提供一些智能合约的安全准则及已知漏洞分析。我们邀请社区对该文档提出修改或完善建议，欢迎各种合并请求(Pull Request)。若有相关的文章或博客的发表，也请将其加入到参考文献中。 目录 安全准则 已知漏洞 数值溢出 漏洞示例 防御方法 真实案例 权限校验 漏洞示例 防御方法 真实案例 apply 校验 漏洞示例 防御方法 真实案例 参考文献 致谢 安全准则 EOS 处于早期阶段并且有很强的实验性质。因此，随着新的 bug 和安全漏洞被发现，新的功能不断被开发出来，其面临的安全威胁也是不断变化的。这篇文章对于开发人员编写安全的智能合约来说只是个开始。 开发智能合约需要一个全新的工程思维，它不同于我们以往项目的开发。因为它犯错的代价是巨大的，很难像中心化类型的软件那样，打上补丁就可以弥补损失。就像直接给硬件编程或金融服务类软件开发，相比于 Web 开发和移动开发都有更大的挑战。因此，仅仅防范已知的漏洞是不够的，还需要学习新的开发理念： 对可能的错误有所准备。任何有意义的智能合约或多或少都存在错误，因此你的代码必须能够正确的处理出现的 bug 和漏洞。需始终保证以下规则： 当智能合约出现错误时，停止合约 管理账户的资金风险，如限制（转账）速率、最大（转账）额度 有效的途径来进行 bug 修复和功能提升 谨慎发布智能合约。 尽量在正式发布智能合约之前发现并修复可能的 bug。 对智能合约进行彻底的测试，并在任何新的攻击手法被发现后及时的测试（包括已经发布的合约） 从 alpha 版本在麒麟测试网(CryptoKylin-Testnet)上发布开始便邀请专业安全审计机构进行审计，并提供漏洞赏金计划(Bug Bounty) 阶段性发布，每个阶段都提供足够的测试 保持智能合约的简洁。复杂会增加出错的风险。 确保智能合约逻辑简洁 确保合约和函数模块化 使用已经被广泛使用的合约或工具（比如，不要自己写一个随机数生成器） 条件允许的话，清晰明了比性能更重要 只在你系统的去中心化部分使用区块链 保持更新。通过公开资源来确保获取到最新的安全进展。 在任何新的漏洞被发现时检查你的智能合约 尽可能快的将使用到的库或者工具更新到最新 使用最新的安全技术 清楚区块链的特性。尽管你先前所拥有的编程经验同样适用于智能合约开发，但这里仍然有些陷阱你需要留意： require_recipient(account_name name) 可触发通知，调用name合约中的同名函数，官方文档 已知漏洞 数值溢出 在进行算术运算时，未进行边界检查可能导致数值上下溢，引起智能合约用户资产受损。 漏洞示例 存在缺陷的代码：batchTransfer 批量转账 typedef struct acnts { account_name name0; account_name name1; account_name name2; account_name name3; } account_names; void transfer(symbol_name symbol, account_name from, account_names to, uint64_t balance) { require_auth(from); account fromaccount; require_recipient(from); require_recipient(to.name0); require_recipient(to.name1); require_recipient(to.name2); require_recipient(to.name3); eosio_assert(is_balance_within_range(balance), "invalid balance"); eosio_assert(balance > 0, "must transfer positive balance"); uint64_t amount = balance * 4; //乘法溢出 int itr = db_find_i64(_self, symbol, N(table), from); eosio_assert(itr >= 0, "Sub-- wrong name"); db_get_i64(itr, &fromaccount, (account)); eosio_assert(fromaccount.balance >= amount, "overdrawn balance"); sub_balance(symbol, from, amount); add_balance(symbol, to.name0, balance); add_balance(symbol, to.name1, balance); add_balance(symbol, to.name2, balance); add_balance(symbol, to.name3, balance); } 防御方法 尽可能使用 asset 结构体进行运算，而不是把 balance 提取出来进行运算。 真实案例 【EOS Fomo3D你千万别玩】狼人杀遭到溢出攻击, 已经凉凉 权限校验 在进行相关操作时，应严格判断函数入参和实际调用者是否一致，使用require_auth进行校验。 漏洞示例 存在缺陷的代码：transfer 转账 void token::transfer( account_name from, account_name to, asset quantity, string memo ) { eosio_assert( from != to, "cannot transfer to self" ); eosio_assert( is_account( to ), "to account does not exist"); auto sym = quantity.symbol.name(); stats statstable( _self, sym ); const auto& st = statstable.get( sym ); require_recipient( from ); require_recipient( to ); eosio_assert( quantity.is_valid(), "invalid quantity" ); eosio_assert( quantity.amount > 0, "must transfer positive quantity" ); eosio_assert( quantity.symbol == st.supply.symbol, "symbol precision mismatch" ); eosio_assert( memo.size() <= 256, "memo has more than 256 bytes" ); auto payer = has_auth( to ) ? to : from; sub_balance( from, quantity ); add_balance( to, quantity, payer ); } 防御方法 使用require_auth( from )校验资产转出账户与调用账户是否一致。 真实案例 暂无 apply 校验 在处理合约调用时，应确保每个 action 与 code 均满足关联要求。 漏洞示例 存在缺陷的代码： // extend from EOSIO_ABI #define EOSIO_ABI_EX( TYPE, MEMBERS ) \ extern "C" { \ void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \ auto self = receiver; \ if( action == N(onerror)) { \ /* onerror is only valid if it is for the "eosio" code account and authorized by "eosio"'s "active permission */ \ eosio_assert(code == N(eosio), "onerror action's are only valid from the \"eosio\" system account"); \ } \ if( code == self || code == N(eosio.token) || action == N(onerror) ) { \ TYPE thiscontract( self ); \ switch( action ) { \ EOSIO_API( TYPE, MEMBERS ) \ } \ /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \ } \ } \ } EOSIO_ABI_EX(eosio::charity, (hi)(transfer)) 防御方法 使用 if( ((code == self && action != N(transfer) ) || (code == N(eosio.token) && action == N(transfer)) || action == N(onerror)) ) { } 绑定每个关键 action 与 code 是否满足要求，避免异常调用。 真实案例 EOSBet 黑客攻击事件复盘 参考文献 保管好私钥就安全了吗？注意隐藏在EOS DAPP中的安全隐患 漏洞详解|恶意 EOS 合约存在吞噬用户 RAM 的安全风险 How EOSBET attacked by aabbccddeefg BET被黑客攻击始末，实锤还原作案现场和攻击手段 累计薅走数百万，EOS Dapps已成黑客提款机？ 致谢 麒麟工作组 eosiofans 荆凯(EOS42) 星魂 岛娘 赵余(EOSLaoMao) 字符