Author Archives: admin

嘉源创业学院第一期 商业计划书的TRCMS五原则

这篇摘要是根据嘉源创业学院第一期内容整理,部分涉及项目隐私内容已删除,如果您在墨尔本,且对创业感兴趣,欢迎联系并加入我们,陪你一起改变世界!

微信:aubound,注释:加入创业群,并发送您的姓名,邮箱,创业状态(有想法 或 有团队 或是 有公司)

商业计划书Business Plan需要注意的五大要点,也叫TRCMS原则,即Team、Rationale、 Competitors、Money和Scale。在交流会中,主讲人为大家展示了去隐私信息的BP,让大家分别体会一下“好的”BP是如何表达的。

T,代表Team,也就是团队介绍。核心团队人数不宜过多,介绍要清晰且精炼,突出每位成员核心价值和在创业中的角色,避免无关信息。尽量使用数字来表达,切忌使用模糊的表达方法。此处的小技巧,欢迎入群交流。

R,代表Rationale,需要介绍市场需求分析,即回答为什么做这个产品,能够解决什么问题?创业者需要对行业有相当程度的了解,这部分描述需要文字简练,尽可能以图形和数字表达,适当引用行业数据,申明痛点和需求。关于真实BP案例,欢迎加入交流群讨论。

C代表Competitors,也就是竞品分析。这部分从另一个角度考察创业团队对行业的了解程度,通过分析竞争格局来体现项目的差异化和竞争优势。切忌用“前无古人后无来者”来体现该创业产品的价值,这对大多数VC来说,这样的表达可信度极低,而且非常有可能让VC认为你对行业不够了解。要通过客观且有针对性的分析,并以高亮关键词来帮助VC迅速找到重要信息。另一方面,尽量避免使用类似“我们的产品零缺点”的表述,这会让VC觉得你对产品的认知不够深刻和全面,或者没有想清楚。在座谈会中,列举了一些表达技巧来说明产品劣势的体现方法,让创业产品看起来更真实且丰满。

M代表Money,这部分谈到了融资金额和使用规划。在这里VC首先关注融资金额是否合理以及资金的用途是否清晰。关于企业估值、融资金额和股权比例,这部分内容比较重要,由于时间关系,将会在将来的单独安排座谈会大家交流。除此之外,资金使用时间(融资节奏)和退出方式也需要简单说明。可以使用图形来清晰的展示这些信息。期间钟博士还强调,投资人看重的并不是仅仅是项目目前的盈利能力,而更重要的是公司的估值增长。他还指出澳洲的VC更加关注创业者自己对项目是否有现金的投入,来衡量创业者对这份项目的投入和信心。

S代表Scale,也就是未来规划或者体量的变化。钟博士指出,这部分时间跨度要与M要点中的融资节奏保持一致,且跨度不宜太大,目标现实且要有想象空间,但切忌浮夸的数字。一般来说,第一个融资周期(通常8-12个月)内的小目标要写的比较详细,体现出团队对拿到资金以后的计划,而未来3年内的大计划可以简单介绍。

如果您想知道如何表达以上内容,欢迎参加由嘉源创新孵化器 主办的嘉源创业学院和交流会。详细信息,欢迎移步嘉源创新孵化器官方网站:

www.sinovation.com.au


首次置业者购房前需要了解的基本知识

每个买家都应该知道的基本房地产用语

抵押贷款保险(Mortgage Insurance)

借贷人支付的一种费用。在贷款违约时,用于保护贷款机构。通常,当贷款数目高于物业价格的80%时,会被有求购买保险。

物业转让(Property Transfer)

把物业从一个人转到另一个人的法律程序。产权转让师通常提供有关买卖合同的建议,调查产权,并协助买家履行法律义务。

冷静期(Cooling-off Period)

在购买后的一定天数内,买方可以取消交易。拍卖会上购买的房产没有冷静期。

对冲帐户(Offset Account)

与房屋贷款帐户相连的储蓄或交易帐户。余额可以抵消住房贷款余额,这意味着贷款机构只对两者之间的差额收取利息。

房屋净值(Home Equity)

指房屋市场价值与房屋贷款金额之间的差额。一些房屋所有者会用房屋净值来借更多的钱购买投资房产或支付翻修费用。

 

每个买家都应该知道的基本房地产用语

验房报告费用(Home Inspection Report Fee)

买房前的验房非常重要,需要留有预算。买家可花5000澳元左右进行尽职调查,便能得到全系列报告。进行适当的害虫和建筑检查十分必要。这是一笔小费用,但可以让买家避免购买到有结构缺陷或存在虫害的房产。首次置业者应该了解房屋结构的完整性。通过对建筑和害虫进行检查,首次置业者可发现房屋可能存在的问题,例如白蚁危害、墙体倾斜等。对此,房产专家建议,买家在将签订销售合同时,就应寻找有信誉的检查员立即进行建筑与害虫检查,并确保在签订合同前有足够的时间完成这项检查。据悉,一般该项检查所需的时间在14天至21天左右。此外,管理费用等的报告也十分重要,尤其是与特殊收费等相关的内容。

房产转让费用(Property Transfer Fee)

一些首次置业者会意外地发现需要聘请过户律师,或者对过户费用感到吃惊。房产转让费用因州而异。首次置业者应该在购买房屋流程开始时便找一位过户律师进行沟通。

向政府和银行缴纳的费用(Payment to Government and Bank)

弗拉维尔列出了首次置业者应该知道的一些政府收费,包括印花税、房产转让费、贷款注册费。在住房贷款方面,则还有贷款申请费、银行手续费及借贷机构的房产估值费用等。此外,还有一项潜在费用是借贷机构的贷款保险(Lender’s Mortgage Insurance),这种保险旨在保护放贷者(银行)。通常情况下,放贷者(银行)向借款人出借了房价的80%或更多资金时,就会向借款人收取贷款抵押保险费。

房屋清洁费用(Cleaning Fee)

首次置业者经常忘记预留专业清洁费用。当租户搬走时,并没有任何规定要求他们要将住宅清理干净。如果房产状态良好,但未达到出租标准,投资型买家可能需要聘请专业清洁人员进行清理,费用可能在500澳元以上。首次置业者买房用于自住或者出租,所需的清洁费存在较大差别。如果想要出租,房产最好呈现出更好的状态,清洁花费就会更高一些。

后院绿化和修复费用(Additional Renovation Fee)

波特建议买家预留4000澳元至5000澳元的维护保养资金。有时候,置业者买到的房产可能会出现没有热水、空调坏掉等情况,需要花钱更换。此外,后院保养费用可能也会令买家感到意外。植物修剪机、割草机等,加起来费用并不便宜。再者,景观美化的费用也不低,尤其是新建筑。

以上内容整理自网络,但编辑时有修改。如果您需要帮助,欢迎联系 盛世·嘉源地产

Aus Property Investment Group, Email at

info#apigroup.com.au

请将#替换为@

或致电全球统一接入热线:(+61)  1300 16 66 88


澳洲最全中文验房清单

验房的DIY清单,大家可以对照清单对自己喜欢的房屋做个初步评估。

P 代表适合买家自己看(1. 基本功能性)

BI  代表适合专业验房师查看

基本功能性相关

水电煤是否接通 P & BI
电器,暖气,冷气,热水是否正常工作 P & BI
测试水压 P & BI
观察排水 P & BI
检查灯开关和电源是否工作 P & BI
门窗是否正常闭合及密封性,锁头是否正常工作 P & BI
玻璃是否破裂 P & BI
柜体状况是否良好,门和抽屉操作是否正常 P & BI
外墙,内墙,天花板有无明显裂缝P & BI
马桶排污是否通畅,是否漏水 P & BI
有无烟雾报警器 P & BI
楼梯阳台栏杆有无松动 P & BI
车库门是否正常开关,遥控是否正常工作P & BI
自动灌溉系统是否正常工作 P & BI
中央吸尘系统是否正常工作 P & BI
钥匙是否齐全 P & BI

外部花园和车库

检查fence condition,包括sleeper, post有无白蚁侵蚀, wood decay BI
检查树对地基的影响 BI
检查车库结构状况,lintel condition, 密封性等 BI
检查drainage BI
检查decking, pergola 结构和安全性 BI
检查管道连接 BI

屋顶

检查dislodged tiles BI
检查瓦片破裂 BI
检查corrugated iron roof condition, nailedproperly BI
检查roof valley 和gutter 有无生锈和排水不畅 BI
检查flashing 是否正确安装 BI

外墙

检查有无明显裂缝 BI
通过裂缝的位置,大小,数量判断是否是地基下陷以及通过仪器测量下陷程度 BI
检查墙面垂直度 BI
检查mortar deterioration 的程度以及湿度测量 BI
检查是否是石棉材料 BI
检查construction joint(Albeflex, caulking) BI
检查防水密封程度 BI

地基

检查footing的结构支撑和连接包括brickpiles, bearer, joistBI
检查ant cap and DPCBI
检查wood decay, water damage BI
检查白蚁和borerBI检查通风和湿度 BI
检查是否有其他危险生物如蜥蜴,蛇 BI

内屋顶 (Roof Space)

检查屋顶的结构的condition, 支撑和连接,有无超负荷和下垂现象(Rafter, underpurlin, strut,hanger, beam) BI
检查有无脱木素,白蚁,water damage BI
检查有无破裂的瓦片,flashing gapsBI
检查管道有无破裂 BI
检查电线的老化程度和与insulation的距离是否在安全标准BI
检查屋顶是否有老鼠和其他生物如Possum BI

内墙

检查内墙湿度 BI
检查内墙垂直度 BI
检查内墙裂缝,通过裂缝的位置,大小,数量判断是否是结构问题 BI
检查承重墙偏斜度 BI

天花板和地板

检查天花板是否存在下陷,连接不牢和active rain stain BI
检查eave condition BI
检查地板是否有白蚁和borer侵蚀,松动 BI
检查地板的水平 BI

电源水暖系统

检查所有外露的管道配件是否有裂纹或泄漏 BI
用测试仪测试所有的电源插头 BI
进入roof space和subfloor检查管道和电线的老化程度 BI

门窗

检查门窗jamb properly fixed BI
检查门窗 jamb 有没有wood decay和白蚁 BI

浴室,厕所,厨房,洗衣房 (Wet Areas)

