aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorchriseth <c@ethdev.com>2015-08-04 22:31:27 +0800
committerchriseth <c@ethdev.com>2015-08-05 23:19:48 +0800
commit4ff890f1dd174fde8c72c7b4dd802a712aa9cc9f (patch)
treed47b19a440bbe7c72a285e078178fdd2f7d09422
parentfbe4ce37a0df8f19ecc8978e1bf65a867bdf6492 (diff)
downloaddexon-solidity-4ff890f1dd174fde8c72c7b4dd802a712aa9cc9f.tar
dexon-solidity-4ff890f1dd174fde8c72c7b4dd802a712aa9cc9f.tar.gz
dexon-solidity-4ff890f1dd174fde8c72c7b4dd802a712aa9cc9f.tar.bz2
dexon-solidity-4ff890f1dd174fde8c72c7b4dd802a712aa9cc9f.tar.lz
dexon-solidity-4ff890f1dd174fde8c72c7b4dd802a712aa9cc9f.tar.xz
dexon-solidity-4ff890f1dd174fde8c72c7b4dd802a712aa9cc9f.tar.zst
dexon-solidity-4ff890f1dd174fde8c72c7b4dd802a712aa9cc9f.zip
Basic tests for auction registrar.
-rw-r--r--contracts/AuctionRegistrar.cpp418
-rw-r--r--contracts/FixedFeeRegistrar.cpp5
-rw-r--r--libsolidity/solidityExecutionFramework.h64
3 files changed, 487 insertions, 0 deletions
diff --git a/contracts/AuctionRegistrar.cpp b/contracts/AuctionRegistrar.cpp
new file mode 100644
index 00000000..3832e8e0
--- /dev/null
+++ b/contracts/AuctionRegistrar.cpp
@@ -0,0 +1,418 @@
+/*
+ This file is part of cpp-ethereum.
+
+ cpp-ethereum is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ cpp-ethereum is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
+*/
+/**
+ * @author Christian <c@ethdev.com>
+ * @date 2015
+ * Tests for a fixed fee registrar contract.
+ */
+
+#include <string>
+#include <tuple>
+#include <boost/test/unit_test.hpp>
+#include <libdevcore/Hash.h>
+#include <libethcore/ABI.h>
+#include <test/libsolidity/solidityExecutionFramework.h>
+
+using namespace std;
+
+namespace dev
+{
+namespace solidity
+{
+namespace test
+{
+
+namespace
+{
+
+static char const* registrarCode = R"DELIMITER(
+//sol
+
+contract NameRegister {
+ function addr(string _name) constant returns (address o_owner);
+ function name(address _owner) constant returns (string o_name);
+}
+
+contract Registrar is NameRegister {
+ event Changed(string indexed name);
+ event PrimaryChanged(string indexed name, address indexed addr);
+
+ function owner(string _name) constant returns (address o_owner);
+ function addr(string _name) constant returns (address o_address);
+ function subRegistrar(string _name) constant returns (address o_subRegistrar);
+ function content(string _name) constant returns (bytes32 o_content);
+
+ function name(address _owner) constant returns (string o_name);
+}
+
+contract AuctionSystem {
+ event AuctionEnded(string indexed _name, address _winner);
+ event NewBid(string indexed _name, address _bidder, uint _value);
+
+ /// Function that is called once an auction ends.
+ function onAuctionEnd(string _name) internal;
+
+ function bid(string _name, address _bidder, uint _value) internal {
+ var auction = m_auctions[_name];
+ if (auction.endDate > 0 && now > auction.endDate)
+ {
+ AuctionEnded(_name, auction.highestBidder);
+ onAuctionEnd(_name);
+ delete m_auctions[_name];
+ return;
+ }
+ if (msg.value > auction.highestBid)
+ {
+ // new bid on auction
+ auction.secondHighestBid = auction.highestBid;
+ auction.sumOfBids += _value;
+ auction.highestBid = _value;
+ auction.highestBidder = _bidder;
+ auction.endDate = now + c_biddingTime;
+
+ NewBid(_name, _bidder, _value);
+ }
+ }
+
+ uint constant c_biddingTime = 7 days;
+
+ struct Auction {
+ address highestBidder;
+ uint highestBid;
+ uint secondHighestBid;
+ uint sumOfBids;
+ uint endDate;
+ }
+ mapping(string => Auction) m_auctions;
+}
+
+contract GlobalRegistrar is Registrar, AuctionSystem {
+ struct Record {
+ address owner;
+ address primary;
+ address subRegistrar;
+ bytes32 content;
+ uint renewalDate;
+ }
+
+ uint constant c_renewalInterval = 1 years;
+ uint constant c_freeBytes = 12;
+
+ function Registrar() {
+ // TODO: Populate with hall-of-fame.
+ }
+
+ function() {
+ // prevent people from just sending funds to the registrar
+ __throw();
+ }
+
+ function onAuctionEnd(string _name) internal {
+ var auction = m_auctions[_name];
+ var record = m_toRecord[_name];
+ if (record.owner != 0)
+ record.owner.send(auction.sumOfBids - auction.highestBid / 100);
+ else
+ auction.highestBidder.send(auction.highestBid - auction.secondHighestBid);
+ record.renewalDate = now + c_renewalInterval;
+ record.owner = auction.highestBidder;
+ Changed(_name);
+ }
+
+ function reserve(string _name) external {
+ if (bytes(_name).length == 0)
+ __throw();
+ bool needAuction = requiresAuction(_name);
+ if (needAuction)
+ {
+ if (now < m_toRecord[_name].renewalDate)
+ __throw();
+ bid(_name, msg.sender, msg.value);
+ }
+ else
+ {
+ Record record = m_toRecord[_name];
+ if (record.owner != 0)
+ __throw();
+ m_toRecord[_name].owner = msg.sender;
+ Changed(_name);
+ }
+ }
+
+ function requiresAuction(string _name) internal returns (bool) {
+ return bytes(_name).length < c_freeBytes;
+ }
+
+ modifier onlyrecordowner(string _name) { if (m_toRecord[_name].owner == msg.sender) _ }
+
+ function transfer(string _name, address _newOwner) onlyrecordowner(_name) {
+ m_toRecord[_name].owner = _newOwner;
+ Changed(_name);
+ }
+
+ function disown(string _name) onlyrecordowner(_name) {
+ if (stringsEqual(m_toName[m_toRecord[_name].primary], _name))
+ {
+ PrimaryChanged(_name, m_toRecord[_name].primary);
+ m_toName[m_toRecord[_name].primary] = "";
+ }
+ delete m_toRecord[_name];
+ Changed(_name);
+ }
+
+ function setAddress(string _name, address _a, bool _primary) onlyrecordowner(_name) {
+ m_toRecord[_name].primary = _a;
+ if (_primary)
+ {
+ PrimaryChanged(_name, _a);
+ m_toName[_a] = _name;
+ }
+ Changed(_name);
+ }
+ function setSubRegistrar(string _name, address _registrar) onlyrecordowner(_name) {
+ m_toRecord[_name].subRegistrar = _registrar;
+ Changed(_name);
+ }
+ function setContent(string _name, bytes32 _content) onlyrecordowner(_name) {
+ m_toRecord[_name].content = _content;
+ Changed(_name);
+ }
+
+ function stringsEqual(string storage _a, string memory _b) internal returns (bool) {
+ bytes storage a = bytes(_a);
+ bytes memory b = bytes(_b);
+ if (a.length != b.length)
+ return false;
+ // @todo unroll this loop
+ for (uint i = 0; i < a.length; i ++)
+ if (a[i] != b[i])
+ return false;
+ return true;
+ }
+
+ function owner(string _name) constant returns (address) { return m_toRecord[_name].owner; }
+ function addr(string _name) constant returns (address) { return m_toRecord[_name].primary; }
+ function subRegistrar(string _name) constant returns (address) { return m_toRecord[_name].subRegistrar; }
+ function content(string _name) constant returns (bytes32) { return m_toRecord[_name].content; }
+ function name(address _addr) constant returns (string o_name) { return m_toName[_addr]; }
+
+ function __throw() internal {
+ // workaround until we have "throw"
+ uint[] x; x[1];
+ }
+
+ mapping (address => string) m_toName;
+ mapping (string => Record) m_toRecord;
+}
+)DELIMITER";
+
+static unique_ptr<bytes> s_compiledRegistrar;
+
+class AuctionRegistrarTestFramework: public ExecutionFramework
+{
+protected:
+ void deployRegistrar()
+ {
+ if (!s_compiledRegistrar)
+ {
+ m_optimize = true;
+ m_compiler.reset(false, m_addStandardSources);
+ m_compiler.addSource("", registrarCode);
+ ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed");
+ s_compiledRegistrar.reset(new bytes(m_compiler.getBytecode("GlobalRegistrar")));
+ }
+ sendMessage(*s_compiledRegistrar, true);
+ BOOST_REQUIRE(!m_output.empty());
+ }
+
+ using ContractInterface = ExecutionFramework::ContractInterface;
+ class RegistrarInterface: public ContractInterface
+ {
+ public:
+ RegistrarInterface(ExecutionFramework& _framework): ContractInterface(_framework) {}
+ void reserve(string const& _name)
+ {
+ callString("reserve", _name);
+ }
+ u160 owner(string const& _name)
+ {
+ return callStringReturnsAddress("owner", _name);
+ }
+ void setAddress(string const& _name, u160 const& _address, bool _primary)
+ {
+ callStringAddressBool("setAddress", _name, _address, _primary);
+ }
+ u160 addr(string const& _name)
+ {
+ return callStringReturnsAddress("addr", _name);
+ }
+ string name(u160 const& _addr)
+ {
+ return callAddressReturnsString("name", _addr);
+ }
+ void setSubRegistrar(string const& _name, u160 const& _address)
+ {
+ callStringAddress("setSubRegistrar", _name, _address);
+ }
+ u160 subRegistrar(string const& _name)
+ {
+ return callStringReturnsAddress("subRegistrar", _name);
+ }
+ void setContent(string const& _name, h256 const& _content)
+ {
+ callStringBytes32("setContent", _name, _content);
+ }
+ h256 content(string const& _name)
+ {
+ return callStringReturnsBytes32("content", _name);
+ }
+ void transfer(string const& _name, u160 const& _target)
+ {
+ return callStringAddress("transfer", _name, _target);
+ }
+ void disown(string const& _name)
+ {
+ return callString("disown", _name);
+ }
+ };
+};
+
+}
+
+/// This is a test suite that tests optimised code!
+BOOST_FIXTURE_TEST_SUITE(SolidityAuctionRegistrar, AuctionRegistrarTestFramework)
+
+BOOST_AUTO_TEST_CASE(creation)
+{
+ deployRegistrar();
+}
+
+BOOST_AUTO_TEST_CASE(reserve)
+{
+ // Test that reserving works for long strings
+ deployRegistrar();
+ vector<string> names{"abcabcabcabcabc", "defdefdefdefdef", "ghighighighighighighighighighighighighighighi"};
+ m_sender = Address(0x123);
+
+ RegistrarInterface registrar(*this);
+
+ // should not work
+ registrar.reserve("");
+ BOOST_CHECK_EQUAL(registrar.owner(""), u160(0));
+
+ for (auto const& name: names)
+ {
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(0x123));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(double_reserve_long)
+{
+ // Test that it is not possible to re-reserve from a different address.
+ deployRegistrar();
+ string name = "abcabcabcabcabcabcabcabcabcabca";
+ m_sender = Address(0x123);
+ RegistrarInterface registrar(*this);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(0x123));
+
+ m_sender = Address(0x124);
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(0x123));
+}
+
+BOOST_AUTO_TEST_CASE(properties)
+{
+ // Test setting and retrieving the various properties works.
+ deployRegistrar();
+ RegistrarInterface registrar(*this);
+ string names[] = {"abcaeouoeuaoeuaoeu", "defncboagufra,fui", "ghagpyajfbcuajouhaeoi"};
+ size_t addr = 0x9872543;
+ for (string const& name: names)
+ {
+ addr++;
+ size_t sender = addr + 10007;
+ m_sender = Address(sender);
+ // setting by sender works
+ registrar.reserve(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(sender));
+ registrar.setAddress(name, addr, true);
+ BOOST_CHECK_EQUAL(registrar.addr(name), u160(addr));
+ registrar.setSubRegistrar(name, addr + 20);
+ BOOST_CHECK_EQUAL(registrar.subRegistrar(name), u160(addr + 20));
+ registrar.setContent(name, h256(u256(addr + 90)));
+ BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90)));
+
+ // but not by someone else
+ m_sender = Address(h256(addr + 10007 - 1));
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(sender));
+ registrar.setAddress(name, addr + 1, true);
+ BOOST_CHECK_EQUAL(registrar.addr(name), u160(addr));
+ registrar.setSubRegistrar(name, addr + 20 + 1);
+ BOOST_CHECK_EQUAL(registrar.subRegistrar(name), u160(addr + 20));
+ registrar.setContent(name, h256(u256(addr + 90 + 1)));
+ BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(addr + 90)));
+ }
+}
+
+BOOST_AUTO_TEST_CASE(transfer)
+{
+ deployRegistrar();
+ string name = "abcaoeguaoucaeoduceo";
+ m_sender = Address(0x123);
+ RegistrarInterface registrar(*this);
+ registrar.reserve(name);
+ registrar.setContent(name, h256(u256(123)));
+ registrar.transfer(name, u160(555));
+ BOOST_CHECK_EQUAL(registrar.owner(name), u160(555));
+ BOOST_CHECK_EQUAL(registrar.content(name), h256(u256(123)));
+}
+
+BOOST_AUTO_TEST_CASE(disown)
+{
+ deployRegistrar();
+ string name = "abcaoeguaoucaeoduceo";
+ m_sender = Address(0x123);
+ RegistrarInterface registrar(*this);
+ registrar.reserve(name);
+ registrar.setContent(name, h256(u256(123)));
+ registrar.setAddress(name, u160(124), true);
+ registrar.setSubRegistrar(name, u160(125));
+
+ // someone else tries disowning
+ m_sender = Address(0x128);
+ registrar.disown(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0x123);
+
+ m_sender = Address(0x123);
+ registrar.disown(name);
+ BOOST_CHECK_EQUAL(registrar.owner(name), 0);
+ BOOST_CHECK_EQUAL(registrar.addr(name), 0);
+ BOOST_CHECK_EQUAL(registrar.subRegistrar(name), 0);
+ BOOST_CHECK_EQUAL(registrar.content(name), h256());
+}
+
+//@todo:
+// - reverse lookup
+// - actual auction
+
+BOOST_AUTO_TEST_SUITE_END()
+
+}
+}
+} // end namespaces
diff --git a/contracts/FixedFeeRegistrar.cpp b/contracts/FixedFeeRegistrar.cpp
index 26373499..ed2ecf0a 100644
--- a/contracts/FixedFeeRegistrar.cpp
+++ b/contracts/FixedFeeRegistrar.cpp
@@ -35,6 +35,9 @@ namespace solidity
namespace test
{
+namespace
+{
+
static char const* registrarCode = R"DELIMITER(
//sol FixedFeeRegistrar
// Simple global registrar with fixed-fee reservations.
@@ -130,6 +133,8 @@ protected:
u256 const m_fee = u256("69000000000000000000");
};
+}
+
/// This is a test suite that tests optimised code!
BOOST_FIXTURE_TEST_SUITE(SolidityFixedFeeRegistrar, RegistrarTestFramework)
diff --git a/libsolidity/solidityExecutionFramework.h b/libsolidity/solidityExecutionFramework.h
index e4c4087f..6a62c445 100644
--- a/libsolidity/solidityExecutionFramework.h
+++ b/libsolidity/solidityExecutionFramework.h
@@ -25,6 +25,7 @@
#include <string>
#include <tuple>
#include "../TestHelper.h"
+#include <libethcore/ABI.h>
#include <libethereum/State.h>
#include <libethereum/Executive.h>
#include <libsolidity/CompilerStack.h>
@@ -166,6 +167,69 @@ public:
return encodeArgs(u256(0x20), u256(_arg.size()), _arg);
}
+ class ContractInterface
+ {
+ public:
+ ContractInterface(ExecutionFramework& _framework): m_framework(_framework) {}
+
+ protected:
+ template <class... Args>
+ bytes const& call(std::string const& _sig, Args const&... _arguments)
+ {
+ auto const& ret = m_framework.callContractFunctionWithValue(_sig, m_nextValue, _arguments...);
+ m_nextValue = 0;
+ return ret;
+ }
+
+ void callString(std::string const& _name, std::string const& _arg)
+ {
+ BOOST_CHECK(call(_name + "(string)", u256(0x20), _arg.length(), _arg).empty());
+ }
+
+ void callStringAddress(std::string const& _name, std::string const& _arg1, u160 const& _arg2)
+ {
+ BOOST_CHECK(call(_name + "(string,address)", u256(0x40), _arg2, _arg1.length(), _arg1).empty());
+ }
+
+ void callStringAddressBool(std::string const& _name, std::string const& _arg1, u160 const& _arg2, bool _arg3)
+ {
+ BOOST_CHECK(call(_name + "(string,address,bool)", u256(0x60), _arg2, _arg3, _arg1.length(), _arg1).empty());
+ }
+
+ void callStringBytes32(std::string const& _name, std::string const& _arg1, h256 const& _arg2)
+ {
+ BOOST_CHECK(call(_name + "(string,bytes32)", u256(0x40), _arg2, _arg1.length(), _arg1).empty());
+ }
+
+ u160 callStringReturnsAddress(std::string const& _name, std::string const& _arg)
+ {
+ bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg);
+ BOOST_REQUIRE(ret.size() == 0x20);
+ BOOST_CHECK(std::count(ret.begin(), ret.begin() + 12, 0) == 12);
+ return eth::abiOut<u160>(ret);
+ }
+
+ std::string callAddressReturnsString(std::string const& _name, u160 const& _arg)
+ {
+ bytes const& ret = call(_name + "(address)", _arg);
+ BOOST_REQUIRE(ret.size() >= 0x20);
+ u256 len = eth::abiOut<u256>(ret);
+ BOOST_REQUIRE(ret.size() == (0x20 + len) / 32 * 32);
+ return std::string(ret.begin() + 0x20, ret.begin() + 0x20 + size_t(len));
+ }
+
+ h256 callStringReturnsBytes32(std::string const& _name, std::string const& _arg)
+ {
+ bytes const& ret = call(_name + "(string)", u256(0x20), _arg.length(), _arg);
+ BOOST_REQUIRE(ret.size() == 0x20);
+ return eth::abiOut<h256>(ret);
+ }
+
+ private:
+ u256 m_nextValue;
+ ExecutionFramework& m_framework;
+ };
+
private:
template <class CppFunction, class... Args>
auto callCppAndEncodeResult(CppFunction const& _cppFunction, Args const&... _arguments)