mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge branch 'develop' into client_ref
Conflicts: libethereum/Client.cpp
This commit is contained in:
commit
d5683e5e2e
@ -72,6 +72,7 @@ add_executable(testeth ${SRC_LIST} ${HEADERS})
|
|||||||
|
|
||||||
target_link_libraries(testeth ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
|
target_link_libraries(testeth ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES})
|
||||||
target_link_libraries(testeth ${CURL_LIBRARIES})
|
target_link_libraries(testeth ${CURL_LIBRARIES})
|
||||||
|
target_link_libraries(testeth ${CRYPTOPP_LIBRARIES})
|
||||||
target_link_libraries(testeth ethereum)
|
target_link_libraries(testeth ethereum)
|
||||||
target_link_libraries(testeth ethcore)
|
target_link_libraries(testeth ethcore)
|
||||||
if (NOT WIN32)
|
if (NOT WIN32)
|
||||||
|
@ -199,10 +199,10 @@ void ImportTest::importState(json_spirit::mObject& _o, State& _state, stateOptio
|
|||||||
stateOptions.m_bHasCode = true;
|
stateOptions.m_bHasCode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (code.size())
|
if (!code.empty())
|
||||||
{
|
{
|
||||||
_state.m_cache[address] = Account(balance, Account::ContractConception);
|
_state.m_cache[address] = Account(balance, Account::ContractConception);
|
||||||
_state.m_cache[address].setCode(code);
|
_state.m_cache[address].setCode(std::move(code));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_state.m_cache[address] = Account(balance, Account::NormalCreation);
|
_state.m_cache[address] = Account(balance, Account::NormalCreation);
|
||||||
@ -728,10 +728,20 @@ Options::Options()
|
|||||||
for (auto i = 0; i < argc; ++i)
|
for (auto i = 0; i < argc; ++i)
|
||||||
{
|
{
|
||||||
auto arg = std::string{argv[i]};
|
auto arg = std::string{argv[i]};
|
||||||
if (arg == "--jit")
|
if (arg == "--vm" && i + 1 < argc)
|
||||||
eth::VMFactory::setKind(eth::VMKind::JIT);
|
{
|
||||||
else if (arg == "--vm=smart")
|
string vmKind = argv[++i];
|
||||||
eth::VMFactory::setKind(eth::VMKind::Smart);
|
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")
|
else if (arg == "--vmtrace")
|
||||||
vmtrace = true;
|
vmtrace = true;
|
||||||
else if (arg == "--filltests")
|
else if (arg == "--filltests")
|
||||||
|
@ -188,6 +188,7 @@ void doTransactionTests(json_spirit::mValue& _v, bool _fillin);
|
|||||||
void doStateTests(json_spirit::mValue& v, bool _fillin);
|
void doStateTests(json_spirit::mValue& v, bool _fillin);
|
||||||
void doVMTests(json_spirit::mValue& v, bool _fillin);
|
void doVMTests(json_spirit::mValue& v, bool _fillin);
|
||||||
void doBlockchainTests(json_spirit::mValue& _v, bool _fillin);
|
void doBlockchainTests(json_spirit::mValue& _v, bool _fillin);
|
||||||
|
void doRlpTests(json_spirit::mValue& v, bool _fillin);
|
||||||
|
|
||||||
template<typename mapType>
|
template<typename mapType>
|
||||||
void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs)
|
void checkAddresses(mapType& _expectedAddrs, mapType& _resultAddrs)
|
||||||
|
@ -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_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)
|
BOOST_AUTO_TEST_CASE(inc_dec_operators)
|
||||||
{
|
{
|
||||||
char const* sourceCode = R"(
|
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_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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ BOOST_AUTO_TEST_CASE(large_string_literal)
|
|||||||
char const* text = "contract test {\n"
|
char const* text = "contract test {\n"
|
||||||
" function f() { var x = \"123456789012345678901234567890123\"; }"
|
" function f() { var x = \"123456789012345678901234567890123\"; }"
|
||||||
"}\n";
|
"}\n";
|
||||||
BOOST_CHECK_THROW(parseTextAndResolveNames(text), TypeError);
|
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_AUTO_TEST_CASE(balance)
|
BOOST_AUTO_TEST_CASE(balance)
|
||||||
@ -2056,6 +2056,60 @@ BOOST_AUTO_TEST_CASE(memory_arrays_not_resizeable)
|
|||||||
BOOST_CHECK_THROW(parseTextAndResolveNames(sourceCode), TypeError);
|
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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
// some number (specified in constructor) of the set of owners (specified in the constructor, modifiable) before the
|
||||||
// interior is executed.
|
// interior is executed.
|
||||||
contract multiowned {
|
contract multiowned {
|
||||||
|
|
||||||
|
// TYPES
|
||||||
|
|
||||||
// struct for the status of a pending operation.
|
// struct for the status of a pending operation.
|
||||||
struct PendingState {
|
struct PendingState {
|
||||||
uint yetNeeded;
|
uint yetNeeded;
|
||||||
uint ownersDone;
|
uint ownersDone;
|
||||||
uint index;
|
uint index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EVENTS
|
||||||
|
|
||||||
// this contract only has five types of events: it can accept a confirmation, in which case
|
// this contract only has five types of events: it can accept a confirmation, in which case
|
||||||
// we record owner and operation (hash) alongside it.
|
// we record owner and operation (hash) alongside it.
|
||||||
event Confirmation(address owner, bytes32 operation);
|
event Confirmation(address owner, bytes32 operation);
|
||||||
@ -63,14 +69,9 @@ contract multiowned {
|
|||||||
event OwnerRemoved(address oldOwner);
|
event OwnerRemoved(address oldOwner);
|
||||||
// the last one is emitted if the required signatures change
|
// the last one is emitted if the required signatures change
|
||||||
event RequirementChanged(uint newRequirement);
|
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.
|
// MODIFIERS
|
||||||
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.
|
// simple single-sig function modifier.
|
||||||
modifier onlyowner {
|
modifier onlyowner {
|
||||||
if (isOwner(msg.sender))
|
if (isOwner(msg.sender))
|
||||||
@ -80,9 +81,21 @@ contract multiowned {
|
|||||||
// that later attempts can be realised as the same underlying operation and
|
// that later attempts can be realised as the same underlying operation and
|
||||||
// thus count as confirmations.
|
// thus count as confirmations.
|
||||||
modifier onlymanyowners(bytes32 _operation) {
|
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
|
// Revokes a prior confirmation of the given operation
|
||||||
function revoke(bytes32 _operation) external {
|
function revoke(bytes32 _operation) external {
|
||||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||||
@ -96,7 +109,75 @@ contract multiowned {
|
|||||||
Revoke(msg.sender, _operation);
|
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:
|
// determine what index the present sender is:
|
||||||
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
uint ownerIndex = m_ownerIndex[uint(msg.sender)];
|
||||||
// make sure they're an owner
|
// 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) {
|
function reorganizeOwners() private returns (bool) {
|
||||||
uint free = 1;
|
uint free = 1;
|
||||||
while (free < m_numOwners)
|
while (free < m_numOwners)
|
||||||
@ -182,6 +228,7 @@ contract multiowned {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearPending() internal {
|
function clearPending() internal {
|
||||||
uint length = m_pendingIndex.length;
|
uint length = m_pendingIndex.length;
|
||||||
for (uint i = 0; i < length; ++i)
|
for (uint i = 0; i < length; ++i)
|
||||||
@ -189,46 +236,54 @@ contract multiowned {
|
|||||||
delete m_pending[m_pendingIndex[i]];
|
delete m_pending[m_pendingIndex[i]];
|
||||||
delete m_pendingIndex;
|
delete m_pendingIndex;
|
||||||
}
|
}
|
||||||
function changeRequirement(uint _newRequired) onlymanyowners(sha3(msg.data)) external {
|
|
||||||
if (_newRequired > m_numOwners) return;
|
// FIELDS
|
||||||
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.
|
// the number of owners that must confirm the same operation before it is run.
|
||||||
uint public m_required;
|
uint public m_required;
|
||||||
// pointer used to find a free slot in m_owners
|
// pointer used to find a free slot in m_owners
|
||||||
uint public m_numOwners;
|
uint public m_numOwners;
|
||||||
|
|
||||||
// list of owners
|
// list of owners
|
||||||
uint[256] public m_owners;
|
uint[256] m_owners;
|
||||||
uint constant c_maxOwners = 250;
|
uint constant c_maxOwners = 250;
|
||||||
// index on the list of owners to allow reverse lookup
|
// index on the list of owners to allow reverse lookup
|
||||||
mapping(uint => uint) public m_ownerIndex;
|
mapping(uint => uint) m_ownerIndex;
|
||||||
// the ongoing operations.
|
// the ongoing operations.
|
||||||
mapping(bytes32 => PendingState) public m_pending;
|
mapping(bytes32 => PendingState) m_pending;
|
||||||
bytes32[] public m_pendingIndex;
|
bytes32[] m_pendingIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// inheritable "property" contract that enables methods to be protected by placing a linear limit (specifiable)
|
// 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
|
// 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.
|
// uses is specified in the modifier.
|
||||||
contract daylimit is multiowned {
|
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.
|
// constructor - just records the present day's index.
|
||||||
function daylimit() {
|
function daylimit() {
|
||||||
m_lastDay = today();
|
m_lastDay = today();
|
||||||
}
|
}
|
||||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent 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;
|
m_dailyLimit = _newLimit;
|
||||||
}
|
}
|
||||||
// (re)sets the daily limit. needs many of the owners to confirm. doesn't alter the amount already spent today.
|
// (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;
|
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
|
// 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.
|
// returns true. otherwise just returns false.
|
||||||
function underLimit(uint _value) internal onlyowner returns (bool) {
|
function underLimit(uint _value) internal onlyowner returns (bool) {
|
||||||
@ -244,60 +299,76 @@ contract daylimit is multiowned {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// simple modifier for daily limit.
|
|
||||||
modifier limitedDaily(uint _value) {
|
|
||||||
if (underLimit(_value))
|
|
||||||
_
|
|
||||||
}
|
|
||||||
// determines today's index.
|
// determines today's index.
|
||||||
function today() private constant returns (uint) { return now / 1 days; }
|
function today() private constant returns (uint) { return now / 1 days; }
|
||||||
uint public m_spentToday;
|
|
||||||
|
// FIELDS
|
||||||
|
|
||||||
uint public m_dailyLimit;
|
uint public m_dailyLimit;
|
||||||
uint public m_lastDay;
|
uint m_spentToday;
|
||||||
|
uint m_lastDay;
|
||||||
}
|
}
|
||||||
|
|
||||||
// interface contract for multisig proxy contracts; see below for docs.
|
// interface contract for multisig proxy contracts; see below for docs.
|
||||||
contract multisig {
|
contract multisig {
|
||||||
event Deposit(address from, uint value);
|
|
||||||
event SingleTransact(address owner, uint value, address to, bytes data);
|
// EVENTS
|
||||||
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:
|
// logged events:
|
||||||
// Funds has arrived into the wallet (record how much).
|
// Funds has arrived into the wallet (record how much).
|
||||||
event Deposit(address from, uint value);
|
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).
|
// 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);
|
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).
|
// 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);*/
|
event MultiTransact(address owner, bytes32 operation, uint value, address to, bytes data);
|
||||||
// constructor - just pass on the owner arra to the multiowned.
|
// Confirmation still needed for a transaction.
|
||||||
event Created();
|
event ConfirmationNeeded(bytes32 operation, address initiator, uint value, address to, bytes data);
|
||||||
function Wallet() {
|
|
||||||
Created();
|
// 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`.
|
// 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);
|
suicide(_to);
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets called when no other function matches
|
// gets called when no other function matches
|
||||||
function() {
|
function() {
|
||||||
// just being sent some cash?
|
// just being sent some cash?
|
||||||
if (msg.value > 0)
|
if (msg.value > 0)
|
||||||
Deposit(msg.sender, msg.value);
|
Deposit(msg.sender, msg.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Outside-visible transact entry point. Executes transacion immediately if below daily spend limit.
|
// 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
|
// 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
|
// 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;
|
return 0;
|
||||||
}
|
}
|
||||||
// determine our operation hash.
|
// determine our operation hash.
|
||||||
_r = sha3(msg.data);
|
_r = sha3(msg.data, block.number);
|
||||||
if (!confirm(_r) && m_txs[_r].to == 0) {
|
if (!confirm(_r) && m_txs[_r].to == 0) {
|
||||||
m_txs[_r].to = _to;
|
m_txs[_r].to = _to;
|
||||||
m_txs[_r].value = _value;
|
m_txs[_r].value = _value;
|
||||||
@ -319,6 +390,7 @@ contract Wallet is multisig, multiowned, daylimit {
|
|||||||
ConfirmationNeeded(_r, msg.sender, _value, _to, _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
|
// 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.
|
// to determine the body of the transaction from the hash provided.
|
||||||
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
|
function confirm(bytes32 _h) onlymanyowners(_h) returns (bool) {
|
||||||
@ -329,20 +401,20 @@ contract Wallet is multisig, multiowned, daylimit {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// INTERNAL METHODS
|
||||||
|
|
||||||
function clearPending() internal {
|
function clearPending() internal {
|
||||||
uint length = m_pendingIndex.length;
|
uint length = m_pendingIndex.length;
|
||||||
for (uint i = 0; i < length; ++i)
|
for (uint i = 0; i < length; ++i)
|
||||||
delete m_txs[m_pendingIndex[i]];
|
delete m_txs[m_pendingIndex[i]];
|
||||||
super.clearPending();
|
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) {
|
// FIELDS
|
||||||
// _to.call.value(_value)(_data);
|
|
||||||
// MultiTransact("out", msg.sender, _h, _value, _to);
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// pending transactions we have at present.
|
// pending transactions we have at present.
|
||||||
mapping (bytes32 => Transaction) public m_txs;
|
mapping (bytes32 => Transaction) m_txs;
|
||||||
}
|
}
|
||||||
)DELIMITER";
|
)DELIMITER";
|
||||||
|
|
||||||
@ -443,7 +515,7 @@ BOOST_AUTO_TEST_CASE(multisig_value_transfer)
|
|||||||
// 4 owners, set required to 3
|
// 4 owners, set required to 3
|
||||||
BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
|
BOOST_REQUIRE(callContractFunction("changeRequirement(uint256)", u256(3)) == encodeArgs());
|
||||||
// check that balance is and stays zero at destination address
|
// 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);
|
BOOST_CHECK_EQUAL(m_state.balance(Address(0x05)), 0);
|
||||||
m_sender = Address(0x12);
|
m_sender = Address(0x12);
|
||||||
BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
|
BOOST_REQUIRE(callContractFunction("execute(address,uint256,bytes)", h256(0x05), 100, 0x60, 0x00) == encodeArgs(opHash));
|
||||||
|
@ -145,6 +145,12 @@ public:
|
|||||||
{
|
{
|
||||||
return bytes();
|
return bytes();
|
||||||
}
|
}
|
||||||
|
//@todo might be extended in the future
|
||||||
|
template <class Arg>
|
||||||
|
static bytes encodeDyn(Arg const& _arg)
|
||||||
|
{
|
||||||
|
return encodeArgs(u256(0x20), u256(_arg.size()), _arg);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <class CppFunction, class... Args>
|
template <class CppFunction, class... Args>
|
||||||
|
Loading…
Reference in New Issue
Block a user