第一章:以太坊智能合约的学习

4 篇文章 0 订阅
3 篇文章 0 订阅


学习内容:第一个只能合约与区块链知识

第一个智能合约:

首先我们知道以太坊的编译智能合约的工具是solidity。

pragma solidity ^0.4.24;

contract Coin {
    // 关键字 public 让状态变量可以从外部读取,它只在创建合约时运行。 这个构造函数做了一件事, 就是保存(外部)函数调用者的地址 msg.sender。
    address public minter;
    mapping (address => uint) public balances;
    
    // 定义了一个事件,客户端可以根据事件变化做出反应
    event Sent(address from,address to,uint amount);
    
    // 构造函数,只有在创建合约是运行一次
    constructor() public {
        minter = msg.sender;
    }
    
    // 挖矿方法,用来产生新的货币,mint 是可以真正被用户或其他合约调用的方法。 不过如果 mint 被非合约创 建者调用, 则什么事也不会发生。
    function mint(address receiver, uint amount) public {
        if (msg.sender != minter) return;
        balances[receiver] += amount;
    }
    
    // 发送货币,send 同样是可以真正被用户或其他合约调用的方法。send 可以被任何人(前 提是拥有这些币〉调用,用来向他人发送币 。
    function send(address receiver, uint amount) public {
        if (balances[msg.sender] < amount) return;
        balances[msg.sender] -= amount;
        balances[receiver] += amount;
        emit Sent(msg.sender, receiver, amount);
    }
}

第一行代码是告诉编译器如何编译这段代码,也就是导入solidity版本包,不同的版本包可以一些代码变量无法使用货兼容。一般在第三位的变动较小。pragma solidity ^0.4.24;

address public minter; 关键宇 public 用来表示状态变量的可见性,这一点和其他语言如 Java 和 C++是类似的,不同的是这行代码声明了一个可以被公开访问的地址类型(address)的状态变量,Solid町的编译器会自动为 public 状态变量生成一个访问函数。

function minter() returns (address) {return minter;}

注意,我们没有必要自己添加这个函数,如果我们添加了一个同名的函数, 编译器生成的将不再生效。

mapping (address => uint) public balances; 

这行代码创建了另 一个 public 状态变量 ,它的数据类型是更加复杂的 mapping (在本书第 4 章中会进一步讲解〉,该类型保存一个个键值对。 mapping 可以被看作一个哈希表,它会执行虚拟初始化,使所有可能存在的键都对应一 个全零的值。 不过和其他语言中的 mapping 不一样的是, Solidity 中的 mapping 不能遍历访问(无法获得所有键或值的列表),对于同样的 public 状态变量,编 译器会为它生成一个访问函数,函数代码如下:

function balances(address _account) public view returns (uint) {
	return balances[_account];
}

我们可以通过这个生成的访问函数来查询某个账户的余额。
event Sent(address from,address to,uint amount);声明了事件,emit Sent(msg.sender, receiver, amount);触发事件

编译solidity的网址:https://remix.ethereum.org

区块链基础概念

交易/事务(Transaction):

学习过数据库的读者,应该能够理解”事务“的含义,如果对事务一词 不是很理解,可以搜索“数据库事务”来学习。

举个例子,想象一张表, 里面列出了某个电子货币所有账户的余额。当从一个账户到另一个账户的转账请求发生时,这个数据库的事务特性确保从一个 账户中减掉的金额会被加到另一个账户上。如果因为某种原因 , 往目标账户上增加金额的操作无法进行,那么原账户中的金额也不会发生任何变化。 此外,当一个事务特性被应用到这个数据库的时候,其他事务不能修改该 数据库。 发送者(创建者)对一个交易进行密码学签名,可以非常直观地理解 为该数据库增加了访问保护。 在上面的电子货币的例子中, mint()函数中的一 个简单检查就可以确保只有持有账户密钥的人才能从该账户向外转账。

区块:

一个区块就是若干交易的集合,它会被标记上时间戳和前一个区块的 Hash 标识。区块在经过哈希运算后生成一份工作量证明 , 从而验证区块中的交易。 有效的区块经过全网络的共识之后会被追加到区块链中。 为什么要这样设计呢?因为在比特币及以太坊这样的完全去中心化的区块 链中,需要解决被称为双花攻击 Cdouble-spend attack)的难题,即如果网络中 存在两笔相互冲突的交易,它们都想花光同一个账户中的钱时会发生什么情况 呢?
实际上网络会自动选择一个交易序列,并打包到区块中,然后在所有参与 节点中执行和分发(广播)。 如果两笔交易相互冲突,那么最终被确认为后发生 的交易将被拒绝,不会被包含到区块中。

