Compare commits

...

87 Commits

Author SHA1 Message Date
jw
0ffda21600 Merge pull request #126 from wowario/master
update checkpoints
2018-12-05 16:55:15 -08:00
wowario
41494095eb update version 2018-12-05 01:12:55 +03:00
wowario
c33e7f7bbb update checkpoints 2018-12-05 01:08:41 +03:00
jw
1fa62a2a25 Merge pull request #125 from 0x000090/cli-win-installer
Add scripts & resources for wownero cli installer.
2018-12-01 14:05:35 -08:00
0x000090
8894ff9381 Add scripts & resources for wownero cli installer. 2018-12-01 14:41:50 -06:00
jw
59ac413283 Merge pull request #123 from wownero/dev-v0.4
Dev v0.4
2018-11-09 08:18:36 -08:00
jw
db77810cd4 Merge pull request #122 from wowario/v0.4
Add LWMA-4, ASCII art, 10v fork height
2018-11-09 08:17:29 -08:00
wowario
fc9d0deb38 update difficulty minimum and guess 2018-11-09 19:09:29 +03:00
wowario
698d041e64 update checkpoints 2018-11-09 18:34:07 +03:00
wowario
7f28f73bd6 Difficulty: temper long solvetime 2018-11-09 13:02:08 +03:00
wowario
003f0b3af3 v0.4 ASCII art 2018-11-09 12:49:51 +03:00
wowario
47bd60ba50 Replace LWMA-3 with LWMA-4, add v10 fork height 2018-11-08 22:42:45 +03:00
wowario
6e6cb4fdaf Merge pull request #120 from wownero/release-v0.3.0.0
Merge release-v0.3.0.0 branch
2018-11-08 20:49:05 +03:00
wowario
de5e3660d9 Merge pull request #119 from wowario/checkpoints
update checkpoints
2018-11-08 18:32:08 +03:00
wowario
7bc2142590 correct cumulative difficulty count 2018-11-08 18:21:52 +03:00
wowario
8adea0a034 update checkpoints 2018-11-08 17:52:47 +03:00
wowario
58d006566b Merge pull request #118 from wowario/checkpoints
Update checkpoints
2018-11-07 22:26:54 +03:00
wowario
3263067b97 update checkpoint 2018-11-07 22:04:23 +03:00
wowario
6cf896f8ab update checkpoints 2018-11-07 20:51:19 +03:00
wowario
caae62e2ce update checkpoints 2018-11-07 20:51:10 +03:00
wowario
4865345889 Merge pull request #117 from wowario/checkpoints
update checkpoints
2018-11-07 19:21:20 +03:00
wowario
8b56a44adc counter attack 2018-11-07 19:19:07 +03:00
wowario
d1623175b2 update checkpoints 2018-11-07 09:13:13 +03:00
jw
0edab6407f Merge pull request #116 from wowario/checkpoints
update checkpoints
2018-10-29 08:19:07 -07:00
jw
7008314cb0 Merge pull request #115 from wowario/version
up version to 0.3.1.1
2018-10-29 08:18:56 -07:00
jw
35d15f2626 Merge pull request #114 from rbrunner7/mms-for-wow2
MMS for Wownero
2018-10-29 08:18:45 -07:00
wowario
47b543b591 update checkpoints 2018-10-29 01:20:17 +03:00
wowario
398a21019c up version 2018-10-29 01:05:00 +03:00
rbrunner7
fc915d3e0f MMS: Some Wownero-specific changes in 'simplewallet.cpp' 2018-10-28 20:00:33 +01:00
rbrunner7
b1e005b3c8 MMS: Initial version for Wownero, merged in from Monero
Wownero-specific changes still missing, see following second commit
2018-10-28 17:06:06 +01:00
jw
d4a5778dd6 Merge pull request #113 from wowario/walletfix
revert wallet2: use a gamma distribution to pick fake outs
2018-10-27 07:41:11 -07:00
jw
cce5708153 Merge pull request #112 from wowario/checkpoints
update checkpoints
2018-10-27 07:40:51 -07:00
wowario
0a97b3784d revert wallet2: use a gamma distribution to pick fake outs 2018-10-27 10:00:43 +03:00
wowario
a8790e240c update checkpoints 2018-10-25 19:14:26 +03:00
jw
c929c2a6bb Merge pull request #111 from wowario/lwma3
add LWMA v3 for v10 HF
2018-10-24 16:53:08 -07:00
jw
741adad93a Merge pull request #110 from wowario/subadd
rpc wallet: revert integrated address generation
2018-10-24 16:52:56 -07:00
jw
73b17fcb62 Merge pull request #109 from wowario/lwma
difficulty: remove testnet variables
2018-10-24 16:52:46 -07:00
wowario
f3ee225f29 add lwma3 2018-10-24 20:29:35 +03:00
wowario
f165101429 rpc wallet: revert integrated address generation 2018-10-24 14:37:54 +03:00
wowario
d2554341b1 difficulty: remove testnet variables 2018-10-24 13:03:02 +03:00
jw
c1632fb30c Merge pull request #108 from wowario/txpool_fix
tx_pool: revert #4592 and move bin2hex conversion to on_get_transaction_pool
2018-10-23 17:57:53 -07:00
stoffu
17cd68f865 tx_pool: revert #4592 and move bin2hex conversion to on_get_transaction_pool 2018-10-23 19:46:13 +03:00
jw
5c6eb96036 Merge pull request #107 from wowario/lwma2
Difficulty: increase minimum to 25m
2018-10-23 03:41:03 -07:00
jw
b67c96af71 Merge pull request #106 from wowario/version
up version to 0.3.1
2018-10-23 03:40:51 -07:00
wowario
88fd5fb494 increase minimum difficulty to 25 million 2018-10-22 23:40:29 +03:00
wowario
5c3195dac9 up version to 0.3.1 2018-10-22 23:30:04 +03:00
jw
8386c6df2c Merge pull request #105 from wowario/upstream
Upstream
2018-10-22 09:12:39 -07:00
wowario
9d952da3a0 Merge branch 'release-v0.3.0.0' into upstream 2018-10-22 16:01:04 +00:00
jw
2320fb5549 Merge pull request #104 from wowario/release-v0.3.0.0
Wallet: encourage use of subaddresses
2018-10-22 08:21:42 -07:00
moneromooo-monero
b40a81c694 wallet2: use a gamma distribution to pick fake outs
as per "An Empirical Analysis of Linkability in the Monero
Blockchain", by Miller et al.
2018-10-20 21:09:50 +03:00
stoffu
308a8f69ad daemon.print_bc: don't print difficulty twice 2018-10-20 21:09:50 +03:00
moneromooo-monero
b491ff61c5 epee: set jsonrpc to "2.0" in parse error return data 2018-10-20 21:09:49 +03:00
rbrunner7
bb0ac27a4a simplewallet: correct number of human-readable months 2018-10-20 21:09:49 +03:00
moneromooo-monero
835caf3722 simplewallet: add a warning and prompt on rescan_blockchain
Many people are using this as a "let's see what this does" command
when something doesn't work as they thought it should, and thus
destroying info that they might still need.
2018-10-20 21:09:49 +03:00
fireice-uk
9021fe99b1 node_rpc_proxy: fix fork earliest height caching [RYO backport]
xref https://github.com/ryo-currency/ryo-currency/pull/86
2018-10-20 21:09:49 +03:00
rbrunner7
bbd251c48f simplewallet: Simplify LOCK_IDLE_SCOPE macro 2018-10-20 21:09:49 +03:00
moneromooo-monero
00c6056e98 simplewallet: allow named priority levels for default-priority
to match those used by the various transfer functions
2018-10-20 21:09:49 +03:00
moneromooo-monero
47f7cf68ae epee: resize vectors where possible in serialization
to avoid unnecessary repeated reallocation
2018-10-20 21:09:49 +03:00
moneromooo-monero
c4bdd5699c epee: some speedup in parsing 2018-10-20 21:09:48 +03:00
moneromooo-monero
7e6b4601c3 tx_pool: fix infinite loop when failing to find a meta record 2018-10-20 21:09:48 +03:00
stoffu
d4f07b40de wallet2.get_reserve_proof: throw when specified amount is zero 2018-10-20 21:09:48 +03:00
moneromooo-monero
1bbfeb325c tx_pool: fix tx removal at startup keeping references 2018-10-20 19:49:28 +03:00
moneromooo-monero
dc1e56d815 daemon: do not display uptime when not known 2018-10-20 19:49:27 +03:00
moneromooo-monero
332ac6a03a rpc: return "already mining" in start_mining if already mining 2018-10-20 16:07:51 +03:00
moneromooo-monero
ae255e3857 cryptonote_format_utils: do not early out on invalid tx pubkeys
Another such pubkey might be valid
2018-10-20 10:43:03 +03:00
stoffu
7cf07e9319 simplewallet: make sure wallet config is stored right after creation 2018-10-20 10:43:02 +03:00
moneromooo-monero
d904ea08a1 wallet2: avoid using arbitrary random values when unknown 2018-10-20 10:43:02 +03:00
moneromooo-monero
0da1f14aaf wallet2: guard against bad outputs in import_outputs
also some minor speedup
2018-10-20 10:43:02 +03:00
moneromooo-monero
583eaf3ccb wallet2: fix O(n^2) behaviour in import_key_images
That takes a lot of time for even not so large wallets
2018-10-20 10:43:02 +03:00
stoffu
ee45ef2c9e simplewallet.unspent_outputs: fix wrong logic for parsing 2018-10-20 10:43:02 +03:00
stoffu
7ba6d69196 wallet-rpc: filter getbalance response by address index 2018-10-20 10:43:02 +03:00
stoffu
83e5d6a829 wallet-rpc: add get_address_index command 2018-10-20 10:43:02 +03:00
Jorropo
a09d5ea211 [monerod] Added blocks remaining count during syncronisation.
And percent if usefull (% < 99)
2018-10-20 10:43:01 +03:00
stoffu
2998b929d5 tx_pool: store hex string instead of raw binary to tx_blob of get_transaction_pool RPC
Inspired by https://github.com/masari-project/masari/issues/93
2018-10-20 10:43:01 +03:00
doy-lee
44e0f9eebf wallet2: clear found out for every tx key
Avoids triggering the sanity check
2018-10-20 10:43:01 +03:00
moneromooo-monero
00bf0b5ef0 wallet_rpc_server: include all transfer records for a txid
Since subaddresses were added, a tx can now create more than
one payment
2018-10-20 10:43:01 +03:00
wowario
d1176c5ef5 rpc: added subaddresses warning 2018-10-18 12:39:44 +03:00
wowario
cbf07f01ba encourage use of subaddresses 2018-10-17 22:37:19 +03:00
jw
9c828a281b Merge pull request #100 from wowario/release-v0.3.1.0
update checkpoints
2018-10-11 08:50:09 -07:00
wowario
a5253e2fb4 update checkpoints 2018-10-09 15:30:06 +03:00
jw
c299569449 Merge pull request #99 from wownero/release-v0.3.0.0
Release v0.3.0.0
2018-10-05 21:52:15 -07:00
jw
ff3a667a90 Merge pull request #98 from wowario/release-v0.3.1.0
compile fix for Ubuntu 18
2018-10-05 21:50:20 -07:00
wowario
a3a84378eb bump version to 0.3.0.1 2018-10-05 20:59:58 +03:00
xiphon
202d568676 build: fix gcc false positive 'stringop-overflow' warning 2018-10-05 20:57:41 +03:00
jw
47aa3f296d Merge pull request #97 from camthegeek/windows-fix5102018
unbound submodule update
2018-10-05 10:39:51 -07:00
wowario
6bdd11abe2 rename to wownero 2018-10-05 20:32:27 +03:00
camthegeek
6486269929 unbound submodule update 2018-10-05 12:12:28 -04:00
43 changed files with 3836 additions and 283 deletions

View File

@@ -95,7 +95,8 @@ Dates are provided in the format YYYY-MM-DD.
| ------------------------------ | -----------| ----------------- | ---------------------- | -------------------------- | ---------------------------------------------------------------------------------- |
| 1 | 2018-04-01 | v7 | v0.1.0.0 | v0.1.0.0 | Cryptonight variant 1, ringsize >= 8, sorted inputs
| 6969 | 2018-04-24 | v8 | v0.2.0.0 | v0.2.0.0 | Bulletproofs, LWMA difficulty algorithm, ringsize >= 10, reduce unlock to 4
| 53666 | 2018-10-06 | v9 | v0.3.0.0 | v0.3.0.0 | Cryptonight variant 2, LWMA v2, ringsize = 22, XXX
| 53666 | 2018-10-06 | v9 | v0.3.0.0 | v0.3.1.3 | Cryptonight variant 2, LWMA v2, ringsize = 22, MMS
| 63469 | 2018-11-11 | v10 | v0.4.0.0 | v0.4.0.0 | LWMA v4
X's indicate that these details have not been determined as of commit date.

View File

