mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	Merge branch 'develop' of https://github.com/ethereum/cpp-ethereum into develop
This commit is contained in:
		
						commit
						d0684f643b
					
				| @ -344,6 +344,18 @@ void ImportTest::exportTest(bytes const& _output, State const& _statePost) | ||||
| 
 | ||||
| 	m_TestObject["out"] = (_output.size() > 4096 && !Options::get().fulloutput) ? "#" + toString(_output.size()) : toHex(_output, 2, HexPrefix::Add); | ||||
| 
 | ||||
| 	// compare expected output with post output
 | ||||
| 	if (m_TestObject.count("expectOut") > 0) | ||||
| 	{ | ||||
| 		std::string warning = "Check State: Error! Unexpected output: " + m_TestObject["out"].get_str() + " Expected: " + m_TestObject["expectOut"].get_str(); | ||||
| 		if (Options::get().checkState) | ||||
| 			BOOST_CHECK_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning); | ||||
| 		else | ||||
| 			BOOST_WARN_MESSAGE((m_TestObject["out"].get_str() == m_TestObject["expectOut"].get_str()), warning); | ||||
| 
 | ||||
| 		m_TestObject.erase(m_TestObject.find("expectOut")); | ||||
| 	} | ||||
| 
 | ||||
| 	// export logs
 | ||||
| 	m_TestObject["logs"] = exportLog(_statePost.pending().size() ? _statePost.log(0) : LogEntries()); | ||||
| 
 | ||||
