solidity学习笔记

  1. 1. Solidity简介
  2. 2. Solidity环境配置
  3. 3. Solidity学习
    1. 3.0.1. 秘密竞拍合约

Solidity简介

Solidity是以太坊智能合约编程语言。是一门面向合约的、为实现智能合约而创建的高级编程语言。这门语言受到了 C++,Python 和 Javascript 语言的影响,设计的目的是能在 以太坊虚拟机(EVM) 上运行。

Solidity 是静态类型语言,支持继承、库和复杂的用户定义类型等特性。

在部署合约时,应该尽量使用最新版本,因为新版本会有一些重大的新特性以及bug修复。

可以在Solidity中文文档中了解更多

Solidity环境配置

我使用的编辑器是VS Code

在扩展中搜索solidity并安装:

pic1

根据需要更改相关设置。

Solidity学习

不知道是不是因为版本原因,Solidity中文文档中的例子复制过来后有许多问题,下面是经过我修改之后的部分例子。

秘密竞拍合约

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract BlindAuction {
struct Bid {
bytes32 blindedBid;
uint deposit;
}

address payable public beneficiary;
//竞拍截止时间
uint public biddingEnd;
//明牌截止时间
uint public revealEnd;
//本次拍卖结束标志
bool public ended;
//通过Hash将地址与Bid结构的竞拍者信息进行映射
mapping(address => Bid[]) public bids;

address public highestBidder;
uint public highestBid;

// 可以取回的之前的出价
mapping(address => uint) pendingReturns;

event AuctionEnded(address winner, uint highestBid);

/// 使用 modifier 可以更便捷的校验函数的入参。
/// `onlyBefore` 会被用于后面的 `bid` 函数:
/// 新的函数体是由 modifier 本身的函数体,并用原函数体替换 `_;` 语句来组成的。
modifier onlyBefore(uint _time) { require(block.timestamp < _time); _; }
modifier onlyAfter(uint _time) { require(block.timestamp > _time); _; }

constructor(
uint _biddingTime,
uint _revealTime,
address payable _beneficiary
) {
beneficiary = _beneficiary;
biddingEnd = block.timestamp + _biddingTime;
revealEnd = biddingEnd + _revealTime;
}

/// 可以通过 `_blindedBid` = keccak256(value, fake, secret)
/// 设置一个秘密竞拍。
/// 只有在出价披露阶段被正确披露,已发送的以太币才会被退还。
/// 如果与出价一起发送的以太币至少为 “value” 且 “fake” 不为真,则出价有效。
/// 将 “fake” 设置为 true ,然后发送满足订金金额但又不与出价相同的金额是隐藏实际出价的方法。
/// 同一个地址可以放置多个出价。
function bid(bytes32 _blindedBid)
public
payable
onlyBefore(biddingEnd)
{
bids[msg.sender].push(Bid({
blindedBid: _blindedBid,
deposit: msg.value
}));
}

/// 披露你的秘密竞拍出价。
/// 对于所有正确披露的无效出价以及除最高出价以外的所有出价,你都将获得退款。
function reveal(
uint[] memory _values,
bool[] memory _fake,
bytes32[] memory _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++) {
Bid storage bidToCheck = bids[msg.sender][i];
(uint value, bool fake, bytes32 secret) =
(_values[i], _fake[i], _secret[i]);
if (bidToCheck.blindedBid != keccak256(abi.encodePacked(value, fake, secret))) {
// 出价未能正确披露
// 不返还订金
continue;
}
refund += bidToCheck.deposit;
if (!fake && bidToCheck.deposit >= value) {
if (placeBid(msg.sender, value))
refund -= value;
}
// 使发送者不可能再次认领同一笔订金
bidToCheck.blindedBid = bytes32(0);
}
payable(msg.sender).transfer(refund);
}

// 这是一个 "internal" 函数, 意味着它只能在本合约(或继承合约)内被调用
function placeBid(address bidder, uint value) internal
returns (bool success)
{
if (value <= highestBid) {
return false;
}
if (highestBidder != address(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;

payable(msg.sender).transfer(amount);
}
}

/// 结束拍卖,并把最高的出价发送给受益人
function auctionEnd()
public
onlyAfter(revealEnd)
{
require(!ended);
emit AuctionEnded(highestBidder, highestBid);
ended = true;
beneficiary.transfer(highestBid);
}
}

在第34行构造函数constructor处,文档中为:

1
2
3
4
5
6
7
8
9
constructor(
uint _biddingTime,
uint _revealTime,
address payable _beneficiary
) public {
beneficiary = _beneficiary;
biddingEnd = block.timestamp + _biddingTime;
revealEnd = biddingEnd + _revealTime;
}

在VSC中会出现警告,编译无法通过

1
Visibility for constructor is ignored. If you want the contract to be non-deployable, making it "abstract" is sufficient.

因为在最初部署合约时,构造函数只运行一次。他们不能在以后调用,因此在变量或函数意义上不是可见的,所以不能添加public可见性标识符。

而抽象合约则没有构造函数,所以这个错误是问我们是打算使用构造函数,还是打算写一个抽象合约?

在63行的reveal中,文档中的代码为:

1
2
3
4
5
function reveal(
uint[] _values,
bool[] _fake,
bytes32[] _secret
)

这同样是有问题的

1
Data location must be "memory" or "calldata" for parameter in function, but none was given

我们需要为其加上memory修饰。