精华内容
下载资源
问答
  • Solidity中,一个合约由一组代码(合约的函数)和数据(合约的状态)组成。合约位于以太坊区块链上的一个特殊地址。uint storedData;这行代码声明了一个状态变量,变量名为storedData,类型为uint (256bits无符号...
  • 1、智能合约概述 2、区块链基础 3、以太坊虚拟机 4、安装Solidity编译器 5、从源代码编译 6、CMake参数 7、版本号字符串详解 8、版本信息详情 9、实例学习Solidity 10、秘密竞价(盲拍) 11、安全的远程购买实例 12...

    如果你有其他语言基础,可以很快入门,如果新手建议了解即可,以下是课程目录:

    1、智能合约概述
    2、区块链基础
    3、以太坊虚拟机
    4、安装Solidity编译器
    5、从源代码编译
    6、CMake参数
    7、版本号字符串详解
    8、版本信息详情
    9、实例学习Solidity
    10、秘密竞价(盲拍)
    11、安全的远程购买实例
    12、深入理解Solidity-源文件结构
    13、深入理解Solidity-合约结构
    14、深入理解Solidity-类型(本篇较长)
    15、深入理解Solidity-单位和全局变量
    16、深入理解Solidity-特殊变量和函数
    17、深入理解Solidity-表达式和控制结构(本篇略长)
    18、深入理解Solidity-合约
    19、深入理解Solidity-函数
    20、深入理解Solidity-事件
    21、深入理解Solidity-继承
    22、深入理解Solidity-抽象合约
    23、深入理解Solidity-接口
    24、深入理解Solidity-库
    25、深入理解Solidity-Using For
    26、深入理解Solidity-Solidity汇编之内联汇编(本篇微长)
    27、深入理解Solidity-Solidity汇编之独立汇编
    28、深入理解Solidity-杂项(有点小长)
    29、安全考量
    30、安全考量-陷阱
    31、安全考量-推荐做法
    32、使用编译器
    33、编译器输入输出JSON描述
    34、合约的元数据
    35、应用二进制接口(ABI) 说明

    原文地址:https://www.tigertoken.team/tiger-498-1-2.html

    展开全文
  • solidity编写智能合约

    千次阅读 2018-03-26 14:44:23
    原文地址:http://www.cnblogs.com/Ellon-Daris/p/5468504.html一个简单的智能合约先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。存储contract SimpleStorage { uint ...

    原文地址:http://www.cnblogs.com/Ellon-Daris/p/5468504.html


    一个简单的智能合约

    先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。

    存储

    contract SimpleStorage {
        uint storedData;
        function set(uint x) {
            storedData = x;
        }
        function get() constant returns (uint retVal) {
            return storedData;
        }
    }

    在Solidity中,一个合约由一组代码(合约的函数)和数据(合约的状态)组成。合约位于以太坊区块链上的一个特殊地址。uint storedData; 这行代码声明了一个状态变量,变量名为storedData,类型为 uint (256bits无符号整数)。你可以认为它就像数据库里面的一个存储单元,跟管理数据库一样,可以通过调用函数查询和修改它。在以太坊中,通常只有合约 的拥有者才能这样做。在这个例子中,函数 set 和 get 分别用于修改和查询变量的值。

    跟很多其他语言一样,访问状态变量时,不需要在前面增加 this. 这样的前缀。

    这个合约还无法做很多事情(受限于以太坊的基础设施),仅仅是允许任何人储存一个数字。而且世界上任何一个人都可以来存取这个数字,缺少一个(可靠 的)方式来保护你发布的数字。任何人都可以调用set方法设置一个不同的数字覆盖你发布的数字。但是你的数字将会留存在区块链的历史上。稍后我们会学习如 何增加一个存取限制,使得只有你才能修改这个数字。

    代币的例子

    接下来的合约将实现一个形式最简单的加密货币。空中取币不再是一个魔术,当然只有创建合约的人才能做这件事情(想用其他货币发行模式也很简单,只是实现细节上的差异)。而且任何人都可以发送货币给其他人,不需要注册用户名和密码,只要有一对以太坊的公私钥即可。

    注意
    对于在线solidity环境来说,这不是一个好的例子。如果你使用在线solidity环境 来尝试这个例子。调用函数时,将无法改变from的地址。所以你只能扮演铸币者的角色,可以铸造货币并发送给其他人,而无法扮演其他人的角色。这点在线 solidity环境将来会做改进。

    contract Coin {
    
    
    //关键字“public”使变量能从合约外部访问。
        address public minter;
        mapping (address => uint) public balances;
    
    //事件让轻客户端能高效的对变化做出反应。
        event Sent(address from, address to, uint amount);
    
    //这个构造函数的代码仅仅只在合约创建的时候被运行。
        function Coin() {
            minter = msg.sender;
        }
        function mint(address receiver, uint amount) {
            if (msg.sender != minter) return;
            balances[receiver] += amount;
        }
        function send(address receiver, uint amount) {
            if (balances[msg.sender] < amount) return;
            balances[msg.sender] -= amount;
            balances[receiver] += amount;
            Sent(msg.sender, receiver, amount);
        }
    }

    这个合约引入了一些新的概念,让我们一个一个来看一下。

    address public minter 这行代码声明了一个可公开访问的状态变量,类型为address。address类型的值大小为160 bits,不支持任何算术操作。适用于存储合约的地址或其他人的公私钥。public关键字会自动为其修饰的状态变量生成访问函数。没有public关键 字的变量将无法被其他合约访问。另外只有本合约内的代码才能写入。自动生成的函数如下:

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

    当然我们自己增加一个这样的访问函数是行不通的。编译器会报错,指出这个函数与一个状态变量重名。

    下一行代码mapping (address => uint) public balances; 创建了一个public的状态变量,但是其类型更加的复杂。该类型将一些address映射到无符号整数。mapping可以被认为是一个哈希表,每一个可能的key对应的value被虚拟的初始化为全0.这个类比不是很严谨,对于一个mapping,无法获取一个包含其所有key或者value 的链表。所以我们得自己记着添加了哪些东西到mapping中。更好的方式是维护一个这样的链表,或者使用其他更高级的数据类型。或者只在不受这个缺陷影 响的场景中使用mapping,就像这个例子。在这个例子中由public关键字生成的访问函数将会更加复杂,其代码大致如下:

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

    我们可以很方便的通过这个函数查询某个特定账号的余额。

    event Sent(address from, address to, uint value)这行代码声明了一 个“事件”。由send函数的最后一行代码触发。客户端(服务端应用也适用)可以以很低的开销来监听这些由区块链触发的事件。事件触发时,监听者会同时接 收到from,to,value这些参数值,可以方便的用于跟踪交易。为了监听这个事件,你可以使用如下代码:

    Coin.Sent().watch({}, '', function(error, result) {
        if (!error) {
            console.log("Coin transfer: " + result.args.amount +
                " coins were sent from " + result.args.from +
                " to " + result.args.to + ".");
            console.log("Balances now:\n" +
                "Sender: " + Coin.balances.call(result.args.from) +
                "Receiver: " + Coin.balances.call(result.args.to));
        }
    }

    注意在客户端中是如何调用自动生成的 balances 函数的。

    这里有个比较特殊的函数 Coin。它是一个构造函数,会在合约创建的时候运行,之后就无法被调用。它会永久得存储合约创建者的地址。msg(以及tx和block)是一个神奇的 全局变量,它包含了一些可以被合约代码访问的属于区块链的属性。msg.sender 总是存放着当前函数的外部调用者的地址。

    最后,真正被用户或者其他合约调用,用来完成本合约功能的函数是mint和send。如果合约创建者之外的其他人调用mint,什么都不会发生。而 send可以被任何人(拥有一定数量的代币)调用,发送一些币给其他人。注意,当你通过该合约发送一些代币到某个地址,在区块链浏览器中查询该地址将什么 也看不到。因为发送代币导致的余额变化只存储在该代币合约的数据存储中。通过事件我们可以很容易创建一个可以追踪你的新币交易和余额的“区块链浏览器”。

    区块链基础

    对于程序员来说,区块链这个概念其实不难理解。因为最难懂的一些东西(挖矿,哈希,椭圆曲线加密,点对点网络等等)只是为了提供一系列的特性和保 障。你只需要接受这些既有的特性,不需要关心其底层的技术。就像你如果仅仅是为了使用亚马逊的AWS,并不需要了解其内部工作原理。

    交易/事务

    区块链是一个全局共享的,事务性的数据库。这意味着参与这个网络的每一个人都可以读取其中的记录。如果你想修改这个数据库中的东西,就必须创建一个 事务,并得到其他所有人的确认。事务这个词意味着你要做的修改(假如你想同时修改两个值)只能被完完全全的实施或者一点都没有进行。

    此外,当你的事务被应用到这个数据库的时候,其他事务不能修改该数据库。

    举个例子,想象一张表,里面列出了某个电子货币所有账号的余额。当从一个账户到另外一个账户的转账请求发生时,这个数据库的事务特性确保从一个账户中减掉的金额会被加到另一个账户上。如果因为某种原因,往目标账户上增加金额无法进行,那么源账户的金额也不会发生任何变化。

    此外,一个事务会被发送者(创建者)进行密码学签名。这项措施非常直观的为数据库的特定修改增加了访问保护。在电子货币的例子中,一个简单的检查就可以确保只有持有账户密钥的人,才能从该账户向外转账。

    区块

    区块链要解决的一个主要难题,在比特币中被称为“双花攻击”。当网络上出现了两笔交易,都要花光一个账户中的钱时,会发生什么?一个冲突?

    简单的回答是你不需要关心这个问题。这些交易会被排序并打包成“区块”,然后被所有参与的节点执行和分发。如果两笔交易相互冲突,排序靠后的交易会被拒绝并剔除出区块。

    这些区块按时间排成一个线性序列。这也正是“区块链”这个词的由来。区块以一个相当规律的时间间隔加入到链上。对于以太坊,这个间隔大致是17秒。

    作为“顺序选择机制”(通常称为“挖矿”)的一部分,一段区块链可能会时不时被回滚。但这种情况只会发生在整条链的末端。回滚涉及的区块越多,其发生的概率越小。所以你的交易可能会被回滚,甚至会被从区块链中删除。但是你等待的越久,这种情况发生的概率就越小。

    以太坊虚拟机

    总览

    以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它不仅被沙箱封装起来,事实上它被完全隔离,也就是说运行在EVM内部的代码不能接触到网络、文件系统或者其它进程。甚至智能合约与其它智能合约只有有限的接触。

    账户

    以太坊中有两类账户,它们共用同一个地址空间。外部账户,该类账户被公钥-私钥对控制(人类)。合约账户,该类账户被存储在账户中的代码控制。

    外部账户的地址是由公钥决定的,合约账户的地址是在创建改合约时确定的(这个地址由合约创建者的地址和该地址发出过的交易数量计算得到,地址发出过的交易数量也被称作"nonce")

    合约账户存储了代码,外部账户则没有,除了这点以外,这两类账户对于EVM来说是一样的。

    每个账户有一个key-value形式的持久化存储。其中key和value的长度都是256bit,名字叫做storage.

    另外,每个账户都有一个以太币余额(单位是“Wei"),该账户余额可以通过向它发送带有以太币的交易来改变。

    交易

    一笔交易是一条消息,从一个账户发送到另一个账户(可能是相同的账户或者零账户,见下文)。交易可以包含二进制数据(payload)和以太币。

    如果目标账户包含代码,该代码会执行,payload就是输入数据。

    如果目标账户是零账户(账户地址是0),交易将创建一个新合约。正如上文所讲,这个合约地址不是零地址,而是由合约创建者的地址和该地址发出过的交 易数量(被称为nonce)计算得到。创建合约交易的payload被当作EVM字节码执行。执行的输出做为合约代码被永久存储。这意味着,为了创建一个 合约,你不需要向合约发送真正的合约代码,而是发送能够返回真正代码的代码。

    Gas

    以太坊上的每笔交易都会被收取一定数量的gas,gas的目的是限制执行交易所需的工作量,同时为执行支付费用。当EVM执行交易时,gas将按照特定规则被逐渐消耗。

    gas price(gas价格,以太币计)是由交易创建者设置的,发送账户需要预付的交易费用 = gas price * gas amount。 如果执行结束还有gas剩余,这些gas将被返还给发送账户。

    无论执行到什么位置,一旦gas被耗尽(比如降为负值),将会触发一个out-of-gas异常。当前调用帧所做的所有状态修改都将被回滚。

    存储,主存和栈

    每个账户有一块持久化内存区域被称为存储。其形式为key-value,key和value的长度均为256比特。在合约里,不能遍历账户的存储。相对于另外两种,存储的读操作相对来说开销较大,修改存储更甚。一个合约只能对它自己的存储进行读写。

    第二个内存区被称为主存。合约执行每次消息调用时,都有一块新的,被清除过的主存。主存可以以字节粒度寻址,但是读写粒度为32字节(256比特)。操作主存的开销随着其增长而变大(平方级别)。

    EVM不是基于寄存器,而是基于栈的虚拟机。因此所有的计算都在一个被称为栈的区域执行。栈最大有1024个元素,每个元素256比特。对栈的访问 只限于其顶端,方式为:允许拷贝最顶端的16个元素中的一个到栈顶,或者是交换栈顶元素和下面16个元素中的一个。所有其他操作都只能取最顶的两个(或一 个,或更多,取决于具体的操作)元素,并把结果压在栈顶。当然可以把栈上的元素放到存储或者主存中。但是无法只访问栈上指定深度的那个元素,在那之前必须 要把指定深度之上的所有元素都从栈中移除才行。

    指令集

    EVM的指令集被刻意保持在最小规模,以尽可能避免可能导致共识问题的错误实现。所有的指令都是针对256比特这个基本的数据类型的操作。具备常用的算术,位,逻辑和比较操作。也可以做到条件和无条件跳转。此外,合约可以访问当前区块的相关属性,比如它的编号和时间戳。

    消息调用

    合约可以通过消息调用的方式来调用其它合约或者发送以太币到非合约账户。消息调用和交易非常类似,它们都有一个源,一个目标,数据负载,以太币,gas和返回数据。事实上每个交易都可以被认为是一个顶层消息调用,这个消息调用会依次产生更多的消息调用。

    一个合约可以决定剩余gas的分配。比如内部消息调用时使用多少gas,或者期望保留多少gas。如果在内部消息调用时发生了out-of-gas 异常(或者其他异常),合约将会得到通知,一个错误码被压在栈上。这种情况只是内部消息调用的gas耗尽。在solidity中,这种情况下发起调用的合 约默认会触发一个人工异常。这个异常会打印出调用栈。

    就像之前说过的,被调用的合约(发起调用的合约也一样)会拥有崭新的主存并能够访问调用的负载。调用负载被存储在一个单独的被称为calldata的区域。调用执行结束后,返回数据将被存放在调用方预先分配好的一块内存中。

    调用层数被限制为1024,因此对于更加复杂的操作,我们应该使用循环而不是递归。

    代码调用和库

    存在一种特殊类型的消息调用,被称为callcode。它跟消息调用几乎完全一样,只是加载自目标地址的代码将在发起调用的合约上下文中运行。

    这意味着一个合约可以在运行时从另外一个地址动态加载代码。存储,当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。

    这使得Solidity可以实现”库“。可复用的库代码可以应用在一个合约的存储上,可以用来实现复杂的数据结构。

    日志

    在区块层面,可以用一种特殊的可索引的数据结构来存储数据。这个特性被称为日志,Solidity用它来实现事件。合约创建之后就无法访问日志数 据,但是这些数据可以从区块链外高效的访问。因为部分日志数据被存储在布隆过滤器(Bloom filter) 中,我们可以高效并且安全的搜索日志,所以那些没有下载整个区块链的网络节点(轻客户端)也可以找到这些日志。

    创建

    合约甚至可以通过一个特殊的指令来创建其他合约(不是简单的向零地址发起调用)。创建合约的调用跟普通的消息调用的区别在于,负载数据执行的结果被当作代码,调用者/创建者在栈上得到新合约的地址。

    自毁

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

    注意,即使一个合约的代码不包含自毁指令,依然可以通过代码调用(callcode)来执行这个操作。



    展开全文
  • 首先创建一个pet_token.sol文件,作为入口合约 pragma solidity ^0.4.4; import ./pet_factory.sol; import ./ecr712.sol; // PET 宠物 version - 1.0 contract PetToken is PetFactory, ERC721{ string public ...
  • solidity编写智能合约(入门)

    千次阅读 2016-05-07 15:54:00
    一个简单的智能合约 先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。 存储 contract SimpleStorage { uint storedData; function set(uint x) { storedData = x;...

    一个简单的智能合约

    先从一个非常基础的例子开始,不用担心你现在还一点都不了解,我们将逐步了解到更多的细节。

    存储

    contract SimpleStorage {
        uint storedData;
        function set(uint x) {
            storedData = x;
        }
        function get() constant returns (uint retVal) {
            return storedData;
        }
    }

    在Solidity中,一个合约由一组代码(合约的函数)和数据(合约的状态)组成。合约位于以太坊区块链上的一个特殊地址。uint storedData; 这行代码声明了一个状态变量,变量名为storedData,类型为 uint (256bits无符号整数)。你可以认为它就像数据库里面的一个存储单元,跟管理数据库一样,可以通过调用函数查询和修改它。在以太坊中,通常只有合约 的拥有者才能这样做。在这个例子中,函数 set 和 get 分别用于修改和查询变量的值。

    跟很多其他语言一样,访问状态变量时,不需要在前面增加 this. 这样的前缀。

    这个合约还无法做很多事情(受限于以太坊的基础设施),仅仅是允许任何人储存一个数字。而且世界上任何一个人都可以来存取这个数字,缺少一个(可靠 的)方式来保护你发布的数字。任何人都可以调用set方法设置一个不同的数字覆盖你发布的数字。但是你的数字将会留存在区块链的历史上。稍后我们会学习如 何增加一个存取限制,使得只有你才能修改这个数字。

    代币的例子

    接下来的合约将实现一个形式最简单的加密货币。空中取币不再是一个魔术,当然只有创建合约的人才能做这件事情(想用其他货币发行模式也很简单,只是实现细节上的差异)。而且任何人都可以发送货币给其他人,不需要注册用户名和密码,只要有一对以太坊的公私钥即可。

    注意
    对于在线solidity环境来说,这不是一个好的例子。如果你使用在线solidity环境 来尝试这个例子。调用函数时,将无法改变from的地址。所以你只能扮演铸币者的角色,可以铸造货币并发送给其他人,而无法扮演其他人的角色。这点在线 solidity环境将来会做改进。

    contract Coin {

    //关键字“public”使变量能从合约外部访问。
        address public minter;
        mapping (address => uint) public balances;
    
    //事件让轻客户端能高效的对变化做出反应。
        event Sent(address from, address to, uint amount);
    
    //这个构造函数的代码仅仅只在合约创建的时候被运行。
        function Coin() {
            minter = msg.sender;
        }
        function mint(address receiver, uint amount) {
            if (msg.sender != minter) return;
            balances[receiver] += amount;
        }
        function send(address receiver, uint amount) {
            if (balances[msg.sender] < amount) return;
            balances[msg.sender] -= amount;
            balances[receiver] += amount;
            Sent(msg.sender, receiver, amount);
        }
    }

    这个合约引入了一些新的概念,让我们一个一个来看一下。

    address public minter 这行代码声明了一个可公开访问的状态变量,类型为address。address类型的值大小为160 bits,不支持任何算术操作。适用于存储合约的地址或其他人的公私钥。public关键字会自动为其修饰的状态变量生成访问函数。没有public关键 字的变量将无法被其他合约访问。另外只有本合约内的代码才能写入。自动生成的函数如下:

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

    当然我们自己增加一个这样的访问函数是行不通的。编译器会报错,指出这个函数与一个状态变量重名。

    下一行代码mapping (address => uint) public balances; 创建了一个public的状态变量,但是其类型更加的复杂。该类型将一些address映射到无符号整数。mapping可以被认为是一个哈希表,每一个可能的key对应的value被虚拟的初始化为全0.这个类比不是很严谨,对于一个mapping,无法获取一个包含其所有key或者value 的链表。所以我们得自己记着添加了哪些东西到mapping中。更好的方式是维护一个这样的链表,或者使用其他更高级的数据类型。或者只在不受这个缺陷影 响的场景中使用mapping,就像这个例子。在这个例子中由public关键字生成的访问函数将会更加复杂,其代码大致如下:

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

    我们可以很方便的通过这个函数查询某个特定账号的余额。

    event Sent(address from, address to, uint value)这行代码声明了一 个“事件”。由send函数的最后一行代码触发。客户端(服务端应用也适用)可以以很低的开销来监听这些由区块链触发的事件。事件触发时,监听者会同时接 收到from,to,value这些参数值,可以方便的用于跟踪交易。为了监听这个事件,你可以使用如下代码:

    Coin.Sent().watch({}, '', function(error, result) {
        if (!error) {
            console.log("Coin transfer: " + result.args.amount +
                " coins were sent from " + result.args.from +
                " to " + result.args.to + ".");
            console.log("Balances now:\n" +
                "Sender: " + Coin.balances.call(result.args.from) +
                "Receiver: " + Coin.balances.call(result.args.to));
        }
    }

    注意在客户端中是如何调用自动生成的 balances 函数的。

    这里有个比较特殊的函数 Coin。它是一个构造函数,会在合约创建的时候运行,之后就无法被调用。它会永久得存储合约创建者的地址。msg(以及tx和block)是一个神奇的 全局变量,它包含了一些可以被合约代码访问的属于区块链的属性。msg.sender 总是存放着当前函数的外部调用者的地址。

    最后,真正被用户或者其他合约调用,用来完成本合约功能的函数是mint和send。如果合约创建者之外的其他人调用mint,什么都不会发生。而 send可以被任何人(拥有一定数量的代币)调用,发送一些币给其他人。注意,当你通过该合约发送一些代币到某个地址,在区块链浏览器中查询该地址将什么 也看不到。因为发送代币导致的余额变化只存储在该代币合约的数据存储中。通过事件我们可以很容易创建一个可以追踪你的新币交易和余额的“区块链浏览器”。

    区块链基础

    对于程序员来说,区块链这个概念其实不难理解。因为最难懂的一些东西(挖矿,哈希,椭圆曲线加密,点对点网络等等)只是为了提供一系列的特性和保 障。你只需要接受这些既有的特性,不需要关心其底层的技术。就像你如果仅仅是为了使用亚马逊的AWS,并不需要了解其内部工作原理。

    交易/事务

    区块链是一个全局共享的,事务性的数据库。这意味着参与这个网络的每一个人都可以读取其中的记录。如果你想修改这个数据库中的东西,就必须创建一个 事务,并得到其他所有人的确认。事务这个词意味着你要做的修改(假如你想同时修改两个值)只能被完完全全的实施或者一点都没有进行。

    此外,当你的事务被应用到这个数据库的时候,其他事务不能修改该数据库。

    举个例子,想象一张表,里面列出了某个电子货币所有账号的余额。当从一个账户到另外一个账户的转账请求发生时,这个数据库的事务特性确保从一个账户中减掉的金额会被加到另一个账户上。如果因为某种原因,往目标账户上增加金额无法进行,那么源账户的金额也不会发生任何变化。

    此外,一个事务会被发送者(创建者)进行密码学签名。这项措施非常直观的为数据库的特定修改增加了访问保护。在电子货币的例子中,一个简单的检查就可以确保只有持有账户密钥的人,才能从该账户向外转账。

    区块

    区块链要解决的一个主要难题,在比特币中被称为“双花攻击”。当网络上出现了两笔交易,都要花光一个账户中的钱时,会发生什么?一个冲突?

    简单的回答是你不需要关心这个问题。这些交易会被排序并打包成“区块”,然后被所有参与的节点执行和分发。如果两笔交易相互冲突,排序靠后的交易会被拒绝并剔除出区块。

    这些区块按时间排成一个线性序列。这也正是“区块链”这个词的由来。区块以一个相当规律的时间间隔加入到链上。对于以太坊,这个间隔大致是17秒。

    作为“顺序选择机制”(通常称为“挖矿”)的一部分,一段区块链可能会时不时被回滚。但这种情况只会发生在整条链的末端。回滚涉及的区块越多,其发生的概率越小。所以你的交易可能会被回滚,甚至会被从区块链中删除。但是你等待的越久,这种情况发生的概率就越小。

    以太坊虚拟机

    总览

    以太坊虚拟机(EVM)是以太坊中智能合约的运行环境。它不仅被沙箱封装起来,事实上它被完全隔离,也就是说运行在EVM内部的代码不能接触到网络、文件系统或者其它进程。甚至智能合约与其它智能合约只有有限的接触。

    账户

    以太坊中有两类账户,它们共用同一个地址空间。外部账户,该类账户被公钥-私钥对控制(人类)。合约账户,该类账户被存储在账户中的代码控制。

    外部账户的地址是由公钥决定的,合约账户的地址是在创建改合约时确定的(这个地址由合约创建者的地址和该地址发出过的交易数量计算得到,地址发出过的交易数量也被称作"nonce")

    合约账户存储了代码,外部账户则没有,除了这点以外,这两类账户对于EVM来说是一样的。

    每个账户有一个key-value形式的持久化存储。其中key和value的长度都是256bit,名字叫做storage.

    另外,每个账户都有一个以太币余额(单位是“Wei"),该账户余额可以通过向它发送带有以太币的交易来改变。

    交易

    一笔交易是一条消息,从一个账户发送到另一个账户(可能是相同的账户或者零账户,见下文)。交易可以包含二进制数据(payload)和以太币。

    如果目标账户包含代码,该代码会执行,payload就是输入数据。

    如果目标账户是零账户(账户地址是0),交易将创建一个新合约。正如上文所讲,这个合约地址不是零地址,而是由合约创建者的地址和该地址发出过的交 易数量(被称为nonce)计算得到。创建合约交易的payload被当作EVM字节码执行。执行的输出做为合约代码被永久存储。这意味着,为了创建一个 合约,你不需要向合约发送真正的合约代码,而是发送能够返回真正代码的代码。

    Gas

    以太坊上的每笔交易都会被收取一定数量的gas,gas的目的是限制执行交易所需的工作量,同时为执行支付费用。当EVM执行交易时,gas将按照特定规则被逐渐消耗。

    gas price(gas价格,以太币计)是由交易创建者设置的,发送账户需要预付的交易费用 = gas price * gas amount。 如果执行结束还有gas剩余,这些gas将被返还给发送账户。

    无论执行到什么位置,一旦gas被耗尽(比如降为负值),将会触发一个out-of-gas异常。当前调用帧所做的所有状态修改都将被回滚。

    存储,主存和栈

    每个账户有一块持久化内存区域被称为存储。其形式为key-value,key和value的长度均为256比特。在合约里,不能遍历账户的存储。相对于另外两种,存储的读操作相对来说开销较大,修改存储更甚。一个合约只能对它自己的存储进行读写。

    第二个内存区被称为主存。合约执行每次消息调用时,都有一块新的,被清除过的主存。主存可以以字节粒度寻址,但是读写粒度为32字节(256比特)。操作主存的开销随着其增长而变大(平方级别)。

    EVM不是基于寄存器,而是基于栈的虚拟机。因此所有的计算都在一个被称为栈的区域执行。栈最大有1024个元素,每个元素256比特。对栈的访问 只限于其顶端,方式为:允许拷贝最顶端的16个元素中的一个到栈顶,或者是交换栈顶元素和下面16个元素中的一个。所有其他操作都只能取最顶的两个(或一 个,或更多,取决于具体的操作)元素,并把结果压在栈顶。当然可以把栈上的元素放到存储或者主存中。但是无法只访问栈上指定深度的那个元素,在那之前必须 要把指定深度之上的所有元素都从栈中移除才行。

    指令集

    EVM的指令集被刻意保持在最小规模,以尽可能避免可能导致共识问题的错误实现。所有的指令都是针对256比特这个基本的数据类型的操作。具备常用的算术,位,逻辑和比较操作。也可以做到条件和无条件跳转。此外,合约可以访问当前区块的相关属性,比如它的编号和时间戳。

    消息调用

    合约可以通过消息调用的方式来调用其它合约或者发送以太币到非合约账户。消息调用和交易非常类似,它们都有一个源,一个目标,数据负载,以太币,gas和返回数据。事实上每个交易都可以被认为是一个顶层消息调用,这个消息调用会依次产生更多的消息调用。

    一个合约可以决定剩余gas的分配。比如内部消息调用时使用多少gas,或者期望保留多少gas。如果在内部消息调用时发生了out-of-gas 异常(或者其他异常),合约将会得到通知,一个错误码被压在栈上。这种情况只是内部消息调用的gas耗尽。在solidity中,这种情况下发起调用的合 约默认会触发一个人工异常。这个异常会打印出调用栈。

    就像之前说过的,被调用的合约(发起调用的合约也一样)会拥有崭新的主存并能够访问调用的负载。调用负载被存储在一个单独的被称为calldata的区域。调用执行结束后,返回数据将被存放在调用方预先分配好的一块内存中。

    调用层数被限制为1024,因此对于更加复杂的操作,我们应该使用循环而不是递归。

    代码调用和库

    存在一种特殊类型的消息调用,被称为callcode。它跟消息调用几乎完全一样,只是加载自目标地址的代码将在发起调用的合约上下文中运行。

    这意味着一个合约可以在运行时从另外一个地址动态加载代码。存储,当前地址和余额都指向发起调用的合约,只有代码是从被调用地址获取的。

    这使得Solidity可以实现”库“。可复用的库代码可以应用在一个合约的存储上,可以用来实现复杂的数据结构。

    日志

    在区块层面,可以用一种特殊的可索引的数据结构来存储数据。这个特性被称为日志,Solidity用它来实现事件。合约创建之后就无法访问日志数 据,但是这些数据可以从区块链外高效的访问。因为部分日志数据被存储在布隆过滤器(Bloom filter) 中,我们可以高效并且安全的搜索日志,所以那些没有下载整个区块链的网络节点(轻客户端)也可以找到这些日志。

    创建

    合约甚至可以通过一个特殊的指令来创建其他合约(不是简单的向零地址发起调用)。创建合约的调用跟普通的消息调用的区别在于,负载数据执行的结果被当作代码,调用者/创建者在栈上得到新合约的地址。

    自毁

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

    注意,即使一个合约的代码不包含自毁指令,依然可以通过代码调用(callcode)来执行这个操作。

    转载于:https://www.cnblogs.com/Ellon-Daris/p/5468504.html

    展开全文
  • 有好几种语言可以用于编写以太坊智能合约,不过Solidity是最热门的语言。在本章中,我们将首先学习Solidity编程语言。然后创建一个DApp,用于证明在特定时间的存在、真实性和所有权,即证明一个文件在一个特定时间...

    有好几种语言可以用于编写以太坊智能合约,不过Solidity是最热门的语言。在本章中,我们将首先学习Solidity编程语言。然后创建一个DApp,用于证明在特定时间的存在、真实性和所有权,即证明一个文件在一个特定时间属于一个特定所有者。

    要点:

    • Solidity源文件的布局
    • 理解Solidity的数据类型
    • 合约的特殊变量和函数
    • 控制结构
    • 合约的结构和功能
    • 编译和部署合约

    Solidity源文件

    Solidity源文件使用的扩展名为.sol。这里面我们使用的是0.4.2版本。

    智能合约的结构

    合约就像一个类(class),其中包含:

    • 状态变量(state variable)
    • 函数(function)
    • 函数修改器(function modifier)
    • 事件(event)
    • 结构(structure)
    • 枚举(enum)

    同时,合约还支持继承与多态。

    示例:

    contract Sample
    {
        //状态变量
        uint256 data;
        address owner;
    
        //定义事件
        event logData(uint256 dataToLog);
        //函数修改器
        modifier onlyOwner() {
            if(msg.sender!=owner) throw;
    
        }
        //构造器,名字与合约名一致
        function Sample(uint256 initData,address initOwner) {
            data = initData;
            owner = initOwner;
        }
        //函数
        function getData() returns (uint256 returnedData) {
        return data;    
        }
        function setData() returns (uint256 newData) onlyOwner {
            logData(newData);
            data = newData;
        }
    
    }

    代码注释:

    • contract 关键字:用于声明一个合约
    • data和owner:是两个状态变量。data包含一些数据,owner包含所有者的以太坊钱包地址,即部署合约者的以太坊地址
    • event logData 定义事件logData,用于通知客户端:一旦data发生变化,将触发这个事件。所有事件都保存在区块链中。
    • 函数修改器:onlyOwner。修改器用于在执行一个函数之前自动检测文件。这里的修改器用于检测合约所有者是否在调用函数。如果没有,则会抛出异常。

    • 合约函数构造器constructor:在部署合约时,构造器用于初始化状态变量。

    • function,getData()用于得到data状态变量的值,setData()用于改变data的值。

    数据位置(较难理解)

    通常,变量会存储在内存中。但是,在Solidity中,会根据不同的情况,变量可能会不存储在内存和文件系统中。

    通常,在Solidity中,数据有一个默认位置。通常,存储有一个storage位置和一个memory位置,即本地存储与内存存储。

    函数参数,包括其返还参数,默认用memory,本地变量默认用storage,例如状态变量,其数据位置强制使用storage。

    注意:

    • 不能把memory中存储的复杂类型分配给storage;

    什么是不同的数据类型

    首先明白3点;

    • Solidity是一种静态类型语言,变量存储的数据类型需要预先定义。
    • 所有变量默认值都是0。
    • 在Solidity中,变量是有函数作用范围的,也就是说,在函数中任何地方声明的变量将对整个函数存在适用范围。

    那么Solidity提供了哪些数据类型——》

    基本类型

    除了数组类型、字符串类型、结构类型、枚举类型和map类型外,
    其他类型均称为基本类型。

    • 无符号型:例如uint8,uint16,uint24,…,uint256分别用于存储无符号的8位,16
      位,24位,…,256位整数
    • 有符号型:例如,int8,int16,…,int256分别用于存储8位,16位,24位,…,256位整数
    • address类型:用于存储以太坊地址,用16进制表示。address类型有两个属性:balance和send。balance用于检测地址余额,send用于向地址发送以太币。send方法拿出需要转账那
      些数量的wei,并根据转账是否成功返回true或者false。

    注意:

    • uint和int是uint256和int256的别名。
    • 如果一个数字超过256位,则使用256位数据类型存储该数字的近似值。

    • 数组:Solidity支持generic和byte两种数组类型。

    数组有length属性,用于发现数组的长度。

    注意:不可以在内存中改变数组大小,也不可以改变非动态数组大小。

    字符串类型

    有两种方法创建字符串:使用bytes和string。
    bytes用于创建原始字符串,而string用于创建UTF-8字符串

    示例:

    contract sample {
        string myString = "";// string
        bytes myRawString;
    
        function sample(string initString,bytes rawStringInit) {
            myString = initString;
    
            string storage myString2 = myString;
            string memory myString3 = "ABCDE";
            myString3 = "imaginecode";
            myRawString = rawStringInit;
            myRawString.length++;
        }
    }
    

    结构类型struct

    示例

    contract sample {
        struct myStruct {
            bool myBool;
            string myString;
        }
        myStruct s1;
        myStruct s2 = myStruct{true,""}; 
    
        function sample(bool initBool,string initString){
            s1 = myStruct(initBool,initString);
    
            myStruct memory s3 = myStruct(initBool,initString);
        }
    }

    注意:函数参数不可以是结构类型,且函数不可以返回结构类型。

    枚举类型 enum

    示例

    contract sample {
        enum OS {OSX, Linux,Unix,windows }
    
        OS choice;
    
        function sample(OS chosen) {
            choice = chosen;
        }
        function setLinux() {
            choice = OS.Linux;
        }
        function getChoice return (OS chosenOS) {
            return choice;
        }
    }
    

    mapping 类型

    • mapping类型只可以存在于storage中,不存在于memory中,因此它们是作为状态变量声明的。

    • mapping类型包含key/value对,不是实际存储key,而是存储key的keccak256哈希,用于查询value。

    • mapping不可以被分配给另一个mapping。

    constract sample {
        mapping (int => string) myMap;
        function sample(){
            myMap[key] = value;
            mapping (int => string) myMap2 = myMap;
        }
    }

    注意:如果想访问mapping中不存在的key,返回的value为0。

    delete 操作符

    可用于操作任何类型的变量。

    • 对动态数组使用delete操作符,则删除所有元素,其长度变为0。

    • 对静态数组使用delete操作符,则重置所有索引

    • 对map类型使用delete操作符,什么都不会发生,但是,对map类型的一个键使用delete操作符,则会删除与该键相关的值

    示例

    contract sample {
        struct Struct {
            mapping (int => int) myMap;
            int myNumber;
        }
        int[] myArray;
        Struct myStruct;
    
        function sample(int key,int value,int number,int[] array) {
            myStruct = Struct(number);
    
            myStruct = Struct(number);
            myStruct.myMap[key] = value;//对某个键赋值
            myArray = array;
        }
        function reset() {
            delete myArray;//myArray数组长度为0
            delete myStruct;//myNumber为0,myMap不变
        }
        function deleteKey(int key) {
                delete myStruct.myMap[key];//删除myMap中的某个键的值
        }
    }
    

    基本类型之间的转换

    • 隐式转换:常用。通常来说,如果没有语义信息丢失,值和类型之间可以进行隐式转换:uint8可转换为uint16,int128可转换为int256,但是int8不可转换为uint256(因为uint256不能存储,例如-1)
    • Solidity也支持显式转换,如果编译器不允许在两种数据类型之间隐式转换,则可以进行显式转换。建议尽量避免显式转换,因为可能返回难以预料的结果。

    示例:

    uint32 a = 0x12345678;
    uint16 b = uint16(a); // b = 0x5678,将uint32类型显式转换为uint16,也就是说,把较大类型转换为较小类型,因此高位被截掉了

    var

    使用关键字var声明的变量,其变量类型根据分配给它的第一个值来动态确定。一旦分配了值,类型就固定了,所以如果给它指定另一个类型,将引起类型转换。

    int256 x= 12;
    var y = x;//此时y的类型是int256
    
    uint256 z = 9;
    y = z;//此时,报出异常,因为uint256不能转换为int256类型

    但要注意的是:

    • 在定义数组array和map时不能使用var。var也不能用于定义函数参数和状态变量

    控制结构

    • if-else
    • while
    • for
    • break\continue
    • return
    • ?:
    • 等等
    //结构上和其他语法没有什么差异
    contract sample {
        int a = 12;
        int[] b;
    
        function sample() {
            if(a == 12) {}
            else if(a==34){}
            else {}
            var temp = 10;
            while(temp<20)
            {
                if(temp==17){break;}
                else {continue;}
            }
            temp++;
        }
        for(var m=0;m<b.length;m++){
    
        }
    }

    用new 操作符创建合约

    一个合约可以使用new关键字来创建一个新合约。

    例如:

    contract sample1 {
        int a;
        function assign(int b){
            a = b;
        }
    }
    
    contract sample2 {
        function sample2(){
            sample1 s = new sample1(); //注意写法
            s.assign(12);
        }
    }

    异常

    异常的抛出分为自动和手动。
    若你想手动抛出异常,可以使用throw手动抛出。
    注意,异常抛出后,会撤销对状态和余额的所有改变。

    contract sample {
        function myFunction () {
            throw;
        }
    }

    函数调用

    • 内部函数调用:一个函数在同一个合约中调用另一个函数
    • 外部函数调用:一个函数调用另一个合约的函数。

    外部函数调用–this关键字

    合约sample1

    contract sample1 {
        int a;
        function sample1(int b) payable {
            a = b;
        }
        function assign(int c){
            a = c;
        }
        function makePayment(int d) payable {
            a = d;
        }
    }

    合约sample2

    contract sample2 {
        function hello() {}
        function sample2(address addressOfContract){
            sample1 s = (new sample1).value(12)(23);
            s.makePayment(22);
            s.makePayment.value(45)(12);
            s.makePayment.value(4).gas(900)(12);
            this.hello(); //利用this调用外部合约函数
            sample1 s2 = sample1(addressOfContract);
            s2.makePayment(112);
        }
    }

    注意:使用this关键字进行的调用称为外部调用。在函数中,this关键字代表当前合约实例

    合约功能——深入理解合约

    可见性

    可见性定义了谁可以看到它,函数和状态变量有四种可见性:external、public、internal和private

    • 函数可见性,默认为 public
    • 状态变量可见性,默认为 internal

    • external:外部函数只能由其他合约调用,或者通过交易调用——this.f()

    • public:公共函数和状态变量可以用所有可行办法访问
    • internal:内部函数和状态变量只可以内部访问,即从当前合约内和继承它的合约访问。不可以使用this访问它
    • private:私有函数和状态变量类似于内部函数,但是继承合约不可以访问它们

    示例

    contract sample1 {
        int public b = 78;
        int internal c = 90;
    
        function sample1() {
            this.a();//外部访问
            b = 21;//内部访问
    
        }
        function a() external {}
    }
    
    contract sample2 {
        int internal d = 9;
        int private e = 90;
    }
    //sample3 继承 sample2
    contract sample3 is sample2 {
        sample1 s;
        function sample3() {
            s = new sample1();
            s.a();//外部访问
        }
    }
    

    函数修改器(较难理解)

    先看一个修改器的例子:

    contract sample {
        int a = 90;
        modifier myModifier1(int b) {
            int c = b;
            _;
            c = a;
            a = 1;
        }
        modifier myModifier2 {
            int c = a;
            _;
    
        }
        modifier myModifier3 {
            a = 96;
            return;
            _;
            a = 99;
        }
        modifier myModifier4 {
            int c = a;
            _;
        }
        function myFunction() myModifier1(a) myModifier2 myModifier3 returns (int d) {
            a = 2;
            return a;
        }
    
    }
    

    注:

    • 在修改器中,无论下一个修改器体或者函数体二者哪个先到达,都会被插入到“_;”出现的地方。

    回退函数

    即一个合约中唯一一个未命名函数。

    • 不能有实参
    • 不能有返回值
    • 如果其他函数都不能匹配给定的函数标识符,那么就执行回退函数
    • 如果你想让你的合约接收以太币,就必须实现回退函数
    contract sample {
        function() payable {
    
        }
    }

    继承

    即使一个合约继承自其他多个合约,在区块链上也只会创建一个合约。
    父合约的代码总是会被复制到最终合约里。

    • 关键字 is

    示例

    contract sample1 {
        function a(){}
        function b() {}
    }
    //合约2继承自合约1
    contract sample2 is sample1{
        function b() {}
    }
    contract sample3{
        function sample3(int b){
        }
    }
    //合约4继承自合约1与合约2
    contract sample4 is sample1,sample2 {
        function a(){}
        function c() {
            a();
            //执行合约1中的a方法
            sample1.a();
            //执行合约2中的b方法
            b();
        }
    }

    关键字super

    • 用于引用最终继承链中的下一个合约
      示例
    contract sample1{
    }
    contract sample2{
    }
    contract sample3 is sample2{
    }
    contract sample4 is sample2{
    }
    contract sample5 is sample4{
        function myFunc(){}
    }
    contract sample6 is sample1,sample2,sample3,sample4,sample5 {
        function myFunc() {
        //执行sample5中的myFunc方法
            super.myFunc();
        }
    }

    抽象合约

    • 仅包含函数原型而不包含函数实现的合约
    • 抽象合约不能被编译
    • 如果一个合约继承自抽象合约且不重写,那么它自己也是抽象合约

    示例

    contract sample1{
        function a() returns (int b);
    }
    contract sample2{
        function myFunc(){
            sample1 s = sample();
            s.a();
        }
    }

    库的目的是在一个特定地址中只部署一次,其其代码可供复用。

    示例-使用solidity的math库:

    library math
    {
        function addInt(int a,int b) return (int c){
            return a+b;
        }
    }
    contract sample {
        function data() returns (int d){
            return math.addInt(1,2);//调用math库中的addInt方法
        }
    }

    使用场景

    • 如果有许多合约,这些合约有一些共同的代码,那么可以把它们共同的代码部署成一个库。这么做的好处是这样能节省gas。因为gas的大小依赖于合约的规模。

    返回多个值

    示例:

    contract sample{
        function a() returns (int a,string c){
                return (1,"m");
        }
        function b(){
            int A;
            string memory B;
    
            (A,B) = a();// A =1,B = "m"
            (A,)  = a();// A =1
            (,B) = a(); //B = "m"
        }
    }

    全局变量

    • 特殊变量
    • 特殊函数

    1、区块和交易属性

    • block.blockhash(uint blockNumber) returns (bytes32) //区块哈希值
    • block.coinbase(address) //当前区块矿工的地址
    • block.difficulty(uint) //当前区块的难度值
    • block.gaslimit(uint) //当前区块的gas上限,定义了整个区块中的所有交易最多能消耗多少gas
    • block.number(uint) //当前区块的序号
    • block.timestamp(uint) //当前区块的时间戳
    • msg.gas(uint) //当前剩余的gas
    • msg.sender(address) //当前调用发起人的地址
    • msg.sig(bytes4) //调用数据的前四个字节
    • msg.value(uint) //这个消息所附带的货币量,单位为wei
    • now(uint) //当前区块的时间戳,等同于block.timestamp
    • tx.gasprice(uint) //交易的gas价格
    • tx.origin(address) //交易的发起人

    2、地址类型相关

    • .balance(uint256) //地址余额,单位为wei
    • .send(uint256 amount) returns(bool) //发送指定数量的wei到地址

    3、合约相关

    • this //当前合约
    • selfdestruct(address recipient) //销毁当前合约,把其中的资金发送到指定地址

    以太币单位

    一个数字可以用wei、finney、szabo、Ether等单位转换为不同面值的以太币。默认使用wei为单位。

    存在、真实性和所有权合约的证明

    下面我们要实现一个“证明文件所有权”的合约。

    分以下几步来进行:

    • 1、成对存储文件的哈希和文件所有者的名字,用以实现所有权证明(PoO)
    • 2、成对存储文件的哈希和区块的时间戳,用以实现文件在某个特定时间存在的证明(PoE)
    • 3、存储哈希自身,用以证明文件的真实性。如果文件被修改,其哈希也会被改变。更改过的文件的哈希会使得合约无法发现文件,从而证明文件被修改过。

    代码如下:

    contract Proof{
        struct FileDetails {
            uint timestamp;
            string owner;
        }
        mapping (string => FileDetails) files;
    
        event logFileAddedStatus(bool status,uint timestamp,string owner,string fileHash);
    
        //存储文件所有者
        function set(string owner,string fileHash){
            if(files[fileHash].timestamp==0){
                files[fileHash] = FileDetails(block.timestamp,owner);
                //触发一个事件以至于前端应用知道文件的存在
                logFileAddedStatus(true,block.timestamp,owner,fileHash);
            }else {
            //告诉前端文件存在,但是不能存储
                logFileAddedStatus(false,block.timestamp,owner,fileHash);
            }
        }
        //获取文件信息
        function get(string fileHash) returns (uint timestamp,string owner){
            return (files[fileHash].timestamp,files[fileHash].owner);
        }
    }

    编译和部署合约

    这里,我们使用solcjs和Browser Solidity ,其中solcjs允许在node.js中以编程方式编译Solidity,而Browser Solidity是一个适用于小型合约的IDE。

    至此,我们将Solidity语言进行了基本的讲解,下一节中我们将介绍如何使用web3.js开发DApp前端。

    展开全文
  • solidity编写智能合约HelloWorld Demo

    千次阅读 2019-06-06 11:14:06
    solidity编写智能合约HelloWorld Demo第一步 打开在线编辑器 remix第二步 新建.sol文件第三步 编写solidity合约代码第四步 Compile 编译合约代码第五步 Run 部署及执行合约代码 使用solidity前你需要 安装matemask...
  • 使用solidity编程语言编写智能合约

    千次阅读 2021-01-25 17:20:48
    编写智能合约2.编译合约3.部署合约总结 文章目录前言一、solidity语言是什么?二、一个简单的solidity智能合约1.编写智能合约2.编译合约3.部署合约总结 前言 随着区块链技术的不断发展,区块链被越来越多运用到...
  • 所谓智能合约是指运行在区块链上的代码 本小节从一个简单的案例讲解智能合约的结构,为了方便期间,我们使用在线编译器 1. 打开在线编辑器  http://remix.ethereum.org/ 2. 将以下代码写入 有过java代码的读者...
  • solidity编写智能合约遇到的各种问题合约部署1.creation of XXXXX errored: transaction execution failed2.creation of CS errored: Error encoding arguments: SyntaxError: Unexpected token x in JSON at ...
  • 在Solidity0.4.23版本的时候,有人在GitHub上列举了一些使用Solidity编写智能合约时常见的错误用法。虽然现在大家基本上都不会再写同样的问题代码,但是重新学习一下仍然有着借鉴意义。 1、tx.origin 错误用法:...
  • 要写智能合约有好几种语言可选:有点类似Javascript的Solidity, 文件扩展名是.sol;Python接近的Serpent, 文件名以.se结尾。还有类似Lisp的LLL,但现在最流行而且最稳定的要算是Soliditysolidity写Hello ...
  • 回顾 3 个底层调用 call(), delegatecall(), callcode() 和 ...call() 用于 Solidity 进行外部调用,例如调用外部合约函数 &lt;address&gt;.call(bytes4(keccak("somefunc(params)"), para...
  • 智能合约是“不可变的”。一旦部署,它们的代码是不能更改的,导致无法修复任何发现的bug。 在潜在的未来里,整个组织都由智能合约代码管控,对于适当的安全性需求巨大。过去的黑客如TheDAO或去年的Parity黑客(7月...
  • Solidity是以太坊用于创建智能合约的语言,此扩展提供: 语法高亮 片段 编制当前合同(按F1固体性:编制当前固体合同),或按F5 编译所有合同(按F1 Solidity:编译所有Solidity合同),或Ctrl + F5 / Cmd + F5 ...
  • Solidity编写的充满智能合约的仓库。 此回购旨在用于教育。 任何人都可以随意使用它,并提出建议和/或请求。 金融 该目录包含与金融应用程序相关的所有智能合约,例如原子掉期交易,跨链掉期和其他工具 积分 可以...
  • 智能合约编写Solidity的高级特性

    千次阅读 2020-03-13 17:26:53
    作者 |毛嘉宇责编 | 徐威龙来源| FISCO BCOS开源社区封面 | CSDN 付费下载于视觉中国系列专题 1:智能合约初探:概念与演变系...
  • Solidity语言是一种以太坊智能合约高级编程语言,运行在以太坊(Ethereum)虚拟机(EVM)之上。 Solidity语言是一种语法类似JavaScript的高级编程语言。Solidity语言被设计成以编译的方式生成以太坊虚拟机代码。使用...
  • 虽然接触过一点区块链的知识,但还没有动手写过智能合约,于是匆忙学习了一下Solidity和Smart Contract的基础,开始编写我的第一个智能合约,顺便记录一下学习历程。 先看下官网给的一个例子 ...
  • 智能合约开发(4)—— solidity实例

    千次阅读 2018-08-20 15:06:02
    这个合约比较复杂,但其中展示了很多的Solidity的特性。它实现了一个投票合约。电子投票的主要问题是如何给正确的人分配投票权,以及如何防止操纵。我们不会在这里解决所有问题,但我们会展示如何进行委派投票,以便...
  • 这是Remix,一个Solidity语言的在线编译器,我们将用此编译器来编写智能合约代码。   第一次访问该页面时,我们需要将本文编辑器预加载的代码删除,并替换为如下内容:   这是我们counter的代码。...
  • 个人使用的是以太坊平台,solidity编写智能合约,solidity版本号为0.4.17 部署智能合约暴露出的问题 项目在部署智能合约时,会将智能合约的API和Bytecode暴露出来,可以通过API获取到项目核心函数代码,因此存在...
  • 以太坊智能合约开发(四):Solidity转账智能合约实验1 合约编写2 在线调试 1 合约编写 编写一个分布式转账智能合约,部署合约的人为合约管理员,只有管理员可以为其他用户发放代币,其他用户之间可以互相转账,...
  • Solidity开发智能合约

    2021-04-09 17:46:23
    solidity是一门面向合约的、为实现智能合约而创建的高级编程语言,能在以太坊虚拟机(EVM)上运行。它是一门静态语言。内含的类型除了常见编程语言中的标准类型,还包括address等以太坊独有的类型。solidity源码文件...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,378
精华内容 1,751
关键字:

solidity编写智能合约