|  | ||||
| @ -105,8 +105,8 @@ BOOST_AUTO_TEST_CASE(location_test) | ||||
| 	shared_ptr<string const> n = make_shared<string>("source"); | ||||
| 	AssemblyItems items = compileContract(sourceCode); | ||||
| 	vector<SourceLocation> locations = | ||||
| 		vector<SourceLocation>(11, SourceLocation(2, 75, n)) + | ||||
| 		vector<SourceLocation>(12, SourceLocation(20, 72, n)) + | ||||
| 		vector<SourceLocation>(17, SourceLocation(2, 75, n)) + | ||||
| 		vector<SourceLocation>(14, SourceLocation(20, 72, n)) + | ||||
| 		vector<SourceLocation>{SourceLocation(42, 51, n), SourceLocation(65, 67, n)} + | ||||
| 		vector<SourceLocation>(4, SourceLocation(58, 67, n)) + | ||||
| 		vector<SourceLocation>(3, SourceLocation(20, 72, n)); | ||||
|  | ||||
| @ -566,16 +566,16 @@ BOOST_AUTO_TEST_CASE(strings) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(empty_string_on_stack) | ||||
| { | ||||
| 	char const* sourceCode = "contract test {\n" | ||||
| 							 "  function run(bytes0 empty, uint8 inp) returns(uint16 a, bytes0 b, bytes4 c) {\n" | ||||
| 							 "    var x = \"abc\";\n" | ||||
| 							 "    var y = \"\";\n" | ||||
| 							 "    var z = inp;\n" | ||||
| 							 "    a = z; b = y; c = x;" | ||||
| 							 "  }\n" | ||||
| 							 "}\n"; | ||||
| 	char const* sourceCode = R"( | ||||
| 		contract test { | ||||
| 			function run() external returns(bytes2 ret) { | ||||
| 				var y = ""; | ||||
| 				ret = y; | ||||
| 			} | ||||
| 		} | ||||
| 	)"; | ||||
| 	compileAndRun(sourceCode); | ||||
| 	BOOST_CHECK(callContractFunction("run(bytes0,uint8)", string(), byte(0x02)) == encodeArgs(0x2, string(""), string("abc\0"))); | ||||
| 	BOOST_CHECK(callContractFunction("run()") == encodeArgs(byte(0x00))); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(inc_dec_operators) | ||||
| @ -2396,7 +2396,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data) | ||||
| 	callContractFunction("deposit()"); | ||||
| 	BOOST_REQUIRE_EQUAL(m_logs.size(), 1); | ||||
| 	BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); | ||||
| 	BOOST_CHECK(m_logs[0].data == encodeArgs(10, 4, 15) + FixedHash<4>(dev::sha3("deposit()")).asBytes()); | ||||
| 	BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 4) + FixedHash<4>(dev::sha3("deposit()")).asBytes()); | ||||
| 	BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); | ||||
| 	BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); | ||||
| } | ||||
| @ -2420,7 +2420,7 @@ BOOST_AUTO_TEST_CASE(event_really_lots_of_data_from_storage) | ||||
| 	callContractFunction("deposit()"); | ||||
| 	BOOST_REQUIRE_EQUAL(m_logs.size(), 1); | ||||
| 	BOOST_CHECK_EQUAL(m_logs[0].address, m_contractAddress); | ||||
| 	BOOST_CHECK(m_logs[0].data == encodeArgs(10, 3, 15) + asBytes("ABC")); | ||||
| 	BOOST_CHECK(m_logs[0].data == encodeArgs(10, 0x60, 15, 3) + asBytes("ABC")); | ||||
| 	BOOST_REQUIRE_EQUAL(m_logs[0].topics.size(), 1); | ||||
| 	BOOST_CHECK_EQUAL(m_logs[0].topics[0], dev::sha3(string("Deposit(uint256,bytes,uint256)"))); | ||||
| } | ||||
| @ -2531,6 +2531,27 @@ BOOST_AUTO_TEST_CASE(sha3_with_bytes) | ||||
| 	BOOST_CHECK(callContractFunction("foo()") == encodeArgs(true)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(iterated_sha3_with_bytes) | ||||
| { | ||||
| 	char const* sourceCode = R"( | ||||
| 		contract c { | ||||
| 			bytes data; | ||||
| 			function foo() returns (bytes32) | ||||
| 			{ | ||||
| 				data.length = 3; | ||||
| 				data[0] = "x"; | ||||
| 				data[1] = "y"; | ||||
| 				data[2] = "z"; | ||||
| 				return sha3("b", sha3(data), "a"); | ||||
| 			} | ||||
| 		} | ||||
| 	)"; | ||||
| 	compileAndRun(sourceCode); | ||||
| 	BOOST_CHECK(callContractFunction("foo()") == encodeArgs( | ||||
| 		u256(dev::sha3(bytes{'b'} + dev::sha3("xyz").asBytes() + bytes{'a'})) | ||||
| 	)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(generic_call) | ||||
| { | ||||
| 	char const* sourceCode = R"**( | ||||
| @ -3786,30 +3807,6 @@ BOOST_AUTO_TEST_CASE(packed_storage_structs_delete) | ||||
| 	BOOST_CHECK(m_state.storage(m_contractAddress).empty()); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(packed_storage_structs_with_bytes0) | ||||
| { | ||||
| 	char const* sourceCode = R"( | ||||
| 		contract C { | ||||
| 			struct str { uint8 a; bytes0 b; uint8 c; } | ||||
| 			uint8 a; | ||||
| 			bytes0 x; | ||||
| 			uint8 b; | ||||
| 			str data; | ||||
| 			function test() returns (bool) { | ||||
| 				a = 2; | ||||
| 				b = 3; | ||||
| 				data.a = 4; | ||||
| 				data.c = 5; | ||||
| 				delete x; | ||||
| 				delete data.b; | ||||
| 				return a == 2 && b == 3 && data.a == 4 && data.c == 5; | ||||
| 			} | ||||
| 		} | ||||
| 	)"; | ||||
| 	compileAndRun(sourceCode); | ||||
| 	BOOST_CHECK(callContractFunction("test()") == encodeArgs(true)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(overloaded_function_call_resolve_to_first) | ||||
| { | ||||
| 	char const* sourceCode = R"( | ||||
| @ -4209,6 +4206,32 @@ BOOST_AUTO_TEST_CASE(failing_send) | ||||
| 	BOOST_REQUIRE(callContractFunction("callHelper(address)", c_helperAddress) == encodeArgs(true, 20)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(reusing_memory) | ||||
| { | ||||
| 	// Invoke some features that use memory and test that they do not interfere with each other.
 | ||||
| 	char const* sourceCode = R"( | ||||
| 		contract Helper { | ||||
| 			uint public flag; | ||||
| 			function Helper(uint x) { | ||||
| 				flag = x; | ||||
| 			} | ||||
| 		} | ||||
| 		contract Main { | ||||
| 			mapping(uint => uint) map; | ||||
| 			function f(uint x) returns (uint) { | ||||
| 				map[x] = x; | ||||
| 				return (new Helper(uint(sha3(this.g(map[x]))))).flag(); | ||||
| 			} | ||||
| 			function g(uint a) returns (uint) | ||||
| 			{ | ||||
| 				return map[a]; | ||||
| 			} | ||||
| 		} | ||||
| 	)"; | ||||
| 	compileAndRun(sourceCode, 0, "Main"); | ||||
| 	BOOST_REQUIRE(callContractFunction("f(uint256)", 0x34) == encodeArgs(dev::sha3(dev::toBigEndian(u256(0x34))))); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_SUITE_END() | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -558,16 +558,6 @@ BOOST_AUTO_TEST_CASE(function_external_call_not_allowed_conversion) | ||||
| 	BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); | ||||
| } | ||||
| 
 | ||||
| // todo delete when implemented
 | ||||
| BOOST_AUTO_TEST_CASE(arrays_in_internal_functions) | ||||
| { | ||||
| 	char const* text = R"( | ||||
| 		contract Test { | ||||
| 			function foo(address[] addresses) {} | ||||
| 	})"; | ||||
| 	BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(function_internal_allowed_conversion) | ||||
| { | ||||
| 	char const* text = R"( | ||||
| @ -1579,7 +1569,6 @@ BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName) | ||||
| 	BOOST_CHECK(*Type::fromElementaryTypeName(Token::UInt256) == *make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned)); | ||||
| 
 | ||||
| 	BOOST_CHECK(*Type::fromElementaryTypeName(Token::Byte) == *make_shared<FixedBytesType>(1)); | ||||
| 	BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes0) == *make_shared<FixedBytesType>(0)); | ||||
| 	BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes1) == *make_shared<FixedBytesType>(1)); | ||||
| 	BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes2) == *make_shared<FixedBytesType>(2)); | ||||
| 	BOOST_CHECK(*Type::fromElementaryTypeName(Token::Bytes3) == *make_shared<FixedBytesType>(3)); | ||||
| @ -1666,16 +1655,6 @@ BOOST_AUTO_TEST_CASE(local_const_variable) | ||||
| 	BOOST_CHECK_THROW(parseTextAndResolveNames(text), ParserError); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(bytes0_array) | ||||
| { | ||||
| 	char const* text = R"( | ||||
| 		contract Foo { | ||||
| 			bytes0[] illegalArray; | ||||
| 		} | ||||
| 	)"; | ||||
| 	BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(overloaded_function_cannot_resolve) | ||||
| { | ||||
| 	char const* sourceCode = R"( | ||||
|  | ||||
							
								
								
									
										491
									
								
								libsolidity/SolidityWallet.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										491
									
								
								libsolidity/SolidityWallet.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,491 @@ | ||||
| /*
 | ||||
| 	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 (comparatively) complex multisig wallet contract. | ||||
|  */ | ||||
| 
 | ||||
| #include <string> | ||||
| #include <tuple> | ||||
| #include <boost/test/unit_test.hpp> | ||||
| #include <libdevcore/Hash.h> | ||||
| #include <test/libsolidity/solidityExecutionFramework.h> | ||||
| 
 | ||||
| using namespace std; | ||||
| 
 | ||||
| namespace dev | ||||
| { | ||||
| namespace solidity | ||||
| { | ||||
| namespace test | ||||
| { | ||||
| 
 | ||||
| static char const* walletCode = R"DELIMITER( | ||||
| //sol Wallet
 | ||||
| // Multi-sig, daily-limited account proxy/wallet.
 | ||||
| // @authors:
 | ||||
| // Gav Wood <g@ethdev.com>
 | ||||
| // inheritable "property" contract that enables methods to be protected by requiring the acquiescence of either a
 | ||||
| // single, or, crucially, each of a number of, designated owners.
 | ||||
| // usage:
 | ||||
| // use modifiers onlyowner (just own owned) or onlymanyowners(hash), whereby the same hash must be provided by
 | ||||
| // some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
 | ||||
| // interior is executed.
 | ||||
| contract multiowned { | ||||
| 	// struct for the status of a pending operation.
 | ||||
| 	struct PendingState { | ||||
| 		uint yetNeeded; | ||||
| 		uint ownersDone; | ||||
| 		uint index; | ||||
| 	} | ||||
| 	// this contract only has five types of events: it can accept a confirmation, in which case
 | ||||
| 	// we record owner and operation (hash) alongside it.
 | ||||
| 	event Confirmation(address owner, bytes32 operation); | ||||
| 	event Revoke(address owner, bytes32 operation); | ||||
| 	// some others are in the case of an owner changing.
 | ||||
| 	event OwnerChanged(address oldOwner, address newOwner); | ||||
| 	event OwnerAdded(address newOwner); | ||||
| 	event OwnerRemoved(address oldOwner); | ||||
| 	// the last one is emitted if the required signatures change
 | ||||
| 	event RequirementChanged(uint newRequirement); | ||||
| 	// constructor is given number of sigs required to do protected "onlymanyowners" transactions
 | ||||
| 	// as well as the selection of addresses capable of confirming them.
 | ||||
| 	function multiowned() { | ||||
| 		m_required = 1; | ||||
| 		m_numOwners = 1; | ||||
| 		m_owners[m_numOwners] = uint(msg.sender); | ||||
| 		m_ownerIndex[uint(msg.sender)] = m_numOwners; | ||||
| 	} | ||||
| 	// simple single-sig function modifier.
 | ||||
| 	modifier onlyowner { | ||||
| 		if (isOwner(msg.sender)) | ||||
| 			_ | ||||
| 	} | ||||
| 	// multi-sig function modifier: the operation must have an intrinsic hash in order
 | ||||
| 	// that later attempts can be realised as the same underlying operation and
 | ||||
| 	// thus count as confirmations.
 | ||||
| 	modifier onlymanyowners(bytes32 _operation) { | ||||
| 		if (confirmed(_operation)) | ||||
| 			_ | ||||
| 	} | ||||
| 	// Revokes a prior confirmation of the given operation
 | ||||
| 	function revoke(bytes32 _operation) external { | ||||
| 		uint ownerIndex = m_ownerIndex[uint(msg.sender)]; | ||||
| 		// make sure they're an owner
 | ||||
| 		if (ownerIndex == 0) return; | ||||
| 		uint ownerIndexBit = 2**ownerIndex; | ||||
| 		var pending = m_pending[_operation]; | ||||
| 		if (pending.ownersDone & ownerIndexBit > 0) { | ||||
| 			pending.yetNeeded++; | ||||
| 			pending.ownersDone -= ownerIndexBit; | ||||
| 			Revoke(msg.sender, _operation); | ||||
| 		} | ||||
| 	} | ||||
| 	function confirmed(bytes32 _operation) internal returns (bool) { | ||||
| 		// determine what index the present sender is:
 | ||||
| 		uint ownerIndex = m_ownerIndex[uint(msg.sender)]; | ||||
| 		// make sure they're an owner
 | ||||
| 		if (ownerIndex == 0) return; | ||||
| 
 | ||||
| 		var pending = m_pending[_operation]; | ||||
| 		// if we're not yet working on this operation, switch over and reset the confirmation status.
 | ||||
| 		if (pending.yetNeeded == 0) { | ||||
| 			// reset count of confirmations needed.
 | ||||
| 			pending.yetNeeded = m_required; | ||||
| 			// reset which owners have confirmed (none) - set our bitmap to 0.
 | ||||
| 			pending.ownersDone = 0; | ||||
| 			pending.index = m_pendingIndex.length++; | ||||
| 			m_pendingIndex[pending.index] = _operation; | ||||
| 		} | ||||
| 		// determine the bit to set for this owner.
 | ||||
| 		uint ownerIndexBit = 2**ownerIndex; | ||||
| 		// make sure we (the message sender) haven't confirmed this operation previously.
 | ||||
| 		if (pending.ownersDone & ownerIndexBit == 0) { | ||||
| 			Confirmation(msg.sender, _operation); | ||||
| 			// ok - check if count is enough to go ahead.
 | ||||
| 			if (pending.yetNeeded <= 1) { | ||||
| 				// enough confirmations: reset and run interior.
 | ||||
| 				delete m_pendingIndex[m_pending[_operation].index]; | ||||
| 				delete m_pending[_operation]; | ||||
| 				return true; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				// not enough: record that this owner in particular confirmed.
 | ||||
| 				pending.yetNeeded--; | ||||
| 				pending.ownersDone |= ownerIndexBit; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Replaces an owner `_from` with another `_to`.
 | ||||
| 	function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data)) external { | ||||
| 		if (isOwner(_to)) return; | ||||
| 		uint ownerIndex = m_ownerIndex[uint(_from)]; | ||||
| 		if (ownerIndex == 0) return; | ||||
| 
 | ||||
| 		clearPending(); | ||||
| 		m_owners[ownerIndex] = uint(_to); | ||||
| 		m_ownerIndex[uint(_from)] = 0; | ||||
| 		m_ownerIndex[uint(_to)] = ownerIndex; | ||||
| 		OwnerChanged(_from, _to); | ||||
| 	} | ||||
| 	function addOwner(address _owner) onlymanyowners(sha3(msg.data)) external { | ||||
| 		if (isOwner(_owner)) return; | ||||
| 
 | ||||
| 		clearPending(); | ||||
| 		if (m_numOwners >= c_maxOwners) | ||||
| 			reorganizeOwners(); | ||||
| 		if (m_numOwners >= c_maxOwners) | ||||
| 			return; | ||||
| 		m_numOwners++; | ||||
| 		m_owners[m_numOwners] = uint(_owner); | ||||
| 		m_ownerIndex[uint(_owner)] = m_numOwners; | ||||
| 		OwnerAdded(_owner); | ||||
| 	} | ||||
| 	function removeOwner(address _owner) onlymanyowners(sha3(msg.data)) external { | ||||
| 		uint ownerIndex = m_ownerIndex[uint(_owner)]; | ||||
| 		if (ownerIndex == 0) return; | ||||
| 		if (m_required > m_numOwners - 1) return; | ||||
| 
 | ||||
| 		m_owners[ownerIndex] = 0; | ||||
| 		m_ownerIndex[uint(_owner)] = 0; | ||||
| 		clearPending(); | ||||
| 		reorganizeOwners(); //make sure m_numOwner is equal to the number of owners and always points to the optimal free slot
 | ||||
| 		OwnerRemoved(_owner); | ||||
| 	} | ||||
| 	function reorganizeOwners() private returns (bool) { | ||||
| 		uint free = 1; | ||||
| 		while (free < m_numOwners) | ||||
| 		{ | ||||
| 			while (free < m_numOwners && m_owners[free] != 0) free++; | ||||
| 			while (m_numOwners > 1 && m_owners[m_numOwners] == 0) m_numOwners--; | ||||
| 			if (free < m_numOwners && m_owners[m_numOwners] != 0 && m_owners[free] == 0) | ||||
| 			{ | ||||
| 				m_owners[free] = m_owners[m_numOwners]; | ||||
| 				m_ownerIndex[m_owners[free]] = free; | ||||
| 				m_owners[m_numOwners] = 0; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	function clearPending() internal { | ||||
| 		uint length = m_pendingIndex.length; | ||||
| 		for (uint i = 0; i < length; ++i) | ||||
| 			if (m_pendingIndex[i] != 0) | ||||
| 				delete m_pending[m_pendingIndex[i]]; | ||||
| 		delete m_pendingIndex; | ||||
| 	} | ||||
| 	function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external { | ||||
| 		if (_newRequired > m_numOwners) return; | ||||
| 		m_required = _newRequired; | ||||
| 		clearPending(); | ||||
| 		RequirementChanged(_newRequired); | ||||
| 	} | ||||
| 	function isOwner(address _addr) returns (bool) { | ||||
| 		return m_ownerIndex[uint(_addr)] > 0; | ||||
| 	} | ||||
| 
 | ||||
| 	// the number of owners that must confirm the same operation before it is run.
 | ||||
| 	uint m_required; | ||||
| 	// pointer used to find a free slot in m_owners
 | ||||
| 	uint m_numOwners; | ||||
| 	// list of owners
 | ||||
| 	uint[256] m_owners; | ||||
| 	uint constant c_maxOwners = 250; | ||||
| 	// index on the list of owners to allow reverse lookup
 | ||||
| 	mapping(uint => uint) m_ownerIndex; | ||||
| 	// the ongoing operations.
 | ||||
| 	mapping(bytes32 => PendingState) m_pending; | ||||
| 	bytes32[] m_pendingIndex; | ||||
| } | ||||
| 
 | ||||
| // inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
 | ||||
| // on a particular resource per calendar day. is multiowned to allow the limit to be altered. resource that method
 | ||||
| // uses is specified in the modifier.
 | ||||
| contract daylimit is multiowned { | ||||
| 	// constructor - just records the present day's index.
 | ||||
| 	function daylimit() { | ||||
| 		m_lastDay = today(); | ||||
| 	} | ||||
| 	// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
 | ||||
| 	function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data)) external { | ||||
| 		m_dailyLimit = _newLimit; | ||||
| 	} | ||||
| 	// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
 | ||||
| 	function resetSpentToday() onlymanyowners(sha3(msg.data)) external { | ||||
| 		m_spentToday = 0; | ||||
| 	} | ||||
| 	// checks to see if there is at least `_value` left from the daily limit today. if there is, subtracts it and
 | ||||
| 	// returns true. otherwise just returns false.
 | ||||
| 	function underLimit(uint _value) internal onlyowner returns (bool) { | ||||
| 		// reset the spend limit if we're on a different day to last time.
 | ||||
| 		if (today() > m_lastDay) { | ||||
| 			m_spentToday = 0; | ||||
| 			m_lastDay = today(); | ||||
| 		} | ||||
| 		// check to see if there's enough left - if so, subtract and return true.
 | ||||
| 		if (m_spentToday + _value >= m_spentToday && m_spentToday + _value <= m_dailyLimit) { | ||||
| 			m_spentToday += _value; | ||||
| 			return true; | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
| 	// simple modifier for daily limit.
 | ||||
| 	modifier limitedDaily(uint _value) { | ||||
| 		if (underLimit(_value)) | ||||
| 			_ | ||||
| 	} | ||||
| 	// determines today's index.
 | ||||
| 	function today() private constant returns (uint) { return now / 1 days; } | ||||
| 	uint m_spentToday; | ||||
| 	uint m_dailyLimit; | ||||
| 	uint m_lastDay; | ||||
| } | ||||
| // interface contract for multisig proxy contracts; see below for docs.
 | ||||
| contract multisig { | ||||
| 	event Deposit(address from, uint value); | ||||
| 	event SingleTransact(address owner, uint value, address to, bytes data); | ||||
| 	event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data); | ||||
| 	event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data); | ||||
| 	function changeOwner(address _from, address _to) external; | ||||
| 	function execute(address _to, uint _value, bytes _data) external returns (bytes32); | ||||
| 	function confirm(bytes32 _h) returns (bool); | ||||
| } | ||||
| // usage:
 | ||||
| // bytes32 h = Wallet(w).from(oneOwner).transact(to, value, data);
 | ||||
| // Wallet(w).from(anotherOwner).confirm(h);
 | ||||
| contract Wallet is multisig, multiowned, daylimit { | ||||
| 	// Transaction structure to remember details of transaction lest it need be saved for a later call.
 | ||||
| 	struct Transaction { | ||||
| 		address to; | ||||
| 		uint value; | ||||
| 		bytes data; | ||||
| 	} | ||||
| 	// logged events:
 | ||||
| 	// Funds has arrived into the wallet (record how much).
 | ||||
| 	event Deposit(address from, uint value); | ||||
| 	// Single transaction going out of the wallet (record who signed for it, how much, and to whom it's going).
 | ||||
| 	event SingleTransact(address owner, uint value, address to, bytes data); | ||||
| 	// constructor - just pass on the owner arra to the multiowned.
 | ||||
| 	event Created(); | ||||
| 	function Wallet() { | ||||
| 		Created(); | ||||
| 	} | ||||
| 	// kills the contract sending everything to `_to`.
 | ||||
| 	function kill(address _to) onlymanyowners(sha3(msg.data)) external { | ||||
| 		suicide(_to); | ||||
| 	} | ||||
| 	// gets called when no other function matches
 | ||||
| 	function() { | ||||
| 		// just being sent some cash?
 | ||||
| 		if (msg.value > 0) | ||||
| 			Deposit(msg.sender, msg.value); | ||||
| 	} | ||||
| 	// Outside-visible transact entry point. Executes transacion immediately if below daily spend limit.
 | ||||
| 	// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
 | ||||
| 	// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
 | ||||
| 	// and _data arguments). They still get the option of using them if they want, anyways.
 | ||||
| 	function execute(address _to, uint _value, bytes _data) onlyowner external returns (bytes32 _r) { | ||||
| 		// first, take the opportunity to check that we're under the daily limit.
 | ||||
| 		if (underLimit(_value)) { | ||||
| 			SingleTransact(msg.sender, _value, _to, _data); | ||||
| 			// yes - just execute the call.
 | ||||
| 			_to.call.value(_value)(_data); | ||||
| 			return 0; | ||||
| 		} | ||||
| 		// determine our operation hash.
 | ||||
| 		_r = sha3(msg.data); | ||||
| 		if (!confirm(_r) && m_txs[_r].to == 0) { | ||||
| 			m_txs[_r].to = _to; | ||||
| 			m_txs[_r].value = _value; | ||||
| 			m_txs[_r].data = _data; | ||||
| 			ConfirmationNeeded(_r, msg.sender, _value, _to, _data); | ||||
| 		} | ||||
| 	} | ||||
| 	// confirm a transaction through just the hash. we use the previous transactions map, m_txs, in order
 | ||||
| 	// to determine the body of the transaction from the hash provided.
 | ||||
| 	function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) { | ||||
| 		if (m_txs[_h].to != 0) { | ||||
| 			m_txs[_h].to.call.value(m_txs[_h].value)(m_txs[_h].data); | ||||
| 			MultiTransact(msg.sender, _h, m_txs[_h].value, m_txs[_h].to, m_txs[_h].data); | ||||
| 			delete m_txs[_h]; | ||||
| 			return true; | ||||
| 		} | ||||
| 	} | ||||
| 	function clearPending() internal { | ||||
| 		uint length = m_pendingIndex.length; | ||||
| 		for (uint i = 0; i < length; ++i) | ||||
| 			delete m_txs[m_pendingIndex[i]]; | ||||
| 		super.clearPending(); | ||||
| 	} | ||||
| 	// pending transactions we have at present.
 | ||||
| 	mapping (bytes32 => Transaction) m_txs; | ||||
| } | ||||
| )DELIMITER"; | ||||
| 
 | ||||
| static unique_ptr<bytes> s_compiledWallet; | ||||
| 
 | ||||
| class WalletTestFramework: public ExecutionFramework | ||||
| { | ||||
| protected: | ||||
| 	void deployWallet(u256 const& _value = 0) | ||||
| 	{ | ||||
| 		if (!s_compiledWallet) | ||||
| 		{ | ||||
| 			m_optimize = true; | ||||
| 			m_compiler.reset(false, m_addStandardSources); | ||||
| 			m_compiler.addSource("", walletCode); | ||||
| 			ETH_TEST_REQUIRE_NO_THROW(m_compiler.compile(m_optimize, m_optimizeRuns), "Compiling contract failed"); | ||||
| 			s_compiledWallet.reset(new bytes(m_compiler.getBytecode("Wallet"))); | ||||
| 		} | ||||
| 		sendMessage(*s_compiledWallet, true, _value); | ||||
| 		BOOST_REQUIRE(!m_output.empty()); | ||||
| 	} | ||||
| }; | ||||
| 
 | ||||
| /// This is a test suite that tests optimised code!
 | ||||
| BOOST_FIXTURE_TEST_SUITE(SolidityWallet, WalletTestFramework) | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(creation) | ||||
| { | ||||
| 	deployWallet(200); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(m_sender, h256::AlignRight)) == encodeArgs(true)); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", ~h256(m_sender, h256::AlignRight)) == encodeArgs(false)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(add_owners) | ||||
| { | ||||
| 	deployWallet(200); | ||||
| 	Address originalOwner = m_sender; | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true)); | ||||
| 	// now let the new owner add someone
 | ||||
| 	m_sender = Address(0x12); | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true)); | ||||
| 	// and check that a non-owner cannot add a new owner
 | ||||
