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