aboutsummaryrefslogtreecommitdiffstats
path: root/test/contracts
diff options
context:
space:
mode:
authorAlex Beregszaszi <alex@rtfs.hu>2018-11-13 22:34:37 +0800
committerAlex Beregszaszi <alex@rtfs.hu>2018-11-14 07:51:17 +0800
commit00cb5dbd43eb07b307258865dfa5abe72a3926a5 (patch)
tree447a1b529cd5540cb596817cca5da5728f3bebd6 /test/contracts
parent6961899cb2a0abd4d236a9a6f2657b4533720484 (diff)
downloaddexon-solidity-00cb5dbd43eb07b307258865dfa5abe72a3926a5.tar.gz
dexon-solidity-00cb5dbd43eb07b307258865dfa5abe72a3926a5.tar.zst
dexon-solidity-00cb5dbd43eb07b307258865dfa5abe72a3926a5.zip
Move LLL tests into a single directory
Diffstat (limited to 'test/contracts')
-rw-r--r--test/contracts/LLL_ENS.cpp507
-rw-r--r--test/contracts/LLL_ERC20.cpp656
2 files changed, 0 insertions, 1163 deletions
diff --git a/test/contracts/LLL_ENS.cpp b/test/contracts/LLL_ENS.cpp
deleted file mode 100644
index cfd6970c..00000000
--- a/test/contracts/LLL_ENS.cpp
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- This file is part of solidity.
-
- solidity 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.
-
- solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @author Ben Edgington <ben@benjaminion.xyz>
- * @date 2017
- * Tests for the deployed ENS Registry implementation written in LLL
- */
-
-#include <string>
-#include <boost/test/unit_test.hpp>
-#include <test/liblll/ExecutionFramework.h>
-#include <liblll/Compiler.h>
-
-#define ACCOUNT(n) h256(account(n), h256::AlignRight)
-
-using namespace std;
-using namespace dev::lll;
-
-namespace dev
-{
-namespace lll
-{
-namespace test
-{
-
-namespace
-{
-
-static char const* ensCode = R"DELIMITER(
-;;; ---------------------------------------------------------------------------
-;;; @title The Ethereum Name Service registry.
-;;; @author Daniel Ellison <daniel@syrinx.net>
-
-(seq
-
- ;; --------------------------------------------------------------------------
- ;; Constant definitions.
-
- ;; Memory layout.
- (def 'node-bytes 0x00)
- (def 'label-bytes 0x20)
- (def 'call-result 0x40)
-
- ;; Struct: Record
- (def 'resolver 0x00) ; address
- (def 'owner 0x20) ; address
- (def 'ttl 0x40) ; uint64
-
- ;; Precomputed function IDs.
- (def 'get-node-owner 0x02571be3) ; owner(bytes32)
- (def 'get-node-resolver 0x0178b8bf) ; resolver(bytes32)
- (def 'get-node-ttl 0x16a25cbd) ; ttl(bytes32)
- (def 'set-node-owner 0x5b0fc9c3) ; setOwner(bytes32,address)
- (def 'set-subnode-owner 0x06ab5923) ; setSubnodeOwner(bytes32,bytes32,address)
- (def 'set-node-resolver 0x1896f70a) ; setResolver(bytes32,address)
- (def 'set-node-ttl 0x14ab9038) ; setTTL(bytes32,uint64)
-
- ;; Jumping here causes an EVM error.
- (def 'invalid-location 0x02)
-
- ;; --------------------------------------------------------------------------
- ;; @notice Shifts the leftmost 4 bytes of a 32-byte number right by 28 bytes.
- ;; @param input A 32-byte number.
-
- (def 'shift-right (input)
- (div input (exp 2 224)))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Determines whether the supplied function ID matches a known
- ;; function hash and executes <code-body> if so.
- ;; @dev The function ID is in the leftmost four bytes of the call data.
- ;; @param function-hash The four-byte hash of a known function signature.
- ;; @param code-body The code to run in the case of a match.
-
- (def 'function (function-hash code-body)
- (when (= (shift-right (calldataload 0x00)) function-hash)
- code-body))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Calculates record location for the node and label passed in.
- ;; @param node The parent node.
- ;; @param label The hash of the subnode label.
-
- (def 'get-record (node label)
- (seq
- (mstore node-bytes node)
- (mstore label-bytes label)
- (sha3 node-bytes 64)))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Retrieves owner from node record.
- ;; @param node Get owner of this node.
-
- (def 'get-owner (node)
- (sload (+ node owner)))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Stores new owner in node record.
- ;; @param node Set owner of this node.
- ;; @param new-owner New owner of this node.
-
- (def 'set-owner (node new-owner)
- (sstore (+ node owner) new-owner))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Stores new subnode owner in node record.
- ;; @param node Set owner of this node.
- ;; @param label The hash of the label specifying the subnode.
- ;; @param new-owner New owner of the subnode.
-
- (def 'set-subowner (node label new-owner)
- (sstore (+ (get-record node label) owner) new-owner))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Retrieves resolver from node record.
- ;; @param node Get resolver of this node.
-
- (def 'get-resolver (node)
- (sload node))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Stores new resolver in node record.
- ;; @param node Set resolver of this node.
- ;; @param new-resolver New resolver for this node.
-
- (def 'set-resolver (node new-resolver)
- (sstore node new-resolver))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Retrieves TTL From node record.
- ;; @param node Get TTL of this node.
-
- (def 'get-ttl (node)
- (sload (+ node ttl)))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Stores new TTL in node record.
- ;; @param node Set TTL of this node.
- ;; @param new-resolver New TTL for this node.
-
- (def 'set-ttl (node new-ttl)
- (sstore (+ node ttl) new-ttl))
-
- ;; --------------------------------------------------------------------------
- ;; @notice Checks that the caller is the node owner.
- ;; @param node Check owner of this node.
-
- (def 'only-node-owner (node)
- (when (!= (caller) (get-owner node))
- (jump invalid-location)))
-
- ;; --------------------------------------------------------------------------
- ;; INIT
-
- ;; Set the owner of the root node (0x00) to the deploying account.
- (set-owner 0x00 (caller))
-
- ;; --------------------------------------------------------------------------
- ;; CODE
-
- (returnlll
- (seq
-
- ;; ----------------------------------------------------------------------
- ;; @notice Returns the address of the resolver for the specified node.
- ;; @dev Signature: resolver(bytes32)
- ;; @param node Return this node's resolver.
- ;; @return The associated resolver.
-
- (def 'node (calldataload 0x04))
-
- (function get-node-resolver
- (seq
-
- ;; Get the node's resolver and save it.
- (mstore call-result (get-resolver node))
-
- ;; Return result.
- (return call-result 32)))
-
- ;; ----------------------------------------------------------------------
- ;; @notice Returns the address that owns the specified node.
- ;; @dev Signature: owner(bytes32)
- ;; @param node Return this node's owner.
- ;; @return The associated address.
-
- (def 'node (calldataload 0x04))
-
- (function get-node-owner
- (seq
-
- ;; Get the node's owner and save it.
- (mstore call-result (get-owner node))
-
- ;; Return result.
- (return call-result 32)))
-
- ;; ----------------------------------------------------------------------
- ;; @notice Returns the TTL of a node and any records associated with it.
- ;; @dev Signature: ttl(bytes32)
- ;; @param node Return this node's TTL.
- ;; @return The node's TTL.
-
- (def 'node (calldataload 0x04))
-
- (function get-node-ttl
- (seq
-
- ;; Get the node's TTL and save it.
- (mstore call-result (get-ttl node))
-
- ;; Return result.
- (return call-result 32)))
-
- ;; ----------------------------------------------------------------------
- ;; @notice Transfers ownership of a node to a new address. May only be
- ;; called by the current owner of the node.
- ;; @dev Signature: setOwner(bytes32,address)
- ;; @param node The node to transfer ownership of.
- ;; @param new-owner The address of the new owner.
-
- (def 'node (calldataload 0x04))
- (def 'new-owner (calldataload 0x24))
-
- (function set-node-owner
- (seq (only-node-owner node)
-
- ;; Transfer ownership by storing passed-in address.
- (set-owner node new-owner)
-
- ;; Emit an event about the transfer.
- ;; Transfer(bytes32 indexed node, address owner);
- (mstore call-result new-owner)
- (log2 call-result 32
- (sha3 0x00 (lit 0x00 "Transfer(bytes32,address)")) node)
-
- ;; Nothing to return.
- (stop)))
-
- ;; ----------------------------------------------------------------------
- ;; @notice Transfers ownership of a subnode to a new address. May only be
- ;; called by the owner of the parent node.
- ;; @dev Signature: setSubnodeOwner(bytes32,bytes32,address)
- ;; @param node The parent node.
- ;; @param label The hash of the label specifying the subnode.
- ;; @param new-owner The address of the new owner.
-
- (def 'node (calldataload 0x04))
- (def 'label (calldataload 0x24))
- (def 'new-owner (calldataload 0x44))
-
- (function set-subnode-owner
- (seq (only-node-owner node)
-
- ;; Transfer ownership by storing passed-in address.
- (set-subowner node label new-owner)
-
- ;; Emit an event about the transfer.
- ;; NewOwner(bytes32 indexed node, bytes32 indexed label, address owner);
- (mstore call-result new-owner)
- (log3 call-result 32
- (sha3 0x00 (lit 0x00 "NewOwner(bytes32,bytes32,address)"))
- node label)
-
- ;; Nothing to return.
- (stop)))
-
- ;; ----------------------------------------------------------------------
- ;; @notice Sets the resolver address for the specified node.
- ;; @dev Signature: setResolver(bytes32,address)
- ;; @param node The node to update.
- ;; @param new-resolver The address of the resolver.
-
- (def 'node (calldataload 0x04))
- (def 'new-resolver (calldataload 0x24))
-
- (function set-node-resolver
- (seq (only-node-owner node)
-
- ;; Transfer ownership by storing passed-in address.
- (set-resolver node new-resolver)
-
- ;; Emit an event about the change of resolver.
- ;; NewResolver(bytes32 indexed node, address resolver);
- (mstore call-result new-resolver)
- (log2 call-result 32
- (sha3 0x00 (lit 0x00 "NewResolver(bytes32,address)")) node)
-
- ;; Nothing to return.
- (stop)))
-
- ;; ----------------------------------------------------------------------
- ;; @notice Sets the TTL for the specified node.
- ;; @dev Signature: setTTL(bytes32,uint64)
- ;; @param node The node to update.
- ;; @param ttl The TTL in seconds.
-
- (def 'node (calldataload 0x04))
- (def 'new-ttl (calldataload 0x24))
-
- (function set-node-ttl
- (seq (only-node-owner node)
-
- ;; Set new TTL by storing passed-in time.
- (set-ttl node new-ttl)
-
- ;; Emit an event about the change of TTL.
- ;; NewTTL(bytes32 indexed node, uint64 ttl);
- (mstore call-result new-ttl)
- (log2 call-result 32
- (sha3 0x00 (lit 0x00 "NewTTL(bytes32,uint64)")) node)
-
- ;; Nothing to return.
- (stop)))
-
- ;; ----------------------------------------------------------------------
- ;; @notice Fallback: No functions matched the function ID provided.
-
- (jump invalid-location)))
-
-)
-)DELIMITER";
-
-static unique_ptr<bytes> s_compiledEns;
-
-class LLLENSTestFramework: public LLLExecutionFramework
-{
-protected:
- void deployEns()
- {
- if (!s_compiledEns)
- {
- vector<string> errors;
- s_compiledEns.reset(new bytes(compileLLL(ensCode, dev::test::Options::get().evmVersion(), dev::test::Options::get().optimize, &errors)));
- BOOST_REQUIRE(errors.empty());
- }
- sendMessage(*s_compiledEns, true);
- BOOST_REQUIRE(m_transactionSuccessful);
- BOOST_REQUIRE(!m_output.empty());
- }
-
-};
-
-}
-
-// Test suite for the deployed ENS Registry implementation written in LLL
-BOOST_FIXTURE_TEST_SUITE(LLLENS, LLLENSTestFramework)
-
-BOOST_AUTO_TEST_CASE(creation)
-{
- deployEns();
-
- // Root node 0x00 should initially be owned by the deploying account, account(0).
- BOOST_CHECK(callContractFunction("owner(bytes32)", 0x00) == encodeArgs(ACCOUNT(0)));
-}
-
-BOOST_AUTO_TEST_CASE(transfer_ownership)
-{
- deployEns();
-
- // Transfer ownership of root node from account(0) to account(1).
- BOOST_REQUIRE(callContractFunction("setOwner(bytes32,address)", 0x00, ACCOUNT(1)) == encodeArgs());
-
- // Check that an event was raised and contents are correct.
- BOOST_REQUIRE(m_logs.size() == 1);
- BOOST_CHECK(m_logs[0].data == encodeArgs(ACCOUNT(1)));
- BOOST_REQUIRE(m_logs[0].topics.size() == 2);
- BOOST_CHECK(m_logs[0].topics[0] == keccak256(string("Transfer(bytes32,address)")));
- BOOST_CHECK(m_logs[0].topics[1] == u256(0x00));
-
- // Verify that owner of 0x00 is now account(1).
- BOOST_CHECK(callContractFunction("owner(bytes32)", 0x00) == encodeArgs(ACCOUNT(1)));
-}
-
-BOOST_AUTO_TEST_CASE(transfer_ownership_fail)
-{
- deployEns();
-
- // Try to steal ownership of node 0x01
- BOOST_REQUIRE(callContractFunction("setOwner(bytes32,address)", 0x01, ACCOUNT(0)) == encodeArgs());
-
- // Verify that owner of 0x01 remains the default zero address
- BOOST_CHECK(callContractFunction("owner(bytes32)", 0x01) == encodeArgs(0));
-}
-
-BOOST_AUTO_TEST_CASE(set_resolver)
-{
- deployEns();
-
- // Set resolver of root node to account(1).
- BOOST_REQUIRE(callContractFunction("setResolver(bytes32,address)", 0x00, ACCOUNT(1)) == encodeArgs());
-
- // Check that an event was raised and contents are correct.
- BOOST_REQUIRE(m_logs.size() == 1);
- BOOST_CHECK(m_logs[0].data == encodeArgs(ACCOUNT(1)));
- BOOST_REQUIRE(m_logs[0].topics.size() == 2);
- BOOST_CHECK(m_logs[0].topics[0] == keccak256(string("NewResolver(bytes32,address)")));
- BOOST_CHECK(m_logs[0].topics[1] == u256(0x00));
-
- // Verify that the resolver is changed to account(1).
- BOOST_CHECK(callContractFunction("resolver(bytes32)", 0x00) == encodeArgs(ACCOUNT(1)));
-}
-
-BOOST_AUTO_TEST_CASE(set_resolver_fail)
-{
- deployEns();
-
- // Try to set resolver of node 0x01, which is not owned by account(0).
- BOOST_REQUIRE(callContractFunction("setResolver(bytes32,address)", 0x01, ACCOUNT(0)) == encodeArgs());
-
- // Verify that the resolver of 0x01 remains default zero address.
- BOOST_CHECK(callContractFunction("resolver(bytes32)", 0x01) == encodeArgs(0));
-}
-
-BOOST_AUTO_TEST_CASE(set_ttl)
-{
- deployEns();
-
- // Set ttl of root node to 3600.
- BOOST_REQUIRE(callContractFunction("setTTL(bytes32,uint64)", 0x00, 3600) == encodeArgs());
-
- // Check that an event was raised and contents are correct.
- BOOST_REQUIRE(m_logs.size() == 1);
- BOOST_CHECK(m_logs[0].data == encodeArgs(3600));
- BOOST_REQUIRE(m_logs[0].topics.size() == 2);
- BOOST_CHECK(m_logs[0].topics[0] == keccak256(string("NewTTL(bytes32,uint64)")));
- BOOST_CHECK(m_logs[0].topics[1] == u256(0x00));
-
- // Verify that the TTL has been set.
- BOOST_CHECK(callContractFunction("ttl(bytes32)", 0x00) == encodeArgs(3600));
-}
-
-BOOST_AUTO_TEST_CASE(set_ttl_fail)
-{
- deployEns();
-
- // Try to set TTL of node 0x01, which is not owned by account(0).
- BOOST_REQUIRE(callContractFunction("setTTL(bytes32,uint64)", 0x01, 3600) == encodeArgs());
-
- // Verify that the TTL of node 0x01 has not changed from the default.
- BOOST_CHECK(callContractFunction("ttl(bytes32)", 0x01) == encodeArgs(0));
-}
-
-BOOST_AUTO_TEST_CASE(create_subnode)
-{
- deployEns();
-
- // Set ownership of "eth" sub-node to account(1)
- BOOST_REQUIRE(callContractFunction("setSubnodeOwner(bytes32,bytes32,address)", 0x00, keccak256(string("eth")), ACCOUNT(1)) == encodeArgs());
-
- // Check that an event was raised and contents are correct.
- BOOST_REQUIRE(m_logs.size() == 1);
- BOOST_CHECK(m_logs[0].data == encodeArgs(ACCOUNT(1)));
- BOOST_REQUIRE(m_logs[0].topics.size() == 3);
- BOOST_CHECK(m_logs[0].topics[0] == keccak256(string("NewOwner(bytes32,bytes32,address)")));
- BOOST_CHECK(m_logs[0].topics[1] == u256(0x00));
- BOOST_CHECK(m_logs[0].topics[2] == keccak256(string("eth")));
-
- // Verify that the sub-node owner is now account(1).
- u256 namehash = keccak256(h256(0x00).asBytes() + keccak256("eth").asBytes());
- BOOST_CHECK(callContractFunction("owner(bytes32)", namehash) == encodeArgs(ACCOUNT(1)));
-}
-
-BOOST_AUTO_TEST_CASE(create_subnode_fail)
-{
- deployEns();
-
- // Send account(1) some ether for gas.
- sendEther(account(1), 1000 * ether);
- BOOST_REQUIRE(balanceAt(account(1)) >= 1000 * ether);
-
- // account(1) tries to set ownership of the "eth" sub-node.
- m_sender = account(1);
- BOOST_REQUIRE(callContractFunction("setSubnodeOwner(bytes32,bytes32,address)", 0x00, keccak256(string("eth")), ACCOUNT(1)) == encodeArgs());
-
- // Verify that the sub-node owner remains at default zero address.
- u256 namehash = keccak256(h256(0x00).asBytes() + keccak256("eth").asBytes());
- BOOST_CHECK(callContractFunction("owner(bytes32)", namehash) == encodeArgs(0));
-}
-
-BOOST_AUTO_TEST_CASE(fallback)
-{
- deployEns();
-
- // Call fallback - should just abort via jump to invalid location.
- BOOST_CHECK(callFallback() == encodeArgs());
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-}
-}
-} // end namespaces
diff --git a/test/contracts/LLL_ERC20.cpp b/test/contracts/LLL_ERC20.cpp
deleted file mode 100644
index 6c6762dd..00000000
--- a/test/contracts/LLL_ERC20.cpp
+++ /dev/null
@@ -1,656 +0,0 @@
-/*
- This file is part of solidity.
-
- solidity 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.
-
- solidity 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 solidity. If not, see <http://www.gnu.org/licenses/>.
-*/
-/**
- * @author Ben Edgington <ben@benjaminion.xyz>
- * @date 2017
- * Tests for an ERC20 token implementation written in LLL
- */
-
-#include <string>
-#include <boost/test/unit_test.hpp>
-#include <test/liblll/ExecutionFramework.h>
-#include <liblll/Compiler.h>
-
-#define TOKENSUPPLY 100000
-#define TOKENDECIMALS 2
-#define TOKENSYMBOL "BEN"
-#define TOKENNAME "Ben Token"
-#define ACCOUNT(n) h256(account(n), h256::AlignRight)
-#define SUCCESS encodeArgs(1)
-
-using namespace std;
-using namespace dev::lll;
-
-namespace dev
-{
-namespace lll
-{
-namespace test
-{
-
-namespace
-{
-
-static char const* erc20Code = R"DELIMITER(
-(seq
-
- ;; --------------------------------------------------------------------------
- ;; CONSTANTS
-
- ;; Token parameters.
- ;; 0x40 is a "magic number" - the text of the string is placed here
- ;; when returning the string to the caller. See return-string below.
- (def 'token-name-string (lit 0x40 "Ben Token"))
- (def 'token-symbol-string (lit 0x40 "BEN"))
- (def 'token-decimals 2)
- (def 'token-supply 100000) ; 1000.00 total tokens
-
- ;; Booleans
- (def 'false 0)
- (def 'true 1)
-
- ;; Memory layout.
- (def 'mem-ret 0x00) ; Fixed due to compiler macro for return.
- (def 'mem-func 0x00) ; No conflict with mem-ret, so re-use.
- (def 'mem-keccak 0x00) ; No conflict with mem-func or mem-ret, so re-use.
- (def 'scratch0 0x20)
- (def 'scratch1 0x40)
-
- ;; Precomputed function IDs.
- (def 'get-name 0x06fdde03) ; name()
- (def 'get-symbol 0x95d89b41) ; symbol()
- (def 'get-decimals 0x313ce567) ; decimals()
- (def 'get-total-supply 0x18160ddd) ; totalSupply()
- (def 'get-balance-of 0x70a08231) ; balanceOf(address)
- (def 'transfer 0xa9059cbb) ; transfer(address,uint256)
- (def 'transfer-from 0x23b872dd) ; transferFrom(address,address,uint256)
- (def 'approve 0x095ea7b3) ; approve(address,uint256)
- (def 'get-allowance 0xdd62ed3e) ; allowance(address,address)
-
- ;; Event IDs
- (def 'transfer-event-id ; Transfer(address,address,uint256)
- 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef)
-
- (def 'approval-event-id ; Approval(address,address,uint256)
- 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925)
-
- ;; --------------------------------------------------------------------------
- ;; UTILITIES
-
- ;; --------------------------------------------------------------------------
- ;; The following define the key data-structures:
- ;; - balance(addr) => value
- ;; - allowance(addr,addr) => value
-
- ;; Balances are stored at s[owner_addr].
- (def 'balance (address) address)
-
- ;; Allowances are stored at s[owner_addr + keccak256(spender_addr)]
- ;; We use a crypto function here to avoid any situation where
- ;; approve(me, spender) can be abused to do approve(target, me).
- (def 'allowance (owner spender)
- (seq
- (mstore mem-keccak spender)
- (keccak256 mem-keccak 0x20)))
-
- ;; --------------------------------------------------------------------------
- ;; For convenience we have macros to refer to function arguments
-
- (def 'arg1 (calldataload 0x04))
- (def 'arg2 (calldataload 0x24))
- (def 'arg3 (calldataload 0x44))
-
- ;; --------------------------------------------------------------------------
- ;; Revert is a soft return that does not consume the remaining gas.
- ;; We use it when rejecting invalid user input.
- ;;
- ;; Note: The REVERT opcode will be implemented in Metropolis (EIP 140).
- ;; Meanwhile it just causes an invalid instruction exception (similar
- ;; to a "throw" in Solidity). When fully implemented, Revert could be
- ;; use to return error codes, or even messages.
-
- (def 'revert () (revert 0 0))
-
- ;; --------------------------------------------------------------------------
- ;; Macro for returning string names.
- ;; Compliant with the ABI format for strings.
-
- (def 'return-string (string-literal)
- (seq
- (mstore 0x00 0x20) ; Points to our string's memory location
- (mstore 0x20 string-literal) ; Length. String itself is copied to 0x40.
- (return 0x00 (& (+ (mload 0x20) 0x5f) (~ 0x1f)))))
- ; Round return up to 32 byte boundary
-
- ;; --------------------------------------------------------------------------
- ;; Convenience macro for raising Events
-
- (def 'event3 (id addr1 addr2 value)
- (seq
- (mstore scratch0 value)
- (log3 scratch0 0x20 id addr1 addr2)))
-
- ;; --------------------------------------------------------------------------
- ;; Determines whether the stored function ID matches a known
- ;; function hash and executes <code-body> if so.
- ;; @param function-hash The four-byte hash of a known function signature.
- ;; @param code-body The code to run in the case of a match.
-
- (def 'function (function-hash code-body)
- (when (= (mload mem-func) function-hash)
- code-body))
-
- ;; --------------------------------------------------------------------------
- ;; Gets the function ID and stores it in memory for reference.
- ;; The function ID is in the leftmost four bytes of the call data.
-
- (def 'uses-functions
- (mstore
- mem-func
- (shr (calldataload 0x00) 224)))
-
- ;; --------------------------------------------------------------------------
- ;; GUARDS
-
- ;; --------------------------------------------------------------------------
- ;; Checks that ensure that each function is called with the right
- ;; number of arguments. For one thing this addresses the "ERC20
- ;; short address attack". For another, it stops me making
- ;; mistakes while testing. We use these only on the non-constant functions.
-
- (def 'has-one-arg (unless (= 0x24 (calldatasize)) (revert)))
- (def 'has-two-args (unless (= 0x44 (calldatasize)) (revert)))
- (def 'has-three-args (unless (= 0x64 (calldatasize)) (revert)))
-
- ;; --------------------------------------------------------------------------
- ;; Check that addresses have only 160 bits and revert if not.
- ;; We use these input type-checks on the non-constant functions.
-
- (def 'is-address (addr)
- (when
- (shr addr 160)
- (revert)))
-
- ;; --------------------------------------------------------------------------
- ;; Check that transfer values are smaller than total supply and
- ;; revert if not. This should effectively exclude negative values.
-
- (def 'is-value (value)
- (when (> value token-supply) (revert)))
-
- ;; --------------------------------------------------------------------------
- ;; Will revert if sent any Ether. We use the macro immediately so as
- ;; to abort if sent any Ether during contract deployment.
-
- (def 'not-payable
- (when (callvalue) (revert)))
-
- not-payable
-
- ;; --------------------------------------------------------------------------
- ;; INITIALISATION
- ;;
- ;; Assign all tokens initially to the owner of the contract.
-
- (sstore (balance (caller)) token-supply)
-
- ;; --------------------------------------------------------------------------
- ;; CONTRACT CODE
-
- (returnlll
- (seq not-payable uses-functions
-
- ;; ----------------------------------------------------------------------
- ;; Getter for the name of the token.
- ;; @abi name() constant returns (string)
- ;; @return The token name as a string.
-
- (function get-name
- (return-string token-name-string))
-
- ;; ----------------------------------------------------------------------
- ;; Getter for the symbol of the token.
- ;; @abi symbol() constant returns (string)
- ;; @return The token symbol as a string.
-
- (function get-symbol
- (return-string token-symbol-string))
-
- ;; ----------------------------------------------------------------------
- ;; Getter for the number of decimals assigned to the token.
- ;; @abi decimals() constant returns (uint256)
- ;; @return The token decimals.
-
- (function get-decimals
- (return token-decimals))
-
- ;; ----------------------------------------------------------------------
- ;; Getter for the total token supply.
- ;; @abi totalSupply() constant returns (uint256)
- ;; @return The token supply.
-
- (function get-total-supply
- (return token-supply))
-
- ;; ----------------------------------------------------------------------
- ;; Returns the account balance of another account.
- ;; @abi balanceOf(address) constant returns (uint256)
- ;; @param owner The address of the account's owner.
- ;; @return The account balance.
-
- (function get-balance-of
- (seq
-
- (def 'owner arg1)
-
- (return (sload (balance owner)))))
-
- ;; ----------------------------------------------------------------------
- ;; Transfers _value amount of tokens to address _to. The command
- ;; should throw if the _from account balance has not enough
- ;; tokens to spend.
- ;; @abi transfer(address, uint256) returns (bool)
- ;; @param to The account to receive the tokens.
- ;; @param value The quantity of tokens to transfer.
- ;; @return Success (true). Other outcomes result in a Revert.
-
- (function transfer
- (seq has-two-args (is-address arg1) (is-value arg2)
-
- (def 'to arg1)
- (def 'value arg2)
-
- (when value ; value == 0 is a no-op
- (seq
-
- ;; The caller's balance. Save in memory for efficiency.
- (mstore scratch0 (sload (balance (caller))))
-
- ;; Revert if the caller's balance is not sufficient.
- (when (> value (mload scratch0))
- (revert))
-
- ;; Make the transfer
- ;; It would be good to check invariants (sum of balances).
- (sstore (balance (caller)) (- (mload scratch0) value))
- (sstore (balance to) (+ (sload (balance to)) value))
-
- ;; Event - Transfer(address,address,uint256)
- (event3 transfer-event-id (caller) to value)))
-
- (return true)))
-
- ;; ----------------------------------------------------------------------
- ;; Send _value amount of tokens from address _from to address _to
- ;; @abi transferFrom(address,address,uint256) returns (bool)
- ;; @param from The account to send the tokens from.
- ;; @param to The account to receive the tokens.
- ;; @param value The quantity of tokens to transfer.
- ;; @return Success (true). Other outcomes result in a Revert.
-
- (function transfer-from
- (seq has-three-args (is-address arg1) (is-address arg2) (is-value arg3)
-
- (def 'from arg1)
- (def 'to arg2)
- (def 'value arg3)
-
- (when value ; value == 0 is a no-op
-
- (seq
-
- ;; Save data to memory for efficiency.
- (mstore scratch0 (sload (balance from)))
- (mstore scratch1 (sload (allowance from (caller))))
-
- ;; Revert if not enough funds, or not enough approved.
- (when
- (||
- (> value (mload scratch0))
- (> value (mload scratch1)))
- (revert))
-
- ;; Make the transfer and update allowance.
- (sstore (balance from) (- (mload scratch0) value))
- (sstore (balance to) (+ (sload (balance to)) value))
- (sstore (allowance from (caller)) (- (mload scratch1) value))
-
- ;; Event - Transfer(address,address,uint256)
- (event3 transfer-event-id from to value)))
-
- (return true)))
-
- ;; ----------------------------------------------------------------------
- ;; Allows _spender to withdraw from your account multiple times,
- ;; up to the _value amount. If this function is called again it
- ;; overwrites the current allowance with _value.
- ;; @abi approve(address,uint256) returns (bool)
- ;; @param spender The withdrawing account having its limit set.
- ;; @param value The maximum allowed amount.
- ;; @return Success (true). Other outcomes result in a Revert.
-
- (function approve
- (seq has-two-args (is-address arg1) (is-value arg2)
-
- (def 'spender arg1)
- (def 'value arg2)
-
- ;; Force users set the allowance to 0 before setting it to
- ;; another value for the same spender. Prevents this attack:
- ;; https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM
- (when
- (&& value (sload (allowance (caller) spender)))
- (revert))
-
- (sstore (allowance (caller) spender) value)
-
- ;; Event - Approval(address,address,uint256)
- (event3 approval-event-id (caller) spender value)
-
- (return true)))
-
- ;; ----------------------------------------------------------------------
- ;; Returns the amount which _spender is still allowed to withdraw
- ;; from _owner.
- ;; @abi allowance(address,address) constant returns (uint256)
- ;; @param owner The owning account.
- ;; @param spender The withdrawing account.
- ;; @return The allowed amount remaining.
-
- (function get-allowance
- (seq
-
- (def 'owner arg1)
- (def 'spender arg2)
-
- (return (sload (allowance owner spender)))))
-
- ;; ----------------------------------------------------------------------
- ;; Fallback: No functions matched the function ID provided.
-
- (revert)))
- )
-)DELIMITER";
-
-static unique_ptr<bytes> s_compiledErc20;
-
-class LLLERC20TestFramework: public LLLExecutionFramework
-{
-protected:
- void deployErc20()
- {
- if (!s_compiledErc20)
- {
- vector<string> errors;
- s_compiledErc20.reset(new bytes(compileLLL(erc20Code, dev::test::Options::get().evmVersion(), dev::test::Options::get().optimize, &errors)));
- BOOST_REQUIRE(errors.empty());
- }
- sendMessage(*s_compiledErc20, true);
- BOOST_REQUIRE(m_transactionSuccessful);
- BOOST_REQUIRE(!m_output.empty());
- }
-
-};
-
-}
-
-// Test suite for an ERC20 contract written in LLL.
-BOOST_FIXTURE_TEST_SUITE(LLLERC20, LLLERC20TestFramework)
-
-BOOST_AUTO_TEST_CASE(creation)
-{
- deployErc20();
-
- // All tokens are initially assigned to the contract creator.
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY));
-}
-
-BOOST_AUTO_TEST_CASE(constants)
-{
- deployErc20();
-
- BOOST_CHECK(callContractFunction("totalSupply()") == encodeArgs(TOKENSUPPLY));
- BOOST_CHECK(callContractFunction("decimals()") == encodeArgs(TOKENDECIMALS));
- BOOST_CHECK(callContractFunction("symbol()") == encodeDyn(string(TOKENSYMBOL)));
- BOOST_CHECK(callContractFunction("name()") == encodeDyn(string(TOKENNAME)));
-}
-
-BOOST_AUTO_TEST_CASE(send_value)
-{
- deployErc20();
-
- // Send value to the contract. Should always fail.
- m_sender = account(0);
- auto contractBalance = balanceAt(m_contractAddress);
-
- // Fallback: check value is not transferred.
- BOOST_CHECK(callFallbackWithValue(42) != SUCCESS);
- BOOST_CHECK(balanceAt(m_contractAddress) == contractBalance);
-
- // Transfer: check nothing happened.
- BOOST_CHECK(callContractFunctionWithValue("transfer(address,uint256)", ACCOUNT(1), 100, 42) != SUCCESS);
- BOOST_CHECK(balanceAt(m_contractAddress) == contractBalance);
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(0));
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY));
-}
-
-BOOST_AUTO_TEST_CASE(transfer)
-{
- deployErc20();
-
- // Transfer 100 tokens from account(0) to account(1).
- int transfer = 100;
- m_sender = account(0);
- BOOST_CHECK(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) == SUCCESS);
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(transfer));
-}
-
-BOOST_AUTO_TEST_CASE(transfer_from)
-{
- deployErc20();
-
- // Approve account(1) to transfer up to 1000 tokens from account(0).
- int allow = 1000;
- m_sender = account(0);
- BOOST_REQUIRE(callContractFunction("approve(address,uint256)", ACCOUNT(1), u256(allow)) == SUCCESS);
- BOOST_REQUIRE(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(allow));
-
- // Send account(1) some ether for gas.
- sendEther(account(1), 1000 * ether);
- BOOST_REQUIRE(balanceAt(account(1)) >= 1000 * ether);
-
- // Transfer 300 tokens from account(0) to account(2); check that the allowance decreases.
- int transfer = 300;
- m_sender = account(1);
- BOOST_REQUIRE(callContractFunction("transferFrom(address,address,uint256)", ACCOUNT(0), ACCOUNT(2), u256(transfer)) == SUCCESS);
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(2)) == encodeArgs(transfer));
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
- BOOST_CHECK(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(allow - transfer));
-}
-
-BOOST_AUTO_TEST_CASE(transfer_event)
-{
- deployErc20();
-
- // Transfer 1000 tokens from account(0) to account(1).
- int transfer = 1000;
- m_sender = account(0);
- BOOST_REQUIRE(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) == SUCCESS);
-
- // Check that a Transfer event was recorded and contents are correct.
- BOOST_REQUIRE(m_logs.size() == 1);
- BOOST_CHECK(m_logs[0].data == encodeArgs(transfer));
- BOOST_REQUIRE(m_logs[0].topics.size() == 3);
- BOOST_CHECK(m_logs[0].topics[0] == keccak256(string("Transfer(address,address,uint256)")));
- BOOST_CHECK(m_logs[0].topics[1] == ACCOUNT(0));
- BOOST_CHECK(m_logs[0].topics[2] == ACCOUNT(1));
-}
-
-BOOST_AUTO_TEST_CASE(transfer_zero_no_event)
-{
- deployErc20();
-
- // Transfer 0 tokens from account(0) to account(1). This is a no-op.
- int transfer = 0;
- m_sender = account(0);
- BOOST_REQUIRE(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) == SUCCESS);
-
- // Check that no Event was recorded.
- BOOST_CHECK(m_logs.size() == 0);
-
- // Check that balances have not changed.
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(transfer));
-}
-
-BOOST_AUTO_TEST_CASE(approval_and_transfer_events)
-{
- deployErc20();
-
- // Approve account(1) to transfer up to 10000 tokens from account(0).
- int allow = 10000;
- m_sender = account(0);
- BOOST_REQUIRE(callContractFunction("approve(address,uint256)", ACCOUNT(1), u256(allow)) == SUCCESS);
-
- // Check that an Approval event was recorded and contents are correct.
- BOOST_REQUIRE(m_logs.size() == 1);
- BOOST_CHECK(m_logs[0].data == encodeArgs(allow));
- BOOST_REQUIRE(m_logs[0].topics.size() == 3);
- BOOST_CHECK(m_logs[0].topics[0] == keccak256(string("Approval(address,address,uint256)")));
- BOOST_CHECK(m_logs[0].topics[1] == ACCOUNT(0));
- BOOST_CHECK(m_logs[0].topics[2] == ACCOUNT(1));
-
- // Send account(1) some ether for gas.
- sendEther(account(1), 1000 * ether);
- BOOST_REQUIRE(balanceAt(account(1)) >= 1000 * ether);
-
- // Transfer 3000 tokens from account(0) to account(2); check that the allowance decreases.
- int transfer = 3000;
- m_sender = account(1);
- BOOST_REQUIRE(callContractFunction("transferFrom(address,address,uint256)", ACCOUNT(0), ACCOUNT(2), u256(transfer)) == SUCCESS);
-
- // Check that a Transfer event was recorded and contents are correct.
- BOOST_REQUIRE(m_logs.size() == 1);
- BOOST_CHECK(m_logs[0].data == encodeArgs(transfer));
- BOOST_REQUIRE(m_logs[0].topics.size() == 3);
- BOOST_CHECK(m_logs[0].topics[0] == keccak256(string("Transfer(address,address,uint256)")));
- BOOST_CHECK(m_logs[0].topics[1] == ACCOUNT(0));
- BOOST_CHECK(m_logs[0].topics[2] == ACCOUNT(2));
-}
-
-BOOST_AUTO_TEST_CASE(invalid_transfer_1)
-{
- deployErc20();
-
- // Transfer more than the total supply; ensure nothing changes.
- int transfer = TOKENSUPPLY + 1;
- m_sender = account(0);
- BOOST_CHECK(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) != SUCCESS);
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY));
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(0));
-}
-
-BOOST_AUTO_TEST_CASE(invalid_transfer_2)
-{
- deployErc20();
-
- // Separate transfers that together exceed initial balance.
- int transfer = 1 + TOKENSUPPLY / 2;
- m_sender = account(0);
-
- // First transfer should succeed.
- BOOST_REQUIRE(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) == SUCCESS);
- BOOST_REQUIRE(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
- BOOST_REQUIRE(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(transfer));
-
- // Second transfer should fail.
- BOOST_CHECK(callContractFunction("transfer(address,uint256)", ACCOUNT(1), u256(transfer)) != SUCCESS);
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY - transfer));
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(1)) == encodeArgs(transfer));
-}
-
-BOOST_AUTO_TEST_CASE(invalid_transfer_from)
-{
- deployErc20();
-
- // TransferFrom without approval.
- int transfer = 300;
-
- // Send account(1) some ether for gas.
- m_sender = account(0);
- sendEther(account(1), 1000 * ether);
- BOOST_REQUIRE(balanceAt(account(1)) >= 1000 * ether);
-
- // Try the transfer; ensure nothing changes.
- m_sender = account(1);
- BOOST_CHECK(callContractFunction("transferFrom(address,address,uint256)", ACCOUNT(0), ACCOUNT(2), u256(transfer)) != SUCCESS);
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(2)) == encodeArgs(0));
- BOOST_CHECK(callContractFunction("balanceOf(address)", ACCOUNT(0)) == encodeArgs(TOKENSUPPLY));
- BOOST_CHECK(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(0));
-}
-
-BOOST_AUTO_TEST_CASE(invalid_reapprove)
-{
- deployErc20();
-
- m_sender = account(0);
-
- // Approve account(1) to transfer up to 1000 tokens from account(0).
- int allow1 = 1000;
- BOOST_REQUIRE(callContractFunction("approve(address,uint256)", ACCOUNT(1), u256(allow1)) == SUCCESS);
- BOOST_REQUIRE(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(allow1));
-
- // Now approve account(1) to transfer up to 500 tokens from account(0).
- // Should fail (we need to reset allowance to 0 first).
- int allow2 = 500;
- BOOST_CHECK(callContractFunction("approve(address,uint256)", ACCOUNT(1), u256(allow2)) != SUCCESS);
- BOOST_CHECK(callContractFunction("allowance(address,address)", ACCOUNT(0), ACCOUNT(1)) == encodeArgs(allow1));
-}
-
-BOOST_AUTO_TEST_CASE(bad_data)
-{
- deployErc20();
-
- m_sender = account(0);
-
- // Correct data: transfer(address _to, 1).
- sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000000123456789a123456789a123456789a123456789a") + encodeArgs(1), false, 0);
- BOOST_CHECK(m_transactionSuccessful);
- BOOST_CHECK(m_output == SUCCESS);
-
- // Too little data (address is truncated by one byte).
- sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000000123456789a123456789a123456789a12345678") + encodeArgs(1), false, 0);
- BOOST_CHECK(!m_transactionSuccessful);
- BOOST_CHECK(m_output != SUCCESS);
-
- // Too much data (address is extended with a zero byte).
- sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000000123456789a123456789a123456789a123456789a00") + encodeArgs(1), false, 0);
- BOOST_CHECK(!m_transactionSuccessful);
- BOOST_CHECK(m_output != SUCCESS);
-
- // Invalid address (a bit above the 160th is set).
- sendMessage((bytes)fromHex("a9059cbb") + (bytes)fromHex("000000000000000000000100123456789a123456789a123456789a123456789a") + encodeArgs(1), false, 0);
- BOOST_CHECK(!m_transactionSuccessful);
- BOOST_CHECK(m_output != SUCCESS);
-}
-
-BOOST_AUTO_TEST_SUITE_END()
-
-}
-}
-} // end namespaces