分类 科普知识 下的文章 - 六币之门
首页
视频教程
网站导航
活动日历
关于我们
用户投稿
推荐
新闻动态
搜 索
1
融资周报 | 公开融资事件11起;加密技术公司Toposware完成500万美元融资,Polygon联创参投
116 阅读
2
六币日报 | 九只比特币ETF在6天内积累了9.5万枚BTC;贝莱德决定停止推出XRP现货ETF计划
79 阅读
3
六币日报 | 美国SEC再次推迟对灰度以太坊期货ETF做出决定;Do Kwon已出黑山监狱等待引渡
76 阅读
4
融资周报 | 公开融资事件27起;L1区块链Monad Labs完成2.25亿美元融资,Paradigm领投
76 阅读
5
【ETH钱包开发06】查询某个地址的交易记录
64 阅读
新闻动态
每日快报
一周精选
融资情况
项目投研
自治组织
数字藏品
去中心化应用
去中心化游戏
去中心化社交
去中心化金融
区块链交易所
科普知识
小白入门
用户手册
开发文档
行业报告
技术前沿
登录
搜 索
标签搜索
新闻
日报
元歌Eden
累计撰写
1,087
篇文章
累计收到
0
条评论
首页
栏目
新闻动态
每日快报
一周精选
融资情况
项目投研
自治组织
数字藏品
去中心化应用
去中心化游戏
去中心化社交
去中心化金融
区块链交易所
科普知识
小白入门
用户手册
开发文档
行业报告
技术前沿
页面
视频教程
网站导航
活动日历
关于我们
用户投稿
推荐
新闻动态
用户登录
登录
找到
227
篇与
科普知识
相关的结果
2023-03-06
区块链转账是怎么收费的?|白话区块链入门245
友情提示:入门连载已经 200 多期了,大白建议新入门的小伙伴们可以去文末扫描二维码,进入科普目录从 0 开始学习噢!作者 | 听风出品|白话区块链(ID:hellobtc)早年银行间转账都是收手续费的,一般按照转账金额的一定比例收取。而跨国转账,由于货币国与国之间的壁垒及外汇管制等,除了支付以上手续费和支付200元左右的电报费,另外还耗时几个工作日。然而,这些壁垒和管制在区块链的世界里都不复存在。你只需要轻轻发送一个转账请求,基本能够实现一小时内到账,那么区块链转账手续费是如何收取的呢? 01 比特币网络转账最早的比特币网络转账是按字节收费的(BTC矿工费=Fees(聪/byte) * Site(byte))。比特币转账是从一个比特币地址转移到另一个比特币地址,在实际交易过程中,如果A要转账给B,包含这笔交易的区块向区块链中的所有用户发布广播,矿工平均每隔10分钟会将比特币网络中未被记账的交易打包进一个区块,这就完成了一次确认,经过6次确认后,这笔转账就被认为不能逆转了。所以这里转账的手续费实际上是支付给矿工记账的报酬。一笔交易(不分金额)是250字节甚至更多,手续费一般为0.0001-0.0015个比特币。而比特币一个区块的容量就1M,一个区块每秒大约只能处理7笔交易,所以比特币上的交易是非常拥堵缓慢的,为了获得优先记账权,可以多付些手续费。 02 以太坊的转账以太坊的转账是按消耗Gas(燃料)计算的(ETH矿工费 = Gas Limit * Gas Price) 。与BTC一样,ETH转账也是需要支付区块链矿工费的。不同的是,以太坊的转账我们称消耗了Gas(燃料),这个值是由Gas Price(单价)和消耗的 Gas Limit(数量)来确定的,Gas limit的数量一般是根据ETH智能合约内容来决定的,因此发起方设置的Gas Price的价格越高,其发起的交易就能越快被打包。不过ETH的Gas用不完是可以退回的。 03 EOS转账不同于BTC和ETH,EOS转账几乎“免费”。EOS 转账过程中会消耗一定网络带宽资源(NET)、CPU计算资源(CPU)。但NET和CPU都是可再生资源,用户可以通过抵押EOS的方式获得,EOS账号信息、智能合约执行信息存储需要消耗运行内存资源(RAM), RAM是稀缺资源,需要购买,所以说EOS转账是免费的,但是还是需要消耗需要购买的RAM。传统的支付转账系统都是依赖于第三方机构作为信用背书,而在区块链的世界里,是点对点的去中心化交易,它速度更快,成本更低,具有更大的优势。你认为区块链转账相比传统行业还有哪些优势和弊端呢?欢迎在留言区分享你的观点。「白话区块链入门系列」互动有奖本文发布24小时后,将精选一名留言者,奖励8.8元红包;指出本文事例、逻辑等重大错误、并提出优秀建议的留言,一经采纳,奖励50元;本系列长期接受投稿,稿酬丰厚。后台回复「投稿」获取详细信息。往期内容精选♢006 比特币的矿工和挖矿是什么意思?♢014 虚拟货币价值的本质是什么?♢015 神奇而有趣的比特币♢018 被称为“区块链2.0”的以太坊是什么?♢021 被称为“区块链3.0”的EOS是什么?★后台回复「入门」获取完整目录!★——End——『声明:本系列内容仅供区块链科普入门学习,不构成任何投资意见或建议。如有任何错漏,敬请留言指出。文章版权和最终解释权归白话区块链所有。』亲,据说99.9%有品位的人都点了「在看」????
2023年03月06日
7 阅读
0 评论
0 点赞
2023-03-06
加密资产涨跌幅度的计算方式 |白话区块链入门246
友情提示:入门连载已经 200 多期了,大白建议新入门的小伙伴们可以去文末扫描二维码,进入科普目录从 0 开始学习噢!作者 | 阿华出品|白话区块链(ID:hellobtc)我们在进行加密资产交易时,总会关注加密资产的当前价格和历史价格。为了方便用户交易,很多交易平台也会特意展示每种加密资产的涨跌幅:现在以及过去价格的对比指标,最常用的是“24小时涨幅”。当然了,不同网站叫法不一定相同,例如有的网站只叫“涨幅”,但其含义和计算原理都差不多。下面,我们一起聊聊“涨幅”这个指标的含义以及计算方法。 01 确立计算标准在看加密资产行情时,要先弄清楚涨幅指标的计算标准。一般来说,交易平台的网站上都有说明,如果网站上没有相关指标的说明,也可以直接联系在线客服进行咨询。例如,OKEx 是以最近 24 小时来计算涨跌的,而火币则是以北京时间 0 点为基准开始计算涨跌,国外其他的加密资产交易平台,计算的时间点也各不相同。由于加密资产是 24 小时不间断交易,所以,根据过去 24 小时的价格走势来计算加密资产的涨跌幅,价格实时性更强一些,相对来说也更合理。大多数交易平台都采用了这种方式,不受世界时区限制,也更容易和国际接轨,方便世界各地的用户。 02 涨幅计算方法由于计算标准不同,所以在计算涨幅这个指标时,所采集的加密资产历史价格也不相同。首先,我们看看以最近 24 小时价格为计算标准的涨幅计算方法。假如现在是 11 月 19 日中午 12 点,EOS 价格是 23 元,而 11 月 18 日中午 12 点 EOS 的开盘价格是 25 元,那么它的涨幅为:(23 - 25) ÷ 25 = -8%,即网站上显示的涨幅指标为 -8%,或者说跌幅为 8%。如果是以北京时间 0 点为基准,涨幅的计算方法则是用当前价格减去今天 0 点的价格,所得的差除以 0 点的价格,得出当前的涨幅。如果以其他时间为基准,计算方法类似。 03 结语总之,由于各个交易平台以及行情软件计算涨幅的标准不同,再加上同一加密资产在不同交易平台的价格也不完全相同,所以显示出来的涨幅指标并不一定相同。我们在进行交易时,要清楚不同交易平台及行情软件的计算标准,只有对涨幅指标的含义有更清晰的理解,才能更好地辅助我们进行交易决策。除了涨幅(涨跌幅),你还关注哪些指标?欢迎在留言区分享你的观点。「白话区块链入门系列」互动有奖本文发布24小时后,将精选一名留言者,奖励8.8元红包;指出本文事例、逻辑等重大错误、并提出优秀建议的留言,一经采纳,奖励50元;本系列长期接受投稿,稿酬丰厚。后台回复「投稿」获取详细信息。往期内容精选♢006 比特币的矿工和挖矿是什么意思?♢014 虚拟货币价值的本质是什么?♢015 神奇而有趣的比特币♢018 被称为“区块链2.0”的以太坊是什么?♢021 被称为“区块链3.0”的EOS是什么?★后台回复「入门」获取完整目录!★——End——『声明:本系列内容仅供区块链科普入门学习,不构成任何投资意见或建议。如有任何错漏,敬请留言指出。文章版权和最终解释权归白话区块链所有。』亲,据说99.9%有品位的人都点了「在看」????
2023年03月06日
7 阅读
0 评论
0 点赞
2023-03-06
矿池和矿场有何区别?|白话区块链入门247
友情提示:入门连载已经 200 多期了,大白建议新入门的小伙伴们可以去文末扫描二维码,进入科普目录从 0 开始学习噢!作者 |宇星出品|白话区块链(ID:hellobtc)大白有个朋友看见比特币价值不菲,而且听说比特币是挖出来的,就两眼发光,兴致勃勃地也想去挖矿。但无奈入矿圈没那么容易,有许多知识需要了解。今天就让大白来介绍一下什么是挖矿,以及矿池和矿场的区别到底是什么? 01 挖矿在《比特币的矿工和挖矿是什么意思?》一文中谈到,在比特币世界中,比特币就好比是“数字黄金”,像黄金是从金矿里面挖出来那样,我们想把比特币从数字里面“挖”出来以此来获得比特币奖励,而挖矿的过程实则就是解决计算难题的过程。而挖矿的实施者则称为矿工。挖矿再详细点其实就是不停地进行哈希运算,直到得到的哈希运算后的结果比目标值小。这里看不懂,先别慌,请继续听着大白往下讲。其实这挖矿的过程就和掷色子类似,比如,游戏规则是让大白同时扔3个色子,扔出的数字序列小于112算胜利,假设大白某一次扔出的色子序列为111(第一个色子正面向上的数字是1,第二个色子是1,第三个色子是1),那么就表示胜利,并获得游戏奖励。在比特币世界中,我们把“掷色子”的这个过程交给了我们的一些计算能力的机器(矿机)去做。上面的例子是3个色子同时扔,而我们的比特币中的哈希值是256位,也就是说相当于同时扔256个色子,而且上面的例子中一个色子只有6面,也就6种可能,但是比特币世界中的一个”色子“相当于硬币有2面(一面是0,一面为1),因此会有10的77次方以上的可能情况产生。因此如果一个矿机挖矿的话,挖到的概率还是非常小的。在比特币世界中,计算机会不断地“掷色子”,直到得到的色子序列比目标值小时,就会出一个区块,而且第一个计算出这个结果的矿工将会获得一定的奖励。这也是激励矿工去挖矿的一种的机制。 02 矿池大白前面提到,比如要找到一个比“112”小的序列,只有当掷到的色子序列为“111”时才能算胜利,可是生活经验会告诉我们扔出这个序列的可能性非常的小,概率是1/216,假设大白自己在那儿一直扔,可能到游戏结束都不会赢。但是假设大白加入到10个人的团队,团队中每个人都扔3个色子,直到团队中一个人扔出的色子序列为“111”,那么就算胜利,这样大白就可以从团队获得的集体奖励中分配到自己的那份收益。从“个人扔色子”到“团体扔色子”的过程在比特币世界中相当于是算力的合并。通过这种算力合并联合运作的方法建立的网站便称作“矿池”。白话点讲,就是把单个的矿机集合起来(即矿机接入矿池的过程),然后大家一起提供算力,最后一起获得收益,再按一定的分配机制分配收益。这样一来,单个矿机获利就会比较的稳定。而且矿池是若干矿机算力的集合,它是一个平台,它不受区域的限制,比如F2Pool鱼池,它的矿机分布在美国、加拿大、新加坡、俄罗斯等全球多个国家。 03 矿场相信通过前面的讲解,大家已经知道挖矿需要有计算能力的机器去计算,那么这个计算的过程必然是需要耗费电力的,而且还需要人去维护挖矿的机器设备。所以为了降低挖矿的成本,人们会想着把许多矿机放到一起,方便维护管理,而且往往会选择一个合适的区域,比如像四川、贵州这些电费比较便宜的地方。这样把许多矿机连接在一起进行挖矿,就形成了矿场。矿场相对于矿池来说,它是针对集中矿机的实际的场地而言,它着重表达的是某个区域的矿机的集合。就好比四川的一些电力资源丰富的地方就有很多的矿场。 04 小结矿场是形容矿机物理的硬件设备的集合,矿池是这些矿机所提供的算力的集合,因此矿池不需要要求集中在一个地方;矿场是指实体的硬件设备的集合,而矿池则相对形象些,指的是可能分布在不同地方的算力的集合,它是一个网络平台。那大白还是用掷色子形容一下矿池和矿机,大白、小黑、小白等人分别在不同的地方,然后一起扔色子,最后把扔色子的结果通过网络集中起来,这就比较像矿池的集中算力。而假如大白、小黑、小白等人都聚集在一个教室,他们都参加扔色子的游戏(即指矿机进行计算),那么这个教室我们就可以当成一个能玩扔色子游戏的聚集地,就好比比特币中的矿场。你了解挖矿,以及矿池和矿场的区别了吗?还有什么疑问呢?欢迎在留言区给出你的评论哦!「白话区块链入门系列」互动有奖本文发布24小时后,将精选一名留言者,奖励8.8元红包;指出本文事例、逻辑等重大错误、并提出优秀建议的留言,一经采纳,奖励50元;本系列长期接受投稿,稿酬丰厚。后台回复「投稿」获取详细信息。往期内容精选♢006 比特币的矿工和挖矿是什么意思?♢014 虚拟货币价值的本质是什么?♢015 神奇而有趣的比特币♢018 被称为“区块链2.0”的以太坊是什么?♢021 被称为“区块链3.0”的EOS是什么?★后台回复「入门」获取完整目录!★——End——『声明:本系列内容仅供区块链科普入门学习,不构成任何投资意见或建议。如有任何错漏,敬请留言指出。文章版权和最终解释权归白话区块链所有。』亲,据说99.9%有品位的人都点了「在看」????
2023年03月06日
5 阅读
0 评论
0 点赞
2023-03-03
025:AirCoin WebUI 设计概要|《ETH原理与智能合约开发》笔记
待字闺中开发了一门区块链方面的课程:《深入浅出ETH原理与智能合约开发》,马良老师讲授。此简书文集记录我的学习笔记。课程共8节课。其中,前四课讲ETH原理,后四课讲智能合约。第八课分为三部分:Web应用架构 AirCoin WebUI 设计概要 AirCoin WebUI 实际开发演示 这篇文章是第八课第二部分的学习笔记:AirCoin WebUI 设计概要。这节课主要讲解了AirCoin Web 前端的设计概要。介绍了其中涉及到文件的作用,序列图,重点讲解了实现前端逻辑的 app.js 文件(下一篇)。1、AirCoin WebUI 设计概要空气币(AirCoin)来自于上节课的例子,它是一个基于 ERC 20 发行的代币,在那节课中只有后端的智能合约实现的逻辑,没有前端。这节课就实现它的前端逻辑。这个例子是一个官方的教学实例,马老师进行了大量的修改。WebUI 设计概要1.1 文件列表静态页面 index.html这个文件修改自 TutorialToken app.js这个文件实现了前端的逻辑,比如:读取合约的JSON文件、生成合约对象,合约实例;调用web3js、truffle-contract;批量转账等功能。 批量转账是向多个账户转账。它可以有两种方式,一种是在智能合约里实现,一种是在JS文件里实现,这节课中用的是后一种。2_deploy_contracts.js这个是合约的部署文件。 bs-config broswer-sync 配置文件前端的测试环境用了一个叫 broswer-sync 的东西,它可以动态地修改前端的参数,加快开发进度。 web3.min.js, truffle-contract.min.js这两个文件也需要更新一下。 1.2 序列图前端交互的时序图。实例中使用的 Ganashi 即代表是后端,也代表整个区块链,它是一个很方便的测试框架。前端就是上图中的webUI,它基于 TruffleContract 和 web3js 两个组件,和Ganashi 进行通信。时序图如上图,创建实例后,前端向两个智能合约发起调用,以及获得反馈。1.3 前端页面展示前端页面(我的电脑因升级导致虚拟机不能用了,这图是视频上截的)最上面是众筹的信息。总的供给量,对应账户及余额。下面是输入部分,账户,以太币,以wei为单位,汇率是8,两个按钮是买空气币,和批量转账。下面是每个账户对应的余额。展示以太币的变化情况。地址0是缺省地址,地址1是钱包地址,负责收钱。无论是谁花了以太币买空气币,都转到地址1;地址5到8演示批量转账。2、代码分析2.1 代码的目录前端的实现文件主要是在 src 文件夹。如图。另外,图中的 bs-config.json 是broswer-sync 的配置文件。目录截图(这图也是视频上截的)2.2 index.htmlindex.html局部截图上图是index.html的主要部分,加了 id 的,是需要使用 JS 更新的,后面结合 app.js 来分析。后面是一个table 表格,用来显示各个地址下的余额。再后面是加载相关的JS文件,其中 web3.min.js, truffle-contract.min.js 要用老师更新过的文件。2.3 app.js使用 vim src/js/app.js 打开文件。这个文件是本实例中最重要的JS文件,它实现了前端页面的所有逻辑。这些逻辑都包含在叫 App 的对象中。(没状态了,先放一放,下篇吧 -_-!!!)小结,这节主要介绍了AirCoin Web 前端的设计概要。不足之外,请批评指正。
2023年03月03日
10 阅读
0 评论
0 点赞
2023-03-03
以太坊(ETH)GAS详解
本文翻译自:http://ethdocs.org/en/latest/contracts-and-transactions/account-types-gas-and-transactions.html?highlight=gas#what-is-gas什么是Gas?以太坊在区块链上的的执行环境的实现称为以太坊虚拟机(EVM)。 参与网络的每个节点都运行EVM作为区块验证协议的一部分。 他们会检查正在验证的块中列出的交易,并运行由EVM中的事务触发的代码。 网络中的每个全节点执行相同的计算并存储相同的值。 显然,以太坊不是要优化计算效率。,因为它的并行处理是冗余的。在不需要可信的第三方,权威或暴力垄断的情况下,以太坊提供一种有效的方式来达成系统状态的共识。但是很明显,这不是最优的计算方式。合约执行是跨节点冗余复制的事实自然会使它们变得昂贵,这通常会产生不使用区块链进行离线计算的动机。当你运行一个去中心化应用(dapp)时,它会与区块链交互以读取和修改其状态,但是dapps通常只会放置对达成共识至关重要的业务逻辑和状态到区块链上。当由于消息或事务触发而执行合约时,每个指令都在网络的每个节点上执行。 这具有一定的成本:对于每个执行的操作都有指定的成本,以一定gas单位表示。Gas是交易发起人需要为以太坊区块链上的每项操作支付的执行费的名称。 gas这个名称的灵感来源于这种费用可以作为加密燃料,驱动智能合约的运动。 gas可以从执行代码的矿工那里购买。 由于gas单位与具有自然成本的计算单元对齐,因此gas和ether有意地解耦,而ether(以太)的价格通常是随市场波动的。 这两者是由自由市场调节的:gas的价格实际上是由矿工决定的,他们可以拒绝处理gas价格低于最低限额的交易。 你只需在你的账户中添加一定的以太币就可以获得gas。 以太坊客户端会自动为你的以太币购买gas,金额为您指定的金额,作为交易的最大支出。根据以太坊协议,在合约或交易中执行的每个计算步骤都要收取费用,以防止在以太坊网络上的恶意攻击和滥用。每笔交易都必须包含gas limit和愿意为gas支付的费用。矿工可以选择是否打包交易和收取费用。如果由交易产生的计算步骤所使用的gas总量(gas used ),包括原始消息和可能被触发的任何子消息,小于或等于gas limit,则处理该交易。如果gas总量超过gas limit,那么所有的改变都会回退,除非交易仍然有效并且矿工接受了这个费用。交易执行中未使用的所有多余的gas将以Ether返还给交易发起人。你不必担心超支,因为你只需支付消耗的gas费用。这意味着发送高于估计值gas limit的交易是有用的,也是安全的。估算交易成本交易中花费的总共的ether成本取决于2个因素:gasUsed:是交易中消耗的总共的gasgasPrice:在交易中指定一个单位gas的价格(ether)总费用 = gasUsed * gasPricegasUsedEVM中的每个操作都指定了要消耗的gas量。 gasUsed是执行所有操作的所有gas的总和。 有一个电子表格,提供了这背后的一些分析。为了估算gasUsed,这里有一个可以使用的estimateGas API,但有一些注意事项。gasPrice用户构造并签名交易时,每个用户都可以指定他们希望的任何gasPrice,它甚至可以是零。 然而,Frontier推出的以太坊客户端的默认gasPrice为0.05e12 wei。 由于矿工优化了他们的收入,如果大多数交易都是以0.05e12 wei的gas价格提交的,那么很难说服矿工接受指定较低或零的gas价格的交易。估算交易成本的例子举个例子,一个合约只是添加2个数字,EVM OPCODE ADD消耗3gas。使用默认gas价格(截至2016年1月)的大致成本为:3 * 0.05e12 = 1.5e11 wei注意:gas的最小单位为wei,1ether = 10^18wei换算成以太的话,就是0.00000015 Ether.question gas fees gas cost calculator Ethereum Gas Prices Operation Name Gas Cost Remark step 1 default amount per execution cycle stop 0 free suicide 0 free sha3 20 sload 20 get from permanent storage sstore 100 put into permanent storage balance 20 create 100 contract creation call 20 initiating a read-only call memory 1 every additional word when expanding memory txdata 5 every byte of data or code for a transaction transaction 500 base fee transaction contract creation 53000 changed in homestead from 21000
2023年03月03日
6 阅读
0 评论
0 点赞
2023-03-03
【ETH钱包开发01】创建、导出钱包
简介这篇文章主要是介绍ETH移动端(Android)钱包开发,核心功能包括创建钱包、导入钱包、钱包转账(收款)、交易查询等。关于钱包的基本概念钱包地址以0x开头的42位的哈希值 (16进制) 字符串keystore明文私钥通过加密算法加密过后的 JSON 格式的字符串, 一般以文件形式存储助记词12 (或者 15、18、21) 单词构成, 用户可以通过助记词导入钱包, 但反过来讲, 如果他人得到了你的助记词, 不需要任何密码就可以轻而易举的转移你的资产, 所以要妥善保管自己的助记词明文私钥64位的16进制哈希值字符串, 用一句话阐述明文私钥的重要性 “谁掌握了私钥, 谁就掌握了该钱包的使用权!” 同样, 如果他人得到了你的明文私钥, 不需要任何密码就可以轻而易举的转移你的资产和银行卡做个简单类比地址=银行卡号密码=银行卡密码私钥=银行卡号+银行卡密码助记词=银行卡号+银行卡密码Keystore+密码=银行卡号+银行卡密码Keystore ≠ 银行卡号私钥通过椭圆曲线签名得到公钥 ,公钥经过哈希得到钱包地址 ,整个过程单向的(不可逆 )私钥------->公钥------->钱包地址关于BIP协议这里先简单介绍一下BIP,后面再单独出一篇文章讲解。BIP协议是比特币的一个改进协议,在钱包开发中主要用到BIP32、BIP39、BIP44BIP32:定义了层级确定性钱包( Hierarchical Deterministic wallet ,简称 HD Wallet),是一个系统可以从单一个 seed 产生一树状结构储存多组 keypairs(私钥和公钥)。好处是可以方便的备份、转移到其他相容装置(因为都只需要 seed),以及分层的权限控制等。BIP39:用于生成助记词,将 seed 用方便记忆和书写的单词表示,一般由 12 个单字组成,单词列表总共有2048个单词。WordlistsBIP44:基于 BIP32 的系统,赋予树状结构中的各层特殊的意义。让同一个 seed 可以支援多币种、多帐户等。各层定义如下:m / purpose' / coin_type' / account' / change / address_indexpurporse': 固定值44', 代表是BIP44 coin_type': 这个代表的是币种, 可以兼容很多种币, 比如BTC是0', ETH是60',btc一般是 m/44'/0'/0'/0,eth一般是 m/44'/60'/0'/0 account’:账号 change’: 0表示外部链(External Chain),用户接收比特币,1表示内部链(Internal Chain),用于接收找零 address_index:钱包索引 准备工具eth钱包开发需要借助2个第三方库web3j:可以理解为eth API的java版本bitcoinj:生成支持bip32和bip44的钱包。还有其他的一些库支持bip32和bip44,比如:Nova Crypto的系列包,包含bip32,bip39,bip44,我就是使用的Nova Crypto系列包。注意:因为web3j不支持生成bip44的钱包,而市面上大多数钱包使用bip32,bip39,bip44标准结合生成,所以引用此包。在创建完钱包之后,你可以使用下面这个工具去测试助记词, 和校验助记词生成的地址、公钥、私钥等。https://iancoleman.io/bip39/创建钱包在了解BIP 后,我们开始以太坊钱包开发,创建的钱包的流程为:1、随机生成一组助记词2、生成 seed3、生成 master key4、生成 child key5、我们取第一组child key即m/44'/60'/0'/0/0 得到私钥,keystore及地址1、引用库:web3j implementation 'org.web3j:core:3.3.1-android' 创建钱包相关全家桶的那个bip32有点问题,用我这里给出的那个// implementation 'org.bitcoinj:bitcoinj-core:0.14.7' implementation 'io.github.novacrypto:BIP39:0.1.9' //用于生成助记词 implementation 'io.github.novacrypto:BIP44:0.0.3' // implementation 'io.github.novacrypto:BIP32:0.0.9' //使用这个bip32 implementation 'com.lhalcyon:bip32:1.0.0' implementation 'com.lambdaworks:scrypt:1.4.0' //加密算法 2、生成随机助记词 /** * generate a random group of mnemonics * 生成一组随机的助记词 */ public String generateMnemonics() { StringBuilder sb = new StringBuilder(); byte[] entropy = new byte[Words.TWELVE.byteLength()]; new SecureRandom().nextBytes(entropy); new MnemonicGenerator(English.INSTANCE) .createMnemonic(entropy, sb::append); return sb.toString(); } 3. 根据助记词计算出Seed,得到master key ,根据BIP44派生地址,获取KeyPair/** * generate key pair to create eth wallet_normal * 生成KeyPair , 用于创建钱包(助记词生成私钥) */ public ECKeyPair generateKeyPair(String mnemonics) { // 1. we just need eth wallet_normal for now AddressIndex addressIndex = BIP44 .m() .purpose44() .coinType(60) .account(0) .external() .address(0); // 2. calculate seed from mnemonics , then get master/root key ; Note that the bip39 passphrase we set "" for common byte[] seed = new SeedCalculator().calculateSeed(mnemonics, ""); ExtendedPrivateKey rootKey = ExtendedPrivateKey.fromSeed(seed, Bitcoin.MAIN_NET); Log.i(TAG, "mnemonics:" + mnemonics); String extendedBase58 = rootKey.extendedBase58(); Log.i(TAG, "extendedBase58:" + extendedBase58); // 3. get child private key deriving from master/root key ExtendedPrivateKey childPrivateKey = rootKey.derive(addressIndex, AddressIndex.DERIVATION); String childExtendedBase58 = childPrivateKey.extendedBase58(); Log.i(TAG, "childExtendedBase58:" + childExtendedBase58); // 4. get key pair byte[] privateKeyBytes = childPrivateKey.getKey(); ECKeyPair keyPair = ECKeyPair.create(privateKeyBytes); // we 've gotten what we need String privateKey = childPrivateKey.getPrivateKey(); String publicKey = childPrivateKey.neuter().getPublicKey(); String address = Keys.getAddress(keyPair); Log.i(TAG, "privateKey:" + privateKey); Log.i(TAG, "publicKey:" + publicKey); Log.i(TAG, "address:" + Constant.PREFIX_16 + address); return keyPair; } 这一步已经得到钱包公钥、私钥、地址了。如果需要测试助记词, 和校验助记词生成的地址, 那么可以访问这个网站 : https://iancoleman.io/bip39/4、通过keypair创建钱包 /** * 创建钱包(助记词方式) * * @param context app context 上下文 * @param password the wallet_normal password(not the bip39 password) 钱包密码(而不是BIP39的密码) * @param mnemonics 助记词 * @param walletName 钱包名称 * @return wallet_normal 钱包 */ public Flowable<HLWallet> generateWallet(Context context, String password, String mnemonics, String walletName) { Flowable<String> flowable = Flowable.just(mnemonics); return flowable .map(s -> { ECKeyPair keyPair = generateKeyPair(s); WalletFile walletFile = Wallet.createLight(password, keyPair); HLWallet hlWallet = new HLWallet(walletFile, walletName); WalletManager.shared().saveWallet(context, hlWallet); //保存钱包信息 return hlWallet; }); } 这样生成的就是符合bip32、bip39、bip44的钱包,也能和市面上包括imtoken在内的大多数钱包通用了。HLWalletpublic class HLWallet { public WalletFile walletFile; //钱包文件,包含私钥、keystore、address等信息 public String walletName; //钱包名称 @JsonIgnore public boolean isCurrent = false; public HLWallet() { } public HLWallet(WalletFile walletFile) { this.walletFile = walletFile; } public HLWallet(WalletFile walletFile, String walletName) { this.walletFile = walletFile; this.walletName = walletName; } public String getAddress(){ return Constant.PREFIX_16 + this.walletFile.getAddress(); } public String getWalletName() { return walletName; } public void setWalletName(String walletName) { this.walletName = walletName; } } 导出钱包导出私钥通过解密获得ECKeyPair通过ECKeyPair获得私钥,并转换成16进制,就是最后的私钥了。 /** * 导出私钥 * * @param password 创建钱包时的密码 * @param walletFile * @return */ public String exportPrivateKey(String password, WalletFile walletFile) { try { // ECKeyPair ecKeyPair = Wallet.decrypt(password, walletFile); //可能出现OOM ECKeyPair ecKeyPair = LWallet.decrypt(password, walletFile); String privateKey = Numeric.toHexStringNoPrefix(ecKeyPair.getPrivateKey()); return privateKey; } catch (CipherException e) { e.printStackTrace(); return "error"; } } 导出keystore /** * 导出Keystore * * @param password 创建钱包时的密码 * @param walletFile * @return */ public String exportKeystore(String password, WalletFile walletFile) { if (decrypt(password, walletFile)) { return new Gson().toJson(walletFile); } else { return "decrypt failed"; } } /** * 解密 * 如果方法没有抛出CipherException异常则表示解密成功,也就是说可以把Wallet相关信息展示给用户看 * * @param password 创建钱包时的密码 * @param walletFile * @return */ public boolean decrypt(String password, WalletFile walletFile) { try { // ECKeyPair ecKeyPair = Wallet.decrypt(password, walletFile); //可能出现OOM ECKeyPair ecKeyPair = LWallet.decrypt(password, walletFile); return true; } catch (CipherException e) { e.printStackTrace(); return false; } } 注意2点:1、在导出私钥、keystore、助记词之前都需要先验证密码是否正确,也就是调用如下这个方法,如果没有抛出异常,则把信息展示给用户看。Wallet.decrypt(password, walletFile); 2、使用web3j的这个decrypt,经常会抛出OOM异常。关于解决方案,大家查看这里。https://www.jianshu.com/p/41d4a38754a3导出助记词助记词是没有办法根据私钥或者keystore推导出来的。一般的做法是在创建钱包的时候把助记词加密后在本地存储,导出时解密。注意:使用IMToken导入私钥或者KeyStore创建的钱包,没有导出助记词的功能;如果是通过助记词创建的,就会有导出助记词的功能。而且助记词一旦备份之后,备份这个功能就会消失,也就是说从本地存储中删除。 /** * 导出助记词 * * @param password * @param hlWallet * @return */ public String exportMnemonics(String password, HLWallet hlWallet) { WalletFile walletFile = hlWallet.walletFile; if (decrypt(password, walletFile)) { return hlWallet.getMnemonic(); } else { return "decrypt failed"; } }
2023年03月03日
5 阅读
0 评论
0 点赞
2023-03-03
【ETH钱包开发02】导入钱包
简介本文主要讲解通过助记词、keystore、私钥 3种方式来导入钱包。导入钱包就是说根据输入的这3者中的一个去重新生成一个新的钱包。导入钱包的过程和创建的过程其实是差不多的。根据助记词导入钱包根据助记词导入钱包不需要原始密码,密码可以重新设置。根据用户输入的助记词,先验证助记词的合规性(格式、个数等),验证正确后,配合用户输入的密码重新生成一个新的钱包。验证助记词的合规性(格式、个数等)private boolean validateInput(String mnemonics, String password, String repassword) { // validate empty if (TextUtils.isEmpty(mnemonics) || TextUtils.isEmpty(password) || TextUtils.isEmpty(repassword)) { ScheduleCompat.snackInMain(startImportBtn, "请填写助记词和密码"); return false; } // validate password if (!TextUtils.equals(password, repassword)) { ScheduleCompat.snackInMain(startImportBtn, "密码不一致"); return false; } // validate mnemonic try { MnemonicValidator.ofWordList(English.INSTANCE).validate(mnemonics); } catch (InvalidChecksumException e) { e.printStackTrace(); ScheduleCompat.snackInMain(startImportBtn, "助记词格式不正确"); return false; } catch (InvalidWordCountException e) { e.printStackTrace(); ScheduleCompat.snackInMain(startImportBtn, "请检查单词个数"); return false; } catch (WordNotFoundException e) { e.printStackTrace(); ScheduleCompat.snackInMain(startImportBtn, "无效的助记词"); return false; } catch (UnexpectedWhiteSpaceException e) { e.printStackTrace(); ScheduleCompat.snackInMain(startImportBtn, "请检查空格与分隔符"); return false; } return true; } 助记词导入钱包/** * 助记词方式导入钱包,不需要以前的密码 * * @param context * @param password * @param mnemonics 重新设置的密码 * @param walletName * @return */ public Flowable<HLWallet> importMnemonic(Context context, String password, String mnemonics, String walletName) { Flowable<String> flowable = Flowable.just(mnemonics); return flowable .flatMap(s -> { ECKeyPair keyPair = generateKeyPair(s); WalletFile walletFile = Wallet.createLight(password, keyPair); HLWallet hlWallet = new HLWallet(walletFile, walletName); if (WalletManager.shared().isWalletExist(hlWallet.getAddress())) { return Flowable.error(new HLError(ReplyCode.walletExisted, new Throwable("Wallet existed!"))); } WalletManager.shared().saveWallet(context, hlWallet); return Flowable.just(hlWallet); }); } 根据私钥导入钱包通过私钥导入钱包其实和创建钱包的过程基本一致。因为私钥在导出的时候转换成了16进制,所以在导入私钥的时候,要把16进制转换为byte数组。/** * 私钥方式导入钱包,不需要以前的密码 * * @param context * @param privateKey * @param password 重新设置的密码 * @param walletName 钱包名称 * @return */ public Flowable<HLWallet> importPrivateKey(Context context, String privateKey, String password, String walletName) { if (privateKey.startsWith(Constant.PREFIX_16)) { privateKey = privateKey.substring(Constant.PREFIX_16.length()); } Flowable<String> flowable = Flowable.just(privateKey); return flowable.flatMap(s -> { byte[] privateBytes = Hex.decode(s); ECKeyPair ecKeyPair = ECKeyPair.create(privateBytes); WalletFile walletFile = Wallet.createLight(password, ecKeyPair); HLWallet hlWallet = new HLWallet(walletFile, walletName); if (WalletManager.shared().isWalletExist(hlWallet.getAddress())) { return Flowable.error(new HLError(ReplyCode.walletExisted, new Throwable("Wallet existed!"))); } WalletManager.shared().saveWallet(context, hlWallet); return Flowable.just(hlWallet); }); } 根据Keystore导入钱包keystore就是钱包文件,实际上就是钱包信息的json字符串。导入keystore是需要输入密码的,这个密码是你最后导出keystore时的密码。将keystore字符串变成walletFile实例再通过Wallet.decrypt(password, walletFile);解密,成功则可以导入,否则不能导入。ECKeyPair keyPair = Wallet.decrypt(password, walletFile); 这是Web3j的API,程序走到这里经常OOM!具体原因的话,我就不多说了,细节大家可以看这里https://www.jianshu.com/p/41d4a38754a3解决办法根据源码修改decrypt方法,这里我用一个已经修改好的第三方库implementation 'com.lambdaworks:scrypt:1.4.0' 修改后的解密方法public static ECKeyPair decrypt(String password, WalletFile walletFile) throws CipherException { validate(walletFile); WalletFile.Crypto crypto = walletFile.getCrypto(); byte[] mac = Numeric.hexStringToByteArray(crypto.getMac()); byte[] iv = Numeric.hexStringToByteArray(crypto.getCipherparams().getIv()); byte[] cipherText = Numeric.hexStringToByteArray(crypto.getCiphertext()); byte[] derivedKey; if (crypto.getKdfparams() instanceof WalletFile.ScryptKdfParams) { WalletFile.ScryptKdfParams scryptKdfParams = (WalletFile.ScryptKdfParams) crypto.getKdfparams(); int dklen = scryptKdfParams.getDklen(); int n = scryptKdfParams.getN(); int p = scryptKdfParams.getP(); int r = scryptKdfParams.getR(); byte[] salt = Numeric.hexStringToByteArray(scryptKdfParams.getSalt()); // derivedKey = generateDerivedScryptKey(password.getBytes(Charset.forName("UTF-8")), salt, n, r, p, dklen); derivedKey = com.lambdaworks.crypto.SCrypt.scryptN(password.getBytes(Charset.forName("UTF-8")), salt, n, r, p, dklen); } else if (crypto.getKdfparams() instanceof WalletFile.Aes128CtrKdfParams) { WalletFile.Aes128CtrKdfParams aes128CtrKdfParams = (WalletFile.Aes128CtrKdfParams) crypto.getKdfparams(); int c = aes128CtrKdfParams.getC(); String prf = aes128CtrKdfParams.getPrf(); byte[] salt = Numeric.hexStringToByteArray(aes128CtrKdfParams.getSalt()); derivedKey = generateAes128CtrDerivedKey( password.getBytes(Charset.forName("UTF-8")), salt, c, prf); } else { throw new CipherException("Unable to deserialize params: " + crypto.getKdf()); } byte[] derivedMac = generateMac(derivedKey, cipherText); if (!Arrays.equals(derivedMac, mac)) { throw new CipherException("Invalid password provided"); } byte[] encryptKey = Arrays.copyOfRange(derivedKey, 0, 16); byte[] privateKey = performCipherOperation(Cipher.DECRYPT_MODE, iv, encryptKey, cipherText); return ECKeyPair.create(privateKey); } 导入Kestore/** * 导入Keystore(推荐使用),Keystore+ 密码才能正确导入 * * @param context * @param keystore * @param password 以前的密码 * @param walletName * @return */ public Flowable<HLWallet> importKeystore(Context context, String keystore, String password, String walletName) { return Flowable.just(keystore) .flatMap(s -> { ObjectMapper objectMapper = new ObjectMapper(); WalletFile walletFile = objectMapper.readValue(keystore, WalletFile.class); //注意:这里用的是修改之后的解密方法 ECKeyPair keyPair = LWallet.decrypt(password, walletFile); HLWallet hlWallet = new HLWallet(walletFile, walletName); WalletFile generateWalletFile = Wallet.createLight(password, keyPair); if (!generateWalletFile.getAddress().equalsIgnoreCase(walletFile.getAddress())) { return Flowable.error(new HLError(ReplyCode.failure, new Throwable("address doesn't match private key"))); } if (WalletManager.shared().isWalletExist(hlWallet.getAddress())) { return Flowable.error(new HLError(ReplyCode.walletExisted, new Throwable("Wallet existed!"))); } WalletManager.shared().saveWallet(context, hlWallet); return Flowable.just(hlWallet); }); } 注意:1、导入助记词和私钥是不需要以前的密码的,而是重新输入新的密码;导入Keystore则需要以前的密码,如果密码不正确,会提示地址和私钥不匹配。2、关于备份助记词用过imtoken的同学可以看到imtoken是可以导出(备份)助记词的。这个一开始我也很困惑,后来了解到其实它实在创建钱包的时候,在app本地保存了助记词,导出只是讲数据读取出来而已。还有一点,imtoken一旦备份了助记词之后,之后就没有备份那个功能了,也就是说助记词在本地存储中删除了;而且导入钱包的时候也是没有备份助记词这个功能的。
2023年03月03日
8 阅读
0 评论
0 点赞
2023-03-03
【ETH钱包开发03】web3j转账ETH
在之前的文章中,讲解了创建、导出、导入钱包。【ETH钱包开发01】创建、导出钱包【ETH钱包开发02】导入钱包本文主要讲解以太坊转账相关的一些知识。交易分为ETH转账和ERC-20 Token转账,本篇先讲一下ETH转账。发起交易的2种方式1、解锁账户发起交易。钱包keyStore文件保存在geth节点上,用户发起交易需要解锁账户,适用于中心化的交易所。2、钱包文件离线签名发起交易。钱包keyStore文件保存在本地,用户使用密码+keystore的方式做离线交易签名来发起交易,适用于dapp,比如钱包。本文主要讲一下第二种方式,也就是钱包离线签名转账的方式。钱包文件签名的方式发起交易交易流程1、通过keystore加载转账所需的凭证Credentials2、创建一笔交易RawTransaction3、使用Credentials对象对交易签名4、发起交易/** * 发起一笔交易(自定义参数) * * @param from 发起人钱包地址 * @param to 转入的钱包地址 * @param value 转账金额,单位是wei * @param privateKey 钱包私钥 * @param gasPrice 转账费用 * @param gasLimit * @param data 备注的信息 * @throws IOException * @throws CipherException * @throws ExecutionException * @throws InterruptedException */ public EthSendTransaction transfer(String from, String to, BigInteger value, String privateKey, BigInteger gasPrice, BigInteger gasLimit, String data) throws IOException, CipherException, ExecutionException, InterruptedException { //加载转账所需的凭证,用私钥 Credentials credentials = Credentials.create(privateKey); //获取nonce,交易笔数 BigInteger nonce = getNonce(from); //创建RawTransaction交易对象 RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice, gasLimit, to, value); //签名Transaction,这里要对交易做签名 byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials); String hexValue = Numeric.toHexString(signMessage); //发送交易 EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get(); return ethSendTransaction; } /** * 获取nonce,交易笔数 * * @param from * @return * @throws ExecutionException * @throws InterruptedException */ private BigInteger getNonce(String from) throws ExecutionException, InterruptedException { EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.LATEST).sendAsync().get(); BigInteger nonce = transactionCount.getTransactionCount(); Log.i(TAG, "transfer nonce : " + nonce); return nonce; } 注意以下几点:1、Credentials这里,我是通过获取私钥的方式来加载Credentials//加载转账所需的凭证,用私钥 Credentials credentials = Credentials.create(privateKey); 还有另外一种方式,通过密码+钱包文件keystore方式来加载Credentials ECKeyPair ecKeyPair = LWallet.decrypt(password, walletFile); Credentials credentials = Credentials.create(ecKeyPair); 2、noncenonce是指发起交易的账户下的交易笔数,每一个账户nonce都是从0开始,当nonce为0的交易处理完之后,才会处理nonce为1的交易,并依次加1的交易才会被处理。可以通过eth_gettransactioncount 获取nonce3、gasPrice和gasLimit交易手续费由gasPrice 和gasLimit来决定,实际花费的交易手续费是gasUsed * gasPrice。所有这两个值你可以自定义,也可以使用系统参数获取当前两个值关于gas,你可以参考我之前的一篇文章。以太坊(ETH)GAS详解gasPrice和gasLimit影响的是转账的速度,如果gas过低,矿工会最后才打包你的交易。在app中,通常给定一个默认值,并且允许用户自己选择手续费。如果不需要自定义的话,还有一种方式来获取。获取以太坊网络最新一笔交易的gasPrice,转账的话,gasLimit一般设置为21000就可以了。/** * 获取当前以太坊网络中最近一笔交易的gasPrice */ public BigInteger requestCurrentGasPrice() throws Exception { EthGasPrice ethGasPrice = web3j.ethGasPrice().sendAsync().get(); return ethGasPrice.getGasPrice(); } Web3j还提供另外一种简单的方式来转账以太币,这种方式的好处是不需要管理nonce,不需要设置gasPrice和gasLimit,会自动获取最新一笔交易的gasPrice,gasLimit 为21000(转账一般设置成这个值就够用了)。 /** * 发起一笔交易 * 使用以太钱包文件发送以太币给其他人,不能设置nonce:(推荐) * * @param privateKey */ public String transfer(String toAddress, BigDecimal value, String privateKey) throws Exception { //转账者私钥 Credentials credentials = Credentials.create(privateKey); TransactionReceipt transactionReceipt = Transfer.sendFunds( web3j, credentials, toAddress, value, Convert.Unit.ETHER).sendAsync().get(); String transactionHash = transactionReceipt.getTransactionHash(); Log.i(TAG, "transfer: " + transactionHash); return transactionHash; } 如何验证交易是否成功?这个问题,我想是很多朋友所关心的吧。但是到目前为止,我还没有看到有讲解这方面的博客。之前问过一些朋友,他们说可以通过区块号、区块哈希来判断,也可以通过Receipt日志来判断。但是经过我的一番尝试,只有BlockHash是可行的,在web3j中根据blocknumber和transactionReceipt都会报空指针异常。原因大致是这样的:在发起一笔交易之后,会返回txHash,然后我们可以根据这个txHash去查询这笔交易相关的信息。但是刚发起交易的时候,由于手续费问题或者以太网络拥堵问题,会导致你的这笔交易还没有被矿工打包进区块,因此一开始是查不到的,通常需要几十秒甚至更长的时间才能获取到结果。我目前的解决方案是轮询的去刷BlockHash,一开始的时候BlockHash的值为0x00000000000,等到打包成功的时候就不再是0了。这里我使用的是rxjava的方式去轮询刷的,5s刷新一次。 /** * 开启轮询 * 根据txhash查询交易是否被打包进区块 * * @param txHash */ public static void startPolling(String txHash) { //5s刷新一次 disposable = Flowable.interval(0, 5, TimeUnit.SECONDS) .compose(ScheduleCompat.apply()) // .takeUntil(Flowable.timer(120, TimeUnit.SECONDS)) .subscribe(new Consumer<Long>() { @Override public void accept(Long num) throws Exception { Log.i(TAG, "第 : " + num + " 次轮询"); //根据blockHash来判断交易是否被打包 String blockHash = getBlockHash(txHash); boolean isSuccess = Numeric.toBigInt(blockHash).compareTo(BigInteger.valueOf(0)) != 0; if (isSuccess) { getTransactionReceipt(txHash); } } }); } /** * 停止轮询 * * @param disposable */ public static void stopPolling(Disposable disposable) { if (!disposable.isDisposed()) { disposable.dispose(); } } /** * 获取blockhash * @param txHash * @return */ public static String getBlockHash(String txHash) { Web3j web3j = Web3jFactory.build(new HttpService("https://rinkeby.infura.io/v3/xxxx")); try { EthTransaction transaction = web3j.ethGetTransactionByHash(txHash).sendAsync().get(); Transaction result = transaction.getResult(); String blockHash = result.getBlockHash(); Log.i(TAG, "getTransactionResult blockHash : " + blockHash); boolean isSuccess = Numeric.toBigInt(blockHash).compareTo(BigInteger.valueOf(0)) != 0; if (isSuccess) { getTransactionReceipt(txHash); stopPolling(disposable); } return blockHash; } catch (Exception e) { e.printStackTrace(); return ""; } } public static void getTransactionReceipt(String transactionHash) { Web3j web3j = Web3jFactory.build(new HttpService("https://rinkeby.infura.io/v3/xxxx")); try { EthGetTransactionReceipt transactionReceipt = web3j.ethGetTransactionReceipt(transactionHash).sendAsync().get(); TransactionReceipt receipt = transactionReceipt.getTransactionReceipt(); String status = receipt.getStatus(); BigInteger gasUsed = receipt.getGasUsed(); BigInteger blockNumber = receipt.getBlockNumber(); String blockHash = receipt.getBlockHash(); Log.i(TAG, "getTransactionReceipt status : " + status); Log.i(TAG, "getTransactionReceipt gasUsed : " + gasUsed); Log.i(TAG, "getTransactionReceipt blockNumber : " + blockNumber); Log.i(TAG, "getTransactionReceipt blockHash : " + blockHash); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } 正常情况下,几十秒内就可以获取到区块信息了。区块确认数区块确认数=当前区块高度-交易被打包时的区块高度。
2023年03月03日
11 阅读
0 评论
0 点赞
2023-03-03
Etherscan 上传和验证合约源码
之前发布合约的时候都没有注意到这个问题,我也是最近在Etherscan浏览器查看自己合约的时候才发现的这个问题,于是就开始解决一下呗。我看了几篇博客,我认为下面这一篇就讲的很详细。https://www.jianshu.com/p/56082f942bb3细节我就不多说了,我说说其中比较关键的一些地方,以及大家比较容易犯错的地方吧。大家先来看看,没有验证过代码的合约是这样的在Remix编译通过后,点击【Details】获取byteCode信息Details-------> ByteCode----->object注意:这里是编译刚通过的时候就要去获取这个ByteCode,而不是等到Run那里填参数之后。60806040526002805460ff1916601217905534801561001d57600080fd5b50604051610a15380380610a1583398101604090815281516020808401518385015160025460ff16600a0a84026003819055600160a060020a0333166000908152600485529586205590850180519395909491019261007e9285019061009b565b50805161009290600190602084019061009b565b50505050610136565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100dc57805160ff1916838001178555610109565b82800160010185558215610109579182015b828111156101095782518255916020019190600101906100ee565b50610115929150610119565b5090565b61013391905b80821115610115576000815560010161011f565b90565b6108d0806101456000396000f3006080604052600436106100b95763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100be578063095ea7b31461014857806318160ddd1461018057806323b872dd146101a7578063313ce567146101d157806342966c68146101fc57806370a082311461021457806379cc67901461023557806395d89b4114610259578063a9059cbb1461026e578063cae9ca5114610294578063dd62ed3e146102fd575b600080fd5b3480156100ca57600080fd5b506100d3610324565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561010d5781810151838201526020016100f5565b50505050905090810190601f16801561013a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015457600080fd5b5061016c600160a060020a03600435166024356103b2565b604080519115158252519081900360200190f35b34801561018c57600080fd5b506101956103e2565b60408051918252519081900360200190f35b3480156101b357600080fd5b5061016c600160a060020a03600435811690602435166044356103e8565b3480156101dd57600080fd5b506101e661045f565b6040805160ff9092168252519081900360200190f35b34801561020857600080fd5b5061016c600435610468565b34801561022057600080fd5b50610195600160a060020a03600435166104f2565b34801561024157600080fd5b5061016c600160a060020a0360043516602435610504565b34801561026557600080fd5b506100d36105e0565b34801561027a57600080fd5b50610292600160a060020a036004351660243561063a565b005b3480156102a057600080fd5b50604080516020600460443581810135601f810184900484028501840190955284845261016c948235600160a060020a03169460248035953695946064949201919081908401838280828437509497506106499650505050505050565b34801561030957600080fd5b50610195600160a060020a0360043581169060243516610780565b6000805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156103aa5780601f1061037f576101008083540402835291602001916103aa565b820191906000526020600020905b81548152906001019060200180831161038d57829003601f168201915b505050505081565b600160a060020a033381166000908152600560209081526040808320938616835292905220819055600192915050565b60035481565b600160a060020a0380841660009081526005602090815260408083203390941683529290529081205482111561041d57600080fd5b600160a060020a038085166000908152600560209081526040808320339094168352929052208054839003905561045584848461079d565b5060019392505050565b60025460ff1681565b600160a060020a03331660009081526004602052604081205482111561048d57600080fd5b600160a060020a03331660008181526004602090815260409182902080548690039055600380548690039055815185815291517fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca59281900390910190a2506001919050565b60046020526000908152604090205481565b600160a060020a03821660009081526004602052604081205482111561052957600080fd5b600160a060020a038084166000908152600560209081526040808320339094168352929052205482111561055c57600080fd5b600160a060020a038084166000818152600460209081526040808320805488900390556005825280832033909516835293815290839020805486900390556003805486900390558251858152925191927fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5929081900390910190a250600192915050565b60018054604080516020600284861615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156103aa5780601f1061037f576101008083540402835291602001916103aa565b61064533838361079d565b5050565b60008361065681856103b2565b156107785780600160a060020a0316638f4ffcb1338630876040518563ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018085600160a060020a0316600160a060020a0316815260200184815260200183600160a060020a0316600160a060020a0316815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561070c5781810151838201526020016106f4565b50505050905090810190601f1680156107395780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b15801561075b57600080fd5b505af115801561076f573d6000803e3d6000fd5b50505050600191505b509392505050565b600560209081526000928352604080842090915290825290205481565b6000600160a060020a03831615156107b457600080fd5b600160a060020a0384166000908152600460205260409020548211156107d957600080fd5b600160a060020a038316600090815260046020526040902054828101116107ff57600080fd5b50600160a060020a038083166000818152600460209081526040808320805495891680855282852080548981039091559486905281548801909155815187815291519390950194927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3600160a060020a0380841660009081526004602052604080822054928716825290205401811461089e57fe5b505050505600a165627a7a723058202fcf433c82780039ff26c6a0f0f297aee8999f119942eed8a2afb54bb1e35aa30029输入构造参数,部署合约之后得到60806040526002805460ff1916601217905534801561001d57600080fd5b50604051610a15380380610a1583398101604090815281516020808401518385015160025460ff16600a0a84026003819055600160a060020a0333166000908152600485529586205590850180519395909491019261007e9285019061009b565b50805161009290600190602084019061009b565b50505050610136565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100dc57805160ff1916838001178555610109565b82800160010185558215610109579182015b828111156101095782518255916020019190600101906100ee565b50610115929150610119565b5090565b61013391905b80821115610115576000815560010161011f565b90565b6108d0806101456000396000f3006080604052600436106100b95763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100be578063095ea7b31461014857806318160ddd1461018057806323b872dd146101a7578063313ce567146101d157806342966c68146101fc57806370a082311461021457806379cc67901461023557806395d89b4114610259578063a9059cbb1461026e578063cae9ca5114610294578063dd62ed3e146102fd575b600080fd5b3480156100ca57600080fd5b506100d3610324565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561010d5781810151838201526020016100f5565b50505050905090810190601f16801561013a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015457600080fd5b5061016c600160a060020a03600435166024356103b2565b604080519115158252519081900360200190f35b34801561018c57600080fd5b506101956103e2565b60408051918252519081900360200190f35b3480156101b357600080fd5b5061016c600160a060020a03600435811690602435166044356103e8565b3480156101dd57600080fd5b506101e661045f565b6040805160ff9092168252519081900360200190f35b34801561020857600080fd5b5061016c600435610468565b34801561022057600080fd5b50610195600160a060020a03600435166104f2565b34801561024157600080fd5b5061016c600160a060020a0360043516602435610504565b34801561026557600080fd5b506100d36105e0565b34801561027a57600080fd5b50610292600160a060020a036004351660243561063a565b005b3480156102a057600080fd5b50604080516020600460443581810135601f810184900484028501840190955284845261016c948235600160a060020a03169460248035953695946064949201919081908401838280828437509497506106499650505050505050565b34801561030957600080fd5b50610195600160a060020a0360043581169060243516610780565b6000805460408051602060026001851615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156103aa5780601f1061037f576101008083540402835291602001916103aa565b820191906000526020600020905b81548152906001019060200180831161038d57829003601f168201915b505050505081565b600160a060020a033381166000908152600560209081526040808320938616835292905220819055600192915050565b60035481565b600160a060020a0380841660009081526005602090815260408083203390941683529290529081205482111561041d57600080fd5b600160a060020a038085166000908152600560209081526040808320339094168352929052208054839003905561045584848461079d565b5060019392505050565b60025460ff1681565b600160a060020a03331660009081526004602052604081205482111561048d57600080fd5b600160a060020a03331660008181526004602090815260409182902080548690039055600380548690039055815185815291517fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca59281900390910190a2506001919050565b60046020526000908152604090205481565b600160a060020a03821660009081526004602052604081205482111561052957600080fd5b600160a060020a038084166000908152600560209081526040808320339094168352929052205482111561055c57600080fd5b600160a060020a038084166000818152600460209081526040808320805488900390556005825280832033909516835293815290839020805486900390556003805486900390558251858152925191927fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5929081900390910190a250600192915050565b60018054604080516020600284861615610100026000190190941693909304601f810184900484028201840190925281815292918301828280156103aa5780601f1061037f576101008083540402835291602001916103aa565b61064533838361079d565b5050565b60008361065681856103b2565b156107785780600160a060020a0316638f4ffcb1338630876040518563ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018085600160a060020a0316600160a060020a0316815260200184815260200183600160a060020a0316600160a060020a0316815260200180602001828103825283818151815260200191508051906020019080838360005b8381101561070c5781810151838201526020016106f4565b50505050905090810190601f1680156107395780820380516001836020036101000a031916815260200191505b5095505050505050600060405180830381600087803b15801561075b57600080fd5b505af115801561076f573d6000803e3d6000fd5b50505050600191505b509392505050565b600560209081526000928352604080842090915290825290205481565b6000600160a060020a03831615156107b457600080fd5b600160a060020a0384166000908152600460205260409020548211156107d957600080fd5b600160a060020a038316600090815260046020526040902054828101116107ff57600080fd5b50600160a060020a038083166000818152600460209081526040808320805495891680855282852080548981039091559486905281548801909155815187815291519390950194927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929181900390910190a3600160a060020a0380841660009081526004602052604080822054928716825290205401811461089e57fe5b505050505600a165627a7a723058202fcf433c82780039ff26c6a0f0f297aee8999f119942eed8a2afb54bb1e35aa300290000000000000000000000000000000000000000000000000000000000000834000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000074d79546f6b656e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024d54000000000000000000000000000000000000000000000000000000000000注意:添加构造参数,部署和鱼成功之后与初始的byteCode比较发现两者就多了后面一段字节数据:0000000000000000000000000000000000000000000000000000000000000834000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000074d79546f6b656e0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024d54000000000000000000000000000000000000000000000000000000000000这一段字节数据就是我们的构造参数的byteCode我就在这里犯过错,之前一直没找到构造参数的byteCode,大家务必按照步骤来,不然后面通不过。盗图借上面这篇文章的上传源码的图说明几点。第一:上面获取到的构造参数的byteCode复制到最下面的那个框中就行(切记按照步骤,我就踩过坑)第二:编译版本不是指你solidity合约代码中的版本,而是如图所示的这个(这里也踩过坑)验证成功之后的页面大概是这样的到此,恭喜你已经成功的上传和验证通过你的合约源码了!
2023年03月03日
9 阅读
0 评论
0 点赞
2023-03-03
【ETH钱包开发04】web3j转账ERC-20 Token
在上一篇文章中讲解了ETH转账,这一篇讲一下ERC-20 Token转账。【ETH钱包开发03】web3j转账ETHERC-20 Token转账的2种方式1、直接用web3j的API2、java/Android调用合约的transfer方法不管用哪种方式来转账,你都需要先写一个solidity智能合约文件来创建ERC-20 Token,然后部署合约,最后才是通过客户端来调用。web3j API转账ERC-20 Token/** * ERC-20Token交易 * * @param from * @param to * @param value * @param privateKey * @param data 附加信息需要转换成16进制数 * @return * @throws Exception */ public EthSendTransaction transferERC20Token(String from, String to, BigInteger value, String privateKey, String contractAddress) throws Exception { //加载转账所需的凭证,用私钥 Credentials credentials = Credentials.create(privateKey); //获取nonce,交易笔数 BigInteger nonce = getNonce(from); //get gasPrice BigInteger gasPrice = requestCurrentGasPrice(); BigInteger gasLimit = Contract.GAS_LIMIT; //创建RawTransaction交易对象 Function function = new Function( "transfer", Arrays.asList(new Address(to), new Uint256(value)), Arrays.asList(new TypeReference<Type>() { })); String encodedFunction = FunctionEncoder.encode(function); RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit, contractAddress, encodedFunction); //签名Transaction,这里要对交易做签名 byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials); String hexValue = Numeric.toHexString(signMessage); //发送交易 EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get(); return ethSendTransaction; } /** * 获取nonce,交易笔数 * * @param from * @return * @throws ExecutionException * @throws InterruptedException */ private BigInteger getNonce(String from) throws ExecutionException, InterruptedException { EthGetTransactionCount transactionCount = web3j.ethGetTransactionCount(from, DefaultBlockParameterName.LATEST).sendAsync().get(); BigInteger nonce = transactionCount.getTransactionCount(); Log.i(TAG, "transfer nonce : " + nonce); return nonce; } 注意:erc-20 token转账和eth转账的区别如下:1、erc-20 token创建交易对象用的是这个方法createTransaction2、erc-20 token需要构建Function,它其实对应的就是erc-20 token合约中的那些方法。它的第一个参数就是ERC20中那几个方法的名称,第二个参数的话就是对应合约方法中的参数,第三个参数是和第二个参数对应的,按照我那样就行了。转账的话就是transfer,我们从合约的transfer可以看到第一个参数是收款地址,第二个参数是金额,所以Function这里对应起来就好。 //创建Function对象 Function function = new Function( "transfer", Arrays.asList(new Address(to), new Uint256(value)), Arrays.asList(new TypeReference<Type>() { })); String encodedFunction = FunctionEncoder.encode(function); // 如下为 ERC20 Token 标准接口:----------- // ERC20 Token Standard Interface // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md // ---------------------------------------------------------------------------- contract ERC20 { function name() constant returns (string name) function symbol() constant returns (string symbol) function decimals() constant returns (uint8 decimals) function totalSupply() constant returns (uint totalSupply); function balanceOf(address _owner) constant returns (uint balance); function transfer(address _to, uint _value) returns (bool success); function transferFrom(address _from, address _to, uint _value) returns (bool success); function approve(address _spender, uint _value) returns (bool success); function allowance(address _owner, address _spender) constant returns (uint remaining); event Transfer(address indexed _from, address indexed _to, uint _value); event Approval(address indexed _owner, address indexed _spender, uint _value); } java/Android调用合约的transfer方法这种方法不需要使用web3j封装的方法,而是直接调用solidity合约的方法。步骤1、web3j加载一个已经部署的合约2、验证合约是否加载成功isValid3、如何加载合约成功,则调用合约的transfer方法 /** * ERC-20Token交易(调用solidity合约方式) 推荐使用 * * @param contractAddress 合约地址 * @param to 收款地址 * @param value 转账金额 * @param privateKey 私钥 * @throws ExecutionException * @throws InterruptedException */ public void transferERC20Token(String contractAddress, String to, BigInteger value, String privateKey) throws ExecutionException, InterruptedException { //加载转账所需的凭证,用私钥 Credentials credentials = Credentials.create(privateKey); TokenERC20 contract = TokenERC20.load( contractAddress, web3j, credentials, Contract.GAS_PRICE, Contract.GAS_LIMIT); TransactionReceipt transactionReceipt = contract.transfer(to, value).sendAsync().get(); String transactionHash = transactionReceipt.getTransactionHash(); Log.i(TAG, "transferERC20Token transactionHash : " + transactionHash); } 注意:1、这里的TokenERC20是根据solidity智能合约生成的对应的Java类,用于java/Android和智能合约交互的,如果你对这里不太清楚,不妨看看我之前的一篇文章。以太坊Web3j命令行生成Java版本的智能合约2、如果加载合约失败,可能的一个原因是合约对应的Java类中的BINARY的值不对,这个值是你部署合约成功之后的bytecode,你最好检查对比一下。我发送一笔交易,可以通过这个地址查询https://rinkeby.etherscan.io/tx/0x05bd947e73068badbd9937854169f020980795da8a8182a67e9c2c1888f1874d
2023年03月03日
5 阅读
0 评论
0 点赞
1
...
18
19
20
...
23