1
0
Fork 0
forked from lthn/blockchain

Merge branch 'pos_impr' into develop

This commit is contained in:
cryptozoidberg 2019-08-14 15:33:28 +02:00
commit d218716983
No known key found for this signature in database
GPG key ID: 22DEB97A54C6FDEC
73 changed files with 3165 additions and 2040 deletions

View file

@ -1,14 +1,70 @@
LMDB 0.9 Change Log
LMDB 0.9.18 Release Engineering
LMDB 0.9.24 Release (2019/07/24)
ITS#8969 Tweak mdb_page_split
ITS#8975 WIN32 fix writemap set_mapsize crash
ITS#9007 Fix loose pages in WRITEMAP
LMDB 0.9.23 Release (2018/12/19)
ITS#8756 Fix loose pages in dirty list
ITS#8831 Fix mdb_load flag init
ITS#8844 Fix mdb_env_close in forked process
Documentation
ITS#8857 mdb_cursor_del doesn't invalidate cursor
ITS#8908 GET_MULTIPLE etc don't change passed in key
LMDB 0.9.22 Release (2018/03/22)
Fix MDB_DUPSORT alignment bug (ITS#8819)
Fix regression with new db from 0.9.19 (ITS#8760)
Fix liblmdb to build on Solaris (ITS#8612)
Fix delete behavior with DUPSORT DB (ITS#8622)
Fix mdb_cursor_get/mdb_cursor_del behavior (ITS#8722)
LMDB 0.9.21 Release (2017/06/01)
Fix xcursor after cursor_del (ITS#8622)
LMDB 0.9.20 (Withdrawn)
Fix mdb_load with escaped plaintext (ITS#8558)
Fix mdb_cursor_last / mdb_put interaction (ITS#8557)
LMDB 0.9.19 Release (2016/12/28)
Fix mdb_env_cwalk cursor init (ITS#8424)
Fix robust mutexes on Solaris 10/11 (ITS#8339)
Tweak Win32 error message buffer
Fix MDB_GET_BOTH on non-dup record (ITS#8393)
Optimize mdb_drop
Fix xcursors after mdb_cursor_del (ITS#8406)
Fix MDB_NEXT_DUP after mdb_cursor_del (ITS#8412)
Fix mdb_cursor_put resetting C_EOF (ITS#8489)
Fix mdb_env_copyfd2 to return EPIPE on SIGPIPE (ITS#8504)
Fix mdb_env_copy with empty DB (ITS#8209)
Fix behaviors with fork (ITS#8505)
Fix mdb_dbi_open with mainDB cursors (ITS#8542)
Fix robust mutexes on kFreeBSD (ITS#8554)
Fix utf8_to_utf16 error checks (ITS#7992)
Fix F_NOCACHE on MacOS, error is non-fatal (ITS#7682)
Build
Make shared lib suffix overridable (ITS#8481)
Documentation
Cleanup doxygen nits
Note reserved vs actual mem/disk usage
LMDB 0.9.18 Release (2016/02/05)
Fix robust mutex detection on glibc 2.10-11 (ITS#8330)
Fix page_search_root assert on FreeDB (ITS#8336)
Fix MDB_APPENDDUP vs. rewrite(single item) (ITS#8334)
Fix mdb_copy of large files on Windows
Fix subcursor move after delete (ITS#8355)
Fix mdb_midl_shirnk off-by-one (ITS#8363)
Check for utf8_to_utf16 failures (ITS#7992)
Catch strdup failure in mdb_dbi_open
Build
Additional makefile var tweaks (ITS#8169)
Documentation
Add Getting Started page
Update WRITEMAP description
LMDB 0.9.17 Release (2015/11/30)
Fix ITS#7377 catch calloc failure

View file

@ -1,4 +1,4 @@
Copyright 2011-2015 Howard Chu, Symas Corp.
Copyright 2011-2019 Howard Chu, Symas Corp.
All rights reserved.
Redistribution and use in source and binary forms, with or without

View file

@ -253,7 +253,7 @@ IDL_PROPERTY_SUPPORT = YES
# member in the group (if any) for the other members of the group. By default
# all members of a group must be documented explicitly.
DISTRIBUTE_GROUP_DOC = NO
DISTRIBUTE_GROUP_DOC = YES
# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
# the same type (for instance a group of public functions) to be put as a

View file

@ -8,7 +8,7 @@
# platforms; you should not need to change any of these.
# Read their descriptions in mdb.c if you do:
#
# - MDB_USE_POSIX_MUTEX, MDB_USE_POSIX_SEM, MDB_USE_SYSV_SEM
# - MDB_USE_POSIX_SEM
# - MDB_DSYNC
# - MDB_FDATASYNC
# - MDB_FDATASYNC_WORKS
@ -24,8 +24,9 @@ W = -W -Wall -Wno-unused-parameter -Wbad-function-cast -Wuninitialized
THREADS = -pthread
OPT = -O2 -g
CFLAGS = $(THREADS) $(OPT) $(W) $(XCFLAGS)
LDLIBS = # -lntdll # Windows needs ntdll
SOLIBS = # -lntdll
LDLIBS =
SOLIBS =
SOEXT = .so
prefix = /usr/local
exec_prefix = $(prefix)
bindir = $(exec_prefix)/bin
@ -37,7 +38,7 @@ mandir = $(datarootdir)/man
########################################################################
IHDRS = lmdb.h
ILIBS = liblmdb.a liblmdb.so
ILIBS = liblmdb.a liblmdb$(SOEXT)
IPROGS = mdb_stat mdb_copy mdb_dump mdb_load
IDOCS = mdb_stat.1 mdb_copy.1 mdb_dump.1 mdb_load.1
PROGS = $(IPROGS) mtest mtest2 mtest3 mtest4 mtest5
@ -63,7 +64,7 @@ test: all
liblmdb.a: mdb.o midl.o
$(AR) rs $@ mdb.o midl.o
liblmdb.so: mdb.lo midl.lo
liblmdb$(SOEXT): mdb.lo midl.lo
# $(CC) $(LDFLAGS) -pthread -shared -Wl,-Bsymbolic -o $@ mdb.o midl.o $(SOLIBS)
$(CC) $(LDFLAGS) -pthread -shared -o $@ mdb.lo midl.lo $(SOLIBS)

View file

@ -1,5 +1,5 @@
/*
* Copyright 2015 Howard Chu, Symas Corp.
* Copyright 2015-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -53,15 +53,14 @@
*
* Fix: Check for stale readers periodically, using the
* #mdb_reader_check function or the \ref mdb_stat_1 "mdb_stat" tool.
* Stale writers will be cleared automatically on most systems:
* Stale writers will be cleared automatically on some systems:
* - Windows - automatic
* - BSD, systems using SysV semaphores - automatic
* - Linux, systems using POSIX mutexes with Robust option - automatic
* - not on BSD, systems using POSIX semaphores.
* Otherwise just make all programs using the database close it;
* the lockfile is always reset on first open of the environment.
*
* - On BSD systems or others configured with MDB_USE_SYSV_SEM or
* MDB_USE_POSIX_SEM,
* - On BSD systems or others configured with MDB_USE_POSIX_SEM,
* startup can fail due to semaphores owned by another userid.
*
* Fix: Open and close the database as the user which owns the
@ -97,11 +96,12 @@
* transactions. Each transaction belongs to one thread. See below.
* The #MDB_NOTLS flag changes this for read-only transactions.
*
* - Use an MDB_env* in the process which opened it, without fork()ing.
* - Use an MDB_env* in the process which opened it, not after fork().
*
* - Do not have open an LMDB database twice in the same process at
* the same time. Not even from a plain open() call - close()ing it
* breaks flock() advisory locking.
* breaks fcntl() advisory locking. (It is OK to reopen it after
* fork() - exec*(), since the lockfile has FD_CLOEXEC set.)
*
* - Avoid long-lived transactions. Read transactions prevent
* reuse of pages freed by newer write transactions, thus the
@ -135,7 +135,7 @@
*
* @author Howard Chu, Symas Corporation.
*
* @copyright Copyright 2011-2016 Howard Chu, Symas Corp. All rights reserved.
* @copyright Copyright 2011-2019 Howard Chu, Symas Corp. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted only as authorized by the OpenLDAP
@ -166,7 +166,6 @@
#define _LMDB_H_
#include <sys/types.h>
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
@ -179,13 +178,6 @@ typedef int mdb_mode_t;
typedef mode_t mdb_mode_t;
#endif
#ifdef MDB_VL32
typedef uint64_t mdb_size_t;
#define mdb_env_create mdb_env_create_vl32 /**< Prevent mixing with non-VL32 builds */
#else
typedef size_t mdb_size_t;
#endif
/** An abstraction for a file handle.
* On POSIX systems file handles are small integers. On Windows
* they're opaque pointers.
@ -208,7 +200,7 @@ typedef int mdb_filehandle_t;
/** Library minor version */
#define MDB_VERSION_MINOR 9
/** Library patch version */
#define MDB_VERSION_PATCH 70
#define MDB_VERSION_PATCH 24
/** Combine args a,b,c into a single integer for easy version comparisons */
#define MDB_VERINT(a,b,c) (((a) << 24) | ((b) << 16) | (c))
@ -218,7 +210,7 @@ typedef int mdb_filehandle_t;
MDB_VERINT(MDB_VERSION_MAJOR,MDB_VERSION_MINOR,MDB_VERSION_PATCH)
/** The release date of this library version */
#define MDB_VERSION_DATE "December 19, 2015"
#define MDB_VERSION_DATE "July 24, 2019"
/** A stringifier for the version info */
#define MDB_VERSTR(a,b,c,d) "LMDB " #a "." #b "." #c ": (" d ")"
@ -311,8 +303,6 @@ typedef void (MDB_rel_func)(MDB_val *item, void *oldptr, void *newptr, void *rel
#define MDB_NORDAHEAD 0x800000
/** don't initialize malloc'd memory before writing to datafile */
#define MDB_NOMEMINIT 0x1000000
/** use the previous snapshot rather than the latest one */
#define MDB_PREVSNAPSHOT 0x2000000
/** @} */
/** @defgroup mdb_dbi_open Database Flags
@ -380,7 +370,7 @@ typedef enum MDB_cursor_op {
MDB_GET_BOTH, /**< Position at key/data pair. Only for #MDB_DUPSORT */
MDB_GET_BOTH_RANGE, /**< position at key, nearest data. Only for #MDB_DUPSORT */
MDB_GET_CURRENT, /**< Return key/data at current cursor position */
MDB_GET_MULTIPLE, /**< Return key and up to a page of duplicate data items
MDB_GET_MULTIPLE, /**< Return up to a page of duplicate data items
from current cursor position. Move cursor to prepare
for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */
MDB_LAST, /**< Position at last key/data item */
@ -389,7 +379,7 @@ typedef enum MDB_cursor_op {
MDB_NEXT, /**< Position at next data item */
MDB_NEXT_DUP, /**< Position at next data item of current key.
Only for #MDB_DUPSORT */
MDB_NEXT_MULTIPLE, /**< Return key and up to a page of duplicate data items
MDB_NEXT_MULTIPLE, /**< Return up to a page of duplicate data items
from next cursor position. Move cursor to prepare
for #MDB_NEXT_MULTIPLE. Only for #MDB_DUPFIXED */
MDB_NEXT_NODUP, /**< Position at first data item of next key */
@ -400,7 +390,7 @@ typedef enum MDB_cursor_op {
MDB_SET, /**< Position at specified key */
MDB_SET_KEY, /**< Position at specified key, return key + data */
MDB_SET_RANGE, /**< Position at first key greater than or equal to specified key. */
MDB_PREV_MULTIPLE /**< Position at previous page and return key and up to
MDB_PREV_MULTIPLE /**< Position at previous page and return up to
a page of duplicate data items. Only for #MDB_DUPFIXED */
} MDB_cursor_op;
@ -467,18 +457,18 @@ typedef struct MDB_stat {
unsigned int ms_psize; /**< Size of a database page.
This is currently the same for all databases. */
unsigned int ms_depth; /**< Depth (height) of the B-tree */
mdb_size_t ms_branch_pages; /**< Number of internal (non-leaf) pages */
mdb_size_t ms_leaf_pages; /**< Number of leaf pages */
mdb_size_t ms_overflow_pages; /**< Number of overflow pages */
mdb_size_t ms_entries; /**< Number of data items */
size_t ms_branch_pages; /**< Number of internal (non-leaf) pages */
size_t ms_leaf_pages; /**< Number of leaf pages */
size_t ms_overflow_pages; /**< Number of overflow pages */
size_t ms_entries; /**< Number of data items */
} MDB_stat;
/** @brief Information about the environment */
typedef struct MDB_envinfo {
void *me_mapaddr; /**< Address of map, if fixed */
mdb_size_t me_mapsize; /**< Size of the data memory map */
mdb_size_t me_last_pgno; /**< ID of the last used page */
mdb_size_t me_last_txnid; /**< ID of the last committed transaction */
size_t me_mapsize; /**< Size of the data memory map */
size_t me_last_pgno; /**< ID of the last used page */
size_t me_last_txnid; /**< ID of the last committed transaction */
unsigned int me_maxreaders; /**< max reader slots in the environment */
unsigned int me_numreaders; /**< max reader slots used in the environment */
} MDB_envinfo;
@ -624,12 +614,6 @@ int mdb_env_create(MDB_env **env);
* caller is expected to overwrite all of the memory that was
* reserved in that case.
* This flag may be changed at any time using #mdb_env_set_flags().
* <li>#MDB_PREVSNAPSHOT
* Open the environment with the previous snapshot rather than the latest
* one. This loses the latest transaction, but may help work around some
* types of corruption. If opened with write access, this must be the
* only process using the environment. This flag is automatically reset
* after a write transaction is successfully committed.
* </ul>
* @param[in] mode The UNIX permissions to set on created files and semaphores.
* This parameter is ignored on Windows.
@ -696,6 +680,7 @@ int mdb_env_copyfd(MDB_env *env, mdb_filehandle_t fd);
* <li>#MDB_CP_COMPACT - Perform compaction while copying: omit free
* pages and sequentially renumber all pages in output. This option
* consumes more CPU and runs more slowly than the default.
* Currently it fails if the environment has suffered a page leak.
* </ul>
* @return A non-zero error value on failure and 0 on success.
*/
@ -810,6 +795,10 @@ int mdb_env_get_flags(MDB_env *env, unsigned int *flags);
int mdb_env_get_path(MDB_env *env, const char **path);
/** @brief Return the filedescriptor for the given environment.
*
* This function may be called after fork(), so the descriptor can be
* closed before exec*(). Other LMDB file descriptors have FD_CLOEXEC.
* (Until LMDB 0.9.18, only the lockfile had that.)
*
* @param[in] env An environment handle returned by #mdb_env_create()
* @param[out] fd Address of a mdb_filehandle_t to contain the descriptor.
@ -853,7 +842,7 @@ int mdb_env_get_fd(MDB_env *env, mdb_filehandle_t *fd);
* an active write transaction.
* </ul>
*/
int mdb_env_set_mapsize(MDB_env *env, mdb_size_t size);
int mdb_env_set_mapsize(MDB_env *env, size_t size);
/** @brief Set the maximum number of threads/reader slots for the environment.
*
@ -966,10 +955,6 @@ int mdb_env_set_assert(MDB_env *env, MDB_assert_func *func);
* <ul>
* <li>#MDB_RDONLY
* This transaction will not perform any write operations.
* <li>#MDB_NOSYNC
* Don't flush system buffers to disk when committing this transaction.
* <li>#MDB_NOMETASYNC
* Flush system buffers but omit metadata flush when committing this transaction.
* </ul>
* @param[out] txn Address where the new #MDB_txn handle will be stored
* @return A non-zero error value on failure and 0 on success. Some possible
@ -1002,7 +987,7 @@ MDB_env *mdb_txn_env(MDB_txn *txn);
* @param[in] txn A transaction handle returned by #mdb_txn_begin()
* @return A transaction ID, valid if input is an active transaction.
*/
mdb_size_t mdb_txn_id(MDB_txn *txn);
size_t mdb_txn_id(MDB_txn *txn);
/** @brief Commit all the operations of a transaction into the database.
*
@ -1118,8 +1103,9 @@ int mdb_txn_renew(MDB_txn *txn);
* This flag may only be used in combination with #MDB_DUPSORT. This option
* tells the library that the data items for this database are all the same
* size, which allows further optimizations in storage and retrieval. When
* all data items are the same size, the #MDB_GET_MULTIPLE and #MDB_NEXT_MULTIPLE
* cursor operations may be used to retrieve multiple items at once.
* all data items are the same size, the #MDB_GET_MULTIPLE, #MDB_NEXT_MULTIPLE
* and #MDB_PREV_MULTIPLE cursor operations may be used to retrieve multiple
* items at once.
* <li>#MDB_INTEGERDUP
* This option specifies that duplicate data items are binary integers,
* similar to #MDB_INTEGERKEY keys.
@ -1524,6 +1510,10 @@ int mdb_cursor_put(MDB_cursor *cursor, MDB_val *key, MDB_val *data,
/** @brief Delete current key/data pair
*
* This function deletes the key/data pair to which the cursor refers.
* This does not invalidate the cursor, so operations such as MDB_NEXT
* can still be used on it.
* Both MDB_NEXT and MDB_GET_CURRENT will return the same record after
* this operation.
* @param[in] cursor A cursor handle returned by #mdb_cursor_open()
* @param[in] flags Options for this operation. This parameter
* must be set to 0 or one of the values described here.
@ -1552,7 +1542,7 @@ int mdb_cursor_del(MDB_cursor *cursor, unsigned int flags);
* <li>EINVAL - cursor is not initialized, or an invalid parameter was specified.
* </ul>
*/
int mdb_cursor_count(MDB_cursor *cursor, mdb_size_t *countp);
int mdb_cursor_count(MDB_cursor *cursor, size_t *countp);
/** @brief Compare two data items according to a particular database.
*

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
.TH MDB_COPY 1 "2014/06/20" "LMDB 0.9.14"
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.TH MDB_COPY 1 "2014/07/01" "LMDB 0.9.14"
.\" Copyright 2012-2018 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.SH NAME
mdb_copy \- LMDB environment copy tool
@ -11,8 +11,6 @@ mdb_copy \- LMDB environment copy tool
.BR \-c ]
[\c
.BR \-n ]
[\c
.BR \-v ]
.B srcpath
[\c
.BR dstpath ]
@ -38,13 +36,10 @@ Write the library version number to the standard output, and exit.
Compact while copying. Only current data pages will be copied; freed
or unused pages will be omitted from the copy. This option will
slow down the backup process as it is more CPU-intensive.
Currently it fails if the environment has suffered a page leak.
.TP
.BR \-n
Open LDMB environment(s) which do not use subdirectories.
.TP
.BR \-v
Use the previous environment state instead of the latest state.
This may be useful if the latest state has been corrupted.
.SH DIAGNOSTICS
Exit status is zero if no errors occur.

View file

@ -1,6 +1,6 @@
/* mdb_copy.c - memory-mapped database backup tool */
/*
* Copyright 2012-2015 Howard Chu, Symas Corp.
* Copyright 2012-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -38,8 +38,6 @@ int main(int argc,char * argv[])
for (; argc > 1 && argv[1][0] == '-'; argc--, argv++) {
if (argv[1][1] == 'n' && argv[1][2] == '\0')
flags |= MDB_NOSUBDIR;
else if (argv[1][1] == 'v' && argv[1][2] == '\0')
flags |= MDB_PREVSNAPSHOT;
else if (argv[1][1] == 'c' && argv[1][2] == '\0')
cpflags |= MDB_CP_COMPACT;
else if (argv[1][1] == 'V' && argv[1][2] == '\0') {
@ -50,7 +48,7 @@ int main(int argc,char * argv[])
}
if (argc<2 || argc>3) {
fprintf(stderr, "usage: %s [-V] [-c] [-n] [-v] srcpath [dstpath]\n", progname);
fprintf(stderr, "usage: %s [-V] [-c] [-n] srcpath [dstpath]\n", progname);
exit(EXIT_FAILURE);
}

View file

@ -1,5 +1,5 @@
.TH MDB_DUMP 1 "2014/06/20" "LMDB 0.9.14"
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.TH MDB_DUMP 1 "2015/09/30" "LMDB 0.9.17"
.\" Copyright 2014-2018 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.SH NAME
mdb_dump \- LMDB environment export tool
@ -14,8 +14,6 @@ mdb_dump \- LMDB environment export tool
[\c
.BR \-n ]
[\c
.BR \-v ]
[\c
.BR \-p ]
[\c
.BR \-a \ |
@ -44,10 +42,6 @@ names will be listed, no data will be output.
.BR \-n
Dump an LMDB database which does not use subdirectories.
.TP
.BR \-v
Use the previous environment state instead of the latest state.
This may be useful if the latest state has been corrupted.
.TP
.BR \-p
If characters in either the key or data items are printing characters (as
defined by isprint(3)), output them directly. This option permits users to

View file

@ -1,6 +1,6 @@
/* mdb_dump.c - memory-mapped database dump tool */
/*
* Copyright 2011-2015 Howard Chu, Symas Corp.
* Copyright 2011-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -25,15 +25,6 @@
#else
#define Z "z"
#endif
#ifdef MDB_VL32
#ifdef _WIN32
#define Y "I64"
#else
#define Y "ll"
#endif
#else
#define Y Z
#endif
#define PRINT 1
static int mode;
@ -124,7 +115,7 @@ static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name)
if (name)
printf("database=%s\n", name);
printf("type=btree\n");
printf("mapsize=%" Y "u\n", info.me_mapsize);
printf("mapsize=%" Z "u\n", info.me_mapsize);
if (info.me_mapaddr)
printf("mapaddr=%p\n", info.me_mapaddr);
printf("maxreaders=%u\n", info.me_maxreaders);
@ -164,7 +155,7 @@ static int dumpit(MDB_txn *txn, MDB_dbi dbi, char *name)
static void usage(char *prog)
{
fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-v] [-a|-s subdb] dbpath\n", prog);
fprintf(stderr, "usage: %s [-V] [-f output] [-l] [-n] [-p] [-a|-s subdb] dbpath\n", prog);
exit(EXIT_FAILURE);
}
@ -188,7 +179,6 @@ int main(int argc, char *argv[])
* -n: use NOSUBDIR flag on env_open
* -p: use printable characters
* -f: write to file instead of stdout
* -v: use previous snapshot
* -V: print version and exit
* (default) dump only the main DB
*/
@ -216,9 +206,6 @@ int main(int argc, char *argv[])
case 'n':
envflags |= MDB_NOSUBDIR;
break;
case 'v':
envflags |= MDB_PREVSNAPSHOT;
break;
case 'p':
mode |= PRINT;
break;

View file

@ -1,5 +1,5 @@
.TH MDB_LOAD 1 "2014/06/20" "LMDB 0.9.14"
.\" Copyright 2014-2015 Howard Chu, Symas Corp. All Rights Reserved.
.TH MDB_LOAD 1 "2015/09/30" "LMDB 0.9.17"
.\" Copyright 2014-2018 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.SH NAME
mdb_load \- LMDB environment import tool
@ -37,13 +37,6 @@ option below.
.BR \-V
Write the library version number to the standard output, and exit.
.TP
.BR \-a
Append all records in the order they appear in the input. The input is assumed to already be
in correctly sorted order and no sorting or checking for redundant values will be performed.
This option must be used to reload data that was produced by running
.B mdb_dump
on a database that uses custom compare functions.
.TP
.BR \-f \ file
Read from the specified file instead of from the standard input.
.TP

View file

@ -1,6 +1,6 @@
/* mdb_load.c - memory-mapped database load tool */
/*
* Copyright 2011-2015 Howard Chu, Symas Corp.
* Copyright 2011-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -37,22 +37,12 @@ static int Eof;
static MDB_envinfo info;
static MDB_val kbuf, dbuf;
static MDB_val k0buf;
#ifdef _WIN32
#define Z "I"
#else
#define Z "z"
#endif
#ifdef MDB_VL32
#ifdef _WIN32
#define Y "I64"
#else
#define Y "ll"
#endif
#else
#define Y Z
#endif
#define STRLENOF(s) (sizeof(s)-1)
@ -78,6 +68,7 @@ static void readhdr(void)
{
char *ptr;
flags = 0;
while (fgets(dbuf.mv_data, dbuf.mv_size, stdin) != NULL) {
lineno++;
if (!strncmp(dbuf.mv_data, "VERSION=", STRLENOF("VERSION="))) {
@ -122,7 +113,7 @@ static void readhdr(void)
int i;
ptr = memchr(dbuf.mv_data, '\n', dbuf.mv_size);
if (ptr) *ptr = '\0';
i = sscanf((char *)dbuf.mv_data+STRLENOF("mapsize="), "%" Y "u", &info.me_mapsize);
i = sscanf((char *)dbuf.mv_data+STRLENOF("mapsize="), "%" Z "u", &info.me_mapsize);
if (i != 1) {
fprintf(stderr, "%s: line %" Z "d: invalid mapsize %s\n",
prog, lineno, (char *)dbuf.mv_data+STRLENOF("mapsize="));
@ -258,7 +249,8 @@ badend:
c2 += 2;
}
} else {
c1++; c2++;
/* copies are redundant when no escapes were used */
*c1++ = *c2++;
}
}
} else {
@ -286,15 +278,10 @@ badend:
static void usage(void)
{
fprintf(stderr, "usage: %s [-V] [-a] [-f input] [-n] [-s name] [-N] [-T] dbpath\n", prog);
fprintf(stderr, "usage: %s [-V] [-f input] [-n] [-s name] [-N] [-T] dbpath\n", prog);
exit(EXIT_FAILURE);
}
static int greater(const MDB_val *a, const MDB_val *b)
{
return 1;
}
int main(int argc, char *argv[])
{
int i, rc;
@ -304,8 +291,7 @@ int main(int argc, char *argv[])
MDB_dbi dbi;
char *envname;
int envflags = 0, putflags = 0;
int dohdr = 0, append = 0;
MDB_val prevk;
int dohdr = 0;
prog = argv[0];
@ -313,23 +299,19 @@ int main(int argc, char *argv[])
usage();
}
/* -a: append records in input order
* -f: load file instead of stdin
/* -f: load file instead of stdin
* -n: use NOSUBDIR flag on env_open
* -s: load into named subDB
* -N: use NOOVERWRITE on puts
* -T: read plaintext
* -V: print version and exit
*/
while ((i = getopt(argc, argv, "af:ns:NTV")) != EOF) {
while ((i = getopt(argc, argv, "f:ns:NTV")) != EOF) {
switch(i) {
case 'V':
printf("%s\n", MDB_VERSION_STRING);
exit(0);
break;
case 'a':
append = 1;
break;
case 'f':
if (freopen(optarg, "r", stdin) == NULL) {
fprintf(stderr, "%s: %s: reopen: %s\n",
@ -388,17 +370,11 @@ int main(int argc, char *argv[])
}
kbuf.mv_size = mdb_env_get_maxkeysize(env) * 2 + 2;
kbuf.mv_data = malloc(kbuf.mv_size * 2);
k0buf.mv_size = kbuf.mv_size;
k0buf.mv_data = (char *)kbuf.mv_data + kbuf.mv_size;
prevk.mv_size = 0;
prevk.mv_data = k0buf.mv_data;
kbuf.mv_data = malloc(kbuf.mv_size);
while(!Eof) {
MDB_val key, data;
int batch = 0;
flags = 0;
int appflag;
if (!dohdr) {
dohdr = 1;
@ -416,11 +392,6 @@ int main(int argc, char *argv[])
fprintf(stderr, "mdb_open failed, error %d %s\n", rc, mdb_strerror(rc));
goto txn_abort;
}
if (append) {
mdb_set_compare(txn, dbi, greater);
if (flags & MDB_DUPSORT)
mdb_set_dupsort(txn, dbi, greater);
}
rc = mdb_cursor_open(txn, dbi, &mc);
if (rc) {
@ -439,20 +410,7 @@ int main(int argc, char *argv[])
goto txn_abort;
}
if (append) {
appflag = MDB_APPEND;
if (flags & MDB_DUPSORT) {
if (prevk.mv_size == key.mv_size && !memcmp(prevk.mv_data, key.mv_data, key.mv_size))
appflag = MDB_APPENDDUP;
else {
memcpy(prevk.mv_data, key.mv_data, key.mv_size);
prevk.mv_size = key.mv_size;
}
}
} else {
appflag = 0;
}
rc = mdb_cursor_put(mc, &key, &data, putflags|appflag);
rc = mdb_cursor_put(mc, &key, &data, putflags);
if (rc == MDB_KEYEXIST && putflags)
continue;
if (rc) {

View file

@ -1,5 +1,5 @@
.TH MDB_STAT 1 "2014/06/20" "LMDB 0.9.14"
.\" Copyright 2012-2015 Howard Chu, Symas Corp. All Rights Reserved.
.TH MDB_STAT 1 "2015/09/30" "LMDB 0.9.17"
.\" Copyright 2012-2018 Howard Chu, Symas Corp. All Rights Reserved.
.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
.SH NAME
mdb_stat \- LMDB environment status tool
@ -14,8 +14,6 @@ mdb_stat \- LMDB environment status tool
[\c
.BR \-n ]
[\c
.BR \-v ]
[\c
.BR \-r [ r ]]
[\c
.BR \-a \ |
@ -41,10 +39,6 @@ If \fB\-fff\fP is given, display the full list of page IDs in the freelist.
.BR \-n
Display the status of an LMDB database which does not use subdirectories.
.TP
.BR \-v
Use the previous environment state instead of the latest state.
This may be useful if the latest state has been corrupted.
.TP
.BR \-r
Display information about the environment reader table.
Shows the process ID, thread ID, and transaction ID for each active

View file

@ -1,6 +1,6 @@
/* mdb_stat.c - memory-mapped database status tool */
/*
* Copyright 2011-2015 Howard Chu, Symas Corp.
* Copyright 2011-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -22,15 +22,6 @@
#else
#define Z "z"
#endif
#ifdef MDB_VL32
#ifdef _WIN32
#define Y "I64"
#else
#define Y "ll"
#endif
#else
#define Y Z
#endif
static void prstat(MDB_stat *ms)
{
@ -38,15 +29,15 @@ static void prstat(MDB_stat *ms)
printf(" Page size: %u\n", ms->ms_psize);
#endif
printf(" Tree depth: %u\n", ms->ms_depth);
printf(" Branch pages: %"Y"u\n", ms->ms_branch_pages);
printf(" Leaf pages: %"Y"u\n", ms->ms_leaf_pages);
printf(" Overflow pages: %"Y"u\n", ms->ms_overflow_pages);
printf(" Entries: %"Y"u\n", ms->ms_entries);
printf(" Branch pages: %"Z"u\n", ms->ms_branch_pages);
printf(" Leaf pages: %"Z"u\n", ms->ms_leaf_pages);
printf(" Overflow pages: %"Z"u\n", ms->ms_overflow_pages);
printf(" Entries: %"Z"u\n", ms->ms_entries);
}
static void usage(char *prog)
{
fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-v] [-a|-s subdb] dbpath\n", prog);
fprintf(stderr, "usage: %s [-V] [-n] [-e] [-r[r]] [-f[f[f]]] [-a|-s subdb] dbpath\n", prog);
exit(EXIT_FAILURE);
}
@ -73,7 +64,6 @@ int main(int argc, char *argv[])
* -f: print freelist info
* -r: print reader info
* -n: use NOSUBDIR flag on env_open
* -v: use previous snapshot
* -V: print version and exit
* (default) print stat of only the main DB
*/
@ -97,9 +87,6 @@ int main(int argc, char *argv[])
case 'n':
envflags |= MDB_NOSUBDIR;
break;
case 'v':
envflags |= MDB_PREVSNAPSHOT;
break;
case 'r':
rdrinfo++;
break;
@ -138,11 +125,11 @@ int main(int argc, char *argv[])
(void)mdb_env_info(env, &mei);
printf("Environment Info\n");
printf(" Map address: %p\n", mei.me_mapaddr);
printf(" Map size: %"Y"u\n", mei.me_mapsize);
printf(" Map size: %"Z"u\n", mei.me_mapsize);
printf(" Page size: %u\n", mst.ms_psize);
printf(" Max pages: %"Y"u\n", mei.me_mapsize / mst.ms_psize);
printf(" Number of pages used: %"Y"u\n", mei.me_last_pgno+1);
printf(" Last transaction ID: %"Y"u\n", mei.me_last_txnid);
printf(" Max pages: %"Z"u\n", mei.me_mapsize / mst.ms_psize);
printf(" Number of pages used: %"Z"u\n", mei.me_last_pgno+1);
printf(" Last transaction ID: %"Z"u\n", mei.me_last_txnid);
printf(" Max readers: %u\n", mei.me_maxreaders);
printf(" Number of readers used: %u\n", mei.me_numreaders);
}

View file

@ -3,7 +3,8 @@
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2000-2015 The OpenLDAP Foundation.
* Copyright 2000-2019 The OpenLDAP Foundation.
* Portions Copyright 2001-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -354,67 +355,5 @@ int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id )
return 0;
}
#ifdef MDB_VL32
unsigned mdb_mid3l_search( MDB_ID3L ids, MDB_ID id )
{
/*
* binary search of id in ids
* if found, returns position of id
* if not found, returns first position greater than id
*/
unsigned base = 0;
unsigned cursor = 1;
int val = 0;
unsigned n = (unsigned)ids[0].mid;
while( 0 < n ) {
unsigned pivot = n >> 1;
cursor = base + pivot + 1;
val = CMP( id, ids[cursor].mid );
if( val < 0 ) {
n = pivot;
} else if ( val > 0 ) {
base = cursor;
n -= pivot + 1;
} else {
return cursor;
}
}
if( val > 0 ) {
++cursor;
}
return cursor;
}
int mdb_mid3l_insert( MDB_ID3L ids, MDB_ID3 *id )
{
unsigned x, i;
x = mdb_mid3l_search( ids, id->mid );
if( x < 1 ) {
/* internal error */
return -2;
}
if ( x <= ids[0].mid && ids[x].mid == id->mid ) {
/* duplicate */
return -1;
}
/* insert id */
ids[0].mid++;
for (i=(unsigned)ids[0].mid; i>x; i--)
ids[i] = ids[i-1];
ids[x] = *id;
return 0;
}
#endif /* MDB_VL32 */
/** @} */
/** @} */

View file

@ -11,7 +11,8 @@
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
*
* Copyright 2000-2015 The OpenLDAP Foundation.
* Copyright 2000-2019 The OpenLDAP Foundation.
* Portions Copyright 2001-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -27,7 +28,6 @@
#define _MDB_MIDL_H_
#include <stddef.h>
#include <inttypes.h>
#ifdef __cplusplus
extern "C" {
@ -43,11 +43,7 @@ extern "C" {
/** A generic unsigned ID number. These were entryIDs in back-bdb.
* Preferably it should have the same size as a pointer.
*/
#ifdef MDB_VL32
typedef uint64_t MDB_ID;
#else
typedef size_t MDB_ID;
#endif
/** An IDL is an ID List, a sorted array of IDs. The first
* element of the array is a counter for how many actual
@ -182,20 +178,6 @@ int mdb_mid2l_insert( MDB_ID2L ids, MDB_ID2 *id );
*/
int mdb_mid2l_append( MDB_ID2L ids, MDB_ID2 *id );
#ifdef MDB_VL32
typedef struct MDB_ID3 {
MDB_ID mid; /**< The ID */
void *mptr; /**< The pointer */
unsigned int mcnt; /**< Number of pages */
unsigned int mref; /**< Refcounter */
} MDB_ID3;
typedef MDB_ID3 *MDB_ID3L;
unsigned mdb_mid3l_search( MDB_ID3L ids, MDB_ID id );
int mdb_mid3l_insert( MDB_ID3L ids, MDB_ID3 *id );
#endif /* MDB_VL32 */
/** @} */
/** @} */
#ifdef __cplusplus

View file

@ -1,6 +1,6 @@
/* mtest.c - memory-mapped database tester/toy */
/*
* Copyright 2011-2015 Howard Chu, Symas Corp.
* Copyright 2011-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -1,6 +1,6 @@
/* mtest2.c - memory-mapped database tester/toy */
/*
* Copyright 2011-2015 Howard Chu, Symas Corp.
* Copyright 2011-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -1,6 +1,6 @@
/* mtest3.c - memory-mapped database tester/toy */
/*
* Copyright 2011-2015 Howard Chu, Symas Corp.
* Copyright 2011-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -1,6 +1,6 @@
/* mtest4.c - memory-mapped database tester/toy */
/*
* Copyright 2011-2015 Howard Chu, Symas Corp.
* Copyright 2011-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -1,6 +1,6 @@
/* mtest5.c - memory-mapped database tester/toy */
/*
* Copyright 2011-2015 Howard Chu, Symas Corp.
* Copyright 2011-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -1,6 +1,6 @@
/* mtest6.c - memory-mapped database tester/toy */
/*
* Copyright 2011-2015 Howard Chu, Symas Corp.
* Copyright 2011-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -3,7 +3,7 @@
* Do a line-by-line comparison of this and sample-mdb.txt
*/
/*
* Copyright 2012-2015 Howard Chu, Symas Corp.
* Copyright 2012-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -3,7 +3,7 @@
* Do a line-by-line comparison of this and sample-bdb.txt
*/
/*
* Copyright 2012-2015 Howard Chu, Symas Corp.
* Copyright 2012-2018 Howard Chu, Symas Corp.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without

View file

@ -251,6 +251,21 @@ DISABLE_VS_WARNINGS(4100)
custom_code; \
}
#define CATCH_ENTRY_WITH_FORWARDING_EXCEPTION() } \
catch(const std::exception& ex) \
{ \
LOG_ERROR("Exception at [" << LOCATION_SS << "], what=" << ex.what()); \
throw std::runtime_error(std::string("[EXCEPTION FORWARDED]: ") + ex.what()); \
} \
catch(...) \
{ \
LOG_ERROR("Exception at [" << LOCATION_SS << "], generic unknown exception \"...\""); \
throw std::runtime_error("[EXCEPTION FORWARDED]"); \
}
#define NESTED_TRY_ENTRY() try { TRY_ENTRY();
#define NESTED_CATCH_ENTRY(location) \

View file

@ -7,8 +7,13 @@
#include "misc_language.h"
#include "string_coding.h"
#include "profile_tools.h"
#include "util.h"
#define BUF_SIZE 1024
#define DB_RESIZE_MIN_FREE_SIZE (100 * 1024 * 1024) // DB map size will grow if that much space left on DB
#define DB_RESIZE_MIN_MAX_SIZE (50 * 1024 * 1024) // Minimum DB map size (starting size)
#define DB_RESIZE_INCREMENT_SIZE (100 * 1024 * 1024) // Grow step size
#define DB_RESIZE_COMMITS_TO_CHECK 50
#define CHECK_AND_ASSERT_MESS_LMDB_DB(rc, ret, mess) CHECK_AND_ASSERT_MES(res == MDB_SUCCESS, ret, "[DB ERROR]:(" << rc << ")" << mdb_strerror(rc) << ", [message]: " << mess);
#define CHECK_AND_ASSERT_THROW_MESS_LMDB_DB(rc, mess) CHECK_AND_ASSERT_THROW_MES(res == MDB_SUCCESS, "[DB ERROR]:(" << rc << ")" << mdb_strerror(rc) << ", [message]: " << mess);
@ -22,10 +27,13 @@ namespace tools
{
namespace db
{
lmdb_db_backend::lmdb_db_backend() : m_penv(AUTO_VAL_INIT(m_penv))
lmdb_db_backend::lmdb_db_backend()
: m_penv(AUTO_VAL_INIT(m_penv))
, m_commits_count(0)
{
}
lmdb_db_backend::~lmdb_db_backend()
{
NESTED_TRY_ENTRY();
@ -44,16 +52,17 @@ namespace tools
res = mdb_env_set_maxdbs(m_penv, 15);
CHECK_AND_ASSERT_MESS_LMDB_DB(res, false, "Unable to mdb_env_set_maxdbs");
res = mdb_env_set_mapsize(m_penv, cache_sz);
CHECK_AND_ASSERT_MESS_LMDB_DB(res, false, "Unable to mdb_env_set_mapsize");
m_path = path_;
#ifdef WIN32
m_path = epee::string_encoding::convert_ansii_to_utf8(m_path);
#endif
res = mdb_env_open(m_penv, m_path.c_str(), MDB_NORDAHEAD , 0644);
CHECK_AND_ASSERT_MES(tools::create_directories_if_necessary(m_path), false, "create_directories_if_necessary failed: " << m_path);
res = mdb_env_open(m_penv, m_path.c_str(), MDB_NORDAHEAD /*| MDB_NOSYNC | MDB_WRITEMAP | MDB_MAPASYNC*/, 0644);
CHECK_AND_ASSERT_MESS_LMDB_DB(res, false, "Unable to mdb_env_open, m_path=" << m_path);
resize_if_needed();
return true;
}
@ -102,6 +111,13 @@ namespace tools
{
LOG_PRINT_CYAN("[DB " << m_path << "] WRITE LOCKED", LOG_LEVEL_3);
CRITICAL_SECTION_LOCK(m_write_exclusive_lock);
if (m_commits_count.fetch_add(1, std::memory_order_relaxed) % DB_RESIZE_COMMITS_TO_CHECK == DB_RESIZE_COMMITS_TO_CHECK - 1)
{
if (!resize_if_needed())
m_commits_count.store(DB_RESIZE_COMMITS_TO_CHECK - 1, std::memory_order_relaxed); // if failed, try again on next commit
}
}
PROFILE_FUNC("lmdb_db_backend::begin_transaction");
{
@ -326,6 +342,7 @@ namespace tools
CHECK_AND_ASSERT_MESS_LMDB_DB(res, false, "Unable to mdb_put");
return true;
}
bool lmdb_db_backend::enumerate(container_handle h, i_db_callback* pcb)
{
CHECK_AND_ASSERT_MES(pcb, false, "null capback ptr passed to enumerate");
@ -380,6 +397,42 @@ namespace tools
}
return true;
}
bool lmdb_db_backend::resize_if_needed()
{
LOG_PRINT_CYAN("[DB " << m_path << "] WRITE LOCKED in resize_if_needed()", LOG_LEVEL_3);
CRITICAL_REGION_LOCAL(m_write_exclusive_lock);
if (have_tx())
{
LOG_PRINT_RED("[DB " << m_path << "] : resize_if_needed(): Have txs on stack, unable to resize!", LOG_LEVEL_0);
return false;
}
MDB_stat st = AUTO_VAL_INIT(st);
mdb_env_stat(m_penv, &st);
MDB_envinfo ei = AUTO_VAL_INIT(ei);
mdb_env_info(m_penv, &ei);
uint64_t dirty_size = ei.me_last_pgno * st.ms_psize;
int64_t size_diff = ei.me_mapsize - dirty_size;
if (size_diff >= DB_RESIZE_MIN_FREE_SIZE && ei.me_mapsize >= DB_RESIZE_MIN_MAX_SIZE)
return true; // resize is not needed
double gigabyte = 1024 * 1024 * 1024;
const uint64_t increment_size_pg_aligned = DB_RESIZE_INCREMENT_SIZE - (DB_RESIZE_INCREMENT_SIZE % st.ms_psize);
// need to resize DB
uint64_t new_size = ei.me_mapsize - (ei.me_mapsize % increment_size_pg_aligned) + increment_size_pg_aligned;
int res = mdb_env_set_mapsize(m_penv, new_size);
CHECK_AND_ASSERT_MESS_LMDB_DB(res, false, "Unable to mdb_env_set_mapsize");
LOG_PRINT_CYAN("[DB " << m_path << "] has grown: " << std::fixed << std::setprecision(2) << ei.me_mapsize / gigabyte << " GiB -> " << std::fixed << std::setprecision(2) << new_size / gigabyte << " GiB", LOG_LEVEL_0);
return true;
}
}
}

View file

@ -38,7 +38,11 @@ namespace tools
boost::recursive_mutex m_cs;
boost::recursive_mutex m_write_exclusive_lock;
std::map<std::thread::id, transactions_list> m_txs; // size_t -> count of nested read_only transactions
std::atomic<uint64_t> m_commits_count;
bool pop_tx_entry(tx_entry& txe);
public:
lmdb_db_backend();
~lmdb_db_backend();
@ -60,6 +64,7 @@ namespace tools
//-------------------------------------------------------------------------------------
bool have_tx();
MDB_txn* get_current_tx();
bool resize_if_needed();
};
}

View file

@ -36,7 +36,7 @@ namespace currency
//------------------------------------------------------------------
int ethash_height_to_epoch(uint64_t height)
{
return height / ETHASH_EPOCH_LENGTH;
return static_cast<int>(height / ETHASH_EPOCH_LENGTH);
}
//--------------------------------------------------------------
crypto::hash ethash_epoch_to_seed(int epoch)
@ -51,7 +51,7 @@ namespace currency
{
int epoch = ethash_height_to_epoch(height);
const auto& context = progpow::get_global_epoch_context_full(static_cast<int>(epoch));
auto res_eth = progpow::hash(context, height, *(ethash::hash256*)&block_header_hash, nonce);
auto res_eth = progpow::hash(context, static_cast<int>(height), *(ethash::hash256*)&block_header_hash, nonce);
crypto::hash result = currency::null_hash;
memcpy(&result.data, &res_eth.final_hash, sizeof(res_eth.final_hash));
return result;

View file

@ -58,7 +58,7 @@ using namespace currency;
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_CURRENT_BLOCK_CUMUL_SZ_LIMIT 0
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_CURRENT_PRUNED_RS_HEIGHT 1
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_LAST_WORKED_VERSION 2
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPATIBILITY_VERSION 3 //mismatch here means full resync
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MAJOR_COMPATIBILITY_VERSION 3 //DON'T CHANGE THIS, if you need to resync db change BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION
#define BLOCKCHAIN_STORAGE_OPTIONS_ID_STORAGE_MINOR_COMPATIBILITY_VERSION 4 //mismatch here means some reinitializations
#define TARGETDATA_CACHE_SIZE DIFFICULTY_WINDOW + 10
@ -68,6 +68,9 @@ using namespace currency;
#else
#define BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION 18000
#endif
#define BLOCK_POS_STRICT_SEQUENCE_LIMIT 20
DISABLE_VS_WARNINGS(4267)
@ -106,7 +109,9 @@ blockchain_storage::blockchain_storage(tx_memory_pool& tx_pool) :m_db(std::share
m_current_fee_median(0),
m_current_fee_median_effective_index(0),
m_is_reorganize_in_process(false),
m_deinit_is_done(false)
m_deinit_is_done(false),
m_cached_next_pow_difficulty(0),
m_cached_next_pos_difficulty(0)
{
@ -357,6 +362,16 @@ bool blockchain_storage::set_lost_tx_unmixable()
//------------------------------------------------------------------
void blockchain_storage::patch_out_if_needed(txout_to_key& out, const crypto::hash& tx_id, uint64_t n) const
{
static crypto::hash tx_id_1 = epee::string_tools::parse_tpod_from_hex_string<crypto::hash>("c2a2229d614e7c026433efbcfdbd0be1f68d9b419220336df3e2c209f5d57314");
static crypto::hash tx_id_2 = epee::string_tools::parse_tpod_from_hex_string<crypto::hash>("647f936c6ffbd136f5c95d9a90ad554bdb4c01541c6eb5755ad40b984d80da67");
if (tx_id == tx_id_1 && n == 12)
{
out.mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX;
}else if(tx_id == tx_id_2 && n == 5)
{
out.mix_attr = CURRENCY_TO_KEY_OUT_FORCED_NO_MIX;
}
}
//------------------------------------------------------------------
void blockchain_storage::initialize_db_solo_options_values()
@ -967,12 +982,21 @@ wide_difficulty_type blockchain_storage::get_next_diff_conditional(bool pos) con
wide_difficulty_type& dif = pos ? m_cached_next_pos_difficulty : m_cached_next_pow_difficulty;
TIME_MEASURE_FINISH_PD(target_calculating_enum_blocks);
TIME_MEASURE_START_PD(target_calculating_calc);
dif = next_difficulty(timestamps, commulative_difficulties, pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET);
if (m_db_blocks.size() > m_core_runtime_config.hard_fork1_starts_after_height)
{
dif = next_difficulty_2(timestamps, commulative_difficulties, pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET);
}
else
{
dif = next_difficulty_1(timestamps, commulative_difficulties, pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET);
}
TIME_MEASURE_FINISH_PD(target_calculating_calc);
return dif;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage::get_next_diff_conditional2(bool pos, const alt_chain_type& alt_chain, uint64_t split_height) const
wide_difficulty_type blockchain_storage::get_next_diff_conditional2(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
std::vector<uint64_t> timestamps;
@ -995,7 +1019,13 @@ wide_difficulty_type blockchain_storage::get_next_diff_conditional2(bool pos, co
return true;
};
enum_blockchain(cb, alt_chain, split_height);
return next_difficulty(timestamps, commulative_difficulties, pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET);
wide_difficulty_type diff = 0;
if(abei.height > m_core_runtime_config.hard_fork1_starts_after_height)
diff = next_difficulty_2(timestamps, commulative_difficulties, pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET);
else
diff = next_difficulty_1(timestamps, commulative_difficulties, pos ? DIFFICULTY_POS_TARGET : DIFFICULTY_POW_TARGET);
return diff;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage::get_cached_next_difficulty(bool pos) const
@ -1054,7 +1084,7 @@ wide_difficulty_type blockchain_storage::get_next_difficulty_for_alternative_cha
commulative_difficulties.push_back(m_db_blocks[i]->cumulative_diff_precise);
}
return next_difficulty(timestamps, commulative_difficulties, pos ? DIFFICULTY_POS_TARGET:DIFFICULTY_POW_TARGET);
return next_difficulty_1(timestamps, commulative_difficulties, pos ? DIFFICULTY_POS_TARGET:DIFFICULTY_POW_TARGET);
}
//------------------------------------------------------------------
bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t height, bool pos) const
@ -1071,9 +1101,29 @@ bool blockchain_storage::prevalidate_miner_transaction(const block& b, uint64_t
CHECK_AND_ASSERT_MES(b.miner_tx.vin[1].type() == typeid(txin_to_key), false, "coinstake transaction in the block has the wrong type");
}
CHECK_AND_ASSERT_MES(get_tx_unlock_time(b.miner_tx) == height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW,
false,
"coinbase transaction has wrong unlock time: " << get_tx_unlock_time(b.miner_tx) << ", expected: " << height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
if (height > m_core_runtime_config.hard_fork1_starts_after_height)
{
// new rules that allow different unlock time in coinbase outputs
uint64_t max_unlock_time = 0;
uint64_t min_unlock_time = 0;
bool r = get_tx_max_min_unlock_time(b.miner_tx, max_unlock_time, min_unlock_time);
CHECK_AND_ASSERT_MES(r && min_unlock_time >= height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW,
false,
"coinbase transaction has wrong min_unlock_time: " << min_unlock_time << ", expected: " << height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
}
else
{
//------------------------------------------------------------------
//bool blockchain_storage::
// pre-hard fork rules that don't allow different unlock time in coinbase outputs
uint64_t max_unlock_time = 0;
uint64_t min_unlock_time = 0;
bool r = get_tx_max_min_unlock_time(b.miner_tx, max_unlock_time, min_unlock_time);
CHECK_AND_ASSERT_MES(r && max_unlock_time == min_unlock_time && min_unlock_time == height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW,
false,
"coinbase transaction has wrong min_unlock_time: " << min_unlock_time << ", expected: " << height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
}
//check outs overflow
if(!check_outs_overflow(b.miner_tx))
@ -1192,7 +1242,12 @@ bool blockchain_storage::create_block_template(block& b,
size_t median_size;
boost::multiprecision::uint128_t already_generated_coins;
CRITICAL_REGION_BEGIN(m_read_lock);
b.major_version = CURRENT_BLOCK_MAJOR_VERSION;
height = m_db_blocks.size();
if(height <= m_core_runtime_config.hard_fork1_starts_after_height)
b.major_version = BLOCK_MAJOR_VERSION_INITAL;
else
b.major_version = CURRENT_BLOCK_MAJOR_VERSION;
b.minor_version = CURRENT_BLOCK_MINOR_VERSION;
b.prev_id = get_top_block_id();
b.timestamp = m_core_runtime_config.get_core_time();
@ -1209,7 +1264,7 @@ bool blockchain_storage::create_block_template(block& b,
CHECK_AND_ASSERT_MES(diffic, false, "difficulty owverhead.");
height = m_db_blocks.size();
median_size = m_db_current_block_cumul_sz_limit / 2;
already_generated_coins = m_db_blocks.back()->already_generated_coins;
@ -1470,6 +1525,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
alt_block_extended_info abei = AUTO_VAL_INIT(abei);
abei.bl = b;
abei.timestamp = m_core_runtime_config.get_core_time();
abei.height = alt_chain.size() ? it_prev->second.height + 1 : *ptr_main_prev + 1;
CHECK_AND_ASSERT_MES_CUSTOM(coinbase_height == abei.height, false, bvc.m_verification_failed = true, "block coinbase height doesn't match with altchain height, declined");
uint64_t connection_height = alt_chain.size() ? alt_chain.front()->second.height:abei.height;
@ -1495,8 +1551,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
CHECK_AND_ASSERT_MES_CUSTOM(!(pos_block && abei.height < m_core_runtime_config.pos_minimum_heigh), false, bvc.m_verification_failed = true, "PoS block is not allowed on this height");
//wide_difficulty_type current_diff = get_next_difficulty_for_alternative_chain(alt_chain, bei, pos_block);
wide_difficulty_type current_diff = get_next_diff_conditional2(pos_block, alt_chain, connection_height);
wide_difficulty_type current_diff = get_next_diff_conditional2(pos_block, alt_chain, connection_height, abei);
CHECK_AND_ASSERT_MES_CUSTOM(current_diff, false, bvc.m_verification_failed = true, "!!!!!!! DIFFICULTY OVERHEAD !!!!!!!");
@ -1555,7 +1610,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
if (abei.height >= m_core_runtime_config.pos_minimum_heigh)
cumulative_diff_delta = correct_difficulty_with_sequence_factor(sequence_factor, cumulative_diff_delta);
if (abei.height > BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION && pos_block && sequence_factor > 20)
if (abei.height > BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION && pos_block && sequence_factor > BLOCK_POS_STRICT_SEQUENCE_LIMIT)
{
LOG_PRINT_RED_L0("Alternative block " << id << " @ " << abei.height << " has too big sequence factor: " << sequence_factor << ", rejected");
bvc.m_verification_failed = true;
@ -1563,9 +1618,15 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
}
abei.cumulative_diff_adjusted += cumulative_diff_delta;
abei.cumulative_diff_precise = get_last_alt_x_block_cumulative_precise_difficulty(alt_chain, abei.height, pos_block);
wide_difficulty_type last_x_cumul_dif_precise_adj = 0;
abei.cumulative_diff_precise = get_last_alt_x_block_cumulative_precise_difficulty(alt_chain, abei.height-1, pos_block, last_x_cumul_dif_precise_adj);
abei.cumulative_diff_precise += current_diff;
//////////////////////////////////////////////////////////////////////////
wide_difficulty_type diff_precise_adj = correct_difficulty_with_sequence_factor(sequence_factor, current_diff);
abei.cumulative_diff_precise_adjusted = last_x_cumul_dif_precise_adj + diff_precise_adj;
//////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
auto i_dres = m_alternative_chains.find(id);
@ -1600,7 +1661,7 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
<< ENDL << "HEIGHT " << abei.height << ", difficulty: " << abei.difficulty << ", cumul_diff_precise: " << abei.cumulative_diff_precise << ", cumul_diff_adj: " << abei.cumulative_diff_adjusted << " (current mainchain cumul_diff_adj: " << m_db_blocks.back()->cumulative_diff_adjusted << ", ki lookup total: " << ki_lookup_total <<")"
, LOG_LEVEL_0);
if (is_reorganize_required(*m_db_blocks.back(), abei, proof))
if (is_reorganize_required(*m_db_blocks.back(), alt_chain, proof))
{
auto a = epee::misc_utils::create_scope_leave_handler([&]() { m_is_reorganize_in_process = false; });
CHECK_AND_ASSERT_THROW_MES(!m_is_reorganize_in_process, "Detected recursive reorganzie");
@ -1616,6 +1677,11 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
return r;
}
bvc.added_to_altchain = true;
//protect ourself from altchains container flood
if (m_alternative_chains.size() > m_core_runtime_config.max_alt_blocks)
prune_aged_alt_blocks();
return true;
}else
{
@ -1641,26 +1707,111 @@ bool blockchain_storage::handle_alternative_block(const block& b, const crypto::
CATCH_ENTRY_CUSTOM("blockchain_storage::handle_alternative_block", bvc.m_verification_failed = true, false);
}
//------------------------------------------------------------------
bool blockchain_storage::is_reorganize_required(const block_extended_info& main_chain_bei, const block_extended_info& alt_chain_bei, const crypto::hash& proof_alt)
wide_difficulty_type blockchain_storage::get_x_difficulty_after_height(uint64_t height, bool is_pos)
{
if (main_chain_bei.cumulative_diff_adjusted < alt_chain_bei.cumulative_diff_adjusted)
return true;
else if (main_chain_bei.cumulative_diff_adjusted > alt_chain_bei.cumulative_diff_adjusted)
return false;
else // main_chain_bei.cumulative_diff_adjusted == alt_chain_bei.cumulative_diff_adjusted
CRITICAL_REGION_LOCAL(m_read_lock);
CHECK_AND_ASSERT_THROW_MES(height < m_db_blocks.size(), "Internal error: condition failed: height (" << height << ") < m_db_blocks.size() " << m_db_blocks.size());
wide_difficulty_type diff = 0;
for (uint64_t i = height + 1; i != m_db_blocks.size(); i++)
{
if (!is_pos_block(main_chain_bei.bl))
return false; // do not reorganize on the same cummul diff if it's a PoW block
auto bei_ptr = m_db_blocks[i];
if (is_pos_block(bei_ptr->bl) == is_pos)
{
diff = bei_ptr->difficulty;
break;
}
}
if (diff == 0)
{
//never met x type of block, that meanst that difficulty is current
diff = get_cached_next_difficulty(is_pos);
}
return diff;
}
//------------------------------------------------------------------
bool blockchain_storage::is_reorganize_required(const block_extended_info& main_chain_bei, const alt_chain_type& alt_chain, const crypto::hash& proof_alt)
{
//alt_chain - back is latest(top), first - connection with main chain
const block_extended_info& alt_chain_bei = alt_chain.back()->second;
const block_extended_info& connection_point = alt_chain.front()->second;
//in case of simultaneous PoS blocks are happened on the same height (quite common for PoS)
//we also try to weight them to guarantee consensus in network
if (std::memcmp(&main_chain_bei.stake_hash, &proof_alt, sizeof(main_chain_bei.stake_hash)) >= 0)
if (connection_point.height <= m_core_runtime_config.hard_fork1_starts_after_height)
{
//use pre-hard fork, old-style comparing
if (main_chain_bei.cumulative_diff_adjusted < alt_chain_bei.cumulative_diff_adjusted)
return true;
else if (main_chain_bei.cumulative_diff_adjusted > alt_chain_bei.cumulative_diff_adjusted)
return false;
else // main_chain_bei.cumulative_diff_adjusted == alt_chain_bei.cumulative_diff_adjusted
{
if (!is_pos_block(main_chain_bei.bl))
return false; // do not reorganize on the same cummul diff if it's a PoW block
//in case of simultaneous PoS blocks are happened on the same height (quite common for PoS)
//we also try to weight them to guarantee consensus in network
if (std::memcmp(&main_chain_bei.stake_hash, &proof_alt, sizeof(main_chain_bei.stake_hash)) >= 0)
return false;
LOG_PRINT_L2("[is_reorganize_required]:TRUE, \"by order of memcmp\" main_stake_hash:" << &main_chain_bei.stake_hash << ", alt_stake_hash" << proof_alt);
return true;
}
}
else if (alt_chain_bei.height > m_core_runtime_config.hard_fork1_starts_after_height)
{
//new rules, applied after HARD_FORK_1
//to learn this algo please read https://github.com/hyle-team/docs/blob/master/zano/PoS_Analysis_and_improvements_proposal.pdf
wide_difficulty_type difficulty_pos_at_split_point = get_x_difficulty_after_height(connection_point.height - 1, true);
wide_difficulty_type difficulty_pow_at_split_point = get_x_difficulty_after_height(connection_point.height - 1, false);
difficulties main_cumul_diff = AUTO_VAL_INIT(main_cumul_diff);
difficulties alt_cumul_diff = AUTO_VAL_INIT(alt_cumul_diff);
//we use get_last_alt_x_block_cumulative_precise_adj_difficulty for getting both alt chain and main chain diff of given block types
wide_difficulty_type alt_pos_diff_end = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain, alt_chain_bei.height, true);
wide_difficulty_type alt_pos_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain_type(), connection_point.height-1, true);
alt_cumul_diff.pos_diff = alt_pos_diff_end - alt_pos_diff_begin;
LOG_PRINT_L2("[is_reorganize_required]:TRUE, \"by order of memcmp\" main_stake_hash:" << &main_chain_bei.stake_hash << ", alt_stake_hash" << proof_alt);
return true;
wide_difficulty_type alt_pow_diff_end = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain, alt_chain_bei.height, false);
wide_difficulty_type alt_pow_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain_type(), connection_point.height - 1, false);
alt_cumul_diff.pow_diff = alt_pow_diff_end - alt_pow_diff_begin;
wide_difficulty_type main_pos_diff_end = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain_type(), m_db_blocks.size()-1, true);
wide_difficulty_type main_pos_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain_type(), connection_point.height - 1, true);
main_cumul_diff.pos_diff = main_pos_diff_end - main_pos_diff_begin;
wide_difficulty_type main_pow_diff_end = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain_type(), m_db_blocks.size() - 1, false);
wide_difficulty_type main_pow_diff_begin = get_last_alt_x_block_cumulative_precise_adj_difficulty(alt_chain_type(), connection_point.height - 1, false);
main_cumul_diff.pow_diff = main_pow_diff_end - main_pow_diff_begin;
//TODO: measurment of precise cumulative difficult
wide_difficulty_type alt = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, alt_cumul_diff, main_cumul_diff);
wide_difficulty_type main = get_a_to_b_relative_cumulative_difficulty(difficulty_pos_at_split_point, difficulty_pow_at_split_point, main_cumul_diff, alt_cumul_diff);
if (main < alt)
return true;
else if (main > alt)
return false;
else
{
if (!is_pos_block(main_chain_bei.bl))
return false; // do not reorganize on the same cummul diff if it's a PoW block
//in case of simultaneous PoS blocks are happened on the same height (quite common for PoS)
//we also try to weight them to guarantee consensus in network
if (std::memcmp(&main_chain_bei.stake_hash, &proof_alt, sizeof(main_chain_bei.stake_hash)) >= 0)
return false;
LOG_PRINT_L2("[is_reorganize_required]:TRUE, \"by order of memcmp\" main_stake_hash:" << &main_chain_bei.stake_hash << ", alt_stake_hash" << proof_alt);
return true;
}
}
else
{
ASSERT_MES_AND_THROW("Unknown version of block");
}
}
//------------------------------------------------------------------
bool blockchain_storage::pre_validate_relayed_block(block& bl, block_verification_context& bvc, const crypto::hash& id)const
{
@ -2101,7 +2252,7 @@ bool blockchain_storage::add_out_to_get_random_outs(COMMAND_RPC_GET_RANDOM_OUTPU
return false;
//check if transaction is unlocked
if (!is_tx_spendtime_unlocked(get_tx_unlock_time(tx)))
if (!is_tx_spendtime_unlocked(get_tx_unlock_time(tx, out_ptr->out_no)))
return false;
//use appropriate mix_attr out
@ -2364,7 +2515,7 @@ bool blockchain_storage::forecast_difficulty(std::vector<std::pair<uint64_t, wid
out_height_2_diff_vector.push_back(std::make_pair(height, last_block_diff_for_this_type)); // the first element corresponds to the last block of this type
for (size_t i = 0; i < DIFFICULTY_CUT; ++i)
{
wide_difficulty_type diff = next_difficulty(timestamps, cumulative_difficulties, target_seconds);
wide_difficulty_type diff = next_difficulty_1(timestamps, cumulative_difficulties, target_seconds);
height += avg_interval;
out_height_2_diff_vector.push_back(std::make_pair(height, diff));
@ -2524,6 +2675,46 @@ void blockchain_storage::print_db_cache_perfeormance_data() const
);
}
//------------------------------------------------------------------
void blockchain_storage::get_last_n_x_blocks(uint64_t n, bool pos_blocks, std::list<std::shared_ptr<const block_extended_info>>& blocks) const
{
uint64_t count = 0;
bool looking_for_a_pos = true;
for (uint64_t i = m_db_blocks.size() - 1; i != 0; --i)
{
auto block_ptr = m_db_blocks[i];
if (is_pos_block(block_ptr->bl) == pos_blocks)
{
blocks.push_back(block_ptr);
++count;
if (count >= n)
break;
}
}
}
//------------------------------------------------------------------
void blockchain_storage::print_last_n_difficulty_numbers(uint64_t n) const
{
std::stringstream ss;
std::list<std::shared_ptr<const block_extended_info>> pos_blocks;
std::list<std::shared_ptr<const block_extended_info>> pow_blocks;
get_last_n_x_blocks(n, true, pos_blocks);
get_last_n_x_blocks(n, false, pow_blocks);
ss << "PoS blocks difficulty:" << ENDL;
for (auto& bl_ptr : pos_blocks)
{
ss << bl_ptr->difficulty << ENDL;
}
ss << "PoW blocks difficulty:" << ENDL;
for (auto& bl_ptr : pow_blocks)
{
ss << bl_ptr->difficulty << ENDL;
}
LOG_PRINT_L0("LAST BLOCKS:" << ss.str());
}
//------------------------------------------------------------------
void blockchain_storage::print_blockchain_outs_stat() const
{
LOG_ERROR("NOT IMPLEMENTED YET");
@ -3268,6 +3459,8 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const
TIME_MEASURE_START_PD(tx_append_is_expired);
CHECK_AND_ASSERT_MES(!is_tx_expired(tx), false, "Transaction can't be added to the blockchain since it's already expired. tx expiration time: " << get_tx_expiration_time(tx) << ", blockchain median time: " << get_tx_expiration_median());
TIME_MEASURE_FINISH_PD_COND(need_to_profile, tx_append_is_expired);
CHECK_AND_ASSERT_MES(validate_tx_for_hardfork_specific_terms(tx, tx_id, bl_height), false, "tx " << tx_id << ": hardfork-specific validation failed");
TIME_MEASURE_START_PD(tx_process_extra);
bool r = process_blockchain_tx_extra(tx);
@ -3280,7 +3473,7 @@ bool blockchain_storage::add_transaction_from_block(const transaction& tx, const
TIME_MEASURE_START_PD(tx_process_inputs);
BOOST_FOREACH(const txin_v& in, tx.vin)
for(const txin_v& in : tx.vin)
{
if(!boost::apply_visitor(add_transaction_input_visitor(*this, m_db_spent_keys, tx_id, bl_id, bl_height), in))
{
@ -3360,7 +3553,7 @@ bool blockchain_storage::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::
bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height, crypto::hash& max_used_block_id) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
bool res = check_tx_inputs(tx, tx_prefix_hash, &max_used_block_height);
bool res = check_tx_inputs(tx, tx_prefix_hash, max_used_block_height);
if(!res) return false;
CHECK_AND_ASSERT_MES(max_used_block_height < m_db_blocks.size(), false, "internal error: max used block index=" << max_used_block_height << " is not less then blockchain size = " << m_db_blocks.size());
get_block_hash(m_db_blocks[max_used_block_height]->bl, max_used_block_id);
@ -3604,19 +3797,16 @@ bool blockchain_storage::have_tx_keyimges_as_spent(const transaction &tx) const
return false;
}
//------------------------------------------------------------------
// bool blockchain_storage::check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height) const
// {
// TIME_MEASURE_START_PD(tx_check_inputs_prefix_hash);
// crypto::hash tx_prefix_hash = get_transaction_prefix_hash(tx);
// TIME_MEASURE_FINISH_PD(tx_check_inputs_prefix_hash);
// return check_tx_inputs(tx, tx_prefix_hash, pmax_used_block_height);
// }
bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash) const
{
uint64_t stub = 0;
return check_tx_inputs(tx, tx_prefix_hash, stub);
}
//------------------------------------------------------------------
bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height) const
bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height) const
{
size_t sig_index = 0;
if(pmax_used_block_height)
*pmax_used_block_height = 0;
max_used_block_height = 0;
std::vector<crypto::signature> sig_stub;
const std::vector<crypto::signature>* psig = &sig_stub;
@ -3642,7 +3832,8 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha
return false;
}
TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_kimage_check);
if (!check_tx_input(tx, sig_index, in_to_key, tx_prefix_hash, *psig, pmax_used_block_height))
uint64_t max_unlock_time = 0;
if (!check_tx_input(tx, sig_index, in_to_key, tx_prefix_hash, *psig, max_used_block_height, max_unlock_time))
{
LOG_ERROR("Failed to validate input #" << sig_index << " tx: " << tx_prefix_hash);
return false;
@ -3651,7 +3842,7 @@ bool blockchain_storage::check_tx_inputs(const transaction& tx, const crypto::ha
else if (txin.type() == typeid(txin_multisig))
{
const txin_multisig& in_ms = boost::get<txin_multisig>(txin);
if (!check_tx_input(tx, sig_index, in_ms, tx_prefix_hash, *psig, pmax_used_block_height))
if (!check_tx_input(tx, sig_index, in_ms, tx_prefix_hash, *psig, max_used_block_height))
{
LOG_ERROR("Failed to validate multisig input #" << sig_index << " (ms out id: " << in_ms.multisig_out_id << ") in tx: " << tx_prefix_hash);
return false;
@ -3682,14 +3873,14 @@ bool blockchain_storage::is_tx_spendtime_unlocked(uint64_t unlock_time) const
}
//------------------------------------------------------------------
bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height) const
bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t& max_related_block_height, uint64_t& max_unlock_time) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
//TIME_MEASURE_START_PD(tx_check_inputs_loop_ch_in_get_keys_loop);
std::vector<crypto::public_key> output_keys;
if(!get_output_keys_for_input_with_checks(txin, output_keys, pmax_related_block_height))
if(!get_output_keys_for_input_with_checks(tx, txin, output_keys, max_related_block_height, max_unlock_time))
{
LOG_PRINT_L0("Failed to get output keys for input #" << in_index << " (amount = " << print_money(txin.amount) << ", key_offset.size = " << txin.key_offsets.size() << ")");
return false;
@ -3708,7 +3899,7 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index,
// 1) source tx unlock time validity
// 2) mixin restrictions
// 3) general gindex/ref_by_id corectness
bool blockchain_storage::get_output_keys_for_input_with_checks(const txin_to_key& txin, std::vector<crypto::public_key>& output_keys, uint64_t* pmax_related_block_height /* = NULL */) const
bool blockchain_storage::get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector<crypto::public_key>& output_keys, uint64_t& max_related_block_height, uint64_t& max_unlock_time) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
@ -3716,17 +3907,30 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const txin_to_key
{
std::vector<crypto::public_key >& m_results_collector;
const blockchain_storage& m_bch;
uint64_t& m_max_unlock_time;
outputs_visitor(std::vector<crypto::public_key>& results_collector,
const blockchain_storage& bch) :m_results_collector(results_collector), m_bch(bch)
const blockchain_storage& bch,
uint64_t& max_unlock_time) :m_results_collector(results_collector), m_bch(bch), m_max_unlock_time(max_unlock_time)
{}
bool handle_output(const transaction& tx, const tx_out& out)
bool handle_output(const transaction& source_tx, const transaction& validated_tx, const tx_out& out, uint64_t out_i)
{
//check tx unlock time
if (!m_bch.is_tx_spendtime_unlocked(get_tx_unlock_time(tx)))
uint64_t source_out_unlock_time = get_tx_unlock_time(source_tx, out_i);
//let coinbase sources for PoS block to have locked inputs, the outputs supposed to be locked same way, except the reward
if (is_coinbase(validated_tx) && is_pos_block(validated_tx))
{
LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << get_tx_unlock_time(tx));
return false;
if (source_out_unlock_time > m_max_unlock_time)
m_max_unlock_time = source_out_unlock_time;
}
else
{
if (!m_bch.is_tx_spendtime_unlocked(source_out_unlock_time))
{
LOG_PRINT_L0("One of outputs for one of inputs have wrong tx.unlock_time = " << get_tx_unlock_time(source_tx, out_i));
return false;
}
}
if(out.target.type() != typeid(txout_to_key))
{
@ -3739,8 +3943,8 @@ bool blockchain_storage::get_output_keys_for_input_with_checks(const txin_to_key
}
};
outputs_visitor vi(output_keys, *this);
return scan_outputkeys_for_indexes(txin, vi, pmax_related_block_height);
outputs_visitor vi(output_keys, *this, max_unlock_time);
return scan_outputkeys_for_indexes(tx, txin, vi, max_related_block_height);
}
//------------------------------------------------------------------
// Note: this function can be used for checking to_key inputs against either main chain or alt chain, that's why it has output_keys_ptrs parameter
@ -3813,7 +4017,7 @@ bool blockchain_storage::check_ms_input(const transaction& tx, size_t in_index,
#define LOC_CHK(cond, msg) CHECK_AND_ASSERT_MES(cond, false, "ms input check failed: ms_id: " << txin.multisig_out_id << ", input #" << in_index << " in tx " << tx_prefix_hash << ", refers to ms output #" << out_n << " in source tx " << get_transaction_hash(source_tx) << ENDL << msg)
CRITICAL_REGION_LOCAL(m_read_lock);
uint64_t unlock_time = get_tx_unlock_time(source_tx);
uint64_t unlock_time = get_tx_unlock_time(source_tx, out_n);
LOC_CHK(is_tx_spendtime_unlocked(unlock_time), "Source transaction is LOCKED! unlock_time: " << unlock_time << ", now is " << m_core_runtime_config.get_core_time() << ", blockchain size is " << get_current_blockchain_size());
LOC_CHK(source_tx.vout.size() > out_n, "internal error: out_n==" << out_n << " is out-of-bounds of source_tx.vout, size=" << source_tx.vout.size());
@ -3893,7 +4097,7 @@ bool blockchain_storage::check_ms_input(const transaction& tx, size_t in_index,
}
//------------------------------------------------------------------
bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height) const
bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t& max_related_block_height) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
@ -3917,8 +4121,7 @@ bool blockchain_storage::check_tx_input(const transaction& tx, size_t in_index,
if (!check_ms_input(tx, in_index, txin, tx_prefix_hash, sig, source_tx_ptr->tx, n))
return false;
if (pmax_related_block_height != nullptr)
*pmax_related_block_height = source_tx_ptr->m_keeper_block_height;
max_related_block_height = source_tx_ptr->m_keeper_block_height;
return true;
#undef LOC_CHK
@ -4066,6 +4269,12 @@ bool blockchain_storage::prune_aged_alt_blocks()
CRITICAL_REGION_LOCAL1(m_alternative_chains_lock);
uint64_t current_height = get_current_blockchain_size();
size_t count_to_delete = 0;
if(m_alternative_chains.size() > m_core_runtime_config.max_alt_blocks)
count_to_delete = m_alternative_chains.size() - m_core_runtime_config.max_alt_blocks;
std::map<uint64_t, alt_chain_container::iterator> alts_to_delete;
for(auto it = m_alternative_chains.begin(); it != m_alternative_chains.end();)
{
if (current_height > it->second.height && current_height - it->second.height > CURRENCY_ALT_BLOCK_LIVETIME_COUNT)
@ -4074,9 +4283,28 @@ bool blockchain_storage::prune_aged_alt_blocks()
}
else
{
if (count_to_delete)
{
if (!alts_to_delete.size())
alts_to_delete[it->second.timestamp] = it;
else
{
if (it->second.timestamp >= alts_to_delete.rbegin()->first)
alts_to_delete[it->second.timestamp] = it;
if (alts_to_delete.size() > count_to_delete)
alts_to_delete.erase(alts_to_delete.begin());
}
}
++it;
}
}
//now, if there was count_to_delete we should erase most oldest entries of altblocks
for (auto& itd : alts_to_delete)
{
m_alternative_chains.erase(itd.second);
}
return true;
}
@ -4156,6 +4384,58 @@ void blockchain_storage::get_pos_mining_estimate(uint64_t amount_coins,
estimate_result = current_amount;
}
//------------------------------------------------------------------
bool blockchain_storage::validate_tx_for_hardfork_specific_terms(const transaction& tx, const crypto::hash& tx_id, uint64_t block_height) const
{
if (block_height <= m_core_runtime_config.hard_fork1_starts_after_height)
{
// before hardfork 1
for (const auto& el : tx.extra)
{
// etc_tx_details_unlock_time2 is not allowed in txs in blocks prior to hardfork 1
CHECK_AND_ASSERT_MES(el.type() != typeid(etc_tx_details_unlock_time2), false, "tx " << tx_id << " contains etc_tx_details_unlock_time2 which is not allowed on height " << block_height);
}
return true;
}
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t max_unlock_time)const
{
uint64_t major_unlock_time = get_tx_x_detail<etc_tx_details_unlock_time>(miner_tx);
if (major_unlock_time)
{
//if there was etc_tx_details_unlock_time present in tx, then ignore etc_tx_details_unlock_time2
if (major_unlock_time < max_unlock_time)
return false;
else
return true;
}
CHECK_AND_ASSERT_MES(get_block_height(miner_tx) > m_core_runtime_config.hard_fork1_starts_after_height, false, "error in block [" << get_block_height(miner_tx) << "] etc_tx_details_unlock_time2 can exist only after hard fork point : " << m_core_runtime_config.hard_fork1_starts_after_height);
//etc_tx_details_unlock_time2 can be kept only after hard_fork_1 point
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
get_type_in_variant_container(miner_tx.extra, ut2);
CHECK_AND_ASSERT_MES(ut2.unlock_time_array.size() == miner_tx.vout.size(), false, "ut2.unlock_time_array.size()<" << ut2.unlock_time_array.size()
<< "> != miner_tx.vout.size()<" << miner_tx.vout.size() << ">");
uint64_t amount_of_coins_in_unlock_in_range = 0;
for (uint64_t i = 0; i != miner_tx.vout.size(); i++)
{
if (ut2.unlock_time_array[i] >= max_unlock_time)
amount_of_coins_in_unlock_in_range += miner_tx.vout[i].amount;
}
if (amount_of_coins_in_unlock_in_range >= staked_amount)
return true;
LOG_ERROR("amount_of_coins_in_unlock_in_range<" << amount_of_coins_in_unlock_in_range << "> is less then staked_amount<" << staked_amount);
return false;
}
//------------------------------------------------------------------
bool blockchain_storage::validate_pos_block(const block& b,
wide_difficulty_type basic_diff,
uint64_t& amount,
@ -4185,11 +4465,11 @@ bool blockchain_storage::validate_pos_block(const block& b,
//check actual time if it there
uint64_t actual_ts = get_actual_timestamp(b);
if ((actual_ts > b.timestamp && actual_ts - b.timestamp > POS_MAC_ACTUAL_TIMESTAMP_TO_MINED) ||
(actual_ts < b.timestamp && b.timestamp - actual_ts > POS_MAC_ACTUAL_TIMESTAMP_TO_MINED)
if ((actual_ts > b.timestamp && actual_ts - b.timestamp > POS_MAX_ACTUAL_TIMESTAMP_TO_MINED) ||
(actual_ts < b.timestamp && b.timestamp - actual_ts > POS_MAX_ACTUAL_TIMESTAMP_TO_MINED)
)
{
LOG_PRINT_L0("PoS block actual timestamp " << actual_ts << " differs from b.timestamp " << b.timestamp << " by " << ((int64_t)actual_ts - (int64_t)b.timestamp) << " s, it's more than allowed " << POS_MAC_ACTUAL_TIMESTAMP_TO_MINED << " s.");
LOG_PRINT_L0("PoS block actual timestamp " << actual_ts << " differs from b.timestamp " << b.timestamp << " by " << ((int64_t)actual_ts - (int64_t)b.timestamp) << " s, it's more than allowed " << POS_MAX_ACTUAL_TIMESTAMP_TO_MINED << " s.");
return false;
}
@ -4232,8 +4512,23 @@ bool blockchain_storage::validate_pos_block(const block& b,
{
// Do coinstake input validation for main chain only.
// Txs in alternative PoS blocks (including miner_tx) are validated by validate_alt_block_txs()
r = check_tx_input(b.miner_tx, 1, coinstake_in, id, b.miner_tx.signatures[0], &max_related_block_height);
uint64_t max_unlock_time = 0;
r = check_tx_input(b.miner_tx, 1, coinstake_in, id, b.miner_tx.signatures[0], max_related_block_height, max_unlock_time);
CHECK_AND_ASSERT_MES(r, false, "Failed to validate coinstake input in miner tx, block_id = " << get_block_hash(b));
if (get_block_height(b) > m_core_runtime_config.hard_fork1_starts_after_height)
{
uint64_t last_pow_h = get_last_x_block_height(false);
CHECK_AND_ASSERT_MES(max_related_block_height <= last_pow_h, false, "Failed to failed to validate coinbase in pos block, condition failed: max_related_block_height(" << max_related_block_height << ") < last_pow_h(" << last_pow_h << ")");
//let's check that coinbase amount and unlock time
r = validate_pos_coinbase_outs_unlock_time(b.miner_tx, coinstake_in.amount, max_unlock_time);
CHECK_AND_ASSERT_MES(r, false, "Failed to validate_pos_coinbase_outs_unlock_time() in miner tx, block_id = " << get_block_hash(b)
<< "max_unlock_time=" << max_unlock_time);
}
else
{
CHECK_AND_ASSERT_MES(is_tx_spendtime_unlocked(max_unlock_time), false, "Failed to failed to validate coinbase in pos block, condition failed: is_tx_spendtime_unlocked(max_unlock_time)(" << max_unlock_time << ")");
}
}
uint64_t block_height = for_altchain ? split_height + alt_chain.size() : m_db_blocks.size();
@ -4341,13 +4636,23 @@ uint64_t blockchain_storage::get_last_x_block_height(bool pos) const
return 0;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage::get_last_alt_x_block_cumulative_precise_difficulty(alt_chain_type& alt_chain, uint64_t block_height, bool pos) const
wide_difficulty_type blockchain_storage::get_last_alt_x_block_cumulative_precise_adj_difficulty(const alt_chain_type& alt_chain, uint64_t block_height, bool pos) const
{
uint64_t main_chain_first_block = block_height - 1;
wide_difficulty_type res = 0;
get_last_alt_x_block_cumulative_precise_difficulty(alt_chain, block_height, pos, res);
return res;
}
//------------------------------------------------------------------
wide_difficulty_type blockchain_storage::get_last_alt_x_block_cumulative_precise_difficulty(const alt_chain_type& alt_chain, uint64_t block_height, bool pos, wide_difficulty_type& cumulative_diff_precise_adj) const
{
uint64_t main_chain_first_block = block_height;
for (auto it = alt_chain.rbegin(); it != alt_chain.rend(); it++)
{
if (is_pos_block((*it)->second.bl) == pos)
{
cumulative_diff_precise_adj = (*it)->second.cumulative_diff_precise_adjusted;
return (*it)->second.cumulative_diff_precise;
}
main_chain_first_block = (*it)->second.height - 1;
}
@ -4358,8 +4663,12 @@ wide_difficulty_type blockchain_storage::get_last_alt_x_block_cumulative_precise
for (uint64_t i = main_chain_first_block; i != 0; i--)
{
if (is_pos_block(m_db_blocks[i]->bl) == pos)
{
cumulative_diff_precise_adj = m_db_blocks[i]->cumulative_diff_precise_adjusted;
return m_db_blocks[i]->cumulative_diff_precise;
}
}
cumulative_diff_precise_adj = 0;
return 0;
}
//------------------------------------------------------------------
@ -4415,7 +4724,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
if (is_pos_bl)
{
bool r = validate_pos_block(bl, current_diffic, pos_coinstake_amount, this_coin_diff, proof_hash, id, false);
CHECK_AND_ASSERT_MES_CUSTOM(r, false, bvc.m_verification_failed = true, "validate_pos_block failed!!");
CHECK_AND_ASSERT_MES_CUSTOM(r, false, bvc.m_verification_failed = true, "validate_pos_block failed!!");
}
else
{
@ -4476,6 +4785,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
size_t tx_processed_count = 0;
uint64_t fee_summary = 0;
uint64_t burned_coins = 0;
for(const crypto::hash& tx_id : bl.tx_hashes)
{
@ -4515,6 +4825,7 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
return false;
}
TIME_MEASURE_FINISH_PD(tx_check_inputs_time);
burned_coins += get_burned_amount(tx);
TIME_MEASURE_START_PD(tx_prapare_append);
uint64_t current_bc_size = get_current_blockchain_size();
@ -4574,6 +4885,12 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
if (is_pos_bl)
bei.stake_hash = proof_hash;
//////////////////////////////////////////////////////////////////////////
//old style cumulative difficulty collecting
//precise difficulty - difficulty used to calculate next difficulty
uint64_t last_x_h = get_last_x_block_height(is_pos_bl);
if (!last_x_h)
@ -4597,11 +4914,10 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
size_t sequence_factor = get_current_sequence_factor(is_pos_bl);
if (bei.height >= m_core_runtime_config.pos_minimum_heigh)
cumulative_diff_delta = correct_difficulty_with_sequence_factor(sequence_factor, cumulative_diff_delta);
if (bei.height > BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION && is_pos_bl && sequence_factor > 20)
if (bei.height > BLOCKCHAIN_HEIGHT_FOR_POS_STRICT_SEQUENCE_LIMITATION && is_pos_bl && sequence_factor > BLOCK_POS_STRICT_SEQUENCE_LIMIT)
{
LOG_PRINT_L0("Block with id: " << id
<< " has too big sequence_factor = " << sequence_factor);
LOG_PRINT_RED_L0("Block " << id << " @ " << bei.height << " has too big sequence factor: " << sequence_factor << ", rejected");
purge_block_data_from_blockchain(bl, tx_processed_count);
bvc.m_verification_failed = true;
return false;
@ -4609,8 +4925,22 @@ bool blockchain_storage::handle_block_to_main_chain(const block& bl, const crypt
bei.cumulative_diff_adjusted += cumulative_diff_delta;
//////////////////////////////////////////////////////////////////////////
// rebuild cumulative_diff_precise_adjusted for whole period
wide_difficulty_type diff_precise_adj = correct_difficulty_with_sequence_factor(sequence_factor, current_diffic);
bei.cumulative_diff_precise_adjusted = last_x_h ? m_db_blocks[last_x_h]->cumulative_diff_precise_adjusted + diff_precise_adj : diff_precise_adj;
//////////////////////////////////////////////////////////////////////////
//etc
bei.already_generated_coins = already_generated_coins + base_reward;
if (already_generated_coins < burned_coins)
{
LOG_ERROR("Condition failed: already_generated_coins(" << already_generated_coins << ") >= burned_coins(" << burned_coins << ")");
purge_block_data_from_blockchain(bl, tx_processed_count);
bvc.m_verification_failed = true;
return false;
}
bei.already_generated_coins = already_generated_coins - burned_coins + base_reward;
auto blocks_index_ptr = m_db_blocks_index.get(id);
if (blocks_index_ptr)
@ -4783,13 +5113,26 @@ bool blockchain_storage::update_next_comulative_size_limit()
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::add_new_block(const block& bl_, block_verification_context& bvc)
bool blockchain_storage::prevalidate_block(const block& bl)
{
if (bl.major_version == BLOCK_MAJOR_VERSION_INITAL && get_block_height(bl) <= m_core_runtime_config.hard_fork1_starts_after_height)
return true;
if (bl.major_version != CURRENT_BLOCK_MAJOR_VERSION)
{
LOG_ERROR("prevalidation failed for block " << get_block_hash(bl) << ": major block version " << static_cast<size_t>(bl.major_version) << " is incorrect, " << CURRENT_BLOCK_MAJOR_VERSION << " is expected" << ENDL
<< obj_to_json_str(bl));
return false;
}
return true;
}
//------------------------------------------------------------------
bool blockchain_storage::add_new_block(const block& bl, block_verification_context& bvc)
{
try
{
m_db.begin_transaction();
block bl = bl_;
//block bl = bl_;
crypto::hash id = get_block_hash(bl);
CRITICAL_REGION_LOCAL(m_tx_pool);
//CRITICAL_REGION_LOCAL1(m_read_lock);
@ -4802,7 +5145,18 @@ bool blockchain_storage::add_new_block(const block& bl_, block_verification_cont
return false;
}
if (!prevalidate_block(bl))
{
LOG_PRINT_RED_L0("block with id = " << id << " failed to prevalidate");
bvc.m_added_to_main_chain = false;
bvc.m_verification_failed = true;
m_db.commit_transaction();
return false;
}
//check that block refers to chain tail
if (!(bl.prev_id == get_top_block_id()))
{
@ -5584,6 +5938,8 @@ bool blockchain_storage::validate_alt_block_txs(const block& b, const crypto::ha
}
update_alt_out_indexes_for_tx_in_block(b.miner_tx, abei);
CHECK_AND_ASSERT_MES(validate_tx_for_hardfork_specific_terms(b.miner_tx, null_hash, height), false, "miner tx hardfork-specific validation failed");
for (auto tx_id : b.tx_hashes)
{
std::shared_ptr<transaction> tx_ptr;
@ -5614,6 +5970,8 @@ bool blockchain_storage::validate_alt_block_txs(const block& b, const crypto::ha
CHECK_AND_ASSERT_MES(false, false, "input #" << n << " has unexpected type (" << tx.vin[n].type().name() << "), tx " << tx_id);
}
}
CHECK_AND_ASSERT_MES(validate_tx_for_hardfork_specific_terms(tx, tx_id, height), false, "tx " << tx_id << ": hardfork-specific validation failed");
// Updating abei (and not updating alt_chain) during this cycle is safe because txs in the same block can't reference one another,
// so only valid references are either to previous alt blocks (accessed via alt_chain) or to main chain blocks.

View file

@ -153,6 +153,9 @@ namespace currency
// {amount -> pub_keys} map of outputs' pub_keys appeared in this alt block ( index_in_vector == output_gindex - gindex_lookup_table[output_amount] )
std::map<uint64_t, std::vector<crypto::public_key> > outputs_pub_keys;
//date added to alt chain storage
uint64_t timestamp;
};
typedef std::unordered_map<crypto::hash, alt_block_extended_info> alt_chain_container;
//typedef std::list<alt_chain_container::iterator> alt_chain_type;
@ -184,6 +187,7 @@ namespace currency
//------------- modifying members --------------
bool add_new_block(const block& bl_, block_verification_context& bvc);
bool prevalidate_block(const block& bl);
bool clear();
bool reset_and_set_genesis_block(const block& b);
//debug function
@ -221,8 +225,11 @@ namespace currency
bool have_tx_keyimg_as_spent(const crypto::key_image &key_im, uint64_t before_height = UINT64_MAX) const;
std::shared_ptr<transaction> get_tx(const crypto::hash &id) const;
template<class visitor_t>
bool scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height = NULL) const ;
bool scan_outputkeys_for_indexes(const transaction &validated_tx, const txin_to_key& tx_in_to_key, visitor_t& vis) { uint64_t stub = 0; return scan_outputkeys_for_indexes(validated_tx, tx_in_to_key, vis, stub); }
template<class visitor_t>
bool scan_outputkeys_for_indexes(const transaction &validated_tx, const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t& max_related_block_height) const ;
uint64_t get_current_blockchain_size() const;
uint64_t get_top_block_height() const;
@ -230,7 +237,7 @@ namespace currency
crypto::hash get_top_block_id(uint64_t& height) const;
bool get_top_block(block& b) const;
wide_difficulty_type get_next_diff_conditional(bool pos) const;
wide_difficulty_type get_next_diff_conditional2(bool pos, const alt_chain_type& alt_chain, uint64_t split_height) const;
wide_difficulty_type get_next_diff_conditional2(bool pos, const alt_chain_type& alt_chain, uint64_t split_height, const alt_block_extended_info& abei) const;
wide_difficulty_type get_cached_next_difficulty(bool pos) const;
typedef bool fill_block_template_func_t(block &bl, bool pos, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins, size_t &total_size, uint64_t &fee, uint64_t height);
@ -260,13 +267,14 @@ namespace currency
uint64_t get_aliases_count()const;
uint64_t get_block_h_older_then(uint64_t timestamp) const;
bool validate_tx_service_attachmens_in_services(const tx_service_attachment& a, size_t i, const transaction& tx)const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height = NULL)const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t* pmax_related_block_height = NULL)const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t* pmax_used_block_height = NULL)const;
//bool check_tx_inputs(const transaction& tx, uint64_t* pmax_used_block_height = NULL)const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id)const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t& max_related_block_height, uint64_t& max_unlock_time)const;
bool check_tx_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, uint64_t& max_related_block_height)const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height)const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash) const;
bool check_tx_inputs(const transaction& tx, const crypto::hash& tx_prefix_hash, uint64_t& max_used_block_height, crypto::hash& max_used_block_id)const;
bool check_ms_input(const transaction& tx, size_t in_index, const txin_multisig& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const transaction& source_tx, size_t out_n) const;
bool get_output_keys_for_input_with_checks(const txin_to_key& txin, std::vector<crypto::public_key>& output_keys, uint64_t* pmax_related_block_height = NULL) const;
bool validate_tx_for_hardfork_specific_terms(const transaction& tx, const crypto::hash& tx_id, uint64_t block_height) const;
bool get_output_keys_for_input_with_checks(const transaction& tx, const txin_to_key& txin, std::vector<crypto::public_key>& output_keys, uint64_t& max_related_block_height, uint64_t& max_unlock_time) const;
bool check_tokey_input(const transaction& tx, size_t in_index, const txin_to_key& txin, const crypto::hash& tx_prefix_hash, const std::vector<crypto::signature>& sig, const std::vector<const crypto::public_key*>& output_keys_ptrs) const;
uint64_t get_current_comulative_blocksize_limit()const;
uint64_t get_current_hashrate(size_t aprox_count)const;
@ -304,6 +312,7 @@ namespace currency
bool build_stake_modifier(stake_modifier_type& sm, const alt_chain_type& alt_chain = alt_chain_type(), uint64_t split_height = 0, crypto::hash *p_last_block_hash = nullptr) const;
bool scan_pos(const COMMAND_RPC_SCAN_POS::request& sp, COMMAND_RPC_SCAN_POS::response& rsp)const;
bool validate_pos_coinbase_outs_unlock_time(const transaction& miner_tx, uint64_t staked_amount, uint64_t max_unlock_time)const;
bool validate_pos_block(const block& b, const crypto::hash& id, bool for_altchain)const;
bool validate_pos_block(const block& b, wide_difficulty_type basic_diff, const crypto::hash& id, bool for_altchain)const;
bool validate_pos_block(const block& b,
@ -421,6 +430,7 @@ namespace currency
void print_blockchain_outs(const std::string& file) const;
void print_blockchain_outs_stat() const;
void print_db_cache_perfeormance_data() const;
void print_last_n_difficulty_numbers(uint64_t n) const;
bool calc_tx_cummulative_blob(const block& bl)const;
bool get_outs_index_stat(outs_index_stat& outs_stat)const;
bool print_lookup_key_image(const crypto::key_image& ki) const;
@ -430,6 +440,7 @@ namespace currency
bool rebuild_tx_fee_medians();
bool validate_all_aliases_for_new_median_mode();
bool print_tx_outputs_lookup(const crypto::hash& tx_id) const;
uint64_t get_last_x_block_height(bool pos)const;
private:
//-------------- DB containers --------------
@ -541,7 +552,8 @@ namespace currency
bool handle_block_to_main_chain(const block& bl, const crypto::hash& id, block_verification_context& bvc);
std::string print_alt_chain(alt_chain_type alt_chain);
bool handle_alternative_block(const block& b, const crypto::hash& id, block_verification_context& bvc);
bool is_reorganize_required(const block_extended_info& main_chain_bei, const block_extended_info& alt_chain_bei, const crypto::hash& proof_alt);
bool is_reorganize_required(const block_extended_info& main_chain_bei, const alt_chain_type& alt_chain, const crypto::hash& proof_alt);
wide_difficulty_type get_x_difficulty_after_height(uint64_t height, bool is_pos);
bool purge_keyimage_from_big_heap(const crypto::key_image& ki, const crypto::hash& id);
bool purge_altblock_keyimages_from_big_heap(const block& b, const crypto::hash& id);
bool append_altblock_keyimages_to_big_heap(const crypto::hash& block_id, const std::set<crypto::key_image>& alt_block_keyimages);
@ -550,9 +562,8 @@ namespace currency
bool validate_alt_block_txs(const block& b, const crypto::hash& id, std::set<crypto::key_image>& collected_keyimages, alt_block_extended_info& abei, const alt_chain_type& alt_chain, uint64_t split_height, uint64_t& ki_lookup_time_total) const;
bool update_alt_out_indexes_for_tx_in_block(const transaction& tx, alt_block_extended_info& abei)const;
bool get_transaction_from_pool_or_db(const crypto::hash& tx_id, std::shared_ptr<transaction>& tx_ptr, uint64_t min_allowed_block_height = 0) const;
void get_last_n_x_blocks(uint64_t n, bool pos_blocks, std::list<std::shared_ptr<const block_extended_info>>& blocks) const;
bool prevalidate_miner_transaction(const block& b, uint64_t height, bool pos)const;
bool validate_transaction(const block& b, uint64_t height, const transaction& tx)const;
bool rollback_blockchain_switching(std::list<block>& original_chain, size_t rollback_height);
bool add_transaction_from_block(const transaction& tx, const crypto::hash& tx_id, const crypto::hash& bl_id, uint64_t bl_height, uint64_t timestamp);
bool push_transaction_to_global_outs_index(const transaction& tx, const crypto::hash& tx_id, std::vector<uint64_t>& global_indexes);
@ -608,8 +619,8 @@ namespace currency
//POS
wide_difficulty_type get_adjusted_cumulative_difficulty_for_next_pos(wide_difficulty_type next_diff)const;
wide_difficulty_type get_adjusted_cumulative_difficulty_for_next_alt_pos(alt_chain_type& alt_chain, uint64_t block_height, wide_difficulty_type next_diff, uint64_t connection_height)const;
uint64_t get_last_x_block_height(bool pos)const;
wide_difficulty_type get_last_alt_x_block_cumulative_precise_difficulty(alt_chain_type& alt_chain, uint64_t block_height, bool pos)const;
wide_difficulty_type get_last_alt_x_block_cumulative_precise_difficulty(const alt_chain_type& alt_chain, uint64_t block_height, bool pos, wide_difficulty_type& cumulative_diff_precise_adj)const;
wide_difficulty_type get_last_alt_x_block_cumulative_precise_adj_difficulty(const alt_chain_type& alt_chain, uint64_t block_height, bool pos) const;
size_t get_current_sequence_factor_for_alt(alt_chain_type& alt_chain, bool pos, uint64_t connection_height)const;
};
@ -648,7 +659,7 @@ namespace currency
//------------------------------------------------------------------
//------------------------------------------------------------------
template<class visitor_t>
bool blockchain_storage::scan_outputkeys_for_indexes(const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t* pmax_related_block_height) const
bool blockchain_storage::scan_outputkeys_for_indexes(const transaction &validated_tx, const txin_to_key& tx_in_to_key, visitor_t& vis, uint64_t& max_related_block_height) const
{
CRITICAL_REGION_LOCAL(m_read_lock);
TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_get_item_size);
@ -704,18 +715,16 @@ namespace currency
CHECK_AND_ASSERT_MES(mixattr_ok, false, "tx output #" << output_index << " violates mixin restrictions: mix_attr = " << static_cast<uint32_t>(outtk.mix_attr) << ", key_offsets.size = " << tx_in_to_key.key_offsets.size());
TIME_MEASURE_START_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output);
if (!vis.handle_output(tx_ptr->tx, tx_ptr->tx.vout[n]))
if (!vis.handle_output(tx_ptr->tx, validated_tx, tx_ptr->tx.vout[n], n))
{
LOG_PRINT_L0("Failed to handle_output for output id = " << tx_id << ", no " << n);
return false;
}
TIME_MEASURE_FINISH_PD(tx_check_inputs_loop_scan_outputkeys_loop_handle_output);
if (pmax_related_block_height)
{
if (*pmax_related_block_height < tx_ptr->m_keeper_block_height)
*pmax_related_block_height = tx_ptr->m_keeper_block_height;
}
if (max_related_block_height < tx_ptr->m_keeper_block_height)
max_related_block_height = tx_ptr->m_keeper_block_height;
++output_index;
}

View file

@ -47,6 +47,7 @@ namespace currency
uint64_t block_cumulative_size;
wide_difficulty_type cumulative_diff_adjusted;
wide_difficulty_type cumulative_diff_precise;
wide_difficulty_type cumulative_diff_precise_adjusted;
wide_difficulty_type difficulty;
boost::multiprecision::uint128_t already_generated_coins;
crypto::hash stake_hash; //TODO: unused field for PoW blocks, subject for refactoring

View file

@ -18,6 +18,8 @@ namespace currency
uint64_t pos_minimum_heigh; //height
uint64_t tx_pool_min_fee;
uint64_t tx_default_fee;
uint64_t hard_fork1_starts_after_height;
uint64_t max_alt_blocks;
crypto::public_key alias_validation_pubkey;
core_time_func_t get_core_time;
@ -34,6 +36,8 @@ namespace currency
pc.pos_minimum_heigh = POS_START_HEIGHT;
pc.tx_pool_min_fee = TX_MINIMUM_FEE;
pc.tx_default_fee = TX_DEFAULT_FEE;
pc.max_alt_blocks = CURRENCY_ALT_BLOCK_MAX_COUNT;
pc.hard_fork1_starts_after_height = ZANO_HARDFORK_1_AFTER_HEIGHT;
pc.get_core_time = &core_runtime_config::_default_core_time_function;
bool r = epee::string_tools::hex_to_pod(ALIAS_SHORT_NAMES_VALIDATION_PUB_KEY, pc.alias_validation_pubkey);
CHECK_AND_ASSERT_THROW_MES(r, "failed to parse alias_validation_pub_key");

View file

@ -336,7 +336,7 @@ namespace currency
};
//number of block (or time), used as a limitation: spend this tx not early then block/time
//number of block (or timestamp if v bigger then CURRENCY_MAX_BLOCK_NUMBER), used as a limitation: spend this tx not early then block/time
struct etc_tx_details_unlock_time
{
uint64_t v;
@ -345,6 +345,16 @@ namespace currency
END_SERIALIZE()
};
//number of block (or timestamp if unlock_time_array[i] bigger then CURRENCY_MAX_BLOCK_NUMBER), used as a limitation: spend this tx not early then block/time
//unlock_time_array[i], i - index of output, unlock_time_array.size() == vout.size()
struct etc_tx_details_unlock_time2
{
std::vector<uint64_t> unlock_time_array;
BEGIN_SERIALIZE()
FIELD(unlock_time_array)
END_SERIALIZE()
};
struct etc_tx_details_expiration_time
{
uint64_t v;
@ -361,7 +371,7 @@ namespace currency
uint64_t v;
BEGIN_SERIALIZE()
VARINT_FIELD(v)
END_SERIALIZE()
END_SERIALIZE()
};
struct etc_tx_details_flags
@ -380,7 +390,7 @@ namespace currency
END_SERIALIZE()
};
typedef boost::mpl::vector<tx_service_attachment, tx_comment, tx_payer, tx_receiver, tx_message, std::string, tx_crypto_checksum, etc_tx_time, etc_tx_details_unlock_time, etc_tx_details_expiration_time, etc_tx_details_flags, crypto::public_key, extra_attachment_info, extra_alias_entry, extra_user_data, extra_padding, etc_tx_derivation_hint> all_payload_types;
typedef boost::mpl::vector<tx_service_attachment, tx_comment, tx_payer, tx_receiver, tx_message, std::string, tx_crypto_checksum, etc_tx_time, etc_tx_details_unlock_time, etc_tx_details_expiration_time, etc_tx_details_flags, crypto::public_key, extra_attachment_info, extra_alias_entry, extra_user_data, extra_padding, etc_tx_derivation_hint, etc_tx_details_unlock_time2> all_payload_types;
typedef boost::make_variant_over<all_payload_types>::type attachment_v;
typedef boost::make_variant_over<all_payload_types>::type extra_v;
typedef boost::make_variant_over<all_payload_types>::type payload_items_v;
@ -553,12 +563,14 @@ namespace currency
uint64_t index;
crypto::key_image keyimage;
uint64_t block_timestamp;
uint64_t stake_unlock_time;
//not for serialization
uint64_t wallet_index;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(amount)
KV_SERIALIZE(index)
KV_SERIALIZE(stake_unlock_time)
KV_SERIALIZE(block_timestamp)
KV_SERIALIZE_VAL_POD_AS_BLOB_FORCE(keyimage)
END_KV_SERIALIZE_MAP()
@ -613,5 +625,6 @@ SET_VARIANT_TAGS(uint64_t, 26, "uint64_t");
SET_VARIANT_TAGS(currency::etc_tx_time, 27, "etc_tx_time");
SET_VARIANT_TAGS(uint32_t, 28, "uint32_t");
SET_VARIANT_TAGS(currency::tx_receiver, 29, "payer");
SET_VARIANT_TAGS(currency::etc_tx_details_unlock_time2, 30, "unlock_time2");
#undef SET_VARIANT_TAGS

View file

@ -190,6 +190,11 @@ namespace boost
a & at.v;
}
template <class Archive>
inline void serialize(Archive &a, currency::etc_tx_details_unlock_time2 &at, const boost::serialization::version_type ver)
{
a & at.unlock_time_array;
}
template <class Archive>
inline void serialize(Archive &a, currency::etc_tx_details_expiration_time &at, const boost::serialization::version_type ver)
{
a & at.v;

View file

@ -24,10 +24,6 @@
#define CURRENCY_POS_BLOCK_FUTURE_TIME_LIMIT 60*20
#define BLOCKCHAIN_TIMESTAMP_CHECK_WINDOW 60
// TOTAL_MONEY_SUPPLY - total number coins to be generated
#define TOTAL_MONEY_SUPPLY ((uint64_t)(-1))
#define POS_START_HEIGHT 0
@ -87,8 +83,10 @@
#define CURRENCY_ALT_BLOCK_LIVETIME_COUNT (CURRENCY_BLOCKS_PER_DAY*7)//one week
#define CURRENCY_ALT_BLOCK_MAX_COUNT 43200 //30 days
#define CURRENCY_MEMPOOL_TX_LIVETIME 345600 //seconds, 4 days
#ifndef TESTNET
#define P2P_DEFAULT_PORT 11121
#define RPC_DEFAULT_PORT 11211
@ -128,7 +126,7 @@
//PoS definitions
#define POS_SCAN_WINDOW 60*10 //seconds // 10 minutes
#define POS_SCAN_STEP 15 //seconds
#define POS_MAC_ACTUAL_TIMESTAMP_TO_MINED (POS_SCAN_WINDOW+100)
#define POS_MAX_ACTUAL_TIMESTAMP_TO_MINED (POS_SCAN_WINDOW+100)
#define POS_STARTER_KERNEL_HASH "00000000000000000006382a8d8f94588ce93a1351924f6ccb9e07dd287c6e4b"
#define POS_MODFIFIER_INTERVAL 10
@ -192,10 +190,11 @@
#define GUI_INTERNAL_CONFIG "gui_internal_config.bin"
#define CURRENT_TRANSACTION_CHAIN_ENTRY_ARCHIVE_VER 3
#define CURRENT_BLOCK_EXTENDED_INFO_ARCHIVE_VER 1
#define BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION CURRENCY_FORMATION_VERSION + 7
#define BLOCKCHAIN_STORAGE_MAJOR_COMPATIBILITY_VERSION CURRENCY_FORMATION_VERSION + 8
#define BLOCKCHAIN_STORAGE_MINOR_COMPATIBILITY_VERSION 1
@ -203,10 +202,19 @@
#define BC_OFFERS_CURRENCY_MARKET_FILENAME "market.bin"
#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+64)
#define WALLET_FILE_SERIALIZATION_VERSION (CURRENCY_FORMATION_VERSION+65)
#define CURRENT_MEMPOOL_ARCHIVE_VER (CURRENCY_FORMATION_VERSION+31)
//hard forks section
#define BLOCK_MAJOR_VERSION_GENESIS 1
#define BLOCK_MINOR_VERSION_GENESIS 0
#define BLOCK_MAJOR_VERSION_INITAL 0
#ifndef TESTNET
#define ZANO_HARDFORK_1_AFTER_HEIGHT 166440
#else
#define ZANO_HARDFORK_1_AFTER_HEIGHT 62102
#endif

View file

@ -107,21 +107,54 @@ namespace currency
out_amounts.resize(out_amounts.size() - 1);
}
std::vector<tx_destination_entry> destinations;
for (auto a : out_amounts)
{
tx_destination_entry de;
tx_destination_entry de = AUTO_VAL_INIT(de);
de.addr.push_back(miner_address);
de.amount = a;
if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
{
//this means that block is creating after hardfork_1 and unlock_time is needed to set for every destination separately
de.unlock_time = height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW;
}
destinations.push_back(de);
}
if (pos)
destinations.push_back(tx_destination_entry(pe.amount, stakeholder_address));
{
uint64_t stake_lock_time = 0;
if (pe.stake_unlock_time && pe.stake_unlock_time > height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW)
stake_lock_time = pe.stake_unlock_time;
destinations.push_back(tx_destination_entry(pe.amount, stakeholder_address, stake_lock_time));
}
return construct_miner_tx(height, median_size, already_generated_coins, current_block_size, fee, destinations, tx, extra_nonce, max_outs, pos, pe);
}
//------------------------------------------------------------------
bool apply_unlock_time(const std::vector<tx_destination_entry>& destinations, transaction& tx)
{
currency::etc_tx_details_unlock_time2 unlock_time2 = AUTO_VAL_INIT(unlock_time2);
unlock_time2.unlock_time_array.resize(destinations.size());
bool found_unlock_time = false;
for (size_t i = 0; i != unlock_time2.unlock_time_array.size(); i++)
{
if (destinations[i].unlock_time)
{
found_unlock_time = true;
unlock_time2.unlock_time_array[i] = destinations[i].unlock_time;
}
}
if (found_unlock_time)
{
tx.extra.push_back(unlock_time2);
}
return true;
}
//------------------------------------------------------------------
bool construct_miner_tx(size_t height, size_t median_size, const boost::multiprecision::uint128_t& already_generated_coins,
size_t current_block_size,
uint64_t fee,
@ -144,9 +177,12 @@ namespace currency
if (!add_tx_extra_userdata(tx, extra_nonce))
return false;
//at this moment we do apply_unlock_time only for coin_base transactions
apply_unlock_time(destinations, tx);
//we always add extra_padding with 2 bytes length to make possible for get_block_template to adjust cumulative size
tx.extra.push_back(extra_padding());
txin_gen in;
in.height = height;
tx.vin.push_back(in);
@ -171,10 +207,15 @@ namespace currency
CHECK_AND_ASSERT_MES(r, false, "Failed to contruct miner tx out");
no++;
}
tx.version = CURRENT_TRANSACTION_VERSION;
set_tx_unlock_time(tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
if (!have_type_in_variant_container<etc_tx_details_unlock_time2>(tx.extra))
{
//if stake unlock time was not set, then we can use simple "whole transaction" lock scheme
set_tx_unlock_time(tx, height + CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
}
return true;
}
//---------------------------------------------------------------
@ -891,16 +932,7 @@ namespace currency
}
return n;
}
//---------------------------------------------------------------
account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector<tx_destination_entry>& destinations)
{
for (const auto& de : destinations)
{
if (de.addr.size() == 1 && sender_account_keys.m_account_address != de.addr.back())
return de.addr.back(); // return the first destination address that is non-multisig and not equal to the sender's address
}
return sender_account_keys.m_account_address; // otherwise, fallback to sender's address
}
//---------------------------------------------------------------
bool construct_tx(const account_keys& sender_account_keys,
const std::vector<tx_source_entry>& sources,
@ -1304,7 +1336,7 @@ namespace currency
bool get_inputs_money_amount(const transaction& tx, uint64_t& money)
{
money = 0;
BOOST_FOREACH(const auto& in, tx.vin)
for(const auto& in : tx.vin)
{
uint64_t this_amount = get_amount_from_variant(in);
if (!this_amount)
@ -1328,7 +1360,7 @@ namespace currency
//---------------------------------------------------------------
bool check_inputs_types_supported(const transaction& tx)
{
BOOST_FOREACH(const auto& in, tx.vin)
for(const auto& in : tx.vin)
{
CHECK_AND_ASSERT_MES(in.type() == typeid(txin_to_key) || in.type() == typeid(txin_multisig), false, "wrong variant type: "
<< in.type().name() << ", expected " << typeid(txin_to_key).name()
@ -1828,9 +1860,7 @@ namespace currency
//for future forks
std::cout << "Currency name: \t\t" << CURRENCY_NAME << "(" << CURRENCY_NAME_SHORT << ")" << std::endl;
std::cout << "Money supply: \t\t" << print_money(TOTAL_MONEY_SUPPLY) << " coins"
<< "(" << print_money(TOTAL_MONEY_SUPPLY) << "), dev bounties is ???" << std::endl;
std::cout << "Money supply: \t\t " << CURRENCY_BLOCK_REWARD * CURRENCY_BLOCKS_PER_DAY * 365 << " coins per year" << std::endl;
std::cout << "PoS block interval: \t" << DIFFICULTY_POS_TARGET << " seconds" << std::endl;
std::cout << "PoW block interval: \t" << DIFFICULTY_POW_TARGET << " seconds" << std::endl;
std::cout << "Total blocks per day: \t" << CURRENCY_BLOCKS_PER_DAY << " seconds" << std::endl;
@ -1874,8 +1904,8 @@ namespace currency
//string_tools::parse_hexstr_to_binbuff(genesis_coinbase_tx_hex, tx_bl);
bool r = parse_and_validate_tx_from_blob(tx_bl, bl.miner_tx);
CHECK_AND_ASSERT_MES(r, false, "failed to parse coinbase tx from hard coded blob");
bl.major_version = CURRENT_BLOCK_MAJOR_VERSION;
bl.minor_version = CURRENT_BLOCK_MINOR_VERSION;
bl.major_version = BLOCK_MAJOR_VERSION_GENESIS;
bl.minor_version = BLOCK_MINOR_VERSION_GENESIS;
bl.timestamp = 0;
bl.nonce = CURRENCY_GENESIS_NONCE;
LOG_PRINT_GREEN("Generated genesis: " << get_block_hash(bl), LOG_LEVEL_0);
@ -2081,6 +2111,20 @@ namespace currency
return true;
}
bool operator()(const etc_tx_details_unlock_time2& ee)
{
tv.type = "unlock_time";
std::stringstream ss;
ss << "[";
for (auto v : ee.unlock_time_array)
{
ss << " " << v;
}
ss << "]";
tv.short_view = ss.str();
return true;
}
bool operator()(const etc_tx_details_expiration_time& ee)
{
tv.type = "expiration_time";
@ -2524,16 +2568,6 @@ namespace currency
{
return epee::string_tools::parse_hexstr_to_binbuff(payment_id_str, payment_id);
}
//------------------------------------------------------------------
bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median)
{
/// tx expiration condition (tx is ok if the following is true)
/// tx_expiration_time - TX_EXPIRATION_MEDIAN_SHIFT > get_last_n_blocks_timestamps_median(TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW)
uint64_t expiration_time = get_tx_expiration_time(tx);
if (expiration_time == 0)
return false; // 0 means it never expires
return expiration_time <= expiration_ts_median + TX_EXPIRATION_MEDIAN_SHIFT;
}
//--------------------------------------------------------------------------------
crypto::hash prepare_prefix_hash_for_sign(const transaction& tx, uint64_t in_index, const crypto::hash& tx_id)
{
@ -2667,4 +2701,41 @@ namespace currency
return false;
}
wide_difficulty_type get_a_to_b_relative_cumulative_difficulty(const wide_difficulty_type& difficulty_pos_at_split_point,
const wide_difficulty_type& difficulty_pow_at_split_point,
const difficulties& a_diff,
const difficulties& b_diff )
{
static const wide_difficulty_type difficulty_starter = DIFFICULTY_STARTER;
const wide_difficulty_type& a_pos_cumulative_difficulty = a_diff.pos_diff > 0 ? a_diff.pos_diff : difficulty_starter;
const wide_difficulty_type& b_pos_cumulative_difficulty = b_diff.pos_diff > 0 ? b_diff.pos_diff : difficulty_starter;
const wide_difficulty_type& a_pow_cumulative_difficulty = a_diff.pow_diff > 0 ? a_diff.pow_diff : difficulty_starter;
const wide_difficulty_type& b_pow_cumulative_difficulty = b_diff.pow_diff > 0 ? b_diff.pow_diff : difficulty_starter;
boost::multiprecision::uint1024_t basic_sum = boost::multiprecision::uint1024_t(a_pow_cumulative_difficulty) + (boost::multiprecision::uint1024_t(a_pos_cumulative_difficulty)*difficulty_pow_at_split_point) / difficulty_pos_at_split_point;
boost::multiprecision::uint1024_t res =
(basic_sum * a_pow_cumulative_difficulty * a_pos_cumulative_difficulty) / (boost::multiprecision::uint1024_t(a_pow_cumulative_difficulty)*a_pos_cumulative_difficulty);
if (res > boost::math::tools::max_value<wide_difficulty_type>())
{
ASSERT_MES_AND_THROW("[INTERNAL ERROR]: Failed to get_a_to_b_relative_cumulative_difficulty, res = " << res << ENDL
<< ", difficulty_pos_at_split_point: " << difficulty_pos_at_split_point << ENDL
<< ", difficulty_pow_at_split_point:" << difficulty_pow_at_split_point << ENDL
<< ", a_pos_cumulative_difficulty:" << a_pos_cumulative_difficulty << ENDL
<< ", b_pos_cumulative_difficulty:" << b_pos_cumulative_difficulty << ENDL
<< ", a_pow_cumulative_difficulty:" << a_pow_cumulative_difficulty << ENDL
<< ", b_pow_cumulative_difficulty:" << b_pow_cumulative_difficulty << ENDL
);
}
TRY_ENTRY();
wide_difficulty_type short_res = res.convert_to<wide_difficulty_type>();
return short_res;
CATCH_ENTRY_WITH_FORWARDING_EXCEPTION();
}
} // namespace currency

View file

@ -59,55 +59,7 @@ namespace currency
typedef boost::multiprecision::uint128_t uint128_tl;
struct tx_source_entry
{
typedef serializable_pair<txout_v, crypto::public_key> output_entry; // txout_v is either global output index or ref_by_id; public_key - is output ephemeral pub key
std::vector<output_entry> outputs; //index + key
uint64_t real_output; //index in outputs vector of real output_entry
crypto::public_key real_out_tx_key; //real output's transaction's public key
size_t real_output_in_tx_index; //index in transaction outputs vector
uint64_t amount; //money
uint64_t transfer_index; //money
crypto::hash multisig_id; //if txin_multisig: multisig output id
size_t ms_sigs_count; //if txin_multisig: must be equal to output's minimum_sigs
size_t ms_keys_count; //if txin_multisig: must be equal to size of output's keys container
bool separately_signed_tx_complete; //for separately signed tx only: denotes the last source entry in complete tx to explicitly mark the final step of tx creation
bool is_multisig() const { return ms_sigs_count > 0; }
BEGIN_SERIALIZE_OBJECT()
FIELD(outputs)
FIELD(real_output)
FIELD(real_out_tx_key)
FIELD(real_output_in_tx_index)
FIELD(amount)
FIELD(transfer_index)
FIELD(multisig_id)
FIELD(ms_sigs_count)
FIELD(ms_keys_count)
FIELD(separately_signed_tx_complete)
END_SERIALIZE()
};
struct tx_destination_entry
{
uint64_t amount; //money
std::list<account_public_address> addr; //destination address, in case of 1 address - txout_to_key, in case of more - txout_multisig
size_t minimum_sigs; // if txout_multisig: minimum signatures that are required to spend this output (minimum_sigs <= addr.size()) IF txout_to_key - not used
uint64_t amount_to_provide; //amount money that provided by initial creator of tx, used with partially created transactions
tx_destination_entry() : amount(0), minimum_sigs(0), amount_to_provide(0){}
tx_destination_entry(uint64_t a, const account_public_address& ad) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0){}
tx_destination_entry(uint64_t a, const std::list<account_public_address>& addr) : amount(a), addr(addr), minimum_sigs(addr.size()), amount_to_provide(0){}
BEGIN_SERIALIZE_OBJECT()
FIELD(amount)
FIELD(addr)
FIELD(minimum_sigs)
FIELD(amount_to_provide)
END_SERIALIZE()
};
struct tx_extra_info
{
@ -208,32 +160,6 @@ namespace currency
//---------------------------------------------------------------
template<class extra_type_t>
uint64_t get_tx_x_detail(const transaction& tx)
{
extra_type_t e = AUTO_VAL_INIT(e);
get_type_in_variant_container(tx.extra, e);
return e.v;
}
template<class extra_type_t>
void set_tx_x_detail(transaction& tx, uint64_t v)
{
extra_type_t e = AUTO_VAL_INIT(e);
e.v = v;
update_or_add_field_to_extra(tx.extra, e);
}
inline uint64_t get_tx_unlock_time(const transaction& tx){ return get_tx_x_detail<etc_tx_details_unlock_time>(tx);}
inline uint64_t get_tx_flags(const transaction& tx){ return get_tx_x_detail<etc_tx_details_flags>(tx); }
inline uint64_t get_tx_expiration_time(const transaction& tx){ return get_tx_x_detail<etc_tx_details_expiration_time>(tx); }
inline void set_tx_unlock_time(transaction& tx, uint64_t v){ set_tx_x_detail<etc_tx_details_unlock_time>(tx, v); }
inline void set_tx_flags(transaction& tx, uint64_t v){ set_tx_x_detail<etc_tx_details_flags>(tx, v); }
inline void set_tx_expiration_time(transaction& tx, uint64_t v){ set_tx_x_detail<etc_tx_details_expiration_time>(tx, v); }
account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector<tx_destination_entry>& destinations);
bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median);
uint64_t get_string_uint64_hash(const std::string& str);
bool construct_tx_out(const tx_destination_entry& de, const crypto::secret_key& tx_sec_key, size_t output_index, transaction& tx, std::set<uint16_t>& deriv_cache, uint8_t tx_outs_attr = CURRENCY_TO_KEY_OUT_RELAXED);
bool validate_alias_name(const std::string& al);
@ -317,6 +243,7 @@ namespace currency
bool parse_amount(uint64_t& amount, const std::string& str_amount);
bool unserialize_block_complete_entry(const COMMAND_RPC_GET_BLOCKS_FAST::response& serialized,
COMMAND_RPC_GET_BLOCKS_DIRECT::response& unserialized);
@ -670,6 +597,19 @@ namespace currency
std::string utf8_to_upper(const std::string& s);
std::string utf8_to_lower(const std::string& s);
bool utf8_substring_test_case_insensitive(const std::string& match, const std::string& s); // Returns true is 's' contains 'match' (case-insensitive)
struct difficulties
{
wide_difficulty_type pos_diff;
wide_difficulty_type pow_diff;
};
wide_difficulty_type get_a_to_b_relative_cumulative_difficulty(const wide_difficulty_type& difficulty_pos_at_split_point,
const wide_difficulty_type& difficulty_pow_at_split_point,
const difficulties& a_diff,
const difficulties& b_diff
);
} // namespace currency

View file

@ -6,6 +6,7 @@
#include "currency_format_utils_blocks.h"
#include "serialization/serialization.h"
#include "currency_format_utils.h"
#include "currency_format_utils_abstract.h"
#include "currency_format_utils_transactions.h"
namespace currency

View file

@ -6,11 +6,127 @@
#include "currency_format_utils_transactions.h"
#include "serialization/serialization.h"
#include "currency_format_utils_abstract.h"
#include "currency_format_utils.h"
#include "currency_format_utils_abstract.h"
namespace currency
{
//---------------------------------------------------------------
account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector<tx_destination_entry>& destinations)
{
for (const auto& de : destinations)
{
if (de.addr.size() == 1 && sender_account_keys.m_account_address != de.addr.back())
return de.addr.back(); // return the first destination address that is non-multisig and not equal to the sender's address
}
return sender_account_keys.m_account_address; // otherwise, fallback to sender's address
}
//------------------------------------------------------------------
bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median)
{
/// tx expiration condition (tx is ok if the following is true)
/// tx_expiration_time - TX_EXPIRATION_MEDIAN_SHIFT > get_last_n_blocks_timestamps_median(TX_EXPIRATION_TIMESTAMP_CHECK_WINDOW)
uint64_t expiration_time = get_tx_expiration_time(tx);
if (expiration_time == 0)
return false; // 0 means it never expires
return expiration_time <= expiration_ts_median + TX_EXPIRATION_MEDIAN_SHIFT;
}
//---------------------------------------------------------------
uint64_t get_burned_amount(const transaction& tx)
{
uint64_t res = 0;
for (auto& o : tx.vout)
{
if (o.target.type() == typeid(txout_to_key))
{
if (boost::get<txout_to_key>(o.target).key == null_pkey)
res += o.amount;
}
}
return res;
}
//---------------------------------------------------------------
uint64_t get_tx_max_unlock_time(const transaction& tx)
{
// etc_tx_details_unlock_time have priority over etc_tx_details_unlock_time2
uint64_t v = get_tx_x_detail<etc_tx_details_unlock_time>(tx);
if (v)
return v;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
get_type_in_variant_container(tx.extra, ut2);
if (!ut2.unlock_time_array.size())
return 0;
uint64_t max_unlock_time = 0;
CHECK_AND_ASSERT_THROW_MES(ut2.unlock_time_array.size() == tx.vout.size(), "unlock_time_array.size=" << ut2.unlock_time_array.size()
<< " is not the same as tx.vout.size =" << tx.vout.size() << " in tx: " << get_transaction_hash(tx));
for (size_t i = 0; i != tx.vout.size(); i++)
{
if (ut2.unlock_time_array[i] > max_unlock_time)
max_unlock_time = ut2.unlock_time_array[i];
}
return max_unlock_time;
}
//---------------------------------------------------------------
uint64_t get_tx_unlock_time(const transaction& tx, uint64_t o_i)
{
// etc_tx_details_expiration_time have priority over etc_tx_details_expiration_time2
uint64_t v = get_tx_x_detail<etc_tx_details_unlock_time>(tx);
if (v)
return v;
CHECK_AND_ASSERT_THROW_MES(tx.vout.size() > o_i, "tx.vout.size=" << tx.vout.size()
<< " is not bigger then o_i=" << o_i << " in tx: " << get_transaction_hash(tx));
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
get_type_in_variant_container(tx.extra, ut2);
if (!ut2.unlock_time_array.size())
return 0;
CHECK_AND_ASSERT_THROW_MES(ut2.unlock_time_array.size() > o_i, "unlock_time_array.size=" << ut2.unlock_time_array.size()
<< " is less then o_i=" << o_i << " in tx: " << get_transaction_hash(tx));
return ut2.unlock_time_array[o_i];
}
//---------------------------------------------------------------
bool get_tx_max_min_unlock_time(const transaction& tx, uint64_t& max_unlock_time, uint64_t& min_unlock_time)
{
max_unlock_time = min_unlock_time = 0;
// etc_tx_details_expiration_time have priority over etc_tx_details_expiration_time2
uint64_t v = get_tx_x_detail<etc_tx_details_unlock_time>(tx);
if (v)
{
max_unlock_time = min_unlock_time = v;
return true;
}
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
get_type_in_variant_container(tx.extra, ut2);
if (!ut2.unlock_time_array.size())
{
return true;
}
CHECK_AND_ASSERT_THROW_MES(ut2.unlock_time_array.size() == tx.vout.size(), "unlock_time_array.size=" << ut2.unlock_time_array.size()
<< " is not equal tx.vout.size()=" << tx.vout.size() << " in tx: " << get_transaction_hash(tx));
if (ut2.unlock_time_array.size())
{
max_unlock_time = min_unlock_time = ut2.unlock_time_array[0];
for (size_t i = 1; i != ut2.unlock_time_array.size(); i++)
{
if (ut2.unlock_time_array[i] > max_unlock_time)
max_unlock_time = ut2.unlock_time_array[i];
if (ut2.unlock_time_array[i] < min_unlock_time)
min_unlock_time = ut2.unlock_time_array[i];
}
}
return true;
}
//---------------------------------------------------------------
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h)
{

View file

@ -9,10 +9,92 @@
#include "crypto/crypto.h"
#include "currency_core/currency_basic.h"
#include "currency_protocol/blobdatatype.h"
#include "currency_core/account.h"
namespace currency
{
struct tx_source_entry
{
typedef serializable_pair<txout_v, crypto::public_key> output_entry; // txout_v is either global output index or ref_by_id; public_key - is output ephemeral pub key
std::vector<output_entry> outputs; //index + key
uint64_t real_output; //index in outputs vector of real output_entry
crypto::public_key real_out_tx_key; //real output's transaction's public key
size_t real_output_in_tx_index; //index in transaction outputs vector
uint64_t amount; //money
uint64_t transfer_index; //money
crypto::hash multisig_id; //if txin_multisig: multisig output id
size_t ms_sigs_count; //if txin_multisig: must be equal to output's minimum_sigs
size_t ms_keys_count; //if txin_multisig: must be equal to size of output's keys container
bool separately_signed_tx_complete; //for separately signed tx only: denotes the last source entry in complete tx to explicitly mark the final step of tx creation
bool is_multisig() const { return ms_sigs_count > 0; }
BEGIN_SERIALIZE_OBJECT()
FIELD(outputs)
FIELD(real_output)
FIELD(real_out_tx_key)
FIELD(real_output_in_tx_index)
FIELD(amount)
FIELD(transfer_index)
FIELD(multisig_id)
FIELD(ms_sigs_count)
FIELD(ms_keys_count)
FIELD(separately_signed_tx_complete)
END_SERIALIZE()
};
struct tx_destination_entry
{
uint64_t amount; //money
std::list<account_public_address> addr; //destination address, in case of 1 address - txout_to_key, in case of more - txout_multisig
size_t minimum_sigs; // if txout_multisig: minimum signatures that are required to spend this output (minimum_sigs <= addr.size()) IF txout_to_key - not used
uint64_t amount_to_provide; //amount money that provided by initial creator of tx, used with partially created transactions
uint64_t unlock_time;
tx_destination_entry() : amount(0), minimum_sigs(0), amount_to_provide(0), unlock_time(0){}
tx_destination_entry(uint64_t a, const account_public_address& ad) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0), unlock_time(0){}
tx_destination_entry(uint64_t a, const account_public_address& ad, uint64_t ut) : amount(a), addr(1, ad), minimum_sigs(0), amount_to_provide(0), unlock_time(ut) {}
tx_destination_entry(uint64_t a, const std::list<account_public_address>& addr) : amount(a), addr(addr), minimum_sigs(addr.size()), amount_to_provide(0), unlock_time(0){}
BEGIN_SERIALIZE_OBJECT()
FIELD(amount)
FIELD(addr)
FIELD(minimum_sigs)
FIELD(amount_to_provide)
FIELD(unlock_time)
END_SERIALIZE()
};
template<class extra_type_t>
uint64_t get_tx_x_detail(const transaction& tx)
{
extra_type_t e = AUTO_VAL_INIT(e);
get_type_in_variant_container(tx.extra, e);
return e.v;
}
template<class extra_type_t>
void set_tx_x_detail(transaction& tx, uint64_t v)
{
extra_type_t e = AUTO_VAL_INIT(e);
e.v = v;
update_or_add_field_to_extra(tx.extra, e);
}
uint64_t get_tx_unlock_time(const transaction& tx, uint64_t o_i);
uint64_t get_tx_max_unlock_time(const transaction& tx);
bool get_tx_max_min_unlock_time(const transaction& tx, uint64_t& max_unlock_time, uint64_t& min_unlock_time);
inline uint64_t get_tx_flags(const transaction& tx) { return get_tx_x_detail<etc_tx_details_flags>(tx); }
inline uint64_t get_tx_expiration_time(const transaction& tx) {return get_tx_x_detail<etc_tx_details_expiration_time>(tx); }
inline void set_tx_unlock_time(transaction& tx, uint64_t v) { set_tx_x_detail<etc_tx_details_unlock_time>(tx, v); }
inline void set_tx_flags(transaction& tx, uint64_t v) { set_tx_x_detail<etc_tx_details_flags>(tx, v); }
inline void set_tx_expiration_time(transaction& tx, uint64_t v) { set_tx_x_detail<etc_tx_details_expiration_time>(tx, v); }
account_public_address get_crypt_address_from_destinations(const account_keys& sender_account_keys, const std::vector<tx_destination_entry>& destinations);
bool is_tx_expired(const transaction& tx, uint64_t expiration_ts_median);
uint64_t get_burned_amount(const transaction& tx);
void get_transaction_prefix_hash(const transaction_prefix& tx, crypto::hash& h);
crypto::hash get_transaction_prefix_hash(const transaction_prefix& tx);
bool parse_and_validate_tx_from_blob(const blobdata& tx_blob, transaction& tx, crypto::hash& tx_hash);

View file

@ -179,7 +179,7 @@ namespace currency {
return res.convert_to<wide_difficulty_type>();
}
wide_difficulty_type next_difficulty(vector<uint64_t>& timestamps, vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds)
wide_difficulty_type next_difficulty_1(vector<uint64_t>& timestamps, vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds)
{
// timestamps - first is latest, back - is oldest timestamps
@ -220,4 +220,40 @@ namespace currency {
}
return summ / devider;
}
wide_difficulty_type next_difficulty_2(vector<uint64_t>& timestamps, vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds)
{
// timestamps - first is latest, back - is oldest timestamps
if (timestamps.size() > DIFFICULTY_WINDOW)
{
timestamps.resize(DIFFICULTY_WINDOW);
cumulative_difficulties.resize(DIFFICULTY_WINDOW);
}
size_t length = timestamps.size();
CHECK_AND_ASSERT_MES(length == cumulative_difficulties.size(), 0, "Check \"length == cumulative_difficulties.size()\" failed");
if (length <= 1)
{
return DIFFICULTY_STARTER;
}
static_assert(DIFFICULTY_WINDOW >= 2, "Window is too small");
CHECK_AND_ASSERT_MES(length <= DIFFICULTY_WINDOW, 0, "length <= DIFFICULTY_WINDOW check failed, length=" << length);
sort(timestamps.begin(), timestamps.end(), std::greater<uint64_t>());
static_assert(2 * DIFFICULTY_CUT <= DIFFICULTY_WINDOW - 2, "Cut length is too large");
wide_difficulty_type dif_slow = get_adjustment_for_zone(timestamps, cumulative_difficulties, target_seconds, DIFFICULTY_WINDOW, DIFFICULTY_CUT / 2, DIFFICULTY_CUT / 2);
wide_difficulty_type dif_medium = get_adjustment_for_zone(timestamps, cumulative_difficulties, target_seconds, DIFFICULTY_WINDOW / 3, DIFFICULTY_CUT / 8, DIFFICULTY_CUT / 12);
uint64_t devider = 1;
wide_difficulty_type summ = dif_slow;
if (dif_medium != 0)
{
summ += dif_medium;
++devider;
}
return summ / devider;
}
}

View file

@ -19,7 +19,8 @@ namespace currency
typedef boost::multiprecision::uint128_t wide_difficulty_type;
bool check_hash(const crypto::hash &hash, wide_difficulty_type difficulty);
wide_difficulty_type next_difficulty(std::vector<std::uint64_t>& timestamps, std::vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds);
wide_difficulty_type next_difficulty_1(std::vector<std::uint64_t>& timestamps, std::vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds);
wide_difficulty_type next_difficulty_2(std::vector<std::uint64_t>& timestamps, std::vector<wide_difficulty_type>& cumulative_difficulties, size_t target_seconds);
uint64_t difficulty_to_boundary(wide_difficulty_type difficulty);
void difficulty_to_boundary_long(wide_difficulty_type difficulty, crypto::hash& result);
}

View file

@ -100,7 +100,7 @@ int main(int argc, char* argv[])
#endif
log_space::get_set_log_detalisation_level(true, LOG_LEVEL_2);
log_space::log_singletone::add_logger(LOGGER_CONSOLE, NULL, NULL);
log_space::log_singletone::enable_channels("core,currency_protocol,tx_pool,wallet");
log_space::log_singletone::enable_channels("core,currency_protocol,tx_pool,wallet,lmdb");
LOG_PRINT_L0("Starting...");
tools::signal_handler::install_fatal([](int sig_number, void* address) {

View file

@ -68,6 +68,7 @@ public:
m_cmd_binder.set_handler("print_block_from_hex_blob", boost::bind(&daemon_commands_handler::print_block_from_hex_blob, this, _1), "Unserialize block from hex binary data to json-like representation");
m_cmd_binder.set_handler("print_tx_from_hex_blob", boost::bind(&daemon_commands_handler::print_tx_from_hex_blob, this, _1), "Unserialize transaction from hex binary data to json-like representation");
m_cmd_binder.set_handler("print_tx_outputs_usage", boost::bind(&daemon_commands_handler::print_tx_outputs_usage, this, _1), "Analyse if tx outputs for involved in subsequent transactions");
m_cmd_binder.set_handler("print_difficulties_of_last_n_blocks", boost::bind(&daemon_commands_handler::print_difficulties_of_last_n_blocks, this, _1), "Print difficulties of last n blocks");
}
@ -696,8 +697,26 @@ private:
m_srv.get_payload_object().get_core().get_blockchain_storage().print_tx_outputs_lookup(tx_hash);
return true;
}
//--------------------------------------------------------------------------------
bool print_difficulties_of_last_n_blocks(const std::vector<std::string>& args)
{
if (args.empty())
{
std::cout << "expected: n - number of blocks to read" << std::endl;
return true;
}
const std::string& amount = args.front();
uint64_t n = 0;
if (!epee::string_tools::get_xtype_from_string(n, amount))
{
std::cout << "unable to convert to number '" << amount << "'" << std::endl;
return true;
}
m_srv.get_payload_object().get_core().get_blockchain_storage().print_last_n_difficulty_numbers(n);
return true;
}
//--------------------------------------------------------------------------------
bool print_pool(const std::vector<std::string>& args)
{

View file

@ -798,6 +798,7 @@ namespace currency
currency::pos_entry pe = AUTO_VAL_INIT(pe);
pe.amount = req.pos_amount;
pe.index = req.pos_index;
pe.stake_unlock_time = req.stake_unlock_time;
//pe.keyimage key image will be set in the wallet
//pe.wallet_index is not included in serialization map, TODO: refactoring here

View file

@ -778,6 +778,7 @@ namespace currency
bool pos_block; //is pos block
uint64_t pos_amount; //
uint64_t pos_index; //
uint64_t stake_unlock_time;
BEGIN_KV_SERIALIZE_MAP()
KV_SERIALIZE(extra_text)
@ -786,6 +787,7 @@ namespace currency
KV_SERIALIZE(pos_block)
KV_SERIALIZE(pos_amount)
KV_SERIALIZE(pos_index)
KV_SERIALIZE(stake_unlock_time)
END_KV_SERIALIZE_MAP()
};

View file

@ -80,7 +80,7 @@ namespace
#define HR_TO_STREAM_IN_MHS_3P(hr) std::fixed << std::setprecision(3) << hr / 1000000.0
// debug stuff
#define DBG_NETWORK_DIFFICULTY 0 // if non-zero: use this value as net difficulty when checking shares (useful for debugging on testnet, recommended value is 1600000000ull)
#define DBG_NETWORK_DIFFICULTY 0 // if non-zero: use this value as net difficulty when checking shares (useful for debugging on testnet, recommended value is 3000000000ull)
#define DBG_CORE_ALWAYS_SYNCRONIZED 0 // if set to 1: allows the server to start even if the core is not syncronized, useful for debugging with --offline-mode
#define STRINGIZE_DETAIL(x) #x
#define STRINGIZE(x) STRINGIZE_DETAIL(x)

View file

@ -33,6 +33,35 @@ using namespace currency;
ENABLE_CHANNEL_BY_DEFAULT("wallet")
namespace tools
{
//---------------------------------------------------------------
uint64_t wallet2::get_max_unlock_time_from_receive_indices(const currency::transaction& tx, const money_transfer2_details& td)
{
uint64_t max_unlock_time = 0;
// etc_tx_details_expiration_time have priority over etc_tx_details_expiration_time2
uint64_t major_unlock_time = get_tx_x_detail<etc_tx_details_unlock_time>(tx);
if (major_unlock_time)
return major_unlock_time;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
get_type_in_variant_container(tx.extra, ut2);
if (!ut2.unlock_time_array.size())
return 0;
CHECK_AND_ASSERT_THROW_MES(ut2.unlock_time_array.size() == tx.vout.size(), "Internal error: wrong tx transfer details: ut2.unlock_time_array.size()" << ut2.unlock_time_array.size() << " is not equal transaction outputs vector size=" << tx.vout.size());
for (auto ri : td.receive_indices)
{
CHECK_AND_ASSERT_THROW_MES(ri < tx.vout.size(), "Internal error: wrong tx transfer details: reciev index=" << ri << " is greater than transaction outputs vector " << tx.vout.size());
if (tx.vout[ri].target.type() == typeid(currency::txout_to_key))
{
//update unlock_time if needed
if (ut2.unlock_time_array[ri] > max_unlock_time)
max_unlock_time = ut2.unlock_time_array[ri];
}
}
return max_unlock_time;
}
//----------------------------------------------------------------------------------------------------
void wallet2::fill_transfer_details(const currency::transaction& tx, const tools::money_transfer2_details& td, tools::wallet_rpc::wallet_transfer_info_details& res_td) const
{
@ -48,7 +77,9 @@ void wallet2::fill_transfer_details(const currency::transaction& tx, const tools
{
WLT_CHECK_AND_ASSERT_MES(ri < tx.vout.size(), void(), "Internal error: wrong tx transfer details: reciev index=" << ri << " is greater than transaction outputs vector " << tx.vout.size());
if (tx.vout[ri].target.type() == typeid(currency::txout_to_key))
{
res_td.rcv.push_back(tx.vout[ri].amount);
}
}
}
//----------------------------------------------------------------------------------------------------
@ -258,8 +289,14 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
i++;
}
//check for transaction income
/*
collect unlock_time from every output that transfered coins to this account and use maximum of
all values m_payments entry, use this strict policy is required to protect exchanges from being feeded with
useless outputs
*/
uint64_t max_out_unlock_time = 0;
//check for transaction income
crypto::key_derivation derivation = AUTO_VAL_INIT(derivation);
r = lookup_acc_outs(m_account.get_keys(), tx, tx_pub_key, outs, tx_money_got_in_outs, derivation);
THROW_IF_TRUE_WALLET_EX(!r, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
@ -355,6 +392,10 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
if (td.m_key_image != currency::null_ki)
m_key_images[td.m_key_image] = transfer_index;
add_transfer_to_transfers_cache(tx.vout[o].amount, transfer_index);
if (max_out_unlock_time < get_tx_unlock_time(tx, o))
max_out_unlock_time = get_tx_unlock_time(tx, o);
WLT_LOG_L0("Received money, transfer #" << transfer_index << ", amount: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx) << ", at height " << height);
}
else if (tx.vout[o].target.type() == typeid(txout_multisig))
@ -379,7 +420,7 @@ void wallet2::process_new_transaction(const currency::transaction& tx, uint64_t
payment.m_tx_hash = currency::get_transaction_hash(tx);
payment.m_amount = received;
payment.m_block_height = height;
payment.m_unlock_time = currency::get_tx_unlock_time(tx);
payment.m_unlock_time = max_out_unlock_time;
m_payments.emplace(payment_id, payment);
WLT_LOG_L2("Payment found, id (hex): " << epee::string_tools::buff_to_hex_nodelimer(payment_id) << ", tx: " << payment.m_tx_hash << ", amount: " << print_money_brief(payment.m_amount));
}
@ -919,9 +960,9 @@ void wallet2::prepare_wti(wallet_rpc::wallet_transfer_info& wti, uint64_t height
wti.amount = amount;
wti.height = height;
fill_transfer_details(tx, td, wti.td);
wti.unlock_time = get_max_unlock_time_from_receive_indices(tx, td);
wti.timestamp = timestamp;
wti.fee = currency::is_coinbase(tx) ? 0:currency::get_tx_fee(tx);
wti.unlock_time = get_tx_unlock_time(tx);
wti.tx_blob_size = static_cast<uint32_t>(currency::get_object_blobsize(wti.tx));
wti.tx_hash = currency::get_transaction_hash(tx);
wti.is_service = currency::is_service_tx(tx);
@ -1027,6 +1068,8 @@ void wallet2::process_new_blockchain_entry(const currency::block& b, const curre
m_blockchain.push_back(bl_id);
++m_local_bc_height;
m_last_bc_timestamp = b.timestamp;
if (!is_pos_block(b))
m_last_pow_block_h = height;
m_wcallback->on_new_block(height, b);
}
@ -1762,6 +1805,7 @@ bool wallet2::reset_all()
m_last_bc_timestamp = 0;
m_height_of_start_sync = 0;
m_last_sync_percent = 0;
m_last_pow_block_h = 0;
return true;
}
//----------------------------------------------------------------------------------------------------
@ -2396,17 +2440,22 @@ bool wallet2::get_transfer_address(const std::string& adr_str, currency::account
return m_core_proxy->get_transfer_address(adr_str, addr, payment_id);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr)
bool wallet2::is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time)
{
if (!tr.is_spendable())
return false;
//blockchain conditions
if (!is_transfer_unlocked(tr))
if (!is_transfer_unlocked(tr, true, stake_unlock_time))
return false;
//prevent staking of after-last-pow-coins
if (m_blockchain.size() - tr.m_ptx_wallet_info->m_block_height <= m_core_runtime_config.min_coinstake_age)
return false;
if (tr.m_ptx_wallet_info->m_block_height > m_last_pow_block_h)
return false;
return true;
}
//----------------------------------------------------------------------------------------------------
@ -2430,13 +2479,15 @@ bool wallet2::get_pos_entries(currency::COMMAND_RPC_SCAN_POS::request& req)
for (size_t i = 0; i != m_transfers.size(); i++)
{
auto& tr = m_transfers[i];
if (!is_transfer_okay_for_pos(tr))
uint64_t stake_unlock_time = 0;
if (!is_transfer_okay_for_pos(tr, stake_unlock_time))
continue;
currency::pos_entry pe = AUTO_VAL_INIT(pe);
pe.amount = tr.amount();
pe.index = tr.m_global_output_index;
pe.keyimage = tr.m_key_image;
pe.wallet_index = i;
pe.stake_unlock_time = stake_unlock_time;
pe.block_timestamp = tr.m_ptx_wallet_info->m_block_timestamp;
req.pos_entries.push_back(pe);
}
@ -2613,6 +2664,7 @@ bool wallet2::build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request&
tmpl_req.pos_amount = req.pos_entries[rsp.index].amount;
tmpl_req.pos_index = req.pos_entries[rsp.index].index;
tmpl_req.extra_text = m_miner_text_info;
tmpl_req.stake_unlock_time = req.pos_entries[rsp.index].stake_unlock_time;
m_core_proxy->call_COMMAND_RPC_GETBLOCKTEMPLATE(tmpl_req, tmpl_rsp);
WLT_CHECK_AND_ASSERT_MES(tmpl_rsp.status == CORE_RPC_STATUS_OK, false, "Failed to create block template after kernel hash found!");
@ -2695,16 +2747,32 @@ currency::core_runtime_config& wallet2::get_core_runtime_config()
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_unlocked(const transfer_details& td) const
{
uint64_t stub = 0;
return is_transfer_unlocked(td, false, stub);
}
//----------------------------------------------------------------------------------------------------
bool wallet2::is_transfer_unlocked(const transfer_details& td, bool for_pos_mining, uint64_t& stake_lock_time) const
{
if (td.m_flags&WALLET_TRANSFER_DETAIL_FLAG_BLOCKED)
return false;
if (!currency::is_tx_spendtime_unlocked(get_tx_unlock_time(td.m_ptx_wallet_info->m_tx), m_blockchain.size(), m_core_runtime_config.get_core_time()))
if (td.m_ptx_wallet_info->m_block_height + WALLET_DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size())
return false;
if(td.m_ptx_wallet_info->m_block_height + WALLET_DEFAULT_TX_SPENDABLE_AGE > m_blockchain.size())
return false;
uint64_t unlock_time = get_tx_unlock_time(td.m_ptx_wallet_info->m_tx, td.m_internal_output_index);
if (for_pos_mining && m_blockchain.size() > m_core_runtime_config.hard_fork1_starts_after_height)
{
//allowed of staking locked coins with
stake_lock_time = unlock_time;
}
else
{
if (!currency::is_tx_spendtime_unlocked(unlock_time, m_blockchain.size(), m_core_runtime_config.get_core_time()))
return false;
}
return true;
}
//----------------------------------------------------------------------------------------------------

View file

@ -64,6 +64,8 @@ ENABLE_CHANNEL_BY_DEFAULT("wallet");
#define WLT_CHECK_AND_ASSERT_MES_NO_RET(expr, msg) CHECK_AND_ASSERT_MES_NO_RET(expr, "[W:" << m_log_prefix << "]" << msg)
#define WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(cond, msg) THROW_IF_FALSE_WALLET_INT_ERR_EX(cond, "[W:" << m_log_prefix << "]" << msg)
class test_generator;
namespace tools
{
#pragma pack(push, 1)
@ -310,7 +312,8 @@ namespace tools
m_height_of_start_sync(0),
m_last_sync_percent(0),
m_do_rise_transfer(false),
m_watch_only(false)
m_watch_only(false),
m_last_pow_block_h(0)
{};
public:
wallet2() : m_stop(false),
@ -322,7 +325,8 @@ namespace tools
m_fake_outputs_count(0),
m_do_rise_transfer(false),
m_log_prefix("???"),
m_watch_only(false)
m_watch_only(false),
m_last_pow_block_h(0)
{
m_core_runtime_config = currency::get_default_core_runtime_config();
};
@ -615,7 +619,7 @@ namespace tools
return;
}
if (ver < 147)
if (ver < 149)
{
LOG_PRINT_MAGENTA("Wallet file truncated due to old version", LOG_LEVEL_0);
return;
@ -650,6 +654,7 @@ namespace tools
a & m_money_expirations;
a & m_pending_key_images;
a & m_tx_keys;
a & m_last_pow_block_h;
}
@ -661,7 +666,7 @@ namespace tools
//synchronous version of function
bool try_mint_pos();
//for unit tests
friend class test_generator;
friend class ::test_generator;
//next functions in public area only because of test_generator
//TODO: Need refactoring - remove it back to private zone
@ -680,6 +685,7 @@ namespace tools
bool build_minted_block(const currency::COMMAND_RPC_SCAN_POS::request& req, const currency::COMMAND_RPC_SCAN_POS::response& rsp, const currency::account_public_address& miner_address, uint64_t new_block_expected_height = UINT64_MAX);
bool reset_history();
bool is_transfer_unlocked(const transfer_details& td) const;
bool is_transfer_unlocked(const transfer_details& td, bool for_pos_mining, uint64_t& stake_lock_time) const;
void get_mining_history(wallet_rpc::mining_history& hist);
void set_core_runtime_config(const currency::core_runtime_config& pc);
currency::core_runtime_config& get_core_runtime_config();
@ -717,7 +723,7 @@ namespace tools
void finalize_transaction(const finalize_tx_param& ftp, currency::transaction& tx, crypto::secret_key& tx_key, bool broadcast_tx);
std::string get_log_prefix() const { return m_log_prefix; }
static uint64_t get_max_unlock_time_from_receive_indices(const currency::transaction& tx, const money_transfer2_details& td);
private:
void add_transfers_to_expiration_list(const std::vector<uint64_t>& selected_transfers, uint64_t expiration, uint64_t change_amount, const crypto::hash& related_tx_id);
void remove_transfer_from_expiration_list(uint64_t transfer_index);
@ -765,7 +771,7 @@ private:
std::string get_alias_for_address(const std::string& addr);
static bool build_kernel(const currency::pos_entry& pe, const currency::stake_modifier_type& stake_modifier, currency::stake_kernel& kernel, uint64_t& coindays_weight, uint64_t timestamp);
bool is_connected_to_net();
bool is_transfer_okay_for_pos(const transfer_details& tr);
bool is_transfer_okay_for_pos(const transfer_details& tr, uint64_t& stake_unlock_time);
bool scan_unconfirmed_outdate_tx();
const currency::transaction& get_transaction_by_id(const crypto::hash& tx_hash);
void rise_on_transfer2(const wallet_rpc::wallet_transfer_info& wti);
@ -812,6 +818,7 @@ private:
const std::vector<currency::payload_items_v>& decrypted_items, crypto::hash& ms_id, bc_services::contract_private_details& cpd,
const currency::transaction& proposal_template_tx);
void fill_transfer_details(const currency::transaction& tx, const tools::money_transfer2_details& td, tools::wallet_rpc::wallet_transfer_info_details& res_td) const;
void print_source_entry(const currency::tx_source_entry& src) const;
@ -862,6 +869,7 @@ private:
std::shared_ptr<i_wallet2_callback> m_wcallback;
uint64_t m_height_of_start_sync;
uint64_t m_last_sync_percent;
uint64_t m_last_pow_block_h;
currency::core_runtime_config m_core_runtime_config;
escrow_contracts_container m_contracts;
std::list<expiration_entry_info> m_money_expirations;
@ -871,6 +879,8 @@ private:
uint64_t m_fake_outputs_count;
std::string m_miner_text_info;
//this needed to access wallets state in coretests, for creating abnormal blocks and tranmsactions
friend class test_generator;
}; // class wallet2
@ -947,10 +957,11 @@ namespace boost
a & x.contract;
a & x.selected_indicies;
a & x.srv_attachments;
a & x.unlock_time;
//do not store this items in the file since it's quite easy to restore it from original tx
if (Archive::is_loading::value)
{
x.unlock_time = currency::get_tx_unlock_time(x.tx);
x.is_service = currency::is_service_tx(x.tx);
x.is_mixing = currency::is_mixin_tx(x.tx);
x.is_mining = currency::is_coinbase(x.tx);

View file

@ -26,7 +26,7 @@ bool wallet2::validate_escrow_proposal(const wallet_rpc::wallet_transfer_info& w
// I. validate escrow proposal tx
const transaction& escrow_proposal_tx = wti.tx;
uint64_t escrow_proposal_tx_unlock_time = get_tx_unlock_time(escrow_proposal_tx);
uint64_t escrow_proposal_tx_unlock_time = get_tx_max_unlock_time(escrow_proposal_tx);
LOC_CHK(escrow_proposal_tx_unlock_time == 0, "proposal tx unlock time is non-zero: " << escrow_proposal_tx_unlock_time);
uint64_t escrow_proposal_expiration_time = get_tx_expiration_time(escrow_proposal_tx);
@ -66,7 +66,7 @@ bool wallet2::validate_escrow_proposal(const wallet_rpc::wallet_transfer_info& w
uint64_t template_expiration_time = get_tx_expiration_time(prop.tx_template);
LOC_CHK(template_expiration_time != 0, "template has no expiration time");
uint64_t template_unlock_time = get_tx_unlock_time(prop.tx_template);
uint64_t template_unlock_time = get_tx_max_unlock_time(prop.tx_template);
LOC_CHK(template_unlock_time == 0, "template has non-zero unlock time: " << template_unlock_time);
// (3/5) outputs
@ -156,7 +156,7 @@ bool wallet2::validate_escrow_release(const transaction& tx, bool release_type_n
uint64_t expiration_time = get_tx_expiration_time(tx);
LOC_CHK(expiration_time == 0, "tx has non-zero expiration time: " << expiration_time);
uint64_t unlock_time = get_tx_unlock_time(tx);
uint64_t unlock_time = get_tx_max_unlock_time(tx);
LOC_CHK(unlock_time == 0, "tx has non-zero unlock time: " << unlock_time);
tx_service_attachment tsa = AUTO_VAL_INIT(tsa);
@ -285,7 +285,7 @@ bool wallet2::validate_escrow_contract(const wallet_rpc::wallet_transfer_info& w
uint64_t tx_expiration_time = get_tx_expiration_time(wti.tx);
LOC_CHK(tx_expiration_time != 0, "no or zero expiration time specified");
uint64_t tx_unlock_time = get_tx_unlock_time(wti.tx);
uint64_t tx_unlock_time = get_tx_max_unlock_time(wti.tx);
LOC_CHK(tx_unlock_time == 0, "non-zero unlock time: " << tx_unlock_time);
#undef LOC_CHK
@ -341,7 +341,7 @@ bool wallet2::validate_escrow_cancel_release(const currency::transaction& tx, co
uint64_t expiration_time = get_tx_expiration_time(tx);
LOC_CHK(expiration_time != 0, "tx has zero or not specified expiration time");
uint64_t unlock_time = get_tx_unlock_time(tx);
uint64_t unlock_time = get_tx_max_unlock_time(tx);
LOC_CHK(unlock_time == 0, "tx has non-zero unlock time: " << unlock_time);
tx_service_attachment tsa = AUTO_VAL_INIT(tsa);
@ -430,7 +430,7 @@ bool wallet2::validate_escrow_cancel_proposal(const wallet_rpc::wallet_transfer_
uint64_t flags = get_tx_flags(wti.tx);
LOC_CHK(flags == 0, "invalid tx flags: " << flags);
uint64_t unlock_time = get_tx_unlock_time(cancellation_request_tx);
uint64_t unlock_time = get_tx_max_unlock_time(cancellation_request_tx);
LOC_CHK(unlock_time == 0, "invalid unlock time: " << unlock_time);
uint64_t expiration_time = get_tx_expiration_time(cancellation_request_tx);

View file

@ -21,7 +21,7 @@ namespace
for (size_t i = 0; i < new_block_count; ++i)
{
block blk_next;
wide_difficulty_type diffic = next_difficulty(timestamps, cummulative_difficulties, DIFFICULTY_POW_TARGET);
wide_difficulty_type diffic = next_difficulty_1(timestamps, cummulative_difficulties, DIFFICULTY_POW_TARGET);
if (!generator.construct_block_manually(blk_next, blk_prev, miner_account,
test_generator::bf_timestamp | test_generator::bf_diffic, 0, 0, blk_prev.timestamp, crypto::hash(), diffic))
return false;
@ -152,7 +152,7 @@ bool gen_block_invalid_nonce::generate(std::vector<test_event_entry>& events) co
return false;
// Create invalid nonce
wide_difficulty_type diffic = next_difficulty(timestamps, commulative_difficulties, DIFFICULTY_POW_TARGET);
wide_difficulty_type diffic = next_difficulty_1(timestamps, commulative_difficulties, DIFFICULTY_POW_TARGET);
CHECK_AND_ASSERT_MES(diffic > 1, false, "diffic > 1 validation failed");
const block& blk_last = boost::get<block>(events.back());
uint64_t timestamp = blk_last.timestamp;
@ -193,7 +193,7 @@ bool gen_block_unlock_time_is_low::generate(std::vector<test_event_entry>& event
BLOCK_VALIDATION_INIT_GENERATE();
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
currency::set_tx_unlock_time(miner_tx, currency::get_tx_unlock_time(miner_tx) - 1);
currency::set_tx_unlock_time(miner_tx, currency::get_tx_max_unlock_time(miner_tx) - 1);
block blk_1;
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);
@ -209,7 +209,7 @@ bool gen_block_unlock_time_is_high::generate(std::vector<test_event_entry>& even
BLOCK_VALIDATION_INIT_GENERATE();
MAKE_MINER_TX_MANUALLY(miner_tx, blk_0);
set_tx_unlock_time(miner_tx, get_tx_unlock_time(miner_tx) + 1);
set_tx_unlock_time(miner_tx, get_tx_max_unlock_time(miner_tx) + 1);
block blk_1;
generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx);

View file

@ -46,10 +46,19 @@ crypto::signature create_invalid_signature()
const crypto::signature invalid_signature = create_invalid_signature();
test_generator::test_generator()
: m_wallet_test_core_proxy(new wallet_test_core_proxy())
: m_wallet_test_core_proxy(new wallet_test_core_proxy()),
m_do_pos_to_low_timestamp(false),
m_ignore_last_pow_in_wallets(false),
m_last_found_timestamp(0),
m_hardfork_after_heigh(CURRENCY_MAX_BLOCK_NUMBER)
{
}
void test_generator::set_hardfork_height(uint64_t h)
{
m_hardfork_after_heigh = h;
}
void test_generator::get_block_chain(std::vector<const block_info*>& blockchain, const crypto::hash& head, size_t n) const
{
crypto::hash curr = head;
@ -142,6 +151,7 @@ void test_generator::add_block(const currency::block& blk,
get_block_reward(is_pos_block(blk), misc_utils::median(block_sizes), block_size, already_generated_coins, block_reward, currency::get_block_height(blk));
m_blocks_info[get_block_hash(blk)] = block_info(blk, already_generated_coins + block_reward, block_size, cum_diff, tx_list, ks_hash);
LOG_PRINT_MAGENTA("ADDED_BLOCK[" << get_block_hash(blk) << "][" << (is_pos_block(blk)? "PoS":"PoW") <<"][" << get_block_height(blk) << "][cumul_diff:" << cum_diff << "]", LOG_LEVEL_0);
}
void test_generator::add_block_info(const block_info& bi)
@ -191,7 +201,11 @@ bool test_generator::construct_block(currency::block& blk,
const std::list<currency::transaction>& tx_list,
const std::list<currency::account_base>& coin_stake_sources)//in case of PoS block
{
blk.major_version = CURRENT_BLOCK_MAJOR_VERSION;
if (height > m_hardfork_after_heigh)
blk.major_version = CURRENT_BLOCK_MAJOR_VERSION;
else
blk.major_version = BLOCK_MAJOR_VERSION_INITAL;
blk.minor_version = CURRENT_BLOCK_MINOR_VERSION;
blk.timestamp = timestamp;
blk.prev_id = prev_id;
@ -305,11 +319,13 @@ bool test_generator::construct_block(currency::block& blk,
CHECK_AND_ASSERT_MES(r, false, "Failed to find_kernel_and_sign()");
}
uint64_t last_x = get_last_block_of_type(is_pos_block(blk), blocks);
add_block(blk,
txs_size,
block_sizes,
already_generated_coins,
blocks.size() ? blocks.back()->cumul_difficulty + a_diffic: a_diffic,
last_x ? blocks[last_x]->cumul_difficulty + a_diffic: a_diffic,
tx_list,
kernerl_hash);
@ -382,6 +398,7 @@ bool test_generator::build_wallets(const blockchain_vector& blocks,
currency::core_runtime_config pc = cc;
pc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE;
pc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH;
pc.hard_fork1_starts_after_height = m_hardfork_after_heigh;
wallets.back()->set_core_runtime_config(pc);
}
@ -466,33 +483,52 @@ bool test_generator::find_kernel(const std::list<currency::account_base>& accs,
uint64_t& found_timestamp,
crypto::hash& found_kh)
{
bool is_after_hardfork = blck_chain.size() > m_hardfork_after_heigh ? true : false;
uint64_t median_timestamp = get_timestamps_median(blck_chain);
wide_difficulty_type basic_diff = 0;
uint64_t i_last_pow_block = get_last_block_of_type(false, blck_chain);
uint64_t expected_avr_timestamp = blck_chain[i_last_pow_block]->b.timestamp + (blck_chain.size() - i_last_pow_block - 1)*DIFFICULTY_POW_TARGET;
uint64_t starter_timestamp = expected_avr_timestamp - POS_SCAN_WINDOW;
uint64_t i_last_pos_block = get_last_block_of_type(true, blck_chain);
uint64_t last_pos_block_timestamp = 0;
if(i_last_pos_block)
last_pos_block_timestamp = blck_chain[i_last_pos_block]->b.timestamp;
else
last_pos_block_timestamp = blck_chain.back()->b.timestamp - DIFFICULTY_POS_TARGET/2;
uint64_t starter_timestamp = last_pos_block_timestamp + DIFFICULTY_POS_TARGET;
if (starter_timestamp < median_timestamp)
starter_timestamp = median_timestamp;
m_last_found_timestamp = 0;
basic_diff = get_difficulty_for_next_block(blck_chain, false);
//lets try to find block
for (auto& w : wallets)
if (basic_diff < 10)
{
currency::COMMAND_RPC_SCAN_POS::request scan_pos_entries;
bool r = w->get_pos_entries(scan_pos_entries);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to get_pos_entries");
starter_timestamp -= 90;
}
if (m_do_pos_to_low_timestamp)
starter_timestamp += 60;
for (size_t i = 0; i != scan_pos_entries.pos_entries.size(); i++)
//adjust timestamp starting from timestamp%POS_SCAN_STEP = 0
//starter_timestamp = starter_timestamp - POS_SCAN_WINDOW;
starter_timestamp = POS_SCAN_STEP - (starter_timestamp%POS_SCAN_STEP) + starter_timestamp;
for (uint64_t ts = starter_timestamp; ts < starter_timestamp + POS_SCAN_WINDOW/2; ts += POS_SCAN_STEP)
{
//lets try to find block
for (auto& w : wallets)
{
//adjust timestamp starting from timestamp%POS_SCAN_STEP = 0
starter_timestamp = starter_timestamp - POS_SCAN_WINDOW;
starter_timestamp = POS_SCAN_STEP - (starter_timestamp%POS_SCAN_STEP) + starter_timestamp;
//set m_last_pow_block_h to big value, to let wallet to use any available outputs, including the those which is not behind last pow block
if (m_ignore_last_pow_in_wallets)
w->m_last_pow_block_h = CURRENCY_MAX_BLOCK_NUMBER;
for (uint64_t ts = starter_timestamp; ts < expected_avr_timestamp + POS_SCAN_WINDOW; ts += POS_SCAN_STEP)
currency::COMMAND_RPC_SCAN_POS::request scan_pos_entries;
bool r = w->get_pos_entries(scan_pos_entries);
CHECK_AND_ASSERT_THROW_MES(r, "Failed to get_pos_entries");
for (size_t i = 0; i != scan_pos_entries.pos_entries.size(); i++)
{
stake_kernel sk = AUTO_VAL_INIT(sk);
uint64_t coindays_weight = 0;
build_kernel(scan_pos_entries.pos_entries[i].amount,
@ -509,10 +545,23 @@ bool test_generator::find_kernel(const std::list<currency::account_base>& accs,
continue;
else
{
if (m_do_pos_to_low_timestamp)
{
if (!m_last_found_timestamp)
{
m_last_found_timestamp = ts;
continue;
}
if(m_last_found_timestamp >= ts)
continue;
}
//found kernel
LOG_PRINT_GREEN("Found kernel: amount=" << print_money(scan_pos_entries.pos_entries[i].amount)
<< ", index=" << scan_pos_entries.pos_entries[i].index
<< ", key_image" << scan_pos_entries.pos_entries[i].keyimage, LOG_LEVEL_0);
<< ", key_image" << scan_pos_entries.pos_entries[i].keyimage
<< ", diff: " << this_coin_diff, LOG_LEVEL_0);
pe = scan_pos_entries.pos_entries[i];
found_wallet_index = i;
found_kh = kernel_hash;
@ -673,7 +722,7 @@ currency::wide_difficulty_type test_generator::get_difficulty_for_next_block(con
timestamps.push_back(blocks[i]->b.timestamp);
commulative_difficulties.push_back(blocks[i]->cumul_difficulty);
}
return next_difficulty(timestamps, commulative_difficulties, pow ? DIFFICULTY_POW_TARGET : DIFFICULTY_POS_TARGET);
return next_difficulty_1(timestamps, commulative_difficulties, pow ? DIFFICULTY_POW_TARGET : DIFFICULTY_POS_TARGET);
}
currency::wide_difficulty_type test_generator::get_cumul_difficulty_for_next_block(const crypto::hash& head_id, bool pow) const
@ -743,10 +792,19 @@ bool test_generator::construct_block(const std::vector<test_event_entry>& events
const currency::block& blk_prev,
const currency::account_base& miner_acc,
const std::list<currency::transaction>& tx_list,
const std::list<currency::account_base>& coin_stake_sources
)
const std::list<currency::account_base>& coin_stake_sources)
{
uint64_t height = boost::get<txin_gen>(blk_prev.miner_tx.vin.front()).height + 1;
return construct_block(0, events, blk, blk_prev, miner_acc, tx_list, coin_stake_sources);
}
bool test_generator::construct_block(int64_t manual_timestamp_adjustment,
const std::vector<test_event_entry>& events,
currency::block& blk,
const currency::block& blk_prev,
const currency::account_base& miner_acc,
const std::list<currency::transaction>& tx_list,
const std::list<currency::account_base>& coin_stake_sources)
{
uint64_t height = boost::get<txin_gen>(blk_prev.miner_tx.vin[0]).height + 1;
crypto::hash prev_id = get_block_hash(blk_prev);
// Keep push difficulty little up to be sure about PoW hash success
std::vector<currency::block> blockchain;
@ -769,6 +827,7 @@ bool test_generator::construct_block(const std::vector<test_event_entry>& events
block& prev_same_type = blockchain[prev_i];
timestamp = adjust_timestamp_finished ? prev_same_type.timestamp + diff_target : prev_same_type.timestamp + diff_target - diff_up_timestamp_delta;
timestamp = timestamp + manual_timestamp_adjustment;
uint64_t already_generated_coins = get_already_generated_coins(prev_id);
std::vector<size_t> block_sizes;
@ -786,7 +845,7 @@ bool test_generator::construct_block(const std::vector<test_event_entry>& events
size_t txs_sizes/* = 0*/)
{
size_t height = get_block_height(prev_block) + 1;
blk.major_version = actual_params & bf_major_ver ? major_ver : CURRENT_BLOCK_MAJOR_VERSION;
blk.major_version = actual_params & bf_major_ver ? major_ver : BLOCK_MAJOR_VERSION_INITAL;
blk.minor_version = actual_params & bf_minor_ver ? minor_ver : CURRENT_BLOCK_MINOR_VERSION;
blk.timestamp = actual_params & bf_timestamp ? timestamp : (height > 10 ? prev_block.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN: prev_block.timestamp + DIFFICULTY_BLOCKS_ESTIMATE_TIMESPAN-POW_DIFF_UP_TIMESTAMP_DELTA); // Keep difficulty unchanged
blk.prev_id = actual_params & bf_prev_id ? prev_id : get_block_hash(prev_block);
@ -1197,10 +1256,10 @@ bool fill_tx_sources(std::vector<currency::tx_source_entry>& sources, const std:
continue;
if (check_for_unlocktime)
{
if (currency::get_tx_unlock_time(*oi.p_tx) < CURRENCY_MAX_BLOCK_NUMBER)
if (currency::get_tx_max_unlock_time(*oi.p_tx) < CURRENCY_MAX_BLOCK_NUMBER)
{
//interpret as block index
if (currency::get_tx_unlock_time(*oi.p_tx) > blockchain.size())
if (currency::get_tx_max_unlock_time(*oi.p_tx) > blockchain.size())
continue;
}
else
@ -1445,14 +1504,14 @@ bool construct_tx_to_key(const std::vector<test_event_entry>& events,
if (!fill_tx_sources(sources, events, blk_head, from.get_keys(), spending_amount, nmix, check_for_spends, check_for_unlocktime, use_ref_by_id))
return false;
int64_t change = get_sources_total_amount(sources);
boost::multiprecision::int128_t change = get_sources_total_amount(sources);
change -= spending_amount;
if (change < 0)
return false; // should never happen if fill_tx_sources succeded
if (change == 0)
return construct_tx(from.get_keys(), sources, destinations, extr, att, tx, sk, 0, mix_attr);
std::vector<tx_destination_entry> local_dst = destinations;
local_dst.push_back(tx_destination_entry(change, from.get_public_address()));
local_dst.push_back(tx_destination_entry(change.convert_to<uint64_t>(), from.get_public_address()));
return construct_tx(from.get_keys(), sources, local_dst, extr, att, tx, sk, 0, mix_attr);
}

View file

@ -247,7 +247,7 @@ struct offers_count_param
size_t offers_count_raw;
};
class test_chain_unit_enchanced : public test_chain_unit_base
class test_chain_unit_enchanced : virtual public test_chain_unit_base
{
public:
test_chain_unit_enchanced();
@ -345,7 +345,7 @@ public:
currency::wide_difficulty_type cumul_difficulty;
std::vector<currency::transaction> m_transactions;
crypto::hash ks_hash;
};
};
// amount vec_ind, tx_index, out index in tx
typedef std::map<uint64_t, std::vector<std::tuple<size_t, size_t, size_t> > > outputs_index;
@ -473,6 +473,7 @@ public:
bool construct_genesis_block(currency::block& blk,
const currency::account_base& miner_acc,
uint64_t timestamp);
bool construct_block(const std::vector<test_event_entry>& events,
currency::block& blk,
const currency::block& blk_prev,
@ -480,6 +481,14 @@ public:
const std::list<currency::transaction>& tx_list = std::list<currency::transaction>(),
const std::list<currency::account_base>& coin_stake_sources = std::list<currency::account_base>() //in case of PoS block
);
bool construct_block(int64_t manual_timestamp_adjustment,
const std::vector<test_event_entry>& events,
currency::block& blk,
const currency::block& blk_prev,
const currency::account_base& miner_acc,
const std::list<currency::transaction>& tx_list = std::list<currency::transaction>(),
const std::list<currency::account_base>& coin_stake_sources = std::list<currency::account_base>() //in case of PoS block
);
bool construct_block_manually(currency::block& blk, const currency::block& prev_block,
@ -500,8 +509,17 @@ public:
static const test_gentime_settings& get_test_gentime_settings() { return m_test_gentime_settings; }
static void set_test_gentime_settings(const test_gentime_settings& s) { m_test_gentime_settings = s; }
static void set_test_gentime_settings_default() { m_test_gentime_settings = m_test_gentime_settings_default; }
void set_pos_to_low_timestamp(bool do_pos_to_low_timestamp) { m_do_pos_to_low_timestamp = do_pos_to_low_timestamp; }
void set_ignore_last_pow_in_wallets(bool ignore_last_pow_in_wallets) { m_ignore_last_pow_in_wallets = ignore_last_pow_in_wallets; }
void set_hardfork_height(uint64_t h);
private:
bool m_do_pos_to_low_timestamp;
bool m_ignore_last_pow_in_wallets;
uint64_t m_last_found_timestamp;
uint64_t m_hardfork_after_heigh;
std::unordered_map<crypto::hash, block_info> m_blocks_info;
static test_gentime_settings m_test_gentime_settings;
static test_gentime_settings m_test_gentime_settings_default;
@ -956,12 +974,30 @@ void append_vector_by_another_vector(U& dst, const V& src)
VEC_EVENTS.push_back(BLK_NAME); \
PRINT_EVENT_NO(VEC_EVENTS);
#define MAKE_NEXT_BLOCK_TIMESTAMP_ADJUSTMENT(ADJ, VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
generator.construct_block(ADJ, VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC); \
VEC_EVENTS.push_back(BLK_NAME); \
PRINT_EVENT_NO(VEC_EVENTS);
#define MAKE_NEXT_POS_BLOCK(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, MINERS_ACC_LIST) \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, std::list<currency::transaction>(), MINERS_ACC_LIST); \
VEC_EVENTS.push_back(BLK_NAME); \
PRINT_EVENT_NO(VEC_EVENTS)
#define MAKE_NEXT_POS_BLOCK_TX1(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, MINERS_ACC_LIST, TX_1) \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
{ \
std::list<currency::transaction>tx_list; \
tx_list.push_back(TX_1); \
generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, tx_list, MINERS_ACC_LIST); \
} \
VEC_EVENTS.push_back(BLK_NAME); \
PRINT_EVENT_NO(VEC_EVENTS)
#define MAKE_NEXT_BLOCK_NO_ADD(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC) \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC); \
@ -982,6 +1018,7 @@ void append_vector_by_another_vector(U& dst, const V& src)
VEC_EVENTS.push_back(BLK_NAME); \
PRINT_EVENT_NO(VEC_EVENTS)
#define MAKE_NEXT_BLOCK_TX_LIST(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST) \
currency::block BLK_NAME = AUTO_VAL_INIT(BLK_NAME); \
generator.construct_block(VEC_EVENTS, BLK_NAME, PREV_BLOCK, MINER_ACC, TXLIST); \

View file

@ -936,6 +936,17 @@ int main(int argc, char* argv[])
GENERATE_AND_PLAY(gen_uint_overflow_1);
GENERATE_AND_PLAY(gen_uint_overflow_2);
// Hardfok1 tests
GENERATE_AND_PLAY(before_hard_fork_1_cumulative_difficulty);
GENERATE_AND_PLAY(inthe_middle_hard_fork_1_cumulative_difficulty);
GENERATE_AND_PLAY(after_hard_fork_1_cumulative_difficulty);
GENERATE_AND_PLAY(hard_fork_1_locked_mining_test);
GENERATE_AND_PLAY(hard_fork_1_bad_pos_source);
GENERATE_AND_PLAY(hard_fork_1_unlock_time_2_in_normal_tx);
GENERATE_AND_PLAY(hard_fork_1_unlock_time_2_in_coinbase);
GENERATE_AND_PLAY(hard_fork_1_chain_switch_pow_only);
GENERATE_AND_PLAY(hard_fork_1_checkpoint_basic_test);
//GENERATE_AND_PLAY(gen_block_reward); */

View file

@ -33,3 +33,7 @@
#include "escrow_wallet_altchain_test.h"
#include "misc_tests.h"
#include "emission_test.h"
#include "hard_fork_1_locked_pos_test.h"
#include "hard_fork_1_consensus_test.h"
#include "hard_fork_1_bad_pos_source.h"
#include "hard_fork_1.h"

View file

@ -7,7 +7,7 @@
#include "chaingen.h"
#include "random_helper.h"
struct checkpoints_test : public test_chain_unit_enchanced
struct checkpoints_test : virtual public test_chain_unit_enchanced
{
checkpoints_test();

View file

@ -0,0 +1,423 @@
// Copyright (c) 2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chaingen.h"
#include "hard_fork_1.h"
#include "pos_block_builder.h"
//#include "tx_builder.h"
//#include "random_helper.h"
using namespace currency;
hard_fork_1_base_test::hard_fork_1_base_test(size_t hardfork_height)
: m_hardfork_height(hardfork_height)
{
REGISTER_CALLBACK_METHOD(hard_fork_1_base_test, configure_core);
}
bool hard_fork_1_base_test::configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config();
pc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE;
pc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH;
pc.hard_fork1_starts_after_height = m_hardfork_height;
c.get_blockchain_storage().set_core_runtime_config(pc);
return true;
}
//------------------------------------------------------------------------------
hard_fork_1_unlock_time_2_in_normal_tx::hard_fork_1_unlock_time_2_in_normal_tx()
: hard_fork_1_base_test(12)
{
REGISTER_CALLBACK_METHOD(hard_fork_1_unlock_time_2_in_normal_tx, configure_core);
}
bool hard_fork_1_unlock_time_2_in_normal_tx::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make sure etc_tx_details_unlock_time2 can be used in normal (non-coinbase) tx
// only after hardfork 1
bool r = false;
GENERATE_ACCOUNT(miner_acc);
GENERATE_ACCOUNT(alice_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
generator.set_hardfork_height(m_hardfork_height);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
//
// before hardfork 1
//
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_0r, miner_acc, alice_acc, MK_TEST_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations), false, "");
// set unlock_time_2
std::vector<extra_v> extra;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.resize(destinations.size());
ut2.unlock_time_array[0] = 1; // not zero, unlocked from block 1
extra.push_back(ut2);
transaction tx_0 = AUTO_VAL_INIT(tx_0);
crypto::secret_key tx_sec_key;
r = construct_tx(miner_acc.get_keys(), sources, destinations, extra, empty_attachment, tx_0, tx_sec_key, 0 /* unlock time 1 is zero and thus will not be set */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
// tx_0 should be accepted
events.push_back(tx_0);
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
DO_CALLBACK(events, "mark_invalid_block");
MAKE_NEXT_BLOCK_TX1(events, blk_1_bad, blk_0r, miner_acc, tx_0);
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
DO_CALLBACK(events, "clear_tx_pool");
// make another tx with the same inputs and extra (tx_0 was rejected so inputs can be reused)
transaction tx_0a = AUTO_VAL_INIT(tx_0a);
r = construct_tx(miner_acc.get_keys(), sources, destinations, extra, empty_attachment, tx_0a, tx_sec_key, 0 /* unlock time 1 is zero and thus will not be set */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
// tx_0a should be accepted as well
events.push_back(tx_0a);
// make an alternative block with it and make sure it is rejected
MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner_acc);
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
DO_CALLBACK(events, "mark_invalid_block");
MAKE_NEXT_BLOCK_TX1(events, blk_1_alt_bad, blk_0r, miner_acc, tx_0a); // this alt block should be rejected because of tx_0a
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
DO_CALLBACK(events, "clear_tx_pool");
// okay, go for a hardfork
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc); // hardfork should happen here
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc);
// make sure hardfork went okay
CHECK_AND_ASSERT_MES(blk_2.major_version != CURRENT_BLOCK_MAJOR_VERSION && blk_3.major_version == CURRENT_BLOCK_MAJOR_VERSION, false, "hardfork did not happen as expected");
//
// after hardfork 1
//
sources.clear();
destinations.clear();
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_3, miner_acc, alice_acc, MK_TEST_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations), false, "");
// set unlock_time_2
extra.clear();
ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.resize(destinations.size());
ut2.unlock_time_array[0] = 1; // not zero, unlocked from block 1
extra.push_back(ut2);
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx(miner_acc.get_keys(), sources, destinations, extra, empty_attachment, tx_1, tx_sec_key, 0 /* unlock time 1 is zero and not set */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
events.push_back(tx_1);
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
MAKE_NEXT_BLOCK_TX1(events, blk_4, blk_3, miner_acc, tx_1); // block with tx_1 should be accepted
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(0));
// do the same check for alt block
sources.clear();
destinations.clear();
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_4, miner_acc, alice_acc, MK_TEST_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations), false, "");
extra.clear();
ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.resize(destinations.size());
ut2.unlock_time_array[0] = 1; // not zero, unlocked from block 1
extra.push_back(ut2);
transaction tx_1a = AUTO_VAL_INIT(tx_1a);
r = construct_tx(miner_acc.get_keys(), sources, destinations, extra, empty_attachment, tx_1a, tx_sec_key, 0 /* unlock time 1 is zero and not set */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
events.push_back(tx_1a);
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
MAKE_NEXT_BLOCK(events, blk_5, blk_4, miner_acc);
MAKE_NEXT_BLOCK_TX1(events, blk_5a, blk_4, miner_acc, tx_1a); // alt block with tx_1a should be accepted
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1)); // tx is still in the pool
// switch chains
MAKE_NEXT_BLOCK(events, blk_6a, blk_5a, miner_acc);
MAKE_NEXT_BLOCK(events, blk_7a, blk_6a, miner_acc);
// make sure switching really happened
DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(blk_7a));
// and tx_1a has gone
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(0));
return true;
}
//------------------------------------------------------------------------------
hard_fork_1_unlock_time_2_in_coinbase::hard_fork_1_unlock_time_2_in_coinbase()
: hard_fork_1_base_test(3)
{
}
bool hard_fork_1_unlock_time_2_in_coinbase::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make sure etc_tx_details_unlock_time2 can be used in-coinbase txs
// only after hardfork 1
bool r = false;
GENERATE_ACCOUNT(miner_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
generator.set_hardfork_height(m_hardfork_height);
DO_CALLBACK(events, "configure_core");
// before hardfork 1
MAKE_NEXT_BLOCK(events, blk_1, blk_0, miner_acc);
events.pop_back(); // remove blk_1
// remove etc_tx_details_unlock_time entries from miner_tx
blk_1.miner_tx.extra.erase(std::remove_if(blk_1.miner_tx.extra.begin(), blk_1.miner_tx.extra.end(), [](extra_v& extra_element) { return extra_element.type() == typeid(etc_tx_details_unlock_time); }), blk_1.miner_tx.extra.end());
// add etc_tx_details_unlock_time2 entry
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.resize(blk_1.miner_tx.vout.size());
ut2.unlock_time_array[0] = get_block_height(blk_1) + CURRENCY_MINED_MONEY_UNLOCK_WINDOW;
blk_1.miner_tx.extra.push_back(ut2);
DO_CALLBACK(events, "mark_invalid_block");
// add blk_1 with modified miner tx
events.push_back(blk_1);
generator.add_block_info(blk_1, std::list<transaction>()); // add modified block info
MAKE_NEXT_BLOCK(events, blk_1a, blk_0, miner_acc);
MAKE_NEXT_BLOCK(events, blk_2, blk_1a, miner_acc);
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc); // hardfork should happen here
MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_acc);
// make sure hardfork went okay
CHECK_AND_ASSERT_MES(blk_3.major_version != CURRENT_BLOCK_MAJOR_VERSION && blk_4.major_version == CURRENT_BLOCK_MAJOR_VERSION, false, "hardfork did not happen as expected");
// after hardfork 1
MAKE_NEXT_BLOCK(events, blk_5, blk_4, miner_acc);
wide_difficulty_type diff = generator.get_block_difficulty(get_block_hash(blk_5)); // remember block difficulty for nonce searching after modification
events.pop_back();
// remove etc_tx_details_unlock_time entries from miner_tx
blk_5.miner_tx.extra.erase(std::remove_if(blk_5.miner_tx.extra.begin(), blk_5.miner_tx.extra.end(), [](extra_v& extra_element) { return extra_element.type() == typeid(etc_tx_details_unlock_time); }), blk_5.miner_tx.extra.end());
// add etc_tx_details_unlock_time2 entry
ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.resize(blk_5.miner_tx.vout.size());
ut2.unlock_time_array[0] = get_block_height(blk_5) + CURRENCY_MINED_MONEY_UNLOCK_WINDOW;
blk_5.miner_tx.extra.push_back(ut2);
miner::find_nonce_for_given_block(blk_5, diff, get_block_height(blk_5));
// add blk_5 with modified miner tx
events.push_back(blk_5);
generator.add_block_info(blk_5, std::list<transaction>()); // add modified block info
MAKE_NEXT_BLOCK(events, blk_6, blk_5, miner_acc);
return true;
}
//------------------------------------------------------------------------------
hard_fork_1_chain_switch_pow_only::hard_fork_1_chain_switch_pow_only()
: hard_fork_1_base_test(13)
{
}
bool hard_fork_1_chain_switch_pow_only::generate(std::vector<test_event_entry>& events) const
{
// Test idea: make sure chain switches without PoS before and after hardfork
bool r = false;
GENERATE_ACCOUNT(miner_acc);
GENERATE_ACCOUNT(alice_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
generator.set_hardfork_height(m_hardfork_height);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
//
// before hardfork 1
//
MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner_acc);
MAKE_NEXT_BLOCK(events, blk_1a, blk_0r, miner_acc);
MAKE_NEXT_BLOCK(events, blk_2a, blk_1a, miner_acc);
// make sure switch happened
DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(blk_2a));
MAKE_NEXT_BLOCK(events, blk_3a, blk_2a, miner_acc); // hardfork should happen here
MAKE_NEXT_BLOCK(events, blk_4a, blk_3a, miner_acc);
// make sure hardfork went okay
CHECK_AND_ASSERT_MES(blk_3a.major_version != CURRENT_BLOCK_MAJOR_VERSION && blk_4a.major_version == CURRENT_BLOCK_MAJOR_VERSION, false, "hardfork did not happen as expected");
//
// after hardfork 1
//
MAKE_NEXT_BLOCK(events, blk_5a, blk_4a, miner_acc);
MAKE_NEXT_BLOCK(events, blk_5b, blk_4a, miner_acc); // alternative chain B
// switch chains
MAKE_NEXT_BLOCK(events, blk_6b, blk_5b, miner_acc);
MAKE_NEXT_BLOCK(events, blk_7b, blk_6b, miner_acc);
// make sure switch happened
DO_CALLBACK_PARAMS(events, "check_top_block", params_top_block(blk_7b));
return true;
}
//------------------------------------------------------------------------------
hard_fork_1_checkpoint_basic_test::hard_fork_1_checkpoint_basic_test()
: hard_fork_1_base_test(13)
, checkpoints_test()
{
}
bool hard_fork_1_checkpoint_basic_test::generate(std::vector<test_event_entry>& events) const
{
bool r = false;
GENERATE_ACCOUNT(miner_acc);
GENERATE_ACCOUNT(alice_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
generator.set_hardfork_height(m_hardfork_height);
DO_CALLBACK(events, "configure_core");
DO_CALLBACK_PARAMS(events, "set_checkpoint", params_checkpoint(CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 2));
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW);
//
// before hardfork 1
//
std::vector<tx_source_entry> sources;
std::vector<tx_destination_entry> destinations;
CHECK_AND_ASSERT_MES(fill_tx_sources_and_destinations(events, blk_0r, miner_acc, alice_acc, MK_TEST_COINS(1), TESTS_DEFAULT_FEE, 0, sources, destinations), false, "");
// set unlock_time_2, should be rejected before hardfork 1
std::vector<extra_v> extra;
etc_tx_details_unlock_time2 ut2 = AUTO_VAL_INIT(ut2);
ut2.unlock_time_array.resize(destinations.size());
ut2.unlock_time_array[0] = 1; // not zero, unlocked from block 1
extra.push_back(ut2);
transaction tx_0 = AUTO_VAL_INIT(tx_0);
crypto::secret_key tx_sec_key;
r = construct_tx(miner_acc.get_keys(), sources, destinations, extra, empty_attachment, tx_0, tx_sec_key, 0 /* unlock time 1 is zero and thus will not be set */);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
// tx_0 should be accepted
events.push_back(tx_0);
DO_CALLBACK(events, "mark_invalid_block");
MAKE_NEXT_BLOCK_TX1(events, blk_1_bad, blk_0r, miner_acc, tx_0); // should be rejected because of tx_0
DO_CALLBACK_PARAMS(events, "check_tx_pool_count", static_cast<size_t>(1));
DO_CALLBACK(events, "clear_tx_pool");
MAKE_NEXT_BLOCK(events, blk_1, blk_0r, miner_acc);
MAKE_NEXT_BLOCK(events, blk_2, blk_1, miner_acc); // <-- checkpoint
MAKE_NEXT_BLOCK(events, blk_3, blk_2, miner_acc); // <-- hard fork
MAKE_NEXT_BLOCK(events, blk_4, blk_3, miner_acc);
// make sure hardfork went okay
CHECK_AND_ASSERT_MES(blk_3.major_version != CURRENT_BLOCK_MAJOR_VERSION && blk_4.major_version == CURRENT_BLOCK_MAJOR_VERSION, false, "hardfork did not happen as expected");
return true;
}
//------------------------------------------------------------------------------
hard_fork_1_pos_and_locked_coins::hard_fork_1_pos_and_locked_coins()
: hard_fork_1_base_test(13) // hardfork height
, m_unique_amount(TESTS_DEFAULT_FEE * 9)
{
REGISTER_CALLBACK_METHOD(hard_fork_1_pos_and_locked_coins, check_outputs_with_unique_amount);
}
bool hard_fork_1_pos_and_locked_coins::generate(std::vector<test_event_entry>& events) const
{
bool r = false;
GENERATE_ACCOUNT(miner_acc);
GENERATE_ACCOUNT(alice_acc);
MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, test_core_time::get_time());
generator.set_hardfork_height(m_hardfork_height);
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
DO_CALLBACK_PARAMS(events, "check_outputs_with_unique_amount", static_cast<size_t>(0));
// create few locked outputs in the blockchain with unique amount
std::vector<extra_v> extra;
etc_tx_details_unlock_time ut = AUTO_VAL_INIT(ut);
ut.v = 100; // locked until block 100
extra.push_back(ut);
std::vector<tx_destination_entry> destinations;
for (size_t i = 0; i < 5; ++i)
destinations.push_back(tx_destination_entry(m_unique_amount, alice_acc.get_public_address()));
transaction tx_0 = AUTO_VAL_INIT(tx_0);
r = construct_tx_to_key(events, tx_0, blk_0r, miner_acc, destinations, TESTS_DEFAULT_FEE, 0, 0, extra);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
events.push_back(tx_0);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_0);
DO_CALLBACK_PARAMS(events, "check_outputs_with_unique_amount", static_cast<size_t>(5));
block blk_0a;
{
crypto::hash prev_id = get_block_hash(blk_0);
size_t height = get_block_height(blk_0) + 1;
currency::wide_difficulty_type diff = generator.get_difficulty_for_next_block(prev_id, false);
const transaction& stake = blk_0.miner_tx;
crypto::public_key stake_tx_pub_key = get_tx_pub_key_from_extra(stake);
size_t stake_output_idx = 0;
size_t stake_output_gidx = 0;
uint64_t stake_output_amount = stake.vout[stake_output_idx].amount;
crypto::key_image stake_output_key_image;
keypair kp;
generate_key_image_helper(miner_acc.get_keys(), stake_tx_pub_key, stake_output_idx, kp, stake_output_key_image);
crypto::public_key stake_output_pubkey = boost::get<txout_to_key>(stake.vout[stake_output_idx].target).key;
pos_block_builder pb;
pb.step1_init_header(height, prev_id);
pb.step2_set_txs(std::vector<transaction>());
pb.step3_build_stake_kernel(stake_output_amount, stake_output_gidx, stake_output_key_image, diff, prev_id, null_hash, blk_0r.timestamp);
pb.step4_generate_coinbase_tx(generator.get_timestamps_median(prev_id), generator.get_already_generated_coins(blk_0r), miner_acc.get_public_address());
pb.step5_sign(stake_tx_pub_key, stake_output_idx, stake_output_pubkey, miner_acc);
blk_0a = pb.m_block;
}
return true;
}
bool hard_fork_1_pos_and_locked_coins::check_outputs_with_unique_amount(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
size_t expected_outputs_count = 0;
const std::string& params = boost::get<callback_entry>(events[ev_index]).callback_params;
CHECK_AND_ASSERT_MES(epee::string_tools::hex_to_pod(params, expected_outputs_count), false, "hex_to_pod failed, params = " << params);
std::list<crypto::public_key> pub_keys;
bool r = c.get_outs(m_unique_amount, pub_keys);
CHECK_AND_ASSERT_MES(r && pub_keys.size() == expected_outputs_count, false, "amount " << print_money_brief(m_unique_amount) << ": " << pub_keys.size() << " != " << expected_outputs_count);
return true;
}

View file

@ -0,0 +1,50 @@
// Copyright (c) 2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "chaingen.h"
//#include "wallet_tests_basic.h"
#include "checkpoints_tests.h"
struct hard_fork_1_base_test : virtual public test_chain_unit_enchanced
{
hard_fork_1_base_test(size_t hardfork_height);
bool configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
size_t m_hardfork_height;
};
struct hard_fork_1_unlock_time_2_in_normal_tx : public hard_fork_1_base_test
{
hard_fork_1_unlock_time_2_in_normal_tx();
bool generate(std::vector<test_event_entry>& events) const;
};
struct hard_fork_1_unlock_time_2_in_coinbase : public hard_fork_1_base_test
{
hard_fork_1_unlock_time_2_in_coinbase();
bool generate(std::vector<test_event_entry>& events) const;
};
struct hard_fork_1_chain_switch_pow_only : public hard_fork_1_base_test
{
hard_fork_1_chain_switch_pow_only();
bool generate(std::vector<test_event_entry>& events) const;
};
struct hard_fork_1_checkpoint_basic_test : public hard_fork_1_base_test, public checkpoints_test
{
hard_fork_1_checkpoint_basic_test();
bool generate(std::vector<test_event_entry>& events) const;
};
struct hard_fork_1_pos_and_locked_coins : public hard_fork_1_base_test
{
hard_fork_1_pos_and_locked_coins();
bool generate(std::vector<test_event_entry>& events) const;
bool check_outputs_with_unique_amount(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
uint64_t m_unique_amount;
};

View file

@ -0,0 +1,93 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chaingen.h"
#include "pos_validation.h"
#include "tx_builder.h"
#include "hard_fork_1_bad_pos_source.h"
#include "random_helper.h"
using namespace epee;
using namespace crypto;
using namespace currency;
hard_fork_1_bad_pos_source::hard_fork_1_bad_pos_source()
{
REGISTER_CALLBACK_METHOD(hard_fork_1_bad_pos_source, c1);
REGISTER_CALLBACK_METHOD(hard_fork_1_bad_pos_source, configure_core);
}
bool hard_fork_1_bad_pos_source::generate(std::vector<test_event_entry>& events) const
{
random_state_test_restorer::reset_random();
GENERATE_ACCOUNT(preminer_acc);
GENERATE_ACCOUNT(miner_acc);
GENERATE_ACCOUNT(pos_miner_acc_before_pow);
GENERATE_ACCOUNT(pos_miner_acc_after_pow);
m_accounts.push_back(miner_acc);
m_accounts.push_back(pos_miner_acc_before_pow);
m_accounts.push_back(pos_miner_acc_after_pow);
std::list<account_base> miner_acc_lst(1, miner_acc);
MAKE_GENESIS_BLOCK(events, blk_0, preminer_acc, 1564434616);
generator.set_hardfork_height(get_hardfork_height());
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 5);
generator.set_pos_to_low_timestamp(true);
MAKE_TX(events, tx_1, miner_acc, pos_miner_acc_before_pow, 1000000000000, blk_0r);
MAKE_NEXT_BLOCK_TX1(events, blk_pow_tx1, blk_0r, miner_acc, tx_1);
MAKE_TX(events, tx_2, preminer_acc, pos_miner_acc_after_pow, 1000000000000, blk_pow_tx1);
MAKE_NEXT_POS_BLOCK_TX1(events, blk_pos_tx2, blk_pow_tx1, miner_acc, miner_acc_lst, tx_2);
block last_block = blk_pos_tx2;
for (size_t i = 0; i != CURRENCY_MINED_MONEY_UNLOCK_WINDOW; i++)
{
MAKE_NEXT_POS_BLOCK(events, next_blk_pos, last_block, miner_acc, miner_acc_lst);
events.push_back(event_core_time(next_blk_pos.timestamp - 10));
last_block = next_blk_pos;
}
std::list<currency::account_base> accounts_before_pow;
accounts_before_pow.push_back(pos_miner_acc_before_pow);
//let's try to mint PoW block from account
MAKE_NEXT_POS_BLOCK(events, next_blk_pos2, last_block, miner_acc, accounts_before_pow);
std::list<currency::account_base> accounts_after_pow;
accounts_after_pow.push_back(pos_miner_acc_after_pow);
//let's try to mint PoW block from account
generator.set_ignore_last_pow_in_wallets(true);
DO_CALLBACK(events, "mark_invalid_block");
MAKE_NEXT_POS_BLOCK(events, next_blk_pos3, next_blk_pos2, miner_acc, accounts_after_pow);
DO_CALLBACK(events, "c1");
return true;
}
bool hard_fork_1_bad_pos_source::configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config();
pc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE; //four blocks
pc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH; //four blocks
pc.hard_fork1_starts_after_height = get_hardfork_height();
c.get_blockchain_storage().set_core_runtime_config(pc);
return true;
}
bool hard_fork_1_bad_pos_source::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
uint64_t h = c.get_blockchain_storage().get_current_blockchain_size();
CHECK_AND_ASSERT_MES(h == 29, false, "locked pos block is not accepted");
return true;
}
uint64_t hard_fork_1_bad_pos_source::get_hardfork_height()const
{
return 10;
}

View file

@ -0,0 +1,18 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "chaingen.h"
#include "wallet_tests_basic.h"
struct hard_fork_1_bad_pos_source : public wallet_test
{
hard_fork_1_bad_pos_source();
bool generate(std::vector<test_event_entry>& events) const;
bool configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
virtual bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
virtual uint64_t get_hardfork_height()const;
};

View file

@ -0,0 +1,119 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chaingen.h"
#include "pos_validation.h"
#include "tx_builder.h"
#include "hard_fork_1_consensus_test.h"
#include "random_helper.h"
using namespace epee;
using namespace crypto;
using namespace currency;
hard_fork_1_cumulative_difficulty_base::hard_fork_1_cumulative_difficulty_base()
{
REGISTER_CALLBACK_METHOD(hard_fork_1_cumulative_difficulty_base, c1);
REGISTER_CALLBACK_METHOD(hard_fork_1_cumulative_difficulty_base, configure_core);
}
bool hard_fork_1_cumulative_difficulty_base::generate(std::vector<test_event_entry>& events) const
{
random_state_test_restorer::reset_random();
GENERATE_ACCOUNT(preminer_acc);
GENERATE_ACCOUNT(miner_acc);
m_accounts.push_back(miner_acc);
std::list<account_base> miner_acc_lst(1, miner_acc);
MAKE_GENESIS_BLOCK(events, blk_0, preminer_acc, 1564434616);
generator.set_hardfork_height(get_hardfork_height());
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
block last_block = blk_0r;
for (size_t i = 0; i != 20; i++)
{
MAKE_NEXT_POS_BLOCK(events, next_blk_pos, last_block, miner_acc, miner_acc_lst);
MAKE_NEXT_BLOCK(events, next_blk_pow, next_blk_pos, miner_acc);
events.push_back(event_core_time(next_blk_pow.timestamp - 10));
last_block = next_blk_pow;
}
generator.set_pos_to_low_timestamp(true);
last_block = blk_0r;
for (size_t i = 0; i != 20; i++)
{
MAKE_NEXT_POS_BLOCK(events, next_blk_pos, last_block, miner_acc, miner_acc_lst);
MAKE_NEXT_BLOCK_TIMESTAMP_ADJUSTMENT(-14, events, next_blk_pow, next_blk_pos, miner_acc);
last_block = next_blk_pow;
}
DO_CALLBACK(events, "c1");
return true;
}
bool hard_fork_1_cumulative_difficulty_base::configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config();
pc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE; //four blocks
pc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH; //four blocks
pc.hard_fork1_starts_after_height = get_hardfork_height();
c.get_blockchain_storage().set_core_runtime_config(pc);
return true;
}
bool before_hard_fork_1_cumulative_difficulty::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<block_extended_info> last_pow_block = c.get_blockchain_storage().get_last_block_of_type(false);
std::shared_ptr<block_extended_info> last_pos_block = c.get_blockchain_storage().get_last_block_of_type(true);
CHECK_AND_ASSERT_MES(last_pow_block->cumulative_diff_precise == 184, false, "Incorrect condition failed: last_pow_block->cumulative_diff_precise == 184");
CHECK_AND_ASSERT_MES(last_pos_block->cumulative_diff_precise == 20, false, "Incorrect condition failed: last_pos_block->cumulative_diff_precise == 20");
//
return true;
}
uint64_t before_hard_fork_1_cumulative_difficulty::get_hardfork_height()const
{
return 10000; //just big number which is obviously bigger then test blockchain
}
bool inthe_middle_hard_fork_1_cumulative_difficulty::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<block_extended_info> last_pow_block = c.get_blockchain_storage().get_last_block_of_type(false);
std::shared_ptr<block_extended_info> last_pos_block = c.get_blockchain_storage().get_last_block_of_type(true);
CHECK_AND_ASSERT_MES(last_pow_block->cumulative_diff_precise == 184, false, "Incorrect condition failed: last_pow_block->cumulative_diff_precise == 184");
CHECK_AND_ASSERT_MES(last_pos_block->cumulative_diff_precise == 20, false, "Incorrect condition failed: last_pos_block->cumulative_diff_precise == 20");
//
return true;
}
uint64_t inthe_middle_hard_fork_1_cumulative_difficulty::get_hardfork_height()const
{
return 15;
}
bool after_hard_fork_1_cumulative_difficulty::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
std::shared_ptr<block_extended_info> last_pow_block = c.get_blockchain_storage().get_last_block_of_type(false);
std::shared_ptr<block_extended_info> last_pos_block = c.get_blockchain_storage().get_last_block_of_type(true);
CHECK_AND_ASSERT_MES(last_pow_block->cumulative_diff_precise == 172, false, "Incorrect condition failed: last_pow_block->cumulative_diff_precise == 184");
CHECK_AND_ASSERT_MES(last_pos_block->cumulative_diff_precise == 199, false, "Incorrect condition failed: last_pos_block->cumulative_diff_precise == 20");
//
return true;
}
uint64_t after_hard_fork_1_cumulative_difficulty::get_hardfork_height()const
{
return 12;
}

View file

@ -0,0 +1,40 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "chaingen.h"
#include "wallet_tests_basic.h"
struct hard_fork_1_cumulative_difficulty_base : public wallet_test
{
hard_fork_1_cumulative_difficulty_base();
bool generate(std::vector<test_event_entry>& events) const;
bool configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
virtual bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)= 0;
virtual uint64_t get_hardfork_height()const =0;
};
// this test check if code still work same way as it supposed to work before hard fork point
struct before_hard_fork_1_cumulative_difficulty : public hard_fork_1_cumulative_difficulty_base
{
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
virtual uint64_t get_hardfork_height()const;
};
// this test check if code still work same way as it supposed to work is split happened before hard fork point but finished after hard fork point
struct inthe_middle_hard_fork_1_cumulative_difficulty : public hard_fork_1_cumulative_difficulty_base
{
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
virtual uint64_t get_hardfork_height()const;
};
// this test check if code follow the new consensus algorithm and prefer balanced branch of the blockchain tree
struct after_hard_fork_1_cumulative_difficulty : public hard_fork_1_cumulative_difficulty_base
{
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
virtual uint64_t get_hardfork_height()const;
};

View file

@ -0,0 +1,97 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "chaingen.h"
#include "pos_validation.h"
#include "tx_builder.h"
#include "hard_fork_1_locked_pos_test.h"
#include "random_helper.h"
using namespace epee;
using namespace crypto;
using namespace currency;
hard_fork_1_locked_mining_test::hard_fork_1_locked_mining_test()
{
REGISTER_CALLBACK_METHOD(hard_fork_1_locked_mining_test, c1);
REGISTER_CALLBACK_METHOD(hard_fork_1_locked_mining_test, configure_core);
}
bool hard_fork_1_locked_mining_test::generate(std::vector<test_event_entry>& events) const
{
random_state_test_restorer::reset_random();
GENERATE_ACCOUNT(preminer_acc);
GENERATE_ACCOUNT(miner_acc);
GENERATE_ACCOUNT(pos_miner_acc);
m_accounts.push_back(miner_acc);
m_accounts.push_back(pos_miner_acc);
std::list<account_base> miner_acc_lst(1, miner_acc);
MAKE_GENESIS_BLOCK(events, blk_0, preminer_acc, 1564434616);
generator.set_hardfork_height(get_hardfork_height());
DO_CALLBACK(events, "configure_core");
REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3);
//construc tx that locks transaction for some period of time
// make a couple of huge txs
bool r = false;
std::vector<extra_v> extra;
std::vector<tx_source_entry> sources_1;
r = fill_tx_sources(sources_1, events, blk_0r, miner_acc.get_keys(), 2000000000000+TESTS_DEFAULT_FEE, 0);
CHECK_AND_ASSERT_MES(r, false, "fill_tx_sources failed");
std::vector<tx_destination_entry> destinations({ tx_destination_entry(2010000000000, pos_miner_acc.get_public_address()) });
crypto::secret_key stub;
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx(miner_acc.get_keys(), sources_1, destinations, extra, empty_attachment, tx_1, stub, get_block_height(blk_0r)+2000);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
events.push_back(tx_1); // push it to the pool
MAKE_NEXT_BLOCK_TX1(events, blk_0r_tx, blk_0r, miner_acc, tx_1);
block last_block = blk_0r_tx;
for (size_t i = 0; i != CURRENCY_MINED_MONEY_UNLOCK_WINDOW; i++)
{
MAKE_NEXT_POS_BLOCK(events, next_blk_pos, last_block, miner_acc, miner_acc_lst);
MAKE_NEXT_BLOCK(events, next_blk_pow, next_blk_pos, miner_acc);
events.push_back(event_core_time(next_blk_pow.timestamp - 10));
last_block = next_blk_pow;
}
std::list<currency::account_base> accounts_2;
accounts_2.push_back(pos_miner_acc);
//let's try to mint PoS block from locked account
MAKE_NEXT_POS_BLOCK(events, next_blk_pos, last_block, pos_miner_acc, accounts_2);
DO_CALLBACK(events, "c1");
return true;
}
bool hard_fork_1_locked_mining_test::configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
currency::core_runtime_config pc = c.get_blockchain_storage().get_core_runtime_config();
pc.min_coinstake_age = TESTS_POS_CONFIG_MIN_COINSTAKE_AGE; //four blocks
pc.pos_minimum_heigh = TESTS_POS_CONFIG_POS_MINIMUM_HEIGH; //four blocks
pc.hard_fork1_starts_after_height = get_hardfork_height();
c.get_blockchain_storage().set_core_runtime_config(pc);
return true;
}
bool hard_fork_1_locked_mining_test::c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events)
{
uint64_t h = c.get_blockchain_storage().get_current_blockchain_size();
CHECK_AND_ASSERT_MES(h == 36, false, "locked pos block is not accepted");
return true;
}
uint64_t hard_fork_1_locked_mining_test::get_hardfork_height()const
{
return 10;
}

View file

@ -0,0 +1,18 @@
// Copyright (c) 2014-2018 Zano Project
// Copyright (c) 2014-2018 The Louisdor Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#pragma once
#include "chaingen.h"
#include "wallet_tests_basic.h"
struct hard_fork_1_locked_mining_test : public wallet_test
{
hard_fork_1_locked_mining_test();
bool generate(std::vector<test_event_entry>& events) const;
bool configure_core(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
virtual bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
virtual uint64_t get_hardfork_height()const;
};

View file

@ -8,6 +8,10 @@
#include "integer_overflow.h"
// TOTAL_MONEY_SUPPLY - total number coins to be generated
#define TX_MAX_TRANSFER_AMOUNT ((uint64_t)(-1))
using namespace epee;
using namespace currency;
@ -83,7 +87,7 @@ bool gen_uint_overflow_1::generate(std::vector<test_event_entry>& events) const
// Problem 1. Miner tx output overflow
MAKE_MINER_TX_MANUALLY(miner_tx_0, blk_0);
split_miner_tx_outs(miner_tx_0, TOTAL_MONEY_SUPPLY);
split_miner_tx_outs(miner_tx_0, TX_MAX_TRANSFER_AMOUNT);
block blk_1;
if (!generator.construct_block_manually(blk_1, blk_0, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx_0))
return false;
@ -91,23 +95,23 @@ bool gen_uint_overflow_1::generate(std::vector<test_event_entry>& events) const
// Problem 1. Miner tx outputs overflow
MAKE_MINER_TX_MANUALLY(miner_tx_1, blk_1);
split_miner_tx_outs(miner_tx_1, TOTAL_MONEY_SUPPLY);
split_miner_tx_outs(miner_tx_1, TX_MAX_TRANSFER_AMOUNT);
block blk_2;
if (!generator.construct_block_manually(blk_2, blk_1, miner_account, test_generator::bf_miner_tx, 0, 0, 0, crypto::hash(), 0, miner_tx_1))
return false;
events.push_back(blk_2);
REWIND_BLOCKS(events, blk_2r, blk_2, miner_account);
MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, TOTAL_MONEY_SUPPLY, blk_2r);
MAKE_TX_LIST(events, txs_0, miner_account, bob_account, TOTAL_MONEY_SUPPLY, blk_2r);
MAKE_TX_LIST_START(events, txs_0, miner_account, bob_account, TX_MAX_TRANSFER_AMOUNT, blk_2r);
MAKE_TX_LIST(events, txs_0, miner_account, bob_account, TX_MAX_TRANSFER_AMOUNT, blk_2r);
MAKE_NEXT_BLOCK_TX_LIST(events, blk_3, blk_2r, miner_account, txs_0);
REWIND_BLOCKS(events, blk_3r, blk_3, miner_account);
// Problem 2. total_fee overflow, block_reward overflow
std::list<currency::transaction> txs_1;
// Create txs with huge fee
txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_TEST_COINS(1), TOTAL_MONEY_SUPPLY - MK_TEST_COINS(1)));
txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_TEST_COINS(1), TOTAL_MONEY_SUPPLY - MK_TEST_COINS(1)));
txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_TEST_COINS(1), TX_MAX_TRANSFER_AMOUNT - MK_TEST_COINS(1)));
txs_1.push_back(construct_tx_with_fee(events, blk_3, bob_account, alice_account, MK_TEST_COINS(1), TX_MAX_TRANSFER_AMOUNT - MK_TEST_COINS(1)));
MAKE_NEXT_BLOCK_TX_LIST(events, blk_4, blk_3r, miner_account, txs_1);
return true;
@ -143,10 +147,10 @@ bool gen_uint_overflow_2::generate(std::vector<test_event_entry>& events) const
std::vector<currency::tx_destination_entry> destinations;
const account_public_address& bob_addr = bob_account.get_keys().m_account_address;
destinations.push_back(tx_destination_entry(TOTAL_MONEY_SUPPLY, bob_addr));
destinations.push_back(tx_destination_entry(TOTAL_MONEY_SUPPLY - 1, bob_addr));
destinations.push_back(tx_destination_entry(TX_MAX_TRANSFER_AMOUNT, bob_addr));
destinations.push_back(tx_destination_entry(TX_MAX_TRANSFER_AMOUNT - 1, bob_addr));
// sources.front().amount = destinations[0].amount + destinations[2].amount + destinations[3].amount + TESTS_DEFAULT_FEE
destinations.push_back(tx_destination_entry(sources.front().amount - TOTAL_MONEY_SUPPLY - TOTAL_MONEY_SUPPLY + 1 - TESTS_DEFAULT_FEE, bob_addr));
destinations.push_back(tx_destination_entry(sources.front().amount - TX_MAX_TRANSFER_AMOUNT - TX_MAX_TRANSFER_AMOUNT + 1 - TESTS_DEFAULT_FEE, bob_addr));
currency::transaction tx_1;
std::vector<currency::attachment_v> attachments;
@ -162,7 +166,7 @@ bool gen_uint_overflow_2::generate(std::vector<test_event_entry>& events) const
for (size_t i = 0; i < tx_1.vout.size(); ++i)
{
auto& tx_1_out = tx_1.vout[i];
if (tx_1_out.amount < TOTAL_MONEY_SUPPLY - 1)
if (tx_1_out.amount < TX_MAX_TRANSFER_AMOUNT - 1)
continue;
append_tx_source_entry(sources, tx_1, i);
@ -171,7 +175,7 @@ bool gen_uint_overflow_2::generate(std::vector<test_event_entry>& events) const
destinations.clear();
currency::tx_destination_entry de;
de.addr.push_back(alice_account.get_keys().m_account_address);
de.amount = TOTAL_MONEY_SUPPLY - TESTS_DEFAULT_FEE;
de.amount = TX_MAX_TRANSFER_AMOUNT - TESTS_DEFAULT_FEE;
destinations.push_back(de);
destinations.push_back(de);

View file

@ -1194,7 +1194,7 @@ bool multisig_and_unlock_time::generate(std::vector<test_event_entry>& events) c
transaction tx_1 = AUTO_VAL_INIT(tx_1);
r = construct_tx(miner_acc.get_keys(), sources, destinations, empty_attachment, tx_1, unlock_time, CURRENCY_TO_KEY_OUT_RELAXED, true);
CHECK_AND_ASSERT_MES(r, false, "construct_tx failed");
CHECK_AND_ASSERT_MES(get_tx_unlock_time(tx_1) == unlock_time, false, "Unlock time was not correctly set");
CHECK_AND_ASSERT_MES(get_tx_max_unlock_time(tx_1) == unlock_time, false, "Unlock time was not correctly set");
events.push_back(tx_1);
MAKE_NEXT_BLOCK_TX1(events, blk_1, blk_0r, miner_acc, tx_1);

View file

@ -24,7 +24,7 @@ void pos_block_builder::step1_init_header(size_t block_height, crypto::hash& pre
{
CHECK_AND_ASSERT_THROW_MES(m_step == 0, "pos_block_builder: incorrect step sequence");
m_block.minor_version = CURRENT_BLOCK_MINOR_VERSION;
m_block.major_version = CURRENT_BLOCK_MAJOR_VERSION;
m_block.major_version = BLOCK_MAJOR_VERSION_INITAL;
m_block.timestamp = 0; // to be set at step 3
m_block.prev_id = prev_block_hash;
m_block.flags = CURRENCY_BLOCK_FLAG_POS_BLOCK;

View file

@ -48,15 +48,15 @@ bool determine_tx_real_inputs(currency::core& c, const currency::transaction& tx
, m_found(false)
{}
bool handle_output(const currency::transaction& tx, const currency::tx_out& out)
bool handle_output(const transaction& source_tx, const transaction& validated_tx, const tx_out& out, uint64_t out_i)
{
CHECK_AND_ASSERT_MES(!m_found, false, "Internal error: m_found is true but the visitor is still being applied");
auto it = std::find(tx.vout.begin(), tx.vout.end(), out);
if (it == tx.vout.end())
auto it = std::find(validated_tx.vout.begin(), validated_tx.vout.end(), out);
if (it == validated_tx.vout.end())
return false;
size_t output_tx_index = it - tx.vout.begin();
size_t output_tx_index = it - validated_tx.vout.begin();
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(validated_tx);
crypto::key_derivation derivation;
bool r = generate_key_derivation(tx_pub_key, m_keys.m_view_secret_key, derivation);
CHECK_AND_ASSERT_MES(r, false, "generate_key_derivation failed");
@ -96,7 +96,7 @@ bool determine_tx_real_inputs(currency::core& c, const currency::transaction& tx
continue;
}
local_visitor vis(keys, in.k_image);
bool r = c.get_blockchain_storage().scan_outputkeys_for_indexes(in, vis);
bool r = c.get_blockchain_storage().scan_outputkeys_for_indexes(tx, in, vis);
CHECK_AND_ASSERT_MES(r || vis.m_found, false, "scan_outputkeys_for_indexes failed");
if (!vis.m_found)
return false;

View file

@ -46,7 +46,7 @@ static const std::vector<currency::attachment_v> empty_attachment;
bool create_block_template_manually(const currency::block& prev_block, boost::multiprecision::uint128_t already_generated_coins, const std::vector<const currency::transaction*>& txs, const currency::account_public_address& miner_addr, currency::block& result)
{
result.flags = 0;
result.major_version = CURRENT_BLOCK_MAJOR_VERSION;
result.major_version = BLOCK_MAJOR_VERSION_INITAL;
result.minor_version = CURRENT_BLOCK_MINOR_VERSION;
result.nonce = 0;
result.prev_id = get_block_hash(prev_block);

View file

@ -313,7 +313,7 @@ void run_emulation(const std::string& path)
PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_configurable, BBR_DIFFICULTY_WINDOW, BBR_DIFFICULTY_CUT, BBR_DIFFICULTY_CUT);
PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_configurable, 500, 60, 60);
PERFORME_SIMULATION_FOR_FUNCTION(bbr_next_difficulty_configurable, 300, 60, 60);
PERFORME_SIMULATION_FOR_FUNCTION_NO_WINDOW(currency::next_difficulty);
PERFORME_SIMULATION_FOR_FUNCTION_NO_WINDOW(currency::next_difficulty_1);
print_blocks(result_blocks, path + "result.txt");
LOG_PRINT_L0("Done");

View file

@ -0,0 +1,104 @@
// Copyright (c) 2019 Zano Project
// Distributed under the MIT/X11 software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <memory>
#include "crypto/crypto.h"
#include "gtest/gtest.h"
#include "common/db_backend_lmdb.h"
#include "common/db_abstract_accessor.h"
using namespace tools;
namespace lmdb_test
{
TEST(lmdb, 2gb_test)
{
bool r = false;
epee::shared_recursive_mutex rw_lock;
epee::log_space::log_singletone::enable_channels("lmdb");
static const uint64_t buffer_size = 64 * 1024; // 64 KB
static const uint64_t db_total_size = static_cast<uint64_t>(2.1 * 1024 * 1024 * 1024); // 2.1 GB -- a bit more than 2GB to test 2GB boundary
static const std::string db_file_path = "2gb_lmdb_test";
std::shared_ptr<db::lmdb_db_backend> lmdb_ptr = std::make_shared<db::lmdb_db_backend>();
db::basic_db_accessor bdba(lmdb_ptr, rw_lock);
//
// write data
//
r = bdba.open(db_file_path, CACHE_SIZE);
ASSERT_TRUE(r);
db::container_handle h;
r = lmdb_ptr->open_container("c1", h);
ASSERT_TRUE(r);
ASSERT_TRUE(lmdb_ptr->begin_transaction());
ASSERT_TRUE(lmdb_ptr->clear(h));
std::vector<uint8_t> buffer;
buffer.resize(buffer_size);
crypto::generate_random_bytes(buffer_size, buffer.data());
uint64_t total_data = 0;
for (uint64_t key = 0; key < db_total_size / buffer_size; ++key)
{
r = lmdb_ptr->set(h, (char*)&key, sizeof key, reinterpret_cast<const char*>(buffer.data()), buffer_size);
ASSERT_TRUE(r);
total_data += buffer_size;
if (key % 1024 == 0)
{
ASSERT_TRUE(lmdb_ptr->commit_transaction());
ASSERT_TRUE(lmdb_ptr->resize_if_needed());
ASSERT_TRUE(lmdb_ptr->begin_transaction());
std::cout << total_data / 1024 / 1024 << " MB written to DB" << ENDL;
}
}
ASSERT_TRUE(lmdb_ptr->commit_transaction());
r = bdba.close();
ASSERT_TRUE(r);
//
// read data and check
//
r = bdba.open(db_file_path);
ASSERT_TRUE(r);
r = lmdb_ptr->open_container("c1", h);
ASSERT_TRUE(r);
ASSERT_TRUE(lmdb_ptr->begin_transaction());
std::string out_buffer;
total_data = 0;
for (uint64_t key = 0; key < db_total_size / buffer_size; ++key)
{
r = lmdb_ptr->get(h, (char*)&key, sizeof key, out_buffer);
ASSERT_TRUE(r);
ASSERT_EQ(buffer_size, out_buffer.size());
ASSERT_TRUE(0 == memcmp(buffer.data(), out_buffer.c_str(), buffer_size));
total_data += buffer_size;
if (key % 1024 == 0)
std::cout << total_data / 1024 / 1024 << " MB read from DB" << ENDL;
}
ASSERT_TRUE(lmdb_ptr->commit_transaction());
r = bdba.close();
ASSERT_TRUE(r);
boost::filesystem::remove_all(db_file_path);
}
}

View file

@ -16,5 +16,6 @@ int main(int argc, char** argv)
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
int ret = RUN_ALL_TESTS();
return ret;
}