完全理解以太坊智能合约

转载自登链社区

快速了解区块链

如果我们不关注细节的话,区块链技术的核心概念是分布式账本。它是许多参与者共享的特定类型的数据库。

这个特殊的数据库只是一个交易列表,记录着网络中发生的每笔交易。每个人都可以拥有自己的交易列表备份,再加上强有力的货币激励措施消除各方之间信任成本。

传统交易通常引入第三方(双方信任的中间人)来解决相互不信任的人进行的交易,如支付宝、Paypal、银行。

使用区块链,可以把信任放在一个网络中,不在需要第三方,它通过强有力的激励去规避作弊行文。

更具体一点:区块链网络是一堆机器,它们都具有相同的交易列表,由于每个人都有相同的列表,因此很难欺骗网络接受虚假交易。通过与一些加密算法和货币激励相结合,只要大家都遵循规则就可以构建一个安全的区块链网络。

通过这种方式也使得区块链几乎无法篡改,改变历史记录的唯一方法是让大多数节点(矿工)同意这样做。

什么事智能合约

以太坊与比特币很大的不同是以太坊拥有智能合约的概念,比特币是数字货币-价值存储。而以太坊不单单是数字货币,智能合约 这个名称有点误导,它不是真正的合约,也不是特别智能,他们只是可以在区块链上运行的代码。

首先要了解的是智能合约是以太坊网络上的一种特殊账户,我们有用户账户,还可以拥有智能合约账户

用户账户:

  • 地址(有点像银行账户,比特币也有同样的概念)
  • 余额(我有多少钱 - 以太)

智能合约账户:

  • 地址
  • 余额(有多少钱 - 以太)
  • 状态
  • 代码

地址:是账户的唯一标识符,与常规用户账户一样;

余额:也与常规用户账户相同,不同的是,智能合约的余额意味着代码可以拥有资产,它可以管理资产,因此如果代码不正确,它可能会错误处理这边资金。

状态:智能合约账户的状态是智能合约中声明的所有变量和变量的当前状态,它的工作方式与大多数编程语言中的类中的变量相同。实际上,最简单方法去理解智能合约可以类比为一个类实例化对象,唯一的区别是这个对象永远存在区块链网络中(除非程序进行自毁)。

代码:智能合约的代码是编译后可以在以太坊客户端和节点可以运行的字节码,它是在创建智能合约时执行的代码,它包含我们可以调用的函数,就像面向对象编程语言中的对象一样。

注:智能合约更有趣地方,他们可以调用其他智能合约,这开启了创造一个自主代理的能力,这些代理可以自己进行交易。

contract Counter {
    uint counter;

    function Counter() public {
        counter = 0;
    }
    function count() public {
        counter = counter + 1;
    }
}

假设我们使用上面的代码创建一个智能合约,代码有一个类型为 unit(无符合整数)名为counter 的变量。counter 变量的内容(值)就是该合约的状态,每当我们调用 count()函数时,此智能合约的区块链状态将增加1,这个状态是对任何人都可见的。

以太坊和比特币在交易层面的区别

比特币交易非常简单,它只做一件事,就是进行交易。忽略细节,这一切都归结为TO(谁收钱),FROM(谁汇款)和AMOUNT(多少钱)。这让比特币网络中的参与者可以传递价值并存储价值。

以太坊很大的不同是其交易还有一个DATA 字段。DATA 字段支持三种类型的交易:

  1. 价值传递(和比特币相同)

    TO:收款地址

    DATA:留空或留言信息

    FROM:谁发出

    AMOUNT:发送多少

  2. 创建合约

    TO:留空(这就是触发创建智能合约的原因)

    DATA:包含编译为字节码的智能合约代码

    FROM:谁创建

    AMOUNT:可以是零或任何数量的以太,它是我们想要给合约的存款

  3. 调用合约函数

    TO:目标合约账户地址

    DATA:包含函数名称和参数 - 标识如何调用智能合约函数

    FROM:谁调用

    AMOUNT:可以是零或任意数量的以太,例如可以支付合约服务费用

以太坊交易

价值传递

{
    to: '0x687422eEA2cB73B5d3e242bA5456b782919AFc85',
    value: 0.0005
    data: ‘0x’ // 也可以附加消息
}

非常简单,就是转移一定数量的以太坊到某个地址,如果我们愿意也可以向交易添加信息。

创建智能合约