@@ -122,6 +122,7 @@
if(!ps.load_from_json(query_info.m_body)) \
{ \
boost::value_initialized<epee::json_rpc::error_response> rsp; \
static_cast<epee::json_rpc::error_response&>(rsp).jsonrpc = "2.0"; \
static_cast<epee::json_rpc::error_response&>(rsp).error.code = -32700; \
static_cast<epee::json_rpc::error_response&>(rsp).error.message = "Parse error"; \
epee::serialization::store_t_to_json(static_cast<epee::json_rpc::error_response&>(rsp), response_info.m_body); \

View File

@@ -35,6 +35,11 @@
namespace epee
{
namespace
{
template<class C> void hint_resize(C &container, size_t size) {}
template<class C> void hint_resize(std::vector<C> &container, size_t size) { container.reserve(size); }
}
namespace serialization
{
@@ -158,6 +163,7 @@ namespace epee
false,
"size in blob " << loaded_size << " not have not zero modulo for sizeof(value_type) = " << sizeof(typename stl_container::value_type));
size_t count = (loaded_size/sizeof(typename stl_container::value_type));
hint_resize(container, count);
for(size_t i = 0; i < count; i++)
container.insert(container.end(), *(pelem++));
}

View File

@@ -28,6 +28,8 @@
#pragma once
#include <algorithm>
namespace epee
{
namespace misc_utils
@@ -36,8 +38,12 @@ namespace misc_utils
{
inline std::string transform_to_escape_sequence(const std::string& src)
{
//std::stringstream res;
static const char escaped[] = "\b\f\n\r\t\v\"\\/";
if (std::find_first_of(src.begin(), src.end(), escaped, escaped + sizeof(escaped)) == src.end())
return src;
std::string res;
res.reserve(2 * src.size());
for(std::string::const_iterator it = src.begin(); it!=src.end(); ++it)
{
switch(*it)
@@ -84,6 +90,7 @@ namespace misc_utils
inline void match_string2(std::string::const_iterator& star_end_string, std::string::const_iterator buf_end, std::string& val)
{
val.clear();
val.reserve(std::distance(star_end_string, buf_end));
bool escape_mode = false;
std::string::const_iterator it = star_end_string;
++it;

View File

@@ -0,0 +1,88 @@
#
# This is a script for easily building the wownero cli installer in an automated build. Note
# that it is also possible to build the installer by dropping the .nsi file on the NSIS GUI,
# but that this will not work, as the script requires some defines, parsed from the wownero
# version file, to be passed on the command line.
#
import sys
sys.dont_write_bytecode = True
import os
import os.path
import subprocess
#
# Grab the dir of this .py
#
basedir = os.path.dirname(os.path.abspath(__file__))
#
# Try to find version.cpp.in.
#
version_file = os.path.join('..', 'src', 'version.cpp.in')
if not os.path.isfile(version_file):
print('Version file not found: %s' % version_file)
sys.exit(-1)
#
# Try to parse version.cpp.in.
#
version_string = None
release_name = None
with open(version_file, 'r') as fp:
version_prefix = '#define DEF_MONERO_VERSION "'
release_prefix = '#define DEF_MONERO_RELEASE_NAME "'
for line in fp:
if line.startswith(version_prefix):
version_string = line.replace(version_prefix, '')[:-2]
elif line.startswith(release_prefix):
release_name = line.replace(release_prefix, '')[:-2]
if not version_string:
print('Failed to parse version from: %s' % version_file)
sys.exit(-1)
if not release_name:
print('Failed to parse release name from: %s' % version_file)
sys.exit(-1)
#
# Check that we got an expected version format.
#
version_parts = version_string.split('.')
if len(version_parts) != 4:
print('Invalid version string: %s' % version_string)
sys.exit(-1)
#
# Try to find makensis.
#
makensis = 'makensis.exe'
if not os.path.isfile(makensis):
for dir in os.environ['PATH'].split(';'):
test = os.path.join(dir, makensis)
if os.path.isfile(test):
makensis = test
break
if not os.path.isfile(makensis):
print('Failed to find makensis.exe')
sys.exit(-1)
#
# Build & run makensis command line.
#
cmd = '"%s"' % makensis
cmd += ' /V4'
cmd += ' /DVERSION_MAJOR=%s' % version_parts[0]
cmd += ' /DVERSION_MINOR=%s' % version_parts[1]
cmd += ' /DVERSION_BUILD=%s' % version_parts[2]
cmd += ' /DVERSION_REVISION=%s' % version_parts[3]
cmd += ' /DRELEASE_NAME="%s"' % release_name
cmd += ' "%s"' % os.path.join(basedir, 'cli-win', 'installer.nsi')
print("Calling makensis: %s" % cmd)
subprocess.call(cmd)

BIN
installers/cli-win/app.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -0,0 +1,262 @@
;-----------------------------------------------------------------------------------------
; File: Wownero CLI NSIS installer
; Author: 0x000090
; Date: 1 Dec 2018
; License: WTFPL
;-----------------------------------------------------------------------------------------
;
; DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
; Version 2, December 2004
;
; Copyright (C) 2018 Wownero Inc., a Monero Enterprise Alliance partner company
;
; DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
; TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
;
; 0. You just DO WHAT THE FUCK YOU WANT TO.
;
;-----------------------------------------------------------------------------------------
!include 'MUI2.nsh'
!include 'TextFunc.nsh'
!include 'WordFunc.nsh'
!include 'Sections.nsh'
;-----------------------------------------------------------------------------------------
!ifndef VERSION_MAJOR
!warning 'VERSION_MAJOR not defined!'
Quit
!endif
!ifndef VERSION_MINOR
!warning 'VERSION_MINOR not defined!'
Quit
!endif
!ifndef VERSION_BUILD
!warning 'VERSION_BUILD not defined!'
Quit
!endif
!ifndef VERSION_REVISION
!warning 'VERSION_REVISION not defined!'
Quit
!endif
!ifndef RELEASE_NAME
!warning 'RELEASE_NAME not defined!'
Quit
!endif
!define VERSION_SHORT '${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_BUILD}'
!define VERSION_LONG '${VERSION_SHORT}.${VERSION_REVISION}'
;-----------------------------------------------------------------------------------------
!define PROJECT_NAME 'Wownero'
!define PRODUCT_NAME 'Wownero CLI'
!define COMPANY_NAME 'Wownero Inc.'
!define EXE_BASENAME 'wownero'
!define HELP_URL 'http://wownero.org/#community'
!define ABOUT_URL 'http://wownero.org/#about'
!define SOURCE_DIR '..\..\build\release\bin'
!define TARGET_DIR '..\..\build\installers'
!define TARGET_PATH '${TARGET_DIR}\Wownero-CLI-${VERSION_LONG}-win.exe'
!define INSTALL_SUBDIR 'cli'
!define INSTALL_SIZE 181000
!define UNINSTALLER_NAME 'uninstall.exe'
!define REG_BASE 'Software\Microsoft\Windows\CurrentVersion\Uninstall'
!define REG_KEY '${REG_BASE}\{E114584F-4E1B-4B2D-8B1C-69A188025E98}'
!define /date CURRENT_YEAR '%Y'
;-----------------------------------------------------------------------------------------
!system 'if not exist "${TARGET_DIR}" md "${TARGET_DIR}"'
;-----------------------------------------------------------------------------------------
Name '${PRODUCT_NAME}'
OutFile '${TARGET_PATH}'
BrandingText '${PRODUCT_NAME} ${VERSION_LONG} "${RELEASE_NAME}"'
CRCCheck force
RequestExecutionLevel admin
InstallDir "$PROGRAMFILES64\${PROJECT_NAME}\${INSTALL_SUBDIR}"
ShowInstDetails show
ShowUninstDetails show
;-----------------------------------------------------------------------------------------
VIAddVersionKey 'CompanyName' '${COMPANY_NAME}'
VIAddVersionKey 'ProductName' '${PRODUCT_NAME}'
VIAddVersionKey 'FileDescription' '${PRODUCT_NAME}'
VIAddVersionKey 'LegalCopyright' 'Copyright (c) ${CURRENT_YEAR}, ${COMPANY_NAME}'
VIAddVersionKey 'FileVersion' '${VERSION_SHORT}'
VIProductVersion '${VERSION_LONG}'
;-----------------------------------------------------------------------------------------
!define MUI_ABORTWARNING
!define MUI_UNABORTWARNING
!define MUI_FINISHPAGE_NOAUTOCLOSE
!define MUI_UNFINISHPAGE_NOAUTOCLOSE
!define MUI_ICON "app.ico"
!define MUI_UNICON "app.ico"
!define MUI_HEADERIMAGE
!define MUI_HEADERIMAGE_RIGHT
!define MUI_HEADERIMAGE_BITMAP "header.bmp"
!define MUI_HEADERIMAGE_UNBITMAP "header.bmp"
!define MUI_HEADERIMAGE_BITMAP_NOSTRETCH
!define MUI_HEADERIMAGE_UNBITMAP_NOSTRETCH
!define MUI_WELCOMEFINISHPAGE_BITMAP "welcome.bmp"
!define MUI_UNWELCOMEFINISHPAGE_BITMAP "welcome.bmp"
!define MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH
!define MUI_UNWELCOMEFINISHPAGE_UNBITMAP_NOSTRETCH
;-----------------------------------------------------------------------------------------
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!insertmacro MUI_UNPAGE_WELCOME
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
;-----------------------------------------------------------------------------------------
!insertmacro MUI_LANGUAGE "English"
;-----------------------------------------------------------------------------------------
Function VerifyUserIsAdmin
UserInfo::GetAccountType
Pop $0
${If} $0 != "admin"
MessageBox MB_ICONSTOP "Admin permissions required, please use right-click > Run as Administrator."
SetErrorLevel 740 ; ERROR_ELEVATION_REQUIRED
Quit
${EndIf}
FunctionEnd
Function .onInit
Call VerifyUserIsAdmin
FunctionEnd
Function un.onInit
FunctionEnd
;-----------------------------------------------------------------------------------------
!macro RemoveFile file
Push $0
${TrimNewLines} '${file}' $0
SetDetailsPrint both
${If} ${FileExists} '$0'
DetailPrint 'Deleting file $0'
SetDetailsPrint textonly
Delete "$0"
${Else}
DetailPrint 'File not found: $0'
${Endif}
SetDetailsPrint lastused
Pop $0
!macroend
!macro RemoveDir dir
Push $0
${TrimNewLines} '${dir}' $0
SetDetailsPrint both
${If} ${FileExists} '$0'
DetailPrint 'Deleting directory $0'
SetDetailsPrint textonly
RmDir /r "$0"
${Else}
DetailPrint 'Directory not found: $0'
${Endif}
SetDetailsPrint lastused
Pop $0
!macroend
;-----------------------------------------------------------------------------------------
Function WriteFiles
DetailPrint 'Installing application files...'
SetDetailsPrint both
SetOutPath '$INSTDIR'
File 'app.ico'
;
; Add here whatever else you want to be included:
;
File '${SOURCE_DIR}\${EXE_BASENAME}d.exe'
File '${SOURCE_DIR}\${EXE_BASENAME}-wallet-cli.exe'
File '${SOURCE_DIR}\${EXE_BASENAME}-wallet-rpc.exe'
File '${SOURCE_DIR}\${EXE_BASENAME}-gen-trusted-multisig.exe'
;
; NOTE: you can also add all files in a dir, like this:
;
;File /r '${SOURCE_DIR}\*.*'
SetDetailsPrint lastused
DetailPrint 'Writing uninstaller...'
WriteUninstaller '$INSTDIR\${UNINSTALLER_NAME}'
FunctionEnd
Function un.WriteFiles
DetailPrint 'Removing application files...'
!insertmacro RemoveDir '$INSTDIR'
FunctionEnd
;-----------------------------------------------------------------------------------------
Function WriteShortcuts
DetailPrint 'Creating Desktop shortcuts...'
CreateShortCut '$DESKTOP\${PRODUCT_NAME} Wallet.lnk' '$INSTDIR\${EXE_BASENAME}-wallet-cli.exe' '' '$INSTDIR\app.ico'
CreateShortCut '$DESKTOP\${PRODUCT_NAME} Daemon.lnk' '$INSTDIR\${EXE_BASENAME}d.exe' '' '$INSTDIR\app.ico'
DetailPrint 'Creating Start Menu shortcuts...'
CreateDirectory '$SMPROGRAMS\${COMPANY_NAME}\${PRODUCT_NAME}'
CreateShortCut '$SMPROGRAMS\${COMPANY_NAME}\${PRODUCT_NAME}\${PRODUCT_NAME} Wallet.lnk' '$INSTDIR\${EXE_BASENAME}-wallet-cli.exe' '' '$INSTDIR\app.ico'
CreateShortCut '$SMPROGRAMS\${COMPANY_NAME}\${PRODUCT_NAME}\${PRODUCT_NAME} Daemon.lnk' '$INSTDIR\${EXE_BASENAME}d.exe' '' '$INSTDIR\app.ico'
CreateShortCut '$SMPROGRAMS\${COMPANY_NAME}\${PRODUCT_NAME}\Uninstall ${PRODUCT_NAME}.lnk' '$INSTDIR\${UNINSTALLER_NAME}' '' '$INSTDIR\app.ico'
FunctionEnd
Function un.WriteShortcuts
DetailPrint 'Removing Desktop shortcuts...'
!insertmacro RemoveFile '$DESKTOP\${PRODUCT_NAME} Daemon.lnk'
!insertmacro RemoveFile '$DESKTOP\${PRODUCT_NAME} Wallet.lnk'
DetailPrint 'Removing Start Menu shortcuts...'
!insertmacro RemoveDir '$SMPROGRAMS\${COMPANY_NAME}\${PRODUCT_NAME}'
FunctionEnd
;-----------------------------------------------------------------------------------------
Function WriteUninstaller
DetailPrint 'Registering ${PRODUCT_NAME} ${VERSION_SHORT}'
WriteRegDWORD HKLM '${REG_KEY}' 'VersionMajor' ${VERSION_MAJOR}
WriteRegDWORD HKLM '${REG_KEY}' 'VersionMinor' ${VERSION_MINOR}
WriteRegDWORD HKLM '${REG_KEY}' 'EstimatedSize' ${INSTALL_SIZE}
WriteRegDWORD HKLM '${REG_KEY}' 'NoModify' 1
WriteRegDWORD HKLM '${REG_KEY}' 'NoRepair' 1
WriteRegStr HKLM '${REG_KEY}' 'Contact' '${COMPANY_NAME}'
WriteRegStr HKLM '${REG_KEY}' 'Publisher' '${COMPANY_NAME}'
WriteRegStr HKLM '${REG_KEY}' 'HelpLink' '${HELP_URL}'
WriteRegStr HKLM '${REG_KEY}' 'URLInfoAbout' '${ABOUT_URL}'
WriteRegStr HKLM '${REG_KEY}' 'DisplayVersion' '${VERSION_SHORT}'
WriteRegStr HKLM '${REG_KEY}' 'Comments' '${PRODUCT_NAME}'
WriteRegStr HKLM '${REG_KEY}' 'DisplayName' '${PRODUCT_NAME} ${VERSION_SHORT}'
WriteRegStr HKLM '${REG_KEY}' 'InstallLocation' '"$INSTDIR"'
WriteRegStr HKLM '${REG_KEY}' 'DisplayIcon' '"$INSTDIR\app.ico"'
WriteRegStr HKLM '${REG_KEY}' 'UninstallString' '"$INSTDIR\${UNINSTALLER_NAME}"'
WriteRegStr HKLM '${REG_KEY}' 'QuietUninstallString' '"$INSTDIR\${UNINSTALLER_NAME}" /S'
FunctionEnd
Function un.WriteUninstaller
DetailPrint 'Unregistering ${PRODUCT_NAME} ${VERSION_SHORT}...'
DeleteRegKey HKLM '${REG_KEY}'
FunctionEnd
;-----------------------------------------------------------------------------------------
Section "${PRODUCT_NAME} ${VERSION_SHORT}"
Call WriteFiles
Call WriteShortcuts
Call WriteUninstaller
SectionEnd
;-----------------------------------------------------------------------------------------
Section "Uninstall"
Call un.WriteFiles
Call un.WriteShortcuts
Call un.WriteUninstaller
SectionEnd

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

31
installers/readme.txt Normal file
View File

@@ -0,0 +1,31 @@
Installer notes
So, at this point I just have one installer, for the cli, on windows. What do you need
to know ..
1. It uses NSIS, and expects to find makensis.exe on your PATH.
2. It expects built products to be found at:
wownero/build/release/bin
3. It currently includes the daemon, wallet, rpc, and gen-trusted-multisig. I note that
releases on the wownero github have had different files (static libs, pthreads dll)
than I end up with in my /bin dir when I build, so you may or may not need to change
which files you include. At any rate, it is easy to do, in the WriteFiles function
in installer.nsi.
4. Provided everything is ok, it should be as simple as executing build-cli-win.py; built
installer, versioned according to src/version.cpp.in, will be depostied here:
wownero/build/installers/Wownero-CLI-<version>-win.exe
5. The installer includes an uninstaller, shortcuts on desktop & start menu, and it
registers itself with windows, so it shows up as expected in programs & features.
6. I have tried to keep the installer script pretty generic, so it can be reused later,
for gui, or whatever. It installs to a <program files>/Wownero/cli subdirectory, so
that other applications can eventually be installed alongside. NOTE: if you copy the
installer.nsi file to use for another project, you will need to make a new GUID for
it, and put it in the REG_KEY define.

Binary file not shown.

View File

@@ -185,7 +185,32 @@ namespace cryptonote
ADD_CHECKPOINT(20000, "52cc7edcb49eb02f28a653b824089a726f4050eb210263ee6f4180d388a1e5cc");
ADD_CHECKPOINT(30000, "d22fde5dd240ade16d3250eb0aa5d1c16dc7cb51c20484e05eb274911032b3fa");
ADD_CHECKPOINT(40000, "aee0d642322542ba069cb1c58ab2acd3560f108d4682c3dc3cb15a54d442d91f");
ADD_CHECKPOINT(50000, "5286ac2a0f39b3aefcba363cd71f2760bd1e0d763cbc81026ebdc3f80a86541f");
ADD_CHECKPOINT(50000, "5286ac2a0f39b3aefcba363cd71f2760bd1e0d763cbc81026ebdc3f80a86541f");
ADD_CHECKPOINT(53666, "3f43f56f66ef0c43cf2fd14d0d28fa2aae0ef8f40716773511345750770f1255"); //Hard fork to v9
ADD_CHECKPOINT(54500, "8ed3078b389c2b44add007803d741b58d3fbed2e1ba4139bda702152d8773c9b");
ADD_CHECKPOINT(55000, "4b662ceccefc3247edb4d654dd610b8fb496e85b88a5de43cc2bdd28171b15ff");
ADD_CHECKPOINT(57000, "08a79f09f12bb5d230b63963356a760d51618e526cfc636047a6f3798217c177");
ADD_CHECKPOINT(59000, "180b51ee2c5fbcd4362eb7a29df9422481310dd77d10bccdf8930724c31e007e");
ADD_CHECKPOINT(59900, "18cc0653ef39cb304c68045dba5eb6b885f936281cd939dea04d0e6c9cd4ae2e");
ADD_CHECKPOINT(60000, "0f02aa57a63f79f63dafed9063abe228a37cb19f00430dc3168b8a8f4ae8016c");
ADD_CHECKPOINT(61000, "509aca8c54eb5fe44623768757b6e890ae39d512478c75f614cbff3d91809350");
ADD_CHECKPOINT(62000, "7fe91ad256c08dbd961e04738968be22fb481093fbfa7959bde7796ccceba0e2");
ADD_CHECKPOINT(62150, "1a7c75f8ebeda0e20eb5877181eafd7db0fc887e3fed43e0b27ab2e7bccafd10");
ADD_CHECKPOINT(62269, "4969555d60742afb93925fd96d83ac28f45e6e3c0e583c9fb3c92d9b2100d38f");
ADD_CHECKPOINT(62405, "4d0ae890cf9f875f231c7069508ad28dc429d14814b52db114dfab7519a27584");
ADD_CHECKPOINT(62419, "bd8bf5ac4c4fb07ab4d0d492bd1699def5c095ab6943ad3b63a89d1d8b1ce748");
ADD_CHECKPOINT(62425, "41a922dba6f3906871b2ccaf31ec9c91033470c503959093dae796deda8940ea");
ADD_CHECKPOINT(62479, "a2e8ff4205ba2980eb70921b0b21b5fc656ee273664ea94b860c68ca069b60dd");
ADD_CHECKPOINT(62503, "25fa115962988b4b8f8cfd22744a3e653b22ead8c8468e64caf334fc75a97d08");
ADD_CHECKPOINT(62550, "bde522a8a81c392c98c979434aa1dd9d20b4ca52230ba6ae0362872757808a48");
ADD_CHECKPOINT(62629, "8368e1ce1d421f1fc969364558433e2b2363d0ffcb5f2d946633095e3e6734f5");
ADD_CHECKPOINT(62720, "f871cddd75951e2fe24c282d2bd28396fc922ea519b354ace992a0162cb333ff");
ADD_CHECKPOINT(62733, "8331dbeeaf23173d2235a062373a437befadb6492cceb7640127bf18653a9e61");
ADD_CHECKPOINT(62877, "62d44adc05d7d4fd9d15239c5575612207beab0bcf2da49158bf89e365441ca1");
ADD_CHECKPOINT(63469, "4e33a9343fc5b86661ec0affaeb5b5a065290602c02d817337e4a979fe5747d8"); //Hard fork to v10
ADD_CHECKPOINT(63950, "155b61475985ac3f48fda10091d732bdc8087a55554504959e88d29962c91b72");
ADD_CHECKPOINT(67500, "84acb8fa140d8c7eb49bcbcf662cbe7570496f463c637a67980613dbd70dbbc3");
return true;
}

View File

@@ -201,15 +201,25 @@ namespace cryptonote
{
crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
bool r = hwdev.generate_key_derivation(tx_public_key, ack.m_view_secret_key, recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
if (!r)
{
MWARNING("key image helper: failed to generate_key_derivation(" << tx_public_key << ", " << ack.m_view_secret_key << ")");
memcpy(&recv_derivation, rct::identity().bytes, sizeof(recv_derivation));
}
std::vector<crypto::key_derivation> additional_recv_derivations;
for (size_t i = 0; i < additional_tx_public_keys.size(); ++i)
{
crypto::key_derivation additional_recv_derivation = AUTO_VAL_INIT(additional_recv_derivation);
r = hwdev.generate_key_derivation(additional_tx_public_keys[i], ack.m_view_secret_key, additional_recv_derivation);
CHECK_AND_ASSERT_MES(r, false, "key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")");
additional_recv_derivations.push_back(additional_recv_derivation);
if (!r)
{
MWARNING("key image helper: failed to generate_key_derivation(" << additional_tx_public_keys[i] << ", " << ack.m_view_secret_key << ")");
}
else
{
additional_recv_derivations.push_back(additional_recv_derivation);
}
}
boost::optional<subaddress_receive_info> subaddr_recv_info = is_out_to_acc_precomp(subaddresses, out_key, recv_derivation, additional_recv_derivations, real_output_index,hwdev);

View File

@@ -256,13 +256,9 @@ namespace cryptonote {
assert(timestamps.size() == cumulative_difficulties.size() && timestamps.size() <= static_cast<uint64_t>(N+1) );
if ( cryptonote::MAINNET && height <= DIFFICULTY_HEIGHT ){
/*if ( height <= DIFFICULTY_HEIGHT ){
return static_cast<uint64_t>(DIFFICULTY_GUESS);
}
if ( cryptonote::TESTNET && height <= DIFFICULTY_TESTNET_HEIGHT ){
return static_cast<uint64_t>(DIFFICULTY_TESTNET_GUESS);
}
}*/
for ( int64_t i = 1; i <= N; i++ ) {
ST = static_cast<int64_t>(timestamps[i]) - static_cast<int64_t>(timestamps[i-1]);
@@ -281,14 +277,79 @@ namespace cryptonote {
next_D = std::max(next_D,(prev_D*108)/100);
}
if ( cryptonote::MAINNET && next_D < DIFFICULTY_MINIMUM ) {
if ( next_D < DIFFICULTY_MINIMUM ) {
return static_cast<uint64_t>(DIFFICULTY_MINIMUM);
}
else if ( cryptonote::TESTNET && next_D < DIFFICULTY_TESTNET_MINIMUM ) {
return static_cast<uint64_t>(DIFFICULTY_TESTNET_MINIMUM);
}
else {
return static_cast<uint64_t>(next_D);
}
}
// LWMA-4 difficulty algorithm
// Copyright (c) 2017-2018 Zawy, MIT License
// https://github.com/zawy12/difficulty-algorithms/issues/3
difficulty_type next_difficulty_v4(std::vector<uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t height) {
uint64_t T = DIFFICULTY_TARGET_V2;
uint64_t N = DIFFICULTY_WINDOW_V2; // N=45, 60, and 90 for T=600, 120, 60.
uint64_t L(0), ST(0), next_D, prev_D, avg_D, i;
assert(timestamps.size() == cumulative_difficulties.size() && timestamps.size() <= N+1 );
if ( height <= DIFFICULTY_HEIGHT + 1 ) { return DIFFICULTY_GUESS; }
// Safely convert out-of-sequence timestamps into > 0 solvetimes.
std::vector<uint64_t>TS(N+1);
TS[0] = timestamps[0];
for ( i = 1; i <= N; i++) {
if ( timestamps[i] > TS[i-1] ) { TS[i] = timestamps[i]; }
else { TS[i] = TS[i-1]; }
}
for ( i = 1; i <= N; i++) {
// Temper long solvetime drops if they were preceded by 3 or 6 fast solves.
if ( i > 4 && TS[i]-TS[i-1] > 5*T && TS[i-1] - TS[i-4] < (14*T)/10 ) { ST = 2*T; }
else if ( i > 7 && TS[i]-TS[i-1] > 5*T && TS[i-1] - TS[i-7] < 4*T ) { ST = 2*T; }
else { // Assume normal conditions, so get ST.
// LWMA drops too much from long ST, so limit drops with a 5*T limit
ST = std::min(5*T ,TS[i] - TS[i-1]);
}
L += ST * i ;
}
if (L < N*N*T/20 ) { L = N*N*T/20; }
avg_D = ( cumulative_difficulties[N] - cumulative_difficulties[0] )/ N;
// Prevent round off error for small D and overflow for large D.
if (avg_D > 2000000*N*N*T) {
next_D = (avg_D/(200*L))*(N*(N+1)*T*97);
}
else { next_D = (avg_D*N*(N+1)*T*97)/(200*L); }
prev_D = cumulative_difficulties[N] - cumulative_difficulties[N-1] ;
// Apply 10% jump rule.
if ( ( TS[N] - TS[N-1] < (2*T)/10 ) ||
( TS[N] - TS[N-2] < (5*T)/10 ) ||
( TS[N] - TS[N-3] < (8*T)/10 ) )
{
next_D = std::max( next_D, std::min( (prev_D*110)/100, (105*avg_D)/100 ) );
}
// Make all insignificant digits zero for easy reading.
i = 1000000000;
while (i > 1) {
if ( next_D > i*100 ) { next_D = ((next_D+i/2)/i)*i; break; }
else { i /= 10; }
}
// Make least 3 digits equal avg of past 10 solvetimes.
if ( next_D > 100000 ) {
next_D = ((next_D+500)/1000)*1000 + std::min(static_cast<uint64_t>(999), (TS[N]-TS[N-10])/10);
}
if ( next_D < DIFFICULTY_MINIMUM ) {
return static_cast<uint64_t>(DIFFICULTY_MINIMUM);
}
else {
return static_cast<uint64_t>(next_D);
}
}
}

View File

@@ -55,4 +55,5 @@ namespace cryptonote
difficulty_type next_difficulty(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
difficulty_type next_difficulty_v2(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
difficulty_type next_difficulty_v3(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t height);
difficulty_type next_difficulty_v4(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t height);
}

View File

@@ -48,6 +48,7 @@
#define CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT 60*60*2
#define CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE 4
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 11
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60
// MONEY_SUPPLY - total number coins to be generated
@@ -81,12 +82,9 @@
#define DIFFICULTY_CUT 60 // timestamps to cut after sorting
#define DIFFICULTY_BLOCKS_COUNT_V2 DIFFICULTY_WINDOW_V2 + 1 // added +1 to make N=N
#define DIFFICULTY_BLOCKS_COUNT DIFFICULTY_WINDOW + DIFFICULTY_LAG
#define DIFFICULTY_HEIGHT 53666 // v9 fork height
#define DIFFICULTY_GUESS 40000000 // difficulty at fork 40m
#define DIFFICULTY_MINIMUM 10000000 // minimum difficulty set to 10m
#define DIFFICULTY_TESTNET_HEIGHT 100
#define DIFFICULTY_TESTNET_GUESS 5069
#define DIFFICULTY_TESTNET_MINIMUM 4069
#define DIFFICULTY_HEIGHT 63469 // v10 fork height
#define DIFFICULTY_GUESS 100000069 // difficulty at fork 100m
#define DIFFICULTY_MINIMUM 40000069 // minimum difficulty set to 40m
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V1 DIFFICULTY_TARGET_V1 * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS
#define CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_SECONDS_V2 DIFFICULTY_TARGET_V2 * CRYPTONOTE_LOCKED_TX_ALLOWED_DELTA_BLOCKS

View File

@@ -93,6 +93,7 @@ static const struct {
{ 7, 1, 0, 1519605000 },
{ 8, 6969, 0, 1524214739 },
{ 9, 53666, 0, 1538689773 },
{ 10, 63469, 0, 1541700352 },
};
static const uint64_t mainnet_hard_fork_version_1_till = ((uint64_t)(0));
@@ -838,8 +839,11 @@ difficulty_type Blockchain::get_difficulty_for_next_block()
else if (version == 8) {
return next_difficulty_v2(timestamps, difficulties, target);
}
else {
else if (version == 9) {
return next_difficulty_v3(timestamps, difficulties, height);
}
else {
return next_difficulty_v4(timestamps, difficulties, height);
}
}
//------------------------------------------------------------------
@@ -1056,9 +1060,12 @@ difficulty_type Blockchain::get_next_difficulty_for_alternative_chain(const std:
}
else if (version == 8) {
return next_difficulty_v2(timestamps, cumulative_difficulties, target);
}
else {
}
else if (version == 9) {
return next_difficulty_v3(timestamps, cumulative_difficulties, height);
}
else {
return next_difficulty_v4(timestamps, cumulative_difficulties, height);
}
}
@@ -1350,12 +1357,13 @@ bool Blockchain::create_block_template(block& b, const account_public_address& m
bool Blockchain::complete_timestamps_vector(uint64_t start_top_height, std::vector<uint64_t>& timestamps)
{
LOG_PRINT_L3("Blockchain::" << __func__);
if(timestamps.size() >= BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
const uint8_t hf_version = m_hardfork->get_current_version();
size_t blockchain_timestamp_check_window = hf_version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
if(timestamps.size() >= blockchain_timestamp_check_window)
return true;
CRITICAL_REGION_LOCAL(m_blockchain_lock);
size_t need_elements = BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW - timestamps.size();
size_t need_elements = blockchain_timestamp_check_window - timestamps.size();
CHECK_AND_ASSERT_MES(start_top_height < m_db->height(), false, "internal error: passed start_height not < " << " m_db->height() -- " << start_top_height << " >= " << m_db->height());
size_t stop_offset = start_top_height > need_elements ? start_top_height - need_elements : 0;
while (start_top_height != stop_offset)
@@ -3203,10 +3211,12 @@ bool Blockchain::check_block_timestamp(std::vector<uint64_t>& timestamps, const
{
LOG_PRINT_L3("Blockchain::" << __func__);
median_ts = epee::misc_utils::median(timestamps);
const uint8_t hf_version = m_hardfork->get_current_version();
size_t blockchain_timestamp_check_window = hf_version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
if(b.timestamp < median_ts)
{
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW << " blocks, " << median_ts);
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", less than median of last " << blockchain_timestamp_check_window << " blocks, " << median_ts);
return false;
}
@@ -3224,6 +3234,8 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
{
LOG_PRINT_L3("Blockchain::" << __func__);
uint64_t cryptonote_block_future_time_limit = get_current_hard_fork_version() >= 8 ? CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT_V2 : CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT;
const uint8_t hf_version = m_hardfork->get_current_version();
size_t blockchain_timestamp_check_window = hf_version >= 10 ? BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW_V2 : BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
if(b.timestamp > get_adjusted_time() + cryptonote_block_future_time_limit)
{
MERROR_VER("Timestamp of block with id: " << get_block_hash(b) << ", " << b.timestamp << ", bigger than adjusted time + 10 minutes");
@@ -3231,7 +3243,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
}
// if not enough blocks, no proper median yet, return true
if(m_db->height() < BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW)
if(m_db->height() < blockchain_timestamp_check_window)
{
return true;
}
@@ -3240,7 +3252,7 @@ bool Blockchain::check_block_timestamp(const block& b, uint64_t& median_ts) cons
auto h = m_db->height();
// need most recent 60 blocks, get index of first of those
size_t offset = h - BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW;
size_t offset = h - blockchain_timestamp_check_window;
for(;offset < h; ++offset)
{
timestamps.push_back(m_db->get_block_timestamp(offset));
@@ -4441,7 +4453,7 @@ void Blockchain::cancel()
}
#if defined(PER_BLOCK_CHECKPOINT)
static const char expected_block_hashes_hash[] = "16cb7e839284c1910925f8d9f8a752f38ad908ae9deec7afdfbbc4a777f5ef2e";
static const char expected_block_hashes_hash[] = "5aaafa48eb50fc5d8fa95b3a94da504170082245dd1f2d32ec7b72eb60877711";
void Blockchain::load_compiled_in_block_hashes()
{
const bool testnet = m_nettype == TESTNET;

View File

@@ -1344,16 +1344,24 @@ namespace cryptonote
main_message = "The daemon is running offline and will not attempt to sync to the Monero network.";
else
main_message = "The daemon will start synchronizing with the network. This may take a long time to complete.";
MGINFO_BLUE(ENDL <<
MGINFO_GREEN(ENDL <<
"\n \n"
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░\n"
"░░░░░░░░░░░░░▄█▄░░░░████▄░████▄░█░░░░░░░░▄█▄░░░░██░░░░▄▀░░▄███▄░░░░░░░░░░░░\n"
"░░░░░░░░░░░░░█▀ ▀▄░░█░░░█░█░░░█░█░░░░░░░░█▀ ▀▄░░█ █░░▄▀░░░█▀░░░▀░░░░░░░░░░░\n"
"░░░░░░░░░░░░░█░░░░░░█░░░█░█░░░█░█░░░░░░░░█░░░░░░█▄▄█ █░▀▄░██▄▄░░░░░░░░░░░░░\n"
"░░░░░░░░░░░░░█▄░░▄▀ ▀████░▀████░███▄░░░░░█▄░░▄▀ █░░█ █░░░█░█▄░░░▄▀░░░░░░░░░\n"
"░░░░░░░░░░░░░▀███▀░░░░░░░░░░░░░░░░░░▀░░░░▀███▀░░░░░█░░███░░▀███▀░░░░░░░░░░░\n"
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█░░░░░░░░░░░░░░░░░░░░░░░░\n"
"░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░▀░░░░░░░░░░░░░░░░░░░░░░░░░" << ENDL);
" \n"
" | __/| \n"
" | @ @ WoW! \n"
" | <> _ \n"
" | _/|------____ ((| |)) \n"
" | `--' | \n"
" ____|_ ___| |___.' \n"
" /_/_____/____/_______| \n"
"########################################################\n"
"### ____ ############ _ #### ____ ######################\n"
"###| _ | __ _ _ __ | | __ | _ | ___ __ _ ___ ###\n"
"###| | | |/ _` | '_ || |/ / | | | |/ _ | / _` |/ _ | ###\n"
"###| |_| | (_| | | | | < | |_| | (_) | (_| | __/ ###\n"
"###|____/ |__,_|_| |_|_||_| |____/ |___/ __, ||___| ###\n"
"#########################################|___/##########\n"
"########################################################"<< ENDL);
MGINFO_YELLOW(ENDL << "**********************************************************************" << ENDL
<< main_message << ENDL
<< ENDL

View File

@@ -127,7 +127,7 @@ namespace cryptonote
out_amounts[1] += out_amounts[0];
for (size_t n = 1; n < out_amounts.size(); ++n)
out_amounts[n - 1] = out_amounts[n];
out_amounts.resize(out_amounts.size() - 1);
out_amounts.pop_back();
}
}
else

View File

@@ -1095,7 +1095,7 @@ namespace cryptonote
LockedTXN lock(m_blockchain);
auto sorted_it = m_txs_by_fee_and_receive_time.begin();
while (sorted_it != m_txs_by_fee_and_receive_time.end())
for (; sorted_it != m_txs_by_fee_and_receive_time.end(); ++sorted_it)
{
txpool_tx_meta_t meta;
if (!m_blockchain.get_txpool_tx_meta(sorted_it->second, meta))
@@ -1109,7 +1109,6 @@ namespace cryptonote
if (max_total_size < total_size + meta.blob_size)
{
LOG_PRINT_L2(" would exceed maximum block size");
sorted_it++;
continue;
}
@@ -1122,14 +1121,12 @@ namespace cryptonote
if(!get_block_reward(median_size, total_size + meta.blob_size, already_generated_coins, block_reward, version))
{
LOG_PRINT_L2(" would exceed maximum block size");
sorted_it++;
continue;
}
coinbase = block_reward + fee + meta.fee;
if (coinbase < template_accept_threshold(best_coinbase))
{
LOG_PRINT_L2(" would decrease coinbase to " << print_money(coinbase));
sorted_it++;
continue;
}
}
@@ -1173,13 +1170,11 @@ namespace cryptonote
if (!ready)
{
LOG_PRINT_L2(" not ready to go");
sorted_it++;
continue;
}
if (have_key_images(k_images, tx))
{
LOG_PRINT_L2(" key images already seen");
sorted_it++;
continue;
}
@@ -1188,7 +1183,6 @@ namespace cryptonote
fee += meta.fee;
best_coinbase = coinbase;
append_key_images(k_images, tx);
sorted_it++;
LOG_PRINT_L2(" added, new block size " << total_size << "/" << max_total_size << ", coinbase " << print_money(best_coinbase));
}
@@ -1284,6 +1278,7 @@ namespace cryptonote
{
MWARNING("Failed to parse tx from txpool, removing");
remove.push_back(txid);
return true;
}
if (!insert_key_images(tx, meta.kept_by_block))
{

View File

@@ -1167,8 +1167,20 @@ skip:
+ " blocks/sec), " + std::to_string(m_block_queue.get_data_size() / 1048576.f) + " MB queued";
if (ELPP->vRegistry()->allowed(el::Level::Debug, "sync-info"))
timing_message += std::string(": ") + m_block_queue.get_overview();
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
if(m_core.get_target_blockchain_height() == 0){
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< timing_message);
} else {
const int completition_percent = (m_core.get_current_blockchain_height() * 100 / m_core.get_target_blockchain_height());
if(completition_percent < 99) {//printing completion percent only if % is < of 99 cause for 99 >= this is useless
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< " (" << completition_percent << "% " << (m_core.get_target_blockchain_height() - m_core.get_current_blockchain_height())
<< " blocks remaining)" << timing_message);
} else {
MGINFO_YELLOW(context << " Synced " << m_core.get_current_blockchain_height() << "/" << m_core.get_target_blockchain_height()
<< timing_message);
}
}
}
}
}
@@ -1752,3 +1764,4 @@ skip:
m_core.stop();
}
} // namespace

View File

@@ -661,7 +661,7 @@ bool t_command_parser_executor::sync_info(const std::vector<std::string>& args)
bool t_command_parser_executor::version(const std::vector<std::string>& args)
{
std::cout << "Monero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl;
std::cout << "Wownero '" << MONERO_RELEASE_NAME << "' (v" << MONERO_VERSION_FULL << ")" << std::endl;
return true;
}

View File

@@ -438,7 +438,8 @@ bool t_rpc_command_executor::show_status() {
}
}
tools::success_msg_writer() << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections, uptime %ud %uh %um %us")
std::stringstream str;
str << boost::format("Height: %llu/%llu (%.1f%%) on %s%s, %s, net hash %s, v%u%s, %s, %u(out)+%u(in) connections")
% (unsigned long long)ires.height
% (unsigned long long)net_height
% get_sync_percentage(ires)
@@ -451,12 +452,21 @@ bool t_rpc_command_executor::show_status() {
% (hfres.state == cryptonote::HardFork::Ready ? "up to date" : hfres.state == cryptonote::HardFork::UpdateNeeded ? "update needed" : "out of date, likely forked")
% (unsigned)ires.outgoing_connections_count
% (unsigned)ires.incoming_connections_count
% (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0)
% (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0))
% (unsigned int)floor(fmod((uptime / 60.0), 60.0))
% (unsigned int)fmod(uptime, 60.0)
;
// restricted RPC does not disclose start time
if (ires.start_time)
{
str << boost::format(", uptime %ud %uh %um %us")
% (unsigned int)floor(uptime / 60.0 / 60.0 / 24.0)
% (unsigned int)floor(fmod((uptime / 60.0 / 60.0), 24.0))
% (unsigned int)floor(fmod((uptime / 60.0), 60.0))
% (unsigned int)fmod(uptime, 60.0)
;
}
tools::success_msg_writer() << str.str();
return true;
}
@@ -554,7 +564,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
if (!first)
std::cout << std::endl;
std::cout
<< "height: " << header.height << ", timestamp: " << header.timestamp << ", difficulty: " << header.difficulty
<< "height: " << header.height << ", timestamp: " << header.timestamp
<< ", size: " << header.block_size << ", transactions: " << header.num_txes << std::endl
<< "major version: " << (unsigned)header.major_version << ", minor version: " << (unsigned)header.minor_version << std::endl
<< "block id: " << header.hash << ", previous block id: " << header.prev_hash << std::endl

View File

@@ -863,7 +863,13 @@ namespace cryptonote
boost::thread::attributes attrs;
attrs.set_stack_size(THREAD_STACK_SIZE);
if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
cryptonote::miner &miner= m_core.get_miner();
if (miner.is_mining())
{
res.status = "Already mining";
return true;
}
if(!miner.start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery))
{
res.status = "Failed, mining not started";
LOG_PRINT_L0(res.status);
@@ -992,6 +998,8 @@ namespace cryptonote
return r;
m_core.get_pool_transactions_and_spent_keys_info(res.transactions, res.spent_key_images, !request_has_rpc_origin || !m_restricted);
for (tx_info& txi : res.transactions)
txi.tx_blob = epee::string_tools::buff_to_hex_nodelimer(txi.tx_blob);
res.status = CORE_RPC_STATUS_OK;
return true;
}

File diff suppressed because it is too large Load Diff

View File

@@ -91,13 +91,13 @@ namespace cryptonote
//! \return Prompts user for password and verifies against local file. Logs on error and returns `none`
boost::optional<tools::password_container> get_and_verify_password() const;
bool new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key,
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const crypto::secret_key& recovery_key,
bool recover, bool two_random, const std::string &old_language);
bool new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const cryptonote::account_public_address& address,
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
bool new_wallet(const boost::program_options::variables_map& vm,
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
const std::string &multisig_keys, const std::string &old_language);
bool new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name);
bool open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();
@@ -121,7 +121,7 @@ namespace cryptonote
bool set_store_tx_info(const std::vector<std::string> &args = std::vector<std::string>());
bool set_auto_refresh(const std::vector<std::string> &args = std::vector<std::string>());
bool set_refresh_type(const std::vector<std::string> &args = std::vector<std::string>());
bool set_confirm_missing_payment_id(const std::vector<std::string> &args = std::vector<std::string>());
bool set_confirm_subaddress(const std::vector<std::string> &args = std::vector<std::string>());
bool set_ask_password(const std::vector<std::string> &args = std::vector<std::string>());
bool set_unit(const std::vector<std::string> &args = std::vector<std::string>());
bool set_min_output_count(const std::vector<std::string> &args = std::vector<std::string>());
@@ -167,7 +167,6 @@ namespace cryptonote
void print_accounts();
void print_accounts(const std::string& tag);
bool print_address(const std::vector<std::string> &args = std::vector<std::string>());
bool print_integrated_address(const std::vector<std::string> &args = std::vector<std::string>());
bool address_book(const std::vector<std::string> &args = std::vector<std::string>());
bool save(const std::vector<std::string> &args);
bool save_watch_only(const std::vector<std::string> &args);
@@ -211,6 +210,7 @@ namespace cryptonote
bool sign_multisig(const std::vector<std::string>& args);
bool submit_multisig(const std::vector<std::string>& args);
bool export_raw_multisig(const std::vector<std::string>& args);
bool mms(const std::vector<std::string>& args);
bool print_ring(const std::vector<std::string>& args);
bool set_ring(const std::vector<std::string>& args);
bool save_known_rings(const std::vector<std::string>& args);
@@ -352,5 +352,38 @@ namespace cryptonote
bool m_auto_refresh_refreshing;
std::atomic<bool> m_in_manual_refresh;
uint32_t m_current_subaddress_account;
// MMS
mms::message_store& get_message_store() const { return m_wallet->get_message_store(); };
mms::multisig_wallet_state get_multisig_wallet_state() const { return m_wallet->get_multisig_wallet_state(); };
bool mms_active() const { return get_message_store().get_active(); };
bool choose_mms_processing(const std::vector<mms::processing_data> &data_list, uint32_t &choice);
void list_mms_messages(const std::vector<mms::message> &messages);
void show_message(const mms::message &m);
void ask_send_all_ready_messages();
void check_for_messages();
bool user_confirms(const std::string &question);
bool get_message_from_arg(const std::string &arg, mms::message &m);
bool get_number_from_arg(const std::string &arg, uint32_t &number, const uint32_t lower_bound, const uint32_t upper_bound);
void mms_init(const std::vector<std::string> &args);
void mms_info(const std::vector<std::string> &args);
void mms_member(const std::vector<std::string> &args);
void mms_list(const std::vector<std::string> &args);
void mms_next(const std::vector<std::string> &args);
void mms_sync(const std::vector<std::string> &args);
void mms_transfer(const std::vector<std::string> &args);
void mms_delete(const std::vector<std::string> &args);
void mms_send(const std::vector<std::string> &args);
void mms_receive(const std::vector<std::string> &args);
void mms_note(const std::vector<std::string> &args);
void mms_show(const std::vector<std::string> &args);
void mms_set(const std::vector<std::string> &args);
void mms_help(const std::vector<std::string> &args);
void mms_debug(const std::vector<std::string> &args);
bool m_called_by_mms = false;
bool called_by_mms();
bool m_command_successful;
};
}