这些块按时间形成了一个线性序列,这正是区块链这个词的来源。区块以 一定的时间间隔被添加到链上。对于以太坊,这个间隔大约是 17 秒
作为“顺序选择机制”(也就是挖矿)的一部分,可能有时会发生区块 被回滚的情况,但通常仅在链的“末端”。 在末端增加的块越多,其发生回滚的 概率越小。因此交易被回滚甚至从区块链中抹除也是可能的,但交易发生后等 待的时间越长, 这种情况发生的概率就越小。

创世区块Genesis Block )是区块链中的第一个区块,其区块序号是 0。 它 是区块链中唯一一个不指向前一个区块的区块,因为没有前一个区块。 只有 当网络中的两个节点有相同的创世区块时,它们才会彼此配对。

共识协议:工作量证明(PoW)

目前以太坊使用工作量证明共识协议防止区块链被篡改。 工作量证明系统 需要解决一个复杂的数学难题以创建新的区块。 解决难题需要大量算力,这就 使创建区块很困难。 每一个矿工都各自解决数学难题,其中 第一个解决难题的矿工是胜利者, 其得到的回报是 5 个以太币和该区块中全部 交易的交易费。 区块有一个 区块头(Header)和一系列交易。每一个区块都存储前一个区块的哈希值,由 此创建一个相连的链。
那么矿工是如何解决难题的?

矿工首先从所收到的广播中收集新的未 挖出的交易,然后过滤掉不合法的交易。 合法的交易必须满足正确地使用私钥 签名、账户有足够的余额进行交易等条件。
区块头包含前一个区块的哈希值、 区块序号、随机数(Nonce)、 目标值( Target)、 时间戳(Timestamp)、 难度值 (Difficulty)、矿工地址 (Address)等内容。 时间戳表示区块初始时间 。 以太坊使用 Ethash 哈希算法, 目标值越低,发现随机数需要的时间越 多; 目标值越高,发现随机数需要的时间越少。

网络中的任何节点都可以检查区块链是否合法,首先检查交易在区块链中 是否合法以及时间戳的验证情况, 然后检查区块的目标值和随机数是否合法、 矿工是否得到合法的回报等。 如果网络中的节点接收到两个不同的合法区块链, 那么所有区块的整体难度值较高的那个区块链将被视为合法的区块链。

权益证明(PoS)

以太坊最终会切换到使用权益证明,以解决工作量证明过于消耗资源的问 题。 权益证明的主要思路是: 作为验证节点 , 首先必须拥有一定数量的 以太币, 根据以太币的数量和时间会产生用于下注验证区块的权益。只有拥有权益的节 点才能有效验证区块,当所验证的区块被打包进链后,将获得和权益成正比的 区块奖励。如果是验证恶意或错误的区块,那么所下注的权益将被扣除。

以太坊虚拟机(EVM)

以太坊虚拟机 (EVM, Ethereum Virtual Machine)是以太坊中智能合约的运行环境。

编译合约

在以太坊虚拟机上运行的是合约的宇节码形式,需要在部署之前先对合约 进行编译,可以选择使用 Remix 或 sole 编译器,之后章节会继续讲解。

账户