| 	m_sender = Address(0x50); | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x20)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x20)) == encodeArgs(false)); | ||||
| 	// finally check that all the owners are there
 | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(originalOwner, h256::AlignRight)) == encodeArgs(true)); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true)); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(change_owners) | ||||
| { | ||||
| 	deployWallet(200); | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(true)); | ||||
| 	BOOST_REQUIRE(callContractFunction("changeOwner(address,address)", h256(0x12), h256(0x13)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12)) == encodeArgs(false)); | ||||
| 	BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x13)) == encodeArgs(true)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(remove_owner) | ||||
| { | ||||
| 	deployWallet(200); | ||||
| 	// add 10 owners
 | ||||
| 	for (unsigned i = 0; i < 10; ++i) | ||||
| 	{ | ||||
| 		BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12 + i)) == encodeArgs()); | ||||
| 		BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true)); | ||||
| 	} | ||||
| 	// check they are there again
 | ||||
| 	for (unsigned i = 0; i < 10; ++i) | ||||
| 		BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true)); | ||||
| 	// remove the odd owners
 | ||||
| 	for (unsigned i = 0; i < 10; ++i) | ||||
| 		if (i % 2 == 1) | ||||
| 			BOOST_REQUIRE(callContractFunction("removeOwner(address)", h256(0x12 + i)) == encodeArgs()); | ||||
| 	// check the result
 | ||||