View File

@@ -1,6 +1,6 @@
#define DEF_MONERO_VERSION_TAG "@VERSIONTAG@"
#define DEF_MONERO_VERSION "0.3.0.0-master"
#define DEF_MONERO_RELEASE_NAME "Cool Cage"
#define DEF_MONERO_VERSION "0.4.0.1"
#define DEF_MONERO_RELEASE_NAME "Dank Doge"
#define DEF_MONERO_VERSION_FULL DEF_MONERO_VERSION "-" DEF_MONERO_VERSION_TAG
#include "version.h"

View File

@@ -34,7 +34,10 @@ set(wallet_sources
wallet2.cpp
wallet_args.cpp
ringdb.cpp
node_rpc_proxy.cpp)
node_rpc_proxy.cpp
message_store.cpp
message_transporter.cpp
)
set(wallet_private_headers
wallet2.h
@@ -44,7 +47,9 @@ set(wallet_private_headers
wallet_rpc_server_commands_defs.h
wallet_rpc_server_error_codes.h
ringdb.h
node_rpc_proxy.h)
node_rpc_proxy.h
message_store.h
message_transporter.h)
monero_private_headers(wallet
${wallet_private_headers})
@@ -102,7 +107,6 @@ set_property(TARGET wallet_rpc_server
OUTPUT_NAME "wownero-wallet-rpc")
install(TARGETS wallet_rpc_server DESTINATION bin)
# build and install libwallet_merged only if we building for GUI
if (BUILD_GUI_DEPS)
set(libs_to_merge

View File

@@ -744,14 +744,6 @@ std::string WalletImpl::address(uint32_t accountIndex, uint32_t addressIndex) co
return m_wallet->get_subaddress_as_str({accountIndex, addressIndex});
}
std::string WalletImpl::integratedAddress(const std::string &payment_id) const
{
crypto::hash8 pid;
if (!tools::wallet2::parse_short_payment_id(payment_id, pid)) {
return "";
}
return m_wallet->get_integrated_address_as_str(pid);
}
std::string WalletImpl::secretViewKey() const
{

View File

@@ -88,7 +88,6 @@ public:
std::string errorString() const;
bool setPassword(const std::string &password);
std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const;
std::string integratedAddress(const std::string &payment_id) const;
std::string secretViewKey() const;
std::string publicViewKey() const;
std::string secretSpendKey() const;

View File

@@ -373,16 +373,6 @@ struct Wallet
virtual void hardForkInfo(uint8_t &version, uint64_t &earliest_height) const = 0;
//! check if hard fork rules should be used
virtual bool useForkRules(uint8_t version, int64_t early_blocks) const = 0;
/*!
* \brief integratedAddress - returns integrated address for current wallet address and given payment_id.
* if passed "payment_id" param is an empty string or not-valid payment id string
* (16 characters hexadecimal string) - random payment_id will be generated
*
* \param payment_id - 16 characters hexadecimal string or empty string if new random payment id needs to be
* generated
* \return - 106 characters string representing integrated address
*/
virtual std::string integratedAddress(const std::string &payment_id) const = 0;
/*!
* \brief secretViewKey - returns secret view key

1003
src/wallet/message_store.cpp Normal file

File diff suppressed because it is too large Load Diff

357
src/wallet/message_store.h Normal file
View File

@@ -0,0 +1,357 @@
// Copyright (c) 2014-2018, 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.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include <cstdlib>
#include <string>
#include <vector>
#include "crypto/hash.h"
#include <boost/serialization/vector.hpp>
#include "serialization/serialization.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_basic/account_boost_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "common/i18n.h"
#include "message_transporter.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.mms"
namespace mms
{
enum class message_type
{
key_set,
finalizing_key_set,
multisig_sync_data,
partially_signed_tx,
fully_signed_tx,
note
};
enum class message_direction
{
in,
out
};
enum class message_state
{
ready_to_send,
sent,
waiting,
processed,
cancelled
};
enum class message_processing
{
prepare_multisig,
make_multisig,
finalize_multisig,
create_sync_data,
process_sync_data,
sign_tx,
send_tx,
submit_tx
};
struct message
{
uint32_t id;
message_type type;
message_direction direction;
std::string content;
uint64_t created;
uint64_t modified;
uint64_t sent;
uint32_t member_index;
crypto::hash hash;
message_state state;
uint32_t wallet_height;
uint32_t round;
uint32_t signature_count;
std::string transport_id;
};
// "wallet_height" (for lack of a short name that would describe what it is about)
// is the number of transfers present in the wallet at the time of message
// construction; used to coordinate generation of sync info (which depends
// on the content of the wallet at time of generation)
struct coalition_member
{
std::string label;
std::string transport_address;
bool monero_address_known;
cryptonote::account_public_address monero_address;
bool me;
uint32_t index;
};
struct processing_data
{
message_processing processing;
std::vector<uint32_t> message_ids;
uint32_t receiving_member_index = 0;
};
struct file_transport_message
{
cryptonote::account_public_address sender_address;
crypto::chacha_iv iv;
crypto::public_key encryption_public_key;
message internal_message;
};
// Overal .mms file structure, with the "message_store" object serialized to and
// encrypted in "encrypted_data"
struct file_data
{
std::string magic_string;
uint32_t file_version;
crypto::chacha_iv iv;
std::string encrypted_data;
};
// The following struct provides info about the current state of a "wallet2" object
// at the time of a "message_store" method call that those methods need. See on the
// one hand a first parameter of this type for several of those methods, and on the
// other hand the method "wallet2::get_multisig_wallet_state" which clients like the
// CLI wallet can use to get that info.
//
// Note that in the case of a wallet that is already multisig "address" is NOT the
// multisig address, but the "original" wallet address at creation time. Likewise
// "view_secret_key" is the original view secret key then.
//
// This struct definition is here and not in "wallet2.h" to avoid circular imports.
struct multisig_wallet_state
{
cryptonote::account_public_address address;
cryptonote::network_type nettype;
crypto::secret_key view_secret_key;
bool multisig;
bool multisig_is_ready;
bool has_multisig_partial_key_images;
size_t num_transfer_details;
std::string mms_file;
~multisig_wallet_state()
{
view_secret_key = crypto::null_skey;
}
};
class message_store
{
public:
message_store();
// Initialize and start to use the MMS, set the first member, this wallet itself
// Filename, if not null and not empty, is used to create the ".mms" file
// reset it if already used, with deletion of all members and messages
void init(const multisig_wallet_state &state, const std::string &own_label,
const std::string &own_transport_address, uint32_t coalition_size, uint32_t threshold);
void set_active(bool active) { m_active = active; };
void set_auto_send(bool auto_send) { m_auto_send = auto_send; };
void set_options(const boost::program_options::variables_map& vm);
void set_options(const std::string &bitmessage_address, const std::string &bitmessage_login);
bool get_active() const { return m_active; };
bool get_auto_send() const { return m_auto_send; };
uint32_t get_threshold() const { return m_threshold; };
uint32_t get_coalition_size() const { return m_coalition_size; };
void set_member(const multisig_wallet_state &state,
uint32_t index,
const boost::optional<std::string> &label,
const boost::optional<std::string> &transport_address,
const boost::optional<cryptonote::account_public_address> monero_address);
const coalition_member &get_member(uint32_t index) const;
bool get_member_index_by_monero_address(const cryptonote::account_public_address &monero_address, uint32_t &index) const;
bool get_member_index_by_label(const std::string label, uint32_t &index) const;
const std::vector<coalition_member> &get_all_members() const { return m_members; };
bool member_info_complete() const;
// Process data just created by "me" i.e. the own local wallet, e.g. as the result of a "prepare_multisig" command
// Creates the resulting messages to the right members
void process_wallet_created_data(const multisig_wallet_state &state, message_type type, const std::string &content);
// Go through all the messages, look at the "ready to process" ones, and check whether any single one
// or any group of them can be processed, because they are processable as single messages (like a tx
// that is fully signed and thus ready for submit to the net) or because they form a complete group
// (e.g. key sets from all coalition members to make the wallet multisig). If there are multiple
// candidates, e.g. in 2/3 multisig sending to one OR the other member to sign, there will be more
// than 1 element in 'data' for the user to choose. If nothing is ready "false" is returned.
// The method mostly ignores the order in which the messages were received because messages may be delayed
// (e.g. sync data from a member arrives AFTER a transaction to submit) or because message time stamps
// may be wrong so it's not possible to order them reliably.
// Messages also may be ready by themselves but the wallet not yet ready for them (e.g. sync data already
// arriving when the wallet is not yet multisig because key sets were delayed or were lost altogether.)
// If nothing is ready 'wait_reason' may contain further info about the reason why.
bool get_processable_messages(const multisig_wallet_state &state,
bool force_sync,
std::vector<processing_data> &data_list,
std::string &wait_reason);
void set_messages_processed(const processing_data &data);
uint32_t add_message(const multisig_wallet_state &state,
uint32_t member_index, message_type type, message_direction direction,
const std::string &content);
const std::vector<message> &get_all_messages() const { return m_messages; };
bool get_message_by_id(uint32_t id, message &m) const;
message get_message_by_id(uint32_t id) const;
void set_message_processed_or_sent(uint32_t id);
void delete_message(uint32_t id);
void delete_all_messages();
void send_message(const multisig_wallet_state &state, uint32_t id);
bool check_for_messages(const multisig_wallet_state &state, std::vector<message> &messages);
void stop() { m_run.store(false, std::memory_order_relaxed); m_transporter.stop(); }
void write_to_file(const multisig_wallet_state &state, const std::string &filename);
void read_from_file(const multisig_wallet_state &state, const std::string &filename);
template <class t_archive>
inline void serialize(t_archive &a, const unsigned int ver)
{
a & m_active;
a & m_coalition_size;
a & m_nettype;
a & m_threshold;
a & m_members;
a & m_messages;
a & m_next_message_id;
a & m_auto_send;
}
const char* message_type_to_string(message_type type);
const char* message_direction_to_string(message_direction direction);
const char* message_state_to_string(message_state state);
std::string member_to_string(const coalition_member &member, uint32_t max_width);
static const char *tr(const char *str) { return i18n_translate(str, "tools::mms"); }
static void init_options(boost::program_options::options_description& desc_params);
private:
bool m_active;
uint32_t m_coalition_size;
uint32_t m_threshold;
bool m_auto_send;
cryptonote::network_type m_nettype;
std::vector<coalition_member> m_members;
std::vector<message> m_messages;
uint32_t m_next_message_id;
std::string m_filename;
message_transporter m_transporter;
std::atomic<bool> m_run;
bool get_message_index_by_id(uint32_t id, uint32_t &index) const;
uint32_t get_message_index_by_id(uint32_t id) const;
bool any_message_of_type(message_type type, message_direction direction) const;
bool any_message_with_hash(const crypto::hash &hash) const;
bool message_ids_complete(const std::vector<uint32_t> ids) const;
void encrypt(uint32_t member_index, const std::string &plaintext,
std::string &ciphertext, crypto::public_key &encryption_public_key, crypto::chacha_iv &iv);
void decrypt(const std::string &ciphertext, const crypto::public_key &encryption_public_key, const crypto::chacha_iv &iv,
const crypto::secret_key &view_secret_key, std::string &plaintext);
void delete_transport_message(uint32_t id);
std::string account_address_to_string(const cryptonote::account_public_address &account_address) const;
void save(const multisig_wallet_state &state);
};
}
BOOST_CLASS_VERSION(mms::file_data, 0)
BOOST_CLASS_VERSION(mms::message_store, 0)
BOOST_CLASS_VERSION(mms::message, 0)
BOOST_CLASS_VERSION(mms::file_transport_message, 0)
BOOST_CLASS_VERSION(mms::coalition_member, 0)
namespace boost
{
namespace serialization
{
template <class Archive>
inline void serialize(Archive &a, mms::file_data &x, const boost::serialization::version_type ver)
{
a & x.magic_string;
a & x.file_version;
a & x.iv;
a & x.encrypted_data;
}
template <class Archive>
inline void serialize(Archive &a, mms::message &x, const boost::serialization::version_type ver)
{
a & x.id;
a & x.type;
a & x.direction;
a & x.content;
a & x.created;
a & x.modified;
a & x.sent;
a & x.member_index;
a & x.hash;
a & x.state;
a & x.wallet_height;
a & x.round;
a & x.signature_count;
a & x.transport_id;
}
template <class Archive>
inline void serialize(Archive &a, mms::coalition_member &x, const boost::serialization::version_type ver)
{
a & x.label;
a & x.transport_address;
a & x.monero_address_known;
a & x.monero_address;
a & x.me;
a & x.index;
}
template <class Archive>
inline void serialize(Archive &a, mms::file_transport_message &x, const boost::serialization::version_type ver)
{
a & x.sender_address;
a & x.iv;
a & x.encryption_public_key;
a & x.internal_message;
}
template <class Archive>
inline void serialize(Archive &a, crypto::chacha_iv &x, const boost::serialization::version_type ver)
{
a & x.data;
}
}
}

View File

@@ -0,0 +1,287 @@
// Copyright (c) 2014-2018, 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.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#include "message_transporter.h"
#include "string_coding.h"
#include <boost/format.hpp>
#include "wallet_errors.h"
#include "net/http_client.h"
#include "net/net_parse_helpers.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.mms"
#define PYBITMESSAGE_DEFAULT_API_PORT 8442
namespace mms
{
namespace bitmessage_rpc
{
struct message_info
{
uint32_t encodingType;
std::string toAddress;
uint32_t read;
std::string msgid;
std::string message;
std::string fromAddress;
std::string receivedTime;
std::string subject;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(encodingType)
KV_SERIALIZE(toAddress)
KV_SERIALIZE(read)
KV_SERIALIZE(msgid)
KV_SERIALIZE(message);
KV_SERIALIZE(fromAddress)
KV_SERIALIZE(receivedTime)
KV_SERIALIZE(subject)
END_KV_SERIALIZE_MAP()
};
struct inbox_messages_response
{
std::vector<message_info> inboxMessages;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(inboxMessages)
END_KV_SERIALIZE_MAP()
};
}
message_transporter::message_transporter()
{
m_run = true;
}
void message_transporter::set_options(const std::string &bitmessage_address, const std::string &bitmessage_login) {
m_bitmessage_url = bitmessage_address;
epee::net_utils::http::url_content address_parts{};
epee::net_utils::parse_url(m_bitmessage_url, address_parts);
if (address_parts.port == 0)
{
address_parts.port = PYBITMESSAGE_DEFAULT_API_PORT;
}
auto pos = bitmessage_login.find(":");
if (pos == std::string::npos)
{
m_bitmessage_user = bitmessage_login;
m_bitmessage_password.clear();
}
else
{
m_bitmessage_user = bitmessage_login.substr(0, pos);
m_bitmessage_password = bitmessage_login.substr(pos + 1);
}
boost::optional<epee::net_utils::http::login> login{};
login.emplace(m_bitmessage_user, m_bitmessage_password);
m_http_client.set_server(address_parts.host, std::to_string(address_parts.port), login);
}
bool message_transporter::receive_messages(const cryptonote::account_public_address &destination_monero_address,
const std::string &destination_transport_address,
std::vector<transport_message> &messages)
{
// The message body of the Bitmessage message is basically the transport message, as JSON (and nothing more).
// Weeding out other, non-MMS messages is done in a simple way: If it deserializes without error, it's an MMS message
// That JSON is Base64-encoded by the MMS because the Monero epee JSON serializer does not escape anything and happily
// includes even 0 (NUL) in strings, which might confuse Bitmessage or at least display confusingly in the client.
// There is yet another Base64-encoding of course as part of the Bitmessage API for the message body parameter
// The Bitmessage API call "getAllInboxMessages" gives back a JSON array with all the messages (despite using
// XML-RPC for the calls, and not JSON-RPC ...)
m_run.store(true, std::memory_order_relaxed);
std::string request;
start_xml_rpc_cmd(request, "getAllInboxMessages");
end_xml_rpc_cmd(request);
std::string answer;
post_request(request, answer);
std::string json = get_str_between_tags(answer, "<string>", "</string>");
bitmessage_rpc::inbox_messages_response bitmessage_res;
epee::serialization::load_t_from_json(bitmessage_res, json);
size_t size = bitmessage_res.inboxMessages.size();
messages.clear();
for (size_t i = 0; i < size; ++i)
{
if (!m_run.load(std::memory_order_relaxed))
{
// Stop was called, don't waste time processing any more messages
return false;
}
bitmessage_rpc::message_info message_info = bitmessage_res.inboxMessages[i];
transport_message message;
bool is_mms_message = false;
try
{
// First Base64-decoding: The message body is Base64 in the Bitmessage API
std::string message_body = epee::string_encoding::base64_decode(message_info.message);
// Second Base64-decoding: The MMS uses Base64 to hide non-textual data in its JSON from Bitmessage
json = epee::string_encoding::base64_decode(message_body);
epee::serialization::load_t_from_json(message, json);
is_mms_message = true;
}
catch(const std::exception& e)
{
}
if (is_mms_message)
{
if (message.destination_monero_address == destination_monero_address)
{
message.transport_id = message_info.msgid;
messages.push_back(message);
}
}
}
return true;
}
bool message_transporter::send_message(const transport_message &message)
{
// <toAddress> <fromAddress> <subject> <message> [encodingType [TTL]]
std::string request;
start_xml_rpc_cmd(request, "sendMessage");
add_xml_rpc_string_param(request, message.destination_transport_address);
add_xml_rpc_string_param(request, message.source_transport_address);
add_xml_rpc_base64_param(request, message.subject);
std::string json = epee::serialization::store_t_to_json(message);
std::string message_body = epee::string_encoding::base64_encode(json); // See comment in "receive_message" about reason for (double-)Base64 encoding
add_xml_rpc_base64_param(request, message_body);
add_xml_rpc_integer_param(request, 2);
end_xml_rpc_cmd(request);
std::string answer;
post_request(request, answer);
return true;
}
bool message_transporter::delete_message(const std::string &transport_id)
{
std::string request;
start_xml_rpc_cmd(request, "trashMessage");
add_xml_rpc_string_param(request, transport_id);
end_xml_rpc_cmd(request);
std::string answer;
post_request(request, answer);
return true;
}
bool message_transporter::post_request(const std::string &request, std::string &answer)
{
// Somehow things do not work out if one tries to connect "m_http_client" to Bitmessage
// and keep it connected over the course of several calls. But with a new connection per
// call and disconnecting after the call there is no problem (despite perhaps a small
// slowdown)
epee::net_utils::http::fields_list additional_params;
// Basic access authentication according to RFC 7617 (which the epee HTTP classes do not seem to support?)
std::string user_password(m_bitmessage_user + ":" + m_bitmessage_password);
std::string auth_string = epee::string_encoding::base64_encode(user_password);
auth_string.insert(0, "Basic ");
additional_params.push_back(std::make_pair("Authorization", auth_string));
additional_params.push_back(std::make_pair("Content-Type", "application/xml; charset=utf-8"));
const epee::net_utils::http::http_response_info* response = NULL;
std::chrono::milliseconds timeout = std::chrono::seconds(15);
bool r = m_http_client.invoke("/", "POST", request, timeout, std::addressof(response), std::move(additional_params));
if (r)
{
answer = response->m_body;
}
else
{
LOG_ERROR("POST request to Bitmessage failed: " << request.substr(0, 300));
THROW_WALLET_EXCEPTION(tools::error::no_connection_to_bitmessage, m_bitmessage_url);
}
m_http_client.disconnect(); // see comment above
std::string string_value = get_str_between_tags(answer, "<string>", "</string>");
if ((string_value.find("API Error") == 0) || (string_value.find("RPC ") == 0))
{
THROW_WALLET_EXCEPTION(tools::error::bitmessage_api_error, string_value);
}
return r;
}
// Pick some string between two delimiters
// When parsing the XML returned by PyBitmessage, don't bother to fully parse it but as a little hack rely on the
// fact that e.g. a single string returned will be, however deeply nested in "<params><param><value>...", delivered
// between the very first "<string>" and "</string>" tags to be found in the XML
std::string message_transporter::get_str_between_tags(const std::string &s, const std::string &start_delim, const std::string &stop_delim)
{
size_t first_delim_pos = s.find(start_delim);
if (first_delim_pos != std::string::npos)
{
size_t end_pos_of_first_delim = first_delim_pos + start_delim.length();
size_t last_delim_pos = s.find(stop_delim);
if (last_delim_pos != std::string::npos)
{
return s.substr(end_pos_of_first_delim, last_delim_pos - end_pos_of_first_delim);
}
}
return std::string();
}
void message_transporter::start_xml_rpc_cmd(std::string &xml, const std::string &method_name)
{
xml = (boost::format("<?xml version=\"1.0\"?><methodCall><methodName>%s</methodName><params>") % method_name).str();
}
void message_transporter::add_xml_rpc_string_param(std::string &xml, const std::string &param)
{
xml += (boost::format("<param><value><string>%s</string></value></param>") % param).str();
}
void message_transporter::add_xml_rpc_base64_param(std::string &xml, const std::string &param)
{
// Bitmessage expects some arguments Base64-encoded, but it wants them as parameters of type "string", not "base64" that is also part of XML-RPC
std::string encoded_param = epee::string_encoding::base64_encode(param);
xml += (boost::format("<param><value><string>%s</string></value></param>") % encoded_param).str();
}
void message_transporter::add_xml_rpc_integer_param(std::string &xml, const int32_t &param)
{
xml += (boost::format("<param><value><int>%i</int></value></param>") % param).str();
}
void message_transporter::end_xml_rpc_cmd(std::string &xml)
{
xml += "</params></methodCall>";
}
}

View File

@@ -0,0 +1,113 @@
// Copyright (c) 2014-2018, 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.
//
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
#pragma once
#include "serialization/keyvalue_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "cryptonote_basic/cryptonote_boost_serialization.h"
#include "cryptonote_basic/account_boost_serialization.h"
#include "cryptonote_basic/cryptonote_basic.h"
#include "net/http_server_impl_base.h"
#include "net/http_client.h"
#include "common/util.h"
#include "serialization/keyvalue_serialization.h"
#define PYBITMESSAGE_API_PORT 8442
namespace mms
{
struct transport_message
{
cryptonote::account_public_address source_monero_address;
std::string source_transport_address;
cryptonote::account_public_address destination_monero_address;
std::string destination_transport_address;
crypto::chacha_iv iv;
crypto::public_key encryption_public_key;
uint64_t timestamp;
uint32_t type;
std::string subject;
std::string content;
crypto::hash hash;
crypto::signature signature;
uint32_t round;
uint32_t signature_count;
std::string transport_id;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(source_monero_address)
KV_SERIALIZE(source_transport_address)
KV_SERIALIZE(destination_monero_address)
KV_SERIALIZE(destination_transport_address)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(iv)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(encryption_public_key)
KV_SERIALIZE(timestamp)
KV_SERIALIZE(type)
KV_SERIALIZE(subject)
KV_SERIALIZE(content)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(hash)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(signature)
KV_SERIALIZE(round)
KV_SERIALIZE(signature_count)
KV_SERIALIZE(transport_id)
END_KV_SERIALIZE_MAP()
};
class message_transporter {
public:
message_transporter();
void set_options(const std::string &bitmessage_address, const std::string &bitmessage_login);
bool send_message(const transport_message &message);
bool receive_messages(const cryptonote::account_public_address &destination_monero_address,
const std::string &destination_transport_address,
std::vector<transport_message> &messages);
bool delete_message(const std::string &transport_id);
void stop() { m_run.store(false, std::memory_order_relaxed); }
private:
epee::net_utils::http::http_simple_client m_http_client;
std::string m_bitmessage_url;
std::string m_bitmessage_user;
std::string m_bitmessage_password;
std::atomic<bool> m_run;
bool post_request(const std::string &request, std::string &answer);
std::string get_str_between_tags(const std::string &s, const std::string &start_delim, const std::string &stop_delim);
void start_xml_rpc_cmd(std::string &xml, const std::string &method_name);
void add_xml_rpc_string_param(std::string &xml, const std::string &param);
void add_xml_rpc_base64_param(std::string &xml, const std::string &param);
void add_xml_rpc_integer_param(std::string &xml, const int32_t &param);
void end_xml_rpc_cmd(std::string &xml);
};
}

View File

@@ -146,7 +146,7 @@ boost::optional<std::string> NodeRPCProxy::get_earliest_height(uint8_t version,
CHECK_AND_ASSERT_MES(r, std::string(), "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status != CORE_RPC_STATUS_BUSY, resp_t.status, "Failed to connect to daemon");
CHECK_AND_ASSERT_MES(resp_t.status == CORE_RPC_STATUS_OK, resp_t.status, "Failed to get hard fork status");
m_earliest_height[version] = resp_t.enabled ? resp_t.earliest_height : std::numeric_limits<uint64_t>::max();
m_earliest_height[version] = resp_t.earliest_height;
}
earliest_height = m_earliest_height[version];

View File

@@ -153,7 +153,7 @@ struct options {
};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file, std::string &mms_file)
{
keys_file = file_path;
wallet_file = file_path;
@@ -165,6 +165,7 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file,
{//provided wallet file name
keys_file += ".keys";
}
mms_file = file_path + ".mms";
}
uint64_t calculate_fee(uint64_t fee_per_kb, size_t bytes, uint64_t fee_multiplier)
@@ -230,6 +231,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
wallet->init(std::move(daemon_address), std::move(login));
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
wallet->get_message_store().set_options(vm);
return wallet;
}
@@ -651,7 +653,7 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_auto_refresh(true),
m_refresh_from_block_height(0),
m_explicit_refresh_from_block_height(true),
m_confirm_missing_payment_id(true),
m_confirm_subaddress(true),
m_confirm_non_default_ring_size(true),
m_ask_password(true),
m_min_output_count(0),
@@ -676,6 +678,8 @@ wallet2::wallet2(network_type nettype, bool restricted):
m_light_wallet_connected(false),
m_light_wallet_balance(0),
m_light_wallet_unlocked_balance(0),
m_original_keys_available(false),
m_message_store(),
m_key_on_device(false),
m_ring_history_saved(false),
m_ringdb()
@@ -709,6 +713,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par
command_line::add_arg(desc_params, opts.stagenet);
command_line::add_arg(desc_params, opts.restricted);
command_line::add_arg(desc_params, opts.shared_ringdb_dir);
mms::message_store::init_options(desc_params);
}
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@@ -887,6 +892,14 @@ cryptonote::account_public_address wallet2::get_subaddress(const cryptonote::sub
return hwdev.get_subaddress(m_account.get_keys(), index);
}
//----------------------------------------------------------------------------------------------------
boost::optional<cryptonote::subaddress_index> wallet2::get_subaddress_index(const cryptonote::account_public_address& address) const
{
auto index = m_subaddresses.find(address.m_spend_public_key);
if (index == m_subaddresses.end())
return boost::none;
return index->second;
}
//----------------------------------------------------------------------------------------------------
crypto::public_key wallet2::get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const
{
hw::device &hwdev = m_account.get_device();
@@ -898,6 +911,7 @@ std::string wallet2::get_subaddress_as_str(const cryptonote::subaddress_index& i
cryptonote::account_public_address address = get_subaddress(index);
return cryptonote::get_account_address_as_str(m_nettype, !index.is_zero(), address);
}
//----------------------------------------------------------------------------------------------------
std::string wallet2::get_integrated_address_as_str(const crypto::hash8& payment_id) const
{
@@ -1105,7 +1119,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// (that is, the prunable stuff may or may not be included)
if (!miner_tx && !pool)
process_unconfirmed(txid, tx, height);
std::vector<size_t> outs;
std::unordered_map<cryptonote::subaddress_index, uint64_t> tx_money_got_in_outs; // per receiving subaddress index
crypto::public_key tx_pub_key = null_pkey;
@@ -1123,6 +1136,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
uint64_t total_received_1 = 0;
while (!tx.vout.empty())
{
std::vector<size_t> outs;
// if tx.vout is not empty, we loop through all tx pubkeys
tx_extra_pub_key pub_key_field;
@@ -2572,6 +2586,7 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
rapidjson::Document json;
json.SetObject();
rapidjson::Value value(rapidjson::kStringType);
value.SetString(account_data.c_str(), account_data.length());
json.AddMember("key_data", value, json.GetAllocator());
if (!seed_language.empty())
@@ -2623,8 +2638,8 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint64(m_refresh_from_block_height);
json.AddMember("refresh_height", value2, json.GetAllocator());
value2.SetInt(m_confirm_missing_payment_id ? 1 :0);
json.AddMember("confirm_missing_payment_id", value2, json.GetAllocator());
value2.SetInt(m_confirm_subaddress ? 1 :0);
json.AddMember("confirm_subaddress", value2, json.GetAllocator());
value2.SetInt(m_confirm_non_default_ring_size ? 1 :0);
json.AddMember("confirm_non_default_ring_size", value2, json.GetAllocator());
@@ -2674,6 +2689,22 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(m_subaddress_lookahead_minor);
json.AddMember("subaddress_lookahead_minor", value2, json.GetAllocator());
value2.SetInt(m_original_keys_available ? 1 : 0);
json.AddMember("original_keys_available", value2, json.GetAllocator());
std::string original_address;
std::string original_view_secret_key;
if (m_original_keys_available)
{
original_address = get_account_address_as_str(m_nettype, false, m_original_address);
value.SetString(original_address.c_str(), original_address.length());
json.AddMember("original_address", value, json.GetAllocator());
original_view_secret_key = epee::string_tools::pod_to_hex(m_original_view_secret_key);
value.SetString(original_view_secret_key.c_str(), original_view_secret_key.length());
json.AddMember("original_view_secret_key", value, json.GetAllocator());
}
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -2734,7 +2765,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_default_priority = 0;
m_auto_refresh = true;
m_refresh_type = RefreshType::RefreshDefault;
m_confirm_missing_payment_id = true;
m_confirm_subaddress = true;
m_confirm_non_default_ring_size = true;
m_ask_password = true;
m_min_output_count = 0;
@@ -2749,6 +2780,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_segregation_height = 0;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
m_original_keys_available = false;
m_key_on_device = false;
}
else if(json.IsObject())
@@ -2837,8 +2869,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
}
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, refresh_height, uint64_t, Uint64, false, 0);
m_refresh_from_block_height = field_refresh_height;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_missing_payment_id, int, Int, false, true);
m_confirm_missing_payment_id = field_confirm_missing_payment_id;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_subaddress, int, Int, false, true);
m_confirm_subaddress = field_confirm_subaddress;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, confirm_non_default_ring_size, int, Int, false, true);
m_confirm_non_default_ring_size = field_confirm_non_default_ring_size;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, ask_password, int, Int, false, true);
@@ -2875,6 +2907,44 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_subaddress_lookahead_major = field_subaddress_lookahead_major;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR);
m_subaddress_lookahead_minor = field_subaddress_lookahead_minor;
if (json.HasMember("original_keys_available"))
{
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_keys_available, int, Int, false, false);
m_original_keys_available = field_original_keys_available;
if (m_original_keys_available)
{
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_address, std::string, String, false, std::string());
if (!field_original_address_found)
{
LOG_ERROR("Field original_address not found in JSON");
return false;
}
address_parse_info info;
bool ok = get_account_address_from_str(info, m_nettype, field_original_address);
if (!ok)
{
LOG_ERROR("Failed to parse original_address from JSON");
return false;
}
m_original_address = info.address;
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, original_view_secret_key, std::string, String, false, std::string());
if (!field_original_view_secret_key_found)
{
LOG_ERROR("Field original_view_secret_key not found in JSON");
return false;
}
ok = epee::string_tools::hex_to_pod(field_original_view_secret_key, m_original_view_secret_key);
if (!ok)
{
LOG_ERROR("Failed to parse original_view_secret_key from JSON");
}
}
}
else
{
m_original_keys_available = false;
}
}
else
{
@@ -3042,6 +3112,10 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig_signers = multisig_signers;
m_key_on_device = false;
// Not possible to restore a multisig wallet that is able to activate the MMS
// (because the original keys are not (yet) part of the restore info)
m_original_keys_available = false;
if (!wallet_.empty())
{
bool r = store_keys(m_keys_file, password, false);
@@ -3092,6 +3166,7 @@ crypto::secret_key wallet2::generate(const std::string& wallet_, const epee::wip
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_original_keys_available = false;
m_key_on_device = false;
// calculate a starting refresh height
@@ -3190,6 +3265,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_original_keys_available = false;
m_key_on_device = false;
if (!wallet_.empty())
@@ -3240,6 +3316,7 @@ void wallet2::generate(const std::string& wallet_, const epee::wipeable_string&
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_original_keys_available = false;
m_key_on_device = false;
if (!wallet_.empty())
@@ -3286,6 +3363,7 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
m_original_keys_available = false;
if (!wallet_.empty()) {
bool r = store_keys(m_keys_file, password, false);
@@ -3360,6 +3438,15 @@ std::string wallet2::make_multisig(const epee::wipeable_string &password,
CHECK_AND_ASSERT_THROW_MES(false, "Unsupported threshold case");
}
if (!m_original_keys_available)
{
// Save the original i.e. non-multisig keys so the MMS can continue to use them to encrypt and decrypt messages
// (making a wallet multisig overwrites those keys, see account_base::make_multisig)
m_original_address = m_account.get_keys().m_account_address;
m_original_view_secret_key = m_account.get_keys().m_view_secret_key;
m_original_keys_available = true;
}
// the multisig view key is shared by all, make one all can derive
MINFO("Creating view key...");
crypto::secret_key view_skey = cryptonote::generate_multisig_view_secret_key(get_account().get_keys().m_view_secret_key, view_keys);
@@ -3687,8 +3774,8 @@ void wallet2::write_watch_only_wallet(const std::string& wallet_name, const epee
//----------------------------------------------------------------------------------------------------
void wallet2::wallet_exists(const std::string& file_path, bool& keys_file_exists, bool& wallet_file_exists)
{
std::string keys_file, wallet_file;
do_prepare_file_names(file_path, keys_file, wallet_file);
std::string keys_file, wallet_file, mms_file;
do_prepare_file_names(file_path, keys_file, wallet_file, mms_file);
boost::system::error_code ignore;
keys_file_exists = boost::filesystem::exists(keys_file, ignore);
@@ -3742,7 +3829,7 @@ bool wallet2::parse_payment_id(const std::string& payment_id_str, crypto::hash&
//----------------------------------------------------------------------------------------------------
bool wallet2::prepare_file_names(const std::string& file_path)
{
do_prepare_file_names(file_path, m_keys_file, m_wallet_file);
do_prepare_file_names(file_path, m_keys_file, m_wallet_file, m_mms_file);
return true;
}
//----------------------------------------------------------------------------------------------------
@@ -3913,6 +4000,8 @@ void wallet2::load(const std::string& wallet_, const epee::wipeable_string& pass
{
MERROR("Failed to save rings, will try again next time");
}
m_message_store.read_from_file(get_multisig_wallet_state(), m_mms_file);
}
//----------------------------------------------------------------------------------------------------
void wallet2::trim_hashchain()
@@ -4020,6 +4109,7 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
const std::string old_file = m_wallet_file;
const std::string old_keys_file = m_keys_file;
const std::string old_address_file = m_wallet_file + ".address.txt";
const std::string old_mms_file = m_mms_file;
// save keys to the new file
// if we here, main wallet file is saved and we only need to save keys and address files
@@ -4049,6 +4139,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
if (!r) {
LOG_ERROR("error removing file: " << old_address_file);
}
// remove old message store file
if (boost::filesystem::exists(old_mms_file))
{
r = boost::filesystem::remove(old_mms_file);
if (!r) {
LOG_ERROR("error removing file: " << old_mms_file);
}
}
} else {
// save to new file
#ifdef WIN32
@@ -4074,6 +4172,14 @@ void wallet2::store_to(const std::string &path, const epee::wipeable_string &pas
std::error_code e = tools::replace_file(new_file, m_wallet_file);
THROW_WALLET_EXCEPTION_IF(e, error::file_save_error, m_wallet_file, e);
}
if (m_message_store.get_active())
{
// While the "m_message_store" object of course always exist, a file for the message
// store should only exist if the MMS is really active
m_message_store.write_to_file(get_multisig_wallet_state(), m_mms_file);
}
}
//----------------------------------------------------------------------------------------------------
uint64_t wallet2::balance(uint32_t index_major) const
@@ -5914,6 +6020,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
bool is_shortly_after_segregation_fork = height >= segregation_fork_height && height < segregation_fork_height + SEGREGATION_FORK_VICINITY;
bool is_after_segregation_fork = height >= segregation_fork_height;
// get histogram for the amounts we need
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t);
cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t);
@@ -5985,6 +6092,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>>
COMMAND_RPC_GET_OUTPUTS_BIN::request req = AUTO_VAL_INIT(req);
COMMAND_RPC_GET_OUTPUTS_BIN::response daemon_resp = AUTO_VAL_INIT(daemon_resp);
size_t num_selected_transfers = 0;
for(size_t idx: selected_transfers)
{
@@ -8124,7 +8232,7 @@ bool wallet2::use_fork_rules(uint8_t version, int64_t early_blocks) const
result = m_node_rpc_proxy.get_earliest_height(version, earliest_height);
throw_on_rpc_response_error(result, "get_hard_fork_info");
bool close_enough = height >= earliest_height - early_blocks && earliest_height != std::numeric_limits<uint64_t>::max(); // start using the rules that many blocks beforehand
bool close_enough = height >= earliest_height - early_blocks; // start using the rules that many blocks beforehand
if (close_enough)
LOG_PRINT_L2("Using v" << (unsigned)version << " rules");
else
@@ -8907,6 +9015,7 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
if (account_minreserve)
{
THROW_WALLET_EXCEPTION_IF(account_minreserve->second == 0, error::wallet_internal_error, "Proved amount must be greater than 0");
// minimize the number of outputs included in the proof, by only picking the N largest outputs that can cover the requested min reserve amount
std::sort(selected_transfers.begin(), selected_transfers.end(), [&](const size_t a, const size_t b)
{ return m_transfers[a].amount() > m_transfers[b].amount(); });
@@ -9572,6 +9681,17 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
// was created by sweep_all, so we can't know the spent height and other detailed info.
std::unordered_map<crypto::key_image, crypto::hash> spent_key_images;
for (const transfer_details &td: m_transfers)
{
for (const cryptonote::txin_v& in : td.m_tx.vin)
{
if (in.type() == typeid(cryptonote::txin_to_key))
spent_key_images.insert(std::make_pair(boost::get<cryptonote::txin_to_key>(in).k_image, td.m_txid));
}
}
for(size_t i = 0; i < signed_key_images.size(); ++i)
{
transfer_details &td = m_transfers[i];
@@ -9585,28 +9705,11 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN)
{
bool is_spent_tx_found = false;
for (auto it = m_transfers.rbegin(); &(*it) != &td; ++it)
{
bool is_spent_tx = false;
for(const cryptonote::txin_v& in : it->m_tx.vin)
{
if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get<cryptonote::txin_to_key>(in).k_image)
{
is_spent_tx = true;
break;
}
}
if (is_spent_tx)
{
is_spent_tx_found = true;
spent_txids.insert(it->m_txid);
break;
}
}
if (!is_spent_tx_found)
const std::unordered_map<crypto::key_image, crypto::hash>::const_iterator skii = spent_key_images.find(td.m_key_image);
if (skii == spent_key_images.end())
swept_transfers.push_back(i);
else
spent_txids.insert(skii->second);
}
}
MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
@@ -9729,11 +9832,10 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
{
const transfer_details& td = m_transfers[n];
confirmed_transfer_details pd;
pd.m_change = (uint64_t)-1; // cahnge is unknown
pd.m_change = (uint64_t)-1; // change is unknown
pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
std::string err;
pd.m_block_height = get_daemon_blockchain_height(err); // spent block height is unknown, so hypothetically set to the highest
crypto::hash spent_txid = crypto::rand<crypto::hash>(); // spent txid is unknown, so hypothetically set to random
pd.m_block_height = 0; // spent block height is unknown
const crypto::hash &spent_txid = crypto::null_hash; // spent txid is unknown
m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
}
}
@@ -9828,18 +9930,20 @@ size_t wallet2::import_outputs(const std::vector<tools::wallet2::transfer_detail
crypto::public_key tx_pub_key = get_tx_pub_key_from_received_outs(td);
const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx);
THROW_WALLET_EXCEPTION_IF(td.m_tx.vout[td.m_internal_output_index].target.type() != typeid(cryptonote::txout_to_key),
error::wallet_internal_error, "Unsupported output type");
const crypto::public_key& out_key = boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key;
bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device());
THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image");
expand_subaddresses(td.m_subaddr_index);
td.m_key_image_known = true;
td.m_key_image_partial = false;
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get<cryptonote::txout_to_key>(td.m_tx.vout[td.m_internal_output_index].target).key,
THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key,
error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast<std::string>(i));
m_key_images[td.m_key_image] = m_transfers.size();
m_pub_keys[td.get_public_key()] = m_transfers.size();
m_transfers.push_back(td);
m_transfers.push_back(std::move(td));
}
return m_transfers.size();
@@ -10554,4 +10658,28 @@ void wallet2::generate_genesis(cryptonote::block& b) const {
cryptonote::generate_genesis_block(b, config::GENESIS_TX, config::GENESIS_NONCE);
}
}
//----------------------------------------------------------------------------------------------------
mms::multisig_wallet_state wallet2::get_multisig_wallet_state()
{
mms::multisig_wallet_state state;
state.nettype = m_nettype;
state.multisig = multisig(&state.multisig_is_ready);
state.has_multisig_partial_key_images = has_multisig_partial_key_images();
state.num_transfer_details = m_transfers.size();
if (state.multisig)
{
THROW_WALLET_EXCEPTION_IF(!m_original_keys_available, error::wallet_internal_error, "MMS use not possible because own original Monero address not available");
state.address = m_original_address;
state.view_secret_key = m_original_view_secret_key;
}
else
{
state.address = m_account.get_keys().m_account_address;
state.view_secret_key = m_account.get_keys().m_view_secret_key;
}
state.mms_file=m_mms_file;
return state;
}
}

View File

@@ -58,6 +58,7 @@
#include "wallet_errors.h"
#include "common/password.h"
#include "node_rpc_proxy.h"
#include "message_store.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.wallet2"
@@ -454,6 +455,7 @@ namespace tools
typedef std::tuple<uint64_t, crypto::public_key, rct::key> get_outs_entry;
/*!
* \brief Generates a wallet or restores one.
* \param wallet_ Name of wallet file
@@ -593,7 +595,7 @@ namespace tools
bool init(std::string daemon_address = "http://localhost:8080",
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_size_limit = 0, bool ssl = false);
void stop() { m_run.store(false, std::memory_order_relaxed); }
void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
i_wallet2_callback* callback() const { return m_callback; }
void callback(i_wallet2_callback* callback) { m_callback = callback; }
@@ -624,6 +626,7 @@ namespace tools
// Subaddress scheme
cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index& index) const;
cryptonote::account_public_address get_address() const { return get_subaddress({0,0}); }
boost::optional<cryptonote::subaddress_index> get_subaddress_index(const cryptonote::account_public_address& address) const;
crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const;
std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
@@ -851,8 +854,8 @@ namespace tools
void set_default_priority(uint32_t p) { m_default_priority = p; }
bool auto_refresh() const { return m_auto_refresh; }
void auto_refresh(bool r) { m_auto_refresh = r; }
bool confirm_missing_payment_id() const { return m_confirm_missing_payment_id; }
void confirm_missing_payment_id(bool always) { m_confirm_missing_payment_id = always; }
bool confirm_subaddress() const { return m_confirm_subaddress; }
void confirm_subaddress(bool always) { m_confirm_subaddress = always; }
bool ask_password() const { return m_ask_password; }
void ask_password(bool always) { m_ask_password = always; }
void set_min_output_count(uint32_t count) { m_min_output_count = count; }
@@ -1076,6 +1079,10 @@ namespace tools
bool unblackball_output(const crypto::public_key &output);
bool is_output_blackballed(const crypto::public_key &output) const;
// MMS -------------------------------------------------------------------------------------------------
mms::message_store& get_message_store() { return m_message_store; };
mms::multisig_wallet_state get_multisig_wallet_state();
private:
/*!
* \brief Stores wallet information to wallet file.
@@ -1147,6 +1154,7 @@ namespace tools
std::string m_daemon_address;
std::string m_wallet_file;
std::string m_keys_file;
std::string m_mms_file;
epee::net_utils::http::http_simple_client m_http_client;
hashchain m_blockchain;
std::atomic<uint64_t> m_local_bc_height; //temporary workaround
@@ -1196,7 +1204,7 @@ namespace tools
// If m_refresh_from_block_height is explicitly set to zero we need this to differentiate it from the case that
// m_refresh_from_block_height was defaulted to zero.*/
bool m_explicit_refresh_from_block_height;
bool m_confirm_missing_payment_id;
bool m_confirm_subaddress;
bool m_confirm_non_default_ring_size;
bool m_ask_password;
uint32_t m_min_output_count;
@@ -1231,6 +1239,11 @@ namespace tools
std::string m_ring_database;
bool m_ring_history_saved;
std::unique_ptr<ringdb> m_ringdb;
mms::message_store m_message_store;
bool m_original_keys_available;
cryptonote::account_public_address m_original_address;
crypto::secret_key m_original_view_secret_key;
};
}
BOOST_CLASS_VERSION(tools::wallet2, 24)