这里的账户可 以简单理解为银行给我们开设的账户 ,**不过在以太坊中账户 不用申请,而是用户根据需要在钱包中生成的。**转账行为是由一个账户发起, 把财产转移到另一个账户 的过程 (实际上这个财产也是一个数字〉。 以太坊也是 类似的,同时在以太坊上赋予了账户更多的功能,实际上账户在以太坊中有很 重要的作用 。 以太坊中有两类账户 。

  • 外部拥有账户 CEOA):该类账户和银行账户很相似,不过它由公钥/ 私钥对控制,即由人控制,没有关联任何代码
  • 合约账户 : 该类账户由存储在账户中的代码控制。

外部拥有账户(本书中有时会简称“外部账户”)和合约账户由同样的地址 空间来表示。 外部账户的地址是由公钥决定的,合约账户的地址是在创建该合 约时确定的(这个地址由合约创建者的地址和该地址发出过的交易数量 Nonce 计算得到)。
两者的区别是?
一个外部账户 可以通过创建和用自己的私钥对交易进行签名,以发送消息给另一个外部账户 或合约账户 。 在两个外部账户之间传送的消息只是简单的价值转移(类似于银 行账户间转账)。

从外部账户到合约账户的消息会激活合约账户的代码,允许它执行 各种动作。 比如转移代币、写入内部存储、挖出一个新代币、执行一些运算、 创建一个新的合约等操作。

不像外部账户,合约账户不可以自己发起一笔交易 。 但合约账户可以在响 应交易时触发另一笔交易 。 因此,在以太坊上任何动作都是由外部账户触发的 交易所发起的(即动作的发起者必须是外部账户)。

钥匙文件

每个外部账户都是由一对钥匙定义的,即一个私钥和一个公钥。 账户 以地 址为索引 (地址就像银行账号的那一串数字),取公钥的最后 20 个宇节。 每对私钥/ 地址都被编码在一个钥匙文件里。 钥匙文件是 JSON 文本文件,可以用任何文本编辑器打开和浏览。 钥匙文件的关键部分账户私钥,通常用创建账户时设置的密码进行加密。以太坊节点数据目录的 keystore 子目录下找到钥匙文件。

如果忘记密码或者钥匙文件丢失。 就会丢失所有的以太币。没有密码不可能进入账户,也没有“忘记密码” 选项。 所以一定不要忘记密码。

账户状态

账户状态有四个组成部分, 不论账户类型是什么,都存在这四个组成部分。

  • Nonce:如果账户是一个外部拥有账户,则 Nonce 代表从此账户地址发送的交易序号: 如果账户是一个合约账户, 则 Nonce 代表此账户创建 的合约序号。

注意:以太坊有两种 Nonce。 一种是账户 Nonce ,表示账户的交易数量;另一种是工作量证明 Nonce,用于计算满足工作量证明的随机数。

  • Balance:此地址拥有的以太币余额数量
  • storageRoot: Merkle Patricia 树的根节点 Hash 值。 Merkle Patricia 树会将此账户存储内容的 Hash值进行编码,默认是空值。
  • codeHash:此账户EVM代码的Hash 值。 对于合约账户,就是被 Hash 的代码并作为codeHash 保存;对于外部拥有账户,codeHash I或是一个 空宇符串的 Hash 值。

以太坊最新的区块总是保存全局共享状态,但每个区块仅仅修改部分状态。

以太坊钱包

