From 1cfb5374de1ff4a21bab9da6ed552c8fb2c717cf Mon Sep 17 00:00:00 2001 From: Decker Date: Mon, 11 Mar 2019 05:51:02 +0300 Subject: [PATCH 001/787] + msvc 2015 deps headers --- .../deps/install/include/acs_defs.h | 265 +++ .../deps/install/include/curses.h | 1846 +++++++++++++++++ .../deps/install/include/curspriv.h | 134 ++ .../deps/install/include/getopt.h | 93 + .../deps/install/include/panel.h | 56 + .../deps/install/include/term.h | 48 + .../deps/install/include/unistd.h | 56 + 7 files changed, 2498 insertions(+) create mode 100644 src/cc/rogue/x86_64-w64-msvc/deps/install/include/acs_defs.h create mode 100644 src/cc/rogue/x86_64-w64-msvc/deps/install/include/curses.h create mode 100644 src/cc/rogue/x86_64-w64-msvc/deps/install/include/curspriv.h create mode 100644 src/cc/rogue/x86_64-w64-msvc/deps/install/include/getopt.h create mode 100644 src/cc/rogue/x86_64-w64-msvc/deps/install/include/panel.h create mode 100644 src/cc/rogue/x86_64-w64-msvc/deps/install/include/term.h create mode 100644 src/cc/rogue/x86_64-w64-msvc/deps/install/include/unistd.h diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/acs_defs.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/acs_defs.h new file mode 100644 index 000000000..c8c02a737 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/acs_defs.h @@ -0,0 +1,265 @@ +/* Many of the following #defines are completely unused for the +nonce. For each character, its code point in code page 437, +Unicode, and page 8859-1 are given. The first is used for +non-wide builds in Win32 console, DOS, SDL, and OS/2. +Unicode is used for all wide builds, and for the non-wide +build of WinGUI. Code page 8859-1 is used for non-wide X11. + + All of these characters exist in CP437 and Unicode. Some +don't exist in 8859-1, in which case the last column is 'TBD'. +Only 32 are used in ncurses. So caution is advised. */ + +#ifdef USE_ISO8859_CHARSET + #define CHOOSE( A, B, C) (C) + #define TBD '!' +#else + #define CHOOSE( A, B, C) (USE_UNICODE_ACS_CHARS ? B : A) +#endif + +/* Codes found from https://en.wikipedia.org/wiki/Code_page_437 */ + +#define SMILE CHOOSE( 0x01, 0x263a, 'O') +#define REV_SMILE CHOOSE( 0x02, 0x263b, 'O') +#define HEART CHOOSE( 0x03, 0x2665, 'H') +#define DIAMOND CHOOSE( 0x04, 0x2666, 0x01) +#define CLUB CHOOSE( 0x05, 0x2663, 'C') +#define SPADE CHOOSE( 0x06, 0x2660, 'S') +#define MEDIUM_BULLET CHOOSE( 0x07, 0x2022, 0xb7) +#define REV_BULLET CHOOSE( 0x08, 0x2508, 0xb7) +#define WHITE_BULLET CHOOSE( 0x09, 0x25cb, 7) +#define REV_WHITE_BULLET CHOOSE( 0x0a, 0x25D9, 7) +#define MALE_SYM CHOOSE( 0x0b, 0x2642, 'm') +#define FEMALE_SYM CHOOSE( 0x0c, 0x2640, 'f') +#define QTR_NOTE CHOOSE( 0x0d, 0x266a, 0xbc) +#define EIGHTH_NOTE CHOOSE( 0x0e, 0x266b, 0xbd) +#define SPLAT CHOOSE( 0x0f, 0xa4 , 0xa4) +#define RIGHT_TRIANGLE CHOOSE( 0x10, 0x25b6, '>') +#define LEFT_TRIANGLE CHOOSE( 0x11, 0x25c0, '<') +#define UP_DOWN_ARROW CHOOSE( 0x12, 0x2195, 0x19) +#define DBL_BANG CHOOSE( 0x13, 0x203c, '!') +#define PILCROW CHOOSE( 0x14, 0xb6 , 0xb6) +#define SECTION_SIGN CHOOSE( 0x15, 0xa7 , 0xa7) +#define LOW_QTR_BLOCK CHOOSE( 0x16, 0x25b2, '_') +#define UP_DOWN_ARROW_UNDERSCORED CHOOSE( 0x17, 0x21ab, 0x19) +#define UP_ARROW CHOOSE( 0x18, 0x2191, '^') +#define DOWN_ARROW CHOOSE( 0x19, 0x2193, 'v') +#define RIGHT_ARROW CHOOSE( 0x1a, 0x2192, '>') +#define LEFT_ARROW CHOOSE( 0x1b, 0x2190, '<') +#define RIGHT_ANGLE CHOOSE( 0x1c, 0x221f, 0xe) +#define LEFT_RIGHT_ARROW CHOOSE( 0x1d, 0x2194, '-') +#define UP_TRIANGLE CHOOSE( 0x1e, 0x25b2, '^') +#define DOWN_TRIANGLE CHOOSE( 0x1f, 0x25bc, 'v') + +#define UPPERCASE_C_CEDILLA CHOOSE( 0x80, 0xc7 , 0xc7) +#define LOWERCASE_U_UMLAUT CHOOSE( 0x81, 0xfc , 0xfc) +#define LOWERCASE_E_ACUTE CHOOSE( 0x82, 0xe9 , 0xe9) +#define LOWERCASE_A_CIRCUMFLEX CHOOSE( 0x83, 0xe2 , 0xe2) +#define LOWERCASE_A_UMLAUT CHOOSE( 0x84, 0xe4 , 0xe4) +#define LOWERCASE_A_GRAVE CHOOSE( 0x85, 0xe0 , 0xea) +#define LOWERCASE_A_RING CHOOSE( 0x86, 0xe5 , 0xe5) +#define LOWERCASE_C_CEDILLA CHOOSE( 0x87, 0xe7 , 0xe7) +#define LOWERCASE_E_CIRCUMFLEX CHOOSE( 0x88, 0xea , 0xea) +#define LOWERCASE_E_UMLAUT CHOOSE( 0x89, 0xeb , 0xeb) +#define LOWERCASE_E_GRAVE CHOOSE( 0x8a, 0xe8 , 0xe8) +#define LOWERCASE_I_UMLAUT CHOOSE( 0x8b, 0xef , 0xef) +#define LOWERCASE_I_CIRCUMFLEX CHOOSE( 0x8c, 0xee , 0xee) +#define LOWERCASE_I_GRAVE CHOOSE( 0x8d, 0xec , 0xce) +#define UPPERCASE_A_UMLAUT CHOOSE( 0x8e, 0xc4 , 0xc4) +#define UPPERCASE_A_RING CHOOSE( 0x8f, 0xc5 , 0xc5) + +#define UPPERCASE_E_ACUTE CHOOSE( 0x90, 0xc9 , 0xc9) +#define LOWERCASE_AE_LIGATURE CHOOSE( 0x91, 0xe6 , 0xe6) +#define UPPERCASE_AE_LIGATURE CHOOSE( 0x92, 0xc6 , 0xc6) +#define LOWERCASE_O_CIRCUMFLEX CHOOSE( 0x93, 0xf4 , 0xf4) +#define LOWERCASE_O_UMLAUT CHOOSE( 0x94, 0xf6 , 0xf6) +#define LOWERCASE_O_GRAVE CHOOSE( 0x95, 0xf2 , 0xf2) +#define LOWERCASE_U_CIRCUMFLEX CHOOSE( 0x96, 0xfb , 0xfb) +#define LOWERCASE_U_GRAVE CHOOSE( 0x97, 0xf9 , 0xf9) +#define LOWERCASE_Y_UMLAUT CHOOSE( 0x98, 0xff , 0xff) +#define UPPERCASE_O_UMLAUT CHOOSE( 0x99, 0xd6 , 0xd6) +#define UPPERCASE_U_UMLAUT CHOOSE( 0x9a, 0xdc , 0xdc) +#define CENT_SIGN CHOOSE( 0x9b, 0xa2 , 0xa2) +#define STERLING_SIGN CHOOSE( 0x9c, 0xa3 , 30) +#define YEN_SIGN CHOOSE( 0x9d, 0xa5 , 0xa5) +#define PESETA_SIGN CHOOSE( 0x9e, 0x20a7, TBD) +#define F_WITH_HOOK CHOOSE( 0x9f, 0x0192, TBD) + +#define LOWERCASE_A_ACUTE CHOOSE( 0xa0, 0xe1 , 0xe1) +#define LOWERCASE_I_ACUTE CHOOSE( 0xa1, 0xed , 0xed) +#define LOWERCASE_O_ACUTE CHOOSE( 0xa2, 0xf3 , 0xf3) +#define LOWERCASE_U_ACUTE CHOOSE( 0xa3, 0xfa , 0xfa) +#define LOWERCASE_N_TILDE CHOOSE( 0xa4, 0xf1 , 0xf1) +#define UPPERCASE_N_TILDE CHOOSE( 0xa5, 0xd1 , 0xd1) +#define A_ORDINAL CHOOSE( 0xa6, 0xaa , 0xaa) +#define O_ORDINAL CHOOSE( 0xa7, 0xba , 0xba) +#define INVERTED_QUESTION_MARK CHOOSE( 0xa8, 0xbf , 0xbf) +#define REVERSED_NOT_SIGN CHOOSE( 0xa9, 0x2310, TBD) +#define NOT_SIGN CHOOSE( 0xaa, 0xac , 0xac) +#define VULGAR_HALF CHOOSE( 0xab, 0xbd , 0xbd) +#define VULGAR_QUARTER CHOOSE( 0xac, 0xbc , 0xbc) +#define INVERTED_EXCLAMATION_MARK CHOOSE( 0xad, 0xa1 , 0xa1) +#define LEFT_ANGLE_QUOTE_MARK CHOOSE( 0xae, 0xab , 0xab) +#define RIGHT_ANGLE_QUOTE_MARK CHOOSE( 0xaf, 0xbb , 0xbb) + +#define LIGHT_SHADE CHOOSE( 0xb0, 0x2591, '#' ) +#define MEDIUM_SHADE CHOOSE( 0xb1, 0x2592, 2) +#define DARK_SHADE CHOOSE( 0xb2, 0x2593, TBD) +#define BOX_VLINE CHOOSE( 0xb3, 0x2502, 25) +#define BOX_RTEE CHOOSE( 0xb4, 0x2524, 22) +#define BOX_SD_RTEE CHOOSE( 0xb5, 0x2561, 22) +#define BOX_DS_RTEE CHOOSE( 0xb6, 0x2562, 22) +#define BOX_DS_URCORNER CHOOSE( 0xb7, 0x2556, 12) +#define BOX_SD_URCORNER CHOOSE( 0xb8, 0x2555, 12) +#define BOX_D_RTEE CHOOSE( 0xb9, 0x2563, 22) +#define BOX_D_VLINE CHOOSE( 0xba, 0x2551, 25) +#define BOX_D_URCORNER CHOOSE( 0xbb, 0x2557, 12) +#define BOX_D_LRCORNER CHOOSE( 0xbc, 0x255D, 11) +#define BOX_DS_LRCORNER CHOOSE( 0xbd, 0x255c, 11) +#define BOX_SD_LRCORNER CHOOSE( 0xbe, 0x255b, 11) +#define BOX_URCORNER CHOOSE( 0xbf, 0x2510, 12) + +#define BOX_LLCORNER CHOOSE( 0xc0, 0x2514, 14) +#define BOX_BTEE CHOOSE( 0xc1, 0x2534, 23) +#define BOX_TTEE CHOOSE( 0xc2, 0x252c, 24) +#define BOX_LTEE CHOOSE( 0xc3, 0x251c, 21) +#define BOX_HLINE CHOOSE( 0xc4, 0x2500, 18) +#define BOX_PLUS CHOOSE( 0xc5, 0x253c, 15) +#define BOX_SD_LTEE CHOOSE( 0xc6, 0x255e, 21) +#define BOX_DS_LTEE CHOOSE( 0xc7, 0x255f, 21) +#define BOX_D_LLCORNER CHOOSE( 0xc8, 0x255A, 14) +#define BOX_D_ULCORNER CHOOSE( 0xc9, 0x2554, 13) +#define BOX_D_BTEE CHOOSE( 0xca, 0x2569, 23) +#define BOX_D_TTEE CHOOSE( 0xcb, 0x2566, 24) +#define BOX_D_LTEE CHOOSE( 0xcc, 0x2560, 21) +#define BOX_D_HLINE CHOOSE( 0xcd, 0x2550, 18) +#define BOX_D_PLUS CHOOSE( 0xce, 0x256C, 15) +#define BOX_SD_BTEE CHOOSE( 0xcf, 0x2567, 23) + +#define BOX_DS_BTEE CHOOSE( 0xd0, 0x2568, 23) +#define BOX_SD_TTEE CHOOSE( 0xd1, 0x2564, 24) +#define BOX_DS_TTEE CHOOSE( 0xd2, 0x2565, 24) +#define BOX_DS_LLCORNER CHOOSE( 0xd3, 0x2559, 14) +#define BOX_SD_LLCORNER CHOOSE( 0xd4, 0x2558, 14) +#define BOX_SD_ULCORNER CHOOSE( 0xd5, 0x2552, 13) +#define BOX_DS_ULCORNER CHOOSE( 0xd6, 0x2553, 13) +#define BOX_DS_PLUS CHOOSE( 0xd7, 0x256b, 15) +#define BOX_SD_PLUS CHOOSE( 0xd8, 0x256a, 15) +#define BOX_LRCORNER CHOOSE( 0xd9, 0x2518, 11) +#define BOX_ULCORNER CHOOSE( 0xda, 0x250c, 13) +#define FULL_BLOCK CHOOSE( 0xdb, 0x2588, 0) +#define LOWER_HALF_BLOCK CHOOSE( 0xdc, 0x2584, TBD) +#define LEFT_HALF_BLOCK CHOOSE( 0xdd, 0x258c, TBD) +#define RIGHT_HALF_BLOCK CHOOSE( 0xde, 0x2590, TBD) +#define UPPER_HALF_BLOCK CHOOSE( 0xdf, 0x2580, TBD) + +#define ALPHA CHOOSE( 0xe0, 0x03b1, TBD) +#define BETA CHOOSE( 0xe1, 0x00df, TBD) +#define GAMMA CHOOSE( 0xe2, 0x0393, TBD) +#define PI CHOOSE( 0xe3, 0x03c0, 28) +#define UPPERCASE_SIGMA CHOOSE( 0xe4, 0x03a3, TBD) +#define LOWERCASE_SIGMA CHOOSE( 0xe5, 0x03c3, TBD) +#define MU CHOOSE( 0xe6, 0x00b5, 0xb5) +#define TAU CHOOSE( 0xe7, 0x03c4, TBD) +#define UPPERCASE_PHI CHOOSE( 0xe8, 0x03a6, TBD) +#define THETA CHOOSE( 0xe9, 0x0398, TBD) +#define OMEGA CHOOSE( 0xea, 0x03a9, TBD) +#define DELTA CHOOSE( 0xeb, 0x03b4, TBD) +#define INFINITY_SIGN CHOOSE( 0xec, 0x221e, TBD) +#define LOWERCASE_PHI CHOOSE( 0xed, 0x03c6, TBD) +#define EPSILON CHOOSE( 0xee, 0x03b5, TBD) +#define INTERSECTION CHOOSE( 0xef, 0x2229, TBD) + +#define TRIPLE_BAR CHOOSE( 0xf0, 0x2261, TBD) +#define PLUS_OR_MINUS CHOOSE( 0xf1, 0x00b1, 8) +#define GREATER_THAN_OR_EQUAL_TO CHOOSE( 0xf2, 0x2265, 27) +#define LESSER_THAN_OR_EQUAL_TO CHOOSE( 0xf3, 0x2264, 26) +#define UPPER_HALF_INTEGRAL_SIGN CHOOSE( 0xf4, 0x2320, TBD) +#define LOWER_HALF_INTEGRAL_SIGN CHOOSE( 0xf5, 0x2321, TBD) +#define DIVISION_SIGN CHOOSE( 0xf6, 0x00f7, 0xf7) +#define APPROXIMATELY_EQUALS_SIGN CHOOSE( 0xf7, 0x2248, TBD) +#define DEGREE_SIGN CHOOSE( 0xf8, 0x00b0, 0xb0) +#define LARGE_BULLET CHOOSE( 0xf9, 0x2219, 7) +#define SMALL_BULLET CHOOSE( 0xfa, 0x00b7, 0xb7) +#define SQUARE_ROOT CHOOSE( 0xfb, 0x221a, TBD) +#define SUPERSCRIPT_N CHOOSE( 0xfc, 0x207f, TBD) +#define SUPERSCRIPT_2 CHOOSE( 0xfd, 0x00b2, 0xb2) +#define CENTERED_SQUARE CHOOSE( 0xfe, 0x25a0, TBD) +#define NON_BREAKING_SPACE CHOOSE( 0xff, 0x00a0, TBD) + + + + /* It says at http://unicode.org/charts/PDF/U2300.pdf */ + /* that '...the scan line numbers here refer to old, */ + /* low-resolution technology for terminals, with only */ + /* nine scan lines per fixed-size character glyph. */ + /* Even-numbered scan lines are unified with box */ + /* drawing graphics." */ + /* The utility of these is questionable; they'd */ + /* work Just Fine in wingdi (_if_ the appropriate */ + /* glyphs are available), but not elsewhere. */ +#define HORIZ_SCAN_LINE_1 CHOOSE( 0x2d, 0x23ba, 16) +#define HORIZ_SCAN_LINE_3 CHOOSE( 0x2d, 0x23bb, 17) +#define HORIZ_SCAN_LINE_7 CHOOSE( 0x2d, 0x23bc, 19) +#define HORIZ_SCAN_LINE_9 CHOOSE( '_', 0x23bd, 20) + + /* Code page 437 lacks a 'for real' not-equals, so for that, */ + /* we use the double-horizontal single-vertical box drawing : */ +#define NOT_EQUALS_SIGN CHOOSE( 0xd8, 0x2260, 29) + +# define A(x) ((chtype)x | A_ALTCHARSET) + +chtype acs_map[128] = +{ + A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), + A(9), A(10), + CLUB, HEART, SPADE, SMILE, REV_SMILE, /* 11 12 13 14 15 */ + MEDIUM_BULLET, WHITE_BULLET, PILCROW, SECTION_SIGN, /* 16 17 18 19 */ + A_ORDINAL, O_ORDINAL, LOWERCASE_PHI, /* 20 21 22 */ + INVERTED_EXCLAMATION_MARK, INVERTED_QUESTION_MARK, /* 23 24 */ + REVERSED_NOT_SIGN, NOT_SIGN, /* 25 26 */ + UPPER_HALF_INTEGRAL_SIGN, LOWER_HALF_INTEGRAL_SIGN, /* 27 28 */ + SUPERSCRIPT_N, CENTERED_SQUARE, F_WITH_HOOK, /* 29 30 31 */ + + RIGHT_ARROW, LEFT_ARROW, UP_ARROW, DOWN_ARROW, /* 32 !"# */ + + PI, NOT_EQUALS_SIGN, VULGAR_HALF, VULGAR_QUARTER, /* $%&' */ + '(', + LEFT_ANGLE_QUOTE_MARK, RIGHT_ANGLE_QUOTE_MARK, /* )* */ + DARK_SHADE, SUPERSCRIPT_2, INFINITY_SIGN, /* +,- */ + ALPHA, BETA, GAMMA, UPPERCASE_SIGMA, LOWERCASE_SIGMA, /* ./012 */ + '3', + MU, TAU, UPPERCASE_PHI, THETA, OMEGA, DELTA, EPSILON, /* 456789: */ + + BOX_SD_LRCORNER, BOX_SD_URCORNER, BOX_SD_ULCORNER, /* ;<= */ + BOX_SD_LLCORNER, BOX_SD_PLUS, /* >? */ + BOX_SD_LTEE, BOX_SD_RTEE, BOX_SD_BTEE, BOX_SD_TTEE, /* @ABC */ + + BOX_D_LRCORNER, BOX_D_URCORNER, BOX_D_ULCORNER, /* DEF */ + BOX_D_LLCORNER, BOX_D_PLUS, /* GH */ + BOX_D_LTEE, BOX_D_RTEE, BOX_D_BTEE, BOX_D_TTEE, /* IJKL */ + + BOX_DS_LRCORNER, BOX_DS_URCORNER, BOX_DS_ULCORNER, /* MNO */ + BOX_DS_LLCORNER, BOX_DS_PLUS, /* PQ */ + BOX_DS_LTEE, BOX_DS_RTEE, BOX_DS_BTEE, BOX_DS_TTEE, /* RSTU */ + + BOX_LRCORNER, BOX_URCORNER, BOX_ULCORNER, /* VWX */ + BOX_LLCORNER, BOX_PLUS, /* YZ */ + BOX_LTEE, BOX_RTEE, BOX_BTEE, BOX_TTEE, /* [\]^ */ + + BOX_HLINE, BOX_VLINE, BOX_D_HLINE, BOX_D_VLINE, /* _`ab */ + + DIVISION_SIGN, APPROXIMATELY_EQUALS_SIGN, /* cd */ + INTERSECTION, TRIPLE_BAR, /* ef */ + SMALL_BULLET, LARGE_BULLET, SQUARE_ROOT, /* ghi */ + DIAMOND, MEDIUM_SHADE, /* jk */ + HORIZ_SCAN_LINE_1, HORIZ_SCAN_LINE_3, /* lm */ + HORIZ_SCAN_LINE_7, HORIZ_SCAN_LINE_9, /* no */ + UPPER_HALF_BLOCK, LOWER_HALF_BLOCK, /* pq */ + LEFT_HALF_BLOCK, RIGHT_HALF_BLOCK, FULL_BLOCK, /* rst */ + LESSER_THAN_OR_EQUAL_TO, GREATER_THAN_OR_EQUAL_TO, /* uv */ + DEGREE_SIGN, PLUS_OR_MINUS, LIGHT_SHADE, SPLAT, /* wxyz */ + CENT_SIGN, YEN_SIGN, PESETA_SIGN, STERLING_SIGN, /* {|}~ */ + A(127) +}; + +# undef A diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curses.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curses.h new file mode 100644 index 000000000..9ee3f08a6 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curses.h @@ -0,0 +1,1846 @@ +/* Public Domain Curses */ + +/*----------------------------------------------------------------------* + * PDCurses * + *----------------------------------------------------------------------*/ + +#ifndef __PDCURSES__ +#define __PDCURSES__ 1 + +/*man-start************************************************************** + +PDCurses definitions list: (Only define those needed) + + XCURSES True if compiling for X11. + PDC_RGB True if you want to use RGB color definitions + (Red = 1, Green = 2, Blue = 4) instead of BGR. + PDC_WIDE True if building wide-character support. + PDC_DLL_BUILD True if building a Windows DLL. + PDC_NCMOUSE Use the ncurses mouse API instead + of PDCurses' traditional mouse API. + +PDCurses portable platform definitions list: + + PDC_BUILD Defines API build version. + PDCURSES Enables access to PDCurses-only routines. + XOPEN Always true. + SYSVcurses True if you are compiling for SYSV portability. + BSDcurses True if you are compiling for BSD portability. + +**man-end****************************************************************/ + +#define PDCURSES 1 /* PDCurses-only routines */ +#define XOPEN 1 /* X/Open Curses routines */ +#define SYSVcurses 1 /* System V Curses routines */ +#define BSDcurses 1 /* BSD Curses routines */ +#if defined( CHTYPE_32) + #define CHTYPE_LONG 1 /* chtypes will be 32 bits */ +#elif !defined( CHTYPE_16) + #define CHTYPE_LONG 2 /* chtypes will be (default) 64 bits */ +#endif + +/*----------------------------------------------------------------------*/ + +#ifdef NO_STDINT_H + #define uint64_t unsigned long long + #define uint32_t unsigned long + #define uint16_t unsigned short +#else + #include +#endif +#include +#include +#include /* Required by X/Open usage below */ + +#ifdef PDC_WIDE +# include +#endif + +#if defined(__STDC_VERSION__) && __STDC_VERSION >= 199901L && \ + !defined(__bool_true_false_are_defined) +# include +#endif + +#ifdef __cplusplus +extern "C" +{ +# define bool _bool +#endif + +/*---------------------------------------------------------------------- + * + * Constants and Types + * + */ + +#undef FALSE +#undef TRUE + +#ifdef __bool_true_false_are_defined + +# define FALSE false +# define TRUE true + +#else + +typedef unsigned char bool; + +# define FALSE 0 +# define TRUE 1 + +#endif + +#undef ERR +#define ERR (-1) + +#undef OK +#define OK 0 + +#ifdef CHTYPE_LONG + #if(CHTYPE_LONG >= 2) /* "non-standard" 64-bit chtypes */ + typedef uint64_t chtype; + #else /* "Standard" CHTYPE_LONG case, 32-bit: */ + typedef uint32_t chtype; + # endif +#else +typedef uint16_t chtype; /* 8-bit attr + 8-bit char */ +#endif + +#ifdef PDC_WIDE +typedef chtype cchar_t; +#endif + +typedef chtype attr_t; + +/* Version constants, available as of version 4.0 : */ +/* Don't forget to update 'version.mif' if MAJOR/MINOR changes! */ + +#define PDC_VER_MAJOR 4 +#define PDC_VER_MINOR 0 +#define PDC_VER_CHANGE 4 +#define PDC_VER_YEAR 2019 +#define PDC_VER_MONTH 1 +#define PDC_VER_DAY 20 + +#define PDC_BUILD (PDC_VER_MAJOR*1000 + PDC_VER_MINOR *100 + PDC_VER_CHANGE) + +/* When using PDCurses as a DLL (Windows) or shared library (BSD or *nix), +it's possible to switch the DLL or shared library. One may therefore want +to inquire of the DLL/shared library the port, version numbers, and +chtype_size used, and make sure they're what one was expecting. The +'PDC_version' structure lets you do just that. */ + +enum PDC_port +{ + PDC_PORT_X11 = 0, + PDC_PORT_WIN32 = 1, + PDC_PORT_WINGUI = 2, + PDC_PORT_DOS = 3, + PDC_PORT_OS2 = 4, + PDC_PORT_SDL1 = 5, + PDC_PORT_SDL2 = 6, + PDC_PORT_VT = 7 +}; + +/* Detailed PDC version information */ +#define PDC_HAS_VERSION_INFO 1 +typedef struct +{ + const enum PDC_port port; + const int ver_major; + const int ver_minor; + const int ver_change; + const size_t chtype_size; + const bool is_wide; + const bool is_forced_utf8; +} PDC_version_info; + +/*---------------------------------------------------------------------- + * + * Mouse Interface -- SYSVR4, with extensions + * + */ + +/* Most flavors of PDCurses support three buttons. WinGUI supports */ +/* these plus two "extended" buttons. But we'll set this macro to */ +/* six, allowing future versions to support up to nine total buttons. */ +/* (The button states are broken up into two arrays to allow for the */ +/* possibility of backward compatibility to DLLs compiled with only */ +/* three mouse buttons.) */ + +#define PDC_MAX_MOUSE_BUTTONS 9 +#define PDC_N_EXTENDED_MOUSE_BUTTONS 6 + +typedef struct +{ + int x; /* absolute column, 0 based, measured in characters */ + int y; /* absolute row, 0 based, measured in characters */ + short button[3]; /* state of three "normal" buttons */ + int changes; /* flags indicating what has changed with the mouse */ + short xbutton[PDC_N_EXTENDED_MOUSE_BUTTONS]; /* state of ext buttons */ +} MOUSE_STATUS; + +#define BUTTON_RELEASED 0x0000 +#define BUTTON_PRESSED 0x0001 +#define BUTTON_CLICKED 0x0002 +#define BUTTON_DOUBLE_CLICKED 0x0003 +#define BUTTON_TRIPLE_CLICKED 0x0004 +#define BUTTON_MOVED 0x0005 /* PDCurses */ +#define WHEEL_SCROLLED 0x0006 /* PDCurses */ +#define BUTTON_ACTION_MASK 0x0007 /* PDCurses */ + +#define PDC_BUTTON_SHIFT 0x0008 /* PDCurses */ +#define PDC_BUTTON_CONTROL 0x0010 /* PDCurses */ +#define PDC_BUTTON_ALT 0x0020 /* PDCurses */ +#define BUTTON_MODIFIER_MASK 0x0038 /* PDCurses */ + +#define MOUSE_X_POS (Mouse_status.x) +#define MOUSE_Y_POS (Mouse_status.y) + +/* + * Bits associated with the .changes field: + * 3 2 1 0 + * 210987654321098765432109876543210 + * 1 <- button 1 has changed 0 + * 10 <- button 2 has changed 1 + * 100 <- button 3 has changed 2 + * 1000 <- mouse has moved 3 + * 10000 <- mouse position report 4 + * 100000 <- mouse wheel up 5 + * 1000000 <- mouse wheel down 6 + * 10000000 <- mouse wheel left 7 + * 100000000 <- mouse wheel right 8 + * 1000000000 <- button 4 has changed 9 + * (NOTE: buttons 6 to 10000000000 <- button 5 has changed 10 + * 9 aren't implemented 100000000000 <- button 6 has changed 11 + * in any flavor of 1000000000000 <- button 7 has changed 12 + * PDCurses yet!) 10000000000000 <- button 8 has changed 13 + * 100000000000000 <- button 9 has changed 14 + */ + +#define PDC_MOUSE_MOVED 0x0008 +#define PDC_MOUSE_POSITION 0x0010 +#define PDC_MOUSE_WHEEL_UP 0x0020 +#define PDC_MOUSE_WHEEL_DOWN 0x0040 +#define PDC_MOUSE_WHEEL_LEFT 0x0080 +#define PDC_MOUSE_WHEEL_RIGHT 0x0100 + +#define A_BUTTON_CHANGED (Mouse_status.changes & 7) +#define MOUSE_MOVED (Mouse_status.changes & PDC_MOUSE_MOVED) +#define MOUSE_POS_REPORT (Mouse_status.changes & PDC_MOUSE_POSITION) +#define BUTTON_CHANGED(x) (Mouse_status.changes & (1 << ((x) - ((x)<4 ? 1 : -5)))) +#define BUTTON_STATUS(x) (Mouse_status.button[(x) - 1]) +#define MOUSE_WHEEL_UP (Mouse_status.changes & PDC_MOUSE_WHEEL_UP) +#define MOUSE_WHEEL_DOWN (Mouse_status.changes & PDC_MOUSE_WHEEL_DOWN) +#define MOUSE_WHEEL_LEFT (Mouse_status.changes & PDC_MOUSE_WHEEL_LEFT) +#define MOUSE_WHEEL_RIGHT (Mouse_status.changes & PDC_MOUSE_WHEEL_RIGHT) + +/* mouse bit-masks */ + +#define BUTTON1_RELEASED 0x00000001L +#define BUTTON1_PRESSED 0x00000002L +#define BUTTON1_CLICKED 0x00000004L +#define BUTTON1_DOUBLE_CLICKED 0x00000008L +#define BUTTON1_TRIPLE_CLICKED 0x00000010L +#define BUTTON1_MOVED 0x00000010L /* PDCurses */ + +#define BUTTON2_RELEASED 0x00000020L +#define BUTTON2_PRESSED 0x00000040L +#define BUTTON2_CLICKED 0x00000080L +#define BUTTON2_DOUBLE_CLICKED 0x00000100L +#define BUTTON2_TRIPLE_CLICKED 0x00000200L +#define BUTTON2_MOVED 0x00000200L /* PDCurses */ + +#define BUTTON3_RELEASED 0x00000400L +#define BUTTON3_PRESSED 0x00000800L +#define BUTTON3_CLICKED 0x00001000L +#define BUTTON3_DOUBLE_CLICKED 0x00002000L +#define BUTTON3_TRIPLE_CLICKED 0x00004000L +#define BUTTON3_MOVED 0x00004000L /* PDCurses */ + +/* For the ncurses-compatible functions only, BUTTON4_PRESSED and + BUTTON5_PRESSED are returned for mouse scroll wheel up and down; + otherwise PDCurses doesn't support buttons 4 and 5... except + as described above for WinGUI, and perhaps to be extended to + other PDCurses flavors */ + +#define BUTTON4_RELEASED 0x00008000L +#define BUTTON4_PRESSED 0x00010000L +#define BUTTON4_CLICKED 0x00020000L +#define BUTTON4_DOUBLE_CLICKED 0x00040000L +#define BUTTON4_TRIPLE_CLICKED 0x00080000L + +#define BUTTON5_RELEASED 0x00100000L +#define BUTTON5_PRESSED 0x00200000L +#define BUTTON5_CLICKED 0x00400000L +#define BUTTON5_DOUBLE_CLICKED 0x00800000L +#define BUTTON5_TRIPLE_CLICKED 0x01000000L + +#define MOUSE_WHEEL_SCROLL 0x02000000L /* PDCurses */ +#define BUTTON_MODIFIER_SHIFT 0x04000000L /* PDCurses */ +#define BUTTON_MODIFIER_CONTROL 0x08000000L /* PDCurses */ +#define BUTTON_MODIFIER_ALT 0x10000000L /* PDCurses */ + +#define ALL_MOUSE_EVENTS 0x1fffffffL +#define REPORT_MOUSE_POSITION 0x20000000L + +/* ncurses mouse interface */ + +typedef unsigned long mmask_t; + +typedef struct +{ + short id; /* unused, always 0 */ + int x, y, z; /* x, y same as MOUSE_STATUS; z unused */ + mmask_t bstate; /* equivalent to changes + button[], but + in the same format as used for mousemask() */ +} MEVENT; + +#if defined(PDC_NCMOUSE) && !defined(NCURSES_MOUSE_VERSION) +# define NCURSES_MOUSE_VERSION 2 +#endif + +#ifdef NCURSES_MOUSE_VERSION +# define BUTTON_SHIFT BUTTON_MODIFIER_SHIFT +# define BUTTON_CONTROL BUTTON_MODIFIER_CONTROL +# define BUTTON_CTRL BUTTON_MODIFIER_CONTROL +# define BUTTON_ALT BUTTON_MODIFIER_ALT +#else +# define BUTTON_SHIFT PDC_BUTTON_SHIFT +# define BUTTON_CONTROL PDC_BUTTON_CONTROL +# define BUTTON_ALT PDC_BUTTON_ALT +#endif + +/*---------------------------------------------------------------------- + * + * Window and Screen Structures + * + */ + +typedef struct _win /* definition of a window */ +{ + int _cury; /* current pseudo-cursor */ + int _curx; + int _maxy; /* max window coordinates */ + int _maxx; + int _begy; /* origin on screen */ + int _begx; + int _flags; /* window properties */ + chtype _attrs; /* standard attributes and colors */ + chtype _bkgd; /* background, normally blank */ + bool _clear; /* causes clear at next refresh */ + bool _leaveit; /* leaves cursor where it is */ + bool _scroll; /* allows window scrolling */ + bool _nodelay; /* input character wait flag */ + bool _immed; /* immediate update flag */ + bool _sync; /* synchronise window ancestors */ + bool _use_keypad; /* flags keypad key mode active */ + chtype **_y; /* pointer to line pointer array */ + int *_firstch; /* first changed character in line */ + int *_lastch; /* last changed character in line */ + int _tmarg; /* top of scrolling region */ + int _bmarg; /* bottom of scrolling region */ + int _delayms; /* milliseconds of delay for getch() */ + int _parx, _pary; /* coords relative to parent (0,0) */ + struct _win *_parent; /* subwin's pointer to parent win */ +} WINDOW; + +/* Avoid using the SCREEN struct directly -- use the corresponding + functions if possible. This struct may eventually be made private. */ + +typedef struct +{ + bool alive; /* if initscr() called, and not endwin() */ + bool autocr; /* if cr -> lf */ + bool cbreak; /* if terminal unbuffered */ + bool echo; /* if terminal echo */ + bool raw_inp; /* raw input mode (v. cooked input) */ + bool raw_out; /* raw output mode (7 v. 8 bits) */ + bool audible; /* FALSE if the bell is visual */ + bool mono; /* TRUE if current screen is mono */ + bool resized; /* TRUE if TERM has been resized */ + bool orig_attr; /* TRUE if we have the original colors */ + short orig_fore; /* original screen foreground color */ + short orig_back; /* original screen foreground color */ + int cursrow; /* position of physical cursor */ + int curscol; /* position of physical cursor */ + int visibility; /* visibility of cursor */ + int orig_cursor; /* original cursor size */ + int lines; /* new value for LINES */ + int cols; /* new value for COLS */ + unsigned long _trap_mbe; /* trap these mouse button events */ + unsigned long _map_mbe_to_key; /* map mouse buttons to slk */ + int mouse_wait; /* time to wait (in ms) for a + button release after a press, in + order to count it as a click */ + int slklines; /* lines in use by slk_init() */ + WINDOW *slk_winptr; /* window for slk */ + int linesrippedoff; /* lines ripped off via ripoffline() */ + int linesrippedoffontop; /* lines ripped off on + top via ripoffline() */ + int delaytenths; /* 1/10ths second to wait block + getch() for */ + bool _preserve; /* TRUE if screen background + to be preserved */ + int _restore; /* specifies if screen background + to be restored, and how */ + bool save_key_modifiers; /* TRUE if each key modifiers saved + with each key press */ + bool return_key_modifiers; /* TRUE if modifier keys are + returned as "real" keys */ + bool key_code; /* TRUE if last key is a special key; + used internally by get_wch() */ +#ifdef XCURSES + int XcurscrSize; /* size of Xcurscr shared memory block */ + bool sb_on; + int sb_viewport_y; + int sb_viewport_x; + int sb_total_y; + int sb_total_x; + int sb_cur_y; + int sb_cur_x; + int exit_key; +#endif + short line_color; /* color of line attributes - default -1 */ +} SCREEN; + +/*---------------------------------------------------------------------- + * + * External Variables + * + */ + +#ifdef PDC_DLL_BUILD +# ifdef CURSES_LIBRARY +# define PDCEX __declspec(dllexport) extern +# else +# define PDCEX __declspec(dllimport) +# endif +#else +# define PDCEX extern +#endif + +PDCEX int LINES; /* terminal height */ +PDCEX int COLS; /* terminal width */ +PDCEX WINDOW *stdscr; /* the default screen window */ +PDCEX WINDOW *curscr; /* the current screen image */ +PDCEX SCREEN *SP; /* curses variables */ +PDCEX MOUSE_STATUS Mouse_status; +PDCEX int COLORS; +PDCEX int COLOR_PAIRS; +PDCEX int TABSIZE; +PDCEX chtype acs_map[]; /* alternate character set map */ +PDCEX char ttytype[]; /* terminal name/description */ +PDCEX PDC_version_info PDC_version; + +/*man-start************************************************************** + +Text Attributes +=============== + +Originally, PDCurses used a short (16 bits) for its chtype. To include +color, a number of things had to be sacrificed from the strict Unix and +System V support. The main problem was fitting all character attributes +and color into an unsigned char (all 8 bits!). + +Today, PDCurses by default uses a long (32 bits) for its chtype, as in +System V. The short chtype is still available, by undefining CHTYPE_LONG +and rebuilding the library. + +The following is the structure of a win->_attrs chtype: + +short form: + + +-----------------------------------------------+ + |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0| + +-----------------------------------------------+ + color number | attrs | character eg 'a' + +The available non-color attributes are bold, reverse and blink. Others +have no effect. The high order char is an index into an array of +physical colors (defined in color.c) -- 32 foreground/background color +pairs (5 bits) plus 3 bits for other attributes. + +long form: + + +--------------------------------------------------------------------+ + |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|..| 2| 1| 0| + +--------------------------------------------------------------------+ + color number | modifiers | character eg 'a' + +The available non-color attributes are bold, underline, invisible, +right-line, left-line, protect, reverse and blink. 256 color pairs (8 +bits), 8 bits for other attributes, and 16 bits for character data. + + Note that there is now a "super-long" 64-bit form, available by +defining CHTYPE_LONG to be 2: + +------------------------------------------------------------------------------- +|63|62|61|60|59|..|34|33|32|31|30|29|28|..|22|21|20|19|18|17|16|..| 3| 2| 1| 0| +------------------------------------------------------------------------------- + color number | modifiers | character eg 'a' + + + We take five more bits for the character (thus allowing Unicode values +past 64K; UTF-16 can go up to 0x10ffff, requiring 21 bits total), and +four more bits for attributes. Three are currently used as A_OVERLINE, A_DIM, +and A_STRIKEOUT; one more is reserved for future use. 31 bits are then used +for color. These are usually just treated as the usual palette +indices, and range from 0 to 255. However, if bit 63 is +set, the remaining 30 bits are interpreted as foreground RGB (first +fifteen bits, five bits for each of the three channels) and background RGB +(same scheme using the remaining 15 bits.) + +**man-end****************************************************************/ + +/*** Video attribute macros ***/ + +#define A_NORMAL (chtype)0 + +#ifdef CHTYPE_LONG + +# if(CHTYPE_LONG >= 2) /* 64-bit chtypes */ + # define PDC_CHARTEXT_BITS 21 + # define A_CHARTEXT (chtype)( ((chtype)0x1 << PDC_CHARTEXT_BITS) - 1) + # define A_ALTCHARSET ((chtype)0x001 << PDC_CHARTEXT_BITS) + # define A_RIGHTLINE ((chtype)0x002 << PDC_CHARTEXT_BITS) + # define A_LEFTLINE ((chtype)0x004 << PDC_CHARTEXT_BITS) + # define A_INVIS ((chtype)0x008 << PDC_CHARTEXT_BITS) + # define A_UNDERLINE ((chtype)0x010 << PDC_CHARTEXT_BITS) + # define A_REVERSE ((chtype)0x020 << PDC_CHARTEXT_BITS) + # define A_BLINK ((chtype)0x040 << PDC_CHARTEXT_BITS) + # define A_BOLD ((chtype)0x080 << PDC_CHARTEXT_BITS) + # define A_OVERLINE ((chtype)0x100 << PDC_CHARTEXT_BITS) + # define A_STRIKEOUT ((chtype)0x200 << PDC_CHARTEXT_BITS) + # define A_DIM ((chtype)0x400 << PDC_CHARTEXT_BITS) +#if 0 + /* May come up with a use for this bit */ + /* someday; reserved for the future: */ + # define A_FUTURE_2 ((chtype)0x800 << PDC_CHARTEXT_BITS) +#endif + # define PDC_COLOR_SHIFT (PDC_CHARTEXT_BITS + 12) + # define A_COLOR ((chtype)0x7fffffff << PDC_COLOR_SHIFT) + # define A_RGB_COLOR ((chtype)0x40000000 << PDC_COLOR_SHIFT) + # define A_ATTRIBUTES (((chtype)0xfff << PDC_CHARTEXT_BITS) | A_COLOR) + # define A_RGB( rfore, gfore, bfore, rback, gback, bback) \ + (( (((chtype)(bfore) << 25) \ + | ((chtype)(gfore) << 20) \ + | ((chtype)(rfore) << 15) \ + | ((chtype)(bback) << 10) \ + | ((chtype)(gback) << 5) \ + | ((chtype)(rback) )) << PDC_COLOR_SHIFT) | A_RGB_COLOR) +# else /* plain ol' 32-bit chtypes */ + # define A_ALTCHARSET (chtype)0x00010000 + # define A_RIGHTLINE (chtype)0x00020000 + # define A_LEFTLINE (chtype)0x00040000 + # define A_INVIS (chtype)0x00080000 + # define A_UNDERLINE (chtype)0x00100000 + # define A_REVERSE (chtype)0x00200000 + # define A_BLINK (chtype)0x00400000 + # define A_BOLD (chtype)0x00800000 + # define A_COLOR (chtype)0xff000000 + # define A_RGB_COLOR A_NORMAL +#ifdef PDC_WIDE + # define A_CHARTEXT (chtype)0x0000ffff + # define A_ATTRIBUTES (chtype)0xffff0000 + # define A_DIM A_NORMAL + # define A_OVERLINE A_NORMAL + # define A_STRIKEOUT A_NORMAL +#else /* with 8-bit chars, we have bits for these attribs : */ + # define A_CHARTEXT (chtype)0x000000ff + # define A_ATTRIBUTES (chtype)0xffffe000 + # define A_DIM (chtype)0x00008000 + # define A_OVERLINE (chtype)0x00004000 + # define A_STRIKEOUT (chtype)0x00002000 +#endif + # define PDC_COLOR_SHIFT 24 +#endif + + +# define A_ITALIC A_INVIS +# define A_PROTECT (A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE) + +#else /* 16-bit chtypes */ +# define A_BOLD (chtype)0x0100 /* X/Open */ +# define A_REVERSE (chtype)0x0200 /* X/Open */ +# define A_BLINK (chtype)0x0400 /* X/Open */ + +# define A_ATTRIBUTES (chtype)0xff00 /* X/Open */ +# define A_CHARTEXT (chtype)0x00ff /* X/Open */ +# define A_COLOR (chtype)0xf800 /* System V */ + +# define A_ALTCHARSET A_NORMAL /* X/Open */ +# define A_PROTECT A_NORMAL /* X/Open */ +# define A_UNDERLINE A_NORMAL /* X/Open */ +# define A_OVERLINE A_NORMAL /* X/Open */ +# define A_STRIKEOUT A_NORMAL /* X/Open */ + +# define A_LEFTLINE A_NORMAL +# define A_RIGHTLINE A_NORMAL +# define A_ITALIC A_NORMAL +# define A_INVIS A_NORMAL +# define A_RGB_COLOR A_NORMAL +# define A_DIM A_NORMAL + +# define PDC_COLOR_SHIFT 11 +#endif + +#define A_STANDOUT (A_REVERSE | A_BOLD) /* X/Open */ + +#define CHR_MSK A_CHARTEXT /* Obsolete */ +#define ATR_MSK A_ATTRIBUTES /* Obsolete */ +#define ATR_NRM A_NORMAL /* Obsolete */ + +/* For use with attr_t -- X/Open says, "these shall be distinct", so + this is a non-conforming implementation. */ + +#define WA_NORMAL A_NORMAL + +#define WA_ALTCHARSET A_ALTCHARSET +#define WA_BLINK A_BLINK +#define WA_BOLD A_BOLD +#define WA_DIM A_DIM +#define WA_INVIS A_INVIS +#define WA_LEFT A_LEFTLINE +#define WA_PROTECT A_PROTECT +#define WA_REVERSE A_REVERSE +#define WA_RIGHT A_RIGHTLINE +#define WA_STANDOUT A_STANDOUT +#define WA_UNDERLINE A_UNDERLINE + +#define WA_HORIZONTAL A_NORMAL +#define WA_LOW A_NORMAL +#define WA_TOP A_NORMAL +#define WA_VERTICAL A_NORMAL + +#define WA_ATTRIBUTES A_ATTRIBUTES + +/*** Alternate character set macros ***/ + +/* 'w' = 32-bit chtype; acs_map[] index | A_ALTCHARSET + 'n' = 16-bit chtype; it gets the fallback set because no bit is + available for A_ALTCHARSET */ + +#ifdef CHTYPE_LONG +# define PDC_ACS(w, n) ((chtype)w | A_ALTCHARSET) +#else +# define PDC_ACS(w, n) ((chtype)n) +#endif + +/* VT100-compatible symbols -- box chars */ + +#define ACS_LRCORNER PDC_ACS('V', '+') +#define ACS_URCORNER PDC_ACS('W', '+') +#define ACS_ULCORNER PDC_ACS('X', '+') +#define ACS_LLCORNER PDC_ACS('Y', '+') +#define ACS_PLUS PDC_ACS('Z', '+') +#define ACS_LTEE PDC_ACS('[', '+') +#define ACS_RTEE PDC_ACS('\\', '+') +#define ACS_BTEE PDC_ACS(']', '+') +#define ACS_TTEE PDC_ACS('^', '+') +#define ACS_HLINE PDC_ACS('_', '-') +#define ACS_VLINE PDC_ACS('`', '|') + +/* PDCurses-only ACS chars. Don't use if ncurses compatibility matters. +Some won't work in non-wide X11 builds (see 'acs_defs.h' for details). */ + +#define ACS_CENT PDC_ACS('{', 'c') +#define ACS_YEN PDC_ACS('|', 'y') +#define ACS_PESETA PDC_ACS('}', 'p') +#define ACS_HALF PDC_ACS('&', '/') +#define ACS_QUARTER PDC_ACS('\'', '/') +#define ACS_LEFT_ANG_QU PDC_ACS(')', '<') +#define ACS_RIGHT_ANG_QU PDC_ACS('*', '>') +#define ACS_D_HLINE PDC_ACS('a', '-') +#define ACS_D_VLINE PDC_ACS('b', '|') +#define ACS_CLUB PDC_ACS( 11, 'C') +#define ACS_HEART PDC_ACS( 12, 'H') +#define ACS_SPADE PDC_ACS( 13, 'S') +#define ACS_SMILE PDC_ACS( 14, 'O') +#define ACS_REV_SMILE PDC_ACS( 15, 'O') +#define ACS_MED_BULLET PDC_ACS( 16, '.') +#define ACS_WHITE_BULLET PDC_ACS( 17, 'O') +#define ACS_PILCROW PDC_ACS( 18, 'O') +#define ACS_SECTION PDC_ACS( 19, 'O') + +#define ACS_SUP2 PDC_ACS(',', '2') +#define ACS_ALPHA PDC_ACS('.', 'a') +#define ACS_BETA PDC_ACS('/', 'b') +#define ACS_GAMMA PDC_ACS('0', 'y') +#define ACS_UP_SIGMA PDC_ACS('1', 'S') +#define ACS_LO_SIGMA PDC_ACS('2', 's') +#define ACS_MU PDC_ACS('4', 'u') +#define ACS_TAU PDC_ACS('5', 't') +#define ACS_UP_PHI PDC_ACS('6', 'F') +#define ACS_THETA PDC_ACS('7', 't') +#define ACS_OMEGA PDC_ACS('8', 'w') +#define ACS_DELTA PDC_ACS('9', 'd') +#define ACS_INFINITY PDC_ACS('-', 'i') +#define ACS_LO_PHI PDC_ACS( 22, 'f') +#define ACS_EPSILON PDC_ACS(':', 'e') +#define ACS_INTERSECT PDC_ACS('e', 'u') +#define ACS_TRIPLE_BAR PDC_ACS('f', '=') +#define ACS_DIVISION PDC_ACS('c', '/') +#define ACS_APPROX_EQ PDC_ACS('d', '~') +#define ACS_SM_BULLET PDC_ACS('g', '.') +#define ACS_SQUARE_ROOT PDC_ACS('i', '!') +#define ACS_UBLOCK PDC_ACS('p', '^') +#define ACS_BBLOCK PDC_ACS('q', '_') +#define ACS_LBLOCK PDC_ACS('r', '<') +#define ACS_RBLOCK PDC_ACS('s', '>') + +#define ACS_A_ORDINAL PDC_ACS(20, 'a') +#define ACS_O_ORDINAL PDC_ACS(21, 'o') +#define ACS_INV_QUERY PDC_ACS(24, '?') +#define ACS_REV_NOT PDC_ACS(25, '!') +#define ACS_NOT PDC_ACS(26, '!') +#define ACS_INV_BANG PDC_ACS(23, '!') +#define ACS_UP_INTEGRAL PDC_ACS(27, '|') +#define ACS_LO_INTEGRAL PDC_ACS(28, '|') +#define ACS_SUP_N PDC_ACS(29, 'n') +#define ACS_CENTER_SQU PDC_ACS(30, 'x') +#define ACS_F_WITH_HOOK PDC_ACS(31, 'f') + +#define ACS_SD_LRCORNER PDC_ACS(';', '+') +#define ACS_SD_URCORNER PDC_ACS('<', '+') +#define ACS_SD_ULCORNER PDC_ACS('=', '+') +#define ACS_SD_LLCORNER PDC_ACS('>', '+') +#define ACS_SD_PLUS PDC_ACS('?', '+') +#define ACS_SD_LTEE PDC_ACS('@', '+') +#define ACS_SD_RTEE PDC_ACS('A', '+') +#define ACS_SD_BTEE PDC_ACS('B', '+') +#define ACS_SD_TTEE PDC_ACS('C', '+') + +#define ACS_D_LRCORNER PDC_ACS('D', '+') +#define ACS_D_URCORNER PDC_ACS('E', '+') +#define ACS_D_ULCORNER PDC_ACS('F', '+') +#define ACS_D_LLCORNER PDC_ACS('G', '+') +#define ACS_D_PLUS PDC_ACS('H', '+') +#define ACS_D_LTEE PDC_ACS('I', '+') +#define ACS_D_RTEE PDC_ACS('J', '+') +#define ACS_D_BTEE PDC_ACS('K', '+') +#define ACS_D_TTEE PDC_ACS('L', '+') + +#define ACS_DS_LRCORNER PDC_ACS('M', '+') +#define ACS_DS_URCORNER PDC_ACS('N', '+') +#define ACS_DS_ULCORNER PDC_ACS('O', '+') +#define ACS_DS_LLCORNER PDC_ACS('P', '+') +#define ACS_DS_PLUS PDC_ACS('Q', '+') +#define ACS_DS_LTEE PDC_ACS('R', '+') +#define ACS_DS_RTEE PDC_ACS('S', '+') +#define ACS_DS_BTEE PDC_ACS('T', '+') +#define ACS_DS_TTEE PDC_ACS('U', '+') + +/* VT100-compatible symbols -- other */ + +#define ACS_S1 PDC_ACS('l', '-') +#define ACS_S9 PDC_ACS('o', '_') +#define ACS_DIAMOND PDC_ACS('j', '+') +#define ACS_CKBOARD PDC_ACS('k', ':') +#define ACS_DEGREE PDC_ACS('w', '\'') +#define ACS_PLMINUS PDC_ACS('x', '#') +#define ACS_BULLET PDC_ACS('h', 'o') + +/* Teletype 5410v1 symbols -- these are defined in SysV curses, but + are not well-supported by most terminals. Stick to VT100 characters + for optimum portability. */ + +#define ACS_LARROW PDC_ACS('!', '<') +#define ACS_RARROW PDC_ACS(' ', '>') +#define ACS_DARROW PDC_ACS('#', 'v') +#define ACS_UARROW PDC_ACS('"', '^') +#define ACS_BOARD PDC_ACS('+', '#') +#define ACS_LTBOARD PDC_ACS('y', '#') +#define ACS_LANTERN PDC_ACS('z', '*') +#define ACS_BLOCK PDC_ACS('t', '#') + +/* That goes double for these -- undocumented SysV symbols. Don't use + them. */ + +#define ACS_S3 PDC_ACS('m', '-') +#define ACS_S7 PDC_ACS('n', '-') +#define ACS_LEQUAL PDC_ACS('u', '<') +#define ACS_GEQUAL PDC_ACS('v', '>') +#define ACS_PI PDC_ACS('$', 'n') +#define ACS_NEQUAL PDC_ACS('%', '+') +#define ACS_STERLING PDC_ACS('~', 'L') + +/* Box char aliases */ + +#define ACS_BSSB ACS_ULCORNER +#define ACS_SSBB ACS_LLCORNER +#define ACS_BBSS ACS_URCORNER +#define ACS_SBBS ACS_LRCORNER +#define ACS_SBSS ACS_RTEE +#define ACS_SSSB ACS_LTEE +#define ACS_SSBS ACS_BTEE +#define ACS_BSSS ACS_TTEE +#define ACS_BSBS ACS_HLINE +#define ACS_SBSB ACS_VLINE +#define ACS_SSSS ACS_PLUS + +/* cchar_t aliases */ + +#ifdef PDC_WIDE +# define WACS_LRCORNER (&(acs_map['V'])) +# define WACS_URCORNER (&(acs_map['W'])) +# define WACS_ULCORNER (&(acs_map['X'])) +# define WACS_LLCORNER (&(acs_map['Y'])) +# define WACS_PLUS (&(acs_map['Z'])) +# define WACS_LTEE (&(acs_map['['])) +# define WACS_RTEE (&(acs_map['\\'])) +# define WACS_BTEE (&(acs_map[']'])) +# define WACS_TTEE (&(acs_map['^'])) +# define WACS_HLINE (&(acs_map['_'])) +# define WACS_VLINE (&(acs_map['`'])) + +# define WACS_CENT (&(acs_map['{'])) +# define WACS_YEN (&(acs_map['|'])) +# define WACS_PESETA (&(acs_map['}'])) +# define WACS_HALF (&(acs_map['&'])) +# define WACS_QUARTER (&(acs_map['\''])) +# define WACS_LEFT_ANG_QU (&(acs_map[')'])) +# define WACS_RIGHT_ANG_QU (&(acs_map['*'])) +# define WACS_D_HLINE (&(acs_map['a'])) +# define WACS_D_VLINE (&(acs_map['b'])) +# define WACS_CLUB (&(acs_map[ 11])) +# define WACS_HEART (&(acs_map[ 12])) +# define WACS_SPADE (&(acs_map[ 13])) +# define WACS_SMILE (&(acs_map[ 14])) +# define WACS_REV_SMILE (&(acs_map[ 15])) +# define WACS_MED_BULLET (&(acs_map[ 16])) +# define WACS_WHITE_BULLET (&(acs_map[ 17])) +# define WACS_PILCROW (&(acs_map[ 18])) +# define WACS_SECTION (&(acs_map[ 19])) + +# define WACS_SUP2 (&(acs_map[','])) +# define WACS_ALPHA (&(acs_map['.'])) +# define WACS_BETA (&(acs_map['/'])) +# define WACS_GAMMA (&(acs_map['0'])) +# define WACS_UP_SIGMA (&(acs_map['1'])) +# define WACS_LO_SIGMA (&(acs_map['2'])) +# define WACS_MU (&(acs_map['4'])) +# define WACS_TAU (&(acs_map['5'])) +# define WACS_UP_PHI (&(acs_map['6'])) +# define WACS_THETA (&(acs_map['7'])) +# define WACS_OMEGA (&(acs_map['8'])) +# define WACS_DELTA (&(acs_map['9'])) +# define WACS_INFINITY (&(acs_map['-'])) +# define WACS_LO_PHI (&(acs_map[ 22])) +# define WACS_EPSILON (&(acs_map[':'])) +# define WACS_INTERSECT (&(acs_map['e'])) +# define WACS_TRIPLE_BAR (&(acs_map['f'])) +# define WACS_DIVISION (&(acs_map['c'])) +# define WACS_APPROX_EQ (&(acs_map['d'])) +# define WACS_SM_BULLET (&(acs_map['g'])) +# define WACS_SQUARE_ROOT (&(acs_map['i'])) +# define WACS_UBLOCK (&(acs_map['p'])) +# define WACS_BBLOCK (&(acs_map['q'])) +# define WACS_LBLOCK (&(acs_map['r'])) +# define WACS_RBLOCK (&(acs_map['s'])) + +# define WACS_A_ORDINAL (&(acs_map[20])) +# define WACS_O_ORDINAL (&(acs_map[21])) +# define WACS_INV_QUERY (&(acs_map[24])) +# define WACS_REV_NOT (&(acs_map[25])) +# define WACS_NOT (&(acs_map[26])) +# define WACS_INV_BANG (&(acs_map[23])) +# define WACS_UP_INTEGRAL (&(acs_map[27])) +# define WACS_LO_INTEGRAL (&(acs_map[28])) +# define WACS_SUP_N (&(acs_map[29])) +# define WACS_CENTER_SQU (&(acs_map[30])) +# define WACS_F_WITH_HOOK (&(acs_map[31])) + +# define WACS_SD_LRCORNER (&(acs_map[';'])) +# define WACS_SD_URCORNER (&(acs_map['<'])) +# define WACS_SD_ULCORNER (&(acs_map['='])) +# define WACS_SD_LLCORNER (&(acs_map['>'])) +# define WACS_SD_PLUS (&(acs_map['?'])) +# define WACS_SD_LTEE (&(acs_map['@'])) +# define WACS_SD_RTEE (&(acs_map['A'])) +# define WACS_SD_BTEE (&(acs_map['B'])) +# define WACS_SD_TTEE (&(acs_map['C'])) + +# define WACS_D_LRCORNER (&(acs_map['D'])) +# define WACS_D_URCORNER (&(acs_map['E'])) +# define WACS_D_ULCORNER (&(acs_map['F'])) +# define WACS_D_LLCORNER (&(acs_map['G'])) +# define WACS_D_PLUS (&(acs_map['H'])) +# define WACS_D_LTEE (&(acs_map['I'])) +# define WACS_D_RTEE (&(acs_map['J'])) +# define WACS_D_BTEE (&(acs_map['K'])) +# define WACS_D_TTEE (&(acs_map['L'])) + +# define WACS_DS_LRCORNER (&(acs_map['M'])) +# define WACS_DS_URCORNER (&(acs_map['N'])) +# define WACS_DS_ULCORNER (&(acs_map['O'])) +# define WACS_DS_LLCORNER (&(acs_map['P'])) +# define WACS_DS_PLUS (&(acs_map['Q'])) +# define WACS_DS_LTEE (&(acs_map['R'])) +# define WACS_DS_RTEE (&(acs_map['S'])) +# define WACS_DS_BTEE (&(acs_map['T'])) +# define WACS_DS_TTEE (&(acs_map['U'])) + +# define WACS_S1 (&(acs_map['l'])) +# define WACS_S9 (&(acs_map['o'])) +# define WACS_DIAMOND (&(acs_map['j'])) +# define WACS_CKBOARD (&(acs_map['k'])) +# define WACS_DEGREE (&(acs_map['w'])) +# define WACS_PLMINUS (&(acs_map['x'])) +# define WACS_BULLET (&(acs_map['h'])) + + +# define WACS_LARROW (&(acs_map['!'])) +# define WACS_RARROW (&(acs_map[' '])) +# define WACS_DARROW (&(acs_map['#'])) +# define WACS_UARROW (&(acs_map['"'])) +# define WACS_BOARD (&(acs_map['+'])) +# define WACS_LTBOARD (&(acs_map['y'])) +# define WACS_LANTERN (&(acs_map['z'])) +# define WACS_BLOCK (&(acs_map['t'])) + +# define WACS_S3 (&(acs_map['m'])) +# define WACS_S7 (&(acs_map['n'])) +# define WACS_LEQUAL (&(acs_map['u'])) +# define WACS_GEQUAL (&(acs_map['v'])) +# define WACS_PI (&(acs_map['$'])) +# define WACS_NEQUAL (&(acs_map['%'])) +# define WACS_STERLING (&(acs_map['~'])) + +# define WACS_BSSB WACS_ULCORNER +# define WACS_SSBB WACS_LLCORNER +# define WACS_BBSS WACS_URCORNER +# define WACS_SBBS WACS_LRCORNER +# define WACS_SBSS WACS_RTEE +# define WACS_SSSB WACS_LTEE +# define WACS_SSBS WACS_BTEE +# define WACS_BSSS WACS_TTEE +# define WACS_BSBS WACS_HLINE +# define WACS_SBSB WACS_VLINE +# define WACS_SSSS WACS_PLUS +#endif + +/*** Color macros ***/ + +#define COLOR_BLACK 0 + +#ifdef PDC_RGB /* RGB */ +# define COLOR_RED 1 +# define COLOR_GREEN 2 +# define COLOR_BLUE 4 +#else /* BGR */ +# define COLOR_BLUE 1 +# define COLOR_GREEN 2 +# define COLOR_RED 4 +#endif + +#define COLOR_CYAN (COLOR_BLUE | COLOR_GREEN) +#define COLOR_MAGENTA (COLOR_RED | COLOR_BLUE) +#define COLOR_YELLOW (COLOR_RED | COLOR_GREEN) + +#define COLOR_WHITE 7 + +/*---------------------------------------------------------------------- + * + * Function and Keypad Key Definitions + * Many are just for compatibility + * + */ + +#ifdef PDC_WIDE + #define KEY_OFFSET 0xec00 +#else + #define KEY_OFFSET 0x100 +#endif + +#define KEY_CODE_YES (KEY_OFFSET + 0x00) /* If get_wch() gives a key code */ + +#define KEY_BREAK (KEY_OFFSET + 0x01) /* Not on PC KBD */ +#define KEY_DOWN (KEY_OFFSET + 0x02) /* Down arrow key */ +#define KEY_UP (KEY_OFFSET + 0x03) /* Up arrow key */ +#define KEY_LEFT (KEY_OFFSET + 0x04) /* Left arrow key */ +#define KEY_RIGHT (KEY_OFFSET + 0x05) /* Right arrow key */ +#define KEY_HOME (KEY_OFFSET + 0x06) /* home key */ +#define KEY_BACKSPACE (KEY_OFFSET + 0x07) /* not on pc */ +#define KEY_F0 (KEY_OFFSET + 0x08) /* function keys; 64 reserved */ + +#define KEY_DL (KEY_OFFSET + 0x48) /* delete line */ +#define KEY_IL (KEY_OFFSET + 0x49) /* insert line */ +#define KEY_DC (KEY_OFFSET + 0x4a) /* delete character */ +#define KEY_IC (KEY_OFFSET + 0x4b) /* insert char or enter ins mode */ +#define KEY_EIC (KEY_OFFSET + 0x4c) /* exit insert char mode */ +#define KEY_CLEAR (KEY_OFFSET + 0x4d) /* clear screen */ +#define KEY_EOS (KEY_OFFSET + 0x4e) /* clear to end of screen */ +#define KEY_EOL (KEY_OFFSET + 0x4f) /* clear to end of line */ +#define KEY_SF (KEY_OFFSET + 0x50) /* scroll 1 line forward */ +#define KEY_SR (KEY_OFFSET + 0x51) /* scroll 1 line back (reverse) */ +#define KEY_NPAGE (KEY_OFFSET + 0x52) /* next page */ +#define KEY_PPAGE (KEY_OFFSET + 0x53) /* previous page */ +#define KEY_STAB (KEY_OFFSET + 0x54) /* set tab */ +#define KEY_CTAB (KEY_OFFSET + 0x55) /* clear tab */ +#define KEY_CATAB (KEY_OFFSET + 0x56) /* clear all tabs */ +#define KEY_ENTER (KEY_OFFSET + 0x57) /* enter or send (unreliable) */ +#define KEY_SRESET (KEY_OFFSET + 0x58) /* soft/reset (partial/unreliable) */ +#define KEY_RESET (KEY_OFFSET + 0x59) /* reset/hard reset (unreliable) */ +#define KEY_PRINT (KEY_OFFSET + 0x5a) /* print/copy */ +#define KEY_LL (KEY_OFFSET + 0x5b) /* home down/bottom (lower left) */ +#define KEY_ABORT (KEY_OFFSET + 0x5c) /* abort/terminate key (any) */ +#define KEY_SHELP (KEY_OFFSET + 0x5d) /* short help */ +#define KEY_LHELP (KEY_OFFSET + 0x5e) /* long help */ +#define KEY_BTAB (KEY_OFFSET + 0x5f) /* Back tab key */ +#define KEY_BEG (KEY_OFFSET + 0x60) /* beg(inning) key */ +#define KEY_CANCEL (KEY_OFFSET + 0x61) /* cancel key */ +#define KEY_CLOSE (KEY_OFFSET + 0x62) /* close key */ +#define KEY_COMMAND (KEY_OFFSET + 0x63) /* cmd (command) key */ +#define KEY_COPY (KEY_OFFSET + 0x64) /* copy key */ +#define KEY_CREATE (KEY_OFFSET + 0x65) /* create key */ +#define KEY_END (KEY_OFFSET + 0x66) /* end key */ +#define KEY_EXIT (KEY_OFFSET + 0x67) /* exit key */ +#define KEY_FIND (KEY_OFFSET + 0x68) /* find key */ +#define KEY_HELP (KEY_OFFSET + 0x69) /* help key */ +#define KEY_MARK (KEY_OFFSET + 0x6a) /* mark key */ +#define KEY_MESSAGE (KEY_OFFSET + 0x6b) /* message key */ +#define KEY_MOVE (KEY_OFFSET + 0x6c) /* move key */ +#define KEY_NEXT (KEY_OFFSET + 0x6d) /* next object key */ +#define KEY_OPEN (KEY_OFFSET + 0x6e) /* open key */ +#define KEY_OPTIONS (KEY_OFFSET + 0x6f) /* options key */ +#define KEY_PREVIOUS (KEY_OFFSET + 0x70) /* previous object key */ +#define KEY_REDO (KEY_OFFSET + 0x71) /* redo key */ +#define KEY_REFERENCE (KEY_OFFSET + 0x72) /* ref(erence) key */ +#define KEY_REFRESH (KEY_OFFSET + 0x73) /* refresh key */ +#define KEY_REPLACE (KEY_OFFSET + 0x74) /* replace key */ +#define KEY_RESTART (KEY_OFFSET + 0x75) /* restart key */ +#define KEY_RESUME (KEY_OFFSET + 0x76) /* resume key */ +#define KEY_SAVE (KEY_OFFSET + 0x77) /* save key */ +#define KEY_SBEG (KEY_OFFSET + 0x78) /* shifted beginning key */ +#define KEY_SCANCEL (KEY_OFFSET + 0x79) /* shifted cancel key */ +#define KEY_SCOMMAND (KEY_OFFSET + 0x7a) /* shifted command key */ +#define KEY_SCOPY (KEY_OFFSET + 0x7b) /* shifted copy key */ +#define KEY_SCREATE (KEY_OFFSET + 0x7c) /* shifted create key */ +#define KEY_SDC (KEY_OFFSET + 0x7d) /* shifted delete char key */ +#define KEY_SDL (KEY_OFFSET + 0x7e) /* shifted delete line key */ +#define KEY_SELECT (KEY_OFFSET + 0x7f) /* select key */ +#define KEY_SEND (KEY_OFFSET + 0x80) /* shifted end key */ +#define KEY_SEOL (KEY_OFFSET + 0x81) /* shifted clear line key */ +#define KEY_SEXIT (KEY_OFFSET + 0x82) /* shifted exit key */ +#define KEY_SFIND (KEY_OFFSET + 0x83) /* shifted find key */ +#define KEY_SHOME (KEY_OFFSET + 0x84) /* shifted home key */ +#define KEY_SIC (KEY_OFFSET + 0x85) /* shifted input key */ + +#define KEY_SLEFT (KEY_OFFSET + 0x87) /* shifted left arrow key */ +#define KEY_SMESSAGE (KEY_OFFSET + 0x88) /* shifted message key */ +#define KEY_SMOVE (KEY_OFFSET + 0x89) /* shifted move key */ +#define KEY_SNEXT (KEY_OFFSET + 0x8a) /* shifted next key */ +#define KEY_SOPTIONS (KEY_OFFSET + 0x8b) /* shifted options key */ +#define KEY_SPREVIOUS (KEY_OFFSET + 0x8c) /* shifted prev key */ +#define KEY_SPRINT (KEY_OFFSET + 0x8d) /* shifted print key */ +#define KEY_SREDO (KEY_OFFSET + 0x8e) /* shifted redo key */ +#define KEY_SREPLACE (KEY_OFFSET + 0x8f) /* shifted replace key */ +#define KEY_SRIGHT (KEY_OFFSET + 0x90) /* shifted right arrow */ +#define KEY_SRSUME (KEY_OFFSET + 0x91) /* shifted resume key */ +#define KEY_SSAVE (KEY_OFFSET + 0x92) /* shifted save key */ +#define KEY_SSUSPEND (KEY_OFFSET + 0x93) /* shifted suspend key */ +#define KEY_SUNDO (KEY_OFFSET + 0x94) /* shifted undo key */ +#define KEY_SUSPEND (KEY_OFFSET + 0x95) /* suspend key */ +#define KEY_UNDO (KEY_OFFSET + 0x96) /* undo key */ + +/* PDCurses-specific key definitions -- PC only */ + +#define ALT_0 (KEY_OFFSET + 0x97) +#define ALT_1 (KEY_OFFSET + 0x98) +#define ALT_2 (KEY_OFFSET + 0x99) +#define ALT_3 (KEY_OFFSET + 0x9a) +#define ALT_4 (KEY_OFFSET + 0x9b) +#define ALT_5 (KEY_OFFSET + 0x9c) +#define ALT_6 (KEY_OFFSET + 0x9d) +#define ALT_7 (KEY_OFFSET + 0x9e) +#define ALT_8 (KEY_OFFSET + 0x9f) +#define ALT_9 (KEY_OFFSET + 0xa0) +#define ALT_A (KEY_OFFSET + 0xa1) +#define ALT_B (KEY_OFFSET + 0xa2) +#define ALT_C (KEY_OFFSET + 0xa3) +#define ALT_D (KEY_OFFSET + 0xa4) +#define ALT_E (KEY_OFFSET + 0xa5) +#define ALT_F (KEY_OFFSET + 0xa6) +#define ALT_G (KEY_OFFSET + 0xa7) +#define ALT_H (KEY_OFFSET + 0xa8) +#define ALT_I (KEY_OFFSET + 0xa9) +#define ALT_J (KEY_OFFSET + 0xaa) +#define ALT_K (KEY_OFFSET + 0xab) +#define ALT_L (KEY_OFFSET + 0xac) +#define ALT_M (KEY_OFFSET + 0xad) +#define ALT_N (KEY_OFFSET + 0xae) +#define ALT_O (KEY_OFFSET + 0xaf) +#define ALT_P (KEY_OFFSET + 0xb0) +#define ALT_Q (KEY_OFFSET + 0xb1) +#define ALT_R (KEY_OFFSET + 0xb2) +#define ALT_S (KEY_OFFSET + 0xb3) +#define ALT_T (KEY_OFFSET + 0xb4) +#define ALT_U (KEY_OFFSET + 0xb5) +#define ALT_V (KEY_OFFSET + 0xb6) +#define ALT_W (KEY_OFFSET + 0xb7) +#define ALT_X (KEY_OFFSET + 0xb8) +#define ALT_Y (KEY_OFFSET + 0xb9) +#define ALT_Z (KEY_OFFSET + 0xba) + +#define CTL_LEFT (KEY_OFFSET + 0xbb) /* Control-Left-Arrow */ +#define CTL_RIGHT (KEY_OFFSET + 0xbc) +#define CTL_PGUP (KEY_OFFSET + 0xbd) +#define CTL_PGDN (KEY_OFFSET + 0xbe) +#define CTL_HOME (KEY_OFFSET + 0xbf) +#define CTL_END (KEY_OFFSET + 0xc0) + +#define KEY_A1 (KEY_OFFSET + 0xc1) /* upper left on Virtual keypad */ +#define KEY_A2 (KEY_OFFSET + 0xc2) /* upper middle on Virt. keypad */ +#define KEY_A3 (KEY_OFFSET + 0xc3) /* upper right on Vir. keypad */ +#define KEY_B1 (KEY_OFFSET + 0xc4) /* middle left on Virt. keypad */ +#define KEY_B2 (KEY_OFFSET + 0xc5) /* center on Virt. keypad */ +#define KEY_B3 (KEY_OFFSET + 0xc6) /* middle right on Vir. keypad */ +#define KEY_C1 (KEY_OFFSET + 0xc7) /* lower left on Virt. keypad */ +#define KEY_C2 (KEY_OFFSET + 0xc8) /* lower middle on Virt. keypad */ +#define KEY_C3 (KEY_OFFSET + 0xc9) /* lower right on Vir. keypad */ + +#define PADSLASH (KEY_OFFSET + 0xca) /* slash on keypad */ +#define PADENTER (KEY_OFFSET + 0xcb) /* enter on keypad */ +#define CTL_PADENTER (KEY_OFFSET + 0xcc) /* ctl-enter on keypad */ +#define ALT_PADENTER (KEY_OFFSET + 0xcd) /* alt-enter on keypad */ +#define PADSTOP (KEY_OFFSET + 0xce) /* stop on keypad */ +#define PADSTAR (KEY_OFFSET + 0xcf) /* star on keypad */ +#define PADMINUS (KEY_OFFSET + 0xd0) /* minus on keypad */ +#define PADPLUS (KEY_OFFSET + 0xd1) /* plus on keypad */ +#define CTL_PADSTOP (KEY_OFFSET + 0xd2) /* ctl-stop on keypad */ +#define CTL_PADCENTER (KEY_OFFSET + 0xd3) /* ctl-enter on keypad */ +#define CTL_PADPLUS (KEY_OFFSET + 0xd4) /* ctl-plus on keypad */ +#define CTL_PADMINUS (KEY_OFFSET + 0xd5) /* ctl-minus on keypad */ +#define CTL_PADSLASH (KEY_OFFSET + 0xd6) /* ctl-slash on keypad */ +#define CTL_PADSTAR (KEY_OFFSET + 0xd7) /* ctl-star on keypad */ +#define ALT_PADPLUS (KEY_OFFSET + 0xd8) /* alt-plus on keypad */ +#define ALT_PADMINUS (KEY_OFFSET + 0xd9) /* alt-minus on keypad */ +#define ALT_PADSLASH (KEY_OFFSET + 0xda) /* alt-slash on keypad */ +#define ALT_PADSTAR (KEY_OFFSET + 0xdb) /* alt-star on keypad */ +#define ALT_PADSTOP (KEY_OFFSET + 0xdc) /* alt-stop on keypad */ +#define CTL_INS (KEY_OFFSET + 0xdd) /* ctl-insert */ +#define ALT_DEL (KEY_OFFSET + 0xde) /* alt-delete */ +#define ALT_INS (KEY_OFFSET + 0xdf) /* alt-insert */ +#define CTL_UP (KEY_OFFSET + 0xe0) /* ctl-up arrow */ +#define CTL_DOWN (KEY_OFFSET + 0xe1) /* ctl-down arrow */ +#define CTL_TAB (KEY_OFFSET + 0xe2) /* ctl-tab */ +#define ALT_TAB (KEY_OFFSET + 0xe3) +#define ALT_MINUS (KEY_OFFSET + 0xe4) +#define ALT_EQUAL (KEY_OFFSET + 0xe5) +#define ALT_HOME (KEY_OFFSET + 0xe6) +#define ALT_PGUP (KEY_OFFSET + 0xe7) +#define ALT_PGDN (KEY_OFFSET + 0xe8) +#define ALT_END (KEY_OFFSET + 0xe9) +#define ALT_UP (KEY_OFFSET + 0xea) /* alt-up arrow */ +#define ALT_DOWN (KEY_OFFSET + 0xeb) /* alt-down arrow */ +#define ALT_RIGHT (KEY_OFFSET + 0xec) /* alt-right arrow */ +#define ALT_LEFT (KEY_OFFSET + 0xed) /* alt-left arrow */ +#define ALT_ENTER (KEY_OFFSET + 0xee) /* alt-enter */ +#define ALT_ESC (KEY_OFFSET + 0xef) /* alt-escape */ +#define ALT_BQUOTE (KEY_OFFSET + 0xf0) /* alt-back quote */ +#define ALT_LBRACKET (KEY_OFFSET + 0xf1) /* alt-left bracket */ +#define ALT_RBRACKET (KEY_OFFSET + 0xf2) /* alt-right bracket */ +#define ALT_SEMICOLON (KEY_OFFSET + 0xf3) /* alt-semi-colon */ +#define ALT_FQUOTE (KEY_OFFSET + 0xf4) /* alt-forward quote */ +#define ALT_COMMA (KEY_OFFSET + 0xf5) /* alt-comma */ +#define ALT_STOP (KEY_OFFSET + 0xf6) /* alt-stop */ +#define ALT_FSLASH (KEY_OFFSET + 0xf7) /* alt-forward slash */ +#define ALT_BKSP (KEY_OFFSET + 0xf8) /* alt-backspace */ +#define CTL_BKSP (KEY_OFFSET + 0xf9) /* ctl-backspace */ +#define PAD0 (KEY_OFFSET + 0xfa) /* keypad 0 */ + +#define CTL_PAD0 (KEY_OFFSET + 0xfb) /* ctl-keypad 0 */ +#define CTL_PAD1 (KEY_OFFSET + 0xfc) +#define CTL_PAD2 (KEY_OFFSET + 0xfd) +#define CTL_PAD3 (KEY_OFFSET + 0xfe) +#define CTL_PAD4 (KEY_OFFSET + 0xff) +#define CTL_PAD5 (KEY_OFFSET + 0x100) +#define CTL_PAD6 (KEY_OFFSET + 0x101) +#define CTL_PAD7 (KEY_OFFSET + 0x102) +#define CTL_PAD8 (KEY_OFFSET + 0x103) +#define CTL_PAD9 (KEY_OFFSET + 0x104) + +#define ALT_PAD0 (KEY_OFFSET + 0x105) /* alt-keypad 0 */ +#define ALT_PAD1 (KEY_OFFSET + 0x106) +#define ALT_PAD2 (KEY_OFFSET + 0x107) +#define ALT_PAD3 (KEY_OFFSET + 0x108) +#define ALT_PAD4 (KEY_OFFSET + 0x109) +#define ALT_PAD5 (KEY_OFFSET + 0x10a) +#define ALT_PAD6 (KEY_OFFSET + 0x10b) +#define ALT_PAD7 (KEY_OFFSET + 0x10c) +#define ALT_PAD8 (KEY_OFFSET + 0x10d) +#define ALT_PAD9 (KEY_OFFSET + 0x10e) + +#define CTL_DEL (KEY_OFFSET + 0x10f) /* clt-delete */ +#define ALT_BSLASH (KEY_OFFSET + 0x110) /* alt-back slash */ +#define CTL_ENTER (KEY_OFFSET + 0x111) /* ctl-enter */ + +#define SHF_PADENTER (KEY_OFFSET + 0x112) /* shift-enter on keypad */ +#define SHF_PADSLASH (KEY_OFFSET + 0x113) /* shift-slash on keypad */ +#define SHF_PADSTAR (KEY_OFFSET + 0x114) /* shift-star on keypad */ +#define SHF_PADPLUS (KEY_OFFSET + 0x115) /* shift-plus on keypad */ +#define SHF_PADMINUS (KEY_OFFSET + 0x116) /* shift-minus on keypad */ +#define SHF_UP (KEY_OFFSET + 0x117) /* shift-up on keypad */ +#define SHF_DOWN (KEY_OFFSET + 0x118) /* shift-down on keypad */ +#define SHF_IC (KEY_OFFSET + 0x119) /* shift-insert on keypad */ +#define SHF_DC (KEY_OFFSET + 0x11a) /* shift-delete on keypad */ + +#define KEY_MOUSE (KEY_OFFSET + 0x11b) /* "mouse" key */ +#define KEY_SHIFT_L (KEY_OFFSET + 0x11c) /* Left-shift */ +#define KEY_SHIFT_R (KEY_OFFSET + 0x11d) /* Right-shift */ +#define KEY_CONTROL_L (KEY_OFFSET + 0x11e) /* Left-control */ +#define KEY_CONTROL_R (KEY_OFFSET + 0x11f) /* Right-control */ +#define KEY_ALT_L (KEY_OFFSET + 0x120) /* Left-alt */ +#define KEY_ALT_R (KEY_OFFSET + 0x121) /* Right-alt */ +#define KEY_RESIZE (KEY_OFFSET + 0x122) /* Window resize */ +#define KEY_SUP (KEY_OFFSET + 0x123) /* Shifted up arrow */ +#define KEY_SDOWN (KEY_OFFSET + 0x124) /* Shifted down arrow */ + + /* The following were added 2011 Sep 14, and are */ + /* not returned by most flavors of PDCurses: */ + +#define CTL_SEMICOLON (KEY_OFFSET + 0x125) +#define CTL_EQUAL (KEY_OFFSET + 0x126) +#define CTL_COMMA (KEY_OFFSET + 0x127) +#define CTL_MINUS (KEY_OFFSET + 0x128) +#define CTL_STOP (KEY_OFFSET + 0x129) +#define CTL_FSLASH (KEY_OFFSET + 0x12a) +#define CTL_BQUOTE (KEY_OFFSET + 0x12b) + +#define KEY_APPS (KEY_OFFSET + 0x12c) +#define KEY_SAPPS (KEY_OFFSET + 0x12d) +#define CTL_APPS (KEY_OFFSET + 0x12e) +#define ALT_APPS (KEY_OFFSET + 0x12f) + +#define KEY_PAUSE (KEY_OFFSET + 0x130) +#define KEY_SPAUSE (KEY_OFFSET + 0x131) +#define CTL_PAUSE (KEY_OFFSET + 0x132) + +#define KEY_PRINTSCREEN (KEY_OFFSET + 0x133) +#define ALT_PRINTSCREEN (KEY_OFFSET + 0x134) +#define KEY_SCROLLLOCK (KEY_OFFSET + 0x135) +#define ALT_SCROLLLOCK (KEY_OFFSET + 0x136) + +#define CTL_0 (KEY_OFFSET + 0x137) +#define CTL_1 (KEY_OFFSET + 0x138) +#define CTL_2 (KEY_OFFSET + 0x139) +#define CTL_3 (KEY_OFFSET + 0x13a) +#define CTL_4 (KEY_OFFSET + 0x13b) +#define CTL_5 (KEY_OFFSET + 0x13c) +#define CTL_6 (KEY_OFFSET + 0x13d) +#define CTL_7 (KEY_OFFSET + 0x13e) +#define CTL_8 (KEY_OFFSET + 0x13f) +#define CTL_9 (KEY_OFFSET + 0x140) + +#define KEY_BROWSER_BACK (KEY_OFFSET + 0x141) +#define KEY_SBROWSER_BACK (KEY_OFFSET + 0x142) +#define KEY_CBROWSER_BACK (KEY_OFFSET + 0x143) +#define KEY_ABROWSER_BACK (KEY_OFFSET + 0x144) +#define KEY_BROWSER_FWD (KEY_OFFSET + 0x145) +#define KEY_SBROWSER_FWD (KEY_OFFSET + 0x146) +#define KEY_CBROWSER_FWD (KEY_OFFSET + 0x147) +#define KEY_ABROWSER_FWD (KEY_OFFSET + 0x148) +#define KEY_BROWSER_REF (KEY_OFFSET + 0x149) +#define KEY_SBROWSER_REF (KEY_OFFSET + 0x14A) +#define KEY_CBROWSER_REF (KEY_OFFSET + 0x14B) +#define KEY_ABROWSER_REF (KEY_OFFSET + 0x14C) +#define KEY_BROWSER_STOP (KEY_OFFSET + 0x14D) +#define KEY_SBROWSER_STOP (KEY_OFFSET + 0x14E) +#define KEY_CBROWSER_STOP (KEY_OFFSET + 0x14F) +#define KEY_ABROWSER_STOP (KEY_OFFSET + 0x150) +#define KEY_SEARCH (KEY_OFFSET + 0x151) +#define KEY_SSEARCH (KEY_OFFSET + 0x152) +#define KEY_CSEARCH (KEY_OFFSET + 0x153) +#define KEY_ASEARCH (KEY_OFFSET + 0x154) +#define KEY_FAVORITES (KEY_OFFSET + 0x155) +#define KEY_SFAVORITES (KEY_OFFSET + 0x156) +#define KEY_CFAVORITES (KEY_OFFSET + 0x157) +#define KEY_AFAVORITES (KEY_OFFSET + 0x158) +#define KEY_BROWSER_HOME (KEY_OFFSET + 0x159) +#define KEY_SBROWSER_HOME (KEY_OFFSET + 0x15A) +#define KEY_CBROWSER_HOME (KEY_OFFSET + 0x15B) +#define KEY_ABROWSER_HOME (KEY_OFFSET + 0x15C) +#define KEY_VOLUME_MUTE (KEY_OFFSET + 0x15D) +#define KEY_SVOLUME_MUTE (KEY_OFFSET + 0x15E) +#define KEY_CVOLUME_MUTE (KEY_OFFSET + 0x15F) +#define KEY_AVOLUME_MUTE (KEY_OFFSET + 0x160) +#define KEY_VOLUME_DOWN (KEY_OFFSET + 0x161) +#define KEY_SVOLUME_DOWN (KEY_OFFSET + 0x162) +#define KEY_CVOLUME_DOWN (KEY_OFFSET + 0x163) +#define KEY_AVOLUME_DOWN (KEY_OFFSET + 0x164) +#define KEY_VOLUME_UP (KEY_OFFSET + 0x165) +#define KEY_SVOLUME_UP (KEY_OFFSET + 0x166) +#define KEY_CVOLUME_UP (KEY_OFFSET + 0x167) +#define KEY_AVOLUME_UP (KEY_OFFSET + 0x168) +#define KEY_NEXT_TRACK (KEY_OFFSET + 0x169) +#define KEY_SNEXT_TRACK (KEY_OFFSET + 0x16A) +#define KEY_CNEXT_TRACK (KEY_OFFSET + 0x16B) +#define KEY_ANEXT_TRACK (KEY_OFFSET + 0x16C) +#define KEY_PREV_TRACK (KEY_OFFSET + 0x16D) +#define KEY_SPREV_TRACK (KEY_OFFSET + 0x16E) +#define KEY_CPREV_TRACK (KEY_OFFSET + 0x16F) +#define KEY_APREV_TRACK (KEY_OFFSET + 0x170) +#define KEY_MEDIA_STOP (KEY_OFFSET + 0x171) +#define KEY_SMEDIA_STOP (KEY_OFFSET + 0x172) +#define KEY_CMEDIA_STOP (KEY_OFFSET + 0x173) +#define KEY_AMEDIA_STOP (KEY_OFFSET + 0x174) +#define KEY_PLAY_PAUSE (KEY_OFFSET + 0x175) +#define KEY_SPLAY_PAUSE (KEY_OFFSET + 0x176) +#define KEY_CPLAY_PAUSE (KEY_OFFSET + 0x177) +#define KEY_APLAY_PAUSE (KEY_OFFSET + 0x178) +#define KEY_LAUNCH_MAIL (KEY_OFFSET + 0x179) +#define KEY_SLAUNCH_MAIL (KEY_OFFSET + 0x17A) +#define KEY_CLAUNCH_MAIL (KEY_OFFSET + 0x17B) +#define KEY_ALAUNCH_MAIL (KEY_OFFSET + 0x17C) +#define KEY_MEDIA_SELECT (KEY_OFFSET + 0x17D) +#define KEY_SMEDIA_SELECT (KEY_OFFSET + 0x17E) +#define KEY_CMEDIA_SELECT (KEY_OFFSET + 0x17F) +#define KEY_AMEDIA_SELECT (KEY_OFFSET + 0x180) +#define KEY_LAUNCH_APP1 (KEY_OFFSET + 0x181) +#define KEY_SLAUNCH_APP1 (KEY_OFFSET + 0x182) +#define KEY_CLAUNCH_APP1 (KEY_OFFSET + 0x183) +#define KEY_ALAUNCH_APP1 (KEY_OFFSET + 0x184) +#define KEY_LAUNCH_APP2 (KEY_OFFSET + 0x185) +#define KEY_SLAUNCH_APP2 (KEY_OFFSET + 0x186) +#define KEY_CLAUNCH_APP2 (KEY_OFFSET + 0x187) +#define KEY_ALAUNCH_APP2 (KEY_OFFSET + 0x188) + +#define KEY_MIN KEY_BREAK /* Minimum curses key value */ +#define KEY_MAX KEY_ALAUNCH_APP2 /* Maximum curses key */ + +#define KEY_F(n) (KEY_F0 + (n)) + +/*---------------------------------------------------------------------- + * + * PDCurses Function Declarations + * + */ + +/* Standard */ + +PDCEX int addch(const chtype); +PDCEX int addchnstr(const chtype *, int); +PDCEX int addchstr(const chtype *); +PDCEX int addnstr(const char *, int); +PDCEX int addstr(const char *); +PDCEX int attroff(chtype); +PDCEX int attron(chtype); +PDCEX int attrset(chtype); +PDCEX int attr_get(attr_t *, short *, void *); +PDCEX int attr_off(attr_t, void *); +PDCEX int attr_on(attr_t, void *); +PDCEX int attr_set(attr_t, short, void *); +PDCEX int baudrate(void); +PDCEX int beep(void); +PDCEX int bkgd(chtype); +PDCEX void bkgdset(chtype); +PDCEX int border(chtype, chtype, chtype, chtype, + chtype, chtype, chtype, chtype); +PDCEX int box(WINDOW *, chtype, chtype); +PDCEX bool can_change_color(void); +PDCEX int cbreak(void); +PDCEX int chgat(int, attr_t, short, const void *); +PDCEX int clearok(WINDOW *, bool); +PDCEX int clear(void); +PDCEX int clrtobot(void); +PDCEX int clrtoeol(void); +PDCEX int color_content(short, short *, short *, short *); +PDCEX int color_set(short, void *); +PDCEX int copywin(const WINDOW *, WINDOW *, int, int, int, + int, int, int, int); +PDCEX int curs_set(int); +PDCEX int def_prog_mode(void); +PDCEX int def_shell_mode(void); +PDCEX int delay_output(int); +PDCEX int delch(void); +PDCEX int deleteln(void); +PDCEX void delscreen(SCREEN *); +PDCEX int delwin(WINDOW *); +PDCEX WINDOW *derwin(WINDOW *, int, int, int, int); +PDCEX int doupdate(void); +PDCEX WINDOW *dupwin(WINDOW *); +PDCEX int echochar(const chtype); +PDCEX int echo(void); +PDCEX int endwin(void); +PDCEX char erasechar(void); +PDCEX int erase(void); +PDCEX void filter(void); +PDCEX int flash(void); +PDCEX int flushinp(void); +PDCEX chtype getbkgd(WINDOW *); +PDCEX int getnstr(char *, int); +PDCEX int getstr(char *); +PDCEX WINDOW *getwin(FILE *); +PDCEX int halfdelay(int); +PDCEX bool has_colors(void); +PDCEX bool has_ic(void); +PDCEX bool has_il(void); +PDCEX int hline(chtype, int); +PDCEX void idcok(WINDOW *, bool); +PDCEX int idlok(WINDOW *, bool); +PDCEX void immedok(WINDOW *, bool); +PDCEX int inchnstr(chtype *, int); +PDCEX int inchstr(chtype *); +PDCEX chtype inch(void); +PDCEX int init_color(short, short, short, short); +PDCEX int init_pair(short, short, short); +PDCEX WINDOW *initscr(void); +PDCEX int innstr(char *, int); +PDCEX int insch(chtype); +PDCEX int insdelln(int); +PDCEX int insertln(void); +PDCEX int insnstr(const char *, int); +PDCEX int insstr(const char *); +PDCEX int instr(char *); +PDCEX int intrflush(WINDOW *, bool); +PDCEX bool isendwin(void); +PDCEX bool is_linetouched(WINDOW *, int); +PDCEX bool is_wintouched(WINDOW *); +PDCEX char *keyname(int); +PDCEX int keypad(WINDOW *, bool); +PDCEX char killchar(void); +PDCEX int leaveok(WINDOW *, bool); +PDCEX char *longname(void); +PDCEX int meta(WINDOW *, bool); +PDCEX int move(int, int); +PDCEX int mvaddch(int, int, const chtype); +PDCEX int mvaddchnstr(int, int, const chtype *, int); +PDCEX int mvaddchstr(int, int, const chtype *); +PDCEX int mvaddnstr(int, int, const char *, int); +PDCEX int mvaddstr(int, int, const char *); +PDCEX int mvchgat(int, int, int, attr_t, short, const void *); +PDCEX int mvcur(int, int, int, int); +PDCEX int mvdelch(int, int); +PDCEX int mvderwin(WINDOW *, int, int); +PDCEX int mvgetch(int, int); +PDCEX int mvgetnstr(int, int, char *, int); +PDCEX int mvgetstr(int, int, char *); +PDCEX int mvhline(int, int, chtype, int); +PDCEX chtype mvinch(int, int); +PDCEX int mvinchnstr(int, int, chtype *, int); +PDCEX int mvinchstr(int, int, chtype *); +PDCEX int mvinnstr(int, int, char *, int); +PDCEX int mvinsch(int, int, chtype); +PDCEX int mvinsnstr(int, int, const char *, int); +PDCEX int mvinsstr(int, int, const char *); +PDCEX int mvinstr(int, int, char *); +PDCEX int mvprintw(int, int, const char *, ...); +PDCEX int mvscanw(int, int, const char *, ...); +PDCEX int mvvline(int, int, chtype, int); +PDCEX int mvwaddchnstr(WINDOW *, int, int, const chtype *, int); +PDCEX int mvwaddchstr(WINDOW *, int, int, const chtype *); +PDCEX int mvwaddch(WINDOW *, int, int, const chtype); +PDCEX int mvwaddnstr(WINDOW *, int, int, const char *, int); +PDCEX int mvwaddstr(WINDOW *, int, int, const char *); +PDCEX int mvwchgat(WINDOW *, int, int, int, attr_t, short, const void *); +PDCEX int mvwdelch(WINDOW *, int, int); +PDCEX int mvwgetch(WINDOW *, int, int); +PDCEX int mvwgetnstr(WINDOW *, int, int, char *, int); +PDCEX int mvwgetstr(WINDOW *, int, int, char *); +PDCEX int mvwhline(WINDOW *, int, int, chtype, int); +PDCEX int mvwinchnstr(WINDOW *, int, int, chtype *, int); +PDCEX int mvwinchstr(WINDOW *, int, int, chtype *); +PDCEX chtype mvwinch(WINDOW *, int, int); +PDCEX int mvwinnstr(WINDOW *, int, int, char *, int); +PDCEX int mvwinsch(WINDOW *, int, int, chtype); +PDCEX int mvwinsnstr(WINDOW *, int, int, const char *, int); +PDCEX int mvwinsstr(WINDOW *, int, int, const char *); +PDCEX int mvwinstr(WINDOW *, int, int, char *); +PDCEX int mvwin(WINDOW *, int, int); +PDCEX int mvwprintw(WINDOW *, int, int, const char *, ...); +PDCEX int mvwscanw(WINDOW *, int, int, const char *, ...); +PDCEX int mvwvline(WINDOW *, int, int, chtype, int); +PDCEX int napms(int); +PDCEX WINDOW *newpad(int, int); +PDCEX SCREEN *newterm(const char *, FILE *, FILE *); +PDCEX WINDOW *newwin(int, int, int, int); +PDCEX int nl(void); +PDCEX int nocbreak(void); +PDCEX int nodelay(WINDOW *, bool); +PDCEX int noecho(void); +PDCEX int nonl(void); +PDCEX void noqiflush(void); +PDCEX int noraw(void); +PDCEX int notimeout(WINDOW *, bool); +PDCEX int overlay(const WINDOW *, WINDOW *); +PDCEX int overwrite(const WINDOW *, WINDOW *); +PDCEX int pair_content(short, short *, short *); +PDCEX int pechochar(WINDOW *, chtype); +PDCEX int pnoutrefresh(WINDOW *, int, int, int, int, int, int); +PDCEX int prefresh(WINDOW *, int, int, int, int, int, int); +PDCEX int printw(const char *, ...); +PDCEX int putwin(WINDOW *, FILE *); +PDCEX void qiflush(void); +PDCEX int raw(void); +PDCEX int redrawwin(WINDOW *); +PDCEX int refresh(void); +PDCEX int reset_prog_mode(void); +PDCEX int reset_shell_mode(void); +PDCEX int resetty(void); +PDCEX int ripoffline(int, int (*)(WINDOW *, int)); +PDCEX int savetty(void); +PDCEX int scanw(const char *, ...); +PDCEX int scr_dump(const char *); +PDCEX int scr_init(const char *); +PDCEX int scr_restore(const char *); +PDCEX int scr_set(const char *); +PDCEX int scrl(int); +PDCEX int scroll(WINDOW *); +PDCEX int scrollok(WINDOW *, bool); +PDCEX SCREEN *set_term(SCREEN *); +PDCEX int setscrreg(int, int); +PDCEX int slk_attroff(const chtype); +PDCEX int slk_attr_off(const attr_t, void *); +PDCEX int slk_attron(const chtype); +PDCEX int slk_attr_on(const attr_t, void *); +PDCEX int slk_attrset(const chtype); +PDCEX int slk_attr_set(const attr_t, short, void *); +PDCEX int slk_clear(void); +PDCEX int slk_color(short); +PDCEX int slk_init(int); +PDCEX char *slk_label(int); +PDCEX int slk_noutrefresh(void); +PDCEX int slk_refresh(void); +PDCEX int slk_restore(void); +PDCEX int slk_set(int, const char *, int); +PDCEX int slk_touch(void); +PDCEX int standend(void); +PDCEX int standout(void); +PDCEX int start_color(void); +PDCEX WINDOW *subpad(WINDOW *, int, int, int, int); +PDCEX WINDOW *subwin(WINDOW *, int, int, int, int); +PDCEX int syncok(WINDOW *, bool); +PDCEX chtype termattrs(void); +PDCEX attr_t term_attrs(void); +PDCEX char *termname(void); +PDCEX void timeout(int); +PDCEX int touchline(WINDOW *, int, int); +PDCEX int touchwin(WINDOW *); +PDCEX int typeahead(int); +PDCEX int untouchwin(WINDOW *); +PDCEX void use_env(bool); +PDCEX int vidattr(chtype); +PDCEX int vid_attr(attr_t, short, void *); +PDCEX int vidputs(chtype, int (*)(int)); +PDCEX int vid_puts(attr_t, short, void *, int (*)(int)); +PDCEX int vline(chtype, int); +PDCEX int vw_printw(WINDOW *, const char *, va_list); +PDCEX int vwprintw(WINDOW *, const char *, va_list); +PDCEX int vw_scanw(WINDOW *, const char *, va_list); +PDCEX int vwscanw(WINDOW *, const char *, va_list); +PDCEX int waddchnstr(WINDOW *, const chtype *, int); +PDCEX int waddchstr(WINDOW *, const chtype *); +PDCEX int waddch(WINDOW *, const chtype); +PDCEX int waddnstr(WINDOW *, const char *, int); +PDCEX int waddstr(WINDOW *, const char *); +PDCEX int wattroff(WINDOW *, chtype); +PDCEX int wattron(WINDOW *, chtype); +PDCEX int wattrset(WINDOW *, chtype); +PDCEX int wattr_get(WINDOW *, attr_t *, short *, void *); +PDCEX int wattr_off(WINDOW *, attr_t, void *); +PDCEX int wattr_on(WINDOW *, attr_t, void *); +PDCEX int wattr_set(WINDOW *, attr_t, short, void *); +PDCEX void wbkgdset(WINDOW *, chtype); +PDCEX int wbkgd(WINDOW *, chtype); +PDCEX int wborder(WINDOW *, chtype, chtype, chtype, chtype, + chtype, chtype, chtype, chtype); +PDCEX int wchgat(WINDOW *, int, attr_t, short, const void *); +PDCEX int wclear(WINDOW *); +PDCEX int wclrtobot(WINDOW *); +PDCEX int wclrtoeol(WINDOW *); +PDCEX int wcolor_set(WINDOW *, short, void *); +PDCEX void wcursyncup(WINDOW *); +PDCEX int wdelch(WINDOW *); +PDCEX int wdeleteln(WINDOW *); +PDCEX int wechochar(WINDOW *, const chtype); +PDCEX int werase(WINDOW *); +PDCEX int wgetch(WINDOW *); +PDCEX int wgetnstr(WINDOW *, char *, int); +PDCEX int wgetstr(WINDOW *, char *); +PDCEX int whline(WINDOW *, chtype, int); +PDCEX int winchnstr(WINDOW *, chtype *, int); +PDCEX int winchstr(WINDOW *, chtype *); +PDCEX chtype winch(WINDOW *); +PDCEX int winnstr(WINDOW *, char *, int); +PDCEX int winsch(WINDOW *, chtype); +PDCEX int winsdelln(WINDOW *, int); +PDCEX int winsertln(WINDOW *); +PDCEX int winsnstr(WINDOW *, const char *, int); +PDCEX int winsstr(WINDOW *, const char *); +PDCEX int winstr(WINDOW *, char *); +PDCEX int wmove(WINDOW *, int, int); +PDCEX int wnoutrefresh(WINDOW *); +PDCEX int wprintw(WINDOW *, const char *, ...); +PDCEX int wredrawln(WINDOW *, int, int); +PDCEX int wrefresh(WINDOW *); +PDCEX int wscanw(WINDOW *, const char *, ...); +PDCEX int wscrl(WINDOW *, int); +PDCEX int wsetscrreg(WINDOW *, int, int); +PDCEX int wstandend(WINDOW *); +PDCEX int wstandout(WINDOW *); +PDCEX void wsyncdown(WINDOW *); +PDCEX void wsyncup(WINDOW *); +PDCEX void wtimeout(WINDOW *, int); +PDCEX int wtouchln(WINDOW *, int, int, int); +PDCEX int wvline(WINDOW *, chtype, int); + +/* Wide-character functions */ + +#ifdef PDC_WIDE +PDCEX int addnwstr(const wchar_t *, int); +PDCEX int addwstr(const wchar_t *); +PDCEX int add_wch(const cchar_t *); +PDCEX int add_wchnstr(const cchar_t *, int); +PDCEX int add_wchstr(const cchar_t *); +PDCEX int border_set(const cchar_t *, const cchar_t *, const cchar_t *, + const cchar_t *, const cchar_t *, const cchar_t *, + const cchar_t *, const cchar_t *); +PDCEX int box_set(WINDOW *, const cchar_t *, const cchar_t *); +PDCEX int echo_wchar(const cchar_t *); +PDCEX int erasewchar(wchar_t *); +PDCEX int getbkgrnd(cchar_t *); +PDCEX int getcchar(const cchar_t *, wchar_t *, attr_t *, short *, void *); +PDCEX int getn_wstr(wint_t *, int); +PDCEX int get_wch(wint_t *); +PDCEX int get_wstr(wint_t *); +PDCEX int hline_set(const cchar_t *, int); +PDCEX int innwstr(wchar_t *, int); +PDCEX int ins_nwstr(const wchar_t *, int); +PDCEX int ins_wch(const cchar_t *); +PDCEX int ins_wstr(const wchar_t *); +PDCEX int inwstr(wchar_t *); +PDCEX int in_wch(cchar_t *); +PDCEX int in_wchnstr(cchar_t *, int); +PDCEX int in_wchstr(cchar_t *); +PDCEX char *key_name(wchar_t); +PDCEX int killwchar(wchar_t *); +PDCEX int mvaddnwstr(int, int, const wchar_t *, int); +PDCEX int mvaddwstr(int, int, const wchar_t *); +PDCEX int mvadd_wch(int, int, const cchar_t *); +PDCEX int mvadd_wchnstr(int, int, const cchar_t *, int); +PDCEX int mvadd_wchstr(int, int, const cchar_t *); +PDCEX int mvgetn_wstr(int, int, wint_t *, int); +PDCEX int mvget_wch(int, int, wint_t *); +PDCEX int mvget_wstr(int, int, wint_t *); +PDCEX int mvhline_set(int, int, const cchar_t *, int); +PDCEX int mvinnwstr(int, int, wchar_t *, int); +PDCEX int mvins_nwstr(int, int, const wchar_t *, int); +PDCEX int mvins_wch(int, int, const cchar_t *); +PDCEX int mvins_wstr(int, int, const wchar_t *); +PDCEX int mvinwstr(int, int, wchar_t *); +PDCEX int mvin_wch(int, int, cchar_t *); +PDCEX int mvin_wchnstr(int, int, cchar_t *, int); +PDCEX int mvin_wchstr(int, int, cchar_t *); +PDCEX int mvvline_set(int, int, const cchar_t *, int); +PDCEX int mvwaddnwstr(WINDOW *, int, int, const wchar_t *, int); +PDCEX int mvwaddwstr(WINDOW *, int, int, const wchar_t *); +PDCEX int mvwadd_wch(WINDOW *, int, int, const cchar_t *); +PDCEX int mvwadd_wchnstr(WINDOW *, int, int, const cchar_t *, int); +PDCEX int mvwadd_wchstr(WINDOW *, int, int, const cchar_t *); +PDCEX int mvwgetn_wstr(WINDOW *, int, int, wint_t *, int); +PDCEX int mvwget_wch(WINDOW *, int, int, wint_t *); +PDCEX int mvwget_wstr(WINDOW *, int, int, wint_t *); +PDCEX int mvwhline_set(WINDOW *, int, int, const cchar_t *, int); +PDCEX int mvwinnwstr(WINDOW *, int, int, wchar_t *, int); +PDCEX int mvwins_nwstr(WINDOW *, int, int, const wchar_t *, int); +PDCEX int mvwins_wch(WINDOW *, int, int, const cchar_t *); +PDCEX int mvwins_wstr(WINDOW *, int, int, const wchar_t *); +PDCEX int mvwin_wch(WINDOW *, int, int, cchar_t *); +PDCEX int mvwin_wchnstr(WINDOW *, int, int, cchar_t *, int); +PDCEX int mvwin_wchstr(WINDOW *, int, int, cchar_t *); +PDCEX int mvwinwstr(WINDOW *, int, int, wchar_t *); +PDCEX int mvwvline_set(WINDOW *, int, int, const cchar_t *, int); +PDCEX int pecho_wchar(WINDOW *, const cchar_t*); +PDCEX int setcchar(cchar_t*, const wchar_t*, const attr_t, + short, const void*); +PDCEX int slk_wset(int, const wchar_t *, int); +PDCEX int unget_wch(const wchar_t); +PDCEX int vline_set(const cchar_t *, int); +PDCEX int waddnwstr(WINDOW *, const wchar_t *, int); +PDCEX int waddwstr(WINDOW *, const wchar_t *); +PDCEX int wadd_wch(WINDOW *, const cchar_t *); +PDCEX int wadd_wchnstr(WINDOW *, const cchar_t *, int); +PDCEX int wadd_wchstr(WINDOW *, const cchar_t *); +PDCEX int wbkgrnd(WINDOW *, const cchar_t *); +PDCEX void wbkgrndset(WINDOW *, const cchar_t *); +PDCEX int wborder_set(WINDOW *, const cchar_t *, const cchar_t *, + const cchar_t *, const cchar_t *, const cchar_t *, + const cchar_t *, const cchar_t *, const cchar_t *); +PDCEX int wecho_wchar(WINDOW *, const cchar_t *); +PDCEX int wgetbkgrnd(WINDOW *, cchar_t *); +PDCEX int wgetn_wstr(WINDOW *, wint_t *, int); +PDCEX int wget_wch(WINDOW *, wint_t *); +PDCEX int wget_wstr(WINDOW *, wint_t *); +PDCEX int whline_set(WINDOW *, const cchar_t *, int); +PDCEX int winnwstr(WINDOW *, wchar_t *, int); +PDCEX int wins_nwstr(WINDOW *, const wchar_t *, int); +PDCEX int wins_wch(WINDOW *, const cchar_t *); +PDCEX int wins_wstr(WINDOW *, const wchar_t *); +PDCEX int winwstr(WINDOW *, wchar_t *); +PDCEX int win_wch(WINDOW *, cchar_t *); +PDCEX int win_wchnstr(WINDOW *, cchar_t *, int); +PDCEX int win_wchstr(WINDOW *, cchar_t *); +PDCEX wchar_t *wunctrl(cchar_t *); +PDCEX int wvline_set(WINDOW *, const cchar_t *, int); +#endif + +/* Quasi-standard */ + +PDCEX chtype getattrs(WINDOW *); +PDCEX int getbegx(WINDOW *); +PDCEX int getbegy(WINDOW *); +PDCEX int getmaxx(WINDOW *); +PDCEX int getmaxy(WINDOW *); +PDCEX int getparx(WINDOW *); +PDCEX int getpary(WINDOW *); +PDCEX int getcurx(WINDOW *); +PDCEX int getcury(WINDOW *); +PDCEX void traceoff(void); +PDCEX void traceon(void); +PDCEX char *unctrl(chtype); + +PDCEX int crmode(void); +PDCEX int nocrmode(void); +PDCEX int draino(int); +PDCEX int resetterm(void); +PDCEX int fixterm(void); +PDCEX int saveterm(void); +PDCEX void setsyx(int, int); + +PDCEX int mouse_set(unsigned long); +PDCEX int mouse_on(unsigned long); +PDCEX int mouse_off(unsigned long); +PDCEX int request_mouse_pos(void); +PDCEX int map_button(unsigned long); +PDCEX void wmouse_position(WINDOW *, int *, int *); +PDCEX unsigned long getmouse(void); +PDCEX unsigned long getbmap(void); + +/* ncurses */ + +PDCEX int assume_default_colors(int, int); +PDCEX const char *curses_version(void); +PDCEX bool has_key(int); +PDCEX int use_default_colors(void); +PDCEX int wresize(WINDOW *, int, int); + +PDCEX int mouseinterval(int); +PDCEX mmask_t mousemask(mmask_t, mmask_t *); +PDCEX bool mouse_trafo(int *, int *, bool); +PDCEX int nc_getmouse(MEVENT *); +PDCEX int ungetmouse(MEVENT *); +PDCEX bool wenclose(const WINDOW *, int, int); +PDCEX bool wmouse_trafo(const WINDOW *, int *, int *, bool); + +/* PDCurses */ + +PDCEX int addrawch(chtype); +PDCEX int insrawch(chtype); +PDCEX bool is_termresized(void); +PDCEX int mvaddrawch(int, int, chtype); +PDCEX int mvdeleteln(int, int); +PDCEX int mvinsertln(int, int); +PDCEX int mvinsrawch(int, int, chtype); +PDCEX int mvwaddrawch(WINDOW *, int, int, chtype); +PDCEX int mvwdeleteln(WINDOW *, int, int); +PDCEX int mvwinsertln(WINDOW *, int, int); +PDCEX int mvwinsrawch(WINDOW *, int, int, chtype); +PDCEX int raw_output(bool); +PDCEX int resize_term(int, int); +PDCEX WINDOW *resize_window(WINDOW *, int, int); +PDCEX int waddrawch(WINDOW *, chtype); +PDCEX int winsrawch(WINDOW *, chtype); +PDCEX char wordchar(void); + +#ifdef PDC_WIDE +PDCEX wchar_t *slk_wlabel(int); +#endif + +PDCEX void PDC_debug(const char *, ...); +PDCEX int PDC_ungetch(int); +PDCEX int PDC_set_blink(bool); +PDCEX int PDC_set_line_color(short); +PDCEX void PDC_set_title(const char *); + +PDCEX int PDC_clearclipboard(void); +PDCEX int PDC_freeclipboard(char *); +PDCEX int PDC_getclipboard(char **, long *); +PDCEX int PDC_setclipboard(const char *, long); + +PDCEX unsigned long PDC_get_input_fd(void); +PDCEX unsigned long PDC_get_key_modifiers(void); +PDCEX int PDC_return_key_modifiers(bool); +PDCEX int PDC_save_key_modifiers(bool); +PDCEX void PDC_set_resize_limits( const int new_min_lines, + const int new_max_lines, + const int new_min_cols, + const int new_max_cols); + +#define FUNCTION_KEY_SHUT_DOWN 0 +#define FUNCTION_KEY_PASTE 1 +#define FUNCTION_KEY_ENLARGE_FONT 2 +#define FUNCTION_KEY_SHRINK_FONT 3 +#define FUNCTION_KEY_CHOOSE_FONT 4 +#define FUNCTION_KEY_ABORT 5 +#define PDC_MAX_FUNCTION_KEYS 6 + +PDCEX int PDC_set_function_key( const unsigned function, + const int new_key); + +PDCEX WINDOW *Xinitscr(int, char **); +#ifdef XCURSES +PDCEX void XCursesExit(void); +PDCEX int sb_init(void); +PDCEX int sb_set_horz(int, int, int); +PDCEX int sb_set_vert(int, int, int); +PDCEX int sb_get_horz(int *, int *, int *); +PDCEX int sb_get_vert(int *, int *, int *); +PDCEX int sb_refresh(void); + #endif + +/*** Functions defined as macros ***/ + +/* getch() and ungetch() conflict with some DOS libraries */ + +#define getch() wgetch(stdscr) +#define ungetch(ch) PDC_ungetch(ch) + +#define COLOR_PAIR(n) (((chtype)(n) << PDC_COLOR_SHIFT) & A_COLOR) +#define PAIR_NUMBER(n) ((((n) & A_COLOR) >> PDC_COLOR_SHIFT) & 0xff) + +/* These will _only_ work as macros */ + +#define getbegyx(w, y, x) (y = getbegy(w), x = getbegx(w)) +#define getmaxyx(w, y, x) (y = getmaxy(w), x = getmaxx(w)) +#define getparyx(w, y, x) (y = getpary(w), x = getparx(w)) +#define getyx(w, y, x) (y = getcury(w), x = getcurx(w)) + +#define getsyx(y, x) { if (curscr->_leaveit) (y)=(x)=-1; \ + else getyx(curscr,(y),(x)); } + +#ifdef NCURSES_MOUSE_VERSION +# define getmouse(x) nc_getmouse(x) +#endif + +/* return codes from PDC_getclipboard() and PDC_setclipboard() calls */ + +#define PDC_CLIP_SUCCESS 0 +#define PDC_CLIP_ACCESS_ERROR 1 +#define PDC_CLIP_EMPTY 2 +#define PDC_CLIP_MEMORY_ERROR 3 + +/* PDCurses key modifier masks */ + +#define PDC_KEY_MODIFIER_SHIFT 1 +#define PDC_KEY_MODIFIER_CONTROL 2 +#define PDC_KEY_MODIFIER_ALT 4 +#define PDC_KEY_MODIFIER_NUMLOCK 8 +#define PDC_KEY_MODIFIER_REPEAT 16 + +#ifdef __cplusplus +# undef bool +} +#endif + +#endif /* __PDCURSES__ */ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curspriv.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curspriv.h new file mode 100644 index 000000000..b5edcc173 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curspriv.h @@ -0,0 +1,134 @@ +/* Public Domain Curses */ + +/* Private definitions and declarations for use within PDCurses. + These should generally not be referenced by applications. */ + +#ifndef __CURSES_INTERNALS__ +#define __CURSES_INTERNALS__ 1 + +#define CURSES_LIBRARY +#include + +#if defined(__TURBOC__) || defined(__EMX__) || defined(__DJGPP__) || \ + defined(__CYGWIN__) || defined(__MINGW32__) || \ + defined(__WATCOMC__) || defined(__PACIFIC__) +# ifndef HAVE_VSSCANF +# define HAVE_VSSCANF /* have vsscanf() */ +# endif +#endif + +#if defined(__CYGWIN__) || defined(__MINGW32__) || \ + defined(__LCC__) || defined(__WATCOMC__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF /* have vsnprintf() */ +# endif +#endif + +#if defined(_MSC_VER) && defined(_WIN32) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE 1 /* kill nonsense warnings */ +#endif + +/*----------------------------------------------------------------------*/ + +typedef struct /* structure for ripped off lines */ +{ + int line; + int (*init)(WINDOW *, int); +} RIPPEDOFFLINE; + +/* Window properties */ + +#define _SUBWIN 0x01 /* window is a subwindow */ +#define _PAD 0x10 /* X/Open Pad. */ +#define _SUBPAD 0x20 /* X/Open subpad. */ + +/* Miscellaneous */ + +#define _NO_CHANGE -1 /* flags line edge unchanged */ + +#define _ECHAR 0x08 /* Erase char (^H) */ +#define _DWCHAR 0x17 /* Delete Word char (^W) */ +#define _DLCHAR 0x15 /* Delete Line char (^U) */ + +extern WINDOW *pdc_lastscr; +extern FILE *pdc_dbfp; /* tracing file pointer (NULL = off) */ +extern bool pdc_color_started; +extern unsigned long pdc_key_modifiers; +extern MOUSE_STATUS pdc_mouse_status; + +/*----------------------------------------------------------------------*/ + +/* Platform implementation functions */ + +void PDC_beep(void); +bool PDC_can_change_color(void); +int PDC_color_content(short, short *, short *, short *); +bool PDC_check_key(void); +int PDC_curs_set(int); +void PDC_flushinp(void); +int PDC_get_columns(void); +int PDC_get_cursor_mode(void); +int PDC_get_key(void); +int PDC_get_rows(void); +void PDC_gotoyx(int, int); +int PDC_init_color(short, short, short, short); +void PDC_init_pair(short, short, short); +int PDC_modifiers_set(void); +int PDC_mouse_set(void); +void PDC_napms(int); +int PDC_pair_content(short, short *, short *); +void PDC_reset_prog_mode(void); +void PDC_reset_shell_mode(void); +int PDC_resize_screen(int, int); +void PDC_restore_screen_mode(int); +void PDC_save_screen_mode(int); +void PDC_scr_close(void); +void PDC_scr_free(void); +int PDC_scr_open(int, char **); +void PDC_set_keyboard_binary(bool); +void PDC_transform_line(int, int, int, const chtype *); +const char *PDC_sysname(void); + +/* Internal cross-module functions */ + +void PDC_init_atrtab(void); +WINDOW *PDC_makelines(WINDOW *); +WINDOW *PDC_makenew(int, int, int, int); +int PDC_mouse_in_slk(int, int); +void PDC_slk_free(void); +void PDC_slk_initialize(void); +void PDC_sync(WINDOW *); + +#ifdef PDC_WIDE +int PDC_mbtowc(wchar_t *, const char *, size_t); +size_t PDC_mbstowcs(wchar_t *, const char *, size_t); +size_t PDC_wcstombs(char *, const wchar_t *, size_t); +#endif + +#ifdef PDCDEBUG +# define PDC_LOG(x) if (pdc_dbfp) PDC_debug x +#else +# define PDC_LOG(x) +#endif + +/* Internal macros for attributes */ + +#ifdef CHTYPE_LONG +# define PDC_COLOR_PAIRS 256 +#else +# define PDC_COLOR_PAIRS 32 +#endif + +#ifndef max +# define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef min +# define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define DIVROUND(num, divisor) (((num) + ((divisor) >> 1)) / (divisor)) + +#define PDC_CLICK_PERIOD 150 /* time to wait for a click, if + not set by mouseinterval() */ + +#endif /* __CURSES_INTERNALS__*/ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/getopt.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/getopt.h new file mode 100644 index 000000000..4de6e853e --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/getopt.h @@ -0,0 +1,93 @@ +/* $Id: getopt.h,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */ +/* $OpenBSD: getopt.h,v 1.1 2002/12/03 20:24:29 millert Exp $ */ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#if 0 +#include +#endif + +/* + * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int getopt_long(int, char * const *, const char *, + const struct option *, int *); +int getopt_long_only(int, char * const *, const char *, + const struct option *, int *); +#ifndef _GETOPT_DEFINED +#define _GETOPT_DEFINED +int getopt(int, char * const *, const char *); +int getsubopt(char **, char * const *, char **); + +extern char *optarg; /* getopt(3) external variables */ +extern int opterr; +extern int optind; +extern int optopt; +extern int optreset; +extern char *suboptarg; /* getsubopt(3) external variable */ +#endif /* _GETOPT_DEFINED */ + +#ifdef __cplusplus +} +#endif +#endif /* !_GETOPT_H_ */ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/panel.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/panel.h new file mode 100644 index 000000000..7f1fb1f17 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/panel.h @@ -0,0 +1,56 @@ +/* Public Domain Curses */ + +/*----------------------------------------------------------------------* + * Panels for PDCurses * + *----------------------------------------------------------------------*/ + +#ifndef __PDCURSES_PANEL_H__ +#define __PDCURSES_PANEL_H__ 1 + +#include + +#if defined(__cplusplus) || defined(__cplusplus__) || defined(__CPLUSPLUS) +extern "C" +{ +#endif + +typedef struct panelobs +{ + struct panelobs *above; + struct panel *pan; +} PANELOBS; + +typedef struct panel +{ + WINDOW *win; + int wstarty; + int wendy; + int wstartx; + int wendx; + struct panel *below; + struct panel *above; + const void *user; + struct panelobs *obscure; +} PANEL; + +PDCEX int bottom_panel(PANEL *pan); +PDCEX int del_panel(PANEL *pan); +PDCEX int hide_panel(PANEL *pan); +PDCEX int move_panel(PANEL *pan, int starty, int startx); +PDCEX PANEL *new_panel(WINDOW *win); +PDCEX PANEL *panel_above(const PANEL *pan); +PDCEX PANEL *panel_below(const PANEL *pan); +PDCEX int panel_hidden(const PANEL *pan); +PDCEX const void *panel_userptr(const PANEL *pan); +PDCEX WINDOW *panel_window(const PANEL *pan); +PDCEX int replace_panel(PANEL *pan, WINDOW *win); +PDCEX int set_panel_userptr(PANEL *pan, const void *uptr); +PDCEX int show_panel(PANEL *pan); +PDCEX int top_panel(PANEL *pan); +PDCEX void update_panels(void); + +#if defined(__cplusplus) || defined(__cplusplus__) || defined(__CPLUSPLUS) +} +#endif + +#endif /* __PDCURSES_PANEL_H__ */ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/term.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/term.h new file mode 100644 index 000000000..0ba0b7a7f --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/term.h @@ -0,0 +1,48 @@ +/* Public Domain Curses */ + +/* PDCurses doesn't operate with terminfo, but we need these functions for + compatibility, to allow some things (notably, interface libraries for + other languages) to be compiled. Anyone who tries to actually _use_ + them will be disappointed, since they only return ERR. */ + +#ifndef __PDCURSES_TERM_H__ +#define __PDCURSES_TERM_H__ 1 + +#include + +#if defined(__cplusplus) || defined(__cplusplus__) || defined(__CPLUSPLUS) +extern "C" +{ +#endif + +typedef struct +{ + const char *_termname; +} TERMINAL; + +/* PDCEX is defined in curses.h */ +PDCEX TERMINAL *cur_term; + +int del_curterm(TERMINAL *); +int putp(const char *); +int restartterm(const char *, int, int *); +TERMINAL *set_curterm(TERMINAL *); +int setterm(const char *); +int setupterm(const char *, int, int *); +int tgetent(char *, const char *); +int tgetflag(const char *); +int tgetnum(const char *); +char *tgetstr(const char *, char **); +char *tgoto(const char *, int, int); +int tigetflag(const char *); +int tigetnum(const char *); +char *tigetstr(const char *); +char *tparm(const char *, long, long, long, long, long, + long, long, long, long); +int tputs(const char *, int, int (*)(int)); + +#if defined(__cplusplus) || defined(__cplusplus__) || defined(__CPLUSPLUS) +} +#endif + +#endif /* __PDCURSES_TERM_H__ */ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/unistd.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/unistd.h new file mode 100644 index 000000000..5d2440309 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/unistd.h @@ -0,0 +1,56 @@ +#ifndef _UNISTD_H +#define _UNISTD_H 1 + +/* This is intended as a drop-in replacement for unistd.h on Windows. + * Please add functionality as neeeded. + * https://stackoverflow.com/a/826027/1202830 + */ + +#include +#include +#include /* getopt at: https://gist.github.com/ashelly/7776712 */ +#include /* for getpid() and the exec..() family */ +#include /* for _getcwd() and _chdir() */ + +#define srandom srand +#define random rand + +/* Values for the second argument to access. + These may be OR'd together. */ +#define R_OK 4 /* Test for read permission. */ +#define W_OK 2 /* Test for write permission. */ +//#define X_OK 1 /* execute permission - unsupported in windows*/ +#define F_OK 0 /* Test for existence. */ + +#define access _access +#define dup2 _dup2 +#define execve _execve +#define ftruncate _chsize +#define unlink _unlink +#define fileno _fileno +#define getcwd _getcwd +#define chdir _chdir +#define isatty _isatty +#define lseek _lseek +/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */ + +#ifdef _WIN64 +#define ssize_t __int64 +#else +#define ssize_t long +#endif + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +/* should be in some equivalent to */ +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#endif /* unistd.h */ \ No newline at end of file From e914f0936f26277a0e78f688f54a38eaff690e5f Mon Sep 17 00:00:00 2001 From: Decker Date: Mon, 11 Mar 2019 05:52:21 +0300 Subject: [PATCH 002/787] + msvc deps build script this script builds only deps, to build rogue binary, open *.sln file in MSVC 2015 and build x64 Release version. --- src/cc/rogue/rogue_build_msvc.cmd | 64 +++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/cc/rogue/rogue_build_msvc.cmd diff --git a/src/cc/rogue/rogue_build_msvc.cmd b/src/cc/rogue/rogue_build_msvc.cmd new file mode 100644 index 000000000..e49ab7228 --- /dev/null +++ b/src/cc/rogue/rogue_build_msvc.cmd @@ -0,0 +1,64 @@ +@echo off +echo Rogue Build Script by Decker (c) 2019 + +@REM Check for Visual Studio +call set "VSPATH=" +if defined VS140COMNTOOLS ( if not defined VSPATH ( + call set "VSPATH=%%VS140COMNTOOLS%%" +) ) + +@REM check if we already have the tools in the environment +if exist "%VCINSTALLDIR%" ( + goto compile +) + +if not defined VSPATH ( + echo You need Microsoft Visual Studio 15 installed + pause + exit +) + +@REM set up the environment +if exist "%VSPATH%..\..\vc\vcvarsall.bat" ( + call "%%VSPATH%%..\..\vc\vcvarsall.bat" amd64 + goto compile +) + +echo Unable to set up the environment +pause +exit + +:compile + +mkdir x86_64-w64-msvc\deps +mkdir x86_64-w64-msvc\deps\install + +pushd x86_64-w64-msvc\deps + +:compile_pdcurses +rem git clone https://github.com/wmcbrine/PDCurses PDCurses.org +git clone https://github.com/Bill-Gray/PDCurses + +set PREFIX_DIR=%CD%\install + +pushd PDCurses +mkdir build64 & pushd build64 +rem cmake -G"Visual Studio 14 2015 Win64" -DPDC_WIDE=ON -DCMAKE_INSTALL_PREFIX=%PREFIX_DIR% -DCMAKE_BUILD_TYPE=Debug -DPDCDEBUG=ON .. +cmake -G"Visual Studio 14 2015 Win64" -DPDC_WIDE=ON -DCMAKE_INSTALL_PREFIX=%PREFIX_DIR% -DCMAKE_BUILD_TYPE=Release .. +popd +rem cmake --build build64 --config Debug --target install +cmake --build build64 --config Release --target install +popd + +:compile_curl + +git clone https://github.com/curl/curl +pushd curl + +mkdir build64 & pushd build64 +cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_INSTALL_PREFIX=%PREFIX_DIR% -DCMAKE_USE_WINSSL:BOOL=ON .. +rem cmake --build . --config Release --target libcurl +cmake --build . --config Release --target install +popd +popd + From 9205b5404ccbef6c0da8331fcbe1590993b88b88 Mon Sep 17 00:00:00 2001 From: Decker Date: Mon, 11 Mar 2019 05:57:04 +0300 Subject: [PATCH 003/787] + msvc solution (*.sln) update --- src/cc/rogue/rogue54.sln | 14 +- src/cc/rogue/rogue54.vcxproj | 257 +++++++++++++++++++++++++++++++++++ 2 files changed, 268 insertions(+), 3 deletions(-) create mode 100644 src/cc/rogue/rogue54.vcxproj diff --git a/src/cc/rogue/rogue54.sln b/src/cc/rogue/rogue54.sln index da1c58f07..751959465 100644 --- a/src/cc/rogue/rogue54.sln +++ b/src/cc/rogue/rogue54.sln @@ -1,17 +1,25 @@ -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual C++ Express 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rogue54", "rogue54.vcproj", "{9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}" +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rogue54", "rogue54.vcxproj", "{9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Debug|Win32.ActiveCfg = Debug|Win32 {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Debug|Win32.Build.0 = Debug|Win32 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Debug|x64.ActiveCfg = Debug|x64 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Debug|x64.Build.0 = Debug|x64 {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Release|Win32.ActiveCfg = Release|Win32 {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Release|Win32.Build.0 = Release|Win32 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Release|x64.ActiveCfg = Release|x64 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/cc/rogue/rogue54.vcxproj b/src/cc/rogue/rogue54.vcxproj new file mode 100644 index 000000000..344598dd8 --- /dev/null +++ b/src/cc/rogue/rogue54.vcxproj @@ -0,0 +1,257 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6} + Win32Proj + 8.1 + + + + Application + v140 + MultiByte + + + Application + v140 + MultiByte + false + + + Application + v140 + MultiByte + + + Application + v140 + MultiByte + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.25431.1 + + + Debug\ + Debug\ + true + + + true + + + Release\ + Release\ + false + + + false + + + + Disabled + Default + ../pdcurses;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;ALLSCORES;MASTER;SCOREFILE="rogue54.scr";LOCKFILE="rogue54.lck";%(PreprocessorDefinitions) + true + false + + EnableFastChecks + MultiThreaded + true + true + false + true + + + Level4 + EditAndContinue + CompileAsC + + + Ws2_32.lib;pdcurses.lib;advapi32.lib;shfolder.lib;user32.lib;%(AdditionalDependencies) + $(OutDir)rogue54.exe + ..\pdcurses;%(AdditionalLibraryDirectories) + false + true + $(OutDir)rogue54.pdb + Console + MachineX86 + + + + + Disabled + Default + $(ProjectDir)x86_64-w64-msvc\deps\install\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;PDC_DLL_BUILD;PDC_WIDE;PDCDEBUG;_CRT_SECURE_NO_DEPRECATE;ALLSCORES;SCOREFILE="rogue54.scr";LOCKFILE="rogue54.lck";%(PreprocessorDefinitions) + true + false + + + EnableFastChecks + MultiThreaded + true + true + false + true + + + + + Level4 + ProgramDatabase + CompileAsC + + + Ws2_32.lib;wincon\pdcurses.lib;libcurl_imp.lib;advapi32.lib;shfolder.lib;user32.lib;%(AdditionalDependencies) + $(OutDir)rogue54.exe + $(ProjectDir)x86_64-w64-msvc\deps\install\lib;$(ProjectDir)x86_64-w64-msvc\deps\install\Release\lib;%(AdditionalLibraryDirectories) + false + true + $(OutDir)rogue54.pdb + Console + + + + + MaxSpeed + OnlyExplicitInline + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + true + NotUsing + Level3 + ProgramDatabase + $(ProjectDir)\x86_64-w64-msvc\include\ncursesw;$(ProjectDir)\x86_64-w64-msvc\include;%(AdditionalIncludeDirectories) + + + $(OutDir)rogue54.exe + true + Windows + true + true + MachineX86 + %(AdditionalLibraryDirectories) + %(AdditionalDependencies) + + + + + MaxSpeed + OnlyExplicitInline + true + WIN32;_WINDOWS;NDEBUG;PDC_DLL_BUILD;PDC_WIDE;PDCDEBUG;_CRT_SECURE_NO_DEPRECATE;ALLSCORES;SCOREFILE="rogue54.scr";LOCKFILE="rogue54.lck";%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + ProgramDatabase + $(ProjectDir)x86_64-w64-msvc\deps\install\include;%(AdditionalIncludeDirectories) + stdafx.h + CompileAsC + + + + + $(OutDir)rogue54.exe + true + Console + true + true + $(ProjectDir)x86_64-w64-msvc\deps\install\lib;$(ProjectDir)x86_64-w64-msvc\deps\install\Release\lib;%(AdditionalLibraryDirectories) + Ws2_32.lib;wincon\pdcurses.lib;libcurl_imp.lib;advapi32.lib;shfolder.lib;user32.lib;%(AdditionalDependencies) + %(IgnoreSpecificDefaultLibraries) + false + %(ForceSymbolReferences) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 56905677d5c20e83bd8d10ed5491bec271ea2d7a Mon Sep 17 00:00:00 2001 From: Decker Date: Mon, 11 Mar 2019 05:58:10 +0300 Subject: [PATCH 004/787] + msvc build fix --- .gitignore | 1 + src/cc/rogue/extern.h | 9 ++++++++- src/cc/rogue/main.c | 45 ++++++++++++++++++++++++++++++++++++++++++- src/cc/rogue/rogue.c | 27 ++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 2ab07ee0f..9f05e147c 100644 --- a/.gitignore +++ b/.gitignore @@ -154,3 +154,4 @@ src/ROGUE.conf src/rogue.scr src/cc/rogue/confdefs.h +src/cc/rogue/x64 diff --git a/src/cc/rogue/extern.h b/src/cc/rogue/extern.h index 7fba842f3..c62646b67 100644 --- a/src/cc/rogue/extern.h +++ b/src/cc/rogue/extern.h @@ -107,6 +107,12 @@ #include #include +#ifdef _WIN32 +#ifdef _MSC_VER +#include +#endif +#endif + #undef SIGTSTP #define MAXSTR 1024 /* maximum length of strings */ @@ -142,7 +148,8 @@ void leave(int); void my_exit(int st); void playltchars(void); void quit(int); -int32_t _quit(); +int32_t _quit(); + void resetltchars(void); void set_order(int *order, int numthings); void tstp(int ignored); diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c index c2155a085..da46898f8 100644 --- a/src/cc/rogue/main.c +++ b/src/cc/rogue/main.c @@ -38,6 +38,31 @@ union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uin typedef union _bits256 bits256; #endif +#ifdef _WIN32 +#ifdef _MSC_VER +int gettimeofday(struct timeval * tp, struct timezone * tzp) +{ + // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; +} +#endif // _MSC_VER +#endif + + + double OS_milliseconds() { struct timeval tv; double millis; @@ -392,6 +417,12 @@ char *post_process_bitcoind_RPC(char *debugstr,char *command,char *rpcstr,char * } #endif +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + /************************************************************************ * * perform the query @@ -919,7 +950,19 @@ int main(int argc, char **argv, char **envp) printf("ASSETCHAINS_SYMBOL.(%s) port.%u (%s) IPADDRESS.%s \n",ASSETCHAINS_SYMBOL,ROGUE_PORT,USERPASS,IPADDRESS); sleep(1); if ( argc == 2 && (fp=fopen(argv[1],"rb")) == 0 ) { - seed = atol(argv[1]); + + #ifdef _WIN32 + #ifdef _MSC_VER + seed = _strtoui64(argv[1], NULL, 10); + fprintf(stderr, "replay seed.str(%s) seed.uint64_t(%I64u)", argv[1], seed); + #else + fprintf(stderr, "replay seed.str(%s) seed.uint64_t(%llu)", argv[1], (long long)seed); + seed = atol(argv[1]); // windows, but not MSVC + #endif // _MSC_VER + #else + seed = atol(argv[1]); // non-windows + #endif // _WIN32 + //fprintf(stderr,"replay %llu\n",(long long)seed); return(rogue_replay(seed,10)); } diff --git a/src/cc/rogue/rogue.c b/src/cc/rogue/rogue.c index 352e950ec..36616b340 100644 --- a/src/cc/rogue/rogue.c +++ b/src/cc/rogue/rogue.c @@ -13,6 +13,7 @@ #include //#include //#include + #include "rogue.h" #ifdef STANDALONE #include "../komodo/src/komodo_cJSON.h" @@ -196,6 +197,12 @@ void rogue_bailout(struct rogue_state *rs) fprintf(stderr,"error issuing (%s)\n",cmd);*/ } +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + int32_t rogue_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis) { struct rogue_state *rs; FILE *fp; int32_t i,n; @@ -215,6 +222,14 @@ int32_t rogue_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t nu globalR = *rs; uint32_t starttime = (uint32_t)time(NULL); rogueiterate(rs); + + /* + // keypress after replay + printf("[Press return to continue]"); + fflush(stdout); + if (fgets(prbuf, 10, stdin) != 0); + */ + if ( 0 ) { fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL) - starttime); @@ -526,6 +541,18 @@ tstp(int ignored) #endif*/ } + +#ifdef _WIN32 +#ifdef _MSC_VER +void usleep(int32_t micros) +{ + if (micros < 1000) + Sleep(1); + else Sleep(micros / 1000); +} +#endif +#endif + /* * playit: * The main loop of the program. Loop until the game is over, From 42e975b4dfa7da992fd3dfece41955fe7dfa97de Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Mon, 11 Mar 2019 06:32:02 +0300 Subject: [PATCH 005/787] fix libcurl deps install (msvc) --- src/cc/rogue/rogue_build_msvc.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/rogue/rogue_build_msvc.cmd b/src/cc/rogue/rogue_build_msvc.cmd index e49ab7228..77e7cd852 100644 --- a/src/cc/rogue/rogue_build_msvc.cmd +++ b/src/cc/rogue/rogue_build_msvc.cmd @@ -57,7 +57,7 @@ pushd curl mkdir build64 & pushd build64 cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_INSTALL_PREFIX=%PREFIX_DIR% -DCMAKE_USE_WINSSL:BOOL=ON .. -rem cmake --build . --config Release --target libcurl +cmake --build . --config Release --target libcurl cmake --build . --config Release --target install popd popd From 445d68395e6ae0a54af7bf33c47361c9ca5ba027 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Tue, 12 Mar 2019 19:25:13 +0300 Subject: [PATCH 006/787] [msvc] fix seed str -> uint64 conversion --- src/cc/rogue/rogue.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/cc/rogue/rogue.c b/src/cc/rogue/rogue.c index 36616b340..7a8e38c15 100644 --- a/src/cc/rogue/rogue.c +++ b/src/cc/rogue/rogue.c @@ -343,7 +343,15 @@ int rogue(int argc, char **argv, char **envp) rs->sleeptime = 1; // non-zero to allow refresh() if ( argc == 3 && strlen(argv[2]) == 64 ) { - rs->seed = atol(argv[1]); + #ifdef _WIN32 + #ifdef _MSC_VER + rs->seed = _strtoui64(argv[1], NULL, 10); + #else + rs->seed = atol(argv[1]); // windows, but not MSVC + #endif // _MSC_VER + #else + rs->seed = atol(argv[1]); // non-windows + #endif // _WIN32 strcpy(Gametxidstr,argv[2]); fprintf(stderr,"setplayerdata\n"); if ( rogue_setplayerdata(rs,Gametxidstr) < 0 ) From 6c907d78c129c003a4bf3222520d2834a072624c Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Tue, 12 Mar 2019 19:26:03 +0300 Subject: [PATCH 007/787] [msvc] fix config file name issue + debug print for send raw tx --- src/cc/rogue/main.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c index da46898f8..80a5349f5 100644 --- a/src/cc/rogue/main.c +++ b/src/cc/rogue/main.c @@ -767,6 +767,14 @@ int32_t rogue_sendrawtransaction(char *rawtx) } free_json(retjson); } + + /* log sendrawtx result in file */ + FILE *debug_file; + debug_file = fopen("tx_debug.log", "a"); + fprintf(debug_file, "%s\n", retstr); + fflush(debug_file); + fclose(debug_file); + free(retstr); } free(params); @@ -944,6 +952,15 @@ int main(int argc, char **argv, char **envp) ASSETCHAINS_SYMBOL[j++] = toupper(c); } ASSETCHAINS_SYMBOL[j++] = 0; + + #ifdef _WIN32 + #ifdef _MSC_VER + if (strncmp(ASSETCHAINS_SYMBOL, "ROGUE.EXE", sizeof(ASSETCHAINS_SYMBOL)) == 0 || strncmp(ASSETCHAINS_SYMBOL, "ROGUE54.EXE", sizeof(ASSETCHAINS_SYMBOL)) == 0) { + strcpy(ASSETCHAINS_SYMBOL, "ROGUE"); // accept ROGUE.conf, instead of ROGUE.EXE.conf or ROGUE54.EXE.conf if build with MSVC + } + #endif + #endif + ROGUE_PORT = komodo_userpass(userpass,ASSETCHAINS_SYMBOL); if ( IPADDRESS[0] == 0 ) strcpy(IPADDRESS,"127.0.0.1"); From 8ec2fe1a5ef8b5b66e2351cf2cbff0e69fe5947b Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Tue, 12 Mar 2019 21:00:52 +0300 Subject: [PATCH 008/787] + comment debug printouts --- src/cc/rogue/main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c index 80a5349f5..dd73e255c 100644 --- a/src/cc/rogue/main.c +++ b/src/cc/rogue/main.c @@ -769,12 +769,15 @@ int32_t rogue_sendrawtransaction(char *rawtx) } /* log sendrawtx result in file */ + + /* FILE *debug_file; debug_file = fopen("tx_debug.log", "a"); fprintf(debug_file, "%s\n", retstr); fflush(debug_file); fclose(debug_file); - + */ + free(retstr); } free(params); From 8a0576f92320ad79741e5a741101539903a2bc75 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Wed, 13 Mar 2019 01:05:11 +0300 Subject: [PATCH 009/787] [ msvc ] display compiler version and build date on startup --- src/cc/rogue/main.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c index dd73e255c..f146cfb4d 100644 --- a/src/cc/rogue/main.c +++ b/src/cc/rogue/main.c @@ -941,9 +941,46 @@ int32_t rogue_setplayerdata(struct rogue_state *rs,char *gametxidstr) return(retval); } +#ifdef _WIN32 +#ifdef _MSC_VER +__inline int msver(void) { + switch (_MSC_VER) { + case 1500: return 2008; + case 1600: return 2010; + case 1700: return 2012; + case 1800: return 2013; + case 1900: return 2015; + //case 1910: return 2017; + default: return (_MSC_VER / 100); + } +} + +static inline bool is_x64(void) { +#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) + return 1; +#elif defined(__amd64__) || defined(__amd64) || defined(_M_X64) || defined(_M_IA64) + return 1; +#else + return 0; +#endif +} + +#define BUILD_DATE __DATE__ " " __TIME__ +#endif // _WIN32 +#endif // _MSC_VER + int main(int argc, char **argv, char **envp) { uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; + + #ifdef _WIN32 + #ifdef _MSC_VER + printf("*** rogue for Windows [ Build %s ] ***\n", BUILD_DATE); + const char* arch = is_x64() ? "64-bits" : "32-bits"; + printf(" Built with VC++ %d (%ld) %s\n\n", msver(), _MSC_FULL_VER, arch); + #endif + #endif + for (i=j=0; argv[0][i]!=0&&j Date: Thu, 21 Mar 2019 03:14:59 -1100 Subject: [PATCH 010/787] Initial Payments CC without validation --- src/cc/CCPayments.h | 9 +- src/cc/CCtokens.cpp | 3 +- src/cc/CCtx.cpp | 20 +- src/cc/cclib.cpp | 2 + src/cc/dice.cpp | 4 +- src/cc/dilithium.c | 6 +- src/cc/faucet.cpp | 6 +- src/cc/gateways.cpp | 6 +- src/cc/marmara.cpp | 6 +- src/cc/payments.cpp | 599 +++++++++++++++++++++++++++++++-------- src/cc/rewards.cpp | 6 +- src/rpc/server.cpp | 6 + src/rpc/server.h | 7 + src/wallet/rpcwallet.cpp | 78 +++++ 14 files changed, 620 insertions(+), 138 deletions(-) diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 187d9c9ad..247a0f2ec 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -19,9 +19,16 @@ #include "CCinclude.h" +#define PAYMENTS_TXFEE 10000 + bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue PaymentsInfo(); +UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr); #endif diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index 8aa5e88f7..8eff2c749 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -586,8 +586,7 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, C LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->additionalTokensEvalcode2 << std::endl); } - threshold = total / (maxinputs != 0 ? maxinputs : 64); // TODO: maxinputs really could not be over 64? what if i want to calc total balance for all available uxtos? - // maybe it is better to add all uxtos if maxinputs == 0 + threshold = total / (maxinputs != 0 ? maxinputs : MAX_CCVINS); for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 3e93b3462..13e097a8d 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -509,16 +509,18 @@ int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t * int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int32_t maxinputs) { - int32_t abovei,belowi,ind,vout,i,n = 0,maxutxos=CC_MAXVINS; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; uint256 txid,hashBlock; std::vector vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up; + int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; uint256 txid,hashBlock; std::vector vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up; #ifdef ENABLE_WALLET assert(pwalletMain != NULL); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos)); - threshold = total/(maxinputs+1); - if ( maxinputs > maxutxos ) - maxutxos = maxinputs; + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; sum = 0; BOOST_FOREACH(const COutput& out, vecOutputs) { @@ -602,12 +604,14 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinputs) { - int32_t abovei,belowi,ind,vout,i,n = 0,maxutxos=CC_MAXVINS; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; char coinaddr[64]; uint256 txid,hashBlock; CTransaction tx; struct CC_utxo *utxos,*up; + int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; char coinaddr[64]; uint256 txid,hashBlock; CTransaction tx; struct CC_utxo *utxos,*up; std::vector > unspentOutputs; utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos)); - threshold = total/(maxinputs+1); - if ( maxinputs > maxutxos ) - maxutxos = maxinputs; + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; sum = 0; Getscriptaddress(coinaddr,CScript() << Mypubkey() << OP_CHECKSIG); SetCCunspents(unspentOutputs,coinaddr); diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 25cd691c5..1330c6b3e 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -492,6 +492,8 @@ int64_t AddCClibInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; if ( maxinputs != 0 ) threshold = total/maxinputs; else threshold = total; diff --git a/src/cc/dice.cpp b/src/cc/dice.cpp index 4f2b421aa..ca1ad7b53 100644 --- a/src/cc/dice.cpp +++ b/src/cc/dice.cpp @@ -1052,9 +1052,11 @@ uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; if ( maxinputs > 0 ) threshold = total / maxinputs; - else threshold = total / 64; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/dilithium.c b/src/cc/dilithium.c index 0854236ec..7ba522f0f 100644 --- a/src/cc/dilithium.c +++ b/src/cc/dilithium.c @@ -3340,7 +3340,11 @@ int64_t dilithium_inputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPu std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/faucet.cpp b/src/cc/faucet.cpp index b794d9b1c..81a2c2933 100644 --- a/src/cc/faucet.cpp +++ b/src/cc/faucet.cpp @@ -146,7 +146,11 @@ int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPub std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/gateways.cpp b/src/cc/gateways.cpp index f0c8735e2..18fc43004 100644 --- a/src/cc/gateways.cpp +++ b/src/cc/gateways.cpp @@ -898,7 +898,11 @@ int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP { GetTokensCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "check " << coinaddr << " for gateway inputs" << std::endl); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { diff --git a/src/cc/marmara.cpp b/src/cc/marmara.cpp index 3c35632c8..60afbc7fd 100644 --- a/src/cc/marmara.cpp +++ b/src/cc/marmara.cpp @@ -388,7 +388,11 @@ int64_t AddMarmarainputs(CMutableTransaction &mtx,std::vector &pubkeys, uint64_t threshold,nValue,totalinputs = 0; uint256 txid,hashBlock; CTransaction tx; int32_t numvouts,ht,unlockht,vout,i,n = 0; uint8_t funcid; CPubKey pk; std::vector vals; std::vector > unspentOutputs; SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index c4f476cfb..2a99c9d31 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -15,14 +15,81 @@ #include "CCPayments.h" -/* - Payments CC is a catchall CC, supported invoices, zpayments, automated funds allocation, including token based revshare +/* + 0) create <- update_allowed flag, locked_blocks, minrelease, list of scriptPubKeys, allocations + 1) lock amount to global CC address + 2) release amount -> vout[i] will be scriptPubKeys[i] and (amount * allocations[i]) / sumallocations[] (only using vins that have been locked for locked_blocks+). will make a tx with less than amount if it can find enough vins for minrelease amount + + 3) update (vins from all scriptPubkeys) new update_allowed flag, locked_blocks, minrelease, list of scriptPubKeys, allocations (only if update_allowed) + + 4) info txid -> display parameters, funds + 5) list -> all txids */ // start of consensus code +CScript EncodePaymentsTxidOpRet(int32_t allocation,std::vector scriptPubKey,std::vector opret) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'T' << allocation << scriptPubKey << opret); + return(opret); +} + +uint8_t DecodePaymentsTxidOpRet(CScript scriptPubKey,int32_t &allocation,std::vector &scriptPubKey,std::vector &opret) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> allocation; ss >> scriptPubKey; ss >> opret) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'T' ) + return(f); + } + return(0); +} + +CScript EncodePaymentsFundOpRet(uint256 checktxid) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << checktxid); + return(opret); +} + +uint8_t DecodePaymentsFundOpRet(CScript scriptPubKey,uint256 &checktxid) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> checktxid) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'F' ) + return(f); + } + return(0); +} + +CScript EncodePaymentsOpRet(int32_t updateflag,int32_t lockedblocks,int32_t minrelease,int32_t totalallocations,std::vector txidoprets) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'C' << updateflag << lockedblocks << minrelease << totalallocations << txidoprets); + return(opret); +} + +uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &updateflag,int32_t &lockedblocks,int32_t &minrelease,int32_t &totalallocations,std::vector &txidoprets) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> updateflag; ss >> lockedblocks; ss >> minrelease; ss >> totalallocations; ss >> txidoprets) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'C' ) + return(f); + } + return(0); +} + int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) { char destaddr[64]; @@ -74,144 +141,434 @@ bool PaymentsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - return eval->Invalid("no validation yet"); - std::vector > txids; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else - { - for (i=0; iInvalid("illegal normal vini"); - } - } - //fprintf(stderr,"check amounts\n"); - if ( PaymentsExactAmounts(cp,eval,tx,1,10000) == false ) - { - fprintf(stderr,"Paymentsget invalid amount\n"); - return false; - } - else - { - txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"Paymentsget validated\n"); - else fprintf(stderr,"Paymentsget invalid\n"); - return(retval); - } - } + return(true); } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp -int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid) { - // add threshold check - char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + char coinaddr[64]; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx,tx; int32_t iter,vout,n = 0; std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; + for (iter=0; iter<2; iter++) { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - // no need to prevent dup - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + if ( iter == 0 ) + GetCCaddress(cp,coinaddr,Paymentspk); + else GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); + SetCCunspents(unspentOutputs,coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - if ( (nValue= IsPaymentsvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - nValue = it->second.satoshis; - totalinputs += nValue; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; + if ( iter == 0 ) + { + std::vector scriptPubKey,opret; + if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() < 2 || DecodePaymentsFundOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,checktxid) != 'F' || checktxid != createtxid ) + continue; + } + if ( (nValue= IsPaymentsvout(cp,vintx,vout)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + { + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } } } } return(totalinputs); } -std::string PaymentsGet(uint64_t txfee,int64_t nValue) +UniValue payments_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) { - CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,Paymentspk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; - cp = CCinit(&C,EVAL_PAYMENTS); - if ( txfee == 0 ) - txfee = 10000; - Paymentspk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - if ( (inputs= AddPaymentsInputs(cp,mtx,Paymentspk,nValue+txfee,60)) > 0 ) + CTransaction tx; + if ( rawtx.size() > 0 ) { - if ( inputs > nValue ) - CCchange = (inputs - nValue - txfee); - if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,CCchange,Paymentspk)); - mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); - j = rand() & 0xfffffff; - for (i=0; i<1000000; i++,j++) + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) { - tmpmtx = mtx; - rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_PAYMENTS << (uint8_t)'G' << j)); - if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) - { - len >>= 1; - decode_hex(buf,len,(char *)rawhex.c_str()); - hash = bits256_doublesha256(0,buf,len); - if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 ) - { - fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL)); - return(rawhex); - } - //fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]); - } - } - fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); - return(""); - } else fprintf(stderr,"cant find Payments inputs\n"); - return(""); -} - -std::string PaymentsFund(uint64_t txfee,int64_t funds) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,Paymentspk; CScript opret; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_PAYMENTS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - Paymentspk = GetUnspendable(cp,0); - if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) - { - mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,funds,Paymentspk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); - } - return(""); -} - -UniValue PaymentsInfo() -{ - UniValue result(UniValue::VOBJ); char numstr[64]; - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey Paymentspk; struct CCcontract_info *cp,C; int64_t funding; - result.push_back(Pair("result","success")); - result.push_back(Pair("name","Payments")); - cp = CCinit(&C,EVAL_PAYMENTS); - Paymentspk = GetUnspendable(cp,0); - funding = AddPaymentsInputs(cp,mtx,Paymentspk,0,0); - sprintf(numstr,"%.8f",(double)funding/COIN); - result.push_back(Pair("funding",numstr)); + if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize payments CCtx")); return(result); } +cJSON *payments_reparse(int32_t *nump,char *jsonstr) +{ + cJSON *params; char *newstr; int32_t i,j; + *nump = 0; + if ( jsonstr != 0 ) + { + if ( jsonstr[0] == '"' && jsonstr[strlen(jsonstr)-1] == '"' ) + { + jsonstr[strlen(jsonstr)-1] = 0; + jsonstr++; + } + newstr = (char *)malloc(strlen(jsonstr)+1); + for (i=j=0; jsonstr[i]!=0; i++) + { + if ( jsonstr[i] == '%' && jsonstr[i+1] == '2' && jsonstr[i+2] == '2' ) + { + newstr[j++] = '"'; + i += 2; + } + else if ( jsonstr[i] == '\'' ) + newstr[j++] = '"'; + else newstr[j++] = jsonstr[i]; + } + newstr[j] = 0; + params = cJSON_Parse(newstr); + if ( 0 && params != 0 ) + printf("new.(%s) -> %s\n",newstr,jprint(params,0)); + free(newstr); + *nump = cJSON_GetArraySize(params); + } else params = 0; + return(params); +} + +uint256 payments_juint256(cJSON *obj) +{ + uint256 tmp; bits256 t = jbits256(obj,0); + memcpy(&tmp,&t,sizeof(tmp)); + return(revuint256(tmp)); +} + +int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t len) +{ + char *hexstr; int32_t val; + if ( (hexstr= jstr(item,0)) != 0 && ((val= is_hexstr(hexstr,0)) == len*2 || (val > 0 && len == 0)) ) + { + val >>= 1; + hexdata.resize(val); + decode_hex(hexdata,val,hexstr); + return(0); + } else return(-1); +} + +UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64]; + cJSON *params = payments_reparse(&n,jsonstr); + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( params != 0 && n == 2 ) + { + createtxid = payments_juint256(jitem(params,0)); + amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; + if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) + { + if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,updateflag,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + { + for (i=0; i scriptPubKey,opret; + vout.nValue = 0; + if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + vout.nValue = allocation; + vout.scriptPubKey.resize(scriptPubKey.size()); + memcpy(&vout.scriptPubKey[0],&scriptPubKey[0],scriptPubKey.size()); + checkallocations += allocation; + if ( opret.size() > 0 ) + { + scriptPubKey.resize(opret.size); + memcpy(&onlyopret[0],&opret[0],opret.size); + numoprets++; + } + } else break; + mtx.vout.push_back(vout); + } + if ( i != txidoprets.size() ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid txidoprets[i]")); + result.push_back(Pair("txi",(int64_t)i)); + return(result); + } + else if ( checkallocations != totalallocations ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","totalallocations mismatch")); + result.push_back(Pair("checkallocations",(int64_t)checkallocations)); + result.push_back(Pair("totalallocations",(int64_t)totalallocations)); + return(result); + } + else if ( numoprets > 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many oprets")); + result.push_back(Pair("numoprets",(int64_t)numoprets)); + return(result); + } + for (i=0; i= amount ) + { + if ( (CCchange= (inputsum - amount)) > TRANSACTIONS_TXFEE ) + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,CCchange,Paymentspk,txidpk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); + return(payments_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough locked funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt decode paymentscreate txid opret")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find paymentscreate txid")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + return(result); +} + +UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); + CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; + cJSON *params = payments_reparse(&n,jsonstr); + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( params != 0 && n > 1 && n <= 3 ) + { + txid = payments_juint256(jitem(params,0)); + amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; + if ( n == 3 ) + useopret = jint(jitem(params,2),0) != 0; + if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() == 1 || DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,updateflag,lockedblocks,minrelease,totalallocations,txidoprets) == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid createtxid")); + } + else if ( AddNormalinputs(mtx,mypk,amount+PAYMENTS_TXFEE,60) > 0 ) + { + if ( useopret == 0 ) + { + txidpk = CCtxidaddr(txidaddr,txid); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,funds,Paymentspk,txidpk)); + } + else + { + mtx.vout.push_back(MakeCCvout(EVAL_PAYMENTS,funds,Paymentspk)); + opret = EncodePaymentsFundOpRet(txid); + } + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,opret); + return(payments_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + return(result); +} + +UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); CPubKey mypk; std::string rawtx; + std::vector scriptPubKey,opret; int32_t allocation,n,retval0,retval1=0; + cJSON *params = payments_reparse(&n,jsonstr); + mypk = pubkey2pk(Mypubkey()); + if ( params != 0 && n > 1 && n <= 3 ) + { + allocation = juint(jitem(params,0),0); + retval0 = payments_parsehexdata(scriptPubKey,jitem(params,1)); + if ( m == 3 ) + retval1 = payments_parsehexdata(opret,jitem(params,2)); + if ( allocation > 0 && retval == 0 && retval1 == 0 && AddNormalinputs(mtx,mypk,PAYMENTS_TXFEE,10) > 0 ) + { + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsTxidOpRet(allocation,scriptPubKey,opret)); + return(payments_rawtxresult(result,rawtx,0)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid params or cant find txfee")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + return(result); +} + +UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations=0; + cJSON *params = payments_reparse(&n,jsonstr); + if ( params != 0 && n >= 4 ) + { + updateflag = juint(jitem(params,0),0); + lockedblocks = juint(jitem(params,1),0); + minrelease = juint(jitem(params,2),0); + for (i=0; i scriptPubKey,opret; int32_t allocation; + if ( myGetTransaction(txidoprets[i],tx,hashBlock) != 0 && tx.vout.size() > 1 && DecodePaymentsTxidOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + totalallocations += allocation; + if ( opret.size() > 0 ) + numoprets++; + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid txidopret")); + result.push_back(Pair("txid",txidoprets[i].GetHex())); + result.push_back(Pair("txi",(int64_t)i)); + return(result); + } + } + if ( numoprets > 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many opreturns")); + result.push_back(Pair("numoprets",(int64_t)numoprets)); + return(result); + } + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(updateflag,lockedblocks,minrelease,totalallocations,txidoprets)); + return(payments_rawtxresult(result,rawtx,0)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough normal funds")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + return(result); +} + +UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) +{ + UniValue result(UniValue::VOBJ),a(UniValue:VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,n,flag=0,allocation,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; int64_t funds,fundsopret; char fundsaddr[64],fundsopretaddr[64],txidaddr[64]; + cJSON *params = payments_reparse(&n,jsonstr); + if ( params != 0 && n == 1 ) + { + Paymentspk = GetUnspendable(cp,0); + txid = payments_juint256(jitem(params,0)); + if ( myGetTransaction(txid,tx,hashBlock) != 0 ) + { + if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,updateflag,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + { + result.push_back(Pair("updateable",updateflag!=0?"yes":"no")); + result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); + result.push_back(Pair("totalallocations",(int64_t)totalallocations)); + result.push_back(Pair("minrelease",(int64_t)minrelease)); + for (i=0; i scriptPubKey,opret; + obj.push_back(Pair("txidopret",txidoprets[i])); + if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + obj.push_back(Pair("scriptPubKey",scriptPubKey)); + if ( opret.size() != 0 ) + { + obj.push_back(Pair("opreturn",opret)); + numoprets++; + } + } + } + flag++; + if ( numoprets > 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many opreturns")); + result.push_back(Pair("numoprets",(int64_t)numoprets)); + } + else + { + txidpk = CCtxidaddr(txidaddr,txid); + GetCCaddress1of2(cp,fundsaddr,Paymentspk,txidpk); + funds = CCaddress_balance(fundsaddr); + result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); + GetCCaddress(cp,fundsopretaddr,Paymentspk); + fundsopret = CCaddress_balance(fundsopretaddr); + result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); + result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); + result.push_back(Pair("result","success")); + } + } + } + if ( flag == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find valid payments create txid")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + return(result); +} + +UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) +{ + std::vector > addressIndex; uint256 txid,hashBlock; + UniValue result(UniValue::VOBJ),a(UniValue:VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; + result.push_back(Pair("result","success")); + Paymentspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); + SetCCtxids(addressIndex,markeraddr); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + txid = it->first.txhash; + if ( it->first.index == 0 && myGetTransaction(txid,tx,hashBlock) != 0 ) + { + if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,updateflag,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' ) + { + a.push_back(uint256_str(str,txid)); + } + } + } + result.push_back(Pair("createtxids",a)); + return(result); +} diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index c201869b0..1351bbaeb 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -333,7 +333,11 @@ int64_t AddRewardsInputs(CScript &scriptPubKey,uint64_t maxseconds,struct CCcont std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 46eb95a25..a44304ff0 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -466,6 +466,12 @@ static const CRPCCommand vRPCCommands[] = // Payments { "payments", "paymentsaddress", &paymentsaddress, true }, + { "payments", "paymentstxidopret", &payments_txidopret, true }, + { "payments", "paymentscreate", &payments_create, true }, + { "payments", "paymentslist", &payments_list, true }, + { "payments", "paymentsinfo", &payments_info, true }, + { "payments", "paymentsfund", &payments_fund, true }, + { "payments", "paymentsrelease", &payments_release, true }, { "CClib", "cclibaddress", &cclibaddress, true }, { "CClib", "cclibinfo", &cclibinfo, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index abd0ea6a8..4eee49da3 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -288,6 +288,13 @@ extern UniValue marmara_creditloop(const UniValue& params, bool fHelp); extern UniValue marmara_settlement(const UniValue& params, bool fHelp); extern UniValue marmara_lock(const UniValue& params, bool fHelp); extern UniValue paymentsaddress(const UniValue& params, bool fHelp); +extern UniValue payments_release(const UniValue& params, bool fHelp); +extern UniValue payments_fund(const UniValue& params, bool fHelp); +extern UniValue payments_txidopret(const UniValue& params, bool fHelp); +extern UniValue payments_create(const UniValue& params, bool fHelp); +extern UniValue payments_info(const UniValue& params, bool fHelp); +extern UniValue payments_list(const UniValue& params, bool fHelp); + extern UniValue cclibaddress(const UniValue& params, bool fHelp); extern UniValue cclibinfo(const UniValue& params, bool fHelp); extern UniValue cclib(const UniValue& params, bool fHelp); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 445f145fd..c64e22103 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5577,6 +5577,84 @@ UniValue cclib(const UniValue& params, bool fHelp) return(CClib(cp,method,jsonstr)); } +UniValue payments_release(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; char *jsonstr=0; + if ( fHelp || params.size() != 2 ) + throw runtime_error("paymentsrelease createtxid amount\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsRelease(cp,jsonstr)); +} + +UniValue payments_fund(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; char *jsonstr=0; + if ( fHelp || params.size() < 2 || params.size() > 3 ) + throw runtime_error("paymentsfund createtxid amount [useopret]\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsFund(cp,jsonstr)); +} + +UniValue payments_txidopret(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; char *jsonstr=0; + if ( fHelp || params.size() < 2 ) + throw runtime_error("paymentstxidopret scriptPubKey opretformat\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsTxidopret(cp,jsonstr)); +} + +UniValue payments_create(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; char *jsonstr=0; + if ( fHelp || params.size() < 5 ) + throw runtime_error("paymentscreate updateallowed lockedblocks minamount paytxid0,...,paytxidN\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsCreate(cp,jsonstr)); +} + +UniValue payments_info(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; char *jsonstr=0; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsinfo createtxid\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsInfo(cp,jsonstr)); +} + +UniValue payments_list(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; char *jsonstr=0; + if ( fHelp || params.size() != 0 ) + throw runtime_error("paymentslist\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsList(cp,jsonstr)); +} + UniValue oraclesaddress(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; std::vector pubkey; From 3fec69791ac2af8a7ce622f453479e76e3cca063 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 03:17:15 -1100 Subject: [PATCH 011/787] utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos)); --- src/cc/CCtx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 13e097a8d..a404b78e4 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -515,7 +515,7 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); - utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos)); + utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos)); if ( maxinputs > CC_MAXVINS ) maxinputs = CC_MAXVINS; if ( maxinputs > 0 ) @@ -606,7 +606,7 @@ int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinput { int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; char coinaddr[64]; uint256 txid,hashBlock; CTransaction tx; struct CC_utxo *utxos,*up; std::vector > unspentOutputs; - utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos)); + utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos)); if ( maxinputs > CC_MAXVINS ) maxinputs = CC_MAXVINS; if ( maxinputs > 0 ) From 20c2d22056b4ee4a11484bf4e77308dbed40cbca Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 03:18:48 -1100 Subject: [PATCH 012/787] Maxinputs --- src/cc/CCtx.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index a404b78e4..cb3f8b1a6 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -555,7 +555,7 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 up->vout = vout; sum += up->nValue; //fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos); - if ( n >= maxutxos || sum >= total ) + if ( n >= maxinputs || sum >= total ) break; } } @@ -648,7 +648,7 @@ int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinput up->vout = vout; sum += up->nValue; //fprintf(stderr,"add %.8f to vins array.%d of %d\n",(double)up->nValue/COIN,n,maxutxos); - if ( n >= maxutxos || sum >= total ) + if ( n >= maxinputs || sum >= total ) break; } } From 37100ff2c59624874b4581051ed79499282a73c0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 03:21:52 -1100 Subject: [PATCH 013/787] CC_MAXVINS --- src/cc/CCtokens.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index 8eff2c749..a81a69b1f 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -586,7 +586,7 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, C LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->additionalTokensEvalcode2 << std::endl); } - threshold = total / (maxinputs != 0 ? maxinputs : MAX_CCVINS); + threshold = total / (maxinputs != 0 ? maxinputs : CC_MAXVINS); for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { From 5ea0cc3c4dbfe88baae82ba40d02c63e768c91a8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 03:26:26 -1100 Subject: [PATCH 014/787] Syntax --- src/cc/payments.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 2a99c9d31..378db54ae 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -30,19 +30,19 @@ // start of consensus code -CScript EncodePaymentsTxidOpRet(int32_t allocation,std::vector scriptPubKey,std::vector opret) +CScript EncodePaymentsTxidOpRet(int32_t allocation,std::vector scriptPubKey,std::vector destopret) { CScript opret; uint8_t evalcode = EVAL_PAYMENTS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'T' << allocation << scriptPubKey << opret); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'T' << allocation << scriptPubKey << destopret); return(opret); } -uint8_t DecodePaymentsTxidOpRet(CScript scriptPubKey,int32_t &allocation,std::vector &scriptPubKey,std::vector &opret) +uint8_t DecodePaymentsTxidOpRet(CScript scriptPubKey,int32_t &allocation,std::vector &destscriptPubKey,std::vector &destopret) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> allocation; ss >> scriptPubKey; ss >> opret) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> allocation; ss >> destscriptPubKey; ss >> destopret) != 0 ) { if ( e == EVAL_PAYMENTS && f == 'T' ) return(f); @@ -149,13 +149,14 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid) { - char coinaddr[64]; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx,tx; int32_t iter,vout,n = 0; + char coinaddr[64]; CPubKey Paymentspk; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx,tx; int32_t iter,vout,n = 0; std::vector > unspentOutputs; if ( maxinputs > CC_MAXVINS ) maxinputs = CC_MAXVINS; if ( maxinputs > 0 ) threshold = total/maxinputs; else threshold = total; + Paymentspk = GetUnspendable(cp,0); for (iter=0; iter<2; iter++) { if ( iter == 0 ) @@ -254,7 +255,7 @@ int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t { val >>= 1; hexdata.resize(val); - decode_hex(hexdata,val,hexstr); + decode_hex(&hexdata[0],val,hexstr); return(0); } else return(-1); } @@ -262,7 +263,7 @@ int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64]; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64]; std::vector txidoprets; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); From 3e7e013503bb2c4dee8ab061d4d00375d0aeeb32 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 03:34:20 -1100 Subject: [PATCH 015/787] Syntax --- src/cc/payments.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 378db54ae..1715df3c9 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -263,7 +263,7 @@ int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64]; std::vector txidoprets; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64]; std::vector txidoprets; std::string rawtx; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -287,8 +287,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) checkallocations += allocation; if ( opret.size() > 0 ) { - scriptPubKey.resize(opret.size); - memcpy(&onlyopret[0],&opret[0],opret.size); + scriptPubKey.resize(opret.size()); + memcpy(&onlyopret[0],&opret[0],opret.size()); numoprets++; } } else break; @@ -324,7 +324,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) txidpk = CCtxidaddr(txidaddr,createtxid); if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,amount,60,createtxid)) >= amount ) { - if ( (CCchange= (inputsum - amount)) > TRANSACTIONS_TXFEE ) + if ( (CCchange= (inputsum - amount)) > PAYMENTS_TXFEE ) mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,CCchange,Paymentspk,txidpk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); return(payments_rawtxresult(result,rawtx,0)); @@ -378,11 +378,11 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) if ( useopret == 0 ) { txidpk = CCtxidaddr(txidaddr,txid); - mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,funds,Paymentspk,txidpk)); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,amount,Paymentspk,txidpk)); } else { - mtx.vout.push_back(MakeCCvout(EVAL_PAYMENTS,funds,Paymentspk)); + mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk)); opret = EncodePaymentsFundOpRet(txid); } rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,opret); @@ -411,10 +411,10 @@ UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) if ( params != 0 && n > 1 && n <= 3 ) { allocation = juint(jitem(params,0),0); - retval0 = payments_parsehexdata(scriptPubKey,jitem(params,1)); - if ( m == 3 ) - retval1 = payments_parsehexdata(opret,jitem(params,2)); - if ( allocation > 0 && retval == 0 && retval1 == 0 && AddNormalinputs(mtx,mypk,PAYMENTS_TXFEE,10) > 0 ) + retval0 = payments_parsehexdata(scriptPubKey,jitem(params,1),0); + if ( n == 3 ) + retval1 = payments_parsehexdata(opret,jitem(params,2),0); + if ( allocation > 0 && retval0 == 0 && retval1 == 0 && AddNormalinputs(mtx,mypk,PAYMENTS_TXFEE,10) > 0 ) { rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsTxidOpRet(allocation,scriptPubKey,opret)); return(payments_rawtxresult(result,rawtx,0)); @@ -433,7 +433,7 @@ UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations=0; + UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations=0; std::string rawtx; cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n >= 4 ) { @@ -488,13 +488,13 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { - UniValue result(UniValue::VOBJ),a(UniValue:VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,n,flag=0,allocation,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; int64_t funds,fundsopret; char fundsaddr[64],fundsopretaddr[64],txidaddr[64]; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,n,flag=0,allocation,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; int64_t funds,fundsopret; char fundsaddr[64],fundsopretaddr[64],txidaddr[64]; uint256 createtxid,hashBlock; cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n == 1 ) { Paymentspk = GetUnspendable(cp,0); - txid = payments_juint256(jitem(params,0)); - if ( myGetTransaction(txid,tx,hashBlock) != 0 ) + createtxid = payments_juint256(jitem(params,0)); + if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) { if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,updateflag,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { @@ -554,7 +554,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { std::vector > addressIndex; uint256 txid,hashBlock; - UniValue result(UniValue::VOBJ),a(UniValue:VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; result.push_back(Pair("result","success")); Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); From d2fce5741fa0f9f66101485db45e375328374090 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 03:37:53 -1100 Subject: [PATCH 016/787] .GetHex() --- src/cc/payments.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 1715df3c9..d50116459 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -505,13 +505,13 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) for (i=0; i scriptPubKey,opret; - obj.push_back(Pair("txidopret",txidoprets[i])); + obj.push_back(Pair("txidopret",txidoprets[i].GetHex())); if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { - obj.push_back(Pair("scriptPubKey",scriptPubKey)); + obj.push_back(Pair("scriptPubKey",scriptPubKey.GetHex())); if ( opret.size() != 0 ) { - obj.push_back(Pair("opreturn",opret)); + obj.push_back(Pair("opreturn",opret.GetHex())); numoprets++; } } @@ -525,7 +525,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) } else { - txidpk = CCtxidaddr(txidaddr,txid); + txidpk = CCtxidaddr(txidaddr,createtxid); GetCCaddress1of2(cp,fundsaddr,Paymentspk,txidpk); funds = CCaddress_balance(fundsaddr); result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); From 12c16f0dd7c866d6efd0968b7b096e0c26d8a214 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 03:38:52 -1100 Subject: [PATCH 017/787] ToString --- src/cc/payments.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index d50116459..91fc9245b 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -508,10 +508,10 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) obj.push_back(Pair("txidopret",txidoprets[i].GetHex())); if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { - obj.push_back(Pair("scriptPubKey",scriptPubKey.GetHex())); + obj.push_back(Pair("scriptPubKey",scriptPubKey.ToString())); if ( opret.size() != 0 ) { - obj.push_back(Pair("opreturn",opret.GetHex())); + obj.push_back(Pair("opreturn",opret.ToString())); numoprets++; } } From 2bc975404effaa782a219f4c8138e8030976e100 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 03:41:53 -1100 Subject: [PATCH 018/787] Ouster --- src/cc/payments.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 91fc9245b..629e8351e 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -488,7 +488,7 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,n,flag=0,allocation,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; int64_t funds,fundsopret; char fundsaddr[64],fundsopretaddr[64],txidaddr[64]; uint256 createtxid,hashBlock; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,allocation,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; int64_t funds,fundsopret; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n == 1 ) { @@ -508,12 +508,20 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) obj.push_back(Pair("txidopret",txidoprets[i].GetHex())); if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { - obj.push_back(Pair("scriptPubKey",scriptPubKey.ToString())); + outstr = (char *)malloc(scriptPubKey.size() + opret.size() + 1); + for (j=0; j Date: Thu, 21 Mar 2019 03:46:05 -1100 Subject: [PATCH 019/787] CCPayments.h --- src/wallet/rpcwallet.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index c64e22103..83793fdad 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5327,6 +5327,7 @@ int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits #include "../cc/CCPrices.h" #include "../cc/CCHeir.h" #include "../cc/CCMarmara.h" +#include "../cc/CCPayments.h" int32_t ensure_CCrequirements(uint8_t evalcode) { From 384ec464ed0cf91982222e9fec0af865f4a3fa20 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 04:13:21 -1100 Subject: [PATCH 020/787] -updateflag --- src/cc/payments.cpp | 38 ++++++++++++++++++-------------------- src/wallet/rpcwallet.cpp | 4 ++-- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 629e8351e..42a8bb1d9 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -70,19 +70,19 @@ uint8_t DecodePaymentsFundOpRet(CScript scriptPubKey,uint256 &checktxid) return(0); } -CScript EncodePaymentsOpRet(int32_t updateflag,int32_t lockedblocks,int32_t minrelease,int32_t totalallocations,std::vector txidoprets) +CScript EncodePaymentsOpRet(int32_t lockedblocks,int32_t minrelease,int32_t totalallocations,std::vector txidoprets) { CScript opret; uint8_t evalcode = EVAL_PAYMENTS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'C' << updateflag << lockedblocks << minrelease << totalallocations << txidoprets); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'C' << lockedblocks << minrelease << totalallocations << txidoprets); return(opret); } -uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &updateflag,int32_t &lockedblocks,int32_t &minrelease,int32_t &totalallocations,std::vector &txidoprets) +uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &totalallocations,std::vector &txidoprets) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> updateflag; ss >> lockedblocks; ss >> minrelease; ss >> totalallocations; ss >> txidoprets) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> totalallocations; ss >> txidoprets) != 0 ) { if ( e == EVAL_PAYMENTS && f == 'C' ) return(f); @@ -263,7 +263,7 @@ int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64]; std::vector txidoprets; std::string rawtx; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64]; std::vector txidoprets; std::string rawtx; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -273,7 +273,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) { - if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,updateflag,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { for (i=0; i txidoprets; + CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,lockedblocks,minrelease,totalallocations; std::vector txidoprets; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -368,7 +368,7 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; if ( n == 3 ) useopret = jint(jitem(params,2),0) != 0; - if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() == 1 || DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,updateflag,lockedblocks,minrelease,totalallocations,txidoprets) == 0 ) + if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() == 1 || DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 0 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid createtxid")); @@ -433,15 +433,14 @@ UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations=0; std::string rawtx; + UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,lockedblocks,minrelease,totalallocations=0; std::string rawtx; cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n >= 4 ) { - updateflag = juint(jitem(params,0),0); - lockedblocks = juint(jitem(params,1),0); - minrelease = juint(jitem(params,2),0); - for (i=0; i scriptPubKey,opret; int32_t allocation; @@ -472,7 +471,7 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) { mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(updateflag,lockedblocks,minrelease,totalallocations,txidoprets)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(lockedblocks,minrelease,totalallocations,txidoprets)); return(payments_rawtxresult(result,rawtx,0)); } result.push_back(Pair("result","error")); @@ -488,7 +487,7 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,allocation,numoprets=0,updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; int64_t funds,fundsopret; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,allocation,numoprets=0,lockedblocks,minrelease,totalallocations; std::vector txidoprets; int64_t funds,fundsopret; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n == 1 ) { @@ -496,9 +495,8 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) createtxid = payments_juint256(jitem(params,0)); if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) { - if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,updateflag,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { - result.push_back(Pair("updateable",updateflag!=0?"yes":"no")); result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); result.push_back(Pair("totalallocations",(int64_t)totalallocations)); result.push_back(Pair("minrelease",(int64_t)minrelease)); @@ -562,7 +560,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { std::vector > addressIndex; uint256 txid,hashBlock; - UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t updateflag,lockedblocks,minrelease,totalallocations; std::vector txidoprets; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease,totalallocations; std::vector txidoprets; result.push_back(Pair("result","success")); Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); @@ -572,7 +570,7 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) txid = it->first.txhash; if ( it->first.index == 0 && myGetTransaction(txid,tx,hashBlock) != 0 ) { - if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,updateflag,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' ) + if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' ) { a.push_back(uint256_str(str,txid)); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 83793fdad..1668a6cb6 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5620,8 +5620,8 @@ UniValue payments_txidopret(const UniValue& params, bool fHelp) UniValue payments_create(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; char *jsonstr=0; - if ( fHelp || params.size() < 5 ) - throw runtime_error("paymentscreate updateallowed lockedblocks minamount paytxid0,...,paytxidN\n"); + if ( fHelp || params.size() < 4 ) + throw runtime_error("paymentscreate lockedblocks minamount paytxid0,...,paytxidN\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; From b7dd9a3685ae5105dbad5950dc8756a4936ed5a8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 04:56:23 -1100 Subject: [PATCH 021/787] All payments rpc to have one string arg --- src/cc/payments.cpp | 16 +++++++++++----- src/wallet/rpcwallet.cpp | 18 +++++++++--------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 42a8bb1d9..a945d6a28 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -16,16 +16,22 @@ #include "CCPayments.h" /* - 0) create <- update_allowed flag, locked_blocks, minrelease, list of scriptPubKeys, allocations + 0) txidopret <- allocation, scriptPubKey, opret + 1) create <- locked_blocks, minrelease, list of txidopret - 1) lock amount to global CC address + 2) lock amount opretflag to global CC address with opret or txidaddr without - 2) release amount -> vout[i] will be scriptPubKeys[i] and (amount * allocations[i]) / sumallocations[] (only using vins that have been locked for locked_blocks+). will make a tx with less than amount if it can find enough vins for minrelease amount - - 3) update (vins from all scriptPubkeys) new update_allowed flag, locked_blocks, minrelease, list of scriptPubKeys, allocations (only if update_allowed) + 3) release amount -> vout[i] will be scriptPubKeys[i] and (amount * allocations[i]) / sumallocations[] (only using vins that have been locked for locked_blocks+). will make a tx with less than amount if it can find enough vins for minrelease amount 4) info txid -> display parameters, funds 5) list -> all txids + + First step is to create txids with the info needed in their opreturns. this info is the weight, scriptPubKey and opret if needed. To do that txidopret is used: + + ./c is a script that invokes komodo-cli with the correct -ac_name + + ./komodo-cli -ac_name=PAY paymentstxidopret \"[9,%222102d6f13a8f745921cdb811e32237bb98950af1a5952be7b3d429abd9152f8e388dac%22]\" + ./c paymentstxidopret \"[1,%2221039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775ac%22]\" */ // start of consensus code diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 1668a6cb6..2bde855eb 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5581,8 +5581,8 @@ UniValue cclib(const UniValue& params, bool fHelp) UniValue payments_release(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; char *jsonstr=0; - if ( fHelp || params.size() != 2 ) - throw runtime_error("paymentsrelease createtxid amount\n"); + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsrelease \"[%22createtxid%22,amount]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; @@ -5594,8 +5594,8 @@ UniValue payments_release(const UniValue& params, bool fHelp) UniValue payments_fund(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; char *jsonstr=0; - if ( fHelp || params.size() < 2 || params.size() > 3 ) - throw runtime_error("paymentsfund createtxid amount [useopret]\n"); + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsfund \"[%22createtxid%22,amount(,useopret)]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; @@ -5607,8 +5607,8 @@ UniValue payments_fund(const UniValue& params, bool fHelp) UniValue payments_txidopret(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; char *jsonstr=0; - if ( fHelp || params.size() < 2 ) - throw runtime_error("paymentstxidopret scriptPubKey opretformat\n"); + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentstxidopret \"[allocation,%22scriptPubKey%22(,%22destopret%22)]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; @@ -5620,8 +5620,8 @@ UniValue payments_txidopret(const UniValue& params, bool fHelp) UniValue payments_create(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; char *jsonstr=0; - if ( fHelp || params.size() < 4 ) - throw runtime_error("paymentscreate lockedblocks minamount paytxid0,...,paytxidN\n"); + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentscreate \"[lockedblocks,minamount,%22paytxid0%22,...,%22paytxidN%22]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; @@ -5634,7 +5634,7 @@ UniValue payments_info(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; char *jsonstr=0; if ( fHelp || params.size() != 1 ) - throw runtime_error("paymentsinfo createtxid\n"); + throw runtime_error("paymentsinfo \"[%22createtxid%22]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; From 51f0cd7bdbe2ab0207f4c7a7cdbe003250c35854 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 05:05:04 -1100 Subject: [PATCH 022/787] +prints --- src/cc/payments.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index a945d6a28..456bab847 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -30,7 +30,7 @@ ./c is a script that invokes komodo-cli with the correct -ac_name - ./komodo-cli -ac_name=PAY paymentstxidopret \"[9,%222102d6f13a8f745921cdb811e32237bb98950af1a5952be7b3d429abd9152f8e388dac%22]\" + ./c paymentstxidopret \"[9,%222102d6f13a8f745921cdb811e32237bb98950af1a5952be7b3d429abd9152f8e388dac%22]\" ./c paymentstxidopret \"[1,%2221039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775ac%22]\" */ @@ -305,6 +305,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid txidoprets[i]")); result.push_back(Pair("txi",(int64_t)i)); + if ( params != 0 ) + free_json(params); return(result); } else if ( checkallocations != totalallocations ) @@ -313,6 +315,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("error","totalallocations mismatch")); result.push_back(Pair("checkallocations",(int64_t)checkallocations)); result.push_back(Pair("totalallocations",(int64_t)totalallocations)); + if ( params != 0 ) + free_json(params); return(result); } else if ( numoprets > 0 ) @@ -320,6 +324,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("result","error")); result.push_back(Pair("error","too many oprets")); result.push_back(Pair("numoprets",(int64_t)numoprets)); + if ( params != 0 ) + free_json(params); return(result); } for (i=0; i PAYMENTS_TXFEE ) mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,CCchange,Paymentspk,txidpk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); + if ( params != 0 ) + free_json(params); return(payments_rawtxresult(result,rawtx,0)); } else @@ -358,6 +366,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("result","error")); result.push_back(Pair("error","parameters error")); } + if ( params != 0 ) + free_json(params); return(result); } @@ -392,6 +402,8 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) opret = EncodePaymentsFundOpRet(txid); } rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,opret); + if ( params != 0 ) + free_json(params); return(payments_rawtxresult(result,rawtx,0)); } else @@ -405,6 +417,8 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("result","error")); result.push_back(Pair("error","parameters error")); } + if ( params != 0 ) + free_json(params); return(result); } @@ -423,6 +437,8 @@ UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) if ( allocation > 0 && retval0 == 0 && retval1 == 0 && AddNormalinputs(mtx,mypk,PAYMENTS_TXFEE,10) > 0 ) { rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsTxidOpRet(allocation,scriptPubKey,opret)); + if ( params != 0 ) + free_json(params); return(payments_rawtxresult(result,rawtx,0)); } result.push_back(Pair("result","error")); @@ -432,7 +448,11 @@ UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) { result.push_back(Pair("result","error")); result.push_back(Pair("error","parameters error")); + result.push_back(Pair("n",(int64_t)n)); + fprintf(stderr,"(%s) %p\n",jsonstr,params); } + if ( params != 0 ) + free_json(params); return(result); } @@ -462,6 +482,8 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("error","invalid txidopret")); result.push_back(Pair("txid",txidoprets[i].GetHex())); result.push_back(Pair("txi",(int64_t)i)); + if ( params != 0 ) + free_json(params); return(result); } } @@ -470,6 +492,8 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("result","error")); result.push_back(Pair("error","too many opreturns")); result.push_back(Pair("numoprets",(int64_t)numoprets)); + if ( params != 0 ) + free_json(params); return(result); } mypk = pubkey2pk(Mypubkey()); @@ -478,6 +502,8 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) { mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(lockedblocks,minrelease,totalallocations,txidoprets)); + if ( params != 0 ) + free_json(params); return(payments_rawtxresult(result,rawtx,0)); } result.push_back(Pair("result","error")); @@ -488,6 +514,8 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("result","error")); result.push_back(Pair("error","parameters error")); } + if ( params != 0 ) + free_json(params); return(result); } @@ -560,6 +588,8 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("result","error")); result.push_back(Pair("error","parameters error")); } + if ( params != 0 ) + free_json(params); return(result); } From 2f752ee6c9b306eeaafbc3caae5f4fa4c9a0c3cc Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 05:12:21 -1100 Subject: [PATCH 023/787] (char *)params[0].get_str().c_str() --- src/wallet/rpcwallet.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 2bde855eb..062ef6148 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5580,7 +5580,7 @@ UniValue cclib(const UniValue& params, bool fHelp) UniValue payments_release(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; char *jsonstr=0; + struct CCcontract_info *cp,C; if ( fHelp || params.size() != 1 ) throw runtime_error("paymentsrelease \"[%22createtxid%22,amount]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) @@ -5588,12 +5588,12 @@ UniValue payments_release(const UniValue& params, bool fHelp) const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); cp = CCinit(&C,EVAL_PAYMENTS); - return(PaymentsRelease(cp,jsonstr)); + return(PaymentsRelease(cp,(char *)params[0].get_str().c_str())); } UniValue payments_fund(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; char *jsonstr=0; + struct CCcontract_info *cp,C; if ( fHelp || params.size() != 1 ) throw runtime_error("paymentsfund \"[%22createtxid%22,amount(,useopret)]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) @@ -5601,12 +5601,12 @@ UniValue payments_fund(const UniValue& params, bool fHelp) const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); cp = CCinit(&C,EVAL_PAYMENTS); - return(PaymentsFund(cp,jsonstr)); + return(PaymentsFund(cp,(char *)params[0].get_str().c_str())); } UniValue payments_txidopret(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; char *jsonstr=0; + struct CCcontract_info *cp,C; if ( fHelp || params.size() != 1 ) throw runtime_error("paymentstxidopret \"[allocation,%22scriptPubKey%22(,%22destopret%22)]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) @@ -5614,12 +5614,12 @@ UniValue payments_txidopret(const UniValue& params, bool fHelp) const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); cp = CCinit(&C,EVAL_PAYMENTS); - return(PaymentsTxidopret(cp,jsonstr)); + return(PaymentsTxidopret(cp,(char *)params[0].get_str().c_str())); } UniValue payments_create(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; char *jsonstr=0; + struct CCcontract_info *cp,C; if ( fHelp || params.size() != 1 ) throw runtime_error("paymentscreate \"[lockedblocks,minamount,%22paytxid0%22,...,%22paytxidN%22]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) @@ -5627,12 +5627,12 @@ UniValue payments_create(const UniValue& params, bool fHelp) const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); cp = CCinit(&C,EVAL_PAYMENTS); - return(PaymentsCreate(cp,jsonstr)); + return(PaymentsCreate(cp,(char *)params[0].get_str().c_str())); } UniValue payments_info(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; char *jsonstr=0; + struct CCcontract_info *cp,C; if ( fHelp || params.size() != 1 ) throw runtime_error("paymentsinfo \"[%22createtxid%22]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) @@ -5640,12 +5640,12 @@ UniValue payments_info(const UniValue& params, bool fHelp) const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); cp = CCinit(&C,EVAL_PAYMENTS); - return(PaymentsInfo(cp,jsonstr)); + return(PaymentsInfo(cp,(char *)params[0].get_str().c_str())); } UniValue payments_list(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; char *jsonstr=0; + struct CCcontract_info *cp,C; if ( fHelp || params.size() != 0 ) throw runtime_error("paymentslist\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) @@ -5653,7 +5653,7 @@ UniValue payments_list(const UniValue& params, bool fHelp) const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); cp = CCinit(&C,EVAL_PAYMENTS); - return(PaymentsList(cp,jsonstr)); + return(PaymentsList(cp,"")); } UniValue oraclesaddress(const UniValue& params, bool fHelp) From 90e3ffbcf0b9541fb5f53a90291ede94b1a13fda Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 05:55:38 -1100 Subject: [PATCH 024/787] Check lockedblocks --- src/cc/payments.cpp | 110 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 11 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 456bab847..531aa2a33 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -19,9 +19,9 @@ 0) txidopret <- allocation, scriptPubKey, opret 1) create <- locked_blocks, minrelease, list of txidopret - 2) lock amount opretflag to global CC address with opret or txidaddr without + 2) fund createtxid amount opretflag to global CC address with opret or txidaddr without - 3) release amount -> vout[i] will be scriptPubKeys[i] and (amount * allocations[i]) / sumallocations[] (only using vins that have been locked for locked_blocks+). will make a tx with less than amount if it can find enough vins for minrelease amount + 3) release amount -> vout[i] will be scriptPubKeys[i] and (amount * allocations[i]) / sumallocations[] (only using vins that have been locked for locked_blocks+). 4) info txid -> display parameters, funds 5) list -> all txids @@ -30,8 +30,43 @@ ./c is a script that invokes komodo-cli with the correct -ac_name - ./c paymentstxidopret \"[9,%222102d6f13a8f745921cdb811e32237bb98950af1a5952be7b3d429abd9152f8e388dac%22]\" - ./c paymentstxidopret \"[1,%2221039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775ac%22]\" + ./c paymentstxidopret \"[9,%222102d6f13a8f745921cdb811e32237bb98950af1a5952be7b3d429abd9152f8e388dac%22]\" -> rawhex with txid 95d9fc8d8a3ef63693c7427e59ff5e177ef63b7345d5f6d6497ac262699a8def + + ./c paymentstxidopret \"[1,%2221039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775ac%22]\" -> rawhex txid 00469695a08b975ceaf7258896abbf1455eb0f383e8a98fc650deace4cbf02a1 + + now we have 2 txid with the required info in the opreturn. one of them has a 9 and the other a 1 for a 90%/10% split. + + ./c paymentscreate \"[0,0,%2295d9fc8d8a3ef63693c7427e59ff5e177ef63b7345d5f6d6497ac262699a8def%22,%2200469695a08b975ceaf7258896abbf1455eb0f383e8a98fc650deace4cbf02a1%22]\" -> created txid 318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a that will be the createtxid that the other rpc calls will use. + + lets see if this appears in the list + + ./c paymentslist -> + { + "result": "success", + "createtxids": [ + "318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a" + ] + } + + It appeared! now lets get more info on it: + ./c paymentsinfo \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22]\" + { + "lockedblocks": 0, + "totalallocations": 10, + "minrelease": 0, + "RWRM36sC8jSctyFZtsu7CyDcHYPdZX7nPZ": 0.00000000, + "REpyKi7avsVduqZ3eimncK4uKqSArLTGGK": 0.00000000, + "totalfunds": 0.00000000, + "result": "success" + } + + There are 2 possible places the funds for this createtxid can be, the first is the special address that is derived from combining the globalCC address with the txidaddr. txidaddr is a non-spendable markeraddress created by converting the txid into a 33 byte pubkey by prefixing 0x02 to the txid. It is a 1of2 address, so it doesnt matter that nobody knows the privkey for this txidaddr. the second address is the global CC address and only utxo to that address with an opreturn containing the createtxid are funds valid for this payments CC createtxid + + next let us add some funds to it. the funds can be to either of the two addresses, controlled by useopret (defaults to 0) + + ./c paymentsfund \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1,0]\" + + */ // start of consensus code @@ -153,9 +188,9 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // helper functions for rpc calls in rpcwallet.cpp -int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid) +int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid,int32_t latestheight) { - char coinaddr[64]; CPubKey Paymentspk; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx,tx; int32_t iter,vout,n = 0; + char coinaddr[64]; CPubKey Paymentspk; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx,tx; int32_t iter,vout,ht,n = 0; std::vector > unspentOutputs; if ( maxinputs > CC_MAXVINS ) maxinputs = CC_MAXVINS; @@ -175,6 +210,13 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP vout = (int32_t)it->first.index; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { + if ( latestheight != 0 ) + { + if ( (ht= komodo_blockheight(hashBlock)) == 0 ) + continue; + else if ( ht > latestheight ) + continue; + } if ( iter == 0 ) { std::vector scriptPubKey,opret; @@ -268,7 +310,8 @@ int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { - CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; + int32_t latestheight,nextheight = komodo_nextheight(); + CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64]; std::vector txidoprets; std::string rawtx; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); @@ -281,6 +324,21 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + return(result); + } + latestheight = (nextheight - lockedblocks - 1); + if ( amount < minrelease ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","amount too smal")); + result.push_back(Pair("amount",ValueFromAmount(amount))); + result.push_back(Pair("minrelease",ValueFromAmount(minrelease))); + return(result); + } for (i=0; i scriptPubKey,opret; @@ -334,9 +392,9 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout[i].nValue /= totalallocations; } txidpk = CCtxidaddr(txidaddr,createtxid); - if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,amount,60,createtxid)) >= amount ) + if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,amount+PAYMENTS_TXFEE,60,createtxid,latestheight)) >= amount ) { - if ( (CCchange= (inputsum - amount)) > PAYMENTS_TXFEE ) + if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,CCchange,Paymentspk,txidpk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) @@ -391,6 +449,14 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) } else if ( AddNormalinputs(mtx,mypk,amount+PAYMENTS_TXFEE,60) > 0 ) { + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } if ( useopret == 0 ) { txidpk = CCtxidaddr(txidaddr,txid); @@ -439,7 +505,7 @@ UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsTxidOpRet(allocation,scriptPubKey,opret)); if ( params != 0 ) free_json(params); - return(payments_rawtxresult(result,rawtx,0)); + return(payments_rawtxresult(result,rawtx,1)); } result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid params or cant find txfee")); @@ -465,6 +531,14 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) { lockedblocks = juint(jitem(params,0),0); minrelease = juint(jitem(params,1),0); + if ( lockedblocks < 0 || minrelease < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } for (i=0; i 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); result.push_back(Pair("totalallocations",(int64_t)totalallocations)); result.push_back(Pair("minrelease",(int64_t)minrelease)); @@ -597,7 +679,6 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { std::vector > addressIndex; uint256 txid,hashBlock; UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease,totalallocations; std::vector txidoprets; - result.push_back(Pair("result","success")); Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); SetCCtxids(addressIndex,markeraddr); @@ -608,10 +689,17 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' ) { + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + return(result); + } a.push_back(uint256_str(str,txid)); } } } + result.push_back(Pair("result","success")); result.push_back(Pair("createtxids",a)); return(result); } From 7f14b077b7260e695ee9d0b759b428d16d956a03 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 06:06:18 -1100 Subject: [PATCH 025/787] +print --- src/cc/payments.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 531aa2a33..21efc0623 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -64,9 +64,13 @@ next let us add some funds to it. the funds can be to either of the two addresses, controlled by useopret (defaults to 0) - ./c paymentsfund \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1,0]\" + ./c paymentsfund \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1,0]\" -> txid 28f69b925bb7a21d2a3ba2327e85eb2031b014e976e43f5c2c6fb8a76767b221, which indeed sent funds to RWRM36sC8jSctyFZtsu7CyDcHYPdZX7nPZ without an opreturn and it appears on the payments info. + + ./c paymentsfund \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1,1]\" -> txid cc93330b5c951b724b246b3b138d00519c33f2a600a7c938bc9e51aff6e20e32, which indeed sent funds to REpyKi7avsVduqZ3eimncK4uKqSArLTGGK with an opreturn and it appears on the payments info. +./c paymentsrelease \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1.5]\" -> + */ // start of consensus code @@ -208,20 +212,30 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP { txid = it->first.txhash; vout = (int32_t)it->first.index; + fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr) if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { if ( latestheight != 0 ) { if ( (ht= komodo_blockheight(hashBlock)) == 0 ) + { + fprintf(stderr,"null ht\n"); continue; + } else if ( ht > latestheight ) + { + fprintf(stderr,"ht.%d > lastheight.%d\n",ht,lastheight); continue; + } } if ( iter == 0 ) { std::vector scriptPubKey,opret; if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() < 2 || DecodePaymentsFundOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,checktxid) != 'F' || checktxid != createtxid ) + { + fprintf(stderr,"bad opret %s vs %s\n",checktxid.GetHex().c_str(),createtxid.GetHex().c_str()); continue; + } } if ( (nValue= IsPaymentsvout(cp,vintx,vout)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { @@ -232,7 +246,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP n++; if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) break; - } + } else fprintf(stderr,"nValue %.8f vs threshold %.8f\n",(double)nValue/COIN,(double)threshold/COIN); } } } From 33e35e4dd3eb2bc99aa443605fb32d520a1bc254 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 06:08:55 -1100 Subject: [PATCH 026/787] ; --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 21efc0623..e4c567cf7 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -212,7 +212,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP { txid = it->first.txhash; vout = (int32_t)it->first.index; - fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr) + fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { if ( latestheight != 0 ) From d6c1609dad3d0721fc15435e4dd7f13446ef2650 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 06:09:55 -1100 Subject: [PATCH 027/787] latestheight --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index e4c567cf7..789918354 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -224,7 +224,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP } else if ( ht > latestheight ) { - fprintf(stderr,"ht.%d > lastheight.%d\n",ht,lastheight); + fprintf(stderr,"ht.%d > lastheight.%d\n",ht,latestheight); continue; } } From 9894fad3526b11daf81b5bc0280a8f5aa2f87dfc Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 06:12:47 -1100 Subject: [PATCH 028/787] IsPaymentsvout fix --- src/cc/payments.cpp | 44 +++----------------------------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 789918354..805e2e031 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -135,55 +135,17 @@ uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t & return(0); } -int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) +int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr) { char destaddr[64]; if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) + if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && (cmpaddr[0] == 0 || strcmp(destaddr,cmpaddr) == 0) ) return(tx.vout[v].nValue); } return(0); } -bool PaymentsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) -{ - static uint256 zerohash; - CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) - { - //fprintf(stderr,"vini.%d check mempool\n",i); - if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("cant find vinTx"); - else - { - //fprintf(stderr,"vini.%d check hash and vout\n",i); - if ( hashBlock == zerohash ) - return eval->Invalid("cant Payments from mempool"); - if ( (assetoshis= IsPaymentsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) - inputs += assetoshis; - } - } - } - for (i=0; iInvalid("mismatched inputs != outputs + txfee"); - } - else return(true); -} - bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { return(true); @@ -237,7 +199,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP continue; } } - if ( (nValue= IsPaymentsvout(cp,vintx,vout)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if ( (nValue= IsPaymentsvout(cp,vintx,vout,coinaddr)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); From ee5fe1118204ec8b08a65897fa70ce321ed514ab Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 06:19:47 -1100 Subject: [PATCH 029/787] GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); --- src/cc/payments.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 805e2e031..24946993d 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -288,7 +288,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { int32_t latestheight,nextheight = komodo_nextheight(); CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64]; std::vector txidoprets; std::string rawtx; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; std::string rawtx; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -372,6 +372,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,CCchange,Paymentspk,txidpk)); + GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); + CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); From e25929eb0d763fe2feba5820b21ed9b5f52d8085 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 06:22:29 -1100 Subject: [PATCH 030/787] -print --- src/cc/payments.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 24946993d..f3be6fd62 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -174,8 +174,8 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP { txid = it->first.txhash; vout = (int32_t)it->first.index; - fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + //fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); + if ( vout == 0 && GetTransaction(txid,vintx,hashBlock,false) != 0 ) { if ( latestheight != 0 ) { @@ -208,7 +208,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP n++; if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) break; - } else fprintf(stderr,"nValue %.8f vs threshold %.8f\n",(double)nValue/COIN,(double)threshold/COIN); + } //else fprintf(stderr,"nValue %.8f vs threshold %.8f\n",(double)nValue/COIN,(double)threshold/COIN); } } } @@ -448,7 +448,7 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,opret); if ( params != 0 ) free_json(params); - return(payments_rawtxresult(result,rawtx,0)); + return(payments_rawtxresult(result,rawtx,1)); } else { @@ -556,7 +556,7 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(lockedblocks,minrelease,totalallocations,txidoprets)); if ( params != 0 ) free_json(params); - return(payments_rawtxresult(result,rawtx,0)); + return(payments_rawtxresult(result,rawtx,1)); } result.push_back(Pair("result","error")); result.push_back(Pair("error","not enough normal funds")); From 423e04e0a4ae51e43cb265c1dcbb95f30c014fd2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 06:23:00 -1100 Subject: [PATCH 031/787] +comment --- src/cc/payments.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index f3be6fd62..a3c8bee95 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -69,7 +69,9 @@ ./c paymentsfund \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1,1]\" -> txid cc93330b5c951b724b246b3b138d00519c33f2a600a7c938bc9e51aff6e20e32, which indeed sent funds to REpyKi7avsVduqZ3eimncK4uKqSArLTGGK with an opreturn and it appears on the payments info. -./c paymentsrelease \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1.5]\" -> +./c paymentsrelease \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1.5]\" -> a8d5dbbb8ee94c05e75c4f3c5221091f59dcb86e0e9c4e1e3d2cf69e6fce6b81 + + it used both fund utxos */ From 403ee2bb1554a4936f17f03c72fe1b1531237c1e Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 06:25:43 -1100 Subject: [PATCH 032/787] Numoprets > 1 --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index a3c8bee95..2a73194fb 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -355,7 +355,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) free_json(params); return(result); } - else if ( numoprets > 0 ) + else if ( numoprets > 1 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","too many oprets")); From 089f13299c2c6a3a6fcb5327542ec6f3c5c2aa87 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 06:36:19 -1100 Subject: [PATCH 033/787] Validation comments --- src/cc/payments.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 2a73194fb..8fa87bd3d 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -150,6 +150,10 @@ int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { + // one of two addresses + // change must go to 1of2 txidaddr + // only 'F' or 1of2 txidaddr can be spent + // all vouts must match exactly return(true); } // end of consensus code From 9cead95733a8812c54a63a4ca2d77913aa59df61 Mon Sep 17 00:00:00 2001 From: DeckerSU Date: Thu, 21 Mar 2019 22:51:40 +0300 Subject: [PATCH 034/787] [fix] player lose focus, when call [i] inventory with MAXPACK (23) items --- src/cc/rogue/things.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/cc/rogue/things.c b/src/cc/rogue/things.c index 39c7b94b7..e0cf48454 100644 --- a/src/cc/rogue/things.c +++ b/src/cc/rogue/things.c @@ -527,6 +527,7 @@ add_line(struct rogue_state *rs,char *fmt, char *arg) touchwin(tw); wrefresh(tw); wait_for(rs,' '); + if (md_hasclreol()) { werase(tw); @@ -543,16 +544,31 @@ add_line(struct rogue_state *rs,char *fmt, char *arg) } else { + char *promptex = "--Wait 5 sec.--"; wmove(hw, LINES - 1, 0); - waddstr(hw, prompt); + waddstr(hw, newpage ? promptex : prompt); wrefresh(hw); - wait_for(rs,' '); + + if (newpage) { + + #ifdef _WIN32 + #ifdef _MSC_VER + #define sleep(x) Sleep(1000*(x)) + #endif + #endif + sleep(5); + + } else + wait_for(rs, ' '); + clearok(curscr, TRUE); wclear(hw); + touchwin(stdscr); } newpage = TRUE; line_cnt = 0; + maxlen = (int) strlen(prompt); } if (fmt != NULL && !(line_cnt == 0 && *fmt == '\0')) From 350c8a72aab49bbe0b3bc64427c5eee5b7975c43 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 20:01:40 -1100 Subject: [PATCH 035/787] Shift payments vouts to put change in vout0 --- src/cc/payments.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 8fa87bd3d..a9e55fd50 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -321,6 +321,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair("minrelease",ValueFromAmount(minrelease))); return(result); } + txidpk = CCtxidaddr(txidaddr,createtxid); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,0,Paymentspk,txidpk)); for (i=0; i scriptPubKey,opret; @@ -370,14 +372,13 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) } for (i=0; i= amount ) { if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) - mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,CCchange,Paymentspk,txidpk)); + mtx.vout[0].nValue = CCchange; GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); From fbc2feefa9fb44fcd7adeb0c9d8dc532d21b4bbd Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 20:41:06 -1100 Subject: [PATCH 036/787] Test --- src/cc/payments.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index a9e55fd50..08b1bca09 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -313,12 +313,12 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) return(result); } latestheight = (nextheight - lockedblocks - 1); - if ( amount < minrelease ) + if ( amount < minrelease*COIN ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","amount too smal")); result.push_back(Pair("amount",ValueFromAmount(amount))); - result.push_back(Pair("minrelease",ValueFromAmount(minrelease))); + result.push_back(Pair("minrelease",ValueFromAmount(minrelease*COIN))); return(result); } txidpk = CCtxidaddr(txidaddr,createtxid); @@ -375,6 +375,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout[i+1].nValue *= amount; mtx.vout[i+1].nValue /= totalallocations; } + fprintf(stderr,"addinputs %.8f\n",(double)amount/COIN); if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,amount+PAYMENTS_TXFEE,60,createtxid,latestheight)) >= amount ) { if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) From a1810eb2d39e285fb1c9108eb20b52e9dc007253 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 20:46:03 -1100 Subject: [PATCH 037/787] Test --- src/cc/payments.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 08b1bca09..744e82c5a 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -240,7 +240,7 @@ UniValue payments_rawtxresult(UniValue &result,std::string rawtx,int32_t broadca cJSON *payments_reparse(int32_t *nump,char *jsonstr) { - cJSON *params; char *newstr; int32_t i,j; + cJSON *params=0; char *newstr; int32_t i,j; *nump = 0; if ( jsonstr != 0 ) { @@ -263,11 +263,11 @@ cJSON *payments_reparse(int32_t *nump,char *jsonstr) } newstr[j] = 0; params = cJSON_Parse(newstr); - if ( 0 && params != 0 ) + if ( 1 && params != 0 ) printf("new.(%s) -> %s\n",newstr,jprint(params,0)); free(newstr); *nump = cJSON_GetArraySize(params); - } else params = 0; + } return(params); } @@ -295,6 +295,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) int32_t latestheight,nextheight = komodo_nextheight(); CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; std::string rawtx; + fprintf(stderr,"jsonstr.(%s)\n",jsonstr); cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); From c00a8f968d0f274f0bdc87aedaf69e42ae4380de Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 20:55:33 -1100 Subject: [PATCH 038/787] Add txidpk marker out --- src/cc/payments.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 744e82c5a..079a64329 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -381,6 +381,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) mtx.vout[0].nValue = CCchange; + mtx.vout.push_back(CTxOut(0,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); From 31c363cdad793d74592254fc371800901201c0d0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 21:01:07 -1100 Subject: [PATCH 039/787] +prints --- src/cc/payments.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 079a64329..4c8958e28 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -180,7 +180,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP { txid = it->first.txhash; vout = (int32_t)it->first.index; - //fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); + fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); if ( vout == 0 && GetTransaction(txid,vintx,hashBlock,false) != 0 ) { if ( latestheight != 0 ) @@ -212,6 +212,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP nValue = it->second.satoshis; totalinputs += nValue; n++; + fprintf(stderr,"iter.%d %s/v%d %s %.8f\n",iter,txid.GetHex().c_str(),vout,coinaddr,(double)nValue/COIN); if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) break; } //else fprintf(stderr,"nValue %.8f vs threshold %.8f\n",(double)nValue/COIN,(double)threshold/COIN); @@ -263,7 +264,7 @@ cJSON *payments_reparse(int32_t *nump,char *jsonstr) } newstr[j] = 0; params = cJSON_Parse(newstr); - if ( 1 && params != 0 ) + if ( 0 && params != 0 ) printf("new.(%s) -> %s\n",newstr,jprint(params,0)); free(newstr); *nump = cJSON_GetArraySize(params); @@ -295,7 +296,6 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) int32_t latestheight,nextheight = komodo_nextheight(); CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; std::string rawtx; - fprintf(stderr,"jsonstr.(%s)\n",jsonstr); cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); From f0d03de3a738be57cd1c493f67719fd75c606713 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 21:04:25 -1100 Subject: [PATCH 040/787] +prints --- src/cc/payments.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 4c8958e28..2b3dcb7e2 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -381,9 +381,12 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) mtx.vout[0].nValue = CCchange; + fprintf(stderr,"CCchange %.8f\n",(double)CCchange/COIN); mtx.vout.push_back(CTxOut(0,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); + fprintf(stderr,"destaddr.(%s)\n",destaddr); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); + fprintf(stderr,"set 1of2\n"); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); From 6e0d22cf2c1a8e4d5819d3c639472fbf514ff33d Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 21:05:00 -1100 Subject: [PATCH 041/787] +print --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 2b3dcb7e2..d1f7e273e 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -386,7 +386,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); fprintf(stderr,"destaddr.(%s)\n",destaddr); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); - fprintf(stderr,"set 1of2\n"); + fprintf(stderr,"set 1of2 opretsize.%d\n",(int32_t)onlyopret.size()); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); From 1df9808b0cf2cd5788b637a3d77086d986f8b332 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 21:09:22 -1100 Subject: [PATCH 042/787] +print --- src/cc/CCtx.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index cb3f8b1a6..33be469c3 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -75,7 +75,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran othercond = MakeCCcond1(cp->evalcode, unspendablepk); GetCCaddress1of2(cp,CC1of2CCaddr,unspendablepk,unspendablepk); - //printf("evalcode.%d (%s)\n",cp->evalcode,unspendable); + fprintf(stderr,"evalcode.%d (%s)\n",cp->evalcode,unspendable); // tokens support: // to spend from dual/three-eval mypk vout @@ -96,6 +96,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran //This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation. for (i=0; i Date: Thu, 21 Mar 2019 21:11:12 -1100 Subject: [PATCH 043/787] -% --- src/cc/CCtx.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 33be469c3..734397227 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -96,7 +96,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran //This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation. for (i=0; i Date: Thu, 21 Mar 2019 21:17:06 -1100 Subject: [PATCH 044/787] +print --- src/cc/CCtx.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 734397227..e99a5b6ee 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -75,7 +75,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran othercond = MakeCCcond1(cp->evalcode, unspendablepk); GetCCaddress1of2(cp,CC1of2CCaddr,unspendablepk,unspendablepk); - fprintf(stderr,"evalcode.%d (%s)\n",cp->evalcode,unspendable); + //fprintf(stderr,"evalcode.%d (%s)\n",cp->evalcode,unspendable); // tokens support: // to spend from dual/three-eval mypk vout @@ -96,7 +96,6 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran //This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation. for (i=0; i (%s) vs %s\n",i,(double)utxovalues[i]/COIN,destaddr,cp->unspendableaddr2); + fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s) vs %s\n",i,(double)utxovalues[i]/COIN,destaddr,cp->unspendableaddr2); //std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " for vin[" << i << "] satoshis=" << utxovalues[i] << std::endl; if( strcmp(destaddr, myaddr) == 0 ) { @@ -183,7 +180,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran { privkey = unspendablepriv; cond = othercond; - //fprintf(stderr,"FinalizeCCTx evalcode(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable); + fprintf(stderr,"FinalizeCCTx evalcode(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable); } else if (strcmp(destaddr, unspendabletokensaddr) == 0) { @@ -212,7 +209,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran // check if this is spending from 1of2 cc coins addr: else if (strcmp(cp->coins1of2addr, destaddr) == 0) { - //fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr); + fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr); privkey = cp->coins1of2priv;//myprivkey; if (othercond1of2 == 0) othercond1of2 = MakeCCcond1of2(cp->evalcode, cp->coins1of2pk[0], cp->coins1of2pk[1]); @@ -280,7 +277,8 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran } } } else fprintf(stderr,"FinalizeCCTx couldnt find %s\n",mtx.vin[i].prevout.hash.ToString().c_str()); - } + fprintf(stderr,"done i.%d of %d\n",i,n); + } if ( mycond != 0 ) cc_free(mycond); if ( condCC2 != 0 ) From 857b9f650c2f4acfa36cf50e026515f3bf081d16 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 21:20:56 -1100 Subject: [PATCH 045/787] +print --- src/cc/CCtx.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index e99a5b6ee..d26b14f6b 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -48,7 +48,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran int32_t i,flag,utxovout,n,err = 0; char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], unspendabletokensaddr[64],CC1of2CCaddr[64]; uint8_t *privkey, myprivkey[32], unspendablepriv[32], /*tokensunspendablepriv[32],*/ *msg32 = 0; - CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond, *condCC2=0,*mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL; + CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond=0, *condCC2=0,*mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL; CPubKey unspendablepk /*, tokensunspendablepk*/; struct CCcontract_info *cpTokens, tokensC; globalpk = GetUnspendable(cp,0); @@ -156,7 +156,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran else { Getscriptaddress(destaddr,vintx.vout[utxovout].scriptPubKey); - fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s) vs %s\n",i,(double)utxovalues[i]/COIN,destaddr,cp->unspendableaddr2); + //fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s) vs %s\n",i,(double)utxovalues[i]/COIN,destaddr,cp->unspendableaddr2); //std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " for vin[" << i << "] satoshis=" << utxovalues[i] << std::endl; if( strcmp(destaddr, myaddr) == 0 ) { @@ -180,7 +180,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran { privkey = unspendablepriv; cond = othercond; - fprintf(stderr,"FinalizeCCTx evalcode(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable); + //fprintf(stderr,"FinalizeCCTx evalcode(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable); } else if (strcmp(destaddr, unspendabletokensaddr) == 0) { @@ -209,7 +209,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran // check if this is spending from 1of2 cc coins addr: else if (strcmp(cp->coins1of2addr, destaddr) == 0) { - fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr); + //fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr); privkey = cp->coins1of2priv;//myprivkey; if (othercond1of2 == 0) othercond1of2 = MakeCCcond1of2(cp->evalcode, cp->coins1of2pk[0], cp->coins1of2pk[1]); @@ -279,6 +279,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran } else fprintf(stderr,"FinalizeCCTx couldnt find %s\n",mtx.vin[i].prevout.hash.ToString().c_str()); fprintf(stderr,"done i.%d of %d\n",i,n); } + fprintf(stderr,"free A\n"); if ( mycond != 0 ) cc_free(mycond); if ( condCC2 != 0 ) @@ -291,6 +292,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran cc_free(othercond3); if ( othercond4 != 0 ) cc_free(othercond4); + fprintf(stderr,"free B\n"); if ( othercond1of2 != 0 ) cc_free(othercond1of2); if ( othercond1of2tokens != 0 ) @@ -301,6 +303,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran cc_free(mysingletokenscond); if ( othertokenscond != 0 ) cc_free(othertokenscond); + fprintf(stderr,"free C\n"); std::string strHex = EncodeHexTx(mtx); if ( strHex.size() > 0 ) return(strHex); From 4657f4db229acb9e58fee4eb936338852a9c2762 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 21:21:57 -1100 Subject: [PATCH 046/787] -print --- src/cc/payments.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index d1f7e273e..eadc3b847 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -376,17 +376,13 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout[i+1].nValue *= amount; mtx.vout[i+1].nValue /= totalallocations; } - fprintf(stderr,"addinputs %.8f\n",(double)amount/COIN); if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,amount+PAYMENTS_TXFEE,60,createtxid,latestheight)) >= amount ) { if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) mtx.vout[0].nValue = CCchange; - fprintf(stderr,"CCchange %.8f\n",(double)CCchange/COIN); mtx.vout.push_back(CTxOut(0,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); - fprintf(stderr,"destaddr.(%s)\n",destaddr); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); - fprintf(stderr,"set 1of2 opretsize.%d\n",(int32_t)onlyopret.size()); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); From 5560b8574b1bfcf8f8ac99219b06405940d76736 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 21:25:51 -1100 Subject: [PATCH 047/787] Test --- src/cc/CCtx.cpp | 5 +---- src/cc/payments.cpp | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index d26b14f6b..3df8026ac 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -277,9 +277,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran } } } else fprintf(stderr,"FinalizeCCTx couldnt find %s\n",mtx.vin[i].prevout.hash.ToString().c_str()); - fprintf(stderr,"done i.%d of %d\n",i,n); } - fprintf(stderr,"free A\n"); if ( mycond != 0 ) cc_free(mycond); if ( condCC2 != 0 ) @@ -292,7 +290,6 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran cc_free(othercond3); if ( othercond4 != 0 ) cc_free(othercond4); - fprintf(stderr,"free B\n"); if ( othercond1of2 != 0 ) cc_free(othercond1of2); if ( othercond1of2tokens != 0 ) @@ -303,8 +300,8 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran cc_free(mysingletokenscond); if ( othertokenscond != 0 ) cc_free(othertokenscond); - fprintf(stderr,"free C\n"); std::string strHex = EncodeHexTx(mtx); + fprintf(stderr,"hex.(%s)\n",strHex.c_str()); if ( strHex.size() > 0 ) return(strHex); else return("0"); diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index eadc3b847..1e7b6626b 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -180,7 +180,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP { txid = it->first.txhash; vout = (int32_t)it->first.index; - fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); + //fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); if ( vout == 0 && GetTransaction(txid,vintx,hashBlock,false) != 0 ) { if ( latestheight != 0 ) @@ -212,7 +212,7 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP nValue = it->second.satoshis; totalinputs += nValue; n++; - fprintf(stderr,"iter.%d %s/v%d %s %.8f\n",iter,txid.GetHex().c_str(),vout,coinaddr,(double)nValue/COIN); + //fprintf(stderr,"iter.%d %s/v%d %s %.8f\n",iter,txid.GetHex().c_str(),vout,coinaddr,(double)nValue/COIN); if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) break; } //else fprintf(stderr,"nValue %.8f vs threshold %.8f\n",(double)nValue/COIN,(double)threshold/COIN); From 5af957ff1393580b801fdede8942a835111fa51e Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 21:37:19 -1100 Subject: [PATCH 048/787] Test --- src/cc/CCtx.cpp | 1 - src/cc/payments.cpp | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index 3df8026ac..ed06c1a4d 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -301,7 +301,6 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran if ( othertokenscond != 0 ) cc_free(othertokenscond); std::string strHex = EncodeHexTx(mtx); - fprintf(stderr,"hex.(%s)\n",strHex.c_str()); if ( strHex.size() > 0 ) return(strHex); else return("0"); diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 1e7b6626b..b2ad47d32 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -230,6 +230,7 @@ UniValue payments_rawtxresult(UniValue &result,std::string rawtx,int32_t broadca result.push_back(Pair("hex",rawtx)); if ( DecodeHexTx(tx,rawtx) != 0 ) { + fprintf(stderr,"decoded\n"); if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) RelayTransaction(tx); result.push_back(Pair("txid",tx.GetHash().ToString())); @@ -295,7 +296,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { int32_t latestheight,nextheight = komodo_nextheight(); CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; std::string rawtx; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -324,7 +325,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) } txidpk = CCtxidaddr(txidaddr,createtxid); mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,0,Paymentspk,txidpk)); - for (i=0; i scriptPubKey,opret; vout.nValue = 0; @@ -343,7 +345,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) } else break; mtx.vout.push_back(vout); } - if ( i != txidoprets.size() ) + if ( i != m ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid txidoprets[i]")); @@ -371,13 +373,14 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) free_json(params); return(result); } - for (i=0; i= amount ) { + std::string rawtx; if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) mtx.vout[0].nValue = CCchange; mtx.vout.push_back(CTxOut(0,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); @@ -386,6 +389,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); + fprintf(stderr,"got rawtx.(%s)\n",rawtx.c_str()); return(payments_rawtxresult(result,rawtx,0)); } else From 62b92bad4edef9d8181232c7cb47da8fd8036b9c Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 21:45:40 -1100 Subject: [PATCH 049/787] Move variable --- src/cc/payments.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index b2ad47d32..d1e11dae5 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -230,7 +230,6 @@ UniValue payments_rawtxresult(UniValue &result,std::string rawtx,int32_t broadca result.push_back(Pair("hex",rawtx)); if ( DecodeHexTx(tx,rawtx) != 0 ) { - fprintf(stderr,"decoded\n"); if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) RelayTransaction(tx); result.push_back(Pair("txid",tx.GetHash().ToString())); @@ -389,7 +388,6 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); - fprintf(stderr,"got rawtx.(%s)\n",rawtx.c_str()); return(payments_rawtxresult(result,rawtx,0)); } else From b897d56363f0cd41ec580ab53f5cc52433217521 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Fri, 22 Mar 2019 16:54:01 +0800 Subject: [PATCH 050/787] . --- src/cc/CCPayments.h | 2 +- src/cc/payments.cpp | 23 +++++++++++++++++++++-- src/wallet/rpcwallet.cpp | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 247a0f2ec..56d36f74e 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -29,6 +29,6 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr); -UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsList(struct CCcontract_info *cp); #endif diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index d1e11dae5..8148be180 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -152,8 +152,23 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & { // one of two addresses // change must go to 1of2 txidaddr + // change is/must be in vout[0] // only 'F' or 1of2 txidaddr can be spent // all vouts must match exactly + BOOST_FOREACH(const CTxIn& vin, tx) + { + uint256 blockhash; CTransaction txin; + if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) + { + fprintf(stderr, "vin txid.%s\n", txin.GetHex().c_str()); + } + } + + BOOST_FOREACH(const CTxOut& vout, tx) + { + fprintf(stderr, "vout txid.%s\n", vout.hash.GetHex().c_str()); + } + return(true); } // end of consensus code @@ -377,6 +392,10 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout[i+1].nValue *= amount; mtx.vout[i+1].nValue /= totalallocations; } +<<<<<<< Updated upstream +======= + //fprintf(stderr,"addinputs %.8f\n",(double)amount/COIN); +>>>>>>> Stashed changes if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,amount+PAYMENTS_TXFEE,60,createtxid,latestheight)) >= amount ) { std::string rawtx; @@ -388,7 +407,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); - return(payments_rawtxresult(result,rawtx,0)); + return(payments_rawtxresult(result,rawtx,1)); } else { @@ -664,7 +683,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) return(result); } -UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) +UniValue PaymentsList(struct CCcontract_info *cp) { std::vector > addressIndex; uint256 txid,hashBlock; UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease,totalallocations; std::vector txidoprets; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 062ef6148..80ad2afe0 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5653,7 +5653,7 @@ UniValue payments_list(const UniValue& params, bool fHelp) const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); cp = CCinit(&C,EVAL_PAYMENTS); - return(PaymentsList(cp,"")); + return(PaymentsList(cp)); } UniValue oraclesaddress(const UniValue& params, bool fHelp) From 5ec3125c455274cbba8a9557b4069e36546b0dc3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 22:06:23 -1100 Subject: [PATCH 051/787] Gamescc stub --- src/cc/cclib.cpp | 12 ++++++++ src/cc/gamescc.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++++++ src/cc/gamescc.h | 31 +++++++++++++++++++ src/cc/makegames | 7 +++++ 4 files changed, 127 insertions(+) create mode 100644 src/cc/gamescc.cpp create mode 100644 src/cc/gamescc.h create mode 100755 src/cc/makegames diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 1330c6b3e..c8e714ba2 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -38,6 +38,9 @@ std::string MYCCLIBNAME = (char *)"rogue"; #elif BUILD_CUSTOMCC #include "customcc.h" +#elif BUILD_GAMESCC +#include "gamescc.h" + #else #define EVAL_SUDOKU 17 #define EVAL_MUSIG 18 @@ -73,6 +76,8 @@ CClib_methods[] = { (char *)"rogue", (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, 'X', EVAL_ROGUE }, #elif BUILD_CUSTOMCC RPC_FUNCS +#elif BUILD_GAMESCC + RPC_FUNCS #else { (char *)"sudoku", (char *)"gen", (char *)"", 0, 0, 'G', EVAL_SUDOKU }, { (char *)"sudoku", (char *)"txidinfo", (char *)"txid", 1, 1, 'T', EVAL_SUDOKU }, @@ -222,6 +227,8 @@ UniValue CClib_method(struct CCcontract_info *cp,char *method,char *jsonstr) } #elif BUILD_CUSTOMCC CUSTOM_DISPATCH +#elif BUILD_GAMESCC + CUSTOM_DISPATCH #else if ( cp->evalcode == EVAL_SUDOKU ) { @@ -420,6 +427,8 @@ bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C return(rogue_validate(cp,height,eval,tx)); #elif BUILD_CUSTOMCC return(custom_validate(cp,height,eval,tx)); +#elif BUILD_GAMESCC + return(games_validate(cp,height,eval,tx)); #else if ( cp->evalcode == EVAL_SUDOKU ) return(sudoku_validate(cp,height,eval,tx)); @@ -677,6 +686,9 @@ int32_t cclib_parsehash(uint8_t *hash32,cJSON *item,int32_t len) #elif BUILD_CUSTOMCC #include "customcc.cpp" +#elif BUILD_GAMESCC +#include "gamescc.cpp" + #else #include "sudoku.cpp" #include "musig.cpp" diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp new file mode 100644 index 000000000..f5f9dfba6 --- /dev/null +++ b/src/cc/gamescc.cpp @@ -0,0 +1,77 @@ + +CScript games_opret(uint8_t funcid,CPubKey pk) +{ + CScript opret; uint8_t evalcode = EVAL_CUSTOM; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk); + return(opret); +} + +uint8_t games_opretdecode(CPubKey &pk,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk) != 0 && e == EVAL_CUSTOM ) + { + return(f); + } + return(0); +} + +UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) +{ + CTransaction tx; + if ( rawtx.size() > 0 ) + { + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); +} + +UniValue games_func0(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result","success")); + result.push_back(Pair("message","just an example of an information returning rpc")); + return(result); +} + +// send yourself 1 coin to your CC address using normal utxo from your -pubkey + +UniValue games_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string rawtx; + UniValue result(UniValue::VOBJ); CPubKey mypk; int64_t amount = COIN; int32_t broadcastflag=0; + if ( txfee == 0 ) + txfee = GAMES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,COIN+txfee,64) >= COIN+txfee ) // add utxo to mtx + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,mypk)); // make vout0 + // add opreturn, change is automatically added and tx is properly signed + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,games_opret('1',mypk)); + return(games_rawtxresult(result,rawtx,broadcastflag)); + } + return(result); +} + +bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + char expectedaddress[64]; CPubKey pk; + if ( tx.vout.size() != 2 ) // make sure the tx only has 2 outputs + return eval->Invalid("invalid number of vouts"); + else if ( games_opretdecode(pk,tx.vout[1].scriptPubKey) != '1' ) // verify has opreturn + return eval->Invalid("invalid opreturn"); + GetCCaddress(cp,expectedaddress,pk); + if ( IsCClibvout(cp,tx,0,expectedaddress) == COIN ) // make sure amount and destination matches + return(true); + else return eval->Invalid("invalid vout0 amount"); +} + + diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h new file mode 100644 index 000000000..6b0bdc771 --- /dev/null +++ b/src/cc/gamescc.h @@ -0,0 +1,31 @@ + +std::string MYCCLIBNAME = (char *)"gamescc"; + +#define EVAL_GAMES (EVAL_FAUCET2+1) +#define GAMES_TXFEE 10000 + +#define MYCCNAME "games" + +#define RPC_FUNCS \ + { (char *)MYCCNAME, (char *)"func0", (char *)"", 1, 1, '0', EVAL_CUSTOM }, \ + { (char *)MYCCNAME, (char *)"func1", (char *)"", 0, 0, '1', EVAL_CUSTOM }, + +bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); +UniValue games_func0(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); + +#define CUSTOM_DISPATCH \ +if ( cp->evalcode == EVAL_GAMES ) \ +{ \ + if ( strcmp(method,"func0") == 0 ) \ + return(games_func0(txfee,cp,params)); \ + else if ( strcmp(method,"func1") == 0 ) \ + return(games_func1(txfee,cp,params)); \ + else \ + { \ + result.push_back(Pair("result","error")); \ + result.push_back(Pair("error","invalid gamescc method")); \ + result.push_back(Pair("method",method)); \ + return(result); \ + } \ +} diff --git a/src/cc/makegames b/src/cc/makegames new file mode 100755 index 000000000..b4b8cb803 --- /dev/null +++ b/src/cc/makegames @@ -0,0 +1,7 @@ +#!/bin/sh +gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp +cp gamescc.so ../libcc.so +cd .. +make +cd cc + From fa381dd73648dce713610c1a4d3f72f421bd15ee Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 22:07:53 -1100 Subject: [PATCH 052/787] EVAL_GAMES --- src/cc/gamescc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 6b0bdc771..5eceb2d35 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -7,8 +7,8 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define MYCCNAME "games" #define RPC_FUNCS \ - { (char *)MYCCNAME, (char *)"func0", (char *)"", 1, 1, '0', EVAL_CUSTOM }, \ - { (char *)MYCCNAME, (char *)"func1", (char *)"", 0, 0, '1', EVAL_CUSTOM }, + { (char *)MYCCNAME, (char *)"func0", (char *)"", 1, 1, '0', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"func1", (char *)"", 0, 0, '1', EVAL_GAMES }, bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); UniValue games_func0(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); From 717e72871cd3980a4a9f6283680f985e365e203f Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 22:09:00 -1100 Subject: [PATCH 053/787] Test --- src/cc/gamescc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index f5f9dfba6..cd2022231 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1,7 +1,7 @@ CScript games_opret(uint8_t funcid,CPubKey pk) { - CScript opret; uint8_t evalcode = EVAL_CUSTOM; + CScript opret; uint8_t evalcode = EVAL_GAMES; opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk); return(opret); } @@ -10,7 +10,7 @@ uint8_t games_opretdecode(CPubKey &pk,CScript scriptPubKey) { std::vector vopret; uint8_t e,f; GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk) != 0 && e == EVAL_CUSTOM ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk) != 0 && e == EVAL_GAMES ) { return(f); } From 550f698a5693219c67fd60859ca99f29d1493632 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Fri, 22 Mar 2019 17:15:19 +0800 Subject: [PATCH 054/787] fix --- src/cc/payments.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 8148be180..8b1156004 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -155,18 +155,18 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // change is/must be in vout[0] // only 'F' or 1of2 txidaddr can be spent // all vouts must match exactly - BOOST_FOREACH(const CTxIn& vin, tx) + BOOST_FOREACH(const CTxIn& vin, tx.vin) { uint256 blockhash; CTransaction txin; if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) { - fprintf(stderr, "vin txid.%s\n", txin.GetHex().c_str()); + fprintf(stderr, "vin txid.%s\n", txin.GetHash().GetHex().c_str()); } } - BOOST_FOREACH(const CTxOut& vout, tx) + BOOST_FOREACH(const CTxOut& vout, tx.vout) { - fprintf(stderr, "vout txid.%s\n", vout.hash.GetHex().c_str()); + fprintf(stderr, "vout txid.%s\n", vout.GetHash().GetHex().c_str()); } return(true); @@ -392,10 +392,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout[i+1].nValue *= amount; mtx.vout[i+1].nValue /= totalallocations; } -<<<<<<< Updated upstream -======= //fprintf(stderr,"addinputs %.8f\n",(double)amount/COIN); ->>>>>>> Stashed changes if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,amount+PAYMENTS_TXFEE,60,createtxid,latestheight)) >= amount ) { std::string rawtx; From 9ef377508f91241118295ceac9209067db7db405 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 22:20:41 -1100 Subject: [PATCH 055/787] Add PAYMENTS_TXFEE --- src/cc/payments.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index d1e11dae5..0d36d3b9e 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -377,12 +377,12 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout[i+1].nValue *= amount; mtx.vout[i+1].nValue /= totalallocations; } - if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,amount+PAYMENTS_TXFEE,60,createtxid,latestheight)) >= amount ) + if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,amount+2*PAYMENTS_TXFEE,60,createtxid,latestheight)) >= amount ) { std::string rawtx; if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) mtx.vout[0].nValue = CCchange; - mtx.vout.push_back(CTxOut(0,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); From a0a3e141ba8ad699a9f48670ab29b6d749f76cb1 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Fri, 22 Mar 2019 17:25:21 +0800 Subject: [PATCH 056/787] fix --- src/cc/payments.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 8b1156004..d63f23515 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -397,8 +397,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { std::string rawtx; if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) - mtx.vout[0].nValue = CCchange; - mtx.vout.push_back(CTxOut(0,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); + mtx.vout[0].nValue = CCchange-PAYMENTS_TXFEE; + mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); From 1272346fecd0a80f833db43f90fd2568a9c4e421 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 22:48:03 -1100 Subject: [PATCH 057/787] Add obj to a[] --- src/cc/payments.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 0d36d3b9e..e864a7ccf 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -626,6 +626,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) } free(outstr); } + a.push_back(obj); } flag++; if ( numoprets > 1 ) @@ -642,6 +643,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); GetCCaddress(cp,fundsopretaddr,Paymentspk); fundsopret = CCaddress_balance(fundsopretaddr); + result.push_back(Pair("txidoprets",a)); result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); result.push_back(Pair("result","success")); From b68178d5411a9eff4fc08efd1fa40966927a6ee7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 22:51:28 -1100 Subject: [PATCH 058/787] result.push_back --- src/cc/payments.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index e864a7ccf..a57fe0c84 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -626,7 +626,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) } free(outstr); } - a.push_back(obj); + result.push_back(obj); } flag++; if ( numoprets > 1 ) @@ -643,7 +643,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); GetCCaddress(cp,fundsopretaddr,Paymentspk); fundsopret = CCaddress_balance(fundsopretaddr); - result.push_back(Pair("txidoprets",a)); + //result.push_back(Pair("txidoprets",a)); result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); result.push_back(Pair("result","success")); From 678ada7d651954288b8072db48acbb25477e3ebe Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 22:56:36 -1100 Subject: [PATCH 059/787] Test --- src/cc/payments.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index a57fe0c84..eb92e4eb7 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -609,23 +609,26 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { UniValue obj(UniValue::VOBJ); std::vector scriptPubKey,opret; obj.push_back(Pair("txidopret",txidoprets[i].GetHex())); + fprintf(stderr,"i%d of %d\n",i,(int32_t)txidoprets.size()); if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { outstr = (char *)malloc(scriptPubKey.size() + opret.size() + 1); for (j=0; j Date: Thu, 21 Mar 2019 22:59:09 -1100 Subject: [PATCH 060/787] Test --- src/cc/payments.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index eb92e4eb7..f302f494c 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -612,24 +612,24 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) fprintf(stderr,"i%d of %d\n",i,(int32_t)txidoprets.size()); if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { - outstr = (char *)malloc(scriptPubKey.size() + opret.size() + 1); + outstr = (char *)malloc(2*(scriptPubKey.size() + opret.size()) + 1); for (j=0; j 1 ) @@ -646,7 +646,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); GetCCaddress(cp,fundsopretaddr,Paymentspk); fundsopret = CCaddress_balance(fundsopretaddr); - //result.push_back(Pair("txidoprets",a)); + result.push_back(Pair("txidoprets",a)); result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); result.push_back(Pair("result","success")); From a543449878fb599b39c7309c6273da02081698f2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 23:01:10 -1100 Subject: [PATCH 061/787] Reorder son --- src/cc/payments.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index f302f494c..371d16e91 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -608,22 +608,21 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) for (i=0; i scriptPubKey,opret; - obj.push_back(Pair("txidopret",txidoprets[i].GetHex())); - fprintf(stderr,"i%d of %d\n",i,(int32_t)txidoprets.size()); + obj.push_back(Pair("txid",txidoprets[i].GetHex())); if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { outstr = (char *)malloc(2*(scriptPubKey.size() + opret.size()) + 1); for (j=0; j 1 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","too many opreturns")); - result.push_back(Pair("numoprets",(int64_t)numoprets)); } else { + result.push_back(Pair("txidoprets",a)); txidpk = CCtxidaddr(txidaddr,createtxid); GetCCaddress1of2(cp,fundsaddr,Paymentspk,txidpk); funds = CCaddress_balance(fundsaddr); result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); GetCCaddress(cp,fundsopretaddr,Paymentspk); fundsopret = CCaddress_balance(fundsopretaddr); - result.push_back(Pair("txidoprets",a)); result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); result.push_back(Pair("result","success")); From 3a612c39012825741544a782a1c0b3b58ac73f77 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 23:05:15 -1100 Subject: [PATCH 062/787] +print --- src/cc/payments.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 371d16e91..c36eed447 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -344,6 +344,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) } else break; mtx.vout.push_back(vout); } + result.push_back(Pair("numoprets",(int64_t)numoprets)); if ( i != m ) { result.push_back(Pair("result","error")); From 60ed1e25e33c42537a06f05c971ed3d0da43b1e7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 23:07:00 -1100 Subject: [PATCH 063/787] Onlyopret --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index c36eed447..c4f869e34 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -368,7 +368,6 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { result.push_back(Pair("result","error")); result.push_back(Pair("error","too many oprets")); - result.push_back(Pair("numoprets",(int64_t)numoprets)); if ( params != 0 ) free_json(params); return(result); @@ -386,6 +385,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); + fprintf(stderr,"onlyopret.[%d]\n",(int32_t)onlyopret.size()); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); From 7650a93d25e02bb0d15ecbcc0e6faf83f62c3926 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 23:08:50 -1100 Subject: [PATCH 064/787] Fix onlyopret --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index c4f869e34..501d48b5a 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -337,7 +337,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) checkallocations += allocation; if ( opret.size() > 0 ) { - scriptPubKey.resize(opret.size()); + onlyopret.resize(opret.size()); memcpy(&onlyopret[0],&opret[0],opret.size()); numoprets++; } From ec58cc8ca7186283a88d6a80e4bf857b47a55c4b Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 23:11:12 -1100 Subject: [PATCH 065/787] -print --- src/cc/payments.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 501d48b5a..a2447404b 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -385,7 +385,6 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); - fprintf(stderr,"onlyopret.[%d]\n",(int32_t)onlyopret.size()); rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); From 1971ee717dd3af885f7b3ccc0e458b91271eafca Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 23:18:43 -1100 Subject: [PATCH 066/787] Minimum payout of PAYMENT_TXFEE --- src/cc/payments.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index a2447404b..16b182800 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -295,7 +295,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { int32_t latestheight,nextheight = komodo_nextheight(); CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t newamount,inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -372,15 +372,21 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) free_json(params); return(result); } + newamount = amount; for (i=0; i= amount ) + if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,60,createtxid,latestheight)) >= newamount+2*PAYMENTS_TXFEE ) { std::string rawtx; - if ( (CCchange= (inputsum - amount)) >= PAYMENTS_TXFEE ) + if ( (CCchange= (inputsum - newamount)) >= PAYMENTS_TXFEE ) mtx.vout[0].nValue = CCchange; mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); @@ -388,6 +394,8 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); if ( params != 0 ) free_json(params); + result.push_back(Pair("amount",ValueFromAmount(amount))); + result.push_back(Pair("newamount",ValueFromAmount(newamount))); return(payments_rawtxresult(result,rawtx,0)); } else From 7965bf808cd997d87251ff74a401f7d64ddb3d36 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 23:37:26 -1100 Subject: [PATCH 067/787] Fix Change --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 16b182800..83565fcd2 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -386,7 +386,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,60,createtxid,latestheight)) >= newamount+2*PAYMENTS_TXFEE ) { std::string rawtx; - if ( (CCchange= (inputsum - newamount)) >= PAYMENTS_TXFEE ) + if ( (CCchange= (inputsum - newamount - PAYMENTS_TXFEE)) >= PAYMENTS_TXFEE ) mtx.vout[0].nValue = CCchange; mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); From e66ec82ea22079abf55560f9c8fb1100c684e529 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Mar 2019 23:52:50 -1100 Subject: [PATCH 068/787] 2*txfee --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 83565fcd2..7971b9cbe 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -386,7 +386,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,60,createtxid,latestheight)) >= newamount+2*PAYMENTS_TXFEE ) { std::string rawtx; - if ( (CCchange= (inputsum - newamount - PAYMENTS_TXFEE)) >= PAYMENTS_TXFEE ) + if ( (CCchange= (inputsum - newamount - 2*PAYMENTS_TXFEE)) >= PAYMENTS_TXFEE ) mtx.vout[0].nValue = CCchange; mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); From b0009ecca2e0d94d5ba139b4d023d60532ebb762 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 00:48:24 -1100 Subject: [PATCH 069/787] Update --- src/cc/CCtx.cpp | 1 - src/cc/gamescc.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++-- src/cc/gamescc.h | 8 ++++---- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index ed06c1a4d..2713dd9ed 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -601,7 +601,6 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 return(0); } - int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinputs) { int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; char coinaddr[64]; uint256 txid,hashBlock; CTransaction tx; struct CC_utxo *utxos,*up; diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index cd2022231..edc4904fb 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -34,9 +34,49 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastf return(result); } -UniValue games_func0(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); + UniValue result(UniValue::VOBJ); int32_t i,n,playerid=0; uint16_t seeds[4]; uint64_t seed; bits256 hash; + if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 2 || n == 3) ) + { + hash = jbits256(jitem(params,0),0); + seed = jdouble(jitem(params,1),0); + if ( n == 3 ) + { + playerid = juint(jitem(params,2),0); + if ( playerid >= 0x100 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","playerid too big")); + return(result); + } + } + if ( seed == 0 ) + { + playerid++; + if ( playerid == 0x100 ) + { + for (i=0; i<8; i++) + hash.uints[i] ^= 0xffffffff; + playerid--; + } + for (i=0; i<8; i++) + { + if ( ((1 << i) & playerid) != 0 ) + seed ^= hash.uints[i]; + } + } + else + { + for (i=0; i<4; i++) + { + seeds[i] = (seed >> (i*16)); + seeds[i] = (seeds[i]*11109 + 13849); + } + seed = ((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]; + } + result.push_back(Pair("seed",seed)); + } result.push_back(Pair("result","success")); result.push_back(Pair("message","just an example of an information returning rpc")); return(result); diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 5eceb2d35..ebdb92a93 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -7,18 +7,18 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define MYCCNAME "games" #define RPC_FUNCS \ - { (char *)MYCCNAME, (char *)"func0", (char *)"", 1, 1, '0', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"rng", (char *)"hash,seed,playerid", 2, 3, '0', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"func1", (char *)"", 0, 0, '1', EVAL_GAMES }, bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); -UniValue games_func0(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #define CUSTOM_DISPATCH \ if ( cp->evalcode == EVAL_GAMES ) \ { \ - if ( strcmp(method,"func0") == 0 ) \ - return(games_func0(txfee,cp,params)); \ + if ( strcmp(method,"rng") == 0 ) \ + return(games_rng(txfee,cp,params)); \ else if ( strcmp(method,"func1") == 0 ) \ return(games_func1(txfee,cp,params)); \ else \ From 966f2410317a7b96cd0fa518bf904ee9a259641d Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:06:54 -1100 Subject: [PATCH 070/787] Test --- src/cc/gamescc.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index edc4904fb..b600a8c3a 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -76,9 +76,13 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) seed = ((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]; } result.push_back(Pair("seed",seed)); + result.push_back(Pair("result","success")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough params")); } - result.push_back(Pair("result","success")); - result.push_back(Pair("message","just an example of an information returning rpc")); return(result); } From b6cdf0725e35cf5ea347540022acffc6d3a869b6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:12:31 -1100 Subject: [PATCH 071/787] Test --- src/cc/gamescc.cpp | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index b600a8c3a..d083e2292 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -36,7 +36,7 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastf UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); int32_t i,n,playerid=0; uint16_t seeds[4]; uint64_t seed; bits256 hash; + UniValue result(UniValue::VOBJ); int32_t i,invertflag=0,n,playerid=0; uint16_t seeds[4]; uint64_t seed; bits256 hash; if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 2 || n == 3) ) { hash = jbits256(jitem(params,0),0); @@ -56,15 +56,9 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) playerid++; if ( playerid == 0x100 ) { - for (i=0; i<8; i++) - hash.uints[i] ^= 0xffffffff; + invertflag = 1; playerid--; } - for (i=0; i<8; i++) - { - if ( ((1 << i) & playerid) != 0 ) - seed ^= hash.uints[i]; - } } else { @@ -74,6 +68,8 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) seeds[i] = (seeds[i]*11109 + 13849); } seed = ((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]; + if ( invertflag != 0 ) + seed ^= -1; } result.push_back(Pair("seed",seed)); result.push_back(Pair("result","success")); From c5bcb12119f6fc8d2a58a8fb04731578d47620f4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:14:47 -1100 Subject: [PATCH 072/787] seeds --- src/cc/gamescc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index d083e2292..25088d7fc 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -59,6 +59,9 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) invertflag = 1; playerid--; } + for (i=0; i<8; i++) + if ( ((1 << i) & playerid) != 0 ) + seed ^= hash.uints[i]; } else { @@ -71,6 +74,7 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( invertflag != 0 ) seed ^= -1; } + result.push_back(Pair("playerid",(int64_t)(playerid - 1 + invertflag))); result.push_back(Pair("seed",seed)); result.push_back(Pair("result","success")); } From 6f6a9d28b20e188024e2a9d3a01353c055fc3d54 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:18:03 -1100 Subject: [PATCH 073/787] Initseed --- src/cc/gamescc.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 25088d7fc..a079df2cf 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -36,7 +36,7 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastf UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); int32_t i,invertflag=0,n,playerid=0; uint16_t seeds[4]; uint64_t seed; bits256 hash; + UniValue result(UniValue::VOBJ); int32_t i,invertflag=0,n,playerid=0; uint16_t seeds[4]; uint64_t seed,initseed; bits256 hash; if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 2 || n == 3) ) { hash = jbits256(jitem(params,0),0); @@ -60,21 +60,22 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) playerid--; } for (i=0; i<8; i++) + { if ( ((1 << i) & playerid) != 0 ) seed ^= hash.uints[i]; - } - else - { - for (i=0; i<4; i++) - { - seeds[i] = (seed >> (i*16)); - seeds[i] = (seeds[i]*11109 + 13849); + if ( invertflag != 0 ) + seed ^= (uint64_t)-1; } - seed = ((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]; - if ( invertflag != 0 ) - seed ^= -1; } + initseed = seed; + for (i=0; i<4; i++) + { + seeds[i] = (seed >> (i*16)); + seeds[i] = (seeds[i]*11109 + 13849); + } + seed = ((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]; result.push_back(Pair("playerid",(int64_t)(playerid - 1 + invertflag))); + result.push_back(Pair("initseed",initseed)); result.push_back(Pair("seed",seed)); result.push_back(Pair("result","success")); } From b03e6652ac640c7e135f7f99cd0d2e102732e0c0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:22:31 -1100 Subject: [PATCH 074/787] Rngnext --- src/cc/gamescc.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index a079df2cf..6efe19da6 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -34,9 +34,21 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastf return(result); } +uint64_t games_rngnext(uint64_t initseed) +{ + uint16_t seeds[4]; int32_t i; + for (i=0; i<4; i++) + { + seeds[i] = (seed >> (i*16)); + seeds[i] = (seeds[i]*11109 + 13849); + } + seed = ((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]; + return(seed); +} + UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); int32_t i,invertflag=0,n,playerid=0; uint16_t seeds[4]; uint64_t seed,initseed; bits256 hash; + UniValue result(UniValue::VOBJ); int32_t i,invertflag=0,n,playerid=0; uint64_t seed,initseed; bits256 hash; if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 2 || n == 3) ) { hash = jbits256(jitem(params,0),0); @@ -44,7 +56,7 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( n == 3 ) { playerid = juint(jitem(params,2),0); - if ( playerid >= 0x100 ) + if ( playerid >= 0xff ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","playerid too big")); @@ -54,26 +66,14 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( seed == 0 ) { playerid++; - if ( playerid == 0x100 ) - { - invertflag = 1; - playerid--; - } for (i=0; i<8; i++) { if ( ((1 << i) & playerid) != 0 ) seed ^= hash.uints[i]; - if ( invertflag != 0 ) - seed ^= (uint64_t)-1; } } initseed = seed; - for (i=0; i<4; i++) - { - seeds[i] = (seed >> (i*16)); - seeds[i] = (seeds[i]*11109 + 13849); - } - seed = ((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]; + seed = games_rngnext(initseed); result.push_back(Pair("playerid",(int64_t)(playerid - 1 + invertflag))); result.push_back(Pair("initseed",initseed)); result.push_back(Pair("seed",seed)); From 52e039e2e0d7b18ba059be738a356a5db16d9cd3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:24:49 -1100 Subject: [PATCH 075/787] More dynamic range --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 6efe19da6..335a72446 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -69,7 +69,7 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) for (i=0; i<8; i++) { if ( ((1 << i) & playerid) != 0 ) - seed ^= hash.uints[i]; + seed ^= (uint64_t)hash.uints[i] << ((i&1)*32); } } initseed = seed; From 9089441e247e93ebcdf795e1f7e20e604b96847d Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:27:23 -1100 Subject: [PATCH 076/787] Test --- src/cc/gamescc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 335a72446..b858338de 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -48,7 +48,7 @@ uint64_t games_rngnext(uint64_t initseed) UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); int32_t i,invertflag=0,n,playerid=0; uint64_t seed,initseed; bits256 hash; + UniValue result(UniValue::VOBJ); int32_t i,n,playerid=0; uint64_t seed,initseed; bits256 hash; if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 2 || n == 3) ) { hash = jbits256(jitem(params,0),0); @@ -74,7 +74,7 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) } initseed = seed; seed = games_rngnext(initseed); - result.push_back(Pair("playerid",(int64_t)(playerid - 1 + invertflag))); + result.push_back(Pair("playerid",(int64_t)(playerid - 1))); result.push_back(Pair("initseed",initseed)); result.push_back(Pair("seed",seed)); result.push_back(Pair("result","success")); From 96604c9906c777c68fece883ea5241c13da1d0a9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:28:10 -1100 Subject: [PATCH 077/787] Syntax --- src/cc/gamescc.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index b858338de..42e1086a6 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -39,11 +39,10 @@ uint64_t games_rngnext(uint64_t initseed) uint16_t seeds[4]; int32_t i; for (i=0; i<4; i++) { - seeds[i] = (seed >> (i*16)); + seeds[i] = (initseed >> (i*16)); seeds[i] = (seeds[i]*11109 + 13849); } - seed = ((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]; - return(seed); + return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); } UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) From 2845aa8a4e601cbc28014613a14599d39e756126 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:33:30 -1100 Subject: [PATCH 078/787] Split --- src/cc/gamescc.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 42e1086a6..bc871535b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -37,11 +37,14 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastf uint64_t games_rngnext(uint64_t initseed) { uint16_t seeds[4]; int32_t i; - for (i=0; i<4; i++) - { - seeds[i] = (initseed >> (i*16)); - seeds[i] = (seeds[i]*11109 + 13849); - } + seeds[0] = initseed; + seeds[1] = (initseed >> 16); + seeds[2] = (initseed >> 32); + seeds[3] = (initseed >> 48); + seeds[0] = (seeds[0]*11109 + 13849); + seeds[1] = ((seeds[0] ^ seeds[1])*11109 + 13849); + seeds[2] = ((seeds[0] ^ seeds[1] ^ seeds[2])*11109 + 13849); + seeds[3] = ((seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3])*11109 + 13849); return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); } From 6bc65b36d5d67a70dee690298f918b3ca98a73ce Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:42:54 -1100 Subject: [PATCH 079/787] #define GAMES_RNGMULT 11109 #define GAMES_RNGOFFSET 13849 #define GAMES_MAXRNGS 10000 --- src/cc/gamescc.cpp | 12 +++++++----- src/cc/gamescc.h | 4 ++++ src/cc/payments.cpp | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index bc871535b..ab1b802e0 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -41,10 +41,10 @@ uint64_t games_rngnext(uint64_t initseed) seeds[1] = (initseed >> 16); seeds[2] = (initseed >> 32); seeds[3] = (initseed >> 48); - seeds[0] = (seeds[0]*11109 + 13849); - seeds[1] = ((seeds[0] ^ seeds[1])*11109 + 13849); - seeds[2] = ((seeds[0] ^ seeds[1] ^ seeds[2])*11109 + 13849); - seeds[3] = ((seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3])*11109 + 13849); + seeds[0] = (seeds[0]*GAMES_RNGMULT + GAMES_RNGOFFSET); + seeds[1] = ((seeds[0] ^ seeds[1])*GAMES_RNGMULT + GAMES_RNGOFFSET); + seeds[2] = ((seeds[0] ^ seeds[1] ^ seeds[2])*GAMES_RNGMULT + GAMES_RNGOFFSET); + seeds[3] = ((seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3])*GAMES_RNGMULT + GAMES_RNGOFFSET); return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); } @@ -79,6 +79,9 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) result.push_back(Pair("playerid",(int64_t)(playerid - 1))); result.push_back(Pair("initseed",initseed)); result.push_back(Pair("seed",seed)); + for (i=0; i= COIN+txfee ) // add utxo to mtx { mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,mypk)); // make vout0 - // add opreturn, change is automatically added and tx is properly signed rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,games_opret('1',mypk)); return(games_rawtxresult(result,rawtx,broadcastflag)); } diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index ebdb92a93..aebda854f 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -4,6 +4,10 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define EVAL_GAMES (EVAL_FAUCET2+1) #define GAMES_TXFEE 10000 +#define GAMES_RNGMULT 11109 +#define GAMES_RNGOFFSET 13849 +#define GAMES_MAXRNGS 10000 + #define MYCCNAME "games" #define RPC_FUNCS \ diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 7971b9cbe..3dfcdcba9 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -383,7 +383,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout[i+1].nValue = PAYMENTS_TXFEE; } } - if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,60,createtxid,latestheight)) >= newamount+2*PAYMENTS_TXFEE ) + if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,MAX_CCVINS/2,createtxid,latestheight)) >= newamount+2*PAYMENTS_TXFEE ) { std::string rawtx; if ( (CCchange= (inputsum - newamount - 2*PAYMENTS_TXFEE)) >= PAYMENTS_TXFEE ) From 407eab0c8423540d870e6afa28f7b9e35dd54b06 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:45:33 -1100 Subject: [PATCH 080/787] CC_MAXVINS --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 3dfcdcba9..97b56ff59 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -383,7 +383,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) mtx.vout[i+1].nValue = PAYMENTS_TXFEE; } } - if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,MAX_CCVINS/2,createtxid,latestheight)) >= newamount+2*PAYMENTS_TXFEE ) + if ( (inputsum= AddPaymentsInputs(cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,CC_MAXVINS/2,createtxid,latestheight)) >= newamount+2*PAYMENTS_TXFEE ) { std::string rawtx; if ( (CCchange= (inputsum - newamount - 2*PAYMENTS_TXFEE)) >= PAYMENTS_TXFEE ) From e74277a9346716c0f736d0f10bbb101874daca98 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:47:23 -1100 Subject: [PATCH 081/787] Maxrngs --- src/cc/gamescc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index ab1b802e0..02ead4d8f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -82,6 +82,7 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) for (i=0; i Date: Fri, 22 Mar 2019 01:53:15 -1100 Subject: [PATCH 082/787] rngnext --- src/cc/gamescc.cpp | 31 +++++++++++++++++++++++++------ src/cc/gamescc.h | 10 +++++----- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 02ead4d8f..a9801d03e 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -34,7 +34,7 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastf return(result); } -uint64_t games_rngnext(uint64_t initseed) +uint64_t _games_rngnext(uint64_t initseed) { uint16_t seeds[4]; int32_t i; seeds[0] = initseed; @@ -48,6 +48,25 @@ uint64_t games_rngnext(uint64_t initseed) return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); } +UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); int32_t n; uint64_t seed; + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) + { + seed = jdouble(jitem(params,0),0); + result.push_back(Pair("seed",seed)); + seed = games_rngnext(seed); + result.push_back(Pair("rng",seed)); + result.push_back(Pair("result","success")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough params")); + } + return(result); +} + UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ); int32_t i,n,playerid=0; uint64_t seed,initseed; bits256 hash; @@ -75,13 +94,13 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) } } initseed = seed; - seed = games_rngnext(initseed); + seed = _games_rngnext(initseed); result.push_back(Pair("playerid",(int64_t)(playerid - 1))); - result.push_back(Pair("initseed",initseed)); - result.push_back(Pair("seed",seed)); + result.push_back(Pair("seed",initseed)); + result.push_back(Pair("rng",seed)); for (i=0; i", 0, 0, '1', EVAL_GAMES }, + { (char *)MYCCNAME, (char *)"rng", (char *)"hash,seed,playerid", 2, 3, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); -UniValue games_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #define CUSTOM_DISPATCH \ if ( cp->evalcode == EVAL_GAMES ) \ { \ if ( strcmp(method,"rng") == 0 ) \ return(games_rng(txfee,cp,params)); \ - else if ( strcmp(method,"func1") == 0 ) \ - return(games_func1(txfee,cp,params)); \ + else if ( strcmp(method,"rngnext") == 0 ) \ + return(games_rngnext(txfee,cp,params)); \ else \ { \ result.push_back(Pair("result","error")); \ From 1747b7ebeae9fe541131d43b49c1e7d00dfb28c8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 01:58:40 -1100 Subject: [PATCH 083/787] _ --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index a9801d03e..b05010e82 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -55,7 +55,7 @@ UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { seed = jdouble(jitem(params,0),0); result.push_back(Pair("seed",seed)); - seed = games_rngnext(seed); + seed = _games_rngnext(seed); result.push_back(Pair("rng",seed)); result.push_back(Pair("result","success")); } From c9765fec2a66f2fddef31615e6dff94f84e06d70 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 02:05:19 -1100 Subject: [PATCH 084/787] Fixes --- src/cc/gamescc.cpp | 20 ++++++++------------ src/cc/gamescc.h | 2 +- 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index b05010e82..c381ead1c 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -69,14 +69,13 @@ UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); int32_t i,n,playerid=0; uint64_t seed,initseed; bits256 hash; - if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 2 || n == 3) ) + UniValue result(UniValue::VOBJ); int32_t i,n,playerid=0; uint64_t seed=0,initseed; bits256 hash; + if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 1 || n == 2) ) { hash = jbits256(jitem(params,0),0); - seed = jdouble(jitem(params,1),0); - if ( n == 3 ) + if ( n == 2 ) { - playerid = juint(jitem(params,2),0); + playerid = juint(jitem(params,1),0); if ( playerid >= 0xff ) { result.push_back(Pair("result","error")); @@ -84,14 +83,11 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } } - if ( seed == 0 ) + playerid++; + for (i=0; i<8; i++) { - playerid++; - for (i=0; i<8; i++) - { - if ( ((1 << i) & playerid) != 0 ) - seed ^= (uint64_t)hash.uints[i] << ((i&1)*32); - } + if ( ((1 << i) & playerid) != 0 ) + seed ^= (uint64_t)hash.uints[i] << ((i&1)*32); } initseed = seed; seed = _games_rngnext(initseed); diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index adb318522..a8e472622 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -11,7 +11,7 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define MYCCNAME "games" #define RPC_FUNCS \ - { (char *)MYCCNAME, (char *)"rng", (char *)"hash,seed,playerid", 2, 3, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); From fa6f9fcbbf4505e8dacd970b69e2a73b25da3bde Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 02:12:31 -1100 Subject: [PATCH 085/787] Rng basics documented --- src/cc/gamescc.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index c381ead1c..d9ea74871 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1,4 +1,32 @@ +/* +./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\" +{ + "playerid": 250, + "seed": 1398876319979341887, + "rng": 14565767519458298868, + "lastrng": 15075236803740723044, + "maxrngs": 10000, + "result": "success" +} + + ./c cclib rngnext 17 \"[14565767519458298868]\" + { + "seed": 14565767519458297856, + "rng": 4253087318999719449, + "result": "success" + } + + The idea is for a game to start with a near future blockhash, so the lobby gets players signed up until just prior to the designated height. then that blockhash can be used to create a stream of rngs. + + the same initial rng can be used for all players, if the identical starting condition is required. up to 255 different initial rng can be derived from a single blockhash. (Actually any number is possible, for simplicity rng rpc limits to 255). + + you will notice maxrngs and lastrng, the lastrng is the rng value that will happen after maxrng iterations of calling rngnext with the current rng. This allows making time based multiplayer games where all the nodes can validate all the other nodes rng, even without realtime synchronization of all user input events. + + Every time period, all players would set their rng value to the lastrng value. The only thing to be careful of is it not exceed the maxrng calls to rngnext in a single time period. otherwise the same set of rng numbers will be repeated. +*/ + + CScript games_opret(uint8_t funcid,CPubKey pk) { CScript opret; uint8_t evalcode = EVAL_GAMES; From 112df09f652ca26d1874776efec3c12c8196b63c Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 04:25:09 -1100 Subject: [PATCH 086/787] Events --- src/cc/CCinclude.h | 2 ++ src/cc/gamescc.cpp | 58 +++++++++++++++++++++++++++++----------------- src/cc/gamescc.h | 18 +++++++++++++- src/main.cpp | 18 ++++++++++++-- src/miner.cpp | 17 ++++++++++++++ 5 files changed, 89 insertions(+), 24 deletions(-) diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index f5877c711..13d7236d4 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -195,6 +195,8 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, C int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid); bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); +void komodo_sendmessage(int32_t minpeers,int32_t maxpeers,const char *message,std::vector payload); +int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t len); CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, vscript_t vopretNonfungible); CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets); diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index d9ea74871..68de4bb8a 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -136,35 +136,51 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -// send yourself 1 coin to your CC address using normal utxo from your -pubkey - -UniValue games_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string rawtx; - UniValue result(UniValue::VOBJ); CPubKey mypk; int64_t amount = COIN; int32_t broadcastflag=0; - if ( txfee == 0 ) - txfee = GAMES_TXFEE; - mypk = pubkey2pk(Mypubkey()); - if ( AddNormalinputs(mtx,mypk,COIN+txfee,64) >= COIN+txfee ) // add utxo to mtx + UniValue result(UniValue::VOBJ); std::vector payload; int32_t n; + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,mypk)); // make vout0 - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,games_opret('1',mypk)); - return(games_rawtxresult(result,rawtx,broadcastflag)); + if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) + { + komodo_sendmessage(4,8,"events",payload); + result.push_back(Pair("result","success")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parsehexdata")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough params")); } return(result); } +UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + return(result); +} + +UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + return(result); +} + +UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + return(result); +} + bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) { - char expectedaddress[64]; CPubKey pk; - if ( tx.vout.size() != 2 ) // make sure the tx only has 2 outputs - return eval->Invalid("invalid number of vouts"); - else if ( games_opretdecode(pk,tx.vout[1].scriptPubKey) != '1' ) // verify has opreturn - return eval->Invalid("invalid opreturn"); - GetCCaddress(cp,expectedaddress,pk); - if ( IsCClibvout(cp,tx,0,expectedaddress) == COIN ) // make sure amount and destination matches - return(true); - else return eval->Invalid("invalid vout0 amount"); + return(true); } diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index a8e472622..02f589056 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -12,11 +12,19 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define RPC_FUNCS \ { (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \ - { (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, + { (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"create", (char *)"game,minplayers,maxplayers,buyin,numblocks", 5, 5, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"info", (char *)"txid", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"events", (char *)"hex", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"register", (char *)"txid", 1, 1, ' ', EVAL_GAMES }, bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #define CUSTOM_DISPATCH \ if ( cp->evalcode == EVAL_GAMES ) \ @@ -25,6 +33,14 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_rng(txfee,cp,params)); \ else if ( strcmp(method,"rngnext") == 0 ) \ return(games_rngnext(txfee,cp,params)); \ + else if ( strcmp(method,"create") == 0 ) \ + return(games_create(txfee,cp,params)); \ + else if ( strcmp(method,"info") == 0 ) \ + return(games_info(txfee,cp,params)); \ + else if ( strcmp(method,"register") == 0 ) \ + return(games_register(txfee,cp,params)); \ + else if ( strcmp(method,"events") == 0 ) \ + return(games_events(txfee,cp,params)); \ else \ { \ result.push_back(Pair("result","error")); \ diff --git a/src/main.cpp b/src/main.cpp index ce0760cf5..996fe2e3c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7035,8 +7035,22 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 1); return false; } - - + else if ( strCommand == "events" ) + { + int32_t i; + if ( ASSETCHAINS_CCLIB != MYCCLIBNAME || ASSETCHAINS_CCLIB != "gamescc" ) + { + Misbehaving(pfrom->GetId(), 1); + return false; + } + CNodeState *state = State(pfrom->GetId()); + if (state == NULL) + return; + for (i=0; iname ); + return(true); + } else if (strCommand == "verack") { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); diff --git a/src/miner.cpp b/src/miner.cpp index 2069e5a8c..eaba96ee1 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -931,6 +931,23 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, return CreateNewBlock(pubkey, scriptPubKey, gpucount, isStake); } +void komodo_sendmessage(int32_t minpeers,int32_t maxpeers,const char *message,std::vector payload) +{ + int32_t numsent = 0; + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if ( pnode->hSocket == INVALID_SOCKET ) + continue; + if ( numsent < minpeers || (rand() % 10) == 0 ) + { + pnode->PushMessage(message,payload); + if ( numsent++ > maxpeers ) + break; + } + } +} + void komodo_broadcast(CBlock *pblock,int32_t limit) { if (IsInitialBlockDownload()) From 3a141d3ccc00f17a26cf683f1fa4efbd6f2cb637 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 04:30:02 -1100 Subject: [PATCH 087/787] Test --- src/main.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 996fe2e3c..4aaa4e4ea 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7038,17 +7038,17 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, else if ( strCommand == "events" ) { int32_t i; - if ( ASSETCHAINS_CCLIB != MYCCLIBNAME || ASSETCHAINS_CCLIB != "gamescc" ) + if ( ASSETCHAINS_CCLIB != "gamescc" ) { Misbehaving(pfrom->GetId(), 1); return false; } - CNodeState *state = State(pfrom->GetId()); - if (state == NULL) - return; for (i=0; iname ); + CNodeState *state = State(pfrom->GetId()); + if ( state != NULL ) + fprintf(stderr," got event[%d] from %s: %s\n", (int32_t)vRecv.size(),__func__, state->name ); + else fprintf(stderr,"got event[%d]\n",(int32_t)vRecv.size()); return(true); } else if (strCommand == "verack") From 9c1f05c72a938f593b4f35157a3143770cdbe918 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 04:56:05 -1100 Subject: [PATCH 088/787] -print --- src/main.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 4aaa4e4ea..5ed1a35fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7045,10 +7045,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } for (i=0; iGetId()); - if ( state != NULL ) - fprintf(stderr," got event[%d] from %s: %s\n", (int32_t)vRecv.size(),__func__, state->name ); - else fprintf(stderr,"got event[%d]\n",(int32_t)vRecv.size()); + fprintf(stderr," got event[%d]\n",(int32_t)vRecv.size()); return(true); } else if (strCommand == "verack") From 5e08aa28b9574efdbdfefe1714cb5116819d77dd Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 05:26:55 -1100 Subject: [PATCH 089/787] Fix --- src/wallet/rpcwallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 062ef6148..f20a63664 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5653,7 +5653,7 @@ UniValue payments_list(const UniValue& params, bool fHelp) const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); cp = CCinit(&C,EVAL_PAYMENTS); - return(PaymentsList(cp,"")); + return(PaymentsList(cp,(char *)"")); } UniValue oraclesaddress(const UniValue& params, bool fHelp) From 08f197fff3ab9d415b70b4a26d1b92689d31872d Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 22 Mar 2019 08:14:34 -1100 Subject: [PATCH 090/787] payload --- src/main.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 5ed1a35fe..5027e5fa6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7043,9 +7043,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 1); return false; } - for (i=0; i payload; + vRecv >> payload; + for (i=0; i Date: Fri, 22 Mar 2019 08:51:37 -1100 Subject: [PATCH 091/787] komodo_netevent --- src/cc/cclib.cpp | 4 ++++ src/cc/gamescc.cpp | 8 ++++++++ src/komodo_defs.h | 1 + src/main.cpp | 4 +--- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index c8e714ba2..65c706862 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -48,6 +48,10 @@ std::string MYCCLIBNAME = (char *)"rogue"; std::string MYCCLIBNAME = (char *)"sudoku"; #endif +#ifndef BUILD_GAMESCC +void komodo_netevent(std::vector payload) {} +#endif + char *CClib_name() { return((char *)MYCCLIBNAME.c_str()); } struct CClib_rpcinfo diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 68de4bb8a..29df990b5 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -178,6 +178,14 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } +void komodo_netevent(std::vector payload) +{ + int32_t i; + for (i=0; i payload); #endif diff --git a/src/main.cpp b/src/main.cpp index 5027e5fa6..e520ad83f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7045,9 +7045,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } std::vector payload; vRecv >> payload; - for (i=0; i Date: Sat, 23 Mar 2019 14:32:43 +0800 Subject: [PATCH 092/787] initial validation commit ONLY validates non OP_RETURN --- src/cc/CCPayments.h | 2 +- src/cc/payments.cpp | 162 +++++++++++++++++++++++++++++++++++++++----- src/main.cpp | 2 +- 3 files changed, 147 insertions(+), 19 deletions(-) diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 56d36f74e..247a0f2ec 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -29,6 +29,6 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr); -UniValue PaymentsList(struct CCcontract_info *cp); +UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr); #endif diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 6bd575835..93272fad7 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -148,27 +148,155 @@ int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t return(0); } +void pub2createtxid(char *str) +{ + int i,n; + char *rev; + n = (int32_t)strlen(str); + rev = (char *)malloc(n + 1); + for (i=0; i txidoprets; + int32_t i; bool fHasOpret = false; + CPubKey txidpk,Paymentspk; + int64_t change; + + //the nValue 0 vout at the end of the tx (last one if no opret) + //it is a pay to pubkey vout + //the "pubkey" is just 0x02 + if ( tx.vout.size() < 2 ) + return(false); + if ( tx.vout.back().scriptPubKey[0] == OP_RETURN ) + { + scriptpubkey = HexStr(tx.vout[tx.vout.size()-2].scriptPubKey.begin()+2, tx.vout[tx.vout.size()-2].scriptPubKey.end()-1); + fHasOpret = true; + } else scriptpubkey = HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin()+2,tx.vout[tx.vout.size()-1].scriptPubKey.end()-1); + strcpy(temp, scriptpubkey.c_str()); + pub2createtxid(temp); + createtxid = Parseuint256(temp); + //printf("createtxid.%s\n",createtxid.ToString().c_str()); + + // use the createtxid to fetch the tx and all of the plans info. + if ( myGetTransaction(createtxid,tmptx,blockhash) != 0 ) + { + if ( tmptx.vout.size() > 0 && DecodePaymentsOpRet(tmptx.vout[tmptx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + { + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + return(false); + Paymentspk = GetUnspendable(cp,0); + //fprintf(stderr, "lockedblocks.%i minrelease.%i totalallocations.%i txidopret1.%s txidopret2.%s\n",lockedblocks, minrelease, totalallocations, txidoprets[0].ToString().c_str(), txidoprets[1].ToString().c_str() ); + + // Get all the script pubkeys and allocations + std::vector allocations; + std::vector scriptPubKeys; + int32_t checkallocations = 0; + i = 0; + BOOST_FOREACH(const uint256& txidopret, txidoprets) + { + CTransaction tx0; std::vector scriptPubKey,opret; int32_t allocation; + if ( myGetTransaction(txidopret,tx0,blockhash) != 0 && tx0.vout.size() > 1 && DecodePaymentsTxidOpRet(tx0.vout[tx0.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + scriptPubKeys.push_back(CScript(scriptPubKey.begin(), scriptPubKey.end())); + allocations.push_back(allocation); + //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%i\n",i,scriptPubKeys[i].ToString().c_str(),allocation); + checkallocations += allocation; + } + i++; + } + + // sanity check to make sure we got all the required info + if ( allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size() ) + return(false); + + //fprintf(stderr, "totalallocations.%i checkallocations.%i\n",totalallocations, checkallocations); + if ( totalallocations != checkallocations ) + return(false); + + txidpk = CCtxidaddr(txidaddr,createtxid); + GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); + //fprintf(stderr, "coinaddr.%s\n", coinaddr); + + // make sure change is in vout 0 and is paying to the contract address. + if ( (change= IsPaymentsvout(cp,tx,0,coinaddr)) == 0 ) + return(false); + + // Check vouts go to the right place and pay the right amounts. + int64_t amount = 0, checkamount; int32_t n = 0; + checkamount = tx.GetValueOut() - change - PAYMENTS_TXFEE; + for (i = 1; i < (fHasOpret ? tx.vout.size()-2 : tx.vout.size()-1); i++) { + std::string destscriptPubKey = HexStr(scriptPubKeys[n].begin(),scriptPubKeys[n].end()); + std::string voutscriptPubKey = HexStr(tx.vout[i].scriptPubKey.begin(),tx.vout[i].scriptPubKey.end()); + if ( destscriptPubKey != voutscriptPubKey ) + { + fprintf(stderr, "pays wrong destination destscriptPubKey.%s voutscriptPubKey.%s\n", destscriptPubKey.c_str(), voutscriptPubKey.c_str()); + return(false); + } + int64_t test = allocations[n]; + test *= checkamount; + test /= totalallocations; + if ( test != tx.vout[i].nValue ) + { + fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); + return(false); + } + amount += tx.vout[i].nValue; + n++; + } + if ( checkamount != amount ) + return(false); + + if ( amount < minrelease*COIN ) + { + fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amount, (int64_t)minrelease*COIN ); + return(false); + } + + i = 0; + BOOST_FOREACH(const CTxIn& vin, tx.vin) + { + CTransaction txin; + if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) + { + // check the vin comes from the CC address's + if ( IsPaymentsvout(cp,txin,i,coinaddr) != 0 ) + { + fprintf(stderr, "vin.%i is not a payments CC vout\n", i); + return(false); + } + // check the chain depth vs locked blcoks requirement. + CBlockIndex* pblockindex = mapBlockIndex[blockhash]; + if ( pblockindex->GetHeight() > chainActive.LastTip()->GetHeight()-lockedblocks ) + { + fprintf(stderr, "vin.%i is not elegible to be spent yet height.%i vs elegible_ht.%i\n", i, pblockindex->GetHeight(), chainActive.LastTip()->GetHeight()-lockedblocks); + return(false); + } + //fprintf(stderr, "vin txid.%s\n", txin.GetHash().GetHex().c_str()); + } + i++; + } + } + } + // one of two addresses? only seems to ever use the 1! // change must go to 1of2 txidaddr // change is/must be in vout[0] // only 'F' or 1of2 txidaddr can be spent // all vouts must match exactly - BOOST_FOREACH(const CTxIn& vin, tx.vin) - { - uint256 blockhash; CTransaction txin; - if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) - { - fprintf(stderr, "vin txid.%s\n", txin.GetHash().GetHex().c_str()); - } - } - - BOOST_FOREACH(const CTxOut& vout, tx.vout) - { - fprintf(stderr, "vout txid.%s\n", vout.GetHash().GetHex().c_str()); - } - return(true); } // end of consensus code @@ -411,7 +539,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) free_json(params); result.push_back(Pair("amount",ValueFromAmount(amount))); result.push_back(Pair("newamount",ValueFromAmount(newamount))); - return(payments_rawtxresult(result,rawtx,0)); + return(payments_rawtxresult(result,rawtx,1)); } else { @@ -691,7 +819,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) return(result); } -UniValue PaymentsList(struct CCcontract_info *cp) +UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { std::vector > addressIndex; uint256 txid,hashBlock; UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease,totalallocations; std::vector txidoprets; diff --git a/src/main.cpp b/src/main.cpp index e520ad83f..9930e733c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7045,7 +7045,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } std::vector payload; vRecv >> payload; - komodo_netevent(payload); + //komodo_netevent(payload); return(true); } else if (strCommand == "verack") From 7fbcfc27e3e4ee4fe383c24d531713865aa50fe9 Mon Sep 17 00:00:00 2001 From: smk762 <35845239+smk762@users.noreply.github.com> Date: Sat, 23 Mar 2019 15:29:23 +0800 Subject: [PATCH 093/787] Update cryptoconditions.py update keys, remove failing tokens tests (no tokenid) --- qa/rpc-tests/cryptoconditions.py | 49 ++++++++++++-------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/qa/rpc-tests/cryptoconditions.py b/qa/rpc-tests/cryptoconditions.py index ee119b75a..d5456e801 100755 --- a/qa/rpc-tests/cryptoconditions.py +++ b/qa/rpc-tests/cryptoconditions.py @@ -3,7 +3,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - from test_framework.test_framework import BitcoinTestFramework from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, assert_greater_than, \ @@ -25,7 +24,6 @@ def generate_random_string(length): random_string = ''.join(choice(ascii_uppercase) for i in range(length)) return random_string - class CryptoConditionsTest (BitcoinTestFramework): def setup_chain(self): @@ -105,13 +103,13 @@ class CryptoConditionsTest (BitcoinTestFramework): faucet = rpc.faucetaddress() assert_equal(faucet['result'], 'success') # verify all keys look like valid AC addrs, could be better - for x in ['myCCaddress', 'FaucetCCaddress', 'Faucetmarker', 'myaddress']: + for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: assert_equal(faucet[x][0], 'R') - + result = rpc.faucetaddress(self.pubkey) assert_success(result) # test that additional CCaddress key is returned - for x in ['myCCaddress', 'FaucetCCaddress', 'Faucetmarker', 'myaddress', 'CCaddress']: + for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: assert_equal(result[x][0], 'R') # no funds in the faucet yet @@ -179,12 +177,12 @@ class CryptoConditionsTest (BitcoinTestFramework): dice = rpc.diceaddress() assert_equal(dice['result'], 'success') - for x in ['myCCaddress', 'DiceCCaddress', 'Dicemarker', 'myaddress']: + for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: assert_equal(dice[x][0], 'R') dice = rpc.diceaddress(self.pubkey) assert_equal(dice['result'], 'success') - for x in ['myCCaddress', 'DiceCCaddress', 'Dicemarker', 'myaddress', 'CCaddress']: + for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: assert_equal(dice[x][0], 'R') # no dice created yet @@ -333,12 +331,12 @@ class CryptoConditionsTest (BitcoinTestFramework): rpc = self.nodes[0] result = rpc.tokenaddress() assert_success(result) - for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress']: + for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: assert_equal(result[x][0], 'R') result = rpc.tokenaddress(self.pubkey) assert_success(result) - for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress', 'CCaddress']: + for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: assert_equal(result[x][0], 'R') # there are no tokens created yet result = rpc.tokenlist() @@ -361,17 +359,6 @@ class CryptoConditionsTest (BitcoinTestFramework): result = rpc.tokenlist() assert_equal(result[0], tokenid) - # there are no token orders yet - result = rpc.tokenorders() - assert_equal(result, []) - - # getting token balance for pubkey - result = rpc.tokenbalance(self.pubkey) - assert_success(result) - assert_equal(result['balance'], 0) - assert_equal(result['CCaddress'], 'RCRsm3VBXz8kKTsYaXKpy7pSEzrtNNQGJC') - assert_equal(result['tokenid'], self.pubkey) - # get token balance for token with pubkey result = rpc.tokenbalance(tokenid, self.pubkey) assert_success(result) @@ -421,7 +408,7 @@ class CryptoConditionsTest (BitcoinTestFramework): tokenask = rpc.tokenask("100", tokenid, "7.77") tokenaskhex = tokenask['hex'] tokenaskid = self.send_and_mine(tokenask['hex'], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) order = result[0] assert order, "found order" @@ -440,7 +427,7 @@ class CryptoConditionsTest (BitcoinTestFramework): assert txid, "found txid" # should be no token orders - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # checking ask cancellation @@ -448,7 +435,7 @@ class CryptoConditionsTest (BitcoinTestFramework): testorderid = self.send_and_mine(testorder['hex'], rpc) cancel = rpc.tokencancelask(tokenid, testorderid) self.send_and_mine(cancel["hex"], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # invalid numtokens bid @@ -474,7 +461,7 @@ class CryptoConditionsTest (BitcoinTestFramework): tokenbid = rpc.tokenbid("100", tokenid, "10") tokenbidhex = tokenbid['hex'] tokenbidid = self.send_and_mine(tokenbid['hex'], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) order = result[0] assert order, "found order" @@ -493,7 +480,7 @@ class CryptoConditionsTest (BitcoinTestFramework): assert txid, "found txid" # should be no token orders - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # checking bid cancellation @@ -501,7 +488,7 @@ class CryptoConditionsTest (BitcoinTestFramework): testorderid = self.send_and_mine(testorder['hex'], rpc) cancel = rpc.tokencancelbid(tokenid, testorderid) self.send_and_mine(cancel["hex"], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # invalid token transfer amount (have to add status to CC code!) @@ -522,11 +509,11 @@ class CryptoConditionsTest (BitcoinTestFramework): def run_rewards_tests(self): rpc = self.nodes[0] result = rpc.rewardsaddress() - for x in ['RewardsCCaddress', 'myCCaddress', 'Rewardsmarker', 'myaddress']: + for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: assert_equal(result[x][0], 'R') result = rpc.rewardsaddress(self.pubkey) - for x in ['RewardsCCaddress', 'myCCaddress', 'Rewardsmarker', 'myaddress', 'CCaddress']: + for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: assert_equal(result[x][0], 'R') # no rewards yet @@ -637,12 +624,13 @@ class CryptoConditionsTest (BitcoinTestFramework): result = rpc.oraclesaddress() assert_success(result) - for x in ['OraclesCCaddress', 'Oraclesmarker', 'myCCaddress', 'myaddress']: + + for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: assert_equal(result[x][0], 'R') result = rpc.oraclesaddress(self.pubkey) assert_success(result) - for x in ['OraclesCCaddress', 'Oraclesmarker', 'myCCaddress', 'myaddress']: + for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: assert_equal(result[x][0], 'R') # there are no oracles created yet @@ -674,7 +662,6 @@ class CryptoConditionsTest (BitcoinTestFramework): # assert_success(result) # globals()["oracle_{}".format(f)] = self.send_and_mine(result['hex'], rpc) - def run_test (self): print("Mining blocks...") rpc = self.nodes[0] From 1f19c7c98710347b98300eaecafdd7ee3ec82029 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Sat, 23 Mar 2019 15:39:33 +0800 Subject: [PATCH 094/787] push fixes to sync PAY --- src/cc/payments.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 93272fad7..aece40b67 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -181,7 +181,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //it is a pay to pubkey vout //the "pubkey" is just 0x02 if ( tx.vout.size() < 2 ) - return(false); + return(eval->Invalid("not enough vouts")); if ( tx.vout.back().scriptPubKey[0] == OP_RETURN ) { scriptpubkey = HexStr(tx.vout[tx.vout.size()-2].scriptPubKey.begin()+2, tx.vout[tx.vout.size()-2].scriptPubKey.end()-1); @@ -198,7 +198,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & if ( tmptx.vout.size() > 0 && DecodePaymentsOpRet(tmptx.vout[tmptx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) - return(false); + return(eval->Invalid("negative values")); Paymentspk = GetUnspendable(cp,0); //fprintf(stderr, "lockedblocks.%i minrelease.%i totalallocations.%i txidopret1.%s txidopret2.%s\n",lockedblocks, minrelease, totalallocations, txidoprets[0].ToString().c_str(), txidoprets[1].ToString().c_str() ); @@ -222,11 +222,11 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // sanity check to make sure we got all the required info if ( allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size() ) - return(false); + return(eval->Invalid("missing data cannot validate")); //fprintf(stderr, "totalallocations.%i checkallocations.%i\n",totalallocations, checkallocations); if ( totalallocations != checkallocations ) - return(false); + return(eval->Invalid("allocation missmatch")); txidpk = CCtxidaddr(txidaddr,createtxid); GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); @@ -234,7 +234,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // make sure change is in vout 0 and is paying to the contract address. if ( (change= IsPaymentsvout(cp,tx,0,coinaddr)) == 0 ) - return(false); + return(eval->Invalid("change is in wrong vout or is wrong tx type")); // Check vouts go to the right place and pay the right amounts. int64_t amount = 0, checkamount; int32_t n = 0; @@ -245,7 +245,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & if ( destscriptPubKey != voutscriptPubKey ) { fprintf(stderr, "pays wrong destination destscriptPubKey.%s voutscriptPubKey.%s\n", destscriptPubKey.c_str(), voutscriptPubKey.c_str()); - return(false); + return(eval->Invalid("pays wrong address")); } int64_t test = allocations[n]; test *= checkamount; @@ -253,18 +253,19 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & if ( test != tx.vout[i].nValue ) { fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); - return(false); + //return(eval->Invalid("amounts do not match")); } amount += tx.vout[i].nValue; n++; } + // This is a backup check to make sure there are no extra vouts paying something else! if ( checkamount != amount ) - return(false); + return(eval->Invalid("amounts do not match")); if ( amount < minrelease*COIN ) { fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amount, (int64_t)minrelease*COIN ); - return(false); + //return(eval->Invalid("amount is too small")); } i = 0; @@ -274,24 +275,26 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) { // check the vin comes from the CC address's - if ( IsPaymentsvout(cp,txin,i,coinaddr) != 0 ) + char destaddr[64]; + Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey); + if ( strcmp(destaddr,coinaddr) != 0 ) { - fprintf(stderr, "vin.%i is not a payments CC vout\n", i); - return(false); + fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s vout.%i\n", i, txin.GetHash().ToString().c_str(), vin.prevout.n); + return(eval->Invalid("vin is not paymentsCC type")); } // check the chain depth vs locked blcoks requirement. CBlockIndex* pblockindex = mapBlockIndex[blockhash]; if ( pblockindex->GetHeight() > chainActive.LastTip()->GetHeight()-lockedblocks ) { fprintf(stderr, "vin.%i is not elegible to be spent yet height.%i vs elegible_ht.%i\n", i, pblockindex->GetHeight(), chainActive.LastTip()->GetHeight()-lockedblocks); - return(false); + return(eval->Invalid("vin not elegible")); } //fprintf(stderr, "vin txid.%s\n", txin.GetHash().GetHex().c_str()); } i++; } - } - } + } else return(eval->Invalid("create transaction cannot decode")); + } else fprintf(stderr, "cannot get contract txn\n");//return(eval->Invalid("Could not get contract transaction")); // one of two addresses? only seems to ever use the 1! // change must go to 1of2 txidaddr // change is/must be in vout[0] From 0844c8290e15ac3f89848562bf1c93e114c8e146 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Sat, 23 Mar 2019 15:50:02 +0800 Subject: [PATCH 095/787] missed return --- src/cc/payments.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index aece40b67..7e2db0381 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -290,7 +290,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & return(eval->Invalid("vin not elegible")); } //fprintf(stderr, "vin txid.%s\n", txin.GetHash().GetHex().c_str()); - } + } else return(eval->Invalid("cant get vin transaction")); i++; } } else return(eval->Invalid("create transaction cannot decode")); From afa0b894008073dc3f908f8fb2c79e3facb5fb20 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 24 Mar 2019 01:58:45 -1100 Subject: [PATCH 096/787] Reduce keystrokes three --- src/cc/gamescc.cpp | 48 ++++++++++++++++++++++---------------------- src/cc/rogue_rpc.cpp | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 29df990b5..89efcc6d2 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -136,30 +136,6 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); std::vector payload; int32_t n; - if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) - { - if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) - { - komodo_sendmessage(4,8,"events",payload); - result.push_back(Pair("result","success")); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt parsehexdata")); - } - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","not enough params")); - } - return(result); -} - UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ); @@ -178,6 +154,30 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } +UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); std::vector payload; int32_t n; + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) + { + if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) + { + komodo_sendmessage(4,8,"events",payload); + result.push_back(Pair("result","success")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parsehexdata")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough params")); + } + return(result); +} + void komodo_netevent(std::vector payload) { int32_t i; diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 495fa8774..0d6d9b3d4 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -918,7 +918,7 @@ UniValue rogue_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *param CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); CPubKey roguepk,mypk; uint256 gametxid,playertxid,batontxid; int64_t batonvalue,buyin; std::vector keystrokes,playerdata; int32_t numplayers,regslot,numkeys,batonht,batonvout,n,elapsed,gameheight,maxplayers; CTransaction tx; CTxOut txout; char *keystrokestr,destaddr[64]; std::string rawtx,symbol,pname; bits256 t; uint8_t mypriv[32]; if ( txfee == 0 ) - txfee = 10000; + txfee = 1000; // smaller than normal on purpose rogue_univalue(result,"keystrokes",-1,-1); if ( params != 0 && (n= cJSON_GetArraySize(params)) == 2 && (keystrokestr= jstr(jitem(params,1),0)) != 0 ) { From 00a8e9f59c2579c18f716ca1f21724b412c7b10d Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 24 Mar 2019 07:18:54 -1100 Subject: [PATCH 097/787] Force 1000 sats for keystrokes --- src/cc/rogue_rpc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 0d6d9b3d4..caf37b566 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -917,7 +917,7 @@ UniValue rogue_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *param int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); CPubKey roguepk,mypk; uint256 gametxid,playertxid,batontxid; int64_t batonvalue,buyin; std::vector keystrokes,playerdata; int32_t numplayers,regslot,numkeys,batonht,batonvout,n,elapsed,gameheight,maxplayers; CTransaction tx; CTxOut txout; char *keystrokestr,destaddr[64]; std::string rawtx,symbol,pname; bits256 t; uint8_t mypriv[32]; - if ( txfee == 0 ) + // if ( txfee == 0 ) txfee = 1000; // smaller than normal on purpose rogue_univalue(result,"keystrokes",-1,-1); if ( params != 0 && (n= cJSON_GetArraySize(params)) == 2 && (keystrokestr= jstr(jitem(params,1),0)) != 0 ) From d0aa3c7e13479468e3d692462b7780bc7086bfdc Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 24 Mar 2019 08:03:13 -1100 Subject: [PATCH 098/787] Skip keystrokes processing unless > 5 min from last one --- src/cc/rogue/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c index f146cfb4d..f0c0d2a5c 100644 --- a/src/cc/rogue/main.c +++ b/src/cc/rogue/main.c @@ -786,10 +786,13 @@ int32_t rogue_sendrawtransaction(char *rawtx) void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) { + static uint32_t lasttime; char cmd[16384],hexstr[16384],params[32768],*retstr,*rawtx,*pastkeys,*pastcmp,*keys; int32_t i,len,numpastkeys; cJSON *retjson,*resobj; //fprintf(stderr,"rogue_progress num.%d\n",num); if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) { + if ( waitflag == 0 && time(NULL) < lasttime+300 ) + return; if ( rs->keystrokeshex != 0 ) { if ( rogue_sendrawtransaction(rs->keystrokeshex) == 0 ) @@ -884,6 +887,7 @@ void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char * free(rs->keystrokeshex), rs->keystrokeshex = 0; } } + lasttime = (uint32_t)time(NULL); } } From 219c6c4239f209fa0f1a559d7a9e568611adf66d Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 24 Mar 2019 19:56:21 -1100 Subject: [PATCH 099/787] Revert delay in keystrokes --- src/cc/rogue/main.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c index f0c0d2a5c..f146cfb4d 100644 --- a/src/cc/rogue/main.c +++ b/src/cc/rogue/main.c @@ -786,13 +786,10 @@ int32_t rogue_sendrawtransaction(char *rawtx) void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) { - static uint32_t lasttime; char cmd[16384],hexstr[16384],params[32768],*retstr,*rawtx,*pastkeys,*pastcmp,*keys; int32_t i,len,numpastkeys; cJSON *retjson,*resobj; //fprintf(stderr,"rogue_progress num.%d\n",num); if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) { - if ( waitflag == 0 && time(NULL) < lasttime+300 ) - return; if ( rs->keystrokeshex != 0 ) { if ( rogue_sendrawtransaction(rs->keystrokeshex) == 0 ) @@ -887,7 +884,6 @@ void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char * free(rs->keystrokeshex), rs->keystrokeshex = 0; } } - lasttime = (uint32_t)time(NULL); } } From 867962c1e200a5bdcf3f963d4198e8e8955d2cb6 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Mon, 25 Mar 2019 14:57:56 +0800 Subject: [PATCH 100/787] add vin check for op_ret type funding. --- src/cc/payments.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 7e2db0381..1828e5ca2 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -239,7 +239,8 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // Check vouts go to the right place and pay the right amounts. int64_t amount = 0, checkamount; int32_t n = 0; checkamount = tx.GetValueOut() - change - PAYMENTS_TXFEE; - for (i = 1; i < (fHasOpret ? tx.vout.size()-2 : tx.vout.size()-1); i++) { + for (i = 1; i < (fHasOpret ? tx.vout.size()-2 : tx.vout.size()-1); i++) + { std::string destscriptPubKey = HexStr(scriptPubKeys[n].begin(),scriptPubKeys[n].end()); std::string voutscriptPubKey = HexStr(tx.vout[i].scriptPubKey.begin(),tx.vout[i].scriptPubKey.end()); if ( destscriptPubKey != voutscriptPubKey ) @@ -279,9 +280,13 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey); if ( strcmp(destaddr,coinaddr) != 0 ) { - fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s vout.%i\n", i, txin.GetHash().ToString().c_str(), vin.prevout.n); - return(eval->Invalid("vin is not paymentsCC type")); - } + std::vector scriptPubKey,opret; CTransaction tx0; uint256 checktxid; + if ( txin.vout.size() < 2 || DecodePaymentsFundOpRet(txin.vout[txin.vout.size()-1].scriptPubKey,checktxid) != 'F' || checktxid != createtxid ) + { + fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s\n", i, txin.GetHash().ToString().c_str()); + return(eval->Invalid("vin is not paymentsCC type")); + } else fprintf(stderr, "vin.%i opret type txid.%s\n", i, txin.GetHash().ToString().c_str()); + } // check the chain depth vs locked blcoks requirement. CBlockIndex* pblockindex = mapBlockIndex[blockhash]; if ( pblockindex->GetHeight() > chainActive.LastTip()->GetHeight()-lockedblocks ) From 82b078033164860fb727e1cb8693d8580540e791 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Mon, 25 Mar 2019 17:47:55 +0800 Subject: [PATCH 101/787] clean up code --- src/cc/payments.cpp | 51 ++++++++++++++++++--------------------------- src/main.cpp | 2 +- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 1828e5ca2..8dec016b1 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -166,20 +166,14 @@ void pub2createtxid(char *str) bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - char temp[128], coinaddr[64], txidaddr[64]; - std::string scriptpubkey; - uint256 createtxid; - uint256 blockhash; - CTransaction tmptx; - int32_t lockedblocks,minrelease,totalallocations; - std::vector txidoprets; - int32_t i; bool fHasOpret = false; - CPubKey txidpk,Paymentspk; - int64_t change; - - //the nValue 0 vout at the end of the tx (last one if no opret) - //it is a pay to pubkey vout - //the "pubkey" is just 0x02 + // one of two addresses + // change must go to 1of2 txidaddr + // change is/must be in vout[0] + // only 'F' or 1of2 txidaddr can be spent + // all vouts must match exactly + char temp[128], coinaddr[64], txidaddr[64]; std::string scriptpubkey; uint256 createtxid, blockhash; CTransaction tmptx; + int32_t i,lockedblocks,minrelease,totalallocations; int64_t change; std::vector txidoprets; bool fHasOpret = false; CPubKey txidpk,Paymentspk; + // user marker vout to get the createtxid if ( tx.vout.size() < 2 ) return(eval->Invalid("not enough vouts")); if ( tx.vout.back().scriptPubKey[0] == OP_RETURN ) @@ -203,9 +197,9 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //fprintf(stderr, "lockedblocks.%i minrelease.%i totalallocations.%i txidopret1.%s txidopret2.%s\n",lockedblocks, minrelease, totalallocations, txidoprets[0].ToString().c_str(), txidoprets[1].ToString().c_str() ); // Get all the script pubkeys and allocations - std::vector allocations; + std::vector allocations; std::vector scriptPubKeys; - int32_t checkallocations = 0; + int64_t checkallocations = 0; i = 0; BOOST_FOREACH(const uint256& txidopret, txidoprets) { @@ -224,7 +218,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & if ( allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size() ) return(eval->Invalid("missing data cannot validate")); - //fprintf(stderr, "totalallocations.%i checkallocations.%i\n",totalallocations, checkallocations); + //fprintf(stderr, "totalallocations.%li checkallocations.%li\n",totalallocations, checkallocations); if ( totalallocations != checkallocations ) return(eval->Invalid("allocation missmatch")); @@ -254,7 +248,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & if ( test != tx.vout[i].nValue ) { fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); - //return(eval->Invalid("amounts do not match")); + return(eval->Invalid("amounts do not match")); } amount += tx.vout[i].nValue; n++; @@ -266,10 +260,11 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & if ( amount < minrelease*COIN ) { fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amount, (int64_t)minrelease*COIN ); - //return(eval->Invalid("amount is too small")); + return(eval->Invalid("amount is too small")); } - i = 0; + i = 0; + int32_t ht = chainActive.LastTip()->GetHeight(); BOOST_FOREACH(const CTxIn& vin, tx.vin) { CTransaction txin; @@ -280,31 +275,25 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey); if ( strcmp(destaddr,coinaddr) != 0 ) { - std::vector scriptPubKey,opret; CTransaction tx0; uint256 checktxid; + std::vector scriptPubKey,opret; uint256 checktxid; if ( txin.vout.size() < 2 || DecodePaymentsFundOpRet(txin.vout[txin.vout.size()-1].scriptPubKey,checktxid) != 'F' || checktxid != createtxid ) { fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s\n", i, txin.GetHash().ToString().c_str()); return(eval->Invalid("vin is not paymentsCC type")); - } else fprintf(stderr, "vin.%i opret type txid.%s\n", i, txin.GetHash().ToString().c_str()); + } //else fprintf(stderr, "vin.%i opret type txid.%s\n", i, txin.GetHash().ToString().c_str()); } // check the chain depth vs locked blcoks requirement. CBlockIndex* pblockindex = mapBlockIndex[blockhash]; - if ( pblockindex->GetHeight() > chainActive.LastTip()->GetHeight()-lockedblocks ) + if ( pblockindex->GetHeight() > ht-lockedblocks ) { - fprintf(stderr, "vin.%i is not elegible to be spent yet height.%i vs elegible_ht.%i\n", i, pblockindex->GetHeight(), chainActive.LastTip()->GetHeight()-lockedblocks); + fprintf(stderr, "vin.%i is not elegible to be spent yet height.%i vs elegible_ht.%i\n", i, pblockindex->GetHeight(), ht-lockedblocks); return(eval->Invalid("vin not elegible")); } - //fprintf(stderr, "vin txid.%s\n", txin.GetHash().GetHex().c_str()); } else return(eval->Invalid("cant get vin transaction")); i++; } } else return(eval->Invalid("create transaction cannot decode")); - } else fprintf(stderr, "cannot get contract txn\n");//return(eval->Invalid("Could not get contract transaction")); - // one of two addresses? only seems to ever use the 1! - // change must go to 1of2 txidaddr - // change is/must be in vout[0] - // only 'F' or 1of2 txidaddr can be spent - // all vouts must match exactly + } else return(eval->Invalid("Could not get contract transaction")); return(true); } // end of consensus code diff --git a/src/main.cpp b/src/main.cpp index 9930e733c..e520ad83f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7045,7 +7045,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, } std::vector payload; vRecv >> payload; - //komodo_netevent(payload); + komodo_netevent(payload); return(true); } else if (strCommand == "verack") From 5a1c07c477ecbc2fe54a2ee64e3a1f0895a80e34 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 24 Mar 2019 23:31:14 -1100 Subject: [PATCH 102/787] +print --- src/komodo_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 079ce1eea..c1f488c5c 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -2079,7 +2079,7 @@ void komodo_args(char *argv0) while ( (dirname= (char *)GetDataDir(false).string().c_str()) == 0 || dirname[0] == 0 ) { - fprintf(stderr,"waiting for datadir\n"); + fprintf(stderr,"waiting for datadir (%s)\n",dirname); #ifndef _WIN32 sleep(3); #else From 3c9b45bf0a38b82da505701692f3b24f3fe4d62c Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Mon, 25 Mar 2019 18:32:19 +0800 Subject: [PATCH 103/787] change allocations to int64 --- src/cc/payments.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 8dec016b1..0190bf198 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -77,14 +77,14 @@ // start of consensus code -CScript EncodePaymentsTxidOpRet(int32_t allocation,std::vector scriptPubKey,std::vector destopret) +CScript EncodePaymentsTxidOpRet(int64_t allocation,std::vector scriptPubKey,std::vector destopret) { CScript opret; uint8_t evalcode = EVAL_PAYMENTS; opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'T' << allocation << scriptPubKey << destopret); return(opret); } -uint8_t DecodePaymentsTxidOpRet(CScript scriptPubKey,int32_t &allocation,std::vector &destscriptPubKey,std::vector &destopret) +uint8_t DecodePaymentsTxidOpRet(CScript scriptPubKey,int64_t &allocation,std::vector &destscriptPubKey,std::vector &destopret) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); @@ -117,14 +117,14 @@ uint8_t DecodePaymentsFundOpRet(CScript scriptPubKey,uint256 &checktxid) return(0); } -CScript EncodePaymentsOpRet(int32_t lockedblocks,int32_t minrelease,int32_t totalallocations,std::vector txidoprets) +CScript EncodePaymentsOpRet(int32_t lockedblocks,int32_t minrelease,int64_t totalallocations,std::vector txidoprets) { CScript opret; uint8_t evalcode = EVAL_PAYMENTS; opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'C' << lockedblocks << minrelease << totalallocations << txidoprets); return(opret); } -uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &totalallocations,std::vector &txidoprets) +uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int64_t &totalallocations,std::vector &txidoprets) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); @@ -172,7 +172,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & // only 'F' or 1of2 txidaddr can be spent // all vouts must match exactly char temp[128], coinaddr[64], txidaddr[64]; std::string scriptpubkey; uint256 createtxid, blockhash; CTransaction tmptx; - int32_t i,lockedblocks,minrelease,totalallocations; int64_t change; std::vector txidoprets; bool fHasOpret = false; CPubKey txidpk,Paymentspk; + int32_t i,lockedblocks,minrelease; int64_t change,totalallocations; std::vector txidoprets; bool fHasOpret = false; CPubKey txidpk,Paymentspk; // user marker vout to get the createtxid if ( tx.vout.size() < 2 ) return(eval->Invalid("not enough vouts")); @@ -203,12 +203,12 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & i = 0; BOOST_FOREACH(const uint256& txidopret, txidoprets) { - CTransaction tx0; std::vector scriptPubKey,opret; int32_t allocation; + CTransaction tx0; std::vector scriptPubKey,opret; int64_t allocation; if ( myGetTransaction(txidopret,tx0,blockhash) != 0 && tx0.vout.size() > 1 && DecodePaymentsTxidOpRet(tx0.vout[tx0.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { scriptPubKeys.push_back(CScript(scriptPubKey.begin(), scriptPubKey.end())); allocations.push_back(allocation); - //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%i\n",i,scriptPubKeys[i].ToString().c_str(),allocation); + //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%li\n",i,scriptPubKeys[i].ToString().c_str(),allocation); checkallocations += allocation; } i++; @@ -435,7 +435,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { int32_t latestheight,nextheight = komodo_nextheight(); CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease,totalallocations,checkallocations=0,allocation; int64_t newamount,inputsum,amount,CCchange=0; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease; int64_t newamount,inputsum,amount,CCchange=0,totalallocations,checkallocations=0,allocation; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); From 55a5f398aaac8319453122dbe2b63e10607afaf6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 00:38:27 -1100 Subject: [PATCH 104/787] Sign event payload --- src/cc/gamescc.cpp | 69 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 89efcc6d2..7628db500 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -45,6 +45,16 @@ uint8_t games_opretdecode(CPubKey &pk,CScript scriptPubKey) return(0); } +uint8_t games_eventdecode(CPubKey &pk,std::vector &sig,std::vector &payload,std::vector message) +{ + uint8_t e,f; + if ( message.size() > 2 && E_UNMARSHAL(message,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) + { + return(f); + } + return(0); +} + UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) { CTransaction tx; @@ -154,15 +164,49 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } +int32_t games_eventsign(std::vector &sig,std::vector payload,CPubKey pk) +{ + bool signSuccess; SignatureData sigdata; uint256 hash; uint8_t *ptr; auto consensusBranchId = 0; + const CKeyStore& keystore = *pwalletMain; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].prevout.hash = payload.GetHash(); + txNew.vin[0].prevout.n = 0; + txNew.vout[0].scriptPubKey = CScript() << payload << OP_DROP << ParseHex(HexString(pk)) << OP_CHECKSIG; + txNew.vout[0].nValue = (int32_t)payload.size(); + txNew.nLockTime = 0; + CTransaction txNewConst(txNew); + signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, *utxovaluep, SIGHASH_ALL), txNew.vout[0].scriptPubKey, sigdata, consensusBranchId); + if ( signSuccess > 0 ) + { + UpdateTransaction(txNew,0,sigdata); + ptr = (uint8_t *)&sigdata.scriptSig[0]; + siglen = sigdata.scriptSig.size(); + sig.resize(siglen); + for (i=0; i payload; int32_t n; + UniValue result(UniValue::VOBJ); std::vector sig,payload; int32_t n; CPubKey mypk; uint256 hash; if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) { if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) { - komodo_sendmessage(4,8,"events",payload); - result.push_back(Pair("result","success")); + mypk = pubkey2pk(Mypubkey()); + if ( games_eventsign(sig,payload,mypk) == 0 ) + { + komodo_sendmessage(4,8,"events",E_MARSHAL(ss << EVAL_GAMES << 'E' << mypk << sig << payload)); + result.push_back(Pair("result","success")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","signing ereror")); + } } else { @@ -178,12 +222,21 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -void komodo_netevent(std::vector payload) +void komodo_netevent(std::vector message) { - int32_t i; - for (i=0; i sig,payload; char str[67]; + if ( games_eventdecode(pk,sig,payload,message) == 'E' ) + { + for (i=0; i Date: Mon, 25 Mar 2019 00:45:04 -1100 Subject: [PATCH 105/787] Syntax --- src/cc/gamescc.cpp | 8 ++++---- src/cc/gamescc.h | 1 + src/cc/payments.cpp | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 7628db500..1a7ce0257 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -166,17 +166,17 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) int32_t games_eventsign(std::vector &sig,std::vector payload,CPubKey pk) { - bool signSuccess; SignatureData sigdata; uint256 hash; uint8_t *ptr; auto consensusBranchId = 0; + bool signSuccess; SignatureData sigdata; int32_t i,siglen; uint256 hash; uint8_t *ptr; auto consensusBranchId = 0; CMutableTransaction txNew; const CKeyStore& keystore = *pwalletMain; txNew.vin.resize(1); txNew.vout.resize(1); txNew.vin[0].prevout.hash = payload.GetHash(); txNew.vin[0].prevout.n = 0; - txNew.vout[0].scriptPubKey = CScript() << payload << OP_DROP << ParseHex(HexString(pk)) << OP_CHECKSIG; - txNew.vout[0].nValue = (int32_t)payload.size(); + txNew.vout[0].scriptPubKey = CScript() << payload << OP_DROP << ParseHex(HexStr(pk)) << OP_CHECKSIG; + txNew.vout[0].nValue = payload.size(); txNew.nLockTime = 0; CTransaction txNewConst(txNew); - signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, *utxovaluep, SIGHASH_ALL), txNew.vout[0].scriptPubKey, sigdata, consensusBranchId); + signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, payload.size(), SIGHASH_ALL), txNew.vout[0].scriptPubKey, sigdata, consensusBranchId); if ( signSuccess > 0 ) { UpdateTransaction(txNew,0,sigdata); diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 02f589056..6e88b9e32 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -1,3 +1,4 @@ +#include "CCinclud.h" std::string MYCCLIBNAME = (char *)"gamescc"; diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 0190bf198..75c1431f0 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -569,7 +569,7 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); - CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,lockedblocks,minrelease,totalallocations; std::vector txidoprets; + CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount,totalallocations; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,lockedblocks,minrelease; std::vector txidoprets; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); @@ -662,7 +662,7 @@ UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,lockedblocks,minrelease,totalallocations=0; std::string rawtx; + UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,lockedblocks,minrelease; std::string rawtx; int64_t totalallocations = 0; cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n >= 4 ) { From 5a6c3fa7e71c922407ff77d7e35fbcd01915a3bc Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 00:45:37 -1100 Subject: [PATCH 106/787] E --- src/cc/gamescc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 6e88b9e32..143ecc50a 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -1,4 +1,4 @@ -#include "CCinclud.h" +#include "CCinclude.h" std::string MYCCLIBNAME = (char *)"gamescc"; From aa1c34fbf488d065593c2703ea065ff6c07d79fb Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 00:49:17 -1100 Subject: [PATCH 107/787] Syntax --- src/cc/gamescc.cpp | 2 +- src/cc/gamescc.h | 1 + src/cc/payments.cpp | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 1a7ce0257..7aa7c46da 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -229,7 +229,7 @@ void komodo_netevent(std::vector message) { for (i=0; i scriptPubKey,opret; int32_t allocation; + std::vector scriptPubKey,opret; int64_t allocation; if ( myGetTransaction(txidoprets[i],tx,hashBlock) != 0 && tx.vout.size() > 1 && DecodePaymentsTxidOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { totalallocations += allocation; @@ -732,7 +732,7 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,allocation,numoprets=0,lockedblocks,minrelease,totalallocations; std::vector txidoprets; int64_t funds,fundsopret; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,numoprets=0,lockedblocks,minrelease; std::vector txidoprets; int64_t funds,fundsopret,totalallocations=0,allocation; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n == 1 ) { From 2f26df49238a89979a9de18d3cd8f7e8261ff67d Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 00:52:36 -1100 Subject: [PATCH 108/787] Test --- src/cc/gamescc.cpp | 3 ++- src/cc/gamescc.h | 5 +++++ src/cc/payments.cpp | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 7aa7c46da..c56df410b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1,3 +1,4 @@ +#include "gamescc.h" /* ./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\" @@ -229,7 +230,7 @@ void komodo_netevent(std::vector message) { for (i=0; ievalcode == EVAL_GAMES ) \ return(result); \ } \ } + +#endif diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 3ee592205..184c92d33 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -819,7 +819,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { std::vector > addressIndex; uint256 txid,hashBlock; - UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease,totalallocations; std::vector txidoprets; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease; std::vector txidoprets; int64_t totalallocations; Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); SetCCtxids(addressIndex,markeraddr); From 807ae7314a72e2b864d62839aa35d7f4a31f9409 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 00:54:40 -1100 Subject: [PATCH 109/787] Test --- src/cc/gamescc.cpp | 2 +- src/cc/gamescc.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index c56df410b..2163d46c5 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -230,7 +230,7 @@ void komodo_netevent(std::vector message) { for (i=0; i Date: Mon, 25 Mar 2019 12:56:01 +0100 Subject: [PATCH 110/787] update rpc-cc scripts for address keys --- qa/rpc-tests/cryptoconditions.py | 690 ---------------------- qa/rpc-tests/cryptoconditions_channels.py | 11 +- qa/rpc-tests/cryptoconditions_dice.py | 13 +- qa/rpc-tests/cryptoconditions_faucet.py | 12 +- qa/rpc-tests/cryptoconditions_gateways.py | 6 +- qa/rpc-tests/cryptoconditions_heir.py | 12 +- qa/rpc-tests/cryptoconditions_oracles.py | 12 +- qa/rpc-tests/cryptoconditions_rewards.py | 12 +- qa/rpc-tests/cryptoconditions_token.py | 20 +- 9 files changed, 63 insertions(+), 725 deletions(-) delete mode 100755 qa/rpc-tests/cryptoconditions.py diff --git a/qa/rpc-tests/cryptoconditions.py b/qa/rpc-tests/cryptoconditions.py deleted file mode 100755 index d5456e801..000000000 --- a/qa/rpc-tests/cryptoconditions.py +++ /dev/null @@ -1,690 +0,0 @@ -#!/usr/bin/env python2 -# Copyright (c) 2018 SuperNET developers -# Distributed under the MIT software license, see the accompanying -# file COPYING or http://www.opensource.org/licenses/mit-license.php. - -from test_framework.test_framework import BitcoinTestFramework -from test_framework.authproxy import JSONRPCException -from test_framework.util import assert_equal, assert_greater_than, \ - initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ - stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises - -import time -from decimal import Decimal -from random import choice -from string import ascii_uppercase - -def assert_success(result): - assert_equal(result['result'], 'success') - -def assert_error(result): - assert_equal(result['result'], 'error') - -def generate_random_string(length): - random_string = ''.join(choice(ascii_uppercase) for i in range(length)) - return random_string - -class CryptoConditionsTest (BitcoinTestFramework): - - def setup_chain(self): - print("Initializing CC test directory "+self.options.tmpdir) - self.num_nodes = 2 - initialize_chain_clean(self.options.tmpdir, self.num_nodes) - - def setup_network(self, split = False): - print("Setting up network...") - self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" - self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" - self.privkey = "UqMgxk7ySPNQ4r9nKAFPjkXy6r5t898yhuNCjSZJLg3RAM4WW1m9" - self.addr1 = "RXEXoa1nRmKhMbuZovpcYwQMsicwzccZBp" - self.pubkey1 = "024026d4ad4ecfc1f705a9b42ca64af6d2ad947509c085534a30b8861d756c6ff0" - self.privkey1 = "UtdydP56pGTFmawHzHr1wDrc4oUwCNW1ttX8Pc3KrvH3MA8P49Wi" - self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, - extra_args=[[ - # always give -ac_name as first extra_arg and port as third - '-ac_name=REGTEST', - '-conf='+self.options.tmpdir+'/node0/REGTEST.conf', - '-port=64367', - '-rpcport=64368', - '-regtest', - '-addressindex=1', - '-spentindex=1', - '-ac_supply=5555555', - '-ac_reward=10000000000000', - '-pubkey=' + self.pubkey, - '-ac_cc=2', - '-whitelist=127.0.0.1', - '-debug', - '--daemon', - '-rpcuser=rt', - '-rpcpassword=rt' - ], - ['-ac_name=REGTEST', - '-conf='+self.options.tmpdir+'/node1/REGTEST.conf', - '-port=64365', - '-rpcport=64366', - '-regtest', - '-addressindex=1', - '-spentindex=1', - '-ac_supply=5555555', - '-ac_reward=10000000000000', - '-pubkey=' + self.pubkey1, - '-ac_cc=2', - '-whitelist=127.0.0.1', - '-debug', - '-addnode=127.0.0.1:64367', - '--daemon', - '-rpcuser=rt', - '-rpcpassword=rt']] - ) - self.is_network_split = split - self.rpc = self.nodes[0] - self.rpc1 = self.nodes[1] - self.sync_all() - print("Done setting up network") - - def send_and_mine(self, xtn, rpc_connection): - txid = rpc_connection.sendrawtransaction(xtn) - assert txid, 'got txid' - # we need the tx above to be confirmed in the next block - rpc_connection.generate(1) - return txid - - def run_faucet_tests(self): - rpc = self.rpc - rpc1 = self.rpc1 - - # basic sanity tests - result = rpc.getwalletinfo() - assert_greater_than(result['txcount'], 100) - assert_greater_than(result['balance'], 0.0) - balance = result['balance'] - - faucet = rpc.faucetaddress() - assert_equal(faucet['result'], 'success') - # verify all keys look like valid AC addrs, could be better - for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: - assert_equal(faucet[x][0], 'R') - - result = rpc.faucetaddress(self.pubkey) - assert_success(result) - # test that additional CCaddress key is returned - for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: - assert_equal(result[x][0], 'R') - - # no funds in the faucet yet - result = rpc.faucetget() - assert_error(result) - - result = rpc.faucetinfo() - assert_success(result) - - result = rpc.faucetfund("0") - assert_error(result) - - result = rpc.faucetfund("-1") - assert_error(result) - - # we need at least 1 + txfee to get - result = rpc.faucetfund("2") - assert_success(result) - assert result['hex'], "hex key found" - - # broadcast the xtn - result = rpc.sendrawtransaction(result['hex']) - txid = result[0] - assert txid, "found txid" - - # we need the tx above to be confirmed in the next block - rpc.generate(1) - self.sync_all() - - result = rpc.getwalletinfo() - # minus one block reward - balance2 = result['balance'] - 100000 - # make sure our balance is less now - assert_greater_than(balance, balance2) - - result = rpc.faucetinfo() - assert_success(result) - assert_greater_than( result['funding'], 0 ) - - # claiming faucet on second node - faucetgethex = rpc1.faucetget() - assert_success(faucetgethex) - assert faucetgethex['hex'], "hex key found" - - balance1 = rpc1.getwalletinfo()['balance'] - - # try to broadcast the faucetget transaction - result = self.send_and_mine(faucetgethex['hex'], rpc1) - assert txid, "transaction broadcasted" - - balance2 = rpc1.getwalletinfo()['balance'] - assert_greater_than(balance2, balance1) - - self.sync_all() - - def run_dice_tests(self): - rpc = self.nodes[0] - rpc1 = self.nodes[1] - self.sync_all() - - # have to generate few blocks on second node to be able to place bets - rpc1.generate(10) - result = rpc1.getbalance() - assert_greater_than(result, 100000) - - dice = rpc.diceaddress() - assert_equal(dice['result'], 'success') - for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: - assert_equal(dice[x][0], 'R') - - dice = rpc.diceaddress(self.pubkey) - assert_equal(dice['result'], 'success') - for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: - assert_equal(dice[x][0], 'R') - - # no dice created yet - result = rpc.dicelist() - assert_equal(result, []) - - # creating dice plan with too long name (>8 chars) - result = rpc.dicefund("THISISTOOLONG", "10000", "10", "10000", "10", "5") - assert_error(result) - - # creating dice plan with < 100 funding - result = rpc.dicefund("LUCKY","10","1","10000","10","5") - assert_error(result) - - # creating dice plan with 0 blocks timeout - result = rpc.dicefund("LUCKY","10","1","10000","10","0") - assert_error(result) - - # creating dice plan - dicefundtx = rpc.dicefund("LUCKY","1000","1","800","10","5") - diceid = self.send_and_mine(dicefundtx['hex'], rpc) - - # checking if it in plans list now - result = rpc.dicelist() - assert_equal(result[0], diceid) - - # set dice name for futher usage - dicename = "LUCKY" - - # adding zero funds to plan - result = rpc.diceaddfunds(dicename,diceid,"0") - assert_error(result) - - # adding negative funds to plan - result = rpc.diceaddfunds(dicename,diceid,"-1") - assert_error(result) - - # adding funds to plan - addfundstx = rpc.diceaddfunds(dicename,diceid,"1100") - result = self.send_and_mine(addfundstx['hex'], rpc) - - # checking if funds added to plan - result = rpc.diceinfo(diceid) - assert_equal(result["funding"], "2100.00000000") - - # not valid dice info checking - result = rpc.diceinfo("invalid") - assert_error(result) - - # placing 0 amount bet - result = rpc1.dicebet(dicename,diceid,"0","2") - assert_error(result) - - # placing negative amount bet - result = rpc1.dicebet(dicename,diceid,"-1","2") - assert_error(result) - - # placing bet more than maxbet - result = rpc1.dicebet(dicename,diceid,"900","2") - assert_error(result) - - # placing bet with amount more than funding - result = rpc1.dicebet(dicename,diceid,"3000","2") - assert_error(result) - - # placing bet with potential won more than funding - result = rpc1.dicebet(dicename,diceid,"750","9") - assert_error(result) - - # placing 0 odds bet - result = rpc1.dicebet(dicename,diceid,"1","0") - assert_error(result) - - # placing negative odds bet - result = rpc1.dicebet(dicename,diceid,"1","-1") - assert_error(result) - - # placing bet with odds more than allowed - result = rpc1.dicebet(dicename,diceid,"1","11") - assert_error(result) - - # placing bet with not correct dice name - result = rpc1.dicebet("nope",diceid,"100","2") - assert_error(result) - - # placing bet with not correct dice id - result = rpc1.dicebet(dicename,self.pubkey,"100","2") - assert_error(result) - - # have to make some entropy for the next test - entropytx = 0 - fundingsum = 1 - while entropytx < 110: - fundingsuminput = str(fundingsum) - fundinghex = rpc.diceaddfunds(dicename,diceid,fundingsuminput) - result = self.send_and_mine(fundinghex['hex'], rpc) - entropytx = entropytx + 1 - fundingsum = fundingsum + 1 - - rpc.generate(2) - self.sync_all() - - # valid bet placing - placebet = rpc1.dicebet(dicename,diceid,"100","2") - betid = self.send_and_mine(placebet["hex"], rpc1) - assert result, "bet placed" - - # check bet status - result = rpc1.dicestatus(dicename,diceid,betid) - assert_success(result) - - # note initial dice funding state at this point. - # TODO: track player balance somehow (hard to do because of mining and fees) - diceinfo = rpc.diceinfo(diceid) - funding = float(diceinfo['funding']) - - # # placing same amount bets with amount 1 and odds 1:3, checking if balance changed correct - # losscounter = 0 - # wincounter = 0 - # betcounter = 0 - # - # while (betcounter < 10): - # placebet = rpc1.dicebet(dicename,diceid,"1","2") - # betid = self.send_and_mine(placebet["hex"], rpc1) - # time.sleep(3) - # self.sync_all() - # finish = rpc.dicefinish(dicename,diceid,betid) - # self.send_and_mine(finish["hex"], rpc1) - # self.sync_all() - # time.sleep(3) - # betresult = rpc1.dicestatus(dicename,diceid,betid) - # betcounter = betcounter + 1 - # if betresult["status"] == "loss": - # losscounter = losscounter + 1 - # elif betresult["status"] == "win": - # wincounter = wincounter + 1 - # else: - # pass - # - # # funding balance should increase if player loss, decrease if player won - # fundbalanceguess = funding + losscounter - wincounter * 2 - # fundinfoactual = rpc.diceinfo(diceid) - # assert_equal(round(fundbalanceguess),round(float(fundinfoactual['funding']))) - - def run_token_tests(self): - rpc = self.nodes[0] - result = rpc.tokenaddress() - assert_success(result) - for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: - assert_equal(result[x][0], 'R') - - result = rpc.tokenaddress(self.pubkey) - assert_success(result) - for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: - assert_equal(result[x][0], 'R') - # there are no tokens created yet - result = rpc.tokenlist() - assert_equal(result, []) - - # trying to create token with negaive supply - result = rpc.tokencreate("NUKE", "-1987420", "no bueno supply") - assert_error(result) - - # creating token with name more than 32 chars - result = rpc.tokencreate("NUKE123456789012345678901234567890", "1987420", "name too long") - assert_error(result) - - # creating valid token - result = rpc.tokencreate("DUKE", "1987.420", "Duke's custom token") - assert_success(result) - - tokenid = self.send_and_mine(result['hex'], rpc) - - result = rpc.tokenlist() - assert_equal(result[0], tokenid) - - # get token balance for token with pubkey - result = rpc.tokenbalance(tokenid, self.pubkey) - assert_success(result) - assert_equal(result['balance'], 198742000000) - assert_equal(result['tokenid'], tokenid) - - # get token balance for token without pubkey - result = rpc.tokenbalance(tokenid) - assert_success(result) - assert_equal(result['balance'], 198742000000) - assert_equal(result['tokenid'], tokenid) - - # this is not a valid assetid - result = rpc.tokeninfo(self.pubkey) - assert_error(result) - - # check tokeninfo for valid token - result = rpc.tokeninfo(tokenid) - assert_success(result) - assert_equal(result['tokenid'], tokenid) - assert_equal(result['owner'], self.pubkey) - assert_equal(result['name'], "DUKE") - assert_equal(result['supply'], 198742000000) - assert_equal(result['description'], "Duke's custom token") - - # invalid numtokens ask - result = rpc.tokenask("-1", tokenid, "1") - assert_error(result) - - # invalid numtokens ask - result = rpc.tokenask("0", tokenid, "1") - assert_error(result) - - # invalid price ask - result = rpc.tokenask("1", tokenid, "-1") - assert_error(result) - - # invalid price ask - result = rpc.tokenask("1", tokenid, "0") - assert_error(result) - - # invalid tokenid ask - result = rpc.tokenask("100", "deadbeef", "1") - assert_error(result) - - # valid ask - tokenask = rpc.tokenask("100", tokenid, "7.77") - tokenaskhex = tokenask['hex'] - tokenaskid = self.send_and_mine(tokenask['hex'], rpc) - result = rpc.tokenorders(tokenid) - order = result[0] - assert order, "found order" - - # invalid ask fillunits - result = rpc.tokenfillask(tokenid, tokenaskid, "0") - assert_error(result) - - # invalid ask fillunits - result = rpc.tokenfillask(tokenid, tokenaskid, "-777") - assert_error(result) - - # valid ask fillunits - fillask = rpc.tokenfillask(tokenid, tokenaskid, "777") - result = self.send_and_mine(fillask['hex'], rpc) - txid = result[0] - assert txid, "found txid" - - # should be no token orders - result = rpc.tokenorders(tokenid) - assert_equal(result, []) - - # checking ask cancellation - testorder = rpc.tokenask("100", tokenid, "7.77") - testorderid = self.send_and_mine(testorder['hex'], rpc) - cancel = rpc.tokencancelask(tokenid, testorderid) - self.send_and_mine(cancel["hex"], rpc) - result = rpc.tokenorders(tokenid) - assert_equal(result, []) - - # invalid numtokens bid - result = rpc.tokenbid("-1", tokenid, "1") - assert_error(result) - - # invalid numtokens bid - result = rpc.tokenbid("0", tokenid, "1") - assert_error(result) - - # invalid price bid - result = rpc.tokenbid("1", tokenid, "-1") - assert_error(result) - - # invalid price bid - result = rpc.tokenbid("1", tokenid, "0") - assert_error(result) - - # invalid tokenid bid - result = rpc.tokenbid("100", "deadbeef", "1") - assert_error(result) - - tokenbid = rpc.tokenbid("100", tokenid, "10") - tokenbidhex = tokenbid['hex'] - tokenbidid = self.send_and_mine(tokenbid['hex'], rpc) - result = rpc.tokenorders(tokenid) - order = result[0] - assert order, "found order" - - # invalid bid fillunits - result = rpc.tokenfillbid(tokenid, tokenbidid, "0") - assert_error(result) - - # invalid bid fillunits - result = rpc.tokenfillbid(tokenid, tokenbidid, "-777") - assert_error(result) - - # valid bid fillunits - fillbid = rpc.tokenfillbid(tokenid, tokenbidid, "1000") - result = self.send_and_mine(fillbid['hex'], rpc) - txid = result[0] - assert txid, "found txid" - - # should be no token orders - result = rpc.tokenorders(tokenid) - assert_equal(result, []) - - # checking bid cancellation - testorder = rpc.tokenbid("100", tokenid, "7.77") - testorderid = self.send_and_mine(testorder['hex'], rpc) - cancel = rpc.tokencancelbid(tokenid, testorderid) - self.send_and_mine(cancel["hex"], rpc) - result = rpc.tokenorders(tokenid) - assert_equal(result, []) - - # invalid token transfer amount (have to add status to CC code!) - randompubkey = "021a559101e355c907d9c553671044d619769a6e71d624f68bfec7d0afa6bd6a96" - result = rpc.tokentransfer(tokenid,randompubkey,"0") - assert_error(result) - - # invalid token transfer amount (have to add status to CC code!) - result = rpc.tokentransfer(tokenid,randompubkey,"-1") - assert_error(result) - - # valid token transfer - sendtokens = rpc.tokentransfer(tokenid,randompubkey,"1") - self.send_and_mine(sendtokens["hex"], rpc) - result = rpc.tokenbalance(tokenid,randompubkey) - assert_equal(result["balance"], 1) - - def run_rewards_tests(self): - rpc = self.nodes[0] - result = rpc.rewardsaddress() - for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: - assert_equal(result[x][0], 'R') - - result = rpc.rewardsaddress(self.pubkey) - for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: - assert_equal(result[x][0], 'R') - - # no rewards yet - result = rpc.rewardslist() - assert_equal(result, []) - - # looking up non-existent reward should return error - result = rpc.rewardsinfo("none") - assert_error(result) - - # creating rewards plan with name > 8 chars, should return error - result = rpc.rewardscreatefunding("STUFFSTUFF", "7777", "25", "0", "10", "10") - assert_error(result) - - # creating rewards plan with 0 funding - result = rpc.rewardscreatefunding("STUFF", "0", "25", "0", "10", "10") - assert_error(result) - - # creating rewards plan with 0 maxdays - result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "0") - assert_error(result) - - # creating rewards plan with > 25% APR - result = rpc.rewardscreatefunding("STUFF", "7777", "30", "0", "10", "10") - assert_error(result) - - # creating valid rewards plan - result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") - assert result['hex'], 'got raw xtn' - fundingtxid = rpc.sendrawtransaction(result['hex']) - assert fundingtxid, 'got txid' - - # confirm the above xtn - rpc.generate(1) - result = rpc.rewardsinfo(fundingtxid) - assert_success(result) - assert_equal(result['name'], 'STUFF') - assert_equal(result['APR'], "25.00000000") - assert_equal(result['minseconds'], 0) - assert_equal(result['maxseconds'], 864000) - assert_equal(result['funding'], "7777.00000000") - assert_equal(result['mindeposit'], "10.00000000") - assert_equal(result['fundingtxid'], fundingtxid) - - # checking if new plan in rewardslist - result = rpc.rewardslist() - assert_equal(result[0], fundingtxid) - - # creating reward plan with already existing name, should return error - result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") - assert_error(result) - - # add funding amount must be positive - result = rpc.rewardsaddfunding("STUFF", fundingtxid, "-1") - assert_error(result) - - # add funding amount must be positive - result = rpc.rewardsaddfunding("STUFF", fundingtxid, "0") - assert_error(result) - - # adding valid funding - result = rpc.rewardsaddfunding("STUFF", fundingtxid, "555") - addfundingtxid = self.send_and_mine(result['hex'], rpc) - assert addfundingtxid, 'got funding txid' - - # checking if funding added to rewardsplan - result = rpc.rewardsinfo(fundingtxid) - assert_equal(result['funding'], "8332.00000000") - - # trying to lock funds, locking funds amount must be positive - result = rpc.rewardslock("STUFF", fundingtxid, "-5") - assert_error(result) - - # trying to lock funds, locking funds amount must be positive - result = rpc.rewardslock("STUFF", fundingtxid, "0") - assert_error(result) - - # trying to lock less than the min amount is an error - result = rpc.rewardslock("STUFF", fundingtxid, "7") - assert_error(result) - - # locking funds in rewards plan - result = rpc.rewardslock("STUFF", fundingtxid, "10") - assert_success(result) - locktxid = result['hex'] - assert locktxid, "got lock txid" - - # locktxid has not been broadcast yet - result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) - assert_error(result) - - # broadcast xtn - txid = rpc.sendrawtransaction(locktxid) - assert txid, 'got txid from sendrawtransaction' - - # confirm the xtn above - rpc.generate(1) - - # will not unlock since reward amount is less than tx fee - result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) - assert_error(result) - - def run_oracles_tests(self): - rpc = self.nodes[0] - rpc1 = self.nodes[1] - - result = rpc1.oraclesaddress() - - result = rpc.oraclesaddress() - assert_success(result) - - for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: - assert_equal(result[x][0], 'R') - - result = rpc.oraclesaddress(self.pubkey) - assert_success(result) - for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: - assert_equal(result[x][0], 'R') - - # there are no oracles created yet - result = rpc.oracleslist() - assert_equal(result, []) - - # looking up non-existent oracle should return error. - result = rpc.oraclesinfo("none") - assert_error(result) - - # attempt to create oracle with not valid data type should return error - result = rpc.oraclescreate("Test", "Test", "Test") - assert_error(result) - - # attempt to create oracle with description > 32 symbols should return error - too_long_name = generate_random_string(33) - result = rpc.oraclescreate(too_long_name, "Test", "s") - - - # attempt to create oracle with description > 4096 symbols should return error - too_long_description = generate_random_string(4100) - result = rpc.oraclescreate("Test", too_long_description, "s") - assert_error(result) - # # valid creating oracles of different types - # # using such naming to re-use it for data publishing / reading (e.g. oracle_s for s type) - # valid_formats = ["s", "S", "d", "D", "c", "C", "t", "T", "i", "I", "l", "L", "h", "Ihh"] - # for f in valid_formats: - # result = rpc.oraclescreate("Test", "Test", f) - # assert_success(result) - # globals()["oracle_{}".format(f)] = self.send_and_mine(result['hex'], rpc) - - def run_test (self): - print("Mining blocks...") - rpc = self.nodes[0] - rpc1 = self.nodes[1] - # utxos from block 1 become mature in block 101 - rpc.generate(101) - self.sync_all() - rpc.getinfo() - rpc1.getinfo() - # this corresponds to -pubkey above - print("Importing privkeys") - rpc.importprivkey(self.privkey) - rpc1.importprivkey(self.privkey1) - self.run_faucet_tests() - self.sync_all() - self.run_rewards_tests() - self.sync_all() - self.run_dice_tests() - self.sync_all() - self.run_token_tests() - self.sync_all() - self.run_oracles_tests() - - -if __name__ == '__main__': - CryptoConditionsTest ().main() diff --git a/qa/rpc-tests/cryptoconditions_channels.py b/qa/rpc-tests/cryptoconditions_channels.py index 71f62f49d..2248efeab 100755 --- a/qa/rpc-tests/cryptoconditions_channels.py +++ b/qa/rpc-tests/cryptoconditions_channels.py @@ -27,13 +27,14 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework): rpc1 = self.nodes[1] # checking channelsaddress call - + result = rpc.channelsaddress(self.pubkey) assert_success(result) # test that additional CCaddress key is returned - for x in ['ChannelsCC1of2TokensAddress', 'myCCAddress(Channels)', 'ChannelsCC1of2Address', 'myAddress', \ - 'myCCaddress', 'ChannelsNormalAddress', 'PubkeyCCaddress(Channels)', 'ChannelsCCAddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # getting empty channels list result = rpc.channelslist() @@ -80,7 +81,7 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework): # now in channelinfo payment information should appear result = rpc.channelsinfo(channel_txid) assert_equal(result["Transactions"][1]["Payment"], payment_tx_id) - + time.sleep(90) # number of payments should be equal 1 (one denomination used) result = rpc.channelsinfo(channel_txid)["Transactions"][1]["Number of payments"] assert_equal(result, 1) diff --git a/qa/rpc-tests/cryptoconditions_dice.py b/qa/rpc-tests/cryptoconditions_dice.py index 7b960cb67..e93260178 100755 --- a/qa/rpc-tests/cryptoconditions_dice.py +++ b/qa/rpc-tests/cryptoconditions_dice.py @@ -28,15 +28,20 @@ class CryptoconditionsDiceTest(CryptoconditionsTestFramework): for x in result.keys(): print(x+": "+str(result[x])) assert_equal(result['result'], 'success') - for x in ['myCCaddress', 'DiceCCAddress', 'myaddress']: - assert_equal(result[x][0], 'R') + + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.diceaddress(self.pubkey) for x in result.keys(): print(x+": "+str(result[x])) assert_equal(result['result'], 'success') - for x in ['myCCaddress', 'DiceCCAddress', 'myaddress', 'DiceCCTokensAddress', 'DiceNormalAddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # no dice created yet result = rpc.dicelist() diff --git a/qa/rpc-tests/cryptoconditions_faucet.py b/qa/rpc-tests/cryptoconditions_faucet.py index c02522cc5..47e81b5ed 100755 --- a/qa/rpc-tests/cryptoconditions_faucet.py +++ b/qa/rpc-tests/cryptoconditions_faucet.py @@ -29,16 +29,20 @@ class CryptoconditionsFaucetTest(CryptoconditionsTestFramework): for x in result.keys(): print(x+": "+str(result[x])) # verify all keys look like valid AC addrs, could be better - for x in ['myCCaddress', 'FaucetCCTokensAddress', 'FaucetNormalAddress', 'myaddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.faucetaddress(self.pubkey) assert_success(result) for x in result.keys(): print(x+": "+str(result[x])) # test that additional CCaddress key is returned - for x in ['myCCaddress', 'FaucetCCTokensAddress', 'FaucetNormalAddress', 'myaddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # no funds in the faucet yet result = rpc.faucetget() diff --git a/qa/rpc-tests/cryptoconditions_gateways.py b/qa/rpc-tests/cryptoconditions_gateways.py index a7f0cad2b..b12ea9f7e 100755 --- a/qa/rpc-tests/cryptoconditions_gateways.py +++ b/qa/rpc-tests/cryptoconditions_gateways.py @@ -20,8 +20,10 @@ class CryptoconditionsGatewaysTest(CryptoconditionsTestFramework): result = rpc.gatewaysaddress() assert_success(result) - for x in ['GatewaysCCaddress', 'myCCaddress', 'Gatewaysmarker', 'myaddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') assert_equal("03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40", result["GatewaysPubkey"]) diff --git a/qa/rpc-tests/cryptoconditions_heir.py b/qa/rpc-tests/cryptoconditions_heir.py index a2443f0b3..12ca8b3da 100755 --- a/qa/rpc-tests/cryptoconditions_heir.py +++ b/qa/rpc-tests/cryptoconditions_heir.py @@ -22,15 +22,19 @@ class CryptoconditionsHeirTest(CryptoconditionsTestFramework): result = rpc.heiraddress('') assert_success(result) + # verify all keys look like valid AC addrs, could be better - for x in ['HeirNormalAddress', 'HeirCCTokensAddress', 'myaddress', 'myCCaddress', 'HeirCCAddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.heiraddress(self.pubkey) assert_success(result) + # test that additional CCaddress key is returned - for x in ['HeirNormalAddress', 'myCCaddress', 'myaddress', 'HeirCC1of2Address', 'HeirCCAddress', 'HeirCC1of2TokensAddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # getting empty heir list result = rpc.heirlist() diff --git a/qa/rpc-tests/cryptoconditions_oracles.py b/qa/rpc-tests/cryptoconditions_oracles.py index 008ab6256..953df5ca9 100755 --- a/qa/rpc-tests/cryptoconditions_oracles.py +++ b/qa/rpc-tests/cryptoconditions_oracles.py @@ -22,13 +22,17 @@ class CryptoconditionsOraclesTest(CryptoconditionsTestFramework): result = rpc.oraclesaddress() assert_success(result) - for x in ['myCCaddress', 'OraclesCCAddress', 'OraclesNormalAddress', 'myaddress', 'OraclesCCTokensAddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.oraclesaddress(self.pubkey) assert_success(result) - for x in ['myCCaddress', 'OraclesCCAddress', 'OraclesNormalAddress', 'myaddress', 'OraclesCCTokensAddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # there are no oracles created yet result = rpc.oracleslist() diff --git a/qa/rpc-tests/cryptoconditions_rewards.py b/qa/rpc-tests/cryptoconditions_rewards.py index d70e40740..8fd5d4c9f 100755 --- a/qa/rpc-tests/cryptoconditions_rewards.py +++ b/qa/rpc-tests/cryptoconditions_rewards.py @@ -19,12 +19,16 @@ class CryptoconditionsRewardsTest(CryptoconditionsTestFramework): rpc = self.nodes[0] result = rpc.rewardsaddress() - for x in ['myCCaddress', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.rewardsaddress(self.pubkey) - for x in ['myCCaddress', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # no rewards yet result = rpc.rewardslist() diff --git a/qa/rpc-tests/cryptoconditions_token.py b/qa/rpc-tests/cryptoconditions_token.py index faf2cbc8d..263d85dde 100755 --- a/qa/rpc-tests/cryptoconditions_token.py +++ b/qa/rpc-tests/cryptoconditions_token.py @@ -21,23 +21,27 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): result = rpc.tokenaddress() assert_success(result) - for x in ['TokensCCAddress', 'myCCaddress', 'myCCAddress(Tokens)', 'myaddress', 'TokensNormalAddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.tokenaddress(self.pubkey) assert_success(result) - for x in ['TokensCCAddress', 'myCCaddress', 'myCCAddress(Tokens)', 'myaddress', 'TokensNormalAddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.assetsaddress() assert_success(result) - for x in ['AssetsCCAddress', 'myCCaddress', 'myCCAddress(Assets)', 'myaddress', 'AssetsNormalAddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.assetsaddress(self.pubkey) assert_success(result) - for x in ['AssetsCCAddress', 'myCCaddress', 'myCCAddress(Assets)', 'myaddress', 'AssetsNormalAddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # there are no tokens created yet result = rpc.tokenlist() From d31eea878a798b8ac8486b18df4b4620a0316e74 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:00:30 -1100 Subject: [PATCH 111/787] Sha256 --- src/cc/gamescc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 2163d46c5..790652ba1 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -171,7 +171,7 @@ int32_t games_eventsign(std::vector &sig,std::vector payload,C const CKeyStore& keystore = *pwalletMain; txNew.vin.resize(1); txNew.vout.resize(1); - txNew.vin[0].prevout.hash = payload.GetHash(); + vcalc_sha256(0,(uint8_t *)&txNew.vin[0].prevout.hash,&payload[0],(int32_t)payload.size()); txNew.vin[0].prevout.n = 0; txNew.vout[0].scriptPubKey = CScript() << payload << OP_DROP << ParseHex(HexStr(pk)) << OP_CHECKSIG; txNew.vout[0].nValue = payload.size(); @@ -230,7 +230,7 @@ void komodo_netevent(std::vector message) { for (i=0; i Date: Mon, 25 Mar 2019 20:00:54 +0800 Subject: [PATCH 112/787] Update cryptoconditions_channels.py --- qa/rpc-tests/cryptoconditions_channels.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa/rpc-tests/cryptoconditions_channels.py b/qa/rpc-tests/cryptoconditions_channels.py index 2248efeab..7f82f2f3c 100755 --- a/qa/rpc-tests/cryptoconditions_channels.py +++ b/qa/rpc-tests/cryptoconditions_channels.py @@ -81,7 +81,7 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework): # now in channelinfo payment information should appear result = rpc.channelsinfo(channel_txid) assert_equal(result["Transactions"][1]["Payment"], payment_tx_id) - time.sleep(90) + # number of payments should be equal 1 (one denomination used) result = rpc.channelsinfo(channel_txid)["Transactions"][1]["Number of payments"] assert_equal(result, 1) From 5aa85d7dad0f4bac5bf9fa533898044a59d4e9e9 Mon Sep 17 00:00:00 2001 From: smk762 <35845239+smk762@users.noreply.github.com> Date: Mon, 25 Mar 2019 20:01:40 +0800 Subject: [PATCH 113/787] Update cryptoconditions_rewards.py --- qa/rpc-tests/cryptoconditions_rewards.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/qa/rpc-tests/cryptoconditions_rewards.py b/qa/rpc-tests/cryptoconditions_rewards.py index 8fd5d4c9f..57d3032b8 100755 --- a/qa/rpc-tests/cryptoconditions_rewards.py +++ b/qa/rpc-tests/cryptoconditions_rewards.py @@ -19,13 +19,11 @@ class CryptoconditionsRewardsTest(CryptoconditionsTestFramework): rpc = self.nodes[0] result = rpc.rewardsaddress() - for x in result.keys(): if x.find('ddress') > 0: assert_equal(result[x][0], 'R') result = rpc.rewardsaddress(self.pubkey) - for x in result.keys(): if x.find('ddress') > 0: assert_equal(result[x][0], 'R') From 1b5605f3f70bedbbab2aaf038226194e66008418 Mon Sep 17 00:00:00 2001 From: smk762 <35845239+smk762@users.noreply.github.com> Date: Mon, 25 Mar 2019 20:02:31 +0800 Subject: [PATCH 114/787] Update cryptoconditions_dice.py --- qa/rpc-tests/cryptoconditions_dice.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/qa/rpc-tests/cryptoconditions_dice.py b/qa/rpc-tests/cryptoconditions_dice.py index e93260178..7b9d3fbae 100755 --- a/qa/rpc-tests/cryptoconditions_dice.py +++ b/qa/rpc-tests/cryptoconditions_dice.py @@ -25,11 +25,8 @@ class CryptoconditionsDiceTest(CryptoconditionsTestFramework): assert_greater_than(result, 100000) result = rpc.diceaddress() - for x in result.keys(): - print(x+": "+str(result[x])) assert_equal(result['result'], 'success') - for x in result.keys(): if x.find('ddress') > 0: assert_equal(result[x][0], 'R') From 3a4fa333f7cec948e25fe4348fa621d2aa4913c0 Mon Sep 17 00:00:00 2001 From: smk762 <35845239+smk762@users.noreply.github.com> Date: Mon, 25 Mar 2019 20:03:07 +0800 Subject: [PATCH 115/787] Update cryptoconditions_faucet.py --- qa/rpc-tests/cryptoconditions_faucet.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/qa/rpc-tests/cryptoconditions_faucet.py b/qa/rpc-tests/cryptoconditions_faucet.py index 47e81b5ed..27c5fce4e 100755 --- a/qa/rpc-tests/cryptoconditions_faucet.py +++ b/qa/rpc-tests/cryptoconditions_faucet.py @@ -26,10 +26,8 @@ class CryptoconditionsFaucetTest(CryptoconditionsTestFramework): result = rpc.faucetaddress() assert_equal(result['result'], 'success') - for x in result.keys(): - print(x+": "+str(result[x])) + # verify all keys look like valid AC addrs, could be better - for x in result.keys(): if x.find('ddress') > 0: assert_equal(result[x][0], 'R') From 7077fcf08e73bf607aedd10650ba790478a87043 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:17:23 -1100 Subject: [PATCH 116/787] Test --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 790652ba1..101e17d18 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -173,7 +173,7 @@ int32_t games_eventsign(std::vector &sig,std::vector payload,C txNew.vout.resize(1); vcalc_sha256(0,(uint8_t *)&txNew.vin[0].prevout.hash,&payload[0],(int32_t)payload.size()); txNew.vin[0].prevout.n = 0; - txNew.vout[0].scriptPubKey = CScript() << payload << OP_DROP << ParseHex(HexStr(pk)) << OP_CHECKSIG; + txNew.vout[0].scriptPubKey = CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG; txNew.vout[0].nValue = payload.size(); txNew.nLockTime = 0; CTransaction txNewConst(txNew); From 581df62b25bd04dfcbe03bad35d25c1dded79336 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:19:00 -1100 Subject: [PATCH 117/787] Json fields --- src/cc/gamescc.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 101e17d18..02df2eb42 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -192,7 +192,7 @@ int32_t games_eventsign(std::vector &sig,std::vector payload,C UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); std::vector sig,payload; int32_t n; CPubKey mypk; uint256 hash; + UniValue result(UniValue::VOBJ); std::vector sig,payload; int32_t n; CPubKey mypk; char str[67]; if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) { if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) @@ -202,6 +202,8 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { komodo_sendmessage(4,8,"events",E_MARSHAL(ss << EVAL_GAMES << 'E' << mypk << sig << payload)); result.push_back(Pair("result","success")); + result.push_back(Pair("pubkey33",pubkey33_str(str,mypk))); + result.push_back(Pair("sig",sig.HexStr())); } else { From 26b7510dba44a548b88d86f5385d56f0d8af14c8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:25:40 -1100 Subject: [PATCH 118/787] Low level signing --- src/cc/gamescc.cpp | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 02df2eb42..b828ea41f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -167,26 +167,20 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) int32_t games_eventsign(std::vector &sig,std::vector payload,CPubKey pk) { - bool signSuccess; SignatureData sigdata; int32_t i,siglen; uint256 hash; uint8_t *ptr; auto consensusBranchId = 0; CMutableTransaction txNew; - const CKeyStore& keystore = *pwalletMain; - txNew.vin.resize(1); - txNew.vout.resize(1); - vcalc_sha256(0,(uint8_t *)&txNew.vin[0].prevout.hash,&payload[0],(int32_t)payload.size()); - txNew.vin[0].prevout.n = 0; - txNew.vout[0].scriptPubKey = CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG; - txNew.vout[0].nValue = payload.size(); - txNew.nLockTime = 0; - CTransaction txNewConst(txNew); - signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, payload.size(), SIGHASH_ALL), txNew.vout[0].scriptPubKey, sigdata, consensusBranchId); - if ( signSuccess > 0 ) + static void *ctx; + size_t siglen = 74; secp256k1_ecdsa_signature signature; uint8_t privkey[32]; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + if ( ctx != 0 ) { - UpdateTransaction(txNew,0,sigdata); - ptr = (uint8_t *)&sigdata.scriptSig[0]; - siglen = sigdata.scriptSig.size(); - sig.resize(siglen); - for (i=0; i 0 ) + { + sig.resize(siglen); + if ( secp256k1_ecdsa_signature_serialize_der(ctx,&sig[0],&siglen,&signature) > 0 ) + return(0); + else return(-3); + } else return(-2); } else return(-1); } From 778320952eb04ac51597e8c55ac9d1476cadfe41 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:28:20 -1100 Subject: [PATCH 119/787] Include --- src/cc/gamescc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 124dac876..4743bd0b7 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -5,6 +5,7 @@ extern CWallet* pwalletMain; #include "CCinclude.h" +#include "secp256k1.h" std::string MYCCLIBNAME = (char *)"gamescc"; From 404ef7d173634f2950b8f505570245067bbdb68b Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:29:22 -1100 Subject: [PATCH 120/787] secp256k1_context* --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index b828ea41f..e6ee0c692 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -167,7 +167,7 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) int32_t games_eventsign(std::vector &sig,std::vector payload,CPubKey pk) { - static void *ctx; + static secp256k1_context *ctx; size_t siglen = 74; secp256k1_ecdsa_signature signature; uint8_t privkey[32]; if ( ctx == 0 ) ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); From 15b10ece314d88a0f526f2633159a8c1a82672c8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:29:45 -1100 Subject: [PATCH 121/787] Syntax --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index e6ee0c692..e0006684f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -196,7 +196,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { komodo_sendmessage(4,8,"events",E_MARSHAL(ss << EVAL_GAMES << 'E' << mypk << sig << payload)); result.push_back(Pair("result","success")); - result.push_back(Pair("pubkey33",pubkey33_str(str,mypk))); + result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); result.push_back(Pair("sig",sig.HexStr())); } else From a590bcb1361a878b2e52e725b949043aa5327b0b Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:31:26 -1100 Subject: [PATCH 122/787] -field --- src/cc/gamescc.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index e0006684f..559910642 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -197,7 +197,6 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) komodo_sendmessage(4,8,"events",E_MARSHAL(ss << EVAL_GAMES << 'E' << mypk << sig << payload)); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); - result.push_back(Pair("sig",sig.HexStr())); } else { From e920af85dcd018336f68dcb5316dd9abf3b2f35e Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:32:21 -1100 Subject: [PATCH 123/787] Calc hash --- src/cc/gamescc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 559910642..61efb799b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -168,12 +168,13 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) int32_t games_eventsign(std::vector &sig,std::vector payload,CPubKey pk) { static secp256k1_context *ctx; - size_t siglen = 74; secp256k1_ecdsa_signature signature; uint8_t privkey[32]; + size_t siglen = 74; secp256k1_ecdsa_signature signature; uint8_t privkey[32]; uint256 hash; if ( ctx == 0 ) ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); if ( ctx != 0 ) { Myprivkey(privkey); + vcalc_sha256(0,(uint8_t *)&hash,&payload[0],(int32_t)payload.size()); if ( secp256k1_ecdsa_sign(ctx,&signature,(uint8_t *)&hash,privkey,NULL,NULL) > 0 ) { sig.resize(siglen); From 7338d62f37b21dd4862047dab7c570659d5a1d59 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:43:41 -1100 Subject: [PATCH 124/787] Test --- src/cc/gamescc.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 61efb799b..bc2123439 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -46,16 +46,6 @@ uint8_t games_opretdecode(CPubKey &pk,CScript scriptPubKey) return(0); } -uint8_t games_eventdecode(CPubKey &pk,std::vector &sig,std::vector &payload,std::vector message) -{ - uint8_t e,f; - if ( message.size() > 2 && E_UNMARSHAL(message,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) - { - return(f); - } - return(0); -} - UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) { CTransaction tx; @@ -219,6 +209,17 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } +uint8_t games_eventdecode(CPubKey &pk,std::vector &sig,std::vector &payload,std::vector message) +{ + uint8_t e,f; + if ( message.size() > 2 && E_UNMARSHAL(message,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) + { + return(f); + } + fprintf(stderr,"e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size()); + return(0); +} + void komodo_netevent(std::vector message) { int32_t i; CPubKey pk; std::vector sig,payload; char str[67]; @@ -230,9 +231,9 @@ void komodo_netevent(std::vector message) } else { - for (i=0; i Date: Mon, 25 Mar 2019 01:49:59 -1100 Subject: [PATCH 125/787] Test --- src/cc/gamescc.cpp | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index bc2123439..5016350f4 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -46,6 +46,25 @@ uint8_t games_opretdecode(CPubKey &pk,CScript scriptPubKey) return(0); } +CScript games_eventopret(CPubKey pk,std::vector sig,std::vector payload) +{ + CScript opret; uint8_t evalcode = EVAL_GAMES; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'E' << pk << sig << payload); + return(opret); +} + +uint8_t games_eventdecode(CPubKey &pk,std::vector &sig,std::vector &payload,std::vector message) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(message,vopret); + if ( message.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) + { + return(f); + } + fprintf(stderr,"e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size()); + return(0); +} + UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) { CTransaction tx; @@ -177,7 +196,7 @@ int32_t games_eventsign(std::vector &sig,std::vector payload,C UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); std::vector sig,payload; int32_t n; CPubKey mypk; char str[67]; + UniValue result(UniValue::VOBJ); std::vector sig,payload; int32_t n; CPubKey mypk; char str[67]; CScript msg; if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) { if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) @@ -185,7 +204,8 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) mypk = pubkey2pk(Mypubkey()); if ( games_eventsign(sig,payload,mypk) == 0 ) { - komodo_sendmessage(4,8,"events",E_MARSHAL(ss << EVAL_GAMES << 'E' << mypk << sig << payload)); + msg = games_eventopret(pk,sig,payload); + komodo_sendmessage(4,8,"events",&msg[0],(int32_t)msg.size()); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); } @@ -209,17 +229,6 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -uint8_t games_eventdecode(CPubKey &pk,std::vector &sig,std::vector &payload,std::vector message) -{ - uint8_t e,f; - if ( message.size() > 2 && E_UNMARSHAL(message,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) - { - return(f); - } - fprintf(stderr,"e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size()); - return(0); -} - void komodo_netevent(std::vector message) { int32_t i; CPubKey pk; std::vector sig,payload; char str[67]; From fe20bf47b007165df74cf1d1db7b10ee08ab61f0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:52:16 -1100 Subject: [PATCH 126/787] Test --- src/cc/gamescc.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 5016350f4..0d1bc4f0e 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -53,10 +53,9 @@ CScript games_eventopret(CPubKey pk,std::vector sig,std::vector &sig,std::vector &payload,std::vector message) +uint8_t games_eventdecode(CPubKey &pk,std::vector &sig,std::vector &payload,std::vector vopret) { - std::vector vopret; uint8_t e,f; - GetOpReturnData(message,vopret); + uint8_t e,f; if ( message.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) { return(f); @@ -196,7 +195,7 @@ int32_t games_eventsign(std::vector &sig,std::vector payload,C UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); std::vector sig,payload; int32_t n; CPubKey mypk; char str[67]; CScript msg; + UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t n; CPubKey mypk; char str[67]; if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) { if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) @@ -204,8 +203,8 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) mypk = pubkey2pk(Mypubkey()); if ( games_eventsign(sig,payload,mypk) == 0 ) { - msg = games_eventopret(pk,sig,payload); - komodo_sendmessage(4,8,"events",&msg[0],(int32_t)msg.size()); + GetOpReturnData(games_eventopret(pk,sig,payload),vopret); + komodo_sendmessage(4,8,"events",&vopret[0],(int32_t)vopret.size()); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); } From 29ac5d0a2d3d3edac115e78123fdae26133bfb01 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:54:15 -1100 Subject: [PATCH 127/787] Syntax --- src/cc/gamescc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 0d1bc4f0e..9f8d110c1 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -56,7 +56,7 @@ CScript games_eventopret(CPubKey pk,std::vector sig,std::vector &sig,std::vector &payload,std::vector vopret) { uint8_t e,f; - if ( message.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) { return(f); } @@ -203,7 +203,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) mypk = pubkey2pk(Mypubkey()); if ( games_eventsign(sig,payload,mypk) == 0 ) { - GetOpReturnData(games_eventopret(pk,sig,payload),vopret); + GetOpReturnData(games_eventopret(mypk,sig,payload),vopret); komodo_sendmessage(4,8,"events",&vopret[0],(int32_t)vopret.size()); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); From d17ad823bc50a68427ee03ca861c422cd7b6eb11 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:55:32 -1100 Subject: [PATCH 128/787] & --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 9f8d110c1..6efa745f2 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -204,7 +204,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( games_eventsign(sig,payload,mypk) == 0 ) { GetOpReturnData(games_eventopret(mypk,sig,payload),vopret); - komodo_sendmessage(4,8,"events",&vopret[0],(int32_t)vopret.size()); + komodo_sendmessage(4,8,"events",(uint8_t *)&vopret[0],(int32_t)vopret.size()); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); } From c29ad5f680445952cc849f23305d718785501cfd Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 01:56:43 -1100 Subject: [PATCH 129/787] Std::Vector --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 6efa745f2..1cbd6674f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -204,7 +204,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( games_eventsign(sig,payload,mypk) == 0 ) { GetOpReturnData(games_eventopret(mypk,sig,payload),vopret); - komodo_sendmessage(4,8,"events",(uint8_t *)&vopret[0],(int32_t)vopret.size()); + komodo_sendmessage(4,8,"events",vopret); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); } From 52eef070eccf1c95b038f9c0538f4f6be6abb969 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 02:07:55 -1100 Subject: [PATCH 130/787] Add timestamp --- src/cc/gamescc.cpp | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 1cbd6674f..0b75c64ab 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -53,11 +53,18 @@ CScript games_eventopret(CPubKey pk,std::vector sig,std::vector &sig,std::vector &payload,std::vector vopret) +uint8_t games_eventdecode(uint32_t ×tamp,CPubKey &pk,std::vector &sig,std::vector &payload,std::vector vopret) { - uint8_t e,f; - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) + uint8_t e,f; int32_t len; + timestamp = 0; + if ( vopret.size() > 6 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) { + len = (int32_t)payload.size(); + timestamp = payload[--len]; + timestamp = (timestamp << 8) | payload[--len]; + timestamp = (timestamp << 8) | payload[--len]; + timestamp = (timestamp << 8) | payload[--len]; + payload.resize(len); return(f); } fprintf(stderr,"e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size()); @@ -176,13 +183,20 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) int32_t games_eventsign(std::vector &sig,std::vector payload,CPubKey pk) { static secp256k1_context *ctx; - size_t siglen = 74; secp256k1_ecdsa_signature signature; uint8_t privkey[32]; uint256 hash; + size_t siglen = 74; secp256k1_ecdsa_signature signature; int32_t len; uint8_t privkey[32]; uint256 hash; uint32_t timestamp; if ( ctx == 0 ) ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); if ( ctx != 0 ) { Myprivkey(privkey); - vcalc_sha256(0,(uint8_t *)&hash,&payload[0],(int32_t)payload.size()); + len = payload.size(); + payload.resize(len + 4); + timestamp = (uin32_t)time(NULL); + payload[len++] = timestamp, timestamp >> 8; + payload[len++] = timestamp, timestamp >> 8; + payload[len++] = timestamp, timestamp >> 8; + payload[len++] = timestamp, timestamp >> 8; + vcalc_sha256(0,(uint8_t *)&hash,&payload[0],len); if ( secp256k1_ecdsa_sign(ctx,&signature,(uint8_t *)&hash,privkey,NULL,NULL) > 0 ) { sig.resize(siglen); @@ -230,12 +244,13 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) void komodo_netevent(std::vector message) { - int32_t i; CPubKey pk; std::vector sig,payload; char str[67]; - if ( games_eventdecode(pk,sig,payload,message) == 'E' ) + int32_t i; uint32_t timestamp,now; CPubKey pk; std::vector sig,payload; char str[67]; + if ( games_eventdecode(timestamp,pk,sig,payload,message) == 'E' ) { + now = (uint32_t)time(NULL); for (i=0; i Date: Mon, 25 Mar 2019 02:08:47 -1100 Subject: [PATCH 131/787] Fix --- src/cc/gamescc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 0b75c64ab..3d3052025 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -191,11 +191,11 @@ int32_t games_eventsign(std::vector &sig,std::vector payload,C Myprivkey(privkey); len = payload.size(); payload.resize(len + 4); - timestamp = (uin32_t)time(NULL); - payload[len++] = timestamp, timestamp >> 8; + timestamp = (uint32_t)time(NULL); payload[len++] = timestamp, timestamp >> 8; payload[len++] = timestamp, timestamp >> 8; payload[len++] = timestamp, timestamp >> 8; + payload[len++] = timestamp; vcalc_sha256(0,(uint8_t *)&hash,&payload[0],len); if ( secp256k1_ecdsa_sign(ctx,&signature,(uint8_t *)&hash,privkey,NULL,NULL) > 0 ) { From 093b14a1584f70820a791e95d2abef8e239b2740 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 02:14:36 -1100 Subject: [PATCH 132/787] Test --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 3d3052025..2a1bb1e4f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -180,7 +180,7 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -int32_t games_eventsign(std::vector &sig,std::vector payload,CPubKey pk) +int32_t games_eventsign(std::vector &sig,std::vector &payload,CPubKey pk) { static secp256k1_context *ctx; size_t siglen = 74; secp256k1_ecdsa_signature signature; int32_t len; uint8_t privkey[32]; uint256 hash; uint32_t timestamp; From 951a8e10cdf6b1420e7cca9bab63e6deb330fa13 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 02:17:39 -1100 Subject: [PATCH 133/787] Test --- src/cc/gamescc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 2a1bb1e4f..363f583a5 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -60,10 +60,10 @@ uint8_t games_eventdecode(uint32_t ×tamp,CPubKey &pk,std::vector & if ( vopret.size() > 6 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) { len = (int32_t)payload.size(); - timestamp = payload[--len]; - timestamp = (timestamp << 8) | payload[--len]; - timestamp = (timestamp << 8) | payload[--len]; - timestamp = (timestamp << 8) | payload[--len]; + timestamp = (uint32_t)payload[--len] << 24; + timestamp |= (uint32_t)payload[--len] << 16; + timestamp |= (uin32_t)payload[--len] << 8; + timestamp |= (uint32_t)payload[--len]; payload.resize(len); return(f); } From d8274fe83240c032f10ac3bd0fce926290974df3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 02:18:18 -1100 Subject: [PATCH 134/787] test --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 363f583a5..1067bc82a 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -62,7 +62,7 @@ uint8_t games_eventdecode(uint32_t ×tamp,CPubKey &pk,std::vector & len = (int32_t)payload.size(); timestamp = (uint32_t)payload[--len] << 24; timestamp |= (uint32_t)payload[--len] << 16; - timestamp |= (uin32_t)payload[--len] << 8; + timestamp |= (uint32_t)payload[--len] << 8; timestamp |= (uint32_t)payload[--len]; payload.resize(len); return(f); From 6ac2a8090d6939cbd9b39248d29c2a3be8cc5508 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 02:24:45 -1100 Subject: [PATCH 135/787] fprintf(stderr,"timestamp %08x\n",timestamp); --- src/cc/gamescc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 1067bc82a..6ef563bed 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -64,6 +64,7 @@ uint8_t games_eventdecode(uint32_t ×tamp,CPubKey &pk,std::vector & timestamp |= (uint32_t)payload[--len] << 16; timestamp |= (uint32_t)payload[--len] << 8; timestamp |= (uint32_t)payload[--len]; + fprintf(stderr,"timestamp %08x\n",timestamp); payload.resize(len); return(f); } @@ -192,6 +193,7 @@ int32_t games_eventsign(std::vector &sig,std::vector &payload, len = payload.size(); payload.resize(len + 4); timestamp = (uint32_t)time(NULL); + fprintf(stderr,"timestamp %08x\n",timestamp); payload[len++] = timestamp, timestamp >> 8; payload[len++] = timestamp, timestamp >> 8; payload[len++] = timestamp, timestamp >> 8; From 5beffe66371d983c7526a41ec3b20e4c6457422d Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 02:31:24 -1100 Subject: [PATCH 136/787] Test --- src/cc/gamescc.cpp | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 6ef563bed..7c4a1fc80 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -46,26 +46,20 @@ uint8_t games_opretdecode(CPubKey &pk,CScript scriptPubKey) return(0); } -CScript games_eventopret(CPubKey pk,std::vector sig,std::vector payload) +CScript games_eventopret(uint32_t timestamp,CPubKey pk,std::vector sig,std::vector payload) { CScript opret; uint8_t evalcode = EVAL_GAMES; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'E' << pk << sig << payload); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'E' << timestamp << pk << sig << payload); return(opret); } uint8_t games_eventdecode(uint32_t ×tamp,CPubKey &pk,std::vector &sig,std::vector &payload,std::vector vopret) { - uint8_t e,f; int32_t len; + uint8_t e,f; timestamp = 0; - if ( vopret.size() > 6 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) + if ( vopret.size() > 6 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> timestamp; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) { - len = (int32_t)payload.size(); - timestamp = (uint32_t)payload[--len] << 24; - timestamp |= (uint32_t)payload[--len] << 16; - timestamp |= (uint32_t)payload[--len] << 8; - timestamp |= (uint32_t)payload[--len]; fprintf(stderr,"timestamp %08x\n",timestamp); - payload.resize(len); return(f); } fprintf(stderr,"e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size()); @@ -181,10 +175,10 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -int32_t games_eventsign(std::vector &sig,std::vector &payload,CPubKey pk) +int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vector payload,CPubKey pk) { static secp256k1_context *ctx; - size_t siglen = 74; secp256k1_ecdsa_signature signature; int32_t len; uint8_t privkey[32]; uint256 hash; uint32_t timestamp; + size_t siglen = 74; secp256k1_ecdsa_signature signature; int32_t len; uint8_t privkey[32]; uint256 hash; uint32_t t; if ( ctx == 0 ) ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); if ( ctx != 0 ) @@ -192,12 +186,12 @@ int32_t games_eventsign(std::vector &sig,std::vector &payload, Myprivkey(privkey); len = payload.size(); payload.resize(len + 4); - timestamp = (uint32_t)time(NULL); + t = timestamp = (uint32_t)time(NULL); fprintf(stderr,"timestamp %08x\n",timestamp); - payload[len++] = timestamp, timestamp >> 8; - payload[len++] = timestamp, timestamp >> 8; - payload[len++] = timestamp, timestamp >> 8; - payload[len++] = timestamp; + payload[len++] = t, t >> 8; + payload[len++] = t, t >> 8; + payload[len++] = t, t >> 8; + payload[len++] = t; vcalc_sha256(0,(uint8_t *)&hash,&payload[0],len); if ( secp256k1_ecdsa_sign(ctx,&signature,(uint8_t *)&hash,privkey,NULL,NULL) > 0 ) { @@ -211,15 +205,15 @@ int32_t games_eventsign(std::vector &sig,std::vector &payload, UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t n; CPubKey mypk; char str[67]; + UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t n; CPubKey mypk; char str[67]; uint32_t timestamp; if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) { if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) { mypk = pubkey2pk(Mypubkey()); - if ( games_eventsign(sig,payload,mypk) == 0 ) + if ( games_eventsign(timestamp,sig,payload,mypk) == 0 ) { - GetOpReturnData(games_eventopret(mypk,sig,payload),vopret); + GetOpReturnData(games_eventopret(timestamp,mypk,sig,payload),vopret); komodo_sendmessage(4,8,"events",vopret); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); From e18ea7d1d4b46205aa6b70e2f2dea7559e46811b Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 02:46:50 -1100 Subject: [PATCH 137/787] Test --- src/cc/gamescc.cpp | 51 +++++++++++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 14 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 7c4a1fc80..b24deca7f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -59,10 +59,9 @@ uint8_t games_eventdecode(uint32_t ×tamp,CPubKey &pk,std::vector & timestamp = 0; if ( vopret.size() > 6 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> timestamp; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) { - fprintf(stderr,"timestamp %08x\n",timestamp); return(f); } - fprintf(stderr,"e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size()); + fprintf(stderr,"ERROR e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size()); return(0); } @@ -178,7 +177,7 @@ UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vector payload,CPubKey pk) { static secp256k1_context *ctx; - size_t siglen = 74; secp256k1_ecdsa_signature signature; int32_t len; uint8_t privkey[32]; uint256 hash; uint32_t t; + size_t siglen = 74; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature signature; int32_t len,verifyflag = 1; uint8_t privkey[32]; uint256 hash; uint32_t t; if ( ctx == 0 ) ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); if ( ctx != 0 ) @@ -186,26 +185,45 @@ int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vecto Myprivkey(privkey); len = payload.size(); payload.resize(len + 4); - t = timestamp = (uint32_t)time(NULL); - fprintf(stderr,"timestamp %08x\n",timestamp); + if ( timestamp == 0 ) + { + timestamp = (uint32_t)time(NULL); + verifyflag = 0; + } + t = timestamp; payload[len++] = t, t >> 8; payload[len++] = t, t >> 8; payload[len++] = t, t >> 8; payload[len++] = t; vcalc_sha256(0,(uint8_t *)&hash,&payload[0],len); - if ( secp256k1_ecdsa_sign(ctx,&signature,(uint8_t *)&hash,privkey,NULL,NULL) > 0 ) + if ( verifyflag == 0 ) { - sig.resize(siglen); - if ( secp256k1_ecdsa_signature_serialize_der(ctx,&sig[0],&siglen,&signature) > 0 ) - return(0); - else return(-3); - } else return(-2); + if ( secp256k1_ecdsa_sign(ctx,&signature,(uint8_t *)&hash,privkey,NULL,NULL) > 0 ) + { + sig.resize(siglen); + if ( secp256k1_ecdsa_signature_serialize_der(ctx,&sig[0],&siglen,&signature) > 0 ) + return(0); + else return(-3); + } else return(-2); + } + else + { + if ( secp256k1_ec_pubkey_parse(ctx,&pubkey,pk.begin(),33) > 0 ) + { + if ( secp256k1_ecdsa_signature_parse_der(ctx,&signature,&sig[0],sig.size()) > 0 ) + { + if ( secp256k1_ecdsa_verify(ctx,&signature,(uint8_t *)&hash,&pubkey) > 0 ) + return(0); + else return(-4); + } else return(-3); + } else return(-2); + } } else return(-1); } UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t n; CPubKey mypk; char str[67]; uint32_t timestamp; + UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t n; CPubKey mypk; char str[67]; uint32_t timestamp = 0; if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) { if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) @@ -240,13 +258,18 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) void komodo_netevent(std::vector message) { - int32_t i; uint32_t timestamp,now; CPubKey pk; std::vector sig,payload; char str[67]; + int32_t i,retval; uint32_t timestamp,now; CPubKey pk; std::vector sig,payload; char str[67]; if ( games_eventdecode(timestamp,pk,sig,payload,message) == 'E' ) { now = (uint32_t)time(NULL); + lag = now - timestamp; + if ( lag < -3 || lag > 3 ) + fprintf(stderr,"LAG ERROR "); + if ( (retval= games_eventsign(timestamp,sig,payload,pk)) != 0 ) + fprintf(stderr,"SIG ERROR.%d ",retval); for (i=0; i Date: Mon, 25 Mar 2019 02:47:21 -1100 Subject: [PATCH 138/787] Test --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index b24deca7f..198933a77 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -258,7 +258,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) void komodo_netevent(std::vector message) { - int32_t i,retval; uint32_t timestamp,now; CPubKey pk; std::vector sig,payload; char str[67]; + int32_t i,retval,lag; uint32_t timestamp,now; CPubKey pk; std::vector sig,payload; char str[67]; if ( games_eventdecode(timestamp,pk,sig,payload,message) == 'E' ) { now = (uint32_t)time(NULL); From 05aba9047a0593ec98896c87464acae72c1b0312 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 02:50:03 -1100 Subject: [PATCH 139/787] sig.resize --- src/cc/gamescc.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 198933a77..02ee3708f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -202,7 +202,11 @@ int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vecto { sig.resize(siglen); if ( secp256k1_ecdsa_signature_serialize_der(ctx,&sig[0],&siglen,&signature) > 0 ) + { + if ( siglen != sig.size() ) + sig.resize(siglen); return(0); + } else return(-3); } else return(-2); } From 933e911bcca7ea1a2c5978b11f1a72dbcd200eac Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 02:52:25 -1100 Subject: [PATCH 140/787] SECP256K1_CONTEXT_VERIFY --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 02ee3708f..1e8b99079 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -179,7 +179,7 @@ int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vecto static secp256k1_context *ctx; size_t siglen = 74; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature signature; int32_t len,verifyflag = 1; uint8_t privkey[32]; uint256 hash; uint32_t t; if ( ctx == 0 ) - ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); if ( ctx != 0 ) { Myprivkey(privkey); From d4ebd390faa180e63cd8249ac42ba53afb863f7f Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 03:03:53 -1100 Subject: [PATCH 141/787] relay --- src/cc/gamescc.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 1e8b99079..ca780b8cf 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -225,6 +225,11 @@ int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vecto } else return(-1); } +int32_t games_payload(CPubKey pk,uint32_t timestamp,std::vector payload) +{ + return(0); +} + UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t n; CPubKey mypk; char str[67]; uint32_t timestamp = 0; @@ -236,6 +241,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( games_eventsign(timestamp,sig,payload,mypk) == 0 ) { GetOpReturnData(games_eventopret(timestamp,mypk,sig,payload),vopret); + games_payload(mypk,timestamp,payload); komodo_sendmessage(4,8,"events",vopret); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); @@ -262,15 +268,26 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) void komodo_netevent(std::vector message) { - int32_t i,retval,lag; uint32_t timestamp,now; CPubKey pk; std::vector sig,payload; char str[67]; + int32_t i,retval,lag,lagerr=0; uint32_t timestamp,now; CPubKey pk; std::vector sig,payload; char str[67]; if ( games_eventdecode(timestamp,pk,sig,payload,message) == 'E' ) { now = (uint32_t)time(NULL); lag = now - timestamp; if ( lag < -3 || lag > 3 ) + { fprintf(stderr,"LAG ERROR "); + lagerr = lag; + } if ( (retval= games_eventsign(timestamp,sig,payload,pk)) != 0 ) fprintf(stderr,"SIG ERROR.%d ",retval); + else if ( lagerr == 0 ) + { + if ( games_payload(pk,timestamp,payload) == 0 ) // first time this is seen + { + if ( (rand() % 10) == 0 ) + komodo_sendmessage(2,2,"events",message); + } + } for (i=0; i Date: Mon, 25 Mar 2019 03:04:52 -1100 Subject: [PATCH 142/787] +print --- src/cc/gamescc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index ca780b8cf..fe2cfdcae 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -285,7 +285,10 @@ void komodo_netevent(std::vector message) if ( games_payload(pk,timestamp,payload) == 0 ) // first time this is seen { if ( (rand() % 10) == 0 ) + { + fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size()); komodo_sendmessage(2,2,"events",message); + } } } for (i=0; i Date: Mon, 25 Mar 2019 03:24:01 -1100 Subject: [PATCH 143/787] Add gametxid and eventid --- src/cc/gamescc.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index fe2cfdcae..f1bb3bf81 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -27,6 +27,8 @@ Every time period, all players would set their rng value to the lastrng value. The only thing to be careful of is it not exceed the maxrng calls to rngnext in a single time period. otherwise the same set of rng numbers will be repeated. */ +uint256 Gametxid; +uint32_t numevents; CScript games_opret(uint8_t funcid,CPubKey pk) { @@ -232,11 +234,26 @@ int32_t games_payload(CPubKey pk,uint32_t timestamp,std::vector payload UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t n; CPubKey mypk; char str[67]; uint32_t timestamp = 0; + static uint256 lastgametxid; + UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t len,i,n; uint32_t x; CPubKey mypk; char str[67]; uint32_t timestamp = 0; if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) { if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) { + if ( Gametxid != lastgametxid ) + { + lastgametxid = Gametxid; + numevents = 0; + } + len = payload.size(); + payload.resize(len + 4 + 32); + for (i=0; i<32; i++) + payload[len++] = ((uint8_t *)&Gametxid)[i]; + x = numevents++; + payload[len++] = x, x >> 8; + payload[len++] = x, x >> 8; + payload[len++] = x, x >> 8; + payload[len++] = x; mypk = pubkey2pk(Mypubkey()); if ( games_eventsign(timestamp,sig,payload,mypk) == 0 ) { From 2136c632de3d4589fa2234c2422adb4991625b48 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 03:28:04 -1100 Subject: [PATCH 144/787] >>= --- src/cc/gamescc.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index f1bb3bf81..a5573753b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -193,9 +193,9 @@ int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vecto verifyflag = 0; } t = timestamp; - payload[len++] = t, t >> 8; - payload[len++] = t, t >> 8; - payload[len++] = t, t >> 8; + payload[len++] = t, t >>= 8; + payload[len++] = t, t >>= 8; + payload[len++] = t, t >>= 8; payload[len++] = t; vcalc_sha256(0,(uint8_t *)&hash,&payload[0],len); if ( verifyflag == 0 ) @@ -250,9 +250,9 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) for (i=0; i<32; i++) payload[len++] = ((uint8_t *)&Gametxid)[i]; x = numevents++; - payload[len++] = x, x >> 8; - payload[len++] = x, x >> 8; - payload[len++] = x, x >> 8; + payload[len++] = x, x >>= 8; + payload[len++] = x, x >>= 8; + payload[len++] = x, x >>= 8; payload[len++] = x; mypk = pubkey2pk(Mypubkey()); if ( games_eventsign(timestamp,sig,payload,mypk) == 0 ) From 0b46d5e610efd9cc52d9e1f9640ec8380f902f4a Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 03:36:56 -1100 Subject: [PATCH 145/787] Extract gametxid and eventide --- src/cc/gamescc.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index a5573753b..b77c2aef4 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -229,7 +229,21 @@ int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vecto int32_t games_payload(CPubKey pk,uint32_t timestamp,std::vector payload) { - return(0); + uint8_t gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) + { + len -= 36; + for (i=0; i<32; i++) + ((uint8_t *)&gametxid)[i] = payload[len+i]; + eventid = (uint32_t)payload[len+32]; + eventid |= (uint32_t)payload[len+33] << 8; + eventid |= (uint32_t)payload[len+34] << 16; + eventid |= (uint32_t)payload[len+35] << 24; + for (i=0; i Date: Mon, 25 Mar 2019 03:38:34 -1100 Subject: [PATCH 146/787] Uint256 --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index b77c2aef4..ae3f0cd6f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -229,7 +229,7 @@ int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vecto int32_t games_payload(CPubKey pk,uint32_t timestamp,std::vector payload) { - uint8_t gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; if ( (len= payload.size()) > 36 ) { len -= 36; From c833422a79c2bd63c1edd990ea343b1c674fc962 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 03:48:00 -1100 Subject: [PATCH 147/787] Gametxid and eventid as params --- src/cc/gamescc.cpp | 27 ++++++++++++++++++++------- src/cc/gamescc.h | 2 +- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index ae3f0cd6f..95caa0f9e 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -248,22 +248,32 @@ int32_t games_payload(CPubKey pk,uint32_t timestamp,std::vector payload UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - static uint256 lastgametxid; - UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t len,i,n; uint32_t x; CPubKey mypk; char str[67]; uint32_t timestamp = 0; - if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) + static uint256 lastgametxid; static uint32_t numevents; + UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t len,i,n; uint32_t x; CPubKey mypk; char str[67]; uint32_t eventid,timestamp = 0; uint256 gametxid; + if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 1 && n <= 3 ) { if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) { - if ( Gametxid != lastgametxid ) + if ( n >= 2 ) + gametxid = juint256(jitem(params,1),0); + else gametxid = zeroid; + if ( gametxid != lastgametxid ) { - lastgametxid = Gametxid; - numevents = 0; + lastgametxid = gametxid; + numevents = 1; + eventid = 0; } + if ( n == 3 ) + { + eventid = juint(jitem(params,2),0); + if ( numevents <= eventid ) + numevents = eventid+1; + } else eventid = numevents++; len = payload.size(); payload.resize(len + 4 + 32); for (i=0; i<32; i++) payload[len++] = ((uint8_t *)&Gametxid)[i]; - x = numevents++; + x = eventid; payload[len++] = x, x >>= 8; payload[len++] = x, x >>= 8; payload[len++] = x, x >>= 8; @@ -274,6 +284,9 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) GetOpReturnData(games_eventopret(timestamp,mypk,sig,payload),vopret); games_payload(mypk,timestamp,payload); komodo_sendmessage(4,8,"events",vopret); + result.push_back(Pair("gametxid",gametxid.GetHex())); + result.push_back(Pair("eventid",(int64_t)eventid)); + result.push_back(Pair("timestamp",(int64_t)timestamp)); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); } diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 4743bd0b7..c1fc63b48 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -23,7 +23,7 @@ std::string MYCCLIBNAME = (char *)"gamescc"; { (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"create", (char *)"game,minplayers,maxplayers,buyin,numblocks", 5, 5, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"info", (char *)"txid", 1, 1, ' ', EVAL_GAMES }, \ - { (char *)MYCCNAME, (char *)"events", (char *)"hex", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"events", (char *)"eventshex [gametxid [eventid]]", 1, 3, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"register", (char *)"txid", 1, 1, ' ', EVAL_GAMES }, bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); From a1f1b47d79f07c078023fc4876f9d578abacf7b9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 03:48:19 -1100 Subject: [PATCH 148/787] -globals --- src/cc/gamescc.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 95caa0f9e..9419986e1 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -27,9 +27,6 @@ Every time period, all players would set their rng value to the lastrng value. The only thing to be careful of is it not exceed the maxrng calls to rngnext in a single time period. otherwise the same set of rng numbers will be repeated. */ -uint256 Gametxid; -uint32_t numevents; - CScript games_opret(uint8_t funcid,CPubKey pk) { CScript opret; uint8_t evalcode = EVAL_GAMES; From 200d451d2c5c4e99f2fc317c2d637586f623bea1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 03:49:14 -1100 Subject: [PATCH 149/787] Syntax --- src/cc/gamescc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 9419986e1..c0c031d71 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -252,7 +252,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) { if ( n >= 2 ) - gametxid = juint256(jitem(params,1),0); + gametxid = juint256(jitem(params,1)); else gametxid = zeroid; if ( gametxid != lastgametxid ) { @@ -269,7 +269,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) len = payload.size(); payload.resize(len + 4 + 32); for (i=0; i<32; i++) - payload[len++] = ((uint8_t *)&Gametxid)[i]; + payload[len++] = ((uint8_t *)&gametxid)[i]; x = eventid; payload[len++] = x, x >>= 8; payload[len++] = x, x >>= 8; From a1f240e680c47e9bdca80921e3b316f1524be5d6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 06:30:44 -1100 Subject: [PATCH 150/787] Lots of gamescc --- src/cc/gamescc.cpp | 1337 +++++++++++++++++++++++++++++++++++++++++- src/cc/gamescc.h | 47 +- src/cc/rogue_rpc.cpp | 3 + 3 files changed, 1358 insertions(+), 29 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index c0c031d71..2330d130c 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -25,26 +25,120 @@ you will notice maxrngs and lastrng, the lastrng is the rng value that will happen after maxrng iterations of calling rngnext with the current rng. This allows making time based multiplayer games where all the nodes can validate all the other nodes rng, even without realtime synchronization of all user input events. Every time period, all players would set their rng value to the lastrng value. The only thing to be careful of is it not exceed the maxrng calls to rngnext in a single time period. otherwise the same set of rng numbers will be repeated. + + events rpc is called after each user event and broadcasts to the network the tuple (gametxid, pk, eventid, payload). This allows the network to verify realtime user events from a specific pk/gametxid and also to make sure no user events were changed. In the event the events were changed, then the guilty pk will be blacklisted. If no realtime events, then the guilty pk would be penalized. + + ./c cclib events 17 \"[%226c%22,%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,0]\" + ./c cclib events 17 \"[%226d%22,%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,1]\" */ -CScript games_opret(uint8_t funcid,CPubKey pk) + +CScript games_newgameopret(int64_t buyin,int32_t maxplayers) { CScript opret; uint8_t evalcode = EVAL_GAMES; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'G' << buyin << maxplayers); return(opret); } -uint8_t games_opretdecode(CPubKey &pk,CScript scriptPubKey) +uint8_t games_newgameopreturndecode(int64_t &buyin,int32_t &maxplayers,CScript scriptPubKey) { std::vector vopret; uint8_t e,f; GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk) != 0 && e == EVAL_GAMES ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> buyin; ss >> maxplayers) != 0 && e == EVAL_GAMES && f == 'G' ) { return(f); } return(0); } +CScript games_registeropret(uint256 gametxid,uint256 playertxid) +{ + CScript opret; uint8_t evalcode = EVAL_GAMES; + //fprintf(stderr,"opret.(%s %s).R\n",gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'R' << gametxid << playertxid); + return(opret); +} + +CScript games_keystrokesopret(uint256 gametxid,uint256 batontxid,CPubKey pk,std::vectorkeystrokes) +{ + CScript opret; uint8_t evalcode = EVAL_GAMES; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'K' << gametxid << batontxid << pk << keystrokes); + return(opret); +} + +uint8_t games_keystrokesopretdecode(uint256 &gametxid,uint256 &batontxid,CPubKey &pk,std::vector &keystrokes,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> batontxid; ss >> pk; ss >> keystrokes) != 0 && e == EVAL_GAMES && f == 'K' ) + { + return(f); + } + return(0); +} + +uint8_t games_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &playertxid,CScript scriptPubKey) +{ + std::string name, description; std::vector vorigPubkey; + std::vector> oprets; + std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; + uint8_t e, f,*script; std::vector voutPubkeys; + tokenid = zeroid; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description,oprets)) == 'c' ) + { + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); + vopret = vopretNonfungible; + } + else if ( script[1] != 'R' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, oprets)) != 0 ) + { + GetOpretBlob(oprets, OPRETID_ROGUEGAMEDATA, vopretDummy); // blob from non-creation tx opret + vopret = vopretDummy; + } + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_GAMES && f == 'R' ) + { + return(f); + } + //fprintf(stderr,"e.%d f.%c game.%s playertxid.%s\n",e,f,gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + return(0); +} + +CScript games_finishopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CPubKey pk,std::vectorplayerdata,std::string pname) +{ + CScript opret; uint8_t evalcode = EVAL_GAMES; std::string symbol(ASSETCHAINS_SYMBOL); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << gametxid << symbol << pname << regslot << pk << playerdata ); + return(opret); +} + +uint8_t games_finishopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t ®slot, CPubKey &pk, std::vector &playerdata, std::string &symbol, std::string &pname,CScript scriptPubKey) +{ + std::string name, description; std::vector vorigPubkey; + std::vector> oprets, opretsDummy; + std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; + uint8_t e, f,*script; std::vector voutPubkeys; + tokenid = zeroid; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description, oprets)) == 'c' ) + { + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); + vopret = vopretNonfungible; + } + else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, opretsDummy)) != 0 ) + { + //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str()); + GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx + vopret = vopretNonfungible; + } + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> symbol; ss >> pname; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_GAMES && (f == 'H' || f == 'Q') ) + { + return(f); + } + fprintf(stderr,"SKIP obsolete: e.%d f.%c game.%s regslot.%d psize.%d\n",e,f,gametxid.GetHex().c_str(),regslot,(int32_t)playerdata.size()); + return(0); +} + CScript games_eventopret(uint32_t timestamp,CPubKey pk,std::vector sig,std::vector payload) { CScript opret; uint8_t evalcode = EVAL_GAMES; @@ -155,24 +249,6 @@ UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); - return(result); -} - -UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); - return(result); -} - -UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); - return(result); -} - int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vector payload,CPubKey pk) { static secp256k1_context *ctx; @@ -224,7 +300,7 @@ int32_t games_eventsign(uint32_t ×tamp,std::vector &sig,std::vecto } else return(-1); } -int32_t games_payload(CPubKey pk,uint32_t timestamp,std::vector payload) +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) { uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; if ( (len= payload.size()) > 36 ) @@ -279,7 +355,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( games_eventsign(timestamp,sig,payload,mypk) == 0 ) { GetOpReturnData(games_eventopret(timestamp,mypk,sig,payload),vopret); - games_payload(mypk,timestamp,payload); + games_payloadrecv(mypk,timestamp,payload); komodo_sendmessage(4,8,"events",vopret); result.push_back(Pair("gametxid",gametxid.GetHex())); result.push_back(Pair("eventid",(int64_t)eventid)); @@ -323,7 +399,7 @@ void komodo_netevent(std::vector message) fprintf(stderr,"SIG ERROR.%d ",retval); else if ( lagerr == 0 ) { - if ( games_payload(pk,timestamp,payload) == 0 ) // first time this is seen + if ( games_payloadrecv(pk,timestamp,payload) == 0 ) // first time this is seen { if ( (rand() % 10) == 0 ) { @@ -344,6 +420,1217 @@ void komodo_netevent(std::vector message) } } +void games_univalue(UniValue &result,const char *method,int64_t maxplayers,int64_t buyin) +{ + if ( method != 0 ) + { + result.push_back(Pair("name","games")); + result.push_back(Pair("method",method)); + } + if ( maxplayers > 0 ) + result.push_back(Pair("maxplayers",maxplayers)); + if ( buyin >= 0 ) + { + result.push_back(Pair("buyin",ValueFromAmount(buyin))); + if ( buyin == 0 ) + result.push_back(Pair("type","newbie")); + else result.push_back(Pair("type","buyin")); + } +} + +int32_t games_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0) +{ + uint256 hashBlock; int32_t i,numvouts; char coinaddr[64]; CPubKey gamespk; uint64_t txfee = 10000; + buyin = maxplayers = 0; + if ( (txid == zeroid || myGetTransaction(txid,tx,hashBlock) != 0) && (numvouts= tx.vout.size()) > 1 ) + { + if ( txid != zeroid ) + gameheight = komodo_blockheight(hashBlock); + else + { + txid = tx.GetHash(); + //fprintf(stderr,"set txid %s %llu\n",txid.GetHex().c_str(),(long long)CCgettxout(txid,0,1)); + } + if ( IsCClibvout(cp,tx,0,cp->unspendableCCaddr) == txfee && (unspentv0 == 0 || CCgettxout(txid,0,1,0) == txfee) ) + { + if ( games_newgameopreturndecode(buyin,maxplayers,tx.vout[numvouts-1].scriptPubKey) == 'G' ) + { + if ( maxplayers < 1 || maxplayers > GAMES_MAXPLAYERS || buyin < 0 ) + return(-6); + if ( numvouts > 2*maxplayers+1 ) + { + for (i=0; i= 0 ) + txid = spenttxid; + else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) + { + fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); + break; + } + txid = spenttxid; + vout = 0; + //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + if ( spentvini != 0 ) + break; + if ( n++ > GAMES_MAXITERATIONS ) + break; + } + if ( txid != zeroid ) + { + if ( myGetTransaction(txid,tx,hashBlock) != 0 ) + { + if ( (pindex= komodo_blockindex(hashBlock)) != 0 ) + { + if ( pindex->GetHeight() <= gameht+GAMES_MAXKEYSTROKESGAP ) + alive++; + } + } + } + } + } + else if ( registration_open != 0 ) + openslots++; + } + //fprintf(stderr,"numalive.%d openslots.%d\n",alive,openslots); + return(alive); +} + +void disp_gamesplayerdata(std::vector playerdata) +{ + struct games_player P; int32_t i; char packitemstr[512]; + if ( playerdata.size() > 0 ) + { + for (i=0; i>16,P.level,P.experience,P.dungeonlevel); + for (i=0; i playerdata,uint256 playertxid,uint256 tokenid,std::string symbol,std::string pname,uint256 gametxid) +{ + int32_t i,vout,spentvini,numvouts,n=0; uint256 txid,spenttxid,hashBlock; struct games_player P; char packitemstr[512],*datastr=0; UniValue obj(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx; + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + datastr = (char *)malloc(playerdata.size()*2+1); + for (i=0; i= 0 ) + txid = spenttxid; + else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) + { + fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); + break; + } + txid = spenttxid; + vout = 0; + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + for (i=0; i GAMES_MAXITERATIONS ) + break; + } + obj.push_back(Pair("gametxid",gametxid.GetHex())); + if ( txid != playertxid ) + obj.push_back(Pair("batontxid",txid.GetHex())); + obj.push_back(Pair("playertxid",playertxid.GetHex())); + if ( tokenid != zeroid ) + obj.push_back(Pair("tokenid",tokenid.GetHex())); + else obj.push_back(Pair("tokenid",playertxid.GetHex())); + if ( datastr != 0 ) + { + obj.push_back(Pair("data",datastr)); + free(datastr); + } + obj.push_back(Pair("pack",a)); + obj.push_back(Pair("packsize",(int64_t)P.packsize)); + obj.push_back(Pair("hitpoints",(int64_t)P.hitpoints)); + obj.push_back(Pair("strength",(int64_t)(P.strength&0xffff))); + obj.push_back(Pair("maxstrength",(int64_t)(P.strength>>16))); + obj.push_back(Pair("level",(int64_t)P.level)); + obj.push_back(Pair("experience",(int64_t)P.experience)); + obj.push_back(Pair("dungeonlevel",(int64_t)P.dungeonlevel)); + obj.push_back(Pair("chain",symbol)); + obj.push_back(Pair("pname",pname)); + return(obj); +} + +int32_t games_iterateplayer(uint256 ®istertxid,uint256 firsttxid,int32_t firstvout,uint256 lasttxid) // retrace playertxid vins to reach highlander <- this verifies player is valid and rogue_playerdataspend makes sure it can only be used once +{ + uint256 spenttxid,txid = firsttxid; int32_t spentvini,n,vout = firstvout; + registertxid = zeroid; + if ( vout < 0 ) + return(-1); + n = 0; + while ( (spentvini= myIsutxo_spent(spenttxid,txid,vout)) == 0 ) + { + txid = spenttxid; + vout = spentvini; + if ( registertxid == zeroid ) + registertxid = txid; + if ( ++n >= GAMES_MAXITERATIONS ) + { + fprintf(stderr,"games_iterateplayer n.%d, seems something is wrong\n",n); + return(-2); + } + } + if ( txid == lasttxid ) + return(0); + else + { + fprintf(stderr,"firsttxid.%s/v%d -> %s != last.%s\n",firsttxid.ToString().c_str(),firstvout,txid.ToString().c_str(),lasttxid.ToString().c_str()); + return(-1); + } +} + +int32_t games_playerdata(struct CCcontract_info *cp,uint256 &origplayergame,uint256 &tokenid,CPubKey &pk,std::vector &playerdata,std::string &symbol,std::string &pname,uint256 playertxid) +{ + uint256 origplayertxid,hashBlock,gametxid,registertxid; CTransaction gametx,playertx,highlandertx; std::vector vopret; uint8_t *script,e,f; int32_t i,regslot,gameheight,numvouts,maxplayers; int64_t buyin; + if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 && (numvouts= playertx.vout.size()) > 0 ) + { + if ( (f= games_finishopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,playertx.vout[numvouts-1].scriptPubKey)) == 'H' || f == 'Q' ) + { + origplayergame = gametxid; + if ( tokenid != zeroid ) + { + playertxid = tokenid; + if ( myGetTransaction(playertxid,playertx,hashBlock) == 0 || (numvouts= playertx.vout.size()) <= 0 ) + { + fprintf(stderr,"couldnt get tokenid.%s\n",playertxid.GetHex().c_str()); + return(-2); + } + } + if ( games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) + { + //fprintf(stderr,"playertxid.%s got vin.%s/v%d gametxid.%s iterate.%d\n",playertxid.ToString().c_str(),playertx.vin[1].prevout.hash.ToString().c_str(),(int32_t)playertx.vin[1].prevout.n-maxplayers,gametxid.ToString().c_str(),games_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid)); + if ( (tokenid != zeroid || playertx.vin[1].prevout.hash == gametxid) && games_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid) == 0 ) + { + // if registertxid has vin from pk, it can be used + return(0); + } else fprintf(stderr,"hash mismatch or illegal gametxid\n"); + } else fprintf(stderr,"invalid game %s\n",gametxid.GetHex().c_str()); + } //else fprintf(stderr,"invalid player funcid.%c\n",f); + } else fprintf(stderr,"couldnt get playertxid.%s\n",playertxid.GetHex().c_str()); + return(-1); +} + +int32_t games_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx,char *mygamesaddr) +{ + int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; char destaddr[64]; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); + if ( strcmp(mygamesaddr,destaddr) == 0 ) + return(1); + //else fprintf(stderr,"myaddr.%s vs %s\n",myrogueaddr,destaddr); + } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"vout %d is unspent\n",vout); + } + return(0); +} + +uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr) +{ + CBlockIndex *pindex; int32_t ht,openslots,delay,numplayers; uint256 hashBlock; uint64_t seed=0; char cmd[512]; CTransaction tx; + if ( myGetTransaction(gametxid,tx,hashBlock) != 0 && (pindex= komodo_blockindex(hashBlock)) != 0 ) + { + ht = pindex->GetHeight(); + delay = GAMES_REGISTRATION * (maxplayers > 1); + obj.push_back(Pair("height",ht)); + obj.push_back(Pair("start",ht+delay)); + if ( komodo_nextheight() > ht+delay ) + { + if ( (pindex= komodo_chainactive(ht+delay)) != 0 ) + { + hashBlock = pindex->GetBlockHash(); + obj.push_back(Pair("starthash",hashBlock.ToString())); + memcpy(&seed,&hashBlock,sizeof(seed)); + seed &= (1LL << 62) - 1; + obj.push_back(Pair("seed",(int64_t)seed)); + if ( games_iamregistered(maxplayers,gametxid,tx,mygamesaddr) > 0 ) + sprintf(cmd,"cc/%s %llu %s",GAMENAME,(long long)seed,gametxid.ToString().c_str()); + else sprintf(cmd,"./komodo-cli -ac_name=%s cclib register %d \"[%%22%s%%22]\"",ASSETCHAINS_SYMBOL,EVAL_GAMES,gametxid.ToString().c_str()); + obj.push_back(Pair("run",cmd)); + } + } + obj.push_back(Pair("alive",games_playersalive(openslots,numplayers,gametxid,maxplayers,ht,tx))); + obj.push_back(Pair("openslots",openslots)); + obj.push_back(Pair("numplayers",numplayers)); + } + obj.push_back(Pair("maxplayers",maxplayers)); + obj.push_back(Pair("buyin",ValueFromAmount(buyin))); + return(seed); +} + +UniValue games_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); std::vector playerdata; uint256 playertxid,tokenid,origplayergame;int32_t n; CPubKey pk; bits256 t; std::string symbol,pname; + result.push_back(Pair("result","success")); + games_univalue(result,"playerinfo",-1,-1); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + playertxid = juint256(jitem(params,0)); + if ( games_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) + return(cclib_error(result,"invalid playerdata")); + result.push_back(Pair("player",games_playerobj(playerdata,playertxid,tokenid,symbol,pname,origplayergame))); + } else return(cclib_error(result,"no playertxid")); + return(result); + } else return(cclib_error(result,"couldnt reparse params")); +} + +int32_t games_playerdataspend(CMutableTransaction &mtx,uint256 playertxid,int32_t vout,uint256 origplayergame) +{ + int64_t txfee = 10000; CTransaction tx; uint256 hashBlock; + if ( CCgettxout(playertxid,vout,1,0) == 1 ) // not sure if this is enough validation + { + mtx.vin.push_back(CTxIn(playertxid,vout,CScript())); + return(0); + } + else + { + vout = 0; + if ( myGetTransaction(playertxid,tx,hashBlock) != 0 && tx.vout[vout].nValue == 1 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) + { + if ( CCgettxout(playertxid,vout,1,0) == 1 ) // not sure if this is enough validation + { + mtx.vin.push_back(CTxIn(playertxid,vout,CScript())); + return(0); + } + } + return(-1); + } +} + +int64_t games_registrationbaton(CMutableTransaction &mtx,uint256 gametxid,CTransaction gametx,int32_t maxplayers) +{ + int32_t vout,j,r; int64_t nValue; + if ( gametx.vout.size() > maxplayers+1 ) + { + r = rand() % maxplayers; + for (j=0; j &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname) +{ + int32_t i,numvouts,spentvini,n,matches = 0; CPubKey pk; uint256 tid,active,spenttxid,tokenid,hashBlock,txid,origplayergame; CTransaction spenttx,matchtx,batontx; std::vector checkdata; CBlockIndex *pindex; char ccaddr[64],*keystrokes=0; + batonvalue = numkeys = numplayers = batonht = 0; + playertxid = batontxid = zeroid; + if ( keystrokesp != 0 ) + *keystrokesp = 0; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + numplayers++; + Getscriptaddress(ccaddr,spenttx.vout[0].scriptPubKey); + if ( strcmp(destaddr,ccaddr) == 0 ) + { + matches++; + regslot = i; + matchtx = spenttx; + } //else fprintf(stderr,"%d+1 doesnt match %s vs %s\n",i,ccaddr,destaddr); + } //else fprintf(stderr,"%d+1 couldnt find spenttx.%s\n",i,spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"%d+1 unspent\n",i); + } + if ( matches == 1 ) + { + numvouts = matchtx.vout.size(); + //fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts); + if ( games_registeropretdecode(txid,tokenid,playertxid,matchtx.vout[numvouts-1].scriptPubKey) == 'R' )//&& txid == gametxid ) + { + //fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + if ( tokenid != zeroid ) + active = tokenid; + else active = playertxid; + if ( active == zeroid || games_playerdata(cp,origplayergame,tid,pk,playerdata,symbol,pname,active) == 0 ) + { + txid = matchtx.GetHash(); + //fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str()); + n = 0; + while ( CCgettxout(txid,0,1,0) < 0 ) + { + spenttxid = zeroid; + spentvini = -1; + if ( (spentvini= myIsutxo_spent(spenttxid,txid,0)) >= 0 ) + txid = spenttxid; + else + { + if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,0) == 0 || spenttxid == zeroid ) + { + fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); + return(-2); + } + } + txid = spenttxid; + //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + if ( spentvini != 0 ) // game is over? + { + return(0); + } + if ( keystrokesp != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 ) + { + uint256 g,b; CPubKey p; std::vector k; + if ( games_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' ) + { + keystrokes = (char *)realloc(keystrokes,numkeys + (int32_t)k.size()); + for (i=0; i= GAMES_MAXITERATIONS ) + { + fprintf(stderr,"games_findbaton n.%d, seems something is wrong\n",n); + return(-5); + } + } + //fprintf(stderr,"set baton %s\n",txid.GetHex().c_str()); + batontxid = txid; + batonvout = 0; // not vini + // how to detect timeout, bailedout, highlander + hashBlock = zeroid; + if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 0 ) + { + if ( hashBlock == zeroid ) + batonht = komodo_nextheight(); + else if ( (pindex= komodo_blockindex(hashBlock)) == 0 ) + return(-4); + else batonht = pindex->GetHeight(); + batonvalue = batontx.vout[0].nValue; + //printf("batonht.%d keystrokes[%d]\n",batonht,numkeys); + return(0); + } else fprintf(stderr,"couldnt find baton\n"); + } else fprintf(stderr,"error with playerdata\n"); + } else fprintf(stderr,"findbaton opret error\n"); + } + return(-1); +} + +UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey gamespk,mypk; char *jsonstr; uint64_t inputsum,change,required,buyin=0; int32_t i,n,maxplayers = 1; + if ( txfee == 0 ) + txfee = 10000; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + maxplayers = juint(jitem(params,0),0); + if ( n > 1 ) + buyin = jdouble(jitem(params,1),0) * COIN + 0.0000000049; + } + } + if ( maxplayers < 1 || maxplayers > GAMES_MAXPLAYERS ) + return(cclib_error(result,"illegal maxplayers")); + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + games_univalue(result,"newgame",maxplayers,buyin); + required = (3*txfee + maxplayers*(GAMES_REGISTRATIONSIZE+txfee)); + if ( (inputsum= AddCClibInputs(cp,mtx,gamespk,required,16,cp->unspendableCCaddr)) >= required ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,gamespk)); + for (i=0; ievalcode,GAMES_REGISTRATIONSIZE,gamespk,gamespk)); + for (i=0; ievalcode,txfee,gamespk,gamespk)); + if ( (change= inputsum - required) >= txfee ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,change,gamespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,games_newgameopret(buyin,maxplayers)); + return(games_rawtxresult(result,rawtx,1)); + } + else return(cclib_error(result,"illegal maxplayers")); + return(result); +} + +UniValue games_list(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); uint256 txid,hashBlock,gametxid,tokenid,playertxid; int32_t vout,maxplayers,gameheight,numvouts; CPubKey gamespk,mypk; char coinaddr[64]; CTransaction tx,gametx; int64_t buyin; + std::vector > addressIndex; + gamespk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + GetCCaddress1of2(cp,coinaddr,gamespk,mypk); + SetCCtxids(addressIndex,coinaddr); + games_univalue(result,"games",-1,-1); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( vout == 0 ) + { + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + if ( games_registeropretdecode(gametxid,tokenid,playertxid,tx.vout[numvouts-1].scriptPubKey) == 'R' ) + { + if ( games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) + { + if ( CCgettxout(txid,vout,1,0) < 0 ) + b.push_back(gametxid.GetHex()); + else a.push_back(gametxid.GetHex()); + } + } + } + } + } + result.push_back(Pair("pastgames",b)); + result.push_back(Pair("games",a)); + result.push_back(Pair("numgames",(int64_t)(a.size()+b.size()))); + return(result); +} + +UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 txid,hashBlock; CTransaction tx; int32_t openslots,maxplayers,numplayers,gameheight,nextheight,vout,numvouts; CPubKey gamespk; char coinaddr[64]; + std::vector > unspentOutputs; + gamespk = GetUnspendable(cp,0); + GetCCaddress(cp,coinaddr,gamespk); + SetCCunspents(unspentOutputs,coinaddr); + nextheight = komodo_nextheight(); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( it->second.satoshis != txfee || vout != 0 ) // reject any that are not highlander markers + continue; + if ( games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,1) == 0 && nextheight <= gameheight+GAMES_MAXKEYSTROKESGAP ) + { + games_playersalive(openslots,numplayers,txid,maxplayers,gameheight,tx); + if ( openslots > 0 ) + a.push_back(txid.GetHex()); + } + } + result.push_back(Pair("result","success")); + games_univalue(result,"pending",-1,-1); + result.push_back(Pair("pending",a)); + result.push_back(Pair("numpending",(int64_t)a.size())); + return(result); +} + +UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,gameheight,maxplayers,numvouts; uint256 txid; CTransaction tx; int64_t buyin; uint64_t seed; bits256 t; char myaddr[64],str[64]; CPubKey mypk,gamespk; + result.push_back(Pair("name","games")); + result.push_back(Pair("method","info")); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + txid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",txid.GetHex())); + if ( games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,0) == 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("gameheight",(int64_t)gameheight)); + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,myaddr,gamespk,mypk); + seed = games_gamefields(result,maxplayers,buyin,txid,myaddr); + result.push_back(Pair("seed",(int64_t)seed)); + for (i=0; i GAMES_REGISTRATIONSIZE 1of2 registration baton from creategame + // vin1 -> optional nonfungible character vout @ + // vin2 -> original creation TCBOO playerdata used + // vin3+ -> buyin + // vout0 -> keystrokes/completion baton + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); char destaddr[64],coinaddr[64]; uint256 tokenid,gametxid,origplayergame,playertxid,hashBlock; int32_t err,maxplayers,gameheight,n,numvouts,vout=1; int64_t inputsum,buyin,CCchange=0; CPubKey pk,mypk,gamespk,burnpk; CTransaction tx,playertx; std::vector playerdata; std::string rawtx,symbol,pname; bits256 t; + + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); + gamespk = GetUnspendable(cp,0); + games_univalue(result,"register",-1,-1); + playertxid = tokenid = zeroid; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + gametxid = juint256(jitem(params,0)); + if ( (err= games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1)) == 0 ) + { + if ( n > 1 ) + { + playertxid = juint256(jitem(params,1)); + if ( games_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) + return(cclib_error(result,"couldnt extract valid playerdata")); + if ( tokenid != zeroid ) // if it is tokentransfer this will be 0 + vout = 1; + } + if ( komodo_nextheight() > gameheight + GAMES_MAXKEYSTROKESGAP ) + return(cclib_error(result,"didnt register in time, GAMES_MAXKEYSTROKESGAP")); + games_univalue(result,0,maxplayers,buyin); + GetCCaddress1of2(cp,coinaddr,gamespk,mypk); + if ( games_iamregistered(maxplayers,gametxid,tx,coinaddr) > 0 ) + return(cclib_error(result,"already registered")); + if ( (inputsum= games_registrationbaton(mtx,gametxid,tx,maxplayers)) != GAMES_REGISTRATIONSIZE ) + return(cclib_error(result,"couldnt find available registration baton")); + else if ( playertxid != zeroid && games_playerdataspend(mtx,playertxid,vout,origplayergame) < 0 ) + return(cclib_error(result,"couldnt find playerdata to spend")); + else if ( buyin > 0 && AddNormalinputs(mtx,mypk,buyin,64) < buyin ) + return(cclib_error(result,"couldnt find enough normal funds for buyin")); + if ( tokenid != zeroid ) + { + mtx.vin.push_back(CTxIn(tokenid,0)); // spending cc marker as token is burned + char unspendableTokenAddr[64]; uint8_t tokenpriv[32]; struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + CPubKey unspPk = GetUnspendable(cpTokens, tokenpriv); + GetCCaddress(cpTokens, unspendableTokenAddr, unspPk); + CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); + } + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,buyin + inputsum - txfee,gamespk,mypk)); + GetCCaddress1of2(cp,destaddr,gamespk,gamespk); + CCaddr1of2set(cp,gamespk,gamespk,cp->CCpriv,destaddr); + mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk)); + + uint8_t e, funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; + CScript opretRegister = games_registeropret(gametxid, playertxid); + if ( playertxid != zeroid ) + { + voutPubkeysEmpty.push_back(burnpk); + if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 ) + { + std::vector> oprets; + if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tid, voutPubkeys, oprets)) != 0) + { // if token in the opret + didtx = 1; + if ( funcid == 'c' ) + tid = tokenid == zeroid ? playertxid : tokenid; + vscript_t vopretRegister; + GetOpReturnData(opretRegister, vopretRegister); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, + EncodeTokenOpRet(tid, voutPubkeysEmpty /*=never spent*/, std::make_pair(OPRETID_ROGUEGAMEDATA, vopretRegister))); + } + } + } + if ( didtx == 0 ) + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opretRegister); + + return(games_rawtxresult(result,rawtx,1)); + } else return(cclib_error(result,"invalid gametxid")); + } else return(cclib_error(result,"no gametxid")); + } else return(cclib_error(result,"couldnt reparse params")); +} + +UniValue games_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + // vin0 -> baton from registration or previous keystrokes + // vout0 -> new baton + // opret -> user input chars + // being killed should auto broadcast (possible to be suppressed?) + // respawn to be prevented by including timestamps + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); + UniValue result(UniValue::VOBJ); CPubKey gamespk,mypk; uint256 gametxid,playertxid,batontxid; int64_t batonvalue,buyin; std::vector keystrokes,playerdata; int32_t numplayers,regslot,numkeys,batonht,batonvout,n,elapsed,gameheight,maxplayers; CTransaction tx; CTxOut txout; char *keystrokestr,destaddr[64]; std::string rawtx,symbol,pname; bits256 t; uint8_t mypriv[32]; + // if ( txfee == 0 ) + txfee = 1000; // smaller than normal on purpose + games_univalue(result,"keystrokes",-1,-1); + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 2 && (keystrokestr= jstr(jitem(params,1),0)) != 0 ) + { + gametxid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",gametxid.GetHex())); + result.push_back(Pair("keystrokes",keystrokestr)); + keystrokes = ParseHex(keystrokestr); + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,destaddr,gamespk,mypk); + if ( games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1) == 0 ) + { + if ( games_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,tx,maxplayers,destaddr,numplayers,symbol,pname) == 0 ) + { + result.push_back(Pair("batontxid",batontxid.GetHex())); + result.push_back(Pair("playertxid",playertxid.GetHex())); + if ( maxplayers == 1 || nextheight <= batonht+GAMES_MAXKEYSTROKESGAP ) + { + mtx.vin.push_back(CTxIn(batontxid,batonvout,CScript())); //this validates user if pk + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,batonvalue-txfee,gamespk,mypk)); + Myprivkey(mypriv); + CCaddr1of2set(cp,gamespk,mypk,mypriv,destaddr); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,games_keystrokesopret(gametxid,batontxid,mypk,keystrokes)); + //fprintf(stderr,"KEYSTROKES.(%s)\n",rawtx.c_str()); + return(games_rawtxresult(result,rawtx,1)); + } else return(cclib_error(result,"keystrokes tx was too late")); + } else return(cclib_error(result,"couldnt find batontxid")); + } else return(cclib_error(result,"invalid gametxid")); + } else return(cclib_error(result,"couldnt reparse params")); +} + +char *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr) +{ + CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64],*keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP; + gamespk = GetUnspendable(cp,0); + *numkeysp = 0; + seed = 0; + num = numkeys = 0; + playertxid = zeroid; + str[0] = 0; + if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 ) + { + if ( (retval= games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,gamesaddr,numplayers,symbol,pname)) == 0 ) + { + UniValue obj; + seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr); + //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + for (i=0; i no playerdata\n"); + newdata.resize(0); + *numkeysp = numkeys; + return(keystrokes); + /* P.gold = (P.gold * 8) / 10; + if ( keystrokes != 0 ) + { + free(keystrokes); + keystrokes = 0; + *numkeysp = 0; + return(keystrokes); + }*/ + } + else + { + sprintf(str,"$$$gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",endP.gold,endP.hitpoints,endP.strength&0xffff,endP.strength>>16,endP.level,endP.experience,endP.dungeonlevel); + //fprintf(stderr,"%s\n",str); + *numkeysp = numkeys; + return(keystrokes); + } + } else num = 0; + } + else + { + fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval); + if ( keystrokes != 0 ) + free(keystrokes), keystrokes = 0; + } + } else fprintf(stderr,"extractgame: invalid game\n"); + //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str()); + return(0); +} + +UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); CPubKey pk,gamespk; int32_t i,n,numkeys,flag = 0; uint64_t seed; char str[512],gamesaddr[64],*pubstr,*hexstr,*keystrokes = 0; std::vector newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33]; + pk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + result.push_back(Pair("name","games")); + result.push_back(Pair("method","extract")); + gamesaddr[0] = 0; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + gametxid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",gametxid.GetHex())); + if ( n == 2 ) + { + if ( (pubstr= jstr(jitem(params,1),0)) != 0 ) + { + if (strlen(pubstr) == 66 ) + { + decode_hex(pub33,33,pubstr); + pk = buf2pk(pub33); + } + else if ( strlen(pubstr) < 36 ) + strcpy(gamesaddr,pubstr); + } + //fprintf(stderr,"gametxid.%s %s\n",gametxid.GetHex().c_str(),pubstr); + } + if ( gamesaddr[0] == 0 ) + GetCCaddress1of2(cp,gamesaddr,gamespk,pk); + result.push_back(Pair("gamesaddr",gamesaddr)); + str[0] = 0; + if ( (keystrokes= games_extractgame(1,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) + { + result.push_back(Pair("status","success")); + flag = 1; + hexstr = (char *)malloc(numkeys*2 + 1); + for (i=0; iamulet != 0 ) + mult *= 5; + dungeonlevel = P->dungeonlevel; + if ( P->amulet != 0 && dungeonlevel < 26 ) + dungeonlevel = 26; + cashout = (uint64_t)P->gold * P->gold * mult * dungeonlevel; + return(cashout); +} + +int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) +{ + static uint32_t good,bad; static uint256 prevgame; + char str[512],*keystrokes,gamesaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; + *cashoutp = 0; + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,gamesaddr,gamespk,pk); + if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) + { + free(keystrokes); + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); + remove(fname); + + for (i=0; i no playerdata, good.%d bad.%d\n",good,bad); + } + *cashoutp = 0; + return(0); + } + } + if ( gametxid != prevgame ) + { + prevgame = gametxid; + bad++; + disp_gamesplayerdata(newdata); + disp_gamesplayerdata(playerdata); + fprintf(stderr,"%s playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d\n",gametxid.GetHex().c_str(),P.gold,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel); + fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,gamesaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); + } + } + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); + remove(fname); + //fprintf(stderr,"no keys games_extractgame %s\n",gametxid.GetHex().c_str()); + return(-1); +} + +UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + //vin0 -> highlander vout from creategame TCBOO + //vin1 -> keystrokes baton of completed game, must be last to quit or first to win, only spent registration batons matter. If more than 60 blocks since last keystrokes, it is forfeit + //vins2+ -> rest of unspent registration utxo so all newgame vouts are spent + //vout0 -> nonfungible character with pack @ + //vout1 -> 1% ingame gold and all the buyins + + // detect if last to bailout + // vin0 -> kestrokes baton of completed game with Q + // vout0 -> playerdata marker + // vout0 -> 1% ingame gold + // get any playerdata, get all keystrokes, replay game and compare final state + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); char *method = "bailout"; + UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,dungeonlevel,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64],*keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid; + struct CCcontract_info *cpTokens, tokensC; + + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,mygamesaddr,gamespk,mypk); + result.push_back(Pair("name","games")); + result.push_back(Pair("method",method)); + result.push_back(Pair("mygamesaddr",mygamesaddr)); + if ( strcmp(method,"bailout") == 0 ) + { + funcid = 'Q'; + //mult = 10; //100000; + } + else + { + funcid = 'H'; + //mult = 20; //200000; + } + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + gametxid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",gametxid.GetHex())); + if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,1)) == 0 ) + { + if ( games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,mygamesaddr,numplayers,symbol,pname) == 0 ) + { + UniValue obj; struct games_player P; + seed = games_gamefields(obj,maxplayers,buyin,gametxid,mygamesaddr); + fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size()); + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + for (i=0; i 0 ) + { + newdata.resize(num); + for (i=0; i no playerdata\n"); + newdata.resize(0); + //P.gold = (P.gold * 8) / 10; + } + else + { + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens,NULL))); // marker to token cc addr, burnable and validated + mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,1,mypk)); + cashout = games_cashout(&P); + fprintf(stderr,"\nextracted $$$gold.%d -> %.8f GAME hp.%d strength.%d/%d level.%d exp.%d dl.%d n.%d amulet.%d\n",P.gold,(double)cashout/COIN,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel,n,P.amulet); + if ( funcid == 'H' && maxplayers > 1 ) + { + if ( P.amulet == 0 ) + { + if ( numplayers != maxplayers ) + return(cclib_error(result,"numplayers != maxplayers")); + else if ( games_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 ) + return(cclib_error(result,"highlander must be a winner or last one standing")); + } + cashout += numplayers * buyin; + } + if ( cashout > 0 ) + { + if ( (inputsum= AddCClibInputs(cp,mtx,gamespk,cashout,60,cp->unspendableCCaddr)) > cashout ) + CCchange = (inputsum - cashout); + else fprintf(stderr,"couldnt find enough utxos\n"); + } + mtx.vout.push_back(CTxOut(cashout,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + } + } + if ( CCchange + (batonvalue-3*txfee) >= txfee ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-3*txfee),gamespk)); + Myprivkey(mypriv); + CCaddr1of2set(cp,gamespk,mypk,mypriv,mygamesaddr); + CScript opret; + if ( pname.size() == 0 ) + pname = Games_pname; + if ( newdata.size() == 0 ) + { + opret = games_finishopret(funcid, gametxid, regslot, mypk, nodata,pname); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,opret); + //fprintf(stderr,"nodata finalizetx.(%s)\n",rawtx.c_str()); + } + else + { + opret = games_finishopret(funcid, gametxid, regslot, mypk, newdata,pname); + char seedstr[32]; + sprintf(seedstr,"%llu",(long long)seed); + std::vector vopretNonfungible; + GetOpReturnData(opret, vopretNonfungible); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), std::string(seedstr), gametxid.GetHex(), vopretNonfungible)); + } + return(games_rawtxresult(result,rawtx,1)); + } + result.push_back(Pair("result","success")); + } else fprintf(stderr,"illegal game err.%d\n",err); + } else fprintf(stderr,"parameters only n.%d\n",n); + } + return(result); +} + +UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 tokenid,gametxid,txid,hashBlock; CTransaction playertx,tx; int32_t maxplayers,vout,numvouts; std::vector playerdata; CPubKey gamespk,mypk,pk; std::string symbol,pname; char coinaddr[64]; + std::vector > unspentOutputs; + gamespk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + GetTokensCCaddress(cp,coinaddr,mypk); + SetCCunspents(unspentOutputs,coinaddr); + games_univalue(result,"players",-1,-1); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( it->second.satoshis != 1 || vout > 1 ) + continue; + if ( games_playerdata(cp,gametxid,tokenid,pk,playerdata,symbol,pname,txid) == 0 )//&& pk == mypk ) + { + a.push_back(txid.GetHex()); + //a.push_back(Pair("playerdata",games_playerobj(playerdata))); + } + } + result.push_back(Pair("playerdata",a)); + result.push_back(Pair("numplayerdata",(int64_t)a.size())); + return(result); +} + +UniValue games_list(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); uint256 txid,hashBlock,gametxid,tokenid,playertxid; int32_t vout,maxplayers,gameheight,numvouts; CPubKey gamespk,mypk; char coinaddr[64]; CTransaction tx,gametx; int64_t buyin; + std::vector > addressIndex; + gamespk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + GetCCaddress1of2(cp,coinaddr,gamespk,mypk); + SetCCtxids(addressIndex,coinaddr); + games_univalue(result,"games",-1,-1); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( vout == 0 ) + { + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + if ( games_registeropretdecode(gametxid,tokenid,playertxid,tx.vout[numvouts-1].scriptPubKey) == 'R' ) + { + if ( games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) + { + if ( CCgettxout(txid,vout,1,0) < 0 ) + b.push_back(gametxid.GetHex()); + else a.push_back(gametxid.GetHex()); + } + } + } + } + } + result.push_back(Pair("pastgames",b)); + result.push_back(Pair("games",a)); + result.push_back(Pair("numgames",(int64_t)(a.size()+b.size()))); + return(result); +} + +UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); int32_t n; char *namestr = 0; + games_univalue(result,"setname",-1,-1); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + if ( (namestr= jstri(params,0)) != 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("pname",namestr)); + Games_pname = namestr; + return(result); + } + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt get name")); + return(result); +} + bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) { return(true); diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index c1fc63b48..2d3f56a48 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -11,28 +11,50 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define EVAL_GAMES (EVAL_FAUCET2+1) #define GAMES_TXFEE 10000 +#define GAMES_MAXITERATIONS 777 +#define GAMES_MAXKEYSTROKESGAP 60 +#define GAMES_MAYPLAYERS 64 +#define GAMES_REGISTRATIONSIZE (100 * 10000) #define GAMES_RNGMULT 11109 #define GAMES_RNGOFFSET 13849 #define GAMES_MAXRNGS 10000 #define MYCCNAME "games" +#define GAMENAME "sudoku" #define RPC_FUNCS \ { (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, \ - { (char *)MYCCNAME, (char *)"create", (char *)"game,minplayers,maxplayers,buyin,numblocks", 5, 5, ' ', EVAL_GAMES }, \ - { (char *)MYCCNAME, (char *)"info", (char *)"txid", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"players", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"list", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"pending", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"setname", (char *)"pname", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"create", (char *)"maxplayers,buyin", 2, 2, 'C', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"playerinfo", (char *)"playertxid", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"info", (char *)"gametxid", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"keystrokes", (char *)"txid,hexstr", 2, 2, 'K', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"finish", (char *)"gametxid", 1, 1, 'Q', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"events", (char *)"eventshex [gametxid [eventid]]", 1, 3, ' ', EVAL_GAMES }, \ - { (char *)MYCCNAME, (char *)"register", (char *)"txid", 1, 1, ' ', EVAL_GAMES }, + { (char *)MYCCNAME, (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"register", (char *)"gametxid [playertxid]", 1, 2, 'R', EVAL_GAMES }, + bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_list(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); -UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #define CUSTOM_DISPATCH \ if ( cp->evalcode == EVAL_GAMES ) \ @@ -58,4 +80,21 @@ if ( cp->evalcode == EVAL_GAMES ) \ } \ } +#define MAXPACK 23 +struct games_packitem +{ + int32_t type,launch,count,which,hplus,dplus,arm,flags,group; + char damage[8],hurldmg[8]; +}; + +struct games_player +{ + int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; + struct games_packitem roguepack[MAXPACK]; +}; + +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis); +void games_packitemstr(char *packitemstr,struct games_packitem *item); + + #endif diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index caf37b566..6f507e51e 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -1594,7 +1594,10 @@ bool rogue_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C return eval->Invalid("mismatched playerdata"); } if ( funcid == 'H' ) + { cashout *= 2; + cashout += numplayers * buyin; + } sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d txid.%s %.8f vs vout2 %.8f",tokentx,decoded,height,txid.GetHex().c_str(),(double)cashout/COIN,(double)tx.vout[2].nValue/COIN); if ( strcmp(laststr,cashstr) != 0 ) { From ff5abe4b6c3e5569216becbb71851f3ff8c1d207 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 06:32:53 -1100 Subject: [PATCH 151/787] Gamespack --- src/cc/gamescc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 2d3f56a48..51432105c 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -13,7 +13,7 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define GAMES_TXFEE 10000 #define GAMES_MAXITERATIONS 777 #define GAMES_MAXKEYSTROKESGAP 60 -#define GAMES_MAYPLAYERS 64 +#define GAMES_MAXPLAYERS 64 #define GAMES_REGISTRATIONSIZE (100 * 10000) #define GAMES_RNGMULT 11109 @@ -90,7 +90,7 @@ struct games_packitem struct games_player { int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; - struct games_packitem roguepack[MAXPACK]; + struct games_packitem gamespack[MAXPACK]; }; int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis); From aad5cac5cca142e9d5b734ff06b6333b08eeae6c Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 06:33:42 -1100 Subject: [PATCH 152/787] Test --- src/cc/gamescc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 51432105c..e060016ff 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -15,6 +15,7 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define GAMES_MAXKEYSTROKESGAP 60 #define GAMES_MAXPLAYERS 64 #define GAMES_REGISTRATIONSIZE (100 * 10000) +#define GAMES_REGISTRATION 5 #define GAMES_RNGMULT 11109 #define GAMES_RNGOFFSET 13849 From 2a01bdd2d15807328e518243eb149de9b5681347 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 06:34:47 -1100 Subject: [PATCH 153/787] Games --- src/cc/gamescc.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index e060016ff..8a6a3b656 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -22,6 +22,8 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define GAMES_MAXRNGS 10000 #define MYCCNAME "games" + +std::string Games_pname; #define GAMENAME "sudoku" #define RPC_FUNCS \ @@ -94,7 +96,7 @@ struct games_player struct games_packitem gamespack[MAXPACK]; }; -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis); +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); void games_packitemstr(char *packitemstr,struct games_packitem *item); From a9684309b85bd81e749751ee6e05c68941e0b902 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 06:39:05 -1100 Subject: [PATCH 154/787] Fixes --- src/cc/gamescc.cpp | 76 ++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 2330d130c..3dd9244aa 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -700,13 +700,47 @@ int32_t games_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx, Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); if ( strcmp(mygamesaddr,destaddr) == 0 ) return(1); - //else fprintf(stderr,"myaddr.%s vs %s\n",myrogueaddr,destaddr); + //else fprintf(stderr,"myaddr.%s vs %s\n",mygamesaddr,destaddr); } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); } //else fprintf(stderr,"vout %d is unspent\n",vout); } return(0); } +void games_gameplayerinfo(struct CCcontract_info *cp,UniValue &obj,uint256 gametxid,CTransaction gametx,int32_t vout,int32_t maxplayers,char *mygamesaddr) +{ + // identify if bailout or quit or timed out + uint256 batontxid,spenttxid,gtxid,ptxid,tokenid,hashBlock,playertxid; CTransaction spenttx,batontx; int32_t numplayers,regslot,numkeys,batonvout,batonht,retval; int64_t batonvalue; std::vector playerdata; char destaddr[64]; std::string symbol,pname; + destaddr[0] = 0; + if ( myIsutxo_spent(spenttxid,gametxid,vout) >= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); + } + obj.push_back(Pair("slot",(int64_t)vout-1)); + if ( (retval= games_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,destaddr,numplayers,symbol,pname)) == 0 ) + { + if ( CCgettxout(gametxid,maxplayers+vout,1,0) == 10000 ) + { + if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 1 ) + { + if ( games_registeropretdecode(gtxid,tokenid,ptxid,batontx.vout[batontx.vout.size()-1].scriptPubKey) == 'R' && ptxid == playertxid && gtxid == gametxid ) + obj.push_back(Pair("status","registered")); + else obj.push_back(Pair("status","alive")); + } else obj.push_back(Pair("status","error")); + } else obj.push_back(Pair("status","finished")); + obj.push_back(Pair("baton",batontxid.ToString())); + obj.push_back(Pair("tokenid",tokenid.ToString())); + obj.push_back(Pair("batonaddr",destaddr)); + obj.push_back(Pair("ismine",strcmp(mygamesaddr,destaddr)==0)); + obj.push_back(Pair("batonvout",(int64_t)batonvout)); + obj.push_back(Pair("batonvalue",ValueFromAmount(batonvalue))); + obj.push_back(Pair("batonht",(int64_t)batonht)); + if ( playerdata.size() > 0 ) + obj.push_back(Pair("player",games_playerobj(playerdata,playertxid,tokenid,symbol,pname,gametxid))); + } else fprintf(stderr,"findbaton err.%d\n",retval); +} + uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr) { CBlockIndex *pindex; int32_t ht,openslots,delay,numplayers; uint256 hashBlock; uint64_t seed=0; char cmd[512]; CTransaction tx; @@ -939,42 +973,6 @@ UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -UniValue games_list(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); uint256 txid,hashBlock,gametxid,tokenid,playertxid; int32_t vout,maxplayers,gameheight,numvouts; CPubKey gamespk,mypk; char coinaddr[64]; CTransaction tx,gametx; int64_t buyin; - std::vector > addressIndex; - gamespk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - GetCCaddress1of2(cp,coinaddr,gamespk,mypk); - SetCCtxids(addressIndex,coinaddr); - games_univalue(result,"games",-1,-1); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); - if ( vout == 0 ) - { - if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) - { - if ( games_registeropretdecode(gametxid,tokenid,playertxid,tx.vout[numvouts-1].scriptPubKey) == 'R' ) - { - if ( games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) - { - if ( CCgettxout(txid,vout,1,0) < 0 ) - b.push_back(gametxid.GetHex()); - else a.push_back(gametxid.GetHex()); - } - } - } - } - } - result.push_back(Pair("pastgames",b)); - result.push_back(Pair("games",a)); - result.push_back(Pair("numgames",(int64_t)(a.size()+b.size()))); - return(result); -} - UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 txid,hashBlock; CTransaction tx; int32_t openslots,maxplayers,numplayers,gameheight,nextheight,vout,numvouts; CPubKey gamespk; char coinaddr[64]; @@ -1029,7 +1027,7 @@ UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( CCgettxout(txid,i+1,1,0) < 0 ) { UniValue obj(UniValue::VOBJ); - games_playerinfo(cp,obj,txid,tx,i+1,maxplayers,myaddr); + games_gameplayerinfo(cp,obj,txid,tx,i+1,maxplayers,myaddr); a.push_back(obj); } else if ( 0 ) @@ -1423,7 +1421,7 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) // vout0 -> playerdata marker // vout0 -> 1% ingame gold // get any playerdata, get all keystrokes, replay game and compare final state - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); char *method = "bailout"; + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); char *method = (char *)"bailout"; UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,dungeonlevel,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64],*keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid; struct CCcontract_info *cpTokens, tokensC; From e6371013aaff8463649f2dbdee227dc81559587c Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 06:41:47 -1100 Subject: [PATCH 155/787] Reorder --- src/cc/gamescc.cpp | 68 +++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 3dd9244aa..aca989fd3 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -707,40 +707,6 @@ int32_t games_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx, return(0); } -void games_gameplayerinfo(struct CCcontract_info *cp,UniValue &obj,uint256 gametxid,CTransaction gametx,int32_t vout,int32_t maxplayers,char *mygamesaddr) -{ - // identify if bailout or quit or timed out - uint256 batontxid,spenttxid,gtxid,ptxid,tokenid,hashBlock,playertxid; CTransaction spenttx,batontx; int32_t numplayers,regslot,numkeys,batonvout,batonht,retval; int64_t batonvalue; std::vector playerdata; char destaddr[64]; std::string symbol,pname; - destaddr[0] = 0; - if ( myIsutxo_spent(spenttxid,gametxid,vout) >= 0 ) - { - if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) - Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); - } - obj.push_back(Pair("slot",(int64_t)vout-1)); - if ( (retval= games_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,destaddr,numplayers,symbol,pname)) == 0 ) - { - if ( CCgettxout(gametxid,maxplayers+vout,1,0) == 10000 ) - { - if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 1 ) - { - if ( games_registeropretdecode(gtxid,tokenid,ptxid,batontx.vout[batontx.vout.size()-1].scriptPubKey) == 'R' && ptxid == playertxid && gtxid == gametxid ) - obj.push_back(Pair("status","registered")); - else obj.push_back(Pair("status","alive")); - } else obj.push_back(Pair("status","error")); - } else obj.push_back(Pair("status","finished")); - obj.push_back(Pair("baton",batontxid.ToString())); - obj.push_back(Pair("tokenid",tokenid.ToString())); - obj.push_back(Pair("batonaddr",destaddr)); - obj.push_back(Pair("ismine",strcmp(mygamesaddr,destaddr)==0)); - obj.push_back(Pair("batonvout",(int64_t)batonvout)); - obj.push_back(Pair("batonvalue",ValueFromAmount(batonvalue))); - obj.push_back(Pair("batonht",(int64_t)batonht)); - if ( playerdata.size() > 0 ) - obj.push_back(Pair("player",games_playerobj(playerdata,playertxid,tokenid,symbol,pname,gametxid))); - } else fprintf(stderr,"findbaton err.%d\n",retval); -} - uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr) { CBlockIndex *pindex; int32_t ht,openslots,delay,numplayers; uint256 hashBlock; uint64_t seed=0; char cmd[512]; CTransaction tx; @@ -936,6 +902,40 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,char **ke return(-1); } +void games_gameplayerinfo(struct CCcontract_info *cp,UniValue &obj,uint256 gametxid,CTransaction gametx,int32_t vout,int32_t maxplayers,char *mygamesaddr) +{ + // identify if bailout or quit or timed out + uint256 batontxid,spenttxid,gtxid,ptxid,tokenid,hashBlock,playertxid; CTransaction spenttx,batontx; int32_t numplayers,regslot,numkeys,batonvout,batonht,retval; int64_t batonvalue; std::vector playerdata; char destaddr[64]; std::string symbol,pname; + destaddr[0] = 0; + if ( myIsutxo_spent(spenttxid,gametxid,vout) >= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); + } + obj.push_back(Pair("slot",(int64_t)vout-1)); + if ( (retval= games_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,destaddr,numplayers,symbol,pname)) == 0 ) + { + if ( CCgettxout(gametxid,maxplayers+vout,1,0) == 10000 ) + { + if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 1 ) + { + if ( games_registeropretdecode(gtxid,tokenid,ptxid,batontx.vout[batontx.vout.size()-1].scriptPubKey) == 'R' && ptxid == playertxid && gtxid == gametxid ) + obj.push_back(Pair("status","registered")); + else obj.push_back(Pair("status","alive")); + } else obj.push_back(Pair("status","error")); + } else obj.push_back(Pair("status","finished")); + obj.push_back(Pair("baton",batontxid.ToString())); + obj.push_back(Pair("tokenid",tokenid.ToString())); + obj.push_back(Pair("batonaddr",destaddr)); + obj.push_back(Pair("ismine",strcmp(mygamesaddr,destaddr)==0)); + obj.push_back(Pair("batonvout",(int64_t)batonvout)); + obj.push_back(Pair("batonvalue",ValueFromAmount(batonvalue))); + obj.push_back(Pair("batonht",(int64_t)batonht)); + if ( playerdata.size() > 0 ) + obj.push_back(Pair("player",games_playerobj(playerdata,playertxid,tokenid,symbol,pname,gametxid))); + } else fprintf(stderr,"findbaton err.%d\n",retval); +} + UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); From e97460a58dee3e50f3eaf1ee1b78f2feb7dbc61d Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 06:43:18 -1100 Subject: [PATCH 156/787] Stub functions --- src/cc/gamescc.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index aca989fd3..b68c9f55c 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1634,4 +1634,14 @@ bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C return(true); } +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) +{ + return(-1); +} + +void games_packitemstr(char *packitemstr,struct games_packitem *item) +{ + sprintf(packitemstr,"not yet"); +} + From c1be8157e76b8c3f7d225d9afdde494b2c5565f4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 06:51:16 -1100 Subject: [PATCH 157/787] Dont advance keystrokes unless keystrokes tx worked --- src/cc/rogue/main.c | 15 ++++++++++----- src/cc/rogue/rogue.c | 16 ++++++---------- src/cc/rogue/rogue.h | 2 +- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c index f146cfb4d..616ade086 100644 --- a/src/cc/rogue/main.c +++ b/src/cc/rogue/main.c @@ -784,9 +784,9 @@ int32_t rogue_sendrawtransaction(char *rawtx) return(retval); } -void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +int32_t rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) { - char cmd[16384],hexstr[16384],params[32768],*retstr,*rawtx,*pastkeys,*pastcmp,*keys; int32_t i,len,numpastkeys; cJSON *retjson,*resobj; + char cmd[16384],hexstr[16384],params[32768],*retstr,*rawtx,*pastkeys,*pastcmp,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; //fprintf(stderr,"rogue_progress num.%d\n",num); if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) { @@ -795,7 +795,7 @@ void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char * if ( rogue_sendrawtransaction(rs->keystrokeshex) == 0 ) { if ( waitflag == 0 ) - return; + return(0); else if ( 0 ) { while ( rogue_sendrawtransaction(rs->keystrokeshex) == 0 ) @@ -866,8 +866,12 @@ void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char * { if ( rs->keystrokeshex != 0 ) free(rs->keystrokeshex); - rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); - strcpy(rs->keystrokeshex,rawtx); + if ( (errstr= jstr(resobj,"error")) == 0 ) + { + rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); + strcpy(rs->keystrokeshex,rawtx); + retflag = 1; + } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); //fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); } free_json(retjson); @@ -885,6 +889,7 @@ void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char * } } } + return(retflag); } int32_t rogue_setplayerdata(struct rogue_state *rs,char *gametxidstr) diff --git a/src/cc/rogue/rogue.c b/src/cc/rogue/rogue.c index 7a8e38c15..c687fd019 100644 --- a/src/cc/rogue/rogue.c +++ b/src/cc/rogue/rogue.c @@ -158,7 +158,7 @@ int32_t flushkeystrokes_local(struct rogue_state *rs,int32_t waitflag) #ifdef BUILD_ROGUE // stubs for inside daemon -void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +int32_t rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) { } @@ -172,15 +172,11 @@ int32_t flushkeystrokes(struct rogue_state *rs,int32_t waitflag) { if ( rs->num > 0 ) { - // need to get existing keystrokes including mempool - // create keystrokes that are not saved - //rs->keytxid = rogue_progress(rs,waitflag,rs->seed,&rs->buffered[rs->lastnum],rs->num - rs->lastnum); - //rs->lastnum = rs->num; - rogue_progress(rs,waitflag,rs->seed,rs->buffered,rs->num); - flushkeystrokes_local(rs,waitflag); - memset(rs->buffered,0,sizeof(rs->buffered)); - //rs->num = 0; - //rs->counter++; + if ( rogue_progress(rs,waitflag,rs->seed,rs->buffered,rs->num) > 0 ) + { + flushkeystrokes_local(rs,waitflag); + memset(rs->buffered,0,sizeof(rs->buffered)); + } } return(0); } diff --git a/src/cc/rogue/rogue.h b/src/cc/rogue/rogue.h index 5540da2da..65ddf1e51 100644 --- a/src/cc/rogue/rogue.h +++ b/src/cc/rogue/rogue.h @@ -380,7 +380,7 @@ int32_t rogue_restorepack(struct rogue_state *rs); void restore_player(struct rogue_state *rs); int32_t rogue_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis); void rogue_bailout(struct rogue_state *rs); -void rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num); +int32_t rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num); int32_t rogue_setplayerdata(struct rogue_state *rs,char *gametxidstr); #define ROGUE_MAXTOTAL (pstats.s_str*2) From 259b9a983851e44f7d9fc7f8cbc0eb6d8a77ea49 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 07:35:23 -1100 Subject: [PATCH 158/787] Errata --- src/cc/gamescc.cpp | 896 ++++++++++++++++++++++++++++++++++++++++++++ src/cc/rogue/main.c | 2 +- 2 files changed, 897 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index b68c9f55c..88df9586d 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1,3 +1,18 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + #include "gamescc.h" /* @@ -1645,3 +1660,884 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) } +/***************************************************************************/ +/** https://github.com/brenns10/tetris + @file main.c + @author Stephen Brennan + @date Created Wednesday, 10 June 2015 + @brief Main program for tetris. + @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised + BSD License. See LICENSE.txt for details. + *******************************************************************************/ + + +#ifndef TETRIS_H +#define TETRIS_H + +#include // for FILE +#include // for bool +#include +#include +#include +#include +#include +#include + +#ifdef BUILD_GAMES +#include "cursesd.h" +#include "rogue/cursesd.c" +#else +#include +#endif + + +/* + Convert a tetromino type to its corresponding cell. + */ +#define TYPE_TO_CELL(x) ((x)+1) + +/* + Strings for how you would print a tetris board. + */ +#define TC_EMPTY_STR " " +#define TC_BLOCK_STR "\u2588" + +/* + Questions about a tetris cell. + */ +#define TC_IS_EMPTY(x) ((x) == TC_EMPTY) +#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x)) + +/* + How many cells in a tetromino? + */ +#define TETRIS 4 +/* + How many tetrominos? + */ +#define NUM_TETROMINOS 7 +/* + How many orientations of a tetromino? + */ +#define NUM_ORIENTATIONS 4 + +/* + Level constants. + */ +#define MAX_LEVEL 19 +#define LINES_PER_LEVEL 10 + +/* + A "cell" is a 1x1 block within a tetris board. + */ +typedef enum { + TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ +} tetris_cell; + +/* + A "type" is a type/shape of a tetromino. Not including orientation. + */ +typedef enum { + TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z +} tetris_type; + +/* + A row,column pair. Negative numbers allowed, because we need them for + offsets. + */ +typedef struct { + int row; + int col; +} tetris_location; + +/* + A "block" is a struct that contains information about a tetromino. + Specifically, what type it is, what orientation it has, and where it is. + */ +typedef struct { + int typ; + int ori; + tetris_location loc; +} tetris_block; + +/* + All possible moves to give as input to the game. + */ +typedef enum { + TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE +} tetris_move; + +/* + A game object! + */ +typedef struct { + /* + Game board stuff: + */ + int rows; + int cols; + char *board; + /* + Scoring information: + */ + int points; + int level; + /* + Falling block is the one currently going down. Next block is the one that + will be falling after this one. Stored is the block that you can swap out. + */ + tetris_block falling; + tetris_block next; + tetris_block stored; + /* + Number of game ticks until the block will move down. + */ + int ticks_till_gravity; + /* + Number of lines until you advance to the next level. + */ + int lines_remaining; +} tetris_game; + +/* + This array stores all necessary information about the cells that are filled by + each tetromino. The first index is the type of the tetromino (i.e. shape, + e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final + array contains 4 tetris_location objects, each mapping to an offset from a + point on the upper left that is the tetromino "origin". + */ +extern tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS]; + +/* + This array tells you how many ticks per gravity by level. Decreases as level + increases, to add difficulty. + */ +extern int GRAVITY_LEVEL[MAX_LEVEL+1]; + +// Data structure manipulation. +void tg_init(tetris_game *obj, int rows, int cols); +tetris_game *tg_create(int rows, int cols); +void tg_destroy(tetris_game *obj); +void tg_delete(tetris_game *obj); +tetris_game *tg_load(FILE *f); +void tg_save(tetris_game *obj, FILE *f); + +// Public methods not related to memory: +char tg_get(tetris_game *obj, int row, int col); +bool tg_check(tetris_game *obj, int row, int col); +bool tg_tick(tetris_game *obj, tetris_move move); +void tg_print(tetris_game *obj, FILE *f); + +#endif // TETRIS_H + + +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +/******************************************************************************* + Array Definitions + *******************************************************************************/ + +tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] = { + // I + {{{1, 0}, {1, 1}, {1, 2}, {1, 3}}, + {{0, 2}, {1, 2}, {2, 2}, {3, 2}}, + {{3, 0}, {3, 1}, {3, 2}, {3, 3}}, + {{0, 1}, {1, 1}, {2, 1}, {3, 1}}}, + // J + {{{0, 0}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 2}}, + {{0, 1}, {1, 1}, {2, 0}, {2, 1}}}, + // L + {{{0, 2}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {2, 1}, {2, 2}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 0}}, + {{0, 0}, {0, 1}, {1, 1}, {2, 1}}}, + // O + {{{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}}, + // S + {{{0, 1}, {0, 2}, {1, 0}, {1, 1}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 2}}, + {{1, 1}, {1, 2}, {2, 0}, {2, 1}}, + {{0, 0}, {1, 0}, {1, 1}, {2, 1}}}, + // T + {{{0, 1}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 1}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 1}}}, + // Z + {{{0, 0}, {0, 1}, {1, 1}, {1, 2}}, + {{0, 2}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {2, 1}, {2, 2}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 0}}}, +}; + +int GRAVITY_LEVEL[MAX_LEVEL+1] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + //10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 30, 28, 26, 24, 22, 20, 16, 12, 8, 4 +}; + +/******************************************************************************* + Helper Functions for Blocks + *******************************************************************************/ + +void sleep_milli(int milliseconds) +{ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = milliseconds * 1000 * 1000; + nanosleep(&ts, NULL); +} + +/* + Return the block at the given row and column. + */ +char tg_get(tetris_game *obj, int row, int column) +{ + return obj->board[obj->cols * row + column]; +} + +/* + Set the block at the given row and column. + */ +static void tg_set(tetris_game *obj, int row, int column, char value) +{ + obj->board[obj->cols * row + column] = value; +} + +/* + Check whether a row and column are in bounds. + */ +bool tg_check(tetris_game *obj, int row, int col) +{ + return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols; +} + +/* + Place a block onto the board. + */ +static void tg_put(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, + TYPE_TO_CELL(block.typ)); + } +} + +/* + Clear a block out of the board. + */ +static void tg_remove(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY); + } +} + +/* + Check if a block can be placed on the board. + */ +static bool tg_fits(tetris_game *obj, tetris_block block) +{ + int i, r, c; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + r = block.loc.row + cell.row; + c = block.loc.col + cell.col; + if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) { + return false; + } + } + return true; +} + +/* + Return a random tetromino type. + */ +static int random_tetromino(void) +{ + return rand() % NUM_TETROMINOS; +} + +/* + Create a new falling block and populate the next falling block with a random + one. + */ +static void tg_new_falling(tetris_game *obj) +{ + // Put in a new falling tetromino. + obj->falling = obj->next; + obj->next.typ = random_tetromino(); + obj->next.ori = 0; + obj->next.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; +} + +/******************************************************************************* + Game Turn Helpers + *******************************************************************************/ + +/* + Tick gravity, and move the block down if gravity should act. + */ +static void tg_do_gravity_tick(tetris_game *obj) +{ + obj->ticks_till_gravity--; + if (obj->ticks_till_gravity <= 0) { + tg_remove(obj, obj->falling); + obj->falling.loc.row++; + if (tg_fits(obj, obj->falling)) { + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + } else { + obj->falling.loc.row--; + tg_put(obj, obj->falling); + + tg_new_falling(obj); + } + tg_put(obj, obj->falling); + } +} + +/* + Move the falling tetris block left (-1) or right (+1). + */ +static void tg_move(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + obj->falling.loc.col += direction; + if (!tg_fits(obj, obj->falling)) { + obj->falling.loc.col -= direction; + } + tg_put(obj, obj->falling); +} + +/* + Send the falling tetris block to the bottom. + */ +static void tg_down(tetris_game *obj) +{ + tg_remove(obj, obj->falling); + while (tg_fits(obj, obj->falling)) { + obj->falling.loc.row++; + } + obj->falling.loc.row--; + tg_put(obj, obj->falling); + tg_new_falling(obj); +} + +/* + Rotate the falling block in either direction (+/-1). + */ +static void tg_rotate(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + + while (true) { + obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS; + + // If the new orientation fits, we're done. + if (tg_fits(obj, obj->falling)) + break; + + // Otherwise, try moving left to make it fit. + obj->falling.loc.col--; + if (tg_fits(obj, obj->falling)) + break; + + // Finally, try moving right to make it fit. + obj->falling.loc.col += 2; + if (tg_fits(obj, obj->falling)) + break; + + // Put it back in its original location and try the next orientation. + obj->falling.loc.col--; + // Worst case, we come back to the original orientation and it fits, so this + // loop will terminate. + } + + tg_put(obj, obj->falling); +} + +/* + Swap the falling block with the block in the hold buffer. + */ +static void tg_hold(tetris_game *obj) +{ + tg_remove(obj, obj->falling); + if (obj->stored.typ == -1) { + obj->stored = obj->falling; + tg_new_falling(obj); + } else { + int typ = obj->falling.typ, ori = obj->falling.ori; + obj->falling.typ = obj->stored.typ; + obj->falling.ori = obj->stored.ori; + obj->stored.typ = typ; + obj->stored.ori = ori; + while (!tg_fits(obj, obj->falling)) { + obj->falling.loc.row--; + } + } + tg_put(obj, obj->falling); +} + +/* + Perform the action specified by the move. + */ +static void tg_handle_move(tetris_game *obj, tetris_move move) +{ + switch (move) { + case TM_LEFT: + tg_move(obj, -1); + break; + case TM_RIGHT: + tg_move(obj, 1); + break; + case TM_DROP: + tg_down(obj); + break; + case TM_CLOCK: + tg_rotate(obj, 1); + break; + case TM_COUNTER: + tg_rotate(obj, -1); + break; + case TM_HOLD: + tg_hold(obj); + break; + default: + // pass + break; + } +} + +/* + Return true if line i is full. + */ +static bool tg_line_full(tetris_game *obj, int i) +{ + int j; + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) + return false; + } + return true; +} + +/* + Shift every row above r down one. + */ +static void tg_shift_lines(tetris_game *obj, int r) +{ + int i, j; + for (i = r-1; i >= 0; i--) { + for (j = 0; j < obj->cols; j++) { + tg_set(obj, i+1, j, tg_get(obj, i, j)); + tg_set(obj, i, j, TC_EMPTY); + } + } +} + +/* + Find rows that are filled, remove them, shift, and return the number of + cleared rows. + */ +static int tg_check_lines(tetris_game *obj) +{ + int i, nlines = 0; + tg_remove(obj, obj->falling); // don't want to mess up falling block + + for (i = obj->rows-1; i >= 0; i--) { + if (tg_line_full(obj, i)) { + tg_shift_lines(obj, i); + i++; // do this line over again since they're shifted + nlines++; + } + } + + tg_put(obj, obj->falling); // replace + return nlines; +} + +/* + Adjust the score for the game, given how many lines were just cleared. + */ +static void tg_adjust_score(tetris_game *obj, int lines_cleared) +{ + static int line_multiplier[] = {0, 40, 100, 300, 1200}; + obj->points += line_multiplier[lines_cleared] * (obj->level + 1); + if (lines_cleared >= obj->lines_remaining) { + obj->level = MIN(MAX_LEVEL, obj->level + 1); + lines_cleared -= obj->lines_remaining; + obj->lines_remaining = LINES_PER_LEVEL - lines_cleared; + } else { + obj->lines_remaining -= lines_cleared; + } +} + +/* + Return true if the game is over. + */ +static bool tg_game_over(tetris_game *obj) +{ + int i, j; + bool over = false; + tg_remove(obj, obj->falling); + for (i = 0; i < 2; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + over = true; + } + } + } + tg_put(obj, obj->falling); + return over; +} + +/******************************************************************************* + Main Public Functions + *******************************************************************************/ + +/* + Do a single game tick: process gravity, user input, and score. Return true if + the game is still running, false if it is over. + */ +bool tg_tick(tetris_game *obj, tetris_move move) +{ + int lines_cleared; + // Handle gravity. + tg_do_gravity_tick(obj); + + // Handle input. + tg_handle_move(obj, move); + + // Check for cleared lines + lines_cleared = tg_check_lines(obj); + + tg_adjust_score(obj, lines_cleared); + + // Return whether the game will continue (NOT whether it's over) + return !tg_game_over(obj); +} + +void tg_init(tetris_game *obj, int rows, int cols) +{ + // Initialization logic + obj->rows = rows; + obj->cols = cols; + obj->board = (char *)malloc(rows * cols); + memset(obj->board, TC_EMPTY, rows * cols); + obj->points = 0; + obj->level = 0; + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + obj->lines_remaining = LINES_PER_LEVEL; + srand(time(NULL)); + tg_new_falling(obj); + tg_new_falling(obj); + obj->stored.typ = -1; + obj->stored.ori = 0; + obj->stored.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; + printf("%d", obj->falling.loc.col); +} + +tetris_game *tg_create(int rows, int cols) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); + tg_init(obj, rows, cols); + return obj; +} + +void tg_destroy(tetris_game *obj) +{ + // Cleanup logic + free(obj->board); +} + +void tg_delete(tetris_game *obj) { + tg_destroy(obj); + free(obj); +} + +/* + Load a game from a file. + */ +tetris_game *tg_load(FILE *f) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); + if (fread(obj, sizeof(tetris_game), 1, f) != 1 ) + { + fprintf(stderr,"read game error\n"); + free(obj); + obj = 0; + } + else + { + obj->board = (char *)malloc(obj->rows * obj->cols); + if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + { + fprintf(stderr,"fread error\n"); + free(obj->board); + free(obj); + obj = 0; + } + } + return obj; +} + +/* + Save a game to a file. + */ +void tg_save(tetris_game *obj, FILE *f) +{ + if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) + fprintf(stderr,"error writing tetrisgame\n"); + else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + fprintf(stderr,"error writing board\n"); +} + +/* + Print a game board to a file. Really just for early debugging. + */ +void tg_print(tetris_game *obj, FILE *f) { + int i, j; + for (i = 0; i < obj->rows; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) { + fputs(TC_EMPTY_STR, f); + } else { + fputs(TC_BLOCK_STR, f); + } + } + fputc('\n', f); + } +} + +/* + 2 columns per cell makes the game much nicer. + */ +#define COLS_PER_CELL 2 +/* + Macro to print a cell of a specific type to a window. + */ +#define ADD_BLOCK(w,x) waddch((w),' '|A_REVERSE|COLOR_PAIR(x)); \ +waddch((w),' '|A_REVERSE|COLOR_PAIR(x)) +#define ADD_EMPTY(w) waddch((w), ' '); waddch((w), ' ') + +/* + Print the tetris board onto the ncurses window. + */ +void display_board(WINDOW *w, tetris_game *obj) +{ + int i, j; + box(w, 0, 0); + for (i = 0; i < obj->rows; i++) { + wmove(w, 1 + i, 1); + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + ADD_BLOCK(w,tg_get(obj, i, j)); + } else { + ADD_EMPTY(w); + } + } + } + wnoutrefresh(w); +} + +/* + Display a tetris piece in a dedicated window. + */ +void display_piece(WINDOW *w, tetris_block block) +{ + int b; + tetris_location c; + wclear(w); + box(w, 0, 0); + if (block.typ == -1) { + wnoutrefresh(w); + return; + } + for (b = 0; b < TETRIS; b++) { + c = TETROMINOS[block.typ][block.ori][b]; + wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1); + ADD_BLOCK(w, TYPE_TO_CELL(block.typ)); + } + wnoutrefresh(w); +} + +/* + Display score information in a dedicated window. + */ +void display_score(WINDOW *w, tetris_game *tg) +{ + wclear(w); + box(w, 0, 0); + wprintw(w, "Score\n%d\n", tg->points); + wprintw(w, "Level\n%d\n", tg->level); + wprintw(w, "Lines\n%d\n", tg->lines_remaining); + wnoutrefresh(w); +} + +/* + Save and exit the game. + */ +void save(tetris_game *game, WINDOW *w) +{ + FILE *f; + + wclear(w); + box(w, 0, 0); // return the border + wmove(w, 1, 1); + wprintw(w, "Save and exit? [Y/n] "); + wrefresh(w); + timeout(-1); + if (getch() == 'n') { + timeout(0); + return; + } + f = fopen("tetris.save", "w"); + tg_save(game, f); + fclose(f); + tg_delete(game); + endwin(); + printf("Game saved to \"tetris.save\".\n"); + printf("Resume by passing the filename as an argument to this program.\n"); + exit(EXIT_SUCCESS); +} + +/* + Do the NCURSES initialization steps for color blocks. + */ +void init_colors(void) +{ + start_color(); + //init_color(COLOR_ORANGE, 1000, 647, 0); + init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK); + init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK); + init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK); + init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK); + init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK); + init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK); + init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); +} + +/* + Main tetris game! + */ +#ifndef STANDALONE + +int main(int argc, char **argv) +{ + tetris_game *tg; + tetris_move move = TM_NONE; + bool running = true; + WINDOW *board, *next, *hold, *score; + //Mix_Music *music; + + // Load file if given a filename. + if (argc >= 2) { + FILE *f = fopen(argv[1], "r"); + if (f == NULL) { + perror("tetris"); + exit(EXIT_FAILURE); + } + tg = tg_load(f); + fclose(f); + } else { + // Otherwise create new game. + tg = tg_create(22, 10); + } + // NCURSES initialization: + initscr(); // initialize curses + cbreak(); // pass key presses to program, but not signals + noecho(); // don't echo key presses to screen + keypad(stdscr, TRUE); // allow arrow keys + timeout(0); // no blocking on getch() + curs_set(0); // set the cursor to invisible + init_colors(); // setup tetris colors + + // Create windows for each section of the interface. + board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); + next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); + hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); + score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); + int32_t counter = 0; + // Game loop + while (running) { + running = tg_tick(tg, move); + display_board(board, tg); + display_piece(next, tg->next); + display_piece(hold, tg->stored); + display_score(score, tg); + if ( (counter++ % 5) == 0 ) + doupdate(); + sleep_milli(10); + + switch (getch()) { + case KEY_LEFT: + move = TM_LEFT; + break; + case KEY_RIGHT: + move = TM_RIGHT; + break; + case KEY_UP: + move = TM_CLOCK; + break; + case KEY_DOWN: + move = TM_DROP; + break; + case 'q': + running = false; + move = TM_NONE; + break; + case 'p': + wclear(board); + box(board, 0, 0); + wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); + wprintw(board, "PAUSED"); + wrefresh(board); + timeout(-1); + getch(); + timeout(0); + move = TM_NONE; + break; + case 's': + save(tg, board); + move = TM_NONE; + break; + case ' ': + move = TM_HOLD; + break; + default: + move = TM_NONE; + } + } + + // Deinitialize NCurses + wclear(stdscr); + endwin(); + + /* Deinitialize Sound + Mix_HaltMusic(); + Mix_FreeMusic(music); + Mix_CloseAudio(); + Mix_Quit();*/ + + // Output ending message. + printf("Game over!\n"); + printf("You finished with %d points on level %d.\n", tg->points, tg->level); + + // Deinitialize Tetris + tg_delete(tg); + return 0; +} + diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c index 616ade086..093b73d70 100644 --- a/src/cc/rogue/main.c +++ b/src/cc/rogue/main.c @@ -786,7 +786,7 @@ int32_t rogue_sendrawtransaction(char *rawtx) int32_t rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) { - char cmd[16384],hexstr[16384],params[32768],*retstr,*rawtx,*pastkeys,*pastcmp,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; + char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*pastcmp,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; //fprintf(stderr,"rogue_progress num.%d\n",num); if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) { From a266b5e613f05f686c056a56e6047fd950dee090 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 07:36:23 -1100 Subject: [PATCH 159/787] Test --- src/cc/gamescc.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 88df9586d..4b5dca4f5 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -2525,13 +2525,6 @@ int main(int argc, char **argv) // Deinitialize NCurses wclear(stdscr); endwin(); - - /* Deinitialize Sound - Mix_HaltMusic(); - Mix_FreeMusic(music); - Mix_CloseAudio(); - Mix_Quit();*/ - // Output ending message. printf("Game over!\n"); printf("You finished with %d points on level %d.\n", tg->points, tg->level); @@ -2540,4 +2533,5 @@ int main(int argc, char **argv) tg_delete(tg); return 0; } +#endif From 59b34d0a1c68fd2cdb13fb9bbe2da4dab074f934 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 07:39:13 -1100 Subject: [PATCH 160/787] #include "rogue/cursesd.c" --- src/cc/cclib.cpp | 1 + src/cc/gamescc.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 65c706862..95748def4 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -692,6 +692,7 @@ int32_t cclib_parsehash(uint8_t *hash32,cJSON *item,int32_t len) #elif BUILD_GAMESCC #include "gamescc.cpp" +#include "rogue/cursesd.c" #else #include "sudoku.cpp" diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 4b5dca4f5..3415c3eb7 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1685,7 +1685,6 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #ifdef BUILD_GAMES #include "cursesd.h" -#include "rogue/cursesd.c" #else #include #endif @@ -2433,7 +2432,7 @@ void init_colors(void) /* Main tetris game! */ -#ifndef STANDALONE +#ifdef STANDALONE int main(int argc, char **argv) { From 284a57b31f774f2800661354e81c12a2310a5ace Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 07:46:19 -1100 Subject: [PATCH 161/787] BUILD_GAMESCC --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 3415c3eb7..0d2564042 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1683,7 +1683,7 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include #include -#ifdef BUILD_GAMES +#ifdef BUILD_GAMESCC #include "cursesd.h" #else #include From f800ced0e20af6e559b6b24f5096960a4d2249b5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 07:46:59 -1100 Subject: [PATCH 162/787] Rogue/cursesd --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 0d2564042..81942ebe4 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1684,7 +1684,7 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include #ifdef BUILD_GAMESCC -#include "cursesd.h" +#include "rogue/cursesd.h" #else #include #endif From 7a1a5f15299816629b7c22124e68b56e2c14f29f Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 07:51:45 -1100 Subject: [PATCH 163/787] Endif --- src/cc/gamescc.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 81942ebe4..c6206b2ee 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1659,6 +1659,7 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) sprintf(packitemstr,"not yet"); } +#ifdef includgame /***************************************************************************/ /** https://github.com/brenns10/tetris @@ -2533,4 +2534,5 @@ int main(int argc, char **argv) return 0; } #endif +#endif From 347ba53d84c64133f5a3c695f7cb3f4515759090 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 07:52:57 -1100 Subject: [PATCH 164/787] // --- src/cc/rogue_rpc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 6f507e51e..fbb1a3d93 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -1596,7 +1596,7 @@ bool rogue_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C if ( funcid == 'H' ) { cashout *= 2; - cashout += numplayers * buyin; + //cashout += numplayers * buyin; } sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d txid.%s %.8f vs vout2 %.8f",tokentx,decoded,height,txid.GetHex().c_str(),(double)cashout/COIN,(double)tx.vout[2].nValue/COIN); if ( strcmp(laststr,cashstr) != 0 ) From 9c555e6168c8442e5994fdc69551e4865bf5cb46 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 07:59:17 -1100 Subject: [PATCH 165/787] Colors --- src/cc/gamescc.cpp | 3 --- src/cc/rogue/cursesd.h | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index c6206b2ee..694846350 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1659,8 +1659,6 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) sprintf(packitemstr,"not yet"); } -#ifdef includgame - /***************************************************************************/ /** https://github.com/brenns10/tetris @file main.c @@ -2534,5 +2532,4 @@ int main(int argc, char **argv) return 0; } #endif -#endif diff --git a/src/cc/rogue/cursesd.h b/src/cc/rogue/cursesd.h index cb74941e5..fbac07855 100644 --- a/src/cc/rogue/cursesd.h +++ b/src/cc/rogue/cursesd.h @@ -16,6 +16,24 @@ #ifndef H_CURSESD_H #define H_CURSESD_H +#define COLOR_BLACK 0 + +#ifdef PDC_RGB /* RGB */ +# define COLOR_RED 1 +# define COLOR_GREEN 2 +# define COLOR_BLUE 4 +#else /* BGR */ +# define COLOR_BLUE 1 +# define COLOR_GREEN 2 +# define COLOR_RED 4 +#endif + +#define COLOR_CYAN (COLOR_BLUE | COLOR_GREEN) +#define COLOR_MAGENTA (COLOR_RED | COLOR_BLUE) +#define COLOR_YELLOW (COLOR_RED | COLOR_GREEN) + +#define COLOR_WHITE 7 + #define LINES 24 #define COLS 80 From 02facf2da73eda9e77633fead9c7e0c4228f1177 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 08:04:50 -1100 Subject: [PATCH 166/787] Test --- src/cc/gamescc.cpp | 12 ++++++------ src/cc/rogue/cursesd.h | 6 ++++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 694846350..175cc3dc2 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -2379,9 +2379,9 @@ void display_score(WINDOW *w, tetris_game *tg) { wclear(w); box(w, 0, 0); - wprintw(w, "Score\n%d\n", tg->points); - wprintw(w, "Level\n%d\n", tg->level); - wprintw(w, "Lines\n%d\n", tg->lines_remaining); + wprintw(w, (char *)"Score\n%d\n", tg->points); + wprintw(w, (char *)"Level\n%d\n", tg->level); + wprintw(w, (char *)"Lines\n%d\n", tg->lines_remaining); wnoutrefresh(w); } @@ -2395,7 +2395,7 @@ void save(tetris_game *game, WINDOW *w) wclear(w); box(w, 0, 0); // return the border wmove(w, 1, 1); - wprintw(w, "Save and exit? [Y/n] "); + wprintw(w, (char *)"Save and exit? [Y/n] "); wrefresh(w); timeout(-1); if (getch() == 'n') { @@ -2407,8 +2407,8 @@ void save(tetris_game *game, WINDOW *w) fclose(f); tg_delete(game); endwin(); - printf("Game saved to \"tetris.save\".\n"); - printf("Resume by passing the filename as an argument to this program.\n"); + fprintf(stderr,"Game saved to \"tetris.save\".\n"); + fprintf(stderr,"Resume by passing the filename as an argument to this program.\n"); exit(EXIT_SUCCESS); } diff --git a/src/cc/rogue/cursesd.h b/src/cc/rogue/cursesd.h index fbac07855..0ae992427 100644 --- a/src/cc/rogue/cursesd.h +++ b/src/cc/rogue/cursesd.h @@ -168,6 +168,12 @@ char *unctrl(char c); #define leaveok(win,bf) 0 #define halfdelay(x) 0 #define nocbreak() 0 +#define init_color(a,b,c) 0 +#define start_color() 0 +#define box(a,b,c) 0 +#define A_REVERSE 0 +#define COLOR_PAIR(a) 0 +#define timeout(x) 0 #ifndef TRUE #define TRUE 1 From a06129b526ff501c30bfed43625a325938e2107c Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 08:07:18 -1100 Subject: [PATCH 167/787] init_pair --- src/cc/rogue/cursesd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/rogue/cursesd.h b/src/cc/rogue/cursesd.h index 0ae992427..cdad704ed 100644 --- a/src/cc/rogue/cursesd.h +++ b/src/cc/rogue/cursesd.h @@ -168,7 +168,7 @@ char *unctrl(char c); #define leaveok(win,bf) 0 #define halfdelay(x) 0 #define nocbreak() 0 -#define init_color(a,b,c) 0 +#define init_pair(a,b,c) 0 #define start_color() 0 #define box(a,b,c) 0 #define A_REVERSE 0 From b91cd4346bca52fcd668c3748f535785b8a3ba25 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 08:50:49 -1100 Subject: [PATCH 168/787] Standalone --- src/cc/gamescc.cpp | 995 ++++++++++++++++++++++++++++++++++++++++- src/cc/rogue/cursesd.h | 3 + 2 files changed, 996 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 175cc3dc2..59adfae1d 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1659,6 +1659,997 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) sprintf(packitemstr,"not yet"); } +#ifdef STANDALONE + +#include +#include +#include +#include +#include +#include +#include +#include + +char USERPASS[8192]; uint16_t GAMES_PORT; +extern char Gametxidstr[67]; + +#define SMALLVAL 0.000000000000001 +#define SATOSHIDEN ((uint64_t)100000000L) +#define dstr(x) ((double)(x) / SATOSHIDEN) +#define KOMODO_ASSETCHAIN_MAXLEN 65 +char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],IPADDRESS[100]; + +#ifndef _BITS256 +#define _BITS256 +union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; +typedef union _bits256 bits256; +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +int gettimeofday(struct timeval * tp, struct timezone * tzp) +{ + // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; +} +#endif // _MSC_VER +#endif + + + +double OS_milliseconds() +{ + struct timeval tv; double millis; +#ifdef __MINGW32__ + mingw_gettimeofday(&tv,NULL); +#else + gettimeofday(&tv,NULL); +#endif + millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.); + //printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis); + return(millis); +} + +int32_t _unhex(char c) +{ + if ( c >= '0' && c <= '9' ) + return(c - '0'); + else if ( c >= 'a' && c <= 'f' ) + return(c - 'a' + 10); + else if ( c >= 'A' && c <= 'F' ) + return(c - 'A' + 10); + return(-1); +} + +int32_t is_hexstr(char *str,int32_t n) +{ + int32_t i; + if ( str == 0 || str[0] == 0 ) + return(0); + for (i=0; str[i]!=0; i++) + { + if ( n > 0 && i >= n ) + break; + if ( _unhex(str[i]) < 0 ) + break; + } + if ( n == 0 ) + return(i); + return(i == n); +} + +int32_t unhex(char c) +{ + int32_t hex; + if ( (hex= _unhex(c)) < 0 ) + { + //printf("unhex: illegal hexchar.(%c)\n",c); + } + return(hex); +} + +unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } + +int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex) +{ + int32_t adjust,i = 0; + //printf("decode.(%s)\n",hex); + if ( is_hexstr(hex,n) <= 0 ) + { + memset(bytes,0,n); + return(n); + } + if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) + hex[--n] = 0; + if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) + { + if ( n > 0 ) + { + bytes[0] = unhex(hex[0]); + printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); + } + bytes++; + hex++; + adjust = 1; + } else adjust = 0; + if ( n > 0 ) + { + for (i=0; i>4) & 0xf); + hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); + //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); + } + hexbytes[len*2] = 0; + //printf("len.%ld\n",len*2+1); + return((int32_t)len*2+1); +} + +char *bits256_str(char hexstr[65],bits256 x) +{ + init_hexbytes_noT(hexstr,x.bytes,sizeof(x)); + return(hexstr); +} + +long _stripwhite(char *buf,int accept) +{ + int32_t i,j,c; + if ( buf == 0 || buf[0] == 0 ) + return(0); + for (i=j=0; buf[i]!=0; i++) + { + buf[j] = c = buf[i]; + if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) + j++; + } + buf[j] = 0; + return(j); +} + +char *clonestr(char *str) +{ + char *clone; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + clone = (char *)malloc(strlen(str)+16); + strcpy(clone,str); + return(clone); +} + +char *parse_conf_line(char *line,char *field) +{ + line += strlen(field); + for (; *line!='='&&*line!=0; line++) + break; + if ( *line == 0 ) + return(0); + if ( *line == '=' ) + line++; + while ( line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n' || line[strlen(line)-1] == ' ' ) + line[strlen(line)-1] = 0; + //printf("LINE.(%s)\n",line); + _stripwhite(line,0); + return(clonestr(line)); +} + +int32_t safecopy(char *dest,char *src,long len) +{ + int32_t i = -1; + if ( src != 0 && dest != 0 && src != dest ) + { + if ( dest != 0 ) + memset(dest,0,len); + for (i=0; i buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +uint8_t *OS_fileptr(long *allocsizep,char *fname) +{ + long filesize = 0; uint8_t *buf = 0; void *retptr; + *allocsizep = 0; + retptr = OS_loadfile(fname,&buf,&filesize,allocsizep); + return((uint8_t *)retptr); +} + +struct MemoryStruct { char *memory; size_t size; }; +struct return_string { char *ptr; size_t len; }; + +// return data from the server +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) + + +/************************************************************************ + * + * Initialize the string handler so that it is thread safe + * + ************************************************************************/ + +void init_string(struct return_string *s) +{ + s->len = 0; + s->ptr = (char *)calloc(1,s->len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr,"init_string malloc() failed\n"); + exit(-1); + } + s->ptr[0] = '\0'; +} + +/************************************************************************ + * + * Use the "writer" to accumulate text until done + * + ************************************************************************/ + +size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string *s) +{ + size_t new_len = s->len + size*nmemb; + s->ptr = (char *)realloc(s->ptr,new_len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr, "accumulate realloc() failed\n"); + exit(-1); + } + memcpy(s->ptr+s->len,ptr,size*nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + return(size * nmemb); +} + +/************************************************************************ + * + * return the current system time in milliseconds + * + ************************************************************************/ + +#define EXTRACT_BITCOIND_RESULT // if defined, ensures error is null and returns the "result" field +#ifdef EXTRACT_BITCOIND_RESULT + +/************************************************************************ + * + * perform post processing of the results + * + ************************************************************************/ + +char *post_process_bitcoind_RPC(char *debugstr,char *command,char *rpcstr,char *params) +{ + long i,j,len; char *retstr = 0; cJSON *json,*result,*error; + //printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + if ( command == 0 || rpcstr == 0 || rpcstr[0] == 0 ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + return(rpcstr); + } + json = cJSON_Parse(rpcstr); + if ( json == 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s can't parse.(%s) params.(%s)\n",debugstr,command,rpcstr,params); + free(rpcstr); + return(0); + } + result = cJSON_GetObjectItem(json,"result"); + error = cJSON_GetObjectItem(json,"error"); + if ( error != 0 && result != 0 ) + { + if ( (error->type&0xff) == cJSON_NULL && (result->type&0xff) != cJSON_NULL ) + { + retstr = cJSON_Print(result); + len = strlen(retstr); + if ( retstr[0] == '"' && retstr[len-1] == '"' ) + { + for (i=1,j=0; itype&0xff) != cJSON_NULL || (result->type&0xff) != cJSON_NULL ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC (%s) error.%s\n",debugstr,command,rpcstr); + } + free(rpcstr); + } else retstr = rpcstr; + free_json(json); + //fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC: postprocess returns.(%s)\n",retstr); + return(retstr); +} +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + +/************************************************************************ + * + * perform the query + * + ************************************************************************/ + +char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params) +{ + static int didinit,count,count2; static double elapsedsum,elapsedsum2; + struct curl_slist *headers = NULL; struct return_string s; CURLcode res; CURL *curl_handle; + char *bracket0,*bracket1,*databuf = 0; long len; int32_t specialcase,numretries; double starttime; + if ( didinit == 0 ) + { + didinit = 1; + curl_global_init(CURL_GLOBAL_ALL); //init the curl session + } + numretries = 0; + if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") == 0 ) + specialcase = 1; + else specialcase = 0; + if ( url[0] == 0 ) + strcpy(url,"http://127.0.0.1:7876/nxt"); + if ( specialcase != 0 && 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: debug.(%s) url.(%s) command.(%s) params.(%s)\n",debugstr,url,command,params); +try_again: + if ( retstrp != 0 ) + *retstrp = 0; + starttime = OS_milliseconds(); + curl_handle = curl_easy_init(); + init_string(&s); + headers = curl_slist_append(0,"Expect:"); + + curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl_handle,CURLOPT_URL, url); + curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function + curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback + curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash + curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback + if ( strncmp(url,"https",5) == 0 ) + { + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0); + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0); + } + if ( userpass != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_USERPWD, userpass); + databuf = 0; + if ( params != 0 ) + { + if ( command != 0 && specialcase == 0 ) + { + len = strlen(params); + if ( len > 0 && params[0] == '[' && params[len-1] == ']' ) { + bracket0 = bracket1 = (char *)""; + } + else + { + bracket0 = (char *)"["; + bracket1 = (char *)"]"; + } + + databuf = (char *)malloc(256 + strlen(command) + strlen(params)); + sprintf(databuf,"{\"id\":\"jl777\",\"method\":\"%s\",\"params\":%s%s%s}",command,bracket0,params,bracket1); + //printf("url.(%s) userpass.(%s) databuf.(%s)\n",url,userpass,databuf); + // + } //else if ( specialcase != 0 ) fprintf(stderr,"databuf.(%s)\n",params); + curl_easy_setopt(curl_handle,CURLOPT_POST,1L); + if ( databuf != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,databuf); + else curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,params); + } + //laststart = milliseconds(); + res = curl_easy_perform(curl_handle); + curl_slist_free_all(headers); + curl_easy_cleanup(curl_handle); + if ( databuf != 0 ) // clean up temporary buffer + { + free(databuf); + databuf = 0; + } + if ( res != CURLE_OK ) + { + numretries++; + if ( specialcase != 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); + free(s.ptr); + return(0); + } + else if ( numretries >= 1 ) + { + //printf("Maximum number of retries exceeded!\n"); + free(s.ptr); + return(0); + } + if ( (rand() % 1000) == 0 ) + printf( "curl_easy_perform() failed: %s %s.(%s %s), retries: %d\n",curl_easy_strerror(res),debugstr,url,command,numretries); + free(s.ptr); + sleep((1< (%s)\n",params,s.ptr); + count2++; + elapsedsum2 += (OS_milliseconds() - starttime); + if ( (count2 % 10000) == 0) + printf("%d: ave %9.6f | elapsed %.3f millis | NXT calls.(%s) cmd.(%s)\n",count2,elapsedsum2/count2,(double)(OS_milliseconds() - starttime),url,command); + return(s.ptr); + } + } + printf("bitcoind_RPC: impossible case\n"); + free(s.ptr); + return(0); +} + +static size_t WriteMemoryCallback(void *ptr,size_t size,size_t nmemb,void *data) +{ + size_t realsize = (size * nmemb); + struct MemoryStruct *mem = (struct MemoryStruct *)data; + mem->memory = (char *)((ptr != 0) ? realloc(mem->memory,mem->size + realsize + 1) : malloc(mem->size + realsize + 1)); + if ( mem->memory != 0 ) + { + if ( ptr != 0 ) + memcpy(&(mem->memory[mem->size]),ptr,realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + //printf("got %d bytes\n",(int32_t)(size*nmemb)); + return(realsize); +} + +char *curl_post(CURL **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3) +{ + struct MemoryStruct chunk; CURL *cHandle; long code; struct curl_slist *headers = 0; + if ( (cHandle= *cHandlep) == NULL ) + *cHandlep = cHandle = curl_easy_init(); + else curl_easy_reset(cHandle); + //#ifdef DEBUG + //curl_easy_setopt(cHandle,CURLOPT_VERBOSE, 1); + //#endif + curl_easy_setopt(cHandle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(cHandle,CURLOPT_SSL_VERIFYPEER,0); + //curl_easy_setopt(cHandle,CURLOPT_SSLVERSION,1); + curl_easy_setopt(cHandle,CURLOPT_URL,url); + curl_easy_setopt(cHandle,CURLOPT_CONNECTTIMEOUT,10); + if ( userpass != 0 && userpass[0] != 0 ) + curl_easy_setopt(cHandle,CURLOPT_USERPWD,userpass); + if ( postfields != 0 && postfields[0] != 0 ) + { + curl_easy_setopt(cHandle,CURLOPT_POST,1); + curl_easy_setopt(cHandle,CURLOPT_POSTFIELDS,postfields); + } + if ( hdr0 != NULL && hdr0[0] != 0 ) + { + //printf("HDR0.(%s) HDR1.(%s) HDR2.(%s) HDR3.(%s)\n",hdr0!=0?hdr0:"",hdr1!=0?hdr1:"",hdr2!=0?hdr2:"",hdr3!=0?hdr3:""); + headers = curl_slist_append(headers,hdr0); + if ( hdr1 != 0 && hdr1[0] != 0 ) + headers = curl_slist_append(headers,hdr1); + if ( hdr2 != 0 && hdr2[0] != 0 ) + headers = curl_slist_append(headers,hdr2); + if ( hdr3 != 0 && hdr3[0] != 0 ) + headers = curl_slist_append(headers,hdr3); + } //headers = curl_slist_append(0,"Expect:"); + if ( headers != 0 ) + curl_easy_setopt(cHandle,CURLOPT_HTTPHEADER,headers); + //res = curl_easy_perform(cHandle); + memset(&chunk,0,sizeof(chunk)); + curl_easy_setopt(cHandle,CURLOPT_WRITEFUNCTION,WriteMemoryCallback); + curl_easy_setopt(cHandle,CURLOPT_WRITEDATA,(void *)&chunk); + curl_easy_perform(cHandle); + curl_easy_getinfo(cHandle,CURLINFO_RESPONSE_CODE,&code); + if ( headers != 0 ) + curl_slist_free_all(headers); + if ( code != 200 ) + printf("(%s) server responded with code %ld (%s)\n",url,code,chunk.memory); + return(chunk.memory); +} + +uint16_t _komodo_userpass(char *username, char *password, FILE *fp) +{ + char *rpcuser,*rpcpassword,*str,*ipaddress,line[8192]; uint16_t port = 0; + rpcuser = rpcpassword = 0; + username[0] = password[0] = 0; + while ( fgets(line,sizeof(line),fp) != 0 ) + { + if ( line[0] == '#' ) + continue; + //printf("line.(%s) %p %p\n",line,strstr(line,(char *)"rpcuser"),strstr(line,(char *)"rpcpassword")); + if ( (str= strstr(line,(char *)"rpcuser")) != 0 ) + rpcuser = parse_conf_line(str,(char *)"rpcuser"); + else if ( (str= strstr(line,(char *)"rpcpassword")) != 0 ) + rpcpassword = parse_conf_line(str,(char *)"rpcpassword"); + else if ( (str= strstr(line,(char *)"rpcport")) != 0 ) + { + port = atoi(parse_conf_line(str,(char *)"rpcport")); + //fprintf(stderr,"rpcport.%u in file\n",port); + } + else if ( (str= strstr(line,(char *)"ipaddress")) != 0 ) + { + ipaddress = parse_conf_line(str,(char *)"ipaddress"); + strcpy(IPADDRESS,ipaddress); + } + } + if ( rpcuser != 0 && rpcpassword != 0 ) + { + strcpy(username,rpcuser); + strcpy(password,rpcpassword); + } + //printf("rpcuser.(%s) rpcpassword.(%s) %u ipaddress.%s\n",rpcuser,rpcpassword,port,ipaddress); + if ( rpcuser != 0 ) + free(rpcuser); + if ( rpcpassword != 0 ) + free(rpcpassword); + return(port); +} + +uint16_t komodo_userpass(char *userpass,char *symbol) +{ + FILE *fp; uint16_t port = 0; char fname[512],username[512],password[512],confname[KOMODO_ASSETCHAIN_MAXLEN]; + userpass[0] = 0; + if ( strcmp("KMD",symbol) == 0 ) + { +#ifdef __APPLE__ + sprintf(confname,"Komodo.conf"); +#else + sprintf(confname,"komodo.conf"); +#endif + } + else sprintf(confname,"%s.conf",symbol); + //komodo_statefname(fname,symbol,confname); + if ( (fp= fopen(confname,"rb")) != 0 ) + { + port = _komodo_userpass(username,password,fp); + sprintf(userpass,"%s:%s",username,password); + if ( strcmp(symbol,ASSETCHAINS_SYMBOL) == 0 ) + strcpy(USERPASS,userpass); + fclose(fp); + } + return(port); +} + +#define is_cJSON_True(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_True) + +char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) +{ + //static void *cHandle; + char url[512],*retstr=0,*retstr2=0,postdata[8192]; + if ( params == 0 || params[0] == 0 ) + params = (char *)"[]"; + if ( strlen(params) < sizeof(postdata)-128 ) + { + sprintf(url,(char *)"http://%s:%u",IPADDRESS,port); + sprintf(postdata,"{\"method\":\"%s\",\"params\":%s}",method,params); + //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,USERPASS); + retstr2 = bitcoind_RPC(&retstr,(char *)"debug",url,userpass,method,params); + //retstr = curl_post(&cHandle,url,USERPASS,postdata,0,0,0,0); + } + return(retstr2); +} + +int32_t games_sendrawtransaction(char *rawtx) +{ + char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; + params = (char *)malloc(strlen(rawtx) + 16); + sprintf(params,"[\"%s\"]",rawtx); + if ( (retstr= komodo_issuemethod(USERPASS,"sendrawtransaction",params,GAMES_PORT)) != 0 ) + { + if ( 0 ) // causes 4th level crash + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("games.sendlog","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,"result")) != 0 ) + { + if ( (hexstr= jstr(resobj,0)) != 0 && is_hexstr(hexstr,64) == 64 ) + retval = 0; + } + free_json(retjson); + } + + /* log sendrawtx result in file */ + + /* + FILE *debug_file; + debug_file = fopen("tx_debug.log", "a"); + fprintf(debug_file, "%s\n", retstr); + fflush(debug_file); + fclose(debug_file); + */ + + free(retstr); + } + free(params); + return(retval); +} + +int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +{ + char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*pastcmp,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; + if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) + { + if ( rs->keystrokeshex != 0 ) + { + if ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + if ( waitflag == 0 ) + return(0); + else if ( 0 ) + { + while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + //fprintf(stderr,"pre-rebroadcast\n"); + sleep(10); + } + } + } + free(rs->keystrokeshex), rs->keystrokeshex = 0; + } + if ( 0 && (pastkeys= games_keystrokesload(&numpastkeys,seed,1)) != 0 ) + { + sprintf(params,"[\"extract\",\"17\",\"[%%22%s%%22]\"]",Gametxidstr); + if ( (retstr= komodo_issuemethod(USERPASS,"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,"result")) != 0 && (keys= jstr(resobj,"keystrokes")) != 0 ) + { + len = strlen(keys) / 2; + pastcmp = (char *)malloc(len + 1); + decode_hex(pastcmp,len,keys); + fprintf(stderr,"keystrokes.(%s) vs pastkeys\n",keys); + for (i=0; i> keystrokes.log",ASSETCHAINS_SYMBOL,Gametxidstr,hexstr); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd); + } + else + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("keystrokes.log","a"); + sprintf(params,"[\"keystrokes\",\"17\",\"[%%22%s%%22,%%22%s%%22]\"]",Gametxidstr,hexstr); + if ( (retstr= komodo_issuemethod(USERPASS,"cclib",params,GAMES_PORT)) != 0 ) + { + if ( fp != 0 ) + { + fprintf(fp,"%s\n",params); + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,"result")) != 0 && (rawtx= jstr(resobj,"hex")) != 0 ) + { + if ( rs->keystrokeshex != 0 ) + free(rs->keystrokeshex); + if ( (errstr= jstr(resobj,"error")) == 0 ) + { + rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); + strcpy(rs->keystrokeshex,rawtx); + retflag = 1; + } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); + //fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); + } + free_json(retjson); + } + free(retstr); + } + if ( 0 && waitflag != 0 && rs->keystrokeshex != 0 ) + { + while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + //fprintf(stderr,"post-rebroadcast\n"); + sleep(3); + } + free(rs->keystrokeshex), rs->keystrokeshex = 0; + } + } + } + return(retflag); +} + +int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) +{ + char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; + if ( rs->guiflag == 0 ) + return(-1); + if ( gametxidstr == 0 || *gametxidstr == 0 ) + return(retval); + if ( 0 ) + { + sprintf(fname,"%s.gameinfo",gametxidstr); + sprintf(cmd,"./komodo-cli -ac_name=%s cclib gameinfo 17 \\\"[%%22%s%%22]\\\" > %s",ASSETCHAINS_SYMBOL,gametxidstr,fname); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd); + else filestr = (char *)OS_fileptr(&allocsize,fname); + } + else + { + sprintf(params,"[\"gameinfo\",\"17\",\"[%%22%s%%22]\"]",gametxidstr); + filestr = komodo_issuemethod(USERPASS,"cclib",params,GAMES_PORT); + } + if ( filestr != 0 ) + { + if ( (retjson= cJSON_Parse(filestr)) != 0 && (resultjson= jobj(retjson,"result")) != 0 ) + { + //fprintf(stderr,"gameinfo.(%s)\n",jprint(resultjson,0)); + if ( (array= jarray(&n,resultjson,"players")) != 0 ) + { + for (i=0; iP,(int32_t)strlen(datastr)/2,datastr); + fprintf(stderr,"set pname[%s] %s\n",pname==0?"":pname,jprint(item,0)); + rs->restoring = 1; + } + } + } + } + } + free_json(retjson); + } + free(filestr); + } + return(retval); +} + +#ifdef _WIN32 +#ifdef _MSC_VER +__inline int msver(void) { + switch (_MSC_VER) { + case 1500: return 2008; + case 1600: return 2010; + case 1700: return 2012; + case 1800: return 2013; + case 1900: return 2015; + //case 1910: return 2017; + default: return (_MSC_VER / 100); + } +} + +static inline bool is_x64(void) { +#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) + return 1; +#elif defined(__amd64__) || defined(__amd64) || defined(_M_X64) || defined(_M_IA64) + return 1; +#else + return 0; +#endif +} + +#define BUILD_DATE __DATE__ " " __TIME__ +#endif // _WIN32 +#endif // _MSC_VER + +int main(int argc, char **argv) +{ + uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; + +#ifdef _WIN32 +#ifdef _MSC_VER + printf("*** games for Windows [ Build %s ] ***\n", BUILD_DATE); + const char* arch = is_x64() ? "64-bits" : "32-bits"; + printf(" Built with VC++ %d (%ld) %s\n\n", msver(), _MSC_FULL_VER, arch); +#endif +#endif + + for (i=j=0; argv[0][i]!=0&&j= 2) { @@ -2531,5 +3521,6 @@ int main(int argc, char **argv) tg_delete(tg); return 0; } + #endif diff --git a/src/cc/rogue/cursesd.h b/src/cc/rogue/cursesd.h index cdad704ed..e3eec5d41 100644 --- a/src/cc/rogue/cursesd.h +++ b/src/cc/rogue/cursesd.h @@ -168,12 +168,15 @@ char *unctrl(char c); #define leaveok(win,bf) 0 #define halfdelay(x) 0 #define nocbreak() 0 + +// for tetris #define init_pair(a,b,c) 0 #define start_color() 0 #define box(a,b,c) 0 #define A_REVERSE 0 #define COLOR_PAIR(a) 0 #define timeout(x) 0 +// end for tetris #ifndef TRUE #define TRUE 1 From 1d57bee66d4697a550e1df72d80e03e20478201b Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 08:54:27 -1100 Subject: [PATCH 169/787] Maketetris --- src/cc/gamescc.cpp | 4 +++- src/cc/maketetris | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 59adfae1d..454af601d 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -15,6 +15,8 @@ #include "gamescc.h" +#ifndef STANDALONE + /* ./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\" { @@ -1658,8 +1660,8 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) { sprintf(packitemstr,"not yet"); } +#else -#ifdef STANDALONE #include #include diff --git a/src/cc/maketetris b/src/cc/maketetris index 9c2a005a0..b83bef611 100755 --- a/src/cc/maketetris +++ b/src/cc/maketetris @@ -1,2 +1,2 @@ -gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE tetris.cpp -lncurses -o tetris +gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE gamescc.cpp -lncurses -o tetris From 3a7ca41dab30e01631befa8ca0ff02482ca5de4f Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:13:13 -1100 Subject: [PATCH 170/787] Realtime events --- src/cc/gamescc.cpp | 56 +++++++++++++++++++++++++++++----------------- src/cc/gamescc.h | 12 ++++++++++ 2 files changed, 47 insertions(+), 21 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 454af601d..33d6722cd 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -336,12 +336,34 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay } else return(-1); } +int32_t games_event(uint32_t timestamp,uint256 gametxid,int32_t eventid,std::vector payload) +{ + std::vector sig,vopret; CPubKey mypk; uint32_t x; int32_t i,len = payload.size(); + payload.resize(len + 4 + 32); + for (i=0; i<32; i++) + payload[len++] = ((uint8_t *)&gametxid)[i]; + x = eventid; + payload[len++] = x, x >>= 8; + payload[len++] = x, x >>= 8; + payload[len++] = x, x >>= 8; + payload[len++] = x; + mypk = pubkey2pk(Mypubkey()); + if ( games_eventsign(timestamp,sig,payload,mypk) == 0 ) + { + GetOpReturnData(games_eventopret(timestamp,mypk,sig,payload),vopret); + games_payloadrecv(mypk,timestamp,payload); + komodo_sendmessage(4,8,"events",vopret); + return(0); + } else return(-1); +} + UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { static uint256 lastgametxid; static uint32_t numevents; - UniValue result(UniValue::VOBJ); std::vector sig,payload,vopret; int32_t len,i,n; uint32_t x; CPubKey mypk; char str[67]; uint32_t eventid,timestamp = 0; uint256 gametxid; + UniValue result(UniValue::VOBJ); std::vector payload; int32_t len,i,n; uint32_t x; CPubKey mypk; char str[67]; uint32_t eventid,timestamp = 0; uint256 gametxid; if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 1 && n <= 3 ) { + mypk = pubkey2pk(Mypubkey()); if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) { if ( n >= 2 ) @@ -359,21 +381,8 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( numevents <= eventid ) numevents = eventid+1; } else eventid = numevents++; - len = payload.size(); - payload.resize(len + 4 + 32); - for (i=0; i<32; i++) - payload[len++] = ((uint8_t *)&gametxid)[i]; - x = eventid; - payload[len++] = x, x >>= 8; - payload[len++] = x, x >>= 8; - payload[len++] = x, x >>= 8; - payload[len++] = x; - mypk = pubkey2pk(Mypubkey()); - if ( games_eventsign(timestamp,sig,payload,mypk) == 0 ) + if ( games_event(timestamp,gametxid,eventid,payload) == 0 ) { - GetOpReturnData(games_eventopret(timestamp,mypk,sig,payload),vopret); - games_payloadrecv(mypk,timestamp,payload); - komodo_sendmessage(4,8,"events",vopret); result.push_back(Pair("gametxid",gametxid.GetHex())); result.push_back(Pair("eventid",(int64_t)eventid)); result.push_back(Pair("timestamp",(int64_t)timestamp)); @@ -2587,7 +2596,7 @@ static inline bool is_x64(void) { int main(int argc, char **argv) { uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; - + strcpy(ASSETCHAINS_SYMBOL,"GTEST"); #ifdef _WIN32 #ifdef _MSC_VER printf("*** games for Windows [ Build %s ] ***\n", BUILD_DATE); @@ -2610,8 +2619,8 @@ int main(int argc, char **argv) #ifdef _WIN32 #ifdef _MSC_VER - if (strncmp(ASSETCHAINS_SYMBOL, "TETRIS.EXE", sizeof(ASSETCHAINS_SYMBOL)) == 0 || strncmp(ASSETCHAINS_SYMBOL, "TETRIS54.EXE", sizeof(ASSETCHAINS_SYMBOL)) == 0) { - strcpy(ASSETCHAINS_SYMBOL, "TETRIS"); // accept TETRIS.conf, instead of TETRIS.EXE.conf or TETRIS54.EXE.conf if build with MSVC + if (strncmp(ASSETCHAINS_SYMBOL, "GTEST.EXE", sizeof(ASSETCHAINS_SYMBOL)) == 0 || strncmp(ASSETCHAINS_SYMBOL, "GTEST54.EXE", sizeof(ASSETCHAINS_SYMBOL)) == 0) { + strcpy(ASSETCHAINS_SYMBOL, "GTEST"); // accept TETRIS.conf, instead of TETRIS.EXE.conf or TETRIS54.EXE.conf if build with MSVC } #endif #endif @@ -3432,7 +3441,7 @@ int tetris(int argc, char **argv) tetris_move move = TM_NONE; bool running = true; WINDOW *board, *next, *hold, *score; - + int32_t c; uint256 gametxid; std::vector payload; uint32_t eventid = 0; // Load file if given a filename. if (argc >= 2) { FILE *f = fopen(argv[1], "r"); @@ -3462,6 +3471,7 @@ int tetris(int argc, char **argv) score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); int32_t counter = 0; // Game loop + payload.resize(1); while (running) { running = tg_tick(tg, move); display_board(board, tg); @@ -3471,8 +3481,12 @@ int tetris(int argc, char **argv) if ( (counter++ % 5) == 0 ) doupdate(); sleep_milli(10); - - switch (getch()) { + c = getch(); + payload[0] = c; + games_event(0,gametxid,eventid,payload); + eventid++; + switch ( c ) + { case KEY_LEFT: move = TM_LEFT; break; diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 8a6a3b656..9a48a14a4 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -96,6 +96,18 @@ struct games_player struct games_packitem gamespack[MAXPACK]; }; +struct games_state +{ + uint64_t seed; + char *keystrokes,*keystrokeshex; + uint32_t needflush,replaydone; + int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; + FILE *logfp; + struct games_player P; + char buffered[10000]; + uint8_t playerdata[10000]; +}; + int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); void games_packitemstr(char *packitemstr,struct games_packitem *item); From dfd756b938f96f3153d39099010bfd674fcd9a4d Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:23:42 -1100 Subject: [PATCH 171/787] issue_games_events --- src/cc/gamescc.cpp | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 33d6722cd..c73b0e46f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -2360,6 +2360,32 @@ char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) return(retstr2); } +int32_t issue_games_events(uint256 gametxid,uint32_t eventid,char c) +{ + char params[512],*retstr; cJSON *retjson,*retobj; int32_t retval = -1; + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c,gametxid.GetHex().c_str(),eventid); + if ( (retstr= komodo_issuemethod(USERPASS,"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,"result")) != 0 ) + { + retval = 0; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } + free(retstr); + } + return(retval); +} + int32_t games_sendrawtransaction(char *rawtx) { char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; @@ -3483,7 +3509,7 @@ int tetris(int argc, char **argv) sleep_milli(10); c = getch(); payload[0] = c; - games_event(0,gametxid,eventid,payload); + issue_games_events(0,gametxid,eventid,payload); eventid++; switch ( c ) { From 3ee77bc360950fe63170783c3d59403b108692f4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:26:42 -1100 Subject: [PATCH 172/787] Test --- src/cc/gamescc.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index c73b0e46f..6ea6981c1 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -2362,7 +2362,10 @@ char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) int32_t issue_games_events(uint256 gametxid,uint32_t eventid,char c) { + static FILE *fp; char params[512],*retstr; cJSON *retjson,*retobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c,gametxid.GetHex().c_str(),eventid); if ( (retstr= komodo_issuemethod(USERPASS,"cclib",params,GAMES_PORT)) != 0 ) { @@ -2371,8 +2374,6 @@ int32_t issue_games_events(uint256 gametxid,uint32_t eventid,char c) if ( (resobj= jobj(retjson,"result")) != 0 ) { retval = 0; - if ( fp == 0 ) - fp = fopen("events.log","wb"); if ( fp != 0 ) { fprintf(fp,"%s\n",jprint(resobj,0)); @@ -2380,9 +2381,9 @@ int32_t issue_games_events(uint256 gametxid,uint32_t eventid,char c) } } free_json(retjson); - } + } else fprintf(fp,"error parsing %s\n",jprint(retstr)); free(retstr); - } + } else fprintf(fp,"error issuing method %s\n",params); return(retval); } From 11b4733cb7e2c06e86764022cc730d76da1c4192 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:38:26 -1100 Subject: [PATCH 173/787] (char *) --- src/cc/gamescc.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 6ea6981c1..826012f43 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -2363,15 +2363,15 @@ char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) int32_t issue_games_events(uint256 gametxid,uint32_t eventid,char c) { static FILE *fp; - char params[512],*retstr; cJSON *retjson,*retobj; int32_t retval = -1; + char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; if ( fp == 0 ) fp = fopen("events.log","wb"); sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c,gametxid.GetHex().c_str(),eventid); - if ( (retstr= komodo_issuemethod(USERPASS,"cclib",params,GAMES_PORT)) != 0 ) + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { - if ( (resobj= jobj(retjson,"result")) != 0 ) + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) { retval = 0; if ( fp != 0 ) @@ -2381,7 +2381,7 @@ int32_t issue_games_events(uint256 gametxid,uint32_t eventid,char c) } } free_json(retjson); - } else fprintf(fp,"error parsing %s\n",jprint(retstr)); + } else fprintf(fp,"error parsing %s\n",retstr); free(retstr); } else fprintf(fp,"error issuing method %s\n",params); return(retval); @@ -2392,7 +2392,7 @@ int32_t games_sendrawtransaction(char *rawtx) char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; params = (char *)malloc(strlen(rawtx) + 16); sprintf(params,"[\"%s\"]",rawtx); - if ( (retstr= komodo_issuemethod(USERPASS,"sendrawtransaction",params,GAMES_PORT)) != 0 ) + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"sendrawtransaction",params,GAMES_PORT)) != 0 ) { if ( 0 ) // causes 4th level crash { @@ -2407,7 +2407,7 @@ int32_t games_sendrawtransaction(char *rawtx) } if ( (retjson= cJSON_Parse(retstr)) != 0 ) { - if ( (resobj= jobj(retjson,"result")) != 0 ) + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) { if ( (hexstr= jstr(resobj,0)) != 0 && is_hexstr(hexstr,64) == 64 ) retval = 0; @@ -2460,7 +2460,7 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,cha { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { - if ( (resobj= jobj(retjson,"result")) != 0 && (keys= jstr(resobj,"keystrokes")) != 0 ) + if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (keys= jstr(resobj,(char *)"keystrokes")) != 0 ) { len = strlen(keys) / 2; pastcmp = (char *)malloc(len + 1); @@ -2508,11 +2508,11 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,cha } if ( (retjson= cJSON_Parse(retstr)) != 0 ) { - if ( (resobj= jobj(retjson,"result")) != 0 && (rawtx= jstr(resobj,"hex")) != 0 ) + if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (rawtx= jstr(resobj,(char *)"hex")) != 0 ) { if ( rs->keystrokeshex != 0 ) free(rs->keystrokeshex); - if ( (errstr= jstr(resobj,"error")) == 0 ) + if ( (errstr= jstr(resobj,(char *)"error")) == 0 ) { rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); strcpy(rs->keystrokeshex,rawtx); @@ -2556,26 +2556,26 @@ int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) else { sprintf(params,"[\"gameinfo\",\"17\",\"[%%22%s%%22]\"]",gametxidstr); - filestr = komodo_issuemethod(USERPASS,"cclib",params,GAMES_PORT); + filestr = komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT); } if ( filestr != 0 ) { - if ( (retjson= cJSON_Parse(filestr)) != 0 && (resultjson= jobj(retjson,"result")) != 0 ) + if ( (retjson= cJSON_Parse(filestr)) != 0 && (resultjson= jobj(retjson,(char *)"result")) != 0 ) { //fprintf(stderr,"gameinfo.(%s)\n",jprint(resultjson,0)); - if ( (array= jarray(&n,resultjson,"players")) != 0 ) + if ( (array= jarray(&n,resultjson,(char *)"players")) != 0 ) { for (i=0; iP,(int32_t)strlen(datastr)/2,datastr); fprintf(stderr,"set pname[%s] %s\n",pname==0?"":pname,jprint(item,0)); From 5f2317bd8e368933c94665ebff2dac682a6ff8eb Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:42:42 -1100 Subject: [PATCH 174/787] fixes --- src/cc/gamescc.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 826012f43..5217d5fa1 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1684,6 +1684,9 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) char USERPASS[8192]; uint16_t GAMES_PORT; extern char Gametxidstr[67]; +#define MAX_STR 1024 +char whoami[MAXSTR]; + #define SMALLVAL 0.000000000000001 #define SATOSHIDEN ((uint64_t)100000000L) #define dstr(x) ((double)(x) / SATOSHIDEN) @@ -2433,7 +2436,7 @@ int32_t games_sendrawtransaction(char *rawtx) int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) { - char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*pastcmp,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; + char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; uint8_t *pastcmp; if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) { if ( rs->keystrokeshex != 0 ) @@ -2456,7 +2459,7 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,cha if ( 0 && (pastkeys= games_keystrokesload(&numpastkeys,seed,1)) != 0 ) { sprintf(params,"[\"extract\",\"17\",\"[%%22%s%%22]\"]",Gametxidstr); - if ( (retstr= komodo_issuemethod(USERPASS,"cclib",params,GAMES_PORT)) != 0 ) + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { @@ -2498,7 +2501,7 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,cha if ( fp == 0 ) fp = fopen("keystrokes.log","a"); sprintf(params,"[\"keystrokes\",\"17\",\"[%%22%s%%22,%%22%s%%22]\"]",Gametxidstr,hexstr); - if ( (retstr= komodo_issuemethod(USERPASS,"cclib",params,GAMES_PORT)) != 0 ) + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { if ( fp != 0 ) { @@ -3510,7 +3513,7 @@ int tetris(int argc, char **argv) sleep_milli(10); c = getch(); payload[0] = c; - issue_games_events(0,gametxid,eventid,payload); + issue_games_events(gametxid,eventid,payload); eventid++; switch ( c ) { From 89f6370c5e66aea81ec14fb7e55c64e0e02c9af0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:44:58 -1100 Subject: [PATCH 175/787] Test --- src/cc/gamescc.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 5217d5fa1..2f68fdfd8 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -13,10 +13,11 @@ * * ******************************************************************************/ -#include "gamescc.h" #ifndef STANDALONE +#include "gamescc.h" + /* ./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\" { @@ -1684,7 +1685,7 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) char USERPASS[8192]; uint16_t GAMES_PORT; extern char Gametxidstr[67]; -#define MAX_STR 1024 +#define MAXSTR 1024 char whoami[MAXSTR]; #define SMALLVAL 0.000000000000001 @@ -2466,7 +2467,7 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,cha if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (keys= jstr(resobj,(char *)"keystrokes")) != 0 ) { len = strlen(keys) / 2; - pastcmp = (char *)malloc(len + 1); + pastcmp = (uint8_t *)malloc(len + 1); decode_hex(pastcmp,len,keys); fprintf(stderr,"keystrokes.(%s) vs pastkeys\n",keys); for (i=0; i Date: Mon, 25 Mar 2019 09:47:58 -1100 Subject: [PATCH 176/787] #include "../core_io.h" #include "../script/sign.h" #include "../wallet/wallet.h" #include --- src/cc/gamescc.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 2f68fdfd8..de08b8992 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1681,6 +1681,9 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include #include #include +#include "../core_io.h" +#include "../wallet/wallet.h" +#include char USERPASS[8192]; uint16_t GAMES_PORT; extern char Gametxidstr[67]; From 43b96f15b9ed811c94433b246dde54e0f2526d66 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:52:12 -1100 Subject: [PATCH 177/787] Ifdef --- src/cc/gamescc.cpp | 3 ++- src/cc/gamescc.h | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index de08b8992..0721e88fe 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -13,10 +13,11 @@ * * ******************************************************************************/ +#include "gamescc.h" + #ifndef STANDALONE -#include "gamescc.h" /* ./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\" diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 9a48a14a4..b6b53e82e 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -1,6 +1,8 @@ #ifndef H_GAMESCC_H #define H_GAMESCC_H +#ifndef STANDALONE + #define ENABLE_WALLET extern CWallet* pwalletMain; @@ -82,6 +84,7 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(result); \ } \ } +#endif #define MAXPACK 23 struct games_packitem From d4d7d1adfdb6ab8cfab9f2e6a8129ff6fd9893f3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:53:19 -1100 Subject: [PATCH 178/787] Stdio --- src/cc/gamescc.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index b6b53e82e..034fee044 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -1,6 +1,9 @@ #ifndef H_GAMESCC_H #define H_GAMESCC_H +#include +#include + #ifndef STANDALONE #define ENABLE_WALLET From e774c89ab5c37ec3329dcc359cb6d1bc2a8cfd6f Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:55:11 -1100 Subject: [PATCH 179/787] -stdlib --- src/cc/gamescc.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 0721e88fe..57ff21131 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1676,7 +1676,6 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include #include -#include #include #include #include From e7031c29640864a0e8d109bb3a3c1593c09b25d4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:58:21 -1100 Subject: [PATCH 180/787] Test --- src/cc/gamescc.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 57ff21131..ffb92bc0d 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1679,12 +1679,15 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include #include #include -#include -#include #include "../core_io.h" #include "../wallet/wallet.h" #include +#define inline +#include +#include +#undef inline + char USERPASS[8192]; uint16_t GAMES_PORT; extern char Gametxidstr[67]; @@ -3475,7 +3478,7 @@ int tetris(int argc, char **argv) tetris_move move = TM_NONE; bool running = true; WINDOW *board, *next, *hold, *score; - int32_t c; uint256 gametxid; std::vector payload; uint32_t eventid = 0; + int32_t c; uint256 gametxid; uint32_t eventid = 0; // Load file if given a filename. if (argc >= 2) { FILE *f = fopen(argv[1], "r"); @@ -3516,8 +3519,7 @@ int tetris(int argc, char **argv) doupdate(); sleep_milli(10); c = getch(); - payload[0] = c; - issue_games_events(gametxid,eventid,payload); + issue_games_events(gametxid,eventid,c); eventid++; switch ( c ) { From 23cd6f2cbef1aab8af7b2606358085dddfbecb4f Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 09:58:56 -1100 Subject: [PATCH 181/787] Mv --- src/cc/gamescc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index ffb92bc0d..cc78ee5fe 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1679,13 +1679,13 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include #include #include -#include "../core_io.h" -#include "../wallet/wallet.h" -#include #define inline #include #include +#include "../core_io.h" +#include "../wallet/wallet.h" +#include #undef inline char USERPASS[8192]; uint16_t GAMES_PORT; From 901bc3fc952d0a3311a275fcabda8413372c5814 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:00:52 -1100 Subject: [PATCH 182/787] Test --- src/cc/gamescc.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index cc78ee5fe..9ec9f3cb0 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1679,14 +1679,8 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include #include #include - -#define inline #include #include -#include "../core_io.h" -#include "../wallet/wallet.h" -#include -#undef inline char USERPASS[8192]; uint16_t GAMES_PORT; extern char Gametxidstr[67]; From 6bce1c76135f585f3b9bf8e9b6443d411a960e61 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:03:02 -1100 Subject: [PATCH 183/787] Test --- src/cc/gamescc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 9ec9f3cb0..229e7d066 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -2364,7 +2364,7 @@ char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) return(retstr2); } -int32_t issue_games_events(uint256 gametxid,uint32_t eventid,char c) +int32_t issue_games_events(bits256 gametxid,uint32_t eventid,char c) { static FILE *fp; char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; @@ -3472,7 +3472,7 @@ int tetris(int argc, char **argv) tetris_move move = TM_NONE; bool running = true; WINDOW *board, *next, *hold, *score; - int32_t c; uint256 gametxid; uint32_t eventid = 0; + int32_t c; bits256 gametxid; uint32_t eventid = 0; // Load file if given a filename. if (argc >= 2) { FILE *f = fopen(argv[1], "r"); From 28f2d009b96e387c49f90e81c5ba98213e655c21 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:06:05 -1100 Subject: [PATCH 184/787] Void *malloc --- src/cc/gamescc.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 229e7d066..1c5818568 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1682,6 +1682,8 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include #include +void *malloc(size_t size); + char USERPASS[8192]; uint16_t GAMES_PORT; extern char Gametxidstr[67]; @@ -2367,10 +2369,10 @@ char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) int32_t issue_games_events(bits256 gametxid,uint32_t eventid,char c) { static FILE *fp; - char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + char params[512],*retstr,str[65]; cJSON *retjson,*resobj; int32_t retval = -1; if ( fp == 0 ) fp = fopen("events.log","wb"); - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c,gametxid.GetHex().c_str(),eventid); + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c,bits256_str(str,gametxid),eventid); if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) @@ -3502,7 +3504,6 @@ int tetris(int argc, char **argv) score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); int32_t counter = 0; // Game loop - payload.resize(1); while (running) { running = tg_tick(tg, move); display_board(board, tg); From a19b854238731b7373f73ad728b7c620031774e0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:09:35 -1100 Subject: [PATCH 185/787] -def --- src/cc/gamescc.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 1c5818568..81bd3f48b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1682,8 +1682,6 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include #include -void *malloc(size_t size); - char USERPASS[8192]; uint16_t GAMES_PORT; extern char Gametxidstr[67]; From 09589bd0e04aa334290418317447a73715531829 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:11:31 -1100 Subject: [PATCH 186/787] Test --- src/cc/gamescc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 81bd3f48b..34d0740c4 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1861,7 +1861,7 @@ long _stripwhite(char *buf,int accept) char *clonestr(char *str) { - char *clone; + char *clone; int32_t len; if ( str == 0 || str[0] == 0 ) { printf("warning cloning nullstr.%p\n",str); @@ -1870,7 +1870,8 @@ char *clonestr(char *str) #endif str = (char *)""; } - clone = (char *)malloc(strlen(str)+16); + len = strlen(str); + clone = (char *)malloc(len+16); strcpy(clone,str); return(clone); } From ce31112c570f45661be5a1586baf434844aad59b Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:12:09 -1100 Subject: [PATCH 187/787] Calloc --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 34d0740c4..4d461ee08 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1871,7 +1871,7 @@ char *clonestr(char *str) str = (char *)""; } len = strlen(str); - clone = (char *)malloc(len+16); + clone = (char *)calloc(1,len+16); strcpy(clone,str); return(clone); } From f83da7830241f3c22878b8a279be2fa1971f6ea9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:14:14 -1100 Subject: [PATCH 188/787] char *clonestr(char *str) --- src/cc/gamescc.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 4d461ee08..cd87e9995 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1684,6 +1684,7 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) char USERPASS[8192]; uint16_t GAMES_PORT; extern char Gametxidstr[67]; +char *clonestr(char *str); #define MAXSTR 1024 char whoami[MAXSTR]; @@ -1859,23 +1860,6 @@ long _stripwhite(char *buf,int accept) return(j); } -char *clonestr(char *str) -{ - char *clone; int32_t len; - if ( str == 0 || str[0] == 0 ) - { - printf("warning cloning nullstr.%p\n",str); -#ifdef __APPLE__ - while ( 1 ) sleep(1); -#endif - str = (char *)""; - } - len = strlen(str); - clone = (char *)calloc(1,len+16); - strcpy(clone,str); - return(clone); -} - char *parse_conf_line(char *line,char *field) { line += strlen(field); @@ -2722,6 +2706,22 @@ int main(int argc, char **argv) #include #endif +char *clonestr(char *str) +{ + char *clone; int32_t len; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + len = strlen(str); + clone = (char *)calloc(1,len+16); + strcpy(clone,str); + return(clone); +} /* Convert a tetromino type to its corresponding cell. From 8665d036e6abc1718c145ee02a7508b06193d0ab Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:15:27 -1100 Subject: [PATCH 189/787] char *clonestr(char *str) --- src/cc/gamescc.cpp | 2 +- src/cc/maketetris | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index cd87e9995..fa36e8537 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1683,7 +1683,7 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) #include char USERPASS[8192]; uint16_t GAMES_PORT; -extern char Gametxidstr[67]; +char Gametxidstr[67]; char *clonestr(char *str); #define MAXSTR 1024 diff --git a/src/cc/maketetris b/src/cc/maketetris index b83bef611..9f0af354e 100755 --- a/src/cc/maketetris +++ b/src/cc/maketetris @@ -1,2 +1,2 @@ -gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE gamescc.cpp -lncurses -o tetris +gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE gamescc.cpp -lncurses -lcurl -o tetris From be9c3500ac7c7d8170c908580f8bf97c12f2faff Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:17:38 -1100 Subject: [PATCH 190/787] Games replay --- src/cc/gamescc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index fa36e8537..9fb135dfe 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -3568,5 +3568,10 @@ int tetris(int argc, char **argv) return 0; } +int32_t games_replay(uint64_t seed,int32_t sleeptime) +{ + return(-1); +} + #endif From 361c928fc835fa1bfca5124ca28f946f40092fc3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:20:53 -1100 Subject: [PATCH 191/787] Move clonestr --- src/cc/gamescc.cpp | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 9fb135dfe..ca9419d83 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -2706,23 +2706,6 @@ int main(int argc, char **argv) #include #endif -char *clonestr(char *str) -{ - char *clone; int32_t len; - if ( str == 0 || str[0] == 0 ) - { - printf("warning cloning nullstr.%p\n",str); -#ifdef __APPLE__ - while ( 1 ) sleep(1); -#endif - str = (char *)""; - } - len = strlen(str); - clone = (char *)calloc(1,len+16); - strcpy(clone,str); - return(clone); -} - /* Convert a tetromino type to its corresponding cell. */ @@ -3466,6 +3449,22 @@ void init_colors(void) Main tetris game! */ #ifdef STANDALONE +char *clonestr(char *str) +{ + char *clone; int32_t len; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + len = strlen(str); + clone = (char *)calloc(1,len+16); + strcpy(clone,str); + return(clone); +} int tetris(int argc, char **argv) { From 63ebb9d604ed36af16de650e183a14ba03e35ad0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:25:44 -1100 Subject: [PATCH 192/787] strcpy(ASSETCHAINS_SYMBOL,"GTEST"); --- src/cc/gamescc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index ca9419d83..85f01ba43 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -2612,7 +2612,6 @@ static inline bool is_x64(void) { int main(int argc, char **argv) { uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; - strcpy(ASSETCHAINS_SYMBOL,"GTEST"); #ifdef _WIN32 #ifdef _MSC_VER printf("*** games for Windows [ Build %s ] ***\n", BUILD_DATE); @@ -2640,7 +2639,8 @@ int main(int argc, char **argv) } #endif #endif - + strcpy(ASSETCHAINS_SYMBOL,"GTEST"); + GAMES_PORT = komodo_userpass(userpass,ASSETCHAINS_SYMBOL); if ( IPADDRESS[0] == 0 ) strcpy(IPADDRESS,"127.0.0.1"); From ab954658167edf41a0233fd0ac0cc3cadde87ddd Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:45:04 -1100 Subject: [PATCH 193/787] Test --- src/cc/gamescc.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 85f01ba43..c80c4e161 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -387,6 +387,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { result.push_back(Pair("gametxid",gametxid.GetHex())); result.push_back(Pair("eventid",(int64_t)eventid)); + result.push_back(Pair("payload",jstr(jitem(params,0),0))); result.push_back(Pair("timestamp",(int64_t)timestamp)); result.push_back(Pair("result","success")); result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); @@ -436,9 +437,9 @@ void komodo_netevent(std::vector message) } } } - for (i=0; i= 2) { FILE *f = fopen(argv[1], "r"); From 2a4a05055748694b8b49d1d44d72610bc6233ed2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 10:49:02 -1100 Subject: [PATCH 194/787] Test --- src/cc/gamescc.cpp | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index c80c4e161..7f7b15c89 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -51,6 +51,24 @@ ./c cclib events 17 \"[%226d%22,%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,1]\" */ +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) +{ + uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) + { + len -= 36; + for (i=0; i<32; i++) + ((uint8_t *)&gametxid)[i] = payload[len+i]; + eventid = (uint32_t)payload[len+32]; + eventid |= (uint32_t)payload[len+33] << 8; + eventid |= (uint32_t)payload[len+34] << 16; + eventid |= (uint32_t)payload[len+35] << 24; + for (i=0; i &sig,std::vecto } else return(-1); } -int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) -{ - uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; - if ( (len= payload.size()) > 36 ) - { - len -= 36; - for (i=0; i<32; i++) - ((uint8_t *)&gametxid)[i] = payload[len+i]; - eventid = (uint32_t)payload[len+32]; - eventid |= (uint32_t)payload[len+33] << 8; - eventid |= (uint32_t)payload[len+34] << 16; - eventid |= (uint32_t)payload[len+35] << 24; - for (i=0; i payload) { std::vector sig,vopret; CPubKey mypk; uint32_t x; int32_t i,len = payload.size(); @@ -3514,7 +3513,8 @@ int tetris(int argc, char **argv) doupdate(); sleep_milli(10); c = getch(); - issue_games_events(gametxid,eventid,c); + if ( c >= 0 ) + issue_games_events(gametxid,eventid,c); eventid++; switch ( c ) { From 9277db7e1e0beef080feb6f753e79724c36899d8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 11:11:24 -1100 Subject: [PATCH 195/787] Skip count --- src/cc/gamescc.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 7f7b15c89..b355584db 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -3472,7 +3472,7 @@ int tetris(int argc, char **argv) tetris_move move = TM_NONE; bool running = true; WINDOW *board, *next, *hold, *score; - int32_t c; bits256 gametxid; uint32_t eventid = 0; + int32_t c,skipcount=0; bits256 gametxid; uint32_t eventid = 0; memset(&gametxid,0,sizeof(gametxid)); // Load file if given a filename. if (argc >= 2) { @@ -3514,7 +3514,12 @@ int tetris(int argc, char **argv) sleep_milli(10); c = getch(); if ( c >= 0 ) + { + if ( skipcount > 0 ) + issue_games_events(gametxid,eventid-skipcount,skipcount | 0x4000); issue_games_events(gametxid,eventid,c); + skipcount = 0; + } else skipcount++; eventid++; switch ( c ) { From 81006ce271fc91f09abe725c580ea8d1acc030f5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 11:50:08 -1100 Subject: [PATCH 196/787] Match rogue roc --- src/cc/gamescc.cpp | 6 +++--- src/cc/gamescc.h | 37 ++++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index b355584db..758d2fc78 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -964,7 +964,7 @@ void games_gameplayerinfo(struct CCcontract_info *cp,UniValue &obj,uint256 gamet } else fprintf(stderr,"findbaton err.%d\n",retval); } -UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey gamespk,mypk; char *jsonstr; uint64_t inputsum,change,required,buyin=0; int32_t i,n,maxplayers = 1; @@ -1030,7 +1030,7 @@ UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,gameheight,maxplayers,numvouts; uint256 txid; CTransaction tx; int64_t buyin; uint64_t seed; bits256 t; char myaddr[64],str[64]; CPubKey mypk,gamespk; result.push_back(Pair("name","games")); @@ -1599,7 +1599,7 @@ UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -UniValue games_list(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); uint256 txid,hashBlock,gametxid,tokenid,playertxid; int32_t vout,maxplayers,gameheight,numvouts; CPubKey gamespk,mypk; char coinaddr[64]; CTransaction tx,gametx; int64_t buyin; std::vector > addressIndex; diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 034fee044..19e5f8d26 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -35,29 +35,28 @@ std::string Games_pname; { (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"players", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ - { (char *)MYCCNAME, (char *)"list", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"games", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"pending", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"setname", (char *)"pname", 1, 1, ' ', EVAL_GAMES }, \ - { (char *)MYCCNAME, (char *)"create", (char *)"maxplayers,buyin", 2, 2, 'C', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"newgame", (char *)"maxplayers,buyin", 2, 2, 'C', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"playerinfo", (char *)"playertxid", 1, 1, ' ', EVAL_GAMES }, \ - { (char *)MYCCNAME, (char *)"info", (char *)"gametxid", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"gameinfo", (char *)"gametxid", 1, 1, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"keystrokes", (char *)"txid,hexstr", 2, 2, 'K', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"finish", (char *)"gametxid", 1, 1, 'Q', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"events", (char *)"eventshex [gametxid [eventid]]", 1, 3, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"register", (char *)"gametxid [playertxid]", 1, 2, 'R', EVAL_GAMES }, - bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); -UniValue games_list(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); -UniValue games_create(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); -UniValue games_info(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); @@ -71,14 +70,30 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_rng(txfee,cp,params)); \ else if ( strcmp(method,"rngnext") == 0 ) \ return(games_rngnext(txfee,cp,params)); \ - else if ( strcmp(method,"create") == 0 ) \ - return(games_create(txfee,cp,params)); \ - else if ( strcmp(method,"info") == 0 ) \ - return(games_info(txfee,cp,params)); \ + else if ( strcmp(method,"newgame") == 0 ) \ + return(games_newgame(txfee,cp,params)); \ + else if ( strcmp(method,"gameinfo") == 0 ) \ + return(games_gameinfo(txfee,cp,params)); \ else if ( strcmp(method,"register") == 0 ) \ return(games_register(txfee,cp,params)); \ else if ( strcmp(method,"events") == 0 ) \ return(games_events(txfee,cp,params)); \ + else if ( strcmp(method,"players") == 0 ) \ + return(games_players(txfee,cp,params)); \ + else if ( strcmp(method,"games") == 0 ) \ + return(games_games(txfee,cp,params)); \ + else if ( strcmp(method,"pending") == 0 ) \ + return(games_pending(txfee,cp,params)); \ + else if ( strcmp(method,"setname") == 0 ) \ + return(games_setname(txfee,cp,params)); \ + else if ( strcmp(method,"playerinfo") == 0 ) \ + return(games_playerinfo(txfee,cp,params)); \ + else if ( strcmp(method,"keystrokes") == 0 ) \ + return(games_keystrokes(txfee,cp,params)); \ + else if ( strcmp(method,"extract") == 0 ) \ + return(games_extract(txfee,cp,params)); \ + else if ( strcmp(method,"finish") == 0 ) \ + return(games_finish(txfee,cp,params)); \ else \ { \ result.push_back(Pair("result","error")); \ From c7ac37f03a713fbbbf61cff73eda72be8127174a Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 26 Mar 2019 14:23:35 +0800 Subject: [PATCH 197/787] add check for transaction fee --- src/cc/rewards.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 1351bbaeb..65e32ee27 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -197,6 +197,9 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t uint256 txid,fundingtxid,hashBlock,vinfundingtxid; uint64_t vinsbits,sbits,APR,minseconds,maxseconds,mindeposit,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx; numvins = tx.vin.size(); numvouts = tx.vout.size(); + int64_t interest; uint64_t valuein; + CCoinsViewCache &view = *pcoinsTip; + valuein = view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime); preventCCvins = preventCCvouts = -1; if ( numvouts < 1 ) return eval->Invalid("no vouts"); @@ -255,6 +258,11 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 ) return eval->Invalid("unexpected normal vin for unlock"); } + if ( valuein-tx.GetValueOut() > txfee ) + { + fprintf(stderr, "valueout.%li vs valuein.%li txfee.%li\n", tx.GetValueOut(), valuein, txfee); + return eval->Invalid("alright is stealing your money"); + } if ( numvouts == 2 && numvins == 1 ) { if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 ) @@ -707,4 +715,3 @@ std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint2 fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN); return(""); } - From d08fedb7a43ac4740f8d29d7e93a2df181e17b6a Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 25 Mar 2019 22:12:33 -1100 Subject: [PATCH 198/787] if ( komodo_nextheight() > 77777 && cashout > ROGUE_MAXCASHOUT ) --- src/cc/gamescc.cpp | 5 +++-- src/cc/rogue_rpc.cpp | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 758d2fc78..38e28a416 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -3513,11 +3513,12 @@ int tetris(int argc, char **argv) doupdate(); sleep_milli(10); c = getch(); - if ( c >= 0 ) + if ( c != -1 || skipcount == 0x3fff ) { if ( skipcount > 0 ) issue_games_events(gametxid,eventid-skipcount,skipcount | 0x4000); - issue_games_events(gametxid,eventid,c); + if ( c != -1 ) + issue_games_events(gametxid,eventid,c); skipcount = 0; } else skipcount++; eventid++; diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index fbb1a3d93..ee5f3d566 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -22,6 +22,7 @@ #define ROGUE_MAXPLAYERS 64 // need to send unused fees back to globalCC address to prevent leeching #define ROGUE_MAXKEYSTROKESGAP 60 #define ROGUE_MAXITERATIONS 777 +#define ROGUE_MAXCASHOUT (777 * COIN) #include "rogue/rogue_player.h" @@ -1112,6 +1113,8 @@ int32_t rogue_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct C dungeonlevel = P.dungeonlevel; if ( P.amulet != 0 && dungeonlevel < 26 ) dungeonlevel = 26; + if ( dungeonlevel > 42 ) + dungeonlevel = 42; *cashoutp = (uint64_t)P.gold * P.gold * mult * dungeonlevel; if ( newdata == playerdata ) { @@ -1281,6 +1284,8 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param } if ( cashout > 0 ) { + if ( komodo_nextheight() > 77777 && cashout > ROGUE_MAXCASHOUT ) + cashout = ROGUE_MAXCASHOUT; if ( (inputsum= AddCClibInputs(cp,mtx,roguepk,cashout,60,cp->unspendableCCaddr)) > cashout ) CCchange = (inputsum - cashout); else fprintf(stderr,"couldnt find enough utxos\n"); @@ -1598,6 +1603,8 @@ bool rogue_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C cashout *= 2; //cashout += numplayers * buyin; } + if ( height > 777777 && cashout > ROGUE_MAXCASHOUT ) + cashout = ROGUE_MAXCASHOUT; sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d txid.%s %.8f vs vout2 %.8f",tokentx,decoded,height,txid.GetHex().c_str(),(double)cashout/COIN,(double)tx.vout[2].nValue/COIN); if ( strcmp(laststr,cashstr) != 0 ) { From fa0a35aa5a2dac695e4452587f5c9b18e5943be0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:11:36 -1100 Subject: [PATCH 199/787] Split out Tetris --- src/cc/dapps/dappstd.c | 1020 +++++++++++++++++ src/cc/gamescc.cpp | 2048 +--------------------------------- src/cc/gamescc.h | 32 +- src/cc/tetris.c | 758 +++++++++++++ src/cc/tetris.cpp | 2376 +--------------------------------------- src/cc/tetris.h | 197 ++++ 6 files changed, 2046 insertions(+), 4385 deletions(-) create mode 100644 src/cc/dapps/dappstd.c create mode 100644 src/cc/tetris.c create mode 100644 src/cc/tetris.h diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c new file mode 100644 index 000000000..ec8045b2f --- /dev/null +++ b/src/cc/dapps/dappstd.c @@ -0,0 +1,1020 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +// requires CHAINNAME and GAMEMAIN() to be #defined + +#include +#include +#include +#include +#include +#include +#include + +char USERPASS[8192]; uint16_t GAMES_PORT; +char Gametxidstr[67]; +char *clonestr(char *str); + +#define MAXSTR 1024 +char whoami[MAXSTR]; + +#define SMALLVAL 0.000000000000001 +#define SATOSHIDEN ((uint64_t)100000000L) +#define dstr(x) ((double)(x) / SATOSHIDEN) +#define KOMODO_ASSETCHAIN_MAXLEN 65 +char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],IPADDRESS[100]; + +#ifndef _BITS256 +#define _BITS256 +union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; +typedef union _bits256 bits256; +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +int gettimeofday(struct timeval * tp, struct timezone * tzp) +{ + // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; +} +#endif // _MSC_VER +#endif + +double OS_milliseconds() +{ + struct timeval tv; double millis; +#ifdef __MINGW32__ + mingw_gettimeofday(&tv,NULL); +#else + gettimeofday(&tv,NULL); +#endif + millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.); + //printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis); + return(millis); +} + +int32_t _unhex(char c) +{ + if ( c >= '0' && c <= '9' ) + return(c - '0'); + else if ( c >= 'a' && c <= 'f' ) + return(c - 'a' + 10); + else if ( c >= 'A' && c <= 'F' ) + return(c - 'A' + 10); + return(-1); +} + +int32_t is_hexstr(char *str,int32_t n) +{ + int32_t i; + if ( str == 0 || str[0] == 0 ) + return(0); + for (i=0; str[i]!=0; i++) + { + if ( n > 0 && i >= n ) + break; + if ( _unhex(str[i]) < 0 ) + break; + } + if ( n == 0 ) + return(i); + return(i == n); +} + +int32_t unhex(char c) +{ + int32_t hex; + if ( (hex= _unhex(c)) < 0 ) + { + //printf("unhex: illegal hexchar.(%c)\n",c); + } + return(hex); +} + +unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } + +int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex) +{ + int32_t adjust,i = 0; + //printf("decode.(%s)\n",hex); + if ( is_hexstr(hex,n) <= 0 ) + { + memset(bytes,0,n); + return(n); + } + if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) + hex[--n] = 0; + if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) + { + if ( n > 0 ) + { + bytes[0] = unhex(hex[0]); + printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); + } + bytes++; + hex++; + adjust = 1; + } else adjust = 0; + if ( n > 0 ) + { + for (i=0; i>4) & 0xf); + hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); + //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); + } + hexbytes[len*2] = 0; + //printf("len.%ld\n",len*2+1); + return((int32_t)len*2+1); +} + +char *bits256_str(char hexstr[65],bits256 x) +{ + init_hexbytes_noT(hexstr,x.bytes,sizeof(x)); + return(hexstr); +} + +long _stripwhite(char *buf,int accept) +{ + int32_t i,j,c; + if ( buf == 0 || buf[0] == 0 ) + return(0); + for (i=j=0; buf[i]!=0; i++) + { + buf[j] = c = buf[i]; + if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) + j++; + } + buf[j] = 0; + return(j); +} + +char *parse_conf_line(char *line,char *field) +{ + line += strlen(field); + for (; *line!='='&&*line!=0; line++) + break; + if ( *line == 0 ) + return(0); + if ( *line == '=' ) + line++; + while ( line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n' || line[strlen(line)-1] == ' ' ) + line[strlen(line)-1] = 0; + //printf("LINE.(%s)\n",line); + _stripwhite(line,0); + return(clonestr(line)); +} + +int32_t safecopy(char *dest,char *src,long len) +{ + int32_t i = -1; + if ( src != 0 && dest != 0 && src != dest ) + { + if ( dest != 0 ) + memset(dest,0,len); + for (i=0; i buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +uint8_t *OS_fileptr(long *allocsizep,char *fname) +{ + long filesize = 0; uint8_t *buf = 0; void *retptr; + *allocsizep = 0; + retptr = OS_loadfile(fname,&buf,&filesize,allocsizep); + return((uint8_t *)retptr); +} + +struct MemoryStruct { char *memory; size_t size; }; +struct return_string { char *ptr; size_t len; }; + +// return data from the server +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) + + +/************************************************************************ + * + * Initialize the string handler so that it is thread safe + * + ************************************************************************/ + +void init_string(struct return_string *s) +{ + s->len = 0; + s->ptr = (char *)calloc(1,s->len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr,"init_string malloc() failed\n"); + exit(-1); + } + s->ptr[0] = '\0'; +} + +/************************************************************************ + * + * Use the "writer" to accumulate text until done + * + ************************************************************************/ + +size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string *s) +{ + size_t new_len = s->len + size*nmemb; + s->ptr = (char *)realloc(s->ptr,new_len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr, "accumulate realloc() failed\n"); + exit(-1); + } + memcpy(s->ptr+s->len,ptr,size*nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + return(size * nmemb); +} + +/************************************************************************ + * + * return the current system time in milliseconds + * + ************************************************************************/ + +#define EXTRACT_BITCOIND_RESULT // if defined, ensures error is null and returns the "result" field +#ifdef EXTRACT_BITCOIND_RESULT + +/************************************************************************ + * + * perform post processing of the results + * + ************************************************************************/ + +char *post_process_bitcoind_RPC(char *debugstr,char *command,char *rpcstr,char *params) +{ + long i,j,len; char *retstr = 0; cJSON *json,*result,*error; + //printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + if ( command == 0 || rpcstr == 0 || rpcstr[0] == 0 ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + return(rpcstr); + } + json = cJSON_Parse(rpcstr); + if ( json == 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s can't parse.(%s) params.(%s)\n",debugstr,command,rpcstr,params); + free(rpcstr); + return(0); + } + result = cJSON_GetObjectItem(json,"result"); + error = cJSON_GetObjectItem(json,"error"); + if ( error != 0 && result != 0 ) + { + if ( (error->type&0xff) == cJSON_NULL && (result->type&0xff) != cJSON_NULL ) + { + retstr = cJSON_Print(result); + len = strlen(retstr); + if ( retstr[0] == '"' && retstr[len-1] == '"' ) + { + for (i=1,j=0; itype&0xff) != cJSON_NULL || (result->type&0xff) != cJSON_NULL ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC (%s) error.%s\n",debugstr,command,rpcstr); + } + free(rpcstr); + } else retstr = rpcstr; + free_json(json); + //fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC: postprocess returns.(%s)\n",retstr); + return(retstr); +} +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + +/************************************************************************ + * + * perform the query + * + ************************************************************************/ + +char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params) +{ + static int didinit,count,count2; static double elapsedsum,elapsedsum2; + struct curl_slist *headers = NULL; struct return_string s; CURLcode res; CURL *curl_handle; + char *bracket0,*bracket1,*databuf = 0; long len; int32_t specialcase,numretries; double starttime; + if ( didinit == 0 ) + { + didinit = 1; + curl_global_init(CURL_GLOBAL_ALL); //init the curl session + } + numretries = 0; + if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") == 0 ) + specialcase = 1; + else specialcase = 0; + if ( url[0] == 0 ) + strcpy(url,"http://127.0.0.1:7876/nxt"); + if ( specialcase != 0 && 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: debug.(%s) url.(%s) command.(%s) params.(%s)\n",debugstr,url,command,params); +try_again: + if ( retstrp != 0 ) + *retstrp = 0; + starttime = OS_milliseconds(); + curl_handle = curl_easy_init(); + init_string(&s); + headers = curl_slist_append(0,"Expect:"); + + curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl_handle,CURLOPT_URL, url); + curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function + curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback + curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash + curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback + if ( strncmp(url,"https",5) == 0 ) + { + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0); + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0); + } + if ( userpass != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_USERPWD, userpass); + databuf = 0; + if ( params != 0 ) + { + if ( command != 0 && specialcase == 0 ) + { + len = strlen(params); + if ( len > 0 && params[0] == '[' && params[len-1] == ']' ) { + bracket0 = bracket1 = (char *)""; + } + else + { + bracket0 = (char *)"["; + bracket1 = (char *)"]"; + } + + databuf = (char *)malloc(256 + strlen(command) + strlen(params)); + sprintf(databuf,"{\"id\":\"jl777\",\"method\":\"%s\",\"params\":%s%s%s}",command,bracket0,params,bracket1); + //printf("url.(%s) userpass.(%s) databuf.(%s)\n",url,userpass,databuf); + // + } //else if ( specialcase != 0 ) fprintf(stderr,"databuf.(%s)\n",params); + curl_easy_setopt(curl_handle,CURLOPT_POST,1L); + if ( databuf != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,databuf); + else curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,params); + } + //laststart = milliseconds(); + res = curl_easy_perform(curl_handle); + curl_slist_free_all(headers); + curl_easy_cleanup(curl_handle); + if ( databuf != 0 ) // clean up temporary buffer + { + free(databuf); + databuf = 0; + } + if ( res != CURLE_OK ) + { + numretries++; + if ( specialcase != 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); + free(s.ptr); + return(0); + } + else if ( numretries >= 1 ) + { + //printf("Maximum number of retries exceeded!\n"); + free(s.ptr); + return(0); + } + if ( (rand() % 1000) == 0 ) + printf( "curl_easy_perform() failed: %s %s.(%s %s), retries: %d\n",curl_easy_strerror(res),debugstr,url,command,numretries); + free(s.ptr); + sleep((1< (%s)\n",params,s.ptr); + count2++; + elapsedsum2 += (OS_milliseconds() - starttime); + if ( (count2 % 10000) == 0) + printf("%d: ave %9.6f | elapsed %.3f millis | NXT calls.(%s) cmd.(%s)\n",count2,elapsedsum2/count2,(double)(OS_milliseconds() - starttime),url,command); + return(s.ptr); + } + } + printf("bitcoind_RPC: impossible case\n"); + free(s.ptr); + return(0); +} + +static size_t WriteMemoryCallback(void *ptr,size_t size,size_t nmemb,void *data) +{ + size_t realsize = (size * nmemb); + struct MemoryStruct *mem = (struct MemoryStruct *)data; + mem->memory = (char *)((ptr != 0) ? realloc(mem->memory,mem->size + realsize + 1) : malloc(mem->size + realsize + 1)); + if ( mem->memory != 0 ) + { + if ( ptr != 0 ) + memcpy(&(mem->memory[mem->size]),ptr,realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + //printf("got %d bytes\n",(int32_t)(size*nmemb)); + return(realsize); +} + +char *curl_post(CURL **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3) +{ + struct MemoryStruct chunk; CURL *cHandle; long code; struct curl_slist *headers = 0; + if ( (cHandle= *cHandlep) == NULL ) + *cHandlep = cHandle = curl_easy_init(); + else curl_easy_reset(cHandle); + //#ifdef DEBUG + //curl_easy_setopt(cHandle,CURLOPT_VERBOSE, 1); + //#endif + curl_easy_setopt(cHandle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(cHandle,CURLOPT_SSL_VERIFYPEER,0); + //curl_easy_setopt(cHandle,CURLOPT_SSLVERSION,1); + curl_easy_setopt(cHandle,CURLOPT_URL,url); + curl_easy_setopt(cHandle,CURLOPT_CONNECTTIMEOUT,10); + if ( userpass != 0 && userpass[0] != 0 ) + curl_easy_setopt(cHandle,CURLOPT_USERPWD,userpass); + if ( postfields != 0 && postfields[0] != 0 ) + { + curl_easy_setopt(cHandle,CURLOPT_POST,1); + curl_easy_setopt(cHandle,CURLOPT_POSTFIELDS,postfields); + } + if ( hdr0 != NULL && hdr0[0] != 0 ) + { + //printf("HDR0.(%s) HDR1.(%s) HDR2.(%s) HDR3.(%s)\n",hdr0!=0?hdr0:"",hdr1!=0?hdr1:"",hdr2!=0?hdr2:"",hdr3!=0?hdr3:""); + headers = curl_slist_append(headers,hdr0); + if ( hdr1 != 0 && hdr1[0] != 0 ) + headers = curl_slist_append(headers,hdr1); + if ( hdr2 != 0 && hdr2[0] != 0 ) + headers = curl_slist_append(headers,hdr2); + if ( hdr3 != 0 && hdr3[0] != 0 ) + headers = curl_slist_append(headers,hdr3); + } //headers = curl_slist_append(0,"Expect:"); + if ( headers != 0 ) + curl_easy_setopt(cHandle,CURLOPT_HTTPHEADER,headers); + //res = curl_easy_perform(cHandle); + memset(&chunk,0,sizeof(chunk)); + curl_easy_setopt(cHandle,CURLOPT_WRITEFUNCTION,WriteMemoryCallback); + curl_easy_setopt(cHandle,CURLOPT_WRITEDATA,(void *)&chunk); + curl_easy_perform(cHandle); + curl_easy_getinfo(cHandle,CURLINFO_RESPONSE_CODE,&code); + if ( headers != 0 ) + curl_slist_free_all(headers); + if ( code != 200 ) + printf("(%s) server responded with code %ld (%s)\n",url,code,chunk.memory); + return(chunk.memory); +} + +uint16_t _komodo_userpass(char *username, char *password, FILE *fp) +{ + char *rpcuser,*rpcpassword,*str,*ipaddress,line[8192]; uint16_t port = 0; + rpcuser = rpcpassword = 0; + username[0] = password[0] = 0; + while ( fgets(line,sizeof(line),fp) != 0 ) + { + if ( line[0] == '#' ) + continue; + //printf("line.(%s) %p %p\n",line,strstr(line,(char *)"rpcuser"),strstr(line,(char *)"rpcpassword")); + if ( (str= strstr(line,(char *)"rpcuser")) != 0 ) + rpcuser = parse_conf_line(str,(char *)"rpcuser"); + else if ( (str= strstr(line,(char *)"rpcpassword")) != 0 ) + rpcpassword = parse_conf_line(str,(char *)"rpcpassword"); + else if ( (str= strstr(line,(char *)"rpcport")) != 0 ) + { + port = atoi(parse_conf_line(str,(char *)"rpcport")); + //fprintf(stderr,"rpcport.%u in file\n",port); + } + else if ( (str= strstr(line,(char *)"ipaddress")) != 0 ) + { + ipaddress = parse_conf_line(str,(char *)"ipaddress"); + strcpy(IPADDRESS,ipaddress); + } + } + if ( rpcuser != 0 && rpcpassword != 0 ) + { + strcpy(username,rpcuser); + strcpy(password,rpcpassword); + } + //printf("rpcuser.(%s) rpcpassword.(%s) %u ipaddress.%s\n",rpcuser,rpcpassword,port,ipaddress); + if ( rpcuser != 0 ) + free(rpcuser); + if ( rpcpassword != 0 ) + free(rpcpassword); + return(port); +} + +uint16_t komodo_userpass(char *userpass,char *symbol) +{ + FILE *fp; uint16_t port = 0; char fname[512],username[512],password[512],confname[KOMODO_ASSETCHAIN_MAXLEN]; + userpass[0] = 0; + if ( strcmp("KMD",symbol) == 0 ) + { +#ifdef __APPLE__ + sprintf(confname,"Komodo.conf"); +#else + sprintf(confname,"komodo.conf"); +#endif + } + else sprintf(confname,"%s.conf",symbol); + //komodo_statefname(fname,symbol,confname); + if ( (fp= fopen(confname,"rb")) != 0 ) + { + port = _komodo_userpass(username,password,fp); + sprintf(userpass,"%s:%s",username,password); + if ( strcmp(symbol,ASSETCHAINS_SYMBOL) == 0 ) + strcpy(USERPASS,userpass); + fclose(fp); + } + return(port); +} + +#define is_cJSON_True(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_True) + +char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) +{ + //static void *cHandle; + char url[512],*retstr=0,*retstr2=0,postdata[8192]; + if ( params == 0 || params[0] == 0 ) + params = (char *)"[]"; + if ( strlen(params) < sizeof(postdata)-128 ) + { + sprintf(url,(char *)"http://%s:%u",IPADDRESS,port); + sprintf(postdata,"{\"method\":\"%s\",\"params\":%s}",method,params); + //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,USERPASS); + retstr2 = bitcoind_RPC(&retstr,(char *)"debug",url,userpass,method,params); + //retstr = curl_post(&cHandle,url,USERPASS,postdata,0,0,0,0); + } + return(retstr2); +} + +int32_t issue_games_events(bits256 gametxid,uint32_t eventid,int32_t c) +{ + static FILE *fp; + char params[512],*retstr,str[65]; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c,bits256_str(str,gametxid),eventid); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); +} + +int32_t games_sendrawtransaction(char *rawtx) +{ + char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; + params = (char *)malloc(strlen(rawtx) + 16); + sprintf(params,"[\"%s\"]",rawtx); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"sendrawtransaction",params,GAMES_PORT)) != 0 ) + { + if ( 0 ) // causes 4th level crash + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("games.sendlog","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + if ( (hexstr= jstr(resobj,0)) != 0 && is_hexstr(hexstr,64) == 64 ) + retval = 0; + } + free_json(retjson); + } + + /* log sendrawtx result in file */ + + /* + FILE *debug_file; + debug_file = fopen("tx_debug.log", "a"); + fprintf(debug_file, "%s\n", retstr); + fflush(debug_file); + fclose(debug_file); + */ + + free(retstr); + } + free(params); + return(retval); +} + +int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +{ + char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; uint8_t *pastcmp; + if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) + { + if ( rs->keystrokeshex != 0 ) + { + if ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + if ( waitflag == 0 ) + return(0); + else if ( 0 ) + { + while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + //fprintf(stderr,"pre-rebroadcast\n"); + sleep(10); + } + } + } + free(rs->keystrokeshex), rs->keystrokeshex = 0; + } + if ( 0 && (pastkeys= games_keystrokesload(&numpastkeys,seed,1)) != 0 ) + { + sprintf(params,"[\"extract\",\"17\",\"[%%22%s%%22]\"]",Gametxidstr); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (keys= jstr(resobj,(char *)"keystrokes")) != 0 ) + { + len = strlen(keys) / 2; + pastcmp = (uint8_t *)malloc(len + 1); + decode_hex(pastcmp,len,keys); + fprintf(stderr,"keystrokes.(%s) vs pastkeys\n",keys); + for (i=0; i> keystrokes.log",ASSETCHAINS_SYMBOL,Gametxidstr,hexstr); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd); + } + else + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("keystrokes.log","a"); + sprintf(params,"[\"keystrokes\",\"17\",\"[%%22%s%%22,%%22%s%%22]\"]",Gametxidstr,hexstr); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( fp != 0 ) + { + fprintf(fp,"%s\n",params); + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (rawtx= jstr(resobj,(char *)"hex")) != 0 ) + { + if ( rs->keystrokeshex != 0 ) + free(rs->keystrokeshex); + if ( (errstr= jstr(resobj,(char *)"error")) == 0 ) + { + rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); + strcpy(rs->keystrokeshex,rawtx); + retflag = 1; + } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); + //fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); + } + free_json(retjson); + } + free(retstr); + } + if ( 0 && waitflag != 0 && rs->keystrokeshex != 0 ) + { + while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + //fprintf(stderr,"post-rebroadcast\n"); + sleep(3); + } + free(rs->keystrokeshex), rs->keystrokeshex = 0; + } + } + } + return(retflag); +} + +int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) +{ + char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; + if ( rs->guiflag == 0 ) + return(-1); + if ( gametxidstr == 0 || *gametxidstr == 0 ) + return(retval); + if ( 0 ) + { + sprintf(fname,"%s.gameinfo",gametxidstr); + sprintf(cmd,"./komodo-cli -ac_name=%s cclib gameinfo 17 \\\"[%%22%s%%22]\\\" > %s",ASSETCHAINS_SYMBOL,gametxidstr,fname); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd); + else filestr = (char *)OS_fileptr(&allocsize,fname); + } + else + { + sprintf(params,"[\"gameinfo\",\"17\",\"[%%22%s%%22]\"]",gametxidstr); + filestr = komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT); + } + if ( filestr != 0 ) + { + if ( (retjson= cJSON_Parse(filestr)) != 0 && (resultjson= jobj(retjson,(char *)"result")) != 0 ) + { + //fprintf(stderr,"gameinfo.(%s)\n",jprint(resultjson,0)); + if ( (array= jarray(&n,resultjson,(char *)"players")) != 0 ) + { + for (i=0; iP,(int32_t)strlen(datastr)/2,datastr); + fprintf(stderr,"set pname[%s] %s\n",pname==0?"":pname,jprint(item,0)); + rs->restoring = 1; + } + } + } + } + } + free_json(retjson); + } + free(filestr); + } + return(retval); +} + +#ifdef _WIN32 +#ifdef _MSC_VER +__inline int msver(void) { + switch (_MSC_VER) { + case 1500: return 2008; + case 1600: return 2010; + case 1700: return 2012; + case 1800: return 2013; + case 1900: return 2015; + //case 1910: return 2017; + default: return (_MSC_VER / 100); + } +} + +static inline bool is_x64(void) { +#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) + return 1; +#elif defined(__amd64__) || defined(__amd64) || defined(_M_X64) || defined(_M_IA64) + return 1; +#else + return 0; +#endif +} + +#define BUILD_DATE __DATE__ " " __TIME__ +#endif // _WIN32 +#endif // _MSC_VER + +int main(int argc, char **argv) +{ + uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; +#ifdef _WIN32 +#ifdef _MSC_VER + printf("*** games for Windows [ Build %s ] ***\n", BUILD_DATE); + const char* arch = is_x64() ? "64-bits" : "32-bits"; + printf(" Built with VC++ %d (%ld) %s\n\n", msver(), _MSC_FULL_VER, arch); +#endif +#endif + + for (i=j=0; argv[0][i]!=0&&j payload) -{ - uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; - if ( (len= payload.size()) > 36 ) - { - len -= 36; - for (i=0; i<32; i++) - ((uint8_t *)&gametxid)[i] = payload[len+i]; - eventid = (uint32_t)payload[len+32]; - eventid |= (uint32_t)payload[len+33] << 8; - eventid |= (uint32_t)payload[len+34] << 16; - eventid |= (uint32_t)payload[len+35] << 24; - for (i=0; i payload); +int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk); +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); +void games_packitemstr(char *packitemstr,struct games_packitem *item); +int64_t games_cashout(struct games_player *P); + CScript games_newgameopret(int64_t buyin,int32_t maxplayers) { @@ -1343,99 +1335,6 @@ UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -int64_t games_cashout(struct games_player *P) -{ - int32_t dungeonlevel; int64_t mult=10,cashout = 0; - if ( P->amulet != 0 ) - mult *= 5; - dungeonlevel = P->dungeonlevel; - if ( P->amulet != 0 && dungeonlevel < 26 ) - dungeonlevel = 26; - cashout = (uint64_t)P->gold * P->gold * mult * dungeonlevel; - return(cashout); -} - -int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) -{ - static uint32_t good,bad; static uint256 prevgame; - char str[512],*keystrokes,gamesaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; - *cashoutp = 0; - gamespk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,gamesaddr,gamespk,pk); - if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) - { - free(keystrokes); - sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); - remove(fname); - - for (i=0; i no playerdata, good.%d bad.%d\n",good,bad); - } - *cashoutp = 0; - return(0); - } - } - if ( gametxid != prevgame ) - { - prevgame = gametxid; - bad++; - disp_gamesplayerdata(newdata); - disp_gamesplayerdata(playerdata); - fprintf(stderr,"%s playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d\n",gametxid.GetHex().c_str(),P.gold,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel); - fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,gamesaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); - } - } - sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); - remove(fname); - //fprintf(stderr,"no keys games_extractgame %s\n",gametxid.GetHex().c_str()); - return(-1); -} - UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { //vin0 -> highlander vout from creategame TCBOO @@ -1657,1928 +1556,11 @@ UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) -{ - return(true); -} +#include "tetris.cpp" -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) -{ - return(-1); -} +#else // STANDALONE -void games_packitemstr(char *packitemstr,struct games_packitem *item) -{ - sprintf(packitemstr,"not yet"); -} -#else - - -#include -#include -#include -#include -#include -#include -#include - -char USERPASS[8192]; uint16_t GAMES_PORT; -char Gametxidstr[67]; -char *clonestr(char *str); - -#define MAXSTR 1024 -char whoami[MAXSTR]; - -#define SMALLVAL 0.000000000000001 -#define SATOSHIDEN ((uint64_t)100000000L) -#define dstr(x) ((double)(x) / SATOSHIDEN) -#define KOMODO_ASSETCHAIN_MAXLEN 65 -char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],IPADDRESS[100]; - -#ifndef _BITS256 -#define _BITS256 -union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; -typedef union _bits256 bits256; -#endif - -#ifdef _WIN32 -#ifdef _MSC_VER -int gettimeofday(struct timeval * tp, struct timezone * tzp) -{ - // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's - static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); - - SYSTEMTIME system_time; - FILETIME file_time; - uint64_t time; - - GetSystemTime(&system_time); - SystemTimeToFileTime(&system_time, &file_time); - time = ((uint64_t)file_time.dwLowDateTime); - time += ((uint64_t)file_time.dwHighDateTime) << 32; - - tp->tv_sec = (long)((time - EPOCH) / 10000000L); - tp->tv_usec = (long)(system_time.wMilliseconds * 1000); - return 0; -} -#endif // _MSC_VER -#endif - - - -double OS_milliseconds() -{ - struct timeval tv; double millis; -#ifdef __MINGW32__ - mingw_gettimeofday(&tv,NULL); -#else - gettimeofday(&tv,NULL); -#endif - millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.); - //printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis); - return(millis); -} - -int32_t _unhex(char c) -{ - if ( c >= '0' && c <= '9' ) - return(c - '0'); - else if ( c >= 'a' && c <= 'f' ) - return(c - 'a' + 10); - else if ( c >= 'A' && c <= 'F' ) - return(c - 'A' + 10); - return(-1); -} - -int32_t is_hexstr(char *str,int32_t n) -{ - int32_t i; - if ( str == 0 || str[0] == 0 ) - return(0); - for (i=0; str[i]!=0; i++) - { - if ( n > 0 && i >= n ) - break; - if ( _unhex(str[i]) < 0 ) - break; - } - if ( n == 0 ) - return(i); - return(i == n); -} - -int32_t unhex(char c) -{ - int32_t hex; - if ( (hex= _unhex(c)) < 0 ) - { - //printf("unhex: illegal hexchar.(%c)\n",c); - } - return(hex); -} - -unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } - -int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex) -{ - int32_t adjust,i = 0; - //printf("decode.(%s)\n",hex); - if ( is_hexstr(hex,n) <= 0 ) - { - memset(bytes,0,n); - return(n); - } - if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) - hex[--n] = 0; - if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) - { - if ( n > 0 ) - { - bytes[0] = unhex(hex[0]); - printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); - } - bytes++; - hex++; - adjust = 1; - } else adjust = 0; - if ( n > 0 ) - { - for (i=0; i>4) & 0xf); - hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); - //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); - } - hexbytes[len*2] = 0; - //printf("len.%ld\n",len*2+1); - return((int32_t)len*2+1); -} - -char *bits256_str(char hexstr[65],bits256 x) -{ - init_hexbytes_noT(hexstr,x.bytes,sizeof(x)); - return(hexstr); -} - -long _stripwhite(char *buf,int accept) -{ - int32_t i,j,c; - if ( buf == 0 || buf[0] == 0 ) - return(0); - for (i=j=0; buf[i]!=0; i++) - { - buf[j] = c = buf[i]; - if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) - j++; - } - buf[j] = 0; - return(j); -} - -char *parse_conf_line(char *line,char *field) -{ - line += strlen(field); - for (; *line!='='&&*line!=0; line++) - break; - if ( *line == 0 ) - return(0); - if ( *line == '=' ) - line++; - while ( line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n' || line[strlen(line)-1] == ' ' ) - line[strlen(line)-1] = 0; - //printf("LINE.(%s)\n",line); - _stripwhite(line,0); - return(clonestr(line)); -} - -int32_t safecopy(char *dest,char *src,long len) -{ - int32_t i = -1; - if ( src != 0 && dest != 0 && src != dest ) - { - if ( dest != 0 ) - memset(dest,0,len); - for (i=0; i buflen ) - { - *allocsizep = filesize; - *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); - } - rewind(fp); - if ( buf == 0 ) - printf("Null buf ???\n"); - else - { - if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) - printf("error reading filesize.%ld\n",(long)filesize); - buf[filesize] = 0; - } - fclose(fp); - *lenp = filesize; - //printf("loaded.(%s)\n",buf); - } //else printf("OS_loadfile couldnt load.(%s)\n",fname); - return(buf); -} - -uint8_t *OS_fileptr(long *allocsizep,char *fname) -{ - long filesize = 0; uint8_t *buf = 0; void *retptr; - *allocsizep = 0; - retptr = OS_loadfile(fname,&buf,&filesize,allocsizep); - return((uint8_t *)retptr); -} - -struct MemoryStruct { char *memory; size_t size; }; -struct return_string { char *ptr; size_t len; }; - -// return data from the server -#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) -#define CURL_GLOBAL_SSL (1<<0) -#define CURL_GLOBAL_WIN32 (1<<1) - - -/************************************************************************ - * - * Initialize the string handler so that it is thread safe - * - ************************************************************************/ - -void init_string(struct return_string *s) -{ - s->len = 0; - s->ptr = (char *)calloc(1,s->len+1); - if ( s->ptr == NULL ) - { - fprintf(stderr,"init_string malloc() failed\n"); - exit(-1); - } - s->ptr[0] = '\0'; -} - -/************************************************************************ - * - * Use the "writer" to accumulate text until done - * - ************************************************************************/ - -size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string *s) -{ - size_t new_len = s->len + size*nmemb; - s->ptr = (char *)realloc(s->ptr,new_len+1); - if ( s->ptr == NULL ) - { - fprintf(stderr, "accumulate realloc() failed\n"); - exit(-1); - } - memcpy(s->ptr+s->len,ptr,size*nmemb); - s->ptr[new_len] = '\0'; - s->len = new_len; - return(size * nmemb); -} - -/************************************************************************ - * - * return the current system time in milliseconds - * - ************************************************************************/ - -#define EXTRACT_BITCOIND_RESULT // if defined, ensures error is null and returns the "result" field -#ifdef EXTRACT_BITCOIND_RESULT - -/************************************************************************ - * - * perform post processing of the results - * - ************************************************************************/ - -char *post_process_bitcoind_RPC(char *debugstr,char *command,char *rpcstr,char *params) -{ - long i,j,len; char *retstr = 0; cJSON *json,*result,*error; - //printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); - if ( command == 0 || rpcstr == 0 || rpcstr[0] == 0 ) - { - if ( strcmp(command,"signrawtransaction") != 0 ) - printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); - return(rpcstr); - } - json = cJSON_Parse(rpcstr); - if ( json == 0 ) - { - printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s can't parse.(%s) params.(%s)\n",debugstr,command,rpcstr,params); - free(rpcstr); - return(0); - } - result = cJSON_GetObjectItem(json,"result"); - error = cJSON_GetObjectItem(json,"error"); - if ( error != 0 && result != 0 ) - { - if ( (error->type&0xff) == cJSON_NULL && (result->type&0xff) != cJSON_NULL ) - { - retstr = cJSON_Print(result); - len = strlen(retstr); - if ( retstr[0] == '"' && retstr[len-1] == '"' ) - { - for (i=1,j=0; itype&0xff) != cJSON_NULL || (result->type&0xff) != cJSON_NULL ) - { - if ( strcmp(command,"signrawtransaction") != 0 ) - printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC (%s) error.%s\n",debugstr,command,rpcstr); - } - free(rpcstr); - } else retstr = rpcstr; - free_json(json); - //fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC: postprocess returns.(%s)\n",retstr); - return(retstr); -} -#endif - -#ifdef _WIN32 -#ifdef _MSC_VER -#define sleep(x) Sleep(1000*(x)) -#endif -#endif - -/************************************************************************ - * - * perform the query - * - ************************************************************************/ - -char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params) -{ - static int didinit,count,count2; static double elapsedsum,elapsedsum2; - struct curl_slist *headers = NULL; struct return_string s; CURLcode res; CURL *curl_handle; - char *bracket0,*bracket1,*databuf = 0; long len; int32_t specialcase,numretries; double starttime; - if ( didinit == 0 ) - { - didinit = 1; - curl_global_init(CURL_GLOBAL_ALL); //init the curl session - } - numretries = 0; - if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") == 0 ) - specialcase = 1; - else specialcase = 0; - if ( url[0] == 0 ) - strcpy(url,"http://127.0.0.1:7876/nxt"); - if ( specialcase != 0 && 0 ) - printf("<<<<<<<<<<< bitcoind_RPC: debug.(%s) url.(%s) command.(%s) params.(%s)\n",debugstr,url,command,params); -try_again: - if ( retstrp != 0 ) - *retstrp = 0; - starttime = OS_milliseconds(); - curl_handle = curl_easy_init(); - init_string(&s); - headers = curl_slist_append(0,"Expect:"); - - curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); - curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(curl_handle,CURLOPT_URL, url); - curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function - curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback - curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash - curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback - if ( strncmp(url,"https",5) == 0 ) - { - curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0); - curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0); - } - if ( userpass != 0 ) - curl_easy_setopt(curl_handle,CURLOPT_USERPWD, userpass); - databuf = 0; - if ( params != 0 ) - { - if ( command != 0 && specialcase == 0 ) - { - len = strlen(params); - if ( len > 0 && params[0] == '[' && params[len-1] == ']' ) { - bracket0 = bracket1 = (char *)""; - } - else - { - bracket0 = (char *)"["; - bracket1 = (char *)"]"; - } - - databuf = (char *)malloc(256 + strlen(command) + strlen(params)); - sprintf(databuf,"{\"id\":\"jl777\",\"method\":\"%s\",\"params\":%s%s%s}",command,bracket0,params,bracket1); - //printf("url.(%s) userpass.(%s) databuf.(%s)\n",url,userpass,databuf); - // - } //else if ( specialcase != 0 ) fprintf(stderr,"databuf.(%s)\n",params); - curl_easy_setopt(curl_handle,CURLOPT_POST,1L); - if ( databuf != 0 ) - curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,databuf); - else curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,params); - } - //laststart = milliseconds(); - res = curl_easy_perform(curl_handle); - curl_slist_free_all(headers); - curl_easy_cleanup(curl_handle); - if ( databuf != 0 ) // clean up temporary buffer - { - free(databuf); - databuf = 0; - } - if ( res != CURLE_OK ) - { - numretries++; - if ( specialcase != 0 ) - { - printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); - free(s.ptr); - return(0); - } - else if ( numretries >= 1 ) - { - //printf("Maximum number of retries exceeded!\n"); - free(s.ptr); - return(0); - } - if ( (rand() % 1000) == 0 ) - printf( "curl_easy_perform() failed: %s %s.(%s %s), retries: %d\n",curl_easy_strerror(res),debugstr,url,command,numretries); - free(s.ptr); - sleep((1< (%s)\n",params,s.ptr); - count2++; - elapsedsum2 += (OS_milliseconds() - starttime); - if ( (count2 % 10000) == 0) - printf("%d: ave %9.6f | elapsed %.3f millis | NXT calls.(%s) cmd.(%s)\n",count2,elapsedsum2/count2,(double)(OS_milliseconds() - starttime),url,command); - return(s.ptr); - } - } - printf("bitcoind_RPC: impossible case\n"); - free(s.ptr); - return(0); -} - -static size_t WriteMemoryCallback(void *ptr,size_t size,size_t nmemb,void *data) -{ - size_t realsize = (size * nmemb); - struct MemoryStruct *mem = (struct MemoryStruct *)data; - mem->memory = (char *)((ptr != 0) ? realloc(mem->memory,mem->size + realsize + 1) : malloc(mem->size + realsize + 1)); - if ( mem->memory != 0 ) - { - if ( ptr != 0 ) - memcpy(&(mem->memory[mem->size]),ptr,realsize); - mem->size += realsize; - mem->memory[mem->size] = 0; - } - //printf("got %d bytes\n",(int32_t)(size*nmemb)); - return(realsize); -} - -char *curl_post(CURL **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3) -{ - struct MemoryStruct chunk; CURL *cHandle; long code; struct curl_slist *headers = 0; - if ( (cHandle= *cHandlep) == NULL ) - *cHandlep = cHandle = curl_easy_init(); - else curl_easy_reset(cHandle); - //#ifdef DEBUG - //curl_easy_setopt(cHandle,CURLOPT_VERBOSE, 1); - //#endif - curl_easy_setopt(cHandle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); - curl_easy_setopt(cHandle,CURLOPT_SSL_VERIFYPEER,0); - //curl_easy_setopt(cHandle,CURLOPT_SSLVERSION,1); - curl_easy_setopt(cHandle,CURLOPT_URL,url); - curl_easy_setopt(cHandle,CURLOPT_CONNECTTIMEOUT,10); - if ( userpass != 0 && userpass[0] != 0 ) - curl_easy_setopt(cHandle,CURLOPT_USERPWD,userpass); - if ( postfields != 0 && postfields[0] != 0 ) - { - curl_easy_setopt(cHandle,CURLOPT_POST,1); - curl_easy_setopt(cHandle,CURLOPT_POSTFIELDS,postfields); - } - if ( hdr0 != NULL && hdr0[0] != 0 ) - { - //printf("HDR0.(%s) HDR1.(%s) HDR2.(%s) HDR3.(%s)\n",hdr0!=0?hdr0:"",hdr1!=0?hdr1:"",hdr2!=0?hdr2:"",hdr3!=0?hdr3:""); - headers = curl_slist_append(headers,hdr0); - if ( hdr1 != 0 && hdr1[0] != 0 ) - headers = curl_slist_append(headers,hdr1); - if ( hdr2 != 0 && hdr2[0] != 0 ) - headers = curl_slist_append(headers,hdr2); - if ( hdr3 != 0 && hdr3[0] != 0 ) - headers = curl_slist_append(headers,hdr3); - } //headers = curl_slist_append(0,"Expect:"); - if ( headers != 0 ) - curl_easy_setopt(cHandle,CURLOPT_HTTPHEADER,headers); - //res = curl_easy_perform(cHandle); - memset(&chunk,0,sizeof(chunk)); - curl_easy_setopt(cHandle,CURLOPT_WRITEFUNCTION,WriteMemoryCallback); - curl_easy_setopt(cHandle,CURLOPT_WRITEDATA,(void *)&chunk); - curl_easy_perform(cHandle); - curl_easy_getinfo(cHandle,CURLINFO_RESPONSE_CODE,&code); - if ( headers != 0 ) - curl_slist_free_all(headers); - if ( code != 200 ) - printf("(%s) server responded with code %ld (%s)\n",url,code,chunk.memory); - return(chunk.memory); -} - -uint16_t _komodo_userpass(char *username, char *password, FILE *fp) -{ - char *rpcuser,*rpcpassword,*str,*ipaddress,line[8192]; uint16_t port = 0; - rpcuser = rpcpassword = 0; - username[0] = password[0] = 0; - while ( fgets(line,sizeof(line),fp) != 0 ) - { - if ( line[0] == '#' ) - continue; - //printf("line.(%s) %p %p\n",line,strstr(line,(char *)"rpcuser"),strstr(line,(char *)"rpcpassword")); - if ( (str= strstr(line,(char *)"rpcuser")) != 0 ) - rpcuser = parse_conf_line(str,(char *)"rpcuser"); - else if ( (str= strstr(line,(char *)"rpcpassword")) != 0 ) - rpcpassword = parse_conf_line(str,(char *)"rpcpassword"); - else if ( (str= strstr(line,(char *)"rpcport")) != 0 ) - { - port = atoi(parse_conf_line(str,(char *)"rpcport")); - //fprintf(stderr,"rpcport.%u in file\n",port); - } - else if ( (str= strstr(line,(char *)"ipaddress")) != 0 ) - { - ipaddress = parse_conf_line(str,(char *)"ipaddress"); - strcpy(IPADDRESS,ipaddress); - } - } - if ( rpcuser != 0 && rpcpassword != 0 ) - { - strcpy(username,rpcuser); - strcpy(password,rpcpassword); - } - //printf("rpcuser.(%s) rpcpassword.(%s) %u ipaddress.%s\n",rpcuser,rpcpassword,port,ipaddress); - if ( rpcuser != 0 ) - free(rpcuser); - if ( rpcpassword != 0 ) - free(rpcpassword); - return(port); -} - -uint16_t komodo_userpass(char *userpass,char *symbol) -{ - FILE *fp; uint16_t port = 0; char fname[512],username[512],password[512],confname[KOMODO_ASSETCHAIN_MAXLEN]; - userpass[0] = 0; - if ( strcmp("KMD",symbol) == 0 ) - { -#ifdef __APPLE__ - sprintf(confname,"Komodo.conf"); -#else - sprintf(confname,"komodo.conf"); -#endif - } - else sprintf(confname,"%s.conf",symbol); - //komodo_statefname(fname,symbol,confname); - if ( (fp= fopen(confname,"rb")) != 0 ) - { - port = _komodo_userpass(username,password,fp); - sprintf(userpass,"%s:%s",username,password); - if ( strcmp(symbol,ASSETCHAINS_SYMBOL) == 0 ) - strcpy(USERPASS,userpass); - fclose(fp); - } - return(port); -} - -#define is_cJSON_True(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_True) - -char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) -{ - //static void *cHandle; - char url[512],*retstr=0,*retstr2=0,postdata[8192]; - if ( params == 0 || params[0] == 0 ) - params = (char *)"[]"; - if ( strlen(params) < sizeof(postdata)-128 ) - { - sprintf(url,(char *)"http://%s:%u",IPADDRESS,port); - sprintf(postdata,"{\"method\":\"%s\",\"params\":%s}",method,params); - //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,USERPASS); - retstr2 = bitcoind_RPC(&retstr,(char *)"debug",url,userpass,method,params); - //retstr = curl_post(&cHandle,url,USERPASS,postdata,0,0,0,0); - } - return(retstr2); -} - -int32_t issue_games_events(bits256 gametxid,uint32_t eventid,int32_t c) -{ - static FILE *fp; - char params[512],*retstr,str[65]; cJSON *retjson,*resobj; int32_t retval = -1; - if ( fp == 0 ) - fp = fopen("events.log","wb"); - sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c,bits256_str(str,gametxid),eventid); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) - { - retval = 0; - if ( fp != 0 ) - { - fprintf(fp,"%s\n",jprint(resobj,0)); - fflush(fp); - } - } - free_json(retjson); - } else fprintf(fp,"error parsing %s\n",retstr); - free(retstr); - } else fprintf(fp,"error issuing method %s\n",params); - return(retval); -} - -int32_t games_sendrawtransaction(char *rawtx) -{ - char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; - params = (char *)malloc(strlen(rawtx) + 16); - sprintf(params,"[\"%s\"]",rawtx); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"sendrawtransaction",params,GAMES_PORT)) != 0 ) - { - if ( 0 ) // causes 4th level crash - { - static FILE *fp; - if ( fp == 0 ) - fp = fopen("games.sendlog","wb"); - if ( fp != 0 ) - { - fprintf(fp,"%s\n",retstr); - fflush(fp); - } - } - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) - { - if ( (hexstr= jstr(resobj,0)) != 0 && is_hexstr(hexstr,64) == 64 ) - retval = 0; - } - free_json(retjson); - } - - /* log sendrawtx result in file */ - - /* - FILE *debug_file; - debug_file = fopen("tx_debug.log", "a"); - fprintf(debug_file, "%s\n", retstr); - fflush(debug_file); - fclose(debug_file); - */ - - free(retstr); - } - free(params); - return(retval); -} - -int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) -{ - char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; uint8_t *pastcmp; - if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) - { - if ( rs->keystrokeshex != 0 ) - { - if ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) - { - if ( waitflag == 0 ) - return(0); - else if ( 0 ) - { - while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) - { - //fprintf(stderr,"pre-rebroadcast\n"); - sleep(10); - } - } - } - free(rs->keystrokeshex), rs->keystrokeshex = 0; - } - if ( 0 && (pastkeys= games_keystrokesload(&numpastkeys,seed,1)) != 0 ) - { - sprintf(params,"[\"extract\",\"17\",\"[%%22%s%%22]\"]",Gametxidstr); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (keys= jstr(resobj,(char *)"keystrokes")) != 0 ) - { - len = strlen(keys) / 2; - pastcmp = (uint8_t *)malloc(len + 1); - decode_hex(pastcmp,len,keys); - fprintf(stderr,"keystrokes.(%s) vs pastkeys\n",keys); - for (i=0; i> keystrokes.log",ASSETCHAINS_SYMBOL,Gametxidstr,hexstr); - if ( system(cmd) != 0 ) - fprintf(stderr,"error issuing (%s)\n",cmd); - } - else - { - static FILE *fp; - if ( fp == 0 ) - fp = fopen("keystrokes.log","a"); - sprintf(params,"[\"keystrokes\",\"17\",\"[%%22%s%%22,%%22%s%%22]\"]",Gametxidstr,hexstr); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( fp != 0 ) - { - fprintf(fp,"%s\n",params); - fprintf(fp,"%s\n",retstr); - fflush(fp); - } - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (rawtx= jstr(resobj,(char *)"hex")) != 0 ) - { - if ( rs->keystrokeshex != 0 ) - free(rs->keystrokeshex); - if ( (errstr= jstr(resobj,(char *)"error")) == 0 ) - { - rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); - strcpy(rs->keystrokeshex,rawtx); - retflag = 1; - } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); - //fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); - } - free_json(retjson); - } - free(retstr); - } - if ( 0 && waitflag != 0 && rs->keystrokeshex != 0 ) - { - while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) - { - //fprintf(stderr,"post-rebroadcast\n"); - sleep(3); - } - free(rs->keystrokeshex), rs->keystrokeshex = 0; - } - } - } - return(retflag); -} - -int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) -{ - char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; - if ( rs->guiflag == 0 ) - return(-1); - if ( gametxidstr == 0 || *gametxidstr == 0 ) - return(retval); - if ( 0 ) - { - sprintf(fname,"%s.gameinfo",gametxidstr); - sprintf(cmd,"./komodo-cli -ac_name=%s cclib gameinfo 17 \\\"[%%22%s%%22]\\\" > %s",ASSETCHAINS_SYMBOL,gametxidstr,fname); - if ( system(cmd) != 0 ) - fprintf(stderr,"error issuing (%s)\n",cmd); - else filestr = (char *)OS_fileptr(&allocsize,fname); - } - else - { - sprintf(params,"[\"gameinfo\",\"17\",\"[%%22%s%%22]\"]",gametxidstr); - filestr = komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT); - } - if ( filestr != 0 ) - { - if ( (retjson= cJSON_Parse(filestr)) != 0 && (resultjson= jobj(retjson,(char *)"result")) != 0 ) - { - //fprintf(stderr,"gameinfo.(%s)\n",jprint(resultjson,0)); - if ( (array= jarray(&n,resultjson,(char *)"players")) != 0 ) - { - for (i=0; iP,(int32_t)strlen(datastr)/2,datastr); - fprintf(stderr,"set pname[%s] %s\n",pname==0?"":pname,jprint(item,0)); - rs->restoring = 1; - } - } - } - } - } - free_json(retjson); - } - free(filestr); - } - return(retval); -} - -#ifdef _WIN32 -#ifdef _MSC_VER -__inline int msver(void) { - switch (_MSC_VER) { - case 1500: return 2008; - case 1600: return 2010; - case 1700: return 2012; - case 1800: return 2013; - case 1900: return 2015; - //case 1910: return 2017; - default: return (_MSC_VER / 100); - } -} - -static inline bool is_x64(void) { -#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) - return 1; -#elif defined(__amd64__) || defined(__amd64) || defined(_M_X64) || defined(_M_IA64) - return 1; -#else - return 0; -#endif -} - -#define BUILD_DATE __DATE__ " " __TIME__ -#endif // _WIN32 -#endif // _MSC_VER - -int main(int argc, char **argv) -{ - uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; -#ifdef _WIN32 -#ifdef _MSC_VER - printf("*** games for Windows [ Build %s ] ***\n", BUILD_DATE); - const char* arch = is_x64() ? "64-bits" : "32-bits"; - printf(" Built with VC++ %d (%ld) %s\n\n", msver(), _MSC_FULL_VER, arch); -#endif -#endif - - for (i=j=0; argv[0][i]!=0&&j // for FILE -#include // for bool -#include -#include -#include -#include -#include -#include - -#ifdef BUILD_GAMESCC -#include "rogue/cursesd.h" -#else -#include -#endif - -/* - Convert a tetromino type to its corresponding cell. - */ -#define TYPE_TO_CELL(x) ((x)+1) - -/* - Strings for how you would print a tetris board. - */ -#define TC_EMPTY_STR " " -#define TC_BLOCK_STR "\u2588" - -/* - Questions about a tetris cell. - */ -#define TC_IS_EMPTY(x) ((x) == TC_EMPTY) -#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x)) - -/* - How many cells in a tetromino? - */ -#define TETRIS 4 -/* - How many tetrominos? - */ -#define NUM_TETROMINOS 7 -/* - How many orientations of a tetromino? - */ -#define NUM_ORIENTATIONS 4 - -/* - Level constants. - */ -#define MAX_LEVEL 19 -#define LINES_PER_LEVEL 10 - -/* - A "cell" is a 1x1 block within a tetris board. - */ -typedef enum { - TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ -} tetris_cell; - -/* - A "type" is a type/shape of a tetromino. Not including orientation. - */ -typedef enum { - TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z -} tetris_type; - -/* - A row,column pair. Negative numbers allowed, because we need them for - offsets. - */ -typedef struct { - int row; - int col; -} tetris_location; - -/* - A "block" is a struct that contains information about a tetromino. - Specifically, what type it is, what orientation it has, and where it is. - */ -typedef struct { - int typ; - int ori; - tetris_location loc; -} tetris_block; - -/* - All possible moves to give as input to the game. - */ -typedef enum { - TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE -} tetris_move; - -/* - A game object! - */ -typedef struct { - /* - Game board stuff: - */ - int rows; - int cols; - char *board; - /* - Scoring information: - */ - int points; - int level; - /* - Falling block is the one currently going down. Next block is the one that - will be falling after this one. Stored is the block that you can swap out. - */ - tetris_block falling; - tetris_block next; - tetris_block stored; - /* - Number of game ticks until the block will move down. - */ - int ticks_till_gravity; - /* - Number of lines until you advance to the next level. - */ - int lines_remaining; -} tetris_game; - -/* - This array stores all necessary information about the cells that are filled by - each tetromino. The first index is the type of the tetromino (i.e. shape, - e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final - array contains 4 tetris_location objects, each mapping to an offset from a - point on the upper left that is the tetromino "origin". - */ -extern tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS]; - -/* - This array tells you how many ticks per gravity by level. Decreases as level - increases, to add difficulty. - */ -extern int GRAVITY_LEVEL[MAX_LEVEL+1]; - -// Data structure manipulation. -void tg_init(tetris_game *obj, int rows, int cols); -tetris_game *tg_create(int rows, int cols); -void tg_destroy(tetris_game *obj); -void tg_delete(tetris_game *obj); -tetris_game *tg_load(FILE *f); -void tg_save(tetris_game *obj, FILE *f); - -// Public methods not related to memory: -char tg_get(tetris_game *obj, int row, int col); -bool tg_check(tetris_game *obj, int row, int col); -bool tg_tick(tetris_game *obj, tetris_move move); -void tg_print(tetris_game *obj, FILE *f); - -#endif // TETRIS_H - - -#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) -#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) - -/******************************************************************************* - Array Definitions - *******************************************************************************/ - -tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] = { - // I - {{{1, 0}, {1, 1}, {1, 2}, {1, 3}}, - {{0, 2}, {1, 2}, {2, 2}, {3, 2}}, - {{3, 0}, {3, 1}, {3, 2}, {3, 3}}, - {{0, 1}, {1, 1}, {2, 1}, {3, 1}}}, - // J - {{{0, 0}, {1, 0}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {2, 1}}, - {{1, 0}, {1, 1}, {1, 2}, {2, 2}}, - {{0, 1}, {1, 1}, {2, 0}, {2, 1}}}, - // L - {{{0, 2}, {1, 0}, {1, 1}, {1, 2}}, - {{0, 1}, {1, 1}, {2, 1}, {2, 2}}, - {{1, 0}, {1, 1}, {1, 2}, {2, 0}}, - {{0, 0}, {0, 1}, {1, 1}, {2, 1}}}, - // O - {{{0, 1}, {0, 2}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {1, 2}}}, - // S - {{{0, 1}, {0, 2}, {1, 0}, {1, 1}}, - {{0, 1}, {1, 1}, {1, 2}, {2, 2}}, - {{1, 1}, {1, 2}, {2, 0}, {2, 1}}, - {{0, 0}, {1, 0}, {1, 1}, {2, 1}}}, - // T - {{{0, 1}, {1, 0}, {1, 1}, {1, 2}}, - {{0, 1}, {1, 1}, {1, 2}, {2, 1}}, - {{1, 0}, {1, 1}, {1, 2}, {2, 1}}, - {{0, 1}, {1, 0}, {1, 1}, {2, 1}}}, - // Z - {{{0, 0}, {0, 1}, {1, 1}, {1, 2}}, - {{0, 2}, {1, 1}, {1, 2}, {2, 1}}, - {{1, 0}, {1, 1}, {2, 1}, {2, 2}}, - {{0, 1}, {1, 0}, {1, 1}, {2, 0}}}, -}; - -int GRAVITY_LEVEL[MAX_LEVEL+1] = { - // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, - //10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 30, 28, 26, 24, 22, 20, 16, 12, 8, 4 -}; - -/******************************************************************************* - Helper Functions for Blocks - *******************************************************************************/ - -void sleep_milli(int milliseconds) -{ - struct timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = milliseconds * 1000 * 1000; - nanosleep(&ts, NULL); -} - -/* - Return the block at the given row and column. - */ -char tg_get(tetris_game *obj, int row, int column) -{ - return obj->board[obj->cols * row + column]; -} - -/* - Set the block at the given row and column. - */ -static void tg_set(tetris_game *obj, int row, int column, char value) -{ - obj->board[obj->cols * row + column] = value; -} - -/* - Check whether a row and column are in bounds. - */ -bool tg_check(tetris_game *obj, int row, int col) -{ - return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols; -} - -/* - Place a block onto the board. - */ -static void tg_put(tetris_game *obj, tetris_block block) -{ - int i; - for (i = 0; i < TETRIS; i++) { - tetris_location cell = TETROMINOS[block.typ][block.ori][i]; - tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, - TYPE_TO_CELL(block.typ)); - } -} - -/* - Clear a block out of the board. - */ -static void tg_remove(tetris_game *obj, tetris_block block) -{ - int i; - for (i = 0; i < TETRIS; i++) { - tetris_location cell = TETROMINOS[block.typ][block.ori][i]; - tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY); - } -} - -/* - Check if a block can be placed on the board. - */ -static bool tg_fits(tetris_game *obj, tetris_block block) -{ - int i, r, c; - for (i = 0; i < TETRIS; i++) { - tetris_location cell = TETROMINOS[block.typ][block.ori][i]; - r = block.loc.row + cell.row; - c = block.loc.col + cell.col; - if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) { - return false; - } - } - return true; -} - -/* - Return a random tetromino type. - */ -static int random_tetromino(void) -{ - return rand() % NUM_TETROMINOS; -} - -/* - Create a new falling block and populate the next falling block with a random - one. - */ -static void tg_new_falling(tetris_game *obj) -{ - // Put in a new falling tetromino. - obj->falling = obj->next; - obj->next.typ = random_tetromino(); - obj->next.ori = 0; - obj->next.loc.row = 0; - obj->next.loc.col = obj->cols/2 - 2; -} - -/******************************************************************************* - Game Turn Helpers - *******************************************************************************/ - -/* - Tick gravity, and move the block down if gravity should act. - */ -static void tg_do_gravity_tick(tetris_game *obj) -{ - obj->ticks_till_gravity--; - if (obj->ticks_till_gravity <= 0) { - tg_remove(obj, obj->falling); - obj->falling.loc.row++; - if (tg_fits(obj, obj->falling)) { - obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; - } else { - obj->falling.loc.row--; - tg_put(obj, obj->falling); - - tg_new_falling(obj); - } - tg_put(obj, obj->falling); - } -} - -/* - Move the falling tetris block left (-1) or right (+1). - */ -static void tg_move(tetris_game *obj, int direction) -{ - tg_remove(obj, obj->falling); - obj->falling.loc.col += direction; - if (!tg_fits(obj, obj->falling)) { - obj->falling.loc.col -= direction; - } - tg_put(obj, obj->falling); -} - -/* - Send the falling tetris block to the bottom. - */ -static void tg_down(tetris_game *obj) -{ - tg_remove(obj, obj->falling); - while (tg_fits(obj, obj->falling)) { - obj->falling.loc.row++; - } - obj->falling.loc.row--; - tg_put(obj, obj->falling); - tg_new_falling(obj); -} - -/* - Rotate the falling block in either direction (+/-1). - */ -static void tg_rotate(tetris_game *obj, int direction) -{ - tg_remove(obj, obj->falling); - - while (true) { - obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS; - - // If the new orientation fits, we're done. - if (tg_fits(obj, obj->falling)) - break; - - // Otherwise, try moving left to make it fit. - obj->falling.loc.col--; - if (tg_fits(obj, obj->falling)) - break; - - // Finally, try moving right to make it fit. - obj->falling.loc.col += 2; - if (tg_fits(obj, obj->falling)) - break; - - // Put it back in its original location and try the next orientation. - obj->falling.loc.col--; - // Worst case, we come back to the original orientation and it fits, so this - // loop will terminate. - } - - tg_put(obj, obj->falling); -} - -/* - Swap the falling block with the block in the hold buffer. - */ -static void tg_hold(tetris_game *obj) -{ - tg_remove(obj, obj->falling); - if (obj->stored.typ == -1) { - obj->stored = obj->falling; - tg_new_falling(obj); - } else { - int typ = obj->falling.typ, ori = obj->falling.ori; - obj->falling.typ = obj->stored.typ; - obj->falling.ori = obj->stored.ori; - obj->stored.typ = typ; - obj->stored.ori = ori; - while (!tg_fits(obj, obj->falling)) { - obj->falling.loc.row--; - } - } - tg_put(obj, obj->falling); -} - -/* - Perform the action specified by the move. - */ -static void tg_handle_move(tetris_game *obj, tetris_move move) -{ - switch (move) { - case TM_LEFT: - tg_move(obj, -1); - break; - case TM_RIGHT: - tg_move(obj, 1); - break; - case TM_DROP: - tg_down(obj); - break; - case TM_CLOCK: - tg_rotate(obj, 1); - break; - case TM_COUNTER: - tg_rotate(obj, -1); - break; - case TM_HOLD: - tg_hold(obj); - break; - default: - // pass - break; - } -} - -/* - Return true if line i is full. - */ -static bool tg_line_full(tetris_game *obj, int i) -{ - int j; - for (j = 0; j < obj->cols; j++) { - if (TC_IS_EMPTY(tg_get(obj, i, j))) - return false; - } - return true; -} - -/* - Shift every row above r down one. - */ -static void tg_shift_lines(tetris_game *obj, int r) -{ - int i, j; - for (i = r-1; i >= 0; i--) { - for (j = 0; j < obj->cols; j++) { - tg_set(obj, i+1, j, tg_get(obj, i, j)); - tg_set(obj, i, j, TC_EMPTY); - } - } -} - -/* - Find rows that are filled, remove them, shift, and return the number of - cleared rows. - */ -static int tg_check_lines(tetris_game *obj) -{ - int i, nlines = 0; - tg_remove(obj, obj->falling); // don't want to mess up falling block - - for (i = obj->rows-1; i >= 0; i--) { - if (tg_line_full(obj, i)) { - tg_shift_lines(obj, i); - i++; // do this line over again since they're shifted - nlines++; - } - } - - tg_put(obj, obj->falling); // replace - return nlines; -} - -/* - Adjust the score for the game, given how many lines were just cleared. - */ -static void tg_adjust_score(tetris_game *obj, int lines_cleared) -{ - static int line_multiplier[] = {0, 40, 100, 300, 1200}; - obj->points += line_multiplier[lines_cleared] * (obj->level + 1); - if (lines_cleared >= obj->lines_remaining) { - obj->level = MIN(MAX_LEVEL, obj->level + 1); - lines_cleared -= obj->lines_remaining; - obj->lines_remaining = LINES_PER_LEVEL - lines_cleared; - } else { - obj->lines_remaining -= lines_cleared; - } -} - -/* - Return true if the game is over. - */ -static bool tg_game_over(tetris_game *obj) -{ - int i, j; - bool over = false; - tg_remove(obj, obj->falling); - for (i = 0; i < 2; i++) { - for (j = 0; j < obj->cols; j++) { - if (TC_IS_FILLED(tg_get(obj, i, j))) { - over = true; - } - } - } - tg_put(obj, obj->falling); - return over; -} - -/******************************************************************************* - Main Public Functions - *******************************************************************************/ - -/* - Do a single game tick: process gravity, user input, and score. Return true if - the game is still running, false if it is over. - */ -bool tg_tick(tetris_game *obj, tetris_move move) -{ - int lines_cleared; - // Handle gravity. - tg_do_gravity_tick(obj); - - // Handle input. - tg_handle_move(obj, move); - - // Check for cleared lines - lines_cleared = tg_check_lines(obj); - - tg_adjust_score(obj, lines_cleared); - - // Return whether the game will continue (NOT whether it's over) - return !tg_game_over(obj); -} - -void tg_init(tetris_game *obj, int rows, int cols) -{ - // Initialization logic - obj->rows = rows; - obj->cols = cols; - obj->board = (char *)malloc(rows * cols); - memset(obj->board, TC_EMPTY, rows * cols); - obj->points = 0; - obj->level = 0; - obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; - obj->lines_remaining = LINES_PER_LEVEL; - srand(time(NULL)); - tg_new_falling(obj); - tg_new_falling(obj); - obj->stored.typ = -1; - obj->stored.ori = 0; - obj->stored.loc.row = 0; - obj->next.loc.col = obj->cols/2 - 2; - printf("%d", obj->falling.loc.col); -} - -tetris_game *tg_create(int rows, int cols) -{ - tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); - tg_init(obj, rows, cols); - return obj; -} - -void tg_destroy(tetris_game *obj) -{ - // Cleanup logic - free(obj->board); -} - -void tg_delete(tetris_game *obj) { - tg_destroy(obj); - free(obj); -} - -/* - Load a game from a file. - */ -tetris_game *tg_load(FILE *f) -{ - tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); - if (fread(obj, sizeof(tetris_game), 1, f) != 1 ) - { - fprintf(stderr,"read game error\n"); - free(obj); - obj = 0; - } - else - { - obj->board = (char *)malloc(obj->rows * obj->cols); - if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) - { - fprintf(stderr,"fread error\n"); - free(obj->board); - free(obj); - obj = 0; - } - } - return obj; -} - -/* - Save a game to a file. - */ -void tg_save(tetris_game *obj, FILE *f) -{ - if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) - fprintf(stderr,"error writing tetrisgame\n"); - else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) - fprintf(stderr,"error writing board\n"); -} - -/* - Print a game board to a file. Really just for early debugging. - */ -void tg_print(tetris_game *obj, FILE *f) { - int i, j; - for (i = 0; i < obj->rows; i++) { - for (j = 0; j < obj->cols; j++) { - if (TC_IS_EMPTY(tg_get(obj, i, j))) { - fputs(TC_EMPTY_STR, f); - } else { - fputs(TC_BLOCK_STR, f); - } - } - fputc('\n', f); - } -} - -/* - 2 columns per cell makes the game much nicer. - */ -#define COLS_PER_CELL 2 -/* - Macro to print a cell of a specific type to a window. - */ -#define ADD_BLOCK(w,x) waddch((w),' '|A_REVERSE|COLOR_PAIR(x)); \ -waddch((w),' '|A_REVERSE|COLOR_PAIR(x)) -#define ADD_EMPTY(w) waddch((w), ' '); waddch((w), ' ') - -/* - Print the tetris board onto the ncurses window. - */ -void display_board(WINDOW *w, tetris_game *obj) -{ - int i, j; - box(w, 0, 0); - for (i = 0; i < obj->rows; i++) { - wmove(w, 1 + i, 1); - for (j = 0; j < obj->cols; j++) { - if (TC_IS_FILLED(tg_get(obj, i, j))) { - ADD_BLOCK(w,tg_get(obj, i, j)); - } else { - ADD_EMPTY(w); - } - } - } - wnoutrefresh(w); -} - -/* - Display a tetris piece in a dedicated window. - */ -void display_piece(WINDOW *w, tetris_block block) -{ - int b; - tetris_location c; - wclear(w); - box(w, 0, 0); - if (block.typ == -1) { - wnoutrefresh(w); - return; - } - for (b = 0; b < TETRIS; b++) { - c = TETROMINOS[block.typ][block.ori][b]; - wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1); - ADD_BLOCK(w, TYPE_TO_CELL(block.typ)); - } - wnoutrefresh(w); -} - -/* - Display score information in a dedicated window. - */ -void display_score(WINDOW *w, tetris_game *tg) -{ - wclear(w); - box(w, 0, 0); - wprintw(w, (char *)"Score\n%d\n", tg->points); - wprintw(w, (char *)"Level\n%d\n", tg->level); - wprintw(w, (char *)"Lines\n%d\n", tg->lines_remaining); - wnoutrefresh(w); -} - -/* - Save and exit the game. - */ -void save(tetris_game *game, WINDOW *w) -{ - FILE *f; - - wclear(w); - box(w, 0, 0); // return the border - wmove(w, 1, 1); - wprintw(w, (char *)"Save and exit? [Y/n] "); - wrefresh(w); - timeout(-1); - if (getch() == 'n') { - timeout(0); - return; - } - f = fopen("tetris.save", "w"); - tg_save(game, f); - fclose(f); - tg_delete(game); - endwin(); - fprintf(stderr,"Game saved to \"tetris.save\".\n"); - fprintf(stderr,"Resume by passing the filename as an argument to this program.\n"); - exit(EXIT_SUCCESS); -} - -/* - Do the NCURSES initialization steps for color blocks. - */ -void init_colors(void) -{ - start_color(); - //init_color(COLOR_ORANGE, 1000, 647, 0); - init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK); - init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK); - init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK); - init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK); - init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK); - init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK); - init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); -} - -/* - Main tetris game! - */ -#ifdef STANDALONE -char *clonestr(char *str) -{ - char *clone; int32_t len; - if ( str == 0 || str[0] == 0 ) - { - printf("warning cloning nullstr.%p\n",str); -#ifdef __APPLE__ - while ( 1 ) sleep(1); -#endif - str = (char *)""; - } - len = strlen(str); - clone = (char *)calloc(1,len+16); - strcpy(clone,str); - return(clone); -} - -int tetris(int argc, char **argv) -{ - tetris_game *tg; - tetris_move move = TM_NONE; - bool running = true; - WINDOW *board, *next, *hold, *score; - int32_t c,skipcount=0; bits256 gametxid; uint32_t eventid = 0; - memset(&gametxid,0,sizeof(gametxid)); - // Load file if given a filename. - if (argc >= 2) { - FILE *f = fopen(argv[1], "r"); - if (f == NULL) { - perror("tetris"); - exit(EXIT_FAILURE); - } - tg = tg_load(f); - fclose(f); - } else { - // Otherwise create new game. - tg = tg_create(22, 10); - } - // NCURSES initialization: - initscr(); // initialize curses - cbreak(); // pass key presses to program, but not signals - noecho(); // don't echo key presses to screen - keypad(stdscr, TRUE); // allow arrow keys - timeout(0); // no blocking on getch() - curs_set(0); // set the cursor to invisible - init_colors(); // setup tetris colors - - // Create windows for each section of the interface. - board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); - next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); - hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); - score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); - int32_t counter = 0; - // Game loop - while (running) { - running = tg_tick(tg, move); - display_board(board, tg); - display_piece(next, tg->next); - display_piece(hold, tg->stored); - display_score(score, tg); - if ( (counter++ % 5) == 0 ) - doupdate(); - sleep_milli(10); - c = getch(); - if ( c != -1 || skipcount == 0x3fff ) - { - if ( skipcount > 0 ) - issue_games_events(gametxid,eventid-skipcount,skipcount | 0x4000); - if ( c != -1 ) - issue_games_events(gametxid,eventid,c); - skipcount = 0; - } else skipcount++; - eventid++; - switch ( c ) - { - case KEY_LEFT: - move = TM_LEFT; - break; - case KEY_RIGHT: - move = TM_RIGHT; - break; - case KEY_UP: - move = TM_CLOCK; - break; - case KEY_DOWN: - move = TM_DROP; - break; - case 'q': - running = false; - move = TM_NONE; - break; - case 'p': - wclear(board); - box(board, 0, 0); - wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); - wprintw(board, "PAUSED"); - wrefresh(board); - timeout(-1); - getch(); - timeout(0); - move = TM_NONE; - break; - case 's': - save(tg, board); - move = TM_NONE; - break; - case ' ': - move = TM_HOLD; - break; - default: - move = TM_NONE; - } - } - - // Deinitialize NCurses - wclear(stdscr); - endwin(); - // Output ending message. - printf("Game over!\n"); - printf("You finished with %d points on level %d.\n", tg->points, tg->level); - - // Deinitialize Tetris - tg_delete(tg); - return 0; -} - -int32_t games_replay(uint64_t seed,int32_t sleeptime) -{ - return(-1); -} +#include "tetris.c" #endif diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 19e5f8d26..07a1f8e65 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -20,7 +20,7 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define GAMES_MAXKEYSTROKESGAP 60 #define GAMES_MAXPLAYERS 64 #define GAMES_REGISTRATIONSIZE (100 * 10000) -#define GAMES_REGISTRATION 5 +#define GAMES_REGISTRATION 1 #define GAMES_RNGMULT 11109 #define GAMES_RNGOFFSET 13849 @@ -29,7 +29,6 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define MYCCNAME "games" std::string Games_pname; -#define GAMENAME "sudoku" #define RPC_FUNCS \ { (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \ @@ -104,33 +103,4 @@ if ( cp->evalcode == EVAL_GAMES ) \ } #endif -#define MAXPACK 23 -struct games_packitem -{ - int32_t type,launch,count,which,hplus,dplus,arm,flags,group; - char damage[8],hurldmg[8]; -}; - -struct games_player -{ - int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; - struct games_packitem gamespack[MAXPACK]; -}; - -struct games_state -{ - uint64_t seed; - char *keystrokes,*keystrokeshex; - uint32_t needflush,replaydone; - int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; - FILE *logfp; - struct games_player P; - char buffered[10000]; - uint8_t playerdata[10000]; -}; - -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); -void games_packitemstr(char *packitemstr,struct games_packitem *item); - - #endif diff --git a/src/cc/tetris.c b/src/cc/tetris.c new file mode 100644 index 000000000..4ffd27575 --- /dev/null +++ b/src/cc/tetris.c @@ -0,0 +1,758 @@ + +#include "tetris.h" +#include "dapps/dappstd.c" + + +/***************************************************************************/ +/** https://github.com/brenns10/tetris + @file main.c + @author Stephen Brennan + @date Created Wednesday, 10 June 2015 + @brief Main program for tetris. + @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised + BSD License. See LICENSE.txt for details. + *******************************************************************************/ + + +#include // for FILE +#include // for bool +#include +#include +#include +#include +#include +#include + +#ifdef BUILD_GAMESCC +#include "rogue/cursesd.h" +#else +#include +#endif + + +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +/******************************************************************************* + Array Definitions + *******************************************************************************/ + +const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] = +{ + // I + {{{1, 0}, {1, 1}, {1, 2}, {1, 3}}, + {{0, 2}, {1, 2}, {2, 2}, {3, 2}}, + {{3, 0}, {3, 1}, {3, 2}, {3, 3}}, + {{0, 1}, {1, 1}, {2, 1}, {3, 1}}}, + // J + {{{0, 0}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 2}}, + {{0, 1}, {1, 1}, {2, 0}, {2, 1}}}, + // L + {{{0, 2}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {2, 1}, {2, 2}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 0}}, + {{0, 0}, {0, 1}, {1, 1}, {2, 1}}}, + // O + {{{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}}, + // S + {{{0, 1}, {0, 2}, {1, 0}, {1, 1}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 2}}, + {{1, 1}, {1, 2}, {2, 0}, {2, 1}}, + {{0, 0}, {1, 0}, {1, 1}, {2, 1}}}, + // T + {{{0, 1}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 1}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 1}}}, + // Z + {{{0, 0}, {0, 1}, {1, 1}, {1, 2}}, + {{0, 2}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {2, 1}, {2, 2}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 0}}}, +}; + +const int GRAVITY_LEVEL[MAX_LEVEL+1] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + //10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 30, 28, 26, 24, 22, 20, 16, 12, 8, 4 +}; + +/******************************************************************************* + Helper Functions for Blocks + *******************************************************************************/ + +void sleep_milli(int milliseconds) +{ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = milliseconds * 1000 * 1000; + nanosleep(&ts, NULL); +} + +/* + Return the block at the given row and column. + */ +char tg_get(tetris_game *obj, int row, int column) +{ + return obj->board[obj->cols * row + column]; +} + +/* + Set the block at the given row and column. + */ +static void tg_set(tetris_game *obj, int row, int column, char value) +{ + obj->board[obj->cols * row + column] = value; +} + +/* + Check whether a row and column are in bounds. + */ +bool tg_check(tetris_game *obj, int row, int col) +{ + return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols; +} + +/* + Place a block onto the board. + */ +static void tg_put(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, + TYPE_TO_CELL(block.typ)); + } +} + +/* + Clear a block out of the board. + */ +static void tg_remove(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY); + } +} + +/* + Check if a block can be placed on the board. + */ +static bool tg_fits(tetris_game *obj, tetris_block block) +{ + int i, r, c; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + r = block.loc.row + cell.row; + c = block.loc.col + cell.col; + if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) { + return false; + } + } + return true; +} + +/* + Create a new falling block and populate the next falling block with a random + one. + */ +static void tg_new_falling(tetris_game *obj) +{ + // Put in a new falling tetromino. + obj->falling = obj->next; + obj->next.typ = random_tetromino(); + obj->next.ori = 0; + obj->next.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; +} + +/******************************************************************************* + Game Turn Helpers + *******************************************************************************/ + +/* + Tick gravity, and move the block down if gravity should act. + */ +static void tg_do_gravity_tick(tetris_game *obj) +{ + obj->ticks_till_gravity--; + if (obj->ticks_till_gravity <= 0) { + tg_remove(obj, obj->falling); + obj->falling.loc.row++; + if (tg_fits(obj, obj->falling)) { + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + } else { + obj->falling.loc.row--; + tg_put(obj, obj->falling); + + tg_new_falling(obj); + } + tg_put(obj, obj->falling); + } +} + +/* + Move the falling tetris block left (-1) or right (+1). + */ +static void tg_move(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + obj->falling.loc.col += direction; + if (!tg_fits(obj, obj->falling)) { + obj->falling.loc.col -= direction; + } + tg_put(obj, obj->falling); +} + +/* + Send the falling tetris block to the bottom. + */ +static void tg_down(tetris_game *obj) +{ + tg_remove(obj, obj->falling); + while (tg_fits(obj, obj->falling)) { + obj->falling.loc.row++; + } + obj->falling.loc.row--; + tg_put(obj, obj->falling); + tg_new_falling(obj); +} + +/* + Rotate the falling block in either direction (+/-1). + */ +static void tg_rotate(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + + while (true) { + obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS; + + // If the new orientation fits, we're done. + if (tg_fits(obj, obj->falling)) + break; + + // Otherwise, try moving left to make it fit. + obj->falling.loc.col--; + if (tg_fits(obj, obj->falling)) + break; + + // Finally, try moving right to make it fit. + obj->falling.loc.col += 2; + if (tg_fits(obj, obj->falling)) + break; + + // Put it back in its original location and try the next orientation. + obj->falling.loc.col--; + // Worst case, we come back to the original orientation and it fits, so this + // loop will terminate. + } + + tg_put(obj, obj->falling); +} + +/* + Swap the falling block with the block in the hold buffer. + */ +static void tg_hold(tetris_game *obj) +{ + tg_remove(obj, obj->falling); + if (obj->stored.typ == -1) { + obj->stored = obj->falling; + tg_new_falling(obj); + } else { + int typ = obj->falling.typ, ori = obj->falling.ori; + obj->falling.typ = obj->stored.typ; + obj->falling.ori = obj->stored.ori; + obj->stored.typ = typ; + obj->stored.ori = ori; + while (!tg_fits(obj, obj->falling)) { + obj->falling.loc.row--; + } + } + tg_put(obj, obj->falling); +} + +/* + Perform the action specified by the move. + */ +static void tg_handle_move(tetris_game *obj, tetris_move move) +{ + switch (move) { + case TM_LEFT: + tg_move(obj, -1); + break; + case TM_RIGHT: + tg_move(obj, 1); + break; + case TM_DROP: + tg_down(obj); + break; + case TM_CLOCK: + tg_rotate(obj, 1); + break; + case TM_COUNTER: + tg_rotate(obj, -1); + break; + case TM_HOLD: + tg_hold(obj); + break; + default: + // pass + break; + } +} + +/* + Return true if line i is full. + */ +static bool tg_line_full(tetris_game *obj, int i) +{ + int j; + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) + return false; + } + return true; +} + +/* + Shift every row above r down one. + */ +static void tg_shift_lines(tetris_game *obj, int r) +{ + int i, j; + for (i = r-1; i >= 0; i--) { + for (j = 0; j < obj->cols; j++) { + tg_set(obj, i+1, j, tg_get(obj, i, j)); + tg_set(obj, i, j, TC_EMPTY); + } + } +} + +/* + Find rows that are filled, remove them, shift, and return the number of + cleared rows. + */ +static int tg_check_lines(tetris_game *obj) +{ + int i, nlines = 0; + tg_remove(obj, obj->falling); // don't want to mess up falling block + + for (i = obj->rows-1; i >= 0; i--) { + if (tg_line_full(obj, i)) { + tg_shift_lines(obj, i); + i++; // do this line over again since they're shifted + nlines++; + } + } + + tg_put(obj, obj->falling); // replace + return nlines; +} + +/* + Adjust the score for the game, given how many lines were just cleared. + */ +static void tg_adjust_score(tetris_game *obj, int lines_cleared) +{ + static int line_multiplier[] = {0, 40, 100, 300, 1200}; + obj->points += line_multiplier[lines_cleared] * (obj->level + 1); + if (lines_cleared >= obj->lines_remaining) { + obj->level = MIN(MAX_LEVEL, obj->level + 1); + lines_cleared -= obj->lines_remaining; + obj->lines_remaining = LINES_PER_LEVEL - lines_cleared; + } else { + obj->lines_remaining -= lines_cleared; + } +} + +/* + Return true if the game is over. + */ +static bool tg_game_over(tetris_game *obj) +{ + int i, j; + bool over = false; + tg_remove(obj, obj->falling); + for (i = 0; i < 2; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + over = true; + } + } + } + tg_put(obj, obj->falling); + return over; +} + +/******************************************************************************* + Main Public Functions + *******************************************************************************/ + +/* + Do a single game tick: process gravity, user input, and score. Return true if + the game is still running, false if it is over. + */ +bool tg_tick(tetris_game *obj, tetris_move move) +{ + int lines_cleared; + // Handle gravity. + tg_do_gravity_tick(obj); + + // Handle input. + tg_handle_move(obj, move); + + // Check for cleared lines + lines_cleared = tg_check_lines(obj); + + tg_adjust_score(obj, lines_cleared); + + // Return whether the game will continue (NOT whether it's over) + return !tg_game_over(obj); +} + +void tg_init(tetris_game *obj, int rows, int cols) +{ + // Initialization logic + obj->rows = rows; + obj->cols = cols; + obj->board = (char *)malloc(rows * cols); + memset(obj->board, TC_EMPTY, rows * cols); + obj->points = 0; + obj->level = 0; + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + obj->lines_remaining = LINES_PER_LEVEL; + //srand(time(NULL)); + tg_new_falling(obj); + tg_new_falling(obj); + obj->stored.typ = -1; + obj->stored.ori = 0; + obj->stored.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; + printf("%d", obj->falling.loc.col); +} + +tetris_game *tg_create(int rows, int cols) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); + tg_init(obj, rows, cols); + return obj; +} + +void tg_destroy(tetris_game *obj) +{ + // Cleanup logic + free(obj->board); +} + +void tg_delete(tetris_game *obj) { + tg_destroy(obj); + free(obj); +} + +/* + Load a game from a file. + */ +tetris_game *tg_load(FILE *f) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); + if (fread(obj, sizeof(tetris_game), 1, f) != 1 ) + { + fprintf(stderr,"read game error\n"); + free(obj); + obj = 0; + } + else + { + obj->board = (char *)malloc(obj->rows * obj->cols); + if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + { + fprintf(stderr,"fread error\n"); + free(obj->board); + free(obj); + obj = 0; + } + } + return obj; +} + +/* + Save a game to a file. + */ +void tg_save(tetris_game *obj, FILE *f) +{ + if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) + fprintf(stderr,"error writing tetrisgame\n"); + else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + fprintf(stderr,"error writing board\n"); +} + +/* + Print a game board to a file. Really just for early debugging. + */ +void tg_print(tetris_game *obj, FILE *f) { + int i, j; + for (i = 0; i < obj->rows; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) { + fputs(TC_EMPTY_STR, f); + } else { + fputs(TC_BLOCK_STR, f); + } + } + fputc('\n', f); + } +} + +/* + 2 columns per cell makes the game much nicer. + */ +#define COLS_PER_CELL 2 +/* + Macro to print a cell of a specific type to a window. + */ +#define ADD_BLOCK(w,x) waddch((w),' '|A_REVERSE|COLOR_PAIR(x)); \ +waddch((w),' '|A_REVERSE|COLOR_PAIR(x)) +#define ADD_EMPTY(w) waddch((w), ' '); waddch((w), ' ') + +/* + Print the tetris board onto the ncurses window. + */ +void display_board(WINDOW *w, tetris_game *obj) +{ + int i, j; + box(w, 0, 0); + for (i = 0; i < obj->rows; i++) { + wmove(w, 1 + i, 1); + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + ADD_BLOCK(w,tg_get(obj, i, j)); + } else { + ADD_EMPTY(w); + } + } + } + wnoutrefresh(w); +} + +/* + Display a tetris piece in a dedicated window. + */ +void display_piece(WINDOW *w, tetris_block block) +{ + int b; + tetris_location c; + wclear(w); + box(w, 0, 0); + if (block.typ == -1) { + wnoutrefresh(w); + return; + } + for (b = 0; b < TETRIS; b++) { + c = TETROMINOS[block.typ][block.ori][b]; + wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1); + ADD_BLOCK(w, TYPE_TO_CELL(block.typ)); + } + wnoutrefresh(w); +} + +/* + Display score information in a dedicated window. + */ +void display_score(WINDOW *w, tetris_game *tg) +{ + wclear(w); + box(w, 0, 0); + wprintw(w, (char *)"Score\n%d\n", tg->points); + wprintw(w, (char *)"Level\n%d\n", tg->level); + wprintw(w, (char *)"Lines\n%d\n", tg->lines_remaining); + wnoutrefresh(w); +} + +/* + Save and exit the game. + */ +void save(tetris_game *game, WINDOW *w) +{ + FILE *f; + + wclear(w); + box(w, 0, 0); // return the border + wmove(w, 1, 1); + wprintw(w, (char *)"Save and exit? [Y/n] "); + wrefresh(w); + timeout(-1); + if (getch() == 'n') { + timeout(0); + return; + } + f = fopen("tetris.save", "w"); + tg_save(game, f); + fclose(f); + tg_delete(game); + endwin(); + fprintf(stderr,"Game saved to \"tetris.save\".\n"); + fprintf(stderr,"Resume by passing the filename as an argument to this program.\n"); + exit(EXIT_SUCCESS); +} + +/* + Do the NCURSES initialization steps for color blocks. + */ +void init_colors(void) +{ + start_color(); + //init_color(COLOR_ORANGE, 1000, 647, 0); + init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK); + init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK); + init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK); + init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK); + init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK); + init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK); + init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); +} + +/* + Main tetris game! + */ +#ifdef STANDALONE +char *clonestr(char *str) +{ + char *clone; int32_t len; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + len = strlen(str); + clone = (char *)calloc(1,len+16); + strcpy(clone,str); + return(clone); +} + +int tetris(int argc, char **argv) +{ + tetris_game *tg; + tetris_move move = TM_NONE; + bool running = true; + WINDOW *board, *next, *hold, *score; + int32_t c,skipcount=0; bits256 gametxid; uint32_t eventid = 0; + memset(&gametxid,0,sizeof(gametxid)); + // Load file if given a filename. + if (argc >= 2) { + FILE *f = fopen(argv[1], "r"); + if (f == NULL) { + perror("tetris"); + exit(EXIT_FAILURE); + } + tg = tg_load(f); + fclose(f); + } else { + // Otherwise create new game. + tg = tg_create(22, 10); + } + // NCURSES initialization: + initscr(); // initialize curses + cbreak(); // pass key presses to program, but not signals + noecho(); // don't echo key presses to screen + keypad(stdscr, TRUE); // allow arrow keys + timeout(0); // no blocking on getch() + curs_set(0); // set the cursor to invisible + init_colors(); // setup tetris colors + + // Create windows for each section of the interface. + board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); + next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); + hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); + score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); + int32_t counter = 0; + // Game loop + while (running) { + running = tg_tick(tg, move); + display_board(board, tg); + display_piece(next, tg->next); + display_piece(hold, tg->stored); + display_score(score, tg); + if ( (counter++ % 5) == 0 ) + doupdate(); + sleep_milli(10); + c = getch(); + if ( c != -1 || skipcount == 0x3fff ) + { + if ( skipcount > 0 ) + issue_games_events(gametxid,eventid-skipcount,skipcount | 0x4000); + if ( c != -1 ) + issue_games_events(gametxid,eventid,c); + skipcount = 0; + } else skipcount++; + eventid++; + switch ( c ) + { + case KEY_LEFT: + move = TM_LEFT; + break; + case KEY_RIGHT: + move = TM_RIGHT; + break; + case KEY_UP: + move = TM_CLOCK; + break; + case KEY_DOWN: + move = TM_DROP; + break; + case 'q': + running = false; + move = TM_NONE; + break; + case 'p': + wclear(board); + box(board, 0, 0); + wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); + wprintw(board, "PAUSED"); + wrefresh(board); + timeout(-1); + getch(); + timeout(0); + move = TM_NONE; + break; + case 's': + save(tg, board); + move = TM_NONE; + break; + case ' ': + move = TM_HOLD; + break; + default: + move = TM_NONE; + } + } + + // Deinitialize NCurses + wclear(stdscr); + endwin(); + // Output ending message. + printf("Game over!\n"); + printf("You finished with %d points on level %d.\n", tg->points, tg->level); + + // Deinitialize Tetris + tg_delete(tg); + return 0; +} + +int32_t games_replay(uint64_t seed,int32_t sleeptime) +{ + return(-1); +} diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index d6ae473fe..fd833d7b7 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -1,939 +1,4 @@ -/***************************************************************************/ -/** https://github.com/brenns10/tetris - @file main.c - @author Stephen Brennan - @date Created Wednesday, 10 June 2015 - @brief Main program for tetris. - @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised - BSD License. See LICENSE.txt for details. - *******************************************************************************/ - - -#ifndef TETRIS_H -#define TETRIS_H - -#include // for FILE -#include // for bool -#include -#include -#include -#include -#include - -#include -#include - -//#include -//#include - - -/* - Convert a tetromino type to its corresponding cell. - */ -#define TYPE_TO_CELL(x) ((x)+1) - -/* - Strings for how you would print a tetris board. - */ -#define TC_EMPTY_STR " " -#define TC_BLOCK_STR "\u2588" - -/* - Questions about a tetris cell. - */ -#define TC_IS_EMPTY(x) ((x) == TC_EMPTY) -#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x)) - -/* - How many cells in a tetromino? - */ -#define TETRIS 4 -/* - How many tetrominos? - */ -#define NUM_TETROMINOS 7 -/* - How many orientations of a tetromino? - */ -#define NUM_ORIENTATIONS 4 - -/* - Level constants. - */ -#define MAX_LEVEL 19 -#define LINES_PER_LEVEL 10 - -/* - A "cell" is a 1x1 block within a tetris board. - */ -typedef enum { - TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ -} tetris_cell; - -/* - A "type" is a type/shape of a tetromino. Not including orientation. - */ -typedef enum { - TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z -} tetris_type; - -/* - A row,column pair. Negative numbers allowed, because we need them for - offsets. - */ -typedef struct { - int row; - int col; -} tetris_location; - -/* - A "block" is a struct that contains information about a tetromino. - Specifically, what type it is, what orientation it has, and where it is. - */ -typedef struct { - int typ; - int ori; - tetris_location loc; -} tetris_block; - -/* - All possible moves to give as input to the game. - */ -typedef enum { - TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE -} tetris_move; - -/* - A game object! - */ -typedef struct { - /* - Game board stuff: - */ - int rows; - int cols; - char *board; - /* - Scoring information: - */ - int points; - int level; - /* - Falling block is the one currently going down. Next block is the one that - will be falling after this one. Stored is the block that you can swap out. - */ - tetris_block falling; - tetris_block next; - tetris_block stored; - /* - Number of game ticks until the block will move down. - */ - int ticks_till_gravity; - /* - Number of lines until you advance to the next level. - */ - int lines_remaining; -} tetris_game; - -/* - This array stores all necessary information about the cells that are filled by - each tetromino. The first index is the type of the tetromino (i.e. shape, - e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final - array contains 4 tetris_location objects, each mapping to an offset from a - point on the upper left that is the tetromino "origin". - */ -extern tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS]; - -/* - This array tells you how many ticks per gravity by level. Decreases as level - increases, to add difficulty. - */ -extern int GRAVITY_LEVEL[MAX_LEVEL+1]; - -// Data structure manipulation. -void tg_init(tetris_game *obj, int rows, int cols); -tetris_game *tg_create(int rows, int cols); -void tg_destroy(tetris_game *obj); -void tg_delete(tetris_game *obj); -tetris_game *tg_load(FILE *f); -void tg_save(tetris_game *obj, FILE *f); - -// Public methods not related to memory: -char tg_get(tetris_game *obj, int row, int col); -bool tg_check(tetris_game *obj, int row, int col); -bool tg_tick(tetris_game *obj, tetris_move move); -void tg_print(tetris_game *obj, FILE *f); - -#endif // TETRIS_H - - -#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) -#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) - -/******************************************************************************* - Array Definitions - *******************************************************************************/ - -tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] = { - // I - {{{1, 0}, {1, 1}, {1, 2}, {1, 3}}, - {{0, 2}, {1, 2}, {2, 2}, {3, 2}}, - {{3, 0}, {3, 1}, {3, 2}, {3, 3}}, - {{0, 1}, {1, 1}, {2, 1}, {3, 1}}}, - // J - {{{0, 0}, {1, 0}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {2, 1}}, - {{1, 0}, {1, 1}, {1, 2}, {2, 2}}, - {{0, 1}, {1, 1}, {2, 0}, {2, 1}}}, - // L - {{{0, 2}, {1, 0}, {1, 1}, {1, 2}}, - {{0, 1}, {1, 1}, {2, 1}, {2, 2}}, - {{1, 0}, {1, 1}, {1, 2}, {2, 0}}, - {{0, 0}, {0, 1}, {1, 1}, {2, 1}}}, - // O - {{{0, 1}, {0, 2}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {1, 2}}}, - // S - {{{0, 1}, {0, 2}, {1, 0}, {1, 1}}, - {{0, 1}, {1, 1}, {1, 2}, {2, 2}}, - {{1, 1}, {1, 2}, {2, 0}, {2, 1}}, - {{0, 0}, {1, 0}, {1, 1}, {2, 1}}}, - // T - {{{0, 1}, {1, 0}, {1, 1}, {1, 2}}, - {{0, 1}, {1, 1}, {1, 2}, {2, 1}}, - {{1, 0}, {1, 1}, {1, 2}, {2, 1}}, - {{0, 1}, {1, 0}, {1, 1}, {2, 1}}}, - // Z - {{{0, 0}, {0, 1}, {1, 1}, {1, 2}}, - {{0, 2}, {1, 1}, {1, 2}, {2, 1}}, - {{1, 0}, {1, 1}, {2, 1}, {2, 2}}, - {{0, 1}, {1, 0}, {1, 1}, {2, 0}}}, -}; - -int GRAVITY_LEVEL[MAX_LEVEL+1] = { - // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, - //10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 30, 28, 26, 24, 22, 20, 16, 12, 8, 4 -}; - -/******************************************************************************* - Helper Functions for Blocks - *******************************************************************************/ - -void sleep_milli(int milliseconds) -{ - struct timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = milliseconds * 1000 * 1000; - nanosleep(&ts, NULL); -} - -/* - Return the block at the given row and column. - */ -char tg_get(tetris_game *obj, int row, int column) -{ - return obj->board[obj->cols * row + column]; -} - -/* - Set the block at the given row and column. - */ -static void tg_set(tetris_game *obj, int row, int column, char value) -{ - obj->board[obj->cols * row + column] = value; -} - -/* - Check whether a row and column are in bounds. - */ -bool tg_check(tetris_game *obj, int row, int col) -{ - return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols; -} - -/* - Place a block onto the board. - */ -static void tg_put(tetris_game *obj, tetris_block block) -{ - int i; - for (i = 0; i < TETRIS; i++) { - tetris_location cell = TETROMINOS[block.typ][block.ori][i]; - tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, - TYPE_TO_CELL(block.typ)); - } -} - -/* - Clear a block out of the board. - */ -static void tg_remove(tetris_game *obj, tetris_block block) -{ - int i; - for (i = 0; i < TETRIS; i++) { - tetris_location cell = TETROMINOS[block.typ][block.ori][i]; - tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY); - } -} - -/* - Check if a block can be placed on the board. - */ -static bool tg_fits(tetris_game *obj, tetris_block block) -{ - int i, r, c; - for (i = 0; i < TETRIS; i++) { - tetris_location cell = TETROMINOS[block.typ][block.ori][i]; - r = block.loc.row + cell.row; - c = block.loc.col + cell.col; - if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) { - return false; - } - } - return true; -} - -/* - Return a random tetromino type. - */ -static int random_tetromino(void) { - return rand() % NUM_TETROMINOS; -} - -/* - Create a new falling block and populate the next falling block with a random - one. - */ -static void tg_new_falling(tetris_game *obj) -{ - // Put in a new falling tetromino. - obj->falling = obj->next; - obj->next.typ = random_tetromino(); - obj->next.ori = 0; - obj->next.loc.row = 0; - obj->next.loc.col = obj->cols/2 - 2; -} - -/******************************************************************************* - Game Turn Helpers - *******************************************************************************/ - -/* - Tick gravity, and move the block down if gravity should act. - */ -static void tg_do_gravity_tick(tetris_game *obj) -{ - obj->ticks_till_gravity--; - if (obj->ticks_till_gravity <= 0) { - tg_remove(obj, obj->falling); - obj->falling.loc.row++; - if (tg_fits(obj, obj->falling)) { - obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; - } else { - obj->falling.loc.row--; - tg_put(obj, obj->falling); - - tg_new_falling(obj); - } - tg_put(obj, obj->falling); - } -} - -/* - Move the falling tetris block left (-1) or right (+1). - */ -static void tg_move(tetris_game *obj, int direction) -{ - tg_remove(obj, obj->falling); - obj->falling.loc.col += direction; - if (!tg_fits(obj, obj->falling)) { - obj->falling.loc.col -= direction; - } - tg_put(obj, obj->falling); -} - -/* - Send the falling tetris block to the bottom. - */ -static void tg_down(tetris_game *obj) -{ - tg_remove(obj, obj->falling); - while (tg_fits(obj, obj->falling)) { - obj->falling.loc.row++; - } - obj->falling.loc.row--; - tg_put(obj, obj->falling); - tg_new_falling(obj); -} - -/* - Rotate the falling block in either direction (+/-1). - */ -static void tg_rotate(tetris_game *obj, int direction) -{ - tg_remove(obj, obj->falling); - - while (true) { - obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS; - - // If the new orientation fits, we're done. - if (tg_fits(obj, obj->falling)) - break; - - // Otherwise, try moving left to make it fit. - obj->falling.loc.col--; - if (tg_fits(obj, obj->falling)) - break; - - // Finally, try moving right to make it fit. - obj->falling.loc.col += 2; - if (tg_fits(obj, obj->falling)) - break; - - // Put it back in its original location and try the next orientation. - obj->falling.loc.col--; - // Worst case, we come back to the original orientation and it fits, so this - // loop will terminate. - } - - tg_put(obj, obj->falling); -} - -/* - Swap the falling block with the block in the hold buffer. - */ -static void tg_hold(tetris_game *obj) -{ - tg_remove(obj, obj->falling); - if (obj->stored.typ == -1) { - obj->stored = obj->falling; - tg_new_falling(obj); - } else { - int typ = obj->falling.typ, ori = obj->falling.ori; - obj->falling.typ = obj->stored.typ; - obj->falling.ori = obj->stored.ori; - obj->stored.typ = typ; - obj->stored.ori = ori; - while (!tg_fits(obj, obj->falling)) { - obj->falling.loc.row--; - } - } - tg_put(obj, obj->falling); -} - -/* - Perform the action specified by the move. - */ -static void tg_handle_move(tetris_game *obj, tetris_move move) -{ - switch (move) { - case TM_LEFT: - tg_move(obj, -1); - break; - case TM_RIGHT: - tg_move(obj, 1); - break; - case TM_DROP: - tg_down(obj); - break; - case TM_CLOCK: - tg_rotate(obj, 1); - break; - case TM_COUNTER: - tg_rotate(obj, -1); - break; - case TM_HOLD: - tg_hold(obj); - break; - default: - // pass - break; - } -} - -/* - Return true if line i is full. - */ -static bool tg_line_full(tetris_game *obj, int i) -{ - int j; - for (j = 0; j < obj->cols; j++) { - if (TC_IS_EMPTY(tg_get(obj, i, j))) - return false; - } - return true; -} - -/* - Shift every row above r down one. - */ -static void tg_shift_lines(tetris_game *obj, int r) -{ - int i, j; - for (i = r-1; i >= 0; i--) { - for (j = 0; j < obj->cols; j++) { - tg_set(obj, i+1, j, tg_get(obj, i, j)); - tg_set(obj, i, j, TC_EMPTY); - } - } -} - -/* - Find rows that are filled, remove them, shift, and return the number of - cleared rows. - */ -static int tg_check_lines(tetris_game *obj) -{ - int i, nlines = 0; - tg_remove(obj, obj->falling); // don't want to mess up falling block - - for (i = obj->rows-1; i >= 0; i--) { - if (tg_line_full(obj, i)) { - tg_shift_lines(obj, i); - i++; // do this line over again since they're shifted - nlines++; - } - } - - tg_put(obj, obj->falling); // replace - return nlines; -} - -/* - Adjust the score for the game, given how many lines were just cleared. - */ -static void tg_adjust_score(tetris_game *obj, int lines_cleared) -{ - static int line_multiplier[] = {0, 40, 100, 300, 1200}; - obj->points += line_multiplier[lines_cleared] * (obj->level + 1); - if (lines_cleared >= obj->lines_remaining) { - obj->level = MIN(MAX_LEVEL, obj->level + 1); - lines_cleared -= obj->lines_remaining; - obj->lines_remaining = LINES_PER_LEVEL - lines_cleared; - } else { - obj->lines_remaining -= lines_cleared; - } -} - -/* - Return true if the game is over. - */ -static bool tg_game_over(tetris_game *obj) -{ - int i, j; - bool over = false; - tg_remove(obj, obj->falling); - for (i = 0; i < 2; i++) { - for (j = 0; j < obj->cols; j++) { - if (TC_IS_FILLED(tg_get(obj, i, j))) { - over = true; - } - } - } - tg_put(obj, obj->falling); - return over; -} - -/******************************************************************************* - Main Public Functions - *******************************************************************************/ - -/* - Do a single game tick: process gravity, user input, and score. Return true if - the game is still running, false if it is over. - */ -bool tg_tick(tetris_game *obj, tetris_move move) -{ - int lines_cleared; - // Handle gravity. - tg_do_gravity_tick(obj); - - // Handle input. - tg_handle_move(obj, move); - - // Check for cleared lines - lines_cleared = tg_check_lines(obj); - - tg_adjust_score(obj, lines_cleared); - - // Return whether the game will continue (NOT whether it's over) - return !tg_game_over(obj); -} - -void tg_init(tetris_game *obj, int rows, int cols) -{ - // Initialization logic - obj->rows = rows; - obj->cols = cols; - obj->board = (char *)malloc(rows * cols); - memset(obj->board, TC_EMPTY, rows * cols); - obj->points = 0; - obj->level = 0; - obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; - obj->lines_remaining = LINES_PER_LEVEL; - srand(time(NULL)); - tg_new_falling(obj); - tg_new_falling(obj); - obj->stored.typ = -1; - obj->stored.ori = 0; - obj->stored.loc.row = 0; - obj->next.loc.col = obj->cols/2 - 2; - printf("%d", obj->falling.loc.col); -} - -tetris_game *tg_create(int rows, int cols) -{ - tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); - tg_init(obj, rows, cols); - return obj; -} - -void tg_destroy(tetris_game *obj) -{ - // Cleanup logic - free(obj->board); -} - -void tg_delete(tetris_game *obj) { - tg_destroy(obj); - free(obj); -} - -/* - Load a game from a file. - */ -tetris_game *tg_load(FILE *f) -{ - tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); - if (fread(obj, sizeof(tetris_game), 1, f) != 1 ) - { - fprintf(stderr,"read game error\n"); - free(obj); - obj = 0; - } - else - { - obj->board = (char *)malloc(obj->rows * obj->cols); - if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) - { - fprintf(stderr,"fread error\n"); - free(obj->board); - free(obj); - obj = 0; - } - } - return obj; -} - -/* - Save a game to a file. - */ -void tg_save(tetris_game *obj, FILE *f) -{ - if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) - fprintf(stderr,"error writing tetrisgame\n"); - else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) - fprintf(stderr,"error writing board\n"); -} - -/* - Print a game board to a file. Really just for early debugging. - */ -void tg_print(tetris_game *obj, FILE *f) { - int i, j; - for (i = 0; i < obj->rows; i++) { - for (j = 0; j < obj->cols; j++) { - if (TC_IS_EMPTY(tg_get(obj, i, j))) { - fputs(TC_EMPTY_STR, f); - } else { - fputs(TC_BLOCK_STR, f); - } - } - fputc('\n', f); - } -} - -/* - 2 columns per cell makes the game much nicer. - */ -#define COLS_PER_CELL 2 -/* - Macro to print a cell of a specific type to a window. - */ -#define ADD_BLOCK(w,x) waddch((w),' '|A_REVERSE|COLOR_PAIR(x)); \ -waddch((w),' '|A_REVERSE|COLOR_PAIR(x)) -#define ADD_EMPTY(w) waddch((w), ' '); waddch((w), ' ') - -/* - Print the tetris board onto the ncurses window. - */ -void display_board(WINDOW *w, tetris_game *obj) -{ - int i, j; - box(w, 0, 0); - for (i = 0; i < obj->rows; i++) { - wmove(w, 1 + i, 1); - for (j = 0; j < obj->cols; j++) { - if (TC_IS_FILLED(tg_get(obj, i, j))) { - ADD_BLOCK(w,tg_get(obj, i, j)); - } else { - ADD_EMPTY(w); - } - } - } - wnoutrefresh(w); -} - -/* - Display a tetris piece in a dedicated window. - */ -void display_piece(WINDOW *w, tetris_block block) -{ - int b; - tetris_location c; - wclear(w); - box(w, 0, 0); - if (block.typ == -1) { - wnoutrefresh(w); - return; - } - for (b = 0; b < TETRIS; b++) { - c = TETROMINOS[block.typ][block.ori][b]; - wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1); - ADD_BLOCK(w, TYPE_TO_CELL(block.typ)); - } - wnoutrefresh(w); -} - -/* - Display score information in a dedicated window. - */ -void display_score(WINDOW *w, tetris_game *tg) -{ - wclear(w); - box(w, 0, 0); - wprintw(w, "Score\n%d\n", tg->points); - wprintw(w, "Level\n%d\n", tg->level); - wprintw(w, "Lines\n%d\n", tg->lines_remaining); - wnoutrefresh(w); -} - -/* - Boss mode! Make it look like you're doing work. - */ -void boss_mode(void) -{ - clear(); - //Mix_PauseMusic(); - printw("user@workstation-312:~/Documents/presentation $ ls -l\n" - "total 528\n" - "drwxr-xr-x 2 user users 4096 Jun 9 17:05 .\n" - "drwxr-xr-x 4 user users 4096 Jun 10 09:52 ..\n" - "-rw-r--r-- 1 user users 88583 Jun 9 14:13 figure1.png\n" - "-rw-r--r-- 1 user users 65357 Jun 9 15:40 figure2.png\n" - "-rw-r--r-- 1 user users 4469 Jun 9 16:17 presentation.aux\n" - "-rw-r--r-- 1 user users 42858 Jun 9 16:17 presentation.log\n" - "-rw-r--r-- 1 user users 2516 Jun 9 16:17 presentation.nav\n" - "-rw-r--r-- 1 user users 183 Jun 9 16:17 presentation.out\n" - "-rw-r--r-- 1 user users 349607 Jun 9 16:17 presentation.pdf\n" - "-rw-r--r-- 1 user users 0 Jun 9 16:17 presentation.snm\n" - "-rw-r--r-- 1 user users 9284 Jun 9 17:05 presentation.tex\n" - "-rw-r--r-- 1 user users 229 Jun 9 16:17 presentation.toc\n" - "\n" - "user@workstation-312:~/Documents/presentation $ "); - echo(); - timeout(-1); - while (getch() != KEY_F(1)); - timeout(0); - noecho(); - clear(); - //Mix_ResumeMusic(); -} - -/* - Save and exit the game. - */ -void save(tetris_game *game, WINDOW *w) -{ - FILE *f; - - wclear(w); - box(w, 0, 0); // return the border - wmove(w, 1, 1); - wprintw(w, "Save and exit? [Y/n] "); - wrefresh(w); - timeout(-1); - if (getch() == 'n') { - timeout(0); - return; - } - f = fopen("tetris.save", "w"); - tg_save(game, f); - fclose(f); - tg_delete(game); - endwin(); - printf("Game saved to \"tetris.save\".\n"); - printf("Resume by passing the filename as an argument to this program.\n"); - exit(EXIT_SUCCESS); -} - -/* - Do the NCURSES initialization steps for color blocks. - */ -void init_colors(void) -{ - start_color(); - //init_color(COLOR_ORANGE, 1000, 647, 0); - init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK); - init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK); - init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK); - init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK); - init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK); - init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK); - init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); -} - -/* - Main tetris game! - */ -#ifdef STANDALONE - -int main(int argc, char **argv) -{ - tetris_game *tg; - tetris_move move = TM_NONE; - bool running = true; - WINDOW *board, *next, *hold, *score; - //Mix_Music *music; - - // Load file if given a filename. - if (argc >= 2) { - FILE *f = fopen(argv[1], "r"); - if (f == NULL) { - perror("tetris"); - exit(EXIT_FAILURE); - } - tg = tg_load(f); - fclose(f); - } else { - // Otherwise create new game. - tg = tg_create(22, 10); - } - - /* Initialize music. - if (SDL_Init(SDL_INIT_AUDIO) < 0) { - fprintf(stderr, "unable to initialize SDL\n"); - exit(EXIT_FAILURE); - } - if (Mix_Init(MIX_INIT_MP3) != MIX_INIT_MP3) { - fprintf(stderr, "unable to initialize SDL_mixer\n"); - exit(EXIT_FAILURE); - } - if (Mix_OpenAudio(MIX_DEFAULT_FREQUENCY, MIX_DEFAULT_FORMAT, 2, 1024) != 0) { - fprintf(stderr, "unable to initialize audio\n"); - exit(EXIT_FAILURE); - } - Mix_AllocateChannels(1); // only need background music - music = Mix_LoadMUS("tetris.mp3"); - if (music) { - Mix_PlayMusic(music, -1); - }*/ - - // NCURSES initialization: - initscr(); // initialize curses - cbreak(); // pass key presses to program, but not signals - noecho(); // don't echo key presses to screen - keypad(stdscr, TRUE); // allow arrow keys - timeout(0); // no blocking on getch() - curs_set(0); // set the cursor to invisible - init_colors(); // setup tetris colors - - // Create windows for each section of the interface. - board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); - next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); - hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); - score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); - int32_t counter = 0; - // Game loop - while (running) { - running = tg_tick(tg, move); - display_board(board, tg); - display_piece(next, tg->next); - display_piece(hold, tg->stored); - display_score(score, tg); - if ( (counter++ % 5) == 0 ) - doupdate(); - sleep_milli(10); - - switch (getch()) { - case KEY_LEFT: - move = TM_LEFT; - break; - case KEY_RIGHT: - move = TM_RIGHT; - break; - case KEY_UP: - move = TM_CLOCK; - break; - case KEY_DOWN: - move = TM_DROP; - break; - case 'q': - running = false; - move = TM_NONE; - break; - case 'p': - wclear(board); - box(board, 0, 0); - wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); - wprintw(board, "PAUSED"); - wrefresh(board); - timeout(-1); - getch(); - timeout(0); - move = TM_NONE; - break; - case 'b': - boss_mode(); - move = TM_NONE; - break; - case 's': - save(tg, board); - move = TM_NONE; - break; - case ' ': - move = TM_HOLD; - break; - default: - move = TM_NONE; - } - } - - // Deinitialize NCurses - wclear(stdscr); - endwin(); - - /* Deinitialize Sound - Mix_HaltMusic(); - Mix_FreeMusic(music); - Mix_CloseAudio(); - Mix_Quit();*/ - - // Output ending message. - printf("Game over!\n"); - printf("You finished with %d points on level %d.\n", tg->points, tg->level); - - // Deinitialize Tetris - tg_delete(tg); - return 0; -} -#else - /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -949,973 +14,55 @@ int main(int argc, char **argv) * * ******************************************************************************/ -#include "cJSON.h" -#include "CCinclude.h" +// game specific code for daemon -#define TETRIS_REGISTRATION 5 -#define TETRIS_REGISTRATIONSIZE (100 * 10000) -#define TETRIS_MAXPLAYERS 64 // need to send unused fees back to globalCC address to prevent leeching -#define TETRIS_MAXKEYSTROKESGAP 60 -#define TETRIS_MAXITERATIONS 777 - - -std::string Tetris_pname = ""; - -CScript tetris_newgameopret(int64_t buyin,int32_t maxplayers) +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) { - CScript opret; uint8_t evalcode = EVAL_TETRIS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'G' << buyin << maxplayers); - return(opret); -} - -CScript tetris_registeropret(uint256 gametxid,uint256 playertxid) -{ - CScript opret; uint8_t evalcode = EVAL_TETRIS; - //fprintf(stderr,"opret.(%s %s).R\n",gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'R' << gametxid << playertxid); - return(opret); -} - -CScript tetris_keystrokesopret(uint256 gametxid,uint256 batontxid,CPubKey pk,std::vectorkeystrokes) -{ - CScript opret; uint8_t evalcode = EVAL_TETRIS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'K' << gametxid << batontxid << pk << keystrokes); - return(opret); -} - -CScript tetris_highlanderopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CPubKey pk,std::vectorplayerdata,std::string pname) -{ - CScript opret; uint8_t evalcode = EVAL_TETRIS; std::string symbol(ASSETCHAINS_SYMBOL); - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << gametxid << symbol << pname << regslot << pk << playerdata ); - return(opret); -} - -uint8_t tetris_highlanderopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t ®slot, CPubKey &pk, std::vector &playerdata, std::string &symbol, std::string &pname,CScript scriptPubKey) -{ - std::string name, description; std::vector vorigPubkey; - std::vector> oprets, opretsDummy; - std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; - uint8_t e, f,*script; std::vector voutPubkeys; - tokenid = zeroid; - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description, oprets)) == 'c' ) + uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) { - GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); - vopret = vopretNonfungible; - } - else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, opretsDummy)) != 0 ) - { - //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str()); - GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx - vopret = vopretNonfungible; - } - if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> symbol; ss >> pname; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_TETRIS && (f == 'H' || f == 'Q') ) - { - return(f); - } - fprintf(stderr,"SKIP obsolete: e.%d f.%c game.%s regslot.%d psize.%d\n",e,f,gametxid.GetHex().c_str(),regslot,(int32_t)playerdata.size()); - return(0); -} - -uint8_t tetris_keystrokesopretdecode(uint256 &gametxid,uint256 &batontxid,CPubKey &pk,std::vector &keystrokes,CScript scriptPubKey) -{ - std::vector vopret; uint8_t e,f; - GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> batontxid; ss >> pk; ss >> keystrokes) != 0 && e == EVAL_TETRIS && f == 'K' ) - { - return(f); - } - return(0); -} - -uint8_t tetris_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &playertxid,CScript scriptPubKey) -{ - std::string name, description; std::vector vorigPubkey; - std::vector> oprets; - std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; - uint8_t e, f,*script; std::vector voutPubkeys; - tokenid = zeroid; - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description,oprets)) == 'c' ) - { - GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); - vopret = vopretNonfungible; - } - else if ( script[1] != 'R' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, oprets)) != 0 ) - { - GetOpretBlob(oprets, OPRETID_TETRISGAMEDATA, vopretDummy); // blob from non-creation tx opret - vopret = vopretDummy; - } - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_TETRIS && f == 'R' ) - { - return(f); - } - //fprintf(stderr,"e.%d f.%c game.%s playertxid.%s\n",e,f,gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); - return(0); -} - -uint8_t tetris_newgameopreturndecode(int64_t &buyin,int32_t &maxplayers,CScript scriptPubKey) -{ - std::vector vopret; uint8_t e,f; - GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> buyin; ss >> maxplayers) != 0 && e == EVAL_TETRIS && f == 'G' ) - { - return(f); - } - return(0); -} - -void tetris_univalue(UniValue &result,const char *method,int64_t maxplayers,int64_t buyin) -{ - if ( method != 0 ) - { - result.push_back(Pair("name","tetris")); - result.push_back(Pair("method",method)); - } - if ( maxplayers > 0 ) - result.push_back(Pair("maxplayers",maxplayers)); - if ( buyin >= 0 ) - { - result.push_back(Pair("buyin",ValueFromAmount(buyin))); - if ( buyin == 0 ) - result.push_back(Pair("type","newbie")); - else result.push_back(Pair("type","buyin")); - } -} - -int32_t tetris_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx,char *mytetrisaddr) -{ - int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; char destaddr[64]; - for (i=0; i= 0 ) - { - if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) - { - Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); - if ( strcmp(mytetrisaddr,destaddr) == 0 ) - return(1); - //else fprintf(stderr,"myaddr.%s vs %s\n",mytetrisaddr,destaddr); - } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); - } //else fprintf(stderr,"vout %d is unspent\n",vout); - } - return(0); -} - -int32_t tetris_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0) -{ - uint256 hashBlock; int32_t i,numvouts; char coinaddr[64]; CPubKey tetrispk; uint64_t txfee = 10000; - buyin = maxplayers = 0; - if ( (txid == zeroid || myGetTransaction(txid,tx,hashBlock) != 0) && (numvouts= tx.vout.size()) > 1 ) - { - if ( txid != zeroid ) - gameheight = komodo_blockheight(hashBlock); - else - { - txid = tx.GetHash(); - //fprintf(stderr,"set txid %s %llu\n",txid.GetHex().c_str(),(long long)CCgettxout(txid,0,1)); - } - if ( IsCClibvout(cp,tx,0,cp->unspendableCCaddr) == txfee && (unspentv0 == 0 || CCgettxout(txid,0,1,0) == txfee) ) - { - if ( tetris_newgameopreturndecode(buyin,maxplayers,tx.vout[numvouts-1].scriptPubKey) == 'G' ) - { - if ( maxplayers < 1 || maxplayers > TETRIS_MAXPLAYERS || buyin < 0 ) - return(-6); - if ( numvouts > 2*maxplayers+1 ) - { - for (i=0; i playerdata,uint256 playertxid,uint256 tokenid,std::string symbol,std::string pname,uint256 gametxid) +int64_t games_cashout(struct games_player *P) { - int32_t i,vout,spentvini,numvouts,n=0; uint256 txid,spenttxid,hashBlock; struct tetris_player P; char packitemstr[512],*datastr=0; UniValue obj(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx; - memset(&P,0,sizeof(P)); - if ( playerdata.size() > 0 ) - { - datastr = (char *)malloc(playerdata.size()*2+1); - for (i=0; i= 0 ) - txid = spenttxid; - else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) - { - fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); - break; - } - txid = spenttxid; - vout = 0; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) - { - for (i=0; i TETRIS_MAXITERATIONS ) - break; - } - obj.push_back(Pair("gametxid",gametxid.GetHex())); - if ( txid != playertxid ) - obj.push_back(Pair("batontxid",txid.GetHex())); - obj.push_back(Pair("playertxid",playertxid.GetHex())); - if ( tokenid != zeroid ) - obj.push_back(Pair("tokenid",tokenid.GetHex())); - else obj.push_back(Pair("tokenid",playertxid.GetHex())); - if ( datastr != 0 ) - { - obj.push_back(Pair("data",datastr)); - free(datastr); - } - obj.push_back(Pair("pack",a)); - obj.push_back(Pair("packsize",(int64_t)P.packsize)); - obj.push_back(Pair("hitpoints",(int64_t)P.hitpoints)); - obj.push_back(Pair("strength",(int64_t)(P.strength&0xffff))); - obj.push_back(Pair("maxstrength",(int64_t)(P.strength>>16))); - obj.push_back(Pair("level",(int64_t)P.level)); - obj.push_back(Pair("experience",(int64_t)P.experience)); - obj.push_back(Pair("dungeonlevel",(int64_t)P.dungeonlevel)); - obj.push_back(Pair("chain",symbol)); - obj.push_back(Pair("pname",pname)); - return(obj); + int32_t dungeonlevel; int64_t mult=10,cashout = 0; + if ( P->amulet != 0 ) + mult *= 5; + dungeonlevel = P->dungeonlevel; + if ( P->amulet != 0 && dungeonlevel < 26 ) + dungeonlevel = 26; + cashout = (uint64_t)P->gold * P->gold * mult * dungeonlevel; + return(cashout); } -int32_t tetris_iterateplayer(uint256 ®istertxid,uint256 firsttxid,int32_t firstvout,uint256 lasttxid) // retrace playertxid vins to reach highlander <- this verifies player is valid and tetris_playerdataspend makes sure it can only be used once -{ - uint256 spenttxid,txid = firsttxid; int32_t spentvini,n,vout = firstvout; - registertxid = zeroid; - if ( vout < 0 ) - return(-1); - n = 0; - while ( (spentvini= myIsutxo_spent(spenttxid,txid,vout)) == 0 ) - { - txid = spenttxid; - vout = spentvini; - if ( registertxid == zeroid ) - registertxid = txid; - if ( ++n >= TETRIS_MAXITERATIONS ) - { - fprintf(stderr,"tetris_iterateplayer n.%d, seems something is wrong\n",n); - return(-2); - } - } - if ( txid == lasttxid ) - return(0); - else - { - fprintf(stderr,"firsttxid.%s/v%d -> %s != last.%s\n",firsttxid.ToString().c_str(),firstvout,txid.ToString().c_str(),lasttxid.ToString().c_str()); - return(-1); - } -} - -/* - playertxid is whoever owns the nonfungible satoshi and it might have been bought and sold many times. - highlander is the game winning tx with the player data and is the only place where the unique player data exists - origplayergame is the gametxid that ends up being won by the highlander and they are linked directly as the highlander tx spends gametxid.vout0 - */ - -int32_t tetris_playerdata(struct CCcontract_info *cp,uint256 &origplayergame,uint256 &tokenid,CPubKey &pk,std::vector &playerdata,std::string &symbol,std::string &pname,uint256 playertxid) -{ - uint256 origplayertxid,hashBlock,gametxid,registertxid; CTransaction gametx,playertx,highlandertx; std::vector vopret; uint8_t *script,e,f; int32_t i,regslot,gameheight,numvouts,maxplayers; int64_t buyin; - if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 && (numvouts= playertx.vout.size()) > 0 ) - { - if ( (f= tetris_highlanderopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,playertx.vout[numvouts-1].scriptPubKey)) == 'H' || f == 'Q' ) - { - origplayergame = gametxid; - if ( tokenid != zeroid ) - { - playertxid = tokenid; - if ( myGetTransaction(playertxid,playertx,hashBlock) == 0 || (numvouts= playertx.vout.size()) <= 0 ) - { - fprintf(stderr,"couldnt get tokenid.%s\n",playertxid.GetHex().c_str()); - return(-2); - } - } - if ( tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) - { - //fprintf(stderr,"playertxid.%s got vin.%s/v%d gametxid.%s iterate.%d\n",playertxid.ToString().c_str(),playertx.vin[1].prevout.hash.ToString().c_str(),(int32_t)playertx.vin[1].prevout.n-maxplayers,gametxid.ToString().c_str(),tetris_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid)); - if ( (tokenid != zeroid || playertx.vin[1].prevout.hash == gametxid) && tetris_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid) == 0 ) - { - // if registertxid has vin from pk, it can be used - return(0); - } else fprintf(stderr,"hash mismatch or illegal gametxid\n"); - } else fprintf(stderr,"invalid game %s\n",gametxid.GetHex().c_str()); - } //else fprintf(stderr,"invalid player funcid.%c\n",f); - } else fprintf(stderr,"couldnt get playertxid.%s\n",playertxid.GetHex().c_str()); - return(-1); -} - -int32_t tetris_playerdataspend(CMutableTransaction &mtx,uint256 playertxid,int32_t vout,uint256 origplayergame) -{ - int64_t txfee = 10000; CTransaction tx; uint256 hashBlock; - if ( CCgettxout(playertxid,vout,1,0) == 1 ) // not sure if this is enough validation - { - mtx.vin.push_back(CTxIn(playertxid,vout,CScript())); - return(0); - } - else - { - vout = 0; - if ( myGetTransaction(playertxid,tx,hashBlock) != 0 && tx.vout[vout].nValue == 1 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - if ( CCgettxout(playertxid,vout,1,0) == 1 ) // not sure if this is enough validation - { - mtx.vin.push_back(CTxIn(playertxid,vout,CScript())); - return(0); - } - } - return(-1); - } -} - -int32_t tetris_findbaton(struct CCcontract_info *cp,uint256 &playertxid,char **keystrokesp,int32_t &numkeys,int32_t ®slot,std::vector &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname) -{ - int32_t i,numvouts,spentvini,n,matches = 0; CPubKey pk; uint256 tid,active,spenttxid,tokenid,hashBlock,txid,origplayergame; CTransaction spenttx,matchtx,batontx; std::vector checkdata; CBlockIndex *pindex; char ccaddr[64],*keystrokes=0; - batonvalue = numkeys = numplayers = batonht = 0; - playertxid = batontxid = zeroid; - for (i=0; i= 0 ) - { - if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) - { - numplayers++; - Getscriptaddress(ccaddr,spenttx.vout[0].scriptPubKey); - if ( strcmp(destaddr,ccaddr) == 0 ) - { - matches++; - regslot = i; - matchtx = spenttx; - } //else fprintf(stderr,"%d+1 doesnt match %s vs %s\n",i,ccaddr,destaddr); - } //else fprintf(stderr,"%d+1 couldnt find spenttx.%s\n",i,spenttxid.GetHex().c_str()); - } //else fprintf(stderr,"%d+1 unspent\n",i); - } - if ( matches == 1 ) - { - numvouts = matchtx.vout.size(); - //fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts); - if ( tetris_registeropretdecode(txid,tokenid,playertxid,matchtx.vout[numvouts-1].scriptPubKey) == 'R' )//&& txid == gametxid ) - { - //fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); - if ( tokenid != zeroid ) - active = tokenid; - else active = playertxid; - if ( active == zeroid || tetris_playerdata(cp,origplayergame,tid,pk,playerdata,symbol,pname,active) == 0 ) - { - txid = matchtx.GetHash(); - //fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str()); - n = 0; - while ( CCgettxout(txid,0,1,0) < 0 ) - { - spenttxid = zeroid; - spentvini = -1; - if ( (spentvini= myIsutxo_spent(spenttxid,txid,0)) >= 0 ) - txid = spenttxid; - else - { - if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,0) == 0 || spenttxid == zeroid ) - { - fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); - return(-2); - } - } - txid = spenttxid; - //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); - if ( spentvini != 0 ) // game is over? - { - return(0); - } - if ( keystrokesp != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 ) - { - uint256 g,b; CPubKey p; std::vector k; - if ( tetris_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' ) - { - keystrokes = (char *)realloc(keystrokes,numkeys + (int32_t)k.size()); - for (i=0; i= TETRIS_MAXITERATIONS ) - { - fprintf(stderr,"tetris_findbaton n.%d, seems something is wrong\n",n); - return(-5); - } - } - //fprintf(stderr,"set baton %s\n",txid.GetHex().c_str()); - batontxid = txid; - batonvout = 0; // not vini - // how to detect timeout, bailedout, highlander - hashBlock = zeroid; - if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 0 ) - { - if ( hashBlock == zeroid ) - batonht = komodo_nextheight(); - else if ( (pindex= komodo_blockindex(hashBlock)) == 0 ) - return(-4); - else batonht = pindex->GetHeight(); - batonvalue = batontx.vout[0].nValue; - //printf("batonht.%d keystrokes[%d]\n",batonht,numkeys); - return(0); - } else fprintf(stderr,"couldnt find baton\n"); - } else fprintf(stderr,"error with playerdata\n"); - } else fprintf(stderr,"findbaton opret error\n"); - } - return(-1); -} - -int32_t tetris_playersalive(int32_t &openslots,int32_t &numplayers,uint256 gametxid,int32_t maxplayers,int32_t gameht,CTransaction gametx) -{ - int32_t i,n,vout,spentvini,registration_open = 0,alive = 0; CTransaction tx; uint256 txid,spenttxid,hashBlock; CBlockIndex *pindex; uint64_t txfee = 10000; - numplayers = openslots = 0; - if ( komodo_nextheight() <= gameht+TETRIS_MAXKEYSTROKESGAP ) - registration_open = 1; - for (i=0; i= 0 ) - txid = spenttxid; - else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) - { - fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); - break; - } - txid = spenttxid; - vout = 0; - //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); - if ( spentvini != 0 ) - break; - if ( n++ > TETRIS_MAXITERATIONS ) - break; - } - if ( txid != zeroid ) - { - if ( myGetTransaction(txid,tx,hashBlock) != 0 ) - { - if ( (pindex= komodo_blockindex(hashBlock)) != 0 ) - { - if ( pindex->GetHeight() <= gameht+TETRIS_MAXKEYSTROKESGAP ) - alive++; - } - } - } - } - } - else if ( registration_open != 0 ) - openslots++; - } - //fprintf(stderr,"numalive.%d openslots.%d\n",alive,openslots); - return(alive); -} - -uint64_t tetris_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mytetrisaddr) -{ - CBlockIndex *pindex; int32_t ht,openslots,delay,numplayers; uint256 hashBlock; uint64_t seed=0; char cmd[512]; CTransaction tx; - if ( myGetTransaction(gametxid,tx,hashBlock) != 0 && (pindex= komodo_blockindex(hashBlock)) != 0 ) - { - ht = pindex->GetHeight(); - delay = TETRIS_REGISTRATION * (maxplayers > 1); - obj.push_back(Pair("height",ht)); - obj.push_back(Pair("start",ht+delay)); - if ( komodo_nextheight() > ht+delay ) - { - if ( (pindex= komodo_chainactive(ht+delay)) != 0 ) - { - hashBlock = pindex->GetBlockHash(); - obj.push_back(Pair("starthash",hashBlock.ToString())); - memcpy(&seed,&hashBlock,sizeof(seed)); - seed &= (1LL << 62) - 1; - obj.push_back(Pair("seed",(int64_t)seed)); - if ( tetris_iamregistered(maxplayers,gametxid,tx,mytetrisaddr) > 0 ) - sprintf(cmd,"cc/tetris %llu %s",(long long)seed,gametxid.ToString().c_str()); - else sprintf(cmd,"./komodo-cli -ac_name=%s cclib register %d \"[%%22%s%%22]\"",ASSETCHAINS_SYMBOL,EVAL_TETRIS,gametxid.ToString().c_str()); - obj.push_back(Pair("run",cmd)); - } - } - obj.push_back(Pair("alive",tetris_playersalive(openslots,numplayers,gametxid,maxplayers,ht,tx))); - obj.push_back(Pair("openslots",openslots)); - obj.push_back(Pair("numplayers",numplayers)); - } - obj.push_back(Pair("maxplayers",maxplayers)); - obj.push_back(Pair("buyin",ValueFromAmount(buyin))); - return(seed); -} - -void tetris_gameplayerinfo(struct CCcontract_info *cp,UniValue &obj,uint256 gametxid,CTransaction gametx,int32_t vout,int32_t maxplayers,char *mytetrisaddr) -{ - // identify if bailout or quit or timed out - uint256 batontxid,spenttxid,gtxid,ptxid,tokenid,hashBlock,playertxid; CTransaction spenttx,batontx; int32_t numplayers,regslot,numkeys,batonvout,batonht,retval; int64_t batonvalue; std::vector playerdata; char destaddr[64]; std::string symbol,pname; - destaddr[0] = 0; - if ( myIsutxo_spent(spenttxid,gametxid,vout) >= 0 ) - { - if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) - Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); - } - obj.push_back(Pair("slot",(int64_t)vout-1)); - if ( (retval= tetris_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,destaddr,numplayers,symbol,pname)) == 0 ) - { - if ( CCgettxout(gametxid,maxplayers+vout,1,0) == 10000 ) - { - if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 1 ) - { - if ( tetris_registeropretdecode(gtxid,tokenid,ptxid,batontx.vout[batontx.vout.size()-1].scriptPubKey) == 'R' && ptxid == playertxid && gtxid == gametxid ) - obj.push_back(Pair("status","registered")); - else obj.push_back(Pair("status","alive")); - } else obj.push_back(Pair("status","error")); - } else obj.push_back(Pair("status","finished")); - obj.push_back(Pair("baton",batontxid.ToString())); - obj.push_back(Pair("tokenid",tokenid.ToString())); - obj.push_back(Pair("batonaddr",destaddr)); - obj.push_back(Pair("ismine",strcmp(mytetrisaddr,destaddr)==0)); - obj.push_back(Pair("batonvout",(int64_t)batonvout)); - obj.push_back(Pair("batonvalue",ValueFromAmount(batonvalue))); - obj.push_back(Pair("batonht",(int64_t)batonht)); - if ( playerdata.size() > 0 ) - obj.push_back(Pair("player",tetris_playerobj(playerdata,playertxid,tokenid,symbol,pname,gametxid))); - } else fprintf(stderr,"findbaton err.%d\n",retval); -} - -int64_t tetris_registrationbaton(CMutableTransaction &mtx,uint256 gametxid,CTransaction gametx,int32_t maxplayers) -{ - int32_t vout,j,r; int64_t nValue; - if ( gametx.vout.size() > maxplayers+1 ) - { - r = rand() % maxplayers; - for (j=0; j 0 ) - { - result.push_back(Pair("hex",rawtx)); - if ( DecodeHexTx(tx,rawtx) != 0 ) - { - if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) - RelayTransaction(tx); - result.push_back(Pair("txid",tx.GetHash().ToString())); - result.push_back(Pair("result","success")); - } else result.push_back(Pair("error","decode hex")); - } else result.push_back(Pair("error","couldnt finalize CCtx")); - return(result); -} - -UniValue tetris_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey tetrispk,mypk; char *jsonstr; uint64_t inputsum,change,required,buyin=0; int32_t i,n,maxplayers = 1; - if ( txfee == 0 ) - txfee = 10000; - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - maxplayers = juint(jitem(params,0),0); - if ( n > 1 ) - buyin = jdouble(jitem(params,1),0) * COIN + 0.0000000049; - } - } - if ( maxplayers < 1 || maxplayers > TETRIS_MAXPLAYERS ) - return(cclib_error(result,"illegal maxplayers")); - mypk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - tetris_univalue(result,"newgame",maxplayers,buyin); - required = (3*txfee + maxplayers*(TETRIS_REGISTRATIONSIZE+txfee)); - if ( (inputsum= AddCClibInputs(cp,mtx,tetrispk,required,16,cp->unspendableCCaddr)) >= required ) - { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,tetrispk)); // for highlander TCBOO creation - for (i=0; ievalcode,TETRIS_REGISTRATIONSIZE,tetrispk,tetrispk)); - for (i=0; ievalcode,txfee,tetrispk,tetrispk)); - if ( (change= inputsum - required) >= txfee ) - mtx.vout.push_back(MakeCC1vout(cp->evalcode,change,tetrispk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,tetris_newgameopret(buyin,maxplayers)); - return(tetris_rawtxresult(result,rawtx,1)); - } - else return(cclib_error(result,"illegal maxplayers")); - return(result); -} - -UniValue tetris_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); std::vector playerdata; uint256 playertxid,tokenid,origplayergame;int32_t n; CPubKey pk; bits256 t; std::string symbol,pname; - result.push_back(Pair("result","success")); - tetris_univalue(result,"playerinfo",-1,-1); - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - playertxid = juint256(jitem(params,0)); - if ( tetris_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) - return(cclib_error(result,"invalid playerdata")); - result.push_back(Pair("player",tetris_playerobj(playerdata,playertxid,tokenid,symbol,pname,origplayergame))); - } else return(cclib_error(result,"no playertxid")); - return(result); - } else return(cclib_error(result,"couldnt reparse params")); -} - -UniValue tetris_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - // vin0 -> TETRIS_REGISTRATIONSIZE 1of2 registration baton from creategame - // vin1 -> optional nonfungible character vout @ - // vin2 -> original creation TCBOO playerdata used - // vin3+ -> buyin - // vout0 -> keystrokes/completion baton - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); char destaddr[64],coinaddr[64]; uint256 tokenid,gametxid,origplayergame,playertxid,hashBlock; int32_t err,maxplayers,gameheight,n,numvouts,vout=1; int64_t inputsum,buyin,CCchange=0; CPubKey pk,mypk,tetrispk,burnpk; CTransaction tx,playertx; std::vector playerdata; std::string rawtx,symbol,pname; bits256 t; - - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); - tetrispk = GetUnspendable(cp,0); - tetris_univalue(result,"register",-1,-1); - playertxid = tokenid = zeroid; - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - gametxid = juint256(jitem(params,0)); - if ( (err= tetris_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1)) == 0 ) - { - if ( n > 1 ) - { - playertxid = juint256(jitem(params,1)); - if ( tetris_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) - return(cclib_error(result,"couldnt extract valid playerdata")); - if ( tokenid != zeroid ) // if it is tokentransfer this will be 0 - vout = 1; - } - if ( komodo_nextheight() > gameheight + TETRIS_MAXKEYSTROKESGAP ) - return(cclib_error(result,"didnt register in time, TETRIS_MAXKEYSTROKESGAP")); - tetris_univalue(result,0,maxplayers,buyin); - GetCCaddress1of2(cp,coinaddr,tetrispk,mypk); - if ( tetris_iamregistered(maxplayers,gametxid,tx,coinaddr) > 0 ) - return(cclib_error(result,"already registered")); - if ( (inputsum= tetris_registrationbaton(mtx,gametxid,tx,maxplayers)) != TETRIS_REGISTRATIONSIZE ) - return(cclib_error(result,"couldnt find available registration baton")); - else if ( playertxid != zeroid && tetris_playerdataspend(mtx,playertxid,vout,origplayergame) < 0 ) - return(cclib_error(result,"couldnt find playerdata to spend")); - else if ( buyin > 0 && AddNormalinputs(mtx,mypk,buyin,64) < buyin ) - return(cclib_error(result,"couldnt find enough normal funds for buyin")); - if ( tokenid != zeroid ) - { - mtx.vin.push_back(CTxIn(tokenid,0)); // spending cc marker as token is burned - char unspendableTokenAddr[64]; uint8_t tokenpriv[32]; struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - CPubKey unspPk = GetUnspendable(cpTokens, tokenpriv); - GetCCaddress(cpTokens, unspendableTokenAddr, unspPk); - CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); - } - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,buyin + inputsum - txfee,tetrispk,mypk)); - GetCCaddress1of2(cp,destaddr,tetrispk,tetrispk); - CCaddr1of2set(cp,tetrispk,tetrispk,cp->CCpriv,destaddr); - mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk)); - - uint8_t e, funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; - CScript opretRegister = tetris_registeropret(gametxid, playertxid); - if ( playertxid != zeroid ) - { - voutPubkeysEmpty.push_back(burnpk); - if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 ) - { - std::vector> oprets; - if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tid, voutPubkeys, oprets)) != 0) - { // if token in the opret - didtx = 1; - if ( funcid == 'c' ) - tid = tokenid == zeroid ? playertxid : tokenid; - vscript_t vopretRegister; - GetOpReturnData(opretRegister, vopretRegister); - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, - EncodeTokenOpRet(tid, voutPubkeysEmpty /*=never spent*/, std::make_pair(OPRETID_TETRISGAMEDATA, vopretRegister))); - } - } - } - if ( didtx == 0 ) - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opretRegister); - - return(tetris_rawtxresult(result,rawtx,1)); - } else return(cclib_error(result,"invalid gametxid")); - } else return(cclib_error(result,"no gametxid")); - } else return(cclib_error(result,"couldnt reparse params")); -} - -UniValue tetris_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - // vin0 -> baton from registration or previous keystrokes - // vout0 -> new baton - // opret -> user input chars - // being killed should auto broadcast (possible to be suppressed?) - // respawn to be prevented by including timestamps - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); - UniValue result(UniValue::VOBJ); CPubKey tetrispk,mypk; uint256 gametxid,playertxid,batontxid; int64_t batonvalue,buyin; std::vector keystrokes,playerdata; int32_t numplayers,regslot,numkeys,batonht,batonvout,n,elapsed,gameheight,maxplayers; CTransaction tx; CTxOut txout; char *keystrokestr,destaddr[64]; std::string rawtx,symbol,pname; bits256 t; uint8_t mypriv[32]; - if ( txfee == 0 ) - txfee = 10000; - tetris_univalue(result,"keystrokes",-1,-1); - if ( params != 0 && (n= cJSON_GetArraySize(params)) == 2 && (keystrokestr= jstr(jitem(params,1),0)) != 0 ) - { - gametxid = juint256(jitem(params,0)); - result.push_back(Pair("gametxid",gametxid.GetHex())); - result.push_back(Pair("keystrokes",keystrokestr)); - keystrokes = ParseHex(keystrokestr); - mypk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,destaddr,tetrispk,mypk); - if ( tetris_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1) == 0 ) - { - if ( tetris_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,tx,maxplayers,destaddr,numplayers,symbol,pname) == 0 ) - { - result.push_back(Pair("batontxid",batontxid.GetHex())); - result.push_back(Pair("playertxid",playertxid.GetHex())); - if ( maxplayers == 1 || nextheight <= batonht+TETRIS_MAXKEYSTROKESGAP ) - { - mtx.vin.push_back(CTxIn(batontxid,batonvout,CScript())); //this validates user if pk - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,batonvalue-txfee,tetrispk,mypk)); - Myprivkey(mypriv); - CCaddr1of2set(cp,tetrispk,mypk,mypriv,destaddr); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,tetris_keystrokesopret(gametxid,batontxid,mypk,keystrokes)); - //fprintf(stderr,"KEYSTROKES.(%s)\n",rawtx.c_str()); - return(tetris_rawtxresult(result,rawtx,1)); - } else return(cclib_error(result,"keystrokes tx was too late")); - } else return(cclib_error(result,"couldnt find batontxid")); - } else return(cclib_error(result,"invalid gametxid")); - } else return(cclib_error(result,"couldnt reparse params")); -} - -char *tetris_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *tetrisaddr) -{ - CPubKey tetrispk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64],*keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct tetris_player P,endP; - tetrispk = GetUnspendable(cp,0); - *numkeysp = 0; - seed = 0; - num = numkeys = 0; - playertxid = zeroid; - str[0] = 0; - if ( (err= tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 ) - { - if ( (retval= tetris_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,tetrisaddr,numplayers,symbol,pname)) == 0 ) - { - UniValue obj; - seed = tetris_gamefields(obj,maxplayers,buyin,gametxid,tetrisaddr); - //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Tetris_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); - memset(&P,0,sizeof(P)); - if ( playerdata.size() > 0 ) - { - for (i=0; i no playerdata\n"); - newdata.resize(0); - *numkeysp = numkeys; - return(keystrokes); - /* P.gold = (P.gold * 8) / 10; - if ( keystrokes != 0 ) - { - free(keystrokes); - keystrokes = 0; - *numkeysp = 0; - return(keystrokes); - }*/ - } - else - { - sprintf(str,"$$$gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",endP.gold,endP.hitpoints,endP.strength&0xffff,endP.strength>>16,endP.level,endP.experience,endP.dungeonlevel); - //fprintf(stderr,"%s\n",str); - *numkeysp = numkeys; - return(keystrokes); - } - } else num = 0; - } - else - { - fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval); - if ( keystrokes != 0 ) - free(keystrokes), keystrokes = 0; - } - } else fprintf(stderr,"extractgame: invalid game\n"); - //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str()); - return(0); -} - -UniValue tetris_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); CPubKey pk,tetrispk; int32_t i,n,numkeys,flag = 0; uint64_t seed; char str[512],tetrisaddr[64],*pubstr,*hexstr,*keystrokes = 0; std::vector newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33]; - pk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - result.push_back(Pair("name","tetris")); - result.push_back(Pair("method","extract")); - tetrisaddr[0] = 0; - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - gametxid = juint256(jitem(params,0)); - result.push_back(Pair("gametxid",gametxid.GetHex())); - if ( n == 2 ) - { - if ( (pubstr= jstr(jitem(params,1),0)) != 0 ) - { - if (strlen(pubstr) == 66 ) - { - decode_hex(pub33,33,pubstr); - pk = buf2pk(pub33); - } - else if ( strlen(pubstr) < 36 ) - strcpy(tetrisaddr,pubstr); - } - //fprintf(stderr,"gametxid.%s %s\n",gametxid.GetHex().c_str(),pubstr); - } - if ( tetrisaddr[0] == 0 ) - GetCCaddress1of2(cp,tetrisaddr,tetrispk,pk); - result.push_back(Pair("tetrisaddr",tetrisaddr)); - str[0] = 0; - if ( (keystrokes= tetris_extractgame(1,str,&numkeys,newdata,seed,playertxid,cp,gametxid,tetrisaddr)) != 0 ) - { - result.push_back(Pair("status","success")); - flag = 1; - hexstr = (char *)malloc(numkeys*2 + 1); - for (i=0; i playerdata,uint256 gametxid,CPubKey pk) +int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) { static uint32_t good,bad; static uint256 prevgame; - char str[512],*keystrokes,tetrisaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed,mult = 10; CPubKey tetrispk; struct tetris_player P; + char str[512],*keystrokes,gamesaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; *cashoutp = 0; - tetrispk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,tetrisaddr,tetrispk,pk); - //fprintf(stderr,"call extractgame\n"); - if ( (keystrokes= tetris_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,tetrisaddr)) != 0 ) + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,gamesaddr,gamespk,pk); + if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) { - //fprintf(stderr,"numkeys.%d tetris_extractgame %s\n",numkeys,gametxid.GetHex().c_str()); free(keystrokes); - sprintf(fname,"tetris.%llu.pack",(long long)seed); + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); remove(fname); - - //fprintf(stderr,"extracted.(%s)\n",str); - for (i=0; i>16,P.level,P.experience,P.dungeonlevel); - fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,tetrisaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); + fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,gamesaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); } } - sprintf(fname,"tetris.%llu.pack",(long long)seed); + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); remove(fname); - //fprintf(stderr,"no keys tetris_extractgame %s\n",gametxid.GetHex().c_str()); + //fprintf(stderr,"no keys games_extractgame %s\n",gametxid.GetHex().c_str()); return(-1); } -UniValue tetris_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params,char *method) +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) // replay in daemon { - //vin0 -> highlander vout from creategame TCBOO - //vin1 -> keystrokes baton of completed game, must be last to quit or first to win, only spent registration batons matter. If more than 60 blocks since last keystrokes, it is forfeit - //vins2+ -> rest of unspent registration utxo so all newgame vouts are spent - //vout0 -> nonfungible character with pack @ - //vout1 -> 1% ingame gold and all the buyins - - // detect if last to bailout - // vin0 -> kestrokes baton of completed game with Q - // vout0 -> playerdata marker - // vout0 -> 1% ingame gold - // get any playerdata, get all keystrokes, replay game and compare final state - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed,mult; int64_t buyin,batonvalue,inputsum,cashout,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,dungeonlevel,numkeys,maxplayers,batonht,batonvout; char mytetrisaddr[64],*keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,tetrispk; uint8_t player[10000],mypriv[32],funcid; - struct CCcontract_info *cpTokens, tokensC; - - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,mytetrisaddr,tetrispk,mypk); - result.push_back(Pair("name","tetris")); - result.push_back(Pair("method",method)); - result.push_back(Pair("mytetrisaddr",mytetrisaddr)); - if ( strcmp(method,"bailout") == 0 ) - { - funcid = 'Q'; - mult = 10; //100000; - } - else - { - funcid = 'H'; - mult = 20; //200000; - } - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - gametxid = juint256(jitem(params,0)); - result.push_back(Pair("gametxid",gametxid.GetHex())); - if ( (err= tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,1)) == 0 ) - { - if ( tetris_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,mytetrisaddr,numplayers,symbol,pname) == 0 ) - { - UniValue obj; struct tetris_player P; - seed = tetris_gamefields(obj,maxplayers,buyin,gametxid,mytetrisaddr); - fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d\n",pname.size()!=0?pname.c_str():Tetris_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size()); - memset(&P,0,sizeof(P)); - if ( playerdata.size() > 0 ) - { - for (i=0; i 0 ) - { - newdata.resize(num); - for (i=0; i no playerdata\n"); - newdata.resize(0); - //P.gold = (P.gold * 8) / 10; - } - else - { - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens,NULL))); // marker to token cc addr, burnable and validated - mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,1,mypk)); - if ( P.amulet != 0 ) - mult *= 5; - dungeonlevel = P.dungeonlevel; - if ( P.amulet != 0 && dungeonlevel < 21 ) - dungeonlevel = 21; - cashout = (uint64_t)P.gold * P.gold * mult * dungeonlevel; - fprintf(stderr,"\nextracted $$$gold.%d -> %.8f TETRIS hp.%d strength.%d/%d level.%d exp.%d dl.%d n.%d amulet.%d\n",P.gold,(double)cashout/COIN,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel,n,P.amulet); - if ( funcid == 'H' && maxplayers > 1 ) - { - if ( P.amulet == 0 ) - { - if ( numplayers != maxplayers ) - return(cclib_error(result,"numplayers != maxplayers")); - else if ( tetris_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 ) - return(cclib_error(result,"highlander must be a winner or last one standing")); - } - cashout += numplayers * buyin; - } - if ( cashout >= txfee ) - { - if ( (inputsum= AddCClibInputs(cp,mtx,tetrispk,cashout,16,cp->unspendableCCaddr)) > (uint64_t)P.gold*mult ) - CCchange = (inputsum - cashout); - mtx.vout.push_back(CTxOut(cashout,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - } - } - } - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-3*txfee),tetrispk)); - Myprivkey(mypriv); - CCaddr1of2set(cp,tetrispk,mypk,mypriv,mytetrisaddr); - CScript opret; - if ( pname.size() == 0 ) - pname = Tetris_pname; - if ( newdata.size() == 0 ) - { - opret = tetris_highlanderopret(funcid, gametxid, regslot, mypk, nodata,pname); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,opret); - //fprintf(stderr,"nodata finalizetx.(%s)\n",rawtx.c_str()); - } - else - { - opret = tetris_highlanderopret(funcid, gametxid, regslot, mypk, newdata,pname); - char seedstr[32]; - sprintf(seedstr,"%llu",(long long)seed); - std::vector vopretNonfungible; - GetOpReturnData(opret, vopretNonfungible); - rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), std::string(seedstr), gametxid.GetHex(), vopretNonfungible)); - } - return(tetris_rawtxresult(result,rawtx,1)); - } - result.push_back(Pair("result","success")); - } else fprintf(stderr,"illegal game err.%d\n",err); - } else fprintf(stderr,"parameters only n.%d\n",n); - } - return(result); + return(-1); } -UniValue tetris_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +void games_packitemstr(char *packitemstr,struct games_packitem *item) { - return(tetris_finishgame(txfee,cp,params,"bailout")); + sprintf(packitemstr,"not yet"); } -UniValue tetris_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) { - return(tetris_finishgame(txfee,cp,params,"highlander")); -} - -UniValue tetris_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,gameheight,maxplayers,numvouts; uint256 txid; CTransaction tx; int64_t buyin; uint64_t seed; bits256 t; char mytetrisaddr[64],str[64]; CPubKey mypk,tetrispk; - result.push_back(Pair("name","tetris")); - result.push_back(Pair("method","gameinfo")); - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - txid = juint256(jitem(params,0)); - result.push_back(Pair("gametxid",txid.GetHex())); - if ( tetris_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,0) == 0 ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("gameheight",(int64_t)gameheight)); - mypk = pubkey2pk(Mypubkey()); - tetrispk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,mytetrisaddr,tetrispk,mypk); - //fprintf(stderr,"mytetrisaddr.%s\n",mytetrisaddr); - seed = tetris_gamefields(result,maxplayers,buyin,txid,mytetrisaddr); - result.push_back(Pair("seed",(int64_t)seed)); - for (i=0; i > unspentOutputs; - tetrispk = GetUnspendable(cp,0); - GetCCaddress(cp,coinaddr,tetrispk); - SetCCunspents(unspentOutputs,coinaddr); - nextheight = komodo_nextheight(); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); - if ( it->second.satoshis != txfee || vout != 0 ) // reject any that are not highlander markers - continue; - if ( tetris_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,1) == 0 && nextheight <= gameheight+TETRIS_MAXKEYSTROKESGAP ) - { - tetris_playersalive(openslots,numplayers,txid,maxplayers,gameheight,tx); - if ( openslots > 0 ) - a.push_back(txid.GetHex()); - } - } - result.push_back(Pair("result","success")); - tetris_univalue(result,"pending",-1,-1); - result.push_back(Pair("pending",a)); - result.push_back(Pair("numpending",(int64_t)a.size())); - return(result); -} - -UniValue tetris_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 tokenid,gametxid,txid,hashBlock; CTransaction playertx,tx; int32_t maxplayers,vout,numvouts; std::vector playerdata; CPubKey tetrispk,mypk,pk; std::string symbol,pname; char coinaddr[64]; - std::vector > unspentOutputs; - tetrispk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - GetTokensCCaddress(cp,coinaddr,mypk); - SetCCunspents(unspentOutputs,coinaddr); - tetris_univalue(result,"players",-1,-1); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); - if ( it->second.satoshis != 1 || vout > 1 ) - continue; - if ( tetris_playerdata(cp,gametxid,tokenid,pk,playerdata,symbol,pname,txid) == 0 )//&& pk == mypk ) - { - a.push_back(txid.GetHex()); - //a.push_back(Pair("playerdata",tetris_playerobj(playerdata))); - } - } - result.push_back(Pair("playerdata",a)); - result.push_back(Pair("numplayerdata",(int64_t)a.size())); - return(result); -} - -UniValue tetris_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); uint256 txid,hashBlock,gametxid,tokenid,playertxid; int32_t vout,maxplayers,gameheight,numvouts; CPubKey tetrispk,mypk; char coinaddr[64]; CTransaction tx,gametx; int64_t buyin; - std::vector > addressIndex; - //std::vector > unspentOutputs; - tetrispk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - GetCCaddress1of2(cp,coinaddr,tetrispk,mypk); - //SetCCunspents(unspentOutputs,coinaddr); - SetCCtxids(addressIndex,coinaddr); - tetris_univalue(result,"games",-1,-1); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - //for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); - if ( vout == 0 ) - { - if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) - { - if ( tetris_registeropretdecode(gametxid,tokenid,playertxid,tx.vout[numvouts-1].scriptPubKey) == 'R' ) - { - if ( tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) - { - if ( CCgettxout(txid,vout,1,0) < 0 ) - b.push_back(gametxid.GetHex()); - else a.push_back(gametxid.GetHex()); - } - } - } - } - } - result.push_back(Pair("pastgames",b)); - result.push_back(Pair("games",a)); - result.push_back(Pair("numgames",(int64_t)(a.size()+b.size()))); - return(result); -} - -UniValue tetris_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - UniValue result(UniValue::VOBJ); int32_t n; char *namestr = 0; - tetris_univalue(result,"setname",-1,-1); - if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) - { - if ( n > 0 ) - { - if ( (namestr= jstri(params,0)) != 0 ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("pname",namestr)); - tetris_pname = namestr; - return(result); - } - } - } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt get name")); - return(result); -} - -bool tetris_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) -{ - CScript scriptPubKey; std::vector vopret; uint8_t *script,e,f,funcid,tokentx=0; int32_t i,maxplayers,enabled = 0,decoded=0,regslot,ind,err,dispflag,gameheight,score,numvouts; CTransaction vintx,gametx; CPubKey pk; uint256 hashBlock,gametxid,txid,tokenid,batontxid,playertxid,ptxid; int64_t buyin,cashout; std::vector playerdata,keystrokes; std::string symbol,pname; - if ( strcmp(ASSETCHAINS_SYMBOL,"ROGUE") == 0 ) - { - if (height < 21274 ) - return(true); - else if ( height > 50000 ) - enabled = 1; - } else enabled = 1; - if ( (numvouts= tx.vout.size()) > 1 ) - { - txid = tx.GetHash(); - scriptPubKey = tx.vout[numvouts-1].scriptPubKey; - GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 ) - { - script = (uint8_t *)vopret.data(); - funcid = script[1]; - if ( (e= script[0]) == EVAL_TOKENS ) - { - tokentx = funcid; - if ( (funcid= tetris_highlanderopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,scriptPubKey)) == 0 ) - { - if ( (funcid= tetris_registeropretdecode(gametxid,tokenid,playertxid,scriptPubKey)) == 0 ) - { - fprintf(stderr,"ht.%d couldnt decode tokens opret (%c)\n",height,script[1]); - } else e = EVAL_TETRIS, decoded = 1; - } else e = EVAL_TETRIS, decoded = 1; - } - if ( e == EVAL_TETRIS ) - { - //fprintf(stderr,"ht.%d tetris.(%c)\n",height,script[1]); - if ( decoded == 0 ) - { - switch ( funcid ) - { - case 'G': // seems just need to make sure no vout abuse is left to do - gametx = tx; - gametxid = tx.GetHash(); - gameheight = height; - if ( (err= tetris_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,zeroid,0)) != 0 ) - { - fprintf(stderr,"height.%d %s tetris_isvalidgame error.%d\n",height,gametxid.GetHex().c_str(),err); - return eval->Invalid("invalid gametxid"); - } - //fprintf(stderr,"height.%d %s tetris_isvalidgame\n",height,gametxid.GetHex().c_str()); - return(true); - break; - case 'R': - if ( (funcid= tetris_registeropretdecode(gametxid,tokenid,playertxid,scriptPubKey)) != 'R' ) - { - return eval->Invalid("couldnt decode register opret"); - } - // baton is created - // validation is done below - break; - case 'K': - if ( (funcid= tetris_keystrokesopretdecode(gametxid,batontxid,pk,keystrokes,scriptPubKey)) != 'K' ) - { - return eval->Invalid("couldnt decode keystrokes opret"); - } - // spending the baton proves it is the user if the pk is the signer - return(true); - break; - case 'H': case 'Q': - // done in the next switch statement as there are some H/Q tx with playerdata which would skip this section - break; - default: - return eval->Invalid("illegal tetris non-decoded funcid"); - break; - } - } - switch ( funcid ) - { - case 'R': // register - // verify vout amounts are as they should be and no vins that shouldnt be - return(true); - case 'H': // win - case 'Q': // bailout - if ( (f= tetris_highlanderopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,scriptPubKey)) != funcid ) - { - //fprintf(stderr,"height.%d couldnt decode H/Q opret\n",height); - //if ( height > 20000 ) - return eval->Invalid("couldnt decode H/Q opret"); - } - // verify pk belongs to this tx - if ( tokentx == 'c' && playerdata.size() > 0 ) - { - static char laststr[512]; char cashstr[512]; - if ( tetris_playerdata_validate(&cashout,ptxid,cp,playerdata,gametxid,pk) < 0 ) - { - sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d gametxid.%s player.%s invalid playerdata[%d]\n",tokentx,decoded,height,gametxid.GetHex().c_str(),ptxid.GetHex().c_str(),(int32_t)playerdata.size()); - if ( strcmp(laststr,cashstr) != 0 ) - { - strcpy(laststr,cashstr); - fprintf(stderr,"%s\n",cashstr); - } - if ( enabled != 0 ) - return eval->Invalid("mismatched playerdata"); - } - if ( funcid == 'H' ) - cashout *= 2; - sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d txid.%s %.8f vs vout2 %.8f",tokentx,decoded,height,txid.GetHex().c_str(),(double)cashout/COIN,(double)tx.vout[2].nValue/COIN); - if ( strcmp(laststr,cashstr) != 0 ) - { - strcpy(laststr,cashstr); - fprintf(stderr,"%s\n",cashstr); - } - if ( enabled != 0 && tx.vout[2].nValue != cashout ) - return eval->Invalid("mismatched cashout amount"); - } - if ( funcid == 'Q' ) - { - // verify vin/vout - } - else // 'H' - { - // verify vin/vout and proper payouts - } - return(true); - break; - default: - return eval->Invalid("illegal tetris funcid"); - break; - } - } else return eval->Invalid("illegal evalcode"); - } else return eval->Invalid("opret too small"); - } else return eval->Invalid("not enough vouts"); return(true); } -#endif diff --git a/src/cc/tetris.h b/src/cc/tetris.h new file mode 100644 index 000000000..f032bf2da --- /dev/null +++ b/src/cc/tetris.h @@ -0,0 +1,197 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#ifndef H_TETRIS_H +#define H_TETRIS_H + +#define GAMENAME "tetris" +#define GAMEMAIN tetris +#define CHAINNAME "GTEST" + +#define MAXPACK 23 +struct games_packitem +{ + int32_t type,launch,count,which,hplus,dplus,arm,flags,group; + char damage[8],hurldmg[8]; +}; + +struct games_player +{ + int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; + struct games_packitem gamespack[MAXPACK]; +}; + +struct games_state +{ + uint64_t seed; + char *keystrokes,*keystrokeshex; + uint32_t needflush,replaydone; + int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; + FILE *logfp; + struct games_player P; + char buffered[10000]; + uint8_t playerdata[10000]; +}; + + +/***************************************************************************/ +/** https://github.com/brenns10/tetris + @file main.c + @author Stephen Brennan + @date Created Wednesday, 10 June 2015 + @brief Main program for tetris. + @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised + BSD License. See LICENSE.txt for details. + *******************************************************************************/ + +/* + Convert a tetromino type to its corresponding cell. + */ +#define TYPE_TO_CELL(x) ((x)+1) + +/* + Strings for how you would print a tetris board. + */ +#define TC_EMPTY_STR " " +#define TC_BLOCK_STR "\u2588" + +/* + Questions about a tetris cell. + */ +#define TC_IS_EMPTY(x) ((x) == TC_EMPTY) +#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x)) + +/* + How many cells in a tetromino? + */ +#define TETRIS 4 +/* + How many tetrominos? + */ +#define NUM_TETROMINOS 7 +/* + How many orientations of a tetromino? + */ +#define NUM_ORIENTATIONS 4 + +/* + Level constants. + */ +#define MAX_LEVEL 19 +#define LINES_PER_LEVEL 10 + +/* + A "cell" is a 1x1 block within a tetris board. + */ +typedef enum { + TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ +} tetris_cell; + +/* + A "type" is a type/shape of a tetromino. Not including orientation. + */ +typedef enum { + TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z +} tetris_type; + +/* + A row,column pair. Negative numbers allowed, because we need them for + offsets. + */ +typedef struct { + int row; + int col; +} tetris_location; + +/* + A "block" is a struct that contains information about a tetromino. + Specifically, what type it is, what orientation it has, and where it is. + */ +typedef struct { + int typ; + int ori; + tetris_location loc; +} tetris_block; + +/* + All possible moves to give as input to the game. + */ +typedef enum { + TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE +} tetris_move; + +/* + A game object! + */ +typedef struct { + /* + Game board stuff: + */ + int rows; + int cols; + char *board; + /* + Scoring information: + */ + int points; + int level; + /* + Falling block is the one currently going down. Next block is the one that + will be falling after this one. Stored is the block that you can swap out. + */ + tetris_block falling; + tetris_block next; + tetris_block stored; + /* + Number of game ticks until the block will move down. + */ + int ticks_till_gravity; + /* + Number of lines until you advance to the next level. + */ + int lines_remaining; +} tetris_game; + +/* + This array stores all necessary information about the cells that are filled by + each tetromino. The first index is the type of the tetromino (i.e. shape, + e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final + array contains 4 tetris_location objects, each mapping to an offset from a + point on the upper left that is the tetromino "origin". + */ +extern tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS]; + +/* + This array tells you how many ticks per gravity by level. Decreases as level + increases, to add difficulty. + */ +extern int GRAVITY_LEVEL[MAX_LEVEL+1]; + +// Data structure manipulation. +void tg_init(tetris_game *obj, int rows, int cols); +tetris_game *tg_create(int rows, int cols); +void tg_destroy(tetris_game *obj); +void tg_delete(tetris_game *obj); +tetris_game *tg_load(FILE *f); +void tg_save(tetris_game *obj, FILE *f); + +// Public methods not related to memory: +char tg_get(tetris_game *obj, int row, int col); +bool tg_check(tetris_game *obj, int row, int col); +bool tg_tick(tetris_game *obj, tetris_move move); +void tg_print(tetris_game *obj, FILE *f); + +#endif + From c5b7efd7f8c25acf310619f15d9b203f7bde8a50 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:26:24 -1100 Subject: [PATCH 200/787] Optimize --- src/cc/gamescc.cpp | 114 ++++----------------------------------------- src/cc/tetris.c | 11 ++++- src/cc/tetris.cpp | 85 +++++++++++++++++++++++++++++++++ src/cc/tetris.h | 88 +++++++++++++++++----------------- 4 files changed, 148 insertions(+), 150 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 11ceb3345..869205acc 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -14,15 +14,18 @@ ******************************************************************************/ #include "gamescc.h" - -#include "tetris.h" - -static int random_tetromino(void) -{ - return rand() % NUM_TETROMINOS; -} +#include "tetris.c" // replace with game code #ifndef STANDALONE +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload); +int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk); +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); +void games_packitemstr(char *packitemstr,struct games_packitem *item); +int64_t games_cashout(struct games_player *P); +char *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr); + +#include "tetris.cpp" // replace with game specific functions + /* ./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\" { @@ -55,12 +58,6 @@ static int random_tetromino(void) ./c cclib events 17 \"[%226d%22,%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,1]\" */ -int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload); -int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk); -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); -void games_packitemstr(char *packitemstr,struct games_packitem *item); -int64_t games_cashout(struct games_player *P); - CScript games_newgameopret(int64_t buyin,int32_t maxplayers) { @@ -1193,91 +1190,6 @@ UniValue games_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *param } else return(cclib_error(result,"couldnt reparse params")); } -char *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr) -{ - CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64],*keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP; - gamespk = GetUnspendable(cp,0); - *numkeysp = 0; - seed = 0; - num = numkeys = 0; - playertxid = zeroid; - str[0] = 0; - if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 ) - { - if ( (retval= games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,gamesaddr,numplayers,symbol,pname)) == 0 ) - { - UniValue obj; - seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr); - //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); - memset(&P,0,sizeof(P)); - if ( playerdata.size() > 0 ) - { - for (i=0; i no playerdata\n"); - newdata.resize(0); - *numkeysp = numkeys; - return(keystrokes); - /* P.gold = (P.gold * 8) / 10; - if ( keystrokes != 0 ) - { - free(keystrokes); - keystrokes = 0; - *numkeysp = 0; - return(keystrokes); - }*/ - } - else - { - sprintf(str,"$$$gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",endP.gold,endP.hitpoints,endP.strength&0xffff,endP.strength>>16,endP.level,endP.experience,endP.dungeonlevel); - //fprintf(stderr,"%s\n",str); - *numkeysp = numkeys; - return(keystrokes); - } - } else num = 0; - } - else - { - fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval); - if ( keystrokes != 0 ) - free(keystrokes), keystrokes = 0; - } - } else fprintf(stderr,"extractgame: invalid game\n"); - //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str()); - return(0); -} - UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ); CPubKey pk,gamespk; int32_t i,n,numkeys,flag = 0; uint64_t seed; char str[512],gamesaddr[64],*pubstr,*hexstr,*keystrokes = 0; std::vector newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33]; @@ -1556,11 +1468,5 @@ UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -#include "tetris.cpp" - -#else // STANDALONE - -#include "tetris.c" - #endif diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 4ffd27575..94ceda83b 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -1,7 +1,10 @@ #include "tetris.h" -#include "dapps/dappstd.c" +static int random_tetromino(void) +{ + return rand() % NUM_TETROMINOS; +} /***************************************************************************/ /** https://github.com/brenns10/tetris @@ -622,10 +625,12 @@ void init_colors(void) init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); } +#else /* Main tetris game! */ -#ifdef STANDALONE +#include "dapps/dappstd.c" + char *clonestr(char *str) { char *clone; int32_t len; @@ -756,3 +761,5 @@ int32_t games_replay(uint64_t seed,int32_t sleeptime) { return(-1); } +#endif + diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index fd833d7b7..4d0be1e96 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -128,6 +128,91 @@ int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct C return(-1); } +char *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr) +{ + CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64],*keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP; + gamespk = GetUnspendable(cp,0); + *numkeysp = 0; + seed = 0; + num = numkeys = 0; + playertxid = zeroid; + str[0] = 0; + if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 ) + { + if ( (retval= games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,gamesaddr,numplayers,symbol,pname)) == 0 ) + { + UniValue obj; + seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr); + //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + for (i=0; i no playerdata\n"); + newdata.resize(0); + *numkeysp = numkeys; + return(keystrokes); + /* P.gold = (P.gold * 8) / 10; + if ( keystrokes != 0 ) + { + free(keystrokes); + keystrokes = 0; + *numkeysp = 0; + return(keystrokes); + }*/ + } + else + { + sprintf(str,"$$$gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",endP.gold,endP.hitpoints,endP.strength&0xffff,endP.strength>>16,endP.level,endP.experience,endP.dungeonlevel); + //fprintf(stderr,"%s\n",str); + *numkeysp = numkeys; + return(keystrokes); + } + } else num = 0; + } + else + { + fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval); + if ( keystrokes != 0 ) + free(keystrokes), keystrokes = 0; + } + } else fprintf(stderr,"extractgame: invalid game\n"); + //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str()); + return(0); +} + int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) // replay in daemon { return(-1); diff --git a/src/cc/tetris.h b/src/cc/tetris.h index f032bf2da..b239a1644 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -1,51 +1,7 @@ -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ #ifndef H_TETRIS_H #define H_TETRIS_H -#define GAMENAME "tetris" -#define GAMEMAIN tetris -#define CHAINNAME "GTEST" - -#define MAXPACK 23 -struct games_packitem -{ - int32_t type,launch,count,which,hplus,dplus,arm,flags,group; - char damage[8],hurldmg[8]; -}; - -struct games_player -{ - int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; - struct games_packitem gamespack[MAXPACK]; -}; - -struct games_state -{ - uint64_t seed; - char *keystrokes,*keystrokeshex; - uint32_t needflush,replaydone; - int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; - FILE *logfp; - struct games_player P; - char buffered[10000]; - uint8_t playerdata[10000]; -}; - - /***************************************************************************/ /** https://github.com/brenns10/tetris @file main.c @@ -193,5 +149,49 @@ bool tg_check(tetris_game *obj, int row, int col); bool tg_tick(tetris_game *obj, tetris_move move); void tg_print(tetris_game *obj, FILE *f); +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ +#define GAMENAME "tetris" +#define GAMEMAIN tetris +#define CHAINNAME "GTEST" + +#define MAXPACK 23 +struct games_packitem +{ + int32_t type,launch,count,which,hplus,dplus,arm,flags,group; + char damage[8],hurldmg[8]; +}; + +struct games_player +{ + int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; + struct games_packitem gamespack[MAXPACK]; +}; + +struct games_state +{ + uint64_t seed; + char *keystrokes,*keystrokeshex; + uint32_t needflush,replaydone; + int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; + FILE *logfp; + struct games_player P; + char buffered[10000]; + uint8_t playerdata[10000]; +}; + + #endif From f9c20dab7876d7f08ccd0411fcbcf7311dee27f6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:27:25 -1100 Subject: [PATCH 201/787] Def standalone --- src/cc/tetris.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 94ceda83b..7c6ff768e 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -625,7 +625,7 @@ void init_colors(void) init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); } -#else +#ifdef STANDALONE /* Main tetris game! */ From a3d531714ec633ee3f685e493c12958500aee6ee Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:28:28 -1100 Subject: [PATCH 202/787] Extern const --- src/cc/tetris.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/tetris.h b/src/cc/tetris.h index b239a1644..e2639cba1 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -127,13 +127,13 @@ typedef struct { array contains 4 tetris_location objects, each mapping to an offset from a point on the upper left that is the tetromino "origin". */ -extern tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS]; +extern const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS]; /* This array tells you how many ticks per gravity by level. Decreases as level increases, to add difficulty. */ -extern int GRAVITY_LEVEL[MAX_LEVEL+1]; +extern const int GRAVITY_LEVEL[MAX_LEVEL+1]; // Data structure manipulation. void tg_init(tetris_game *obj, int rows, int cols); From 8c71d44ba66aabfde1cdad6055d94185d3ae14dc Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:31:43 -1100 Subject: [PATCH 203/787] Reorder --- src/cc/gamescc.cpp | 26 -------------------------- src/cc/tetris.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 869205acc..f62a8699c 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -17,12 +17,6 @@ #include "tetris.c" // replace with game code #ifndef STANDALONE -int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload); -int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk); -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); -void games_packitemstr(char *packitemstr,struct games_packitem *item); -int64_t games_cashout(struct games_player *P); -char *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr); #include "tetris.cpp" // replace with game specific functions @@ -551,26 +545,6 @@ int32_t games_playersalive(int32_t &openslots,int32_t &numplayers,uint256 gametx return(alive); } -void disp_gamesplayerdata(std::vector playerdata) -{ - struct games_player P; int32_t i; char packitemstr[512]; - if ( playerdata.size() > 0 ) - { - for (i=0; i>16,P.level,P.experience,P.dungeonlevel); - for (i=0; i playerdata,uint256 playertxid,uint256 tokenid,std::string symbol,std::string pname,uint256 gametxid) { int32_t i,vout,spentvini,numvouts,n=0; uint256 txid,spenttxid,hashBlock; struct games_player P; char packitemstr[512],*datastr=0; UniValue obj(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx; diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index 4d0be1e96..cb9c47cc8 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -14,6 +14,10 @@ * * ******************************************************************************/ +int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,char **keystrokesp,int32_t &numkeys,int32_t ®slot,std::vector &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname); +int32_t games_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0); +uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr); + // game specific code for daemon int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) @@ -47,6 +51,26 @@ int64_t games_cashout(struct games_player *P) return(cashout); } +void disp_gamesplayerdata(std::vector playerdata) +{ + struct games_player P; int32_t i; char packitemstr[512]; + if ( playerdata.size() > 0 ) + { + for (i=0; i>16,P.level,P.experience,P.dungeonlevel); + for (i=0; i playerdata,uint256 gametxid,CPubKey pk) { static uint32_t good,bad; static uint256 prevgame; From 43bdea08406e8d7e0b759c555c2d77085464583e Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:33:49 -1100 Subject: [PATCH 204/787] Reorder --- src/cc/tetris.cpp | 174 +++++++++++++++++++++++----------------------- 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index cb9c47cc8..f9e4e167a 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -20,6 +20,16 @@ uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 // game specific code for daemon +void games_packitemstr(char *packitemstr,struct games_packitem *item) +{ + sprintf(packitemstr,"not yet"); +} + +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) // replay in daemon +{ + return(-1); +} + int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) { uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; @@ -71,87 +81,6 @@ void disp_gamesplayerdata(std::vector playerdata) } } -int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) -{ - static uint32_t good,bad; static uint256 prevgame; - char str[512],*keystrokes,gamesaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; - *cashoutp = 0; - gamespk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,gamesaddr,gamespk,pk); - if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) - { - free(keystrokes); - sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); - remove(fname); - - for (i=0; i no playerdata, good.%d bad.%d\n",good,bad); - } - *cashoutp = 0; - return(0); - } - } - if ( gametxid != prevgame ) - { - prevgame = gametxid; - bad++; - disp_gamesplayerdata(newdata); - disp_gamesplayerdata(playerdata); - fprintf(stderr,"%s playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d\n",gametxid.GetHex().c_str(),P.gold,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel); - fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,gamesaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); - } - } - sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); - remove(fname); - //fprintf(stderr,"no keys games_extractgame %s\n",gametxid.GetHex().c_str()); - return(-1); -} - char *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr) { CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64],*keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP; @@ -237,16 +166,87 @@ char *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vecto return(0); } -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) // replay in daemon +int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) { + static uint32_t good,bad; static uint256 prevgame; + char str[512],*keystrokes,gamesaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; + *cashoutp = 0; + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,gamesaddr,gamespk,pk); + if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) + { + free(keystrokes); + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); + remove(fname); + + for (i=0; i no playerdata, good.%d bad.%d\n",good,bad); + } + *cashoutp = 0; + return(0); + } + } + if ( gametxid != prevgame ) + { + prevgame = gametxid; + bad++; + disp_gamesplayerdata(newdata); + disp_gamesplayerdata(playerdata); + fprintf(stderr,"%s playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d\n",gametxid.GetHex().c_str(),P.gold,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel); + fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,gamesaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); + } + } + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); + remove(fname); + //fprintf(stderr,"no keys games_extractgame %s\n",gametxid.GetHex().c_str()); return(-1); } -void games_packitemstr(char *packitemstr,struct games_packitem *item) -{ - sprintf(packitemstr,"not yet"); -} - bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) { return(true); From c166507c62a7afb2b21d69243a968a28fe51907d Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:36:06 -1100 Subject: [PATCH 205/787] rand() --- src/cc/tetris.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 7c6ff768e..d3e9911a1 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -1,6 +1,12 @@ #include "tetris.h" +/* + In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events + */ + +int rand(); + static int random_tetromino(void) { return rand() % NUM_TETROMINOS; From d5e26e7bc590a061aabe1ae336bd66e7fae20ab4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:44:10 -1100 Subject: [PATCH 206/787] Declare rs --- src/cc/tetris.c | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index d3e9911a1..874a6437d 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -2,7 +2,7 @@ #include "tetris.h" /* - In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events + In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. */ int rand(); @@ -654,14 +654,40 @@ char *clonestr(char *str) return(clone); } +struct games_state globalR; + int tetris(int argc, char **argv) { tetris_game *tg; tetris_move move = TM_NONE; bool running = true; WINDOW *board, *next, *hold, *score; + struct games_state *rs = &globalR; int32_t c,skipcount=0; bits256 gametxid; uint32_t eventid = 0; memset(&gametxid,0,sizeof(gametxid)); + memset(rs,0,sizeof(*rs)); + rs->guiflag = 1; + rs->sleeptime = 1; // non-zero to allow refresh() + if ( argc == 3 && strlen(argv[2]) == 64 ) + { +#ifdef _WIN32 +#ifdef _MSC_VER + rs->seed = _strtoui64(argv[1], NULL, 10); +#else + rs->seed = atol(argv[1]); // windows, but not MSVC +#endif // _MSC_VER +#else + rs->seed = atol(argv[1]); // non-windows +#endif // _WIN32 + strcpy(Gametxidstr,argv[2]); + fprintf(stderr,"setplayerdata\n"); + if ( games_setplayerdata(rs,Gametxidstr) < 0 ) + { + fprintf(stderr,"invalid gametxid, or already started\n"); + return(-1); + } + } else rs->seed = 777; + // Load file if given a filename. if (argc >= 2) { FILE *f = fopen(argv[1], "r"); From 226179dcd386417dded5910f9c28154d5f905868 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:45:17 -1100 Subject: [PATCH 207/787] Skip rand() --- src/cc/tetris.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 874a6437d..adcf098f5 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -5,11 +5,10 @@ In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. */ -int rand(); - static int random_tetromino(void) { - return rand() % NUM_TETROMINOS; + return(0); + //return rand() % NUM_TETROMINOS; } /***************************************************************************/ From b4dd00266d9c2496f0e039a3e2d708e61bb3d540 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 00:55:53 -1100 Subject: [PATCH 208/787] Active rngnext --- src/cc/tetris.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index adcf098f5..db88c0659 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -3,12 +3,14 @@ /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. + + also, the standalone game needs to support argv of seed gametxid, along with replay args */ -static int random_tetromino(void) +static int random_tetromino(struct games_state *rs) { - return(0); - //return rand() % NUM_TETROMINOS; + rs->seed = _games_rngnext(rs->seed); + return(rs->seed % NUM_TETROMINOS); } /***************************************************************************/ @@ -173,11 +175,11 @@ static bool tg_fits(tetris_game *obj, tetris_block block) Create a new falling block and populate the next falling block with a random one. */ -static void tg_new_falling(tetris_game *obj) +static void tg_new_falling(struct games_state *rs,tetris_game *obj) { // Put in a new falling tetromino. obj->falling = obj->next; - obj->next.typ = random_tetromino(); + obj->next.typ = random_tetromino(rs); obj->next.ori = 0; obj->next.loc.row = 0; obj->next.loc.col = obj->cols/2 - 2; @@ -190,7 +192,7 @@ static void tg_new_falling(tetris_game *obj) /* Tick gravity, and move the block down if gravity should act. */ -static void tg_do_gravity_tick(tetris_game *obj) +static void tg_do_gravity_tick(struct games_state *rs,tetris_game *obj) { obj->ticks_till_gravity--; if (obj->ticks_till_gravity <= 0) { @@ -202,7 +204,7 @@ static void tg_do_gravity_tick(tetris_game *obj) obj->falling.loc.row--; tg_put(obj, obj->falling); - tg_new_falling(obj); + tg_new_falling(rs,obj); } tg_put(obj, obj->falling); } @@ -411,11 +413,11 @@ static bool tg_game_over(tetris_game *obj) Do a single game tick: process gravity, user input, and score. Return true if the game is still running, false if it is over. */ -bool tg_tick(tetris_game *obj, tetris_move move) +bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move) { int lines_cleared; // Handle gravity. - tg_do_gravity_tick(obj); + tg_do_gravity_tick(rs,obj); // Handle input. tg_handle_move(obj, move); @@ -687,7 +689,7 @@ int tetris(int argc, char **argv) } } else rs->seed = 777; - // Load file if given a filename. + /* Load file if given a filename. if (argc >= 2) { FILE *f = fopen(argv[1], "r"); if (f == NULL) { @@ -699,7 +701,9 @@ int tetris(int argc, char **argv) } else { // Otherwise create new game. tg = tg_create(22, 10); - } + }*/ + tg = tg_create(22, 10); + // NCURSES initialization: initscr(); // initialize curses cbreak(); // pass key presses to program, but not signals @@ -717,7 +721,7 @@ int tetris(int argc, char **argv) int32_t counter = 0; // Game loop while (running) { - running = tg_tick(tg, move); + running = tg_tick(rs,tg, move); display_board(board, tg); display_piece(next, tg->next); display_piece(hold, tg->stored); From 46dba9071ba32f439570e7bd8bc390b0103e677c Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 01:49:11 -1100 Subject: [PATCH 209/787] Add keystrokes replay --- src/cc/dapps/dappstd.c | 318 ++++++++++++++++++++++++++++------------- src/cc/tetris.c | 65 +++++++-- 2 files changed, 274 insertions(+), 109 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index ec8045b2f..033633665 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -688,33 +688,6 @@ char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) return(retstr2); } -int32_t issue_games_events(bits256 gametxid,uint32_t eventid,int32_t c) -{ - static FILE *fp; - char params[512],*retstr,str[65]; cJSON *retjson,*resobj; int32_t retval = -1; - if ( fp == 0 ) - fp = fopen("events.log","wb"); - sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c,bits256_str(str,gametxid),eventid); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) - { - retval = 0; - if ( fp != 0 ) - { - fprintf(fp,"%s\n",jprint(resobj,0)); - fflush(fp); - } - } - free_json(retjson); - } else fprintf(fp,"error parsing %s\n",retstr); - free(retstr); - } else fprintf(fp,"error issuing method %s\n",params); - return(retval); -} - int32_t games_sendrawtransaction(char *rawtx) { char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; @@ -781,91 +754,240 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,cha } free(rs->keystrokeshex), rs->keystrokeshex = 0; } - if ( 0 && (pastkeys= games_keystrokesload(&numpastkeys,seed,1)) != 0 ) - { - sprintf(params,"[\"extract\",\"17\",\"[%%22%s%%22]\"]",Gametxidstr); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (keys= jstr(resobj,(char *)"keystrokes")) != 0 ) - { - len = strlen(keys) / 2; - pastcmp = (uint8_t *)malloc(len + 1); - decode_hex(pastcmp,len,keys); - fprintf(stderr,"keystrokes.(%s) vs pastkeys\n",keys); - for (i=0; i> keystrokes.log",ASSETCHAINS_SYMBOL,Gametxidstr,hexstr); - if ( system(cmd) != 0 ) - fprintf(stderr,"error issuing (%s)\n",cmd); - } - else - { - static FILE *fp; - if ( fp == 0 ) - fp = fopen("keystrokes.log","a"); - sprintf(params,"[\"keystrokes\",\"17\",\"[%%22%s%%22,%%22%s%%22]\"]",Gametxidstr,hexstr); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + if ( fp != 0 ) { - if ( fp != 0 ) + fprintf(fp,"%s\n",params); + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (rawtx= jstr(resobj,(char *)"hex")) != 0 ) { - fprintf(fp,"%s\n",params); - fprintf(fp,"%s\n",retstr); - fflush(fp); - } - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 && (rawtx= jstr(resobj,(char *)"hex")) != 0 ) + if ( rs->keystrokeshex != 0 ) + free(rs->keystrokeshex); + if ( (errstr= jstr(resobj,(char *)"error")) == 0 ) { - if ( rs->keystrokeshex != 0 ) - free(rs->keystrokeshex); - if ( (errstr= jstr(resobj,(char *)"error")) == 0 ) - { - rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); - strcpy(rs->keystrokeshex,rawtx); - retflag = 1; - } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); - //fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); - } - free_json(retjson); + rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); + strcpy(rs->keystrokeshex,rawtx); + retflag = 1; + } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); + //fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); } - free(retstr); - } - if ( 0 && waitflag != 0 && rs->keystrokeshex != 0 ) - { - while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) - { - //fprintf(stderr,"post-rebroadcast\n"); - sleep(3); - } - free(rs->keystrokeshex), rs->keystrokeshex = 0; + free_json(retjson); } + free(retstr); } } return(retflag); } +int32_t gamesfname(char *fname,uint64_t seed,int32_t counter) +{ + sprintf(fname,"%s.%llu.%d",GAMENAME,(long long)seed,counter); + return(0); +} + +int32_t flushkeystrokes_local(struct games_state *rs,int32_t waitflag) +{ +#ifdef STANDALONE + char fname[1024]; FILE *fp; int32_t i,retflag = -1; + rs->counter++; + gamesfname(fname,rs->seed,rs->counter); + if ( (fp= fopen(fname,"wb")) != 0 ) + { + if ( fwrite(rs->buffered,1,rs->num,fp) == rs->num ) + { + rs->num = 0; + retflag = 0; + fclose(fp); + gamesfname(fname,rs->seed,rs->counter+1); + if ( (fp= fopen(fname,"wb")) != 0 ) // truncate next file + fclose(fp); + //fprintf(stderr,"savefile <- %s retflag.%d\n",fname,retflag); + //} + } else fprintf(stderr,"error writing (%s)\n",fname); + } else fprintf(stderr,"error creating (%s)\n",fname); + return(retflag); +#else + return(0); +#endif +} + +#ifndef STANDALONE +// stubs for inside daemon + +int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +{ + return(0); +} + +int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) +{ + return(-1); +} +#endif + +int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag) +{ + if ( rs->num > 0 ) + { + if ( games_progress(rs,waitflag,rs->seed,rs->buffered,rs->num) > 0 ) + { + flushkeystrokes_local(rs,waitflag); + memset(rs->buffered,0,sizeof(rs->buffered)); + } + } + return(0); +} + +void games_bailout(struct games_state *rs) +{ + flushkeystrokes(rs,1); +} + +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + +int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) +{ + struct games_state *rs; FILE *fp; int32_t i,n; + rs = (struct games_state *)calloc(1,sizeof(*rs)); + rs->seed = seed; + rs->keystrokes = keystrokes; + rs->numkeys = num; + rs->sleeptime = sleepmillis * 1000; + if ( player != 0 ) + { + rs->P = *player; + rs->restoring = 1; + //fprintf(stderr,"restore player packsize.%d HP.%d\n",rs->P.packsize,rs->P.hitpoints); + if ( rs->P.packsize > MAXPACK ) + rs->P.packsize = MAXPACK; + } + globalR = *rs; + uint32_t starttime = (uint32_t)time(NULL); + gamesiterate(rs); + if ( 0 ) + { + fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL) - starttime); + sleep(2); + starttime = (uint32_t)time(NULL); + for (i=0; i<10000; i++) + { + memset(rs,0,sizeof(*rs)); + rs->seed = seed; + rs->keystrokes = keystrokes; + rs->numkeys = num; + rs->sleeptime = 0; + gamesiterate(rs); + } + fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL)-starttime); + sleep(3); + } + // extract playerdata + + /*if ( (fp= fopen("checkfile","wb")) != 0 ) + { + //save_file(rs,fp,0); + //fprintf(stderr,"gold.%d hp.%d strength.%d/%d level.%d exp.%d dungeon.%d data[%d]\n",rs->P.gold,rs->P.hitpoints,rs->P.strength&0xffff,rs->P.strength>>16,rs->P.level,rs->P.experience,rs->P.dungeonlevel,rs->playersize); + if ( newdata != 0 && rs->playersize > 0 ) + memcpy(newdata,rs->playerdata,rs->playersize); + }*/ + if ( newdata != 0 && rs->playersize > 0 ) + memcpy(newdata,rs->playerdata,rs->playersize); + n = rs->playersize; + free(rs); + return(n); +} + +long get_filesize(FILE *fp) +{ + long fsize,fpos = ftell(fp); + fseek(fp,0,SEEK_END); + fsize = ftell(fp); + fseek(fp,fpos,SEEK_SET); + return(fsize); +} + +char *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter) +{ + char fname[1024],*keystrokes = 0; FILE *fp; long fsize; int32_t num = 0; + *numkeysp = 0; + while ( 1 ) + { + gamesfname(fname,seed,counter); + //printf("check (%s)\n",fname); + if ( (fp= fopen(fname,"rb")) == 0 ) + break; + if ( (fsize= get_filesize(fp)) <= 0 ) + { + fclose(fp); + //printf("fsize.%ld\n",fsize); + break; + } + if ( (keystrokes= (char *)realloc(keystrokes,num+fsize)) == 0 ) + { + fprintf(stderr,"error reallocating keystrokes\n"); + fclose(fp); + return(0); + } + if ( fread(&keystrokes[num],1,fsize,fp) != fsize ) + { + fprintf(stderr,"error reading keystrokes from (%s)\n",fname); + fclose(fp); + free(keystrokes); + return(0); + } + fclose(fp); + num += fsize; + counter++; + //fprintf(stderr,"loaded %ld from (%s) total %d\n",fsize,fname,num); + } + *numkeysp = num; + return(keystrokes); +} + +int32_t games_replay(uint64_t seed,int32_t sleeptime) +{ + FILE *fp; char fname[1024]; char *keystrokes = 0; long fsize; int32_t i,num=0,counter = 0; struct games_state *rs; struct games_player P,*player = 0; + if ( seed == 0 ) + seed = 777; + keystrokes = games_keystrokesload(&num,seed,counter); + if ( num > 0 ) + { + sprintf(fname,"%s.%llu.player",GAMENAME,(long long)seed); + if ( (fp=fopen(fname,"rb")) != 0 ) + { + if ( fread(&P,1,sizeof(P),fp) > 0 ) + { + //printf("max size player\n"); + player = &P; + } + fclose(fp); + } + games_replay2(0,seed,keystrokes,num,player,sleeptime); + mvaddstr(LINES - 2, 0, (char *)"replay completed"); + endwin(); + my_exit(0); + } + if ( keystrokes != 0 ) + free(keystrokes); + return(num); +} + int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) { char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; diff --git a/src/cc/tetris.c b/src/cc/tetris.c index db88c0659..5e873e5c4 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -638,6 +638,34 @@ void init_colors(void) */ #include "dapps/dappstd.c" +int32_t issue_games_events(struct games_state *rs,bits256 gametxid,uint32_t eventid,char c) +{ + static FILE *fp; + char params[512],*retstr,str[65]; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c,bits256_str(str,gametxid),eventid); + rs->buffered[rs->num++] = c; + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); +} + char *clonestr(char *str) { char *clone; int32_t len; @@ -730,34 +758,49 @@ int tetris(int argc, char **argv) doupdate(); sleep_milli(10); c = getch(); - if ( c != -1 || skipcount == 0x3fff ) + switch ( c ) + { + case KEY_LEFT: + c = 'h'; + break; + case KEY_RIGHT: + c = 'l'; + break; + case KEY_UP: + c = 'k'; + break; + case KEY_DOWN: + c = 'j'; + break; + } + if ( c < 0 || skipcount == 0x7f ) { if ( skipcount > 0 ) - issue_games_events(gametxid,eventid-skipcount,skipcount | 0x4000); + issue_games_events(rs,gametxid,eventid-skipcount,skipcount | 0x80); if ( c != -1 ) - issue_games_events(gametxid,eventid,c); + issue_games_events(rs,gametxid,eventid,c); skipcount = 0; } else skipcount++; eventid++; switch ( c ) { - case KEY_LEFT: + case 'h': move = TM_LEFT; break; - case KEY_RIGHT: + case 'l': move = TM_RIGHT; break; - case KEY_UP: + case 'k': move = TM_CLOCK; break; - case KEY_DOWN: + case 'j': move = TM_DROP; break; case 'q': running = false; move = TM_NONE; break; - case 'p': + /*case 'p': wclear(board); box(board, 0, 0); wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); @@ -771,7 +814,7 @@ int tetris(int argc, char **argv) case 's': save(tg, board); move = TM_NONE; - break; + break;*/ case ' ': move = TM_HOLD; break; @@ -792,9 +835,9 @@ int tetris(int argc, char **argv) return 0; } -int32_t games_replay(uint64_t seed,int32_t sleeptime) +void gamesiterate(struct games_state *rs) { - return(-1); + } #endif From c9a3844f54799b77c862c38f9faf06c3a0a08a1e Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 01:52:18 -1100 Subject: [PATCH 210/787] Test --- src/cc/tetris.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cc/tetris.h b/src/cc/tetris.h index e2639cba1..40a9c97cb 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -137,7 +137,7 @@ extern const int GRAVITY_LEVEL[MAX_LEVEL+1]; // Data structure manipulation. void tg_init(tetris_game *obj, int rows, int cols); -tetris_game *tg_create(int rows, int cols); +tetris_game *tg_create(struct games_state *rs,int rows, int cols); void tg_destroy(tetris_game *obj); void tg_delete(tetris_game *obj); tetris_game *tg_load(FILE *f); @@ -146,7 +146,7 @@ void tg_save(tetris_game *obj, FILE *f); // Public methods not related to memory: char tg_get(tetris_game *obj, int row, int col); bool tg_check(tetris_game *obj, int row, int col); -bool tg_tick(tetris_game *obj, tetris_move move); +bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move); void tg_print(tetris_game *obj, FILE *f); /****************************************************************************** @@ -192,6 +192,7 @@ struct games_state uint8_t playerdata[10000]; }; +uint64_t _games_rngnext(uint64_t initseed); #endif From a49081204962ba5076cdf59ec509d2182b6c4911 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 01:54:09 -1100 Subject: [PATCH 211/787] struct games_state *rs, --- src/cc/tetris.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 5e873e5c4..4dbb71f89 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -226,7 +226,7 @@ static void tg_move(tetris_game *obj, int direction) /* Send the falling tetris block to the bottom. */ -static void tg_down(tetris_game *obj) +static void tg_down(struct games_state *rs,tetris_game *obj) { tg_remove(obj, obj->falling); while (tg_fits(obj, obj->falling)) { @@ -234,7 +234,7 @@ static void tg_down(tetris_game *obj) } obj->falling.loc.row--; tg_put(obj, obj->falling); - tg_new_falling(obj); + tg_new_falling(rs,obj); } /* @@ -273,12 +273,12 @@ static void tg_rotate(tetris_game *obj, int direction) /* Swap the falling block with the block in the hold buffer. */ -static void tg_hold(tetris_game *obj) +static void tg_hold(struct games_state *rs,tetris_game *obj) { tg_remove(obj, obj->falling); if (obj->stored.typ == -1) { obj->stored = obj->falling; - tg_new_falling(obj); + tg_new_falling(rs,obj); } else { int typ = obj->falling.typ, ori = obj->falling.ori; obj->falling.typ = obj->stored.typ; @@ -431,7 +431,7 @@ bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move) return !tg_game_over(obj); } -void tg_init(tetris_game *obj, int rows, int cols) +void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols) { // Initialization logic obj->rows = rows; @@ -443,8 +443,8 @@ void tg_init(tetris_game *obj, int rows, int cols) obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; obj->lines_remaining = LINES_PER_LEVEL; //srand(time(NULL)); - tg_new_falling(obj); - tg_new_falling(obj); + tg_new_falling(rs,obj); + tg_new_falling(ts,obj); obj->stored.typ = -1; obj->stored.ori = 0; obj->stored.loc.row = 0; @@ -452,10 +452,10 @@ void tg_init(tetris_game *obj, int rows, int cols) printf("%d", obj->falling.loc.col); } -tetris_game *tg_create(int rows, int cols) +tetris_game *tg_create(struct games_state *rs,int rows, int cols) { tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); - tg_init(obj, rows, cols); + tg_init(rs,obj, rows, cols); return obj; } From 30df5445c762767263c3312560624476bac74c02 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 01:57:07 -1100 Subject: [PATCH 212/787] struct games_state *rs, --- src/cc/tetris.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 4dbb71f89..1b039dc6f 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -295,7 +295,7 @@ static void tg_hold(struct games_state *rs,tetris_game *obj) /* Perform the action specified by the move. */ -static void tg_handle_move(tetris_game *obj, tetris_move move) +static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move move) { switch (move) { case TM_LEFT: @@ -305,7 +305,7 @@ static void tg_handle_move(tetris_game *obj, tetris_move move) tg_move(obj, 1); break; case TM_DROP: - tg_down(obj); + tg_down(rs,obj); break; case TM_CLOCK: tg_rotate(obj, 1); @@ -314,7 +314,7 @@ static void tg_handle_move(tetris_game *obj, tetris_move move) tg_rotate(obj, -1); break; case TM_HOLD: - tg_hold(obj); + tg_hold(rs,obj); break; default: // pass @@ -420,7 +420,7 @@ bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move) tg_do_gravity_tick(rs,obj); // Handle input. - tg_handle_move(obj, move); + tg_handle_move(rs,obj, move); // Check for cleared lines lines_cleared = tg_check_lines(obj); @@ -444,7 +444,7 @@ void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols) obj->lines_remaining = LINES_PER_LEVEL; //srand(time(NULL)); tg_new_falling(rs,obj); - tg_new_falling(ts,obj); + tg_new_falling(rs,obj); obj->stored.typ = -1; obj->stored.ori = 0; obj->stored.loc.row = 0; @@ -728,9 +728,9 @@ int tetris(int argc, char **argv) fclose(f); } else { // Otherwise create new game. - tg = tg_create(22, 10); + tg = tg_create(rs,22, 10); }*/ - tg = tg_create(22, 10); + tg = tg_create(rs,22, 10); // NCURSES initialization: initscr(); // initialize curses From 8e6f5a6a2dd9e45b90c7d4d7d603f0901a157941 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 26 Mar 2019 20:59:31 +0800 Subject: [PATCH 213/787] initial commit for backup release tx needs testing, i think it half works. --- src/cc/CCinclude.h | 1 + src/cc/CCutils.cpp | 14 +++++++++++++- src/cc/payments.cpp | 6 ++++-- src/cc/rewards.cpp | 21 +++++++-------------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 13d7236d4..839fd2c95 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -247,6 +247,7 @@ void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, c int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode); bool IsCCInput(CScript const& scriptSig); +bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_t blocktime); int32_t unstringbits(char *buf,uint64_t bits); uint64_t stringbits(char *str); uint256 revuint256(uint256 txid); diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index a3bf0a68b..887550a0c 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -168,6 +168,18 @@ bool IsCCInput(CScript const& scriptSig) return true; } +bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_t blocktime) +{ + int64_t interest; uint64_t valuein; + CCoinsViewCache &view = *pcoinsTip; + valuein = view.GetValueIn(height,&interest,tx,blocktime); + if ( valuein-tx.GetValueOut() > txfee ) + { + //fprintf(stderr, "txfee.%li vs txfee.%li\n", valuein-tx.GetValueOut(), txfee); + return false; + } + return true; +} // set additional 'unspendable' addr void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr) @@ -668,4 +680,4 @@ bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector paramsNull,co return(false); //eval->Invalid("error in CClib_validate"); } return eval->Invalid("cclib CC must have evalcode between 16 and 127"); -} \ No newline at end of file +} diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 184c92d33..e0c4c8ada 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -192,7 +192,9 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & if ( tmptx.vout.size() > 0 && DecodePaymentsOpRet(tmptx.vout[tmptx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) - return(eval->Invalid("negative values")); + return(eval->Invalid("negative values")); + if ( !CheckTxFee(tx, PAYMENTS_TXFEE, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) + return eval->Invalid("txfee is too high"); Paymentspk = GetUnspendable(cp,0); //fprintf(stderr, "lockedblocks.%i minrelease.%i totalallocations.%i txidopret1.%s txidopret2.%s\n",lockedblocks, minrelease, totalallocations, txidoprets[0].ToString().c_str(), txidoprets[1].ToString().c_str() ); @@ -281,7 +283,7 @@ bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s\n", i, txin.GetHash().ToString().c_str()); return(eval->Invalid("vin is not paymentsCC type")); } //else fprintf(stderr, "vin.%i opret type txid.%s\n", i, txin.GetHash().ToString().c_str()); - } + } // check the chain depth vs locked blcoks requirement. CBlockIndex* pblockindex = mapBlockIndex[blockhash]; if ( pblockindex->GetHeight() > ht-lockedblocks ) diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 65e32ee27..db64127e7 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -197,9 +197,6 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t uint256 txid,fundingtxid,hashBlock,vinfundingtxid; uint64_t vinsbits,sbits,APR,minseconds,maxseconds,mindeposit,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx; numvins = tx.vin.size(); numvouts = tx.vout.size(); - int64_t interest; uint64_t valuein; - CCoinsViewCache &view = *pcoinsTip; - valuein = view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime); preventCCvins = preventCCvouts = -1; if ( numvouts < 1 ) return eval->Invalid("no vouts"); @@ -258,20 +255,17 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 ) return eval->Invalid("unexpected normal vin for unlock"); } - if ( valuein-tx.GetValueOut() > txfee ) + if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) + return eval->Invalid("txfee is too high"); + if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 && tx.vout[1].nValue == 10000 ) { - fprintf(stderr, "valueout.%li vs valuein.%li txfee.%li\n", tx.GetValueOut(), valuein, txfee); - return eval->Invalid("alright is stealing your money"); - } - if ( numvouts == 2 && numvins == 1 ) - { - if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 ) - return eval->Invalid("unlock recover tx vout.0 is not normal output"); + if ( tx.vout[1].scriptPubKey != tx.vout[0].scriptPubKey ) + return eval->Invalid("unlock recover tx vout.1 mismatched scriptPubKey"); else if ( tx.vout[0].scriptPubKey != vinTx.vout[1].scriptPubKey ) return eval->Invalid("unlock recover tx vout.0 mismatched scriptPubKey"); else if ( tx.vout[0].nValue > vinTx.vout[0].nValue ) return eval->Invalid("unlock recover tx vout.0 mismatched amounts"); - else if ( tx.vout[1].nValue > 0 ) + else if ( tx.vout[2].nValue > 0 ) return eval->Invalid("unlock recover tx vout.1 nonz amount"); else return(true); } @@ -689,8 +683,7 @@ std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint2 } else { - firstmtx.vout.push_back(CTxOut(amount-txfee,scriptPubKey)); - //CCerror = "cant find enough rewards inputs"; + firstmtx.vout.push_back(CTxOut(amount-txfee*2,scriptPubKey)); fprintf(stderr,"not enough rewards funds to payout %.8f, recover mode tx\n",(double)(reward+txfee)/COIN); return(FinalizeCCTx(-1LL,cp,firstmtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid))); } From 3a6db6cc718d9550b414dca6190775755f8cf6a3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 01:59:56 -1100 Subject: [PATCH 214/787] Gamesiterate --- src/cc/dapps/dappstd.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 033633665..5c8fb0204 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -23,6 +23,9 @@ #include #include +extern struct games_state globalR; +void gamesiterate(struct games_state *rs); + char USERPASS[8192]; uint16_t GAMES_PORT; char Gametxidstr[67]; char *clonestr(char *str); @@ -960,6 +963,19 @@ char *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter) return(keystrokes); } +void games_exit() +{ + uint32_t counter; + resetltchars(); + if ( globalR.guiflag != 0 || globalR.sleeptime != 0 ) + exit(st); + else if ( counter++ < 10 ) + { + fprintf(stderr,"would have exit.(%d) sleeptime.%d\n",st,globalR.sleeptime); + globalR.replaydone = 1; + } +} + int32_t games_replay(uint64_t seed,int32_t sleeptime) { FILE *fp; char fname[1024]; char *keystrokes = 0; long fsize; int32_t i,num=0,counter = 0; struct games_state *rs; struct games_player P,*player = 0; @@ -981,7 +997,7 @@ int32_t games_replay(uint64_t seed,int32_t sleeptime) games_replay2(0,seed,keystrokes,num,player,sleeptime); mvaddstr(LINES - 2, 0, (char *)"replay completed"); endwin(); - my_exit(0); + games_exit(); } if ( keystrokes != 0 ) free(keystrokes); From 7604ee369749937957eb18958542dee377691973 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 02:01:32 -1100 Subject: [PATCH 215/787] Exit --- src/cc/dapps/dappstd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 5c8fb0204..5f8821890 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -966,9 +966,9 @@ char *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter) void games_exit() { uint32_t counter; - resetltchars(); + //resetltchars(); if ( globalR.guiflag != 0 || globalR.sleeptime != 0 ) - exit(st); + exit(0); else if ( counter++ < 10 ) { fprintf(stderr,"would have exit.(%d) sleeptime.%d\n",st,globalR.sleeptime); From 62fc48877e96a4877de5d8ae12752221cdf4af30 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 02:05:19 -1100 Subject: [PATCH 216/787] -print --- src/cc/dapps/dappstd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 5f8821890..b93e7b41d 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -971,7 +971,7 @@ void games_exit() exit(0); else if ( counter++ < 10 ) { - fprintf(stderr,"would have exit.(%d) sleeptime.%d\n",st,globalR.sleeptime); + fprintf(stderr,"would have exit sleeptime.%d\n",globalR.sleeptime); globalR.replaydone = 1; } } From 7b8adf33e196a8a3e3ba5d957796a980a13922cf Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 02:06:33 -1100 Subject: [PATCH 217/787] Gamesrngnext --- src/cc/gamescc.cpp | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index f62a8699c..367c8d96e 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -16,6 +16,21 @@ #include "gamescc.h" #include "tetris.c" // replace with game code + +uint64_t _games_rngnext(uint64_t initseed) +{ + uint16_t seeds[4]; int32_t i; + seeds[0] = initseed; + seeds[1] = (initseed >> 16); + seeds[2] = (initseed >> 32); + seeds[3] = (initseed >> 48); + seeds[0] = (seeds[0]*GAMES_RNGMULT + GAMES_RNGOFFSET); + seeds[1] = ((seeds[0] ^ seeds[1])*GAMES_RNGMULT + GAMES_RNGOFFSET); + seeds[2] = ((seeds[0] ^ seeds[1] ^ seeds[2])*GAMES_RNGMULT + GAMES_RNGOFFSET); + seeds[3] = ((seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3])*GAMES_RNGMULT + GAMES_RNGOFFSET); + return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); +} + #ifndef STANDALONE #include "tetris.cpp" // replace with game specific functions @@ -195,20 +210,6 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastf return(result); } -uint64_t _games_rngnext(uint64_t initseed) -{ - uint16_t seeds[4]; int32_t i; - seeds[0] = initseed; - seeds[1] = (initseed >> 16); - seeds[2] = (initseed >> 32); - seeds[3] = (initseed >> 48); - seeds[0] = (seeds[0]*GAMES_RNGMULT + GAMES_RNGOFFSET); - seeds[1] = ((seeds[0] ^ seeds[1])*GAMES_RNGMULT + GAMES_RNGOFFSET); - seeds[2] = ((seeds[0] ^ seeds[1] ^ seeds[2])*GAMES_RNGMULT + GAMES_RNGOFFSET); - seeds[3] = ((seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3])*GAMES_RNGMULT + GAMES_RNGOFFSET); - return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); -} - UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ); int32_t n; uint64_t seed; From fd3ded58bc46422389e95033d69f22779fa99ec9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 02:07:13 -1100 Subject: [PATCH 218/787] #define GAMES_RNGMULT 11109 #define GAMES_RNGOFFSET 13849 #define GAMES_MAXRNGS 10000 --- src/cc/gamescc.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 07a1f8e65..6ec5dc9c4 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -3,6 +3,9 @@ #include #include +#define GAMES_RNGMULT 11109 +#define GAMES_RNGOFFSET 13849 +#define GAMES_MAXRNGS 10000 #ifndef STANDALONE @@ -22,9 +25,6 @@ std::string MYCCLIBNAME = (char *)"gamescc"; #define GAMES_REGISTRATIONSIZE (100 * 10000) #define GAMES_REGISTRATION 1 -#define GAMES_RNGMULT 11109 -#define GAMES_RNGOFFSET 13849 -#define GAMES_MAXRNGS 10000 #define MYCCNAME "games" From 4e9f443e4a20fa1cf635541c6f0b6886802c36fb Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 26 Mar 2019 21:09:24 +0800 Subject: [PATCH 219/787] fix minrelease recover transaction --- src/cc/rewards.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index db64127e7..2438b4035 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -257,9 +257,12 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t } if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) return eval->Invalid("txfee is too high"); + reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit); if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 && tx.vout[1].nValue == 10000 ) { - if ( tx.vout[1].scriptPubKey != tx.vout[0].scriptPubKey ) + if ( reward == 0 ) + return eval->Invalid("unlock recover no rewards"); + else if ( tx.vout[1].scriptPubKey != tx.vout[0].scriptPubKey ) return eval->Invalid("unlock recover tx vout.1 mismatched scriptPubKey"); else if ( tx.vout[0].scriptPubKey != vinTx.vout[1].scriptPubKey ) return eval->Invalid("unlock recover tx vout.0 mismatched scriptPubKey"); From 5330c2349980178a6236e6d32308ef4b4982573c Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 02:23:50 -1100 Subject: [PATCH 220/787] Gamesiterate --- src/cc/tetris.c | 186 ++++++++++++++++++++++++------------------------ 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 1b039dc6f..1cc7962f5 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -638,13 +638,13 @@ void init_colors(void) */ #include "dapps/dappstd.c" -int32_t issue_games_events(struct games_state *rs,bits256 gametxid,uint32_t eventid,char c) +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,char c) { static FILE *fp; - char params[512],*retstr,str[65]; cJSON *retjson,*resobj; int32_t retval = -1; + char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; if ( fp == 0 ) fp = fopen("events.log","wb"); - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c,bits256_str(str,gametxid),eventid); + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c,gametxidstr,eventid); rs->buffered[rs->num++] = c; if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { @@ -685,15 +685,98 @@ char *clonestr(char *str) struct games_state globalR; +void gamesiterate(struct games_state *rs,tetris_game *tg) +{ + uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; + int32_t c,skipcount=0; uint32_t eventid = 0; + WINDOW *board, *next, *hold, *score; + // Create windows for each section of the interface. + board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); + next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); + hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); + score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); + while (running) + { + running = tg_tick(rs,tg, move); + display_board(board, tg); + display_piece(next, tg->next); + display_piece(hold, tg->stored); + display_score(score, tg); + if ( (counter++ % 5) == 0 ) + doupdate(); + sleep_milli(10); + c = getch(); + switch ( c ) + { + case KEY_LEFT: + c = 'h'; + break; + case KEY_RIGHT: + c = 'l'; + break; + case KEY_UP: + c = 'k'; + break; + case KEY_DOWN: + c = 'j'; + break; + } + if ( c < 0 || skipcount == 0x7f ) + { + if ( skipcount > 0 ) + issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x80); + if ( c != -1 ) + issue_games_events(rs,Gametxidstr,eventid,c); + skipcount = 0; + } else skipcount++; + eventid++; + switch ( c ) + { + case 'h': + move = TM_LEFT; + break; + case 'l': + move = TM_RIGHT; + break; + case 'k': + move = TM_CLOCK; + break; + case 'j': + move = TM_DROP; + break; + case 'q': + running = false; + move = TM_NONE; + break; + /*case 'p': + wclear(board); + box(board, 0, 0); + wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); + wprintw(board, "PAUSED"); + wrefresh(board); + timeout(-1); + getch(); + timeout(0); + move = TM_NONE; + break; + case 's': + save(tg, board); + move = TM_NONE; + break;*/ + case ' ': + move = TM_HOLD; + break; + default: + move = TM_NONE; + } + } +} + int tetris(int argc, char **argv) { tetris_game *tg; - tetris_move move = TM_NONE; - bool running = true; - WINDOW *board, *next, *hold, *score; struct games_state *rs = &globalR; - int32_t c,skipcount=0; bits256 gametxid; uint32_t eventid = 0; - memset(&gametxid,0,sizeof(gametxid)); + int32_t c,skipcount=0; uint32_t eventid = 0; memset(rs,0,sizeof(*rs)); rs->guiflag = 1; rs->sleeptime = 1; // non-zero to allow refresh() @@ -741,88 +824,9 @@ int tetris(int argc, char **argv) curs_set(0); // set the cursor to invisible init_colors(); // setup tetris colors - // Create windows for each section of the interface. - board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); - next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); - hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); - score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); - int32_t counter = 0; // Game loop - while (running) { - running = tg_tick(rs,tg, move); - display_board(board, tg); - display_piece(next, tg->next); - display_piece(hold, tg->stored); - display_score(score, tg); - if ( (counter++ % 5) == 0 ) - doupdate(); - sleep_milli(10); - c = getch(); - switch ( c ) - { - case KEY_LEFT: - c = 'h'; - break; - case KEY_RIGHT: - c = 'l'; - break; - case KEY_UP: - c = 'k'; - break; - case KEY_DOWN: - c = 'j'; - break; - } - if ( c < 0 || skipcount == 0x7f ) - { - if ( skipcount > 0 ) - issue_games_events(rs,gametxid,eventid-skipcount,skipcount | 0x80); - if ( c != -1 ) - issue_games_events(rs,gametxid,eventid,c); - skipcount = 0; - } else skipcount++; - eventid++; - switch ( c ) - { - case 'h': - move = TM_LEFT; - break; - case 'l': - move = TM_RIGHT; - break; - case 'k': - move = TM_CLOCK; - break; - case 'j': - move = TM_DROP; - break; - case 'q': - running = false; - move = TM_NONE; - break; - /*case 'p': - wclear(board); - box(board, 0, 0); - wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); - wprintw(board, "PAUSED"); - wrefresh(board); - timeout(-1); - getch(); - timeout(0); - move = TM_NONE; - break; - case 's': - save(tg, board); - move = TM_NONE; - break;*/ - case ' ': - move = TM_HOLD; - break; - default: - move = TM_NONE; - } - } - + gamesiterate(rs,tg); + games_bailout(rs); // Deinitialize NCurses wclear(stdscr); endwin(); @@ -835,9 +839,5 @@ int tetris(int argc, char **argv) return 0; } -void gamesiterate(struct games_state *rs) -{ - -} #endif From b0d0adb729531b32267a673e9a0f1a6dbd42eb04 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 02:28:32 -1100 Subject: [PATCH 221/787] Gamesiterate --- src/cc/tetris.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 1cc7962f5..98e6a2fab 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -685,12 +685,13 @@ char *clonestr(char *str) struct games_state globalR; -void gamesiterate(struct games_state *rs,tetris_game *tg) +void gamesiterate(struct games_state *rs) { uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; - int32_t c,skipcount=0; uint32_t eventid = 0; + int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg; WINDOW *board, *next, *hold, *score; // Create windows for each section of the interface. + tg = tg_create(rs,22, 10); board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); @@ -774,7 +775,6 @@ void gamesiterate(struct games_state *rs,tetris_game *tg) int tetris(int argc, char **argv) { - tetris_game *tg; struct games_state *rs = &globalR; int32_t c,skipcount=0; uint32_t eventid = 0; memset(rs,0,sizeof(*rs)); @@ -813,7 +813,6 @@ int tetris(int argc, char **argv) // Otherwise create new game. tg = tg_create(rs,22, 10); }*/ - tg = tg_create(rs,22, 10); // NCURSES initialization: initscr(); // initialize curses @@ -825,7 +824,7 @@ int tetris(int argc, char **argv) init_colors(); // setup tetris colors // Game loop - gamesiterate(rs,tg); + gamesiterate(rs); games_bailout(rs); // Deinitialize NCurses wclear(stdscr); From a02e96bc3b1b1c62b6b1ec7006b7a2ae2f3ec0e9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 02:41:16 -1100 Subject: [PATCH 222/787] Free obj --- src/cc/dapps/dappstd.c | 15 ++++++++++----- src/cc/tetris.c | 21 +++++++++++---------- src/cc/tetris.h | 2 +- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index b93e7b41d..1ab3e6427 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -24,7 +24,7 @@ #include extern struct games_state globalR; -void gamesiterate(struct games_state *rs); +void *gamesiterate(struct games_state *rs); char USERPASS[8192]; uint16_t GAMES_PORT; char Gametxidstr[67]; @@ -866,7 +866,7 @@ void games_bailout(struct games_state *rs) int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) { - struct games_state *rs; FILE *fp; int32_t i,n; + struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; rs = (struct games_state *)calloc(1,sizeof(*rs)); rs->seed = seed; rs->keystrokes = keystrokes; @@ -882,7 +882,7 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t nu } globalR = *rs; uint32_t starttime = (uint32_t)time(NULL); - gamesiterate(rs); + ptr = gamesiterate(rs); if ( 0 ) { fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL) - starttime); @@ -909,8 +909,13 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t nu if ( newdata != 0 && rs->playersize > 0 ) memcpy(newdata,rs->playerdata,rs->playersize); }*/ - if ( newdata != 0 && rs->playersize > 0 ) - memcpy(newdata,rs->playerdata,rs->playersize); + if ( ptr != 0 ) + { + // extract data from ptr + if ( newdata != 0 && rs->playersize > 0 ) + memcpy(newdata,rs->playerdata,rs->playersize); + free(ptr); + } n = rs->playersize; free(rs); return(n); diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 98e6a2fab..0f81971c4 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -436,7 +436,7 @@ void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols) // Initialization logic obj->rows = rows; obj->cols = cols; - obj->board = (char *)malloc(rows * cols); + //obj->board = (char *)malloc(rows * cols); memset(obj->board, TC_EMPTY, rows * cols); obj->points = 0; obj->level = 0; @@ -454,19 +454,19 @@ void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols) tetris_game *tg_create(struct games_state *rs,int rows, int cols) { - tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game) + rows*cols); tg_init(rs,obj, rows, cols); return obj; } -void tg_destroy(tetris_game *obj) +/*void tg_destroy(tetris_game *obj) { // Cleanup logic free(obj->board); -} +}*/ void tg_delete(tetris_game *obj) { - tg_destroy(obj); + //tg_destroy(obj); free(obj); } @@ -591,7 +591,7 @@ void display_score(WINDOW *w, tetris_game *tg) /* Save and exit the game. - */ + void save(tetris_game *game, WINDOW *w) { FILE *f; @@ -614,7 +614,7 @@ void save(tetris_game *game, WINDOW *w) fprintf(stderr,"Game saved to \"tetris.save\".\n"); fprintf(stderr,"Resume by passing the filename as an argument to this program.\n"); exit(EXIT_SUCCESS); -} +}*/ /* Do the NCURSES initialization steps for color blocks. @@ -685,7 +685,7 @@ char *clonestr(char *str) struct games_state globalR; -void gamesiterate(struct games_state *rs) +void *gamesiterate(struct games_state *rs) { uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg; @@ -771,12 +771,13 @@ void gamesiterate(struct games_state *rs) move = TM_NONE; } } + return(tg); } int tetris(int argc, char **argv) { struct games_state *rs = &globalR; - int32_t c,skipcount=0; uint32_t eventid = 0; + int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg = 0; memset(rs,0,sizeof(*rs)); rs->guiflag = 1; rs->sleeptime = 1; // non-zero to allow refresh() @@ -824,7 +825,7 @@ int tetris(int argc, char **argv) init_colors(); // setup tetris colors // Game loop - gamesiterate(rs); + tg = gamesiterate(rs); games_bailout(rs); // Deinitialize NCurses wclear(stdscr); diff --git a/src/cc/tetris.h b/src/cc/tetris.h index 40a9c97cb..9fce377d7 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -97,7 +97,6 @@ typedef struct { */ int rows; int cols; - char *board; /* Scoring information: */ @@ -118,6 +117,7 @@ typedef struct { Number of lines until you advance to the next level. */ int lines_remaining; + char board[]; } tetris_game; /* From 44776662d087dd12d3e2d40a4f73adb0ff126ea3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 02:45:09 -1100 Subject: [PATCH 223/787] -load --- src/cc/tetris.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 0f81971c4..23d58e45c 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -472,7 +472,7 @@ void tg_delete(tetris_game *obj) { /* Load a game from a file. - */ + tetris_game *tg_load(FILE *f) { tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); @@ -494,18 +494,18 @@ tetris_game *tg_load(FILE *f) } } return obj; -} +}*/ /* Save a game to a file. - */ + void tg_save(tetris_game *obj, FILE *f) { if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) fprintf(stderr,"error writing tetrisgame\n"); else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) fprintf(stderr,"error writing board\n"); -} +}*/ /* Print a game board to a file. Really just for early debugging. From 6712caf91e3ae2cf1f013e689e419dc72cacdfa8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 02:46:10 -1100 Subject: [PATCH 224/787] Ptr --- src/cc/tetris.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 23d58e45c..305bf0c3b 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -825,7 +825,7 @@ int tetris(int argc, char **argv) init_colors(); // setup tetris colors // Game loop - tg = gamesiterate(rs); + tg = (tetris_game *)gamesiterate(rs); games_bailout(rs); // Deinitialize NCurses wclear(stdscr); From 6ba7bf538c81c86345fb41d70dfbed977b84aa69 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 26 Mar 2019 22:13:14 +0800 Subject: [PATCH 225/787] small fix --- src/cc/rewards.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 2438b4035..73f723453 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -258,10 +258,12 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) return eval->Invalid("txfee is too high"); reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit); - if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 && tx.vout[1].nValue == 10000 ) + if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) { if ( reward == 0 ) return eval->Invalid("unlock recover no rewards"); + else if ( tx.vout[1].nValue != 10000 ) + return eval->Invalid("wrong marker vour value"); else if ( tx.vout[1].scriptPubKey != tx.vout[0].scriptPubKey ) return eval->Invalid("unlock recover tx vout.1 mismatched scriptPubKey"); else if ( tx.vout[0].scriptPubKey != vinTx.vout[1].scriptPubKey ) From b7c8205870d3ada608abbf1d29acb67c43c0eb39 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:14:26 -1100 Subject: [PATCH 226/787] Origseed --- src/cc/dapps/dappstd.c | 65 ++++++++++++++++++++++++++++++--- src/cc/tetris.c | 82 +++++++++++++++++++++++++----------------- src/cc/tetris.h | 2 +- 3 files changed, 111 insertions(+), 38 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 1ab3e6427..f98a3af63 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -805,7 +805,7 @@ int32_t flushkeystrokes_local(struct games_state *rs,int32_t waitflag) #ifdef STANDALONE char fname[1024]; FILE *fp; int32_t i,retflag = -1; rs->counter++; - gamesfname(fname,rs->seed,rs->counter); + gamesfname(fname,rs->origseed,rs->counter); if ( (fp= fopen(fname,"wb")) != 0 ) { if ( fwrite(rs->buffered,1,rs->num,fp) == rs->num ) @@ -813,7 +813,7 @@ int32_t flushkeystrokes_local(struct games_state *rs,int32_t waitflag) rs->num = 0; retflag = 0; fclose(fp); - gamesfname(fname,rs->seed,rs->counter+1); + gamesfname(fname,rs->origseed,rs->counter+1); if ( (fp= fopen(fname,"wb")) != 0 ) // truncate next file fclose(fp); //fprintf(stderr,"savefile <- %s retflag.%d\n",fname,retflag); @@ -844,7 +844,7 @@ int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag) { if ( rs->num > 0 ) { - if ( games_progress(rs,waitflag,rs->seed,rs->buffered,rs->num) > 0 ) + if ( games_progress(rs,waitflag,rs->origseed,rs->buffered,rs->num) > 0 ) { flushkeystrokes_local(rs,waitflag); memset(rs->buffered,0,sizeof(rs->buffered)); @@ -868,7 +868,7 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t nu { struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; rs = (struct games_state *)calloc(1,sizeof(*rs)); - rs->seed = seed; + rs->seed = rs->origseed = seed; rs->keystrokes = keystrokes; rs->numkeys = num; rs->sleeptime = sleepmillis * 1000; @@ -891,7 +891,7 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t nu for (i=0; i<10000; i++) { memset(rs,0,sizeof(*rs)); - rs->seed = seed; + rs->seed = rs->origseed = seed; rs->keystrokes = keystrokes; rs->numkeys = num; rs->sleeptime = 0; @@ -1009,6 +1009,61 @@ int32_t games_replay(uint64_t seed,int32_t sleeptime) return(num); } +char games_readchar(struct rogue_state *rs) +{ + char c,ch = -1; + if ( rs != 0 && rs->guiflag == 0 ) + { + static uint32_t counter; + if ( rs->ind < rs->numkeys ) + { + c = rs->keystrokes[rs->ind++]; + if ( 0 ) + { + static FILE *fp; static int32_t counter; + if ( fp == 0 ) + fp = fopen("log","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); + fflush(fp); + counter++; + } + } + return(c); + } + if ( rs->replaydone != 0 && counter++ < 3 ) + fprintf(stderr,"replay finished but readchar called\n"); + rs->replaydone = (uint32_t)time(NULL); + if ( counter < 3 || (counter & 1) == 0 ) + return('y'); + else return(ESCAPE); + } + if ( rs == 0 || rs->guiflag != 0 ) + { + ch = (char) getch(); + if (ch == 3) + { + _quit(); + return(27); + } + if ( rs != 0 && rs->guiflag != 0 ) + { + if ( rs->num < sizeof(rs->buffered) ) + { + rs->buffered[rs->num++] = ch; + if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) + { + rs->needflush = (uint32_t)time(NULL); + //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); + //sleep(3); + } + } else fprintf(stderr,"buffer filled without flushed\n"); + } + } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); + return(ch); +} + int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) { char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 305bf0c3b..3047e0e55 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -696,40 +696,57 @@ void *gamesiterate(struct games_state *rs) next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); - while (running) + while ( running != 0 ) { - running = tg_tick(rs,tg, move); - display_board(board, tg); - display_piece(next, tg->next); - display_piece(hold, tg->stored); - display_score(score, tg); - if ( (counter++ % 5) == 0 ) - doupdate(); - sleep_milli(10); - c = getch(); - switch ( c ) + running = tg_tick(rs,tg,move); + if ( rs->guiflag != 0 ) { - case KEY_LEFT: - c = 'h'; - break; - case KEY_RIGHT: - c = 'l'; - break; - case KEY_UP: - c = 'k'; - break; - case KEY_DOWN: - c = 'j'; - break; + display_board(board,tg); + display_piece(next,tg->next); + display_piece(hold,tg->stored); + display_score(score,tg); + if ( (counter++ % 5) == 0 ) + doupdate(); + sleep_milli(10); + c = games_readchar(rs); + switch ( c ) + { + case KEY_LEFT: + c = 'h'; + break; + case KEY_RIGHT: + c = 'l'; + break; + case KEY_UP: + c = 'k'; + break; + case KEY_DOWN: + c = 'j'; + break; + } + if ( c < 0 || skipcount == 0x7f ) + { + if ( skipcount > 0 ) + issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x80); + if ( c != -1 ) + issue_games_events(rs,Gametxidstr,eventid,c); + skipcount = 0; + } else skipcount++; } - if ( c < 0 || skipcount == 0x7f ) + else { + if ( skipcount == 0 ) + { + c = games_readchar(rs); + if ( (c & 0x80) != 0 ) + { + skipcount = (c & 0x7f); + c = 'S'; + } + } if ( skipcount > 0 ) - issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x80); - if ( c != -1 ) - issue_games_events(rs,Gametxidstr,eventid,c); - skipcount = 0; - } else skipcount++; + skipcount--; + } eventid++; switch ( c ) { @@ -785,13 +802,14 @@ int tetris(int argc, char **argv) { #ifdef _WIN32 #ifdef _MSC_VER - rs->seed = _strtoui64(argv[1], NULL, 10); + rs->origseed = _strtoui64(argv[1], NULL, 10); #else - rs->seed = atol(argv[1]); // windows, but not MSVC + rs->origseed = atol(argv[1]); // windows, but not MSVC #endif // _MSC_VER #else - rs->seed = atol(argv[1]); // non-windows + rs->origseed = atol(argv[1]); // non-windows #endif // _WIN32 + rs->seed = rs->origseed; strcpy(Gametxidstr,argv[2]); fprintf(stderr,"setplayerdata\n"); if ( games_setplayerdata(rs,Gametxidstr) < 0 ) diff --git a/src/cc/tetris.h b/src/cc/tetris.h index 9fce377d7..317dad62b 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -182,7 +182,7 @@ struct games_player struct games_state { - uint64_t seed; + uint64_t seed,origseed; char *keystrokes,*keystrokeshex; uint32_t needflush,replaydone; int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; From 81263613bb334f39325a4c46bcae4b3cd8eceded Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:15:11 -1100 Subject: [PATCH 227/787] Games --- src/cc/dapps/dappstd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index f98a3af63..714bcaf82 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -1009,7 +1009,7 @@ int32_t games_replay(uint64_t seed,int32_t sleeptime) return(num); } -char games_readchar(struct rogue_state *rs) +char games_readchar(struct games_state *rs) { char c,ch = -1; if ( rs != 0 && rs->guiflag == 0 ) From d223563eff2e4595e0fa11102d4b7c429b76ffa6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:16:36 -1100 Subject: [PATCH 228/787] Test --- src/cc/dapps/dappstd.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 714bcaf82..0f3d8cc24 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -1035,16 +1035,14 @@ char games_readchar(struct games_state *rs) if ( rs->replaydone != 0 && counter++ < 3 ) fprintf(stderr,"replay finished but readchar called\n"); rs->replaydone = (uint32_t)time(NULL); - if ( counter < 3 || (counter & 1) == 0 ) - return('y'); - else return(ESCAPE); + return(0); } if ( rs == 0 || rs->guiflag != 0 ) { ch = (char) getch(); if (ch == 3) { - _quit(); + //_quit(); return(27); } if ( rs != 0 && rs->guiflag != 0 ) From a4103580310f3648eecf57747a5b6ed1b371066d Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Tue, 26 Mar 2019 15:36:10 +0100 Subject: [PATCH 229/787] Correct GatewaysCC validation to check spending from global address. (#19) - Add additional vin/vout check - Check that marker spending from global CC address is only for a marker value - Set fixed marker values instead of depending on txfee --- src/cc/gateways.cpp | 163 +++++++++++++++++++++++--------------------- 1 file changed, 85 insertions(+), 78 deletions(-) diff --git a/src/cc/gateways.cpp b/src/cc/gateways.cpp index f0c8735e2..4993e1078 100644 --- a/src/cc/gateways.cpp +++ b/src/cc/gateways.cpp @@ -152,6 +152,7 @@ #define KMD_P2SHTYPE 85 #define KMD_WIFTYPE 188 #define KMD_TADDR 0 +#define CC_MARKER_VALUE 10000 CScript EncodeGatewaysBindOpRet(uint8_t funcid,uint256 tokenid,std::string coin,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector gatewaypubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2,uint8_t wiftype) { @@ -185,7 +186,7 @@ uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,ui if ( N > 1 ) { strcpy(depositaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,gatewaypubkeys))).ToString().c_str()); - LOGSTREAM("gatewayscc", CCLOG_DEBUG1, stream << "f." << f << " M." << M << " of N." << N << " size." << (int32_t)gatewaypubkeys.size() << " -> " << depositaddr << std::endl); + LOGSTREAM("gatewayscc", CCLOG_DEBUG1, stream << "f." << f << " M." << (int)M << " of N." << (int)N << " size." << (int32_t)gatewaypubkeys.size() << " -> " << depositaddr << std::endl); } else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(gatewaypubkeys[0])) << OP_CHECKSIG); } else @@ -600,7 +601,7 @@ int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks,height,claimvout; bool retval; uint8_t funcid,hash[32],K,M,N,taddr,prefix,prefix2,wiftype; - char str[65],destaddr[64],depositaddr[65],validationError[512]; + char str[65],destaddr[65],depositaddr[65],gatewaystokensaddr[65],validationError[512]; std::vector txids; std::vector pubkeys,publishers,tmppublishers; std::vector proof; int64_t fullsupply,totalsupply,amount,tmpamount; uint256 hashblock,txid,bindtxid,deposittxid,withdrawtxid,completetxid,tokenid,tmptokenid,oracletxid,bindtokenid,cointxid,tmptxid,merkleroot,mhash; CTransaction bindtx,tmptx; std::string refcoin,tmprefcoin,hex,name,description,format; CPubKey pubkey,tmppubkey,gatewayspk; @@ -619,7 +620,8 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & // } // else // { - gatewayspk = GetUnspendable(cp,0); + gatewayspk = GetUnspendable(cp,0); + GetTokensCCaddress(cp, gatewaystokensaddr, gatewayspk); if ( (funcid = DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)) != 0) { switch ( funcid ) @@ -628,14 +630,14 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & //vin.0: normal input //vin.1: CC input of tokens //vout.0: CC vout of gateways tokens to gateways tokens CC address - //vout.1: CC vout txfee marker + //vout.1: CC vout marker //vout.n-1: opreturn - 'B' tokenid coin totalsupply oracletxid M N pubkeys taddr prefix prefix2 wiftype return eval->Invalid("unexpected GatewaysValidate for gatewaysbind!"); break; case 'D': //vin.0: normal input - //vout.0: CC vout txfee marker to destination pubkey - //vout.1: normal output txfee marker to txidaddr + //vout.0: CC vout marker to destination pubkey + //vout.1: normal output marker to txidaddr //vout.n-1: opreturn - 'D' bindtxid coin publishers txids height cointxid claimvout deposithex proof destpub amount return eval->Invalid("unexpected GatewaysValidate for gatewaysdeposit!"); break; @@ -647,19 +649,19 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & //vout.1: CC vout change of gateways tokens to gateways tokens CC address (if any) //vout.n-1: opreturn - 'C' tokenid bindtxid coin deposittxid destpub amount if ((numvouts=tx.vout.size()) < 1 || DecodeGatewaysClaimOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,refcoin,deposittxid,pubkey,amount)!='C') - return eval->Invalid("invalid gatewaysClaim OP_RETURN data!"); - else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for gatewaysClaim!"); - else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for gatewaysClaim!"); - else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for gatewaysClaim!"); - else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.0 is CC for gatewaysClaim!"); + return eval->Invalid("invalid gatewaysclaim OP_RETURN data!"); else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) return eval->Invalid("invalid gatewaysbind txid!"); else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysbind!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysbind!"); + else if ( ConstrainVout(tmptx.vout[0],1,gatewaystokensaddr,totalsupply)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysbind!"); + else if ( ConstrainVout(tmptx.vout[1],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysbind!"); else if (tmprefcoin!=refcoin) return eval->Invalid("refcoin different than in bind tx"); else if (tmptokenid!=tokenid) @@ -697,6 +699,12 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & return eval->Invalid("invalid gatewaysdeposittxid!"); else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysDepositOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptxid,tmprefcoin,tmppublishers,txids,height,cointxid,claimvout,hex,proof,tmppubkey,tmpamount) != 'D') return eval->Invalid("invalid gatewaysdeposit OP_RETURN data!"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysdeposit!"); + else if ( GetCCaddress(cp,destaddr,tmppubkey)==0 || ConstrainVout(tmptx.vout[0],1,destaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid CC marker vout for gatewaysdeposit!"); + else if ( CCtxidaddr(destaddr,cointxid)==CPubKey() || ConstrainVout(tmptx.vout[1],0,destaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid normal marker vout for gatewaysdeposit!"); else if (tmprefcoin!=refcoin) return eval->Invalid("refcoin different than in deposit tx"); else if (bindtxid!=tmptxid) @@ -705,6 +713,18 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & return eval->Invalid("deposit amount greater then bind total supply"); else if (komodo_txnotarizedconfirmed(deposittxid) == false) return eval->Invalid("gatewaysdeposit tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(tx.vin[2].prevout.hash,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysdeposittxid!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewaysclaim!"); + else if (IsCCInput(tx.vin[1].scriptSig) == 0) + return eval->Invalid("vin.1 is CC for gatewaysclaim!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || myGetTransaction(tx.vin[2].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker for gatewaysclaim or invalid marker amount!"); + else if (_GetCCaddress(destaddr,EVAL_TOKENS,pubkey)==0 || ConstrainVout(tx.vout[0],1,destaddr,amount)==0) + return eval->Invalid("invalid vout tokens to destpub for gatewaysclaim!"); + else if (numvouts>2 && (myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || ConstrainVout(tx.vout[1],1,gatewaystokensaddr,tmptx.vout[tx.vin[1].prevout.n].nValue-amount)==0)) + return eval->Invalid("invalid CC change vout for gatewaysclaim!"); else if (amount!=tmpamount) return eval->Invalid("claimed amount different then deposit amount"); else if (tx.vout[0].nValue!=amount) @@ -739,7 +759,7 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & case 'W': //vin.0: normal input //vin.1: CC input of tokens - //vout.0: CC vout txfee marker to gateways CC address + //vout.0: CC vout marker to gateways CC address //vout.1: CC vout of gateways tokens back to gateways tokens CC address //vout.2: CC vout change of tokens back to owners pubkey (if any) //vout.n-1: opreturn - 'W' tokenid bindtxid refcoin withdrawpub amount @@ -748,16 +768,10 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & case 'P': //vin.0: normal input //vin.1: CC input of marker from previous tx (withdraw or partialsing) - //vout.0: CC vout txfee marker to gateways CC address + //vout.0: CC vout marker to gateways CC address //vout.n-1: opreturn - 'P' withdrawtxid refcoin number_of_signs mypk hex if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,pubkey,hex)!='P') - return eval->Invalid("invalid gatewaysPartialSign OP_RETURN data!"); - else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for gatewaysPartialSign!"); - else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for gatewaysPartialSign!"); - else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.0 is CC for gatewaysPartialSign!"); + return eval->Invalid("invalid gatewaysPartialSign OP_RETURN data!"); else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) return eval->Invalid("invalid withdraw txid!"); else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W') @@ -768,10 +782,10 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); - else if ( tmptx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.0 is CC for gatewaysWithdraw!"); - else if ( tmptx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.1 is CC for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); else if (tmptx.vout[1].nValue!=amount) return eval->Invalid("amount in opret not matching tx tokens amount!"); else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) @@ -785,23 +799,23 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & else if (tmptokenid!=tokenid) return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); else if (komodo_txnotarizedconfirmed(bindtxid) == false) - return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayspartialsign!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewayspartialsign or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayspartialsign!"); else if (K>M) return eval->Invalid("invalid number of signs!"); break; case 'S': //vin.0: normal input //vin.1: CC input of marker from previous tx (withdraw or partialsing) - //vout.0: CC vout txfee marker to gateways CC address + //vout.0: CC vout marker to gateways CC address //vout.n-1: opreturn - 'S' withdrawtxid refcoin hex if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,hex)!='S') return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); - else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for gatewayscompletesigning!"); - else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for gatewayscompletesigning!"); - else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.0 is CC for gatewayscompletesigning!"); else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) return eval->Invalid("invalid withdraw txid!"); else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W') @@ -812,10 +826,10 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); - else if ( tmptx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.0 is CC for gatewaysWithdraw!"); - else if ( tmptx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.1 is CC for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); else if (tmptx.vout[1].nValue!=amount) return eval->Invalid("amount in opret not matching tx tokens amount!"); else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) @@ -829,21 +843,22 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & else if (tmptokenid!=tokenid) return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); else if (komodo_txnotarizedconfirmed(bindtxid) == false) - return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayscompletesigning!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewayscompletesigning or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayscompletesigning!"); else if (KInvalid("invalid number of signs!"); break; case 'M': - //vin.0: CC input of gatewayscompletesigning tx marker to gateways CC address + //vin.0: normal input + //vin.1: CC input of gatewayscompletesigning tx marker to gateways CC address //vout.0: opreturn - 'M' withdrawtxid refcoin completetxid if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysMarkDoneOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,completetxid)!='M') - return eval->Invalid("invalid gatewaysmarkdone OP_RETURN data!"); - else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for gatewaysmarkdone!"); - else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for gatewaysmarkdone!"); - else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 ) - return eval->Invalid("vout.0 is normal for gatewaysmarkdone!"); + return eval->Invalid("invalid gatewaysmarkdone OP_RETURN data!"); else if (myGetTransaction(completetxid,tmptx,hashblock) == 0) return eval->Invalid("invalid gatewayscompletesigning txid!"); else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysCompleteSigningOpRet(tmptx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmprefcoin,K,hex)!='S') @@ -867,7 +882,11 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & else if (tmptokenid!=tokenid) return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); else if (komodo_txnotarizedconfirmed(bindtxid) == false) - return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysmarkdone!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewaysmarkdone or invalid marker amount!"); else if (KInvalid("invalid number of signs!"); break; @@ -907,29 +926,17 @@ int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { funcid=DecodeGatewaysOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey); - if (vout==0 && funcid=='B' && bindtxid==txid && total != 0 && maxinputs != 0) + if ((vout==0 && funcid=='B' && bindtxid==txid && total != 0 && maxinputs != 0) || + (vout==1 && funcid=='W' && DecodeGatewaysWithdrawOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,withdrawpub,amount) == 'W' && + tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0) || + (vout==1 && funcid=='C' && DecodeGatewaysClaimOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,deposittxid,destpub,amount) == 'C' && + tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0)) { mtx.vin.push_back(CTxIn(txid,vout,CScript())); totalinputs += it->second.satoshis; n++; if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; - } - else if (vout==1 && funcid=='W' && DecodeGatewaysWithdrawOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,withdrawpub,amount) == 'W' && - tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0) - { - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - totalinputs += it->second.satoshis; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; - } - else if (vout==1 && funcid=='C' && DecodeGatewaysClaimOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,deposittxid,destpub,amount) == 'C' && - tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0) - { - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - totalinputs += it->second.satoshis; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; - } + } } } return(totalinputs); @@ -1033,12 +1040,12 @@ std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0 ) { if (AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, totalsupply, 64)>0) { mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,totalsupply,gatewayspk)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,gatewayspk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,gatewayspk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysBindOpRet('B',tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype))); } } @@ -1112,10 +1119,10 @@ std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std:: LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( AddNormalinputs(mtx,mypk,3*txfee,4) > 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,4) > 0 ) { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,destpub)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,destpub)); + mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysDepositOpRet('D',bindtxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,destpub,amount))); } CCerror = strprintf("cant find enough inputs"); @@ -1262,12 +1269,12 @@ std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin } } } - if( AddNormalinputs(mtx, mypk, 3*txfee, 4) > 0 ) + if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE, 4) > 0 ) { if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, amount, 60)) > 0) { if ( inputs > amount ) CCchange = (inputs - amount); - mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,txfee,gatewayspk)); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); mtx.vout.push_back(MakeTokensCC1vout(EVAL_GATEWAYS,amount,gatewayspk)); if ( CCchange != 0 ) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); return(FinalizeCCTx(0, cpTokens, mtx, mypk, txfee,EncodeGatewaysWithdrawOpRet('W',tokenid,bindtxid,refcoin,withdrawpub,amount))); @@ -1377,7 +1384,7 @@ std::string GatewaysPartialSign(uint64_t txfee,uint256 lasttxid,std::string refc if (AddNormalinputs(mtx,mypk,txfee,3)!=0) { mtx.vin.push_back(CTxIn(tx.GetHash(),0,CScript())); - mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,txfee,gatewayspk)); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysPartialOpRet('P',withdrawtxid,refcoin,K+1,mypk,hex))); } CCerror = strprintf("error adding funds for partialsign"); @@ -1476,7 +1483,7 @@ std::string GatewaysCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string if (AddNormalinputs(mtx,mypk,txfee,3)!=0) { mtx.vin.push_back(CTxIn(lasttxid,0,CScript())); - mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,txfee,gatewayspk)); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysCompleteSigningOpRet('S',withdrawtxid,refcoin,K+1,hex))); } CCerror = strprintf("error adding funds for completesigning"); @@ -1541,7 +1548,7 @@ std::string GatewaysMarkDone(uint64_t txfee,uint256 completetxid,std::string ref if (AddNormalinputs(mtx,mypk,txfee,3)!=0) { mtx.vin.push_back(CTxIn(completetxid,0,CScript())); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysMarkDoneOpRet('M',withdrawtxid,refcoin,completetxid))); } CCerror = strprintf("error adding funds for markdone"); From 80085d9d390de918412418dff05e64121b55b8aa Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:38:25 -1100 Subject: [PATCH 230/787] Fund --- src/cc/gamescc.cpp | 37 +++++++++++++++++++++++++++++++++++++ src/cc/gamescc.h | 2 ++ src/cc/tetris.c | 2 +- 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 367c8d96e..a361b43ba 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1443,5 +1443,42 @@ UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } +UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; CPubKey gamespk,mypk; std::vector opret; + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) + { + amount = jdouble(jitem(params,1),0) * COIN + 0.0000000049; + gamespk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + if ( amount > GAMES_TXFEE ) + { + if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) + { + mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,amount,gamespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,opret); + return(games_rawtxresult(result,rawtx,1)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","amount too small")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parse")); + } + return(result); +} + #endif diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 6ec5dc9c4..a61b07564 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -33,6 +33,7 @@ std::string Games_pname; #define RPC_FUNCS \ { (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"fund", (char *)"amount", 1, 1, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"players", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"games", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"pending", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ @@ -49,6 +50,7 @@ std::string Games_pname; bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 3047e0e55..fe042d28c 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -811,7 +811,7 @@ int tetris(int argc, char **argv) #endif // _WIN32 rs->seed = rs->origseed; strcpy(Gametxidstr,argv[2]); - fprintf(stderr,"setplayerdata\n"); + fprintf(stderr,"setplayerdata %s\n",Gametxidstr); if ( games_setplayerdata(rs,Gametxidstr) < 0 ) { fprintf(stderr,"invalid gametxid, or already started\n"); From 84023b1540678bd5bb3be354295ff6b2e58177b0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:40:55 -1100 Subject: [PATCH 231/787] Script --- src/cc/gamescc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index a361b43ba..41e571c15 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1446,8 +1446,8 @@ UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; CPubKey gamespk,mypk; std::vector opret; - if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; CPubKey gamespk,mypk; CScript opret; + if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { amount = jdouble(jitem(params,1),0) * COIN + 0.0000000049; gamespk = GetUnspendable(cp,0); From e954ca48c7df58953263d5affaf1bd805686a795 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:42:56 -1100 Subject: [PATCH 232/787] Enable fund --- src/cc/gamescc.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index a61b07564..e3bd15ea7 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -95,6 +95,8 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_extract(txfee,cp,params)); \ else if ( strcmp(method,"finish") == 0 ) \ return(games_finish(txfee,cp,params)); \ + else if ( strcmp(method,"fund") == 0 ) \ + return(games_fund(txfee,cp,params)); \ else \ { \ result.push_back(Pair("result","error")); \ From 62731f9972a0a88c9b7702bbb7d4ca6bb833c6ad Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:44:39 -1100 Subject: [PATCH 233/787] params0 --- src/cc/gamescc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 41e571c15..9be6426ea 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1449,7 +1449,7 @@ UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; CPubKey gamespk,mypk; CScript opret; if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { - amount = jdouble(jitem(params,1),0) * COIN + 0.0000000049; + amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049; gamespk = GetUnspendable(cp,0); mypk = pubkey2pk(Mypubkey()); if ( amount > GAMES_TXFEE ) From fc3753d23b96782459aab4663a8543f6713b6a66 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:50:45 -1100 Subject: [PATCH 234/787] +print --- src/cc/gamescc.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 9be6426ea..5ac8fc5c7 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1457,6 +1457,11 @@ UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) { mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,amount,gamespk)); + { + char destaddr[64]; + GetScriptaddress(destaddr,mtx.vout[0].scriptPubKey); + fprintf(stderr,"destaddr.(%s) %d\n",destaddr,cp->evalcode); + } rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,opret); return(games_rawtxresult(result,rawtx,1)); } From 00e6b83927e172ca8204778e800c4059d0bc9e8f Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:51:19 -1100 Subject: [PATCH 235/787] Fix --- src/cc/gamescc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 5ac8fc5c7..2f48a2c3c 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1456,7 +1456,8 @@ UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) { - mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,amount,gamespk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); + if ( 0 ) { char destaddr[64]; GetScriptaddress(destaddr,mtx.vout[0].scriptPubKey); From f3c31104f7b62483bd1bfbe76e107a870128df34 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 03:51:35 -1100 Subject: [PATCH 236/787] Test --- src/cc/gamescc.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 2f48a2c3c..2f5c031c5 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1457,12 +1457,6 @@ UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) { mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); - if ( 0 ) - { - char destaddr[64]; - GetScriptaddress(destaddr,mtx.vout[0].scriptPubKey); - fprintf(stderr,"destaddr.(%s) %d\n",destaddr,cp->evalcode); - } rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,opret); return(games_rawtxresult(result,rawtx,1)); } From 4b5da75d7da26636917cc757d3f01932505e96f4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 04:10:41 -1100 Subject: [PATCH 237/787] Fix gameloop --- src/cc/tetris.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index fe042d28c..d09116881 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -724,11 +724,11 @@ void *gamesiterate(struct games_state *rs) c = 'j'; break; } - if ( c < 0 || skipcount == 0x7f ) + if ( c >= 0 || skipcount == 0x7f ) { if ( skipcount > 0 ) issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x80); - if ( c != -1 ) + if ( c >= 0 ) issue_games_events(rs,Gametxidstr,eventid,c); skipcount = 0; } else skipcount++; From b4043bc6bb0c58cb6ee7d0cc33cdc1f2d5ac6bed Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 04:17:43 -1100 Subject: [PATCH 238/787] Scrub vals --- src/cc/dapps/dappstd.c | 15 +++++++++++++++ src/cc/tetris.c | 20 +++----------------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 0f3d8cc24..267335b05 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -1040,6 +1040,21 @@ char games_readchar(struct games_state *rs) if ( rs == 0 || rs->guiflag != 0 ) { ch = (char) getch(); + switch ( ch ) + { + case KEY_LEFT: + c = 'h'; + break; + case KEY_RIGHT: + c = 'l'; + break; + case KEY_UP: + c = 'k'; + break; + case KEY_DOWN: + c = 'j'; + break; + } if (ch == 3) { //_quit(); diff --git a/src/cc/tetris.c b/src/cc/tetris.c index d09116881..5ddb10ca4 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -299,9 +299,11 @@ static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move { switch (move) { case TM_LEFT: + fprintf(stderr,"LEFT "); tg_move(obj, -1); break; case TM_RIGHT: + fprintf(stderr,"RIGHT "); tg_move(obj, 1); break; case TM_DROP: @@ -644,8 +646,7 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; if ( fp == 0 ) fp = fopen("events.log","wb"); - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c,gametxidstr,eventid); - rs->buffered[rs->num++] = c; + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) @@ -709,21 +710,6 @@ void *gamesiterate(struct games_state *rs) doupdate(); sleep_milli(10); c = games_readchar(rs); - switch ( c ) - { - case KEY_LEFT: - c = 'h'; - break; - case KEY_RIGHT: - c = 'l'; - break; - case KEY_UP: - c = 'k'; - break; - case KEY_DOWN: - c = 'j'; - break; - } if ( c >= 0 || skipcount == 0x7f ) { if ( skipcount > 0 ) From 510689b9541daa870f0b136502ef663af755ab31 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 04:18:59 -1100 Subject: [PATCH 239/787] Necroses --- src/cc/dapps/dappstd.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 267335b05..80b651b45 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -22,6 +22,7 @@ #include #include #include +#include extern struct games_state globalR; void *gamesiterate(struct games_state *rs); From aeba5e4b071ce0a470bd007f084a417d1572a607 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 04:20:20 -1100 Subject: [PATCH 240/787] Fetch --- src/cc/dapps/dappstd.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 80b651b45..a5ef379db 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -22,7 +22,6 @@ #include #include #include -#include extern struct games_state globalR; void *gamesiterate(struct games_state *rs); @@ -1012,7 +1011,7 @@ int32_t games_replay(uint64_t seed,int32_t sleeptime) char games_readchar(struct games_state *rs) { - char c,ch = -1; + char ch = -1; int32_t c; if ( rs != 0 && rs->guiflag == 0 ) { static uint32_t counter; @@ -1040,8 +1039,8 @@ char games_readchar(struct games_state *rs) } if ( rs == 0 || rs->guiflag != 0 ) { - ch = (char) getch(); - switch ( ch ) + c = getch(); + switch ( c ) { case KEY_LEFT: c = 'h'; @@ -1056,6 +1055,7 @@ char games_readchar(struct games_state *rs) c = 'j'; break; } + ch = c; if (ch == 3) { //_quit(); @@ -1065,7 +1065,7 @@ char games_readchar(struct games_state *rs) { if ( rs->num < sizeof(rs->buffered) ) { - rs->buffered[rs->num++] = ch; + rs->buffered[rs->num++] = c; if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) { rs->needflush = (uint32_t)time(NULL); @@ -1075,7 +1075,7 @@ char games_readchar(struct games_state *rs) } else fprintf(stderr,"buffer filled without flushed\n"); } } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); - return(ch); + return(c); } int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) From e96b3bf96ae29acd188baa7f336313c8e48c9d9e Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 04:34:08 -1100 Subject: [PATCH 241/787] Gamesevent --- src/cc/dapps/dappstd.c | 43 +++++++++++++++++++++++++----------------- src/cc/tetris.c | 18 +++++++++--------- src/cc/tetris.cpp | 6 +++--- src/cc/tetris.h | 8 +++++--- 4 files changed, 43 insertions(+), 32 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index a5ef379db..05f4fe332 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -249,7 +249,7 @@ int32_t safecopy(char *dest,char *src,long len) //#endif int32_t games_replay(uint64_t seed,int32_t sleeptime); -char *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter); +gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter); int GAMEMAIN(int argc, char **argv); @@ -735,9 +735,9 @@ int32_t games_sendrawtransaction(char *rawtx) return(retval); } -int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,gamesevent *keystrokes,int32_t num) { - char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; uint8_t *pastcmp; + char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx; int32_t i,len,retflag = -1; cJSON *retjson,*resobj; if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) { if ( rs->keystrokeshex != 0 ) @@ -757,9 +757,18 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,cha } free(rs->keystrokeshex), rs->keystrokeshex = 0; } + memset(hexstr,0,sizeof(hexstr)); for (i=0; iorigseed,rs->counter); if ( (fp= fopen(fname,"wb")) != 0 ) { - if ( fwrite(rs->buffered,1,rs->num,fp) == rs->num ) + if ( fwrite(rs->buffered,sizeof(*rs->buffered),rs->num,fp) == rs->num ) { rs->num = 0; retflag = 0; @@ -864,7 +873,7 @@ void games_bailout(struct games_state *rs) #endif #endif -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) +int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) { struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; rs = (struct games_state *)calloc(1,sizeof(*rs)); @@ -930,9 +939,9 @@ long get_filesize(FILE *fp) return(fsize); } -char *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter) +gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter) { - char fname[1024],*keystrokes = 0; FILE *fp; long fsize; int32_t num = 0; + char fname[1024]; gamesevent *keystrokes = 0; FILE *fp; long fsize; int32_t num = 0; *numkeysp = 0; while ( 1 ) { @@ -946,7 +955,7 @@ char *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter) //printf("fsize.%ld\n",fsize); break; } - if ( (keystrokes= (char *)realloc(keystrokes,num+fsize)) == 0 ) + if ( (keystrokes= (char *)realloc(keystrokes,sizeof(*keystrokes)*(num+fsize))) == 0 ) { fprintf(stderr,"error reallocating keystrokes\n"); fclose(fp); @@ -983,7 +992,7 @@ void games_exit() int32_t games_replay(uint64_t seed,int32_t sleeptime) { - FILE *fp; char fname[1024]; char *keystrokes = 0; long fsize; int32_t i,num=0,counter = 0; struct games_state *rs; struct games_player P,*player = 0; + FILE *fp; char fname[1024]; gamesevent *keystrokes = 0; long fsize; int32_t i,num=0,counter = 0; struct games_state *rs; struct games_player P,*player = 0; if ( seed == 0 ) seed = 777; keystrokes = games_keystrokesload(&num,seed,counter); @@ -1009,15 +1018,15 @@ int32_t games_replay(uint64_t seed,int32_t sleeptime) return(num); } -char games_readchar(struct games_state *rs) +gamesevent games_readevent(struct games_state *rs) { - char ch = -1; int32_t c; + gamesevent ch = -1; int32_t c; if ( rs != 0 && rs->guiflag == 0 ) { static uint32_t counter; if ( rs->ind < rs->numkeys ) { - c = rs->keystrokes[rs->ind++]; + ch = rs->keystrokes[rs->ind++]; if ( 0 ) { static FILE *fp; static int32_t counter; @@ -1030,7 +1039,7 @@ char games_readchar(struct games_state *rs) counter++; } } - return(c); + return(ch); } if ( rs->replaydone != 0 && counter++ < 3 ) fprintf(stderr,"replay finished but readchar called\n"); @@ -1065,7 +1074,7 @@ char games_readchar(struct games_state *rs) { if ( rs->num < sizeof(rs->buffered) ) { - rs->buffered[rs->num++] = c; + rs->buffered[rs->num++] = ch; if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) { rs->needflush = (uint32_t)time(NULL); @@ -1075,7 +1084,7 @@ char games_readchar(struct games_state *rs) } else fprintf(stderr,"buffer filled without flushed\n"); } } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); - return(c); + return(ch); } int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 5ddb10ca4..048a701f5 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -640,13 +640,13 @@ void init_colors(void) */ #include "dapps/dappstd.c" -int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,char c) +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,int16_t c) { static FILE *fp; char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; if ( fp == 0 ) fp = fopen("events.log","wb"); - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) @@ -689,7 +689,7 @@ struct games_state globalR; void *gamesiterate(struct games_state *rs) { uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; - int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg; + gamesevent c; uint16_t skipcount=0; uint32_t eventid = 0; tetris_game *tg; WINDOW *board, *next, *hold, *score; // Create windows for each section of the interface. tg = tg_create(rs,22, 10); @@ -709,11 +709,11 @@ void *gamesiterate(struct games_state *rs) if ( (counter++ % 5) == 0 ) doupdate(); sleep_milli(10); - c = games_readchar(rs); - if ( c >= 0 || skipcount == 0x7f ) + c = games_readevent(rs); + if ( c >= 0 || skipcount == 0x3fff ) { if ( skipcount > 0 ) - issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x80); + issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); if ( c >= 0 ) issue_games_events(rs,Gametxidstr,eventid,c); skipcount = 0; @@ -723,10 +723,10 @@ void *gamesiterate(struct games_state *rs) { if ( skipcount == 0 ) { - c = games_readchar(rs); - if ( (c & 0x80) != 0 ) + c = games_readevent(rs); + if ( (c & 0x4000) == 0x4000 ) { - skipcount = (c & 0x7f); + skipcount = (c & 0x3fff); c = 'S'; } } diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index f9e4e167a..a20652012 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -42,9 +42,9 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay eventid |= (uint32_t)payload[len+33] << 8; eventid |= (uint32_t)payload[len+34] << 16; eventid |= (uint32_t)payload[len+35] << 24; - for (i=0; i Date: Tue, 26 Mar 2019 04:35:25 -1100 Subject: [PATCH 242/787] Gamesevent --- src/cc/dapps/dappstd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 05f4fe332..55ffb33be 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -767,7 +767,7 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,gam else if ( sizeof(gamesevent) == 4 ) sprintf(&hexstr[i<<3],"%08x",keystrokes[i]&0xffffffff); else if ( sizeof(gamesevent) == 8 ) - sprintf(&hexstr[i<<4],"%016x",keystrokes[i]&0xffffffffffffffff); + sprintf(&hexstr[i<<4],"%016x",(long long)(keystrokes[i]&0xffffffffffffffffLL)); } static FILE *fp; if ( fp == 0 ) @@ -955,7 +955,7 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter //printf("fsize.%ld\n",fsize); break; } - if ( (keystrokes= (char *)realloc(keystrokes,sizeof(*keystrokes)*(num+fsize))) == 0 ) + if ( (keystrokes= (gamesevent *)realloc(keystrokes,sizeof(*keystrokes)*(num+fsize))) == 0 ) { fprintf(stderr,"error reallocating keystrokes\n"); fclose(fp); From 297794b25367e2140aa5700edc8c0bd2ded736d8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 04:36:01 -1100 Subject: [PATCH 243/787] Llx --- src/cc/dapps/dappstd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 55ffb33be..e120787bf 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -767,7 +767,7 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,gam else if ( sizeof(gamesevent) == 4 ) sprintf(&hexstr[i<<3],"%08x",keystrokes[i]&0xffffffff); else if ( sizeof(gamesevent) == 8 ) - sprintf(&hexstr[i<<4],"%016x",(long long)(keystrokes[i]&0xffffffffffffffffLL)); + sprintf(&hexstr[i<<4],"%016llx",(long long)(keystrokes[i]&0xffffffffffffffffLL)); } static FILE *fp; if ( fp == 0 ) From 4aed5b5b98c3a28924b7568d84cb803ee7c5a629 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 04:46:22 -1100 Subject: [PATCH 244/787] Uint check --- src/cc/tetris.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 048a701f5..6541d61e5 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -710,7 +710,7 @@ void *gamesiterate(struct games_state *rs) doupdate(); sleep_milli(10); c = games_readevent(rs); - if ( c >= 0 || skipcount == 0x3fff ) + if ( c <= 0x7f || skipcount == 0x3fff ) { if ( skipcount > 0 ) issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); From 0642013b11d5083ad953a88bfbecba447021fccc Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:02:16 -1100 Subject: [PATCH 245/787] Skip -1 --- src/cc/dapps/dappstd.c | 4 ++-- src/cc/tetris.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index e120787bf..4bd116235 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -1070,7 +1070,7 @@ gamesevent games_readevent(struct games_state *rs) //_quit(); return(27); } - if ( rs != 0 && rs->guiflag != 0 ) + /*if ( rs != 0 && rs->guiflag != 0 ) { if ( rs->num < sizeof(rs->buffered) ) { @@ -1082,7 +1082,7 @@ gamesevent games_readevent(struct games_state *rs) //sleep(3); } } else fprintf(stderr,"buffer filled without flushed\n"); - } + }*/ } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); return(ch); } diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 6541d61e5..19aa0467b 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -640,13 +640,21 @@ void init_colors(void) */ #include "dapps/dappstd.c" -int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,int16_t c) +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) { static FILE *fp; char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; if ( fp == 0 ) fp = fopen("events.log","wb"); - sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + rs->buffered[rs->num++] = c; + if ( sizeof(c) == 1 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + else if ( sizeof(c) == 2 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + else if ( sizeof(c) == 4 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); + else if ( sizeof(c) == 8 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) From bb97066b46ce6958b59a5df84eeced24070ded82 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:15:09 -1100 Subject: [PATCH 246/787] replay2 --- src/cc/dapps/dappstd.c | 57 ------------------------------------------ src/cc/gamescc.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++ src/cc/tetris.cpp | 6 ++--- 3 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 4bd116235..bac8cd50c 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -873,63 +873,6 @@ void games_bailout(struct games_state *rs) #endif #endif -int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) -{ - struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; - rs = (struct games_state *)calloc(1,sizeof(*rs)); - rs->seed = rs->origseed = seed; - rs->keystrokes = keystrokes; - rs->numkeys = num; - rs->sleeptime = sleepmillis * 1000; - if ( player != 0 ) - { - rs->P = *player; - rs->restoring = 1; - //fprintf(stderr,"restore player packsize.%d HP.%d\n",rs->P.packsize,rs->P.hitpoints); - if ( rs->P.packsize > MAXPACK ) - rs->P.packsize = MAXPACK; - } - globalR = *rs; - uint32_t starttime = (uint32_t)time(NULL); - ptr = gamesiterate(rs); - if ( 0 ) - { - fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL) - starttime); - sleep(2); - starttime = (uint32_t)time(NULL); - for (i=0; i<10000; i++) - { - memset(rs,0,sizeof(*rs)); - rs->seed = rs->origseed = seed; - rs->keystrokes = keystrokes; - rs->numkeys = num; - rs->sleeptime = 0; - gamesiterate(rs); - } - fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL)-starttime); - sleep(3); - } - // extract playerdata - - /*if ( (fp= fopen("checkfile","wb")) != 0 ) - { - //save_file(rs,fp,0); - //fprintf(stderr,"gold.%d hp.%d strength.%d/%d level.%d exp.%d dungeon.%d data[%d]\n",rs->P.gold,rs->P.hitpoints,rs->P.strength&0xffff,rs->P.strength>>16,rs->P.level,rs->P.experience,rs->P.dungeonlevel,rs->playersize); - if ( newdata != 0 && rs->playersize > 0 ) - memcpy(newdata,rs->playerdata,rs->playersize); - }*/ - if ( ptr != 0 ) - { - // extract data from ptr - if ( newdata != 0 && rs->playersize > 0 ) - memcpy(newdata,rs->playerdata,rs->playersize); - free(ptr); - } - n = rs->playersize; - free(rs); - return(n); -} - long get_filesize(FILE *fp) { long fsize,fpos = ftell(fp); diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 2f5c031c5..31b33e2b5 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -31,6 +31,63 @@ uint64_t _games_rngnext(uint64_t initseed) return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); } +int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) +{ + struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; + rs = (struct games_state *)calloc(1,sizeof(*rs)); + rs->seed = rs->origseed = seed; + rs->keystrokes = keystrokes; + rs->numkeys = num; + rs->sleeptime = sleepmillis * 1000; + if ( player != 0 ) + { + rs->P = *player; + rs->restoring = 1; + //fprintf(stderr,"restore player packsize.%d HP.%d\n",rs->P.packsize,rs->P.hitpoints); + if ( rs->P.packsize > MAXPACK ) + rs->P.packsize = MAXPACK; + } + globalR = *rs; + uint32_t starttime = (uint32_t)time(NULL); + ptr = gamesiterate(rs); + if ( 0 ) + { + fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL) - starttime); + sleep(2); + starttime = (uint32_t)time(NULL); + for (i=0; i<10000; i++) + { + memset(rs,0,sizeof(*rs)); + rs->seed = rs->origseed = seed; + rs->keystrokes = keystrokes; + rs->numkeys = num; + rs->sleeptime = 0; + gamesiterate(rs); + } + fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL)-starttime); + sleep(3); + } + // extract playerdata + + /*if ( (fp= fopen("checkfile","wb")) != 0 ) + { + //save_file(rs,fp,0); + //fprintf(stderr,"gold.%d hp.%d strength.%d/%d level.%d exp.%d dungeon.%d data[%d]\n",rs->P.gold,rs->P.hitpoints,rs->P.strength&0xffff,rs->P.strength>>16,rs->P.level,rs->P.experience,rs->P.dungeonlevel,rs->playersize); + if ( newdata != 0 && rs->playersize > 0 ) + memcpy(newdata,rs->playerdata,rs->playersize); + }*/ + if ( ptr != 0 ) + { + // extract data from ptr + if ( newdata != 0 && rs->playersize > 0 ) + memcpy(newdata,rs->playerdata,rs->playersize); + free(ptr); + } + n = rs->playersize; + free(rs); + return(n); +} + #ifndef STANDALONE #include "tetris.cpp" // replace with game specific functions diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index a20652012..6a1269099 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -20,15 +20,15 @@ uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 // game specific code for daemon -void games_packitemstr(char *packitemstr,struct games_packitem *item) +/*void games_packitemstr(char *packitemstr,struct games_packitem *item) { sprintf(packitemstr,"not yet"); } int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) // replay in daemon { - return(-1); -} + return(0); +}*/ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) { From 2ed6eccec62b8c459c88f0d3f3bedbf25dcfb9b1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:16:46 -1100 Subject: [PATCH 247/787] Test --- src/cc/gamescc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 31b33e2b5..d5ec503c9 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -14,8 +14,6 @@ ******************************************************************************/ #include "gamescc.h" -#include "tetris.c" // replace with game code - uint64_t _games_rngnext(uint64_t initseed) { @@ -88,6 +86,9 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 return(n); } +#include "tetris.c" // replace with game code + + #ifndef STANDALONE #include "tetris.cpp" // replace with game specific functions From d61a4c44cf1f00a739dd5da7e74c4ee5ff571d86 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:17:56 -1100 Subject: [PATCH 248/787] Test --- src/cc/gamescc.cpp | 4 +--- src/cc/tetris.h | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index d5ec503c9..80f89332f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -14,6 +14,7 @@ ******************************************************************************/ #include "gamescc.h" +#include "tetris.c" // replace with game code uint64_t _games_rngnext(uint64_t initseed) { @@ -86,9 +87,6 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 return(n); } -#include "tetris.c" // replace with game code - - #ifndef STANDALONE #include "tetris.cpp" // replace with game specific functions diff --git a/src/cc/tetris.h b/src/cc/tetris.h index 3c246a3bd..b2f2fbe04 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -193,8 +193,10 @@ struct games_state gamesevent buffered[5000],*keystrokes; uint8_t playerdata[1024]; }; +extern struct games_state globalR; uint64_t _games_rngnext(uint64_t initseed); +int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); #endif From 32851ac12aa85f2a599ac6b13791f75304051720 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:24:33 -1100 Subject: [PATCH 249/787] Baton support for games event --- src/cc/gamescc.cpp | 17 +++++++++++------ src/cc/tetris.h | 2 ++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 80f89332f..227b3c5ae 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -848,9 +848,9 @@ int64_t games_registrationbaton(CMutableTransaction &mtx,uint256 gametxid,CTrans return(0); } -int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,char **keystrokesp,int32_t &numkeys,int32_t ®slot,std::vector &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname) +int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gamesevent **keystrokesp,int32_t &numkeys,int32_t ®slot,std::vector &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname) { - int32_t i,numvouts,spentvini,n,matches = 0; CPubKey pk; uint256 tid,active,spenttxid,tokenid,hashBlock,txid,origplayergame; CTransaction spenttx,matchtx,batontx; std::vector checkdata; CBlockIndex *pindex; char ccaddr[64],*keystrokes=0; + int32_t i,numvouts,spentvini,n,matches = 0; CPubKey pk; uint256 tid,active,spenttxid,tokenid,hashBlock,txid,origplayergame; CTransaction spenttx,matchtx,batontx; std::vector checkdata; CBlockIndex *pindex; char ccaddr[64]; gamesevent *keystrokes=0; batonvalue = numkeys = numplayers = batonht = 0; playertxid = batontxid = zeroid; if ( keystrokesp != 0 ) @@ -913,10 +913,15 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,char **ke uint256 g,b; CPubKey p; std::vector k; if ( games_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' ) { - keystrokes = (char *)realloc(keystrokes,numkeys + (int32_t)k.size()); + keystrokes = (char *)realloc(keystrokes,sizeof(*keystrokes)*(numkeys + (int32_t)k.size())); for (i=0; i 1% ingame gold // get any playerdata, get all keystrokes, replay game and compare final state CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); char *method = (char *)"bailout"; - UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,dungeonlevel,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64],*keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid; + UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,dungeonlevel,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64]; gamesevent *keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid; struct CCcontract_info *cpTokens, tokensC; if ( txfee == 0 ) diff --git a/src/cc/tetris.h b/src/cc/tetris.h index b2f2fbe04..94d56dbfc 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -194,7 +194,9 @@ struct games_state uint8_t playerdata[1024]; }; extern struct games_state globalR; +void *gamesiterate(struct games_state *rs); +void games_packitemstr(char *packitemstr,struct games_packitem *item); uint64_t _games_rngnext(uint64_t initseed); int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); From 985e8a71273f48f85a7f9246b11ef74f0c1263d7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:26:45 -1100 Subject: [PATCH 250/787] Test --- src/cc/gamescc.cpp | 1 + src/cc/tetris.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 227b3c5ae..7dc368d72 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -916,6 +916,7 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven keystrokes = (char *)realloc(keystrokes,sizeof(*keystrokes)*(numkeys + (int32_t)k.size())); for (i=0; i playerdata) } } -char *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr) +gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr) { - CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64],*keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP; + CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64]; gamesevent *keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP; gamespk = GetUnspendable(cp,0); *numkeysp = 0; seed = 0; @@ -110,7 +110,7 @@ char *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vecto sprintf(fname,"%s.%llu.0",GAMENAME,(long long)seed); if ( (fp= fopen(fname,"wb")) != 0 ) { - if ( fwrite(keystrokes,1,numkeys,fp) != numkeys ) + if ( fwrite(keystrokes,sizeof(*keystrokes),numkeys,fp) != numkeys ) fprintf(stderr,"error writing %s\n",fname); fclose(fp); } From ce1a3197b2d933c7104dcfbf1abe7e617f82ebdf Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:28:25 -1100 Subject: [PATCH 251/787] Test --- src/cc/tetris.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index 2ef048364..216d4c4d0 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -14,7 +14,7 @@ * * ******************************************************************************/ -int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,char **keystrokesp,int32_t &numkeys,int32_t ®slot,std::vector &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname); +int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gamesevent **keystrokesp,int32_t &numkeys,int32_t ®slot,std::vector &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname); int32_t games_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0); uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr); @@ -169,7 +169,7 @@ gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std: int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) { static uint32_t good,bad; static uint256 prevgame; - char str[512],*keystrokes,gamesaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; + char str[512],gamesaddr[64],str2[67],fname[64]; gamesevent *keystrokes; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; *cashoutp = 0; gamespk = GetUnspendable(cp,0); GetCCaddress1of2(cp,gamesaddr,gamespk,pk); From 6ce5d7f762bf9cc2115151b281695c42eb7ce9d2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:31:50 -1100 Subject: [PATCH 252/787] Hexer --- src/cc/gamescc.cpp | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 7dc368d72..0f261a184 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -913,7 +913,7 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven uint256 g,b; CPubKey p; std::vector k; if ( games_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' ) { - keystrokes = (char *)realloc(keystrokes,sizeof(*keystrokes)*(numkeys + (int32_t)k.size())); + keystrokes = (gamesevent *)realloc(keystrokes,sizeof(*keystrokes)*(numkeys + (int32_t)k.size())); for (i=0; i newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33]; + UniValue result(UniValue::VOBJ); CPubKey pk,gamespk; int32_t i,n,numkeys,flag = 0; uint64_t seed; char str[512],gamesaddr[64],*pubstr,*hexstr; gamesevent *keystrokes = 0; std::vector newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33]; pk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); result.push_back(Pair("name","games")); @@ -1263,10 +1263,25 @@ UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { result.push_back(Pair("status","success")); flag = 1; - hexstr = (char *)malloc(numkeys*2 + 1); + hexstr = (char *)calloc(1,sizeof(gamesevent)*numkeys*2 + 1); for (i=0; i Date: Tue, 26 Mar 2019 05:33:56 -1100 Subject: [PATCH 253/787] Link errors --- src/cc/tetris.c | 116 +++++++++++++++++++++++----------------------- src/cc/tetris.cpp | 4 +- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 19aa0467b..803d163fa 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -634,64 +634,6 @@ void init_colors(void) init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); } -#ifdef STANDALONE -/* - Main tetris game! - */ -#include "dapps/dappstd.c" - -int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) -{ - static FILE *fp; - char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; - if ( fp == 0 ) - fp = fopen("events.log","wb"); - rs->buffered[rs->num++] = c; - if ( sizeof(c) == 1 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); - else if ( sizeof(c) == 2 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); - else if ( sizeof(c) == 4 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); - else if ( sizeof(c) == 8 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) - { - retval = 0; - if ( fp != 0 ) - { - fprintf(fp,"%s\n",jprint(resobj,0)); - fflush(fp); - } - } - free_json(retjson); - } else fprintf(fp,"error parsing %s\n",retstr); - free(retstr); - } else fprintf(fp,"error issuing method %s\n",params); - return(retval); -} - -char *clonestr(char *str) -{ - char *clone; int32_t len; - if ( str == 0 || str[0] == 0 ) - { - printf("warning cloning nullstr.%p\n",str); -#ifdef __APPLE__ - while ( 1 ) sleep(1); -#endif - str = (char *)""; - } - len = strlen(str); - clone = (char *)calloc(1,len+16); - strcpy(clone,str); - return(clone); -} - struct games_state globalR; void *gamesiterate(struct games_state *rs) @@ -785,6 +727,64 @@ void *gamesiterate(struct games_state *rs) return(tg); } +#ifdef STANDALONE +/* + Main tetris game! + */ +#include "dapps/dappstd.c" + +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) +{ + static FILE *fp; + char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + rs->buffered[rs->num++] = c; + if ( sizeof(c) == 1 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + else if ( sizeof(c) == 2 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + else if ( sizeof(c) == 4 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); + else if ( sizeof(c) == 8 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); +} + +char *clonestr(char *str) +{ + char *clone; int32_t len; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + len = strlen(str); + clone = (char *)calloc(1,len+16); + strcpy(clone,str); + return(clone); +} + int tetris(int argc, char **argv) { struct games_state *rs = &globalR; diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index 216d4c4d0..c2caaa8a9 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -19,12 +19,12 @@ int32_t games_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransa uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr); // game specific code for daemon - -/*void games_packitemstr(char *packitemstr,struct games_packitem *item) +void games_packitemstr(char *packitemstr,struct games_packitem *item) { sprintf(packitemstr,"not yet"); } +/* int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) // replay in daemon { return(0); From a10f3988eeb975f9d7319e22e0fe812478bf89ec Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:35:58 -1100 Subject: [PATCH 254/787] Test --- src/cc/tetris.c | 71 ++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 803d163fa..f6a4974c4 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -635,6 +635,43 @@ void init_colors(void) } struct games_state globalR; +gamesevent games_readevent(struct games_state *rs); +extern char Gametxidstr[]; + +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) +{ + static FILE *fp; + char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + rs->buffered[rs->num++] = c; + if ( sizeof(c) == 1 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + else if ( sizeof(c) == 2 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + else if ( sizeof(c) == 4 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); + else if ( sizeof(c) == 8 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); +} void *gamesiterate(struct games_state *rs) { @@ -733,40 +770,6 @@ void *gamesiterate(struct games_state *rs) */ #include "dapps/dappstd.c" -int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) -{ - static FILE *fp; - char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; - if ( fp == 0 ) - fp = fopen("events.log","wb"); - rs->buffered[rs->num++] = c; - if ( sizeof(c) == 1 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); - else if ( sizeof(c) == 2 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); - else if ( sizeof(c) == 4 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); - else if ( sizeof(c) == 8 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) - { - retval = 0; - if ( fp != 0 ) - { - fprintf(fp,"%s\n",jprint(resobj,0)); - fflush(fp); - } - } - free_json(retjson); - } else fprintf(fp,"error parsing %s\n",retstr); - free(retstr); - } else fprintf(fp,"error issuing method %s\n",params); - return(retval); -} char *clonestr(char *str) { From 38bf6a68951aaf878ad881eae0812c704a5cfd3e Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:38:17 -1100 Subject: [PATCH 255/787] Fix --- src/cc/tetris.c | 74 +++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index f6a4974c4..3b330f5ca 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -635,43 +635,8 @@ void init_colors(void) } struct games_state globalR; -gamesevent games_readevent(struct games_state *rs); extern char Gametxidstr[]; - -int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) -{ - static FILE *fp; - char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; - if ( fp == 0 ) - fp = fopen("events.log","wb"); - rs->buffered[rs->num++] = c; - if ( sizeof(c) == 1 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); - else if ( sizeof(c) == 2 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); - else if ( sizeof(c) == 4 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); - else if ( sizeof(c) == 8 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) - { - if ( (retjson= cJSON_Parse(retstr)) != 0 ) - { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) - { - retval = 0; - if ( fp != 0 ) - { - fprintf(fp,"%s\n",jprint(resobj,0)); - fflush(fp); - } - } - free_json(retjson); - } else fprintf(fp,"error parsing %s\n",retstr); - free(retstr); - } else fprintf(fp,"error issuing method %s\n",params); - return(retval); -} +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); void *gamesiterate(struct games_state *rs) { @@ -689,6 +654,7 @@ void *gamesiterate(struct games_state *rs) running = tg_tick(rs,tg,move); if ( rs->guiflag != 0 ) { +#ifdef STANDALONE display_board(board,tg); display_piece(next,tg->next); display_piece(hold,tg->stored); @@ -705,6 +671,7 @@ void *gamesiterate(struct games_state *rs) issue_games_events(rs,Gametxidstr,eventid,c); skipcount = 0; } else skipcount++; +#endif } else { @@ -788,6 +755,41 @@ char *clonestr(char *str) return(clone); } +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) +{ + static FILE *fp; + char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + rs->buffered[rs->num++] = c; + if ( sizeof(c) == 1 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + else if ( sizeof(c) == 2 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + else if ( sizeof(c) == 4 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); + else if ( sizeof(c) == 8 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); +} + int tetris(int argc, char **argv) { struct games_state *rs = &globalR; From 82c132b6fbc6ea6f97f737626e7ea226ac362c24 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:38:58 -1100 Subject: [PATCH 256/787] gamesevent games_readevent(struct games_state *rs) --- src/cc/tetris.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 3b330f5ca..487566ed4 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -637,6 +637,7 @@ void init_colors(void) struct games_state globalR; extern char Gametxidstr[]; int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); +gamesevent games_readevent(struct games_state *rs); void *gamesiterate(struct games_state *rs) { From 8ba50f49c8d7981d7530a8d725305f6f8636c28c Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:41:31 -1100 Subject: [PATCH 257/787] Test --- src/cc/dapps/dappstd.c | 69 ------------------------------------------ src/cc/gamescc.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index bac8cd50c..3992cb80e 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -961,75 +961,6 @@ int32_t games_replay(uint64_t seed,int32_t sleeptime) return(num); } -gamesevent games_readevent(struct games_state *rs) -{ - gamesevent ch = -1; int32_t c; - if ( rs != 0 && rs->guiflag == 0 ) - { - static uint32_t counter; - if ( rs->ind < rs->numkeys ) - { - ch = rs->keystrokes[rs->ind++]; - if ( 0 ) - { - static FILE *fp; static int32_t counter; - if ( fp == 0 ) - fp = fopen("log","wb"); - if ( fp != 0 ) - { - fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); - fflush(fp); - counter++; - } - } - return(ch); - } - if ( rs->replaydone != 0 && counter++ < 3 ) - fprintf(stderr,"replay finished but readchar called\n"); - rs->replaydone = (uint32_t)time(NULL); - return(0); - } - if ( rs == 0 || rs->guiflag != 0 ) - { - c = getch(); - switch ( c ) - { - case KEY_LEFT: - c = 'h'; - break; - case KEY_RIGHT: - c = 'l'; - break; - case KEY_UP: - c = 'k'; - break; - case KEY_DOWN: - c = 'j'; - break; - } - ch = c; - if (ch == 3) - { - //_quit(); - return(27); - } - /*if ( rs != 0 && rs->guiflag != 0 ) - { - if ( rs->num < sizeof(rs->buffered) ) - { - rs->buffered[rs->num++] = ch; - if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) - { - rs->needflush = (uint32_t)time(NULL); - //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); - //sleep(3); - } - } else fprintf(stderr,"buffer filled without flushed\n"); - }*/ - } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); - return(ch); -} - int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) { char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 0f261a184..b99c052ac 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -30,6 +30,75 @@ uint64_t _games_rngnext(uint64_t initseed) return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); } +gamesevent games_readevent(struct games_state *rs) +{ + gamesevent ch = -1; int32_t c; + if ( rs != 0 && rs->guiflag == 0 ) + { + static uint32_t counter; + if ( rs->ind < rs->numkeys ) + { + ch = rs->keystrokes[rs->ind++]; + if ( 0 ) + { + static FILE *fp; static int32_t counter; + if ( fp == 0 ) + fp = fopen("log","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); + fflush(fp); + counter++; + } + } + return(ch); + } + if ( rs->replaydone != 0 && counter++ < 3 ) + fprintf(stderr,"replay finished but readchar called\n"); + rs->replaydone = (uint32_t)time(NULL); + return(0); + } + if ( rs == 0 || rs->guiflag != 0 ) + { + c = getch(); + switch ( c ) + { + case KEY_LEFT: + c = 'h'; + break; + case KEY_RIGHT: + c = 'l'; + break; + case KEY_UP: + c = 'k'; + break; + case KEY_DOWN: + c = 'j'; + break; + } + ch = c; + if (ch == 3) + { + //_quit(); + return(27); + } + /*if ( rs != 0 && rs->guiflag != 0 ) + { + if ( rs->num < sizeof(rs->buffered) ) + { + rs->buffered[rs->num++] = ch; + if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) + { + rs->needflush = (uint32_t)time(NULL); + //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); + //sleep(3); + } + } else fprintf(stderr,"buffer filled without flushed\n"); + }*/ + } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); + return(ch); +} + int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) { struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; From e34fc6b5108521078b0c4d934044aafa91937129 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:43:30 -1100 Subject: [PATCH 258/787] Linker fix --- src/cc/gamescc.cpp | 138 ++++++++++++++++++++++----------------------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index b99c052ac..2746e8097 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -30,75 +30,6 @@ uint64_t _games_rngnext(uint64_t initseed) return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); } -gamesevent games_readevent(struct games_state *rs) -{ - gamesevent ch = -1; int32_t c; - if ( rs != 0 && rs->guiflag == 0 ) - { - static uint32_t counter; - if ( rs->ind < rs->numkeys ) - { - ch = rs->keystrokes[rs->ind++]; - if ( 0 ) - { - static FILE *fp; static int32_t counter; - if ( fp == 0 ) - fp = fopen("log","wb"); - if ( fp != 0 ) - { - fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); - fflush(fp); - counter++; - } - } - return(ch); - } - if ( rs->replaydone != 0 && counter++ < 3 ) - fprintf(stderr,"replay finished but readchar called\n"); - rs->replaydone = (uint32_t)time(NULL); - return(0); - } - if ( rs == 0 || rs->guiflag != 0 ) - { - c = getch(); - switch ( c ) - { - case KEY_LEFT: - c = 'h'; - break; - case KEY_RIGHT: - c = 'l'; - break; - case KEY_UP: - c = 'k'; - break; - case KEY_DOWN: - c = 'j'; - break; - } - ch = c; - if (ch == 3) - { - //_quit(); - return(27); - } - /*if ( rs != 0 && rs->guiflag != 0 ) - { - if ( rs->num < sizeof(rs->buffered) ) - { - rs->buffered[rs->num++] = ch; - if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) - { - rs->needflush = (uint32_t)time(NULL); - //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); - //sleep(3); - } - } else fprintf(stderr,"buffer filled without flushed\n"); - }*/ - } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); - return(ch); -} - int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) { struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; @@ -1628,3 +1559,72 @@ UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) #endif +gamesevent games_readevent(struct games_state *rs) +{ + gamesevent ch = -1; int32_t c; + if ( rs != 0 && rs->guiflag == 0 ) + { + static uint32_t counter; + if ( rs->ind < rs->numkeys ) + { + ch = rs->keystrokes[rs->ind++]; + if ( 0 ) + { + static FILE *fp; static int32_t counter; + if ( fp == 0 ) + fp = fopen("log","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); + fflush(fp); + counter++; + } + } + return(ch); + } + if ( rs->replaydone != 0 && counter++ < 3 ) + fprintf(stderr,"replay finished but readchar called\n"); + rs->replaydone = (uint32_t)time(NULL); + return(0); + } + if ( rs == 0 || rs->guiflag != 0 ) + { + c = getch(); + switch ( c ) + { + case KEY_LEFT: + c = 'h'; + break; + case KEY_RIGHT: + c = 'l'; + break; + case KEY_UP: + c = 'k'; + break; + case KEY_DOWN: + c = 'j'; + break; + } + ch = c; + if (ch == 3) + { + //_quit(); + return(27); + } + /*if ( rs != 0 && rs->guiflag != 0 ) + { + if ( rs->num < sizeof(rs->buffered) ) + { + rs->buffered[rs->num++] = ch; + if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) + { + rs->needflush = (uint32_t)time(NULL); + //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); + //sleep(3); + } + } else fprintf(stderr,"buffer filled without flushed\n"); + }*/ + } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); + return(ch); +} + From e495123a5d5611b32d5cf7ed594e7a05f3ca9e81 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:44:39 -1100 Subject: [PATCH 259/787] link --- src/cc/gamescc.cpp | 68 --------------------------------------------- src/cc/tetris.c | 69 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 68 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 2746e8097..ad578400d 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1559,72 +1559,4 @@ UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) #endif -gamesevent games_readevent(struct games_state *rs) -{ - gamesevent ch = -1; int32_t c; - if ( rs != 0 && rs->guiflag == 0 ) - { - static uint32_t counter; - if ( rs->ind < rs->numkeys ) - { - ch = rs->keystrokes[rs->ind++]; - if ( 0 ) - { - static FILE *fp; static int32_t counter; - if ( fp == 0 ) - fp = fopen("log","wb"); - if ( fp != 0 ) - { - fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); - fflush(fp); - counter++; - } - } - return(ch); - } - if ( rs->replaydone != 0 && counter++ < 3 ) - fprintf(stderr,"replay finished but readchar called\n"); - rs->replaydone = (uint32_t)time(NULL); - return(0); - } - if ( rs == 0 || rs->guiflag != 0 ) - { - c = getch(); - switch ( c ) - { - case KEY_LEFT: - c = 'h'; - break; - case KEY_RIGHT: - c = 'l'; - break; - case KEY_UP: - c = 'k'; - break; - case KEY_DOWN: - c = 'j'; - break; - } - ch = c; - if (ch == 3) - { - //_quit(); - return(27); - } - /*if ( rs != 0 && rs->guiflag != 0 ) - { - if ( rs->num < sizeof(rs->buffered) ) - { - rs->buffered[rs->num++] = ch; - if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) - { - rs->needflush = (uint32_t)time(NULL); - //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); - //sleep(3); - } - } else fprintf(stderr,"buffer filled without flushed\n"); - }*/ - } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); - return(ch); -} diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 487566ed4..9808622f4 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -732,6 +732,75 @@ void *gamesiterate(struct games_state *rs) return(tg); } +gamesevent games_readevent(struct games_state *rs) +{ + gamesevent ch = -1; int32_t c; + if ( rs != 0 && rs->guiflag == 0 ) + { + static uint32_t counter; + if ( rs->ind < rs->numkeys ) + { + ch = rs->keystrokes[rs->ind++]; + if ( 0 ) + { + static FILE *fp; static int32_t counter; + if ( fp == 0 ) + fp = fopen("log","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); + fflush(fp); + counter++; + } + } + return(ch); + } + if ( rs->replaydone != 0 && counter++ < 3 ) + fprintf(stderr,"replay finished but readchar called\n"); + rs->replaydone = (uint32_t)time(NULL); + return(0); + } + if ( rs == 0 || rs->guiflag != 0 ) + { + c = getch(); + switch ( c ) + { + case KEY_LEFT: + c = 'h'; + break; + case KEY_RIGHT: + c = 'l'; + break; + case KEY_UP: + c = 'k'; + break; + case KEY_DOWN: + c = 'j'; + break; + } + ch = c; + if (ch == 3) + { + //_quit(); + return(27); + } + /*if ( rs != 0 && rs->guiflag != 0 ) + { + if ( rs->num < sizeof(rs->buffered) ) + { + rs->buffered[rs->num++] = ch; + if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) + { + rs->needflush = (uint32_t)time(NULL); + //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); + //sleep(3); + } + } else fprintf(stderr,"buffer filled without flushed\n"); + }*/ + } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); + return(ch); +} + #ifdef STANDALONE /* Main tetris game! From fd1ff16d471d38297bec4f0f23f217b20768a0f6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:46:18 -1100 Subject: [PATCH 260/787] Test --- src/cc/rogue/cursesd.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cc/rogue/cursesd.h b/src/cc/rogue/cursesd.h index e3eec5d41..0b68bc307 100644 --- a/src/cc/rogue/cursesd.h +++ b/src/cc/rogue/cursesd.h @@ -16,6 +16,13 @@ #ifndef H_CURSESD_H #define H_CURSESD_H +#define KEY_OFFSET 0x100 +#define KEY_DOWN (KEY_OFFSET + 0x02) /* Down arrow key */ +#define KEY_UP (KEY_OFFSET + 0x03) /* Up arrow key */ +#define KEY_LEFT (KEY_OFFSET + 0x04) /* Left arrow key */ +#define KEY_RIGHT (KEY_OFFSET + 0x05) /* Right arrow key */ + + #define COLOR_BLACK 0 #ifdef PDC_RGB /* RGB */ From db01efa0221cd74add1802356ba7bdf514828d12 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:47:13 -1100 Subject: [PATCH 261/787] Readevent --- src/cc/gamescc.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++++ src/cc/tetris.c | 69 ---------------------------------------------- 2 files changed, 69 insertions(+), 69 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index ad578400d..255866c86 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -30,6 +30,75 @@ uint64_t _games_rngnext(uint64_t initseed) return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); } +gamesevent games_readevent(struct games_state *rs) +{ + gamesevent ch = -1; int32_t c; + if ( rs != 0 && rs->guiflag == 0 ) + { + static uint32_t counter; + if ( rs->ind < rs->numkeys ) + { + ch = rs->keystrokes[rs->ind++]; + if ( 0 ) + { + static FILE *fp; static int32_t counter; + if ( fp == 0 ) + fp = fopen("log","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); + fflush(fp); + counter++; + } + } + return(ch); + } + if ( rs->replaydone != 0 && counter++ < 3 ) + fprintf(stderr,"replay finished but readchar called\n"); + rs->replaydone = (uint32_t)time(NULL); + return(0); + } + if ( rs == 0 || rs->guiflag != 0 ) + { + c = getch(); + switch ( c ) + { + case KEY_LEFT: + c = 'h'; + break; + case KEY_RIGHT: + c = 'l'; + break; + case KEY_UP: + c = 'k'; + break; + case KEY_DOWN: + c = 'j'; + break; + } + ch = c; + if (ch == 3) + { + //_quit(); + return(27); + } + /*if ( rs != 0 && rs->guiflag != 0 ) + { + if ( rs->num < sizeof(rs->buffered) ) + { + rs->buffered[rs->num++] = ch; + if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) + { + rs->needflush = (uint32_t)time(NULL); + //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); + //sleep(3); + } + } else fprintf(stderr,"buffer filled without flushed\n"); + }*/ + } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); + return(ch); +} + int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) { struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 9808622f4..487566ed4 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -732,75 +732,6 @@ void *gamesiterate(struct games_state *rs) return(tg); } -gamesevent games_readevent(struct games_state *rs) -{ - gamesevent ch = -1; int32_t c; - if ( rs != 0 && rs->guiflag == 0 ) - { - static uint32_t counter; - if ( rs->ind < rs->numkeys ) - { - ch = rs->keystrokes[rs->ind++]; - if ( 0 ) - { - static FILE *fp; static int32_t counter; - if ( fp == 0 ) - fp = fopen("log","wb"); - if ( fp != 0 ) - { - fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); - fflush(fp); - counter++; - } - } - return(ch); - } - if ( rs->replaydone != 0 && counter++ < 3 ) - fprintf(stderr,"replay finished but readchar called\n"); - rs->replaydone = (uint32_t)time(NULL); - return(0); - } - if ( rs == 0 || rs->guiflag != 0 ) - { - c = getch(); - switch ( c ) - { - case KEY_LEFT: - c = 'h'; - break; - case KEY_RIGHT: - c = 'l'; - break; - case KEY_UP: - c = 'k'; - break; - case KEY_DOWN: - c = 'j'; - break; - } - ch = c; - if (ch == 3) - { - //_quit(); - return(27); - } - /*if ( rs != 0 && rs->guiflag != 0 ) - { - if ( rs->num < sizeof(rs->buffered) ) - { - rs->buffered[rs->num++] = ch; - if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) - { - rs->needflush = (uint32_t)time(NULL); - //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); - //sleep(3); - } - } else fprintf(stderr,"buffer filled without flushed\n"); - }*/ - } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); - return(ch); -} - #ifdef STANDALONE /* Main tetris game! From 2fda05a5310a37b222e4f0c7731ddebbf5e511ee Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 05:57:58 -1100 Subject: [PATCH 262/787] Disable return0 --- src/cc/gamescc.cpp | 1 + src/cc/tetris.cpp | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 255866c86..442632c4e 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -102,6 +102,7 @@ gamesevent games_readevent(struct games_state *rs) int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) { struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; +return(0); rs = (struct games_state *)calloc(1,sizeof(*rs)); rs->seed = rs->origseed = seed; rs->keystrokes = keystrokes; diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index c2caaa8a9..1e0cb95d1 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -24,12 +24,6 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) sprintf(packitemstr,"not yet"); } -/* -int32_t games_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) // replay in daemon -{ - return(0); -}*/ - int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) { uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; From 21b50727bfe56f6e4ac43625046ccd6d965746f8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:01:08 -1100 Subject: [PATCH 263/787] +print --- src/cc/gamescc.cpp | 18 +++++++++--------- src/cc/tetris.cpp | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 442632c4e..650c051b9 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -927,7 +927,7 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven *keystrokesp = 0; for (i=0; i= 0 ) { if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) @@ -946,17 +946,17 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven if ( matches == 1 ) { numvouts = matchtx.vout.size(); - //fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts); +fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts); if ( games_registeropretdecode(txid,tokenid,playertxid,matchtx.vout[numvouts-1].scriptPubKey) == 'R' )//&& txid == gametxid ) { - //fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); if ( tokenid != zeroid ) active = tokenid; else active = playertxid; if ( active == zeroid || games_playerdata(cp,origplayergame,tid,pk,playerdata,symbol,pname,active) == 0 ) { txid = matchtx.GetHash(); - //fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str()); + fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str()); n = 0; while ( CCgettxout(txid,0,1,0) < 0 ) { @@ -973,7 +973,7 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven } } txid = spenttxid; - //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); if ( spentvini != 0 ) // game is over? { return(0); @@ -994,17 +994,17 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven } numkeys += (int32_t)k.size() / sizeof(gamesevent); (*keystrokesp) = keystrokes; - //fprintf(stderr,"updated keystrokes.%p[%d]\n",keystrokes,numkeys); + fprintf(stderr,"updated keystrokes.%p[%d]\n",keystrokes,numkeys); } } - //fprintf(stderr,"n.%d txid.%s\n",n,txid.GetHex().c_str()); + fprintf(stderr,"n.%d txid.%s\n",n,txid.GetHex().c_str()); if ( ++n >= GAMES_MAXITERATIONS ) { fprintf(stderr,"games_findbaton n.%d, seems something is wrong\n",n); return(-5); } } - //fprintf(stderr,"set baton %s\n",txid.GetHex().c_str()); + fprintf(stderr,"set baton %s\n",txid.GetHex().c_str()); batontxid = txid; batonvout = 0; // not vini // how to detect timeout, bailedout, highlander @@ -1017,7 +1017,7 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven return(-4); else batonht = pindex->GetHeight(); batonvalue = batontx.vout[0].nValue; - //printf("batonht.%d keystrokes[%d]\n",batonht,numkeys); + printf("batonht.%d keystrokes[%d]\n",batonht,numkeys); return(0); } else fprintf(stderr,"couldnt find baton\n"); } else fprintf(stderr,"error with playerdata\n"); diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index 1e0cb95d1..a91157dde 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -90,7 +90,7 @@ gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std: { UniValue obj; seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr); - //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); + fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); memset(&P,0,sizeof(P)); if ( playerdata.size() > 0 ) { @@ -116,7 +116,7 @@ gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std: fclose(fp); } } - //fprintf(stderr,"call replay2\n"); + fprintf(stderr,"call replay2\n"); num = games_replay2(newplayer,seed,keystrokes,numkeys,playerdata.size()==0?0:&P,0); newdata.resize(num); for (i=0; i no playerdata\n"); From 1c12ebcf34fe1d1046481122e944dd4e1ca2379f Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:05:22 -1100 Subject: [PATCH 264/787] +print --- src/cc/gamescc.cpp | 19 ++++++++++--------- src/cc/tetris.cpp | 1 + 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 650c051b9..8fa629b7f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -927,7 +927,7 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven *keystrokesp = 0; for (i=0; i= 0 ) { if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) @@ -946,17 +946,17 @@ fprintf(stderr,"findbaton.%d of %d\n",i,maxplayers); if ( matches == 1 ) { numvouts = matchtx.vout.size(); -fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts); +//fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts); if ( games_registeropretdecode(txid,tokenid,playertxid,matchtx.vout[numvouts-1].scriptPubKey) == 'R' )//&& txid == gametxid ) { - fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + //fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); if ( tokenid != zeroid ) active = tokenid; else active = playertxid; if ( active == zeroid || games_playerdata(cp,origplayergame,tid,pk,playerdata,symbol,pname,active) == 0 ) { txid = matchtx.GetHash(); - fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str()); + //fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str()); n = 0; while ( CCgettxout(txid,0,1,0) < 0 ) { @@ -973,9 +973,10 @@ fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex( } } txid = spenttxid; - fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); if ( spentvini != 0 ) // game is over? { + fprintf(stderr,"gameisover n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); return(0); } if ( keystrokesp != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 ) @@ -994,17 +995,17 @@ fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex( } numkeys += (int32_t)k.size() / sizeof(gamesevent); (*keystrokesp) = keystrokes; - fprintf(stderr,"updated keystrokes.%p[%d]\n",keystrokes,numkeys); + //fprintf(stderr,"updated keystrokes.%p[%d]\n",keystrokes,numkeys); } } - fprintf(stderr,"n.%d txid.%s\n",n,txid.GetHex().c_str()); + //fprintf(stderr,"n.%d txid.%s\n",n,txid.GetHex().c_str()); if ( ++n >= GAMES_MAXITERATIONS ) { fprintf(stderr,"games_findbaton n.%d, seems something is wrong\n",n); return(-5); } } - fprintf(stderr,"set baton %s\n",txid.GetHex().c_str()); + //fprintf(stderr,"set baton %s\n",txid.GetHex().c_str()); batontxid = txid; batonvout = 0; // not vini // how to detect timeout, bailedout, highlander @@ -1017,7 +1018,7 @@ fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex( return(-4); else batonht = pindex->GetHeight(); batonvalue = batontx.vout[0].nValue; - printf("batonht.%d keystrokes[%d]\n",batonht,numkeys); + //printf("batonht.%d keystrokes[%d]\n",batonht,numkeys); return(0); } else fprintf(stderr,"couldnt find baton\n"); } else fprintf(stderr,"error with playerdata\n"); diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index a91157dde..1a55de005 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -89,6 +89,7 @@ gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std: if ( (retval= games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,gamesaddr,numplayers,symbol,pname)) == 0 ) { UniValue obj; + fprintf(stderr,"got baton\n"); seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr); fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); memset(&P,0,sizeof(P)); From 33ba39090d1f82f9d11ffd2a999f6eded3732860 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:07:43 -1100 Subject: [PATCH 265/787] Test --- src/cc/gamescc.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 8fa629b7f..503eb17e3 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -984,18 +984,19 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven uint256 g,b; CPubKey p; std::vector k; if ( games_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' ) { - keystrokes = (gamesevent *)realloc(keystrokes,sizeof(*keystrokes)*(numkeys + (int32_t)k.size())); - for (i=0; i Date: Tue, 26 Mar 2019 06:14:00 -1100 Subject: [PATCH 266/787] Test --- src/cc/gamescc.cpp | 8 ++++---- src/cc/tetris.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 503eb17e3..1684e6a18 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -976,7 +976,7 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); if ( spentvini != 0 ) // game is over? { - fprintf(stderr,"gameisover n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + //fprintf(stderr,"gameisover n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); return(0); } if ( keystrokesp != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 ) @@ -984,7 +984,7 @@ int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gameseven uint256 g,b; CPubKey p; std::vector k; if ( games_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' ) { - fprintf(stderr,"update keystrokes.%p[%d]\n",keystrokes,numkeys); + //fprintf(stderr,"update keystrokes.%p[%d]\n",keystrokes,numkeys); keystrokes = (gamesevent *)realloc(keystrokes,(int32_t)(sizeof(*keystrokes)*numkeys + k.size())); for (i=0; i 0 ) { @@ -117,7 +117,7 @@ gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std: fclose(fp); } } - fprintf(stderr,"call replay2\n"); + //fprintf(stderr,"call replay2\n"); num = games_replay2(newplayer,seed,keystrokes,numkeys,playerdata.size()==0?0:&P,0); newdata.resize(num); for (i=0; i no playerdata\n"); From 63cc12029754c0deac3f433a0af52404ad3cbf27 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:25:03 -1100 Subject: [PATCH 267/787] Replay --- src/cc/tetris.c | 25 ++++++++++++++----------- src/cc/tetris.cpp | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 487566ed4..6b63bb008 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -644,8 +644,19 @@ void *gamesiterate(struct games_state *rs) uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; gamesevent c; uint16_t skipcount=0; uint32_t eventid = 0; tetris_game *tg; WINDOW *board, *next, *hold, *score; - // Create windows for each section of the interface. + if ( rs->guiflag != 0 ) + { + // NCURSES initialization: + initscr(); // initialize curses + cbreak(); // pass key presses to program, but not signals + noecho(); // don't echo key presses to screen + keypad(stdscr, TRUE); // allow arrow keys + timeout(0); // no blocking on getch() + curs_set(0); // set the cursor to invisible + init_colors(); // setup tetris colors + } tg = tg_create(rs,22, 10); + // Create windows for each section of the interface. board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); @@ -668,7 +679,7 @@ void *gamesiterate(struct games_state *rs) { if ( skipcount > 0 ) issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); - if ( c >= 0 ) + if ( c <= 0x7f ) issue_games_events(rs,Gametxidstr,eventid,c); skipcount = 0; } else skipcount++; @@ -679,6 +690,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); + fprintf(stderr,"%04x\n",c); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); @@ -833,15 +845,6 @@ int tetris(int argc, char **argv) tg = tg_create(rs,22, 10); }*/ - // NCURSES initialization: - initscr(); // initialize curses - cbreak(); // pass key presses to program, but not signals - noecho(); // don't echo key presses to screen - keypad(stdscr, TRUE); // allow arrow keys - timeout(0); // no blocking on getch() - curs_set(0); // set the cursor to invisible - init_colors(); // setup tetris colors - // Game loop tg = (tetris_game *)gamesiterate(rs); games_bailout(rs); diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index 605c969ab..7f601bb56 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -91,7 +91,7 @@ gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std: UniValue obj; //fprintf(stderr,"got baton\n"); seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr); - fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); + //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); memset(&P,0,sizeof(P)); if ( playerdata.size() > 0 ) { From 73f31d4a2edf3b780088f2acff03ac356028b929 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:33:51 -1100 Subject: [PATCH 268/787] Keystrokes --- src/cc/dapps/dappstd.c | 10 +++++----- src/cc/tetris.c | 15 +++++++++------ 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 3992cb80e..4f67f20e5 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -889,7 +889,7 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter while ( 1 ) { gamesfname(fname,seed,counter); - //printf("check (%s)\n",fname); + printf("check (%s)\n",fname); if ( (fp= fopen(fname,"rb")) == 0 ) break; if ( (fsize= get_filesize(fp)) <= 0 ) @@ -898,7 +898,7 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter //printf("fsize.%ld\n",fsize); break; } - if ( (keystrokes= (gamesevent *)realloc(keystrokes,sizeof(*keystrokes)*(num+fsize))) == 0 ) + if ( (keystrokes= (gamesevent *)realloc(keystrokes,sizeof(*keystrokes)*num+fsize))) == 0 ) { fprintf(stderr,"error reallocating keystrokes\n"); fclose(fp); @@ -912,9 +912,9 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter return(0); } fclose(fp); - num += fsize; + num += (int32_t)(fsize / sizeof(gamesevent)); counter++; - //fprintf(stderr,"loaded %ld from (%s) total %d\n",fsize,fname,num); + fprintf(stderr,"loaded %ld from (%s) total %d\n",fsize,fname,num); } *numkeysp = num; return(keystrokes); @@ -1097,7 +1097,7 @@ int main(int argc, char **argv) seed = atol(argv[1]); // non-windows #endif // _WIN32 - //fprintf(stderr,"replay %llu\n",(long long)seed); + fprintf(stderr,"replay %llu\n",(long long)seed); return(games_replay(seed,10)); } else diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 6b63bb008..5c3f742bb 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -810,7 +810,7 @@ int tetris(int argc, char **argv) memset(rs,0,sizeof(*rs)); rs->guiflag = 1; rs->sleeptime = 1; // non-zero to allow refresh() - if ( argc == 3 && strlen(argv[2]) == 64 ) + if ( argc >= 2 && strlen(argv[2]) == 64 ) { #ifdef _WIN32 #ifdef _MSC_VER @@ -822,12 +822,15 @@ int tetris(int argc, char **argv) rs->origseed = atol(argv[1]); // non-windows #endif // _WIN32 rs->seed = rs->origseed; - strcpy(Gametxidstr,argv[2]); - fprintf(stderr,"setplayerdata %s\n",Gametxidstr); - if ( games_setplayerdata(rs,Gametxidstr) < 0 ) + if ( argc >= 3 ) { - fprintf(stderr,"invalid gametxid, or already started\n"); - return(-1); + strcpy(Gametxidstr,argv[2]); + fprintf(stderr,"setplayerdata %s\n",Gametxidstr); + if ( games_setplayerdata(rs,Gametxidstr) < 0 ) + { + fprintf(stderr,"invalid gametxid, or already started\n"); + return(-1); + } } } else rs->seed = 777; From 42b2c36f726b6f1941dc1a7fb320cf33cf04dd79 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:34:30 -1100 Subject: [PATCH 269/787] Test --- src/cc/dapps/dappstd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 4f67f20e5..18f66ca58 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -898,7 +898,7 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter //printf("fsize.%ld\n",fsize); break; } - if ( (keystrokes= (gamesevent *)realloc(keystrokes,sizeof(*keystrokes)*num+fsize))) == 0 ) + if ( (keystrokes= (gamesevent *)realloc(keystrokes,sizeof(*keystrokes)*num+fsize)) == 0 ) { fprintf(stderr,"error reallocating keystrokes\n"); fclose(fp); From bd855221dfa67a79ac3b6c6c2545c355d5815684 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:36:47 -1100 Subject: [PATCH 270/787] Activate replay --- src/cc/dapps/dappstd.c | 5 +++-- src/cc/gamescc.cpp | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 18f66ca58..d8ed48b9b 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -886,10 +886,10 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter { char fname[1024]; gamesevent *keystrokes = 0; FILE *fp; long fsize; int32_t num = 0; *numkeysp = 0; - while ( 1 ) + if ( 1 ) { gamesfname(fname,seed,counter); - printf("check (%s)\n",fname); + //printf("check (%s)\n",fname); if ( (fp= fopen(fname,"rb")) == 0 ) break; if ( (fsize= get_filesize(fp)) <= 0 ) @@ -939,6 +939,7 @@ int32_t games_replay(uint64_t seed,int32_t sleeptime) if ( seed == 0 ) seed = 777; keystrokes = games_keystrokesload(&num,seed,counter); + fprintf(stderr,"keystrokes.%p num.%d\n",keystrokes,num); if ( num > 0 ) { sprintf(fname,"%s.%llu.player",GAMENAME,(long long)seed); diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 1684e6a18..282b0051b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -102,7 +102,6 @@ gamesevent games_readevent(struct games_state *rs) int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) { struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; -return(0); rs = (struct games_state *)calloc(1,sizeof(*rs)); rs->seed = rs->origseed = seed; rs->keystrokes = keystrokes; From 81170c2e29f845c254173ab83e7d9609665999ec Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:37:28 -1100 Subject: [PATCH 271/787] Break --- src/cc/dapps/dappstd.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index d8ed48b9b..2b5900408 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -886,7 +886,7 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter { char fname[1024]; gamesevent *keystrokes = 0; FILE *fp; long fsize; int32_t num = 0; *numkeysp = 0; - if ( 1 ) + while ( 1 ) { gamesfname(fname,seed,counter); //printf("check (%s)\n",fname); @@ -915,6 +915,7 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter num += (int32_t)(fsize / sizeof(gamesevent)); counter++; fprintf(stderr,"loaded %ld from (%s) total %d\n",fsize,fname,num); + break; } *numkeysp = num; return(keystrokes); From b4a06f0cbc27d47eb441af5ac8791cfa0d4f2d0b Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:39:26 -1100 Subject: [PATCH 272/787] Test --- src/cc/dapps/dappstd.c | 2 +- src/cc/rogue/cursesd.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 2b5900408..697e9f948 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -914,7 +914,7 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter fclose(fp); num += (int32_t)(fsize / sizeof(gamesevent)); counter++; - fprintf(stderr,"loaded %ld from (%s) total %d\n",fsize,fname,num); + //fprintf(stderr,"loaded %ld from (%s) total %d\n",fsize,fname,num); break; } *numkeysp = num; diff --git a/src/cc/rogue/cursesd.h b/src/cc/rogue/cursesd.h index 0b68bc307..7dd83d435 100644 --- a/src/cc/rogue/cursesd.h +++ b/src/cc/rogue/cursesd.h @@ -175,6 +175,8 @@ char *unctrl(char c); #define leaveok(win,bf) 0 #define halfdelay(x) 0 #define nocbreak() 0 +#define cbreak() 0 +#define curs_set(x) 0 // for tetris #define init_pair(a,b,c) 0 From 5295446c978338d3eee62de4fabd001985b6d6cf Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 06:44:16 -1100 Subject: [PATCH 273/787] Revendian --- src/cc/dapps/dappstd.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 697e9f948..3653166cb 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -882,9 +882,21 @@ long get_filesize(FILE *fp) return(fsize); } +gamesevent revendian(gamesevent revx) +{ + gamesevent x = 0; + for (i=0; i>= 8; + } + return(x); +} + gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter) { - char fname[1024]; gamesevent *keystrokes = 0; FILE *fp; long fsize; int32_t num = 0; + char fname[1024]; gamesevent *keystrokes = 0; FILE *fp; long fsize; int32_t i,num = 0; *numkeysp = 0; while ( 1 ) { @@ -911,6 +923,8 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter free(keystrokes); return(0); } + for (i=0; i Date: Tue, 26 Mar 2019 06:44:59 -1100 Subject: [PATCH 274/787] Switch --- src/cc/dapps/dappstd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 3653166cb..9240c6404 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -884,11 +884,11 @@ long get_filesize(FILE *fp) gamesevent revendian(gamesevent revx) { - gamesevent x = 0; + int32_t i; gamesevent x = 0; for (i=0; i>= 8; } return(x); From 9df89f97fcccf72444b83e06bd6ea102572b9fd3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 07:01:15 -1100 Subject: [PATCH 275/787] +print --- src/cc/dapps/dappstd.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 9240c6404..5961b5e7a 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -885,12 +885,10 @@ long get_filesize(FILE *fp) gamesevent revendian(gamesevent revx) { int32_t i; gamesevent x = 0; + fprintf(stderr,"%04x -> ",revx); for (i=0; i>= 8; - } + ((uint8_t *)&x)[i] = ((uint8_t *)&revx)[sizeof(gamesevent)-1-i]; + fprintf(stderr,"%04x\n",x); return(x); } From 15143c0129bb15824ab1c06f9b4d97319bf3617b Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 07:02:35 -1100 Subject: [PATCH 276/787] Fix --- src/cc/dapps/dappstd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 5961b5e7a..6ef4381e5 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -921,10 +921,10 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter free(keystrokes); return(0); } - for (i=0; i Date: Tue, 26 Mar 2019 07:09:02 -1100 Subject: [PATCH 277/787] display_board(board,tg); display_piece(next,tg->next); display_piece(hold,tg->stored); display_score(score,tg); if ( (counter++ % 5) == 0 ) doupdate(); --- src/cc/dapps/dappstd.c | 6 +++--- src/cc/tetris.c | 15 ++++++++++----- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 6ef4381e5..eee51739b 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -885,10 +885,10 @@ long get_filesize(FILE *fp) gamesevent revendian(gamesevent revx) { int32_t i; gamesevent x = 0; - fprintf(stderr,"%04x -> ",revx); + //fprintf(stderr,"%04x -> ",revx); for (i=0; i 0 ) { sprintf(fname,"%s.%llu.player",GAMENAME,(long long)seed); diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 5c3f742bb..cb54ee261 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -299,11 +299,11 @@ static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move { switch (move) { case TM_LEFT: - fprintf(stderr,"LEFT "); + //fprintf(stderr,"LEFT "); tg_move(obj, -1); break; case TM_RIGHT: - fprintf(stderr,"RIGHT "); + //fprintf(stderr,"RIGHT "); tg_move(obj, 1); break; case TM_DROP: @@ -644,7 +644,7 @@ void *gamesiterate(struct games_state *rs) uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; gamesevent c; uint16_t skipcount=0; uint32_t eventid = 0; tetris_game *tg; WINDOW *board, *next, *hold, *score; - if ( rs->guiflag != 0 ) + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) { // NCURSES initialization: initscr(); // initialize curses @@ -664,15 +664,18 @@ void *gamesiterate(struct games_state *rs) while ( running != 0 ) { running = tg_tick(rs,tg,move); - if ( rs->guiflag != 0 ) + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) { -#ifdef STANDALONE display_board(board,tg); display_piece(next,tg->next); display_piece(hold,tg->stored); display_score(score,tg); if ( (counter++ % 5) == 0 ) doupdate(); + } + if ( rs->guiflag != 0 ) + { +#ifdef STANDALONE sleep_milli(10); c = games_readevent(rs); if ( c <= 0x7f || skipcount == 0x3fff ) @@ -687,6 +690,8 @@ void *gamesiterate(struct games_state *rs) } else { + if ( rs->sleeptime >= 1000 ) + sleep_milli(rs->sleeptime/1000); if ( skipcount == 0 ) { c = games_readevent(rs); From 140468d27dcba162144a60412f7015671ad2d7a5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 07:10:36 -1100 Subject: [PATCH 278/787] -print --- src/cc/dapps/dappstd.c | 2 +- src/cc/tetris.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index eee51739b..9b44bc6a9 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -1112,7 +1112,7 @@ int main(int argc, char **argv) #endif // _WIN32 fprintf(stderr,"replay %llu\n",(long long)seed); - return(games_replay(seed,10)); + return(games_replay(seed,3)); } else { diff --git a/src/cc/tetris.c b/src/cc/tetris.c index cb54ee261..bd9a5526d 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -695,7 +695,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); - fprintf(stderr,"%04x\n",c); + //fprintf(stderr,"%04x\n",c); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); From 2bcc183fb073c89dff6b9ca5e145ed0ba089a1ea Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 07:16:24 -1100 Subject: [PATCH 279/787] +print --- src/cc/tetris.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index bd9a5526d..cb54ee261 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -695,7 +695,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); - //fprintf(stderr,"%04x\n",c); + fprintf(stderr,"%04x\n",c); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); From bd4bab770c09f227000b5641b03dcf07fd315dea Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 07:23:50 -1100 Subject: [PATCH 280/787] Dont flip --- src/cc/dapps/dappstd.c | 12 +----------- src/cc/gamescc.cpp | 12 +++++++++++- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 9b44bc6a9..764a569ac 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -882,16 +882,6 @@ long get_filesize(FILE *fp) return(fsize); } -gamesevent revendian(gamesevent revx) -{ - int32_t i; gamesevent x = 0; - //fprintf(stderr,"%04x -> ",revx); - for (i=0; i ",revx); + for (i=0; i Date: Tue, 26 Mar 2019 07:24:48 -1100 Subject: [PATCH 281/787] gamesevent games_revendian(gamesevent revx) --- src/cc/tetris.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/tetris.h b/src/cc/tetris.h index 94d56dbfc..338eee3cb 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -199,6 +199,7 @@ void *gamesiterate(struct games_state *rs); void games_packitemstr(char *packitemstr,struct games_packitem *item); uint64_t _games_rngnext(uint64_t initseed); int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); +gamesevent games_revendian(gamesevent revx); #endif From 4799abb82dd0bcf86353a8eee3246599a458ce92 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 07:27:08 -1100 Subject: [PATCH 282/787] -print --- src/cc/tetris.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index cb54ee261..bd9a5526d 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -695,7 +695,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); - fprintf(stderr,"%04x\n",c); + //fprintf(stderr,"%04x\n",c); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); From 317b1a249829b6f4e400c295280c0af00731096e Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:02:29 -1100 Subject: [PATCH 283/787] Tweaks --- src/cc/gamescc.cpp | 222 +++++++++++++++++++++++++++++++++++------ src/cc/gamescc.h | 12 ++- src/cc/rogue_rpc.cpp | 71 +++++++------ src/cc/tetris.c | 12 ++- src/cc/tetris.cpp | 232 ++++++------------------------------------- src/cc/tetris.h | 14 +-- 6 files changed, 288 insertions(+), 275 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index c01a89b69..063da9355 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -16,6 +16,9 @@ #include "gamescc.h" #include "tetris.c" // replace with game code +int32_t GAMEDATA(struct games_player *P,void *ptr); +void GAMEJSON(UniValue &obj,struct games_player *P); + uint64_t _games_rngnext(uint64_t initseed) { uint16_t seeds[4]; int32_t i; @@ -92,19 +95,6 @@ gamesevent games_readevent(struct games_state *rs) //_quit(); return(27); } - /*if ( rs != 0 && rs->guiflag != 0 ) - { - if ( rs->num < sizeof(rs->buffered) ) - { - rs->buffered[rs->num++] = ch; - if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) - { - rs->needflush = (uint32_t)time(NULL); - //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); - //sleep(3); - } - } else fprintf(stderr,"buffer filled without flushed\n"); - }*/ } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); return(ch); } @@ -121,7 +111,6 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 { rs->P = *player; rs->restoring = 1; - //fprintf(stderr,"restore player packsize.%d HP.%d\n",rs->P.packsize,rs->P.hitpoints); if ( rs->P.packsize > MAXPACK ) rs->P.packsize = MAXPACK; } @@ -150,14 +139,15 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 /*if ( (fp= fopen("checkfile","wb")) != 0 ) { //save_file(rs,fp,0); - //fprintf(stderr,"gold.%d hp.%d strength.%d/%d level.%d exp.%d dungeon.%d data[%d]\n",rs->P.gold,rs->P.hitpoints,rs->P.strength&0xffff,rs->P.strength>>16,rs->P.level,rs->P.experience,rs->P.dungeonlevel,rs->playersize); if ( newdata != 0 && rs->playersize > 0 ) memcpy(newdata,rs->playerdata,rs->playersize); }*/ if ( ptr != 0 ) { // extract data from ptr - if ( newdata != 0 && rs->playersize > 0 ) + if ( GAMEDATA(&rs->P,ptr) < 0 ) + memset(&rs->P,0,sizeof(rs->P)); + else if ( newdata != 0 && rs->playersize > 0 ) memcpy(newdata,rs->playerdata,rs->playersize); free(ptr); } @@ -695,7 +685,6 @@ UniValue games_playerobj(std::vector playerdata,uint256 playertxid,uint } datastr[i<<1] = 0; } - int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,pad; for (i=0; i playerdata,uint256 playertxid,uint free(datastr); } obj.push_back(Pair("pack",a)); - obj.push_back(Pair("packsize",(int64_t)P.packsize)); - obj.push_back(Pair("hitpoints",(int64_t)P.hitpoints)); - obj.push_back(Pair("strength",(int64_t)(P.strength&0xffff))); - obj.push_back(Pair("maxstrength",(int64_t)(P.strength>>16))); - obj.push_back(Pair("level",(int64_t)P.level)); - obj.push_back(Pair("experience",(int64_t)P.experience)); - obj.push_back(Pair("dungeonlevel",(int64_t)P.dungeonlevel)); + GAMEPLAYERJSON(obj,&P); obj.push_back(Pair("chain",symbol)); obj.push_back(Pair("pname",pname)); return(obj); @@ -834,6 +817,24 @@ int32_t games_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx, return(0); } +int64_t games_buyins(uint256 gametxid) +{ + int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; int64_t buyins = 0; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + if ( spenttx.vout[0].nValue > GAMES_REGISTRATIONSIZE ) + buyins += (spenttx.vout[0].nValue - GAMES_REGISTRATIONSIZE); + } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"vout %d is unspent\n",vout); + } + return(buyins); +} + uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr) { CBlockIndex *pindex; int32_t ht,openslots,delay,numplayers; uint256 hashBlock; uint64_t seed=0; char cmd[512]; CTransaction tx; @@ -861,6 +862,7 @@ uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 obj.push_back(Pair("alive",games_playersalive(openslots,numplayers,gametxid,maxplayers,ht,tx))); obj.push_back(Pair("openslots",openslots)); obj.push_back(Pair("numplayers",numplayers)); + obj.push_back(Pair("buyins",ValueFromAmount(game_buyins(gametxid)))); } obj.push_back(Pair("maxplayers",maxplayers)); obj.push_back(Pair("buyin",ValueFromAmount(buyin))); @@ -885,6 +887,27 @@ UniValue games_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *param } else return(cclib_error(result,"couldnt reparse params")); } +void disp_gamesplayerdata(std::vector playerdata) +{ + struct games_player P; int32_t i; char packitemstr[512],line[512]; + if ( playerdata.size() > 0 ) + { + for (i=0; i &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr) +{ + CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64]; gamesevent *keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP; + gamespk = GetUnspendable(cp,0); + *numkeysp = 0; + seed = 0; + num = numkeys = 0; + playertxid = zeroid; + str[0] = 0; + if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 ) + { + if ( (retval= games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,gamesaddr,numplayers,symbol,pname)) == 0 ) + { + UniValue obj; + //fprintf(stderr,"got baton\n"); + seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr); + //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + for (i=0; i no playerdata\n"); + newdata.resize(0); + *numkeysp = numkeys; + return(keystrokes); + } + else + { + *numkeysp = numkeys; + return(keystrokes); + } + } else num = 0; + } + else + { + fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval); + if ( keystrokes != 0 ) + free(keystrokes), keystrokes = 0; + } + } else fprintf(stderr,"extractgame: invalid game\n"); + //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str()); + return(0); +} + UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ); CPubKey pk,gamespk; int32_t i,n,numkeys,flag = 0; uint64_t seed; char str[512],gamesaddr[64],*pubstr,*hexstr; gamesevent *keystrokes = 0; std::vector newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33]; @@ -1380,7 +1478,7 @@ UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } -UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params,char *method) { //vin0 -> highlander vout from creategame TCBOO //vin1 -> keystrokes baton of completed game, must be last to quit or first to win, only spent registration batons matter. If more than 60 blocks since last keystrokes, it is forfeit @@ -1393,8 +1491,8 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) // vout0 -> playerdata marker // vout0 -> 1% ingame gold // get any playerdata, get all keystrokes, replay game and compare final state - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); char *method = (char *)"bailout"; - UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,dungeonlevel,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64]; gamesevent *keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid; + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64]; gamesevent *keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid; struct CCcontract_info *cpTokens, tokensC; if ( txfee == 0 ) @@ -1452,11 +1550,10 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) newdata[i] = player[i]; ((uint8_t *)&P)[i] = player[i]; } - if ( (P.gold <= 0 || P.hitpoints <= 0 || (P.strength&0xffff) <= 0 || P.level <= 0 || P.experience <= 0 || P.dungeonlevel <= 0) ) + if ( disp_gamesplayer(str,&P) < 0 ) { //fprintf(stderr,"zero value character was killed -> no playerdata\n"); newdata.resize(0); - //P.gold = (P.gold * 8) / 10; } else { @@ -1464,7 +1561,7 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens,NULL))); // marker to token cc addr, burnable and validated mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,1,mypk)); cashout = games_cashout(&P); - fprintf(stderr,"\nextracted $$$gold.%d -> %.8f GAME hp.%d strength.%d/%d level.%d exp.%d dl.%d n.%d amulet.%d\n",P.gold,(double)cashout/COIN,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel,n,P.amulet); + fprintf(stderr,"\ncashout %.8f extracted %s\n",(double)cashout/COIN,str); if ( funcid == 'H' && maxplayers > 1 ) { if ( P.amulet == 0 ) @@ -1474,7 +1571,7 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) else if ( games_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 ) return(cclib_error(result,"highlander must be a winner or last one standing")); } - cashout += numplayers * buyin; + cashout += games_buyins(gametxid);//numplayers * buyin; } if ( cashout > 0 ) { @@ -1516,6 +1613,16 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } +UniValue games_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + return(games_finish(txfee,cp,params,"bailout")); +} + +UniValue games_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + return(games_finish(txfee,cp,params,"highlander")); +} + UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 tokenid,gametxid,txid,hashBlock; CTransaction playertx,tx; int32_t maxplayers,vout,numvouts; std::vector playerdata; CPubKey gamespk,mypk,pk; std::string symbol,pname; char coinaddr[64]; @@ -1638,6 +1745,59 @@ UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } +int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) +{ + static uint32_t good,bad; static uint256 prevgame; + char str[512],gamesaddr[64],str2[67],fname[64]; gamesevent *keystrokes; int32_t i,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; + *cashoutp = 0; + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,gamesaddr,gamespk,pk); + if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) + { + free(keystrokes); + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); + remove(fname); + for (i=0; ievalcode == EVAL_GAMES ) \ return(games_keystrokes(txfee,cp,params)); \ else if ( strcmp(method,"extract") == 0 ) \ return(games_extract(txfee,cp,params)); \ - else if ( strcmp(method,"finish") == 0 ) \ - return(games_finish(txfee,cp,params)); \ + else if ( strcmp(method,"bailout") == 0 ) \ + return(games_bailout(txfee,cp,params)); \ + else if ( strcmp(method,"highlander") == 0 ) \ + return(games_highlander(txfee,cp,params)); \ else if ( strcmp(method,"fund") == 0 ) \ return(games_fund(txfee,cp,params)); \ else \ diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index ee5f3d566..4b74e21b7 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -283,6 +283,24 @@ int32_t rogue_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx, return(0); } +int64_t rogue_buyins(uint256 gametxid) +{ + int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; int64_t buyins = 0; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + if ( spenttx.vout[0].nValue > ROGUE_REGISTRATIONSIZE ) + buyins += (spenttx.vout[0].nValue - ROGUE_REGISTRATIONSIZE); + } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"vout %d is unspent\n",vout); + } + return(buyins); +} + int32_t rogue_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0) { uint256 hashBlock; int32_t i,numvouts; char coinaddr[64]; CPubKey roguepk; uint64_t txfee = 10000; @@ -690,6 +708,7 @@ uint64_t rogue_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 obj.push_back(Pair("alive",rogue_playersalive(openslots,numplayers,gametxid,maxplayers,ht,tx))); obj.push_back(Pair("openslots",openslots)); obj.push_back(Pair("numplayers",numplayers)); + obj.push_back(Pair("buyins",ValueFromAmount(rogue_buyins(gametxid)))); } obj.push_back(Pair("maxplayers",maxplayers)); obj.push_back(Pair("buyin",ValueFromAmount(buyin))); @@ -1093,6 +1112,20 @@ UniValue rogue_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } +int64_t rogue_cashout(struct rogue_player *P) +{ + int32_t dungeonlevel; int64_t mult = 10; + if ( P->amulet != 0 ) + mult *= 5; + dungeonlevel = P->dungeonlevel; + if ( P->amulet != 0 && dungeonlevel < 26 ) + dungeonlevel = 26; + if ( dungeonlevel > 42 ) + dungeonlevel = 42; + cashout = (uint64_t)P->gold * P->gold * mult * dungeonlevel; + return(cashout); +} + int32_t rogue_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) { static uint32_t good,bad; static uint256 prevgame; @@ -1105,17 +1138,9 @@ int32_t rogue_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct C free(keystrokes); sprintf(fname,"rogue.%llu.pack",(long long)seed); remove(fname); - for (i=0; i 42 ) - dungeonlevel = 42; - *cashoutp = (uint64_t)P.gold * P.gold * mult * dungeonlevel; + *cashoutp = rogue_cashout(&P); if ( newdata == playerdata ) { if ( gametxid != prevgame ) @@ -1206,16 +1231,10 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param result.push_back(Pair("name","rogue")); result.push_back(Pair("method",method)); result.push_back(Pair("myrogueaddr",myrogueaddr)); + mult = 10; //100000; if ( strcmp(method,"bailout") == 0 ) - { funcid = 'Q'; - mult = 10; //100000; - } - else - { - funcid = 'H'; - mult = 20; //200000; - } + else funcid = 'H'; if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) { if ( n > 0 ) @@ -1264,13 +1283,10 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param cpTokens = CCinit(&tokensC, EVAL_TOKENS); mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens,NULL))); // marker to token cc addr, burnable and validated mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,1,mypk)); - if ( P.amulet != 0 ) - mult *= 5; - dungeonlevel = P.dungeonlevel; - if ( P.amulet != 0 && dungeonlevel < 26 ) - dungeonlevel = 26; - cashout = (uint64_t)P.gold * P.gold * mult * dungeonlevel; + cashout = rogue_cashout(&P); fprintf(stderr,"\nextracted $$$gold.%d -> %.8f ROGUE hp.%d strength.%d/%d level.%d exp.%d dl.%d n.%d amulet.%d\n",P.gold,(double)cashout/COIN,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel,n,P.amulet); + if ( komodo_nextheight() > 77777 && cashout > ROGUE_MAXCASHOUT ) + cashout = ROGUE_MAXCASHOUT; if ( funcid == 'H' && maxplayers > 1 ) { if ( P.amulet == 0 ) @@ -1280,12 +1296,11 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param else if ( rogue_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 ) return(cclib_error(result,"highlander must be a winner or last one standing")); } + cashout *= 2; cashout += numplayers * buyin; } if ( cashout > 0 ) { - if ( komodo_nextheight() > 77777 && cashout > ROGUE_MAXCASHOUT ) - cashout = ROGUE_MAXCASHOUT; if ( (inputsum= AddCClibInputs(cp,mtx,roguepk,cashout,60,cp->unspendableCCaddr)) > cashout ) CCchange = (inputsum - cashout); else fprintf(stderr,"couldnt find enough utxos\n"); @@ -1598,13 +1613,13 @@ bool rogue_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C if ( enabled != 0 ) return eval->Invalid("mismatched playerdata"); } + if ( height > 777777 && cashout > ROGUE_MAXCASHOUT ) + cashout = ROGUE_MAXCASHOUT; if ( funcid == 'H' ) { cashout *= 2; - //cashout += numplayers * buyin; + cashout += rogue_buyins(gametxid); } - if ( height > 777777 && cashout > ROGUE_MAXCASHOUT ) - cashout = ROGUE_MAXCASHOUT; sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d txid.%s %.8f vs vout2 %.8f",tokentx,decoded,height,txid.GetHex().c_str(),(double)cashout/COIN,(double)tx.vout[2].nValue/COIN); if ( strcmp(laststr,cashstr) != 0 ) { diff --git a/src/cc/tetris.c b/src/cc/tetris.c index bd9a5526d..54ca3b953 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -7,12 +7,20 @@ also, the standalone game needs to support argv of seed gametxid, along with replay args */ -static int random_tetromino(struct games_state *rs) +int random_tetromino(struct games_state *rs) { rs->seed = _games_rngnext(rs->seed); return(rs->seed % NUM_TETROMINOS); } +int32_t tetrisdata(struct games_player *P,void *ptr) +{ + tetris_game *tg = ptr; + P->gold = tg->points; + P->dungeonlevel = tg->level; + return(0); +} + /***************************************************************************/ /** https://github.com/brenns10/tetris @file main.c @@ -676,7 +684,7 @@ void *gamesiterate(struct games_state *rs) if ( rs->guiflag != 0 ) { #ifdef STANDALONE - sleep_milli(10); + sleep_milli(25); c = games_readevent(rs); if ( c <= 0x7f || skipcount == 0x3fff ) { diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index 7f601bb56..e44b50925 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -14,14 +14,37 @@ * * ******************************************************************************/ -int32_t games_findbaton(struct CCcontract_info *cp,uint256 &playertxid,gamesevent **keystrokesp,int32_t &numkeys,int32_t ®slot,std::vector &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname); -int32_t games_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0); -uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr); - // game specific code for daemon void games_packitemstr(char *packitemstr,struct games_packitem *item) { - sprintf(packitemstr,"not yet"); + sprintf(packitemstr,""); +} + +int64_t games_cashout(struct games_player *P) +{ + int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0; + cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel; + return(cashout); +} + +void tetrisjson(UniValue &obj,struct games_player *P) +{ + obj.push_back(Pair("packsize",(int64_t)P->packsize)); + obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); + obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff))); + obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16))); + obj.push_back(Pair("level",(int64_t)P->level)); + obj.push_back(Pair("experience",(int64_t)P->experience)); + obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel)); +} + +int32_t disp_gamesplayer(char *str,struct games_player *P) +{ + str[0] = 0; + if ( P->gold <= 0 || P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) + return(-1); + sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); + return(0); } int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) @@ -43,205 +66,6 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay } else return(-1); } -int64_t games_cashout(struct games_player *P) -{ - int32_t dungeonlevel; int64_t mult=10,cashout = 0; - if ( P->amulet != 0 ) - mult *= 5; - dungeonlevel = P->dungeonlevel; - if ( P->amulet != 0 && dungeonlevel < 26 ) - dungeonlevel = 26; - cashout = (uint64_t)P->gold * P->gold * mult * dungeonlevel; - return(cashout); -} - -void disp_gamesplayerdata(std::vector playerdata) -{ - struct games_player P; int32_t i; char packitemstr[512]; - if ( playerdata.size() > 0 ) - { - for (i=0; i>16,P.level,P.experience,P.dungeonlevel); - for (i=0; i &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr) -{ - CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64]; gamesevent *keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP; - gamespk = GetUnspendable(cp,0); - *numkeysp = 0; - seed = 0; - num = numkeys = 0; - playertxid = zeroid; - str[0] = 0; - if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 ) - { - if ( (retval= games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,gamesaddr,numplayers,symbol,pname)) == 0 ) - { - UniValue obj; - //fprintf(stderr,"got baton\n"); - seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr); - //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); - memset(&P,0,sizeof(P)); - if ( playerdata.size() > 0 ) - { - for (i=0; i no playerdata\n"); - newdata.resize(0); - *numkeysp = numkeys; - return(keystrokes); - /* P.gold = (P.gold * 8) / 10; - if ( keystrokes != 0 ) - { - free(keystrokes); - keystrokes = 0; - *numkeysp = 0; - return(keystrokes); - }*/ - } - else - { - sprintf(str,"$$$gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",endP.gold,endP.hitpoints,endP.strength&0xffff,endP.strength>>16,endP.level,endP.experience,endP.dungeonlevel); - //fprintf(stderr,"%s\n",str); - *numkeysp = numkeys; - return(keystrokes); - } - } else num = 0; - } - else - { - fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval); - if ( keystrokes != 0 ) - free(keystrokes), keystrokes = 0; - } - } else fprintf(stderr,"extractgame: invalid game\n"); - //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str()); - return(0); -} - -int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) -{ - static uint32_t good,bad; static uint256 prevgame; - char str[512],gamesaddr[64],str2[67],fname[64]; gamesevent *keystrokes; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; - *cashoutp = 0; - gamespk = GetUnspendable(cp,0); - GetCCaddress1of2(cp,gamesaddr,gamespk,pk); - if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) - { - free(keystrokes); - sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); - remove(fname); - - for (i=0; i no playerdata, good.%d bad.%d\n",good,bad); - } - *cashoutp = 0; - return(0); - } - } - if ( gametxid != prevgame ) - { - prevgame = gametxid; - bad++; - disp_gamesplayerdata(newdata); - disp_gamesplayerdata(playerdata); - fprintf(stderr,"%s playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d\n",gametxid.GetHex().c_str(),P.gold,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel); - fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,gamesaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); - } - } - sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); - remove(fname); - //fprintf(stderr,"no keys games_extractgame %s\n",gametxid.GetHex().c_str()); - return(-1); -} - bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) { return(true); diff --git a/src/cc/tetris.h b/src/cc/tetris.h index 338eee3cb..79a1c2c3e 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -163,9 +163,12 @@ void tg_print(tetris_game *obj, FILE *f); * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ -#define GAMENAME "tetris" -#define GAMEMAIN tetris -#define CHAINNAME "GTEST" +#define GAMENAME "tetris" // name of executable +#define GAMEMAIN tetris // main program of game +#define GAMEJSON tetrisjson // displays game specific json +#define GAMEDATA tetrisdata // extracts data from game specific variables into games_state +#define CHAINNAME "GTEST" // -ac_name= +typedef uint16_t gamesevent; // can be 8, 16, 32, or 64 bits #define MAXPACK 23 struct games_packitem @@ -180,8 +183,6 @@ struct games_player struct games_packitem gamespack[MAXPACK]; }; -typedef uint16_t gamesevent; - struct games_state { uint64_t seed,origseed; @@ -191,7 +192,7 @@ struct games_state FILE *logfp; struct games_player P; gamesevent buffered[5000],*keystrokes; - uint8_t playerdata[1024]; + uint8_t playerdata[8192]; }; extern struct games_state globalR; void *gamesiterate(struct games_state *rs); @@ -200,6 +201,7 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item); uint64_t _games_rngnext(uint64_t initseed); int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); gamesevent games_revendian(gamesevent revx); +int32_t disp_gamesplayer(char *str,struct games_player *P); #endif From ef1c722d2de8985d423c2ff17ca41ccd0c125490 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:04:11 -1100 Subject: [PATCH 284/787] Test --- src/cc/gamescc.cpp | 3 ++- src/cc/tetris.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 063da9355..6763b9033 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -17,7 +17,6 @@ #include "tetris.c" // replace with game code int32_t GAMEDATA(struct games_player *P,void *ptr); -void GAMEJSON(UniValue &obj,struct games_player *P); uint64_t _games_rngnext(uint64_t initseed) { @@ -160,6 +159,8 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 #include "tetris.cpp" // replace with game specific functions +void GAMEJSON(UniValue &obj,struct games_player *P); + /* ./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\" { diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 54ca3b953..dd142e036 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -15,7 +15,7 @@ int random_tetromino(struct games_state *rs) int32_t tetrisdata(struct games_player *P,void *ptr) { - tetris_game *tg = ptr; + tetris_game *tg = (tetris_game *)ptr; P->gold = tg->points; P->dungeonlevel = tg->level; return(0); From 1254c3ed5cbb7e3ad6e26c3d859956be2b723198 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:10:39 -1100 Subject: [PATCH 285/787] Test --- src/cc/gamescc.cpp | 14 +++++++------- src/cc/tetris.cpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 6763b9033..62d49c3dd 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -818,7 +818,7 @@ int32_t games_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx, return(0); } -int64_t games_buyins(uint256 gametxid) +int64_t games_buyins(uint256 gametxid,int32_t maxplayers) { int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; int64_t buyins = 0; for (i=0; i playerdata) { - struct games_player P; int32_t i; char packitemstr[512],line[512]; + struct games_player P; int32_t i; char packitemstr[512],str[512]; if ( playerdata.size() > 0 ) { for (i=0; i 1% ingame gold // get any playerdata, get all keystrokes, replay game and compare final state CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64]; gamesevent *keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid; + UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64],str[512]; gamesevent *keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid; struct CCcontract_info *cpTokens, tokensC; if ( txfee == 0 ) @@ -1572,7 +1572,7 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params,ch else if ( games_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 ) return(cclib_error(result,"highlander must be a winner or last one standing")); } - cashout += games_buyins(gametxid);//numplayers * buyin; + cashout += games_buyins(gametxid,maxplayers);//numplayers * buyin; } if ( cashout > 0 ) { @@ -1616,12 +1616,12 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params,ch UniValue games_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - return(games_finish(txfee,cp,params,"bailout")); + return(games_finish(txfee,cp,params,(char *)"bailout")); } UniValue games_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - return(games_finish(txfee,cp,params,"highlander")); + return(games_finish(txfee,cp,params,(char *)"highlander")); } UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index e44b50925..a862e2a3f 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -17,7 +17,7 @@ // game specific code for daemon void games_packitemstr(char *packitemstr,struct games_packitem *item) { - sprintf(packitemstr,""); + strcpy(packitemstr,""); } int64_t games_cashout(struct games_player *P) From f1737210077500e3ebec693f8ab674fec799251c Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:11:29 -1100 Subject: [PATCH 286/787] Test --- src/cc/tetris.cpp | 2 +- src/cc/tetris.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index a862e2a3f..a461d139e 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -27,7 +27,7 @@ int64_t games_cashout(struct games_player *P) return(cashout); } -void tetrisjson(UniValue &obj,struct games_player *P) +void tetrisplayerjson(UniValue &obj,struct games_player *P) { obj.push_back(Pair("packsize",(int64_t)P->packsize)); obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); diff --git a/src/cc/tetris.h b/src/cc/tetris.h index 79a1c2c3e..9d16a8e8b 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -165,7 +165,7 @@ void tg_print(tetris_game *obj, FILE *f); ******************************************************************************/ #define GAMENAME "tetris" // name of executable #define GAMEMAIN tetris // main program of game -#define GAMEJSON tetrisjson // displays game specific json +#define GAMEPLAYERJSON tetrisplayerjson // displays game specific json #define GAMEDATA tetrisdata // extracts data from game specific variables into games_state #define CHAINNAME "GTEST" // -ac_name= typedef uint16_t gamesevent; // can be 8, 16, 32, or 64 bits From 5abd8a92071773f5e85e7a33dfb2c587e0be71ac Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:19:43 -1100 Subject: [PATCH 287/787] test --- src/cc/tetris.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index dd142e036..3e2d4a24c 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -307,11 +307,11 @@ static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move { switch (move) { case TM_LEFT: - //fprintf(stderr,"LEFT "); + fprintf(stderr,"LEFT "); tg_move(obj, -1); break; case TM_RIGHT: - //fprintf(stderr,"RIGHT "); + fprintf(stderr,"RIGHT "); tg_move(obj, 1); break; case TM_DROP: @@ -459,7 +459,7 @@ void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols) obj->stored.ori = 0; obj->stored.loc.row = 0; obj->next.loc.col = obj->cols/2 - 2; - printf("%d", obj->falling.loc.col); + //printf("%d", obj->falling.loc.col); } tetris_game *tg_create(struct games_state *rs,int rows, int cols) @@ -672,7 +672,7 @@ void *gamesiterate(struct games_state *rs) while ( running != 0 ) { running = tg_tick(rs,tg,move); - if ( rs->guiflag != 0 || rs->sleeptime != 0 ) + if ( 0 && (rs->guiflag != 0 || rs->sleeptime != 0) ) { display_board(board,tg); display_piece(next,tg->next); @@ -703,7 +703,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); - //fprintf(stderr,"%04x\n",c); + fprintf(stderr,"%04x\n",c); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); From 701f7027f5c9252482850d68f25bfcd952c05cf6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:21:06 -1100 Subject: [PATCH 288/787] -flip --- src/cc/dapps/dappstd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 764a569ac..925b8749a 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -913,8 +913,8 @@ gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter } fclose(fp); num += (int32_t)(fsize / sizeof(gamesevent)); - for (i=0; i Date: Tue, 26 Mar 2019 09:22:13 -1100 Subject: [PATCH 289/787] -print --- src/cc/tetris.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 3e2d4a24c..7db0e7fa0 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -307,11 +307,11 @@ static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move { switch (move) { case TM_LEFT: - fprintf(stderr,"LEFT "); + //fprintf(stderr,"LEFT "); tg_move(obj, -1); break; case TM_RIGHT: - fprintf(stderr,"RIGHT "); + //fprintf(stderr,"RIGHT "); tg_move(obj, 1); break; case TM_DROP: @@ -703,7 +703,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); - fprintf(stderr,"%04x\n",c); + //fprintf(stderr,"%04x\n",c); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); From 250fe82e9c18dd63e433efad944c9e8ce81db9eb Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:23:50 -1100 Subject: [PATCH 290/787] Display --- src/cc/tetris.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 7db0e7fa0..e5a9be712 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -672,7 +672,7 @@ void *gamesiterate(struct games_state *rs) while ( running != 0 ) { running = tg_tick(rs,tg,move); - if ( 0 && (rs->guiflag != 0 || rs->sleeptime != 0) ) + if ( 1 && (rs->guiflag != 0 || rs->sleeptime != 0) ) { display_board(board,tg); display_piece(next,tg->next); @@ -698,8 +698,8 @@ void *gamesiterate(struct games_state *rs) } else { - if ( rs->sleeptime >= 1000 ) - sleep_milli(rs->sleeptime/1000); + if ( rs->sleeptime != 0 ) + sleep_milli(1); if ( skipcount == 0 ) { c = games_readevent(rs); From 7c63b83176b25a0e14cdfae2ad5ebd6821fa69df Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:26:40 -1100 Subject: [PATCH 291/787] Test --- src/cc/tetris.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index e5a9be712..6308740e7 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -678,13 +678,13 @@ void *gamesiterate(struct games_state *rs) display_piece(next,tg->next); display_piece(hold,tg->stored); display_score(score,tg); - if ( (counter++ % 5) == 0 ) - doupdate(); } if ( rs->guiflag != 0 ) { #ifdef STANDALONE - sleep_milli(25); + sleep_milli(15); + if ( (counter++ % 10) == 0 ) + doupdate(); c = games_readevent(rs); if ( c <= 0x7f || skipcount == 0x3fff ) { @@ -699,7 +699,11 @@ void *gamesiterate(struct games_state *rs) else { if ( rs->sleeptime != 0 ) + { sleep_milli(1); + if ( (counter++ % 20) == 0 ) + doupdate(); + } if ( skipcount == 0 ) { c = games_readevent(rs); From a29ad0f9bbbd478c15a296e8ef62ed2362061991 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:28:19 -1100 Subject: [PATCH 292/787] +print --- src/cc/tetris.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 6308740e7..852aecbc9 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -707,7 +707,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); - //fprintf(stderr,"%04x\n",c); + fprintf(stderr,"%04x\n",c); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); From 0d7dbc3929e4462882aa1848dfea6860c96aed29 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:31:44 -1100 Subject: [PATCH 293/787] rs->replaydone --- src/cc/tetris.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 852aecbc9..3bf5b098a 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -698,6 +698,8 @@ void *gamesiterate(struct games_state *rs) } else { + if ( rs->replaydone != 0 ) + break; if ( rs->sleeptime != 0 ) { sleep_milli(1); @@ -707,7 +709,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); - fprintf(stderr,"%04x\n",c); + fprintf(stderr,"%04x score.%d level.%d\n",c,tg->score,tg->level); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); From b8f944dfe9fa3dfdf03bde72e61f6a830b221682 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:32:24 -1100 Subject: [PATCH 294/787] Points --- src/cc/tetris.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 3bf5b098a..97c35ecd9 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -709,7 +709,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); - fprintf(stderr,"%04x score.%d level.%d\n",c,tg->score,tg->level); + fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); From d35bad65b5bc02bc60995acba09f88eb68ab079e Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:35:18 -1100 Subject: [PATCH 295/787] Print --- src/cc/gamescc.cpp | 1 + src/cc/tetris.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 62d49c3dd..ce0b3e479 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -152,6 +152,7 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 } n = rs->playersize; free(rs); + fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level); return(n); } diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 97c35ecd9..214145659 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -709,7 +709,7 @@ void *gamesiterate(struct games_state *rs) if ( skipcount == 0 ) { c = games_readevent(rs); - fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); + //fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); if ( (c & 0x4000) == 0x4000 ) { skipcount = (c & 0x3fff); From 4b555126d5c5ca1163a15d2203cd10e2c54ca134 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:36:15 -1100 Subject: [PATCH 296/787] Test --- src/cc/gamescc.cpp | 1 - src/cc/tetris.c | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index ce0b3e479..62d49c3dd 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -152,7 +152,6 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 } n = rs->playersize; free(rs); - fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level); return(n); } diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 214145659..0a613c0c9 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -18,6 +18,7 @@ int32_t tetrisdata(struct games_player *P,void *ptr) tetris_game *tg = (tetris_game *)ptr; P->gold = tg->points; P->dungeonlevel = tg->level; + fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level); return(0); } From 91fcfefd16831bebce19239153294ed092222655 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:38:35 -1100 Subject: [PATCH 297/787] Test --- src/cc/tetris.c | 2 +- src/cc/tetris.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/tetris.c b/src/cc/tetris.c index 0a613c0c9..c507ec019 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -18,7 +18,7 @@ int32_t tetrisdata(struct games_player *P,void *ptr) tetris_game *tg = (tetris_game *)ptr; P->gold = tg->points; P->dungeonlevel = tg->level; - fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level); + //fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level); return(0); } diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index a461d139e..39e8f1a65 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -41,7 +41,7 @@ void tetrisplayerjson(UniValue &obj,struct games_player *P) int32_t disp_gamesplayer(char *str,struct games_player *P) { str[0] = 0; - if ( P->gold <= 0 || P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) + if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) return(-1); sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); return(0); From c2f90d6173669d602c7644c481508699b8695d5c Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:41:47 -1100 Subject: [PATCH 298/787] Test --- src/cc/gamescc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 62d49c3dd..867c0a79b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -151,6 +151,7 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 free(ptr); } n = rs->playersize; + fprintf(stderr,"gold.%d\n",rs->P.gold); sleep(3); free(rs); return(n); } @@ -1374,7 +1375,7 @@ gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std: fclose(fp); } } - //fprintf(stderr,"call replay2\n"); + fprintf(stderr,"call replay2\n"); num = games_replay2(newplayer,seed,keystrokes,numkeys,playerdata.size()==0?0:&P,0); newdata.resize(num); for (i=0; i Date: Tue, 26 Mar 2019 09:44:32 -1100 Subject: [PATCH 299/787] Test --- src/cc/gamescc.cpp | 2 +- src/cc/tetris.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 867c0a79b..698890c57 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1375,7 +1375,6 @@ gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std: fclose(fp); } } - fprintf(stderr,"call replay2\n"); num = games_replay2(newplayer,seed,keystrokes,numkeys,playerdata.size()==0?0:&P,0); newdata.resize(num); for (i=0; i no playerdata\n"); diff --git a/src/cc/tetris.cpp b/src/cc/tetris.cpp index 39e8f1a65..8a52dcb37 100644 --- a/src/cc/tetris.cpp +++ b/src/cc/tetris.cpp @@ -41,8 +41,8 @@ void tetrisplayerjson(UniValue &obj,struct games_player *P) int32_t disp_gamesplayer(char *str,struct games_player *P) { str[0] = 0; - if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) - return(-1); + //if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) + // return(-1); sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); return(0); } From 67ba7536bdbed814b00041266fadaea2f3b7fb32 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:47:04 -1100 Subject: [PATCH 300/787] Test --- src/cc/gamescc.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 698890c57..2c5668253 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -146,12 +146,13 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 // extract data from ptr if ( GAMEDATA(&rs->P,ptr) < 0 ) memset(&rs->P,0,sizeof(rs->P)); - else if ( newdata != 0 && rs->playersize > 0 ) + else rs->playersize = sizeof(rs->P); + if ( newdata != 0 && rs->playersize > 0 ) memcpy(newdata,rs->playerdata,rs->playersize); free(ptr); } n = rs->playersize; - fprintf(stderr,"gold.%d\n",rs->P.gold); sleep(3); + //fprintf(stderr,"gold.%d\n",rs->P.gold); sleep(3); free(rs); return(n); } From a43f0b65d4535cfd24f1db97547b7d954789d45b Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:50:16 -1100 Subject: [PATCH 301/787] Test --- src/cc/dapps/dappstd.c | 2 +- src/cc/tetris.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 925b8749a..917045ed5 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -862,7 +862,7 @@ int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag) return(0); } -void games_bailout(struct games_state *rs) +void gamesbailout(struct games_state *rs) { flushkeystrokes(rs,1); } diff --git a/src/cc/tetris.c b/src/cc/tetris.c index c507ec019..cb89c0da0 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -651,7 +651,7 @@ gamesevent games_readevent(struct games_state *rs); void *gamesiterate(struct games_state *rs) { uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; - gamesevent c; uint16_t skipcount=0; uint32_t eventid = 0; tetris_game *tg; + gamesevent c; uint16_t skipcount=0; int32_t prevlevel; uint32_t eventid = 0; tetris_game *tg; WINDOW *board, *next, *hold, *score; if ( rs->guiflag != 0 || rs->sleeptime != 0 ) { @@ -665,6 +665,7 @@ void *gamesiterate(struct games_state *rs) init_colors(); // setup tetris colors } tg = tg_create(rs,22, 10); + prevlevel = tg->level; // Create windows for each section of the interface. board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); @@ -693,6 +694,11 @@ void *gamesiterate(struct games_state *rs) issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); if ( c <= 0x7f ) issue_games_events(rs,Gametxidstr,eventid,c); + if ( tg->level != prevlevel ) + { + flushkeystrokes(rs,0); + prevlevel = tg->level; + } skipcount = 0; } else skipcount++; #endif @@ -870,7 +876,7 @@ int tetris(int argc, char **argv) // Game loop tg = (tetris_game *)gamesiterate(rs); - games_bailout(rs); + gamesbailout(rs); // Deinitialize NCurses wclear(stdscr); endwin(); From 8660a2eedc523474c9d47fe342c4389892b593b8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:51:32 -1100 Subject: [PATCH 302/787] -print --- src/cc/gamescc.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 2c5668253..3e12a7010 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -146,9 +146,12 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 // extract data from ptr if ( GAMEDATA(&rs->P,ptr) < 0 ) memset(&rs->P,0,sizeof(rs->P)); - else rs->playersize = sizeof(rs->P); - if ( newdata != 0 && rs->playersize > 0 ) - memcpy(newdata,rs->playerdata,rs->playersize); + else + { + rs->playersize = sizeof(rs->P); + if ( newdata != 0 ) + memcpy(newdata,&rs->P,rs->playersize); + } free(ptr); } n = rs->playersize; @@ -1383,7 +1386,7 @@ gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std: newdata[i] = newplayer[i]; ((uint8_t *)&endP)[i] = newplayer[i]; } - fprintf(stderr,"newgold.%d\n",endP.gold); sleep(3); + //fprintf(stderr,"newgold.%d\n",endP.gold); sleep(3); if ( disp_gamesplayer(str,&endP) < 0 ) { sprintf(str,"zero value character -> no playerdata\n"); From 8a7a1da70f3773317c9e370d90e8957c0fc1e04b Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 09:52:15 -1100 Subject: [PATCH 303/787] int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag) --- src/cc/tetris.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/tetris.h b/src/cc/tetris.h index 9d16a8e8b..c4cfb3b31 100644 --- a/src/cc/tetris.h +++ b/src/cc/tetris.h @@ -196,6 +196,7 @@ struct games_state }; extern struct games_state globalR; void *gamesiterate(struct games_state *rs); +int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag); void games_packitemstr(char *packitemstr,struct games_packitem *item); uint64_t _games_rngnext(uint64_t initseed); From 2e8573c0ec888a33a90abfd2a2cc72d311fde2d8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 19:59:13 -1100 Subject: [PATCH 304/787] Fix rogue compile --- src/cc/gamescc.cpp | 13 +++---------- src/cc/rogue_rpc.cpp | 8 ++++---- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 3e12a7010..4e8660c18 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1510,15 +1510,8 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params,ch result.push_back(Pair("method",method)); result.push_back(Pair("mygamesaddr",mygamesaddr)); if ( strcmp(method,"bailout") == 0 ) - { funcid = 'Q'; - //mult = 10; //100000; - } - else - { - funcid = 'H'; - //mult = 20; //200000; - } + else funcid = 'H'; if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) { if ( n > 0 ) @@ -1570,13 +1563,13 @@ UniValue games_finish(uint64_t txfee,struct CCcontract_info *cp,cJSON *params,ch fprintf(stderr,"\ncashout %.8f extracted %s\n",(double)cashout/COIN,str); if ( funcid == 'H' && maxplayers > 1 ) { - if ( P.amulet == 0 ) + /*if ( P.amulet == 0 ) { if ( numplayers != maxplayers ) return(cclib_error(result,"numplayers != maxplayers")); else if ( games_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 ) return(cclib_error(result,"highlander must be a winner or last one standing")); - } + }*/ cashout += games_buyins(gametxid,maxplayers);//numplayers * buyin; } if ( cashout > 0 ) diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 4b74e21b7..52323adbe 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -283,7 +283,7 @@ int32_t rogue_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx, return(0); } -int64_t rogue_buyins(uint256 gametxid) +int64_t rogue_buyins(uint256 gametxid,int32_t maxplayers) { int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; int64_t buyins = 0; for (i=0; iamulet != 0 ) mult *= 5; dungeonlevel = P->dungeonlevel; @@ -1618,7 +1618,7 @@ bool rogue_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C if ( funcid == 'H' ) { cashout *= 2; - cashout += rogue_buyins(gametxid); + cashout += rogue_buyins(gametxid,maxplayers); } sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d txid.%s %.8f vs vout2 %.8f",tokentx,decoded,height,txid.GetHex().c_str(),(double)cashout/COIN,(double)tx.vout[2].nValue/COIN); if ( strcmp(laststr,cashstr) != 0 ) From 58215af9b0d138c1a0b87351958ec22541ae8ee4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 21:48:44 -1100 Subject: [PATCH 305/787] Make 4 cc libs --- src/cc/makecclib | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cc/makecclib b/src/cc/makecclib index adac757c3..3aded32e3 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -1,2 +1,8 @@ #!/bin/sh gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o ../libcc.so cclib.cpp + +gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp + +gcc -O3 -DBUILD_ROGUE -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o roguecc.so cclib.cpp + +gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp From 906cc5ccb4cd5e4aa9a059af87a78515ae5c683c Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 21:49:59 -1100 Subject: [PATCH 306/787] -print --- src/cc/gamescc.cpp | 2 +- src/cc/tetris.c | 45 ++++++++++++++++++++++++--------------------- 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 4e8660c18..38396562e 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -546,7 +546,7 @@ void komodo_netevent(std::vector message) { if ( (rand() % 10) == 0 ) { - fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size()); + //fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size()); komodo_sendmessage(2,2,"events",message); } } diff --git a/src/cc/tetris.c b/src/cc/tetris.c index cb89c0da0..651b1018c 100644 --- a/src/cc/tetris.c +++ b/src/cc/tetris.c @@ -801,32 +801,35 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve if ( fp == 0 ) fp = fopen("events.log","wb"); rs->buffered[rs->num++] = c; - if ( sizeof(c) == 1 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); - else if ( sizeof(c) == 2 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); - else if ( sizeof(c) == 4 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); - else if ( sizeof(c) == 8 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); - if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + if ( 0 ) { - if ( (retjson= cJSON_Parse(retstr)) != 0 ) + if ( sizeof(c) == 1 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + else if ( sizeof(c) == 2 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + else if ( sizeof(c) == 4 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); + else if ( sizeof(c) == 8 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { - if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + if ( (retjson= cJSON_Parse(retstr)) != 0 ) { - retval = 0; - if ( fp != 0 ) + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) { - fprintf(fp,"%s\n",jprint(resobj,0)); - fflush(fp); + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } } - } - free_json(retjson); - } else fprintf(fp,"error parsing %s\n",retstr); - free(retstr); - } else fprintf(fp,"error issuing method %s\n",params); - return(retval); + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); + } else return(0); } int tetris(int argc, char **argv) From 3da4f521dc060c6a0275b4c9ff49f9aeeb74f5e8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 21:50:51 -1100 Subject: [PATCH 307/787] Add apps to makecclib --- src/cc/makecclib | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cc/makecclib b/src/cc/makecclib index 3aded32e3..d9a5f2a82 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -6,3 +6,6 @@ gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include gcc -O3 -DBUILD_ROGUE -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o roguecc.so cclib.cpp gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp + +./maketetris +make -f Makefile_rogue From 1e6630620c9db3ae2cd34cbf5fc6a721cba9b499 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 21:52:41 -1100 Subject: [PATCH 308/787] Fix --- src/cc/makecclib | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/makecclib b/src/cc/makecclib index d9a5f2a82..8db8514cd 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -8,4 +8,5 @@ gcc -O3 -DBUILD_ROGUE -std=c++11 -I../secp256k1/include -I../univalue/include -I gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp ./maketetris -make -f Makefile_rogue + +cd rogue; make; cd .. From 8a208a02c794571f8a65829c3c718a2626345b8b Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 21:54:08 -1100 Subject: [PATCH 309/787] Make rogue --- src/cc/makecclib | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cc/makecclib b/src/cc/makecclib index 8db8514cd..e64934d31 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -8,5 +8,4 @@ gcc -O3 -DBUILD_ROGUE -std=c++11 -I../secp256k1/include -I../univalue/include -I gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp ./maketetris - -cd rogue; make; cd .. +./makerogue From 119decca6f464a9b4ecc22b8188593e4242338a9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 21:56:35 -1100 Subject: [PATCH 310/787] Fix --- src/cc/makecclib | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc/makecclib b/src/cc/makecclib index e64934d31..6a1908829 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -1,11 +1,12 @@ #!/bin/sh +rm *.so rogue/rogue tetris + gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o ../libcc.so cclib.cpp gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp - -gcc -O3 -DBUILD_ROGUE -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o roguecc.so cclib.cpp +./maketetris gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp -./maketetris +make -f Makefile_rogue ./makerogue From 329b7437640fcb17908a861525c2bb075cca5693 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 22:02:53 -1100 Subject: [PATCH 311/787] +prints --- src/cc/makecclib | 9 +++++++-- src/cc/rogue_rpc.cpp | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/cc/makecclib b/src/cc/makecclib index 6a1908829..35b4a9d8d 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -1,12 +1,17 @@ #!/bin/sh rm *.so rogue/rogue tetris +echo rogue +make -f Makefile_rogue +./makerogue + +echo sudoku/musig/dilithium gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o ../libcc.so cclib.cpp +echo games tetris gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp ./maketetris +echo customcc stub gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp -make -f Makefile_rogue -./makerogue diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 52323adbe..5040c34d3 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -1341,12 +1341,12 @@ UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *param UniValue rogue_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - return(rogue_finishgame(txfee,cp,params,"bailout")); + return(rogue_finishgame(txfee,cp,params,(char *)"bailout")); } UniValue rogue_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - return(rogue_finishgame(txfee,cp,params,"highlander")); + return(rogue_finishgame(txfee,cp,params,(char *)"highlander")); } UniValue rogue_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) From 3640e3c4fcb67981ca8c440fb72a9dc2ecc5608a Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 22:28:18 -1100 Subject: [PATCH 312/787] Prices example --- src/cc/gamescc.cpp | 13 +- src/cc/makecclib | 4 + src/cc/prices.c | 896 +++++++++++++++++++++++++++++++++++++++++++++ src/cc/prices.cpp | 463 +++-------------------- src/cc/prices.h | 208 +++++++++++ 5 files changed, 1159 insertions(+), 425 deletions(-) create mode 100644 src/cc/prices.c create mode 100644 src/cc/prices.h diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 38396562e..f010ac282 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -14,7 +14,11 @@ ******************************************************************************/ #include "gamescc.h" -#include "tetris.c" // replace with game code +#ifdef BUILD_PRICES +#include "prices.c" +#elif +#include "tetris.c" +#endif int32_t GAMEDATA(struct games_player *P,void *ptr); @@ -161,8 +165,11 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 } #ifndef STANDALONE - -#include "tetris.cpp" // replace with game specific functions +#ifdef BUILD_PRICES +#include "prices.cpp" +#elif +#include "tetris.cpp" +#endif void GAMEJSON(UniValue &obj,struct games_player *P); diff --git a/src/cc/makecclib b/src/cc/makecclib index 35b4a9d8d..38634aa78 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -12,6 +12,10 @@ echo games tetris gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp ./maketetris +echo games prices +gcc -O3 -DBUILD_GAMESCC -DBUILD_PRICES -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o pricescc.so cclib.cpp +./makeprices + echo customcc stub gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp diff --git a/src/cc/prices.c b/src/cc/prices.c new file mode 100644 index 000000000..651b1018c --- /dev/null +++ b/src/cc/prices.c @@ -0,0 +1,896 @@ + +#include "tetris.h" + +/* + In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. + + also, the standalone game needs to support argv of seed gametxid, along with replay args + */ + +int random_tetromino(struct games_state *rs) +{ + rs->seed = _games_rngnext(rs->seed); + return(rs->seed % NUM_TETROMINOS); +} + +int32_t tetrisdata(struct games_player *P,void *ptr) +{ + tetris_game *tg = (tetris_game *)ptr; + P->gold = tg->points; + P->dungeonlevel = tg->level; + //fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level); + return(0); +} + +/***************************************************************************/ +/** https://github.com/brenns10/tetris + @file main.c + @author Stephen Brennan + @date Created Wednesday, 10 June 2015 + @brief Main program for tetris. + @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised + BSD License. See LICENSE.txt for details. + *******************************************************************************/ + + +#include // for FILE +#include // for bool +#include +#include +#include +#include +#include +#include + +#ifdef BUILD_GAMESCC +#include "rogue/cursesd.h" +#else +#include +#endif + + +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +/******************************************************************************* + Array Definitions + *******************************************************************************/ + +const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] = +{ + // I + {{{1, 0}, {1, 1}, {1, 2}, {1, 3}}, + {{0, 2}, {1, 2}, {2, 2}, {3, 2}}, + {{3, 0}, {3, 1}, {3, 2}, {3, 3}}, + {{0, 1}, {1, 1}, {2, 1}, {3, 1}}}, + // J + {{{0, 0}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 2}}, + {{0, 1}, {1, 1}, {2, 0}, {2, 1}}}, + // L + {{{0, 2}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {2, 1}, {2, 2}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 0}}, + {{0, 0}, {0, 1}, {1, 1}, {2, 1}}}, + // O + {{{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}}, + // S + {{{0, 1}, {0, 2}, {1, 0}, {1, 1}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 2}}, + {{1, 1}, {1, 2}, {2, 0}, {2, 1}}, + {{0, 0}, {1, 0}, {1, 1}, {2, 1}}}, + // T + {{{0, 1}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 1}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 1}}}, + // Z + {{{0, 0}, {0, 1}, {1, 1}, {1, 2}}, + {{0, 2}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {2, 1}, {2, 2}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 0}}}, +}; + +const int GRAVITY_LEVEL[MAX_LEVEL+1] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + //10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 30, 28, 26, 24, 22, 20, 16, 12, 8, 4 +}; + +/******************************************************************************* + Helper Functions for Blocks + *******************************************************************************/ + +void sleep_milli(int milliseconds) +{ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = milliseconds * 1000 * 1000; + nanosleep(&ts, NULL); +} + +/* + Return the block at the given row and column. + */ +char tg_get(tetris_game *obj, int row, int column) +{ + return obj->board[obj->cols * row + column]; +} + +/* + Set the block at the given row and column. + */ +static void tg_set(tetris_game *obj, int row, int column, char value) +{ + obj->board[obj->cols * row + column] = value; +} + +/* + Check whether a row and column are in bounds. + */ +bool tg_check(tetris_game *obj, int row, int col) +{ + return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols; +} + +/* + Place a block onto the board. + */ +static void tg_put(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, + TYPE_TO_CELL(block.typ)); + } +} + +/* + Clear a block out of the board. + */ +static void tg_remove(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY); + } +} + +/* + Check if a block can be placed on the board. + */ +static bool tg_fits(tetris_game *obj, tetris_block block) +{ + int i, r, c; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + r = block.loc.row + cell.row; + c = block.loc.col + cell.col; + if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) { + return false; + } + } + return true; +} + +/* + Create a new falling block and populate the next falling block with a random + one. + */ +static void tg_new_falling(struct games_state *rs,tetris_game *obj) +{ + // Put in a new falling tetromino. + obj->falling = obj->next; + obj->next.typ = random_tetromino(rs); + obj->next.ori = 0; + obj->next.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; +} + +/******************************************************************************* + Game Turn Helpers + *******************************************************************************/ + +/* + Tick gravity, and move the block down if gravity should act. + */ +static void tg_do_gravity_tick(struct games_state *rs,tetris_game *obj) +{ + obj->ticks_till_gravity--; + if (obj->ticks_till_gravity <= 0) { + tg_remove(obj, obj->falling); + obj->falling.loc.row++; + if (tg_fits(obj, obj->falling)) { + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + } else { + obj->falling.loc.row--; + tg_put(obj, obj->falling); + + tg_new_falling(rs,obj); + } + tg_put(obj, obj->falling); + } +} + +/* + Move the falling tetris block left (-1) or right (+1). + */ +static void tg_move(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + obj->falling.loc.col += direction; + if (!tg_fits(obj, obj->falling)) { + obj->falling.loc.col -= direction; + } + tg_put(obj, obj->falling); +} + +/* + Send the falling tetris block to the bottom. + */ +static void tg_down(struct games_state *rs,tetris_game *obj) +{ + tg_remove(obj, obj->falling); + while (tg_fits(obj, obj->falling)) { + obj->falling.loc.row++; + } + obj->falling.loc.row--; + tg_put(obj, obj->falling); + tg_new_falling(rs,obj); +} + +/* + Rotate the falling block in either direction (+/-1). + */ +static void tg_rotate(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + + while (true) { + obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS; + + // If the new orientation fits, we're done. + if (tg_fits(obj, obj->falling)) + break; + + // Otherwise, try moving left to make it fit. + obj->falling.loc.col--; + if (tg_fits(obj, obj->falling)) + break; + + // Finally, try moving right to make it fit. + obj->falling.loc.col += 2; + if (tg_fits(obj, obj->falling)) + break; + + // Put it back in its original location and try the next orientation. + obj->falling.loc.col--; + // Worst case, we come back to the original orientation and it fits, so this + // loop will terminate. + } + + tg_put(obj, obj->falling); +} + +/* + Swap the falling block with the block in the hold buffer. + */ +static void tg_hold(struct games_state *rs,tetris_game *obj) +{ + tg_remove(obj, obj->falling); + if (obj->stored.typ == -1) { + obj->stored = obj->falling; + tg_new_falling(rs,obj); + } else { + int typ = obj->falling.typ, ori = obj->falling.ori; + obj->falling.typ = obj->stored.typ; + obj->falling.ori = obj->stored.ori; + obj->stored.typ = typ; + obj->stored.ori = ori; + while (!tg_fits(obj, obj->falling)) { + obj->falling.loc.row--; + } + } + tg_put(obj, obj->falling); +} + +/* + Perform the action specified by the move. + */ +static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move move) +{ + switch (move) { + case TM_LEFT: + //fprintf(stderr,"LEFT "); + tg_move(obj, -1); + break; + case TM_RIGHT: + //fprintf(stderr,"RIGHT "); + tg_move(obj, 1); + break; + case TM_DROP: + tg_down(rs,obj); + break; + case TM_CLOCK: + tg_rotate(obj, 1); + break; + case TM_COUNTER: + tg_rotate(obj, -1); + break; + case TM_HOLD: + tg_hold(rs,obj); + break; + default: + // pass + break; + } +} + +/* + Return true if line i is full. + */ +static bool tg_line_full(tetris_game *obj, int i) +{ + int j; + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) + return false; + } + return true; +} + +/* + Shift every row above r down one. + */ +static void tg_shift_lines(tetris_game *obj, int r) +{ + int i, j; + for (i = r-1; i >= 0; i--) { + for (j = 0; j < obj->cols; j++) { + tg_set(obj, i+1, j, tg_get(obj, i, j)); + tg_set(obj, i, j, TC_EMPTY); + } + } +} + +/* + Find rows that are filled, remove them, shift, and return the number of + cleared rows. + */ +static int tg_check_lines(tetris_game *obj) +{ + int i, nlines = 0; + tg_remove(obj, obj->falling); // don't want to mess up falling block + + for (i = obj->rows-1; i >= 0; i--) { + if (tg_line_full(obj, i)) { + tg_shift_lines(obj, i); + i++; // do this line over again since they're shifted + nlines++; + } + } + + tg_put(obj, obj->falling); // replace + return nlines; +} + +/* + Adjust the score for the game, given how many lines were just cleared. + */ +static void tg_adjust_score(tetris_game *obj, int lines_cleared) +{ + static int line_multiplier[] = {0, 40, 100, 300, 1200}; + obj->points += line_multiplier[lines_cleared] * (obj->level + 1); + if (lines_cleared >= obj->lines_remaining) { + obj->level = MIN(MAX_LEVEL, obj->level + 1); + lines_cleared -= obj->lines_remaining; + obj->lines_remaining = LINES_PER_LEVEL - lines_cleared; + } else { + obj->lines_remaining -= lines_cleared; + } +} + +/* + Return true if the game is over. + */ +static bool tg_game_over(tetris_game *obj) +{ + int i, j; + bool over = false; + tg_remove(obj, obj->falling); + for (i = 0; i < 2; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + over = true; + } + } + } + tg_put(obj, obj->falling); + return over; +} + +/******************************************************************************* + Main Public Functions + *******************************************************************************/ + +/* + Do a single game tick: process gravity, user input, and score. Return true if + the game is still running, false if it is over. + */ +bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move) +{ + int lines_cleared; + // Handle gravity. + tg_do_gravity_tick(rs,obj); + + // Handle input. + tg_handle_move(rs,obj, move); + + // Check for cleared lines + lines_cleared = tg_check_lines(obj); + + tg_adjust_score(obj, lines_cleared); + + // Return whether the game will continue (NOT whether it's over) + return !tg_game_over(obj); +} + +void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols) +{ + // Initialization logic + obj->rows = rows; + obj->cols = cols; + //obj->board = (char *)malloc(rows * cols); + memset(obj->board, TC_EMPTY, rows * cols); + obj->points = 0; + obj->level = 0; + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + obj->lines_remaining = LINES_PER_LEVEL; + //srand(time(NULL)); + tg_new_falling(rs,obj); + tg_new_falling(rs,obj); + obj->stored.typ = -1; + obj->stored.ori = 0; + obj->stored.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; + //printf("%d", obj->falling.loc.col); +} + +tetris_game *tg_create(struct games_state *rs,int rows, int cols) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game) + rows*cols); + tg_init(rs,obj, rows, cols); + return obj; +} + +/*void tg_destroy(tetris_game *obj) +{ + // Cleanup logic + free(obj->board); +}*/ + +void tg_delete(tetris_game *obj) { + //tg_destroy(obj); + free(obj); +} + +/* + Load a game from a file. + +tetris_game *tg_load(FILE *f) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); + if (fread(obj, sizeof(tetris_game), 1, f) != 1 ) + { + fprintf(stderr,"read game error\n"); + free(obj); + obj = 0; + } + else + { + obj->board = (char *)malloc(obj->rows * obj->cols); + if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + { + fprintf(stderr,"fread error\n"); + free(obj->board); + free(obj); + obj = 0; + } + } + return obj; +}*/ + +/* + Save a game to a file. + +void tg_save(tetris_game *obj, FILE *f) +{ + if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) + fprintf(stderr,"error writing tetrisgame\n"); + else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + fprintf(stderr,"error writing board\n"); +}*/ + +/* + Print a game board to a file. Really just for early debugging. + */ +void tg_print(tetris_game *obj, FILE *f) { + int i, j; + for (i = 0; i < obj->rows; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) { + fputs(TC_EMPTY_STR, f); + } else { + fputs(TC_BLOCK_STR, f); + } + } + fputc('\n', f); + } +} + +/* + 2 columns per cell makes the game much nicer. + */ +#define COLS_PER_CELL 2 +/* + Macro to print a cell of a specific type to a window. + */ +#define ADD_BLOCK(w,x) waddch((w),' '|A_REVERSE|COLOR_PAIR(x)); \ +waddch((w),' '|A_REVERSE|COLOR_PAIR(x)) +#define ADD_EMPTY(w) waddch((w), ' '); waddch((w), ' ') + +/* + Print the tetris board onto the ncurses window. + */ +void display_board(WINDOW *w, tetris_game *obj) +{ + int i, j; + box(w, 0, 0); + for (i = 0; i < obj->rows; i++) { + wmove(w, 1 + i, 1); + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + ADD_BLOCK(w,tg_get(obj, i, j)); + } else { + ADD_EMPTY(w); + } + } + } + wnoutrefresh(w); +} + +/* + Display a tetris piece in a dedicated window. + */ +void display_piece(WINDOW *w, tetris_block block) +{ + int b; + tetris_location c; + wclear(w); + box(w, 0, 0); + if (block.typ == -1) { + wnoutrefresh(w); + return; + } + for (b = 0; b < TETRIS; b++) { + c = TETROMINOS[block.typ][block.ori][b]; + wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1); + ADD_BLOCK(w, TYPE_TO_CELL(block.typ)); + } + wnoutrefresh(w); +} + +/* + Display score information in a dedicated window. + */ +void display_score(WINDOW *w, tetris_game *tg) +{ + wclear(w); + box(w, 0, 0); + wprintw(w, (char *)"Score\n%d\n", tg->points); + wprintw(w, (char *)"Level\n%d\n", tg->level); + wprintw(w, (char *)"Lines\n%d\n", tg->lines_remaining); + wnoutrefresh(w); +} + +/* + Save and exit the game. + +void save(tetris_game *game, WINDOW *w) +{ + FILE *f; + + wclear(w); + box(w, 0, 0); // return the border + wmove(w, 1, 1); + wprintw(w, (char *)"Save and exit? [Y/n] "); + wrefresh(w); + timeout(-1); + if (getch() == 'n') { + timeout(0); + return; + } + f = fopen("tetris.save", "w"); + tg_save(game, f); + fclose(f); + tg_delete(game); + endwin(); + fprintf(stderr,"Game saved to \"tetris.save\".\n"); + fprintf(stderr,"Resume by passing the filename as an argument to this program.\n"); + exit(EXIT_SUCCESS); +}*/ + +/* + Do the NCURSES initialization steps for color blocks. + */ +void init_colors(void) +{ + start_color(); + //init_color(COLOR_ORANGE, 1000, 647, 0); + init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK); + init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK); + init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK); + init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK); + init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK); + init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK); + init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); +} + +struct games_state globalR; +extern char Gametxidstr[]; +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); +gamesevent games_readevent(struct games_state *rs); + +void *gamesiterate(struct games_state *rs) +{ + uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; + gamesevent c; uint16_t skipcount=0; int32_t prevlevel; uint32_t eventid = 0; tetris_game *tg; + WINDOW *board, *next, *hold, *score; + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) + { + // NCURSES initialization: + initscr(); // initialize curses + cbreak(); // pass key presses to program, but not signals + noecho(); // don't echo key presses to screen + keypad(stdscr, TRUE); // allow arrow keys + timeout(0); // no blocking on getch() + curs_set(0); // set the cursor to invisible + init_colors(); // setup tetris colors + } + tg = tg_create(rs,22, 10); + prevlevel = tg->level; + // Create windows for each section of the interface. + board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); + next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); + hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); + score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); + while ( running != 0 ) + { + running = tg_tick(rs,tg,move); + if ( 1 && (rs->guiflag != 0 || rs->sleeptime != 0) ) + { + display_board(board,tg); + display_piece(next,tg->next); + display_piece(hold,tg->stored); + display_score(score,tg); + } + if ( rs->guiflag != 0 ) + { +#ifdef STANDALONE + sleep_milli(15); + if ( (counter++ % 10) == 0 ) + doupdate(); + c = games_readevent(rs); + if ( c <= 0x7f || skipcount == 0x3fff ) + { + if ( skipcount > 0 ) + issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); + if ( c <= 0x7f ) + issue_games_events(rs,Gametxidstr,eventid,c); + if ( tg->level != prevlevel ) + { + flushkeystrokes(rs,0); + prevlevel = tg->level; + } + skipcount = 0; + } else skipcount++; +#endif + } + else + { + if ( rs->replaydone != 0 ) + break; + if ( rs->sleeptime != 0 ) + { + sleep_milli(1); + if ( (counter++ % 20) == 0 ) + doupdate(); + } + if ( skipcount == 0 ) + { + c = games_readevent(rs); + //fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); + if ( (c & 0x4000) == 0x4000 ) + { + skipcount = (c & 0x3fff); + c = 'S'; + } + } + if ( skipcount > 0 ) + skipcount--; + } + eventid++; + switch ( c ) + { + case 'h': + move = TM_LEFT; + break; + case 'l': + move = TM_RIGHT; + break; + case 'k': + move = TM_CLOCK; + break; + case 'j': + move = TM_DROP; + break; + case 'q': + running = false; + move = TM_NONE; + break; + /*case 'p': + wclear(board); + box(board, 0, 0); + wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); + wprintw(board, "PAUSED"); + wrefresh(board); + timeout(-1); + getch(); + timeout(0); + move = TM_NONE; + break; + case 's': + save(tg, board); + move = TM_NONE; + break;*/ + case ' ': + move = TM_HOLD; + break; + default: + move = TM_NONE; + } + } + return(tg); +} + +#ifdef STANDALONE +/* + Main tetris game! + */ +#include "dapps/dappstd.c" + + +char *clonestr(char *str) +{ + char *clone; int32_t len; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + len = strlen(str); + clone = (char *)calloc(1,len+16); + strcpy(clone,str); + return(clone); +} + +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) +{ + static FILE *fp; + char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + rs->buffered[rs->num++] = c; + if ( 0 ) + { + if ( sizeof(c) == 1 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + else if ( sizeof(c) == 2 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + else if ( sizeof(c) == 4 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); + else if ( sizeof(c) == 8 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); + } else return(0); +} + +int tetris(int argc, char **argv) +{ + struct games_state *rs = &globalR; + int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg = 0; + memset(rs,0,sizeof(*rs)); + rs->guiflag = 1; + rs->sleeptime = 1; // non-zero to allow refresh() + if ( argc >= 2 && strlen(argv[2]) == 64 ) + { +#ifdef _WIN32 +#ifdef _MSC_VER + rs->origseed = _strtoui64(argv[1], NULL, 10); +#else + rs->origseed = atol(argv[1]); // windows, but not MSVC +#endif // _MSC_VER +#else + rs->origseed = atol(argv[1]); // non-windows +#endif // _WIN32 + rs->seed = rs->origseed; + if ( argc >= 3 ) + { + strcpy(Gametxidstr,argv[2]); + fprintf(stderr,"setplayerdata %s\n",Gametxidstr); + if ( games_setplayerdata(rs,Gametxidstr) < 0 ) + { + fprintf(stderr,"invalid gametxid, or already started\n"); + return(-1); + } + } + } else rs->seed = 777; + + /* Load file if given a filename. + if (argc >= 2) { + FILE *f = fopen(argv[1], "r"); + if (f == NULL) { + perror("tetris"); + exit(EXIT_FAILURE); + } + tg = tg_load(f); + fclose(f); + } else { + // Otherwise create new game. + tg = tg_create(rs,22, 10); + }*/ + + // Game loop + tg = (tetris_game *)gamesiterate(rs); + gamesbailout(rs); + // Deinitialize NCurses + wclear(stdscr); + endwin(); + // Output ending message. + printf("Game over!\n"); + printf("You finished with %d points on level %d.\n", tg->points, tg->level); + + // Deinitialize Tetris + tg_delete(tg); + return 0; +} + +#endif + diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index c53207562..8a52dcb37 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -1,3 +1,4 @@ + /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * @@ -13,442 +14,60 @@ * * ******************************************************************************/ -#include "CCassets.h" -#include "CCPrices.h" - -/* - Prices CC would best build on top of the oracles CC, ie. to combine payments for multiple oracles and to calculate a 51% protected price feed. - - We need to assume there is an oracle for a specific price. In the event there are more than one provider, the majority need to be within correlation distance to update a pricepoint. - - int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); - - Using the above function, a consensus price can be obtained for a datasource. - - given an oracletxid, the marketaddr and format can be extracted to be used for future calls to OraclePrice. This allows to set a starting price and that in turn allows cash settled leveraged trading! - - Funds work like with dice, ie. there is a Prices plan that traders bet against. - - PricesFunding oracletxid, margin, priceaveraging, maxleverage, funding, longtoken, shorttoken, N [pubkeys] - - PricesBet -> oracletxid start with 'L', leverage, funding, direction - funds are locked into global CC address - it can be closed at anytime by the trader for cash settlement - the house account can close it if rekt - - Implementation Notes: - In order to eliminate the need for worrying about sybil attacks, each prices plan would be able to specific pubkey(s?) for whitelisted publishers. It would be possible to have a non-whitelisted plan that would use 50% correlation between publishers. - - delta neutral balancing of riskexposure: fabs(long exposure - short exposure) - bet +B at leverage L - absval(sum(+BLi) - sum(-Bli)) - - validate: update riskexposure and it needs to be <= funds - - PricesProfits: limit withdraw to funds in excess of riskexposure - PricesFinish: payout (if winning) and update riskexposure - need long/short exposure assets - - funding -> 1of2 CC global CC address and dealer address, exposure tokens to global 1of2 assets CC address - pricebet -> user funds and exposure token to 1of2 address. - pricewin -> winnings from dealer funds, exposure token back to global address - priceloss -> exposuretoken back to global address - - exposure address, funds address - - - -*/ - -// start of consensus code - -int64_t PricesOraclePrice(int64_t &rektprice,uint64_t mode,uint256 oracletxid,std::vectorpubkeys,int32_t dir,int64_t amount,int32_t leverage) +// game specific code for daemon +void games_packitemstr(char *packitemstr,struct games_packitem *item) { - int64_t price; - // howto ensure price when block it confirms it not known - // get price from oracle + current chaintip - // normalize leveraged amount - if ( dir > 0 ) - rektprice = price * leverage / (leverage-1); - else rektprice = price * (leverage-1) / leverage; - return(price); + strcpy(packitemstr,""); } -CScript EncodePricesFundingOpRet(uint8_t funcid,CPubKey planpk,uint256 oracletxid,uint256 longtoken,uint256 shorttoken,int32_t millimargin,uint64_t mode,int32_t maxleverage,std::vector pubkeys,uint256 bettoken) +int64_t games_cashout(struct games_player *P) { - CScript opret; - fprintf(stderr,"implement EncodePricesFundingOpRet\n"); - return(opret); + int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0; + cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel; + return(cashout); } -uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &oracletxid,uint256 &longtoken,uint256 &shorttoken,int32_t &millimargin,uint64_t &mode,int32_t &maxleverage,std::vector &pubkeys,uint256 &bettoken) +void tetrisplayerjson(UniValue &obj,struct games_player *P) { - fprintf(stderr,"implement DecodePricesFundingOpRet\n"); + obj.push_back(Pair("packsize",(int64_t)P->packsize)); + obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); + obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff))); + obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16))); + obj.push_back(Pair("level",(int64_t)P->level)); + obj.push_back(Pair("experience",(int64_t)P->experience)); + obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel)); +} + +int32_t disp_gamesplayer(char *str,struct games_player *P) +{ + str[0] = 0; + //if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) + // return(-1); + sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); return(0); } -bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - - return true; // TODO remove, for test dual-evals - - return eval->Invalid("no validation yet"); - std::vector > txids; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else + uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) { - for (i=0; iInvalid("illegal normal vini"); - } - } - //fprintf(stderr,"check amounts\n"); - //if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) - { - fprintf(stderr,"Pricesget invalid amount\n"); - return false; - } - //else - { - txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"Pricesget validated\n"); - else fprintf(stderr,"Pricesget invalid\n"); - return(retval); - } - } -} -// end of consensus code - -// helper functions for rpc calls in rpcwallet.cpp - -int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,uint256 tolenid,int64_t total,int32_t maxinputs) -{ - // add threshold check - int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; - std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,destaddr); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - // need to prevent dup - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) - { - // need to verify assetid - if ( (nValue= vintx.vout[vout].nValue) >= 10000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) - { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - nValue = it->second.satoshis; - totalinputs += nValue; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; - } - } - } - return(totalinputs); + len -= 36; + for (i=0; i<32; i++) + ((uint8_t *)&gametxid)[i] = payload[len+i]; + eventid = (uint32_t)payload[len+32]; + eventid |= (uint32_t)payload[len+33] << 8; + eventid |= (uint32_t)payload[len+34] << 16; + eventid |= (uint32_t)payload[len+35] << 24; + //for (i=0; i > addressIndex; struct CCcontract_info *cp,C; uint64_t mode; int32_t margin,maxleverage; std::vectorpubkeys; uint256 txid,hashBlock,oracletxid,longtoken,shorttoken,bettoken; CPubKey planpk,pricespk; char str[65]; CTransaction vintx; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - SetCCtxids(addressIndex,cp->normaladdr); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - txid = it->first.txhash; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) - { - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) - { - result.push_back(uint256_str(str,txid)); - } - } - } - return(result); + return(true); } -// longtoken satoshis limits long exposure -// shorttoken satoshis limits short exposure -// both must be in the 1of2 CC address with its total supply -// bettoken -std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction oracletx; int64_t fullsupply,inputs,CCchange=0; uint256 hashBlock; char str[65],coinaddr[64],houseaddr[64]; CPubKey mypk,pricespk; int32_t i,N,numvouts; struct CCcontract_info *cp,C; - if ( funding < 100*COIN || maxleverage <= 0 || maxleverage > 10000 ) - { - CCerror = "invalid parameter error"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( (N= (int32_t)pubkeys.size()) || N > 15 ) - { - fprintf(stderr,"too many pubkeys N.%d\n",N); - return(""); - } - for (i=0; i 0 ) - { - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',mypk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken))); - } - else - { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - return(""); -} - -UniValue PricesInfo(uint256 fundingtxid) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CPubKey pricespk,planpk; uint256 hashBlock,oracletxid,longtoken,shorttoken,bettoken; CTransaction vintx; int64_t balance,supply,exposure; uint64_t funding,mode; int32_t i,margin,maxleverage; char numstr[65],houseaddr[64],exposureaddr[64],str[65]; std::vectorpubkeys; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - if ( GetTransaction(fundingtxid,vintx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - ERR_RESULT("cant find fundingtxid"); - return(result); - } - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("fundingtxid",uint256_str(str,fundingtxid))); - result.push_back(Pair("bettoken",uint256_str(str,bettoken))); - result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); - sprintf(numstr,"%.3f",(double)margin/1000); - result.push_back(Pair("profitmargin",numstr)); - result.push_back(Pair("maxleverage",maxleverage)); - result.push_back(Pair("mode",(int64_t)mode)); - for (i=0; ipubkeys; - if ( amount < 10000 ) - { - CCerror = "amount must be positive"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); - } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) - { - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,60)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(planpk)) << OP_CHECKSIG)); - if ( inputs > amount+txfee ) - CCchange = (inputs - amount); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); - // add addr2 - - std::vector voutTokenPubkeysEmpty; //TODO: add token vout pubkeys - return(FinalizeCCTx(0,cp,mtx,mypk,txfee, - EncodeTokenOpRet(bettoken, voutTokenPubkeysEmpty, - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('t',/*bettoken,*/zeroid, 0, Mypubkey()))))); - } - else - { - CCerror = "cant find enough bet inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - else - { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - return(""); -} - -std::string PricesBet(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,int64_t amount,int32_t leverage) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - struct CCcontract_info *cp,C; CPubKey pricespk,planpk,mypk; uint256 hashBlock,oracletxid,longtoken,shorttoken,tokenid,bettoken; CTransaction tx; int64_t balance,supply,exposure,inputs,inputs2,longexposure,netexposure,shortexposure,CCchange = 0,CCchange2 = 0; uint64_t funding,mode; int32_t dir,margin,maxleverage; char houseaddr[64],myaddr[64],exposureaddr[64]; std::vectorpubkeys; - if ( amount < 0 ) - { - amount = -amount; - dir = -1; - } else dir = 1; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); - } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) - { - if ( leverage > maxleverage || leverage < 1 ) - { - fprintf(stderr,"illegal leverage\n"); - return(""); - } - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - GetCCaddress1of2(cp,exposureaddr,pricespk,pricespk); - if ( dir < 0 ) - tokenid = shorttoken; - else tokenid = longtoken; - exposure = leverage * amount; - longexposure = CCtoken_balance(exposureaddr,longtoken); - shortexposure = CCtoken_balance(exposureaddr,shorttoken); - netexposure = (longexposure - shortexposure + exposure*dir); - if ( netexposure < 0 ) - netexposure = -netexposure; - balance = CCtoken_balance(myaddr,bettoken) / COIN; - if ( balance < netexposure*9/10 ) // 10% extra room for dynamically closed bets in wrong direction - { - fprintf(stderr,"balance %lld < 90%% netexposure %lld, refuse bet\n",(long long)balance,(long long)netexposure); - return(""); - } - if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,houseaddr,tokenid,exposure,30)) >= exposure ) - { - if ( (inputs2= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,30)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,exposure,pricespk,pricespk)); - if ( inputs > exposure+txfee ) - CCchange = (inputs - exposure); - if ( inputs2 > amount+txfee ) - CCchange2 = (inputs2 - amount); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,CCchange,pricespk,planpk)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange2,mypk)); - // add addr2 and addr3 - //return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesExtra('T',tokenid,bettoken,zeroid,dir*leverage))); - CScript opret; - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); - } - else - { - fprintf(stderr,"cant find enough bettoken inputs\n"); - return(""); - } - } - else - { - fprintf(stderr,"cant find enough exposure inputs\n"); - return(""); - } - } - else - { - CCerror = "cant find enough inputsB"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - return(""); -} - -UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) -{ - UniValue result(UniValue::VOBJ); - // get height of bettxid - // get price and rekt - // get current height and price - // what about if rekt in the past? - return(result); -} - -std::string PricesFinish(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) -{ - return(""); -} - - - diff --git a/src/cc/prices.h b/src/cc/prices.h new file mode 100644 index 000000000..ae1c46821 --- /dev/null +++ b/src/cc/prices.h @@ -0,0 +1,208 @@ + +#ifndef H_PRICES_H +#define H_PRICES_H + +/***************************************************************************/ +/** https://github.com/brenns10/tetris + @file main.c + @author Stephen Brennan + @date Created Wednesday, 10 June 2015 + @brief Main program for tetris. + @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised + BSD License. See LICENSE.txt for details. + *******************************************************************************/ + +/* + Convert a tetromino type to its corresponding cell. + */ +#define TYPE_TO_CELL(x) ((x)+1) + +/* + Strings for how you would print a tetris board. + */ +#define TC_EMPTY_STR " " +#define TC_BLOCK_STR "\u2588" + +/* + Questions about a tetris cell. + */ +#define TC_IS_EMPTY(x) ((x) == TC_EMPTY) +#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x)) + +/* + How many cells in a tetromino? + */ +#define TETRIS 4 +/* + How many tetrominos? + */ +#define NUM_TETROMINOS 7 +/* + How many orientations of a tetromino? + */ +#define NUM_ORIENTATIONS 4 + +/* + Level constants. + */ +#define MAX_LEVEL 19 +#define LINES_PER_LEVEL 10 + +/* + A "cell" is a 1x1 block within a tetris board. + */ +typedef enum { + TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ +} tetris_cell; + +/* + A "type" is a type/shape of a tetromino. Not including orientation. + */ +typedef enum { + TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z +} tetris_type; + +/* + A row,column pair. Negative numbers allowed, because we need them for + offsets. + */ +typedef struct { + int row; + int col; +} tetris_location; + +/* + A "block" is a struct that contains information about a tetromino. + Specifically, what type it is, what orientation it has, and where it is. + */ +typedef struct { + int typ; + int ori; + tetris_location loc; +} tetris_block; + +/* + All possible moves to give as input to the game. + */ +typedef enum { + TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE +} tetris_move; + +/* + A game object! + */ +typedef struct { + /* + Game board stuff: + */ + int rows; + int cols; + /* + Scoring information: + */ + int points; + int level; + /* + Falling block is the one currently going down. Next block is the one that + will be falling after this one. Stored is the block that you can swap out. + */ + tetris_block falling; + tetris_block next; + tetris_block stored; + /* + Number of game ticks until the block will move down. + */ + int ticks_till_gravity; + /* + Number of lines until you advance to the next level. + */ + int lines_remaining; + char board[]; +} tetris_game; + +/* + This array stores all necessary information about the cells that are filled by + each tetromino. The first index is the type of the tetromino (i.e. shape, + e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final + array contains 4 tetris_location objects, each mapping to an offset from a + point on the upper left that is the tetromino "origin". + */ +extern const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS]; + +/* + This array tells you how many ticks per gravity by level. Decreases as level + increases, to add difficulty. + */ +extern const int GRAVITY_LEVEL[MAX_LEVEL+1]; + +// Data structure manipulation. +void tg_init(tetris_game *obj, int rows, int cols); +tetris_game *tg_create(struct games_state *rs,int rows, int cols); +void tg_destroy(tetris_game *obj); +void tg_delete(tetris_game *obj); +tetris_game *tg_load(FILE *f); +void tg_save(tetris_game *obj, FILE *f); + +// Public methods not related to memory: +char tg_get(tetris_game *obj, int row, int col); +bool tg_check(tetris_game *obj, int row, int col); +bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move); +void tg_print(tetris_game *obj, FILE *f); + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ +#define GAMENAME "prices" // name of executable +#define GAMEMAIN prices // main program of game +#define GAMEPLAYERJSON pricesplayerjson // displays game specific json +#define GAMEDATA pricesdata // extracts data from game specific variables into games_state +#define CHAINNAME "PRICES" // -ac_name= +typedef uint64_t gamesevent; // can be 8, 16, 32, or 64 bits + +#define MAXPACK 23 +struct games_packitem +{ + int32_t type,launch,count,which,hplus,dplus,arm,flags,group; + char damage[8],hurldmg[8]; +}; + +struct games_player +{ + int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; + struct games_packitem gamespack[MAXPACK]; +}; + +struct games_state +{ + uint64_t seed,origseed; + char *keystrokeshex; + uint32_t needflush,replaydone; + int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; + FILE *logfp; + struct games_player P; + gamesevent buffered[5000],*keystrokes; + uint8_t playerdata[8192]; +}; +extern struct games_state globalR; +void *gamesiterate(struct games_state *rs); +int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag); + +void games_packitemstr(char *packitemstr,struct games_packitem *item); +uint64_t _games_rngnext(uint64_t initseed); +int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); +gamesevent games_revendian(gamesevent revx); +int32_t disp_gamesplayer(char *str,struct games_player *P); + +#endif + From 36fe43ff5876b0f161aaed3dc7b93999264a9ef0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 22:32:25 -1100 Subject: [PATCH 313/787] Make prices --- src/cc/gamescc.cpp | 4 ++-- src/cc/makeprices | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) create mode 100755 src/cc/makeprices diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index f010ac282..ef9aa157b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -16,7 +16,7 @@ #include "gamescc.h" #ifdef BUILD_PRICES #include "prices.c" -#elif +#else #include "tetris.c" #endif @@ -167,7 +167,7 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 #ifndef STANDALONE #ifdef BUILD_PRICES #include "prices.cpp" -#elif +#else #include "tetris.cpp" #endif diff --git a/src/cc/makeprices b/src/cc/makeprices new file mode 100755 index 000000000..012b8e157 --- /dev/null +++ b/src/cc/makeprices @@ -0,0 +1,3 @@ +gcc -O3 -DBUILD_GAMESCC -DBUILD_PRICES -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o pricescc.so cclib.cpp +gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE -DBUILD_PRICES gamescc.cpp -lncurses -lcurl -o tetris + From 7b832d7124d72473ca58066589970a9c764e45d0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:00:55 -1100 Subject: [PATCH 314/787] Games dir --- src/cc/{ => games}/prices.c | 6 +- src/cc/{tetris.cpp => games/prices.cpp} | 0 src/cc/{ => games}/prices.h | 0 src/cc/{ => games}/tetris.c | 0 src/cc/games/tetris.cpp | 73 ++++ src/cc/{ => games}/tetris.h | 0 src/cc/makeprices | 3 + src/cc/prices.cpp | 462 +++++++++++++++++++++--- 8 files changed, 500 insertions(+), 44 deletions(-) rename src/cc/{ => games}/prices.c (99%) rename src/cc/{tetris.cpp => games/prices.cpp} (100%) rename src/cc/{ => games}/prices.h (100%) rename src/cc/{ => games}/tetris.c (100%) create mode 100644 src/cc/games/tetris.cpp rename src/cc/{ => games}/tetris.h (100%) diff --git a/src/cc/prices.c b/src/cc/games/prices.c similarity index 99% rename from src/cc/prices.c rename to src/cc/games/prices.c index 651b1018c..02366fbc4 100644 --- a/src/cc/prices.c +++ b/src/cc/games/prices.c @@ -1,5 +1,5 @@ -#include "tetris.h" +#include "prices.h" /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. @@ -13,7 +13,7 @@ int random_tetromino(struct games_state *rs) return(rs->seed % NUM_TETROMINOS); } -int32_t tetrisdata(struct games_player *P,void *ptr) +int32_t pricesdata(struct games_player *P,void *ptr) { tetris_game *tg = (tetris_game *)ptr; P->gold = tg->points; @@ -832,7 +832,7 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve } else return(0); } -int tetris(int argc, char **argv) +int prices(int argc, char **argv) { struct games_state *rs = &globalR; int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg = 0; diff --git a/src/cc/tetris.cpp b/src/cc/games/prices.cpp similarity index 100% rename from src/cc/tetris.cpp rename to src/cc/games/prices.cpp diff --git a/src/cc/prices.h b/src/cc/games/prices.h similarity index 100% rename from src/cc/prices.h rename to src/cc/games/prices.h diff --git a/src/cc/tetris.c b/src/cc/games/tetris.c similarity index 100% rename from src/cc/tetris.c rename to src/cc/games/tetris.c diff --git a/src/cc/games/tetris.cpp b/src/cc/games/tetris.cpp new file mode 100644 index 000000000..8a52dcb37 --- /dev/null +++ b/src/cc/games/tetris.cpp @@ -0,0 +1,73 @@ + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +// game specific code for daemon +void games_packitemstr(char *packitemstr,struct games_packitem *item) +{ + strcpy(packitemstr,""); +} + +int64_t games_cashout(struct games_player *P) +{ + int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0; + cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel; + return(cashout); +} + +void tetrisplayerjson(UniValue &obj,struct games_player *P) +{ + obj.push_back(Pair("packsize",(int64_t)P->packsize)); + obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); + obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff))); + obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16))); + obj.push_back(Pair("level",(int64_t)P->level)); + obj.push_back(Pair("experience",(int64_t)P->experience)); + obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel)); +} + +int32_t disp_gamesplayer(char *str,struct games_player *P) +{ + str[0] = 0; + //if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) + // return(-1); + sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); + return(0); +} + +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) +{ + uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) + { + len -= 36; + for (i=0; i<32; i++) + ((uint8_t *)&gametxid)[i] = payload[len+i]; + eventid = (uint32_t)payload[len+32]; + eventid |= (uint32_t)payload[len+33] << 8; + eventid |= (uint32_t)payload[len+34] << 16; + eventid |= (uint32_t)payload[len+35] << 24; + //for (i=0; i oracletxid start with 'L', leverage, funding, direction + funds are locked into global CC address + it can be closed at anytime by the trader for cash settlement + the house account can close it if rekt + + Implementation Notes: + In order to eliminate the need for worrying about sybil attacks, each prices plan would be able to specific pubkey(s?) for whitelisted publishers. It would be possible to have a non-whitelisted plan that would use 50% correlation between publishers. + + delta neutral balancing of riskexposure: fabs(long exposure - short exposure) + bet +B at leverage L + absval(sum(+BLi) - sum(-Bli)) + + validate: update riskexposure and it needs to be <= funds + + PricesProfits: limit withdraw to funds in excess of riskexposure + PricesFinish: payout (if winning) and update riskexposure + need long/short exposure assets + + funding -> 1of2 CC global CC address and dealer address, exposure tokens to global 1of2 assets CC address + pricebet -> user funds and exposure token to 1of2 address. + pricewin -> winnings from dealer funds, exposure token back to global address + priceloss -> exposuretoken back to global address + + exposure address, funds address + + + +*/ + +// start of consensus code + +int64_t PricesOraclePrice(int64_t &rektprice,uint64_t mode,uint256 oracletxid,std::vectorpubkeys,int32_t dir,int64_t amount,int32_t leverage) { - strcpy(packitemstr,""); + int64_t price; + // howto ensure price when block it confirms it not known + // get price from oracle + current chaintip + // normalize leveraged amount + if ( dir > 0 ) + rektprice = price * leverage / (leverage-1); + else rektprice = price * (leverage-1) / leverage; + return(price); } -int64_t games_cashout(struct games_player *P) +CScript EncodePricesFundingOpRet(uint8_t funcid,CPubKey planpk,uint256 oracletxid,uint256 longtoken,uint256 shorttoken,int32_t millimargin,uint64_t mode,int32_t maxleverage,std::vector pubkeys,uint256 bettoken) { - int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0; - cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel; - return(cashout); + CScript opret; + fprintf(stderr,"implement EncodePricesFundingOpRet\n"); + return(opret); } -void tetrisplayerjson(UniValue &obj,struct games_player *P) +uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &oracletxid,uint256 &longtoken,uint256 &shorttoken,int32_t &millimargin,uint64_t &mode,int32_t &maxleverage,std::vector &pubkeys,uint256 &bettoken) { - obj.push_back(Pair("packsize",(int64_t)P->packsize)); - obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); - obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff))); - obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16))); - obj.push_back(Pair("level",(int64_t)P->level)); - obj.push_back(Pair("experience",(int64_t)P->experience)); - obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel)); -} - -int32_t disp_gamesplayer(char *str,struct games_player *P) -{ - str[0] = 0; - //if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) - // return(-1); - sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); + fprintf(stderr,"implement DecodePricesFundingOpRet\n"); return(0); } -int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) +bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; - if ( (len= payload.size()) > 36 ) + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; + + return true; // TODO remove, for test dual-evals + + return eval->Invalid("no validation yet"); + std::vector > txids; + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + preventCCvins = preventCCvouts = -1; + if ( numvouts < 1 ) + return eval->Invalid("no vouts"); + else { - len -= 36; - for (i=0; i<32; i++) - ((uint8_t *)&gametxid)[i] = payload[len+i]; - eventid = (uint32_t)payload[len+32]; - eventid |= (uint32_t)payload[len+33] << 8; - eventid |= (uint32_t)payload[len+34] << 16; - eventid |= (uint32_t)payload[len+35] << 24; - //for (i=0; iInvalid("illegal normal vini"); + } + } + //fprintf(stderr,"check amounts\n"); + //if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) + { + fprintf(stderr,"Pricesget invalid amount\n"); + return false; + } + //else + { + txid = tx.GetHash(); + memcpy(hash,&txid,sizeof(hash)); + retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); + if ( retval != 0 ) + fprintf(stderr,"Pricesget validated\n"); + else fprintf(stderr,"Pricesget invalid\n"); + return(retval); + } + } } +// end of consensus code -bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +// helper functions for rpc calls in rpcwallet.cpp + +int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,uint256 tolenid,int64_t total,int32_t maxinputs) { - return(true); + // add threshold check + int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + std::vector > unspentOutputs; + SetCCunspents(unspentOutputs,destaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + // need to prevent dup + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) + { + // need to verify assetid + if ( (nValue= vintx.vout[vout].nValue) >= 10000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + { + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } + } + } + return(totalinputs); } +UniValue PricesList() +{ + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint64_t mode; int32_t margin,maxleverage; std::vectorpubkeys; uint256 txid,hashBlock,oracletxid,longtoken,shorttoken,bettoken; CPubKey planpk,pricespk; char str[65]; CTransaction vintx; + cp = CCinit(&C,EVAL_PRICES); + pricespk = GetUnspendable(cp,0); + SetCCtxids(addressIndex,cp->normaladdr); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + txid = it->first.txhash; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + { + if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) + { + result.push_back(uint256_str(str,txid)); + } + } + } + return(result); +} + +// longtoken satoshis limits long exposure +// shorttoken satoshis limits short exposure +// both must be in the 1of2 CC address with its total supply +// bettoken +std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction oracletx; int64_t fullsupply,inputs,CCchange=0; uint256 hashBlock; char str[65],coinaddr[64],houseaddr[64]; CPubKey mypk,pricespk; int32_t i,N,numvouts; struct CCcontract_info *cp,C; + if ( funding < 100*COIN || maxleverage <= 0 || maxleverage > 10000 ) + { + CCerror = "invalid parameter error"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + return(""); + } + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + if ( (N= (int32_t)pubkeys.size()) || N > 15 ) + { + fprintf(stderr,"too many pubkeys N.%d\n",N); + return(""); + } + for (i=0; i 0 ) + { + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',mypk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken))); + } + else + { + CCerror = "cant find enough inputs"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } + return(""); +} + +UniValue PricesInfo(uint256 fundingtxid) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CPubKey pricespk,planpk; uint256 hashBlock,oracletxid,longtoken,shorttoken,bettoken; CTransaction vintx; int64_t balance,supply,exposure; uint64_t funding,mode; int32_t i,margin,maxleverage; char numstr[65],houseaddr[64],exposureaddr[64],str[65]; std::vectorpubkeys; struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_PRICES); + pricespk = GetUnspendable(cp,0); + if ( GetTransaction(fundingtxid,vintx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find fundingtxid\n"); + ERR_RESULT("cant find fundingtxid"); + return(result); + } + if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("fundingtxid",uint256_str(str,fundingtxid))); + result.push_back(Pair("bettoken",uint256_str(str,bettoken))); + result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); + sprintf(numstr,"%.3f",(double)margin/1000); + result.push_back(Pair("profitmargin",numstr)); + result.push_back(Pair("maxleverage",maxleverage)); + result.push_back(Pair("mode",(int64_t)mode)); + for (i=0; ipubkeys; + if ( amount < 10000 ) + { + CCerror = "amount must be positive"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + return(""); + } + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find fundingtxid\n"); + return(""); + } + if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) + { + GetCCaddress1of2(cp,houseaddr,pricespk,planpk); + if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + { + if ( (inputs= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,60)) >= amount ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(planpk)) << OP_CHECKSIG)); + if ( inputs > amount+txfee ) + CCchange = (inputs - amount); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); + // add addr2 + + std::vector voutTokenPubkeysEmpty; //TODO: add token vout pubkeys + return(FinalizeCCTx(0,cp,mtx,mypk,txfee, + EncodeTokenOpRet(bettoken, voutTokenPubkeysEmpty, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('t',/*bettoken,*/zeroid, 0, Mypubkey()))))); + } + else + { + CCerror = "cant find enough bet inputs"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } + } + else + { + CCerror = "cant find enough inputs"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } + } + return(""); +} + +std::string PricesBet(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,int64_t amount,int32_t leverage) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + struct CCcontract_info *cp,C; CPubKey pricespk,planpk,mypk; uint256 hashBlock,oracletxid,longtoken,shorttoken,tokenid,bettoken; CTransaction tx; int64_t balance,supply,exposure,inputs,inputs2,longexposure,netexposure,shortexposure,CCchange = 0,CCchange2 = 0; uint64_t funding,mode; int32_t dir,margin,maxleverage; char houseaddr[64],myaddr[64],exposureaddr[64]; std::vectorpubkeys; + if ( amount < 0 ) + { + amount = -amount; + dir = -1; + } else dir = 1; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find fundingtxid\n"); + return(""); + } + if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) + { + if ( leverage > maxleverage || leverage < 1 ) + { + fprintf(stderr,"illegal leverage\n"); + return(""); + } + GetCCaddress1of2(cp,houseaddr,pricespk,planpk); + GetCCaddress1of2(cp,exposureaddr,pricespk,pricespk); + if ( dir < 0 ) + tokenid = shorttoken; + else tokenid = longtoken; + exposure = leverage * amount; + longexposure = CCtoken_balance(exposureaddr,longtoken); + shortexposure = CCtoken_balance(exposureaddr,shorttoken); + netexposure = (longexposure - shortexposure + exposure*dir); + if ( netexposure < 0 ) + netexposure = -netexposure; + balance = CCtoken_balance(myaddr,bettoken) / COIN; + if ( balance < netexposure*9/10 ) // 10% extra room for dynamically closed bets in wrong direction + { + fprintf(stderr,"balance %lld < 90%% netexposure %lld, refuse bet\n",(long long)balance,(long long)netexposure); + return(""); + } + if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) + { + if ( (inputs= AddTokensInputs(cp,mtx,houseaddr,tokenid,exposure,30)) >= exposure ) + { + if ( (inputs2= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,30)) >= amount ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,exposure,pricespk,pricespk)); + if ( inputs > exposure+txfee ) + CCchange = (inputs - exposure); + if ( inputs2 > amount+txfee ) + CCchange2 = (inputs2 - amount); + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,CCchange,pricespk,planpk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange2,mypk)); + // add addr2 and addr3 + //return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesExtra('T',tokenid,bettoken,zeroid,dir*leverage))); + CScript opret; + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); + } + else + { + fprintf(stderr,"cant find enough bettoken inputs\n"); + return(""); + } + } + else + { + fprintf(stderr,"cant find enough exposure inputs\n"); + return(""); + } + } + else + { + CCerror = "cant find enough inputsB"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } + } + return(""); +} + +UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) +{ + UniValue result(UniValue::VOBJ); + // get height of bettxid + // get price and rekt + // get current height and price + // what about if rekt in the past? + return(result); +} + +std::string PricesFinish(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) +{ + return(""); +} + + From 8f6561951e9fd0a9ad2646214f051edc96b3a7b2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:01:45 -1100 Subject: [PATCH 315/787] Games/ --- src/cc/gamescc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index ef9aa157b..6bcd17b09 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -15,9 +15,9 @@ #include "gamescc.h" #ifdef BUILD_PRICES -#include "prices.c" +#include "games/prices.c" #else -#include "tetris.c" +#include "games/tetris.c" #endif int32_t GAMEDATA(struct games_player *P,void *ptr); @@ -166,9 +166,9 @@ int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int3 #ifndef STANDALONE #ifdef BUILD_PRICES -#include "prices.cpp" +#include "games/prices.cpp" #else -#include "tetris.cpp" +#include "games/tetris.cpp" #endif void GAMEJSON(UniValue &obj,struct games_player *P); From f31b33355a64fb5d42b7bed6bef8f8a2b8a1d8b0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:03:15 -1100 Subject: [PATCH 316/787] Pricesplayerjson --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 8a52dcb37..cb2de54a1 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -27,7 +27,7 @@ int64_t games_cashout(struct games_player *P) return(cashout); } -void tetrisplayerjson(UniValue &obj,struct games_player *P) +void pricesplayerjson(UniValue &obj,struct games_player *P) { obj.push_back(Pair("packsize",(int64_t)P->packsize)); obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); From 6fca707153a3dde7826bf3798085fb30f61cb8ce Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:04:32 -1100 Subject: [PATCH 317/787] (uint8_t) --- src/cc/gamescc.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 6bcd17b09..56fd13310 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1461,13 +1461,13 @@ UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) switch ( sizeof(gamesevent) ) { case 1: - sprintf(&hexstr[i<<1],"%02x",keystrokes[i]); + sprintf(&hexstr[i<<1],"%02x",(uint8_t)keystrokes[i]); break; case 2: - sprintf(&hexstr[i<<2],"%04x",keystrokes[i]); + sprintf(&hexstr[i<<2],"%04x",(uint16_t)keystrokes[i]); break; case 4: - sprintf(&hexstr[i<<3],"%08x",keystrokes[i]); + sprintf(&hexstr[i<<3],"%08x",(uint32_t)keystrokes[i]); break; case 8: sprintf(&hexstr[i<<4],"%016llxx",(long long)keystrokes[i]); From 5f2eeac994966f0b2629204e09eb753f0f472a44 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:08:20 -1100 Subject: [PATCH 318/787] Fixes --- src/cc/makeprices | 2 +- src/cc/maketetris | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cc/makeprices b/src/cc/makeprices index dc120deff..8bb98dd68 100755 --- a/src/cc/makeprices +++ b/src/cc/makeprices @@ -1,6 +1,6 @@ gcc -O3 -DBUILD_GAMESCC -DBUILD_PRICES -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o pricescc.so cclib.cpp cd games -gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE -DBUILD_PRICES gamescc.cpp -lncurses -lcurl -o tetris +gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE -DBUILD_PRICES ../gamescc.cpp -lncurses -lcurl -o tetris cd .. diff --git a/src/cc/maketetris b/src/cc/maketetris index 9f0af354e..f11a4de9c 100755 --- a/src/cc/maketetris +++ b/src/cc/maketetris @@ -1,2 +1,4 @@ -gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE gamescc.cpp -lncurses -lcurl -o tetris +cd games +gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE ../gamescc.cpp -lncurses -lcurl -o tetris +cd .. From 7e1fc1ef0986d8564a30f9551f9865e42e28af62 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:10:16 -1100 Subject: [PATCH 319/787] (uint8_t) --- src/cc/dapps/dappstd.c | 6 +++--- src/cc/makeprices | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c index 917045ed5..82366315c 100644 --- a/src/cc/dapps/dappstd.c +++ b/src/cc/dapps/dappstd.c @@ -761,11 +761,11 @@ int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,gam for (i=0; i Date: Tue, 26 Mar 2019 23:12:41 -1100 Subject: [PATCH 320/787] (uint8_t) --- src/cc/games/prices.c | 6 +++--- src/cc/games/tetris.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 02366fbc4..f35e8164b 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -804,11 +804,11 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve if ( 0 ) { if ( sizeof(c) == 1 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",(uint8_t)c&0xff,gametxidstr,eventid); else if ( sizeof(c) == 2 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",(uint16_t)c&0xffff,gametxidstr,eventid); else if ( sizeof(c) == 4 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",(uint32_t)c&0xffffffff,gametxidstr,eventid); else if ( sizeof(c) == 8 ) sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) diff --git a/src/cc/games/tetris.c b/src/cc/games/tetris.c index 651b1018c..254d324a8 100644 --- a/src/cc/games/tetris.c +++ b/src/cc/games/tetris.c @@ -804,11 +804,11 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve if ( 0 ) { if ( sizeof(c) == 1 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",c&0xff,gametxidstr,eventid); + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",(uint8_t)c&0xff,gametxidstr,eventid); else if ( sizeof(c) == 2 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",c&0xffff,gametxidstr,eventid); + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",(uint16_t)c&0xffff,gametxidstr,eventid); else if ( sizeof(c) == 4 ) - sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",c&0xffffffff,gametxidstr,eventid); + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",(uint32_t)c&0xffffffff,gametxidstr,eventid); else if ( sizeof(c) == 8 ) sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) From 97460af39460d404315a4268c6689e46f948db5d Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:21:34 -1100 Subject: [PATCH 321/787] Fix makecclib --- src/cc/makecclib | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/makecclib b/src/cc/makecclib index 38634aa78..8179623ad 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -6,7 +6,7 @@ make -f Makefile_rogue ./makerogue echo sudoku/musig/dilithium -gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o ../libcc.so cclib.cpp +gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o sudokucc.so cclib.cpp echo games tetris gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp From eed57deaffa0ca4ccfde66a5ac5852459af1c2d0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:36:34 -1100 Subject: [PATCH 322/787] Btcusd --- src/cc/games/prices.c | 777 +++++------------------------------------- 1 file changed, 83 insertions(+), 694 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index f35e8164b..cb475c763 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -1,5 +1,6 @@ #include "prices.h" +#include /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. @@ -22,90 +23,6 @@ int32_t pricesdata(struct games_player *P,void *ptr) return(0); } -/***************************************************************************/ -/** https://github.com/brenns10/tetris - @file main.c - @author Stephen Brennan - @date Created Wednesday, 10 June 2015 - @brief Main program for tetris. - @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised - BSD License. See LICENSE.txt for details. - *******************************************************************************/ - - -#include // for FILE -#include // for bool -#include -#include -#include -#include -#include -#include - -#ifdef BUILD_GAMESCC -#include "rogue/cursesd.h" -#else -#include -#endif - - -#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) -#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) - -/******************************************************************************* - Array Definitions - *******************************************************************************/ - -const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] = -{ - // I - {{{1, 0}, {1, 1}, {1, 2}, {1, 3}}, - {{0, 2}, {1, 2}, {2, 2}, {3, 2}}, - {{3, 0}, {3, 1}, {3, 2}, {3, 3}}, - {{0, 1}, {1, 1}, {2, 1}, {3, 1}}}, - // J - {{{0, 0}, {1, 0}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {2, 1}}, - {{1, 0}, {1, 1}, {1, 2}, {2, 2}}, - {{0, 1}, {1, 1}, {2, 0}, {2, 1}}}, - // L - {{{0, 2}, {1, 0}, {1, 1}, {1, 2}}, - {{0, 1}, {1, 1}, {2, 1}, {2, 2}}, - {{1, 0}, {1, 1}, {1, 2}, {2, 0}}, - {{0, 0}, {0, 1}, {1, 1}, {2, 1}}}, - // O - {{{0, 1}, {0, 2}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, - {{0, 1}, {0, 2}, {1, 1}, {1, 2}}}, - // S - {{{0, 1}, {0, 2}, {1, 0}, {1, 1}}, - {{0, 1}, {1, 1}, {1, 2}, {2, 2}}, - {{1, 1}, {1, 2}, {2, 0}, {2, 1}}, - {{0, 0}, {1, 0}, {1, 1}, {2, 1}}}, - // T - {{{0, 1}, {1, 0}, {1, 1}, {1, 2}}, - {{0, 1}, {1, 1}, {1, 2}, {2, 1}}, - {{1, 0}, {1, 1}, {1, 2}, {2, 1}}, - {{0, 1}, {1, 0}, {1, 1}, {2, 1}}}, - // Z - {{{0, 0}, {0, 1}, {1, 1}, {1, 2}}, - {{0, 2}, {1, 1}, {1, 2}, {2, 1}}, - {{1, 0}, {1, 1}, {2, 1}, {2, 2}}, - {{0, 1}, {1, 0}, {1, 1}, {2, 0}}}, -}; - -const int GRAVITY_LEVEL[MAX_LEVEL+1] = { - // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, - //10, 11, 12, 13, 14, 15, 16, 17, 18, 19, - 30, 28, 26, 24, 22, 20, 16, 12, 8, 4 -}; - -/******************************************************************************* - Helper Functions for Blocks - *******************************************************************************/ - void sleep_milli(int milliseconds) { struct timespec ts; @@ -114,578 +31,117 @@ void sleep_milli(int milliseconds) nanosleep(&ts, NULL); } -/* - Return the block at the given row and column. - */ -char tg_get(tetris_game *obj, int row, int column) +void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) { - return obj->board[obj->cols * row + column]; -} - -/* - Set the block at the given row and column. - */ -static void tg_set(tetris_game *obj, int row, int column, char value) -{ - obj->board[obj->cols * row + column] = value; -} - -/* - Check whether a row and column are in bounds. - */ -bool tg_check(tetris_game *obj, int row, int col) -{ - return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols; -} - -/* - Place a block onto the board. - */ -static void tg_put(tetris_game *obj, tetris_block block) -{ - int i; - for (i = 0; i < TETRIS; i++) { - tetris_location cell = TETROMINOS[block.typ][block.ori][i]; - tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, - TYPE_TO_CELL(block.typ)); - } -} - -/* - Clear a block out of the board. - */ -static void tg_remove(tetris_game *obj, tetris_block block) -{ - int i; - for (i = 0; i < TETRIS; i++) { - tetris_location cell = TETROMINOS[block.typ][block.ori][i]; - tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY); - } -} - -/* - Check if a block can be placed on the board. - */ -static bool tg_fits(tetris_game *obj, tetris_block block) -{ - int i, r, c; - for (i = 0; i < TETRIS; i++) { - tetris_location cell = TETROMINOS[block.typ][block.ori][i]; - r = block.loc.row + cell.row; - c = block.loc.col + cell.col; - if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) { - return false; - } - } - return true; -} - -/* - Create a new falling block and populate the next falling block with a random - one. - */ -static void tg_new_falling(struct games_state *rs,tetris_game *obj) -{ - // Put in a new falling tetromino. - obj->falling = obj->next; - obj->next.typ = random_tetromino(rs); - obj->next.ori = 0; - obj->next.loc.row = 0; - obj->next.loc.col = obj->cols/2 - 2; -} - -/******************************************************************************* - Game Turn Helpers - *******************************************************************************/ - -/* - Tick gravity, and move the block down if gravity should act. - */ -static void tg_do_gravity_tick(struct games_state *rs,tetris_game *obj) -{ - obj->ticks_till_gravity--; - if (obj->ticks_till_gravity <= 0) { - tg_remove(obj, obj->falling); - obj->falling.loc.row++; - if (tg_fits(obj, obj->falling)) { - obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; - } else { - obj->falling.loc.row--; - tg_put(obj, obj->falling); - - tg_new_falling(rs,obj); - } - tg_put(obj, obj->falling); - } -} - -/* - Move the falling tetris block left (-1) or right (+1). - */ -static void tg_move(tetris_game *obj, int direction) -{ - tg_remove(obj, obj->falling); - obj->falling.loc.col += direction; - if (!tg_fits(obj, obj->falling)) { - obj->falling.loc.col -= direction; - } - tg_put(obj, obj->falling); -} - -/* - Send the falling tetris block to the bottom. - */ -static void tg_down(struct games_state *rs,tetris_game *obj) -{ - tg_remove(obj, obj->falling); - while (tg_fits(obj, obj->falling)) { - obj->falling.loc.row++; - } - obj->falling.loc.row--; - tg_put(obj, obj->falling); - tg_new_falling(rs,obj); -} - -/* - Rotate the falling block in either direction (+/-1). - */ -static void tg_rotate(tetris_game *obj, int direction) -{ - tg_remove(obj, obj->falling); - - while (true) { - obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS; - - // If the new orientation fits, we're done. - if (tg_fits(obj, obj->falling)) - break; - - // Otherwise, try moving left to make it fit. - obj->falling.loc.col--; - if (tg_fits(obj, obj->falling)) - break; - - // Finally, try moving right to make it fit. - obj->falling.loc.col += 2; - if (tg_fits(obj, obj->falling)) - break; - - // Put it back in its original location and try the next orientation. - obj->falling.loc.col--; - // Worst case, we come back to the original orientation and it fits, so this - // loop will terminate. - } - - tg_put(obj, obj->falling); -} - -/* - Swap the falling block with the block in the hold buffer. - */ -static void tg_hold(struct games_state *rs,tetris_game *obj) -{ - tg_remove(obj, obj->falling); - if (obj->stored.typ == -1) { - obj->stored = obj->falling; - tg_new_falling(rs,obj); - } else { - int typ = obj->falling.typ, ori = obj->falling.ori; - obj->falling.typ = obj->stored.typ; - obj->falling.ori = obj->stored.ori; - obj->stored.typ = typ; - obj->stored.ori = ori; - while (!tg_fits(obj, obj->falling)) { - obj->falling.loc.row--; - } - } - tg_put(obj, obj->falling); -} - -/* - Perform the action specified by the move. - */ -static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move move) -{ - switch (move) { - case TM_LEFT: - //fprintf(stderr,"LEFT "); - tg_move(obj, -1); - break; - case TM_RIGHT: - //fprintf(stderr,"RIGHT "); - tg_move(obj, 1); - break; - case TM_DROP: - tg_down(rs,obj); - break; - case TM_CLOCK: - tg_rotate(obj, 1); - break; - case TM_COUNTER: - tg_rotate(obj, -1); - break; - case TM_HOLD: - tg_hold(rs,obj); - break; - default: - // pass - break; - } -} - -/* - Return true if line i is full. - */ -static bool tg_line_full(tetris_game *obj, int i) -{ - int j; - for (j = 0; j < obj->cols; j++) { - if (TC_IS_EMPTY(tg_get(obj, i, j))) - return false; - } - return true; -} - -/* - Shift every row above r down one. - */ -static void tg_shift_lines(tetris_game *obj, int r) -{ - int i, j; - for (i = r-1; i >= 0; i--) { - for (j = 0; j < obj->cols; j++) { - tg_set(obj, i+1, j, tg_get(obj, i, j)); - tg_set(obj, i, j, TC_EMPTY); - } - } -} - -/* - Find rows that are filled, remove them, shift, and return the number of - cleared rows. - */ -static int tg_check_lines(tetris_game *obj) -{ - int i, nlines = 0; - tg_remove(obj, obj->falling); // don't want to mess up falling block - - for (i = obj->rows-1; i >= 0; i--) { - if (tg_line_full(obj, i)) { - tg_shift_lines(obj, i); - i++; // do this line over again since they're shifted - nlines++; - } - } - - tg_put(obj, obj->falling); // replace - return nlines; -} - -/* - Adjust the score for the game, given how many lines were just cleared. - */ -static void tg_adjust_score(tetris_game *obj, int lines_cleared) -{ - static int line_multiplier[] = {0, 40, 100, 300, 1200}; - obj->points += line_multiplier[lines_cleared] * (obj->level + 1); - if (lines_cleared >= obj->lines_remaining) { - obj->level = MIN(MAX_LEVEL, obj->level + 1); - lines_cleared -= obj->lines_remaining; - obj->lines_remaining = LINES_PER_LEVEL - lines_cleared; - } else { - obj->lines_remaining -= lines_cleared; - } -} - -/* - Return true if the game is over. - */ -static bool tg_game_over(tetris_game *obj) -{ - int i, j; - bool over = false; - tg_remove(obj, obj->falling); - for (i = 0; i < 2; i++) { - for (j = 0; j < obj->cols; j++) { - if (TC_IS_FILLED(tg_get(obj, i, j))) { - over = true; - } - } - } - tg_put(obj, obj->falling); - return over; -} - -/******************************************************************************* - Main Public Functions - *******************************************************************************/ - -/* - Do a single game tick: process gravity, user input, and score. Return true if - the game is still running, false if it is over. - */ -bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move) -{ - int lines_cleared; - // Handle gravity. - tg_do_gravity_tick(rs,obj); - - // Handle input. - tg_handle_move(rs,obj, move); - - // Check for cleared lines - lines_cleared = tg_check_lines(obj); - - tg_adjust_score(obj, lines_cleared); - - // Return whether the game will continue (NOT whether it's over) - return !tg_game_over(obj); -} - -void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols) -{ - // Initialization logic - obj->rows = rows; - obj->cols = cols; - //obj->board = (char *)malloc(rows * cols); - memset(obj->board, TC_EMPTY, rows * cols); - obj->points = 0; - obj->level = 0; - obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; - obj->lines_remaining = LINES_PER_LEVEL; - //srand(time(NULL)); - tg_new_falling(rs,obj); - tg_new_falling(rs,obj); - obj->stored.typ = -1; - obj->stored.ori = 0; - obj->stored.loc.row = 0; - obj->next.loc.col = obj->cols/2 - 2; - //printf("%d", obj->falling.loc.col); -} - -tetris_game *tg_create(struct games_state *rs,int rows, int cols) -{ - tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game) + rows*cols); - tg_init(rs,obj, rows, cols); - return obj; -} - -/*void tg_destroy(tetris_game *obj) -{ - // Cleanup logic - free(obj->board); -}*/ - -void tg_delete(tetris_game *obj) { - //tg_destroy(obj); - free(obj); -} - -/* - Load a game from a file. - -tetris_game *tg_load(FILE *f) -{ - tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); - if (fread(obj, sizeof(tetris_game), 1, f) != 1 ) + FILE *fp; + long filesize,buflen = *allocsizep; + uint8_t *buf = *bufp; + *lenp = 0; + if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) { - fprintf(stderr,"read game error\n"); - free(obj); - obj = 0; - } - else - { - obj->board = (char *)malloc(obj->rows * obj->cols); - if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + fseek(fp,0,SEEK_END); + filesize = ftell(fp); + if ( filesize == 0 ) { - fprintf(stderr,"fread error\n"); - free(obj->board); - free(obj); - obj = 0; + fclose(fp); + *lenp = 0; + //printf("loadfile null size.(%s)\n",fname); + return(0); } - } - return obj; -}*/ - -/* - Save a game to a file. - -void tg_save(tetris_game *obj, FILE *f) -{ - if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) - fprintf(stderr,"error writing tetrisgame\n"); - else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) - fprintf(stderr,"error writing board\n"); -}*/ - -/* - Print a game board to a file. Really just for early debugging. - */ -void tg_print(tetris_game *obj, FILE *f) { - int i, j; - for (i = 0; i < obj->rows; i++) { - for (j = 0; j < obj->cols; j++) { - if (TC_IS_EMPTY(tg_get(obj, i, j))) { - fputs(TC_EMPTY_STR, f); - } else { - fputs(TC_BLOCK_STR, f); - } + if ( filesize > buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); } - fputc('\n', f); - } -} - -/* - 2 columns per cell makes the game much nicer. - */ -#define COLS_PER_CELL 2 -/* - Macro to print a cell of a specific type to a window. - */ -#define ADD_BLOCK(w,x) waddch((w),' '|A_REVERSE|COLOR_PAIR(x)); \ -waddch((w),' '|A_REVERSE|COLOR_PAIR(x)) -#define ADD_EMPTY(w) waddch((w), ' '); waddch((w), ' ') - -/* - Print the tetris board onto the ncurses window. - */ -void display_board(WINDOW *w, tetris_game *obj) -{ - int i, j; - box(w, 0, 0); - for (i = 0; i < obj->rows; i++) { - wmove(w, 1 + i, 1); - for (j = 0; j < obj->cols; j++) { - if (TC_IS_FILLED(tg_get(obj, i, j))) { - ADD_BLOCK(w,tg_get(obj, i, j)); - } else { - ADD_EMPTY(w); - } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; } - } - wnoutrefresh(w); + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); } -/* - Display a tetris piece in a dedicated window. - */ -void display_piece(WINDOW *w, tetris_block block) +void *filestr(long *allocsizep,char *_fname) { - int b; - tetris_location c; - wclear(w); - box(w, 0, 0); - if (block.typ == -1) { - wnoutrefresh(w); - return; - } - for (b = 0; b < TETRIS; b++) { - c = TETROMINOS[block.typ][block.ori][b]; - wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1); - ADD_BLOCK(w, TYPE_TO_CELL(block.typ)); - } - wnoutrefresh(w); + long filesize = 0; char *fname,*buf = 0; void *retptr; + *allocsizep = 0; + fname = malloc(strlen(_fname)+1); + strcpy(fname,_fname); + retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); + free(fname); + return(retptr); } -/* - Display score information in a dedicated window. - */ -void display_score(WINDOW *w, tetris_game *tg) +char *send_curl(char *url,char *fname) { - wclear(w); - box(w, 0, 0); - wprintw(w, (char *)"Score\n%d\n", tg->points); - wprintw(w, (char *)"Level\n%d\n", tg->level); - wprintw(w, (char *)"Lines\n%d\n", tg->lines_remaining); - wnoutrefresh(w); + long fsize; char curlstr[1024]; + sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); + system(curlstr); + return(filestr(&fsize,fname)); } -/* - Save and exit the game. - -void save(tetris_game *game, WINDOW *w) +cJSON *get_urljson(char *url,char *fname) { - FILE *f; - - wclear(w); - box(w, 0, 0); // return the border - wmove(w, 1, 1); - wprintw(w, (char *)"Save and exit? [Y/n] "); - wrefresh(w); - timeout(-1); - if (getch() == 'n') { - timeout(0); - return; + char *jsonstr; cJSON *json = 0; + if ( (jsonstr= send_curl(url,fname)) != 0 ) + { + //printf("(%s) -> (%s)\n",url,jsonstr); + json = cJSON_Parse(jsonstr); + free(jsonstr); } - f = fopen("tetris.save", "w"); - tg_save(game, f); - fclose(f); - tg_delete(game); - endwin(); - fprintf(stderr,"Game saved to \"tetris.save\".\n"); - fprintf(stderr,"Resume by passing the filename as an argument to this program.\n"); - exit(EXIT_SUCCESS); -}*/ + return(json); +} -/* - Do the NCURSES initialization steps for color blocks. - */ -void init_colors(void) +////////////////////////////////////////////// +// start of dapp +////////////////////////////////////////////// + +uint64_t get_btcusd() { - start_color(); - //init_color(COLOR_ORANGE, 1000, 647, 0); - init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK); - init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK); - init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK); - init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK); - init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK); - init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK); - init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); + cJSON *pjson,*bpi,*usd; uint64_t btcusd = 0; + if ( (pjson= get_urljson("http://api.coindesk.com/v1/bpi/currentprice.json","/tmp/oraclefeed.json")) != 0 ) + { + if ( (bpi= jobj(pjson,"bpi")) != 0 && (usd= jobj(bpi,"USD")) != 0 ) + { + btcusd = jdouble(usd,"rate_float") * SATOSHIDEN; + printf("BTC/USD %.4f\n",dstr(btcusd)); + } + free_json(pjson); + } + return(btcusd); } struct games_state globalR; extern char Gametxidstr[]; int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); -gamesevent games_readevent(struct games_state *rs); void *gamesiterate(struct games_state *rs) { - uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; - gamesevent c; uint16_t skipcount=0; int32_t prevlevel; uint32_t eventid = 0; tetris_game *tg; - WINDOW *board, *next, *hold, *score; + bool running = true; uint32_t eventid = 0; int64_t price; if ( rs->guiflag != 0 || rs->sleeptime != 0 ) { - // NCURSES initialization: - initscr(); // initialize curses - cbreak(); // pass key presses to program, but not signals - noecho(); // don't echo key presses to screen - keypad(stdscr, TRUE); // allow arrow keys - timeout(0); // no blocking on getch() - curs_set(0); // set the cursor to invisible - init_colors(); // setup tetris colors } - tg = tg_create(rs,22, 10); - prevlevel = tg->level; - // Create windows for each section of the interface. - board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); - next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); - hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); - score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); while ( running != 0 ) { - running = tg_tick(rs,tg,move); - if ( 1 && (rs->guiflag != 0 || rs->sleeptime != 0) ) + //running = tg_tick(rs,tg,move); + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) { - display_board(board,tg); - display_piece(next,tg->next); - display_piece(hold,tg->stored); - display_score(score,tg); } if ( rs->guiflag != 0 ) { #ifdef STANDALONE - sleep_milli(15); - if ( (counter++ % 10) == 0 ) + sleep_milli(10000); + price = get_btcusd(); + fprintf(stderr,"price %llu %.8f\n",(long long)price,(double)price/COIN); + /*if ( (counter++ % 10) == 0 ) doupdate(); c = games_readevent(rs); if ( c <= 0x7f || skipcount == 0x3fff ) @@ -700,7 +156,7 @@ void *gamesiterate(struct games_state *rs) prevlevel = tg->level; } skipcount = 0; - } else skipcount++; + } else skipcount++;*/ #endif } else @@ -713,7 +169,7 @@ void *gamesiterate(struct games_state *rs) if ( (counter++ % 20) == 0 ) doupdate(); } - if ( skipcount == 0 ) + /*if ( skipcount == 0 ) { c = games_readevent(rs); //fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); @@ -724,56 +180,14 @@ void *gamesiterate(struct games_state *rs) } } if ( skipcount > 0 ) - skipcount--; + skipcount--;*/ } eventid++; - switch ( c ) - { - case 'h': - move = TM_LEFT; - break; - case 'l': - move = TM_RIGHT; - break; - case 'k': - move = TM_CLOCK; - break; - case 'j': - move = TM_DROP; - break; - case 'q': - running = false; - move = TM_NONE; - break; - /*case 'p': - wclear(board); - box(board, 0, 0); - wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); - wprintw(board, "PAUSED"); - wrefresh(board); - timeout(-1); - getch(); - timeout(0); - move = TM_NONE; - break; - case 's': - save(tg, board); - move = TM_NONE; - break;*/ - case ' ': - move = TM_HOLD; - break; - default: - move = TM_NONE; - } } - return(tg); + return(0); } #ifdef STANDALONE -/* - Main tetris game! - */ #include "dapps/dappstd.c" @@ -835,7 +249,7 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve int prices(int argc, char **argv) { struct games_state *rs = &globalR; - int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg = 0; + int32_t c,skipcount=0; uint32_t eventid = 0; memset(rs,0,sizeof(*rs)); rs->guiflag = 1; rs->sleeptime = 1; // non-zero to allow refresh() @@ -862,33 +276,8 @@ int prices(int argc, char **argv) } } } else rs->seed = 777; - - /* Load file if given a filename. - if (argc >= 2) { - FILE *f = fopen(argv[1], "r"); - if (f == NULL) { - perror("tetris"); - exit(EXIT_FAILURE); - } - tg = tg_load(f); - fclose(f); - } else { - // Otherwise create new game. - tg = tg_create(rs,22, 10); - }*/ - - // Game loop - tg = (tetris_game *)gamesiterate(rs); - gamesbailout(rs); - // Deinitialize NCurses - wclear(stdscr); - endwin(); - // Output ending message. - printf("Game over!\n"); - printf("You finished with %d points on level %d.\n", tg->points, tg->level); - - // Deinitialize Tetris - tg_delete(tg); + gamesiterate(rs); + //gamesbailout(rs); return 0; } From 02e76e122bf1b71515e0707962d1f0f0b92fb834 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:40:09 -1100 Subject: [PATCH 323/787] syntax --- src/cc/games/prices.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index cb475c763..81f58521c 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -31,6 +31,37 @@ void sleep_milli(int milliseconds) nanosleep(&ts, NULL); } +char *nonportable_path(char *str) +{ + int32_t i; + for (i=0; str[i]!=0; i++) + if ( str[i] == '/' ) + str[i] = '\\'; + return(str); +} + +char *portable_path(char *str) +{ +#ifdef _WIN32 + return(nonportable_path(str)); +#else +#ifdef __PNACL + /*int32_t i,n; + if ( str[0] == '/' ) + return(str); + else + { + n = (int32_t)strlen(str); + for (i=n; i>0; i--) + str[i] = str[i-1]; + str[0] = '/'; + str[n+1] = 0; + }*/ +#endif + return(str); +#endif +} + void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) { FILE *fp; @@ -73,7 +104,7 @@ void *filestr(long *allocsizep,char *_fname) { long filesize = 0; char *fname,*buf = 0; void *retptr; *allocsizep = 0; - fname = malloc(strlen(_fname)+1); + fname = (char *)malloc(strlen(_fname)+1); strcpy(fname,_fname); retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); free(fname); From 2821c37693fe66b4fafef0120cc9a6546abe1f81 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:41:07 -1100 Subject: [PATCH 324/787] Ncurses --- src/cc/games/prices.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 81f58521c..96506b41c 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -1,6 +1,7 @@ #include "prices.h" #include +#include /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. From 4d66ed85244123fa136689bf3e760c1aa12c8dd8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:43:26 -1100 Subject: [PATCH 325/787] -curses.h --- src/cc/games/prices.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 96506b41c..81f58521c 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -1,7 +1,6 @@ #include "prices.h" #include -#include /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. From 55b3b006edb1e99a85de8b60b9e00f89f20b8f71 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:44:12 -1100 Subject: [PATCH 326/787] Test --- src/cc/games/prices.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 81f58521c..66cf54030 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -219,6 +219,7 @@ void *gamesiterate(struct games_state *rs) } #ifdef STANDALONE +#include #include "dapps/dappstd.c" From 20ff5d20d4182a17abf495ed0b9cbaab2419b0e3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:46:06 -1100 Subject: [PATCH 327/787] syntax --- src/cc/games/prices.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 66cf54030..e125a1602 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -116,7 +116,7 @@ char *send_curl(char *url,char *fname) long fsize; char curlstr[1024]; sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); system(curlstr); - return(filestr(&fsize,fname)); + return((char *)filestr(&fsize,fname)); } cJSON *get_urljson(char *url,char *fname) @@ -138,11 +138,11 @@ cJSON *get_urljson(char *url,char *fname) uint64_t get_btcusd() { cJSON *pjson,*bpi,*usd; uint64_t btcusd = 0; - if ( (pjson= get_urljson("http://api.coindesk.com/v1/bpi/currentprice.json","/tmp/oraclefeed.json")) != 0 ) + if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json","/tmp/oraclefeed.json")) != 0 ) { - if ( (bpi= jobj(pjson,"bpi")) != 0 && (usd= jobj(bpi,"USD")) != 0 ) + if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 && (usd= jobj(bpi,(char *)"USD")) != 0 ) { - btcusd = jdouble(usd,"rate_float") * SATOSHIDEN; + btcusd = jdouble(usd,(char *)"rate_float") * SATOSHIDEN; printf("BTC/USD %.4f\n",dstr(btcusd)); } free_json(pjson); @@ -197,8 +197,6 @@ void *gamesiterate(struct games_state *rs) if ( rs->sleeptime != 0 ) { sleep_milli(1); - if ( (counter++ % 20) == 0 ) - doupdate(); } /*if ( skipcount == 0 ) { From eca3bda195239b325d1f71b350b3983c46b6bc13 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:48:03 -1100 Subject: [PATCH 328/787] Prints --- src/cc/games/prices.c | 2 +- src/cc/makeprices | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index e125a1602..057e8be7f 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -138,7 +138,7 @@ cJSON *get_urljson(char *url,char *fname) uint64_t get_btcusd() { cJSON *pjson,*bpi,*usd; uint64_t btcusd = 0; - if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json","/tmp/oraclefeed.json")) != 0 ) + if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json",(char *)"/tmp/oraclefeed.json")) != 0 ) { if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 && (usd= jobj(bpi,(char *)"USD")) != 0 ) { diff --git a/src/cc/makeprices b/src/cc/makeprices index ca8b80065..2779c1c5b 100755 --- a/src/cc/makeprices +++ b/src/cc/makeprices @@ -1,4 +1,6 @@ +echo pricescc.so gcc -O3 -DBUILD_GAMESCC -DBUILD_PRICES -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o pricescc.so cclib.cpp +echo prices cd games gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE -DBUILD_PRICES ../gamescc.cpp -lncurses -lcurl -o prices cd .. From 382dd5021874f14230ac7e448517f00563de1eef Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:49:50 -1100 Subject: [PATCH 329/787] Test --- src/cc/games/prices.c | 142 +++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 71 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 057e8be7f..fa8749605 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -31,6 +31,77 @@ void sleep_milli(int milliseconds) nanosleep(&ts, NULL); } +struct games_state globalR; +extern char Gametxidstr[]; +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); +uint64_t get_btcusd(); + +void *gamesiterate(struct games_state *rs) +{ + bool running = true; uint32_t eventid = 0; int64_t price; + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) + { + } + while ( running != 0 ) + { + //running = tg_tick(rs,tg,move); + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) + { + } + if ( rs->guiflag != 0 ) + { +#ifdef STANDALONE + sleep_milli(10000); + price = get_btcusd(); + fprintf(stderr,"price %llu %.8f\n",(long long)price,(double)price/COIN); + /*if ( (counter++ % 10) == 0 ) + doupdate(); + c = games_readevent(rs); + if ( c <= 0x7f || skipcount == 0x3fff ) + { + if ( skipcount > 0 ) + issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); + if ( c <= 0x7f ) + issue_games_events(rs,Gametxidstr,eventid,c); + if ( tg->level != prevlevel ) + { + flushkeystrokes(rs,0); + prevlevel = tg->level; + } + skipcount = 0; + } else skipcount++;*/ +#endif + } + else + { + if ( rs->replaydone != 0 ) + break; + if ( rs->sleeptime != 0 ) + { + sleep_milli(1); + } + /*if ( skipcount == 0 ) + { + c = games_readevent(rs); + //fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); + if ( (c & 0x4000) == 0x4000 ) + { + skipcount = (c & 0x3fff); + c = 'S'; + } + } + if ( skipcount > 0 ) + skipcount--;*/ + } + eventid++; + } + return(0); +} + +#ifdef STANDALONE +#include +#include "dapps/dappstd.c" + char *nonportable_path(char *str) { int32_t i; @@ -150,77 +221,6 @@ uint64_t get_btcusd() return(btcusd); } -struct games_state globalR; -extern char Gametxidstr[]; -int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); - -void *gamesiterate(struct games_state *rs) -{ - bool running = true; uint32_t eventid = 0; int64_t price; - if ( rs->guiflag != 0 || rs->sleeptime != 0 ) - { - } - while ( running != 0 ) - { - //running = tg_tick(rs,tg,move); - if ( rs->guiflag != 0 || rs->sleeptime != 0 ) - { - } - if ( rs->guiflag != 0 ) - { -#ifdef STANDALONE - sleep_milli(10000); - price = get_btcusd(); - fprintf(stderr,"price %llu %.8f\n",(long long)price,(double)price/COIN); - /*if ( (counter++ % 10) == 0 ) - doupdate(); - c = games_readevent(rs); - if ( c <= 0x7f || skipcount == 0x3fff ) - { - if ( skipcount > 0 ) - issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); - if ( c <= 0x7f ) - issue_games_events(rs,Gametxidstr,eventid,c); - if ( tg->level != prevlevel ) - { - flushkeystrokes(rs,0); - prevlevel = tg->level; - } - skipcount = 0; - } else skipcount++;*/ -#endif - } - else - { - if ( rs->replaydone != 0 ) - break; - if ( rs->sleeptime != 0 ) - { - sleep_milli(1); - } - /*if ( skipcount == 0 ) - { - c = games_readevent(rs); - //fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); - if ( (c & 0x4000) == 0x4000 ) - { - skipcount = (c & 0x3fff); - c = 'S'; - } - } - if ( skipcount > 0 ) - skipcount--;*/ - } - eventid++; - } - return(0); -} - -#ifdef STANDALONE -#include -#include "dapps/dappstd.c" - - char *clonestr(char *str) { char *clone; int32_t len; From 498c813b674b21fd1557004cc029f2e65c0bf42c Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:53:59 -1100 Subject: [PATCH 330/787] #include "rogue/cursesd.c" --- src/cc/cclib.cpp | 2 +- src/cc/games/prices.c | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 95748def4..975b0735c 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -691,8 +691,8 @@ int32_t cclib_parsehash(uint8_t *hash32,cJSON *item,int32_t len) #include "customcc.cpp" #elif BUILD_GAMESCC -#include "gamescc.cpp" #include "rogue/cursesd.c" +#include "gamescc.cpp" #else #include "sudoku.cpp" diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index fa8749605..b9da75fe2 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -53,7 +53,7 @@ void *gamesiterate(struct games_state *rs) #ifdef STANDALONE sleep_milli(10000); price = get_btcusd(); - fprintf(stderr,"price %llu %.8f\n",(long long)price,(double)price/COIN); + fprintf(stderr,"price %llu %.8f\n",(long long)price,(double)price/SATOSHIDEN); /*if ( (counter++ % 10) == 0 ) doupdate(); c = games_readevent(rs); @@ -184,9 +184,12 @@ void *filestr(long *allocsizep,char *_fname) char *send_curl(char *url,char *fname) { - long fsize; char curlstr[1024]; + long fsize; char curlstr[1024],*retstr,*retstr2; sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); - system(curlstr); + //retstr2 = bitcoind_RPC(&retstr,(char *)"prices",url,"","",""); + + if ( system(curlstr) != 0 ) + fprintf(stderr,"error doing system(%s)\n",curlstr); return((char *)filestr(&fsize,fname)); } From 75465c987cfbd6707f3a5438918ed12747a7ae1e Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:55:08 -1100 Subject: [PATCH 331/787] SATOSHIDEN --- src/cc/games/prices.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index b9da75fe2..3d72965fc 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -1,6 +1,7 @@ #include "prices.h" #include +#define SATOSHIDEN 100000000 /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. From 9f42c31c2a81872822e9944dd8d5fd82be943072 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:55:38 -1100 Subject: [PATCH 332/787] #define SATOSHIDEN ((uint64_t)100000000L) --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 3d72965fc..0ac0844c2 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -1,7 +1,7 @@ #include "prices.h" #include -#define SATOSHIDEN 100000000 +#define SATOSHIDEN ((uint64_t)100000000L) /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. From 73e6a1cb6bb803cf529f51b1a0f6cd8083b7903a Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:58:10 -1100 Subject: [PATCH 333/787] Fx --- src/cc/makecclib | 2 -- src/cc/maketetris | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cc/makecclib b/src/cc/makecclib index 8179623ad..81026e75a 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -9,11 +9,9 @@ echo sudoku/musig/dilithium gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o sudokucc.so cclib.cpp echo games tetris -gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp ./maketetris echo games prices -gcc -O3 -DBUILD_GAMESCC -DBUILD_PRICES -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o pricescc.so cclib.cpp ./makeprices echo customcc stub diff --git a/src/cc/maketetris b/src/cc/maketetris index f11a4de9c..c39536229 100755 --- a/src/cc/maketetris +++ b/src/cc/maketetris @@ -1,3 +1,6 @@ +echo gamescc.so with tetris +gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp +echo tetris dapp cd games gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE ../gamescc.cpp -lncurses -lcurl -o tetris cd .. From f49f901f4f12d73b47915be3b9adb0ab526d3f11 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:06:18 -1100 Subject: [PATCH 334/787] Change Tetris washout --- src/cc/games/tetris.cpp | 4 ++-- src/cc/makecclib | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/games/tetris.cpp b/src/cc/games/tetris.cpp index 8a52dcb37..f1b6416ba 100644 --- a/src/cc/games/tetris.cpp +++ b/src/cc/games/tetris.cpp @@ -22,8 +22,8 @@ void games_packitemstr(char *packitemstr,struct games_packitem *item) int64_t games_cashout(struct games_player *P) { - int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0; - cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel; + int32_t dungeonlevel = P->dungeonlevel; int64_t mult=10000,cashout = 0; + cashout = (uint64_t)P->gold * mult; return(cashout); } diff --git a/src/cc/makecclib b/src/cc/makecclib index 81026e75a..b2f8e2ee1 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -1,5 +1,5 @@ #!/bin/sh -rm *.so rogue/rogue tetris +rm *.so rogue/rogue games/tetris games/prices echo rogue make -f Makefile_rogue From 58be59a00d8876b492faeef0b176a09478ec704c Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:10:17 -1100 Subject: [PATCH 335/787] Test --- src/cc/games/prices.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 0ac0844c2..658ed2406 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -185,13 +185,14 @@ void *filestr(long *allocsizep,char *_fname) char *send_curl(char *url,char *fname) { - long fsize; char curlstr[1024],*retstr,*retstr2; - sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); - //retstr2 = bitcoind_RPC(&retstr,(char *)"prices",url,"","",""); - - if ( system(curlstr) != 0 ) - fprintf(stderr,"error doing system(%s)\n",curlstr); - return((char *)filestr(&fsize,fname)); + //long fsize; char curlstr[1024]; + //sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); + char *retstr=0,*retstr2; + retstr2 = bitcoind_RPC(&retstr,(char *)"prices",url,"","",""); + return(retstr2); + //if ( system(curlstr) != 0 ) + // fprintf(stderr,"error doing system(%s)\n",curlstr); + //return((char *)filestr(&fsize,fname)); } cJSON *get_urljson(char *url,char *fname) From 5bbe79b586a6ba832fd779806eed5ba8cc341d10 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:11:07 -1100 Subject: [PATCH 336/787] Sleep --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 658ed2406..8798fa68f 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -52,7 +52,7 @@ void *gamesiterate(struct games_state *rs) if ( rs->guiflag != 0 ) { #ifdef STANDALONE - sleep_milli(10000); + sleep(1); price = get_btcusd(); fprintf(stderr,"price %llu %.8f\n",(long long)price,(double)price/SATOSHIDEN); /*if ( (counter++ % 10) == 0 ) From 73cc7ba642629343f8b7ef6e6553b3e19771c8b3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:11:31 -1100 Subject: [PATCH 337/787] (char *) --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 8798fa68f..32e7e509c 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -188,7 +188,7 @@ char *send_curl(char *url,char *fname) //long fsize; char curlstr[1024]; //sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); char *retstr=0,*retstr2; - retstr2 = bitcoind_RPC(&retstr,(char *)"prices",url,"","",""); + retstr2 = bitcoind_RPC(&retstr,(char *)"prices",url,(char *)"",(char *)"",(char *)""); return(retstr2); //if ( system(curlstr) != 0 ) // fprintf(stderr,"error doing system(%s)\n",curlstr); From e7373723f4693bcfcef5900cf4a847a764048f0e Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:12:38 -1100 Subject: [PATCH 338/787] Units --- src/cc/games/prices.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 32e7e509c..41ddb8fd7 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -1,6 +1,7 @@ #include "prices.h" #include +#include #define SATOSHIDEN ((uint64_t)100000000L) /* @@ -52,7 +53,7 @@ void *gamesiterate(struct games_state *rs) if ( rs->guiflag != 0 ) { #ifdef STANDALONE - sleep(1); + sleep(10); price = get_btcusd(); fprintf(stderr,"price %llu %.8f\n",(long long)price,(double)price/SATOSHIDEN); /*if ( (counter++ % 10) == 0 ) From e2527311f2bc8e8aa2d9bf6f18313b6556e1310f Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:14:56 -1100 Subject: [PATCH 339/787] Test --- src/cc/games/prices.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 41ddb8fd7..339f3293c 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -190,6 +190,10 @@ char *send_curl(char *url,char *fname) //sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); char *retstr=0,*retstr2; retstr2 = bitcoind_RPC(&retstr,(char *)"prices",url,(char *)"",(char *)"",(char *)""); + if ( retstr2 != 0 ) + printf("retstr2 (%s)\n",retstr2); + if ( retstr != 0 ) + printf("retstr (%s)\n",retstr); return(retstr2); //if ( system(curlstr) != 0 ) // fprintf(stderr,"error doing system(%s)\n",curlstr); @@ -201,7 +205,7 @@ cJSON *get_urljson(char *url,char *fname) char *jsonstr; cJSON *json = 0; if ( (jsonstr= send_curl(url,fname)) != 0 ) { - //printf("(%s) -> (%s)\n",url,jsonstr); + printf("(%s) -> (%s)\n",url,jsonstr); json = cJSON_Parse(jsonstr); free(jsonstr); } From 63a02c688ab238141847f2ad93a0c0b1bebe7db4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:17:23 -1100 Subject: [PATCH 340/787] issue_curl --- src/cc/games/prices.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 339f3293c..6d3694d30 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -188,13 +188,11 @@ char *send_curl(char *url,char *fname) { //long fsize; char curlstr[1024]; //sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); - char *retstr=0,*retstr2; - retstr2 = bitcoind_RPC(&retstr,(char *)"prices",url,(char *)"",(char *)"",(char *)""); - if ( retstr2 != 0 ) - printf("retstr2 (%s)\n",retstr2); + char *retstr; + retstr = issue_curl(url,10); if ( retstr != 0 ) printf("retstr (%s)\n",retstr); - return(retstr2); + return(retstr); //if ( system(curlstr) != 0 ) // fprintf(stderr,"error doing system(%s)\n",curlstr); //return((char *)filestr(&fsize,fname)); From 7d251f6f240333f7727384dc7fdd5dd390df4ed7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:18:30 -1100 Subject: [PATCH 341/787] #define issue_curl(cmdstr) bitcoind_RPC(0,"curl",cmdstr,0,0,0,0) --- src/cc/games/prices.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 6d3694d30..e45222c13 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -3,6 +3,7 @@ #include #include #define SATOSHIDEN ((uint64_t)100000000L) +#define issue_curl(cmdstr) bitcoind_RPC(0,"prices",cmdstr,0,0,0,0) /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. @@ -189,7 +190,7 @@ char *send_curl(char *url,char *fname) //long fsize; char curlstr[1024]; //sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); char *retstr; - retstr = issue_curl(url,10); + retstr = issue_curl(url); if ( retstr != 0 ) printf("retstr (%s)\n",retstr); return(retstr); From 82850e26352730bfcbca46c608440764e65b4001 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:19:13 -1100 Subject: [PATCH 342/787] -0 --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index e45222c13..b5eca9c50 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -3,7 +3,7 @@ #include #include #define SATOSHIDEN ((uint64_t)100000000L) -#define issue_curl(cmdstr) bitcoind_RPC(0,"prices",cmdstr,0,0,0,0) +#define issue_curl(cmdstr) bitcoind_RPC(0,"prices",cmdstr,0,0,0) /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. From a0667c2488b6c8bed921501b950623def355259d Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:19:49 -1100 Subject: [PATCH 343/787] Char * --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index b5eca9c50..96b7b72e0 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -3,7 +3,7 @@ #include #include #define SATOSHIDEN ((uint64_t)100000000L) -#define issue_curl(cmdstr) bitcoind_RPC(0,"prices",cmdstr,0,0,0) +#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"prices",cmdstr,0,0,0) /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. From a13b601d779caac72f7f98b579706df11317cece Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:20:17 -1100 Subject: [PATCH 344/787] sleep(10); --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 96b7b72e0..ff98da774 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -54,9 +54,9 @@ void *gamesiterate(struct games_state *rs) if ( rs->guiflag != 0 ) { #ifdef STANDALONE - sleep(10); price = get_btcusd(); fprintf(stderr,"price %llu %.8f\n",(long long)price,(double)price/SATOSHIDEN); + sleep(10); /*if ( (counter++ % 10) == 0 ) doupdate(); c = games_readevent(rs); From ba37ac2c088950769b6e8665e398f2174f14ec66 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:25:43 -1100 Subject: [PATCH 345/787] Encode price --- src/cc/games/prices.c | 98 ++++--------------------------------------- 1 file changed, 7 insertions(+), 91 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index ff98da774..4a95ef99e 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -55,7 +55,9 @@ void *gamesiterate(struct games_state *rs) { #ifdef STANDALONE price = get_btcusd(); - fprintf(stderr,"price %llu %.8f\n",(long long)price,(double)price/SATOSHIDEN); + fprintf(stderr,"%llu -> t%u %.4f\n",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); + issue_games_events(rs,Gametxidstr,eventid,price); + eventid++; sleep(10); /*if ( (counter++ % 10) == 0 ) doupdate(); @@ -105,98 +107,11 @@ void *gamesiterate(struct games_state *rs) #include #include "dapps/dappstd.c" -char *nonportable_path(char *str) -{ - int32_t i; - for (i=0; str[i]!=0; i++) - if ( str[i] == '/' ) - str[i] = '\\'; - return(str); -} - -char *portable_path(char *str) -{ -#ifdef _WIN32 - return(nonportable_path(str)); -#else -#ifdef __PNACL - /*int32_t i,n; - if ( str[0] == '/' ) - return(str); - else - { - n = (int32_t)strlen(str); - for (i=n; i>0; i--) - str[i] = str[i-1]; - str[0] = '/'; - str[n+1] = 0; - }*/ -#endif - return(str); -#endif -} - -void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) -{ - FILE *fp; - long filesize,buflen = *allocsizep; - uint8_t *buf = *bufp; - *lenp = 0; - if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) - { - fseek(fp,0,SEEK_END); - filesize = ftell(fp); - if ( filesize == 0 ) - { - fclose(fp); - *lenp = 0; - //printf("loadfile null size.(%s)\n",fname); - return(0); - } - if ( filesize > buflen ) - { - *allocsizep = filesize; - *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); - } - rewind(fp); - if ( buf == 0 ) - printf("Null buf ???\n"); - else - { - if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) - printf("error reading filesize.%ld\n",(long)filesize); - buf[filesize] = 0; - } - fclose(fp); - *lenp = filesize; - //printf("loaded.(%s)\n",buf); - } //else printf("OS_loadfile couldnt load.(%s)\n",fname); - return(buf); -} - -void *filestr(long *allocsizep,char *_fname) -{ - long filesize = 0; char *fname,*buf = 0; void *retptr; - *allocsizep = 0; - fname = (char *)malloc(strlen(_fname)+1); - strcpy(fname,_fname); - retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); - free(fname); - return(retptr); -} - char *send_curl(char *url,char *fname) { - //long fsize; char curlstr[1024]; - //sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); char *retstr; retstr = issue_curl(url); - if ( retstr != 0 ) - printf("retstr (%s)\n",retstr); return(retstr); - //if ( system(curlstr) != 0 ) - // fprintf(stderr,"error doing system(%s)\n",curlstr); - //return((char *)filestr(&fsize,fname)); } cJSON *get_urljson(char *url,char *fname) @@ -204,7 +119,7 @@ cJSON *get_urljson(char *url,char *fname) char *jsonstr; cJSON *json = 0; if ( (jsonstr= send_curl(url,fname)) != 0 ) { - printf("(%s) -> (%s)\n",url,jsonstr); + //printf("(%s) -> (%s)\n",url,jsonstr); json = cJSON_Parse(jsonstr); free(jsonstr); } @@ -217,17 +132,18 @@ cJSON *get_urljson(char *url,char *fname) uint64_t get_btcusd() { - cJSON *pjson,*bpi,*usd; uint64_t btcusd = 0; + cJSON *pjson,*bpi,*usd; uint64_t x,btcusd = 0; if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json",(char *)"/tmp/oraclefeed.json")) != 0 ) { if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 && (usd= jobj(bpi,(char *)"USD")) != 0 ) { btcusd = jdouble(usd,(char *)"rate_float") * SATOSHIDEN; + x = ((uint64_t)time(NULL) << 32) | (btcusd / 10000); printf("BTC/USD %.4f\n",dstr(btcusd)); } free_json(pjson); } - return(btcusd); + return(x); } char *clonestr(char *str) From adcf1042a1699b1f3980d5806200a421d4b43681 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:27:54 -1100 Subject: [PATCH 346/787] Broadcast --- src/cc/games/prices.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 4a95ef99e..7987c45a0 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -139,7 +139,7 @@ uint64_t get_btcusd() { btcusd = jdouble(usd,(char *)"rate_float") * SATOSHIDEN; x = ((uint64_t)time(NULL) << 32) | (btcusd / 10000); - printf("BTC/USD %.4f\n",dstr(btcusd)); + //printf("BTC/USD %.4f\n",dstr(btcusd)); } free_json(pjson); } @@ -170,7 +170,7 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve if ( fp == 0 ) fp = fopen("events.log","wb"); rs->buffered[rs->num++] = c; - if ( 0 ) + if ( 1 ) { if ( sizeof(c) == 1 ) sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",(uint8_t)c&0xff,gametxidstr,eventid); From 9e878493e9a7a55fea2ca44c019f2629d47b75af Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 00:32:04 -1100 Subject: [PATCH 347/787] +print --- src/cc/games/prices.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index cb2de54a1..82e132c7f 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -59,9 +59,9 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay eventid |= (uint32_t)payload[len+33] << 8; eventid |= (uint32_t)payload[len+34] << 16; eventid |= (uint32_t)payload[len+35] << 24; - //for (i=0; i Date: Wed, 27 Mar 2019 00:50:13 -1100 Subject: [PATCH 348/787] +prints --- src/cc/gamescc.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 56fd13310..dc36cc757 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -553,14 +553,14 @@ void komodo_netevent(std::vector message) { if ( (rand() % 10) == 0 ) { - //fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size()); + fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size()); komodo_sendmessage(2,2,"events",message); } } } - //for (i=0; i Date: Wed, 27 Mar 2019 00:52:39 -1100 Subject: [PATCH 349/787] +print --- src/cc/gamescc.cpp | 4 +++- src/miner.cpp | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index dc36cc757..f04ffb22f 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -477,7 +477,9 @@ int32_t games_event(uint32_t timestamp,uint256 gametxid,int32_t eventid,std::vec games_payloadrecv(mypk,timestamp,payload); komodo_sendmessage(4,8,"events",vopret); return(0); - } else return(-1); + } + fprintf(stderr,"games_eventsign error\n"); + return(-1); } UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) diff --git a/src/miner.cpp b/src/miner.cpp index eaba96ee1..ca4ce8714 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -941,6 +941,7 @@ void komodo_sendmessage(int32_t minpeers,int32_t maxpeers,const char *message,st continue; if ( numsent < minpeers || (rand() % 10) == 0 ) { + fprintf(stderr,"pushmessage\n"); pnode->PushMessage(message,payload); if ( numsent++ > maxpeers ) break; From 2bb01b26340a0e018e1241c5d34e81a2e99f17f8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:26:08 -1100 Subject: [PATCH 350/787] fprintf(stderr,"%llu -> t%u %.4f\n",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); --- src/cc/games/prices.cpp | 5 +++-- src/cc/gamescc.cpp | 6 +++--- src/miner.cpp | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 82e132c7f..4c9698593 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -49,7 +49,7 @@ int32_t disp_gamesplayer(char *str,struct games_player *P) int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) { - uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + uint256 gametxid; int32_t i,len; char str[67]; int64_t price; uint32_t eventid = 0; if ( (len= payload.size()) > 36 ) { len -= 36; @@ -60,7 +60,8 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay eventid |= (uint32_t)payload[len+34] << 16; eventid |= (uint32_t)payload[len+35] << 24; for (i=0; i t%u %.4f ",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid); return(0); } else return(-1); diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index f04ffb22f..17b0d371b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -555,13 +555,13 @@ void komodo_netevent(std::vector message) { if ( (rand() % 10) == 0 ) { - fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size()); + //fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size()); komodo_sendmessage(2,2,"events",message); } } } - for (i=0; iPushMessage(message,payload); if ( numsent++ > maxpeers ) break; From c9446180181a0d093e7af2ef9f319d0c611cc53b Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:28:55 -1100 Subject: [PATCH 351/787] Test --- src/cc/games/prices.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 4c9698593..3f71c786c 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -59,8 +59,8 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay eventid |= (uint32_t)payload[len+33] << 8; eventid |= (uint32_t)payload[len+34] << 16; eventid |= (uint32_t)payload[len+35] << 24; - for (i=0; i t%u %.4f ",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid); return(0); From c3e822b232a59a0255cbc8fc4856b3ffd5ff2197 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:37:10 -1100 Subject: [PATCH 352/787] prices_update --- src/cc/games/prices.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 3f71c786c..b9f9bfc76 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -14,6 +14,12 @@ * * ******************************************************************************/ + +void prices_update(uint32_t timestamp,uint32_t uprice) +{ + fprintf(stderr,"%t%u %.4f ",timstamp,uprice); +} + // game specific code for daemon void games_packitemstr(char *packitemstr,struct games_packitem *item) { @@ -61,8 +67,9 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay eventid |= (uint32_t)payload[len+35] << 24; for (i=0; i t%u %.4f ",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); - fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid); + prices_update((uint32_t)(price >> 32),(uint32_t)(price & 0xffffffff)/10000); + //fprintf(stderr,"%llu -> t%u %.4f ",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); + //fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid); return(0); } else return(-1); } From c428d5b8cb86f09cc4c0b6d074f0707b4d472dfe Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:39:18 -1100 Subject: [PATCH 353/787] Timestamp --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index b9f9bfc76..a103c5f70 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -17,7 +17,7 @@ void prices_update(uint32_t timestamp,uint32_t uprice) { - fprintf(stderr,"%t%u %.4f ",timstamp,uprice); + fprintf(stderr,"%t%u %.4f ",timestamp,uprice); } // game specific code for daemon From 1b86b9a1a36644bb5795dfb0cc7c172b5addfe21 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:39:54 -1100 Subject: [PATCH 354/787] Fix --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index a103c5f70..c57fd5c36 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -17,7 +17,7 @@ void prices_update(uint32_t timestamp,uint32_t uprice) { - fprintf(stderr,"%t%u %.4f ",timestamp,uprice); + fprintf(stderr,"%t%u %.4f\n",timestamp,(double)uprice/1000); } // game specific code for daemon From 837200f4eec248e53c87c9ec7f8e93edd17655bb Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:40:41 -1100 Subject: [PATCH 355/787] -% --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index c57fd5c36..f042b6b1f 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -17,7 +17,7 @@ void prices_update(uint32_t timestamp,uint32_t uprice) { - fprintf(stderr,"%t%u %.4f\n",timestamp,(double)uprice/1000); + fprintf(stderr,"t%u %.4f\n",timestamp,(double)uprice/1000); } // game specific code for daemon From 76bcf303657bec09079e52fa615a985bbd2a28e3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:42:07 -1100 Subject: [PATCH 356/787] -/10000 --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index f042b6b1f..29ca9759e 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -67,7 +67,7 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay eventid |= (uint32_t)payload[len+35] << 24; for (i=0; i> 32),(uint32_t)(price & 0xffffffff)/10000); + prices_update((uint32_t)(price >> 32),(uint32_t)(price & 0xffffffff)); //fprintf(stderr,"%llu -> t%u %.4f ",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); //fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid); return(0); From 1717fb6d87d5203149bfd239fc062a47d11b496b Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:42:56 -1100 Subject: [PATCH 357/787] -print --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 7987c45a0..ed3dae051 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -55,7 +55,7 @@ void *gamesiterate(struct games_state *rs) { #ifdef STANDALONE price = get_btcusd(); - fprintf(stderr,"%llu -> t%u %.4f\n",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); + //fprintf(stderr,"%llu -> t%u %.4f\n",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); issue_games_events(rs,Gametxidstr,eventid,price); eventid++; sleep(10); From c993184197994d899a33073d3ef5b4b51c62168e Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:45:39 -1100 Subject: [PATCH 358/787] 10000 --- src/cc/games/prices.c | 2 +- src/cc/games/prices.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index ed3dae051..67e66bfed 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -138,7 +138,7 @@ uint64_t get_btcusd() if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 && (usd= jobj(bpi,(char *)"USD")) != 0 ) { btcusd = jdouble(usd,(char *)"rate_float") * SATOSHIDEN; - x = ((uint64_t)time(NULL) << 32) | (btcusd / 10000); + x = ((uint64_t)time(NULL) << 32) | ((btcusd / 10000) & 0xffffffff); //printf("BTC/USD %.4f\n",dstr(btcusd)); } free_json(pjson); diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 29ca9759e..7428dfb3b 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -17,7 +17,7 @@ void prices_update(uint32_t timestamp,uint32_t uprice) { - fprintf(stderr,"t%u %.4f\n",timestamp,(double)uprice/1000); + fprintf(stderr,"t%u %.4f\n",timestamp,(double)uprice/10000); } // game specific code for daemon From a2d44907bd3009b333ba75bd37fd60a2b57afc4f Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 01:48:14 -1100 Subject: [PATCH 359/787] Is mine --- src/cc/games/prices.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 7428dfb3b..d3f74a287 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -15,9 +15,9 @@ ******************************************************************************/ -void prices_update(uint32_t timestamp,uint32_t uprice) +void prices_update(uint32_t timestamp,uint32_t uprice,int32_t ismine) { - fprintf(stderr,"t%u %.4f\n",timestamp,(double)uprice/10000); + fprintf(stderr,"%s t%u %.4f\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000); } // game specific code for daemon @@ -67,7 +67,7 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay eventid |= (uint32_t)payload[len+35] << 24; for (i=0; i> 32),(uint32_t)(price & 0xffffffff)); + prices_update((uint32_t)(price >> 32),(uint32_t)(price & 0xffffffff),pk == pubkey2pk(Mypubkey())); //fprintf(stderr,"%llu -> t%u %.4f ",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); //fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid); return(0); From 3d425a23fd790bf83592471e1914ba16cf4ef4c6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 02:07:01 -1100 Subject: [PATCH 360/787] Net_change --- src/cc/games/prices.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 67e66bfed..421fb8ff0 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -4,6 +4,7 @@ #include #define SATOSHIDEN ((uint64_t)100000000L) #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"prices",cmdstr,0,0,0) +extern int64_t Net_change; /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. @@ -59,6 +60,11 @@ void *gamesiterate(struct games_state *rs) issue_games_events(rs,Gametxidstr,eventid,price); eventid++; sleep(10); + switch ( getch() ) + { + case '+': Net_change++; break; + case '-': Net_change--; break; + } /*if ( (counter++ % 10) == 0 ) doupdate(); c = games_readevent(rs); @@ -106,6 +112,7 @@ void *gamesiterate(struct games_state *rs) #ifdef STANDALONE #include #include "dapps/dappstd.c" +int64_t Net_change; char *send_curl(char *url,char *fname) { @@ -132,14 +139,16 @@ cJSON *get_urljson(char *url,char *fname) uint64_t get_btcusd() { - cJSON *pjson,*bpi,*usd; uint64_t x,btcusd = 0; + cJSON *pjson,*bpi,*usd; uint64_t x,newprice,mult,btcusd = 0; if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json",(char *)"/tmp/oraclefeed.json")) != 0 ) { if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 && (usd= jobj(bpi,(char *)"USD")) != 0 ) { btcusd = jdouble(usd,(char *)"rate_float") * SATOSHIDEN; - x = ((uint64_t)time(NULL) << 32) | ((btcusd / 10000) & 0xffffffff); - //printf("BTC/USD %.4f\n",dstr(btcusd)); + mult = SATOSHIDEN + Net_change*100000; + newprice = (btcusd * mult) / SATOSHIDEN; + x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff); + printf("BTC/USD %.4f Net_change %lld * 0.001 -> %.4f\n",dstr(btcusd),(long long)Net_change,dstr(newprice)); } free_json(pjson); } From 9363ef04111d567bf316ee2def799a57ad5edce2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 02:10:14 -1100 Subject: [PATCH 361/787] cursesd --- src/cc/games/prices.c | 6 ++++++ src/cc/games/tetris.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 421fb8ff0..9d3014fdb 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -2,6 +2,12 @@ #include "prices.h" #include #include +#ifdef BUILD_GAMESCC +#include "../rogue/cursesd.h" +#else +#include +#endif + #define SATOSHIDEN ((uint64_t)100000000L) #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"prices",cmdstr,0,0,0) extern int64_t Net_change; diff --git a/src/cc/games/tetris.c b/src/cc/games/tetris.c index 254d324a8..711170b0d 100644 --- a/src/cc/games/tetris.c +++ b/src/cc/games/tetris.c @@ -43,7 +43,7 @@ int32_t tetrisdata(struct games_player *P,void *ptr) #include #ifdef BUILD_GAMESCC -#include "rogue/cursesd.h" +#include "../rogue/cursesd.h" #else #include #endif From f3123a4d5b40a205fff13664dc188c02cddf52cc Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 02:12:25 -1100 Subject: [PATCH 362/787] Test --- src/cc/games/prices.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 9d3014fdb..0856eaa32 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -151,8 +151,8 @@ uint64_t get_btcusd() if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 && (usd= jobj(bpi,(char *)"USD")) != 0 ) { btcusd = jdouble(usd,(char *)"rate_float") * SATOSHIDEN; - mult = SATOSHIDEN + Net_change*100000; - newprice = (btcusd * mult) / SATOSHIDEN; + mult = 10000 + Net_change*10; + newprice = (btcusd * mult) / 10000; x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff); printf("BTC/USD %.4f Net_change %lld * 0.001 -> %.4f\n",dstr(btcusd),(long long)Net_change,dstr(newprice)); } From 3da222eae80b71ed44de24f4f8e1deba536554b7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 02:14:33 -1100 Subject: [PATCH 363/787] Activate curses --- src/cc/games/prices.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 0856eaa32..b0a16a3cc 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -51,6 +51,9 @@ void *gamesiterate(struct games_state *rs) bool running = true; uint32_t eventid = 0; int64_t price; if ( rs->guiflag != 0 || rs->sleeptime != 0 ) { + initscr(); // initialize curses + cbreak(); // pass key presses to program, but not signals + noecho(); // don't echo key presses to screen } while ( running != 0 ) { From 86491b25f022a553a27fdc371fdeb53f95fa7eb8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 02:18:33 -1100 Subject: [PATCH 364/787] Test --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index b0a16a3cc..7d3daef1f 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -157,7 +157,7 @@ uint64_t get_btcusd() mult = 10000 + Net_change*10; newprice = (btcusd * mult) / 10000; x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff); - printf("BTC/USD %.4f Net_change %lld * 0.001 -> %.4f\n",dstr(btcusd),(long long)Net_change,dstr(newprice)); + fprintf(stderr,"BTC/USD %.4f Net_change %lld * 0.001 -> %.4f\n",dstr(btcusd),(long long)Net_change,dstr(newprice)); } free_json(pjson); } From d87dcb4d44d197b5feafebbc85c0a67291650053 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 02:25:14 -1100 Subject: [PATCH 365/787] Display --- src/cc/games/prices.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 7d3daef1f..b6bb6a640 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -148,7 +148,7 @@ cJSON *get_urljson(char *url,char *fname) uint64_t get_btcusd() { - cJSON *pjson,*bpi,*usd; uint64_t x,newprice,mult,btcusd = 0; + cJSON *pjson,*bpi,*usd; char str[512]; uint64_t x,newprice,mult,btcusd = 0; if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json",(char *)"/tmp/oraclefeed.json")) != 0 ) { if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 && (usd= jobj(bpi,(char *)"USD")) != 0 ) @@ -157,7 +157,10 @@ uint64_t get_btcusd() mult = 10000 + Net_change*10; newprice = (btcusd * mult) / 10000; x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff); - fprintf(stderr,"BTC/USD %.4f Net_change %lld * 0.001 -> %.4f\n",dstr(btcusd),(long long)Net_change,dstr(newprice)); + sprintf(str,"BTC/USD %.4f Net_change %lld * 0.001 -> %.4f\n",dstr(btcusd),(long long)Net_change,dstr(newprice)); + mvaddstr(0, 0, str); + clrtoeol(); + refresh(); } free_json(pjson); } From 829e9811647042f87441b4a2ea1c167f070c5c72 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 02:27:58 -1100 Subject: [PATCH 366/787] doupdate --- src/cc/games/prices.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index b6bb6a640..3ac7e76fe 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -68,6 +68,7 @@ void *gamesiterate(struct games_state *rs) //fprintf(stderr,"%llu -> t%u %.4f\n",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); issue_games_events(rs,Gametxidstr,eventid,price); eventid++; + doupdate(); sleep(10); switch ( getch() ) { @@ -160,7 +161,7 @@ uint64_t get_btcusd() sprintf(str,"BTC/USD %.4f Net_change %lld * 0.001 -> %.4f\n",dstr(btcusd),(long long)Net_change,dstr(newprice)); mvaddstr(0, 0, str); clrtoeol(); - refresh(); + doupdate(); } free_json(pjson); } From 6c14369eac9c83af9fab18b9527d1b542728193c Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 02:44:28 -1100 Subject: [PATCH 367/787] timeout(0); --- src/cc/games/prices.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 3ac7e76fe..97c617be7 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -54,6 +54,7 @@ void *gamesiterate(struct games_state *rs) initscr(); // initialize curses cbreak(); // pass key presses to program, but not signals noecho(); // don't echo key presses to screen + timeout(0); } while ( running != 0 ) { From 46bd37f80083ca7fb5ab27e6d87ff56585935d55 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 02:50:42 -1100 Subject: [PATCH 368/787] -print --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index d3f74a287..9db720907 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -17,7 +17,7 @@ void prices_update(uint32_t timestamp,uint32_t uprice,int32_t ismine) { - fprintf(stderr,"%s t%u %.4f\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000); + //fprintf(stderr,"%s t%u %.4f\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000); } // game specific code for daemon From 89913fa6ae498be1d9bbe96dcf86694182a0e579 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 03:46:03 -1100 Subject: [PATCH 369/787] pricedata --- src/cc/games/prices.c | 6 ------ src/cc/games/prices.cpp | 40 +++++++++++++++++++++++++++++++++++++++- src/cc/games/tetris.cpp | 6 ++++++ src/cc/gamescc.cpp | 2 +- src/cc/gamescc.h | 6 +++++- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 97c617be7..3d4a65e14 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -12,12 +12,6 @@ #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"prices",cmdstr,0,0,0) extern int64_t Net_change; -/* - In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. - - also, the standalone game needs to support argv of seed gametxid, along with replay args - */ - int random_tetromino(struct games_state *rs) { rs->seed = _games_rngnext(rs->seed); diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 9db720907..a903355e4 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -14,10 +14,48 @@ * * ******************************************************************************/ +UniValue games_pricedata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t inputsum,price; CPubKey mypk; + if ( params != 0 && cJSON_GetArraySize(params) == 1 ) + { + if ( cclib_parsehash(&price,jitem(params,0),8) < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parsehash")); + } + mypk = pubkey2pk(Mypubkey()); + if ( amount > GAMES_TXFEE ) + { + if ( (inputsum= AddNormalinputs(mtx,mypk,GAMES_TXFEE,64)) >= GAMES_TXFEE ) + { + rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,CScript() << OP_RETURN << price); + return(games_rawtxresult(result,rawtx,1)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","amount too small")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parse")); + } + return(result); +} void prices_update(uint32_t timestamp,uint32_t uprice,int32_t ismine) { - //fprintf(stderr,"%s t%u %.4f\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000); + fprintf(stderr,"%s t%u %.4f %16llx\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000,(long long)(timestamp<<32) | uprice); } // game specific code for daemon diff --git a/src/cc/games/tetris.cpp b/src/cc/games/tetris.cpp index f1b6416ba..12c443e1e 100644 --- a/src/cc/games/tetris.cpp +++ b/src/cc/games/tetris.cpp @@ -66,6 +66,12 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay } else return(-1); } +UniValue games_oracledata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result; + return(result); +} + bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) { return(true); diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 17b0d371b..77b463822 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1731,7 +1731,7 @@ UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,opret); - return(games_rawtxresult(result,rawtx,1)); + return(games_rawtxresult(resulft,rawtx,1)); } else { diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 63d029b0a..19f3f66cf 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -46,6 +46,7 @@ std::string Games_pname; { (char *)MYCCNAME, (char *)"highlander", (char *)"gametxid", 1, 1, 'H', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"events", (char *)"eventshex [gametxid [eventid]]", 1, 3, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"pricedata", (char *)"hexstr", 1, 1, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"register", (char *)"gametxid [playertxid]", 1, 2, 'R', EVAL_GAMES }, bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); @@ -65,6 +66,7 @@ UniValue games_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *param UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_pricedata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #define CUSTOM_DISPATCH \ if ( cp->evalcode == EVAL_GAMES ) \ @@ -100,7 +102,9 @@ if ( cp->evalcode == EVAL_GAMES ) \ else if ( strcmp(method,"highlander") == 0 ) \ return(games_highlander(txfee,cp,params)); \ else if ( strcmp(method,"fund") == 0 ) \ - return(games_fund(txfee,cp,params)); \ + return(games_fund(txfee,cp,params)); \ + else if ( strcmp(method,"pricedata") == 0 ) \ + return(games_pricedata(txfee,cp,params)); \ else \ { \ result.push_back(Pair("result","error")); \ From 9e4f5663da07dcfcafbdf12b172da00263bc385c Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 03:49:23 -1100 Subject: [PATCH 370/787] Test --- src/cc/games/prices.cpp | 22 ++++++++-------------- src/cc/gamescc.cpp | 2 +- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index a903355e4..88fdab88a 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -14,35 +14,29 @@ * * ******************************************************************************/ +UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); + UniValue games_pricedata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); std::string rawtx; int64_t inputsum,price; CPubKey mypk; if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { - if ( cclib_parsehash(&price,jitem(params,0),8) < 0 ) + if ( cclib_parsehash((uint8_t *)&price,jitem(params,0),8) < 0 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt parsehash")); } mypk = pubkey2pk(Mypubkey()); - if ( amount > GAMES_TXFEE ) + if ( (inputsum= AddNormalinputs(mtx,mypk,GAMES_TXFEE,64)) >= GAMES_TXFEE ) { - if ( (inputsum= AddNormalinputs(mtx,mypk,GAMES_TXFEE,64)) >= GAMES_TXFEE ) - { - rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,CScript() << OP_RETURN << price); - return(games_rawtxresult(result,rawtx,1)); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","not enough funds")); - } + rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,CScript() << OP_RETURN << price); + return(games_rawtxresult(result,rawtx,1)); } else { result.push_back(Pair("result","error")); - result.push_back(Pair("error","amount too small")); + result.push_back(Pair("error","not enough funds")); } } else @@ -55,7 +49,7 @@ UniValue games_pricedata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params void prices_update(uint32_t timestamp,uint32_t uprice,int32_t ismine) { - fprintf(stderr,"%s t%u %.4f %16llx\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000,(long long)(timestamp<<32) | uprice); + fprintf(stderr,"%s t%u %.4f %16llx\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000,(long long)((uint64_t)timestamp<<32) | uprice); } // game specific code for daemon diff --git a/src/cc/gamescc.cpp b/src/cc/gamescc.cpp index 77b463822..17b0d371b 100644 --- a/src/cc/gamescc.cpp +++ b/src/cc/gamescc.cpp @@ -1731,7 +1731,7 @@ UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,opret); - return(games_rawtxresult(resulft,rawtx,1)); + return(games_rawtxresult(result,rawtx,1)); } else { From c0798791e9d99b235d4efcf493a18bf9ee064d00 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:05:10 -1100 Subject: [PATCH 371/787] Bet --- src/cc/games/prices.cpp | 11 +++++++---- src/cc/gamescc.h | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 88fdab88a..03fcff33a 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -19,17 +19,20 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastf UniValue games_pricedata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx; int64_t inputsum,price; CPubKey mypk; - if ( params != 0 && cJSON_GetArraySize(params) == 1 ) + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum,price; CPubKey mypk; + if ( params != 0 && cJSON_GetArraySize(params) == 2 ) { - if ( cclib_parsehash((uint8_t *)&price,jitem(params,0),8) < 0 ) + amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049; + if ( cclib_parsehash((uint8_t *)&price,jitem(params,1),8) < 0 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt parsehash")); } mypk = pubkey2pk(Mypubkey()); - if ( (inputsum= AddNormalinputs(mtx,mypk,GAMES_TXFEE,64)) >= GAMES_TXFEE ) + gamespk = GetUnspendable(cp,0); + if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,CScript() << OP_RETURN << price); return(games_rawtxresult(result,rawtx,1)); } diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 19f3f66cf..bfaf036d0 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -46,7 +46,7 @@ std::string Games_pname; { (char *)MYCCNAME, (char *)"highlander", (char *)"gametxid", 1, 1, 'H', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"events", (char *)"eventshex [gametxid [eventid]]", 1, 3, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, ' ', EVAL_GAMES }, \ - { (char *)MYCCNAME, (char *)"pricedata", (char *)"hexstr", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"bet", (char *)"amount hexstr", 2, 2, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"register", (char *)"gametxid [playertxid]", 1, 2, 'R', EVAL_GAMES }, bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); @@ -66,7 +66,7 @@ UniValue games_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *param UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); -UniValue games_pricedata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #define CUSTOM_DISPATCH \ if ( cp->evalcode == EVAL_GAMES ) \ @@ -103,8 +103,8 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_highlander(txfee,cp,params)); \ else if ( strcmp(method,"fund") == 0 ) \ return(games_fund(txfee,cp,params)); \ - else if ( strcmp(method,"pricedata") == 0 ) \ - return(games_pricedata(txfee,cp,params)); \ + else if ( strcmp(method,"bet") == 0 ) \ + return(games_bet(txfee,cp,params)); \ else \ { \ result.push_back(Pair("result","error")); \ From 29fc5b9ea069e79f0477619c26fb587e79fa3caa Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:05:50 -1100 Subject: [PATCH 372/787] Gamespk --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 03fcff33a..27d41ca9b 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -19,7 +19,7 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastf UniValue games_pricedata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum,price; CPubKey mypk; + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum,price; CPubKey gamespk,mypk; if ( params != 0 && cJSON_GetArraySize(params) == 2 ) { amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049; From 8e4a613eb06e25db6e32d97edb67df763b7d36c3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:06:39 -1100 Subject: [PATCH 373/787] games_bet --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 27d41ca9b..e099d57af 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -16,7 +16,7 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); -UniValue games_pricedata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum,price; CPubKey gamespk,mypk; From 7c1c706f18a7062bae09d59df1754e8afae85a77 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:10:39 -1100 Subject: [PATCH 374/787] Test --- src/cc/games/prices.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index e099d57af..296d80a8c 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -28,6 +28,7 @@ UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt parsehash")); } + fprintf(stderr,"amount %llu price %llx\n",(long long)amount,(long long)price); mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) From 3de3e931bb5afee45f9cd1369676d3cbb6a9c648 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:12:41 -1100 Subject: [PATCH 375/787] Test --- src/cc/gamescc.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index bfaf036d0..52627cd1c 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -109,7 +109,6 @@ if ( cp->evalcode == EVAL_GAMES ) \ { \ result.push_back(Pair("result","error")); \ result.push_back(Pair("error","invalid gamescc method")); \ - result.push_back(Pair("method",method)); \ return(result); \ } \ } From 2bf9af5f57333ddfb49df056d89e5a2dd8949dfc Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:13:19 -1100 Subject: [PATCH 376/787] Test --- src/cc/gamescc.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 52627cd1c..af888ed05 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -71,6 +71,7 @@ UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #define CUSTOM_DISPATCH \ if ( cp->evalcode == EVAL_GAMES ) \ { \ + UniValue res; \ if ( strcmp(method,"rng") == 0 ) \ return(games_rng(txfee,cp,params)); \ else if ( strcmp(method,"rngnext") == 0 ) \ @@ -107,9 +108,9 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_bet(txfee,cp,params)); \ else \ { \ - result.push_back(Pair("result","error")); \ - result.push_back(Pair("error","invalid gamescc method")); \ - return(result); \ + res.push_back(Pair("result","error")); \ + res.push_back(Pair("error","invalid gamescc method")); \ + return(res); \ } \ } #endif From 2660c2b7f8d43f10c13ea003e73505258c8fc4de Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:50:17 -1100 Subject: [PATCH 377/787] issue_bet --- src/cc/games/prices.c | 34 ++++++++++++++++++++++++++++++---- src/cc/gamescc.h | 7 +++---- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 3d4a65e14..52ddc477c 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -10,7 +10,7 @@ #define SATOSHIDEN ((uint64_t)100000000L) #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"prices",cmdstr,0,0,0) -extern int64_t Net_change; +extern int64_t Net_change,Betsize; int random_tetromino(struct games_state *rs) { @@ -39,6 +39,7 @@ struct games_state globalR; extern char Gametxidstr[]; int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); uint64_t get_btcusd(); +int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize); void *gamesiterate(struct games_state *rs) { @@ -61,7 +62,8 @@ void *gamesiterate(struct games_state *rs) #ifdef STANDALONE price = get_btcusd(); //fprintf(stderr,"%llu -> t%u %.4f\n",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); - issue_games_events(rs,Gametxidstr,eventid,price); + //issue_games_events(rs,Gametxidstr,eventid,price); + issue_bet(rs,price,Betsize); eventid++; doupdate(); sleep(10); @@ -69,6 +71,10 @@ void *gamesiterate(struct games_state *rs) { case '+': Net_change++; break; case '-': Net_change--; break; + case '0': Net_change = 0; break; + case '$': Betsize = SATOSHIDEN; break; + case '^': Betsize += (Betsize >> 3); break; + case '/': Betsize -= (Betsize >> 3); break; } /*if ( (counter++ % 10) == 0 ) doupdate(); @@ -117,7 +123,7 @@ void *gamesiterate(struct games_state *rs) #ifdef STANDALONE #include #include "dapps/dappstd.c" -int64_t Net_change; +int64_t Net_change,Betsize; char *send_curl(char *url,char *fname) { @@ -153,7 +159,7 @@ uint64_t get_btcusd() mult = 10000 + Net_change*10; newprice = (btcusd * mult) / 10000; x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff); - sprintf(str,"BTC/USD %.4f Net_change %lld * 0.001 -> %.4f\n",dstr(btcusd),(long long)Net_change,dstr(newprice)); + sprintf(str,"BTC/USD %.4f Net_change %lld * 0.001 -> Betsize %.8f %.4f\n",dstr(btcusd),(long long)Net_change,dstr(Betsize),dstr(newprice)); mvaddstr(0, 0, str); clrtoeol(); doupdate(); @@ -218,6 +224,26 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve } else return(0); } +int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize) +{ + char params[512],hexstr[64],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + sprintf(params,"[\"bet\",\"17\",\"[%.8f,%%22%s%%22]\"]",dstr(x),hexstr); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + fprintf(stderr,"%s\n",jprint(resobj,0)); + } + free_json(retjson); + } + free(retstr); + } + return(retval); +} + int prices(int argc, char **argv) { struct games_state *rs = &globalR; diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index af888ed05..52627cd1c 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -71,7 +71,6 @@ UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #define CUSTOM_DISPATCH \ if ( cp->evalcode == EVAL_GAMES ) \ { \ - UniValue res; \ if ( strcmp(method,"rng") == 0 ) \ return(games_rng(txfee,cp,params)); \ else if ( strcmp(method,"rngnext") == 0 ) \ @@ -108,9 +107,9 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_bet(txfee,cp,params)); \ else \ { \ - res.push_back(Pair("result","error")); \ - res.push_back(Pair("error","invalid gamescc method")); \ - return(res); \ + result.push_back(Pair("result","error")); \ + result.push_back(Pair("error","invalid gamescc method")); \ + return(result); \ } \ } #endif From 72922243f989904eb2cbe97bc60dce2d2bf52d0b Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:53:38 -1100 Subject: [PATCH 378/787] Fix --- src/cc/games/prices.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 52ddc477c..93c89ab83 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -227,7 +227,13 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize) { char params[512],hexstr[64],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; - sprintf(params,"[\"bet\",\"17\",\"[%.8f,%%22%s%%22]\"]",dstr(x),hexstr); + memset(hexstr,0,sizeof(hexstr)); + for (i=0; i<8; i++) + { + sprintf(&hexstr[i<<1],"%02x",x & 0xff); + x >>= 8; + } + sprintf(params,"[\"bet\",\"17\",\"[%.8f,%%22%s%%22]\"]",dstr(betsize),hexstr); if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) From 251d6fb88f962c9b4fbaf165b42312d4138f778e Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:54:29 -1100 Subject: [PATCH 379/787] I --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 93c89ab83..13b0c4934 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -226,7 +226,7 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize) { - char params[512],hexstr[64],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + char params[512],hexstr[64],*retstr; cJSON *retjson,*resobj; int32_t i,retval = -1; memset(hexstr,0,sizeof(hexstr)); for (i=0; i<8; i++) { From 05b91ae06385927e506d040f330a5148057e2d79 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 04:55:34 -1100 Subject: [PATCH 380/787] Test --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 13b0c4934..f33838c64 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -230,7 +230,7 @@ int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize) memset(hexstr,0,sizeof(hexstr)); for (i=0; i<8; i++) { - sprintf(&hexstr[i<<1],"%02x",x & 0xff); + sprintf(&hexstr[i<<1],"%02x",(uint8_t)(x & 0xff)); x >>= 8; } sprintf(params,"[\"bet\",\"17\",\"[%.8f,%%22%s%%22]\"]",dstr(betsize),hexstr); From fb132238f0f0fcece0e0d657700852aa0edee441 Mon Sep 17 00:00:00 2001 From: "Anton \"TonyL\" Lysakov" Date: Wed, 27 Mar 2019 23:36:58 +0700 Subject: [PATCH 381/787] Create cryptoconditions.py Forgot that we using it as a "library" for other CC tests. So deletion was a mistake. --- qa/rpc-tests/cryptoconditions.py | 690 +++++++++++++++++++++++++++++++ 1 file changed, 690 insertions(+) create mode 100644 qa/rpc-tests/cryptoconditions.py diff --git a/qa/rpc-tests/cryptoconditions.py b/qa/rpc-tests/cryptoconditions.py new file mode 100644 index 000000000..d5456e801 --- /dev/null +++ b/qa/rpc-tests/cryptoconditions.py @@ -0,0 +1,690 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 SuperNET developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ + stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises + +import time +from decimal import Decimal +from random import choice +from string import ascii_uppercase + +def assert_success(result): + assert_equal(result['result'], 'success') + +def assert_error(result): + assert_equal(result['result'], 'error') + +def generate_random_string(length): + random_string = ''.join(choice(ascii_uppercase) for i in range(length)) + return random_string + +class CryptoConditionsTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing CC test directory "+self.options.tmpdir) + self.num_nodes = 2 + initialize_chain_clean(self.options.tmpdir, self.num_nodes) + + def setup_network(self, split = False): + print("Setting up network...") + self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" + self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" + self.privkey = "UqMgxk7ySPNQ4r9nKAFPjkXy6r5t898yhuNCjSZJLg3RAM4WW1m9" + self.addr1 = "RXEXoa1nRmKhMbuZovpcYwQMsicwzccZBp" + self.pubkey1 = "024026d4ad4ecfc1f705a9b42ca64af6d2ad947509c085534a30b8861d756c6ff0" + self.privkey1 = "UtdydP56pGTFmawHzHr1wDrc4oUwCNW1ttX8Pc3KrvH3MA8P49Wi" + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, + extra_args=[[ + # always give -ac_name as first extra_arg and port as third + '-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node0/REGTEST.conf', + '-port=64367', + '-rpcport=64368', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt' + ], + ['-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node1/REGTEST.conf', + '-port=64365', + '-rpcport=64366', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey1, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '-addnode=127.0.0.1:64367', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt']] + ) + self.is_network_split = split + self.rpc = self.nodes[0] + self.rpc1 = self.nodes[1] + self.sync_all() + print("Done setting up network") + + def send_and_mine(self, xtn, rpc_connection): + txid = rpc_connection.sendrawtransaction(xtn) + assert txid, 'got txid' + # we need the tx above to be confirmed in the next block + rpc_connection.generate(1) + return txid + + def run_faucet_tests(self): + rpc = self.rpc + rpc1 = self.rpc1 + + # basic sanity tests + result = rpc.getwalletinfo() + assert_greater_than(result['txcount'], 100) + assert_greater_than(result['balance'], 0.0) + balance = result['balance'] + + faucet = rpc.faucetaddress() + assert_equal(faucet['result'], 'success') + # verify all keys look like valid AC addrs, could be better + for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: + assert_equal(faucet[x][0], 'R') + + result = rpc.faucetaddress(self.pubkey) + assert_success(result) + # test that additional CCaddress key is returned + for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: + assert_equal(result[x][0], 'R') + + # no funds in the faucet yet + result = rpc.faucetget() + assert_error(result) + + result = rpc.faucetinfo() + assert_success(result) + + result = rpc.faucetfund("0") + assert_error(result) + + result = rpc.faucetfund("-1") + assert_error(result) + + # we need at least 1 + txfee to get + result = rpc.faucetfund("2") + assert_success(result) + assert result['hex'], "hex key found" + + # broadcast the xtn + result = rpc.sendrawtransaction(result['hex']) + txid = result[0] + assert txid, "found txid" + + # we need the tx above to be confirmed in the next block + rpc.generate(1) + self.sync_all() + + result = rpc.getwalletinfo() + # minus one block reward + balance2 = result['balance'] - 100000 + # make sure our balance is less now + assert_greater_than(balance, balance2) + + result = rpc.faucetinfo() + assert_success(result) + assert_greater_than( result['funding'], 0 ) + + # claiming faucet on second node + faucetgethex = rpc1.faucetget() + assert_success(faucetgethex) + assert faucetgethex['hex'], "hex key found" + + balance1 = rpc1.getwalletinfo()['balance'] + + # try to broadcast the faucetget transaction + result = self.send_and_mine(faucetgethex['hex'], rpc1) + assert txid, "transaction broadcasted" + + balance2 = rpc1.getwalletinfo()['balance'] + assert_greater_than(balance2, balance1) + + self.sync_all() + + def run_dice_tests(self): + rpc = self.nodes[0] + rpc1 = self.nodes[1] + self.sync_all() + + # have to generate few blocks on second node to be able to place bets + rpc1.generate(10) + result = rpc1.getbalance() + assert_greater_than(result, 100000) + + dice = rpc.diceaddress() + assert_equal(dice['result'], 'success') + for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: + assert_equal(dice[x][0], 'R') + + dice = rpc.diceaddress(self.pubkey) + assert_equal(dice['result'], 'success') + for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: + assert_equal(dice[x][0], 'R') + + # no dice created yet + result = rpc.dicelist() + assert_equal(result, []) + + # creating dice plan with too long name (>8 chars) + result = rpc.dicefund("THISISTOOLONG", "10000", "10", "10000", "10", "5") + assert_error(result) + + # creating dice plan with < 100 funding + result = rpc.dicefund("LUCKY","10","1","10000","10","5") + assert_error(result) + + # creating dice plan with 0 blocks timeout + result = rpc.dicefund("LUCKY","10","1","10000","10","0") + assert_error(result) + + # creating dice plan + dicefundtx = rpc.dicefund("LUCKY","1000","1","800","10","5") + diceid = self.send_and_mine(dicefundtx['hex'], rpc) + + # checking if it in plans list now + result = rpc.dicelist() + assert_equal(result[0], diceid) + + # set dice name for futher usage + dicename = "LUCKY" + + # adding zero funds to plan + result = rpc.diceaddfunds(dicename,diceid,"0") + assert_error(result) + + # adding negative funds to plan + result = rpc.diceaddfunds(dicename,diceid,"-1") + assert_error(result) + + # adding funds to plan + addfundstx = rpc.diceaddfunds(dicename,diceid,"1100") + result = self.send_and_mine(addfundstx['hex'], rpc) + + # checking if funds added to plan + result = rpc.diceinfo(diceid) + assert_equal(result["funding"], "2100.00000000") + + # not valid dice info checking + result = rpc.diceinfo("invalid") + assert_error(result) + + # placing 0 amount bet + result = rpc1.dicebet(dicename,diceid,"0","2") + assert_error(result) + + # placing negative amount bet + result = rpc1.dicebet(dicename,diceid,"-1","2") + assert_error(result) + + # placing bet more than maxbet + result = rpc1.dicebet(dicename,diceid,"900","2") + assert_error(result) + + # placing bet with amount more than funding + result = rpc1.dicebet(dicename,diceid,"3000","2") + assert_error(result) + + # placing bet with potential won more than funding + result = rpc1.dicebet(dicename,diceid,"750","9") + assert_error(result) + + # placing 0 odds bet + result = rpc1.dicebet(dicename,diceid,"1","0") + assert_error(result) + + # placing negative odds bet + result = rpc1.dicebet(dicename,diceid,"1","-1") + assert_error(result) + + # placing bet with odds more than allowed + result = rpc1.dicebet(dicename,diceid,"1","11") + assert_error(result) + + # placing bet with not correct dice name + result = rpc1.dicebet("nope",diceid,"100","2") + assert_error(result) + + # placing bet with not correct dice id + result = rpc1.dicebet(dicename,self.pubkey,"100","2") + assert_error(result) + + # have to make some entropy for the next test + entropytx = 0 + fundingsum = 1 + while entropytx < 110: + fundingsuminput = str(fundingsum) + fundinghex = rpc.diceaddfunds(dicename,diceid,fundingsuminput) + result = self.send_and_mine(fundinghex['hex'], rpc) + entropytx = entropytx + 1 + fundingsum = fundingsum + 1 + + rpc.generate(2) + self.sync_all() + + # valid bet placing + placebet = rpc1.dicebet(dicename,diceid,"100","2") + betid = self.send_and_mine(placebet["hex"], rpc1) + assert result, "bet placed" + + # check bet status + result = rpc1.dicestatus(dicename,diceid,betid) + assert_success(result) + + # note initial dice funding state at this point. + # TODO: track player balance somehow (hard to do because of mining and fees) + diceinfo = rpc.diceinfo(diceid) + funding = float(diceinfo['funding']) + + # # placing same amount bets with amount 1 and odds 1:3, checking if balance changed correct + # losscounter = 0 + # wincounter = 0 + # betcounter = 0 + # + # while (betcounter < 10): + # placebet = rpc1.dicebet(dicename,diceid,"1","2") + # betid = self.send_and_mine(placebet["hex"], rpc1) + # time.sleep(3) + # self.sync_all() + # finish = rpc.dicefinish(dicename,diceid,betid) + # self.send_and_mine(finish["hex"], rpc1) + # self.sync_all() + # time.sleep(3) + # betresult = rpc1.dicestatus(dicename,diceid,betid) + # betcounter = betcounter + 1 + # if betresult["status"] == "loss": + # losscounter = losscounter + 1 + # elif betresult["status"] == "win": + # wincounter = wincounter + 1 + # else: + # pass + # + # # funding balance should increase if player loss, decrease if player won + # fundbalanceguess = funding + losscounter - wincounter * 2 + # fundinfoactual = rpc.diceinfo(diceid) + # assert_equal(round(fundbalanceguess),round(float(fundinfoactual['funding']))) + + def run_token_tests(self): + rpc = self.nodes[0] + result = rpc.tokenaddress() + assert_success(result) + for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: + assert_equal(result[x][0], 'R') + + result = rpc.tokenaddress(self.pubkey) + assert_success(result) + for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: + assert_equal(result[x][0], 'R') + # there are no tokens created yet + result = rpc.tokenlist() + assert_equal(result, []) + + # trying to create token with negaive supply + result = rpc.tokencreate("NUKE", "-1987420", "no bueno supply") + assert_error(result) + + # creating token with name more than 32 chars + result = rpc.tokencreate("NUKE123456789012345678901234567890", "1987420", "name too long") + assert_error(result) + + # creating valid token + result = rpc.tokencreate("DUKE", "1987.420", "Duke's custom token") + assert_success(result) + + tokenid = self.send_and_mine(result['hex'], rpc) + + result = rpc.tokenlist() + assert_equal(result[0], tokenid) + + # get token balance for token with pubkey + result = rpc.tokenbalance(tokenid, self.pubkey) + assert_success(result) + assert_equal(result['balance'], 198742000000) + assert_equal(result['tokenid'], tokenid) + + # get token balance for token without pubkey + result = rpc.tokenbalance(tokenid) + assert_success(result) + assert_equal(result['balance'], 198742000000) + assert_equal(result['tokenid'], tokenid) + + # this is not a valid assetid + result = rpc.tokeninfo(self.pubkey) + assert_error(result) + + # check tokeninfo for valid token + result = rpc.tokeninfo(tokenid) + assert_success(result) + assert_equal(result['tokenid'], tokenid) + assert_equal(result['owner'], self.pubkey) + assert_equal(result['name'], "DUKE") + assert_equal(result['supply'], 198742000000) + assert_equal(result['description'], "Duke's custom token") + + # invalid numtokens ask + result = rpc.tokenask("-1", tokenid, "1") + assert_error(result) + + # invalid numtokens ask + result = rpc.tokenask("0", tokenid, "1") + assert_error(result) + + # invalid price ask + result = rpc.tokenask("1", tokenid, "-1") + assert_error(result) + + # invalid price ask + result = rpc.tokenask("1", tokenid, "0") + assert_error(result) + + # invalid tokenid ask + result = rpc.tokenask("100", "deadbeef", "1") + assert_error(result) + + # valid ask + tokenask = rpc.tokenask("100", tokenid, "7.77") + tokenaskhex = tokenask['hex'] + tokenaskid = self.send_and_mine(tokenask['hex'], rpc) + result = rpc.tokenorders(tokenid) + order = result[0] + assert order, "found order" + + # invalid ask fillunits + result = rpc.tokenfillask(tokenid, tokenaskid, "0") + assert_error(result) + + # invalid ask fillunits + result = rpc.tokenfillask(tokenid, tokenaskid, "-777") + assert_error(result) + + # valid ask fillunits + fillask = rpc.tokenfillask(tokenid, tokenaskid, "777") + result = self.send_and_mine(fillask['hex'], rpc) + txid = result[0] + assert txid, "found txid" + + # should be no token orders + result = rpc.tokenorders(tokenid) + assert_equal(result, []) + + # checking ask cancellation + testorder = rpc.tokenask("100", tokenid, "7.77") + testorderid = self.send_and_mine(testorder['hex'], rpc) + cancel = rpc.tokencancelask(tokenid, testorderid) + self.send_and_mine(cancel["hex"], rpc) + result = rpc.tokenorders(tokenid) + assert_equal(result, []) + + # invalid numtokens bid + result = rpc.tokenbid("-1", tokenid, "1") + assert_error(result) + + # invalid numtokens bid + result = rpc.tokenbid("0", tokenid, "1") + assert_error(result) + + # invalid price bid + result = rpc.tokenbid("1", tokenid, "-1") + assert_error(result) + + # invalid price bid + result = rpc.tokenbid("1", tokenid, "0") + assert_error(result) + + # invalid tokenid bid + result = rpc.tokenbid("100", "deadbeef", "1") + assert_error(result) + + tokenbid = rpc.tokenbid("100", tokenid, "10") + tokenbidhex = tokenbid['hex'] + tokenbidid = self.send_and_mine(tokenbid['hex'], rpc) + result = rpc.tokenorders(tokenid) + order = result[0] + assert order, "found order" + + # invalid bid fillunits + result = rpc.tokenfillbid(tokenid, tokenbidid, "0") + assert_error(result) + + # invalid bid fillunits + result = rpc.tokenfillbid(tokenid, tokenbidid, "-777") + assert_error(result) + + # valid bid fillunits + fillbid = rpc.tokenfillbid(tokenid, tokenbidid, "1000") + result = self.send_and_mine(fillbid['hex'], rpc) + txid = result[0] + assert txid, "found txid" + + # should be no token orders + result = rpc.tokenorders(tokenid) + assert_equal(result, []) + + # checking bid cancellation + testorder = rpc.tokenbid("100", tokenid, "7.77") + testorderid = self.send_and_mine(testorder['hex'], rpc) + cancel = rpc.tokencancelbid(tokenid, testorderid) + self.send_and_mine(cancel["hex"], rpc) + result = rpc.tokenorders(tokenid) + assert_equal(result, []) + + # invalid token transfer amount (have to add status to CC code!) + randompubkey = "021a559101e355c907d9c553671044d619769a6e71d624f68bfec7d0afa6bd6a96" + result = rpc.tokentransfer(tokenid,randompubkey,"0") + assert_error(result) + + # invalid token transfer amount (have to add status to CC code!) + result = rpc.tokentransfer(tokenid,randompubkey,"-1") + assert_error(result) + + # valid token transfer + sendtokens = rpc.tokentransfer(tokenid,randompubkey,"1") + self.send_and_mine(sendtokens["hex"], rpc) + result = rpc.tokenbalance(tokenid,randompubkey) + assert_equal(result["balance"], 1) + + def run_rewards_tests(self): + rpc = self.nodes[0] + result = rpc.rewardsaddress() + for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: + assert_equal(result[x][0], 'R') + + result = rpc.rewardsaddress(self.pubkey) + for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: + assert_equal(result[x][0], 'R') + + # no rewards yet + result = rpc.rewardslist() + assert_equal(result, []) + + # looking up non-existent reward should return error + result = rpc.rewardsinfo("none") + assert_error(result) + + # creating rewards plan with name > 8 chars, should return error + result = rpc.rewardscreatefunding("STUFFSTUFF", "7777", "25", "0", "10", "10") + assert_error(result) + + # creating rewards plan with 0 funding + result = rpc.rewardscreatefunding("STUFF", "0", "25", "0", "10", "10") + assert_error(result) + + # creating rewards plan with 0 maxdays + result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "0") + assert_error(result) + + # creating rewards plan with > 25% APR + result = rpc.rewardscreatefunding("STUFF", "7777", "30", "0", "10", "10") + assert_error(result) + + # creating valid rewards plan + result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") + assert result['hex'], 'got raw xtn' + fundingtxid = rpc.sendrawtransaction(result['hex']) + assert fundingtxid, 'got txid' + + # confirm the above xtn + rpc.generate(1) + result = rpc.rewardsinfo(fundingtxid) + assert_success(result) + assert_equal(result['name'], 'STUFF') + assert_equal(result['APR'], "25.00000000") + assert_equal(result['minseconds'], 0) + assert_equal(result['maxseconds'], 864000) + assert_equal(result['funding'], "7777.00000000") + assert_equal(result['mindeposit'], "10.00000000") + assert_equal(result['fundingtxid'], fundingtxid) + + # checking if new plan in rewardslist + result = rpc.rewardslist() + assert_equal(result[0], fundingtxid) + + # creating reward plan with already existing name, should return error + result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") + assert_error(result) + + # add funding amount must be positive + result = rpc.rewardsaddfunding("STUFF", fundingtxid, "-1") + assert_error(result) + + # add funding amount must be positive + result = rpc.rewardsaddfunding("STUFF", fundingtxid, "0") + assert_error(result) + + # adding valid funding + result = rpc.rewardsaddfunding("STUFF", fundingtxid, "555") + addfundingtxid = self.send_and_mine(result['hex'], rpc) + assert addfundingtxid, 'got funding txid' + + # checking if funding added to rewardsplan + result = rpc.rewardsinfo(fundingtxid) + assert_equal(result['funding'], "8332.00000000") + + # trying to lock funds, locking funds amount must be positive + result = rpc.rewardslock("STUFF", fundingtxid, "-5") + assert_error(result) + + # trying to lock funds, locking funds amount must be positive + result = rpc.rewardslock("STUFF", fundingtxid, "0") + assert_error(result) + + # trying to lock less than the min amount is an error + result = rpc.rewardslock("STUFF", fundingtxid, "7") + assert_error(result) + + # locking funds in rewards plan + result = rpc.rewardslock("STUFF", fundingtxid, "10") + assert_success(result) + locktxid = result['hex'] + assert locktxid, "got lock txid" + + # locktxid has not been broadcast yet + result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) + assert_error(result) + + # broadcast xtn + txid = rpc.sendrawtransaction(locktxid) + assert txid, 'got txid from sendrawtransaction' + + # confirm the xtn above + rpc.generate(1) + + # will not unlock since reward amount is less than tx fee + result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) + assert_error(result) + + def run_oracles_tests(self): + rpc = self.nodes[0] + rpc1 = self.nodes[1] + + result = rpc1.oraclesaddress() + + result = rpc.oraclesaddress() + assert_success(result) + + for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: + assert_equal(result[x][0], 'R') + + result = rpc.oraclesaddress(self.pubkey) + assert_success(result) + for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: + assert_equal(result[x][0], 'R') + + # there are no oracles created yet + result = rpc.oracleslist() + assert_equal(result, []) + + # looking up non-existent oracle should return error. + result = rpc.oraclesinfo("none") + assert_error(result) + + # attempt to create oracle with not valid data type should return error + result = rpc.oraclescreate("Test", "Test", "Test") + assert_error(result) + + # attempt to create oracle with description > 32 symbols should return error + too_long_name = generate_random_string(33) + result = rpc.oraclescreate(too_long_name, "Test", "s") + + + # attempt to create oracle with description > 4096 symbols should return error + too_long_description = generate_random_string(4100) + result = rpc.oraclescreate("Test", too_long_description, "s") + assert_error(result) + # # valid creating oracles of different types + # # using such naming to re-use it for data publishing / reading (e.g. oracle_s for s type) + # valid_formats = ["s", "S", "d", "D", "c", "C", "t", "T", "i", "I", "l", "L", "h", "Ihh"] + # for f in valid_formats: + # result = rpc.oraclescreate("Test", "Test", f) + # assert_success(result) + # globals()["oracle_{}".format(f)] = self.send_and_mine(result['hex'], rpc) + + def run_test (self): + print("Mining blocks...") + rpc = self.nodes[0] + rpc1 = self.nodes[1] + # utxos from block 1 become mature in block 101 + rpc.generate(101) + self.sync_all() + rpc.getinfo() + rpc1.getinfo() + # this corresponds to -pubkey above + print("Importing privkeys") + rpc.importprivkey(self.privkey) + rpc1.importprivkey(self.privkey1) + self.run_faucet_tests() + self.sync_all() + self.run_rewards_tests() + self.sync_all() + self.run_dice_tests() + self.sync_all() + self.run_token_tests() + self.sync_all() + self.run_oracles_tests() + + +if __name__ == '__main__': + CryptoConditionsTest ().main() From dbb8f0acc20634c44c6b5378f5ef5d35b349a9b8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 06:02:13 -1100 Subject: [PATCH 382/787] Test --- src/cc/games/prices.c | 6 +++--- src/cc/games/prices.cpp | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index f33838c64..271271a67 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -123,7 +123,7 @@ void *gamesiterate(struct games_state *rs) #ifdef STANDALONE #include #include "dapps/dappstd.c" -int64_t Net_change,Betsize; +int64_t Net_change,Betsize = SATOSHIDEN; char *send_curl(char *url,char *fname) { @@ -159,7 +159,7 @@ uint64_t get_btcusd() mult = 10000 + Net_change*10; newprice = (btcusd * mult) / 10000; x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff); - sprintf(str,"BTC/USD %.4f Net_change %lld * 0.001 -> Betsize %.8f %.4f\n",dstr(btcusd),(long long)Net_change,dstr(Betsize),dstr(newprice)); + sprintf(str,"BTC/USD %.4f -> Betsize %.8f (^ / to change) && %.4f [+ - to change]\n",dstr(btcusd),dstr(Betsize),dstr(newprice)); mvaddstr(0, 0, str); clrtoeol(); doupdate(); @@ -241,7 +241,7 @@ int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize) if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) { retval = 0; - fprintf(stderr,"%s\n",jprint(resobj,0)); + //fprintf(stderr,"%s\n",jprint(resobj,0)); } free_json(retjson); } diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 296d80a8c..a49fc1982 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -15,11 +15,18 @@ ******************************************************************************/ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); +extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum,price; CPubKey gamespk,mypk; + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum,price; CPubKey gamespk,mypk,acpk; + if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error"," no -ac_pubkey for price reference")); + } + acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); if ( params != 0 && cJSON_GetArraySize(params) == 2 ) { amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049; @@ -28,7 +35,9 @@ UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt parsehash")); } - fprintf(stderr,"amount %llu price %llx\n",(long long)amount,(long long)price); + if ( mypk == acpk ) + amount = 0; // i am the reference price feed + //fprintf(stderr,"amount %llu price %llx\n",(long long)amount,(long long)price); mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) @@ -53,7 +62,7 @@ UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) void prices_update(uint32_t timestamp,uint32_t uprice,int32_t ismine) { - fprintf(stderr,"%s t%u %.4f %16llx\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000,(long long)((uint64_t)timestamp<<32) | uprice); + //fprintf(stderr,"%s t%u %.4f %16llx\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000,(long long)((uint64_t)timestamp<<32) | uprice); } // game specific code for daemon From f18867055dddeea88e1ce5e880c8f75e292e73c4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 06:45:23 -1100 Subject: [PATCH 383/787] Test --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 271271a67..99b95f7c2 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -159,7 +159,7 @@ uint64_t get_btcusd() mult = 10000 + Net_change*10; newprice = (btcusd * mult) / 10000; x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff); - sprintf(str,"BTC/USD %.4f -> Betsize %.8f (^ / to change) && %.4f [+ - to change]\n",dstr(btcusd),dstr(Betsize),dstr(newprice)); + sprintf(str,"BTC/USD %.4f -> Betsize %.8f (^ / to change) && %.4f Net %.1f%% [+ - to change]\n",dstr(btcusd),dstr(Betsize),dstr(newprice),(double)100*mult/10000); mvaddstr(0, 0, str); clrtoeol(); doupdate(); From 2dfd8dacdb4f192f70523c5bc83ff8fb069adac6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 06:47:03 -1100 Subject: [PATCH 384/787] Display perc --- src/cc/games/prices.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c index 99b95f7c2..1bc62533e 100644 --- a/src/cc/games/prices.c +++ b/src/cc/games/prices.c @@ -159,7 +159,7 @@ uint64_t get_btcusd() mult = 10000 + Net_change*10; newprice = (btcusd * mult) / 10000; x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff); - sprintf(str,"BTC/USD %.4f -> Betsize %.8f (^ / to change) && %.4f Net %.1f%% [+ - to change]\n",dstr(btcusd),dstr(Betsize),dstr(newprice),(double)100*mult/10000); + sprintf(str,"BTC/USD %.4f -> Betsize %.8f (^ / to change) && %.4f Net %.1f%% [+ - to change]\n",dstr(btcusd),dstr(Betsize),dstr(newprice),(double)100*(mult-10000)/10000); mvaddstr(0, 0, str); clrtoeol(); doupdate(); From 7596077f2a3fbe657dc6897906cf1d0d9c20cfc7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 07:10:30 -1100 Subject: [PATCH 385/787] Settle stub --- src/cc/games/prices.cpp | 7 +++++++ src/cc/games/tetris.cpp | 8 +++++++- src/cc/gamescc.h | 4 ++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index a49fc1982..4098a3980 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -17,6 +17,13 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; + +UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result; + return(result); +} + UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); diff --git a/src/cc/games/tetris.cpp b/src/cc/games/tetris.cpp index 12c443e1e..d1ca9c30b 100644 --- a/src/cc/games/tetris.cpp +++ b/src/cc/games/tetris.cpp @@ -66,7 +66,13 @@ int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector pay } else return(-1); } -UniValue games_oracledata(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result; + return(result); +} + +UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result; return(result); diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 52627cd1c..7a6b7c66c 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -47,6 +47,7 @@ std::string Games_pname; { (char *)MYCCNAME, (char *)"events", (char *)"eventshex [gametxid [eventid]]", 1, 3, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"bet", (char *)"amount hexstr", 2, 2, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"settle", (char *)"height", 1, 1, ' ', EVAL_GAMES }, \ { (char *)MYCCNAME, (char *)"register", (char *)"gametxid [playertxid]", 1, 2, 'R', EVAL_GAMES }, bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); @@ -67,6 +68,7 @@ UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #define CUSTOM_DISPATCH \ if ( cp->evalcode == EVAL_GAMES ) \ @@ -105,6 +107,8 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_fund(txfee,cp,params)); \ else if ( strcmp(method,"bet") == 0 ) \ return(games_bet(txfee,cp,params)); \ + else if ( strcmp(method,"settle") == 0 ) \ + return(games_settle(txfee,cp,params)); \ else \ { \ result.push_back(Pair("result","error")); \ From 00eee20935f82635587776b527de745628a9dedb Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 07:33:33 -1100 Subject: [PATCH 386/787] Settle stub --- src/cc/games/prices.cpp | 50 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 4098a3980..7bc540171 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -14,24 +14,69 @@ * * ******************************************************************************/ +#define PRICES_BETPERIOD 3 UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result; + UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; int32_t i,n,numvouts,height,nextheight = komodo_nextheight(); + if ( params != 0 && cJSON_GetArraySize(params) == 1 ) + { + height = juint(jitem(params,0),0); + result.push_back(Pair("height",(int64_t)height)); + if ( (pindex= komodo_chainactive(height)) != 0 ) + { + if ( komodo_blockload(block,pindex) == 0 ) + { + n = pindex->block.vtx.size(); + for (i=0; i 1 ) + { + GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); + E_UNMARSHAL(vopret,ss >> pricebits); + fprintf(stderr,"i.%d %.8f %llx\n",i,(double)tx.vout[0].nValue/COIN,(long long)pricebits); + } + } + // display bets + if ( height <= nextheight-PRICES_BETPERIOD ) + { + // settle bets + } + result.push_back(Pair("result","success")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant load block at height")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find block at height")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parse")); + } return(result); } UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum,price; CPubKey gamespk,mypk,acpk; + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; uint64_t price; CPubKey gamespk,mypk,acpk; if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error"," no -ac_pubkey for price reference")); + return(result); } acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); if ( params != 0 && cJSON_GetArraySize(params) == 2 ) @@ -41,6 +86,7 @@ UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt parsehash")); + return(result); } if ( mypk == acpk ) amount = 0; // i am the reference price feed From ed6a4160aa30c9fcc3d7d9ec0d6550cd1132a2b3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 07:38:43 -1100 Subject: [PATCH 387/787] block.vtx --- src/cc/CCinclude.h | 1 + src/cc/games/prices.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 839fd2c95..12b4a6d12 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -197,6 +197,7 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); void komodo_sendmessage(int32_t minpeers,int32_t maxpeers,const char *message,std::vector payload); int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t len); +int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex); CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, vscript_t vopretNonfungible); CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets); diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 7bc540171..9909144f0 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -30,7 +30,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { if ( komodo_blockload(block,pindex) == 0 ) { - n = pindex->block.vtx.size(); + n = block.vtx.size(); for (i=0; i Date: Wed, 27 Mar 2019 07:51:09 -1100 Subject: [PATCH 388/787] +prints --- src/cc/games/prices.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 9909144f0..271470219 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -21,7 +21,7 @@ extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; int32_t i,n,numvouts,height,nextheight = komodo_nextheight(); + UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; uint32_t timestamp,pricebits; int32_t i,n,numvouts,height,nextheight = komodo_nextheight(); if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { height = juint(jitem(params,0),0); @@ -38,7 +38,9 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); E_UNMARSHAL(vopret,ss >> pricebits); - fprintf(stderr,"i.%d %.8f %llx\n",i,(double)tx.vout[0].nValue/COIN,(long long)pricebits); + timestamp = (uint32_t)(pricebits >> 32); + uprice = (uint32_t)pricebits; + fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f\n",scriptPubkey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000); } } // display bets From 68802af3193756d5a4671f120f95893f630c079b Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 07:51:56 -1100 Subject: [PATCH 389/787] Price --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 271470219..305d64f4e 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -21,7 +21,7 @@ extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; uint32_t timestamp,pricebits; int32_t i,n,numvouts,height,nextheight = komodo_nextheight(); + UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; uint32_t timestamp,uprice; int32_t i,n,numvouts,height,nextheight = komodo_nextheight(); if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { height = juint(jitem(params,0),0); From a26f3ea4a4c0eb4bfdae017c4c9883767475524c Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 07:52:38 -1100 Subject: [PATCH 390/787] tx.vout[numvouts-1]. --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 305d64f4e..790f4a687 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -40,7 +40,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) E_UNMARSHAL(vopret,ss >> pricebits); timestamp = (uint32_t)(pricebits >> 32); uprice = (uint32_t)pricebits; - fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f\n",scriptPubkey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000); + fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f\n",tx.vout[numvouts-1].scriptPubkey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000); } } // display bets From f003d0d8befcafd6010a47cf6e1fe7239af8f81d Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 07:53:33 -1100 Subject: [PATCH 391/787] Test --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 790f4a687..de16a7f2b 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -40,7 +40,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) E_UNMARSHAL(vopret,ss >> pricebits); timestamp = (uint32_t)(pricebits >> 32); uprice = (uint32_t)pricebits; - fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f\n",tx.vout[numvouts-1].scriptPubkey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000); + fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d\n",tx.vout[numvouts-1].scriptPubkey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts); } } // display bets From a1a8014f057355d12e1580bc2b4988c4890583f6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 07:54:17 -1100 Subject: [PATCH 392/787] scriptPubKey --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index de16a7f2b..ba78e8560 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -40,7 +40,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) E_UNMARSHAL(vopret,ss >> pricebits); timestamp = (uint32_t)(pricebits >> 32); uprice = (uint32_t)pricebits; - fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d\n",tx.vout[numvouts-1].scriptPubkey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts); + fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts); } } // display bets From 4a998b2595fe111192bada2dff1a425d057ec14c Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:01:21 -1100 Subject: [PATCH 393/787] +print --- src/cc/games/prices.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index ba78e8560..1171c3aba 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -34,7 +34,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) for (i=0; i 1 ) + if ( (numvouts= tx.vout.size()) > 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) { GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); E_UNMARSHAL(vopret,ss >> pricebits); @@ -91,7 +91,10 @@ UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result); } if ( mypk == acpk ) + { amount = 0; // i am the reference price feed + fprintf(stderr,"i am the reference\n"); + } //fprintf(stderr,"amount %llu price %llx\n",(long long)amount,(long long)price); mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); From 03f663ea3795c3b146f54a819db3407ff1714950 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:04:06 -1100 Subject: [PATCH 394/787] Reorder --- src/cc/games/prices.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 1171c3aba..f5a866727 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -21,7 +21,10 @@ extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; uint32_t timestamp,uprice; int32_t i,n,numvouts,height,nextheight = komodo_nextheight(); + UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; uint32_t timestamp,uprice; CPubKey acpk,mypk,gamespk; int32_t i,n,numvouts,height,nextheight = komodo_nextheight(); + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { height = juint(jitem(params,0),0); @@ -80,6 +83,8 @@ UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) result.push_back(Pair("error"," no -ac_pubkey for price reference")); return(result); } + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); if ( params != 0 && cJSON_GetArraySize(params) == 2 ) { @@ -93,11 +98,9 @@ UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( mypk == acpk ) { amount = 0; // i am the reference price feed - fprintf(stderr,"i am the reference\n"); + //fprintf(stderr,"i am the reference\n"); } //fprintf(stderr,"amount %llu price %llx\n",(long long)amount,(long long)price); - mypk = pubkey2pk(Mypubkey()); - gamespk = GetUnspendable(cp,0); if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) { mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); From a28e03acdd52e6dec2c2b5b3ecdb646e3fbbb558 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:14:39 -1100 Subject: [PATCH 395/787] Test --- src/cc/games/prices.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index f5a866727..69c45f286 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -21,10 +21,11 @@ extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; uint32_t timestamp,uprice; CPubKey acpk,mypk,gamespk; int32_t i,n,numvouts,height,nextheight = komodo_nextheight(); + UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; char acaddr[64],destaddr[64]; uint32_t timestamp,uprice; CPubKey acpk,mypk,gamespk; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts,height,nextheight = komodo_nextheight(); mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); + Getscriptaddress(acaddr,CScript() << ParseHex(HexStr(acpk)) << OP_CHECKSIG); if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { height = juint(jitem(params,0),0); @@ -34,16 +35,24 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) if ( komodo_blockload(block,pindex) == 0 ) { n = block.vtx.size(); + vini = 0; for (i=0; i 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) + if ( myGetTransaction(tx.vin[vini].prevout.hash,vintx,hashBlock) == 0 ) + continue; + else if ( Getscriptaddress(destaddr,vintx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ) + continue; + else if ( (numvouts= tx.vout.size()) > 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) { + prizefund += tx.vout[0].nValue; GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); E_UNMARSHAL(vopret,ss >> pricebits); timestamp = (uint32_t)(pricebits >> 32); uprice = (uint32_t)pricebits; - fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts); + if ( strcmp(acaddr,destaddr) == 0 ) + fprintf(stderr,"REF "); + fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); } } // display bets @@ -51,6 +60,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { // settle bets } + result.push_back(Pair("prizefund",ValueToAmount(prizefund))); result.push_back(Pair("result","success")); } else From d23d46d9534325fd2c4a149c2aab6845db73560c Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:19:18 -1100 Subject: [PATCH 396/787] ValueFromAmount --- src/cc/games/prices.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 69c45f286..64699dfe7 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -21,7 +21,7 @@ extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx; uint64_t pricebits; char acaddr[64],destaddr[64]; uint32_t timestamp,uprice; CPubKey acpk,mypk,gamespk; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts,height,nextheight = komodo_nextheight(); + UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char acaddr[64],destaddr[64]; uint32_t timestamp,uprice; CPubKey acpk,mypk,gamespk; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts,height,nextheight = komodo_nextheight(); mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); @@ -60,7 +60,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { // settle bets } - result.push_back(Pair("prizefund",ValueToAmount(prizefund))); + result.push_back(Pair("prizefund",ValueFromAmount(prizefund))); result.push_back(Pair("result","success")); } else From d2fbc97ee88f5f421c6bd04ca7af37a7abc3bcbd Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:21:49 -1100 Subject: [PATCH 397/787] -print --- src/cc/games/prices.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 64699dfe7..146f6a926 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -50,9 +50,9 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) E_UNMARSHAL(vopret,ss >> pricebits); timestamp = (uint32_t)(pricebits >> 32); uprice = (uint32_t)pricebits; - if ( strcmp(acaddr,destaddr) == 0 ) - fprintf(stderr,"REF "); - fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); + //if ( strcmp(acaddr,destaddr) == 0 ) + // fprintf(stderr,"REF "); + //fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); } } // display bets From a5406418471059ca5594fbbf7c3d5ff7f6416221 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:30:19 -1100 Subject: [PATCH 398/787] Test --- src/cc/games/prices.cpp | 83 +++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 40 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 146f6a926..770cdfc8e 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -18,10 +18,41 @@ UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; +int64_t prices_blockinfo(int32_t height,char *acaddr) +{ + std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t timestamp,uprice; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts; + if ( (pindex= komodo_chainactive(height)) != 0 ) + { + if ( komodo_blockload(block,pindex) == 0 ) + { + n = block.vtx.size(); + vini = 0; + for (i=0; i= vintx.vout.size() || Getscriptaddress(destaddr,vintx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ) + continue; + else if ( (numvouts= tx.vout.size()) > 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) + { + prizefund += tx.vout[0].nValue; + GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); + E_UNMARSHAL(vopret,ss >> pricebits); + timestamp = (uint32_t)(pricebits >> 32); + uprice = (uint32_t)pricebits; + //if ( strcmp(acaddr,destaddr) == 0 ) + // fprintf(stderr,"REF "); + //fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); + } + } + } else return(-2); + } else return(-1); +} UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result; std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char acaddr[64],destaddr[64]; uint32_t timestamp,uprice; CPubKey acpk,mypk,gamespk; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts,height,nextheight = komodo_nextheight(); + UniValue result; char acaddr[64]; CPubKey acpk,mypk,gamespk; int64_t prizefund = 0; int32_t height,nextheight = komodo_nextheight(); mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); @@ -30,49 +61,21 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { height = juint(jitem(params,0),0); result.push_back(Pair("height",(int64_t)height)); - if ( (pindex= komodo_chainactive(height)) != 0 ) + if ( 1 || (prizefund= prices_blockinfo(height,acaddr)) < 0 ) { - if ( komodo_blockload(block,pindex) == 0 ) - { - n = block.vtx.size(); - vini = 0; - for (i=0; i 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) - { - prizefund += tx.vout[0].nValue; - GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); - E_UNMARSHAL(vopret,ss >> pricebits); - timestamp = (uint32_t)(pricebits >> 32); - uprice = (uint32_t)pricebits; - //if ( strcmp(acaddr,destaddr) == 0 ) - // fprintf(stderr,"REF "); - //fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); - } - } - // display bets - if ( height <= nextheight-PRICES_BETPERIOD ) - { - // settle bets - } - result.push_back(Pair("prizefund",ValueFromAmount(prizefund))); - result.push_back(Pair("result","success")); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant load block at height")); - } + result.push_back(Pair("result","error")); + result.push_back(Pair("errorcode",prizefund)); + result.push_back(Pair("error","blockinfo error")); } else { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find block at height")); + // display bets + if ( height <= nextheight-PRICES_BETPERIOD ) + { + // settle bets + } + result.push_back(Pair("prizefund",ValueFromAmount(prizefund))); + result.push_back(Pair("result","success")); } } else From 513731ea367a4a03eeae8a3561d7f4887eb949ef Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:32:55 -1100 Subject: [PATCH 399/787] Test --- src/cc/gamescc.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 7a6b7c66c..efdd3b8e1 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -77,6 +77,8 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_rng(txfee,cp,params)); \ else if ( strcmp(method,"rngnext") == 0 ) \ return(games_rngnext(txfee,cp,params)); \ + else if ( strcmp(method,"settle") == 0 ) \ + return(games_settle(txfee,cp,params)); \ else if ( strcmp(method,"newgame") == 0 ) \ return(games_newgame(txfee,cp,params)); \ else if ( strcmp(method,"gameinfo") == 0 ) \ @@ -107,8 +109,6 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_fund(txfee,cp,params)); \ else if ( strcmp(method,"bet") == 0 ) \ return(games_bet(txfee,cp,params)); \ - else if ( strcmp(method,"settle") == 0 ) \ - return(games_settle(txfee,cp,params)); \ else \ { \ result.push_back(Pair("result","error")); \ From 8de793c57bdc8159fb4f951151b7c452500333d1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:35:06 -1100 Subject: [PATCH 400/787] Test --- src/cc/games/prices.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 770cdfc8e..ea5e28211 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -53,6 +53,8 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result; char acaddr[64]; CPubKey acpk,mypk,gamespk; int64_t prizefund = 0; int32_t height,nextheight = komodo_nextheight(); + result.push_back(Pair("result","success")); + return(result); mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); From 0e1310dd8a3538b7cabc96072b493339be69a9c3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:42:44 -1100 Subject: [PATCH 401/787] Test --- src/cc/games/prices.cpp | 2 ++ src/cc/games/tetris.cpp | 2 ++ src/cc/gamescc.h | 1 - 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index ea5e28211..275589b02 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -14,6 +14,8 @@ * * ******************************************************************************/ +std::string MYCCLIBNAME = (char *)"gamescc"; + #define PRICES_BETPERIOD 3 UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; diff --git a/src/cc/games/tetris.cpp b/src/cc/games/tetris.cpp index d1ca9c30b..cd609154d 100644 --- a/src/cc/games/tetris.cpp +++ b/src/cc/games/tetris.cpp @@ -14,6 +14,8 @@ * * ******************************************************************************/ +std::string MYCCLIBNAME = (char *)"gamescc"; + // game specific code for daemon void games_packitemstr(char *packitemstr,struct games_packitem *item) { diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index efdd3b8e1..b83276d8d 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -15,7 +15,6 @@ extern CWallet* pwalletMain; #include "CCinclude.h" #include "secp256k1.h" -std::string MYCCLIBNAME = (char *)"gamescc"; #define EVAL_GAMES (EVAL_FAUCET2+1) #define GAMES_TXFEE 10000 From aa24e63f86a17fee395204efa2edfa113b59ae1f Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:46:03 -1100 Subject: [PATCH 402/787] Test --- src/cc/gamescc.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index b83276d8d..4d095ab12 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -24,6 +24,7 @@ extern CWallet* pwalletMain; #define GAMES_REGISTRATIONSIZE (100 * 10000) #define GAMES_REGISTRATION 1 +extern std::string MYCCLIBNAME; #define MYCCNAME "games" @@ -76,8 +77,6 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_rng(txfee,cp,params)); \ else if ( strcmp(method,"rngnext") == 0 ) \ return(games_rngnext(txfee,cp,params)); \ - else if ( strcmp(method,"settle") == 0 ) \ - return(games_settle(txfee,cp,params)); \ else if ( strcmp(method,"newgame") == 0 ) \ return(games_newgame(txfee,cp,params)); \ else if ( strcmp(method,"gameinfo") == 0 ) \ @@ -108,6 +107,8 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_fund(txfee,cp,params)); \ else if ( strcmp(method,"bet") == 0 ) \ return(games_bet(txfee,cp,params)); \ + else if ( strcmp(method,"settle") == 0 ) \ + return(games_settle(txfee,cp,params)); \ else \ { \ result.push_back(Pair("result","error")); \ From 1a413d246f0302cbb066d05c4d898c9dbec57756 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 08:58:40 -1100 Subject: [PATCH 403/787] Prices --- src/cc/cclib.cpp | 2 ++ src/cc/games/prices.cpp | 2 +- src/cc/gamescc.h | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 975b0735c..00a7c6fe0 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -52,6 +52,8 @@ std::string MYCCLIBNAME = (char *)"sudoku"; void komodo_netevent(std::vector payload) {} #endif +extern std::string MYCCLIBNAME; + char *CClib_name() { return((char *)MYCCLIBNAME.c_str()); } struct CClib_rpcinfo diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 275589b02..7293a8de9 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -14,7 +14,7 @@ * * ******************************************************************************/ -std::string MYCCLIBNAME = (char *)"gamescc"; +std::string MYCCLIBNAME = (char *)"pricescc"; #define PRICES_BETPERIOD 3 UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 4d095ab12..b804216d7 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -24,7 +24,6 @@ extern CWallet* pwalletMain; #define GAMES_REGISTRATIONSIZE (100 * 10000) #define GAMES_REGISTRATION 1 -extern std::string MYCCLIBNAME; #define MYCCNAME "games" From 985386a7e31bde57567266305956f834051546aa Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:00:40 -1100 Subject: [PATCH 404/787] Prices --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 7293a8de9..8624bc635 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -14,7 +14,7 @@ * * ******************************************************************************/ -std::string MYCCLIBNAME = (char *)"pricescc"; +std::string MYCCLIBNAME = (char *)"prices"; #define PRICES_BETPERIOD 3 UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); From 2102294650bf5f4c4bd59ff47575bcf5ee7642ea Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:04:14 -1100 Subject: [PATCH 405/787] Test --- src/cc/games/prices.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 8624bc635..4777e9de0 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -55,8 +55,12 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result; char acaddr[64]; CPubKey acpk,mypk,gamespk; int64_t prizefund = 0; int32_t height,nextheight = komodo_nextheight(); - result.push_back(Pair("result","success")); - return(result); + if ( 1 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error"," no -ac_pubkey for price reference")); + return(result); + } mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); From 5e817c05acf57db0342d028958d475eb6a4ca786 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:06:30 -1100 Subject: [PATCH 406/787] Test --- src/cc/games/prices.cpp | 49 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 4777e9de0..91f4aa790 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -52,7 +52,7 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) } else return(-1); } -UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_origsettle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result; char acaddr[64]; CPubKey acpk,mypk,gamespk; int64_t prizefund = 0; int32_t height,nextheight = komodo_nextheight(); if ( 1 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) @@ -93,6 +93,53 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) } return(result); } +UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; uint64_t price; CPubKey gamespk,mypk,acpk; + if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error"," no -ac_pubkey for price reference")); + return(result); + } + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); + if ( params != 0 && cJSON_GetArraySize(params) == 2 ) + { + amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049; + if ( cclib_parsehash((uint8_t *)&price,jitem(params,1),8) < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parsehash")); + return(result); + } + if ( mypk == acpk ) + { + amount = 0; // i am the reference price feed + //fprintf(stderr,"i am the reference\n"); + } + //fprintf(stderr,"amount %llu price %llx\n",(long long)amount,(long long)price); + if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,CScript() << OP_RETURN << price); + return(games_rawtxresult(result,rawtx,1)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parse")); + } + return(result); +} UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { From dcfae63863f27e4dc82587d0da2a9e370df279b7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:09:10 -1100 Subject: [PATCH 407/787] Test --- src/cc/games/prices.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 91f4aa790..112383554 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -106,7 +106,12 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); - if ( params != 0 && cJSON_GetArraySize(params) == 2 ) + if ( params != 0 && cJSON_GetArraySize(params) == 1 ) + { + height = juint(jitem(params,0),0); + result.push_back(Pair("height",(int64_t)height)); + } + /*if ( params != 0 && cJSON_GetArraySize(params) == 2 ) { amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049; if ( cclib_parsehash((uint8_t *)&price,jitem(params,1),8) < 0 ) @@ -137,7 +142,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt parse")); - } + }*/ return(result); } From 23e378493fa7b9f5ec01df6a8b78e4c69f837ce5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:10:02 -1100 Subject: [PATCH 408/787] Test --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 112383554..c9826fd9c 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -96,7 +96,7 @@ UniValue games_origsettle(uint64_t txfee,struct CCcontract_info *cp,cJSON *param UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; uint64_t price; CPubKey gamespk,mypk,acpk; + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; uint64_t price; CPubKey gamespk,mypk,acpk; int32_t height; if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) { result.push_back(Pair("result","error")); From d294ec1b93d9fdd5b0d258e111658db5c228ae51 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:12:22 -1100 Subject: [PATCH 409/787] Test --- src/cc/games/prices.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index c9826fd9c..a638a7016 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -93,6 +93,7 @@ UniValue games_origsettle(uint64_t txfee,struct CCcontract_info *cp,cJSON *param } return(result); } + UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -106,10 +107,11 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); + result.push_back(Pair("result","success")); if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { height = juint(jitem(params,0),0); - result.push_back(Pair("height",(int64_t)height)); + //result.push_back(Pair("height",(int64_t)height)); } /*if ( params != 0 && cJSON_GetArraySize(params) == 2 ) { From 7b6ad019e63caadbc963b64627c7958f2635a8fe Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:13:48 -1100 Subject: [PATCH 410/787] Test --- src/cc/games/prices.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index a638a7016..1ac7e3edb 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -110,8 +110,8 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) result.push_back(Pair("result","success")); if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { - height = juint(jitem(params,0),0); - //result.push_back(Pair("height",(int64_t)height)); + //height = juint(jitem(params,0),0); + result.push_back(Pair("height",(int64_t)height)); } /*if ( params != 0 && cJSON_GetArraySize(params) == 2 ) { From ecd3a17c85b1b016aac8e971b61ca6f6545305cb Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:16:03 -1100 Subject: [PATCH 411/787] Test --- src/cc/games/prices.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 1ac7e3edb..7f807954d 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -52,10 +52,10 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) } else return(-1); } -UniValue games_origsettle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { UniValue result; char acaddr[64]; CPubKey acpk,mypk,gamespk; int64_t prizefund = 0; int32_t height,nextheight = komodo_nextheight(); - if ( 1 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) + if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error"," no -ac_pubkey for price reference")); @@ -64,6 +64,16 @@ UniValue games_origsettle(uint64_t txfee,struct CCcontract_info *cp,cJSON *param mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); + result.push_back(Pair("result","success")); + if ( params != 0 && cJSON_GetArraySize(params) == 1 ) + { + height = juint(jitem(params,0),0); + result.push_back(Pair("height",(int64_t)height)); + } + return(result); + /*mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); Getscriptaddress(acaddr,CScript() << ParseHex(HexStr(acpk)) << OP_CHECKSIG); if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { @@ -91,7 +101,7 @@ UniValue games_origsettle(uint64_t txfee,struct CCcontract_info *cp,cJSON *param result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt parse")); } - return(result); + return(result);*/ } UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) @@ -110,7 +120,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) result.push_back(Pair("result","success")); if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { - //height = juint(jitem(params,0),0); + height = juint(jitem(params,0),0); result.push_back(Pair("height",(int64_t)height)); } /*if ( params != 0 && cJSON_GetArraySize(params) == 2 ) From 42235ad98d80acd044f021e359f8f8c9bd752166 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:17:08 -1100 Subject: [PATCH 412/787] -2nd --- src/cc/games/prices.cpp | 54 ----------------------------------------- 1 file changed, 54 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 7f807954d..c88827fa2 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -104,60 +104,6 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) return(result);*/ } -UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; uint64_t price; CPubKey gamespk,mypk,acpk; int32_t height; - if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error"," no -ac_pubkey for price reference")); - return(result); - } - mypk = pubkey2pk(Mypubkey()); - gamespk = GetUnspendable(cp,0); - acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); - result.push_back(Pair("result","success")); - if ( params != 0 && cJSON_GetArraySize(params) == 1 ) - { - height = juint(jitem(params,0),0); - result.push_back(Pair("height",(int64_t)height)); - } - /*if ( params != 0 && cJSON_GetArraySize(params) == 2 ) - { - amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049; - if ( cclib_parsehash((uint8_t *)&price,jitem(params,1),8) < 0 ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt parsehash")); - return(result); - } - if ( mypk == acpk ) - { - amount = 0; // i am the reference price feed - //fprintf(stderr,"i am the reference\n"); - } - //fprintf(stderr,"amount %llu price %llx\n",(long long)amount,(long long)price); - if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) - { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,CScript() << OP_RETURN << price); - return(games_rawtxresult(result,rawtx,1)); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","not enough funds")); - } - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt parse")); - }*/ - return(result); -} - UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); From e909810c76fc2c109ca8b71ea80dfaf0967d5466 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:18:29 -1100 Subject: [PATCH 413/787] Test --- src/cc/games/prices.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index c88827fa2..142bc4c6b 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -65,11 +65,11 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); result.push_back(Pair("result","success")); - if ( params != 0 && cJSON_GetArraySize(params) == 1 ) + /*if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { height = juint(jitem(params,0),0); result.push_back(Pair("height",(int64_t)height)); - } + }*/ return(result); /*mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); From bdf32a01fb74f249bcf30807af3a75db4f5b256a Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:19:27 -1100 Subject: [PATCH 414/787] Test --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 142bc4c6b..be85b92f1 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -63,7 +63,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) } mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); - acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); + //acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); result.push_back(Pair("result","success")); /*if ( params != 0 && cJSON_GetArraySize(params) == 1 ) { From a94d3bd0f753f395bbab32de8bd55f02ec28df07 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:21:58 -1100 Subject: [PATCH 415/787] d'oh --- src/cc/games/prices.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index be85b92f1..7a43c8218 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -54,7 +54,7 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result; char acaddr[64]; CPubKey acpk,mypk,gamespk; int64_t prizefund = 0; int32_t height,nextheight = komodo_nextheight(); + UniValue result(UniValue::VOBJ); char acaddr[64]; CPubKey acpk,mypk,gamespk; int64_t prizefund = 0; int32_t height,nextheight = komodo_nextheight(); if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) { result.push_back(Pair("result","error")); @@ -63,16 +63,6 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) } mypk = pubkey2pk(Mypubkey()); gamespk = GetUnspendable(cp,0); - //acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); - result.push_back(Pair("result","success")); - /*if ( params != 0 && cJSON_GetArraySize(params) == 1 ) - { - height = juint(jitem(params,0),0); - result.push_back(Pair("height",(int64_t)height)); - }*/ - return(result); - /*mypk = pubkey2pk(Mypubkey()); - gamespk = GetUnspendable(cp,0); acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); Getscriptaddress(acaddr,CScript() << ParseHex(HexStr(acpk)) << OP_CHECKSIG); if ( params != 0 && cJSON_GetArraySize(params) == 1 ) @@ -101,7 +91,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt parse")); } - return(result);*/ + return(result); } UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) From 40174b32742b8d481c166ceea263d09977351087 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:23:55 -1100 Subject: [PATCH 416/787] Test --- src/cc/games/prices.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 7a43c8218..6385626fc 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -20,6 +20,8 @@ std::string MYCCLIBNAME = (char *)"prices"; UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; +// generate bars + int64_t prices_blockinfo(int32_t height,char *acaddr) { std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t timestamp,uprice; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts; @@ -69,7 +71,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { height = juint(jitem(params,0),0); result.push_back(Pair("height",(int64_t)height)); - if ( 1 || (prizefund= prices_blockinfo(height,acaddr)) < 0 ) + if ( (prizefund= prices_blockinfo(height,acaddr)) < 0 ) { result.push_back(Pair("result","error")); result.push_back(Pair("errorcode",prizefund)); @@ -80,7 +82,7 @@ UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) // display bets if ( height <= nextheight-PRICES_BETPERIOD ) { - // settle bets + // settle bets by first nonzero reference bar } result.push_back(Pair("prizefund",ValueFromAmount(prizefund))); result.push_back(Pair("result","success")); From 9c12a5523762798880c98b39a0cc0b4bfa111bab Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:25:35 -1100 Subject: [PATCH 417/787] Return prizefund --- src/cc/games/prices.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 6385626fc..177417c9b 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -45,11 +45,12 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) E_UNMARSHAL(vopret,ss >> pricebits); timestamp = (uint32_t)(pricebits >> 32); uprice = (uint32_t)pricebits; - //if ( strcmp(acaddr,destaddr) == 0 ) - // fprintf(stderr,"REF "); - //fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); + if ( strcmp(acaddr,destaddr) == 0 ) + fprintf(stderr,"REF "); + fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); } } + return(prizefund); } else return(-2); } else return(-1); } From 5a557264fa1c98ef90f8ce8cb8c4fb5c58532782 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 09:27:56 -1100 Subject: [PATCH 418/787] Test --- src/cc/games/prices.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 177417c9b..9178eb46b 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -42,12 +42,15 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) { prizefund += tx.vout[0].nValue; GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); - E_UNMARSHAL(vopret,ss >> pricebits); - timestamp = (uint32_t)(pricebits >> 32); - uprice = (uint32_t)pricebits; - if ( strcmp(acaddr,destaddr) == 0 ) - fprintf(stderr,"REF "); - fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); + if ( vopret.size() == 8 ) + { + E_UNMARSHAL(vopret,ss >> pricebits); + timestamp = (uint32_t)(pricebits >> 32); + uprice = (uint32_t)pricebits; + if ( strcmp(acaddr,destaddr) == 0 ) + fprintf(stderr,"REF "); + fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); + } else return(-3); } } return(prizefund); From 0597306ea564fa640c7e91384421945e22804928 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 27 Mar 2019 19:31:39 -1100 Subject: [PATCH 419/787] 69522 exemption --- src/cc/rogue_rpc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 5040c34d3..c5350a68d 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -1521,7 +1521,7 @@ bool rogue_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C if ( (numvouts= tx.vout.size()) > 1 ) { txid = tx.GetHash(); - if ( txid == Parseuint256("1ae04dc0c5f2fca2053819a3a1b2efe5d355c34f58d6f16d59e5e2573e7baf7f") ) // osx rogue chain ht.50902 + if ( txid == Parseuint256("1ae04dc0c5f2fca2053819a3a1b2efe5d355c34f58d6f16d59e5e2573e7baf7f") || txid == Parseuint256("2a34b36cc1292aecfaabdad79b42cab9989fa6dcc87ac8ca88aa6162dab1e2c4") ) // osx rogue chain ht.50902, 69522 enabled = 0; scriptPubKey = tx.vout[numvouts-1].scriptPubKey; GetOpReturnData(scriptPubKey,vopret); From de646890858f06a30fe72ed2ec97e53c5e27e590 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Thu, 28 Mar 2019 14:45:34 +0800 Subject: [PATCH 420/787] fix rewards minrelease validation --- src/cc/rewards.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 73f723453..129cbc47f 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -258,11 +258,11 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) return eval->Invalid("txfee is too high"); reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit); + if ( reward == 0 ) + return eval->Invalid("no elegible rewards"); if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) { - if ( reward == 0 ) - return eval->Invalid("unlock recover no rewards"); - else if ( tx.vout[1].nValue != 10000 ) + if ( tx.vout[1].nValue != 10000 ) return eval->Invalid("wrong marker vour value"); else if ( tx.vout[1].scriptPubKey != tx.vout[0].scriptPubKey ) return eval->Invalid("unlock recover tx vout.1 mismatched scriptPubKey"); From 87b039a2673ad0f0758c5f9c18a23da5084be8b1 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Thu, 28 Mar 2019 14:56:51 +0800 Subject: [PATCH 421/787] fix --- src/cc/rewards.cpp | 3 +-- src/main.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 129cbc47f..152842bbd 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -263,7 +263,7 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) { if ( tx.vout[1].nValue != 10000 ) - return eval->Invalid("wrong marker vour value"); + return eval->Invalid("wrong marker vout value"); else if ( tx.vout[1].scriptPubKey != tx.vout[0].scriptPubKey ) return eval->Invalid("unlock recover tx vout.1 mismatched scriptPubKey"); else if ( tx.vout[0].scriptPubKey != vinTx.vout[1].scriptPubKey ) @@ -285,7 +285,6 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey ) return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey"); amount = vinTx.vout[0].nValue; - reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit); if ( RewardsExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue,sbits,fundingtxid) == 0 ) return false; else if ( tx.vout[1].nValue > amount+reward ) diff --git a/src/main.cpp b/src/main.cpp index e520ad83f..019507c55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1383,7 +1383,7 @@ bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransactio } } if ( txout.scriptPubKey.size() > IGUANA_MAXSCRIPTSIZE ) - return state.DoS(100, error("CheckTransaction(): txout.scriptPubKey.size() too big"),REJECT_INVALID, "bad-txns-vout-negative"); + return state.DoS(100, error("CheckTransaction(): txout.scriptPubKey.size() too big"),REJECT_INVALID, "bad-txns-opret-too-big"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) return state.DoS(100, error("CheckTransaction(): txout total out of range"), From fab49cbf44440d7203bd48976b8ba24088c860cc Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 00:52:17 -1100 Subject: [PATCH 422/787] prices_bar --- src/cc/games/prices.cpp | 88 ++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 19 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 9178eb46b..b5540650a 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -20,37 +20,87 @@ std::string MYCCLIBNAME = (char *)"prices"; UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; -// generate bars +#define bstr(x) ((double)((uint32_t)x) / 10000.) + +struct prices_bar +{ + uint64_t open,high,low,close,sum; + int32_t num; +}; + +int32_t prices_barupdate(struct prices_bar *bar,uint64_t pricebits) +{ + uint32_t uprice,timestamp; + timestamp = (uint32_t)(pricebits >> 32); + uprice = (uint32_t)pricebits; + bar->sum += uprice, bar->num++; + if ( bar->open == 0 ) + bar->open = bar->high = bar->low = pricebits; + if ( uprice > (uint32_t)bar->high ) + bar->high = pricebits; + else if ( uprice < (uint32_t)bar->low ) + bar->low = pricebits; + bar->close = pricebits; + return(0); +} + +int32_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebits) +{ + int32_t dist = 0; uint32_t uprice = (uint32_t)pricebits; + dist = (uprice - aveprice) * (uprice - aveprice); + return(dist); +} + +void prices_bardisp(struct prices_bar *bar) +{ + if ( bar->num == 0 ) + fprintf(stderr,"BAR null\n"); + else fprintf(stderr,"BAR ave %.4f (O %.4f, H %.4f, L %.4f, C %.4f)\n",bstr(bar->sum/bar->num),bstr(bar->open),bstr(bar->high),bstr(bar->low),bstr(bar->close)); +} int64_t prices_blockinfo(int32_t height,char *acaddr) { - std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t timestamp,uprice; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts; + std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts,iter; struct bar_info refbar; if ( (pindex= komodo_chainactive(height)) != 0 ) { if ( komodo_blockload(block,pindex) == 0 ) { n = block.vtx.size(); vini = 0; - for (i=0; i= vintx.vout.size() || Getscriptaddress(destaddr,vintx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ) - continue; - else if ( (numvouts= tx.vout.size()) > 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) + for (i=0; i= vintx.vout.size() || Getscriptaddress(destaddr,vintx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ) + continue; + else if ( iter == 0 != strcmp(acaddr,destaddr) != 0 ) + continue; + else if ( (numvouts= tx.vout.size()) > 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) { - E_UNMARSHAL(vopret,ss >> pricebits); - timestamp = (uint32_t)(pricebits >> 32); - uprice = (uint32_t)pricebits; - if ( strcmp(acaddr,destaddr) == 0 ) - fprintf(stderr,"REF "); - fprintf(stderr,"[%02x] i.%d %.8f %llx t%u %.4f numvouts.%d %s lag.%d\n",tx.vout[numvouts-1].scriptPubKey[0],i,(double)tx.vout[0].nValue/COIN,(long long)pricebits,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp)); - } else return(-3); + prizefund += tx.vout[0].nValue; + GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); + if ( vopret.size() == 8 ) + { + E_UNMARSHAL(vopret,ss >> pricebits); + timestamp = (uint32_t)(pricebits >> 32); + uprice = (uint32_t)pricebits; + if ( iter == 0 ) + prices_barupdate(&refbar,pricebits); + if ( strcmp(acaddr,destaddr) == 0 ) + fprintf(stderr,"REF "); + fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%d\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,prices_bardist(&refbar,aveprice,pricebits)); + } else return(-3); + } + if ( iter == 0 ) + { + prices_bardisp(&refbar); + if ( refbar.num != 0 ) + aveprice = (uint32_t)refbar.sum / refbar.num; + } } } return(prizefund); From f970eb5c11009a639060215e7fb2690f6ef9135d Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 00:53:02 -1100 Subject: [PATCH 423/787] prices_bar --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index b5540650a..1a599d390 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -60,7 +60,7 @@ void prices_bardisp(struct prices_bar *bar) int64_t prices_blockinfo(int32_t height,char *acaddr) { - std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts,iter; struct bar_info refbar; + std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts,iter; struct prices_bar refbar; if ( (pindex= komodo_chainactive(height)) != 0 ) { if ( komodo_blockload(block,pindex) == 0 ) From 3b880b21801208d59d1f14f6903f74dc72db3399 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 00:56:02 -1100 Subject: [PATCH 424/787] Fix skip condition --- src/cc/games/prices.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 1a599d390..40d747f65 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -47,7 +47,11 @@ int32_t prices_barupdate(struct prices_bar *bar,uint64_t pricebits) int32_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebits) { int32_t dist = 0; uint32_t uprice = (uint32_t)pricebits; - dist = (uprice - aveprice) * (uprice - aveprice); + if ( aveprice != 0 ) + { + dist = (uprice - aveprice); + dist *= dist; + } return(dist); } @@ -77,7 +81,7 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) continue; else if ( tx.vin[vini].prevout.n >= vintx.vout.size() || Getscriptaddress(destaddr,vintx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ) continue; - else if ( iter == 0 != strcmp(acaddr,destaddr) != 0 ) + else if ( iter != strcmp(acaddr,destaddr) ) continue; else if ( (numvouts= tx.vout.size()) > 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) { From 751156efa3be2e36db315340af7765b83597c608 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:09:08 -1100 Subject: [PATCH 425/787] Cleanup loop --- src/cc/games/prices.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 40d747f65..f6ee704ee 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -81,11 +81,8 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) continue; else if ( tx.vin[vini].prevout.n >= vintx.vout.size() || Getscriptaddress(destaddr,vintx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ) continue; - else if ( iter != strcmp(acaddr,destaddr) ) - continue; else if ( (numvouts= tx.vout.size()) > 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) { - prizefund += tx.vout[0].nValue; GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() == 8 ) { @@ -93,18 +90,22 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) timestamp = (uint32_t)(pricebits >> 32); uprice = (uint32_t)pricebits; if ( iter == 0 ) - prices_barupdate(&refbar,pricebits); - if ( strcmp(acaddr,destaddr) == 0 ) - fprintf(stderr,"REF "); - fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%d\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,prices_bardist(&refbar,aveprice,pricebits)); + { + prizefund += tx.vout[0].nValue; + if ( strcmp(acaddr,destaddr) == 0 ) + { + fprintf(stderr,"REF "); + prices_barupdate(&refbar,pricebits); + } + } else fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%d\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,prices_bardist(&refbar,aveprice,pricebits)); } else return(-3); } - if ( iter == 0 ) - { - prices_bardisp(&refbar); - if ( refbar.num != 0 ) - aveprice = (uint32_t)refbar.sum / refbar.num; - } + } + if ( iter == 0 ) + { + prices_bardisp(&refbar); + if ( refbar.num != 0 ) + aveprice = (uint32_t)refbar.sum / refbar.num; } } return(prizefund); From 2f9c10cb73c366e5a1d38b2e05c6be6e35424f84 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:12:10 -1100 Subject: [PATCH 426/787] Test --- src/cc/games/prices.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index f6ee704ee..8c70750bf 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -51,6 +51,7 @@ int32_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebi { dist = (uprice - aveprice); dist *= dist; + fprintf(stderr,"dist.%d (u %u - ave %u) %d\n",dist,uprice,aveprice,uprice-aveprice); } return(dist); } @@ -97,7 +98,9 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) fprintf(stderr,"REF "); prices_barupdate(&refbar,pricebits); } - } else fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%d\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,prices_bardist(&refbar,aveprice,pricebits)); + } + else if ( strcmp(acaddr,destaddr) != 0 ) + fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%d\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,prices_bardist(&refbar,aveprice,pricebits)); } else return(-3); } } From 58ebf2262dede6fed26c0ace4bafa8a480c4c084 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:14:12 -1100 Subject: [PATCH 427/787] Int64 --- src/cc/games/prices.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 8c70750bf..d4a87ee39 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -44,14 +44,14 @@ int32_t prices_barupdate(struct prices_bar *bar,uint64_t pricebits) return(0); } -int32_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebits) +int64_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebits) { - int32_t dist = 0; uint32_t uprice = (uint32_t)pricebits; + int64_t dist = 0; uint32_t uprice = (uint32_t)pricebits; if ( aveprice != 0 ) { dist = (uprice - aveprice); dist *= dist; - fprintf(stderr,"dist.%d (u %u - ave %u) %d\n",dist,uprice,aveprice,uprice-aveprice); + fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); } return(dist); } @@ -100,7 +100,7 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) } } else if ( strcmp(acaddr,destaddr) != 0 ) - fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%d\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,prices_bardist(&refbar,aveprice,pricebits)); + fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%lld\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,(long long)prices_bardist(&refbar,aveprice,pricebits)); } else return(-3); } } From 4414e5a5f47bcb08a7de59ab239f55d3900163b9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:17:50 -1100 Subject: [PATCH 428/787] Test --- src/cc/games/prices.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index d4a87ee39..abcefed74 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -51,7 +51,7 @@ int64_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebi { dist = (uprice - aveprice); dist *= dist; - fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); + //fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); } return(dist); } @@ -99,8 +99,7 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) prices_barupdate(&refbar,pricebits); } } - else if ( strcmp(acaddr,destaddr) != 0 ) - fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%lld\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,(long long)prices_bardist(&refbar,aveprice,pricebits)); + fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%lld\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,(long long)prices_bardist(&refbar,aveprice,pricebits)); } else return(-3); } } From d7758d26c83b2060577805887e8a03a0313cbab9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:20:16 -1100 Subject: [PATCH 429/787] Test --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index abcefed74..f8f61085b 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -51,7 +51,7 @@ int64_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebi { dist = (uprice - aveprice); dist *= dist; - //fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); + fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); } return(dist); } From a3b757fdbd1fd06a915212c7e40a4f7c9a4d7ab4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:23:03 -1100 Subject: [PATCH 430/787] Test --- src/cc/games/prices.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index f8f61085b..ed5c285c3 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -49,8 +49,7 @@ int64_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebi int64_t dist = 0; uint32_t uprice = (uint32_t)pricebits; if ( aveprice != 0 ) { - dist = (uprice - aveprice); - dist *= dist; + dist = ((int64_t)(uprice - aveprice) * (uprice - aveprice)); fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); } return(dist); From fda7fa0ef1e4e78562cac5316dbd4991ae4316fe Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:25:38 -1100 Subject: [PATCH 431/787] Test --- src/cc/games/prices.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index ed5c285c3..28c882446 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -46,10 +46,13 @@ int32_t prices_barupdate(struct prices_bar *bar,uint64_t pricebits) int64_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebits) { - int64_t dist = 0; uint32_t uprice = (uint32_t)pricebits; + int64_t a,b,dist = 0; uint32_t uprice = (uint32_t)pricebits; if ( aveprice != 0 ) { - dist = ((int64_t)(uprice - aveprice) * (uprice - aveprice)); + a = uprice; + b = aveprice; + dist = (a - b); + dist *= dist; fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); } return(dist); From b31363ad3cbbef4d6ad6c82140553c3b2c6fcfd2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:27:32 -1100 Subject: [PATCH 432/787] Test --- src/cc/games/prices.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 28c882446..9b36d6d80 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -46,14 +46,13 @@ int32_t prices_barupdate(struct prices_bar *bar,uint64_t pricebits) int64_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebits) { - int64_t a,b,dist = 0; uint32_t uprice = (uint32_t)pricebits; + int64_t a,dist = 0; if ( aveprice != 0 ) { - a = uprice; - b = aveprice; - dist = (a - b); + a = (pricebits & 0xffffffff); + dist = (a - aveprice); dist *= dist; - fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); + //fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); } return(dist); } From 1dcca1c4c1cd1da615dabed613d45bedbb6341a4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:32:09 -1100 Subject: [PATCH 433/787] Mini --- src/cc/games/prices.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 9b36d6d80..27d98b532 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -66,7 +66,7 @@ void prices_bardisp(struct prices_bar *bar) int64_t prices_blockinfo(int32_t height,char *acaddr) { - std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t prizefund = 0; int32_t i,n,vini,numvouts,iter; struct prices_bar refbar; + std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t dist,bestdist=0,prizefund = 0; int32_t mini=-1,i,n,vini,numvouts,iter; struct prices_bar refbar; if ( (pindex= komodo_chainactive(height)) != 0 ) { if ( komodo_blockload(block,pindex) == 0 ) @@ -96,11 +96,20 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) prizefund += tx.vout[0].nValue; if ( strcmp(acaddr,destaddr) == 0 ) { - fprintf(stderr,"REF "); + //fprintf(stderr,"REF "); prices_barupdate(&refbar,pricebits); } } - fprintf(stderr,"i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%lld\n",i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,(long long)prices_bardist(&refbar,aveprice,pricebits)); + else if ( strcmp(acaddr,destaddr) != 0 ) + { + dist = prices_bardist(&refbar,aveprice,pricebits); + if ( dist == 0 || dist < mindist ) + { + mindist = dist; + mini = i; + } + fprintf(stderr,"mini.%d i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%lld\n",mini,i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,(long long)dist); + } } else return(-3); } } From 5191feefd4c543b4a02d965382cf70ba4b73627f Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:32:46 -1100 Subject: [PATCH 434/787] Mindset --- src/cc/games/prices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 27d98b532..79e9d16c3 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -66,7 +66,7 @@ void prices_bardisp(struct prices_bar *bar) int64_t prices_blockinfo(int32_t height,char *acaddr) { - std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t dist,bestdist=0,prizefund = 0; int32_t mini=-1,i,n,vini,numvouts,iter; struct prices_bar refbar; + std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t dist,mindist=0,prizefund = 0; int32_t mini=-1,i,n,vini,numvouts,iter; struct prices_bar refbar; if ( (pindex= komodo_chainactive(height)) != 0 ) { if ( komodo_blockload(block,pindex) == 0 ) From 1f36dbf75fb34899c7080e988a1f79b40ecaed5f Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 01:34:24 -1100 Subject: [PATCH 435/787] Mindset --- src/cc/games/prices.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp index 79e9d16c3..5c8437e5d 100644 --- a/src/cc/games/prices.cpp +++ b/src/cc/games/prices.cpp @@ -66,7 +66,7 @@ void prices_bardisp(struct prices_bar *bar) int64_t prices_blockinfo(int32_t height,char *acaddr) { - std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t dist,mindist=0,prizefund = 0; int32_t mini=-1,i,n,vini,numvouts,iter; struct prices_bar refbar; + std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t dist,mindist=(1LL<<60),prizefund = 0; int32_t mini=-1,i,n,vini,numvouts,iter; struct prices_bar refbar; if ( (pindex= komodo_chainactive(height)) != 0 ) { if ( komodo_blockload(block,pindex) == 0 ) @@ -103,7 +103,7 @@ int64_t prices_blockinfo(int32_t height,char *acaddr) else if ( strcmp(acaddr,destaddr) != 0 ) { dist = prices_bardist(&refbar,aveprice,pricebits); - if ( dist == 0 || dist < mindist ) + if ( dist < mindist ) { mindist = dist; mini = i; From 394b00366e05193a4e98cc0d1c8f79acfa091f5a Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Thu, 28 Mar 2019 20:34:55 +0800 Subject: [PATCH 436/787] fixed! --- src/cc/rewards.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 152842bbd..bc9464fb8 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -257,9 +257,10 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t } if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) return eval->Invalid("txfee is too high"); + amount = vinTx.vout[0].nValue; reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit); if ( reward == 0 ) - return eval->Invalid("no elegible rewards"); + return eval->Invalid("no eligible rewards"); if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) { if ( tx.vout[1].nValue != 10000 ) @@ -284,7 +285,6 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t return eval->Invalid("unlock tx vout.1 is CC output"); else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey ) return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey"); - amount = vinTx.vout[0].nValue; if ( RewardsExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue,sbits,fundingtxid) == 0 ) return false; else if ( tx.vout[1].nValue > amount+reward ) From 21a8da86675b4e44fde97c69a7dc18605d8e80c8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 03:11:59 -1100 Subject: [PATCH 437/787] Mineropret and cbopret --- src/bitcoind.cpp | 23 ++++++++++-- src/komodo_defs.h | 2 +- src/komodo_gateway.h | 87 ++++++++++++++++++++++++++++++++++++++++++++ src/komodo_globals.h | 3 +- src/komodo_utils.h | 24 +++++++++++- src/main.cpp | 9 +++++ src/miner.cpp | 8 +++- 7 files changed, 148 insertions(+), 8 deletions(-) diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index b2fa534a1..3c5cfe4d6 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -59,13 +59,16 @@ static bool fDaemon; #include "komodo_defs.h" #define KOMODO_ASSETCHAIN_MAXLEN 65 extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +extern uint32_t ASSETCHAINS_BLOCKTIME; +extern uint64_t ASSETCHAINS_CBOPRET; void komodo_passport_iteration(); uint64_t komodo_interestsum(); int32_t komodo_longestchain(); +void komodo_cbopretupdate(); void WaitForShutdown(boost::thread_group* threadGroup) { - bool fShutdown = ShutdownRequested(); + int32_t i; bool fShutdown = ShutdownRequested(); // Tell the main threads to shutdown. while (!fShutdown) { @@ -73,13 +76,27 @@ void WaitForShutdown(boost::thread_group* threadGroup) if ( ASSETCHAINS_SYMBOL[0] == 0 ) { komodo_passport_iteration(); - MilliSleep(10000); + for (i=0; i<10; i++) + { + fShutdown = ShutdownRequested(); + if ( fShutdown != 0 ) + break; + MilliSleep(1000); + } } else { //komodo_interestsum(); //komodo_longestchain(); - MilliSleep(20000); + if ( ASSETCHAINS_CBOPRET != 0 ) + komodo_cbopretupdate(); + for (i=0; i Mineropret; + +CScript komodo_mineropret(int32_t nHeight) +{ + CScript opret; + if ( Mineropret.size() != 0 ) + { + opret << OP_RETURN << Mineropret); + } + return(opret); +} + +int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) +{ + std::vector vopret; uint32_t pricebits[4]; int32_t i; + if ( ASSETCHAINS_CBOPRET != 0 ) + { + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() == sizeof(pricebits) ) + { + memcpy(pricebits,&Mineropret[0],sizeof(pricebits)); + fprintf(stderr,"ht.%d: t%u %.4f USD, %.4f GBP, %.4f EUR\n",nHeight,pricebits[0],(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000) + return(0); + } + return(-1); + } + return(0); +} + +cJSON *get_urljson(char *url) +{ + char *jsonstr; cJSON *json = 0; + if ( (jsonstr= issue_curl(url)) != 0 ) + { + fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); + json = cJSON_Parse(jsonstr); + free(jsonstr); + } + return(json); +} + +int32_t get_btcusd(uint32_t pricebits[4]) +{ + cJSON *pjson,*bpi,*obj; char str[512]; uint64_t btcusd = 0,btcgbp = 0,btceur = 0; + if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json")) != 0 ) + { + if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 ) + { + pricebits[0] = (uint32_t)time(NULL); + if ( (obj= jobj(bpi,(char *)"USD")) != 0 ) + { + btcusd = jdouble(obj,(char *)"rate_float") * SATOSHIDEN; + pricebits[1] = ((btcusd / 10000) & 0xffffffff); + } + if ( (obj= jobj(bpi,(char *)"GBP")) != 0 ) + { + btcgbp = jdouble(obj,(char *)"rate_float") * SATOSHIDEN; + pricebits[2] = ((btcgbp / 10000) & 0xffffffff); + } + if ( (obj= jobj(bpi,(char *)"EUR")) != 0 ) + { + btceur = jdouble(obj,(char *)"rate_float") * SATOSHIDEN; + pricebits[3] = ((btceur / 10000) & 0xffffffff); + } + } + free_json(pjson); + fprintf(stderr,"BTC/USD %.4f, BTC/GBP %.4f, BTC/EUR %.4f\n",dstr(btcusd),dstr(btcgbp),dstr(btceur)); + return(0); + } + return(-1); +} + +void komodo_cbopretupdate() +{ + uint32_t pricebits[4]; + if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) + { + if ( get_btcusd(pricebits) == 0 ) + { + Mineropret.resize(sizeof(pricebits)); + fprintf(stderr,"set pricebits\n"); + memcpy(&Mineropret[0],pricebits,sizeof(pricebits)); + } + } +} + diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 69ad6b840..097d10da0 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -50,6 +50,7 @@ int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JU std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,NOTARY_ADDRESS,WHITELIST_ADDRESS,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB; uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,NUM_NOTARIES,ASSETCHAINS_MARMARA; bool VERUS_MINTBLOCKS; +std::vector Mineropret; char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],ASSETCHAINS_USERPASS[4096],NOTARYADDRS[64][36]; uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_BEAMPORT,ASSETCHAINS_CODAPORT; @@ -66,7 +67,7 @@ int64_t MAX_MONEY = 200000000 * 100000000LL; // spec will use an op_return with CLTV at front and anything after |OP_RETURN|PUSH of rest|OPRETTYPE_TIMELOCK|script| #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff uint64_t ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF; -uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0; +uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0,ASSETCHAINS_CBOPRET=0; uint64_t ASSETCHAINS_LASTERA = 1; uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS]; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index c1f488c5c..2702772f8 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1668,7 +1668,7 @@ extern int64_t MAX_MONEY; void komodo_args(char *argv0) { extern const char *Notaries_elected1[][2]; - std::string name,addn; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256]; + std::string name,addn,hexstr; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256]; IS_KOMODO_NOTARY = GetBoolArg("-notary", false); IS_STAKED_NOTARY = GetArg("-stakednotary", -1); if ( IS_STAKED_NOTARY != -1 && IS_KOMODO_NOTARY == true ) { @@ -1810,6 +1810,16 @@ void komodo_args(char *argv0) ASSETCHAINS_BEAMPORT = GetArg("-ac_beam",0); ASSETCHAINS_CODAPORT = GetArg("-ac_coda",0); ASSETCHAINS_MARMARA = GetArg("-ac_marmara",0); + ASSETCHAINS_CBOPRET = GetArg("-ac_cbopret",0); + hexstr = GetArg("-ac_mineropret",""); + if ( hexstr.size() != 0 ) + { + Mineropret.resize(hexstr.size()/2) + decode_hex(&Mineropret,hexstr.size()/2,(char *)hexstr.c_str()); + for (i=0; i 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 ) + if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 ) { fprintf(stderr,"perc %.4f%% ac_pub=[%02x%02x%02x...] acsize.%d\n",dstr(ASSETCHAINS_COMMISSION)*100,ASSETCHAINS_OVERRIDE_PUBKEY33[0],ASSETCHAINS_OVERRIDE_PUBKEY33[1],ASSETCHAINS_OVERRIDE_PUBKEY33[2],(int32_t)ASSETCHAINS_SCRIPTPUB.size()); extraptr = extrabuf; @@ -2048,6 +2058,16 @@ void komodo_args(char *argv0) } if ( ASSETCHAINS_BLOCKTIME != 60 ) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_BLOCKTIME),(void *)&ASSETCHAINS_BLOCKTIME); + if ( ASSETCHAINS_CBOPRET != 0 ) + { + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_CBOPRET),(void *)&ASSETCHAINS_CBOPRET); + fprintf(stderr,"This blockchain uses data produced from CoinDesk Bitcoin Price Index\n"); + } + if ( Mineropret.size() != 0 ) + { + for (i=0; i 0 && (nHeight & 1) == 0 ) + { + + } + else if ( ASSETCHAINS_MINEROPRET != 0 ) + { + if ( komodo_opretvalidate(nHeight,tx.vout[1].scriptPubKey) < 0 ) + return(false); + } return(true); } diff --git a/src/miner.cpp b/src/miner.cpp index 48b8c5c62..af1f14ba3 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -155,6 +155,7 @@ CScript MarmaraCoinbaseOpret(uint8_t funcid,int32_t height,CPubKey pk); uint64_t komodo_notarypay(CMutableTransaction &txNew, std::vector &NotarisationNotaries, uint32_t timestamp, int32_t height, uint8_t *script, int32_t len); int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); int32_t komodo_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *script, int32_t len); +CScript komodo_mineropret(int32_t nHeight); CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake) { @@ -644,7 +645,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 txNew.vout[1].nValue = 0; txNew.vout[1].scriptPubKey = MarmaraCoinbaseOpret('C',nHeight,pk); } - else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0 ) + else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_MINEROPRET != 0 ||(ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0) ) { int32_t i; uint8_t *ptr; txNew.vout.resize(2); @@ -672,6 +673,11 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 ptr[34] = OP_CHECKSIG; //fprintf(stderr," set ASSETCHAINS_OVERRIDE_PUBKEY33 into vout[1]\n"); } + if ( ASSETCHAINS_MINEROPRET != 0 ) + { + txNew.vout.resize(txNew.size()+1); + txNew.vout[txNew.size()-1].scriptPubKey = komodo_mineropret(nHeight); + } //printf("autocreate commision vout\n"); } else if ( (uint64_t)(txNew.vout[0].nValue) >= ASSETCHAINS_TIMELOCKGTE) From ca1ee9a8c1846fe6e7ce714785ec57793158ee68 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 03:18:06 -1100 Subject: [PATCH 438/787] syntax --- src/komodo_gateway.h | 6 ++---- src/komodo_utils.h | 4 ++-- src/main.cpp | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 46fc1d805..26443d41f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1554,9 +1554,7 @@ CScript komodo_mineropret(int32_t nHeight) { CScript opret; if ( Mineropret.size() != 0 ) - { - opret << OP_RETURN << Mineropret); - } + return(opret << OP_RETURN << Mineropret); return(opret); } @@ -1569,7 +1567,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( vopret.size() == sizeof(pricebits) ) { memcpy(pricebits,&Mineropret[0],sizeof(pricebits)); - fprintf(stderr,"ht.%d: t%u %.4f USD, %.4f GBP, %.4f EUR\n",nHeight,pricebits[0],(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000) + fprintf(stderr,"ht.%d: t%u %.4f USD, %.4f GBP, %.4f EUR\n",nHeight,pricebits[0],(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000); return(0); } return(-1); diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 2702772f8..fa3278fda 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1814,8 +1814,8 @@ void komodo_args(char *argv0) hexstr = GetArg("-ac_mineropret",""); if ( hexstr.size() != 0 ) { - Mineropret.resize(hexstr.size()/2) - decode_hex(&Mineropret,hexstr.size()/2,(char *)hexstr.c_str()); + Mineropret.resize(hexstr.size()/2); + decode_hex(&Mineropret[0],hexstr.size()/2,(char *)hexstr.c_str()); for (i=0; i Date: Thu, 28 Mar 2019 03:20:00 -1100 Subject: [PATCH 439/787] issue_curl --- src/komodo_gateway.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 26443d41f..0e687c3d4 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1575,6 +1575,8 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) return(0); } +#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) + cJSON *get_urljson(char *url) { char *jsonstr; cJSON *json = 0; From 99c55801a3b0006b93152364254e0815d1140ebd Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 03:23:14 -1100 Subject: [PATCH 440/787] vout.size() --- src/miner.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index af1f14ba3..f79602d90 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -645,7 +645,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 txNew.vout[1].nValue = 0; txNew.vout[1].scriptPubKey = MarmaraCoinbaseOpret('C',nHeight,pk); } - else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_MINEROPRET != 0 ||(ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0) ) + else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_CBOPRET != 0 ||(ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0) ) { int32_t i; uint8_t *ptr; txNew.vout.resize(2); @@ -673,10 +673,10 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 ptr[34] = OP_CHECKSIG; //fprintf(stderr," set ASSETCHAINS_OVERRIDE_PUBKEY33 into vout[1]\n"); } - if ( ASSETCHAINS_MINEROPRET != 0 ) + if ( ASSETCHAINS_CBOPRET != 0 ) { - txNew.vout.resize(txNew.size()+1); - txNew.vout[txNew.size()-1].scriptPubKey = komodo_mineropret(nHeight); + txNew.vout.resize(txNew.vout.size()+1); + txNew.vout[txNew.vout.size()-1].scriptPubKey = komodo_mineropret(nHeight); } //printf("autocreate commision vout\n"); } From f28b62c766e59259346ee41288647a9076a7ab3b Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 03:42:23 -1100 Subject: [PATCH 441/787] Skop genesis --- src/komodo_gateway.h | 5 +++-- src/main.cpp | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 0e687c3d4..12e7cc4c2 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1560,14 +1560,15 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; uint32_t pricebits[4]; int32_t i; + std::vector vopret; uint32_t pricebits[4]; int32_t i,lag; if ( ASSETCHAINS_CBOPRET != 0 ) { GetOpReturnData(scriptPubKey,vopret); if ( vopret.size() == sizeof(pricebits) ) { memcpy(pricebits,&Mineropret[0],sizeof(pricebits)); - fprintf(stderr,"ht.%d: t%u %.4f USD, %.4f GBP, %.4f EUR\n",nHeight,pricebits[0],(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000); + lag = (int32_t)(time(NULL) - pricebits[0]); + fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000); return(0); } return(-1); diff --git a/src/main.cpp b/src/main.cpp index d6993d203..b73a31856 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1018,9 +1018,9 @@ bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeigh { } - else if ( ASSETCHAINS_CBOPRET != 0 ) + else if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 && tx.vout.size() > 0 ) { - if ( komodo_opretvalidate(nHeight,tx.vout[1].scriptPubKey) < 0 ) + if ( komodo_opretvalidate(nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 ) return(false); } return(true); From dd3704e2b06b4701e4c745e109d4b313556a831d Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 03:45:21 -1100 Subject: [PATCH 442/787] -print --- src/komodo_gateway.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 12e7cc4c2..d6e308229 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1583,7 +1583,7 @@ cJSON *get_urljson(char *url) char *jsonstr; cJSON *json = 0; if ( (jsonstr= issue_curl(url)) != 0 ) { - fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); + //fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); json = cJSON_Parse(jsonstr); free(jsonstr); } @@ -1629,7 +1629,6 @@ void komodo_cbopretupdate() if ( get_btcusd(pricebits) == 0 ) { Mineropret.resize(sizeof(pricebits)); - fprintf(stderr,"set pricebits\n"); memcpy(&Mineropret[0],pricebits,sizeof(pricebits)); } } From eab69d1d69175f23fa8ea141d80dcbbaca0e2c74 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 03:49:25 -1100 Subject: [PATCH 443/787] Reorder coinbase opret checks --- src/main.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index b73a31856..da1730696 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -980,7 +980,16 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeight) { // if time locks are on, ensure that this coin base is time locked exactly as it should be - if (((uint64_t)(tx.GetValueOut()) >= ASSETCHAINS_TIMELOCKGTE) || + if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) + { + + } + else if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 && tx.vout.size() > 0 ) + { + if ( komodo_opretvalidate(nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 ) + return(false); + } + else if (((uint64_t)(tx.GetValueOut()) >= ASSETCHAINS_TIMELOCKGTE) || (((nHeight >= 31680) || strcmp(ASSETCHAINS_SYMBOL, "VRSC") != 0) && komodo_ac_block_subsidy(nHeight) >= ASSETCHAINS_TIMELOCKGTE)) { CScriptID scriptHash; @@ -1014,15 +1023,6 @@ bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeigh } return(false); } - else if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) - { - - } - else if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 && tx.vout.size() > 0 ) - { - if ( komodo_opretvalidate(nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 ) - return(false); - } return(true); } From c2a73508a8e1670e1440e3bdd841b5ae394d3563 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 03:53:23 -1100 Subject: [PATCH 444/787] +print --- src/komodo_gateway.h | 2 +- src/main.cpp | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d6e308229..4eee1a300 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1570,7 +1570,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) lag = (int32_t)(time(NULL) - pricebits[0]); fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000); return(0); - } + } else fprintf(stderr,"wrong size %d vs %d\n",(int32_t)vopret.size(),(int32_t)sizeof(pricebits)); return(-1); } return(0); diff --git a/src/main.cpp b/src/main.cpp index da1730696..b73a31856 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -980,16 +980,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeight) { // if time locks are on, ensure that this coin base is time locked exactly as it should be - if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) - { - - } - else if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 && tx.vout.size() > 0 ) - { - if ( komodo_opretvalidate(nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 ) - return(false); - } - else if (((uint64_t)(tx.GetValueOut()) >= ASSETCHAINS_TIMELOCKGTE) || + if (((uint64_t)(tx.GetValueOut()) >= ASSETCHAINS_TIMELOCKGTE) || (((nHeight >= 31680) || strcmp(ASSETCHAINS_SYMBOL, "VRSC") != 0) && komodo_ac_block_subsidy(nHeight) >= ASSETCHAINS_TIMELOCKGTE)) { CScriptID scriptHash; @@ -1023,6 +1014,15 @@ bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeigh } return(false); } + else if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) + { + + } + else if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 && tx.vout.size() > 0 ) + { + if ( komodo_opretvalidate(nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 ) + return(false); + } return(true); } From f6acb611122551d7a096ac18d5d859c7556c6cc8 Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Thu, 28 Mar 2019 15:57:16 +0100 Subject: [PATCH 445/787] Updated ChannelsCC validation (#20) - More constrained vins/vouts - Fixed CC marker value - Added validation of ChannelsOpen in all tx - Switched to LOGSTREAM for logging --- src/cc/channels.cpp | 284 ++++++++++++++++++++++---------------------- 1 file changed, 142 insertions(+), 142 deletions(-) diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp index 504ba629a..08dedb3f9 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -62,6 +62,8 @@ Possible third iteration: // start of consensus code +#define CC_MARKER_VALUE 10000 + int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey srcpub, CPubKey destpub,int32_t v) { char destaddr[65],channeladdr[65],tokenschanneladdr[65]; @@ -126,8 +128,8 @@ uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint2 { return(f); } - } else fprintf(stderr,"script[0] %02x != EVAL_CHANNELS\n",script[0]); - } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size()); + } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "script[0] " << script[0] << " != EVAL_CHANNELS" << std::endl); + } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "not enough opret.[" << vopret.size() << "]" << std::endl); return(0); } @@ -167,7 +169,7 @@ bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti } if ( inputs != outputs ) { - fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); + LOGSTREAM("channelscc",CCLOG_INFO, stream << "inputs " << inputs << " vs outputs " << outputs << std::endl); return eval->Invalid("mismatched inputs != outputs"); } else return (true); @@ -183,7 +185,7 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numpayments,p1,param1; bool retval; uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain,tokenid; - uint8_t funcid,hash[32],hashdest[32]; + uint8_t funcid,hash[32],hashdest[32]; char channeladdress[65],srcmarker[65],destmarker[65],destaddr[65],srcaddr[65],desttokensaddr[65],srctokensaddr[65]; int64_t p2,param2,payment; CPubKey srcpub, destpub; CTransaction channelOpenTx,channelCloseTx,prevTx; @@ -202,9 +204,20 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & else { txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); + memcpy(hash,&txid,sizeof(hash)); if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, opentxid, srcpub, destpub, param1, param2, param3)) != 0) { + if (myGetTransaction(opentxid,channelOpenTx,hashblock)== 0) + return eval->Invalid("invalid channelopen tx!"); + else if ((numvouts=channelOpenTx.vout.size()) > 0 && (DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 'O') + return eval->Invalid("invalid channelopen OP_RETURN data!"); + GetCCaddress1of2(cp,channeladdress,srcpub,destpub); + GetCCaddress(cp,srcmarker,srcpub); + GetCCaddress(cp,destmarker,destpub); + Getscriptaddress(srcaddr,CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG); + Getscriptaddress(destaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); + _GetCCaddress(srctokensaddr,EVAL_TOKENS,srcpub); + _GetCCaddress(desttokensaddr,EVAL_TOKENS,destpub); switch ( funcid ) { case 'O': @@ -225,56 +238,53 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //vout.3: normal output of payment amount to receiver pubkey //vout.n-2: normal change //vout.n-1: opreturn - 'P' opentxid senderspubkey receiverspubkey depth numpayments secret - if (komodo_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelPayment!"); + return eval->Invalid("vin.0 is normal for channelpayment!"); else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for channelPayment!"); + return eval->Invalid("vin.1 is CC for channelpayment!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for channelPayment!"); - else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition()==0 ) - return eval->Invalid("vout.0 is CC for channelPayment!"); - else if ( IsChannelsMarkervout(cp,tx,srcpub,1)==0 ) - return eval->Invalid("vout.1 is CC for channelPayment (marker to srcPub)!"); - else if ( IsChannelsMarkervout(cp,tx,destpub,2)==0 ) - return eval->Invalid("vout.2 is CC for channelPayment (marker to dstPub)!"); - else if ( tokenid!=zeroid && tx.vout[3].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.3 is CC for channelPayment!"); - else if ( tokenid==zeroid && tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) - return eval->Invalid("vout.3 is normal for channelPayment!"); - else if ( tokenid!=zeroid && tx.vout[3].scriptPubKey!=MakeCC1vout(EVAL_TOKENS,tx.vout[3].nValue,destpub).scriptPubKey) - return eval->Invalid("payment funds do not go to receiver!"); - else if ( tokenid==zeroid && tx.vout[3].scriptPubKey!=CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG) - return eval->Invalid("payment funds do not go to receiver!"); + return eval->Invalid("vin.2 is CC for channelpayment!"); + else if ( ConstrainVout(tx.vout[0],1,channeladdress,(numpayments-param2)*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid CC change amount for channelpayment!"); + else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelpayment!"); + else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelpayment!"); + else if ( tokenid!=zeroid && ConstrainVout(tx.vout[3],1,desttokensaddr,param2*payment)==0 ) + return eval->Invalid("vout.3 is CC or invalid amount or invalid receiver for channelpayment!"); + else if ( tokenid==zeroid && ConstrainVout(tx.vout[3],0,destaddr,param2*payment)==0 ) + return eval->Invalid("vout.3 is normal or invalid amount or invalid receiver for channelpayment!"); else if ( param1 > CHANNELS_MAXPAYMENTS) return eval->Invalid("too many payment increments!"); else - { - if (myGetTransaction(opentxid,channelOpenTx,hashblock) != 0) + { + endiancpy(hash, (uint8_t * ) & param3, 32); + for (i = 0; i < numpayments-param1; i++) { - if ((numvouts=channelOpenTx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 0 && funcid!='O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - endiancpy(hash, (uint8_t * ) & param3, 32); - for (i = 0; i < numpayments-param1; i++) - { - vcalc_sha256(0, hashdest, hash, 32); - memcpy(hash, hashdest, 32); - } - endiancpy((uint8_t*)&genhashchain,hashdest,32); - if (hashchain!=genhashchain) - return eval->Invalid("invalid secret for payment, does not reach final hashchain!"); - else if (tx.vout[3].nValue != param2*payment) - return eval->Invalid("vout amount does not match number_of_payments*payment!"); + vcalc_sha256(0, hashdest, hash, 32); + memcpy(hash, hashdest, 32); } + endiancpy((uint8_t*)&genhashchain,hashdest,32); + if (hashchain!=genhashchain) + return eval->Invalid("invalid secret for payment, does not reach final hashchain!"); + else if (tx.vout[3].nValue != param2*payment) + return eval->Invalid("vout amount does not match number_of_payments*payment!"); if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[1].scriptPubKey != prevTx.vout[1].scriptPubKey) - return eval->Invalid("invalid destination for sender marker!"); - else if (tx.vout[2].scriptPubKey != prevTx.vout[2].scriptPubKey) - return eval->Invalid("invalid destination for receiver marker!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelpayment!"); else if (param1+param2!=p1) return eval->Invalid("invalid payment depth!"); else if (tx.vout[3].nValue > prevTx.vout[0].nValue) @@ -290,37 +300,37 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //vout.1: CC vout marker to senders pubKey //vout.2: CC vout marker to receiver pubkey //vout.n-2: normal change - //vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey 0 0 0 - if (komodo_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + //vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey numpayments payment 0 + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelClose!"); + return eval->Invalid("vin.0 is normal for channelclose!"); else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for channelClose!"); + return eval->Invalid("vin.1 is CC for channelclose!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for channelClose!"); - else if ( IsChannelsvout(cp,tx,srcpub,destpub,0)==0 ) - return eval->Invalid("vout.0 is CC for channelClose!"); - else if ( IsChannelsMarkervout(cp,tx,srcpub,1)==0 ) - return eval->Invalid("vout.1 is CC for channelClose (marker to srcPub)!"); - else if ( IsChannelsMarkervout(cp,tx,destpub,2)==0 ) - return eval->Invalid("vout.2 is CC for channelClose (marker to dstPub)!"); + return eval->Invalid("vin.2 is CC for channelclose!"); + else if ( ConstrainVout(tx.vout[0],1,channeladdress,0)==0 ) + return eval->Invalid("vout.0 is CC for channelclose!"); + else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelclose!"); + else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelclose!"); else if ( param1 > CHANNELS_MAXPAYMENTS) return eval->Invalid("too many payment increments!"); - else if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - return eval->Invalid("invalid open txid!"); - else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - else if (tx.vout[0].nValue != param1*payment) - return eval->Invalid("vout amount does not match number_of_payments*payment!"); else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[1].scriptPubKey != prevTx.vout[1].scriptPubKey) - return eval->Invalid("invalid destination for sender marker!"); - else if (tx.vout[2].scriptPubKey != prevTx.vout[2].scriptPubKey) - return eval->Invalid("invalid destination for receiver marker!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelclose!"); else if (tx.vout[0].nValue != prevTx.vout[0].nValue) return eval->Invalid("invalid CC amount, amount must match funds in channel"); } @@ -334,50 +344,40 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //vout.2: normal output of CC input to senders pubkey //vout.n-2: normal change //vout.n-1: opreturn - 'R' opentxid senderspubkey receiverspubkey numpayments payment closetxid - if (komodo_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); else if (komodo_txnotarizedconfirmed(param3) == 0) return eval->Invalid("channelClose is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelRefund!"); + return eval->Invalid("vin.0 is normal for channelrefund!"); else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for channelRefund!"); + return eval->Invalid("vin.1 is CC for channelrefund!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for channelRefund!"); - else if ( IsChannelsMarkervout(cp,tx,srcpub,0)==0 ) - return eval->Invalid("vout.0 is CC for channelRefund (marker to srcPub)!"); - else if ( IsChannelsMarkervout(cp,tx,destpub,1)==0 ) - return eval->Invalid("vout.1 is CC for channelRefund (marker to dstPub)!"); - else if ( tokenid!=zeroid && tx.vout[2].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.2 is CC for channelPayment!"); - else if ( tokenid==zeroid && tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) - return eval->Invalid("vout.2 is normal for channelPayment!"); - else if ( tokenid!=zeroid && tx.vout[2].scriptPubKey!=MakeCC1vout(EVAL_TOKENS,tx.vout[2].nValue,srcpub).scriptPubKey) - return eval->Invalid("payment funds do not go to sender!"); - else if ( tokenid==zeroid && tx.vout[2].scriptPubKey!=CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG) - return eval->Invalid("payment funds do not go to sender!"); + return eval->Invalid("vin.2 is CC for channelrefund!"); + else if ( ConstrainVout(tx.vout[0],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.0 is CC marker to srcpub or invalid amount for channelrefund!"); + else if ( ConstrainVout(tx.vout[1],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to destpub or invalid amount for channelrefund!"); + else if ( tokenid!=zeroid && ConstrainVout(tx.vout[2],1,srctokensaddr,param1*payment)==0 ) + return eval->Invalid("vout.2 is CC or invalid amount or invalid receiver for channelrefund!"); + else if ( tokenid==zeroid && ConstrainVout(tx.vout[2],0,srcaddr,param1*payment)==0 ) + return eval->Invalid("vout.2 is normal or invalid amount or invalid receiver for channelrefund!"); else if ( param1 > CHANNELS_MAXPAYMENTS) return eval->Invalid("too many payment increments!"); - else if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - return eval->Invalid("invalid open txid!"); - else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - else if (myGetTransaction(param3,channelCloseTx,hashblock) == 0) - return eval->Invalid("invalid close txid!"); - else if ((numvouts=channelCloseTx.vout.size()) > 0 && DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, param1, param2, param3) != 'C') - return eval->Invalid("invalid channelclose OP_RETURN data!"); - else if (tmp_txid!=opentxid) - return eval->Invalid("invalid close tx, opentxid do not match on close and refund!"); - else if (tx.vout[2].nValue != param1*payment) - return eval->Invalid("vout amount does not match number_of_payments*payment!"); else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[0].scriptPubKey != prevTx.vout[1].scriptPubKey) - return eval->Invalid("invalid destination for sender marker!"); - else if (tx.vout[1].scriptPubKey != prevTx.vout[2].scriptPubKey) - return eval->Invalid("invalid destination for receiver marker!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelrefund!"); else if (tx.vout[2].nValue != prevTx.vout[0].nValue) return eval->Invalid("invalid amount, refund amount and funds in channel must match!"); } @@ -390,8 +390,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & else return eval->Invalid("unexpected channels missing funcid"); retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); if ( retval != 0 ) - fprintf(stderr,"Channel tx validated\n"); - else fprintf(stderr,"Channel tx invalid\n"); + LOGSTREAM("channels",CCLOG_INFO, stream << "Channels tx validated" << std::endl); + else fprintf(stderr,"Channels tx invalid\n"); return(retval); } } @@ -415,7 +415,7 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C } else { - fprintf(stderr,"invalid channel open txid\n"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << "invalid channel open txid" << std::endl); return 0; } if (srcpub==mypk) marker=1; @@ -473,7 +473,7 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS ) { CCerror = strprintf("invalid ChannelOpen param numpayments.%d max.%d payment.%lld\n",numpayments,CHANNELS_MAXPAYMENTS,(long long)payment); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } cp = CCinit(&C,EVAL_CHANNELS); @@ -484,11 +484,11 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 funds = numpayments * payment; if (tokenid!=zeroid) { - amount=AddNormalinputs(mtx,mypk,3*txfee,5); + amount=AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,5); tokens=AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, funds, 64); } - else amount=AddNormalinputs(mtx,mypk,funds+3*txfee,64); - if (amount+tokens >= funds+2*txfee) + else amount=AddNormalinputs(mtx,mypk,funds+txfee+2*CC_MARKER_VALUE,64); + if (amount+tokens >= funds+txfee+2*CC_MARKER_VALUE) { hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); endiancpy(hash,(uint8_t *)&hentropy,32); @@ -500,13 +500,13 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 endiancpy((uint8_t *)&hashchain,hashdest,32); if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid && tokens>funds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',tokenid,zeroid,mypk,destpub,numpayments,payment,hashchain))); } CCerror = strprintf("error adding funds"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -526,7 +526,7 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel open txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain)=='O') @@ -534,23 +534,23 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (mypk != srcpub && mypk != destpub) { CCerror = strprintf("this is not our channel"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } else if (amount % payment != 0 || amount 0) + if (AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0) { @@ -562,12 +562,12 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (numpayments > prevdepth) { CCerror = strprintf("not enough funds in channel for that amount"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } else if (numpayments == 0) { CCerror = strprintf("invalid amount"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (secret!=zeroid) @@ -582,7 +582,7 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (gensecret!=hashchain) { CCerror = strprintf("invalid secret supplied"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } @@ -605,13 +605,13 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 else { CCerror = strprintf("invalid previous tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,srcpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,srcpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, amount, destpub)); else mtx.vout.push_back(CTxOut(amount, CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG)); return (FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', tokenid, opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret))); @@ -619,12 +619,12 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 else { CCerror = strprintf("error adding CC inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } CCerror = strprintf("error adding normal inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -645,40 +645,40 @@ std::string ChannelClose(uint64_t txfee,uint256 opentxid) if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel open txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O') { CCerror = strprintf("invalid channel open tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (mypk != srcpub) { CCerror = strprintf("cannot close, you are not channel owner"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0 ) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',tokenid,opentxid,mypk,destpub,funds/payment,payment,zeroid))); } else { CCerror = strprintf("error adding CC inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } CCerror = strprintf("error adding normal inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -699,48 +699,48 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) if (GetTransaction(closetxid,channelCloseTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel close txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,param1,param2,param3)!='C') { CCerror = strprintf("invalid channel close tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (txid!=opentxid) { CCerror = strprintf("open and close txid are not from same channel"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel open txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain)!='O') { CCerror = strprintf("invalid channel open tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (mypk != srcpub) { CCerror = strprintf("cannot refund, you are not the channel owner"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0 ) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { if ((GetTransaction(prevtxid,prevTx,hashblock,false) != 0) && (numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3) != 0) { - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,funds,mypk)); else mtx.vout.push_back(CTxOut(funds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',tokenid,opentxid,mypk,destpub,funds/payment,payment,closetxid))); @@ -748,19 +748,19 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) else { CCerror = strprintf("previous tx is invalid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } else { CCerror = strprintf("error adding CC inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } CCerror = strprintf("error adding normal inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -781,7 +781,7 @@ UniValue ChannelsList() txid = it->first.txhash; vout = (int32_t)it->first.index; nValue = (int64_t)it->second; - if ( (vout == 1 || vout == 2) && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) + if ( (vout == 1 || vout == 2) && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) { if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O') { From fb5b955a955e755890092066a47e293dd5aa6bea Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 03:57:28 -1100 Subject: [PATCH 446/787] +print --- src/komodo_gateway.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 4eee1a300..a6aa1b6f8 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1630,6 +1630,7 @@ void komodo_cbopretupdate() { Mineropret.resize(sizeof(pricebits)); memcpy(&Mineropret[0],pricebits,sizeof(pricebits)); + fprintf(stderr,"set Mineropret[%d]\n",(int32_t)Mineropret.size()); } } } From 451af129c709cd3aefdc7f4403b1d1c46853c9b8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 04:02:27 -1100 Subject: [PATCH 447/787] Est --- src/komodo_gateway.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a6aa1b6f8..4a81624ac 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1566,11 +1566,11 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) GetOpReturnData(scriptPubKey,vopret); if ( vopret.size() == sizeof(pricebits) ) { - memcpy(pricebits,&Mineropret[0],sizeof(pricebits)); + memcpy(pricebits,&vpopret[0],sizeof(pricebits)); lag = (int32_t)(time(NULL) - pricebits[0]); fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000); return(0); - } else fprintf(stderr,"wrong size %d vs %d\n",(int32_t)vopret.size(),(int32_t)sizeof(pricebits)); + } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)sizeof(pricebits),(int32_t)scriptPubKey.size(),scriptPubKey[0]); return(-1); } return(0); @@ -1628,7 +1628,8 @@ void komodo_cbopretupdate() { if ( get_btcusd(pricebits) == 0 ) { - Mineropret.resize(sizeof(pricebits)); + if ( Mineropret.size() != sizeof(pricebits) ) + Mineropret.resize(sizeof(pricebits)); memcpy(&Mineropret[0],pricebits,sizeof(pricebits)); fprintf(stderr,"set Mineropret[%d]\n",(int32_t)Mineropret.size()); } From 6dcc38b76545713979ca69eba7ae9595d322c8ee Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 04:03:44 -1100 Subject: [PATCH 448/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 4a81624ac..2c78383df 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1566,7 +1566,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) GetOpReturnData(scriptPubKey,vopret); if ( vopret.size() == sizeof(pricebits) ) { - memcpy(pricebits,&vpopret[0],sizeof(pricebits)); + memcpy(pricebits,&vopret[0],sizeof(pricebits)); lag = (int32_t)(time(NULL) - pricebits[0]); fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000); return(0); From 54ab6c61a174c7dc19079e2972fe0fc9d0f586b4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 04:05:15 -1100 Subject: [PATCH 449/787] Height --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 2c78383df..7e6deb57d 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1568,7 +1568,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { memcpy(pricebits,&vopret[0],sizeof(pricebits)); lag = (int32_t)(time(NULL) - pricebits[0]); - fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000); + fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1)); return(0); } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)sizeof(pricebits),(int32_t)scriptPubKey.size(),scriptPubKey[0]); return(-1); From 3ba62bf865fca436f3165fb899dbaa4da4ca4512 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 04:07:39 -1100 Subject: [PATCH 450/787] Test --- src/komodo_gateway.h | 4 +++- src/komodo_utils.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7e6deb57d..9391406dc 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1631,7 +1631,9 @@ void komodo_cbopretupdate() if ( Mineropret.size() != sizeof(pricebits) ) Mineropret.resize(sizeof(pricebits)); memcpy(&Mineropret[0],pricebits,sizeof(pricebits)); - fprintf(stderr,"set Mineropret[%d]\n",(int32_t)Mineropret.size()); + int32_t i; for (i=0; i Date: Thu, 28 Mar 2019 04:08:51 -1100 Subject: [PATCH 451/787] +print --- src/komodo_gateway.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 9391406dc..63ada8787 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1554,7 +1554,10 @@ CScript komodo_mineropret(int32_t nHeight) { CScript opret; if ( Mineropret.size() != 0 ) + { + fprintf(stderr,"use Mineropret[%d]\n",(int32_t)Mineropret.size()); return(opret << OP_RETURN << Mineropret); + } return(opret); } From 4b62adba68f5603fea7dd04a28e9f2faf7059d8b Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 04:15:53 -1100 Subject: [PATCH 452/787] Test --- src/komodo_utils.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/komodo_utils.h b/src/komodo_utils.h index d072f2dd5..a5a2eeb2c 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -2059,17 +2059,17 @@ void komodo_args(char *argv0) } if ( ASSETCHAINS_BLOCKTIME != 60 ) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_BLOCKTIME),(void *)&ASSETCHAINS_BLOCKTIME); + if ( Mineropret.size() != 0 ) + { + for (i=0; i Date: Thu, 28 Mar 2019 04:23:13 -1100 Subject: [PATCH 453/787] Fix miner --- src/komodo_utils.h | 2 +- src/miner.cpp | 48 +++++++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/komodo_utils.h b/src/komodo_utils.h index a5a2eeb2c..1bc379f42 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -2067,7 +2067,7 @@ void komodo_args(char *argv0) if ( ASSETCHAINS_CBOPRET != 0 ) { extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_CBOPRET),(void *)&ASSETCHAINS_CBOPRET); - komodo_cbopretupdate(); + komodo_cbopretupdate(); // will set Mineropret fprintf(stderr,"This blockchain uses data produced from CoinDesk Bitcoin Price Index\n"); } } diff --git a/src/miner.cpp b/src/miner.cpp index f79602d90..31a6b3f9d 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -648,37 +648,41 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_CBOPRET != 0 ||(ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0) ) { int32_t i; uint8_t *ptr; - txNew.vout.resize(2); - txNew.vout[1].nValue = commission; - if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) + if ( commission != 0 ) { - //fprintf(stderr,"mine to -ac_script\n"); - //txNew.vout[1].scriptPubKey = CScript() << ParseHex(); - int32_t len = strlen(ASSETCHAINS_SCRIPTPUB.c_str()); - len >>= 1; - txNew.vout[1].scriptPubKey.resize(len); - ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; - decode_hex(ptr,len,(char *)ASSETCHAINS_SCRIPTPUB.c_str()); - } - else - { - txNew.vout[1].scriptPubKey.resize(35); - ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; - ptr[0] = 33; - for (i=0; i<33; i++) + fprintf(stderr,"nonzero commission %.8f\n",(double)commission/COIN); + txNew.vout.resize(2); + txNew.vout[1].nValue = commission; + if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) { - ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i]; - //fprintf(stderr,"%02x",ptr[i+1]); + //fprintf(stderr,"mine to -ac_script\n"); + //txNew.vout[1].scriptPubKey = CScript() << ParseHex(); + int32_t len = strlen(ASSETCHAINS_SCRIPTPUB.c_str()); + len >>= 1; + txNew.vout[1].scriptPubKey.resize(len); + ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; + decode_hex(ptr,len,(char *)ASSETCHAINS_SCRIPTPUB.c_str()); + } + else + { + txNew.vout[1].scriptPubKey.resize(35); + ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; + ptr[0] = 33; + for (i=0; i<33; i++) + { + ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i]; + //fprintf(stderr,"%02x",ptr[i+1]); + } + ptr[34] = OP_CHECKSIG; + //fprintf(stderr," set ASSETCHAINS_OVERRIDE_PUBKEY33 into vout[1]\n"); } - ptr[34] = OP_CHECKSIG; - //fprintf(stderr," set ASSETCHAINS_OVERRIDE_PUBKEY33 into vout[1]\n"); } if ( ASSETCHAINS_CBOPRET != 0 ) { txNew.vout.resize(txNew.vout.size()+1); txNew.vout[txNew.vout.size()-1].scriptPubKey = komodo_mineropret(nHeight); } - //printf("autocreate commision vout\n"); + printf("autocreate commision/cbopret.%lld vout[%d]\n",(long long)ASSETCHAINS_CBOPRET,(int32_t)txNew.vout.size()); } else if ( (uint64_t)(txNew.vout[0].nValue) >= ASSETCHAINS_TIMELOCKGTE) { From 99527844f38241a47a61043602cdc5671643b928 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 04:27:46 -1100 Subject: [PATCH 454/787] Fix miner --- src/miner.cpp | 62 ++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/src/miner.cpp b/src/miner.cpp index 31a6b3f9d..69580da7b 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -645,44 +645,35 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 txNew.vout[1].nValue = 0; txNew.vout[1].scriptPubKey = MarmaraCoinbaseOpret('C',nHeight,pk); } - else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_CBOPRET != 0 ||(ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0) ) + else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0 ) { int32_t i; uint8_t *ptr; - if ( commission != 0 ) + txNew.vout.resize(2); + txNew.vout[1].nValue = commission; + if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) { - fprintf(stderr,"nonzero commission %.8f\n",(double)commission/COIN); - txNew.vout.resize(2); - txNew.vout[1].nValue = commission; - if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) - { - //fprintf(stderr,"mine to -ac_script\n"); - //txNew.vout[1].scriptPubKey = CScript() << ParseHex(); - int32_t len = strlen(ASSETCHAINS_SCRIPTPUB.c_str()); - len >>= 1; - txNew.vout[1].scriptPubKey.resize(len); - ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; - decode_hex(ptr,len,(char *)ASSETCHAINS_SCRIPTPUB.c_str()); - } - else - { - txNew.vout[1].scriptPubKey.resize(35); - ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; - ptr[0] = 33; - for (i=0; i<33; i++) - { - ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i]; - //fprintf(stderr,"%02x",ptr[i+1]); - } - ptr[34] = OP_CHECKSIG; - //fprintf(stderr," set ASSETCHAINS_OVERRIDE_PUBKEY33 into vout[1]\n"); - } + //fprintf(stderr,"mine to -ac_script\n"); + //txNew.vout[1].scriptPubKey = CScript() << ParseHex(); + int32_t len = strlen(ASSETCHAINS_SCRIPTPUB.c_str()); + len >>= 1; + txNew.vout[1].scriptPubKey.resize(len); + ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; + decode_hex(ptr,len,(char *)ASSETCHAINS_SCRIPTPUB.c_str()); } - if ( ASSETCHAINS_CBOPRET != 0 ) + else { - txNew.vout.resize(txNew.vout.size()+1); - txNew.vout[txNew.vout.size()-1].scriptPubKey = komodo_mineropret(nHeight); + txNew.vout[1].scriptPubKey.resize(35); + ptr = (uint8_t *)&txNew.vout[1].scriptPubKey[0]; + ptr[0] = 33; + for (i=0; i<33; i++) + { + ptr[i+1] = ASSETCHAINS_OVERRIDE_PUBKEY33[i]; + //fprintf(stderr,"%02x",ptr[i+1]); + } + ptr[34] = OP_CHECKSIG; + //fprintf(stderr," set ASSETCHAINS_OVERRIDE_PUBKEY33 into vout[1]\n"); } - printf("autocreate commision/cbopret.%lld vout[%d]\n",(long long)ASSETCHAINS_CBOPRET,(int32_t)txNew.vout.size()); + //printf("autocreate commision vout\n"); } else if ( (uint64_t)(txNew.vout[0].nValue) >= ASSETCHAINS_TIMELOCKGTE) { @@ -733,7 +724,12 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 fprintf(stderr, "Created notary payment coinbase totalsat.%lu\n",totalsats); } else fprintf(stderr, "vout 2 of notarisation is not OP_RETURN scriptlen.%i\n", scriptlen); } - + if ( ASSETCHAINS_CBOPRET != 0 ) + { + txNew.vout.resize(txNew.vout.size()+1); + txNew.vout[txNew.vout.size()-1].scriptPubKey = komodo_mineropret(nHeight); + printf("autocreate commision/cbopret.%lld vout[%d]\n",(long long)ASSETCHAINS_CBOPRET,(int32_t)txNew.vout.size()); + } pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; From 7ecb2d8b5daae27acbb8094de694956214080b95 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 04:32:07 -1100 Subject: [PATCH 455/787] Test --- src/komodo_gateway.h | 2 +- src/miner.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 63ada8787..2b9e7709d 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1555,7 +1555,7 @@ CScript komodo_mineropret(int32_t nHeight) CScript opret; if ( Mineropret.size() != 0 ) { - fprintf(stderr,"use Mineropret[%d]\n",(int32_t)Mineropret.size()); + //fprintf(stderr,"use Mineropret[%d]\n",(int32_t)Mineropret.size()); return(opret << OP_RETURN << Mineropret); } return(opret); diff --git a/src/miner.cpp b/src/miner.cpp index 69580da7b..9246b7e6a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -726,8 +726,10 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } if ( ASSETCHAINS_CBOPRET != 0 ) { - txNew.vout.resize(txNew.vout.size()+1); - txNew.vout[txNew.vout.size()-1].scriptPubKey = komodo_mineropret(nHeight); + int32_t numv = (int32_t)txNew.vout.size(); + txNew.vout.resize(numv+1); + txNew.vout[numv].nValue = 0; + txNew.vout[numv].scriptPubKey = komodo_mineropret(nHeight); printf("autocreate commision/cbopret.%lld vout[%d]\n",(long long)ASSETCHAINS_CBOPRET,(int32_t)txNew.vout.size()); } pblock->vtx[0] = txNew; From eb58e86e18559b5b8f4bfed2ba6574560558d795 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 04:32:37 -1100 Subject: [PATCH 456/787] Test --- src/miner.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/miner.cpp b/src/miner.cpp index 9246b7e6a..f131bc90e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -730,7 +730,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 txNew.vout.resize(numv+1); txNew.vout[numv].nValue = 0; txNew.vout[numv].scriptPubKey = komodo_mineropret(nHeight); - printf("autocreate commision/cbopret.%lld vout[%d]\n",(long long)ASSETCHAINS_CBOPRET,(int32_t)txNew.vout.size()); + //printf("autocreate commision/cbopret.%lld vout[%d]\n",(long long)ASSETCHAINS_CBOPRET,(int32_t)txNew.vout.size()); } pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; From 71b0de3f217361465b2860a1f77eb3b763feba19 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 04:34:58 -1100 Subject: [PATCH 457/787] -print --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 2b9e7709d..e39470317 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1634,9 +1634,9 @@ void komodo_cbopretupdate() if ( Mineropret.size() != sizeof(pricebits) ) Mineropret.resize(sizeof(pricebits)); memcpy(&Mineropret[0],pricebits,sizeof(pricebits)); - int32_t i; for (i=0; i Date: Thu, 28 Mar 2019 04:36:43 -1100 Subject: [PATCH 458/787] Test --- src/komodo_gateway.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e39470317..03816ee79 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1563,7 +1563,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; uint32_t pricebits[4]; int32_t i,lag; + std::vector vopret; uint32_t pricebits[4]; int32_t i,lag,lag2; if ( ASSETCHAINS_CBOPRET != 0 ) { GetOpReturnData(scriptPubKey,vopret); @@ -1571,7 +1571,8 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { memcpy(pricebits,&vopret[0],sizeof(pricebits)); lag = (int32_t)(time(NULL) - pricebits[0]); - fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1)); + lag2 = (int32_t)(pricebits[0] - komodo_heightstamp(nHeight-1)); + fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); return(0); } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)sizeof(pricebits),(int32_t)scriptPubKey.size(),scriptPubKey[0]); return(-1); From 180ca470b0a9f79f7a8964f4224e62ac218c0c0e Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Thu, 28 Mar 2019 17:16:09 +0100 Subject: [PATCH 459/787] Fix CC change calculation for channelspayment --- src/cc/channels.cpp | 28 ++++++++++++++++++++++++++-- src/wallet/rpcwallet.cpp | 4 ++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp index 08dedb3f9..2887c18e1 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -254,8 +254,6 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & return eval->Invalid("vin.1 is CC for channelpayment!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) return eval->Invalid("vin.2 is CC for channelpayment!"); - else if ( ConstrainVout(tx.vout[0],1,channeladdress,(numpayments-param2)*payment)==0 ) - return eval->Invalid("vout.0 is CC or invalid CC change amount for channelpayment!"); else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelpayment!"); else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) @@ -283,6 +281,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); + else if ( ConstrainVout(tx.vout[0],1,channeladdress,(p1-param2)*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid CC change amount for channelpayment!"); else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelpayment!"); else if (param1+param2!=p1) @@ -550,6 +550,12 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(opentxid)==false) + { + CCerror = strprintf("channelsopen tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0) @@ -654,6 +660,12 @@ std::string ChannelClose(uint64_t txfee,uint256 opentxid) LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(opentxid)==false) + { + CCerror = strprintf("channelsopen tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (mypk != srcpub) { CCerror = strprintf("cannot close, you are not channel owner"); @@ -708,6 +720,12 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(closetxid)==false) + { + CCerror = strprintf("channelsclose tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (txid!=opentxid) { CCerror = strprintf("open and close txid are not from same channel"); @@ -720,6 +738,12 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(opentxid)==false) + { + CCerror = strprintf("channelsopen tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain)!='O') { CCerror = strprintf("invalid channel open tx"); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 107508c18..5456f3b9c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -6105,6 +6105,7 @@ UniValue channelsopen(const UniValue& params, bool fHelp) tokenid=Parseuint256((char *)params[3].get_str().c_str()); } hex = ChannelOpen(0,pubkey2pk(destpub),numpayments,payment,tokenid); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -6135,6 +6136,7 @@ UniValue channelspayment(const UniValue& params, bool fHelp) secret = Parseuint256((char *)params[2].get_str().c_str()); } hex = ChannelPayment(0,opentxid,amount,secret); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -6155,6 +6157,7 @@ UniValue channelsclose(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); opentxid = Parseuint256((char *)params[0].get_str().c_str()); hex = ChannelClose(0,opentxid); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -6176,6 +6179,7 @@ UniValue channelsrefund(const UniValue& params, bool fHelp) opentxid = Parseuint256((char *)params[0].get_str().c_str()); closetxid = Parseuint256((char *)params[1].get_str().c_str()); hex = ChannelRefund(0,opentxid,closetxid); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); From 736a5cc15c42bb46090f5038352d6473410e23c5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 05:42:16 -1100 Subject: [PATCH 460/787] Price bits --- src/komodo_gateway.h | 102 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 92 insertions(+), 10 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 03816ee79..3a06d14b6 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1549,13 +1549,79 @@ void komodo_passport_iteration() } extern std::vector Mineropret; +#define PRICES_MAXCHANGE (COIN / 100) +#define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) + +int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) +{ + CBlockIndex *pindex; CBlock block; int32_t numvouts; std::vector vopret; + if ( (pindex= komodo_chainactive(nHeight)) != 0 ) + { + if ( komodo_blockload(block,pindex) == 0 ) + { + numvouts = (int32_t)block.vout[0].size(); + GetOpReturnData(block.vout[numvouts-1].scriptPubKey,vopret); + if ( vopret.size() == PRICES_SIZEBIT0 ) + { + memcpy(prevbits,&vopret[0],PRICES_SIZEBIT0); + return(0); + } + } + } + fprintf(stderr,"couldnt get pricebits for %d\n",nHeight); + return(-1); +} + +uint32_t komodo_pricenew(uint32_t price,uint32_t refprice,int64_t tolerance) +{ + uint32_t highprice,lowprice; + highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; + lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; + if ( price > highprice ) + return(highprice); + else if ( price < lowprice ) + return(lowprice); + else return(0); +} + +int32_t komodo_pricecmp(uint32_t pricebitsA[4],uint32_t pricebitsB[4],int64_t tolerance) +{ + int32_t i; + for (i=1; i<4; i++) + if ( komodo_pricenew(pricebitsA[i],pricebitsB[i],tolerance) != 0 ) + return(-1); + return(0); +} + +int32_t komodo_priceclamp(uint32_t pricebits[4],uint32_t refprices[4],int64_t tolerance) +{ + int32_t i; uint32_t newprice; + for (i=1; i<4; i++) + { + if ( (newprice= komodo_pricenew(pricebits[i],refprices[i],tolerance)) != 0 ) + { + fprintf(stderr,"priceclamp[%d] %u -> %u\n",i,pricebits[i],newprice); + pricebits[i] = newprice; + } + } + return(0); +} CScript komodo_mineropret(int32_t nHeight) { - CScript opret; - if ( Mineropret.size() != 0 ) + CScript opret; uint32_t pricebits[4],prevbits[4]; + if ( Mineropret.size() == PRICES_SIZEBIT0 ) { - //fprintf(stderr,"use Mineropret[%d]\n",(int32_t)Mineropret.size()); + if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) + { + memcpy(pricebits,&Mineropret[0],PRICES_SIZEBIT0); + if ( komodo_pricecmp(pricebit,prevbits,PRICES_MAXCHANGE) < 0 ) + { + komodo_priceclamp(pricebits,prevbits,PRICES_MAXCHANGE); + fprintf(stderr,"update Mineropret to clamped prices\n"); + memcpy(&Mineropret[0],pricebits,PRICES_SIZEBIT0); + } + } return(opret << OP_RETURN << Mineropret); } return(opret); @@ -1563,18 +1629,34 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; uint32_t pricebits[4]; int32_t i,lag,lag2; + std::vector vopret; uint32_t pricebits[4],prevbits[4]; int32_t i,lag,lag2; if ( ASSETCHAINS_CBOPRET != 0 ) { GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() == sizeof(pricebits) ) + if ( vopret.size() == PRICES_SIZEBIT0 ) { - memcpy(pricebits,&vopret[0],sizeof(pricebits)); + memcpy(pricebits,&vopret[0],PRICES_SIZEBIT0); lag = (int32_t)(time(NULL) - pricebits[0]); + if ( lag < 0 ) + lag = -lag; lag2 = (int32_t)(pricebits[0] - komodo_heightstamp(nHeight-1)); fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); + if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() == PRICES_SIZEBIT0 ) + { + memcpy(prevbits,&Mineropret[0],PRICES_SIZEBIT0); + if ( komodo_pricecmp(pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + return(-1); + } + if ( nHeight > 1 ) + { + if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) + { + if ( komodo_pricecmp(pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + return(-1); + } else return(-1); + } return(0); - } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)sizeof(pricebits),(int32_t)scriptPubKey.size(),scriptPubKey[0]); + } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)PRICES_SIZEBIT0,(int32_t)scriptPubKey.size(),scriptPubKey[0]); return(-1); } return(0); @@ -1632,9 +1714,9 @@ void komodo_cbopretupdate() { if ( get_btcusd(pricebits) == 0 ) { - if ( Mineropret.size() != sizeof(pricebits) ) - Mineropret.resize(sizeof(pricebits)); - memcpy(&Mineropret[0],pricebits,sizeof(pricebits)); + if ( Mineropret.size() != PRICES_SIZEBIT0 ) + Mineropret.resize(PRICES_SIZEBIT0); + memcpy(&Mineropret[0],pricebits,PRICES_SIZEBIT0); //int32_t i; for (i=0; i Date: Thu, 28 Mar 2019 05:44:49 -1100 Subject: [PATCH 461/787] fix --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3a06d14b6..664548c2c 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1615,7 +1615,7 @@ CScript komodo_mineropret(int32_t nHeight) if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { memcpy(pricebits,&Mineropret[0],PRICES_SIZEBIT0); - if ( komodo_pricecmp(pricebit,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { komodo_priceclamp(pricebits,prevbits,PRICES_MAXCHANGE); fprintf(stderr,"update Mineropret to clamped prices\n"); From 66f7b96df6140864ad9c663d2ae6323702a7cad1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 05:46:14 -1100 Subject: [PATCH 462/787] Fix --- src/komodo_gateway.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 664548c2c..1e5aed8cf 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1554,13 +1554,14 @@ extern std::vector Mineropret; int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) { - CBlockIndex *pindex; CBlock block; int32_t numvouts; std::vector vopret; + CBlockIndex *pindex; CBlock block; CTransaction tx; int32_t numvouts; std::vector vopret; if ( (pindex= komodo_chainactive(nHeight)) != 0 ) { if ( komodo_blockload(block,pindex) == 0 ) { - numvouts = (int32_t)block.vout[0].size(); - GetOpReturnData(block.vout[numvouts-1].scriptPubKey,vopret); + tx = block.vtx[0]; + numvouts = (int32_t)tx.vout[0].size(); + GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() == PRICES_SIZEBIT0 ) { memcpy(prevbits,&vopret[0],PRICES_SIZEBIT0); From 0698ed694afcf9ce65572583bb4efed583b9fc16 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 05:47:20 -1100 Subject: [PATCH 463/787] -tx --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 1e5aed8cf..1b0664bb8 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1560,7 +1560,7 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) if ( komodo_blockload(block,pindex) == 0 ) { tx = block.vtx[0]; - numvouts = (int32_t)tx.vout[0].size(); + numvouts = (int32_t)tx.vout.size(); GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() == PRICES_SIZEBIT0 ) { From e9f398a93291935d076072c3ed5bb57a9e57bd36 Mon Sep 17 00:00:00 2001 From: Anton Lysakov Date: Thu, 28 Mar 2019 23:50:52 +0700 Subject: [PATCH 464/787] added tuis --- src/tui/LICENSE | 21 + src/tui/README.md | 58 + src/tui/lib/logo.txt | 39 + src/tui/lib/rpclib.py | 129 ++ src/tui/lib/tuilib.py | 1965 ++++++++++++++++++++++++++++++ src/tui/requirements.txt | 8 + src/tui/tui_assets.py | 67 + src/tui/tui_gateways_creation.py | 67 + src/tui/tui_gateways_usage.py | 96 ++ src/tui/tui_marmara.py | 68 ++ src/tui/tui_oracles.py | 67 + src/tui/tui_rogue.py | 116 ++ src/tui/tui_tetris.py | 96 ++ 13 files changed, 2797 insertions(+) create mode 100644 src/tui/LICENSE create mode 100644 src/tui/README.md create mode 100644 src/tui/lib/logo.txt create mode 100644 src/tui/lib/rpclib.py create mode 100755 src/tui/lib/tuilib.py create mode 100644 src/tui/requirements.txt create mode 100755 src/tui/tui_assets.py create mode 100755 src/tui/tui_gateways_creation.py create mode 100755 src/tui/tui_gateways_usage.py create mode 100755 src/tui/tui_marmara.py create mode 100755 src/tui/tui_oracles.py create mode 100755 src/tui/tui_rogue.py create mode 100755 src/tui/tui_tetris.py diff --git a/src/tui/LICENSE b/src/tui/LICENSE new file mode 100644 index 000000000..3300ef648 --- /dev/null +++ b/src/tui/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Anton Lysakov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/tui/README.md b/src/tui/README.md new file mode 100644 index 000000000..61e733794 --- /dev/null +++ b/src/tui/README.md @@ -0,0 +1,58 @@ +# Komodo Cryptoconditons Terminal User Interfaces (aka TUIs) + +These tools creating for demonstration and partial automation of Komodo cryptoconditions modules testing. (RogueCC game, AssetsCC, OraclesCC, GatewaysCC, MarmaraCC, ...) + + +Developer installation (on Ubuntu 18.04) : + +Python3 required for execution: + +* `sudo apt-get install python3.6 python3-pip libgnutls28-dev` + +pip packages needed: + +* `pip3 install setuptools wheel slick-bitcoinrpc` +* or `pip3 install -r requirements.txt` + +Starting: + +# TUI for RogueCC + +If you're looking for player 3 in 1 (daemon + game + TUI) multiOS bundle - please check `releases` of this repo. + +`python3 rogue_tui.py` + +![alt text](https://i.imgur.com/gkcxMGt.png) + +# TUI for OraclesCC + +Have files uploader/downloader functionality - also there is a AWS branch for AWS certificates uploading demonstration + +`python3 oracles_cc_tui.py` + +![alt text](https://i.imgur.com/tfHwRqc.png) + +# TUI for GatewaysCC + +![alt text](https://i.imgur.com/c8DPfpp.png) + +`python3 gateways_creation_tui.py` + +`python3 gateways_usage_tui.py` + +At the moment raw version of manual gateway how-to guide can be found here: https://docs.komodoplatform.com/cc/contracts/gateways/scenarios/tutorial.html I advice to read it before you start use this tool to understand the flow. + +# TUI for MarmaraCC + +`python3 marmara_tui.py` + +![alt text](https://i.imgur.com/uonMWHl.png) + +# TUI for AssetsCC (not much finished) + +`python3 assets_cc_tui.py` + +Before execution be sure than daemon for needed AC up. + + + diff --git a/src/tui/lib/logo.txt b/src/tui/lib/logo.txt new file mode 100644 index 000000000..15ace1ad5 --- /dev/null +++ b/src/tui/lib/logo.txt @@ -0,0 +1,39 @@ +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0xlc:ldOKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0xo:,........';lxOXNMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMWNKkoc,..................':ox0XWMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMWNKkdc;............................,:ok0NWMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMWNKOdl;'.....................................,cdkKNWMMMMMMMMMMMMMM +MMMMMMMMMMMMMW0c'..............................................';kNMMMMMMMMMMMMM +MMMMMMMMMMMMMK:......................';:c:'......................,kWMMMMMMMMMMMM +MMMMMMMMMMMMXl...................;cdkKNWWNXOdl;'..................;OWMMMMMMMMMMM +MMMMMMMMMMMNo...............,cok0XWMMMMMMMMMMWNKkdc;'..............:KMMMMMMMMMMM +MMMMMMMMMMWx'...........;ox0XWMMMMMMMMMMMMMMMMMMMMWNKko:............lXMMMMMMMMMM +MMMMMMMMMWk,...........lXWMMMMMMMMMMMMWWWWMMMMMMMMMMMMMNx'...........oNMMMMMMMMM +MMMMMMMMW0;...........cKMMMMMMMMMWNXOdl::cdkKNWMMMMMMMMMNo...........'xWMMMMMMMM +MMMMMMMMKc...........;0WMMMMMWN0xl:,........';ldOXWMMMMMMXl...........,OWMMMMMMM +MMMMMMMXl...........,kWMMMMMMKl..................;OWMMMMMMK:...........;0MMMMMMM +MMMMMMNd...........'xNMMMMMMXl....................:0WMMMMMWO;...........cKMMMMMM +MMMMMNx'...........oNMMMMMMNd......................cKMMMMMMWk'...........lXMMMMM +MMMMWO,...........lXMMMMMMWx'.......................oXMMMMMMNd'...........dNMMMM +MMMMXc...........,OWMMMMMMK:........................,kWMMMMMMKc...........;0MMMM +MMMMWx'...........oXMMMMMMNd........................cKMMMMMMWk,...........lXMMMM +MMMMMNd...........'dNMMMMMMXl......................:0MMMMMMWO;...........cKMMMMM +MMMMMMXl...........,kWMMMMMMKc....................,OWMMMMMM0:...........;0MMMMMM +MMMMMMMKc...........;OWMMMMMW0:..................,kWMMMMMMXc...........,OWMMMMMM +MMMMMMMM0;...........:KMMMMMMWKko:,..........';lx0WMMMMMMNo...........'xWMMMMMMM +MMMMMMMMWk,...........lXMMMMMMMMWWXOxl:,,;cdOKNWMMMMMMMMNx'...........dNMMMMMMMM +MMMMMMMMMWx'...........dNMMMMMMMMMMMMWNXXNWMMMMMMMMMMMMWk,...........lXMMMMMMMMM +MMMMMMMMMMNo............cx0XWMMMMMMMMMMMMMMMMMMMMMMWN0kl,...........cKMMMMMMMMMM +MMMMMMMMMMMXl..............,:ok0XWMMMMMMMMMMMMWNKkdc;..............;0WMMMMMMMMMM +MMMMMMMMMMMMK:..................,cokKNWMMWNKOdl;'.................,kWMMMMMMMMMMM +MMMMMMMMMMMMWO;......................;cool;'.....................'xNMMMMMMMMMMMM +MMMMMMMMMMMMMWk;................................................'dNMMMMMMMMMMMMM +MMMMMMMMMMMMMMWXOxl;'.......................................,cdkKWMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMWNKOdc;..............................,cok0NWMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMWNKkoc,....................,:ox0XWMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0ko:,..........':lx0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0xl:,,;ldOKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNNXNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM \ No newline at end of file diff --git a/src/tui/lib/rpclib.py b/src/tui/lib/rpclib.py new file mode 100644 index 000000000..a79fc73d4 --- /dev/null +++ b/src/tui/lib/rpclib.py @@ -0,0 +1,129 @@ +import http +from slickrpc import Proxy + + +# RPC connection +def rpc_connect(rpc_user, rpc_password, port): + try: + rpc_connection = Proxy("http://%s:%s@127.0.0.1:%d"%(rpc_user, rpc_password, port)) + except Exception: + raise Exception("Connection error! Probably no daemon on selected port.") + return rpc_connection + + +# Non CC calls +def getinfo(rpc_connection): + try: + getinfo = rpc_connection.getinfo() + except Exception: + raise Exception("Connection error!") + return getinfo + + +def sendrawtransaction(rpc_connection, hex): + tx_id = rpc_connection.sendrawtransaction(hex) + return tx_id + + +def gettransaction(rpc_connection, tx_id): + transaction_info = rpc_connection.gettransaction(tx_id) + return transaction_info + + +def getrawtransaction(rpc_connection, tx_id): + rawtransaction = rpc_connection.getrawtransaction(tx_id) + return rawtransaction + + +def getbalance(rpc_connection): + balance = rpc_connection.getbalance() + return balance + +# Token CC calls +def token_create(rpc_connection, name, supply, description): + token_hex = rpc_connection.tokencreate(name, supply, description) + return token_hex + + +def token_info(rpc_connection, token_id): + token_info = rpc_connection.tokeninfo(token_id) + return token_info + + +#TODO: have to add option with pubkey input +def token_balance(rpc_connection, token_id): + token_balance = rpc_connection.tokenbalance(token_id) + return token_balance + +def token_list(rpc_connection): + token_list = rpc_connection.tokenlist() + return token_list + + +def token_convert(rpc_connection, evalcode, token_id, pubkey, supply): + token_convert_hex = rpc_connection.tokenconvert(evalcode, token_id, pubkey, supply) + return token_convert_hex + +def get_rawmempool(rpc_connection): + mempool = rpc_connection.getrawmempool() + return mempool + +# Oracle CC calls +def oracles_create(rpc_connection, name, description, data_type): + oracles_hex = rpc_connection.oraclescreate(name, description, data_type) + return oracles_hex + + +def oracles_register(rpc_connection, oracle_id, data_fee): + oracles_register_hex = rpc_connection.oraclesregister(oracle_id, data_fee) + return oracles_register_hex + + +def oracles_subscribe(rpc_connection, oracle_id, publisher_id, data_fee): + oracles_subscribe_hex = rpc_connection.oraclessubscribe(oracle_id, publisher_id, data_fee) + return oracles_subscribe_hex + + +def oracles_info(rpc_connection, oracle_id): + oracles_info = rpc_connection.oraclesinfo(oracle_id) + return oracles_info + + +def oracles_data(rpc_connection, oracle_id, hex_string): + oracles_data = rpc_connection.oraclesdata(oracle_id, hex_string) + return oracles_data + + +def oracles_list(rpc_connection): + oracles_list = rpc_connection.oracleslist() + return oracles_list + + +def oracles_samples(rpc_connection, oracletxid, batonutxo, num): + oracles_sample = rpc_connection.oraclessamples(oracletxid, batonutxo, num) + return oracles_sample + + +# Gateways CC calls +# Arguments changing dynamically depends of M N, so supposed to wrap it this way +# token_id, oracle_id, coin_name, token_supply, M, N + pubkeys for each N +def gateways_bind(rpc_connection, *args): + gateways_bind_hex = rpc_connection.gatewaysbind(*args) + return gateways_bind_hex + + +def gateways_deposit(rpc_connection, gateway_id, height, coin_name,\ + coin_txid, claim_vout, deposit_hex, proof, dest_pub, amount): + gateways_deposit_hex = rpc_connection.gatewaysdeposit(gateway_id, height, coin_name,\ + coin_txid, claim_vout, deposit_hex, proof, dest_pub, amount) + return gateways_deposit_hex + + +def gateways_claim(rpc_connection, gateway_id, coin_name, deposit_txid, dest_pub, amount): + gateways_claim_hex = rpc_connection.gatewaysclaim(gateway_id, coin_name, deposit_txid, dest_pub, amount) + return gateways_claim_hex + + +def gateways_withdraw(rpc_connection, gateway_id, coin_name, withdraw_pub, amount): + gateways_withdraw_hex = rpc_connection.gatewayswithdraw(gateway_id, coin_name, withdraw_pub, amount) + return gateways_withdraw_hex diff --git a/src/tui/lib/tuilib.py b/src/tui/lib/tuilib.py new file mode 100755 index 000000000..da1d7658a --- /dev/null +++ b/src/tui/lib/tuilib.py @@ -0,0 +1,1965 @@ +from lib import rpclib +import json +import time +import re +import sys +import pickle +import platform +import os +import subprocess +import signal +from slickrpc import Proxy +from binascii import hexlify +from binascii import unhexlify +from functools import partial +from shutil import copy + + +operating_system = platform.system() +if operating_system != 'Win64' and operating_system != 'Windows': + import readline + + +def colorize(string, color): + + colors = { + 'blue': '\033[94m', + 'magenta': '\033[95m', + 'green': '\033[92m', + 'red': '\033[91m' + } + if color not in colors: + return string + else: + return colors[color] + string + '\033[0m' + + +def rpc_connection_tui(): + # TODO: possible to save multiply entries from successfull sessions and ask user to choose then + while True: + restore_choice = input("Do you want to use connection details from previous session? [y/n]: ") + if restore_choice == "y": + try: + with open("connection.json", "r") as file: + connection_json = json.load(file) + rpc_user = connection_json["rpc_user"] + rpc_password = connection_json["rpc_password"] + rpc_port = connection_json["rpc_port"] + rpc_connection = rpclib.rpc_connect(rpc_user, rpc_password, int(rpc_port)) + except FileNotFoundError: + print(colorize("You do not have cached connection details. Please select n for connection setup", "red")) + break + elif restore_choice == "n": + rpc_user = input("Input your rpc user: ") + rpc_password = input("Input your rpc password: ") + rpc_port = input("Input your rpc port: ") + connection_details = {"rpc_user": rpc_user, + "rpc_password": rpc_password, + "rpc_port": rpc_port} + connection_json = json.dumps(connection_details) + with open("connection.json", "w+") as file: + file.write(connection_json) + rpc_connection = rpclib.rpc_connect(rpc_user, rpc_password, int(rpc_port)) + break + else: + print(colorize("Please input y or n", "red")) + return rpc_connection + + +def def_credentials(chain): + rpcport =''; + operating_system = platform.system() + if operating_system == 'Darwin': + ac_dir = os.environ['HOME'] + '/Library/Application Support/Komodo' + elif operating_system == 'Linux': + ac_dir = os.environ['HOME'] + '/.komodo' + elif operating_system == 'Win64' or operating_system == 'Windows': + ac_dir = '%s/komodo/' % os.environ['APPDATA'] + if chain == 'KMD': + coin_config_file = str(ac_dir + '/komodo.conf') + else: + coin_config_file = str(ac_dir + '/' + chain + '/' + chain + '.conf') + with open(coin_config_file, 'r') as f: + for line in f: + l = line.rstrip() + if re.search('rpcuser', l): + rpcuser = l.replace('rpcuser=', '') + elif re.search('rpcpassword', l): + rpcpassword = l.replace('rpcpassword=', '') + elif re.search('rpcport', l): + rpcport = l.replace('rpcport=', '') + if len(rpcport) == 0: + if chain == 'KMD': + rpcport = 7771 + else: + print("rpcport not in conf file, exiting") + print("check "+coin_config_file) + exit(1) + + return(Proxy("http://%s:%s@127.0.0.1:%d"%(rpcuser, rpcpassword, int(rpcport)))) + + +def getinfo_tui(rpc_connection): + + info_raw = rpclib.getinfo(rpc_connection) + if isinstance(info_raw, dict): + for key in info_raw: + print("{}: {}".format(key, info_raw[key])) + input("Press [Enter] to continue...") + else: + print("Error!\n") + print(info_raw) + input("\nPress [Enter] to continue...") + + +def token_create_tui(rpc_connection): + + while True: + try: + name = input("Set your token name: ") + supply = input("Set your token supply: ") + description = input("Set your token description: ") + except KeyboardInterrupt: + break + else: + token_hex = rpclib.token_create(rpc_connection, name, supply, description) + if token_hex['result'] == "error": + print(colorize("\nSomething went wrong!\n", "pink")) + print(token_hex) + print("\n") + input("Press [Enter] to continue...") + break + else: + try: + token_txid = rpclib.sendrawtransaction(rpc_connection, + token_hex['hex']) + except KeyError: + print(token_txid) + print("Error") + input("Press [Enter] to continue...") + break + finally: + print(colorize("Token creation transaction broadcasted: " + token_txid, "green")) + file = open("tokens_list", "a") + file.writelines(token_txid + "\n") + file.close() + print(colorize("Entry added to tokens_list file!\n", "green")) + input("Press [Enter] to continue...") + break + + +def oracle_create_tui(rpc_connection): + + print(colorize("\nAvailiable data types:\n", "blue")) + oracles_data_types = ["Ihh -> height, blockhash, merkleroot\ns -> <256 char string\nS -> <65536 char string\nd -> <256 binary data\nD -> <65536 binary data", + "c -> 1 byte signed little endian number, C unsigned\nt -> 2 byte signed little endian number, T unsigned", + "i -> 4 byte signed little endian number, I unsigned\nl -> 8 byte signed little endian number, L unsigned", + "h -> 32 byte hash\n"] + for oracles_type in oracles_data_types: + print(str(oracles_type)) + while True: + try: + name = input("Set your oracle name: ") + description = input("Set your oracle description: ") + oracle_data_type = input("Set your oracle type (e.g. Ihh): ") + except KeyboardInterrupt: + break + else: + oracle_hex = rpclib.oracles_create(rpc_connection, name, description, oracle_data_type) + if oracle_hex['result'] == "error": + print(colorize("\nSomething went wrong!\n", "pink")) + print(oracle_hex) + print("\n") + input("Press [Enter] to continue...") + break + else: + try: + oracle_txid = rpclib.sendrawtransaction(rpc_connection, oracle_hex['hex']) + except KeyError: + print(oracle_txid) + print("Error") + input("Press [Enter] to continue...") + break + finally: + print(colorize("Oracle creation transaction broadcasted: " + oracle_txid, "green")) + file = open("oracles_list", "a") + file.writelines(oracle_txid + "\n") + file.close() + print(colorize("Entry added to oracles_list file!\n", "green")) + input("Press [Enter] to continue...") + break + + +def oracle_register_tui(rpc_connection): + #TODO: have an idea since blackjoker new RPC call + #grab all list and printout only or which owner match with node pubkey + try: + print(colorize("Oracles created from this instance by TUI: \n", "blue")) + with open("oracles_list", "r") as file: + for oracle in file: + print(oracle) + print(colorize('_' * 65, "blue")) + print("\n") + except FileNotFoundError: + print("Seems like a no oracles created from this instance yet\n") + pass + while True: + try: + oracle_id = input("Input txid of oracle you want to register to: ") + data_fee = input("Set publisher datafee (in satoshis): ") + except KeyboardInterrupt: + break + oracle_register_hex = rpclib.oracles_register(rpc_connection, oracle_id, data_fee) + if oracle_register_hex['result'] == "error": + print(colorize("\nSomething went wrong!\n", "pink")) + print(oracle_register_hex) + print("\n") + input("Press [Enter] to continue...") + break + else: + try: + oracle_register_txid = rpclib.sendrawtransaction(rpc_connection, oracle_register_hex['hex']) + except KeyError: + print(oracle_register_hex) + print("Error") + input("Press [Enter] to continue...") + break + else: + print(colorize("Oracle registration transaction broadcasted: " + oracle_register_txid, "green")) + input("Press [Enter] to continue...") + break + + +def oracle_subscription_utxogen(rpc_connection): + # TODO: have an idea since blackjoker new RPC call + # grab all list and printout only or which owner match with node pubkey + try: + print(colorize("Oracles created from this instance by TUI: \n", "blue")) + with open("oracles_list", "r") as file: + for oracle in file: + print(oracle) + print(colorize('_' * 65, "blue")) + print("\n") + except FileNotFoundError: + print("Seems like a no oracles created from this instance yet\n") + pass + while True: + try: + oracle_id = input("Input oracle ID you want to subscribe to: ") + #printout to fast copypaste publisher id + oracle_info = rpclib.oracles_info(rpc_connection, oracle_id) + publishers = 0 + print(colorize("\nPublishers registered for a selected oracle: \n", "blue")) + try: + for entry in oracle_info["registered"]: + publisher = entry["publisher"] + print(publisher + "\n") + publishers = publishers + 1 + print("Total publishers:{}".format(publishers)) + except (KeyError, ConnectionResetError): + print(colorize("Please re-check your input. Oracle txid seems not valid.", "red")) + pass + print(colorize('_' * 65, "blue")) + print("\n") + if publishers == 0: + print(colorize("This oracle have no publishers to subscribe.\n" + "Please register as an oracle publisher first and/or wait since registration transaciton mined!", "red")) + input("Press [Enter] to continue...") + break + publisher_id = input("Input oracle publisher id you want to subscribe to: ") + data_fee = input("Input subscription fee (in COINS!): ") + utxo_num = int(input("Input how many transactions you want to broadcast: ")) + except KeyboardInterrupt: + break + while utxo_num > 0: + while True: + oracle_subscription_hex = rpclib.oracles_subscribe(rpc_connection, oracle_id, publisher_id, data_fee) + oracle_subscription_txid = rpclib.sendrawtransaction(rpc_connection, oracle_subscription_hex['hex']) + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscription_txid in mempool: + break + else: + pass + print(colorize("Oracle subscription transaction broadcasted: " + oracle_subscription_txid, "green")) + utxo_num = utxo_num - 1 + input("Press [Enter] to continue...") + break + +def gateways_bind_tui(rpc_connection): + # main loop with keyboard interrupt handling + while True: + try: + while True: + try: + print(colorize("Tokens created from this instance by TUI: \n", "blue")) + with open("tokens_list", "r") as file: + for oracle in file: + print(oracle) + print(colorize('_' * 65, "blue")) + print("\n") + except FileNotFoundError: + print("Seems like a no oracles created from this instance yet\n") + pass + token_id = input("Input id of token you want to use in gw bind: ") + try: + token_name = rpclib.token_info(rpc_connection, token_id)["name"] + except KeyError: + print(colorize("Not valid tokenid. Please try again.", "red")) + input("Press [Enter] to continue...") + token_info = rpclib.token_info(rpc_connection, token_id) + print(colorize("\n{} token total supply: {}\n".format(token_id, token_info["supply"]), "blue")) + token_supply = input("Input supply for token binding: ") + try: + print(colorize("\nOracles created from this instance by TUI: \n", "blue")) + with open("oracles_list", "r") as file: + for oracle in file: + print(oracle) + print(colorize('_' * 65, "blue")) + print("\n") + except FileNotFoundError: + print("Seems like a no oracles created from this instance yet\n") + pass + oracle_id = input("Input id of oracle you want to use in gw bind: ") + try: + oracle_name = rpclib.oracles_info(rpc_connection, oracle_id)["name"] + except KeyError: + print(colorize("Not valid oracleid. Please try again.", "red")) + input("Press [Enter] to continue...") + while True: + coin_name = input("Input external coin ticker (binded oracle and token need to have same name!): ") + if token_name == oracle_name and token_name == coin_name: + break + else: + print(colorize("Token name, oracle name and external coin ticker should match!", "red")) + while True: + M = input("Input minimal amount of pubkeys needed for transaction confirmation (1 for non-multisig gw): ") + N = input("Input maximal amount of pubkeys needed for transaction confirmation (1 for non-multisig gw): ") + if (int(N) >= int(M)): + break + else: + print("Maximal amount of pubkeys should be more or equal than minimal. Please try again.") + pubkeys = [] + for i in range(int(N)): + pubkeys.append(input("Input pubkey {}: ".format(i+1))) + pubtype = input("Input pubtype of external coin: ") + p2shtype = input("Input p2shtype of external coin: ") + wiftype = input("Input wiftype of external coin: ") + args = [rpc_connection, token_id, oracle_id, coin_name, token_supply, M, N] + new_args = [str(pubtype), str(p2shtype), wiftype] + args = args + pubkeys + new_args + # broadcasting block + try: + gateways_bind_hex = rpclib.gateways_bind(*args) + except Exception as e: + print(e) + input("Press [Enter] to continue...") + break + try: + gateways_bind_txid = rpclib.sendrawtransaction(rpc_connection, gateways_bind_hex["hex"]) + except Exception as e: + print(e) + print(gateways_bind_hex) + input("Press [Enter] to continue...") + break + else: + print(colorize("Gateway bind transaction broadcasted: " + gateways_bind_txid, "green")) + file = open("gateways_list", "a") + file.writelines(gateways_bind_txid + "\n") + file.close() + print(colorize("Entry added to gateways_list file!\n", "green")) + input("Press [Enter] to continue...") + break + break + except KeyboardInterrupt: + break + +# temporary :trollface: custom connection function solution +# to have connection to KMD daemon and cache it in separate file + + +def rpc_kmd_connection_tui(): + while True: + restore_choice = input("Do you want to use KMD daemon connection details from previous session? [y/n]: ") + if restore_choice == "y": + try: + with open("connection_kmd.json", "r") as file: + connection_json = json.load(file) + rpc_user = connection_json["rpc_user"] + rpc_password = connection_json["rpc_password"] + rpc_port = connection_json["rpc_port"] + rpc_connection_kmd = rpclib.rpc_connect(rpc_user, rpc_password, int(rpc_port)) + try: + print(rpc_connection_kmd.getinfo()) + print(colorize("Successfully connected!\n", "green")) + input("Press [Enter] to continue...") + break + except Exception as e: + print(e) + print(colorize("NOT CONNECTED!\n", "red")) + input("Press [Enter] to continue...") + break + except FileNotFoundError: + print(colorize("You do not have cached KMD daemon connection details." + " Please select n for connection setup", "red")) + input("Press [Enter] to continue...") + elif restore_choice == "n": + rpc_user = input("Input your rpc user: ") + rpc_password = input("Input your rpc password: ") + rpc_port = input("Input your rpc port: ") + connection_details = {"rpc_user": rpc_user, + "rpc_password": rpc_password, + "rpc_port": rpc_port} + connection_json = json.dumps(connection_details) + with open("connection_kmd.json", "w+") as file: + file.write(connection_json) + rpc_connection_kmd = rpclib.rpc_connect(rpc_user, rpc_password, int(rpc_port)) + try: + print(rpc_connection_kmd.getinfo()) + print(colorize("Successfully connected!\n", "green")) + input("Press [Enter] to continue...") + break + except Exception as e: + print(e) + print(colorize("NOT CONNECTED!\n", "red")) + input("Press [Enter] to continue...") + break + else: + print(colorize("Please input y or n", "red")) + return rpc_connection_kmd + + +def z_sendmany_twoaddresses(rpc_connection, sendaddress, recepient1, amount1, recepient2, amount2): + str_sending_block = "[{{\"address\":\"{}\",\"amount\":{}}},{{\"address\":\"{}\",\"amount\":{}}}]".format(recepient1, amount1, recepient2, amount2) + sending_block = json.loads(str_sending_block) + operation_id = rpc_connection.z_sendmany(sendaddress,sending_block) + return operation_id + + +def operationstatus_to_txid(rpc_connection, zstatus): + str_sending_block = "[\"{}\"]".format(zstatus) + sending_block = json.loads(str_sending_block) + operation_json = rpc_connection.z_getoperationstatus(sending_block) + operation_dump = json.dumps(operation_json) + operation_dict = json.loads(operation_dump)[0] + txid = operation_dict['result']['txid'] + return txid + + +def gateways_send_kmd(rpc_connection): + # TODO: have to handle CTRL+C on text input + print(colorize("Please be carefull when input wallet addresses and amounts since all transactions doing in real KMD!", "pink")) + print("Your addresses with balances: ") + list_address_groupings = rpc_connection.listaddressgroupings() + for address in list_address_groupings: + print(str(address) + "\n") + sendaddress = input("Input address from which you transfer KMD: ") + recepient1 = input("Input address which belongs to pubkey which will receive tokens: ") + amount1 = 0.0001 + recepient2 = input("Input gateway deposit address: ") + file = open("deposits_list", "a") + #have to show here deposit addresses for gateways created by user + amount2 = input("Input how many KMD you want to deposit on this gateway: ") + operation = z_sendmany_twoaddresses(rpc_connection, sendaddress, recepient1, amount1, recepient2, amount2) + print("Operation proceed! " + str(operation) + " Let's wait 2 seconds to get txid") + # trying to avoid pending status of operation + time.sleep(2) + txid = operationstatus_to_txid(rpc_connection, operation) + file.writelines(txid + "\n") + file.close() + print(colorize("KMD Transaction ID: " + str(txid) + " Entry added to deposits_list file", "green")) + input("Press [Enter] to continue...") + + +def gateways_deposit_tui(rpc_connection_assetchain, rpc_connection_komodo): + while True: + bind_txid = input("Input your gateway bind txid: ") + coin_name = input("Input your external coin ticker (e.g. KMD): ") + coin_txid = input("Input your deposit txid: ") + dest_pub = input("Input pubkey which claim deposit: ") + amount = input("Input amount of your deposit: ") + height = rpc_connection_komodo.getrawtransaction(coin_txid, 1)["height"] + deposit_hex = rpc_connection_komodo.getrawtransaction(coin_txid, 1)["hex"] + claim_vout = "0" + proof_sending_block = "[\"{}\"]".format(coin_txid) + proof = rpc_connection_komodo.gettxoutproof(json.loads(proof_sending_block)) + deposit_hex = rpclib.gateways_deposit(rpc_connection_assetchain, bind_txid, str(height), coin_name, \ + coin_txid, claim_vout, deposit_hex, proof, dest_pub, amount) + print(deposit_hex) + deposit_txid = rpclib.sendrawtransaction(rpc_connection_assetchain, deposit_hex["hex"]) + print("Done! Gateways deposit txid is: " + deposit_txid + " Please not forget to claim your deposit!") + input("Press [Enter] to continue...") + break + + +def gateways_claim_tui(rpc_connection): + while True: + bind_txid = input("Input your gateway bind txid: ") + coin_name = input("Input your external coin ticker (e.g. KMD): ") + deposit_txid = input("Input your gatewaysdeposit txid: ") + dest_pub = input("Input pubkey which claim deposit: ") + amount = input("Input amount of your deposit: ") + claim_hex = rpclib.gateways_claim(rpc_connection, bind_txid, coin_name, deposit_txid, dest_pub, amount) + try: + claim_txid = rpclib.sendrawtransaction(rpc_connection, claim_hex["hex"]) + except Exception as e: + print(e) + print(claim_hex) + input("Press [Enter] to continue...") + break + else: + print("Succesfully claimed! Claim transaction id: " + claim_txid) + input("Press [Enter] to continue...") + break + + +def gateways_withdrawal_tui(rpc_connection): + while True: + bind_txid = input("Input your gateway bind txid: ") + coin_name = input("Input your external coin ticker (e.g. KMD): ") + withdraw_pub = input("Input pubkey to which you want to withdraw: ") + amount = input("Input amount of withdrawal: ") + withdraw_hex = rpclib.gateways_withdraw(rpc_connection, bind_txid, coin_name, withdraw_pub, amount) + withdraw_txid = rpclib.sendrawtransaction(rpc_connection, withdraw_hex["hex"]) + print(withdraw_txid) + input("Press [Enter] to continue...") + break + + +def print_mempool(rpc_connection): + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + tx_counter = 0 + print(colorize("Transactions in mempool: \n", "magenta")) + for transaction in mempool: + print(transaction + "\n") + tx_counter = tx_counter + 1 + print("Total: " + str(tx_counter) + " transactions\n") + print("R + Enter to refresh list. E + Enter to exit menu." + "\n") + is_refresh = input("Choose your destiny: ") + if is_refresh == "R": + print("\n") + pass + elif is_refresh == "E": + print("\n") + break + else: + print("\nPlease choose R or E\n") + + +def print_tokens_list(rpc_connection): + # TODO: have to print it with tokeninfo to have sense + pass + + +def print_tokens_balances(rpc_connection): + # TODO: checking tokenbalance for each token from tokenlist and reflect non zero ones + pass + + +def hexdump(filename, chunk_size=1<<15): + data = "" + #add_spaces = partial(re.compile(b'(..)').sub, br'\1 ') + #write = getattr(sys.stdout, 'buffer', sys.stdout).write + with open(filename, 'rb') as file: + for chunk in iter(partial(file.read, chunk_size), b''): + data += str(hexlify(chunk).decode()) + return data + + +def convert_file_oracle_d(rpc_connection): + while True: + path = input("Input path to file you want to upload to oracle: ") + try: + hex_data = (hexdump(path, 1))[2:] + except Exception as e: + print(e) + print("Seems something goes wrong (I guess you've specified wrong path)!") + input("Press [Enter] to continue...") + break + else: + length = round(len(hex_data) / 2) + if length > 256: + print("Length: " + str(length) + " bytes") + print("File is too big for this app") + input("Press [Enter] to continue...") + break + else: + hex_length = format(length, '#04x')[2:] + data_for_oracle = str(hex_length) + hex_data + print("File hex representation: \n") + print(data_for_oracle + "\n") + print("Length: " + str(length) + " bytes") + print("File converted!") + new_oracle_hex = rpclib.oracles_create(rpc_connection, "tonyconvert", path, "d") + new_oracle_txid = rpclib.sendrawtransaction(rpc_connection, new_oracle_hex["hex"]) + time.sleep(0.5) + oracle_register_hex = rpclib.oracles_register(rpc_connection, new_oracle_txid, "10000") + oracle_register_txid = rpclib.sendrawtransaction(rpc_connection, oracle_register_hex["hex"]) + time.sleep(0.5) + oracle_subscribe_hex = rpclib.oracles_subscribe(rpc_connection, new_oracle_txid, rpclib.getinfo(rpc_connection)["pubkey"], "0.001") + oracle_subscribe_txid = rpclib.sendrawtransaction(rpc_connection, oracle_subscribe_hex["hex"]) + time.sleep(0.5) + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscribe_txid in mempool: + print("Waiting for oracle subscribtion tx to be mined" + "\n") + time.sleep(6) + pass + else: + break + oracles_data_hex = rpclib.oracles_data(rpc_connection, new_oracle_txid, data_for_oracle) + try: + oracle_data_txid = rpclib.sendrawtransaction(rpc_connection, oracles_data_hex["hex"]) + except Exception as e: + print(oracles_data_hex) + print(e) + print("Oracle created: " + str(new_oracle_txid)) + print("Data published: " + str(oracle_data_txid)) + input("Press [Enter] to continue...") + break + + +def convert_file_oracle_D(rpc_connection): + while True: + path = input("Input path to file you want to upload to oracle: ") + try: + hex_data = (hexdump(path, 1)) + except Exception as e: + print(e) + print("Seems something goes wrong (I guess you've specified wrong path)!") + input("Press [Enter] to continue...") + break + else: + length = round(len(hex_data) / 2) + # if length > 800000: + # print("Too big file size to upload for this version of program. Maximum size is 800KB.") + # input("Press [Enter] to continue...") + # break + if length > 8000: + # if file is more than 8000 bytes - slicing it to <= 8000 bytes chunks (16000 symbols = 8000 bytes) + data = [hex_data[i:i + 16000] for i in range(0, len(hex_data), 16000)] + chunks_amount = len(data) + # TODO: have to create oracle but subscribe this time chunks amount times to send whole file in same block + # TODO: 2 - on some point file will not fit block - have to find this point + # TODO: 3 way how I want to implement it first will keep whole file in RAM - have to implement some way to stream chunks to oracle before whole file readed + # TODO: have to "optimise" registration fee + # Maybe just check size first by something like a du ? + print("Length: " + str(length) + " bytes.\n Chunks amount: " + str(chunks_amount)) + new_oracle_hex = rpclib.oracles_create(rpc_connection, "tonyconvert_" + str(chunks_amount), path, "D") + new_oracle_txid = rpclib.sendrawtransaction(rpc_connection, new_oracle_hex["hex"]) + time.sleep(0.5) + oracle_register_hex = rpclib.oracles_register(rpc_connection, new_oracle_txid, "10000") + oracle_register_txid = rpclib.sendrawtransaction(rpc_connection, oracle_register_hex["hex"]) + # subscribe chunks_amount + 1 times, but lets limit our broadcasting 100 tx per block (800KB/block) + if chunks_amount > 100: + utxo_num = 101 + else: + utxo_num = chunks_amount + while utxo_num > 0: + while True: + oracle_subscription_hex = rpclib.oracles_subscribe(rpc_connection, new_oracle_txid, rpclib.getinfo(rpc_connection)["pubkey"], "0.001") + oracle_subscription_txid = rpclib.sendrawtransaction(rpc_connection, + oracle_subscription_hex['hex']) + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscription_txid in mempool: + break + else: + pass + print(colorize("Oracle subscription transaction broadcasted: " + oracle_subscription_txid, "green")) + utxo_num = utxo_num - 1 + # waiting for last broadcasted subscribtion transaction to be mined to be sure that money are on oracle balance + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscription_txid in mempool: + print("Waiting for oracle subscribtion tx to be mined" + "\n") + time.sleep(6) + pass + else: + break + print("Oracle preparation is finished. Oracle txid: " + new_oracle_txid) + # can publish data now + counter = 0 + for chunk in data: + hex_length_bigendian = format(round(len(chunk) / 2), '#06x')[2:] + # swap to get little endian length + a = hex_length_bigendian[2:] + b = hex_length_bigendian[:2] + hex_length = a + b + data_for_oracle = str(hex_length) + chunk + counter = counter + 1 + # print("Chunk number: " + str(counter) + "\n") + # print(data_for_oracle) + try: + oracles_data_hex = rpclib.oracles_data(rpc_connection, new_oracle_txid, data_for_oracle) + except Exception as e: + print(data_for_oracle) + print(e) + input("Press [Enter] to continue...") + break + # on broadcasting ensuring that previous one reached mempool before blast next one + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + oracle_data_txid = rpclib.sendrawtransaction(rpc_connection, oracles_data_hex["hex"]) + #time.sleep(0.1) + if oracle_data_txid in mempool: + break + else: + pass + # blasting not more than 100 at once (so maximum capacity per block can be changed here) + # but keep in mind that registration UTXOs amount needs to be changed too ! + if counter % 100 == 0 and chunks_amount > 100: + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_data_txid in mempool: + print("Waiting for previous data chunks to be mined before send new ones" + "\n") + print("Sent " + str(counter) + " chunks from " + str(chunks_amount)) + time.sleep(6) + pass + else: + break + + print("Last baton: " + oracle_data_txid) + input("Press [Enter] to continue...") + break + # if file suits single oraclesdata just broadcasting it straight without any slicing + else: + hex_length_bigendian = format(length, '#06x')[2:] + # swap to get little endian length + a = hex_length_bigendian[2:] + b = hex_length_bigendian[:2] + hex_length = a + b + data_for_oracle = str(hex_length) + hex_data + print("File hex representation: \n") + print(data_for_oracle + "\n") + print("Length: " + str(length) + " bytes") + print("File converted!") + new_oracle_hex = rpclib.oracles_create(rpc_connection, "tonyconvert_" + "1", path, "D") + new_oracle_txid = rpclib.sendrawtransaction(rpc_connection, new_oracle_hex["hex"]) + time.sleep(0.5) + oracle_register_hex = rpclib.oracles_register(rpc_connection, new_oracle_txid, "10000") + oracle_register_txid = rpclib.sendrawtransaction(rpc_connection, oracle_register_hex["hex"]) + time.sleep(0.5) + oracle_subscribe_hex = rpclib.oracles_subscribe(rpc_connection, new_oracle_txid, rpclib.getinfo(rpc_connection)["pubkey"], "0.001") + oracle_subscribe_txid = rpclib.sendrawtransaction(rpc_connection, oracle_subscribe_hex["hex"]) + time.sleep(0.5) + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscribe_txid in mempool: + print("Waiting for oracle subscribtion tx to be mined" + "\n") + time.sleep(6) + pass + else: + break + oracles_data_hex = rpclib.oracles_data(rpc_connection, new_oracle_txid, data_for_oracle) + try: + oracle_data_txid = rpclib.sendrawtransaction(rpc_connection, oracles_data_hex["hex"]) + except Exception as e: + print(oracles_data_hex) + print(e) + input("Press [Enter] to continue...") + break + else: + print("Oracle created: " + str(new_oracle_txid)) + print("Data published: " + str(oracle_data_txid)) + input("Press [Enter] to continue...") + break + + +def get_files_list(rpc_connection): + + start_time = time.time() + oracles_list = rpclib.oracles_list(rpc_connection) + files_list = [] + for oracle_txid in oracles_list: + oraclesinfo_result = rpclib.oracles_info(rpc_connection, oracle_txid) + description = oraclesinfo_result['description'] + name = oraclesinfo_result['name'] + if name[0:12] == 'tonyconvert_': + new_file = '[' + name + ': ' + description + ']: ' + oracle_txid + files_list.append(new_file) + print("--- %s seconds ---" % (time.time() - start_time)) + return files_list + + +def display_files_list(rpc_connection): + print("Scanning oracles. Please wait...") + list_to_display = get_files_list(rpc_connection) + while True: + for file in list_to_display: + print(file + "\n") + input("Press [Enter] to continue...") + break + + +def files_downloader(rpc_connection): + while True: + display_files_list(rpc_connection) + print("\n") + oracle_id = input("Input oracle ID you want to download file from: ") + output_path = input("Input output path for downloaded file (name included) e.g. /home/test.txt: ") + oracle_info = rpclib.oracles_info(rpc_connection, oracle_id) + name = oracle_info['name'] + latest_baton_txid = oracle_info['registered'][0]['batontxid'] + if name[0:12] == 'tonyconvert_': + # downloading process here + chunks_amount = int(name[12:]) + data = rpclib.oracles_samples(rpc_connection, oracle_id, latest_baton_txid, str(chunks_amount))["samples"] + for chunk in reversed(data): + with open(output_path, 'ab+') as file: + file.write(unhexlify(chunk[0])) + print("I hope that file saved to " + output_path + "\n") + input("Press [Enter] to continue...") + break + + else: + print("I cant recognize file inside this oracle. I'm very sorry, boss.") + input("Press [Enter] to continue...") + break + + +def marmara_receive_tui(rpc_connection): + while True: + issuer_pubkey = input("Input pubkey of person who do you want to receive MARMARA from: ") + issuance_sum = input("Input amount of MARMARA you want to receive: ") + blocks_valid = input("Input amount of blocks for cheque matures: ") + try: + marmara_receive_txinfo = rpc_connection.marmarareceive(issuer_pubkey, issuance_sum, "MARMARA", blocks_valid) + marmara_receive_txid = rpc_connection.sendrawtransaction(marmara_receive_txinfo["hex"]) + print("Marmara receive txid broadcasted: " + marmara_receive_txid + "\n") + print(json.dumps(marmara_receive_txinfo, indent=4, sort_keys=True) + "\n") + with open("receive_txids.txt", 'a+') as file: + file.write(marmara_receive_txid + "\n") + file.write(json.dumps(marmara_receive_txinfo, indent=4, sort_keys=True) + "\n") + print("Transaction id is saved to receive_txids.txt file.") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_receive_txinfo) + print(e) + print("Something went wrong. Please check your input") + + +def marmara_issue_tui(rpc_connection): + while True: + receiver_pubkey = input("Input pubkey of person who do you want to issue MARMARA: ") + issuance_sum = input("Input amount of MARMARA you want to issue: ") + maturing_block = input("Input number of block on which issuance mature: ") + approval_txid = input("Input receiving request transaction id: ") + try: + marmara_issue_txinfo = rpc_connection.marmaraissue(receiver_pubkey, issuance_sum, "MARMARA", maturing_block, approval_txid) + marmara_issue_txid = rpc_connection.sendrawtransaction(marmara_issue_txinfo["hex"]) + print("Marmara issuance txid broadcasted: " + marmara_issue_txid + "\n") + print(json.dumps(marmara_issue_txinfo, indent=4, sort_keys=True) + "\n") + with open("issue_txids.txt", "a+") as file: + file.write(marmara_issue_txid + "\n") + file.write(json.dumps(marmara_issue_txinfo, indent=4, sort_keys=True) + "\n") + print("Transaction id is saved to issue_txids.txt file.") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_issue_txinfo) + print(e) + print("Something went wrong. Please check your input") + + +def marmara_creditloop_tui(rpc_connection): + while True: + loop_txid = input("Input transaction ID of credit loop you want to get info about: ") + try: + marmara_creditloop_info = rpc_connection.marmaracreditloop(loop_txid) + print(json.dumps(marmara_creditloop_info, indent=4, sort_keys=True) + "\n") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_creditloop_info) + print(e) + print("Something went wrong. Please check your input") + + +def marmara_settlement_tui(rpc_connection): + while True: + loop_txid = input("Input transaction ID of credit loop to make settlement: ") + try: + marmara_settlement_info = rpc_connection.marmarasettlement(loop_txid) + marmara_settlement_txid = rpc_connection.sendrawtransaction(marmara_settlement_info["hex"]) + print("Loop " + loop_txid + " succesfully settled!\nSettlement txid: " + marmara_settlement_txid) + with open("settlement_txids.txt", "a+") as file: + file.write(marmara_settlement_txid + "\n") + file.write(json.dumps(marmara_settlement_info, indent=4, sort_keys=True) + "\n") + print("Transaction id is saved to settlement_txids.txt file.") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_settlement_info) + print(e) + print("Something went wrong. Please check your input") + input("Press [Enter] to continue...") + break + + +def marmara_lock_tui(rpc_connection): + while True: + amount = input("Input amount of coins you want to lock for settlement and staking: ") + unlock_height = input("Input height on which coins should be unlocked: ") + try: + marmara_lock_info = rpc_connection.marmaralock(amount, unlock_height) + marmara_lock_txid = rpc_connection.sendrawtransaction(marmara_lock_info["hex"]) + with open("lock_txids.txt", "a+") as file: + file.write(marmara_lock_txid + "\n") + file.write(json.dumps(marmara_lock_info, indent=4, sort_keys=True) + "\n") + print("Transaction id is saved to lock_txids.txt file.") + input("Press [Enter] to continue...") + break + except Exception as e: + print(e) + print("Something went wrong. Please check your input") + input("Press [Enter] to continue...") + break + + +def marmara_info_tui(rpc_connection): + while True: + firstheight = input("Input first height (default 0): ") + if not firstheight: + firstheight = "0" + lastheight = input("Input last height (default current (0) ): ") + if not lastheight: + lastheight = "0" + minamount = input("Input min amount (default 0): ") + if not minamount: + minamount = "0" + maxamount = input("Input max amount (default 0): ") + if not maxamount: + maxamount = "0" + issuerpk = input("Optional. Input issuer public key: ") + try: + if issuerpk: + marmara_info = rpc_connection.marmarainfo(firstheight, lastheight, minamount, maxamount, "MARMARA", issuerpk) + else: + marmara_info = rpc_connection.marmarainfo(firstheight, lastheight, minamount, maxamount) + print(json.dumps(marmara_info, indent=4, sort_keys=True) + "\n") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_info) + print(e) + print("Something went wrong. Please check your input") + input("Press [Enter] to continue...") + break + + +def rogue_game_info(rpc_connection, game_txid): + game_info_arg = '"' + "[%22" + game_txid + "%22]" + '"' + game_info = rpc_connection.cclib("gameinfo", "17", game_info_arg) + return game_info + + +def rogue_game_register(rpc_connection, game_txid, player_txid = False): + if player_txid: + registration_info_arg = '"' + "[%22" + game_txid + "%22,%22" + player_txid + "%22]" + '"' + else: + registration_info_arg = '"' + "[%22" + game_txid + "%22]" + '"' + registration_info = rpc_connection.cclib("register", "17", registration_info_arg) + return registration_info + + +def rogue_pending(rpc_connection): + rogue_pending_list = rpc_connection.cclib("pending", "17") + return rogue_pending_list + + +def rogue_bailout(rpc_connection, game_txid): + bailout_info_arg = '"' + "[%22" + game_txid + "%22]" + '"' + bailout_info = rpc_connection.cclib("bailout", "17", bailout_info_arg) + return bailout_info + + +def rogue_highlander(rpc_connection, game_txid): + highlander_info_arg = '"' + "[%22" + game_txid + "%22]" + '"' + highlander_info = rpc_connection.cclib("highlander", "17", highlander_info_arg) + return highlander_info + + +def rogue_players_list(rpc_connection): + rogue_players_list = rpc_connection.cclib("players", "17") + return rogue_players_list + + +def rogue_player_info(rpc_connection, playertxid): + player_info_arg = '"' + "[%22" + playertxid + "%22]" + '"' + player_info = rpc_connection.cclib("playerinfo", "17", player_info_arg) + return player_info + + +def rogue_extract(rpc_connection, game_txid, pubkey): + extract_info_arg = '"' + "[%22" + game_txid + "%22,%22" + pubkey + "%22]" + '"' + extract_info = rpc_connection.cclib("extract", "17", extract_info_arg) + return extract_info + + +def rogue_keystrokes(rpc_connection, game_txid, keystroke): + rogue_keystrokes_arg = '"' + "[%22" + game_txid + "%22,%22" + keystroke + "%22]" + '"' + keystroke_info = rpc_connection.cclib("keystrokes", "17", rogue_keystrokes_arg) + return keystroke_info + + +def print_multiplayer_games_list(rpc_connection): + while True: + pending_list = rogue_pending(rpc_connection) + multiplayer_pending_list = [] + for game in pending_list["pending"]: + if rogue_game_info(rpc_connection, game)["maxplayers"] > 1: + multiplayer_pending_list.append(game) + print("Multiplayer games availiable to join: \n") + for active_multiplayer_game in multiplayer_pending_list: + game_info = rogue_game_info(rpc_connection, active_multiplayer_game) + print(colorize("\n================================\n", "green")) + print("Game txid: " + game_info["gametxid"]) + print("Game buyin: " + str(game_info["buyin"])) + print("Game height: " + str(game_info["gameheight"])) + print("Start height: " + str(game_info["start"])) + print("Alive players: " + str(game_info["alive"])) + print("Registered players: " + str(game_info["numplayers"])) + print("Max players: " + str(game_info["maxplayers"])) + print(colorize("\n***\n", "blue")) + print("Players in game:") + for player in game_info["players"]: + print("Slot: " + str(player["slot"])) + if "baton" in player.keys(): + print("Baton: " + str(player["baton"])) + if "tokenid" in player.keys(): + print("Tokenid: " + str(player["tokenid"])) + print("Is mine?: " + str(player["ismine"])) + print(colorize("\nR + Enter - refresh list.\nE + Enter - to the game choice.\nCTRL + C - back to main menu", "blue")) + is_refresh = input("Choose your destiny: ") + if is_refresh == "R": + print("\n") + pass + elif is_refresh == "E": + print("\n") + break + else: + print("\nPlease choose R or E\n") + + +def rogue_newgame_singleplayer(rpc_connection, is_game_a_rogue=True): + try: + new_game_txid = rpc_connection.cclib("newgame", "17", "[1]")["txid"] + print("New singleplayer training game succesfully created. txid: " + new_game_txid) + while True: + mempool = rpc_connection.getrawmempool() + if new_game_txid in mempool: + print(colorize("Waiting for game transaction to be mined", "blue")) + time.sleep(5) + else: + print(colorize("Game transaction is mined", "green")) + break + players_list = rogue_players_list(rpc_connection) + if len(players_list["playerdata"]) > 0: + print_players_list(rpc_connection) + while True: + is_choice_needed = input("Do you want to choose a player for this game? [y/n] ") + if is_choice_needed == "y": + player_txid = input("Please input player txid: ") + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid, player_txid)["txid"] + break + elif is_choice_needed == "n": + set_warriors_name(rpc_connection) + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid)["txid"] + break + else: + print("Please choose y or n !") + else: + print("No players available to select") + input("Press [Enter] to continue...") + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid)["txid"] + while True: + mempool = rpc_connection.getrawmempool() + if newgame_regisration_txid in mempool: + print(colorize("Waiting for registration transaction to be mined", "blue")) + time.sleep(5) + else: + print(colorize("Registration transaction is mined", "green")) + break + game_info = rogue_game_info(rpc_connection, new_game_txid) + start_time = time.time() + while True: + if is_game_a_rogue: + subprocess.call(["../cc/rogue/rogue", str(game_info["seed"]), str(game_info["gametxid"])]) + else: + subprocess.call(["../cc/games/tetris", str(game_info["seed"]), str(game_info["gametxid"])]) + time_elapsed = time.time() - start_time + if time_elapsed > 1: + break + else: + print("Game less than 1 second. Trying to start again") + time.sleep(1) + game_end_height = int(rpc_connection.getinfo()["blocks"]) + while True: + current_height = int(rpc_connection.getinfo()["blocks"]) + height_difference = current_height - game_end_height + if height_difference == 0: + print(current_height) + print(game_end_height) + print(colorize("Waiting for next block before bailout", "blue")) + time.sleep(5) + else: + break + #print("\nKeystrokes of this game:\n") + #time.sleep(0.5) + while True: + keystrokes_rpc_responses = find_game_keystrokes_in_log(new_game_txid)[1::2] + if len(keystrokes_rpc_responses) < 1: + print("No keystrokes broadcasted yet. Let's wait 5 seconds") + time.sleep(5) + else: + break + #print(keystrokes_rpc_responses) + for keystroke in keystrokes_rpc_responses: + json_keystroke = json.loads(keystroke)["result"] + if "status" in json_keystroke.keys() and json_keystroke["status"] == "error": + while True: + print("Trying to re-brodcast keystroke") + keystroke_rebroadcast = rogue_keystrokes(rpc_connection, json_keystroke["gametxid"], json_keystroke["keystrokes"]) + if "txid" in keystroke_rebroadcast.keys(): + print("Keystroke broadcasted! txid: " + keystroke_rebroadcast["txid"]) + break + else: + print("Let's try again in 5 seconds") + time.sleep(5) + # waiting for last keystroke confirmation here + last_keystroke_json = json.loads(keystrokes_rpc_responses[-1]) + while True: + while True: + try: + rpc_connection.sendrawtransaction(last_keystroke_json["result"]["hex"]) + except Exception as e: + pass + try: + confirmations_amount = rpc_connection.getrawtransaction(last_keystroke_json["result"]["txid"], 1)["confirmations"] + break + except Exception as e: + print(e) + print("Let's wait a little bit more") + time.sleep(5) + pass + if confirmations_amount < 2: + print("Last keystroke not confirmed yet! Let's wait a little") + time.sleep(10) + else: + print("Last keystroke confirmed!") + break + while True: + print("\nExtraction info:\n") + extraction_info = rogue_extract(rpc_connection, new_game_txid, rpc_connection.getinfo()["pubkey"]) + if extraction_info["status"] == "error": + print(colorize("Your warrior died or no any information about game was saved on blockchain", "red")) + print("If warrior was alive - try to wait a little (choose n to wait for a next block). If he is dead - you can bailout now (choose y).") + else: + print("Current game state:") + print("Game txid: " + extraction_info["gametxid"]) + print("Information about game saved on chain: " + extraction_info["extracted"]) + print("\n") + is_bailout_needed = input("Do you want to make bailout now [y] or wait for one more block [n]? [y/n]: ") + if is_bailout_needed == "y": + bailout_info = rogue_bailout(rpc_connection, new_game_txid) + while True: + try: + confirmations_amount = rpc_connection.getrawtransaction(bailout_info["txid"], 1)["confirmations"] + break + except Exception as e: + print(e) + print("Bailout not on blockchain yet. Let's wait a little bit more") + time.sleep(20) + pass + break + elif is_bailout_needed == "n": + game_end_height = int(rpc_connection.getinfo()["blocks"]) + while True: + current_height = int(rpc_connection.getinfo()["blocks"]) + height_difference = current_height - game_end_height + if height_difference == 0: + print(current_height) + print(game_end_height) + print(colorize("Waiting for next block before bailout", "blue")) + time.sleep(5) + else: + break + else: + print("Please choose y or n !") + print(bailout_info) + print("\nGame is finished!\n") + bailout_txid = bailout_info["txid"] + input("Press [Enter] to continue...") + except Exception as e: + print("Something went wrong.") + print(e) + input("Press [Enter] to continue...") + + +def play_multiplayer_game(rpc_connection): + # printing list of user active multiplayer games + active_games_list = rpc_connection.cclib("games", "17")["games"] + active_multiplayer_games_list = [] + for game in active_games_list: + gameinfo = rogue_game_info(rpc_connection, game) + if gameinfo["maxplayers"] > 1: + active_multiplayer_games_list.append(gameinfo) + games_counter = 0 + for active_multiplayer_game in active_multiplayer_games_list: + games_counter = games_counter + 1 + is_ready_to_start = False + try: + active_multiplayer_game["seed"] + is_ready_to_start = True + except Exception as e: + pass + print(colorize("\n================================\n", "green")) + print("Game txid: " + active_multiplayer_game["gametxid"]) + print("Game buyin: " + str(active_multiplayer_game["buyin"])) + if is_ready_to_start: + print(colorize("Ready for start!", "green")) + else: + print(colorize("Not ready for start yet, wait until start height!", "red")) + print("Game height: " + str(active_multiplayer_game["gameheight"])) + print("Start height: " + str(active_multiplayer_game["start"])) + print("Alive players: " + str(active_multiplayer_game["alive"])) + print("Registered players: " + str(active_multiplayer_game["numplayers"])) + print("Max players: " + str(active_multiplayer_game["maxplayers"])) + print(colorize("\n***\n", "blue")) + print("Players in game:") + for player in active_multiplayer_game["players"]: + print("Slot: " + str(player["slot"])) + print("Baton: " + str(player["baton"])) + print("Tokenid: " + str(player["tokenid"])) + print("Is mine?: " + str(player["ismine"])) + # asking user if he want to start any of them + while True: + start_game = input("\nDo you want to start any of your pendning multiplayer games?[y/n]: ") + if start_game == "y": + new_game_txid = input("Input txid of game which you want to start: ") + game_info = rogue_game_info(rpc_connection, new_game_txid) + try: + start_time = time.time() + while True: + subprocess.call(["cc/rogue/rogue", str(game_info["seed"]), str(game_info["gametxid"])]) + time_elapsed = time.time() - start_time + if time_elapsed > 1: + break + else: + print("Game less than 1 second. Trying to start again") + time.sleep(1) + except Exception as e: + print("Maybe game isn't ready for start yet or your input was not correct, sorry.") + input("Press [Enter] to continue...") + break + game_end_height = int(rpc_connection.getinfo()["blocks"]) + while True: + current_height = int(rpc_connection.getinfo()["blocks"]) + height_difference = current_height - game_end_height + if height_difference == 0: + print(current_height) + print(game_end_height) + print(colorize("Waiting for next block before bailout or highlander", "blue")) + time.sleep(5) + else: + break + while True: + keystrokes_rpc_responses = find_game_keystrokes_in_log(new_game_txid)[1::2] + if len(keystrokes_rpc_responses) < 1: + print("No keystrokes broadcasted yet. Let's wait 5 seconds") + time.sleep(5) + else: + break + for keystroke in keystrokes_rpc_responses: + json_keystroke = json.loads(keystroke)["result"] + if "status" in json_keystroke.keys() and json_keystroke["status"] == "error": + while True: + print("Trying to re-brodcast keystroke") + keystroke_rebroadcast = rogue_keystrokes(rpc_connection, json_keystroke["gametxid"], + json_keystroke["keystrokes"]) + if "txid" in keystroke_rebroadcast.keys(): + print("Keystroke broadcasted! txid: " + keystroke_rebroadcast["txid"]) + break + else: + print("Let's try again in 5 seconds") + time.sleep(5) + last_keystroke_json = json.loads(keystrokes_rpc_responses[-1]) + while True: + while True: + try: + confirmations_amount = rpc_connection.getrawtransaction(last_keystroke_json["result"]["txid"], 1)["confirmations"] + break + except Exception as e: + print(e) + print("Let's wait a little bit more") + rpc_connection.sendrawtransaction(last_keystroke_json["result"]["hex"]) + time.sleep(5) + pass + if confirmations_amount < 2: + print("Last keystroke not confirmed yet! Let's wait a little") + time.sleep(10) + else: + print("Last keystroke confirmed!") + break + while True: + print("\nExtraction info:\n") + extraction_info = rogue_extract(rpc_connection, new_game_txid, rpc_connection.getinfo()["pubkey"]) + if extraction_info["status"] == "error": + print(colorize("Your warrior died or no any information about game was saved on blockchain", "red")) + print("If warrior was alive - try to wait a little (choose n to wait for a next block). If he is dead - you can bailout now (choose y).") + else: + print("Current game state:") + print("Game txid: " + extraction_info["gametxid"]) + print("Information about game saved on chain: " + extraction_info["extracted"]) + print("\n") + is_bailout_needed = input( + "Do you want to make bailout now [y] or wait for one more block [n]? [y/n]: ") + if is_bailout_needed == "y": + if game_info["alive"] > 1: + bailout_info = rogue_bailout(rpc_connection, new_game_txid) + try: + bailout_txid = bailout_info["txid"] + print(bailout_info) + print("\nGame is finished!\n") + input("Press [Enter] to continue...") + break + except Exception: + highlander_info = rogue_highlander(rpc_connection, new_game_txid) + highlander_info = highlander_info["txid"] + print(highlander_info) + print("\nGame is finished!\n") + input("Press [Enter] to continue...") + break + else: + highlander_info = rogue_highlander(rpc_connection, new_game_txid) + if 'error' in highlander_info.keys() and highlander_info["error"] == 'numplayers != maxplayers': + bailout_info = rogue_bailout(rpc_connection, new_game_txid) + print(bailout_info) + print("\nGame is finished!\n") + input("Press [Enter] to continue...") + break + else: + print(highlander_info) + print("\nGame is finished!\n") + input("Press [Enter] to continue...") + break + elif is_bailout_needed == "n": + game_end_height = int(rpc_connection.getinfo()["blocks"]) + while True: + current_height = int(rpc_connection.getinfo()["blocks"]) + height_difference = current_height - game_end_height + if height_difference == 0: + print(current_height) + print(game_end_height) + print(colorize("Waiting for next block before bailout", "blue")) + time.sleep(5) + else: + break + break + break + if start_game == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def rogue_newgame_multiplayer(rpc_connection): + while True: + max_players = input("Input game max. players (>1): ") + if int(max_players) > 1: + break + else: + print("Please re-check your input") + input("Press [Enter] to continue...") + while True: + buyin = input("Input game buyin (>0.001): ") + if float(buyin) > 0.001: + break + else: + print("Please re-check your input") + input("Press [Enter] to continue...") + try: + new_game_txid = rpc_connection.cclib("newgame", "17", '"[' + max_players + "," + buyin + ']"')["txid"] + print(colorize("New multiplayer game succesfully created. txid: " + new_game_txid, "green")) + input("Press [Enter] to continue...") + except Exception as e: + print("Something went wrong.") + print(e) + input("Press [Enter] to continue...") + + +def rogue_join_multiplayer_game(rpc_connection): + while True: + try: + print_multiplayer_games_list(rpc_connection) + # TODO: optional player data txid (print players you have and ask if you want to choose one) + game_txid = input("Input txid of game you want to join: ") + try: + while True: + print_players_list(rpc_connection) + is_choice_needed = input("Do you want to choose a player for this game? [y/n] ") + if is_choice_needed == "y": + player_txid = input("Please input player txid: ") + newgame_regisration_txid = rogue_game_register(rpc_connection, game_txid, player_txid)["txid"] + break + elif is_choice_needed == "n": + set_warriors_name(rpc_connection) + newgame_regisration_txid = rogue_game_register(rpc_connection, game_txid)["txid"] + break + else: + print("Please choose y or n !") + except Exception as e: + print("Something went wrong. Maybe you're trying to register on game twice or don't have enough funds to pay buyin.") + print(e) + input("Press [Enter] to continue...") + break + print(colorize("Succesfully registered.", "green")) + while True: + mempool = rpc_connection.getrawmempool() + if newgame_regisration_txid in mempool: + print(colorize("Waiting for registration transaction to be mined", "blue")) + time.sleep(5) + else: + print(colorize("Registration transaction is mined", "green")) + break + print(newgame_regisration_txid) + input("Press [Enter] to continue...") + break + except KeyboardInterrupt: + break + + +def print_players_list(rpc_connection): + players_list = rogue_players_list(rpc_connection) + print(colorize("\nYou own " + str(players_list["numplayerdata"]) + " warriors\n", "blue")) + warrior_counter = 0 + for player in players_list["playerdata"]: + warrior_counter = warrior_counter + 1 + player_data = rogue_player_info(rpc_connection, player)["player"] + print(colorize("\n================================\n","green")) + print("Warrior " + str(warrior_counter)) + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n","blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + input("Press [Enter] to continue...") + + +def sell_warrior(rpc_connection): + print(colorize("Your brave warriors: \n", "blue")) + print_players_list(rpc_connection) + print("\n") + while True: + need_sell = input("Do you want to place order to sell any? [y/n]: ") + if need_sell == "y": + playertxid = input("Input playertxid of warrior you want to sell: ") + price = input("Input price (in ROGUE coins) you want to sell warrior for: ") + try: + tokenid = rogue_player_info(rpc_connection, playertxid)["player"]["tokenid"] + except Exception as e: + print(e) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + token_ask_raw = rpc_connection.tokenask("1", tokenid, price) + try: + token_ask_txid = rpc_connection.sendrawtransaction(token_ask_raw["hex"]) + except Exception as e: + print(e) + print(token_ask_raw) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + print(colorize("Ask succesfully placed. Ask txid is: " + token_ask_txid, "green")) + input("Press [Enter] to continue...") + break + if need_sell == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +#TODO: have to combine into single scanner with different cases +def is_warrior_alive(rpc_connection, warrior_txid): + warrior_alive = False + raw_transaction = rpc_connection.getrawtransaction(warrior_txid, 1) + for vout in raw_transaction["vout"]: + if vout["value"] == 0.00000001 and rpc_connection.gettxout(raw_transaction["txid"], vout["n"]): + warrior_alive = True + return warrior_alive + + +def warriors_scanner(rpc_connection): + start_time = time.time() + token_list = rpc_connection.tokenlist() + my_warriors_list = rogue_players_list(rpc_connection) + warriors_list = {} + for token in token_list: + player_info = rogue_player_info(rpc_connection, token) + if "status" in player_info and player_info["status"] == "error": + pass + elif player_info["player"]["playertxid"] in my_warriors_list["playerdata"]: + pass + elif not is_warrior_alive(rpc_connection, player_info["player"]["playertxid"]): + pass + else: + warriors_list[token] = player_info["player"] + print("--- %s seconds ---" % (time.time() - start_time)) + return warriors_list + + +def warriors_scanner_for_rating(rpc_connection): + print("It can take some time") + token_list = rpc_connection.tokenlist() + my_warriors_list = rogue_players_list(rpc_connection) + actual_playerids = [] + warriors_list = {} + for token in token_list: + player_info = rogue_player_info(rpc_connection, token) + if "status" in player_info and player_info["status"] == "error": + pass + else: + while True: + if "batontxid" in player_info["player"].keys(): + player_info = rogue_player_info(rpc_connection, player_info["player"]["batontxid"]) + else: + actual_playerids.append(player_info["player"]["playertxid"]) + break + for player_id in actual_playerids: + player_info = rogue_player_info(rpc_connection, player_id) + if not is_warrior_alive(rpc_connection, player_info["player"]["playertxid"]): + pass + else: + warriors_list[player_id] = player_info["player"] + return warriors_list + + +def warriors_scanner_for_dex(rpc_connection): + start_time = time.time() + token_list = rpc_connection.tokenlist() + my_warriors_list = rogue_players_list(rpc_connection) + warriors_list = {} + for token in token_list: + player_info = rogue_player_info(rpc_connection, token) + if "status" in player_info and player_info["status"] == "error": + pass + elif player_info["player"]["tokenid"] in my_warriors_list["playerdata"]: + pass + else: + warriors_list[token] = player_info["player"] + print("--- %s seconds ---" % (time.time() - start_time)) + return warriors_list + + +def print_warrior_list(rpc_connection): + players_list = warriors_scanner(rpc_connection) + print(colorize("All warriors on ROGUE chain: \n", "blue")) + warrior_counter = 0 + for player in players_list: + warrior_counter = warrior_counter + 1 + player_data = rogue_player_info(rpc_connection, player)["player"] + print(colorize("\n================================\n","green")) + print("Warrior " + str(warrior_counter)) + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n","blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + input("Press [Enter] to continue...") + + +def place_bid_on_warriror(rpc_connection): + warriors_list = print_warrior_list(rpc_connection) + # TODO: have to drop my warriors or at least print my warriors ids + while True: + need_buy = input("Do you want to place order to buy some warrior? [y/n]: ") + if need_buy == "y": + playertxid = input("Input playertxid of warrior you want to place bid for: ") + price = input("Input price (in ROGUE coins) you want to buy warrior for: ") + tokenid = rogue_player_info(rpc_connection, playertxid)["player"]["tokenid"] + token_bid_raw = rpc_connection.tokenbid("1", tokenid, price) + try: + token_bid_txid = rpc_connection.sendrawtransaction(token_bid_raw["hex"]) + except Exception as e: + print(e) + print(token_bid_raw) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + print(colorize("Bid succesfully placed. Bid txid is: " + token_bid_txid, "green")) + input("Press [Enter] to continue...") + break + if need_buy == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def check_incoming_bids(rpc_connection): + # TODO: have to scan for warriors which are in asks as well + players_list = rogue_players_list(rpc_connection) + incoming_orders = [] + for player in players_list["playerdata"]: + token_id = rogue_player_info(rpc_connection, player)["player"]["tokenid"] + orders = rpc_connection.tokenorders(token_id) + if len(orders) > 0: + for order in orders: + if order["funcid"] == "b": + incoming_orders.append(order) + return incoming_orders + + +def print_icoming_bids(rpc_connection): + incoming_bids = check_incoming_bids(rpc_connection) + for bid in incoming_bids: + print("Recieved bid for warrior " + bid["tokenid"]) + player_data = rogue_player_info(rpc_connection, bid["tokenid"])["player"] + print(colorize("\n================================\n", "green")) + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n", "blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + print(colorize("\n================================\n", "blue")) + print("Order info: \n") + print("Bid txid: " + bid["txid"]) + print("Price: " + str(bid["price"]) + "\n") + if len(incoming_bids) == 0: + print(colorize("There is no any incoming orders!", "blue")) + input("Press [Enter] to continue...") + else: + while True: + want_to_sell = input("Do you want to fill any incoming bid? [y/n]: ") + if want_to_sell == "y": + bid_txid = input("Input bid txid you want to fill: ") + for bid in incoming_bids: + if bid_txid == bid["txid"]: + tokenid = bid["tokenid"] + fill_sum = bid["totalrequired"] + fillbid_hex = rpc_connection.tokenfillbid(tokenid, bid_txid, str(fill_sum)) + try: + fillbid_txid = rpc_connection.sendrawtransaction(fillbid_hex["hex"]) + except Exception as e: + print(e) + print(fillbid_hex) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + print(colorize("Warrior succesfully sold. Txid is: " + fillbid_txid, "green")) + input("Press [Enter] to continue...") + break + if want_to_sell == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def find_warriors_asks(rpc_connection): + warriors_list = warriors_scanner_for_dex(rpc_connection) + warriors_asks = [] + for player in warriors_list: + orders = rpc_connection.tokenorders(player) + if len(orders) > 0: + for order in orders: + if order["funcid"] == "s": + warriors_asks.append(order) + for ask in warriors_asks: + print(colorize("\n================================\n", "green")) + print("Warrior selling on marketplace: " + ask["tokenid"]) + player_data = rogue_player_info(rpc_connection, ask["tokenid"])["player"] + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n", "blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + print(colorize("Order info: \n", "red")) + print("Ask txid: " + ask["txid"]) + print("Price: " + str(ask["price"]) + "\n") + while True: + want_to_buy = input("Do you want to buy any warrior? [y/n]: ") + if want_to_buy == "y": + ask_txid = input("Input asktxid which you want to fill: ") + for ask in warriors_asks: + if ask_txid == ask["txid"]: + tokenid = ask["tokenid"] + try: + fillask_raw = rpc_connection.tokenfillask(tokenid, ask_txid, "1") + except Exception as e: + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + try: + fillask_txid = rpc_connection.sendrawtransaction(fillask_raw["hex"]) + except Exception as e: + print(e) + print(fillask_raw) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + print(colorize("Warrior succesfully bought. Txid is: " + fillask_txid, "green")) + input("Press [Enter] to continue...") + break + if want_to_buy == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def warriors_orders_check(rpc_connection): + my_orders_list = rpc_connection.mytokenorders("17") + warriors_orders = {} + for order in my_orders_list: + player_info = rogue_player_info(rpc_connection, order["tokenid"]) + if "status" in player_info and player_info["status"] == "error": + pass + else: + warriors_orders[order["tokenid"]] = order + bids_list = [] + asks_list = [] + for order in warriors_orders: + if warriors_orders[order]["funcid"] == "s": + asks_list.append(warriors_orders[order]) + else: + bids_list.append(order) + print(colorize("\nYour asks:\n", "blue")) + print(colorize("\n********************************\n", "red")) + for ask in asks_list: + print("txid: " + ask["txid"]) + print("Price: " + ask["price"]) + print("Warrior tokenid: " + ask["tokenid"]) + print(colorize("\n================================\n", "green")) + print("Warrior selling on marketplace: " + ask["tokenid"]) + player_data = rogue_player_info(rpc_connection, ask["tokenid"])["player"] + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n", "blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + print(colorize("\n================================\n", "green")) + print(colorize("\nYour bids:\n", "blue")) + print(colorize("\n********************************\n", "red")) + for bid in bids_list: + print("txid: " + bid["txid"]) + print("Price: " + bid["price"]) + print("Warrior tokenid: " + bid["tokenid"]) + print(colorize("\n================================\n", "green")) + print("Warrior selling on marketplace: " + bid["tokenid"]) + player_data = rogue_player_info(rpc_connection, bid["tokenid"])["player"] + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n", "blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + print(colorize("\n================================\n", "green")) + while True: + need_order_change = input("Do you want to cancel any of your orders? [y/n]: ") + if need_order_change == "y": + while True: + ask_or_bid = input("Do you want cancel ask or bid? [a/b]: ") + if ask_or_bid == "a": + ask_txid = input("Input txid of ask you want to cancel: ") + warrior_tokenid = input("Input warrior token id for this ask: ") + try: + ask_cancellation_hex = rpc_connection.tokencancelask(warrior_tokenid, ask_txid) + ask_cancellation_txid = rpc_connection.sendrawtransaction(ask_cancellation_hex["hex"]) + except Exception as e: + print(colorize("Please re-check your input!", "red")) + print(colorize("Ask succefully cancelled. Cancellation txid: " + ask_cancellation_txid, "green")) + break + if ask_or_bid == "b": + bid_txid = input("Input txid of bid you want to cancel: ") + warrior_tokenid = input("Input warrior token id for this bid: ") + try: + bid_cancellation_hex = rpc_connection.tokencancelbid(warrior_tokenid, bid_txid) + bid_cancellation_txid = rpc_connection.sendrawtransaction(bid_cancellation_hex["hex"]) + except Exception as e: + print(colorize("Please re-check your input!", "red")) + print(colorize("Bid succefully cancelled. Cancellation txid: " + bid_cancellation_txid, "green")) + break + else: + print(colorize("Choose a or b!", "red")) + input("Press [Enter] to continue...") + break + if need_order_change == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def set_warriors_name(rpc_connection): + warriors_name = input("What warrior name do you want for legends and tales about your brave adventures?: ") + warrior_name_arg = '"' + "[%22" + warriors_name + "%22]" + '"' + set_name_status = rpc_connection.cclib("setname", "17", warrior_name_arg) + print(colorize("Warrior name succesfully set", "green")) + print("Result: " + set_name_status["result"]) + print("Name: " + set_name_status["pname"]) + input("Press [Enter] to continue...") + + +def top_warriors_rating(rpc_connection): + start_time = time.time() + warriors_list = warriors_scanner_for_rating(rpc_connection) + warriors_exp = {} + for warrior in warriors_list: + warriors_exp[warrior] = warriors_list[warrior]["experience"] + warriors_exp_sorted = {} + temp = [(k, warriors_exp[k]) for k in sorted(warriors_exp, key=warriors_exp.get, reverse=True)] + for k,v in temp: + warriors_exp_sorted[k] = v + counter = 0 + for experienced_warrior in warriors_exp_sorted: + if counter < 20: + counter = counter + 1 + print("\n" + str(counter) + " place.") + print(colorize("\n================================\n", "blue")) + player_data = rogue_player_info(rpc_connection, experienced_warrior)["player"] + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print("--- %s seconds ---" % (time.time() - start_time)) + input("Press [Enter] to continue...") + + +def exit(): + sys.exit() + + +def warrior_trasnfer(rpc_connection): + print(colorize("Your brave warriors: \n", "blue")) + print_players_list(rpc_connection) + print("\n") + while True: + need_transfer = input("Do you want to transfer any warrior? [y/n]: ") + if need_transfer == "y": + warrior_tokenid = input("Input warrior tokenid: ") + recepient_pubkey = input("Input recepient pubkey: ") + try: + token_transfer_hex = rpc_connection.tokentransfer(warrior_tokenid, recepient_pubkey, "1") + token_transfer_txid = rpc_connection.sendrawtransaction(token_transfer_hex["hex"]) + except Exception as e: + print(e) + print("Something went wrong. Please be careful with your input next time!") + input("Press [Enter] to continue...") + break + print(colorize("Warrior succesfully transferred! Transfer txid: " + token_transfer_txid, "green")) + input("Press [Enter] to continue...") + break + if need_transfer == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def check_if_config_is_here(rpc_connection, assetchain_name): + config_name = assetchain_name + ".conf" + if os.path.exists(config_name): + print(colorize("Config is already in daemon folder", "green")) + else: + if operating_system == 'Darwin': + path_to_config = os.environ['HOME'] + '/Library/Application Support/Komodo/' + assetchain_name + '/' + config_name + elif operating_system == 'Linux': + path_to_config = os.environ['HOME'] + '/.komodo/' + assetchain_name + '/' + config_name + elif operating_system == 'Win64' or operating_system == 'Windows': + path_to_config = '%s/komodo/' + assetchain_name + '/' + config_name % os.environ['APPDATA'] + try: + copy(path_to_config, os.getcwd()) + except Exception as e: + print(e) + print("Can't copy config to current daemon directory automatically by some reason.") + print("Please copy it manually. It's locating here: " + path_to_config) + + +def find_game_keystrokes_in_log(gametxid): + + operating_system = platform.system() + if operating_system == 'Win64' or operating_system == 'Windows': + p1 = subprocess.Popen(["type", "keystrokes.log"], stdout=subprocess.PIPE, shell=True) + p2 = subprocess.Popen(["findstr", gametxid], stdin=p1.stdout, stdout=subprocess.PIPE, shell=True) + else: + p1 = subprocess.Popen(["cat", "keystrokes.log"], stdout=subprocess.PIPE) + p2 = subprocess.Popen(["grep", gametxid], stdin=p1.stdout, stdout=subprocess.PIPE) + p1.stdout.close() + output = p2.communicate()[0] + keystrokes_log_for_game = bytes.decode(output).split("\n") + return keystrokes_log_for_game + + +def check_if_tx_in_mempool(rpc_connection, txid): + while True: + mempool = rpc_connection.getrawmempool() + if txid in mempool: + print(colorize("Waiting for " + txid + " transaction to be mined", "blue")) + time.sleep(5) + else: + print(colorize("Transaction is mined", "green")) + break diff --git a/src/tui/requirements.txt b/src/tui/requirements.txt new file mode 100644 index 000000000..734da529c --- /dev/null +++ b/src/tui/requirements.txt @@ -0,0 +1,8 @@ +configobj==5.0.6 +pip==9.0.1 +pycurl==7.43.0.2 +setuptools==39.0.1 +six==1.12.0 +slick-bitcoinrpc==0.1.4 +ujson==1.35 +wheel==0.32.3 diff --git a/src/tui/tui_assets.py b/src/tui/tui_assets.py new file mode 100755 index 000000000..091484a40 --- /dev/null +++ b/src/tui/tui_assets.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time + + +header = "\ + ___ _ _____ \n\ + / _ \ | | / __ \\\n\ +/ /_\ \ ___ ___ ___ | |_ ___ | / \/\n\ +| _ |/ __|/ __| / _ \| __|/ __|| | \n\ +| | | |\__ \\\__ \| __/| |_ \__ \| \__/\\\n\ +\_| |_/|___/|___/ \___| \__||___/ \____/\n" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Print tokens list": tuilib.print_tokens_list}, + {"Check my tokens balances" : tuilib.print_tokens_balances}, + # transfer tokens (pre-print tokens balances) + {"Create token": tuilib.token_create_tui}, + # trading zone - pre-print token orders - possible to open order or fill existing one + {"Exit": exit} +] + + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.2 by Anton Lysakov\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + try: + print(tuilib.colorize("Welcome to the GatewaysCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.rpc_connection_tui() + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() + diff --git a/src/tui/tui_gateways_creation.py b/src/tui/tui_gateways_creation.py new file mode 100755 index 000000000..7bb489c7f --- /dev/null +++ b/src/tui/tui_gateways_creation.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time + +header = "\ + _____ _ _____ _____ \n\ +| __ \ | | / __ \/ __ \\\n\ +| | \/ __ _| |_ _____ ____ _ _ _ ___| / \/| / \/\n\ +| | __ / _` | __/ _ \ \ /\ / / _` | | | / __| | | | \n\ +| |_\ \ (_| | || __/\ V V / (_| | |_| \__ \ \__/\| \__/\\\n\ + \____/\__,_|\__\___| \_/\_/ \__,_|\__, |___/\____/ \____/\n\ + __/ | \n\ + |___/ \n" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Create token": tuilib.token_create_tui}, + {"Create oracle": tuilib.oracle_create_tui}, + {"Register as publisher for oracle": tuilib.oracle_register_tui}, + {"Subscribe on oracle (+UTXO generator)": tuilib.oracle_subscription_utxogen}, + {"Bind Gateway": tuilib.gateways_bind_tui}, + {"Exit": exit} +] + + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.2\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + try: + print(tuilib.colorize("Welcome to the GatewaysCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.rpc_connection_tui() + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_gateways_usage.py b/src/tui/tui_gateways_usage.py new file mode 100755 index 000000000..0c989e3af --- /dev/null +++ b/src/tui/tui_gateways_usage.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os, time + +header = "\ + _____ _ _____ _____ \n\ +| __ \ | | / __ \/ __ \\\n\ +| | \/ __ _| |_ _____ ____ _ _ _ ___| / \/| / \/\n\ +| | __ / _` | __/ _ \ \ /\ / / _` | | | / __| | | | \n\ +| |_\ \ (_| | || __/\ V V / (_| | |_| \__ \ \__/\| \__/\\\n\ + \____/\__,_|\__\___| \_/\_/ \__,_|\__, |___/\____/ \____/\n\ + __/ | \n\ + |___/ \n" + +menuItems = [ + {"Check connection to assetchain": tuilib.getinfo_tui}, + {"Check assetchain mempool": tuilib.print_mempool}, + {"Check connection to KMD": tuilib.getinfo_tui}, + {"Connect to KMD daemon": tuilib.rpc_kmd_connection_tui}, + {"Send KMD gateway deposit transaction": tuilib.gateways_send_kmd}, + {"Execute gateways deposit": tuilib.gateways_deposit_tui}, + {"Execute gateways claim": tuilib.gateways_claim_tui}, + {"Execute gateways withdrawal": tuilib.gateways_withdrawal_tui}, + {"Exit": exit} +] + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.2\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + # We have to call KMD specific functions with connection to KMD daemon + elif list(menuItems[int(choice)].keys())[0] == "Connect to KMD daemon": + rpc_connection_kmd = list(menuItems[int(choice)].values())[0]() + elif list(menuItems[int(choice)].keys())[0] == "Check connection to KMD": + while True: + try: + list(menuItems[int(choice)].values())[0](rpc_connection_kmd) + break + except Exception as e: + print("Please connect to KMD daemon first!") + input("Press [Enter] to continue...") + break + elif list(menuItems[int(choice)].keys())[0] == "Send KMD gateway deposit transaction": + while True: + try: + list(menuItems[int(choice)].values())[0](rpc_connection_kmd) + break + except Exception as e: + print(e) + print("Please connect to KMD daemon first!") + input("Press [Enter] to continue...") + break + elif list(menuItems[int(choice)].keys())[0] == "Execute gateways deposit": + while True: + try: + list(menuItems[int(choice)].values())[0](rpc_connection, rpc_connection_kmd) + break + except Exception as e: + print(e) + print("Please connect to KMD daemon first!") + input("Press [Enter] to continue...") + break + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + try: + print(tuilib.colorize("Welcome to the GatewaysCC TUI!\nPlease provide RPC connection details for initialization", "blue")) + rpc_connection = tuilib.rpc_connection_tui() + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_marmara.py b/src/tui/tui_marmara.py new file mode 100755 index 000000000..cfe628890 --- /dev/null +++ b/src/tui/tui_marmara.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time + + +header = "\ +___ ___ _____ _ _ _____ \n\ +| \/ | |_ _| | | |_ _|\n\ +| . . | __ _ _ __ _ __ ___ __ _ _ __ __ _ | | | | | | | |\n\ +| |\/| |/ _` | '__| '_ ` _ \ / _` | '__/ _` | | | | | | | | |\n\ +| | | | (_| | | | | | | | | (_| | | | (_| | | | | |_| |_| |_\n\ +\_| |_/\__,_|_| |_| |_| |_|\__,_|_| \__,_| \_/ \___/ \___/\n" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Check MARMARA info": tuilib.marmara_info_tui}, + {"Lock funds for MARMARA": tuilib.marmara_lock_tui}, + {"Request MARMARA cheque": tuilib.marmara_receive_tui}, + {"Issue MARMARA cheque": tuilib.marmara_issue_tui}, + {"Check credit loop status": tuilib.marmara_creditloop_tui}, + {"Settle MARMARA loop": tuilib.marmara_settlement_tui}, + {"Exit": exit} +] + + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.1\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + chain = input("Input assetchain name (-ac_name= value) you want to work with: ") + try: + print(tuilib.colorize("Welcome to the MarmaraCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.def_credentials(chain) + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_oracles.py b/src/tui/tui_oracles.py new file mode 100755 index 000000000..fec874d35 --- /dev/null +++ b/src/tui/tui_oracles.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time + +header = "\ + _____ _ _____ _____ \n\ +| _ | | | / __ \/ __ \\\n\ +| | | | _ __ __ _ ___ | | ___ ___ | / \/| / \/\n\ +| | | || '__| / _` | / __|| | / _ \/ __|| | | |\n\ +\ \_/ /| | | (_| || (__ | || __/\__ \| \__/\| \__/\\\n\ + \___/ |_| \__,_| \___||_| \___||___/ \____/ \____/\n" + +menuItems = [ + # TODO: Have to implement here native oracle file uploader / reader, should be dope + # TODO: data publisher / converter for different types + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Create oracle": tuilib.oracle_create_tui}, + {"Register as publisher for oracle": tuilib.oracle_register_tui}, + {"Subscribe on oracle (+UTXO generator)": tuilib.oracle_subscription_utxogen}, + {"Upload file to oracle": tuilib.convert_file_oracle_D}, + {"Display list of files uploaded to this AC": tuilib.display_files_list}, + {"Download files from oracle": tuilib.files_downloader}, + {"Exit": exit} +] + + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.2 by Anton Lysakov\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + try: + print(tuilib.colorize("Welcome to the GatewaysCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.rpc_connection_tui() + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_rogue.py b/src/tui/tui_rogue.py new file mode 100755 index 000000000..9942369e2 --- /dev/null +++ b/src/tui/tui_rogue.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time +import sys +import platform + +header = "\ +______ _____ _____ \n\ +| ___ \ / __ \/ __ \\\n\ +| |_/ /___ __ _ _ _ ___| / \/| / \/\n\ +| // _ \ / _` | | | |/ _ \ | | |\n\ +| |\ \ (_) | (_| | |_| | __/ \__/\| \__/\\\n\ +\_| \_\___/ \__, |\__,_|\___|\____/ \____/\n\ + __/ |\n\ + |___/\n" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Check my warriors list": tuilib.print_players_list}, + {"Transfer warrior to other pubkey": tuilib.warrior_trasnfer}, + {"TOP-20 ROGUE Warriors": tuilib.top_warriors_rating}, + {"Set warriors name": tuilib.set_warriors_name}, + {"Start singleplayer training game (creating, registering and starting game)": tuilib.rogue_newgame_singleplayer}, + {"Create multiplayer game": tuilib.rogue_newgame_multiplayer}, + {"Join (register) multiplayer game": tuilib.rogue_join_multiplayer_game}, + {"Check my multiplayer games status / start": tuilib.play_multiplayer_game}, + {"Check if somebody wants to buy your warrior (incoming bids)": tuilib.print_icoming_bids}, + {"Place order to sell warrior": tuilib.sell_warrior}, + {"Place order to buy someones warrior": tuilib.place_bid_on_warriror}, + {"Check if somebody selling warrior": tuilib.find_warriors_asks}, + {"Check / cancel my warriors trade orders": tuilib.warriors_orders_check}, + # {"Manually exit the game (bailout)": "test"}, + # {"Manually claim ROGUE coins for game (highlander)": "test"}, + {"Exit": tuilib.exit} +] + +def main(): + while True: + operating_system = platform.system() + if operating_system != 'Win64' and operating_system != 'Windows': + os.system('clear') + else: + os.system('cls') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('TUI v0.0.3\n', 'green')) + menu_items_counter = 0 + for item in menuItems: + if menu_items_counter == 0: + print("\nUtility:\n") + menu_items_counter = menu_items_counter + 1 + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + if menu_items_counter == 6: + print("\nNew singleplayer game:\n") + if menu_items_counter == 7: + print("\nMultiplayer games:\n") + if menu_items_counter == 10: + print("\nDEX features:\n") + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + chain = "ROGUE" + try: + print(tuilib.colorize("Welcome to the RogueCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.def_credentials(chain) + rpclib.getinfo(rpc_connection) + # waiting until chain is in sync + while True: + have_blocks = rpclib.getinfo(rpc_connection)["blocks"] + longest_chain = rpclib.getinfo(rpc_connection)["longestchain"] + if have_blocks != longest_chain: + print(tuilib.colorize("ROGUE not synced yet.", "red")) + print("Have " + str(have_blocks) + " from " + str(longest_chain) + " blocks") + time.sleep(5) + else: + print(tuilib.colorize("Chain is synced!", "green")) + break + # checking if pubkey is set and set valid if not + info = rpclib.getinfo(rpc_connection) + if "pubkey" in info.keys(): + print("Pubkey is already set") + else: + valid_address = rpc_connection.getaccountaddress("") + valid_pubkey = rpc_connection.validateaddress(valid_address)["pubkey"] + rpc_connection.setpubkey(valid_pubkey) + print(tuilib.colorize("Pubkey is succesfully set!", "green")) + # copy ROGUE config to current daemon directory if it's not here + tuilib.check_if_config_is_here(rpc_connection, "ROGUE") + except Exception: + print(tuilib.colorize("Cant connect to ROGUE daemon RPC! Please check if daemon is up.", "pink")) + tuilib.exit() + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_tetris.py b/src/tui/tui_tetris.py new file mode 100755 index 000000000..3c42d4daa --- /dev/null +++ b/src/tui/tui_tetris.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time +import sys +import platform + +header = "\ + _____ _ _ ______\n\ +|_ _| | | (_) | _ \n\ + | | ___| |_ _ __ _ ___| | | |__ _ _ __ _ __\n\ + | |/ _ \ __| '__| / __| | | / _` | '_ \| '_ \\\n\ + | | __/ |_| | | \__ \ |/ / (_| | |_) | |_) |\n\ + \_/\___|\__|_| |_|___/___/ \__,_| .__/| .__/\n\ + | | | |\n\ + |_| |_|" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Start singleplayer tetris game (creating, registering and starting game)": tuilib.rogue_newgame_singleplayer}, + {"Exit": tuilib.exit} +] + + +def main(): + while True: + operating_system = platform.system() + if operating_system != 'Win64' and operating_system != 'Windows': + os.system('clear') + else: + os.system('cls') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('TUI v0.0.3\n', 'green')) + menu_items_counter = 0 + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + elif list(menuItems[int(choice)].keys())[0] == "Start singleplayer tetris game (creating, registering and starting game)": + list(menuItems[int(choice)].values())[0](rpc_connection, False) + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + chain = "GTEST" + try: + print(tuilib.colorize("Welcome to the Tetris TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.def_credentials(chain) + rpclib.getinfo(rpc_connection) + # waiting until chain is in sync + while True: + have_blocks = rpclib.getinfo(rpc_connection)["blocks"] + longest_chain = rpclib.getinfo(rpc_connection)["longestchain"] + if have_blocks != longest_chain: + print(tuilib.colorize("GTEST not synced yet.", "red")) + print("Have " + str(have_blocks) + " from " + str(longest_chain) + " blocks") + time.sleep(5) + else: + print(tuilib.colorize("Chain is synced!", "green")) + break + # checking if pubkey is set and set valid if not + info = rpclib.getinfo(rpc_connection) + if "pubkey" in info.keys(): + print("Pubkey is already set") + else: + valid_address = rpc_connection.getaccountaddress("") + valid_pubkey = rpc_connection.validateaddress(valid_address)["pubkey"] + rpc_connection.setpubkey(valid_pubkey) + print(tuilib.colorize("Pubkey is succesfully set!", "green")) + # copy ROGUE config to current daemon directory if it's not here + tuilib.check_if_config_is_here(rpc_connection, "GTEST") + except Exception: + print(tuilib.colorize("Cant connect to GTEST daemon RPC! Please check if daemon is up.", "pink")) + tuilib.exit() + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() From 7ca041ee23431d14862dc33b5ff8e918d099c9af Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 05:52:33 -1100 Subject: [PATCH 465/787] Test --- src/komodo_gateway.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 1b0664bb8..4a17a1b3d 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1578,6 +1578,7 @@ uint32_t komodo_pricenew(uint32_t price,uint32_t refprice,int64_t tolerance) uint32_t highprice,lowprice; highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; + fprintf(stderr,"%.4f -> (%.4f %.4f)\n",(double)price/10000,(double)lowprice/10000,(double)highprice/10000); if ( price > highprice ) return(highprice); else if ( price < lowprice ) From 10df98e580a9c3ac52c8c19379aecc3c0c51265f Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 06:03:01 -1100 Subject: [PATCH 466/787] -print --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 4a17a1b3d..58054feba 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1578,7 +1578,7 @@ uint32_t komodo_pricenew(uint32_t price,uint32_t refprice,int64_t tolerance) uint32_t highprice,lowprice; highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; - fprintf(stderr,"%.4f -> (%.4f %.4f)\n",(double)price/10000,(double)lowprice/10000,(double)highprice/10000); + //fprintf(stderr,"%.4f -> (%.4f %.4f)\n",(double)price/10000,(double)lowprice/10000,(double)highprice/10000); if ( price > highprice ) return(highprice); else if ( price < lowprice ) @@ -1602,7 +1602,7 @@ int32_t komodo_priceclamp(uint32_t pricebits[4],uint32_t refprices[4],int64_t to { if ( (newprice= komodo_pricenew(pricebits[i],refprices[i],tolerance)) != 0 ) { - fprintf(stderr,"priceclamp[%d] %u -> %u\n",i,pricebits[i],newprice); + fprintf(stderr,"priceclamped[%d] %u -> %u\n",i,pricebits[i],newprice); pricebits[i] = newprice; } } From 2f0c3c23fa69fb14c12f38d04d1f08645035c34a Mon Sep 17 00:00:00 2001 From: Anton Lysakov Date: Fri, 29 Mar 2019 00:39:15 +0700 Subject: [PATCH 467/787] added komodoku gui --- src/gui/README.md | 27 +++ src/gui/Roboto-Light.ttf | Bin 0 -> 140276 bytes src/gui/Sudoku.py | 362 +++++++++++++++++++++++++++++++++++++++ src/gui/background.png | Bin 0 -> 308479 bytes src/gui/board.png | Bin 0 -> 5678 bytes src/gui/sudoku_kmdlib.py | 41 +++++ 6 files changed, 430 insertions(+) create mode 100644 src/gui/README.md create mode 100755 src/gui/Roboto-Light.ttf create mode 100644 src/gui/Sudoku.py create mode 100644 src/gui/background.png create mode 100644 src/gui/board.png create mode 100644 src/gui/sudoku_kmdlib.py diff --git a/src/gui/README.md b/src/gui/README.md new file mode 100644 index 000000000..8a3778ea0 --- /dev/null +++ b/src/gui/README.md @@ -0,0 +1,27 @@ +About +----- +Komodo SudokuCC GUI + +Just solve Sudoku and earn SUDOKU coins! + +![alt text](https://i.imgur.com/std99XW.png) + +To run you need up and running SUDOKU chain daemon built from latest https://github.com/jl777/komodo/tree/FSM and started with valid for your wallet pubkey in `-pubkey=` param. + +SUDOKU chain params: +```./komodod -ac_name=SUDOKU -ac_supply=1000000 -pubkey= -addnode=5.9.102.210 -gen -genproclimit=1 -ac_cclib=sudoku -ac_perc=10000000 -ac_reward=100000000 -ac_cc=60000 -ac_script=2ea22c80203d1579313abe7d8ea85f48c65ea66fc512c878c0d0e6f6d54036669de940febf8103120c008203000401cc &``` + +1) install dependencies: + +``` +$ sudo apt-get install python-pygame libgnutls28-dev +$ pip install requests wheel slick-bitcoinrpc pygame +``` + +2) and then start: + +``` +$ git clone https://github.com/tonymorony/Komodoku +$ cd Komodoku +$ python Sudoku.py +``` diff --git a/src/gui/Roboto-Light.ttf b/src/gui/Roboto-Light.ttf new file mode 100755 index 0000000000000000000000000000000000000000..664e1b2f9dbafbf6280305123d2df7fa0c66cee9 GIT binary patch literal 140276 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 literal 0 HcmV?d00001 diff --git a/src/gui/Sudoku.py b/src/gui/Sudoku.py new file mode 100644 index 000000000..7b46d93fc --- /dev/null +++ b/src/gui/Sudoku.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python + +# Copyright (C) 2010 Paul Bourke +# Copyright (C) 2019 Anton Lysakov +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import pygame +import sys +import random +import sudoku_kmdlib +import time + +class PyGameBoard(): + """Represents the game's frontend using pygame""" + + def __init__(self, engine, windowSize, gridValues, timestampValues): + pygame.init() + pygame.display.set_caption('Sudoku') + self.__engine = engine + self.__gridValues = gridValues + self.__timestampValues = timestampValues + self.__screen = pygame.display.set_mode(windowSize) + background = pygame.image.load(sys.path[0] + '/background.png').convert() + board = pygame.image.load(sys.path[0] + '/board.png') + boardX = boardY = 10 + self.__screen.blit(background, (0, 0)) + self.__screen.blit(board, (boardX, boardY)) + self.__tiles = self.__createTiles(boardX, boardY) + self.__drawUI() + self.__draw() + + def __draw(self): + """Handles events and updates display buffer""" + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() + elif event.type == pygame.MOUSEBUTTONUP and event.button == 1: + self.__handleMouse(event.pos) + elif (event.type == pygame.KEYUP): + self.__handleKeyboard(event.key) + pygame.display.flip() + + def __drawUI(self): + '''Draws the text buttons along the right panel''' + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 28) + font.set_underline(True) + self.__titleText = font.render('Sudoku', 1, (0, 0, 0)) + self.__titleTextRect = self.__titleText.get_rect() + self.__titleTextRect.centerx = 445 + self.__titleTextRect.centery = 30 + self.__screen.blit(self.__titleText, self.__titleTextRect) + + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 14) + self.__titleText = font.render('TonyL 2019', 1, (0, 0, 0)) + self.__titleTextRect = self.__titleText.get_rect() + self.__titleTextRect.centerx = 445 + self.__titleTextRect.centery = 55 + self.__screen.blit(self.__titleText, self.__titleTextRect) + + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) + self.__newGameText = font.render('-New Game-', 1, (0, 0, 0)) + self.__newGameTextRect = self.__newGameText.get_rect() + self.__newGameTextRect.centerx = 495 + self.__newGameTextRect.centery = 180 + self.__screen.blit(self.__newGameText, self.__newGameTextRect) + + self.__solveText = font.render('-Check Balance-', 1, (0, 0, 0)) + self.__solveTextRect = self.__solveText.get_rect() + self.__solveTextRect.centerx = 495 + self.__solveTextRect.centery = 220 + self.__screen.blit(self.__solveText, self.__solveTextRect) + + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) + self.__checkText = font.render('-Check Solution-', 1, (0, 0, 0)) + self.__checkTextRect = self.__checkText.get_rect() + self.__checkTextRect.centerx = 495 + self.__checkTextRect.centery = 260 + self.__screen.blit(self.__checkText, self.__checkTextRect) + + def __handleKeyboard(self, key): + """Get key pressed and update the game board""" + validKeys = {pygame.K_0: "0", pygame.K_1: "1", pygame.K_2: "2", + pygame.K_3: "3", pygame.K_4: "4", pygame.K_5: "5", + pygame.K_6: "6", pygame.K_7: "7", pygame.K_8: "8", + pygame.K_9: "9", pygame.K_BACKSPACE: "", pygame.K_DELETE: ""} + if key == pygame.K_ESCAPE: + sys.exit() + elif key in validKeys: + i = self.__currentTile.getGridLoc()[0] + j = self.__currentTile.getGridLoc()[1] + cell_num = 9 * i + (j + 1) + self.__currentTile.setFontColor(pygame.color.THECOLORS['blue']) + self.__currentTile.updateValue(validKeys[key]) + self.__gridValues[i][j] = self.__currentTile.getValue() + self.__timestampValues[cell_num] = int(round(time.time())) + + def __handleMouse(self, (x, y)): + for row in self.__tiles: + for tile in row: + if tile.getRect().collidepoint(x, y): + if not tile.isReadOnly(): + tile.highlight(pygame.color.THECOLORS['lightyellow']) + if self.__currentTile.isCorrect(): + self.__currentTile.unhighlight() + else: + self.__currentTile.highlight((255, 164, 164)) + self.__currentTile = tile + if self.__newGameTextRect.collidepoint(x, y): + self.__engine.startNewGame() + elif self.__solveTextRect.collidepoint(x, y): + self.__engine.getSolution() + elif self.__checkTextRect.collidepoint(x, y): + ret = self.__engine.checkSolution(self.__gridValues, self.__timestampValues) + + def __updateBoard(self, gridValues): + for i in range(9): + for j in range(9): + self.__tiles[i][j].updateValue(gridValues[i][j]) + + def __unhightlightBoard(self): + for i in range(9): + for j in range(9): + self.__tiles[i][j].unhighlight() + + def __createTiles(self, initX=0, initY=0): + """Set up a list of tiles corresponding to the grid, along with + each ones location coordinates on the board""" + square_size = 40 + tiles = list() + x = y = 0 + for i in range(0, 9): + row = list() + for j in range(0, 9): + if j in (0, 1, 2): + x = (j * 41) + (initX + 2) + if j in (3, 4, 5): + x = (j * 41) + (initX + 6) + if j in (6, 7, 8): + x = (j * 41) + (initX + 10) + if i in (0, 1, 2): + y = (i * 41) + (initY + 2) + if i in (3, 4, 5): + y = (i * 41) + (initY + 6) + if i in (6, 7, 8): + y = (i * 41) + (initY + 10) + tile = Tile(self.__gridValues[i][j], (x, y), (i, j), square_size) + row.append(tile) + tiles.append(row) + self.__currentTile = tiles[0][0] + return tiles + + +class Tile(): + """Represents a graphical tile on the board""" + + def __init__(self, value, coords, gridLoc, size): + xpos = coords[0] + ypos = coords[1] + self.__fontColor = pygame.color.THECOLORS["black"] + self.__readOnly = False + self.__colorSquare = pygame.Surface((size, size)).convert() + self.__colorSquare.fill(pygame.color.THECOLORS['white'], None, pygame.BLEND_RGB_ADD) + self.__colorSquareRect = self.__colorSquare.get_rect() + self.__colorSquareRect = self.__colorSquareRect.move(xpos + 1, ypos + 1) + self.__value = value + self.__gridLoc = gridLoc + self.__screen = pygame.display.get_surface() + self.__rect = pygame.Rect(xpos, ypos, size, size) + self.__isCorrect = True + if self.__value is not '-': + self.__readOnly = True + self.__draw() + + def updateValue(self, value): + self.__value = value + self.__draw() + + def isCorrect(self): + return self.__isCorrect + + def setCorrect(self, isCorrect): + self.__isCorrect = isCorrect + + def setFontColor(self, fontColor): + self.__fontColor = fontColor + + def getValue(self): + return self.__value + + def getRect(self): + return self.__rect + + def getGridLoc(self): + return self.__gridLoc + + def isReadOnly(self): + return self.__readOnly + + def highlight(self, color): + if self.__readOnly is True: + return + self.__colorSquare.fill(color) + self.__draw() + + def unhighlight(self): + self.__colorSquare.fill((255, 225, 255), None, pygame.BLEND_RGB_ADD) + self.__draw() + + def __draw(self): + value = self.__value + if self.__value == '-': + value = '' + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) + text = font.render(str(value), 1, self.__fontColor) + textpos = text.get_rect() + textpos.centerx = self.__rect.centerx + textpos.centery = self.__rect.centery + self.__screen.blit(self.__colorSquare, self.__colorSquareRect) + self.__screen.blit(text, textpos) + + +class Sudoku: + """Represents the game's backend and logic""" + + def __init__(self, puzzleFile, rpc_connection): + self.__puzzleFile = puzzleFile + self.__rpc_connection = rpc_connection + self.startNewGame() + + def startNewGame(self): + self.__linePuzzle = self.__loadPuzzle(self.__puzzleFile) + gridValues = self.lineToGrid(self.__linePuzzle) + # prefill 0 timestamps for already known numbers + timestampValues = self.prefill_timestamps(gridValues) + board = PyGameBoard(self, (600, 400), gridValues, timestampValues) + board.setValues(gridValues) + + def __loadPuzzle(self, listName): + self.__chosen_puzzle = random.choice(listName) + puzzle = self.__rpc_connection.cclib("txidinfo", "17", '"%22' + self.__chosen_puzzle + '%22"')["unsolved"] + print "Puzzle ID: " + self.__chosen_puzzle + print "Reward amount: " + str(self.__rpc_connection.cclib("txidinfo", "17", '"%22' + self.__chosen_puzzle + '%22"')["amount"]) + ret = [] + linePuzzle = str(puzzle) + for i in linePuzzle: + ret.append(i) + return ret + + def gridToLine(self, grid): + linePuzzle = '' + for i in range(9): + for j in range(9): + linePuzzle += grid[i][j] + return linePuzzle + + def lineToGrid(self, linePuzzle): + assert (len(linePuzzle) == 81) + grid = [] + for i in xrange(0, 81, 9): + grid.append(linePuzzle[i:i + 9]) + return grid + + def getSolution(self): + balance = self.__rpc_connection.cclibaddress("17")["mybalance"] + print "Your balance: " + str(balance) + + def __solve(self, linePuzzle): + linePuzzle = ''.join(linePuzzle) + i = linePuzzle.find('-') + if i == -1: + return linePuzzle + + excluded_numbers = set() + for j in range(81): + if self.sameRow(i, j) or self.sameCol(i, j) or self.sameBlock(i, j): + excluded_numbers.add(linePuzzle[j]) + + for m in '123456789': + if m not in excluded_numbers: + funcRet = self.__solve(linePuzzle[:i] + m + linePuzzle[i + 1:]) + if funcRet is not None: + return funcRet + + def prefill_timestamps(self, grid): + timestamps = {} + for i in range(9): + for j in range(9): + if grid[i][j] != '-': + cell_num = 9 * i + ( j + 1 ) + timestamps[cell_num] = 0 + return timestamps + + def sameRow(self, i, j): + return (i / 9 == j / 9) + + def sameCol(self, i, j): + return (i - j) % 9 == 0 + + def sameBlock(self, i, j): + return (i / 27 == j / 27 and i % 9 / 3 == j % 9 / 3) + + def checkSolution(self, attemptGrid, timestampValues): + # [%22%22,%22%22,t0,t1,t2,...] + attemptLine = self.gridToLine(attemptGrid) + + #print attemptLine + #print timestampValues + timestampsline = "" + for timestamp in timestampValues.values(): + timestampsline += "," + timestampsline += str(timestamp) + arg_line = "[%22"+self.__chosen_puzzle+"%22,%22"+attemptLine+"%22"+timestampsline+"]" + print arg_line + try: + solution_info = self.__rpc_connection.cclib("solution", "17", '"' + arg_line + '"') + print solution_info + solution_txid = self.__rpc_connection.sendrawtransaction(solution_info["hex"]) + print "Solution accepted!" + print solution_txid + except Exception as e: + print(e) + print(solution_info) + solution_txid = 'error' + return solution_txid + +def main(): + while True: + # Assetchain hardcoded here + chain = 'SUDOKU' + try: + print 'Welcome to the Komodo SudokuCC' + rpc_connection = sudoku_kmdlib.def_credentials(chain) + pending_puzzles = rpc_connection.cclib("pending", "17")["pending"] + puzzle_list = [] + for puzzle in pending_puzzles: + puzzle_list.append(puzzle["txid"]) + + except Exception as e: + #print rpc_connection + print e + print 'Cant connect to SUDOKU Daemon! Please re-check if it up' + sys.exit() + else: + print 'Succesfully connected!\n' + break + newGame = Sudoku(puzzle_list, rpc_connection) + +if __name__ == '__main__': + main() diff --git a/src/gui/background.png b/src/gui/background.png new file mode 100644 index 0000000000000000000000000000000000000000..dc4844a0bfb7b2e023fb5b008c3ad35565277f6c GIT binary patch literal 308479 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%0DIMW)1ob#@zUwv~hUnR6cWAd1 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

@^0LcoFZ<}_-7$K_YTr&=5cC(gJyQ+M#*Z1%m% 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�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

+tP3Z)2P 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 literal 0 HcmV?d00001 diff --git a/src/gui/komodoku/Sudoku.py b/src/gui/komodoku/Sudoku.py new file mode 100644 index 000000000..7b46d93fc --- /dev/null +++ b/src/gui/komodoku/Sudoku.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python + +# Copyright (C) 2010 Paul Bourke +# Copyright (C) 2019 Anton Lysakov +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import pygame +import sys +import random +import sudoku_kmdlib +import time + +class PyGameBoard(): + """Represents the game's frontend using pygame""" + + def __init__(self, engine, windowSize, gridValues, timestampValues): + pygame.init() + pygame.display.set_caption('Sudoku') + self.__engine = engine + self.__gridValues = gridValues + self.__timestampValues = timestampValues + self.__screen = pygame.display.set_mode(windowSize) + background = pygame.image.load(sys.path[0] + '/background.png').convert() + board = pygame.image.load(sys.path[0] + '/board.png') + boardX = boardY = 10 + self.__screen.blit(background, (0, 0)) + self.__screen.blit(board, (boardX, boardY)) + self.__tiles = self.__createTiles(boardX, boardY) + self.__drawUI() + self.__draw() + + def __draw(self): + """Handles events and updates display buffer""" + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() + elif event.type == pygame.MOUSEBUTTONUP and event.button == 1: + self.__handleMouse(event.pos) + elif (event.type == pygame.KEYUP): + self.__handleKeyboard(event.key) + pygame.display.flip() + + def __drawUI(self): + '''Draws the text buttons along the right panel''' + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 28) + font.set_underline(True) + self.__titleText = font.render('Sudoku', 1, (0, 0, 0)) + self.__titleTextRect = self.__titleText.get_rect() + self.__titleTextRect.centerx = 445 + self.__titleTextRect.centery = 30 + self.__screen.blit(self.__titleText, self.__titleTextRect) + + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 14) + self.__titleText = font.render('TonyL 2019', 1, (0, 0, 0)) + self.__titleTextRect = self.__titleText.get_rect() + self.__titleTextRect.centerx = 445 + self.__titleTextRect.centery = 55 + self.__screen.blit(self.__titleText, self.__titleTextRect) + + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) + self.__newGameText = font.render('-New Game-', 1, (0, 0, 0)) + self.__newGameTextRect = self.__newGameText.get_rect() + self.__newGameTextRect.centerx = 495 + self.__newGameTextRect.centery = 180 + self.__screen.blit(self.__newGameText, self.__newGameTextRect) + + self.__solveText = font.render('-Check Balance-', 1, (0, 0, 0)) + self.__solveTextRect = self.__solveText.get_rect() + self.__solveTextRect.centerx = 495 + self.__solveTextRect.centery = 220 + self.__screen.blit(self.__solveText, self.__solveTextRect) + + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) + self.__checkText = font.render('-Check Solution-', 1, (0, 0, 0)) + self.__checkTextRect = self.__checkText.get_rect() + self.__checkTextRect.centerx = 495 + self.__checkTextRect.centery = 260 + self.__screen.blit(self.__checkText, self.__checkTextRect) + + def __handleKeyboard(self, key): + """Get key pressed and update the game board""" + validKeys = {pygame.K_0: "0", pygame.K_1: "1", pygame.K_2: "2", + pygame.K_3: "3", pygame.K_4: "4", pygame.K_5: "5", + pygame.K_6: "6", pygame.K_7: "7", pygame.K_8: "8", + pygame.K_9: "9", pygame.K_BACKSPACE: "", pygame.K_DELETE: ""} + if key == pygame.K_ESCAPE: + sys.exit() + elif key in validKeys: + i = self.__currentTile.getGridLoc()[0] + j = self.__currentTile.getGridLoc()[1] + cell_num = 9 * i + (j + 1) + self.__currentTile.setFontColor(pygame.color.THECOLORS['blue']) + self.__currentTile.updateValue(validKeys[key]) + self.__gridValues[i][j] = self.__currentTile.getValue() + self.__timestampValues[cell_num] = int(round(time.time())) + + def __handleMouse(self, (x, y)): + for row in self.__tiles: + for tile in row: + if tile.getRect().collidepoint(x, y): + if not tile.isReadOnly(): + tile.highlight(pygame.color.THECOLORS['lightyellow']) + if self.__currentTile.isCorrect(): + self.__currentTile.unhighlight() + else: + self.__currentTile.highlight((255, 164, 164)) + self.__currentTile = tile + if self.__newGameTextRect.collidepoint(x, y): + self.__engine.startNewGame() + elif self.__solveTextRect.collidepoint(x, y): + self.__engine.getSolution() + elif self.__checkTextRect.collidepoint(x, y): + ret = self.__engine.checkSolution(self.__gridValues, self.__timestampValues) + + def __updateBoard(self, gridValues): + for i in range(9): + for j in range(9): + self.__tiles[i][j].updateValue(gridValues[i][j]) + + def __unhightlightBoard(self): + for i in range(9): + for j in range(9): + self.__tiles[i][j].unhighlight() + + def __createTiles(self, initX=0, initY=0): + """Set up a list of tiles corresponding to the grid, along with + each ones location coordinates on the board""" + square_size = 40 + tiles = list() + x = y = 0 + for i in range(0, 9): + row = list() + for j in range(0, 9): + if j in (0, 1, 2): + x = (j * 41) + (initX + 2) + if j in (3, 4, 5): + x = (j * 41) + (initX + 6) + if j in (6, 7, 8): + x = (j * 41) + (initX + 10) + if i in (0, 1, 2): + y = (i * 41) + (initY + 2) + if i in (3, 4, 5): + y = (i * 41) + (initY + 6) + if i in (6, 7, 8): + y = (i * 41) + (initY + 10) + tile = Tile(self.__gridValues[i][j], (x, y), (i, j), square_size) + row.append(tile) + tiles.append(row) + self.__currentTile = tiles[0][0] + return tiles + + +class Tile(): + """Represents a graphical tile on the board""" + + def __init__(self, value, coords, gridLoc, size): + xpos = coords[0] + ypos = coords[1] + self.__fontColor = pygame.color.THECOLORS["black"] + self.__readOnly = False + self.__colorSquare = pygame.Surface((size, size)).convert() + self.__colorSquare.fill(pygame.color.THECOLORS['white'], None, pygame.BLEND_RGB_ADD) + self.__colorSquareRect = self.__colorSquare.get_rect() + self.__colorSquareRect = self.__colorSquareRect.move(xpos + 1, ypos + 1) + self.__value = value + self.__gridLoc = gridLoc + self.__screen = pygame.display.get_surface() + self.__rect = pygame.Rect(xpos, ypos, size, size) + self.__isCorrect = True + if self.__value is not '-': + self.__readOnly = True + self.__draw() + + def updateValue(self, value): + self.__value = value + self.__draw() + + def isCorrect(self): + return self.__isCorrect + + def setCorrect(self, isCorrect): + self.__isCorrect = isCorrect + + def setFontColor(self, fontColor): + self.__fontColor = fontColor + + def getValue(self): + return self.__value + + def getRect(self): + return self.__rect + + def getGridLoc(self): + return self.__gridLoc + + def isReadOnly(self): + return self.__readOnly + + def highlight(self, color): + if self.__readOnly is True: + return + self.__colorSquare.fill(color) + self.__draw() + + def unhighlight(self): + self.__colorSquare.fill((255, 225, 255), None, pygame.BLEND_RGB_ADD) + self.__draw() + + def __draw(self): + value = self.__value + if self.__value == '-': + value = '' + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) + text = font.render(str(value), 1, self.__fontColor) + textpos = text.get_rect() + textpos.centerx = self.__rect.centerx + textpos.centery = self.__rect.centery + self.__screen.blit(self.__colorSquare, self.__colorSquareRect) + self.__screen.blit(text, textpos) + + +class Sudoku: + """Represents the game's backend and logic""" + + def __init__(self, puzzleFile, rpc_connection): + self.__puzzleFile = puzzleFile + self.__rpc_connection = rpc_connection + self.startNewGame() + + def startNewGame(self): + self.__linePuzzle = self.__loadPuzzle(self.__puzzleFile) + gridValues = self.lineToGrid(self.__linePuzzle) + # prefill 0 timestamps for already known numbers + timestampValues = self.prefill_timestamps(gridValues) + board = PyGameBoard(self, (600, 400), gridValues, timestampValues) + board.setValues(gridValues) + + def __loadPuzzle(self, listName): + self.__chosen_puzzle = random.choice(listName) + puzzle = self.__rpc_connection.cclib("txidinfo", "17", '"%22' + self.__chosen_puzzle + '%22"')["unsolved"] + print "Puzzle ID: " + self.__chosen_puzzle + print "Reward amount: " + str(self.__rpc_connection.cclib("txidinfo", "17", '"%22' + self.__chosen_puzzle + '%22"')["amount"]) + ret = [] + linePuzzle = str(puzzle) + for i in linePuzzle: + ret.append(i) + return ret + + def gridToLine(self, grid): + linePuzzle = '' + for i in range(9): + for j in range(9): + linePuzzle += grid[i][j] + return linePuzzle + + def lineToGrid(self, linePuzzle): + assert (len(linePuzzle) == 81) + grid = [] + for i in xrange(0, 81, 9): + grid.append(linePuzzle[i:i + 9]) + return grid + + def getSolution(self): + balance = self.__rpc_connection.cclibaddress("17")["mybalance"] + print "Your balance: " + str(balance) + + def __solve(self, linePuzzle): + linePuzzle = ''.join(linePuzzle) + i = linePuzzle.find('-') + if i == -1: + return linePuzzle + + excluded_numbers = set() + for j in range(81): + if self.sameRow(i, j) or self.sameCol(i, j) or self.sameBlock(i, j): + excluded_numbers.add(linePuzzle[j]) + + for m in '123456789': + if m not in excluded_numbers: + funcRet = self.__solve(linePuzzle[:i] + m + linePuzzle[i + 1:]) + if funcRet is not None: + return funcRet + + def prefill_timestamps(self, grid): + timestamps = {} + for i in range(9): + for j in range(9): + if grid[i][j] != '-': + cell_num = 9 * i + ( j + 1 ) + timestamps[cell_num] = 0 + return timestamps + + def sameRow(self, i, j): + return (i / 9 == j / 9) + + def sameCol(self, i, j): + return (i - j) % 9 == 0 + + def sameBlock(self, i, j): + return (i / 27 == j / 27 and i % 9 / 3 == j % 9 / 3) + + def checkSolution(self, attemptGrid, timestampValues): + # [%22%22,%22%22,t0,t1,t2,...] + attemptLine = self.gridToLine(attemptGrid) + + #print attemptLine + #print timestampValues + timestampsline = "" + for timestamp in timestampValues.values(): + timestampsline += "," + timestampsline += str(timestamp) + arg_line = "[%22"+self.__chosen_puzzle+"%22,%22"+attemptLine+"%22"+timestampsline+"]" + print arg_line + try: + solution_info = self.__rpc_connection.cclib("solution", "17", '"' + arg_line + '"') + print solution_info + solution_txid = self.__rpc_connection.sendrawtransaction(solution_info["hex"]) + print "Solution accepted!" + print solution_txid + except Exception as e: + print(e) + print(solution_info) + solution_txid = 'error' + return solution_txid + +def main(): + while True: + # Assetchain hardcoded here + chain = 'SUDOKU' + try: + print 'Welcome to the Komodo SudokuCC' + rpc_connection = sudoku_kmdlib.def_credentials(chain) + pending_puzzles = rpc_connection.cclib("pending", "17")["pending"] + puzzle_list = [] + for puzzle in pending_puzzles: + puzzle_list.append(puzzle["txid"]) + + except Exception as e: + #print rpc_connection + print e + print 'Cant connect to SUDOKU Daemon! Please re-check if it up' + sys.exit() + else: + print 'Succesfully connected!\n' + break + newGame = Sudoku(puzzle_list, rpc_connection) + +if __name__ == '__main__': + main() diff --git a/src/gui/komodoku/background.png b/src/gui/komodoku/background.png new file mode 100644 index 0000000000000000000000000000000000000000..dc4844a0bfb7b2e023fb5b008c3ad35565277f6c GIT binary patch literal 308479 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%0DIMW)1ob#@zUwv~hUnR6cWAd1 z9dC3~!qe%5P)&u>uwAcG3{U%G9;vz+WE(?xt#I?Pfts3zG$Jpx6eL~OO_eu-JdM6a zjL4V`hw&4l@nB4Gq^PRzd_~3xmH^N(ohqFynjj zc-E#CF-A!_m<$;09A@Lry+{GEp0Wx)qN0{Ti=6JmR%`L`RfrYG-kZUg;7;mNEEUa# zEjY@CySuyL?9L$$_6{}zMkpd;LO?A6A;?*2X0v=|H#cM+bMT__aoMiO?l3Eu?0##` zOCGH@m|igoSi0Y!tj|h*Pg;Dxen$JoA0q#qzb@xHfMv~!+Lpn6e&4HPYgVIt6z4Y0 zd|KwA`k70`G7E9?B2$;-QFh|d6;n{Y;^O<8K?L7d`g<;yN383~BdCmSQ`NK1Gfmq; zoNNrK5WTPq2(@6}4&)``_rLiK{LlZ(|AZEIL^Pap#%awspE4FjPHU?u6*y`^t6c$T zJU?X(qP2is<8&^&iN^xgx?tb0It>dr_JWH}qJ(u& zugwZ4CUkR#nDk%Wyswd?xNmUn;I7ww;G-BP#$>-7T*_lPhRm4&C>MipTF&Qt9EUm0 z9tssMgR*awpsNe;x;i=6jpS%!@_5CdP>FegtIT8rk0yjRv(M8Z&Qjw!f6dT~9&)|= z=VBvGhUVYqkZoP2!|5sGK!@J#vz_wEyC=Iej$`XQHCnvVRV5{E233dlFk`-CT~(-s zk{uST*x^k#%ken@U=GU;8aI=38h9TDQO}0A%pr0$pS>q?OrP@`XPbbcnOFu(J2ysA zqwRWeQt75+?>Vu47_q40X&zUZQ#!kLhm;(tgAs?1ecB6E#+L)ZI1D_fK2nXIw`rq` zoRaei%jtHH``@lIFW^ zy=g;ng>M(ubo4l-jJ;eCnvUga>8{;z!K+uFqP89Hyz?$%)v*;wbPuoocP+d+YFtkx zRNTgkQh@Fro^E61sGfNtr9SS|X2sOjX3^4PZiKF}Z&#uhi5ow%j4y}Z->h-naXR1W zIZ<%EKI3=3@h|X4fAo7;a>c{C;(Y&rhlhLIo$qlvtyq_{f4)|O{;=Q$WkYk%%TgPz z+ZE53E1sX9@!6yN^XYQM^L59Uzx-AFM}PZ2#`$!I>t3-eXXIr?K*X_Mu8?U7f-l-?-4ilJ};MxJ0=6w_ZG*V97?92#Tbkb zSiwGc=BYt2ub-`GK~z9SzRO@Gl5>_UH#$pNYgo=_M;v(Fz0H#;%sziwEP{}Upz%Ol zoST1^6;>7?J%nF}=%d1+c?vcHCFHD{*H16r8p?oD4F$o}nD(_G{IODs6R52Tx$c`Z z1Emgh0zR`tjEIiDbTdg0A}11e4N}PPyDoK?coqo1^EfJ@(Z)3_h>9*rQDRwul3#lq zSDa2~9J}}+t?Sag@KQo(@B*t(KEz@{V#AV*5%XveYkO`!Tl_q>rr6YY>Z@k^gKb-E z>S6`gP7@A}HtUAlV`#cGJCYH~jr4jsu>JGjL;IDVmmp)yK~-Nvdq<5>{gf6j>=@zq z^{mznfsB7$fMJ3l0BKopY?~Bmns+#lh3(w75gn>$M+>Bhz z4%_GRc|-sSxio~yi%CNAc#P+0NK_sZl8?-SDiiq`M3sy~$2(?;VyNPwsDpRcZRAck z`JUd@tnRZ5W-R>Ugpx@{rgP4MnI^^oJ(GQ}CX=LK6a*^_)+-Ht#O@;)wH0w<`-FTO z+JHI*+t!Abw3!X@hV#ki$Knl;jGeeq=7Qm92k!45uw9G0!Z!#9s(Hl7LTf}a1QDxb z%c%ioHE|FG(Ym`6^eG^twqy9LA(;OXCS<639L#YP9~U#%&0OFs(Oqo($Kr=X#fKqS ziAJOLXJ@3>?0ynWf)4lSs!l!dJWQn@3_R9P-{6zq{FnIPAO2l+RD&9BcOb&}$uR=k zxlbIz; z0TyD!zF$P7MEkP=19z+(2fqLPzeEVYiEmwxGQ;hh(s$GWU|^YmJi9>mSR6!BGr zj;^=kKzMrmr13>2o3?YB(K+dho;q7oMOZSxDj3K6;3>Oe>}!Wv&LEPHp= zTkC{Zc~D7Rh>cKOTmk4*27Q@1XXItUzHRdFViZHSZ66SnI*bvrhdMF|*bX{FxOleS z4`cWxh{gg3uLTThjAGPEnhB|8^mn34e(7t67gb0@X+10<^K9Ib-tvb}3sE_I@WmZ~-TZILPXOu&X9{xb)y%~Hi?L{v!8d13t!#?5HGkAM&5)1^V#XX+an18xH!#Q0Z?EZYBXdzQ0G zO$5T@!!X%Ru;&j)VzCi12b{C(ywy2pdmw=fxhKn!r_?cf=0L8a6O*7My>5o=hK zoj-FVSWF3JKd_!o?%(B24MiQ9C@2QGoU0c6Hde9XtGm9FYRe;cio2?tBCjcl-kMpF zx2yd{z_1?BfTN5SAz|Q6sVONvcR>G6&ZtMZv5jDaEGVjNV;n;`_%co@VJ{m_=X>m1 zanVq%yDQu{RJV^}g~VTuN}u~se>zgz&LCGO(hhz9Z2y1Ao1tC|fBc7BwubBiK6jtzhD-EZUl z_dciZM#k~&*m2!96hr6N)klvhWwaO&TGT>wSBlRCAAR%@Vp>&Tm+_UaensxX?Sjko z8J~Xg1AO-S6+V0X45eiJ^`HMmynOjSo*!T1`SBHE&N!Xco`ivj8l+*pZU@qb|Kry` zG+sv^AzcP;O;h7IJ2;~HNFCee2$Y#_z0I4Y`U`@S!z>JXcCfZFjv+|KoiYNWn0Qrk zA%~iKEy!73_M(QOu*zH^!Vn{_&lk_~>*Vz6U7F+wHVgf$Lf zP@fc(R*@Ge2%8>c@?IFNggU6_9%H3sUFTOV3l=Sejrh;1*-EGwiz;kSFoI=c_GHL} z$6JhCL>{U0xSz)qJSJp6H@9k0Zp?Z*d4XZd!($gw%V`x-@RFkzSfXGn<_9W8DuG!gUb>Pp|}2Atu>0P}5O(!o@awHp+#tD?B(+CD|1uzi9A9$)=p zJ=AEC$B?qVR#8pGqz&qr7{DB)neG6}M=YvvmVMWQgk!XEdlF@`?p=;ix^7q8-QVN+ zazU$26x@0q8;Zi#JA>h++7ocI&4$JHjFxqE^s0|@dmbJ)THCg{3YO1)D~Q&0@yHr$ zOpUYaEk%1In7N0W;&NG5=T>za8ZDwU(6|L@2I7(xG3z3-WK`PItsd<@(P4X;nnKb*P2J;2x_k5b+xLPsVF%w*f!yHEk+B`QDNZCq;?Ve zwxiX8?|<|?EK7DTKq=q?_CpBxrR-SOjJxv*(Bd+*D#DI(py2>e@#f8^`1I2sU_YL4 zT%K?`uQ;y@-hcOfq>zweL#Rc$WfB7YHO#DOs0T18CIhtj>8V#Cdtgg2`kZj$9Dqqh~?c121nmL&~p z9#+`!p=h1kqERy2b;G(Yez|PhrV6|TrEcmhEgf$P5qpttHe|_%I;%p8{ZIr-v|`9y zs*`q%<>nBxQJ6f7q{u+j!X&7Ra2iI60|y|&{g!6+g=f*3=hu{1#uwupF~S`v^nh=g z|7Hiqx@1Rt_>h(&+u}8Z2T~x!?c=hk1Y3#={yglrGOMr{rD6CvPKN>e^LP=Z$f*63 z3$4R)s7ljrHQbPu`$$fp-@&^`PHtQ`%{yD~x*t%MIHRb=S~XKY^NO1SiJ3E(9I+F= z)U3#LGI=RLt;iPPGac0I>BJ(3ZZcXlKeCH1JapLubGb+`WNE)Cf*7SW(FKoz7!_mg zN2PJ06`f`~A-qkw;CJF@F^bE^upq-hyO3?aTb^i~xMe(coH;FyqJ`0oT!_n*g{OIg z!MRpJ3#{aZqNNr4PHt8(^%4qt1z0M zTI6ACgx?3aeMJ9!@H3ppux9s z4>E>~LqRu_^ckJC&mhXPw3cZ ze20gJmw0%1N0i#3pq2x#UcC~8Dz8}A6H;98{PcvfUyzp|u{*BDl?c3Nt#@jTNMHZ@ z*FI#pmk)Z|Xv?yy19-a`i7YCQE`&6342XDAt#0dJ-Ib`-Mrl8f5Bj(QI^&e+ket;Q z#T6LAJySwph&;HP_qwczSrf|jz)uACODC~-CZZHOQ02neZ5%+)rNh2atqnRPK1Auz zp>NSJy+DcN{(5*u9H*S~VZ$6BE55AYnhg|X(+3Y#%j!*vq+wa<7H$@CvmdE)GnLShUddJ0R56` zj+_k^0lLmie!p#-TP2v;WL0mPIXjNIG=YM;jUW`KaZ5~TECQ%Fh|}7ipCK1K@*Rq2 zKFT;V4dhrO(vRb!pwS@thJVBY241f`9%PP}b?zSVVGhxHAz3sQ7tie6&tn=c+=K(O z>Bvh)zQ-<u zJ^~*tEym`=&K};IL!d8-QbLfXoOlo&D|pB{D1=+C5hFXKhon&7b+ zBT6hjYJ`||W8-pzd|E?NiXaY#+z#VUbh10IK}P-}jRgUqnKG1A>Ar8Zcr^Qzx#>0H zHy5#t+I9dUdt>5}UpWJh7hM17+qnCyKdG@ma#n0KRhjvp&d*y`wA#FgH6EN59JV*) zyea;DB-Nt1-xOLC&#fkTd}7tGJBz89*vAB2zKXexcXiB1_+gon?o8JZs9a}2qM zqZw!($kOan#PJUnS##_cy!!YfY}*BKS%4PQF}J8UQt|^>Mx;u-jdIwB28Xn-*DD?# z9;PC?3@Dm;crhX}3m9hJ*^JiB1viTxLeh(=^cbU=gl}q4UK{*QiaK2v=W7T@sVQQ* zseQ(JI(e7IVdk@6QX+2|M!GCUX%LZ2qKinM%Rb|>$~c$Tx4A%fBi>^u^14?X6+d=S zYe&m#kU@@_JdvU!H5uKc7`ydGs}R0#ax>as zrjvfIHAe+NX%YTBC<4sSFC`%hH!-+%gZ>cp-x|Hf5IPlO$PZLyy8SFGw(7KB7||Ls z>Ov>npY*->p4YQgkdFm<$phJ&&%NIKjE?jsK;SjUXJ2a#pMLr&Uc7kGM|&v@RuXI{ zVvW(7-Fxo)U!15b06AD^rWX9XkXZIH) zi-r)oqrFhWs!}H8jI#Gs>oKSvjT>i(TB3=^t5+Xm->-;i)yV~VKCPKV2cQNKBDJQY zyCe?(>w1UNS;&dn5 z@>V<^=K0ou@aFZ$%@%ryP3xXCjQa#pdQvr4cdL$=(VV`>xM+~E6P!nomt}YvERp3! zBh|P)aEl;G&b_)*^T>V?L~`pDH|0Y+OhY{)!doTJOvoP$H5G;g$(&OXsr^s6$jQ~TM^qMdCHB^U(kFx<6X$2?^0${^)xahFKUo!ZtsSG1pqBZ ztT_O+pq7dn6Ux!1fZyuew!t0z5O1ja^I3}Ml#!Pn6U35fE{fQsHci3d91P9{h5hrO z4r_K8cZ5#oBS|wUlZ`3Od7KOdJvjg}PPDmw%t^lr=001BWNklm!+?tb*Uu->C1XqR-WxZKSOBF!TE7CYU3bJ3l%x({FNT)pluO+>W-^VM1}e z_H}A&XWtLyhE>lZ#H9M&yusYLUxuXK5vp|cvNK_jW z{!|SxPx}1t#CLH?ZW$r>+@>ih=uIi4u=#)g)xShqGVUJU$GWUN(=mo2PuHCa@+wVQ z&f;06b;M~sEBahCSbFjAwrMl<=IMf`%M%hBe)NOS;gkYWUMFs}pm5$)+XM|#5N>-# z`ucDD;}6lg{e4h%n-g)>TPkQ*ep*&U%gq?aG&~8n7ab7hff38m|f)}d>7*{g_|rf;qo-Re)R*~ zpE5#N5Y>fzmhtjvjEeIm^DPRW9d6 zN2(10wwHxC9VS1p!>e)muM~2p)tuK=*+$Vn_RdXK&jH`F86+bg_I>S8GuiS%J|{fl zvI{Qi%__=TRUnb_)JgO-X0t$QL+V6XA_r4fHujL}pzN5OvJ)7KujzvnnqiRVi&1Iy z)B^TyaY;3+<|yPj+6b*Sl(G*jjw1#ZKDiBr%iA$havY7eHqRL zUUzdMLWt6pigdAK&jfkL9@<{*yQ;)TV6f-Ig%7+|nP{humkHyR;!Y^g>>e9HZ5T#-}>Vb$j zAmpz43K4l(C5{CF=aXDoHlCKweH50Yl<@fYI22e+nXm%Ol)^Cv9ObH!G0m+nY>nH` zw(lHMcx#At&KcWvyE)tDn5Q0vHU{1AYU{!Nj36V~^>lJ{u?ww6z9a_sbhuK`Oq@#< zU$UGfJ)za8oWDM`K?rEi8=n98JGlG#pZ55nS-tJNRcmmnjLGCPb%9!pKsh8{Cg^!1 zHa87<&4LCQA1efuvSC?I-ZWWcmBbx&fdVUmYm;U*Ym>?&<S@_R${ zeAiy^~b(mPl z2bhnbXk@}x0ag&UHXh`FEYOF$JJA7!1>4b(lN6~l>3^{NpA;?jOi!m(RcUNwIg*_% z#(>?FgApa^MayM5cJ-Ev#+}{1A5MI?V#=WHxA|Ey8fD*MKA7gv=*|yV2G@GFEEp01 zCgr3@nGD4K?wMlnxcv?*zWHL7j=9T&5duE?=p(%I@?9se+F|KNdNj(ct-2rD+oFOv zzD3sA<|-_EhKmx=QLA|4_~+WyIw5+|XE83vzB%V7s5eckZQ#xsV!$>h^IDrPBmSOj z7Rc||e2s{(LI_Irj7URP8P%+{;?Uy9_Y0`(p9EU=36^ ztJ1Q5=1mJZblXEnWy(3=F?&YRAm5k=j5mwyb&LQ=KNwXONTVt(u4BTY$ z&$IOzD6pFgL2kN*>$}JpNjyG`~KfwK8`ziVP)|j;Bb`cWUWrCB`Ee@iL z;ZLWNs4s0~R@=Y_GhZ|VQUrx6Jx*xy*JEDrAav5RLKK9>&t^ln_fl0|9o_iW>WMZ* zKrPGSqd^vrlzPQCzxlgTM5%>D(t=S6rL1V^3Gcjo7cXAC<3_hJEgI{S#0fu$h()~h zk~bjseYWiaw1VsPf^r;q@7;H?#svt(t!`o-+)y3tk@bE^ifn5*N=5qG*Z%tt8-j!e zN1EeKi?{6J17h%C)>*M#N<7{(u8v*wo@t~CC_x(5fj?4-8^v)s4)@16jAj-h7tXOP z)>#-T#fSjIzC6EQ^m{!&KkL8W0EqG>gx~?mLqtTasQWI8jF7SI4fUvl`f!e9;e*Ri z1}2D$s^N^{MM-zC&gv)}LcmoKMBXq^Ik7D|nd#@j#aRx7;$xi7-#YxCb9B$F7PJHM zbUF<@8!j5p=kwr2#Tkv9$2fC{_;MZ)6dGUIXSN(XR{L1SRU!g6TXL9G-3qLC-+g!J zgqYW1VX8LHU6x5r$L66yZ0uZWthGofqQY^bRej%0vl@-?Dg2U%Wl37p?+5|7JU=1l z)$;*s6+*T>+p4CdbJP+muj~@ImC57A2E#8FLYJ&qlny=&s9&8TZrFFo_LYyo^=F#O z@laW~6}Pk9z^t5Q{>U6TWarAD2&4TsDrUR6tQavuO+Fi9%*bich@OIV&3;)H4NkUf zwve)i^t!2|x`hTvtO;602qN_sRbat-T9I-Tgh?}`Q|iIutSq#VV^ABCfIelI8(GW$ z>n4hkMyP$<9w3*l@o8$S-SdeOB778RJRvpD<^3x~g-kW_y2_bLaWK5*_*`Gt#hXV9 z4v&Ojjd^?BGm&W=+>BXs6Ls{%P2fV!C>K_Yu5-!+7m%Re1L9uszIq$)taE4EP#jt| zX3o(O6de$XDXNNzaNTfvv*Y-&cd@Le0qJ++RE>~HSwtb0Q`nlEzedM+dVbVE^aYJZ zma^YV549&%Dx=yAT(EV~Aac>iR9hMDI-8ip7y*y+X)0Qa9yrYuBah-sGL@;VqLeGX z`K^DAv?PQSQ4VSDRz*~oWyQ;v@8JIa1=jV9Wm&y&mz05k6c$CTbN8rI)jwL&>{u}{ zuXV?^UC~0r7eDwhL3l%vajMxe#Doy)FhLR2)6SNJ^tG@3cOS+Ov94J;DBW*^6(1?~ zEVyzULkc%9+Sz2#7NLxf#sh#a)ts|?dCVr7feI-5cO1*Y9k6{_`)r0N9LIsvy5hUv z{Vv{n@4cJOk2f^*z%w#&mxXupRks;PK&t_l{Xhy?6=KH?fs~2b>@{S+(>Nxllg5_l z%eU`m#itz{=es)}iTQ%IlyvG;4VmJ1!4?vx*p$&uaWZ%*7>pKsXS=w0TUSkvJPCVV z=B>ot=M;I6W`6e%_@=j-6XWOTNBoynB3j>tQP<>S~Y^RBD{lIvv>cPjP&A- zt%!>$^!N5ijM#ZE=73I|G~W^5pD-u}fia!=^vW zm*(8j%h);(A;dPe^ovl11CeqQOQ~MuJCaDG+@?Ng96j>33yXyk9>T6fSk9B1-Ph;! z$5t5UyE`X>59W=U*B3o{MNr*<z;-Mg>3V96ShfZ#D&7z@s{UZdN zbluG=aof*^gxc89&VMf6SYd1^)v|Ok$LzK*0vkQEs*HYZGJ&THwmCLD9JTtXY*vT-h={GuYmLRLBL`wl44_1F)0^PMdo%)Q#kzkSXx zT$9=wzVxLp4S})dj+}GyQJ0`4c9}2ga=q*5EDp+#QYyBxVa+E10mptBlw3R_~mVgfOx0~hr|-eeRO7WX&S9{{?eibqgA2Xei=VKJ@taIiE&`k>^I9# zV>0v{(IT5Rco?sdaXE{T3Ggi2x63vVDrTSG(%}s)yq+^*`C6RbMuGd(M--6 zIf)Vzt#eQ~YcN<*(5mN@F%7`>y1g?-KO>nunxB}3r<)S<*!MBKTLZW`3_K`Gf4;jD z$@yExpS3mY&(FG7wV68t&gVNV=9JTQXg=O`lTJA&MS_x_b(T+B+aVmPB5SAYho)mx z@$S=RNK9`>9#(VQp^X>ND9V$|QO{Q^MC^QXb=`zonh?O!E-B5{RYNTkJY*^@hcltL}aPjpKq8YLV&%P97n!L+mKtxAGYH33u&Pv5rn~tEye`f^z zlRETU@VS%JwTP+2ue1WvIGf{`65jOjNPe&}GJ%Uy7MKpv$F_Ai3yysk-jsgN7$S~Z zv7AS z61M$W&b-=sL{9B?DFAS~I|H@&T0oPqdx_8@8c+;4YC((*r@K4c-#_5Zn^*Yc;~(Jh z@d-=%1o!t3IN#mjbUNei?jB1%;o<%Pd0DY65h*P=ogcK;$jE7>{XwxgE-VUI^6G%k zx~xbsqqPKt10l$nW86-s9qD9aS8jC987LK54vXZMnRFmo`5=o!zsIo=aXsCP_vB(| zLj(;M8LT{F`vg;S@Q8-z+Ev|+Pc~ANQvvm;H^`NFSv@~LVO`H!*&bMyvvbQh8&N)2 zR7FL9u`UmYDPg-_hp3#H55tP54%b#9i%Zws zpVjWqH>jGYn}^EgY<>x{n{rTcZt%mKQ9bVrwZbnA*e)ucM|#Y+C$|dsWL-tccRrtoLoQKa!G*%efzHhi(u07Bi@SHX@InZlq%AYz=%kJi$#wR-8-}|VA1p%``Y|a|R zQ*1xyy6Zjhy?WDv zw^CEA*n9EtFyvY?%|(njr9djJqH4oozy+sV$k`mQs1dl?%Ura->;gL*g4mh#L!Ch*gJ`Ys-CXW8nADc-+Oes zj~I|cmY=x;U;L3D!@gf|K0inr$$7=GU$Do3%hMyS&yTn}Kj8JNk7ezJj290N*tZ>7 zEnol(}xKfg7p|I4Aq_d_hU=pWw#Um8YJRalG%m~OlhGIDK&l86 zyS@Ytg|*MnVd>Q`QSl5aPQ>Qf`Iv=RT_sRTyXo|q7sj0kdw#tQisrEsUkc0voBdT7 zS>WHDhVe@oMnCg^cpSp;u)PQCI9qE<9Gs#sYQF$G6pot=x0#@BV-G_)=4a!Vr9#FQ zqHCX*tp=>XsFa6ghj^*Y<3kL7w!-c5{Mv?$8fVfN_6=C^qP8J^9=Ts8qU%LOJUvr*sabV4Dd#~Taf`|vxP zd2;OREj)+iEeQaly^4zi6u}g7aHkRYXkVKL8!I!RdG^%f@ODP!xDeqBXC9Suux(1v zhz=?8a!RO2bJutu-U`AP3{y zDye;Pu^wV6~o!@GivXl-Y6omi;@ia2D&G$mO6>qdsJ<_I+ zoO{5%*k0_~fcN!#yZUPgfk4S)Kle~3T%)BjJ#gh76;@VkVQx-5$rf0~;3eD1tl zQbY+#HP00 z_Z^qZCaEZBNdM>`{kI>+7>KOb$eF#7YM>vGDGBeRhK8e7R6wMYfD=JEB47>@wM$|i z^SOV8UK;zpt73vdy?~U{4GB-YnUX92%%j6dcKow#kz41@Dl=PzAqXeP#vknE#PfD0 zigSUuuIo(*vW*V8n|T`%enxv(lG};vVIXqyXqEW|25kr8dvW1mWc6pYt>B`r)o#Vm z{i^ct_gR_6&$6}RmjE9=j8@FWs6AWzonorjl%JJ`BH(hl+*n>1qE{(0hsoP~_?Svm z4u7&VAOy&s0h8rJ2w06%G8rUV;HQ~Z+Fo&4FQEjE3Xs!uA_UTTMvDCK;mbuxrN|*h z4hGbKx@cSfJi_NBfP=j0SmJJB(9GMi(Tf#4?#Gj}8zQEhU4rcjHVu%qXA&@ue!aoa zJ!0eZk$W-{;R`7;hiDv1YcQ*gbzQx{ml3O+M`uO)YL`e63ZhC7g@X12bGu@SD zsz#rGSHS3>C+OwksUoMKAE>?rw^%t?PpCeCIE)EGzP| z;Cw!NpmP^`1vj}CPH3d}b@-^#a(9EK!<2GGUJ@Q3Kf~qvgx7CA#;2cthR?nKdE~qx zC7=~xyF5#VXo{0FfQ*q%L1})4>7V?QU;D6BNzgu>S0!W`HzU!Z)xD3*UcaJB$Zbj< zY@1^r_3#*kRf1d$FrvZZ4cmxCXYl^eH4UCeZe3kiCxt_GG>QuuE)cqBQ}D|ogy#IC zbzKJ;?zU~t>u>_LUNG*LLdBI8QFi&w945ZF8F}C?)DW(yoB(lUPELMY^|_9t!8#ZY z&hagF2-u;4$xK}9!z{!XO6wO*JVmc5g*4qcH;M-FwiZ|I`^?}o@2 z^27y^e+J#)EGtelVuGgE^WRyNFT#iXzHb9>$-k31vWIT8H@Z2iqE$CuBz4S;f(|1u ziq7Cplb_uj)6>XBdAzVYQJGDBS-fHeJxh(}5jHmkjHcRJGUHh<@agwk9Bd%|haoT= zn%kze$L`ryY=I1d-}cTqX2bTT{qN?5mQuLM2MriAKINI=W(;lL-->u^IJ__?gqnTa z#ON^NayhW=8}9B_0Ec$r(Vg#`DoM?$of8%;__+_-CbYsXJB&f>LVSyQ$Ou%N!2lGj}0p}zVw2m&?`LJ!<%{^csffah?)f<%W z{1B&~{VKM7N7S(-s~(x-%*Mw9a!aT(kL{hecvM97g^Jn?9?d2TLT@OwBG4$m!K@sD zli67{=xC~ndmNh7#av4_D~F%$#m#VypXX}W)_{<(o-+ROyWjJ5Yu{D)($zsBSL$mL zgc*zq2$D3fIC_yDYSnP)L-H2)1N*Tfr4!zN|3`5;ttiKaloMj8ICdG=*4B$%G00x@ z9*}ydq9N@2n^ukkwN-4}j`Qi%iGEFaGSR$vtT>S*?6imnkBsxG8kUWn*~L zVyIoI73aINtBvxq097Qv)`gk5BOh|62r{*s5OuS1-xin39{X{ly0JVe-?-*AqX_4E zIw1zV+;Z$L)LKv+dr`9S;juTXu3uBCv838i5HV5YxC|}uylCejS$L;qL`i!O<>;o5 z$05j~5I==8CmEjQt>&r2$UqAbRx9yn*FL9-B4UgZT|(V77v@%!8yY(!5&V6EX5m#u zHY||VY))bbcz%9%w09nd8{)+AQ?qqQQY2NY0=0aOKH6YPK&9e|UA7xJvU6ea{QNvv zxEvJW#lAzr?+Gq|u?sh)?1r^@StO4w2&cqCXuEZSq7_l>HVnaX)N}IoGt#tk1M7m* zX+dpQw6Y_{wWGOK=%Dlh3gSjzr{`gRhkuu$prsb1kX%XS&1Gm>#8w^0$!O5unKwMD zoUDzA@*?JGC_u5eus(r63^<+coZxFo6D&@!ePvCsSaewAh*(z09^FalTKz(|pkf|n z0ktP}7`Ia+j;uH}zS{Y``qX4u#CFA%xawWS$fKL~mNKMqY}fgrm<$}^09v<#-y>7$wz#A~-ZP~uY zq&hTLe*qm7O+2k)KpH3-Cu&%^XH4q9(?S>Os(zFmAeB2)A2?%_DL-6r`RHBwUDC7$ zRWX`#fcR>Kmeho{;MHfJ;@|$;Z~6MTUN8C?blj-i#PxJSR9lJ|n2wQBD_Tgnzk3fKeDGxeSx3Daj`9rDg8d>C4QN4MbPd~DIZDIR)1#oJU;p*5eQ0d@ ztO~Q3GKw9#(V|~Q=s2pGS+e}C2AtN@;D5qZew{`m*XwoYP;FepY7FZj7DAh{`k=_= zL^JEOsDFh*!bn19HA6<@=P(@U=t=Nm%1G5Yi(izDkL2gBE=}JyF(FH;M&SaHRf=<0 zF?Jp&KX_!kAjKXlWCS5Q>xC{#zvCFWS7DI!_e*XLi?(9d9lce;zbM?=YDG>GZM5W7 zN5qS&4x20K%*w&STeTk6*)|YUz;RTU;`RX1G>stDBS^dFs)3!!U|biS73a8c;IKvD zOh{+ml_9Z4JD9C97BZHt)Xce+O6;An z1m0Fx5OqG59F)OD9o^*A-r41P5!I9Buqpjup9)zU9lL30kV32+J5mgYLFfy|cBsK- zMTMeQr7Dj|QTbZbQX~xVQ3|m+|I22r79j1HN&rRW1Tu2L>b zv#_xyaNi2n(-|oQ=L6P>pE`?}&^<`apT-LHkmK~(=PcaEsxb^rSYdYIy zL;c2DA&o0wUp61|RIaqJKm1+fSpT?NgRw0hxY1@Hd4!zo0>EUN8D3j(M`&D^PR#ZtB z*Rz+JcZi&u#iaE)XOPu|c8D;Pn{om|a#Y7gDj{??KgN~}79cvt28~iV2zdy$r8PYZXV`GGkAM#^vVb1o`~3VY zURZ-{UY}DM`CNvY@|gq`NHo1*XojWgsLp3iDdX~d6;v>4tWAxcTi7g5O~*7GEri$= zkv1wdB-xSv5PIIK7FlibaWGQ6*kgquW3o;G#32M6+jc_`4y_I*$x$y8j+8+YZtff? zJ%o^x5$sHjV6g>rlXxxK=+Mq%o`kS>RjtAsYqhC~ZzRrc9!c`r^vN11q&Oz31(Cve z0yF!sbp#I6y6kgaF{M|;P(@;{0)PFC$N%e_SpLp0$YrgKCMpJc$}AR}pLbEzNJJM_ zga;?bTY=A_G$XPPJja+uaZU(biA{SpP`LJ-=3z}>tyTDrwcEAGvkP7%*;=jMD2M}l zQ`6&bed~8n>qXt+CEmx@l)d|OI=iaUYLbAApbGt<=FoXPBSv+g&-eK1m;VYo-4x`LHoGZCDPF$aRP{(bXE z;SQb08O(j)O=+l#RpPbU1#Jd#MG0_u-~gm0`_H^QU)0Yf48>I`#mRhfaa>7(5vTmV z9umr*xlzvD+X~H|nU=hJNIAJw{B%0G{y$*BJ#PiI!sFjn-kAXB~yARb|M=ALnp85Ol)JE)9Iw&FWjh#{QVgk zNf){i?`=I8(VV{v|7X=MvX+8CDph;deJmLC{X>zC({#Tt&4qynb+=GD0hR} z%?s9O*^-7Z&?vt9FI9y@+fdvdJ|aBS^tZmzz`@>C?O)1>Y z<2Z1beFyPKS$WRNX1mxt3v>!f=RM)(rZH%7CCLxh>lKfWk3$LuJBV-kJvd&+UF^vx zj8&|B=J6dm3rNH#X&b%#6d|$m!|Wl-wqt+&h*Qd_+m6(P$iNMHPeoa75I#Fsd5RK3 z(;UnyQ-K(Ks$;xWdB#ya+6>9>2z)pFKD^-oq2sKu&N(C7nG-wec|MF?3W)WvGz~B86g5=Fc zZLn&<7k}g{Na@rk7O-&MGDPw*kJZn660126_1AvwAAe}SoE3I{zTDJ%i#gybHU?8XmWXH z^rw%K+R%L&VS9EhH(ppL*rtJ>V5Gh9p`gjSnzD9DMP*8wAC))d)@k{g(1T}84_{6pJEov>Vo zapp{dhMOAZ8#D}r5E!CoqXbho@~Ef#0~w-gdm{K*SKlIU?;up^aT{*a&<5l&(jJI} z@ENtCQPBhSx)L!^)qrUkOPXCt(F=+VKZ8;TEEkjcdvmtwEYUYk%?(-zvu&X9n8t?) zPs(KTxcT{#$+-6QOlV!!m@YQvWQ37XD?87Q<8V~g_BNm0v)up}Ud*H9LXQ8AtwR$y zyv@}=(4Ox9zIiq;7v8sS>}7D7pEi*uuYN1u29q_mhdd?vLEutkdXqrY?u%n z?F&=KZ!=d`H8B*}!w-vRuFV`IE9e*v<8dG#uw&KF(HmXPEEiFA&d?19tak22%8SRB z_-EE^G?DQ3S+x}kDP^3`CodX}KyQWD-QB&9RT_0VJl-5fLjlW@hw9!oTHo*^0+OAT^E#kxa@o;NV~}?fMY)h1uM$K3XbTQBBYNen#;r) zg)<(D#7e9Pp}4idppqj8W9D$QHgfw~0~U3PetLQu4`>S@R!)g=2cS;U)|s23UQB^T zypfb*<5*0L{d&=Dkry_q*|y!s(rp@_vB%WD@9sOrow}_jE7EOD&jIJN>$w#Gb0ieU z4katnlAJO;4&v_I_9`RAP7L7y+iv}E@&ie%N%V^qT63l^u?EGsy8Y{?h+ ziSi!jxGdu!iBtG8yt_W;Idt4)xrxFXDaTGEb5(h!-kkOLjo|ywk+0Z!QbkiHJZ>aK z)Y9mA$j6L?h;?#(^thegAhjFON}xu-)O7BVH5DrZipugttGh(LPYjc1-rppLpL}7+?=qD-E1$?=%T(}H>4aXLPpWn$7j@!K0*E~Uq(v_OUejJ zQ<)X;ET9?2*VJS|Z81bt&^#X?gzo=l{N~z3^cG_9I43E{gEt(2G^y77SgJx!3Be{V z^bl-)mSa-^M(pOgts#)olhIhX16=65!4TCSYsLHTy@$*7QT?uj_q8k;DMm!idrb&9 zuPaVD15t7?bBb6`i#7%)q;$rYzw{G&=Ql5oLjanZEQi>q6Mi=51$h}o2Ze=iZ(e`g z7*=EifiF3PdXv?3&dU%PGXGr;K%6DOydcbr$y}z5YqFDpq8;M)=hzR#E~o4D8S6PC zV)68PJM=kbAw(drD_Yq})*aEBTmqkc_8KqWx%UDr(1FY-m2GxYY}IO))ZzRoyU-b# zGf0;?CG0sgM)8yZ%5EC)xh!|m1!Ax+k;;f64I`EwBHM=XH2;Umrz29>2AJGj_^mlOj@m6QCg*TA zrH#sQQT^{Q&Wa%+<_xsr{6BlP?6cL1@hwE9_wex2i~q61l}m3|m7a}*hR&QWCRp?waaWlt;gWZ&h{!ZMZnI2b?quGzP!xl#0xDe z;^^XJ_x0Jlm+enx-rYPZ9YUnvf^3*di}#bfmwIDC!8{s4zQw%wI1ZfDz>v9Nd@dSF zx9__bvz9Ar=99Cr@{FRnTeqnbz5Wq(S4h1GoBfoAb>d@qDY|MK%}Jeq)oy=3Vjy%; z&!~D3N`xDGgQIMCe0)@)$D)DKX&Q&?8L)^uzNz)xszrrU{A_Ge;YC$yh+z~MF|{EY zdIIwSV-$YOwq@nroPk=LP5_`4KgYP(w>@h2R$u>Z2zzGFHsh)GjN5{|oPh@H`!n|M z{s8HZ{}QMF;OEg!3sNaKj*8lXHdt4`+bm7Nk$hF|G7`oVpy#buqGZh=#d1{CO%#Tbt!bHoS=WnL;%t}K zc>Vf^A|zQ7w(USt8cwMVr&Vf%qe=Fx{QQiUFW<%a{H}&V%kOC+NUT>VXl+L*1+`V| z$93Sz@_Y4rw*+kbyq}+5HGTn1jvENb?HEFQPwR^9a=i(VWuwIT;^36+u=S8t`rlD5n(@EkHq9{>a zn#_WP7#vzZ_T9Vx!BEzefbL}kKd^F2xGKkGE>bZSp|is@7jPB=D@0sT(8@ZL*em0X z9i+e<#yrX=skP$n?(W7?!oKgO{5@NP#*Hw*M7%M)n^PI!8J)CofwP$djD2!{4GT(@I* zhK8uE8jaS^`$T0XAph?y(&_huL^C%3ea6F^MLz669%lD_y*E0Y!MK6R-yMkDY}G+^ zdye4IaMtA8`6J&g;_2yWqG_ly`qS5V{o8+l_y686;m8qDeV$@6PUCUo*v`CXLt55# z?Sd(L9;S3Xj^VsBo@pas!p7g0sc8yLw2J*u)F>w3JE37f=uLc)pc%T1*pfm~{+!)M zl26!L5TQ{EZexNF(L&X*a?M{10ihKjNaD=%^OJlo1Sy;dNp8WAbP?Tn6cobXEhpz< zL8(^+&4R7%82H0%oNmp^aR9rjTaQu@F3+DeLwdMSVp+Hf&~K{nHFYG%;L`K?>C9^OH|L=|R&ffoJa}IkXy5%wFuqm{6)n z+ZjQ#uFSqKiy>w&wbSX;eR%Y}mr`(de=o?f61F*9ytXz(=NJNI{6iiM%N{M>FLd+x8%PbuG(+H@YuI z4&m5|v*DwyG0RrtXJwHqb6k;~QH&h3VQN$AfzYcO@{$J6rGJ+ZFQ)_qa?CdaNE z%OV7PcL49M?|zwPF^qI#JMo)j;u26pMQq`%$ePnAlnjqXU_)p)%Asg*(mcDYn@EHc zcK%zzE|dvDqxR#1WjzlKb5`enMz3TYWw%3-=PKuvg;>v%k;~v4rw%T2qIT2S}6WU%sYPt)bLoKt4hU2=&$> z{cY-rSsKfo@gReqLOS0Do13W^cjd=XbQE!MUn!aQH|KlwklY})0VyG%ASN}3v~f|+ zsB{1`N%&AhJ}y?6z@Ha0X{~M*V73Yd_0RSRIcGOCt^q8SZWf)^b zqUUSx)h;(ym=*M>lvbsh&jh>2DYq_8zC_jurU}Wk&oiSZPLxD(UO^-Ho6(^ziIolB zm8HUOvUPVlov>{eB1q$=aO-@*=rjKq7d6aXoSbOSb>BAm-PVQetJzF<9z$O)@@yU+9vq1Ss;UXk?$mEx zoyS)3Xn>G1L$bo;=FWvrAfzR*(v6BJURTGyJA%hTfLVFW%1e6=h7R!A#UndLUjcYz z$#Lu+E^Neh8JQL{td?ej_R<{h#N%Q|oE>dRXccOH+r03K6AESMml1zEI+e(_L6L~i z)O@ijXDwljuyh8y8ko`q_2)&3r%ikTEdfN< zP?fs_Ks$<$>O?)+aFjMEMZ2irHu1wQMfofTQp_~kPGlUd%_^A%^P1)iQJ$vo{^D`A z+Y6Lk@0r87V~n2i7+?NfbQ_7Bv+og810RR&&7^-$@5Go8KYqkV|NI;H;P3rSlvBnU zB1&j()6iORN)1zSY^*ms%g^7Nd1RadVLV!}abPgV00desF8#JV zuHF>{FIwb<7CrQv{@LB5Yg%hqPbXJ#S;%Ih5ep-PtW*SmVj;0A$%ba`S@**kaY2dO zo%fW-cpf4y&wMN9P>r07g45~TRjVoDa=Bt%yZywrOQO4RI4m!ceXd3UfTCJdOtKG8 zC#mWjWT3o&B5U|YBm>YR+HLVGrt)dYcQ$3O#32Esx&U{7@_2*fG;5qpB+961@$sw z#qZeG6RV1Sygixwj!`eo3oDMcF(oS(DYX`LykC4&XayT{cFZcI){4{lgnE?0R>Cf? zTBS%ccX9+#RR%*Hsd;OZAL_k!6XWNo(I2IC6r-7uZ?NF#UK~{;fm?B+JggomGuk=4 zJuq0F=eA!Fs?=h}B}*w|J>R1i<*_<{G&+ghI8aN@%J(x<*y6^SgCerF!S`$ovv1_z z*Ib;zXN#Rn$s3sB+{w8~Wb|-Af|a!0RM{!WY8i?$ri+M&T)Mr^TOA|3Xx(=KlPLKt z-!6oJcvNiP`yrNp?~4dIAyf$hH&bl058C&``BY2~Vah=!E16|k2CD&}iJLn`Sv94j zjJ7eAM!s2uV1dLmgF3zoC1lhb@VU=_9;FsZ6lg^az3Co`5e+t?4W5ZC8WIs)AQ<;A zK@yOg*{3>3TxT}uKVke&XtyO-2r?o!T2SYLRm6bqdlsbuviE1tR#0zM-)i*LzVFgh zwz{?J^%)In;2D;IWX*h-K!ww~u80^9^x7Ks(j!sq@M9>|$f${m30ri$LA*;ntv69R zfu!}UK;f|x%hjEbN_l2gi>|1uDpSCiksOaD&2bx(+R?hzLS9RQdpFq&@aQx7LA&^- zY^r!kS>^J&kY;1iKB|+?E@~wy6b|K#ariPc8pTFMb2zROmGzz3Wxgznj~AJ5vhP<% z5hRW%4?o9{N*mX(*ujXChD31SH_x-r=QE99iWVYe5DLu5=Ik%U2v3{Bj^{D8tB2gN zc#)10Mb-|&U|7d7P|QVh9*FWIBr0dj1&pzK-kABOIY-;)W6v}m<$6#u7yddn&r<+3 zCbXH~_x${rU&oNYAJp1-49@EuPCE%dW(Jt2hZ>)Q}ni__Wn9ryS5 zg691HbiG@zZP|7n^!3{qbFRJX*bb2*3rBgyan(5|D5tix5o0vCO&F-fPY|`mOQMF1?Mhww;vDIaRyXnsbbPY3*y@WpeZr zXpMG=Xqm{0Lc4cG!7D<{f`AiJ*xe3d@kMh*&(DWNWAxImLR>4)5u?w9>yHVLSbmNAqGxT znNpC&Uw*+){?|Xkcfa+YfRL~uqKAoycxpb_reG=7Q%ZPyd;4HmY)zw3Qn=aIh!}~f zK@i>;S>b*vd_%x!&MJh4ghK~SAxCny6pORPUSz#BQ|Di&L95x)e9i$H!KFo7;Yrf$t1kFVdK zFNF86zn~b`hZ$pjhz{^h?I%B;KrTu_co)vhQ;RA6=N<|!E;hxh%~bkt7iP&HRA5_L zsKWYeOTKOi-EOqyJ7_P}^_-6>&ay1spXPRJzAl&XfG0p}q(GBHF$nj9E|O?3F%L1H zMhQG3ISlpdeV&M!{_^6q3`X%7vSwa}UQXs;bW!&W=f>*~n&2RIgSs7QdP(Vr6wVX( z``d}koLvaCzkK~+mYgx9FC4ClA&-ECKsFlj2A1CF(iiX7Xc#}j_PheWF3i4wSpC#{ z$DodV+l`NO&D2yM8lUZl5HRaYxGPaYRFgB~8-@@m%BJ0t=OlU_qYv^PUjF=zux!sM zAs4W!W{_UEqVMKf4EyMdZ>dDDMtlDAedHzLgf#gA|LW}R_Xhze(jEE&^;+2Z0)FLi z>S%~g>c^`QWjEh12s)VnsH=GTVB+a*HI|F{2BINn%J~^78mJe9at^g=V3I8it@7^I ztm2cv^C*$00Fu~D=zt`+^Gmr);$$ira!fMLr%_h2LWwTv4yVRso};O_OK*~6$njoE zz$dXm7_Bd>3V9#Xts_IhBhxmX$LoV_LJIM;Z?A>IHGuw!6dIHFn3ZbNaz0>k9-%JUIjKNI5%y%PP-Z z&{i3#`vTAmDWPUqi9+XFkuQ26UghDrWbu#M9M>w^A@%9gr_&{9Va8)D z>9o6mg7=fsTsJ)kE<{yd=$taqv|CpmjJwzrdevv3qn;QensmGV!YE>RdNq{v7p)>0 z(RE~ho>K6N^!azAD2)F5N+zx*;pfX<4>Rq^a?aTH%?e&07JGGA_gnx|__(ptwLsEh zsx@q~1Jl~VA0K5f*iYF7Pw-e1YX{kopa)KW%q zdhn~ewV_5AZ#K@;?5DBmHHXjJR7AH&9*>8uVJ}qCal5Iu$_9LQTMKYcAm2}9KQkpzJC463P|g^59dcVPJ6)Cjf3Kq99?V04?q0yVb6G8tu~^kzZAnoi`tt%3eEs@G)(#W?OW7rUjlKAE&*-_TIjOz36S&~- z)vav!^3$KoKFyM^d$#q^f6sCl9ly$pM5aTQ^>^_8|@xb9~32sRAVa zg&`(9pHG?kh`F^=FKo1}0wPhm#%z-%T`x3AK4%hOf`@>-7K4S8`&?Taaid#=8w)O$ zNvP7F)?X7LOey517?+ZuwG)^FQO4?8Q>a6|-I+Q7Wk8z036dh(?rrB(uVd0skFS(- z;R=wZb=aPm?8i6@9wg@tw1}G_MH>-$60|N(Lm*;0!MwUK^pdm%;T&2GajD}}c)%WP zIJw9BD4tU^fsMPG9W1ISTUlS>Lq3)MEN{N(eMWo$sJ8~t?$mfH+8NLA0l}P8Hif9* z%w^cg%ryrJZUN3YF`dNQx_jxkgFC=|L2IAs!b=JCwPI^tr~uzcuHPaG0g z*qJ4TvsCbx;MrdUj>i#}(&U0bdVY^xhVB5-LjD|s6F(5&?eZ}v{~L%V+Ng#K_{b(L zls#H_LR~Q}ibhH9eoeD-Xme`IDCqGp4=exqHbK@j3rxE{%^4$a@t~FxY7*-BLsv@; zUts#qUP-t*($(*KeSRm2@^D4Bv`@@kNBLL+|0odrwE?{evN@ z|7^?GjteFJc`B>lzKNLWT4rP6yA(@9lxFE-aTBbdV&uxJ;*y;~p1Y>J7SBQLIFJI5 zcJfw0Q`jeVzR37b_jQsr0FM7q4&pl3&gYemUA$$%GRay_+%zXT$L}JSZRH(K<`J>) zT#}JoIz8to1=vEBU0}1DHgMh{J|I`V$?&M9CuI}SiF4}xx`=rE?;F8kCfer-fffft zc(E20lM;q_C#pOA7}F4Ey@SSBacVxRS3k?^^+YMzvRUUCD7l>Ov-&4L#`xoZgSWr; zw{T3t1{NZ1+wBrFMaGfyBYtf%{D`)ca-Pb}A@1qS(d(lzzjVkQbmF^B_Fv35yX(nK zb!af>2q7&bFve4~8AQCmqLPnsVKHTi@QIRY07TckEt=2-C_BrTm9wa>0eoMCljj@% zPB$qrs|Xo~+gUbc3rx`L%n3{da~@|-tAHF`p8a7lUu^yJIx5oN|NH;#_n$w1cFx;p zl^IqM!6&Gu| zcoAKdaaZy-x%WaA5iv~-qX?K1-qrd-$)dGZ?6=#=H|bW<7A=$ zUMs^o^dw$t=lG&?CY2wf#=spX5l)4m9=JTpE~u49!5S*8qBB@ z;&u6Tk=a8mSo|EmtLnoOdRjJ9eqGnXhtOc*$_4qIF)pIK=WzO;?;YvY3+swvdS~~P zRzig5K>A|Ya~oF?wrYi|DXJf#Xny5!R2dO+W}v#gUyV)?*5Q4QbCIy{*_{mtY#)0b zmlqISM{*oUFI>ME%%MD{D8a^QDWRtZ>Jmolh|-8yWYioIAGV4sb(Qtc#hSLb&Z1Rl z&665Y--#7t41>vfO{aT6(He~tBbVgid1K_()G3{S&gRb7=It5}bmeD8;{~t4WqogU z7K?9La0s`Z9oqaz@urOs7pEiwnsQP}w^pT5Sb&1=Q$<%(iZ+^K&Qmli4&&E?km~^b z^aqr``s-*!NCZp?713xY-*6{OsTfWn9-SxP3Ssu@yCOtnqK-!Dn3!Lk7&@Q_WM+GQL_-|*Aq_#AHV%`i8z zKubk%WaRgy8eHSRV1d$ecJ{p#h9ZLJm9NQFYaNrsc87RWtW+5@KelEp;&>g*Gy8+z z|K0DO&nI@_EvX~;QhxwBC$xSDi(V8>rGOAHCQF9B=F=+2)(64I33>0 zML4=Kl~*ol;SP6rCu-vL7MxSh42=RCl#g~i+{iWrVJXgot;WFry zG_T4;Rz>ngXL4vVnX@ORS0TX9i(is{hAW@QL-1=f(=kS~yfHJPoMZ=lF6@02L;4U; za?N+sXa=t&;)7#a5vj(ECO4LXyI?|RKZBnp^gqPJ>}%H$lu{d@4=&qOi~jZ^~e8m%tAP-`8d4+|Pr z&WNrt?_{m+>%O4$UO`uqG#A&=`L&f3DrqCC7(p?hjSd>(xzz`&nXPy$7L0rx_2dTc z-=Ff{e50`<$T(Pfhby!&Yt>eM(@nT0CHlZK?CSj@j<3F*RU2eW2wS zt#kr0sj+u((K;|cxB$YO%?*ka(G+gu9QMPs0MoNf8xf5L0tru(YyfD*c0fd+fE*Lr znAS|E-}}9P_`P;LoS(*B`<%nzzag%)a}EUOzqH~J+9?$F8c)NWRI&wEO8F2fdv($E z>JFcorx&$;hBXRX0k`IbB0f}}(CwYwy>d!t_|rks3o%GWQ1*XbIW^vGp7}J+B+xDw z`atIOVSU$2)X2**8UY=r$ewAROI0kI^W{xgJptH=;Bt|9PoOrMZ6NQ!dQnYw2DD2p%n;=-`Y=^z9V zp3kQge@n9L-fxWK=b!p>OZjs}jZL>5Rk)gdOvzb!yTzyI`Q`blS8pQE7rO=> z^Z8OR`fzq3@q-1%)ny(Ui$c?KlLxOm%&t0jo-Z^s{-V>L2!5&|N68$59VAqn#9?PHVz7MsMu_oVr=Vhzld4Y@Xr)TauQ(;c- z%%@rw66!Ok(B}7T<&=do>W&ns{0L_J$KX2gnfWB}4j8cjxf63klQem{cx5o1q zMLen;_Hd?j9_n3+Cpr<=7wIn=e5qvG-ntR|t}j|USnX!@XDF%OAB;Y$&mU%L+?JgM zfGMNgq568gmQ%XqNNM-m#Zg@spC>36=XrCLyZU@JC05SKW5G>K43(5RT_^7gI^RuX zg-mtaAtR&V#+&{!JzmENvGJqOYrdiS|L7+6ToB*=DYUH@m=_#e-OFwB%3S}N4X2ym zUncrl^y1-BAmn0Zx8Hf`cGd0JBLIiU_OF~8&lftWB*k?e4wBAg$4xwZVo28g_>0oR z?_&%c$I1ZHHSR4&blqs-M3;!CYbU10yMABzpKZKH8GHEPWnZuW;6mKG+B0HXO8yWl z6H`Q$-=V+D6MDV6OF19@o%UXhyQCfTH9qMYx1&V)eczYL=^FvWy7tt*KtD5|FHt&X zR^n-%-8J6m6!+npFFjwL&*!N)@!|qM-qm8z3vXg%#NmFs*}hORzMe5DB~!KPI?~@A zW0b;uSyt%Z_Xqjo@xYvf=i|A!bMkrf#f(`^L?M!YZkCyMqWO{7I;1kT}f2H1A)+t{q3OMdFfN<@lyPiL@ZDka=*H@lB~7U$LcR`-+s<)3%X z*=Q86ukYAPLCFOnu>X`6Qu=#!&FC4WX2M?0ssbfO>LCCCbLP}}-8VbB2lPzPDTh&5 z+?r7|-NM!{&Eb?08ZOf#FT^mLqS3hso=i3eO+=c&=v=7GAt?orcidJW_v}dGhQKy+ zYpkoL;{Yrz0u)SSaLswuv(LXT&u{ZZ>ZF0zx$d~%?-nxtdc7Di{lkCs``_~-ik9-; zJ45o(GRl&1v-lvLPN}oF2R@B_S;!~jQg}YjRXOB49IsaVR`|4DqNnbZ=BWtLgH8|B zZ;@{OL%SjiaOy=JSNZeAA5uPmmS4;nvDySl^WRk$UAzR?T9@gZapwimQLade$$86U z3qoC%3X6B*FNSOJ#bd|?-Rk1;7pyOieYdO->eY!{ovJ-=!gJpg#q^nu`gm2f_)YXp zF7B(^=gKeg9sZf{8kRAfK0A@*Pnrg&Gly!aBXwMUbp_YLILaFGK0j)3c#TxK8XS7X zs7C<5RZMB|a`pXMYC+)#eZx@J7h`MeB&66&2)ObtdJjEAM@i~*!A4d!q`Uv`8c0#+tB6W*~vUDX`t`d;Qm_%NUhdi;`35!!k=LVkr?G5G!u33_E02&|z1vhB zmoqzjtGxn8mo+O0!Z)~u%E!{*?X3p<82($06Wkf+(Nsf2I9Yzn|{KYY@9f#C@aTxn`P%0R$UwFV0mm(ae}ll!QfZzhw`i z&n#zYy66(V?V240J>>ldf^f8)H<<*58*fjN1D>Lq=6EOwDn+IoRMSvZ8Ya7kyTt8; zWyq@|=@{&MSBi>%c2WJ@uCymnKE^DQnV2+N97FEz+8WaD{LVl4eq}@j>E?qi?rq!T z&~5+$x7+>n0IE&5M^m4q(XyTiJx2o*0G2+DY$tI-xeLv6A3Fi<~ zl@PlEL-{2^0&@}g(k3Z9n4FsyiF9=1tW3v|@o0-Tn2a$9Ig*K;+a~^0Dj!##i713P zbe2rTV*Vs!p!JS9_)^fKecLvr5!lFfIL_AVI56a5HnM1nCPOxZJT+(}=3pi5(u6S2 z#Rm-jH-4MI+J`{P$}d_6G%kj@(;6V}L&b+%GLDiK=@9qt(L0YfCKpsGv!ZL;cXJ+| zmx!usQ=>u@b%ZB}`dke-{n^mF_qoAogz`ELlzn4Yd*j}$@guo$y6)js^qt4fy2q3= z;!#)S@(q`v=8H!y#5|(LmqHgi7;#}21>Q}pY;?42q8wG43188VB~`)JdPjHT)2la_ z7vU%x6GL<;TeEX+DVT@tsURcwsUuGUhqt>JBt;oah3L*#Eo_5JC-Fs=7Vd;)!C;M3 z%5LpWhgNs??K^#sZu`k78Dm=ER<>eDwjb5{M$ucn=;teCyP%@50Ai+b%+u)gT4avN zioT|M>@s@wnSTmQZ2PuijsT?09M$RGXOgqR?^!*=vZ^Ceso{YPPAXD(NlHx0i4m*-3>wHH>e_C*Py*Q?RmXkGx$Ldl zqD?Vjk{7`dXb$wz#W;Bi8W&Bdk=zJ;)f(8lYl;^T|gR3GlkGRCRgS|i04G? z*D}>$Od?SFU<1O|J6E(ZVPc4)F9Pz?#{{0wCtqYi5kN`nmc_@1dp94A0KVvSQ5sjn z6(2ze?MwvU(A%;O;C!ehN#C|jiaSQS#!$shz?j1zezxc!$;DzdNQ?Q=3;%Oc8vxe~bVo*JQtL2R?C0}=ZM!*Azl;SCt+=yd$MHL_1nH%0 zU1Em8Ap=hj*`)v$K$DiYzSvjUQlNr+5{r_oPiKZMRI#0$^nl%*FV^Oi3;?>X@x}kuu2U$ju%QUC#9YzitDasF;t+SzDVjS?+fuUhk+0} zdfv7oNDbe)X#)A@?-+miF9MUJk@kJFLd{QGc>K$vl#&w$O@tJH2=v*}M#IEJ*FwK! zP=J{hlTW^gOL2^#MeI@&#u&tmb08v^z$u8rUadE3t^8g^J;+FJM@YP`X^=JBF+@=n z7KFrS1CJ*sa=wYnd=ip*M$kw&=T(qbNdb=Hz`#jPH#sAx1Wu1jJfJ8< z1CT;MJq`g|*oUh%oh(dK91fXu5QhBRZ1+UyuLn(Gz1PN&bC&KwRdFE(%+}3wMLAWJ zr&al54kOL#taB5mUmYK)T$LYYSjj$ zmbV&W{y+|=;r))JlS;w(2E&cNf*eF75g6({mm64KFY#gOH)h%un`7k3Gc-xfkT&G?mYJDsOi zN(sI86Bi_gfR1hSXye4Ik+I41rEt}8D;otzb}3gBN1kKMF1t>~w6$xo(y`@& z*Xt#*Qc)~tI>zJzJBBD;b1zX@NAn+xs@`*cM9OoG&gAu51ouUDX4!a5#iDZOkRmE9 zb+nJkiBUs*%OV-ezZ)b=&``H_2qKX|(u64|Q!j6#f!bb3`6ddtWbZ8{LE)V6=ueaGxw6#T(>fFZCU^tOBdxcMPr zp|vK(t?XZY22v2+@AuQFQNLp}@c>Si4(W$?eEp~YJ3jwczX_UURLc4K>xv=1J5wE3WO(;9tFSY#~ zgh_D;&CEd`;vB#Z0x7UJmyI*EsEX-B#8C{{knj)#XbhW1c!aXgW)z6%!lip}CK_YA zogj$nLVhW9U`q3q0_JEg)SB;$oDz;U(EC7b9qGUO?f>EXSQ5TIpO_(Fgo$Gg#4TgS z0QOWRq;10hq0__+K-xBB+A%9(^nlqTO1go1z-&N70nLa#-;q;7NgHO93Y`*4`Gnbk zl5WUpL!=Bs#^?-b_eQwyZ|F53h7Az~_x&?(o_NFP{O@8W)MFxrf-wS8C1M zXn-I7Ae1cTq?1RVqYqAY4*|0`LI3KvSyx3j8O0U9boY>MjwOI1qP2PLX3c{fwHk33}VEPuws^hCyXJU z(SlAQM(geoSA0?*1Gz9-NkrK4W*${mgvr<{D8SC$b+LduMK=CZNL=Ok?9!nbP;Q+)OM;PDcJHqm%)OVv1}D_?|O zLUcjkiUc{;*5okUIy$<1-=>IJViawVD#!R4>{6&)6hSmDm+2?9V-rX-TtstB_FPW>D~_N*54 zyq~)j%=&%3X|aa2dtpwD5qKWx|JRR^|JrYW6bTA}!4EJGX+olW2zHk6d9v~7>J&-E z7A{QbAZw7k_)Hu1QW!~!2q&MpQVPC&`GVW+jyZW;o=UOo-XRE_ofTdF_vE4qB1&Nt zuSo$X_AX!^HDMqG62OG4(bVv5E~eDgpdrx2oQ>aSm}1+&=n@~J6EQOh{&_r1ov8L3 zYNuqh9chdaCZ4YcVggbQlGV&-P9IX-2iW-pY36F{@-D?&>+9Di#Tbmh-s%e>IF+A3 z-|eN5AncIdY2rm4kHcHh*1iGR6v+Qu5aR7 zB8N(hXswD#S(XLO&~I%W+a^2JCOzFamU%dz<^wI(x6?5$goWxi>xBE_>!h&sXdG3+xc;q+%e|Cz{R$rB$H|@>~Gvp8DX{B7`YWw%1xa1>%rl@iUe_E!lAuWh~$BQZR1!MbzYw| z?e7&q8KNL)JjP#V@xE`IFp;A8heCs2fqnw{37kqh_j%@A`Zly5GMp4k7&IK$BR4clr)~?iTln6kiy5z<>6xJ?d>MW2s1Z%^t6kyAs5C$ zOyKka+DgXP_XnHCksQvEp??xHw^9n?Y>Y076Zy2uPh|J|E|;j7#3o)BYOu(GqjEWn z#{9Iw1aXTmaUH4_D?A@x&crDvw92NEwKkDg2eitEo0=I;DNHHoc(UOA^~C+Qp^uK) znULQ{HIapmC%hr+l%iT|fHe-|?=|~CDSQxD@iEF<;k9l4KM}JxY{Cyy$@$~`fqi52 zs*gn;4dOc&0Oo|qMa73z!0on+nr)ifJo8Y35rv!ZzP6G9Ui{MprOYL1Cr@+V`Ac)p%!qvE#TRzyvRGA14- z65;RGQCq{NNYl0%r?1tDs4C1kEV!NP2o4-4EcNf0Q(}hHe#7}K$;e(`V2T{SF~>BH zToyhZr10lvOvfbLq`bYo0g>0azApsAoWYD9d4KZAfkWH7iD!=2z^D5gCUt4D0*DeO zfZM*wsG3ob<2bnaflB2&QYtoYCc7-OQ<3SgYHcWPuC5E<#|gd77v4RZzz3CL)z zV$6Y0Z=VD;BSQfB;!Ea5lvTxM2DohYxb3`tvl$2n^Gu2X!X1|IUoa=uJo=)T9S5dp zP#}ra;+4#c1QfbqZlG0K1Ox6lKy?Hgtv%uf^)ySBz>9e}NHBu~jjcuzp9 zjn6Hi$>{zdg3uy>lz9ZIg(VS(ZL?}NS`-bxC-DswC1LCQ9a3S$HU{2Htu-e8tFiCk z`%WAFl`pvXjtK0I-rKNIZx(vTl!mO^zn^8&L1>*L(YX0wkV3g{eXdbI&55$*)x3&- zUvICA_$`zfcw6<*rx^@VJ91Hp!EAk-*5CmEboCH34142aW1N-g1e2e4)jycJT z1xn`28{mWEXw{sDa|t%{-L?(yU%w*8gl*p}@N&$_FggLFiz(v(fCG~_gn9~uTB}I* zLo#RE6sAa>T?p^&XuUJJP}M<5Cns(*G3*0~!HBkp#D(bQ(&#S)MtRg!u(u#ciY_u% zUM%%aF~hVz*oO>3#P{XV8M%7E}e!uhcX^g5dM89HeT5qCw3rPEp zqjr?Mp|#E#Yp94JqcxG@Zvpki&*y%>NjJ!ayK$n_O}Mfua%knODH$oqANulbf9jh8Tg@aqz`dcBGu} zdR56XJH#zM3r8ojC}9M2st6^bRu*@(*0660NywUffbvB-C@@+a2#@DGwrxip%T)LM z>kIpB=L3tlNQ$~oLjqyQXGv02SG?VCvL_koXpM{;S=$S@+YN_E=Z6#oV>G;36jH*g z9?XA}y$b-3=M%Tv9i0Lm&j-quxhNSEVVun#TW`p>yKpYMTzJ6k#!mdt#}jXNR#3&< zrQiau2@vui#2key$QQ}y`zJh~4~)^V?ZW*DjB3`Snnzv?5p_hozd!LSKl+T`o_POS z@!gNU!|VAKArNl+hLkvw;dNA$oN?PC9*=|1y=^xq_Blno1ODCs3l+9~7eh`yks})J z_d9<0;R`FHOU9f9G%DV11%n7}fawddaUw}1nk?!VI>1dx#0Dx z*fJyMJY8TE>-FSlmB-_Wec$ku4A(h^6j)+_Nd(M}@9rDEyg#ws-Vmu9URLYC{eHva zRhdtkqacS&V&C_I(GI-bZ+Jc)c>DYb&ljH;F-2@!!e~4~+HW^JUJntBY@Df=w&ko8|GXlw>f5%0(|=XhF|>r=P3DxqZ3}ofnWLVGwSP!EoZ#yflr@5qtAi&=M$|r z+{#_X{L`!sN)GHdW_K6wzfO~ts1iH-d>sH0KHYcJ*MY4tDSb+Fd%Tzn^rQC|{`8;y zZ}F)6Jc>;SI0VgC#J#qT890M6f-?a>fBq3SP;&1)U+ke4hJ466T+O>w}NA^prN;hC=)83{HVH}X$e#6q<~N# zKafflpU)>+Yxw-5?*szQi#HKyL9T)2@L@1yJw#EGOGckOs$f|&FRD_qK=gcgjXtoY z-I5qcF45YDb?hoN*F!l98IMPh9eu3XHX~$k`DMdwCdL@p1rZ2Bl6$+q8DTExv=*SE zH#E(aeY?FeSuG_LBI~k;j7^aFknOdhY^)+mMO>zZ32!4baa3mXfsiTjNHls5O|-Fu z+GgzcyQnIGDw|+$52~IA|HmR6fF=_nXc06-9_KGa3{4Vog!vJ0z#f#$Xbu)M_;)7J_z!&A3CV1QT|F@JyN^ zSW0T2g6>Rl3D!{%`v!GK4o)(fn9SAb9DC84pmDXLY>cEdwKIqb0|Z3gWHCfUnrOl+ z0Vy)YuzAUwg>d&VWh@gdgA8F{j)|?@&>N3dig0K)DHbUKDe#78LTBKvb_`4mlpL+; z0RmHp#zZIPIv}yaYz`ZIuv|EbwSwf`-EKRALyA;=IHeO)k2{SG3Yen$;x1L30gYhG z2V_G|n{=Rz25tL>Fqb!;%HWUV9eFFK(bSwP-bJ0EoAKc`9>F;>Hc2K}Z4ISFBlxN< z#*E|SKQ$*IhV4XX*HLbkMy^&ymj(il?uBQ=)B_gFL>aw=5HNzo`XC?$rgF`|u^B|e z&VV(x5L=A^3|h|8$BP5iHR7gEwLeLQSn61VgoHfAI8;X`DHgn5Ur{pi&q~>aqzs`+ z>=^Tj6z{^#(IEB2mw|Zw9hDnrjZaI7FIEIXJ9tc#(o$RWmKi?8wm@5T_^%vry&9G!#TB$a^Haq>x_O zz?#&8N0jgrKmws12ezG+pFA5<&%rq1Q(tRrVS`C5&{Dd zb2LN*v_7q950`-U6;wSC;s(OO=nN@unGDg5qC}@aCtDO2Tkse%$~iiTkRublQ;ImA zFH>Ig{$Z<=03MyIZ`JG73?SI|9q$iTg7fnoAA;Wr>~z4BPGV%f!DtFA?I1b3rzDn5 z%nQ~`Sau1J26U)Z&Oq$}w|&Fx&5+Pn9oPgR9DSg+da`>`b#3hvDFr-VFN8<^LUZy# zrL)8?j@O5H&&)AE0eJiLNqAO?M-Z(sVHte5KA$h#G9xk+n7p=cJ11Wg;kI*?yXAs0 zI7VVC8GXoQC&7z6!xlfPTFuao?E^AiuWI3^N=^?k*n^nh11Ss4?fDg3+AwBwIZ$jT zvE6piSnlc&zn~@yBj$`iOzh4nn@jlO(BO1sTG4j>_ij$ZAYua{AOZI z?4P2x2!Xj&QKa9Kra-1#sP-ZMJO(Lb8qb5p?@9fbp2t^gWykDOiim_Ug9UEpt>F21 zAb`iL%5fQenBs&lg#nt8q2?r9C3yy8OqA{B{6nU>6>Ffc&cbudX3of?rk6dxA-nbRX8mm&yPPSX+6ID}WWp}soXJruG7 zQ_1mZMB~MbskSl89C)d|R_k1eZQkuFT20XCc*}Tyf3S1_Z_EIPJ=DPhM^IE&Ng zI-0FQmj+8kDG@{wZFJUlv{!8$?40)qID%UN5l)SrJA#Wgex*b1v?j#Cs?Ly$Zdg z?2?h#cpNu7!jFHB$G`j&{OaHR+n`*~Mne$KsFT+nfZm3zH=ab@Zab(?^f~No(V%Z_ zbhdp5CLwg7YzY(wsPj;aN&*cCL7zuukM{@eZ+8??VfWtAo&($cCIW|IQ8USW*m$2z z3a8kc7y<%Kgv{ru@+eabn4%(nJl=)RM&_&4`>?aWivs_4DQONymS++n)Yn%^DdHve z3F>WBTa`n<3Na4OseK}XgC|1>Xrl?qj8UsOCbli(b-bkG4K@nXIGH)Rxvy%d^O%eV zeEISP+rBedJ8!HOh&+EM1VKlFm`G09FkKFHuH;Ebt^w+6l@LNg3?$O_rCMQ>J9q$+$CE8at2Dm;gBE;P6QV)Y0W)=Z)OEl7$xxDQ)O-iA8Wo2O}bVbT(jx z8qzT$Gz6g|BZ4P!uFXz1D@LdxVzxE=UW8*lXf2W=W>atg5nJBa+e!omj1(p$aQ8}> zrl`u1J*-k;`FaQu6TESvC~$Hv{Cq;-k*ekb3jc#uj3k9tmo+3o-ywp+ltnyES;*+; zG!>wt5IQ>tl)NpfGFhYi&Xe&~vUuw-w=Igd6vO&$G_=8%BUOdQb_^s0 zR<;mXo_W1D+-|#!A_JzZ*&)bshzV`*ch5ouZml5`qiUsa(Lx|}3{jCq5jRu>WFt27 z&s%7Y%C=*Qm|eIUljH*9!;%rN<^Lp}1IDqNG!aOVK^Ck`1Vo_{zzDb#BTS5X2ql6M zubi@TuIDvU@{ZQ|^4B6$ZCu8f4#k?L-tvLekc(_evD`;vh4;ojJvn_6e$haP#EB%L zQj4OVBw2T3ik*h|;#p3neg=z167yL}2S_omEV=3JW1~pu#WDj^RGkyz3V=yLNXOIh z>jFY#&S;e98syw%fJ4t;0u^q;*btYwlZkYi(b=F<3GgBeDP`m4ModXr9X3T7J;XRT zSy(&ufk?~HIK~JH2~E!V2;ev>w%g9&LtoZ1!z@v9l~tQ5ad>s$=}QnRob)~MB{^%Dtd@BlFU-joY>Ep~v(ZCbo+`$;r_~v!QiRyr zOa-@$FmKY$4uK}wg2)_|<-xtAQX~wtK9FMu1%_g4Yq&{{+i26GXVmxUcDu0x&MEhD zg1~|iqcQQFJ1~GSP_`T1zq~VqSc;%kFCNDbD+!duNC4Ux5+5Msz)8rP`XRYYJ2AAI zbDDBN<>a$-x7rmY5kD+bS&V32N36`52yrsECKqv#k9Jv>l7(MEK;T(=t5u9nBa&>g zZQCq+5K)^Pb);n_(U?P!CCP>qS^0h|65A1wLK4Jon!7O*5lK)=_CQi{KFbLun7R#- zod;e^0T%hA#)3Qo?gHyWT%*o0$LYxji}^4?#1Y4fkw^<7mpveg-7!@$8!%_U>o|~8K}0|i@U9+J zKzhq-L6IEG!4O`T)T>(5soDuKm^;Zu=(A|AJ}|8!EDLxM9m1`s;-l zgYeFnZ#SKTxFRUGw435IMBd*oO=aiuDEOh)K7<^VB*v$%rl zMYICSH8u%E{5iJFTtXQgPju|tCyY@ssUwCPQY!XLxgle8iG)iR#f&)RPoPJ|Shx3vjTI%g@+BU-OA`ix>4%I- zH^c#m6a)oYG!s?1jY$?_uHup7`4Z{_H+|f2aw5Rd7zGam%-)x?w@_bV$~ayx%bcx} zH=D$n9%Et%563c7nY_zmCiQ<(hj=kBRy1?`>Cf=|SN|5D{?30ag$GxtU7QQDI5k zHe|`bVfEnz3B9D&!|t0XW4z($Jbf@Hc~Y=Kbo9aFc90d!>b;i|6Y%-$S((UY1lxx+ zhf4)Kwcrz5m)G-wn3vb0*-}`JDT%895Yr~qmKFcjMTxH?d?G@xFSO3*a(F&|pk)&; zn_Rj`MVzkj7y1iSGOOm6Aq1=Je2$#=X3-8+?fdoT`1lW>EbsHjRz6TPWs!wDi+W(X zbZ`A!J{PLZCVY;LxBDBbwFDi}&)r(X7B?G5>GxQEBK?%(r!Oyw37`+ZRvY~gge&B1 z2-Fo;eO!hS)W<19P|5PwtgaP96NTUxP63tIcW0Sbr_k(a(taPV>@yZM+w1!GSFR+? zJ$e0^XwqBd?d^I05K)oLdy2a`R-e{W1cv{;$ujnsf1nCB%K&s70ga?k8MKWwVz3dV zj)r^=WQqZVB#3R1!V7r4p1jt!&Bnj_98KYpSy9$In@pz2{1*QCWZ|U#yUei+0e#My z%AL1;Lp`dk2{n{Gj>8#D2IC%@Re>w&bswslO2f8&7MhyTo-#RY7|8{EDjB2m*se7` z_z^hV^zH4%9-_#m9YnOsJk?t>+^k{VQV57uhu<&WYC|nXw7F5iialQ}kFQ#*jl_Mt)HU~T+WESSGc_h)lGg&x~Sy5$Texeq3kbP(c zGmRyP5;t+3+Nh^6B%>)mOHdK&|f##9RsZ)Fo)0jKAv(jxeG zc3W?(UWfS!uFT4@?2h#A$s5y7dY7Q)h=HYSRo+%WT4(I2CmR``IjZkL_xZ`P$CV&c{avCl+WtF8|N6Vj4~@WY<mh$(3)_duQ|4vf1bx8e@ZSD$e}+*6-%myI0J;M78B(Ui==hyiVnYxyv;; zYYIJ3eBiB)0{rLj>q*xVi!$OVTTlx^A6l*d_jo+)e2FoNx+^dra7=OaW@On%w)l7C z3;QTPzm&p}R4E}jgG@cIvJ1twf_-*5k6^*mKGga7eB#rmPxiaJjd6Iq=!3}BG*Sy3 zEmrh1_+V>w#qUn4p7VAJrB}yn{jSGxh>>d%Vav4hHUz%py-}nuz;&PLd)CXu7e|&a z7~^Rz>K?0AedFdOeENLJ=^Ek-hKtRiUVm=C>3Y)NW%;+4VjePuun4FCgc&X#bE~27 z`HlWupiN?fqGvUf?F`{`ThF!Bzlt2rK?ar!o3Yj#^JSv~#k^B|1}8 zJ~N{sXfYM^)=uj~?~VDVKf~+){5SaSKl>Z#LOIC7bvvou#M4QgH2Cur3=s_R8i>Z_ z^i6FNp-0x{S`!Hzh+^lG6g<{gNTHRpaL%$ex^3Bs$e&N8u-~Pio^wu6WHFD~eaN}w z_ZGjeZ1>VppQq-=_KoI$0w?oYABe4cQ3KMgFi#1y^|LFyaCCz;t$f~0=+BI3?dh6$ znN$s?d%VA%;*~{6fA9yt`@Iqx)#wcFL9Z&<^Fihbq0!4fx^CHKl8)f#<7j-Bte2^& z-|~8>9)<8-oiAYY((_1_c9V3uFqB8KOx-$0XQ{7w9}O}EPjoK3KGD6khW&OsMLhUR zD++(2l)?mgxXdphuhS$8&*iv`!@OOJWC{9!UH=~RcliRI)~XT2d7LlY8`seI=c*TJYYn&C&3>OA zUd&UYOVkTKL<0H7&7PCXfzt~%T+kRkkgGL`dMUBb5XrlaVgQ>?g{}Yo`n+Rwp4oIL`5MOw6p zVa^{+=nxUirJX&;m}95w-->1_>bh$@*aoD3`|k5uj&kPsgaW!0aO=M7#!u4-FkJ@_ zGu!xVc>Va#aQ~~n!L4~96d`;2ro;9mErg#(R@I%IQ^aA%X>w$sfh&3s@%r}NVt$u?pe43F9N`JAa^5BLV!L}zT*US{Rvu=n@8l0NUqsYeKe%5TIavyu zbLSpTU6B7`{-IwlB;O^&jq1$jDd$sF?Kiu2kL1Q-T6Ac!qkr!>4y3KHleKueuyTil zw=~w}H0SA%$~l`{+mGpNW6q;RNAc#{?dYN-5bT;zyAFVDsU*%TavI@aLFFB^ZL`krDsi z{eSv#ip{&-$auxVWY||MWX9;fYJOSC1T* z=>}2nTu1SKf9X2*>tW?vb|dx|Q9k;yv`1@o4=G*Xx(}z2!VyUh;N?@}3t%k_bQEfe zMVGs0h0vw=9%BG08Cs`};BM!aL{owJhFUcEJ=4Xfu8C$zjo=#^wZpVQ4d5ieyhui200ID=kF%U;v;%U%%_0C1*k^2-$2h z?j{@k?^5vaI#kL6H?^lTa`3wF(+a%@^>a^A{_4zA-|6%9LTwFiZ*R7CqAOnf^U68O zd33&Cd}B0!{8Rk=pZy>Bwg2)raf|^(LYicsSqm{$!UqwP@$Z7EEHwn0pYtZvioVvD z@T<>($GlE73hR2-=%3?)a+-jF-&MPut?S;FV;ffFqL90i{z(DW3 z{?i}+qu>8tFCULWX-B*+wvXMjqDa20>uyx%gKsTk$sr9sl$a3=I7DooF2vI)!-X8{ zWs)x4qh3bCEpSC|_hz6@fVbKLKbN1MqEAs+VeB?@J#M~XtCn?a-N@|?_c3wKZ4P} z6zS0g?qge2b!3jm4teq88DA`Gm#p8@^Z4}LdnAUl6IbD6oIVY7mO?|JCUx8n0;cq?IOvB3-+cDNL+IE9{kxCCrC$g*OA*A z|7Cj?+qN0!BfIftYyH%~YD1!pl2SKxS?PtRb`$y@buIe~n#3hug)F{#_DF*N^S=35 zIc?F-7LU;BzHDy9rHbGXBj=V%5HS3||B z(VD|rt27Q$kjgu`B4Fei4Sj!#NNM9SCll|)94het>>&CpZmBlEm(?JG{qQE zBqD0M{m2xN!_L*$>#&pzKb7)(L5nngw+RRdh(L}R3~Vn2_>cl!(;0@0&y{@61)djc zkootW1M11pIpu-*GgD1tgZg#JUe|pJK|s~q`?lX)nEg5Qo)r+PV$BGT_g@lUNbQ7r z80;I9gd{FQ%mDd97?@*S$W@>-AK>cep;t1cpP4eu+HA(Hm39p0?vOi;aq6CYVPPGQ z5T2p&89K6YqcQs#VdWqhDrbewFvGGdS@p^hTNj+0@v7$$k>ypb->uyMODJhhv{tc; z_Y?m)HVD)Wu^{b!8sVm#(Azv6qSw)#nLWxgu#o~0x0-DS_o2pK~5Nk$I%8XGJ!S@yOL8*wSeq>9A3e3k{cr6Mk&!OGaeq z=M_0*Wv^VOLL$~;+F#oKGkN}73<-nS7jB4G+P>cnIq?OW6&g;o;Q5hbj1w|tV?kHw z>1S`VIfKwFHWNI?)C=`o7{Sg5EinDqbkEtEp61Z0;(X3X_6UgZd_Ayj1*K%fnJiOt z{oHmM*D1D%n{JX1~{T ze|p-1FT#95o^!!*aO_de8^)~68%hopjOEmb>7~iVR9dMD;vSF7IqjRt}n86 zy#}{$eFNYp2+8eV<{v0-jTL=p1JOfws&O@IbnpPCU9~QotP-9e;$Qw_>5sev+ zEYh*^2AM~j&?2xX%ORIK;x{Z9?7hXCGvW!g@|U_-k6uaaNgS~KnW4m6|N9x;U|+tRSQ*8^ z3uNbrN5?%6^zH5Kj8^Dc7c|Oq7CrAI>PW(wm2QGSB?;m7g^x#k{Kcm4Q{P2O8AUF6 zj|_MUgh%UrGvf;keZE?7UPVP(Ecjw%&S|4pe@6S?H!gx3|IEDbg`;{nT)sH|AWhD< zWxuq1Q64LY2x3mkf*Hv3jq!|jeT!1y5nH2_$Y<>?=-$8c<@6n`LAmnhJi6==IvdA} zHx}D|B;zgV&r%`E7z1zj`w4;0#`g+#HYmM7K@I>&Z6dwD5dPhN2W?IiF=&Q_X%`{T z>ljc;_xznUv{p~+#-nQf+^S+@vbxE`(?!MrwRW2*XF>VL+S(P&$1I=bxh;y z#*htFRI83ILWY<)+ZK{78AC$r6)CR>zAEckz4&ymCYjpw*2J!76_JdNLM)Erhq3!f z1m*yRrAS|Zi`G1ymSs`;z2Ezt?=`93UwEGMrCn}HnZ0tP(||?}3^r?12-KrFzVNyJ z?+=`+7)~P!kGSXy((&E(a=&tThzL#MVy*HY_?s;OnW)kXu_u>ezMD!zN zeQ&)F3q$qmNd`DO*kCTh z8eQo6o8K5|On1rq-}RDI0NtnCwn@a)u-~f}f!}k<@szF{Dv#X4UB0bpY!IDBO8kC; z2?zWIe;&obE=B*l{NDF;0KL5Q0=FDa;|e9Tc6{@9=|27zPjeN8;l#Vui&BZ({`~eA zwnqnZ&IrnN&DqdfFS^l-$kt1a7LCQJ0=qz}os zaQOSfk6(R5$7pXf!iSERckyFfg(KfnzF1okYahRZ#>TY9UT5?DiTYr6iiG<2uvCo2RF3?f z#jjF)34^3->Ruv8TF;x%d;l|vMTi)ZH>&xv{O>~wv0+74@0=Y7&pC5}csEJ2W zyli)kiLwRVUJJ8$8GHF+ZJ`Nd2coZ<747hc*$QzEAJlaUy?PSt6?M_itlyVtoYKR6 z(UD8Bs0dYK(d?&jioZDdyDYxd>dxlrvf_WnMtS~X^kYK*tgi^pAbdkLdPeWAqXI8* zxQ>u~p{hkzbV@H39VpMnkHGXW*2C@E*?VQ@6{_~ccvI2HA?nx0wNqVOooJCQgdq7~ zXEo3VGPQMdES%63jyWa=M9ZJ+VrHmDf9nEZo{hKQxPE7Uke&1O)+kw(z+B|V(|E-5 z*{UlT?Y+*WI8sE$uOEHJCn9Xj7_-TN6vcROVBgQ)RbDvbk!$a9)g>s^7i0i9-esO` z0%9&Q-kcVJrTi%UjvkruetG^Zu<>DQ#{01*L4AyjFWE6P|8mkAdCv9zmyHdbP#{&a zDspb;M&9f)@F>j{3BHOQJgTpkzmC7OaQ2oF9tqMlrp1JM*?{JNta8gnek@1xL`RohB5S)FD z%X`$Kdr>a3@o|!i^kR_^48a1$6aU9IaC7kV=QLT7X#rDHf}M5D#g%jMRWbhTr#SxT zPw=C^`!_LTz9uv2H zH?J%oXssLpEeial=D7jh0%H=Bf=~7fDW3{~eZM*NN&0ZPdj!Xi%&x|YUMztNJiKk2 z6`@yU)73h{BQ?IDRw=kwwtB^jsy35DGW}(ud;uk)w>AX9VG?#djt_^?nX{#9T7MVN zjs^trDw1cR>mvkxMoSgQF6RZsb-KRW&CdK%*N7@zQYkV-UyyT1op+yY8YTjN=393$-a6f`B8=bpI4LF2>9aL<$ZCyigGxUO8+~&jes8P z{*Yhx2;)>7CqbpB^Uz^h#5fuKs(~(Bc? zr@p|tzrCHfMc$)s?ag^I-u0Kr6^-!L35Kv-L@xev^g@@LJH>nroM99 z$zRcPE>+E?y0Jx>T%=?z3jG=2g&8MatqU~1a!Hlj>n9=pJip!VhUg?k!Gyo5&w@$m zOVHHn5FfY2#WHJ;t*m4YRbhH@l#a5EM$wGVwoTxdfBb*r?LT0fhMW>&mc)UR;2@Zq z&D6a zHWb692&8}T+yDLd>ZM@?M1X}Pb9{qb9KG=eR6asXmf4gthqnd^A4YIlezOA<%%qT= z&BN(K@c|B*zX!S0J{k=&+9FP`(uMRf%o|4ugt!z$0PKLfa{gB(91&qG?d|B;qVf$3?R-W^3rZVova-Z|MUcxkk#}(C4w=Zis>N z8gt1ADN1A$7jMzUOZbAxE@(#wU@xg~LWVu+=L;ZDhV?wUr1^j{THQ(kiN_F$)xnta z|EKF+TW;I7^q{9-$DDKR%c)Z#(Mq5yF{z3HC5j&u2@x@fhz381_&3B#>T-%~|B?zq zLKFoF91SRFNg~9eN-4`ar}ky7cg`_JzxknEdh6p|wc&8iS$nN_%{fNDwDz>;qFlF0 z;C*i(dmS&?2EnFH$SRPrgqZ${C}M!}&no8t*5>i?<4*BUqfji3K@$T9uwR7%^U`tb z;O-Zdq%?{akrm!o;ZZr{{joE)pdsSIog%4J^?3%*XOUY**NzojB&Ok_aWo8$a*+g| z#yOgEyh9h6^W;-SMd6*zQQ2JB0UjQu7V{jYx{VOjVo zeweK{%E{z>*a2uX7L=Fg)QY10@Co}r`8|B`*MAkg0u{h$gM}yu-3MG?c#fG8bVUq} z?S#Ri_Y$FHxnY(IY0)VeXOsOx=Ugxxc@Ed>k*S}emH5a=E3mIFJyw#vFn?*XITOrwo;M7!a zszAyz%eCl&!0&d%Bo(zKYhEF8+ofGQv?aUDRMj$SvL0H(le(Jkr4*IdVK=G0_~a0! zpKIeX29Fx6IdGvtmDf?&)t`iXWaydIi>-znT*8%G`EFBAFTOt4LPXH7t7pztGT-=O zw)`neo|bCe?up_0q!et*#CV-Fv( zGO@(>F(_syrOce4&fYCSi*+%|qw&MOD1GEJ5eJgre+Wq1M&m2nRr1=9{dpy=S{n~L zZn32uu&@*4ojFd7;X_TdE&RVe&up?Zn5VRwz&lSbu zLM}9venG1A|9{4IMmaNZ?@T|X0i!b`>T{g_Rn4HYNeztky#Z>9Dx55R2hN+I`7vXH zq!lrZuO)}~-jRpH_zt>vYEdq_ zp-B|O_)S{iK+hKWEUisI$Os<@6yo|pOk=4cW@Pnbz`!XAeO1@@-cedd_^sdkyWd=u zTm9m;PMtR1sSvItYw5!5Tvx+R7u0IyvRD$Ug{+^?=TceQaog3+_iCUhki3xlQcBqO z-T27(1+KOkhB9}z2$)r%T)R_Sc+x>S{ds0lm=+?oRNp#wGm5o^?`HTJ*89mCtH}sc zf|@OvSY)KS$T&RJ&fDcemuF}

@^0LcoFZ<}_-7$K_YTr&=5cC(gJyQ+M#*Z1%m% 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�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

+tP3Z)2P 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 diff --git a/src/gui/Sudoku.py b/src/gui/Sudoku.py deleted file mode 100644 index 7b46d93fc..000000000 --- a/src/gui/Sudoku.py +++ /dev/null @@ -1,362 +0,0 @@ -#!/usr/bin/env python - -# Copyright (C) 2010 Paul Bourke -# Copyright (C) 2019 Anton Lysakov -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -import pygame -import sys -import random -import sudoku_kmdlib -import time - -class PyGameBoard(): - """Represents the game's frontend using pygame""" - - def __init__(self, engine, windowSize, gridValues, timestampValues): - pygame.init() - pygame.display.set_caption('Sudoku') - self.__engine = engine - self.__gridValues = gridValues - self.__timestampValues = timestampValues - self.__screen = pygame.display.set_mode(windowSize) - background = pygame.image.load(sys.path[0] + '/background.png').convert() - board = pygame.image.load(sys.path[0] + '/board.png') - boardX = boardY = 10 - self.__screen.blit(background, (0, 0)) - self.__screen.blit(board, (boardX, boardY)) - self.__tiles = self.__createTiles(boardX, boardY) - self.__drawUI() - self.__draw() - - def __draw(self): - """Handles events and updates display buffer""" - while True: - for event in pygame.event.get(): - if event.type == pygame.QUIT: - sys.exit() - elif event.type == pygame.MOUSEBUTTONUP and event.button == 1: - self.__handleMouse(event.pos) - elif (event.type == pygame.KEYUP): - self.__handleKeyboard(event.key) - pygame.display.flip() - - def __drawUI(self): - '''Draws the text buttons along the right panel''' - font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 28) - font.set_underline(True) - self.__titleText = font.render('Sudoku', 1, (0, 0, 0)) - self.__titleTextRect = self.__titleText.get_rect() - self.__titleTextRect.centerx = 445 - self.__titleTextRect.centery = 30 - self.__screen.blit(self.__titleText, self.__titleTextRect) - - font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 14) - self.__titleText = font.render('TonyL 2019', 1, (0, 0, 0)) - self.__titleTextRect = self.__titleText.get_rect() - self.__titleTextRect.centerx = 445 - self.__titleTextRect.centery = 55 - self.__screen.blit(self.__titleText, self.__titleTextRect) - - font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) - self.__newGameText = font.render('-New Game-', 1, (0, 0, 0)) - self.__newGameTextRect = self.__newGameText.get_rect() - self.__newGameTextRect.centerx = 495 - self.__newGameTextRect.centery = 180 - self.__screen.blit(self.__newGameText, self.__newGameTextRect) - - self.__solveText = font.render('-Check Balance-', 1, (0, 0, 0)) - self.__solveTextRect = self.__solveText.get_rect() - self.__solveTextRect.centerx = 495 - self.__solveTextRect.centery = 220 - self.__screen.blit(self.__solveText, self.__solveTextRect) - - font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) - self.__checkText = font.render('-Check Solution-', 1, (0, 0, 0)) - self.__checkTextRect = self.__checkText.get_rect() - self.__checkTextRect.centerx = 495 - self.__checkTextRect.centery = 260 - self.__screen.blit(self.__checkText, self.__checkTextRect) - - def __handleKeyboard(self, key): - """Get key pressed and update the game board""" - validKeys = {pygame.K_0: "0", pygame.K_1: "1", pygame.K_2: "2", - pygame.K_3: "3", pygame.K_4: "4", pygame.K_5: "5", - pygame.K_6: "6", pygame.K_7: "7", pygame.K_8: "8", - pygame.K_9: "9", pygame.K_BACKSPACE: "", pygame.K_DELETE: ""} - if key == pygame.K_ESCAPE: - sys.exit() - elif key in validKeys: - i = self.__currentTile.getGridLoc()[0] - j = self.__currentTile.getGridLoc()[1] - cell_num = 9 * i + (j + 1) - self.__currentTile.setFontColor(pygame.color.THECOLORS['blue']) - self.__currentTile.updateValue(validKeys[key]) - self.__gridValues[i][j] = self.__currentTile.getValue() - self.__timestampValues[cell_num] = int(round(time.time())) - - def __handleMouse(self, (x, y)): - for row in self.__tiles: - for tile in row: - if tile.getRect().collidepoint(x, y): - if not tile.isReadOnly(): - tile.highlight(pygame.color.THECOLORS['lightyellow']) - if self.__currentTile.isCorrect(): - self.__currentTile.unhighlight() - else: - self.__currentTile.highlight((255, 164, 164)) - self.__currentTile = tile - if self.__newGameTextRect.collidepoint(x, y): - self.__engine.startNewGame() - elif self.__solveTextRect.collidepoint(x, y): - self.__engine.getSolution() - elif self.__checkTextRect.collidepoint(x, y): - ret = self.__engine.checkSolution(self.__gridValues, self.__timestampValues) - - def __updateBoard(self, gridValues): - for i in range(9): - for j in range(9): - self.__tiles[i][j].updateValue(gridValues[i][j]) - - def __unhightlightBoard(self): - for i in range(9): - for j in range(9): - self.__tiles[i][j].unhighlight() - - def __createTiles(self, initX=0, initY=0): - """Set up a list of tiles corresponding to the grid, along with - each ones location coordinates on the board""" - square_size = 40 - tiles = list() - x = y = 0 - for i in range(0, 9): - row = list() - for j in range(0, 9): - if j in (0, 1, 2): - x = (j * 41) + (initX + 2) - if j in (3, 4, 5): - x = (j * 41) + (initX + 6) - if j in (6, 7, 8): - x = (j * 41) + (initX + 10) - if i in (0, 1, 2): - y = (i * 41) + (initY + 2) - if i in (3, 4, 5): - y = (i * 41) + (initY + 6) - if i in (6, 7, 8): - y = (i * 41) + (initY + 10) - tile = Tile(self.__gridValues[i][j], (x, y), (i, j), square_size) - row.append(tile) - tiles.append(row) - self.__currentTile = tiles[0][0] - return tiles - - -class Tile(): - """Represents a graphical tile on the board""" - - def __init__(self, value, coords, gridLoc, size): - xpos = coords[0] - ypos = coords[1] - self.__fontColor = pygame.color.THECOLORS["black"] - self.__readOnly = False - self.__colorSquare = pygame.Surface((size, size)).convert() - self.__colorSquare.fill(pygame.color.THECOLORS['white'], None, pygame.BLEND_RGB_ADD) - self.__colorSquareRect = self.__colorSquare.get_rect() - self.__colorSquareRect = self.__colorSquareRect.move(xpos + 1, ypos + 1) - self.__value = value - self.__gridLoc = gridLoc - self.__screen = pygame.display.get_surface() - self.__rect = pygame.Rect(xpos, ypos, size, size) - self.__isCorrect = True - if self.__value is not '-': - self.__readOnly = True - self.__draw() - - def updateValue(self, value): - self.__value = value - self.__draw() - - def isCorrect(self): - return self.__isCorrect - - def setCorrect(self, isCorrect): - self.__isCorrect = isCorrect - - def setFontColor(self, fontColor): - self.__fontColor = fontColor - - def getValue(self): - return self.__value - - def getRect(self): - return self.__rect - - def getGridLoc(self): - return self.__gridLoc - - def isReadOnly(self): - return self.__readOnly - - def highlight(self, color): - if self.__readOnly is True: - return - self.__colorSquare.fill(color) - self.__draw() - - def unhighlight(self): - self.__colorSquare.fill((255, 225, 255), None, pygame.BLEND_RGB_ADD) - self.__draw() - - def __draw(self): - value = self.__value - if self.__value == '-': - value = '' - font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) - text = font.render(str(value), 1, self.__fontColor) - textpos = text.get_rect() - textpos.centerx = self.__rect.centerx - textpos.centery = self.__rect.centery - self.__screen.blit(self.__colorSquare, self.__colorSquareRect) - self.__screen.blit(text, textpos) - - -class Sudoku: - """Represents the game's backend and logic""" - - def __init__(self, puzzleFile, rpc_connection): - self.__puzzleFile = puzzleFile - self.__rpc_connection = rpc_connection - self.startNewGame() - - def startNewGame(self): - self.__linePuzzle = self.__loadPuzzle(self.__puzzleFile) - gridValues = self.lineToGrid(self.__linePuzzle) - # prefill 0 timestamps for already known numbers - timestampValues = self.prefill_timestamps(gridValues) - board = PyGameBoard(self, (600, 400), gridValues, timestampValues) - board.setValues(gridValues) - - def __loadPuzzle(self, listName): - self.__chosen_puzzle = random.choice(listName) - puzzle = self.__rpc_connection.cclib("txidinfo", "17", '"%22' + self.__chosen_puzzle + '%22"')["unsolved"] - print "Puzzle ID: " + self.__chosen_puzzle - print "Reward amount: " + str(self.__rpc_connection.cclib("txidinfo", "17", '"%22' + self.__chosen_puzzle + '%22"')["amount"]) - ret = [] - linePuzzle = str(puzzle) - for i in linePuzzle: - ret.append(i) - return ret - - def gridToLine(self, grid): - linePuzzle = '' - for i in range(9): - for j in range(9): - linePuzzle += grid[i][j] - return linePuzzle - - def lineToGrid(self, linePuzzle): - assert (len(linePuzzle) == 81) - grid = [] - for i in xrange(0, 81, 9): - grid.append(linePuzzle[i:i + 9]) - return grid - - def getSolution(self): - balance = self.__rpc_connection.cclibaddress("17")["mybalance"] - print "Your balance: " + str(balance) - - def __solve(self, linePuzzle): - linePuzzle = ''.join(linePuzzle) - i = linePuzzle.find('-') - if i == -1: - return linePuzzle - - excluded_numbers = set() - for j in range(81): - if self.sameRow(i, j) or self.sameCol(i, j) or self.sameBlock(i, j): - excluded_numbers.add(linePuzzle[j]) - - for m in '123456789': - if m not in excluded_numbers: - funcRet = self.__solve(linePuzzle[:i] + m + linePuzzle[i + 1:]) - if funcRet is not None: - return funcRet - - def prefill_timestamps(self, grid): - timestamps = {} - for i in range(9): - for j in range(9): - if grid[i][j] != '-': - cell_num = 9 * i + ( j + 1 ) - timestamps[cell_num] = 0 - return timestamps - - def sameRow(self, i, j): - return (i / 9 == j / 9) - - def sameCol(self, i, j): - return (i - j) % 9 == 0 - - def sameBlock(self, i, j): - return (i / 27 == j / 27 and i % 9 / 3 == j % 9 / 3) - - def checkSolution(self, attemptGrid, timestampValues): - # [%22%22,%22%22,t0,t1,t2,...] - attemptLine = self.gridToLine(attemptGrid) - - #print attemptLine - #print timestampValues - timestampsline = "" - for timestamp in timestampValues.values(): - timestampsline += "," - timestampsline += str(timestamp) - arg_line = "[%22"+self.__chosen_puzzle+"%22,%22"+attemptLine+"%22"+timestampsline+"]" - print arg_line - try: - solution_info = self.__rpc_connection.cclib("solution", "17", '"' + arg_line + '"') - print solution_info - solution_txid = self.__rpc_connection.sendrawtransaction(solution_info["hex"]) - print "Solution accepted!" - print solution_txid - except Exception as e: - print(e) - print(solution_info) - solution_txid = 'error' - return solution_txid - -def main(): - while True: - # Assetchain hardcoded here - chain = 'SUDOKU' - try: - print 'Welcome to the Komodo SudokuCC' - rpc_connection = sudoku_kmdlib.def_credentials(chain) - pending_puzzles = rpc_connection.cclib("pending", "17")["pending"] - puzzle_list = [] - for puzzle in pending_puzzles: - puzzle_list.append(puzzle["txid"]) - - except Exception as e: - #print rpc_connection - print e - print 'Cant connect to SUDOKU Daemon! Please re-check if it up' - sys.exit() - else: - print 'Succesfully connected!\n' - break - newGame = Sudoku(puzzle_list, rpc_connection) - -if __name__ == '__main__': - main() diff --git a/src/gui/background.png b/src/gui/background.png deleted file mode 100644 index dc4844a0bfb7b2e023fb5b008c3ad35565277f6c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 308479 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%0DIMW)1ob#@zUwv~hUnR6cWAd1 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

@^0LcoFZ<}_-7$K_YTr&=5cC(gJyQ+M#*Z1%m% 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�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

+tP3Z)2P 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

Jq5C-aTifz(E5Kg}P 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⋙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 LNkvXXu0mjfL3yYq diff --git a/src/gui/board.png b/src/gui/board.png deleted file mode 100644 index bd77ca419999b45f31af1dc6e91aedd4b0f53a9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5678 zcmeAS@N?(olHy`uVBq!ia0y~yV5|jU4mP03i^?5=K#C>Z(btiI;o6NW{t-q%zGR7O zL`iUdT1k0gQ7VIDN`6wRf@f}GdTLN=VoGJ<$y6H#2GIkaE{-7;w~|s45`LUF;Bq^t zA;G#h!G)oLAxVn2DG(xJ$)L~>k~F@M{Q-MU|kN+74koMTY6Tl7ztmwLMJ)o!OKl^U$ z_Acp(KNlC;OMPGb(5mQ>955ZVmqSZM7DiyHsQbko(uWx3jfTYH1W+nSVtMp`@j+w+V|BDHrVGdyBIgycp zse|>{kN^MeA1_$HP(t!Qe_1)7sJYT-Q19TK`{B|jM{I$qitjzNR2=QlI_X`4raI^*LDqz40|2~awwXxSpWxo89ZJ6T-G@yGywpilHk$+ diff --git a/src/gui/sudoku_kmdlib.py b/src/gui/sudoku_kmdlib.py deleted file mode 100644 index a2b2aa239..000000000 --- a/src/gui/sudoku_kmdlib.py +++ /dev/null @@ -1,41 +0,0 @@ -import platform -import os -import re -import random -from slickrpc import Proxy - - -# define function that fetchs rpc creds from .conf -def def_credentials(chain): - rpcport =''; - operating_system = platform.system() - if operating_system == 'Darwin': - ac_dir = os.environ['HOME'] + '/Library/Application Support/Komodo' - elif operating_system == 'Linux': - ac_dir = os.environ['HOME'] + '/.komodo' - elif operating_system == 'Windows': - ac_dir = '%s/komodo/' % os.environ['APPDATA'] - if chain == 'KMD': - coin_config_file = str(ac_dir + '/komodo.conf') - else: - coin_config_file = str(ac_dir + '/' + chain + '/' + chain + '.conf') - with open(coin_config_file, 'r') as f: - for line in f: - l = line.rstrip() - if re.search('rpcuser', l): - rpcuser = l.replace('rpcuser=', '') - elif re.search('rpcpassword', l): - rpcpassword = l.replace('rpcpassword=', '') - elif re.search('rpcport', l): - rpcport = l.replace('rpcport=', '') - if len(rpcport) == 0: - if chain == 'KMD': - rpcport = 7771 - else: - print("rpcport not in conf file, exiting") - print("check "+coin_config_file) - exit(1) - - return(Proxy("http://%s:%s@127.0.0.1:%d"%(rpcuser, rpcpassword, int(rpcport)))) - - From c8fcf61bb781174e2cc5f960a304570f9ed08df4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 09:49:47 -1100 Subject: [PATCH 470/787] +comments --- src/komodo_gateway.h | 88 +++++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 30 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 58054feba..3669f29c0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1548,10 +1548,11 @@ void komodo_passport_iteration() } } -extern std::vector Mineropret; -#define PRICES_MAXCHANGE (COIN / 100) -#define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) +extern std::vector Mineropret; // opreturn data set by the data gathering code +#define PRICES_MAXCHANGE (COIN / 100) // maximum acceptable change, set at 1% +#define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR +// komodo_heightpricebits() extracts the price data in the coinbase for nHeight int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) { CBlockIndex *pindex; CBlock block; CTransaction tx; int32_t numvouts; std::vector vopret; @@ -1562,7 +1563,7 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) tx = block.vtx[0]; numvouts = (int32_t)tx.vout.size(); GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); - if ( vopret.size() == PRICES_SIZEBIT0 ) + if ( vopret.size() >= PRICES_SIZEBIT0 ) { memcpy(prevbits,&vopret[0],PRICES_SIZEBIT0); return(0); @@ -1573,34 +1574,47 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) return(-1); } -uint32_t komodo_pricenew(uint32_t price,uint32_t refprice,int64_t tolerance) +/* + komodo_pricenew() is passed in a reference price, the change tolerance and the proposed price. it needs to return a clipped price if it is too big and also set a flag if it is at or above the limit + */ +uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int64_t tolerance) { uint32_t highprice,lowprice; - highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; - lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; - //fprintf(stderr,"%.4f -> (%.4f %.4f)\n",(double)price/10000,(double)lowprice/10000,(double)highprice/10000); - if ( price > highprice ) - return(highprice); - else if ( price < lowprice ) - return(lowprice); + highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; // calc highest acceptable price + lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; // and lowest + if ( price >= highprice ) + { + *maxflagp = 1; + if ( price > highprice ) // return non-zero only if we violate the tolerance + return(highprice); + } + else if ( price <= lowprice ) + { + *maxflagp = 1; + if ( price < lowprice ) + return(lowprice); + } else return(0); } -int32_t komodo_pricecmp(uint32_t pricebitsA[4],uint32_t pricebitsB[4],int64_t tolerance) +// komodo_pricecmp() returns -1 if any of the prices are beyond the tolerance +int32_t komodo_pricecmp(int32_t *maxflagp,uint32_t pricebitsA[4],uint32_t pricebitsB[4],int64_t tolerance) { int32_t i; + *maxflagp = 0; for (i=1; i<4; i++) - if ( komodo_pricenew(pricebitsA[i],pricebitsB[i],tolerance) != 0 ) + if ( komodo_pricenew(maxflagp,pricebitsA[i],pricebitsB[i],tolerance) != 0 ) return(-1); return(0); } +// komodo_priceclamp() clamps any price that is beyond tolerance int32_t komodo_priceclamp(uint32_t pricebits[4],uint32_t refprices[4],int64_t tolerance) { - int32_t i; uint32_t newprice; + int32_t i,maxflag = 0; uint32_t newprice; for (i=1; i<4; i++) { - if ( (newprice= komodo_pricenew(pricebits[i],refprices[i],tolerance)) != 0 ) + if ( (newprice= komodo_pricenew(&maxflagp,pricebits[i],refprices[i],tolerance)) != 0 ) { fprintf(stderr,"priceclamped[%d] %u -> %u\n",i,pricebits[i],newprice); pricebits[i] = newprice; @@ -1609,16 +1623,18 @@ int32_t komodo_priceclamp(uint32_t pricebits[4],uint32_t refprices[4],int64_t to return(0); } +// komodo_mineropret() returns a valid pricedata to add to the coinbase opreturn for nHeight CScript komodo_mineropret(int32_t nHeight) { - CScript opret; uint32_t pricebits[4],prevbits[4]; - if ( Mineropret.size() == PRICES_SIZEBIT0 ) + CScript opret; uint32_t pricebits[4],prevbits[4]; int32_t maxflag; + if ( Mineropret.size() >= PRICES_SIZEBIT0 ) { if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { memcpy(pricebits,&Mineropret[0],PRICES_SIZEBIT0); - if ( komodo_pricecmp(pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { + // if the new prices are not within tolerance, update Mineropret with clipped prices komodo_priceclamp(pricebits,prevbits,PRICES_MAXCHANGE); fprintf(stderr,"update Mineropret to clamped prices\n"); memcpy(&Mineropret[0],pricebits,PRICES_SIZEBIT0); @@ -1629,13 +1645,21 @@ CScript komodo_mineropret(int32_t nHeight) return(opret); } +/* + komodo_opretvalidate() is the entire price validation! + it prints out some useful info for debugging, like the lag from current time and prev block and the prices encoded in the opreturn. + + The only way komodo_opretvalidate() doesnt return an error is if maxflag is set or it is within tolerance of both the prior block and the local data. The local data validation only happens if it is a recent block and not a block from the past as the local node is only getting the current price data. + + */ + int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; uint32_t pricebits[4],prevbits[4]; int32_t i,lag,lag2; + std::vector vopret; uint32_t pricebits[4],prevbits[4]; int32_t i,lag,lag2,maxflag=0; if ( ASSETCHAINS_CBOPRET != 0 ) { GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() == PRICES_SIZEBIT0 ) + if ( vopret.size() >= PRICES_SIZEBIT0 ) { memcpy(pricebits,&vopret[0],PRICES_SIZEBIT0); lag = (int32_t)(time(NULL) - pricebits[0]); @@ -1643,20 +1667,20 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) lag = -lag; lag2 = (int32_t)(pricebits[0] - komodo_heightstamp(nHeight-1)); fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); - if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() == PRICES_SIZEBIT0 ) - { - memcpy(prevbits,&Mineropret[0],PRICES_SIZEBIT0); - if ( komodo_pricecmp(pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) - return(-1); - } if ( nHeight > 1 ) { if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - if ( komodo_pricecmp(pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) return(-1); } else return(-1); } + if ( maxflag == 0 && lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) + { + memcpy(prevbits,&Mineropret[0],PRICES_SIZEBIT0); + if ( komodo_pricecmp(&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + return(-1); + } return(0); } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)PRICES_SIZEBIT0,(int32_t)scriptPubKey.size(),scriptPubKey[0]); return(-1); @@ -1664,6 +1688,8 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) return(0); } +// get_urljson just returns the JSON returned by the URL using issue_curl + #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) cJSON *get_urljson(char *url) @@ -1678,6 +1704,8 @@ cJSON *get_urljson(char *url) return(json); } +// parse the coindesk specific data. yes, if this changes, it will require an update. However, regardless if the format from the data source changes, then the code that extracts it must be changed. One way to mitigate this is to have a large variety of data sources so that there is only a very remote chance that all of them are not available. Certainly the data gathering needs to be made more robust, but it doesnt really affect the proof of concept for the decentralized trustless oracle. The trustlessness is achieved by having all nodes get the oracle data. + int32_t get_btcusd(uint32_t pricebits[4]) { cJSON *pjson,*bpi,*obj; char str[512]; uint64_t btcusd = 0,btcgbp = 0,btceur = 0; @@ -1709,6 +1737,7 @@ int32_t get_btcusd(uint32_t pricebits[4]) return(-1); } +// komodo_cbopretupdate() obtains the external price data and encodes it into Mineropret, which will then be used by the miner and validation void komodo_cbopretupdate() { uint32_t pricebits[4]; @@ -1716,7 +1745,7 @@ void komodo_cbopretupdate() { if ( get_btcusd(pricebits) == 0 ) { - if ( Mineropret.size() != PRICES_SIZEBIT0 ) + if ( Mineropret.size() < PRICES_SIZEBIT0 ) Mineropret.resize(PRICES_SIZEBIT0); memcpy(&Mineropret[0],pricebits,PRICES_SIZEBIT0); //int32_t i; for (i=0; i Date: Thu, 28 Mar 2019 09:51:08 -1100 Subject: [PATCH 471/787] Maxflag --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3669f29c0..b7bfa9bd5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1614,7 +1614,7 @@ int32_t komodo_priceclamp(uint32_t pricebits[4],uint32_t refprices[4],int64_t to int32_t i,maxflag = 0; uint32_t newprice; for (i=1; i<4; i++) { - if ( (newprice= komodo_pricenew(&maxflagp,pricebits[i],refprices[i],tolerance)) != 0 ) + if ( (newprice= komodo_pricenew(&maxflag,pricebits[i],refprices[i],tolerance)) != 0 ) { fprintf(stderr,"priceclamped[%d] %u -> %u\n",i,pricebits[i],newprice); pricebits[i] = newprice; From b7f15dd8b3365907d944b4cc4dfdb73cefc39bc4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 10:22:29 -1100 Subject: [PATCH 472/787] Local validation of limit move --- src/komodo_gateway.h | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b7bfa9bd5..868c639d8 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1590,7 +1590,7 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 } else if ( price <= lowprice ) { - *maxflagp = 1; + *maxflagp = -1; if ( price < lowprice ) return(lowprice); } @@ -1655,7 +1655,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; uint32_t pricebits[4],prevbits[4]; int32_t i,lag,lag2,maxflag=0; + std::vector vopret; uint32_t localbits[4],pricebits[4],prevbits[4],newprice; int32_t i,lag,lag2,maxflag=0; if ( ASSETCHAINS_CBOPRET != 0 ) { GetOpReturnData(scriptPubKey,vopret); @@ -1675,11 +1675,29 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) return(-1); } else return(-1); } - if ( maxflag == 0 && lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) + if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) { - memcpy(prevbits,&Mineropret[0],PRICES_SIZEBIT0); - if ( komodo_pricecmp(&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) - return(-1); + memcpy(localbits,&Mineropret[0],PRICES_SIZEBIT0); + if ( maxflag == 0 ) + { + if ( komodo_pricecmp(&maxflag,localbits,prevbits,PRICES_MAXCHANGE) < 0 ) + return(-1); + } + else + { + for (i=1; i<4; i++) + { + maxflag = 0; + if ( (newprice= komodo_pricenew(maxflag,pricebits[i],prevbits[i],PRICES_MAXCHANGE)) != 0 ) // proposed price is clamped + { + // make sure local price is beyond clamped + if ( maxflag > 0 && localbits[i] < pricebits[i] ) + return(-1); + else if ( maxflag < 0 && localbits[i] > pricebits[i] ) + return(-1); + } + } + } } return(0); } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)PRICES_SIZEBIT0,(int32_t)scriptPubKey.size(),scriptPubKey[0]); From c7a0e8562d134b90697722b8bba473422cd5c101 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 28 Mar 2019 10:23:24 -1100 Subject: [PATCH 473/787] & --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 868c639d8..6a9afd473 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1688,7 +1688,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) for (i=1; i<4; i++) { maxflag = 0; - if ( (newprice= komodo_pricenew(maxflag,pricebits[i],prevbits[i],PRICES_MAXCHANGE)) != 0 ) // proposed price is clamped + if ( (newprice= komodo_pricenew(&maxflag,pricebits[i],prevbits[i],PRICES_MAXCHANGE)) != 0 ) // proposed price is clamped { // make sure local price is beyond clamped if ( maxflag > 0 && localbits[i] < pricebits[i] ) From a3bbaaef9b57a74e42500e275e016bdabfd220e4 Mon Sep 17 00:00:00 2001 From: Anton Lysakov Date: Fri, 29 Mar 2019 07:44:41 +0700 Subject: [PATCH 474/787] tetris tweaks --- src/tui/lib/tuilib.py | 79 ++++++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 35 deletions(-) diff --git a/src/tui/lib/tuilib.py b/src/tui/lib/tuilib.py index da1d7658a..9a2fed639 100755 --- a/src/tui/lib/tuilib.py +++ b/src/tui/lib/tuilib.py @@ -7,6 +7,7 @@ import pickle import platform import os import subprocess +import random import signal from slickrpc import Proxy from binascii import hexlify @@ -1044,34 +1045,41 @@ def print_multiplayer_games_list(rpc_connection): def rogue_newgame_singleplayer(rpc_connection, is_game_a_rogue=True): try: - new_game_txid = rpc_connection.cclib("newgame", "17", "[1]")["txid"] - print("New singleplayer training game succesfully created. txid: " + new_game_txid) - while True: - mempool = rpc_connection.getrawmempool() - if new_game_txid in mempool: - print(colorize("Waiting for game transaction to be mined", "blue")) - time.sleep(5) - else: - print(colorize("Game transaction is mined", "green")) - break - players_list = rogue_players_list(rpc_connection) - if len(players_list["playerdata"]) > 0: - print_players_list(rpc_connection) + if is_game_a_rogue: + new_game_txid = rpc_connection.cclib("newgame", "17", "[1]")["txid"] + print("New singleplayer training game succesfully created. txid: " + new_game_txid) while True: - is_choice_needed = input("Do you want to choose a player for this game? [y/n] ") - if is_choice_needed == "y": - player_txid = input("Please input player txid: ") - newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid, player_txid)["txid"] - break - elif is_choice_needed == "n": - set_warriors_name(rpc_connection) - newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid)["txid"] - break + mempool = rpc_connection.getrawmempool() + if new_game_txid in mempool: + print(colorize("Waiting for game transaction to be mined", "blue")) + time.sleep(5) else: - print("Please choose y or n !") + print(colorize("Game transaction is mined", "green")) + break + else: + pending_games = rpc_connection.cclib("pending", "17")["pending"] + new_game_txid = random.choice(pending_games) + if is_game_a_rogue: + players_list = rogue_players_list(rpc_connection) + if len(players_list["playerdata"]) > 0: + print_players_list(rpc_connection) + while True: + is_choice_needed = input("Do you want to choose a player for this game? [y/n] ") + if is_choice_needed == "y": + player_txid = input("Please input player txid: ") + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid, player_txid)["txid"] + break + elif is_choice_needed == "n": + set_warriors_name(rpc_connection) + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid)["txid"] + break + else: + print("Please choose y or n !") + else: + print("No players available to select") + input("Press [Enter] to continue...") + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid)["txid"] else: - print("No players available to select") - input("Press [Enter] to continue...") newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid)["txid"] while True: mempool = rpc_connection.getrawmempool() @@ -1143,7 +1151,7 @@ def rogue_newgame_singleplayer(rpc_connection, is_game_a_rogue=True): print("Let's wait a little bit more") time.sleep(5) pass - if confirmations_amount < 2: + if confirmations_amount < 1: print("Last keystroke not confirmed yet! Let's wait a little") time.sleep(10) else: @@ -1163,15 +1171,16 @@ def rogue_newgame_singleplayer(rpc_connection, is_game_a_rogue=True): is_bailout_needed = input("Do you want to make bailout now [y] or wait for one more block [n]? [y/n]: ") if is_bailout_needed == "y": bailout_info = rogue_bailout(rpc_connection, new_game_txid) - while True: - try: - confirmations_amount = rpc_connection.getrawtransaction(bailout_info["txid"], 1)["confirmations"] - break - except Exception as e: - print(e) - print("Bailout not on blockchain yet. Let's wait a little bit more") - time.sleep(20) - pass + if is_game_a_rogue: + while True: + try: + confirmations_amount = rpc_connection.getrawtransaction(bailout_info["txid"], 1)["confirmations"] + break + except Exception as e: + print(e) + print("Bailout not on blockchain yet. Let's wait a little bit more") + time.sleep(20) + pass break elif is_bailout_needed == "n": game_end_height = int(rpc_connection.getinfo()["blocks"]) From aee4b6ec2b7dafc2a44de1fd31ae852d9e3c1130 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:25:55 -1100 Subject: [PATCH 475/787] alphadvantage api --- src/komodo_gateway.h | 101 +++++++++++++++++++++++++++++++++++++++++-- src/komodo_utils.h | 2 +- 2 files changed, 98 insertions(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6a9afd473..5f235ee63 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1631,13 +1631,13 @@ CScript komodo_mineropret(int32_t nHeight) { if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - memcpy(pricebits,&Mineropret[0],PRICES_SIZEBIT0); + memcpy(pricebits,Mineropret.data(),PRICES_SIZEBIT0); if ( komodo_pricecmp(&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { // if the new prices are not within tolerance, update Mineropret with clipped prices komodo_priceclamp(pricebits,prevbits,PRICES_MAXCHANGE); fprintf(stderr,"update Mineropret to clamped prices\n"); - memcpy(&Mineropret[0],pricebits,PRICES_SIZEBIT0); + memcpy(Mineropret.data(),pricebits,PRICES_SIZEBIT0); } } return(opret << OP_RETURN << Mineropret); @@ -1677,7 +1677,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) } if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) { - memcpy(localbits,&Mineropret[0],PRICES_SIZEBIT0); + memcpy(localbits,Mineropret.data(),PRICES_SIZEBIT0); if ( maxflag == 0 ) { if ( komodo_pricecmp(&maxflag,localbits,prevbits,PRICES_MAXCHANGE) < 0 ) @@ -1709,6 +1709,19 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) // get_urljson just returns the JSON returned by the URL using issue_curl #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) +char *Cryptos[] = { "KMD", "BTC", "ETH", "LTC", "BCH", "XMR", "IOTA", "DASH", "XTZ", "XEM", "ZEC", "WAVES", "DOGE", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "DGB", "STEEM", "ENJ", "STRAT", "VEO" }; + +char *ForexMajor[] = { "USD", "EUR", "JPY", "GBP", "AUD", "CHF", "CNY", "RUB" }; + +char *ForexMinor[] = { "CAD", "NZD", "MXN", "BRL", "INR", "HKD", "TRY", "ZAR", "PLN", "NOK", "SEK", "DKK", "CZK", "HUF", "ILS", "KRW", "MYR", "PHP", "RON", "SGD", "THB", "BGN", "IDR", "HRK", "UAH", "AED", "SAR" }; + +char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; + +char *Markets[] = { "DJIA", "SPX", "NDX", "VIX" }; + +char *Techstocks[] = +{ "AAPL","ADBE","ADSK","AKAM","AMD","AMZN","ATVI","BB","CDW","CRM","CSCO","CYBR","DBX","EA","FB","GDDY","GOOG","GRMN","GSAT","HPQ","IBM","INFY","INTC","INTU","JNPR","MSFT","MSI","MU","MXL","NATI","NCR","NFLX","NTAP","NVDA","ORCL","PANW","PYPL","QCOM","RHT","S","SHOP","SNAP","SPOT","SYMC","SYNA","T","TRIP","TWTR","TXN","VMW","VOD","VRSN","VZ","WDC","XRX","YELP","YNDX","ZEN" +}; cJSON *get_urljson(char *url) { @@ -1722,6 +1735,61 @@ cJSON *get_urljson(char *url) return(json); } +uint32_t get_stockprice(char *symbol) +{ + char url[512]; cJSON *json,*obj; uint32_t high,low,price = 0; + sprintf(url,"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=%s&interval=15min&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); + if ( (json= get_urljson(url)) != 0 ) + { + if ( (obj= jobj(jitem(json,"Time Series (15min)"),0)) != 0 ) + { + high = jdouble(jitem(obj,0),"2. high")*10000 + 0.000049; + low = jdouble(jitem(obj,0),"3. low")*10000 + 0.000049; + price = (high + low) / 2; + } + free_json(json); + } + return(price); +} + +uint32_t get_currencyprice(char *symbol) +{ + char url[512]; cJSON *json,*obj; uint32_t price = 0; + sprintf(url,"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); + if ( (json= get_urljson(url)) != 0 ) + { + if ( (obj= jobj(jitem(json,0),0)) != 0 ) + price = jdouble(obj,"5. Exchange Rate")*10000 + 0.000049; + free_json(json); + } + return(price); +} + +int32_t get_currencies(char *list[],int32_t n) +{ + int32_t i,errs=0; uint32_t price; + for (i=0; i Date: Fri, 29 Mar 2019 01:27:32 -1100 Subject: [PATCH 476/787] Test --- src/komodo_gateway.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 5f235ee63..d37927af2 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1741,10 +1741,10 @@ uint32_t get_stockprice(char *symbol) sprintf(url,"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=%s&interval=15min&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); if ( (json= get_urljson(url)) != 0 ) { - if ( (obj= jobj(jitem(json,"Time Series (15min)"),0)) != 0 ) + if ( (obj= jobj(jitem(json,(char *)"Time Series (15min)"),0)) != 0 ) { - high = jdouble(jitem(obj,0),"2. high")*10000 + 0.000049; - low = jdouble(jitem(obj,0),"3. low")*10000 + 0.000049; + high = jdouble(jitem(obj,0),(char *)"2. high")*10000 + 0.000049; + low = jdouble(jitem(obj,0),(char *)"3. low")*10000 + 0.000049; price = (high + low) / 2; } free_json(json); @@ -1759,7 +1759,7 @@ uint32_t get_currencyprice(char *symbol) if ( (json= get_urljson(url)) != 0 ) { if ( (obj= jobj(jitem(json,0),0)) != 0 ) - price = jdouble(obj,"5. Exchange Rate")*10000 + 0.000049; + price = jdouble(obj,(char *)"5. Exchange Rate")*10000 + 0.000049; free_json(json); } return(price); @@ -1839,6 +1839,7 @@ void komodo_cbopretupdate() // fprintf(stderr,"%02x",Mineropret[i]); //fprintf(stderr," <- set Mineropret[%d]\n",(int32_t)Mineropret.size()); } +ASSETCHAINS_CBOPRET = 0xff; if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) { get_currencies(Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); From 888282a9f9e676954184bc273f4a2899ba23ab13 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:28:40 -1100 Subject: [PATCH 477/787] const --- src/komodo_gateway.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d37927af2..18f54c961 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1709,17 +1709,17 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) // get_urljson just returns the JSON returned by the URL using issue_curl #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) -char *Cryptos[] = { "KMD", "BTC", "ETH", "LTC", "BCH", "XMR", "IOTA", "DASH", "XTZ", "XEM", "ZEC", "WAVES", "DOGE", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "DGB", "STEEM", "ENJ", "STRAT", "VEO" }; +const char *Cryptos[] = { "KMD", "BTC", "ETH", "LTC", "BCH", "XMR", "IOTA", "DASH", "XTZ", "XEM", "ZEC", "WAVES", "DOGE", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "DGB", "STEEM", "ENJ", "STRAT", "VEO" }; -char *ForexMajor[] = { "USD", "EUR", "JPY", "GBP", "AUD", "CHF", "CNY", "RUB" }; +const char *ForexMajor[] = { "USD", "EUR", "JPY", "GBP", "AUD", "CHF", "CNY", "RUB" }; -char *ForexMinor[] = { "CAD", "NZD", "MXN", "BRL", "INR", "HKD", "TRY", "ZAR", "PLN", "NOK", "SEK", "DKK", "CZK", "HUF", "ILS", "KRW", "MYR", "PHP", "RON", "SGD", "THB", "BGN", "IDR", "HRK", "UAH", "AED", "SAR" }; +const char *ForexMinor[] = { "CAD", "NZD", "MXN", "BRL", "INR", "HKD", "TRY", "ZAR", "PLN", "NOK", "SEK", "DKK", "CZK", "HUF", "ILS", "KRW", "MYR", "PHP", "RON", "SGD", "THB", "BGN", "IDR", "HRK", "UAH", "AED", "SAR" }; -char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; +const char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; -char *Markets[] = { "DJIA", "SPX", "NDX", "VIX" }; +const char *Markets[] = { "DJIA", "SPX", "NDX", "VIX" }; -char *Techstocks[] = +const char *Techstocks[] = { "AAPL","ADBE","ADSK","AKAM","AMD","AMZN","ATVI","BB","CDW","CRM","CSCO","CYBR","DBX","EA","FB","GDDY","GOOG","GRMN","GSAT","HPQ","IBM","INFY","INTC","INTU","JNPR","MSFT","MSI","MU","MXL","NATI","NCR","NFLX","NTAP","NVDA","ORCL","PANW","PYPL","QCOM","RHT","S","SHOP","SNAP","SPOT","SYMC","SYNA","T","TRIP","TWTR","TXN","VMW","VOD","VRSN","VZ","WDC","XRX","YELP","YNDX","ZEN" }; From 338c0392744e438fad6eecf91f35b562f80b18c7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:30:16 -1100 Subject: [PATCH 478/787] Obj Derek --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 18f54c961..673774be7 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1741,7 +1741,7 @@ uint32_t get_stockprice(char *symbol) sprintf(url,"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=%s&interval=15min&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); if ( (json= get_urljson(url)) != 0 ) { - if ( (obj= jobj(jitem(json,(char *)"Time Series (15min)"),0)) != 0 ) + if ( (obj= jobj(json,(char *)"Time Series (15min)")) != 0 ) { high = jdouble(jitem(obj,0),(char *)"2. high")*10000 + 0.000049; low = jdouble(jitem(obj,0),(char *)"3. low")*10000 + 0.000049; From 8cfcf57ae3de24ece5c5573a5f6135312bf82e52 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:31:12 -1100 Subject: [PATCH 479/787] Const --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 673774be7..328df66dc 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1765,7 +1765,7 @@ uint32_t get_currencyprice(char *symbol) return(price); } -int32_t get_currencies(char *list[],int32_t n) +int32_t get_currencies(const char *list[],int32_t n) { int32_t i,errs=0; uint32_t price; for (i=0; i Date: Fri, 29 Mar 2019 01:32:07 -1100 Subject: [PATCH 480/787] Const --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 328df66dc..b26d8bb9b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1735,7 +1735,7 @@ cJSON *get_urljson(char *url) return(json); } -uint32_t get_stockprice(char *symbol) +uint32_t get_stockprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t high,low,price = 0; sprintf(url,"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=%s&interval=15min&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); @@ -1752,7 +1752,7 @@ uint32_t get_stockprice(char *symbol) return(price); } -uint32_t get_currencyprice(char *symbol) +uint32_t get_currencyprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t price = 0; sprintf(url,"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); From 8d6cc79729ebd57f8c62d30df03a36db4aedb344 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:34:19 -1100 Subject: [PATCH 481/787] Test --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b26d8bb9b..265458e4d 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1728,7 +1728,7 @@ cJSON *get_urljson(char *url) char *jsonstr; cJSON *json = 0; if ( (jsonstr= issue_curl(url)) != 0 ) { - //fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); + fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); json = cJSON_Parse(jsonstr); free(jsonstr); } @@ -1839,7 +1839,7 @@ void komodo_cbopretupdate() // fprintf(stderr,"%02x",Mineropret[i]); //fprintf(stderr," <- set Mineropret[%d]\n",(int32_t)Mineropret.size()); } -ASSETCHAINS_CBOPRET = 0xff; +ASSETCHAINS_CBOPRET = 5; if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) { get_currencies(Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); From b2fade3af5114e9cf786db056d47f06ee283a55b Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:36:54 -1100 Subject: [PATCH 482/787] +print --- src/komodo_gateway.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 265458e4d..8777eb602 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1738,7 +1738,7 @@ cJSON *get_urljson(char *url) uint32_t get_stockprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t high,low,price = 0; - sprintf(url,"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=%s&interval=15min&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); + sprintf(url,"http://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=%s&interval=15min&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); if ( (json= get_urljson(url)) != 0 ) { if ( (obj= jobj(json,(char *)"Time Series (15min)")) != 0 ) @@ -1755,7 +1755,8 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_currencyprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t price = 0; - sprintf(url,"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); + sprintf(url,"http://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); + fprintf(stderr,"issue (%s)\n",url); if ( (json= get_urljson(url)) != 0 ) { if ( (obj= jobj(jitem(json,0),0)) != 0 ) From 99f2cd9bd9388f5bb288b1c591b439d6bf5a9f00 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:39:19 -1100 Subject: [PATCH 483/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 8777eb602..b0eed88b0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1755,7 +1755,7 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_currencyprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t price = 0; - sprintf(url,"http://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); + sprintf(url,"http://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,"D0185MGYVTIW0G6H");//NOTARY_PUBKEY.data()+50); fprintf(stderr,"issue (%s)\n",url); if ( (json= get_urljson(url)) != 0 ) { From 751251b1f110ccaaacfe1ff57a6e5cd8841fbfc1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:41:12 -1100 Subject: [PATCH 484/787] Test --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b0eed88b0..a0eac269b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1738,7 +1738,7 @@ cJSON *get_urljson(char *url) uint32_t get_stockprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t high,low,price = 0; - sprintf(url,"http://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=%s&interval=15min&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); + sprintf(url,"https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=%s&interval=15min&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); if ( (json= get_urljson(url)) != 0 ) { if ( (obj= jobj(json,(char *)"Time Series (15min)")) != 0 ) @@ -1755,7 +1755,7 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_currencyprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t price = 0; - sprintf(url,"http://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,"D0185MGYVTIW0G6H");//NOTARY_PUBKEY.data()+50); + sprintf(url,"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,"D0185MGYVTIW0G6H");//NOTARY_PUBKEY.data()+50); fprintf(stderr,"issue (%s)\n",url); if ( (json= get_urljson(url)) != 0 ) { From d5d5169310a7d37a390efc575916d4f48cda5982 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:45:28 -1100 Subject: [PATCH 485/787] Test --- src/komodo_bitcoind.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index e76da09ec..5834a1f5e 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -182,6 +182,7 @@ char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char * if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") == 0 ) specialcase = 1; else specialcase = 0; +specialcase = 1; if ( url[0] == 0 ) strcpy(url,"http://127.0.0.1:7876/nxt"); if ( specialcase != 0 && 0 ) @@ -247,13 +248,13 @@ try_again: numretries++; if ( specialcase != 0 ) { - printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); + fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); free(s.ptr); return(0); } else if ( numretries >= 1 ) { - //printf("Maximum number of retries exceeded!\n"); + fprintf(stderr,"Maximum number of retries exceeded!\n"); free(s.ptr); return(0); } From 10de70008ce9b644cf40a45bbf8ec81633c687c2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:51:52 -1100 Subject: [PATCH 486/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a0eac269b..5d3326efb 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1755,7 +1755,7 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_currencyprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t price = 0; - sprintf(url,"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,"D0185MGYVTIW0G6H");//NOTARY_PUBKEY.data()+50); + sprintf(url,"http://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,"D0185MGYVTIW0G6H");//NOTARY_PUBKEY.data()+50); fprintf(stderr,"issue (%s)\n",url); if ( (json= get_urljson(url)) != 0 ) { From b8e28e6e7d7f64d76dfa112cb3beab4506df15ae Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:55:57 -1100 Subject: [PATCH 487/787] Useragent --- src/komodo_bitcoind.h | 2 +- src/komodo_gateway.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 5834a1f5e..6fd5767a2 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -195,7 +195,7 @@ try_again: init_string(&s); headers = curl_slist_append(0,"Expect:"); - curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"Mozilla/4.0 (compatible; )");//"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl_handle,CURLOPT_URL, url); curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 5d3326efb..a0eac269b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1755,7 +1755,7 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_currencyprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t price = 0; - sprintf(url,"http://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,"D0185MGYVTIW0G6H");//NOTARY_PUBKEY.data()+50); + sprintf(url,"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,"D0185MGYVTIW0G6H");//NOTARY_PUBKEY.data()+50); fprintf(stderr,"issue (%s)\n",url); if ( (json= get_urljson(url)) != 0 ) { From 2420161fc83dd232c50ad0ccfb1a35df835e323b Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 01:58:57 -1100 Subject: [PATCH 488/787] Test --- src/komodo_bitcoind.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 6fd5767a2..20062c588 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -195,8 +195,9 @@ try_again: init_string(&s); headers = curl_slist_append(0,"Expect:"); - curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"Mozilla/4.0 (compatible; )");//"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); - curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + if ( headers != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl_handle,CURLOPT_URL, url); curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback From cdd520ef6095f99505f751ce38c23e86f04baf1e Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 02:03:10 -1100 Subject: [PATCH 489/787] curl_easy_setopt --- src/komodo_bitcoind.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 20062c588..a16d6d7ed 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -203,6 +203,8 @@ try_again: curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback + curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + if ( strncmp(url,"https",5) == 0 ) { curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0); From cd66407853ff4f685d2a8402b6fbc757a3dc675f Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 02:04:42 -1100 Subject: [PATCH 490/787] curl_easy_setopt --- src/komodo_bitcoind.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index a16d6d7ed..aa757f47e 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -203,7 +203,8 @@ try_again: curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback - curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + //curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, 3); if ( strncmp(url,"https",5) == 0 ) { From aa21c5314967e3d2f0b802441e671d40a24b085d Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 02:07:13 -1100 Subject: [PATCH 491/787] Test --- src/komodo_bitcoind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index aa757f47e..300a33a63 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -204,7 +204,7 @@ try_again: curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback //curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, 3); + curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, 1); if ( strncmp(url,"https",5) == 0 ) { From b793b800276084eee6b67fa2462535873992c290 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 02:08:42 -1100 Subject: [PATCH 492/787] V2 --- src/komodo_bitcoind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 300a33a63..e4c6139de 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -204,7 +204,7 @@ try_again: curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback //curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, 1); + curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, 2); if ( strncmp(url,"https",5) == 0 ) { From cd196766ca70b3695052a7c3a640aab0442ae5d2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 02:10:49 -1100 Subject: [PATCH 493/787] Test --- src/komodo_bitcoind.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index e4c6139de..b5bc7d634 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -196,15 +196,14 @@ try_again: headers = curl_slist_append(0,"Expect:"); curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); - if ( headers != 0 ) - curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); + //curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl_handle,CURLOPT_URL, url); curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback //curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); - curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, 2); + //curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, 2); if ( strncmp(url,"https",5) == 0 ) { From cd78a331b83a30d86f68ca058186ab2a60a13c2f Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 02:21:39 -1100 Subject: [PATCH 494/787] Test --- src/komodo_bitcoind.h | 3 +- src/komodo_gateway.h | 93 +++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index b5bc7d634..38f9041c1 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -182,7 +182,6 @@ char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char * if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") == 0 ) specialcase = 1; else specialcase = 0; -specialcase = 1; if ( url[0] == 0 ) strcpy(url,"http://127.0.0.1:7876/nxt"); if ( specialcase != 0 && 0 ) @@ -196,7 +195,7 @@ try_again: headers = curl_slist_append(0,"Expect:"); curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); - //curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); curl_easy_setopt(curl_handle,CURLOPT_URL, url); curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a0eac269b..7a002abbe 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1706,6 +1706,94 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) return(0); } +char *nonportable_path(char *str) +{ + int32_t i; + for (i=0; str[i]!=0; i++) + if ( str[i] == '/' ) + str[i] = '\\'; + return(str); +} + +char *portable_path(char *str) +{ +#ifdef _WIN32 + return(nonportable_path(str)); +#else +#ifdef __PNACL + /*int32_t i,n; + if ( str[0] == '/' ) + return(str); + else + { + n = (int32_t)strlen(str); + for (i=n; i>0; i--) + str[i] = str[i-1]; + str[0] = '/'; + str[n+1] = 0; + }*/ +#endif + return(str); +#endif +} + +void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) +{ + FILE *fp; + long filesize,buflen = *allocsizep; + uint8_t *buf = *bufp; + *lenp = 0; + if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) + { + fseek(fp,0,SEEK_END); + filesize = ftell(fp); + if ( filesize == 0 ) + { + fclose(fp); + *lenp = 0; + //printf("loadfile null size.(%s)\n",fname); + return(0); + } + if ( filesize > buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +void *filestr(long *allocsizep,char *_fname) +{ + long filesize = 0; char *fname,*buf = 0; void *retptr; + *allocsizep = 0; + fname = malloc(strlen(_fname)+1); + strcpy(fname,_fname); + retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); + free(fname); + return(retptr); +} + +char *send_curl(char *url,char *fname) +{ + long fsize; char curlstr[1024]; + sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); + system(curlstr); + return(filestr(&fsize,fname)); +} + // get_urljson just returns the JSON returned by the URL using issue_curl #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) @@ -1755,9 +1843,8 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_currencyprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t price = 0; - sprintf(url,"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,"D0185MGYVTIW0G6H");//NOTARY_PUBKEY.data()+50); - fprintf(stderr,"issue (%s)\n",url); - if ( (json= get_urljson(url)) != 0 ) + sprintf(url,"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); + if ( (json= send_curl(url,"curldata")) != 0 )//get_urljson(url)) != 0 ) { if ( (obj= jobj(jitem(json,0),0)) != 0 ) price = jdouble(obj,(char *)"5. Exchange Rate")*10000 + 0.000049; From 5e0ab6bf2ab852e608771f06866fb6c50337df85 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 02:24:21 -1100 Subject: [PATCH 495/787] Test --- src/komodo_gateway.h | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7a002abbe..e019a9d68 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1779,19 +1779,26 @@ void *filestr(long *allocsizep,char *_fname) { long filesize = 0; char *fname,*buf = 0; void *retptr; *allocsizep = 0; - fname = malloc(strlen(_fname)+1); + fname = (char *)malloc(strlen(_fname)+1); strcpy(fname,_fname); retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); free(fname); return(retptr); } -char *send_curl(char *url,char *fname) +cJSON *send_curl(char *url,char *fname) { - long fsize; char curlstr[1024]; + long fsize; char curlstr[1024],*jsonstr; cJSON *json=0; sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); - system(curlstr); - return(filestr(&fsize,fname)); + if ( system(curlstr) == 0 ) + { + if ( (jsonstr= filestr((void *)&fsize,fname)) != 0 ) + { + json = cJSON_Parse(jsonstr); + free(jsonstr); + } + } + return(json); } // get_urljson just returns the JSON returned by the URL using issue_curl From 894593a1c9928eb97ac84b1faf033f58001c1c9d Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 02:26:01 -1100 Subject: [PATCH 496/787] Curl data --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e019a9d68..ca924a770 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1792,7 +1792,7 @@ cJSON *send_curl(char *url,char *fname) sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); if ( system(curlstr) == 0 ) { - if ( (jsonstr= filestr((void *)&fsize,fname)) != 0 ) + if ( (jsonstr= (char *)filestr((long *)&fsize,fname)) != 0 ) { json = cJSON_Parse(jsonstr); free(jsonstr); @@ -1851,7 +1851,7 @@ uint32_t get_currencyprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t price = 0; sprintf(url,"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE&from_currency=%s&to_currency=USD&apikey=%s",symbol,NOTARY_PUBKEY.data()+50); - if ( (json= send_curl(url,"curldata")) != 0 )//get_urljson(url)) != 0 ) + if ( (json= send_curl(url,(char *)"curldata")) != 0 )//get_urljson(url)) != 0 ) { if ( (obj= jobj(jitem(json,0),0)) != 0 ) price = jdouble(obj,(char *)"5. Exchange Rate")*10000 + 0.000049; From 10f6aa68908bb106cdf1096f51107c1a381fc5b6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 02:38:08 -1100 Subject: [PATCH 497/787] get_dailyfx --- src/komodo_gateway.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index ca924a770..4dcefd217 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1847,6 +1847,17 @@ uint32_t get_stockprice(const char *symbol) return(price); } +uint32_t get_dailyfx() +{ + char url[512]; cJSON *json; uint32_t datenum=0,price = 0; + sprintf(url,"https://api.exchangeratesapi.io/latest"); + if ( (json= get_urljson(url)) != 0 ) + { + free_json(json); + } + return(datenum); +} + uint32_t get_currencyprice(const char *symbol) { char url[512]; cJSON *json,*obj; uint32_t price = 0; @@ -1934,7 +1945,7 @@ void komodo_cbopretupdate() // fprintf(stderr,"%02x",Mineropret[i]); //fprintf(stderr," <- set Mineropret[%d]\n",(int32_t)Mineropret.size()); } -ASSETCHAINS_CBOPRET = 5; + get_dailyfx(); if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) { get_currencies(Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); From 9547e334a15de98b64b50892d00830c5a4cd6681 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:07:06 -1100 Subject: [PATCH 498/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 4dcefd217..61c04c34b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1850,7 +1850,7 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_dailyfx() { char url[512]; cJSON *json; uint32_t datenum=0,price = 0; - sprintf(url,"https://api.exchangeratesapi.io/latest"); + sprintf(url,"http://api.openrates.io/latest");//http://api.exchangeratesapi.io/latest"); if ( (json= get_urljson(url)) != 0 ) { free_json(json); From 829c399f1ff33f0e55c5251b2ca8b78f6afbc1af Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:09:50 -1100 Subject: [PATCH 499/787] Test --- src/komodo_gateway.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 61c04c34b..8952e7df7 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1850,8 +1850,9 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_dailyfx() { char url[512]; cJSON *json; uint32_t datenum=0,price = 0; - sprintf(url,"http://api.openrates.io/latest");//http://api.exchangeratesapi.io/latest"); - if ( (json= get_urljson(url)) != 0 ) + sprintf(url,"http://api.openrates.io/latest"); + if ( (json= send_curl(url,(char *)"curldata")) != 0 )//get_urljson(url)) != 0 ) + //if ( (json= get_urljson(url)) != 0 ) { free_json(json); } From 76f2e11e4734778239ce7be3ba8da2b1c7af85da Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:12:09 -1100 Subject: [PATCH 500/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 8952e7df7..9b72327f5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1789,7 +1789,7 @@ void *filestr(long *allocsizep,char *_fname) cJSON *send_curl(char *url,char *fname) { long fsize; char curlstr[1024],*jsonstr; cJSON *json=0; - sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); + sprintf(curlstr,"wget \"%s\" > %s",url,fname); if ( system(curlstr) == 0 ) { if ( (jsonstr= (char *)filestr((long *)&fsize,fname)) != 0 ) From b4d66481d86b2282ec2c4a738acb561a2b26095f Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:16:04 -1100 Subject: [PATCH 501/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 9b72327f5..163e680d3 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1789,7 +1789,7 @@ void *filestr(long *allocsizep,char *_fname) cJSON *send_curl(char *url,char *fname) { long fsize; char curlstr[1024],*jsonstr; cJSON *json=0; - sprintf(curlstr,"wget \"%s\" > %s",url,fname); + sprintf(curlstr,"wget -q \"%s\" -O %s",url,fname); if ( system(curlstr) == 0 ) { if ( (jsonstr= (char *)filestr((long *)&fsize,fname)) != 0 ) From 7d9ed2dcf12ea52a9b14d2228f78966f03c3ea23 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:34:48 -1100 Subject: [PATCH 502/787] Test --- src/komodo_gateway.h | 49 +++++++++++++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 163e680d3..05ea4ba05 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1850,15 +1850,39 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_dailyfx() { char url[512]; cJSON *json; uint32_t datenum=0,price = 0; - sprintf(url,"http://api.openrates.io/latest"); - if ( (json= send_curl(url,(char *)"curldata")) != 0 )//get_urljson(url)) != 0 ) - //if ( (json= get_urljson(url)) != 0 ) + sprintf(url,"http://api.openrates.io/latest?base=USD"); + if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) { free_json(json); } return(datenum); } +uint32_t get_binanceprice(const char *symbol) +{ + char url[512]; cJSON *json; uint32_t price = 0; + sprintf(url,"https://api.binance.com/api/v1/ticker/price?symbol=%sBTC",symbol); + if ( (json= get_urljson(url)) != 0 ) + { + price = jdouble(json,(char *)"price")*SATOSHIDEN + 0.0000000049; + free_json(json); + } + return(price); +} + +int32_t get_cryptoprices(const char *list[],int32_t n) +{ + int32_t i,errs=0; uint32_t price; + for (i=0; i Date: Fri, 29 Mar 2019 03:35:52 -1100 Subject: [PATCH 503/787] Test --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 05ea4ba05..2a1b6fdeb 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1961,7 +1961,7 @@ void komodo_cbopretupdate() get_cryptoprices(Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); if ( (counter % 300) == 0 ) get_dailyfx(); - if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) + /*if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) { get_currencies(Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); } @@ -1984,7 +1984,7 @@ void komodo_cbopretupdate() if ( (ASSETCHAINS_CBOPRET & 64) != 0 ) { get_stocks(Techstocks,(int32_t)(sizeof(Techstocks)/sizeof(*Techstocks))); - } + }*/ } counter++; } From cfcfea00e44152ba21e07c0266d57d25f779abde Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:38:25 -1100 Subject: [PATCH 504/787] Test --- src/komodo_gateway.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 2a1b6fdeb..a50b79819 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1862,11 +1862,12 @@ uint32_t get_binanceprice(const char *symbol) { char url[512]; cJSON *json; uint32_t price = 0; sprintf(url,"https://api.binance.com/api/v1/ticker/price?symbol=%sBTC",symbol); - if ( (json= get_urljson(url)) != 0 ) + if ( (json= issue_curl(url,(char *)"bnbprice")) != 0 ) { price = jdouble(json,(char *)"price")*SATOSHIDEN + 0.0000000049; free_json(json); } + usleep(100000); return(price); } From de144ec34e57dc93da79481967f61a5e5c7a28e5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:39:15 -1100 Subject: [PATCH 505/787] send_curl --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a50b79819..d0ccf4f5f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1862,7 +1862,7 @@ uint32_t get_binanceprice(const char *symbol) { char url[512]; cJSON *json; uint32_t price = 0; sprintf(url,"https://api.binance.com/api/v1/ticker/price?symbol=%sBTC",symbol); - if ( (json= issue_curl(url,(char *)"bnbprice")) != 0 ) + if ( (json= send_curl(url,(char *)"bnbprice")) != 0 ) { price = jdouble(json,(char *)"price")*SATOSHIDEN + 0.0000000049; free_json(json); From 5511751b6a9eca8b8ba6a4101fb36cb27f870aac Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:41:26 -1100 Subject: [PATCH 506/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d0ccf4f5f..f95551407 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1878,7 +1878,7 @@ int32_t get_cryptoprices(const char *list[],int32_t n) { if ( (price= get_binanceprice(list[i])) == 0 ) errs++; - else fprintf(stderr,"(%s %.4f) ",list[i],(double)price/10000); + fprintf(stderr,"(%s %.8f) ",list[i],(double)price/SATOSHIDEN); } fprintf(stderr," errs.%d\n",errs); return(-errs); From 3dacf07fe3069a67f2dbe7deb7116a644a9871b5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:46:14 -1100 Subject: [PATCH 507/787] Test --- src/komodo_gateway.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index f95551407..d60bc037f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1804,11 +1804,7 @@ cJSON *send_curl(char *url,char *fname) // get_urljson just returns the JSON returned by the URL using issue_curl #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) -const char *Cryptos[] = { "KMD", "BTC", "ETH", "LTC", "BCH", "XMR", "IOTA", "DASH", "XTZ", "XEM", "ZEC", "WAVES", "DOGE", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "DGB", "STEEM", "ENJ", "STRAT", "VEO" }; - -const char *ForexMajor[] = { "USD", "EUR", "JPY", "GBP", "AUD", "CHF", "CNY", "RUB" }; - -const char *ForexMinor[] = { "CAD", "NZD", "MXN", "BRL", "INR", "HKD", "TRY", "ZAR", "PLN", "NOK", "SEK", "DKK", "CZK", "HUF", "ILS", "KRW", "MYR", "PHP", "RON", "SGD", "THB", "BGN", "IDR", "HRK", "UAH", "AED", "SAR" }; +const char *Cryptos[] = { "KMD", "ETH", "LTC", "BCHABC", "XMR", "IOTA", "DASH", "XEM", "ZEC", "WAVES", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" }; const char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; From fea8ac8afd96a77e78eb6b24173f96d1ed43bcd9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:46:40 -1100 Subject: [PATCH 508/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d60bc037f..898477215 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1819,7 +1819,7 @@ cJSON *get_urljson(char *url) char *jsonstr; cJSON *json = 0; if ( (jsonstr= issue_curl(url)) != 0 ) { - fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); + //fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); json = cJSON_Parse(jsonstr); free(jsonstr); } From de3d5538f3580cfce086ffbe6378e91693543990 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 03:53:47 -1100 Subject: [PATCH 509/787] Test --- src/komodo_gateway.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 898477215..704084221 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1845,10 +1845,22 @@ uint32_t get_stockprice(const char *symbol) uint32_t get_dailyfx() { - char url[512]; cJSON *json; uint32_t datenum=0,price = 0; + //{"base":"USD","rates":{"BGN":1.74344803,"NZD":1.471652701,"ILS":3.6329113924,"RUB":65.1997682296,"CAD":1.3430201462,"USD":1.0,"PHP":52.8641469068,"CHF":0.9970582992,"AUD":1.4129078267,"JPY":110.6792654662,"TRY":5.6523444464,"HKD":7.8499732573,"MYR":4.0824567659,"HRK":6.6232840078,"CZK":22.9862720628,"IDR":14267.4986628633,"DKK":6.6551078624,"NOK":8.6806917454,"HUF":285.131039401,"GBP":0.7626582278,"MXN":19.4183455161,"THB":31.8702085933,"ISK":122.5708682475,"ZAR":14.7033339276,"BRL":3.9750401141,"SGD":1.3573720806,"PLN":3.8286682118,"INR":69.33187734,"KRW":1139.1602781244,"RON":4.2423783206,"CNY":6.7387234801,"SEK":9.3385630237,"EUR":0.8914244963},"date":"2019-03-28"} + char url[512],*datestr; cJSON *json,*rates; int32_t i,n; uint32_t datenum=0,price = 0; sprintf(url,"http://api.openrates.io/latest?base=USD"); if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) { + if ( (rates= jobj(json,"rates")) != 0 && (n= cJSON_GetObjectSize()) > 0 ) + { + for (i=0; i Date: Fri, 29 Mar 2019 03:54:59 -1100 Subject: [PATCH 510/787] Test --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 704084221..774450c72 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1850,7 +1850,7 @@ uint32_t get_dailyfx() sprintf(url,"http://api.openrates.io/latest?base=USD"); if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) { - if ( (rates= jobj(json,"rates")) != 0 && (n= cJSON_GetObjectSize()) > 0 ) + if ( (rates= jobj(json,(char *)"rates")) != 0 && (n= cJSON_GetArraySize()) > 0 ) { for (i=0; i Date: Fri, 29 Mar 2019 03:55:54 -1100 Subject: [PATCH 511/787] Rates --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 774450c72..17e971535 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1850,7 +1850,7 @@ uint32_t get_dailyfx() sprintf(url,"http://api.openrates.io/latest?base=USD"); if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) { - if ( (rates= jobj(json,(char *)"rates")) != 0 && (n= cJSON_GetArraySize()) > 0 ) + if ( (rates= jobj(json,(char *)"rates")) != 0 && (n= cJSON_GetArraySize(rates)) > 0 ) { for (i=0; i Date: Fri, 29 Mar 2019 03:59:08 -1100 Subject: [PATCH 512/787] +print --- src/komodo_gateway.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 17e971535..d2d98d91e 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1794,6 +1794,7 @@ cJSON *send_curl(char *url,char *fname) { if ( (jsonstr= (char *)filestr((long *)&fsize,fname)) != 0 ) { + fprintf(stderr,"[%s]\n",jsonstr); json = cJSON_Parse(jsonstr); free(jsonstr); } @@ -1848,8 +1849,10 @@ uint32_t get_dailyfx() //{"base":"USD","rates":{"BGN":1.74344803,"NZD":1.471652701,"ILS":3.6329113924,"RUB":65.1997682296,"CAD":1.3430201462,"USD":1.0,"PHP":52.8641469068,"CHF":0.9970582992,"AUD":1.4129078267,"JPY":110.6792654662,"TRY":5.6523444464,"HKD":7.8499732573,"MYR":4.0824567659,"HRK":6.6232840078,"CZK":22.9862720628,"IDR":14267.4986628633,"DKK":6.6551078624,"NOK":8.6806917454,"HUF":285.131039401,"GBP":0.7626582278,"MXN":19.4183455161,"THB":31.8702085933,"ISK":122.5708682475,"ZAR":14.7033339276,"BRL":3.9750401141,"SGD":1.3573720806,"PLN":3.8286682118,"INR":69.33187734,"KRW":1139.1602781244,"RON":4.2423783206,"CNY":6.7387234801,"SEK":9.3385630237,"EUR":0.8914244963},"date":"2019-03-28"} char url[512],*datestr; cJSON *json,*rates; int32_t i,n; uint32_t datenum=0,price = 0; sprintf(url,"http://api.openrates.io/latest?base=USD"); + fprintf(stderr,"dailfx\n"); if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) { + fprintf(stderr,"(%s)\n",jprint(json,0)); if ( (rates= jobj(json,(char *)"rates")) != 0 && (n= cJSON_GetArraySize(rates)) > 0 ) { for (i=0; i Date: Fri, 29 Mar 2019 04:03:14 -1100 Subject: [PATCH 513/787] Test --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d2d98d91e..1a5a9a3ea 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1970,8 +1970,8 @@ void komodo_cbopretupdate() // fprintf(stderr,"%02x",Mineropret[i]); //fprintf(stderr," <- set Mineropret[%d]\n",(int32_t)Mineropret.size()); } - get_cryptoprices(Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); - if ( (counter % 300) == 0 ) + //get_cryptoprices(Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); + //if ( (counter % 300) == 0 ) get_dailyfx(); /*if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) { From 7051bf44a8227e9e7d6b1483108e2e7d8e07cbd0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 04:06:42 -1100 Subject: [PATCH 514/787] Https --- src/komodo_gateway.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 1a5a9a3ea..dbee7f803 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1848,8 +1848,7 @@ uint32_t get_dailyfx() { //{"base":"USD","rates":{"BGN":1.74344803,"NZD":1.471652701,"ILS":3.6329113924,"RUB":65.1997682296,"CAD":1.3430201462,"USD":1.0,"PHP":52.8641469068,"CHF":0.9970582992,"AUD":1.4129078267,"JPY":110.6792654662,"TRY":5.6523444464,"HKD":7.8499732573,"MYR":4.0824567659,"HRK":6.6232840078,"CZK":22.9862720628,"IDR":14267.4986628633,"DKK":6.6551078624,"NOK":8.6806917454,"HUF":285.131039401,"GBP":0.7626582278,"MXN":19.4183455161,"THB":31.8702085933,"ISK":122.5708682475,"ZAR":14.7033339276,"BRL":3.9750401141,"SGD":1.3573720806,"PLN":3.8286682118,"INR":69.33187734,"KRW":1139.1602781244,"RON":4.2423783206,"CNY":6.7387234801,"SEK":9.3385630237,"EUR":0.8914244963},"date":"2019-03-28"} char url[512],*datestr; cJSON *json,*rates; int32_t i,n; uint32_t datenum=0,price = 0; - sprintf(url,"http://api.openrates.io/latest?base=USD"); - fprintf(stderr,"dailfx\n"); + sprintf(url,"https://api.openrates.io/latest?base=USD"); if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) { fprintf(stderr,"(%s)\n",jprint(json,0)); From 9645cf2e19bc9888c7c3360eab607551ab80db86 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 04:14:37 -1100 Subject: [PATCH 515/787] Test --- src/komodo_gateway.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index dbee7f803..719b3894e 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1794,7 +1794,6 @@ cJSON *send_curl(char *url,char *fname) { if ( (jsonstr= (char *)filestr((long *)&fsize,fname)) != 0 ) { - fprintf(stderr,"[%s]\n",jsonstr); json = cJSON_Parse(jsonstr); free(jsonstr); } @@ -1811,6 +1810,10 @@ const char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; const char *Markets[] = { "DJIA", "SPX", "NDX", "VIX" }; +const char *Forex[] = +{ "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" +}; + const char *Techstocks[] = { "AAPL","ADBE","ADSK","AKAM","AMD","AMZN","ATVI","BB","CDW","CRM","CSCO","CYBR","DBX","EA","FB","GDDY","GOOG","GRMN","GSAT","HPQ","IBM","INFY","INTC","INTU","JNPR","MSFT","MSI","MU","MXL","NATI","NCR","NFLX","NTAP","NVDA","ORCL","PANW","PYPL","QCOM","RHT","S","SHOP","SNAP","SPOT","SYMC","SYNA","T","TRIP","TWTR","TXN","VMW","VOD","VRSN","VZ","WDC","XRX","YELP","YNDX","ZEN" }; @@ -1851,18 +1854,17 @@ uint32_t get_dailyfx() sprintf(url,"https://api.openrates.io/latest?base=USD"); if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) { - fprintf(stderr,"(%s)\n",jprint(json,0)); - if ( (rates= jobj(json,(char *)"rates")) != 0 && (n= cJSON_GetArraySize(rates)) > 0 ) + if ( (rates= jobj(json,(char *)"rates")) != 0 ) { - for (i=0; i Date: Fri, 29 Mar 2019 04:15:58 -1100 Subject: [PATCH 516/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 719b3894e..0c0e9314f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1858,7 +1858,7 @@ uint32_t get_dailyfx() { for (i=0; i Date: Fri, 29 Mar 2019 22:54:27 +0700 Subject: [PATCH 517/787] moving vertically also if block not fit game field on swap --- src/cc/games/tetris.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/games/tetris.c b/src/cc/games/tetris.c index 711170b0d..a0fd0615c 100644 --- a/src/cc/games/tetris.c +++ b/src/cc/games/tetris.c @@ -296,6 +296,7 @@ static void tg_hold(struct games_state *rs,tetris_game *obj) obj->stored.ori = ori; while (!tg_fits(obj, obj->falling)) { obj->falling.loc.row--; + obj->falling.loc.col--; } } tg_put(obj, obj->falling); From e1bfe06bf51cce1c2a4ff805eaefb04f57c2daf1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 05:41:08 -1100 Subject: [PATCH 518/787] Cleanup --- src/komodo_gateway.h | 48 +++++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 27 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 0c0e9314f..262e73ce0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1804,19 +1804,21 @@ cJSON *send_curl(char *url,char *fname) // get_urljson just returns the JSON returned by the URL using issue_curl #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) + const char *Cryptos[] = { "KMD", "ETH", "LTC", "BCHABC", "XMR", "IOTA", "DASH", "XEM", "ZEC", "WAVES", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" }; -const char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; - -const char *Markets[] = { "DJIA", "SPX", "NDX", "VIX" }; - const char *Forex[] = { "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" }; +/* const char *Techstocks[] = { "AAPL","ADBE","ADSK","AKAM","AMD","AMZN","ATVI","BB","CDW","CRM","CSCO","CYBR","DBX","EA","FB","GDDY","GOOG","GRMN","GSAT","HPQ","IBM","INFY","INTC","INTU","JNPR","MSFT","MSI","MU","MXL","NATI","NCR","NFLX","NTAP","NVDA","ORCL","PANW","PYPL","QCOM","RHT","S","SHOP","SNAP","SPOT","SYMC","SYNA","T","TRIP","TWTR","TXN","VMW","VOD","VRSN","VZ","WDC","XRX","YELP","YNDX","ZEN" }; +const char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; + +const char *Markets[] = { "DJIA", "SPX", "NDX", "VIX" }; +*/ cJSON *get_urljson(char *url) { @@ -1847,10 +1849,10 @@ uint32_t get_stockprice(const char *symbol) return(price); } -uint32_t get_dailyfx() +uint32_t get_dailyfx(uint32_t *prices) { //{"base":"USD","rates":{"BGN":1.74344803,"NZD":1.471652701,"ILS":3.6329113924,"RUB":65.1997682296,"CAD":1.3430201462,"USD":1.0,"PHP":52.8641469068,"CHF":0.9970582992,"AUD":1.4129078267,"JPY":110.6792654662,"TRY":5.6523444464,"HKD":7.8499732573,"MYR":4.0824567659,"HRK":6.6232840078,"CZK":22.9862720628,"IDR":14267.4986628633,"DKK":6.6551078624,"NOK":8.6806917454,"HUF":285.131039401,"GBP":0.7626582278,"MXN":19.4183455161,"THB":31.8702085933,"ISK":122.5708682475,"ZAR":14.7033339276,"BRL":3.9750401141,"SGD":1.3573720806,"PLN":3.8286682118,"INR":69.33187734,"KRW":1139.1602781244,"RON":4.2423783206,"CNY":6.7387234801,"SEK":9.3385630237,"EUR":0.8914244963},"date":"2019-03-28"} - char url[512],*datestr; cJSON *json,*rates; int32_t i,n; uint32_t datenum=0,price = 0; + char url[512],*datestr; cJSON *json,*rates; int32_t i; uint32_t datenum=0,price = 0; sprintf(url,"https://api.openrates.io/latest?base=USD"); if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) { @@ -1858,13 +1860,14 @@ uint32_t get_dailyfx() { for (i=0; i Date: Fri, 29 Mar 2019 06:05:13 -1100 Subject: [PATCH 519/787] Test --- src/komodo_gateway.h | 71 ++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 23 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 262e73ce0..e1a32ea05 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1565,7 +1565,7 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) { - memcpy(prevbits,&vopret[0],PRICES_SIZEBIT0); + memcpy(prevbits,vopret.data(),vopret.size()); return(0); } } @@ -1598,21 +1598,21 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 } // komodo_pricecmp() returns -1 if any of the prices are beyond the tolerance -int32_t komodo_pricecmp(int32_t *maxflagp,uint32_t pricebitsA[4],uint32_t pricebitsB[4],int64_t tolerance) +int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) { int32_t i; *maxflagp = 0; - for (i=1; i<4; i++) + for (i=1; i= PRICES_SIZEBIT0 ) { if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - memcpy(pricebits,Mineropret.data(),PRICES_SIZEBIT0); - if ( komodo_pricecmp(&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + memcpy(pricebits,Mineropret.data(),Mineropret.size()); + n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); + if ( komodo_pricecmp(n,&maxflag,pricebits,prevbits,Mineropret.size()) < 0 ) { // if the new prices are not within tolerance, update Mineropret with clipped prices - komodo_priceclamp(pricebits,prevbits,PRICES_MAXCHANGE); + komodo_priceclamp(n,pricebits,prevbits,PRICES_MAXCHANGE); fprintf(stderr,"update Mineropret to clamped prices\n"); - memcpy(Mineropret.data(),pricebits,PRICES_SIZEBIT0); + memcpy(Mineropret.data(),pricebits,Mineropret.size()); } } + int32_t i; + for (i=0; i vopret; uint32_t localbits[4],pricebits[4],prevbits[4],newprice; int32_t i,lag,lag2,maxflag=0; + std::vector vopret; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; if ( ASSETCHAINS_CBOPRET != 0 ) { GetOpReturnData(scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) { - memcpy(pricebits,&vopret[0],PRICES_SIZEBIT0); + n = (int32_t)(vopret.size() / sizeof(uint32_t)); + memcpy(pricebits,vopret.data(),Mineropret.size()); lag = (int32_t)(time(NULL) - pricebits[0]); if ( lag < 0 ) lag = -lag; @@ -1671,21 +1677,21 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - if ( komodo_pricecmp(&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) return(-1); } else return(-1); } if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) { - memcpy(localbits,Mineropret.data(),PRICES_SIZEBIT0); + memcpy(localbits,Mineropret.data(),Mineropret.size()); if ( maxflag == 0 ) { - if ( komodo_pricecmp(&maxflag,localbits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(n,&maxflag,localbits,prevbits,PRICES_MAXCHANGE) < 0 ) return(-1); } else { - for (i=1; i<4; i++) + for (i=1; i Date: Fri, 29 Mar 2019 06:11:08 -1100 Subject: [PATCH 520/787] Test --- src/komodo_utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_utils.h b/src/komodo_utils.h index a91dae1e8..44b1192a4 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -2067,7 +2067,7 @@ void komodo_args(char *argv0) if ( ASSETCHAINS_CBOPRET != 0 ) { extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_CBOPRET),(void *)&ASSETCHAINS_CBOPRET); - komodo_cbopretupdate(); // will set Mineropret + //komodo_cbopretupdate(); // will set Mineropret fprintf(stderr,"This blockchain uses data produced from CoinDesk Bitcoin Price Index\n"); } } From 2b8eac7ca17485a2581fbefdf90093fb0dccdbc1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 06:14:33 -1100 Subject: [PATCH 521/787] Test --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e1a32ea05..271362c4b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1985,9 +1985,9 @@ void komodo_cbopretupdate() if ( get_btcusd(pricebits) == 0 ) { memcpy(Mineropret.data(),pricebits,PRICES_SIZEBIT0); - //int32_t i; for (i=0; i Date: Sat, 30 Mar 2019 00:15:48 +0700 Subject: [PATCH 522/787] fixing fit from both ends --- src/cc/games/tetris.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cc/games/tetris.c b/src/cc/games/tetris.c index a0fd0615c..8e7642837 100644 --- a/src/cc/games/tetris.c +++ b/src/cc/games/tetris.c @@ -296,7 +296,14 @@ static void tg_hold(struct games_state *rs,tetris_game *obj) obj->stored.ori = ori; while (!tg_fits(obj, obj->falling)) { obj->falling.loc.row--; + if (tg_fits(obj, obj->falling)) { + break; + } obj->falling.loc.col--; + if (tg_fits(obj, obj->falling)) { + break; + } + obj->falling.loc.col += 2; } } tg_put(obj, obj->falling); From b7cf1a57e9ae788fad86cc1e31e736072895e6fb Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 06:17:03 -1100 Subject: [PATCH 523/787] 333 --- src/komodo_gateway.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 271362c4b..c71f7c4f2 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1974,6 +1974,8 @@ void komodo_cbopretupdate() int32_t size; if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) { + if ( komodo_nextheight() > 333 ) + ASSETCHAINS_CBOPRET = 7; size = PRICES_SIZEBIT0; if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) size += sizeof(forexprices); From 3b4a6191f9759df2bd440f257f9c3636830e0f02 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 06:19:33 -1100 Subject: [PATCH 524/787] Test --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index c71f7c4f2..0612caa80 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1987,9 +1987,6 @@ void komodo_cbopretupdate() if ( get_btcusd(pricebits) == 0 ) { memcpy(Mineropret.data(),pricebits,PRICES_SIZEBIT0); - int32_t i; for (i=0; i Date: Fri, 29 Mar 2019 06:38:55 -1100 Subject: [PATCH 525/787] Test --- src/komodo_gateway.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 0612caa80..fbdda445c 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1603,8 +1603,13 @@ int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_ int32_t i; *maxflagp = 0; for (i=1; i %u out of tolerance maxflag.%d\n",i,pricebitsB[i],pricebitsA[i],*maxflagp); return(-1); + } + } return(0); } @@ -1697,6 +1702,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( (newprice= komodo_pricenew(&maxflag,pricebits[i],prevbits[i],PRICES_MAXCHANGE)) != 0 ) // proposed price is clamped { // make sure local price is beyond clamped + fprintf(stderr,"maxflag.%d i.%d localbits.%u vs pricebits.%u\n",maxflag,i,localbits[i],pricebits[i]); if ( maxflag > 0 && localbits[i] < pricebits[i] ) return(-1); else if ( maxflag < 0 && localbits[i] > pricebits[i] ) @@ -2003,9 +2009,9 @@ void komodo_cbopretupdate() memcpy(&Mineropret.data()[size],cryptoprices,sizeof(cryptoprices)); size += sizeof(cryptoprices); } - int32_t i; for (i=0; i Date: Fri, 29 Mar 2019 07:01:56 -1100 Subject: [PATCH 526/787] +prints --- src/komodo_gateway.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index fbdda445c..94d5928a9 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1584,12 +1584,14 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; // and lowest if ( price >= highprice ) { + fprintf(stderr,"high %u vs h%u l%u\n",price,highprice,lowprice); *maxflagp = 1; if ( price > highprice ) // return non-zero only if we violate the tolerance return(highprice); } else if ( price <= lowprice ) { + fprintf(stderr,"low %u vs h%u l%u\n",price,highprice,lowprice); *maxflagp = -1; if ( price < lowprice ) return(lowprice); @@ -1606,7 +1608,7 @@ int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_ { if ( komodo_pricenew(maxflagp,pricebitsA[i],pricebitsB[i],tolerance) != 0 ) { - fprintf(stderr,"i.%d %u -> %u out of tolerance maxflag.%d\n",i,pricebitsB[i],pricebitsA[i],*maxflagp); + fprintf(stderr,"i.%d/%d %u -> %u out of tolerance maxflag.%d\n",i,n,pricebitsB[i],pricebitsA[i],*maxflagp); return(-1); } } @@ -1621,7 +1623,7 @@ int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int6 { if ( (newprice= komodo_pricenew(&maxflag,pricebits[i],refprices[i],tolerance)) != 0 ) { - fprintf(stderr,"priceclamped[%d] %u -> %u\n",i,pricebits[i],newprice); + fprintf(stderr,"priceclamped[%d of %d] %u -> %u\n",i,n,pricebits[i],newprice); pricebits[i] = newprice; } } From 3af96cfe61f9492d23ea6bf2219ff6e93868f181 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 07:03:48 -1100 Subject: [PATCH 527/787] Test --- src/komodo_gateway.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 94d5928a9..b60399329 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1602,13 +1602,13 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 // komodo_pricecmp() returns -1 if any of the prices are beyond the tolerance int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) { - int32_t i; + int32_t i; uint32_t newprice; *maxflagp = 0; for (i=1; i %u out of tolerance maxflag.%d\n",i,n,pricebitsB[i],pricebitsA[i],*maxflagp); + fprintf(stderr,"i.%d/%d %u vs %u -> newprice.%u out of tolerance maxflag.%d\n",i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); return(-1); } } @@ -1623,7 +1623,7 @@ int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int6 { if ( (newprice= komodo_pricenew(&maxflag,pricebits[i],refprices[i],tolerance)) != 0 ) { - fprintf(stderr,"priceclamped[%d of %d] %u -> %u\n",i,n,pricebits[i],newprice); + fprintf(stderr,"priceclamped[%d of %d] %u vs %u -> %u\n",i,n,refprices[i],pricebits[i],newprice); pricebits[i] = newprice; } } From ec1c7ffcc7dd2535f9772ca7e5ba8862c02a1bc9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 07:10:46 -1100 Subject: [PATCH 528/787] Int64 --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b60399329..7a12f21bd 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1579,19 +1579,19 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) */ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int64_t tolerance) { - uint32_t highprice,lowprice; + uint64_t highprice,lowprice; highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; // calc highest acceptable price lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; // and lowest if ( price >= highprice ) { - fprintf(stderr,"high %u vs h%u l%u\n",price,highprice,lowprice); + fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); *maxflagp = 1; if ( price > highprice ) // return non-zero only if we violate the tolerance return(highprice); } else if ( price <= lowprice ) { - fprintf(stderr,"low %u vs h%u l%u\n",price,highprice,lowprice); + fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); *maxflagp = -1; if ( price < lowprice ) return(lowprice); From c12368a75c7fefe97453cfa2169ac02d88cf7a8f Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 07:18:47 -1100 Subject: [PATCH 529/787] Print --- src/komodo_gateway.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7a12f21bd..adf71c070 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1582,6 +1582,8 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 uint64_t highprice,lowprice; highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; // calc highest acceptable price lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; // and lowest + fprintf(stderr,"ref.%u * %llu -> %llu -> highprice %llu\n",refprice,(long long)(COIN+tolerance),(long long)((uint64_t)refprice * (COIN + tolerance)),(long long)highprice); + fprintf(stderr,"ref.%u * %llu -> %llu -> lowprice %llu\n",refprice,(long long)(COIN-tolerance),(long long)((uint64_t)refprice * (COIN - tolerance)),(long long)lowprice); if ( price >= highprice ) { fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); From 5f9d3a95528174ef97161511dbda0ab407d6f27e Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 07:27:33 -1100 Subject: [PATCH 530/787] Test --- src/komodo_gateway.h | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index adf71c070..6ef0739e9 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1635,14 +1635,24 @@ int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int6 // komodo_mineropret() returns a valid pricedata to add to the coinbase opreturn for nHeight CScript komodo_mineropret(int32_t nHeight) { - CScript opret; uint32_t pricebits[8192],prevbits[8192]; int32_t maxflag,n; + CScript opret; uint32_t pricebits[8192],prevbits[8192]; int32_t maxflag,n,numzero=0; if ( Mineropret.size() >= PRICES_SIZEBIT0 ) { + n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); + numzero = 1; + while ( numzero > 0 ) + { + memcpy(pricebits,Mineropret.data(),Mineropret.size()); + for (i=numzero=0; i Date: Fri, 29 Mar 2019 07:28:47 -1100 Subject: [PATCH 531/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6ef0739e9..8400289ef 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1635,7 +1635,7 @@ int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int6 // komodo_mineropret() returns a valid pricedata to add to the coinbase opreturn for nHeight CScript komodo_mineropret(int32_t nHeight) { - CScript opret; uint32_t pricebits[8192],prevbits[8192]; int32_t maxflag,n,numzero=0; + CScript opret; uint32_t pricebits[8192],prevbits[8192]; int32_t maxflag,i,n,numzero=0; if ( Mineropret.size() >= PRICES_SIZEBIT0 ) { n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); From 92f0ec2616b71cf5fbbb4d5070e83e0ab961287c Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 07:34:46 -1100 Subject: [PATCH 532/787] Make sure highprice and lowprice != refprice --- src/komodo_gateway.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 8400289ef..cb0bc761b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1580,20 +1580,24 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int64_t tolerance) { uint64_t highprice,lowprice; + if ( refprice < 2 ) + return(0); highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; // calc highest acceptable price lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; // and lowest - fprintf(stderr,"ref.%u * %llu -> %llu -> highprice %llu\n",refprice,(long long)(COIN+tolerance),(long long)((uint64_t)refprice * (COIN + tolerance)),(long long)highprice); - fprintf(stderr,"ref.%u * %llu -> %llu -> lowprice %llu\n",refprice,(long long)(COIN-tolerance),(long long)((uint64_t)refprice * (COIN - tolerance)),(long long)lowprice); + if ( highprice == refprice ) + highprice++; + if ( lowprice == refprice ) + lowprice--; if ( price >= highprice ) { - fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + //fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); *maxflagp = 1; if ( price > highprice ) // return non-zero only if we violate the tolerance return(highprice); } else if ( price <= lowprice ) { - fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + //fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); *maxflagp = -1; if ( price < lowprice ) return(lowprice); From e955d862fb9e652e83865549a9f9732683a57150 Mon Sep 17 00:00:00 2001 From: "Anton \"TonyL\" Lysakov" Date: Sat, 30 Mar 2019 01:45:33 +0700 Subject: [PATCH 533/787] looking for keystrokes.log in the right place --- src/tui/lib/tuilib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tui/lib/tuilib.py b/src/tui/lib/tuilib.py index 9a2fed639..b46030af8 100755 --- a/src/tui/lib/tuilib.py +++ b/src/tui/lib/tuilib.py @@ -1952,10 +1952,10 @@ def find_game_keystrokes_in_log(gametxid): operating_system = platform.system() if operating_system == 'Win64' or operating_system == 'Windows': - p1 = subprocess.Popen(["type", "keystrokes.log"], stdout=subprocess.PIPE, shell=True) + p1 = subprocess.Popen(["type", "../keystrokes.log"], stdout=subprocess.PIPE, shell=True) p2 = subprocess.Popen(["findstr", gametxid], stdin=p1.stdout, stdout=subprocess.PIPE, shell=True) else: - p1 = subprocess.Popen(["cat", "keystrokes.log"], stdout=subprocess.PIPE) + p1 = subprocess.Popen(["cat", "../keystrokes.log"], stdout=subprocess.PIPE) p2 = subprocess.Popen(["grep", gametxid], stdin=p1.stdout, stdout=subprocess.PIPE) p1.stdout.close() output = p2.communicate()[0] From 6c9eb0e0f9c4836159d337bf0ff2f7bbdef16e28 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 07:46:06 -1100 Subject: [PATCH 534/787] Dailyfx every 5 hours -> on average -> 2 hours ave delay --- src/komodo_gateway.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index cb0bc761b..94f85531a 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1993,7 +1993,7 @@ int32_t get_btcusd(uint32_t pricebits[4]) void komodo_cbopretupdate() { - static uint32_t counter; + static uint32_t lasttime; static uint32_t pricebits[4],cryptoprices[sizeof(Cryptos)/sizeof(*Cryptos)],forexprices[sizeof(Forex)/sizeof(*Forex)]; int32_t size; if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) @@ -2014,10 +2014,11 @@ void komodo_cbopretupdate() } if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) { - if ( (counter % 300) == 0 || forexprices[0] == 0 ) + if ( time(NULL) > lasttime+3600*5 || forexprices[0] == 0 ) { get_dailyfx(forexprices); memcpy(&Mineropret.data()[size],forexprices,sizeof(forexprices)); + lasttime = (uint32_t)time(NULL); } size += sizeof(forexprices); } From 6053dc843dbd4e9cd110fa8ac1201f43a63efee7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 07:48:51 -1100 Subject: [PATCH 535/787] -counter --- src/komodo_gateway.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 94f85531a..43c9981f2 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2045,5 +2045,4 @@ void komodo_cbopretupdate() get_stocks(Techstocks,(int32_t)(sizeof(Techstocks)/sizeof(*Techstocks))); }*/ } - counter++; } From 1976dcbfdb128ba924e437d3ef7a75fe3344e349 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 07:55:23 -1100 Subject: [PATCH 536/787] test --- src/komodo_gateway.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 43c9981f2..16a64f86b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1701,7 +1701,10 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { if ( komodo_pricecmp(n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + { + fprintf(stderr,"vs prev maxflag.%d cmp error\n",maxflag); return(-1); + } } else return(-1); } if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) @@ -1710,7 +1713,10 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( maxflag == 0 ) { if ( komodo_pricecmp(n,&maxflag,localbits,prevbits,PRICES_MAXCHANGE) < 0 ) + { + fprintf(stderr,"maxflag.0 cmp error\n"); return(-1); + } } else { From e24193c7a8774b9a5d911d89660499554c4b8c83 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 07:59:47 -1100 Subject: [PATCH 537/787] Return 0 in all fall through cases for pricecmp --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 16a64f86b..3614ec4cc 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1602,7 +1602,7 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 if ( price < lowprice ) return(lowprice); } - else return(0); + return(0); } // komodo_pricecmp() returns -1 if any of the prices are beyond the tolerance From eb4c6419fd0da61ff3887fc779d11f48bd77dfc2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 08:24:28 -1100 Subject: [PATCH 538/787] Edge case of no local prices yet --- src/komodo_gateway.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3614ec4cc..bf07218eb 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1609,10 +1609,10 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) { int32_t i; uint32_t newprice; - *maxflagp = 0; for (i=1; i newprice.%u out of tolerance maxflag.%d\n",i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); return(-1); @@ -1624,9 +1624,10 @@ int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_ // komodo_priceclamp() clamps any price that is beyond tolerance int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int64_t tolerance) { - int32_t i,maxflag = 0; uint32_t newprice; + int32_t i,maxflag; uint32_t newprice; for (i=1; i %u\n",i,n,refprices[i],pricebits[i],newprice); From 78dd8ae93776ee9fcf5735977c282a2f7d39020b Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 08:27:16 -1100 Subject: [PATCH 539/787] Only skip check if localbits is 0 --- src/komodo_gateway.h | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index bf07218eb..26fbeda02 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1612,7 +1612,7 @@ int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_ for (i=1; i newprice.%u out of tolerance maxflag.%d\n",i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); return(-1); @@ -1711,27 +1711,33 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) { memcpy(localbits,Mineropret.data(),Mineropret.size()); - if ( maxflag == 0 ) + for (i=0; i 0 && localbits[i] < pricebits[i] ) - return(-1); - else if ( maxflag < 0 && localbits[i] > pricebits[i] ) - return(-1); + fprintf(stderr,"maxflag.0 cmp error\n"); + return(-1); + } + } + else + { + for (i=1; i 0 && localbits[i] < pricebits[i] ) + return(-1); + else if ( maxflag < 0 && localbits[i] > pricebits[i] ) + return(-1); + } } } } From 78564c9b3b3c02c8724239fa7c55be217fefbb0e Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 08:28:21 -1100 Subject: [PATCH 540/787] Handle partial 0 --- src/komodo_gateway.h | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 26fbeda02..63c210d6a 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1713,31 +1713,28 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) memcpy(localbits,Mineropret.data(),Mineropret.size()); for (i=0; i 0 && localbits[i] < pricebits[i] ) - return(-1); - else if ( maxflag < 0 && localbits[i] > pricebits[i] ) - return(-1); - } + // make sure local price is beyond clamped + fprintf(stderr,"maxflag.%d i.%d localbits.%u vs pricebits.%u\n",maxflag,i,localbits[i],pricebits[i]); + if ( maxflag > 0 && localbits[i] < pricebits[i] ) + return(-1); + else if ( maxflag < 0 && localbits[i] > pricebits[i] ) + return(-1); } } } From 67352a31b59499e452c8564af14eda31850e2ac5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 08:35:02 -1100 Subject: [PATCH 541/787] Fix --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 63c210d6a..1306696f0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1609,9 +1609,9 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) { int32_t i; uint32_t newprice; + *maxflagp = 0; for (i=1; i newprice.%u out of tolerance maxflag.%d\n",i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); From 725ac7114718704719a807465add337d49919261 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 08:52:12 -1100 Subject: [PATCH 542/787] +print --- src/komodo_gateway.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 1306696f0..225524eaf 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1590,14 +1590,14 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 lowprice--; if ( price >= highprice ) { - //fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); *maxflagp = 1; if ( price > highprice ) // return non-zero only if we violate the tolerance return(highprice); } else if ( price <= lowprice ) { - //fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); *maxflagp = -1; if ( price < lowprice ) return(lowprice); @@ -1606,7 +1606,7 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 } // komodo_pricecmp() returns -1 if any of the prices are beyond the tolerance -int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) +int32_t komodo_pricecmp(int32_t nHeight,int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) { int32_t i; uint32_t newprice; *maxflagp = 0; @@ -1614,7 +1614,7 @@ int32_t komodo_pricecmp(int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_ { if ( (newprice= komodo_pricenew(maxflagp,pricebitsA[i],pricebitsB[i],tolerance)) != 0 ) { - fprintf(stderr,"i.%d/%d %u vs %u -> newprice.%u out of tolerance maxflag.%d\n",i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); + fprintf(stderr,"ht.%d i.%d/%d %u vs %u -> newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); return(-1); } } @@ -1657,7 +1657,7 @@ CScript komodo_mineropret(int32_t nHeight) if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { memcpy(pricebits,Mineropret.data(),Mineropret.size()); - if ( komodo_pricecmp(n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(0,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { // if the new prices are not within tolerance, update Mineropret with clipped prices komodo_priceclamp(n,pricebits,prevbits,PRICES_MAXCHANGE); @@ -1701,7 +1701,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - if ( komodo_pricecmp(n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { fprintf(stderr,"vs prev maxflag.%d cmp error\n",maxflag); return(-1); @@ -1716,7 +1716,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) localbits[i] = prevbits[i]; if ( maxflag == 0 ) { - if ( komodo_pricecmp(n,&maxflag,localbits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(nHeight,n,&maxflag,localbits,prevbits,PRICES_MAXCHANGE) < 0 ) { fprintf(stderr,"maxflag.0 cmp error\n"); return(-1); From d605780e576e1f1952c8158cb693003a73559d26 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 29 Mar 2019 09:04:39 -1100 Subject: [PATCH 543/787] +print --- src/komodo_gateway.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 225524eaf..d82cb47e8 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1591,16 +1591,22 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 if ( price >= highprice ) { fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); - *maxflagp = 1; if ( price > highprice ) // return non-zero only if we violate the tolerance + { + *maxflagp = 2; return(highprice); + } + *maxflagp = 1; } else if ( price <= lowprice ) { fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); - *maxflagp = -1; if ( price < lowprice ) + { + *maxflagp = -2; return(lowprice); + } + *maxflagp = -1; } return(0); } From d3ff021e338f09f7d7acd997df32d8621aedef10 Mon Sep 17 00:00:00 2001 From: "Anton \"TonyL\" Lysakov" Date: Sun, 31 Mar 2019 05:42:13 +0700 Subject: [PATCH 544/787] Update tuilib.py Discovered that `keystrokes.log` creating in folder from which game calling, not especially daemon folder. So before changed from correct to not correct :/ --- src/tui/lib/tuilib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tui/lib/tuilib.py b/src/tui/lib/tuilib.py index b46030af8..9a2fed639 100755 --- a/src/tui/lib/tuilib.py +++ b/src/tui/lib/tuilib.py @@ -1952,10 +1952,10 @@ def find_game_keystrokes_in_log(gametxid): operating_system = platform.system() if operating_system == 'Win64' or operating_system == 'Windows': - p1 = subprocess.Popen(["type", "../keystrokes.log"], stdout=subprocess.PIPE, shell=True) + p1 = subprocess.Popen(["type", "keystrokes.log"], stdout=subprocess.PIPE, shell=True) p2 = subprocess.Popen(["findstr", gametxid], stdin=p1.stdout, stdout=subprocess.PIPE, shell=True) else: - p1 = subprocess.Popen(["cat", "../keystrokes.log"], stdout=subprocess.PIPE) + p1 = subprocess.Popen(["cat", "keystrokes.log"], stdout=subprocess.PIPE) p2 = subprocess.Popen(["grep", gametxid], stdin=p1.stdout, stdout=subprocess.PIPE) p1.stdout.close() output = p2.communicate()[0] From b0edb6718cdd5528742537e9f8a8aff60338686f Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 04:19:43 -1100 Subject: [PATCH 545/787] Fix cmp bug --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d82cb47e8..bec86a439 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1722,7 +1722,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) localbits[i] = prevbits[i]; if ( maxflag == 0 ) { - if ( komodo_pricecmp(nHeight,n,&maxflag,localbits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,localbits,PRICES_MAXCHANGE) < 0 ) { fprintf(stderr,"maxflag.0 cmp error\n"); return(-1); From c85b4db249d1c027be04e08744312d93caa82e07 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 04:42:11 -1100 Subject: [PATCH 546/787] Test --- src/bitcoind.cpp | 2 +- src/komodo_gateway.h | 31 ++++++++++++++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 3c5cfe4d6..0afb47894 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -90,7 +90,7 @@ void WaitForShutdown(boost::thread_group* threadGroup) //komodo_longestchain(); if ( ASSETCHAINS_CBOPRET != 0 ) komodo_cbopretupdate(); - for (i=0; i vopret; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; + std::vector vopret; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 ) { GetOpReturnData(scriptPubKey,vopret); @@ -1698,9 +1698,13 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { n = (int32_t)(vopret.size() / sizeof(uint32_t)); memcpy(pricebits,vopret.data(),Mineropret.size()); - lag = (int32_t)(time(NULL) - pricebits[0]); + lag = (int32_t)(now - pricebits[0]); + if ( lag > 60 ) // blocks from future not so good to have + return(-1); if ( lag < 0 ) lag = -lag; + if ( lag > ASSETCHAINS_BLOCKTIME ) + return(-1); lag2 = (int32_t)(pricebits[0] - komodo_heightstamp(nHeight-1)); fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); if ( nHeight > 1 ) @@ -1735,11 +1739,11 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) maxflag = 0; if ( (newprice= komodo_pricenew(&maxflag,pricebits[i],prevbits[i],PRICES_MAXCHANGE)) != 0 ) // proposed price is clamped { - // make sure local price is beyond clamped + // make sure local price is moving in right direction fprintf(stderr,"maxflag.%d i.%d localbits.%u vs pricebits.%u\n",maxflag,i,localbits[i],pricebits[i]); - if ( maxflag > 0 && localbits[i] < pricebits[i] ) + if ( maxflag > 0 && localbits[i] < prevbits[i] ) return(-1); - else if ( maxflag < 0 && localbits[i] > pricebits[i] ) + else if ( maxflag < 0 && localbits[i] > prevbits[i] ) return(-1); } } @@ -2009,9 +2013,9 @@ int32_t get_btcusd(uint32_t pricebits[4]) void komodo_cbopretupdate() { - static uint32_t lasttime; + static uint32_t lasttime,lastcrypto,lastbtc; static uint32_t pricebits[4],cryptoprices[sizeof(Cryptos)/sizeof(*Cryptos)],forexprices[sizeof(Forex)/sizeof(*Forex)]; - int32_t size; + int32_t size; uint32_t now = (uint32_t)time(NULL); if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) { if ( komodo_nextheight() > 333 ) @@ -2024,13 +2028,14 @@ void komodo_cbopretupdate() if ( Mineropret.size() < size ) Mineropret.resize(size); size = PRICES_SIZEBIT0; - if ( get_btcusd(pricebits) == 0 ) + if ( now > lastbtc+30 && get_btcusd(pricebits) == 0 ) { memcpy(Mineropret.data(),pricebits,PRICES_SIZEBIT0); + lastbtc = (uint32_t)time(NULL); } if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) { - if ( time(NULL) > lasttime+3600*5 || forexprices[0] == 0 ) + if ( now > lasttime+3600*5 || forexprices[0] == 0 ) { get_dailyfx(forexprices); memcpy(&Mineropret.data()[size],forexprices,sizeof(forexprices)); @@ -2040,8 +2045,12 @@ void komodo_cbopretupdate() } if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) { - get_cryptoprices(cryptoprices,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); - memcpy(&Mineropret.data()[size],cryptoprices,sizeof(cryptoprices)); + if ( now > lastcrypto+60 ) + { + get_cryptoprices(cryptoprices,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); + memcpy(&Mineropret.data()[size],cryptoprices,sizeof(cryptoprices)); + lastcrypto = (uint32_t)time(NULL); + } size += sizeof(cryptoprices); } //int32_t i; for (i=0; i Date: Sun, 31 Mar 2019 04:50:02 -1100 Subject: [PATCH 547/787] Test --- src/komodo_gateway.h | 68 ++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 66fff3984..74ba9a779 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1698,17 +1698,17 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { n = (int32_t)(vopret.size() / sizeof(uint32_t)); memcpy(pricebits,vopret.data(),Mineropret.size()); - lag = (int32_t)(now - pricebits[0]); - if ( lag > 60 ) // blocks from future not so good to have - return(-1); - if ( lag < 0 ) - lag = -lag; - if ( lag > ASSETCHAINS_BLOCKTIME ) - return(-1); - lag2 = (int32_t)(pricebits[0] - komodo_heightstamp(nHeight-1)); - fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); if ( nHeight > 1 ) { + lag = (int32_t)(now - pricebits[0]); + if ( lag > 60 ) // blocks from future not so good to have + return(-1); + if ( lag < 0 ) + lag = -lag; + if ( lag > ASSETCHAINS_BLOCKTIME ) + return(-1); + lag2 = (int32_t)(pricebits[0] - komodo_heightstamp(nHeight-1)); + fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) @@ -1717,34 +1717,34 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) return(-1); } } else return(-1); - } - if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) - { - memcpy(localbits,Mineropret.data(),Mineropret.size()); - for (i=0; i= PRICES_SIZEBIT0 ) { - if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,localbits,PRICES_MAXCHANGE) < 0 ) + memcpy(localbits,Mineropret.data(),Mineropret.size()); + for (i=0; i 0 && localbits[i] < prevbits[i] ) - return(-1); - else if ( maxflag < 0 && localbits[i] > prevbits[i] ) - return(-1); + fprintf(stderr,"maxflag.0 cmp error\n"); + return(-1); + } + } + else + { + for (i=1; i 0 && localbits[i] < prevbits[i] ) + return(-1); + else if ( maxflag < 0 && localbits[i] > prevbits[i] ) + return(-1); + } } } } From eb23d9e3c46e858419b5d63820c5cdf3accfe6cd Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 04:52:29 -1100 Subject: [PATCH 548/787] Fix lag calc --- src/komodo_gateway.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 74ba9a779..c8219fc08 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1701,12 +1701,18 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( nHeight > 1 ) { lag = (int32_t)(now - pricebits[0]); - if ( lag > 60 ) // blocks from future not so good to have + if ( lag > ASSETCHAINS_BLOCKTIME ) + { + fprintf(stderr,"now.%u - pricebits[0] %u -> lag.%d\n",now,pricebits[0],lag); return(-1); + } if ( lag < 0 ) lag = -lag; - if ( lag > ASSETCHAINS_BLOCKTIME ) + if ( lag > 60 ) // avoid data from future + { + fprintf(stderr,"now.%u - pricebits[0] %u -> lag.%d\n",now,pricebits[0],lag); return(-1); + } lag2 = (int32_t)(pricebits[0] - komodo_heightstamp(nHeight-1)); fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) From 275e78667c9c8957bf6f6f8eed9db845dd79bd73 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 04:56:26 -1100 Subject: [PATCH 549/787] Use lag2 --- src/komodo_gateway.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index c8219fc08..f989594ff 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1701,19 +1701,19 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( nHeight > 1 ) { lag = (int32_t)(now - pricebits[0]); - if ( lag > ASSETCHAINS_BLOCKTIME ) - { - fprintf(stderr,"now.%u - pricebits[0] %u -> lag.%d\n",now,pricebits[0],lag); - return(-1); - } if ( lag < 0 ) lag = -lag; - if ( lag > 60 ) // avoid data from future + lag2 = (int32_t)(komodo_heightstamp(nHeight-1) - pricebits[0]); + if ( lag2 > 60 ) { - fprintf(stderr,"now.%u - pricebits[0] %u -> lag.%d\n",now,pricebits[0],lag); + fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); + return(-1); + } + if ( lag2 < -ASSETCHAINS_BLOCKTIME-60 ) // avoid data from future + { + fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); return(-1); } - lag2 = (int32_t)(pricebits[0] - komodo_heightstamp(nHeight-1)); fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { From c171940c3f563ffe4cfa36477deda946869ab034 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 05:00:04 -1100 Subject: [PATCH 550/787] Test --- src/komodo_gateway.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index f989594ff..e3ab02086 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1701,15 +1701,8 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( nHeight > 1 ) { lag = (int32_t)(now - pricebits[0]); - if ( lag < 0 ) - lag = -lag; lag2 = (int32_t)(komodo_heightstamp(nHeight-1) - pricebits[0]); - if ( lag2 > 60 ) - { - fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); - return(-1); - } - if ( lag2 < -ASSETCHAINS_BLOCKTIME-60 ) // avoid data from future + if ( lag < -60 ) // avoid data from future { fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); return(-1); From 14943d68064c143acc7bcbb92c8a80eba1780579 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 08:54:17 -1100 Subject: [PATCH 551/787] Test --- src/komodo_gateway.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e3ab02086..64b11bafe 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1707,9 +1707,13 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); return(-1); } + // else need to check against current blocktime to prevent pricebits[0] games fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { + for (i=1; i Date: Sun, 31 Mar 2019 08:56:54 -1100 Subject: [PATCH 552/787] Test --- src/komodo_gateway.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 64b11bafe..25dc6d0bd 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1711,6 +1711,9 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { + for (i=1; i Date: Sun, 31 Mar 2019 09:03:21 -1100 Subject: [PATCH 553/787] Retry prev block error (most likely due to unfleshed data) --- src/komodo_gateway.h | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 25dc6d0bd..4e49d59d0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1711,16 +1711,23 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - for (i=1; i= PRICES_SIZEBIT0 ) From 98977bb947924964ec1c0e5e1796d7c0fa8a4131 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 09:24:02 -1100 Subject: [PATCH 554/787] Add some forex calc --- src/komodo_gateway.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 4e49d59d0..b7e1ec8f9 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1690,7 +1690,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; uint32_t now = (uint32_t)time(NULL); + std::vector vopret; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 ) { GetOpReturnData(scriptPubKey,vopret); @@ -1708,7 +1708,10 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) return(-1); } // else need to check against current blocktime to prevent pricebits[0] games - fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR htstamp.%d [%d]\n",nHeight,pricebits[0],lag,(double)pricebits[1]/10000,(double)pricebits[2]/10000,(double)pricebits[3]/10000,komodo_heightstamp(nHeight-1),lag2); + btcusd = (double)pricebits[1]/10000; + btcgbp = (double)pricebits[2]/10000; + btceur = (double)pricebits[3]/10000; + fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.7f, EURGBP %.6f htstamp.%d [%d]\n",nHeight,pricebits[0],lag,btcusd,btcgbp,btceur,btcgbp/btcusd,btceur/btcusd,btceur/btcgbp,komodo_heightstamp(nHeight-1),lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) @@ -1720,6 +1723,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"%.4f ",(double)pricebits[i]/10000); fprintf(stderr," newprices.%d\n",nHeight); sleep(3); + memcpy(pricebits,vopret.data(),Mineropret.size()); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) From 7a2cdcb8c57578e0fb441408c14fd820d09fd159 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 09:24:52 -1100 Subject: [PATCH 555/787] -timestamp prints --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b7e1ec8f9..47d84e6df 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1711,7 +1711,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) btcusd = (double)pricebits[1]/10000; btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; - fprintf(stderr,"ht.%d: t%u lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.7f, EURGBP %.6f htstamp.%d [%d]\n",nHeight,pricebits[0],lag,btcusd,btcgbp,btceur,btcgbp/btcusd,btceur/btcusd,btceur/btcgbp,komodo_heightstamp(nHeight-1),lag2); + fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.7f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcgbp/btcusd,btceur/btcusd,btceur/btcgbp,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) From 9beb02477d82a8ab322204c45a83b7b85dc438e6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 09:29:55 -1100 Subject: [PATCH 556/787] +prints --- src/komodo_gateway.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 47d84e6df..3995dadf1 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1711,7 +1711,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) btcusd = (double)pricebits[1]/10000; btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; - fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.7f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcgbp/btcusd,btceur/btcusd,btceur/btcgbp,lag2); + fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcgbp/btcusd,btceur/btcusd,btceur/btcgbp,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) @@ -1997,7 +1997,7 @@ int32_t get_stocks(const char *list[],int32_t n) int32_t get_btcusd(uint32_t pricebits[4]) { - cJSON *pjson,*bpi,*obj; char str[512]; uint64_t btcusd = 0,btcgbp = 0,btceur = 0; + cJSON *pjson,*bpi,*obj; char str[512]; double dbtcgbp,dbtcusd,dbtceur; uint64_t btcusd = 0,btcgbp = 0,btceur = 0; if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json")) != 0 ) { if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 ) @@ -2020,7 +2020,10 @@ int32_t get_btcusd(uint32_t pricebits[4]) } } free_json(pjson); - fprintf(stderr,"BTC/USD %.4f, BTC/GBP %.4f, BTC/EUR %.4f\n",dstr(btcusd),dstr(btcgbp),dstr(btceur)); + dbtcusd = (double)pricebits[1]/10000; + dbtcgbp = (double)pricebits[2]/10000; + dbtceur = (double)pricebits[3]/10000; + fprintf(stderr,"BTC/USD %.4f, BTC/GBP %.4f, BTC/EUR %.4f GBPUSD %.6f, EURUSD %.6f EURGBP %.6f\n",dbtcusd,dbtcgbp,dbtceur,dbtcgbp/dbtcusd,dbtceur/dbtcusd,dbtceur/dbtcgbp); return(0); } return(-1); From 8a520ea4399f415accc7e33bf9af5d4b9658f085 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 09:32:33 -1100 Subject: [PATCH 557/787] Invert forex calc --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3995dadf1..12a980d78 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1711,7 +1711,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) btcusd = (double)pricebits[1]/10000; btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; - fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcgbp/btcusd,btceur/btcusd,btceur/btcgbp,lag2); + fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcgbp/btceur,btcgbp/btceur,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) @@ -2023,7 +2023,7 @@ int32_t get_btcusd(uint32_t pricebits[4]) dbtcusd = (double)pricebits[1]/10000; dbtcgbp = (double)pricebits[2]/10000; dbtceur = (double)pricebits[3]/10000; - fprintf(stderr,"BTC/USD %.4f, BTC/GBP %.4f, BTC/EUR %.4f GBPUSD %.6f, EURUSD %.6f EURGBP %.6f\n",dbtcusd,dbtcgbp,dbtceur,dbtcgbp/dbtcusd,dbtceur/dbtcusd,dbtceur/dbtcgbp); + fprintf(stderr,"BTC/USD %.4f, BTC/GBP %.4f, BTC/EUR %.4f GBPUSD %.6f, EURUSD %.6f EURGBP %.6f\n",dbtcusd,dbtcgbp,dbtceur,dbtcusd/dbtcgbp,dbtcgbp/dbtceur,dbtcgbp/dbtceur); return(0); } return(-1); From 3e48426eb56ad0dd748eef821ed7e2139086a4ad Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 09:35:06 -1100 Subject: [PATCH 558/787] Fix eurusd --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 12a980d78..d04bb9fc4 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1711,7 +1711,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) btcusd = (double)pricebits[1]/10000; btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; - fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcgbp/btceur,btcgbp/btceur,lag2); + fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) @@ -2023,7 +2023,7 @@ int32_t get_btcusd(uint32_t pricebits[4]) dbtcusd = (double)pricebits[1]/10000; dbtcgbp = (double)pricebits[2]/10000; dbtceur = (double)pricebits[3]/10000; - fprintf(stderr,"BTC/USD %.4f, BTC/GBP %.4f, BTC/EUR %.4f GBPUSD %.6f, EURUSD %.6f EURGBP %.6f\n",dbtcusd,dbtcgbp,dbtceur,dbtcusd/dbtcgbp,dbtcgbp/dbtceur,dbtcgbp/dbtceur); + fprintf(stderr,"BTC/USD %.4f, BTC/GBP %.4f, BTC/EUR %.4f GBPUSD %.6f, EURUSD %.6f EURGBP %.6f\n",dbtcusd,dbtcgbp,dbtceur,dbtcusd/dbtcgbp,dbtcusd/dbtceur,dbtcgbp/dbtceur); return(0); } return(-1); From b025dd2eaf8068b2068e09112dbbfab478197127 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 09:43:53 -1100 Subject: [PATCH 559/787] Test --- src/komodo_gateway.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d04bb9fc4..c1e5dcdcb 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1590,7 +1590,7 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 lowprice--; if ( price >= highprice ) { - fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + //fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); if ( price > highprice ) // return non-zero only if we violate the tolerance { *maxflagp = 2; @@ -1600,7 +1600,7 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 } else if ( price <= lowprice ) { - fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + //fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); if ( price < lowprice ) { *maxflagp = -2; @@ -2048,7 +2048,7 @@ void komodo_cbopretupdate() if ( Mineropret.size() < size ) Mineropret.resize(size); size = PRICES_SIZEBIT0; - if ( now > lastbtc+30 && get_btcusd(pricebits) == 0 ) + if ( now > lastbtc+120 && get_btcusd(pricebits) == 0 ) { memcpy(Mineropret.data(),pricebits,PRICES_SIZEBIT0); lastbtc = (uint32_t)time(NULL); @@ -2065,7 +2065,7 @@ void komodo_cbopretupdate() } if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) { - if ( now > lastcrypto+60 ) + if ( now > lastcrypto+100 ) { get_cryptoprices(cryptoprices,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); memcpy(&Mineropret.data()[size],cryptoprices,sizeof(cryptoprices)); From c6fca9511810429d79e49fd2ae194064b28c90e4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 21:26:51 -1100 Subject: [PATCH 560/787] Fix strange data bug --- src/komodo_gateway.h | 17 ++++++----------- src/komodo_utils.h | 2 +- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index c1e5dcdcb..d863c8a27 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1665,7 +1665,7 @@ CScript komodo_mineropret(int32_t nHeight) memcpy(pricebits,Mineropret.data(),Mineropret.size()); if ( komodo_pricecmp(0,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { - // if the new prices are not within tolerance, update Mineropret with clipped prices + // if the new prices are outside tolerance, update Mineropret with clamped prices komodo_priceclamp(n,pricebits,prevbits,PRICES_MAXCHANGE); fprintf(stderr,"update Mineropret to clamped prices\n"); memcpy(Mineropret.data(),pricebits,Mineropret.size()); @@ -1722,17 +1722,10 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) for (i=1; i= PRICES_SIZEBIT0 ) { @@ -2030,6 +2023,8 @@ int32_t get_btcusd(uint32_t pricebits[4]) } // komodo_cbopretupdate() obtains the external price data and encodes it into Mineropret, which will then be used by the miner and validation +// save history, use new data to approve past rejection, where is the auto-reconsiderblock? +// 51% correlation, smoothing void komodo_cbopretupdate() { diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 44b1192a4..a91dae1e8 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -2067,7 +2067,7 @@ void komodo_args(char *argv0) if ( ASSETCHAINS_CBOPRET != 0 ) { extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_CBOPRET),(void *)&ASSETCHAINS_CBOPRET); - //komodo_cbopretupdate(); // will set Mineropret + komodo_cbopretupdate(); // will set Mineropret fprintf(stderr,"This blockchain uses data produced from CoinDesk Bitcoin Price Index\n"); } } From 7f81f5f6ffd44e01b30007c6882eb81b7d6cb16a Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 21:44:27 -1100 Subject: [PATCH 561/787] Allow 0 price for earlyblocks --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d863c8a27..2eac0e7f4 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1618,7 +1618,7 @@ int32_t komodo_pricecmp(int32_t nHeight,int32_t n,int32_t *maxflagp,uint32_t *pr *maxflagp = 0; for (i=1; i newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); return(-1); From dc1b504b980b606e5a60f158335aab71db618466 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 21:48:54 -1100 Subject: [PATCH 562/787] Latch prevbits if new is 0 --- src/komodo_gateway.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 2eac0e7f4..1cc899f1a 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1714,6 +1714,9 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { + for (i=0; i Date: Sun, 31 Mar 2019 21:53:31 -1100 Subject: [PATCH 563/787] +print --- src/komodo_gateway.h | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 1cc899f1a..4d84fa19f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1590,7 +1590,7 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 lowprice--; if ( price >= highprice ) { - //fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); if ( price > highprice ) // return non-zero only if we violate the tolerance { *maxflagp = 2; @@ -1618,7 +1618,7 @@ int32_t komodo_pricecmp(int32_t nHeight,int32_t n,int32_t *maxflagp,uint32_t *pr *maxflagp = 0; for (i=1; i= 500 || pricebitsB[i] != 0) && (newprice= komodo_pricenew(maxflagp,pricebitsA[i],pricebitsB[i],tolerance)) != 0 ) { fprintf(stderr,"ht.%d i.%d/%d %u vs %u -> newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); return(-1); @@ -1714,9 +1714,12 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - for (i=0; i Date: Sun, 31 Mar 2019 21:56:44 -1100 Subject: [PATCH 564/787] 339 exemption --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 4d84fa19f..8fad25202 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1720,7 +1720,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( pricebits[i] == 0 ) pricebits[i] = prevbits[i]; } - if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( nHeight != 339 && komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { for (i=1; i Date: Sun, 31 Mar 2019 21:58:47 -1100 Subject: [PATCH 565/787] 340 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 8fad25202..4aab886b5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1720,7 +1720,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( pricebits[i] == 0 ) pricebits[i] = prevbits[i]; } - if ( nHeight != 339 && komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( nHeight != 340 && komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { for (i=1; i Date: Sun, 31 Mar 2019 22:02:28 -1100 Subject: [PATCH 566/787] Skip first 500 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 4aab886b5..c8a3d3c9f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1720,7 +1720,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( pricebits[i] == 0 ) pricebits[i] = prevbits[i]; } - if ( nHeight != 340 && komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( nHeight < 500 && komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { for (i=1; i Date: Sun, 31 Mar 2019 22:06:51 -1100 Subject: [PATCH 567/787] 500 --- src/komodo_gateway.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index c8a3d3c9f..26bf2fcc3 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1618,7 +1618,7 @@ int32_t komodo_pricecmp(int32_t nHeight,int32_t n,int32_t *maxflagp,uint32_t *pr *maxflagp = 0; for (i=1; i= 500 || pricebitsB[i] != 0) && (newprice= komodo_pricenew(maxflagp,pricebitsA[i],pricebitsB[i],tolerance)) != 0 ) + if ( (newprice= komodo_pricenew(maxflagp,pricebitsA[i],pricebitsB[i],tolerance)) != 0 ) { fprintf(stderr,"ht.%d i.%d/%d %u vs %u -> newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); return(-1); @@ -1693,6 +1693,8 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) std::vector vopret; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 ) { + if ( nHeight < 500 ) + return(0); GetOpReturnData(scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) { @@ -1714,13 +1716,13 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - if ( nHeight < 500 ) + /*if ( nHeight < 500 ) { for (i=0; i Date: Sun, 31 Mar 2019 22:09:36 -1100 Subject: [PATCH 568/787] 600 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 26bf2fcc3..66a6f2515 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1693,7 +1693,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) std::vector vopret; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 ) { - if ( nHeight < 500 ) + if ( nHeight < 600 ) return(0); GetOpReturnData(scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) From 6e25432673f5cd2a9f8a00c64ec0ea9aef7e8a80 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:13:44 -1100 Subject: [PATCH 569/787] Fetch data first --- src/komodo_gateway.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 66a6f2515..e71b8cd4d 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1691,9 +1691,11 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { std::vector vopret; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; uint32_t now = (uint32_t)time(NULL); - if ( ASSETCHAINS_CBOPRET != 0 ) + if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { - if ( nHeight < 600 ) + if ( komodo_heightpricebits(prevbits,nHeight-1) < 0 ) + return(-1); + if ( nHeight < 350 ) return(0); GetOpReturnData(scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) @@ -1714,7 +1716,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); - if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) + //if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { /*if ( nHeight < 500 ) { @@ -1734,7 +1736,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"vs prev maxflag.%d cmp error\n",maxflag); return(-1); } // else this is the good case we hope to happen - } else return(-1); + } //else return(-1); if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) { memcpy(localbits,Mineropret.data(),Mineropret.size()); From 6d7828d809f684ac31ad5c8c2a5f4c42bbe86682 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:17:45 -1100 Subject: [PATCH 570/787] Test --- src/komodo_gateway.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e71b8cd4d..65487219d 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1693,10 +1693,6 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) std::vector vopret; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { - if ( komodo_heightpricebits(prevbits,nHeight-1) < 0 ) - return(-1); - if ( nHeight < 350 ) - return(0); GetOpReturnData(scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) { @@ -1704,6 +1700,10 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) memcpy(pricebits,vopret.data(),Mineropret.size()); if ( nHeight > 1 ) { + if ( komodo_heightpricebits(prevbits,nHeight-1) < 0 ) + return(-1); + if ( nHeight < 350 ) + return(0); lag = (int32_t)(now - pricebits[0]); lag2 = (int32_t)(komodo_heightstamp(nHeight-1) - pricebits[0]); if ( lag < -60 ) // avoid data from future From 5479c8ae392e69d861a23b2494a548c97dd51311 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:25:10 -1100 Subject: [PATCH 571/787] Test --- src/komodo_gateway.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 65487219d..fc410d5ad 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1698,12 +1698,8 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { n = (int32_t)(vopret.size() / sizeof(uint32_t)); memcpy(pricebits,vopret.data(),Mineropret.size()); - if ( nHeight > 1 ) + if ( nHeight > 1 && vopret.size() == Mineropret.size() ) { - if ( komodo_heightpricebits(prevbits,nHeight-1) < 0 ) - return(-1); - if ( nHeight < 350 ) - return(0); lag = (int32_t)(now - pricebits[0]); lag2 = (int32_t)(komodo_heightstamp(nHeight-1) - pricebits[0]); if ( lag < -60 ) // avoid data from future @@ -1769,6 +1765,8 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) } } } + else if ( nHeight > 500 ) + return(-1); return(0); } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)Mineropret.size(),(int32_t)scriptPubKey.size(),scriptPubKey[0]); return(-1); From 0cba720dbd3fec82618add4880adaa6b0a0b6ac7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:28:30 -1100 Subject: [PATCH 572/787] Test --- src/komodo_gateway.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index fc410d5ad..f5149d473 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1698,7 +1698,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { n = (int32_t)(vopret.size() / sizeof(uint32_t)); memcpy(pricebits,vopret.data(),Mineropret.size()); - if ( nHeight > 1 && vopret.size() == Mineropret.size() ) + if ( nHeight > 1 ) { lag = (int32_t)(now - pricebits[0]); lag2 = (int32_t)(komodo_heightstamp(nHeight-1) - pricebits[0]); @@ -1712,14 +1712,14 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); - //if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) + if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - /*if ( nHeight < 500 ) + if ( nHeight < 500 ) { for (i=0; i 500 ) - return(-1); return(0); } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)Mineropret.size(),(int32_t)scriptPubKey.size(),scriptPubKey[0]); return(-1); From 485376098a763386dab77d14039412d2f8623f85 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:32:03 -1100 Subject: [PATCH 573/787] Test --- src/komodo_gateway.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index f5149d473..06ed31474 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1730,7 +1730,8 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr," newprices.%d\n",nHeight); fprintf(stderr,"vs prev maxflag.%d cmp error\n",maxflag); - return(-1); + if ( nHeight != 339 ) + return(-1); } // else this is the good case we hope to happen } //else return(-1); if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) From dd851e864413a33986ef3b3b8dda7dd7bdbcfeee Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:37:49 -1100 Subject: [PATCH 574/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 06ed31474..86d1ab541 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1730,7 +1730,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr," newprices.%d\n",nHeight); fprintf(stderr,"vs prev maxflag.%d cmp error\n",maxflag); - if ( nHeight != 339 ) + if ( nHeight != 339 && nHeight != 340 ) return(-1); } // else this is the good case we hope to happen } //else return(-1); From 46d59bf1d71ee46449971b469b0a3896d53f8cd0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:39:42 -1100 Subject: [PATCH 575/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 86d1ab541..79b6642e5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1730,7 +1730,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr," newprices.%d\n",nHeight); fprintf(stderr,"vs prev maxflag.%d cmp error\n",maxflag); - if ( nHeight != 339 && nHeight != 340 ) + if ( nHeight != 339 && nHeight != 340 && nHeight != 341 ) return(-1); } // else this is the good case we hope to happen } //else return(-1); From 7291a4fc73eef6d59654ca875d7b261c7b718c75 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:41:25 -1100 Subject: [PATCH 576/787] 500 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 79b6642e5..5f8c6d134 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1730,7 +1730,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr," newprices.%d\n",nHeight); fprintf(stderr,"vs prev maxflag.%d cmp error\n",maxflag); - if ( nHeight != 339 && nHeight != 340 && nHeight != 341 ) + if ( nHeight > 500 ) return(-1); } // else this is the good case we hope to happen } //else return(-1); From 1ee8042e98fb12215121fcb5b61edeb5ac69965e Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:43:38 -1100 Subject: [PATCH 577/787] Test --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 5f8c6d134..be262b24f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1714,12 +1714,12 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - if ( nHeight < 500 ) + /*if ( nHeight < 500 ) { for (i=0; i 500 ) + //if ( nHeight > 500 ) return(-1); } // else this is the good case we hope to happen } //else return(-1); From 0792e959c86a4cf3f635c5af02d20346685cb0c3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:54:42 -1100 Subject: [PATCH 578/787] CBOPRET reconsider --- src/main.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index b73a31856..bac0def5d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4120,7 +4120,14 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) + { InvalidBlockFound(pindexNew, state); + if ( ASSETCHAINS_CBOPRET != 0 ) + { + pindex->nStatus &= ~BLOCK_FAILED_MASK; + fprinf(stderr,"reconsiderblock %d\n",(int32_t)pindex->nHeight); + } + } return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } mapBlockSource.erase(pindexNew->GetBlockHash()); @@ -5211,7 +5218,7 @@ bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidat miSelf->second = pindex = AddToBlockIndex(block); if (ppindex) *ppindex = pindex; - if ( pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK ) + if ( pindex != 0 && (pindex->nStatus & BLOCK_FAILED_MASK) != 0 ) { if ( ASSETCHAINS_CC == 0 )//&& (ASSETCHAINS_PRIVATE == 0 || KOMODO_INSYNC >= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight) ) return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); From f7a881d1b3a0b411cf522951a9e4b14fb4609093 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:55:46 -1100 Subject: [PATCH 579/787] pindexNew --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bac0def5d..8a139c247 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4124,8 +4124,8 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * InvalidBlockFound(pindexNew, state); if ( ASSETCHAINS_CBOPRET != 0 ) { - pindex->nStatus &= ~BLOCK_FAILED_MASK; - fprinf(stderr,"reconsiderblock %d\n",(int32_t)pindex->nHeight); + pindexNew->nStatus &= ~BLOCK_FAILED_MASK; + fprinf(stderr,"reconsiderblock %d\n",(int32_t)pindexNew->nHeight); } } return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); From baacd254e3abef202f9c8674b8363f6407f3412c Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:56:55 -1100 Subject: [PATCH 580/787] fprinTf --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 8a139c247..e8e1e554c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4125,7 +4125,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * if ( ASSETCHAINS_CBOPRET != 0 ) { pindexNew->nStatus &= ~BLOCK_FAILED_MASK; - fprinf(stderr,"reconsiderblock %d\n",(int32_t)pindexNew->nHeight); + fprintf(stderr,"reconsiderblock %d\n",(int32_t)pindexNew->nHeight); } } return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); From 1af4f989ac9c1fa7f91198bf1685d208d1fa6940 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 22:58:52 -1100 Subject: [PATCH 581/787] GetHeight() --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index e8e1e554c..be516c25d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4125,7 +4125,7 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * if ( ASSETCHAINS_CBOPRET != 0 ) { pindexNew->nStatus &= ~BLOCK_FAILED_MASK; - fprintf(stderr,"reconsiderblock %d\n",(int32_t)pindexNew->nHeight); + fprintf(stderr,"reconsiderblock %d\n",(int32_t)pindexNew->GetHeight()); } } return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); From 6b7f7d91aeb6a51d716f6aee0fcb39728d81bd5d Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 23:01:10 -1100 Subject: [PATCH 582/787] Test --- src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index be516c25d..1036670d6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4122,11 +4122,11 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * if (state.IsInvalid()) { InvalidBlockFound(pindexNew, state); - if ( ASSETCHAINS_CBOPRET != 0 ) + /*if ( ASSETCHAINS_CBOPRET != 0 ) { pindexNew->nStatus &= ~BLOCK_FAILED_MASK; fprintf(stderr,"reconsiderblock %d\n",(int32_t)pindexNew->GetHeight()); - } + }*/ } return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } From 7a739655e4b616ae4436559c5bf35718e6c35855 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 31 Mar 2019 23:07:55 -1100 Subject: [PATCH 583/787] Test --- src/komodo_gateway.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index be262b24f..60bf83006 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1687,6 +1687,10 @@ CScript komodo_mineropret(int32_t nHeight) The only way komodo_opretvalidate() doesnt return an error is if maxflag is set or it is within tolerance of both the prior block and the local data. The local data validation only happens if it is a recent block and not a block from the past as the local node is only getting the current price data. */ +// reconsiderblock 002aca768b09dfcf36bd934ab34b23983148b116e12cb0b6e1a3f895d1db63aa +// and +// reconsiderblock 0034cf582018eacc0b4ae001491ce460113514cb1a3f217567ef4a2207de361a +// are needed to sync past initial blocks with different data set int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { @@ -1714,12 +1718,12 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - /*if ( nHeight < 500 ) + if ( nHeight < 500 ) { for (i=0; i 500 ) - return(-1); + return(-1); } // else this is the good case we hope to happen } //else return(-1); if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) From 31580754dbd6b13c1c1bdfac909061ff33bb2674 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 02:30:27 -1100 Subject: [PATCH 584/787] Always do the clamp check --- src/komodo_gateway.h | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 60bf83006..c964f19ee 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1577,7 +1577,7 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) /* komodo_pricenew() is passed in a reference price, the change tolerance and the proposed price. it needs to return a clipped price if it is too big and also set a flag if it is at or above the limit */ -uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int64_t tolerance) +uint32_t komodo_pricenew(char *maxflagp,uint32_t price,uint32_t refprice,int64_t tolerance) { uint64_t highprice,lowprice; if ( refprice < 2 ) @@ -1612,13 +1612,12 @@ uint32_t komodo_pricenew(int32_t *maxflagp,uint32_t price,uint32_t refprice,int6 } // komodo_pricecmp() returns -1 if any of the prices are beyond the tolerance -int32_t komodo_pricecmp(int32_t nHeight,int32_t n,int32_t *maxflagp,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) +int32_t komodo_pricecmp(int32_t nHeight,int32_t n,char *maxflags,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) { int32_t i; uint32_t newprice; - *maxflagp = 0; for (i=1; i newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); return(-1); @@ -1646,7 +1645,7 @@ int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int6 // komodo_mineropret() returns a valid pricedata to add to the coinbase opreturn for nHeight CScript komodo_mineropret(int32_t nHeight) { - CScript opret; uint32_t pricebits[8192],prevbits[8192]; int32_t maxflag,i,n,numzero=0; + CScript opret; char maxflags[8192]; uint32_t pricebits[8192],prevbits[8192]; int32_t maxflag,i,n,numzero=0; if ( Mineropret.size() >= PRICES_SIZEBIT0 ) { n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); @@ -1663,7 +1662,8 @@ CScript komodo_mineropret(int32_t nHeight) if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { memcpy(pricebits,Mineropret.data(),Mineropret.size()); - if ( komodo_pricecmp(0,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + memset(maxflags,0,sizeof(maxflags)); + if ( komodo_pricecmp(0,n,maxflags,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { // if the new prices are outside tolerance, update Mineropret with clamped prices komodo_priceclamp(n,pricebits,prevbits,PRICES_MAXCHANGE); @@ -1690,11 +1690,12 @@ CScript komodo_mineropret(int32_t nHeight) // reconsiderblock 002aca768b09dfcf36bd934ab34b23983148b116e12cb0b6e1a3f895d1db63aa // and // reconsiderblock 0034cf582018eacc0b4ae001491ce460113514cb1a3f217567ef4a2207de361a +// reconsiderbloc 000abf51c023b64af327c50c1b060797b8cb281c696d30ab92fd002a8b8c9aea // are needed to sync past initial blocks with different data set int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n,maxflag=0; uint32_t now = (uint32_t)time(NULL); + std::vector vopret; char maxflags[8192]; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { GetOpReturnData(scriptPubKey,vopret); @@ -1702,6 +1703,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { n = (int32_t)(vopret.size() / sizeof(uint32_t)); memcpy(pricebits,vopret.data(),Mineropret.size()); + memset(maxflags,0,sizeof(maxflags)); if ( nHeight > 1 ) { lag = (int32_t)(now - pricebits[0]); @@ -1724,7 +1726,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( pricebits[i] == 0 ) pricebits[i] = prevbits[i]; } - if ( komodo_pricecmp(nHeight,n,&maxflag,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) + if ( komodo_pricecmp(nHeight,n,maxflags,pricebits,prevbits,PRICES_MAXCHANGE) < 0 ) { for (i=1; i Date: Mon, 1 Apr 2019 02:32:20 -1100 Subject: [PATCH 585/787] Maxflag --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index c964f19ee..d90016ee4 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1619,7 +1619,7 @@ int32_t komodo_pricecmp(int32_t nHeight,int32_t n,char *maxflags,uint32_t *price { if ( (newprice= komodo_pricenew(&maxflags[i],pricebitsA[i],pricebitsB[i],tolerance)) != 0 ) { - fprintf(stderr,"ht.%d i.%d/%d %u vs %u -> newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,*maxflagp); + fprintf(stderr,"ht.%d i.%d/%d %u vs %u -> newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,maxflags[i]); return(-1); } } @@ -1695,7 +1695,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; char maxflags[8192]; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,lag,lag2,n; uint32_t now = (uint32_t)time(NULL); + std::vector vopret; char maxflags[8192]; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,maxflag,lag,lag2,n; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { GetOpReturnData(scriptPubKey,vopret); From a3c6ea84f9e14db1be10281f124d2f822f8b7525 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 02:34:02 -1100 Subject: [PATCH 586/787] 2048 --- src/komodo_gateway.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d90016ee4..0b94ccda4 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1629,10 +1629,10 @@ int32_t komodo_pricecmp(int32_t nHeight,int32_t n,char *maxflags,uint32_t *price // komodo_priceclamp() clamps any price that is beyond tolerance int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int64_t tolerance) { - int32_t i,maxflag; uint32_t newprice; + int32_t i; uint32_t newprice; char maxflags[2048]; + memset(maxflags,0,sizeof(maxflags)); for (i=1; i %u\n",i,n,refprices[i],pricebits[i],newprice); @@ -1645,7 +1645,7 @@ int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int6 // komodo_mineropret() returns a valid pricedata to add to the coinbase opreturn for nHeight CScript komodo_mineropret(int32_t nHeight) { - CScript opret; char maxflags[8192]; uint32_t pricebits[8192],prevbits[8192]; int32_t maxflag,i,n,numzero=0; + CScript opret; char maxflags[2048]; uint32_t pricebits[2048],prevbits[2048]; int32_t maxflag,i,n,numzero=0; if ( Mineropret.size() >= PRICES_SIZEBIT0 ) { n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); @@ -1695,7 +1695,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; char maxflags[8192]; double btcusd,btcgbp,btceur; uint32_t localbits[8192],pricebits[8192],prevbits[8192],newprice; int32_t i,maxflag,lag,lag2,n; uint32_t now = (uint32_t)time(NULL); + std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,maxflag,lag,lag2,n; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { GetOpReturnData(scriptPubKey,vopret); From 32c65f4686d999b11a419cf9a37ff6b25de90b04 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 02:35:07 -1100 Subject: [PATCH 587/787] &maxflags[I] --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 0b94ccda4..22952230e 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1633,7 +1633,7 @@ int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int6 memset(maxflags,0,sizeof(maxflags)); for (i=1; i %u\n",i,n,refprices[i],pricebits[i],newprice); pricebits[i] = newprice; From 16eddc774c639f755a0f58497ddee700557958ec Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 03:06:38 -1100 Subject: [PATCH 588/787] Cleanup code --- src/komodo_gateway.h | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 22952230e..2f5dfc5c5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1738,36 +1738,26 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"vs prev maxflag.%d cmp error\n",maxflag); return(-1); } // else this is the good case we hope to happen - } //else return(-1); + } else return(-1); if ( lag < ASSETCHAINS_BLOCKTIME && Mineropret.size() >= PRICES_SIZEBIT0 ) { memcpy(localbits,Mineropret.data(),Mineropret.size()); - for (i=0; i 0 && localbits[i] < prevbits[i] ) - return(-1); - else if ( maxflag < 0 && localbits[i] > prevbits[i] ) - return(-1); - } + // make sure local price is moving in right direction + fprintf(stderr,"maxflag.%d i.%d localbits.%u vs pricebits.%u\n",maxflag,i,localbits[i],pricebits[i]); + if ( maxflag > 0 && localbits[i] < prevbits[i] ) + return(-1); + else if ( maxflag < 0 && localbits[i] > prevbits[i] ) + return(-1); } } } From 19f067493995ba4e8f864de7ac52dc03c62df9b5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 04:22:00 -1100 Subject: [PATCH 589/787] Tweak print --- src/komodo_gateway.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 2f5dfc5c5..932efd051 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1692,6 +1692,8 @@ CScript komodo_mineropret(int32_t nHeight) // reconsiderblock 0034cf582018eacc0b4ae001491ce460113514cb1a3f217567ef4a2207de361a // reconsiderbloc 000abf51c023b64af327c50c1b060797b8cb281c696d30ab92fd002a8b8c9aea // are needed to sync past initial blocks with different data set +// pass in blockhash and nTime, latch if it is rejected due to local price, then if localprice changes in a way that would validate then issue reconsiderblock +// add rpc call for extracting rawprices int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) { @@ -1753,7 +1755,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) if ( (maxflag= maxflags[i]) != 0 ) { // make sure local price is moving in right direction - fprintf(stderr,"maxflag.%d i.%d localbits.%u vs pricebits.%u\n",maxflag,i,localbits[i],pricebits[i]); + fprintf(stderr,"maxflag.%d i.%d localbits.%u vs pricebits.%u prevbits.%u\n",maxflag,i,localbits[i],pricebits[i],prevbits[i]); if ( maxflag > 0 && localbits[i] < prevbits[i] ) return(-1); else if ( maxflag < 0 && localbits[i] > prevbits[i] ) From cf38e77da5fa12dc252f0f34ed930d2deae0a443 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 05:36:21 -1100 Subject: [PATCH 590/787] Change to 150000 activation --- src/komodo_bitcoind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 38f9041c1..eefccd32a 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1237,7 +1237,7 @@ uint64_t komodo_commission(const CBlock *pblock,int32_t height) n = pblock->vtx[i].vout.size(); for (j=0; j 100000 && ASSETCHAINS_STAKED != 0 && txn_count > 1 && i == txn_count-1 && j == n-1 ) + if ( height > 150000 && ASSETCHAINS_STAKED != 0 && txn_count > 1 && i == txn_count-1 && j == n-1 ) break; //fprintf(stderr,"(%d %.8f).%d ",i,dstr(pblock->vtx[i].vout[j].nValue),j); if ( i != 0 || j != 1 ) From fc12fd86d332edca0dfdd8cd36e9baad04945250 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 05:39:46 -1100 Subject: [PATCH 591/787] 225k activation --- src/komodo_bitcoind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index eefccd32a..e537df018 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1237,7 +1237,7 @@ uint64_t komodo_commission(const CBlock *pblock,int32_t height) n = pblock->vtx[i].vout.size(); for (j=0; j 150000 && ASSETCHAINS_STAKED != 0 && txn_count > 1 && i == txn_count-1 && j == n-1 ) + if ( height > 225000 && ASSETCHAINS_STAKED != 0 && txn_count > 1 && i == txn_count-1 && j == n-1 ) break; //fprintf(stderr,"(%d %.8f).%d ",i,dstr(pblock->vtx[i].vout[j].nValue),j); if ( i != 0 || j != 1 ) From ddc0fb6de14f2f866c26175ba56ed650f42dc4f3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 20:09:38 -1100 Subject: [PATCH 592/787] Check for lag2 --- src/komodo_gateway.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 932efd051..10c8718d9 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1712,7 +1712,12 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) lag2 = (int32_t)(komodo_heightstamp(nHeight-1) - pricebits[0]); if ( lag < -60 ) // avoid data from future { - fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); + fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag); + return(-1); + } + if ( lag2 < 0 ) + { + fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag2.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); return(-1); } // else need to check against current blocktime to prevent pricebits[0] games From 5150bed539d194da8212b8e863b669341e6b03fa Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 20:16:42 -1100 Subject: [PATCH 593/787] Skip block 1 --- src/komodo_gateway.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 10c8718d9..b8a62792a 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1706,7 +1706,7 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) n = (int32_t)(vopret.size() / sizeof(uint32_t)); memcpy(pricebits,vopret.data(),Mineropret.size()); memset(maxflags,0,sizeof(maxflags)); - if ( nHeight > 1 ) + if ( nHeight > 2 ) { lag = (int32_t)(now - pricebits[0]); lag2 = (int32_t)(komodo_heightstamp(nHeight-1) - pricebits[0]); @@ -1720,7 +1720,6 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag2.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); return(-1); } - // else need to check against current blocktime to prevent pricebits[0] games btcusd = (double)pricebits[1]/10000; btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; From 5d1010c35ada8b091291814bc6a77fb974fdab78 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 20:28:33 -1100 Subject: [PATCH 594/787] Test --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b8a62792a..e8b33c073 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1712,12 +1712,12 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) lag2 = (int32_t)(komodo_heightstamp(nHeight-1) - pricebits[0]); if ( lag < -60 ) // avoid data from future { - fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag); + fprintf(stderr,"ht.%d now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",nHeight,now,komodo_heightstamp(nHeight-1),pricebits[0],lag); return(-1); } if ( lag2 < 0 ) { - fprintf(stderr,"now.%u htstamp.%u - pricebits[0] %u -> lag2.%d\n",now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); + fprintf(stderr,"ht.%d now.%u htstamp.%u - pricebits[0] %u -> lag2.%d\n",nHeight,now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); return(-1); } btcusd = (double)pricebits[1]/10000; From 8a9eaea869704f6407855833989bf420e83254ec Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 20:40:26 -1100 Subject: [PATCH 595/787] Pass through block and previndex --- src/komodo_gateway.h | 19 +++++++++++++------ src/main.cpp | 12 ++++++------ src/main.h | 2 +- 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e8b33c073..724525795 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1695,9 +1695,9 @@ CScript komodo_mineropret(int32_t nHeight) // pass in blockhash and nTime, latch if it is rejected due to local price, then if localprice changes in a way that would validate then issue reconsiderblock // add rpc call for extracting rawprices -int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) +int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,maxflag,lag,lag2,n; uint32_t now = (uint32_t)time(NULL); + std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,prevtime,maxflag,lag,lag2,lag3,n; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { GetOpReturnData(scriptPubKey,vopret); @@ -1708,16 +1708,23 @@ int32_t komodo_opretvalidate(int32_t nHeight,CScript scriptPubKey) memset(maxflags,0,sizeof(maxflags)); if ( nHeight > 2 ) { + prevtime = previndex->nTime; lag = (int32_t)(now - pricebits[0]); - lag2 = (int32_t)(komodo_heightstamp(nHeight-1) - pricebits[0]); + lag2 = (int32_t)(pricebits[0] - prevtime); + lag3 = (int32_t)(block->nTime - pricebits[0]); if ( lag < -60 ) // avoid data from future { - fprintf(stderr,"ht.%d now.%u htstamp.%u - pricebits[0] %u -> lag.%d\n",nHeight,now,komodo_heightstamp(nHeight-1),pricebits[0],lag); + fprintf(stderr,"ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < 0 ) + if ( lag2 < 0 ) // must be after last block timestamp { - fprintf(stderr,"ht.%d now.%u htstamp.%u - pricebits[0] %u -> lag2.%d\n",nHeight,now,komodo_heightstamp(nHeight-1),pricebits[0],lag2); + fprintf(stderr,"ht.%d now.%u htstamp.%u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); + return(-1); + } + if ( lag3 < -60 || lag > ASSETCHAINS_BLOCKTIME ) + { + fprintf(stderr,"ht.%d now.%u htstamp.%u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } btcusd = (double)pricebits[1]/10000; diff --git a/src/main.cpp b/src/main.cpp index 1036670d6..a037bd828 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -977,7 +977,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in * Ensure that a coinbase transaction is structured according to the consensus rules of the * chain */ -bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeight) +bool ContextualCheckCoinbaseTransaction(const CBlock *block,CBlockIndex * const previndex,const CTransaction& tx, const int nHeight) { // if time locks are on, ensure that this coin base is time locked exactly as it should be if (((uint64_t)(tx.GetValueOut()) >= ASSETCHAINS_TIMELOCKGTE) || @@ -1020,7 +1020,7 @@ bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeigh } else if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 && tx.vout.size() > 0 ) { - if ( komodo_opretvalidate(nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 ) + if ( komodo_opretvalidate(block,previndex,nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 ) return(false); } return(true); @@ -1035,7 +1035,7 @@ bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeigh * and ContextualCheckBlock (which calls this function). * 3. The isInitBlockDownload argument is only to assist with testing. */ -bool ContextualCheckTransaction( +bool ContextualCheckTransaction(const CBlock *block, CBlockIndex * const previndex, const CTransaction& tx, CValidationState &state, const int nHeight, @@ -1171,7 +1171,7 @@ bool ContextualCheckTransaction( if (tx.IsCoinBase()) { - if (!ContextualCheckCoinbaseTransaction(tx, nHeight)) + if (!ContextualCheckCoinbaseTransaction(block,previndex,tx, nHeight)) return state.DoS(100, error("CheckTransaction(): invalid script data for coinbase time lock"), REJECT_INVALID, "bad-txns-invalid-script-data-for-coinbase-time-lock"); } @@ -1679,7 +1679,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // DoS level set to 10 to be more forgiving. // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. - if (!fSkipExpiry && !ContextualCheckTransaction(tx, state, nextBlockHeight, (dosLevel == -1) ? 10 : dosLevel)) + if (!fSkipExpiry && !ContextualCheckTransaction(0,0,tx, state, nextBlockHeight, (dosLevel == -1) ? 10 : dosLevel)) { return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); } @@ -5173,7 +5173,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn const CTransaction& tx = block.vtx[i]; // Check transaction contextually against consensus rules at block height - if (!ContextualCheckTransaction(tx, state, nHeight, 100)) { + if (!ContextualCheckTransaction(&block,pindexPrev,tx, state, nHeight, 100)) { return false; // Failure reason has been set in validation state object } diff --git a/src/main.h b/src/main.h index f12bcb8cd..0cdb0b7a8 100644 --- a/src/main.h +++ b/src/main.h @@ -706,7 +706,7 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons std::vector *pvChecks = NULL); /** Check a transaction contextually against a set of consensus rules */ -bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel, +bool ContextualCheckTransaction(const CBlock *block, CBlockIndex * const pindexPrev,const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel, bool (*isInitBlockDownload)() = IsInitialBlockDownload); /** Apply the effects of this transaction on the UTXO set represented by view */ From c8d70aa804dbaf58dee13fd01f5bc7fdd586c1ee Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 20:49:11 -1100 Subject: [PATCH 596/787] Test --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 724525795..1661e3225 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1714,17 +1714,17 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i lag3 = (int32_t)(block->nTime - pricebits[0]); if ( lag < -60 ) // avoid data from future { - fprintf(stderr,"ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); + fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } if ( lag2 < 0 ) // must be after last block timestamp { - fprintf(stderr,"ht.%d now.%u htstamp.%u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); + fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } if ( lag3 < -60 || lag > ASSETCHAINS_BLOCKTIME ) { - fprintf(stderr,"ht.%d now.%u htstamp.%u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); + fprintf(stderr,"C ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } btcusd = (double)pricebits[1]/10000; From b1e8644e050a35b3f1a0a4ab703d0d28b520cdee Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 21:30:32 -1100 Subject: [PATCH 597/787] testchain_exemption = 500 --- src/komodo_gateway.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 1661e3225..847c50248 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1697,6 +1697,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { + int32_t testchain_exemption = 500; std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,prevtime,maxflag,lag,lag2,lag3,n; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { @@ -1720,9 +1721,10 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i if ( lag2 < 0 ) // must be after last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); - return(-1); + if ( nHeight > testchain_exemption ) + return(-1); } - if ( lag3 < -60 || lag > ASSETCHAINS_BLOCKTIME ) + if ( lag3 < -60 || lag3 > ASSETCHAINS_BLOCKTIME ) { fprintf(stderr,"C ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); @@ -1733,7 +1735,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) { - if ( nHeight < 500 ) + if ( nHeight < testchain_exemption ) { for (i=0; i= PRICES_SIZEBIT0 ) { memcpy(localbits,Mineropret.data(),Mineropret.size()); - if ( nHeight < 500 ) + if ( nHeight < testchain_exemption ) { for (i=0; i Date: Mon, 1 Apr 2019 21:33:39 -1100 Subject: [PATCH 598/787] if ( nHeight > testchain_exemption ) --- src/komodo_gateway.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 847c50248..100dd2a57 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1727,7 +1727,8 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i if ( lag3 < -60 || lag3 > ASSETCHAINS_BLOCKTIME ) { fprintf(stderr,"C ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); - return(-1); + if ( nHeight > testchain_exemption ) + return(-1); } btcusd = (double)pricebits[1]/10000; btcgbp = (double)pricebits[2]/10000; From 034479cf780eedac9f53774dffdd2832d0482592 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 21:37:18 -1100 Subject: [PATCH 599/787] 506 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 100dd2a57..3f90b4b60 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1697,7 +1697,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - int32_t testchain_exemption = 500; + int32_t testchain_exemption = 506; std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,prevtime,maxflag,lag,lag2,lag3,n; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { From 244c924fe1343dcba2cf26e565a146e86db7db60 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 21:49:56 -1100 Subject: [PATCH 600/787] Allow more variance from prev timestamp --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3f90b4b60..9452b62ed 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1697,7 +1697,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - int32_t testchain_exemption = 506; + int32_t testchain_exemption = 350; std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,prevtime,maxflag,lag,lag2,lag3,n; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { @@ -1718,7 +1718,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < 0 ) // must be after last block timestamp + if ( lag2 < -ASSETCHAINS_BLOCKTIME ) // must be close to last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); if ( nHeight > testchain_exemption ) From 698ea547d0351caa20f63ac5a8c712cd3bbf92bc Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 21:53:49 -1100 Subject: [PATCH 601/787] +print --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 9452b62ed..3ed1cab49 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1720,7 +1720,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( lag2 < -ASSETCHAINS_BLOCKTIME ) // must be close to last block timestamp { - fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); + fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); if ( nHeight > testchain_exemption ) return(-1); } From 3a9b1492000f316135b6f46fd508e85b967c7f89 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 21:57:23 -1100 Subject: [PATCH 602/787] int32_t --- src/bitcoind.cpp | 2 +- src/chainparams.cpp | 2 +- src/komodo_defs.h | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 0afb47894..b65fcf1a9 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -59,7 +59,7 @@ static bool fDaemon; #include "komodo_defs.h" #define KOMODO_ASSETCHAIN_MAXLEN 65 extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -extern uint32_t ASSETCHAINS_BLOCKTIME; +extern int32_t ASSETCHAINS_BLOCKTIME; extern uint64_t ASSETCHAINS_CBOPRET; void komodo_passport_iteration(); uint64_t komodo_interestsum(); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6bfcc0e5c..26e29d9de 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -91,7 +91,7 @@ static CBlock CreateGenesisBlock(uint32_t nTime, const uint256& nNonce, const st */ void *chainparams_commandline(void *ptr); #include "komodo_defs.h" -uint32_t ASSETCHAINS_BLOCKTIME = 60; +int32_t ASSETCHAINS_BLOCKTIME = 60; const arith_uint256 maxUint = UintToArith256(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 0f053f6ca..b0f618e93 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -42,8 +42,8 @@ extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; int32_t MAX_BLOCK_SIZE(int32_t height); extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; -extern uint32_t ASSETCHAIN_INIT, ASSETCHAINS_MAGIC,ASSETCHAINS_BLOCKTIME; -extern int32_t VERUS_BLOCK_POSUNITS, ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER; +extern uint32_t ASSETCHAIN_INIT, ASSETCHAINS_MAGIC; +extern int32_t VERUS_BLOCK_POSUNITS, ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER,ASSETCHAINS_BLOCKTIME; extern uint64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_FOUNDERS_REWARD; extern uint64_t ASSETCHAINS_TIMELOCKGTE; From d464a48a0d6aba57bcaf880ff28391f09fc07146 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 22:11:26 -1100 Subject: [PATCH 603/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3ed1cab49..52d9f0776 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1718,7 +1718,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < -ASSETCHAINS_BLOCKTIME ) // must be close to last block timestamp + if ( lag2 < -60 )//-ASSETCHAINS_BLOCKTIME/2 ) // must be close to last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); if ( nHeight > testchain_exemption ) From 79dab848b4b73a652e7a81aa9b36047a6b096872 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 22:16:21 -1100 Subject: [PATCH 604/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 52d9f0776..7624a57d5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1718,7 +1718,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < -60 )//-ASSETCHAINS_BLOCKTIME/2 ) // must be close to last block timestamp + if ( lag2 < -63 )//-ASSETCHAINS_BLOCKTIME/2 ) // must be close to last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); if ( nHeight > testchain_exemption ) From cbd4c636d4ea16550b2bcbef5d79d5848803368d Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 22:17:59 -1100 Subject: [PATCH 605/787] -120 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7624a57d5..7e1ede5d5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1718,7 +1718,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < -63 )//-ASSETCHAINS_BLOCKTIME/2 ) // must be close to last block timestamp + if ( lag2 < -120 )//-ASSETCHAINS_BLOCKTIME/2 ) // must be close to last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); if ( nHeight > testchain_exemption ) From 175d0fa4a6f78d75d3d493f0b596fb9fb59c0811 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 22:19:33 -1100 Subject: [PATCH 606/787] 500 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7e1ede5d5..401199837 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1697,7 +1697,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - int32_t testchain_exemption = 350; + int32_t testchain_exemption = 500; std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,prevtime,maxflag,lag,lag2,lag3,n; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { From c90448edbae897f41dca1225d240656ecfec5135 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 1 Apr 2019 23:09:51 -1100 Subject: [PATCH 607/787] prices rpc call stub --- src/komodo_gateway.h | 26 ++++++++++---- src/rpc/blockchain.cpp | 80 ++++++++++++++++++++++++++++++++---------- src/rpc/server.cpp | 1 + src/rpc/server.h | 2 ++ 4 files changed, 84 insertions(+), 25 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 401199837..a59e47a04 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1553,7 +1553,7 @@ extern std::vector Mineropret; // opreturn data set by the data gatheri #define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR // komodo_heightpricebits() extracts the price data in the coinbase for nHeight -int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) +int32_t komodo_heightpricebits(uint32_t *heightbits,int32_t nHeight) { CBlockIndex *pindex; CBlock block; CTransaction tx; int32_t numvouts; std::vector vopret; if ( (pindex= komodo_chainactive(nHeight)) != 0 ) @@ -1565,8 +1565,8 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) { - memcpy(prevbits,vopret.data(),vopret.size()); - return(0); + memcpy(heightbits,vopret.data(),vopret.size()); + return((int32_t)(vopret.size()/sizeof(uint32_t))); } } } @@ -1574,6 +1574,20 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) return(-1); } +int32_t komodo_prices(uint32_t *prices,uint32_t *correlated,uint32_t *smoothed,int32_t height) +{ + int32_t i,n; + n = komodo_heightpricebits(prices,height); + for (i=0; i 0 ) { memcpy(pricebits,Mineropret.data(),Mineropret.size()); memset(maxflags,0,sizeof(maxflags)); @@ -1718,7 +1732,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < -120 )//-ASSETCHAINS_BLOCKTIME/2 ) // must be close to last block timestamp + if ( lag2 < -testchain_exemption ) // must be close to last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); if ( nHeight > testchain_exemption ) @@ -1734,7 +1748,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); - if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) + if ( komodo_heightpricebits(prevbits,nHeight-1) > 0 ) { if ( nHeight < testchain_exemption ) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 6dac120e4..fc9ec5f7a 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1174,38 +1174,80 @@ UniValue paxprice(const UniValue& params, bool fHelp) return ret; } -UniValue paxprices(const UniValue& params, bool fHelp) +int32_t komodo_prices(uint32_t *prices,uint32_t *correlated,uint32_t *smoothed,int32_t height); + +UniValue prices(const UniValue& params, bool fHelp) { - if ( fHelp || params.size() != 3 ) - throw runtime_error("paxprices \"base\" \"rel\" maxsamples\n"); + if ( fHelp || params.size() != 1 ) + throw runtime_error("prices maxsamples\n"); LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t relvolume,prices[4096]; uint32_t i,n; int32_t heights[sizeof(prices)/sizeof(*prices)]; - std::string base = params[0].get_str(); - std::string rel = params[1].get_str(); - int32_t maxsamples = atoi(params[2].get_str().c_str()); + UniValue ret(UniValue::VOBJ); uint32_t *prices,*correlated,*smoothed; uint32_t i,firstn=-1,n,nextheight,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; + int32_t maxsamples = atoi(params[0].get_str().c_str()); if ( maxsamples < 1 ) maxsamples = 1; else if ( maxsamples > sizeof(heights)/sizeof(*heights) ) maxsamples = sizeof(heights)/sizeof(*heights); - ret.push_back(Pair("base", base)); - ret.push_back(Pair("rel", rel)); - n = komodo_paxprices(heights,prices,maxsamples,(char *)base.c_str(),(char *)rel.c_str()); + nextheight = komodo_nextheight(); UniValue a(UniValue::VARR); - for (i=0; idaywindow+2; i++,ht--) { - UniValue item(UniValue::VOBJ); - if ( heights[i] < 0 || heights[i] > chainActive.Height() ) + if ( ht < 0 || ht > chainActive.Height() ) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); else { - CBlockIndex *pblockindex = chainActive[heights[i]]; - - item.push_back(Pair("t", (int64_t)pblockindex->nTime)); - item.push_back(Pair("p", (double)prices[i] / COIN)); - a.push_back(item); + if ( (n= komodo_prices(rawprices,ht)) > 0 ) + { + if ( firstn == -1 ) + firstn = n; + else if ( n != firstn ) + throw JSONRPCError(RPC_INVALID_PARAMETER, "numprices != first numprices"); + else + { + for (j=0; j Date: Tue, 2 Apr 2019 00:08:01 -1100 Subject: [PATCH 608/787] KOMODO_LOCALPRICE_CACHESIZE --- src/bitcoind.cpp | 4 +- src/komodo_gateway.h | 176 ++++++++++++++++++++++++++++++++--------- src/komodo_utils.h | 4 +- src/rpc/blockchain.cpp | 82 ++++++++++--------- 4 files changed, 187 insertions(+), 79 deletions(-) diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index b65fcf1a9..4e010d28e 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -64,7 +64,7 @@ extern uint64_t ASSETCHAINS_CBOPRET; void komodo_passport_iteration(); uint64_t komodo_interestsum(); int32_t komodo_longestchain(); -void komodo_cbopretupdate(); +void komodo_cbopretupdate(int32_t forceflag); void WaitForShutdown(boost::thread_group* threadGroup) { @@ -89,7 +89,7 @@ void WaitForShutdown(boost::thread_group* threadGroup) //komodo_interestsum(); //komodo_longestchain(); if ( ASSETCHAINS_CBOPRET != 0 ) - komodo_cbopretupdate(); + komodo_cbopretupdate(0); for (i=0; i<=ASSETCHAINS_BLOCKTIME/5; i++) { fShutdown = ShutdownRequested(); diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a59e47a04..6035cf82f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1551,6 +1551,15 @@ void komodo_passport_iteration() extern std::vector Mineropret; // opreturn data set by the data gathering code #define PRICES_MAXCHANGE (COIN / 100) // maximum acceptable change, set at 1% #define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR +#define KOMODO_LOCALPRICE_CACHESIZE 7 +uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex)]; + +void komodo_PriceCache_shift() +{ + int32_t i; + for (i=KOMODO_LOCALPRICE_CACHESIZE-1; i>=0; i--) + memcpy(PriceCache[i],PriceCache[i-1],sizeof(PriceCache[i])); +} // komodo_heightpricebits() extracts the price data in the coinbase for nHeight int32_t komodo_heightpricebits(uint32_t *heightbits,int32_t nHeight) @@ -1574,20 +1583,6 @@ int32_t komodo_heightpricebits(uint32_t *heightbits,int32_t nHeight) return(-1); } -int32_t komodo_prices(uint32_t *prices,uint32_t *correlated,uint32_t *smoothed,int32_t height) -{ - int32_t i,n; - n = komodo_heightpricebits(prices,height); - for (i=0; i vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,prevtime,maxflag,lag,lag2,lag3,n; uint32_t now = (uint32_t)time(NULL); + std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { GetOpReturnData(scriptPubKey,vopret); @@ -1778,16 +1773,48 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i if ( localbits[i] == 0 ) localbits[i] = prevbits[i]; } - for (i=1; i 0 && localbits[i] < prevbits[i] ) - return(-1); - else if ( maxflag < 0 && localbits[i] > prevbits[i] ) - return(-1); + if ( (maxflag= maxflags[i]) != 0 ) + { + // make sure local price is moving in right direction + fprintf(stderr,"maxflag.%d i.%d localbits.%u vs pricebits.%u prevbits.%u\n",maxflag,i,localbits[i],pricebits[i],prevbits[i]); + if ( maxflag > 0 && localbits[i] < prevbits[i] ) + { + if ( iter == 0 ) + break; + // second iteration checks recent prices to see if within local volatility + for (j=0; j= prevbits[i] ) + { + fprintf(stderr,"within recent localprices[%d] %u >= %u\n",j,PriceCache[j][i],prevbits[i]); + continue; + } + break; + } + else if ( maxflag < 0 && localbits[i] > prevbits[i] ) + { + if ( iter == 0 ) + break; + for (j=0; j 333 ) - ASSETCHAINS_CBOPRET = 7; +if ( komodo_nextheight() > 333 ) // for debug only! + ASSETCHAINS_CBOPRET = 7; size = PRICES_SIZEBIT0; if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) size += sizeof(forexprices); @@ -2076,31 +2104,48 @@ void komodo_cbopretupdate() if ( Mineropret.size() < size ) Mineropret.resize(size); size = PRICES_SIZEBIT0; - if ( now > lastbtc+120 && get_btcusd(pricebits) == 0 ) + if ( (forceflag != 0 || now > lastbtc+120) && get_btcusd(pricebits) == 0 ) { - memcpy(Mineropret.data(),pricebits,PRICES_SIZEBIT0); - lastbtc = (uint32_t)time(NULL); + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(PriceCache[0],pricebits,PRICES_SIZEBIT0); + flags |= 1; } if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) { - if ( now > lasttime+3600*5 || forexprices[0] == 0 ) + if ( now > lasttime+3600*5 || forexprices[0] == 0 ) // cant assume timestamp is valid for forex price as it is a daily weekday changing thing anyway. { get_dailyfx(forexprices); - memcpy(&Mineropret.data()[size],forexprices,sizeof(forexprices)); - lasttime = (uint32_t)time(NULL); + if ( flags == 0 ) + komodo_PriceCache_shift(); + flags |= 2; + memcpy(&PriceCache[0][size],forexprices,sizeof(forexprices)); } size += sizeof(forexprices); } if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) { - if ( now > lastcrypto+100 ) + if ( forceflag != 0 || flags != 0 ) { get_cryptoprices(cryptoprices,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); - memcpy(&Mineropret.data()[size],cryptoprices,sizeof(cryptoprices)); - lastcrypto = (uint32_t)time(NULL); + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(&PriceCache[0][size],cryptoprices,sizeof(cryptoprices)); + flags |= 4; // very rarely we can see flags == 6 case } size += sizeof(cryptoprices); } + if ( flags != 0 ) + { + now = (uint32_t)time(NULL); + if ( (flags & 1) != 0 ) + lastbtc = now; + if ( (flags & 2) != 0 ) + lasttime = now; + if ( (flags & 4) != 0 ) + lastcrypto = now; + memcpy(Mineropret.data(),PriceCache[0],size); + } //int32_t i; for (i=0; i sizeof(heights)/sizeof(*heights) ) - maxsamples = sizeof(heights)/sizeof(*heights); nextheight = komodo_nextheight(); UniValue a(UniValue::VARR); + if ( daywindow < 7 ) + daywindow = 7; prices = (uint32_t *)calloc(sizeof(*prices),maxsamples+daywindow); correlated = (uint32_t *)calloc(sizeof(*correlated),maxsamples+daywindow); smoothed = (uint32_t *)calloc(sizeof(*smoothed),maxsamples+daywindow); @@ -1198,7 +1201,7 @@ UniValue prices(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); else { - if ( (n= komodo_prices(rawprices,ht)) > 0 ) + if ( (n= komodo_heightpricebits(rawprices,ht)) > 0 ) { if ( firstn == -1 ) firstn = n; @@ -1212,42 +1215,45 @@ UniValue prices(const UniValue& params, bool fHelp) } else throw JSONRPCError(RPC_INVALID_PARAMETER, "no komodo_rawprices found"); } } + for (i=0; i Date: Tue, 2 Apr 2019 00:10:28 -1100 Subject: [PATCH 609/787] Rearrange --- src/komodo_gateway.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6035cf82f..ccc1e05ce 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1552,6 +1552,16 @@ extern std::vector Mineropret; // opreturn data set by the data gatheri #define PRICES_MAXCHANGE (COIN / 100) // maximum acceptable change, set at 1% #define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR #define KOMODO_LOCALPRICE_CACHESIZE 7 + + +#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) + +const char *Cryptos[] = { "KMD", "ETH", "LTC", "BCHABC", "XMR", "IOTA", "DASH", "XEM", "ZEC", "WAVES", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" }; // must be on binance (for now) + +const char *Forex[] = +{ "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" +}; // must be in ECB list + uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex)]; void komodo_PriceCache_shift() @@ -1923,13 +1933,6 @@ cJSON *send_curl(char *url,char *fname) // get_urljson just returns the JSON returned by the URL using issue_curl -#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) - -const char *Cryptos[] = { "KMD", "ETH", "LTC", "BCHABC", "XMR", "IOTA", "DASH", "XEM", "ZEC", "WAVES", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" }; - -const char *Forex[] = -{ "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" -}; /* const char *Techstocks[] = From aa2f4d910926f9d6f71fce28f306ad55131802e0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 00:14:47 -1100 Subject: [PATCH 610/787] Arr --- src/rpc/blockchain.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 20a2a8ac2..29979a489 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1178,6 +1178,8 @@ int32_t komodo_heightpricebits(uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); uint32_t komodo_pricesmoothed(uint32_t *correlatedp,uint32_t *rawprices,int32_t numprices); uint32_t komodo_timestampset(uint32_t *correlatedp,uint32_t *rawprices,int32_t numprices); +int32_t komodo_nextheight(); +uint32_t komodo_heightstamp(int32_t height); UniValue prices(const UniValue& params, bool fHelp) { @@ -1237,9 +1239,9 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*(maxsamples+daywindow) + i; smoothed[offset] = komodo_pricesmoothed(&correlated[offset],&rawprices[offset],daywindow); UniValue parr(UniValue::VARR); - parr.push_back((double)prices[offset]/10000.); - parr.push_back((double)correlated[offset]/10000.); - parr.push_back((double)smoothed[offset]/10000.); + parr.push_back((uint64_t)prices[offset]); + parr.push_back((uint64_t)correlated[offset]); + parr.push_back((uint64_t)smoothed[offset]); prices.push_back(parr); } item.push_back(Pair("prices",prices)); From fa309e5b0e84c9287556234784d3abce1198c713 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 2 Apr 2019 19:14:51 +0800 Subject: [PATCH 611/787] Add context flag to stop checking twice --- src/chain.h | 5 ++++- src/main.cpp | 20 +++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/chain.h b/src/chain.h index ee56c7662..d810ed4cb 100644 --- a/src/chain.h +++ b/src/chain.h @@ -102,6 +102,9 @@ enum BlockStatus: uint32_t { //! Scripts & signatures ok. Implies all parents are also at least SCRIPTS. BLOCK_VALID_SCRIPTS = 5, + // flag to check if contextual check block has passed in Accept block, if it has not check at connect block. + BLOCK_VALID_CONTEXT = 6, + //! All validity bits. BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS | BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS, @@ -115,7 +118,7 @@ enum BlockStatus: uint32_t { BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD, BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade - BLOCK_IN_TMPFILE = 256 + BLOCK_IN_TMPFILE = 256 }; //! Short-hand for the highest consensus validity we implement. diff --git a/src/main.cpp b/src/main.cpp index 20a963287..e8fe3810c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3274,14 +3274,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin //fprintf(stderr,"checkblock failure in connectblock futureblock.%d\n",futureblock); return false; } - // check pindex->CONTEXT_VALIDATED flag - if ( fCheckPOW != 0 && !ContextualCheckBlock(block, state, pindex->pprev) ) // Activate Jan 15th, 2019 + if ( fCheckPOW != 0 && (pindex->nStatus & BLOCK_VALID_CONTEXT) != BLOCK_VALID_CONTEXT ) // Activate Jan 15th, 2019 { - fprintf(stderr,"ContextualCheckBlock failed ht.%d\n",(int32_t)pindex->GetHeight()); - if ( pindex->nTime > 1547510400 ) - return false; - fprintf(stderr,"grandfathered exception, until jan 15th 2019\n"); + if ( !ContextualCheckBlock(block, state, pindex->pprev) ) + { + fprintf(stderr,"ContextualCheckBlock failed ht.%d\n",(int32_t)pindex->GetHeight()); + if ( pindex->nTime > 1547510400 ) + return false; + fprintf(stderr,"grandfathered exception, until jan 15th 2019\n"); + } else pindex->nStatus |= BLOCK_VALID_CONTEXT; } + // Do this here before the block is moved to the main block files. if ( ASSETCHAINS_NOTARY_PAY[0] != 0 && pindex->GetHeight() > 10 ) { @@ -5340,7 +5343,8 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C // See method docstring for why this is always disabled auto verifier = libzcash::ProofVerifier::Disabled(); - if ((!CheckBlock(futureblockp,pindex->GetHeight(),pindex,block, state, verifier,0)) || !ContextualCheckBlock(block, state, pindex->pprev)) + bool fContextualCheckBlock = ContextualCheckBlock(block, state, pindex->pprev); + if ( (!CheckBlock(futureblockp,pindex->GetHeight(),pindex,block, state, verifier,0)) || !fContextualCheckBlock ) { static int32_t saplinght = -1; CBlockIndex *tmpptr; @@ -5365,6 +5369,8 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C return false; } } + if ( fContextualCheckBlock ) + pindex->nStatus |= BLOCK_VALID_CONTEXT; int nHeight = pindex->GetHeight(); // Temp File fix. LABS has been using this for ages with no bad effects. From a29c2f179ea0545ce05b583cc7a925ab7681a6df Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 00:29:03 -1100 Subject: [PATCH 612/787] Test --- src/komodo_gateway.h | 12 +++++++---- src/rpc/blockchain.cpp | 46 +++++++++++++++++++++++++----------------- 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index ccc1e05ce..021abbd60 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2213,14 +2213,18 @@ char *komodo_pricename(char *name,int32_t ind) return(0); } -uint32_t komodo_timestampset(uint32_t *correlatedp,uint32_t *rawtimestamps,int32_t numtimestamps) +int64_t komodo_timestampset(int64_t *correlatedp,uint32_t *rawtimestamps,int32_t numtimestamps) { *correlatedp = rawtimestamps[0]; - return(rawtimestamps[0]); // really to do this would need to do it on a per pricefeed and which prices were used in the correlation, but that is a lot of extra work for a field which is not critical + return(*correlatedp); // really to do this would need to do it on a per pricefeed and which prices were used in the correlation, but that is a lot of extra work for a field which is not critical } -uint32_t komodo_pricesmoothed(uint32_t *correlatedp,uint32_t *rawprices,int32_t numprices) +int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) { - + return(0); } +int64_t komodo_pricecorrelated(uint32_t *rawprices,int32_t numprices) +{ + return(0); +} diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 29979a489..01939bb7d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1176,8 +1176,9 @@ UniValue paxprice(const UniValue& params, bool fHelp) int32_t komodo_heightpricebits(uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); -uint32_t komodo_pricesmoothed(uint32_t *correlatedp,uint32_t *rawprices,int32_t numprices); -uint32_t komodo_timestampset(uint32_t *correlatedp,uint32_t *rawprices,int32_t numprices); +int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices); +int64_t komodo_pricecorrelated(uint32_t *rawprices,int32_t numprices); +int64_t komodo_timestampset(int64_t *correlatedp,uint32_t *rawprices,int32_t numprices); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); @@ -1186,7 +1187,7 @@ UniValue prices(const UniValue& params, bool fHelp) if ( fHelp || params.size() != 1 ) throw runtime_error("prices maxsamples\n"); LOCK(cs_main); - UniValue ret(UniValue::VOBJ); char name[64],*str; uint32_t rawprices[2048],*prices,*correlated,*smoothed; uint32_t i,j,firstn=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; + UniValue ret(UniValue::VOBJ); int64_t smoothed,*correlated; char name[64],*str; uint32_t rawprices[2048],*prices; uint32_t i,width,j,firstn=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; int32_t maxsamples = atoi(params[0].get_str().c_str()); if ( maxsamples < 1 ) maxsamples = 1; @@ -1194,10 +1195,11 @@ UniValue prices(const UniValue& params, bool fHelp) UniValue a(UniValue::VARR); if ( daywindow < 7 ) daywindow = 7; - prices = (uint32_t *)calloc(sizeof(*prices),maxsamples+daywindow); - correlated = (uint32_t *)calloc(sizeof(*correlated),maxsamples+daywindow); - smoothed = (uint32_t *)calloc(sizeof(*smoothed),maxsamples+daywindow); - for (ht=nextheight-1,i=0; idaywindow+2; i++,ht--) + width = maxsamples+2*daywindow; + prices = (uint32_t *)calloc(sizeof(*prices),width); + correlated = (int64_t *)calloc(sizeof(*correlated),width); + //smoothed = (uint32_t *)calloc(sizeof(*smoothed),width); + for (ht=nextheight-1,i=0; i2*daywindow+2; i++,ht--) { if ( ht < 0 || ht > chainActive.Height() ) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); @@ -1212,7 +1214,7 @@ UniValue prices(const UniValue& params, bool fHelp) else { for (j=0; j Date: Tue, 2 Apr 2019 00:55:09 -1100 Subject: [PATCH 613/787] Fix --- src/komodo_gateway.h | 21 +++++++++++++-------- src/rpc/blockchain.cpp | 36 ++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 021abbd60..450f42aeb 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2213,18 +2213,23 @@ char *komodo_pricename(char *name,int32_t ind) return(0); } -int64_t komodo_timestampset(int64_t *correlatedp,uint32_t *rawtimestamps,int32_t numtimestamps) -{ - *correlatedp = rawtimestamps[0]; - return(*correlatedp); // really to do this would need to do it on a per pricefeed and which prices were used in the correlation, but that is a lot of extra work for a field which is not critical -} - int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) { - return(0); + int32_t i; + for (i=0; i2*daywindow+2; i++,ht--) { if ( ht < 0 || ht > chainActive.Height() ) @@ -1207,9 +1208,7 @@ UniValue prices(const UniValue& params, bool fHelp) { if ( (n= komodo_heightpricebits(rawprices,ht)) > 0 ) { - if ( firstn == -1 ) - firstn = n; - else if ( n != firstn ) + if ( n != numpricefeeds ) throw JSONRPCError(RPC_INVALID_PARAMETER, "numprices != first numprices"); else { @@ -1222,15 +1221,12 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Tue, 2 Apr 2019 00:56:12 -1100 Subject: [PATCH 614/787] uint32_t price; --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 450f42aeb..578b36cd6 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2224,7 +2224,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) int64_t komodo_pricecorrelated(uint32_t *rawprices,int32_t numprices) { - int64_t sum = 0; uint32_t price; + int32_t i; uint32_t price; int64_t sum = 0; for (i=0; i Date: Tue, 2 Apr 2019 00:58:55 -1100 Subject: [PATCH 615/787] daywindow --- src/rpc/blockchain.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 09c297ab9..bc8df8132 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1187,6 +1187,9 @@ UniValue prices(const UniValue& params, bool fHelp) throw runtime_error("prices maxsamples\n"); LOCK(cs_main); UniValue ret(UniValue::VOBJ); int64_t smoothed,*correlated; char name[64],*str; uint32_t rawprices[2048],*prices; uint32_t i,width,j,numpricefeeds=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; + if ( ASSETCHAINS_CBOPRET == 0 ) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + int32_t maxsamples = atoi(params[0].get_str().c_str()); if ( maxsamples < 1 ) maxsamples = 1; @@ -1256,7 +1259,7 @@ UniValue prices(const UniValue& params, bool fHelp) ret.push_back(Pair("height",(int64_t)nextheight-1)); ret.push_back(Pair("maxsamples",(int64_t)maxsamples)); ret.push_back(Pair("width",(int64_t)width)); - ret.push_back(Pair("daywidth",(int64_t)daywidth)); + ret.push_back(Pair("daywindow",(int64_t)daywindow)); ret.push_back(Pair("numpricefeeds",(int64_t)numpricefeeds)); free(prices); free(correlated); From ef41124c631d401166bbbd1899e5d01b0650ec98 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 01:06:00 -1100 Subject: [PATCH 616/787] +print --- src/rpc/blockchain.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index bc8df8132..6485bc2cc 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1235,6 +1235,7 @@ UniValue prices(const UniValue& params, bool fHelp) if ( (str= komodo_pricename(name,j)) != 0 ) { item.push_back(Pair("name",str)); + fprintf(stderr,"%s from %d to %d width.%d\n",name,j*width,j*width+maxsamples+daywindow,width); for (i=0; i Date: Tue, 2 Apr 2019 01:11:41 -1100 Subject: [PATCH 617/787] Fix off by one in PriceCache copy --- src/komodo_gateway.h | 8 ++++++-- src/rpc/blockchain.cpp | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 578b36cd6..c780306d0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1567,8 +1567,12 @@ uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][4+sizeof(Cryptos)/sizeof(*Crypt void komodo_PriceCache_shift() { int32_t i; - for (i=KOMODO_LOCALPRICE_CACHESIZE-1; i>=0; i--) + for (i=KOMODO_LOCALPRICE_CACHESIZE-1; i>0; i--) + { memcpy(PriceCache[i],PriceCache[i-1],sizeof(PriceCache[i])); + //for (j=0; j<4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex); j++) + // PriceCache[i][j] = PriceCache[i-1][j]; + } } // komodo_heightpricebits() extracts the price data in the coinbase for nHeight @@ -1787,7 +1791,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i { for (i=1; i Date: Tue, 2 Apr 2019 01:16:42 -1100 Subject: [PATCH 618/787] NOT raw prices, that is just single blocks data --- src/komodo_gateway.h | 2 ++ src/rpc/blockchain.cpp | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index c780306d0..a78927c05 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2199,6 +2199,7 @@ char *komodo_pricename(char *name,int32_t ind) if ( ind < sizeof(Forex)/sizeof(*Forex) ) { strcpy(name,Forex[ind]); + strcat(name,"USD"); return(name); } else ind -= sizeof(Forex)/sizeof(*Forex); } @@ -2209,6 +2210,7 @@ char *komodo_pricename(char *name,int32_t ind) if ( ind < sizeof(Cryptos)/sizeof(*Cryptos) ) { strcpy(name,Cryptos[ind]); + strcat(name,"BTC"); return(name); } else ind -= sizeof(Cryptos)/sizeof(*Cryptos); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 52a56f1ac..2ee802fb3 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1224,7 +1224,7 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Tue, 2 Apr 2019 01:19:26 -1100 Subject: [PATCH 619/787] Test --- src/rpc/blockchain.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 2ee802fb3..a1beefefd 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1231,7 +1231,7 @@ UniValue prices(const UniValue& params, bool fHelp) } for (j=1; j Date: Tue, 2 Apr 2019 01:20:34 -1100 Subject: [PATCH 620/787] Double --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a1beefefd..ac1690634 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1246,7 +1246,7 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*width + i; smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); - parr.push_back((double)prices[offset]/10000); + parr.push_back((double)(prices[offset]/10000)); parr.push_back(ValueFromAmount(correlated[i])); parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); From f6232685cd04397b681e97e9a4a4e804a0dd78eb Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 01:21:19 -1100 Subject: [PATCH 621/787] Cons --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index ac1690634..550217e2d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1246,7 +1246,7 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*width + i; smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); - parr.push_back((double)(prices[offset]/10000)); + parr.push_back(ValueFromAmount(prices[offset]*10000)); parr.push_back(ValueFromAmount(correlated[i])); parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); From d93b34b04a92360a531bc941a6bbea9b90232c96 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 01:25:45 -1100 Subject: [PATCH 622/787] Test --- src/komodo_gateway.h | 2 ++ src/rpc/blockchain.cpp | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a78927c05..6b8df1834 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2235,7 +2235,9 @@ int64_t komodo_pricecorrelated(uint32_t *rawprices,int32_t numprices) { if ( (price= rawprices[i]) == 0 ) return(0); + fprintf(stderr,"%.4f ",(double)price/10000); sum += price * 10000; } + fprintf(stderr," ave %.8f [%d]\n",((double)sum/numprices)/COIN,numprices); return(sum / numprices); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 550217e2d..8a2752088 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1229,13 +1229,12 @@ UniValue prices(const UniValue& params, bool fHelp) ret.push_back(Pair("timestamps",timestamps)); ret.push_back(Pair("firstheight", (int64_t)nextheight-1-i)); } - for (j=1; j Date: Tue, 2 Apr 2019 01:29:54 -1100 Subject: [PATCH 623/787] Limit copy range --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 8a2752088..a748e63c4 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1203,7 +1203,7 @@ UniValue prices(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "illegal numpricefeeds"); prices = (uint32_t *)calloc(sizeof(*prices),width*numpricefeeds); correlated = (int64_t *)calloc(sizeof(*correlated),width); - for (ht=nextheight-1,i=0; i2*daywindow+2; i++,ht--) + for (ht=nextheight-1,i=0; i2*daywindow+2; i++,ht--) { if ( ht < 0 || ht > chainActive.Height() ) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); From 61e193953fa380ccb41fb22c786d80972e987a8d Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 01:33:28 -1100 Subject: [PATCH 624/787] Cross copy --- src/rpc/blockchain.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a748e63c4..c1eb1bac6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1203,7 +1203,7 @@ UniValue prices(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "illegal numpricefeeds"); prices = (uint32_t *)calloc(sizeof(*prices),width*numpricefeeds); correlated = (int64_t *)calloc(sizeof(*correlated),width); - for (ht=nextheight-1,i=0; i2*daywindow+2; i++,ht--) + for (ht=nextheight-1,i=0; i2*daywindow+2; i++,ht--) { if ( ht < 0 || ht > chainActive.Height() ) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); @@ -1215,8 +1215,12 @@ UniValue prices(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "numprices != first numprices"); else { - for (j=0; j Date: Tue, 2 Apr 2019 01:34:31 -1100 Subject: [PATCH 625/787] ; --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c1eb1bac6..d5c78e68a 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1219,7 +1219,7 @@ UniValue prices(const UniValue& params, bool fHelp) { prices[j*width + i] = rawprices[j]; if ( j == 1 ) - fprintf(stderr,"[%d] <- %.1f ",j*width+i,(double)rawprices[j]/10000) + fprintf(stderr,"[%d] <- %.1f ",j*width+i,(double)rawprices[j]/10000); } } } else throw JSONRPCError(RPC_INVALID_PARAMETER, "no komodo_rawprices found"); From 7b0d2efa74c8a3939ad5670180408ab2d0c212e9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 01:37:16 -1100 Subject: [PATCH 626/787] cast --- src/komodo_gateway.h | 4 ++-- src/rpc/blockchain.cpp | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6b8df1834..6ecfd170c 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2235,8 +2235,8 @@ int64_t komodo_pricecorrelated(uint32_t *rawprices,int32_t numprices) { if ( (price= rawprices[i]) == 0 ) return(0); - fprintf(stderr,"%.4f ",(double)price/10000); - sum += price * 10000; + fprintf(stderr,"%.0f ",(double)price/10000); + sum += (int64_t)price * 10000; } fprintf(stderr," ave %.8f [%d]\n",((double)sum/numprices)/COIN,numprices); return(sum / numprices); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d5c78e68a..32b1c9cb7 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1216,11 +1216,7 @@ UniValue prices(const UniValue& params, bool fHelp) else { for (j=0; j Date: Tue, 2 Apr 2019 01:45:33 -1100 Subject: [PATCH 627/787] Fix --- src/komodo_gateway.h | 4 ++-- src/rpc/blockchain.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6ecfd170c..a87a04e57 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2235,9 +2235,9 @@ int64_t komodo_pricecorrelated(uint32_t *rawprices,int32_t numprices) { if ( (price= rawprices[i]) == 0 ) return(0); - fprintf(stderr,"%.0f ",(double)price/10000); + //fprintf(stderr,"%.0f ",(double)price/10000); sum += (int64_t)price * 10000; } - fprintf(stderr," ave %.8f [%d]\n",((double)sum/numprices)/COIN,numprices); + //fprintf(stderr," ave %.8f [%d]\n",((double)sum/numprices)/COIN,numprices); return(sum / numprices); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 32b1c9cb7..f927cf050 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1229,7 +1229,7 @@ UniValue prices(const UniValue& params, bool fHelp) ret.push_back(Pair("timestamps",timestamps)); ret.push_back(Pair("firstheight", (int64_t)nextheight-1-i)); } - for (j=1; j<2; j++) + for (j=1; j Date: Tue, 2 Apr 2019 01:54:47 -1100 Subject: [PATCH 628/787] -print --- src/komodo_gateway.h | 4 ++-- src/rpc/blockchain.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a87a04e57..bb67d1e8d 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2230,13 +2230,13 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) int64_t komodo_pricecorrelated(uint32_t *rawprices,int32_t numprices) { - int32_t i; uint32_t price; int64_t sum = 0; + int32_t i; int64_t price,sum = 0; for (i=0; i Date: Tue, 2 Apr 2019 01:58:18 -1100 Subject: [PATCH 629/787] Rescale --- src/rpc/blockchain.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 1288c02ea..2b0186f22 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1245,9 +1245,9 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*width + i; smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); - parr.push_back(((int64_t)prices[offset]*10000)); - parr.push_back((correlated[i])); - parr.push_back((smoothed)); + parr.push_back(ValueFromAmount((int64_t)prices[offset])); + parr.push_back(ValueFromAmount(correlated[i]/10000)); + parr.push_back(ValueFromAmount(smoothed/10000)); p.push_back(parr); } item.push_back(Pair("prices",p)); From b5af10d99f56f11d17bb22dcef8ebab7e6502a8a Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 02:05:27 -1100 Subject: [PATCH 630/787] Normalize all prices --- src/komodo_gateway.h | 7 ++++--- src/rpc/blockchain.cpp | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index bb67d1e8d..bbad94c51 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2228,15 +2228,16 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) return(correlated[0]); } -int64_t komodo_pricecorrelated(uint32_t *rawprices,int32_t numprices) +int64_t komodo_pricecorrelated(int32_t ind,uint32_t *rawprices,int32_t numprices) { int32_t i; int64_t price,sum = 0; for (i=0; i= 36 ) + price *= 10000; + sum += price; } //fprintf(stderr," ave %.8f [%d]\n",((double)sum/numprices)/COIN,numprices); return(sum / numprices); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 2b0186f22..c628ab074 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1246,8 +1246,8 @@ UniValue prices(const UniValue& params, bool fHelp) smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); parr.push_back(ValueFromAmount((int64_t)prices[offset])); - parr.push_back(ValueFromAmount(correlated[i]/10000)); - parr.push_back(ValueFromAmount(smoothed/10000)); + parr.push_back(ValueFromAmount(correlated[i])); + parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); } item.push_back(Pair("prices",p)); From cf281b048edc19fdbdeb1fbb567f8964d1bcaa48 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 02:07:32 -1100 Subject: [PATCH 631/787] int64_t komodo_pricecorrelated(int32_t ind,uint32_t *rawprices,int32_t numprices) --- src/rpc/blockchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c628ab074..eab787f17 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1177,7 +1177,7 @@ UniValue paxprice(const UniValue& params, bool fHelp) int32_t komodo_heightpricebits(uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices); -int64_t komodo_pricecorrelated(uint32_t *rawprices,int32_t numprices); +int64_t komodo_pricecorrelated(int32_t ind,uint32_t *rawprices,int32_t numprices); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); @@ -1238,7 +1238,7 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Tue, 2 Apr 2019 02:13:20 -1100 Subject: [PATCH 632/787] Reverse --- src/komodo_gateway.h | 14 +++++++++----- src/rpc/blockchain.cpp | 12 ++++++------ 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index bbad94c51..293b57367 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1576,9 +1576,11 @@ void komodo_PriceCache_shift() } // komodo_heightpricebits() extracts the price data in the coinbase for nHeight -int32_t komodo_heightpricebits(uint32_t *heightbits,int32_t nHeight) +int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight) { CBlockIndex *pindex; CBlock block; CTransaction tx; int32_t numvouts; std::vector vopret; + if ( seedp != 0 ) + *seedp = 0; if ( (pindex= komodo_chainactive(nHeight)) != 0 ) { if ( komodo_blockload(block,pindex) == 0 ) @@ -1588,6 +1590,8 @@ int32_t komodo_heightpricebits(uint32_t *heightbits,int32_t nHeight) GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) { + if ( seedp != 0 ) + memcpy(seedp,&pindex->hashMerkleRoot,sizeof(*seedp)); memcpy(heightbits,vopret.data(),vopret.size()); return((int32_t)(vopret.size()/sizeof(uint32_t))); } @@ -1682,7 +1686,7 @@ CScript komodo_mineropret(int32_t nHeight) if ( numzero != 0 ) fprintf(stderr,"numzero.%d\n",numzero); } - if ( komodo_heightpricebits(prevbits,nHeight-1) > 0 ) + if ( komodo_heightpricebits(0,prevbits,nHeight-1) > 0 ) { memcpy(pricebits,Mineropret.data(),Mineropret.size()); memset(maxflags,0,sizeof(maxflags)); @@ -1757,7 +1761,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); - if ( komodo_heightpricebits(prevbits,nHeight-1) > 0 ) + if ( komodo_heightpricebits(0,prevbits,nHeight-1) > 0 ) { if ( nHeight < testchain_exemption ) { @@ -2228,14 +2232,14 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) return(correlated[0]); } -int64_t komodo_pricecorrelated(int32_t ind,uint32_t *rawprices,int32_t numprices) +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices) { int32_t i; int64_t price,sum = 0; for (i=0; i= 36 ) + if ( ind < 36 ) price *= 10000; sum += price; } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index eab787f17..1cfcf4eb7 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1174,10 +1174,10 @@ UniValue paxprice(const UniValue& params, bool fHelp) return ret; } -int32_t komodo_heightpricebits(uint32_t *heightbits,int32_t nHeight); +int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices); -int64_t komodo_pricecorrelated(int32_t ind,uint32_t *rawprices,int32_t numprices); +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); @@ -1186,7 +1186,7 @@ UniValue prices(const UniValue& params, bool fHelp) if ( fHelp || params.size() != 1 ) throw runtime_error("prices maxsamples\n"); LOCK(cs_main); - UniValue ret(UniValue::VOBJ); int64_t smoothed,*correlated; char name[64],*str; uint32_t rawprices[2048],*prices; uint32_t i,width,j,numpricefeeds=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; + UniValue ret(UniValue::VOBJ); uint64_t seed; int64_t smoothed,*correlated; char name[64],*str; uint32_t rawprices[2048],*prices; uint32_t i,width,j,numpricefeeds=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; if ( ASSETCHAINS_CBOPRET == 0 ) throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); @@ -1198,7 +1198,7 @@ UniValue prices(const UniValue& params, bool fHelp) if ( daywindow < 7 ) daywindow = 7; width = maxsamples+2*daywindow; - numpricefeeds = komodo_heightpricebits(rawprices,nextheight-1); + numpricefeeds = komodo_heightpricebits(&seed,rawprices,nextheight-1); if ( numpricefeeds <= 0 ) throw JSONRPCError(RPC_INVALID_PARAMETER, "illegal numpricefeeds"); prices = (uint32_t *)calloc(sizeof(*prices),width*numpricefeeds); @@ -1209,7 +1209,7 @@ UniValue prices(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); else { - if ( (n= komodo_heightpricebits(rawprices,ht)) > 0 ) + if ( (n= komodo_heightpricebits(0,rawprices,ht)) > 0 ) { if ( n != numpricefeeds ) throw JSONRPCError(RPC_INVALID_PARAMETER, "numprices != first numprices"); @@ -1238,7 +1238,7 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Tue, 2 Apr 2019 02:17:35 -1100 Subject: [PATCH 633/787] Again --- src/rpc/blockchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 1cfcf4eb7..5bbf1ef1f 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1245,8 +1245,8 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*width + i; smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); - parr.push_back(ValueFromAmount((int64_t)prices[offset])); - parr.push_back(ValueFromAmount(correlated[i])); + parr.push_back(ValueFromAmount((int64_t)prices[offset]*10000)); + parr.push_back(ValueFromAmount(correlated[i]*ind<36?10000:1)); parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); } From 57b769c44d11e62a0af9dd73eb22991fc238efcd Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 02:19:32 -1100 Subject: [PATCH 634/787] J --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 5bbf1ef1f..772c590b6 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1246,7 +1246,7 @@ UniValue prices(const UniValue& params, bool fHelp) smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); parr.push_back(ValueFromAmount((int64_t)prices[offset]*10000)); - parr.push_back(ValueFromAmount(correlated[i]*ind<36?10000:1)); + parr.push_back(ValueFromAmount(correlated[i]*j<36?10000:1)); parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); } From 8e84d8e0ea186f1645b8d83e660d30fb71ecc172 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 02:24:31 -1100 Subject: [PATCH 635/787] ugh --- src/komodo_gateway.h | 4 ++-- src/rpc/blockchain.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 293b57367..1f1ce8dc6 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2202,8 +2202,8 @@ char *komodo_pricename(char *name,int32_t ind) return(0); if ( ind < sizeof(Forex)/sizeof(*Forex) ) { - strcpy(name,Forex[ind]); - strcat(name,"USD"); + name[0] = 'U', name[1] = 'S', name[2] = 'D'; + strcpy(name+3,Forex[ind]); return(name); } else ind -= sizeof(Forex)/sizeof(*Forex); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 772c590b6..5ba28438b 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1245,8 +1245,8 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*width + i; smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); - parr.push_back(ValueFromAmount((int64_t)prices[offset]*10000)); - parr.push_back(ValueFromAmount(correlated[i]*j<36?10000:1)); + parr.push_back(ValueFromAmount((int64_t)prices[offset]*j<36?10000:1)); + parr.push_back(ValueFromAmount(correlated[i])); parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); } From d701423bfbc274a0ab520f4c8ff52a17ac9d4db0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 02:27:08 -1100 Subject: [PATCH 636/787] Fiddle --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 5ba28438b..9f4b24235 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1245,7 +1245,7 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*width + i; smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); - parr.push_back(ValueFromAmount((int64_t)prices[offset]*j<36?10000:1)); + parr.push_back(ValueFromAmount((int64_t)prices[offset]*10000)); parr.push_back(ValueFromAmount(correlated[i])); parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); From 2df1a61d73ed3dc27c7ae91353313dda6127cde5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 02:28:59 -1100 Subject: [PATCH 637/787] Finally --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 9f4b24235..bbf90f163 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1245,7 +1245,7 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*width + i; smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); - parr.push_back(ValueFromAmount((int64_t)prices[offset]*10000)); + parr.push_back(ValueFromAmount((int64_t)prices[offset]* j<36?10000:1)); parr.push_back(ValueFromAmount(correlated[i])); parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); From b952e57e95448b2b85b0f2c492eca4941e708e06 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 02:34:32 -1100 Subject: [PATCH 638/787] ?: precedence --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index bbf90f163..668081934 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1245,7 +1245,7 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*width + i; smoothed = komodo_pricesmoothed(&correlated[i],daywindow); UniValue parr(UniValue::VARR); - parr.push_back(ValueFromAmount((int64_t)prices[offset]* j<36?10000:1)); + parr.push_back(ValueFromAmount((int64_t)prices[offset] * (j<36?10000:1))); parr.push_back(ValueFromAmount(correlated[i])); parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); From d725af022effeb935cefc2f05d8efe19cc7ef00a Mon Sep 17 00:00:00 2001 From: "Jonathan \"Duke\" Leto" Date: Tue, 2 Apr 2019 06:44:09 -0700 Subject: [PATCH 639/787] Client name is not a consensus-related option, it can be configured by any node --- src/clientversion.cpp | 2 +- src/init.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 663e2e937..084bbd5ce 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -35,7 +35,7 @@ * for both bitcoind and bitcoin-core, to make it harder for attackers to * target servers or GUI users specifically. */ -const std::string CLIENT_NAME = GetArg("-ac_clientname", "MagicBean"); +const std::string CLIENT_NAME = GetArg("-clientname", "MagicBean"); /** * Client version number diff --git a/src/init.cpp b/src/init.cpp index d2546347d..24b32a720 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -367,6 +367,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); strUsage += HelpMessageOpt("-checkblocks=", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), 288)); strUsage += HelpMessageOpt("-checklevel=", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), 3)); + strUsage += HelpMessageOpt("-clientname=", _("Full node client name, default 'MagicBean'")); strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file (default: %s)"), "komodo.conf")); if (mode == HMM_BITCOIND) { @@ -571,7 +572,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-ac_cclib", _("Cryptoconditions dynamicly loadable library")); strUsage += HelpMessageOpt("-ac_ccenable", _("Cryptoconditions to enable")); strUsage += HelpMessageOpt("-ac_ccactivate", _("Block height to enable Cryptoconditions")); - strUsage += HelpMessageOpt("-ac_clientname", _("Full node client name, default 'MagicBean'")); strUsage += HelpMessageOpt("-ac_decay", _("Percentage of block reward decrease at each halving")); strUsage += HelpMessageOpt("-ac_end", _("Block height at which block rewards will end")); strUsage += HelpMessageOpt("-ac_eras", _("Block reward eras")); From 4b7a2ca365e9d37fd21584c927ea19a643ecbbe2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 03:16:20 -1100 Subject: [PATCH 640/787] First version correlator --- src/komodo_gateway.h | 42 ++++++++++++++++++++++++++++++++---------- src/rpc/blockchain.cpp | 4 +++- 2 files changed, 35 insertions(+), 11 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 1f1ce8dc6..b59a6fbf1 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2232,17 +2232,39 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) return(correlated[0]); } -int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices) +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow) { - int32_t i; int64_t price,sum = 0; - for (i=0; i= daywindow ) + i = 0; + if ( (price= rawprices[i]) == 0 ) + return(-1); + if ( price >= lowprice && price <= highprice ) + { + sum += price; + correlation++; + if ( correlation > (daywindow>>1) ) + { + fprintf(stderr,"iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); + return(sum / correlation) + } + } + } } - //fprintf(stderr," ave %.8f [%d]\n",((double)sum/numprices)/COIN,numprices); - return(sum / numprices); + return(0); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 668081934..c68d4b14b 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1238,7 +1238,8 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Tue, 2 Apr 2019 03:17:38 -1100 Subject: [PATCH 641/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b59a6fbf1..9bfc76835 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2261,7 +2261,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( correlation > (daywindow>>1) ) { fprintf(stderr,"iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); - return(sum / correlation) + return(sum / correlation); } } } From e3a910b8cbf3c181cc37e15f0a65f7df03511a17 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 03:30:46 -1100 Subject: [PATCH 642/787] +print --- src/komodo_gateway.h | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 9bfc76835..d19325778 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1807,7 +1807,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i for (j=0; j= prevbits[i] ) { - fprintf(stderr,"within recent localprices[%d] %u >= %u\n",j,PriceCache[j][i],prevbits[i]); + fprintf(stderr,"i.%d within recent localprices[%d] %u >= %u\n",i,j,PriceCache[j][i],prevbits[i]); continue; } break; @@ -1819,7 +1819,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i for (j=0; j 333 ) // for debug only! get_stocks(Techstocks,(int32_t)(sizeof(Techstocks)/sizeof(*Techstocks))); }*/ } + pending = 0; } char *komodo_pricename(char *name,int32_t ind) @@ -2242,8 +2250,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int sum = correlation = 0; i = (j + seed) % daywindow; refprice = rawprices[i] * (ind < 36 ? 10000 : 1); - highprice = ((int64_t)refprice * (COIN + PRICES_MAXCHANGE/2)) / COIN; - lowprice = ((int64_t)refprice * (COIN - PRICES_MAXCHANGE/2)) / COIN; + highprice = ((int64_t)refprice * (COIN + PRICES_MAXCHANGE/10)) / COIN; + lowprice = ((int64_t)refprice * (COIN - PRICES_MAXCHANGE/10)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) @@ -2260,7 +2268,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int correlation++; if ( correlation > (daywindow>>1) ) { - fprintf(stderr,"iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); + fprintf(stderr,"ind.%d iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); return(sum / correlation); } } From 790873660f8d93046bf70a4b42d4bb17f295c870 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 03:33:11 -1100 Subject: [PATCH 643/787] Test --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d19325778..bda57fab4 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2250,8 +2250,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int sum = correlation = 0; i = (j + seed) % daywindow; refprice = rawprices[i] * (ind < 36 ? 10000 : 1); - highprice = ((int64_t)refprice * (COIN + PRICES_MAXCHANGE/10)) / COIN; - lowprice = ((int64_t)refprice * (COIN - PRICES_MAXCHANGE/10)) / COIN; + highprice = ((int64_t)refprice * (COIN + PRICES_MAXCHANGE)) / COIN; + lowprice = ((int64_t)refprice * (COIN - PRICES_MAXCHANGE)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) From 8209b487d8eb9d7ae94d4964ef708ed224acca46 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 03:36:05 -1100 Subject: [PATCH 644/787] Test --- src/komodo_gateway.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index bda57fab4..2e7a3f5ea 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2242,7 +2242,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow) { - int32_t i,j,iter,correlation; int64_t price,sum; uint32_t refprice,lowprice,highprice; + int32_t i,j,iter,correlation,maxcorrelation=0; int64_t price,sum; uint32_t refprice,lowprice,highprice; if ( daywindow < 2 ) return(-1); for (iter=0; iter maxcorrelation ) + maxcorrelation = correlation; } + fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d\n",ind,itermaxcorrelation); return(0); } From c808e4c196cd08a1d25c2c96d86f538a53d0721a Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 03:45:08 -1100 Subject: [PATCH 645/787] Weighted correlation --- src/komodo_gateway.h | 40 +++++++++++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 2e7a3f5ea..3dc5b1bd8 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2242,12 +2242,12 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow) { - int32_t i,j,iter,correlation,maxcorrelation=0; int64_t price,sum; uint32_t refprice,lowprice,highprice; + int32_t i,j,n,iter,correlation,maxcorrelation=0; int64_t price,sum,den; uint32_t refprice,lowprice,highprice; if ( daywindow < 2 ) return(-1); for (iter=0; iter= lowprice && price <= highprice ) { - sum += price; correlation++; if ( correlation > (daywindow>>1) ) { - fprintf(stderr,"ind.%d iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); - return(sum / correlation); + n = 0; + i = (j + seed) % daywindow; + for (j=0; j= daywindow ) + i = 0; + if ( n >= (daywindow>>1) ) + rawprices[i] = 0; + else + { + price = rawprices[i]; + if ( price < lowprice || price > highprice ) + rawprices[i] = 0; + else n++; + } + } + fprintf(stderr,"ind.%d iter.%d j.%d i.%d n.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,n,correlation,(long long)refprice,(long long)sum/correlation); + if ( n != correlation ) + return(-1); + sum = den = n = 0; + for (i=0; i %.8f\n",((double)sum / den) / COIN); + return(sum / den); } } } From 24ff9d6e54b281a2ce558f51ad7f3bc4218f16ea Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 03:46:40 -1100 Subject: [PATCH 646/787] if ( n != correlation ) return(-1); --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3dc5b1bd8..9254980b2 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2250,8 +2250,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int correlation = 0; i = (j + seed) % daywindow; refprice = rawprices[i] * (ind < 36 ? 10000 : 1); - highprice = ((int64_t)refprice * (COIN + PRICES_MAXCHANGE)) / COIN; - lowprice = ((int64_t)refprice * (COIN - PRICES_MAXCHANGE)) / COIN; + highprice = ((int64_t)refprice * (COIN + 10*PRICES_MAXCHANGE)) / COIN; + lowprice = ((int64_t)refprice * (COIN - 10*PRICES_MAXCHANGE)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) @@ -2306,6 +2306,6 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( correlation > maxcorrelation ) maxcorrelation = correlation; } - fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d\n",ind,itermaxcorrelation); + fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d\n",ind,iter,maxcorrelation); return(0); } From db8fff57ab9a8ad1b8e3c989ccac3eadf545b686 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 03:55:45 -1100 Subject: [PATCH 647/787] Scale prices --- src/komodo_gateway.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 9254980b2..06e263616 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2250,8 +2250,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int correlation = 0; i = (j + seed) % daywindow; refprice = rawprices[i] * (ind < 36 ? 10000 : 1); - highprice = ((int64_t)refprice * (COIN + 10*PRICES_MAXCHANGE)) / COIN; - lowprice = ((int64_t)refprice * (COIN - 10*PRICES_MAXCHANGE)) / COIN; + highprice = ((int64_t)refprice * (COIN + PRICES_MAXCHANGE)) / COIN; + lowprice = ((int64_t)refprice * (COIN - PRICES_MAXCHANGE)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) @@ -2262,6 +2262,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int i = 0; if ( (price= rawprices[i]) == 0 ) return(-1); + if ( ind < 36 ) + price *= 10000; if ( price >= lowprice && price <= highprice ) { correlation++; @@ -2306,6 +2308,6 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( correlation > maxcorrelation ) maxcorrelation = correlation; } - fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d\n",ind,iter,maxcorrelation); + fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d ref.%u high.%u low.%u\n",ind,iter,maxcorrelation,refprice,highprice,lowprice); return(0); } From a4788f61e7550553a2ba86e6314267771f65819d Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 03:56:29 -1100 Subject: [PATCH 648/787] Test --- src/komodo_gateway.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 06e263616..5c578fc5e 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2242,14 +2242,17 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow) { - int32_t i,j,n,iter,correlation,maxcorrelation=0; int64_t price,sum,den; uint32_t refprice,lowprice,highprice; + int32_t i,j,n,iter,correlation,maxcorrelation=0; int64_t price,sum,den; uint32_t mult,refprice,lowprice,highprice; if ( daywindow < 2 ) return(-1); + if ( ind < 36 ) + mult = 10000; + else mult = 1; for (iter=0; iter= lowprice && price <= highprice ) { correlation++; From f99fa65eda198c2e976c5f0c45c33f8d623643b0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 04:01:54 -1100 Subject: [PATCH 649/787] Test --- src/komodo_gateway.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 5c578fc5e..b5e000eb9 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2242,7 +2242,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow) { - int32_t i,j,n,iter,correlation,maxcorrelation=0; int64_t price,sum,den; uint32_t mult,refprice,lowprice,highprice; + int32_t i,j,n,iter,correlation,maxcorrelation=0; int64_t price,sum,den,mult,refprice,lowprice,highprice; if ( daywindow < 2 ) return(-1); if ( ind < 36 ) @@ -2253,8 +2253,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int correlation = 0; i = (j + seed) % daywindow; refprice = rawprices[i] * mult; - highprice = ((int64_t)refprice * (COIN + PRICES_MAXCHANGE)) / COIN; - lowprice = ((int64_t)refprice * (COIN - PRICES_MAXCHANGE)) / COIN; + highprice = (refprice * (COIN + PRICES_MAXCHANGE)) / COIN; + lowprice = (refprice * (COIN - PRICES_MAXCHANGE)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) @@ -2268,10 +2268,11 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int price *= mult; if ( price >= lowprice && price <= highprice ) { + sum += price; correlation++; if ( correlation > (daywindow>>1) ) { - n = 0; + /*n = 0; i = (j + seed) % daywindow; for (j=0; j %llu\n",ind,iter,j,i,n,correlation,(long long)refprice,(long long)sum/correlation); - if ( n != correlation ) + }*/ + fprintf(stderr,"ind.%d iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); + /*if ( n != correlation ) return(-1); sum = den = n = 0; for (i=0; i %.8f\n",((double)sum / den) / COIN); - return(sum / den); + return(sum / den);*/ + return(sum / correlation); } } } From c159b78f6f0bb33c89c86dac13ab2dba7640130b Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 04:08:11 -1100 Subject: [PATCH 650/787] Test --- src/komodo_gateway.h | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b5e000eb9..09559d960 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2231,15 +2231,6 @@ char *komodo_pricename(char *name,int32_t ind) return(0); } -int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) -{ - int32_t i; - for (i=0; i %llu\n",ind,iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); + //fprintf(stderr,"ind.%d iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); /*if ( n != correlation ) return(-1); sum = den = n = 0; @@ -2312,6 +2303,17 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( correlation > maxcorrelation ) maxcorrelation = correlation; } - fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d ref.%u high.%u low.%u\n",ind,iter,maxcorrelation,refprice,highprice,lowprice); + fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d ref.%llu high.%llu low.%llu\n",ind,iter,maxcorrelation,(long long)refprice,(long long)highprice,(long long)lowprice); return(0); } + + +int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) +{ + int32_t i; + for (i=0; i Date: Tue, 2 Apr 2019 04:18:44 -1100 Subject: [PATCH 651/787] Init sum --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 09559d960..b0b21b49f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2243,23 +2243,23 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int { correlation = 0; i = (j + seed) % daywindow; - refprice = rawprices[i] * mult; + refprice = rawprices[i]; highprice = (refprice * (COIN + PRICES_MAXCHANGE)) / COIN; lowprice = (refprice * (COIN - PRICES_MAXCHANGE)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) lowprice--; + sum = 0; for (j=0; j= daywindow ) i = 0; if ( (price= rawprices[i]) == 0 ) return(-1); - price *= mult; if ( price >= lowprice && price <= highprice ) { - sum += price; + sum += price * mult; correlation++; if ( correlation > (daywindow>>1) ) { From 2b1dff7b0c5c6d9325de82bf4fd6395a7f7a1781 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 04:19:05 -1100 Subject: [PATCH 652/787] Allow more variance --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b0b21b49f..7df402d69 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2244,8 +2244,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int correlation = 0; i = (j + seed) % daywindow; refprice = rawprices[i]; - highprice = (refprice * (COIN + PRICES_MAXCHANGE)) / COIN; - lowprice = (refprice * (COIN - PRICES_MAXCHANGE)) / COIN; + highprice = (refprice * (COIN + PRICES_MAXCHANGE*2)) / COIN; + lowprice = (refprice * (COIN - PRICES_MAXCHANGE*2)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) From 47f2069490780624642adf8d4350da9033b1cfbf Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 04:29:54 -1100 Subject: [PATCH 653/787] Weight correlated price --- src/komodo_gateway.h | 33 +++++++++++++++++++-------------- src/rpc/blockchain.cpp | 9 ++++++--- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7df402d69..66e9fae78 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2231,7 +2231,7 @@ char *komodo_pricename(char *name,int32_t ind) return(0); } -int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow) +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow,uint32_t *rawprices2) { int32_t i,j,n,iter,correlation,maxcorrelation=0; int64_t price,sum,den,mult,refprice,lowprice,highprice; if ( daywindow < 2 ) @@ -2239,13 +2239,15 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( ind < 36 ) mult = 10000; else mult = 1; + if ( memcmp(rawprices,rawprices2,daywindow*sizeof(*rawprices)) != 0 ) + fprintf(stderr,"ind.%d rawprices2 != rawprices\n",ind); for (iter=0; iter (daywindow>>1) ) { - /*n = 0; + n = 0; i = (j + seed) % daywindow; for (j=0; j= daywindow ) i = 0; if ( n >= (daywindow>>1) ) - rawprices[i] = 0; + rawprices2[i] = 0; else { price = rawprices[i]; if ( price < lowprice || price > highprice ) - rawprices[i] = 0; - else n++; + rawprices2[i] = 0; + else + { + rawprices2[i] = price; + n++; + } } - }*/ - //fprintf(stderr,"ind.%d iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); - /*if ( n != correlation ) + } + fprintf(stderr,"ind.%d iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); + if ( n != correlation ) return(-1); sum = den = n = 0; for (i=0; i %.8f\n",((double)sum / den) / COIN); - return(sum / den);*/ - return(sum / correlation); + fprintf(stderr,"weighted -> %.8f\n",((double)(sum*mult) / den) / COIN); + return((sum * mult) / den); } } } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c68d4b14b..c41803908 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1177,7 +1177,7 @@ UniValue paxprice(const UniValue& params, bool fHelp) int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices); -int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices); +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices,uint32_t *rawprices2); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); @@ -1186,7 +1186,7 @@ UniValue prices(const UniValue& params, bool fHelp) if ( fHelp || params.size() != 1 ) throw runtime_error("prices maxsamples\n"); LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t seed; int64_t smoothed,*correlated; char name[64],*str; uint32_t rawprices[2048],*prices; uint32_t i,width,j,numpricefeeds=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; + UniValue ret(UniValue::VOBJ); uint64_t seed; int64_t smoothed,*correlated; char name[64],*str; uint32_t rawprices[2048],*prices,*prices2; uint32_t i,width,j,numpricefeeds=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; if ( ASSETCHAINS_CBOPRET == 0 ) throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); @@ -1203,6 +1203,7 @@ UniValue prices(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "illegal numpricefeeds"); prices = (uint32_t *)calloc(sizeof(*prices),width*numpricefeeds); correlated = (int64_t *)calloc(sizeof(*correlated),width); + prices2 = (int64_t *)calloc(sizeof(*prices2),width); for (ht=nextheight-1,i=0; i2*daywindow+2; i++,ht--) { if ( ht < 0 || ht > chainActive.Height() ) @@ -1238,7 +1239,8 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Tue, 2 Apr 2019 04:31:40 -1100 Subject: [PATCH 654/787] Test --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c41803908..9b0f5ec0a 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1203,7 +1203,7 @@ UniValue prices(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_INVALID_PARAMETER, "illegal numpricefeeds"); prices = (uint32_t *)calloc(sizeof(*prices),width*numpricefeeds); correlated = (int64_t *)calloc(sizeof(*correlated),width); - prices2 = (int64_t *)calloc(sizeof(*prices2),width); + prices2 = (uint32_t *)calloc(sizeof(*prices2),width); for (ht=nextheight-1,i=0; i2*daywindow+2; i++,ht--) { if ( ht < 0 || ht > chainActive.Height() ) From a589a789f1eeee708896253bc50d125e7e1f7bb6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 04:35:59 -1100 Subject: [PATCH 655/787] +print --- src/komodo_gateway.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 66e9fae78..85c24c475 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2299,7 +2299,10 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int } } if ( n != correlation || sum == 0 || den == 0 ) + { + fprintf(stderr,"n.%d vs correlation.%d sum %llu, den %llu\n",n,correlation,(long long)sum,(long long)den); return(-1); + } fprintf(stderr,"weighted -> %.8f\n",((double)(sum*mult) / den) / COIN); return((sum * mult) / den); } From 41748a83fc55884bbd78e101245f59e8a1562153 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 04:39:01 -1100 Subject: [PATCH 656/787] Test --- src/komodo_gateway.h | 10 +++++----- src/rpc/blockchain.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 85c24c475..a3a6445f6 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2233,14 +2233,14 @@ char *komodo_pricename(char *name,int32_t ind) int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow,uint32_t *rawprices2) { - int32_t i,j,n,iter,correlation,maxcorrelation=0; int64_t price,sum,den,mult,refprice,lowprice,highprice; + int32_t i,j,k,n,iter,correlation,maxcorrelation=0; int64_t price,sum,den,mult,refprice,lowprice,highprice; if ( daywindow < 2 ) return(-1); if ( ind < 36 ) mult = 10000; else mult = 1; - if ( memcmp(rawprices,rawprices2,daywindow*sizeof(*rawprices)) != 0 ) - fprintf(stderr,"ind.%d rawprices2 != rawprices\n",ind); + //if ( memcmp(rawprices,rawprices2,daywindow*sizeof(*rawprices)) != 0 ) + // fprintf(stderr,"ind.%d rawprices2 != rawprices\n",ind); for (iter=0; iter= daywindow ) i = 0; @@ -2300,7 +2300,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int } if ( n != correlation || sum == 0 || den == 0 ) { - fprintf(stderr,"n.%d vs correlation.%d sum %llu, den %llu\n",n,correlation,(long long)sum,(long long)den); + fprintf(stderr,"seed.%llu n.%d vs correlation.%d sum %llu, den %llu\n",(long long)seed,n,correlation,(long long)sum,(long long)den); return(-1); } fprintf(stderr,"weighted -> %.8f\n",((double)(sum*mult) / den) / COIN); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 9b0f5ec0a..bc2e50fe4 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1239,7 +1239,7 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Tue, 2 Apr 2019 04:42:42 -1100 Subject: [PATCH 657/787] Test --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a3a6445f6..5fabf72b2 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2271,7 +2271,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int { if ( i >= daywindow ) i = 0; - if ( n >= (daywindow>>1) ) + if ( n > (daywindow>>1) ) rawprices2[i] = 0; else { @@ -2285,7 +2285,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int } } } - fprintf(stderr,"ind.%d iter.%d j.%d i.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,correlation,(long long)refprice,(long long)sum/correlation); + fprintf(stderr,"ind.%d iter.%d j.%d i.%d n.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,n,correlation,(long long)refprice,(long long)sum/correlation); if ( n != correlation ) return(-1); sum = den = n = 0; From 306314dceef91dc37ad0de86bf2ddf707c15e36b Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 04:54:34 -1100 Subject: [PATCH 658/787] Smoother --- src/komodo_gateway.h | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 5fabf72b2..d4b629f0d 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2239,8 +2239,6 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( ind < 36 ) mult = 10000; else mult = 1; - //if ( memcmp(rawprices,rawprices2,daywindow*sizeof(*rawprices)) != 0 ) - // fprintf(stderr,"ind.%d rawprices2 != rawprices\n",ind); for (iter=0; iter %llu\n",ind,iter,j,i,n,correlation,(long long)refprice,(long long)sum/correlation); + //fprintf(stderr,"ind.%d iter.%d j.%d i.%d n.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,n,correlation,(long long)refprice,(long long)sum/correlation); if ( n != correlation ) return(-1); sum = den = n = 0; @@ -2303,7 +2301,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int fprintf(stderr,"seed.%llu n.%d vs correlation.%d sum %llu, den %llu\n",(long long)seed,n,correlation,(long long)sum,(long long)den); return(-1); } - fprintf(stderr,"weighted -> %.8f\n",((double)(sum*mult) / den) / COIN); + //fprintf(stderr,"weighted -> %.8f\n",((double)(sum*mult) / den) / COIN); return((sum * mult) / den); } } @@ -2318,10 +2316,32 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) { - int32_t i; - for (i=0; i Date: Tue, 2 Apr 2019 05:00:31 -1100 Subject: [PATCH 659/787] Anchor to latest --- src/komodo_gateway.h | 7 ++++--- src/rpc/blockchain.cpp | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d4b629f0d..e7186ccc3 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2233,7 +2233,7 @@ char *komodo_pricename(char *name,int32_t ind) int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow,uint32_t *rawprices2) { - int32_t i,j,k,n,iter,correlation,maxcorrelation=0; int64_t price,sum,den,mult,refprice,lowprice,highprice; + int32_t i,j,k,n,iter,correlation,maxcorrelation=0; int64_t firstprice,price,sum,den,mult,refprice,lowprice,highprice; if ( daywindow < 2 ) return(-1); if ( ind < 36 ) @@ -2287,12 +2287,13 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( n != correlation ) return(-1); sum = den = n = 0; + firstprice = rawprices2[0]; for (i=0; i> 1); n++; } } @@ -2337,7 +2338,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) sum = den = 0; for (i=0; i> 1); den += (numprices - i); } smoothed = (sum / den); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index bc2e50fe4..2ff865f88 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1256,6 +1256,7 @@ UniValue prices(const UniValue& params, bool fHelp) item.push_back(Pair("prices",p)); } else item.push_back(Pair("name","error")); a.push_back(item); +break; } ret.push_back(Pair("pricefeeds",a)); ret.push_back(Pair("result","success")); From fd3996cb88064c718cede483310d0d62b4d4b8bf Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 05:04:22 -1100 Subject: [PATCH 660/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e7186ccc3..9f0f1e7fe 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2302,7 +2302,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int fprintf(stderr,"seed.%llu n.%d vs correlation.%d sum %llu, den %llu\n",(long long)seed,n,correlation,(long long)sum,(long long)den); return(-1); } - //fprintf(stderr,"weighted -> %.8f\n",((double)(sum*mult) / den) / COIN); + fprintf(stderr,"weighted -> %.8f\n",((double)(sum*mult) / den) / COIN); return((sum * mult) / den); } } From 88eee07c7ffca378f30f8b07d6c9ad845438f30f Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 05:08:03 -1100 Subject: [PATCH 661/787] tst --- src/komodo_gateway.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 9f0f1e7fe..57835ab7a 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1617,7 +1617,7 @@ uint32_t komodo_pricenew(char *maxflagp,uint32_t price,uint32_t refprice,int64_t lowprice--; if ( price >= highprice ) { - fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + //fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); if ( price > highprice ) // return non-zero only if we violate the tolerance { *maxflagp = 2; @@ -2287,7 +2287,10 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( n != correlation ) return(-1); sum = den = n = 0; - firstprice = rawprices2[0]; + for (i=0; i> 1); n++; - } + } else return(-1); } if ( n != correlation || sum == 0 || den == 0 ) { fprintf(stderr,"seed.%llu n.%d vs correlation.%d sum %llu, den %llu\n",(long long)seed,n,correlation,(long long)sum,(long long)den); return(-1); } - fprintf(stderr,"weighted -> %.8f\n",((double)(sum*mult) / den) / COIN); + //fprintf(stderr,"firstprice.%llu weighted -> %.8f\n",(long long)firstprice,((double)(sum*mult) / den) / COIN); return((sum * mult) / den); } } From fcea5524d7df451cb9ce0dafaf82e1c94d0f842a Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 05:10:45 -1100 Subject: [PATCH 662/787] Skip --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 57835ab7a..e0efd5734 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2298,7 +2298,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int den += (daywindow - i); sum += (daywindow - i) * ((price + firstprice) >> 1); n++; - } else return(-1); + } } if ( n != correlation || sum == 0 || den == 0 ) { From 4c0eaa7a1f5411f3eddf2faa3d5e8ede5b7e7ffd Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 05:15:02 -1100 Subject: [PATCH 663/787] 2/3 --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e0efd5734..3e6d8061a 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2296,7 +2296,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( (price= rawprices2[i]) != 0 ) { den += (daywindow - i); - sum += (daywindow - i) * ((price + firstprice) >> 1); + sum += (daywindow - i) * ((price + firstprice*2) / 3); n++; } } @@ -2305,7 +2305,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int fprintf(stderr,"seed.%llu n.%d vs correlation.%d sum %llu, den %llu\n",(long long)seed,n,correlation,(long long)sum,(long long)den); return(-1); } - //fprintf(stderr,"firstprice.%llu weighted -> %.8f\n",(long long)firstprice,((double)(sum*mult) / den) / COIN); + fprintf(stderr,"firstprice.%llu weighted -> %.8f\n",(long long)firstprice,((double)(sum*mult) / den) / COIN); return((sum * mult) / den); } } @@ -2341,7 +2341,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices) sum = den = 0; for (i=0; i> 1); + sum += (numprices - i) * ((correlated[i] + firstprice*2) / 3); den += (numprices - i); } smoothed = (sum / den); From fe9eb8df39f1ca763cfdddb22c0435f1942de5f6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 05:19:01 -1100 Subject: [PATCH 664/787] nonzprices --- src/komodo_gateway.h | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3e6d8061a..57b537606 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2231,7 +2231,7 @@ char *komodo_pricename(char *name,int32_t ind) return(0); } -int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow,uint32_t *rawprices2) +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow,uint32_t *nonzprices) { int32_t i,j,k,n,iter,correlation,maxcorrelation=0; int64_t firstprice,price,sum,den,mult,refprice,lowprice,highprice; if ( daywindow < 2 ) @@ -2270,15 +2270,16 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( i >= daywindow ) i = 0; if ( n > (daywindow>>1) ) - rawprices2[i] = 0; + nonzprices[i] = 0; else { price = rawprices[i]; if ( price < lowprice || price > highprice ) - rawprices2[i] = 0; + nonzprices[i] = 0; else { - rawprices2[i] = price; + nonzprices[i] = price; + fprintf(stderr,"(%d %u) ",i,rawprices[i]); n++; } } @@ -2288,12 +2289,13 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int return(-1); sum = den = n = 0; for (i=0; i Date: Tue, 2 Apr 2019 05:23:32 -1100 Subject: [PATCH 665/787] Test --- src/rpc/blockchain.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 2ff865f88..bc8302589 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1186,7 +1186,7 @@ UniValue prices(const UniValue& params, bool fHelp) if ( fHelp || params.size() != 1 ) throw runtime_error("prices maxsamples\n"); LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t seed; int64_t smoothed,*correlated; char name[64],*str; uint32_t rawprices[2048],*prices,*prices2; uint32_t i,width,j,numpricefeeds=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; + UniValue ret(UniValue::VOBJ); uint64_t seed,rngval; int64_t smoothed,*correlated; char name[64],*str; uint32_t rawprices[2048],*prices,*prices2; uint32_t i,width,j,numpricefeeds=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; if ( ASSETCHAINS_CBOPRET == 0 ) throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); @@ -1230,8 +1230,10 @@ UniValue prices(const UniValue& params, bool fHelp) ret.push_back(Pair("timestamps",timestamps)); ret.push_back(Pair("firstheight", (int64_t)nextheight-1-i)); } + rngval = seed; for (j=1; j Date: Tue, 2 Apr 2019 05:28:11 -1100 Subject: [PATCH 666/787] Iter + seed --- src/komodo_gateway.h | 4 ++-- src/rpc/blockchain.cpp | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 57b537606..eff32b495 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2242,7 +2242,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int for (iter=0; iter (daywindow>>1) ) { n = 0; - i = (j + seed) % daywindow; + i = (iter + seed) % daywindow; for (k=0; k= daywindow ) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index bc8302589..7055e65c3 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1241,7 +1241,6 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Tue, 2 Apr 2019 05:31:47 -1100 Subject: [PATCH 667/787] Rngval inside inner loop --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 7055e65c3..fc476657d 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1233,7 +1233,6 @@ UniValue prices(const UniValue& params, bool fHelp) rngval = seed; for (j=1; j Date: Tue, 2 Apr 2019 05:38:06 -1100 Subject: [PATCH 668/787] Test --- src/rpc/blockchain.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index fc476657d..eaeae8f60 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1231,6 +1231,9 @@ UniValue prices(const UniValue& params, bool fHelp) ret.push_back(Pair("firstheight", (int64_t)nextheight-1-i)); } rngval = seed; + for (i=0; i Date: Tue, 2 Apr 2019 05:42:40 -1100 Subject: [PATCH 669/787] tst --- src/rpc/blockchain.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index eaeae8f60..a72f0c341 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1222,16 +1222,13 @@ UniValue prices(const UniValue& params, bool fHelp) } else throw JSONRPCError(RPC_INVALID_PARAMETER, "no komodo_rawprices found"); } } + ret.push_back(Pair("firstheight", (int64_t)nextheight-1-i)); + UniValue timestamps(UniValue::VARR); for (i=0; i Date: Tue, 2 Apr 2019 05:47:10 -1100 Subject: [PATCH 670/787] Test --- src/komodo_gateway.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index eff32b495..2ea4c2753 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2263,6 +2263,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int correlation++; if ( correlation > (daywindow>>1) ) { + return(sum*mult/correlation); n = 0; i = (iter + seed) % daywindow; for (k=0; k Date: Tue, 2 Apr 2019 05:50:12 -1100 Subject: [PATCH 671/787] Test --- src/komodo_gateway.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 2ea4c2753..e6bdd9a8f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2251,6 +2251,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( lowprice == refprice ) lowprice--; sum = 0; + fprintf(stderr,"firsti.%d: ",firsti); for (j=0; j= daywindow ) @@ -2259,10 +2260,12 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int return(-1); if ( price >= lowprice && price <= highprice ) { - sum += price * mult; + fprintf(stderr,"%.1f ",(double)price/10000); + sum += price; correlation++; if ( correlation > (daywindow>>1) ) { + fprintf(stderr,"-> %.4f\n",(double)sum*mult/correlation); return(sum*mult/correlation); n = 0; i = (iter + seed) % daywindow; From 9b75e70178dddf5064f0eae97861e1f3dcdc4de2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 05:51:17 -1100 Subject: [PATCH 672/787] I --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index e6bdd9a8f..446619eb7 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2251,7 +2251,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( lowprice == refprice ) lowprice--; sum = 0; - fprintf(stderr,"firsti.%d: ",firsti); + fprintf(stderr,"firsti.%d: ",i); for (j=0; j= daywindow ) From 355ce81643952bfb6e5beddb40665b2cc68781fc Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 05:54:30 -1100 Subject: [PATCH 673/787] -print --- src/komodo_gateway.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 446619eb7..36de71951 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2251,7 +2251,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( lowprice == refprice ) lowprice--; sum = 0; - fprintf(stderr,"firsti.%d: ",i); + //fprintf(stderr,"firsti.%d: ",i); for (j=0; j= daywindow ) @@ -2260,13 +2260,13 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int return(-1); if ( price >= lowprice && price <= highprice ) { - fprintf(stderr,"%.1f ",(double)price/10000); + //fprintf(stderr,"%.1f ",(double)price/10000); sum += price; correlation++; if ( correlation > (daywindow>>1) ) { - fprintf(stderr,"-> %.4f\n",(double)sum*mult/correlation); - return(sum*mult/correlation); + //fprintf(stderr,"-> %.4f\n",(double)sum*mult/correlation); + //return(sum*mult/correlation); n = 0; i = (iter + seed) % daywindow; for (k=0; k %.8f\n",(long long)firstprice,((double)(sum*mult) / den) / COIN); + //fprintf(stderr,"firstprice.%llu weighted -> %.8f\n",(long long)firstprice,((double)(sum*mult) / den) / COIN); return((sum * mult) / den); } } From fae4422d85b83a007d4c7e52441e6feeab8a94fb Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 05:55:26 -1100 Subject: [PATCH 674/787] Rc --- src/rpc/blockchain.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index a72f0c341..5e426b88e 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1228,9 +1228,9 @@ UniValue prices(const UniValue& params, bool fHelp) timestamps.push_back((int64_t)prices[i]); ret.push_back(Pair("timestamps",timestamps)); rngval = seed; - for (i=0; i Date: Tue, 2 Apr 2019 06:03:24 -1100 Subject: [PATCH 675/787] Test --- src/komodo_gateway.h | 18 ++++++++++-------- src/rpc/blockchain.cpp | 8 +++++--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 36de71951..2d708f036 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2239,6 +2239,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( ind < 36 ) mult = 10000; else mult = 1; + memset(nonzprices,0,sizeof(*nonzprices)*daywindow); for (iter=0; iter2*daywindow+2; i++,ht--) { @@ -1247,7 +1248,7 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Tue, 2 Apr 2019 06:31:42 -1100 Subject: [PATCH 676/787] Update --- src/komodo_gateway.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 2d708f036..65f534778 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1832,6 +1832,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i { fprintf(stderr,"force update prices\n"); komodo_cbopretupdate(1); + memcpy(localbits,Mineropret.data(),Mineropret.size()); } else return(-1); } } From 503eadb7d989ff3db774ef8cfe7acdded257686f Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 06:48:55 -1100 Subject: [PATCH 677/787] komodo_cbopretupdate if zero price --- src/komodo_gateway.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 65f534778..7b6227b92 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1684,7 +1684,10 @@ CScript komodo_mineropret(int32_t nHeight) if ( pricebits[i] == 0 ) numzero++; if ( numzero != 0 ) - fprintf(stderr,"numzero.%d\n",numzero); + { + fprintf(stderr,"komodo_mineropret numzero.%d vs n.%d\n",numzero,n); + komodo_cbopretupdate(1); + } } if ( komodo_heightpricebits(0,prevbits,nHeight-1) > 0 ) { From 8be53197b396c244eab12b09d8e27e26aaa7223d Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 06:52:50 -1100 Subject: [PATCH 678/787] +prnt --- src/komodo_gateway.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7b6227b92..c04bfb381 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1682,11 +1682,15 @@ CScript komodo_mineropret(int32_t nHeight) memcpy(pricebits,Mineropret.data(),Mineropret.size()); for (i=numzero=0; i 0 ) @@ -2167,10 +2171,10 @@ if ( komodo_nextheight() > 333 ) // for debug only! if ( (flags & 4) != 0 ) lastcrypto = now; memcpy(Mineropret.data(),PriceCache[0],size); + int32_t i; for (i=0; i Date: Tue, 2 Apr 2019 06:58:35 -1100 Subject: [PATCH 679/787] Fix init conditions --- src/komodo_gateway.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index c04bfb381..99462fdd3 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1573,6 +1573,7 @@ void komodo_PriceCache_shift() //for (j=0; j<4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex); j++) // PriceCache[i][j] = PriceCache[i-1][j]; } + memcpy(PriceCache[0],Mineropret.data(),Mineropret.size()); } // komodo_heightpricebits() extracts the price data in the coinbase for nHeight @@ -2173,7 +2174,7 @@ if ( komodo_nextheight() > 333 ) // for debug only! memcpy(Mineropret.data(),PriceCache[0],size); int32_t i; for (i=0; i Date: Tue, 2 Apr 2019 07:02:57 -1100 Subject: [PATCH 680/787] /sizeof(uint32_t) --- src/komodo_gateway.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 99462fdd3..f13dbdd47 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1691,7 +1691,7 @@ CScript komodo_mineropret(int32_t nHeight) { fprintf(stderr," komodo_mineropret numzero.%d vs n.%d\n",numzero,n); komodo_cbopretupdate(1); - sleep(5); + sleep(61); } } if ( komodo_heightpricebits(0,prevbits,nHeight-1) > 0 ) @@ -1702,7 +1702,7 @@ CScript komodo_mineropret(int32_t nHeight) { // if the new prices are outside tolerance, update Mineropret with clamped prices komodo_priceclamp(n,pricebits,prevbits,PRICES_MAXCHANGE); - fprintf(stderr,"update Mineropret to clamped prices\n"); + //fprintf(stderr,"update Mineropret to clamped prices\n"); memcpy(Mineropret.data(),pricebits,Mineropret.size()); } } @@ -2146,7 +2146,7 @@ if ( komodo_nextheight() > 333 ) // for debug only! if ( flags == 0 ) komodo_PriceCache_shift(); flags |= 2; - memcpy(&PriceCache[0][size],forexprices,sizeof(forexprices)); + memcpy(&PriceCache[0][size/sizeof(uint32_t)],forexprices,sizeof(forexprices)); } size += sizeof(forexprices); } @@ -2157,7 +2157,7 @@ if ( komodo_nextheight() > 333 ) // for debug only! get_cryptoprices(cryptoprices,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); if ( flags == 0 ) komodo_PriceCache_shift(); - memcpy(&PriceCache[0][size],cryptoprices,sizeof(cryptoprices)); + memcpy(&PriceCache[0][size/sizeof(uint32_t)],cryptoprices,sizeof(cryptoprices)); flags |= 4; // very rarely we can see flags == 6 case } size += sizeof(cryptoprices); From 62a9756bc0de1d2b130b3ad34330e7b14ce813ef Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 07:07:09 -1100 Subject: [PATCH 681/787] Test --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index f13dbdd47..6d2fef363 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2172,9 +2172,9 @@ if ( komodo_nextheight() > 333 ) // for debug only! if ( (flags & 4) != 0 ) lastcrypto = now; memcpy(Mineropret.data(),PriceCache[0],size); - int32_t i; for (i=0; i Date: Tue, 2 Apr 2019 08:13:43 -1100 Subject: [PATCH 682/787] Test --- src/komodo_gateway.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6d2fef363..dbc879806 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2345,7 +2345,10 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz if ( correlated[i] == 0 ) correlated[i] = correlated[i-1]; if ( firstprice == 0 && correlated[i] != 0 ) + { firstprice = correlated[i]; + break; + } } if ( firstprice != 0 ) { @@ -2358,7 +2361,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i Date: Tue, 2 Apr 2019 08:34:57 -1100 Subject: [PATCH 683/787] Pre-smoother --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index dbc879806..276bb8667 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2312,7 +2312,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( (price= nonzprices[i]) != 0 ) { den += (daywindow - i); - sum += (daywindow - i) * price;//((price + firstprice*2) / 3); + sum += (daywindow - i) * ((price + firstprice) / 2); n++; } } @@ -2361,7 +2361,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i Date: Tue, 2 Apr 2019 08:38:18 -1100 Subject: [PATCH 684/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 276bb8667..d5f9c98f9 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2361,7 +2361,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i Date: Tue, 2 Apr 2019 09:35:01 -1100 Subject: [PATCH 685/787] 2/3 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d5f9c98f9..a926e3de0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2361,7 +2361,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i Date: Tue, 2 Apr 2019 09:38:09 -1100 Subject: [PATCH 686/787] 1:1 --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a926e3de0..9c9b3959e 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2312,7 +2312,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( (price= nonzprices[i]) != 0 ) { den += (daywindow - i); - sum += (daywindow - i) * ((price + firstprice) / 2); + sum += (daywindow - i) * price; n++; } } @@ -2361,7 +2361,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i Date: Tue, 2 Apr 2019 09:43:58 -1100 Subject: [PATCH 687/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 9c9b3959e..120267dad 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2364,7 +2364,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum += (daywindow - i) * correlated[i]; den += (daywindow - i); } - smoothed = (sum / den); + smoothed = ((sum / den) + firstprice) >> 1; } return(smoothed); } From 480349a221c09c65957e9becc61a75df6a4067e8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 09:49:48 -1100 Subject: [PATCH 688/787] 0.9 decay --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 120267dad..78d320c91 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2312,7 +2312,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( (price= nonzprices[i]) != 0 ) { den += (daywindow - i); - sum += (daywindow - i) * price; + sum += ((daywindow - i) * (price + firstprice*9)) / 10; n++; } } @@ -2361,10 +2361,10 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i> 1; + smoothed = (sum / den); } return(smoothed); } From 05bd8d8af63d51ea3ba11f4c7816f48b97f7bd7c Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 10:16:03 -1100 Subject: [PATCH 689/787] Smooth coifs --- src/komodo_gateway.h | 36 +++++++++++++++++++++++++++--------- src/rpc/blockchain.cpp | 11 ++++++----- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 78d320c91..afa3b40c1 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2312,7 +2312,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( (price= nonzprices[i]) != 0 ) { den += (daywindow - i); - sum += ((daywindow - i) * (price + firstprice*9)) / 10; + sum += ((daywindow - i) * (price + firstprice*2)) / 3; n++; } } @@ -2334,11 +2334,17 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int } -int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonzprices) +int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonzprices,int32_t smoothwidth) { - int32_t i; int64_t sum,den,smoothed=0,firstprice = correlated[0]; + const int64_t coeffs[7] = { 7, 13, 5, 4, 6, 7, 3 }; + int32_t i,iter; int64_t smoothedden,smoothedsum,sum,den,smoothed[7],firstprice = correlated[0]; if ( daywindow < 2 ) return(0); + if ( sizeof(smoothed)/sizeof(*smoothed) ) + { + fprintf(stderr,"smoothwidth %d != %d\n",smoothwidth,(int32_t)(sizeof(smoothed)/sizeof(*smoothed))); + return(0); + } memset(nonzprices,0,sizeof(*nonzprices)*daywindow); for (i=1; i %.4f\n",(double)(smoothedsum/smoothedden)/10000); + return(smoothedsum/smoothedden); } - return(smoothed); + return(0); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 0080c9b2c..520307c4b 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1183,6 +1183,7 @@ uint32_t komodo_heightstamp(int32_t height); UniValue prices(const UniValue& params, bool fHelp) { + int32_t smoothwidth = 7; if ( fHelp || params.size() != 1 ) throw runtime_error("prices maxsamples\n"); LOCK(cs_main); @@ -1197,7 +1198,7 @@ UniValue prices(const UniValue& params, bool fHelp) UniValue a(UniValue::VARR); if ( daywindow < 7 ) daywindow = 7; - width = maxsamples+2*daywindow; + width = maxsamples+2*daywindow+smoothwidth; numpricefeeds = komodo_heightpricebits(&seed,rawprices,nextheight-1); if ( numpricefeeds <= 0 ) throw JSONRPCError(RPC_INVALID_PARAMETER, "illegal numpricefeeds"); @@ -1205,7 +1206,7 @@ UniValue prices(const UniValue& params, bool fHelp) correlated = (int64_t *)calloc(sizeof(*correlated),width); correlated2 = (int64_t *)calloc(sizeof(*correlated2),width); prices2 = (uint32_t *)calloc(sizeof(*prices2),width); - for (ht=nextheight-1,i=0; i2*daywindow+2; i++,ht--) + for (ht=nextheight-1,i=0; i2*daywindow+2+smoothwidth; i++,ht--) { if ( ht < 0 || ht > chainActive.Height() ) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); @@ -1238,17 +1239,17 @@ UniValue prices(const UniValue& params, bool fHelp) if ( (str= komodo_pricename(name,j)) != 0 ) { item.push_back(Pair("name",str)); - for (i=0; i Date: Tue, 2 Apr 2019 10:17:38 -1100 Subject: [PATCH 690/787] Smooth width --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 520307c4b..e31f5d8a4 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1176,7 +1176,7 @@ UniValue paxprice(const UniValue& params, bool fHelp) int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); -int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices,int64_t *correlated2); +int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices,int64_t *correlated2,int32_t smoothwidth); int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices,uint32_t *rawprices2); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); From 778b70285efca1d9aab687f007644c638ae9be4b Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 10:19:24 -1100 Subject: [PATCH 691/787] Add condition --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index afa3b40c1..d0539426e 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2340,7 +2340,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz int32_t i,iter; int64_t smoothedden,smoothedsum,sum,den,smoothed[7],firstprice = correlated[0]; if ( daywindow < 2 ) return(0); - if ( sizeof(smoothed)/sizeof(*smoothed) ) + if ( smoothwidth != sizeof(smoothed)/sizeof(*smoothed) ) { fprintf(stderr,"smoothwidth %d != %d\n",smoothwidth,(int32_t)(sizeof(smoothed)/sizeof(*smoothed))); return(0); From 903455b07b0332e031c561e186615fae64d1ad7e Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 10:24:52 -1100 Subject: [PATCH 692/787] Gilt --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d0539426e..9543cc1b9 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2336,7 +2336,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonzprices,int32_t smoothwidth) { - const int64_t coeffs[7] = { 7, 13, 5, 4, 6, 7, 3 }; + const int64_t coeffs[7] = { 7, 7, 7, 7, 7, 7, 7 }; int32_t i,iter; int64_t smoothedden,smoothedsum,sum,den,smoothed[7],firstprice = correlated[0]; if ( daywindow < 2 ) return(0); @@ -2378,8 +2378,8 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz for (i=0; i<7; i++) { fprintf(stderr,"%.4f ",(double)smoothed[i]/10000); - smoothedsum += coeffs[i] * smoothed[i]; - smoothedden += coeffs[i]; + smoothedsum += (7-i) * smoothed[i]; + smoothedden += (7-i); } fprintf(stderr,"-> %.4f\n",(double)(smoothedsum/smoothedden)/10000); return(smoothedsum/smoothedden); From a5957af75a3170c0816866658656787bc5e56109 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 10:25:59 -1100 Subject: [PATCH 693/787] 1:1 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 9543cc1b9..8da2a94bf 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2369,7 +2369,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i Date: Tue, 2 Apr 2019 10:32:08 -1100 Subject: [PATCH 694/787] Test --- src/komodo_gateway.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 8da2a94bf..63e6898eb 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2254,8 +2254,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int correlation = 0; i = (iter + seed) % daywindow; refprice = rawprices[i]; - highprice = (refprice * (COIN + PRICES_MAXCHANGE*3)) / COIN; - lowprice = (refprice * (COIN - PRICES_MAXCHANGE*3)) / COIN; + highprice = (refprice * (COIN + PRICES_MAXCHANGE*4)) / COIN; + lowprice = (refprice * (COIN - PRICES_MAXCHANGE*4)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) @@ -2312,7 +2312,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( (price= nonzprices[i]) != 0 ) { den += (daywindow - i); - sum += ((daywindow - i) * (price + firstprice*2)) / 3; + sum += ((daywindow - i) * (price + firstprice*9)) / 10; n++; } } @@ -2369,7 +2369,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i Date: Tue, 2 Apr 2019 10:34:58 -1100 Subject: [PATCH 695/787] Arg --- src/komodo_gateway.h | 7 +++++-- src/rpc/blockchain.cpp | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 63e6898eb..0d4274ae5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2240,7 +2240,7 @@ char *komodo_pricename(char *name,int32_t ind) return(0); } -int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow,uint32_t *nonzprices) +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t daywindow,uint32_t *nonzprices,int32_t smoothwidth) { int32_t i,j,k,n,iter,correlation,maxcorrelation=0; int64_t firstprice,price,sum,den,mult,refprice,lowprice,highprice; if ( daywindow < 2 ) @@ -2249,7 +2249,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int mult = 10000; else mult = 1; memset(nonzprices,0,sizeof(*nonzprices)*daywindow); - for (iter=0; iter= daywindow ) i = 0; if ( (price= rawprices[i]) == 0 ) + { + fprintf(stderr,"null rawprice.[%d]\n",i); return(-1); + } if ( price >= lowprice && price <= highprice ) { //fprintf(stderr,"%.1f ",(double)price/10000); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index e31f5d8a4..6e6746a30 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1177,7 +1177,7 @@ UniValue paxprice(const UniValue& params, bool fHelp) int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices,int64_t *correlated2,int32_t smoothwidth); -int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices,uint32_t *rawprices2); +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices,uint32_t *rawprices2,int32_t smoothwidth); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); @@ -1243,7 +1243,7 @@ UniValue prices(const UniValue& params, bool fHelp) { offset = j*width + i; rngval = (rngval*11109 + 13849); - if ( (correlated[i]= komodo_pricecorrelated(rngval,j,&prices[offset],daywindow+smoothwidth,prices2)) < 0 ) + if ( (correlated[i]= komodo_pricecorrelated(rngval,j,&prices[offset],daywindow+smoothwidth,prices2,smoothwidth)) < 0 ) throw JSONRPCError(RPC_INVALID_PARAMETER, "null correlated price"); } for (i=0; i Date: Tue, 2 Apr 2019 10:38:42 -1100 Subject: [PATCH 696/787] -smoothwidth --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 6e6746a30..e471492ce 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1243,7 +1243,7 @@ UniValue prices(const UniValue& params, bool fHelp) { offset = j*width + i; rngval = (rngval*11109 + 13849); - if ( (correlated[i]= komodo_pricecorrelated(rngval,j,&prices[offset],daywindow+smoothwidth,prices2,smoothwidth)) < 0 ) + if ( (correlated[i]= komodo_pricecorrelated(rngval,j,&prices[offset],daywindow,prices2,smoothwidth)) < 0 ) throw JSONRPCError(RPC_INVALID_PARAMETER, "null correlated price"); } for (i=0; i Date: Tue, 2 Apr 2019 10:42:03 -1100 Subject: [PATCH 697/787] .9 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 0d4274ae5..28913c08e 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2372,7 +2372,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i Date: Tue, 2 Apr 2019 10:47:41 -1100 Subject: [PATCH 698/787] Test --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 28913c08e..6d6f8f155 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2372,7 +2372,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz sum = den = 0; for (i=0; i Date: Tue, 2 Apr 2019 10:53:33 -1100 Subject: [PATCH 699/787] Test --- src/komodo_gateway.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6d6f8f155..64fcbb957 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2339,7 +2339,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonzprices,int32_t smoothwidth) { - const int64_t coeffs[7] = { 7, 7, 7, 7, 7, 7, 7 }; + const int64_t coeffs[7] = { -1, 9, -45, 0, 45, -9, 1 }; // / 60 int32_t i,iter; int64_t smoothedden,smoothedsum,sum,den,smoothed[7],firstprice = correlated[0]; if ( daywindow < 2 ) return(0); @@ -2377,12 +2377,13 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz } smoothed[iter] = (sum / den); } - smoothedsum = smoothedden = 0; + smoothedsum = 0; + smoothedden = 60; for (i=0; i<7; i++) { fprintf(stderr,"%.4f ",(double)smoothed[i]/10000); - smoothedsum += (7-i) * smoothed[i]; - smoothedden += (7-i); + smoothedsum += coeffs[i] * smoothed[i]; + //smoothedden += (7-i); } fprintf(stderr,"-> %.4f\n",(double)(smoothedsum/smoothedden)/10000); return(smoothedsum/smoothedden); From 966fafb2700a4c5543e5381f9191cadf7e1e84f1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 10:54:20 -1100 Subject: [PATCH 700/787] Test --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 64fcbb957..6f72f855b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2339,7 +2339,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonzprices,int32_t smoothwidth) { - const int64_t coeffs[7] = { -1, 9, -45, 0, 45, -9, 1 }; // / 60 + const int64_t coeffs[7] = { -1, 9, -45, 1, 45, -9, 1 }; // / 60 int32_t i,iter; int64_t smoothedden,smoothedsum,sum,den,smoothed[7],firstprice = correlated[0]; if ( daywindow < 2 ) return(0); @@ -2378,7 +2378,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz smoothed[iter] = (sum / den); } smoothedsum = 0; - smoothedden = 60; + smoothedden = 1; for (i=0; i<7; i++) { fprintf(stderr,"%.4f ",(double)smoothed[i]/10000); From 79120f3cac81e208c97156640c05c9f0b73b7f6d Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 10:58:48 -1100 Subject: [PATCH 701/787] Test --- src/komodo_gateway.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6f72f855b..7507122f3 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2339,7 +2339,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonzprices,int32_t smoothwidth) { - const int64_t coeffs[7] = { -1, 9, -45, 1, 45, -9, 1 }; // / 60 + //const int64_t coeffs[7] = { -1, 9, -45, 1, 45, -9, 1 }; // / 60 + const int64_t coeffs[7] = { -2, 0, 18, 32, 18, 0, -2 }; int32_t i,iter; int64_t smoothedden,smoothedsum,sum,den,smoothed[7],firstprice = correlated[0]; if ( daywindow < 2 ) return(0); @@ -2378,7 +2379,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz smoothed[iter] = (sum / den); } smoothedsum = 0; - smoothedden = 1; + smoothedden = 64; for (i=0; i<7; i++) { fprintf(stderr,"%.4f ",(double)smoothed[i]/10000); From 8adcc5022c167ac1aed17181a1eabe2b7caa1bd8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 11:04:32 -1100 Subject: [PATCH 702/787] Test --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7507122f3..778f5b318 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2315,7 +2315,7 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( (price= nonzprices[i]) != 0 ) { den += (daywindow - i); - sum += ((daywindow - i) * (price + firstprice*9)) / 10; + sum += ((daywindow - i) * (price + firstprice*4)) / 5; n++; } } @@ -2382,11 +2382,11 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz smoothedden = 64; for (i=0; i<7; i++) { - fprintf(stderr,"%.4f ",(double)smoothed[i]/10000); + //fprintf(stderr,"%.4f ",(double)smoothed[i]/10000); smoothedsum += coeffs[i] * smoothed[i]; //smoothedden += (7-i); } - fprintf(stderr,"-> %.4f\n",(double)(smoothedsum/smoothedden)/10000); + //fprintf(stderr,"-> %.4f\n",(double)(smoothedsum/smoothedden)/10000); return(smoothedsum/smoothedden); } return(0); From 6cf121c5d5d1f9c82d399b2358ac2db6934933ef Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 11:18:19 -1100 Subject: [PATCH 703/787] Smooth64 --- src/komodo_gateway.h | 76 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 778f5b318..6659dcced 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2336,12 +2336,82 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int return(0); } +int64_t _pairave64(int64_t valA,int64_t valB) +{ + if ( valA != 0 && valB != 0 ) + return((valA + valB) / 2); + else if ( valA != 0 ) return(valA); + else return(valB); +} + +int64_t _pairdiff64(register int64_t valA,register int64_t valB) +{ + if ( valA != 0 && valB != 0 ) + return(valA - valB); + else return(0); +} + +int64_t balanced_ave64(int64_t buf[],int32_t i,int32_t width) +{ + register int32_t nonz,j; register int64_t sum,price; + nonz = 0; + sum = 0; + for (j=-width; j<=width; j++) + { + price = buf[i + j]; + if ( price != 0 ) + { + sum += price; + nonz++; + } + } + if ( nonz != 0 ) + sum /= nonz; + return(sum); +} + +void buf_trioave64(int64_t dest[],int64_t src[],int32_t n) +{ + register int32_t i,j,width = 3; + for (i=0; i<128; i++) + src[i] = 0; + //for (i=n-width-1; i>width; i--) + // dest[i] = balanced_ave(src,i,width); + //for (i=width; i>0; i--) + // dest[i] = balanced_ave(src,i,i); + for (i=1; i Date: Tue, 2 Apr 2019 11:19:38 -1100 Subject: [PATCH 704/787] smoothwidth --- src/komodo_gateway.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6659dcced..25b87d6e3 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2400,8 +2400,8 @@ void smooth64(int64_t dest[],int64_t src[],int32_t width,int32_t smoothiters) buf_trioave64(smoothbufA,src,width); for (i=0; i Date: Tue, 2 Apr 2019 11:20:39 -1100 Subject: [PATCH 705/787] Trioave64 --- src/komodo_gateway.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 25b87d6e3..b1dbfbfe7 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2403,7 +2403,7 @@ void smooth64(int64_t dest[],int64_t src[],int32_t width,int32_t smoothiters) buf_trioave64(smoothbufB,smoothbufA,width); buf_trioave64(smoothbufA,smoothbufB,width); } - buf_trioave(dest,smoothbufA,width); + buf_trioave64(dest,smoothbufA,width); } else memcpy(dest,src,width*sizeof(*dest)); } From 9b72450a1f2064c080ad86a6083b361be0e71afb Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 11:24:20 -1100 Subject: [PATCH 706/787] Revert --- src/komodo_gateway.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b1dbfbfe7..d16fa4bc5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2411,7 +2411,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz { //const int64_t coeffs[7] = { -1, 9, -45, 1, 45, -9, 1 }; // / 60 const int64_t coeffs[7] = { -2, 0, 18, 32, 18, 0, -2 }; - int32_t i,iter; int64_t dest[1440*3],orig[1440*3],smoothedden,smoothedsum,sum,den,smoothed[7],firstprice = correlated[0]; + int32_t i,iter; int64_t smoothedden,smoothedsum,sum,den,smoothed[7],firstprice = correlated[0]; if ( daywindow < 2 ) return(0); if ( smoothwidth != sizeof(smoothed)/sizeof(*smoothed) ) @@ -2438,19 +2438,19 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz correlated[i] = firstprice; else break; } - memcpy(orig,correlated,(daywindow+smoothwidth)*sizeof(*correlated)); + //memcpy(orig,correlated,(daywindow+smoothwidth)*sizeof(*correlated)); for (iter=0; iter Date: Tue, 2 Apr 2019 21:12:33 -1100 Subject: [PATCH 707/787] Fix localcache scan control flow --- src/komodo_gateway.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index d16fa4bc5..16a96b526 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1816,9 +1816,10 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i if ( PriceCache[j][i] >= prevbits[i] ) { fprintf(stderr,"i.%d within recent localprices[%d] %u >= %u\n",i,j,PriceCache[j][i],prevbits[i]); - continue; + break; } - break; + if ( j == KOMODO_LOCALPRICE_CACHESIZE ) + break; } else if ( maxflag < 0 && localbits[i] > prevbits[i] ) { @@ -1828,9 +1829,10 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i if ( PriceCache[j][i] <= prevbits[i] ) { fprintf(stderr,"i.%d within recent localprices[%d] %u <= prev %u\n",i,j,PriceCache[j][i],prevbits[i]); - continue; + break; } - break; + if ( j == KOMODO_LOCALPRICE_CACHESIZE ) + break; } } } From 4a704517b9d3cbafe5e841a61ef72cd12a310c42 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 21:17:05 -1100 Subject: [PATCH 708/787] +print --- src/cc/gamescc.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index b804216d7..2a8be815e 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -110,6 +110,7 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_settle(txfee,cp,params)); \ else \ { \ +fprintf(stderr,"invalid method (%s)\n",method); \ result.push_back(Pair("result","error")); \ result.push_back(Pair("error","invalid gamescc method")); \ return(result); \ From 1b6bb2d0a18aa58b22164345013e9dd7477da1bc Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 21:18:45 -1100 Subject: [PATCH 709/787] +print --- src/cc/cclib.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 00a7c6fe0..370b1b8e3 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -195,7 +195,7 @@ cJSON *cclib_reparse(int32_t *nump,char *jsonstr) // assumes origparams will be UniValue CClib_method(struct CCcontract_info *cp,char *method,char *jsonstr) { UniValue result(UniValue::VOBJ); uint64_t txfee = 10000; int32_t m; cJSON *params = cclib_reparse(&m,jsonstr); - //fprintf(stderr,"method.(%s) -> (%s)\n",jsonstr!=0?jsonstr:"",params!=0?jprint(params,0):""); + fprintf(stderr,"method.(%s) -> (%s)\n",jsonstr!=0?jsonstr:"",params!=0?jprint(params,0):""); #ifdef BUILD_ROGUE if ( cp->evalcode == EVAL_ROGUE ) { From 679d310c5edbd762cd59443f7ae3645afeed1d60 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 21:20:45 -1100 Subject: [PATCH 710/787] Fix crash on missing method --- src/cc/cclib.cpp | 4 ++-- src/cc/gamescc.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 370b1b8e3..8552cfa1b 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -351,7 +351,7 @@ UniValue CClib_info(struct CCcontract_info *cp) UniValue CClib(struct CCcontract_info *cp,char *method,char *jsonstr) { UniValue result(UniValue::VOBJ); int32_t i; std::string rawtx; cJSON *params; - //printf("CClib params.(%s)\n",jsonstr!=0?jsonstr:""); +//printf("CClib params.(%s)\n",jsonstr!=0?jsonstr:""); for (i=0; ievalcode == CClib_methods[i].evalcode && strcmp(method,CClib_methods[i].method) == 0 ) @@ -369,7 +369,7 @@ UniValue CClib(struct CCcontract_info *cp,char *method,char *jsonstr) } } result.push_back(Pair("result","error")); - result.push_back(Pair("method",CClib_methods[i].method)); + result.push_back(Pair("method",method)); result.push_back(Pair("error","method not found")); return(result); } diff --git a/src/cc/gamescc.h b/src/cc/gamescc.h index 2a8be815e..b804216d7 100644 --- a/src/cc/gamescc.h +++ b/src/cc/gamescc.h @@ -110,7 +110,6 @@ if ( cp->evalcode == EVAL_GAMES ) \ return(games_settle(txfee,cp,params)); \ else \ { \ -fprintf(stderr,"invalid method (%s)\n",method); \ result.push_back(Pair("result","error")); \ result.push_back(Pair("error","invalid gamescc method")); \ return(result); \ From 5d861c188abddd2c0db60fdf31008a51f52503ed Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 23:37:01 -1100 Subject: [PATCH 711/787] -ac_prices --- src/cc/pegs.cpp | 10 ++++++---- src/komodo_defs.h | 1 + src/komodo_gateway.h | 31 ++++++++++++++++++++----------- src/komodo_globals.h | 1 + src/komodo_utils.h | 7 +++++++ 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index c435d9920..dba9fc818 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -16,15 +16,17 @@ #include "CCPegs.h" /* - Pegs CC builds on top of Prices CC and would bind a pricefeed to a token bid/ask automated marketmaking. + Pegs CC builds on top of CCOPRET modes which will create pricefeeds in the coinbase. + flag&1: BTC/USD 4966.1400, BTC/GBP 3770.0402, BTC/EUR 4416.8452 GBPUSD 1.317264, EURUSD 1.124364 EURGBP 0.853560 + flag&2: (BGN 1.7462) (NZD 1.4771) (ILS 3.6258) (RUB 65.3700) (CAD 1.3320) (PHP 52.4160) (CHF 0.9995) (AUD 1.4122) (JPY 111.3660) (TRY 5.5483) (HKD 7.8498) (MYR 4.0816) (HRK 6.6337) (CZK 22.9937) (IDR 14220.0000) (DKK 6.6648) (NOK 8.6111) (HUF 287.2142) (GBP 0.7678) (MXN 19.1046) (THB 31.7500) (ISK 122.4107) (ZAR 14.1300) (BRL 3.8604) (SGD 1.3551) (PLN 3.8350) (INR 68.7531) (KRW 1136.3839) (RON 4.2516) (CNY 6.7200) (SEK 9.3230) (EUR 0.8928) (2019-04-02) +flag&4: (KMD 0.00025110) (ETH 0.03357500) (LTC 0.01642400) (BCHABC 0.05167400) (XMR 0.01398800) (IOTA 0.00007217) (DASH 0.02552600) (XEM 0.00001459) (ZEC 0.01440500) (WAVES 0.00062170) (RVN 0.00001215) (LSK 0.00041130) (DCR 0.00496000) (BTS 0.00001444) (ICX 0.00008750) (HOT 0.00000027) (STEEM 0.00010070) (ENJ 0.00003221) (STRAT 0.00022790) errs.0 + Funds deposited into CC address for a specific peg would then be used to fund the bid/ask as the pricefeed changes the price. Profits/losses would accumulate in the associated address. In the event funds exceed a specified level, it can be spent into a collection address. The idea is that the collection address can further be used for revshares. - int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); - - OraclePrice is very useful for pegs. + */ diff --git a/src/komodo_defs.h b/src/komodo_defs.h index b0f618e93..c1eeb375b 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -59,6 +59,7 @@ extern uint32_t ASSETCHAINS_VERUSHASH, ASSETCHAINS_VERUSHASHV1_1, ASSETCHAINS_NO extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern std::string NOTARY_PUBKEY,ASSETCHAINS_OVERRIDE_PUBKEY,ASSETCHAINS_SCRIPTPUB; extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_MARMARA; +extern std::vector ASSETCHAINS_PRICES; extern char ASSETCHAINS_SYMBOL[65]; extern int32_t VERUS_BLOCK_POSUNITS, VERUS_CONSECUTIVE_POS_THRESHOLD, VERUS_NOPOS_THRESHHOLD; diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 16a96b526..620ba1f9b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1553,7 +1553,6 @@ extern std::vector Mineropret; // opreturn data set by the data gatheri #define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR #define KOMODO_LOCALPRICE_CACHESIZE 7 - #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) const char *Cryptos[] = { "KMD", "ETH", "LTC", "BCHABC", "XMR", "IOTA", "DASH", "XEM", "ZEC", "WAVES", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" }; // must be on binance (for now) @@ -1563,6 +1562,7 @@ const char *Forex[] = }; // must be in ECB list uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex)]; +int32_t komodo_cbopretsize(uint64_t flags); void komodo_PriceCache_shift() { @@ -1722,7 +1722,7 @@ CScript komodo_mineropret(int32_t nHeight) The only way komodo_opretvalidate() doesnt return an error is if maxflag is set or it is within tolerance of both the prior block and the local data. The local data validation only happens if it is a recent block and not a block from the past as the local node is only getting the current price data. */ -// reconsiderblock 002aca768b09dfcf36bd934ab34b23983148b116e12cb0b6e1a3f895d1db63aa +// for PRICES: reconsiderblock 002aca768b09dfcf36bd934ab34b23983148b116e12cb0b6e1a3f895d1db63aa // and // reconsiderblock 0034cf582018eacc0b4ae001491ce460113514cb1a3f217567ef4a2207de361a // reconsiderbloc 000abf51c023b64af327c50c1b060797b8cb281c696d30ab92fd002a8b8c9aea @@ -1732,7 +1732,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - int32_t testchain_exemption = 500; + int32_t testchain_exemption = 0; std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { @@ -1753,7 +1753,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < -testchain_exemption ) // must be close to last block timestamp + if ( lag2 < -60 ) //testchain_exemption ) // must be close to last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); if ( nHeight > testchain_exemption ) @@ -2107,6 +2107,19 @@ int32_t get_btcusd(uint32_t pricebits[4]) // komodo_cbopretupdate() obtains the external price data and encodes it into Mineropret, which will then be used by the miner and validation // save history, use new data to approve past rejection, where is the auto-reconsiderblock? +int32_t komodo_cbopretsize(uint64_t flags) +{ + int32_t size = 0; + if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) + { + size = PRICES_SIZEBIT0; + if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) + size += sizeof(forexprices); + if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) + size += sizeof(cryptoprices); + } + return(size); +} void komodo_cbopretupdate(int32_t forceflag) { @@ -2123,13 +2136,9 @@ void komodo_cbopretupdate(int32_t forceflag) now = (uint32_t)time(NULL); if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) { -if ( komodo_nextheight() > 333 ) // for debug only! - ASSETCHAINS_CBOPRET = 7; - size = PRICES_SIZEBIT0; - if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) - size += sizeof(forexprices); - if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) - size += sizeof(cryptoprices); +//if ( komodo_nextheight() > 333 ) // for debug only! +// ASSETCHAINS_CBOPRET = 7; + size = komodo_cbopretsize(ASSETCHAINS_CBOPRET); if ( Mineropret.size() < size ) Mineropret.resize(size); size = PRICES_SIZEBIT0; diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 097d10da0..1c47e72e6 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -72,6 +72,7 @@ uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0,ASSETCHAIN uint64_t ASSETCHAINS_LASTERA = 1; uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS]; uint8_t ASSETCHAINS_CCDISABLES[256]; +std::vector ASSETCHAINS_PRICES; #define _ASSETCHAINS_EQUIHASH 0 uint32_t ASSETCHAINS_NUMALGOS = 3; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index a2eb3d5a1..a382e8c22 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1812,6 +1812,13 @@ void komodo_args(char *argv0) ASSETCHAINS_CODAPORT = GetArg("-ac_coda",0); ASSETCHAINS_MARMARA = GetArg("-ac_marmara",0); ASSETCHAINS_CBOPRET = GetArg("-ac_cbopret",0); + if ( ASSETCHAINS_CBOPRET != 0 ) + { + Split(GetArg("-ac_prices",""), ASSETCHAINS_PRICES, 0); + for (i=0; i Date: Tue, 2 Apr 2019 23:47:37 -1100 Subject: [PATCH 712/787] SplitStr --- src/komodo_gateway.h | 3 ++- src/komodo_utils.h | 5 +++-- src/util.cpp | 23 +++++++++++++++++++++++ 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 620ba1f9b..1d1379c15 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2109,7 +2109,8 @@ int32_t get_btcusd(uint32_t pricebits[4]) int32_t komodo_cbopretsize(uint64_t flags) { - int32_t size = 0; + int32_t size = 0; uint32_t cryptoprices[sizeof(Cryptos)/sizeof(*Cryptos)],forexprices[sizeof(Forex)/sizeof(*Forex)]; + if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) { size = PRICES_SIZEBIT0; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index a382e8c22..0d8b2ed63 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1665,6 +1665,7 @@ uint64_t komodo_ac_block_subsidy(int nHeight) extern int64_t MAX_MONEY; void komodo_cbopretupdate(int32_t forceflag); +void SplitStr(const std::string& strVal, std::vector outVals); void komodo_args(char *argv0) { @@ -1814,9 +1815,9 @@ void komodo_args(char *argv0) ASSETCHAINS_CBOPRET = GetArg("-ac_cbopret",0); if ( ASSETCHAINS_CBOPRET != 0 ) { - Split(GetArg("-ac_prices",""), ASSETCHAINS_PRICES, 0); + SplitStr(GetArg("-ac_prices",""), ASSETCHAINS_PRICES); for (i=0; i outVals) +{ + stringstream ss(strVal); + vector str; + + while ( ss.peek() == ' ' ) + ss.ignore(); + + while ( ss >> str ) + { + //outVals[numVals] = i; + outVals.push_back(i); + numVals += 1; + + while ( ss.peek() == ' ' ) + ss.ignore(); + if ( ss.peek() == ',' ) + ss.ignore(); + while ( ss.peek() == ' ' ) + ss.ignore(); + } +} + void Split(const std::string& strVal, uint64_t *outVals, const uint64_t nDefault) { stringstream ss(strVal); From 2c272b175d0924ea452da3104ed6e153a45bfa42 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 23:53:14 -1100 Subject: [PATCH 713/787] Str --- src/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.cpp b/src/util.cpp index 44e2c2dac..cca39ca38 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -405,7 +405,7 @@ void SplitStr(const std::string& strVal, std::vector outVals) while ( ss >> str ) { //outVals[numVals] = i; - outVals.push_back(i); + outVals.push_back(str); numVals += 1; while ( ss.peek() == ' ' ) From 7d4e16646ef3ba0c0e959323f4597bc966ffd6c6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 2 Apr 2019 23:54:51 -1100 Subject: [PATCH 714/787] -= --- src/util.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/util.cpp b/src/util.cpp index cca39ca38..ebc6dbccb 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -404,10 +404,7 @@ void SplitStr(const std::string& strVal, std::vector outVals) while ( ss >> str ) { - //outVals[numVals] = i; outVals.push_back(str); - numVals += 1; - while ( ss.peek() == ' ' ) ss.ignore(); if ( ss.peek() == ',' ) From cf437222feb010d555c4267d377789fb98bcccc0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 3 Apr 2019 00:03:17 -1100 Subject: [PATCH 715/787] Unsmooth --- src/komodo_gateway.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 1d1379c15..4f59141f5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2261,13 +2261,13 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int mult = 10000; else mult = 1; memset(nonzprices,0,sizeof(*nonzprices)*daywindow); - for (iter=0; iter (daywindow>>1) ) { + return(refprice * mult); //fprintf(stderr,"-> %.4f\n",(double)sum*mult/correlation); //return(sum*mult/correlation); n = 0; @@ -2458,8 +2459,8 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t daywindow,int64_t *nonz //smooth64(correlated+iter,dest,daywindow,1); for (i=0; i Date: Wed, 3 Apr 2019 00:06:05 -1100 Subject: [PATCH 716/787] Std::string --- src/util.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.cpp b/src/util.cpp index ebc6dbccb..4265ff759 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -397,7 +397,7 @@ void ParseParameters(int argc, const char* const argv[]) void SplitStr(const std::string& strVal, std::vector outVals) { stringstream ss(strVal); - vector str; + std::string str; while ( ss.peek() == ' ' ) ss.ignore(); From 5b9a4c353a8cfc9552c8afde59f849feb72d81b7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 3 Apr 2019 00:27:09 -1100 Subject: [PATCH 717/787] Revert incompatible changes --- src/komodo_gateway.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 4f59141f5..6862fcf14 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1732,7 +1732,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - int32_t testchain_exemption = 0; + int32_t testchain_exemption = 350; std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { @@ -1753,7 +1753,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < -60 ) //testchain_exemption ) // must be close to last block timestamp + if ( lag2 < -testchain_exemption ) // must be close to last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); if ( nHeight > testchain_exemption ) @@ -2137,8 +2137,8 @@ void komodo_cbopretupdate(int32_t forceflag) now = (uint32_t)time(NULL); if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) { -//if ( komodo_nextheight() > 333 ) // for debug only! -// ASSETCHAINS_CBOPRET = 7; +if ( komodo_nextheight() > 333 ) // for debug only! + ASSETCHAINS_CBOPRET = 7; size = komodo_cbopretsize(ASSETCHAINS_CBOPRET); if ( Mineropret.size() < size ) Mineropret.resize(size); From 43e0d2fec6f390fdd391e94c714f306097556252 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 3 Apr 2019 05:58:14 -1100 Subject: [PATCH 718/787] -ac_prices --- src/komodo_gateway.h | 98 ++++++++++++++++++++++++++---------------- src/komodo_utils.h | 13 +++++- src/rpc/blockchain.cpp | 5 ++- 3 files changed, 76 insertions(+), 40 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6862fcf14..b70574677 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1552,6 +1552,7 @@ extern std::vector Mineropret; // opreturn data set by the data gatheri #define PRICES_MAXCHANGE (COIN / 100) // maximum acceptable change, set at 1% #define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR #define KOMODO_LOCALPRICE_CACHESIZE 7 +#define KOMODO_MAXPRICES 2048 #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) @@ -1561,18 +1562,15 @@ const char *Forex[] = { "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" }; // must be in ECB list -uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex)]; +uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][KOMODO_MAXPRICES];//4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex)]; +int64_t PriceMult[KOMODO_MAXPRICES]; int32_t komodo_cbopretsize(uint64_t flags); void komodo_PriceCache_shift() { int32_t i; for (i=KOMODO_LOCALPRICE_CACHESIZE-1; i>0; i--) - { memcpy(PriceCache[i],PriceCache[i-1],sizeof(PriceCache[i])); - //for (j=0; j<4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex); j++) - // PriceCache[i][j] = PriceCache[i-1][j]; - } memcpy(PriceCache[0],Mineropret.data(),Mineropret.size()); } @@ -1657,7 +1655,7 @@ int32_t komodo_pricecmp(int32_t nHeight,int32_t n,char *maxflags,uint32_t *price // komodo_priceclamp() clamps any price that is beyond tolerance int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int64_t tolerance) { - int32_t i; uint32_t newprice; char maxflags[2048]; + int32_t i; uint32_t newprice; char maxflags[KOMODO_MAXPRICES]; memset(maxflags,0,sizeof(maxflags)); for (i=1; i= PRICES_SIZEBIT0 ) { n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); @@ -1727,13 +1725,11 @@ CScript komodo_mineropret(int32_t nHeight) // reconsiderblock 0034cf582018eacc0b4ae001491ce460113514cb1a3f217567ef4a2207de361a // reconsiderbloc 000abf51c023b64af327c50c1b060797b8cb281c696d30ab92fd002a8b8c9aea // are needed to sync past initial blocks with different data set -// pass in blockhash and nTime, latch if it is rejected due to local price, then if localprice changes in a way that would validate then issue reconsiderblock -// add rpc call for extracting rawprices int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - int32_t testchain_exemption = 350; - std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now = (uint32_t)time(NULL); + int32_t testchain_exemption = 0; + std::vector vopret; char maxflags[KOMODO_MAXPRICES]; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { GetOpReturnData(scriptPubKey,vopret); @@ -1753,7 +1749,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < -testchain_exemption ) // must be close to last block timestamp + if ( lag2 < -60 ) //testchain_exemption ) // must be close to last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); if ( nHeight > testchain_exemption ) @@ -2028,14 +2024,17 @@ uint32_t get_binanceprice(const char *symbol) return(price); } -int32_t get_cryptoprices(uint32_t *prices,const char *list[],int32_t n) +int32_t get_cryptoprices(uint32_t *prices,const char *list[],int32_t n,std::vector strvec) { - int32_t i,errs=0; uint32_t price; + int32_t i,errs=0; uint32_t price; char *symbol; for (i=0; i 333 ) // for debug only! - ASSETCHAINS_CBOPRET = 7; +//if ( komodo_nextheight() > 333 ) // for debug only! +// ASSETCHAINS_CBOPRET = 7; size = komodo_cbopretsize(ASSETCHAINS_CBOPRET); if ( Mineropret.size() < size ) Mineropret.resize(size); @@ -2160,19 +2158,19 @@ if ( komodo_nextheight() > 333 ) // for debug only! flags |= 2; memcpy(&PriceCache[0][size/sizeof(uint32_t)],forexprices,sizeof(forexprices)); } - size += sizeof(forexprices); + size += (sizeof(Forex)/sizeof(*Forex)) * sizeof(uint32_t); } if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) { if ( forceflag != 0 || flags != 0 ) { - get_cryptoprices(cryptoprices,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); + get_cryptoprices(cryptoprices,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos)),ASSETCHAINS_PRICES); if ( flags == 0 ) komodo_PriceCache_shift(); - memcpy(&PriceCache[0][size/sizeof(uint32_t)],cryptoprices,sizeof(cryptoprices)); + memcpy(&PriceCache[0][size/sizeof(uint32_t)],cryptoprices,(sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t)); flags |= 4; // very rarely we can see flags == 6 case } - size += sizeof(cryptoprices); + size += (sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t); } if ( flags != 0 ) { @@ -2205,10 +2203,35 @@ if ( komodo_nextheight() > 333 ) // for debug only! pending = 0; } +int64_t komodo_pricemult(int32_t ind) +{ + int32_t i,j; + if ( (ASSETCHAINS_CBOPRET & 1) != 0 && ind < KOMODO_MAXPRICES ) + { + if ( PriceMult[0] == 0 ) + { + for (i=0; i<4; i++) + PriceMult[i] = 10000; + if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) + { + for (j=0; j= KOMODO_MAXPRICES ) return(-1); - if ( ind < 36 ) - mult = 10000; - else mult = 1; + mult = PriceMult[ind]; memset(nonzprices,0,sizeof(*nonzprices)*daywindow); for (iter=0; iter outVals); void komodo_args(char *argv0) { extern const char *Notaries_elected1[][2]; - std::string name,addn,hexstr; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256]; + std::string name,addn,hexstr,symbol; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256]; IS_KOMODO_NOTARY = GetBoolArg("-notary", false); IS_STAKED_NOTARY = GetArg("-stakednotary", -1); if ( IS_STAKED_NOTARY != -1 && IS_KOMODO_NOTARY == true ) { @@ -1735,7 +1735,7 @@ void komodo_args(char *argv0) { printf("KOMODO_REWIND %d\n",KOMODO_REWIND); } - if ( name.c_str()[0] != 0 ) + if ( name.c_str()[0] != 0 ) { std::string selectedAlgo = GetArg("-ac_algo", std::string(ASSETCHAINS_ALGORITHMS[0])); @@ -2075,6 +2075,15 @@ void komodo_args(char *argv0) if ( ASSETCHAINS_CBOPRET != 0 ) { extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_CBOPRET),(void *)&ASSETCHAINS_CBOPRET); + if ( ASSETCHAINS_PRICES.size() != 0 ) + { + for (i=0; i Date: Wed, 3 Apr 2019 06:07:13 -1100 Subject: [PATCH 719/787] Move symbols --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index b70574677..8c976ebe1 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1556,8 +1556,8 @@ extern std::vector Mineropret; // opreturn data set by the data gatheri #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) -const char *Cryptos[] = { "KMD", "ETH", "LTC", "BCHABC", "XMR", "IOTA", "DASH", "XEM", "ZEC", "WAVES", "RVN", "LSK", "DCR", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" }; // must be on binance (for now) - +const char *Cryptos[] = { "KMD", "ETH", "LTC", "BCHABC", "XMR", "IOTA", "ZEC", "WAVES", "LSK", "DCR" }; // must be on binance (for now) +//"RVN", "DASH", "XEM", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" const char *Forex[] = { "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" }; // must be in ECB list From ddee092afbe481b075c4c5107eaad02d06822aa7 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 3 Apr 2019 06:08:56 -1100 Subject: [PATCH 720/787] Fix --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 8c976ebe1..a81a6c6f3 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2030,8 +2030,8 @@ int32_t get_cryptoprices(uint32_t *prices,const char *list[],int32_t n,std::vect for (i=0; i Date: Wed, 3 Apr 2019 06:10:27 -1100 Subject: [PATCH 721/787] "RVN", "DASH", "XEM", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" --- src/rpc/blockchain.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 7e7a44f7c..5f1d671e4 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1180,7 +1180,7 @@ int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices,int64_t *corr int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices,uint32_t *rawprices2,int32_t smoothwidth); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); -int64_t komodo_pricesmult(int32_t ind); +int64_t komodo_pricemult(int32_t ind); UniValue prices(const UniValue& params, bool fHelp) { @@ -1252,7 +1252,7 @@ UniValue prices(const UniValue& params, bool fHelp) offset = j*width + i; smoothed = komodo_pricesmoothed(&correlated[i],daywindow,correlated2,smoothwidth); UniValue parr(UniValue::VARR); - parr.push_back(ValueFromAmount((int64_t)prices[offset] * komodo_pricesmult(j))); + parr.push_back(ValueFromAmount((int64_t)prices[offset] * komodo_pricemult(j))); parr.push_back(ValueFromAmount(correlated[i])); parr.push_back(ValueFromAmount(smoothed)); p.push_back(parr); From 02938d0a127484a3e2b979fd09c8a7d57898242c Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 3 Apr 2019 06:13:37 -1100 Subject: [PATCH 722/787] Test --- src/komodo_utils.h | 2 +- src/util.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_utils.h b/src/komodo_utils.h index c807eff48..2d0fe4f4e 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1665,7 +1665,7 @@ uint64_t komodo_ac_block_subsidy(int nHeight) extern int64_t MAX_MONEY; void komodo_cbopretupdate(int32_t forceflag); -void SplitStr(const std::string& strVal, std::vector outVals); +void SplitStr(const std::string& strVal, std::vector &outVals); void komodo_args(char *argv0) { diff --git a/src/util.cpp b/src/util.cpp index 4265ff759..b8733ffca 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -394,7 +394,7 @@ void ParseParameters(int argc, const char* const argv[]) } } -void SplitStr(const std::string& strVal, std::vector outVals) +void SplitStr(const std::string& strVal, std::vector &outVals) { stringstream ss(strVal); std::string str; From cd16c88711502ce727863730fb39b209e363a62a Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 3 Apr 2019 06:22:53 -1100 Subject: [PATCH 723/787] Fix --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index a81a6c6f3..28ed3ee3a 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1556,8 +1556,8 @@ extern std::vector Mineropret; // opreturn data set by the data gatheri #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) -const char *Cryptos[] = { "KMD", "ETH", "LTC", "BCHABC", "XMR", "IOTA", "ZEC", "WAVES", "LSK", "DCR" }; // must be on binance (for now) -//"RVN", "DASH", "XEM", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" +const char *Cryptos[] = { "KMD", "ETH" }; // must be on binance (for now) +// "LTC", "BCHABC", "XMR", "IOTA", "ZEC", "WAVES", "LSK", "DCR", "RVN", "DASH", "XEM", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" const char *Forex[] = { "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" }; // must be in ECB list @@ -2027,7 +2027,7 @@ uint32_t get_binanceprice(const char *symbol) int32_t get_cryptoprices(uint32_t *prices,const char *list[],int32_t n,std::vector strvec) { int32_t i,errs=0; uint32_t price; char *symbol; - for (i=0; i Date: Wed, 3 Apr 2019 06:37:57 -1100 Subject: [PATCH 724/787] Remove , --- src/util.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/util.cpp b/src/util.cpp index b8733ffca..980d82ac9 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -404,6 +404,10 @@ void SplitStr(const std::string& strVal, std::vector &outVals) while ( ss >> str ) { + if ( str.size() == 0 ) + continue; + if ( str[str.size()-1] == ',' ) + str.resize(str.size()-1); outVals.push_back(str); while ( ss.peek() == ' ' ) ss.ignore(); From 9b565ea675e39a68214e51ee64cd992d38f81305 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 3 Apr 2019 07:00:43 -1100 Subject: [PATCH 725/787] Handle early block prices --- src/rpc/blockchain.cpp | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 5f1d671e4..ce27238a9 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1188,7 +1188,7 @@ UniValue prices(const UniValue& params, bool fHelp) if ( fHelp || params.size() != 1 ) throw runtime_error("prices maxsamples\n"); LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t seed,rngval; int64_t smoothed,*correlated,*correlated2; char name[64],*str; uint32_t rawprices[1440*6],*prices,*prices2; uint32_t i,width,j,numpricefeeds=-1,n,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; + UniValue ret(UniValue::VOBJ); uint64_t seed,rngval; int64_t smoothed,*correlated,*correlated2; char name[64],*str; uint32_t rawprices[1440*6],*prices,*prices2; uint32_t i,width,j,numpricefeeds=-1,n,num,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; if ( ASSETCHAINS_CBOPRET == 0 ) throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); @@ -1225,6 +1225,7 @@ UniValue prices(const UniValue& params, bool fHelp) } else throw JSONRPCError(RPC_INVALID_PARAMETER, "no komodo_rawprices found"); } } + num = i; ret.push_back(Pair("firstheight", (int64_t)nextheight-1-i)); UniValue timestamps(UniValue::VARR); for (i=0; i= width ) { - offset = j*width + i; - rngval = (rngval*11109 + 13849); - if ( (correlated[i]= komodo_pricecorrelated(rngval,j,&prices[offset],daywindow,prices2,smoothwidth)) < 0 ) - throw JSONRPCError(RPC_INVALID_PARAMETER, "null correlated price"); + for (i=0; i Date: Wed, 3 Apr 2019 07:01:10 -1100 Subject: [PATCH 726/787] Parr -> p --- src/rpc/blockchain.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index ce27238a9..61ea71476 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1266,9 +1266,7 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Wed, 3 Apr 2019 07:02:44 -1100 Subject: [PATCH 727/787] numsamples --- src/rpc/blockchain.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 61ea71476..18927ce8b 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1188,7 +1188,7 @@ UniValue prices(const UniValue& params, bool fHelp) if ( fHelp || params.size() != 1 ) throw runtime_error("prices maxsamples\n"); LOCK(cs_main); - UniValue ret(UniValue::VOBJ); uint64_t seed,rngval; int64_t smoothed,*correlated,*correlated2; char name[64],*str; uint32_t rawprices[1440*6],*prices,*prices2; uint32_t i,width,j,numpricefeeds=-1,n,num,nextheight,offset,ht,num=0,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; + UniValue ret(UniValue::VOBJ); uint64_t seed,rngval; int64_t smoothed,*correlated,*correlated2; char name[64],*str; uint32_t rawprices[1440*6],*prices,*prices2; uint32_t i,width,j,numpricefeeds=-1,n,numsamples,nextheight,offset,ht,daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; if ( ASSETCHAINS_CBOPRET == 0 ) throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); @@ -1225,7 +1225,7 @@ UniValue prices(const UniValue& params, bool fHelp) } else throw JSONRPCError(RPC_INVALID_PARAMETER, "no komodo_rawprices found"); } } - num = i; + numsamples = i; ret.push_back(Pair("firstheight", (int64_t)nextheight-1-i)); UniValue timestamps(UniValue::VARR); for (i=0; i= width ) + if ( numsamples >= width ) { - for (i=0; i Date: Wed, 3 Apr 2019 07:07:25 -1100 Subject: [PATCH 728/787] Test --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 18927ce8b..e7893a458 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1266,7 +1266,7 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Wed, 3 Apr 2019 07:10:39 -1100 Subject: [PATCH 729/787] -) --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index e7893a458..fe4d71bf4 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1266,7 +1266,7 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Wed, 3 Apr 2019 07:13:40 -1100 Subject: [PATCH 730/787] Nested --- src/rpc/blockchain.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index fe4d71bf4..11c8067f7 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1266,7 +1266,9 @@ UniValue prices(const UniValue& params, bool fHelp) for (i=0; i Date: Wed, 3 Apr 2019 07:16:02 -1100 Subject: [PATCH 731/787] Ht limit --- src/rpc/blockchain.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 11c8067f7..0b0f0030c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1207,7 +1207,8 @@ UniValue prices(const UniValue& params, bool fHelp) correlated = (int64_t *)calloc(sizeof(*correlated),width); correlated2 = (int64_t *)calloc(sizeof(*correlated2),width); prices2 = (uint32_t *)calloc(sizeof(*prices2),width); - for (ht=nextheight-1,i=0; i2*daywindow+2+smoothwidth; i++,ht--) + i = 0; + for (ht=nextheight-1,i=0; i2; i++,ht--) { if ( ht < 0 || ht > chainActive.Height() ) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); From bf4a8750f3a18c2cdf814ac6c89e28b585d3c32b Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 3 Apr 2019 20:44:46 -1100 Subject: [PATCH 732/787] Change correlation factor to 4 --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6862fcf14..7076f7808 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2266,8 +2266,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int correlation = 0; i = (iter + seed) % daywindow; refprice = rawprices[i]; - highprice = (refprice * (COIN + PRICES_MAXCHANGE*3)) / COIN; - lowprice = (refprice * (COIN - PRICES_MAXCHANGE*3)) / COIN; + highprice = (refprice * (COIN + PRICES_MAXCHANGE*4)) / COIN; + lowprice = (refprice * (COIN - PRICES_MAXCHANGE*4)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) From 9561e30a283b42def4621ad3e3c94fab8cc52cd4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 3 Apr 2019 20:48:38 -1100 Subject: [PATCH 733/787] 5% --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 7076f7808..e42b8aa4b 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2266,8 +2266,8 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int correlation = 0; i = (iter + seed) % daywindow; refprice = rawprices[i]; - highprice = (refprice * (COIN + PRICES_MAXCHANGE*4)) / COIN; - lowprice = (refprice * (COIN - PRICES_MAXCHANGE*4)) / COIN; + highprice = (refprice * (COIN + PRICES_MAXCHANGE*5)) / COIN; + lowprice = (refprice * (COIN - PRICES_MAXCHANGE*5)) / COIN; if ( highprice == refprice ) highprice++; if ( lowprice == refprice ) From 3155223e5176b534eea98b4448fb81954a453de3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 5 Apr 2019 21:17:53 -1100 Subject: [PATCH 734/787] Compiler errors --- src/cc/CCPrices.h | 22 +- src/cc/payments.cpp | 72 ++++ src/cc/prices.cpp | 835 +++++++++++++++++++++++++---------------- src/komodo_defs.h | 1 + src/komodo_gateway.h | 117 +++--- src/rpc/blockchain.cpp | 69 +++- src/txdb.cpp | 109 +++++- 7 files changed, 800 insertions(+), 425 deletions(-) diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 1a68be7dd..9f3039a6b 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -18,17 +18,27 @@ #define CC_PRICES_H #include "CCinclude.h" +int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind,int32_t daywindow); + +#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +#define PRICES_TXFEE 10000 +#define PRICES_SMOOTHWIDTH 1 +#define KOMODO_MAXPRICES 2048 // must be power of 2 and less than 8192 +#define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) +#define PRICES_WEIGHT (KOMODO_MAXPRICES * 1) +#define PRICES_MUL (KOMODO_MAXPRICES * 2) +#define PRICES_DIV (KOMODO_MAXPRICES * 3) +#define PRICES_INV (KOMODO_MAXPRICES * 4) +#define PRICES_MDD (KOMODO_MAXPRICES * 5) +#define PRICES_MMD (KOMODO_MAXPRICES * 6) +#define PRICES_MMM (KOMODO_MAXPRICES * 7) +#define PRICES_DDD (KOMODO_MAXPRICES * 8) bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom UniValue PricesList(); -UniValue PricesInfo(uint256 fundingtxid); -UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid); -std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys); -std::string PricesAddFunding(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,int64_t amount); -std::string PricesBet(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,int64_t amount,int32_t leverage); -std::string PricesFinish(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,uint256 bettxid); +UniValue PricesBet(uint64_t txfee,int64_t amount,int32_t leverage,std::string synthetic); #endif diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index e0c4c8ada..ae83e2661 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -732,6 +732,78 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) return(result); } +UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) +{ + // need to code: exclude list of tokenid, dust threshold, maxpayees, excluded pubkeys[] + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,lockedblocks,minrelease; std::string rawtx; int64_t totalallocations = 0; + cJSON *params = payments_reparse(&n,jsonstr); + if ( params != 0 && n >= 4 ) + { + lockedblocks = juint(jitem(params,0),0); + minrelease = juint(jitem(params,1),0); + if ( lockedblocks < 0 || minrelease < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } + for (i=0; i scriptPubKey,opret; int64_t allocation; + if ( myGetTransaction(txidoprets[i],tx,hashBlock) != 0 && tx.vout.size() > 1 && DecodePaymentsTxidOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + totalallocations += allocation; + if ( opret.size() > 0 ) + numoprets++; + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid txidopret")); + result.push_back(Pair("txid",txidoprets[i].GetHex())); + result.push_back(Pair("txi",(int64_t)i)); + if ( params != 0 ) + free_json(params); + return(result); + } + } + if ( numoprets > 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many opreturns")); + result.push_back(Pair("numoprets",(int64_t)numoprets)); + if ( params != 0 ) + free_json(params); + return(result); + } + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(lockedblocks,minrelease,totalallocations,txidoprets)); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,1)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough normal funds")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,numoprets=0,lockedblocks,minrelease; std::vector txidoprets; int64_t funds,fundsopret,totalallocations=0,allocation; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 1ab5aa1cb..eab975acd 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -17,123 +17,115 @@ #include "CCPrices.h" /* - Prices CC would best build on top of the oracles CC, ie. to combine payments for multiple oracles and to calculate a 51% protected price feed. +CBOPRET creates trustless oracles, which can be used for making a synthetic cash settlement system based on real world prices; - We need to assume there is an oracle for a specific price. In the event there are more than one provider, the majority need to be within correlation distance to update a pricepoint. - - int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); - - Using the above function, a consensus price can be obtained for a datasource. + 0.5% fee based on betamount, NOT leveraged betamount!! + 0.1% collected by price basis determinant + 0.2% collected by rekt tx - given an oracletxid, the marketaddr and format can be extracted to be used for future calls to OraclePrice. This allows to set a starting price and that in turn allows cash settled leveraged trading! + PricesBet -> +/-leverage, amount, synthetic -> opreturn includes current price + funds are locked into 1of2 global CC address + for first day, long basis is MAX(correlated,smoothed), short is MIN() + reference price is the smoothed of the previous block + if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price + original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund + 0.5% of bet -> globalfund + + PricesStatus -> bettxid maxsamples returns initial params, cost basis, amount left, rekt:true/false, rektheight, initial synthetic price, current synthetic price, net gain - Funds work like with dice, ie. there is a Prices plan that traders bet against. + PricesRekt -> bettxid height -> 0.1% to miner, rest to global CC - PricesFunding oracletxid, margin, priceaveraging, maxleverage, funding, longtoken, shorttoken, N [pubkeys] + PricesClose -> bettxid returns (synthetic value + amount) - PricesBet -> oracletxid start with 'L', leverage, funding, direction - funds are locked into global CC address - it can be closed at anytime by the trader for cash settlement - the house account can close it if rekt + PricesList -> all bettxid -> list [bettxid, netgain] - Implementation Notes: - In order to eliminate the need for worrying about sybil attacks, each prices plan would be able to specific pubkey(s?) for whitelisted publishers. It would be possible to have a non-whitelisted plan that would use 50% correlation between publishers. - - delta neutral balancing of riskexposure: fabs(long exposure - short exposure) - bet +B at leverage L - absval(sum(+BLi) - sum(-Bli)) - - validate: update riskexposure and it needs to be <= funds - - PricesProfits: limit withdraw to funds in excess of riskexposure - PricesFinish: payout (if winning) and update riskexposure - need long/short exposure assets - - funding -> 1of2 CC global CC address and dealer address, exposure tokens to global 1of2 assets CC address - pricebet -> user funds and exposure token to 1of2 address. - pricewin -> winnings from dealer funds, exposure token back to global address - priceloss -> exposuretoken back to global address - - exposure address, funds address - - */ // start of consensus code -int64_t PricesOraclePrice(int64_t &rektprice,uint64_t mode,uint256 oracletxid,std::vectorpubkeys,int32_t dir,int64_t amount,int32_t leverage) -{ - int64_t price; - // howto ensure price when block it confirms it not known - // get price from oracle + current chaintip - // normalize leveraged amount - if ( dir > 0 ) - rektprice = price * leverage / (leverage-1); - else rektprice = price * (leverage-1) / leverage; - return(price); -} - -CScript EncodePricesFundingOpRet(uint8_t funcid,CPubKey planpk,uint256 oracletxid,uint256 longtoken,uint256 shorttoken,int32_t millimargin,uint64_t mode,int32_t maxleverage,std::vector pubkeys,uint256 bettoken) +CScript prices_betopret(CPubKey mypk,int32_t height,int64_t amount,int16_t leverage,int64_t firstprice,std::vector vec,uint256 tokenid) { CScript opret; - fprintf(stderr,"implement EncodePricesFundingOpRet\n"); + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'B' << mypk << height << amount << leverage << firstprice << vec << tokenid); return(opret); } -uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &oracletxid,uint256 &longtoken,uint256 &shorttoken,int32_t &millimargin,uint64_t &mode,int32_t &maxleverage,std::vector &pubkeys,uint256 &bettoken) +uint8_t prices_betopretdecode(CScript scriptPubKey,CPubKey &pk,int32_t &height,int64_t &amount,int16_t &leverage,int64_t &firstprice,std::vector &vec,uint256 &tokenid) { - fprintf(stderr,"implement DecodePricesFundingOpRet\n"); + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> height; ss >> amount; ss >> leverage; ss >> firstprice; ss >> vec; ss >> tokenid) != 0 && e == EVAL_PRICES && f == 'B' ) + { + return(f); + } + return(0); +} + +CScript prices_addopret(uint256 bettxid,CPubKey mypk,int64_t amount) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'A' << bettxid << mypk << amount); + return(opret); +} + +uint8_t prices_addopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int64_t &amount) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> pk; ss >> amount) != 0 && e == EVAL_PRICES && f == 'A' ) + { + return(f); + } + return(0); +} + +CScript prices_costbasisopret(uint256 bettxid,CPubKey mypk,int32_t height,int64_t costbasis) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'C' << bettxid << mypk << height << costbasis); + return(opret); +} + +uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int32_t &height,int64_t &costbasis) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> pk; ss >> height; ss >> costbasis) != 0 && e == EVAL_PRICES && f == 'C' ) + { + return(f); + } + return(0); +} + +CScript prices_finalopret(uint256 bettxid,int64_t profits,int32_t height,CPubKey mypk,int64_t firstprice,int64_t costbasis,int64_t addedbets,int64_t positionsize,int16_t leverage) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'F' << bettxid << profits << height << mypk << firstprice << costbasis << addedbets << positionsize << leverage); + return(opret); +} + +uint8_t prices_finalopretdecode(CScript scriptPubKey,uint256 &bettxid,int64_t &profits,int32_t &height,CPubKey &pk,int64_t &firstprice,int64_t &costbasis,int64_t &addedbets,int64_t &positionsize,int16_t &leverage) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> profits; ss >> height; ss >> pk; ss >> firstprice; ss >> costbasis; ss >> addedbets; ss >> positionsize; ss >> leverage) != 0 && e == EVAL_PRICES && f == 'F' ) + { + return(f); + } return(0); } bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - - return true; // TODO remove, for test dual-evals - - return eval->Invalid("no validation yet"); - std::vector > txids; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else - { - for (i=0; iInvalid("illegal normal vini"); - } - } - //fprintf(stderr,"check amounts\n"); - //if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) - { - fprintf(stderr,"Pricesget invalid amount\n"); - return false; - } - //else - { - txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"Pricesget validated\n"); - else fprintf(stderr,"Pricesget invalid\n"); - return(retval); - } - } + return true; } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp -int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,uint256 tolenid,int64_t total,int32_t maxinputs) +int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,int64_t total,int32_t maxinputs,uint256 vintxid,int32_t vinvout) { - // add threshold check int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; SetCCunspents(unspentOutputs,destaddr); @@ -141,11 +133,11 @@ int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char { txid = it->first.txhash; vout = (int32_t)it->first.index; - // need to prevent dup + if ( vout == vinvout && txid == vintxid ) + continue; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) { - // need to verify assetid - if ( (nValue= vintx.vout[vout].nValue) >= 10000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if ( (nValue= vintx.vout[vout].nValue) >= total/maxinputs && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -162,7 +154,7 @@ int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char UniValue PricesList() { - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint64_t mode; int32_t margin,maxleverage; std::vectorpubkeys; uint256 txid,hashBlock,oracletxid,longtoken,shorttoken,bettoken; CPubKey planpk,pricespk; char str[65]; CTransaction vintx; + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; cp = CCinit(&C,EVAL_PRICES); pricespk = GetUnspendable(cp,0); SetCCtxids(addressIndex,cp->normaladdr); @@ -171,7 +163,7 @@ UniValue PricesList() txid = it->first.txhash; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) + if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec) == 'B' ) { result.push_back(uint256_str(str,txid)); } @@ -180,274 +172,455 @@ UniValue PricesList() return(result); } -// longtoken satoshis limits long exposure -// shorttoken satoshis limits short exposure -// both must be in the 1of2 CC address with its total supply -// bettoken -std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys) +UniValue prices_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) { - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction oracletx; int64_t fullsupply,inputs,CCchange=0; uint256 hashBlock; char str[65],coinaddr[64],houseaddr[64]; CPubKey mypk,pricespk; int32_t i,N,numvouts; struct CCcontract_info *cp,C; - if ( funding < 100*COIN || maxleverage <= 0 || maxleverage > 10000 ) + CTransaction tx; + if ( rawtx.size() > 0 ) { - CCerror = "invalid parameter error"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( (N= (int32_t)pubkeys.size()) || N > 15 ) - { - fprintf(stderr,"too many pubkeys N.%d\n",N); - return(""); - } - for (i=0; i 0 ) - { - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',mypk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken))); - } - else - { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - return(""); + if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); } -UniValue PricesInfo(uint256 fundingtxid) +int32_t prices_syntheticvec(std::vector &vec,std::vector synthetic) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CPubKey pricespk,planpk; uint256 hashBlock,oracletxid,longtoken,shorttoken,bettoken; CTransaction vintx; int64_t balance,supply,exposure; uint64_t funding,mode; int32_t i,margin,maxleverage; char numstr[65],houseaddr[64],exposureaddr[64],str[65]; std::vectorpubkeys; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - if ( GetTransaction(fundingtxid,vintx,hashBlock,false) == 0 ) + int32_t i,need,depth = 0; std::string opstr; uint16_t opcode,weight; + if ( synthetic.size() == 0 ) + return(-1); + for (i=0; i= 0 ) + opcode = ind, need = 0; + else if ( (weight= atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES ) + { + opcode = PRICES_WEIGHT | weight; + need = 1; + } else return(-2); + if ( depth < need ) + return(-3); + depth -= need; + if ( (opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT ) // weight + depth++; + if ( depth > 3 ) + return(-4); + vec.push_back(opcode); + } + if ( depth != 0 ) + { + fprintf(stderr,"depth.%d not empty\n",depth); + return(-5); + } + return(0); +} + +int64_t prices_syntheticprice(std::vector vec,int32_t height,int32_t minmax,int16_t leverage) +{ + int32_t i,errcode,depth,retval = -1; uint16_t opcode; int64_t *pricedata,stack[4],price,den,a,b,c; + pricedata = (int64_t *)calloc(sizeof(*pricedata)*3,numblocks + PRICES_DAYWINDOW*2 + PRICES_SMOOTHWIDTH); + price = den = depth = errcode = 0; + for (i=0; i 0 ) + pricestack[depth] = MAX(pricedata[1],pricedata[2]); + else pricestack[depth] = MIN(pricedata[1],pricedata[2]); + } + } + if ( pricestack[depth] == 0 ) + errcode = -1; + depth++; + break; + case PRICES_WEIGHT: // multiply by weight and consume top of stack by updating price + if ( depth == 1 ) + { + depth--; + price += pricestack[0] * ind; + den += ind; + } else errcode = -2; + break; + case PRICES_MUL: + if ( depth >= 2 ) + { + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (a * b) / SATOSHIDEN; + } else errcode = -3; + break; + case PRICES_DIV: + if ( depth >= 2 ) + { + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (a * SATOSHIDEN) / b; + } else errcode = -4; + break; + case PRICES_INV: + if ( depth >= 1 ) + { + a = pricestack[--depth]; + pricestack[depth++] = (SATOSHIDEN * SATOSHIDEN) / a; + } else errcode = -5; + break; + case PRICES_MDD: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (((a * SATOSHIDEN) / b) * SATOSHIDEN) / c; + } else errcode = -6; + break; + case PRICES_MMD: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (a * b) / c; + } else errcode = -7; + break; + case PRICES_MMM: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = ((a * b) / SATOSHIDEN) * c; + } else errcode = -8; + break; + case PRICES_DDD: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (((((SATOSHIDEN * SATOSHIDEN) / a) * SATOSHIDEN) / b) * SATOSHIDEN) / c; + } else errcode = -9; + break; + default: + errcode = -10; + break; + } + if ( errcode != 0 ) + break; + } + free(pricedata); + if ( den == 0 ) + return(-11); + else if ( depth != 0 ) + return(-12); + else if ( errcode != 0 ) + return(errcode); + return(price / den); +} + +int64_t prices_syntheticprofits(int64_t &costbasis,int32_t firstheight,int32_t height,int16_t leverage,std::vector vec,int64_t positionsize,int64_t addedbets) +{ + int64_t price,profits = 0; int32_t minmax; + minmax = (height > firstheight+PRICES_DAYWINDOW); + if ( (price= prices_syntheticprice(vec,height,minmax)) < 0 ) + { + fprintf(stderr,"unexpected zero synthetic price at height.%d\n",height); + return(0); + } + if ( minmax != 0 ) + { + if ( leverage > 0 && price > costbasis ) + costbasis = price; + else if ( leverage < 0 && (costbasis == 0 || price < costbasis) ) + costbasis = price; + } + profits = ((price * SATOSHIDEN) / costbasis) - SATOSHIDEN; + profits *= leverage * positionsize; + return(positionsize + addedbets + profits) +} + +void prices_betjson(UniValue &result,int64_t profits,int64_t costbasis,int64_t positionsize,int64_t addedbets,int16_t leverage,int32_t firstheight,int64_t firstprice) +{ + result.push_back(Pair("profits",ValueFromAmount(profits))); + result.push_back(Pair("costbasis",ValueFromAmount(costbasis))); + result.push_back(Pair("positionsize",ValueFromAmount(positionsize))); + result.push_back(Pair("addedbets",ValueFromAmount(addedbets))); + result.push_back(Pair("leverage",(int64_t)leverage)); + result.push_back(Pair("firstheight",(int64_t)firstheight)); + result.push_back(Pair("firstprice",ValueFromAmount(firstprice))); +} + +int64_t prices_costbasis(CTransaction bettx) +{ + int64_t costbasis = 0; + // if vout1 is spent, follow and extract costbasis from opreturn + //uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int32_t &height,int64_t &costbasis) + + return(costbasis); +} + +int64_t prices_batontxid(uint256 &batontxid,CTransaction bettx,uint256 bettxid); +{ + int64_t addedbets = 0; + // iterate through batons, adding up vout1 -> addedbets + return(addedbets); +} + +UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t betamount,firstprice; std::vector vec; + if ( leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","leverage too big")); return(result); } - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("fundingtxid",uint256_str(str,fundingtxid))); - result.push_back(Pair("bettoken",uint256_str(str,bettoken))); - result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); - sprintf(numstr,"%.3f",(double)margin/1000); - result.push_back(Pair("profitmargin",numstr)); - result.push_back(Pair("maxleverage",maxleverage)); - result.push_back(Pair("mode",(int64_t)mode)); - for (i=0; ipubkeys; - if ( amount < 10000 ) - { - CCerror = "amount must be positive"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) - txfee = 10000; + txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); pricespk = GetUnspendable(cp,0); GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) + if ( prices_syntheticvec(vec,synthetic) < 0 || (firstprice= prices_syntheticprice(vec,nextheight-1,1)) < 0 || vec.size() == 0 || vec.size() > 4096 ) { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid synthetic")); + return(result); } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) + if ( AddNormalinputs(mtx,mypk,amount+4*txfee,64) >= amount+4*txfee ) { - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,60)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(planpk)) << OP_CHECKSIG)); - if ( inputs > amount+txfee ) - CCchange = (inputs - amount); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); - // add addr2 + betamount = (amount * 199) / 200; + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding + mtx.vout.push_back(MakeCC1vout(cp->evalcode,(amount-betamount)+2*txfee,pricespk)); + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,betamount,pricespk,mypk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_betopret(mypk,nextheight-1,amount,leverage,firstprice,vec,zeroid)); + return(prices_rawtxresult(result,rawtx,0)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + return(result); +} - std::vector voutTokenPubkeysEmpty; //TODO: add token vout pubkeys - return(FinalizeCCTx(0,cp,mtx,mypk,txfee, - EncodeTokenOpRet(bettoken, voutTokenPubkeysEmpty, - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('t',/*bettoken,*/zeroid, 0, Mypubkey()))))); +UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batonttxid; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( AddNormalinputs(mtx,mypk,amount+txfee,64) >= amount+txfee ) + { + if ( prices_batontxid(addedbets,batontxid,bettxid) >= 0 ) + { + mtx.vin.push_back(CTxVin(batontxid,0,CScript())); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_addopret(bettxid,mypk,amount)); + return(prices_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find batonttxid")); + return(result); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + return(result); +} + +UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec) == 'B' ) + { + addedbets = prices_addedbets(bettx); + mtx.vin.push_back(CTxVin(bettx,1,CScript())); + for (i=0; ievalcode,bettx.vout[1].nValue-myfee-txfee,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_costbasisopret(bettxid,mypk,firstheight+PRICES_DAYWINDOW-1,costbasis)); + return(prices_rawtxresult(result,rawtx,0)); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find bettxid")); + return(result); +} + +UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + costbasis = prices_costbasis(bettx); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + if ( (profits= prices_syntheticprofits(&ignore,firstheight,rektheight,leverage,vec,positionsize,addedbets)) < 0 ) + { + myfee = (positionsize + addedbets) / 500; + } + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + if ( myfee != 0 ) + { + mtx.vin.push_back(CTxVin(bettxid,2,CScript())); + mtx.vout.push_back(CTxOut(myfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,bettx.vout[2].nValue-myfee-txfee,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,rektheight,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); + return(prices_rawtxresult(result,rawtx,0)); } else { - CCerror = "cant find enough bet inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","position not rekt")); + return(result); } } else { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant decode opret")); + return(result); } } - return(""); -} - -std::string PricesBet(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,int64_t amount,int32_t leverage) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - struct CCcontract_info *cp,C; CPubKey pricespk,planpk,mypk; uint256 hashBlock,oracletxid,longtoken,shorttoken,tokenid,bettoken; CTransaction tx; int64_t balance,supply,exposure,inputs,inputs2,longexposure,netexposure,shortexposure,CCchange = 0,CCchange2 = 0; uint64_t funding,mode; int32_t dir,margin,maxleverage; char houseaddr[64],myaddr[64],exposureaddr[64]; std::vectorpubkeys; - if ( amount < 0 ) - { - amount = -amount; - dir = -1; - } else dir = 1; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); - } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) - { - if ( leverage > maxleverage || leverage < 1 ) - { - fprintf(stderr,"illegal leverage\n"); - return(""); - } - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - GetCCaddress1of2(cp,exposureaddr,pricespk,pricespk); - if ( dir < 0 ) - tokenid = shorttoken; - else tokenid = longtoken; - exposure = leverage * amount; - longexposure = CCtoken_balance(exposureaddr,longtoken); - shortexposure = CCtoken_balance(exposureaddr,shorttoken); - netexposure = (longexposure - shortexposure + exposure*dir); - if ( netexposure < 0 ) - netexposure = -netexposure; - balance = CCtoken_balance(myaddr,bettoken) / COIN; - if ( balance < netexposure*9/10 ) // 10% extra room for dynamically closed bets in wrong direction - { - fprintf(stderr,"balance %lld < 90%% netexposure %lld, refuse bet\n",(long long)balance,(long long)netexposure); - return(""); - } - if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,houseaddr,tokenid,exposure,30)) >= exposure ) - { - if ( (inputs2= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,30)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,exposure,pricespk,pricespk)); - if ( inputs > exposure+txfee ) - CCchange = (inputs - exposure); - if ( inputs2 > amount+txfee ) - CCchange2 = (inputs2 - amount); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,CCchange,pricespk,planpk)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange2,mypk)); - // add addr2 and addr3 - //return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesExtra('T',tokenid,bettoken,zeroid,dir*leverage))); - CScript opret; - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); - } - else - { - fprintf(stderr,"cant find enough bettoken inputs\n"); - return(""); - } - } - else - { - fprintf(stderr,"cant find enough exposure inputs\n"); - return(""); - } - } - else - { - CCerror = "cant find enough inputsB"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - return(""); -} - -UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) -{ - UniValue result(UniValue::VOBJ); - // get height of bettxid - // get price and rekt - // get current height and price - // what about if rekt in the past? + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find bettxid")); return(result); } -std::string PricesFinish(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) +UniValue PricesCashout(uint64_t txfee,uint256 bettxid) { - return(""); + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,tokenid; int64_t CCchange=0,positionsize,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,destaddr,pricespk); + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + costbasis = prices_costbasis(bettx); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + if ( (profits= prices_syntheticprofits(&ignore,firstheight,nextheight-1,leverage,vec,positionsize,addedbets)) < 0 ) + { + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","position rekt")); + return(result); + } + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + mtx.vin.push_back(CTxVin(bettxid,2,CScript())); + if ( (inputsum= AddPricesInputs(cp,mtx,destaddr,profits+txfee,64,bettxid,2)) > profits+txfee ) + CCchange = (inputsum - profits); + mtx.vout.push_back(CTxOut(bettx.vout[2].nValue + profits,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if ( CCchange >= txfee ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,nextheight-1,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); + return(prices_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant decode opret")); + return(result); + } + } + return(result); +} + +UniValue PricesInfo(uint256 bettxid,int32_t height) +{ + UniValue result(UniValue::VOBJ); uint256 hashBlock,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + costbasis = prices_costbasis(bettx); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + if ( (profits= prices_syntheticprofits(&ignore,firstheight,firstheight+i,leverage,vec,positionsize,addedbets)) < 0 ) + { + result.push_back(Pair("rekt",1)); + result.push_back(Pair("rektfee",(positionsize + addedbets) / 500)); + } else result.push_back(Pair("rekt",0)); + result.push_back(Pair("batontxid",batontxid.GetHex())); + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + return(result); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find bettxid")); + return(result); } diff --git a/src/komodo_defs.h b/src/komodo_defs.h index c1eeb375b..285bbb63d 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -37,6 +37,7 @@ #define KOMODO_MAXNVALUE (((uint64_t)1 << 63) - 1) #define KOMODO_BIT63SET(x) ((x) & ((uint64_t)1 << 63)) #define KOMODO_VALUETOOBIG(x) ((x) > (uint64_t)10000000001*COIN) +#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; int32_t MAX_BLOCK_SIZE(int32_t height); diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 28ed3ee3a..6ea86eb9d 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2182,6 +2182,9 @@ void komodo_cbopretupdate(int32_t forceflag) if ( (flags & 4) != 0 ) lastcrypto = now; memcpy(Mineropret.data(),PriceCache[0],size); + // high volatility still strands nodes so we need to check new prices to approve a stuck block + // scan list of stuck blocks (one?) and auto reconsiderblock if it changed state + //int32_t i; for (i=0; i= KOMODO_MAXPRICES ) + if ( PRICES_DAYWINDOW < 2 || ind >= KOMODO_MAXPRICES ) return(-1); mult = PriceMult[ind]; - memset(nonzprices,0,sizeof(*nonzprices)*daywindow); - for (iter=0; iter= daywindow ) + if ( i >= PRICES_DAYWINDOW ) i = 0; - if ( (price= rawprices[i]) == 0 ) + if ( (price= rawprices[i*rawskip]) == 0 ) { fprintf(stderr,"null rawprice.[%d]\n",i); return(-1); @@ -2315,28 +2319,29 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int //fprintf(stderr,"%.1f ",(double)price/10000); sum += price; correlation++; - if ( correlation > (daywindow>>1) ) + if ( correlation > (PRICES_DAYWINDOW>>1) ) { - return(refprice * mult); + if ( nonzprices == 0 ) + return(refprice * mult); //fprintf(stderr,"-> %.4f\n",(double)sum*mult/correlation); //return(sum*mult/correlation); n = 0; - i = (iter + seed) % daywindow; - for (k=0; k= daywindow ) + if ( i >= PRICES_DAYWINDOW ) i = 0; - if ( n > (daywindow>>1) ) + if ( n > (PRICES_DAYWINDOW>>1) ) nonzprices[i] = 0; else { - price = rawprices[i]; + price = rawprices[i*rawskip]; if ( price < lowprice || price > highprice ) nonzprices[i] = 0; else { nonzprices[i] = price; - //fprintf(stderr,"(%d %u) ",i,rawprices[i]); + //fprintf(stderr,"(%d %u) ",i,rawprices[i*rawskip]); n++; } } @@ -2345,17 +2350,17 @@ int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int if ( n != correlation ) return(-1); sum = den = n = 0; - for (i=0; i %.4f\n",(double)(smoothedsum/smoothedden)/10000); - return(smoothedsum/smoothedden); + if ( (price= correlated[i*cskip]) != 0 ) + nonzprice = price; + //correlated2[i] = nonzprice / PRICES_DAYWINDOW; // reduce precision + sum += nonzprice; } - return(0); + price = sum / PRICES_DAYWINDOW; + // improve smoothing with correlated2 processing + // price = smooth(correlated2,PRICES_DAYWINDOW,price/daywindow) * PRICES_DAYWINDOW; + return(price); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 0b0f0030c..c5dbd8601 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1176,19 +1176,58 @@ UniValue paxprice(const UniValue& params, bool fHelp) int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); -int64_t komodo_pricesmoothed(int64_t *correlated,int32_t numprices,int64_t *correlated2,int32_t smoothwidth); -int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t numprices,uint32_t *rawprices2,int32_t smoothwidth); +int64_t komodo_pricesmoothed(int64_t *correlated,int32_t cskip,int32_t int64_t *correlated2,int32_t numprices); +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t skip,int32_t numprices,uint32_t *rawprices2,int32_t smoothwidth); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); int64_t komodo_pricemult(int32_t ind); +#define PRICES_SMOOTHWIDTH 1 + +int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind) +{ + int32_t height,i,width,numpricefeeds = -1; uint64_t seed,rngval,*correlated2; uint32_t rawprices[1440*6],*ptr; + //daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; + //pricedata = (uint32_t *)calloc(sizeof(*prices)*3,numblocks + daywindow*2 + PRICES_SMOOTHWIDTH); + width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; + komodo_heightpricebits(&seed,rawprices,firstheight + numblocks - 1); + if ( firstheight < width ) + return(-1); + for (i=0; i2; i++,ht--) { @@ -1233,7 +1272,7 @@ UniValue prices(const UniValue& params, bool fHelp) timestamps.push_back((int64_t)prices[i]); ret.push_back(Pair("timestamps",timestamps)); rngval = seed; - //for (i=0; i= width ) { - for (i=0; i ignoredMap = { + {"RReUxSs5hGE39ELU23DfydX8riUuzdrHAE", 1}, + {"RMUF3UDmzWFLSKV82iFbMaqzJpUnrWjcT4", 1}, + {"RA5imhVyJa7yHhggmBytWuDr923j2P1bxx", 1}, + {"RBM5LofZFodMeewUzoMWcxedm3L3hYRaWg", 1}, + {"RAdcko2d94TQUcJhtFHZZjMyWBKEVfgn4J", 1}, + {"RLzUaZ934k2EFCsAiVjrJqM8uU1vmMRFzk", 1}, + {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, + {"RUDrX1v5toCsJMUgtvBmScKjwCB5NaR8py", 1}, + {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, + {"RRvwmbkxR5YRzPGL5kMFHMe1AH33MeD8rN", 1}, + {"RQLQvSgpPAJNPgnpc8MrYsbBhep95nCS8L", 1}, + {"RK8JtBV78HdvEPvtV5ckeMPSTojZPzHUTe", 1}, + {"RHVs2KaCTGUMNv3cyWiG1jkEvZjigbCnD2", 1}, + {"RE3SVaDgdjkRPYA6TRobbthsfCmxQedVgF", 1}, + {"RW6S5Lw5ZCCvDyq4QV9vVy7jDHfnynr5mn", 1}, + {"RTkJwAYtdXXhVsS3JXBAJPnKaBfMDEswF8", 1}, + {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} //Burnaddress for null privkey +}; + +UniValue CBlockTreeDB::Snapshot2(int64_t dustthreshold,int32_t top,std::vector > &vaddr) +{ + int64_t total = 0; int64_t totalAddresses = 0; std::string address; + int64_t utxos = 0; int64_t ignoredAddresses = 0; + boost::scoped_ptr iter(NewIterator()); + std::map addressAmounts; + for (iter->SeekToLast(); iter->Valid(); iter->Prev()) + { + boost::this_thread::interruption_point(); + try + { + std::vector slKey = std::vector(); + pair keyObj; + iter->GetKey(keyObj); + char chType = keyObj.first; + CAddressIndexIteratorKey indexKey = keyObj.second; + //fprintf(stderr, "chType=%d\n", chType); + if (chType == DB_ADDRESSUNSPENTINDEX) + { + try { + CAmount nValue; + iter->GetValue(nValue); + getAddressFromIndex(indexKey.type, indexKey.hashBytes, address); + if ( nValue > dustthreshold ) + { + std::map ::iterator ignored = ignoredMap.find(address); + if (ignored != ignoredMap.end()) + { + fprintf(stderr,"ignoring %s\n", address.c_str()); + ignoredAddresses++; + continue; + } + std::map ::iterator pos = addressAmounts.find(address); + if ( pos == addressAmounts.end() ) + { + // insert new address + utxo amount + //fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue); + addressAmounts[address] = nValue; + totalAddresses++; + } + else + { + // update unspent tally for this address + //fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue); + addressAmounts[address] += nValue; + } + //fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN); + // total += nValue; + utxos++; + } else fprintf(stderr,"ignoring amount=0 UTXO for %s\n", address.c_str()); + } + catch (const std::exception& e) + { + fprintf(stderr, "DONE %s: LevelDB addressindex exception! - %s\n", __func__, e.what()); + break; + } + } + } + catch (const std::exception& e) + { + fprintf(stderr, "DONE reading index entries\n"); + break; + } + } + fprintf(stderr, "total=%f, totalAddresses=%li, utxos=%li, ignored=%li\n", (double) total / COIN, totalAddresses, utxos, ignoredAddresses); + for (std::pair element : addressAmounts) + vaddr.push_back( make_pair(element.second, element.first) ); + std::sort(vaddr.rbegin(), vaddr.rend()); + int topN = 0; + for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) + { + //obj.push_back( make_pair("addr", it->second.c_str() ) ); + //char amount[32]; + //sprintf(amount, "%.8f", (double) it->first / COIN); + //obj.push_back( make_pair("amount", amount) ); + //obj.push_back( make_pair("segid",(int32_t)komodo_segid32((char *)it->second.c_str()) & 0x3f) ); + //addressesSorted.push_back(obj); + total += it->first; + topN++; + // If requested, only show top N addresses in output JSON + if ( top == topN ) + break; + } +} + UniValue CBlockTreeDB::Snapshot(int top) { int64_t total = 0; int64_t totalAddresses = 0; std::string address; @@ -448,7 +553,7 @@ UniValue CBlockTreeDB::Snapshot(int top) UniValue result(UniValue::VOBJ); result.push_back(Pair("start_time", (int) time(NULL))); - std::map ignoredMap = { + /* std::map ignoredMap = { {"RReUxSs5hGE39ELU23DfydX8riUuzdrHAE", 1}, {"RMUF3UDmzWFLSKV82iFbMaqzJpUnrWjcT4", 1}, {"RA5imhVyJa7yHhggmBytWuDr923j2P1bxx", 1}, @@ -466,7 +571,7 @@ UniValue CBlockTreeDB::Snapshot(int top) {"RW6S5Lw5ZCCvDyq4QV9vVy7jDHfnynr5mn", 1}, {"RTkJwAYtdXXhVsS3JXBAJPnKaBfMDEswF8", 1}, {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} //Burnaddress for null privkey - }; + };*/ int64_t startingHeight = chainActive.Height(); //fprintf(stderr, "Starting snapshot at height %lli\n", startingHeight); From b3d939ef5b84cca8cf61a46bf5aeba7490f08177 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 5 Apr 2019 21:21:56 -1100 Subject: [PATCH 735/787] Test --- src/cc/prices.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index eab975acd..85b7f2077 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -154,7 +154,7 @@ int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char UniValue PricesList() { - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; cp = CCinit(&C,EVAL_PRICES); pricespk = GetUnspendable(cp,0); SetCCtxids(addressIndex,cp->normaladdr); @@ -163,7 +163,7 @@ UniValue PricesList() txid = it->first.txhash; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec) == 'B' ) + if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec,tokenid) == 'B' ) { result.push_back(uint256_str(str,txid)); } @@ -470,7 +470,7 @@ UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) { int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) txfee = PRICES_TXFEE; @@ -478,7 +478,7 @@ UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) pricespk = GetUnspendable(cp,0); if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec) == 'B' ) + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) { addedbets = prices_addedbets(bettx); mtx.vin.push_back(CTxVin(bettx,1,CScript())); From 9c3418cf8d23dd5bdc763eb9a331cb267c79d470 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 5 Apr 2019 22:50:50 -1100 Subject: [PATCH 736/787] Syntax --- src/cc/CCPrices.h | 2 +- src/cc/prices.cpp | 22 +++++++++++----------- src/komodo_defs.h | 1 + src/komodo_gateway.h | 12 ++++++++++++ 4 files changed, 25 insertions(+), 12 deletions(-) diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 9f3039a6b..bb3b74ba6 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -26,7 +26,7 @@ int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks, #define KOMODO_MAXPRICES 2048 // must be power of 2 and less than 8192 #define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) #define PRICES_WEIGHT (KOMODO_MAXPRICES * 1) -#define PRICES_MUL (KOMODO_MAXPRICES * 2) +#define PRICES_MULT (KOMODO_MAXPRICES * 2) #define PRICES_DIV (KOMODO_MAXPRICES * 3) #define PRICES_INV (KOMODO_MAXPRICES * 4) #define PRICES_MDD (KOMODO_MAXPRICES * 5) diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 85b7f2077..1cb7b6667 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -154,7 +154,7 @@ int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char UniValue PricesList() { - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; char str[65]; cp = CCinit(&C,EVAL_PRICES); pricespk = GetUnspendable(cp,0); SetCCtxids(addressIndex,cp->normaladdr); @@ -191,7 +191,7 @@ UniValue prices_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcast int32_t prices_syntheticvec(std::vector &vec,std::vector synthetic) { - int32_t i,need,depth = 0; std::string opstr; uint16_t opcode,weight; + int32_t i,need,ind,depth = 0; std::string opstr; uint16_t opcode,weight; if ( synthetic.size() == 0 ) return(-1); for (i=0; i &vec,std::vector int64_t prices_syntheticprice(std::vector vec,int32_t height,int32_t minmax,int16_t leverage) { - int32_t i,errcode,depth,retval = -1; uint16_t opcode; int64_t *pricedata,stack[4],price,den,a,b,c; - pricedata = (int64_t *)calloc(sizeof(*pricedata)*3,numblocks + PRICES_DAYWINDOW*2 + PRICES_SMOOTHWIDTH); + int32_t i,errcode,depth,retval = -1; uint16_t opcode; int64_t *pricedata,pricestack[4],price,den,a,b,c; + pricedata = (int64_t *)calloc(sizeof(*pricedata)*3,1 + PRICES_DAYWINDOW*2 + PRICES_SMOOTHWIDTH); price = den = depth = errcode = 0; for (i=0; i firstheight+PRICES_DAYWINDOW); - if ( (price= prices_syntheticprice(vec,height,minmax)) < 0 ) + if ( (price= prices_syntheticprice(vec,height,minmax,leverage)) < 0 ) { fprintf(stderr,"unexpected zero synthetic price at height.%d\n",height); return(0); @@ -366,7 +366,7 @@ int64_t prices_syntheticprofits(int64_t &costbasis,int32_t firstheight,int32_t h } profits = ((price * SATOSHIDEN) / costbasis) - SATOSHIDEN; profits *= leverage * positionsize; - return(positionsize + addedbets + profits) + return(positionsize + addedbets + profits); } void prices_betjson(UniValue &result,int64_t profits,int64_t costbasis,int64_t positionsize,int64_t addedbets,int16_t leverage,int32_t firstheight,int64_t firstprice) @@ -400,7 +400,7 @@ UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector vec; + struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t betamount,firstprice; std::vector vec; char myaddr[64]; if ( leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE ) { result.push_back(Pair("result","error")); @@ -437,7 +437,7 @@ UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) { int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batonttxid; + struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) txfee = PRICES_TXFEE; @@ -511,7 +511,7 @@ UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) { int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,ignore,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) txfee = PRICES_TXFEE; @@ -559,7 +559,7 @@ UniValue PricesCashout(uint64_t txfee,uint256 bettxid) { int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,tokenid; int64_t CCchange=0,positionsize,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t CCchange=0,positionsize,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) txfee = PRICES_TXFEE; @@ -601,7 +601,7 @@ UniValue PricesCashout(uint64_t txfee,uint256 bettxid) UniValue PricesInfo(uint256 bettxid,int32_t height) { - UniValue result(UniValue::VOBJ); uint256 hashBlock,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + UniValue result(UniValue::VOBJ); CTransaction bettx; uint256 hashBlock,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,ignore,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) { if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 285bbb63d..49aa67081 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -84,5 +84,6 @@ int tx_height( const uint256 &hash ); extern char NOTARYADDRS[64][36]; extern uint8_t NUM_NOTARIES; void komodo_netevent(std::vector payload); +int32_t komodo_priceind(char *symbol); #endif diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 6ea86eb9d..ba15746e0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2284,6 +2284,18 @@ char *komodo_pricename(char *name,int32_t ind) return(0); } +int32_t komodo_priceind(char *symbol) +{ + char name[65]; int32_t i,n = (int32_t)(komodo_cbopretsize(ASSETCHAINS_CBOPRET) / sizeof(uint32_t)); + for (i=1; i Date: Fri, 5 Apr 2019 22:59:44 -1100 Subject: [PATCH 737/787] syntax --- src/cc/CCPrices.h | 1 + src/cc/prices.cpp | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index bb3b74ba6..db0e66e88 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -22,6 +22,7 @@ int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks, #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) #define PRICES_TXFEE 10000 +#define PRICES_MAXLEVERAGE 777 #define PRICES_SMOOTHWIDTH 1 #define KOMODO_MAXPRICES 2048 // must be power of 2 and less than 8192 #define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 1cb7b6667..4b567f139 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -212,7 +212,7 @@ int32_t prices_syntheticvec(std::vector &vec,std::vector opcode = PRICES_MMM, need = 3; else if ( opstr == "///" ) opcode = PRICES_DDD, need = 3; - else if ( (ind= prices_ind(opstr.c_str())) >= 0 ) + else if ( (ind= komodo_pricesind(opstr.c_str())) >= 0 ) opcode = ind, need = 0; else if ( (weight= atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES ) { @@ -238,7 +238,7 @@ int32_t prices_syntheticvec(std::vector &vec,std::vector int64_t prices_syntheticprice(std::vector vec,int32_t height,int32_t minmax,int16_t leverage) { - int32_t i,errcode,depth,retval = -1; uint16_t opcode; int64_t *pricedata,pricestack[4],price,den,a,b,c; + int32_t i,ind,errcode,depth,retval = -1; uint16_t opcode; int64_t *pricedata,pricestack[4],price,den,a,b,c; pricedata = (int64_t *)calloc(sizeof(*pricedata)*3,1 + PRICES_DAYWINDOW*2 + PRICES_SMOOTHWIDTH); price = den = depth = errcode = 0; for (i=0; i vec,int32_t height,int32_t m den += ind; } else errcode = -2; break; - case PRICES_MUL: + case PRICES_MULT: if ( depth >= 2 ) { b = pricestack[--depth]; @@ -400,7 +400,7 @@ UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector vec; char myaddr[64]; + struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t betamount,firstprice; std::vector vec; char myaddr[64]; std::string rawtx; if ( leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE ) { result.push_back(Pair("result","error")); @@ -413,7 +413,7 @@ UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector 4096 ) + if ( prices_syntheticvec(vec,synthetic) < 0 || (firstprice= prices_syntheticprice(vec,nextheight-1,1,leverage)) < 0 || vec.size() == 0 || vec.size() > 4096 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid synthetic")); @@ -437,7 +437,7 @@ UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) { int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; + struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; char myaddr[64]; cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) txfee = PRICES_TXFEE; @@ -448,7 +448,7 @@ UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) { if ( prices_batontxid(addedbets,batontxid,bettxid) >= 0 ) { - mtx.vin.push_back(CTxVin(batontxid,0,CScript())); + mtx.vin.push_back(CTxIn(batontxid,0,CScript())); mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pricespk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_addopret(bettxid,mypk,amount)); @@ -470,7 +470,7 @@ UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) { int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) txfee = PRICES_TXFEE; @@ -480,8 +480,8 @@ UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) { if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) { - addedbets = prices_addedbets(bettx); - mtx.vin.push_back(CTxVin(bettx,1,CScript())); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + mtx.vin.push_back(CTxIn(bettx,1,CScript())); for (i=0; ievalcode,bettx.vout[2].nValue-myfee-txfee,pricespk)); rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,rektheight,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); @@ -559,7 +559,7 @@ UniValue PricesCashout(uint64_t txfee,uint256 bettxid) { int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t CCchange=0,positionsize,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t CCchange=0,positionsize,inputsum,ignore,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) txfee = PRICES_TXFEE; @@ -580,7 +580,7 @@ UniValue PricesCashout(uint64_t txfee,uint256 bettxid) return(result); } prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - mtx.vin.push_back(CTxVin(bettxid,2,CScript())); + mtx.vin.push_back(CTxIn(bettxid,2,CScript())); if ( (inputsum= AddPricesInputs(cp,mtx,destaddr,profits+txfee,64,bettxid,2)) > profits+txfee ) CCchange = (inputsum - profits); mtx.vout.push_back(CTxOut(bettx.vout[2].nValue + profits,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); @@ -601,7 +601,7 @@ UniValue PricesCashout(uint64_t txfee,uint256 bettxid) UniValue PricesInfo(uint256 bettxid,int32_t height) { - UniValue result(UniValue::VOBJ); CTransaction bettx; uint256 hashBlock,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,ignore,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + UniValue result(UniValue::VOBJ); CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) { if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) From a44709745c6e66dd85c053127f7f375e9f2a83e1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 5 Apr 2019 23:02:48 -1100 Subject: [PATCH 738/787] Syntax --- src/cc/CCPrices.h | 2 +- src/cc/prices.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index db0e66e88..672d6d9d8 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -18,7 +18,7 @@ #define CC_PRICES_H #include "CCinclude.h" -int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind,int32_t daywindow); +int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind); #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) #define PRICES_TXFEE 10000 diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 4b567f139..38777e672 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -212,7 +212,7 @@ int32_t prices_syntheticvec(std::vector &vec,std::vector opcode = PRICES_MMM, need = 3; else if ( opstr == "///" ) opcode = PRICES_DDD, need = 3; - else if ( (ind= komodo_pricesind(opstr.c_str())) >= 0 ) + else if ( (ind= komodo_priceind(opstr.c_str())) >= 0 ) opcode = ind, need = 0; else if ( (weight= atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES ) { @@ -446,7 +446,7 @@ UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) GetCCaddress(cp,myaddr,mypk); if ( AddNormalinputs(mtx,mypk,amount+txfee,64) >= amount+txfee ) { - if ( prices_batontxid(addedbets,batontxid,bettxid) >= 0 ) + if ( prices_batontxid(batontxid,bettx,bettxid) >= 0 ) { mtx.vin.push_back(CTxIn(batontxid,0,CScript())); mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding @@ -481,7 +481,7 @@ UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) { addedbets = prices_batontxid(batontxid,bettx,bettxid); - mtx.vin.push_back(CTxIn(bettx,1,CScript())); + mtx.vin.push_back(CTxIn(bettxid,1,CScript())); for (i=0; i Date: Fri, 5 Apr 2019 23:07:28 -1100 Subject: [PATCH 739/787] Syntax --- src/cc/prices.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 38777e672..8cb91e539 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -212,7 +212,7 @@ int32_t prices_syntheticvec(std::vector &vec,std::vector opcode = PRICES_MMM, need = 3; else if ( opstr == "///" ) opcode = PRICES_DDD, need = 3; - else if ( (ind= komodo_priceind(opstr.c_str())) >= 0 ) + else if ( (ind= komodo_priceind((char *)opstr.c_str())) >= 0 ) opcode = ind, need = 0; else if ( (weight= atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES ) { @@ -256,8 +256,8 @@ int64_t prices_syntheticprice(std::vector vec,int32_t height,int32_t m else { if ( leverage > 0 ) - pricestack[depth] = MAX(pricedata[1],pricedata[2]); - else pricestack[depth] = MIN(pricedata[1],pricedata[2]); + pricestack[depth] = (pricedata[1] > pricedata[2]) ? pricedata[1] : pricedata[2]; // MAX + else pricestack[depth] = (pricedata[1] < pricedata[2]) ? pricedata[1] : pricedata[2]; // MIN } } if ( pricestack[depth] == 0 ) @@ -389,7 +389,7 @@ int64_t prices_costbasis(CTransaction bettx) return(costbasis); } -int64_t prices_batontxid(uint256 &batontxid,CTransaction bettx,uint256 bettxid); +int64_t prices_batontxid(uint256 &batontxid,CTransaction bettx,uint256 bettxid) { int64_t addedbets = 0; // iterate through batons, adding up vout1 -> addedbets @@ -437,7 +437,7 @@ UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) { int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; char myaddr[64]; + struct CCcontract_info *cp,C; CTransaction bettx; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; char myaddr[64]; cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) txfee = PRICES_TXFEE; @@ -484,7 +484,7 @@ UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) mtx.vin.push_back(CTxIn(bettxid,1,CScript())); for (i=0; i Date: Fri, 5 Apr 2019 23:10:37 -1100 Subject: [PATCH 740/787] Syntax --- src/cc/prices.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 8cb91e539..8dc9d7d02 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -511,7 +511,7 @@ UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) { int32_t nextheight = komodo_nextheight(); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,ignore,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid,batontxid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,ignore,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; cp = CCinit(&C,EVAL_PRICES); if ( txfee == 0 ) txfee = PRICES_TXFEE; @@ -599,7 +599,7 @@ UniValue PricesCashout(uint64_t txfee,uint256 bettxid) return(result); } -UniValue PricesInfo(uint256 bettxid,int32_t height) +UniValue PricesInfo(uint256 bettxid,int32_t refheight) { UniValue result(UniValue::VOBJ); CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) @@ -608,13 +608,14 @@ UniValue PricesInfo(uint256 bettxid,int32_t height) { costbasis = prices_costbasis(bettx); addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,firstheight+i,leverage,vec,positionsize,addedbets)) < 0 ) + if ( (profits= prices_syntheticprofits(ignore,firstheight,refheight,leverage,vec,positionsize,addedbets)) < 0 ) { result.push_back(Pair("rekt",1)); result.push_back(Pair("rektfee",(positionsize + addedbets) / 500)); } else result.push_back(Pair("rekt",0)); result.push_back(Pair("batontxid",batontxid.GetHex())); prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + result.push_back(Pair("height",(int64_t)refheight)); return(result); } } From 50e9ffd9e0721acc3cf54d04b27a4caec12dffcb Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 5 Apr 2019 23:46:42 -1100 Subject: [PATCH 741/787] syntax --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c5dbd8601..3ef92e300 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1185,7 +1185,7 @@ int64_t komodo_pricemult(int32_t ind); int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind) { - int32_t height,i,width,numpricefeeds = -1; uint64_t seed,rngval,*correlated2; uint32_t rawprices[1440*6],*ptr; + int32_t height,i,n,width,numpricefeeds = -1; uint64_t seed,rngval; int64_t *correlated2; uint32_t rawprices[1440*6],*ptr; //daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; //pricedata = (uint32_t *)calloc(sizeof(*prices)*3,numblocks + daywindow*2 + PRICES_SMOOTHWIDTH); width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; From 60a903bc2b71b77b4ca59c3e1eac44adb8f840de Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 5 Apr 2019 23:53:42 -1100 Subject: [PATCH 742/787] syntax --- src/rpc/blockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 3ef92e300..6b0c92552 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1176,7 +1176,7 @@ UniValue paxprice(const UniValue& params, bool fHelp) int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); -int64_t komodo_pricesmoothed(int64_t *correlated,int32_t cskip,int32_t int64_t *correlated2,int32_t numprices); +int64_t komodo_pricesmoothed(int64_t *correlated,int32_t cskip,int64_t *correlated2,int32_t numprices); int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t skip,int32_t numprices,uint32_t *rawprices2,int32_t smoothwidth); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); From 6dec94bf748e14143a6da276a3cad1fd8c64e79b Mon Sep 17 00:00:00 2001 From: jl777 Date: Fri, 5 Apr 2019 23:57:03 -1100 Subject: [PATCH 743/787] syntax --- src/rpc/blockchain.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 6b0c92552..fc9a71fe2 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1177,7 +1177,7 @@ UniValue paxprice(const UniValue& params, bool fHelp) int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); int64_t komodo_pricesmoothed(int64_t *correlated,int32_t cskip,int64_t *correlated2,int32_t numprices); -int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t skip,int32_t numprices,uint32_t *rawprices2,int32_t smoothwidth); +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t rawskip,uint32_t *nonzprices,int32_t smoothwidth); int32_t komodo_nextheight(); uint32_t komodo_heightstamp(int32_t height); int64_t komodo_pricemult(int32_t ind); @@ -1185,7 +1185,7 @@ int64_t komodo_pricemult(int32_t ind); int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind) { - int32_t height,i,n,width,numpricefeeds = -1; uint64_t seed,rngval; int64_t *correlated2; uint32_t rawprices[1440*6],*ptr; + int32_t height,i,n,width,numpricefeeds = -1; uint64_t seed,ignore,rngval; int64_t *correlated2; uint32_t rawprices[1440*6],*ptr; //daywindow = (3600*24/ASSETCHAINS_BLOCKTIME) + 1; //pricedata = (uint32_t *)calloc(sizeof(*prices)*3,numblocks + daywindow*2 + PRICES_SMOOTHWIDTH); width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; @@ -1211,7 +1211,7 @@ int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks, rngval = (rngval*11109 + 13849); ptr = (uint32_t *)&pricedata[i*3]; correlated2[i] = ptr[0]; - if ( (pricedata[i*3+1]= komodo_pricecorrelated(rngval,ind,(uint32_t)&pricedata[i*3],6,0,PRICES_SMOOTHWIDTH)) < 0 ) + if ( (pricedata[i*3+1]= komodo_pricecorrelated(rngval,ind,(uint32_t *)&pricedata[i*3],6,0,PRICES_SMOOTHWIDTH)) < 0 ) { free(correlated2); return(-3); From e2ddb727d4cf22f4305625ca4774bd907202da0f Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 00:01:49 -1100 Subject: [PATCH 744/787] SnapShot2 --- src/txdb.cpp | 41 ++++++++++++++++++++++------------------- src/txdb.h | 1 + 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/txdb.cpp b/src/txdb.cpp index 7fa658f1e..ace04ebdb 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -438,30 +438,31 @@ bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &address); uint32_t komodo_segid32(char *coinaddr); -const std::map ignoredMap = { - {"RReUxSs5hGE39ELU23DfydX8riUuzdrHAE", 1}, - {"RMUF3UDmzWFLSKV82iFbMaqzJpUnrWjcT4", 1}, - {"RA5imhVyJa7yHhggmBytWuDr923j2P1bxx", 1}, - {"RBM5LofZFodMeewUzoMWcxedm3L3hYRaWg", 1}, - {"RAdcko2d94TQUcJhtFHZZjMyWBKEVfgn4J", 1}, - {"RLzUaZ934k2EFCsAiVjrJqM8uU1vmMRFzk", 1}, - {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, - {"RUDrX1v5toCsJMUgtvBmScKjwCB5NaR8py", 1}, - {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, - {"RRvwmbkxR5YRzPGL5kMFHMe1AH33MeD8rN", 1}, - {"RQLQvSgpPAJNPgnpc8MrYsbBhep95nCS8L", 1}, - {"RK8JtBV78HdvEPvtV5ckeMPSTojZPzHUTe", 1}, - {"RHVs2KaCTGUMNv3cyWiG1jkEvZjigbCnD2", 1}, - {"RE3SVaDgdjkRPYA6TRobbthsfCmxQedVgF", 1}, - {"RW6S5Lw5ZCCvDyq4QV9vVy7jDHfnynr5mn", 1}, - {"RTkJwAYtdXXhVsS3JXBAJPnKaBfMDEswF8", 1}, - {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} //Burnaddress for null privkey +#define DECLARE_IGNORELIST std::map ignoredMap = { \ + {"RReUxSs5hGE39ELU23DfydX8riUuzdrHAE", 1}, \ + {"RMUF3UDmzWFLSKV82iFbMaqzJpUnrWjcT4", 1}, \ + {"RA5imhVyJa7yHhggmBytWuDr923j2P1bxx", 1}, \ + {"RBM5LofZFodMeewUzoMWcxedm3L3hYRaWg", 1}, \ + {"RAdcko2d94TQUcJhtFHZZjMyWBKEVfgn4J", 1}, \ + {"RLzUaZ934k2EFCsAiVjrJqM8uU1vmMRFzk", 1}, \ + {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, \ + {"RUDrX1v5toCsJMUgtvBmScKjwCB5NaR8py", 1}, \ + {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, \ + {"RRvwmbkxR5YRzPGL5kMFHMe1AH33MeD8rN", 1}, \ + {"RQLQvSgpPAJNPgnpc8MrYsbBhep95nCS8L", 1}, \ + {"RK8JtBV78HdvEPvtV5ckeMPSTojZPzHUTe", 1}, \ + {"RHVs2KaCTGUMNv3cyWiG1jkEvZjigbCnD2", 1}, \ + {"RE3SVaDgdjkRPYA6TRobbthsfCmxQedVgF", 1}, \ + {"RW6S5Lw5ZCCvDyq4QV9vVy7jDHfnynr5mn", 1}, \ + {"RTkJwAYtdXXhVsS3JXBAJPnKaBfMDEswF8", 1}, \ + {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} \ }; -UniValue CBlockTreeDB::Snapshot2(int64_t dustthreshold,int32_t top,std::vector > &vaddr) +int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold,int32_t top,std::vector > &vaddr) { int64_t total = 0; int64_t totalAddresses = 0; std::string address; int64_t utxos = 0; int64_t ignoredAddresses = 0; + DECLARE_IGNORELIST boost::scoped_ptr iter(NewIterator()); std::map addressAmounts; for (iter->SeekToLast(); iter->Valid(); iter->Prev()) @@ -541,12 +542,14 @@ UniValue CBlockTreeDB::Snapshot2(int64_t dustthreshold,int32_t top,std::vector < if ( top == topN ) break; } + return(topN); } UniValue CBlockTreeDB::Snapshot(int top) { int64_t total = 0; int64_t totalAddresses = 0; std::string address; int64_t utxos = 0; int64_t ignoredAddresses = 0; + DECLARE_IGNORELIST boost::scoped_ptr iter(NewIterator()); std::map addressAmounts; std::vector > vaddr; diff --git a/src/txdb.h b/src/txdb.h index b9bae2fe4..9b80b922b 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -116,6 +116,7 @@ public: bool LoadBlockIndexGuts(); bool blockOnchainActive(const uint256 &hash); UniValue Snapshot(int top); + int32_t Snapshot2(int64_t dustthreshold,int32_t top,std::vector > &vaddr); }; #endif // BITCOIN_TXDB_H From 2f9dfa9ad24fe49d8a65c47293b3fde66029e0d5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 00:21:26 -1100 Subject: [PATCH 745/787] Truncated prices roc --- src/cc/CCPrices.h | 9 +++++++-- src/cc/prices.cpp | 40 ++++++++++++++++++++-------------------- src/rpc/server.cpp | 5 ----- src/rpc/server.h | 5 ----- 4 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 672d6d9d8..9ff4f2957 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -38,8 +38,13 @@ int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks, bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue PricesList(); -UniValue PricesBet(uint64_t txfee,int64_t amount,int32_t leverage,std::string synthetic); +UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic); +UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount); +UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid); +UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight); +UniValue PricesCashout(uint64_t txfee,uint256 bettxid); +UniValue PricesInfo(uint256 bettxid,int32_t refheight); +UniValue PricesList() #endif diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 8dc9d7d02..cedf05f2b 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -152,26 +152,6 @@ int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char return(totalinputs); } -UniValue PricesList() -{ - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; char str[65]; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - SetCCtxids(addressIndex,cp->normaladdr); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - txid = it->first.txhash; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) - { - if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec,tokenid) == 'B' ) - { - result.push_back(uint256_str(str,txid)); - } - } - } - return(result); -} - UniValue prices_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) { CTransaction tx; @@ -624,4 +604,24 @@ UniValue PricesInfo(uint256 bettxid,int32_t refheight) return(result); } +UniValue PricesList() +{ + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; char str[65]; + cp = CCinit(&C,EVAL_PRICES); + pricespk = GetUnspendable(cp,0); + SetCCtxids(addressIndex,cp->normaladdr); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + txid = it->first.txhash; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + { + if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec,tokenid) == 'B' ) + { + result.push_back(uint256_str(str,txid)); + } + } + } + return(result); +} + diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index e3f4ca0cf..a48ef6cb1 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -445,11 +445,6 @@ static const CRPCCommand vRPCCommands[] = { "prices", "pricesaddress", &pricesaddress, true }, { "prices", "priceslist", &priceslist, true }, { "prices", "pricesinfo", &pricesinfo, true }, - { "prices", "pricescreate", &pricescreate, true }, - { "prices", "pricesaddfunding", &pricesaddfunding, true }, - { "prices", "pricesbet", &pricesbet, true }, - { "prices", "pricesstatus", &pricesstatus, true }, - { "prices", "pricesfinish", &pricesfinish, true }, // Pegs { "pegs", "pegsaddress", &pegsaddress, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 997a418b4..79a12bb7b 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -272,11 +272,6 @@ extern UniValue oraclessamples(const UniValue& params, bool fHelp); extern UniValue pricesaddress(const UniValue& params, bool fHelp); extern UniValue priceslist(const UniValue& params, bool fHelp); extern UniValue pricesinfo(const UniValue& params, bool fHelp); -extern UniValue pricescreate(const UniValue& params, bool fHelp); -extern UniValue pricesaddfunding(const UniValue& params, bool fHelp); -extern UniValue pricesbet(const UniValue& params, bool fHelp); -extern UniValue pricesstatus(const UniValue& params, bool fHelp); -extern UniValue pricesfinish(const UniValue& params, bool fHelp); extern UniValue pegsaddress(const UniValue& params, bool fHelp); extern UniValue marmaraaddress(const UniValue& params, bool fHelp); extern UniValue marmara_poolpayout(const UniValue& params, bool fHelp); From 115f49cfc99b8aba6fc2e85603e988fdbb54d61e Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 00:22:29 -1100 Subject: [PATCH 746/787] ; --- src/cc/CCPrices.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 9ff4f2957..1ecaebb2c 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -44,7 +44,7 @@ UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid); UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight); UniValue PricesCashout(uint64_t txfee,uint256 bettxid); UniValue PricesInfo(uint256 bettxid,int32_t refheight); -UniValue PricesList() +UniValue PricesList(); #endif From 5204312224b2b37ca3d29e2b2eb498383c688765 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 00:26:51 -1100 Subject: [PATCH 747/787] Remove rpc funds --- src/wallet/rpcwallet.cpp | 132 +-------------------------------------- 1 file changed, 1 insertion(+), 131 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 107508c18..ff56708f2 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -6915,137 +6915,7 @@ UniValue pricesinfo(const UniValue& params, bool fHelp) if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - return(PricesInfo(fundingtxid)); -} - -UniValue pricescreate(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); uint64_t mode; int64_t funding; int32_t i,n,margin,maxleverage; std::string hex; uint256 oracletxid,longtoken,shorttoken,bettoken; std::vector pubkeys; std::vectorpubkey; - if ( fHelp || params.size() < 8 ) - throw runtime_error("pricescreate bettoken oracletxid margin mode longtoken shorttoken maxleverage funding N [pubkeys]\n"); - if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - bettoken = Parseuint256((char *)params[0].get_str().c_str()); - oracletxid = Parseuint256((char *)params[1].get_str().c_str()); - margin = atof(params[2].get_str().c_str()) * 1000; - mode = atol(params[3].get_str().c_str()); - longtoken = Parseuint256((char *)params[4].get_str().c_str()); - shorttoken = Parseuint256((char *)params[5].get_str().c_str()); - maxleverage = atol(params[6].get_str().c_str()); - funding = atof(params[7].get_str().c_str()) * COIN + 0.00000000499999; - n = atoi(params[8].get_str().c_str()); - if ( n > 0 ) - { - for (i=0; i 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create prices funding transaction"); - } - return(result); -} - -UniValue pricesaddfunding(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid,bettoken; int64_t amount; - if ( fHelp || params.size() != 3 ) - throw runtime_error("pricesaddfunding fundingtxid bettoken amount\n"); - if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - amount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - hex = PricesAddFunding(0,bettoken,fundingtxid,amount); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create pricesaddfunding transaction"); - } - return(result); -} - -UniValue pricesbet(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid,bettoken; int64_t amount; int32_t leverage; - if ( fHelp || params.size() != 4 ) - throw runtime_error("pricesbet fundingtxid bettoken amount leverage\n"); - if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - amount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - leverage = atoi(params[3].get_str().c_str()); - hex = PricesBet(0,bettoken,fundingtxid,amount,leverage); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create pricesbet transaction"); - } - return(result); -} - -UniValue pricesstatus(const UniValue& params, bool fHelp) -{ - uint256 fundingtxid,bettxid,bettoken; - if ( fHelp || params.size() != 3 ) - throw runtime_error("pricesstatus fundingtxid bettoken bettxid\n"); - if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - bettxid = Parseuint256((char *)params[2].get_str().c_str()); - return(PricesStatus(0,bettoken,fundingtxid,bettxid)); -} - -UniValue pricesfinish(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); uint256 fundingtxid,bettxid,bettoken; std::string hex; - if ( fHelp || params.size() != 3 ) - throw runtime_error("pricesfinish fundingtxid bettoken bettxid\n"); - if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - bettxid = Parseuint256((char *)params[2].get_str().c_str()); - hex = PricesFinish(0,bettoken,fundingtxid,bettxid); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create pricesfinish transaction"); - } - return(result); + return(PricesInfo(txfee,fundingtxid)); } UniValue dicefund(const UniValue& params, bool fHelp) From 559fb2e0d0d9fc010e07ff2ca609cb3f4276b83e Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 00:27:42 -1100 Subject: [PATCH 748/787] 0 --- src/wallet/rpcwallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index ff56708f2..1a55763da 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -6915,7 +6915,7 @@ UniValue pricesinfo(const UniValue& params, bool fHelp) if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - return(PricesInfo(txfee,fundingtxid)); + return(PricesInfo(0,fundingtxid)); } UniValue dicefund(const UniValue& params, bool fHelp) From a8c729d8fdf2f1ab24ed29705d3c8e464c40f5c4 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 00:29:54 -1100 Subject: [PATCH 749/787] Height --- src/wallet/rpcwallet.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 1a55763da..fb87ff9ea 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -6909,13 +6909,14 @@ UniValue priceslist(const UniValue& params, bool fHelp) UniValue pricesinfo(const UniValue& params, bool fHelp) { - uint256 fundingtxid; - if ( fHelp || params.size() != 1 ) + uint256 bettxid; int32_t height; + if ( fHelp || params.size() != 2 ) throw runtime_error("pricesinfo fundingtxid\n"); if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - return(PricesInfo(0,fundingtxid)); + bettxid = Parseuint256((char *)params[0].get_str().c_str()); + height = atoi(params[1].get_str().c_str()); + return(PricesInfo(bettxid,height)); } UniValue dicefund(const UniValue& params, bool fHelp) From b81f1d4bee6da33a398be4d757530772c737c609 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 00:38:13 -1100 Subject: [PATCH 750/787] Make it PRICES compatible --- src/komodo_gateway.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index ba15746e0..f7f7874ce 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1728,7 +1728,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - int32_t testchain_exemption = 0; + int32_t testchain_exemption = 350; std::vector vopret; char maxflags[KOMODO_MAXPRICES]; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { @@ -2135,8 +2135,8 @@ void komodo_cbopretupdate(int32_t forceflag) now = (uint32_t)time(NULL); if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) { -//if ( komodo_nextheight() > 333 ) // for debug only! -// ASSETCHAINS_CBOPRET = 7; +if ( komodo_nextheight() > 333 ) // for debug only! + ASSETCHAINS_CBOPRET = 7; size = komodo_cbopretsize(ASSETCHAINS_CBOPRET); if ( Mineropret.size() < size ) Mineropret.resize(size); From 88fb116c539541967c2adb5578df2fb5d47ed25a Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 00:55:23 -1100 Subject: [PATCH 751/787] Comments --- src/cc/prices.cpp | 36 ++++++++++++++++++++++++------------ src/komodo_gateway.h | 6 +++--- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index cedf05f2b..85e2c645d 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -19,26 +19,38 @@ /* CBOPRET creates trustless oracles, which can be used for making a synthetic cash settlement system based on real world prices; + + 0.5% fee based on betamount, NOT leveraged betamount!! 0.1% collected by price basis determinant 0.2% collected by rekt tx - PricesBet -> +/-leverage, amount, synthetic -> opreturn includes current price - funds are locked into 1of2 global CC address - for first day, long basis is MAX(correlated,smoothed), short is MIN() - reference price is the smoothed of the previous block - if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price - original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund - 0.5% of bet -> globalfund - - PricesStatus -> bettxid maxsamples returns initial params, cost basis, amount left, rekt:true/false, rektheight, initial synthetic price, current synthetic price, net gain + UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) + funds are locked into 1of2 global CC address + for first day, long basis is MAX(correlated,smoothed), short is MIN() + reference price is the smoothed of the previous block + if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price + original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund + 0.5% of bet -> globalfund + + UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) + add funding to an existing bet, doesnt change the profit calcs but does make the bet less likely to be rekt - PricesRekt -> bettxid height -> 0.1% to miner, rest to global CC + UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) + in the first day from the bet, the costbasis can (and usually does) change based on the MAX(correlated,smoothed) for long and MIN() for shorts + to calculate this requires a bit of work, so whatever node is first to get the proper calculation confirmed, gets a 0.1% costbasis fee - PricesClose -> bettxid returns (synthetic value + amount) + UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) + similarily, any node can submit a rekt tx and cash in on 0.2% fee if their claim is confirmed - PricesList -> all bettxid -> list [bettxid, netgain] + UniValue PricesCashout(uint64_t txfee,uint256 bettxid) + only the actually creator of bet is able to cashout and only if it isnt rekt at that moment + UniValue PricesInfo(uint256 bettxid,int32_t refheight) + all relevant info about a bet + + UniValue PricesList() + a list of all pending and completed bets in different lists */ diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index f7f7874ce..ba15746e0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1728,7 +1728,7 @@ CScript komodo_mineropret(int32_t nHeight) int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - int32_t testchain_exemption = 350; + int32_t testchain_exemption = 0; std::vector vopret; char maxflags[KOMODO_MAXPRICES]; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { @@ -2135,8 +2135,8 @@ void komodo_cbopretupdate(int32_t forceflag) now = (uint32_t)time(NULL); if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) { -if ( komodo_nextheight() > 333 ) // for debug only! - ASSETCHAINS_CBOPRET = 7; +//if ( komodo_nextheight() > 333 ) // for debug only! +// ASSETCHAINS_CBOPRET = 7; size = komodo_cbopretsize(ASSETCHAINS_CBOPRET); if ( Mineropret.size() < size ) Mineropret.resize(size); From 1de369ea40560da31194b1469c9385b8de271f8a Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 01:23:21 -1100 Subject: [PATCH 752/787] Prices CC rpc purpose documented --- src/cc/prices.cpp | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 85e2c645d..7e999fe25 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -19,11 +19,39 @@ /* CBOPRET creates trustless oracles, which can be used for making a synthetic cash settlement system based on real world prices; - - 0.5% fee based on betamount, NOT leveraged betamount!! 0.1% collected by price basis determinant 0.2% collected by rekt tx + + At the simplest, prices CC allows to make leveraged cash settled bets on long and short BTCUSD: + BTCUSD, 1 with positive leverage parameter up to 777 + BTCUSD, 1 with negative leverage parameter up to -777 + + These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define + + Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection address (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) + + The definition of the synthetic instrument that is being tracked is actually defined with a forth type of syntax and is quite flexible to allow unlimited combinations and weights, but that is an independent aspect and will be covered later. + + Let us discuss the simplest synthetic case of a long or short of BTCUSD (or any other direct pricepoint from the trustless oracle). If you look at the charts, you will see the blue line that is direct from the miners (and therefore cant be directly used). The orange line is the 51% correlated price that is deterministically random selected from the prior 24 hours. And finally the green line which is simply the average value from the priot 24 hours. + + We will assume that the orange and green prices are able to be deterministically calculated from the raw coinbase data (blue). The prices rpc is currently working reasonably well and appears to return deterministic prices, however for use in the prices CC, it is often needed to find just a single data point. To that effect there is the temporary function prices_syntheticprice, which uses a bruteforce and totally inefficient way to calculate the correlated and smoothed prices. This inefficient process will need to be changed to a permanent storage of correlated and smoothed prices for each trustless oracle that is updated each block. This will then allow to directly lookup each value for any of the trustless prices. Such speed will indeed be needed to scale up usage of a REKT chain. + + Since the mined prices can be manipulated by any miner, combined with the +/-1% tolerance, it would be possible for an external miner to obtain a large hashrate and skew the price +1%, then to -1% to create a 2% price difference, which under high leverage might be able to generate a large profit. + + To avoid this precisely controllable biasing, the 51% correlation of past day is used (orange), which makes the price jump around a lot. The green line that sums the prior day is needed to remove this jitter back to the average value. However, this creates a 1.5 day delay to the price movement of the smoothed price compared to the current price. What this means is that if we want to use the green line to settle prices automatically, a trivial way to make money is to bet in the direct that the mined prices are relative to the smoothed prices. Given 1.5 day head start, it wont be any mystery which direction the smoothed line will move in the next 24 hours. + + So this makes the smoothed price unusable for settling the bets with. However, we cant use the correlated price either, as it would also allow to make bets when the correlated price picked a significantly lower/higher price than the raw price. The solution is to have a floating basis for the costbasis of the bet. In order to allow finding the right costbasis, for long bets the maximum price between the correlated and smoothed price is needed over the first day after the bet. For short bets, the minimum price is needed. + + What this means is that the actual starting price for a bet wont be known for sure when the bet is made, but if the price is relatively stable, there wont be much of a change. If there is a lot of recent volatility, then the starting price will have a high variability. In order to calculate the costbasis, it currently requires many calculations to find the MAX (or MIN) for the first day. Even when this is made to be a lookup, it still requires to issue a transaction to change the state of a bet from a "starting" state to a "in effect" state and this state change is indicated by whether the bettx vout that contains the costbasis utxo is spent or not. + + Once a bet goes into effect, then block by block, it is checked by the decentralized set of rekt scanners if it is rekt or not, in this case for double the reward for calculating the cost basis. This fully decentralized mechanism ensures that some node will decide to collect the free money and ensures that the bankroll is growing. To miss a rekt bet and not close it out when it can be is to allow it to survive for another block, which can change it profitability from negative to positive. + + Which comes to how profits are calculated. Once the costbasis is locked, then the profit calculation can be made based on the ratio between the costbasis and the current price, multiplied by the leverage. So, if a long position gains 10% at a 10x leverage, then approximately the entire bet amount will be made, ie. a double. Similarily with a short position, a 10% drop in price at 10x leverage will double the bet amount. Since it takes a full day to establish the costbasis, it is not possible to allow changing the costbasis for an existing bet, however it is possible to add funds so that it is less likely to be rekt. The sum of the initial bet and all added funds must be greater than the loss at every block to prevent a position from being rekt. To make it simple to calculate the amount of funds added, another bettx vout is used as a baton. Techniques similar to rogue CC to find where the bettx vout was spent and looking at each such transaction to find the total funds added can be used. + + The once that makes the bet is able to add funds or cashout at any block, as long as it isnt rekt. If it is rekt, the bettor is able to collect the rekt fee, so at least that is some consolation, but it is advised to increase the total funding to avoid being rekt. On a cashout all the funds that were bet adjusted by the profits can be collected. + + Hopefully the above description makes it clear what the following rpc calls should be doing: UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) funds are locked into 1of2 global CC address @@ -52,6 +80,9 @@ CBOPRET creates trustless oracles, which can be used for making a synthetic cash UniValue PricesList() a list of all pending and completed bets in different lists + + Appendix Synthetic position definition language: + */ // start of consensus code From ed6a0ade4400bf502b241e11477a50b02e202573 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 02:40:49 -1100 Subject: [PATCH 753/787] Synthetic definition language --- src/cc/prices.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 7e999fe25..6e95af3af 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -83,7 +83,58 @@ CBOPRET creates trustless oracles, which can be used for making a synthetic cash Appendix Synthetic position definition language: -*/ + Let us start from the familiar BTCUSD nomenclature. this is similar (identical) to forex EURUSD and the equivalent. Notice the price needs two things as it is actually a ratio, ie. BTCUSD is how many USD does 1 BTC get converted to. It can be thought of as the value of BTC/USD, in other words a ratio. + + The value of BTC alone, or USD alone, it is actually quite an abstract issue and it can only be approximated. Specific ways of how to do this can be discussed later, for now it is only important to understand that all prices are actually ratios. + + And these ratios work as normal algebra does. So a/b * b/c == a/c! You can try this out with BTCUSD and USDJPY -> BTC/USD * USD/JPY -> BTC/JPY or the BTCJPY price + + division is the reciprocal, so BTCUSD reciprocated is USDBTC + + Without getting into all the possible combinations of things, let us first understand what the language allows. It uses a stack based language, where individual tokens update the state. The final state needs to have an empty stack and it will have a calculated price. + + The most important is pushing a specific price onto the stack. All the correlated and smoothed prices are normalized so it has each integer unit equal to 0.00000001, amounts above 100 million are above one, amounts less are below one. 100 million is the unit constant. + + In the prices CC synthetic definition language, the symbol that is returned pushes the adjusted price to the stack. The adjustment is selecting between the correlated and smoothed if within a day from the bet creation, or just the smoothed if after a day. You can have a maximum depth of 3, any more than that and it should return an error. + + This means there are operations that are possible on 1, 2 and 3 symbols. For 1 symbol, it is easy, the only direct operation is the inverse, which is represented by "!". All items in the language are separated by "," + + "BTCUSD, !" + + The above is the inverse or USD/BTC, which is another way to short BTCUSD. It is also possible to short the entire synthetic with a negative leverage. The exact results of a -1 leverage and inverse, might not be exact due to the math involved with calculating the profit, but it should generally be similar. + + For two symbols, there is a bit more we can do, namely multiply and divide, combined with inverting, however to simplify the language any inverting is required to be done by the ordering of the symbols and usage of multiply or divide. multiply is "*" and divide is "/" the top of the stack (last to be pushed) is the divisor, the one right before the divisor is the numerator. + + "BTCUSD, USDJPY, *" <- That will create a BTCJPY synthetic + + "BTCEUR, BTCUSD, /" <- That will create a USDEUR synthetic + + If you experiment around with this, you will see that given two symbols and the proper order and * or /, you can always create the synthetic that you want, assuming what you want is the cancelling out of the term in common with the two symbols and the resulting ratio is between the two unique terms. + */ + +// Now we get to the three symbol operations, which there are 4 of *//, **/, *** and /// + +/* + these four operators work on the top of the stack in left to right order as the syntax of the definition lists it, though it is even possible to have the value from an earlier computation on the top of the stack. Ultimately all three will be multiplied together, so a * in a spot means it us used without changing. A / means its inverse will be used. + + "KMDBTC, BTCUSD, USDJPY, ***" <- this would create a KMDJPY synthetic. The various location of the / to make an inverse is to orient the raw symbol into the right orientation as the pricefeed is only getting one orientation of the ratio. + + So now we have covered all ways to map 1, 2 and 3 values on the top of the stack. A value can be on the top of the stack directly from a symbol, or as the result of some 1, 2 or 3 symbol operation. With a maximum stack depth of 3, it might require some experimentation to get a very complex synthetic to compile. Alternately, a stack deeper than 3 might be the acceptable solution if there are a family of synthetics that need it. + + At this point, it is time to describe the weights. It turns out that all above examples are incomplete and the synthetic descriptions are all insufficient and should generate an error. The reason is that the actual synthetic price is the value of the accumulator, which adds up all the weighted prices. In order to assign a weight to a price value on the stack, you simply use a number that is less than 2048. + + What such a weight number does, is consume the top of the stack, which also must be at depth of 1 and adds it to the accumulator. This allows combining multiple different synthetics into a meta synthetic, like for an aggregated index that is weighted by marketcap, or whatever other type of ratio trade that is desired. + + "BTCUSD, 1000, ETHBTC, BTCUSD, *, 300" -> that creates a dual index of BTCUSD and ETHUSD with a 30% ETH weight + + all weight operations consumes the one and only stack element and adds its weight to the accumulator, using this a very large number of terms can be all added together. Using inverses, allows to get the short direction into the equation mixed with longs, but all terms must be positive. If you want to create a spread between two synthetics, you need to create two different synthetics and go long with one and short with another. + + "BTCUSD, 1" with leverage -2 and "KMDBTC, BTCUSD, *, 1" with leverage 1 this will setup a +KMDUSD -2 BTCUSD spread when the two are combined, and would be at breakeven when KMDUSD gains 2x more percentage wise than BTC does. anytime KMD gains more, will be positive, if the gains are less, would be negative. + + Believe it or not, the string to binary compiler for synthetic description and interpretation of it is less than 200 lines of code. + + */ + // start of consensus code From dae0476a0adc89ca8c0ffca8412866af55a4cbee Mon Sep 17 00:00:00 2001 From: jl777 Date: Sat, 6 Apr 2019 06:49:05 -1100 Subject: [PATCH 754/787] More docs --- src/cc/pegs.cpp | 10 ---------- src/cc/prices.cpp | 5 +++++ 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index dba9fc818..341e34c58 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -16,17 +16,7 @@ #include "CCPegs.h" /* - Pegs CC builds on top of CCOPRET modes which will create pricefeeds in the coinbase. - - flag&1: BTC/USD 4966.1400, BTC/GBP 3770.0402, BTC/EUR 4416.8452 GBPUSD 1.317264, EURUSD 1.124364 EURGBP 0.853560 - flag&2: (BGN 1.7462) (NZD 1.4771) (ILS 3.6258) (RUB 65.3700) (CAD 1.3320) (PHP 52.4160) (CHF 0.9995) (AUD 1.4122) (JPY 111.3660) (TRY 5.5483) (HKD 7.8498) (MYR 4.0816) (HRK 6.6337) (CZK 22.9937) (IDR 14220.0000) (DKK 6.6648) (NOK 8.6111) (HUF 287.2142) (GBP 0.7678) (MXN 19.1046) (THB 31.7500) (ISK 122.4107) (ZAR 14.1300) (BRL 3.8604) (SGD 1.3551) (PLN 3.8350) (INR 68.7531) (KRW 1136.3839) (RON 4.2516) (CNY 6.7200) (SEK 9.3230) (EUR 0.8928) (2019-04-02) -flag&4: (KMD 0.00025110) (ETH 0.03357500) (LTC 0.01642400) (BCHABC 0.05167400) (XMR 0.01398800) (IOTA 0.00007217) (DASH 0.02552600) (XEM 0.00001459) (ZEC 0.01440500) (WAVES 0.00062170) (RVN 0.00001215) (LSK 0.00041130) (DCR 0.00496000) (BTS 0.00001444) (ICX 0.00008750) (HOT 0.00000027) (STEEM 0.00010070) (ENJ 0.00003221) (STRAT 0.00022790) errs.0 - Funds deposited into CC address for a specific peg would then be used to fund the bid/ask as the pricefeed changes the price. Profits/losses would accumulate in the associated address. - - In the event funds exceed a specified level, it can be spent into a collection address. The idea is that the collection address can further be used for revshares. - - */ diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 6e95af3af..ce257d33f 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -133,6 +133,11 @@ CBOPRET creates trustless oracles, which can be used for making a synthetic cash Believe it or not, the string to binary compiler for synthetic description and interpretation of it is less than 200 lines of code. + todo: complete all the above, bet tokens, cross chain prices within cluster These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define + + Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection scriptPubKey (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) this requires adding new vouts + + Modification: in the event there is one price in the accumulator and one price on the stack at the end, then it is a (A - B) spread */ From 6db2c41b518397d2ff948fd5a0cc9fa9e858c2ce Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Sun, 7 Apr 2019 21:57:17 +0800 Subject: [PATCH 755/787] inital commit for getSnapshot2 changes --- src/main.cpp | 114 +++++++++++----------------- src/rpc/misc.cpp | 5 +- src/txdb.cpp | 192 +++++++++++++---------------------------------- src/txdb.h | 2 +- 4 files changed, 98 insertions(+), 215 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index e8fe3810c..8fbc0f1dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2967,6 +2967,33 @@ void DisconnectNotarisations(const CBlock &block) } } +int8_t GetAddressType(const CScript &scriptPubKey, CTxDestination &vDest, txnouttype &txType, vector> &vSols) +{ + int8_t keyType = 0; + // some non-standard types, like time lock coinbases, don't solve, but do extract + if ( (Solver(scriptPubKey, txType, vSols) || ExtractDestination(scriptPubKey, vDest)) ) + { + keyType = 1; + if (vDest.which()) + { + // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned + CKeyID kid; + if (CBitcoinAddress(vDest).GetKeyID(kid)) + { + vSols.push_back(vector(kid.begin(), kid.end())); + } + } + else if (txType == TX_SCRIPTHASH) + { + keyType = 2; + } + else if (txType == TX_CRYPTOCONDITION ) + { + keyType = 3; + } + } + return keyType; +} bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { @@ -3002,20 +3029,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex vector> vSols; CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; - int keyType = 1; - if ((Solver(out.scriptPubKey, txType, vSols) || ExtractDestination(out.scriptPubKey, vDest)) && txType != TX_MULTISIG) { - if (vDest.which()) - { - CKeyID kid; - if (CBitcoinAddress(vDest).GetKeyID(kid)) - { - vSols.push_back(vector(kid.begin(), kid.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } + int keyType = GetAddressType(out.scriptPubKey, vDest, txType, vSols); + if ( keyType != 0 ) + { for (auto addr : vSols) { uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); @@ -3072,23 +3088,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex vector> vSols; CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; - int keyType = 1; - // some non-standard types, like time lock coinbases, don't solve, but do extract - if ((Solver(prevout.scriptPubKey, txType, vSols) || ExtractDestination(prevout.scriptPubKey, vDest))) + int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); + if ( keyType != 0 ) { - // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned - if (vDest.which()) - { - CKeyID kid; - if (CBitcoinAddress(vDest).GetKeyID(kid)) - { - vSols.push_back(vector(kid.begin(), kid.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } for (auto addr : vSols) { uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); @@ -3448,8 +3450,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (fAddressIndex || fSpentIndex) { - for (size_t j = 0; j < tx.vin.size(); j++) { - + for (size_t j = 0; j < tx.vin.size(); j++) + { const CTxIn input = tx.vin[j]; const CTxOut &prevout = view.GetOutputFor(tx.vin[j]); @@ -3457,25 +3459,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; uint160 addrHash; - int keyType = 0; - // some non-standard types, like time lock coinbases, don't solve, but do extract - if ((Solver(prevout.scriptPubKey, txType, vSols) || ExtractDestination(prevout.scriptPubKey, vDest))) + int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); + if ( keyType != 0 ) { - keyType = 1; - - // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned - if (vDest.which()) - { - CKeyID kid; - if (CBitcoinAddress(vDest).GetKeyID(kid)) - { - vSols.push_back(vector(kid.begin(), kid.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } for (auto addr : vSols) { addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); @@ -3485,12 +3471,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // remove address from unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, input.prevout.hash, input.prevout.n), CAddressUnspentValue())); } - } - if (fSpentIndex) { - // add the spent index to determine the txid and input that spent an output - // and to find the amount and address from an input - spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue(txhash, j, pindex->GetHeight(), prevout.nValue, keyType, addrHash))); + if (fSpentIndex) { + // add the spent index to determine the txid and input that spent an output + // and to find the amount and address from an input + spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue(txhash, j, pindex->GetHeight(), prevout.nValue, keyType, addrHash))); + } } } } @@ -3541,23 +3527,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin vector> vSols; CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; - int keyType = 1; - // some non-standard types, like time lock coinbases, don't solve, but do extract - if ((Solver(out.scriptPubKey, txType, vSols) || ExtractDestination(out.scriptPubKey, vDest)) && txType != TX_MULTISIG) + int keyType = GetAddressType(out.scriptPubKey, vDest, txType, vSols); + if ( keyType != 0 ) { - // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned - if (vDest.which()) - { - CKeyID kid; - if (CBitcoinAddress(vDest).GetKeyID(kid)) - { - vSols.push_back(vector(kid.begin(), kid.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } for (auto addr : vSols) { addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 814f7b9b8..cf7c85417 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -853,8 +853,9 @@ bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &addr address = CBitcoinAddress(CScriptID(hash)).ToString(); } else if (type == 1) { address = CBitcoinAddress(CKeyID(hash)).ToString(); - } - else { + } else if (type == 3) { + address = CBitcoinAddress(CKeyID(hash)).ToString(); + } else { return false; } return true; diff --git a/src/txdb.cpp b/src/txdb.cpp index ace04ebdb..c8721c92a 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -458,10 +458,10 @@ uint32_t komodo_segid32(char *coinaddr); {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} \ }; -int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold,int32_t top,std::vector > &vaddr) +int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top, bool fRPC ,std::vector > &vaddr, UniValue &ret) { int64_t total = 0; int64_t totalAddresses = 0; std::string address; - int64_t utxos = 0; int64_t ignoredAddresses = 0; + int64_t utxos = 0; int64_t ignoredAddresses = 0, cryptoConditionsUTXOs = 0, cryptoConditionsTotals = 0; DECLARE_IGNORELIST boost::scoped_ptr iter(NewIterator()); std::map addressAmounts; @@ -482,6 +482,12 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold,int32_t top,std::vector GetValue(nValue); getAddressFromIndex(indexKey.type, indexKey.hashBytes, address); + if ( indexKey.type == 3 ) + { + cryptoConditionsUTXOs++; + cryptoConditionsTotals += nValue; + continue; + } if ( nValue > dustthreshold ) { std::map ::iterator ignored = ignoredMap.find(address); @@ -508,7 +514,7 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold,int32_t top,std::vector element : addressAmounts) vaddr.push_back( make_pair(element.second, element.first) ); std::sort(vaddr.rbegin(), vaddr.rend()); int topN = 0; for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) { - //obj.push_back( make_pair("addr", it->second.c_str() ) ); - //char amount[32]; - //sprintf(amount, "%.8f", (double) it->first / COIN); - //obj.push_back( make_pair("amount", amount) ); - //obj.push_back( make_pair("segid",(int32_t)komodo_segid32((char *)it->second.c_str()) & 0x3f) ); - //addressesSorted.push_back(obj); total += it->first; topN++; // If requested, only show top N addresses in output JSON if ( top == topN ) break; } + // this is for the snapshot RPC, you can skip this by passing false to the 3rd arugment. + // Still needs UniValue defined to use SnapShot2 though, can this be changed to just pass 0? + // I tried to make it a pointer, and check if the pointer was 0, but UniValue class didnt like that. + if ( fRPC ) + { + // Total amount in this snapshot, which is less than circulating supply if top parameter is used + ret.push_back(make_pair("total", (double) total / COIN )); + // Average amount in each address of this snapshot + ret.push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); + // Total number of utxos processed in this snaphot + ret.push_back(make_pair("utxos", utxos)); + // Total number of addresses in this snaphot + ret.push_back(make_pair("total_addresses", top ? top : totalAddresses )); + // Total number of ignored addresses in this snaphot + ret.push_back(make_pair("ignored_addresses", ignoredAddresses)); + // Total number of crypto condition utxos we skipped + ret.push_back(make_pair("skipped_cc_utxos", cryptoConditionsUTXOs)); + // Total value of skipped crypto condition utxos + ret.push_back(make_pair("cc_utxo_value", (double) cryptoConditionsTotals / COIN)); + // The snapshot finished at this block height + ret.push_back(make_pair("ending_height", chainActive.Height())); + } return(topN); } UniValue CBlockTreeDB::Snapshot(int top) { - int64_t total = 0; int64_t totalAddresses = 0; std::string address; - int64_t utxos = 0; int64_t ignoredAddresses = 0; - DECLARE_IGNORELIST - boost::scoped_ptr iter(NewIterator()); - std::map addressAmounts; + int topN = 0; std::vector > vaddr; UniValue result(UniValue::VOBJ); - result.push_back(Pair("start_time", (int) time(NULL))); - - /* std::map ignoredMap = { - {"RReUxSs5hGE39ELU23DfydX8riUuzdrHAE", 1}, - {"RMUF3UDmzWFLSKV82iFbMaqzJpUnrWjcT4", 1}, - {"RA5imhVyJa7yHhggmBytWuDr923j2P1bxx", 1}, - {"RBM5LofZFodMeewUzoMWcxedm3L3hYRaWg", 1}, - {"RAdcko2d94TQUcJhtFHZZjMyWBKEVfgn4J", 1}, - {"RLzUaZ934k2EFCsAiVjrJqM8uU1vmMRFzk", 1}, - {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, - {"RUDrX1v5toCsJMUgtvBmScKjwCB5NaR8py", 1}, - {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, - {"RRvwmbkxR5YRzPGL5kMFHMe1AH33MeD8rN", 1}, - {"RQLQvSgpPAJNPgnpc8MrYsbBhep95nCS8L", 1}, - {"RK8JtBV78HdvEPvtV5ckeMPSTojZPzHUTe", 1}, - {"RHVs2KaCTGUMNv3cyWiG1jkEvZjigbCnD2", 1}, - {"RE3SVaDgdjkRPYA6TRobbthsfCmxQedVgF", 1}, - {"RW6S5Lw5ZCCvDyq4QV9vVy7jDHfnynr5mn", 1}, - {"RTkJwAYtdXXhVsS3JXBAJPnKaBfMDEswF8", 1}, - {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} //Burnaddress for null privkey - };*/ - - int64_t startingHeight = chainActive.Height(); - //fprintf(stderr, "Starting snapshot at height %lli\n", startingHeight); - for (iter->SeekToLast(); iter->Valid(); iter->Prev()) - { - boost::this_thread::interruption_point(); - try - { - std::vector slKey = std::vector(); - pair keyObj; - iter->GetKey(keyObj); - - char chType = keyObj.first; - CAddressIndexIteratorKey indexKey = keyObj.second; - - //fprintf(stderr, "chType=%d\n", chType); - if (chType == DB_ADDRESSUNSPENTINDEX) - { - try { - CAmount nValue; - iter->GetValue(nValue); - - getAddressFromIndex(indexKey.type, indexKey.hashBytes, address); - - if (nValue > 0) { - std::map ::iterator ignored = ignoredMap.find(address); - if (ignored != ignoredMap.end()) { - fprintf(stderr,"ignoring %s\n", address.c_str()); - ignoredAddresses++; - continue; - } - - std::map ::iterator pos = addressAmounts.find(address); - if (pos == addressAmounts.end()) { - // insert new address + utxo amount - //fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue); - addressAmounts[address] = nValue; - totalAddresses++; - } else { - // update unspent tally for this address - //fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue); - addressAmounts[address] += nValue; - } - //fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN); - // total += nValue; - utxos++; - } else { - fprintf(stderr,"ignoring amount=0 UTXO for %s\n", address.c_str()); - } - } catch (const std::exception& e) { - fprintf(stderr, "DONE %s: LevelDB addressindex exception! - %s\n", __func__, e.what()); - break; - } - } - } catch (const std::exception& e) { - fprintf(stderr, "DONE reading index entries\n"); - break; - } - } - - UniValue addresses(UniValue::VARR); - //fprintf(stderr, "total=%f, totalAddresses=%li, utxos=%li, ignored=%li\n", (double) total / COIN, totalAddresses, utxos, ignoredAddresses); - - for (std::pair element : addressAmounts) { - vaddr.push_back( make_pair(element.second, element.first) ); - } - std::sort(vaddr.rbegin(), vaddr.rend()); - - UniValue obj(UniValue::VOBJ); UniValue addressesSorted(UniValue::VARR); - int topN = 0; - for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) + result.push_back(Pair("start_time", (int) time(NULL))); + if ( Snapshot2(0,top,true,vaddr,result) != 0 ) { - UniValue obj(UniValue::VOBJ); - obj.push_back( make_pair("addr", it->second.c_str() ) ); - char amount[32]; - sprintf(amount, "%.8f", (double) it->first / COIN); - obj.push_back( make_pair("amount", amount) ); - obj.push_back( make_pair("segid",(int32_t)komodo_segid32((char *)it->second.c_str()) & 0x3f) ); - total += it->first; - addressesSorted.push_back(obj); - topN++; - // If requested, only show top N addresses in output JSON - if (top == topN) - break; - } - - if (top) - totalAddresses = top; - - if (totalAddresses > 0) { - // Array of all addreses with balances + for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) + { + UniValue obj(UniValue::VOBJ); + obj.push_back( make_pair("addr", it->second.c_str() ) ); + char amount[32]; + sprintf(amount, "%.8f", (double) it->first / COIN); + obj.push_back( make_pair("amount", amount) ); + obj.push_back( make_pair("segid",(int32_t)komodo_segid32((char *)it->second.c_str()) & 0x3f) ); + addressesSorted.push_back(obj); + topN++; + // If requested, only show top N addresses in output JSON + if ( top == topN ) + break; + } + // Array of all addreses with balances result.push_back(make_pair("addresses", addressesSorted)); - // Total amount in this snapshot, which is less than circulating supply if top parameter is used - result.push_back(make_pair("total", (double) total / COIN )); - // Average amount in each address of this snapshot - result.push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); - } - // Total number of utxos processed in this snaphot - result.push_back(make_pair("utxos", utxos)); - // Total number of addresses in this snaphot - result.push_back(make_pair("total_addresses", totalAddresses)); - // Total number of ignored addresses in this snaphot - result.push_back(make_pair("ignored_addresses", ignoredAddresses)); - // The snapshot began at this block height - result.push_back(make_pair("start_height", startingHeight)); - // The snapshot finished at this block height - result.push_back(make_pair("ending_height", chainActive.Height())); + } else result.push_back(make_pair("error", "problem doing snapshot")); return(result); } diff --git a/src/txdb.h b/src/txdb.h index 9b80b922b..8b98994a2 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -116,7 +116,7 @@ public: bool LoadBlockIndexGuts(); bool blockOnchainActive(const uint256 &hash); UniValue Snapshot(int top); - int32_t Snapshot2(int64_t dustthreshold,int32_t top,std::vector > &vaddr); + int32_t Snapshot2(int64_t dustthreshold, int32_t top, bool fRPC ,std::vector > &vaddr, UniValue &ret); }; #endif // BITCOIN_TXDB_H From df118e7c8ff255a3fa3ecf4d14b6e0b8ba9fff62 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Sun, 7 Apr 2019 23:16:25 +0800 Subject: [PATCH 756/787] fix json output --- src/txdb.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/txdb.cpp b/src/txdb.cpp index c8721c92a..e5c260e4f 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -548,7 +548,7 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top, bool fRPC ,s if ( fRPC ) { // Total amount in this snapshot, which is less than circulating supply if top parameter is used - ret.push_back(make_pair("total", (double) total / COIN )); + ret.push_back(make_pair("total", (double) (total+cryptoConditionsTotals)/ COIN )); // Average amount in each address of this snapshot ret.push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); // Total number of utxos processed in this snaphot @@ -561,6 +561,8 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top, bool fRPC ,s ret.push_back(make_pair("skipped_cc_utxos", cryptoConditionsUTXOs)); // Total value of skipped crypto condition utxos ret.push_back(make_pair("cc_utxo_value", (double) cryptoConditionsTotals / COIN)); + // total of all the address's, does not count coins in CC vouts. + ret.push_back(make_pair("address_total", (double) total/ COIN )); // The snapshot finished at this block height ret.push_back(make_pair("ending_height", chainActive.Height())); } From 7c86c40d7153ae22b7278de4b88e38db5128b350 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Sun, 7 Apr 2019 23:54:44 +0800 Subject: [PATCH 757/787] description comment --- src/txdb.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/txdb.cpp b/src/txdb.cpp index e5c260e4f..93000d9fb 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -548,6 +548,7 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top, bool fRPC ,s if ( fRPC ) { // Total amount in this snapshot, which is less than circulating supply if top parameter is used + // Use the address_total for a total of all address included when using top parameter. ret.push_back(make_pair("total", (double) (total+cryptoConditionsTotals)/ COIN )); // Average amount in each address of this snapshot ret.push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); From 247a2ccc7f7a03e5ea7e91c45da8d5eb5d9986a6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Sun, 7 Apr 2019 06:15:43 -1100 Subject: [PATCH 758/787] Sync --- src/cc/pegs.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index 341e34c58..0c9613df3 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -17,8 +17,7 @@ /* - -*/ + */ // start of consensus code From 07f9635bd0e2ea46208bb4c8cb1f12d43611c39f Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 8 Apr 2019 02:41:05 -1100 Subject: [PATCH 759/787] Comments --- src/cc/pegs.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index 0c9613df3..f6c9ae5b1 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -16,7 +16,52 @@ #include "CCPegs.h" /* +pegs CC is able to create a coin backed (by any supported coin with gateways CC deposits) and pegged to any synthetic price that is able to be calculated based on prices CC + + First, the prices CC needs to be understood, so the extensive comments at the top of ~/src/cc/prices.cpp needs to be understood. + + The second aspect is the ability to import coins, as used by the crosschain burn/import and the -ac_import chains. + + + + OK, now we are ready to describe the pegs CC. Let us imagine an -ac_import sidechain with KMD gateways CC. Now we have each native coin fungible with the real KMD via the gateways deposit/withdraw mechanism. Let us start with that and make a pegged and backed USD chain. + + + + Here the native coin is KMD, but we want USD, so there needs to be a way to convert the KMD amounts into USD amounts. Something like "KMDBTC, BTCUSD, *, 1" which is the prices CC syntax to calculate KMD/USD, which is exactly what we need. So now we can assume that we have a block by block usable KMD/USD price. implementationwise, there can be an -ac option like -ac_peg="KMDBTC, BTCUSD, *, 1" and in conjunction with -ac_import=KMD gateways CC sidechain, we now have a chain where deposit of KMD issues the correct USD coins and redeem of USD coins releases the correct number of KMD coins. + + Are we done yet? + + Not quite, as the prices of KMD will be quite volatile relative to USD, which is good during bull markets, not so happy during bear markets. There are 2 halves to this problem, how to deal with massive price increase (easy to solve), how to solve 90% price drop (a lot harder). + + In order to solve both, what is needed is an "account" based tracking which updates based on both price change, coins issued, payments made. So let us create an account that is based on a specific pubkey, where all relevant deposits, issuances, withdraws are tracked via appropriate vin/vout markers. + + Let us modify the USD chain above so that only 80% of the possible USD is issued and 20% held in reserve. This 80% should be at least some easily changeable #define, or even an -ac parameter. We want the issued coins to be released without any encumberances, but the residual 20% value is still controlled (owned) but the depositor. This account has the amount of KMD deposited and USD issued. At the moment of deposit, there will still be 20% equity left. Let us start with 1000 KMD deposit, $1.5 per KMD -> 800 KMD converted to 1200 USD into depositor pubkey and the account of (1000 KMD, -1200 USD) = 200 KMD or $300 equity. + + Now it becomes easy for the bull market case, which is to allow (for a fee like 1%) issuance of more USD as the equity increases, so let us imagine KMD at $10: + + (1000 KMD, -1200 USD, 200KMD reserve) -> $2000 equity, issue 80% -> $1600 using 160 KMD + (1000 KMD, -1200 USD, 200KMD reserve, -160KMD, issue $1600 USD, 40 KMD reserve) + + we have $2800 USD in circulation, 40 KMD reserve left against $10000 marketcap of the original deposit. It it easy to see that there are never any problems with lack of KMD to redeem the issued USD in a world where prices only go up. Total USD issuance can be limited by using a decentralized account tracking based on each deposit. + + What is evident though is that with the constantly changing price and the various times that all the various deposits issue USD, the global reserves are something that will be hard to predict and in fact needs to be specifically tracked. Let us combine all accounts exposure in to a global reserves factor. This factor will control various max/min/ allowed and fee percentages. + + Now we are prepared to handle the price goes down scenario. We can rely on the global equity/reserve ratio to be changing relatively slowly as the reference price is the smooted trustless oracles price. This means there will be enough blocks to adjust the global reserves percentage. What we need to do is liquidate specific positions that have the least reserves. + + What does liquidation mean? It means a specific account will be purchased at below its current value and the KMD withdrawn. Let us assume the price drops to $5: + + (1000 KMD, -1200 USD, 200KMD reserve, -160KMD, issue $1600 USD, 40 KMD reserve) 1000 KMD with 2800 USD issued so $2200 reserves. Let us assume it can be liquidated at a 10% discount, so for $2000 in addition to the $2800, the 5000 KMD is able to be withdrawn. This removes 4800 USD coins for 1000 KMD, which is a very low reserve amount of 4%. If a low reserve amount is removed from the system, then the global reserve amount must be improved. + + Now, we have a decentralized mechanism to handle the price going lower! Combined with the fully decentralized method new USD coins are issued, makes this argubably the first decentralized blockchain that is both backed and pegged. There is the reliance on the gateways CC multisig signers, so there is a fundamental federated trust for chains without intrinsic value. + + Also, notice that the flexibly syntax of prices CC allows to define pegs easily for virtually any type of synthetic, and all the ECB fiats can easily get a backed and pegged coin. + + Let us now consider how to enforce a peg onto a specific gateways CC token. If this can also be achieved, then a full DEX for all the different gateways CC supported coins can be created onto a single fiat denominated chain. + + + */ // start of consensus code From 74aa8a95364df9b26e18bbd961e0fdcfa5fd0fd6 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 8 Apr 2019 02:47:02 -1100 Subject: [PATCH 760/787] Update --- src/cc/pegs.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index f6c9ae5b1..218f4c68e 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -53,6 +53,9 @@ pegs CC is able to create a coin backed (by any supported coin with gateways CC (1000 KMD, -1200 USD, 200KMD reserve, -160KMD, issue $1600 USD, 40 KMD reserve) 1000 KMD with 2800 USD issued so $2200 reserves. Let us assume it can be liquidated at a 10% discount, so for $2000 in addition to the $2800, the 5000 KMD is able to be withdrawn. This removes 4800 USD coins for 1000 KMD, which is a very low reserve amount of 4%. If a low reserve amount is removed from the system, then the global reserve amount must be improved. + In addition to the global reserves calculation, there needs to be a trigger percentage that enables positions to be liquidated. We also want to liquidate the worst positions, so in addition to the trigger percentage, there should be a liquidation threshold, and the liquidator would need to find 3 or more better positions that are beyond the liquidation threshold, to be able to liquidate. This will get us to at most 3 accounts that could be liquidated but are not able to, so some method to allow those to also be liquidated. The liquidating nodes are making instant profits, so they should be expected to do whatever blockchain scanning and proving to make things easy for the rest of the nodes + + Now, we have a decentralized mechanism to handle the price going lower! Combined with the fully decentralized method new USD coins are issued, makes this argubably the first decentralized blockchain that is both backed and pegged. There is the reliance on the gateways CC multisig signers, so there is a fundamental federated trust for chains without intrinsic value. Also, notice that the flexibly syntax of prices CC allows to define pegs easily for virtually any type of synthetic, and all the ECB fiats can easily get a backed and pegged coin. From 01be71fd44280d2c23d348b775ddf697f5a27759 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 8 Apr 2019 05:29:47 -1100 Subject: [PATCH 761/787] Unsolved issue --- src/cc/pegs.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index 218f4c68e..b83279276 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -53,8 +53,15 @@ pegs CC is able to create a coin backed (by any supported coin with gateways CC (1000 KMD, -1200 USD, 200KMD reserve, -160KMD, issue $1600 USD, 40 KMD reserve) 1000 KMD with 2800 USD issued so $2200 reserves. Let us assume it can be liquidated at a 10% discount, so for $2000 in addition to the $2800, the 5000 KMD is able to be withdrawn. This removes 4800 USD coins for 1000 KMD, which is a very low reserve amount of 4%. If a low reserve amount is removed from the system, then the global reserve amount must be improved. - In addition to the global reserves calculation, there needs to be a trigger percentage that enables positions to be liquidated. We also want to liquidate the worst positions, so in addition to the trigger percentage, there should be a liquidation threshold, and the liquidator would need to find 3 or more better positions that are beyond the liquidation threshold, to be able to liquidate. This will get us to at most 3 accounts that could be liquidated but are not able to, so some method to allow those to also be liquidated. The liquidating nodes are making instant profits, so they should be expected to do whatever blockchain scanning and proving to make things easy for the rest of the nodes + In addition to the global reserves calculation, there needs to be a trigger percentage that enables positions to be liquidated. We also want to liquidate the worst positions, so in addition to the trigger percentage, there should be a liquidation threshold, and the liquidator would need to find 3 or more better positions that are beyond the liquidation threshold, to be able to liquidate. This will get us to at most 3 accounts that could be liquidated but are not able to, so some method to allow those to also be liquidated. The liquidating nodes are making instant profits, so they should be expected to do whatever blockchain scanning and proving to make things easy for the rest of the nodes. + One last issue is the normal redemption case where we are not liquidating. In this case, it should be done at the current marketprice, should improve the global reserves metrics and not cause anybody whose position was modified to have cause for complaint. Ideally, there would be an account that has the identical to the global reserve percentage and also at the same price as current marketprice, but this is not realistic, so we need to identify classes of accounts and consider which ones can be fully or partially liquidated to satisfy the constraints. + + looking at our example account: + (1000 KMD, -1200 USD, 200KMD reserve, -160KMD, issue $1600 USD, 40 KMD reserve) + + what sort of non-liquidation withdraw would be acceptable? if the base amount 1000 KMD is reduced along with USD owed, then the reserve status will go up for the account. but that would seem to allow extra USD to be able to be issued. there should be no disadvantage from funding a withdraw, but also not any large advantage. it needs to be a neutral event.... + UNSOLVED selection, account adjustment issue Now, we have a decentralized mechanism to handle the price going lower! Combined with the fully decentralized method new USD coins are issued, makes this argubably the first decentralized blockchain that is both backed and pegged. There is the reliance on the gateways CC multisig signers, so there is a fundamental federated trust for chains without intrinsic value. From d8edaa38ecf3b9aa3be5470c89d1ec508bcc3380 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 9 Apr 2019 01:36:30 +0800 Subject: [PATCH 762/787] make wallet filter work for testnet notary --- src/wallet/wallet.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 1208a77f2..003404c97 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1772,7 +1772,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0) { // wallet filter for notary nodes. Disabled! Can be reenabled or customised for any specific use, pools could also use this to prevent wallet dwy attack. - if ( 0 && !tx.IsCoinBase() && !NOTARY_ADDRESS.empty() && IS_STAKED_NOTARY > -1 ) + if ( !tx.IsCoinBase() ) //&& !NOTARY_ADDRESS.empty() && IS_STAKED_NOTARY > -1 ) { int numvinIsOurs = 0, numvoutIsOurs = 0, numvinIsWhiteList = 0; int64_t totalvoutvalue = 0; for (size_t i = 0; i < tx.vin.size(); i++) @@ -1782,7 +1782,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl { if (ExtractDestination(txin.vout[tx.vin[i].prevout.n].scriptPubKey, address)) { - if ( CBitcoinAddress(address).ToString() == NOTARY_ADDRESS ) + if ( CBitcoinAddress(address).ToString() == "RXhapCShoqNeWytLXeWR2wX7m5xfNYfxNx" ) numvinIsOurs++; if ( !WHITELIST_ADDRESS.empty() ) { @@ -1803,6 +1803,11 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl // Count vouts, check if OUR notary address is the receiver. if ( numvinIsOurs == 0 && numvinIsWhiteList == 0 ) { + // if MIN_RECV_SATS is 0, we are on full lock down mode, accept NO transactions. + if ( 1 ) //MIN_RECV_SATS == 0 ) { + fprintf(stderr, "This node is on full lock down all txs are ignored! \n"); + return false; + } for (size_t i = 0; i < tx.vout.size() ; i++) { CTxDestination address2; @@ -1815,11 +1820,6 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl } } } - // if MIN_RECV_SATS is 0, we are on full lock down mode, accept NO transactions. - if ( MIN_RECV_SATS == 0 ) { - fprintf(stderr, "This node is on full lock down all txs are ignored! \n"); - return false; - } // If no vouts are to the notary address we will ignore them. if ( numvoutIsOurs == 0 ) { fprintf(stderr, "Received transaction to address other than notary address, ignored! \n"); From 7e74cb117ca964bf7a415656b2e622edc95d3599 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 9 Apr 2019 01:39:38 +0800 Subject: [PATCH 763/787] fix --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 003404c97..592a22c34 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1804,7 +1804,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl if ( numvinIsOurs == 0 && numvinIsWhiteList == 0 ) { // if MIN_RECV_SATS is 0, we are on full lock down mode, accept NO transactions. - if ( 1 ) //MIN_RECV_SATS == 0 ) { + if ( 1 ) { //MIN_RECV_SATS == 0 ) { fprintf(stderr, "This node is on full lock down all txs are ignored! \n"); return false; } From faea9232848f80892130f6f3d76cc0372560ed98 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 9 Apr 2019 05:10:52 +0800 Subject: [PATCH 764/787] set notary address from -pubkey filter still default to on. --- src/wallet/wallet.cpp | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 592a22c34..c16720e60 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1751,6 +1751,8 @@ extern uint8_t NOTARY_PUBKEY33[33]; extern std::string NOTARY_ADDRESS,WHITELIST_ADDRESS; extern int32_t IS_STAKED_NOTARY; extern uint64_t MIN_RECV_SATS; +bool WALLET_FILTER = true; +#include "cc/CCinclude.h" bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) { @@ -1769,41 +1771,41 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl return false; } } + static std::string NotaryAddress; static bool didinit; + if ( !didinit && NotaryAddress.empty() && NOTARY_PUBKEY33[0] != 0 ) + { + didinit = true; + char Raddress[64]; + pubkey2addr((char *)Raddress,(uint8_t *)NOTARY_PUBKEY33); + NotaryAddress.assign(Raddress); + } if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0) { // wallet filter for notary nodes. Disabled! Can be reenabled or customised for any specific use, pools could also use this to prevent wallet dwy attack. - if ( !tx.IsCoinBase() ) //&& !NOTARY_ADDRESS.empty() && IS_STAKED_NOTARY > -1 ) + if ( !tx.IsCoinBase() && WALLET_FILTER && !NotaryAddress.empty() ) //&& !NOTARY_ADDRESS.empty() && IS_STAKED_NOTARY > -1 ) { - int numvinIsOurs = 0, numvoutIsOurs = 0, numvinIsWhiteList = 0; int64_t totalvoutvalue = 0; + int numvinIsOurs = 0, numvinIsWhiteList = 0; // numvoutIsOurs = 0, int64_t totalvoutvalue = 0; for (size_t i = 0; i < tx.vin.size(); i++) { uint256 hash; CTransaction txin; CTxDestination address; - if (GetTransaction(tx.vin[i].prevout.hash,txin,hash,false)) + if ( GetTransaction(tx.vin[i].prevout.hash,txin,hash,false) && ExtractDestination(txin.vout[tx.vin[i].prevout.n].scriptPubKey, address) ) { - if (ExtractDestination(txin.vout[tx.vin[i].prevout.n].scriptPubKey, address)) - { - if ( CBitcoinAddress(address).ToString() == "RXhapCShoqNeWytLXeWR2wX7m5xfNYfxNx" ) - numvinIsOurs++; - if ( !WHITELIST_ADDRESS.empty() ) - { - //fprintf(stderr, "white list address: %s recv address: %s\n", WHITELIST_ADDRESS.c_str(),CBitcoinAddress(address).ToString().c_str()); - if ( CBitcoinAddress(address).ToString() == WHITELIST_ADDRESS ) { - //fprintf(stderr, "whitlisted is set to true here.\n"); - numvinIsWhiteList++; - } - } - } + if ( CBitcoinAddress(address).ToString() == NotaryAddress ) + numvinIsOurs++; + if ( !WHITELIST_ADDRESS.empty() && CBitcoinAddress(address).ToString() == WHITELIST_ADDRESS ) + numvinIsWhiteList++; } } // Now we know if it was a tx sent to us, that wasnt from ourself or the whitelist address if set.. if ( numvinIsOurs != 0 ) - fprintf(stderr, "We sent from address: %s vins: %d\n",NOTARY_ADDRESS.c_str(),numvinIsOurs); + fprintf(stderr, "We sent from address: %s vins: %d\n",NotaryAddress.c_str(),numvinIsOurs); if ( numvinIsWhiteList != 0 ) fprintf(stderr, "We received from whitelisted address: %s\n",WHITELIST_ADDRESS.c_str()); // Count vouts, check if OUR notary address is the receiver. if ( numvinIsOurs == 0 && numvinIsWhiteList == 0 ) { - // if MIN_RECV_SATS is 0, we are on full lock down mode, accept NO transactions. + return false; + /*/ if MIN_RECV_SATS is 0, we are on full lock down mode, accept NO transactions. if ( 1 ) { //MIN_RECV_SATS == 0 ) { fprintf(stderr, "This node is on full lock down all txs are ignored! \n"); return false; @@ -1832,7 +1834,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl // average vout size is less than set minimum, default is 1 coin, we will ignore it fprintf(stderr, "ignored: %d vouts average size of %ld sats.\n",numvoutIsOurs, (long)avgVoutSize); return false; - } + } */ } } From a30fb4ae364d3040e352dc154d197c3a6ba33c8f Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 9 Apr 2019 05:38:10 +0800 Subject: [PATCH 765/787] test --- src/wallet/wallet.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c16720e60..ac6b7d6c0 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1778,6 +1778,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl char Raddress[64]; pubkey2addr((char *)Raddress,(uint8_t *)NOTARY_PUBKEY33); NotaryAddress.assign(Raddress); + fprintf(stderr, "1 notary address.%s\n", NotaryAddress.c_str()); } if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0) { @@ -1790,12 +1791,14 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl uint256 hash; CTransaction txin; CTxDestination address; if ( GetTransaction(tx.vin[i].prevout.hash,txin,hash,false) && ExtractDestination(txin.vout[tx.vin[i].prevout.n].scriptPubKey, address) ) { + fprintf(stderr, "2 notary address.%s address.%s\n", NotaryAddress.c_str(), address.c_str()); if ( CBitcoinAddress(address).ToString() == NotaryAddress ) numvinIsOurs++; if ( !WHITELIST_ADDRESS.empty() && CBitcoinAddress(address).ToString() == WHITELIST_ADDRESS ) numvinIsWhiteList++; } } + fprintf(stderr, "3 notary address.%s\n", NotaryAddress.c_str()); // Now we know if it was a tx sent to us, that wasnt from ourself or the whitelist address if set.. if ( numvinIsOurs != 0 ) fprintf(stderr, "We sent from address: %s vins: %d\n",NotaryAddress.c_str(),numvinIsOurs); From 0d587ccc31697e1a758d27e9bd4da10aa3093a60 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 9 Apr 2019 05:39:34 +0800 Subject: [PATCH 766/787] fix --- src/wallet/wallet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index ac6b7d6c0..279b149e8 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1791,7 +1791,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl uint256 hash; CTransaction txin; CTxDestination address; if ( GetTransaction(tx.vin[i].prevout.hash,txin,hash,false) && ExtractDestination(txin.vout[tx.vin[i].prevout.n].scriptPubKey, address) ) { - fprintf(stderr, "2 notary address.%s address.%s\n", NotaryAddress.c_str(), address.c_str()); + fprintf(stderr, "2 notary address.%s address.%s\n", NotaryAddress.c_str(), CBitcoinAddress(address).ToString().c_str()); if ( CBitcoinAddress(address).ToString() == NotaryAddress ) numvinIsOurs++; if ( !WHITELIST_ADDRESS.empty() && CBitcoinAddress(address).ToString() == WHITELIST_ADDRESS ) From 6f5e339884dcee6c825c59de2aa339e2db2be1cb Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 9 Apr 2019 05:50:06 +0800 Subject: [PATCH 767/787] fix --- src/wallet/wallet.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 279b149e8..f4a52cf24 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1778,7 +1778,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl char Raddress[64]; pubkey2addr((char *)Raddress,(uint8_t *)NOTARY_PUBKEY33); NotaryAddress.assign(Raddress); - fprintf(stderr, "1 notary address.%s\n", NotaryAddress.c_str()); + fprintf(stderr, "notary address set to %s\n", NotaryAddress.c_str()); } if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0) { @@ -1791,14 +1791,12 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl uint256 hash; CTransaction txin; CTxDestination address; if ( GetTransaction(tx.vin[i].prevout.hash,txin,hash,false) && ExtractDestination(txin.vout[tx.vin[i].prevout.n].scriptPubKey, address) ) { - fprintf(stderr, "2 notary address.%s address.%s\n", NotaryAddress.c_str(), CBitcoinAddress(address).ToString().c_str()); if ( CBitcoinAddress(address).ToString() == NotaryAddress ) numvinIsOurs++; if ( !WHITELIST_ADDRESS.empty() && CBitcoinAddress(address).ToString() == WHITELIST_ADDRESS ) numvinIsWhiteList++; } } - fprintf(stderr, "3 notary address.%s\n", NotaryAddress.c_str()); // Now we know if it was a tx sent to us, that wasnt from ourself or the whitelist address if set.. if ( numvinIsOurs != 0 ) fprintf(stderr, "We sent from address: %s vins: %d\n",NotaryAddress.c_str(),numvinIsOurs); From e101aa3f9bf3e5e831237dd5519d06a93c0a040b Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 9 Apr 2019 06:10:02 +0800 Subject: [PATCH 768/787] make -whitelistaddress= activate wallet filter --- src/wallet/wallet.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index f4a52cf24..35edc24c6 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1751,7 +1751,6 @@ extern uint8_t NOTARY_PUBKEY33[33]; extern std::string NOTARY_ADDRESS,WHITELIST_ADDRESS; extern int32_t IS_STAKED_NOTARY; extern uint64_t MIN_RECV_SATS; -bool WALLET_FILTER = true; #include "cc/CCinclude.h" bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) @@ -1783,7 +1782,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0) { // wallet filter for notary nodes. Disabled! Can be reenabled or customised for any specific use, pools could also use this to prevent wallet dwy attack. - if ( !tx.IsCoinBase() && WALLET_FILTER && !NotaryAddress.empty() ) //&& !NOTARY_ADDRESS.empty() && IS_STAKED_NOTARY > -1 ) + if ( !tx.IsCoinBase() && !WHITELIST_ADDRESS.empty() && !NotaryAddress.empty() ) //&& !NOTARY_ADDRESS.empty() && IS_STAKED_NOTARY > -1 ) { int numvinIsOurs = 0, numvinIsWhiteList = 0; // numvoutIsOurs = 0, int64_t totalvoutvalue = 0; for (size_t i = 0; i < tx.vin.size(); i++) @@ -1793,7 +1792,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl { if ( CBitcoinAddress(address).ToString() == NotaryAddress ) numvinIsOurs++; - if ( !WHITELIST_ADDRESS.empty() && CBitcoinAddress(address).ToString() == WHITELIST_ADDRESS ) + if ( CBitcoinAddress(address).ToString() == WHITELIST_ADDRESS ) numvinIsWhiteList++; } } From 2e19349105ca2813646a0240417635cba7ee9a36 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 8 Apr 2019 21:54:24 -1100 Subject: [PATCH 769/787] Redeem solution --- src/cc/pegs.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index b83279276..00d76d8fd 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -61,7 +61,10 @@ pegs CC is able to create a coin backed (by any supported coin with gateways CC (1000 KMD, -1200 USD, 200KMD reserve, -160KMD, issue $1600 USD, 40 KMD reserve) what sort of non-liquidation withdraw would be acceptable? if the base amount 1000 KMD is reduced along with USD owed, then the reserve status will go up for the account. but that would seem to allow extra USD to be able to be issued. there should be no disadvantage from funding a withdraw, but also not any large advantage. it needs to be a neutral event.... - UNSOLVED selection, account adjustment issue + + One solution is to allow for the chance for any account to be liquidated, but the equity compensated for with a premium based on the account reserves. So in the above case, a premium of 5% on the 40KMD reserve is paid to liquidate its account. Instead of 5% premium, a lower 1% can be done if based on the MAX(correlated[daywindow],smoothed) so we get something that is close to the current marketprice. To prevent people taking advantage of the slowness of the smoothed price to adjust, there would need to be a one day delay in the withdraw. + + From a practical sense, it seems a day is a long time, so maybe having a way to pay a premium like 10%, or wait a day to get the MAX(correlated[daywindow],smoothed) price. This price "jumping" might also be taken advantage of in the deposit side, so similar to prices CC it seems good to have the MAX(correlated[daywindow],smoothed) method. Now, we have a decentralized mechanism to handle the price going lower! Combined with the fully decentralized method new USD coins are issued, makes this argubably the first decentralized blockchain that is both backed and pegged. There is the reliance on the gateways CC multisig signers, so there is a fundamental federated trust for chains without intrinsic value. From 237f11559425593a0a65554fc01d093f596b88dd Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Tue, 9 Apr 2019 18:53:32 +0800 Subject: [PATCH 770/787] Clean up some LABS related code, remove un-used/needed stuff. Adds actual working and feild tested wallet filter. Can set any amount of whitelist address's, in conf file or commandline args. fix daemon deadlock, when doing z_mergetoaddress and mining at same time (needs more testing on staking chains) Set z_mergtoaddress to use a 0 expiry height when not doing z-tx's. They were failing and peers were getting banned trying to broadcast them. --- src/komodo.h | 10 ------ src/komodo_defs.h | 4 +-- src/komodo_globals.h | 10 +++--- src/komodo_utils.h | 2 -- src/main.cpp | 3 -- src/miner.cpp | 35 +++++---------------- src/notaries_staked.cpp | 16 ++-------- src/notaries_staked.h | 1 - src/wallet/rpcwallet.cpp | 4 ++- src/wallet/wallet.cpp | 67 ++++++++++++---------------------------- 10 files changed, 40 insertions(+), 112 deletions(-) diff --git a/src/komodo.h b/src/komodo.h index 791adba33..3ceb29cc3 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -836,16 +836,6 @@ int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block) int8_t numSN = numStakedNotaries(tmp_pubkeys,staked_era); UpdateNotaryAddrs(tmp_pubkeys,numSN); STAKED_ERA = staked_era; - if ( NOTARYADDRS[0][0] != 0 && NOTARY_PUBKEY33[0] != 0 ) - { - if ( (IS_STAKED_NOTARY= updateStakedNotary()) > -1 ) - { - IS_KOMODO_NOTARY = 0; - if ( MIN_RECV_SATS == -1 ) - MIN_RECV_SATS = 100000000; - fprintf(stderr, "Staked Notary Protection Active! NotaryID.%d RADD.%s ERA.%d MIN_TX_VALUE.%lu \n",IS_STAKED_NOTARY,NOTARY_ADDRESS.c_str(),staked_era,MIN_RECV_SATS); - } - } lastStakedEra = staked_era; } } diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 49aa67081..9377ea276 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -80,9 +80,9 @@ extern int32_t VERUS_MIN_STAKEAGE; extern std::string DONATION_PUBKEY; extern uint8_t ASSETCHAINS_PRIVATE; extern int32_t USE_EXTERNAL_PUBKEY; +extern char NOTARYADDRS[64][64]; int tx_height( const uint256 &hash ); -extern char NOTARYADDRS[64][36]; -extern uint8_t NUM_NOTARIES; +extern std::vector vWhiteListAddress; void komodo_netevent(std::vector payload); int32_t komodo_priceind(char *symbol); diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 1c47e72e6..eccbb34f1 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -47,12 +47,14 @@ unsigned int WITNESS_CACHE_SIZE = _COINBASE_MATURITY+10; int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,IS_STAKED_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,STAKED_ERA,KOMODO_CONNECTING = -1,KOMODO_DEALERNODE,KOMODO_EXTRASATOSHI,ASSETCHAINS_FOUNDERS; int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JUMBLR_PAUSE = 1; -std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,NOTARY_ADDRESS,WHITELIST_ADDRESS,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB; -uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,NUM_NOTARIES,ASSETCHAINS_MARMARA; +std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,NOTARY_ADDRESS,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB; +uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,ASSETCHAINS_MARMARA; bool VERUS_MINTBLOCKS; std::vector Mineropret; +std::vector vWhiteListAddress; +char NOTARYADDRS[64][64]; -char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],ASSETCHAINS_USERPASS[4096],NOTARYADDRS[64][36]; +char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],ASSETCHAINS_USERPASS[4096]; uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_BEAMPORT,ASSETCHAINS_CODAPORT; uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC,KOMODO_STOPAT,KOMODO_DPOWCONFS = 1,STAKING_MIN_DIFF; uint32_t ASSETCHAINS_MAGIC = 2387029918; @@ -99,7 +101,7 @@ int32_t ASSETCHAINS_OVERWINTER = -1; uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE; int32_t ASSETCHAINS_STAKED; -uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY = 10,MIN_RECV_SATS,ASSETCHAINS_FOUNDERS_REWARD; +uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY = 10,ASSETCHAINS_FOUNDERS_REWARD; uint32_t KOMODO_INITDONE; char KMDUSERPASS[8192+512+1],BTCUSERPASS[8192]; uint16_t KMD_PORT = 7771,BITCOIND_RPCPORT = 7771; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 2d0fe4f4e..043d7282e 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1677,8 +1677,6 @@ void komodo_args(char *argv0) fprintf(stderr, "Cannot be STAKED and KMD notary at the same time!\n"); exit(0); } - MIN_RECV_SATS = GetArg("-mintxvalue",-1); - WHITELIST_ADDRESS = GetArg("-whitelistaddress",""); memset(ccenables,0,sizeof(ccenables)); memset(disablebits,0,sizeof(disablebits)); if ( GetBoolArg("-gen", false) != 0 ) diff --git a/src/main.cpp b/src/main.cpp index 8fbc0f1dd..faa9e6a9c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1270,9 +1270,6 @@ bool CheckTransaction(uint32_t tiptime,const CTransaction& tx, CValidationState } } -extern char NOTARYADDRS[64][36]; -extern uint8_t NUM_NOTARIES; - int32_t komodo_isnotaryvout(char *coinaddr) // from ac_private chains only { static int32_t didinit; static char notaryaddrs[sizeof(Notaries_elected1)/sizeof(*Notaries_elected1) + 1][64]; diff --git a/src/miner.cpp b/src/miner.cpp index f131bc90e..54b18195a 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -224,8 +224,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 CBlockIndex* pindexPrev = 0; { - ENTER_CRITICAL_SECTION(cs_main); - ENTER_CRITICAL_SECTION(mempool.cs); + LOCK2(cs_main,mempool.cs); pindexPrev = chainActive.LastTip(); const int nHeight = pindexPrev->GetHeight() + 1; const Consensus::Params &consensusParams = chainparams.GetConsensus(); @@ -564,11 +563,10 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 //LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x stake.%i\n", nBlockSize,blocktime,pblock->nBits,isStake); if ( ASSETCHAINS_SYMBOL[0] != 0 && isStake ) { - LEAVE_CRITICAL_SECTION(cs_main); - LEAVE_CRITICAL_SECTION(mempool.cs); uint64_t txfees,utxovalue; uint32_t txtime; uint256 utxotxid; int32_t i,siglen,numsigs,utxovout; uint8_t utxosig[512],*ptr; CMutableTransaction txStaked = CreateNewContextualCMutableTransaction(Params().GetConsensus(), stakeHeight); - + LEAVE_CRITICAL_SECTION(cs_main); + LEAVE_CRITICAL_SECTION(mempool.cs); if (ASSETCHAINS_LWMAPOS != 0) { uint32_t nBitsPOS; @@ -593,7 +591,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 { sleep(1); if ( (rand() % 100) < 1 ) - fprintf(stderr, "%u seconds until elegible, waiting.\n", blocktime-((uint32_t)GetAdjustedTime()+57)); + fprintf(stderr, "%u seconds until elegible, waiting...\n", blocktime-((uint32_t)GetAdjustedTime()+57)); if ( chainActive.LastTip()->GetHeight() >= stakeHeight ) { fprintf(stderr, "Block Arrived, reset staking loop.\n"); @@ -603,7 +601,8 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 return(0); } } - + ENTER_CRITICAL_SECTION(cs_main); + ENTER_CRITICAL_SECTION(mempool.cs); if ( siglen > 0 ) { CAmount txfees; @@ -616,7 +615,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 nFees += txfees; pblock->nTime = blocktime; //printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeight()+1,blocktime,(uint32_t)(GetAdjustedTime() - (blocktime-13))); - } else return(0); //fprintf(stderr,"no utxos eligible for staking\n"); + } else return(0); //fprintf(stderr,"no utxos eligible for staking\n"); } // Create coinbase tx @@ -688,11 +687,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 if (scriptPubKeyIn.IsPayToScriptHash() || scriptPubKeyIn.IsPayToCryptoCondition()) { fprintf(stderr,"CreateNewBlock: attempt to add timelock to pay2sh or pay2cc\n"); - if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) ) - { - LEAVE_CRITICAL_SECTION(cs_main); - LEAVE_CRITICAL_SECTION(mempool.cs); - } return 0; } @@ -714,11 +708,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 if ( totalsats == 0 ) { fprintf(stderr, "Could not create notary payment, trying again.\n"); - if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) ) - { - LEAVE_CRITICAL_SECTION(cs_main); - LEAVE_CRITICAL_SECTION(mempool.cs); - } return(0); } fprintf(stderr, "Created notary payment coinbase totalsat.%lu\n",totalsats); @@ -789,11 +778,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 else { fprintf(stderr,"error adding notaryvin, need to create 0.0001 utxos\n"); - if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) ) - { - LEAVE_CRITICAL_SECTION(cs_main); - LEAVE_CRITICAL_SECTION(mempool.cs); - } return(0); } } @@ -808,11 +792,6 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 //fprintf(stderr,"valid\n"); } } - if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) ) - { - LEAVE_CRITICAL_SECTION(cs_main); - LEAVE_CRITICAL_SECTION(mempool.cs); - } //fprintf(stderr,"done new block\n"); return pblocktemplate.release(); } diff --git a/src/notaries_staked.cpp b/src/notaries_staked.cpp index a3278ac12..0b7c97ceb 100644 --- a/src/notaries_staked.cpp +++ b/src/notaries_staked.cpp @@ -4,11 +4,11 @@ #include "cc/CCinclude.h" #include -extern char NOTARYADDRS[64][36]; +extern char NOTARYADDRS[64][64]; extern std::string NOTARY_ADDRESS,NOTARY_PUBKEY; extern int32_t STAKED_ERA,IS_STAKED_NOTARY,IS_KOMODO_NOTARY; extern pthread_mutex_t staked_mutex; -extern uint8_t NOTARY_PUBKEY33[33],NUM_NOTARIES; +extern uint8_t NOTARY_PUBKEY33[33]; int8_t is_STAKED(const char *chain_name) { @@ -46,16 +46,6 @@ int32_t STAKED_era(int timestamp) return(0); }; -int8_t updateStakedNotary() { - std::string notaryname; - char Raddress[18]; uint8_t pubkey33[33]; - decode_hex(pubkey33,33,(char *)NOTARY_PUBKEY.c_str()); - pubkey2addr((char *)Raddress,(uint8_t *)pubkey33); - NOTARY_ADDRESS.clear(); - NOTARY_ADDRESS.assign(Raddress); - return(StakedNotaryID(notaryname,Raddress)); -} - int8_t StakedNotaryID(std::string ¬aryname, char *Raddress) { if ( STAKED_ERA != 0 ) { @@ -116,7 +106,6 @@ void UpdateNotaryAddrs(uint8_t pubkeys[64][33],int8_t numNotaries) { // null pubkeys, era 0. pthread_mutex_lock(&staked_mutex); memset(NOTARYADDRS,0,sizeof(NOTARYADDRS)); - NUM_NOTARIES = 0; pthread_mutex_unlock(&staked_mutex); } else @@ -125,7 +114,6 @@ void UpdateNotaryAddrs(uint8_t pubkeys[64][33],int8_t numNotaries) { pthread_mutex_lock(&staked_mutex); for (int i = 0; i builder; if (isToSaplingZaddr || saplingNoteInputs.size() > 0) { builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); - } + } else + contextualTx.nExpiryHeight = 0; // set non z-tx to have no expiry height. + // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); std::shared_ptr operation( diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 35edc24c6..9b2975db1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -38,6 +38,7 @@ #include "crypter.h" #include "coins.h" #include "zcash/zip32.h" +#include "cc/CCinclude.h" #include @@ -1747,11 +1748,6 @@ bool CWallet::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx) * pblock is optional, but should be provided if the transaction is known to be in a block. * If fUpdate is true, existing transactions will be updated. */ -extern uint8_t NOTARY_PUBKEY33[33]; -extern std::string NOTARY_ADDRESS,WHITELIST_ADDRESS; -extern int32_t IS_STAKED_NOTARY; -extern uint64_t MIN_RECV_SATS; -#include "cc/CCinclude.h" bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) { @@ -1777,14 +1773,20 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl char Raddress[64]; pubkey2addr((char *)Raddress,(uint8_t *)NOTARY_PUBKEY33); NotaryAddress.assign(Raddress); - fprintf(stderr, "notary address set to %s\n", NotaryAddress.c_str()); + vWhiteListAddress = mapMultiArgs["-whitelistaddress"]; + if ( !vWhiteListAddress.empty() ) + { + fprintf(stderr, "Activated Wallet Filter \n Notary Address: %s \n Adding whitelist address's:\n", NotaryAddress.c_str()); + for ( auto wladdr : vWhiteListAddress ) + fprintf(stderr, " %s\n", wladdr.c_str()); + } } if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0) { - // wallet filter for notary nodes. Disabled! Can be reenabled or customised for any specific use, pools could also use this to prevent wallet dwy attack. - if ( !tx.IsCoinBase() && !WHITELIST_ADDRESS.empty() && !NotaryAddress.empty() ) //&& !NOTARY_ADDRESS.empty() && IS_STAKED_NOTARY > -1 ) + // wallet filter for notary nodes. Enables by setting -whitelistaddress= as startup param or in conf file (works same as -addnode byut with R-address's) + if ( !tx.IsCoinBase() && !vWhiteListAddress.empty() && !NotaryAddress.empty() ) { - int numvinIsOurs = 0, numvinIsWhiteList = 0; // numvoutIsOurs = 0, int64_t totalvoutvalue = 0; + int numvinIsOurs = 0, numvinIsWhiteList = 0; for (size_t i = 0; i < tx.vin.size(); i++) { uint256 hash; CTransaction txin; CTxDestination address; @@ -1792,50 +1794,21 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl { if ( CBitcoinAddress(address).ToString() == NotaryAddress ) numvinIsOurs++; - if ( CBitcoinAddress(address).ToString() == WHITELIST_ADDRESS ) - numvinIsWhiteList++; - } - } - // Now we know if it was a tx sent to us, that wasnt from ourself or the whitelist address if set.. - if ( numvinIsOurs != 0 ) - fprintf(stderr, "We sent from address: %s vins: %d\n",NotaryAddress.c_str(),numvinIsOurs); - if ( numvinIsWhiteList != 0 ) - fprintf(stderr, "We received from whitelisted address: %s\n",WHITELIST_ADDRESS.c_str()); - // Count vouts, check if OUR notary address is the receiver. - if ( numvinIsOurs == 0 && numvinIsWhiteList == 0 ) - { - return false; - /*/ if MIN_RECV_SATS is 0, we are on full lock down mode, accept NO transactions. - if ( 1 ) { //MIN_RECV_SATS == 0 ) { - fprintf(stderr, "This node is on full lock down all txs are ignored! \n"); - return false; - } - for (size_t i = 0; i < tx.vout.size() ; i++) - { - CTxDestination address2; - if ( ExtractDestination(tx.vout[i].scriptPubKey, address2)) + for ( auto wladdr : vWhiteListAddress ) { - if ( CBitcoinAddress(address2).ToString() == NOTARY_ADDRESS ) + if ( CBitcoinAddress(address).ToString() == wladdr ) { - numvoutIsOurs++; - totalvoutvalue += tx.vout[i].nValue; + //fprintf(stderr, "We received from whitelisted address.%s\n", wladdr.c_str()); + numvinIsWhiteList++; } } } - // If no vouts are to the notary address we will ignore them. - if ( numvoutIsOurs == 0 ) { - fprintf(stderr, "Received transaction to address other than notary address, ignored! \n"); - return false; - } - fprintf(stderr, "address: %s received %ld sats from %d vouts.\n",NOTARY_ADDRESS.c_str(),totalvoutvalue,(int32_t)numvoutIsOurs); - // here we add calculation for number if vouts received, average size and determine if we accept them to wallet or not. - int64_t avgVoutSize = totalvoutvalue / numvoutIsOurs; - if ( avgVoutSize < MIN_RECV_SATS ) { - // average vout size is less than set minimum, default is 1 coin, we will ignore it - fprintf(stderr, "ignored: %d vouts average size of %ld sats.\n",numvoutIsOurs, (long)avgVoutSize); - return false; - } */ } + // Now we know if it was a tx sent to us, by either a whitelisted address, or ourself. + if ( numvinIsOurs != 0 ) + fprintf(stderr, "We sent from address: %s vins: %d\n",NotaryAddress.c_str(),numvinIsOurs); + if ( numvinIsOurs == 0 && numvinIsWhiteList == 0 ) + return false; } CWalletTx wtx(this,tx); From bce0b87e6f2735dff3c1137780cb6418da3798b8 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Wed, 10 Apr 2019 02:11:40 +0800 Subject: [PATCH 771/787] . --- src/komodo_bitcoind.h | 32 ++++++++++++---------- src/rpc/client.cpp | 1 + src/rpc/crosschain.cpp | 60 +++++++++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 15 deletions(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index e537df018..aa21896aa 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1917,33 +1917,37 @@ uint64_t komodo_notarypay(CMutableTransaction &txNew, std::vector &Notar return(total); } -uint64_t komodo_checknotarypay(CBlock *pblock,int32_t height) +bool GetNotarisationNotaries(uint8_t notarypubkeys[64][33], int8_t &numNN, const std::vector &vin, std::vector &NotarisationNotaries) { - std::vector NotarisationNotaries; - uint32_t timestamp = pblock->nTime; - int8_t numSN = 0; uint8_t notarypubkeys[64][33] = {0}; - numSN = komodo_notaries(notarypubkeys, height, timestamp); - - // No point going further, no notaries can be paid. - if ( notarypubkeys[0][0] == 0 ) - return(0); - uint8_t *script; int32_t scriptlen; - // Loop over the notarisation and extract the position of the participating notaries in the array of pukeys for this era. - BOOST_FOREACH(const CTxIn& txin, pblock->vtx[1].vin) + if ( notarypubkeys[0][0] == 0 ) + return false; + BOOST_FOREACH(const CTxIn& txin, vin) { uint256 hash; CTransaction tx1; if ( GetTransaction(txin.prevout.hash,tx1,hash,false) ) { - for (int8_t i = 0; i < numSN; i++) + for (int8_t i = 0; i < numNN; i++) { script = (uint8_t *)&tx1.vout[txin.prevout.n].scriptPubKey[0]; scriptlen = (int32_t)tx1.vout[txin.prevout.n].scriptPubKey.size(); if ( scriptlen == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && memcmp(script+1,notarypubkeys[i],33) == 0 ) NotarisationNotaries.push_back(i); } - } + } else return false; } + return true; +} + +uint64_t komodo_checknotarypay(CBlock *pblock,int32_t height) +{ + std::vector NotarisationNotaries; uint8_t *script; int32_t scriptlen; + uint64_t timestamp = pblock->nTime; + int8_t numSN = 0; uint8_t notarypubkeys[64][33] = {0}; + numSN = komodo_notaries(notarypubkeys, height, timestamp); + if ( !GetNotarisationNotaries(notarypubkeys, numSN, pblock->vtx[1].vin, NotarisationNotaries) ) + return(0); + // check a notary didnt sign twice (this would be an invalid notarisation later on and cause problems) std::set checkdupes( NotarisationNotaries.begin(), NotarisationNotaries.end() ); if ( checkdupes.size() != NotarisationNotaries.size() ) { diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 19910b25c..cab89092c 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -171,6 +171,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "assetchainproof", 1}, { "crosschainproof", 1}, { "getproofroot", 2}, + { "getNotarisationsForBlock", 0}, { "height_MoM", 1}, { "calc_MoM", 2}, }; diff --git a/src/rpc/crosschain.cpp b/src/rpc/crosschain.cpp index 710f99f09..72c24086f 100644 --- a/src/rpc/crosschain.cpp +++ b/src/rpc/crosschain.cpp @@ -33,6 +33,7 @@ #include "script/script_error.h" #include "script/sign.h" #include "script/standard.h" +#include "notaries_staked.h" #include "key_io.h" @@ -49,6 +50,7 @@ int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_ int32_t komodo_MoMoMdata(char *hexstr,int32_t hexsize,struct komodo_ccdataMoMoM *mdata,char *symbol,int32_t kmdheight,int32_t notarized_height); struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi); uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); +int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); extern std::string ASSETCHAINS_SELFIMPORT; uint256 Parseuint256(char *hexstr); @@ -406,7 +408,63 @@ UniValue selfimport(const UniValue& params, bool fHelp) return result; } +bool GetNotarisationNotaries(uint8_t notarypubkeys[64][33], int8_t &numNN, const std::vector &vin, std::vector &NotarisationNotaries); + UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) +{ + // TODO take timestamp as param, and loop blockindex to get starting/finish height. + if (fHelp || params.size() != 1) + throw runtime_error("getNotarisationsForBlock height\n\n" + "Takes a block height and returns notarisation information " + "within the block"); + + LOCK(cs_main); + int32_t height = params[0].get_int(); + if ( height < 0 || height > chainActive.Height() ) + throw runtime_error("height out of range.\n"); + + uint256 blockHash = chainActive[height]->GetBlockHash(); + + NotarisationsInBlock nibs; + GetBlockNotarisations(blockHash, nibs); + UniValue out(UniValue::VOBJ); + //out.push_back(make_pair("blocktime",(int))); + UniValue labs(UniValue::VARR); + UniValue kmd(UniValue::VARR); + // Gets KMD notaries on KMD... but LABS notaries on labs chains needs to be fixed so LABS are identified on KMD. + int8_t numNN = 0; uint8_t notarypubkeys[64][33] = {0}; + numNN = komodo_notaries(notarypubkeys, height, chainActive[height]->nTime); + + BOOST_FOREACH(const Notarisation& n, nibs) + { + UniValue item(UniValue::VOBJ); UniValue notaryarr(UniValue::VARR); std::vector NotarisationNotaries; + if ( is_STAKED(n.second.symbol) != 0 ) + continue; // for now just skip this... need to fetch diff pubkeys for these chains. labs.push_back(item); + uint256 hash; CTransaction tx; + if ( GetTransaction(n.first,tx,hash,false) ) + { + if ( !GetNotarisationNotaries(notarypubkeys, numNN, tx.vin, NotarisationNotaries) ) + continue; + if ( NotarisationNotaries.size() < numNN/5 ) + continue; + } + item.push_back(make_pair("txid", n.first.GetHex())); + item.push_back(make_pair("chain", n.second.symbol)); + item.push_back(make_pair("height", (int)n.second.height)); + item.push_back(make_pair("blockhash", n.second.blockHash.GetHex())); + item.push_back(make_pair("KMD_height", height)); // for when timstamp input is used. + + for ( auto notary : NotarisationNotaries ) + notaryarr.push_back(notary); + item.push_back(make_pair("notaries",notaryarr)); + kmd.push_back(item); + } + out.push_back(make_pair("KMD", kmd)); + //out.push_back(make_pair("LABS", labs)); + return out; +} + +/*UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error("getNotarisationsForBlock blockHash\n\n" @@ -426,7 +484,7 @@ UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) out.push_back(item); } return out; -} +}*/ UniValue scanNotarisationsDB(const UniValue& params, bool fHelp) From df79068ab65e64eb8d91cdfafcc5528560f40a12 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Wed, 10 Apr 2019 02:12:52 +0800 Subject: [PATCH 772/787] y --- src/notarystats.py | 99 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 src/notarystats.py diff --git a/src/notarystats.py b/src/notarystats.py new file mode 100644 index 000000000..cc67ad10e --- /dev/null +++ b/src/notarystats.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +""" +build komodod from here: https://github.com/blackjok3rtt/komodo/tree/nota_stats +sudo apt-get install python3-dev +sudo apt-get install python3 libgnutls28-dev libssl-dev +sudo apt-get install python3-pip +pip3 install setuptools +pip3 install wheel +pip3 install base58 slick-bitcoinrpc +./notarystats.py +------------------------------------------------ +""" +import platform +import os +import re +import sys +import time +import pprint +from slickrpc import Proxy + +# fucntion to define rpc_connection +def def_credentials(chain): + rpcport = ''; + operating_system = platform.system() + if operating_system == 'Darwin': + ac_dir = os.environ['HOME'] + '/Library/Application Support/Komodo' + elif operating_system == 'Linux': + ac_dir = os.environ['HOME'] + '/.komodo' + elif operating_system == 'Windows': + ac_dir = '%s/komodo/' % os.environ['APPDATA'] + if chain == 'KMD': + coin_config_file = str(ac_dir + '/komodo.conf') + else: + coin_config_file = str(ac_dir + '/' + chain + '/' + chain + '.conf') + with open(coin_config_file, 'r') as f: + for line in f: + l = line.rstrip() + if re.search('rpcuser', l): + rpcuser = l.replace('rpcuser=', '') + elif re.search('rpcpassword', l): + rpcpassword = l.replace('rpcpassword=', '') + elif re.search('rpcport', l): + rpcport = l.replace('rpcport=', '') + if len(rpcport) == 0: + if chain == 'KMD': + rpcport = 7771 + else: + print("rpcport not in conf file, exiting") + print("check " + coin_config_file) + exit(1) + return (Proxy("http://%s:%s@127.0.0.1:%d" % (rpcuser, rpcpassword, int(rpcport)))) + + +rpc = def_credentials('KMD') +pp = pprint.PrettyPrinter(indent=2) + +#Second time filter for assetchains (block 821657) +notarynames = [ "0dev1_jl777", "0dev2_kolo", "0dev3_kolo", "0dev4_decker", "a-team_SH", "artik_AR", "artik_EU", "artik_NA", "artik_SH", "badass_EU", "badass_NA", "batman_AR", "batman_SH", "ca333_EU", "chainmakers_EU", "chainmakers_NA", "chainstrike_SH", "cipi_AR", "cipi_NA", "crackers_EU", "crackers_NA", "dwy_EU", "emmanux_SH", "etszombi_EU", "fullmoon_AR", "fullmoon_NA", "fullmoon_SH", "goldenman_EU", "indenodes_AR", "indenodes_EU", "indenodes_NA", "indenodes_SH", "jackson_AR", "jeezy_EU", "karasugoi_NA", "komodoninja_EU", "komodoninja_SH", "komodopioneers_SH", "libscott_SH", "lukechilds_AR", "madmax_AR", "meshbits_AR", "meshbits_SH", "metaphilibert_AR", "metaphilibert_SH", "patchkez_SH", "pbca26_NA", "peer2cloud_AR", "peer2cloud_SH", "polycryptoblog_NA", "hyper_AR", "hyper_EU", "hyper_SH", "hyper_NA", "popcornbag_AR", "popcornbag_NA", "alien_AR", "alien_EU", "thegaltmines_NA", "titomane_AR", "titomane_EU", "titomane_SH", "webworker01_NA", "xrobesx_NA" ] +notaries = 64 * [0] + +startheight = 821657 +stopheight = 1300000 +for i in range(startheight,stopheight): + ret = rpc.getNotarisationsForBlock(i) + KMD = ret['KMD'] + if len(KMD) > 0: + for obj in KMD: + # sanity check, KMD nota dont appear in DB anyway! + if obj['chain'] == 'KMD': + continue; + for notary in obj['notaries']: + notaries[notary] = notaries[notary] + 1 + +i = 0 +SH = [] +AR = [] +EU = [] +NA = [] +for notary in notaries: + tmpnotary = {} + tmpnotary['node'] = notarynames[i] + tmpnotary['ac_count'] = notary + if notarynames[i].endswith('SH'): + SH.append(tmpnotary) + elif notarynames[i].endswith('AR'): + AR.append(tmpnotary) + elif notarynames[i].endswith('EU'): + EU.append(tmpnotary) + elif notarynames[i].endswith('NA'): + NA.append(tmpnotary) + i = i + 1 + +regions = {} +regions['SH'] = sorted(SH, key=lambda k: k['ac_count'], reverse=True) +regions['AR'] = sorted(AR, key=lambda k: k['ac_count'], reverse=True) +regions['EU'] = sorted(EU, key=lambda k: k['ac_count'], reverse=True) +regions["NA"] = sorted(NA, key=lambda k: k['ac_count'], reverse=True) + +pp.pprint(regions) From e688959c6e3343f1c93c24b4642310ba4e93a062 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Wed, 10 Apr 2019 11:41:29 +0800 Subject: [PATCH 773/787] ... --- src/notarystats.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) mode change 100644 => 100755 src/notarystats.py diff --git a/src/notarystats.py b/src/notarystats.py old mode 100644 new mode 100755 index cc67ad10e..c8b3ab1f1 --- a/src/notarystats.py +++ b/src/notarystats.py @@ -58,14 +58,14 @@ pp = pprint.PrettyPrinter(indent=2) notarynames = [ "0dev1_jl777", "0dev2_kolo", "0dev3_kolo", "0dev4_decker", "a-team_SH", "artik_AR", "artik_EU", "artik_NA", "artik_SH", "badass_EU", "badass_NA", "batman_AR", "batman_SH", "ca333_EU", "chainmakers_EU", "chainmakers_NA", "chainstrike_SH", "cipi_AR", "cipi_NA", "crackers_EU", "crackers_NA", "dwy_EU", "emmanux_SH", "etszombi_EU", "fullmoon_AR", "fullmoon_NA", "fullmoon_SH", "goldenman_EU", "indenodes_AR", "indenodes_EU", "indenodes_NA", "indenodes_SH", "jackson_AR", "jeezy_EU", "karasugoi_NA", "komodoninja_EU", "komodoninja_SH", "komodopioneers_SH", "libscott_SH", "lukechilds_AR", "madmax_AR", "meshbits_AR", "meshbits_SH", "metaphilibert_AR", "metaphilibert_SH", "patchkez_SH", "pbca26_NA", "peer2cloud_AR", "peer2cloud_SH", "polycryptoblog_NA", "hyper_AR", "hyper_EU", "hyper_SH", "hyper_NA", "popcornbag_AR", "popcornbag_NA", "alien_AR", "alien_EU", "thegaltmines_NA", "titomane_AR", "titomane_EU", "titomane_SH", "webworker01_NA", "xrobesx_NA" ] notaries = 64 * [0] -startheight = 821657 -stopheight = 1300000 +startheight = 1300000 #821657 +stopheight = 99999999 for i in range(startheight,stopheight): ret = rpc.getNotarisationsForBlock(i) KMD = ret['KMD'] if len(KMD) > 0: for obj in KMD: - # sanity check, KMD nota dont appear in DB anyway! + #for now skip KMD for this. As official stats are from BTC chain? if obj['chain'] == 'KMD': continue; for notary in obj['notaries']: From 3d83dda142d7fceafd951255d8738e90bc0521cb Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Wed, 10 Apr 2019 12:07:31 +0800 Subject: [PATCH 774/787] fix script --- src/notarystats.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/notarystats.py b/src/notarystats.py index c8b3ab1f1..b606db1a6 100755 --- a/src/notarystats.py +++ b/src/notarystats.py @@ -54,18 +54,18 @@ def def_credentials(chain): rpc = def_credentials('KMD') pp = pprint.PrettyPrinter(indent=2) -#Second time filter for assetchains (block 821657) notarynames = [ "0dev1_jl777", "0dev2_kolo", "0dev3_kolo", "0dev4_decker", "a-team_SH", "artik_AR", "artik_EU", "artik_NA", "artik_SH", "badass_EU", "badass_NA", "batman_AR", "batman_SH", "ca333_EU", "chainmakers_EU", "chainmakers_NA", "chainstrike_SH", "cipi_AR", "cipi_NA", "crackers_EU", "crackers_NA", "dwy_EU", "emmanux_SH", "etszombi_EU", "fullmoon_AR", "fullmoon_NA", "fullmoon_SH", "goldenman_EU", "indenodes_AR", "indenodes_EU", "indenodes_NA", "indenodes_SH", "jackson_AR", "jeezy_EU", "karasugoi_NA", "komodoninja_EU", "komodoninja_SH", "komodopioneers_SH", "libscott_SH", "lukechilds_AR", "madmax_AR", "meshbits_AR", "meshbits_SH", "metaphilibert_AR", "metaphilibert_SH", "patchkez_SH", "pbca26_NA", "peer2cloud_AR", "peer2cloud_SH", "polycryptoblog_NA", "hyper_AR", "hyper_EU", "hyper_SH", "hyper_NA", "popcornbag_AR", "popcornbag_NA", "alien_AR", "alien_EU", "thegaltmines_NA", "titomane_AR", "titomane_EU", "titomane_SH", "webworker01_NA", "xrobesx_NA" ] notaries = 64 * [0] -startheight = 1300000 #821657 -stopheight = 99999999 +startheight = 821657 #Second time filter for assetchains (block 821657) for KMD its 814000 +stopheight = rpc.getblockcount() # @kolo what height does season end?! for i in range(startheight,stopheight): ret = rpc.getNotarisationsForBlock(i) KMD = ret['KMD'] if len(KMD) > 0: for obj in KMD: - #for now skip KMD for this. As official stats are from BTC chain? + #for now skip KMD for this. As official stats are from BTC chain + # this can be reversed to !== to count KMD numbers :) if obj['chain'] == 'KMD': continue; for notary in obj['notaries']: From 2fbb858beff294255787898d38963c1222ba2346 Mon Sep 17 00:00:00 2001 From: blackjok3rtt <30971146+blackjok3rtt@users.noreply.github.com> Date: Wed, 10 Apr 2019 12:27:53 +0800 Subject: [PATCH 775/787] remove git link --- src/notarystats.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/notarystats.py b/src/notarystats.py index b606db1a6..0e7673833 100755 --- a/src/notarystats.py +++ b/src/notarystats.py @@ -1,6 +1,5 @@ #!/usr/bin/env python3 """ -build komodod from here: https://github.com/blackjok3rtt/komodo/tree/nota_stats sudo apt-get install python3-dev sudo apt-get install python3 libgnutls28-dev libssl-dev sudo apt-get install python3-pip From 2e2bb2b51883c2e2479df531e1cff33a624e3046 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Wed, 10 Apr 2019 16:53:08 +0800 Subject: [PATCH 776/787] fix getsnapshot2 thanks to dimxy's advice. --- src/txdb.cpp | 28 +++++++++++++--------------- src/txdb.h | 2 +- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/txdb.cpp b/src/txdb.cpp index 93000d9fb..4c9ea31ca 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -458,7 +458,7 @@ uint32_t komodo_segid32(char *coinaddr); {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} \ }; -int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top, bool fRPC ,std::vector > &vaddr, UniValue &ret) +int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector > &vaddr, UniValue *ret) { int64_t total = 0; int64_t totalAddresses = 0; std::string address; int64_t utxos = 0; int64_t ignoredAddresses = 0, cryptoConditionsUTXOs = 0, cryptoConditionsTotals = 0; @@ -542,30 +542,28 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top, bool fRPC ,s if ( top == topN ) break; } - // this is for the snapshot RPC, you can skip this by passing false to the 3rd arugment. - // Still needs UniValue defined to use SnapShot2 though, can this be changed to just pass 0? - // I tried to make it a pointer, and check if the pointer was 0, but UniValue class didnt like that. - if ( fRPC ) + // this is for the snapshot RPC, you can skip this by passing a 0 as the last argument. + if (ret) { // Total amount in this snapshot, which is less than circulating supply if top parameter is used // Use the address_total for a total of all address included when using top parameter. - ret.push_back(make_pair("total", (double) (total+cryptoConditionsTotals)/ COIN )); + ret->push_back(make_pair("total", (double) (total+cryptoConditionsTotals)/ COIN )); // Average amount in each address of this snapshot - ret.push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); + ret->push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); // Total number of utxos processed in this snaphot - ret.push_back(make_pair("utxos", utxos)); + ret->push_back(make_pair("utxos", utxos)); // Total number of addresses in this snaphot - ret.push_back(make_pair("total_addresses", top ? top : totalAddresses )); + ret->push_back(make_pair("total_addresses", top ? top : totalAddresses )); // Total number of ignored addresses in this snaphot - ret.push_back(make_pair("ignored_addresses", ignoredAddresses)); + ret->push_back(make_pair("ignored_addresses", ignoredAddresses)); // Total number of crypto condition utxos we skipped - ret.push_back(make_pair("skipped_cc_utxos", cryptoConditionsUTXOs)); + ret->push_back(make_pair("skipped_cc_utxos", cryptoConditionsUTXOs)); // Total value of skipped crypto condition utxos - ret.push_back(make_pair("cc_utxo_value", (double) cryptoConditionsTotals / COIN)); + ret->push_back(make_pair("cc_utxo_value", (double) cryptoConditionsTotals / COIN)); // total of all the address's, does not count coins in CC vouts. - ret.push_back(make_pair("address_total", (double) total/ COIN )); + ret->push_back(make_pair("address_total", (double) total/ COIN )); // The snapshot finished at this block height - ret.push_back(make_pair("ending_height", chainActive.Height())); + ret->push_back(make_pair("ending_height", chainActive.Height())); } return(topN); } @@ -577,7 +575,7 @@ UniValue CBlockTreeDB::Snapshot(int top) UniValue result(UniValue::VOBJ); UniValue addressesSorted(UniValue::VARR); result.push_back(Pair("start_time", (int) time(NULL))); - if ( Snapshot2(0,top,true,vaddr,result) != 0 ) + if ( Snapshot2(0,top,vaddr,&result) != 0 ) { for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) { diff --git a/src/txdb.h b/src/txdb.h index 8b98994a2..b4c4cd6bd 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -116,7 +116,7 @@ public: bool LoadBlockIndexGuts(); bool blockOnchainActive(const uint256 &hash); UniValue Snapshot(int top); - int32_t Snapshot2(int64_t dustthreshold, int32_t top, bool fRPC ,std::vector > &vaddr, UniValue &ret); + int32_t Snapshot2(int64_t dustthreshold, int32_t top,std::vector > &vaddr, UniValue *ret); }; #endif // BITCOIN_TXDB_H From 2872c88795f76019ca7b783910220b259a43db1c Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Wed, 10 Apr 2019 13:09:11 +0200 Subject: [PATCH 777/787] Adding dual daemon import and gateway import. (#21) --- src/Makefile.am | 1 + src/cc/CCGateways.h | 1 - src/cc/CCImportGateway.h | 38 ++ src/cc/CCcustom.cpp | 22 +- src/cc/CCinclude.h | 14 +- src/cc/CCutils.cpp | 89 +++ src/cc/dapps/oraclefeed.c | 77 ++- src/cc/eval.h | 3 +- src/cc/gateways.cpp | 187 ++---- src/cc/import.cpp | 589 ++++++++++++----- src/cc/importgateway.cpp | 1292 +++++++++++++++++++++++++++++++++++++ src/cc/oracles.cpp | 3 +- src/importcoin.cpp | 83 ++- src/importcoin.h | 11 +- src/rpc/crosschain.cpp | 357 +++++++++- src/rpc/server.cpp | 15 + src/rpc/server.h | 13 +- src/wallet/rpcwallet.cpp | 14 +- 18 files changed, 2440 insertions(+), 369 deletions(-) create mode 100644 src/cc/CCImportGateway.h create mode 100644 src/cc/importgateway.cpp diff --git a/src/Makefile.am b/src/Makefile.am index ce7fd1731..248f9aa27 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -288,6 +288,7 @@ libbitcoin_server_a_SOURCES = \ bloom.cpp \ cc/eval.cpp \ cc/import.cpp \ + cc/importgateway.cpp \ cc/CCassetsCore.cpp \ cc/CCcustom.cpp \ cc/CCtx.cpp \ diff --git a/src/cc/CCGateways.h b/src/cc/CCGateways.h index 1f594e38b..8dfed186f 100644 --- a/src/cc/CCGateways.h +++ b/src/cc/CCGateways.h @@ -18,7 +18,6 @@ #define CC_GATEWAYS_H #include "CCinclude.h" -#include "../merkleblock.h" bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4); diff --git a/src/cc/CCImportGateway.h b/src/cc/CCImportGateway.h new file mode 100644 index 000000000..995cde460 --- /dev/null +++ b/src/cc/CCImportGateway.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * Copyright © 2014-2018 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + + +#ifndef CC_IMPORTGATEWAY_H +#define CC_IMPORTGATEWAY_H + +#include "CCinclude.h" + +// CCcustom +bool ImportGatewayValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn); +bool ImportGatewayExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); +std::string ImportGatewayBind(uint64_t txfee,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4); +std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t burnvout,std::string rawburntx,std::vectorproof,CPubKey destpub); +std::string ImportGatewayWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount); +std::string ImportGatewayPartialSign(uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex); +std::string ImportGatewayCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex); +std::string ImportGatewayMarkDone(uint64_t txfee,uint256 completetxid,std::string refcoin); +UniValue ImportGatewayPendingDeposits(uint256 bindtxid,std::string refcoin); +UniValue ImportGatewayPendingWithdraws(uint256 bindtxid,std::string refcoin); +UniValue ImportGatewayProcessedWithdraws(uint256 bindtxid,std::string refcoin); +UniValue ImportGatewayExternalAddress(uint256 bindtxid,CPubKey pubkey); +UniValue ImportGatewayDumpPrivKey(uint256 bindtxid,CKey key); +UniValue ImportGatewayList(); +UniValue ImportGatewayInfo(uint256 bindtxid); +#endif \ No newline at end of file diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index 3a00e0c98..6c66b3599 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -31,6 +31,7 @@ #include "CCPayments.h" #include "CCGateways.h" #include "CCtokens.h" +#include "CCImportGateway.h" /* CCcustom has most of the functions that need to be extended to create a new CC contract. @@ -63,7 +64,6 @@ const char *AssetsCCaddr = "RGKRjeTBw4LYFotSDLT6RWzMHbhXri6BG6"; const char *AssetsNormaladdr = "RFYE2yL3KknWdHK6uNhvWacYsCUtwzjY3u"; char AssetsCChexstr[67] = { "02adf84e0e075cf90868bd4e3d34a03420e034719649c41f371fc70d8e33aa2702" }; uint8_t AssetsCCpriv[32] = { 0x9b, 0x17, 0x66, 0xe5, 0x82, 0x66, 0xac, 0xb6, 0xba, 0x43, 0x83, 0x74, 0xf7, 0x63, 0x11, 0x3b, 0xf0, 0xf3, 0x50, 0x6f, 0xd9, 0x6b, 0x67, 0x85, 0xf9, 0x7a, 0xf0, 0x54, 0x4d, 0xb1, 0x30, 0x77 }; - #include "CCcustom.inc" #undef FUNCNAME #undef EVALCODE @@ -75,7 +75,6 @@ const char *FaucetCCaddr = "R9zHrofhRbub7ER77B7NrVch3A63R39GuC"; const char *FaucetNormaladdr = "RKQV4oYs4rvxAWx1J43VnT73rSTVtUeckk"; char FaucetCChexstr[67] = { "03682b255c40d0cde8faee381a1a50bbb89980ff24539cb8518e294d3a63cefe12" }; uint8_t FaucetCCpriv[32] = { 0xd4, 0x4f, 0xf2, 0x31, 0x71, 0x7d, 0x28, 0x02, 0x4b, 0xc7, 0xdd, 0x71, 0xa0, 0x39, 0xc4, 0xbe, 0x1a, 0xfe, 0xeb, 0xc2, 0x46, 0xda, 0x76, 0xf8, 0x07, 0x53, 0x3d, 0x96, 0xb4, 0xca, 0xa0, 0xe9 }; - #include "CCcustom.inc" #undef FUNCNAME #undef EVALCODE @@ -243,6 +242,17 @@ uint8_t CClibCCpriv[32] = { 0x57, 0xcf, 0x49, 0x71, 0x7d, 0xb4, 0x15, 0x1b, 0x4f #undef FUNCNAME #undef EVALCODE +// ImportGateway +#define FUNCNAME IsImportGatewayInput +#define EVALCODE EVAL_IMPORTGATEWAY +const char *ImportGatewayCCaddr = "RXJT6CRAXHFuQ2UjqdxMj7EfrayF6UJpzZ"; +const char *ImportGatewayNormaladdr = "RNFRho63Ddz1Rh2eGPETykrU4fA8r67S4Y"; +char ImportGatewayCChexstr[67] = { "0397231cfe04ea32d5fafb2206773ec9fba6e15c5a4e86064468bca195f7542714" }; +uint8_t ImportGatewayCCpriv[32] = { 0x65, 0xef, 0x27, 0xeb, 0x3d, 0xb0, 0xb4, 0xae, 0x0f, 0xbc, 0x77, 0xdb, 0xf8, 0x40, 0x48, 0x90, 0x52, 0x20, 0x9e, 0x45, 0x3b, 0x49, 0xd8, 0x97, 0x60, 0x8c, 0x27, 0x4c, 0x59, 0x46, 0xe1, 0xdf }; +#include "CCcustom.inc" +#undef FUNCNAME +#undef EVALCODE + int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode) { CPubKey pk; int32_t i; uint8_t pub33[33],check33[33],hash[32]; char CCaddr[64],checkaddr[64],str[67]; @@ -425,6 +435,14 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) cp->validate = TokensValidate; cp->ismyvin = IsTokensInput; break; + case EVAL_IMPORTGATEWAY: + strcpy(cp->unspendableCCaddr, ImportGatewayCCaddr); + strcpy(cp->normaladdr, ImportGatewayNormaladdr); + strcpy(cp->CChexstr, ImportGatewayCChexstr); + memcpy(cp->CCpriv, ImportGatewayCCpriv, 32); + cp->validate = ImportGatewayValidate; + cp->ismyvin = IsImportGatewayInput; + break; default: if ( CClib_initcp(cp,evalcode) < 0 ) return(0); diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 12b4a6d12..d1fda0154 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -51,6 +51,7 @@ one other technical note is that komodod has the insight-explorer extensions bui #include "../komodo_defs.h" #include "../utlist.h" #include "../uthash.h" +#include "merkleblock.h" #define CC_BURNPUBKEY "02deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead" #define CC_MAXVINS 1024 @@ -185,9 +186,12 @@ bool GetCCParams(Eval* eval, const CTransaction &tx, uint32_t nIn, CTransaction &txOut, std::vector> &preConditions, std::vector> ¶ms); int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); -uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format); uint256 OracleMerkle(int32_t height,uint256 reforacletxid,char *format,std::vectorpublishers); uint256 OraclesBatontxid(uint256 oracletxid,CPubKey pk); +uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format); +uint8_t DecodeOraclesOpRet(const CScript &scriptPubKey,uint256 &oracletxid,CPubKey &pk,int64_t &num); +uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector &data); +int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen); //int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs); int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs); @@ -212,11 +216,6 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible); bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vinPubkeys); -uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector &data); -int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen); - - - // CCcustom CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv); //uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopret1, std::vector &vopret2); @@ -270,6 +269,9 @@ bool GetCustomscriptaddress(char *destaddr,const CScript &scriptPubKey,uint8_t t std::vector Mypubkey(); bool Myprivkey(uint8_t myprivkey[]); int64_t CCduration(int32_t &numblocks,uint256 txid); +uint256 CCOraclesReverseScan(char const *logcategory,uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid); +int32_t CCCointxidExists(char const *logcategory,uint256 cointxid); +uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids); bool komodo_txnotarizedconfirmed(uint256 txid); CPubKey check_signing_pubkey(CScript scriptSig); // CCtx diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 887550a0c..9a68c2456 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -532,6 +532,95 @@ int64_t CCduration(int32_t &numblocks,uint256 txid) return(duration); } +uint256 CCOraclesReverseScan(char const *logcategory,uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid) +{ + CTransaction tx; uint256 hash,mhash,bhash,hashBlock,oracletxid; int32_t len,len2,numvouts; + int64_t val,merkleht; CPubKey pk; std::vectordata; char str[65],str2[65]; + + txid = zeroid; + LogPrint(logcategory,"start reverse scan %s\n",uint256_str(str,batontxid)); + while ( myGetTransaction(batontxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) + { + LogPrint(logcategory,"check %s\n",uint256_str(str,batontxid)); + if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,bhash,pk,data) == 'D' && oracletxid == reforacletxid ) + { + LogPrint(logcategory,"decoded %s\n",uint256_str(str,batontxid)); + if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height ) + { + len = oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size()); + len2 = oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size()); + + LogPrint(logcategory,"found merkleht.%d len.%d len2.%d %s %s\n",(int32_t)merkleht,len,len2,uint256_str(str,hash),uint256_str(str2,mhash)); + if ( len == sizeof(hash)+sizeof(int32_t) && len2 == 2*sizeof(mhash)+sizeof(int32_t) && mhash != zeroid ) + { + txid = batontxid; + LogPrint(logcategory,"set txid\n"); + return(mhash); + } + else + { + LogPrint(logcategory,"missing hash\n"); + return(zeroid); + } + } + else LogPrint(logcategory,"height.%d vs search ht.%d\n",(int32_t)merkleht,(int32_t)height); + batontxid = bhash; + LogPrint(logcategory,"new hash %s\n",uint256_str(str,batontxid)); + } else break; + } + LogPrint(logcategory,"end of loop\n"); + return(zeroid); +} + +int32_t myIs_coinaddr_inmempoolvout(char const *logcategory,char *coinaddr) +{ + int32_t i,n; char destaddr[64]; + BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) + { + const CTransaction &tx = e.GetTx(); + if ( (n= tx.vout.size()) > 0 ) + { + const uint256 &txid = tx.GetHash(); + for (i=0; i > addressIndex; + CCtxidaddr(txidaddr,cointxid); + SetCCtxids(addressIndex,txidaddr); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + return(-1); + } + return(myIs_coinaddr_inmempoolvout(logcategory,txidaddr)); +} + +/* Get the block merkle root for a proof + * IN: proofData + * OUT: merkle root + * OUT: transaction IDS + */ +uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids) +{ + CMerkleBlock merkleBlock; + if (!E_UNMARSHAL(proofData, ss >> merkleBlock)) + return uint256(); + return merkleBlock.txn.ExtractMatches(txids); +} + bool komodo_txnotarizedconfirmed(uint256 txid) { char str[65]; diff --git a/src/cc/dapps/oraclefeed.c b/src/cc/dapps/oraclefeed.c index cf7a2eacb..6ac014ad1 100644 --- a/src/cc/dapps/oraclefeed.c +++ b/src/cc/dapps/oraclefeed.c @@ -450,10 +450,12 @@ int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *m return(0); } -cJSON *get_gatewayspending(char *refcoin,char *acname,char *bindtxidstr) +cJSON *get_gatewayspending(int8_t type,char *refcoin,char *acname,char *bindtxidstr) { - cJSON *retjson; char *retstr; - if ( (retjson= get_cli(refcoin,&retstr,acname,"gatewayspendingwithdraws",bindtxidstr,refcoin,"","")) != 0 ) + cJSON *retjson; char *retstr; char function[64]; + if (type==0) sprintf(function,"%s","gatewayspendingwithdraws"); + else if (type==1) sprintf(function,"%s","importgatewaypendingwithdraws"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bindtxidstr,refcoin,"","")) != 0 ) { //printf("pending.(%s)\n",jprint(retjson,0)); return(retjson); @@ -466,10 +468,13 @@ cJSON *get_gatewayspending(char *refcoin,char *acname,char *bindtxidstr) return(0); } -cJSON *get_gatewaysprocessed(char *refcoin,char *acname,char *bindtxidstr) +cJSON *get_gatewaysprocessed(int8_t type,char *refcoin,char *acname,char *bindtxidstr) { cJSON *retjson; char *retstr; - if ( (retjson= get_cli(refcoin,&retstr,acname,"gatewaysprocessed",bindtxidstr,refcoin,"","")) != 0 ) + char function[64]; + if (type==0) sprintf(function,"%s","gatewaysprocessed"); + else if (type==1) sprintf(function,"%s","importgatewayprocessed"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bindtxidstr,refcoin,"","")) != 0 ) { //printf("pending.(%s)\n",jprint(retjson,0)); return(retjson); @@ -692,10 +697,12 @@ cJSON *addsignature(char *refcoin,char *acname,char *rawtx, int M) return(0); } -bits256 gatewayspartialsign(char *refcoin,char *acname,bits256 txid,char *hex) +bits256 gatewayspartialsign(int8_t type,char *refcoin,char *acname,bits256 txid,char *hex) { - char str[65],*retstr; cJSON *retjson; - if ( (retjson= get_cli(refcoin,&retstr,acname,"gatewayspartialsign",bits256_str(str,txid),refcoin,hex,"")) != 0 ) + char str[65],*retstr; cJSON *retjson; char function[64]; + if (type==0) sprintf(function,"%s","gatewayspartialsign"); + else if (type==1) sprintf(function,"%s","importgatewaypartialsign"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bits256_str(str,txid),refcoin,hex,"")) != 0 ) { if (strcmp("error",jstr(retjson,"result"))!=0) txid=broadcasttx(refcoin,acname,retjson); else printf("%s\n",jstr(retjson,"error")); @@ -710,10 +717,13 @@ bits256 gatewayspartialsign(char *refcoin,char *acname,bits256 txid,char *hex) return (zeroid); } -bits256 gatewayscompletesigning(char *refcoin,char *acname,bits256 withtxid,char *hex) +bits256 gatewayscompletesigning(int8_t type,char *refcoin,char *acname,bits256 withtxid,char *hex) { - char str[65],*retstr; cJSON *retjson; bits256 txid; - if ( (retjson= get_cli(refcoin,&retstr,acname,"gatewayscompletesigning",bits256_str(str,withtxid),refcoin,hex,"")) != 0 ) + char str[65],*retstr; cJSON *retjson; bits256 txid; char function[64]; + + if (type==0) sprintf(function,"%s","gatewayscompletesigning"); + else if (type==1) sprintf(function,"%s","importgatewaycompletesigning"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bits256_str(str,withtxid),refcoin,hex,"")) != 0 ) { if (strcmp("error",jstr(retjson,"result"))!=0) txid=broadcasttx(refcoin,acname,retjson); else printf("%s\n",jstr(retjson,"error")); @@ -728,10 +738,13 @@ bits256 gatewayscompletesigning(char *refcoin,char *acname,bits256 withtxid,char return (zeroid); } -bits256 gatewaysmarkdone(char *refcoin,char *acname,bits256 withtxid) +bits256 gatewaysmarkdone(int8_t type,char *refcoin,char *acname,bits256 withtxid) { - char str[65],str2[65],*retstr; cJSON *retjson; bits256 txid; - if ( (retjson= get_cli(refcoin,&retstr,acname,"gatewaysmarkdone",bits256_str(str,withtxid),refcoin,"","")) != 0 ) + char str[65],str2[65],*retstr; cJSON *retjson; bits256 txid; ; char function[64]; + + if (type==0) sprintf(function,"%s","gatewaysmarkdone"); + else if (type==1) sprintf(function,"%s","importgatewaymarkdone"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bits256_str(str,withtxid),refcoin,"","")) != 0 ) { if (strcmp("error",jstr(retjson,"result"))!=0) txid=broadcasttx(refcoin,acname,retjson); else printf("%s\n",jstr(retjson,"error")); @@ -746,10 +759,13 @@ bits256 gatewaysmarkdone(char *refcoin,char *acname,bits256 withtxid) return (zeroid); } -int32_t get_gatewaysinfo(char *refcoin,char *acname,char *depositaddr,int32_t *Mp,int32_t *Np,char *bindtxidstr,char *coin,char *oraclestr, char **pubkeys) +int32_t get_gatewaysinfo(int8_t type,char *refcoin,char *acname,char *depositaddr,int32_t *Mp,int32_t *Np,char *bindtxidstr,char *coin,char *oraclestr, char **pubkeys) { - char *oracle,*retstr,*name,*deposit,temp[128]; cJSON *retjson,*pubarray; int32_t n; - if ( (retjson= get_cli(refcoin,&retstr,acname,"gatewaysinfo",bindtxidstr,"","","")) != 0 ) + char *oracle,*retstr,*name,*deposit,temp[128]; cJSON *retjson,*pubarray; int32_t n; char function[64]; + + if (type==0) sprintf(function,"%s","gatewaysinfo"); + else if (type==1) sprintf(function,"%s","importgatewayinfo"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bindtxidstr,"","","")) != 0 ) { if ( (oracle= jstr(retjson,"oracletxid")) != 0 && strcmp(oracle,oraclestr) == 0 && (deposit= jstr(retjson,"deposit")) != 0 ) { @@ -774,7 +790,6 @@ int32_t get_gatewaysinfo(char *refcoin,char *acname,char *depositaddr,int32_t *M strcat(*pubkeys,temp); } } - else printf("%s != %s\n",oracle,oraclestr); free_json(retjson); } else if ( retstr != 0 ) @@ -851,7 +866,7 @@ int32_t markerexists(char *refcoin,char *acname,char *coinaddr) } -void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t M,int32_t N) +void update_gatewayspending(int8_t type,char *refcoin,char *acname,char *bindtxidstr,int32_t M,int32_t N) { // check queue to prevent duplicate // check KMD chain and mempool for txidaddr @@ -864,7 +879,7 @@ void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t int32_t i,j,n,K,retval,processed = 0; bits256 txid,cointxid,withdrawtxid,lasttxid,completetxid; int64_t satoshis; memset(&zeroid,0,sizeof(zeroid)); - if ( (retjson= get_gatewayspending(refcoin,acname,bindtxidstr)) != 0 ) + if ( (retjson= get_gatewayspending(type,refcoin,acname,bindtxidstr)) != 0 ) { if ( jint(retjson,"queueflag") != 0 && (coinstr= jstr(retjson,"coin")) != 0 && strcmp(coinstr,refcoin) == 0 ) { @@ -888,7 +903,7 @@ void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t { if ( (clijson=addsignature(refcoin,"",rawtx,M)) != 0 && is_cJSON_True(jobj(clijson,"complete")) != 0) { - txid=gatewayscompletesigning(refcoin,acname,withdrawtxid,jstr(clijson,"hex")); + txid=gatewayscompletesigning(type,refcoin,acname,withdrawtxid,jstr(clijson,"hex")); if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s 1of1\n",bits256_str(str,withdrawtxid)); else fprintf(stderr,"### SIGNING error broadcasting tx on %s",acname); free_json(clijson); @@ -910,13 +925,13 @@ void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t { if ( is_cJSON_True(jobj(clijson,"complete")) != 0 ) { - txid=gatewayscompletesigning(refcoin,acname,lasttxid,jstr(clijson,"hex")); + txid=gatewayscompletesigning(type,refcoin,acname,lasttxid,jstr(clijson,"hex")); if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s %dof%d\n",bits256_str(str,withdrawtxid),K+1,N); else fprintf(stderr,"### SIGNING error broadcasting tx on %s\n",acname); } else if ( jint(clijson,"partialtx") != 0 ) { - txid=gatewayspartialsign(refcoin,acname,lasttxid,jstr(clijson,"hex")); + txid=gatewayspartialsign(type,refcoin,acname,lasttxid,jstr(clijson,"hex")); if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s %d/%dof%d\n",bits256_str(str,withdrawtxid),K+1,M,N); else fprintf(stderr,"### SIGNING error broadcasting tx on %s\n",acname); } @@ -932,7 +947,7 @@ void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t } free_json(retjson); } - if ( (retjson= get_gatewaysprocessed(refcoin,acname,bindtxidstr)) != 0 ) + if ( (retjson= get_gatewaysprocessed(type,refcoin,acname,bindtxidstr)) != 0 ) { if ( jint(retjson,"queueflag") != 0 && (coinstr= jstr(retjson,"coin")) != 0 && strcmp(coinstr,refcoin) == 0 ) { @@ -954,7 +969,7 @@ void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t { withdrawaddr = jstr(item,"withdrawaddr"); fprintf(stderr,"### WITHDRAW %.8f %s sent to %s\n",amount,refcoin,withdrawaddr); - txid=gatewaysmarkdone(refcoin,acname,completetxid); + txid=gatewaysmarkdone(type,refcoin,acname,completetxid); if (txid.txid!=zeroid.txid) fprintf(stderr,"### MARKDONE withdraw %s\n",bits256_str(str,withdrawtxid)); else fprintf(stderr,"### MARKDONE error broadcasting tx on %s\n",refcoin); } @@ -1024,7 +1039,7 @@ oraclesdata 17a841a919c284cea8a676f34e793da002e606f19a9258a3190bed12d5aaa3ff 034 int32_t main(int32_t argc,char **argv) { - cJSON *clijson,*clijson2,*regjson,*item; int32_t acheight,i,retval,M,N,n,height,prevheight = 0; char *pubkeys,*format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,depositaddr[64],hexstr[4096],refcoin[64]; uint64_t price; bits256 txid; + cJSON *clijson,*clijson2,*regjson,*item; int32_t type,i,retval,M,N,n,height,prevheight = 0; char *pubkeys,*format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,depositaddr[64],hexstr[4096],refcoin[64]; uint64_t price; bits256 txid; if ( argc < 6 ) { printf("usage: oraclefeed $ACNAME $ORACLETXID $MYPUBKEY $FORMAT $BINDTXID [refcoin_cli]\n"); @@ -1044,8 +1059,7 @@ int32_t main(int32_t argc,char **argv) printf("only formats of L and Ihh are supported now\n"); return(-1); } - M = N = 1; - acheight = 0; + M = N = 0; refcoin[0] = 0; while ( 1 ) { @@ -1061,7 +1075,9 @@ int32_t main(int32_t argc,char **argv) exit(0); } pubkeys=0; - if ( get_gatewaysinfo(refcoin,acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,&pubkeys) < 0 ) + if ( get_gatewaysinfo(0,refcoin,acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,&pubkeys) == 0 ) type=0; + else if ( get_gatewaysinfo(1,refcoin,acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,&pubkeys) == 0 ) type=1; + else { printf("cant find bindtxid.(%s)\n",bindtxidstr); exit(0); @@ -1089,9 +1105,8 @@ int32_t main(int32_t argc,char **argv) if ( bits256_nonz(txid) != 0 ) { prevheight = height; - acheight = get_coinheight(refcoin,""); printf("%s ht.%d <- %s\n",refcoin,height,hexstr); - update_gatewayspending(refcoin,acname,bindtxidstr,M,N); + update_gatewayspending(type,refcoin,acname,bindtxidstr,M,N); } free_json(clijson2); } diff --git a/src/cc/eval.h b/src/cc/eval.h index a42bbdb2f..1324d5e21 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -56,7 +56,8 @@ EVAL(EVAL_MARMARA, 0xef) \ EVAL(EVAL_PAYMENTS, 0xf0) \ EVAL(EVAL_GATEWAYS, 0xf1) \ - EVAL(EVAL_TOKENS, 0xf2) + EVAL(EVAL_TOKENS, 0xf2) \ + EVAL(EVAL_IMPORTGATEWAY, 0xf3) \ // evalcodes 0x10 to 0x7f are reserved for cclib dynamic CC diff --git a/src/cc/gateways.cpp b/src/cc/gateways.cpp index 3c1a49194..1039e9176 100644 --- a/src/cc/gateways.cpp +++ b/src/cc/gateways.cpp @@ -412,91 +412,6 @@ bool GatewaysExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti else return(true); } -static int32_t myIs_coinaddr_inmempoolvout(char *coinaddr) -{ - int32_t i,n; char destaddr[64]; - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) - { - const CTransaction &tx = e.GetTx(); - if ( (n= tx.vout.size()) > 0 ) - { - const uint256 &txid = tx.GetHash(); - for (i=0; idata; - txid = zeroid; - LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "start reverse scan " << batontxid.GetHex() << std::endl); - while ( myGetTransaction(batontxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) - { - LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "check " << batontxid.GetHex() << std::endl); - if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,bhash,pk,data) == 'D' && oracletxid == reforacletxid ) - { - LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "decoded " << batontxid.GetHex() << std::endl); - if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height ) - { - len = oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size()); - len2 = oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size()); - LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "found merkleht." << (int32_t)merkleht << " len." << len << " len2." << len2 << " " << hash.GetHex() << " " << mhash.GetHex() << std::endl); - if ( len == sizeof(hash)+sizeof(int32_t) && len2 == 2*sizeof(mhash)+sizeof(int32_t) && mhash != zeroid ) - { - txid = batontxid; - LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "set txid" << std::endl); - return(mhash); - } - else - { - LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "missing hash" << std::endl); - return(zeroid); - } - } else LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "height." << (int32_t)merkleht << " vs search ht." << (int32_t)height << std::endl); - batontxid = bhash; - LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "new hash " << batontxid.GetHex() << std::endl); - } else break; - } - LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "end of loop\n"); - return(zeroid); -} - -int32_t GatewaysCointxidExists(struct CCcontract_info *cp,uint256 cointxid) -{ - char txidaddr[64]; std::string coin; int32_t numvouts; uint256 hashBlock; - std::vector > addressIndex; - CCtxidaddr(txidaddr,cointxid); - SetCCtxids(addressIndex,txidaddr); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - return(-1); - } - return(myIs_coinaddr_inmempoolvout(txidaddr)); -} - -/* Get the block merkle root for a proof - * IN: proofData - * OUT: merkle root - * OUT: transaction IDS - */ -uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids) -{ - CMerkleBlock merkleBlock; - if (!E_UNMARSHAL(proofData, ss >> merkleBlock)) - return uint256(); - return merkleBlock.txn.ExtractMatches(txids); -} - int64_t GatewaysVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 cointxid,const std::string deposithex,std::vectorproof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2) { std::vector txids; uint256 proofroot,hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format; @@ -737,7 +652,7 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & merkleroot = zeroid; for (i=m=0; i >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) @@ -1630,15 +1545,15 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) GetTokensCCaddress(cp,tokensaddr,gatewayspk); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) { - CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); } if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) { - CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); } n = msigpubkeys.size(); queueflag = 0; @@ -1717,16 +1632,16 @@ UniValue GatewaysProcessedWithdraws(uint256 bindtxid,std::string refcoin) gatewayspk = GetUnspendable(cp,0); _GetCCaddress(coinaddr,EVAL_GATEWAYS,gatewayspk); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) - { - CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); } if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) { - CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); } n = msigpubkeys.size(); queueflag = 0; @@ -1795,16 +1710,16 @@ UniValue GatewaysExternalAddress(uint256 bindtxid,CPubKey pubkey) cp = CCinit(&C,EVAL_GATEWAYS); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) - { - CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); } if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') { - CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); } GetCustomscriptaddress(addr,CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG,taddr,prefix,prefix2); result.push_back(Pair("result","success")); @@ -1819,16 +1734,16 @@ UniValue GatewaysDumpPrivKey(uint256 bindtxid,CKey key) cp = CCinit(&C,EVAL_GATEWAYS); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) - { - CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); } if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') { - CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); } priv=EncodeCustomSecret(key,wiftype); @@ -1848,16 +1763,16 @@ UniValue GatewaysInfo(uint256 bindtxid) Gatewayspk = GetUnspendable(cp,0); GetTokensCCaddress(cp,gatewaystokens,Gatewayspk); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) - { - CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); } if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') { - CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); - LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); - return(""); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); } if ( GetTransaction(bindtxid,tx,hashBlock,false) != 0 ) { @@ -1866,19 +1781,17 @@ UniValue GatewaysInfo(uint256 bindtxid) depositaddr[0] = 0; if ( tx.vout.size() > 0 && DecodeGatewaysBindOpRet(depositaddr,tx.vout[tx.vout.size()-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 && M <= N && N > 0 ) { - if ( N > 1 ) - { - result.push_back(Pair("M",M)); - result.push_back(Pair("N",N)); - for (i=0; i -//#define LEV_INFO 0 -//#define LEV_DEBUG1 1 -//#define LOGSTREAM(category, level, logoperator) { std::ostringstream stream; logoperator; for(int i = 0; i < level; i ++) if( LogAcceptCategory( (std::string(category) + (level > 0 ? std::string("-")+std::to_string(level) : std::string("") )).c_str() ) ) LogPrintStr(stream.str()); } - -/* - * CC Eval method for import coin. - * - * This method should control every parameter of the ImportCoin transaction, since it has no signature - * to protect it from malleability. - - ##### 0xffffffff is a special CCid for single chain/dual daemon imports - */ +#include "key_io.h" +#define CODA_BURN_ADDRESS "KPrrRoPfHOnNpZZQ6laHXdQDkSQDkVHaN0V+LizLlHxz7NaA59sBAAAA" extern std::string ASSETCHAINS_SELFIMPORT; extern uint16_t ASSETCHAINS_CODAPORT,ASSETCHAINS_BEAMPORT; @@ -41,10 +32,97 @@ extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids); uint256 GatewaysReverseScan(uint256 &txid, int32_t height, uint256 reforacletxid, uint256 batontxid); int32_t GatewaysCointxidExists(struct CCcontract_info *cp, uint256 cointxid); -uint8_t DecodeGatewaysBindOpRet(char *depositaddr, const CScript &scriptPubKey, std::string &coin, uint256 &tokenid, int64_t &totalsupply, uint256 &oracletxid, uint8_t &M, uint8_t &N, std::vector &pubkeys, uint8_t &taddr, uint8_t &prefix, uint8_t &prefix2); +uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype); + +char *nonportable_path(char *str) +{ + int32_t i; + for (i=0; str[i]!=0; i++) + if ( str[i] == '/' ) + str[i] = '\\'; + return(str); +} + +char *portable_path(char *str) +{ +#ifdef _WIN32 + return(nonportable_path(str)); +#else +#ifdef __PNACL + /*int32_t i,n; + if ( str[0] == '/' ) + return(str); + else + { + n = (int32_t)strlen(str); + for (i=n; i>0; i--) + str[i] = str[i-1]; + str[0] = '/'; + str[n+1] = 0; + }*/ +#endif + return(str); +#endif +} + +void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) +{ + FILE *fp; + long filesize,buflen = *allocsizep; + uint8_t *buf = *bufp; + *lenp = 0; + if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) + { + fseek(fp,0,SEEK_END); + filesize = ftell(fp); + if ( filesize == 0 ) + { + fclose(fp); + *lenp = 0; + //printf("loadfile null size.(%s)\n",fname); + return(0); + } + if ( filesize > buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +void *filestr(long *allocsizep,char *_fname) +{ + long filesize = 0; char *fname,*buf = 0; void *retptr; + *allocsizep = 0; + fname = (char *)malloc(strlen(_fname)+1); + strcpy(fname,_fname); + retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); + free(fname); + return(retptr); +} // ac_import=chain support: // encode opret for gateways import +CScript EncodeImportTxOpRet(uint32_t targetCCid, std::string coin, std::vector publishers, std::vectortxids, int32_t height, uint256 cointxid, int32_t claimvout, std::string rawburntx, std::vectorproof, CPubKey destpub, int64_t amount) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << targetCCid << coin << publishers << txids << height << cointxid << claimvout << rawburntx << proof << destpub << amount); + return(opret); +} + CScript EncodeGatewaysImportTxOpRet(uint32_t targetCCid, std::string coin, uint256 bindtxid, std::vector publishers, std::vectortxids, int32_t height, uint256 cointxid, int32_t claimvout, std::string rawburntx, std::vectorproof, CPubKey destpub, int64_t amount) { CScript opret; @@ -52,169 +130,132 @@ CScript EncodeGatewaysImportTxOpRet(uint32_t targetCCid, std::string coin, uint2 return(opret); } -bool ImportCoinGatewaysVerify(char *refdepositaddr, uint256 oracletxid, int32_t claimvout, std::string refcoin, uint256 burntxid, const std::string rawburntx, std::vectorproof, uint256 merkleroot) +CScript EncodeCodaImportTxOpRet(uint32_t targetCCid, std::string coin, std::string burntx, uint256 bindtxid, CPubKey destpub, int64_t amount) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << targetCCid << burntx << bindtxid << destpub << amount); + return(opret); +} + +cJSON* CodaRPC(char **retstr,char const *arg0,char const *arg1,char const *arg2,char const *arg3,char const *arg4,char const *arg5) +{ + char cmdstr[5000],fname[256],*jsonstr; + long fsize; + cJSON *retjson=NULL; + + sprintf(fname,"/tmp/coda.%s",arg0); + sprintf(cmdstr,"coda.exe client %s %s %s %s %s %s > %s 2>&1",arg0,arg1,arg2,arg3,arg4,arg5,fname); + *retstr = 0; + if (system(cmdstr)<0) return (retjson); + if ( (jsonstr=(char *)filestr(&fsize,fname)) != 0 ) + { + jsonstr[strlen(jsonstr)-1]='\0'; + if ( (strncmp(jsonstr,"Merkle List of transactions:",28)!=0) || (retjson= cJSON_Parse(jsonstr+29)) == 0) + *retstr=jsonstr; + else free(jsonstr); + } + return(retjson); +} + +bool ImportCoinGatewaysVerify(CTransaction oracletx, int32_t claimvout, std::string refcoin, uint256 burntxid, const std::string rawburntx, std::vectorproof, uint256 merkleroot) { std::vector txids; - uint256 proofroot, hashBlock, foundtxid = zeroid; - CTransaction oracletx, burntx; + uint256 proofroot; std::string name, description, format; - char destaddr[64], destpubaddr[64], claimaddr[64]; int32_t i, numvouts; - int64_t nValue = 0; - if (myGetTransaction(oracletxid, oracletx, hashBlock) == 0 || (numvouts = oracletx.vout.size()) <= 0) - { - LOGSTREAM("importcoin", CCLOG_INFO, stream << "ImportCoinGatewaysVerify can't find oracletxid=" << oracletxid.GetHex() << std::endl); - return false; - } if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey, name, description, format) != 'C' || name != refcoin) { LOGSTREAM("importcoin", CCLOG_INFO, stream << "ImportCoinGatewaysVerify mismatched oracle name=" << name.c_str() << " != " << refcoin.c_str() << std::endl); return false; } - proofroot = BitcoinGetProofMerkleRoot(proof, txids); - if (proofroot != merkleroot) - { - LOGSTREAM("importcoin", CCLOG_INFO, stream << "ImportCoinGatewaysVerify mismatched proof merkleroot=" << proofroot.GetHex() << " and oracles merkleroot=" << merkleroot.GetHex() << std::endl); - return false; - } - - // check the burntxid is in the proof: - if (std::find(txids.begin(), txids.end(), burntxid) == txids.end()) { - LOGSTREAM("importcoin", CCLOG_INFO, stream << "ImportCoinGatewaysVerify invalid proof for this burntxid=" << burntxid.GetHex() << std::endl); - return false; - } - - /* - if (DecodeHexTx(burntx, rawburntx) != 0) - { - Getscriptaddress(claimaddr, burntx.vout[claimvout].scriptPubKey); - Getscriptaddress(destpubaddr, CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); - if (strcmp(claimaddr, destpubaddr) == 0) - { - for (i = 0; iproof, std::string rawburntx, int32_t ivout, uint256 burntxid) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction burntx, bindtx; - CPubKey mypk, gatewayspk; - uint256 oracletxid, merkleroot, mhash, hashBlock, tokenid, txid; - int64_t totalsupply; - int32_t i, m, n, numvouts; - uint8_t M, N, taddr, prefix, prefix2; - std::string coin; - struct CCcontract_info *cp, C; - std::vector pubkeys, publishers; - std::vectortxids; - char depositaddr[64], txidaddr[64]; +// std::string MakeGatewaysImportTx(uint64_t txfee, uint256 oracletxid, int32_t height, std::string refcoin, std::vector proof, std::string rawburntx, int32_t ivout, uint256 burntxid,std::string destaddr) +// { +// CMutableTransaction burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); +// CTransaction oracletx,regtx; CPubKey regpk; +// uint256 proofroot,txid,tmporacletxid,merkleroot,mhash,hashBlock; int32_t i,m,n=0,numvouts; +// std::string name,desc,format; std::vector vouts; +// std::vector pubkeys; std::vectortxids; +// char markeraddr[64]; int64_t datafee; +// std::vector > unspentOutputs; - cp = CCinit(&C, EVAL_GATEWAYS); - /*if (txfee == 0) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - gatewayspk = GetUnspendable(cp, 0); */ - - if (!E_UNMARSHAL(ParseHex(rawburntx), ss >> burntx)) - return std::string(""); - - CAmount amount = GetCoinImportValue(burntx); // equal to int64_t - - LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx height=" << height << " coin=" << refcoin << " amount=" << (double)amount / COIN << " pubkeys num=" << pubkeys.size() << std::endl); - - if (GetTransaction(bindtxid, bindtx, hashBlock, false) == 0 || (numvouts = bindtx.vout.size()) <= 0) - { - LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx cant find bindtxid=" << bindtxid.GetHex() << std::endl); - return(""); - } -/* if (DecodeGatewaysBindOpRet(depositaddr, bindtx.vout[numvouts - 1].scriptPubKey, coin, tokenid, totalsupply, oracletxid, M, N, pubkeys, taddr, prefix, prefix2) != 'B' || refcoin != coin) - { - LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid coin - bindtxid=" << bindtxid.GetHex() << " coin=" << coin.c_str() << std::endl); - return(""); - } eliminate link err */ - n = (int32_t)pubkeys.size(); - merkleroot = zeroid; - for (i = m = 0; i < n; i++) - { - LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx using pubkeys[" << i << "]=" << HexStr(pubkeys[i]) << std::endl); - if ((mhash = GatewaysReverseScan(txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid) - { - if (merkleroot == zeroid) - merkleroot = mhash, m = 1; - else if (mhash == merkleroot) - m ++; - publishers.push_back(pubkeys[i]); - txids.push_back(txid); - } - } - - LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx burntxid=" << burntxid.GetHex() << " nodes m=" << m << " of n=" << n << std::endl); - if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot - { - LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx couldnt find merkleroot for block height=" << height << "coin=" << coin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl ); - return(""); - } - if (GatewaysCointxidExists(cp, burntxid) != 0) - { - LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx burntxid=" << burntxid.GetHex() << " already exists" << std::endl); - return(""); - } - if (!ImportCoinGatewaysVerify(depositaddr, oracletxid, ivout, coin, burntxid, rawburntx, proof, merkleroot)) - { - LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx could not validate burntx, txid=" << burntxid.GetHex() << std::endl); - return(""); - } - - - std::vector leaftxids; - BitcoinGetProofMerkleRoot(proof, leaftxids); - MerkleBranch newBranch(0, leaftxids); - TxProof txProof = std::make_pair(burntxid, newBranch); - - std::vector vouts; - - - - return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof, burntx, vouts))); - - /*if (AddNormalinputs(mtx, mypk, 3 * txfee, 4) > 0) - { - mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, destpub)); - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr, burntxid))) << OP_CHECKSIG)); - return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeGatewaysImportTxOpRet(0xFFFFFFFF, coin, bindtxid, publishers, txids, height, burntxid, ivout, rawburntx, proof, destpub, amount))); - } - LOGSTREAM("importcoin", LEV_INFO, stream << "MakeGatewaysImportTx coud not find normal imputs" << std::endl);*/ - return(""); -} +// if (!E_UNMARSHAL(ParseHex(rawburntx), ss >> burntx)) +// return std::string(""); +// CAmount amount = GetCoinImportValue(burntx); // equal to int64_t +// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx height=" << height << " coin=" << refcoin << " amount=" << (double)amount / COIN << " pubkeys num=" << pubkeys.size() << std::endl); +// if (GetTransaction(oracletxid, oracletx, hashBlock, false) == 0 || (numvouts = oracletx.vout.size()) <= 0) +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx cant find oracletxid=" << oracletxid.GetHex() << std::endl); +// return(""); +// } +// if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C') +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl); +// return(""); +// } +// if (name!=refcoin || format!="Ihh") +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl); +// return(""); +// } +// CCtxidaddr(markeraddr,oracletxid); +// SetCCunspents(unspentOutputs,markeraddr); +// for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) +// { +// txid = it->first.txhash; +// if ( GetTransaction(txid,regtx,hashBlock,false) != 0 && regtx.vout.size() > 0 +// && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid ) +// { +// pubkeys.push_back(regpk); +// n++; +// } +// } +// merkleroot = zeroid; +// for (i = m = 0; i < n; i++) +// { +// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx using pubkeys[" << i << "]=" << HexStr(pubkeys[i]) << std::endl); +// if ((mhash = CCOraclesReverseScan("importcoind-1",txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid) +// { +// if (merkleroot == zeroid) +// merkleroot = mhash, m = 1; +// else if (mhash == merkleroot) +// m ++; +// txids.push_back(txid); +// } +// } +// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx burntxid=" << burntxid.GetHex() << " nodes m=" << m << " of n=" << n << std::endl); +// if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl ); +// return(""); +// } +// proofroot = BitcoinGetProofMerkleRoot(proof, txids); +// if (proofroot != merkleroot) +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx mismatched proof merkleroot=" << proofroot.GetHex() << " and oracles merkleroot=" << merkleroot.GetHex() << std::endl); +// return(""); +// } +// // check the burntxid is in the proof: +// if (std::find(txids.begin(), txids.end(), burntxid) == txids.end()) { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid proof for this burntxid=" << burntxid.GetHex() << std::endl); +// return(""); +// } +// burntx.vout.push_back(MakeBurnOutput(amount,0xffffffff,refcoin,vouts,proof,oracletxid,height)); +// std::vector leaftxids; +// BitcoinGetProofMerkleRoot(proof, leaftxids); +// MerkleBranch newBranch(0, leaftxids); +// TxProof txProof = std::make_pair(burntxid, newBranch); +// CTxDestination dest = DecodeDestination(destaddr.c_str()); +// CScript scriptPubKey = GetScriptForDestination(dest); +// vouts.push_back(CTxOut(amount,scriptPubKey)); +// return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof, burntx, vouts))); +// } // makes source tx for self import tx std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx) @@ -332,6 +373,90 @@ int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript return 0; } +// make import tx with burntx and dual daemon +std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector vouts) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()),burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk; uint256 codaburntxid; std::vector dummyproof; + int32_t i,numvouts,n,m; std::string coin,error; struct CCcontract_info *cp, C; + cJSON *result,*tmp,*tmp1; unsigned char hash[SHA256_DIGEST_LENGTH+1]; + char out[SHA256_DIGEST_LENGTH*2+1],*retstr,*destaddr,*receiver; TxProof txProof; uint64_t amount; + + cp = CCinit(&C, EVAL_GATEWAYS); + if (txfee == 0) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, receipt.c_str(), receipt.size()); + SHA256_Final(hash, &sha256); + for(i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + sprintf(out + (i * 2), "%02x", hash[i]); + } + out[65]='\0'; + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: hash=" << out << std::endl); + codaburntxid.SetHex(out); + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: receipt=" << receipt << " codaburntxid=" << codaburntxid.GetHex().data() << " amount=" << (double)amount / COIN << std::endl); + result=CodaRPC(&retstr,"prove-payment","-address",srcaddr.c_str(),"-receipt-chain-hash",receipt.c_str(),""); + if (result==0) + { + if (retstr!=0) + { + CCerror=std::string("CodaRPC: ")+retstr; + free(retstr); + } + return(""); + } + else + { + if ((tmp=jobj(jitem(jarray(&n,result,(char *)"payments"),0),(char *)"payload"))!=0 && (destaddr=jstr(jobj(tmp,(char *)"common"),(char *)"memo"))!=0 && + (receiver=jstr(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"receiver"))!=0 && (amount=j64bits(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"amount"))!=0) + { + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: receiver=" << receiver << " destaddr=" << destaddr << " amount=" << amount << std::endl); + if (strcmp(receiver,CODA_BURN_ADDRESS)!=0) + { + CCerror="MakeCodaImportTx: invalid burn address, coins do not go to predefined burn address - "; + CCerror+=CODA_BURN_ADDRESS; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); + } + CTxDestination dest = DecodeDestination(destaddr); + CScript scriptPubKey = GetScriptForDestination(dest); + if (vouts[0]!=CTxOut(amount*COIN,scriptPubKey)) + { + CCerror="MakeCodaImportTx: invalid destination address, burnTx memo!=importTx destination"; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); + } + if (amount*COIN!=vouts[0].nValue) + { + CCerror="MakeCodaImportTx: invalid amount, burnTx amount!=importTx amount"; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); + } + burntx.vin.push_back(CTxIn(codaburntxid,0,CScript())); + burntx.vout.push_back(MakeBurnOutput(amount*COIN,0xffffffff,"CODA",vouts,dummyproof,srcaddr,receipt)); + return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof,burntx,vouts))); + } + else + { + CCerror="MakeCodaImportTx: invalid Coda burn tx"; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); + } + + } + CCerror="MakeCodaImportTx: error fetching Coda tx"; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); +} + // use proof from the above functions to validate the import int32_t CheckBEAMimport(TxProof proof,std::vector rawproof,CTransaction burnTx,std::vector payouts) @@ -340,17 +465,135 @@ int32_t CheckBEAMimport(TxProof proof,std::vector rawproof,CTransaction return(-1); } -int32_t CheckCODAimport(TxProof proof,std::vector rawproof,CTransaction burnTx,std::vector payouts) +int32_t CheckCODAimport(CTransaction importTx,CTransaction burnTx,std::vector payouts,std::string srcaddr,std::string receipt) { + cJSON *result,*tmp,*tmp1; char *retstr,out[SHA256_DIGEST_LENGTH*2+1]; unsigned char hash[SHA256_DIGEST_LENGTH+1]; int i,n,m; + SHA256_CTX sha256; uint256 codaburntxid; char *destaddr,*receiver; uint64_t amount; + // check with dual-CODA daemon via ASSETCHAINS_CODAPORT for validity of burnTx - return(-1); + SHA256_Init(&sha256); + SHA256_Update(&sha256, receipt.c_str(), receipt.size()); + SHA256_Final(hash, &sha256); + for(i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + sprintf(out + (i * 2), "%02x", hash[i]); + } + out[65]='\0'; + codaburntxid.SetHex(out); + result=CodaRPC(&retstr,"prove-payment","-address",srcaddr.c_str(),"-receipt-chain-hash",receipt.c_str(),""); + if (result==0) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "CodaRPC error: " << retstr << std::endl); + free(retstr); + return (-1); + } + else + { + if ((tmp=jobj(jitem(jarray(&n,result,(char *)"payments"),0),(char *)"payload"))==0 || (destaddr=jstr(jobj(tmp,(char *)"common"),(char *)"memo"))==0 || + (receiver=jstr(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"receiver"))==0 || (amount=j64bits(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"amount"))==0) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid Coda burn tx" << jprint(result,1) << std::endl); + free(result); + return (-1); + } + CTxDestination dest = DecodeDestination(destaddr); + CScript scriptPubKey = GetScriptForDestination(dest); + if (payouts[0]!=CTxOut(amount*COIN,scriptPubKey)); + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Destination address in burn tx does not match destination in import tx" << std::endl); + free(result); + return (-1); + } + if (strcmp(receiver,CODA_BURN_ADDRESS)!=0) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid burn address " << jstr(tmp1,(char *)"receiver") << std::endl); + free(result); + return (-1); + } + if (amount*COIN!=payouts[0].nValue) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Burn amount and import amount not matching, " << j64bits(tmp,(char *)"amount") << " - " << payouts[0].nValue/COIN << std::endl); + free(result); + return (-1); + } + if (burnTx.vin[0].prevout.hash!=codaburntxid || importTx.vin[0].prevout.hash!=burnTx.GetHash()) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid import/burn tx vin" << std::endl); + free(result); + return (-1); + } + free(result); + } + return(0); } -int32_t CheckGATEWAYimport(TxProof proof,std::vector rawproof,CTransaction burnTx,std::vector payouts) +int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction burnTx,std::string refcoin,std::vector proof, + uint256 bindtxid,std::vector publishers,std::vector txids,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub) { + // CTransaction oracletx,regtx; CPubKey regpk; + // uint256 proofroot,txid,tmporacletxid,merkleroot,mhash,hashBlock; int32_t i,m,n=0,numvouts; + // std::string name,desc,format; std::vector vouts; + // std::vector pubkeys; std::vectortxids; + // char markeraddr[64]; int64_t datafee; + // std::vector > unspentOutputs; // ASSETCHAINS_SELFIMPORT is coin // check for valid burn from external coin blockchain and if valid return(0); - return(-1); + // if (GetTransaction(oracletxid, oracletx, hashBlock, false) == 0 || (numvouts = oracletx.vout.size()) <= 0) + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport cant find oracletxid=" << oracletxid.GetHex() << std::endl); + // return(-1); + // } + // if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C') + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl); + // return(-1); + // } + // if (name!=refcoin || format!="Ihh") + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl); + // return(-1); + // } + // CCtxidaddr(markeraddr,oracletxid); + // SetCCunspents(unspentOutputs,markeraddr); + // for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + // { + // txid = it->first.txhash; + // if ( GetTransaction(txid,regtx,hashBlock,false) != 0 && regtx.vout.size() > 0 + // && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid ) + // { + // pubkeys.push_back(regpk); + // n++; + // } + // } + // merkleroot = zeroid; + // for (i = m = 0; i < n; i++) + // { + // if ((mhash = CCOraclesReverseScan("importcoind-1",txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid) + // { + // if (merkleroot == zeroid) + // merkleroot = mhash, m = 1; + // else if (mhash == merkleroot) + // m ++; + // txids.push_back(txid); + // } + // } + // if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl ); + // return(-1); + // } + // proofroot = BitcoinGetProofMerkleRoot(proof, txids); + // if (proofroot != merkleroot) + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport mismatched proof merkleroot=" << proofroot.GetHex() << " and oracles merkleroot=" << merkleroot.GetHex() << std::endl); + // return(-1); + // } + // // check the burntxid is in the proof: + // if (std::find(txids.begin(), txids.end(), burnTx.GetHash()) == txids.end()) { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid proof for this burntxid=" << burnTx.GetHash().GetHex() << std::endl); + // return(-1); + // } + return(0); } int32_t CheckPUBKEYimport(TxProof proof,std::vector rawproof,CTransaction burnTx,std::vector payouts) @@ -404,10 +647,20 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector rawproof,CTransacti return(0); } +/* + * CC Eval method for import coin. + * + * This method should control every parameter of the ImportCoin transaction, since it has no signature + * to protect it from malleability. + + ##### 0xffffffff is a special CCid for single chain/dual daemon imports + */ bool Eval::ImportCoin(const std::vector params,const CTransaction &importTx,unsigned int nIn) { - TxProof proof; CTransaction burnTx; std::vector payouts; uint64_t txfee = 10000; - uint32_t targetCcid; std::string targetSymbol; uint256 payoutsHash; std::vector rawproof; + TxProof proof; CTransaction burnTx; std::vector payouts; uint64_t txfee = 10000; int32_t height,burnvout; std::vector publishers; + uint32_t targetCcid; std::string targetSymbol,srcaddr,destaddr,receipt,rawburntx; uint256 payoutsHash,bindtxid; std::vector rawproof; + std::vector txids; CPubKey destpub; + if ( importTx.vout.size() < 2 ) return Invalid("too-few-vouts"); // params @@ -458,7 +711,7 @@ bool Eval::ImportCoin(const std::vector params,const CTransaction &impo { if ( ASSETCHAINS_CODAPORT == 0 ) return Invalid("CODA-import-without-port"); - else if ( CheckCODAimport(proof,rawproof,burnTx,payouts) < 0 ) + else if ( UnmarshalBurnTx(burnTx,srcaddr,receipt)==0 || CheckCODAimport(importTx,burnTx,payouts,srcaddr,receipt) < 0 ) return Invalid("CODA-import-failure"); } else if ( targetSymbol == "PUBKEY" ) @@ -472,7 +725,7 @@ bool Eval::ImportCoin(const std::vector params,const CTransaction &impo { if ( targetSymbol != ASSETCHAINS_SELFIMPORT ) return Invalid("invalid-gateway-import-coin"); - else if ( CheckGATEWAYimport(proof,rawproof,burnTx,payouts) < 0 ) + else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,height,burnvout,rawburntx,destpub)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,height,burnvout,rawburntx,destpub) < 0 ) return Invalid("GATEWAY-import-failure"); } } diff --git a/src/cc/importgateway.cpp b/src/cc/importgateway.cpp new file mode 100644 index 000000000..4e943b472 --- /dev/null +++ b/src/cc/importgateway.cpp @@ -0,0 +1,1292 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include "CCImportGateway.h" +#include "key_io.h" +#include "../importcoin.h" + +// start of consensus code + +#define KMD_PUBTYPE 60 +#define KMD_P2SHTYPE 85 +#define KMD_WIFTYPE 188 +#define KMD_TADDR 0 +#define CC_MARKER_VALUE 10000 + +CScript EncodeImportGatewayBindOpRet(uint8_t funcid,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector importgatewaypubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2,uint8_t wiftype) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << oracletxid << M << N << importgatewaypubkeys << taddr << prefix << prefix2 << wiftype); + return(opret); +} + +uint8_t DecodeImportGatewayBindOpRet(char *burnaddr,const CScript &scriptPubKey,std::string &coin,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &importgatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype) +{ + std::vector vopret; uint8_t *script,e,f; std::vector pubkeys; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + burnaddr[0] = 0; + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> oracletxid; ss >> M; ss >> N; ss >> importgatewaypubkeys; ss >> taddr; ss >> prefix; ss >> prefix2; ss >> wiftype) != 0 ) + { + if ( prefix == KMD_PUBTYPE && prefix2 == KMD_P2SHTYPE ) + { + if ( N > 1 ) + { + strcpy(burnaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,importgatewaypubkeys))).ToString().c_str()); + LOGSTREAM("importgateway", CCLOG_DEBUG1, stream << "f." << f << " M." << (int)M << " of N." << (int)N << " size." << (int32_t)importgatewaypubkeys.size() << " -> " << burnaddr << std::endl); + } else Getscriptaddress(burnaddr,CScript() << ParseHex(HexStr(importgatewaypubkeys[0])) << OP_CHECKSIG); + } + else + { + if ( N > 1 ) strcpy(burnaddr,CCustomBitcoinAddress(CScriptID(GetScriptForMultisig(M,importgatewaypubkeys)),taddr,prefix,prefix2).ToString().c_str()); + else GetCustomscriptaddress(burnaddr,CScript() << ParseHex(HexStr(importgatewaypubkeys[0])) << OP_CHECKSIG,taddr,prefix,prefix2); + } + return(f); + } else LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "error decoding bind opret" << std::endl); + return(0); +} + +CScript EncodeImportGatewayDepositOpRet(uint8_t funcid,uint256 bindtxid,std::string refcoin,std::vector publishers,std::vectortxids,int32_t height,uint256 burntxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << refcoin << bindtxid << publishers << txids << height << burntxid << claimvout << deposithex << proof << destpub << amount); + return(opret); +} + +uint8_t DecodeImportGatewayDepositOpRet(const CScript &scriptPubKey,uint256 &bindtxid,std::string &refcoin,std::vector&publishers,std::vector&txids,int32_t &height,uint256 &burntxid, int32_t &claimvout,std::string &deposithex,std::vector &proof,CPubKey &destpub,int64_t &amount) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> refcoin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> burntxid; ss >> claimvout; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeImportGatewayWithdrawOpRet(uint8_t funcid,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << bindtxid << refcoin << withdrawpub << amount); + return(opret); +} + +uint8_t DecodeImportGatewayWithdrawOpRet(const CScript &scriptPubKey,uint256 &bindtxid,std::string &refcoin,CPubKey &withdrawpub,int64_t &amount) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeImportGatewayPartialOpRet(uint8_t funcid, uint256 withdrawtxid,std::string refcoin,uint8_t K, CPubKey signerpk,std::string hex) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << signerpk << hex); + return(opret); +} + +uint8_t DecodeImportGatewayPartialOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,CPubKey &signerpk,std::string &hex) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> signerpk; ss >> hex) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeImportGatewayCompleteSigningOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint8_t K,std::string hex) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << hex); + return(opret); +} + +uint8_t DecodeImportGatewayCompleteSigningOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,std::string &hex) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> hex) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeImportGatewayMarkDoneOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint256 completetxid) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << completetxid); + return(opret); +} + +uint8_t DecodeImportGatewayMarkDoneOpRet(const CScript &scriptPubKey, uint256 &withdrawtxid, std::string &refcoin, uint256 &completetxid) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> completetxid;) != 0 ) + { + return(f); + } + return(0); +} + +uint8_t DecodeImportGatewayOpRet(const CScript &scriptPubKey) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_IMPORTGATEWAY) + { + f=script[1]; + if (f == 'B' || f == 'D' || f == 'C' || f == 'W' || f == 'P' || f == 'S' || f == 'M') + return(f); + } + return(0); +} + +int64_t IsImportGatewayvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) +{ + char destaddr[64]; + + if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) + { + if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) + return(tx.vout[v].nValue); + } + return(0); +} + +int64_t ImportGatewayVerify(char *refburnaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 burntxid,const std::string deposithex,std::vectorproof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2) +{ + std::vector txids; uint256 proofroot,hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format; + char destaddr[64],destpubaddr[64],claimaddr[64]; int32_t i,numvouts; int64_t nValue = 0; + + if ( myGetTransaction(oracletxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify cant find oracletxid " << oracletxid.GetHex() << std::endl); + return(0); + } + if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' || name != refcoin ) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify mismatched oracle name " << name << " != " << refcoin << std::endl); + return(0); + } + proofroot = BitcoinGetProofMerkleRoot(proof,txids); + if ( proofroot != merkleroot ) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify mismatched merkleroot " << proofroot.GetHex() << " != " << merkleroot.GetHex() << std::endl); + return(0); + } + if (std::find(txids.begin(), txids.end(), burntxid) == txids.end()) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify invalid proof for this burntxid" << std::endl); + return 0; + } + if ( DecodeHexTx(tx,deposithex) != 0 ) + { + GetCustomscriptaddress(claimaddr,tx.vout[claimvout].scriptPubKey,taddr,prefix,prefix2); + GetCustomscriptaddress(destpubaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG,taddr,prefix,prefix2); + if ( strcmp(claimaddr,destpubaddr) == 0 ) + { + for (i=0; i publishers; std::vectortxids; uint256 bindtxid,burntxid; std::vector proof; CPubKey claimpubkey; + if ( (numvouts= tx.vout.size()) > 0 ) + { + if ( DecodeImportGatewayDepositOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,publishers,txids,height,burntxid,claimvout,deposithex,proof,claimpubkey,amount) == 'D' && claimpubkey == mypk ) + { + return(amount); + } + } + return(0); +} + +int32_t ImportGatewayBindExists(struct CCcontract_info *cp,CPubKey importgatewaypk,std::string refcoin) +{ + char markeraddr[64],burnaddr[64]; std::string coin; int32_t numvouts; int64_t totalsupply; uint256 tokenid,oracletxid,hashBlock; + uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; CTransaction tx; + std::vector > addressIndex; + + _GetCCaddress(markeraddr,EVAL_IMPORTGATEWAY,importgatewaypk); + SetCCtxids(addressIndex,markeraddr); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + if ( myGetTransaction(it->first.txhash,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey)=='B' ) + { + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B' ) + { + if ( coin == refcoin ) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "trying to bind an existing import for coin" << std::endl); + return(1); + } + } + } + } + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + { + const CTransaction &txmempool = e.GetTx(); + const uint256 &hash = txmempool.GetHash(); + + if ((numvouts=txmempool.vout.size()) > 0 && DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey)=='B') + if (DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B') + return(1); + } + + return(0); +} + +bool ImportGatewayValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn) +{ + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks,height,claimvout; bool retval; uint8_t funcid,hash[32],K,M,N,taddr,prefix,prefix2,wiftype; + char str[65],destaddr[65],burnaddr[65],importgatewayaddr[65],validationError[512]; + std::vector txids; std::vector pubkeys,publishers,tmppublishers; std::vector proof; int64_t amount,tmpamount; + uint256 hashblock,txid,bindtxid,deposittxid,withdrawtxid,completetxid,tmptokenid,oracletxid,bindtokenid,burntxid,tmptxid,merkleroot,mhash; CTransaction bindtx,tmptx; + std::string refcoin,tmprefcoin,hex,name,description,format; CPubKey pubkey,tmppubkey,importgatewaypk; + + return (true); + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + preventCCvins = preventCCvouts = -1; + if ( numvouts < 1 ) + return eval->Invalid("no vouts"); + else + { + //LogPrint("importgateway-1","check amounts\n"); + // if ( ImportGatewayExactAmounts(cp,eval,tx,1,10000) == false ) + // { + // return eval->Invalid("invalid inputs vs. outputs!"); + // } + // else + // { + importgatewaypk = GetUnspendable(cp,0); + GetCCaddress(cp, importgatewayaddr, importgatewaypk); + if ( (funcid = DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey)) != 0) + { + switch ( funcid ) + { + case 'B': + //vin.0: normal input + //vout.0: CC vout marker + //vout.n-1: opreturn - 'B' coin oracletxid M N pubkeys taddr prefix prefix2 wiftype + return eval->Invalid("unexpected ImportGatewayValidate for gatewaysbind!"); + break; + case 'W': + //vin.0: normal input + //vin.1: CC input of tokens + //vout.0: CC vout marker to gateways CC address + //vout.1: CC vout of gateways tokens back to gateways tokens CC address + //vout.2: CC vout change of tokens back to owners pubkey (if any) + //vout.n-1: opreturn - 'W' bindtxid refcoin withdrawpub amount + return eval->Invalid("unexpected ImportGatewayValidate for gatewaysWithdraw!"); + break; + case 'P': + //vin.0: normal input + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'P' withdrawtxid refcoin number_of_signs mypk hex + if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,pubkey,hex)!='P') + return eval->Invalid("invalid gatewaysPartialSign OP_RETURN data!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,importgatewayaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (tmptx.vout[1].nValue!=amount) + return eval->Invalid("amount in opret not matching tx tokens amount!"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayspartialsign!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewayspartialsign or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayspartialsign!"); + else if (K>M) + return eval->Invalid("invalid number of signs!"); + break; + case 'S': + //vin.0: normal input + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'S' withdrawtxid refcoin hex + if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,importgatewayaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (tmptx.vout[1].nValue!=amount) + return eval->Invalid("amount in opret not matching tx tokens amount!"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayscompletesigning!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewayscompletesigning or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayscompletesigning!"); + else if (KInvalid("invalid number of signs!"); + break; + case 'M': + //vin.0: normal input + //vin.1: CC input of gatewayscompletesigning tx marker to gateways CC address + //vout.0: opreturn - 'M' withdrawtxid refcoin completetxid + if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayMarkDoneOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,completetxid)!='M') + return eval->Invalid("invalid gatewaysmarkdone OP_RETURN data!"); + else if (myGetTransaction(completetxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewayscompletesigning txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayCompleteSigningOpRet(tmptx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmprefcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); + else if (komodo_txnotarizedconfirmed(completetxid) == false) + return eval->Invalid("gatewayscompletesigning tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysmarkdone!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewaysmarkdone or invalid marker amount!"); + else if (KInvalid("invalid number of signs!"); + break; + } + } + retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); + if ( retval != 0 ) + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGateway tx validated" << std::endl); + else fprintf(stderr,"ImportGateway tx invalid\n"); + return(retval); + // } + } +} +// end of consensus code + +// helper functions for rpc calls in rpcwallet.cpp + +std::string ImportGatewayBind(uint64_t txfee,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction oracletx; uint8_t taddr,prefix,prefix2,wiftype; CPubKey mypk,importgatewaypk; CScript opret; uint256 hashBlock; + struct CCcontract_info *cp,*cpTokens,C,CTokens; std::string name,description,format; int32_t i,numvouts; + char destaddr[64],coinaddr[64],myTokenCCaddr[64],str[65],*fstr; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if (coin=="KMD") + { + prefix = KMD_PUBTYPE; + prefix2 = KMD_P2SHTYPE; + wiftype = KMD_WIFTYPE; + taddr = KMD_TADDR; + } + else + { + prefix = p1; + prefix2 = p2; + wiftype = p3; + taddr = p4; + LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "set prefix " << prefix << ", prefix2 " << prefix2 << ", wiftype " << wiftype << ", taddr " << taddr << " for " << coin << std::endl); + } + if ( N == 0 || N > 15 || M > N ) + { + CCerror = strprintf("illegal M.%d or N.%d",M,N); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( pubkeys.size() != N ) + { + CCerror = strprintf("M.%d N.%d but pubkeys[%d]",M,N,(int32_t)pubkeys.size()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for (i=0; i 0 ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,importgatewaypk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayBindOpRet('B',coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype))); + } + CCerror = strprintf("cant find enough inputs"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t claimvout,std::string rawburntx,std::vectorproof,CPubKey destpub) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()), burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction bindtx; CPubKey mypk; uint256 oracletxid,merkleroot,mhash,hashBlock,txid; std::vector vouts; + int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::string coin; struct CCcontract_info *cp,C; + std::vector pubkeys,publishers; std::vector txids; char str[128],burnaddr[64]; int64_t amount; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if (!E_UNMARSHAL(ParseHex(rawburntx), ss >> burntx)) + { + return std::string(""); + } + amount=burntx.vout[0].nValue; + LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "ImportGatewayDeposit ht." << height << " " << refcoin << " " << (double)amount/COIN << " numpks." << (int32_t)pubkeys.size() << std::endl); + if ( GetTransaction(bindtxid,bindtx,hashBlock,false) == 0 || (numvouts= bindtx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,bindtx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) + { + CCerror = strprintf("invalid coin - bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(bindtxid)==false) + { + CCerror = strprintf("gatewaysbind tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + n = (int32_t)pubkeys.size(); + merkleroot = zeroid; + for (i=m=0; i leaftxids; + BitcoinGetProofMerkleRoot(proof, leaftxids); + MerkleBranch newBranch(0, leaftxids); + TxProof txProof = std::make_pair(burntxid, newBranch); + return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof, burntx, vouts))); +} + +std::string ImportGatewayWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction tx; CPubKey mypk,importgatewaypk,signerpk; uint256 txid,hashBlock,oracletxid,tmptokenid,tmpbindtxid,withdrawtxid; int32_t vout,numvouts; + int64_t nValue,inputs,CCchange=0,tmpamount; uint8_t funcid,K,M,N,taddr,prefix,prefix2,wiftype; std::string coin,hex; + std::vector msigpubkeys; char burnaddr[64],str[65],coinaddr[64]; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp, 0); + + if( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || coin != refcoin ) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(bindtxid)==false) + { + CCerror = strprintf("gatewaysbind tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,importgatewaypk); + SetCCunspents(unspentOutputs,coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + K=0; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P')) + { + if (funcid=='W' && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmpbindtxid==bindtxid) + { + CCerror = strprintf("unable to create withdraw, another withdraw pending"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + + else if (funcid=='P' && DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)=='P' && + GetTransaction(withdrawtxid,tx,hashBlock,false)!=0 && (numvouts=tx.vout.size())>0 && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmpbindtxid==bindtxid) + { + CCerror = strprintf("unable to create withdraw, another withdraw pending"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + } + if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE+amount, 64) > 0 ) + { + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,CC_MARKER_VALUE,importgatewaypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,amount,importgatewaypk)); + return(FinalizeCCTx(0, cp, mtx, mypk, txfee,EncodeImportGatewayWithdrawOpRet('W',bindtxid,refcoin,withdrawpub,amount))); + } + CCerror = strprintf("cant find enough normal inputs"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string ImportGatewayPartialSign(uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,withdrawpub,signerpk,importgatewaypk; struct CCcontract_info *cp,C; CTransaction tx,tmptx; + std::vector > unspentOutputs; char funcid,str[65],burnaddr[64]; + int32_t numvouts; uint256 withdrawtxid,hashBlock,bindtxid,tokenid,oracletxid; std::string coin,tmphex; int64_t amount; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + if (GetTransaction(lasttxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())<=0 + || (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) + { + CCerror = strprintf("can't find last tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (funcid=='W') + { + withdrawtxid=lasttxid; + if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("invalid withdraw tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + else if (funcid=='P') + { + if (DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,tmphex)!='P' || refcoin!=coin) + { + CCerror = strprintf("cannot decode partialsign tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(withdrawtxid,tmptx,hashBlock,false)==0 || (numvouts= tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find withdraw tx %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin) + { + CCerror = strprintf("invalid withdraw tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(tx.GetHash(),0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,CC_MARKER_VALUE,importgatewaypk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayPartialOpRet('P',withdrawtxid,refcoin,K+1,mypk,hex))); + } + CCerror = strprintf("error adding funds for partialsign"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string ImportGatewayCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,importgatewaypk,signerpk,withdrawpub; struct CCcontract_info *cp,C; char funcid,str[65],burnaddr[64]; int64_t amount; + std::string coin,tmphex; CTransaction tx,tmptx; uint256 withdrawtxid,hashBlock,tokenid,bindtxid,oracletxid; int32_t numvouts; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + if ( txfee == 0 ) + txfee = 10000; + if (GetTransaction(lasttxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())<=0 + || (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) + { + CCerror = strprintf("invalid last txid %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (funcid=='W') + { + withdrawtxid=lasttxid; + if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + else if (funcid=='P') + { + if (DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,tmphex)!='P' || refcoin!=coin) + { + CCerror = strprintf("cannot decode partialsign tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(withdrawtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())==0) + { + CCerror = strprintf("invalid withdraw txid %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(lasttxid,0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,CC_MARKER_VALUE,importgatewaypk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayCompleteSigningOpRet('S',withdrawtxid,refcoin,K+1,hex))); + } + CCerror = strprintf("error adding funds for completesigning"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string ImportGatewayMarkDone(uint64_t txfee,uint256 completetxid,std::string refcoin) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk; struct CCcontract_info *cp,C; char str[65],burnaddr[64]; CTransaction tx; int32_t numvouts; + uint256 withdrawtxid,bindtxid,oracletxid,tokenid,hashBlock; std::string coin,hex; + uint8_t K,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; int64_t amount; CPubKey withdrawpub; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + if ( txfee == 0 ) + txfee = 10000; + if (GetTransaction(completetxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())<=0) + { + CCerror = strprintf("invalid completesigning txid %s",uint256_str(str,completetxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex)!='S' || refcoin!=coin) + { + CCerror = strprintf("cannot decode completesigning tx opret %s",uint256_str(str,completetxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(completetxid)==false) + { + CCerror = strprintf("gatewayscompletesigning tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(withdrawtxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())==0) + { + CCerror = strprintf("invalid withdraw txid %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s\n",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(completetxid,0,CScript())); + mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayMarkDoneOpRet('M',withdrawtxid,refcoin,completetxid))); + } + CCerror = strprintf("error adding funds for markdone"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +UniValue ImportGatewayPendingDeposits(uint256 bindtxid,std::string refcoin) +{ + UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string coin,hex,pub; + CPubKey mypk,importgatewaypk,destpub; std::vector pubkeys,publishers; std::vector txids; + uint256 tmpbindtxid,hashBlock,txid,oracletxid,burntxid; uint8_t M,N,taddr,prefix,prefix2,wiftype; + char burnaddr[65],coinaddr[65],str[65],destaddr[65],txidaddr[65]; std::vector proof; + int32_t numvouts,vout,claimvout,height; int64_t nValue,amount; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,mypk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + SetCCunspents(unspentOutputs,coinaddr); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + DecodeImportGatewayDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,publishers,txids,height,burntxid,claimvout,hex,proof,destpub,amount) == 'D' + && tmpbindtxid==bindtxid && refcoin == coin && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("burntxid",uint256_str(str,burntxid))); + obj.push_back(Pair("deposittxid",uint256_str(str,txid))); + CCtxidaddr(txidaddr,txid); + obj.push_back(Pair("deposittxidaddr",txidaddr)); + _GetCCaddress(destaddr,EVAL_TOKENS,destpub); + obj.push_back(Pair("depositaddr",burnaddr)); + obj.push_back(Pair("tokens_destination_address",destaddr)); + pub=HexStr(destpub); + obj.push_back(Pair("claim_pubkey",pub)); + obj.push_back(Pair("amount",(double)amount/COIN)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); + pending.push_back(obj); + } + } + result.push_back(Pair("coin",refcoin)); + result.push_back(Pair("pending",pending)); + return(result); +} + +UniValue ImportGatewayPendingWithdraws(uint256 bindtxid,std::string refcoin) +{ + UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string coin,hex; CPubKey mypk,importgatewaypk,withdrawpub,signerpk; + std::vector msigpubkeys; uint256 hashBlock,txid,tmpbindtxid,tmptokenid,oracletxid,withdrawtxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char funcid,burnaddr[65],coinaddr[65],destaddr[65],str[65],withaddr[65],numstr[32],signeraddr[65],txidaddr[65]; + int32_t i,n,numvouts,vout,queueflag; int64_t amount,nValue; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,importgatewaypk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + n = msigpubkeys.size(); + queueflag = 0; + for (i=0; i >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + K=0; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P') && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) + { + if (funcid=='W') + { + if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,amount)==0 || refcoin!=coin || tmpbindtxid!=bindtxid) continue; + } + else if (funcid=='P') + { + if (DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)!='P' || GetTransaction(withdrawtxid,tx,hashBlock,false)==0 + || (numvouts=tx.vout.size())<=0 || DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin || tmpbindtxid!=bindtxid) + continue; + } + Getscriptaddress(destaddr,tx.vout[1].scriptPubKey); + GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); + if ( strcmp(destaddr,coinaddr) == 0 ) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("withdrawtxid",uint256_str(str,tx.GetHash()))); + CCCustomtxidaddr(txidaddr,tx.GetHash(),taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxidaddr",txidaddr)); + obj.push_back(Pair("withdrawaddr",withaddr)); + sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); + obj.push_back(Pair("amount",numstr)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(tx.GetHash()))); + if ( queueflag != 0 ) + { + obj.push_back(Pair("depositaddr",burnaddr)); + GetCustomscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG,taddr,prefix,prefix2); + obj.push_back(Pair("signeraddr",signeraddr)); + } + if (N>1) + { + obj.push_back(Pair("number_of_signs",K)); + obj.push_back(Pair("last_txid",uint256_str(str,txid))); + if (K>0) obj.push_back(Pair("hex",hex)); + } + pending.push_back(obj); + } + } + } + result.push_back(Pair("coin",refcoin)); + result.push_back(Pair("pending",pending)); + result.push_back(Pair("queueflag",queueflag)); + return(result); +} + +UniValue ImportGatewayProcessedWithdraws(uint256 bindtxid,std::string refcoin) +{ + UniValue result(UniValue::VOBJ),processed(UniValue::VARR); CTransaction tx; std::string coin,hex; + CPubKey mypk,importgatewaypk,withdrawpub; std::vector msigpubkeys; + uint256 withdrawtxid,hashBlock,txid,tmptokenid,oracletxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char burnaddr[65],coinaddr[65],str[65],numstr[32],withaddr[65],txidaddr[65]; + int32_t i,n,numvouts,vout,queueflag; int64_t nValue,amount; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,importgatewaypk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + n = msigpubkeys.size(); + queueflag = 0; + for (i=0; i >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && + DecodeImportGatewayCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex) == 'S' && refcoin == coin && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) + { + if (GetTransaction(withdrawtxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 + && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount) == 'W' || refcoin!=coin) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("completesigningtxid",uint256_str(str,txid))); + obj.push_back(Pair("withdrawtxid",uint256_str(str,withdrawtxid))); + CCCustomtxidaddr(txidaddr,withdrawtxid,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxidaddr",txidaddr)); + GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawaddr",withaddr)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); + sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); + obj.push_back(Pair("amount",numstr)); + obj.push_back(Pair("hex",hex)); + processed.push_back(obj); + } + } + } + result.push_back(Pair("coin",refcoin)); + result.push_back(Pair("processed",processed)); + result.push_back(Pair("queueflag",queueflag)); + return(result); +} + +UniValue ImportGatewayList() +{ + UniValue result(UniValue::VARR); std::vector > addressIndex; + struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction vintx; std::string coin; + char str[65],burnaddr[64]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + SetCCtxids(addressIndex,cp->unspendableCCaddr); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + txid = it->first.txhash; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + { + if ( vintx.vout.size() > 0 && DecodeImportGatewayBindOpRet(burnaddr,vintx.vout[vintx.vout.size()-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 ) + { + result.push_back(uint256_str(str,txid)); + } + } + } + return(result); +} + +UniValue ImportGatewayExternalAddress(uint256 bindtxid,CPubKey pubkey) +{ + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction tx; + std::string coin; int64_t numvouts; char str[65],addr[65],burnaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + GetCustomscriptaddress(addr,CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG,taddr,prefix,prefix2); + result.push_back(Pair("result","success")); + result.push_back(Pair("address",addr)); + return(result); +} + +UniValue ImportGatewayDumpPrivKey(uint256 bindtxid,CKey key) +{ + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction tx; + std::string coin,priv; int64_t numvouts; char str[65],addr[65],burnaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + + priv=EncodeCustomSecret(key,wiftype); + result.push_back(Pair("result","success")); + result.push_back(Pair("privkey",priv.c_str())); + return(result); +} + +UniValue ImportGatewayInfo(uint256 bindtxid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::string coin; char str[67],numstr[65],burnaddr[64],gatewaystokens[64]; + uint8_t M,N; std::vector pubkeys; uint8_t taddr,prefix,prefix2,wiftype; uint256 oracletxid,hashBlock; CTransaction tx; + CPubKey ImportGatewaypk; struct CCcontract_info *cp,C; int32_t i; int64_t numvouts,remaining; std::vector msigpubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + ImportGatewaypk = GetUnspendable(cp,0); + GetTokensCCaddress(cp,gatewaystokens,ImportGatewaypk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( GetTransaction(bindtxid,tx,hashBlock,false) != 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("name","ImportGateway")); + burnaddr[0] = 0; + if ( tx.vout.size() > 0 && DecodeImportGatewayBindOpRet(burnaddr,tx.vout[tx.vout.size()-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 && M <= N && N > 0 ) + { + result.push_back(Pair("M",M)); + result.push_back(Pair("N",N)); + for (i=0; i > unspentOutputs; CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction regtx,tx; std::string name,description,format; uint256 hashBlock,txid,oracletxid,batontxid; CPubKey pk; struct CCcontract_info *cp,C; int64_t datafee,funding; char str[67],markeraddr[64],numstr[64],batonaddr[64]; std::vector data; + CTransaction regtx,tx; std::string name,description,format; uint256 hashBlock,txid,oracletxid,batontxid; CPubKey pk; + struct CCcontract_info *cp,C; int64_t datafee,funding; char str[67],markeraddr[64],numstr[64],batonaddr[64]; std::vector data; cp = CCinit(&C,EVAL_ORACLES); CCtxidaddr(markeraddr,origtxid); if ( GetTransaction(origtxid,tx,hashBlock,false) == 0 ) diff --git a/src/importcoin.cpp b/src/importcoin.cpp index 018025e5a..051129020 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -35,8 +35,7 @@ CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction b mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), CScript() << payload)); mtx.vout = payouts; auto importData = E_MARSHAL(ss << proof; ss << burnTx); - mtx.vout.insert(mtx.vout.begin(), CTxOut(0, CScript() << OP_RETURN << importData)); - + mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << importData)); if (nExpiryHeightOverride != 0) mtx.nExpiryHeight = nExpiryHeightOverride; //this is for construction of the tx used for validating importtx return CTransaction(mtx); @@ -53,29 +52,97 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb return CTxOut(value, CScript() << OP_RETURN << opret); } +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof, + uint256 bindtxid,std::vector publishers,std::vector txids,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub) +{ + std::vector opret; + opret = E_MARSHAL(ss << VARINT(targetCCid); + ss << targetSymbol; + ss << SerializeHash(payouts); + ss << rawproof; + ss << bindtxid; + ss << publishers; + ss << txids; + ss << height; + ss << burnvout; + ss << rawburntx; + ss << destpub); + + return CTxOut(value, CScript() << OP_RETURN << opret); +} -bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx, +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof,std::string srcaddr, + std::string receipt) +{ + std::vector opret; + opret = E_MARSHAL(ss << VARINT(targetCCid); + ss << targetSymbol; + ss << SerializeHash(payouts); + ss << rawproof; + ss << srcaddr; + ss << receipt); + return CTxOut(value, CScript() << OP_RETURN << opret); +} + + +bool UnmarshalImportTx(const CTransaction importTx, TxProof &proof, CTransaction &burnTx, std::vector &payouts) { std::vector vData; - GetOpReturnData(importTx.vout[0].scriptPubKey, vData); + GetOpReturnData(importTx.vout[importTx.vout.size()-1].scriptPubKey, vData); if (importTx.vout.size() < 1) return false; - payouts = std::vector(importTx.vout.begin()+1, importTx.vout.end()); + payouts = std::vector(importTx.vout.begin(), importTx.vout.end()-1); return importTx.vin.size() == 1 && importTx.vin[0].scriptSig == (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN)) && E_UNMARSHAL(vData, ss >> proof; ss >> burnTx); } -bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector&rawproof) +bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector&rawproof) { - std::vector burnOpret; uint32_t ccid = 0; + std::vector burnOpret; uint32_t ccid = 0; bool isEof=true; + if (burnTx.vout.size() == 0) return false; GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid); ss >> targetSymbol; ss >> payoutsHash; - ss >> rawproof); + ss >> rawproof; isEof=ss.eof();) || !isEof; +} + +bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt) +{ + std::vector burnOpret,rawproof; bool isEof=true; + std::string targetSymbol; uint32_t targetCCid; uint256 payoutsHash; + + if (burnTx.vout.size() == 0) return false; + GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); + return (E_UNMARSHAL(burnOpret, ss >> VARINT(targetCCid); + ss >> targetSymbol; + ss >> payoutsHash; + ss >> rawproof; + ss >> srcaddr; + ss >> receipt)); +} + +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector &publishers,std::vector &txids,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub) +{ + std::vector burnOpret,rawproof; bool isEof=true; + uint32_t targetCCid; uint256 payoutsHash; std::string targetSymbol; + + if (burnTx.vout.size() == 0) return false; + GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); + return (E_UNMARSHAL(burnOpret, ss >> VARINT(targetCCid); + ss >> targetSymbol; + ss >> payoutsHash; + ss >> rawproof; + ss >> bindtxid; + ss >> publishers; + ss >> txids; + ss >> height; + ss >> burnvout; + ss >> rawburntx; + ss >> destpub)); } diff --git a/src/importcoin.h b/src/importcoin.h index 947debcd8..8cb8dbc58 100644 --- a/src/importcoin.h +++ b/src/importcoin.h @@ -29,10 +29,15 @@ CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride = 0); CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof); +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof, + uint256 bindtxid,std::vector publishers,std::vectortxids,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub); +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof,std::string srcaddr, + std::string receipt); -bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector &rawproof); -bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx, - std::vector &payouts); +bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector &rawproof); +bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt); +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector &publishers,std::vector &txids,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub); +bool UnmarshalImportTx(const CTransaction importTx, TxProof &proof, CTransaction &burnTx,std::vector &payouts); bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state); diff --git a/src/rpc/crosschain.cpp b/src/rpc/crosschain.cpp index 710f99f09..fa795e0e5 100644 --- a/src/rpc/crosschain.cpp +++ b/src/rpc/crosschain.cpp @@ -24,6 +24,7 @@ #include "consensus/validation.h" #include "cc/eval.h" #include "cc/utils.h" +#include "cc/CCinclude.h" #include "main.h" #include "primitives/transaction.h" #include "rpc/server.h" @@ -35,6 +36,7 @@ #include "script/standard.h" #include "key_io.h" +#include "cc/CCImportGateway.h" #include #include @@ -42,19 +44,23 @@ using namespace std; +#define RETURN_IF_ERROR(CCerror) if ( CCerror != "" ) { ERR_RESULT(CCerror); return(result); } +#define ERR_RESULT(x) result.push_back(Pair("result", "error")) , result.push_back(Pair("error", x)); + +extern std::string CCerror; extern std::string ASSETCHAINS_SELFIMPORT; extern uint16_t ASSETCHAINS_CODAPORT, ASSETCHAINS_BEAMPORT; +int32_t ensure_CCrequirements(uint8_t evalcode); int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip); int32_t komodo_MoMoMdata(char *hexstr,int32_t hexsize,struct komodo_ccdataMoMoM *mdata,char *symbol,int32_t kmdheight,int32_t notarized_height); struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi); uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); extern std::string ASSETCHAINS_SELFIMPORT; -uint256 Parseuint256(char *hexstr); std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx); int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount); -std::string MakeGatewaysImportTx(uint64_t txfee, uint256 bindtxid, int32_t height, std::string refcoin, std::vectorproof, std::string rawburntx, int32_t ivout, uint256 burntxid); +std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector vouts); UniValue assetchainproof(const UniValue& params, bool fHelp) { @@ -338,7 +344,6 @@ UniValue selfimport(const UniValue& params, bool fHelp) source = params[5].get_str(); } */ - if (source == "BEAM") { if (ASSETCHAINS_BEAMPORT == 0) @@ -398,14 +403,348 @@ UniValue selfimport(const UniValue& params, bool fHelp) // source is external coin is the assetchains symbol in the burnTx OP_RETURN // burnAmount, rawtx and rawproof should be enough for gatewaysdeposit equivalent - std::string hextx = MakeGatewaysImportTx(0, bindtxid, height, source, rawproof, rawsourcetx, ivout, sourcetxid); + //std::string hextx = MakeGatewaysImportTx(0, bindtxid, height, source, rawproof, rawsourcetx, ivout, ""); - result.push_back(Pair("hex", hextx)); - result.push_back(Pair("UsedRawtxVout", ivout)); // notify user about the used vout of rawtx + // result.push_back(Pair("hex", hextx)); + // result.push_back(Pair("UsedRawtxVout", ivout)); // notify user about the used vout of rawtx } return result; } +UniValue importdual(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx; + std::string hex,source,sourceaddr,destaddr,burntxid; uint64_t burnAmount; + CPubKey destpub; std::vector vouts; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importdual only works on -ac_import chains"); + + if (fHelp || params.size() < 4) + throw runtime_error("burntxid source_addr dest_pubkey amount\n"); + + CCerror = ""; + + burntxid = params[0].get_str(); + sourceaddr = params[1].get_str(); + destaddr = params[2].get_str(); + burnAmount = atof(params[3].get_str().c_str()) * COIN + 0.00000000499999; + + source = ASSETCHAINS_SELFIMPORT; //defaults to -ac_import=... param + + CTxDestination dest = DecodeDestination(destaddr.c_str()); + CScript scriptPubKey = GetScriptForDestination(dest); + vouts.push_back(CTxOut(burnAmount,scriptPubKey)); + + if (source == "BEAM") + { + if (ASSETCHAINS_BEAMPORT == 0) + return(-1); + // confirm via ASSETCHAINS_BEAMPORT that burnTx/hash is a valid BEAM burn + // return(0); + return -1; + } + else if (source == "CODA") + { + if (ASSETCHAINS_CODAPORT == 0) + return(-1); + hex=MakeCodaImportTx(0,burntxid,sourceaddr,vouts); + // confirm via ASSETCHAINS_CODAPORT that burnTx/hash is a valid CODA burn + // return(0); + } + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importdual"); + return result; +} + +UniValue importgatewayinfo(const UniValue& params, bool fHelp) +{ + uint256 txid; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaybind only works on -ac_import chains"); + if ( fHelp || params.size() != 1 ) + throw runtime_error("importgatewayinfo bindtxid\n"); + txid = Parseuint256(params[0].get_str().c_str()); + return(ImportGatewayInfo(txid)); +} + +UniValue importgatewaybind(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx; std::vector pubkey; + std::string hex,coin; int32_t i,M,N; std::vector pubkeys; + uint256 oracletxid; uint8_t p1,p2,p3,p4; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaybind only works on -ac_import chains"); + if ( fHelp || params.size() != 8) + throw runtime_error("use \'importgatewaybind coin orcletxid M N pubkeys pubtype p2shtype wiftype [taddr]\' to bind an import gateway\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + CCerror = ""; + coin = params[0].get_str(); + oracletxid = Parseuint256(params[1].get_str().c_str()); + M = atoi(params[2].get_str().c_str()); + N = atoi(params[3].get_str().c_str()); + if ( M > N || N == 0 || N > 15 ) + throw runtime_error("illegal M or N > 15\n"); + if ( params.size() < 4+N+3 ) + throw runtime_error("not enough parameters for N pubkeys\n"); + for (i=0; i 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaybind"); + return result; +} + +UniValue importgatewaydeposit(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx; std::vector rawproof; + std::string hex,coin,rawburntx; int32_t height,burnvout; + CPubKey destpub; std::vector vouts; uint256 bindtxid,burntxid; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaydeposit only works on -ac_import chains"); + if ( fHelp || params.size() != 8) + throw runtime_error("use \'importgatewaydeposit bindtxid height coin burntxid nvout rawburntx rawproof destpub\' to import deposited coins\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + CCerror = ""; + bindtxid = Parseuint256(params[0].get_str().c_str()); + height = atoi(params[1].get_str().c_str()); + coin = params[2].get_str(); + burntxid = Parseuint256(params[3].get_str().c_str()); + burnvout = atoi(params[4].get_str().c_str()); + rawburntx = params[5].get_str(); + rawproof = ParseHex(params[6].get_str()); + destpub = ParseHex(params[7].get_str()); + if (coin == "BEAM" || coin == "CODA") + { + ERR_RESULT("for BEAM and CODA import use importdual RPC"); + return result; + } + else if (coin != ASSETCHAINS_SELFIMPORT) + { + ERR_RESULT("source coin not equal to ac_import name"); + return result; + } + hex = ImportGatewayDeposit(0, bindtxid, height, coin, burntxid, burnvout, rawburntx, rawproof, destpub); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaydeposit"); + return result; +} + +UniValue importgatewaywithdraw(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx; std::vector rawproof; + std::string hex,coin,rawburntx; int64_t amount; int32_t height,burnvout; + CPubKey destpub; std::vector vouts; uint256 bindtxid,burntxid; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaywithdraw only works on -ac_import chains"); + if ( fHelp || params.size() != 4) + throw runtime_error("use \'importgatewaywithdraw bindtxid coin withdrawpub amount\' to burn imported coins and withdraw them on external chain\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + CCerror = ""; + bindtxid = Parseuint256(params[0].get_str().c_str()); + coin = params[1].get_str(); + destpub = ParseHex(params[2].get_str()); + amount = atof((char *)params[3].get_str().c_str()) * COIN + 0.00000000499999; + if (coin == "BEAM" || coin == "CODA") + { + ERR_RESULT("for BEAM and CODA import use importdual RPC"); + return result; + } + else if (coin != ASSETCHAINS_SELFIMPORT) + { + ERR_RESULT("source coin not equal to ac_import name"); + return result; + } + hex = ImportGatewayWithdraw(0, bindtxid, coin, destpub, amount); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaywithdraw"); + return result; +} + +UniValue importgatewaypartialsign(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string coin,parthex,hex; uint256 txid; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewayspartialsign only works on -ac_import chains"); + if ( fHelp || params.size() != 3 ) + throw runtime_error("importgatewayspartialsign txidaddr refcoin hex\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + txid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + parthex = params[2].get_str(); + hex = ImportGatewayPartialSign(0,txid,coin,parthex); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex",hex)); + } else ERR_RESULT("couldnt importgatewayspartialsign"); + return(result); +} + +UniValue importgatewaycompletesigning(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); uint256 withdrawtxid; std::string txhex,hex,coin; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaycompletesigning only works on -ac_import chains"); + if ( fHelp || params.size() != 3 ) + throw runtime_error("importgatewaycompletesigning withdrawtxid coin hex\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + withdrawtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + txhex = params[2].get_str(); + hex = ImportGatewayCompleteSigning(0,withdrawtxid,coin,txhex); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaycompletesigning"); + return(result); +} + +UniValue importgatewaymarkdone(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); uint256 completetxid; std::string hex,coin; + if ( fHelp || params.size() != 2 ) + throw runtime_error("importgatewaymarkdone completesigningtx coin\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + completetxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + hex = ImportGatewayMarkDone(0,completetxid,coin); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaymarkdone"); + return(result); +} + +UniValue importgatewaypendingdeposits(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; std::string coin; + if ( fHelp || params.size() != 2 ) + throw runtime_error("importgatewaypendingdeposits bindtxid coin\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + return(ImportGatewayPendingDeposits(bindtxid,coin)); +} + +UniValue importgatewaypendingwithdraws(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; std::string coin; + if ( fHelp || params.size() != 2 ) + throw runtime_error("importgatewaypendingwithdraws bindtxid coin\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + return(ImportGatewayPendingWithdraws(bindtxid,coin)); +} + +UniValue importgatewayprocessed(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; std::string coin; + if ( fHelp || params.size() != 2 ) + throw runtime_error("importgatewayprocessed bindtxid coin\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + return(ImportGatewayProcessedWithdraws(bindtxid,coin)); +} + +UniValue importgatewayexternaladdress(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; CPubKey pubkey; + + if ( fHelp || params.size() != 2) + throw runtime_error("importgatewayexternaladdress bindtxid pubkey\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + pubkey = ParseHex(params[1].get_str().c_str()); + return(ImportGatewayExternalAddress(bindtxid,pubkey)); +} + +UniValue importgatewaydumpprivkey(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; + + if ( fHelp || params.size() != 2) + throw runtime_error("importgatewaydumpprivkey bindtxid address\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + std::string strAddress = params[1].get_str(); + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid transparent address"); + } + const CKeyID *keyID = boost::get(&dest); + if (!keyID) { + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + } + CKey vchSecret; + // if (!pwalletMain->GetKey(*keyID, vchSecret)) { + // throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); + //} + return(ImportGatewayDumpPrivKey(bindtxid,vchSecret)); +} + UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -536,9 +875,9 @@ UniValue getimports(const UniValue& params, bool fHelp) UniValue objTx(UniValue::VOBJ); objTx.push_back(Pair("txid",tx.GetHash().ToString())); TxProof proof; CTransaction burnTx; std::vector payouts; CTxDestination importaddress; - TotalImported += tx.vout[1].nValue; - objTx.push_back(Pair("amount", ValueFromAmount(tx.vout[1].nValue))); - if (ExtractDestination(tx.vout[1].scriptPubKey, importaddress)) + TotalImported += tx.vout[0].nValue; + objTx.push_back(Pair("amount", ValueFromAmount(tx.vout[0].nValue))); + if (ExtractDestination(tx.vout[0].scriptPubKey, importaddress)) { objTx.push_back(Pair("address", CBitcoinAddress(importaddress).ToString())); } diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index a48ef6cb1..fc34a2a3f 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -354,6 +354,21 @@ static const CRPCCommand vRPCCommands[] = { "crosschain", "migrate_createimporttransaction", &migrate_createimporttransaction, true }, { "crosschain", "migrate_completeimporttransaction", &migrate_completeimporttransaction, true }, { "crosschain", "selfimport", &selfimport, true }, + { "crosschain", "importdual", &importdual, true }, + //ImportGateway + { "crosschain", "importgatewayddress", &importgatewayaddress, true }, + { "crosschain", "importgatewayinfo", &importgatewayinfo, true }, + { "crosschain", "importgatewaybind", &importgatewaybind, true }, + { "crosschain", "importgatewaydeposit", &importgatewaydeposit, true }, + { "crosschain", "importgatewaywithdraw", &importgatewaywithdraw, true }, + { "crosschain", "importgatewaypartialsign", &importgatewaypartialsign, true }, + { "crosschain", "importgatewaycompletesigning", &importgatewaycompletesigning, true }, + { "crosschain", "importgatewaymarkdone", &importgatewaymarkdone, true }, + { "crosschain", "importgatewaypendingdeposits", &importgatewaypendingdeposits, true }, + { "crosschain", "importgatewaypendingwithdraws", &importgatewaypendingwithdraws, true }, + { "crosschain", "importgatewayprocessed", &importgatewayprocessed, true }, + + /* Mining */ { "mining", "getblocktemplate", &getblocktemplate, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 79a12bb7b..d8fd0e736 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -314,7 +314,6 @@ extern UniValue channelsopen(const UniValue& params, bool fHelp); extern UniValue channelspayment(const UniValue& params, bool fHelp); extern UniValue channelsclose(const UniValue& params, bool fHelp); extern UniValue channelsrefund(const UniValue& params, bool fHelp); - //extern UniValue tokenswapask(const UniValue& params, bool fHelp); //extern UniValue tokenfillswap(const UniValue& params, bool fHelp); extern UniValue faucetfund(const UniValue& params, bool fHelp); @@ -435,6 +434,18 @@ extern UniValue invalidateblock(const UniValue& params, bool fHelp); extern UniValue reconsiderblock(const UniValue& params, bool fHelp); extern UniValue getspentinfo(const UniValue& params, bool fHelp); extern UniValue selfimport(const UniValue& params, bool fHelp); +extern UniValue importdual(const UniValue& params, bool fHelp); +extern UniValue importgatewayaddress(const UniValue& params, bool fHelp); +extern UniValue importgatewayinfo(const UniValue& params, bool fHelp); +extern UniValue importgatewaybind(const UniValue& params, bool fHelp); +extern UniValue importgatewaydeposit(const UniValue& params, bool fHelp); +extern UniValue importgatewaywithdraw(const UniValue& params, bool fHelp); +extern UniValue importgatewaypartialsign(const UniValue& params, bool fHelp); +extern UniValue importgatewaycompletesigning(const UniValue& params, bool fHelp); +extern UniValue importgatewaymarkdone(const UniValue& params, bool fHelp); +extern UniValue importgatewaypendingdeposits(const UniValue& params, bool fHelp); +extern UniValue importgatewaypendingwithdraws(const UniValue& params, bool fHelp); +extern UniValue importgatewayprocessed(const UniValue& params, bool fHelp); extern UniValue getblocksubsidy(const UniValue& params, bool fHelp); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index d720cf33f..77b2fb62d 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -5858,6 +5858,19 @@ UniValue tokenaddress(const UniValue& params, bool fHelp) return(CCaddress(cp,(char *)"Tokens", pubkey)); } +UniValue importgatewayaddress(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; std::vector pubkey; + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( fHelp || params.size() > 1 ) + throw runtime_error("importgatewayddress [pubkey]\n"); + if ( ensure_CCrequirements(0) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + if ( params.size() == 1 ) + pubkey = ParseHex(params[0].get_str().c_str()); + return(CCaddress(cp,(char *)"ImportGateway", pubkey)); +} + UniValue marmara_poolpayout(const UniValue& params, bool fHelp) { int32_t firstheight; double perc; char *jsonstr; @@ -7994,7 +8007,6 @@ UniValue test_heirmarker(const UniValue& params, bool fHelp) return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); } - UniValue test_burntx(const UniValue& params, bool fHelp) { // make fake token tx: From ba0fda90b278fd51cf1b2b444ea6bea6916dc9a8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 10 Apr 2019 02:09:18 -1100 Subject: [PATCH 778/787] Small changes --- src/komodo_gateway.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index ba15746e0..5aabbffd5 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1551,7 +1551,7 @@ void komodo_passport_iteration() extern std::vector Mineropret; // opreturn data set by the data gathering code #define PRICES_MAXCHANGE (COIN / 100) // maximum acceptable change, set at 1% #define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR -#define KOMODO_LOCALPRICE_CACHESIZE 7 +#define KOMODO_LOCALPRICE_CACHESIZE 13 #define KOMODO_MAXPRICES 2048 #define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) @@ -1815,7 +1815,10 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i break; } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) + { + komodo_queuelocalprice(1,block.GetHash(),i,prevbits[i]); break; + } } else if ( maxflag < 0 && localbits[i] > prevbits[i] ) { @@ -1828,7 +1831,10 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i break; } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) + { + komodo_queuelocalprice(-1,block.GetHash(),i,prevbits[i]); break; + } } } } From ae7c5ee027aa4db23e7b919f77d413bc9506655e Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 10 Apr 2019 02:13:07 -1100 Subject: [PATCH 779/787] Fix declaration --- src/komodo_gateway.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 5aabbffd5..44ec7c9f3 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1720,11 +1720,10 @@ CScript komodo_mineropret(int32_t nHeight) The only way komodo_opretvalidate() doesnt return an error is if maxflag is set or it is within tolerance of both the prior block and the local data. The local data validation only happens if it is a recent block and not a block from the past as the local node is only getting the current price data. */ -// for PRICES: reconsiderblock 002aca768b09dfcf36bd934ab34b23983148b116e12cb0b6e1a3f895d1db63aa -// and -// reconsiderblock 0034cf582018eacc0b4ae001491ce460113514cb1a3f217567ef4a2207de361a -// reconsiderbloc 000abf51c023b64af327c50c1b060797b8cb281c696d30ab92fd002a8b8c9aea -// are needed to sync past initial blocks with different data set +void komodo_queuelocalprice(int32_t dir,uint256 blockhash,int32_t ind,uint32_t pricebits) +{ + +} int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { @@ -1816,7 +1815,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) { - komodo_queuelocalprice(1,block.GetHash(),i,prevbits[i]); + komodo_queuelocalprice(1,block.GetBlockHash(),i,prevbits[i]); break; } } @@ -1832,7 +1831,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) { - komodo_queuelocalprice(-1,block.GetHash(),i,prevbits[i]); + komodo_queuelocalprice(-1,block.GetBlockHash(),i,prevbits[i]); break; } } From 6618a43e8a8e137243572ccfd8d2b10dec1d49c2 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 10 Apr 2019 02:14:07 -1100 Subject: [PATCH 780/787] -> --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 44ec7c9f3..82491941f 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1815,7 +1815,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) { - komodo_queuelocalprice(1,block.GetBlockHash(),i,prevbits[i]); + komodo_queuelocalprice(1,block->GetBlockHash(),i,prevbits[i]); break; } } @@ -1831,7 +1831,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) { - komodo_queuelocalprice(-1,block.GetBlockHash(),i,prevbits[i]); + komodo_queuelocalprice(-1,block->GetBlockHash(),i,prevbits[i]); break; } } From 137f51651603c5a681699c6d3c9cd7202a55f9c1 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 10 Apr 2019 02:16:07 -1100 Subject: [PATCH 781/787] GetHash() --- src/komodo_gateway.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 82491941f..3db8bfb1a 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1815,7 +1815,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) { - komodo_queuelocalprice(1,block->GetBlockHash(),i,prevbits[i]); + komodo_queuelocalprice(1,block->GetHash(),i,prevbits[i]); break; } } @@ -1831,7 +1831,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) { - komodo_queuelocalprice(-1,block->GetBlockHash(),i,prevbits[i]); + komodo_queuelocalprice(-1,block->GetHash(),i,prevbits[i]); break; } } From a2cdadf17de5e7c0b06e69f21ee3612075d52ff5 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 10 Apr 2019 02:43:11 -1100 Subject: [PATCH 782/787] Add auto resync for local prices --- src/komodo_gateway.h | 43 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3db8bfb1a..70a69ebc8 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1720,17 +1720,33 @@ CScript komodo_mineropret(int32_t nHeight) The only way komodo_opretvalidate() doesnt return an error is if maxflag is set or it is within tolerance of both the prior block and the local data. The local data validation only happens if it is a recent block and not a block from the past as the local node is only getting the current price data. */ -void komodo_queuelocalprice(int32_t dir,uint256 blockhash,int32_t ind,uint32_t pricebits) + +struct komodo_extremeprice { - + uint256 blockhash; + uint32_t pricebits,timestamp; + int32_t height; + int16_t dir,ind; +} ExtremePrice; + +void komodo_queuelocalprice(int32_t dir,int32_t height,uint32_t timestamp,uint256 blockhash,int32_t ind,uint32_t pricebits) +{ + ExtremePrice.dir = dir; + ExtremePrice.height = height; + ExtremePrice.blockhash = blockhash; + ExtremePrice.ind = ind; + ExtremePrice.timestamp = timestamp; + ExtremePrice.pricebits = pricebits; } int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { int32_t testchain_exemption = 0; - std::vector vopret; char maxflags[KOMODO_MAXPRICES]; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now = (uint32_t)time(NULL); + std::vector vopret; char maxflags[KOMODO_MAXPRICES]; uint245 bhash; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now; + now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { + bhash = block->GetHash(); GetOpReturnData(scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) { @@ -1815,7 +1831,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) { - komodo_queuelocalprice(1,block->GetHash(),i,prevbits[i]); + komodo_queuelocalprice(1,nHeight,block->nTime,bhash,i,prevbits[i]); break; } } @@ -1831,7 +1847,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( j == KOMODO_LOCALPRICE_CACHESIZE ) { - komodo_queuelocalprice(-1,block->GetHash(),i,prevbits[i]); + komodo_queuelocalprice(-1,nHeight,block->nTime,bhash,i,prevbits[i]); break; } } @@ -1849,6 +1865,11 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } } } + if ( bhash == ExtremePrice.blockhash ) + { + fprintf(stderr,"approved a previously extreme price based on new data ht.%d vs %u vs %u\n",ExtremePrice.height,ExtremePrice.timestamp,(uint32_t)block.nTime); + memset(&ExtremePrice,0,sizeof(ExtremePrice)); + } return(0); } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)Mineropret.size(),(int32_t)scriptPubKey.size(),scriptPubKey[0]); return(-1); @@ -2129,7 +2150,7 @@ void komodo_cbopretupdate(int32_t forceflag) { static uint32_t lasttime,lastcrypto,lastbtc,pending; static uint32_t pricebits[4],cryptoprices[KOMODO_MAXPRICES],forexprices[sizeof(Forex)/sizeof(*Forex)]; - int32_t size; uint32_t flags=0,now; + int32_t size; uint32_t flags=0,now; CBlockIndex *pindex; if ( forceflag != 0 && pending != 0 ) { while ( pending != 0 ) @@ -2187,6 +2208,16 @@ void komodo_cbopretupdate(int32_t forceflag) if ( (flags & 4) != 0 ) lastcrypto = now; memcpy(Mineropret.data(),PriceCache[0],size); + if ( ExtremePrice.dir != 0 && ExtremePrice.ind > 0 && ExtremePrice.ind < size/sizeof(uint32_t) && now < ExtremePrice.timestamp+3600 ) + { + if ( (ExtremePrice.dir > 0 && PriceCache[0][ExtremePrice.ind] >= ExtremePrice.pricebits) || (ExtremePrice.dir < 0 && PriceCache[0][ExtremePrice.ind] <= ExtremePrice.pricebits) ) + { + fprintf(stderr,"future price is close enough to allow approving previously rejected block ind.%d %u vs %u ht.%d\n",ExtremePrice.ind,PriceCache[0][ExtremePrice.ind],ExtremePrice.pricebits,height); + if ( (pindex= komodo_blockindex(ExtremePrice.blockhash)) != 0 ) + pindex->nStatus &= ~BLOCK_FAILED_MASK; + else fprintf(stderr,"couldnt find block.%s\n",ExtremePrice.blockhash.GetHex()); + } + } // high volatility still strands nodes so we need to check new prices to approve a stuck block // scan list of stuck blocks (one?) and auto reconsiderblock if it changed state From 43b18d52599059914b249e9c62a74c68cd02ec77 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 10 Apr 2019 02:47:17 -1100 Subject: [PATCH 783/787] Fix --- src/komodo_gateway.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 70a69ebc8..9afc9845c 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1742,7 +1742,7 @@ void komodo_queuelocalprice(int32_t dir,int32_t height,uint32_t timestamp,uint25 int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { int32_t testchain_exemption = 0; - std::vector vopret; char maxflags[KOMODO_MAXPRICES]; uint245 bhash; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now; + std::vector vopret; char maxflags[KOMODO_MAXPRICES]; uint256 bhash; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now; now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { @@ -1867,7 +1867,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i } if ( bhash == ExtremePrice.blockhash ) { - fprintf(stderr,"approved a previously extreme price based on new data ht.%d vs %u vs %u\n",ExtremePrice.height,ExtremePrice.timestamp,(uint32_t)block.nTime); + fprintf(stderr,"approved a previously extreme price based on new data ht.%d vs %u vs %u\n",ExtremePrice.height,ExtremePrice.timestamp,(uint32_t)block->nTime); memset(&ExtremePrice,0,sizeof(ExtremePrice)); } return(0); @@ -2212,10 +2212,10 @@ void komodo_cbopretupdate(int32_t forceflag) { if ( (ExtremePrice.dir > 0 && PriceCache[0][ExtremePrice.ind] >= ExtremePrice.pricebits) || (ExtremePrice.dir < 0 && PriceCache[0][ExtremePrice.ind] <= ExtremePrice.pricebits) ) { - fprintf(stderr,"future price is close enough to allow approving previously rejected block ind.%d %u vs %u ht.%d\n",ExtremePrice.ind,PriceCache[0][ExtremePrice.ind],ExtremePrice.pricebits,height); + fprintf(stderr,"future price is close enough to allow approving previously rejected block ind.%d %u vs %u\n",ExtremePrice.ind,PriceCache[0][ExtremePrice.ind],ExtremePrice.pricebits); if ( (pindex= komodo_blockindex(ExtremePrice.blockhash)) != 0 ) pindex->nStatus &= ~BLOCK_FAILED_MASK; - else fprintf(stderr,"couldnt find block.%s\n",ExtremePrice.blockhash.GetHex()); + else fprintf(stderr,"couldnt find block.%s\n",ExtremePrice.blockhash.GetHex().c_str()); } } // high volatility still strands nodes so we need to check new prices to approve a stuck block From cd8d41c5f8301a7f8dba1056168926562135d7a2 Mon Sep 17 00:00:00 2001 From: Mihailo Milenkovic Date: Wed, 10 Apr 2019 18:07:53 +0200 Subject: [PATCH 784/787] Fix broken build --- src/cc/import.cpp | 84 +++-------------------------------------------- 1 file changed, 4 insertions(+), 80 deletions(-) diff --git a/src/cc/import.cpp b/src/cc/import.cpp index 157fa6888..14f359398 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -33,86 +33,10 @@ uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::ve uint256 GatewaysReverseScan(uint256 &txid, int32_t height, uint256 reforacletxid, uint256 batontxid); int32_t GatewaysCointxidExists(struct CCcontract_info *cp, uint256 cointxid); uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype); - -char *nonportable_path(char *str) -{ - int32_t i; - for (i=0; str[i]!=0; i++) - if ( str[i] == '/' ) - str[i] = '\\'; - return(str); -} - -char *portable_path(char *str) -{ -#ifdef _WIN32 - return(nonportable_path(str)); -#else -#ifdef __PNACL - /*int32_t i,n; - if ( str[0] == '/' ) - return(str); - else - { - n = (int32_t)strlen(str); - for (i=n; i>0; i--) - str[i] = str[i-1]; - str[0] = '/'; - str[n+1] = 0; - }*/ -#endif - return(str); -#endif -} - -void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) -{ - FILE *fp; - long filesize,buflen = *allocsizep; - uint8_t *buf = *bufp; - *lenp = 0; - if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) - { - fseek(fp,0,SEEK_END); - filesize = ftell(fp); - if ( filesize == 0 ) - { - fclose(fp); - *lenp = 0; - //printf("loadfile null size.(%s)\n",fname); - return(0); - } - if ( filesize > buflen ) - { - *allocsizep = filesize; - *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); - } - rewind(fp); - if ( buf == 0 ) - printf("Null buf ???\n"); - else - { - if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) - printf("error reading filesize.%ld\n",(long)filesize); - buf[filesize] = 0; - } - fclose(fp); - *lenp = filesize; - //printf("loaded.(%s)\n",buf); - } //else printf("OS_loadfile couldnt load.(%s)\n",fname); - return(buf); -} - -void *filestr(long *allocsizep,char *_fname) -{ - long filesize = 0; char *fname,*buf = 0; void *retptr; - *allocsizep = 0; - fname = (char *)malloc(strlen(_fname)+1); - strcpy(fname,_fname); - retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); - free(fname); - return(retptr); -} +char *nonportable_path(char *str); +char *portable_path(char *str); +void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep); +void *filestr(long *allocsizep,char *_fname); // ac_import=chain support: // encode opret for gateways import From 2056c8727be31a38ae563bc207e4b3b8df26e1ae Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 10 Apr 2019 05:14:54 -1100 Subject: [PATCH 785/787] Token peg ideas --- src/cc/pegs.cpp | 6 ++++++ src/cc/prices.cpp | 3 +++ 2 files changed, 9 insertions(+) diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index 00d76d8fd..38933ebdc 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -72,7 +72,13 @@ pegs CC is able to create a coin backed (by any supported coin with gateways CC Let us now consider how to enforce a peg onto a specific gateways CC token. If this can also be achieved, then a full DEX for all the different gateways CC supported coins can be created onto a single fiat denominated chain. + I think just having a pegscreate rpc call that binds an existing gateways create to a price CC syntax price will be almost enough to support this. Let us assume a USD stablechain and we have a BTC token, then pegscreate "BTCUSD, 1" + that will specify using the BTCUSD price, so now we need to create a based way to do tokenbid/tokenask. For a based price, the smoothed price is substituted. + There is the issue of the one day delay, so it might make sense to allow specific bid/ask to be based on some simple combinations of the three possible prices. it might even be possible to go a bit overboard and make a forth like syntax to define the dynamic price for a bid, which maybe at times wont be valid, like it is only valid if the three prices are within 1% of each other. But all that seems over complex and for initial release it can just use the mined, correlated or smoothed price, with some specified percentage offset + + Implementation notes: + make sure that fees and markers that can be sent to an unspendable address are sent to: RNdqHx26GWy9bk8MtmH1UiXjQcXE4RKK2P, this is the address for BOTS */ diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index ce257d33f..19194d16f 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -138,6 +138,9 @@ CBOPRET creates trustless oracles, which can be used for making a synthetic cash Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection scriptPubKey (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) this requires adding new vouts Modification: in the event there is one price in the accumulator and one price on the stack at the end, then it is a (A - B) spread + + Monetizations should be sent to: RGsWqwFviaNJSbmgWi6338NL2tKkY7ZqKL + */ From 9b62776a8be938892056378868a4d2ff9f7d6d77 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 10 Apr 2019 05:27:06 -1100 Subject: [PATCH 786/787] -conflict --- src/rpc/crosschain.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rpc/crosschain.cpp b/src/rpc/crosschain.cpp index 74ac53c44..0feb5173c 100644 --- a/src/rpc/crosschain.cpp +++ b/src/rpc/crosschain.cpp @@ -413,9 +413,9 @@ UniValue selfimport(const UniValue& params, bool fHelp) return result; } -<<<<<<< HEAD bool GetNotarisationNotaries(uint8_t notarypubkeys[64][33], int8_t &numNN, const std::vector &vin, std::vector &NotarisationNotaries); -======= + + UniValue importdual(const UniValue& params, bool fHelp) { UniValue result(UniValue::VOBJ); @@ -749,7 +749,6 @@ UniValue importgatewaydumpprivkey(const UniValue& params, bool fHelp) //} return(ImportGatewayDumpPrivKey(bindtxid,vchSecret)); } ->>>>>>> FSM UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) { From 0e2969debb70a41c1ab24340ee1863d7fb5da179 Mon Sep 17 00:00:00 2001 From: blackjok3r Date: Thu, 11 Apr 2019 14:28:24 +0800 Subject: [PATCH 787/787] notarystats.py update to official regions/stop height. --- src/notarystats.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/notarystats.py b/src/notarystats.py index 0e7673833..49713b6c3 100755 --- a/src/notarystats.py +++ b/src/notarystats.py @@ -53,11 +53,11 @@ def def_credentials(chain): rpc = def_credentials('KMD') pp = pprint.PrettyPrinter(indent=2) -notarynames = [ "0dev1_jl777", "0dev2_kolo", "0dev3_kolo", "0dev4_decker", "a-team_SH", "artik_AR", "artik_EU", "artik_NA", "artik_SH", "badass_EU", "badass_NA", "batman_AR", "batman_SH", "ca333_EU", "chainmakers_EU", "chainmakers_NA", "chainstrike_SH", "cipi_AR", "cipi_NA", "crackers_EU", "crackers_NA", "dwy_EU", "emmanux_SH", "etszombi_EU", "fullmoon_AR", "fullmoon_NA", "fullmoon_SH", "goldenman_EU", "indenodes_AR", "indenodes_EU", "indenodes_NA", "indenodes_SH", "jackson_AR", "jeezy_EU", "karasugoi_NA", "komodoninja_EU", "komodoninja_SH", "komodopioneers_SH", "libscott_SH", "lukechilds_AR", "madmax_AR", "meshbits_AR", "meshbits_SH", "metaphilibert_AR", "metaphilibert_SH", "patchkez_SH", "pbca26_NA", "peer2cloud_AR", "peer2cloud_SH", "polycryptoblog_NA", "hyper_AR", "hyper_EU", "hyper_SH", "hyper_NA", "popcornbag_AR", "popcornbag_NA", "alien_AR", "alien_EU", "thegaltmines_NA", "titomane_AR", "titomane_EU", "titomane_SH", "webworker01_NA", "xrobesx_NA" ] +notarynames = [ "0dev1_jl777", "0dev2_kolo", "0dev3_kolo", "0dev4_decker_AR", "a-team_SH", "artik_AR", "artik_EU", "artik_NA", "artik_SH", "badass_EU", "badass_NA", "batman_AR", "batman_SH", "ca333", "chainmakers_EU", "chainmakers_NA", "chainstrike_SH", "cipi_AR", "cipi_NA", "crackers_EU", "crackers_NA", "dwy_EU", "emmanux_SH", "etszombi_EU", "fullmoon_AR", "fullmoon_NA", "fullmoon_SH", "goldenman_EU", "indenodes_AR", "indenodes_EU", "indenodes_NA", "indenodes_SH", "jackson_AR", "jeezy_EU", "karasugoi_NA", "komodoninja_EU", "komodoninja_SH", "komodopioneers_SH", "libscott_SH", "lukechilds_AR", "madmax_AR", "meshbits_AR", "meshbits_SH", "metaphilibert_AR", "metaphilibert_SH", "patchkez_SH", "pbca26_NA", "peer2cloud_AR", "peer2cloud_SH", "polycryptoblog_NA", "hyper_AR", "hyper_EU", "hyper_SH", "hyper_NA", "popcornbag_AR", "popcornbag_NA", "alien_AR", "alien_EU", "thegaltmines_NA", "titomane_AR", "titomane_EU", "titomane_SH", "webworker01_NA", "xrobesx_NA" ] notaries = 64 * [0] startheight = 821657 #Second time filter for assetchains (block 821657) for KMD its 814000 -stopheight = rpc.getblockcount() # @kolo what height does season end?! +stopheight = 1307200 for i in range(startheight,stopheight): ret = rpc.getNotarisationsForBlock(i) KMD = ret['KMD']

Jq5C-aTifz(E5Kg}P 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⋙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 LNkvXXu0mjfL3yYq literal 0 HcmV?d00001 diff --git a/src/gui/komodoku/board.png b/src/gui/komodoku/board.png new file mode 100644 index 0000000000000000000000000000000000000000..bd77ca419999b45f31af1dc6e91aedd4b0f53a9f GIT binary patch literal 5678 zcmeAS@N?(olHy`uVBq!ia0y~yV5|jU4mP03i^?5=K#C>Z(btiI;o6NW{t-q%zGR7O zL`iUdT1k0gQ7VIDN`6wRf@f}GdTLN=VoGJ<$y6H#2GIkaE{-7;w~|s45`LUF;Bq^t zA;G#h!G)oLAxVn2DG(xJ$)L~>k~F@M{Q-MU|kN+74koMTY6Tl7ztmwLMJ)o!OKl^U$ z_Acp(KNlC;OMPGb(5mQ>955ZVmqSZM7DiyHsQbko(uWx3jfTYH1W+nSVtMp`@j+w+V|BDHrVGdyBIgycp zse|>{kN^MeA1_$HP(t!Qe_1)7sJYT-Q19TK`{B|jM{I$qitjzNR2=QlI_X`4raI^*LDqz40|2~awwXxSpWxo89ZJ6T-G@yGywpilHk$+ literal 0 HcmV?d00001 diff --git a/src/gui/komodoku/sudoku_kmdlib.py b/src/gui/komodoku/sudoku_kmdlib.py new file mode 100644 index 000000000..a2b2aa239 --- /dev/null +++ b/src/gui/komodoku/sudoku_kmdlib.py @@ -0,0 +1,41 @@ +import platform +import os +import re +import random +from slickrpc import Proxy + + +# define function that fetchs rpc creds from .conf +def def_credentials(chain): + rpcport =''; + operating_system = platform.system() + if operating_system == 'Darwin': + ac_dir = os.environ['HOME'] + '/Library/Application Support/Komodo' + elif operating_system == 'Linux': + ac_dir = os.environ['HOME'] + '/.komodo' + elif operating_system == 'Windows': + ac_dir = '%s/komodo/' % os.environ['APPDATA'] + if chain == 'KMD': + coin_config_file = str(ac_dir + '/komodo.conf') + else: + coin_config_file = str(ac_dir + '/' + chain + '/' + chain + '.conf') + with open(coin_config_file, 'r') as f: + for line in f: + l = line.rstrip() + if re.search('rpcuser', l): + rpcuser = l.replace('rpcuser=', '') + elif re.search('rpcpassword', l): + rpcpassword = l.replace('rpcpassword=', '') + elif re.search('rpcport', l): + rpcport = l.replace('rpcport=', '') + if len(rpcport) == 0: + if chain == 'KMD': + rpcport = 7771 + else: + print("rpcport not in conf file, exiting") + print("check "+coin_config_file) + exit(1) + + return(Proxy("http://%s:%s@127.0.0.1:%d"%(rpcuser, rpcpassword, int(rpcport)))) + + From 6c49bda16bb1aefda799bd2ef6585985202cd906 Mon Sep 17 00:00:00 2001 From: Anton Lysakov Date: Fri, 29 Mar 2019 00:42:43 +0700 Subject: [PATCH 469/787] moved to subfolder --- src/gui/README.md | 27 --- src/gui/Roboto-Light.ttf | Bin 140276 -> 0 bytes src/gui/Sudoku.py | 362 --------------------------------------- src/gui/background.png | Bin 308479 -> 0 bytes src/gui/board.png | Bin 5678 -> 0 bytes src/gui/sudoku_kmdlib.py | 41 ----- 6 files changed, 430 deletions(-) delete mode 100644 src/gui/README.md delete mode 100755 src/gui/Roboto-Light.ttf delete mode 100644 src/gui/Sudoku.py delete mode 100644 src/gui/background.png delete mode 100644 src/gui/board.png delete mode 100644 src/gui/sudoku_kmdlib.py diff --git a/src/gui/README.md b/src/gui/README.md deleted file mode 100644 index 8a3778ea0..000000000 --- a/src/gui/README.md +++ /dev/null @@ -1,27 +0,0 @@ -About ------ -Komodo SudokuCC GUI - -Just solve Sudoku and earn SUDOKU coins! - -![alt text](https://i.imgur.com/std99XW.png) - -To run you need up and running SUDOKU chain daemon built from latest https://github.com/jl777/komodo/tree/FSM and started with valid for your wallet pubkey in `-pubkey=` param. - -SUDOKU chain params: -```./komodod -ac_name=SUDOKU -ac_supply=1000000 -pubkey= -addnode=5.9.102.210 -gen -genproclimit=1 -ac_cclib=sudoku -ac_perc=10000000 -ac_reward=100000000 -ac_cc=60000 -ac_script=2ea22c80203d1579313abe7d8ea85f48c65ea66fc512c878c0d0e6f6d54036669de940febf8103120c008203000401cc &``` - -1) install dependencies: - -``` -$ sudo apt-get install python-pygame libgnutls28-dev -$ pip install requests wheel slick-bitcoinrpc pygame -``` - -2) and then start: - -``` -$ git clone https://github.com/tonymorony/Komodoku -$ cd Komodoku -$ python Sudoku.py -``` diff --git a/src/gui/Roboto-Light.ttf b/src/gui/Roboto-Light.ttf deleted file mode 100755 index 664e1b2f9dbafbf6280305123d2df7fa0c66cee9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 140276 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

Jq5C-aTifz(E5Kg}P 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⋙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 LNkvXXu0mjfL3yYq literal 0 HcmV?d00001 diff --git a/src/gui/board.png b/src/gui/board.png new file mode 100644 index 0000000000000000000000000000000000000000..bd77ca419999b45f31af1dc6e91aedd4b0f53a9f GIT binary patch literal 5678 zcmeAS@N?(olHy`uVBq!ia0y~yV5|jU4mP03i^?5=K#C>Z(btiI;o6NW{t-q%zGR7O zL`iUdT1k0gQ7VIDN`6wRf@f}GdTLN=VoGJ<$y6H#2GIkaE{-7;w~|s45`LUF;Bq^t zA;G#h!G)oLAxVn2DG(xJ$)L~>k~F@M{Q-MU|kN+74koMTY6Tl7ztmwLMJ)o!OKl^U$ z_Acp(KNlC;OMPGb(5mQ>955ZVmqSZM7DiyHsQbko(uWx3jfTYH1W+nSVtMp`@j+w+V|BDHrVGdyBIgycp zse|>{kN^MeA1_$HP(t!Qe_1)7sJYT-Q19TK`{B|jM{I$qitjzNR2=QlI_X`4raI^*LDqz40|2~awwXxSpWxo89ZJ6T-G@yGywpilHk$+ literal 0 HcmV?d00001 diff --git a/src/gui/sudoku_kmdlib.py b/src/gui/sudoku_kmdlib.py new file mode 100644 index 000000000..a2b2aa239 --- /dev/null +++ b/src/gui/sudoku_kmdlib.py @@ -0,0 +1,41 @@ +import platform +import os +import re +import random +from slickrpc import Proxy + + +# define function that fetchs rpc creds from .conf +def def_credentials(chain): + rpcport =''; + operating_system = platform.system() + if operating_system == 'Darwin': + ac_dir = os.environ['HOME'] + '/Library/Application Support/Komodo' + elif operating_system == 'Linux': + ac_dir = os.environ['HOME'] + '/.komodo' + elif operating_system == 'Windows': + ac_dir = '%s/komodo/' % os.environ['APPDATA'] + if chain == 'KMD': + coin_config_file = str(ac_dir + '/komodo.conf') + else: + coin_config_file = str(ac_dir + '/' + chain + '/' + chain + '.conf') + with open(coin_config_file, 'r') as f: + for line in f: + l = line.rstrip() + if re.search('rpcuser', l): + rpcuser = l.replace('rpcuser=', '') + elif re.search('rpcpassword', l): + rpcpassword = l.replace('rpcpassword=', '') + elif re.search('rpcport', l): + rpcport = l.replace('rpcport=', '') + if len(rpcport) == 0: + if chain == 'KMD': + rpcport = 7771 + else: + print("rpcport not in conf file, exiting") + print("check "+coin_config_file) + exit(1) + + return(Proxy("http://%s:%s@127.0.0.1:%d"%(rpcuser, rpcpassword, int(rpcport)))) + + From f4150ce014a22d6b5b24ed1a951b81c8f1c5f6a9 Mon Sep 17 00:00:00 2001 From: Anton Lysakov Date: Fri, 29 Mar 2019 00:40:19 +0700 Subject: [PATCH 468/787] fixed path --- src/gui/komodoku/README.md | 27 +++ src/gui/komodoku/Roboto-Light.ttf | Bin 0 -> 140276 bytes src/gui/komodoku/Sudoku.py | 362 ++++++++++++++++++++++++++++++ src/gui/komodoku/background.png | Bin 0 -> 308479 bytes src/gui/komodoku/board.png | Bin 0 -> 5678 bytes src/gui/komodoku/sudoku_kmdlib.py | 41 ++++ 6 files changed, 430 insertions(+) create mode 100644 src/gui/komodoku/README.md create mode 100755 src/gui/komodoku/Roboto-Light.ttf create mode 100644 src/gui/komodoku/Sudoku.py create mode 100644 src/gui/komodoku/background.png create mode 100644 src/gui/komodoku/board.png create mode 100644 src/gui/komodoku/sudoku_kmdlib.py diff --git a/src/gui/komodoku/README.md b/src/gui/komodoku/README.md new file mode 100644 index 000000000..8a3778ea0 --- /dev/null +++ b/src/gui/komodoku/README.md @@ -0,0 +1,27 @@ +About +----- +Komodo SudokuCC GUI + +Just solve Sudoku and earn SUDOKU coins! + +![alt text](https://i.imgur.com/std99XW.png) + +To run you need up and running SUDOKU chain daemon built from latest https://github.com/jl777/komodo/tree/FSM and started with valid for your wallet pubkey in `-pubkey=` param. + +SUDOKU chain params: +```./komodod -ac_name=SUDOKU -ac_supply=1000000 -pubkey= -addnode=5.9.102.210 -gen -genproclimit=1 -ac_cclib=sudoku -ac_perc=10000000 -ac_reward=100000000 -ac_cc=60000 -ac_script=2ea22c80203d1579313abe7d8ea85f48c65ea66fc512c878c0d0e6f6d54036669de940febf8103120c008203000401cc &``` + +1) install dependencies: + +``` +$ sudo apt-get install python-pygame libgnutls28-dev +$ pip install requests wheel slick-bitcoinrpc pygame +``` + +2) and then start: + +``` +$ git clone https://github.com/tonymorony/Komodoku +$ cd Komodoku +$ python Sudoku.py +``` diff --git a/src/gui/komodoku/Roboto-Light.ttf b/src/gui/komodoku/Roboto-Light.ttf new file mode 100755 index 0000000000000000000000000000000000000000..664e1b2f9dbafbf6280305123d2df7fa0c66cee9 GIT binary patch literal 140276 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