| 	for (unsigned i = 0; i < 10; ++i) | ||||
| 		BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(i % 2 == 0)); | ||||
| 	// add them again
 | ||||
| 	for (unsigned i = 0; i < 10; ++i) | ||||
| 		if (i % 2 == 1) | ||||
| 			BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12 + i)) == encodeArgs()); | ||||
| 	// check everyone is there
 | ||||
| 	for (unsigned i = 0; i < 10; ++i) | ||||
| 		BOOST_REQUIRE(callContractFunction("isOwner(address)", h256(0x12 + i)) == encodeArgs(true)); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(multisig_value_transfer) | ||||
| { | ||||
| 	deployWallet(200); | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x14)) == encodeArgs()); | ||||
| 	// 4 owners, set required to 3
 | ||||
| 	BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs()); | ||||
| 	// check that balance is and stays zero at destination address
 | ||||
| 	h256 opHash("f916231db11c12e0142dc51f23632bc655de87c63f83fc928c443e90f7aa364a"); | ||||
| 	BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0); | ||||
| 	m_sender = Address(0x12); | ||||
| 	BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash)); | ||||
| 	BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0); | ||||
| 	m_sender = Address(0x13); | ||||
| 	BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash)); | ||||
| 	BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0); | ||||
| 	m_sender = Address(0x14); | ||||
| 	BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash)); | ||||
| 	// now it should go through
 | ||||
