合约编写
合约编辑器有很多,我们用这个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的方式了。