精通以太坊-7~9章-思维导图

区块链、以太坊专题链接直达

区块链入门
精通以太坊-1~3章-思维导图
精通以太坊-4~6章-思维导图
精通以太坊-7~9章-思维导图
精通以太坊-10~14章-思维导图

《精通以太坊》

第七章~第九章

智能合约 安全 风险 漏洞 Solidity Vyper

学习笔记 思维导图
精通以太坊-7~9章-思维导图_第1张图片

附:文本结构

精通以太坊-7~9章
	智能合约
		以太坊的智能合约既不智能,也没有法律上的合约效力
		以太坊的智能合约是那些不可改变的计算机程序
			以确定性方式运行在以太坊的虚拟机上的程序
			以太坊去中心化世界计算机
		定义
			计算机程序
				智能合约就是计算机程序,这里的合约没有任何法律上相关的含义
			不可改变
				一旦部署,智能合约的代码就不能被改变
				更改智能合约的唯一办法就是部署一个新的实例
			确定性的
				智能合约执行结果对于每一个运行或调用它的人来说都是一样的
			以太坊虚拟机上下文
				智能合约运行在一个非常有限的执行环境中。
				它们可以访问自己的状态,调用合约交易的上下文信息,以及有关最近区块的信息
			去中心化的世界计算机
				EVM作为每一个以太坊节点的本地实例运行,但是因为所有EVM都是运行在相同的初始状态,并且输出完全相同的最终状态,所以整个系统就像是一台世界计算机
		生命周期
			通过发起目标地址为0x0的交易部署合约
			合约的创建者不会再协议层获得相对其他用户而言的任何特权
				但可以把自己的特权写入合约内部
			合约账户没有私钥,智能合约的主人不是创建者,而是它自己
			合约只有在被交易调用时才会执行
				合约可以调用另一个合约,然后一层层地在合约之间不断调用
				但这个执行链条中的第一个合约的执行,一定是由外部账户所创建的交易触发的
			合约直到交易触发执行前,都处于等待调用的状态,而永远不会“自动执行”或者“在后台运行”
			任何情况下智能合约的“并发执行”都是没有意义的
				以太坊世界计算机可以被认为是一台单线程的计算机
			交易原子化
				无论调用多少合约,多少次操作,如果错误会回滚
				失败的交易仍然会被作为一次失败的尝试而记录在案
					所花费的gas依然会被扣除
			合约实例删除
				执行名为SELFDESTRUCT的EVM字节码
				需要合约的开发者在代码中编写了对应的删除功能,SELFDESTRUCT字节码才会起作用
					否则该合约实例无法删除
					可控制仅能由创建者删除
				删除合约实例,不会执行任何代码
				删除操作会产生负的gas消耗,也就是系统会提供gas退款
					用于激励人们通过删除存储状态的方式释放资源
				删除合约并不会清楚这个合约之前交易的历史记录
		注意事项
			gas消耗
				开发智能合约时必须认真对待gas相关问题,gas用于限定衣他交易运行所耗费的最大计算量。
				耗尽gas将会
					抛出“out of gas”异常
					状态被恢复到执行开始之前
					所有在这次执行过程中的gas开销都会被作为交易费用
				有一些需要遵循的最佳实践,尽可能把合约调用的gas消耗最小化
			避免动态尺寸的数组
				遍历数组的每一个元素进行查找或操作,都有触发高额gas消耗的风险
				因此要避免使用动态尺寸的数组
			避免调用其他合约
				特别是gas消耗未知的合约
				没有经过测试和广泛使用的库合约
			估计gas开销
				gasEstimate可用于估计调用开销
		合约的常见漏洞
			可自毁合约
				任何地址都可杀死
			贪婪合约
				达到某个执行状态,就无法释放以太币
			慷慨合约
				把以太币转给任意地址
		读取数据
			智能合约可以将数据写进两个地方
				全局状态
					状态变量存储在以太坊全局状态树中
					只能存储、读取、修改与自身地址相关的数据
					不能读写其他智能合约的数据
				日志
					通过日志事件,将数据写入以太坊区块链数据中
					智能合约无法对所创建的链上日志进行读操作
					但日志数据可悲轻客户端发现和读取
					可获得指定交易收据的日志数据
	Vyper与Solidity
		抛弃修饰符
			提高代码可审计性和可读性
				可通过明确的代码代替
		抛弃继承
			继承关系需要在多个文件之间跳转会提高阅读难度
		抛弃内联汇编
			内联汇编可读性差
		抛弃函数重载
			不允许同名函数
			避免混淆
		变量类型转换
			使用convert函数
			更安全
		显式地处理前置和后置条件和状态变化
			虽然会产生冗余代码,但提高可读性和安全性
			Vyper中编写时应遵循三点
				条件
					以太坊状态变量的当前状态和条件是什么?
				影响
					智能合约在执行时对状态变量的条件的影响
						什么会受影响
						什么不会受影响
						这些影响是否与智能合约的意图一致
				交互
					有逻辑地逐步执行代码,并考虑代码执行后所有可能的永久结果、后果、场景
		总结
			Vyper
				功能强大
				面向合约编程
				偏向正确性,牺牲灵活性
				更安全
	智能合约安全
		安全最佳实践
			最小化/简单化
			代码重用
			代码质量
				因为你处在航天工程那样或其他类似的零容错的工程领域之中
			可读性和可审计性
				智能合约是公开的,任何人都可以获得其字节码并进行反向工程
				因此智能合约很契合在开源社区协作开发
			测试覆盖率
				尽可能测试所有情况
		安全风险
			重入
				漏洞
					外部的恶意合约通过函数调用来重新进入合约代码执行过程
				防范技术
					尽可能使用内置的transfer函数向外部合约发送以太币
					所有对状态变量的修改都在向其他合约发送以太币之前来执行
					引入互斥锁,增加一个状态变量来在代码执行中锁定合约,避免重入的调用
			算数溢出
				漏洞
					比如unit8范围是[0,255],用来保存256时就溢出变为了0
						257变为1,以此类推
						因此可能被攻击者利用来伪造数值
					也要注意加减乘等等的溢出
						上溢
						下溢
					除法不会导致溢出
						除以0时会抛出异常
				防范技术
					使用或构建安全的算数运算的库合约来代替标准的算数操作
						如使用OpenZeppelin的SafeMath.sol
			意外的以太币
				漏洞
					攻击者向合约发送以太币,导致合约的初始数值非零,产生一些负面影响
						不是所有攻击,攻击者都是为了盈利,也可能是为了纯粹的破坏,就算自己付出了些许代价
				防范技术
					避免依赖合约余额的具体数值(this.balance),因为它可以被人为地操纵
					自定义变量在payable函数记录数额变动
			DELEGATECALL
				运行在调用合约的上下文
				CALL则是运行在目标合约的上下文
				漏洞
					可能导致非预期的代码执行结果
				防范技术
					使用DELEGATECALL要非常仔细地注意库合约和主调用合约的可能的调用上下文
					并尽可能构建无状态的库合约
			默认的可见性
				漏洞
					函数默认可见性是public
					因此如果函数不指定可见性,函数就是可以被外部用户调用的
				防范技术
					对合约中的所有函数明确指定可见性
			无序错觉
				漏洞
					区块链是一个确定性的系统
					随机数都是伪随机数
					不能通过时钟来产生随机数
					如果使用未来的区块哈希值判定赌局,矿工可以不发布不利于自己的区块,而是重新打包区块直到得到有利的新的区块哈希值再发布
					如果使用过去的变量更加灾难,因为这些对所有人都是透明的,也就更容易被攻击者知晓
					区块变量不能作为无序性的来源,因为可以被矿工们操纵
				防范技术
					无序性(随机性)必须来自区块链外部
					比如使用RandDAO
					比如每个参与者提供一个seed,再一起进行hash公示
			外部合约引用
				漏洞
					在部署时更改外部合约引用,从而运行攻击者自己的代码
				防范技术
					使用new创建引用的合约,这样以来部署的用户也无法在不更改代码的情况下替换改引用合约
						代码发生变化,合约字节码就发生变化,合作用户就能发现代码被篡改过
					对外部合约地址进行硬编码
						将外部合约地址设定为public,用户就可以轻松检查合约所引用的外部合约
			短地址/参数攻击
				漏洞
					攻击者故意传一个位数少了的地址参数,让没有检查正确性的合约使用0自动补齐,进而产生不良影响
				防范技术
					所有外部应用在把输入参数发送到区块链之前都应该对它们进行校验,包括参数的顺序等都同样扮演着重要角色
			未检查的调用返回值
				漏洞
					Call喝send函数会返回一个布尔值来指明调用是否成功,因此如果外部调用失败,执行了这些函数的那个交易不会revert回滚
				防范技术
					调用call喝send函数后检查布尔值,以分别处理成功以及失败的场景
			竞争条件/预先交易
				漏洞
					攻击者监视交易池中的交易,如果交易中包含了对某个问题的答案,那么攻击者可以去修改或者撤销解决者的权限,或者把合约修改为对解决者不利的状态
					然后,攻击者从解决者的交易中获取数据,并用更高的gasPrice创建他们自己的交易,这样他们的交易就会优先于原始交易被打包到区块中
					直白点:攻击者作弊偷了答案,并用更高的gasPrice让自己优先交卷
						交易打包按gasPrice排序
					两类攻击
						用户修改其交易的gasPrice
						矿工自己按照自己的意愿随意对区块内的交易进行排序
							仅在自己找到区块的工作量证明时才能发起攻击
				防范技术
					将gasPrice设置为上限,避免第一类攻击
					提示-揭示策略
						发送带有隐秘信息的交易(通常是一个哈希值)
						当交易被包含到区块后再发送一个交易揭示先前发送的数据
						如ENS智能合约允许用户先提交它们希望话费的以太币数量,同时附带任意数量的以太币,然后在揭示阶段,用户可以获得以太币返还
							?
					水底发送策略
			拒绝服务
				漏洞
					基于可被外部操纵的映射或数组的循环
						Distribute向多个账户分发代币时,攻击者创建过多的账户,使该合约的gas消耗超过gasLimit
					主人的操作
						必须经过拥有特权的主任来进行某些操作才能进入下一步状态
						如果主人丢失了私钥,这个合约就进行不了下一步了
					基于外部调用来修改状态
						比如创建一个不接受转账的合约
						而新的状态需要先将以太币被全部取走才能进入下一步
				防范技术
					第一个例子,合约不应该基于一个可以被外部用户人为操作的数据结构来执行循环
						这里推荐使用取回模式
						只能让取款人单独地调用withdraw函数来取回他们各自的代币
					第二、三个例子
						可以使用时间解锁机制
							保证意外发生,也可在到期时自动进行下一步
			区块时间戳操纵
				漏洞
					矿工是可以操纵时间戳的
				防范技术
					时间戳不应该被用来作为无序数据或生成随机数
					或者说不能用来作为重要的状态变动
			小心使用构造函数
				漏洞
					如果合约名称被改动后,构造函数忘记修改,就会变成普通的可被调用的public函数,从而引发攻击
				防范技术
					0.4.22版本的solidity编译器引入了constructor关键字来制定构造函数,因此此漏洞已经不存在了
			未初始化的存储指针
				漏洞
					EVM是用存储或内存来保存数据的,不适当的变量初始化可能会产生有漏洞的合约
				防范技术
					注意为初始化的存储变量警告
					在处理复杂数据类型时严格使用memory或storage标识符
						努力让行为符合预期
			浮点数和精度
				漏洞
					旧版solidity没有浮点型,需要特殊处理
				防范技术
					有时候先乘后除精度会更高
			Tx.Origin验证
				Solidity中的一个全局变量,它会回溯整个调用栈并返回最初发起这个调用或交易的账户地址
				漏洞
					如果受害者的合约A是通过tx.origin==owner来授权提取余额的
					那攻击者可以编写攻击合约B,诱导受害者调用合约B(如在将合约B伪造成外部账户在社交活动中钓鱼),而实际上合约B调用了合约A,从而将合约A中的余额转入攻击者账户上
				防范技术
					智能合约不应该使用tx.origin来进行验证授权
					Tx.origin的合理用法如:可以用来拒绝外部合约调用当前合约
	合约程序库
		OpenZeppelin系列
		ZeppelinOS
		ethpm
	注意
		公共网络上合约的代码并不是默认公开可见的
		不要重复发明加密算法(不要重复造轮子)

你可能感兴趣的:(区块链)