| 	BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 100); | ||||
| } | ||||
| 
 | ||||
| BOOST_AUTO_TEST_CASE(daylimit) | ||||
| { | ||||
| 	deployWallet(200); | ||||
| 	BOOST_REQUIRE(callContractFunction("setDailyLimit(uint256)", h256(100)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x12)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x13)) == encodeArgs()); | ||||
| 	BOOST_REQUIRE(callContractFunction("addOwner(address)", h256(0x14)) == encodeArgs()); | ||||
| 	// 4 owners, set required to 3
 | ||||
| 	BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs()); | ||||
| 
 | ||||
| 	// try to send tx over daylimit
 | ||||
| 	BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0); | ||||
| 	m_sender = Address(0x12); | ||||
| 	BOOST_REQUIRE( | ||||
| 		callContractFunction("execute(address,uint256,bytes)", h256(0x05), 150, 0x60, 0x00) != | ||||
| 		encodeArgs(u256(0)) | ||||
| 	); | ||||
| 	BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0); | ||||
| 	// try to send tx under daylimit by stranger
 | ||||
| 	m_sender = Address(0x77); | ||||
| 	BOOST_REQUIRE( | ||||
| 		callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) == | ||||
| 		encodeArgs(u256(0)) | ||||
| 	); | ||||
| 	BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0); | ||||
| 	// now send below limit by owner
 | ||||