湿度仪测量柜体湿度 BI
热探测仪测试管道泄漏 BI
检查防水 BI

以上内容整理自网络,但编辑时有修改。如果您需要帮助,欢迎联系 盛世·嘉源地产

Aus Property Investment Group, Email at

info#apigroup.com.au

请将#替换为@

或致电全球统一接入热线:(+61)  1300 16 66 88


二手房屋购买中不可忽视的巨大隐患之一——石棉

房屋买卖中最容易被忽视的一部分 
石棉的前世今生石棉,是天然的纤维晶体状的6大硅酸盐类矿物质的总称;由于石棉的纤维柔软,具有绝缘、绝热、隔音、耐高温、耐酸碱、耐腐蚀和耐磨等特性,在商业、公共事业和工业设施中有相当多的用途,例如耐火的石棉纺织品、输水管、绝缘板等石棉水泥制品,及各种绝热材料等广泛的应用于建筑、电器、汽车、家庭用品等。另一方面,极其微小的石棉纤维飞散到空中,被吸入到人体的肺后,经过20到40年的潜伏期,很容易诱发肺尘病、肺癌等肺部疾病。这就是在世界各国受到不同程度关注的石棉公害问题。在欧洲,据预测到2020年因石棉公害引发的肺癌而致死的患者将达到50万人。而在日本,预测到2040年将有10万人因此死亡。

石棉在澳洲

Approximately one third of all homes built in Australia contain asbestos products. As a general rule, if your house was built before the mid-1980s, it is highly likely that it would have some asbestos containing materials. If your house was built between the mid-1980s and 1990, it is likely that it would have asbestos containing materials. If your house was built after 1990, it is unlikely that it would have asbestos containing materials.

https://asbestossafety.gov.au/asbestos-information

澳洲于1991年禁止含石棉材料的产品,但是仍有石棉建材在随后的几年里被使用在房屋建造上。直到2003年年底,澳洲全面禁止使用、生产石棉类或者含有石棉类的材料。

图片来源:https://www.asbestossafety.gov.au/history-asbestos-chronology

在维多利亚州,98%的1976年之前动工的房屋均含有石棉建材,而20%的房屋屋顶含有石棉材料。

石棉板不能收到激烈震荡,不然内部纤维会跑出来,类似的石棉瓦,在下雨尤其是下冰雹都会引起它的震动,激发里边的分子。我们前面讲到过,石棉纤维是严重的致癌物!

如何辨别建筑是否含有石棉

想要从外观上或者其他物理方法检测建筑是否含有石棉是很困难的。不过基本上20世纪80年代之前的建筑几乎都含有石棉材料。现有的检测办法只能通过实验室专业检测。任何房屋的维护、装修、拆除之前都必须通过石棉检测。

图片:房屋内可能存在石棉的位置

•Fibro and corrugated roof 屋顶瓦片中

•Insulation in the ceiling 天花板隔热层中

•Gutters & downpipes 屋顶的排水管和排气管

•Soffits 天花板表面

•Fibro walls 纤维墙面

•Fake brick walls 人造砖墙

•Fibro ceilings 纤维天花板

•Vermiculite (popcorn) ceilings蛭石天花板

•Kitchens厨房;

•Bathrooms卫生间

•Laundries洗衣房

•Water tanks水箱

•Flues烟道,通气道

•Pipes 管道

•Damp courses 潮湿的地方

•Vinyl floor coverings 地板覆盖层

•Carpet underlays 地毯衬垫物

•Electrical meter boards 电路板

•Floor 地板瓷砖

新房含有石棉材料吗

上文提过,2003年12月31日起,澳洲全面禁止生产、使用含有石棉的建材。进口、存储、供应、销售、组装、使用、再使用石棉材料在澳洲均是违法的!可惜的是,这条法律不适用于2003年前施工的建筑。

如何移除建筑内的石棉

唯一而正确的确定含有石棉的房屋是否安全,应由获得许可的石棉评估者做判断。这些专业石棉评估人员获得了各州和领地政府机构的认可

普通人凭肉眼很难分辨石棉。一旦发现房屋有石棉, 要记住一个原则,自己不要去惊动石棉,不要人为的震动,抖动,钻孔,切割,这样石棉的危害暂时可以降到最低,一定要请有执照的专业人士清除石棉。

图片为专业人士处理石棉瓦

视频:http://www.asbestoswise.com.au/information-and-resources/asbestos-in-the-home/

以上内容整理自网络,但编辑时有修改。如果您需要帮助,欢迎联系 盛世·嘉源地产

Aus Property Investment Group, Email at

info#apigroup.com.au

请将#替换为@

或致电全球统一接入热线:(+61)  1300 16 66 88


手把手教你如何看懂购房合同

在中国买房,买的是使用权。因为是楼房,以后没有任何改建的可能性,所以合同相对简单。而在澳洲,房子表面上都是一个样子,没什么差别。而实际上表面的一致之下隐藏着巨大的差别,玄机都隐藏在购房合同中。

通常说的合同分为两部分,contract of sale (CoS)和 section 32 (S32)。
contract of sale模版基本上一样,一般的律师事务所有自己的模版,但是都大同小异。最重要的条款包括:买方信息、价格、settlement (交割)日期、subject to finance (贷款批下为前提)(或者unconditional)还有subject to lease or not(有出租为前提)。这些信息页被称作particulars。particulars以后一般会是卖房律师起草的special condition (特殊条款),现在基本上每个合同都会有或多或少的特殊条款用来保护卖房人的利益,并且需要注意的是特殊条款是优先于普通条款的,就是说特殊条款和普通条款有差异的时候,以特殊条款为准。很常见的就是提高延迟settle的罚金利率或者其他费用。

Section 32 是了解房子的基础。基本上关于房子的一切信息都能够在Section 32 里直接得到或者找到线索。并且Section 32之后通常会附有attachments(附件),一般包括以下文件:

1.title search-可以理解为房产证上面的信息,比如有没有贷款,房主的基本信息,或者该房产有没有covenant

2.copy of plan -最新council审批通过的图纸

3.covenant (if applicable)- 对该土地使用的限制,做地产开发的需要特别留意这个

4.land information certificate - council对该土地的一些评估以及收费证明

5.water certificate - 水费证明

6.land tax certificate - 如果该房产是卖方的投资房,那么卖方就需要pay land tax, 这个是相关证明,如果你在年中购买了该房产,你需要承担settle后的那部分land tax。

7.owners corporation certificate -物业收费证明

8.certificate of currency -物业的保险证明

9.planning certificate -council 对该地产的规划信息,属于什么zone,是否包含overlay,这个对于投资或者重建非常重要

10.building certificate 1 - 过去十年council批准过对该地产改建的信息

11.building certificate 2 - council对该地产是否会遭受自然灾害影响的评估,比如洪水,白蚁等

12.roads certificate - 关于未来道路发展,是否影响该地产的证明

13.bushfire - 是否在山火影响范围内

关于section32,还有几点需要注意并了解

Easement (政府管道等使用权)

这个指的是其他人(包括政府)由于特殊的原因有权使用你的房产,或您自己房产的一部分可能会被限制使用。

Easement其实包含了很多方面。有的朋友简单地认为easement就是简单的指排污管道(sewage),但实际上排污管(sewage)只是其中的一部分。首先优先级最高的是政府用途,比如排水管、电线、煤气等等。接下来就是不能阻挡邻居(比如邻居没有自己的出入车道drive way,那你就不能改这条车道,还有你后来加建或者改建也不可以妨碍邻居的光照和视线)。

植被保护(Overlay)

很多人认为土地是我的,那我有权处理土地上的任何花花草草还有树木。但实际上作为世界上环境保护最好的国家之一,澳大利亚有着严格的动植物保护法律。尤其是买来将来准备做开发的朋友,一定要注意section 32里面的overlay这一项,看看土地上有没有受保护的植物。如果有的话就基本意味着这幢房子很难被推到重建或者盖成2-3个独立的townhouse。就算没有overlay,有的区对于植物的保护特别严格,不可以随便砍伐树木,一定要获得council(市政厅)批准。而且有的区就算council允许你砍树,也会要求你再重新种一棵同样大小的树木。

有无申请过建筑许可(Building Permit)

实际上对于自住来说这一项无关紧要,可是如果想要开发的朋友就要好好看一下了。比如一块900平米的土地,可能正常是可以开发3套townhouse的。

但是!
不同的区对于人口的需求是不同的。大家一定要看仔细section 32上面写的你的区的性质是什么:
Mixed Use Zone (MUZ) —— 这个代表着这个区域会是高速发展的,有商业,有住宅。那如果你的房子地理位置很好的话,将来可能会被用作商业用地,那价值的增长将会是成倍的。
Residential Growth Zone (RGZ) —— 这个代表政府鼓励这个区域人口高速增长,所以同样的一块地,在这个区域可能比在正常的住宅区多盖一幢房子,而且也有更高几率盖超过两层的房子。那相应的收益也会提高,开发价值就会特别高。
General Residential Zone (GRZ) —— 这个代表的是普通住宅区,下面还细分为zone 1、2、3、4、5这样,每一个数字代表的要求也不同,总的来说从开发价值角度来讲,1,5比较好。
Neighbourhood Residential Zone (NRZ) —— 曾经这个代表此区域会被重点保护,人口增长是被严格限制的。一般老牌的富人区或者重点环境保护的区会是这种。但在2017年政府变更了一次政策,允许NRZ地区进行不限量分割,但有些限制最小分割面积。

Covenant (契约)

这个一般指的是很早之前屋主与邻居的一个约定。因为历史很长,所以有些用的都不是现代英语,所以如果在section 32上面看到covenant这个词,大家一定要打起十二分的精神,找律师或者专业人士去帮你看一下,看看有没有可能会损害你利益的条款。
澳洲买房和中国很不一样,因为牵扯到土地,所以条款会比较多。大家在看section 32的时候一定要擦亮眼睛,即使不认识英文,看到我们上面提到的这些词也可以大概明白一些意思,避免一些将来不必要的麻烦。

 

以上内容整理自网络,但编辑时有修改。如果您有兴趣参与投资开发,

欢迎联系 盛世·嘉源地产

Aus Property Investment Group, Email at

info#apigroup.com.au

请将#替换为@

