美文网首页
智能合约开发之 Hello World!

智能合约开发之 Hello World!

作者: AlleniCode | 来源:发表于2018-01-16 19:01 被阅读2740次

    一、Solidity 语言

    Solidity 是一种智能合约高级语言,运行在 Ethereum 虚拟机(EVM:Ethereum Virtual Machine)之上。
    Solidity 的语法接近于 Javascript,是一种面向对象的语言,而且围绕着 Solidity 的各种开发工具链,都是使用属于 Javascript 生态系的 npm 来提供的。

    Solidity语言:http://www.tryblockchain.org
    Solidity documentation:https://solidity.readthedocs.org
    GitHub:https://github.com/ethereum/solidity

    二、编辑器

    我目前是使用 Atom 搭配 solidity(linter-solium) 插件来开发。

    三、Truffle 框架

    Truffle 是针对基于以太坊的 Solidity 语言的一套开发框架,本身基于 Javascript。

    四、工具安装

    1、安装 Node.jsNPM
    2、安装 Truffle 框架,在终端运行命令:

    $ sudo npm install -g truffle

    安装 Truffle

    3、安装 Truffle 客户端 EtherumJS TestRPC

    $ sudo npm install -g ganache-cli

    安装 TestRPC

    五、启动 TestRPC

    使用以下命令来启动以太坊测试环境:

    $ ganache-cli

    启动以太坊测试环境

    可以看到启动后自动建立了10个帐号(Accounts),与每个帐号对应的私钥(Private Key)。每个帐号中都有100个测试用的以太币(Ether)。要注意以太坊测试环境仅运行在內存中,因此每次重开时都会回到全新的状态。

    六、创建项目

    重新打开一个终端窗口,运行以下命令以创建项目:

    $ mkdir SmartContractProject
    $ cd SmartContractProject/
    $ mkdir HelloWorld
    $ cd HelloWorld/
    $ truffle init

    初始化 Truffle HelloWorld 工程目录 QQ20180116-160844@2x.png

    目录结构:

    • contracts/:Truffle默认的合约文件存放地址;
    • migrations/:存放发布的脚本文件;
    • test/:存放测试应用和合约的测试文件;
    • truffle.js 和 truffle-config.js:Truffle的配置文件。

    七、新建 HelloWorld 合约

    contracts 文件夹下新建 HelloWorld.sol 文件:

    $ cd contracts/
    $ truffle create contract HelloWorld

    新建 HelloWorld 合约 QQ20180116-163158@2x.png

    HelloWorld.sol 文件内容如下:

    HelloWorld.sol

    讲解:

    pragma solidity ^0.4.4;
    

    第一行指名目前使用的 solidity 版本,不同版本的 solidity 可能会编译出不同的 bytecode。^ 代表兼容 solidity 0.4.4 ~ 0.4.9 的版本。

     function HelloWorld() {
       // constructor
     }
    }
    

    contract 关键字类似于其他语言中较常见的 class。因为solidity 是专为智能合约(Contact)设计的语言,声明 contract 后即内置了开发智能合约所需的功能。也可以把这句理解为 class HelloWorld extends Contract

    函数的结构与其他程序类似,但如果有传入的参数或回传值,需要指定参数或回传值的类型(type)。

    八、向合约中添加新的方法

    function sayHello() returns (string) {
        return ("Hello World");
    }
    
    添加新的方法

    九、编译合约

    现在执行 truffle compile 命令,我们可以将 HelloWorld.sol 原始码编译成 Ethereum bytecode

    $ cd ..
    $ truffle compile

    编译合约

    【注意出现了警告】
    重新修改 HelloWorld.sol 文件中的方法:

    pragma solidity ^0.4.4;
    
    contract HelloWorld {
      function HelloWorld() public {
        // constructor
      }
    
      function sayHello() public pure returns (string) {
        return ("Hello World");
    }
    
    }
    
    修改合约中的方法

    保存后重新运行命令:

    $ truffle compile

    重新编译合约

    命令运行成功后会多出一个 build 的目录,如下:

    build 目录

    HelloWorld 文件夹下面的 build/contracts 文件夹下面会看见 HelloWorld.json 文件:

    HelloWorld.json

    十、修改 truffle.js 文件内容:

    添加以下内容到 truffle.js 文件并保存 :

    networks: {
            development: {
                host:"localhost",
                port:8545,
                network_id:"*"  // 匹配任何network id
            }
        }
    
    修改 truffle.js 文件

    十一、部署合约

    migrations 目录下创建移植文件:

    $ cd migrations/
    $ truffle create migration 2_deploy_helloworld

    创建移植文件 migrations 目录 1516095208_2_deploy_helloworld.js 文件内容

    修改文件名及文件内容如下:

    var HelloWorld = artifacts.require("./HelloWorld.sol");
    
    module.exports = function(deployer) {
      deployer.deploy(HelloWorld);
    };
    
    修改移植文件内容

    使用 artifacts.require 语句来取得准备部署的合约。
    使用deployer.deploy 语句将合约部署到区块链上。
    这边HelloWorldcontract 的名称而不是文件名。
    因此可以用此语法读入任一 .sol 文件中的任一合约。

    现在执行 truffle migrate 命令:

    $ truffle migrate

    部署合约

    部署成功你会看到 启动 TestRPC 的终端窗口会有以下变化:


    TestRPC 的终端窗口变化

    十二、与合约互动

    Truffle 提供命令行工具,执行 truffle console 命令后,可用Javascript 来和刚刚部署的合约互动:

    $ cd ..
    $ truffle console
    $ HelloWorld.deployed().then(instance => contract = instance)

    与合约互动1 与合约互动2
    yutaos-MacBook-Pro:migrations yutaozhang$ cd ..
    yutaos-MacBook-Pro:HelloWorld yutaozhang$ truffle console
    truffle(development)> HelloWorld.deployed().then(instance => contract = instance)
    TruffleContract {
      constructor: 
       { [Function: TruffleContract]
         _static_methods: 
          { setProvider: [Function: setProvider],
            new: [Function: new],
            at: [Function: at],
            deployed: [Function: deployed],
            defaults: [Function: defaults],
            hasNetwork: [Function: hasNetwork],
            isDeployed: [Function: isDeployed],
            detectNetwork: [Function: detectNetwork],
            setNetwork: [Function: setNetwork],
            resetAddress: [Function: resetAddress],
            link: [Function: link],
            clone: [Function: clone],
            addProp: [Function: addProp],
            toJSON: [Function: toJSON] },
         _properties: 
          { contract_name: [Object],
            contractName: [Object],
            abi: [Object],
            network: [Function: network],
            networks: [Function: networks],
            address: [Object],
            links: [Function: links],
            events: [Function: events],
            binary: [Function: binary],
            deployedBinary: [Function: deployedBinary],
            unlinked_binary: [Object],
            bytecode: [Object],
            deployedBytecode: [Object],
            sourceMap: [Object],
            deployedSourceMap: [Object],
            source: [Object],
            sourcePath: [Object],
            ast: [Object],
            compiler: [Object],
            schema_version: [Function: schema_version],
            schemaVersion: [Function: schemaVersion],
            updated_at: [Function: updated_at],
            updatedAt: [Function: updatedAt] },
         _property_values: {},
         _json: 
          { contractName: 'HelloWorld',
            abi: [Array],
            bytecode: '0x6060604052341561000f57600080fd5b6101578061001e6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc610117565b6040805190810160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820b825548240e74063f6a0c0dd2a2b787ed288e3359cb7682b670288211d37f14f0029',
            deployedBytecode: '0x606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef5fb05b14610046575b600080fd5b341561005157600080fd5b6100596100d4565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561009957808201518184015260208101905061007e565b50505050905090810190601f1680156100c65780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6100dc610117565b6040805190810160405280600b81526020017f48656c6c6f20576f726c64000000000000000000000000000000000000000000815250905090565b6020604051908101604052806000815250905600a165627a7a72305820b825548240e74063f6a0c0dd2a2b787ed288e3359cb7682b670288211d37f14f0029',
            sourceMap: '25:164:0:-;;;49:53;;;;;;;;25:164;;;;;;',
            deployedSourceMap: '25:164:0:-;;;;;;;;;;;;;;;;;;;;;;;;106:80;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;23:1:-1;8:100;33:3;30:1;27:2;8:100;;;99:1;94:3;90;84:5;80:1;75:3;71;64:6;52:2;49:1;45:3;40:15;;8:100;;;12:14;3:109;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;106:80:0;147:6;;:::i;:::-;161:22;;;;;;;;;;;;;;;;;;;;106:80;:::o;25:164::-;;;;;;;;;;;;;;;:::o',
            source: 'pragma solidity ^0.4.4;\n\ncontract HelloWorld {\n  function HelloWorld() public {\n    // constructor\n  }\n\n  function sayHello() public pure returns (string) {\n    return ("Hello World");\n}\n\n}\n',
            sourcePath: '/Users/yutaozhang/SmartContractProject/HelloWorld/contracts/HelloWorld.sol',
            ast: [Object],
            compiler: [Object],
            networks: [Object],
            schemaVersion: '1.0.1',
            updatedAt: '2018-01-16T09:55:58.496Z' },
         setProvider: [Function: bound setProvider],
         new: [Function: bound new],
         at: [Function: bound at],
         deployed: [Function: bound deployed],
         defaults: [Function: bound defaults],
         hasNetwork: [Function: bound hasNetwork],
         isDeployed: [Function: bound isDeployed],
         detectNetwork: [Function: bound detectNetwork],
         setNetwork: [Function: bound setNetwork],
         resetAddress: [Function: bound resetAddress],
         link: [Function: bound link],
         clone: [Function: bound clone],
         addProp: [Function: bound addProp],
         toJSON: [Function: bound toJSON],
         web3: 
          Web3 {
            _requestManager: [Object],
            currentProvider: [Object],
            eth: [Object],
            db: [Object],
            shh: [Object],
            net: [Object],
            personal: [Object],
            bzz: [Object],
            settings: [Object],
            version: [Object],
            providers: [Object],
            _extend: [Object] },
         class_defaults: 
          { from: '0xcb009af857f4e65d3b234f491ffebe76cee6cbe7',
            gas: 6721975,
            gasPrice: 100000000000 },
         currentProvider: 
          HttpProvider {
            host: 'http://localhost:8545',
            timeout: 0,
            user: undefined,
            password: undefined,
            send: [Function],
            sendAsync: [Function],
            _alreadyWrapped: true },
         network_id: '1516087157119' },
      abi: 
       [ { constant: true,
           inputs: [],
           name: 'sayHello',
           outputs: [Array],
           payable: false,
           stateMutability: 'pure',
           type: 'function' },
         { inputs: [],
           payable: false,
           stateMutability: 'nonpayable',
           type: 'constructor' } ],
      contract: 
       Contract {
         _eth: 
          Eth {
            _requestManager: [Object],
            getBalance: [Object],
            getStorageAt: [Object],
            getCode: [Object],
            getBlock: [Object],
            getUncle: [Object],
            getCompilers: [Object],
            getBlockTransactionCount: [Object],
            getBlockUncleCount: [Object],
            getTransaction: [Object],
            getTransactionFromBlock: [Object],
            getTransactionReceipt: [Object],
            getTransactionCount: [Object],
            call: [Object],
            estimateGas: [Object],
            sendRawTransaction: [Object],
            signTransaction: [Object],
            sendTransaction: [Object],
            sign: [Object],
            compile: [Object],
            submitWork: [Object],
            getWork: [Object],
            coinbase: [Getter],
            getCoinbase: [Object],
            mining: [Getter],
            getMining: [Object],
            hashrate: [Getter],
            getHashrate: [Object],
            syncing: [Getter],
            getSyncing: [Object],
            gasPrice: [Getter],
            getGasPrice: [Object],
            accounts: [Getter],
            getAccounts: [Object],
            blockNumber: [Getter],
            getBlockNumber: [Object],
            protocolVersion: [Getter],
            getProtocolVersion: [Object],
            iban: [Object],
            sendIBANTransaction: [Function: bound transfer] },
         transactionHash: null,
         address: '0x742c7a36d3a2b65a5607180fdee7fd5befc8a164',
         abi: [ [Object], [Object] ],
         sayHello: 
          { [Function: bound ]
            request: [Function: bound ],
            call: [Function: bound ],
            sendTransaction: [Function: bound ],
            estimateGas: [Function: bound ],
            getData: [Function: bound ],
            '': [Circular] },
         allEvents: [Function: bound ] },
      sayHello: 
       { [Function]
         call: [Function],
         sendTransaction: [Function],
         request: [Function: bound ],
         estimateGas: [Function] },
      sendTransaction: [Function],
      send: [Function],
      allEvents: [Function: bound ],
      address: '0x742c7a36d3a2b65a5607180fdee7fd5befc8a164',
      transactionHash: null }
    truffle(development)> 
    

    讲解:

    HelloWorld.deployed().then(instance => contract = instance)

    truffle console 中预载了 truffle-contract 函数库,以方便操作部署到区块链上的合约。
    这边使用 HelloWorld.deployed().then 语句来取得 HelloWorld合约的 Instance(实例),并存到 contract 变量中,以方便后续的调用。

    输入以下命令:

    $ contract.sayHello.call()

    与合约互动3

    这里直接呼叫 contract.sayHello() 也会得到一样的结果。truffle-contract 提供使用 call() 来读取只读 (read only) 的数据,这样就不需提供 gas。因此如果遇到的操作需要向区块链写入数据,我们就不能用 call 语句了。

    如此一来,我们已写好并部署完成了第一个智能合约,也验证了合约确实可以运作。

    十三、加入新方法

    我们在 HelloWorld.sol 中再加入一个 echo 方法,echo 方法接受输入一个参数,并回传传送的参数`:

    function echo(string name) public pure returns (string) {
        return name;
    }
    
    加入新方法

    由于更新了合约内容,我们需要先重新新编译一次,将编译结果部署到 testrpc 上,再透过 truffle console 执行看看结果。

    $ truffle compile
    $ truffle migrate --reset

    移植重置

    $ truffle console
    $ let contract
    HelloWorld.deployed().then(instance => contract = instance)

    再次与合约互动1
    再次与合约互动2

    有一点需要注意的,是这次如果还是用 $ truffle migrate 命令,我们会得到如下信息:

    $ truffle migrate
    Using network 'development'.
    Network up to date.
    

    Truffle 会告诉你现在网络上的合约都已是最新的,但事实上刚刚程序中新增的方法并没有更新到内存块链上。要更新内存块链上已部署的程序,需要改写 migrations 中的脚本,还好我们开发用的内存块链是怎么修改都没关系的 testrpc,可以使用 truffle migrate --reset 命令直接重新在 testrpc 上部署一次。

    总结

    欢迎留言讨论,有错误请指出,谢谢!

    【联系我(QQ:3500229193)或者加入社群,请戳这里!】

    参考链接

    更新日志

    • 2018.01.16 第一次更新
    • 2018.02.23 第二次更新
    • 2018.08.07 第三次更新

    相关文章

      网友评论

          本文标题:智能合约开发之 Hello World!

          本文链接:https://www.haomeiwen.com/subject/nbssoxtx.html