| 	m_sender = Address(0x12); | ||||
| 	BOOST_REQUIRE( | ||||
| 		callContractFunction("execute(address,uint256,bytes)", h256(0x05), 90, 0x60, 0x00) == | ||||
| 		encodeArgs(u256(0)) | ||||
| 	); | ||||
| 	BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 90); | ||||
| } | ||||
| 
 | ||||
| //@todo test data calls
 | ||||
| 
 | ||||
| BOOST_AUTO_TEST_SUITE_END() | ||||
| 
 | ||||
| } | ||||
| } | ||||
| } // end namespaces
 | ||||
| @ -148,6 +148,8 @@ protected: | ||||
| 	{ | ||||
| 		m_state.addBalance(m_sender, _value); // just in case
 | ||||
| 		eth::Executive executive(m_state, eth::LastHashes(), 0); | ||||
| 		eth::ExecutionResult res; | ||||
| 		executive.setResultRecipient(res); | ||||
| 		eth::Transaction t = | ||||
| 			_isCreation ? | ||||
| 				eth::Transaction(_value, m_gasPrice, m_gas, _data, 0, KeyPair::create().sec()) : | ||||
| @ -176,7 +178,7 @@ protected: | ||||
| 		m_state.noteSending(m_sender); | ||||
| 		executive.finalize(); | ||||
| 		m_gasUsed = executive.gasUsed(); | ||||
| 		m_output = executive.out().toVector(); | ||||
| 		m_output = std::move(res.output); // FIXME: Looks like Framework needs ExecutiveResult embedded
 | ||||
| 		m_logs = executive.logs(); | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user