View File

@@ -784,6 +784,31 @@ namespace tools
std::string m_wallet_file;
};
//----------------------------------------------------------------------------------------------------
struct mms_error : public wallet_logic_error
{
protected:
explicit mms_error(std::string&& loc, const std::string& message)
: wallet_logic_error(std::move(loc), message)
{
}
};
//----------------------------------------------------------------------------------------------------
struct no_connection_to_bitmessage : public mms_error
{
explicit no_connection_to_bitmessage(std::string&& loc, const std::string& address)
: mms_error(std::move(loc), "no connection to PyBitmessage at address " + address)
{
}
};
//----------------------------------------------------------------------------------------------------
struct bitmessage_api_error : public mms_error
{
explicit bitmessage_api_error(std::string&& loc, const std::string& error_string)
: mms_error(std::move(loc), "PyBitmessage returned " + error_string)
{
}
};
//----------------------------------------------------------------------------------------------------
#if !defined(_MSC_VER)

View File

@@ -336,14 +336,20 @@ namespace tools
std::map<uint32_t, uint64_t> unlocked_balance_per_subaddress = m_wallet->unlocked_balance_per_subaddress(req.account_index);
std::vector<tools::wallet2::transfer_details> transfers;
m_wallet->get_transfers(transfers);
for (const auto& i : balance_per_subaddress)
std::set<uint32_t> address_indices = req.address_indices;
if (address_indices.empty())
{
for (const auto& i : balance_per_subaddress)
address_indices.insert(i.first);
}
for (uint32_t i : address_indices)
{
wallet_rpc::COMMAND_RPC_GET_BALANCE::per_subaddress_info info;
info.address_index = i.first;
info.address_index = i;
cryptonote::subaddress_index index = {req.account_index, info.address_index};
info.address = m_wallet->get_subaddress_as_str(index);
info.balance = i.second;
info.unlocked_balance = unlocked_balance_per_subaddress[i.first];
info.balance = balance_per_subaddress[i];
info.unlocked_balance = unlocked_balance_per_subaddress[i];
info.label = m_wallet->get_subaddress_label(index);
info.num_unspent_outputs = std::count_if(transfers.begin(), transfers.end(), [&](const tools::wallet2::transfer_details& td) { return !td.m_spent && td.m_subaddr_index == index; });
res.per_subaddress.push_back(info);
@@ -397,6 +403,27 @@ namespace tools
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
cryptonote::address_parse_info info;
if(!get_account_address_from_str(info, m_wallet->nettype(), req.address))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Invalid address";
return false;
}
auto index = m_wallet->get_subaddress_index(info.address);
if (!index)
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_ADDRESS;
er.message = "Address doesn't belong to the wallet";
return false;
}
res.index = *index;
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
bool wallet_rpc_server::on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er)
{
if (!m_wallet) return not_open(er);
@@ -1938,8 +1965,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) {
if (i->second.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->second.m_tx_hash, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->second.m_tx_hash, i->first, i->second);
}
}
@@ -1948,8 +1975,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::confirmed_transfer_details>>::const_iterator i = payments_out.begin(); i != payments_out.end(); ++i) {
if (i->first == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
@@ -1958,8 +1985,8 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::unconfirmed_transfer_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) {
if (i->first == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
@@ -1970,11 +1997,17 @@ namespace tools
for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) {
if (i->second.m_pd.m_tx_hash == txid)
{
fill_transfer_entry(res.transfer, i->first, i->second);
return true;
res.transfers.resize(res.transfers.size() + 1);
fill_transfer_entry(res.transfers.back(), i->first, i->second);
}
}
if (!res.transfers.empty())
{
res.transfer = res.transfers.front(); // backward compat
return true;
}
er.code = WALLET_RPC_ERROR_CODE_WRONG_TXID;
er.message = "Transaction not found.";
return false;
@@ -3007,6 +3040,7 @@ just_dir:
wrpc.send_stop_signal();
});
LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
LOG_PRINT_L0(tools::wallet_rpc_server::tr("WARNING: Payment IDs/integrated addresses will be deprecated soon. Use sub-addresses."));
try
{
wrpc.run();

View File

@@ -69,6 +69,7 @@ namespace tools
BEGIN_JSON_RPC_MAP("/json_rpc")
MAP_JON_RPC_WE("get_balance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("get_address", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("get_address_index", on_getaddress_index, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX)
MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE)
MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS)
MAP_JON_RPC_WE("create_address", on_create_address, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS)
@@ -141,6 +142,7 @@ namespace tools
//json_rpc
bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er);
bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_getaddress_index(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_INDEX::response& res, epee::json_rpc::error& er);
bool on_create_address(const wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_CREATE_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_label_address(const wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_LABEL_ADDRESS::response& res, epee::json_rpc::error& er);
bool on_get_accounts(const wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::request& req, wallet_rpc::COMMAND_RPC_GET_ACCOUNTS::response& res, epee::json_rpc::error& er);

View File

@@ -39,6 +39,17 @@
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
// When making *any* change here, bump minor
// If the change is incompatible, then bump major and set minor to 0
// This ensures WALLET_RPC_VERSION always increases, that every change
// has its own version, and that clients can just test major to see
// whether they can talk to a given wallet without having to know in
// 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 4
#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
{
namespace wallet_rpc
@@ -51,8 +62,10 @@ namespace wallet_rpc
struct request
{
uint32_t account_index;
std::set<uint32_t> address_indices;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(account_index)
KV_SERIALIZE(address_indices)
END_KV_SERIALIZE_MAP()
};
@@ -130,6 +143,25 @@ namespace wallet_rpc
};
};
struct COMMAND_RPC_GET_ADDRESS_INDEX
{
struct request
{
std::string address;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(address)
END_KV_SERIALIZE_MAP()
};
struct response
{
cryptonote::subaddress_index index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(index)
END_KV_SERIALIZE_MAP()
};
};
struct COMMAND_RPC_CREATE_ADDRESS
{
struct request
@@ -1311,9 +1343,11 @@ namespace wallet_rpc
struct response
{
transfer_entry transfer;
std::list<transfer_entry> transfers;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(transfer);
KV_SERIALIZE(transfers);
END_KV_SERIALIZE_MAP()
};
};