diff --git a/CMakeLists.txt b/CMakeLists.txt index 952b1674f..cbf184808 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,6 +72,7 @@ add_executable(testeth ${SRC_LIST} ${HEADERS}) target_link_libraries(testeth ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) target_link_libraries(testeth ${CURL_LIBRARIES}) +target_link_libraries(testeth ${CRYPTOPP_LIBRARIES}) target_link_libraries(testeth ethereum) target_link_libraries(testeth ethcore) if (NOT WIN32) diff --git a/TestHelper.cpp b/TestHelper.cpp index 698f35122..9a1b16935 100644 --- a/TestHelper.cpp +++ b/TestHelper.cpp @@ -199,10 +199,10 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state, stateOptio stateOptions.m_bHasCode = true; } - if (code.size()) + if (!code.empty()) { _state.m_cache[address] = Account(balance, Account::ContractConception); - _state.m_cache[address].setCode(code); + _state.m_cache[address].setCode(std::move(code)); } else _state.m_cache[address] = Account(balance, Account::NormalCreation); @@ -235,7 +235,7 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state) } void ImportTest::importTransaction(json_spirit::mObject& _o) -{ +{ if (_o.count("secretKey") > 0) { assert(_o.count("nonce") > 0); @@ -728,10 +728,20 @@ Options::Options() for (auto i = 0; i < argc; ++i) { auto arg = std::string{argv[i]}; - if (arg == "--jit") - eth::VMFactory::setKind(eth::VMKind::JIT); - else if (arg == "--vm=smart") - eth::VMFactory::setKind(eth::VMKind::Smart); + if (arg == "--vm" && i + 1 < argc) + { + string vmKind = argv[++i]; + if (vmKind == "interpreter") + VMFactory::setKind(VMKind::Interpreter); + else if (vmKind == "jit") + VMFactory::setKind(VMKind::JIT); + else if (vmKind == "smart") + VMFactory::setKind(VMKind::Smart); + else + cerr << "Unknown VM kind: " << vmKind << endl; + } + else if (arg == "--jit") // TODO: Remove deprecated option "--jit" + VMFactory::setKind(VMKind::JIT); else if (arg == "--vmtrace") vmtrace = true; else if (arg == "--filltests") diff --git a/TestHelper.h b/TestHelper.h index 1c1dfb5f0..4ac59e917 100644 --- a/TestHelper.h +++ b/TestHelper.h @@ -188,6 +188,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin); void doStateTests(json_spirit::mValue& v, bool _fillin); void doVMTests(json_spirit::mValue& v, bool _fillin); void doBlockchainTests(json_spirit::mValue& _v, bool _fillin); +void doRlpTests(json_spirit::mValue& v, bool _fillin); template void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs) diff --git a/libsolidity/SolidityEndToEndTest.cpp b/libsolidity/SolidityEndToEndTest.cpp index a2dbc35e6..ad2175461 100644 --- a/libsolidity/SolidityEndToEndTest.cpp +++ b/libsolidity/SolidityEndToEndTest.cpp @@ -564,20 +564,6 @@ BOOST_AUTO_TEST_CASE(strings) BOOST_CHECK(callContractFunction("pipeThrough(bytes2,bool)", string("\0\x02", 2), true) == encodeArgs(string("\0\x2", 2), true)); } -BOOST_AUTO_TEST_CASE(empty_string_on_stack) -{ - char const* sourceCode = R"( - contract test { - function run() external returns(bytes2 ret) { - var y = ""; - ret = y; - } - } - )"; - compileAndRun(sourceCode); - BOOST_CHECK(callContractFunction("run()") == encodeArgs(byte(0x00))); -} - BOOST_AUTO_TEST_CASE(inc_dec_operators) { char const* sourceCode = R"( @@ -4806,6 +4792,232 @@ BOOST_AUTO_TEST_CASE(memory_arrays_dynamic_index_access_write) BOOST_CHECK(callContractFunction("f()") == encodeArgs(u256(0x20), u256(4), data)); } +BOOST_AUTO_TEST_CASE(memory_structs_read_write) +{ + char const* sourceCode = R"( + contract Test { + struct S { uint8 x; uint16 y; uint z; uint8[2] a; } + S[5] data; + function testInit() returns (uint8 x, uint16 y, uint z, uint8 a, bool flag) { + S[2] memory d; + x = d[0].x; + y = d[0].y; + z = d[0].z; + a = d[0].a[1]; + flag = true; + } + function testCopyRead() returns (uint8 x, uint16 y, uint z, uint8 a) { + data[2].x = 1; + data[2].y = 2; + data[2].z = 3; + data[2].a[1] = 4; + S memory s = data[2]; + x = s.x; + y = s.y; + z = s.z; + a = s.a[1]; + } + function testAssign() returns (uint8 x, uint16 y, uint z, uint8 a) { + S memory s; + s.x = 1; + s.y = 2; + s.z = 3; + s.a[1] = 4; + x = s.x; + y = s.y; + z = s.z; + a = s.a[1]; + } + } + )"; + compileAndRun(sourceCode, 0, "Test"); + + BOOST_CHECK(callContractFunction("testInit()") == encodeArgs(u256(0), u256(0), u256(0), u256(0), true)); + BOOST_CHECK(callContractFunction("testCopyRead()") == encodeArgs(u256(1), u256(2), u256(3), u256(4))); + BOOST_CHECK(callContractFunction("testAssign()") == encodeArgs(u256(1), u256(2), u256(3), u256(4))); +} + +BOOST_AUTO_TEST_CASE(memory_structs_as_function_args) +{ + char const* sourceCode = R"( + contract Test { + struct S { uint8 x; uint16 y; uint z; } + function test() returns (uint x, uint y, uint z) { + S memory data = combine(1, 2, 3); + x = extract(data, 0); + y = extract(data, 1); + z = extract(data, 2); + } + function extract(S s, uint which) internal returns (uint x) { + if (which == 0) return s.x; + else if (which == 1) return s.y; + else return s.z; + } + function combine(uint8 x, uint16 y, uint z) internal returns (S s) { + s.x = x; + s.y = y; + s.z = z; + } + } + )"; + compileAndRun(sourceCode, 0, "Test"); + + BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(1), u256(2), u256(3))); +} + +BOOST_AUTO_TEST_CASE(memory_structs_nested) +{ + char const* sourceCode = R"( + contract Test { + struct S { uint8 x; uint16 y; uint z; } + struct X { uint8 x; S s; } + function test() returns (uint a, uint x, uint y, uint z) { + X memory d = combine(1, 2, 3, 4); + a = extract(d, 0); + x = extract(d, 1); + y = extract(d, 2); + z = extract(d, 3); + } + function extract(X s, uint which) internal returns (uint x) { + if (which == 0) return s.x; + else if (which == 1) return s.s.x; + else if (which == 2) return s.s.y; + else return s.s.z; + } + function combine(uint8 a, uint8 x, uint16 y, uint z) internal returns (X s) { + s.x = a; + s.s.x = x; + s.s.y = y; + s.s.z = z; + } + } + )"; + compileAndRun(sourceCode, 0, "Test"); + + BOOST_CHECK(callContractFunction("test()") == encodeArgs(u256(1), u256(2), u256(3), u256(4))); +} + +BOOST_AUTO_TEST_CASE(memory_structs_nested_load) +{ + char const* sourceCode = R"( + contract Test { + struct S { uint8 x; uint16 y; uint z; } + struct X { uint8 x; S s; uint8[2] a; } + X m_x; + function load() returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { + m_x.x = 1; + m_x.s.x = 2; + m_x.s.y = 3; + m_x.s.z = 4; + m_x.a[0] = 5; + m_x.a[1] = 6; + X memory d = m_x; + a = d.x; + x = d.s.x; + y = d.s.y; + z = d.s.z; + a1 = d.a[0]; + a2 = d.a[1]; + } + function store() returns (uint a, uint x, uint y, uint z, uint a1, uint a2) { + X memory d; + d.x = 1; + d.s.x = 2; + d.s.y = 3; + d.s.z = 4; + d.a[0] = 5; + d.a[1] = 6; + m_x = d; + a = m_x.x; + x = m_x.s.x; + y = m_x.s.y; + z = m_x.s.z; + a1 = m_x.a[0]; + a2 = m_x.a[1]; + } + } + )"; + compileAndRun(sourceCode, 0, "Test"); + + auto out = encodeArgs(u256(1), u256(2), u256(3), u256(4), u256(5), u256(6)); + BOOST_CHECK(callContractFunction("load()") == out); + BOOST_CHECK(callContractFunction("store()") == out); +} + +BOOST_AUTO_TEST_CASE(struct_constructor_nested) +{ + char const* sourceCode = R"( + contract C { + struct X { uint x1; uint x2; } + struct S { uint s1; uint[3] s2; X s3; } + S s; + function C() { + uint[3] memory s2; + s2[1] = 9; + s = S(1, s2, X(4, 5)); + } + function get() returns (uint s1, uint[3] s2, uint x1, uint x2) + { + s1 = s.s1; + s2 = s.s2; + x1 = s.s3.x1; + x2 = s.s3.x2; + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + + auto out = encodeArgs(u256(1), u256(0), u256(9), u256(0), u256(4), u256(5)); + BOOST_CHECK(callContractFunction("get()") == out); +} + +BOOST_AUTO_TEST_CASE(struct_named_constructor) +{ + char const* sourceCode = R"( + contract C { + struct S { uint a; bool x; } + S public s; + function C() { + s = S({a: 1, x: true}); + } + } + )"; + compileAndRun(sourceCode, 0, "C"); + + BOOST_CHECK(callContractFunction("s()") == encodeArgs(u256(1), true)); +} + +BOOST_AUTO_TEST_CASE(literal_strings) +{ + char const* sourceCode = R"( + contract Test { + string public long; + string public medium; + string public short; + string public empty; + function f() returns (string) { + long = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + medium = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; + short = "123"; + empty = ""; + return "Hello, World!"; + } + } + )"; + compileAndRun(sourceCode, 0, "Test"); + string longStr = "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678900123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789001234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + string medium = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"; + string shortStr = "123"; + string hello = "Hello, World!"; + + BOOST_CHECK(callContractFunction("f()") == encodeDyn(hello)); + BOOST_CHECK(callContractFunction("long()") == encodeDyn(longStr)); + BOOST_CHECK(callContractFunction("medium()") == encodeDyn(medium)); + BOOST_CHECK(callContractFunction("short()") == encodeDyn(shortStr)); + BOOST_CHECK(callContractFunction("empty()") == encodeDyn(string())); +} + + BOOST_AUTO_TEST_SUITE_END() } diff --git a/libsolidity/SolidityNameAndTypeResolution.cpp b/libsolidity/SolidityNameAndTypeResolution.cpp index 4914ef975..1e40ee4f6 100644 --- a/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/libsolidity/SolidityNameAndTypeResolution.cpp @@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(large_string_literal) char const* text = "contract test {\n" " function f() { var x = \"123456789012345678901234567890123\"; }" "}\n"; - BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError); + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); } BOOST_AUTO_TEST_CASE(balance) @@ -2056,6 +2056,60 @@ BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable) BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError); } +BOOST_AUTO_TEST_CASE(struct_constructor) +{ + char const* sourceCode = R"( + contract C { + struct S { uint a; bool x; } + function f() { + S memory s = S(1, true); + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(struct_constructor_nested) +{ + char const* sourceCode = R"( + contract C { + struct X { uint x1; uint x2; } + struct S { uint s1; uint[3] s2; X s3; } + function f() { + uint[3] memory s2; + S memory s = S(1, s2, X(4, 5)); + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(struct_named_constructor) +{ + char const* sourceCode = R"( + contract C { + struct S { uint a; bool x; } + function f() { + S memory s = S({a: 1, x: true}); + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(sourceCode)); +} + +BOOST_AUTO_TEST_CASE(literal_strings) +{ + char const* text = R"( + contract Foo { + function f() { + string memory long = "01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"; + string memory short = "123"; + } + } + )"; + BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); +} + BOOST_AUTO_TEST_SUITE_END() } diff --git a/libsolidity/SolidityWallet.cpp b/libsolidity/SolidityWallet.cpp index ba8f198f8..1c296b9f7 100644 --- a/libsolidity/SolidityWallet.cpp +++ b/libsolidity/SolidityWallet.cpp @@ -47,12 +47,18 @@ static char const* walletCode = R"DELIMITER( // some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the // interior is executed. contract multiowned { + + // TYPES + // struct for the status of a pending operation. struct PendingState { uint yetNeeded; uint ownersDone; uint index; } + + // EVENTS + // 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); @@ -63,14 +69,9 @@ contract multiowned { 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; - } + + // MODIFIERS + // simple single-sig function modifier. modifier onlyowner { if (isOwner(msg.sender)) @@ -80,9 +81,21 @@ contract multiowned { // that later attempts can be realised as the same underlying operation and // thus count as confirmations. modifier onlymanyowners(bytes32 _operation) { - if (confirmed(_operation)) + if (confirmAndCheck(_operation)) _ } + + // METHODS + + // 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; + } + // Revokes a prior confirmation of the given operation function revoke(bytes32 _operation) external { uint ownerIndex = m_ownerIndex[uint(msg.sender)]; @@ -96,7 +109,75 @@ contract multiowned { Revoke(msg.sender, _operation); } } - function confirmed(bytes32 _operation) internal returns (bool) { + + // Replaces an owner `_from` with another `_to`. + function changeOwner(address _from, address _to) onlymanyowners(sha3(msg.data, block.number)) 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, block.number)) 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, block.number)) 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 changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data, block.number)) external { + if (_newRequired > m_numOwners) return; + m_required = _newRequired; + clearPending(); + RequirementChanged(_newRequired); + } + + function isOwner(address _addr) returns (bool) { + return m_ownerIndex[uint(_addr)] > 0; + } + + function hasConfirmed(bytes32 _operation, address _owner) constant returns (bool) { + var pending = m_pending[_operation]; + uint ownerIndex = m_ownerIndex[uint(_owner)]; + + // make sure they're an owner + if (ownerIndex == 0) return false; + + // determine the bit to set for this owner. + uint ownerIndexBit = 2**ownerIndex; + if (pending.ownersDone & ownerIndexBit == 0) { + return false; + } else { + return true; + } + } + + // INTERNAL METHODS + + function confirmAndCheck(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 @@ -132,42 +213,7 @@ contract multiowned { } } } - // 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) @@ -182,6 +228,7 @@ contract multiowned { } } } + function clearPending() internal { uint length = m_pendingIndex.length; for (uint i = 0; i < length; ++i) @@ -189,46 +236,54 @@ contract multiowned { 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; - } + + // FIELDS // the number of owners that must confirm the same operation before it is run. uint public m_required; // pointer used to find a free slot in m_owners uint public m_numOwners; + // list of owners - uint[256] public m_owners; + uint[256] m_owners; uint constant c_maxOwners = 250; // index on the list of owners to allow reverse lookup - mapping(uint => uint) public m_ownerIndex; + mapping(uint => uint) m_ownerIndex; // the ongoing operations. - mapping(bytes32 => PendingState) public m_pending; - bytes32[] public m_pendingIndex; + 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 { + + // MODIFIERS + + // simple modifier for daily limit. + modifier limitedDaily(uint _value) { + if (underLimit(_value)) + _ + } + + // METHODS + // 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 { + function setDailyLimit(uint _newLimit) onlymanyowners(sha3(msg.data, block.number)) 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 { + function resetSpentToday() onlymanyowners(sha3(msg.data, block.number)) external { m_spentToday = 0; } + + // INTERNAL METHODS + // 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) { @@ -244,60 +299,76 @@ contract daylimit is multiowned { } 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 public m_spentToday; + + // FIELDS + uint public m_dailyLimit; - uint public m_lastDay; + uint m_spentToday; + 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; - } - /* + + // EVENTS + // 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); // Multi-sig transaction going out of the wallet (record who signed for it last, the operation hash, how much, and to whom it's going). - event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);*/ - // constructor - just pass on the owner arra to the multiowned. - event Created(); - function Wallet() { - Created(); + event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data); + // Confirmation still needed for a transaction. + event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data); + + // FUNCTIONS + + // TODO: document + 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 { + + // TYPES + + // Transaction structure to remember details of transaction lest it need be saved for a later call. + struct Transaction { + address to; + uint value; + bytes data; } + + // EVENTS + + event Created(bytes32 indexed identifier); + + // METHODS + + // constructor - just pass on the owner arra to the multiowned. + function Wallet(bytes32 identifier) { + Created(identifier); + } + // kills the contract sending everything to `_to`. - function kill(address _to) onlymanyowners(sha3(msg.data)) external { + function kill(address _to) onlymanyowners(sha3(msg.data, block.number)) 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 @@ -311,7 +382,7 @@ contract Wallet is multisig, multiowned, daylimit { return 0; } // determine our operation hash. - _r = sha3(msg.data); + _r = sha3(msg.data, block.number); if (!confirm(_r) && m_txs[_r].to == 0) { m_txs[_r].to = _to; m_txs[_r].value = _value; @@ -319,6 +390,7 @@ contract Wallet is multisig, multiowned, daylimit { 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) { @@ -329,20 +401,20 @@ contract Wallet is multisig, multiowned, daylimit { return true; } } + + // INTERNAL METHODS + function clearPending() internal { uint length = m_pendingIndex.length; for (uint i = 0; i < length; ++i) delete m_txs[m_pendingIndex[i]]; super.clearPending(); } - // // internally confirm transaction with all of the info. returns true iff confirmed good and executed. - // function confirmVerbose(bytes32 _h, address _to, uint _value, bytes _data) private onlymanyowners(_h) returns (bool) { - // _to.call.value(_value)(_data); - // MultiTransact("out", msg.sender, _h, _value, _to); - // return true; - // } + + // FIELDS + // pending transactions we have at present. - mapping (bytes32 => Transaction) public m_txs; + mapping (bytes32 => Transaction) m_txs; } )DELIMITER"; @@ -443,7 +515,7 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer) // 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"); + h256 opHash("8f27f478ebcfaf28b0c354f4809ace8087000d668b89c8bc3b1b608bfdbe6654"); 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)); diff --git a/libsolidity/solidityExecutionFramework.h b/libsolidity/solidityExecutionFramework.h index 200940a43..03a201c72 100644 --- a/libsolidity/solidityExecutionFramework.h +++ b/libsolidity/solidityExecutionFramework.h @@ -145,6 +145,12 @@ public: { return bytes(); } + //@todo might be extended in the future + template + static bytes encodeDyn(Arg const& _arg) + { + return encodeArgs(u256(0x20), u256(_arg.size()), _arg); + } private: template