Merge commit 'a1736a94298fefb3c33adbf22e9333ced55c15bd' into MergeUpstream3

This commit is contained in:
Doyle 2020-05-22 10:44:14 +10:00
commit 0c3e3db2eb
25 changed files with 1823 additions and 53 deletions

View File

@ -591,7 +591,7 @@ KV_SERIALIZE_MAP_CODE_BEGIN(txpool_stats)
KV_SERIALIZE(num_10m)
KV_SERIALIZE(num_not_relayed)
KV_SERIALIZE(histo_98pc)
KV_SERIALIZE_CONTAINER_POD_AS_BLOB(histo)
KV_SERIALIZE(histo)
KV_SERIALIZE(num_double_spends)
KV_SERIALIZE_MAP_CODE_END()

View File

@ -64,7 +64,7 @@ namespace rpc {
// has its own version, and that clients can just test major to see
// whether they can talk to a given daemon without having to know in
// advance which version they will stop working with
constexpr version_t VERSION = {3, 6};
constexpr version_t VERSION = {3, 7};
/// Makes a version array from a packed 32-bit integer version
constexpr version_t make_version(uint32_t version)

View File

@ -3280,6 +3280,21 @@ bool wallet2::add_address_book_row(const cryptonote::account_public_address &add
return false;
}
bool wallet2::set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress)
{
wallet2::address_book_row a;
a.m_address = address;
a.m_payment_id = payment_id;
a.m_description = description;
a.m_is_subaddress = is_subaddress;
const auto size = m_address_book.size();
if (row_id >= size)
return false;
m_address_book[row_id] = a;
return true;
}
bool wallet2::delete_address_book_row(std::size_t row_id) {
if(m_address_book.size() <= row_id)
return false;

View File

@ -1273,6 +1273,7 @@ private:
*/
std::vector<address_book_row> get_address_book() const { return m_address_book; }
bool add_address_book_row(const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress);
bool set_address_book_row(size_t row_id, const cryptonote::account_public_address &address, const crypto::hash &payment_id, const std::string &description, bool is_subaddress);
bool delete_address_book_row(std::size_t row_id);
uint64_t get_num_rct_outputs();

View File

@ -2711,6 +2711,108 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_edit_address_book(const wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);
if (m_restricted)
{
er.code = WALLET_RPC_ERROR_CODE_DENIED;
er.message = "Command unavailable in restricted mode.";
return false;
}
const auto ab = m_wallet->get_address_book();
if (req.index >= ab.size())
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_INDEX;
er.message = "Index out of range: " + std::to_string(req.index);
return false;
}
tools::wallet2::address_book_row entry = ab[req.index];
cryptonote::address_parse_info info;
crypto::hash payment_id = crypto::null_hash;
if (req.set_address)
{
er.message = "";
if(!get_account_address_from_str_or_url(info, m_wallet->nettype(), req.address,
[&er](const std::string &url, const std::vector<std::string> &addresses, bool dnssec_valid)->std::string {
if (!dnssec_valid)
{
er.message = std::string("Invalid DNSSEC for ") + url;
return {};
}
if (addresses.empty())
{
er.message = std::string("No Monero address found at ") + url;
return {};
}
return addresses[0];
}))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
if (er.message.empty())
er.message = std::string("WALLET_RPC_ERROR_CODE_WRONG_ADDRESS: ") + req.address;
return false;
}
entry.m_address = info.address;
entry.m_is_subaddress = info.is_subaddress;
if (info.has_payment_id)
{
memcpy(entry.m_payment_id.data, info.payment_id.data, 8);
memset(entry.m_payment_id.data + 8, 0, 24);
}
}
if (req.set_payment_id)
{
if (req.payment_id.empty())
{
payment_id = crypto::null_hash;
}
else
{
if (req.set_address && info.has_payment_id)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Separate payment ID given with integrated address";
return false;
}
if (!wallet2::parse_long_payment_id(req.payment_id, payment_id))
{
crypto::hash8 spid;
if (!wallet2::parse_short_payment_id(req.payment_id, spid))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Payment id has invalid format: \"" + req.payment_id + "\", expected 64 character string";
return false;
}
else
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_PAYMENT_ID;
er.message = "Payment id has invalid format: standalone short payment IDs are forbidden, they must be part of an integrated address";
return false;
}
}
}
entry.m_payment_id = payment_id;
}
if (req.set_description)
entry.m_description = req.description;
if (!m_wallet->set_address_book_row(req.index, entry.m_address, entry.m_payment_id, entry.m_description, entry.m_is_subaddress))
{
er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR;
er.message = "Failed to edit address book entry";
return false;
}
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx)
{
if (!m_wallet) return not_open(er);

View File

@ -130,6 +130,7 @@ namespace tools
MAP_JON_RPC_WE("parse_uri", on_parse_uri, wallet_rpc::COMMAND_RPC_PARSE_URI)
MAP_JON_RPC_WE("get_address_book", on_get_address_book, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("add_address_book", on_add_address_book, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("edit_address_book", on_edit_address_book, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("delete_address_book",on_delete_address_book,wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY)
MAP_JON_RPC_WE("refresh", on_refresh, wallet_rpc::COMMAND_RPC_REFRESH)
MAP_JON_RPC_WE("auto_refresh", on_auto_refresh, wallet_rpc::COMMAND_RPC_AUTO_REFRESH)
@ -229,6 +230,7 @@ namespace tools
bool on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_add_address_book(const wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_ADD_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_edit_address_book(const wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_delete_address_book(const wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_DELETE_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_refresh(const wallet_rpc::COMMAND_RPC_REFRESH::request& req, wallet_rpc::COMMAND_RPC_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);
bool on_auto_refresh(const wallet_rpc::COMMAND_RPC_AUTO_REFRESH::request& req, wallet_rpc::COMMAND_RPC_AUTO_REFRESH::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL);

View File

@ -48,7 +48,7 @@
// advance which version they will stop working with
// Don't go over 32767 for any of these
#define WALLET_RPC_VERSION_MAJOR 1
#define WALLET_RPC_VERSION_MINOR 14
#define WALLET_RPC_VERSION_MINOR 15
#define MAKE_WALLET_RPC_VERSION(major,minor) (((major)<<16)|(minor))
#define WALLET_RPC_VERSION MAKE_WALLET_RPC_VERSION(WALLET_RPC_VERSION_MAJOR, WALLET_RPC_VERSION_MINOR)
namespace tools
@ -1838,6 +1838,38 @@ namespace wallet_rpc
};
};
LOKI_RPC_DOC_INTROSPECT
// Edit a entry in the address book.
struct COMMAND_RPC_EDIT_ADDRESS_BOOK_ENTRY
{
struct request
{
uint64_t index;
bool set_address;
std::string address;
bool set_payment_id;
std::string payment_id;
bool set_description;
std::string description;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(index)
KV_SERIALIZE(set_address)
KV_SERIALIZE(address)
KV_SERIALIZE(set_payment_id)
KV_SERIALIZE(payment_id)
KV_SERIALIZE(set_description)
KV_SERIALIZE(description)
END_KV_SERIALIZE_MAP()
};
struct response
{
BEGIN_KV_SERIALIZE_MAP()
END_KV_SERIALIZE_MAP()
};
};
LOKI_RPC_DOC_INTROSPECT
// Retrieves entries from the address book.
struct COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY

View File

@ -49,3 +49,7 @@ else()
message(WARNING "functional_tests_rpc skipped, needs the 'requests' python3 module")
set(CTEST_CUSTOM_TESTS_IGNORE ${CTEST_CUSTOM_TESTS_IGNORE} functional_tests_rpc)
endif()
add_test(
NAME check_missing_rpc_methods
COMMAND ${PYTHON_EXECUTABLE} "${CMAKE_CURRENT_SOURCE_DIR}/check_missing_rpc_methods.py" "${CMAKE_SOURCE_DIR}")

View File

@ -0,0 +1,304 @@
#!/usr/bin/env python3
#encoding=utf-8
# Copyright (c) 2019 The Monero Project
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Test wallet address book RPC
"""
from __future__ import print_function
from framework.wallet import Wallet
class AddressBookTest():
def run_test(self):
self.create()
self.test_address_book()
def create(self):
print('Creating wallet')
wallet = Wallet()
# close the wallet if any, will throw if none is loaded
try: wallet.close_wallet()
except: pass
seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
res = wallet.restore_deterministic_wallet(seed = seed)
assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert res.seed == seed
def test_address_book(self):
print('Testing address book')
wallet = Wallet()
# empty at start
res = wallet.get_address_book()
assert not 'entries' in res or (res.entries) == 0
ok = False
try: wallet.get_address_book([0])
except: ok = True
assert ok
ok = False
try: wallet.delete_address_book(0)
except: ok = True
assert ok
ok = False
try: wallet.edit_address_book(0, description = '')
except: ok = True
assert ok
# add one
res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = 'self')
assert res.index == 0
for get_all in [True, False]:
res = wallet.get_address_book() if get_all else wallet.get_address_book([0])
assert len(res.entries) == 1
e = res.entries[0]
assert e.index == 0
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert e.payment_id == '' or e.payment_id == '0' * 16 or e.payment_id == '0' * 64
assert e.description == 'self'
# add a duplicate
res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = 'self')
assert res.index == 1
res = wallet.get_address_book()
assert len(res.entries) == 2
assert res.entries[0].index == 0
assert res.entries[1].index == 1
assert res.entries[0].address == res.entries[1].address
assert res.entries[0].payment_id == res.entries[1].payment_id
assert res.entries[0].description == res.entries[1].description
e = res.entries[1]
res = wallet.get_address_book([1])
assert len(res.entries) == 1
assert e == res.entries[0]
# request (partially) out of range
ok = False
try: res = wallet.get_address_book[4, 2]
except: ok = True
assert ok
ok = False
try: res = wallet.get_address_book[0, 2]
except: ok = True
assert ok
ok = False
try: res = wallet.get_address_book[2, 0]
except: ok = True
assert ok
# delete first
res = wallet.delete_address_book(0)
res = wallet.get_address_book()
assert len(res.entries) == 1
assert res.entries[0].index == 0
assert res.entries[0].address == e.address
assert res.entries[0].payment_id == e.payment_id
assert res.entries[0].description == e.description
# delete (new) first
res = wallet.delete_address_book(0)
res = wallet.get_address_book()
assert not 'entries' in res or (res.entries) == 0
# add non-addresses
errors = 0
try: wallet.add_address_book('', description = 'bad')
except: errors += 1
try: wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm ', description = 'bad')
except: errors += 1
try: wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDn', description = 'bad')
except: errors += 1
try: wallet.add_address_book('9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB', description = 'bad')
except: errors += 1
try: wallet.add_address_book('donate@example.com', description = 'bad')
except: errors += 1
assert errors == 5
res = wallet.get_address_book()
assert not 'entries' in res or len(res.entries) == 0
# openalias
res = wallet.add_address_book('donate@getmonero.org', description = 'dev fund')
assert res.index == 0
res = wallet.get_address_book()
assert len(res.entries) == 1
e = res.entries[0]
assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
assert e.description == 'dev fund'
# UTF-8
res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', description = u'あまやかす')
assert res.index == 1
res = wallet.get_address_book([1])
assert len(res.entries) == 1
assert res.entries[0].description == u'あまやかす'
e = res.entries[0]
# duplicate request
res = wallet.get_address_book([1, 1])
assert len(res.entries) == 2
assert res.entries[0] == e
assert res.entries[1] == e
# payment IDs
res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 64)
assert res.index == 2
ok = False
try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = 'x' * 64)
except: ok = True
assert ok
ok = False
try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 65)
except: ok = True
assert ok
ok = False
try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 63)
except: ok = True
assert ok
ok = False
try: res = wallet.add_address_book('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', payment_id = '0' * 16)
except: ok = True
assert ok
# various address types
res = wallet.make_integrated_address()
integrated_address = res.integrated_address
integrated_address_payment_id = res.payment_id
ok = False
try: res = wallet.add_address_book(integrated_address, payment_id = '0' * 64)
except: ok = True
assert ok
res = wallet.add_address_book(integrated_address)
assert res.index == 3
res = wallet.add_address_book('87KfgTZ8ER5D3Frefqnrqif11TjVsTPaTcp37kqqKMrdDRUhpJRczeR7KiBmSHF32UJLP3HHhKUDmEQyJrv2mV8yFDCq8eB')
assert res.index == 4
# get them back
res = wallet.get_address_book([0])
assert len(res.entries) == 1
assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
assert res.entries[0].description == 'dev fund'
res = wallet.get_address_book([1])
assert len(res.entries) == 1
assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert res.entries[0].description == u'あまやかす'
res = wallet.get_address_book([2])
assert len(res.entries) == 1
assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
res = wallet.get_address_book([3])
assert len(res.entries) == 1
if False: # for now, the address book splits integrated addresses
assert res.entries[0].address == integrated_address
else:
assert res.entries[0].address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert res.entries[0].payment_id == integrated_address_payment_id + '0' * 48
res = wallet.get_address_book([4])
assert len(res.entries) == 1
assert res.entries[0].address == '87KfgTZ8ER5D3Frefqnrqif11TjVsTPaTcp37kqqKMrdDRUhpJRczeR7KiBmSHF32UJLP3HHhKUDmEQyJrv2mV8yFDCq8eB'
# edit
res = wallet.get_address_book([1])
assert len(res.entries) == 1
e = res.entries[0]
assert e.index == 1
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert e.payment_id == '0' * 64
assert e.description == u'あまやかす'
res = wallet.edit_address_book(1, payment_id = '1' * 64)
res = wallet.get_address_book([1])
assert len(res.entries) == 1
e = res.entries[0]
assert e.index == 1
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert e.payment_id == '1' * 64
assert e.description == u'あまやかす'
res = wallet.edit_address_book(1, description = '')
res = wallet.get_address_book([1])
assert len(res.entries) == 1
e = res.entries[0]
assert e.index == 1
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert e.payment_id == '1' * 64
assert e.description == ''
res = wallet.edit_address_book(1, description = 'えんしゅう')
res = wallet.get_address_book([1])
assert len(res.entries) == 1
e = res.entries[0]
assert e.index == 1
assert e.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert e.payment_id == '1' * 64
assert e.description == u'えんしゅう'
res = wallet.edit_address_book(1, address = '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A')
res = wallet.get_address_book([1])
assert len(res.entries) == 1
e = res.entries[0]
assert e.index == 1
assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
assert e.payment_id == '1' * 64
assert e.description == u'えんしゅう'
res = wallet.edit_address_book(1, payment_id = '')
res = wallet.get_address_book([1])
assert len(res.entries) == 1
e = res.entries[0]
assert e.index == 1
assert e.address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
assert e.payment_id == '0' * 64
assert e.description == u'えんしゅう'
ok = False
try: res = wallet.edit_address_book(1, address = '')
except: ok = True
assert ok
ok = False
try: res = wallet.edit_address_book(1, payment_id = 'asdnd')
except: ok = True
assert ok
ok = False
try: res = wallet.edit_address_book(1, address = 'address')
except: ok = True
assert ok
res = wallet.edit_address_book(1)
res = wallet.get_address_book([1])
assert len(res.entries) == 1
assert e == res.entries[0]
# empty
wallet.delete_address_book(4)
wallet.delete_address_book(0)
res = wallet.get_address_book([0]) # entries above the deleted one collapse one slot up
assert len(res.entries) == 1
assert res.entries[0].address == '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
assert res.entries[0].description == u'えんしゅう'
wallet.delete_address_book(2)
wallet.delete_address_book(0)
wallet.delete_address_book(0)
res = wallet.get_address_book()
assert not 'entries' in res or len(res.entries) == 0
if __name__ == '__main__':
AddressBookTest().run_test()

View File

@ -53,7 +53,8 @@ class BlockchainTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
daemon.pop_blocks(1000)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def _test_generateblocks(self, blocks):
@ -330,6 +331,9 @@ class BlockchainTest():
for txid in [alt_blocks[0], alt_blocks[2], alt_blocks[4]]:
assert len([chain for chain in res.chains if chain.block_hash == txid]) == 1
print('Saving blockchain explicitely')
daemon.save_bc()
if __name__ == '__main__':
BlockchainTest().run_test()

View File

@ -0,0 +1,50 @@
#!/usr/bin/env python
from __future__ import print_function
import sys
import re
USAGE = 'usage: check_untested_methods.py <rootdir>'
try:
rootdir = sys.argv[1]
except:
print(USAGE)
sys.exit(1)
sys.path.insert(0, rootdir + '/utils/python-rpc')
from framework import daemon
from framework import wallet
modules = [
{
'name': 'daemon',
'object': daemon.Daemon(),
'path': rootdir + '/src/rpc/core_rpc_server.h',
'ignore': []
},
{
'name': 'wallet',
'object': wallet.Wallet(),
'path': rootdir + '/src/wallet/wallet_rpc_server.h',
'ignore': []
}
]
error = False
for module in modules:
for line in open(module['path']).readlines():
if 'MAP_URI_AUTO_JON2' in line or 'MAP_JON_RPC' in line:
match = re.search('.*\"(.*)\".*', line)
name = match.group(1)
if name in module['ignore'] or name.endswith('.bin'):
continue
if 'MAP_URI_AUTO_JON2' in line:
if not name.startswith('/'):
print('Error: %s does not start with /' % name)
error = True
name = name[1:]
if not hasattr(module['object'], name):
print('Error: %s API method %s does not have a matching function' % (module['name'], name))
sys.exit(1 if error else 0)

View File

@ -45,7 +45,8 @@ class ColdSigningTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
daemon.pop_blocks(1000)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self, idx):

View File

@ -10,7 +10,7 @@ import string
import os
USAGE = 'usage: functional_tests_rpc.py <python> <srcdir> <builddir> [<tests-to-run> | all]'
DEFAULT_TESTS = ['bans', 'daemon_info', 'blockchain', 'wallet_address', 'integrated_address', 'mining', 'transfer', 'txpool', 'multisig', 'cold_signing', 'sign_message', 'proofs', 'get_output_distribution']
DEFAULT_TESTS = ['address_book', 'bans', 'blockchain', 'cold_signing', 'daemon_info', 'get_output_distribution', 'integrated_address', 'mining', 'multisig', 'proofs', 'sign_message', 'transfer', 'txpool', 'uri', 'validate_address', 'wallet']
try:
python = sys.argv[1]
srcdir = sys.argv[2]
@ -36,9 +36,10 @@ except:
N_MONERODS = 1
N_WALLETS = 4
WALLET_DIRECTORY = builddir + "/functional-tests-directory"
monerod_base = [builddir + "/bin/monerod", "--regtest", "--fixed-difficulty", "1", "--offline", "--no-igd", "--p2p-bind-port", "monerod_p2p_port", "--rpc-bind-port", "monerod_rpc_port", "--zmq-rpc-bind-port", "monerod_zmq_port", "--non-interactive", "--disable-dns-checkpoints", "--check-updates", "disabled", "--rpc-ssl", "disabled", "--log-level", "1"]
wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", builddir + "/functional-tests-directory", "--rpc-bind-port", "wallet_port", "--disable-rpc-login", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--daemon-port", "18180", "--log-level", "1"]
wallet_base = [builddir + "/bin/monero-wallet-rpc", "--wallet-dir", WALLET_DIRECTORY, "--rpc-bind-port", "wallet_port", "--disable-rpc-login", "--rpc-ssl", "disabled", "--daemon-ssl", "disabled", "--daemon-port", "18180", "--log-level", "1"]
command_lines = []
processes = []
@ -62,6 +63,8 @@ try:
PYTHONPATH += ':'
PYTHONPATH += srcdir + '/../../utils/python-rpc'
os.environ['PYTHONPATH'] = PYTHONPATH
os.environ['WALLET_DIRECTORY'] = WALLET_DIRECTORY
os.environ['PYTHONIOENCODING'] = 'utf-8'
for i in range(len(command_lines)):
#print('Running: ' + str(command_lines[i]))
processes.append(subprocess.Popen(command_lines[i], stdout = outputs[i]))
@ -133,6 +136,6 @@ else:
if len(FAIL) == 0:
print('Done, ' + str(len(PASS)) + '/' + str(len(tests)) + ' tests passed')
else:
print('Done, ' + str(len(FAIL)) + '/' + str(len(tests)) + ' tests failed: ' + string.join(FAIL, ', '))
print('Done, ' + str(len(FAIL)) + '/' + str(len(tests)) + ' tests failed: ' + ', '.join(FAIL))
sys.exit(0 if len(FAIL) == 0 else 1)

View File

@ -44,7 +44,8 @@ class GetOutputDistributionTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
daemon.pop_blocks(1000)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):

View File

@ -46,12 +46,15 @@ class MiningTest():
def run_test(self):
self.reset()
self.create()
self.mine()
self.mine(True)
self.mine(False)
self.submitblock()
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
daemon.pop_blocks(1000)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):
@ -62,8 +65,8 @@ class MiningTest():
except: pass
res = wallet.restore_deterministic_wallet(seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted')
def mine(self):
print("Test mining")
def mine(self, via_daemon):
print("Test mining via " + ("daemon" if via_daemon else "wallet"))
daemon = Daemon()
wallet = Wallet()
@ -76,7 +79,10 @@ class MiningTest():
res_status = daemon.mining_status()
res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1)
if via_daemon:
res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1)
else:
res = wallet.start_mining(threads_count = 1)
res_status = daemon.mining_status()
assert res_status.active == True
@ -101,7 +107,11 @@ class MiningTest():
timeout -= 1
assert timeout >= 0
res = daemon.stop_mining()
if via_daemon:
res = daemon.stop_mining()
else:
res = wallet.stop_mining()
res_status = daemon.mining_status()
assert res_status.active == False
@ -113,7 +123,10 @@ class MiningTest():
balance = res_getbalance.balance
assert balance >= prev_balance + (new_height - prev_height) * 600000000000
res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1, do_background_mining = True)
if via_daemon:
res = daemon.start_mining('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', threads_count = 1, do_background_mining = True)
else:
res = wallet.start_mining(threads_count = 1, do_background_mining = True)
res_status = daemon.mining_status()
assert res_status.active == True
assert res_status.threads_count == 1
@ -122,10 +135,40 @@ class MiningTest():
assert res_status.block_reward >= 600000000000
# don't wait, might be a while if the machine is busy, which it probably is
res = daemon.stop_mining()
if via_daemon:
res = daemon.stop_mining()
else:
res = wallet.stop_mining()
res_status = daemon.mining_status()
assert res_status.active == False
def submitblock(self):
print("Test submitblock")
daemon = Daemon()
res = daemon.get_height()
height = res.height
res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 5)
assert len(res.blocks) == 5
hashes = res.blocks
blocks = []
for block_hash in hashes:
res = daemon.getblock(hash = block_hash)
assert len(res.blob) > 0 and len(res.blob) % 2 == 0
blocks.append(res.blob)
res = daemon.get_height()
assert res.height == height + 5
res = daemon.pop_blocks(5)
res = daemon.get_height()
assert res.height == height
for i in range(len(hashes)):
block_hash = hashes[i]
assert len(block_hash) == 64
res = daemon.submitblock(blocks[i])
res = daemon.get_height()
assert res.height == height + i + 1
assert res.hash == block_hash
if __name__ == '__main__':
MiningTest().run_test()

View File

@ -46,6 +46,8 @@ class MultisigTest():
self.mine('4ADHswEU3XBUee8pudBkZQd9beJainqNo1BQKkHJujAEPJyQrLj9U4dNm8HEMdHuWwKMFGzMUB3RCTvcTaW9kHpdRUDxgjW', 5)
self.mine('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 60)
self.test_states()
self.create_multisig_wallets(2, 2, '493DsrfJPqiN3Suv9RcRDoZEbQtKZX1sNcGPA3GhkKYEEmivk8kjQrTdRdVc4ZbmzWJuE157z9NNUKmF2VDfdYDR3CziGMk')
self.import_multisig_info([1, 0], 5)
txid = self.transfer([1, 0])
@ -79,7 +81,8 @@ class MultisigTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
daemon.pop_blocks(1000)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def mine(self, address, blocks):
@ -152,6 +155,72 @@ class MultisigTest():
assert res.threshold == M_threshold
assert res.total == N_total
def test_states(self):
print('Testing multisig states')
seeds = [
'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted',
'peeled mixture ionic radar utopia puddle buying illness nuns gadget river spout cavernous bounced paradise drunk looking cottage jump tequila melting went winter adjust spout',
'dilute gutter certain antics pamphlet macro enjoy left slid guarded bogeys upload nineteen bomb jubilee enhanced irritate turnip eggs swung jukebox loudly reduce sedan slid',
]
info = []
wallet = [None, None, None]
for i in range(3):
wallet[i] = Wallet(idx = i)
try: wallet[i].close_wallet()
except: pass
res = wallet[i].restore_deterministic_wallet(seed = seeds[i])
res = wallet[i].is_multisig()
assert not res.multisig
res = wallet[i].prepare_multisig()
assert len(res.multisig_info) > 0
info.append(res.multisig_info)
for i in range(3):
ok = False
try: res = wallet[i].finalize_multisig(info)
except: ok = True
assert ok
ok = False
try: res = wallet[i].exchange_multisig_keys(info)
except: ok = True
assert ok
res = wallet[i].is_multisig()
assert not res.multisig
res = wallet[0].make_multisig(info[0:2], 2)
res = wallet[0].is_multisig()
assert res.multisig
assert res.ready
ok = False
try: res = wallet[0].finalize_multisig(info)
except: ok = True
assert ok
ok = False
try: res = wallet[0].prepare_multisig()
except: ok = True
assert ok
ok = False
try: res = wallet[0].make_multisig(info[0:2], 2)
except: ok = True
assert ok
res = wallet[1].make_multisig(info, 2)
res = wallet[1].is_multisig()
assert res.multisig
assert not res.ready
ok = False
try: res = wallet[1].prepare_multisig()
except: ok = True
assert ok
ok = False
try: res = wallet[1].make_multisig(info[0:2], 2)
except: ok = True
assert ok
def import_multisig_info(self, signers, expected_outputs):
assert len(signers) >= 2

View File

@ -44,12 +44,14 @@ class ProofsTest():
txid, tx_key, amount = self.transfer()
self.check_tx_key(txid, tx_key, amount)
self.check_tx_proof(txid, amount)
self.check_spend_proof(txid)
self.check_reserve_proof()
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
daemon.pop_blocks(1000)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def mine(self, address, blocks):
@ -217,6 +219,40 @@ class ProofsTest():
except: ok = True
assert ok or not res.good
def check_spend_proof(self, txid):
daemon = Daemon()
print('Checking spend proof')
self.wallet[0].refresh()
self.wallet[1].refresh()
res = self.wallet[0].get_spend_proof(txid, message = 'foo')
assert len(res.signature) > 0
signature = res.signature
res = self.wallet[1].check_spend_proof(txid, message = 'foo', signature = signature)
assert res.good
res = self.wallet[0].get_spend_proof(txid, message = 'foobar')
assert len(res.signature) > 0
signature2 = res.signature
res = self.wallet[1].check_spend_proof(txid, message = 'foobar', signature = signature2)
assert res.good
ok = False
try: res = self.wallet[1].check_spend_proof('0' * 64, message = 'foo', signature = signature)
except: ok = True
assert ok or not res.good
ok = False
try: res = self.wallet[1].check_spend_proof(txid, message = 'bar', signature = signature)
except: ok = True
assert ok or not res.good
ok = False
try: res = self.wallet[1].check_spend_proof(txid, message = 'foo', signature = signature2)
except: ok = True
assert ok or not res.good
def check_reserve_proof(self):
daemon = Daemon()

View File

@ -47,14 +47,27 @@ from framework.wallet import Wallet
class SpeedTest():
def set_test_params(self):
self.num_nodes = 1
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def run_test(self):
self.reset()
daemon = Daemon()
wallet = Wallet()
destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1,3)
# close the wallet if any, will throw if none is loaded
try: wallet.close_wallet()
except: pass
wallet.restore_deterministic_wallet('velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted')
destinations = []
for i in range(3):
destinations.append({"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'})
self._test_speed_generateblocks(daemon=daemon, blocks=70)
for i in range(1, 10):
@ -69,7 +82,6 @@ class SpeedTest():
start = time.time()
res = daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', blocks)
# wallet seed: velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted
print('generating ', blocks, 'blocks took: ', time.time() - start, 'seconds')
@ -77,7 +89,7 @@ class SpeedTest():
print('Test speed of transfer')
start = time.time()
destinations = wallet.make_uniform_destinations('44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A',1)
destinations = [{"amount":1,"address":'44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'}]
res = wallet.transfer_split(destinations)
print('generating tx took: ', time.time() - start, 'seconds')

View File

@ -44,14 +44,20 @@ class TransferTest():
self.mine()
self.transfer()
self.check_get_bulk_payments()
self.check_get_payments()
self.check_double_spend_detection()
self.sweep_dust()
self.sweep_single()
self.check_destinations()
self.check_tx_notes()
self.check_rescan()
self.check_is_key_image_spent()
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
daemon.pop_blocks(1000)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):
@ -114,7 +120,7 @@ class TransferTest():
except: ok = True
assert ok
res = self.wallet[0].transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = False)
res = self.wallet[0].transfer([dst], ring_size = 11, payment_id = payment_id, get_tx_key = False, get_tx_hex = True)
assert len(res.tx_hash) == 32*2
txid = res.tx_hash
assert len(res.tx_key) == 0
@ -122,12 +128,19 @@ class TransferTest():
amount = res.amount
assert res.fee > 0
fee = res.fee
assert len(res.tx_blob) == 0
assert len(res.tx_blob) > 0
blob_size = len(res.tx_blob) // 2
assert len(res.tx_metadata) == 0
assert len(res.multisig_txset) == 0
assert len(res.unsigned_txset) == 0
unsigned_txset = res.unsigned_txset
res = daemon.get_fee_estimate(10)
assert res.fee > 0
assert res.quantization_mask > 0
expected_fee = (res.fee * 1 * blob_size + res.quantization_mask - 1) // res.quantization_mask * res.quantization_mask
assert abs(1 - fee / expected_fee) < 0.01
self.wallet[0].refresh()
res = daemon.get_info()
@ -523,6 +536,28 @@ class TransferTest():
res = self.wallet[1].get_bulk_payments(["1111111122222222"])
assert len(res.payments) >= 1 # we have one of these
def check_get_payments(self):
print('Checking get_payments')
daemon = Daemon()
res = daemon.get_info()
height = res.height
self.wallet[0].refresh()
self.wallet[1].refresh()
res = self.wallet[0].get_payments('1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde')
assert 'payments' not in res or len(res.payments) == 0
res = self.wallet[1].get_payments('1234500000012345abcde00000abcdeff1234500000012345abcde00000abcde')
assert len(res.payments) >= 2
res = self.wallet[1].get_payments('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
assert 'payments' not in res or len(res.payments) == 0
res = self.wallet[1].get_payments(payment_id = '1111111122222222' + '0'*48)
assert len(res.payments) >= 1 # one tx to integrated address
def check_double_spend_detection(self):
print('Checking double spend detection')
txes = [[None, None], [None, None]]
@ -583,6 +618,13 @@ class TransferTest():
assert tx.in_pool
assert tx.double_spend_seen
def sweep_dust(self):
print("Sweeping dust")
daemon = Daemon()
self.wallet[0].refresh()
res = self.wallet[0].sweep_dust()
assert not 'tx_hash_list' in res or len(res.tx_hash_list) == 0 # there's just one, but it cannot meet the fee
def sweep_single(self):
daemon = Daemon()
@ -603,11 +645,19 @@ class TransferTest():
self.wallet[0].refresh()
res = self.wallet[0].get_balance()
balance = res.balance
res = self.wallet[0].incoming_transfers(transfer_type = 'all')
res = daemon.is_key_image_spent([ki])
assert len(res.spent_status) == 1
assert res.spent_status[0] == 0
res = self.wallet[0].sweep_single('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', key_image = ki)
assert len(res.tx_hash) == 64
tx_hash = res.tx_hash
res = daemon.is_key_image_spent([ki])
assert len(res.spent_status) == 1
assert res.spent_status[0] == 2
daemon.generateblocks('44Kbx4sJ7JDRDV5aAhLJzQCjDz2ViLRduE3ijDZu3osWKBjMGkV1XPk4pfDUMqt1Aiezvephdqm6YD19GKFD9ZcXVUTp6BW', 1)
res = daemon.is_key_image_spent([ki])
assert len(res.spent_status) == 1
assert res.spent_status[0] == 1
self.wallet[0].refresh()
res = self.wallet[0].get_balance()
new_balance = res.balance
@ -687,7 +737,97 @@ class TransferTest():
daemon.generateblocks('42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 1)
self.wallet[0].refresh()
def check_tx_notes(self):
daemon = Daemon()
print('Testing tx notes')
res = self.wallet[0].get_transfers()
assert len(res['in']) > 0
in_txid = res['in'][0].txid
assert len(res['out']) > 0
out_txid = res['out'][0].txid
res = self.wallet[0].get_tx_notes([in_txid, out_txid])
assert res.notes == ['', '']
res = self.wallet[0].set_tx_notes([in_txid, out_txid], ['in txid', 'out txid'])
res = self.wallet[0].get_tx_notes([in_txid, out_txid])
assert res.notes == ['in txid', 'out txid']
res = self.wallet[0].get_tx_notes([out_txid, in_txid])
assert res.notes == ['out txid', 'in txid']
def check_rescan(self):
daemon = Daemon()
print('Testing rescan_spent')
res = self.wallet[0].incoming_transfers(transfer_type = 'all')
transfers = res.transfers
res = self.wallet[0].rescan_spent()
res = self.wallet[0].incoming_transfers(transfer_type = 'all')
assert transfers == res.transfers
for hard in [False, True]:
print('Testing %s rescan_blockchain' % ('hard' if hard else 'soft'))
res = self.wallet[0].incoming_transfers(transfer_type = 'all')
transfers = res.transfers
res = self.wallet[0].get_transfers()
t_in = res['in']
t_out = res.out
res = self.wallet[0].rescan_blockchain(hard = hard)
res = self.wallet[0].incoming_transfers(transfer_type = 'all')
assert transfers == res.transfers
res = self.wallet[0].get_transfers()
assert t_in == res['in']
# some information can not be recovered for out txes
unrecoverable_fields = ['payment_id', 'destinations', 'note']
old_t_out = []
for x in t_out:
e = {}
for k in x.keys():
if not k in unrecoverable_fields:
e[k] = x[k]
old_t_out.append(e)
new_t_out = []
for x in res.out:
e = {}
for k in x.keys():
if not k in unrecoverable_fields:
e[k] = x[k]
new_t_out.append(e)
assert sorted(old_t_out, key = lambda k: k['txid']) == sorted(new_t_out, key = lambda k: k['txid'])
def check_is_key_image_spent(self):
daemon = Daemon()
print('Testing is_key_image_spent')
res = self.wallet[0].incoming_transfers(transfer_type = 'all')
transfers = res.transfers
ki = [x.key_image for x in transfers]
expected = [1 if x.spent else 0 for x in transfers]
res = daemon.is_key_image_spent(ki)
assert res.spent_status == expected
res = self.wallet[0].incoming_transfers(transfer_type = 'available')
transfers = res.transfers
ki = [x.key_image for x in transfers]
expected = [0 for x in transfers]
res = daemon.is_key_image_spent(ki)
assert res.spent_status == expected
res = self.wallet[0].incoming_transfers(transfer_type = 'unavailable')
transfers = res.transfers
ki = [x.key_image for x in transfers]
expected = [1 for x in transfers]
res = daemon.is_key_image_spent(ki)
assert res.spent_status == expected
ki = [ki[-1]] * 5
expected = [1] * len(ki)
res = daemon.is_key_image_spent(ki)
assert res.spent_status == expected
ki = ['2'*64, '1'*64]
expected = [0, 0]
res = daemon.is_key_image_spent(ki)
assert res.spent_status == expected
if __name__ == '__main__':

View File

@ -46,7 +46,8 @@ class TransferTest():
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
daemon.pop_blocks(1000)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):
@ -81,6 +82,26 @@ class TransferTest():
return txes
def check_empty_pool(self):
daemon = Daemon()
res = daemon.get_transaction_pool_hashes()
assert not 'tx_hashes' in res or len(res.tx_hashes) == 0
res = daemon.get_transaction_pool_stats()
assert res.pool_stats.bytes_total == 0
assert res.pool_stats.bytes_min == 0
assert res.pool_stats.bytes_max == 0
assert res.pool_stats.bytes_med == 0
assert res.pool_stats.fee_total == 0
assert res.pool_stats.oldest == 0
assert res.pool_stats.txs_total == 0
assert res.pool_stats.num_failing == 0
assert res.pool_stats.num_10m == 0
assert res.pool_stats.num_not_relayed == 0
assert res.pool_stats.histo_98pc == 0
assert not 'histo' in res.pool_stats or len(res.pool_stats.histo) == 0
assert res.pool_stats.num_double_spends == 0
def check_txpool(self):
daemon = Daemon()
wallet = Wallet()
@ -89,6 +110,8 @@ class TransferTest():
height = res.height
txpool_size = res.tx_pool_size
self.check_empty_pool()
txes = self.create_txes('46r4nYSevkfBUMhuykdK3gQ98XDqDTYW1hNLaXNvjpsJaSbNtdXh1sKMsdVgqkaihChAzEy29zEDPMR3NHQvGoZCLGwTerK', 5)
res = daemon.get_info()
@ -97,6 +120,10 @@ class TransferTest():
res = daemon.get_transaction_pool()
assert len(res.transactions) == txpool_size
total_bytes = 0
total_fee = 0
min_bytes = 99999999999999
max_bytes = 0
for txid in txes.keys():
x = [x for x in res.transactions if x.id_hash == txid]
assert len(x) == 1
@ -110,9 +137,26 @@ class TransferTest():
assert x.fee == txes[txid].fee
assert x.tx_blob == txes[txid].tx_blob
total_bytes += x.blob_size
total_fee += x.fee
min_bytes = min(min_bytes, x.blob_size)
max_bytes = max(max_bytes, x.blob_size)
res = daemon.get_transaction_pool_hashes()
assert sorted(res.tx_hashes) == sorted(txes.keys())
res = daemon.get_transaction_pool_stats()
assert res.pool_stats.bytes_total == total_bytes
assert res.pool_stats.bytes_min == min_bytes
assert res.pool_stats.bytes_max == max_bytes
assert res.pool_stats.bytes_med >= min_bytes and res.pool_stats.bytes_med <= max_bytes
assert res.pool_stats.fee_total == total_fee
assert res.pool_stats.txs_total == len(txes)
assert res.pool_stats.num_failing == 0
assert res.pool_stats.num_10m == 0
assert res.pool_stats.num_not_relayed == 0
assert res.pool_stats.num_double_spends == 0
print('Flushing 2 transactions')
txes_keys = list(txes.keys())
daemon.flush_txpool([txes_keys[1], txes_keys[3]])
@ -127,6 +171,42 @@ class TransferTest():
res = daemon.get_transaction_pool_hashes()
assert sorted(res.tx_hashes) == sorted(new_keys)
res = daemon.get_transaction_pool()
assert len(res.transactions) == len(new_keys)
total_bytes = 0
total_fee = 0
min_bytes = 99999999999999
max_bytes = 0
for txid in new_keys:
x = [x for x in res.transactions if x.id_hash == txid]
assert len(x) == 1
x = x[0]
assert x.kept_by_block == False
assert x.last_failed_id_hash == '0'*64
assert x.double_spend_seen == False
assert x.weight >= x.blob_size
assert x.blob_size * 2 == len(txes[txid].tx_blob)
assert x.fee == txes[txid].fee
assert x.tx_blob == txes[txid].tx_blob
total_bytes += x.blob_size
total_fee += x.fee
min_bytes = min(min_bytes, x.blob_size)
max_bytes = max(max_bytes, x.blob_size)
res = daemon.get_transaction_pool_stats()
assert res.pool_stats.bytes_total == total_bytes
assert res.pool_stats.bytes_min == min_bytes
assert res.pool_stats.bytes_max == max_bytes
assert res.pool_stats.bytes_med >= min_bytes and res.pool_stats.bytes_med <= max_bytes
assert res.pool_stats.fee_total == total_fee
assert res.pool_stats.txs_total == len(new_keys)
assert res.pool_stats.num_failing == 0
assert res.pool_stats.num_10m == 0
assert res.pool_stats.num_not_relayed == 0
assert res.pool_stats.num_double_spends == 0
print('Flushing unknown transactions')
unknown_txids = ['1'*64, '2'*64, '3'*64]
daemon.flush_txpool(unknown_txids)
@ -140,6 +220,8 @@ class TransferTest():
res = daemon.get_transaction_pool_hashes()
assert not 'tx_hashes' in res or len(res.tx_hashes) == 0
self.check_empty_pool()
print('Popping block')
daemon.pop_blocks(1)
res = daemon.get_transaction_pool_hashes()
@ -159,6 +241,9 @@ class TransferTest():
assert x.fee == txes[txid].fee
assert x.tx_blob == txes[txid].tx_blob
daemon.flush_txpool()
self.check_empty_pool()
if __name__ == '__main__':
TransferTest().run_test()

234
tests/functional_tests/uri.py Executable file
View File

@ -0,0 +1,234 @@
#!/usr/bin/env python3
#encoding=utf-8
# Copyright (c) 2019 The Monero Project
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this list of
# conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
# of conditions and the following disclaimer in the documentation and/or other
# materials provided with the distribution.
#
# 3. Neither the name of the copyright holder nor the names of its contributors may be
# used to endorse or promote products derived from this software without specific
# prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Test URI RPC
"""
from __future__ import print_function
try:
from urllib import quote as urllib_quote
except:
from urllib.parse import quote as urllib_quote
from framework.wallet import Wallet
class URITest():
def run_test(self):
self.create()
self.test_monero_uri()
def create(self):
print('Creating wallet')
wallet = Wallet()
# close the wallet if any, will throw if none is loaded
try: wallet.close_wallet()
except: pass
seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
res = wallet.restore_deterministic_wallet(seed = seed)
assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert res.seed == seed
def test_monero_uri(self):
print('Testing monero: URI')
wallet = Wallet()
utf8string = [u'えんしゅう', u'あまやかす']
quoted_utf8string = [urllib_quote(x.encode('utf8')) for x in utf8string]
ok = False
try: res = wallet.make_uri()
except: ok = True
assert ok
ok = False
try: res = wallet.make_uri(address = '')
except: ok = True
assert ok
ok = False
try: res = wallet.make_uri(address = 'kjshdkj')
except: ok = True
assert ok
for address in [
'42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
'4BxSHvcgTwu25WooY4BVmgdcKwZu5EksVZSZkDd6ooxSVVqQ4ubxXkhLF6hEqtw96i9cf3cVfLw8UWe95bdDKfRQeYtPwLm1Jiw7AKt2LY',
'8AsN91rznfkBGTY8psSNkJBg9SZgxxGGRUhGwRptBhgr5XSQ1XzmA9m8QAnoxydecSh5aLJXdrgXwTDMMZ1AuXsN1EX5Mtm'
]:
res = wallet.make_uri(address = address)
assert res.uri == 'monero:' + address
res = wallet.parse_uri(res.uri)
assert res.uri.address == address
assert res.uri.payment_id == ''
assert res.uri.amount == 0
assert res.uri.tx_description == ''
assert res.uri.recipient_name == ''
assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
res = wallet.make_uri(address = address, amount = 11000000000)
assert res.uri == 'monero:' + address + '?tx_amount=0.011' or res.uri == 'monero:' + address + '?tx_amount=0.011000000000'
res = wallet.parse_uri(res.uri)
assert res.uri.address == address
assert res.uri.payment_id == ''
assert res.uri.amount == 11000000000
assert res.uri.tx_description == ''
assert res.uri.recipient_name == ''
assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
address = '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
res = wallet.make_uri(address = address, tx_description = utf8string[0])
assert res.uri == 'monero:' + address + '?tx_description=' + quoted_utf8string[0]
res = wallet.parse_uri(res.uri)
assert res.uri.address == address
assert res.uri.payment_id == ''
assert res.uri.amount == 0
assert res.uri.tx_description == utf8string[0]
assert res.uri.recipient_name == ''
assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
res = wallet.make_uri(address = address, recipient_name = utf8string[0])
assert res.uri == 'monero:' + address + '?recipient_name=' + quoted_utf8string[0]
res = wallet.parse_uri(res.uri)
assert res.uri.address == address
assert res.uri.payment_id == ''
assert res.uri.amount == 0
assert res.uri.tx_description == ''
assert res.uri.recipient_name == utf8string[0]
assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
res = wallet.make_uri(address = address, recipient_name = utf8string[0], tx_description = utf8string[1])
assert res.uri == 'monero:' + address + '?recipient_name=' + quoted_utf8string[0] + '&tx_description=' + quoted_utf8string[1]
res = wallet.parse_uri(res.uri)
assert res.uri.address == address
assert res.uri.payment_id == ''
assert res.uri.amount == 0
assert res.uri.tx_description == utf8string[1]
assert res.uri.recipient_name == utf8string[0]
assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
res = wallet.make_uri(address = address, recipient_name = utf8string[0], tx_description = utf8string[1], amount = 1000000000000)
assert res.uri == 'monero:' + address + '?tx_amount=1.000000000000&recipient_name=' + quoted_utf8string[0] + '&tx_description=' + quoted_utf8string[1]
res = wallet.parse_uri(res.uri)
assert res.uri.address == address
assert res.uri.payment_id == ''
assert res.uri.amount == 1000000000000
assert res.uri.tx_description == utf8string[1]
assert res.uri.recipient_name == utf8string[0]
assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
res = wallet.make_uri(address = address, recipient_name = utf8string[0], tx_description = utf8string[1], amount = 1000000000000, payment_id = '1' * 64)
assert res.uri == 'monero:' + address + '?tx_payment_id=' + '1' * 64 + '&tx_amount=1.000000000000&recipient_name=' + quoted_utf8string[0] + '&tx_description=' + quoted_utf8string[1]
res = wallet.parse_uri(res.uri)
assert res.uri.address == address
assert res.uri.payment_id == '1' * 64
assert res.uri.amount == 1000000000000
assert res.uri.tx_description == utf8string[1]
assert res.uri.recipient_name == utf8string[0]
assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
# spaces must be encoded as %20
res = wallet.make_uri(address = address, tx_description = ' ' + utf8string[1] + ' ' + utf8string[0] + ' ', amount = 1000000000000)
assert res.uri == 'monero:' + address + '?tx_amount=1.000000000000&tx_description=%20' + quoted_utf8string[1] + '%20' + quoted_utf8string[0] + '%20'
res = wallet.parse_uri(res.uri)
assert res.uri.address == address
assert res.uri.payment_id == ''
assert res.uri.amount == 1000000000000
assert res.uri.tx_description == ' ' + utf8string[1] + ' ' + utf8string[0] + ' '
assert res.uri.recipient_name == ''
assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
# the example from the docs
res = wallet.parse_uri('monero:46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em?tx_amount=239.39014&tx_description=donation')
assert res.uri.address == '46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy66qeFQkn6sfK8aHYjA3jk3o1Bv16em'
assert res.uri.amount == 239390140000000
assert res.uri.tx_description == 'donation'
assert res.uri.recipient_name == ''
assert res.uri.payment_id == ''
assert not 'unknown_parameters' in res or len(res.unknown_parameters) == 0
# malformed/invalid
for uri in [
'',
':',
'monero',
'notmonero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
'MONERO:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
'MONERO::42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm',
'monero:',
'monero:badaddress',
'monero:tx_amount=10',
'monero:?tx_amount=10',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=-1',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=1e12',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=+12',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=1+2',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=A',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=0x2',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=222222222222222222222',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDn?tx_amount=10',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=10=',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=10=&',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm&tx_amount=10=&foo=bar',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_amount=10&tx_amount=20',
'monero:42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm?tx_payment_id=1111111111111111',
'monero:4BxSHvcgTwu25WooY4BVmgdcKwZu5EksVZSZkDd6ooxSVVqQ4ubxXkhLF6hEqtw96i9cf3cVfLw8UWe95bdDKfRQeYtPwLm1Jiw7AKt2LY?tx_payment_id=' + '1' * 64,
'monero:9ujeXrjzf7bfeK3KZdCqnYaMwZVFuXemPU8Ubw335rj2FN1CdMiWNyFV3ksEfMFvRp9L9qum5UxkP5rN9aLcPxbH1au4WAB',
'monero:5K8mwfjumVseCcQEjNbf59Um6R9NfVUNkHTLhhPCmNvgDLVS88YW5tScnm83rw9mfgYtchtDDTW5jEfMhygi27j1QYphX38hg6m4VMtN29',
'monero:7A1Hr63MfgUa8pkWxueD5xBqhQczkusYiCMYMnJGcGmuQxa7aDBxN1G7iCuLCNB3VPeb2TW7U9FdxB27xKkWKfJ8VhUZthF',
]:
ok = False
try: res = wallet.parse_uri(uri)
except: ok = True
assert ok, res
# unknown parameters but otherwise valid
res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&foo=bar')
assert res.uri.address == address
assert res.uri.amount == 239390140000000
assert res.unknown_parameters == ['foo=bar'], res
res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&foo=bar&baz=quux')
assert res.uri.address == address
assert res.uri.amount == 239390140000000
assert res.unknown_parameters == ['foo=bar', 'baz=quux'], res
res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&%20=%20')
assert res.uri.address == address
assert res.uri.amount == 239390140000000
assert res.unknown_parameters == ['%20=%20'], res
res = wallet.parse_uri('monero:' + address + '?tx_amount=239.39014&unknown=' + quoted_utf8string[0])
assert res.uri.address == address
assert res.uri.amount == 239390140000000
assert res.unknown_parameters == [u'unknown=' + quoted_utf8string[0]], res
if __name__ == '__main__':
URITest().run_test()

View File

@ -28,11 +28,10 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import time
"""Test address validation RPC calls
"""
from __future__ import print_function
from framework.wallet import Wallet
class AddressValidationTest():

View File

@ -29,31 +29,54 @@
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""Test transaction creation RPC calls
Test the following RPCs:
- [TODO: many tests still need to be written]
"""Test basic wallet functionality
"""
from __future__ import print_function
import sys
import os
import errno
from framework.wallet import Wallet
from framework.daemon import Daemon
class WalletAddressTest():
class WalletTest():
def run_test(self):
self.reset()
self.create()
self.check_main_address()
self.check_keys()
self.create_subaddresses()
self.tags()
self.attributes()
self.open_close()
self.languages()
self.change_password()
self.store()
def remove_file(self, name):
WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
assert WALLET_DIRECTORY != ''
try:
os.unlink(WALLET_DIRECTORY + '/' + name)
except OSError as e:
if e.errno != errno.ENOENT:
raise
def remove_wallet_files(self, name):
for suffix in ['', '.keys']:
self.remove_file(name + suffix)
def file_exists(self, name):
WALLET_DIRECTORY = os.environ['WALLET_DIRECTORY']
assert WALLET_DIRECTORY != ''
return os.path.isfile(WALLET_DIRECTORY + '/' + name)
def reset(self):
print('Resetting blockchain')
daemon = Daemon()
daemon.pop_blocks(1000)
res = daemon.get_height()
daemon.pop_blocks(res.height - 1)
daemon.flush_txpool()
def create(self):
@ -158,6 +181,103 @@ class WalletAddressTest():
res = wallet.get_address_index('82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf')
assert res.index == {'major': 1, 'minor': 0}
res = wallet.label_account(0, "main")
def tags(self):
print('Testing tags')
wallet = Wallet()
res = wallet.get_account_tags()
assert not 'account_tags' in res or len(res.account_tags) == 0
ok = False
try: res = wallet.get_accounts('tag')
except: ok = True
assert ok or not 'subaddress_accounts' in res or res.subaddress_accounts == 0
wallet.tag_accounts('tag0', [1])
res = wallet.get_account_tags()
assert len(res.account_tags) == 1
assert res.account_tags[0].tag == 'tag0'
assert res.account_tags[0].label == ''
assert res.account_tags[0].accounts == [1]
res = wallet.get_accounts('tag0')
assert len(res.subaddress_accounts) == 1
assert res.subaddress_accounts[0].account_index == 1
assert res.subaddress_accounts[0].base_address == '82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf'
assert res.subaddress_accounts[0].balance == 0
assert res.subaddress_accounts[0].unlocked_balance == 0
assert res.subaddress_accounts[0].label == 'idx1_new'
assert res.subaddress_accounts[0].tag == 'tag0'
wallet.untag_accounts([0])
res = wallet.get_account_tags()
assert len(res.account_tags) == 1
assert res.account_tags[0].tag == 'tag0'
assert res.account_tags[0].label == ''
assert res.account_tags[0].accounts == [1]
wallet.untag_accounts([1])
res = wallet.get_account_tags()
assert not 'account_tags' in res or len(res.account_tags) == 0
wallet.tag_accounts('tag0', [0])
wallet.tag_accounts('tag1', [1])
res = wallet.get_account_tags()
assert len(res.account_tags) == 2
x = [x for x in res.account_tags if x.tag == 'tag0']
assert len(x) == 1
assert x[0].tag == 'tag0'
assert x[0].label == ''
assert x[0].accounts == [0]
x = [x for x in res.account_tags if x.tag == 'tag1']
assert len(x) == 1
assert x[0].tag == 'tag1'
assert x[0].label == ''
assert x[0].accounts == [1]
wallet.tag_accounts('tagA', [0, 1])
res = wallet.get_account_tags()
assert len(res.account_tags) == 1
assert res.account_tags[0].tag == 'tagA'
assert res.account_tags[0].label == ''
assert res.account_tags[0].accounts == [0, 1]
wallet.tag_accounts('tagB', [1, 0])
res = wallet.get_account_tags()
assert len(res.account_tags) == 1
assert res.account_tags[0].tag == 'tagB'
assert res.account_tags[0].label == ''
assert res.account_tags[0].accounts == [0, 1]
wallet.set_account_tag_description('tagB', 'tag B')
res = wallet.get_account_tags()
assert len(res.account_tags) == 1
assert res.account_tags[0].tag == 'tagB'
assert res.account_tags[0].label == 'tag B'
assert res.account_tags[0].accounts == [0, 1]
res = wallet.get_accounts('tagB')
assert len(res.subaddress_accounts) == 2
subaddress_accounts = []
for x in res.subaddress_accounts:
assert x.balance == 0
assert x.unlocked_balance == 0
subaddress_accounts.append((x.account_index, x.base_address, x.label))
assert sorted(subaddress_accounts) == [(0, '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm', 'main'), (1, '82pP87g1Vkd3LUMssBCumk3MfyEsFqLAaGDf6oxddu61EgSFzt8gCwUD4tr3kp9TUfdPs2CnpD7xLZzyC1Ei9UsW3oyCWDf', 'idx1_new')]
def attributes(self):
print('Testing attributes')
wallet = Wallet()
ok = False
try: res = wallet.get_attribute('foo')
except: ok = True
assert ok
res = wallet.set_attribute('foo', 'bar')
res = wallet.get_attribute('foo')
assert res.value == 'bar'
res = wallet.set_attribute('foo', 'いっしゅん')
res = wallet.get_attribute('foo')
assert res.value == u'いっしゅん'
ok = False
try: res = wallet.get_attribute('いちりゅう')
except: ok = True
assert ok
res = wallet.set_attribute('いちりゅう', 'いっぽう')
res = wallet.get_attribute('いちりゅう')
assert res.value == u'いっぽう'
def open_close(self):
print('Testing open/close')
wallet = Wallet()
@ -200,11 +320,72 @@ class WalletAddressTest():
languages = res.languages
languages_local = res.languages_local
for language in languages + languages_local:
print('Creating ' + language.encode('utf8') + ' wallet')
sys.stdout.write('Creating ' + language + ' wallet\n')
wallet.create_wallet(filename = '', language = language)
res = wallet.query_key('mnemonic')
wallet.close_wallet()
def change_password(self):
print('Testing password change')
wallet = Wallet()
# close the wallet if any, will throw if none is loaded
try: wallet.close_wallet()
except: pass
self.remove_wallet_files('test1')
seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1')
assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert res.seed == seed
wallet.close_wallet()
res = wallet.open_wallet('test1', password = '')
res = wallet.get_address()
assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
res = wallet.change_wallet_password(old_password = '', new_password = 'foo')
wallet.close_wallet()
ok = False
try: res = wallet.open_wallet('test1', password = '')
except: ok = True
assert ok
res = wallet.open_wallet('test1', password = 'foo')
res = wallet.get_address()
assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
wallet.close_wallet()
self.remove_wallet_files('test1')
def store(self):
print('Testing store')
wallet = Wallet()
# close the wallet if any, will throw if none is loaded
try: wallet.close_wallet()
except: pass
self.remove_wallet_files('test1')
seed = 'velvet lymph giddy number token physics poetry unquoted nibs useful sabotage limits benches lifestyle eden nitrogen anvil fewest avoid batch vials washing fences goat unquoted'
res = wallet.restore_deterministic_wallet(seed = seed, filename = 'test1')
assert res.address == '42ey1afDFnn4886T7196doS9GPMzexD9gXpsZJDwVjeRVdFCSoHnv7KPbBeGpzJBzHRCAs9UxqeoyFQMYbqSWYTfJJQAWDm'
assert res.seed == seed
self.remove_file('test1')
assert self.file_exists('test1.keys')
assert not self.file_exists('test1')
wallet.store()
assert self.file_exists('test1.keys')
assert self.file_exists('test1')
wallet.close_wallet()
self.remove_wallet_files('test1')
if __name__ == '__main__':
WalletAddressTest().run_test()
WalletTest().run_test()

View File

@ -49,6 +49,7 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(getblocktemplate)
get_block_template = getblocktemplate
def send_raw_transaction(self, tx_as_hex, do_not_relay = False, do_sanity_checks = True):
send_raw_transaction = {
@ -57,6 +58,7 @@ class Daemon(object):
'do_sanity_checks': do_sanity_checks,
}
return self.rpc.send_request("/send_raw_transaction", send_raw_transaction)
sendrawtransaction = send_raw_transaction
def submitblock(self, block):
submitblock = {
@ -66,6 +68,7 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(submitblock)
submit_block = submitblock
def getblock(self, hash = '', height = 0, fill_pow_hash = False):
getblock = {
@ -79,6 +82,7 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(getblock)
get_block = getblock
def getlastblockheader(self):
getlastblockheader = {
@ -89,6 +93,7 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(getlastblockheader)
get_last_block_header = getlastblockheader
def getblockheaderbyhash(self, hash = "", hashes = []):
getblockheaderbyhash = {
@ -101,6 +106,7 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(getblockheaderbyhash)
get_block_header_by_hash = getblockheaderbyhash
def getblockheaderbyheight(self, height):
getblockheaderbyheight = {
@ -112,6 +118,7 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(getblockheaderbyheight)
get_block_header_by_height = getblockheaderbyheight
def getblockheadersrange(self, start_height, end_height, fill_pow_hash = False):
getblockheadersrange = {
@ -125,6 +132,7 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(getblockheadersrange)
get_block_headers_range = getblockheadersrange
def get_connections(self):
get_connections = {
@ -141,6 +149,7 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(get_info)
getinfo = get_info
def hard_fork_info(self):
hard_fork_info = {
@ -172,6 +181,7 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_request("/get_height", get_height)
getheight = get_height
def pop_blocks(self, nblocks = 1):
pop_blocks = {
@ -208,6 +218,11 @@ class Daemon(object):
}
return self.rpc.send_request('/get_transaction_pool_hashes', get_transaction_pool_hashes)
def get_transaction_pool_stats(self):
get_transaction_pool_stats = {
}
return self.rpc.send_request('/get_transaction_pool_stats', get_transaction_pool_stats)
def flush_txpool(self, txids = []):
flush_txpool = {
'method': 'flush_txpool',
@ -256,6 +271,7 @@ class Daemon(object):
'split': split,
}
return self.rpc.send_request('/get_transactions', get_transactions)
gettransactions = get_transactions
def get_outs(self, outputs = [], get_txid = False):
get_outs = {
@ -344,3 +360,141 @@ class Daemon(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(get_fee_estimate)
def is_key_image_spent(self, key_images = []):
is_key_image_spent = {
'key_images': key_images,
}
return self.rpc.send_request('/is_key_image_spent', is_key_image_spent)
def save_bc(self):
save_bc = {
}
return self.rpc.send_request('/save_bc', save_bc)
def get_peer_list(self):
get_peer_list = {
}
return self.rpc.send_request('/get_peer_list', get_peer_list)
def set_log_hash_rate(self, visible):
set_log_hash_rate = {
'visible': visible,
}
return self.rpc.send_request('/set_log_hash_rate', set_log_hash_rate)
def stop_daemon(self):
stop_daemon = {
}
return self.rpc.send_request('/stop_daemon', stop_daemon)
def get_net_stats(self):
get_net_stats = {
}
return self.rpc.send_request('/get_net_stats', get_net_stats)
def get_limit(self):
get_limit = {
}
return self.rpc.send_request('/get_limit', get_limit)
def set_limit(self, limit_down, limit_up):
set_limit = {
'limit_down': limit_down,
'limit_up': limit_up,
}
return self.rpc.send_request('/set_limit', set_limit)
def out_peers(self, out_peers):
out_peers = {
'out_peers': out_peers,
}
return self.rpc.send_request('/out_peers', out_peers)
def in_peers(self, in_peers):
in_peers = {
'in_peers': in_peers,
}
return self.rpc.send_request('/in_peers', in_peers)
def update(self, command, path = None):
update = {
'command': command,
'path': path,
}
return self.rpc.send_request('/update', update)
def get_block_count(self):
get_block_count = {
'method': 'get_block_count',
'params': {
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(get_block_count)
getblockcount = get_block_count
def get_block_hash(self, height):
get_block_hash = {
'method': 'get_block_hash',
'params': [height],
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(get_block_hash)
on_get_block_hash = get_block_hash
on_getblockhash = get_block_hash
def relay_tx(self, txids = []):
relay_tx = {
'method': 'relay_tx',
'params': {
'txids': txids,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(relay_tx)
def sync_info(self):
sync_info = {
'method': 'sync_info',
'params': {
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(sync_info)
def get_txpool_backlog(self):
get_txpool_backlog = {
'method': 'get_txpool_backlog',
'params': {
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(get_txpool_backlog)
def prune_blockchain(self, check = False):
prune_blockchain = {
'method': 'prune_blockchain',
'params': {
'check': check,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(prune_blockchain)
def get_block_rate(self, seconds = [3600]):
get_block_rate = {
'method': 'get_block_rate',
'params': {
'seconds': seconds,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(get_block_rate)

View File

@ -37,18 +37,6 @@ class Wallet(object):
self.port = port
self.rpc = JSONRPC('{protocol}://{host}:{port}'.format(protocol=protocol, host=host, port=port if port else 18090+idx))
def make_uniform_destinations(self, address, transfer_amount, transfer_number_of_destinations=1):
destinations = []
for i in range(transfer_number_of_destinations):
destinations.append({"amount":transfer_amount,"address":address})
return destinations
def make_destinations(self, addresses, transfer_amounts):
destinations = []
for i in range(len(addresses)):
destinations.append({'amount':transfer_amounts[i],'address':addresses[i]})
return destinations
def transfer(self, destinations, account_index = 0, subaddr_indices = [], priority = 0, ring_size = 0, unlock_time = 0, payment_id = '', get_tx_key = True, do_not_relay = False, get_tx_hex = False, get_tx_metadata = False):
transfer = {
'method': 'transfer',
@ -103,6 +91,17 @@ class Wallet(object):
}
return self.rpc.send_json_rpc_request(get_transfer_by_txid)
def get_payments(self, payment_id):
get_payments = {
'method': 'get_payments',
'params': {
'payment_id': payment_id,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(get_payments)
def get_bulk_payments(self, payment_ids = [], min_block_height = 0):
get_bulk_payments = {
'method': 'get_bulk_payments',
@ -153,14 +152,22 @@ class Wallet(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(get_balance)
getbalance = get_balance
def sweep_dust(self):
def sweep_dust(self, get_tx_keys = True, do_not_relay = False, get_tx_hex = False, get_tx_metadata = False):
sweep_dust = {
'method': 'sweep_dust',
'params': {
'get_tx_keys': get_tx_keys,
'do_not_relay': do_not_relay,
'get_tx_hex': get_tx_hex,
'get_tx_metadata': get_tx_metadata,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(sweep_dust)
sweep_unmixable = sweep_dust
def sweep_all(self, address = '', account_index = 0, subaddr_indices = [], priority = 0, ring_size = 0, outputs = 1, unlock_time = 0, payment_id = '', get_tx_keys = False, below_amount = 0, do_not_relay = False, get_tx_hex = False, get_tx_metadata = False):
sweep_all = {
@ -217,6 +224,7 @@ class Wallet(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(get_address)
getaddress = get_address
def create_account(self, label = ""):
create_account = {
@ -345,6 +353,34 @@ class Wallet(object):
}
return self.rpc.send_json_rpc_request(close_wallet)
def change_wallet_password(self, old_password, new_password):
change_wallet_password = {
'method': 'change_wallet_password',
'params' : {
'old_password': old_password,
'new_password': new_password,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(change_wallet_password)
def store(self):
store = {
'method': 'store',
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(store)
def stop_wallet(self):
stop_wallet = {
'method': 'stop_wallet',
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(stop_wallet)
def refresh(self):
refresh = {
'method': 'refresh',
@ -475,6 +511,18 @@ class Wallet(object):
}
return self.rpc.send_json_rpc_request(make_multisig)
def finalize_multisig(self, multisig_info, password = ''):
finalize_multisig = {
'method': 'finalize_multisig',
'params' : {
'multisig_info': multisig_info,
'password': password,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(finalize_multisig)
def exchange_multisig_keys(self, multisig_info, password = ''):
exchange_multisig_keys = {
'method': 'exchange_multisig_keys',
@ -605,6 +653,31 @@ class Wallet(object):
}
return self.rpc.send_json_rpc_request(check_tx_proof)
def get_spend_proof(self, txid = '', message = ''):
get_spend_proof = {
'method': 'get_spend_proof',
'params' : {
'txid': txid,
'message': message,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(get_spend_proof)
def check_spend_proof(self, txid = '', message = '', signature = ''):
check_spend_proof = {
'method': 'check_spend_proof',
'params' : {
'txid': txid,
'message': message,
'signature': signature,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(check_spend_proof)
def get_reserve_proof(self, all_ = True, account_index = 0, amount = 0, message = ''):
get_reserve_proof = {
'method': 'get_reserve_proof',
@ -663,6 +736,7 @@ class Wallet(object):
'id': '0'
}
return self.rpc.send_json_rpc_request(get_height)
getheight = get_height
def relay_tx(self, hex_):
relay_tx = {
@ -764,6 +838,230 @@ class Wallet(object):
}
return self.rpc.send_json_rpc_request(validate_address)
def get_accounts(self, tag):
get_accounts = {
'method': 'get_accounts',
'params': {
'tag': tag,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(get_accounts)
def get_account_tags(self):
get_account_tags = {
'method': 'get_account_tags',
'params': {
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(get_account_tags)
def tag_accounts(self, tag, accounts = []):
tag_accounts = {
'method': 'tag_accounts',
'params': {
'tag': tag,
'accounts': accounts,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(tag_accounts)
def untag_accounts(self, accounts = []):
untag_accounts = {
'method': 'untag_accounts',
'params': {
'accounts': accounts,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(untag_accounts)
def set_account_tag_description(self, tag, description):
set_account_tag_description = {
'method': 'set_account_tag_description',
'params': {
'tag': tag,
'description': description,
},
'jsonrpc': '2.0',
'id': '0'
}
return self.rpc.send_json_rpc_request(set_account_tag_description)
def rescan_blockchain(self, hard = False):
rescan_blockchain = {
'method': 'rescan_blockchain',
'jsonrpc': '2.0',
'params': {
'hard': hard,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(rescan_blockchain)
def rescan_spent(self):
rescan_spent = {
'method': 'rescan_spent',
'jsonrpc': '2.0',
'params': {
},
'id': '0'
}
return self.rpc.send_json_rpc_request(rescan_spent)
def set_tx_notes(self, txids = [], notes = []):
set_tx_notes = {
'method': 'set_tx_notes',
'jsonrpc': '2.0',
'params': {
'txids': txids,
'notes': notes,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(set_tx_notes)
def get_tx_notes(self, txids = []):
get_tx_notes = {
'method': 'get_tx_notes',
'jsonrpc': '2.0',
'params': {
'txids': txids,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(get_tx_notes)
def set_attribute(self, key, value):
set_attribute = {
'method': 'set_attribute',
'jsonrpc': '2.0',
'params': {
'key': key,
'value': value,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(set_attribute)
def get_attribute(self, key):
get_attribute = {
'method': 'get_attribute',
'jsonrpc': '2.0',
'params': {
'key': key,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(get_attribute)
def make_uri(self, address = '', payment_id = '', amount = 0, tx_description = '', recipient_name = ''):
make_uri = {
'method': 'make_uri',
'jsonrpc': '2.0',
'params': {
'address': address,
'payment_id': payment_id,
'amount': amount,
'tx_description': tx_description,
'recipient_name': recipient_name,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(make_uri)
def parse_uri(self, uri):
parse_uri = {
'method': 'parse_uri',
'jsonrpc': '2.0',
'params': {
'uri': uri,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(parse_uri)
def add_address_book(self, address = '', payment_id = '', description = ''):
add_address_book = {
'method': 'add_address_book',
'jsonrpc': '2.0',
'params': {
'address': address,
'payment_id': payment_id,
'description': description,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(add_address_book)
def edit_address_book(self, index, address = None, payment_id = None, description = None):
edit_address_book = {
'method': 'edit_address_book',
'jsonrpc': '2.0',
'params': {
'index': index,
'set_address': address != None,
'address': address or '',
'set_payment_id': payment_id != None,
'payment_id': payment_id or '',
'set_description': description != None,
'description': description or '',
},
'id': '0'
}
return self.rpc.send_json_rpc_request(edit_address_book)
def get_address_book(self, entries = []):
get_address_book = {
'method': 'get_address_book',
'jsonrpc': '2.0',
'params': {
'entries': entries,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(get_address_book)
def delete_address_book(self, index):
delete_address_book = {
'method': 'delete_address_book',
'jsonrpc': '2.0',
'params': {
'index': index,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(delete_address_book)
def start_mining(self, threads_count, do_background_mining = False, ignore_battery = False):
start_mining = {
'method': 'start_mining',
'jsonrpc': '2.0',
'params': {
'threads_count': threads_count,
'do_background_mining': do_background_mining,
'ignore_battery': ignore_battery,
},
'id': '0'
}
return self.rpc.send_json_rpc_request(start_mining)
def stop_mining(self):
stop_mining = {
'method': 'stop_mining',
'jsonrpc': '2.0',
'params': {
},
'id': '0'
}
return self.rpc.send_json_rpc_request(stop_mining)
def get_version(self):
get_version = {
'method': 'get_version',