以太坊钱包是以太坊账户的管理工具,如使用钱包可以生成账户、账户转 账等,,例如这笔交易可以是转账、 挖矿、智能合约的部署和合 约函数调用等。
Geth 是以太坊官方提供的客户端(钱包〉,是开发智能合约常用的工具之一,它基于 Go 语言开发。 后面在开展学习。

交易

交易可以包含二进制数 据负载(Payload)和以太币(Ether)。
如果目标账户包含代码,该代码会执行, Payload 就是输入数据。
如果目标账户是零账户(账户地址是 0),交易将创建一个新合约,新合约地址将由创建者的地址和该地址发出过的交易数量 (Nonce)计算得到。 这笔交易(创建合约交易)的 Payload 被当作 EVM 宇节码执行,输出作为合约代码被永久存储。这意味着为了创建一个合约,不需要向合约发送真正的合约代码, 而是发送能够返回真正代码的代码。
一个合约也可以通过一个特殊的指令来创建其他合约(不是简单地向零地 址发起调用 )。创建合约的调用跟普通的消息调用的区别在于, Payload 数据执 行的结果被存储为合约代码,调用者(创建者)在枝上可以得到新合约的地址。

消息调用

合约可以通过消息调用的方式来调用其他合约 , 或者发送以太币到非合约账户。消息调用和交易非常类似,它们都有一个源、 一个目标、数据负载、以太币、 gas (费用)和返回数据。 事实上每笔交易都可以被认为是一个顶层的消 息调用,这个消息调用会依次产生更多的消息调用。
一个合约可以决定剩余 gas 的分配。 比如在内部消息调用时使用多少 gas, 或者期望保留多少 gas。 如果在内部消息调用时发生了费用不足(out-of-gas) 异常(或者其他异常),合约将会得到通知(异常会“冒泡”到合约的调用核)。
当合约调用时,被调用的合约会拥有崭新的内存, 以及能够访 问调用的 Payload (由被称为“calldata" 的独立区域所提供的数据)。 当调用执行结束后, 返回数据将被存储在调用者预先分配好的一块内存中 。
调用层数被限制为 1024, 因此对于更加复杂的操作,我们应该使用循环而 不是递归。

费用( gas)

gas 的目的是限制执行交易所需的工作量,同时为执行支付费用 。 当 EVM 执行交易时, gas 将按照特定规则(这个规则在以太坊黄皮书中有详细说明〉被逐渐消耗。

思考费用的作用

施加费用可以防止用户超负荷使用网络。 以太坊是一个图灵完备的系统, 它允许有循环,并使以太坊受到停机问题的影响,这个问题让你无法确定程序 是否无限制地运行。如果没有费用的话, 恶意的执行者通过执行一个包含无限 循环的交易就可以很容易地让网络瘫痪。 因此,费用保护网络不受蓄意攻击。
gas price (gas 价格,以太币计)是由交易创建者设置的, 发送者账户需要 预付的交易费用(可以理解为给矿工的预算) =gas price × gas limit。 如果交易完成还有剩余 gas,这些 gas 将被返还给发送者账户 。
无论执行到什么位置,一旦 gas 被耗尽(比如降为负值),就会触发一个费用不足异常,当前调用帧所做的所有状态修改都将被还原。 前面介绍过交易(事务〉是一个原子操作,要么所有操作全部成功,要么操作失败所有的执行都将被还原,不能出现中间状态。
另外, gas 不仅仅是用来支付计算的费用,也是用来支付存储的费用 。

以太坊网络

主网(Mainnet)

以太坊网络实时数据如区块、散表难度、 gas 价格和 gas 花费等,可以在 https://ethstats.net 上查询到。部署在主网上的智能合约,任何应用都可以 调用,相关交易信息可以在 https://etherscan.io 上查询到。

测试网络(Testnet)

在主网上任何合约的执行都会消耗真实的以太币,不适合进行开发、调试和测试。因此以太坊专门提供了测试网络,在测试网络中可以很容易获得免费的以太币 。测试网络同样是一个全球网络。 目前以太坊公开的测试网络有:
Ropsten、Rinkeby、Kovan
目前开发人员最常用的测试网络是 Ropsten 和Rinkeby, 使用测试网络依然有一个缺点,即需要花较长时间初始化节点。 在实际使用中,测试网络更适合担当如灰度发布之类的角色。

私有网络、开发者模式

我们可以自己创建私有链进行开发,通过上面提到的 Geth 就可以很容易创建一个属于自己的测试网络,在自己的测试网络中,想挖多少以太币就挖多少,也省去了同步网络的耗时。 或者直接使用 Geth 提供的开发者模式。相比私有链,在开发者网络(模式)下,会自动分配一个有大量余额的开发者账户给我们使用 。

模拟环境网络

还有一种创建测试网络的方法是使用 Ganache。 Ganache 在本地使用内存模 拟的一个以太坊环境,对于开发调试来说更方便、快捷。而且 Ganache 可以在 启动时帮我们创建 10 个存有资金的测试账户 。 在进行合约开发时,可 以在 Ganache 中测试通过后 , 再部署到 Geth 节点中。

存储、内存和能

每个账户都有一块持久化存储区域,被称为存储(storage)。key 和 value 的长度均为 256 位。在合约中不能遍历账户的存储。存储的读写操作开销较大,修改操作开销更大。一个合约只能对它自己的存储 进行读写。

开销指的是消耗gas 的量。

第一个存储区被称为内存( memory)。可以以宇节为粒度寻址。第二个存储区被称为(stack) ,

委托调用和库

存在一种特殊类型的消息调用,被称为委托调用( delegatecall)。 它跟上面讲到的消息调用几乎完全一样,不同的是它将目标地址的代码加载到发起调用的合约上下文中来执行,并且 msg.sendermsg.value 不变。 例如: A 调用 B, B 委托调用 C,此时 msg.sender 为 A。 而如果是普通调用,则 msg.sender 为 B。
这意味着一个合约在运行时可以从另外一个地址动态加载代码。 存储当前 地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。这使得 Solidity 可以实现库能力。

日志

以太坊允许日志跟踪各种交易和信息,日志是用一种特殊的可索引的数据结构来存储的。 Solidity 用日志特性来实现事件。创建合约之后就无法访问日志数据了,但是可以从区块链外高效地访问这些日志数据。
由于部分日志数据被存储在布隆过滤器中,因此可以高效并安全地搜索日志。所以那些没有下载整个区块链的网络节点(轻客户端)也可以找到这些日志。

布隆过滤器( Bloom Filter )是由布隆( Burton Howard Bloom )在 1970 年提出的。 它实际上是由一个很长的二进制向量和一系列随机映射函数组成的, 可以用于检索一个元素是否在一个集合中。 布隆过滤器的优点是空间效率和 查询时间都远远超过一般的算法。

自毁

只有当某个地址上的合约执行自毁(selfdestruct) 操作时,才会从区块链上移除合约代码。 合约地址上剩余的以太币会被发送给指定的目标, 然后其(当前和未来的区块上)存储和代码被移除。

其实合约的删除也依赖以太坊的各种客户端程序实现(可以选择是否删除旧合约)。 另外,归档节点可选择无限期保留合约存储和代码。 目前,外部账户不能从状态中移除。

以太坊路线图

以太坊的发展分为四个阶段。
前沿 (Frontier)
家园 (Homestead)
第二阶段,以太坊的第一个正式版本
大都会(Metropolis)
第三阶段 ,引入四大特性: zk-Snarks (基于“零知识证明”)、 PoS (Proof of Stake, 权益证明)早期实施、智能合约更灵活和稳定使用抽象账户。大都会拆分为两个阶段实施(两个硬分叉):拜占庭(Byzantium )和君士坦丁堡 (Constantinople)。

  • 拜占庭。拜占庭硬分叉在第 437 万个区块高度发生,后来引入了 zk-Snarks 及抽象账户等。
  • 君士坦丁堡。主要特性就是平滑处理掉所有由于 “拜占庭”所引发的问题,并引入 PoW 和 PoS 的混合链模式。
    宁静(Serenity)
    第四阶段,有两大主要特性:深度抽象和 Casper (基于保证金的权益证明 算法)。
    重点关注:拜占庭与君士坦丁堡。

借鉴书籍:精通以太坊智能合约开发

第二章:以太坊智能合约内容、数据类型的认识https://blog.csdn.net/qq_44423523/article/details/108296586

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值