{
    to: '',
    value: 0.0
    data: '0x6060604052341561000c57xlb60405160c0806……………'
}

如上所述,TO为空表示创建智能合约,DATA 包含编译为字节码的智能合约代码。

调用合约方法

{
    to: '0x687422eEA2cB73B5d3e242bA5456b782919AFc85’, //合约
    value: 0.0
    data: '0x6060604052341561000c57fe5b60405160c0806……………'
}

函数调用信息放在DATA变量中,把这个交易信息发送到要调用的智能合约的地址。

关于成本和执行

可以想象,我们不能一直在区块链上免费运行计算量很大的程序。

代码的执行由调用者以称为Gas的方式支付费用。Gas是运行以太坊虚拟机的燃料。我们可以将其市委每次执行指令的付款。

在发起调用时,我们必须设置此调用上预计花费的最大Gas消耗量(Gas Limit)。例如,如果我们调用的代码进入永久循环,执行花费不会超过我们设定的最大消耗量(Gas Limit)。

Gas执行费用由网络的矿工(他们是运行代码的节点)决定的。

Gas 参考文章: 以太坊,Gas,燃料和费用

智能合约是如何运行的

当智能合约部署到以太坊网络时,任何人都可以调用智能合约的功能(当然也可能存在某些安全原因阻止人们使用的功能,但不妨碍我们自由尝试)。

在智能合约上调用函数在很多方面和正常编程类似 - 当然在执行方面存在一些差异。

假设我们有一个 “MyObject” 类型的对象,该对象有一个名为 “myFunction” 的函数。要调用它,我们可以简单地引用对象的实例,调用哪个函数以及调用它的参数。例如:

myObjectReference.myFunction(parameters);

如果函数返回任何值,则可以把它保存在变量中:

myVariable = myObject.myFunction(parameters);

从概念上讲,调用智能合约也是这样一回事。唯一的区别是我们必须将有关调用的信息放入交易中,对其进行签名并发送到以太坊网络中执行。

假设想用一些参数调用智能合约 “0x0123456” 上函数 “myFunction”,大概包含以下4个步骤:

现在,当交易被打包放入区块链时,状态的改变将反映在整个网络中。

世界计算机

许多人称以太坊为世界计算机,这不算一个糟糕的比喻,它确实像一个由全世界维护的虚拟机。

有一点要注意:虽然智能合约是图灵完整的,理论上可以做任何事情,但他们并不合适繁重的计算工作。

以太坊世界计算机就像一台老旧的慢速计算机,可以运行简单的程序,基于成本和安全性考虑,保持以太坊智能合约小而简单至关重要。

合约的计算越多,运行它的成本就越高。合约越复杂,就越有可能出现安全漏洞。智能合约中的安全漏洞是很难修复的,因为区块链的不可篡改特性。

智能合约应用:Token(通证或代币)

解释一下通证是如何工作的,哪些在ICO中每个人都赞不绝口的代币,就是一个智能合约,我们自己也可以在以太坊上创建自己的代币。

大多数这些代币都是在以太坊上创建的,这个概念非常简单,我们需要关注以下几个信息:

  • 总供应量
  • 账号
  • 账户中的金额
  • 代币的流动

通过用户和金额之间的简单映射,可以实现1、2和3:

Map<Account, Double> usersAndTheirMoney;

使用构造函数,我们可以在自己的账户中设置初始供应量(或在任意其他中帐户中分配):

public Token(Account initialAccount, double initialSupply) {
  usersAndTheirMoney.put(initialAccount, initialSupply);
}

代币的移动是通过简单的功能完成的,只需从一个账户中减去并添加到另一个账户:

public transfer(Account from, Account to, double amount) {
  verifySenderOfMoneyIsCaller(from);
  verifySenderOfMoneyHasEnoughMoney(from, amount);
  usersAndTheirMoney.put(from, usersAndTheirMoney.get(from)-amount);
  usersAndTheirMoney.put(to, usersAndTheirMoney.get(to)+amount);
}

这与我们在以太坊中用于创建通证的完全相同的概念。不过以太坊有自己的编程语言 Solidity ,使用 Solidity 编写代码大概这个样子:

contract MyToken {
    mapping (address => uint256) public balances;
    function MyToken(uint256 initialSupply) {
        balances[msg.sender] = initialSupply;
    }   
   
    function transfer(address to, uint256 amount) public {
        balances[msg.sender] -= amount;
        balances[to] += amount;
    }
}

本文英文地址