或致电全球统一接入热线:(+61)  1300 16 66 88


澳洲不同房产类别横向评测,到底哪种适合你?

原创文章,最早发布于2016年,但现在仍然有一定的参考价值。转载必须注明来自钟坚-澳洲嘉源地产 新浪博客,微博 http://weibo.com/uc288168

客户经常会问,“我投资哪种房产好?公寓还是别墅?”这是一个非常普遍的问题。
澳洲房产广义上分为公寓、联排别墅、独栋别墅。而从我们投资角度来说,我们会分的更细致一些。以下以墨尔本为例说明。

公寓分为:
CBD和大CBD公寓;
自住公寓,如Docklands;
学区富人区公寓;

联排分为:
semi-detached联排,只有一堵墙和别人共用;
大联排,左右墙都和别人共用;
unit,后院独立,前院共用,车库和别人相连或者不相连;

独栋别墅:
单层独栋
双层独栋

如此多的分类,到底您适合哪种呢?下面我们以您的目的来进行推荐:
1. 如果你是为了抵税,比如你在澳洲有一份薪水不错的工作,不论是自营还是打工,这个时候,首选则是新房或楼花公寓。公寓拥有更为简单的管理方式,新房具备更高的折价。当抵税若干年后,公寓的租金也可以成为一笔不小的收入,可谓是养老非常不错的选择。

那么该选择哪里的公寓呢?个人建议首选学区和富人区的公寓。这点和国内并不相同。国内人喜欢CBD中心,北京的二环三环价值不菲,但澳洲不同,因为澳洲人的生活习惯和国内并不相同,他们不喜欢扎堆住在人口密集的CBD,而喜欢郊区的别墅。而年轻的当地人因为资金不足,则不会选择别墅,只好选择公寓,这个时候,富人区的公寓就成为了当地人的首选。这也就是为什么郊区的公寓增长率高于CBD公寓的其中一个原因。

另外,学区公寓,是一个保证公寓出租的稳定的基石,可以让房子几乎不会空租。

那么CBD公寓将会如何发展呢?墨尔本CBD公寓正在以非常迅猛的速度建设,看到2014年规划以后,笔者也吓了一跳。尽管2014的房市非常火热,但如此的项目数量仍然让人咂舌。预计,2014年,CBD将有1万套公寓上线销售,这批公寓,包含去年的,将集中在2016年~2018年竣工,届时,将有大量的公寓出现在CBD,将对出租市场造成一个不小的冲击。尽管,CBD寸土寸金,资源有用完的一天,之后价格必定上扬。然而,这需要等待非常久远,而非10年可以完成。

所以,我们以保守推荐,学区和富人区交叠地区的公寓,是避税人士的首选。

2. 初级纯投资者,追求简单的投资方式,还没有在澳洲持有地产。
这种投资者,我们同样建议选择公寓。可以通过投资公寓的方式,了解澳洲投资流程和相关政策。具有低风险,中回报的优势。

推荐学区和富人区重叠的区域。

3. 中级投资者,已经在澳洲持有公寓投资,希望拥有更大回报。
这种投资者建议选择独栋别墅。已有的公寓将会逐渐呈现越来越大的正现金流,可以用于抵消别墅出租时付现金流,达到以房养房的目的。

众所周知,房产升值主要来自于地皮价值的升值,上面的房子是贬值的。也就是说,房价上涨,其实是地皮上涨减去房子折价后剩余的部分,如果是正的,则为“增值”,付的则为“贬值”。

这里我们强调价值,就是要让大家注意,公寓也有地皮价值,这也是公寓增值的原因。

为什么要投资别墅呢?澳洲房价除了水涨船高之外,别墅价值跳高来自于其可再分割的属性。这个属性是一个随着时间而出现的增值属性,有些二手房产一开始就携带这种属性,有些则是需要若干年后出现。

举例:一个普通别墅购买时55万,假设若干年后,平均房价涨到80万。如果这个别墅带有可分割属性,那么,其带批文价格可以根据市场热度卖到90~120万。这额外的10~40万就是可分割带来的额外的价值。

选择别墅时,一定要注意不可复制的地区优势,也就是环境优势。很多房价飙升,就是因为其不可复制的优势。比如Brighton海边别墅,学区别墅等。

4. 高级投资者,已经拥有公寓,也已经投资别墅。
这类投资者,我建议你不在选择投资购买,而进行开发。我们会帮您选择合适的开发土地,给您一个详尽的可行性报告。

开发的回报率远高于普通购买投资,这里不再详述,我们有专门的子公司负责项目开发。

如果您需要帮助,欢迎联系 盛世·嘉源地产

Aus Property Investment Group, Email at

info#apigroup.com.au

请将#替换为@

或致电全球统一接入热线:(+61)  1300 16 66 88


以太坊智能合约 —— 最佳安全开发指南

1

基本理念

以太坊和其他复杂的区块链项目都处于早期阶段并且有很强的实验性质。因此,随着新的bug和安全漏洞被发现,新的功能不断被开发出来,其面临的安全威胁也是不断变化的。这篇文章对于开发人员编写安全的智能合约来说只是个开始。

开发智能合约需要一个全新的工程思维,它不同于我们以往项目的开发。因为它犯错的代价是巨大的,并且很难像传统软件那样轻易的打上补丁。就像直接给硬件编程或金融服务类软件开发,相比于web开发和移动开发都有更大的挑战。因此,仅仅防范已知的漏洞是不够的,你还需要学习新的开发理念:

对可能的错误有所准备。任何有意义的智能合约或多或少都存在错误。因此你的代码必须能够正确的处理出现的bug和漏洞。始终保证以下规则: – 当智能合约出现错误时,停止合约,(“断路开关”) – 管理账户的资金风险(限制(转账)速率、最大(转账)额度)

  • 有效的途径来进行bug修复和功能提升

谨慎发布智能合约。尽量在正式发布智能合约之前发现并修复可能的bug。 – 对智能合约进行彻底的测试,并在任何新的攻击手法被发现后及时的测试(包括已经发布的合约) – 从alpha版本在测试网(testnet)上发布开始便提供bug赏金计划

  • 阶段性发布,每个阶段都提供足够的测试

保持智能合约的简洁。复杂会增加出错的风险。

  • 确保智能合约逻辑简洁
  • 确保合约和函数模块化
  • 使用已经被广泛使用的合约或工具(比如,不要自己写一个随机数生成器)
  • 条件允许的话,清晰明了比性能更重要
  • 只在你系统的去中心化部分使用区块链

保持更新。通过下一章节所列出的资源来确保获取到最新的安全进展。

  • 在任何新的漏洞被发现时检查你的智能合约
  • 尽可能快的将使用到的库或者工具更新到最新
  • 使用最新的安全技术

清楚区块链的特性。尽管你先前所拥有的编程经验同样适用于以太坊开发,但这里仍然有些陷阱你需要留意:

  • 特别小心针对外部合约的调用,因为你可能执行的是一段恶意代码然后更改控制流程
  • 清楚你的public function是公开的,意味着可以被恶意调用。(在以太坊上)你的private data也是对他人可见的
  • 清楚gas的花费和区块的gas limit

基本权衡:简单性与复杂性

在评估一个智能合约的架构和安全性时有很多需要权衡的地方。对任何智能合约的建议是在各个权衡点中找到一个平衡点。

从传统软件工程的角度出发:一个理想的智能合约首先需要模块化,能够重用代码而不是重复编写,并且支持组件升级。从智能合约安全架构的角度出发同样如此,模块化和重用被严格审查检验过的合约是最佳策略,特别是在复杂智能合约系统里。

然而,这里有几个重要的例外,它们从合约安全和传统软件工程两个角度考虑,所得到的重要性排序可能不同。当中每一条,都需要针对智能合约系统的特点找到最优的组合方式来达到平衡。

  • 固化 vs 可升级
  • 庞大 vs 模块化
  • 重复 vs 可重用

固化 vs 可升级

在很多文档或者开发指南中,包括该指南,都会强调延展性比如:可终止,可升级或可更改的特性,不过对于智能合约来说,延展性和安全之间是个基本权衡。

延展性会增加程序复杂性和潜在的攻击面。对于那些只在特定的时间段内提供有限的功能的智能合约,简单性比复杂性显得更加高效,比如无管治功能,有限短期内使用的代币发行的智能合约系统(governance-fee,finite-time-frame token-sale contracts)。

庞大 vs 模块化

一个庞大的独立的智能合约把所有的变量和模块都放到一个合约中。尽管只有少数几个大家熟知的智能合约系统真的做到了大体量,但在将数据和流程都放到一个合约中还是享有部分优点–比如,提高代码审核(code review)效率。

和在这里讨论的其他权衡点一样,传统软件开发策略和从合约安全角度出发考虑,两者不同主要在对于简单、短生命周期的智能合约;对于更复杂、长生命周期的智能合约,两者策略理念基本相同。

重复 vs 可重用

从软件工程角度看,智能合约系统希望在合理的情况下最大程度地实现重用。 在Solidity中重用合约代码有很多方法。 使用你拥有的以前部署的经过验证的智能合约是实现代码重用的最安全的方式。

在以前所拥有已部署智能合约不可重用时重复还是很需要的。 现在Live Libs 和Zeppelin Solidity 正寻求提供安全的智能合约组件使其能够被重用而不需要每次都重新编写。任何合约安全性分析都必须标明重用代码,特别是以前没有建立与目标智能合同系统中处于风险中的资金相称的信任级别的代码。

2

安全通知

以下这些地方通常会通报在Ethereum或Solidity中新发现的漏洞。安全通告的官方来源是Ethereum Blog,但是一般漏洞都会在其他地方先被披露和讨论。

Ethereum Blog: The official Ethereum blog

