本篇文章给大家谈谈【Solidity】4.单位和全局可变量-深入理解Solidity,同时本文还将给你拓展根据例子学习Solidity、深入理解Solidity之二---Solidity源代码文件结构
本篇文章给大家谈谈【Solidity】4.单位和全局可变量 - 深入理解Solidity,同时本文还将给你拓展
- 【Solidity】4.单位和全局可变量 - 深入理解Solidity
根据例子学习Solidity 深入理解Solidity之二---Solidity源代码文件结构 - Solidity 官方文档中文版 3_安装Solidity
- Solidity 官方文档中文版 4_Solidity 编程实例
【Solidity】4.单位和全局可变量 - 深入理解Solidity
索引
- 【Solidity】1.一个Solidity源文件的布局
- 【Solidity】2.合约的结构体
- 【Solidity】3.类型
- 【Solidity】4.单位和全局可变量
- 【Solidity】5.表达式和控制结构
- 【Solidity】6. 合约
- 【Solidity】7. 部件
- 【Solidity】8. 杂项
单位和全局可变量
Ether单元
一个字面上的数字可以带有wei,finney,szabo或者以太网的后缀,可以在以太网的子目录之间进行转换,其中没有后缀的以太网货币号被假定为魏。 2 ether == 2000 finney评估为true。
时间单位
可以使用文字数字后的秒,分,小时,天,周和年份进行后缀转换,其中以秒为单位,以下列方式将单位视为天真的时间单位:
1 == 1 seconds
1 minutes == 60 seconds
1 hours == 60 minutes
1 days == 24 hours
1 weeks == 7 days
1 years == 365 days
如果您使用这些单位执行日历计算,请小心,因为不是每年等于365天,甚至每天都没有24小时,因为闰秒。 由于无法预测闰秒的事实,必须由外部的oracle更新精确的日历库。
这些后缀不能应用于变量。 如果你想解释某些输入变量,例如。 天,你可以通过以下方式做到:
function f(uint start,uint daysAfter) { if (Now >= start + daysAfter * 1 days) { // ... } }
特殊变量和函数
有一些特殊的变量和函数总是存在于全局命名空间中,主要用于提供关于块链的信息。
块和事务属性
-
block.blockhash(uint blockNumber) returns (bytes32)
给定块的哈希 - 仅适用于256个不包括当前最新块 -
block.coinbase (address)
当前块矿工地址 -
block.difficulty (uint)
当前块难度 -
block.gaslimit (uint)
当前块gaslimit -
block.number (uint)
当前数据块号 -
block.timestamp (uint)
当前块时间戳从unix纪元开始为秒 -
msg.data (bytes)
完整的calldata -
msg.gas (uint)
剩余gas -
msg.sender (address)
该消息(当前呼叫)的发送者 -
msg.sig (bytes4)
呼叫数据的前四个字节(即功能标识符) -
msg.value (uint)
发送的消息的数量 -
Now (uint)
当前块时间戳(block.timestamp的别名) -
tx.gasprice (uint)
gas价格的交易 -
tx.origin (address)
交易的发送者(全调用链)
msg的所有成员的值(包括msg.sender和msg.value)可以针对每个外部函数调用进行更改。 这包括对库函数的调用。
如果要使用msg.sender在库函数中实现访问限制,则必须手动提供msg.sender的值作为参数。
错误处理
assert(bool condition)
:
如果条件不满足,则抛出 - 用于内部错误。 require(bool condition)
:
如果条件不满足,则抛出 - 用于输入或外部组件中的错误。 revert()
:
中止执行并恢复状态更改
数学和加密功能
addmod(uint x,uint y,uint k) returns (uint)
计算(x + y)%k,其中以任意精度执行加法,并且不在2 ** 256处围绕
mulmod(uint x,uint k) returns (uint)
计算(x * y)%k,其中乘法以任意精度执行,并且不会在2 ** 256处循环。
keccak256(...) returns (bytes32)
计算的(紧凑)参数的Ethereum-SHA-3(Keccak-256)的散列
sha256(...) returns (bytes32)
计算(紧密包装)参数的SHA-256散列
sha3(...) returns (bytes32)
keccak256的别名
ripemd160(...) returns (bytes20)
计算(紧密包装)参数的RIPEMD-160哈希值
ecrecover(bytes32 hash,uint8 v,bytes32 r,bytes32 s) returns (address)
从椭圆曲线签名中恢复与公钥相关的地址,或者在错误时返回零(示例使用)
在上面,“紧密包装”意味着参数是无连接的连接。 这意味着以下内容完全相同:
keccak256("ab","c") keccak256("abc") keccak256(0x616263) keccak256(6382179) keccak256(97,98,99)
如果需要填充,可以使用显式类型转换:keccak256("\x00\x12")
与keccak256(uint16(0x12))
相同。
请注意,常量将使用存储它们所需的最少字节数来打包。 这意味着,例如keccak256(0) == keccak256(uint8(0))
和keccak256(0x12345678) == keccak256(uint32(0x12345678))
这可能是你在一个私人的块上遇到了sha256,ripemd160或ecrecover的Out-of-Gas。 这样做的原因是那些被实现为所谓的预编译合同,这些合同在收到第一条消息之后才真正存在(尽管他们的合同代码是硬编码的)。 对非现有合约的消息更昂贵,因此执行将会进入“Out-of-Gas”错误。 这个问题的解决方法是首先发送例如 在您将实际合同中使用这些合约之前,每个合约1 Wei。 官网或测试网不是问题。
地址相关
<address>.balance (uint256)
平衡地址在Wei
<address>.transfer(uint256 amount)
发送一定量wei向地址,抛出失败
<address>.send(uint256 amount) returns (bool)
发送一定量wei向地址,失败时返回false
<address>.call(...) returns (bool)
发出低级CALL,失败返回false
<address>.callcode(...) returns (bool)
发出低级CALLCODE,失败时返回false
<address>.delegatecall(...) returns (bool)
发出低级DELEGATECALL,失败返回false
有关详细信息,请参阅地址部分。
使用send有一些危险:如果调用堆栈深度为1024(这可以始终被呼叫者强制),则传输失败,如果接收方耗尽gas,则传输失败。 所以为了使安全的以太网传输,始终检查发送的返回值,使用转移甚至更好:使用接收方提取钱的模式。
不鼓励使用callcode,将来会被删除。
合约相关
this
当前合约,明确转换为地址
selfdestruct(address recipient)
摧毁目前的合同,将资金送到给定的地址
suicide(address recipient)
selfdestruct的别名
此外,当前合约的所有功能都可以直接调用,包括当前的功能。
根据例子学习Solidity
声明:本系列文章是自己在http://solidity-cn.readthedoc... 学习solidity时,因为英语水平不够,被迫用谷歌粗略翻译的。仅为了方便学习,中间肯定有很多错误的地方。请勘误。
本文原地址:http://solidity-cn.readthedoc...
以下是翻译正文:
投票:
以下合同非常复杂,但展示了很多Solidity的功能。 它执行投票合同。 当然,电子投票的主要问题是如何为正确的人员分配投票权,以及如何防止操纵。 我们不会在这里解决所有问题,但至少我们会展示如何进行委派投票,以便计票自动且完全透明。
这个想法是为每个选票创建一个合同,为每个选项提供一个简称。 然后,担任主席的合同创建者将分别给予每个地址的投票权。
然后,地址背后的人可以选择自己投票,或者将他们的投票委托给他们信任的人。
在投票时间结束时,winningProposal()
将返回投票数最多的提案。
pragma solidity ^0.4.21
/// @title Voting with delegation.
contract Ballot {
//这将声明一个新的复合类型
//稍后用于变量。
//它会代表一个voter。
struct Voter {
uint weight; //weight is accumulated by delegation
bool voted; // 如果true,已经voted
address delegate; // 被授权的人
uint vote; // vote提案的索引
}
// a type for a single proposal.
struct Proposal {
bytes32 name;
uint voteCount;
}
address public chairperson;
//声明一个状态变量,为每个可能的地址存储一个`Voter`结构体。
mapping(address => Voter) public voters;
// Proposal结构的动态大小的数组。
Proposal[] public proposals;
///创建一个新ballot来选择`proposalNames`中的一个。
function Ballot(bytes32[] proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
//对于提供的每个提议名称,创建一个新的提议对象并将其添加到数组的末尾。
for (uint i = 0, i < proposalNames.length; i++) {
//`Proposal({...})`创建一个临时的Proposal对象,
// `proposals.push(...)`将它附加到`proposals`的末尾
proposals.push(Proposal({
name:proposalNames[i];
voteCount:0
}));
}
}
//让`voter`有权对这张选票进行投票。
//只能由`chairperson`调用。
function giveRightToVote(address voter) public {
//如果`require`的参数评估为''false'',
//它会终止并恢复对状态和以太平衡的所有更改。 如果函数被错误地调用,
//通常使用它是一个好方法。 但要小心,这也将消耗所有提供的gas(这是计划在未来改变)。
require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0))
voters[voter].weight = 1;
}
function delegate(address to) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted);
require(to != msg.sender);
// 一般来说,这样的循环是非常危险的,因为如果它们运行时间太长,
// 它们可能需要比块中可用的更多的气体。在这种情况下,委托将不会被执行,
// 但在其他情况下,这样的循环可能导致合同 完全“卡住”。
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
require(to != msg.sender);
}
//因为`sender`是一个引用,所以这会修改`voters[msg.sender].voted`
sender.voted = true;
sender.delegate = to;
Voter storage delegate = voters[to];
if(delegate.voted) {
//如果委托人已投票,则直接添加投票数
proposals[delegate.vote].voteCount += sender.weight;
} else {
//如果代表尚未投票,增加
delegate.weight += sender.weight;
}
}
///将您的投票(包括授予您的投票)提交给提案[proposal] .name`。
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted);
sender.voted = true;
sender.vote = proposal;
//如果`proposal''超出了数组范围,这将自动抛出并恢复所有更改。
proposals[proposal].voteCount += sender.weight;
}
/// @dev计算以前所有投票的获胜建议。
function winningProposal() public view returns (uint winningProposal) {
uint winningVoteCount = 0;
for (uint p = 0, p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal = p;
}
}
}
//调用winningProposal()函数获取提议数组中包含的获奖者的索引,然后返回获胜者的名字
function winnerName() public view
returns (bytes32 winnerName)
{
winnerName = proposals[winningProposal()].name;
}
}
可能的改进
目前,需要许多交易来将投票权分配给所有参与者。 你能想出更好的方法吗?
秘密竞价(盲拍)
在本节中,我们将展示在以太坊创建一个完全失明的拍卖合同是多么容易。 我们将从公开拍卖开始,每个人都可以看到所做的投标,然后将此合同扩展到盲目拍卖,在竞标期结束之前无法看到实际出价。
简单的公开拍卖
以下简单的拍卖合同的总体思路是每个人都可以在投标期内发送他们的出价。 出价已经包括发送金钱/以太币以使投标人与他们的出价相结合。 如果提高最高出价,以前出价最高的出价人可以拿回她的钱。 在投标期结束后,合同必须手动为受益人接收他的钱, 合同不能激活自己。
pragma solidity ^0.4.21
contract SimpleAuction {
//拍卖的参数。 时间是绝对的unix时间戳
//(自1970-01-01以来的秒数)或以秒为单位的时间段。
address public beneficiary;
uint public auctionEnd;
// 拍卖当前的状态
address public highestBidder;
uint public highestBid;
//允许撤回之前的出价
mapping(address => uint) pendingReturns;
// 设置为true,禁止任何更改
bool ended;
// 将在更改中触发的事件。
event HighestBidIncreased(address bidder, uint amount);
event AuctionEnded(address winner, uint amount);
//以下是所谓的natspec评论,可以通过三个斜杠来识别。
//当用户被要求确认交易时将显示。
///代表受益人地址`_beneficiary`以`_biddingTime`秒的投标时间创建一个简单的拍卖。
function SimpleAuction(
uint _biddingTime,
address _beneficiary
) public {
beneficiary = _beneficiary;
auctionEnd = now + _biddingTime;
}
///使用与此交易一起发送的价格拍卖拍卖品。 如果拍卖没成功,价值只会被退还。
function bid() public payable {
//不需要参数,所有信息已经是交易的一部分。 为了能够接收以太网,功能需要关键字。
//如果结束,请恢复通话。
require(now <= auctionEnd);
// 如果出价不高,将钱退回。
require(msg.value > highestBid);
if (highestBid != 0) {
//通过使用highestBidder.send(highestBid)发送回款有安全风险,
// 因为它可以执行不可信的合同。
// 让收款人自己收回钱会比较安全。
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
HighestBidIncreased(msg.sender, msg.value);
}
///撤销高出价的出价。
function withdraw() public returns (bool) {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
//将它设置为零是很重要的,因为收件人可以在`send`返回
// 之前再次调用此函数作为接收调用的一部分。
pendingReturns[msg.sender] = 0;
if (!msg.sender.send(amount)) {
//不需要在这里呼叫,只需重新设置欠款额
pendingReturns[msg.sender] = amount;
return false;
}
}
return true;
}
///结束拍卖并将最高出价发送给受益人。
function auctionEnd() public {
//将与其他合约交互的函数(即它们调用函数或发送Ether)结构化为三个阶段是一个很好的指导:
// 1.检查条件
// 2.执行操作(潜在的变化条件)
// 3.与其他合同交互
//如果这些阶段混淆在一起,另一个合约可以回拨到当前合约中,并修改多次执行的状态或原因效果(以太付款).
//如果内部调用的函数包含与外部合同的交互,则必须将它们视为与外部合同的交互。
// 1.条件
require(now >= auctionEnd); //拍卖还没有结束
require(!ended); //此功能已被调用
// 2.效果
ended = true;
AuctionEnded(highestBidder, highestBid);
// 3.交互
beneficiary.transfer(highestBid);
}
}
盲拍
以前的公开拍卖会延伸到以下的盲拍。盲拍的优势在于投标期结束时没有时间压力。在一个透明的计算平台上创建一个盲目拍卖可能听起来像是一个矛盾,但是密码学可以解决这个问题。
在投标期间,投标人实际上并没有发出她的投标,而只是一个散列版本。由于目前认为实际上不可能找到两个(足够长)的哈希值相等的值,因此投标人承诺通过该投标。投标结束后,投标人必须公开他们的投标:他们将他们的价值未加密并且合同检查散列值与投标期间提供的散列值相同。
另一个挑战是如何在同一时间使拍卖具有约束力和盲目性:在赢得拍卖后,防止投标人不发送货币的唯一方法是让她在拍卖中一并发送。由于价值转移不能在以太坊蒙蔽,任何人都可以看到价值。
以下合同通过接受任何大于最高出价的值来解决此问题。因为这当然只能在披露阶段进行检查,所以有些出价可能是无效的,这是有意的(它甚至提供了一个明确的标记,用高价值转让放置无效出价):投标人可以通过放置几个较高的低无效出价。
pragma solidity ^0.4.21
contract BlindAuction {
struct Bid {
bytes32 blindedBid;
uint deposit;
}
address public beneficiary;
uint public biddingEnd;
uint public revealEnd;
bool public ended;
mapping(address => Bid[]) public bids;
address public highestBidder;
uint public highestBid;
//允许撤回之前的出价
mapping(address => uint) pendingReturns;
event AuctionEnded(address winnder, uint highestBid);
///修饰符是验证函数输入的便捷方式。 `onlyBefore`应用于下面的`bid`:
// 新函数体是修饰符的主体,其中`_`被旧函数体替换。
modifier onlyBefore(uint _time) { require(now < _time); _; }
modifier onlyAfter(uint _time) { require(now > _time); _; }
function BlindAuction {
uint _biddingTime,
uint _revealTime,
address _beneficiary
} public {
beneficiary = _beneficiary;
biddingEnd = now + _biddingTime;
revealEnd = biddingEnd + _revealTime;
}
///用`_blindedBid` = keccak256(value,fake,secret)放置一个不知情的出价。
///如果投标在披露阶段正确显示,则只会退还已发送的以太币。
//如果与投标一起发送的以太币至少“value”和“fake”不是true,则投标有效。
//将“fake”设置为true,并发送不确切的金额是隐藏实际出价但仍然需要存款的方法。 同一个地址可以放置多个出价。
function bid(bytes32 _blindedBid) public payable onlyBefore(biddingEnd)
{
bids[msg.sender].push(Bid({
blindedBid: _blindedBid,
deposit: msg.value
}));
}
///揭示你不知情的投标。 对于所有正确无视的无效出价以及除最高出价以外的所有出价,您都将获得退款。
function reveal (
uint[] _values,
bool[] _fake,
bytes32[] _secret
) public onlyAfter(biddingEnd) onlyBefore(revealEnd)
{
uint length = bids[msg.sender].length;
require(_values.length == length);
require(_fake.length == length);
require(_secret.length == length);
uint refund;
for (uint i = 0; i < length; i++) {
var bid = bids[msg.sender][i];
var (value, fake, secret) = (_values[i], _fake[i], _secret[i]);
if (bid.blindedBid != keccak256(value,fake,secret)) {
//投标并未实际显示。
//不要押金。
continue;
}
refund += bid.deposit;
if (!fake && bid.deposit > value) {
if (placeBid(msg.sender, value)) refund -= value;
}
//使发件人无法重新申请相同的存款。
bid.blindedBid = bytes32(0);
}
msg.sender.transfer(refund);
}
//这是一个“内部”功能,这意味着它只能从合同本身(或衍生合同)中调用。
function placeBid(address bidder, uint value) internal return (bool success)
{
if (value <= highestBid) {
return false;
}
if (highestBidder != 0) {
//退还先前出价最高的出价者。
pendingReturns[highestBidder] += highestBid;
}
highestBid = value;
highestBidder = bidder;
return true;
}
///撤销高出价的出价。
function withdraw() public {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
//将它设置为零是很重要的,因为接收者可以在`transfer`返回之前再次调用
//此函数作为接收调用的一部分(请参阅上面关于条件 - >效果 - >交互的注释)。
pendingReturns[msg.sender] = 0;
msg.sender.transfer(amount);
}
}
///结束拍卖并将最高出价发送给受益人。
function auctionEnd () public onlyAfter(revealEnd)
{
require(!ended);
AuctionEnded(highestBidder, highestBid);
ended = true;
beneficiary.transfer(highestBid);
}
}
安全的远程购买
pragma solidity ^0.4.21
contract Purchase {
uint public value;
address public seller;
address public buyer;
enum State { Created, Locked, Inactive}
State public state;
//确保`msg.value`是一个偶数。
//如果它是一个奇数,则它将被截断。
//通过乘法检查它不是奇数。
function Purchase() public payable {
seller = msg.sender;
value = msg.value / 2;
require((2 * value) == msg.value);
}
modifier condition(bool _condition) {
require(_condition);
_;
}
modifier onlyBuyer() {
require(msg.sender == buyer);
_;
}
modifier onlySeller() {
require(msg.sender == seller);
_;
}
modifier inState(State _state) {
require(state == _state);
_;
}
event Aborted();
event PurchaseConfirmed();
event ItemReceived();
///中止购买并回收以太。
///只能在合同被锁定之前由卖家调用。
function abort() public onlySeller inState(State.Created)
{
Aborted();
state = State.Inactive;
seller.transfer(this.balance);
}
///将购买确认为买家。
/// Transaction必须包含`2 * value`以太。
///以太会被锁定,直到confirmReceived被调用。
function confirmPurchase() public inState(State.Created) condition(msg.value == (2 * value)) payable
{
PurchaseConfirmed();
buyer = msg.sender;
state = State.Locked;
}
///确认您(买家)收到该物品。
///这将释放锁定的以太。
function confirmReceived() public onlyBuyer inState(State.Locked)
{
ItemReceived();
//先改变状态很重要,否则使用下面的`send`调用的合约可以在这里再次调用。
state = State.Inactive;
//NOTE:这实际上允许买家和卖家阻止退款 - 应该使用退款模式。
buyer.transfer(value);
seller.transfer(this.balance);
}
}
微支付通道
To be written.
深入理解Solidity之二---Solidity源代码文件结构
Solidity源代码文件结构
源文件可以包含任意数量的合约定义,包括指令和编译指示。
版本Pragma
源文件可以(也应该)用所谓的版本注释来注释,以拒绝被编译为未来可能引入不兼容更改的编译器版本。 我们试图将这种变化保持在绝对最低限度,特别是引入变化的方式是语义的变化也需要语法的变化,但这当然不总是可能的。 因此,至少对于包含重大更改的版本,通读更新日志总是一个好主意,这些版本始终具有0.x.0或x.0.0格式的版本。
版本附注使用如下:
pragma solidity ^0.4.0;
这样的源代码文件不会使用早于版本0.4.0的编译器进行编译,并且它也不适用于从版本0.5.0开始的编译器(第二个条件是使用^添加的)。 这背后的想法是,在版本0.5.0之前不会有任何重大更改,所以我们始终可以确定我们的代码将按照我们打算的方式进行编译。 我们不修复编译器的确切版本,因此bug修复版本仍然有可能。
可以为编译器版本指定更复杂的规则,表达式遵循npm使用的规则。
导入其他源文件
语法和语义
Solidity支持非常类似于JavaScript中可用的导入语句(来自ES6),尽管Solidity不知道“默认导出”的概念。
在全局范围内,您可以使用以下格式的导入语句:
import "filename";
该语句从“文件名”(及其导入的符号)中导入所有全局符号到当前全局作用域(与ES6不同,但向后兼容Solidity)。
import * as symbolName from "filename";
...创建一个新的全局符号symbolName
,其成员全部来自“filename
”的全局符号。
import {symbol1 as alias, symbol2} from "filename";
...分别创建新的全局符号alias
和symbol2
,它们分别从“filename
”引用symbol1
和symbol2
。
另一种语法不是ES6的一部分,但可能很方便:
import "filename" as symbolName;
这相当于从import * as symbolName from "filename";
。
Paths
在上面,filename
总是被视为一个路径,其中/
作为目录分隔符。.
作为当前和..
作为父目录。 什么时候 .
或..
后跟一个除/
以外的字符,它不被视为当前或父目录。 所有路径名都被视为绝对路径,除非它们以当前的.
或父目录开头..
。
要从与当前文件相同的目录中导入文件x
,请使用import "./x" as x;
. 如果使用import "x" as x;
则可以引用不同的文件(在全局“包含目录”中)。
它取决于编译器(见下文)如何实际解析路径。 通常,目录层次不需要严格映射到你的本地文件系统,它也可以映射到通过例如发现的资源。 ipfs,http或者git。
在实际编译器中使用
调用编译器时,不仅可以指定如何发现路径的第一个元素,但可以指定路径前缀重新映射,以便例如 github.com/ethereum/dapp-bin/library
被重新映射到/usr/local/dapp-bin/library
,编译器将从那里读取文件。 如果可以应用多重重映射,则首先尝试使用最长密钥的那个。 这允许用例如fallback-remapping
""
映射到/usr/local/include/solidity
(原文:This allows for a "fallback-remapping" with e.g. "" maps to
> "/usr/local/include/solidity") 翻译的不太通顺。
。 此外,这些重新映射可能取决于上下文,从而允许您配置要导入的包。 不同版本的同名图书馆。
solc:
对于solc(命令行编译器),这些重新映射是作为context:prefix = target
参数提供的,其中context:
和= target
部分都是可选的(在这种情况下目标缺省为前缀)。 所有重映射值都是常规文件(包括它们的依赖关系)。 这种机制是完全向后兼容的(只要没有文件名包含=或:),因此不是一个突破性的改变。 在导入以prefix
开头的文件的context
目录下的文件中的所有导入都将通过将prefix
替换为target
进行重定向。
例如,如果您将github.com/ethereum/dapp-bin/
本地克隆到/usr/local/dapp-bin
,则可以在源文件中使用以下内容:
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
然后运行编译器
solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol
作为一个更复杂的例子,假设你依赖于一些使用非常旧版本的dapp-bin的模块。 dapp-bin的旧版本在/usr/local/dapp-bin_old
处检出,然后您可以使用:
solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \
module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \
source.sol
以便module2
中的所有导入都指向旧版本,但module1
中的导入将获得新版本。
请注意,solc仅允许您包含来自特定目录的文件:它们必须位于某个明确指定的源文件的目录(或子目录)中,或位于重新映射目标的目录(或子目录)中。 如果你想允许直接绝对包含,只需添加重新映射=/
。
如果存在多个导致有效文件的重映射,则选择具有最长公共前缀的重映射。
Remix:
Remix为github提供了一个自动重新映射,并且还会自动通过网络检索文件:您可以通过导入可迭代映射例如:
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
未来可能会添加其他源代码提供者。
Comments
单行注释(//
)和多行注释(/*...*/
)是可用的。
// This is a single-line comment.
/*
This is a
multi-line comment.
*/
此外,还有另一种类型的注释称为natspec注释,对此文档尚未编写。 它们用三斜杠(///
)或双星号块(/ ** ... * /
)编写,它们应该直接用在函数声明或语句之上。 您可以在这些注释中使用Doxygen风格的标签来记录函数,为形式验证注释条件,并提供确认文本,当用户尝试调用函数时向用户显示。
在下面的例子中,我们记录了合同的标题,两个输入参数和两个返回值的解释。
pragma solidity ^0.4.0;
/** @title Shape calculator. */
contract shapeCalculator {
/** @dev Calculates a rectangle''s surface and perimeter.
* @param w Width of the rectangle.
* @param h Height of the rectangle.
* @return s The calculated surface.
* @return p The calculated perimeter.
*/
function rectangle(uint w, uint h) returns (uint s, uint p) {
s = w * h;
p = 2 * (w + h);
}
}
Solidity 官方文档中文版 3_安装Solidity
基于浏览器的Solidity
如果你只是想尝试一个使用Solidity的小合约,你不需要安装任何东西,只要访问 基于浏览器的Solidity http://remix.ethereum.org/。
如果你想离线使用,你可以保存页面到本地,或者从 http://github.com/chriseth/browser-solidity 克隆一个。
NPM / node.js
这可能安装Solidity到本地最轻便最省事的方法。
在基于浏览器的Solidity上,Emscripten提供了一个跨平台JavaScript库,把C++源码编译为JavaScript,同时也提供NPM安装包。
去安装它就可以简单使用。,
npm install solc
如何使用nodejs包的详细信息可以在代码库中找到.
二进制安装包
Ethereum.
包括Mix IDE的二进制Solidity安装包在Ethereum网站C++ bundle中下载。
从源码构建
在MacOS X、Ubuntu和其它类Unix系统中编译安装Solidity非常相似。这个指南开始讲解如何在每个平台下安装相关的依赖软件,然后构建Solidity。
MacOS X
系统需求:
-
OS X Yosemite (10.10.5)
-
Homebrew
- Xcode
安装Homebrew:
brew update
brew install boost --c++11 # 这需要等待一段时间
brew install cmake cryptopp miniupnpc leveldb gmp libmicrohttpd libjson-rpc-cpp
# 仅仅安装Mix IDE和Alethzero
brew install xz d-bus
brew install llvm --HEAD --with-clang
brew install qt5 --with-d-bus # 如果长时间的等待让你发疯,那么添加--verbose输出信息会让你感觉更好。
Ubuntu 系统
下面是在最新版Ubuntu系统上编译安装Solidity的指南。最佳的支持平台是2014年11月发布的64位Ubuntu 14.04,至少需要2GB内存。我们所有的测试都是基于此版本,当然我们也欢迎其它版本的测试贡献者。
安装依赖软件:
在你从源码编译之前,你需要准备一些工具和依赖软件。
首先,升级你的代码库。Ubuntu主代码库不提供所有的包,你需要从Ethereum PPA和LLVM获取。
注意
Ubuntu 14.04的用户需要使用:sudo apt-add-repository ppa:george-edison55/cmake-3.x
获取最新版本的cmake。
现在加入其它的包:
sudo apt-get -y update
sudo apt-get -y install language-pack-en-base
sudo dpkg-reconfigure locales
sudo apt-get -y install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo add-apt-repository -y ppa:ethereum/ethereum-dev
sudo apt-get -y update
sudo apt-get -y upgrade
对于Ubbuntu 15.04(Vivid Vervet)或者更老版本,使用下面的命令获取开发相关的包:
sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjson-rpc-cpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev
对于Ubbuntu 15.10(Wily Werewolf)或者更新版本,使用下面的命令获取开发相关的包:
sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjsonrpccpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev
不同版本使用不同获取命令的原因是,libjsonrpccpp-dev已经在最新版的Ubuntu的通用代码仓库中。
编译
如果你只准备安装solidity,忽略末尾Alethzero和Mix的错误。
git clone --recursive https://github.com/ethereum/webthree-umbrella.git
cd webthree-umbrella
./webthree-helpers/scripts/ethupdate.sh --no-push --simple-pull --project solidity
# 更新Solidity库
./webthree-helpers/scripts/ethbuild.sh --no-git --project solidity --all --cores 4 -DEVMJIT=0
# 编译Solidity及其它
# 在OS X系统加上DEVMJIT将不能编译,在Linux系统上则没问题
如果你选择安装Alethzero和Mix:
git clone --recursive https://github.com/ethereum/webthree-umbrella.git
cd webthree-umbrella && mkdir -p build && cd build
cmake ..
如果你想帮助Solidity的开发,你需要分支(fork)Solidity并添加到你的私人远端分支:
cd webthree-umbrella/solidity
git remote add personal git@github.com:username/solidity.git
注意webthree-umbrella使用的子模块,所以solidity是其自己的git代码库,但是他的设置不是保存在 .git/config
, 而是在webthree-umbrella/.git/modules/solidity/config
.
Solidity 官方文档中文版 4_Solidity 编程实例
Voting 投票
接下来的合约非常复杂,但展示了很多Solidity的特性。它实现了一个投票合约。当然,电子选举的主要问题是如何赋予投票权给准确的人,并防止操纵。我们不能解决所有的问题,但至少我们会展示如何委托投票可以同时做到投票统计是自动和完全透明。
思路是为每张选票创建一个合约,每个投票选项提供一个短名称。合约创建者作为会长将会给每个投票参与人各自的地址投票权。
地址后面的人们可以选择自己投票或者委托信任的代表人替他们投票。在投票结束后,winningProposal()将会返回获得票数最多的提案。
/// @title Voting with delegation.
/// @title 授权投票
contract Ballot
{
// 这里声明了复杂类型
// 将会在被后面的参数使用
// 代表一个独立的投票人。
struct Voter
{
uint weight; // 累积的权重。
bool voted; // 如果为真,则表示该投票人已经投票。
address delegate; // 委托的投票代表
uint vote; // 投票选择的提案索引号
}
// 这是一个独立提案的类型
struct Proposal
{
bytes32 name; // 短名称(32字节)
uint voteCount; // 累计获得的票数
}
address public chairperson;
//这里声明一个状态变量,保存每个独立地址的`Voter` 结构
mapping(address => Voter) public voters;
//一个存储`Proposal`结构的动态数组
Proposal[] public proposals;
// 创建一个新的投票用于选出一个提案名`proposalNames`.
function Ballot(bytes32[] proposalNames)
{
chairperson = msg.sender;
voters[chairperson].weight = 1;
//对提供的每一个提案名称,创建一个新的提案
//对象添加到数组末尾
for (uint i = 0; i < proposalNames.length; i++)
//`Proposal({...})` 创建了一个临时的提案对象,
//`proposal.push(...)`添加到了提案数组`proposals`末尾。
proposals.push(Proposal({
name: proposalNames[i],
voteCount: 0
}));
}
//给投票人`voter`参加投票的投票权,
//只能由投票主持人`chairperson`调用。
function giveRightToVote(address voter)
{
if (msg.sender != chairperson || voters[voter].voted)
//`throw`会终止和撤销所有的状态和以太改变。
//如果函数调用无效,这通常是一个好的选择。
//但是需要注意,这会消耗提供的所有gas。
throw;
voters[voter].weight = 1;
}
// 委托你的投票权到一个投票代表 `to`。
function delegate(address to)
{
// 指定引用
Voter sender = voters[msg.sender];
if (sender.voted)
throw;
//当投票代表`to`也委托给别人时,寻找到最终的投票代表
while (voters[to].delegate != address(0) &&
voters[to].delegate != msg.sender)
to = voters[to].delegate;
// 当最终投票代表等于调用者,是不被允许的。
if (to == msg.sender)
throw;
//因为`sender`是一个引用,
//这里实际修改了`voters[msg.sender].voted`
sender.voted = true;
sender.delegate = to;
Voter delegate = voters[to];
if (delegate.voted)
//如果委托的投票代表已经投票了,直接修改票数
proposals[delegate.vote].voteCount += sender.weight;
else
//如果投票代表还没有投票,则修改其投票权重。
delegate.weight += sender.weight;
}
///投出你的选票(包括委托给你的选票)
///给 `proposals[proposal].name`。
function vote(uint proposal)
{
Voter sender = voters[msg.sender];
if (sender.voted) throw;
sender.voted = true;
sender.vote = proposal;
//如果`proposal`索引超出了给定的提案数组范围
//将会自动抛出异常,并撤销所有的改变。
proposals[proposal].voteCount += sender.weight;
}
///@dev 根据当前所有的投票计算出当前的胜出提案
function winningProposal() constant
returns (uint winningProposal)
{
uint winningVoteCount = 0;
for (uint p = 0; p < proposals.length; p++)
{
if (proposals[p].voteCount > winningVoteCount)
{
winningVoteCount = proposals[p].voteCount;
winningProposal = p;
}
}
}
}
可能的改进
现在,指派投票权到所有的投票参加者需要许多的交易。你能想出更好的方法么?
盲拍
这一节,我们将展示在以太上创建一个完整的盲拍合约是多么简单。我们从一个所有人都能看到出价的公开拍卖开始,接着扩展合约成为一个在拍卖结束以前不能看到实际出价的盲拍。
简单的公开拍卖
通常简单的公开拍卖合约,是每个人可以在拍卖期间发送他们的竞拍出价。为了实现绑定竞拍人的到他们的拍卖,竞拍包括发送金额/ether。如果产生了新的最高竞拍价,前一个最高价竞拍人将会拿回他的钱。在竞拍阶段结束后,受益人人需要手动调用合约收取他的钱 — — 合约不会激活自己。
contract SimpleAuction {
// 拍卖的参数。
// 时间要么为unix绝对时间戳(自1970-01-01以来的秒数),
// 或者是以秒为单位的出块时间
address public beneficiary;
uint public auctionStart;
uint public biddingTime;
//当前的拍卖状态
address public highestBidder;
uint public highestBid;
//在结束时设置为true来拒绝任何改变
bool ended;
//当改变时将会触发的Event
event HighestBidIncreased(address bidder, uint amount);
event AuctionEnded(address winner, uint amount);
//下面是一个叫做natspec的特殊注释,
//由3个连续的斜杠标记,当询问用户确认交易事务时将显示。
///创建一个简单的合约使用`_biddingTime`表示的竞拍时间,
/// 地址`_beneficiary`.代表实际的拍卖者
function SimpleAuction(uint _biddingTime,
address _beneficiary) {
beneficiary = _beneficiary;
auctionStart = now;
biddingTime = _biddingTime;
}
///对拍卖的竞拍保证金会随着交易事务一起发送,
///只有在竞拍失败的时候才会退回
function bid() {
//不需要任何参数,所有的信息已经是交易事务的一部分
if (now > auctionStart + biddingTime)
//当竞拍结束时撤销此调用
throw;
if (msg.value <= highestBid)
//如果出价不是最高的,发回竞拍保证金。
throw;
if (highestBidder != 0)
highestBidder.send(highestBid);
highestBidder = msg.sender;
highestBid = msg.value;
HighestBidIncreased(msg.sender, msg.value);
}
///拍卖结束后发送最高的竞价到拍卖人
function auctionEnd() {
if (now <= auctionStart + biddingTime)
throw;
//拍卖还没有结束
if (ended)
throw;
//这个收款函数已经被调用了
AuctionEnded(highestBidder, highestBid);
//发送合约拥有所有的钱,因为有一些保证金可能退回失败了。
beneficiary.send(this.balance);
ended = true;
}
function () {
//这个函数将会在发送到合约的交易事务包含无效数据
//或无数据的时执行,这里撤销所有的发送,
//所以没有人会在使用合约时因为意外而丢钱。
throw;
}
}
Blind Auction 盲拍
接下来扩展前面的公开拍卖成为一个盲拍。盲拍的特点是拍卖结束以前没有时间压力。在一个透明的计算平台上创建盲拍系统听起来可能有些矛盾,但是加密算法能让你脱离困境。
在拍卖阶段, 竞拍人不需要发送实际的出价,仅仅只需要发送一个它的散列值。因为目前几乎不可能找到两个值(足够长)的散列值相等,竞拍者提交他们的出价散列值。在拍卖结束后,竞拍人重新发送未加密的竞拍出价,合约将检查其散列值是否和拍卖阶段发送的一样。 另一个挑战是如何让拍卖同时实现绑定和致盲 :防止竞拍人竞拍成功后不付钱的唯一的办法是,在竞拍出价的同时发送保证金。但是在Ethereum上发送保证金是无法致盲,所有人都能看到保证金。下面的合约通过接受任何尽量大的出价来解决这个问题。当然这可以在最后的揭拍阶段进行复核,一些竞拍出价可能是无效的,这样做的目的是(它提供一个显式的标志指出是无效的竞拍,同时包含高额保证金):竞拍人可以通过放置几个无效的高价和低价竞拍来混淆竞争对手。
contract BlindAuction
{
struct Bid
{
bytes32 blindedBid;
uint deposit;
}
address public beneficiary;
uint public auctionStart;
uint public biddingEnd;
uint public revealEnd;
bool public ended;
mapping(address => Bid[]) public bids;
address public highestBidder;
uint public highestBid;
event AuctionEnded(address winner, uint highestBid);
///修饰器(Modifier)是一个简便的途径用来验证函数输入的有效性。
///`onlyBefore` 应用于下面的 `bid`函数,其旧的函数体替换修饰器主体中 `_`后就是其新的函数体
modifier onlyBefore(uint _time) { if (now >= _time) throw; _ }
modifier onlyAfter(uint _time) { if (now <= _time) throw; _ }
function BlindAuction(uint _biddingTime,
uint _revealTime,
address _beneficiary)
{
beneficiary = _beneficiary;
auctionStart = now;
biddingEnd = now + _biddingTime;
revealEnd = biddingEnd + _revealTime;
}
///放置一个盲拍出价使用`_blindedBid`=sha3(value,fake,secret).
///仅仅在竞拍结束正常揭拍后退还发送的以太。当随同发送的以太至少
///等于 "value"指定的保证金并且 "fake"不为true的时候才是有效的竞拍
///出价。设置 "fake"为true或发送不合适的金额将会掩没真正的竞拍出
///价,但是仍然需要抵押保证金。同一个地址可以放置多个竞拍。
function bid(bytes32 _blindedBid)
onlyBefore(biddingEnd)
{
bids[msg.sender].push(Bid({
blindedBid: _blindedBid,
deposit: msg.value
}));
}
///揭开你的盲拍竞价。你将会拿回除了最高出价外的所有竞拍保证金
///以及正常的无效盲拍保证金。
function reveal(uint[] _values, bool[] _fake,
bytes32[] _secret)
onlyAfter(biddingEnd)
onlyBefore(revealEnd)
{
uint length = bids[msg.sender].length;
if (_values.length != length || _fake.length != length ||
_secret.length != length)
throw;
uint refund;
for (uint i = 0; i < length; i++)
{
var bid = bids[msg.sender][i];
var (value, fake, secret) =
(_values[i], _fake[i], _secret[i]);
if (bid.blindedBid != sha3(value, fake, secret))
//出价未被正常揭拍,不能取回保证金。
continue;
refund += bid.deposit;
if (!fake && bid.deposit >= value)
if (placeBid(msg.sender, value))
refund -= value;
//保证发送者绝不可能重复取回保证金
bid.blindedBid = 0;
}
msg.sender.send(refund);
}
//这是一个内部 (internal)函数,
//意味着仅仅只有合约(或者从其继承的合约)可以调用
function placeBid(address bidder, uint value) internal
returns (bool success)
{
if (value <= highestBid)
return false;
if (highestBidder != 0)
//退还前一个最高竞拍出价
highestBidder.send(highestBid);
highestBid = value;
highestBidder = bidder;
return true;
}
///竞拍结束后发送最高出价到竞拍人
function auctionEnd()
onlyAfter(revealEnd)
{
if (ended) throw;
AuctionEnded(highestBidder, highestBid);
//发送合约拥有所有的钱,因为有一些保证金退回可能失败了。
beneficiary.send(this.balance);
ended = true;
}
function () { throw; }
}
Safe Remote Purchase 安全的远程购物
contract Purchase
{
uint public value;
address public seller;
address public buyer;
enum State { Created, Locked, Inactive }
State public state;
function Purchase()
{
seller = msg.sender;
value = msg.value / 2;
if (2 * value != msg.value) throw;
}
modifier require(bool _condition)
{
if (!_condition) throw;
_
}
modifier onlyBuyer()
{
if (msg.sender != buyer) throw;
_
}
modifier onlySeller()
{
if (msg.sender != seller) throw;
_
}
modifier inState(State _state)
{
if (state != _state) throw;
_
}
event aborted();
event purchaseConfirmed();
event itemReceived();
///终止购物并收回以太。仅仅可以在合约未锁定时被卖家调用。
function abort()
onlySeller
inState(State.Created)
{
aborted();
seller.send(this.balance);
state = State.Inactive;
}
///买家确认购买。交易包含两倍价值的(`2 * value`)以太。
///这些以太会一直锁定到收货确认(confirmReceived)被调用。
function confirmPurchase()
inState(State.Created)
require(msg.value == 2 * value)
{
purchaseConfirmed();
buyer = msg.sender;
state = State.Locked;
}
///确认你(买家)收到了货物,这将释放锁定的以太。
function confirmReceived()
onlyBuyer
inState(State.Locked)
{
itemReceived();
buyer.send(value);//我们有意忽略了返回值。
seller.send(this.balance);
state = State.Inactive;
}
function() { throw; }
}
小额支付通道
今天的关于【Solidity】4.单位和全局可变量 - 深入理解Solidity的分享已经结束,谢谢您的关注,如果想了解更多关于
本文标签: