IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    基于以太坊私有链搭建一个分布式应用【2】

    杜工发表于 2018-03-15 14:01:27
    love 0

     

    合约编写

     

    合约编辑器有很多,我们用这个IDE http://remix.ethereum.org/

    合约格式是sol,需要编译成js文件让geth执行。以上工具可以方便的实现。

    Conference.sol示例:

    pragma solidity ^0.4.19;

     

    contract Conference {  // can be killed, so the owner gets sent the money in the end

     

    address public organizer;

    mapping (address => uint) public registrantsPaid;

    uint public numRegistrants;

    uint256 public ticketPrice;

    uint public quota;

     

    event Deposit(address _from, uint _amount); // so you can log the event,more information to see http://71j.cn

    event Refund(address _to, uint _amount); // so you can log the event,more information to see http://71j.cn

     

    function Conference() public{

    organizer = msg.sender;

    quota = 100;

    ticketPrice = 1 ether;

    numRegistrants = 0;

    }

    /** 1 msg 指谁调用该智能合约发来的信息。 注意:智能合约也是一个账户

    * 2 msg 包含的成员:

    * msg.data(bytes): 获取调用者传入的数据信息

    * msg.gas(uint): 剩余的以太币

    * msg.sender(address): 当前调用者的以太坊账户地址

    * msg.sig(bytes4): first four bytes of cakkData

    * msg.value(uint): number of wei sent with the message;注意: 只有当有以太坊币的转义时, 该变量才有值(除调用合约所花费以太币这种情况)

    */

    function buyTicket(uint ticketNumber) payable public {

    require(ticketNumber>0);

    require(numRegistrants+ticketNumber <= quota);

    uint totalPrice = ticketNumber*ticketPrice;

    require(msg.value==totalPrice);

    registrantsPaid[msg.sender] += totalPrice;

    numRegistrants+=ticketNumber;

    //this.transfer(totalPrice);

    organizer.transfer(totalPrice);

    Deposit(msg.sender, totalPrice);

    }

    /*Contracts that receive Ether directly (without a function call, i.e. using send or transfer) but do not define a fallback function throw an exception, sending back the Ether (this was different before Solidity v0.4.0). So if you want your contract to receive Ether, you have to implement a fallback function.

    */

    function () public payable {

    /*只要有支付行为,就会调用这个后备

    函数。举例:

    *合约计划仅接受用户转入指定价值的eth,如果用户转入eth较少,则直接返回异常,当用户转入eth较多,则将用户多转入的eth退回。

    *在这里我们可以借助 fallback function,也就是后备函数进行转入金额的限定。

    */

     

    }

     

    function changeQuota(uint newquota) public {

    require(msg.sender == organizer);

    quota = newquota;

    }

     

    function refundTicket(address recipient, uint amount,uint ticketNumber) payable public{

    require(msg.sender == organizer);

    if (registrantsPaid[recipient] >= amount) {

    //address myAddress = this;

    if (organizer.balance >= amount) {

    recipient.transfer(amount);

    Refund(recipient, amount);

    registrantsPaid[recipient] -= amount;

    numRegistrants-=ticketNumber;

    }

    }

    return;

    }

     

     

    function destroy () public{

    require(msg.sender == organizer);// without this funds could be locked in the contract forever!

    selfdestruct(organizer);

     

    }

    }

    *在Node1命令行中审视一下自己编辑的合约:solc –gas Conference.sol

    *网上说以太坊在标准库方面只实现了非常非常少的一部分,比如SHA3.SHA3操作的基本开销是30gas,之后每个32字节的数据hash需要3gas。内置的StringEqual操作需要5 gas的基本开销,随后比较每个字节需要1 gas

     

    用IDE编译成Conference,js后,回到geth,可以导入合约。!注意,这个过程矿机不能停。!

     

    > loadScript(‘./Conference.js’)

    null [object Object]

    true

    > null [object Object]

    Contract mined! address: 0xcbf1b22ad7eda3482f7f00948539ea5684a6882b transactionHash: 0xb7c3a8c1282baef3c7b7063b1a96b10e28ff26a2c1fe9fd5a3c81ccbafcbea7d

     

    我们可以看到合约有一个address,意味着它能收发以太值,以及一个transactionHash,用它来找到区块链中的位置。直接输入ss(我们初始化的实例名字),发现合约的abi信息以及公共的可调用的函数也列出了。abi是Application Binary Interface的缩写,字面意思 应用二进制接口,可以通俗的理解为合约的接口说明。当合约被编译后,那么它的abi也就确定

    现在让我们调用下:

    ss.changeQuota.sendTransaction(100)

    Error: invalid address

    at web3.js:3930:15

    at web3.js:3756:20

    at web3.js:5025:28

    at map (<native code>)

    at web3.js:5024:12

    at web3.js:5050:18

    at web3.js:5075:23

    at web3.js:4137:16

    at <anonymous>:1:1

    我去,没成功。再试试这个:

    > ss.quota()

    100

    > ss.changeQuota.sendTransaction(200,{from:eth.coinbase})

    “0x44acef0216b4da50e32a25bdaaf074ddd349f23280f181bf0d284c32769830d2”

    > ss.quota()

    100

    > ss.quota()

    200

    由于web3.js封装了合约调用的方法。我们可以使用可以使用web3.eth.contract的里的sendTransaction来修改区块链数据。在这里有个坑,有可能会出现Error: invalid address,原因是没有传from,交易发起者的地址。在使用web3.js的API都需留意,出现这种找不到地址的,都看看from字段吧。

     

    接下来看看gas消耗的情况:

    Node1:

    > ss.organizer()

    “0xf4182f0dc92313f8e106fc033641c3351703911f”

    > eth.getBalance(“0xf4182f0dc92313f8e106fc033641c3351703911f”)

    2.0000037903157114008e+28

    为了保证合约内容讲解的完成性,这块我们要穿越到第二天Node2节点已经搭建完成了!

    Node2:

    var bb=eth.contract.(abi).at(address);//这是在新节点初始化合约的方法

    > personal.unlockAccount(eth.coinbase)

    Unlock account 0xf2ad598bb92b28989d1e8bf686f0e07a8a036a5a

    Passphrase:

    true

    > eth.getBalance(eth.coinbase)

    9.574379388346e+21

    > bb.buyTicket.sendTransaction(2,{from:eth.coinbase,value:web3.toWei(2)})

    “0x2f5bf547eadbff0c96db240aa896990142bd23f769be87f2e459261caf8d633c”

    > eth.getBalance(eth.coinbase)

    9.572378091716e+21

    > eth.getTransaction(“0x2f5bf547eadbff0c96db240aa896990142bd23f769be87f2e459261caf8d633c”)

    {

    blockHash: “0xb207210430239571b5d999c4c000f065d66e660b64045e6218e179a817f8c006”,

    blockNumber: 8422,

    from: “0xf2ad598bb92b28989d1e8bf686f0e07a8a036a5a”,

    gas: 90000,

    gasPrice: 18000000000,

    hash: “0x2f5bf547eadbff0c96db240aa896990142bd23f769be87f2e459261caf8d633c”,

    input: “0x67dd74ca0000000000000000000000000000000000000000000000000000000000000002”,

    nonce: 42,

    r: “0xef743cab7bdf70706052877ab675a289094a56e1f0241f618b7eb7ad73b273a7”,

    s: “0x5cb18b44f1e3276c8f695ad126a578ebe1badd2f532b7d4139592eb2bd3e05f7”,

    to: “0xfbcd7a73ff412512117300f59248f4281579c80f”,

    transactionIndex: 0,

    v: “0x3c”,

    value: 2000000000000000000

    }

    >  9.574379388346e+21-9.572378091716e+21//少了这么多钱

    2001296630000648200

    > 2001296630000648200-2000000000000000000//减掉票钱

    1296630000648192

    > 1296630000648192/18000000000

    72035.00003601066//大概花了这么多gas

     

    创建新Node

    登录246服务器,修改genesis.json!!!!!错误!!!!这文件一个字都不能改。

    geth –datadir “/home/appadmin/.ethereum” init eth/genesis.json

    ./start.sh删除unlock和password, 修改rpcaddr为246

     

     

    在Node1的 boot node 上获取 enode 信息

    > admin.nodeInfo.enode

    “enode://2794bb6a9439d80c55c394328b396105adff8f5a2c9686fd56945024ebadb847ac2bbca2266742138ddfb94a03ceccbcd98b1caffc4820d1ead5c369234aad8b@[::]:30303”

    替换:

    “enode://2794bb6a9439d80c55c394328b396105adff8f5a2c9686fd56945024ebadb847ac2bbca2266742138ddfb94a03ceccbcd98b1caffc4820d1ead5c369234aad8b@10.120.113.245:30303”

    将 boot node 的 enode 信息写入 node2

    >admin.addPeer(“enode://2794bb6a9439d80c55c394328b396105adff8f5a2c9686fd56945024ebadb847ac2bbca2266742138ddfb94a03ceccbcd98b1caffc4820d1ead5c369234aad8b@10.120.113.245:30303”)

    true

    > admin.peers

    []

    > admin.peers

    [{

    caps: [“eth/62”, “eth/63”],

    id: “edcc39c963e128555c192be58ddd387a674aba2123ec7daef9144b6ff109ac5712bcfea3904a63031fcdbbfae30c26bb6148af0c559944b34f00d0cd2889688c”,

    name: “Geth/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4”,

    network: {

    inbound: true,

    localAddress: “10.120.113.245:30303”,

    remoteAddress: “10.120.113.246:48204”,

    static: false,

    trusted: false

    },

    protocols: {

    eth: {
    difficulty: 7035219,

    head: “0xf8b772c1b23cd8bea305aa52f13cd68a1697339e86f9badd5d064502c3c84e65”,

    version: 63

    }

    }

    }]

    也可以在start.sh中直接指定Node1的信息

    #!/bin/sh

    geth –ipcdisable –rpc –rpcaddr “10.120.113.246”  –port 30303 –rpcport 8101 –rpccorsdomain “*” –networkid 12 –datadir ‘~/.ethereum’ –bootnodes “enode://3d6ff07e12e59a4dbd972e5d8ed33578570203c852f9837b263a04fa91bc9c795b46cbd3ee801a67aa741e317fcd734409bf1e81c8a4e81e6e27f420fd25ba0a@10.120.113.245:30303” console 2>>log

    这样新节点就添加完成了,他们之间的通信就是p2p的方式了。



沪ICP备19023445号-2号
友情链接