(地址:blog.ethereum.org/

  • Ethereum Blog – Security only: 所有相关博客都带有Security标签

blog.ethereum.org/categ

Ethereum Gitter 聊天室

(地址:gitter.im/ethereum/home

Reddit(地址:reddit.com/r/ethereum/

Network Stats(地址:ethstats.net/

强烈建议你经常浏览这些网站,尤其是他们提到的可能会影响你的智能合约的漏洞。

另外, 这里列出了以太坊参与安全模块相关的核心开发成员, 浏览 bibliography 获取更多信息。

(地址:github.com/ConsenSys/sm

  • Vitalik Buterin: Twitter, Github, Reddit, Ethereum Blog
  • Dr. Christian Reitwiessner: Twitter, Github, Ethereum Blog
  • Dr. Gavin Wood: Twitter, Blog, Github
  • Vlad Zamfir: Twitter, Github, Ethereum Blog

除了关注核心开发成员,参与到各个区块链安全社区也很重要,因为安全漏洞的披露或研究将通过各方进行。

3

关于使用Solidity开发的智能合约安全建议

外部调用

尽量避免外部调用

调用不受信任的外部合约可能会引发一系列意外的风险和错误。外部调用可能在其合约和它所依赖的其他合约内执行恶意代码。因此,每一个外部调用都会有潜在的安全威胁,尽可能的从你的智能合约内移除外部调用。当无法完全去除外部调用时,可以使用这一章节其他部分提供的建议来尽量减少风险。

仔细权衡“send()”、“transfer()”、以及“call.value()”

当转账Ether时,需要仔细权衡

“someAddress.send()”、“someAddress.transfer()”、和“someAddress.call.value()()”之间的差别。

  • x.transfer(y)和if (!x.send(y)) throw;是等价的。send是transfer的底层实现,建议尽可能直接使用transfer。
  • someAddress.send()和someAddress.transfer() 能保证可重入 安全 。尽管这些外部智能合约的函数可以被触发执行,但补贴给外部智能合约的2,300 gas,意味着仅仅只够记录一个event到日志中。
  • someAddress.call.value()() 将会发送指定数量的Ether并且触发对应代码的执行。被调用的外部智能合约代码将享有所有剩余的gas,通过这种方式转账是很容易有可重入漏洞的,非常 不安全。

使用send() 或transfer() 可以通过制定gas值来预防可重入, 但是这样做可能会导致在和合约调用fallback函数时出现问题,由于gas可能不足,而合约的fallback函数执行至少需要2,300 gas消耗。

一种被称为push 和pull的机制试图来平衡两者, 在 push 部分使用send() 或transfer(),在pull 部分使用call.value()()。

(*译者注:在需要对外未知地址转账Ether时使用send() 或transfer(),已知明确内部无恶意代码的地址转账Ether使用call.value()())

需要注意的是使用send() 或transfer() 进行转账并不能保证该智能合约本身重入安全,它仅仅只保证了这次转账操作时重入安全的。

处理外部调用错误

Solidity提供了一系列在raw address上执行操作的底层方法,比如:

address.call(),address.callcode(), address.delegatecall()和address.send。

这些底层方法不会抛出异常(throw),只是会在遇到错误时返回false。

另一方面, contract calls (比如,

ExternalContract.doSomething())会自动传递异常,(比如,

doSomething()抛出异常,那么ExternalContract.doSomething() 同样会进行throw )。

如果你选择使用底层方法,一定要检查返回值来对可能的错误进行处理。

 

[codesyntax lang=”php”]

// bad
someAddress.send(55);

someAddress.call.value(55)(); 
// this is doubly dangerous, as it will forward all remaining gas and doesn'tcheck for result

someAddress.call.value(100)(bytes4(sha3("deposit()"))); 
// if deposit throws an exception, the raw call() will only return false and transaction will NOT be reverted

[/codesyntax]

 

 

[codesyntax lang=”php”]

// good

if(!someAddress.send(55)) {

// Some failurecode

}

ExternalContract(someAddress).deposit.value(100);

[/codesyntax]

 

不要假设你知道外部调用的控制流程

无论是使用raw calls 或是contract calls,如果这个ExternalContract是不受信任的都应该假设存在恶意代码。即使ExternalContract不包含恶意代码,但它所调用的其他合约代码可能会包含恶意代码。一个具体的危险例子便是恶意代码可能会劫持控制流程导致竞态。

(浏览Race Conditions获取更多关于这个问题的讨论,

地址:github.com/ConsenSys/sm

对于外部合约优先使用pull 而不是push

外部调用可能会有意或无意的失败。为了最小化这些外部调用失败带来的损失,通常好的做法是将外部调用函数与其余代码隔离,最终是由收款发起方负责发起调用该函数。这种做法对付款操作尤为重要,比如让用户自己撤回资产而不是直接发送给他们。(译者注:事先设置需要付给某一方的资产的值,表明接收方可以从当前账户撤回资金的额度,然后由接收方调用当前合约提现函数完成转账)。

(这种方法同时也避免了造成 gas limit相关问题。

地址:github.com/ConsenSys/sm

 

[codesyntax lang=”php”]

// bad
contract auction {
address highestBidder;
uint highestBid; function bid() payable {

if (msg.value < highestBid) throw;

if (highestBidder != 0) {

if

(!highestBidder.send(highestBid)) { // if

this call consistently fails, no one else can bid
throw;
}
}

highestBidder = msg.sender;
highestBid = msg.value;
}
}

[/codesyntax]

 

 

[codesyntax lang=”php”]

// good
contract auction {
address highestBidder;
uint highestBid;
mapping(address => uint) refunds; function bid() payable external {

if (msg.value < highestBid) throw;

if (highestBidder != 0) {
refunds[highestBidder] +=

highestBid; // record the refund that this user can claim
}

highestBidder = msg.sender;
highestBid = msg.value;
}

function withdrawRefund() external {
uint refund = refunds[msg.sender];
refunds[msg.sender] = 0; if (!msg.sender.send(refund)) {
refunds[msg.sender] = refund; //

reverting state because send failed
}
}
}

[/codesyntax]

 

标记不受信任的合约

当你自己的函数调用外部合约时,你的变量、方法、合约接口命名应该表明和他们可能是不安全的。

 

[codesyntax lang=”php”]

// bad
Bank.withdraw(100); // Unclear whether

trusted or untrusted

function makeWithdrawal(uint amount) { //

Isn't clear that this function is potentially unsafe Bank.withdraw(amount);

}

[/codesyntax]

 

 

[codesyntax lang=”php”]

// good

UntrustedBank.withdraw(100); // untrusted external call

TrustedBank.withdraw(100); // external but trusted bank contract maintained by XYZ Corp

function makeUntrustedWithdrawal(uint amount) { UntrustedBank.withdraw(amount);

}

[/codesyntax]

 

使用assert()强制不变性

当断言条件不满足时将触发断言保护 — 比如不变的属性发生了变化。举个例子,代币在以太坊上的发行比例,在代币的发行合约里可以通过这种方式得到解决。断言保护经常需要和其他技术组合使用,比如当断言被触发时先挂起合约然后升级。(否则将一直触发断言,你将陷入僵局)

例如:

 

[codesyntax lang=”php”]

contract Token {
mapping(address => uint) public balanceOf;
uint public totalSupply;

function deposit() public payable {
balanceOf[msg.sender] += msg.value;
totalSupply += msg.value;
assert(this.balance >= totalSupply);
}
}

[/codesyntax]

 

注意断言保护 不是 严格意义的余额检测, 因为智能合约可以不通过deposit() 函数被 强制发送Ether!

正确使用assert()和require()

在Solidity 0.4.10 中assert()和require()被加入。require(condition)被用来验证用户的输入,如果条件不满足便会抛出异常,应当使用它验证所有用户的输入。

assert(condition) 在条件不满足也会抛出异常,但是最好只用于固定变量:内部错误或你的智能合约陷入无效的状态。遵循这些范例,使用分析工具来验证永远不会执行这些无效操作码:意味着代码中不存在任何不变量,并且代码已经正式验证。

小心整数除法的四舍五入

所有整数除数都会四舍五入到最接近的整数。 如果您需要更高精度,请考虑使用乘数,或存储分子和分母。

(将来Solidity会有一个fixed-point类型来让这一切变得容易。)

 

[codesyntax lang=”php”]

// bad
uint x = 5 / 2; // Result is 2, all integer

divison rounds DOWN to the nearest integer

[/codesyntax]

 

 

[codesyntax lang=”php”]

// good
uint multiplier = 10;

uint x = (5 * multiplier) / 2;

uint numerator = 5;

uint denominator = 2;

[/codesyntax]

 

记住Ether可以被强制发送到账户

谨慎编写用来检查账户余额的不变量。

攻击者可以强制发送wei到任何账户,而且这是不能被阻止的(即使让fallback函数throw也不行)

攻击者可以仅仅使用1 wei来创建一个合约,然后调用selfdestruct(victimAddress)。在victimAddress中没有代码被执行,所以这是不能被阻止的。

不要假设合约创建时余额为零

攻击者可以在合约创建之前向合约的地址发送wei。合约不能假设它的初始状态包含的余额为零。浏览issue 61 获取更多信息。

(地址:github.com/ConsenSys/sm

记住链上的数据是公开的

许多应用需要提交的数据是私有的,直到某个时间点才能工作。游戏(比如,链上游戏rock-paper-scissors(石头剪刀布))和拍卖机(比如,sealed-bid second-price auctions)是两个典型的例子。如果你的应用存在隐私保护问题,一定要避免过早发布用户信息。

例如:

  • 在游戏石头剪刀布中,需要参与游戏的双方提交他们“行动计划”的hash值,然后需要双方随后提交他们的行动计划;如果双方的“行动计划”和先前提交的hash值对不上则抛出异常。
  • 在拍卖中,要求玩家在初始阶段提交其所出价格的hash值(以及超过其出价的保证金),然后在第二阶段提交他们所出价格的资金。
  • 当开发一个依赖随机数生成器的应用时,正确的顺序应当是(1)玩家提交行动计划,(2)生成随机数,(3)玩家支付。产生随机数是一个值得研究的领域;当前最优的解决方案包括比特币区块头(通过btcrelay.org验证),hash-commit-reveal方案(比如,一方产生number后,将其散列值提交作为对这个number的“提交”,然后在随后再暴露这个number本身)和 RANDAO。
  • 如果你正在实现频繁的批量拍卖,那么hash-commit机制也是个不错的选择。

权衡Abstract合约和Interfaces

Interfaces和Abstract合约都是用来使智能合约能更好的被定制和重用。Interfaces是在Solidity 0.4.11中被引入的,和Abstract合约很像但是不能定义方法只能申明。

Interfaces存在一些限制比如不能够访问storage或者从其他Interfaces那继承,通常这些使Abstract合约更实用。尽管如此,Interfaces在实现智能合约之前的设计智能合约阶段仍然有很大用处。另外,需要注意的是如果一个智能合约从另一个Abstract合约继承而来那么它必须实现所有Abstract合约内的申明并未实现的函数,否则它也会成为一个Abstract合约。

在双方或多方参与的智能合约中,参与者可能会“脱机离线”后不再返回

不要让退款和索赔流程依赖于参与方执行的某个特定动作而没有其他途径来获取资金。比如,在石头剪刀布游戏中,一个常见的错误是在两个玩家提交他们的行动计划之前不要付钱。然而一个恶意玩家可以通过一直不提交它的行动计划来使对方蒙受损失 — 事实上,如果玩家看到其他玩家泄露的行动计划然后决定他是否会损失(译者注:发现自己输了),那么他完全有理由不再提交他自己的行动计划。这些问题也同样会出现在通道结算。当这些情形出现导致问题后:(1)提供一种规避非参与者和参与者的方式,可能通过设置时间限制,和(2)考虑为参与者提供额外的经济激励,以便在他们应该这样做的所有情况下仍然提交信息。

使Fallback函数尽量简单

Fallback函数在合约执行消息发送没有携带参数(或当没有匹配的函数可供调用)时将会被调用,而且当调用 .send() or .transfer()时,只会有2,300 gas 用于失败后fallback函数的执行(译者注:合约收到Ether也会触发fallback函数执行)

如果你希望能够监听.send()或.transfer()接收到Ether,则可以在fallback函数中使用event(译者注:让客户端监听相应事件做相应处理)。谨慎编写fallback函数以免gas不够用。

 

[codesyntax lang=”php”]

// bad

function() payable { balances[msg.sender] += msg.value; }

[/codesyntax]

 

 

[codesyntax lang=”php”]

// good

function deposit() payable external { balances[msg.sender] += msg.value; }

function() payable { LogDepositReceived(msg.sender); }

[/codesyntax]

 

明确标明函数和状态变量的可见性

明确标明函数和状态变量的可见性。函数可以声明为 external,public, internal 或 private。

分清楚它们之间的差异,例如external 可能已够用而不是使用 public。对于状态变量,external是不可能的。明确标注可见性将使得更容易避免关于谁可以调用该函数或访问变量的错误假设。

 

[codesyntax lang=”php”]

// bad
uint x; // the default is private for state variables, but it should be made explicit

functionbuy() { // the default is public

// public code
}

[/codesyntax]

 

 

[codesyntax lang=”php”]

// good
uint private y;

function buy() external {
// only callable externally
}

function utility() public {
// callable externally, as well as internally: changing this code requires thinking about both cases.
}

function internalAction() internal {
// internal code
}

[/codesyntax]

 

将程序锁定到特定的编译器版本

智能合约应该应该使用和它们测试时使用最多的编译器相同的版本来部署。锁定编译器版本有助于确保合约不会被用于最新的可能还有bug未被发现的编译器去部署。智能合约也可能会由他人部署,而pragma标明了合约作者希望使用哪个版本的编译器来部署合约。

[codesyntax lang=”php”]

// bad
pragma solidity ^0.4.4;

[/codesyntax]

[codesyntax lang=”php”]

// good
pragma solidity 0.4.4;

[/codesyntax]

(译者注:这当然也会付出兼容性的代价)

小心分母为零 (Solidity < 0.4)

早于0.4版本, 当一个数尝试除以零时,Solidity 返回zero 并没有 throw 一个异常。确保你使用的Solidity版本至少为 0.4。

区分函数和事件

为了防止函数和事件(Event)产生混淆,命名一个事件使用大写并加入前缀(我们建议LOG)。对于函数, 始终以小写字母开头,构造函数除外。

 

[codesyntax lang=”php”]

// bad
event Transfer() {}

function transfer() {}

[/codesyntax]

 

 

[codesyntax lang=”php”]

// good
event LogTransfer() {}

function transfer() external {}

[/codesyntax]

 

使用Solidity更新的构造器

更合适的构造器/别名,如selfdestruct(旧版本为’suicide)和keccak256(旧版本为sha3)。

像require(msg.sender.send(1 ether))“的模式也可以简化为使用transfer(),如`msg.sender.transfer(1 ether)`。

4

已知的攻击

竞态*

调用外部契约的主要危险之一是它们可以接管控制流,并对调用函数意料之外的数据进行更改。 这类bug有多种形式,导致DAO崩溃的两个主要错误都是这种错误。

重入

这个版本的bug被注意到是其可以在第一次调用这个函数完成之前被多次重复调用。对这个函数不断的调用可能会造成极大的破坏。

 

[codesyntax lang=”php” lines=”normal”]

// INSECURE
mapping (address => uint) private

userBalances;

function withdrawBalance() public {
uint amountToWithdraw =

userBalances[msg.sender]; if (!(msg.sender.call.value(amountToWithdraw)())) { throw; } // At this point, the caller's code is executed, and can call withdrawBalance again userBalances[msg.sender] = 0;

}

[/codesyntax]

 

(译者注:使用msg.sender.call.value()())传递给fallback函数可用的气是当前剩余的所有气,在这里,假如从你账户执行提现操作的恶意合约的fallback函数内递归调用你的withdrawBalance()便可以从你的账户转走更多的币。)

可以看到当调msg.sender.call.value()()时,并没有将userBalances[msg.sender] 清零,于是在这之前可以成功递归调用很多次withdrawBalance()函数。 一个非常相像的bug便是出现在针对 DAO 的攻击。

在给出来的例子中,最好的方法是:使用 send() 而不是call.value()()。这将避免多余的代码被执行。

然而,如果你没法完全移除外部调用,另一个简单的方法来阻止这个攻击是确保你在完成你所有内部工作之前不要进行外部调用:

 

[codesyntax lang=”php” lines=”fancy”]

mapping (address => uint) private userBalances;

functionwithdrawBalance() public {

uint amountToWithdraw = userBalances[msg.sender];
userBalances[msg.sender] = 0; if (!(msg.sender.call.value(amountToWithdraw)())) { throw; } // The user's balance is already 0, so future invocations won't withdraw anything
}

[/codesyntax]

 

注意如果你有另一个函数也调用了 withdrawBalance(), 那么这里潜在的存在上面的攻击,所以你必须认识到任何调用了不受信任的合约代码的合约也是不受信任的。继续浏览下面的相关潜在威胁解决办法的讨论。

跨函数竞态

攻击者也可以使用两个共享状态变量的不同的函数来进行类似攻击。

 

[codesyntax lang=”php” lines=”normal”]

// INSECURE
mapping (address => uint) private userBalances;

function transfer(address to, uint amount) {

if (userBalances[msg.sender] >= amount) {
userBalances[to] += amount;
userBalances[msg.sender] -= amount;
}
}

function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender]; if (!(msg.sender.call.value(amountToWithdraw)())) { throw; } // At this point, the caller's code is executed, and can call transfer() userBalances[msg.sender] = 0;

}

[/codesyntax]

 

这个例子中,攻击者在他们外部调用withdrawBalance函数时调用transfer(),如果这个时候withdrawBalance还没有执行到userBalances[msg.sender] = 0;这里,那么他们的余额就没有被清零,那么他们就能够调用transfer()转走代币尽管他们其实已经收到了代币。这个弱点也可以被用到对DAO的攻击。

同样的解决办法也会管用,在执行转账操作之前先清零。也要注意在这个例子中所有函数都是在同一个合约内。然而,如果这些合约共享了状态,同样的bug也可以发生在跨合约调用中。

竞态解决办法中的陷阱

由于竞态既可以发生在跨函数调用,也可以发生在跨合约调用,任何只是避免重入的解决办法都是不够的。

作为替代,我们建议首先应该完成所有内部的工作然后再执行外部调用。这个规则可以避免竞态发生。然而,你不仅应该避免过早调用外部函数而且应该避免调用那些也调用了外部函数的外部函数。例如,下面的这段代码是不安全的:

 

[codesyntax lang=”php” lines=”fancy”]

// INSECURE
mapping (address => uint) private userBalances;

mapping (address => bool) private claimedBonus;

mapping (address => uint) private rewardsForA;

functionwithdraw(address recipient) public {

uint amountToWithdraw = userBalances[recipient];
rewardsForA[recipient] = 0; if (!(recipient.call.value(amountToWithdraw)())) { throw; }

}

function getFirstWithdrawalBonus(address recipient) public {

if (claimedBonus[recipient]) { throw; } // Each recipient should only be able to claim the bonus once

rewardsForA[recipient] += 100;
withdraw(recipient); // At this point, the caller will be able to execute getFirstWithdrawalBonus again.
claimedBonus[recipient] = true;

}

[/codesyntax]

 

尽管getFirstWithdrawalBonus() 没有直接调用外部合约,但是它调用的withdraw() 却会导致竞态的产生。在这里你不应该认为withdraw()是受信任的。

 

[codesyntax lang=”php” lines=”normal”]

mapping (address => uint) private userBalances;

mapping (address => bool) private claimedBonus;

mapping (address => uint) private rewardsForA;

function untrustedWithdraw(address recipient) public {
uint amountToWithdraw = userBalances[recipient];
rewardsForA[recipient] = 0; if (!(recipient.call.value(amountToWithdraw)())) { throw; }
}

function untrustedGetFirstWithdrawalBonus(address recipient) public {

if (claimedBonus[recipient]) { throw; } // Each recipient should only be able to claim the bonus once

claimedBonus[recipient] = true;
rewardsForA[recipient] += 100;
untrustedWithdraw(recipient); // claimedBonus has been set to true, so reentry is impossible
}

[/codesyntax]

 

除了修复bug让重入不可能成功,不受信任的函数也已经被标记出来 。

同样的情景:untrustedGetFirstWithdrawalBonus()调用untrustedWithdraw(), 而后者调用了外部合约,因此在这里untrustedGetFirstWithdrawalBonus() 是不安全的。

另一个经常被提及的解决办法是(译者注:像传统多线程编程中一样)使用mutex。它会”lock” 当前状态,只有锁的当前拥有者能够更改当前状态。一个简单的例子如下:

 

[codesyntax lang=”php” lines=”normal”]

// Note: This is a rudimentary example, and mutexes are particularly useful where there is substantial logic and/or shared state
mapping (address => uint) private balances;

bool private lockBalances;

function deposit() payable public returns (bool) {

if (!lockBalances) {
lockBalances = true;
balances[msg.sender] += msg.value;
lockBalances = false; return true;
}

throw;

}

function withdraw(uint amount) payable public returns (bool) {

if (!lockBalances && amount > 0 && balances[msg.sender] >= amount) {
lockBalances = true; if (msg.sender.call(amount)()) { // Normally insecure, but the mutex saves it
balances[msg.sender] -= amount;
}

lockBalances = false; return true;
}

throw;

}

[/codesyntax]

 

如果用户试图在第一次调用结束前第二次调用 withdraw(),将会被锁住。 这看上去很有效果,但当你使用多个合约互相交互时问题变得严峻了。 下面是一段不安全的代码:

 

[codesyntax lang=”php” lines=”normal”]

// INSECURE
contract StateHolder {
uint private n;
address private lockHolder;

function getLock() {
if (lockHolder != 0) { throw; }
lockHolder = msg.sender;
}

function releaseLock() {
lockHolder = 0;
}

function set(uint newState) {
if (msg.sender != lockHolder) { throw; }
n = newState;
}
}

[/codesyntax]

 

攻击者可以只调用getLock(),然后就不再调用 releaseLock()。如果他们真这样做,那么这个合约将会被永久锁住,任何接下来的操作都不会发生了。如果你使用mutexs来避免竞态,那么一定要确保没有地方能够打断锁的进程或绝不释放锁。(这里还有一个潜在的威胁,比如死锁和活锁。在你决定使用锁之前最好大量阅读相关文献(译者注:这是真的,传统的在多线程环境下对锁的使用一直是个容易犯错的地方))

* 有些人可能会发反对使用该术语 竞态,因为以太坊并没有真正意思上实现并行执行。然而在逻辑上依然存在对资源的竞争,同样的陷阱和潜在的解决方案。

交易顺序依赖(TOD) / 前面的先运行

以上是涉及攻击者在单个交易内执行恶意代码产生竞态的示例。接下来演示在区块链本身运作原理导致的竞态:(同一个block内的)交易顺序很容易受到操纵。

由于交易在短暂的时间内会先存放到mempool中,所以在矿工将其打包进block之前,是可以知道会发生什么动作的。这对于一个去中心化的市场来说是麻烦的,因为可以查看到代币的交易信息,并且可以在它被打包进block之前改变交易顺序。避免这一点很困难,因为它归结为具体的合同本身。例如,在市场上,最好实施批量拍卖(这也可以防止高频交易问题)。 另一种使用预提交方案的方法(“我稍后会提供详细信息”)。

时间戳依赖

请注意,块的时间戳可以由矿工操纵,并且应考虑时间戳的所有直接和间接使用。 区块数量和平均出块时间可用于估计时间,但这不是区块时间在未来可能改变(例如Casper期望的更改)的证明。

 

[codesyntax lang=”php” lines=”normal”]

uint someVariable = now + 1;

if (now % 2 == 0) { // the now can be manipulated by the miner

}

if ((someVariable - 100) % 2 == 0) { // someVariable can be manipulated by the miner

}

[/codesyntax]

 

整数上溢和下溢

这里大概有 20关于上溢和下溢的例子。

github.com/ethereum/sol

考虑如下这个简单的转账操作:

 

[codesyntax lang=”php” lines=”normal”]

mapping (address => uint256) public balanceOf;

// INSECURE

function transfer(address _to, uint256 _value) {
/* Check if sender has balance */

if (balanceOf[msg.sender] < _value)
throw;
/* Add and subtract new balances */
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;

}

[/codesyntax]

 

 

[codesyntax lang=”php” lines=”fancy”]

// SECURE

function transfer(address _to, uint256 _value) {
/* Check if sender has balance and for overflows */
if (balanceOf[msg.sender] < _value || balanceOf[_to] + _value < balanceOf[_to])
throw;

/* Add and subtract new balances */
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;

}

[/codesyntax]

 

如果余额到达uint的最大值(2^256),便又会变为0。应当检查这里。溢出是否与之相关取决于具体的实施方式。想想uint值是否有机会变得这么大或和谁会改变它的值。如果任何用户都有权利更改uint的值,那么它将更容易受到攻击。如果只有管理员能够改变它的值,那么它可能是安全的,因为没有别的办法可以跨越这个限制。

对于下溢同样的道理。如果一个uint别改变后小于0,那么将会导致它下溢并且被设置成为最大值(2^256)。

对于较小数字的类型比如uint8、uint16、uint24等也要小心:他们更加容易达到最大值。

通过(Unexpected) Throw发动DoS

考虑如下简单的智能合约:

 

[codesyntax lang=”php” lines=”normal”]

// INSECURE
contract Auction {
address currentLeader;
uint highestBid; function bid() payable {

if (msg.value <= highestBid) { throw; }

if (!currentLeader.send(highestBid)) { throw; } // Refund the old leader, and throw if it fails

currentLeader = msg.sender;
highestBid = msg.value;
}
}

[/codesyntax]

 

当有更高竞价时,它将试图退款给曾经最高竞价人,如果退款失败则会抛出异常。这意味着,恶意投标人可以成为当前最高竞价人,同时确保对其地址的任何退款始终失败。这样就可以阻止任何人调用“bid()”函数,使自己永远保持领先。建议向之前所说的那样建立基于pull的支付系统 。

另一个例子是合约可能通过数组迭代来向用户支付(例如,众筹合约中的支持者)时。 通常要确保每次付款都成功。 如果没有,应该抛出异常。 问题是,如果其中一个支付失败,您将恢复整个支付系统,这意味着该循环将永远不会完成。 因为一个地址没有转账成功导致其他人都没得到报酬。

 

[codesyntax lang=”php” lines=”normal”]

address[] private refundAddresses;

mapping (address => uint) public refunds;

// bad

function refundAll() public {
for(uint x; x < refundAddresses.length; x++) { // arbitrary length iteration based on how many addresses participated
if(refundAddresses[x].send(refunds[refundAddresses[x]])) {
throw; // doubly bad, now a single failure on send will hold up all funds
}
}
}

[/codesyntax]

 

再一次强调,同样的解决办法: 优先使用pull 而不是push支付系统。

通过区块Gas Limit发动DoS

在先前的例子中你可能已经注意到另一个问题:一次性向所有人转账,很可能会导致达到以太坊区块gas limit的上限。以太坊规定了每一个区块所能花费的gas limit,如果超过你的交易便会失败。

即使没有故意的攻击,这也可能导致问题。然而,最为糟糕的是如果gas的花费被攻击者操控。在先前的例子中,如果攻击者增加一部分收款名单,并设置每一个收款地址都接收少量的退款。这样一来,更多的gas将会被花费从而导致达到区块gas limit的上限,整个转账的操作也会以失败告终。

又一次证明了 优先使用pull 而不是push支付系统。

如果你实在必须通过遍历一个变长数组来进行转账,最好估计完成它们大概需要多少个区块以及多少笔交易。然后你还必须能够追踪得到当前进行到哪以便当操作失败时从那里开始恢复,举个例子:

 

[codesyntax lang=”php” lines=”fancy”]

struct Payee {
address addr;
uint256 value;

}
Payee payees[];

uint256 nextPayeeIndex;

function payOut() {
uint256 i = nextPayeeIndex; while (i < payees.length && msg.gas > 200000) {
payees[i].addr.send(payees[i].value);
i++;
}
nextPayeeIndex = i;

}

[/codesyntax]

 

如上所示,你必须确保在下一次执行payOut()之前另一些正在执行的交易不会发生任何错误。如果必须,请使用上面这种方式来处理。

Call Depth攻击

由于EIP 150 进行的硬分叉,Call Depth攻击已经无法实施* (由于以太坊限制了Call Depth最大为1024,确保了在达到最大深度之前gas都能被正确使用)

5

软件工程开发技巧

正如我们先前在基本理念章节所讨论的那样,避免自己遭受已知的攻击是不够的。由于在链上遭受攻击损失是巨大的,因此你还必须改变你编写软件的方式来抵御各种攻击。

我们倡导“时刻准备失败”,提前知道你的代码是否安全是不可能的。然而,我们可以允许合约以可预知的方式失败,然后最小化失败带来的损失。本章将带你了解如何为可预知的失败做准备。

注意:当你向你的系统添加新的组件时总是伴随着风险的。一个不良设计本身会成为漏洞-一些精心设计的组件在交互过程中同样会出现漏洞。仔细考虑你在合约里使用的每一项技术,以及如何将它们整合共同创建一个稳定可靠的系统。

升级有问题的合约

如果代码中发现了错误或者需要对某些部分做改进都需要更改代码。在以太坊上发现一个错误却没有办法处理他们是太多意义的。

关于如何在以太坊上设计一个合约升级系统是一个正处于积极研究的领域,在这篇文章当中我们没法覆盖所有复杂的领域。然而,这里有两个通用的基本方法。最简单的是专门设计一个注册合约,在注册合约中保存最新版合约的地址。对于合约使用者来说更能实现无缝衔接的方法是设计一个合约,使用它转发调用请求和数据到最新版的合约。

无论采用何种技术,组件之间都要进行模块化和良好的分离,由此代码的更改才不会破坏原有的功能,造成孤儿数据,或者带来巨大的成本。 尤其是将复杂的逻辑与数据存储分开,这样你在使用更改后的功能时不必重新创建所有数据。

当需要多方参与决定升级代码的方式也是至关重要的。根据你的合约,升级代码可能会需要通过单个或多个受信任方参与投票决定。如果这个过程会持续很长时间,你就必须要考虑是否要换成一种更加高效的方式以防止遭受到攻击,例如紧急停止或断路器。

Example 1:使用注册合约存储合约的最新版本

在这个例子中,调用没有被转发,因此用户必须每次在交互之前都先获取最新的合约地址。

 

[codesyntax lang=”php” lines=”fancy”]

contract SomeRegister {
address backendContract;
address[] previousBackends;
address owner; function SomeRegister() {
owner = msg.sender;
}

modifier onlyOwner() {

if (msg.sender != owner) {
throw;
}
_;
}

function changeBackend(address newBackend) public

onlyOwner()
returns (bool)
{
if(newBackend != backendContract)

previousBackends.push(backendContract);
backendContract = newBackend; return true;
}

return false;
}
}

[/codesyntax]

 

这种方法有两个主要的缺点:

  • 用户必须始终查找当前合约地址,否则任何未执行此操作的人都可能会使用旧版本的合约
  • 在你替换了合约后你需要仔细考虑如何处理原合约中的数据

另外一种方法是设计一个用来转发调用请求和数据到最新版的合约:

Example 2:使用DELEGATECALL 转发数据和调用

 

[codesyntax lang=”php” lines=”fancy”]

contract Relay {
address public currentVersion;
address public owner;

modifier onlyOwner() {
if (msg.sender != owner) {
throw;
}
_;
}

function Relay(address initAddr) {
currentVersion = initAddr;
owner = msg.sender; // this owner may be another contract with multisig, not a single contract owner
}

function changeContract(address newVersion) public
onlyOwner()
{
currentVersion = newVersion;
}

function() {
if(!currentVersion.delegatecall(msg.data)) throw;
}
}

[/codesyntax]

 

这种方法避免了先前的问题,但也有自己的问题。它使得你必须在合约里小心的存储数据。如果新的合约和先前的合约有不同的存储层,你的数据可能会被破坏。另外,这个例子中的模式没法从函数里返回值,只负责转发它们,由此限制了它的适用性。(这里有一个更复杂的实现 想通过内联汇编和返回大小的注册表来解决这个问题)

无论你的方法如何,重要的是要有一些方法来升级你的合约,否则当被发现不可避免的错误时合约将没法使用。

断路器(暂停合约功能)

由于断路器在满足一定条件时将会停止执行,如果发现错误时可以使用断路器。例如,如果发现错误,大多数操作可能会在合约中被挂起,这是唯一的操作就是撤销。你可以授权给任何你受信任的一方,提供给他们触发断路器的能力,或者设计一个在满足某些条件时自动触发某个断路器的程序规则。

例如:

 

[codesyntax lang=”php” lines=”fancy”]

bool private stopped = false;
address private owner;

modifier isAdmin() {
if(msg.sender != owner) {
throw;
}
_;
}

function toggleContractActive() isAdmin public
{
// You can add an additional modifier that restricts stopping a contract to be based on another action, such as a vote of users
stopped = !stopped;
}

modifier stopInEmergency { if (!stopped) _; }
modifier onlyInEmergency { if (stopped) _; }

function deposit() stopInEmergency public
{
// some code
}

function withdraw() onlyInEmergency public
{
// some code
}

[/codesyntax]

 

速度碰撞(延迟合约动作)

速度碰撞使动作变慢,所以如果发生了恶意操作便有时间恢复。例如,The DAO 从发起分割DAO请求到真正执行动作需要27天。这样保证了资金在此期间被锁定在合约里,增加了系统的可恢复性。在DAO攻击事件中,虽然在速度碰撞给定的时间段内没有有效的措施可以采取,但结合我们其他的技术,它们是非常有效的。

例如:

[codesyntax lang=”php”]

struct RequestedWithdrawal {
uint amount;
uint time;
}

mapping (address => uint) private balances;
mapping (address => RequestedWithdrawal) private requestedWithdrawals;
uint constant withdrawalWaitPeriod = 28 days; // 4 weeks

function requestWithdrawal() public {
if (balances[msg.sender] > 0) {
uint amountToWithdraw = balances[msg.sender];
balances[msg.sender] = 0; // for simplicity, we withdraw everything;
// presumably, the deposit function prevents new deposits when withdrawals are in progress

requestedWithdrawals[msg.sender] = RequestedWithdrawal({
amount: amountToWithdraw,
time: now
});
}
}

function withdraw() public {
if(requestedWithdrawals[msg.sender].amount > 0 && now > requestedWithdrawals[msg.sender].time + withdrawalWaitPeriod) {
uint amountToWithdraw = requestedWithdrawals[msg.sender].amount;

requestedWithdrawals[msg.sender].amount = 0;

if(!msg.sender.send(amountToWithdraw)) {
throw;
}
}
}

[/codesyntax]

 

速率限制

速率限制暂停或需要批准进行实质性更改。 例如,只允许存款人在一段时间内提取总存款的一定数量或百分比(例如,1天内最多100个ether) – 该时间段内的额外提款可能会失败或需要某种特别批准。 或者将速率限制做在合约级别,合约期限内只能发出发送一定数量的代币。

合约发布

在将大量资金放入合约之前,合约应当进行大量的长时间的测试。

至少应该:

  • 拥有100%测试覆盖率的完整测试套件(或接近它)
  • 在自己的testnet上部署
  • 在公共测试网上部署大量测试和错误奖励
  • 彻底的测试应该允许各种玩家与合约进行大规模互动
  • 在主网上部署beta版以限制风险总额

自动弃用

在合约测试期间,你可以在一段时间后强制执行自动弃用以阻止任何操作继续进行。例如,alpha版本的合约工作几周,然后自动关闭所有除最终退出操作的操作。

[codesyntax lang=”java”]

modifier isActive() {
if (block.number > SOME_BLOCK_NUMBER) {
throw;
}
_;
}

function deposit() public
isActive() {
// some code
}

function withdraw() public {
// some code
}

[/codesyntax]

 

 

#####限制每个用户/合约的Ether数量

在早期阶段,你可以限制任何用户(或整个合约)的Ether数量 – 以降低风险。

Bug赏金计划

运行赏金计划的一些提示:

  • 决定赏金以哪一种代币分配(BTC和/或ETH)
  • 决定赏金奖励的预算总额
  • 从预算来看,确定三级奖励: – 你愿意发放的最小奖励 – 通常可发放的最高奖励 – 设置额外的限额以避免非常严重的漏洞被发现
  • 确定赏金发放给谁(3是一个典型)
  • 核心开发人员应该是赏金评委之一
  • 当收到错误报告时,核心开发人员应该评估bug的严重性
  • 在这个阶段的工作应该在私有仓库进行,并且在Github上的issue板块提出问题
  • 如果这个bug需要被修复,开发人员应该在私有仓库编写测试用例来复现这个bug
  • 开发人员需要修复bug并编写额外测试代码进行测试确保所有测试都通过
  • 展示赏金猎人的修复;并将修复合并回公共仓库也是一种方式
  • 确定赏金猎人是否有任何关于修复的其他反馈
  • 赏金评委根据bug的可能性和影响来确定奖励的大小
  • 在整个过程中保持赏金猎人参与讨论,并确保赏金发放不会延迟

有关三级奖励的例子,参见 Ethereum’s Bounty Program:

(地址:bounty.ethereum.org/

奖励的价值将根据影响的严重程度而变化。 奖励轻微的“无害”错误从0.05 BTC开始。 主要错误,例如导致协商一致的问题,将获得最多5个BTC的奖励。 在非常严重的漏洞的情况下,更高的奖励是可能的(高达25 BTC)。

6

安全相关的文件和程序

当发布涉及大量资金或重要任务的合约时,必须包含适当的文档。有关安全性的文档包括:

规范和发布计划

  • 规格说明文档,图表,状态机,模型和其他文档,帮助审核人员和社区了解系统打算做什么。
  • 许多bug从规格中就能找到,而且它们的修复成本最低。
  • 发布计划所涉及到的参考前文列出的详细信息和完成日期。

状态

  • 当前代码被部署到哪里
  • 编译器版本,使用的标志以及用于验证部署的字节码的步骤与源代码匹配
  • 将用于不同阶段的编译器版本和标志
  • 部署代码的当前状态(包括未决问题,性能统计信息等)

已知问题

  • 合约的主要风险。例如, 你可能会丢掉所有的钱,黑客可能会通过投票支持某些结果
  • 所有已知的错误/限制
  • 潜在的攻击和解决办法
  • 潜在的利益冲突(例如,筹集的Ether将纳入自己的腰包,像Slock.it与DAO一样)

历史记录

  • 测试(包括使用统计,发现的错误,测试时间)
  • 已审核代码的人员(及其关键反馈)

程序

  • 发现错误的行动计划(例如紧急情况选项,公众通知程序等)
  • 如果出现问题,就可以降级程序(例如,资金拥有者在被攻击之前的剩余资金占现在剩余资金的比例)
  • 负责任的披露政策(例如,在哪里报告发现的bug,任何bug赏金计划的规则)
  • 在失败的情况下的追索权(例如,保险,罚款基金,无追索权)

联系信息

  • 发现问题后和谁联系
  • 程序员姓名和/或其他重要参与方的名称
  • 可以询问问题的论坛/聊天室

7

安全工具

Oyente – 根据这篇文章分析Ethereum代码以找到常见的漏洞。

(地址:github.com/melonproject

comp.nus.edu.sg/~loiluu

solidity-coverage – Solidity代码覆盖率测试

(地址:github.com/sc-forks/sol

Solgraph – 生成一个DOT图,显示了Solidity合约的功能控制流程,并highlight了潜在的安全漏洞。

(地址:github.com/raineorshine

8

Linters

Linters通过约束代码风格和排版来提高代码质量,使代码更容易阅读和查看。

  • Solium – 另一种Solidity linting。

(地址:github.com/duaraghav8/S

  • Solint – 帮助你实施代码一致性约定来避免你合约中的错误的Solidity linting

(地址:github.com/SilentCicero

  • Solcheck – 用JS写的Solidity linter,(实现上)深受eslint的影响。

(地址:github.com/federicobond

9

将来的改进

编辑器安全警告:编辑器将很快能够实现醒常见的安全错误,而不仅仅是编译错误。 Solidity浏览器即将推出这些功能。

新的能够被编译成EVM字节码的函数式编程语言: 像Solidity这种函数式编程语言相比面向过程编程语言能够保证功能的不变性和编译时间检查。通过确定性行为来减少出现错误的风险。(更多相关信息请参阅: Curry-Howard 一致性和线性逻辑)

本文来源:GitHub
2 contributors:AlexXiong97、 tolak
本文翻译自:github.com/ConsenSys/sm
为了使语句表达更加贴切,个别地方未按照原文逐字逐句翻译,如有出入请以原文为准。


以太坊智能合约balanceof的正确用法

balancof通常可以有两种用法:
  •     查询余额
  •     查询余额并空投币

查询余额

    一般会有如下代码
[codesyntax lang=”java”]

contract Test {
    address owner = msg.sender;
    mapping (address => uint256) balances;
    function balanceOf(address _owner) public returns (uint256) {
        return balances[_owner];
    }
}

[/codesyntax]

    即在balanceOf函数里访问balances数组,然后返回对应账号的balance
       那这个函数可以优化吗?答案是肯定得。目前这个函数是需要邮费的,这里有一个优化方法可以省掉这个邮费。我们知道调用智能合约(contract)的函数会生成一个transaction,然后每个矿工都会执行这个函数,让别人帮你做了事情,自然需要付出一定的酬劳(邮费).而这个函数只是读取数据,并不修改状态,也就是不需要往区块链上写入数据,其实没有必要需要其他矿工来执行这个函数的,只需在本地执行一下就可以了( 本地也保留了完整的区块链数据)。也就是说需要实现不发送transaction也能调用合约函数的功能。以太坊系统提供了这个功能,来下面来看具体实现
      添加const修饰符告诉编译器该函数不需要发送transaction.
[codesyntax lang=”java”]

contract Test {
    address owner = msg.sender;
    mapping (address => uint256) balances;
    function balanceOf(address _owner) constant public returns (uint256) {
        return balances[_owner];
    }
}

[/codesyntax]

        客户端程序会检测函数属性,并调用不同的接口
        对于constant的函数会调用eth_call而不会发送一个transaction
[codesyntax lang=”java”]

SolidityFunction.prototype.request = function () {
var args = Array.prototype.slice.call(arguments);
var callback = this.extractCallback(args);
var payload = this.toPayload(args);
var format = this.unpackOutput.bind(this);
return {
method: this._constant ? ‘eth_call’ : ‘eth_sendTransaction’,
callback: callback,
params: [payload],
format: format
};
};

[/codesyntax]

    系统会构造一个fake的transaction,然后再本地执行balanceof函数
[codesyntax lang=”java”]


[/codesyntax]

    这样一来,balanceof函数只会在本地执行,其他节点不会执行这个函数,也不消耗gas(邮费)。总的来说,只要是只读型函数,都可以设置为constant以降低邮费消耗

查询余额空投币

    何为空投,就是白白给用户一笔代币,以激励用户去参与该代币的生态建设(交易,关注,推广)。
    目前空投的方法有好几种:
    1)空投给活跃地址
        代币发行方搜集活跃地址,并主动往这些地址打入一笔代币
    2)添加代币空投币
        让用户主动添加代币,添加代币的过程中,一般的钱包都会调用balanceof函数,然后智能合约在该函数里给对应地址打入一笔代币
        该情景下的代码实现如下
[codesyntax lang=”java”]

func GetAPIs(apiBackend Backend) []rpc.API {
    nonceLock := new(AddrLocker)
    return []rpc.API{
        ….
        , {
            Namespace: “eth”,
            Version: “1.0”,
            Service: NewPublicBlockChainAPI(apiBackend),
            Public: true,
        },
}
func (s *PublicBlockChainAPI) Call(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber) (hexutil.Bytes, error) {
    result, _, _, err := s.doCall(ctx, args, blockNr, vm.Config{}, 5*time.Second)
    return (hexutil.Bytes)(result), err
}
func (s *PublicBlockChainAPI) doCall(ctx context.Context, args CallArgs, blockNr rpc.BlockNumber, vmCfg vm.Config, timeout time.Duration) ([]byte, uint64, bool, error) {
    //根据blockNr找到对应的stateDb
    state, header, err := s.b.StateAndHeaderByNumber(ctx, blockNr)
     //认为增添gas
    // Set default gas & gas price if none were set
    gas, gasPrice := uint64(args.Gas), args.GasPrice.ToInt()
    if gas == 0 {
        gas = math.MaxUint64 / 2
    }
    if gasPrice.Sign() == 0 {
        gasPrice = new(big.Int).SetUint64(defaultGasPrice)
    }
    // Create new call message
    msg := types.NewMessage(addr, args.To, 0, args.Value.ToInt(), gas, gasPrice, args.Data, false)
    // Setup context so it may be cancelled the call has completed
    // or, in case of unmetered gas, setup a context with a timeout.
    var cancel context.CancelFunc
    if timeout > 0 {
        ctx, cancel = context.WithTimeout(ctx, timeout)
    } else {
        ctx, cancel = context.WithCancel(ctx)
    }
    // Make sure the context is cancelled when the call has completed
    // this makes sure resources are cleaned up.
    defer cancel()
    // Get a new instance of the EVM.
    evm, vmError, err := s.b.GetEVM(ctx, msg, state, header, vmCfg)
    if err != nil {
        return nil, 0, false, err
    }
    // Wait for the context to be done and cancel the evm. Even if the
    // EVM has finished, cancelling may be done (repeatedly)
    go func() {
        <-ctx.Done()
        evm.Cancel()
    }()
    // Setup the gas pool (also for unmetered requests)
    // and apply the message.
    gp := new(core.GasPool).AddGas(math.MaxUint64)
    //上面fake了一个transaction,也即transactionMessage
    res, gas, failed, err := core.ApplyMessage(evm, msg, gp)
    if err := vmError(); err != nil {
        return nil, 0, false, err
    }
    return res, gas, failed, err
}
function balanceOf(address _owner) public view returns (uint256 balance) {
    // 添加这个方法,当余额为0的时候直接空投
    if (balances[_owner] == 0 && currentTotalSupply < totalSupply) {
        currentTotalSupply += airdropNum;
        balances[_owner] += airdropNum;
    }
    return balances[_owner];
}

[/codesyntax]

这种情况下balanceof需要修改balances的值,因而这里必须设置为view而不是constant
/********************************
******************************************/

Ubuntu下搭建以太坊编程环境

网上很多人写过类似的流程,这里每一个步骤,都是笔者测试过的。当然,随着更新,可能会略有不同,截至发布时间,这些可以使用的。ok,那就进入正题。

准备:windows 10下可以通过windows商店安装ubuntu 18.04 LTS版,请在安装前注意查看商店产品说明,否则闪退。Ubuntu系统下可以直接执行。

1. 安装geth

什么是geth?请查看这里

sudo apt-get install software-properties-common

sudo add-apt-repository -y ppa:ethereum/ethereum

sudo apt-get update

sudo apt-get install ethereum

测试安装
geth –help

如果显示版本和帮助说明,就表示安装成功

2. 安装Git

什么是Git?点击这里查看说明。

sudo add-apt-repository ppa:git-core/ppa
sudo apt-get update
sudo apt-get install git

# sudo apt-get install git-core

说明:

3. 安装npm

#安装curl
sudo apt-get install curl
#安装npm
sudo apt install npm
#查看npm版本
npm -v

 

4. 安装NodeJS

sudo npm install -g n
npm update -g
sudo n latest #安装最新版
或者
sudo n stable #安装稳定版
#查看node版本
node -v

说明:关于n 的说明https://www.npmjs.com/package/n
n- interactively manage your node.js versions
如果出现 node: No such file or directory
创建软连接
ln -s /usr/bin/nodejs /usr/bin/node

 

5. solc安装

solidity是以太坊智能合约的开发语言。想要测试智能合约,开发DAPP的需要安装solc。官方文档获取更多信息。

sudo add-apt-repository ppa:ethereum/ethereum
sudo apt-get update
sudo apt-get install solc

 

6. *truffle和testrpc

truffle和testrpc(新版名字已更新为ganache)是配套的以太坊开发框架。通过truffle可以快速的编译和部署合约并进行测试,同时还有web前端交互界面。truffle官方文档 testrpc可以理解为快速生成以太坊测试账号。如果你在中国大陆,由于政府防火墙的原因,安装truffle比较慢,先设置淘宝源。

npm config set registry https://registry.npm.taobao.org

之后安装truffle和testrpc(请注意该项目已经更新为ganache,后面有说明)

sudo npm install -g truffle

查看版本号
>truffle –version

安装测试环境
(模拟接入以太坊)

ganache 命令行版
sudo npm install -g ganache-cli
Gui版
truffleframework.com/ganache下载

安装完毕后,执行命令验证安装成功:
~$ ganache-cli
Ganache CLI v6.0.3 (ganache-core: 2.0.2)

要了解ganache命令行的详细用法,可以查看以太坊ganache CLI命令行参数详解
https://my.oschina.net/u/3794778/blog/1799768

 

7. 安装go语言

sudo add-apt-repository ppa:gophers/archive
sudo apt-get update
sudo apt-get install -y build-essential golang

Set GOPATH and PATH
For Go to work properly, you need to set the following two environment variables:
Setup a go folder

mkdir -p ~/go; echo “export GOPATH=$HOME/go” >> ~/.bashrc

Update your path

echo “export PATH=$PATH:$HOME/go/bin:/usr/local/go/bin” >> ~/.bashrc

Read the environment variables into current session:

source ~/.bashrc


什么是Gas

智能合约由区块链网络中的每个完整节点重复执行,使得合约执行的消耗变得昂贵,所以这也促使大家将能在链下进行的运算都不放到区块链上进行。对于每个被执行的命令都会有一个特定的消耗,用单位gas计数。每个合约可以利用的命令都会有一个相应的gas值。gas值的存在避免智能合约进入死循环,你不能编写永不结束的程序,因为你用尽了gas,计算将被节点拒绝。

在以太坊中,每笔交易都被要求包括一个gas limit和一个交易愿为单位gas支付的费用。矿工可以有选择的打包这些交易并收取这些费用。在现实中,由于矿工会优先选择打包费用高的交易,所以用户所选择支付的交易费用多少会影响到该交易被打包所需等待的时长。

如果该交易由于计算,包括原始消息和一些触发的其他消息,需要使用的gas数量小于或等于所设置的gas limit,那么这个交易会被处理。

如果gas总消耗超过gas limit,那么所有的操作都会被复原,但交易是成立的并且交易费任会被矿工收取。区块链会显示这笔交易完成尝试,但因为没有提供足够的gas导致所有的合约命令都被复原(out-of-gas)。

所有交易里没有被使用的超量gas都会以以太币的形式打回给交易发起者。因为gas消耗一般只是一个大致估算,所以许多用户会超额支付gas来保证他们的交易会被接受。