1 /*
2 	This file is part of solidity.
3 
4 	solidity is free software: you can redistribute it and/or modify
5 	it under the terms of the GNU General Public License as published by
6 	the Free Software Foundation, either version 3 of the License, or
7 	(at your option) any later version.
8 
9 	solidity is distributed in the hope that it will be useful,
10 	but WITHOUT ANY WARRANTY; without even the implied warranty of
11 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 	GNU General Public License for more details.
13 
14 	You should have received a copy of the GNU General Public License
15 	along with solidity.  If not, see <http://www.gnu.org/licenses/>.
16 */
17 // SPDX-License-Identifier: GPL-3.0
18 /**
19  * @author Christian <c@ethdev.com>
20  * @date 2015
21  * Tests for a fixed fee registrar contract.
22  */
23 
24 #include <test/libsolidity/SolidityExecutionFramework.h>
25 #include <test/contracts/ContractInterface.h>
26 #include <test/EVMHost.h>
27 
28 #include <libsolutil/LazyInit.h>
29 
30 #include <boost/test/unit_test.hpp>
31 
32 #include <string>
33 #include <optional>
34 
35 using namespace std;
36 using namespace solidity;
37 using namespace solidity::util;
38 using namespace solidity::test;
39 
40 namespace solidity::frontend::test
41 {
42 
43 namespace
44 {
45 static char const* registrarCode = R"DELIMITER(
46 pragma solidity >=0.7.0 <0.9.0;
47 
48 abstract contract NameRegister {
49 	function addr(string memory _name) public virtual view returns (address o_owner);
50 	function name(address _owner) public view virtual returns (string memory o_name);
51 }
52 
53 abstract contract Registrar is NameRegister {
54 	event Changed(string indexed name);
55 	event PrimaryChanged(string indexed name, address indexed addr);
56 
57 	function owner(string memory _name) public view virtual returns (address o_owner);
58 	function addr(string memory _name) public virtual override view returns (address o_address);
59 	function subRegistrar(string memory _name) public virtual view returns (address o_subRegistrar);
60 	function content(string memory _name) public virtual view returns (bytes32 o_content);
61 
62 	function name(address _owner) public virtual override view returns (string memory o_name);
63 }
64 
65 abstract contract AuctionSystem {
66 	event AuctionEnded(string indexed _name, address _winner);
67 	event NewBid(string indexed _name, address _bidder, uint _value);
68 
69 	/// Function that is called once an auction ends.
70 	function onAuctionEnd(string memory _name) internal virtual;
71 
72 	function bid(string memory _name, address payable _bidder, uint _value) internal {
73 		Auction storage auction = m_auctions[_name];
74 		if (auction.endDate > 0 && block.timestamp > auction.endDate)
75 		{
76 			emit AuctionEnded(_name, auction.highestBidder);
77 			onAuctionEnd(_name);
78 			delete m_auctions[_name];
79 			return;
80 		}
81 		if (msg.value > auction.highestBid)
82 		{
83 			// new bid on auction
84 			auction.secondHighestBid = auction.highestBid;
85 			auction.sumOfBids += _value;
86 			auction.highestBid = _value;
87 			auction.highestBidder = _bidder;
88 			auction.endDate = block.timestamp + c_biddingTime;
89 
90 			emit NewBid(_name, _bidder, _value);
91 		}
92 	}
93 
94 	uint constant c_biddingTime = 7 days;
95 
96 	struct Auction {
97 		address payable highestBidder;
98 		uint highestBid;
99 		uint secondHighestBid;
100 		uint sumOfBids;
101 		uint endDate;
102 	}
103 	mapping(string => Auction) m_auctions;
104 }
105 
106 contract GlobalRegistrar is Registrar, AuctionSystem {
107 	struct Record {
108 		address payable owner;
109 		address primary;
110 		address subRegistrar;
111 		bytes32 content;
112 		uint renewalDate;
113 	}
114 
115 	uint constant c_renewalInterval = 365 days;
116 	uint constant c_freeBytes = 12;
117 
118 	constructor() {
119 		// TODO: Populate with hall-of-fame.
120 	}
121 
122 	function onAuctionEnd(string memory _name) internal override {
123 		Auction storage auction = m_auctions[_name];
124 		Record storage record = m_toRecord[_name];
125 		address previousOwner = record.owner;
126 		record.renewalDate = block.timestamp + c_renewalInterval;
127 		record.owner = auction.highestBidder;
128 		emit Changed(_name);
129 		if (previousOwner != 0x0000000000000000000000000000000000000000) {
130 			if (!record.owner.send(auction.sumOfBids - auction.highestBid / 100))
131 				revert();
132 		} else {
133 			if (!auction.highestBidder.send(auction.highestBid - auction.secondHighestBid))
134 				revert();
135 		}
136 	}
137 
138 	function reserve(string calldata _name) external payable {
139 		if (bytes(_name).length == 0)
140 			revert();
141 		bool needAuction = requiresAuction(_name);
142 		if (needAuction)
143 		{
144 			if (block.timestamp < m_toRecord[_name].renewalDate)
145 				revert();
146 			bid(_name, payable(msg.sender), msg.value);
147 		} else {
148 			Record storage record = m_toRecord[_name];
149 			if (record.owner != 0x0000000000000000000000000000000000000000)
150 				revert();
151 			m_toRecord[_name].owner = payable(msg.sender);
152 			emit Changed(_name);
153 		}
154 	}
155 
156 	function requiresAuction(string memory _name) internal returns (bool) {
157 		return bytes(_name).length < c_freeBytes;
158 	}
159 
160 	modifier onlyrecordowner(string memory _name) { if (m_toRecord[_name].owner == msg.sender) _; }
161 
162 	function transfer(string memory _name, address payable _newOwner) onlyrecordowner(_name) public {
163 		m_toRecord[_name].owner = _newOwner;
164 		emit Changed(_name);
165 	}
166 
167 	function disown(string memory _name) onlyrecordowner(_name) public {
168 		if (stringsEqual(m_toName[m_toRecord[_name].primary], _name))
169 		{
170 			emit PrimaryChanged(_name, m_toRecord[_name].primary);
171 			m_toName[m_toRecord[_name].primary] = "";
172 		}
173 		delete m_toRecord[_name];
174 		emit Changed(_name);
175 	}
176 
177 	function setAddress(string memory _name, address _a, bool _primary) onlyrecordowner(_name) public {
178 		m_toRecord[_name].primary = _a;
179 		if (_primary)
180 		{
181 			emit PrimaryChanged(_name, _a);
182 			m_toName[_a] = _name;
183 		}
184 		emit Changed(_name);
185 	}
186 	function setSubRegistrar(string memory _name, address _registrar) onlyrecordowner(_name) public {
187 		m_toRecord[_name].subRegistrar = _registrar;
188 		emit Changed(_name);
189 	}
190 	function setContent(string memory _name, bytes32 _content) onlyrecordowner(_name) public {
191 		m_toRecord[_name].content = _content;
192 		emit Changed(_name);
193 	}
194 
195 	function stringsEqual(string storage _a, string memory _b) internal returns (bool) {
196 		bytes storage a = bytes(_a);
197 		bytes memory b = bytes(_b);
198 		if (a.length != b.length)
199 			return false;
200 		// @todo unroll this loop
201 		for (uint i = 0; i < a.length; i ++)
202 			if (a[i] != b[i])
203 				return false;
204 		return true;
205 	}
206 
207 	function owner(string memory _name) public override view returns (address) { return m_toRecord[_name].owner; }
208 	function addr(string memory _name) public override view returns (address) { return m_toRecord[_name].primary; }
209 	function subRegistrar(string memory _name) public override view returns (address) { return m_toRecord[_name].subRegistrar; }
210 	function content(string memory _name) public override view returns (bytes32) { return m_toRecord[_name].content; }
211 	function name(address _addr) public override view returns (string memory o_name) { return m_toName[_addr]; }
212 
213 	mapping (address => string) m_toName;
214 	mapping (string => Record) m_toRecord;
215 }
216 )DELIMITER";
217 
218 static LazyInit<bytes> s_compiledRegistrar;
219 
220 class AuctionRegistrarTestFramework: public SolidityExecutionFramework
221 {
222 protected:
deployRegistrar()223 	void deployRegistrar()
224 	{
225 		bytes const& compiled = s_compiledRegistrar.init([&]{
226 			return compileContract(registrarCode, "GlobalRegistrar");
227 		});
228 
229 		sendMessage(compiled, true);
230 		BOOST_REQUIRE(m_transactionSuccessful);
231 		BOOST_REQUIRE(!m_output.empty());
232 	}
233 
234 	class RegistrarInterface: public ContractInterface
235 	{
236 	public:
RegistrarInterface(SolidityExecutionFramework & _framework)237 		RegistrarInterface(SolidityExecutionFramework& _framework): ContractInterface(_framework) {}
reserve(string const & _name)238 		void reserve(string const& _name)
239 		{
240 			callString("reserve", _name);
241 		}
owner(string const & _name)242 		h160 owner(string const& _name)
243 		{
244 			return callStringReturnsAddress("owner", _name);
245 		}
setAddress(string const & _name,h160 const & _address,bool _primary)246 		void setAddress(string const& _name, h160 const& _address, bool _primary)
247 		{
248 			callStringAddressBool("setAddress", _name, _address, _primary);
249 		}
addr(string const & _name)250 		h160 addr(string const& _name)
251 		{
252 			return callStringReturnsAddress("addr", _name);
253 		}
name(h160 const & _addr)254 		string name(h160 const& _addr)
255 		{
256 			return callAddressReturnsString("name", _addr);
257 		}
setSubRegistrar(string const & _name,h160 const & _address)258 		void setSubRegistrar(string const& _name, h160 const& _address)
259 		{
260 			callStringAddress("setSubRegistrar", _name, _address);
261 		}
subRegistrar(string const & _name)262 		h160 subRegistrar(string const& _name)
263 		{
264 			return callStringReturnsAddress("subRegistrar", _name);
265 		}
setContent(string const & _name,h256 const & _content)266 		void setContent(string const& _name, h256 const& _content)
267 		{
268 			callStringBytes32("setContent", _name, _content);
269 		}
content(string const & _name)270 		h256 content(string const& _name)
271 		{
272 			return callStringReturnsBytes32("content", _name);
273 		}
transfer(string const & _name,h160 const & _target)274 		void transfer(string const& _name, h160 const& _target)
275 		{
276 			return callStringAddress("transfer", _name, _target);
277 		}
disown(string const & _name)278 		void disown(string const& _name)
279 		{
280 			return callString("disown", _name);
281 		}
282 	};
283 
284 	int64_t const m_biddingTime = 7 * 24 * 3600;
285 };
286 
287 }
288 
289 /// This is a test suite that tests optimised code!
BOOST_FIXTURE_TEST_SUITE(SolidityAuctionRegistrar,AuctionRegistrarTestFramework)290 BOOST_FIXTURE_TEST_SUITE(SolidityAuctionRegistrar, AuctionRegistrarTestFramework)
291 
292 BOOST_AUTO_TEST_CASE(creation)
293 {
294 	deployRegistrar();
295 }
296 
BOOST_AUTO_TEST_CASE(reserve)297 BOOST_AUTO_TEST_CASE(reserve)
298 {
299 	// Test that reserving works for long strings
300 	deployRegistrar();
301 	vector<string> names{"abcabcabcabcabc", "defdefdefdefdef", "ghighighighighighighighighighighighighighighi"};
302 
303 	RegistrarInterface registrar(*this);
304 
305 	// should not work
306 	registrar.reserve("");
307 	BOOST_CHECK_EQUAL(registrar.owner(""), h160{});
308 
309 	for (auto const& name: names)
310 	{
311 		registrar.reserve(name);
312 		BOOST_CHECK_EQUAL(registrar.owner(name), m_sender);
313 	}
314 }
315 
BOOST_AUTO_TEST_CASE(double_reserve_long)316 BOOST_AUTO_TEST_CASE(double_reserve_long)
317 {
318 	// Test that it is not possible to re-reserve from a different address.
319 	deployRegistrar();
320 	string name = "abcabcabcabcabcabcabcabcabcabca";
321 	RegistrarInterface registrar(*this);
322 	registrar.reserve(name);
323 	BOOST_CHECK_EQUAL(registrar.owner(name), m_sender);
324 
325 	sendEther(account(1), u256(10) * ether);
326 	m_sender = account(1);
327 	registrar.reserve(name);
328 	BOOST_CHECK_EQUAL(registrar.owner(name), account(0));
329 }
330 
BOOST_AUTO_TEST_CASE(properties)331 BOOST_AUTO_TEST_CASE(properties)
332 {
333 	// Test setting and retrieving  the various properties works.
334 	deployRegistrar();
335 	RegistrarInterface registrar(*this);
336 	string names[] = {"abcaeouoeuaoeuaoeu", "defncboagufra,fui", "ghagpyajfbcuajouhaeoi"};
337 	size_t addr = 0x9872543;
338 	size_t count = 1;
339 	for (string const& name: names)
340 	{
341 		m_sender = account(0);
342 		sendEther(account(count), u256(20) * ether);
343 		m_sender = account(count);
344 		auto sender = m_sender;
345 		addr += count;
346 		// setting by sender works
347 		registrar.reserve(name);
348 		BOOST_CHECK_EQUAL(registrar.owner(name), sender);
349 		registrar.setAddress(name, h160(addr), true);
350 		BOOST_CHECK_EQUAL(registrar.addr(name), h160(addr));
351 		registrar.setSubRegistrar(name, h160(addr + 20));
352 		BOOST_CHECK_EQUAL(registrar.subRegistrar(name), h160(addr + 20));
353 		registrar.setContent(name, h256(u256(addr + 90)));
354 		BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90)));
355 
356 		// but not by someone else
357 		m_sender = account(count - 1);
358 		BOOST_CHECK_EQUAL(registrar.owner(name), sender);
359 		registrar.setAddress(name, h160(addr + 1), true);
360 		BOOST_CHECK_EQUAL(registrar.addr(name), h160(addr));
361 		registrar.setSubRegistrar(name, h160(addr + 20 + 1));
362 		BOOST_CHECK_EQUAL(registrar.subRegistrar(name), h160(addr + 20));
363 		registrar.setContent(name, h256(u256(addr + 90 + 1)));
364 		BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90)));
365 		count++;
366 	}
367 }
368 
BOOST_AUTO_TEST_CASE(transfer)369 BOOST_AUTO_TEST_CASE(transfer)
370 {
371 	deployRegistrar();
372 	string name = "abcaoeguaoucaeoduceo";
373 	RegistrarInterface registrar(*this);
374 	registrar.reserve(name);
375 	registrar.setContent(name, h256(u256(123)));
376 	registrar.transfer(name, h160(555));
377 	BOOST_CHECK_EQUAL(registrar.owner(name), h160(555));
378 	BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(123)));
379 }
380 
BOOST_AUTO_TEST_CASE(disown)381 BOOST_AUTO_TEST_CASE(disown)
382 {
383 	deployRegistrar();
384 	string name = "abcaoeguaoucaeoduceo";
385 
386 	RegistrarInterface registrar(*this);
387 	registrar.reserve(name);
388 	registrar.setContent(name, h256(u256(123)));
389 	registrar.setAddress(name, h160(124), true);
390 	registrar.setSubRegistrar(name, h160(125));
391 	BOOST_CHECK_EQUAL(registrar.name(h160(124)), name);
392 
393 	// someone else tries disowning
394 	sendEther(account(1), u256(10) * ether);
395 	m_sender = account(1);
396 	registrar.disown(name);
397 	BOOST_CHECK_EQUAL(registrar.owner(name), account(0));
398 
399 	m_sender = account(0);
400 	registrar.disown(name);
401 	BOOST_CHECK_EQUAL(registrar.owner(name), h160());
402 	BOOST_CHECK_EQUAL(registrar.addr(name), h160());
403 	BOOST_CHECK_EQUAL(registrar.subRegistrar(name), h160());
404 	BOOST_CHECK_EQUAL(registrar.content(name), h256());
405 	BOOST_CHECK_EQUAL(registrar.name(h160(124)), "");
406 }
407 
BOOST_AUTO_TEST_CASE(auction_simple)408 BOOST_AUTO_TEST_CASE(auction_simple)
409 {
410 	deployRegistrar();
411 	string name = "x";
412 
413 	RegistrarInterface registrar(*this);
414 	// initiate auction
415 	registrar.setNextValue(8);
416 	registrar.reserve(name);
417 	BOOST_CHECK_EQUAL(registrar.owner(name), h160());
418 	// "wait" until auction end
419 
420 	m_evmcHost->tx_context.block_timestamp += m_biddingTime + 10;
421 	// trigger auction again
422 	registrar.reserve(name);
423 	BOOST_CHECK_EQUAL(registrar.owner(name), m_sender);
424 }
425 
BOOST_AUTO_TEST_CASE(auction_bidding)426 BOOST_AUTO_TEST_CASE(auction_bidding)
427 {
428 	deployRegistrar();
429 	string name = "x";
430 
431 	unsigned startTime = 0x776347e2;
432 	m_evmcHost->tx_context.block_timestamp = startTime;
433 
434 	RegistrarInterface registrar(*this);
435 	// initiate auction
436 	registrar.setNextValue(8);
437 	registrar.reserve(name);
438 	BOOST_CHECK_EQUAL(registrar.owner(name), h160());
439 	// overbid self
440 	m_evmcHost->tx_context.block_timestamp = startTime + m_biddingTime - 10;
441 	registrar.setNextValue(12);
442 	registrar.reserve(name);
443 	// another bid by someone else
444 	sendEther(account(1), 10 * ether);
445 	m_sender = account(1);
446 	m_evmcHost->tx_context.block_timestamp = startTime + 2 * m_biddingTime - 50;
447 	registrar.setNextValue(13);
448 	registrar.reserve(name);
449 	BOOST_CHECK_EQUAL(registrar.owner(name), h160());
450 	// end auction by first bidder (which is not highest) trying to overbid again (too late)
451 	m_sender = account(0);
452 	m_evmcHost->tx_context.block_timestamp = startTime + 4 * m_biddingTime;
453 	registrar.setNextValue(20);
454 	registrar.reserve(name);
455 	BOOST_CHECK_EQUAL(registrar.owner(name), account(1));
456 }
457 
458 BOOST_AUTO_TEST_SUITE_END()
459 
460 } // end namespaces
461