aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blockchain_utilities/CMakeLists.txt4
-rw-r--r--src/checkpoints/CMakeLists.txt10
-rw-r--r--src/crypto/crypto_ops_builder/ietf.txt1404
-rw-r--r--src/cryptonote_basic/CMakeLists.txt10
-rw-r--r--src/cryptonote_basic/account.cpp8
-rw-r--r--src/cryptonote_basic/account.h3
-rw-r--r--src/cryptonote_core/blockchain.cpp6
-rw-r--r--src/daemon/CMakeLists.txt4
-rw-r--r--src/daemon/main.cpp7
-rw-r--r--src/daemon/rpc_command_executor.cpp2
-rw-r--r--src/device/device.cpp70
-rw-r--r--src/device/device.hpp13
-rw-r--r--src/gen_multisig/gen_multisig.cpp4
-rw-r--r--src/mnemonics/chinese_simplified.h9
-rw-r--r--src/mnemonics/dutch.h9
-rw-r--r--src/mnemonics/english.h9
-rw-r--r--src/mnemonics/english_old.h9
-rw-r--r--src/mnemonics/esperanto.h9
-rw-r--r--src/mnemonics/french.h9
-rw-r--r--src/mnemonics/german.h9
-rw-r--r--src/mnemonics/italian.h9
-rw-r--r--src/mnemonics/japanese.h9
-rw-r--r--src/mnemonics/language_base.h13
-rw-r--r--src/mnemonics/lojban.h9
-rw-r--r--src/mnemonics/portuguese.h9
-rw-r--r--src/mnemonics/russian.h9
-rw-r--r--src/mnemonics/spanish.h9
-rw-r--r--src/ringct/rctTypes.h4
-rw-r--r--src/rpc/core_rpc_server.cpp10
-rw-r--r--src/rpc/rpc_args.cpp14
-rw-r--r--src/simplewallet/simplewallet.cpp80
-rw-r--r--src/simplewallet/simplewallet.h3
-rw-r--r--src/wallet/CMakeLists.txt2
-rw-r--r--src/wallet/api/wallet.cpp18
-rw-r--r--src/wallet/api/wallet.h1
-rw-r--r--src/wallet/api/wallet2_api.h5
-rw-r--r--src/wallet/wallet2.cpp69
-rw-r--r--src/wallet/wallet2.h9
-rw-r--r--src/wallet/wallet_rpc_server.cpp314
39 files changed, 550 insertions, 1654 deletions
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt
index fe57895e1..37bca671f 100644
--- a/src/blockchain_utilities/CMakeLists.txt
+++ b/src/blockchain_utilities/CMakeLists.txt
@@ -28,7 +28,9 @@
set(blocksdat "")
if(PER_BLOCK_CHECKPOINT)
- if(APPLE)
+ if(APPLE AND DEPENDS)
+ add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
+ elseif(APPLE AND NOT DEPENDS)
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
else()
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt
index 02bb2891a..715006522 100644
--- a/src/checkpoints/CMakeLists.txt
+++ b/src/checkpoints/CMakeLists.txt
@@ -27,9 +27,13 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if(APPLE)
- find_library(IOKIT_LIBRARY IOKit)
- mark_as_advanced(IOKIT_LIBRARY)
- list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY})
+ if(DEPENDS)
+ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
+ else()
+ find_library(IOKIT_LIBRARY IOKit)
+ mark_as_advanced(IOKIT_LIBRARY)
+ list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY})
+ endif()
endif()
set(checkpoints_sources
diff --git a/src/crypto/crypto_ops_builder/ietf.txt b/src/crypto/crypto_ops_builder/ietf.txt
index 0736f71ec..609f5e75a 100644
--- a/src/crypto/crypto_ops_builder/ietf.txt
+++ b/src/crypto/crypto_ops_builder/ietf.txt
@@ -1,1402 +1,4 @@
-
+https://tools.ietf.org/id/draft-josefsson-eddsa-ed25519-02.txt
-[Docs] [txt|pdf] [Tracker] [Email] [Diff1] [Diff2] [Nits]
-
-Versions: 00 01 02
-
-Network Working Group S. Josefsson
-Internet-Draft SJD AB
-Intended status: Informational N. Moeller
-Expires: August 26, 2015
- February 22, 2015
-
-
- EdDSA and Ed25519
-
- draft-josefsson-eddsa-ed25519-02
-
-
-Abstract
-
- The elliptic curve signature scheme EdDSA and one instance of it
- called Ed25519 is described. An example implementation and test
- vectors are provided.
-
-Status of This Memo
-
- This Internet-Draft is submitted in full conformance with the
- provisions of BCP 78 and BCP 79.
-
- Internet-Drafts are working documents of the Internet Engineering
- Task Force (IETF). Note that other groups may also distribute
- working documents as Internet-Drafts. The list of current Internet-
- Drafts is at http://datatracker.ietf.org/drafts/current/.
-
- Internet-Drafts are draft documents valid for a maximum of six months
- and may be updated, replaced, or obsoleted by other documents at any
- time. It is inappropriate to use Internet-Drafts as reference
- material or to cite them other than as "work in progress."
-
- This Internet-Draft will expire on August 26, 2015.
-
-Copyright Notice
-
- Copyright (c) 2015 IETF Trust and the persons identified as the
- document authors. All rights reserved.
-
- This document is subject to BCP 78 and the IETF Trust's Legal
- Provisions Relating to IETF Documents
- (http://trustee.ietf.org/license-info) in effect on the date of
- publication of this document. Please review these documents
- carefully, as they describe your rights and restrictions with respect
- to this document. Code Components extracted from this document must
- include Simplified BSD License text as described in Section 4.e of
- the Trust Legal Provisions and are provided without warranty as
- described in the Simplified BSD License.
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 1]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
-Table of Contents
-
- 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2
- 2. Notation . . . . . . . . . . . . . . . . . . . . . . . . . . 3
- 3. Background . . . . . . . . . . . . . . . . . . . . . . . . . 3
- 4. EdDSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
- 4.1. Encoding . . . . . . . . . . . . . . . . . . . . . . . . 4
- 4.2. Keys . . . . . . . . . . . . . . . . . . . . . . . . . . 5
- 4.3. Sign . . . . . . . . . . . . . . . . . . . . . . . . . . 5
- 4.4. Verify . . . . . . . . . . . . . . . . . . . . . . . . . 5
- 5. Ed25519 . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
- 5.1. Modular arithmetic . . . . . . . . . . . . . . . . . . . 6
- 5.2. Encoding . . . . . . . . . . . . . . . . . . . . . . . . 6
- 5.3. Decoding . . . . . . . . . . . . . . . . . . . . . . . . 6
- 5.4. Point addition . . . . . . . . . . . . . . . . . . . . . 7
- 5.5. Key Generation . . . . . . . . . . . . . . . . . . . . . 8
- 5.6. Sign . . . . . . . . . . . . . . . . . . . . . . . . . . 8
- 5.7. Verify . . . . . . . . . . . . . . . . . . . . . . . . . 9
- 5.8. Python illustration . . . . . . . . . . . . . . . . . . . 9
- 6. Test Vectors for Ed25519 . . . . . . . . . . . . . . . . . . 14
- 7. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 17
- 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 18
- 9. Security Considerations . . . . . . . . . . . . . . . . . . . 18
- 9.1. Side-channel leaks . . . . . . . . . . . . . . . . . . . 18
- 10. References . . . . . . . . . . . . . . . . . . . . . . . . . 18
- 10.1. Normative References . . . . . . . . . . . . . . . . . . 18
- 10.2. Informative References . . . . . . . . . . . . . . . . . 18
- Appendix A. Ed25519 Python Library . . . . . . . . . . . . . . . 19
- Appendix B. Library driver . . . . . . . . . . . . . . . . . . . 23
- Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 24
-
-1. Introduction
-
-
- The Edwards-curve Digital Signature Algorithm (EdDSA) is a variant of
- Schnorr's signature system with Twisted Edwards curves. EdDSA needs
- to be instantiated with certain parameters and this document describe
- Ed25519 - an instantiation of EdDSA in a curve over GF(2^255-19). To
- facilitate adoption in the Internet community of Ed25519, this
- document describe the signature scheme in an implementation-oriented
- way, and we provide sample code and test vectors.
-
- The advantages with EdDSA and Ed25519 include:
-
- 1. High-performance on a variety of platforms.
-
- 2. Does not require the use of a unique random number for each
- signature.
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 2]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- 3. More resilient to side-channel attacks.
-
- 4. Small public keys (32 bytes) and signatures (64 bytes).
-
- 5. The formulas are "strongly unified", i.e., they are valid for all
- points on the curve, with no exceptions. This obviates the need
- for EdDSA to perform expensive point validation on untrusted
- public values.
-
- 6. Collision resilience, meaning that hash-function collisions do
- not break this system.
-
- For further background, see the original EdDSA paper [EDDSA].
-
-2. Notation
-
-
- The following notation is used throughout the document:
-
- GF(p) finite field with p elements
-
- x^y x multiplied by itself y times
-
- B generator of the group or subgroup of interest
-
- n B B added to itself n times.
-
- h_i the i'th bit of h
-
- a || b (bit-)string a concatenated with (bit-)string b
-
-3. Background
-
-
- EdDSA is defined using an elliptic curve over GF(p) of the form
-
- -x^2 + y^2 = 1 + d x^2 y^2
-
- In general, p could be a prime power, but it is usually chosen as a
- prime number. It is required that p = 1 modulo 4 (which implies that
- -1 is a square modulo p) and that d is a non-square modulo p. For
- Ed25519, the curve used is equivalent to Curve25519 [CURVE25519],
- under a change of coordinates, which means that the difficulty of the
- discrete logarithm problem is the same as for Curve25519.
-
- Points on this curve form a group under addition, (x3, y3) = (x1, y1)
- + (x2, y2), with the formulas
-
-
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 3]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- x1 y2 + x2 y1 y1 y2 + x1 x2
- x3 = -------------------, y3 = -------------------
- 1 + d x1 x2 y1 y2 1 - d x1 x2 y1 y2
-
- The neutral element in the group is (0, 1).
-
- Unlike manyy other curves used for cryptographic applications, these
- formulas are "strongly unified": they are valid for all points on the
- curve, with no exceptions. In particular, the denominators are non-
- zero for all input points.
-
- There are more efficient formulas, which are still strongly unified,
- which use homogeneous coordinates to avoid the expensive modulo p
- inversions. See [Faster-ECC] and [Edwards-revisited].
-
-4. EdDSA
-
-
- EdDSA is a digital signature system with several parameters. The
- generic EdDSA digital signature system is normally not implemented
- directly, but instead a particular instance of EdDSA (like Ed25519)
- is implemented. A precise explanation of the generic EdDSA is thus
- not particulary useful for implementers, but for background and
- completeness, a succint description of the generic EdDSA algorithm is
- given here.
-
- EdDSA has seven parameters:
-
- 1. an integer b >= 10.
-
- 2. a cryptographic hash function H producing 2b-bit outputs.
-
- 3. a prime power p congruent to 1 modulo 4.
-
- 4. a (b-1)-bit encoding of elements of the finite field GF(p).
-
- 5. a non-square element d of GF(p)
-
- 6. an element B != (0,1) of the set E = { (x,y) is a member of GF(p)
- x GF(p) such that -x^2 + y^2 = 1 + dx^2y^2 }.
-
- 7. a prime q, of size b-3 bits, such that qB = (0, 1), i.e., q is
- the order of B or a multiple thereof.
-
-4.1. Encoding
-
-
- An element (x,y) of E is encoded as a b-bit string called ENC(x,y)
- which is the (b-1)-bit encoding of y concatenated with one bit that
- is 1 if x is negative and 0 if x is not negative. Negative elements
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 4]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- of GF(q) are those x which the (b-1)-bit encoding of x is
- lexicographically larger than the (b-1)-bit encoding of -x.
-
-4.2. Keys
-
-
- An EdDSA secret key is a b-bit string k. Let the hash H(k) = (h_0,
- h_1, ..., h_(2b-1)) determine an integer a which is 2^(b-2) plus the
- sum of m = 2^i * h_i for all i equal or larger than 3 and equal to or
- less than b-3 such that m is a member of the set { 2^(b-2), 2^(b-2) +
- 8, ..., 2^(b-1) - 8 }. The EdDSA public key is ENC(A) = ENC(aB).
- The bits h_b, ..., h_(2b-1) is used below during signing.
-
-4.3. Sign
-
-
- The signature of a message M under a secret key k is the 2b-bit
- string ENC(R) || ENC'(S), where ENC'(S) is defined as the b-bit
- little-endian encoding of S. R and S are derived as follows. First
- define r = H(h_b, ... h_(2b-1)), M) interpreting 2b-bit strings in
- little-endian form as integers in {0, 1, ..., 2^(2b)-1}. Let R=rB
- and S=(r+H(ENC(R) || ENC(A) || M)a) mod l.
-
-4.4. Verify
-
-
- To verify a signature ENC(R) || ENC'(S) on a message M under a public
- key ENC(A), proceed as follows. Parse the inputs so that A and R is
- an element of E, and S is a member of the set {0, 1, ..., l-1 }.
- Compute H' = H(ENC(R) || ENC(A) || M) and check the group equation
- 8SB = 8R + 8H'A in E. Verification is rejected if parsing fails or
- the group equation does not hold.
-
-5. Ed25519
-
-
- Theoretically, Ed25519 is EdDSA instantiated with b=256, H being
- SHA-512 [RFC4634], p is the prime 2^255-19, the 255-bit encoding of
- GF(2^255-19) being the little-endian encoding of {0, 1, ...,
- 2^255-20}, q is the prime 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed,
- d = -121665/121666 which is a member of GF(p), and B is the unique
- point (x, 4/5) in E for which x is "positive", which with the
- encoding used simply means that the least significant bit of x is 0.
- The curve p, prime q, d and B follows from [I-D.irtf-cfrg-curves].
-
- Written out explicitly, B is the point (15112221349535400772501151409
- 588531511454012693041857206046113283949847762202, 4631683569492647816
- 9428394003475163141307993866256225615783033603165251855960).
-
-
-
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 5]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
-5.1. Modular arithmetic
-
-
- For advise on how to implement arithmetic modulo p = 2^255 - 1
- efficiently and securely, see Curve25519 [CURVE25519]. For inversion
- modulo p, it is recommended to use the identity x^-1 = x^(p-2) (mod
- p).
-
- For point decoding or "decompression", square roots modulo p are
- needed. They can be computed using the Tonelli-Shanks algorithm, or
- the special case for p = 5 (mod 8). To find a square root of a,
- first compute the candidate root x = a^((p+3)/8) (mod p). Then there
- are three cases:
-
- x^2 = a (mod p). Then x is a square root.
-
- x^2 = -a (mod p). Then 2^((p-1)/4) x is a square root.
-
- a is not a square modulo p.
-
-5.2. Encoding
-
-
- All values are coded as octet strings, and integers are coded using
- little endian convention. I.e., a 32-octet string h h[0],...h[31]
- represents the integer h[0] + 2^8 h[1] + ... + 2^248 h[31].
-
- A curve point (x,y), with coordiantes in the range 0 <= x,y < p, is
- coded as follows. First encode the y-coordinate as a little-endian
- string of 32 octets. The most significant bit of the final octet is
- always zero. To form the encoding of the point, copy the least
- significant bit of the x-coordinate to the most significant bit of
- the final octet.
-
-5.3. Decoding
-
-
- Decoding a point, given as a 32-octet string, is a little more
- complicated.
-
- 1. First interpret the string as an integer in little-endian
- representation. Bit 255 of this number is the least significant
- bit of the x-coordinate, and denote this value x_0. The
- y-coordinate is recovered simply by clearing this bit. If the
- resulting value is >= p, decoding fails.
-
- 2. To recover the x coordinate, the curve equation implies x^2 =
- (y^2 - 1) / (d y^2 + 1) (mod p). Since d is a non-square and -1
- is a square, the numerator, (d y^2 + 1), is always invertible
- modulo p. Let u = y^2 - 1 and v = d y^2 + 1. To compute the
- square root of (u/v), the first step is to compute the candidate
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 6]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- root x = (u/v)^((p+3)/8). This can be done using the following
- trick, to use a single modular powering for both the inversion of
- v and the square root:
-
- (p+3)/8 3 (p-5)/8
- x = (u/v) = u v (u v^7) (mod p)
-
- 3. Again, there are three cases:
-
- 1. If v x^2 = u (mod p), x is a square root.
-
- 2. If v x^2 = -u (mod p), set x <-- x 2^((p-1)/4), which is a
- square root.
-
- 3. Otherwise, no square root exists modulo p, and decoding
- fails.
-
- 4. Finally, use the x_0 bit to select the right square root. If x =
- 0, and x_0 = 1, decoding fails. Otherwise, if x_0 != x mod 2,
- set x <-- p - x. Return the decoded point (x,y).
-
-5.4. Point addition
-
-
- For point addition, the following method is recommended. A point
- (x,y) is represented in extended homogeneous coordinates (X, Y, Z,
- T), with x = X/Z, y = Y/Z, x y = T/Z.
-
- The following formulas for adding two points, (x3,y3) =
- (x1,y1)+(x2,y2) are described in [Edwards-revisited], section 3.1.
- They are strongly unified, i.e., they work for any pair of valid
- input points.
-
- A = (Y1-X1)*(Y2-X2)
- B = (Y1+X1)*(Y2+X2)
- C = T1*2*d*T2
- D = Z1*2*Z2
- E = B-A
- F = D-C
- G = D+C
- H = B+A
- X3 = E*F
- Y3 = G*H
- T3 = E*H
- Z3 = F*G
-
-
-
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 7]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
-5.5. Key Generation
-
-
- The secret is 32 octets (256 bits, corresponding to b) of
- cryptographically-secure random data. See [RFC4086] for a discussion
- about randomness.
-
- The 32-byte public key is generated by the following steps.
-
- 1. Hash the 32-byte secret using SHA-512, storing the digest in a
- 64-octet large buffer, denoted h. Only the lower 32 bytes are
- used for generating the public key.
-
- 2. Prune the buffer. In C terminology:
-
- h[0] &= ~0x07;
- h[31] &= 0x7F;
- h[31] |= 0x40;
-
- 3. Interpret the buffer as the little-endian integer, forming a
- secret scalar a. Perform a known-base-point scalar
- multiplication a B.
-
- 4. The public key A is the encoding of the point aB. First encode
- the y coordinate (in the range 0 <= y < p) as a little-endian
- string of 32 octets. The most significant bit of the final octet
- is always zero. To form the encoding of the point aB, copy the
- least significant bit of the x coordinate to the most significant
- bit of the final octet. The result is the public key.
-
-5.6. Sign
-
-
- The imputs to the signing procedure is the secret key, a 32-octet
- string, and a message M of arbitrary size.
-
- 1. Hash the secret key, 32-octets, using SHA-512. Let h denote the
- resulting digest. Construct the secret scalar a from the first
- half of the digest, and the corresponding public key A, as
- described in the previous section. Let prefix denote the second
- half of the hash digest, h[32],...,h[63].
-
- 2. Compute SHA-512(prefix || M), where M is the message to be
- signed. Interpret the 64-octet digest as a little-endian integer
- r.
-
- 3. Compute the point rB. For efficiency, do this by first reducing
- r modulo q, the group order of B. Let the string R be the
- encoding of this point.
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 8]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- 4. Compute SHA512(R || A || M), and interpret the 64-octet digest as
- a little-endian integer k.
-
- 5. Compute s = (r + k a) mod q. For efficiency, again reduce k
- modulo q first.
-
- 6. Form the signature of the concatenation of R (32 octets) and the
- little-endian encoding of s (32 octets, three most significant
- bits of the final octets always zero).
-
-5.7. Verify
-
-
- 1. To verify a signature on a message M, first split the signature
- into two 32-octet halves. Decode the first half as a point R,
- and the second half as an integer s, in the range 0 <= s < q. If
- the decoding fails, the signature is invalid.
-
- 2. Compute SHA512(R || A || M), and interpret the 64-octet digest as
- a little-endian integer k.
-
- 3. Check the group equation 8s B = 8 R + 8k A. It's sufficient, but
- not required, to instead check s B = R + k A.
-
-5.8. Python illustration
-
-
- The rest of this section describes how Ed25519 can be implemented in
- Python (version 3.2 or later) for illustration. See appendix A for
- the complete implementation and appendix B for a test-driver to run
- it through some test vectors.
-
- First some preliminaries that will be needed.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 9]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- import hashlib
-
- def sha512(s):
- return hashlib.sha512(s).digest()
-
- # Base field Z_p
- p = 2**255 - 19
-
- def modp_inv(x):
- return pow(x, p-2, p)
-
- # Curve constant
- d = -121665 * modp_inv(121666) % p
-
- # Group order
- q = 2**252 + 27742317777372353535851937790883648493
-
- def sha512_modq(s):
- return int.from_bytes(sha512(s), "little") % q
-
- Then follows functions to perform point operations.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 10]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
-# Points are represented as tuples (X, Y, Z, T) of extended coordinates,
-# with x = X/Z, y = Y/Z, x*y = T/Z
-
-def point_add(P, Q):
- A = (P[1]-P[0])*(Q[1]-Q[0]) % p
- B = (P[1]+P[0])*(Q[1]+Q[0]) % p
- C = 2 * P[3] * Q[3] * d % p
- D = 2 * P[2] * Q[2] % p
- E = B-A
- F = D-C
- G = D+C
- H = B+A
- return (E*F, G*H, F*G, E*H)
-
-# Computes Q = s * Q
-def point_mul(s, P):
- Q = (0, 1, 1, 0) # Neutral element
- while s > 0:
- # Is there any bit-set predicate?
- if s & 1:
- Q = point_add(Q, P)
- P = point_add(P, P)
- s >>= 1
- return Q
-
-def point_equal(P, Q):
- # x1 / z1 == x2 / z2 <==> x1 * z2 == x2 * z1
- if (P[0] * Q[2] - Q[0] * P[2]) % p != 0:
- return False
- if (P[1] * Q[2] - Q[1] * P[2]) % p != 0:
- return False
- return True
-
- Now follows functions for point compression.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 11]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
-# Square root of -1
-modp_sqrt_m1 = pow(2, (p-1) // 4, p)
-
-# Compute corresponding x coordinate, with low bit corresponding to sign,
-# or return None on failure
-def recover_x(y, sign):
- x2 = (y*y-1) * modp_inv(d*y*y+1)
- if x2 == 0:
- if sign:
- return None
- else:
- return 0
-
- # Compute square root of x2
- x = pow(x2, (p+3) // 8, p)
- if (x*x - x2) % p != 0:
- x = x * modp_sqrt_m1 % p
- if (x*x - x2) % p != 0:
- return None
-
- if (x & 1) != sign:
- x = p - x
- return x
-
-# Base point
-g_y = 4 * modp_inv(5) % p
-g_x = recover_x(g_y, 0)
-G = (g_x, g_y, 1, g_x * g_y % p)
-
-def point_compress(P):
- zinv = modp_inv(P[2])
- x = P[0] * zinv % p
- y = P[1] * zinv % p
- return int.to_bytes(y | ((x & 1) << 255), 32, "little")
-
-def point_decompress(s):
- if len(s) != 32:
- raise Exception("Invalid input length for decompression")
- y = int.from_bytes(s, "little")
- sign = y >> 255
- y &= (1 << 255) - 1
-
- x = recover_x(y, sign)
- if x is None:
- return None
- else:
- return (x, y, 1, x*y % p)
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 12]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- These are functions for manipulating the secret.
-
- def secret_expand(secret):
- if len(secret) != 32:
- raise Exception("Bad size of private key")
- h = sha512(secret)
- a = int.from_bytes(h[:32], "little")
- a &= (1 << 254) - 8
- a |= (1 << 254)
- return (a, h[32:])
-
- def secret_to_public(secret):
- (a, dummy) = secret_expand(secret)
- return point_compress(point_mul(a, G))
-
- The signature function works as below.
-
- def sign(secret, msg):
- a, prefix = secret_expand(secret)
- A = point_compress(point_mul(a, G))
- r = sha512_modq(prefix + msg)
- R = point_mul(r, G)
- Rs = point_compress(R)
- h = sha512_modq(Rs + A + msg)
- s = (r + h * a) % q
- return Rs + int.to_bytes(s, 32, "little")
-
- And finally the verification function.
-
- def verify(public, msg, signature):
- if len(public) != 32:
- raise Exception("Bad public-key length")
- if len(signature) != 64:
- Exception("Bad signature length")
- A = point_decompress(public)
- if not A:
- return False
- Rs = signature[:32]
- R = point_decompress(Rs)
- if not R:
- return False
- s = int.from_bytes(signature[32:], "little")
- h = sha512_modq(Rs + public + msg)
- sB = point_mul(s, G)
- hA = point_mul(h, A)
- return point_equal(sB, point_add(R, hA))
-
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 13]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
-6. Test Vectors for Ed25519
-
-
- Below is a sequence of octets with test vectors for the the Ed25519
- signature algorithm. The octets are hex encoded and whitespace is
- inserted for readability. Private keys are 64 bytes, public keys 32
- bytes, message of arbitrary length, and signatures are 64 bytes. The
- test vectors are taken from [ED25519-TEST-VECTORS] (but we removed
- the public key as a suffix of the secret key, and removed the message
- from the signature) and [ED25519-LIBGCRYPT-TEST-VECTORS].
-
- -----TEST 1
- SECRET KEY:
- 9d61b19deffd5a60ba844af492ec2cc4
- 4449c5697b326919703bac031cae7f60
-
- PUBLIC KEY:
- d75a980182b10ab7d54bfed3c964073a
- 0ee172f3daa62325af021a68f707511a
-
- MESSAGE (length 0 bytes):
-
- SIGNATURE:
- e5564300c360ac729086e2cc806e828a
- 84877f1eb8e5d974d873e06522490155
- 5fb8821590a33bacc61e39701cf9b46b
- d25bf5f0595bbe24655141438e7a100b
-
- -----TEST 2
- SECRET KEY:
- 4ccd089b28ff96da9db6c346ec114e0f
- 5b8a319f35aba624da8cf6ed4fb8a6fb
-
- PUBLIC KEY:
- 3d4017c3e843895a92b70aa74d1b7ebc
- 9c982ccf2ec4968cc0cd55f12af4660c
-
- MESSAGE (length 1 byte):
- 72
-
- SIGNATURE:
- 92a009a9f0d4cab8720e820b5f642540
- a2b27b5416503f8fb3762223ebdb69da
- 085ac1e43e15996e458f3613d0f11d8c
- 387b2eaeb4302aeeb00d291612bb0c00
-
- -----TEST 3
- SECRET KEY:
- c5aa8df43f9f837bedb7442f31dcb7b1
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 14]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- 66d38535076f094b85ce3a2e0b4458f7
-
- PUBLIC KEY:
- fc51cd8e6218a1a38da47ed00230f058
- 0816ed13ba3303ac5deb911548908025
-
- MESSAGE (length 2 bytes):
- af82
-
- SIGNATURE:
- 6291d657deec24024827e69c3abe01a3
- 0ce548a284743a445e3680d7db5ac3ac
- 18ff9b538d16f290ae67f760984dc659
- 4a7c15e9716ed28dc027beceea1ec40a
-
- -----TEST 1024
- SECRET KEY:
- f5e5767cf153319517630f226876b86c
- 8160cc583bc013744c6bf255f5cc0ee5
-
- PUBLIC KEY:
- 278117fc144c72340f67d0f2316e8386
- ceffbf2b2428c9c51fef7c597f1d426e
-
- MESSAGE:
- 08b8b2b733424243760fe426a4b54908
- 632110a66c2f6591eabd3345e3e4eb98
- fa6e264bf09efe12ee50f8f54e9f77b1
- e355f6c50544e23fb1433ddf73be84d8
- 79de7c0046dc4996d9e773f4bc9efe57
- 38829adb26c81b37c93a1b270b20329d
- 658675fc6ea534e0810a4432826bf58c
- 941efb65d57a338bbd2e26640f89ffbc
- 1a858efcb8550ee3a5e1998bd177e93a
- 7363c344fe6b199ee5d02e82d522c4fe
- ba15452f80288a821a579116ec6dad2b
- 3b310da903401aa62100ab5d1a36553e
- 06203b33890cc9b832f79ef80560ccb9
- a39ce767967ed628c6ad573cb116dbef
- efd75499da96bd68a8a97b928a8bbc10
- 3b6621fcde2beca1231d206be6cd9ec7
- aff6f6c94fcd7204ed3455c68c83f4a4
- 1da4af2b74ef5c53f1d8ac70bdcb7ed1
- 85ce81bd84359d44254d95629e9855a9
- 4a7c1958d1f8ada5d0532ed8a5aa3fb2
- d17ba70eb6248e594e1a2297acbbb39d
- 502f1a8c6eb6f1ce22b3de1a1f40cc24
- 554119a831a9aad6079cad88425de6bd
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 15]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- e1a9187ebb6092cf67bf2b13fd65f270
- 88d78b7e883c8759d2c4f5c65adb7553
- 878ad575f9fad878e80a0c9ba63bcbcc
- 2732e69485bbc9c90bfbd62481d9089b
- eccf80cfe2df16a2cf65bd92dd597b07
- 07e0917af48bbb75fed413d238f5555a
- 7a569d80c3414a8d0859dc65a46128ba
- b27af87a71314f318c782b23ebfe808b
- 82b0ce26401d2e22f04d83d1255dc51a
- ddd3b75a2b1ae0784504df543af8969b
- e3ea7082ff7fc9888c144da2af58429e
- c96031dbcad3dad9af0dcbaaaf268cb8
- fcffead94f3c7ca495e056a9b47acdb7
- 51fb73e666c6c655ade8297297d07ad1
- ba5e43f1bca32301651339e22904cc8c
- 42f58c30c04aafdb038dda0847dd988d
- cda6f3bfd15c4b4c4525004aa06eeff8
- ca61783aacec57fb3d1f92b0fe2fd1a8
- 5f6724517b65e614ad6808d6f6ee34df
- f7310fdc82aebfd904b01e1dc54b2927
- 094b2db68d6f903b68401adebf5a7e08
- d78ff4ef5d63653a65040cf9bfd4aca7
- 984a74d37145986780fc0b16ac451649
- de6188a7dbdf191f64b5fc5e2ab47b57
- f7f7276cd419c17a3ca8e1b939ae49e4
- 88acba6b965610b5480109c8b17b80e1
- b7b750dfc7598d5d5011fd2dcc5600a3
- 2ef5b52a1ecc820e308aa342721aac09
- 43bf6686b64b2579376504ccc493d97e
- 6aed3fb0f9cd71a43dd497f01f17c0e2
- cb3797aa2a2f256656168e6c496afc5f
- b93246f6b1116398a346f1a641f3b041
- e989f7914f90cc2c7fff357876e506b5
- 0d334ba77c225bc307ba537152f3f161
- 0e4eafe595f6d9d90d11faa933a15ef1
- 369546868a7f3a45a96768d40fd9d034
- 12c091c6315cf4fde7cb68606937380d
- b2eaaa707b4c4185c32eddcdd306705e
- 4dc1ffc872eeee475a64dfac86aba41c
- 0618983f8741c5ef68d3a101e8a3b8ca
- c60c905c15fc910840b94c00a0b9d0
-
- SIGNATURE:
- 0aab4c900501b3e24d7cdf4663326a3a
- 87df5e4843b2cbdb67cbf6e460fec350
- aa5371b1508f9f4528ecea23c436d94b
- 5e8fcd4f681e30a6ac00a9704a188a03
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 16]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- -----TEST 1A
- -----An additional test with the data from test 1 but using an
- -----uncompressed public key.
- SECRET KEY:
- 9d61b19deffd5a60ba844af492ec2cc4
- 4449c5697b326919703bac031cae7f60
-
- PUBLIC KEY:
- 0455d0e09a2b9d34292297e08d60d0f6
- 20c513d47253187c24b12786bd777645
- ce1a5107f7681a02af2523a6daf372e1
- 0e3a0764c9d3fe4bd5b70ab18201985a
- d7
-
- MSG (length 0 bytes):
-
- SIGNATURE:
- e5564300c360ac729086e2cc806e828a
- 84877f1eb8e5d974d873e06522490155
- 5fb8821590a33bacc61e39701cf9b46b
- d25bf5f0595bbe24655141438e7a100b
-
- -----TEST 1B
- -----An additional test with the data from test 1 but using an
- -----compressed prefix.
- SECRET KEY:
- 9d61b19deffd5a60ba844af492ec2cc4
- 4449c5697b326919703bac031cae7f60
-
- PUBLIC KEY:
- 40d75a980182b10ab7d54bfed3c96407
- 3a0ee172f3daa62325af021a68f70751
- 1a
-
- MESSAGE (length 0 bytes):
-
- SIGNATURE:
- e5564300c360ac729086e2cc806e828a
- 84877f1eb8e5d974d873e06522490155
- 5fb8821590a33bacc61e39701cf9b46b
- d25bf5f0595bbe24655141438e7a100b
- -----
-
-7. Acknowledgements
-
-
- Feedback on this document was received from Werner Koch and Damien
- Miller.
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 17]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
-8. IANA Considerations
-
-
- None.
-
-9. Security Considerations
-
-
-9.1. Side-channel leaks
-
-
- For implementations performing signatures, secrecy of the key is
- fundamental. It is possible to protect against some side-channel
- attacks by ensuring that the implementation executes exactly the same
- sequence of instructions and performs exactly the same memory
- accesses, for any value of the secret key.
-
- To make an implementation side-channel silent in this way, the modulo
- p arithmetic must not use any data-dependent branches, e.g., related
- to carry propagation. Side channel-silent point addition is
- straight-forward, thanks to the unified formulas.
-
- Scalar multiplication, multiplying a point by an integer, needs some
- additional effort to implement in a side-channel silent manner. One
- simple approach is to implement a side-channel silent conditional
- assignment, and use together with the binary algorithm to examine one
- bit of the integer at a time.
-
- Note that the example implementation in this document does not
- attempt to be side-channel silent.
-
-10. References
-
-
-10.1. Normative References
-
-
- [RFC4634] Eastlake, D. and T. Hansen, "US Secure Hash Algorithms
- (SHA and HMAC-SHA)", RFC 4634, July 2006.
-
- [I-D.irtf-cfrg-curves]
- Langley, A., Salz, R., and S. Turner, "Elliptic Curves for
- Security", draft-irtf-cfrg-curves-01 (work in progress),
- January 2015.
-
-10.2. Informative References
-
-
- [RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness
- Requirements for Security", BCP 106, RFC 4086, June 2005.
-
-
-
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 18]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- [EDDSA] Bernstein, D., Duif, N., Lange, T., Schwabe, P., and B.
- Yang, "High-speed high-security signatures", WWW
- http://ed25519.cr.yp.to/ed25519-20110926.pdf, September
- 2011.
-
- [Faster-ECC]
- Bernstein, D. and T. Lange, "Faster addition and doubling
- on elliptic curves", WWW http://eprint.iacr.org/2007/286,
- July 2007.
-
- [Edwards-revisited]
- Hisil, H., Wong, K., Carter, G., and E. Dawson, "Twisted
- Edwards Curves Revisited", WWW
- http://eprint.iacr.org/2008/522, December 2008.
-
- [CURVE25519]
- Bernstein, D., "Curve25519: new Diffie-Hellman speed
- records", WWW http://cr.yp.to/ecdh.html, February 2006.
-
- [ED25519-TEST-VECTORS]
- Bernstein, D., Duif, N., Lange, T., Schwabe, P., and B.
- Yang, "Ed25519 test vectors", WWW
- http://ed25519.cr.yp.to/python/sign.input, July 2011.
-
- [ED25519-LIBGCRYPT-TEST-VECTORS]
- Koch, W., "Ed25519 Libgcrypt test vectors", WWW
- http://git.gnupg.org/cgi-
- bin/gitweb.cgi?p=libgcrypt.git;a=blob;f=tests/t-ed25519.in
- p;h=e13566f826321eece65e02c593bc7d885b3dbe23;hb=refs/
- heads/master, July 2014.
-
-Appendix A. Ed25519 Python Library
-
-
- Below is an example implementation of Ed25519 written in Python,
- version 3.2 or higher is required.
-
-# Loosely based on the public domain code at
-# http://ed25519.cr.yp.to/software.html
-#
-# Needs python-3.2
-
-import hashlib
-
-
-def sha512(s):
- return hashlib.sha512(s).digest()
-
-# Base field Z_p
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 19]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
-p = 2**255 - 19
-
-
-def modp_inv(x):
- return pow(x, p-2, p)
-
-# Curve constant
-d = -121665 * modp_inv(121666) % p
-
-# Group order
-q = 2**252 + 27742317777372353535851937790883648493
-
-
-def sha512_modq(s):
- return int.from_bytes(sha512(s), "little") % q
-
-# Points are represented as tuples (X, Y, Z, T) of extended coordinates,
-# with x = X/Z, y = Y/Z, x*y = T/Z
-
-
-def point_add(P, Q):
- A = (P[1]-P[0])*(Q[1]-Q[0]) % p
- B = (P[1]+P[0])*(Q[1]+Q[0]) % p
- C = 2 * P[3] * Q[3] * d % p
- D = 2 * P[2] * Q[2] % p
- E = B-A
- F = D-C
- G = D+C
- H = B+A
- return (E*F, G*H, F*G, E*H)
-
-
-# Computes Q = s * Q
-def point_mul(s, P):
- Q = (0, 1, 1, 0) # Neutral element
- while s > 0:
- # Is there any bit-set predicate?
- if s & 1:
- Q = point_add(Q, P)
- P = point_add(P, P)
- s >>= 1
- return Q
-
-
-def point_equal(P, Q):
- # x1 / z1 == x2 / z2 <==> x1 * z2 == x2 * z1
- if (P[0] * Q[2] - Q[0] * P[2]) % p != 0:
- return False
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 20]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- if (P[1] * Q[2] - Q[1] * P[2]) % p != 0:
- return False
- return True
-
-# Square root of -1
-modp_sqrt_m1 = pow(2, (p-1) // 4, p)
-
-
-# Compute corresponding x coordinate, with low bit corresponding to sign,
-# or return None on failure
-def recover_x(y, sign):
- x2 = (y*y-1) * modp_inv(d*y*y+1)
- if x2 == 0:
- if sign:
- return None
- else:
- return 0
-
- # Compute square root of x2
- x = pow(x2, (p+3) // 8, p)
- if (x*x - x2) % p != 0:
- x = x * modp_sqrt_m1 % p
- if (x*x - x2) % p != 0:
- return None
-
- if (x & 1) != sign:
- x = p - x
- return x
-
-# Base point
-g_y = 4 * modp_inv(5) % p
-g_x = recover_x(g_y, 0)
-G = (g_x, g_y, 1, g_x * g_y % p)
-
-
-def point_compress(P):
- zinv = modp_inv(P[2])
- x = P[0] * zinv % p
- y = P[1] * zinv % p
- return int.to_bytes(y | ((x & 1) << 255), 32, "little")
-
-
-def point_decompress(s):
- if len(s) != 32:
- raise Exception("Invalid input length for decompression")
- y = int.from_bytes(s, "little")
- sign = y >> 255
- y &= (1 << 255) - 1
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 21]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- x = recover_x(y, sign)
- if x is None:
- return None
- else:
- return (x, y, 1, x*y % p)
-
-
-def secret_expand(secret):
- if len(secret) != 32:
- raise Exception("Bad size of private key")
- h = sha512(secret)
- a = int.from_bytes(h[:32], "little")
- a &= (1 << 254) - 8
- a |= (1 << 254)
- return (a, h[32:])
-
-
-def secret_to_public(secret):
- (a, dummy) = secret_expand(secret)
- return point_compress(point_mul(a, G))
-
-
-def sign(secret, msg):
- a, prefix = secret_expand(secret)
- A = point_compress(point_mul(a, G))
- r = sha512_modq(prefix + msg)
- R = point_mul(r, G)
- Rs = point_compress(R)
- h = sha512_modq(Rs + A + msg)
- s = (r + h * a) % q
- return Rs + int.to_bytes(s, 32, "little")
-
-
-def verify(public, msg, signature):
- if len(public) != 32:
- raise Exception("Bad public-key length")
- if len(signature) != 64:
- Exception("Bad signature length")
- A = point_decompress(public)
- if not A:
- return False
- Rs = signature[:32]
- R = point_decompress(Rs)
- if not R:
- return False
- s = int.from_bytes(signature[32:], "little")
- h = sha512_modq(Rs + public + msg)
- sB = point_mul(s, G)
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 22]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- hA = point_mul(h, A)
- return point_equal(sB, point_add(R, hA))
-
-Appendix B. Library driver
-
-
- Below is a command-line tool that uses the library above to perform
- computations, for interactive use or for self-checking.
-
- import sys
- import binascii
-
- from ed25519 import *
-
- def point_valid(P):
- zinv = modp_inv(P[2])
- x = P[0] * zinv % p
- y = P[1] * zinv % p
- assert (x*y - P[3]*zinv) % p == 0
- return (-x*x + y*y - 1 - d*x*x*y*y) % p == 0
-
- assert point_valid(G)
- Z = (0, 1, 1, 0)
- assert point_valid(Z)
-
- assert point_equal(Z, point_add(Z, Z))
- assert point_equal(G, point_add(Z, G))
- assert point_equal(Z, point_mul(0, G))
- assert point_equal(G, point_mul(1, G))
- assert point_equal(point_add(G, G), point_mul(2, G))
- for i in range(0, 100):
- assert point_valid(point_mul(i, G))
- assert point_equal(Z, point_mul(q, G))
-
- def munge_string(s, pos, change):
- return (s[:pos] +
- int.to_bytes(s[pos] ^ change, 1, "little") +
- s[pos+1:])
-
- # Read a file in the format of
- # http://ed25519.cr.yp.to/python/sign.input
- lineno = 0
- while True:
- line = sys.stdin.readline()
- if not line:
- break
- lineno = lineno + 1
- print(lineno)
- fields = line.split(":")
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 23]
-
-
-Internet-Draft EdDSA & Ed25519 February 2015
-
-
- secret = (binascii.unhexlify(fields[0]))[:32]
- public = binascii.unhexlify(fields[1])
- msg = binascii.unhexlify(fields[2])
- signature = binascii.unhexlify(fields[3])[:64]
-
- assert public == secret_to_public(secret)
- assert signature == sign(secret, msg)
- assert verify(public, msg, signature)
- if len(msg) == 0:
- bad_msg = b"x"
- else:
- bad_msg = munge_string(msg, len(msg) // 3, 4)
- assert not verify(public, bad_msg, signature)
- bad_signature = munge_string(signature, 20, 8)
- assert not verify(public, msg, bad_signature)
- bad_signature = munge_string(signature, 40, 16)
- assert not verify(public, msg, bad_signature)
-
-Authors' Addresses
-
- Simon Josefsson
- SJD AB
-
- Email: simon@josefsson.org
- URI: http://josefsson.org/
-
-
- Niels Moeller
-
- Email: nisse@lysator.liu.se
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Josefsson & Moeller Expires August 26, 2015 [Page 24]
-
-
-
-Html markup produced by rfcmarkup 1.113, available from https://tools.ietf.org/tools/rfcmarkup/
+Note: This draft is now superseded by https://datatracker.ietf.org/doc/rfc8032/
+(review of the differences is left as an exercise for the reader)
diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt
index d50a9df67..21445959d 100644
--- a/src/cryptonote_basic/CMakeLists.txt
+++ b/src/cryptonote_basic/CMakeLists.txt
@@ -27,9 +27,13 @@
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
if(APPLE)
- find_library(IOKIT_LIBRARY IOKit)
- mark_as_advanced(IOKIT_LIBRARY)
- list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY})
+ if(DEPENDS)
+ list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit")
+ else()
+ find_library(IOKIT_LIBRARY IOKit)
+ mark_as_advanced(IOKIT_LIBRARY)
+ list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY})
+ endif()
endif()
set(cryptonote_basic_sources
diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp
index 4cbfa8142..e891a748d 100644
--- a/src/cryptonote_basic/account.cpp
+++ b/src/cryptonote_basic/account.cpp
@@ -197,10 +197,14 @@ DISABLE_VS_WARNINGS(4244 4345)
//-----------------------------------------------------------------
void account_base::create_from_device(const std::string &device_name)
{
-
hw::device &hwdev = hw::get_device(device_name);
- m_keys.set_device(hwdev);
hwdev.set_name(device_name);
+ create_from_device(hwdev);
+ }
+
+ void account_base::create_from_device(hw::device &hwdev)
+ {
+ m_keys.set_device(hwdev);
MCDEBUG("ledger", "device type: "<<typeid(hwdev).name());
hwdev.init();
hwdev.connect();
diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h
index dac66ff1a..98bba55b1 100644
--- a/src/cryptonote_basic/account.h
+++ b/src/cryptonote_basic/account.h
@@ -77,7 +77,8 @@ namespace cryptonote
public:
account_base();
crypto::secret_key generate(const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false, bool two_random = false);
- void create_from_device(const std::string &device_name) ;
+ void create_from_device(const std::string &device_name);
+ void create_from_device(hw::device &hwdev);
void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey);
void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey);
bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys);
diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp
index c7e2f5a65..3751d6473 100644
--- a/src/cryptonote_core/blockchain.cpp
+++ b/src/cryptonote_core/blockchain.cpp
@@ -109,6 +109,12 @@ static const struct {
// version 7 starts from block 1546000, which is on or around the 6th of April, 2018. Fork time finalised on 2018-03-17.
{ 7, 1546000, 0, 1521303150 },
+
+ // version 8 starts from block 1685555, which is on or around the 18th of October, 2018. Fork time finalised on 2018-09-02.
+ { 8, 1685555, 0, 1535889547 },
+
+ // version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02.
+ { 9, 1686275, 0, 1535889548 },
};
static const uint64_t mainnet_hard_fork_version_1_till = 1009826;
diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt
index 84004c3c6..b1c4b711d 100644
--- a/src/daemon/CMakeLists.txt
+++ b/src/daemon/CMakeLists.txt
@@ -28,7 +28,9 @@
set(blocksdat "")
if(PER_BLOCK_CHECKPOINT)
- if(APPLE)
+ if(APPLE AND DEPENDS)
+ add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
+ elseif(APPLE AND NOT DEPENDS)
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*)
else()
add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat)
diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp
index 82ece62a9..f483ba6c9 100644
--- a/src/daemon/main.cpp
+++ b/src/daemon/main.cpp
@@ -239,11 +239,14 @@ int main(int argc, char const * argv[])
return 1;
}
+ const char *env_rpc_login = nullptr;
+ const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login);
+ const bool use_rpc_env = !has_rpc_arg && (env_rpc_login = getenv("RPC_LOGIN")) != nullptr && strlen(env_rpc_login) > 0;
boost::optional<tools::login> login{};
- if (command_line::has_arg(vm, arg.rpc_login))
+ if (has_rpc_arg || use_rpc_env)
{
login = tools::login::parse(
- command_line::get_arg(vm, arg.rpc_login), false, [](bool verify) {
+ has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), false, [](bool verify) {
#ifdef HAVE_READLINE
rdln::suspend_readline pause_readline;
#endif
diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp
index 9ab1be246..6b6c88907 100644
--- a/src/daemon/rpc_command_executor.cpp
+++ b/src/daemon/rpc_command_executor.cpp
@@ -533,6 +533,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u
req.start_height = start_block_index;
req.end_height = end_block_index;
+ req.fill_pow_hash = false;
std::string fail_message = "Unsuccessful";
@@ -1746,6 +1747,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks)
bhreq.start_height = ires.height - nblocks;
bhreq.end_height = ires.height - 1;
+ bhreq.fill_pow_hash = false;
if (m_is_rpc)
{
if (!m_rpc_client->json_rpc_request(bhreq, bhres, "getblockheadersrange", fail_message.c_str()))
diff --git a/src/device/device.cpp b/src/device/device.cpp
index 983f59b60..8a8b40061 100644
--- a/src/device/device.cpp
+++ b/src/device/device.cpp
@@ -39,32 +39,60 @@ namespace hw {
/* ======================================================================= */
/* SETUP */
- /* ======================================================================= */
- device& get_device(const std::string device_descriptor) {
-
- struct s_devices {
- std::map<std::string, std::unique_ptr<device>> registry;
- s_devices() : registry() {
- hw::core::register_all(registry);
- #ifdef HAVE_PCSC
- hw::ledger::register_all(registry);
- #endif
- };
- };
-
- static const s_devices devices;
+ /* ======================================================================= */
+
+ static std::unique_ptr<device_registry> registry;
+
+ device_registry::device_registry(){
+ hw::core::register_all(registry);
+ #ifdef HAVE_PCSC
+ hw::ledger::register_all(registry);
+ #endif
+ }
+
+ bool device_registry::register_device(const std::string & device_name, device * hw_device){
+ auto search = registry.find(device_name);
+ if (search != registry.end()){
+ return false;
+ }
+
+ registry.insert(std::make_pair(device_name, std::unique_ptr<device>(hw_device)));
+ return true;
+ }
+
+ device& device_registry::get_device(const std::string & device_descriptor){
+ // Device descriptor can contain further specs after first :
+ auto delim = device_descriptor.find(':');
+ auto device_descriptor_lookup = device_descriptor;
+ if (delim != std::string::npos) {
+ device_descriptor_lookup = device_descriptor.substr(0, delim);
+ }
- auto device = devices.registry.find(device_descriptor);
- if (device == devices.registry.end()) {
- MERROR("device not found in registry: '" << device_descriptor << "'\n" <<
- "known devices:");
-
- for( const auto& sm_pair : devices.registry ) {
+ auto device = registry.find(device_descriptor_lookup);
+ if (device == registry.end()) {
+ MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: ");
+ for( const auto& sm_pair : registry ) {
MERROR(" - " << sm_pair.first);
}
- throw std::runtime_error("device not found: "+ device_descriptor);
+ throw std::runtime_error("device not found: " + device_descriptor);
}
return *device->second;
}
+ device& get_device(const std::string & device_descriptor) {
+ if (!registry){
+ registry.reset(new device_registry());
+ }
+
+ return registry->get_device(device_descriptor);
+ }
+
+ bool register_device(const std::string & device_name, device * hw_device){
+ if (!registry){
+ registry.reset(new device_registry());
+ }
+
+ return registry->register_device(device_name, hw_device);
+ }
+
}
diff --git a/src/device/device.hpp b/src/device/device.hpp
index c21456daf..d14b8848c 100644
--- a/src/device/device.hpp
+++ b/src/device/device.hpp
@@ -202,6 +202,17 @@ namespace hw {
~reset_mode() { hwref.set_mode(hw::device::NONE);}
};
- device& get_device(const std::string device_descriptor) ;
+ class device_registry {
+ private:
+ std::map<std::string, std::unique_ptr<device>> registry;
+
+ public:
+ device_registry();
+ bool register_device(const std::string & device_name, device * hw_device);
+ device& get_device(const std::string & device_descriptor);
+ };
+
+ device& get_device(const std::string & device_descriptor);
+ bool register_device(const std::string & device_name, device * hw_device);
}
diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp
index e680a8157..f11f442bc 100644
--- a/src/gen_multisig/gen_multisig.cpp
+++ b/src/gen_multisig/gen_multisig.cpp
@@ -91,8 +91,8 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str
for (size_t n = 0; n < total; ++n)
{
std::string name = basename + "-" + std::to_string(n + 1);
- wallets[n].reset(new tools::wallet2(nettype));
- wallets[n]->init(false, "");
+ wallets[n].reset(new tools::wallet2(nettype, 1, false));
+ wallets[n]->init("");
wallets[n]->generate(name, pwd_container->password(), rct::rct2sk(rct::skGen()), false, false, create_address_file);
}
diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h
index 1ae8c89d6..0566b1079 100644
--- a/src/mnemonics/chinese_simplified.h
+++ b/src/mnemonics/chinese_simplified.h
@@ -72,7 +72,10 @@ namespace Language
class Chinese_Simplified: public Base
{
public:
- Chinese_Simplified(): Base("简体中文 (中国)", "Chinese (simplified)", std::vector<std::string>({
+ Chinese_Simplified(): Base("简体中文 (中国)", "Chinese (simplified)", {}, 1)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"的",
"一",
"是",
@@ -1699,8 +1702,8 @@ namespace Language
"秒",
"浙",
"貌"
- }), 1)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h
index c9806f450..801caf986 100644
--- a/src/mnemonics/dutch.h
+++ b/src/mnemonics/dutch.h
@@ -49,7 +49,10 @@ namespace Language
class Dutch: public Base
{
public:
- Dutch(): Base("Nederlands", "Dutch", std::vector<std::string>({
+ Dutch(): Base("Nederlands", "Dutch", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"aalglad",
"aalscholver",
"aambeeld",
@@ -1676,8 +1679,8 @@ namespace Language
"zwiep",
"zwijmel",
"zworen"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h
index ee087674d..d5c5594ef 100644
--- a/src/mnemonics/english.h
+++ b/src/mnemonics/english.h
@@ -49,7 +49,10 @@ namespace Language
class English: public Base
{
public:
- English(): Base("English", "English", std::vector<std::string>({
+ English(): Base("English", "English", {}, 3)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"abbey",
"abducts",
"ability",
@@ -1676,8 +1679,8 @@ namespace Language
"zombie",
"zones",
"zoom"
- }), 3)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/english_old.h b/src/mnemonics/english_old.h
index b31491646..e35b907df 100644
--- a/src/mnemonics/english_old.h
+++ b/src/mnemonics/english_old.h
@@ -51,7 +51,10 @@ namespace Language
class EnglishOld: public Base
{
public:
- EnglishOld(): Base("EnglishOld", "English (old)", std::vector<std::string>({
+ EnglishOld(): Base("EnglishOld", "English (old)", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"like",
"just",
"love",
@@ -1678,8 +1681,8 @@ namespace Language
"unseen",
"weapon",
"weary"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps(ALLOW_DUPLICATE_PREFIXES | ALLOW_SHORT_WORDS);
}
};
diff --git a/src/mnemonics/esperanto.h b/src/mnemonics/esperanto.h
index a1d1a3f30..b0be235ed 100644
--- a/src/mnemonics/esperanto.h
+++ b/src/mnemonics/esperanto.h
@@ -58,7 +58,10 @@ namespace Language
class Esperanto: public Base
{
public:
- Esperanto(): Base("Esperanto", "Esperanto", std::vector<std::string>({
+ Esperanto(): Base("Esperanto", "Esperanto", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"abako",
"abdiki",
"abelo",
@@ -1685,8 +1688,8 @@ namespace Language
"zorgi",
"zukino",
"zumilo",
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h
index 7eaf45650..48ec46f78 100644
--- a/src/mnemonics/french.h
+++ b/src/mnemonics/french.h
@@ -49,7 +49,10 @@ namespace Language
class French: public Base
{
public:
- French(): Base("Français", "French", std::vector<std::string>({
+ French(): Base("Français", "French", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"abandon",
"abattre",
"aboi",
@@ -1676,8 +1679,8 @@ namespace Language
"zinc",
"zone",
"zoom"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h
index 8eff43523..883a173a3 100644
--- a/src/mnemonics/german.h
+++ b/src/mnemonics/german.h
@@ -51,7 +51,10 @@ namespace Language
class German: public Base
{
public:
- German(): Base("Deutsch", "German", std::vector<std::string>({
+ German(): Base("Deutsch", "German", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"Abakus",
"Abart",
"abbilden",
@@ -1678,8 +1681,8 @@ namespace Language
"Zündung",
"Zweck",
"Zyklop"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h
index d5ecb74f4..57cdfa25e 100644
--- a/src/mnemonics/italian.h
+++ b/src/mnemonics/italian.h
@@ -51,7 +51,10 @@ namespace Language
class Italian: public Base
{
public:
- Italian(): Base("Italiano", "Italian", std::vector<std::string>({
+ Italian(): Base("Italiano", "Italian", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"abbinare",
"abbonato",
"abisso",
@@ -1678,8 +1681,8 @@ namespace Language
"zolfo",
"zombie",
"zucchero"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h
index f3b3e4924..5baabedf2 100644
--- a/src/mnemonics/japanese.h
+++ b/src/mnemonics/japanese.h
@@ -71,7 +71,10 @@ namespace Language
class Japanese: public Base
{
public:
- Japanese(): Base("日本語", "Japanese", std::vector<std::string>({
+ Japanese(): Base("日本語", "Japanese", {}, 3)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"あいこくしん",
"あいさつ",
"あいだ",
@@ -1698,8 +1701,8 @@ namespace Language
"ひさん",
"びじゅつかん",
"ひしょ"
- }), 3)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h
index cf518ab2a..52e784cef 100644
--- a/src/mnemonics/language_base.h
+++ b/src/mnemonics/language_base.h
@@ -83,7 +83,10 @@ namespace Language
ALLOW_SHORT_WORDS = 1<<0,
ALLOW_DUPLICATE_PREFIXES = 1<<1,
};
- const std::vector<std::string> word_list; /*!< A pointer to the array of words */
+ enum {
+ NWORDS = 1626
+ };
+ std::vector<std::string> word_list; /*!< A pointer to the array of words */
std::unordered_map<epee::wipeable_string, uint32_t> word_map; /*!< hash table to find word's index */
std::unordered_map<epee::wipeable_string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
std::string language_name; /*!< Name of language */
@@ -96,7 +99,7 @@ namespace Language
{
int ii;
std::vector<std::string>::const_iterator it;
- if (word_list.size () != 1626)
+ if (word_list.size () != NWORDS)
throw std::runtime_error("Wrong word list length for " + language_name);
for (it = word_list.begin(), ii = 0; it != word_list.end(); it++, ii++)
{
@@ -138,6 +141,12 @@ namespace Language
virtual ~Base()
{
}
+ void set_words(const char * const words[])
+ {
+ word_list.resize(NWORDS);
+ for (size_t i = 0; i < NWORDS; ++i)
+ word_list[i] = words[i];
+ }
/*!
* \brief Returns a pointer to the word list.
* \return A pointer to the word list.
diff --git a/src/mnemonics/lojban.h b/src/mnemonics/lojban.h
index 0966a1169..5162a8ec9 100644
--- a/src/mnemonics/lojban.h
+++ b/src/mnemonics/lojban.h
@@ -56,7 +56,10 @@ namespace Language
class Lojban: public Base
{
public:
- Lojban(): Base("Lojban", "Lojban", std::vector<std::string>({
+ Lojban(): Base("Lojban", "Lojban", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"backi",
"bacru",
"badna",
@@ -1683,8 +1686,8 @@ namespace Language
"noltruti'u",
"samtci",
"snaxa'a",
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h
index 0195389ce..af04f89c2 100644
--- a/src/mnemonics/portuguese.h
+++ b/src/mnemonics/portuguese.h
@@ -72,7 +72,10 @@ namespace Language
class Portuguese: public Base
{
public:
- Portuguese(): Base("Português", "Portuguese", std::vector<std::string>({
+ Portuguese(): Base("Português", "Portuguese", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"abaular",
"abdominal",
"abeto",
@@ -1699,8 +1702,8 @@ namespace Language
"zeloso",
"zenite",
"zumbi"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h
index d5dd556ef..f3e70ede6 100644
--- a/src/mnemonics/russian.h
+++ b/src/mnemonics/russian.h
@@ -51,7 +51,10 @@ namespace Language
class Russian: public Base
{
public:
- Russian(): Base("русский язык", "Russian", std::vector<std::string>({
+ Russian(): Base("русский язык", "Russian", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"абажур",
"абзац",
"абонент",
@@ -1678,8 +1681,8 @@ namespace Language
"яхта",
"ячейка",
"ящик"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h
index 51f38fede..4d7a896a6 100644
--- a/src/mnemonics/spanish.h
+++ b/src/mnemonics/spanish.h
@@ -72,7 +72,10 @@ namespace Language
class Spanish: public Base
{
public:
- Spanish(): Base("Español", "Spanish", std::vector<std::string>({
+ Spanish(): Base("Español", "Spanish", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"ábaco",
"abdomen",
"abeja",
@@ -1699,8 +1702,8 @@ namespace Language
"risa",
"ritmo",
"rito"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps(ALLOW_SHORT_WORDS);
}
};
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h
index ffc4df3ed..18290637b 100644
--- a/src/ringct/rctTypes.h
+++ b/src/ringct/rctTypes.h
@@ -313,10 +313,10 @@ namespace rct {
return false;
if (type == RCTTypeBulletproof)
{
- ar.tag("bp");
- ar.begin_array();
uint32_t nbp = bulletproofs.size();
FIELD(nbp)
+ ar.tag("bp");
+ ar.begin_array();
if (nbp > outputs)
return false;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs);
diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp
index db7f2dbaa..a85b94541 100644
--- a/src/rpc/core_rpc_server.cpp
+++ b/src/rpc/core_rpc_server.cpp
@@ -198,7 +198,7 @@ namespace cryptonote
res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit();
res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median();
res.status = CORE_RPC_STATUS_OK;
- res.start_time = (uint64_t)m_core.get_start_time();
+ res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time();
res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space();
res.offline = m_core.offline();
res.bootstrap_daemon_address = m_bootstrap_daemon_address;
@@ -779,7 +779,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);
diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp
index d4044d11b..60c78480a 100644
--- a/src/rpc/rpc_args.cpp
+++ b/src/rpc/rpc_args.cpp
@@ -82,11 +82,17 @@ namespace cryptonote
}
}
- if (command_line::has_arg(vm, arg.rpc_login))
+ const char *env_rpc_login = nullptr;
+ const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login);
+ const bool use_rpc_env = !has_rpc_arg && (env_rpc_login = getenv("RPC_LOGIN")) != nullptr && strlen(env_rpc_login) > 0;
+ boost::optional<tools::login> login{};
+ if (has_rpc_arg || use_rpc_env)
{
- config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, [](bool verify) {
- return tools::password_container::prompt(verify, "RPC server password");
- });
+ config.login = tools::login::parse(
+ has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), true, [](bool verify) {
+ return tools::password_container::prompt(verify, "RPC server password");
+ });
+
if (!config.login)
return boost::none;
diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp
index e535b6d27..391d1f03e 100644
--- a/src/simplewallet/simplewallet.cpp
+++ b/src/simplewallet/simplewallet.cpp
@@ -2523,6 +2523,10 @@ simple_wallet::simple_wallet()
boost::bind(&simple_wallet::import_key_images, this, _1),
tr("import_key_images <file>"),
tr("Import a signed key images list and verify their spent status."));
+ m_cmd_binder.set_handler("hw_reconnect",
+ boost::bind(&simple_wallet::hw_reconnect, this, _1),
+ tr("hw_reconnect"),
+ tr("Attempts to reconnect HW wallet."));
m_cmd_binder.set_handler("export_outputs",
boost::bind(&simple_wallet::export_outputs, this, _1),
tr("export_outputs <file>"),
@@ -2650,6 +2654,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args)
success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second;
success_msg_writer() << "segregation-height = " << m_wallet->segregation_height();
success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs();
+ success_msg_writer() << "device_name = " << m_wallet->device_name();
return true;
}
else
@@ -3009,20 +3014,19 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse view secret key
- std::string viewkey_string = input_line("View key: ");
+ epee::wipeable_string viewkey_string = input_secure_line("Secret view key: ");
if (std::cin.eof())
return false;
if (viewkey_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
}
- cryptonote::blobdata viewkey_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
+ crypto::secret_key viewkey;
+ if (viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
{
fail_msg_writer() << tr("failed to parse view key secret key");
return false;
}
- crypto::secret_key viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
m_wallet_file=m_generate_from_view_key;
@@ -3045,14 +3049,14 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_spend_key;
// parse spend secret key
- std::string spendkey_string = input_line("Secret spend key: ");
+ epee::wipeable_string spendkey_string = input_secure_line("Secret spend key: ");
if (std::cin.eof())
return false;
if (spendkey_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
}
- if (!epee::string_tools::hex_to_pod(spendkey_string, m_recovery_key))
+ if (!spendkey_string.hex_to_pod(unwrap(unwrap(m_recovery_key))))
{
fail_msg_writer() << tr("failed to parse spend key secret key");
return false;
@@ -3085,36 +3089,34 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse spend secret key
- std::string spendkey_string = input_line("Secret spend key: ");
+ epee::wipeable_string spendkey_string = input_secure_line("Secret spend key: ");
if (std::cin.eof())
return false;
if (spendkey_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
}
- cryptonote::blobdata spendkey_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
+ crypto::secret_key spendkey;
+ if (!spendkey_string.hex_to_pod(unwrap(unwrap(spendkey))))
{
fail_msg_writer() << tr("failed to parse spend key secret key");
return false;
}
- crypto::secret_key spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
// parse view secret key
- std::string viewkey_string = input_line("Secret view key: ");
+ epee::wipeable_string viewkey_string = input_secure_line("Secret view key: ");
if (std::cin.eof())
return false;
if (viewkey_string.empty()) {
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
}
- cryptonote::blobdata viewkey_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
+ crypto::secret_key viewkey;
+ if(!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
{
fail_msg_writer() << tr("failed to parse view key secret key");
return false;
}
- crypto::secret_key viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
m_wallet_file=m_generate_from_keys;
@@ -3190,7 +3192,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
}
// parse secret view key
- std::string viewkey_string = input_line("Secret view key: ");
+ epee::wipeable_string viewkey_string = input_secure_line("Secret view key: ");
if (std::cin.eof())
return false;
if (viewkey_string.empty())
@@ -3198,13 +3200,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
}
- cryptonote::blobdata viewkey_data;
- if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key))
+ crypto::secret_key viewkey;
+ if(!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey))))
{
fail_msg_writer() << tr("failed to parse secret view key");
return false;
}
- crypto::secret_key viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data());
// check that the view key matches the given address
crypto::public_key pkey;
@@ -3225,12 +3226,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
if(multisig_m == multisig_n)
{
std::vector<crypto::secret_key> multisig_secret_spendkeys(multisig_n);
- std::string spendkey_string;
+ epee::wipeable_string spendkey_string;
cryptonote::blobdata spendkey_data;
// get N secret spend keys from user
for(unsigned int i=0; i<multisig_n; ++i)
{
- spendkey_string = input_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+1) % multisig_m).str().c_str()));
+ spendkey_string = input_secure_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+1) % multisig_m).str().c_str()));
if (std::cin.eof())
return false;
if (spendkey_string.empty())
@@ -3238,12 +3239,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
fail_msg_writer() << tr("No data supplied, cancelled");
return false;
}
- if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key))
+ if(!spendkey_string.hex_to_pod(unwrap(unwrap(multisig_secret_spendkeys[i]))))
{
fail_msg_writer() << tr("failed to parse spend key secret key");
return false;
}
- multisig_secret_spendkeys[i] = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data());
}
// sum the spend keys together to get the master spend key
@@ -3295,7 +3295,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm)
{
m_wallet_file = m_generate_from_device;
// create wallet
- auto r = new_wallet(vm, "Ledger");
+ auto r = new_wallet(vm);
CHECK_AND_ASSERT_MES(r, false, tr("account creation failed"));
password = *r;
// if no block_height is specified, assume its a new account and start it "now"
@@ -3703,8 +3703,8 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
}
//----------------------------------------------------------------------------------------------------
-boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm,
- const std::string &device_name) {
+boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm)
+{
auto rc = tools::wallet2::make_new(vm, false, password_prompter);
m_wallet = std::move(rc.first);
if (!m_wallet)
@@ -3723,10 +3723,11 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr
if (m_restore_height)
m_wallet->set_refresh_from_block_height(m_restore_height);
+ auto device_desc = tools::wallet2::device_name_option(vm);
try
{
bool create_address_file = command_line::get_arg(vm, arg_create_address_file);
- m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_name, create_address_file);
+ m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file);
message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ")
<< m_wallet->get_account().get_public_address_str(m_wallet->nettype());
}
@@ -4083,7 +4084,7 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args)
daemon_url = args[0];
}
LOCK_IDLE_SCOPE();
- m_wallet->init(false, daemon_url);
+ m_wallet->init(daemon_url);
if (args.size() == 2)
{
@@ -7745,6 +7746,31 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args)
return true;
}
//----------------------------------------------------------------------------------------------------
+bool simple_wallet::hw_reconnect(const std::vector<std::string> &args)
+{
+ if (!m_wallet->key_on_device())
+ {
+ fail_msg_writer() << tr("command only supported by HW wallet");
+ return true;
+ }
+
+ LOCK_IDLE_SCOPE();
+ try
+ {
+ bool r = m_wallet->reconnect_device();
+ if (!r){
+ fail_msg_writer() << tr("Failed to reconnect device");
+ }
+ }
+ catch (const std::exception &e)
+ {
+ fail_msg_writer() << tr("Failed to reconnect device: ") << tr(e.what());
+ return true;
+ }
+
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
bool simple_wallet::export_outputs(const std::vector<std::string> &args)
{
if (m_wallet->key_on_device())
diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h
index bfbe633ac..d50e4ce04 100644
--- a/src/simplewallet/simplewallet.h
+++ b/src/simplewallet/simplewallet.h
@@ -98,7 +98,7 @@ namespace cryptonote
const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey);
boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm,
const epee::wipeable_string &multisig_keys, const std::string &old_language);
- boost::optional<epee::wipeable_string> 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);
bool open_wallet(const boost::program_options::variables_map& vm);
bool close_wallet();
@@ -201,6 +201,7 @@ namespace cryptonote
bool verify(const std::vector<std::string> &args);
bool export_key_images(const std::vector<std::string> &args);
bool import_key_images(const std::vector<std::string> &args);
+ bool hw_reconnect(const std::vector<std::string> &args);
bool export_outputs(const std::vector<std::string> &args);
bool import_outputs(const std::vector<std::string> &args);
bool show_transfer(const std::vector<std::string> &args);
diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt
index a16f4fe19..be10b9f62 100644
--- a/src/wallet/CMakeLists.txt
+++ b/src/wallet/CMakeLists.txt
@@ -90,6 +90,8 @@ target_link_libraries(wallet_rpc_server
cncrypto
common
version
+ daemonizer
+ ${EPEE_READLINE}
${Boost_CHRONO_LIBRARY}
${Boost_PROGRAM_OPTIONS_LIBRARY}
${Boost_FILESYSTEM_LIBRARY}
diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp
index f64a44243..463185284 100644
--- a/src/wallet/api/wallet.cpp
+++ b/src/wallet/api/wallet.cpp
@@ -376,7 +376,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds)
, m_rebuildWalletCache(false)
, m_is_connected(false)
{
- m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds);
+ m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true);
m_history = new TransactionHistoryImpl(this);
m_wallet2Callback = new Wallet2CallbackImpl(this);
m_wallet->callback(m_wallet2Callback);
@@ -1244,6 +1244,20 @@ size_t WalletImpl::importMultisigImages(const vector<string>& images) {
return 0;
}
+bool WalletImpl::hasMultisigPartialKeyImages() const {
+ try {
+ clearStatus();
+ checkMultisigWalletReady(m_wallet);
+
+ return m_wallet->has_multisig_partial_key_images();
+ } catch (const exception& e) {
+ LOG_ERROR("Error on checking for partial multisig key images: ") << e.what();
+ setStatusError(string(tr("Failed to check for partial multisig key images: ")) + e.what());
+ }
+
+ return false;
+}
+
PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signData) {
try {
clearStatus();
@@ -2033,7 +2047,7 @@ bool WalletImpl::isNewWallet() const
bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl)
{
// claim RPC so there's no in-memory encryption for now
- if (!m_wallet->init(true, daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
+ if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl))
return false;
// in case new wallet, this will force fast-refresh (pulling hashes instead of blocks)
diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h
index 209040157..2cbfa30d0 100644
--- a/src/wallet/api/wallet.h
+++ b/src/wallet/api/wallet.h
@@ -139,6 +139,7 @@ public:
bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) override;
bool exportMultisigImages(std::string& images) override;
size_t importMultisigImages(const std::vector<std::string>& images) override;
+ bool hasMultisigPartialKeyImages() const override;
PendingTransaction* restoreMultisigTransaction(const std::string& signData) override;
PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id,
diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h
index 594fcc6f1..933916076 100644
--- a/src/wallet/api/wallet2_api.h
+++ b/src/wallet/api/wallet2_api.h
@@ -720,6 +720,11 @@ struct Wallet
* @return number of imported images
*/
virtual size_t importMultisigImages(const std::vector<std::string>& images) = 0;
+ /**
+ * @brief hasMultisigPartialKeyImages - checks if wallet needs to import multisig key images from other participants
+ * @return true if there are partial key images
+ */
+ virtual bool hasMultisigPartialKeyImages() const = 0;
/**
* @brief restoreMultisigTransaction creates PendingTransaction from signData
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index b266821d6..6502463e2 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -161,6 +161,7 @@ struct options {
}
};
const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1};
+ const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""};
};
void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file)
@@ -211,6 +212,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
auto daemon_address = command_line::get_arg(vm, opts.daemon_address);
auto daemon_host = command_line::get_arg(vm, opts.daemon_host);
auto daemon_port = command_line::get_arg(vm, opts.daemon_port);
+ auto device_name = command_line::get_arg(vm, opts.hw_device);
THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port,
tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once"));
@@ -261,10 +263,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl
catch (const std::exception &e) { }
}
- std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds));
- wallet->init(unattended, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
+ std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended));
+ wallet->init(std::move(daemon_address), std::move(login), 0, false, *trusted_daemon);
boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir);
wallet->set_ring_database(ringdb_path.string());
+ wallet->device_name(device_name);
return wallet;
}
@@ -748,7 +751,7 @@ wallet_keys_unlocker::~wallet_keys_unlocker()
w.encrypt_keys(key);
}
-wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
+wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended):
m_multisig_rescan_info(NULL),
m_multisig_rescan_k(NULL),
m_run(true),
@@ -796,7 +799,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds):
m_ringdb(),
m_last_block_reward(0),
m_encrypt_keys_after_refresh(boost::none),
- m_unattended(false)
+ m_unattended(unattended)
{
}
@@ -814,6 +817,11 @@ bool wallet2::has_stagenet_option(const boost::program_options::variables_map& v
return command_line::get_arg(vm, options().stagenet);
}
+std::string wallet2::device_name_option(const boost::program_options::variables_map& vm)
+{
+ return command_line::get_arg(vm, options().hw_device);
+}
+
void wallet2::init_options(boost::program_options::options_description& desc_params)
{
const options opts{};
@@ -829,6 +837,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.shared_ringdb_dir);
command_line::add_arg(desc_params, opts.kdf_rounds);
+ command_line::add_arg(desc_params, opts.hw_device);
}
std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter)
@@ -872,9 +881,8 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
}
//----------------------------------------------------------------------------------------------------
-bool wallet2::init(bool unattended, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon)
+bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon)
{
- m_unattended = unattended;
m_checkpoints.init_default_checkpoints(m_nettype);
if(m_http_client.is_connected())
m_http_client.disconnect();
@@ -984,6 +992,27 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl
return true;
}
//----------------------------------------------------------------------------------------------------
+bool wallet2::reconnect_device()
+{
+ bool r = true;
+ hw::device &hwdev = hw::get_device(m_device_name);
+ hwdev.set_name(m_device_name);
+ r = hwdev.init();
+ if (!r){
+ LOG_PRINT_L2("Could not init device");
+ return false;
+ }
+
+ r = hwdev.connect();
+ if (!r){
+ LOG_PRINT_L2("Could not connect to the device");
+ return false;
+ }
+
+ m_account.set_device(hwdev);
+ return true;
+}
+//----------------------------------------------------------------------------------------------------
/*!
* \brief Gets the seed language
*/
@@ -2980,6 +3009,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable
value2.SetUint(1);
json.AddMember("encrypted_secret_keys", value2, json.GetAllocator());
+ value.SetString(m_device_name.c_str(), m_device_name.size());
+ json.AddMember("device_name", value, json.GetAllocator());
+
// Serialize the JSON object
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
@@ -3088,6 +3120,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
m_ignore_fractional_outputs = true;
m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR;
m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR;
+ m_device_name = "";
m_key_on_device = false;
encrypted_secret_keys = false;
}
@@ -3219,8 +3252,15 @@ 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;
+
GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false);
encrypted_secret_keys = field_encrypted_secret_keys;
+
+ GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string());
+ if (m_device_name.empty() && field_device_name_found)
+ {
+ m_device_name = field_device_name;
+ }
}
else
{
@@ -3231,7 +3271,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_
r = epee::serialization::load_t_from_binary(m_account, account_data);
if (r && m_key_on_device) {
LOG_PRINT_L0("Account on device. Initing device...");
- hw::device &hwdev = hw::get_device("Ledger");
+ hw::device &hwdev = hw::get_device(m_device_name);
+ hwdev.set_name(m_device_name);
hwdev.init();
hwdev.connect();
m_account.set_device(hwdev);
@@ -3673,13 +3714,18 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p
THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file);
}
m_key_on_device = true;
- m_account.create_from_device(device_name);
+
+ auto &hwdev = hw::get_device(device_name);
+ hwdev.set_name(device_name);
+
+ m_account.create_from_device(hwdev);
m_account_public_address = m_account.get_keys().m_account_address;
m_watch_only = false;
m_multisig = false;
m_multisig_threshold = 0;
m_multisig_signers.clear();
setup_keys(password);
+ m_device_name = device_name;
create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file);
if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR)
@@ -10784,7 +10830,12 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs)
refresh(false);
}
- catch (...) {}
+ catch (...)
+ {
+ m_multisig_rescan_info = NULL;
+ m_multisig_rescan_k = NULL;
+ throw;
+ }
m_multisig_rescan_info = NULL;
m_multisig_rescan_k = NULL;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 402066b50..c30ca1d85 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -172,6 +172,7 @@ namespace tools
static bool has_testnet_option(const boost::program_options::variables_map& vm);
static bool has_stagenet_option(const boost::program_options::variables_map& vm);
+ static std::string device_name_option(const boost::program_options::variables_map& vm);
static void init_options(boost::program_options::options_description& desc_params);
//! Uses stdin and stdout. Returns a wallet2 if no errors.
@@ -189,7 +190,7 @@ namespace tools
static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds);
- wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1);
+ wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false);
~wallet2();
struct multisig_info
@@ -638,7 +639,7 @@ namespace tools
bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;}
bool deinit();
- bool init(bool unatteded, std::string daemon_address = "http://localhost:8080",
+ bool init(std::string daemon_address = "http://localhost:8080",
boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false);
void stop() { m_run.store(false, std::memory_order_relaxed); }
@@ -709,6 +710,7 @@ namespace tools
bool has_unknown_key_images() const;
bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const;
bool key_on_device() const { return m_key_on_device; }
+ bool reconnect_device();
// locked & unlocked balance of given or current subaddress account
uint64_t balance(uint32_t subaddr_index_major) const;
@@ -938,6 +940,8 @@ namespace tools
void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; }
bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; }
void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; }
+ const std::string & device_name() const { return m_device_name; }
+ void device_name(const std::string & device_name) { m_device_name = device_name; }
bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const;
void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys);
@@ -1319,6 +1323,7 @@ namespace tools
NodeRPCProxy m_node_rpc_proxy;
std::unordered_set<crypto::hash> m_scanned_pool_txs[2];
size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor;
+ std::string m_device_name;
// Light wallet
bool m_light_wallet; /* sends view key to daemon for scanning */
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index ee86587bc..86b46b173 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -51,6 +51,7 @@ using namespace epee;
#include "mnemonics/electrum-words.h"
#include "rpc/rpc_args.h"
#include "rpc/core_rpc_server_commands_defs.h"
+#include "daemonizer/daemonizer.h"
#undef MONERO_DEFAULT_LOG_CATEGORY
#define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc"
@@ -759,10 +760,10 @@ namespace tools
{
if (get_tx_key)
{
- std::string s = epee::string_tools::pod_to_hex(ptx.tx_key);
+ epee::wipeable_string s = epee::to_hex::wipeable_string(ptx.tx_key);
for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys)
- s += epee::string_tools::pod_to_hex(additional_tx_key);
- fill(tx_key, s);
+ s += epee::to_hex::wipeable_string(additional_tx_key);
+ fill(tx_key, std::string(s.data(), s.size()));
}
// Compute amount leaving wallet in tx. By convention dests does not include change outputs
fill(amount, total_amount(ptx));
@@ -1569,11 +1570,13 @@ namespace tools
}
else if(req.key_type.compare("view_key") == 0)
{
- res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key);
+ epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_view_secret_key);
+ res.key = std::string(key.data(), key.size());
}
else if(req.key_type.compare("spend_key") == 0)
{
- res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key);
+ epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key);
+ res.key = std::string(key.data(), key.size());
}
else
{
@@ -1799,11 +1802,11 @@ namespace tools
return false;
}
- std::ostringstream oss;
- oss << epee::string_tools::pod_to_hex(tx_key);
+ epee::wipeable_string s;
+ s += epee::to_hex::wipeable_string(tx_key);
for (size_t i = 0; i < additional_tx_keys.size(); ++i)
- oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]);
- res.tx_key = oss.str();
+ s += epee::to_hex::wipeable_string(additional_tx_keys[i]);
+ res.tx_key = std::string(s.data(), s.size());
return true;
}
//------------------------------------------------------------------------------------------------------------------------------
@@ -1819,26 +1822,33 @@ namespace tools
return false;
}
- std::string tx_key_str = req.tx_key;
+ epee::wipeable_string tx_key_str = req.tx_key;
+ if (tx_key_str.size() < 64 || tx_key_str.size() % 64)
+ {
+ er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
+ er.message = "Tx key has invalid format";
+ return false;
+ }
+ const char *data = tx_key_str.data();
crypto::secret_key tx_key;
- if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key))
+ if (!epee::wipeable_string(data, 64).hex_to_pod(unwrap(unwrap(tx_key))))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
er.message = "Tx key has invalid format";
return false;
}
- tx_key_str = tx_key_str.substr(64);
+ size_t offset = 64;
std::vector<crypto::secret_key> additional_tx_keys;
- while (!tx_key_str.empty())
+ while (offset < tx_key_str.size())
{
additional_tx_keys.resize(additional_tx_keys.size() + 1);
- if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back()))
+ if (!epee::wipeable_string(data + offset, 64).hex_to_pod(unwrap(unwrap(additional_tx_keys.back()))))
{
er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY;
er.message = "Tx key has invalid format";
return false;
}
- tx_key_str = tx_key_str.substr(64);
+ offset += 64;
}
cryptonote::address_parse_info info;
@@ -3265,12 +3275,172 @@ namespace tools
//------------------------------------------------------------------------------------------------------------------------------
}
+class t_daemon
+{
+private:
+ const boost::program_options::variables_map& vm;
+
+public:
+ t_daemon(boost::program_options::variables_map const & _vm)
+ : vm(_vm)
+ {
+ }
+
+ bool run()
+ {
+ std::unique_ptr<tools::wallet2> wal;
+ try
+ {
+ const bool testnet = tools::wallet2::has_testnet_option(vm);
+ const bool stagenet = tools::wallet2::has_stagenet_option(vm);
+ if (testnet && stagenet)
+ {
+ MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet"));
+ return false;
+ }
+
+ const auto arg_wallet_file = wallet_args::arg_wallet_file();
+ const auto arg_from_json = wallet_args::arg_generate_from_json();
+
+ const auto wallet_file = command_line::get_arg(vm, arg_wallet_file);
+ const auto from_json = command_line::get_arg(vm, arg_from_json);
+ const auto wallet_dir = command_line::get_arg(vm, arg_wallet_dir);
+ const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password);
+ const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
+
+ if(!wallet_file.empty() && !from_json.empty())
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json"));
+ return false;
+ }
+
+ if (!wallet_dir.empty())
+ {
+ wal = NULL;
+ goto just_dir;
+ }
+
+ if (wallet_file.empty() && from_json.empty())
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
+ return false;
+ }
+
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
+ if(!wallet_file.empty())
+ {
+ wal = tools::wallet2::make_from_file(vm, true, wallet_file, password_prompt).first;
+ }
+ else
+ {
+ try
+ {
+ wal = tools::wallet2::make_from_json(vm, true, from_json, password_prompt);
+ }
+ catch (const std::exception &e)
+ {
+ MERROR("Error creating wallet: " << e.what());
+ return false;
+ }
+ }
+ if (!wal)
+ {
+ return false;
+ }
+
+ bool quit = false;
+ tools::signal_handler::install([&wal, &quit](int) {
+ assert(wal);
+ quit = true;
+ wal->stop();
+ });
+
+ wal->refresh(wal->is_trusted_daemon());
+ // if we ^C during potentially length load/refresh, there's no server loop yet
+ if (quit)
+ {
+ MINFO(tools::wallet_rpc_server::tr("Saving wallet..."));
+ wal->store();
+ MINFO(tools::wallet_rpc_server::tr("Successfully saved"));
+ return false;
+ }
+ MINFO(tools::wallet_rpc_server::tr("Successfully loaded"));
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
+ return false;
+ }
+ just_dir:
+ tools::wallet_rpc_server wrpc;
+ if (wal) wrpc.set_wallet(wal.release());
+ bool r = wrpc.init(&vm);
+ CHECK_AND_ASSERT_MES(r, false, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server"));
+ tools::signal_handler::install([&wrpc](int) {
+ wrpc.send_stop_signal();
+ });
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
+ try
+ {
+ wrpc.run();
+ }
+ catch (const std::exception &e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
+ return false;
+ }
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server"));
+ try
+ {
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet..."));
+ wrpc.stop();
+ LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved"));
+ }
+ catch (const std::exception& e)
+ {
+ LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what());
+ return false;
+ }
+ return true;
+ }
+};
+
+class t_executor final
+{
+public:
+ static std::string const NAME;
+
+ std::string const & name()
+ {
+ return NAME;
+ }
+
+ t_daemon create_daemon(boost::program_options::variables_map const & vm)
+ {
+ return t_daemon(vm);
+ }
+
+ bool run_non_interactive(boost::program_options::variables_map const & vm)
+ {
+ return t_daemon(vm).run();
+ }
+
+ bool run_interactive(boost::program_options::variables_map const & vm)
+ {
+ return t_daemon(vm).run();
+ }
+};
+
+std::string const t_executor::NAME = "Wallet RPC Daemon";
+
int main(int argc, char** argv) {
namespace po = boost::program_options;
const auto arg_wallet_file = wallet_args::arg_wallet_file();
const auto arg_from_json = wallet_args::arg_generate_from_json();
+ po::options_description hidden_options("Hidden");
+
po::options_description desc_params(wallet_args::tr("Wallet options"));
tools::wallet2::init_options(desc_params);
command_line::add_arg(desc_params, arg_rpc_bind_port);
@@ -3282,6 +3452,8 @@ int main(int argc, char** argv) {
command_line::add_arg(desc_params, arg_wallet_dir);
command_line::add_arg(desc_params, arg_prompt_for_password);
+ daemonizer::init_options(hidden_options, desc_params);
+
boost::optional<po::variables_map> vm;
bool should_terminate = false;
std::tie(vm, should_terminate) = wallet_args::main(
@@ -3303,115 +3475,5 @@ int main(int argc, char** argv) {
return 0;
}
- std::unique_ptr<tools::wallet2> wal;
- try
- {
- const bool testnet = tools::wallet2::has_testnet_option(*vm);
- const bool stagenet = tools::wallet2::has_stagenet_option(*vm);
- if (testnet && stagenet)
- {
- MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet"));
- return 1;
- }
-
- const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file);
- const auto from_json = command_line::get_arg(*vm, arg_from_json);
- const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir);
- const auto prompt_for_password = command_line::get_arg(*vm, arg_prompt_for_password);
- const auto password_prompt = prompt_for_password ? password_prompter : nullptr;
-
- if(!wallet_file.empty() && !from_json.empty())
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json"));
- return 1;
- }
-
- if (!wallet_dir.empty())
- {
- wal = NULL;
- goto just_dir;
- }
-
- if (wallet_file.empty() && from_json.empty())
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir"));
- return 1;
- }
-
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet..."));
- if(!wallet_file.empty())
- {
- wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first;
- }
- else
- {
- try
- {
- wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt);
- }
- catch (const std::exception &e)
- {
- MERROR("Error creating wallet: " << e.what());
- return 1;
- }
- }
- if (!wal)
- {
- return 1;
- }
-
- bool quit = false;
- tools::signal_handler::install([&wal, &quit](int) {
- assert(wal);
- quit = true;
- wal->stop();
- });
-
- wal->refresh(wal->is_trusted_daemon());
- // if we ^C during potentially length load/refresh, there's no server loop yet
- if (quit)
- {
- MINFO(tools::wallet_rpc_server::tr("Saving wallet..."));
- wal->store();
- MINFO(tools::wallet_rpc_server::tr("Successfully saved"));
- return 1;
- }
- MINFO(tools::wallet_rpc_server::tr("Successfully loaded"));
- }
- catch (const std::exception& e)
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what());
- return 1;
- }
-just_dir:
- tools::wallet_rpc_server wrpc;
- if (wal) wrpc.set_wallet(wal.release());
- bool r = wrpc.init(&(vm.get()));
- CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server"));
- tools::signal_handler::install([&wrpc](int) {
- wrpc.send_stop_signal();
- });
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server"));
- try
- {
- wrpc.run();
- }
- catch (const std::exception &e)
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what());
- return 1;
- }
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server"));
- try
- {
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet..."));
- wrpc.stop();
- LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved"));
- }
- catch (const std::exception& e)
- {
- LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what());
- return 1;
- }
- return 0;
+ return daemonizer::daemonize(argc, const_cast<const char**>(argv), t_executor{}, *vm) ? 0 : 1;
}