diff --git a/CMakeLists.txt b/CMakeLists.txt index f8e60f19f..a0fb1cdfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -58,6 +58,7 @@ SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fPIC") #ADD_DEFINITIONS(-DBOOST_NO_AUTO_PTR=1 -DBOOST_NO_RTTI=1 -DBOOST_NO_TYPEID=1) IF(CMAKE_BUILD_TYPE STREQUAL "DEBUG") ADD_DEFINITIONS(-DMVS_DEBUG=1) + ADD_DEFINITIONS(-DBOOST_CB_DISABLE_DEBUG=1) ENDIF() # --------------- Outputs --------------------- @@ -131,6 +132,7 @@ INCLUDE_DIRECTORIES("${ZeroMQ_INCLUDE_DIRS}") IF(ENABLE_SHARED_LIBS) SET(mongoose_LIBRARY mongoose_shared) SET(jsoncpp_LIBRARY jsoncpp_shared) + SET(sodium_LIBRARY sodium_shared) SET(cryptojs_LIBRARY cryptojs_shared) SET(bitcoin_LIBRARY bitcoin_shared) SET(bitcoinmath_LIBRARY bitcoinmath_shared) @@ -144,6 +146,7 @@ IF(ENABLE_SHARED_LIBS) ELSE() SET(mongoose_LIBRARY mongoose_static) SET(jsoncpp_LIBRARY jsoncpp_static) + SET(sodium_LIBRARY sodium_static) SET(cryptojs_LIBRARY cryptojs_static) SET(bitcoin_LIBRARY bitcoin_static) SET(bitcoinmath_LIBRARY bitcoinmath_static) diff --git a/README.md b/README.md index 1f3056d83..1974a5e65 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,15 @@ $ ./configure --enable-module-recovery $ make -j4 $ sudo make install && sudo ldconfig ``` +Sometimes we may meet the following compile error +``` +undefined reference to '__gmpn_sub_n' ... +``` +we may disable bignum in secp256k1 in this situation, use +``` +$ ./configure --enable-module-recovery --with-bignum=no +``` +and see more information here [#issue209](https://github.com/mvs-org/metaverse/issues/209) ## miniupnpc Modules blockchain/network with UPnP function required. diff --git a/builds/mac/Metaverse.xcodeproj/project.pbxproj b/builds/mac/Metaverse.xcodeproj/project.pbxproj index 33d7e0ff7..d104c3d26 100644 --- a/builds/mac/Metaverse.xcodeproj/project.pbxproj +++ b/builds/mac/Metaverse.xcodeproj/project.pbxproj @@ -7,30 +7,30 @@ objects = { /* Begin PBXBuildFile section */ - 4C05ECA81F502042006CCA1D /* mvsd in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4C05ECA71F502042006CCA1D /* mvsd */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 49F4E035215B20F000B18D78 /* mvsd in Copy Files */ = {isa = PBXBuildFile; fileRef = 49F4E034215B20EF00B18D78 /* mvsd */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 4CD414641F4FF0F800A6CA41 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4CD414631F4FF0F800A6CA41 /* Assets.xcassets */; }; 4CE027CD1F4FC6FE004FB704 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4CE027CB1F4FC6FE004FB704 /* MainMenu.xib */; }; - 4CE027CF1F4FC70A004FB704 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4CE027CE1F4FC70A004FB704 /* Info.plist */; }; 4CEF0CD41F553C3400328974 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEF0CD31F553C3400328974 /* AppDelegate.swift */; }; 4CEF0CD61F553C4700328974 /* GetBSDProcessList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CEF0CD51F553C4700328974 /* GetBSDProcessList.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ - 0A7A475B1E3D2C800093D1AB /* CopyFiles */ = { + 0A7A475B1E3D2C800093D1AB /* Copy Files */ = { isa = PBXCopyFilesBuildPhase; - buildActionMask = 8; + buildActionMask = 12; dstPath = ""; dstSubfolderSpec = 6; files = ( - 4C05ECA81F502042006CCA1D /* mvsd in CopyFiles */, + 49F4E035215B20F000B18D78 /* mvsd in Copy Files */, ); - runOnlyForDeploymentPostprocessing = 1; + name = "Copy Files"; + runOnlyForDeploymentPostprocessing = 0; }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 0ACF9ABE1E30FAB600D5C935 /* Metaverse.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Metaverse.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 4C05ECA71F502042006CCA1D /* mvsd */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = mvsd; path = ../build/bin/mvsd; sourceTree = ""; }; + 49F4E034215B20EF00B18D78 /* mvsd */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = mvsd; path = "installer/mvs-pkg/bin/mvsd"; sourceTree = ""; }; 4CD414631F4FF0F800A6CA41 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Metaverse/Assets.xcassets; sourceTree = SOURCE_ROOT; }; 4CE027CC1F4FC6FE004FB704 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = MainMenu.xib; sourceTree = ""; }; 4CE027CE1F4FC70A004FB704 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Metaverse/Info.plist; sourceTree = SOURCE_ROOT; }; @@ -52,7 +52,7 @@ 0ACF9AB51E30FAB600D5C935 = { isa = PBXGroup; children = ( - 4C05ECA71F502042006CCA1D /* mvsd */, + 49F4E034215B20EF00B18D78 /* mvsd */, 0ACF9AC01E30FAB600D5C935 /* Metaverse */, 0ACF9ABF1E30FAB600D5C935 /* Products */, ); @@ -97,7 +97,7 @@ 0ACF9ABA1E30FAB600D5C935 /* Sources */, 0ACF9ABB1E30FAB600D5C935 /* Frameworks */, 0ACF9ABC1E30FAB600D5C935 /* Resources */, - 0A7A475B1E3D2C800093D1AB /* CopyFiles */, + 0A7A475B1E3D2C800093D1AB /* Copy Files */, ); buildRules = ( ); @@ -115,13 +115,13 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0800; - LastUpgradeCheck = 0830; + LastUpgradeCheck = 0940; ORGANIZATIONNAME = "Metaverse Fundation"; TargetAttributes = { 0ACF9ABD1E30FAB600D5C935 = { CreatedOnToolsVersion = 8.0; - DevelopmentTeam = 6EDHACMBKZ; - LastSwiftMigration = 0830; + DevelopmentTeam = Z9LLD4BACL; + LastSwiftMigration = 0940; ProvisioningStyle = Automatic; }; }; @@ -149,7 +149,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4CE027CF1F4FC70A004FB704 /* Info.plist in Resources */, 4CE027CD1F4FC6FE004FB704 /* MainMenu.xib in Resources */, 4CD414641F4FF0F800A6CA41 /* Assets.xcassets in Resources */, ); @@ -190,15 +189,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -206,6 +213,7 @@ CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 9D4XG38K67; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -241,15 +249,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -257,6 +273,7 @@ CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 9D4XG38K67; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -269,6 +286,7 @@ GCC_WARN_UNUSED_VARIABLE = YES; MACOSX_DEPLOYMENT_TARGET = 10.12; MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = NO; PRODUCT_NAME = Metaverse; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; @@ -280,9 +298,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 6EDHACMBKZ; + DEVELOPMENT_TEAM = Z9LLD4BACL; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; INFOPLIST_FILE = "$(SRCROOT)/Metaverse/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; @@ -299,9 +318,10 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = "Mac Developer"; + CODE_SIGN_IDENTITY = ""; + CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; - DEVELOPMENT_TEAM = 6EDHACMBKZ; + DEVELOPMENT_TEAM = Z9LLD4BACL; GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = NO; INFOPLIST_FILE = "$(SRCROOT)/Metaverse/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; @@ -322,7 +342,7 @@ 0ACF9ACA1E30FAB600D5C935 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Debug; + defaultConfigurationName = Release; }; 0ACF9ACB1E30FAB600D5C935 /* Build configuration list for PBXNativeTarget "Metaverse" */ = { isa = XCConfigurationList; @@ -331,7 +351,7 @@ 0ACF9ACD1E30FAB600D5C935 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Debug; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/builds/mac/Metaverse/AppDelegate.swift b/builds/mac/Metaverse/AppDelegate.swift index 184462446..b7b30dbfe 100644 --- a/builds/mac/Metaverse/AppDelegate.swift +++ b/builds/mac/Metaverse/AppDelegate.swift @@ -155,8 +155,9 @@ class AppDelegate: NSObject, NSApplicationDelegate { let processes = GetBSDProcessList()! let metaverseProcess = processes.index(where: { var name = $0.kp_proc.p_comm + let length = MemoryLayout.size(ofValue: name) let str = withUnsafePointer(to: &name) { - $0.withMemoryRebound(to: UInt8.self, capacity: MemoryLayout.size(ofValue: name)) { + $0.withMemoryRebound(to: UInt8.self, capacity: length) { String(cString: $0) } } diff --git a/builds/mac/Metaverse/Info.plist b/builds/mac/Metaverse/Info.plist index 90465e12b..4f0f4f62c 100644 --- a/builds/mac/Metaverse/Info.plist +++ b/builds/mac/Metaverse/Info.plist @@ -27,7 +27,7 @@ LSUIElement NSHumanReadableCopyright - Copyright © 2017 Metaverse Foundation. All rights reserved. + Copyright © 2018 Metaverse Foundation. All rights reserved. NSMainNibFile MainMenu NSPrincipalClass diff --git a/builds/mac/installer/post-install-withdb.sh b/builds/mac/installer/post-install-withdb.sh index a1c37c557..09b0eb48b 100755 --- a/builds/mac/installer/post-install-withdb.sh +++ b/builds/mac/installer/post-install-withdb.sh @@ -1,9 +1,9 @@ #!/bin/bash - #=============================================================================== # -# FILE: pre-install.sh +# FILE: post-install-withdb.sh # -# USAGE: ./post-install.sh +# USAGE: ./post-install-withdb.sh # # DESCRIPTION: # diff --git a/builds/mac/installer/post-install.sh b/builds/mac/installer/post-install.sh index 0b67bffdb..fb8dfa36d 100755 --- a/builds/mac/installer/post-install.sh +++ b/builds/mac/installer/post-install.sh @@ -1,7 +1,7 @@ #!/bin/bash - #=============================================================================== # -# FILE: pre-install.sh +# FILE: post-install.sh # # USAGE: ./post-install.sh # diff --git a/builds/msvc-140/bitcoin/bitcoin.vcxproj b/builds/msvc-140/bitcoin/bitcoin.vcxproj index 30ad9c4d2..0717ca288 100644 --- a/builds/msvc-140/bitcoin/bitcoin.vcxproj +++ b/builds/msvc-140/bitcoin/bitcoin.vcxproj @@ -165,6 +165,7 @@ + @@ -231,6 +232,7 @@ + @@ -337,6 +339,7 @@ + @@ -379,6 +382,11 @@ + + + {b383014a-74fb-4241-a98a-be33b92e9ea3} + + {54C72203-A5A0-4714-8D0C-D51AA4AABAC8} Win32Proj @@ -428,7 +436,7 @@ true MultiThreadedDebug $(IntDir)a\a%(RelativeDir) - ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\..\secp256k1\include;..\..\..\src\lib\consensus\clone;..\..\..\contrib;..\..\..\..\zeromq-4.2.1\include; + ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\..\secp256k1\include;..\..\..\src\lib\consensus\clone;..\..\..\contrib;..\..\..\contrib\sodium;..\..\..\..\zeromq-4.2.1\include; Windows @@ -445,7 +453,7 @@ true MultiThreaded $(IntDir)a\a%(RelativeDir) - ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\..\secp256k1\include;..\..\..\src\lib\consensus\clone;..\..\..\contrib;..\..\..\..\zeromq-4.2.1\include; + ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\..\secp256k1\include;..\..\..\src\lib\consensus\clone;..\..\..\..\zeromq-4.2.1\include;..\..\..\contrib;..\..\..\contrib\sodium Windows diff --git a/builds/msvc-140/bitcoin/bitcoin.vcxproj.filters b/builds/msvc-140/bitcoin/bitcoin.vcxproj.filters index 97c6102f4..f2fddf939 100644 --- a/builds/msvc-140/bitcoin/bitcoin.vcxproj.filters +++ b/builds/msvc-140/bitcoin/bitcoin.vcxproj.filters @@ -581,6 +581,9 @@ Source Files\chain + + Source Files\wallet + @@ -1153,6 +1156,12 @@ Header Files + + Header Files\math + + + Header Files\wallet + diff --git a/builds/msvc-140/consensus/consensus.vcxproj b/builds/msvc-140/consensus/consensus.vcxproj index dacaf4bcb..310748223 100644 --- a/builds/msvc-140/consensus/consensus.vcxproj +++ b/builds/msvc-140/consensus/consensus.vcxproj @@ -14,6 +14,7 @@ + @@ -33,6 +34,7 @@ + @@ -80,7 +82,9 @@ + + {D21052F1-33B6-4F1F-BB8D-5C0E8173E416} diff --git a/builds/msvc-140/consensus/consensus.vcxproj.filters b/builds/msvc-140/consensus/consensus.vcxproj.filters index a1632c173..2d451a429 100644 --- a/builds/msvc-140/consensus/consensus.vcxproj.filters +++ b/builds/msvc-140/consensus/consensus.vcxproj.filters @@ -173,6 +173,12 @@ Source Files\clone\script + + Header Files + + + Header Files + @@ -250,5 +256,11 @@ Source Files\consensus + + Source Files + + + Source Files + \ No newline at end of file diff --git a/builds/msvc-140/explorer/explorer.vcxproj b/builds/msvc-140/explorer/explorer.vcxproj index 47f110bef..45dbf1509 100644 --- a/builds/msvc-140/explorer/explorer.vcxproj +++ b/builds/msvc-140/explorer/explorer.vcxproj @@ -63,9 +63,14 @@ + + + + + @@ -118,6 +123,7 @@ + @@ -184,6 +190,10 @@ + + + + @@ -227,6 +237,7 @@ + @@ -239,6 +250,7 @@ + @@ -312,7 +324,7 @@ true NDEBUG;BC_STATIC;BCC_STATIC;BCX_STATIC;BCB_STATIC;BCT_STATIC;%(PreprocessorDefinitions) MultiThreaded - ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\contrib; + ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\contrib 4996 diff --git a/builds/msvc-140/explorer/explorer.vcxproj.filters b/builds/msvc-140/explorer/explorer.vcxproj.filters index 963fb1643..9689f1df2 100644 --- a/builds/msvc-140/explorer/explorer.vcxproj.filters +++ b/builds/msvc-140/explorer/explorer.vcxproj.filters @@ -302,6 +302,9 @@ Header Files\extensions\commands + + Header Files\extensions\commands + Header Files @@ -374,6 +377,9 @@ Header Files\extensions\commands + + Header Files\extensions\commands + Header Files\extensions\commands @@ -392,6 +398,21 @@ Header Files\extensions\commands + + Header Files + + + Header Files\extensions\commands + + + Header Files\extensions\commands + + + Header Files\extensions\commands + + + Header Files\extensions\commands + @@ -624,6 +645,9 @@ Source Files\extensions\commands + + Source Files\extensions\commands + Source Files @@ -741,6 +765,21 @@ Source Files\extensions\commands + + Source Files\extensions\commands + + + Source Files\extensions\commands + + + Source Files\extensions\commands + + + Source Files\extensions\commands + + + Source Files\extensions\commands + diff --git a/builds/msvc-140/metaverse.sln b/builds/msvc-140/metaverse.sln index db93ac13e..093f0637e 100644 --- a/builds/msvc-140/metaverse.sln +++ b/builds/msvc-140/metaverse.sln @@ -49,6 +49,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jsoncpp", "jsoncpp\jsoncpp. EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cryptojs", "cryptojs\cryptojs.vcxproj", "{4E2B7608-BB5C-4D0E-AC0D-67910C65A6DA}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sodium", "sodium\sodium.vcxproj", "{B383014A-74FB-4241-A98A-BE33B92E9EA3}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -155,6 +157,12 @@ Global {4E2B7608-BB5C-4D0E-AC0D-67910C65A6DA}.Release|x64.Build.0 = Release|x64 {4E2B7608-BB5C-4D0E-AC0D-67910C65A6DA}.Release|x86.ActiveCfg = Release|Win32 {4E2B7608-BB5C-4D0E-AC0D-67910C65A6DA}.Release|x86.Build.0 = Release|Win32 + {B383014A-74FB-4241-A98A-BE33B92E9EA3}.Debug|x64.ActiveCfg = Debug|x64 + {B383014A-74FB-4241-A98A-BE33B92E9EA3}.Debug|x64.Build.0 = Debug|x64 + {B383014A-74FB-4241-A98A-BE33B92E9EA3}.Debug|x86.ActiveCfg = Debug|x64 + {B383014A-74FB-4241-A98A-BE33B92E9EA3}.Release|x64.ActiveCfg = Release|x64 + {B383014A-74FB-4241-A98A-BE33B92E9EA3}.Release|x64.Build.0 = Release|x64 + {B383014A-74FB-4241-A98A-BE33B92E9EA3}.Release|x86.ActiveCfg = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/builds/msvc-140/mvs-cli/mvs-cli.vcxproj b/builds/msvc-140/mvs-cli/mvs-cli.vcxproj index ce783a044..833cf66e3 100644 --- a/builds/msvc-140/mvs-cli/mvs-cli.vcxproj +++ b/builds/msvc-140/mvs-cli/mvs-cli.vcxproj @@ -72,7 +72,7 @@ NDEBUG;_MBCS;BCX_STATIC;BC_STATIC;BCB_STATIC;BCC_STATIC;BCK_STATIC;BCD_STATIC;BCT_STATIC;BCN_STATIC;BCP_STATIC;BCS_STATIC;ZMQ_STATIC;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) true MultiThreaded - ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\contrib;..\..\..\..\zeromq-4.2.1\include;..\..\..\..\secp256k1\include; + ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\contrib;..\..\..\..\zeromq-4.2.1\include;..\..\..\..\secp256k1\include 4996 diff --git a/builds/msvc-140/mvsd/mvsd.vcxproj b/builds/msvc-140/mvsd/mvsd.vcxproj index f671ae973..42c5fa367 100644 --- a/builds/msvc-140/mvsd/mvsd.vcxproj +++ b/builds/msvc-140/mvsd/mvsd.vcxproj @@ -78,7 +78,7 @@ true MultiThreaded true - ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\..\secp256k1\include;..\..\..\src\lib\consensus\clone;..\..\..\contrib;..\..\..\..\zeromq-4.2.1\include + ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\..\secp256k1\include;..\..\..\contrib;..\..\..\..\zeromq-4.2.1\include 4996 @@ -190,6 +190,9 @@ {06beb260-0a84-40d3-a0a1-16487e51a4bf} + + {b383014a-74fb-4241-a98a-be33b92e9ea3} + diff --git a/builds/msvc-140/network/network.vcxproj b/builds/msvc-140/network/network.vcxproj index 81cce9b55..c8a050cbd 100644 --- a/builds/msvc-140/network/network.vcxproj +++ b/builds/msvc-140/network/network.vcxproj @@ -136,7 +136,7 @@ USE_UPNP;MINIUPNP_STATICLIB;NDEBUG;_LIB;BC_STATIC;BCT_STATIC;BOOST_ASIO_ENABLE_CANCELIO;_SCL_SECURE_NO_WARNINGS true MultiThreaded - ..\..\..\..\miniupnp;..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\src\lib\consensus\clone + ..\..\..\..\miniupnp;..\..\..\include;..\..\..\..\boost_1_63_0 Windows diff --git a/builds/msvc-140/protocol/protocol.vcxproj b/builds/msvc-140/protocol/protocol.vcxproj index 177bf0189..dabea0c8d 100644 --- a/builds/msvc-140/protocol/protocol.vcxproj +++ b/builds/msvc-140/protocol/protocol.vcxproj @@ -109,7 +109,7 @@ NDEBUG;_LIB;BC_STATIC;BCP_STATIC;ZMQ_STATIC true MultiThreaded - ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\..\zeromq-4.2.1\include; + ..\..\..\include;..\..\..\..\boost_1_63_0;..\..\..\..\zeromq-4.2.1\include Windows diff --git a/builds/msvc-140/sodium/sodium.vcxproj b/builds/msvc-140/sodium/sodium.vcxproj new file mode 100644 index 000000000..f27c1e854 --- /dev/null +++ b/builds/msvc-140/sodium/sodium.vcxproj @@ -0,0 +1,111 @@ + + + + + Debug + x64 + + + Release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {B383014A-74FB-4241-A98A-BE33B92E9EA3} + Win32Proj + mongoose + 8.1 + + + + StaticLibrary + true + v140 + Unicode + + + StaticLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + libsodium-vc140-mt-gd + + + + NotUsing + Level3 + Disabled + _DEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreadedDebug + + + + + Windows + + + + + Level3 + NotUsing + MaxSpeed + true + true + NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + MultiThreaded + + + + + Windows + true + true + + + + + + \ No newline at end of file diff --git a/builds/msvc-140/sodium/sodium.vcxproj.filters b/builds/msvc-140/sodium/sodium.vcxproj.filters new file mode 100644 index 000000000..e75be32f9 --- /dev/null +++ b/builds/msvc-140/sodium/sodium.vcxproj.filters @@ -0,0 +1,86 @@ + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {4ca91b93-204b-4f16-863d-e2562255017b} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\fe_25_5 + + + Header Files\fe_25_5 + + + Header Files\fe_25_5 + + + Header Files\fe_25_5 + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/builds/msvc/vs2015/metaverse.vcxproj b/builds/msvc/vs2015/metaverse.vcxproj index 825ce416e..4694157b0 100644 --- a/builds/msvc/vs2015/metaverse.vcxproj +++ b/builds/msvc/vs2015/metaverse.vcxproj @@ -290,6 +290,7 @@ + @@ -303,6 +304,7 @@ + @@ -805,7 +807,9 @@ + + diff --git a/builds/msvc/vs2015/metaverse.vcxproj.filters b/builds/msvc/vs2015/metaverse.vcxproj.filters index a37589371..37cd5c775 100644 --- a/builds/msvc/vs2015/metaverse.vcxproj.filters +++ b/builds/msvc/vs2015/metaverse.vcxproj.filters @@ -519,6 +519,9 @@ Source Files + + Source Files + Source Files @@ -558,6 +561,9 @@ Source Files + + Source Files + Source Files @@ -2075,9 +2081,15 @@ Header Files + + Header Files + Header Files + + Header Files + Header Files diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index e87b901fe..7a1884d1b 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -1,3 +1,4 @@ ADD_SUBDIRECTORY(mongoose) ADD_SUBDIRECTORY(jsoncpp) -ADD_SUBDIRECTORY(cryptojs) \ No newline at end of file +ADD_SUBDIRECTORY(cryptojs) +ADD_SUBDIRECTORY(sodium) \ No newline at end of file diff --git a/contrib/sodium/CMakeLists.txt b/contrib/sodium/CMakeLists.txt new file mode 100644 index 000000000..29a065e3c --- /dev/null +++ b/contrib/sodium/CMakeLists.txt @@ -0,0 +1,15 @@ +FILE(GLOB_RECURSE sodium_SOURCES "*.cpp") + +INCLUDE_DIRECTORIES("${PROJECT_SOURCE_DIR}/include") + +ADD_LIBRARY(sodium_static STATIC ${sodium_SOURCES}) +SET_TARGET_PROPERTIES(sodium_static PROPERTIES OUTPUT_NAME sodium) +TARGET_LINK_LIBRARIES(sodium_static) +INSTALL(TARGETS sodium_static DESTINATION lib) + +IF(ENABLE_SHARED_LIBS) + ADD_LIBRARY(sodium_shared SHARED ${sodium_SOURCES}) + SET_TARGET_PROPERTIES(sodium_shared PROPERTIES OUTPUT_NAME sodium) + TARGET_LINK_LIBRARIES(sodium_shared) + INSTALL(TARGETS sodium_shared DESTINATION lib) +ENDIF() diff --git a/contrib/sodium/common.h b/contrib/sodium/common.h new file mode 100644 index 000000000..dd32b049b --- /dev/null +++ b/contrib/sodium/common.h @@ -0,0 +1,275 @@ +/* +Copyright (c) 2018 - 2019 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + + + +#ifndef common_H +#define common_H 1 + +#include +#include +#include + +namespace sodium { + +#define COMPILER_ASSERT(X) (void) sizeof(char[(X) ? 1 : -1]) + + +#ifdef HAVE_TI_MODE +# if defined(__SIZEOF_INT128__) +typedef unsigned __int128 uint128_t; +# else +typedef unsigned uint128_t __attribute__((mode(TI))); +# endif +#endif + +#define ROTL32(X, B) rotl32((X), (B)) +static inline uint32_t +rotl32(const uint32_t x, const int b) +{ + return (x << b) | (x >> (32 - b)); +} + +#define ROTL64(X, B) rotl64((X), (B)) +static inline uint64_t +rotl64(const uint64_t x, const int b) +{ + return (x << b) | (x >> (64 - b)); +} + +#define ROTR32(X, B) rotr32((X), (B)) +static inline uint32_t +rotr32(const uint32_t x, const int b) +{ + return (x >> b) | (x << (32 - b)); +} + +#define ROTR64(X, B) rotr64((X), (B)) +static inline uint64_t +rotr64(const uint64_t x, const int b) +{ + return (x >> b) | (x << (64 - b)); +} + +#define LOAD64_LE(SRC) load64_le(SRC) +static inline uint64_t +load64_le(const uint8_t src[8]) +{ +#ifdef NATIVE_LITTLE_ENDIAN + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + uint64_t w = (uint64_t) src[0]; + w |= (uint64_t) src[1] << 8; + w |= (uint64_t) src[2] << 16; + w |= (uint64_t) src[3] << 24; + w |= (uint64_t) src[4] << 32; + w |= (uint64_t) src[5] << 40; + w |= (uint64_t) src[6] << 48; + w |= (uint64_t) src[7] << 56; + return w; +#endif +} + +#define STORE64_LE(DST, W) store64_le((DST), (W)) +static inline void +store64_le(uint8_t dst[8], uint64_t w) +{ +#ifdef NATIVE_LITTLE_ENDIAN + memcpy(dst, &w, sizeof w); +#else + dst[0] = (uint8_t) w; w >>= 8; + dst[1] = (uint8_t) w; w >>= 8; + dst[2] = (uint8_t) w; w >>= 8; + dst[3] = (uint8_t) w; w >>= 8; + dst[4] = (uint8_t) w; w >>= 8; + dst[5] = (uint8_t) w; w >>= 8; + dst[6] = (uint8_t) w; w >>= 8; + dst[7] = (uint8_t) w; +#endif +} + +#define LOAD32_LE(SRC) load32_le(SRC) +static inline uint32_t +load32_le(const uint8_t src[4]) +{ +#ifdef NATIVE_LITTLE_ENDIAN + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + uint32_t w = (uint32_t) src[0]; + w |= (uint32_t) src[1] << 8; + w |= (uint32_t) src[2] << 16; + w |= (uint32_t) src[3] << 24; + return w; +#endif +} + +#define STORE32_LE(DST, W) store32_le((DST), (W)) +static inline void +store32_le(uint8_t dst[4], uint32_t w) +{ +#ifdef NATIVE_LITTLE_ENDIAN + memcpy(dst, &w, sizeof w); +#else + dst[0] = (uint8_t) w; w >>= 8; + dst[1] = (uint8_t) w; w >>= 8; + dst[2] = (uint8_t) w; w >>= 8; + dst[3] = (uint8_t) w; +#endif +} + +/* ----- */ + +#define LOAD64_BE(SRC) load64_be(SRC) +static inline uint64_t +load64_be(const uint8_t src[8]) +{ +#ifdef NATIVE_BIG_ENDIAN + uint64_t w; + memcpy(&w, src, sizeof w); + return w; +#else + uint64_t w = (uint64_t) src[7]; + w |= (uint64_t) src[6] << 8; + w |= (uint64_t) src[5] << 16; + w |= (uint64_t) src[4] << 24; + w |= (uint64_t) src[3] << 32; + w |= (uint64_t) src[2] << 40; + w |= (uint64_t) src[1] << 48; + w |= (uint64_t) src[0] << 56; + return w; +#endif +} + +#define STORE64_BE(DST, W) store64_be((DST), (W)) +static inline void +store64_be(uint8_t dst[8], uint64_t w) +{ +#ifdef NATIVE_BIG_ENDIAN + memcpy(dst, &w, sizeof w); +#else + dst[7] = (uint8_t) w; w >>= 8; + dst[6] = (uint8_t) w; w >>= 8; + dst[5] = (uint8_t) w; w >>= 8; + dst[4] = (uint8_t) w; w >>= 8; + dst[3] = (uint8_t) w; w >>= 8; + dst[2] = (uint8_t) w; w >>= 8; + dst[1] = (uint8_t) w; w >>= 8; + dst[0] = (uint8_t) w; +#endif +} + +#define LOAD32_BE(SRC) load32_be(SRC) +static inline uint32_t +load32_be(const uint8_t src[4]) +{ +#ifdef NATIVE_BIG_ENDIAN + uint32_t w; + memcpy(&w, src, sizeof w); + return w; +#else + uint32_t w = (uint32_t) src[3]; + w |= (uint32_t) src[2] << 8; + w |= (uint32_t) src[1] << 16; + w |= (uint32_t) src[0] << 24; + return w; +#endif +} + +#define STORE32_BE(DST, W) store32_be((DST), (W)) +static inline void +store32_be(uint8_t dst[4], uint32_t w) +{ +#ifdef NATIVE_BIG_ENDIAN + memcpy(dst, &w, sizeof w); +#else + dst[3] = (uint8_t) w; w >>= 8; + dst[2] = (uint8_t) w; w >>= 8; + dst[1] = (uint8_t) w; w >>= 8; + dst[0] = (uint8_t) w; +#endif +} + +#define XOR_BUF(OUT, IN, N) xor_buf((OUT), (IN), (N)) +static inline void +xor_buf(unsigned char *out, const unsigned char *in, size_t n) +{ + size_t i; + + for (i = 0; i < n; i++) { + out[i] ^= in[i]; + } +} + +#if !defined(__clang__) && !defined(__GNUC__) +# ifdef __attribute__ +# undef __attribute__ +# endif +# define __attribute__(a) +#endif + +#ifndef CRYPTO_ALIGN +# if defined(__INTEL_COMPILER) || defined(_MSC_VER) +# define CRYPTO_ALIGN(x) __declspec(align(x)) +# else +# define CRYPTO_ALIGN(x) __attribute__ ((aligned(x))) +# endif +#endif + +#if defined(_MSC_VER) && \ + (defined(_M_X64) || defined(_M_AMD64) || defined(_M_IX86)) + +# include + +# define HAVE_INTRIN_H 1 +# define HAVE_MMINTRIN_H 1 +# define HAVE_EMMINTRIN_H 1 +# define HAVE_PMMINTRIN_H 1 +# define HAVE_TMMINTRIN_H 1 +# define HAVE_SMMINTRIN_H 1 +# define HAVE_AVXINTRIN_H 1 +# if _MSC_VER >= 1600 +# define HAVE_WMMINTRIN_H 1 +# endif +# if _MSC_VER >= 1700 && defined(_M_X64) +# define HAVE_AVX2INTRIN_H 1 +# endif +#elif defined(HAVE_INTRIN_H) +# include +#endif + +#ifdef HAVE_LIBCTGRIND +extern void ct_poison (const void *, size_t); +extern void ct_unpoison(const void *, size_t); +# define POISON(X, L) ct_poison((X), (L)) +# define UNPOISON(X, L) ct_unpoison((X), (L)) +#else +# define POISON(X, L) (void) 0 +# define UNPOISON(X, L) (void) 0 +#endif +} + +#endif diff --git a/contrib/sodium/convert.cpp b/contrib/sodium/convert.cpp new file mode 100644 index 000000000..71dc3f19a --- /dev/null +++ b/contrib/sodium/convert.cpp @@ -0,0 +1,129 @@ +/* +Copyright (c) 2018 - 2019 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#include +#include "convert.h" +#include "vrf.h" +#include "sha512EL.h" +#include "ed25519_ref10.h" + +namespace sodium { + +static const unsigned char ONE = 0x01; +static const unsigned char TWO = 0x02; + +/* Encode elliptic curve point into a 32-byte octet string per RFC8032 section + * 5.1.2. + */ + +void +_vrf_ietfdraft03_point_to_string(unsigned char string[32], const ge25519_p3 *point) +{ + ge25519_p3_tobytes(string, point); +} + +/* Decode elliptic curve point from 32-byte octet string per RFC8032 section + * 5.1.3. + * + * In particular we must reject non-canonical encodings (i.e., when the encoded + * y coordinate is not reduced mod p). We do not check whether the point is on + * the main subgroup or whether it is of low order. Returns 0 on success (and + * stores decoded point in *point), nonzero if decoding fails. + */ +int +_vrf_ietfdraft03_string_to_point(ge25519_p3 *point, const unsigned char string[32]) +{ + if (ge25519_is_canonical(string) == 0 || + ge25519_frombytes(point, string) != 0) { + return -1; + } + return 0; +} + +/* Hash a message to a curve point using Elligator2. + * Specified in VRF draft spec section 5.4.1.2. + * The actual elligator2 implementation is ge25519_from_uniform. + * Runtime depends only on alphalen (the message length) + */ +void +_vrf_ietfdraft03_hash_to_curve_elligator2_25519(unsigned char H_string[32], + const ge25519_p3 *Y_point, + const unsigned char *alpha, + const unsigned long long alphalen) +{ + crypto_hash_sha512_state hs; + unsigned char Y_string[32], r_string[64]; + + _vrf_ietfdraft03_point_to_string(Y_string, Y_point); + + /* r = first 32 bytes of SHA512(suite || 0x01 || Y || alpha) */ + crypto_hash_sha512_init(&hs); + crypto_hash_sha512_update(&hs, &SUITE, 1); + crypto_hash_sha512_update(&hs, &ONE, 1); + crypto_hash_sha512_update(&hs, Y_string, 32); + crypto_hash_sha512_update(&hs, alpha, alphalen); + crypto_hash_sha512_final(&hs, r_string); + + r_string[31] &= 0x7f; /* clear sign bit */ + ge25519_from_uniform(H_string, r_string); /* elligator2 */ +} + +/* Subroutine specified in draft spec section 5.4.3. + * Hashes four points to a 16-byte string. + * Constant time. */ +void +_vrf_ietfdraft03_hash_points(unsigned char c[16], const ge25519_p3 *P1, + const ge25519_p3 *P2, const ge25519_p3 *P3, + const ge25519_p3 *P4) +{ + unsigned char str[2+32*4], c1[64]; + + str[0] = SUITE; + str[1] = TWO; + _vrf_ietfdraft03_point_to_string(str+2+32*0, P1); + _vrf_ietfdraft03_point_to_string(str+2+32*1, P2); + _vrf_ietfdraft03_point_to_string(str+2+32*2, P3); + _vrf_ietfdraft03_point_to_string(str+2+32*3, P4); + crypto_hash_sha512(c1, str, sizeof str); + memmove(c, c1, 16); + memset(c1, 0, 64); +} + +/* Decode an 80-byte proof pi into a point gamma, a 16-byte scalar c, and a + * 32-byte scalar s, as specified in IETF draft section 5.4.4. + * Returns 0 on success, nonzero on failure. + */ +int +_vrf_ietfdraft03_decode_proof(ge25519_p3 *Gamma, unsigned char c[16], + unsigned char s[32], const unsigned char pi[80]) +{ + /* gamma = decode_point(pi[0:32]) */ + if (_vrf_ietfdraft03_string_to_point(Gamma, pi) != 0) { + return -1; + } + memmove(c, pi+32, 16); /* c = pi[32:48] */ + memmove(s, pi+48, 32); /* s = pi[48:80] */ + return 0; +} + +} \ No newline at end of file diff --git a/contrib/sodium/convert.h b/contrib/sodium/convert.h new file mode 100644 index 000000000..5e6fac23d --- /dev/null +++ b/contrib/sodium/convert.h @@ -0,0 +1,53 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ +#include "ed25519_ref10.h" + +#ifndef CONVERT_H +#define CONVERT_H + +namespace sodium { + +static const unsigned char SUITE = 0x04; /* ECVRF-ED25519-SHA512-Elligator2 */ + +void _vrf_ietfdraft03_point_to_string(unsigned char string[32], + const ge25519_p3 *point); + +int _vrf_ietfdraft03_string_to_point(ge25519_p3 *point, + const unsigned char string[32]); + +int _vrf_ietfdraft03_decode_proof(ge25519_p3 *Gamma, unsigned char c[16], + unsigned char s[32], + const unsigned char pi[80]); + +void _vrf_ietfdraft03_hash_to_curve_elligator2_25519(unsigned char H_string[32], + const ge25519_p3 *Y_point, + const unsigned char *alpha, + const unsigned long long alphalen); + +void _vrf_ietfdraft03_hash_points(unsigned char c[16], const ge25519_p3 *P1, + const ge25519_p3 *P2, const ge25519_p3 *P3, + const ge25519_p3 *P4); +} + + +#endif \ No newline at end of file diff --git a/contrib/sodium/crypto_verify.cpp b/contrib/sodium/crypto_verify.cpp new file mode 100644 index 000000000..dd994b88a --- /dev/null +++ b/contrib/sodium/crypto_verify.cpp @@ -0,0 +1,59 @@ +/* +Copyright (c) 2018 - 2019 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ +#include +#include + +#include "crypto_verify_16.h" + +namespace sodium { + +size_t crypto_verify_16_bytes(void){ + + return crypto_verify_16_BYTES; +} + + +static inline int +crypto_verify_n(const unsigned char *x_, const unsigned char *y_, + const int n) +{ + const volatile unsigned char *volatile x = + (const volatile unsigned char *volatile) x_; + const volatile unsigned char *volatile y = + (const volatile unsigned char *volatile) y_; + volatile uint_fast16_t d = 0U; + int i; + + for (i = 0; i < n; i++) { + d |= x[i] ^ y[i]; + } + return (1 & ((d - 1) >> 8)) - 1; +} + +int crypto_verify_16(const unsigned char *x, const unsigned char *y) +{ + return crypto_verify_n(x, y, crypto_verify_16_BYTES); +} + + +} \ No newline at end of file diff --git a/contrib/sodium/crypto_verify_16.h b/contrib/sodium/crypto_verify_16.h new file mode 100644 index 000000000..8ad8a2fb4 --- /dev/null +++ b/contrib/sodium/crypto_verify_16.h @@ -0,0 +1,39 @@ +/* +Copyright (c) 2018 - 2019 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ +#ifndef crypto_verify_16_H +#define crypto_verify_16_H + +#include +#include "common.h" + +namespace sodium { + +#define crypto_verify_16_BYTES 16U +size_t crypto_verify_16_bytes(void); + +int crypto_verify_16(const unsigned char *x, const unsigned char *y) + __attribute__ ((warn_unused_result)); + +} + +#endif diff --git a/contrib/sodium/crypto_vrf.cpp b/contrib/sodium/crypto_vrf.cpp new file mode 100644 index 000000000..22267a8a2 --- /dev/null +++ b/contrib/sodium/crypto_vrf.cpp @@ -0,0 +1,115 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#include +#include "vrf.h" +#include "crypto_vrf.h" + +namespace sodium { + +size_t +crypto_vrf_publickeybytes(void) +{ + return crypto_vrf_PUBLICKEYBYTES; +} + +size_t +crypto_vrf_secretkeybytes(void) +{ + return crypto_vrf_SECRETKEYBYTES; +} + +size_t +crypto_vrf_seedbytes(void) +{ + return crypto_vrf_SEEDBYTES; +} + +size_t +crypto_vrf_proofbytes(void) +{ + return crypto_vrf_PROOFBYTES; +} + +size_t +crypto_vrf_outputbytes(void) +{ + return crypto_vrf_OUTPUTBYTES; +} + +const char * +crypto_vrf_primitive(void) +{ + return crypto_vrf_PRIMITIVE; +} + +int +crypto_vrf_keypair(unsigned char *pk, unsigned char *sk) +{ + return crypto_vrf_ietfdraft03_keypair(pk, sk); +} + +int crypto_vrf_keypair_from_seed(unsigned char *pk, unsigned char *sk, const unsigned char *seed){ + + return crypto_vrf_ietfdraft03_keypair_from_seed(pk, sk, seed); +} + +int +crypto_vrf_is_valid_key(const unsigned char *pk) +{ + return crypto_vrf_ietfdraft03_is_valid_key(pk); +} + +int +crypto_vrf_prove(unsigned char *proof, const unsigned char *skpk, + const unsigned char *m, const unsigned long long mlen) +{ + return crypto_vrf_ietfdraft03_prove(proof, skpk, m, mlen); +} + +int +crypto_vrf_verify(unsigned char *output, const unsigned char *pk, + const unsigned char *proof, const unsigned char *m, + const unsigned long long mlen) +{ + return crypto_vrf_ietfdraft03_verify(output, pk, proof, m, mlen); +} + +int +crypto_vrf_proof_to_hash(unsigned char *hash, const unsigned char *proof) +{ + return crypto_vrf_ietfdraft03_proof_to_hash(hash, proof); +} + +void +crypto_vrf_sk_to_pk(unsigned char *pk, const unsigned char *skpk) +{ + crypto_vrf_ietfdraft03_sk_to_pk(pk, skpk); +} + +void +crypto_vrf_sk_to_seed(unsigned char *seed, const unsigned char *skpk) +{ + crypto_vrf_ietfdraft03_sk_to_seed(seed, skpk); +} +} \ No newline at end of file diff --git a/contrib/sodium/crypto_vrf.h b/contrib/sodium/crypto_vrf.h new file mode 100644 index 000000000..9df0cc365 --- /dev/null +++ b/contrib/sodium/crypto_vrf.h @@ -0,0 +1,92 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#ifndef crypto_vrf_H +#define crypto_vrf_H + +/* + * THREAD SAfe25519TY: crypto_vrf_keypair() is thread-safe25519 provided that + * sodium_init() was called before. + * + * Other functions, including crypto_vrf_keypair_from_seed(), are always + * thread-safe25519. + */ + +#include + +#include "vrf.h" + +#ifdef __cplusplus +extern "C" { +#endif + +namespace sodium { + +#define crypto_vrf_PUBLICKEYBYTES crypto_vrf_ietfdraft03_PUBLICKEYBYTES +size_t crypto_vrf_publickeybytes(void); + +#define crypto_vrf_SECRETKEYBYTES crypto_vrf_ietfdraft03_SECRETKEYBYTES +size_t crypto_vrf_secretkeybytes(void); + +#define crypto_vrf_SEEDBYTES crypto_vrf_ietfdraft03_SEEDBYTES +size_t crypto_vrf_seedbytes(void); + +#define crypto_vrf_PROOFBYTES crypto_vrf_ietfdraft03_PROOFBYTES +size_t crypto_vrf_proofbytes(void); + +#define crypto_vrf_OUTPUTBYTES crypto_vrf_ietfdraft03_OUTPUTBYTES +size_t crypto_vrf_outputbytes(void); + +#define crypto_vrf_PRIMITIVE "ietfdraft03" +const char *crypto_vrf_primitive(void); + +int crypto_vrf_keypair(unsigned char *pk, unsigned char *sk); + +int crypto_vrf_keypair_from_seed(unsigned char *pk, unsigned char *sk, + const unsigned char *seed); + +int crypto_vrf_is_valid_key(const unsigned char *pk) + __attribute__ ((warn_unused_result)); + +int crypto_vrf_prove(unsigned char *proof, const unsigned char *sk, + const unsigned char *m, unsigned long long mlen); + +int crypto_vrf_verify(unsigned char *output, + const unsigned char *pk, + const unsigned char *proof, + const unsigned char *m, unsigned long long mlen) + __attribute__ ((warn_unused_result)); + +int crypto_vrf_proof_to_hash(unsigned char *hash, const unsigned char *proof); + +void crypto_vrf_sk_to_pk(unsigned char *pk, const unsigned char *skpk); + +void crypto_vrf_sk_to_seed(unsigned char *seed, const unsigned char *skpk); + +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/contrib/sodium/ed25519_ref10.cpp b/contrib/sodium/ed25519_ref10.cpp new file mode 100644 index 000000000..0ab248b3a --- /dev/null +++ b/contrib/sodium/ed25519_ref10.cpp @@ -0,0 +1,2057 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#include +#include +#include +#include + +#include "crypto_verify_16.h" +#include "common.h" +#include "ed25519_ref10.h" +# include "ed25519_ref10_fe_25_5.h" + +namespace sodium { + +static inline uint64_t +load_3(const unsigned char *in) +{ + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + + return result; +} + +static inline uint64_t +load_4(const unsigned char *in) +{ + uint64_t result; + + result = (uint64_t) in[0]; + result |= ((uint64_t) in[1]) << 8; + result |= ((uint64_t) in[2]) << 16; + result |= ((uint64_t) in[3]) << 24; + + return result; +} + +/* + * Field arithmetic: + * Use 5*51 bit limbs on 64-bit systems with support for 128 bit arithmetic, + * and 10*25.5 bit limbs elsewhere. + * + * Functions used elsewhere that are candidates for inlining are defined + * via "private/curve25519_ref10.h". + */ + +#ifdef HAVE_TI_MODE +# include "fe_51/constants.h" +# include "fe_51/fe.h" +#else +# include "fe_25_5/constants.h" +# include "fe_25_5/fe.h" +#endif + +void +fe25519_invert(fe25519 out, const fe25519 z) +{ + fe25519 t0; + fe25519 t1; + fe25519 t2; + fe25519 t3; + int i; + + fe25519_sq(t0, z); + fe25519_sq(t1, t0); + fe25519_sq(t1, t1); + fe25519_mul(t1, z, t1); + fe25519_mul(t0, t0, t1); + fe25519_sq(t2, t0); + fe25519_mul(t1, t1, t2); + fe25519_sq(t2, t1); + for (i = 1; i < 5; ++i) { + fe25519_sq(t2, t2); + } + fe25519_mul(t1, t2, t1); + fe25519_sq(t2, t1); + for (i = 1; i < 10; ++i) { + fe25519_sq(t2, t2); + } + fe25519_mul(t2, t2, t1); + fe25519_sq(t3, t2); + for (i = 1; i < 20; ++i) { + fe25519_sq(t3, t3); + } + fe25519_mul(t2, t3, t2); + fe25519_sq(t2, t2); + for (i = 1; i < 10; ++i) { + fe25519_sq(t2, t2); + } + fe25519_mul(t1, t2, t1); + fe25519_sq(t2, t1); + for (i = 1; i < 50; ++i) { + fe25519_sq(t2, t2); + } + fe25519_mul(t2, t2, t1); + fe25519_sq(t3, t2); + for (i = 1; i < 100; ++i) { + fe25519_sq(t3, t3); + } + fe25519_mul(t2, t3, t2); + fe25519_sq(t2, t2); + for (i = 1; i < 50; ++i) { + fe25519_sq(t2, t2); + } + fe25519_mul(t1, t2, t1); + fe25519_sq(t1, t1); + for (i = 1; i < 5; ++i) { + fe25519_sq(t1, t1); + } + fe25519_mul(out, t1, t0); +} + +static void +fe25519_pow22523(fe25519 out, const fe25519 z) +{ + fe25519 t0; + fe25519 t1; + fe25519 t2; + int i; + + fe25519_sq(t0, z); + fe25519_sq(t1, t0); + fe25519_sq(t1, t1); + fe25519_mul(t1, z, t1); + fe25519_mul(t0, t0, t1); + fe25519_sq(t0, t0); + fe25519_mul(t0, t1, t0); + fe25519_sq(t1, t0); + for (i = 1; i < 5; ++i) { + fe25519_sq(t1, t1); + } + fe25519_mul(t0, t1, t0); + fe25519_sq(t1, t0); + for (i = 1; i < 10; ++i) { + fe25519_sq(t1, t1); + } + fe25519_mul(t1, t1, t0); + fe25519_sq(t2, t1); + for (i = 1; i < 20; ++i) { + fe25519_sq(t2, t2); + } + fe25519_mul(t1, t2, t1); + fe25519_sq(t1, t1); + for (i = 1; i < 10; ++i) { + fe25519_sq(t1, t1); + } + fe25519_mul(t0, t1, t0); + fe25519_sq(t1, t0); + for (i = 1; i < 50; ++i) { + fe25519_sq(t1, t1); + } + fe25519_mul(t1, t1, t0); + fe25519_sq(t2, t1); + for (i = 1; i < 100; ++i) { + fe25519_sq(t2, t2); + } + fe25519_mul(t1, t2, t1); + fe25519_sq(t1, t1); + for (i = 1; i < 50; ++i) { + fe25519_sq(t1, t1); + } + fe25519_mul(t0, t1, t0); + fe25519_sq(t0, t0); + fe25519_sq(t0, t0); + fe25519_mul(out, t0, z); +} + +/* + r = p + q + */ + +void +ge25519_add(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_cached *q) +{ + fe25519 t0; + + fe25519_add(r->X, p->Y, p->X); + fe25519_sub(r->Y, p->Y, p->X); + fe25519_mul(r->Z, r->X, q->YplusX); + fe25519_mul(r->Y, r->Y, q->YminusX); + fe25519_mul(r->T, q->T2d, p->T); + fe25519_mul(r->X, p->Z, q->Z); + fe25519_add(t0, r->X, r->X); + fe25519_sub(r->X, r->Z, r->Y); + fe25519_add(r->Y, r->Z, r->Y); + fe25519_add(r->Z, t0, r->T); + fe25519_sub(r->T, t0, r->T); +} + +static void +slide_vartime(signed char *r, const unsigned char *a) +{ + int i; + int b; + int k; + int ribs; + int cmp; + + for (i = 0; i < 256; ++i) { + r[i] = 1 & (a[i >> 3] >> (i & 7)); + } + for (i = 0; i < 256; ++i) { + if (! r[i]) { + continue; + } + for (b = 1; b <= 6 && i + b < 256; ++b) { + if (! r[i + b]) { + continue; + } + ribs = r[i + b] << b; + cmp = r[i] + ribs; + if (cmp <= 15) { + r[i] = cmp; + r[i + b] = 0; + } else { + cmp = r[i] - ribs; + if (cmp < -15) { + break; + } + r[i] = cmp; + for (k = i + b; k < 256; ++k) { + if (! r[k]) { + r[k] = 1; + break; + } + r[k] = 0; + } + } + } + } +} + +int +ge25519_frombytes(ge25519_p3 *h, const unsigned char *s) +{ + fe25519 u; + fe25519 v; + fe25519 v3; + fe25519 vxx; + fe25519 m_root_check, p_root_check; + fe25519 negx; + fe25519 x_sqrtm1; + int has_m_root, has_p_root; + + fe25519_frombytes(h->Y, s); + fe25519_1(h->Z); + fe25519_sq(u, h->Y); + fe25519_mul(v, u, d); + fe25519_sub(u, u, h->Z); /* u = y^2-1 */ + fe25519_add(v, v, h->Z); /* v = dy^2+1 */ + + fe25519_sq(v3, v); + fe25519_mul(v3, v3, v); /* v3 = v^3 */ + fe25519_sq(h->X, v3); + fe25519_mul(h->X, h->X, v); + fe25519_mul(h->X, h->X, u); /* x = uv^7 */ + + fe25519_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ + fe25519_mul(h->X, h->X, v3); + fe25519_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ + + fe25519_sq(vxx, h->X); + fe25519_mul(vxx, vxx, v); + fe25519_sub(m_root_check, vxx, u); /* vx^2-u */ + fe25519_add(p_root_check, vxx, u); /* vx^2+u */ + has_m_root = fe25519_iszero(m_root_check); + has_p_root = fe25519_iszero(p_root_check); + fe25519_mul(x_sqrtm1, h->X, sqrtm1); /* x*sqrt(-1) */ + fe25519_cmov(h->X, x_sqrtm1, 1 - has_m_root); + + fe25519_neg(negx, h->X); + fe25519_cmov(h->X, negx, fe25519_isnegative(h->X) ^ (s[31] >> 7)); + fe25519_mul(h->T, h->X, h->Y); + + return (has_m_root | has_p_root) - 1; +} + +int +ge25519_frombytes_negate_vartime(ge25519_p3 *h, const unsigned char *s) +{ + fe25519 u; + fe25519 v; + fe25519 v3; + fe25519 vxx; + fe25519 m_root_check, p_root_check; + + fe25519_frombytes(h->Y, s); + fe25519_1(h->Z); + fe25519_sq(u, h->Y); + fe25519_mul(v, u, d); + fe25519_sub(u, u, h->Z); /* u = y^2-1 */ + fe25519_add(v, v, h->Z); /* v = dy^2+1 */ + + fe25519_sq(v3, v); + fe25519_mul(v3, v3, v); /* v3 = v^3 */ + fe25519_sq(h->X, v3); + fe25519_mul(h->X, h->X, v); + fe25519_mul(h->X, h->X, u); /* x = uv^7 */ + + fe25519_pow22523(h->X, h->X); /* x = (uv^7)^((q-5)/8) */ + fe25519_mul(h->X, h->X, v3); + fe25519_mul(h->X, h->X, u); /* x = uv^3(uv^7)^((q-5)/8) */ + + fe25519_sq(vxx, h->X); + fe25519_mul(vxx, vxx, v); + fe25519_sub(m_root_check, vxx, u); /* vx^2-u */ + if (fe25519_iszero(m_root_check) == 0) { + fe25519_add(p_root_check, vxx, u); /* vx^2+u */ + if (fe25519_iszero(p_root_check) == 0) { + return -1; + } + fe25519_mul(h->X, h->X, sqrtm1); + } + + if (fe25519_isnegative(h->X) == (s[31] >> 7)) { + fe25519_neg(h->X, h->X); + } + fe25519_mul(h->T, h->X, h->Y); + + return 0; +} + +/* + r = p + q + */ + +static void +ge25519_madd(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_precomp *q) +{ + fe25519 t0; + + fe25519_add(r->X, p->Y, p->X); + fe25519_sub(r->Y, p->Y, p->X); + fe25519_mul(r->Z, r->X, q->yplusx); + fe25519_mul(r->Y, r->Y, q->yminusx); + fe25519_mul(r->T, q->xy2d, p->T); + fe25519_add(t0, p->Z, p->Z); + fe25519_sub(r->X, r->Z, r->Y); + fe25519_add(r->Y, r->Z, r->Y); + fe25519_add(r->Z, t0, r->T); + fe25519_sub(r->T, t0, r->T); +} + +/* + r = p - q + */ + +static void +ge25519_msub(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_precomp *q) +{ + fe25519 t0; + + fe25519_add(r->X, p->Y, p->X); + fe25519_sub(r->Y, p->Y, p->X); + fe25519_mul(r->Z, r->X, q->yminusx); + fe25519_mul(r->Y, r->Y, q->yplusx); + fe25519_mul(r->T, q->xy2d, p->T); + fe25519_add(t0, p->Z, p->Z); + fe25519_sub(r->X, r->Z, r->Y); + fe25519_add(r->Y, r->Z, r->Y); + fe25519_sub(r->Z, t0, r->T); + fe25519_add(r->T, t0, r->T); +} + +/* + r = p + */ + +void +ge25519_p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p) +{ + fe25519_mul(r->X, p->X, p->T); + fe25519_mul(r->Y, p->Y, p->Z); + fe25519_mul(r->Z, p->Z, p->T); +} + +/* + r = p + */ + +void +ge25519_p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p) +{ + fe25519_mul(r->X, p->X, p->T); + fe25519_mul(r->Y, p->Y, p->Z); + fe25519_mul(r->Z, p->Z, p->T); + fe25519_mul(r->T, p->X, p->Y); +} + +static void +ge25519_p2_0(ge25519_p2 *h) +{ + fe25519_0(h->X); + fe25519_1(h->Y); + fe25519_1(h->Z); +} + +/* + r = 2 * p + */ + +static void +ge25519_p2_dbl(ge25519_p1p1 *r, const ge25519_p2 *p) +{ + fe25519 t0; + + fe25519_sq(r->X, p->X); + fe25519_sq(r->Z, p->Y); + fe25519_sq2(r->T, p->Z); + fe25519_add(r->Y, p->X, p->Y); + fe25519_sq(t0, r->Y); + fe25519_add(r->Y, r->Z, r->X); + fe25519_sub(r->Z, r->Z, r->X); + fe25519_sub(r->X, t0, r->Y); + fe25519_sub(r->T, r->T, r->Z); +} + +static void +ge25519_p3_0(ge25519_p3 *h) +{ + fe25519_0(h->X); + fe25519_1(h->Y); + fe25519_1(h->Z); + fe25519_0(h->T); +} + +static void +ge25519_cached_0(ge25519_cached *h) +{ + fe25519_1(h->YplusX); + fe25519_1(h->YminusX); + fe25519_1(h->Z); + fe25519_0(h->T2d); +} + +/* + r = p + */ + +void +ge25519_p3_to_cached(ge25519_cached *r, const ge25519_p3 *p) +{ + fe25519_add(r->YplusX, p->Y, p->X); + fe25519_sub(r->YminusX, p->Y, p->X); + fe25519_copy(r->Z, p->Z); + fe25519_mul(r->T2d, p->T, d2); +} + +static void +ge25519_p3_to_precomp(ge25519_precomp *pi, const ge25519_p3 *p) +{ + fe25519 recip; + fe25519 x; + fe25519 y; + fe25519 xy; + + fe25519_invert(recip, p->Z); + fe25519_mul(x, p->X, recip); + fe25519_mul(y, p->Y, recip); + fe25519_add(pi->yplusx, y, x); + fe25519_sub(pi->yminusx, y, x); + fe25519_mul(xy, x, y); + fe25519_mul(pi->xy2d, xy, d2); +} + +/* + r = p + */ + +static void +ge25519_p3_to_p2(ge25519_p2 *r, const ge25519_p3 *p) +{ + fe25519_copy(r->X, p->X); + fe25519_copy(r->Y, p->Y); + fe25519_copy(r->Z, p->Z); +} + +void +ge25519_p3_tobytes(unsigned char *s, const ge25519_p3 *h) +{ + fe25519 recip; + fe25519 x; + fe25519 y; + + fe25519_invert(recip, h->Z); + fe25519_mul(x, h->X, recip); + fe25519_mul(y, h->Y, recip); + fe25519_tobytes(s, y); + s[31] ^= fe25519_isnegative(x) << 7; +} + +/* + r = 2 * p + */ + +static void +ge25519_p3_dbl(ge25519_p1p1 *r, const ge25519_p3 *p) +{ + ge25519_p2 q; + ge25519_p3_to_p2(&q, p); + ge25519_p2_dbl(r, &q); +} + +static void +ge25519_precomp_0(ge25519_precomp *h) +{ + fe25519_1(h->yplusx); + fe25519_1(h->yminusx); + fe25519_0(h->xy2d); +} + +static unsigned char +equal(signed char b, signed char c) +{ + unsigned char ub = b; + unsigned char uc = c; + unsigned char x = ub ^ uc; /* 0: yes; 1..255: no */ + uint32_t y = x; /* 0: yes; 1..255: no */ + + y -= 1; /* 4294967295: yes; 0..254: no */ + y >>= 31; /* 1: yes; 0: no */ + + return y; +} + +static unsigned char +negative(signed char b) +{ + /* 18446744073709551361..18446744073709551615: yes; 0..255: no */ + uint64_t x = b; + + x >>= 63; /* 1: yes; 0: no */ + + return x; +} + +static void +ge25519_cmov(ge25519_precomp *t, const ge25519_precomp *u, unsigned char b) +{ + fe25519_cmov(t->yplusx, u->yplusx, b); + fe25519_cmov(t->yminusx, u->yminusx, b); + fe25519_cmov(t->xy2d, u->xy2d, b); +} + +static void +ge25519_cmov_cached(ge25519_cached *t, const ge25519_cached *u, unsigned char b) +{ + fe25519_cmov(t->YplusX, u->YplusX, b); + fe25519_cmov(t->YminusX, u->YminusX, b); + fe25519_cmov(t->Z, u->Z, b); + fe25519_cmov(t->T2d, u->T2d, b); +} + +static void +ge25519_select(ge25519_precomp *t, const ge25519_precomp precomp[8], const signed char b) +{ + ge25519_precomp minust; + const unsigned char bnegative = negative(b); + const unsigned char babs = b - (((-bnegative) & b) * ((signed char) 1 << 1)); + + ge25519_precomp_0(t); + ge25519_cmov(t, &precomp[0], equal(babs, 1)); + ge25519_cmov(t, &precomp[1], equal(babs, 2)); + ge25519_cmov(t, &precomp[2], equal(babs, 3)); + ge25519_cmov(t, &precomp[3], equal(babs, 4)); + ge25519_cmov(t, &precomp[4], equal(babs, 5)); + ge25519_cmov(t, &precomp[5], equal(babs, 6)); + ge25519_cmov(t, &precomp[6], equal(babs, 7)); + ge25519_cmov(t, &precomp[7], equal(babs, 8)); + fe25519_copy(minust.yplusx, t->yminusx); + fe25519_copy(minust.yminusx, t->yplusx); + fe25519_neg(minust.xy2d, t->xy2d); + ge25519_cmov(t, &minust, bnegative); +} + +static void +ge25519_select_base(ge25519_precomp *t, const int pos, const signed char b) +{ + static const ge25519_precomp base[32][8] = { /* base[i][j] = (j+1)*256^i*B */ +#ifdef HAVE_TI_MODE +# include "fe_51/base.h" +#else +# include "fe_25_5/base.h" +#endif + }; + ge25519_select(t, base[pos], b); +} + +static void +ge25519_select_cached(ge25519_cached *t, const ge25519_cached cached[8], const signed char b) +{ + ge25519_cached minust; + const unsigned char bnegative = negative(b); + const unsigned char babs = b - (((-bnegative) & b) * ((signed char) 1 << 1)); + + ge25519_cached_0(t); + ge25519_cmov_cached(t, &cached[0], equal(babs, 1)); + ge25519_cmov_cached(t, &cached[1], equal(babs, 2)); + ge25519_cmov_cached(t, &cached[2], equal(babs, 3)); + ge25519_cmov_cached(t, &cached[3], equal(babs, 4)); + ge25519_cmov_cached(t, &cached[4], equal(babs, 5)); + ge25519_cmov_cached(t, &cached[5], equal(babs, 6)); + ge25519_cmov_cached(t, &cached[6], equal(babs, 7)); + ge25519_cmov_cached(t, &cached[7], equal(babs, 8)); + fe25519_copy(minust.YplusX, t->YminusX); + fe25519_copy(minust.YminusX, t->YplusX); + fe25519_copy(minust.Z, t->Z); + fe25519_neg(minust.T2d, t->T2d); + ge25519_cmov_cached(t, &minust, bnegative); +} + +/* + r = p - q + */ + +void +ge25519_sub(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_cached *q) +{ + fe25519 t0; + + fe25519_add(r->X, p->Y, p->X); + fe25519_sub(r->Y, p->Y, p->X); + fe25519_mul(r->Z, r->X, q->YminusX); + fe25519_mul(r->Y, r->Y, q->YplusX); + fe25519_mul(r->T, q->T2d, p->T); + fe25519_mul(r->X, p->Z, q->Z); + fe25519_add(t0, r->X, r->X); + fe25519_sub(r->X, r->Z, r->Y); + fe25519_add(r->Y, r->Z, r->Y); + fe25519_sub(r->Z, t0, r->T); + fe25519_add(r->T, t0, r->T); +} + +void +ge25519_tobytes(unsigned char *s, const ge25519_p2 *h) +{ + fe25519 recip; + fe25519 x; + fe25519 y; + + fe25519_invert(recip, h->Z); + fe25519_mul(x, h->X, recip); + fe25519_mul(y, h->Y, recip); + fe25519_tobytes(s, y); + s[31] ^= fe25519_isnegative(x) << 7; +} + +/* + r = a * A + b * B + where a = a[0]+256*a[1]+...+256^31 a[31]. + and b = b[0]+256*b[1]+...+256^31 b[31]. + B is the Ed25519 base point (x,4/5) with x positive. + + Only used for signatures verification. + */ + +void +ge25519_double_scalarmult_vartime(ge25519_p2 *r, const unsigned char *a, + const ge25519_p3 *A, const unsigned char *b) +{ + static const ge25519_precomp Bi[8] = { +#ifdef HAVE_TI_MODE +# include "fe_51/base2.h" +#else +# include "fe_25_5/base2.h" +#endif + }; + signed char aslide[256]; + signed char bslide[256]; + ge25519_cached Ai[8]; /* A,3A,5A,7A,9A,11A,13A,15A */ + ge25519_p1p1 t; + ge25519_p3 u; + ge25519_p3 A2; + int i; + + slide_vartime(aslide, a); + slide_vartime(bslide, b); + + ge25519_p3_to_cached(&Ai[0], A); + + ge25519_p3_dbl(&t, A); + ge25519_p1p1_to_p3(&A2, &t); + + ge25519_add(&t, &A2, &Ai[0]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[1], &u); + + ge25519_add(&t, &A2, &Ai[1]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[2], &u); + + ge25519_add(&t, &A2, &Ai[2]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[3], &u); + + ge25519_add(&t, &A2, &Ai[3]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[4], &u); + + ge25519_add(&t, &A2, &Ai[4]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[5], &u); + + ge25519_add(&t, &A2, &Ai[5]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[6], &u); + + ge25519_add(&t, &A2, &Ai[6]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[7], &u); + + ge25519_p2_0(r); + + for (i = 255; i >= 0; --i) { + if (aslide[i] || bslide[i]) { + break; + } + } + + for (; i >= 0; --i) { + ge25519_p2_dbl(&t, r); + + if (aslide[i] > 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_add(&t, &u, &Ai[aslide[i] / 2]); + } else if (aslide[i] < 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + } + + if (bslide[i] > 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_madd(&t, &u, &Bi[bslide[i] / 2]); + } else if (bslide[i] < 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_msub(&t, &u, &Bi[(-bslide[i]) / 2]); + } + + ge25519_p1p1_to_p2(r, &t); + } +} + +/* + h = a * p + where a = a[0]+256*a[1]+...+256^31 a[31] + + Preconditions: + a[31] <= 127 + + p is public + */ + +void +ge25519_scalarmult(ge25519_p3 *h, const unsigned char *a, const ge25519_p3 *p) +{ + signed char e[64]; + signed char carry; + ge25519_p1p1 r; + ge25519_p2 s; + ge25519_p1p1 t2, t3, t4, t5, t6, t7, t8; + ge25519_p3 p2, p3, p4, p5, p6, p7, p8; + ge25519_cached pi[8]; + ge25519_cached t; + int i; + + ge25519_p3_to_cached(&pi[1 - 1], p); /* p */ + + ge25519_p3_dbl(&t2, p); + ge25519_p1p1_to_p3(&p2, &t2); + ge25519_p3_to_cached(&pi[2 - 1], &p2); /* 2p = 2*p */ + + ge25519_add(&t3, p, &pi[2 - 1]); + ge25519_p1p1_to_p3(&p3, &t3); + ge25519_p3_to_cached(&pi[3 - 1], &p3); /* 3p = 2p+p */ + + ge25519_p3_dbl(&t4, &p2); + ge25519_p1p1_to_p3(&p4, &t4); + ge25519_p3_to_cached(&pi[4 - 1], &p4); /* 4p = 2*2p */ + + ge25519_add(&t5, p, &pi[4 - 1]); + ge25519_p1p1_to_p3(&p5, &t5); + ge25519_p3_to_cached(&pi[5 - 1], &p5); /* 5p = 4p+p */ + + ge25519_p3_dbl(&t6, &p3); + ge25519_p1p1_to_p3(&p6, &t6); + ge25519_p3_to_cached(&pi[6 - 1], &p6); /* 6p = 2*3p */ + + ge25519_add(&t7, p, &pi[6 - 1]); + ge25519_p1p1_to_p3(&p7, &t7); + ge25519_p3_to_cached(&pi[7 - 1], &p7); /* 7p = 6p+p */ + + ge25519_p3_dbl(&t8, &p4); + ge25519_p1p1_to_p3(&p8, &t8); + ge25519_p3_to_cached(&pi[8 - 1], &p8); /* 8p = 2*4p */ + + for (i = 0; i < 32; ++i) { + e[2 * i + 0] = (a[i] >> 0) & 15; + e[2 * i + 1] = (a[i] >> 4) & 15; + } + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + + carry = 0; + for (i = 0; i < 63; ++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry * ((signed char) 1 << 4); + } + e[63] += carry; + /* each e[i] is between -8 and 8 */ + + ge25519_p3_0(h); + + for (i = 63; i != 0; i--) { + ge25519_select_cached(&t, pi, e[i]); + ge25519_add(&r, h, &t); + + ge25519_p1p1_to_p2(&s, &r); + ge25519_p2_dbl(&r, &s); + ge25519_p1p1_to_p2(&s, &r); + ge25519_p2_dbl(&r, &s); + ge25519_p1p1_to_p2(&s, &r); + ge25519_p2_dbl(&r, &s); + ge25519_p1p1_to_p2(&s, &r); + ge25519_p2_dbl(&r, &s); + + ge25519_p1p1_to_p3(h, &r); /* *16 */ + } + ge25519_select_cached(&t, pi, e[i]); + ge25519_add(&r, h, &t); + + ge25519_p1p1_to_p3(h, &r); +} + +/* + h = a * B (with precomputation) + where a = a[0]+256*a[1]+...+256^31 a[31] + B is the Ed25519 base point (x,4/5) with x positive + (as bytes: 0x5866666666666666666666666666666666666666666666666666666666666666) + + Preconditions: + a[31] <= 127 + */ + +void +ge25519_scalarmult_base(ge25519_p3 *h, const unsigned char *a) +{ + signed char e[64]; + signed char carry; + ge25519_p1p1 r; + ge25519_p2 s; + ge25519_precomp t; + int i; + + for (i = 0; i < 32; ++i) { + e[2 * i + 0] = (a[i] >> 0) & 15; + e[2 * i + 1] = (a[i] >> 4) & 15; + } + /* each e[i] is between 0 and 15 */ + /* e[63] is between 0 and 7 */ + + carry = 0; + for (i = 0; i < 63; ++i) { + e[i] += carry; + carry = e[i] + 8; + carry >>= 4; + e[i] -= carry * ((signed char) 1 << 4); + } + e[63] += carry; + /* each e[i] is between -8 and 8 */ + + ge25519_p3_0(h); + + for (i = 1; i < 64; i += 2) { + ge25519_select_base(&t, i / 2, e[i]); + ge25519_madd(&r, h, &t); + ge25519_p1p1_to_p3(h, &r); + } + + ge25519_p3_dbl(&r, h); + ge25519_p1p1_to_p2(&s, &r); + ge25519_p2_dbl(&r, &s); + ge25519_p1p1_to_p2(&s, &r); + ge25519_p2_dbl(&r, &s); + ge25519_p1p1_to_p2(&s, &r); + ge25519_p2_dbl(&r, &s); + ge25519_p1p1_to_p3(h, &r); + + for (i = 0; i < 64; i += 2) { + ge25519_select_base(&t, i / 2, e[i]); + ge25519_madd(&r, h, &t); + ge25519_p1p1_to_p3(h, &r); + } +} + +/* multiply by the order of the main subgroup l = 2^252+27742317777372353535851937790883648493 */ +static void +ge25519_mul_l(ge25519_p3 *r, const ge25519_p3 *A) +{ + static const signed char aslide[253] = { + 13, 0, 0, 0, 0, -1, 0, 0, 0, 0, -11, 0, 0, 0, 0, 0, 0, -5, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, -13, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, -13, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, -13, 0, 0, 0, 0, 0, 0, -3, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 3, 0, 0, 0, 0, -11, 0, 0, 0, 0, 0, 0, 0, 15, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 + }; + ge25519_cached Ai[8]; + ge25519_p1p1 t; + ge25519_p3 u; + ge25519_p3 A2; + int i; + + ge25519_p3_to_cached(&Ai[0], A); + ge25519_p3_dbl(&t, A); + ge25519_p1p1_to_p3(&A2, &t); + ge25519_add(&t, &A2, &Ai[0]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[1], &u); + ge25519_add(&t, &A2, &Ai[1]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[2], &u); + ge25519_add(&t, &A2, &Ai[2]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[3], &u); + ge25519_add(&t, &A2, &Ai[3]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[4], &u); + ge25519_add(&t, &A2, &Ai[4]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[5], &u); + ge25519_add(&t, &A2, &Ai[5]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[6], &u); + ge25519_add(&t, &A2, &Ai[6]); + ge25519_p1p1_to_p3(&u, &t); + ge25519_p3_to_cached(&Ai[7], &u); + + ge25519_p3_0(r); + + for (i = 252; i >= 0; --i) { + ge25519_p3_dbl(&t, r); + + if (aslide[i] > 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_add(&t, &u, &Ai[aslide[i] / 2]); + } else if (aslide[i] < 0) { + ge25519_p1p1_to_p3(&u, &t); + ge25519_sub(&t, &u, &Ai[(-aslide[i]) / 2]); + } + + ge25519_p1p1_to_p3(r, &t); + } +} + +int +ge25519_is_on_curve(const ge25519_p3 *p) +{ + fe25519 x2; + fe25519 y2; + fe25519 z2; + fe25519 z4; + fe25519 t0; + fe25519 t1; + + fe25519_sq(x2, p->X); + fe25519_sq(y2, p->Y); + fe25519_sq(z2, p->Z); + fe25519_sub(t0, y2, x2); + fe25519_mul(t0, t0, z2); + + fe25519_mul(t1, x2, y2); + fe25519_mul(t1, t1, d); + fe25519_sq(z4, z2); + fe25519_add(t1, t1, z4); + fe25519_sub(t0, t0, t1); + + return fe25519_iszero(t0); +} + +int +ge25519_is_on_main_subgroup(const ge25519_p3 *p) +{ + ge25519_p3 pl; + + ge25519_mul_l(&pl, p); + + return fe25519_iszero(pl.X); +} + +int +ge25519_is_canonical(const unsigned char *s) +{ + unsigned char c; + unsigned char d; + unsigned int i; + + c = (s[31] & 0x7f) ^ 0x7f; + for (i = 30; i > 0; i--) { + c |= s[i] ^ 0xff; + } + c = (((unsigned int) c) - 1U) >> 8; + d = (0xed - 1U - (unsigned int) s[0]) >> 8; + + return 1 - (c & d & 1); +} + +int +ge25519_has_small_order(const unsigned char s[32]) +{ + CRYPTO_ALIGN(16) + static const unsigned char blacklist[][32] = { + /* 0 (order 4) */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 1 (order 1) */ + { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + /* 2707385501144840649318225287225658788936804267575313519463743609750303402022 + (order 8) */ + { 0x26, 0xe8, 0x95, 0x8f, 0xc2, 0xb2, 0x27, 0xb0, 0x45, 0xc3, 0xf4, + 0x89, 0xf2, 0xef, 0x98, 0xf0, 0xd5, 0xdf, 0xac, 0x05, 0xd3, 0xc6, + 0x33, 0x39, 0xb1, 0x38, 0x02, 0x88, 0x6d, 0x53, 0xfc, 0x05 }, + /* 55188659117513257062467267217118295137698188065244968500265048394206261417927 + (order 8) */ + { 0xc7, 0x17, 0x6a, 0x70, 0x3d, 0x4d, 0xd8, 0x4f, 0xba, 0x3c, 0x0b, + 0x76, 0x0d, 0x10, 0x67, 0x0f, 0x2a, 0x20, 0x53, 0xfa, 0x2c, 0x39, + 0xcc, 0xc6, 0x4e, 0xc7, 0xfd, 0x77, 0x92, 0xac, 0x03, 0x7a }, + /* p-1 (order 2) */ + { 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }, + /* p (=0, order 4) */ + { 0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f }, + /* p+1 (=1, order 1) */ + { 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f } + }; + unsigned char c[7] = { 0 }; + unsigned int k; + size_t i, j; + + COMPILER_ASSERT(7 == sizeof blacklist / sizeof blacklist[0]); + for (j = 0; j < 31; j++) { + for (i = 0; i < sizeof blacklist / sizeof blacklist[0]; i++) { + c[i] |= s[j] ^ blacklist[i][j]; + } + } + for (i = 0; i < sizeof blacklist / sizeof blacklist[0]; i++) { + c[i] |= (s[j] & 0x7f) ^ blacklist[i][j]; + } + k = 0; + for (i = 0; i < sizeof blacklist / sizeof blacklist[0]; i++) { + k |= (c[i] - 1); + } + return (int) ((k >> 8) & 1); +} + +/* + Input: + a[0]+256*a[1]+...+256^31*a[31] = a + b[0]+256*b[1]+...+256^31*b[31] = b + c[0]+256*c[1]+...+256^31*c[31] = c + * + Output: + s[0]+256*s[1]+...+256^31*s[31] = (ab+c) mod l + where l = 2^252 + 27742317777372353535851937790883648493. + */ + +void +sc25519_muladd(unsigned char *s, const unsigned char *a, + const unsigned char *b, const unsigned char *c) +{ + int64_t a0 = 2097151 & load_3(a); + int64_t a1 = 2097151 & (load_4(a + 2) >> 5); + int64_t a2 = 2097151 & (load_3(a + 5) >> 2); + int64_t a3 = 2097151 & (load_4(a + 7) >> 7); + int64_t a4 = 2097151 & (load_4(a + 10) >> 4); + int64_t a5 = 2097151 & (load_3(a + 13) >> 1); + int64_t a6 = 2097151 & (load_4(a + 15) >> 6); + int64_t a7 = 2097151 & (load_3(a + 18) >> 3); + int64_t a8 = 2097151 & load_3(a + 21); + int64_t a9 = 2097151 & (load_4(a + 23) >> 5); + int64_t a10 = 2097151 & (load_3(a + 26) >> 2); + int64_t a11 = (load_4(a + 28) >> 7); + + int64_t b0 = 2097151 & load_3(b); + int64_t b1 = 2097151 & (load_4(b + 2) >> 5); + int64_t b2 = 2097151 & (load_3(b + 5) >> 2); + int64_t b3 = 2097151 & (load_4(b + 7) >> 7); + int64_t b4 = 2097151 & (load_4(b + 10) >> 4); + int64_t b5 = 2097151 & (load_3(b + 13) >> 1); + int64_t b6 = 2097151 & (load_4(b + 15) >> 6); + int64_t b7 = 2097151 & (load_3(b + 18) >> 3); + int64_t b8 = 2097151 & load_3(b + 21); + int64_t b9 = 2097151 & (load_4(b + 23) >> 5); + int64_t b10 = 2097151 & (load_3(b + 26) >> 2); + int64_t b11 = (load_4(b + 28) >> 7); + + int64_t c0 = 2097151 & load_3(c); + int64_t c1 = 2097151 & (load_4(c + 2) >> 5); + int64_t c2 = 2097151 & (load_3(c + 5) >> 2); + int64_t c3 = 2097151 & (load_4(c + 7) >> 7); + int64_t c4 = 2097151 & (load_4(c + 10) >> 4); + int64_t c5 = 2097151 & (load_3(c + 13) >> 1); + int64_t c6 = 2097151 & (load_4(c + 15) >> 6); + int64_t c7 = 2097151 & (load_3(c + 18) >> 3); + int64_t c8 = 2097151 & load_3(c + 21); + int64_t c9 = 2097151 & (load_4(c + 23) >> 5); + int64_t c10 = 2097151 & (load_3(c + 26) >> 2); + int64_t c11 = (load_4(c + 28) >> 7); + + int64_t s0; + int64_t s1; + int64_t s2; + int64_t s3; + int64_t s4; + int64_t s5; + int64_t s6; + int64_t s7; + int64_t s8; + int64_t s9; + int64_t s10; + int64_t s11; + int64_t s12; + int64_t s13; + int64_t s14; + int64_t s15; + int64_t s16; + int64_t s17; + int64_t s18; + int64_t s19; + int64_t s20; + int64_t s21; + int64_t s22; + int64_t s23; + + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + int64_t carry17; + int64_t carry18; + int64_t carry19; + int64_t carry20; + int64_t carry21; + int64_t carry22; + + s0 = c0 + a0 * b0; + s1 = c1 + a0 * b1 + a1 * b0; + s2 = c2 + a0 * b2 + a1 * b1 + a2 * b0; + s3 = c3 + a0 * b3 + a1 * b2 + a2 * b1 + a3 * b0; + s4 = c4 + a0 * b4 + a1 * b3 + a2 * b2 + a3 * b1 + a4 * b0; + s5 = c5 + a0 * b5 + a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 + a5 * b0; + s6 = c6 + a0 * b6 + a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 + + a6 * b0; + s7 = c7 + a0 * b7 + a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + + a6 * b1 + a7 * b0; + s8 = c8 + a0 * b8 + a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + + a6 * b2 + a7 * b1 + a8 * b0; + s9 = c9 + a0 * b9 + a1 * b8 + a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + + a6 * b3 + a7 * b2 + a8 * b1 + a9 * b0; + s10 = c10 + a0 * b10 + a1 * b9 + a2 * b8 + a3 * b7 + a4 * b6 + a5 * b5 + + a6 * b4 + a7 * b3 + a8 * b2 + a9 * b1 + a10 * b0; + s11 = c11 + a0 * b11 + a1 * b10 + a2 * b9 + a3 * b8 + a4 * b7 + a5 * b6 + + a6 * b5 + a7 * b4 + a8 * b3 + a9 * b2 + a10 * b1 + a11 * b0; + s12 = a1 * b11 + a2 * b10 + a3 * b9 + a4 * b8 + a5 * b7 + a6 * b6 + + a7 * b5 + a8 * b4 + a9 * b3 + a10 * b2 + a11 * b1; + s13 = a2 * b11 + a3 * b10 + a4 * b9 + a5 * b8 + a6 * b7 + a7 * b6 + + a8 * b5 + a9 * b4 + a10 * b3 + a11 * b2; + s14 = a3 * b11 + a4 * b10 + a5 * b9 + a6 * b8 + a7 * b7 + a8 * b6 + + a9 * b5 + a10 * b4 + a11 * b3; + s15 = a4 * b11 + a5 * b10 + a6 * b9 + a7 * b8 + a8 * b7 + a9 * b6 + + a10 * b5 + a11 * b4; + s16 = + a5 * b11 + a6 * b10 + a7 * b9 + a8 * b8 + a9 * b7 + a10 * b6 + a11 * b5; + s17 = a6 * b11 + a7 * b10 + a8 * b9 + a9 * b8 + a10 * b7 + a11 * b6; + s18 = a7 * b11 + a8 * b10 + a9 * b9 + a10 * b8 + a11 * b7; + s19 = a8 * b11 + a9 * b10 + a10 * b9 + a11 * b8; + s20 = a9 * b11 + a10 * b10 + a11 * b9; + s21 = a10 * b11 + a11 * b10; + s22 = a11 * b11; + s23 = 0; + + carry0 = (s0 + (int64_t) (1L << 20)) >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t) 1L << 21); + carry2 = (s2 + (int64_t) (1L << 20)) >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t) 1L << 21); + carry4 = (s4 + (int64_t) (1L << 20)) >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t) 1L << 21); + carry6 = (s6 + (int64_t) (1L << 20)) >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t) 1L << 21); + carry8 = (s8 + (int64_t) (1L << 20)) >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t) 1L << 21); + carry10 = (s10 + (int64_t) (1L << 20)) >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t) 1L << 21); + carry12 = (s12 + (int64_t) (1L << 20)) >> 21; + s13 += carry12; + s12 -= carry12 * ((uint64_t) 1L << 21); + carry14 = (s14 + (int64_t) (1L << 20)) >> 21; + s15 += carry14; + s14 -= carry14 * ((uint64_t) 1L << 21); + carry16 = (s16 + (int64_t) (1L << 20)) >> 21; + s17 += carry16; + s16 -= carry16 * ((uint64_t) 1L << 21); + carry18 = (s18 + (int64_t) (1L << 20)) >> 21; + s19 += carry18; + s18 -= carry18 * ((uint64_t) 1L << 21); + carry20 = (s20 + (int64_t) (1L << 20)) >> 21; + s21 += carry20; + s20 -= carry20 * ((uint64_t) 1L << 21); + carry22 = (s22 + (int64_t) (1L << 20)) >> 21; + s23 += carry22; + s22 -= carry22 * ((uint64_t) 1L << 21); + + carry1 = (s1 + (int64_t) (1L << 20)) >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t) 1L << 21); + carry3 = (s3 + (int64_t) (1L << 20)) >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t) 1L << 21); + carry5 = (s5 + (int64_t) (1L << 20)) >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t) 1L << 21); + carry7 = (s7 + (int64_t) (1L << 20)) >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t) 1L << 21); + carry9 = (s9 + (int64_t) (1L << 20)) >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t) 1L << 21); + carry11 = (s11 + (int64_t) (1L << 20)) >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t) 1L << 21); + carry13 = (s13 + (int64_t) (1L << 20)) >> 21; + s14 += carry13; + s13 -= carry13 * ((uint64_t) 1L << 21); + carry15 = (s15 + (int64_t) (1L << 20)) >> 21; + s16 += carry15; + s15 -= carry15 * ((uint64_t) 1L << 21); + carry17 = (s17 + (int64_t) (1L << 20)) >> 21; + s18 += carry17; + s17 -= carry17 * ((uint64_t) 1L << 21); + carry19 = (s19 + (int64_t) (1L << 20)) >> 21; + s20 += carry19; + s19 -= carry19 * ((uint64_t) 1L << 21); + carry21 = (s21 + (int64_t) (1L << 20)) >> 21; + s22 += carry21; + s21 -= carry21 * ((uint64_t) 1L << 21); + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + + carry6 = (s6 + (int64_t) (1L << 20)) >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t) 1L << 21); + carry8 = (s8 + (int64_t) (1L << 20)) >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t) 1L << 21); + carry10 = (s10 + (int64_t) (1L << 20)) >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t) 1L << 21); + carry12 = (s12 + (int64_t) (1L << 20)) >> 21; + s13 += carry12; + s12 -= carry12 * ((uint64_t) 1L << 21); + carry14 = (s14 + (int64_t) (1L << 20)) >> 21; + s15 += carry14; + s14 -= carry14 * ((uint64_t) 1L << 21); + carry16 = (s16 + (int64_t) (1L << 20)) >> 21; + s17 += carry16; + s16 -= carry16 * ((uint64_t) 1L << 21); + + carry7 = (s7 + (int64_t) (1L << 20)) >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t) 1L << 21); + carry9 = (s9 + (int64_t) (1L << 20)) >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t) 1L << 21); + carry11 = (s11 + (int64_t) (1L << 20)) >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t) 1L << 21); + carry13 = (s13 + (int64_t) (1L << 20)) >> 21; + s14 += carry13; + s13 -= carry13 * ((uint64_t) 1L << 21); + carry15 = (s15 + (int64_t) (1L << 20)) >> 21; + s16 += carry15; + s15 -= carry15 * ((uint64_t) 1L << 21); + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (int64_t) (1L << 20)) >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t) 1L << 21); + carry2 = (s2 + (int64_t) (1L << 20)) >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t) 1L << 21); + carry4 = (s4 + (int64_t) (1L << 20)) >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t) 1L << 21); + carry6 = (s6 + (int64_t) (1L << 20)) >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t) 1L << 21); + carry8 = (s8 + (int64_t) (1L << 20)) >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t) 1L << 21); + carry10 = (s10 + (int64_t) (1L << 20)) >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t) 1L << 21); + + carry1 = (s1 + (int64_t) (1L << 20)) >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t) 1L << 21); + carry3 = (s3 + (int64_t) (1L << 20)) >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t) 1L << 21); + carry5 = (s5 + (int64_t) (1L << 20)) >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t) 1L << 21); + carry7 = (s7 + (int64_t) (1L << 20)) >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t) 1L << 21); + carry9 = (s9 + (int64_t) (1L << 20)) >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t) 1L << 21); + carry11 = (s11 + (int64_t) (1L << 20)) >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t) 1L << 21); + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t) 1L << 21); + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t) 1L << 21); + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t) 1L << 21); + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t) 1L << 21); + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t) 1L << 21); + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t) 1L << 21); + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t) 1L << 21); + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t) 1L << 21); + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t) 1L << 21); + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t) 1L << 21); + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t) 1L << 21); + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t) 1L << 21); + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t) 1L << 21); + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t) 1L << 21); + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t) 1L << 21); + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t) 1L << 21); + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t) 1L << 21); + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t) 1L << 21); + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t) 1L << 21); + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t) 1L << 21); + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t) 1L << 21); + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t) 1L << 21); + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t) 1L << 21); + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 * ((uint64_t) 1 << 5)); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 * ((uint64_t) 1 << 2)); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 * ((uint64_t) 1 << 7)); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 * ((uint64_t) 1 << 4)); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 * ((uint64_t) 1 << 1)); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 * ((uint64_t) 1 << 6)); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 * ((uint64_t) 1 << 3)); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 * ((uint64_t) 1 << 5)); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 * ((uint64_t) 1 << 2)); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 * ((uint64_t) 1 << 7)); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} + +/* + Input: + s[0]+256*s[1]+...+256^63*s[63] = s + * + Output: + s[0]+256*s[1]+...+256^31*s[31] = s mod l + where l = 2^252 + 27742317777372353535851937790883648493. + Overwrites s in place. + */ + +void +sc25519_reduce(unsigned char *s) +{ + int64_t s0 = 2097151 & load_3(s); + int64_t s1 = 2097151 & (load_4(s + 2) >> 5); + int64_t s2 = 2097151 & (load_3(s + 5) >> 2); + int64_t s3 = 2097151 & (load_4(s + 7) >> 7); + int64_t s4 = 2097151 & (load_4(s + 10) >> 4); + int64_t s5 = 2097151 & (load_3(s + 13) >> 1); + int64_t s6 = 2097151 & (load_4(s + 15) >> 6); + int64_t s7 = 2097151 & (load_3(s + 18) >> 3); + int64_t s8 = 2097151 & load_3(s + 21); + int64_t s9 = 2097151 & (load_4(s + 23) >> 5); + int64_t s10 = 2097151 & (load_3(s + 26) >> 2); + int64_t s11 = 2097151 & (load_4(s + 28) >> 7); + int64_t s12 = 2097151 & (load_4(s + 31) >> 4); + int64_t s13 = 2097151 & (load_3(s + 34) >> 1); + int64_t s14 = 2097151 & (load_4(s + 36) >> 6); + int64_t s15 = 2097151 & (load_3(s + 39) >> 3); + int64_t s16 = 2097151 & load_3(s + 42); + int64_t s17 = 2097151 & (load_4(s + 44) >> 5); + int64_t s18 = 2097151 & (load_3(s + 47) >> 2); + int64_t s19 = 2097151 & (load_4(s + 49) >> 7); + int64_t s20 = 2097151 & (load_4(s + 52) >> 4); + int64_t s21 = 2097151 & (load_3(s + 55) >> 1); + int64_t s22 = 2097151 & (load_4(s + 57) >> 6); + int64_t s23 = (load_4(s + 60) >> 3); + + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + int64_t carry10; + int64_t carry11; + int64_t carry12; + int64_t carry13; + int64_t carry14; + int64_t carry15; + int64_t carry16; + + s11 += s23 * 666643; + s12 += s23 * 470296; + s13 += s23 * 654183; + s14 -= s23 * 997805; + s15 += s23 * 136657; + s16 -= s23 * 683901; + + s10 += s22 * 666643; + s11 += s22 * 470296; + s12 += s22 * 654183; + s13 -= s22 * 997805; + s14 += s22 * 136657; + s15 -= s22 * 683901; + + s9 += s21 * 666643; + s10 += s21 * 470296; + s11 += s21 * 654183; + s12 -= s21 * 997805; + s13 += s21 * 136657; + s14 -= s21 * 683901; + + s8 += s20 * 666643; + s9 += s20 * 470296; + s10 += s20 * 654183; + s11 -= s20 * 997805; + s12 += s20 * 136657; + s13 -= s20 * 683901; + + s7 += s19 * 666643; + s8 += s19 * 470296; + s9 += s19 * 654183; + s10 -= s19 * 997805; + s11 += s19 * 136657; + s12 -= s19 * 683901; + + s6 += s18 * 666643; + s7 += s18 * 470296; + s8 += s18 * 654183; + s9 -= s18 * 997805; + s10 += s18 * 136657; + s11 -= s18 * 683901; + + carry6 = (s6 + (int64_t) (1L << 20)) >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t) 1L << 21); + carry8 = (s8 + (int64_t) (1L << 20)) >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t) 1L << 21); + carry10 = (s10 + (int64_t) (1L << 20)) >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t) 1L << 21); + carry12 = (s12 + (int64_t) (1L << 20)) >> 21; + s13 += carry12; + s12 -= carry12 * ((uint64_t) 1L << 21); + carry14 = (s14 + (int64_t) (1L << 20)) >> 21; + s15 += carry14; + s14 -= carry14 * ((uint64_t) 1L << 21); + carry16 = (s16 + (int64_t) (1L << 20)) >> 21; + s17 += carry16; + s16 -= carry16 * ((uint64_t) 1L << 21); + + carry7 = (s7 + (int64_t) (1L << 20)) >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t) 1L << 21); + carry9 = (s9 + (int64_t) (1L << 20)) >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t) 1L << 21); + carry11 = (s11 + (int64_t) (1L << 20)) >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t) 1L << 21); + carry13 = (s13 + (int64_t) (1L << 20)) >> 21; + s14 += carry13; + s13 -= carry13 * ((uint64_t) 1L << 21); + carry15 = (s15 + (int64_t) (1L << 20)) >> 21; + s16 += carry15; + s15 -= carry15 * ((uint64_t) 1L << 21); + + s5 += s17 * 666643; + s6 += s17 * 470296; + s7 += s17 * 654183; + s8 -= s17 * 997805; + s9 += s17 * 136657; + s10 -= s17 * 683901; + + s4 += s16 * 666643; + s5 += s16 * 470296; + s6 += s16 * 654183; + s7 -= s16 * 997805; + s8 += s16 * 136657; + s9 -= s16 * 683901; + + s3 += s15 * 666643; + s4 += s15 * 470296; + s5 += s15 * 654183; + s6 -= s15 * 997805; + s7 += s15 * 136657; + s8 -= s15 * 683901; + + s2 += s14 * 666643; + s3 += s14 * 470296; + s4 += s14 * 654183; + s5 -= s14 * 997805; + s6 += s14 * 136657; + s7 -= s14 * 683901; + + s1 += s13 * 666643; + s2 += s13 * 470296; + s3 += s13 * 654183; + s4 -= s13 * 997805; + s5 += s13 * 136657; + s6 -= s13 * 683901; + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = (s0 + (int64_t) (1L << 20)) >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t) 1L << 21); + carry2 = (s2 + (int64_t) (1L << 20)) >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t) 1L << 21); + carry4 = (s4 + (int64_t) (1L << 20)) >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t) 1L << 21); + carry6 = (s6 + (int64_t) (1L << 20)) >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t) 1L << 21); + carry8 = (s8 + (int64_t) (1L << 20)) >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t) 1L << 21); + carry10 = (s10 + (int64_t) (1L << 20)) >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t) 1L << 21); + + carry1 = (s1 + (int64_t) (1L << 20)) >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t) 1L << 21); + carry3 = (s3 + (int64_t) (1L << 20)) >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t) 1L << 21); + carry5 = (s5 + (int64_t) (1L << 20)) >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t) 1L << 21); + carry7 = (s7 + (int64_t) (1L << 20)) >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t) 1L << 21); + carry9 = (s9 + (int64_t) (1L << 20)) >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t) 1L << 21); + carry11 = (s11 + (int64_t) (1L << 20)) >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t) 1L << 21); + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + s12 = 0; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t) 1L << 21); + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t) 1L << 21); + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t) 1L << 21); + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t) 1L << 21); + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t) 1L << 21); + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t) 1L << 21); + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t) 1L << 21); + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t) 1L << 21); + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t) 1L << 21); + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t) 1L << 21); + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t) 1L << 21); + carry11 = s11 >> 21; + s12 += carry11; + s11 -= carry11 * ((uint64_t) 1L << 21); + + s0 += s12 * 666643; + s1 += s12 * 470296; + s2 += s12 * 654183; + s3 -= s12 * 997805; + s4 += s12 * 136657; + s5 -= s12 * 683901; + + carry0 = s0 >> 21; + s1 += carry0; + s0 -= carry0 * ((uint64_t) 1L << 21); + carry1 = s1 >> 21; + s2 += carry1; + s1 -= carry1 * ((uint64_t) 1L << 21); + carry2 = s2 >> 21; + s3 += carry2; + s2 -= carry2 * ((uint64_t) 1L << 21); + carry3 = s3 >> 21; + s4 += carry3; + s3 -= carry3 * ((uint64_t) 1L << 21); + carry4 = s4 >> 21; + s5 += carry4; + s4 -= carry4 * ((uint64_t) 1L << 21); + carry5 = s5 >> 21; + s6 += carry5; + s5 -= carry5 * ((uint64_t) 1L << 21); + carry6 = s6 >> 21; + s7 += carry6; + s6 -= carry6 * ((uint64_t) 1L << 21); + carry7 = s7 >> 21; + s8 += carry7; + s7 -= carry7 * ((uint64_t) 1L << 21); + carry8 = s8 >> 21; + s9 += carry8; + s8 -= carry8 * ((uint64_t) 1L << 21); + carry9 = s9 >> 21; + s10 += carry9; + s9 -= carry9 * ((uint64_t) 1L << 21); + carry10 = s10 >> 21; + s11 += carry10; + s10 -= carry10 * ((uint64_t) 1L << 21); + + s[0] = s0 >> 0; + s[1] = s0 >> 8; + s[2] = (s0 >> 16) | (s1 * ((uint64_t) 1 << 5)); + s[3] = s1 >> 3; + s[4] = s1 >> 11; + s[5] = (s1 >> 19) | (s2 * ((uint64_t) 1 << 2)); + s[6] = s2 >> 6; + s[7] = (s2 >> 14) | (s3 * ((uint64_t) 1 << 7)); + s[8] = s3 >> 1; + s[9] = s3 >> 9; + s[10] = (s3 >> 17) | (s4 * ((uint64_t) 1 << 4)); + s[11] = s4 >> 4; + s[12] = s4 >> 12; + s[13] = (s4 >> 20) | (s5 * ((uint64_t) 1 << 1)); + s[14] = s5 >> 7; + s[15] = (s5 >> 15) | (s6 * ((uint64_t) 1 << 6)); + s[16] = s6 >> 2; + s[17] = s6 >> 10; + s[18] = (s6 >> 18) | (s7 * ((uint64_t) 1 << 3)); + s[19] = s7 >> 5; + s[20] = s7 >> 13; + s[21] = s8 >> 0; + s[22] = s8 >> 8; + s[23] = (s8 >> 16) | (s9 * ((uint64_t) 1 << 5)); + s[24] = s9 >> 3; + s[25] = s9 >> 11; + s[26] = (s9 >> 19) | (s10 * ((uint64_t) 1 << 2)); + s[27] = s10 >> 6; + s[28] = (s10 >> 14) | (s11 * ((uint64_t) 1 << 7)); + s[29] = s11 >> 1; + s[30] = s11 >> 9; + s[31] = s11 >> 17; +} + +int +sc25519_is_canonical(const unsigned char *s) +{ + /* 2^252+27742317777372353535851937790883648493 */ + static const unsigned char L[32] = { + 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, + 0xa2, 0xde, 0xf9, 0xde, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10 + }; + unsigned char c = 0; + unsigned char n = 1; + unsigned int i = 32; + + do { + i--; + c |= ((s[i] - L[i]) >> 8) & n; + n &= ((s[i] ^ L[i]) - 1) >> 8; + } while (i != 0); + + return (c != 0); +} + +static void +chi25519(fe25519 out, const fe25519 z) +{ + fe25519 t0, t1, t2, t3; + int i; + + fe25519_sq(t0, z); + fe25519_mul(t1, t0, z); + fe25519_sq(t0, t1); + fe25519_sq(t2, t0); + fe25519_sq(t2, t2); + fe25519_mul(t2, t2, t0); + fe25519_mul(t1, t2, z); + fe25519_sq(t2, t1); + + for (i = 1; i < 5; i++) { + fe25519_sq(t2, t2); + } + fe25519_mul(t1, t2, t1); + fe25519_sq(t2, t1); + for (i = 1; i < 10; i++) { + fe25519_sq(t2, t2); + } + fe25519_mul(t2, t2, t1); + fe25519_sq(t3, t2); + for (i = 1; i < 20; i++) { + fe25519_sq(t3, t3); + } + fe25519_mul(t2, t3, t2); + fe25519_sq(t2, t2); + for (i = 1; i < 10; i++) { + fe25519_sq(t2, t2); + } + fe25519_mul(t1, t2, t1); + fe25519_sq(t2, t1); + for (i = 1; i < 50; i++) { + fe25519_sq(t2, t2); + } + fe25519_mul(t2, t2, t1); + fe25519_sq(t3, t2); + for (i = 1; i < 100; i++) { + fe25519_sq(t3, t3); + } + fe25519_mul(t2, t3, t2); + fe25519_sq(t2, t2); + for (i = 1; i < 50; i++) { + fe25519_sq(t2, t2); + } + fe25519_mul(t1, t2, t1); + fe25519_sq(t1, t1); + for (i = 1; i < 4; i++) { + fe25519_sq(t1, t1); + } + fe25519_mul(out, t1, t0); +} + +void +ge25519_from_uniform(unsigned char s[32], const unsigned char r[32]) +{ + fe25519 e; + fe25519 negx; + fe25519 rr2; + fe25519 x, x2, x3; + ge25519_p3 p3; + ge25519_p1p1 p1; + ge25519_p2 p2; + unsigned int e_is_minus_1; + unsigned char x_sign; + + memcpy(s, r, 32); + x_sign = s[31] & 0x80; + s[31] &= 0x7f; + + fe25519_frombytes(rr2, s); + + /* elligator */ + fe25519_sq2(rr2, rr2); + rr2[0]++; + fe25519_invert(rr2, rr2); + fe25519_mul(x, curve25519_A, rr2); + fe25519_neg(x, x); + + fe25519_sq(x2, x); + fe25519_mul(x3, x, x2); + fe25519_add(e, x3, x); + fe25519_mul(x2, x2, curve25519_A); + fe25519_add(e, x2, e); + + chi25519(e, e); + + fe25519_tobytes(s, e); + e_is_minus_1 = s[1] & 1; + fe25519_neg(negx, x); + fe25519_cmov(x, negx, e_is_minus_1); + fe25519_0(x2); + fe25519_cmov(x2, curve25519_A, e_is_minus_1); + fe25519_sub(x, x, x2); + + /* yed = (x-1)/(x+1) */ + { + fe25519 one; + fe25519 x_plus_one; + fe25519 x_plus_one_inv; + fe25519 x_minus_one; + fe25519 yed; + + fe25519_1(one); + fe25519_add(x_plus_one, x, one); + fe25519_sub(x_minus_one, x, one); + fe25519_invert(x_plus_one_inv, x_plus_one); + fe25519_mul(yed, x_minus_one, x_plus_one_inv); + fe25519_tobytes(s, yed); + } + + /* recover x */ + s[31] |= x_sign; + if (ge25519_frombytes(&p3, s) != 0) { + abort(); /* LCOV_EXCL_LINE */ + } + + /* multiply by the cofactor */ + ge25519_p3_dbl(&p1, &p3); + ge25519_p1p1_to_p2(&p2, &p1); + ge25519_p2_dbl(&p1, &p2); + ge25519_p1p1_to_p2(&p2, &p1); + ge25519_p2_dbl(&p1, &p2); + ge25519_p1p1_to_p3(&p3, &p1); + + ge25519_p3_tobytes(s, &p3); +} +} \ No newline at end of file diff --git a/contrib/sodium/ed25519_ref10.h b/contrib/sodium/ed25519_ref10.h new file mode 100644 index 000000000..372d0c392 --- /dev/null +++ b/contrib/sodium/ed25519_ref10.h @@ -0,0 +1,154 @@ + +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#ifndef ed25519_ref10_H +#define ed25519_ref10_H + +#include +#include + +/* + fe means field element. + Here the field is \Z/(2^255-19). + */ +namespace sodium { + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int32_t fe25519[10]; + + +void fe25519_invert(fe25519 out, const fe25519 z); +void fe25519_frombytes(fe25519 h, const unsigned char *s); +void fe25519_tobytes(unsigned char *s, const fe25519 h); + + + + +/* + ge means group element. + + Here the group is the set of pairs (x,y) of field elements + satisfying -x^2 + y^2 = 1 + d x^2y^2 + where d = -121665/121666. + + Representations: + ge25519_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z + ge25519_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT + ge25519_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T + ge25519_precomp (Duif): (y+x,y-x,2dxy) + */ + + +typedef struct { + fe25519 X; + fe25519 Y; + fe25519 Z; +} ge25519_p2; + +typedef struct { + fe25519 X; + fe25519 Y; + fe25519 Z; + fe25519 T; +} ge25519_p3; + +typedef struct { + fe25519 X; + fe25519 Y; + fe25519 Z; + fe25519 T; +} ge25519_p1p1; + +typedef struct { + fe25519 yplusx; + fe25519 yminusx; + fe25519 xy2d; +} ge25519_precomp; + +typedef struct { + fe25519 YplusX; + fe25519 YminusX; + fe25519 Z; + fe25519 T2d; +} ge25519_cached; + +void ge25519_tobytes(unsigned char *s, const ge25519_p2 *h); + +void ge25519_p3_tobytes(unsigned char *s, const ge25519_p3 *h); + +int ge25519_frombytes(ge25519_p3 *h, const unsigned char *s); + +int ge25519_frombytes_negate_vartime(ge25519_p3 *h, const unsigned char *s); + +void ge25519_p3_to_cached(ge25519_cached *r, const ge25519_p3 *p); + +void ge25519_p1p1_to_p2(ge25519_p2 *r, const ge25519_p1p1 *p); + +void ge25519_p1p1_to_p3(ge25519_p3 *r, const ge25519_p1p1 *p); + +void ge25519_add(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_cached *q); + +void ge25519_sub(ge25519_p1p1 *r, const ge25519_p3 *p, const ge25519_cached *q); + +void ge25519_scalarmult_base(ge25519_p3 *h, const unsigned char *a); + +void ge25519_double_scalarmult_vartime(ge25519_p2 *r, const unsigned char *a, + const ge25519_p3 *A, + const unsigned char *b); + +void ge25519_scalarmult(ge25519_p3 *h, const unsigned char *a, + const ge25519_p3 *p); + +int ge25519_is_canonical(const unsigned char *s); + +int ge25519_is_on_curve(const ge25519_p3 *p); + +int ge25519_is_on_main_subgroup(const ge25519_p3 *p); + +int ge25519_has_small_order(const unsigned char s[32]); + +void ge25519_from_uniform(unsigned char s[32], const unsigned char r[32]); + +/* + The set of scalars is \Z/l + where l = 2^252 + 27742317777372353535851937790883648493. + */ + +void sc25519_reduce(unsigned char *s); + +void sc25519_muladd(unsigned char *s, const unsigned char *a, + const unsigned char *b, const unsigned char *c); + +int sc25519_is_canonical(const unsigned char *s); + +#ifdef __cplusplus +} +#endif + +} + +#endif diff --git a/contrib/sodium/ed25519_ref10_fe_25_5.h b/contrib/sodium/ed25519_ref10_fe_25_5.h new file mode 100644 index 000000000..41f8416e2 --- /dev/null +++ b/contrib/sodium/ed25519_ref10_fe_25_5.h @@ -0,0 +1,1085 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#ifndef ed25519_ref10_FE_25_5_H +#define ed25519_ref10_FE_25_5_H + +#include + +#include "common.h" + + +namespace sodium { + +/* + h = 0 + */ + +static inline void fe25519_0(fe25519 h) +{ + memset(&h[0], 0, 10 * sizeof h[0]); +} + +/* + h = 1 + */ + +static inline void fe25519_1(fe25519 h) +{ + h[0] = 1; + h[1] = 0; + memset(&h[2], 0, 8 * sizeof h[0]); +} + +/* + h = f + g + Can overlap h with f or g. + * + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + * + Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + */ + +static inline void +fe25519_add(fe25519 h, const fe25519 f, const fe25519 g) +{ + int32_t h0 = f[0] + g[0]; + int32_t h1 = f[1] + g[1]; + int32_t h2 = f[2] + g[2]; + int32_t h3 = f[3] + g[3]; + int32_t h4 = f[4] + g[4]; + int32_t h5 = f[5] + g[5]; + int32_t h6 = f[6] + g[6]; + int32_t h7 = f[7] + g[7]; + int32_t h8 = f[8] + g[8]; + int32_t h9 = f[9] + g[9]; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + +/* + h = f - g + Can overlap h with f or g. + * + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + |g| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + * + Postconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + */ + +/*static*/ void fe25519_sub(fe25519 h, const fe25519 f, const fe25519 g) +{ + int32_t h0 = f[0] - g[0]; + int32_t h1 = f[1] - g[1]; + int32_t h2 = f[2] - g[2]; + int32_t h3 = f[3] - g[3]; + int32_t h4 = f[4] - g[4]; + int32_t h5 = f[5] - g[5]; + int32_t h6 = f[6] - g[6]; + int32_t h7 = f[7] - g[7]; + int32_t h8 = f[8] - g[8]; + int32_t h9 = f[9] - g[9]; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + +/* + h = -f + * + Preconditions: + |f| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + * + Postconditions: + |h| bounded by 1.1*2^25,1.1*2^24,1.1*2^25,1.1*2^24,etc. + */ + +static inline void fe25519_neg(fe25519 h, const fe25519 f) +{ + int32_t h0 = -f[0]; + int32_t h1 = -f[1]; + int32_t h2 = -f[2]; + int32_t h3 = -f[3]; + int32_t h4 = -f[4]; + int32_t h5 = -f[5]; + int32_t h6 = -f[6]; + int32_t h7 = -f[7]; + int32_t h8 = -f[8]; + int32_t h9 = -f[9]; + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + +/* + Replace (f,g) with (g,g) if b == 1; + replace (f,g) with (f,g) if b == 0. + * + Preconditions: b in {0,1}. + */ + +/*static*/ void fe25519_cmov(fe25519 f, const fe25519 g, unsigned int b) +{ + const uint32_t mask = (uint32_t) (-(int32_t) b); + + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + int32_t x0 = f0 ^ g[0]; + int32_t x1 = f1 ^ g[1]; + int32_t x2 = f2 ^ g[2]; + int32_t x3 = f3 ^ g[3]; + int32_t x4 = f4 ^ g[4]; + int32_t x5 = f5 ^ g[5]; + int32_t x6 = f6 ^ g[6]; + int32_t x7 = f7 ^ g[7]; + int32_t x8 = f8 ^ g[8]; + int32_t x9 = f9 ^ g[9]; + + x0 &= mask; + x1 &= mask; + x2 &= mask; + x3 &= mask; + x4 &= mask; + x5 &= mask; + x6 &= mask; + x7 &= mask; + x8 &= mask; + x9 &= mask; + + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; +} + +/*static*/ void fe25519_cswap(fe25519 f, fe25519 g, unsigned int b) +{ + const uint32_t mask = (uint32_t) (-(int64_t) b); + + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + + int32_t x0 = f0 ^ g0; + int32_t x1 = f1 ^ g1; + int32_t x2 = f2 ^ g2; + int32_t x3 = f3 ^ g3; + int32_t x4 = f4 ^ g4; + int32_t x5 = f5 ^ g5; + int32_t x6 = f6 ^ g6; + int32_t x7 = f7 ^ g7; + int32_t x8 = f8 ^ g8; + int32_t x9 = f9 ^ g9; + + x0 &= mask; + x1 &= mask; + x2 &= mask; + x3 &= mask; + x4 &= mask; + x5 &= mask; + x6 &= mask; + x7 &= mask; + x8 &= mask; + x9 &= mask; + + f[0] = f0 ^ x0; + f[1] = f1 ^ x1; + f[2] = f2 ^ x2; + f[3] = f3 ^ x3; + f[4] = f4 ^ x4; + f[5] = f5 ^ x5; + f[6] = f6 ^ x6; + f[7] = f7 ^ x7; + f[8] = f8 ^ x8; + f[9] = f9 ^ x9; + + g[0] = g0 ^ x0; + g[1] = g1 ^ x1; + g[2] = g2 ^ x2; + g[3] = g3 ^ x3; + g[4] = g4 ^ x4; + g[5] = g5 ^ x5; + g[6] = g6 ^ x6; + g[7] = g7 ^ x7; + g[8] = g8 ^ x8; + g[9] = g9 ^ x9; +} + +/* + h = f + */ + +static inline void fe25519_copy(fe25519 h, const fe25519 f) +{ + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + h[0] = f0; + h[1] = f1; + h[2] = f2; + h[3] = f3; + h[4] = f4; + h[5] = f5; + h[6] = f6; + h[7] = f7; + h[8] = f8; + h[9] = f9; +} + +/* + return 1 if f is in {1,3,5,...,q-2} + return 0 if f is in {0,2,4,...,q-1} + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + */ + +static inline int fe25519_isnegative(const fe25519 f) +{ + unsigned char s[32]; + + fe25519_tobytes(s, f); + + return s[0] & 1; +} + +/* + return 1 if f == 0 + return 0 if f != 0 + + Preconditions: + |f| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + */ + + +static inline int local_is_zero(const unsigned char *n, const size_t nlen) +{ + size_t i; + volatile unsigned char d = 0U; + + for (i = 0U; i < nlen; i++) { + d |= n[i]; + } + return 1 & ((d - 1) >> 8); +} + +static inline int fe25519_iszero(const fe25519 f) +{ + unsigned char s[32]; + + fe25519_tobytes(s, f); + + return local_is_zero(s, 32); +} + +/* + h = f * g + Can overlap h with f or g. + * + Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + |g| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + * + Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. + */ + +/* + Notes on implementation strategy: + * + Using schoolbook multiplication. + Karatsuba would save a little in some cost models. + * + Most multiplications by 2 and 19 are 32-bit precomputations; + cheaper than 64-bit postcomputations. + * + There is one remaining multiplication by 19 in the carry chain; + one *19 precomputation can be merged into this, + but the resulting data flow is considerably less clean. + * + There are 12 carries below. + 10 of them are 2-way parallelizable and vectorizable. + Can get away with 11 carries, but then data flow is much deeper. + * + With tighter constraints on inputs can squeeze carries into int32. + */ + +/*static*/ void +fe25519_mul(fe25519 h, const fe25519 f, const fe25519 g) +{ + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + int32_t g0 = g[0]; + int32_t g1 = g[1]; + int32_t g2 = g[2]; + int32_t g3 = g[3]; + int32_t g4 = g[4]; + int32_t g5 = g[5]; + int32_t g6 = g[6]; + int32_t g7 = g[7]; + int32_t g8 = g[8]; + int32_t g9 = g[9]; + + int32_t g1_19 = 19 * g1; /* 1.959375*2^29 */ + int32_t g2_19 = 19 * g2; /* 1.959375*2^30; still ok */ + int32_t g3_19 = 19 * g3; + int32_t g4_19 = 19 * g4; + int32_t g5_19 = 19 * g5; + int32_t g6_19 = 19 * g6; + int32_t g7_19 = 19 * g7; + int32_t g8_19 = 19 * g8; + int32_t g9_19 = 19 * g9; + int32_t f1_2 = 2 * f1; + int32_t f3_2 = 2 * f3; + int32_t f5_2 = 2 * f5; + int32_t f7_2 = 2 * f7; + int32_t f9_2 = 2 * f9; + + int64_t f0g0 = f0 * (int64_t) g0; + int64_t f0g1 = f0 * (int64_t) g1; + int64_t f0g2 = f0 * (int64_t) g2; + int64_t f0g3 = f0 * (int64_t) g3; + int64_t f0g4 = f0 * (int64_t) g4; + int64_t f0g5 = f0 * (int64_t) g5; + int64_t f0g6 = f0 * (int64_t) g6; + int64_t f0g7 = f0 * (int64_t) g7; + int64_t f0g8 = f0 * (int64_t) g8; + int64_t f0g9 = f0 * (int64_t) g9; + int64_t f1g0 = f1 * (int64_t) g0; + int64_t f1g1_2 = f1_2 * (int64_t) g1; + int64_t f1g2 = f1 * (int64_t) g2; + int64_t f1g3_2 = f1_2 * (int64_t) g3; + int64_t f1g4 = f1 * (int64_t) g4; + int64_t f1g5_2 = f1_2 * (int64_t) g5; + int64_t f1g6 = f1 * (int64_t) g6; + int64_t f1g7_2 = f1_2 * (int64_t) g7; + int64_t f1g8 = f1 * (int64_t) g8; + int64_t f1g9_38 = f1_2 * (int64_t) g9_19; + int64_t f2g0 = f2 * (int64_t) g0; + int64_t f2g1 = f2 * (int64_t) g1; + int64_t f2g2 = f2 * (int64_t) g2; + int64_t f2g3 = f2 * (int64_t) g3; + int64_t f2g4 = f2 * (int64_t) g4; + int64_t f2g5 = f2 * (int64_t) g5; + int64_t f2g6 = f2 * (int64_t) g6; + int64_t f2g7 = f2 * (int64_t) g7; + int64_t f2g8_19 = f2 * (int64_t) g8_19; + int64_t f2g9_19 = f2 * (int64_t) g9_19; + int64_t f3g0 = f3 * (int64_t) g0; + int64_t f3g1_2 = f3_2 * (int64_t) g1; + int64_t f3g2 = f3 * (int64_t) g2; + int64_t f3g3_2 = f3_2 * (int64_t) g3; + int64_t f3g4 = f3 * (int64_t) g4; + int64_t f3g5_2 = f3_2 * (int64_t) g5; + int64_t f3g6 = f3 * (int64_t) g6; + int64_t f3g7_38 = f3_2 * (int64_t) g7_19; + int64_t f3g8_19 = f3 * (int64_t) g8_19; + int64_t f3g9_38 = f3_2 * (int64_t) g9_19; + int64_t f4g0 = f4 * (int64_t) g0; + int64_t f4g1 = f4 * (int64_t) g1; + int64_t f4g2 = f4 * (int64_t) g2; + int64_t f4g3 = f4 * (int64_t) g3; + int64_t f4g4 = f4 * (int64_t) g4; + int64_t f4g5 = f4 * (int64_t) g5; + int64_t f4g6_19 = f4 * (int64_t) g6_19; + int64_t f4g7_19 = f4 * (int64_t) g7_19; + int64_t f4g8_19 = f4 * (int64_t) g8_19; + int64_t f4g9_19 = f4 * (int64_t) g9_19; + int64_t f5g0 = f5 * (int64_t) g0; + int64_t f5g1_2 = f5_2 * (int64_t) g1; + int64_t f5g2 = f5 * (int64_t) g2; + int64_t f5g3_2 = f5_2 * (int64_t) g3; + int64_t f5g4 = f5 * (int64_t) g4; + int64_t f5g5_38 = f5_2 * (int64_t) g5_19; + int64_t f5g6_19 = f5 * (int64_t) g6_19; + int64_t f5g7_38 = f5_2 * (int64_t) g7_19; + int64_t f5g8_19 = f5 * (int64_t) g8_19; + int64_t f5g9_38 = f5_2 * (int64_t) g9_19; + int64_t f6g0 = f6 * (int64_t) g0; + int64_t f6g1 = f6 * (int64_t) g1; + int64_t f6g2 = f6 * (int64_t) g2; + int64_t f6g3 = f6 * (int64_t) g3; + int64_t f6g4_19 = f6 * (int64_t) g4_19; + int64_t f6g5_19 = f6 * (int64_t) g5_19; + int64_t f6g6_19 = f6 * (int64_t) g6_19; + int64_t f6g7_19 = f6 * (int64_t) g7_19; + int64_t f6g8_19 = f6 * (int64_t) g8_19; + int64_t f6g9_19 = f6 * (int64_t) g9_19; + int64_t f7g0 = f7 * (int64_t) g0; + int64_t f7g1_2 = f7_2 * (int64_t) g1; + int64_t f7g2 = f7 * (int64_t) g2; + int64_t f7g3_38 = f7_2 * (int64_t) g3_19; + int64_t f7g4_19 = f7 * (int64_t) g4_19; + int64_t f7g5_38 = f7_2 * (int64_t) g5_19; + int64_t f7g6_19 = f7 * (int64_t) g6_19; + int64_t f7g7_38 = f7_2 * (int64_t) g7_19; + int64_t f7g8_19 = f7 * (int64_t) g8_19; + int64_t f7g9_38 = f7_2 * (int64_t) g9_19; + int64_t f8g0 = f8 * (int64_t) g0; + int64_t f8g1 = f8 * (int64_t) g1; + int64_t f8g2_19 = f8 * (int64_t) g2_19; + int64_t f8g3_19 = f8 * (int64_t) g3_19; + int64_t f8g4_19 = f8 * (int64_t) g4_19; + int64_t f8g5_19 = f8 * (int64_t) g5_19; + int64_t f8g6_19 = f8 * (int64_t) g6_19; + int64_t f8g7_19 = f8 * (int64_t) g7_19; + int64_t f8g8_19 = f8 * (int64_t) g8_19; + int64_t f8g9_19 = f8 * (int64_t) g9_19; + int64_t f9g0 = f9 * (int64_t) g0; + int64_t f9g1_38 = f9_2 * (int64_t) g1_19; + int64_t f9g2_19 = f9 * (int64_t) g2_19; + int64_t f9g3_38 = f9_2 * (int64_t) g3_19; + int64_t f9g4_19 = f9 * (int64_t) g4_19; + int64_t f9g5_38 = f9_2 * (int64_t) g5_19; + int64_t f9g6_19 = f9 * (int64_t) g6_19; + int64_t f9g7_38 = f9_2 * (int64_t) g7_19; + int64_t f9g8_19 = f9 * (int64_t) g8_19; + int64_t f9g9_38 = f9_2 * (int64_t) g9_19; + + int64_t h0 = f0g0 + f1g9_38 + f2g8_19 + f3g7_38 + f4g6_19 + f5g5_38 + + f6g4_19 + f7g3_38 + f8g2_19 + f9g1_38; + int64_t h1 = f0g1 + f1g0 + f2g9_19 + f3g8_19 + f4g7_19 + f5g6_19 + f6g5_19 + + f7g4_19 + f8g3_19 + f9g2_19; + int64_t h2 = f0g2 + f1g1_2 + f2g0 + f3g9_38 + f4g8_19 + f5g7_38 + f6g6_19 + + f7g5_38 + f8g4_19 + f9g3_38; + int64_t h3 = f0g3 + f1g2 + f2g1 + f3g0 + f4g9_19 + f5g8_19 + f6g7_19 + + f7g6_19 + f8g5_19 + f9g4_19; + int64_t h4 = f0g4 + f1g3_2 + f2g2 + f3g1_2 + f4g0 + f5g9_38 + f6g8_19 + + f7g7_38 + f8g6_19 + f9g5_38; + int64_t h5 = f0g5 + f1g4 + f2g3 + f3g2 + f4g1 + f5g0 + f6g9_19 + f7g8_19 + + f8g7_19 + f9g6_19; + int64_t h6 = f0g6 + f1g5_2 + f2g4 + f3g3_2 + f4g2 + f5g1_2 + f6g0 + + f7g9_38 + f8g8_19 + f9g7_38; + int64_t h7 = f0g7 + f1g6 + f2g5 + f3g4 + f4g3 + f5g2 + f6g1 + f7g0 + + f8g9_19 + f9g8_19; + int64_t h8 = f0g8 + f1g7_2 + f2g6 + f3g5_2 + f4g4 + f5g3_2 + f6g2 + f7g1_2 + + f8g0 + f9g9_38; + int64_t h9 = + f0g9 + f1g8 + f2g7 + f3g6 + f4g5 + f5g4 + f6g3 + f7g2 + f8g1 + f9g0; + + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + /* + |h0| <= (1.65*1.65*2^52*(1+19+19+19+19)+1.65*1.65*2^50*(38+38+38+38+38)) + i.e. |h0| <= 1.4*2^60; narrower ranges for h2, h4, h6, h8 + |h1| <= (1.65*1.65*2^51*(1+1+19+19+19+19+19+19+19+19)) + i.e. |h1| <= 1.7*2^59; narrower ranges for h3, h5, h7, h9 + */ + + carry0 = (h0 + (int64_t)(1L << 25)) >> 26; + h1 += carry0; + h0 -= carry0 * ((uint64_t) 1L << 26); + carry4 = (h4 + (int64_t)(1L << 25)) >> 26; + h5 += carry4; + h4 -= carry4 * ((uint64_t) 1L << 26); + /* |h0| <= 2^25 */ + /* |h4| <= 2^25 */ + /* |h1| <= 1.71*2^59 */ + /* |h5| <= 1.71*2^59 */ + + carry1 = (h1 + (int64_t)(1L << 24)) >> 25; + h2 += carry1; + h1 -= carry1 * ((uint64_t) 1L << 25); + carry5 = (h5 + (int64_t)(1L << 24)) >> 25; + h6 += carry5; + h5 -= carry5 * ((uint64_t) 1L << 25); + /* |h1| <= 2^24; from now on fits into int32 */ + /* |h5| <= 2^24; from now on fits into int32 */ + /* |h2| <= 1.41*2^60 */ + /* |h6| <= 1.41*2^60 */ + + carry2 = (h2 + (int64_t)(1L << 25)) >> 26; + h3 += carry2; + h2 -= carry2 * ((uint64_t) 1L << 26); + carry6 = (h6 + (int64_t)(1L << 25)) >> 26; + h7 += carry6; + h6 -= carry6 * ((uint64_t) 1L << 26); + /* |h2| <= 2^25; from now on fits into int32 unchanged */ + /* |h6| <= 2^25; from now on fits into int32 unchanged */ + /* |h3| <= 1.71*2^59 */ + /* |h7| <= 1.71*2^59 */ + + carry3 = (h3 + (int64_t)(1L << 24)) >> 25; + h4 += carry3; + h3 -= carry3 * ((uint64_t) 1L << 25); + carry7 = (h7 + (int64_t)(1L << 24)) >> 25; + h8 += carry7; + h7 -= carry7 * ((uint64_t) 1L << 25); + /* |h3| <= 2^24; from now on fits into int32 unchanged */ + /* |h7| <= 2^24; from now on fits into int32 unchanged */ + /* |h4| <= 1.72*2^34 */ + /* |h8| <= 1.41*2^60 */ + + carry4 = (h4 + (int64_t)(1L << 25)) >> 26; + h5 += carry4; + h4 -= carry4 * ((uint64_t) 1L << 26); + carry8 = (h8 + (int64_t)(1L << 25)) >> 26; + h9 += carry8; + h8 -= carry8 * ((uint64_t) 1L << 26); + /* |h4| <= 2^25; from now on fits into int32 unchanged */ + /* |h8| <= 2^25; from now on fits into int32 unchanged */ + /* |h5| <= 1.01*2^24 */ + /* |h9| <= 1.71*2^59 */ + + carry9 = (h9 + (int64_t)(1L << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 * ((uint64_t) 1L << 25); + /* |h9| <= 2^24; from now on fits into int32 unchanged */ + /* |h0| <= 1.1*2^39 */ + + carry0 = (h0 + (int64_t)(1L << 25)) >> 26; + h1 += carry0; + h0 -= carry0 * ((uint64_t) 1L << 26); + /* |h0| <= 2^25; from now on fits into int32 unchanged */ + /* |h1| <= 1.01*2^24 */ + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + +/* + h = f * f + Can overlap h with f. + * + Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + * + Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. + */ + +/*static*/ void +fe25519_sq(fe25519 h, const fe25519 f) +{ + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry0 = (h0 + (int64_t)(1L << 25)) >> 26; + h1 += carry0; + h0 -= carry0 * ((uint64_t) 1L << 26); + carry4 = (h4 + (int64_t)(1L << 25)) >> 26; + h5 += carry4; + h4 -= carry4 * ((uint64_t) 1L << 26); + + carry1 = (h1 + (int64_t)(1L << 24)) >> 25; + h2 += carry1; + h1 -= carry1 * ((uint64_t) 1L << 25); + carry5 = (h5 + (int64_t)(1L << 24)) >> 25; + h6 += carry5; + h5 -= carry5 * ((uint64_t) 1L << 25); + + carry2 = (h2 + (int64_t)(1L << 25)) >> 26; + h3 += carry2; + h2 -= carry2 * ((uint64_t) 1L << 26); + carry6 = (h6 + (int64_t)(1L << 25)) >> 26; + h7 += carry6; + h6 -= carry6 * ((uint64_t) 1L << 26); + + carry3 = (h3 + (int64_t)(1L << 24)) >> 25; + h4 += carry3; + h3 -= carry3 * ((uint64_t) 1L << 25); + carry7 = (h7 + (int64_t)(1L << 24)) >> 25; + h8 += carry7; + h7 -= carry7 * ((uint64_t) 1L << 25); + + carry4 = (h4 + (int64_t)(1L << 25)) >> 26; + h5 += carry4; + h4 -= carry4 * ((uint64_t) 1L << 26); + carry8 = (h8 + (int64_t)(1L << 25)) >> 26; + h9 += carry8; + h8 -= carry8 * ((uint64_t) 1L << 26); + + carry9 = (h9 + (int64_t)(1L << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 * ((uint64_t) 1L << 25); + + carry0 = (h0 + (int64_t)(1L << 25)) >> 26; + h1 += carry0; + h0 -= carry0 * ((uint64_t) 1L << 26); + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + +/* + h = 2 * f * f + Can overlap h with f. + * + Preconditions: + |f| bounded by 1.65*2^26,1.65*2^25,1.65*2^26,1.65*2^25,etc. + * + Postconditions: + |h| bounded by 1.01*2^25,1.01*2^24,1.01*2^25,1.01*2^24,etc. + */ + +/*static*/ void +fe25519_sq2(fe25519 h, const fe25519 f) +{ + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + + int32_t f0_2 = 2 * f0; + int32_t f1_2 = 2 * f1; + int32_t f2_2 = 2 * f2; + int32_t f3_2 = 2 * f3; + int32_t f4_2 = 2 * f4; + int32_t f5_2 = 2 * f5; + int32_t f6_2 = 2 * f6; + int32_t f7_2 = 2 * f7; + int32_t f5_38 = 38 * f5; /* 1.959375*2^30 */ + int32_t f6_19 = 19 * f6; /* 1.959375*2^30 */ + int32_t f7_38 = 38 * f7; /* 1.959375*2^30 */ + int32_t f8_19 = 19 * f8; /* 1.959375*2^30 */ + int32_t f9_38 = 38 * f9; /* 1.959375*2^30 */ + + int64_t f0f0 = f0 * (int64_t) f0; + int64_t f0f1_2 = f0_2 * (int64_t) f1; + int64_t f0f2_2 = f0_2 * (int64_t) f2; + int64_t f0f3_2 = f0_2 * (int64_t) f3; + int64_t f0f4_2 = f0_2 * (int64_t) f4; + int64_t f0f5_2 = f0_2 * (int64_t) f5; + int64_t f0f6_2 = f0_2 * (int64_t) f6; + int64_t f0f7_2 = f0_2 * (int64_t) f7; + int64_t f0f8_2 = f0_2 * (int64_t) f8; + int64_t f0f9_2 = f0_2 * (int64_t) f9; + int64_t f1f1_2 = f1_2 * (int64_t) f1; + int64_t f1f2_2 = f1_2 * (int64_t) f2; + int64_t f1f3_4 = f1_2 * (int64_t) f3_2; + int64_t f1f4_2 = f1_2 * (int64_t) f4; + int64_t f1f5_4 = f1_2 * (int64_t) f5_2; + int64_t f1f6_2 = f1_2 * (int64_t) f6; + int64_t f1f7_4 = f1_2 * (int64_t) f7_2; + int64_t f1f8_2 = f1_2 * (int64_t) f8; + int64_t f1f9_76 = f1_2 * (int64_t) f9_38; + int64_t f2f2 = f2 * (int64_t) f2; + int64_t f2f3_2 = f2_2 * (int64_t) f3; + int64_t f2f4_2 = f2_2 * (int64_t) f4; + int64_t f2f5_2 = f2_2 * (int64_t) f5; + int64_t f2f6_2 = f2_2 * (int64_t) f6; + int64_t f2f7_2 = f2_2 * (int64_t) f7; + int64_t f2f8_38 = f2_2 * (int64_t) f8_19; + int64_t f2f9_38 = f2 * (int64_t) f9_38; + int64_t f3f3_2 = f3_2 * (int64_t) f3; + int64_t f3f4_2 = f3_2 * (int64_t) f4; + int64_t f3f5_4 = f3_2 * (int64_t) f5_2; + int64_t f3f6_2 = f3_2 * (int64_t) f6; + int64_t f3f7_76 = f3_2 * (int64_t) f7_38; + int64_t f3f8_38 = f3_2 * (int64_t) f8_19; + int64_t f3f9_76 = f3_2 * (int64_t) f9_38; + int64_t f4f4 = f4 * (int64_t) f4; + int64_t f4f5_2 = f4_2 * (int64_t) f5; + int64_t f4f6_38 = f4_2 * (int64_t) f6_19; + int64_t f4f7_38 = f4 * (int64_t) f7_38; + int64_t f4f8_38 = f4_2 * (int64_t) f8_19; + int64_t f4f9_38 = f4 * (int64_t) f9_38; + int64_t f5f5_38 = f5 * (int64_t) f5_38; + int64_t f5f6_38 = f5_2 * (int64_t) f6_19; + int64_t f5f7_76 = f5_2 * (int64_t) f7_38; + int64_t f5f8_38 = f5_2 * (int64_t) f8_19; + int64_t f5f9_76 = f5_2 * (int64_t) f9_38; + int64_t f6f6_19 = f6 * (int64_t) f6_19; + int64_t f6f7_38 = f6 * (int64_t) f7_38; + int64_t f6f8_38 = f6_2 * (int64_t) f8_19; + int64_t f6f9_38 = f6 * (int64_t) f9_38; + int64_t f7f7_38 = f7 * (int64_t) f7_38; + int64_t f7f8_38 = f7_2 * (int64_t) f8_19; + int64_t f7f9_76 = f7_2 * (int64_t) f9_38; + int64_t f8f8_19 = f8 * (int64_t) f8_19; + int64_t f8f9_38 = f8 * (int64_t) f9_38; + int64_t f9f9_38 = f9 * (int64_t) f9_38; + + int64_t h0 = f0f0 + f1f9_76 + f2f8_38 + f3f7_76 + f4f6_38 + f5f5_38; + int64_t h1 = f0f1_2 + f2f9_38 + f3f8_38 + f4f7_38 + f5f6_38; + int64_t h2 = f0f2_2 + f1f1_2 + f3f9_76 + f4f8_38 + f5f7_76 + f6f6_19; + int64_t h3 = f0f3_2 + f1f2_2 + f4f9_38 + f5f8_38 + f6f7_38; + int64_t h4 = f0f4_2 + f1f3_4 + f2f2 + f5f9_76 + f6f8_38 + f7f7_38; + int64_t h5 = f0f5_2 + f1f4_2 + f2f3_2 + f6f9_38 + f7f8_38; + int64_t h6 = f0f6_2 + f1f5_4 + f2f4_2 + f3f3_2 + f7f9_76 + f8f8_19; + int64_t h7 = f0f7_2 + f1f6_2 + f2f5_2 + f3f4_2 + f8f9_38; + int64_t h8 = f0f8_2 + f1f7_4 + f2f6_2 + f3f5_4 + f4f4 + f9f9_38; + int64_t h9 = f0f9_2 + f1f8_2 + f2f7_2 + f3f6_2 + f4f5_2; + + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + h0 += h0; + h1 += h1; + h2 += h2; + h3 += h3; + h4 += h4; + h5 += h5; + h6 += h6; + h7 += h7; + h8 += h8; + h9 += h9; + + carry0 = (h0 + (int64_t)(1L << 25)) >> 26; + h1 += carry0; + h0 -= carry0 * ((uint64_t) 1L << 26); + carry4 = (h4 + (int64_t)(1L << 25)) >> 26; + h5 += carry4; + h4 -= carry4 * ((uint64_t) 1L << 26); + + carry1 = (h1 + (int64_t)(1L << 24)) >> 25; + h2 += carry1; + h1 -= carry1 * ((uint64_t) 1L << 25); + carry5 = (h5 + (int64_t)(1L << 24)) >> 25; + h6 += carry5; + h5 -= carry5 * ((uint64_t) 1L << 25); + + carry2 = (h2 + (int64_t)(1L << 25)) >> 26; + h3 += carry2; + h2 -= carry2 * ((uint64_t) 1L << 26); + carry6 = (h6 + (int64_t)(1L << 25)) >> 26; + h7 += carry6; + h6 -= carry6 * ((uint64_t) 1L << 26); + + carry3 = (h3 + (int64_t)(1L << 24)) >> 25; + h4 += carry3; + h3 -= carry3 * ((uint64_t) 1L << 25); + carry7 = (h7 + (int64_t)(1L << 24)) >> 25; + h8 += carry7; + h7 -= carry7 * ((uint64_t) 1L << 25); + + carry4 = (h4 + (int64_t)(1L << 25)) >> 26; + h5 += carry4; + h4 -= carry4 * ((uint64_t) 1L << 26); + carry8 = (h8 + (int64_t)(1L << 25)) >> 26; + h9 += carry8; + h8 -= carry8 * ((uint64_t) 1L << 26); + + carry9 = (h9 + (int64_t)(1L << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 * ((uint64_t) 1L << 25); + + carry0 = (h0 + (int64_t)(1L << 25)) >> 26; + h1 += carry0; + h0 -= carry0 * ((uint64_t) 1L << 26); + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + +/*static*/ void +fe25519_scalar_product(fe25519 h, const fe25519 f, uint32_t n) +{ + int64_t sn = (int64_t) n; + int32_t f0 = f[0]; + int32_t f1 = f[1]; + int32_t f2 = f[2]; + int32_t f3 = f[3]; + int32_t f4 = f[4]; + int32_t f5 = f[5]; + int32_t f6 = f[6]; + int32_t f7 = f[7]; + int32_t f8 = f[8]; + int32_t f9 = f[9]; + int64_t h0 = f0 * sn; + int64_t h1 = f1 * sn; + int64_t h2 = f2 * sn; + int64_t h3 = f3 * sn; + int64_t h4 = f4 * sn; + int64_t h5 = f5 * sn; + int64_t h6 = f6 * sn; + int64_t h7 = f7 * sn; + int64_t h8 = f8 * sn; + int64_t h9 = f9 * sn; + int64_t carry0, carry1, carry2, carry3, carry4, carry5, carry6, carry7, + carry8, carry9; + + carry9 = (h9 + ((int64_t) 1 << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 * ((int64_t) 1 << 25); + carry1 = (h1 + ((int64_t) 1 << 24)) >> 25; + h2 += carry1; + h1 -= carry1 * ((int64_t) 1 << 25); + carry3 = (h3 + ((int64_t) 1 << 24)) >> 25; + h4 += carry3; + h3 -= carry3 * ((int64_t) 1 << 25); + carry5 = (h5 + ((int64_t) 1 << 24)) >> 25; + h6 += carry5; + h5 -= carry5 * ((int64_t) 1 << 25); + carry7 = (h7 + ((int64_t) 1 << 24)) >> 25; + h8 += carry7; + h7 -= carry7 * ((int64_t) 1 << 25); + + carry0 = (h0 + ((int64_t) 1 << 25)) >> 26; + h1 += carry0; + h0 -= carry0 * ((int64_t) 1 << 26); + carry2 = (h2 + ((int64_t) 1 << 25)) >> 26; + h3 += carry2; + h2 -= carry2 * ((int64_t) 1 << 26); + carry4 = (h4 + ((int64_t) 1 << 25)) >> 26; + h5 += carry4; + h4 -= carry4 * ((int64_t) 1 << 26); + carry6 = (h6 + ((int64_t) 1 << 25)) >> 26; + h7 += carry6; + h6 -= carry6 * ((int64_t) 1 << 26); + carry8 = (h8 + ((int64_t) 1 << 25)) >> 26; + h9 += carry8; + h8 -= carry8 * ((int64_t) 1 << 26); + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + +} + +#endif \ No newline at end of file diff --git a/contrib/sodium/fe_25_5/base.h b/contrib/sodium/fe_25_5/base.h new file mode 100644 index 000000000..16e11fc52 --- /dev/null +++ b/contrib/sodium/fe_25_5/base.h @@ -0,0 +1,1367 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +{ /* 0/31 */ + { + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 } + }, + { + { -12815894, -12976347, -21581243, 11784320, -25355658, -2750717, -11717903, -3814571, -358445, -10211303 }, + { -21703237, 6903825, 27185491, 6451973, -29577724, -9554005, -15616551, 11189268, -26829678, -5319081 }, + { 26966642, 11152617, 32442495, 15396054, 14353839, -12752335, -3128826, -9541118, -15472047, -4166697 } + }, + { + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 } + }, + { + { -17036878, 13921892, 10945806, -6033431, 27105052, -16084379, -28926210, 15006023, 3284568, -6276540 }, + { 23599295, -8306047, -11193664, -7687416, 13236774, 10506355, 7464579, 9656445, 13059162, 10374397 }, + { 7798556, 16710257, 3033922, 2874086, 28997861, 2835604, 32406664, -3839045, -641708, -101325 } + }, + { + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 } + }, + { + { -15371964, -12862754, 32573250, 4720197, -26436522, 5875511, -19188627, -15224819, -9818940, -12085777 }, + { -8549212, 109983, 15149363, 2178705, 22900618, 4543417, 3044240, -15689887, 1762328, 14866737 }, + { -18199695, -15951423, -10473290, 1707278, -17185920, 3916101, -28236412, 3959421, 27914454, 4383652 } + }, + { + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 } + }, + { + { 14499471, -2729599, -33191113, -4254652, 28494862, 14271267, 30290735, 10876454, -33154098, 2381726 }, + { -7195431, -2655363, -14730155, 462251, -27724326, 3941372, -6236617, 3696005, -32300832, 15351955 }, + { 27431194, 8222322, 16448760, -3907995, -18707002, 11938355, -32961401, -2970515, 29551813, 10109425 } + } +}, +{ /* 1/31 */ + { + { -13657040, -13155431, -31283750, 11777098, 21447386, 6519384, -2378284, -1627556, 10092783, -4764171 }, + { 27939166, 14210322, 4677035, 16277044, -22964462, -12398139, -32508754, 12005538, -17810127, 12803510 }, + { 17228999, -15661624, -1233527, 300140, -1224870, -11714777, 30364213, -9038194, 18016357, 4397660 } + }, + { + { -10958843, -7690207, 4776341, -14954238, 27850028, -15602212, -26619106, 14544525, -17477504, 982639 }, + { 29253598, 15796703, -2863982, -9908884, 10057023, 3163536, 7332899, -4120128, -21047696, 9934963 }, + { 5793303, 16271923, -24131614, -10116404, 29188560, 1206517, -14747930, 4559895, -30123922, -10897950 } + }, + { + { -27643952, -11493006, 16282657, -11036493, 28414021, -15012264, 24191034, 4541697, -13338309, 5500568 }, + { 12650548, -1497113, 9052871, 11355358, -17680037, -8400164, -17430592, 12264343, 10874051, 13524335 }, + { 25556948, -3045990, 714651, 2510400, 23394682, -10415330, 33119038, 5080568, -22528059, 5376628 } + }, + { + { -26088264, -4011052, -17013699, -3537628, -6726793, 1920897, -22321305, -9447443, 4535768, 1569007 }, + { -2255422, 14606630, -21692440, -8039818, 28430649, 8775819, -30494562, 3044290, 31848280, 12543772 }, + { -22028579, 2943893, -31857513, 6777306, 13784462, -4292203, -27377195, -2062731, 7718482, 14474653 } + }, + { + { 2385315, 2454213, -22631320, 46603, -4437935, -15680415, 656965, -7236665, 24316168, -5253567 }, + { 13741529, 10911568, -33233417, -8603737, -20177830, -1033297, 33040651, -13424532, -20729456, 8321686 }, + { 21060490, -2212744, 15712757, -4336099, 1639040, 10656336, 23845965, -11874838, -9984458, 608372 } + }, + { + { -13672732, -15087586, -10889693, -7557059, -6036909, 11305547, 1123968, -6780577, 27229399, 23887 }, + { -23244140, -294205, -11744728, 14712571, -29465699, -2029617, 12797024, -6440308, -1633405, 16678954 }, + { -29500620, 4770662, -16054387, 14001338, 7830047, 9564805, -1508144, -4795045, -17169265, 4904953 } + }, + { + { 24059557, 14617003, 19037157, -15039908, 19766093, -14906429, 5169211, 16191880, 2128236, -4326833 }, + { -16981152, 4124966, -8540610, -10653797, 30336522, -14105247, -29806336, 916033, -6882542, -2986532 }, + { -22630907, 12419372, -7134229, -7473371, -16478904, 16739175, 285431, 2763829, 15736322, 4143876 } + }, + { + { 2379352, 11839345, -4110402, -5988665, 11274298, 794957, 212801, -14594663, 23527084, -16458268 }, + { 33431127, -11130478, -17838966, -15626900, 8909499, 8376530, -32625340, 4087881, -15188911, -14416214 }, + { 1767683, 7197987, -13205226, -2022635, -13091350, 448826, 5799055, 4357868, -4774191, -16323038 } + } +}, +{ /* 2/31 */ + { + { 6721966, 13833823, -23523388, -1551314, 26354293, -11863321, 23365147, -3949732, 7390890, 2759800 }, + { 4409041, 2052381, 23373853, 10530217, 7676779, -12885954, 21302353, -4264057, 1244380, -12919645 }, + { -4421239, 7169619, 4982368, -2957590, 30256825, -2777540, 14086413, 9208236, 15886429, 16489664 } + }, + { + { 1996075, 10375649, 14346367, 13311202, -6874135, -16438411, -13693198, 398369, -30606455, -712933 }, + { -25307465, 9795880, -2777414, 14878809, -33531835, 14780363, 13348553, 12076947, -30836462, 5113182 }, + { -17770784, 11797796, 31950843, 13929123, -25888302, 12288344, -30341101, -7336386, 13847711, 5387222 } + }, + { + { -18582163, -3416217, 17824843, -2340966, 22744343, -10442611, 8763061, 3617786, -19600662, 10370991 }, + { 20246567, -14369378, 22358229, -543712, 18507283, -10413996, 14554437, -8746092, 32232924, 16763880 }, + { 9648505, 10094563, 26416693, 14745928, -30374318, -6472621, 11094161, 15689506, 3140038, -16510092 } + }, + { + { -16160072, 5472695, 31895588, 4744994, 8823515, 10365685, -27224800, 9448613, -28774454, 366295 }, + { 19153450, 11523972, -11096490, -6503142, -24647631, 5420647, 28344573, 8041113, 719605, 11671788 }, + { 8678025, 2694440, -6808014, 2517372, 4964326, 11152271, -15432916, -15266516, 27000813, -10195553 } + }, + { + { -15157904, 7134312, 8639287, -2814877, -7235688, 10421742, 564065, 5336097, 6750977, -14521026 }, + { 11836410, -3979488, 26297894, 16080799, 23455045, 15735944, 1695823, -8819122, 8169720, 16220347 }, + { -18115838, 8653647, 17578566, -6092619, -8025777, -16012763, -11144307, -2627664, -5990708, -14166033 } + }, + { + { -23308498, -10968312, 15213228, -10081214, -30853605, -11050004, 27884329, 2847284, 2655861, 1738395 }, + { -27537433, -14253021, -25336301, -8002780, -9370762, 8129821, 21651608, -3239336, -19087449, -11005278 }, + { 1533110, 3437855, 23735889, 459276, 29970501, 11335377, 26030092, 5821408, 10478196, 8544890 } + }, + { + { 32173121, -16129311, 24896207, 3921497, 22579056, -3410854, 19270449, 12217473, 17789017, -3395995 }, + { -30552961, -2228401, -15578829, -10147201, 13243889, 517024, 15479401, -3853233, 30460520, 1052596 }, + { -11614875, 13323618, 32618793, 8175907, -15230173, 12596687, 27491595, -4612359, 3179268, -9478891 } + }, + { + { 31947069, -14366651, -4640583, -15339921, -15125977, -6039709, -14756777, -16411740, 19072640, -9511060 }, + { 11685058, 11822410, 3158003, -13952594, 33402194, -4165066, 5977896, -5215017, 473099, 5040608 }, + { -20290863, 8198642, -27410132, 11602123, 1290375, -2799760, 28326862, 1721092, -19558642, -3131606 } + } +}, +{ /* 3/31 */ + { + { 7881532, 10687937, 7578723, 7738378, -18951012, -2553952, 21820786, 8076149, -27868496, 11538389 }, + { -19935666, 3899861, 18283497, -6801568, -15728660, -11249211, 8754525, 7446702, -5676054, 5797016 }, + { -11295600, -3793569, -15782110, -7964573, 12708869, -8456199, 2014099, -9050574, -2369172, -5877341 } + }, + { + { -22472376, -11568741, -27682020, 1146375, 18956691, 16640559, 1192730, -3714199, 15123619, 10811505 }, + { 14352098, -3419715, -18942044, 10822655, 32750596, 4699007, -70363, 15776356, -28886779, -11974553 }, + { -28241164, -8072475, -4978962, -5315317, 29416931, 1847569, -20654173, -16484855, 4714547, -9600655 } + }, + { + { 15200332, 8368572, 19679101, 15970074, -31872674, 1959451, 24611599, -4543832, -11745876, 12340220 }, + { 12876937, -10480056, 33134381, 6590940, -6307776, 14872440, 9613953, 8241152, 15370987, 9608631 }, + { -4143277, -12014408, 8446281, -391603, 4407738, 13629032, -7724868, 15866074, -28210621, -8814099 } + }, + { + { 26660628, -15677655, 8393734, 358047, -7401291, 992988, -23904233, 858697, 20571223, 8420556 }, + { 14620715, 13067227, -15447274, 8264467, 14106269, 15080814, 33531827, 12516406, -21574435, -12476749 }, + { 236881, 10476226, 57258, -14677024, 6472998, 2466984, 17258519, 7256740, 8791136, 15069930 } + }, + { + { 1276410, -9371918, 22949635, -16322807, -23493039, -5702186, 14711875, 4874229, -30663140, -2331391 }, + { 5855666, 4990204, -13711848, 7294284, -7804282, 1924647, -1423175, -7912378, -33069337, 9234253 }, + { 20590503, -9018988, 31529744, -7352666, -2706834, 10650548, 31559055, -11609587, 18979186, 13396066 } + }, + { + { 24474287, 4968103, 22267082, 4407354, 24063882, -8325180, -18816887, 13594782, 33514650, 7021958 }, + { -11566906, -6565505, -21365085, 15928892, -26158305, 4315421, -25948728, -3916677, -21480480, 12868082 }, + { -28635013, 13504661, 19988037, -2132761, 21078225, 6443208, -21446107, 2244500, -12455797, -8089383 } + }, + { + { -30595528, 13793479, -5852820, 319136, -25723172, -6263899, 33086546, 8957937, -15233648, 5540521 }, + { -11630176, -11503902, -8119500, -7643073, 2620056, 1022908, -23710744, -1568984, -16128528, -14962807 }, + { 23152971, 775386, 27395463, 14006635, -9701118, 4649512, 1689819, 892185, -11513277, -15205948 } + }, + { + { 9770129, 9586738, 26496094, 4324120, 1556511, -3550024, 27453819, 4763127, -19179614, 5867134 }, + { -32765025, 1927590, 31726409, -4753295, 23962434, -16019500, 27846559, 5931263, -29749703, -16108455 }, + { 27461885, -2977536, 22380810, 1815854, -23033753, -3031938, 7283490, -15148073, -19526700, 7734629 } + } +}, +{ /* 4/31 */ + { + { -8010264, -9590817, -11120403, 6196038, 29344158, -13430885, 7585295, -3176626, 18549497, 15302069 }, + { -32658337, -6171222, -7672793, -11051681, 6258878, 13504381, 10458790, -6418461, -8872242, 8424746 }, + { 24687205, 8613276, -30667046, -3233545, 1863892, -1830544, 19206234, 7134917, -11284482, -828919 } + }, + { + { 11334899, -9218022, 8025293, 12707519, 17523892, -10476071, 10243738, -14685461, -5066034, 16498837 }, + { 8911542, 6887158, -9584260, -6958590, 11145641, -9543680, 17303925, -14124238, 6536641, 10543906 }, + { -28946384, 15479763, -17466835, 568876, -1497683, 11223454, -2669190, -16625574, -27235709, 8876771 } + }, + { + { -25742899, -12566864, -15649966, -846607, -33026686, -796288, -33481822, 15824474, -604426, -9039817 }, + { 10330056, 70051, 7957388, -9002667, 9764902, 15609756, 27698697, -4890037, 1657394, 3084098 }, + { 10477963, -7470260, 12119566, -13250805, 29016247, -5365589, 31280319, 14396151, -30233575, 15272409 } + }, + { + { -12288309, 3169463, 28813183, 16658753, 25116432, -5630466, -25173957, -12636138, -25014757, 1950504 }, + { -26180358, 9489187, 11053416, -14746161, -31053720, 5825630, -8384306, -8767532, 15341279, 8373727 }, + { 28685821, 7759505, -14378516, -12002860, -31971820, 4079242, 298136, -10232602, -2878207, 15190420 } + }, + { + { -32932876, 13806336, -14337485, -15794431, -24004620, 10940928, 8669718, 2742393, -26033313, -6875003 }, + { -1580388, -11729417, -25979658, -11445023, -17411874, -10912854, 9291594, -16247779, -12154742, 6048605 }, + { -30305315, 14843444, 1539301, 11864366, 20201677, 1900163, 13934231, 5128323, 11213262, 9168384 } + }, + { + { -26280513, 11007847, 19408960, -940758, -18592965, -4328580, -5088060, -11105150, 20470157, -16398701 }, + { -23136053, 9282192, 14855179, -15390078, -7362815, -14408560, -22783952, 14461608, 14042978, 5230683 }, + { 29969567, -2741594, -16711867, -8552442, 9175486, -2468974, 21556951, 3506042, -5933891, -12449708 } + }, + { + { -3144746, 8744661, 19704003, 4581278, -20430686, 6830683, -21284170, 8971513, -28539189, 15326563 }, + { -19464629, 10110288, -17262528, -3503892, -23500387, 1355669, -15523050, 15300988, -20514118, 9168260 }, + { -5353335, 4488613, -23803248, 16314347, 7780487, -15638939, -28948358, 9601605, 33087103, -9011387 } + }, + { + { -19443170, -15512900, -20797467, -12445323, -29824447, 10229461, -27444329, -15000531, -5996870, 15664672 }, + { 23294591, -16632613, -22650781, -8470978, 27844204, 11461195, 13099750, -2460356, 18151676, 13417686 }, + { -24722913, -4176517, -31150679, 5988919, -26858785, 6685065, 1661597, -12551441, 15271676, -15452665 } + } +}, +{ /* 5/31 */ + { + { 11433042, -13228665, 8239631, -5279517, -1985436, -725718, -18698764, 2167544, -6921301, -13440182 }, + { -31436171, 15575146, 30436815, 12192228, -22463353, 9395379, -9917708, -8638997, 12215110, 12028277 }, + { 14098400, 6555944, 23007258, 5757252, -15427832, -12950502, 30123440, 4617780, -16900089, -655628 } + }, + { + { -4026201, -15240835, 11893168, 13718664, -14809462, 1847385, -15819999, 10154009, 23973261, -12684474 }, + { -26531820, -3695990, -1908898, 2534301, -31870557, -16550355, 18341390, -11419951, 32013174, -10103539 }, + { -25479301, 10876443, -11771086, -14625140, -12369567, 1838104, 21911214, 6354752, 4425632, -837822 } + }, + { + { -10433389, -14612966, 22229858, -3091047, -13191166, 776729, -17415375, -12020462, 4725005, 14044970 }, + { 19268650, -7304421, 1555349, 8692754, -21474059, -9910664, 6347390, -1411784, -19522291, -16109756 }, + { -24864089, 12986008, -10898878, -5558584, -11312371, -148526, 19541418, 8180106, 9282262, 10282508 } + }, + { + { -26205082, 4428547, -8661196, -13194263, 4098402, -14165257, 15522535, 8372215, 5542595, -10702683 }, + { -10562541, 14895633, 26814552, -16673850, -17480754, -2489360, -2781891, 6993761, -18093885, 10114655 }, + { -20107055, -929418, 31422704, 10427861, -7110749, 6150669, -29091755, -11529146, 25953725, -106158 } + }, + { + { -4234397, -8039292, -9119125, 3046000, 2101609, -12607294, 19390020, 6094296, -3315279, 12831125 }, + { -15998678, 7578152, 5310217, 14408357, -33548620, -224739, 31575954, 6326196, 7381791, -2421839 }, + { -20902779, 3296811, 24736065, -16328389, 18374254, 7318640, 6295303, 8082724, -15362489, 12339664 } + }, + { + { 27724736, 2291157, 6088201, -14184798, 1792727, 5857634, 13848414, 15768922, 25091167, 14856294 }, + { -18866652, 8331043, 24373479, 8541013, -701998, -9269457, 12927300, -12695493, -22182473, -9012899 }, + { -11423429, -5421590, 11632845, 3405020, 30536730, -11674039, -27260765, 13866390, 30146206, 9142070 } + }, + { + { 3924129, -15307516, -13817122, -10054960, 12291820, -668366, -27702774, 9326384, -8237858, 4171294 }, + { -15921940, 16037937, 6713787, 16606682, -21612135, 2790944, 26396185, 3731949, 345228, -5462949 }, + { -21327538, 13448259, 25284571, 1143661, 20614966, -8849387, 2031539, -12391231, -16253183, -13582083 } + }, + { + { 31016211, -16722429, 26371392, -14451233, -5027349, 14854137, 17477601, 3842657, 28012650, -16405420 }, + { -5075835, 9368966, -8562079, -4600902, -15249953, 6970560, -9189873, 16292057, -8867157, 3507940 }, + { 29439664, 3537914, 23333589, 6997794, -17555561, -11018068, -15209202, -15051267, -9164929, 6580396 } + } +}, +{ /* 6/31 */ + { + { -12185861, -7679788, 16438269, 10826160, -8696817, -6235611, 17860444, -9273846, -2095802, 9304567 }, + { 20714564, -4336911, 29088195, 7406487, 11426967, -5095705, 14792667, -14608617, 5289421, -477127 }, + { -16665533, -10650790, -6160345, -13305760, 9192020, -1802462, 17271490, 12349094, 26939669, -3752294 } + }, + { + { -12889898, 9373458, 31595848, 16374215, 21471720, 13221525, -27283495, -12348559, -3698806, 117887 }, + { 22263325, -6560050, 3984570, -11174646, -15114008, -566785, 28311253, 5358056, -23319780, 541964 }, + { 16259219, 3261970, 2309254, -15534474, -16885711, -4581916, 24134070, -16705829, -13337066, -13552195 } + }, + { + { 9378160, -13140186, -22845982, -12745264, 28198281, -7244098, -2399684, -717351, 690426, 14876244 }, + { 24977353, -314384, -8223969, -13465086, 28432343, -1176353, -13068804, -12297348, -22380984, 6618999 }, + { -1538174, 11685646, 12944378, 13682314, -24389511, -14413193, 8044829, -13817328, 32239829, -5652762 } + }, + { + { -18603066, 4762990, -926250, 8885304, -28412480, -3187315, 9781647, -10350059, 32779359, 5095274 }, + { -33008130, -5214506, -32264887, -3685216, 9460461, -9327423, -24601656, 14506724, 21639561, -2630236 }, + { -16400943, -13112215, 25239338, 15531969, 3987758, -4499318, -1289502, -6863535, 17874574, 558605 } + }, + { + { -13600129, 10240081, 9171883, 16131053, -20869254, 9599700, 33499487, 5080151, 2085892, 5119761 }, + { -22205145, -2519528, -16381601, 414691, -25019550, 2170430, 30634760, -8363614, -31999993, -5759884 }, + { -6845704, 15791202, 8550074, -1312654, 29928809, -12092256, 27534430, -7192145, -22351378, 12961482 } + }, + { + { -24492060, -9570771, 10368194, 11582341, -23397293, -2245287, 16533930, 8206996, -30194652, -5159638 }, + { -11121496, -3382234, 2307366, 6362031, -135455, 8868177, -16835630, 7031275, 7589640, 8945490 }, + { -32152748, 8917967, 6661220, -11677616, -1192060, -15793393, 7251489, -11182180, 24099109, -14456170 } + }, + { + { 5019558, -7907470, 4244127, -14714356, -26933272, 6453165, -19118182, -13289025, -6231896, -10280736 }, + { 10853594, 10721687, 26480089, 5861829, -22995819, 1972175, -1866647, -10557898, -3363451, -6441124 }, + { -17002408, 5906790, 221599, -6563147, 7828208, -13248918, 24362661, -2008168, -13866408, 7421392 } + }, + { + { 8139927, -6546497, 32257646, -5890546, 30375719, 1886181, -21175108, 15441252, 28826358, -4123029 }, + { 6267086, 9695052, 7709135, -16603597, -32869068, -1886135, 14795160, -7840124, 13746021, -1742048 }, + { 28584902, 7787108, -6732942, -15050729, 22846041, -7571236, -3181936, -363524, 4771362, -8419958 } + } +}, +{ /* 7/31 */ + { + { 24949256, 6376279, -27466481, -8174608, -18646154, -9930606, 33543569, -12141695, 3569627, 11342593 }, + { 26514989, 4740088, 27912651, 3697550, 19331575, -11472339, 6809886, 4608608, 7325975, -14801071 }, + { -11618399, -14554430, -24321212, 7655128, -1369274, 5214312, -27400540, 10258390, -17646694, -8186692 } + }, + { + { 11431204, 15823007, 26570245, 14329124, 18029990, 4796082, -31446179, 15580664, 9280358, -3973687 }, + { -160783, -10326257, -22855316, -4304997, -20861367, -13621002, -32810901, -11181622, -15545091, 4387441 }, + { -20799378, 12194512, 3937617, -5805892, -27154820, 9340370, -24513992, 8548137, 20617071, -7482001 } + }, + { + { -938825, -3930586, -8714311, 16124718, 24603125, -6225393, -13775352, -11875822, 24345683, 10325460 }, + { -19855277, -1568885, -22202708, 8714034, 14007766, 6928528, 16318175, -1010689, 4766743, 3552007 }, + { -21751364, -16730916, 1351763, -803421, -4009670, 3950935, 3217514, 14481909, 10988822, -3994762 } + }, + { + { 15564307, -14311570, 3101243, 5684148, 30446780, -8051356, 12677127, -6505343, -8295852, 13296005 }, + { -9442290, 6624296, -30298964, -11913677, -4670981, -2057379, 31521204, 9614054, -30000824, 12074674 }, + { 4771191, -135239, 14290749, -13089852, 27992298, 14998318, -1413936, -1556716, 29832613, -16391035 } + }, + { + { 7064884, -7541174, -19161962, -5067537, -18891269, -2912736, 25825242, 5293297, -27122660, 13101590 }, + { -2298563, 2439670, -7466610, 1719965, -27267541, -16328445, 32512469, -5317593, -30356070, -4190957 }, + { -30006540, 10162316, -33180176, 3981723, -16482138, -13070044, 14413974, 9515896, 19568978, 9628812 } + }, + { + { 33053803, 199357, 15894591, 1583059, 27380243, -4580435, -17838894, -6106839, -6291786, 3437740 }, + { -18978877, 3884493, 19469877, 12726490, 15913552, 13614290, -22961733, 70104, 7463304, 4176122 }, + { -27124001, 10659917, 11482427, -16070381, 12771467, -6635117, -32719404, -5322751, 24216882, 5944158 } + }, + { + { 8894125, 7450974, -2664149, -9765752, -28080517, -12389115, 19345746, 14680796, 11632993, 5847885 }, + { 26942781, -2315317, 9129564, -4906607, 26024105, 11769399, -11518837, 6367194, -9727230, 4782140 }, + { 19916461, -4828410, -22910704, -11414391, 25606324, -5972441, 33253853, 8220911, 6358847, -1873857 } + }, + { + { 801428, -2081702, 16569428, 11065167, 29875704, 96627, 7908388, -4480480, -13538503, 1387155 }, + { 19646058, 5720633, -11416706, 12814209, 11607948, 12749789, 14147075, 15156355, -21866831, 11835260 }, + { 19299512, 1155910, 28703737, 14890794, 2925026, 7269399, 26121523, 15467869, -26560550, 5052483 } + } +}, +{ /* 8/31 */ + { + { -3017432, 10058206, 1980837, 3964243, 22160966, 12322533, -6431123, -12618185, 12228557, -7003677 }, + { 32944382, 14922211, -22844894, 5188528, 21913450, -8719943, 4001465, 13238564, -6114803, 8653815 }, + { 22865569, -4652735, 27603668, -12545395, 14348958, 8234005, 24808405, 5719875, 28483275, 2841751 } + }, + { + { -16420968, -1113305, -327719, -12107856, 21886282, -15552774, -1887966, -315658, 19932058, -12739203 }, + { -11656086, 10087521, -8864888, -5536143, -19278573, -3055912, 3999228, 13239134, -4777469, -13910208 }, + { 1382174, -11694719, 17266790, 9194690, -13324356, 9720081, 20403944, 11284705, -14013818, 3093230 } + }, + { + { 16650921, -11037932, -1064178, 1570629, -8329746, 7352753, -302424, 16271225, -24049421, -6691850 }, + { -21911077, -5927941, -4611316, -5560156, -31744103, -10785293, 24123614, 15193618, -21652117, -16739389 }, + { -9935934, -4289447, -25279823, 4372842, 2087473, 10399484, 31870908, 14690798, 17361620, 11864968 } + }, + { + { -11307610, 6210372, 13206574, 5806320, -29017692, -13967200, -12331205, -7486601, -25578460, -16240689 }, + { 14668462, -12270235, 26039039, 15305210, 25515617, 4542480, 10453892, 6577524, 9145645, -6443880 }, + { 5974874, 3053895, -9433049, -10385191, -31865124, 3225009, -7972642, 3936128, -5652273, -3050304 } + }, + { + { 30625386, -4729400, -25555961, -12792866, -20484575, 7695099, 17097188, -16303496, -27999779, 1803632 }, + { -3553091, 9865099, -5228566, 4272701, -5673832, -16689700, 14911344, 12196514, -21405489, 7047412 }, + { 20093277, 9920966, -11138194, -5343857, 13161587, 12044805, -32856851, 4124601, -32343828, -10257566 } + }, + { + { -20788824, 14084654, -13531713, 7842147, 19119038, -13822605, 4752377, -8714640, -21679658, 2288038 }, + { -26819236, -3283715, 29965059, 3039786, -14473765, 2540457, 29457502, 14625692, -24819617, 12570232 }, + { -1063558, -11551823, 16920318, 12494842, 1278292, -5869109, -21159943, -3498680, -11974704, 4724943 } + }, + { + { 17960970, -11775534, -4140968, -9702530, -8876562, -1410617, -12907383, -8659932, -29576300, 1903856 }, + { 23134274, -14279132, -10681997, -1611936, 20684485, 15770816, -12989750, 3190296, 26955097, 14109738 }, + { 15308788, 5320727, -30113809, -14318877, 22902008, 7767164, 29425325, -11277562, 31960942, 11934971 } + }, + { + { -27395711, 8435796, 4109644, 12222639, -24627868, 14818669, 20638173, 4875028, 10491392, 1379718 }, + { -13159415, 9197841, 3875503, -8936108, -1383712, -5879801, 33518459, 16176658, 21432314, 12180697 }, + { -11787308, 11500838, 13787581, -13832590, -22430679, 10140205, 1465425, 12689540, -10301319, -13872883 } + } +}, +{ /* 9/31 */ + { + { 5414091, -15386041, -21007664, 9643570, 12834970, 1186149, -2622916, -1342231, 26128231, 6032912 }, + { -26337395, -13766162, 32496025, -13653919, 17847801, -12669156, 3604025, 8316894, -25875034, -10437358 }, + { 3296484, 6223048, 24680646, -12246460, -23052020, 5903205, -8862297, -4639164, 12376617, 3188849 } + }, + { + { 29190488, -14659046, 27549113, -1183516, 3520066, -10697301, 32049515, -7309113, -16109234, -9852307 }, + { -14744486, -9309156, 735818, -598978, -20407687, -5057904, 25246078, -15795669, 18640741, -960977 }, + { -6928835, -16430795, 10361374, 5642961, 4910474, 12345252, -31638386, -494430, 10530747, 1053335 } + }, + { + { -29265967, -14186805, -13538216, -12117373, -19457059, -10655384, -31462369, -2948985, 24018831, 15026644 }, + { -22592535, -3145277, -2289276, 5953843, -13440189, 9425631, 25310643, 13003497, -2314791, -15145616 }, + { -27419985, -603321, -8043984, -1669117, -26092265, 13987819, -27297622, 187899, -23166419, -2531735 } + }, + { + { -21744398, -13810475, 1844840, 5021428, -10434399, -15911473, 9716667, 16266922, -5070217, 726099 }, + { 29370922, -6053998, 7334071, -15342259, 9385287, 2247707, -13661962, -4839461, 30007388, -15823341 }, + { -936379, 16086691, 23751945, -543318, -1167538, -5189036, 9137109, 730663, 9835848, 4555336 } + }, + { + { -23376435, 1410446, -22253753, -12899614, 30867635, 15826977, 17693930, 544696, -11985298, 12422646 }, + { 31117226, -12215734, -13502838, 6561947, -9876867, -12757670, -5118685, -4096706, 29120153, 13924425 }, + { -17400879, -14233209, 19675799, -2734756, -11006962, -5858820, -9383939, -11317700, 7240931, -237388 } + }, + { + { -31361739, -11346780, -15007447, -5856218, -22453340, -12152771, 1222336, 4389483, 3293637, -15551743 }, + { -16684801, -14444245, 11038544, 11054958, -13801175, -3338533, -24319580, 7733547, 12796905, -6335822 }, + { -8759414, -10817836, -25418864, 10783769, -30615557, -9746811, -28253339, 3647836, 3222231, -11160462 } + }, + { + { 18606113, 1693100, -25448386, -15170272, 4112353, 10045021, 23603893, -2048234, -7550776, 2484985 }, + { 9255317, -3131197, -12156162, -1004256, 13098013, -9214866, 16377220, -2102812, -19802075, -3034702 }, + { -22729289, 7496160, -5742199, 11329249, 19991973, -3347502, -31718148, 9936966, -30097688, -10618797 } + }, + { + { 21878590, -5001297, 4338336, 13643897, -3036865, 13160960, 19708896, 5415497, -7360503, -4109293 }, + { 27736861, 10103576, 12500508, 8502413, -3413016, -9633558, 10436918, -1550276, -23659143, -8132100 }, + { 19492550, -12104365, -29681976, -852630, -3208171, 12403437, 30066266, 8367329, 13243957, 8709688 } + } +}, +{ /* 10/31 */ + { + { 12015105, 2801261, 28198131, 10151021, 24818120, -4743133, -11194191, -5645734, 5150968, 7274186 }, + { 2831366, -12492146, 1478975, 6122054, 23825128, -12733586, 31097299, 6083058, 31021603, -9793610 }, + { -2529932, -2229646, 445613, 10720828, -13849527, -11505937, -23507731, 16354465, 15067285, -14147707 } + }, + { + { 7840942, 14037873, -33364863, 15934016, -728213, -3642706, 21403988, 1057586, -19379462, -12403220 }, + { 915865, -16469274, 15608285, -8789130, -24357026, 6060030, -17371319, 8410997, -7220461, 16527025 }, + { 32922597, -556987, 20336074, -16184568, 10903705, -5384487, 16957574, 52992, 23834301, 6588044 } + }, + { + { 32752030, 11232950, 3381995, -8714866, 22652988, -10744103, 17159699, 16689107, -20314580, -1305992 }, + { -4689649, 9166776, -25710296, -10847306, 11576752, 12733943, 7924251, -2752281, 1976123, -7249027 }, + { 21251222, 16309901, -2983015, -6783122, 30810597, 12967303, 156041, -3371252, 12331345, -8237197 } + }, + { + { 8651614, -4477032, -16085636, -4996994, 13002507, 2950805, 29054427, -5106970, 10008136, -4667901 }, + { 31486080, 15114593, -14261250, 12951354, 14369431, -7387845, 16347321, -13662089, 8684155, -10532952 }, + { 19443825, 11385320, 24468943, -9659068, -23919258, 2187569, -26263207, -6086921, 31316348, 14219878 } + }, + { + { -28594490, 1193785, 32245219, 11392485, 31092169, 15722801, 27146014, 6992409, 29126555, 9207390 }, + { 32382935, 1110093, 18477781, 11028262, -27411763, -7548111, -4980517, 10843782, -7957600, -14435730 }, + { 2814918, 7836403, 27519878, -7868156, -20894015, -11553689, -21494559, 8550130, 28346258, 1994730 } + }, + { + { -19578299, 8085545, -14000519, -3948622, 2785838, -16231307, -19516951, 7174894, 22628102, 8115180 }, + { -30405132, 955511, -11133838, -15078069, -32447087, -13278079, -25651578, 3317160, -9943017, 930272 }, + { -15303681, -6833769, 28856490, 1357446, 23421993, 1057177, 24091212, -1388970, -22765376, -10650715 } + }, + { + { -22751231, -5303997, -12907607, -12768866, -15811511, -7797053, -14839018, -16554220, -1867018, 8398970 }, + { -31969310, 2106403, -4736360, 1362501, 12813763, 16200670, 22981545, -6291273, 18009408, -15772772 }, + { -17220923, -9545221, -27784654, 14166835, 29815394, 7444469, 29551787, -3727419, 19288549, 1325865 } + }, + { + { 15100157, -15835752, -23923978, -1005098, -26450192, 15509408, 12376730, -3479146, 33166107, -8042750 }, + { 20909231, 13023121, -9209752, 16251778, -5778415, -8094914, 12412151, 10018715, 2213263, -13878373 }, + { 32529814, -11074689, 30361439, -16689753, -9135940, 1513226, 22922121, 6382134, -5766928, 8371348 } + } +}, +{ /* 11/31 */ + { + { 9923462, 11271500, 12616794, 3544722, -29998368, -1721626, 12891687, -8193132, -26442943, 10486144 }, + { -22597207, -7012665, 8587003, -8257861, 4084309, -12970062, 361726, 2610596, -23921530, -11455195 }, + { 5408411, -1136691, -4969122, 10561668, 24145918, 14240566, 31319731, -4235541, 19985175, -3436086 } + }, + { + { -13994457, 16616821, 14549246, 3341099, 32155958, 13648976, -17577068, 8849297, 65030, 8370684 }, + { -8320926, -12049626, 31204563, 5839400, -20627288, -1057277, -19442942, 6922164, 12743482, -9800518 }, + { -2361371, 12678785, 28815050, 4759974, -23893047, 4884717, 23783145, 11038569, 18800704, 255233 } + }, + { + { -5269658, -1773886, 13957886, 7990715, 23132995, 728773, 13393847, 9066957, 19258688, -14753793 }, + { -2936654, -10827535, -10432089, 14516793, -3640786, 4372541, -31934921, 2209390, -1524053, 2055794 }, + { 580882, 16705327, 5468415, -2683018, -30926419, -14696000, -7203346, -8994389, -30021019, 7394435 } + }, + { + { 23838809, 1822728, -15738443, 15242727, 8318092, -3733104, -21672180, -3492205, -4821741, 14799921 }, + { 13345610, 9759151, 3371034, -16137791, 16353039, 8577942, 31129804, 13496856, -9056018, 7402518 }, + { 2286874, -4435931, -20042458, -2008336, -13696227, 5038122, 11006906, -15760352, 8205061, 1607563 } + }, + { + { 14414086, -8002132, 3331830, -3208217, 22249151, -5594188, 18364661, -2906958, 30019587, -9029278 }, + { -27688051, 1585953, -10775053, 931069, -29120221, -11002319, -14410829, 12029093, 9944378, 8024 }, + { 4368715, -3709630, 29874200, -15022983, -20230386, -11410704, -16114594, -999085, -8142388, 5640030 } + }, + { + { 10299610, 13746483, 11661824, 16234854, 7630238, 5998374, 9809887, -16694564, 15219798, -14327783 }, + { 27425505, -5719081, 3055006, 10660664, 23458024, 595578, -15398605, -1173195, -18342183, 9742717 }, + { 6744077, 2427284, 26042789, 2720740, -847906, 1118974, 32324614, 7406442, 12420155, 1994844 } + }, + { + { 14012521, -5024720, -18384453, -9578469, -26485342, -3936439, -13033478, -10909803, 24319929, -6446333 }, + { 16412690, -4507367, 10772641, 15929391, -17068788, -4658621, 10555945, -10484049, -30102368, -4739048 }, + { 22397382, -7767684, -9293161, -12792868, 17166287, -9755136, -27333065, 6199366, 21880021, -12250760 } + }, + { + { -4283307, 5368523, -31117018, 8163389, -30323063, 3209128, 16557151, 8890729, 8840445, 4957760 }, + { -15447727, 709327, -6919446, -10870178, -29777922, 6522332, -21720181, 12130072, -14796503, 5005757 }, + { -2114751, -14308128, 23019042, 15765735, -25269683, 6002752, 10183197, -13239326, -16395286, -2176112 } + } +}, +{ /* 12/31 */ + { + { -19025756, 1632005, 13466291, -7995100, -23640451, 16573537, -32013908, -3057104, 22208662, 2000468 }, + { 3065073, -1412761, -25598674, -361432, -17683065, -5703415, -8164212, 11248527, -3691214, -7414184 }, + { 10379208, -6045554, 8877319, 1473647, -29291284, -12507580, 16690915, 2553332, -3132688, 16400289 } + }, + { + { 15716668, 1254266, -18472690, 7446274, -8448918, 6344164, -22097271, -7285580, 26894937, 9132066 }, + { 24158887, 12938817, 11085297, -8177598, -28063478, -4457083, -30576463, 64452, -6817084, -2692882 }, + { 13488534, 7794716, 22236231, 5989356, 25426474, -12578208, 2350710, -3418511, -4688006, 2364226 } + }, + { + { 16335052, 9132434, 25640582, 6678888, 1725628, 8517937, -11807024, -11697457, 15445875, -7798101 }, + { 29004207, -7867081, 28661402, -640412, -12794003, -7943086, 31863255, -4135540, -278050, -15759279 }, + { -6122061, -14866665, -28614905, 14569919, -10857999, -3591829, 10343412, -6976290, -29828287, -10815811 } + }, + { + { 27081650, 3463984, 14099042, -4517604, 1616303, -6205604, 29542636, 15372179, 17293797, 960709 }, + { 20263915, 11434237, -5765435, 11236810, 13505955, -10857102, -16111345, 6493122, -19384511, 7639714 }, + { -2830798, -14839232, 25403038, -8215196, -8317012, -16173699, 18006287, -16043750, 29994677, -15808121 } + }, + { + { 9769828, 5202651, -24157398, -13631392, -28051003, -11561624, -24613141, -13860782, -31184575, 709464 }, + { 12286395, 13076066, -21775189, -1176622, -25003198, 4057652, -32018128, -8890874, 16102007, 13205847 }, + { 13733362, 5599946, 10557076, 3195751, -5557991, 8536970, -25540170, 8525972, 10151379, 10394400 } + }, + { + { 4024660, -16137551, 22436262, 12276534, -9099015, -2686099, 19698229, 11743039, -33302334, 8934414 }, + { -15879800, -4525240, -8580747, -2934061, 14634845, -698278, -9449077, 3137094, -11536886, 11721158 }, + { 17555939, -5013938, 8268606, 2331751, -22738815, 9761013, 9319229, 8835153, -9205489, -1280045 } + }, + { + { -461409, -7830014, 20614118, 16688288, -7514766, -4807119, 22300304, 505429, 6108462, -6183415 }, + { -5070281, 12367917, -30663534, 3234473, 32617080, -8422642, 29880583, -13483331, -26898490, -7867459 }, + { -31975283, 5726539, 26934134, 10237677, -3173717, -605053, 24199304, 3795095, 7592688, -14992079 } + }, + { + { 21594432, -14964228, 17466408, -4077222, 32537084, 2739898, 6407723, 12018833, -28256052, 4298412 }, + { -20650503, -11961496, -27236275, 570498, 3767144, -1717540, 13891942, -1569194, 13717174, 10805743 }, + { -14676630, -15644296, 15287174, 11927123, 24177847, -8175568, -796431, 14860609, -26938930, -5863836 } + } +}, +{ /* 13/31 */ + { + { 12962541, 5311799, -10060768, 11658280, 18855286, -7954201, 13286263, -12808704, -4381056, 9882022 }, + { 18512079, 11319350, -20123124, 15090309, 18818594, 5271736, -22727904, 3666879, -23967430, -3299429 }, + { -6789020, -3146043, 16192429, 13241070, 15898607, -14206114, -10084880, -6661110, -2403099, 5276065 } + }, + { + { 30169808, -5317648, 26306206, -11750859, 27814964, 7069267, 7152851, 3684982, 1449224, 13082861 }, + { 10342826, 3098505, 2119311, 193222, 25702612, 12233820, 23697382, 15056736, -21016438, -8202000 }, + { -33150110, 3261608, 22745853, 7948688, 19370557, -15177665, -26171976, 6482814, -10300080, -11060101 } + }, + { + { 32869458, -5408545, 25609743, 15678670, -10687769, -15471071, 26112421, 2521008, -22664288, 6904815 }, + { 29506923, 4457497, 3377935, -9796444, -30510046, 12935080, 1561737, 3841096, -29003639, -6657642 }, + { 10340844, -6630377, -18656632, -2278430, 12621151, -13339055, 30878497, -11824370, -25584551, 5181966 } + }, + { + { 25940115, -12658025, 17324188, -10307374, -8671468, 15029094, 24396252, -16450922, -2322852, -12388574 }, + { -21765684, 9916823, -1300409, 4079498, -1028346, 11909559, 1782390, 12641087, 20603771, -6561742 }, + { -18882287, -11673380, 24849422, 11501709, 13161720, -4768874, 1925523, 11914390, 4662781, 7820689 } + }, + { + { 12241050, -425982, 8132691, 9393934, 32846760, -1599620, 29749456, 12172924, 16136752, 15264020 }, + { -10349955, -14680563, -8211979, 2330220, -17662549, -14545780, 10658213, 6671822, 19012087, 3772772 }, + { 3753511, -3421066, 10617074, 2028709, 14841030, -6721664, 28718732, -15762884, 20527771, 12988982 } + }, + { + { -14822485, -5797269, -3707987, 12689773, -898983, -10914866, -24183046, -10564943, 3299665, -12424953 }, + { -16777703, -15253301, -9642417, 4978983, 3308785, 8755439, 6943197, 6461331, -25583147, 8991218 }, + { -17226263, 1816362, -1673288, -6086439, 31783888, -8175991, -32948145, 7417950, -30242287, 1507265 } + }, + { + { 29692663, 6829891, -10498800, 4334896, 20945975, -11906496, -28887608, 8209391, 14606362, -10647073 }, + { -3481570, 8707081, 32188102, 5672294, 22096700, 1711240, -33020695, 9761487, 4170404, -2085325 }, + { -11587470, 14855945, -4127778, -1531857, -26649089, 15084046, 22186522, 16002000, -14276837, -8400798 } + }, + { + { -4811456, 13761029, -31703877, -2483919, -3312471, 7869047, -7113572, -9620092, 13240845, 10965870 }, + { -7742563, -8256762, -14768334, -13656260, -23232383, 12387166, 4498947, 14147411, 29514390, 4302863 }, + { -13413405, -12407859, 20757302, -13801832, 14785143, 8976368, -5061276, -2144373, 17846988, -13971927 } + } +}, +{ /* 14/31 */ + { + { -2244452, -754728, -4597030, -1066309, -6247172, 1455299, -21647728, -9214789, -5222701, 12650267 }, + { -9906797, -16070310, 21134160, 12198166, -27064575, 708126, 387813, 13770293, -19134326, 10958663 }, + { 22470984, 12369526, 23446014, -5441109, -21520802, -9698723, -11772496, -11574455, -25083830, 4271862 } + }, + { + { -25169565, -10053642, -19909332, 15361595, -5984358, 2159192, 75375, -4278529, -32526221, 8469673 }, + { 15854970, 4148314, -8893890, 7259002, 11666551, 13824734, -30531198, 2697372, 24154791, -9460943 }, + { 15446137, -15806644, 29759747, 14019369, 30811221, -9610191, -31582008, 12840104, 24913809, 9815020 } + }, + { + { -4709286, -5614269, -31841498, -12288893, -14443537, 10799414, -9103676, 13438769, 18735128, 9466238 }, + { 11933045, 9281483, 5081055, -5183824, -2628162, -4905629, -7727821, -10896103, -22728655, 16199064 }, + { 14576810, 379472, -26786533, -8317236, -29426508, -10812974, -102766, 1876699, 30801119, 2164795 } + }, + { + { 15995086, 3199873, 13672555, 13712240, -19378835, -4647646, -13081610, -15496269, -13492807, 1268052 }, + { -10290614, -3659039, -3286592, 10948818, 23037027, 3794475, -3470338, -12600221, -17055369, 3565904 }, + { 29210088, -9419337, -5919792, -4952785, 10834811, -13327726, -16512102, -10820713, -27162222, -14030531 } + }, + { + { -13161890, 15508588, 16663704, -8156150, -28349942, 9019123, -29183421, -3769423, 2244111, -14001979 }, + { -5152875, -3800936, -9306475, -6071583, 16243069, 14684434, -25673088, -16180800, 13491506, 4641841 }, + { 10813417, 643330, -19188515, -728916, 30292062, -16600078, 27548447, -7721242, 14476989, -12767431 } + }, + { + { 10292079, 9984945, 6481436, 8279905, -7251514, 7032743, 27282937, -1644259, -27912810, 12651324 }, + { -31185513, -813383, 22271204, 11835308, 10201545, 15351028, 17099662, 3988035, 21721536, -3148940 }, + { 10202177, -6545839, -31373232, -9574638, -32150642, -8119683, -12906320, 3852694, 13216206, 14842320 } + }, + { + { -15815640, -10601066, -6538952, -7258995, -6984659, -6581778, -31500847, 13765824, -27434397, 9900184 }, + { 14465505, -13833331, -32133984, -14738873, -27443187, 12990492, 33046193, 15796406, -7051866, -8040114 }, + { 30924417, -8279620, 6359016, -12816335, 16508377, 9071735, -25488601, 15413635, 9524356, -7018878 } + }, + { + { 12274201, -13175547, 32627641, -1785326, 6736625, 13267305, 5237659, -5109483, 15663516, 4035784 }, + { -2951309, 8903985, 17349946, 601635, -16432815, -4612556, -13732739, -15889334, -22258478, 4659091 }, + { -16916263, -4952973, -30393711, -15158821, 20774812, 15897498, 5736189, 15026997, -2178256, -13455585 } + } +}, +{ /* 15/31 */ + { + { -8858980, -2219056, 28571666, -10155518, -474467, -10105698, -3801496, 278095, 23440562, -290208 }, + { 10226241, -5928702, 15139956, 120818, -14867693, 5218603, 32937275, 11551483, -16571960, -7442864 }, + { 17932739, -12437276, -24039557, 10749060, 11316803, 7535897, 22503767, 5561594, -3646624, 3898661 } + }, + { + { 7749907, -969567, -16339731, -16464, -25018111, 15122143, -1573531, 7152530, 21831162, 1245233 }, + { 26958459, -14658026, 4314586, 8346991, -5677764, 11960072, -32589295, -620035, -30402091, -16716212 }, + { -12165896, 9166947, 33491384, 13673479, 29787085, 13096535, 6280834, 14587357, -22338025, 13987525 } + }, + { + { -24349909, 7778775, 21116000, 15572597, -4833266, -5357778, -4300898, -5124639, -7469781, -2858068 }, + { 9681908, -6737123, -31951644, 13591838, -6883821, 386950, 31622781, 6439245, -14581012, 4091397 }, + { -8426427, 1470727, -28109679, -1596990, 3978627, -5123623, -19622683, 12092163, 29077877, -14741988 } + }, + { + { 5269168, -6859726, -13230211, -8020715, 25932563, 1763552, -5606110, -5505881, -20017847, 2357889 }, + { 32264008, -15407652, -5387735, -1160093, -2091322, -3946900, 23104804, -12869908, 5727338, 189038 }, + { 14609123, -8954470, -6000566, -16622781, -14577387, -7743898, -26745169, 10942115, -25888931, -14884697 } + }, + { + { 20513500, 5557931, -15604613, 7829531, 26413943, -2019404, -21378968, 7471781, 13913677, -5137875 }, + { -25574376, 11967826, 29233242, 12948236, -6754465, 4713227, -8940970, 14059180, 12878652, 8511905 }, + { -25656801, 3393631, -2955415, -7075526, -2250709, 9366908, -30223418, 6812974, 5568676, -3127656 } + }, + { + { 11630004, 12144454, 2116339, 13606037, 27378885, 15676917, -17408753, -13504373, -14395196, 8070818 }, + { 27117696, -10007378, -31282771, -5570088, 1127282, 12772488, -29845906, 10483306, -11552749, -1028714 }, + { 10637467, -5688064, 5674781, 1072708, -26343588, -6982302, -1683975, 9177853, -27493162, 15431203 } + }, + { + { 20525145, 10892566, -12742472, 12779443, -29493034, 16150075, -28240519, 14943142, -15056790, -7935931 }, + { -30024462, 5626926, -551567, -9981087, 753598, 11981191, 25244767, -3239766, -3356550, 9594024 }, + { -23752644, 2636870, -5163910, -10103818, 585134, 7877383, 11345683, -6492290, 13352335, -10977084 } + }, + { + { -1931799, -5407458, 3304649, -12884869, 17015806, -4877091, -29783850, -7752482, -13215537, -319204 }, + { 20239939, 6607058, 6203985, 3483793, -18386976, -779229, -20723742, 15077870, -22750759, 14523817 }, + { 27406042, -6041657, 27423596, -4497394, 4996214, 10002360, -28842031, -4545494, -30172742, -4805667 } + } +}, +{ /* 16/31 */ + { + { 11374242, 12660715, 17861383, -12540833, 10935568, 1099227, -13886076, -9091740, -27727044, 11358504 }, + { -12730809, 10311867, 1510375, 10778093, -2119455, -9145702, 32676003, 11149336, -26123651, 4985768 }, + { -19096303, 341147, -6197485, -239033, 15756973, -8796662, -983043, 13794114, -19414307, -15621255 } + }, + { + { 6490081, 11940286, 25495923, -7726360, 8668373, -8751316, 3367603, 6970005, -1691065, -9004790 }, + { 1656497, 13457317, 15370807, 6364910, 13605745, 8362338, -19174622, -5475723, -16796596, -5031438 }, + { -22273315, -13524424, -64685, -4334223, -18605636, -10921968, -20571065, -7007978, -99853, -10237333 } + }, + { + { 17747465, 10039260, 19368299, -4050591, -20630635, -16041286, 31992683, -15857976, -29260363, -5511971 }, + { 31932027, -4986141, -19612382, 16366580, 22023614, 88450, 11371999, -3744247, 4882242, -10626905 }, + { 29796507, 37186, 19818052, 10115756, -11829032, 3352736, 18551198, 3272828, -5190932, -4162409 } + }, + { + { 12501286, 4044383, -8612957, -13392385, -32430052, 5136599, -19230378, -3529697, 330070, -3659409 }, + { 6384877, 2899513, 17807477, 7663917, -2358888, 12363165, 25366522, -8573892, -271295, 12071499 }, + { -8365515, -4042521, 25133448, -4517355, -6211027, 2265927, -32769618, 1936675, -5159697, 3829363 } + }, + { + { 28425966, -5835433, -577090, -4697198, -14217555, 6870930, 7921550, -6567787, 26333140, 14267664 }, + { -11067219, 11871231, 27385719, -10559544, -4585914, -11189312, 10004786, -8709488, -21761224, 8930324 }, + { -21197785, -16396035, 25654216, -1725397, 12282012, 11008919, 1541940, 4757911, -26491501, -16408940 } + }, + { + { 13537262, -7759490, -20604840, 10961927, -5922820, -13218065, -13156584, 6217254, -15943699, 13814990 }, + { -17422573, 15157790, 18705543, 29619, 24409717, -260476, 27361681, 9257833, -1956526, -1776914 }, + { -25045300, -10191966, 15366585, 15166509, -13105086, 8423556, -29171540, 12361135, -18685978, 4578290 } + }, + { + { 24579768, 3711570, 1342322, -11180126, -27005135, 14124956, -22544529, 14074919, 21964432, 8235257 }, + { -6528613, -2411497, 9442966, -5925588, 12025640, -1487420, -2981514, -1669206, 13006806, 2355433 }, + { -16304899, -13605259, -6632427, -5142349, 16974359, -10911083, 27202044, 1719366, 1141648, -12796236 } + }, + { + { -12863944, -13219986, -8318266, -11018091, -6810145, -4843894, 13475066, -3133972, 32674895, 13715045 }, + { 11423335, -5468059, 32344216, 8962751, 24989809, 9241752, -13265253, 16086212, -28740881, -15642093 }, + { -1409668, 12530728, -6368726, 10847387, 19531186, -14132160, -11709148, 7791794, -27245943, 4383347 } + } +}, +{ /* 17/31 */ + { + { -28970898, 5271447, -1266009, -9736989, -12455236, 16732599, -4862407, -4906449, 27193557, 6245191 }, + { -15193956, 5362278, -1783893, 2695834, 4960227, 12840725, 23061898, 3260492, 22510453, 8577507 }, + { -12632451, 11257346, -32692994, 13548177, -721004, 10879011, 31168030, 13952092, -29571492, -3635906 } + }, + { + { 3877321, -9572739, 32416692, 5405324, -11004407, -13656635, 3759769, 11935320, 5611860, 8164018 }, + { -16275802, 14667797, 15906460, 12155291, -22111149, -9039718, 32003002, -8832289, 5773085, -8422109 }, + { -23788118, -8254300, 1950875, 8937633, 18686727, 16459170, -905725, 12376320, 31632953, 190926 } + }, + { + { -24593607, -16138885, -8423991, 13378746, 14162407, 6901328, -8288749, 4508564, -25341555, -3627528 }, + { 8884438, -5884009, 6023974, 10104341, -6881569, -4941533, 18722941, -14786005, -1672488, 827625 }, + { -32720583, -16289296, -32503547, 7101210, 13354605, 2659080, -1800575, -14108036, -24878478, 1541286 } + }, + { + { 2901347, -1117687, 3880376, -10059388, -17620940, -3612781, -21802117, -3567481, 20456845, -1885033 }, + { 27019610, 12299467, -13658288, -1603234, -12861660, -4861471, -19540150, -5016058, 29439641, 15138866 }, + { 21536104, -6626420, -32447818, -10690208, -22408077, 5175814, -5420040, -16361163, 7779328, 109896 } + }, + { + { 30279744, 14648750, -8044871, 6425558, 13639621, -743509, 28698390, 12180118, 23177719, -554075 }, + { 26572847, 3405927, -31701700, 12890905, -19265668, 5335866, -6493768, 2378492, 4439158, -13279347 }, + { -22716706, 3489070, -9225266, -332753, 18875722, -1140095, 14819434, -12731527, -17717757, -5461437 } + }, + { + { -5056483, 16566551, 15953661, 3767752, -10436499, 15627060, -820954, 2177225, 8550082, -15114165 }, + { -18473302, 16596775, -381660, 15663611, 22860960, 15585581, -27844109, -3582739, -23260460, -8428588 }, + { -32480551, 15707275, -8205912, -5652081, 29464558, 2713815, -22725137, 15860482, -21902570, 1494193 } + }, + { + { -19562091, -14087393, -25583872, -9299552, 13127842, 759709, 21923482, 16529112, 8742704, 12967017 }, + { -28464899, 1553205, 32536856, -10473729, -24691605, -406174, -8914625, -2933896, -29903758, 15553883 }, + { 21877909, 3230008, 9881174, 10539357, -4797115, 2841332, 11543572, 14513274, 19375923, -12647961 } + }, + { + { 8832269, -14495485, 13253511, 5137575, 5037871, 4078777, 24880818, -6222716, 2862653, 9455043 }, + { 29306751, 5123106, 20245049, -14149889, 9592566, 8447059, -2077124, -2990080, 15511449, 4789663 }, + { -20679756, 7004547, 8824831, -9434977, -4045704, -3750736, -5754762, 108893, 23513200, 16652362 } + } +}, +{ /* 18/31 */ + { + { -33256173, 4144782, -4476029, -6579123, 10770039, -7155542, -6650416, -12936300, -18319198, 10212860 }, + { 2756081, 8598110, 7383731, -6859892, 22312759, -1105012, 21179801, 2600940, -9988298, -12506466 }, + { -24645692, 13317462, -30449259, -15653928, 21365574, -10869657, 11344424, 864440, -2499677, -16710063 } + }, + { + { -26432803, 6148329, -17184412, -14474154, 18782929, -275997, -22561534, 211300, 2719757, 4940997 }, + { -1323882, 3911313, -6948744, 14759765, -30027150, 7851207, 21690126, 8518463, 26699843, 5276295 }, + { -13149873, -6429067, 9396249, 365013, 24703301, -10488939, 1321586, 149635, -15452774, 7159369 } + }, + { + { 9987780, -3404759, 17507962, 9505530, 9731535, -2165514, 22356009, 8312176, 22477218, -8403385 }, + { 18155857, -16504990, 19744716, 9006923, 15154154, -10538976, 24256460, -4864995, -22548173, 9334109 }, + { 2986088, -4911893, 10776628, -3473844, 10620590, -7083203, -21413845, 14253545, -22587149, 536906 } + }, + { + { 4377756, 8115836, 24567078, 15495314, 11625074, 13064599, 7390551, 10589625, 10838060, -15420424 }, + { -19342404, 867880, 9277171, -3218459, -14431572, -1986443, 19295826, -15796950, 6378260, 699185 }, + { 7895026, 4057113, -7081772, -13077756, -17886831, -323126, -716039, 15693155, -5045064, -13373962 } + }, + { + { -7737563, -5869402, -14566319, -7406919, 11385654, 13201616, 31730678, -10962840, -3918636, -9669325 }, + { 10188286, -15770834, -7336361, 13427543, 22223443, 14896287, 30743455, 7116568, -21786507, 5427593 }, + { 696102, 13206899, 27047647, -10632082, 15285305, -9853179, 10798490, -4578720, 19236243, 12477404 } + }, + { + { -11229439, 11243796, -17054270, -8040865, -788228, -8167967, -3897669, 11180504, -23169516, 7733644 }, + { 17800790, -14036179, -27000429, -11766671, 23887827, 3149671, 23466177, -10538171, 10322027, 15313801 }, + { 26246234, 11968874, 32263343, -5468728, 6830755, -13323031, -15794704, -101982, -24449242, 10890804 } + }, + { + { -31365647, 10271363, -12660625, -6267268, 16690207, -13062544, -14982212, 16484931, 25180797, -5334884 }, + { -586574, 10376444, -32586414, -11286356, 19801893, 10997610, 2276632, 9482883, 316878, 13820577 }, + { -9882808, -4510367, -2115506, 16457136, -11100081, 11674996, 30756178, -7515054, 30696930, -3712849 } + }, + { + { 32988917, -9603412, 12499366, 7910787, -10617257, -11931514, -7342816, -9985397, -32349517, 7392473 }, + { -8855661, 15927861, 9866406, -3649411, -2396914, -16655781, -30409476, -9134995, 25112947, -2926644 }, + { -2504044, -436966, 25621774, -5678772, 15085042, -5479877, -24884878, -13526194, 5537438, -13914319 } + } +}, +{ /* 19/31 */ + { + { -11225584, 2320285, -9584280, 10149187, -33444663, 5808648, -14876251, -1729667, 31234590, 6090599 }, + { -9633316, 116426, 26083934, 2897444, -6364437, -2688086, 609721, 15878753, -6970405, -9034768 }, + { -27757857, 247744, -15194774, -9002551, 23288161, -10011936, -23869595, 6503646, 20650474, 1804084 } + }, + { + { -27589786, 15456424, 8972517, 8469608, 15640622, 4439847, 3121995, -10329713, 27842616, -202328 }, + { -15306973, 2839644, 22530074, 10026331, 4602058, 5048462, 28248656, 5031932, -11375082, 12714369 }, + { 20807691, -7270825, 29286141, 11421711, -27876523, -13868230, -21227475, 1035546, -19733229, 12796920 } + }, + { + { 12076899, -14301286, -8785001, -11848922, -25012791, 16400684, -17591495, -12899438, 3480665, -15182815 }, + { -32361549, 5457597, 28548107, 7833186, 7303070, -11953545, -24363064, -15921875, -33374054, 2771025 }, + { -21389266, 421932, 26597266, 6860826, 22486084, -6737172, -17137485, -4210226, -24552282, 15673397 } + }, + { + { -20184622, 2338216, 19788685, -9620956, -4001265, -8740893, -20271184, 4733254, 3727144, -12934448 }, + { 6120119, 814863, -11794402, -622716, 6812205, -15747771, 2019594, 7975683, 31123697, -10958981 }, + { 30069250, -11435332, 30434654, 2958439, 18399564, -976289, 12296869, 9204260, -16432438, 9648165 } + }, + { + { 32705432, -1550977, 30705658, 7451065, -11805606, 9631813, 3305266, 5248604, -26008332, -11377501 }, + { 17219865, 2375039, -31570947, -5575615, -19459679, 9219903, 294711, 15298639, 2662509, -16297073 }, + { -1172927, -7558695, -4366770, -4287744, -21346413, -8434326, 32087529, -1222777, 32247248, -14389861 } + }, + { + { 14312628, 1221556, 17395390, -8700143, -4945741, -8684635, -28197744, -9637817, -16027623, -13378845 }, + { -1428825, -9678990, -9235681, 6549687, -7383069, -468664, 23046502, 9803137, 17597934, 2346211 }, + { 18510800, 15337574, 26171504, 981392, -22241552, 7827556, -23491134, -11323352, 3059833, -11782870 } + }, + { + { 10141598, 6082907, 17829293, -1947643, 9830092, 13613136, -25556636, -5544586, -33502212, 3592096 }, + { 33114168, -15889352, -26525686, -13343397, 33076705, 8716171, 1151462, 1521897, -982665, -6837803 }, + { -32939165, -4255815, 23947181, -324178, -33072974, -12305637, -16637686, 3891704, 26353178, 693168 } + }, + { + { 30374239, 1595580, -16884039, 13186931, 4600344, 406904, 9585294, -400668, 31375464, 14369965 }, + { -14370654, -7772529, 1510301, 6434173, -18784789, -6262728, 32732230, -13108839, 17901441, 16011505 }, + { 18171223, -11934626, -12500402, 15197122, -11038147, -15230035, -19172240, -16046376, 8764035, 12309598 } + } +}, +{ /* 20/31 */ + { + { 5975908, -5243188, -19459362, -9681747, -11541277, 14015782, -23665757, 1228319, 17544096, -10593782 }, + { 5811932, -1715293, 3442887, -2269310, -18367348, -8359541, -18044043, -15410127, -5565381, 12348900 }, + { -31399660, 11407555, 25755363, 6891399, -3256938, 14872274, -24849353, 8141295, -10632534, -585479 } + }, + { + { -12675304, 694026, -5076145, 13300344, 14015258, -14451394, -9698672, -11329050, 30944593, 1130208 }, + { 8247766, -6710942, -26562381, -7709309, -14401939, -14648910, 4652152, 2488540, 23550156, -271232 }, + { 17294316, -3788438, 7026748, 15626851, 22990044, 113481, 2267737, -5908146, -408818, -137719 } + }, + { + { 16091085, -16253926, 18599252, 7340678, 2137637, -1221657, -3364161, 14550936, 3260525, -7166271 }, + { -4910104, -13332887, 18550887, 10864893, -16459325, -7291596, -23028869, -13204905, -12748722, 2701326 }, + { -8574695, 16099415, 4629974, -16340524, -20786213, -6005432, -10018363, 9276971, 11329923, 1862132 } + }, + { + { 14763076, -15903608, -30918270, 3689867, 3511892, 10313526, -21951088, 12219231, -9037963, -940300 }, + { 8894987, -3446094, 6150753, 3013931, 301220, 15693451, -31981216, -2909717, -15438168, 11595570 }, + { 15214962, 3537601, -26238722, -14058872, 4418657, -15230761, 13947276, 10730794, -13489462, -4363670 } + }, + { + { -2538306, 7682793, 32759013, 263109, -29984731, -7955452, -22332124, -10188635, 977108, 699994 }, + { -12466472, 4195084, -9211532, 550904, -15565337, 12917920, 19118110, -439841, -30534533, -14337913 }, + { 31788461, -14507657, 4799989, 7372237, 8808585, -14747943, 9408237, -10051775, 12493932, -5409317 } + }, + { + { -25680606, 5260744, -19235809, -6284470, -3695942, 16566087, 27218280, 2607121, 29375955, 6024730 }, + { 842132, -2794693, -4763381, -8722815, 26332018, -12405641, 11831880, 6985184, -9940361, 2854096 }, + { -4847262, -7969331, 2516242, -5847713, 9695691, -7221186, 16512645, 960770, 12121869, 16648078 } + }, + { + { -15218652, 14667096, -13336229, 2013717, 30598287, -464137, -31504922, -7882064, 20237806, 2838411 }, + { -19288047, 4453152, 15298546, -16178388, 22115043, -15972604, 12544294, -13470457, 1068881, -12499905 }, + { -9558883, -16518835, 33238498, 13506958, 30505848, -1114596, -8486907, -2630053, 12521378, 4845654 } + }, + { + { -28198521, 10744108, -2958380, 10199664, 7759311, -13088600, 3409348, -873400, -6482306, -12885870 }, + { -23561822, 6230156, -20382013, 10655314, -24040585, -11621172, 10477734, -1240216, -3113227, 13974498 }, + { 12966261, 15550616, -32038948, -1615346, 21025980, -629444, 5642325, 7188737, 18895762, 12629579 } + } +}, +{ /* 21/31 */ + { + { 14741879, -14946887, 22177208, -11721237, 1279741, 8058600, 11758140, 789443, 32195181, 3895677 }, + { 10758205, 15755439, -4509950, 9243698, -4879422, 6879879, -2204575, -3566119, -8982069, 4429647 }, + { -2453894, 15725973, -20436342, -10410672, -5803908, -11040220, -7135870, -11642895, 18047436, -15281743 } + }, + { + { -25173001, -11307165, 29759956, 11776784, -22262383, -15820455, 10993114, -12850837, -17620701, -9408468 }, + { 21987233, 700364, -24505048, 14972008, -7774265, -5718395, 32155026, 2581431, -29958985, 8773375 }, + { -25568350, 454463, -13211935, 16126715, 25240068, 8594567, 20656846, 12017935, -7874389, -13920155 } + }, + { + { 6028182, 6263078, -31011806, -11301710, -818919, 2461772, -31841174, -5468042, -1721788, -2776725 }, + { -12278994, 16624277, 987579, -5922598, 32908203, 1248608, 7719845, -4166698, 28408820, 6816612 }, + { -10358094, -8237829, 19549651, -12169222, 22082623, 16147817, 20613181, 13982702, -10339570, 5067943 } + }, + { + { -30505967, -3821767, 12074681, 13582412, -19877972, 2443951, -19719286, 12746132, 5331210, -10105944 }, + { 30528811, 3601899, -1957090, 4619785, -27361822, -15436388, 24180793, -12570394, 27679908, -1648928 }, + { 9402404, -13957065, 32834043, 10838634, -26580150, -13237195, 26653274, -8685565, 22611444, -12715406 } + }, + { + { 22190590, 1118029, 22736441, 15130463, -30460692, -5991321, 19189625, -4648942, 4854859, 6622139 }, + { -8310738, -2953450, -8262579, -3388049, -10401731, -271929, 13424426, -3567227, 26404409, 13001963 }, + { -31241838, -15415700, -2994250, 8939346, 11562230, -12840670, -26064365, -11621720, -15405155, 11020693 } + }, + { + { 1866042, -7949489, -7898649, -10301010, 12483315, 13477547, 3175636, -12424163, 28761762, 1406734 }, + { -448555, -1777666, 13018551, 3194501, -9580420, -11161737, 24760585, -4347088, 25577411, -13378680 }, + { -24290378, 4759345, -690653, -1852816, 2066747, 10693769, -29595790, 9884936, -9368926, 4745410 } + }, + { + { -9141284, 6049714, -19531061, -4341411, -31260798, 9944276, -15462008, -11311852, 10931924, -11931931 }, + { -16561513, 14112680, -8012645, 4817318, -8040464, -11414606, -22853429, 10856641, -20470770, 13434654 }, + { 22759489, -10073434, -16766264, -1871422, 13637442, -10168091, 1765144, -12654326, 28445307, -5364710 } + }, + { + { 29875063, 12493613, 2795536, -3786330, 1710620, 15181182, -10195717, -8788675, 9074234, 1167180 }, + { -26205683, 11014233, -9842651, -2635485, -26908120, 7532294, -18716888, -9535498, 3843903, 9367684 }, + { -10969595, -6403711, 9591134, 9582310, 11349256, 108879, 16235123, 8601684, -139197, 4242895 } + } +}, +{ /* 22/31 */ + { + { 22092954, -13191123, -2042793, -11968512, 32186753, -11517388, -6574341, 2470660, -27417366, 16625501 }, + { -11057722, 3042016, 13770083, -9257922, 584236, -544855, -7770857, 2602725, -27351616, 14247413 }, + { 6314175, -10264892, -32772502, 15957557, -10157730, 168750, -8618807, 14290061, 27108877, -1180880 } + }, + { + { -8586597, -7170966, 13241782, 10960156, -32991015, -13794596, 33547976, -11058889, -27148451, 981874 }, + { 22833440, 9293594, -32649448, -13618667, -9136966, 14756819, -22928859, -13970780, -10479804, -16197962 }, + { -7768587, 3326786, -28111797, 10783824, 19178761, 14905060, 22680049, 13906969, -15933690, 3797899 } + }, + { + { 21721356, -4212746, -12206123, 9310182, -3882239, -13653110, 23740224, -2709232, 20491983, -8042152 }, + { 9209270, -15135055, -13256557, -6167798, -731016, 15289673, 25947805, 15286587, 30997318, -6703063 }, + { 7392032, 16618386, 23946583, -8039892, -13265164, -1533858, -14197445, -2321576, 17649998, -250080 } + }, + { + { -9301088, -14193827, 30609526, -3049543, -25175069, -1283752, -15241566, -9525724, -2233253, 7662146 }, + { -17558673, 1763594, -33114336, 15908610, -30040870, -12174295, 7335080, -8472199, -3174674, 3440183 }, + { -19889700, -5977008, -24111293, -9688870, 10799743, -16571957, 40450, -4431835, 4862400, 1133 } + }, + { + { -32856209, -7873957, -5422389, 14860950, -16319031, 7956142, 7258061, 311861, -30594991, -7379421 }, + { -3773428, -1565936, 28985340, 7499440, 24445838, 9325937, 29727763, 16527196, 18278453, 15405622 }, + { -4381906, 8508652, -19898366, -3674424, -5984453, 15149970, -13313598, 843523, -21875062, 13626197 } + }, + { + { 2281448, -13487055, -10915418, -2609910, 1879358, 16164207, -10783882, 3953792, 13340839, 15928663 }, + { 31727126, -7179855, -18437503, -8283652, 2875793, -16390330, -25269894, -7014826, -23452306, 5964753 }, + { 4100420, -5959452, -17179337, 6017714, -18705837, 12227141, -26684835, 11344144, 2538215, -7570755 } + }, + { + { -9433605, 6123113, 11159803, -2156608, 30016280, 14966241, -20474983, 1485421, -629256, -15958862 }, + { -26804558, 4260919, 11851389, 9658551, -32017107, 16367492, -20205425, -13191288, 11659922, -11115118 }, + { 26180396, 10015009, -30844224, -8581293, 5418197, 9480663, 2231568, -10170080, 33100372, -1306171 } + }, + { + { 15121113, -5201871, -10389905, 15427821, -27509937, -15992507, 21670947, 4486675, -5931810, -14466380 }, + { 16166486, -9483733, -11104130, 6023908, -31926798, -1364923, 2340060, -16254968, -10735770, -10039824 }, + { 28042865, -3557089, -12126526, 12259706, -3717498, -6945899, 6766453, -8689599, 18036436, 5803270 } + } +}, +{ /* 23/31 */ + { + { -817581, 6763912, 11803561, 1585585, 10958447, -2671165, 23855391, 4598332, -6159431, -14117438 }, + { -31031306, -14256194, 17332029, -2383520, 31312682, -5967183, 696309, 50292, -20095739, 11763584 }, + { -594563, -2514283, -32234153, 12643980, 12650761, 14811489, 665117, -12613632, -19773211, -10713562 } + }, + { + { 30464590, -11262872, -4127476, -12734478, 19835327, -7105613, -24396175, 2075773, -17020157, 992471 }, + { 18357185, -6994433, 7766382, 16342475, -29324918, 411174, 14578841, 8080033, -11574335, -10601610 }, + { 19598397, 10334610, 12555054, 2555664, 18821899, -10339780, 21873263, 16014234, 26224780, 16452269 } + }, + { + { -30223925, 5145196, 5944548, 16385966, 3976735, 2009897, -11377804, -7618186, -20533829, 3698650 }, + { 14187449, 3448569, -10636236, -10810935, -22663880, -3433596, 7268410, -10890444, 27394301, 12015369 }, + { 19695761, 16087646, 28032085, 12999827, 6817792, 11427614, 20244189, -1312777, -13259127, -3402461 } + }, + { + { 30860103, 12735208, -1888245, -4699734, -16974906, 2256940, -8166013, 12298312, -8550524, -10393462 }, + { -5719826, -11245325, -1910649, 15569035, 26642876, -7587760, -5789354, -15118654, -4976164, 12651793 }, + { -2848395, 9953421, 11531313, -5282879, 26895123, -12697089, -13118820, -16517902, 9768698, -2533218 } + }, + { + { -24719459, 1894651, -287698, -4704085, 15348719, -8156530, 32767513, 12765450, 4940095, 10678226 }, + { 18860224, 15980149, -18987240, -1562570, -26233012, -11071856, -7843882, 13944024, -24372348, 16582019 }, + { -15504260, 4970268, -29893044, 4175593, -20993212, -2199756, -11704054, 15444560, -11003761, 7989037 } + }, + { + { 31490452, 5568061, -2412803, 2182383, -32336847, 4531686, -32078269, 6200206, -19686113, -14800171 }, + { -17308668, -15879940, -31522777, -2831, -32887382, 16375549, 8680158, -16371713, 28550068, -6857132 }, + { -28126887, -5688091, 16837845, -1820458, -6850681, 12700016, -30039981, 4364038, 1155602, 5988841 } + }, + { + { 21890435, -13272907, -12624011, 12154349, -7831873, 15300496, 23148983, -4470481, 24618407, 8283181 }, + { -33136107, -10512751, 9975416, 6841041, -31559793, 16356536, 3070187, -7025928, 1466169, 10740210 }, + { -1509399, -15488185, -13503385, -10655916, 32799044, 909394, -13938903, -5779719, -32164649, -15327040 } + }, + { + { 3960823, -14267803, -28026090, -15918051, -19404858, 13146868, 15567327, 951507, -3260321, -573935 }, + { 24740841, 5052253, -30094131, 8961361, 25877428, 6165135, -24368180, 14397372, -7380369, -6144105 }, + { -28888365, 3510803, -28103278, -1158478, -11238128, -10631454, -15441463, -14453128, -1625486, -6494814 } + } +}, +{ /* 24/31 */ + { + { 793299, -9230478, 8836302, -6235707, -27360908, -2369593, 33152843, -4885251, -9906200, -621852 }, + { 5666233, 525582, 20782575, -8038419, -24538499, 14657740, 16099374, 1468826, -6171428, -15186581 }, + { -4859255, -3779343, -2917758, -6748019, 7778750, 11688288, -30404353, -9871238, -1558923, -9863646 } + }, + { + { 10896332, -7719704, 824275, 472601, -19460308, 3009587, 25248958, 14783338, -30581476, -15757844 }, + { 10566929, 12612572, -31944212, 11118703, -12633376, 12362879, 21752402, 8822496, 24003793, 14264025 }, + { 27713862, -7355973, -11008240, 9227530, 27050101, 2504721, 23886875, -13117525, 13958495, -5732453 } + }, + { + { -23481610, 4867226, -27247128, 3900521, 29838369, -8212291, -31889399, -10041781, 7340521, -15410068 }, + { 4646514, -8011124, -22766023, -11532654, 23184553, 8566613, 31366726, -1381061, -15066784, -10375192 }, + { -17270517, 12723032, -16993061, 14878794, 21619651, -6197576, 27584817, 3093888, -8843694, 3849921 } + }, + { + { -9064912, 2103172, 25561640, -15125738, -5239824, 9582958, 32477045, -9017955, 5002294, -15550259 }, + { -12057553, -11177906, 21115585, -13365155, 8808712, -12030708, 16489530, 13378448, -25845716, 12741426 }, + { -5946367, 10645103, -30911586, 15390284, -3286982, -7118677, 24306472, 15852464, 28834118, -7646072 } + }, + { + { -17335748, -9107057, -24531279, 9434953, -8472084, -583362, -13090771, 455841, 20461858, 5491305 }, + { 13669248, -16095482, -12481974, -10203039, -14569770, -11893198, -24995986, 11293807, -28588204, -9421832 }, + { 28497928, 6272777, -33022994, 14470570, 8906179, -1225630, 18504674, -14165166, 29867745, -8795943 } + }, + { + { -16207023, 13517196, -27799630, -13697798, 24009064, -6373891, -6367600, -13175392, 22853429, -4012011 }, + { 24191378, 16712145, -13931797, 15217831, 14542237, 1646131, 18603514, -11037887, 12876623, -2112447 }, + { 17902668, 4518229, -411702, -2829247, 26878217, 5258055, -12860753, 608397, 16031844, 3723494 } + }, + { + { -28632773, 12763728, -20446446, 7577504, 33001348, -13017745, 17558842, -7872890, 23896954, -4314245 }, + { -20005381, -12011952, 31520464, 605201, 2543521, 5991821, -2945064, 7229064, -9919646, -8826859 }, + { 28816045, 298879, -28165016, -15920938, 19000928, -1665890, -12680833, -2949325, -18051778, -2082915 } + }, + { + { 16000882, -344896, 3493092, -11447198, -29504595, -13159789, 12577740, 16041268, -19715240, 7847707 }, + { 10151868, 10572098, 27312476, 7922682, 14825339, 4723128, -32855931, -6519018, -10020567, 3852848 }, + { -11430470, 15697596, -21121557, -4420647, 5386314, 15063598, 16514493, -15932110, 29330899, -15076224 } + } +}, +{ /* 25/31 */ + { + { -25499735, -4378794, -15222908, -6901211, 16615731, 2051784, 3303702, 15490, -27548796, 12314391 }, + { 15683520, -6003043, 18109120, -9980648, 15337968, -5997823, -16717435, 15921866, 16103996, -3731215 }, + { -23169824, -10781249, 13588192, -1628807, -3798557, -1074929, -19273607, 5402699, -29815713, -9841101 } + }, + { + { 23190676, 2384583, -32714340, 3462154, -29903655, -1529132, -11266856, 8911517, -25205859, 2739713 }, + { 21374101, -3554250, -33524649, 9874411, 15377179, 11831242, -33529904, 6134907, 4931255, 11987849 }, + { -7732, -2978858, -16223486, 7277597, 105524, -322051, -31480539, 13861388, -30076310, 10117930 } + }, + { + { -29501170, -10744872, -26163768, 13051539, -25625564, 5089643, -6325503, 6704079, 12890019, 15728940 }, + { -21972360, -11771379, -951059, -4418840, 14704840, 2695116, 903376, -10428139, 12885167, 8311031 }, + { -17516482, 5352194, 10384213, -13811658, 7506451, 13453191, 26423267, 4384730, 1888765, -5435404 } + }, + { + { -25817338, -3107312, -13494599, -3182506, 30896459, -13921729, -32251644, -12707869, -19464434, -3340243 }, + { -23607977, -2665774, -526091, 4651136, 5765089, 4618330, 6092245, 14845197, 17151279, -9854116 }, + { -24830458, -12733720, -15165978, 10367250, -29530908, -265356, 22825805, -7087279, -16866484, 16176525 } + }, + { + { -23583256, 6564961, 20063689, 3798228, -4740178, 7359225, 2006182, -10363426, -28746253, -10197509 }, + { -10626600, -4486402, -13320562, -5125317, 3432136, -6393229, 23632037, -1940610, 32808310, 1099883 }, + { 15030977, 5768825, -27451236, -2887299, -6427378, -15361371, -15277896, -6809350, 2051441, -15225865 } + }, + { + { -3362323, -7239372, 7517890, 9824992, 23555850, 295369, 5148398, -14154188, -22686354, 16633660 }, + { 4577086, -16752288, 13249841, -15304328, 19958763, -14537274, 18559670, -10759549, 8402478, -9864273 }, + { -28406330, -1051581, -26790155, -907698, -17212414, -11030789, 9453451, -14980072, 17983010, 9967138 } + }, + { + { -25762494, 6524722, 26585488, 9969270, 24709298, 1220360, -1677990, 7806337, 17507396, 3651560 }, + { -10420457, -4118111, 14584639, 15971087, -15768321, 8861010, 26556809, -5574557, -18553322, -11357135 }, + { 2839101, 14284142, 4029895, 3472686, 14402957, 12689363, -26642121, 8459447, -5605463, -7621941 } + }, + { + { -4839289, -3535444, 9744961, 2871048, 25113978, 3187018, -25110813, -849066, 17258084, -7977739 }, + { 18164541, -10595176, -17154882, -1542417, 19237078, -9745295, 23357533, -15217008, 26908270, 12150756 }, + { -30264870, -7647865, 5112249, -7036672, -1499807, -6974257, 43168, -5537701, -32302074, 16215819 } + } +}, +{ /* 26/31 */ + { + { -6898905, 9824394, -12304779, -4401089, -31397141, -6276835, 32574489, 12532905, -7503072, -8675347 }, + { -27343522, -16515468, -27151524, -10722951, 946346, 16291093, 254968, 7168080, 21676107, -1943028 }, + { 21260961, -8424752, -16831886, -11920822, -23677961, 3968121, -3651949, -6215466, -3556191, -7913075 } + }, + { + { 16544754, 13250366, -16804428, 15546242, -4583003, 12757258, -2462308, -8680336, -18907032, -9662799 }, + { -2415239, -15577728, 18312303, 4964443, -15272530, -12653564, 26820651, 16690659, 25459437, -4564609 }, + { -25144690, 11425020, 28423002, -11020557, -6144921, -15826224, 9142795, -2391602, -6432418, -1644817 } + }, + { + { -23104652, 6253476, 16964147, -3768872, -25113972, -12296437, -27457225, -16344658, 6335692, 7249989 }, + { -30333227, 13979675, 7503222, -12368314, -11956721, -4621693, -30272269, 2682242, 25993170, -12478523 }, + { 4364628, 5930691, 32304656, -10044554, -8054781, 15091131, 22857016, -10598955, 31820368, 15075278 } + }, + { + { 31879134, -8918693, 17258761, 90626, -8041836, -4917709, 24162788, -9650886, -17970238, 12833045 }, + { 19073683, 14851414, -24403169, -11860168, 7625278, 11091125, -19619190, 2074449, -9413939, 14905377 }, + { 24483667, -11935567, -2518866, -11547418, -1553130, 15355506, -25282080, 9253129, 27628530, -7555480 } + }, + { + { 17597607, 8340603, 19355617, 552187, 26198470, -3176583, 4593324, -9157582, -14110875, 15297016 }, + { 510886, 14337390, -31785257, 16638632, 6328095, 2713355, -20217417, -11864220, 8683221, 2921426 }, + { 18606791, 11874196, 27155355, -5281482, -24031742, 6265446, -25178240, -1278924, 4674690, 13890525 } + }, + { + { 13609624, 13069022, -27372361, -13055908, 24360586, 9592974, 14977157, 9835105, 4389687, 288396 }, + { 9922506, -519394, 13613107, 5883594, -18758345, -434263, -12304062, 8317628, 23388070, 16052080 }, + { 12720016, 11937594, -31970060, -5028689, 26900120, 8561328, -20155687, -11632979, -14754271, -10812892 } + }, + { + { 15961858, 14150409, 26716931, -665832, -22794328, 13603569, 11829573, 7467844, -28822128, 929275 }, + { 11038231, -11582396, -27310482, -7316562, -10498527, -16307831, -23479533, -9371869, -21393143, 2465074 }, + { 20017163, -4323226, 27915242, 1529148, 12396362, 15675764, 13817261, -9658066, 2463391, -4622140 } + }, + { + { -16358878, -12663911, -12065183, 4996454, -1256422, 1073572, 9583558, 12851107, 4003896, 12673717 }, + { -1731589, -15155870, -3262930, 16143082, 19294135, 13385325, 14741514, -9103726, 7903886, 2348101 }, + { 24536016, -16515207, 12715592, -3862155, 1511293, 10047386, -3842346, -7129159, -28377538, 10048127 } + } +}, +{ /* 27/31 */ + { + { -12622226, -6204820, 30718825, 2591312, -10617028, 12192840, 18873298, -7297090, -32297756, 15221632 }, + { -26478122, -11103864, 11546244, -1852483, 9180880, 7656409, -21343950, 2095755, 29769758, 6593415 }, + { -31994208, -2907461, 4176912, 3264766, 12538965, -868111, 26312345, -6118678, 30958054, 8292160 } + }, + { + { 31429822, -13959116, 29173532, 15632448, 12174511, -2760094, 32808831, 3977186, 26143136, -3148876 }, + { 22648901, 1402143, -22799984, 13746059, 7936347, 365344, -8668633, -1674433, -3758243, -2304625 }, + { -15491917, 8012313, -2514730, -12702462, -23965846, -10254029, -1612713, -1535569, -16664475, 8194478 } + }, + { + { 27338066, -7507420, -7414224, 10140405, -19026427, -6589889, 27277191, 8855376, 28572286, 3005164 }, + { 26287124, 4821776, 25476601, -4145903, -3764513, -15788984, -18008582, 1182479, -26094821, -13079595 }, + { -7171154, 3178080, 23970071, 6201893, -17195577, -4489192, -21876275, -13982627, 32208683, -1198248 } + }, + { + { -16657702, 2817643, -10286362, 14811298, 6024667, 13349505, -27315504, -10497842, -27672585, -11539858 }, + { 15941029, -9405932, -21367050, 8062055, 31876073, -238629, -15278393, -1444429, 15397331, -4130193 }, + { 8934485, -13485467, -23286397, -13423241, -32446090, 14047986, 31170398, -1441021, -27505566, 15087184 } + }, + { + { -18357243, -2156491, 24524913, -16677868, 15520427, -6360776, -15502406, 11461896, 16788528, -5868942 }, + { -1947386, 16013773, 21750665, 3714552, -17401782, -16055433, -3770287, -10323320, 31322514, -11615635 }, + { 21426655, -5650218, -13648287, -5347537, -28812189, -4920970, -18275391, -14621414, 13040862, -12112948 } + }, + { + { 11293895, 12478086, -27136401, 15083750, -29307421, 14748872, 14555558, -13417103, 1613711, 4896935 }, + { -25894883, 15323294, -8489791, -8057900, 25967126, -13425460, 2825960, -4897045, -23971776, -11267415 }, + { -15924766, -5229880, -17443532, 6410664, 3622847, 10243618, 20615400, 12405433, -23753030, -8436416 } + }, + { + { -7091295, 12556208, -20191352, 9025187, -17072479, 4333801, 4378436, 2432030, 23097949, -566018 }, + { 4565804, -16025654, 20084412, -7842817, 1724999, 189254, 24767264, 10103221, -18512313, 2424778 }, + { 366633, -11976806, 8173090, -6890119, 30788634, 5745705, -7168678, 1344109, -3642553, 12412659 } + }, + { + { -24001791, 7690286, 14929416, -168257, -32210835, -13412986, 24162697, -15326504, -3141501, 11179385 }, + { 18289522, -14724954, 8056945, 16430056, -21729724, 7842514, -6001441, -1486897, -18684645, -11443503 }, + { 476239, 6601091, -6152790, -9723375, 17503545, -4863900, 27672959, 13403813, 11052904, 5219329 } + } +}, +{ /* 28/31 */ + { + { 20678546, -8375738, -32671898, 8849123, -5009758, 14574752, 31186971, -3973730, 9014762, -8579056 }, + { -13644050, -10350239, -15962508, 5075808, -1514661, -11534600, -33102500, 9160280, 8473550, -3256838 }, + { 24900749, 14435722, 17209120, -15292541, -22592275, 9878983, -7689309, -16335821, -24568481, 11788948 } + }, + { + { -3118155, -11395194, -13802089, 14797441, 9652448, -6845904, -20037437, 10410733, -24568470, -1458691 }, + { -15659161, 16736706, -22467150, 10215878, -9097177, 7563911, 11871841, -12505194, -18513325, 8464118 }, + { -23400612, 8348507, -14585951, -861714, -3950205, -6373419, 14325289, 8628612, 33313881, -8370517 } + }, + { + { -20186973, -4967935, 22367356, 5271547, -1097117, -4788838, -24805667, -10236854, -8940735, -5818269 }, + { -6948785, -1795212, -32625683, -16021179, 32635414, -7374245, 15989197, -12838188, 28358192, -4253904 }, + { -23561781, -2799059, -32351682, -1661963, -9147719, 10429267, -16637684, 4072016, -5351664, 5596589 } + }, + { + { -28236598, -3390048, 12312896, 6213178, 3117142, 16078565, 29266239, 2557221, 1768301, 15373193 }, + { -7243358, -3246960, -4593467, -7553353, -127927, -912245, -1090902, -4504991, -24660491, 3442910 }, + { -30210571, 5124043, 14181784, 8197961, 18964734, -11939093, 22597931, 7176455, -18585478, 13365930 } + }, + { + { -7877390, -1499958, 8324673, 4690079, 6261860, 890446, 24538107, -8570186, -9689599, -3031667 }, + { 25008904, -10771599, -4305031, -9638010, 16265036, 15721635, 683793, -11823784, 15723479, -15163481 }, + { -9660625, 12374379, -27006999, -7026148, -7724114, -12314514, 11879682, 5400171, 519526, -1235876 } + }, + { + { 22258397, -16332233, -7869817, 14613016, -22520255, -2950923, -20353881, 7315967, 16648397, 7605640 }, + { -8081308, -8464597, -8223311, 9719710, 19259459, -15348212, 23994942, -5281555, -9468848, 4763278 }, + { -21699244, 9220969, -15730624, 1084137, -25476107, -2852390, 31088447, -7764523, -11356529, 728112 } + }, + { + { 26047220, -11751471, -6900323, -16521798, 24092068, 9158119, -4273545, -12555558, -29365436, -5498272 }, + { 17510331, -322857, 5854289, 8403524, 17133918, -3112612, -28111007, 12327945, 10750447, 10014012 }, + { -10312768, 3936952, 9156313, -8897683, 16498692, -994647, -27481051, -666732, 3424691, 7540221 } + }, + { + { 30322361, -6964110, 11361005, -4143317, 7433304, 4989748, -7071422, -16317219, -9244265, 15258046 }, + { 13054562, -2779497, 19155474, 469045, -12482797, 4566042, 5631406, 2711395, 1062915, -5136345 }, + { -19240248, -11254599, -29509029, -7499965, -5835763, 13005411, -6066489, 12194497, 32960380, 1459310 } + } +}, +{ /* 29/31 */ + { + { 19852034, 7027924, 23669353, 10020366, 8586503, -6657907, 394197, -6101885, 18638003, -11174937 }, + { 31395534, 15098109, 26581030, 8030562, -16527914, -5007134, 9012486, -7584354, -6643087, -5442636 }, + { -9192165, -2347377, -1997099, 4529534, 25766844, 607986, -13222, 9677543, -32294889, -6456008 } + }, + { + { -2444496, -149937, 29348902, 8186665, 1873760, 12489863, -30934579, -7839692, -7852844, -8138429 }, + { -15236356, -15433509, 7766470, 746860, 26346930, -10221762, -27333451, 10754588, -9431476, 5203576 }, + { 31834314, 14135496, -770007, 5159118, 20917671, -16768096, -7467973, -7337524, 31809243, 7347066 } + }, + { + { -9606723, -11874240, 20414459, 13033986, 13716524, -11691881, 19797970, -12211255, 15192876, -2087490 }, + { -12663563, -2181719, 1168162, -3804809, 26747877, -14138091, 10609330, 12694420, 33473243, -13382104 }, + { 33184999, 11180355, 15832085, -11385430, -1633671, 225884, 15089336, -11023903, -6135662, 14480053 } + }, + { + { 31308717, -5619998, 31030840, -1897099, 15674547, -6582883, 5496208, 13685227, 27595050, 8737275 }, + { -20318852, -15150239, 10933843, -16178022, 8335352, -7546022, -31008351, -12610604, 26498114, 66511 }, + { 22644454, -8761729, -16671776, 4884562, -3105614, -13559366, 30540766, -4286747, -13327787, -7515095 } + }, + { + { -28017847, 9834845, 18617207, -2681312, -3401956, -13307506, 8205540, 13585437, -17127465, 15115439 }, + { 23711543, -672915, 31206561, -8362711, 6164647, -9709987, -33535882, -1426096, 8236921, 16492939 }, + { -23910559, -13515526, -26299483, -4503841, 25005590, -7687270, 19574902, 10071562, 6708380, -6222424 } + }, + { + { 2101391, -4930054, 19702731, 2367575, -15427167, 1047675, 5301017, 9328700, 29955601, -11678310 }, + { 3096359, 9271816, -21620864, -15521844, -14847996, -7592937, -25892142, -12635595, -9917575, 6216608 }, + { -32615849, 338663, -25195611, 2510422, -29213566, -13820213, 24822830, -6146567, -26767480, 7525079 } + }, + { + { -23066649, -13985623, 16133487, -7896178, -3389565, 778788, -910336, -2782495, -19386633, 11994101 }, + { 21691500, -13624626, -641331, -14367021, 3285881, -3483596, -25064666, 9718258, -7477437, 13381418 }, + { 18445390, -4202236, 14979846, 11622458, -1727110, -3582980, 23111648, -6375247, 28535282, 15779576 } + }, + { + { 30098053, 3089662, -9234387, 16662135, -21306940, 11308411, -14068454, 12021730, 9955285, -16303356 }, + { 9734894, -14576830, -7473633, -9138735, 2060392, 11313496, -18426029, 9924399, 20194861, 13380996 }, + { -26378102, -7965207, -22167821, 15789297, -18055342, -6168792, -1984914, 15707771, 26342023, 10146099 } + } +}, +{ /* 30/31 */ + { + { -26016874, -219943, 21339191, -41388, 19745256, -2878700, -29637280, 2227040, 21612326, -545728 }, + { -13077387, 1184228, 23562814, -5970442, -20351244, -6348714, 25764461, 12243797, -20856566, 11649658 }, + { -10031494, 11262626, 27384172, 2271902, 26947504, -15997771, 39944, 6114064, 33514190, 2333242 } + }, + { + { -21433588, -12421821, 8119782, 7219913, -21830522, -9016134, -6679750, -12670638, 24350578, -13450001 }, + { -4116307, -11271533, -23886186, 4843615, -30088339, 690623, -31536088, -10406836, 8317860, 12352766 }, + { 18200138, -14475911, -33087759, -2696619, -23702521, -9102511, -23552096, -2287550, 20712163, 6719373 } + }, + { + { 26656208, 6075253, -7858556, 1886072, -28344043, 4262326, 11117530, -3763210, 26224235, -3297458 }, + { -17168938, -14854097, -3395676, -16369877, -19954045, 14050420, 21728352, 9493610, 18620611, -16428628 }, + { -13323321, 13325349, 11432106, 5964811, 18609221, 6062965, -5269471, -9725556, -30701573, -16479657 } + }, + { + { -23860538, -11233159, 26961357, 1640861, -32413112, -16737940, 12248509, -5240639, 13735342, 1934062 }, + { 25089769, 6742589, 17081145, -13406266, 21909293, -16067981, -15136294, -3765346, -21277997, 5473616 }, + { 31883677, -7961101, 1083432, -11572403, 22828471, 13290673, -7125085, 12469656, 29111212, -5451014 } + }, + { + { 24244947, -15050407, -26262976, 2791540, -14997599, 16666678, 24367466, 6388839, -10295587, 452383 }, + { -25640782, -3417841, 5217916, 16224624, 19987036, -4082269, -24236251, -5915248, 15766062, 8407814 }, + { -20406999, 13990231, 15495425, 16395525, 5377168, 15166495, -8917023, -4388953, -8067909, 2276718 } + }, + { + { 30157918, 12924066, -17712050, 9245753, 19895028, 3368142, -23827587, 5096219, 22740376, -7303417 }, + { 2041139, -14256350, 7783687, 13876377, -25946985, -13352459, 24051124, 13742383, -15637599, 13295222 }, + { 33338237, -8505733, 12532113, 7977527, 9106186, -1715251, -17720195, -4612972, -4451357, -14669444 } + }, + { + { -20045281, 5454097, -14346548, 6447146, 28862071, 1883651, -2469266, -4141880, 7770569, 9620597 }, + { 23208068, 7979712, 33071466, 8149229, 1758231, -10834995, 30945528, -1694323, -33502340, -14767970 }, + { 1439958, -16270480, -1079989, -793782, 4625402, 10647766, -5043801, 1220118, 30494170, -11440799 } + }, + { + { -5037580, -13028295, -2970559, -3061767, 15640974, -6701666, -26739026, 926050, -1684339, -13333647 }, + { 13908495, -3549272, 30919928, -6273825, -21521863, 7989039, 9021034, 9078865, 3353509, 4033511 }, + { -29663431, -15113610, 32259991, -344482, 24295849, -12912123, 23161163, 8839127, 27485041, 7356032 } + } +}, +{ /* 31/31 */ + { + { 9661027, 705443, 11980065, -5370154, -1628543, 14661173, -6346142, 2625015, 28431036, -16771834 }, + { -23839233, -8311415, -25945511, 7480958, -17681669, -8354183, -22545972, 14150565, 15970762, 4099461 }, + { 29262576, 16756590, 26350592, -8793563, 8529671, -11208050, 13617293, -9937143, 11465739, 8317062 } + }, + { + { -25493081, -6962928, 32500200, -9419051, -23038724, -2302222, 14898637, 3848455, 20969334, -5157516 }, + { -20384450, -14347713, -18336405, 13884722, -33039454, 2842114, -21610826, -3649888, 11177095, 14989547 }, + { -24496721, -11716016, 16959896, 2278463, 12066309, 10137771, 13515641, 2581286, -28487508, 9930240 } + }, + { + { -17751622, -2097826, 16544300, -13009300, -15914807, -14949081, 18345767, -13403753, 16291481, -5314038 }, + { -33229194, 2553288, 32678213, 9875984, 8534129, 6889387, -9676774, 6957617, 4368891, 9788741 }, + { 16660756, 7281060, -10830758, 12911820, 20108584, -8101676, -21722536, -8613148, 16250552, -11111103 } + }, + { + { -19765507, 2390526, -16551031, 14161980, 1905286, 6414907, 4689584, 10604807, -30190403, 4782747 }, + { -1354539, 14736941, -7367442, -13292886, 7710542, -14155590, -9981571, 4383045, 22546403, 437323 }, + { 31665577, -12180464, -16186830, 1491339, -18368625, 3294682, 27343084, 2786261, -30633590, -14097016 } + }, + { + { -14467279, -683715, -33374107, 7448552, 19294360, 14334329, -19690631, 2355319, -19284671, -6114373 }, + { 15121312, -15796162, 6377020, -6031361, -10798111, -12957845, 18952177, 15496498, -29380133, 11754228 }, + { -2637277, -13483075, 8488727, -14303896, 12728761, -1622493, 7141596, 11724556, 22761615, -10134141 } + }, + { + { 16918416, 11729663, -18083579, 3022987, -31015732, -13339659, -28741185, -12227393, 32851222, 11717399 }, + { 11166634, 7338049, -6722523, 4531520, -29468672, -7302055, 31474879, 3483633, -1193175, -4030831 }, + { -185635, 9921305, 31456609, -13536438, -12013818, 13348923, 33142652, 6546660, -19985279, -3948376 } + }, + { + { -32460596, 11266712, -11197107, -7899103, 31703694, 3855903, -8537131, -12833048, -30772034, -15486313 }, + { -18006477, 12709068, 3991746, -6479188, -21491523, -10550425, -31135347, -16049879, 10928917, 3011958 }, + { -6957757, -15594337, 31696059, 334240, 29576716, 14796075, -30831056, -12805180, 18008031, 10258577 } + }, + { + { -22448644, 15655569, 7018479, -4410003, -30314266, -1201591, -1853465, 1367120, 25127874, 6671743 }, + { 29701166, -14373934, -10878120, 9279288, -17568, 13127210, 21382910, 11042292, 25838796, 4642684 }, + { -20430234, 14955537, -24126347, 8124619, -5369288, -5990470, 30468147, -13900640, 18423289, 4177476 } + } +} diff --git a/contrib/sodium/fe_25_5/base2.h b/contrib/sodium/fe_25_5/base2.h new file mode 100644 index 000000000..3c0996ae0 --- /dev/null +++ b/contrib/sodium/fe_25_5/base2.h @@ -0,0 +1,63 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +{ + { 25967493, -14356035, 29566456, 3660896, -12694345, 4014787, 27544626, -11754271, -6079156, 2047605 }, + { -12545711, 934262, -2722910, 3049990, -727428, 9406986, 12720692, 5043384, 19500929, -15469378 }, + { -8738181, 4489570, 9688441, -14785194, 10184609, -12363380, 29287919, 11864899, -24514362, -4438546 } +}, +{ + { 15636291, -9688557, 24204773, -7912398, 616977, -16685262, 27787600, -14772189, 28944400, -1550024 }, + { 16568933, 4717097, -11556148, -1102322, 15682896, -11807043, 16354577, -11775962, 7689662, 11199574 }, + { 30464156, -5976125, -11779434, -15670865, 23220365, 15915852, 7512774, 10017326, -17749093, -9920357 } +}, +{ + { 10861363, 11473154, 27284546, 1981175, -30064349, 12577861, 32867885, 14515107, -15438304, 10819380 }, + { 4708026, 6336745, 20377586, 9066809, -11272109, 6594696, -25653668, 12483688, -12668491, 5581306 }, + { 19563160, 16186464, -29386857, 4097519, 10237984, -4348115, 28542350, 13850243, -23678021, -15815942 } +}, +{ + { 5153746, 9909285, 1723747, -2777874, 30523605, 5516873, 19480852, 5230134, -23952439, -15175766 }, + { -30269007, -3463509, 7665486, 10083793, 28475525, 1649722, 20654025, 16520125, 30598449, 7715701 }, + { 28881845, 14381568, 9657904, 3680757, -20181635, 7843316, -31400660, 1370708, 29794553, -1409300 } +}, +{ + { -22518993, -6692182, 14201702, -8745502, -23510406, 8844726, 18474211, -1361450, -13062696, 13821877 }, + { -6455177, -7839871, 3374702, -4740862, -27098617, -10571707, 31655028, -7212327, 18853322, -14220951 }, + { 4566830, -12963868, -28974889, -12240689, -7602672, -2830569, -8514358, -10431137, 2207753, -3209784 } +}, +{ + { -25154831, -4185821, 29681144, 7868801, -6854661, -9423865, -12437364, -663000, -31111463, -16132436 }, + { 25576264, -2703214, 7349804, -11814844, 16472782, 9300885, 3844789, 15725684, 171356, 6466918 }, + { 23103977, 13316479, 9739013, -16149481, 817875, -15038942, 8965339, -14088058, -30714912, 16193877 } +}, +{ + { -33521811, 3180713, -2394130, 14003687, -16903474, -16270840, 17238398, 4729455, -18074513, 9256800 }, + { -25182317, -4174131, 32336398, 5036987, -21236817, 11360617, 22616405, 9761698, -19827198, 630305 }, + { -13720693, 2639453, -24237460, -7406481, 9494427, -5774029, -6554551, -15960994, -2449256, -14291300 } +}, +{ + { -3151181, -5046075, 9282714, 6866145, -31907062, -863023, -18940575, 15033784, 25105118, -7894876 }, + { -24326370, 15950226, -31801215, -14592823, -11662737, -5090925, 1573892, -2625887, 2198790, -15804619 }, + { -3099351, 10324967, -2241613, 7453183, -5446979, -2735503, -13812022, -16236442, -32461234, -12290683 } +} diff --git a/contrib/sodium/fe_25_5/constants.h b/contrib/sodium/fe_25_5/constants.h new file mode 100644 index 000000000..9c6123f8a --- /dev/null +++ b/contrib/sodium/fe_25_5/constants.h @@ -0,0 +1,44 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +/* 37095705934669439343138083508754565189542113879843219016388785533085940283555 */ + +static const fe25519 d = { + -10913610, 13857413, -15372611, 6949391, 114729, -8787816, -6275908, -3247719, -18696448, -12055116 +}; + +/* 2 * d = + * 16295367250680780974490674513165176452449235426866156013048779062215315747161 + */ +static const fe25519 d2 = { + -21827239, -5839606, -30745221, 13898782, 229458, 15978800, -12551817, -6495438, 29715968, 9444199 }; + +/* sqrt(-1) */ +static const fe25519 sqrtm1 = { + -32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482 +}; + +/* A = 486662 */ +static const fe25519 curve25519_A = { + 486662, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; \ No newline at end of file diff --git a/contrib/sodium/fe_25_5/fe.h b/contrib/sodium/fe_25_5/fe.h new file mode 100644 index 000000000..53b5e1b4f --- /dev/null +++ b/contrib/sodium/fe_25_5/fe.h @@ -0,0 +1,240 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +/* + Ignores top bit of h. + */ + +void fe25519_frombytes(fe25519 h, const unsigned char *s) +{ + int64_t h0 = load_4(s); + int64_t h1 = load_3(s + 4) << 6; + int64_t h2 = load_3(s + 7) << 5; + int64_t h3 = load_3(s + 10) << 3; + int64_t h4 = load_3(s + 13) << 2; + int64_t h5 = load_4(s + 16); + int64_t h6 = load_3(s + 20) << 7; + int64_t h7 = load_3(s + 23) << 5; + int64_t h8 = load_3(s + 26) << 4; + int64_t h9 = (load_3(s + 29) & 8388607) << 2; + + int64_t carry0; + int64_t carry1; + int64_t carry2; + int64_t carry3; + int64_t carry4; + int64_t carry5; + int64_t carry6; + int64_t carry7; + int64_t carry8; + int64_t carry9; + + carry9 = (h9 + (int64_t)(1L << 24)) >> 25; + h0 += carry9 * 19; + h9 -= carry9 * ((uint64_t) 1L << 25); + carry1 = (h1 + (int64_t)(1L << 24)) >> 25; + h2 += carry1; + h1 -= carry1 * ((uint64_t) 1L << 25); + carry3 = (h3 + (int64_t)(1L << 24)) >> 25; + h4 += carry3; + h3 -= carry3 * ((uint64_t) 1L << 25); + carry5 = (h5 + (int64_t)(1L << 24)) >> 25; + h6 += carry5; + h5 -= carry5 * ((uint64_t) 1L << 25); + carry7 = (h7 + (int64_t)(1L << 24)) >> 25; + h8 += carry7; + h7 -= carry7 * ((uint64_t) 1L << 25); + + carry0 = (h0 + (int64_t)(1L << 25)) >> 26; + h1 += carry0; + h0 -= carry0 * ((uint64_t) 1L << 26); + carry2 = (h2 + (int64_t)(1L << 25)) >> 26; + h3 += carry2; + h2 -= carry2 * ((uint64_t) 1L << 26); + carry4 = (h4 + (int64_t)(1L << 25)) >> 26; + h5 += carry4; + h4 -= carry4 * ((uint64_t) 1L << 26); + carry6 = (h6 + (int64_t)(1L << 25)) >> 26; + h7 += carry6; + h6 -= carry6 * ((uint64_t) 1L << 26); + carry8 = (h8 + (int64_t)(1L << 25)) >> 26; + h9 += carry8; + h8 -= carry8 * ((uint64_t) 1L << 26); + + h[0] = (int32_t) h0; + h[1] = (int32_t) h1; + h[2] = (int32_t) h2; + h[3] = (int32_t) h3; + h[4] = (int32_t) h4; + h[5] = (int32_t) h5; + h[6] = (int32_t) h6; + h[7] = (int32_t) h7; + h[8] = (int32_t) h8; + h[9] = (int32_t) h9; +} + +/* + Preconditions: + |h| bounded by 1.1*2^26,1.1*2^25,1.1*2^26,1.1*2^25,etc. + + Write p=2^255-19; q=floor(h/p). + Basic claim: q = floor(2^(-255)(h + 19 2^(-25)h9 + 2^(-1))). + + Proof: + Have |h|<=p so |q|<=1 so |19^2 2^(-255) q|<1/4. + Also have |h-2^230 h9|<2^231 so |19 2^(-255)(h-2^230 h9)|<1/4. + + Write y=2^(-1)-19^2 2^(-255)q-19 2^(-255)(h-2^230 h9). + Then 0> 25; + q = (h0 + q) >> 26; + q = (h1 + q) >> 25; + q = (h2 + q) >> 26; + q = (h3 + q) >> 25; + q = (h4 + q) >> 26; + q = (h5 + q) >> 25; + q = (h6 + q) >> 26; + q = (h7 + q) >> 25; + q = (h8 + q) >> 26; + q = (h9 + q) >> 25; + + /* Goal: Output h-(2^255-19)q, which is between 0 and 2^255-20. */ + h0 += 19 * q; + /* Goal: Output h-2^255 q, which is between 0 and 2^255-20. */ + + carry0 = h0 >> 26; + h1 += carry0; + h0 -= carry0 * ((uint32_t) 1L << 26); + carry1 = h1 >> 25; + h2 += carry1; + h1 -= carry1 * ((uint32_t) 1L << 25); + carry2 = h2 >> 26; + h3 += carry2; + h2 -= carry2 * ((uint32_t) 1L << 26); + carry3 = h3 >> 25; + h4 += carry3; + h3 -= carry3 * ((uint32_t) 1L << 25); + carry4 = h4 >> 26; + h5 += carry4; + h4 -= carry4 * ((uint32_t) 1L << 26); + carry5 = h5 >> 25; + h6 += carry5; + h5 -= carry5 * ((uint32_t) 1L << 25); + carry6 = h6 >> 26; + h7 += carry6; + h6 -= carry6 * ((uint32_t) 1L << 26); + carry7 = h7 >> 25; + h8 += carry7; + h7 -= carry7 * ((uint32_t) 1L << 25); + carry8 = h8 >> 26; + h9 += carry8; + h8 -= carry8 * ((uint32_t) 1L << 26); + carry9 = h9 >> 25; + h9 -= carry9 * ((uint32_t) 1L << 25); + + h[0] = h0; + h[1] = h1; + h[2] = h2; + h[3] = h3; + h[4] = h4; + h[5] = h5; + h[6] = h6; + h[7] = h7; + h[8] = h8; + h[9] = h9; +} + +/* + Goal: Output h0+...+2^255 h10-2^255 q, which is between 0 and 2^255-20. + Have h0+...+2^230 h9 between 0 and 2^255-1; + evidently 2^255 h10-2^255 q = 0. + + Goal: Output h0+...+2^230 h9. + */ + +void fe25519_tobytes(unsigned char *s, const fe25519 h) +{ + fe25519 t; + + fe25519_reduce(t, h); + s[0] = t[0] >> 0; + s[1] = t[0] >> 8; + s[2] = t[0] >> 16; + s[3] = (t[0] >> 24) | (t[1] * ((uint32_t) 1 << 2)); + s[4] = t[1] >> 6; + s[5] = t[1] >> 14; + s[6] = (t[1] >> 22) | (t[2] * ((uint32_t) 1 << 3)); + s[7] = t[2] >> 5; + s[8] = t[2] >> 13; + s[9] = (t[2] >> 21) | (t[3] * ((uint32_t) 1 << 5)); + s[10] = t[3] >> 3; + s[11] = t[3] >> 11; + s[12] = (t[3] >> 19) | (t[4] * ((uint32_t) 1 << 6)); + s[13] = t[4] >> 2; + s[14] = t[4] >> 10; + s[15] = t[4] >> 18; + s[16] = t[5] >> 0; + s[17] = t[5] >> 8; + s[18] = t[5] >> 16; + s[19] = (t[5] >> 24) | (t[6] * ((uint32_t) 1 << 1)); + s[20] = t[6] >> 7; + s[21] = t[6] >> 15; + s[22] = (t[6] >> 23) | (t[7] * ((uint32_t) 1 << 3)); + s[23] = t[7] >> 5; + s[24] = t[7] >> 13; + s[25] = (t[7] >> 21) | (t[8] * ((uint32_t) 1 << 4)); + s[26] = t[8] >> 4; + s[27] = t[8] >> 12; + s[28] = (t[8] >> 20) | (t[9] * ((uint32_t) 1 << 6)); + s[29] = t[9] >> 2; + s[30] = t[9] >> 10; + s[31] = t[9] >> 18; +} diff --git a/contrib/sodium/keypair.cpp b/contrib/sodium/keypair.cpp new file mode 100644 index 000000000..f02dbd0f6 --- /dev/null +++ b/contrib/sodium/keypair.cpp @@ -0,0 +1,74 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#include + +#include "sha512EL.h" +#include "vrf.h" +#include "ed25519_ref10.h" +#include "randombytes.h" + +namespace sodium { + +int crypto_vrf_ietfdraft03_keypair(unsigned char pk[crypto_vrf_ietfdraft03_PUBLICKEYBYTES], + unsigned char sk[crypto_vrf_ietfdraft03_SECRETKEYBYTES]) +{ + unsigned char seed[32]; + + randombytes_buf(seed, sizeof seed); + crypto_vrf_ietfdraft03_keypair_from_seed(pk, sk, seed); + memset(seed, 0, sizeof seed); + + return 0; +} + +int crypto_vrf_ietfdraft03_keypair_from_seed(unsigned char pk[crypto_vrf_ietfdraft03_PUBLICKEYBYTES], + unsigned char sk[crypto_vrf_ietfdraft03_SECRETKEYBYTES], + const unsigned char seed[crypto_vrf_ietfdraft03_SEEDBYTES]) +{ + ge25519_p3 A; + + crypto_hash_sha512(sk, seed, 32); + sk[0] &= 248; + sk[31] &= 127; + sk[31] |= 64; + ge25519_scalarmult_base(&A, sk); + ge25519_p3_tobytes(pk, &A); + memmove(sk, seed, 32); + memmove(sk + 32, pk, 32); + + return 0; +} + +void crypto_vrf_ietfdraft03_sk_to_pk(unsigned char pk[crypto_vrf_ietfdraft03_PUBLICKEYBYTES], + const unsigned char skpk[crypto_vrf_ietfdraft03_SECRETKEYBYTES]) +{ + memmove(pk, skpk+32, 32); +} + +void crypto_vrf_ietfdraft03_sk_to_seed(unsigned char seed[crypto_vrf_ietfdraft03_SEEDBYTES], + const unsigned char skpk[crypto_vrf_ietfdraft03_SECRETKEYBYTES]) +{ + memmove(seed, skpk, 32); +} +} \ No newline at end of file diff --git a/contrib/sodium/prove.cpp b/contrib/sodium/prove.cpp new file mode 100644 index 000000000..196838353 --- /dev/null +++ b/contrib/sodium/prove.cpp @@ -0,0 +1,159 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#include + +#include "sha512EL.h" +#include "vrf.h" +#include "ed25519_ref10.h" +#include "convert.h" + +/* Utility function to convert a "secret key" (32-byte seed || 32-byte PK) + * into the public point Y, the private saclar x, and truncated hash of the + * seed to be used later in nonce generation. + * Return 0 on success, -1 on failure decoding the public point Y. + */ + +namespace sodium { + +static int +vrf_expand_sk(ge25519_p3 *Y_point, unsigned char x_scalar[32], + unsigned char truncated_hashed_sk_string[32], const unsigned char skpk[64]) +{ + unsigned char h[64]; + + crypto_hash_sha512(h, skpk, 32); + h[0] &= 248; + h[31] &= 127; + h[31] |= 64; + memmove(x_scalar, h, 32); + memmove(truncated_hashed_sk_string, h + 32, 32); + memset(h, 0, 64); + + return _vrf_ietfdraft03_string_to_point(Y_point, skpk+32); +} + + +/* Deterministically generate a (secret) nonce to be used in a proof. + * Specified in draft spec section 5.4.2.2. + * Note: In the spec, this subroutine computes truncated_hashed_sk_string + * Here we instead takes it as an argument, and we compute it in vrf_expand_sk + */ +static void +vrf_nonce_generation(unsigned char k_scalar[32], + const unsigned char truncated_hashed_sk_string[32], + const unsigned char h_string[32]) +{ + crypto_hash_sha512_state hs; + unsigned char k_string[64]; + + /* k_string = SHA512(truncated_hashed_sk_string || h_string) */ + crypto_hash_sha512_init(&hs); + crypto_hash_sha512_update(&hs, truncated_hashed_sk_string, 32); + crypto_hash_sha512_update(&hs, h_string, 32); + crypto_hash_sha512_final(&hs, k_string); + + sc25519_reduce(k_string); /* k_string[0:32] = string_to_int(k_string) mod q */ + memmove(k_scalar, k_string, 32); + + memset(k_string, 0, sizeof k_string); +} + +/* Construct a proof for a message alpha per draft spec section 5.1. + * Takes in a secret scalar x, a public point Y, and a secret string + * truncated_hashed_sk that is used in nonce generation. + * These are computed from the secret key using the expand_sk function. + * Constant time in everything except alphalen (the length of the message) + */ +static void +vrf_prove(unsigned char pi[80], const ge25519_p3 *Y_point, + const unsigned char x_scalar[32], + const unsigned char truncated_hashed_sk_string[32], + const unsigned char *alpha, unsigned long long alphalen) +{ + /* c fits in 16 bytes, but we store it in a 32-byte array because + * sc25519_muladd expects a 32-byte scalar */ + unsigned char h_string[32], k_scalar[32], c_scalar[32]; + ge25519_p3 H_point, Gamma_point, kB_point, kH_point; + + _vrf_ietfdraft03_hash_to_curve_elligator2_25519(h_string, Y_point, alpha, alphalen); + ge25519_frombytes(&H_point, h_string); + + ge25519_scalarmult(&Gamma_point, x_scalar, &H_point); /* Gamma = x*H */ + vrf_nonce_generation(k_scalar, truncated_hashed_sk_string, h_string); + ge25519_scalarmult_base(&kB_point, k_scalar); /* compute k*B */ + ge25519_scalarmult(&kH_point, k_scalar, &H_point); /* compute k*H */ + + /* c = ECVRF_hash_points(h, gamma, k*B, k*H) + * (writes only to the first 16 bytes of c_scalar */ + _vrf_ietfdraft03_hash_points(c_scalar, &H_point, &Gamma_point, &kB_point, &kH_point); + memset(c_scalar+16, 0, 16); /* zero the remaining 16 bytes of c_scalar */ + + /* output pi */ + _vrf_ietfdraft03_point_to_string(pi, &Gamma_point); /* pi[0:32] = point_to_string(Gamma) */ + memmove(pi+32, c_scalar, 16); /* pi[32:48] = c (16 bytes) */ + sc25519_muladd(pi+48, c_scalar, x_scalar, k_scalar); /* pi[48:80] = s = c*x + k (mod q) */ + + memset(k_scalar, 0, sizeof k_scalar); /* k must remain secret */ + /* erase other non-sensitive intermediate state for good measure */ + memset(h_string, 0, sizeof h_string); + memset(c_scalar, 0, sizeof c_scalar); + memset(&H_point, 0, sizeof H_point); + memset(&Gamma_point, 0, sizeof Gamma_point); + memset(&kB_point, 0, sizeof kB_point); + memset(&kH_point, 0, sizeof kH_point); +} + +/* Construct a VRF proof given a secret key and a message. + * + * The "secret key" is 64 bytes long -- 32 byte secret seed concatenated + * with 32 byte precomputed public key. Our keygen functions return secret keys + * of this form. + * + * Returns 0 on success, nonzero on failure decoding the public key. + * + * Constant time in everything except msglen, unless decoding the public key + * fails. + */ +int +crypto_vrf_ietfdraft03_prove(unsigned char proof[crypto_vrf_ietfdraft03_PROOFBYTES], + const unsigned char skpk[crypto_vrf_ietfdraft03_SECRETKEYBYTES], + const unsigned char *msg, + unsigned long long msglen) +{ + ge25519_p3 Y_point; + unsigned char x_scalar[32], truncated_hashed_sk_string[32]; + + if (vrf_expand_sk(&Y_point, x_scalar, truncated_hashed_sk_string, skpk) != 0) { + memset(x_scalar, 0, 32); + memset(truncated_hashed_sk_string, 0, 32); + memset(&Y_point, 0, sizeof Y_point); /* for good measure */ + return -1; + } + vrf_prove(proof, &Y_point, x_scalar, truncated_hashed_sk_string, msg, msglen); + memset(x_scalar, 0, 32); + memset(truncated_hashed_sk_string, 0, 32); + memset(&Y_point, 0, sizeof Y_point); /* for good measure */ + return 0; +} +} \ No newline at end of file diff --git a/contrib/sodium/randombytes.cpp b/contrib/sodium/randombytes.cpp new file mode 100644 index 000000000..21f62813c --- /dev/null +++ b/contrib/sodium/randombytes.cpp @@ -0,0 +1,123 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ +#include +#include +#include +#include + +#include + +#include "randombytes.h" +#include "common.h" + +#ifndef RANDOMBYTES_DEFAULT_IMPLEMENTATION +#define RANDOMBYTES_DEFAULT_IMPLEMENTATION +#endif + + +/* C++Builder defines a "random" macro */ +#undef random + +namespace sodium { + +static const randombytes_implementation *implementation; + +#ifndef RANDOMBYTES_DEFAULT_IMPLEMENTATION +# define RANDOMBYTES_DEFAULT_IMPLEMENTATION NULL +#endif + +static void randombytes_init_if_needed(void){ + + //implementation == NULL; +} + +int randombytes_set_implementation(randombytes_implementation *impl){ + + implementation = impl; + return 0; +} + +const char *randombytes_implementation_name(void) +{ + randombytes_init_if_needed(); + return implementation->implementation_name(); +} + +uint32_t randombytes_random(void){ + + randombytes_init_if_needed(); + return implementation->random(); +} + +uint32_t randombytes_uniform(const uint32_t upper_bound) +{ + uint32_t min; + uint32_t r; + + randombytes_init_if_needed(); + if (implementation->uniform != NULL) { + return implementation->uniform(upper_bound); + } + if (upper_bound < 2) { + return 0; + } + min = (1U + ~upper_bound) % upper_bound; /* = 2**32 mod upper_bound */ + do { + r = randombytes_random(); + } while (r < min); + /* r is now clamped to a set whose size mod upper_bound == 0 + * the worst case (2**31+1) requires ~ 2 attempts */ + + return r % upper_bound; +} + +void randombytes_buf(void * const buf, const size_t size) +{ + randombytes_init_if_needed(); + if (size > (size_t) 0U) { + implementation->buf(buf, size); + } +} + +size_t +randombytes_seedbytes(void) +{ + return randombytes_SEEDBYTES; +} + +int +randombytes_close(void) +{ + if (implementation != NULL && implementation->close != NULL) { + return implementation->close(); + } + return 0; +} + +void +randombytes(unsigned char * const buf, const unsigned long long buf_len) +{ + assert(buf_len <= SIZE_MAX); + randombytes_buf(buf, (size_t) buf_len); +} +} \ No newline at end of file diff --git a/contrib/sodium/randombytes.h b/contrib/sodium/randombytes.h new file mode 100644 index 000000000..f438de34f --- /dev/null +++ b/contrib/sodium/randombytes.h @@ -0,0 +1,58 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ +#ifndef randombytes_H +#define randombytes_H + +#include +#include + +#include + +namespace sodium { + +typedef struct randombytes_implementation { + const char *(*implementation_name)(void); /* required */ + uint32_t (*random)(void); /* required */ + uint32_t (*uniform)(const uint32_t upper_bound); /* optional, a default implementation will be used if NULL */ + void (*buf)(void * const buf, const size_t size); /* required */ + int (*close)(void); /* optional */ +} randombytes_implementation; + +#define randombytes_SEEDBYTES 32U +size_t randombytes_seedbytes(void); + +void randombytes_buf(void * const buf, const size_t size); + +uint32_t randombytes_random(void); + +uint32_t randombytes_uniform(const uint32_t upper_bound); + +int randombytes_close(void); + +int randombytes_set_implementation(randombytes_implementation *impl); + +const char *randombytes_implementation_name(void); + +} + +#endif diff --git a/contrib/sodium/sha512EL.cpp b/contrib/sodium/sha512EL.cpp new file mode 100644 index 000000000..9f8980838 --- /dev/null +++ b/contrib/sodium/sha512EL.cpp @@ -0,0 +1,278 @@ +/* +Copyright (c) 2018 - 2019 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#include +#include +#include +#include + +#include +#include "sha512EL.h" +#include "common.h" + +namespace sodium { + +static void +be64enc_vect(unsigned char *dst, const uint64_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 8; i++) { + STORE64_BE(dst + i * 8, src[i]); + } +} + +static void +be64dec_vect(uint64_t *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 8; i++) { + dst[i] = LOAD64_BE(src + i * 8); + } +} + +static const uint64_t Krnd[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, + 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, + 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, + 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, + 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, + 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, + 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, + 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, + 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, + 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, + 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ROTR64(x, n) +#define S0(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) +#define S1(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) +#define s0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ SHR(x, 7)) +#define s1(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ SHR(x, 6)) + +#define RND(a, b, c, d, e, f, g, h, k) \ + h += S1(e) + Ch(e, f, g) + k; \ + d += h; \ + h += S0(a) + Maj(a, b, c); + +#define RNDr(S, W, i, ii) \ + RND(S[(80 - i) % 8], S[(81 - i) % 8], S[(82 - i) % 8], S[(83 - i) % 8], \ + S[(84 - i) % 8], S[(85 - i) % 8], S[(86 - i) % 8], S[(87 - i) % 8], \ + W[i + ii] + Krnd[i + ii]) + +#define MSCH(W, ii, i) \ + W[i + ii + 16] = \ + s1(W[i + ii + 14]) + W[i + ii + 9] + s0(W[i + ii + 1]) + W[i + ii] + +static void +SHA512_Transform(uint64_t *state, const uint8_t block[128], uint64_t W[80], + uint64_t S[8]) +{ + int i; + + be64dec_vect(W, block, 128); + memcpy(S, state, 64); + for (i = 0; i < 80; i += 16) { + RNDr(S, W, 0, i); + RNDr(S, W, 1, i); + RNDr(S, W, 2, i); + RNDr(S, W, 3, i); + RNDr(S, W, 4, i); + RNDr(S, W, 5, i); + RNDr(S, W, 6, i); + RNDr(S, W, 7, i); + RNDr(S, W, 8, i); + RNDr(S, W, 9, i); + RNDr(S, W, 10, i); + RNDr(S, W, 11, i); + RNDr(S, W, 12, i); + RNDr(S, W, 13, i); + RNDr(S, W, 14, i); + RNDr(S, W, 15, i); + if (i == 64) { + break; + } + MSCH(W, 0, i); + MSCH(W, 1, i); + MSCH(W, 2, i); + MSCH(W, 3, i); + MSCH(W, 4, i); + MSCH(W, 5, i); + MSCH(W, 6, i); + MSCH(W, 7, i); + MSCH(W, 8, i); + MSCH(W, 9, i); + MSCH(W, 10, i); + MSCH(W, 11, i); + MSCH(W, 12, i); + MSCH(W, 13, i); + MSCH(W, 14, i); + MSCH(W, 15, i); + } + for (i = 0; i < 8; i++) { + state[i] += S[i]; + } +} + +static const uint8_t PAD[128] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static void +SHA512_Pad(crypto_hash_sha512_state *state, uint64_t tmp64[80 + 8]) +{ + unsigned int r; + unsigned int i; + + r = (unsigned int) ((state->count[1] >> 3) & 0x7f); + if (r < 112) { + for (i = 0; i < 112 - r; i++) { + state->buf[r + i] = PAD[i]; + } + } else { + for (i = 0; i < 128 - r; i++) { + state->buf[r + i] = PAD[i]; + } + SHA512_Transform(state->state, state->buf, &tmp64[0], &tmp64[80]); + memset(&state->buf[0], 0, 112); + } + be64enc_vect(&state->buf[112], state->count, 16); + SHA512_Transform(state->state, state->buf, &tmp64[0], &tmp64[80]); +} + +int +crypto_hash_sha512_init(crypto_hash_sha512_state *state) +{ + static const uint64_t sha512_initial_state[8] = { + 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, + 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, + 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL + }; + + state->count[0] = state->count[1] = (uint64_t) 0U; + memcpy(state->state, sha512_initial_state, sizeof sha512_initial_state); + + return 0; +} + +int +crypto_hash_sha512_update(crypto_hash_sha512_state *state, + const unsigned char *in, unsigned long long inlen) +{ + uint64_t tmp64[80 + 8]; + uint64_t bitlen[2]; + unsigned long long i; + unsigned long long r; + + if (inlen <= 0U) { + return 0; + } + r = (unsigned long long) ((state->count[1] >> 3) & 0x7f); + + bitlen[1] = ((uint64_t) inlen) << 3; + bitlen[0] = ((uint64_t) inlen) >> 61; + /* LCOV_EXCL_START */ + if ((state->count[1] += bitlen[1]) < bitlen[1]) { + state->count[0]++; + } + /* LCOV_EXCL_STOP */ + state->count[0] += bitlen[0]; + if (inlen < 128 - r) { + for (i = 0; i < inlen; i++) { + state->buf[r + i] = in[i]; + } + return 0; + } + for (i = 0; i < 128 - r; i++) { + state->buf[r + i] = in[i]; + } + SHA512_Transform(state->state, state->buf, &tmp64[0], &tmp64[80]); + in += 128 - r; + inlen -= 128 - r; + + while (inlen >= 128) { + SHA512_Transform(state->state, in, &tmp64[0], &tmp64[80]); + in += 128; + inlen -= 128; + } + inlen &= 127; + for (i = 0; i < inlen; i++) { + state->buf[i] = in[i]; + } + memset((void *) tmp64, 0, sizeof(tmp64)); + + return 0; +} + +int +crypto_hash_sha512_final(crypto_hash_sha512_state *state, unsigned char *out) +{ + uint64_t tmp64[80 + 8]; + + SHA512_Pad(state, tmp64); + be64enc_vect(out, state->state, 64); + memset((void *) tmp64, 0, sizeof(tmp64)); + memset((void *) state, 0, sizeof(*state)); + + return 0; +} + +int +crypto_hash_sha512(unsigned char *out, const unsigned char *in, + unsigned long long inlen) +{ + crypto_hash_sha512_state state; + + crypto_hash_sha512_init(&state); + crypto_hash_sha512_update(&state, in, inlen); + crypto_hash_sha512_final(&state, out); + + return 0; +} +} \ No newline at end of file diff --git a/contrib/sodium/sha512EL.h b/contrib/sodium/sha512EL.h new file mode 100644 index 000000000..1c7e90f37 --- /dev/null +++ b/contrib/sodium/sha512EL.h @@ -0,0 +1,63 @@ +/* +Copyright (c) 2018 - 2019 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + +#ifndef SHA512EL_H +#define SHA512EL_H + +#include +#include +#include + +namespace sodium { + +typedef struct crypto_hash_sha512_state { + uint64_t state[8]; + uint64_t count[2]; + uint8_t buf[128]; +} crypto_hash_sha512_state; + + +size_t crypto_hash_sha512_statebytes(void); + +#define crypto_hash_sha512_BYTES 64U +size_t crypto_hash_sha512_bytes(void); + + +int crypto_hash_sha512(unsigned char *out, const unsigned char *in, + unsigned long long inlen); + + +int crypto_hash_sha512_init(crypto_hash_sha512_state *state); + + +int crypto_hash_sha512_update(crypto_hash_sha512_state *state, + const unsigned char *in, + unsigned long long inlen); + + +int crypto_hash_sha512_final(crypto_hash_sha512_state *state, + unsigned char *out); + +} + +#endif diff --git a/contrib/sodium/verify.cpp b/contrib/sodium/verify.cpp new file mode 100644 index 000000000..590a5872d --- /dev/null +++ b/contrib/sodium/verify.cpp @@ -0,0 +1,188 @@ +/* +Copyright (c) 2018 Amir Hossein Alikhah Mishamandani +Copyright (c) 2018 Algorand LLC + +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. +*/ + + +#include + +#include "sha512EL.h" +#include "crypto_verify_16.h" +#include "vrf.h" +#include "ed25519_ref10.h" +#include "convert.h" +namespace sodium { + +static const unsigned char THREE = 0x03; + +/* Utility function to multiply a point by the cofactor (8) in place. */ +static void +multiply_by_cofactor(ge25519_p3 *point) { + ge25519_cached tmp_point; + ge25519_p1p1 tmp2_point; + + ge25519_p3_to_cached(&tmp_point, point); /* tmp = input */ + ge25519_add(&tmp2_point, point, &tmp_point); /* tmp2 = 2*input */ + ge25519_p1p1_to_p3(point, &tmp2_point); /* point = 2*input */ + ge25519_p3_to_cached(&tmp_point, point); /* tmp = 2*input */ + ge25519_add(&tmp2_point, point, &tmp_point); /* tmp2 = 4*input */ + ge25519_p1p1_to_p3(point, &tmp2_point); /* point = 4*input */ + ge25519_p3_to_cached(&tmp_point, point); /* tmp = 4*input */ + ge25519_add(&tmp2_point, point, &tmp_point); /* tmp2 = 8*input */ + ge25519_p1p1_to_p3(point, &tmp2_point); /* point = 8*input */ +} + +/* Convert a VRF proof pi into a VRF output hash beta per draft spec section 5.2. + * This function does not verify the proof! For an untrusted proof, instead call + * crypto_vrf_ietfdraft03_verify, which will output the hash if verification + * succeeds. + * Returns 0 on success, -1 on failure decoding the proof. + */ +int +crypto_vrf_ietfdraft03_proof_to_hash(unsigned char beta[crypto_vrf_ietfdraft03_OUTPUTBYTES], + const unsigned char pi[crypto_vrf_ietfdraft03_PROOFBYTES]) +{ + ge25519_p3 Gamma_point; + unsigned char c_scalar[16], s_scalar[32]; /* unused */ + unsigned char hash_input[2+32]; + + /* (Gamma, c, s) = ECVRF_decode_proof(pi_string) */ + if (_vrf_ietfdraft03_decode_proof(&Gamma_point, c_scalar, s_scalar, pi) != 0) { + return -1; + } + + /* beta_string = Hash(suite_string || three_string || point_to_string(cofactor * Gamma)) */ + hash_input[0] = SUITE; + hash_input[1] = THREE; + multiply_by_cofactor(&Gamma_point); + _vrf_ietfdraft03_point_to_string(hash_input+2, &Gamma_point); + crypto_hash_sha512(beta, hash_input, sizeof hash_input); + + return 0; +} + +/* Validate an untrusted public key as specified in the draft spec section + * 5.6.1. + * + * This means check that it is not of low order and that it is canonically + * encoded (i.e., y coordinate is already reduced mod p) Per the spec, we do not + * check if the point is on the main subgroup. + * + * Returns 0 on success (and stores decoded curve point in y_out), -1 on + * failure. + */ +static int +vrf_validate_key(ge25519_p3 *y_out, const unsigned char pk_string[32]) +{ + if (ge25519_has_small_order(pk_string) != 0 || _vrf_ietfdraft03_string_to_point(y_out, pk_string) != 0) { + return -1; + } + return 0; +} + +/* Validate an untrusted public key as specified in the draft spec section + * 5.6.1. Return 1 if the key is valid, 0 otherwise. + */ +int +crypto_vrf_ietfdraft03_is_valid_key(const unsigned char pk[crypto_vrf_ietfdraft03_PUBLICKEYBYTES]) +{ + ge25519_p3 point; /* unused */ + return (vrf_validate_key(&point, pk) == 0); +} + +/* Verify a proof per draft section 5.3. Return 0 on success, -1 on failure. + * We assume Y_point has passed public key validation already. + * Assuming verification succeeds, runtime does not depend on the message alpha + * (but does depend on its length alphalen) + */ +static int +vrf_verify(const ge25519_p3 *Y_point, const unsigned char pi[80], + const unsigned char *alpha, const unsigned long long alphalen) +{ + /* Note: c fits in 16 bytes, but ge25519_scalarmult expects a 32-byte scalar. + * Similarly, s_scalar fits in 32 bytes but sc25519_reduce takes in 64 bytes. */ + unsigned char h_string[32], c_scalar[32], s_scalar[64], cprime[16]; + + ge25519_p3 H_point, Gamma_point, U_point, V_point, tmp_p3_point; + ge25519_p1p1 tmp_p1p1_point; + ge25519_cached tmp_cached_point; + + if (_vrf_ietfdraft03_decode_proof(&Gamma_point, c_scalar, s_scalar, pi) != 0) { + return -1; + } + /* vrf_decode_proof writes to the first 16 bytes of c_scalar; we zero the + * second 16 bytes ourselves, as ge25519_scalarmult expects a 32-byte scalar. + */ + memset(c_scalar+16, 0, 16); + + /* vrf_decode_proof sets only the first 32 bytes of s_scalar; we zero the + * second 32 bytes ourselves, as sc25519_reduce expects a 64-byte scalar. + * Reducing the scalar s mod q ensures the high order bit of s is 0, which + * ref10's scalarmult functions require. + */ + memset(s_scalar+32, 0, 32); + sc25519_reduce(s_scalar); + + _vrf_ietfdraft03_hash_to_curve_elligator2_25519(h_string, Y_point, alpha, alphalen); + ge25519_frombytes(&H_point, h_string); + + /* calculate U = s*B - c*Y */ + ge25519_scalarmult(&tmp_p3_point, c_scalar, Y_point); /* tmp_p3 = c*Y */ + ge25519_p3_to_cached(&tmp_cached_point, &tmp_p3_point); /* tmp_cached = c*Y */ + ge25519_scalarmult_base(&tmp_p3_point, s_scalar); /* tmp_p3 = s*B */ + ge25519_sub(&tmp_p1p1_point, &tmp_p3_point, &tmp_cached_point); /* tmp_p1p1 = tmp_p3 - tmp_cached = s*B - c*Y */ + ge25519_p1p1_to_p3(&U_point, &tmp_p1p1_point); /* U = s*B - c*Y */ + + /* calculate V = s*H - c*Gamma */ + ge25519_scalarmult(&tmp_p3_point, c_scalar, &Gamma_point); /* tmp_p3 = c*Gamma */ + ge25519_p3_to_cached(&tmp_cached_point, &tmp_p3_point); /* tmp_cached = c*Gamma */ + ge25519_scalarmult(&tmp_p3_point, s_scalar, &H_point); /* tmp_p3 = s*H */ + ge25519_sub(&tmp_p1p1_point, &tmp_p3_point, &tmp_cached_point); /* tmp_p1p1 = tmp_p3 - tmp_cached = s*H - c*Gamma */ + ge25519_p1p1_to_p3(&V_point, &tmp_p1p1_point); /* V = s*H - c*Gamma */ + + _vrf_ietfdraft03_hash_points(cprime, &H_point, &Gamma_point, &U_point, &V_point); + return crypto_verify_16(c_scalar, cprime); +} + +/* Verify a VRF proof (for a given a public key and message) and validate the + * public key. If verification succeeds, store the VRF output hash in output[]. + * Specified in draft spec section 5.3. + * + * For a given public key and message, there are many possible proofs but only + * one possible output hash. + * + * Returns 0 if verification succeeds (and stores output hash in output[]), + * nonzero on failure. + */ +int +crypto_vrf_ietfdraft03_verify(unsigned char output[crypto_vrf_ietfdraft03_OUTPUTBYTES], + const unsigned char pk[crypto_vrf_ietfdraft03_PUBLICKEYBYTES], + const unsigned char proof[crypto_vrf_ietfdraft03_PROOFBYTES], + const unsigned char *msg, const unsigned long long msglen) +{ + ge25519_p3 Y; + if ((vrf_validate_key(&Y, pk) == 0) && (vrf_verify(&Y, proof, msg, msglen) == 0)) { + return crypto_vrf_ietfdraft03_proof_to_hash(output, proof); + } else { + return -1; + } +} +} \ No newline at end of file diff --git a/contrib/sodium/vrf.h b/contrib/sodium/vrf.h new file mode 100644 index 000000000..01ce38b5b --- /dev/null +++ b/contrib/sodium/vrf.h @@ -0,0 +1,88 @@ +/* +Copyright (c) 2018 - 2019 Amir Hossein Alikhah Mishamandani + +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. +*/ +#include +#include "common.h" + +#ifndef VRF_H +#define VRF_H + +namespace sodium { + +#ifdef __cplusplus +extern "C" { +#endif + + +#define crypto_vrf_ietfdraft03_PUBLICKEYBYTES 32U +size_t crypto_vrf_ietfdraft03_publickeybytes(void); + +#define crypto_vrf_ietfdraft03_SECRETKEYBYTES 64U +size_t crypto_vrf_ietfdraft03_secretkeybytes(void); + +#define crypto_vrf_ietfdraft03_SEEDBYTES 32U +size_t crypto_vrf_ietfdraft03_seedbytes(void); + +#define crypto_vrf_ietfdraft03_PROOFBYTES 80U +size_t crypto_vrf_ietfdraft03_proofbytes(void); + +#define crypto_vrf_ietfdraft03_OUTPUTBYTES 64U +size_t crypto_vrf_ietfdraft03_outputbytes(void); + +int crypto_vrf_ietfdraft03_prove(unsigned char *proof, + const unsigned char *sk, + const unsigned char *m, + unsigned long long mlen); + +int crypto_vrf_ietfdraft03_keypair(unsigned char pk[crypto_vrf_ietfdraft03_PUBLICKEYBYTES], + unsigned char sk[crypto_vrf_ietfdraft03_SECRETKEYBYTES]); + +int crypto_vrf_ietfdraft03_keypair_from_seed(unsigned char pk[crypto_vrf_ietfdraft03_PUBLICKEYBYTES], + unsigned char sk[crypto_vrf_ietfdraft03_SECRETKEYBYTES], + const unsigned char seed[crypto_vrf_ietfdraft03_SEEDBYTES]); + +void crypto_vrf_ietfdraft03_sk_to_pk(unsigned char pk[crypto_vrf_ietfdraft03_PUBLICKEYBYTES], + const unsigned char skpk[crypto_vrf_ietfdraft03_SECRETKEYBYTES]); + +void crypto_vrf_ietfdraft03_sk_to_seed(unsigned char seed[crypto_vrf_ietfdraft03_SEEDBYTES], + const unsigned char skpk[crypto_vrf_ietfdraft03_SECRETKEYBYTES]); + +int crypto_vrf_ietfdraft03_is_valid_key(const unsigned char *pk) + __attribute__ ((warn_unused_result)); + +int crypto_vrf_ietfdraft03_verify(unsigned char *output, + const unsigned char *pk, + const unsigned char *proof, + const unsigned char *m, + unsigned long long mlen) + __attribute__ ((warn_unused_result)); + +int crypto_vrf_ietfdraft03_proof_to_hash(unsigned char *hash, + const unsigned char *proof); + + +#ifdef __cplusplus +} +#endif + +} + +#endif \ No newline at end of file diff --git a/include/metaverse/bitcoin/chain/attachment/account/account.hpp b/include/metaverse/bitcoin/chain/attachment/account/account.hpp index b68538c04..07a9a9de1 100644 --- a/include/metaverse/bitcoin/chain/attachment/account/account.hpp +++ b/include/metaverse/bitcoin/chain/attachment/account/account.hpp @@ -57,6 +57,36 @@ enum account_type : uint8_t { common = 0, multisignature, + script_, // 'script' conflicts with class 'script' +}; + +#define is_multisignature(type) ((type & account_type::multisignature) == account_type::multisignature) +#define is_script(type) ((type & account_type::script_) == account_type::script_) + +class BC_API account_script +{ +public: + typedef std::vector list; + account_script(); + + const std::string& get_description() const{return description_;}; + void set_description(const std::string& description); + + const std::string& get_address() const{return address_;}; + void set_address(const std::string& address); + + const data_chunk& get_script() const{return script_;}; + void set_script(const data_chunk& script); + + bool from_data(reader& source); + void to_data(writer& sink) const; + + uint64_t serialized_size() const; + bool operator==(const account_script& other) const; +private: + std::string description_; + std::string address_; + data_chunk script_; }; /// used for store account related information @@ -188,6 +218,14 @@ class BC_API account void remove_multisig(const account_multisig& multisig); std::shared_ptr get_multisig(const std::string& addr); + const account_script::list& get_script_vec() const; + void set_script_vec(account_script::list&& script); + bool is_script_exist(const account_script& script); + void set_script(const account_script& script); + void modify_script(const account_script& script); + void remove_script(const account_script& script); + std::shared_ptr get_script(const std::string& addr); + private: std::string name; std::string mnemonic; @@ -202,11 +240,19 @@ class BC_API account // multisig fields account_multisig::list multisig_vec; //account_multisig multisig; + account_script::list script_vec; }; } // namespace chain } // namespace libbitcoin +using account_status = libbitcoin::chain::account_status; +using account_priority = libbitcoin::chain::account_priority; +using account_type = libbitcoin::chain::account_type; +using account_script = libbitcoin::chain::account_script; +using account = libbitcoin::chain::account; +using account_multisig = libbitcoin::chain::account_multisig; + #endif diff --git a/include/metaverse/bitcoin/chain/attachment/account/account_address.hpp b/include/metaverse/bitcoin/chain/attachment/account/account_address.hpp index c9bf69775..c3a5553eb 100644 --- a/include/metaverse/bitcoin/chain/attachment/account/account_address.hpp +++ b/include/metaverse/bitcoin/chain/attachment/account/account_address.hpp @@ -48,7 +48,8 @@ enum account_address_status : uint8_t diabale = 0, enable = 1, // common address multisig_addr = 2, // multisig address - stealth_addr = 3 // stealth address + stealth_addr = 3, // stealth address + script_addr = 4, // scirpt address }; class BC_API account_address @@ -104,5 +105,8 @@ class BC_API account_address } // namespace chain } // namespace libbitcoin +using account_address_status = libbitcoin::chain::account_address_status; +using account_address = libbitcoin::chain::account_address; + #endif diff --git a/include/metaverse/bitcoin/chain/attachment/asset/asset.hpp b/include/metaverse/bitcoin/chain/attachment/asset/asset.hpp index b7873fbf0..8b0b8a98b 100644 --- a/include/metaverse/bitcoin/chain/attachment/asset/asset.hpp +++ b/include/metaverse/bitcoin/chain/attachment/asset/asset.hpp @@ -33,7 +33,12 @@ #include #include -using namespace libbitcoin::chain; +namespace libbitcoin { +namespace chain { +class asset; +} +} +using asset = libbitcoin::chain::asset; #define ASSET_STATUS2UINT32(kd) (static_cast::type>(kd)) diff --git a/include/metaverse/bitcoin/chain/attachment/asset/asset_cert.hpp b/include/metaverse/bitcoin/chain/attachment/asset/asset_cert.hpp index 1b9d7784d..70f523156 100644 --- a/include/metaverse/bitcoin/chain/attachment/asset/asset_cert.hpp +++ b/include/metaverse/bitcoin/chain/attachment/asset/asset_cert.hpp @@ -26,6 +26,31 @@ #include #include #include +#include + +#ifdef __BIG_ENDIAN__ + #define ASSET_CERT_BIG_ENDIAN +#elif defined __LITTLE_ENDIAN__ + /* override */ +#elif defined __BYTE_ORDER + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define ASSET_CERT_BIG_ENDIAN + #endif +#else /* !defined __LITTLE_ENDIAN__ */ + #include /* machine/endian.h */ + #if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + #define ASSET_CERT_BIG_ENDIAN + #endif +#endif + +namespace libbitcoin { +namespace chain { +class asset_cert; +union asset_cert_type; +} +} +using asset_cert = libbitcoin::chain::asset_cert; +using asset_cert_type = libbitcoin::chain::asset_cert_type; #define ASSET_CERT_STATUS2UINT32(kd) (static_cast::type>(kd)) @@ -47,16 +72,47 @@ BC_CONSTEXPR size_t ASSET_CERT_FIX_SIZE = (ASSET_CERT_SYMBOL_FIX_SIZE + ASSET_CERT_OWNER_FIX_SIZE + ASSET_CERT_ADDRESS_FIX_SIZE + ASSET_CERT_TYPE_FIX_SIZE + ASSET_CERT_STATUS_FIX_SIZE); -using asset_cert_type = uint32_t; + +union asset_cert_type +{ + asset_cert_type(uint32_t mask_= 0) + :mask(mask_) + { + } + operator uint32_t()const + { + return mask; + } + + struct{ +#ifdef ASSET_CERT_BIG_ENDIAN + uint32_t custom:1; + uint32_t unmovable:1; + uint32_t :10; + uint32_t type:20; +#else + uint32_t type:20; + uint32_t :10; + uint32_t unmovable:1; + uint32_t custom:1; +#endif + } cert_type_status; + + uint32_t mask; +}; + +std::istream& operator>>(std::istream& in, asset_cert_type& out); + namespace asset_cert_ns { - constexpr asset_cert_type none = 0; - constexpr asset_cert_type issue = 1; - constexpr asset_cert_type domain = 2; - constexpr asset_cert_type naming = 3; - - constexpr asset_cert_type custom = 0x80000000; - constexpr asset_cert_type marriage = custom + 0; - constexpr asset_cert_type kyc = custom + 1; + const asset_cert_type none = 0; + const asset_cert_type issue = 1; + const asset_cert_type domain = 2; + const asset_cert_type naming = 3; + + const asset_cert_type custom = 0x80000000; + const asset_cert_type custom_max = 0x800fffff; + const asset_cert_type marriage = custom + 0; + const asset_cert_type kyc = custom + 1; } class BC_API asset_cert @@ -120,6 +176,9 @@ class BC_API asset_cert static bool is_valid_domain(const std::string& domain); static std::string get_key(const std::string&symbol, const asset_cert_type& bit); + static bool is_unmovable(asset_cert_type cert_type); + bool is_unmovable() const; + private: // NOTICE: ref CAssetCert in transaction.h // asset_cert and CAssetCert should have the same size and order. @@ -133,5 +192,7 @@ class BC_API asset_cert } // namespace chain } // namespace libbitcoin +namespace asset_cert_ns = libbitcoin::chain::asset_cert_ns; + #endif diff --git a/include/metaverse/bitcoin/chain/attachment/asset/asset_detail.hpp b/include/metaverse/bitcoin/chain/attachment/asset/asset_detail.hpp index 95f331f82..ea617f247 100644 --- a/include/metaverse/bitcoin/chain/attachment/asset/asset_detail.hpp +++ b/include/metaverse/bitcoin/chain/attachment/asset/asset_detail.hpp @@ -119,5 +119,7 @@ class BC_API asset_detail } // namespace chain } // namespace libbitcoin +using asset_detail = libbitcoin::chain::asset_detail; + #endif diff --git a/include/metaverse/bitcoin/chain/attachment/asset/asset_mit.hpp b/include/metaverse/bitcoin/chain/attachment/asset/asset_mit.hpp index b473ebe93..1e4bc14e0 100644 --- a/include/metaverse/bitcoin/chain/attachment/asset/asset_mit.hpp +++ b/include/metaverse/bitcoin/chain/attachment/asset/asset_mit.hpp @@ -29,6 +29,13 @@ #include #include +namespace libbitcoin { +namespace chain { +class asset_mit; +} +} +using asset_mit = libbitcoin::chain::asset_mit; + #define MIT_STATUS2UINT32(kd) (static_cast::type>(kd)) #define MIT_STATUS_NONE MIT_STATUS2UINT32(asset_mit::mit_status::mit_status_none) @@ -133,5 +140,8 @@ struct BC_API asset_mit_info } // namespace chain } // namespace libbitcoin +using asset_mit = libbitcoin::chain::asset_mit; +using asset_mit_info = libbitcoin::chain::asset_mit_info; + #endif diff --git a/include/metaverse/bitcoin/chain/attachment/asset/asset_transfer.hpp b/include/metaverse/bitcoin/chain/attachment/asset/asset_transfer.hpp index 178a56582..bd09e9323 100644 --- a/include/metaverse/bitcoin/chain/attachment/asset/asset_transfer.hpp +++ b/include/metaverse/bitcoin/chain/attachment/asset/asset_transfer.hpp @@ -109,5 +109,9 @@ class BC_API asset_transfer } // namespace chain } // namespace libbitcoin +using asset_balances = libbitcoin::chain::asset_balances; +using asset_deposited_balance = libbitcoin::chain::asset_deposited_balance; +using asset_transfer = libbitcoin::chain::asset_transfer; + #endif diff --git a/include/metaverse/bitcoin/chain/attachment/asset/attenuation_model.hpp b/include/metaverse/bitcoin/chain/attachment/asset/attenuation_model.hpp index 6fbad27a2..485a7e7a9 100644 --- a/include/metaverse/bitcoin/chain/attachment/asset/attenuation_model.hpp +++ b/include/metaverse/bitcoin/chain/attachment/asset/attenuation_model.hpp @@ -26,8 +26,6 @@ #include #include -using namespace libbitcoin::chain; - // forward declaration namespace libbitcoin { namespace blockchain { @@ -105,5 +103,7 @@ class attenuation_model } // namespace chain } // namespace libbitcoin +using attenuation_model = libbitcoin::chain::attenuation_model; + #endif diff --git a/include/metaverse/bitcoin/chain/attachment/asset/blockchain_asset.hpp b/include/metaverse/bitcoin/chain/attachment/asset/blockchain_asset.hpp index 6eaec05bf..694a39319 100644 --- a/include/metaverse/bitcoin/chain/attachment/asset/blockchain_asset.hpp +++ b/include/metaverse/bitcoin/chain/attachment/asset/blockchain_asset.hpp @@ -73,4 +73,5 @@ class BC_API blockchain_asset } // namespace chain } // namespace libbitcoin +using blockchain_asset = libbitcoin::chain::blockchain_asset; diff --git a/include/metaverse/bitcoin/chain/attachment/attachment.hpp b/include/metaverse/bitcoin/chain/attachment/attachment.hpp index d5dafb4b1..673343ccb 100644 --- a/include/metaverse/bitcoin/chain/attachment/attachment.hpp +++ b/include/metaverse/bitcoin/chain/attachment/attachment.hpp @@ -36,7 +36,13 @@ #include #include -using namespace libbitcoin::chain; +namespace libbitcoin { +namespace chain { +class attachment; +} +} +using attachment = libbitcoin::chain::attachment; + #define TYPE2UINT32(kd) (static_cast::type>(kd)) #define ETP_TYPE TYPE2UINT32(attachment::attachment_type::attachment_etp) @@ -49,6 +55,7 @@ using namespace libbitcoin::chain; #define DID_ATTACH_VERIFY_VERSION TYPE2UINT32(207) +#define ATTACH_NULL_TYPE MAX_UINT32 namespace libbitcoin { namespace chain { @@ -80,6 +87,7 @@ class BC_API attachment > attachment_data_type; attachment(); + attachment(uint32_t type); attachment(const std::string& from_did, const std::string& to_did); @@ -88,6 +96,11 @@ class BC_API attachment : type(type), version(version), attach(attach_data) {} + attachment(attachment&& other); + attachment(const attachment& other); + attachment& operator=(attachment&& other); + attachment& operator=(const attachment& other); + static uint64_t satoshi_fixed_size(); bool from_data_t(reader& source); @@ -102,6 +115,7 @@ class BC_API attachment void set_version(uint32_t version); uint32_t get_type() const; void set_type(uint32_t type); + void set_null(); std::string get_to_did() const; void set_to_did(const std::string& did); diff --git a/include/metaverse/bitcoin/chain/attachment/did/blockchain_did.hpp b/include/metaverse/bitcoin/chain/attachment/did/blockchain_did.hpp index c98f47ce8..ec330d59c 100644 --- a/include/metaverse/bitcoin/chain/attachment/did/blockchain_did.hpp +++ b/include/metaverse/bitcoin/chain/attachment/did/blockchain_did.hpp @@ -82,4 +82,5 @@ class BC_API blockchain_did } // namespace chain } // namespace libbitcoin +using blockchain_did = libbitcoin::chain::blockchain_did; diff --git a/include/metaverse/bitcoin/chain/attachment/did/did.hpp b/include/metaverse/bitcoin/chain/attachment/did/did.hpp index 3a7811d8f..693418f29 100644 --- a/include/metaverse/bitcoin/chain/attachment/did/did.hpp +++ b/include/metaverse/bitcoin/chain/attachment/did/did.hpp @@ -32,7 +32,12 @@ #include #include -using namespace libbitcoin::chain; +namespace libbitcoin { +namespace chain { +class did; +} +} +using did = libbitcoin::chain::did; #define DID_STATUS2UINT32(kd) (static_cast::type>(kd)) diff --git a/include/metaverse/bitcoin/chain/attachment/did/did_detail.hpp b/include/metaverse/bitcoin/chain/attachment/did/did_detail.hpp index 0158b419d..2e9bd919c 100644 --- a/include/metaverse/bitcoin/chain/attachment/did/did_detail.hpp +++ b/include/metaverse/bitcoin/chain/attachment/did/did_detail.hpp @@ -62,7 +62,6 @@ class BC_API did_detail bool operator< (const did_detail& other) const; std::string to_string() const; - void to_json(std::ostream& out); bool is_valid() const; void reset(); @@ -81,5 +80,7 @@ class BC_API did_detail } // namespace chain } // namespace libbitcoin +using did_detail = libbitcoin::chain::did_detail; + #endif diff --git a/include/metaverse/bitcoin/chain/attachment/etp/etp.hpp b/include/metaverse/bitcoin/chain/attachment/etp/etp.hpp index 5d47fce57..aeb4dfe58 100644 --- a/include/metaverse/bitcoin/chain/attachment/etp/etp.hpp +++ b/include/metaverse/bitcoin/chain/attachment/etp/etp.hpp @@ -58,5 +58,7 @@ class BC_API etp } // namespace chain } // namespace libbitcoin +using etp = libbitcoin::chain::etp; + #endif diff --git a/include/metaverse/bitcoin/chain/attachment/etp/etp_award.hpp b/include/metaverse/bitcoin/chain/attachment/etp/etp_award.hpp index 14dc4e466..e583b2610 100644 --- a/include/metaverse/bitcoin/chain/attachment/etp/etp_award.hpp +++ b/include/metaverse/bitcoin/chain/attachment/etp/etp_award.hpp @@ -58,5 +58,7 @@ class BC_API etp_award } // namespace chain } // namespace libbitcoin +using etp_award = libbitcoin::chain::etp_award; + #endif diff --git a/include/metaverse/bitcoin/chain/block.hpp b/include/metaverse/bitcoin/chain/block.hpp index 0950dd9dd..995a26ee5 100644 --- a/include/metaverse/bitcoin/chain/block.hpp +++ b/include/metaverse/bitcoin/chain/block.hpp @@ -54,11 +54,11 @@ class BC_API block block(); block(const block& other); block(const chain::header& header, - const chain::transaction::list& transactions); + const chain::transaction::list& transactions, const ec_signature& blocksig={}); block(block&& other); block(chain::header&& header, - chain::transaction::list&& transactions); + chain::transaction::list&& transactions, ec_signature&& blocksig={}); /// This class is move assignable but not copy assignable. block& operator=(block&& other); @@ -69,12 +69,22 @@ class BC_API block bool is_valid() const; void reset(); uint64_t serialized_size(bool with_transaction_count = true) const; + bool is_proof_of_stake() const; + bool is_proof_of_work() const; + bool is_proof_of_dpos() const; chain::header header; transaction::list transactions; + + bool can_use_dpos_consensus() const; + bool must_use_pow_consensus() const; + + ec_signature blocksig; // pos block only }; } // namespace chain } // namespace libbitcoin +using block = libbitcoin::chain::block; + #endif diff --git a/include/metaverse/bitcoin/chain/business_data.hpp b/include/metaverse/bitcoin/chain/business_data.hpp index 6dd606668..bd6079d07 100644 --- a/include/metaverse/bitcoin/chain/business_data.hpp +++ b/include/metaverse/bitcoin/chain/business_data.hpp @@ -318,5 +318,16 @@ class BC_API business_address_message } // namespace chain } // namespace libbitcoin +using business_kind = libbitcoin::chain::business_kind; +using business_status = libbitcoin::chain::business_status; +using business_data = libbitcoin::chain::business_data; +using business_record = libbitcoin::chain::business_record; +using business_history = libbitcoin::chain::business_history; +using business_address_asset = libbitcoin::chain::business_address_asset; +using business_address_asset_cert = libbitcoin::chain::business_address_asset_cert; +using business_address_mit = libbitcoin::chain::business_address_mit; +using business_address_did = libbitcoin::chain::business_address_did; +using business_address_message = libbitcoin::chain::business_address_message; + #endif diff --git a/include/metaverse/bitcoin/chain/header.hpp b/include/metaverse/bitcoin/chain/header.hpp index 308287f08..41a52b862 100644 --- a/include/metaverse/bitcoin/chain/header.hpp +++ b/include/metaverse/bitcoin/chain/header.hpp @@ -49,6 +49,15 @@ namespace libbitcoin { namespace chain { +enum block_version { + block_version_any = 0, + block_version_min = 1, + block_version_pow = 1, + block_version_pos = 2, + block_version_dpos = 3, + block_version_max = 4 +}; + class BC_API header : public base_primary
{ @@ -58,7 +67,7 @@ class BC_API header typedef std::vector ptr_list; static uint64_t satoshi_fixed_size_without_transaction_count(); - + header(); header(const header& other); header(uint32_t version, const hash_digest& previous_block_hash, @@ -83,6 +92,10 @@ class BC_API header void reset(); uint64_t serialized_size(bool with_transaction_count = true) const; + bool is_proof_of_stake() const; + bool is_proof_of_work() const; + bool is_proof_of_dpos() const; + uint32_t version; hash_digest previous_block_hash; hash_digest merkle; @@ -104,6 +117,11 @@ class BC_API header BC_API bool operator==(const header& left, const header& right); BC_API bool operator!=(const header& left, const header& right); + +std::string get_block_version(const header& header); +std::string get_block_version(block_version version); +std::string get_block_version(uint32_t version); + } // namespace chain } // namespace libbitcoin diff --git a/include/metaverse/bitcoin/chain/history.hpp b/include/metaverse/bitcoin/chain/history.hpp index 9118755a0..9868f5a0c 100644 --- a/include/metaverse/bitcoin/chain/history.hpp +++ b/include/metaverse/bitcoin/chain/history.hpp @@ -94,4 +94,8 @@ struct BC_API history } // namespace chain } // namespace libbitcoin +using point_kind = libbitcoin::chain::point_kind; +using history = libbitcoin::chain::history; +using history_compact = libbitcoin::chain::history_compact; + #endif diff --git a/include/metaverse/bitcoin/chain/output.hpp b/include/metaverse/bitcoin/chain/output.hpp index b3c1f56c1..a3fc141ea 100644 --- a/include/metaverse/bitcoin/chain/output.hpp +++ b/include/metaverse/bitcoin/chain/output.hpp @@ -66,6 +66,7 @@ class BC_API output void to_data_t(writer& sink) const; std::string to_string(uint32_t flags) const; bool is_valid() const; + bool is_null() const; code check_attachment_address(bc::blockchain::block_chain_impl& chain) const; std::string get_script_address() const; void reset(); @@ -80,6 +81,7 @@ class BC_API output std::string get_asset_cert_address() const; asset_cert_type get_asset_cert_type() const; const data_chunk& get_attenuation_model_param() const; + uint32_t get_lock_sequence() const; bool is_asset() const; bool is_asset_transfer() const; bool is_asset_issue() const; @@ -114,4 +116,6 @@ class BC_API output } // namespace chain } // namespace libbitcoin +using output = libbitcoin::chain::output; + #endif diff --git a/include/metaverse/bitcoin/chain/output_point.hpp b/include/metaverse/bitcoin/chain/output_point.hpp index 2ee77c595..ff5f6cf7c 100644 --- a/include/metaverse/bitcoin/chain/output_point.hpp +++ b/include/metaverse/bitcoin/chain/output_point.hpp @@ -36,30 +36,6 @@ class BC_API output_point { public: - // THIS IS FOR LIBRARY USE ONLY, DO NOT CREATE A DEPENDENCY ON IT. - struct validation - { - /// Must be false if confirmed is false. - /// output spender's tx->block is indexed or confirmed not above fork. - bool spent = false; - - /// The output->tx is confirmed|indexed, fork point dependent. - bool confirmed = false; - - /// The previous output is a coinbase (must verify spender maturity). - bool coinbase = false; - - /// Prevout height is used for coinbase maturity and relative lock time. - size_t height = 0; - - /// Median time past is used for relative lock time. - uint32_t median_time_past = 0; - - /// The output cache contains the output referenced by the input point. - /// If the cache.value is not_found (default) the output is not found. - output cache; - }; - // Constructors. //------------------------------------------------------------------------- @@ -95,23 +71,14 @@ class BC_API output_point static output_point factory(std::istream& stream); static output_point factory(reader& source); - // Validation. - //------------------------------------------------------------------------- - - /// True if cached previous output is mature enough to spend from height. - bool is_mature(size_t height) const; - - // THIS IS FOR LIBRARY USE ONLY, DO NOT CREATE A DEPENDENCY ON IT. - mutable validation metadata; - protected: // So that input may call reset from its own. friend class input; }; -struct BC_API output_info +struct BC_API output_point_info { - typedef std::vector list; + typedef std::vector list; output_point point; uint64_t value; @@ -123,8 +90,20 @@ struct BC_API points_info uint64_t change; }; +struct BC_API output_info +{ + typedef std::vector list; + + output data; + output_point point; + uint64_t height; +}; } // namespace chain } // namespace libbitcoin +using output_point = libbitcoin::chain::output_point; +using output_point_info = libbitcoin::chain::output_point_info; +using output_info = libbitcoin::chain::output_info; + #endif diff --git a/include/metaverse/bitcoin/chain/point.hpp b/include/metaverse/bitcoin/chain/point.hpp index d2b95dcbc..50b20edb4 100644 --- a/include/metaverse/bitcoin/chain/point.hpp +++ b/include/metaverse/bitcoin/chain/point.hpp @@ -99,6 +99,8 @@ typedef point input_point; } // namespace chain } // namespace libbitcoin +using input_point = libbitcoin::chain::input_point; + namespace std { diff --git a/include/metaverse/bitcoin/chain/script/opcode.hpp b/include/metaverse/bitcoin/chain/script/opcode.hpp index 4caa6c8c4..9fa83c9d7 100644 --- a/include/metaverse/bitcoin/chain/script/opcode.hpp +++ b/include/metaverse/bitcoin/chain/script/opcode.hpp @@ -179,6 +179,7 @@ enum script_context : uint32_t all_enabled = 0xffffffff }; +BC_API script_context get_script_context(); BC_API std::string opcode_to_string(opcode value, uint32_t flags); BC_API opcode string_to_opcode(const std::string& value); BC_API opcode data_to_opcode(const data_chunk& value); diff --git a/include/metaverse/bitcoin/chain/script/operation.hpp b/include/metaverse/bitcoin/chain/script/operation.hpp index 24a879ba2..19dbdf671 100644 --- a/include/metaverse/bitcoin/chain/script/operation.hpp +++ b/include/metaverse/bitcoin/chain/script/operation.hpp @@ -96,6 +96,13 @@ enum class script_pattern /// Signature script: pay_key_hash_with_attenuation_model, + /// BIP112 check sequence + /// Pubkey script: + /// OP_CHECKSEQUENCEVERIFY DROP + /// OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG + /// Signature script: + pay_key_hash_with_sequence_lock, + }; class BC_API operation @@ -122,6 +129,7 @@ class BC_API operation static bool is_pay_script_hash_pattern(const operation::stack& ops); static bool is_pay_blackhole_pattern(const operation::stack& ops); static bool is_pay_key_hash_with_attenuation_model_pattern(const operation::stack& ops); + static bool is_pay_key_hash_with_sequence_lock_pattern(const operation::stack& ops); /// signature script patterns (standard) static bool is_sign_multisig_pattern(const operation::stack& ops); @@ -134,6 +142,7 @@ class BC_API operation static uint64_t get_lock_height_from_pay_key_hash_with_lock_height(const operation::stack& ops); static const data_chunk& get_model_param_from_pay_key_hash_with_attenuation_model(const operation::stack& ops); static const data_chunk& get_input_point_from_pay_key_hash_with_attenuation_model(const operation::stack& ops); + static uint32_t get_lock_sequence_from_pay_key_hash_with_sequence_lock(const operation::stack& ops); /// stack factories static stack to_null_data_pattern(data_slice data); @@ -148,6 +157,9 @@ class BC_API operation static stack to_pay_blackhole_pattern(const short_hash& hash); static stack to_pay_key_hash_with_attenuation_model_pattern( const short_hash& hash, const std::string& model_param, const point& input_point); + static stack to_pay_key_hash_with_sequence_lock_pattern(const short_hash& hash, uint32_t sequence_lock); + + static operation from_raw_data(const data_chunk& data); bool from_data(const data_chunk& data); bool from_data(std::istream& stream); @@ -160,6 +172,8 @@ class BC_API operation void reset(); uint64_t serialized_size() const; + bool operator==(const operation& other) const; + opcode code; data_chunk data; diff --git a/include/metaverse/bitcoin/chain/transaction.hpp b/include/metaverse/bitcoin/chain/transaction.hpp index 92b1311f9..5f463ea1a 100644 --- a/include/metaverse/bitcoin/chain/transaction.hpp +++ b/include/metaverse/bitcoin/chain/transaction.hpp @@ -73,7 +73,7 @@ class BC_API transaction transaction& operator=(const transaction& other) /*= delete*/; bool from_data_t(reader& source); - void to_data_t(writer& sink) const; + void to_data_t(writer& sink, bool for_merkle=false) const; std::string to_string(uint32_t flags) const; bool is_valid() const; void reset(); @@ -82,6 +82,8 @@ class BC_API transaction // sighash_type is used by OP_CHECKSIG hash_digest hash(uint32_t sighash_type) const; bool is_coinbase() const; + bool is_pos_genesis_tx(bool is_testnet) const; + bool is_coinstake() const; bool is_final(uint64_t block_height, uint32_t block_time) const; bool is_locked(size_t block_height, uint32_t median_time_past) const; bool is_locktime_conflict() const; @@ -96,13 +98,15 @@ class BC_API transaction bool has_did_register() const; bool has_did_transfer() const; - std::string get_did_transfer_old_address() const; uint32_t version; uint32_t locktime; input::list inputs; output::list outputs; +protected: + bool all_inputs_final() const; + private: mutable upgrade_mutex mutex_; mutable std::shared_ptr hash_; @@ -111,4 +115,7 @@ class BC_API transaction } // namespace chain } // namespace libbitcoin +using transaction = libbitcoin::chain::transaction; +using transaction_version = libbitcoin::chain::transaction_version; + #endif diff --git a/include/metaverse/bitcoin/constants.hpp b/include/metaverse/bitcoin/constants.hpp index 174435077..2c894ef4c 100644 --- a/include/metaverse/bitcoin/constants.hpp +++ b/include/metaverse/bitcoin/constants.hpp @@ -60,18 +60,59 @@ BC_CONSTEXPR uint64_t min_fee_to_issue_asset = 10 * 100000000; BC_CONSTEXPR uint64_t min_fee_to_register_did = 1 * 100000000; BC_CONSTEXPR uint32_t min_fee_percentage_to_miner = 20; +// Relative locktime constants. +//----------------------------------------------------------------------------- + // Threshold for nLockTime: below this value it is interpreted as block number, // otherwise as UNIX timestamp. [Tue Nov 5 00:53:20 1985 UTC] BC_CONSTEXPR uint32_t locktime_threshold = 500000000; -// Relative locktime constants. +extern const size_t relative_locktime_min_version; + +/* Below flags apply in the context of BIP 68*/ +/* If this flag set, input::sequence is NOT interpreted as a + * relative lock-time. */ +BC_CONSTEXPR uint32_t relative_locktime_disabled = (1 << 31); + +/* If input::sequence encodes a relative lock-time and this flag + * is set, the relative lock-time has units of 32 seconds, + * otherwise it specifies blocks with a granularity of 1. */ +BC_CONSTEXPR uint32_t relative_locktime_time_locked = (1 << 22); + +/* If input::sequence encodes a relative lock-time, this mask is + * applied to extract that lock-time from the sequence field. */ +BC_CONSTEXPR uint32_t relative_locktime_mask = 0x000fffff; + +/* In order to use the same number of bits to encode roughly the + * same wall-clock duration, and because blocks are naturally + * limited to occur every 30s on average, the minimum granularity + * for time-based relative lock-time is fixed at 32 seconds. + * Converting from input::sequence to seconds is performed by + * multiplying by 32 = 2^5, or equivalently shifting up by + * 5 bits. */ +BC_CONSTEXPR size_t relative_locktime_seconds_shift = 5; + +// Future blocktime fork constants. +//----------------------------------------------------------------------------- +extern const uint64_t future_blocktime_fork_height; + +// Relative PoS constants. //----------------------------------------------------------------------------- +extern const uint64_t pos_enabled_height; +extern const uint32_t pos_coinstake_max_utxos; + +extern const uint64_t pos_lock_min_value; +extern const uint64_t pos_lock_min_height; +extern const uint64_t pos_lock_gap_height; -BC_CONSTEXPR size_t relative_locktime_min_version = 2; -BC_CONSTEXPR size_t relative_locktime_seconds_shift = 9; -BC_CONSTEXPR uint32_t relative_locktime_mask = 0x0000ffff; -BC_CONSTEXPR uint32_t relative_locktime_disabled = 0x80000000; -BC_CONSTEXPR uint32_t relative_locktime_time_locked = 0x00400000; +extern const uint64_t pos_stake_min_value; +extern const uint64_t pos_stake_min_height; +extern const double pos_stake_factor; +extern const uint32_t block_timespan_window; +extern const uint64_t pos_genesis_reward; + +// price +//----------------------------------------------------------------------------- BC_CONSTFUNC uint64_t max_money_recursive(uint64_t current) { @@ -132,6 +173,8 @@ BC_API hash_number max_target(); BC_API std::string get_developer_community_address(bool is_testnet); +BC_API std::string get_foundation_address(bool is_testnet); + } // namespace libbitcoin #endif diff --git a/include/metaverse/bitcoin/error.hpp b/include/metaverse/bitcoin/error.hpp index 496c40e7d..326613a1e 100644 --- a/include/metaverse/bitcoin/error.hpp +++ b/include/metaverse/bitcoin/error.hpp @@ -178,6 +178,20 @@ enum error_code_t mit_not_exist, // 90 sequence_locked, + sync_disabled, + block_version_not_match, + witness_sign_invalid, + witness_mismatch, // 95 + witness_vote_error, + witness_update_error, + + proof_of_stake, + miss_coinstake, + illegal_coinstake, + extra_coinstakes, + coinstake_version_invalid, + cointstake_signature_invalid, + check_pos_genesis_error }; diff --git a/include/metaverse/bitcoin/math/hash_number.hpp b/include/metaverse/bitcoin/math/hash_number.hpp index 1e71d2744..dbfa5bde7 100644 --- a/include/metaverse/bitcoin/math/hash_number.hpp +++ b/include/metaverse/bitcoin/math/hash_number.hpp @@ -33,26 +33,26 @@ namespace libbitcoin { * Used for block proof of works to calculate whether they reach * a certain target or which chain is longest. */ -class hash_number +class BC_API hash_number { public: - BC_API hash_number(); - BC_API hash_number(uint64_t value); + hash_number(); + hash_number(uint64_t value); // Returns false if negative or overflowed. - BC_API bool set_compact(uint32_t compact); - BC_API uint32_t compact() const; - BC_API void set_hash(const hash_digest& hash); - BC_API hash_digest hash() const; + bool set_compact(uint32_t compact); + uint32_t compact() const; + void set_hash(const hash_digest& hash); + hash_digest hash() const; - BC_API const hash_number operator~() const; + const hash_number operator~() const; // int64_t resolves to this in Satoshi's GetNextWorkRequired() - BC_API hash_number& operator*=(uint32_t value); - BC_API hash_number& operator/=(uint32_t value); - BC_API hash_number& operator<<=(uint32_t shift); + hash_number& operator*=(uint32_t value); + hash_number& operator/=(uint32_t value); + hash_number& operator<<=(uint32_t shift); - BC_API hash_number& operator/=(const hash_number& number_b); - BC_API hash_number& operator+=(const hash_number& number_b); + hash_number& operator/=(const hash_number& number_b); + hash_number& operator+=(const hash_number& number_b); private: friend bool operator>( @@ -71,19 +71,6 @@ class hash_number uint256_t hash_; }; -BC_API bool operator>( - const hash_number& number_a, const hash_number& number_b); -BC_API bool operator<=( - const hash_number& number_a, const hash_number& number_b); -BC_API const hash_number operator<<( - const hash_number& number_a, int shift); -BC_API const hash_number operator/( - const hash_number& number_a, const hash_number& number_b); -BC_API const hash_number operator+( - const hash_number& number_a, const hash_number& number_b); -BC_API bool operator==( - const hash_number& number, uint64_t value); - } // namespace libbitcoin #endif diff --git a/include/metaverse/bitcoin/unicode/unicode.hpp b/include/metaverse/bitcoin/unicode/unicode.hpp index faef05f2f..1ec4c2251 100644 --- a/include/metaverse/bitcoin/unicode/unicode.hpp +++ b/include/metaverse/bitcoin/unicode/unicode.hpp @@ -90,15 +90,14 @@ \ int wmain(int argc, wchar_t* argv[]) \ { \ - using namespace libbitcoin; \ boost::locale::generator locale; \ std::locale::global(locale(BC_LOCALE_UTF8)); \ boost::filesystem::path::imbue(std::locale()); \ \ - auto variables = to_utf8(_wenviron); \ + auto variables = bc::to_utf8(_wenviron); \ environ = reinterpret_cast(variables.data()); \ \ - auto arguments = to_utf8(argc, argv); \ + auto arguments = bc::to_utf8(argc, argv); \ auto args = reinterpret_cast(arguments.data()); \ \ return libbitcoin::main(argc, args); \ diff --git a/include/metaverse/bitcoin/utility/random.hpp b/include/metaverse/bitcoin/utility/random.hpp index 0fb4c00fa..45f829f63 100644 --- a/include/metaverse/bitcoin/utility/random.hpp +++ b/include/metaverse/bitcoin/utility/random.hpp @@ -21,13 +21,71 @@ #ifndef MVS_RANDOM_HPP #define MVS_RANDOM_HPP +#include #include #include #include #include +#include namespace libbitcoin { +class BC_API pseudo_random +{ + public: + /** + * Fill a container with randomness using the default random engine. + */ + template + static void fill(Container& out) + { + // uniform_int_distribution is undefined for sizes < 16 bits. + std::uniform_int_distribution distribution(0, max_uint8); + auto& twister = pseudo_random::get_twister(); + + const auto fill = [&distribution, &twister](uint8_t) + { + return static_cast(distribution(twister)); + }; + + std::transform(out.begin(), out.end(), out.begin(), fill); + } + + /** + * Shuffle a container using the default random engine. + */ + template + static void shuffle(Container& out) + { + std::shuffle(out.begin(), out.end(), get_twister()); + } + + /** + * Generate a pseudo random number within the domain. + * @return The 64 bit number. + */ + static uint64_t next(); + + /** + * Generate a pseudo random number within [begin, end]. + * @return The 64 bit number. + */ + static uint64_t next(uint64_t begin, uint64_t end); + + /** + * Convert a time duration to a value in the range [max/ratio, max]. + * @param[in] maximum The maximum value to return. + * @param[in] ratio The determinant of the minimum duration as the inverse + * portion of the maximum duration. + * @return The randomized duration. + */ + static asio::duration duration(const asio::duration& maximum, + uint8_t ratio=2); + + private: + static std::mt19937& get_twister(); +}; + /** * Generate a pseudo random number within the domain. * @return The 64 bit number (use % to subset domain). @@ -40,6 +98,12 @@ BC_API uint64_t pseudo_random(); */ BC_API uint64_t nonzero_pseudo_random(); +/** + * Generate a pseudo random number within [begin, end]. + * @return The 64 bit number. + */ +BC_API uint64_t pseudo_random(uint64_t begin, uint64_t end); + /** * Fill a buffer with randomness using the default random engine. * @param[in] chunk The buffer to fill with randomness. diff --git a/include/metaverse/bitcoin/utility/thread.hpp b/include/metaverse/bitcoin/utility/thread.hpp index 0d0a497f2..fa704c8ec 100644 --- a/include/metaverse/bitcoin/utility/thread.hpp +++ b/include/metaverse/bitcoin/utility/thread.hpp @@ -43,6 +43,7 @@ typedef boost::mutex::scoped_lock scoped_lock; typedef boost::unique_lock unique_lock; typedef boost::shared_lock shared_lock; typedef boost::upgrade_lock upgrade_lock; +typedef boost::upgrade_to_unique_lock upgrade_to_unique_lock; BC_API void set_thread_priority(thread_priority priority); diff --git a/include/metaverse/bitcoin/utility/variable_uint_size.hpp b/include/metaverse/bitcoin/utility/variable_uint_size.hpp index 318e13002..91c4f9665 100644 --- a/include/metaverse/bitcoin/utility/variable_uint_size.hpp +++ b/include/metaverse/bitcoin/utility/variable_uint_size.hpp @@ -24,11 +24,13 @@ #include #include #include +#include namespace libbitcoin { BC_API size_t variable_uint_size(uint64_t value); BC_API size_t variable_string_size(const std::string& str); +BC_API size_t variable_data_chunk_size(const data_chunk& data); BC_API std::string limit_size_string(const std::string& str, size_t limit_size); } // namespace libbitcoin diff --git a/include/metaverse/bitcoin/version.hpp b/include/metaverse/bitcoin/version.hpp index a7aee1381..75caa5649 100644 --- a/include/metaverse/bitcoin/version.hpp +++ b/include/metaverse/bitcoin/version.hpp @@ -12,9 +12,6 @@ * For interpretation of the versioning scheme see: http://semver.org */ -#define MVS_VERSION "0.8.4" -#define MVS_MAJOR_VERSION 0 -#define MVS_MINOR_VERSION 8 -#define MVS_PATCH_VERSION 4 +#define MVS_VERSION "0.8.5" #endif diff --git a/include/metaverse/bitcoin/wallet/select_outputs.hpp b/include/metaverse/bitcoin/wallet/select_outputs.hpp index a64190bf7..4efc939b2 100644 --- a/include/metaverse/bitcoin/wallet/select_outputs.hpp +++ b/include/metaverse/bitcoin/wallet/select_outputs.hpp @@ -38,7 +38,7 @@ struct BC_API select_outputs /// Select optimal outpoints for a spend from unspent outputs list. /// Return includes the amount of change remaining from the spend. static void select(chain::points_info& out, - chain::output_info::list unspent, uint64_t minimum_value, + chain::output_point_info::list unspent, uint64_t minimum_value, algorithm option=algorithm::greedy); }; diff --git a/include/metaverse/bitcoin/wallet/vrf_private.hpp b/include/metaverse/bitcoin/wallet/vrf_private.hpp new file mode 100644 index 000000000..034c808ba --- /dev/null +++ b/include/metaverse/bitcoin/wallet/vrf_private.hpp @@ -0,0 +1,96 @@ +/** + * Copyright (c) 2016-2018 metaverse core developers (see MVS-AUTHORS) + * + * This file is part of metaverse. + * + * metaverse is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#ifndef MVS_WALLET_vrf_private_KEY_HPP +#define MVS_WALLET_vrf_private_KEY_HPP + +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace wallet { + +// +BC_CONSTEXPR size_t vrf_public_size = crypto_vrf_PUBLICKEYBYTES; +typedef byte_array vrf_public; + +BC_CONSTEXPR size_t vrf_secret_size = crypto_vrf_SECRETKEYBYTES; +typedef byte_array vrf_secret; + +BC_CONSTEXPR size_t vrf_proof_size = crypto_vrf_PROOFBYTES; +typedef byte_array vrf_proof; + +BC_CONSTEXPR size_t vrf_hash_size = crypto_vrf_OUTPUTBYTES; +typedef byte_array vrf_hash; + +BC_CONSTEXPR size_t vrf_seed_size = crypto_vrf_SEEDBYTES; +typedef byte_array vrf_seed; + +/// An extended private key, as defined by BIP 32. +class BC_API vrf_private +{ +public: + /// Constructors. + vrf_private(); + vrf_private(const vrf_private& other); + vrf_private(const data_chunk& data); + vrf_private(const vrf_seed & seed); + + + /// Operators. + bool operator<(const vrf_private& other) const; + bool operator==(const vrf_private& other) const; + bool operator!=(const vrf_private& other) const; + vrf_private& operator=(const vrf_private& other); + friend std::istream& operator>>(std::istream& in, vrf_private& to); + friend std::ostream& operator<<(std::ostream& out, + const vrf_private& of); + + /// Cast operators. + operator const vrf_secret&() const; + + /// Serializer. + std::string encoded() const; + + /// Accessors. + const vrf_secret& secret() const; + + /// Methods. + vrf_public to_public() const; + + bool prove(vrf_proof& proof, const data_chunk & m); + + ///verify + //prove to hash + static bool proof_to_hash(vrf_hash& result, const vrf_proof& proof); + //verify proof and pk ,return "result'" + static bool verify(vrf_hash& result, const vrf_proof& proof, + const vrf_public& pk, const data_chunk & m); +private: + vrf_secret secret_; +}; + +} // namespace wallet +} // namespace libbitcoin + +#endif diff --git a/include/metaverse/blockchain/block_chain.hpp b/include/metaverse/blockchain/block_chain.hpp index c1a6e5b22..0e6299ec4 100644 --- a/include/metaverse/blockchain/block_chain.hpp +++ b/include/metaverse/blockchain/block_chain.hpp @@ -49,6 +49,7 @@ class BCB_API block_chain typedef handle1 locator_block_hashes_fetch_handler; typedef handle1 locator_block_headers_fetch_handler; typedef handle1 transaction_hashes_fetch_handler; + typedef handle1 block_signature_fetch_handler; typedef handle1 block_height_fetch_handler; typedef handle1 last_height_fetch_handler; typedef handle1 transaction_fetch_handler; @@ -88,6 +89,12 @@ class BCB_API block_chain virtual void fetch_block_transaction_hashes(const hash_digest& hash, transaction_hashes_fetch_handler handler) = 0; + virtual void fetch_block_signature(uint64_t height, + block_signature_fetch_handler handler) = 0; + + virtual void fetch_block_signature(const hash_digest& hash, + block_signature_fetch_handler handler) = 0; + virtual void fetch_block_locator(block_locator_fetch_handler handler) = 0; virtual void fetch_locator_block_hashes(const message::get_blocks& locator, @@ -132,6 +139,16 @@ class BCB_API block_chain virtual void fired() = 0; // used for removing out of date action virtual organizer& get_organizer() = 0; + + virtual bool check_pos_capability( + uint64_t best_height, + const wallet::payment_address& pay_addres, + bool wait_db = true) = 0; + virtual bool select_utxo_for_staking( + uint64_t best_height, + const wallet::payment_address& pay_addres, + chain::output_info::list& outputs, + uint32_t max_count = max_uint32) = 0; }; } // namespace blockchain diff --git a/include/metaverse/blockchain/block_chain_impl.hpp b/include/metaverse/blockchain/block_chain_impl.hpp index 61d11913b..a626ce020 100644 --- a/include/metaverse/blockchain/block_chain_impl.hpp +++ b/include/metaverse/blockchain/block_chain_impl.hpp @@ -35,20 +35,32 @@ #include #include #include -#include +#include #define LOG_BLOCK_CHAIN_IMPL "block_chain_impl" -using namespace libbitcoin::message; namespace libbitcoin { namespace blockchain { typedef console_result operation_result; +class block_chain_impl; +class validate_block; + +// encap to safer write database +class block_chain_writer { +public: + block_chain_writer(block_chain_impl& chain); + ~block_chain_writer(); +private: + block_chain_impl& chain_; +}; + /// The simple_chain interface portion of this class is not thread safe. class BCB_API block_chain_impl : public block_chain, public simple_chain { + friend class block_chain_writer; public: block_chain_impl(threadpool& pool, const blockchain::settings& chain_settings, @@ -96,6 +108,7 @@ class BCB_API block_chain_impl /// Get the header of the block at the given height. bool get_header(chain::header& out_header, uint64_t height) const; + uint64_t get_transaction_count(uint64_t block_height) const; /// Get the height of the block with the given hash. bool get_height(uint64_t& out_height, const hash_digest& block_hash) const; @@ -157,6 +170,15 @@ class BCB_API block_chain_impl void fetch_block_transaction_hashes(const hash_digest& hash, transaction_hashes_fetch_handler handler); + /// fetch hashes of transactions for a block, by block height. + void fetch_block_signature(uint64_t height, + block_signature_fetch_handler handler); + + /// fetch hashes of transactions for a block, by block hash. + void fetch_block_signature(const hash_digest& hash, + block_signature_fetch_handler handler); + + /// fetch a block locator relative to the current top and threshold. void fetch_block_locator(block_locator_fetch_handler handler); @@ -219,9 +241,43 @@ class BCB_API block_chain_impl /// Subscribe to blockchain reorganizations. virtual void subscribe_reorganize(reorganize_handler handler); - inline hash_digest get_hash(const std::string& str); - inline short_hash get_short_hash(const std::string& str); - + /// must be have enough etp locked in the address + virtual bool check_pos_capability( + uint64_t best_height, + const wallet::payment_address& pay_addres, + bool need_sync_lock = true); + + /// select pos utxo. target value + virtual bool select_utxo_for_staking( + uint64_t best_height, + const wallet::payment_address& pay_addres, + chain::output_info::list& stake_outputs, + uint32_t max_count = max_uint32) override; + + inline bool check_pos_utxo_height_and_value( + const uint64_t& out_height, + const uint64_t& curr_height, + const uint64_t& value) + { + return (value >= pos_stake_min_value) && (out_height + pos_stake_min_height <= curr_height); + } + + bool check_pos_utxo_capability( + const uint64_t& height, + const chain::transaction& tx, + const uint32_t& out_index , + const uint64_t& out_height, + bool strict=true, + const validate_block* validate_block=nullptr + ); + + bool pos_exist_before(const uint64_t& height); + + virtual chain::header::ptr get_last_block_header(const chain::header& parent_header, bool is_staking) const; + + inline hash_digest get_hash(const std::string& str) const; + inline short_hash get_short_hash(const std::string& str) const; + std::shared_ptr get_spends_output(const input_point& input); @@ -262,18 +318,19 @@ class BCB_API block_chain_impl bool is_asset_exist(const std::string& asset_name, bool check_local_db=true); uint64_t get_asset_height(const std::string& asset_name) const ; std::shared_ptr get_local_assets(); - std::shared_ptr get_issued_assets(const std::string& symbol=""); + std::shared_ptr get_issued_assets( + const std::string& symbol="", const std::string& address=""); std::shared_ptr get_issued_asset(const std::string& symbol); std::shared_ptr get_account_assets(); std::shared_ptr get_account_unissued_assets(const std::string& name); std::shared_ptr get_account_unissued_asset( const std::string& name, const std::string& symbol); - + std::shared_ptr get_asset_register_output(const std::string& symbol); // cert api bool is_asset_cert_exist(const std::string& symbol, asset_cert_type cert_type); uint64_t get_asset_cert_height(const std::string& cert_symbol,const asset_cert_type& cert_type); - std::shared_ptr get_issued_asset_certs(); + std::shared_ptr get_issued_asset_certs(const std::string& address = ""); std::shared_ptr get_account_asset_cert( const std::string& account, const std::string& symbol, asset_cert_type cert_type); std::shared_ptr get_account_asset_certs( @@ -292,12 +349,12 @@ class BCB_API block_chain_impl const std::string& account, const std::string& symbol=""); // account did api - bool is_did_exist(const std::string& symbol); + bool is_did_exist(const std::string& symbol) const; uint64_t get_did_height(const std::string& symbol) const; bool is_address_registered_did(const std::string& address, uint64_t fork_index = max_uint64); bool is_account_owned_did(const std::string& account, const std::string& symbol); std::string get_did_from_address(const std::string& address, uint64_t fork_index = max_uint64); - std::shared_ptr get_registered_did(const std::string& symbol); + std::shared_ptr get_registered_did(const std::string& symbol) const; std::shared_ptr get_registered_dids(); std::shared_ptr get_account_dids(const std::string& account); @@ -338,7 +395,7 @@ class BCB_API block_chain_impl chain::transaction& tx, uint64_t& tx_height); bool get_transaction_callback(const hash_digest& hash, std::function handler); - bool get_history_callback(const payment_address& address, + bool get_history_callback(const wallet::payment_address& address, size_t limit, size_t from_height, std::function handler); bool get_history(const wallet::payment_address& address, @@ -348,6 +405,24 @@ class BCB_API block_chain_impl bool get_tx_inputs_etp_value (chain::transaction& tx, uint64_t& etp_val); void safe_store_account(account& acc, std::vector>& addresses); + shared_mutex& get_mutex(); + bool is_sync_disabled() const; + void set_sync_disabled(bool b); + + uint64_t calc_number_of_blocks(uint64_t from, uint64_t to, const validate_block* validate_block=nullptr) const; + uint64_t get_expiration_height(uint64_t from, uint64_t lock_height) const; + + std::pair get_locked_balance(const std::string& address, + uint64_t expiration, const std::string& asset_symbol="") const; + + std::vector> get_register_witnesses( + const std::string& addr, const std::string& symbol, + size_t start_height, size_t end_height=0, uint64_t limit=0, uint64_t page_number=0) const; + + std::shared_ptr get_register_witnesses_with_stake( + const std::string& addr, const std::string& symbol, + size_t start_height, size_t end_height=0, uint64_t limit=0, uint64_t page_number=0) const; + private: typedef std::function perform_read_functor; @@ -379,10 +454,11 @@ class BCB_API block_chain_impl void fetch_serial(perform_read_functor perform_read); bool stopped() const; - std::string get_asset_symbol_from_business_data(const business_data& data); + std::string get_asset_symbol_from_business_data(const business_data& data) const; private: std::atomic stopped_; + std::atomic sync_disabled_; const settings& settings_; // These are thread safe. @@ -393,7 +469,7 @@ class BCB_API block_chain_impl // This is protected by mutex. database::data_base database_; - mutable shared_mutex mutex_; + shared_mutex mutex_; }; } // namespace blockchain diff --git a/include/metaverse/blockchain/organizer.hpp b/include/metaverse/blockchain/organizer.hpp index ad9f8b6b1..45bf56041 100644 --- a/include/metaverse/blockchain/organizer.hpp +++ b/include/metaverse/blockchain/organizer.hpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include @@ -53,7 +52,7 @@ class BCB_API organizer reorganize_handler; /// Construct an instance. - organizer(threadpool& pool, simple_chain& chain, const settings& settings); + organizer(threadpool& pool, block_chain_impl& chain, const settings& settings); /// This method is NOT thread safe. virtual void organize(); @@ -83,7 +82,8 @@ class BCB_API organizer virtual code verify(uint64_t fork_index, const block_detail::list& orphan_chain, uint64_t orphan_index); void process(block_detail::ptr process_block); - void replace_chain(uint64_t fork_index, detail_list& orphan_chain); + /// Return a tuple + std::tuple replace_chain(uint64_t fork_index, detail_list& orphan_chain); void remove_processed(block_detail::ptr remove_block); void clip_orphans(detail_list& orphan_chain, uint64_t orphan_index, const code& invalid_reason); @@ -97,7 +97,7 @@ class BCB_API organizer const config::checkpoint::list checkpoints_; // These are protected by the caller protecting organize(). - simple_chain& chain_; + block_chain_impl& chain_; block_detail::list process_queue_; // These are thread safe. diff --git a/include/metaverse/blockchain/transaction_pool_index.hpp b/include/metaverse/blockchain/transaction_pool_index.hpp index 2725747d6..8c710f4c2 100644 --- a/include/metaverse/blockchain/transaction_pool_index.hpp +++ b/include/metaverse/blockchain/transaction_pool_index.hpp @@ -37,7 +37,7 @@ class BCB_API transaction_pool_index { public: typedef handle0 completion_handler; - typedef handle2 + typedef handle2 query_handler; typedef block_chain::history_fetch_handler fetch_handler; @@ -64,20 +64,20 @@ class BCB_API transaction_pool_index private: typedef chain::spend_info spend_info; - typedef chain::output_info output_info; + typedef chain::output_point_info output_point_info; typedef chain::history_compact::list history_list; typedef wallet::payment_address payment_address; typedef std::unordered_multimap spends_map; - typedef std::unordered_multimap outputs_map; + typedef std::unordered_multimap outputs_map; static bool exists(history_list& history, const spend_info& spend); - static bool exists(history_list& history, const output_info& output); + static bool exists(history_list& history, const output_point_info& output); static void add(history_list& history, const spend_info& spend); - static void add(history_list& history, const output_info& output); + static void add(history_list& history, const output_point_info& output); static void add(history_list& history, const spend_info::list& spends); - static void add(history_list& history, const output_info::list& outputs); + static void add(history_list& history, const output_point_info::list& outputs); static void index_history_fetched(const code& ec, - const spend_info::list& spends, const output_info::list& outputs, + const spend_info::list& spends, const output_point_info::list& outputs, const history_list& history, fetch_handler handler); void blockchain_history_fetched(const code& ec, diff --git a/include/metaverse/blockchain/validate_block.hpp b/include/metaverse/blockchain/validate_block.hpp index 4ad066a20..7df5217d0 100644 --- a/include/metaverse/blockchain/validate_block.hpp +++ b/include/metaverse/blockchain/validate_block.hpp @@ -27,11 +27,12 @@ #include #include #include -#include namespace libbitcoin { namespace blockchain { +class block_chain_impl; + // Max block size (1000000 bytes). constexpr uint32_t max_block_size = 1000000; @@ -50,10 +51,11 @@ class BCB_API validate_block /// Required to call before calling accept_block or connect_block. void initialize_context(); - static size_t legacy_sigops_count(const chain::transaction& tx); - static bool script_hash_signature_operations_count(size_t& out_count, const chain::script& output_script, const chain::script& input_script); + static uint64_t legacy_sigops_count(const chain::transaction& tx); + static bool script_hash_signature_operations_count(uint64_t& out_count, const chain::script& output_script, const chain::script& input_script); - bool get_transaction(const hash_digest& tx_hash, chain::transaction& prev_tx, size_t& prev_height) const; + bool get_transaction(const hash_digest& tx_hash, chain::transaction& prev_tx, uint64_t& prev_height) const; + bool get_header(chain::header& out_header, uint64_t height) const; virtual std::string get_did_from_address_consider_orphan_chain(const std::string& address, const std::string& did_symbol) const = 0; virtual bool is_did_match_address_in_orphan_chain(const std::string& symbol, const std::string& address) const = 0; @@ -62,36 +64,40 @@ class BCB_API validate_block virtual bool is_asset_cert_in_orphan_chain(const std::string& symbol, asset_cert_type cert_type) const = 0; virtual bool is_asset_mit_in_orphan_chain(const std::string& symbol) const = 0; - virtual size_t get_fork_index() const { return max_size_t; } + virtual uint64_t get_fork_index() const { return max_uint64; } + const uint64_t get_height() const {return height_;} + virtual uint64_t median_time_past() const = 0; + + code check_coinbase(const chain::header& prev_header, bool check_genesis_tx) const; protected: typedef std::vector versions; typedef std::function stopped_callback; - validate_block(size_t height, const chain::block& block, + validate_block(uint64_t height, const chain::block& block, bool testnet, const config::checkpoint::list& checks, stopped_callback stop_callback); virtual bool check_get_coinage_reward_transaction(const chain::transaction& coinage_reward_coinbase, const chain::output& tx) const = 0; - virtual uint64_t median_time_past() const = 0; virtual u256 previous_block_bits() const = 0; - virtual uint64_t actual_time_span(size_t interval) const = 0; - virtual versions preceding_block_versions(size_t count) const = 0; - virtual chain::header fetch_block(size_t fetch_height) const = 0; + virtual uint64_t actual_time_span(uint64_t interval) const = 0; + virtual versions preceding_block_versions(uint64_t count) const = 0; + virtual chain::header fetch_block(uint64_t fetch_height) const = 0; + virtual chain::header::ptr get_last_block_header(const chain::header& parent_header, bool is_staking) const = 0; virtual bool transaction_exists(const hash_digest& tx_hash) const = 0; - virtual bool fetch_transaction(chain::transaction& tx, size_t& tx_height, + virtual bool fetch_transaction(chain::transaction& tx, uint64_t& tx_height, const hash_digest& tx_hash) const = 0; virtual bool is_output_spent(const chain::output_point& outpoint) const = 0; virtual bool is_output_spent(const chain::output_point& previous_output, - size_t index_in_parent, size_t input_index) const = 0; + uint64_t index_in_parent, uint64_t input_index) const = 0; // These have default implementations that can be overriden. - virtual bool connect_input(size_t index_in_parent, - const chain::transaction& current_tx, size_t input_index, - uint64_t& value_in, size_t& total_sigops) const; + virtual bool connect_input(uint64_t index_in_parent, + const chain::transaction& current_tx, uint64_t input_index, + uint64_t& value_in, uint64_t& total_sigops) const; virtual bool validate_inputs(const chain::transaction& tx, - size_t index_in_parent, uint64_t& value_in, - size_t& total_sigops) const; + uint64_t index_in_parent, uint64_t& value_in, + uint64_t& total_sigops) const; // These are protected virtual for testability. bool stopped() const; @@ -103,18 +109,23 @@ class BCB_API validate_block bool check_time_stamp(uint32_t timestamp, const asio::seconds& window) const; u256 work_required(bool is_testnet) const; + bool check_block_signature(blockchain::block_chain_impl& chain) const; + + virtual bool verify_stake(const chain::block& block) const = 0; + virtual bool is_coin_stake(const chain::block& block) const = 0; + + + virtual bool check_work(const chain::block& block) const = 0; + static bool is_distinct_tx_set(const chain::transaction::list& txs); - virtual bool is_valid_proof_of_work(const chain::header& header)const = 0; - static bool is_valid_coinbase_height(size_t height, - const chain::block& block); - //static size_t legacy_sigops_count(const chain::transaction& tx); - static size_t legacy_sigops_count(const chain::transaction::list& txs); + static bool is_valid_coinbase_height(uint64_t height, const chain::block& block, uint64_t index); + //static uint64_t legacy_sigops_count(const chain::transaction& tx); + static uint64_t legacy_sigops_count(const chain::transaction::list& txs); private: bool testnet_; - const size_t height_; + const uint64_t height_; uint32_t activations_; - uint32_t minimum_version_; const chain::block& current_block_; const config::checkpoint::list& checkpoints_; const stopped_callback stop_callback_; diff --git a/include/metaverse/blockchain/validate_block_impl.hpp b/include/metaverse/blockchain/validate_block_impl.hpp index e174d0d9a..21ade6f40 100644 --- a/include/metaverse/blockchain/validate_block_impl.hpp +++ b/include/metaverse/blockchain/validate_block_impl.hpp @@ -25,24 +25,31 @@ #include #include #include -#include #include +#include namespace libbitcoin { namespace blockchain { +class block_chain_impl; + /// This class is not thread safe. class BCB_API validate_block_impl : public validate_block { public: - validate_block_impl(simple_chain& chain, size_t fork_index, - const block_detail::list& orphan_chain, size_t orphan_index, - size_t height, const chain::block& block, bool testnet, + validate_block_impl(block_chain_impl& chain, uint64_t fork_index, + const block_detail::list& orphan_chain, uint64_t orphan_index, + uint64_t height, const chain::block& block, bool testnet, const config::checkpoint::list& checkpoints, stopped_callback stopped); - virtual bool is_valid_proof_of_work(const chain::header& header) const; - virtual bool check_get_coinage_reward_transaction(const chain::transaction& coinage_reward_coinbase, const chain::output& output) const; + + virtual bool verify_stake(const chain::block& block) const; + virtual bool is_coin_stake(const chain::block& block) const; + + virtual bool check_work(const chain::block& block) const; + virtual bool check_get_coinage_reward_transaction( + const chain::transaction& coinage_reward_coinbase, const chain::output& output) const; virtual std::string get_did_from_address_consider_orphan_chain(const std::string& address, const std::string& did_symbol) const override; virtual bool is_did_match_address_in_orphan_chain(const std::string& symbol, const std::string& address) const override; @@ -51,31 +58,32 @@ class BCB_API validate_block_impl virtual bool is_asset_cert_in_orphan_chain(const std::string& symbol, asset_cert_type cert_type) const override; virtual bool is_asset_mit_in_orphan_chain(const std::string& symbol) const override; - virtual size_t get_fork_index() const override { return fork_index_; } + virtual uint64_t get_fork_index() const override { return fork_index_; } + uint64_t median_time_past() const; protected: - uint64_t median_time_past() const; u256 previous_block_bits() const; - uint64_t actual_time_span(size_t interval) const; - versions preceding_block_versions(size_t maximum) const; - chain::header fetch_block(size_t fetch_height) const; - bool fetch_transaction(chain::transaction& tx, size_t& tx_height, + uint64_t actual_time_span(uint64_t interval) const; + versions preceding_block_versions(uint64_t maximum) const; + chain::header fetch_block(uint64_t fetch_height) const; + chain::header::ptr get_last_block_header(const chain::header& parent_header, bool is_staking) const; + bool fetch_transaction(chain::transaction& tx, uint64_t& tx_height, const hash_digest& tx_hash) const; bool is_output_spent(const chain::output_point& outpoint) const; bool is_output_spent(const chain::output_point& previous_output, - size_t index_in_parent, size_t input_index) const; + uint64_t index_in_parent, uint64_t input_index) const; bool transaction_exists(const hash_digest& tx_hash) const; private: bool fetch_orphan_transaction(chain::transaction& tx, - size_t& previous_height, const hash_digest& tx_hash) const; + uint64_t& previous_height, const hash_digest& tx_hash) const; bool orphan_is_spent(const chain::output_point& previous_output, - size_t skip_tx, size_t skip_input) const; + uint64_t skip_tx, uint64_t skip_input) const; - simple_chain& chain_; - size_t height_; - size_t fork_index_; - size_t orphan_index_; + block_chain_impl& chain_; + uint64_t height_; + uint64_t fork_index_; + uint64_t orphan_index_; const block_detail::list& orphan_chain_; }; diff --git a/include/metaverse/blockchain/validate_transaction.hpp b/include/metaverse/blockchain/validate_transaction.hpp index 0d6645e17..42ef7c35b 100644 --- a/include/metaverse/blockchain/validate_transaction.hpp +++ b/include/metaverse/blockchain/validate_transaction.hpp @@ -27,13 +27,14 @@ #include #include #include -#include -#include namespace libbitcoin { namespace blockchain { class validate_block; +class transaction_pool; +class block_chain_impl; +class block_chain; /// This class is not thread safe. /// This is a utility for transaction_pool::validate and validate_block. @@ -55,12 +56,14 @@ class BCB_API validate_transaction void start(validate_handler handler); static bool check_consensus(const chain::script& prevout_script, - const chain::transaction& current_tx, size_t input_index, + const chain::transaction& current_tx, uint64_t input_index, uint32_t flags); - code check_transaction_connect_input(size_t last_height); + code check_transaction_connect_input(uint64_t last_height); code check_transaction() const; code check_transaction_basic() const; + code check_sequence_locks() const; + code check_final_tx() const; code check_asset_issue_transaction() const; code check_asset_cert_transaction() const; code check_secondaryissue_transaction() const; @@ -72,10 +75,10 @@ class BCB_API validate_transaction code check_attachment_to_did(const output& output) const; code connect_attachment_from_did(const output& output) const; - bool connect_input(const chain::transaction& previous_tx, size_t parent_height); + bool connect_input(const chain::transaction& previous_tx, uint64_t parent_height); - static bool tally_fees(blockchain::block_chain_impl& chain, - const chain::transaction& tx, uint64_t value_in, uint64_t& fees); + static bool tally_fees(block_chain_impl& chain, + const chain::transaction& tx, uint64_t value_in, uint64_t& fees, bool is_coinstake = false); static bool check_special_fees(bool is_testnet, const chain::transaction& tx, uint64_t fees); bool check_asset_amount(const transaction& tx) const; @@ -87,32 +90,33 @@ class BCB_API validate_transaction //check input did match output did bool check_did_symbol_match(const transaction& tx) const; - static bool is_nova_feature_activated(blockchain::block_chain_impl& chain); + static bool is_nova_feature_activated(block_chain_impl& chain); bool get_previous_tx(chain::transaction& prev_tx, uint64_t& prev_height, const chain::input&) const; - transaction& get_tx() { return *tx_; } - const transaction& get_tx() const { return *tx_; } - blockchain::block_chain_impl& get_blockchain() { return blockchain_; } - const blockchain::block_chain_impl& get_blockchain() const { return blockchain_; } + transaction& get_tx(); + const transaction& get_tx() const; + block_chain_impl& get_blockchain(); + const block_chain_impl& get_blockchain() const; + const validate_block* get_validate_block() const; private: code basic_checks() const; bool is_standard() const; void handle_duplicate_check(const code& ec); - void reset(size_t last_height); + void reset(uint64_t last_height); // Last height used for checking coinbase maturity. - void set_last_height(const code& ec, size_t last_height); + void set_last_height(const code& ec, uint64_t last_height); // Begin looping through the inputs, fetching the previous tx void next_previous_transaction(); - void previous_tx_index(const code& ec, size_t parent_height); + void previous_tx_index(const code& ec, uint64_t parent_height); // If previous_tx_index didn't find it then check in pool instead void search_pool_previous_tx(); void handle_previous_tx(const code& ec, - const chain::transaction& previous_tx, size_t parent_height); + const chain::transaction& previous_tx, uint64_t parent_height); // After running connect_input, we check whether this validated previous // output was not already spent by another input in the blockchain. @@ -132,7 +136,7 @@ class BCB_API validate_transaction const validate_block* const validate_block_; const hash_digest tx_hash_; - size_t last_block_height_; + uint64_t last_block_height_; uint64_t value_in_; uint64_t asset_amount_in_; std::vector asset_certs_in_; diff --git a/include/metaverse/consensus/fts.hpp b/include/metaverse/consensus/fts.hpp new file mode 100644 index 000000000..076b0cc6f --- /dev/null +++ b/include/metaverse/consensus/fts.hpp @@ -0,0 +1,128 @@ +/** + * Copyright (c) 2016-2018 metaverse core developers (see MVS-AUTHORS) + * + * This file is part of metaverse-consensus. + * + * metaverse-consensus is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef MVS_CONSENSUS_FTS_HPP +#define MVS_CONSENSUS_FTS_HPP + +#include +#include + +namespace libbitcoin { +namespace consensus { + +/** + * A stakeholder in the Merkle tree. Each stakeholder has an address, + * and controls an amount of coins. + */ +class fts_stake_holder +{ +public: + typedef std::shared_ptr ptr; + typedef std::vector list; + typedef std::vector ptr_list; + + fts_stake_holder(); + fts_stake_holder(const std::string& address, uint64_t stake); + fts_stake_holder(const fts_stake_holder& other); + + bool operator==(const fts_stake_holder& other) const; + + void set_stake(uint64_t stake); + void set_address(const std::string& address); + + uint64_t stake() const; + std::string address() const; + + std::string to_string() const; + + static std::shared_ptr convert(const list& stakeholders); + + private: + std::string address_; + uint64_t stake_; +}; + +/** + * Node of follow-the-satoshi merkle tree + */ +class fts_node +{ +public: + typedef std::vector list; + typedef std::shared_ptr ptr; + + fts_node(const fts_node::ptr& left, const fts_node::ptr& right); + fts_node(const fts_stake_holder::ptr& stakeholder); + + fts_node::ptr left() const; + fts_node::ptr right() const; + fts_stake_holder::ptr stake_holder() const; + hash_digest hash() const; + + uint64_t stake() const; + bool is_leaf() const; + + std::string to_string() const; + +private : + fts_node::ptr left_; + fts_node::ptr right_; + fts_stake_holder::ptr stake_holder_; + hash_digest hash_; +}; + +/** + * Follow-the-satoshi algorithm + * The edges the Merkle tree are labelled with the amount of coins in the left + * and right subtree respectively. Given a psuedo-random number generator, + * one can randomly select a stakeholder from the tree, weighted by the amount + * of coins they own, by traversing the tree down to a leaf node, containing one + * of the stakeholders. Each stakeholder controls a number of coins + * and a private key used to sign blocks. + */ +class fts +{ +public: + + // select count items from stakeholders. + static std::shared_ptr select_by_fts( + const fts_stake_holder::ptr_list& stakeholders, + uint32_t seed, uint32_t count); + + // build FTS Merkle tree with stakeholders + static fts_node::ptr build_merkle_tree( + const fts_stake_holder::ptr_list& stakeholders); + + // select one item from FTS Merkle tree. + static fts_node::ptr select_by_fts(const fts_node::ptr& merkle_tree, uint32_t seed); + + // target_hash is mixed hash of merkle root and stake leaf node. + static bool verify(const fts_node::ptr& merkle_tree, uint32_t seed, const hash_digest& target_hash); + + static hash_digest to_hash(const fts_node::ptr& left, const fts_node::ptr& right); + static hash_digest to_hash(const fts_stake_holder& stakeholder); + + static void test(); +}; + +} // consensus +} // libbitcoin + +#endif diff --git a/include/metaverse/consensus/libdevcore/BasicType.h b/include/metaverse/consensus/libdevcore/BasicType.h index b48d7671f..6f122e391 100644 --- a/include/metaverse/consensus/libdevcore/BasicType.h +++ b/include/metaverse/consensus/libdevcore/BasicType.h @@ -7,8 +7,10 @@ #include #include "Exceptions.h" #include -#include #include +#include +#include +#include namespace libbitcoin @@ -18,18 +20,40 @@ class HeaderAux { public: static HeaderAux* get(); - static h256 seedHash(libbitcoin::chain::header& _bi); - static h256 hashHead(libbitcoin::chain::header& _bi); - static h256 boundary(libbitcoin::chain::header& _bi) { auto d = _bi.bits; return d ? (h256)u256(((bigint(1) << 255)-bigint(1) +(bigint(1) << 255) ) / d) : h256(); } - static u256 calculateDifficulty(libbitcoin::chain::header& _bi, libbitcoin::chain::header& _parent); - static uint64_t number(h256& _seedHash); - static uint64_t cacheSize(libbitcoin::chain::header& _header); - static uint64_t dataSize(uint64_t _blockNumber); + static h256 seedHash(const chain::header& bi); + static h256 hashHead(const chain::header& bi); + static h256 boundary(const chain::header& bi); + + static uint64_t number(h256& seedHash); + static uint64_t cacheSize(const chain::header& header); + static uint64_t dataSize(uint64_t blockNumber); static void set_as_testnet(){ is_testnet = true; } + static h256 hash_head_pos(const chain::header& header, const chain::output_info& stateOutput); + + static u256 calculate_difficulty( + const chain::header& current, + const chain::header::ptr prev, + const chain::header::ptr pprev, + bool is_staking=false); + + static bigint adjust_difficulty(uint32_t actual_timespan, bigint & result); + private: HeaderAux() {} + + static u256 calculate_difficulty_v1( + const chain::header& current, + const chain::header::ptr prev, + const chain::header::ptr pprev); + + static u256 calculate_difficulty_v2( + const chain::header& current, + const chain::header::ptr prev, + const chain::header::ptr pprev); + +private: Mutex x_epochs; h256s m_seedHashes; std::unordered_map m_epochs; @@ -50,7 +74,7 @@ struct Result struct WorkPackage { WorkPackage() = default; - WorkPackage(libbitcoin::chain::header& _bh); + WorkPackage(chain::header& _bh); h256 boundary; h256 headerHash;///< When h256() means "pause until notified a new work package is available". h256 seedHash; @@ -77,39 +101,4 @@ struct FullAllocation using LightType = std::shared_ptr; using FullType = std::shared_ptr; -struct ChainOperationParams -{ - ChainOperationParams(); - - explicit operator bool() const { return accountStartNonce != Invalid256; } - - /// The chain sealer name: e.g. Ethash, NoProof, BasicAuthority - std::string sealEngineName = "NoProof"; - - /// General chain params. - u256 blockReward = 0; - u256 maximumExtraDataSize = 1024; - u256 accountStartNonce = 0; - bool tieBreakingGas = true; - - /// Precompiled contracts as specified in the chain params. -// std::unordered_map precompiled; - - /** - * @brief Additional parameters. - * - * e.g. Ethash specific: - * - minGasLimit - * - maxGasLimit - * - gasLimitBoundDivisor - * - minimumDifficulty - * - difficultyBoundDivisor - * - durationLimit - */ - std::unordered_map otherParams; - - /// Convenience method to get an otherParam as a u256 int. - u256 u256Param(std::string const& _name); -}; - } diff --git a/include/metaverse/consensus/miner.hpp b/include/metaverse/consensus/miner.hpp index a4d48c9a2..7935316e6 100644 --- a/include/metaverse/consensus/miner.hpp +++ b/include/metaverse/consensus/miner.hpp @@ -23,8 +23,7 @@ #include #include - -#include "metaverse/blockchain/block_chain_impl.hpp" +#include #include "metaverse/blockchain/transaction_pool.hpp" #include "metaverse/bitcoin/chain/block.hpp" #include "metaverse/bitcoin/chain/input.hpp" @@ -35,17 +34,19 @@ namespace libbitcoin { namespace node { class p2p_node; } +namespace blockchain { +class block_chain_impl; +} } namespace libbitcoin { namespace consensus { -BC_CONSTEXPR unsigned int min_tx_fee_per_kb = 1000; -BC_CONSTEXPR unsigned int median_time_span = 11; -BC_CONSTEXPR uint64_t future_blocktime_fork_height = 1030000; +BC_CONSTEXPR uint32_t min_tx_fee_per_kb = 1000; +BC_CONSTEXPR uint32_t median_time_span = 11; extern int bucket_size; -extern vector lock_heights; +extern std::vector lock_heights; class miner { @@ -74,40 +75,67 @@ class miner exit_ }; + void set_pos_params(bool isStaking, const std::string& account, const std::string& passwd); bool start(const wallet::payment_address& pay_address, uint16_t number = 0); - bool start(const std::string& pay_public_key, uint16_t number = 0); bool stop(); static block_ptr create_genesis_block(bool is_mainnet); - bool script_hash_signature_operations_count(size_t &count, const chain::input::list& inputs, - vector& transactions); - bool script_hash_signature_operations_count(size_t &count, const chain::input& input, - vector& transactions); - transaction_ptr create_coinbase_tx(const wallet::payment_address& pay_addres, - uint64_t value, uint64_t block_height, int lock_height, uint32_t reward_lock_time); + bool script_hash_signature_operations_count(uint64_t &count, const chain::input::list& inputs, + std::vector& transactions); + bool script_hash_signature_operations_count(uint64_t &count, const chain::input& input, + std::vector& transactions); + transaction_ptr create_coinbase_tx(const wallet::payment_address& pay_address, + uint64_t value, uint64_t block_height, int lock_height); + transaction_ptr create_coinstake_tx( + const ec_secret& private_key, + const wallet::payment_address& pay_address, + block_ptr pblock, const chain::output_info::list& stake_outputs); + bool sign_coinstake_tx( + const ec_secret& private_key, + transaction_ptr coinstake); + transaction_ptr create_pos_genesis_tx(uint64_t block_height, uint32_t block_time); block_ptr get_block(bool is_force_create_block = false); bool get_work(std::string& seed_hash, std::string& header_hash, std::string& boundary); bool put_result(const std::string& nonce, const std::string& mix_hash, const std::string& header_hash, const uint64_t &nounce_mask); - bool set_miner_public_key(const string& public_key); bool set_miner_payment_address(const wallet::payment_address& address); - void get_state(uint64_t &height, uint64_t &rate, string& difficulty, bool& is_mining); - bool get_block_header(chain::header& block_header, const string& para); + void get_state(uint64_t &height, uint64_t &rate, std::string& difficulty, bool& is_mining); + bool get_block_header(chain::header& block_header, const std::string& para); static int get_lock_heights_index(uint64_t height); - static uint64_t calculate_block_subsidy(uint64_t height, bool is_testnet); + static uint64_t calculate_block_subsidy(uint64_t height, bool is_testnet, uint32_t version); + static uint64_t calculate_block_subsidy_pow(uint64_t height, bool is_testnet); + static uint64_t calculate_block_subsidy_pos(uint64_t height, bool is_testnet); + static uint64_t calculate_block_subsidy_dpos(uint64_t height, bool is_testnet); static uint64_t calculate_lockblock_reward(uint64_t lcok_heights, uint64_t num); + chain::block_version get_accept_block_version() const; + void set_accept_block_version(chain::block_version v); + + bool is_witness() const; + bool set_pub_and_pri_key(const std::string& pubkey, const std::string& prikey); + private: - void work(const wallet::payment_address pay_address); - block_ptr create_new_block(const wallet::payment_address& pay_addres); - unsigned int get_adjust_time(uint64_t height) const; - unsigned int get_median_time_past(uint64_t height) const; + void work(const wallet::payment_address& pay_address); + block_ptr create_new_block(const wallet::payment_address& pay_address); + block_ptr create_new_block_pow(const wallet::payment_address& pay_address); + block_ptr create_new_block_pos(const wallet::payment_address& pay_address); + block_ptr create_new_block_dpos(const wallet::payment_address& pay_address); + + uint32_t get_adjust_time(uint64_t height) const; + uint32_t get_median_time_past(uint64_t height) const; bool get_transaction(std::vector&, previous_out_map_t&, tx_fee_map_t&) const; + bool get_block_transactions( + uint64_t last_height, std::vector& txs, std::vector& reward_txs, + uint64_t& total_fee, uint32_t& total_tx_sig_length); uint64_t store_block(block_ptr block); uint64_t get_height() const; bool get_input_etp(const transaction&, const std::vector&, uint64_t&, previous_out_map_t&) const ; - bool is_stop_miner(uint64_t block_height) const; + bool is_stop_miner(uint64_t block_height, block_ptr block) const; + uint32_t get_tx_sign_length(transaction_ptr tx); + void sleep_for_mseconds(uint32_t interval, bool force = false); + + u256 get_next_target_required(const chain::header& header, const chain::header& prev_header, bool is_staking); private: p2p_node& node_; @@ -115,10 +143,13 @@ class miner mutable state state_; uint16_t new_block_number_; uint16_t new_block_limit_; + chain::block_version accept_block_version_; block_ptr new_block_; wallet::payment_address pay_address_; const blockchain::settings& setting_; + data_chunk public_key_data_; + ec_secret private_key_; }; } diff --git a/include/metaverse/consensus/miner/MinerAux.h b/include/metaverse/consensus/miner/MinerAux.h index 0104a765d..98223207c 100644 --- a/include/metaverse/consensus/miner/MinerAux.h +++ b/include/metaverse/consensus/miner/MinerAux.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include namespace libbitcoin @@ -22,11 +23,12 @@ class MinerAux static void setMixHash(chain::header& _bi, h256& _v){_bi.mixhash = (FixedHash<32>::Arith)_v; } static LightType get_light(h256& _seedHash); static FullType get_full(h256& _seedHash); - static bool verifySeal(chain::header& header,chain::header& _parent); static bool search(chain::header& header, std::function is_exit); static uint64_t getRate(){ return get()->m_rate; } - + static bool verify_work(const chain::header& header, const chain::header::ptr parent); + static bool verify_stake(const chain::header& header, const chain::output_info& stake_output); + static bool check_proof_of_stake(const chain::header& header, const chain::output_info& stake_output); private: MinerAux() {m_rate = 0;} diff --git a/include/metaverse/consensus/witness.hpp b/include/metaverse/consensus/witness.hpp new file mode 100644 index 000000000..7cec8f515 --- /dev/null +++ b/include/metaverse/consensus/witness.hpp @@ -0,0 +1,156 @@ +/** + * Copyright (c) 2016-2018 metaverse core developers (see MVS-AUTHORS) + * + * This file is part of metaverse-consensus. + * + * metaverse-consensus is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#ifndef MVS_CONSENSUS_WITNESS_HPP +#define MVS_CONSENSUS_WITNESS_HPP + +#include +#include + +// forward declaration +namespace libbitcoin { + namespace node { + class p2p_node; + } // node + namespace blockchain { + class settings; + class validate_block; + } // blockchain + namespace wallet { + class payment_address; + } // wallet +} // libbitcoin + +namespace libbitcoin { +namespace consensus { + +class witness +{ +public: + using p2p_node = libbitcoin::node::p2p_node; + using settings = blockchain::settings; + using validate_block = blockchain::validate_block; + + using public_key_t = data_chunk; + using witness_id = data_chunk; + using list = std::vector; + using iterator = list::iterator; + using const_iterator = list::const_iterator; + + static uint32_t pow_check_point_height; + static uint64_t witness_enable_height; + static uint32_t witness_number; + static uint32_t epoch_cycle_height; + static uint32_t register_witness_lock_height; + static uint64_t witness_lock_threshold; + static uint32_t vote_maturity; + + static const uint32_t max_candidate_count; + static const uint32_t witness_register_fee; + static const std::string witness_registry_did; + +public: + ~witness(); + + // singleton + static witness& create(p2p_node& node); + static witness& get(); + + const witness_id& get_witness(uint32_t slot) const; + + // return a copy list + list get_witness_list() const; + void swap_witness_list(list&); + + std::string show_list() const; + static std::string show_list(const list& witness_list); + + bool is_witness(const witness_id& id) const; + + // generate a new epoch witness list + bool calc_witness_list(uint64_t height); + bool calc_witness_list(list& witness_list, uint64_t height) const; + bool init_witness_list(); + bool update_witness_list(uint64_t height); + bool update_witness_list(const chain::block& block); + chain::output create_witness_vote_result(uint64_t height); + bool add_witness_vote_result(chain::transaction& coinbase_tx, uint64_t block_height); + chain::block::ptr fetch_vote_result_block(uint64_t height); + + uint32_t get_slot_num(const witness_id& id) const; + uint32_t calc_slot_num(uint64_t height) const; + size_t get_witness_number(); + + static public_key_t witness_to_public_key(const witness_id& id); + static std::string witness_to_string(const witness_id& id); + static std::string endorse_to_string(const endorsement& endorse); + static witness_id to_witness_id(const public_key_t& public_key); + static std::string to_string(const public_key_t& public_key); + + static bool is_witness_enabled(uint64_t height); + static bool is_dpos_enabled(); + static uint64_t get_height_in_epoch(uint64_t height); + static uint64_t get_vote_result_height(uint64_t height); + static uint64_t get_round_begin_height(uint64_t height); + + static bool is_begin_of_epoch(uint64_t height); + static bool is_between_vote_maturity_interval(uint64_t height); + static bool is_in_same_epoch(uint64_t height1, uint64_t height2); + + // signature + static bool sign(endorsement& out, const ec_secret& secret, const chain::header& h); + static bool verify_sign(const endorsement& out, const public_key_t& public_key, const chain::header& h); + bool verify_signer(const public_key_t& public_key, uint64_t height) const; + bool verify_signer(uint32_t witness_slot_num, uint64_t height) const; + bool verify_vote_result(const chain::block& block, list& witness_list) const; + + static u256 calc_mixhash(const list& witness_list); + + void set_validate_block(const validate_block*); + bool get_header(chain::header& out_header, uint64_t height) const; + +private: + witness(p2p_node& node); + static void init(p2p_node& node); + static bool exists(const list&, const witness_id&); + static const_iterator finds(const list&, const witness_id&); + static iterator finds(list&, const witness_id&); + +private: + static witness* instance_; + p2p_node& node_; + const settings& setting_; + list witness_list_; + const validate_block* validate_block_; + mutable upgrade_mutex mutex_; +}; + +struct witness_with_validate_block_context +{ + using validate_block = blockchain::validate_block; + witness& witness_; + witness_with_validate_block_context(witness& w, const validate_block* v); + ~witness_with_validate_block_context(); +}; + +} // consensus +} // libbitcoin + +#endif diff --git a/include/metaverse/database/data_base.hpp b/include/metaverse/database/data_base.hpp index 1794644c6..22d37fc30 100644 --- a/include/metaverse/database/data_base.hpp +++ b/include/metaverse/database/data_base.hpp @@ -58,9 +58,6 @@ #include #include -using namespace libbitcoin::wallet; -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { @@ -188,11 +185,11 @@ class BCD_API data_base void push(const chain::block& block, uint64_t height); /// Throws if the chain is empty. - chain::block pop(); + bool pop(chain::block& block); /* begin store asset info into database */ - void push_attachment(const attachment& attach, const payment_address& address, + void push_attachment(const attachment& attach, const wallet::payment_address& address, const output_point& outpoint, uint32_t output_height, uint64_t value); void push_etp(const etp& etp, const short_hash& key, diff --git a/include/metaverse/database/databases/account_address_database.hpp b/include/metaverse/database/databases/account_address_database.hpp index 5ca081424..351c9ef4b 100644 --- a/include/metaverse/database/databases/account_address_database.hpp +++ b/include/metaverse/database/databases/account_address_database.hpp @@ -28,8 +28,6 @@ #include #include #include -//#include // todo -- remove later -using namespace libbitcoin::chain; namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/databases/account_asset_database.hpp b/include/metaverse/database/databases/account_asset_database.hpp index bd6a60700..d2a4d5f2a 100644 --- a/include/metaverse/database/databases/account_asset_database.hpp +++ b/include/metaverse/database/databases/account_asset_database.hpp @@ -29,8 +29,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/databases/account_database.hpp b/include/metaverse/database/databases/account_database.hpp index 1da137486..f3f7aeb43 100644 --- a/include/metaverse/database/databases/account_database.hpp +++ b/include/metaverse/database/databases/account_database.hpp @@ -29,8 +29,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/databases/address_asset_database.hpp b/include/metaverse/database/databases/address_asset_database.hpp index b52a7e83c..7fff9f4e3 100644 --- a/include/metaverse/database/databases/address_asset_database.hpp +++ b/include/metaverse/database/databases/address_asset_database.hpp @@ -30,8 +30,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/databases/address_did_database.hpp b/include/metaverse/database/databases/address_did_database.hpp index e9005a34d..ec1fbf3e8 100644 --- a/include/metaverse/database/databases/address_did_database.hpp +++ b/include/metaverse/database/databases/address_did_database.hpp @@ -29,8 +29,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/databases/address_mit_database.hpp b/include/metaverse/database/databases/address_mit_database.hpp index b72c37c6a..0931d6e92 100644 --- a/include/metaverse/database/databases/address_mit_database.hpp +++ b/include/metaverse/database/databases/address_mit_database.hpp @@ -28,8 +28,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/databases/asset_database.hpp b/include/metaverse/database/databases/asset_database.hpp index 21e1fbc85..5e93c00ca 100644 --- a/include/metaverse/database/databases/asset_database.hpp +++ b/include/metaverse/database/databases/asset_database.hpp @@ -30,8 +30,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/databases/mit_history_database.hpp b/include/metaverse/database/databases/mit_history_database.hpp index fc3fb9b78..0565e78c2 100644 --- a/include/metaverse/database/databases/mit_history_database.hpp +++ b/include/metaverse/database/databases/mit_history_database.hpp @@ -28,8 +28,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/memory/allocator.hpp b/include/metaverse/database/memory/allocator.hpp index fe3ebfe4f..cd7a8bbfc 100644 --- a/include/metaverse/database/memory/allocator.hpp +++ b/include/metaverse/database/memory/allocator.hpp @@ -38,7 +38,7 @@ class BCD_API allocator : public memory { public: - allocator(shared_mutex& mutex); + allocator(upgrade_mutex& mutex); ~allocator(); /// This class is not copyable. @@ -57,7 +57,7 @@ class BCD_API allocator void downgrade(uint8_t* data); private: - shared_mutex& mutex_; + upgrade_mutex& mutex_; uint8_t* data_; }; diff --git a/include/metaverse/database/result/account_address_result.hpp b/include/metaverse/database/result/account_address_result.hpp index f754e589b..9ffdf046e 100644 --- a/include/metaverse/database/result/account_address_result.hpp +++ b/include/metaverse/database/result/account_address_result.hpp @@ -28,8 +28,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/result/account_asset_result.hpp b/include/metaverse/database/result/account_asset_result.hpp index 9fb7a5190..9c48ea045 100644 --- a/include/metaverse/database/result/account_asset_result.hpp +++ b/include/metaverse/database/result/account_asset_result.hpp @@ -28,8 +28,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/result/account_result.hpp b/include/metaverse/database/result/account_result.hpp index 633960537..c2739e685 100644 --- a/include/metaverse/database/result/account_result.hpp +++ b/include/metaverse/database/result/account_result.hpp @@ -29,9 +29,6 @@ #include #include -using namespace libbitcoin::chain; -using namespace std; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/result/asset_result.hpp b/include/metaverse/database/result/asset_result.hpp index 959c85b47..f5ee68259 100644 --- a/include/metaverse/database/result/asset_result.hpp +++ b/include/metaverse/database/result/asset_result.hpp @@ -28,8 +28,6 @@ #include #include -using namespace libbitcoin::chain; - namespace libbitcoin { namespace database { diff --git a/include/metaverse/database/result/block_result.hpp b/include/metaverse/database/result/block_result.hpp index f744ec22b..33dd03715 100644 --- a/include/metaverse/database/result/block_result.hpp +++ b/include/metaverse/database/result/block_result.hpp @@ -51,6 +51,9 @@ class BCD_API block_result /// A transaction hash where index < transaction_count. hash_digest transaction_hash(size_t index) const; + // the signature for pos block + ec_signature blocksig() const; + private: const memory_ptr slab_; }; diff --git a/include/metaverse/explorer.hpp b/include/metaverse/explorer.hpp index 6382affea..96987a6d6 100644 --- a/include/metaverse/explorer.hpp +++ b/include/metaverse/explorer.hpp @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include diff --git a/include/metaverse/explorer/extensions/account_info.hpp b/include/metaverse/explorer/extensions/account_info.hpp index 20f34e373..4858107f0 100644 --- a/include/metaverse/explorer/extensions/account_info.hpp +++ b/include/metaverse/explorer/extensions/account_info.hpp @@ -22,15 +22,17 @@ #include #include #include -#include -#include -#include -#include -#include -#include -#include +#include #include #include +#include + +namespace libbitcoin { +namespace blockchain { +class block_chain_impl; +} +} + namespace libbitcoin { namespace chain { @@ -71,5 +73,5 @@ class BC_API account_info } // namespace chain } // namespace libbitcoin - +using account_info = libbitcoin::chain::account_info; diff --git a/include/metaverse/explorer/extensions/base_helper.hpp b/include/metaverse/explorer/extensions/base_helper.hpp index 63bc60449..80f868a0f 100644 --- a/include/metaverse/explorer/extensions/base_helper.hpp +++ b/include/metaverse/explorer/extensions/base_helper.hpp @@ -20,9 +20,17 @@ #pragma once #include +#include +#include +#include #include #include -#include + +namespace libbitcoin { +namespace blockchain { +class block_chain_impl; +} +} namespace libbitcoin { namespace explorer { @@ -83,11 +91,12 @@ struct address_asset_record uint64_t amount{0}; // spendable etp amount std::string symbol; uint64_t asset_amount{0}; // spendable asset amount - asset_cert_type asset_cert{asset_cert_ns::none}; + asset_cert_type asset_cert{chain::asset_cert_ns::none}; utxo_attach_type type{utxo_attach_type::invalid}; output_point output; chain::script script; uint32_t hd_index{0}; // only used for multisig tx + uint32_t sequence{max_input_sequence}; }; struct receiver_record @@ -98,35 +107,47 @@ struct receiver_record std::string symbol; uint64_t amount{0}; // etp value uint64_t asset_amount{0}; - asset_cert_type asset_cert{asset_cert_ns::none}; + asset_cert_type asset_cert{chain::asset_cert_ns::none}; utxo_attach_type type{utxo_attach_type::invalid}; attachment attach_elem; // used for MESSAGE_TYPE, used for information transfer etc. chain::input_point input_point{null_hash, max_uint32}; + bool is_lock_seq_{false}; receiver_record() : target() , symbol() , amount(0) , asset_amount(0) - , asset_cert(asset_cert_ns::none) + , asset_cert(chain::asset_cert_ns::none) , type(utxo_attach_type::invalid) , attach_elem() , input_point{null_hash, max_uint32} + , is_lock_seq_(false) + {} + + receiver_record(const std::string& target_, const std::string& symbol_, + uint64_t amount_, uint64_t asset_amount_, + utxo_attach_type type_, const attachment& attach_elem_, bool is_lock_seq) + : receiver_record(target_, symbol_, amount_, asset_amount_, + chain::asset_cert_ns::none, type_, attach_elem_, + chain::input_point(null_hash, max_uint32), is_lock_seq) {} receiver_record(const std::string& target_, const std::string& symbol_, uint64_t amount_, uint64_t asset_amount_, utxo_attach_type type_, const attachment& attach_elem_ = attachment(), - const chain::input_point& input_point_ = {null_hash, max_uint32}) + const chain::input_point& input_point_ = {null_hash, max_uint32}, + bool is_lock_seq = false) : receiver_record(target_, symbol_, amount_, asset_amount_, - asset_cert_ns::none, type_, attach_elem_, input_point_) + chain::asset_cert_ns::none, type_, attach_elem_, input_point_, is_lock_seq) {} receiver_record(const std::string& target_, const std::string& symbol_, uint64_t amount_, uint64_t asset_amount_, asset_cert_type asset_cert_, utxo_attach_type type_, const attachment& attach_elem_ = attachment(), - const chain::input_point& input_point_ = {null_hash, max_uint32}) + const chain::input_point& input_point_ = {null_hash, max_uint32}, + bool is_lock_seq = false) : target(target_) , symbol(symbol_) , amount(amount_) @@ -135,11 +156,21 @@ struct receiver_record , type(type_) , attach_elem(attach_elem_) , input_point(input_point_) + , is_lock_seq_(is_lock_seq) {} bool is_empty() const; }; +struct utxo_balance { + typedef std::vector list; + std::string output_hash; + uint32_t output_index; + uint64_t output_height; + uint64_t unspent_balance; + uint64_t frozen_balance; +}; + struct balances { uint64_t total_received; uint64_t confirmed_balance; @@ -147,8 +178,20 @@ struct balances { uint64_t frozen_balance; }; +struct locked_balance { + typedef std::vector list; + std::string address; + uint64_t locked_value; + uint64_t locked_height; + uint64_t expiration_height; + + bool operator< (const locked_balance& other) const { + return expiration_height < other.expiration_height; + } +}; + struct deposited_balance { - deposited_balance(const std::string& address_, const string& tx_hash_, + deposited_balance(const std::string& address_, const std::string& tx_hash_, uint64_t deposited_, uint64_t expiration_) : address(address_) , tx_hash(tx_hash_) @@ -178,6 +221,9 @@ struct deposited_balance { void sync_fetchbalance(wallet::payment_address& address, bc::blockchain::block_chain_impl& blockchain, balances& addr_balance); +void sync_fetchbalance(wallet::payment_address& address, + bc::blockchain::block_chain_impl& blockchain, std::shared_ptr sh_vec); + void sync_fetch_deposited_balance(wallet::payment_address& address, bc::blockchain::block_chain_impl& blockchain, std::shared_ptr sh_vec); @@ -196,10 +242,15 @@ std::shared_ptr sync_fetch_asset_deposited_view( const std::string& symbol, bc::blockchain::block_chain_impl& blockchain); +void sync_fetch_locked_balance(const std::string& address, + bc::blockchain::block_chain_impl& blockchain, + std::shared_ptr sp_vec, + const std::string& asset_symbol, + uint64_t expiration = 0); -void sync_fetch_asset_cert_balance(const std::string& address, const string& symbol, +void sync_fetch_asset_cert_balance(const std::string& address, const std::string& symbol, bc::blockchain::block_chain_impl& blockchain, - std::shared_ptr sh_vec, asset_cert_type cert_type=asset_cert_ns::none); + std::shared_ptr sh_vec, asset_cert_type cert_type=chain::asset_cert_ns::none); std::string get_random_payment_address(std::shared_ptr>, bc::blockchain::block_chain_impl& blockchain); @@ -216,15 +267,19 @@ std::string get_address_from_did(const std::string& did, std::string get_fee_dividend_address(bc::blockchain::block_chain_impl& blockchain); -bool is_ETH_Address(const string& address); +bool is_ETH_Address(const std::string& address); void check_asset_symbol(const std::string& symbol, bool check_sensitive=false); void check_mit_symbol(const std::string& symbol, bool check_sensitive=false); void check_did_symbol(const std::string& symbol, bool check_sensitive=false); void check_message(const std::string& message, bool check_sensitive=false); +asset_cert_type check_issue_cert(bc::blockchain::block_chain_impl& blockchain, + const std::string& account, const std::string& symbol, const std::string& cert_name); +asset_cert_type check_cert_type_name(const std::string& cert_type_name, bool all=false); class BCX_API base_transfer_common { public: + using exclude_range_t = std::pair; enum filter : uint8_t { FILTER_ETP = 1 << 0, FILTER_ASSET = 1 << 1, @@ -241,13 +296,18 @@ class BCX_API base_transfer_common base_transfer_common( bc::blockchain::block_chain_impl& blockchain, receiver_record::list&& receiver_list, uint64_t fee, - std::string&& symbol, std::string&& from, std::string&& change) + std::string&& symbol, std::string&& from, std::string&& change, + uint32_t locktime = 0, uint32_t sequence = bc::max_input_sequence, + exclude_range_t exclude_etp_range = {0, 0}) : blockchain_{blockchain} , symbol_{std::move(symbol)} , from_{std::move(from)} , mychange_{std::move(change)} , payment_etp_{fee} , receiver_list_{std::move(receiver_list)} + , locktime_(locktime) + , sequence_(sequence) + , exclude_etp_range_(exclude_etp_range) { }; @@ -265,7 +325,7 @@ class BCX_API base_transfer_common virtual bool get_spendable_output(chain::output&, const chain::history&, uint64_t height) const; virtual chain::operation::stack get_script_operations(const receiver_record& record) const; virtual void sync_fetchutxo( - const std::string& prikey, const std::string& addr, filter filter = FILTER_ALL); + const std::string& prikey, const std::string& addr, filter filter = FILTER_ALL, const history::list& spec_rows={}); virtual attachment populate_output_attachment(const receiver_record& record); virtual void sum_payments(); virtual void sum_payment_amount(); @@ -325,6 +385,9 @@ class BCX_API base_transfer_common uint8_t unspent_mit_{0}; std::vector receiver_list_; std::vector from_list_; + uint32_t locktime_; + uint32_t sequence_; + exclude_range_t exclude_etp_range_; }; class BCX_API base_transfer_helper : public base_transfer_common @@ -333,11 +396,15 @@ class BCX_API base_transfer_helper : public base_transfer_common base_transfer_helper(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& from, receiver_record::list&& receiver_list, - uint64_t fee, std::string&& symbol = std::string(""), - std::string&& change = std::string("")) + uint64_t fee, + std::string&& symbol = std::string(""), + std::string&& change = std::string(""), + uint32_t locktime = 0, + uint32_t sequence = bc::max_input_sequence, + exclude_range_t exclude_etp_range = {0, 0}) : base_transfer_common(blockchain, std::move(receiver_list), fee, std::move(symbol), std::move(from), - std::move(change)) + std::move(change), locktime, sequence, exclude_etp_range) , cmd_{cmd} , name_{std::move(name)} , passwd_{std::move(passwd)} @@ -360,10 +427,11 @@ class BCX_API base_multisig_transfer_helper : public base_transfer_helper base_multisig_transfer_helper(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& from, receiver_record::list&& receiver_list, - uint64_t fee, std::string&& symbol, - account_multisig&& multisig_from) + uint64_t fee, std::string&& symbol, + account_multisig&& multisig_from, uint32_t locktime = 0) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::move(from), std::move(receiver_list), fee, std::move(symbol)) + std::move(from), std::move(receiver_list), + fee, std::move(symbol), "", locktime) , multisig_(std::move(multisig_from)) {} @@ -387,9 +455,9 @@ class BCX_API base_transaction_constructor : public base_transfer_common base_transaction_constructor(bc::blockchain::block_chain_impl& blockchain, utxo_attach_type type, std::vector&& from_vec, receiver_record::list&& receiver_list, std::string&& symbol, std::string&& change, - std::string&& message, uint64_t fee) + std::string&& message, uint64_t fee, uint32_t locktime = 0) : base_transfer_common(blockchain, std::move(receiver_list), fee, - std::move(symbol), "", std::move(change)) + std::move(symbol), "", std::move(change), locktime) , type_{type} , message_{std::move(message)} , from_vec_{std::move(from_vec)} @@ -420,9 +488,9 @@ class BCX_API depositing_etp : public base_transfer_helper depositing_etp(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& to, receiver_record::list&& receiver_list, - uint16_t deposit_cycle = 7, uint64_t fee = 10000) + uint16_t deposit_cycle = 7, uint64_t fee = 10000, uint32_t locktime = 0) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::string(""), std::move(receiver_list), fee) + std::string(""), std::move(receiver_list), fee, "", "", locktime) , to_{std::move(to)} , deposit_cycle_{deposit_cycle} {} @@ -446,10 +514,11 @@ class BCX_API depositing_etp_transaction : public base_transaction_constructor depositing_etp_transaction(bc::blockchain::block_chain_impl& blockchain, utxo_attach_type type, std::vector&& from_vec, receiver_record::list&& receiver_list, uint16_t deposit, std::string&& change, - std::string&& message, uint64_t fee) - : base_transaction_constructor(blockchain, type, std::forward>(from_vec), + std::string&& message, uint64_t fee, uint32_t locktime = 0) + : base_transaction_constructor( + blockchain, type, std::forward>(from_vec), std::move(receiver_list), std::string(""), - std::move(change), std::move(message), fee) + std::move(change), std::move(message), fee, locktime) , deposit_{deposit} {} @@ -471,24 +540,45 @@ class BCX_API sending_etp : public base_transfer_helper sending_etp(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& from, receiver_record::list&& receiver_list, - std::string&& change, uint64_t fee) + std::string&& change, uint64_t fee, uint32_t locktime = 0, + exclude_range_t exclude_etp_range = {0, 0}) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::move(from), std::move(receiver_list), fee, "", std::move(change)) + std::move(from), std::move(receiver_list), + fee, "", std::move(change), locktime, + bc::max_input_sequence, exclude_etp_range) {} ~sending_etp(){} }; +class BCX_API lock_sending : public base_transfer_helper +{ +public: + lock_sending(command& cmd, bc::blockchain::block_chain_impl& blockchain, + std::string&& name, std::string&& passwd, + std::string&& from, receiver_record::list&& receiver_list, + std::string&& change, uint64_t fee, uint32_t sequence) + : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), + std::move(from), std::move(receiver_list), + fee, "", std::move(change), 0, sequence) + {} + + ~lock_sending(){} + + chain::operation::stack get_script_operations(const receiver_record& record) const override; +}; + class BCX_API sending_multisig_tx : public base_multisig_transfer_helper { public: sending_multisig_tx(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& from, receiver_record::list&& receiver_list, uint64_t fee, - account_multisig& multisig, std::string&& symbol = std::string("")) + account_multisig& multisig, std::string&& symbol = std::string(""), + uint32_t locktime = 0) : base_multisig_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), std::move(from), std::move(receiver_list), fee, std::move(symbol), - std::move(multisig)) + std::move(multisig), locktime) {} ~sending_multisig_tx(){} @@ -503,9 +593,12 @@ class BCX_API issuing_asset : public base_transfer_helper std::string&& name, std::string&& passwd, std::string&& from, std::string&& symbol, std::string&& model_param, - receiver_record::list&& receiver_list, uint64_t fee, uint32_t fee_percentage_to_miner) + receiver_record::list&& receiver_list, + uint64_t fee, uint32_t fee_percentage_to_miner, + uint32_t locktime = 0) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::move(from), std::move(receiver_list), fee, std::move(symbol)) + std::move(from), std::move(receiver_list), + fee, std::move(symbol), "", locktime) , attenuation_model_param_{std::move(model_param)} , fee_percentage_to_miner_(fee_percentage_to_miner) {} @@ -531,9 +624,11 @@ class BCX_API secondary_issuing_asset : public base_transfer_helper std::string&& name, std::string&& passwd, std::string&& from, std::string&& symbol, std::string&& model_param, - receiver_record::list&& receiver_list, uint64_t fee, uint64_t volume) + receiver_record::list&& receiver_list, + uint64_t fee, uint64_t volume, uint32_t locktime = 0) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::move(from), std::move(receiver_list), fee, std::move(symbol)) + std::move(from), std::move(receiver_list), + fee, std::move(symbol), "", locktime) , volume_(volume) , attenuation_model_param_{std::move(model_param)} {} @@ -551,7 +646,7 @@ class BCX_API secondary_issuing_asset : public base_transfer_helper void populate_tx_header() override { tx_.version = transaction_version::check_nova_feature; - tx_.locktime = 0; + tx_.locktime = locktime_; }; private: @@ -564,15 +659,16 @@ class BCX_API secondary_issuing_asset : public base_transfer_helper class BCX_API sending_asset : public base_transfer_helper { public: - sending_asset(command& cmd, bc::blockchain::block_chain_impl& blockchain, + sending_asset(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& from, std::string&& symbol, std::string&& model_param, receiver_record::list&& receiver_list, uint64_t fee, - std::string&& message, std::string&& change) + std::string&& message, std::string&& change, + uint32_t locktime = 0) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), std::move(from), std::move(receiver_list), fee, - std::move(symbol), std::move(change)) + std::move(symbol), std::move(change), locktime) , attenuation_model_param_{std::move(model_param)} , message_{std::move(message)} {} @@ -596,10 +692,10 @@ class BCX_API registering_did : public base_multisig_transfer_helper std::string&& name, std::string&& passwd, std::string&& from, std::string&& symbol, receiver_record::list&& receiver_list, uint64_t fee, uint32_t fee_percentage_to_miner, - account_multisig&& multisig) + account_multisig&& multisig, uint32_t locktime = 0) : base_multisig_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), std::move(from), std::move(receiver_list), fee, std::move(symbol), - std::move(multisig)) + std::move(multisig), locktime) , fee_percentage_to_miner_(fee_percentage_to_miner) {} @@ -610,7 +706,7 @@ class BCX_API registering_did : public base_multisig_transfer_helper void populate_tx_header() override { tx_.version = transaction_version::check_nova_feature; - tx_.locktime = 0; + tx_.locktime = locktime_; }; private: @@ -623,11 +719,15 @@ class BCX_API sending_multisig_did : public base_transfer_helper sending_multisig_did(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& from, std::string&& feefrom, std::string&& symbol, - receiver_record::list&& receiver_list - , uint64_t fee, account_multisig&& multisig, account_multisig&& multisigto) + receiver_record::list&& receiver_list, uint64_t fee, + account_multisig&& multisig, account_multisig&& multisigto, + uint32_t locktime = 0) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::move(from), std::move(receiver_list), fee, std::move(symbol)) - , fromfee(feefrom), multisig_from_(std::move(multisig)), multisig_to_(std::move(multisigto)) + std::move(from), std::move(receiver_list), + fee, std::move(symbol), "", locktime) + , fromfee(feefrom) + , multisig_from_(std::move(multisig)) + , multisig_to_(std::move(multisigto)) {} ~sending_multisig_did() @@ -644,7 +744,7 @@ class BCX_API sending_multisig_did : public base_transfer_helper void populate_tx_header() override { tx_.version = transaction_version::check_nova_feature; - tx_.locktime = 0; + tx_.locktime = locktime_; }; private: @@ -659,9 +759,12 @@ class BCX_API sending_did : public base_transfer_helper sending_did(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& from, std::string&& feefrom, std::string&& symbol, - receiver_record::list&& receiver_list, uint64_t fee) + receiver_record::list&& receiver_list, + uint64_t fee, uint32_t locktime = 0) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::move(from), std::move(receiver_list), fee, std::move(symbol)),fromfee(feefrom) + std::move(from), std::move(receiver_list), + fee, std::move(symbol), "", locktime) + ,fromfee(feefrom) {} ~sending_did() @@ -673,7 +776,7 @@ class BCX_API sending_did : public base_transfer_helper void populate_tx_header() override { tx_.version = transaction_version::check_nova_feature; - tx_.locktime = 0; + tx_.locktime = locktime_; }; private: @@ -687,10 +790,10 @@ class BCX_API transferring_asset_cert : public base_multisig_transfer_helper std::string&& name, std::string&& passwd, std::string&& from, std::string&& symbol, receiver_record::list&& receiver_list, uint64_t fee, - account_multisig&& multisig_from) + account_multisig&& multisig_from, uint32_t locktime = 0) : base_multisig_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), std::move(from), std::move(receiver_list), fee, std::move(symbol), - std::move(multisig_from)) + std::move(multisig_from), locktime) {} ~transferring_asset_cert() @@ -698,7 +801,7 @@ class BCX_API transferring_asset_cert : public base_multisig_transfer_helper void populate_tx_header() override { tx_.version = transaction_version::check_nova_feature; - tx_.locktime = 0; + tx_.locktime = locktime_; }; }; @@ -708,9 +811,11 @@ class BCX_API issuing_asset_cert : public base_transfer_helper issuing_asset_cert(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& from, std::string&& symbol, - receiver_record::list&& receiver_list, uint64_t fee) + receiver_record::list&& receiver_list, + uint64_t fee, uint32_t locktime = 0) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::move(from), std::move(receiver_list), fee, std::move(symbol)) + std::move(from), std::move(receiver_list), + fee, std::move(symbol), "", locktime) {} ~issuing_asset_cert() @@ -720,7 +825,7 @@ class BCX_API issuing_asset_cert : public base_transfer_helper void populate_tx_header() override { tx_.version = transaction_version::check_nova_feature; - tx_.locktime = 0; + tx_.locktime = locktime_; }; }; class BCX_API registering_mit : public base_transfer_helper @@ -729,9 +834,11 @@ class BCX_API registering_mit : public base_transfer_helper registering_mit(command& cmd, bc::blockchain::block_chain_impl& blockchain, std::string&& name, std::string&& passwd, std::string&& from, std::string&& symbol, std::map&& mit_map, - receiver_record::list&& receiver_list, uint64_t fee) + receiver_record::list&& receiver_list, + uint64_t fee, uint32_t locktime = 0) : base_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::move(from), std::move(receiver_list), fee, std::move(symbol)) + std::move(from), std::move(receiver_list), + fee, std::move(symbol), "", locktime) , mit_map_(mit_map) {} @@ -740,7 +847,7 @@ class BCX_API registering_mit : public base_transfer_helper void populate_tx_header() override { tx_.version = transaction_version::check_nova_feature; - tx_.locktime = 0; + tx_.locktime = locktime_; }; attachment populate_output_attachment(const receiver_record& record) override; @@ -756,10 +863,11 @@ class BCX_API transferring_mit : public base_multisig_transfer_helper std::string&& name, std::string&& passwd, std::string&& from, std::string&& symbol, receiver_record::list&& receiver_list, uint64_t fee, - account_multisig&& multisig_from) + account_multisig&& multisig_from, uint32_t locktime = 0) : base_multisig_transfer_helper(cmd, blockchain, std::move(name), std::move(passwd), - std::move(from), std::move(receiver_list), fee, std::move(symbol), - std::move(multisig_from)) + std::move(from), std::move(receiver_list), + fee, std::move(symbol), + std::move(multisig_from), locktime) {} ~transferring_mit() @@ -767,7 +875,7 @@ class BCX_API transferring_mit : public base_multisig_transfer_helper void populate_tx_header() override { tx_.version = transaction_version::check_nova_feature; - tx_.locktime = 0; + tx_.locktime = locktime_; }; attachment populate_output_attachment(const receiver_record& record) override; diff --git a/include/metaverse/explorer/extensions/command_extension.hpp b/include/metaverse/explorer/extensions/command_extension.hpp index 84b4f51d5..8ff5046db 100644 --- a/include/metaverse/explorer/extensions/command_extension.hpp +++ b/include/metaverse/explorer/extensions/command_extension.hpp @@ -33,6 +33,10 @@ namespace libbitcoin { namespace explorer { namespace commands { +constexpr const char* DEFAULT_INVALID_ASSET_SYMBOL = "#(DEFAULT INVALID ASSET SYMBOL);"; +#define is_default_invalid_asset_symbol(s) ((s) == (DEFAULT_INVALID_ASSET_SYMBOL)) + + struct prikey_amount { std::string first; @@ -91,8 +95,21 @@ class BCX_API colon_delimited2_item if (tokens.size() != 2) return false; - deserialize(height.first_, tokens[0], true); - deserialize(height.second_, tokens[1], true); + if (!tokens[0].empty()) { + deserialize(height.first_, tokens[0], true); + } else if (std::is_arithmetic::value) { + height.first_ = std::numeric_limits::min(); + } else { + height.first_ = T1(); + } + + if (!tokens[1].empty()) { + deserialize(height.second_, tokens[1], true); + } else if (std::is_arithmetic::value) { + height.second_ = std::numeric_limits::max(); + } else { + height.second_ = T2(); + } return true; }; @@ -105,6 +122,36 @@ class BCX_API colon_delimited2_item return result.str(); }; + std::string encode_colon_delimited() + { + return encode_colon_delimited(*this); + } + + bool is_default() const + { + return first_ == T1() && second_ == T2(); + } + + template + bool is_in_range(T3 value, bool default_ok=true) const + { + if (std::is_arithmetic::value && + std::is_arithmetic::value && + std::is_arithmetic::value) { + return (default_ok && is_default()) || (first_ <= value && value < second_); + } + return false; + } + + bool is_valid(bool default_ok=true) const + { + if (std::is_arithmetic::value && + std::is_arithmetic::value) { + return (default_ok && is_default()) || (first_ < second_); + } + return true; + } + /** * Overload stream in. Throws if colon_delimited2_item is invalid. * @param[in] colon_delimited2_item The colon_delimited2_item stream to read the value from. @@ -117,7 +164,7 @@ class BCX_API colon_delimited2_item stream >> tuple; if (!decode_colon_delimited(argument, tuple)) { - throw std::logic_error{"invalid option " + tuple}; + throw std::logic_error{"invalid colon delimited option " + tuple}; } return stream; diff --git a/include/metaverse/explorer/extensions/commands/addnode.hpp b/include/metaverse/explorer/extensions/commands/addnode.hpp index b2a61f417..33b97bd90 100644 --- a/include/metaverse/explorer/extensions/commands/addnode.hpp +++ b/include/metaverse/explorer/extensions/commands/addnode.hpp @@ -68,8 +68,8 @@ class addnode: public command_extension ) ( "NODEADDRESS", - value(&argument_.address)->required(), - "The target node address[x.x.x.x:port]." + value(&argument_.address), + "The target node address[x.x.x.x:port], required by all operations except list|reseed." ) ( "ADMINNAME", @@ -84,7 +84,7 @@ class addnode: public command_extension ( "operation,o", value(&option_.operation), - "The operation[ add|ban ] to the target node address. default: add." + "The operation[ add|ban|peer|list|reseed ] to the target node address. default: add." ); return options; diff --git a/include/metaverse/explorer/extensions/commands/createrawtx.hpp b/include/metaverse/explorer/extensions/commands/createrawtx.hpp index 9a26af390..f38b0fc3e 100644 --- a/include/metaverse/explorer/extensions/commands/createrawtx.hpp +++ b/include/metaverse/explorer/extensions/commands/createrawtx.hpp @@ -20,6 +20,7 @@ #pragma once +#include #include #include #include @@ -66,15 +67,20 @@ class createrawtx: public command_extension value(&option_.type)->required(), "Transaction type. 0 -- transfer etp, 1 -- deposit etp, 3 -- transfer asset" ) + ( + "utxos,u", + value>(&option_.utxos), + "Use the specific UTXO as input. format: \"tx-hash:output-index\"" + ) ( "senders,s", - value>(&option_.senders)->required(), - "Send from addresses" + value>(&option_.senders), + "Send from dids/addresses" ) ( "receivers,r", value>(&option_.receivers)->required(), - "Send to [address:amount]. amount is asset number if sybol option specified" + "Send to [did/address:amount]. amount is asset number if symbol option specified" ) ( "symbol,n", @@ -89,13 +95,20 @@ class createrawtx: public command_extension ( "mychange,m", value(&option_.mychange_address), - "Mychange to this address, includes etp and asset change" + "Mychange to this did/address, includes etp and asset change" ) ( "message,i", value(&option_.message), "Message/Information attached to this transaction" ) +#ifdef ENABLE_LOCKTIME + ( + "locktime,x", + value(&option_.locktime)->default_value(0), + "Locktime. defaults to 0" + ) +#endif ( "fee,f", value(&option_.fee)->default_value(10000), @@ -122,6 +135,7 @@ class createrawtx: public command_extension struct option { uint16_t type; + std::vector utxos; std::vector senders; std::vector receivers; std::string symbol; @@ -129,6 +143,7 @@ class createrawtx: public command_extension std::string message; uint16_t deposit; uint64_t fee; + uint32_t locktime; } option_; diff --git a/include/metaverse/explorer/extensions/commands/deposit.hpp b/include/metaverse/explorer/extensions/commands/deposit.hpp index 2a4c3e70d..0555fe0f2 100644 --- a/include/metaverse/explorer/extensions/commands/deposit.hpp +++ b/include/metaverse/explorer/extensions/commands/deposit.hpp @@ -83,7 +83,7 @@ class deposit : public command_extension ( "address,a", value(&argument_.address), - "The deposit target address." + "The deposit target did/address." ) ( "deposit,d", diff --git a/include/metaverse/explorer/extensions/commands/getaddressasset.hpp b/include/metaverse/explorer/extensions/commands/getaddressasset.hpp index 37f1e2354..40d3194c4 100644 --- a/include/metaverse/explorer/extensions/commands/getaddressasset.hpp +++ b/include/metaverse/explorer/extensions/commands/getaddressasset.hpp @@ -66,7 +66,7 @@ class getaddressasset: public command_extension ( "ADDRESS", value(&argument_.address)->required(), - "address" + "did/address" ) ( "cert,c", diff --git a/include/metaverse/explorer/extensions/commands/getaddressetp.hpp b/include/metaverse/explorer/extensions/commands/getaddressetp.hpp index 16cc057d9..210851ca4 100644 --- a/include/metaverse/explorer/extensions/commands/getaddressetp.hpp +++ b/include/metaverse/explorer/extensions/commands/getaddressetp.hpp @@ -43,14 +43,14 @@ class getaddressetp: public command_extension arguments_metadata& load_arguments() override { return get_argument_metadata() - .add("PAYMENT_ADDRESS", 1); + .add("ADDRESS", 1); } void load_fallbacks (std::istream& input, po::variables_map& variables) override { const auto raw = requires_raw_input(); - load_input(auth_.auth, "PAYMENT_ADDRESS", variables, input, raw); + load_input(argument_.address, "ADDRESS", variables, input, raw); } options_metadata& load_options() override @@ -64,9 +64,24 @@ class getaddressetp: public command_extension "Get a description and instructions for this command." ) ( - "PAYMENT_ADDRESS", - value(&argument_.address)->required(), - "The payment address. If not specified the address is read from STDIN." + "ADDRESS", + value(&argument_.address)->required(), + "did/address. If not specified it is read from STDIN." + ) + ( + "deposited,d", + value(&option_.deposited)->zero_tokens()->default_value(false), + "If specified, then only get deposited etp. Default is not specified." + ) + ( + "utxo,u", + value(&option_.utxo)->zero_tokens()->default_value(false), + "If specified, list all utxos. Default is not specified." + ) + ( + "range,r", + value>(&option_.range), + "Pick utxo whose value is between this range [begin:end)." ); return options; @@ -81,11 +96,14 @@ class getaddressetp: public command_extension struct argument { - bc::wallet::payment_address address; + std::string address; } argument_; struct option { + bool deposited; + bool utxo; + colon_delimited2_item range = {0, 0}; } option_; }; diff --git a/include/metaverse/explorer/extensions/commands/getasset.hpp b/include/metaverse/explorer/extensions/commands/getasset.hpp index a49fb2906..3e95b6baf 100644 --- a/include/metaverse/explorer/extensions/commands/getasset.hpp +++ b/include/metaverse/explorer/extensions/commands/getasset.hpp @@ -72,7 +72,13 @@ class getasset: public command_extension "cert,c", value(&option_.is_cert)->default_value(false)->zero_tokens(), "If specified, then only get related cert. Default is not specified." - ); + ) + ( + "issuer,i", + value(&option_.issuer)->default_value(""), + "did/address. If specified, then only get asset/cert issued by issuer. Default is not specified." + ) + ; return options; } @@ -97,6 +103,7 @@ class getasset: public command_extension struct option { bool is_cert; + std::string issuer; } option_; }; diff --git a/include/metaverse/explorer/extensions/commands/getlocked.hpp b/include/metaverse/explorer/extensions/commands/getlocked.hpp new file mode 100644 index 000000000..fdb63f1aa --- /dev/null +++ b/include/metaverse/explorer/extensions/commands/getlocked.hpp @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +#pragma once +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + + +/************************ getlocked *************************/ + +class getlocked: public command_extension +{ +public: + static const char* symbol(){ return "getlocked";} + const char* name() override { return symbol();} + bool category(int bs) override { return (ex_online & bs ) == bs; } + const char* description() override { return "Get any valid target address ETP balance."; } + + arguments_metadata& load_arguments() override + { + return get_argument_metadata() + .add("ADDRESS", 1); + } + + void load_fallbacks (std::istream& input, + po::variables_map& variables) override + { + const auto raw = requires_raw_input(); + load_input(argument_.address, "ADDRESS", variables, input, raw); + } + + options_metadata& load_options() override + { + using namespace po; + options_description& options = get_option_metadata(); + options.add_options() + ( + BX_HELP_VARIABLE ",h", + value()->zero_tokens(), + "Get a description and instructions for this command." + ) + ( + "ADDRESS", + value(&argument_.address)->required(), + "did/address" + ) + ( + "expiration,e", + value(&option_.expiration)->default_value(0), + "expiration height, should be still locked at this height." + ) + ( + "symbol,s", + value(&option_.asset_symbol)->default_value(DEFAULT_INVALID_ASSET_SYMBOL), + "asset symbol" + ); + + return options; + } + + void set_defaults_from_config (po::variables_map& variables) override + { + } + + console_result invoke (Json::Value& jv_output, + libbitcoin::server::server_node& node) override; + + struct argument + { + std::string address; + } argument_; + + struct option + { + std::string asset_symbol; + uint64_t expiration; + } option_; + +}; + + + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/include/metaverse/explorer/extensions/commands/getnewaccount.hpp b/include/metaverse/explorer/extensions/commands/getnewaccount.hpp index 2c7100152..31a48a488 100644 --- a/include/metaverse/explorer/extensions/commands/getnewaccount.hpp +++ b/include/metaverse/explorer/extensions/commands/getnewaccount.hpp @@ -72,12 +72,12 @@ class getnewaccount: public command_extension ) ( "ACCOUNTNAME", - value(&auth_.name)->required(), + value(&auth_.name)->default_value(""), BX_ACCOUNT_NAME ) ( "ACCOUNTAUTH", - value(&auth_.auth)->required(), + value(&auth_.auth)->default_value(""), BX_ACCOUNT_AUTH ); @@ -89,7 +89,7 @@ class getnewaccount: public command_extension } console_result invoke (Json::Value& jv_output, - libbitcoin::server::server_node& node) override; + bc::server::server_node& node) override; struct argument { @@ -100,6 +100,12 @@ class getnewaccount: public command_extension std::string language; } option_; +private: + console_result create_account(Json::Value& jv_output, + bc::server::server_node& node); + + console_result create_address(Json::Value& jv_output, + bc::server::server_node& node); }; diff --git a/include/metaverse/explorer/extensions/commands/getpublickey.hpp b/include/metaverse/explorer/extensions/commands/getpublickey.hpp index 03fc440d3..9f661aeac 100644 --- a/include/metaverse/explorer/extensions/commands/getpublickey.hpp +++ b/include/metaverse/explorer/extensions/commands/getpublickey.hpp @@ -78,7 +78,7 @@ class getpublickey: public command_extension ( "ADDRESS", value(&argument_.address)->required(), - "Address." + "Did/Address/Publickey." ); return options; diff --git a/include/metaverse/explorer/extensions/commands/importaddress.hpp b/include/metaverse/explorer/extensions/commands/importaddress.hpp new file mode 100644 index 000000000..8992c9127 --- /dev/null +++ b/include/metaverse/explorer/extensions/commands/importaddress.hpp @@ -0,0 +1,123 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +#pragma once +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + + +/************************ importkeyfile *************************/ + +class importaddress: public command_extension +{ +public: + static const char* symbol(){ return "importaddress";} + const char* name() override { return symbol();} + bool category(int bs) override { return (ctgy_extension & bs ) == bs; } + const char* description() override { return "import a script (in hex) or p2sh address that can be watched as if it were in your wallet but cannot be used to spend."; } + + arguments_metadata& load_arguments() override + { + return get_argument_metadata() + .add("ACCOUNTNAME", 1) + .add("ACCOUNTAUTH", 1) + .add("SCRIPT", 1); + } + + void load_fallbacks (std::istream& input, + po::variables_map& variables) override + { + const auto raw = requires_raw_input(); + load_input(auth_.name, "ACCOUNTNAME", variables, input, raw); + load_input(auth_.auth, "ACCOUNTAUTH", variables, input, raw); + load_input(argument_.script, "SCRIPT", variables, input, raw); + } + + options_metadata& load_options() override + { + using namespace po; + options_description& options = get_option_metadata(); + options.add_options() + ( + BX_HELP_VARIABLE ",h", + value()->zero_tokens(), + "Get a description and instructions for this command." + ) + ( + "ACCOUNTNAME", + value(&auth_.name)->required(), + BX_ACCOUNT_NAME + ) + ( + "ACCOUNTAUTH", + value(&auth_.auth)->required(), + BX_ACCOUNT_AUTH + ) + ( + "SCRIPT", + value(&argument_.script)->required(), + "The hex-encoded script or p2sh address." + ) + ( + "description,d", + value(&option_.description), + "The description for this p2sh address." + ); + + return options; + } + + void set_defaults_from_config (po::variables_map& variables) override + { + } + + console_result invoke (Json::Value& jv_output, + libbitcoin::server::server_node& node) override; + + struct argument + { + std::string script; + } argument_; + + struct option + { + option() + : description("") + { + } + + std::string description; + } option_; + +}; + + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/include/metaverse/explorer/extensions/commands/listaddresses.hpp b/include/metaverse/explorer/extensions/commands/listaddresses.hpp index b6a281c10..bd6cee437 100644 --- a/include/metaverse/explorer/extensions/commands/listaddresses.hpp +++ b/include/metaverse/explorer/extensions/commands/listaddresses.hpp @@ -74,6 +74,11 @@ class listaddresses: public command_extension "ACCOUNTAUTH", value(&auth_.auth)->required(), BX_ACCOUNT_AUTH + ) + ( + "script,s", + value(&option_.b_script)->default_value(false)->zero_tokens(), + "If specified, then only get addresses created by \"importaddress\". Default is not specified." ); return options; @@ -92,6 +97,7 @@ class listaddresses: public command_extension struct option { + bool b_script; } option_; }; diff --git a/include/metaverse/explorer/extensions/commands/listtxs.hpp b/include/metaverse/explorer/extensions/commands/listtxs.hpp index 806e634f3..4d61e0506 100644 --- a/include/metaverse/explorer/extensions/commands/listtxs.hpp +++ b/include/metaverse/explorer/extensions/commands/listtxs.hpp @@ -67,18 +67,18 @@ class listtxs: public command_extension ) ( "ACCOUNTNAME", - value(&auth_.name)->required(), + value(&auth_.name), BX_ACCOUNT_NAME ) ( "ACCOUNTAUTH", - value(&auth_.auth)->required(), + value(&auth_.auth), BX_ACCOUNT_AUTH ) ( "address,a", value(&argument_.address), - "Address." + "Did/Address." ) ( "height,e", diff --git a/include/metaverse/explorer/extensions/commands/lock.hpp b/include/metaverse/explorer/extensions/commands/lock.hpp new file mode 100644 index 000000000..dac01b07f --- /dev/null +++ b/include/metaverse/explorer/extensions/commands/lock.hpp @@ -0,0 +1,148 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +#pragma once +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + +class lock: public send_command +{ +public: + static const char* symbol(){ return "lock";} + const char* name() override { return symbol();} + bool category(int bs) override { return (ex_online & bs ) == bs; } + const char* description() override { return "lock etp or asset to a target did/address."; } + + arguments_metadata& load_arguments() override + { + return get_argument_metadata() + .add("ACCOUNTNAME", 1) + .add("ACCOUNTAUTH", 1) + .add("TO_", 1) + .add("AMOUNT", 1) + .add("SEQUENCE", 1); + } + + void load_fallbacks (std::istream& input, + po::variables_map& variables) override + { + const auto raw = requires_raw_input(); + load_input(auth_.name, "ACCOUNTNAME", variables, input, raw); + load_input(auth_.auth, "ACCOUNTAUTH", variables, input, raw); + load_input(argument_.to, "TO_", variables, input, raw); + load_input(argument_.amount, "AMOUNT", variables, input, raw); + load_input(argument_.amount, "SEQUENCE", variables, input, raw); + } + + options_metadata& load_options() override + { + using namespace po; + options_description& options = get_option_metadata(); + options.add_options() + ( + BX_HELP_VARIABLE ",h", + value()->zero_tokens(), + "Get a description and instructions for this command." + ) + ( + "ACCOUNTNAME", + value(&auth_.name)->required(), + BX_ACCOUNT_NAME + ) + ( + "ACCOUNTAUTH", + value(&auth_.auth)->required(), + BX_ACCOUNT_AUTH + ) + ( + "TO_", + value(&argument_.to)->required(), + "Lock to this did/address" + ) + ( + "AMOUNT", + value(&argument_.amount)->required(), + "ETP integer bits." + ) + ( + "SEQUENCE", + value(&argument_.sequence)->required(), + "Lock sequence value" + ) + ( + "symbol,s", + value(&option_.asset_symbol)->default_value(DEFAULT_INVALID_ASSET_SYMBOL), + "Lock asset of this symbol" + ) + ( + "change,c", + value(&option_.change)->default_value(""), + "Change to this did/address" + ) + ( + "memo,m", + value(&option_.memo)->default_value(""), + "Attached memo for this transaction." + ) + ( + "fee,f", + value(&option_.fee)->default_value(10000), + "Transaction fee. defaults to 10000 etp bits" + ); + + return options; + } + + void set_defaults_from_config (po::variables_map& variables) override + { + } + + console_result invoke (Json::Value& jv_output, + libbitcoin::server::server_node& node) override; + + struct argument + { + std::string to; + uint64_t amount; + uint32_t sequence; + } argument_; + + struct option + { + uint64_t fee; + std::string memo; + std::string change; + std::string asset_symbol; + } option_; + +}; + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/include/metaverse/explorer/extensions/commands/popblock.hpp b/include/metaverse/explorer/extensions/commands/popblock.hpp new file mode 100644 index 000000000..6ef743ec9 --- /dev/null +++ b/include/metaverse/explorer/extensions/commands/popblock.hpp @@ -0,0 +1,98 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + + +/************************ popblock *************************/ + +class popblock: public command_extension +{ +public: + popblock() noexcept {}; + + static const char* symbol(){ return "popblock";} + const char* name() override { return symbol();} + bool category(int bs) override { return (ex_online & bs ) == bs; } + const char* description() override { return "popblock from height"; } + + arguments_metadata& load_arguments() override + { + return get_argument_metadata() + .add("height", 1); + } + + void load_fallbacks (std::istream& input, + po::variables_map& variables) override + { + const auto raw = requires_raw_input(); + load_input(argument_.height, "height", variables, input, raw); + } + + options_metadata& load_options() override + { + using namespace po; + options_description& options = get_option_metadata(); + options.add_options() + ( + BX_HELP_VARIABLE ",h", + value()->zero_tokens(), + "Get a description and instructions for this command." + ) + ( + "height", + value(&argument_.height)->required(), + "specify the starting point to pop out blocks. eg, if specified 1000000, then all blocks with height greater than or equal to 1000000 will be poped out." + ); + + return options; + } + + void set_defaults_from_config (po::variables_map& variables) override + { + } + + console_result invoke (Json::Value& jv_output, + libbitcoin::server::server_node& node) override; + + struct argument + { + uint32_t height; + } argument_; + + struct option + { + } option_; + +}; + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/include/metaverse/explorer/extensions/commands/send.hpp b/include/metaverse/explorer/extensions/commands/send.hpp index 2945c3750..884e123bb 100644 --- a/include/metaverse/explorer/extensions/commands/send.hpp +++ b/include/metaverse/explorer/extensions/commands/send.hpp @@ -20,6 +20,7 @@ #pragma once +#include #include #include #include @@ -97,6 +98,18 @@ class send: public send_command value(&option_.memo)->default_value(""), "Attached memo for this transaction." ) +#ifdef ENABLE_LOCKTIME + ( + "locktime,x", + value(&option_.locktime)->default_value(0), + "Locktime. defaults to 0" + ) +#endif + ( + "exclude,e", + value>(&option_.exclude), + "Exclude utxo whose value is between this range [begin:end)." + ) ( "fee,f", value(&option_.fee)->default_value(10000), @@ -123,12 +136,11 @@ class send: public send_command struct option { - option():fee(10000), memo(""), change("") - {}; - uint64_t fee; std::string memo; std::string change; + uint32_t locktime; + colon_delimited2_item exclude = {0, 0}; } option_; }; diff --git a/include/metaverse/explorer/extensions/commands/sendfrom.hpp b/include/metaverse/explorer/extensions/commands/sendfrom.hpp index 131bab8a3..24e9d2dbc 100644 --- a/include/metaverse/explorer/extensions/commands/sendfrom.hpp +++ b/include/metaverse/explorer/extensions/commands/sendfrom.hpp @@ -20,6 +20,7 @@ #pragma once +#include #include #include #include @@ -106,6 +107,18 @@ class sendfrom: public send_command value(&option_.memo)->default_value(""), "The memo to descript transaction" ) +#ifdef ENABLE_LOCKTIME + ( + "locktime,x", + value(&option_.locktime)->default_value(0), + "Locktime. defaults to 0" + ) +#endif + ( + "exclude,e", + value>(&option_.exclude), + "Exclude utxo whose value is between this range [begin:end)." + ) ( "fee,f", value(&option_.fee)->default_value(10000), @@ -124,8 +137,6 @@ class sendfrom: public send_command struct argument { - argument():from(""), to(""), amount(0) - {}; std::string from; std::string to; uint64_t amount; @@ -133,12 +144,11 @@ class sendfrom: public send_command struct option { - option():fee(10000), memo(""), change("") - {}; - uint64_t fee; std::string memo; std::string change; + uint32_t locktime; + colon_delimited2_item exclude = {0, 0}; } option_; }; diff --git a/include/metaverse/explorer/extensions/commands/sendmoreasset.hpp b/include/metaverse/explorer/extensions/commands/sendmoreasset.hpp new file mode 100644 index 000000000..c23ad87b8 --- /dev/null +++ b/include/metaverse/explorer/extensions/commands/sendmoreasset.hpp @@ -0,0 +1,139 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +#pragma once +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + + +/************************ sendmoreasset *************************/ + +class sendmoreasset: public command_extension +{ +public: + static const char* symbol(){ return "sendmoreasset";} + const char* name() override { return symbol();} + bool category(int bs) override { return (ex_online & bs ) == bs; } + const char* description() override { return "sendmoreasset, alias as sendassetmore"; } + + arguments_metadata& load_arguments() override + { + return get_argument_metadata() + .add("ACCOUNTNAME", 1) + .add("ACCOUNTAUTH", 1) + .add("ASSET", 1); + } + + void load_fallbacks (std::istream& input, + po::variables_map& variables) override + { + const auto raw = requires_raw_input(); + load_input(auth_.name, "ACCOUNTNAME", variables, input, raw); + load_input(auth_.auth, "ACCOUNTAUTH", variables, input, raw); + load_input(argument_.symbol, "ASSET", variables, input, raw); + } + + options_metadata& load_options() override + { + using namespace po; + options_description& options = get_option_metadata(); + options.add_options() + ( + BX_HELP_VARIABLE ",h", + value()->zero_tokens(), + "Get a description and instructions for this command." + ) + ( + "ACCOUNTNAME", + value(&auth_.name)->required(), + BX_ACCOUNT_NAME + ) + ( + "ACCOUNTAUTH", + value(&auth_.auth)->required(), + BX_ACCOUNT_AUTH + ) + ( + "ASSET", + value(&argument_.symbol)->required(), + "Asset MST symbol." + ) + ( + "receivers,r", + value>(&argument_.receivers)->required(), + "Send to [did/address:Asset integer bits. see asset ]." + ) + ( + "change,c", + value(&option_.change)->default_value(""), + "Change to this did/address" + ) + ( + "model,m", + value(&option_.attenuation_model_param)->default_value(""), + BX_MST_OFFERING_CURVE + ) + ( + "fee,f", + value(&option_.fee)->default_value(10000), + "Transaction fee. defaults to 10000 ETP bits" + ) + ( + "memo,i", + value(&option_.memo)->default_value(""), + "Attached memo for this transaction." + ); + return options; + } + + void set_defaults_from_config (po::variables_map& variables) override + { + } + + console_result invoke (Json::Value& jv_output, + libbitcoin::server::server_node& node) override; + + struct argument + { + std::string symbol; + std::vector receivers; + } argument_; + + struct option + { + std::string attenuation_model_param; + std::string memo; + std::string change; + uint64_t fee; + } option_; + +}; + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/include/metaverse/explorer/extensions/commands/sendrawtx.hpp b/include/metaverse/explorer/extensions/commands/sendrawtx.hpp index d2fee3e90..e8712c88a 100644 --- a/include/metaverse/explorer/extensions/commands/sendrawtx.hpp +++ b/include/metaverse/explorer/extensions/commands/sendrawtx.hpp @@ -67,11 +67,6 @@ class sendrawtx: public command_extension "TRANSACTION", value(&argument_.transaction)->required(), "The input Base16 transaction to broadcast." - ) - ( - "fee,f", - value(&argument_.fee)->default_value(1000000000), - "The max tx fee. default_value 10 etp" ); return options; @@ -87,7 +82,6 @@ class sendrawtx: public command_extension struct argument { explorer::config::transaction transaction; - uint64_t fee; } argument_; struct option diff --git a/include/metaverse/explorer/extensions/commands/signrawtx.hpp b/include/metaverse/explorer/extensions/commands/signrawtx.hpp index 293cbea16..3e175301c 100644 --- a/include/metaverse/explorer/extensions/commands/signrawtx.hpp +++ b/include/metaverse/explorer/extensions/commands/signrawtx.hpp @@ -81,6 +81,11 @@ class signrawtx: public command_extension "TRANSACTION", value(&argument_.transaction)->required(), "The input Base16 transaction to sign." + ) + ( + "wif,w", + value(&option_.private_key)->default_value(""), + "The wif or private key to sign." ); return options; @@ -91,7 +96,7 @@ class signrawtx: public command_extension } console_result invoke (Json::Value& jv_output, - libbitcoin::server::server_node& node) override; + bc::server::server_node& node) override; struct argument { @@ -100,8 +105,19 @@ class signrawtx: public command_extension struct option { + std::string private_key; } option_; +private: + std::string get_private_key(blockchain::block_chain_impl& blockchain, const std::string& address); + + bc::endorsement sign( + const std::string& private_key, + tx_type tx_, + const uint32_t& index, + const bc::explorer::config::script& config_contract, + data_chunk& public_key_data); + }; } // namespace commands diff --git a/include/metaverse/explorer/extensions/commands/startmining.hpp b/include/metaverse/explorer/extensions/commands/startmining.hpp index 6954d615b..7ea3787e4 100644 --- a/include/metaverse/explorer/extensions/commands/startmining.hpp +++ b/include/metaverse/explorer/extensions/commands/startmining.hpp @@ -20,6 +20,7 @@ #pragma once +#include #include #include #include @@ -78,13 +79,21 @@ class startmining: public command_extension ( "address,a", value(&option_.address), - "The mining target address. Defaults to empty, means a new address will be generated." + "The mining target did/address. Defaults to empty, means a new address will be generated." ) ( "number,n", value(&option_.number)->default_value(0), "The number of mining blocks, useful for testing. Defaults to 0, means no limit." - ); + ) +#ifdef PRIVATE_CHAIN + ( + "consensus,c", + value(&option_.consensus)->default_value("pow"), + "Accept block with the specified consensus, eg. pow, pos, dpos, defaults to pow." + ) +#endif + ; return options; } @@ -104,6 +113,7 @@ class startmining: public command_extension { std::string address; uint16_t number; + std::string consensus = "pow"; } option_; }; diff --git a/include/metaverse/explorer/extensions/commands/validatesymbol.hpp b/include/metaverse/explorer/extensions/commands/validatesymbol.hpp new file mode 100644 index 000000000..260a1ff92 --- /dev/null +++ b/include/metaverse/explorer/extensions/commands/validatesymbol.hpp @@ -0,0 +1,119 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +#pragma once +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + + +/************************ validatesymbol *************************/ + +class validatesymbol: public command_extension +{ +public: + static const char* symbol(){ return "validatesymbol";} + const char* name() override { return symbol();} + bool category(int bs) override { return (ctgy_extension & bs ) == bs; } + const char* description() override { return "validatesymbol "; } + + arguments_metadata& load_arguments() override + { + return get_argument_metadata() + .add("ACCOUNTNAME", 1) + .add("ACCOUNTAUTH", 1) + .add("SYMBOL", 1); + } + + void load_fallbacks (std::istream& input, + po::variables_map& variables) override + { + const auto raw = requires_raw_input(); + load_input(auth_.name, "ACCOUNTNAME", variables, input, raw); + load_input(auth_.auth, "ACCOUNTAUTH", variables, input, raw); + load_input(argument_.symbol, "SYMBOL", variables, input, raw); + } + + options_metadata& load_options() override + { + using namespace po; + options_description& options = get_option_metadata(); + options.add_options() + ( + BX_HELP_VARIABLE ",h", + value()->zero_tokens(), + "Get a description and instructions for this command." + ) + ( + "ACCOUNTNAME", + value(&auth_.name)->required(), + BX_ACCOUNT_NAME + ) + ( + "ACCOUNTAUTH", + value(&auth_.auth)->required(), + BX_ACCOUNT_AUTH + ) + ( + "SYMBOL", + value(&argument_.symbol)->required(), + "Symbol to be validated." + ) + ( + "cert,c", + value(&option_.cert_type)->default_value(""), + "If specified name of cert type, then validate cert symbol. Default is not specified." + ); + + return options; + } + + void set_defaults_from_config (po::variables_map& variables) override + { + } + + console_result invoke (Json::Value& jv_output, + libbitcoin::server::server_node& node) override; + + struct argument + { + std::string symbol; + } argument_; + + struct option + { + std::string cert_type; + } option_; + +}; + + + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/include/metaverse/explorer/utility.hpp b/include/metaverse/explorer/utility.hpp index 1c0a4a812..49550af04 100644 --- a/include/metaverse/explorer/utility.hpp +++ b/include/metaverse/explorer/utility.hpp @@ -246,6 +246,13 @@ BCX_API data_chunk wrap(const wallet::wrapped_data& data); BCX_API std::ostream& write_stream(std::ostream& output, const Json::Value& tree, encoding_engine engine=encoding_engine::info); +/** + * convert text to number, throw logic_error with specified description if failed + * eg. auto number = to_uint32_throw("1234", "wrong number format!") + */ +BCX_API uint32_t to_uint32_throw(const std::string& text, const std::string& except_desc); +BCX_API uint64_t to_uint64_throw(const std::string& text, const std::string& except_desc); + } // namespace explorer } // namespace libbitcoin diff --git a/include/metaverse/explorer/version.hpp b/include/metaverse/explorer/version.hpp deleted file mode 100644 index 11ebf73c7..000000000 --- a/include/metaverse/explorer/version.hpp +++ /dev/null @@ -1,20 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright (c) 2014-2015 libbitcoin-explorer developers (see COPYING). -// -// GENERATED SOURCE CODE, DO NOT EDIT EXCEPT EXPERIMENTALLY -// -/////////////////////////////////////////////////////////////////////////////// -#ifndef MVS_EXPLORER_VERSION_HPP -#define MVS_EXPLORER_VERSION_HPP - -/** - * The semantic version of this repository as: [major].[minor].[patch] - * For interpretation of the versioning scheme see: http://semver.org - */ - -#define MVS_EXPLORER_VERSION "0.8.4" -#define MVS_EXPLORER_MAJOR_VERSION 0 -#define MVS_EXPLORER_MINOR_VERSION 8 -#define MVS_EXPLORER_PATCH_VERSION 4 - -#endif diff --git a/include/metaverse/macros_define.hpp b/include/metaverse/macros_define.hpp new file mode 100644 index 000000000..61adf2a79 --- /dev/null +++ b/include/metaverse/macros_define.hpp @@ -0,0 +1,10 @@ +/** + * Copyright (c) 2016-2018 metaverse core developers (see MVS-AUTHORS) + */ +#ifndef MVS_MACROS_DEFINE_HPP +#define MVS_MACROS_DEFINE_HPP + +// #define PRIVATE_CHAIN +// #define ENABLE_LOCKTIME + +#endif diff --git a/include/metaverse/mgbubble/HttpServ.hpp b/include/metaverse/mgbubble/HttpServ.hpp index 707f89576..2d2454d9e 100644 --- a/include/metaverse/mgbubble/HttpServ.hpp +++ b/include/metaverse/mgbubble/HttpServ.hpp @@ -17,8 +17,6 @@ namespace server{ namespace mgbubble{ -using namespace bc; - class HttpServ : public MgServer { typedef MgServer base; @@ -82,7 +80,7 @@ class HttpServ : public MgServer static thread_local int state_; const char* const servername_{"Metaverse " MVS_VERSION}; libbitcoin::server::server_node &node_; - string document_root_; + std::string document_root_; }; } // mgbubble diff --git a/include/metaverse/network/channel.hpp b/include/metaverse/network/channel.hpp index da8a2eab9..af5a9d2a6 100644 --- a/include/metaverse/network/channel.hpp +++ b/include/metaverse/network/channel.hpp @@ -58,6 +58,9 @@ class BCT_API channel void invoke_protocol_start_handler(const code& ec); + virtual bool stopped(const code& ec) const; + using proxy::stopped; + protected: virtual void handle_activity(); virtual void handle_stopping(); diff --git a/include/metaverse/network/hosts.hpp b/include/metaverse/network/hosts.hpp index 271932790..201eba2c6 100644 --- a/include/metaverse/network/hosts.hpp +++ b/include/metaverse/network/hosts.hpp @@ -44,7 +44,8 @@ namespace network { struct address_compare{ bool operator()(const libbitcoin::message::network_address& lhs, const libbitcoin::message::network_address& rhs) const { - return lhs.ip < rhs.ip ? true : (lhs.ip > rhs.ip ? false : lhs.port < rhs.port); + typedef std::tuple tup_cmp; + return tup_cmp(lhs.ip, lhs.port) < tup_cmp(rhs.ip, rhs.port); } }; @@ -75,38 +76,47 @@ class BCT_API hosts virtual size_t count() const; virtual code fetch(address& out, const config::authority::list& excluded_list); + virtual code fetch_seed(address& out, const config::authority::list& excluded_list); + virtual code store_seed(const address& host); + virtual code remove_seed(const address& host); virtual code remove(const address& host); virtual code store(const address& host); virtual void store(const address::list& hosts, result_handler handler); address::list copy(); -private: - // typedef boost::circular_buffer
list; - using list = std::set; + address::list copy_seeds(); +private: + typedef boost::circular_buffer
list; typedef list::iterator iterator; iterator find(const address& host); - void do_store(const address& host, result_handler handler); + iterator find(list& buffer, const address& host); void handle_timer(const code& ec); + bool store_cache(bool succeed_clear_buffer = false); + + template + code fetch(T& buffer, address& out, const config::authority::list& excluded_list); + + // record the seed count + const size_t seed_count; + const size_t host_pool_capacity_; + // These are protected by a mutex. list buffer_; list backup_; list inactive_; + address::list seeds_; std::atomic stopped_; mutable upgrade_mutex mutex_; - // This is thread safe. - dispatcher dispatch_; - // HACK: we use this because the buffer capacity cannot be set to zero. const bool disabled_; const boost::filesystem::path file_path_; threadpool& pool_; deadline::ptr snap_timer_; - // record the seed count - const size_t seed_count; + const config::authority& self_; }; } // namespace network diff --git a/include/metaverse/network/p2p.hpp b/include/metaverse/network/p2p.hpp index e894daafc..718b435fa 100644 --- a/include/metaverse/network/p2p.hpp +++ b/include/metaverse/network/p2p.hpp @@ -174,10 +174,15 @@ class BCT_API p2p /// Get a randomly-selected address. virtual void fetch_address(const config::authority::list& excluded_list, address_handler handler); + virtual void fetch_seed_address(const config::authority::list& excluded_list, address_handler handler); ///Get authority of all connections config::authority::list authority_list(); + /// Store an address. + void store_seed(const address& address, result_handler handler); + void remove_seed(const address& address, result_handler handler); + /// Store an address. virtual void store(const address& address, result_handler handler); @@ -191,6 +196,7 @@ class BCT_API p2p virtual void address_count(count_handler handler); address::list address_list(); + address::list seed_address_list(); /// Get connection pool. virtual connections::ptr connections_ptr(); @@ -203,7 +209,9 @@ class BCT_API p2p #endif //restart the seeding session - void restart_seeding(); + void restart_seeding(bool manual=false); + + virtual bool is_use_testnet_rules() const {return false;} protected: diff --git a/include/metaverse/network/protocols/protocol_events.hpp b/include/metaverse/network/protocols/protocol_events.hpp index d0fabec11..a07765870 100644 --- a/include/metaverse/network/protocols/protocol_events.hpp +++ b/include/metaverse/network/protocols/protocol_events.hpp @@ -65,7 +65,12 @@ class BCT_API protocol_events /** * Determine if the event handler has been cleared. */ - virtual bool stopped(); + virtual bool stopped() const; + + /** + * Determine if the code is a stop code or the handler has been cleared. + */ + virtual bool stopped(const code& ec) const; protected: void handle_send(const code& ec, const std::string& command); diff --git a/include/metaverse/network/proxy.hpp b/include/metaverse/network/proxy.hpp index b6c9d2ede..49b14028d 100644 --- a/include/metaverse/network/proxy.hpp +++ b/include/metaverse/network/proxy.hpp @@ -107,6 +107,8 @@ class BCT_API proxy static bool manualbanned(const config::authority& authority); static void manual_ban(const config::authority&); static void manual_unban(const config::authority&); + static std::map get_banned(); + static std::list get_manual_banned(); virtual bool misbehaving(int32_t howmuch); diff --git a/include/metaverse/network/sessions/session.hpp b/include/metaverse/network/sessions/session.hpp index 8b3d4f30b..3f40ef6d1 100644 --- a/include/metaverse/network/sessions/session.hpp +++ b/include/metaverse/network/sessions/session.hpp @@ -138,9 +138,11 @@ class BCT_API session /// Properties. virtual void address_count(count_handler handler); virtual void fetch_address(host_handler handler); + virtual void fetch_seed_address(host_handler handler); virtual void connection_count(count_handler handler); virtual bool blacklisted(const authority& authority) const; virtual bool stopped() const; + virtual bool stopped(const code& ec) const; void remove(const message::network_address& address, result_handler handler); diff --git a/include/metaverse/network/sessions/session_batch.hpp b/include/metaverse/network/sessions/session_batch.hpp index 657effe54..54cd23a2e 100644 --- a/include/metaverse/network/sessions/session_batch.hpp +++ b/include/metaverse/network/sessions/session_batch.hpp @@ -47,6 +47,8 @@ class BCT_API session_batch /// Create a channel from the configured number of concurrent attempts. virtual void connect(connector::ptr connect, channel_handler handler); + void connect_seed(connector::ptr connect, channel_handler handler); + private: typedef std::atomic atomic_counter; typedef std::shared_ptr atomic_counter_ptr; @@ -58,7 +60,7 @@ class BCT_API session_batch // Connect sequence void new_connect(connector::ptr connect, atomic_counter_ptr counter, - channel_handler handler); + channel_handler handler, bool only_seed=false); void start_connect(const code& ec, const authority& host, connector::ptr connect, atomic_counter_ptr counter, channel_handler handler); diff --git a/include/metaverse/network/sessions/session_manual.hpp b/include/metaverse/network/sessions/session_manual.hpp index 00209efd0..79abf06f0 100644 --- a/include/metaverse/network/sessions/session_manual.hpp +++ b/include/metaverse/network/sessions/session_manual.hpp @@ -76,9 +76,10 @@ class BCT_API session_manual void handle_channel_start(const code& ec, const std::string& hostname, uint16_t port, channel::ptr channel, channel_handler handler); void handle_channel_stop(const code& ec, const std::string& hostname, - uint16_t port); + uint16_t port, channel::ptr channel); bc::atomic connector_; + deadline::ptr connect_timer_; }; } // namespace network diff --git a/include/metaverse/network/sessions/session_outbound.hpp b/include/metaverse/network/sessions/session_outbound.hpp index 57700a01b..1ab8ef40c 100644 --- a/include/metaverse/network/sessions/session_outbound.hpp +++ b/include/metaverse/network/sessions/session_outbound.hpp @@ -43,6 +43,7 @@ class BCT_API session_outbound /// Construct an instance. session_outbound(p2p& network); + ~session_outbound(); /// Start the session. void start(result_handler handler) override; @@ -50,24 +51,30 @@ class BCT_API session_outbound protected: /// Override to attach specialized protocols upon channel start. virtual void attach_protocols(channel::ptr channel); - void delay_new_connect(connector::ptr connect); + void delay_new_connect(connector::ptr connect, deadline::ptr connect_timer, bool only_seed=false); void delay_reseeding(); private: - void new_connection(connector::ptr connect); + void new_connection(connector::ptr connect, deadline::ptr connect_timer, bool reconnect=true, bool only_seed=false); void handle_started(const code& ec, result_handler handler); void handle_connect(const code& ec, channel::ptr channel, - connector::ptr connect); + connector::ptr connect, deadline::ptr connect_timer, + bool reconnect=true, bool only_seed=false); void handle_channel_stop(const code& ec, connector::ptr connect, - channel::ptr channel); + channel::ptr channel, deadline::ptr connect_timer, bool only_seed=false); void handle_channel_start(const code& ec, connector::ptr connect, channel::ptr channel); + void handle_reseeding(); + std::atomic_int outbound_counter; std::atomic_bool in_reseeding; //to mark if the re-seeding timer is active p2p& network__; + + deadline::ptr reseeding_timer_; + std::vector connect_timer_list_; }; } // namespace network diff --git a/include/metaverse/protocol/zmq/worker.hpp b/include/metaverse/protocol/zmq/worker.hpp index 7843f3d0e..29c3ecea1 100644 --- a/include/metaverse/protocol/zmq/worker.hpp +++ b/include/metaverse/protocol/zmq/worker.hpp @@ -56,6 +56,7 @@ class BCP_API worker protected: bool stopped(); + bool stopped(const code& ec); bool started(bool result); bool finished(bool result); bool forward(socket& from, socket& to); diff --git a/include/metaverse/server/server_node.hpp b/include/metaverse/server/server_node.hpp index c4824e0b4..b7c4f0137 100644 --- a/include/metaverse/server/server_node.hpp +++ b/include/metaverse/server/server_node.hpp @@ -65,6 +65,9 @@ class BCS_API server_node /// Ensure all threads are coalesced. virtual ~server_node(); + /// Invoke startup and seeding sequence, call from constructing thread. + void start(result_handler handler) override; + // Properties. // ---------------------------------------------------------------------------- @@ -105,6 +108,8 @@ class BCS_API server_node /// Get miner. virtual consensus::miner& miner(); + virtual bool is_use_testnet_rules() const override; + bool is_blockchain_sync() const { return under_blockchain_sync_.load(std::memory_order_relaxed); } private: diff --git a/include/metaverse/server/settings.hpp b/include/metaverse/server/settings.hpp index f1c2f7ae6..1fb94fc38 100644 --- a/include/metaverse/server/settings.hpp +++ b/include/metaverse/server/settings.hpp @@ -47,6 +47,7 @@ class BCS_API settings std::string mongoose_listen; std::string websocket_listen; std::string log_level; + std::string rpc_version; bool administrator_required; bool secure_only; diff --git a/src/lib/bitcoin/CMakeLists.txt b/src/lib/bitcoin/CMakeLists.txt index e02deb3cc..691a3e293 100644 --- a/src/lib/bitcoin/CMakeLists.txt +++ b/src/lib/bitcoin/CMakeLists.txt @@ -11,13 +11,13 @@ SET_TARGET_PROPERTIES(secp256k1_static PROPERTIES IMPORTED_LOCATION ${secp256k1_ ADD_LIBRARY(bitcoin_static STATIC ${bitcoin_SOURCES}) SET_TARGET_PROPERTIES(bitcoin_static PROPERTIES OUTPUT_NAME mvs_bitcoin) -TARGET_LINK_LIBRARIES(bitcoin_static ${Boost_LIBRARIES} secp256k1_static ${bitcoinmath_LIBRARY}) +TARGET_LINK_LIBRARIES(bitcoin_static ${Boost_LIBRARIES} secp256k1_static ${bitcoinmath_LIBRARY} ${sodium_LIBRARY}) INSTALL(TARGETS bitcoin_static DESTINATION lib) IF(ENABLE_SHARED_LIBS) ADD_DEFINITIONS(-DBC_DLL=1) ADD_LIBRARY(bitcoin_shared SHARED ${bitcoin_SOURCES}) SET_TARGET_PROPERTIES(bitcoin_shared PROPERTIES OUTPUT_NAME mvs_bitcoin) - TARGET_LINK_LIBRARIES(bitcoin_shared ${Boost_LIBRARIES} secp256k1 ${bitcoinmath_LIBRARY}) + TARGET_LINK_LIBRARIES(bitcoin_shared ${Boost_LIBRARIES} secp256k1 ${bitcoinmath_LIBRARY} ${sodium_LIBRARY}) INSTALL(TARGETS bitcoin_shared DESTINATION lib) ENDIF() diff --git a/src/lib/bitcoin/chain/attachment/account/account.cpp b/src/lib/bitcoin/chain/attachment/account/account.cpp index 4e23c1767..8c2bc976c 100644 --- a/src/lib/bitcoin/chain/attachment/account/account.cpp +++ b/src/lib/bitcoin/chain/attachment/account/account.cpp @@ -37,6 +37,53 @@ using namespace libbitcoin::wallet; namespace libbitcoin { namespace chain { +account_script::account_script() +{ +} + +void account_script::set_description(const std::string& description) +{ + description_ = description; +} + +void account_script::set_address(const std::string& address) +{ + address_ = address; +} + +void account_script::set_script(const data_chunk& script) +{ + script_ = script; +} + +bool account_script::from_data(reader& source) +{ + description_ = source.read_string(); + address_ = source.read_string(); + const auto size = source.read_variable_uint_little_endian(); + script_ = source.read_data(size); + return !address_.empty(); +} + +void account_script::to_data(writer& sink) const +{ + sink.write_string(description_); + sink.write_string(address_); + sink.write_variable_uint_little_endian(script_.size()); + sink.write_data(script_); +} + +bool account_script::operator==(const account_script& other) const +{ + //return (description_ == other.description_) && (address_ == other.address_) && (script_ == other.script_); + return address_ == other.address_; +} + +uint64_t account_script::serialized_size() const +{ + return variable_string_size(description_) + variable_string_size(address_) + variable_data_chunk_size(script_); +} + account_multisig::account_multisig() : hd_index_(0), m_(0), n_(0) { @@ -281,7 +328,7 @@ bool account::from_data_t(reader& source) //status = source.read_2_bytes_little_endian(); type = source.read_byte(); status = source.read_byte(); - if (type == account_type::multisignature) { + if (is_multisignature(type)) { //multisig.from_data(source); account_multisig multisig; uint32_t size = source.read_4_bytes_little_endian(); @@ -291,6 +338,14 @@ bool account::from_data_t(reader& source) multisig_vec.push_back(multisig); } } + if (is_script(type)) { + uint32_t size = source.read_4_bytes_little_endian(); + while (size--) { + account_script script; + script.from_data(source); + script_vec.push_back(script); + } + } return true; } @@ -304,7 +359,7 @@ void account::to_data_t(writer& sink) const //sink.write_2_bytes_little_endian(status); sink.write_byte(type); sink.write_byte(status); - if (type == account_type::multisignature) { + if (is_multisignature(type)) { //multisig.to_data(sink); sink.write_4_bytes_little_endian(multisig_vec.size()); if (multisig_vec.size()) { @@ -313,17 +368,28 @@ void account::to_data_t(writer& sink) const } } } + if (is_script(type)) { + sink.write_4_bytes_little_endian(script_vec.size()); + for (auto& each : script_vec) { + each.to_data(sink); + } + } } uint64_t account::serialized_size() const { uint64_t size = name.size() + mnemonic.size() + passwd.size() + 4 + 1 + 2 + 2 * 9; // 2 string len - if (type == account_type::multisignature) { + if (is_multisignature(type)) { //size += multisig.serialized_size(); size += 4; // vector size for (auto& each : multisig_vec) size += each.serialized_size(); } + if (is_script(type)) { + size += 4; // vector size + for (auto& each : script_vec) + size += each.serialized_size(); + } return size; } @@ -352,7 +418,7 @@ std::string account::to_string() << "\t priority = " << priority << "\n" << "\t type = " << type << "\n" << "\t status = " << status << "\n"; - if (type == account_type::multisignature) { + if (is_multisignature(type)) { for (auto& each : multisig_vec) ss << "\t\t" << each.to_string(); } @@ -420,7 +486,7 @@ uint8_t account::get_type() const void account::set_type(uint8_t type) { - this->type = type; + this->type |= type; } uint8_t account::get_status() const @@ -500,5 +566,53 @@ void account::modify_multisig(const account_multisig& multisig) } } +const account_script::list& account::get_script_vec() const +{ + return script_vec; +} +void account::set_script_vec(account_script::list&& script) +{ + script_vec = std::move(script); +} +bool account::is_script_exist(const account_script& script) +{ + const auto iter = std::find(script_vec.begin(), script_vec.end(), script); + return iter != script_vec.end(); +} +void account::set_script(const account_script& script) +{ + if (!is_script_exist(script)) { + script_vec.push_back(script); + } +} +void account::modify_script(const account_script& script) +{ + for (auto& each : script_vec) { + if (each == script) { + each = script; + break; + } + } +} +void account::remove_script(const account_script& script) +{ + for (auto it = script_vec.begin(); it != script_vec.end(); ++it) { + if (*it == script) { + it = script_vec.erase(it); + break; + } + } +} +std::shared_ptr account::get_script(const std::string& addr) +{ + auto acc_vec = std::make_shared(); + for (auto& each : script_vec) { + if (addr == each.get_address()) { + acc_vec->push_back(each); + } + } + return acc_vec; +} + } // namspace chain } // namspace libbitcoin diff --git a/src/lib/bitcoin/chain/attachment/asset/asset_cert.cpp b/src/lib/bitcoin/chain/attachment/asset/asset_cert.cpp index f604bb9f6..c25dab782 100644 --- a/src/lib/bitcoin/chain/attachment/asset/asset_cert.cpp +++ b/src/lib/bitcoin/chain/attachment/asset/asset_cert.cpp @@ -31,6 +31,12 @@ namespace chain { #define ASSET_SYMBOL_DELIMITER "." +std::istream& operator>>(std::istream& in, asset_cert_type& out){ + uint32_t & cert_type = out.mask; + in >> cert_type; + return in; +} + asset_cert::asset_cert() { reset(); @@ -271,6 +277,15 @@ bool asset_cert::test_certs(const std::vector& total, const std return true; } +bool asset_cert::is_unmovable(asset_cert_type cert_type) +{ + return cert_type.cert_type_status.unmovable; +} + +bool asset_cert::is_unmovable() const +{ + return asset_cert::is_unmovable(cert_type_); +} } // namspace chain } // namspace libbitcoin diff --git a/src/lib/bitcoin/chain/attachment/asset/asset_detail.cpp b/src/lib/bitcoin/chain/attachment/asset/asset_detail.cpp index 27ada96df..f7b3bbe2f 100644 --- a/src/lib/bitcoin/chain/attachment/asset/asset_detail.cpp +++ b/src/lib/bitcoin/chain/attachment/asset/asset_detail.cpp @@ -26,7 +26,6 @@ #include #include #include -#include #define ASSET_SYMBOL_DELIMITER "." diff --git a/src/lib/bitcoin/chain/attachment/asset/asset_transfer.cpp b/src/lib/bitcoin/chain/attachment/asset/asset_transfer.cpp index f7479c261..1f5d73b21 100644 --- a/src/lib/bitcoin/chain/attachment/asset/asset_transfer.cpp +++ b/src/lib/bitcoin/chain/attachment/asset/asset_transfer.cpp @@ -26,7 +26,6 @@ #include #include #include -#include namespace libbitcoin { namespace chain { diff --git a/src/lib/bitcoin/chain/attachment/asset/attenuation_model.cpp b/src/lib/bitcoin/chain/attachment/asset/attenuation_model.cpp index 453c7da46..c46e3f9c3 100644 --- a/src/lib/bitcoin/chain/attachment/asset/attenuation_model.cpp +++ b/src/lib/bitcoin/chain/attachment/asset/attenuation_model.cpp @@ -873,6 +873,7 @@ code attenuation_model::check_model_param(const blockchain::validate_transaction { const transaction& tx = validate_tx.get_tx(); const blockchain::block_chain_impl& chain = validate_tx.get_blockchain(); + const blockchain::validate_block* validate_block = validate_tx.get_validate_block(); if (tx.version < transaction_version::check_nova_feature) { return error::success; @@ -954,7 +955,7 @@ code attenuation_model::check_model_param(const blockchain::validate_transaction return error::attenuation_model_param_error; } - auto curr_diff_height = current_blockheight - iter->prev_blockheight_; + auto curr_diff_height = chain.calc_number_of_blocks(iter->prev_blockheight_, current_blockheight, validate_block); auto real_diff_height = get_diff_height(prev_model_param, model_param); if (real_diff_height > curr_diff_height) { @@ -990,7 +991,7 @@ code attenuation_model::check_model_param(const blockchain::validate_transaction // check the left is all spendable for (const auto& ext_input : vec_prev_input) { const auto& prev_model_param = ext_input.prev_output_.get_attenuation_model_param(); - auto curr_diff_height = current_blockheight - ext_input.prev_blockheight_; + auto curr_diff_height = chain.calc_number_of_blocks(ext_input.prev_blockheight_, current_blockheight, validate_block); auto real_diff_height = get_diff_height(prev_model_param, data_chunk()); if (real_diff_height > curr_diff_height) { log::debug(LOG_HEADER) << "check diff height failed for all spendable, " diff --git a/src/lib/bitcoin/chain/attachment/attachment.cpp b/src/lib/bitcoin/chain/attachment/attachment.cpp index 2a6c68f1b..d8e733f43 100644 --- a/src/lib/bitcoin/chain/attachment/attachment.cpp +++ b/src/lib/bitcoin/chain/attachment/attachment.cpp @@ -34,6 +34,12 @@ attachment::attachment() reset(); } +attachment::attachment(uint32_t type) +{ + reset(); + this->type = type; +} + attachment::attachment(const std::string& from_did, const std::string& to_did) : version(DID_ATTACH_VERIFY_VERSION) , type(0) //attachment_type::attach_none; @@ -44,6 +50,44 @@ attachment::attachment(const std::string& from_did, const std::string& to_did) boost::apply_visitor(visitor, attach); } +attachment::attachment(attachment&& other) + : version(other.version) + , type(other.type) + , todid(std::move(other.todid)) + , fromdid(std::move(other.fromdid)) + , attach(std::move(other.attach)) +{ +} + +attachment::attachment(const attachment& other) + : version(other.version) + , type(other.type) + , todid(other.todid) + , fromdid(other.fromdid) + , attach(other.attach) +{ +} + +attachment& attachment::operator=(attachment&& other) +{ + version = other.version; + type = other.type; + todid = std::move(other.todid); + fromdid = std::move(other.fromdid); + attach = std::move(other.attach); + return *this; +} + +attachment& attachment::operator=(const attachment& other) +{ + version = other.version; + type = other.type; + todid = other.todid; + fromdid = other.fromdid; + attach = other.attach; + return *this; +} + void attachment::reset() { version = 0; @@ -133,8 +177,8 @@ bool attachment::from_data_t(reader& source) result = boost::apply_visitor(visitor, attach); } else { - result = false; - reset(); + // result = false; + // reset(); } return result; @@ -202,6 +246,12 @@ void attachment::set_type(uint32_t type) this->type = type; } +void attachment::set_null() +{ + reset(); + this->type = ATTACH_NULL_TYPE; +} + std::string attachment::get_to_did() const { return todid; diff --git a/src/lib/bitcoin/chain/attachment/did/did_detail.cpp b/src/lib/bitcoin/chain/attachment/did/did_detail.cpp index 9d1d67801..bcf83a5ba 100644 --- a/src/lib/bitcoin/chain/attachment/did/did_detail.cpp +++ b/src/lib/bitcoin/chain/attachment/did/did_detail.cpp @@ -25,7 +25,6 @@ #include #include #include -#include namespace libbitcoin { namespace chain { @@ -105,14 +104,6 @@ std::string did_detail::to_string() const return ss.str(); } -void did_detail::to_json(std::ostream& output) -{ - minijson::object_writer json_writer(output); - json_writer.write("symbol", symbol); - json_writer.write("address", address); - json_writer.close(); -} - const std::string& did_detail::get_symbol() const { return symbol; diff --git a/src/lib/bitcoin/chain/block.cpp b/src/lib/bitcoin/chain/block.cpp index d1cf68742..664b53027 100644 --- a/src/lib/bitcoin/chain/block.cpp +++ b/src/lib/bitcoin/chain/block.cpp @@ -19,6 +19,7 @@ * along with this program. If not, see . */ #include +#include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include namespace libbitcoin { namespace chain { @@ -40,25 +42,28 @@ block::block() } block::block(const block& other) - : block(other.header, other.transactions) + : block(other.header, other.transactions, other.blocksig) { } block::block(const chain::header& header, - const chain::transaction::list& transactions) - : header(header), transactions(transactions) + const chain::transaction::list& transactions, + const ec_signature& blocksig) + : header(header), transactions(transactions), blocksig(blocksig) { } block::block(block&& other) : block(std::forward(other.header), - std::forward(other.transactions)) + std::forward(other.transactions), + std::forward(other.blocksig)) { } -block::block(chain::header&& header, chain::transaction::list&& transactions) +block::block(chain::header&& header, chain::transaction::list&& transactions, ec_signature&& blocksig) : header(std::forward(header)), - transactions(std::forward(transactions)) + transactions(std::forward(transactions)), + blocksig(std::forward(blocksig)) { } @@ -66,6 +71,7 @@ block& block::operator=(block&& other) { header = std::move(other.header); transactions = std::move(other.transactions); + blocksig = std::move(other.blocksig); return *this; } @@ -79,6 +85,22 @@ void block::reset() header.reset(); transactions.clear(); transactions.shrink_to_fit(); + blocksig.fill(0); +} + +bool block::is_proof_of_stake() const +{ + return header.is_proof_of_stake(); +} + +bool block::is_proof_of_work() const +{ + return header.is_proof_of_work(); +} + +bool block::is_proof_of_dpos() const +{ + return header.is_proof_of_dpos(); } bool block::from_data_t(reader& source, bool with_transaction_count) @@ -103,6 +125,9 @@ bool block::from_data_t(reader& source, bool with_transaction_count) if (!result) reset(); + if (header.is_proof_of_stake()) { + source.read_data(blocksig.data(), blocksig.size()); + } return result; } @@ -113,6 +138,10 @@ void block::to_data_t(writer& sink, bool with_transaction_count) const for (const auto& tx: transactions) tx.to_data(sink); + + if (header.is_proof_of_stake()){ + sink.write_data(blocksig.data(), blocksig.size()); + } } uint64_t block::serialized_size(bool with_transaction_count) const @@ -122,6 +151,9 @@ uint64_t block::serialized_size(bool with_transaction_count) const for (const auto& tx: transactions) block_size += tx.serialized_size(); + if (header.is_proof_of_stake()) + block_size += blocksig.size(); + return block_size; } @@ -251,5 +283,33 @@ chain::block block::genesis_testnet() return genesis; } +bool block::must_use_pow_consensus() const +{ + if (!consensus::witness::is_witness_enabled(header.number)) { + return true; + } + // ensure the vote is security, + // first vote_maturity blocks of each epoch must use pow + if (consensus::witness::is_between_vote_maturity_interval(header.number)) { + return true; + } + if (header.number % consensus::witness::pow_check_point_height == 0) { + return true; + } + return false; +} + +bool block::can_use_dpos_consensus() const +{ + if (must_use_pow_consensus()) { + return false; + } + // only use DPOS to pack real txs, forbid block with only coinbase tx + if (transactions.size() == 1) { + return false; + } + return true; +} + } // namspace chain } // namspace libbitcoin diff --git a/src/lib/bitcoin/chain/business_data.cpp b/src/lib/bitcoin/chain/business_data.cpp index a77fffb42..5166087c8 100644 --- a/src/lib/bitcoin/chain/business_data.cpp +++ b/src/lib/bitcoin/chain/business_data.cpp @@ -126,8 +126,8 @@ bool business_data::from_data_t(reader& source) } else { - result = false; - reset(); + // result = false; + // reset(); } return result; diff --git a/src/lib/bitcoin/chain/header.cpp b/src/lib/bitcoin/chain/header.cpp index 48151cf36..4c6d3972b 100644 --- a/src/lib/bitcoin/chain/header.cpp +++ b/src/lib/bitcoin/chain/header.cpp @@ -126,6 +126,21 @@ bool header::is_valid() const (nonce != 0); } +bool header::is_proof_of_stake() const +{ + return version == block_version_pos; +} + +bool header::is_proof_of_work() const +{ + return version == block_version_pow; +} + +bool header::is_proof_of_dpos() const +{ + return version == block_version_dpos; +} + void header::reset() { version = 0; @@ -239,5 +254,31 @@ bool operator!=(const header& left, const header& right) return !(left == right); } +std::string get_block_version(const header& header) +{ + return get_block_version(header.version); +} + +std::string get_block_version(block_version version) +{ + return get_block_version((uint32_t)version); +} + +std::string get_block_version(uint32_t version) +{ + switch (version) { + case block_version_any: + return " Any"; + case block_version_pow: + return " PoW"; + case block_version_pos: + return " PoS"; + case block_version_dpos: + return "DPoS"; + default:; + } + return "Unknown"; +} + } // namspace chain } // namspace libbitcoin diff --git a/src/lib/bitcoin/chain/input.cpp b/src/lib/bitcoin/chain/input.cpp index 55e917c35..49da2681f 100644 --- a/src/lib/bitcoin/chain/input.cpp +++ b/src/lib/bitcoin/chain/input.cpp @@ -132,6 +132,8 @@ uint64_t input::serialized_size() const std::string input::to_string(uint32_t flags) const { + flags = chain::get_script_context(); + std::ostringstream ss; ss << previous_output.to_string() << "\n" @@ -146,27 +148,6 @@ bool input::is_final() const return (sequence == max_input_sequence); } -bool input::is_locked(size_t block_height, uint32_t median_time_past) const -{ - if ((sequence & relative_locktime_disabled) != 0) - return false; - - // bip68: a minimum block-height constraint over the input's age. - const auto minimum = (sequence & relative_locktime_mask); - const auto& prevout = previous_output.metadata; - - if ((sequence & relative_locktime_time_locked) != 0) { - // Median time past must be monotonically-increasing by block. - BITCOIN_ASSERT(median_time_past >= prevout.median_time_past); - const auto age_seconds = median_time_past - prevout.median_time_past; - return age_seconds < (minimum << relative_locktime_seconds_shift); - } - - BITCOIN_ASSERT(block_height >= prevout.height); - const auto age_blocks = block_height - prevout.height; - return age_blocks < minimum; -} - std::string input::get_script_address() const { auto payment_address = wallet::payment_address::extract(script); diff --git a/src/lib/bitcoin/chain/output.cpp b/src/lib/bitcoin/chain/output.cpp index 1f1e48e44..dfd9737e8 100644 --- a/src/lib/bitcoin/chain/output.cpp +++ b/src/lib/bitcoin/chain/output.cpp @@ -34,10 +34,13 @@ namespace libbitcoin { namespace chain { -output::output(){} +output::output() +{ + reset(); +} output::output(output&& other) -: output(std::move(other.value), std::move(other.script),std::move(other.attach_data)) +: output(other.value, std::move(other.script), std::move(other.attach_data)) { } output::output(const output& other) @@ -153,6 +156,11 @@ bool output::is_valid() const || attach_data.is_valid(); // added for asset issue/transfer } +bool output::is_null() const +{ + return !is_valid(); +} + std::string output::get_script_address() const { auto payment_address = wallet::payment_address::extract(script); @@ -238,6 +246,8 @@ uint64_t output::serialized_size() const std::string output::to_string(uint32_t flags) const { + flags = chain::get_script_context(); + std::ostringstream ss; ss << "\tvalue = " << value << "\n" @@ -583,5 +593,13 @@ const data_chunk& output::get_attenuation_model_param() const return operation::get_model_param_from_pay_key_hash_with_attenuation_model(script.operations); } +uint32_t output::get_lock_sequence() const +{ + if (operation::is_pay_key_hash_with_sequence_lock_pattern(script.operations)) { + return operation::get_lock_sequence_from_pay_key_hash_with_sequence_lock(script.operations); + } + return max_input_sequence; +} + } // namspace chain } // namspace libbitcoin diff --git a/src/lib/bitcoin/chain/output_point.cpp b/src/lib/bitcoin/chain/output_point.cpp index 3362ee097..7cd483a1d 100644 --- a/src/lib/bitcoin/chain/output_point.cpp +++ b/src/lib/bitcoin/chain/output_point.cpp @@ -32,32 +32,32 @@ namespace chain { //----------------------------------------------------------------------------- output_point::output_point() - : point{}, metadata{} + : point{} { } output_point::output_point(point&& value) - : point(std::move(value)), metadata{} + : point(std::move(value)) { } output_point::output_point(const point& value) - : point(value), metadata{} + : point(value) { } output_point::output_point(const output_point& other) - : point(other), metadata(other.metadata) + : point(other) { } output_point::output_point(output_point&& other) - : point(std::move(other)), metadata(std::move(other.metadata)) + : point(std::move(other)) { } output_point::output_point(hash_digest&& hash, uint32_t index) - : point({ std::move(hash), index }), metadata{} + : point({ std::move(hash), index }) { } @@ -86,14 +86,12 @@ output_point& output_point::operator=(const point& other) output_point& output_point::operator=(output_point&& other) { point::operator=(std::move(other)); - metadata = std::move(other.metadata); return *this; } output_point& output_point::operator=(const output_point& other) { point::operator=(other); - metadata = other.metadata; return *this; } @@ -141,19 +139,5 @@ output_point output_point::factory(reader& source) return instance; } -// Validation. -//----------------------------------------------------------------------------- - -// For tx pool validation height is that of the candidate block. -bool output_point::is_mature(size_t height) const -{ - // Coinbase (null) inputs and those with non-coinbase prevouts are mature. - if (!metadata.coinbase || is_null()) - return true; - - // The (non-coinbase) input refers to a coinbase output, so validate depth. - return floor_subtract(height, metadata.height) >= coinbase_maturity; -} - } // namespace chain } // namespace libbitcoin diff --git a/src/lib/bitcoin/chain/script/opcode.cpp b/src/lib/bitcoin/chain/script/opcode.cpp index dd3794f7e..447ed66bc 100644 --- a/src/lib/bitcoin/chain/script/opcode.cpp +++ b/src/lib/bitcoin/chain/script/opcode.cpp @@ -20,9 +20,9 @@ */ #include #include - -#include +#include #include +#include namespace libbitcoin { namespace chain { @@ -518,5 +518,17 @@ opcode data_to_opcode(const data_chunk& value) return code; } +script_context get_script_context() +{ +#ifdef ENABLE_LOCKTIME + return script_context::all_enabled; +#else + uint32_t context = script_context::all_enabled; + context &= ~bip65_enabled; // disable nLocktime + context &= ~bip112_enabled; // disable nSequence + return static_cast(context); +#endif +} + } // namspace chain } // namspace libbitcoin diff --git a/src/lib/bitcoin/chain/script/operation.cpp b/src/lib/bitcoin/chain/script/operation.cpp index 274233da5..2bb59f243 100644 --- a/src/lib/bitcoin/chain/script/operation.cpp +++ b/src/lib/bitcoin/chain/script/operation.cpp @@ -37,6 +37,15 @@ namespace chain { const size_t operation::max_null_data_size = 80; +operation operation::from_raw_data(const data_chunk& data) +{ + BITCOIN_ASSERT_MSG(data.size() > 0, "operation::from_raw_data must take non-empty raw data."); + operation instance; + instance.data = data; + instance.code = data_to_opcode(data); + return instance; +} + operation operation::factory_from_data(const data_chunk& data) { operation instance; @@ -190,6 +199,8 @@ uint64_t operation::serialized_size() const std::string operation::to_string(uint32_t flags) const { + flags = chain::get_script_context(); + std::ostringstream ss; if (data.empty()) @@ -373,6 +384,21 @@ bool operation::is_pay_key_hash_with_attenuation_model_pattern(const operation:: && ops[7].code == opcode::checksig; } +bool operation::is_pay_key_hash_with_sequence_lock_pattern(const operation::stack& ops) +{ + return ops.size() == 8 + && ops[0].code == opcode::special + && ops[1].code == opcode::checksequenceverify + && ops[2].code == opcode::drop + && ops[3].code == opcode::dup + && ops[4].code == opcode::hash160 + && ops[5].code == opcode::special + && ops[5].data.size() == short_hash_size + && ops[6].code == opcode::equalverify + && ops[7].code == opcode::checksig; +} + + bool operation::is_sign_multisig_pattern(const operation::stack& ops) { if (ops.size() < 2 || !is_push_only(ops)) @@ -381,7 +407,12 @@ bool operation::is_sign_multisig_pattern(const operation::stack& ops) if (ops.front().code != opcode::zero) return false; - return true; + const auto found = [](const operation& op) + { + return op.code == opcode::special; + }; + + return std::all_of(ops.begin()+1, ops.end()-1, found); } bool operation::is_sign_public_key_pattern(const operation::stack& ops) @@ -447,6 +478,12 @@ const data_chunk& operation::get_input_point_from_pay_key_hash_with_attenuation_ return ops[1].data; } +uint32_t operation::get_lock_sequence_from_pay_key_hash_with_sequence_lock(const operation::stack& ops) +{ + CScriptNum num(ops[0].data, 1); + return static_cast(num.getint64()); +} + // pattern templates // ---------------------------------------------------------------------------- @@ -580,5 +617,26 @@ operation::stack operation::to_pay_key_hash_with_attenuation_model_pattern( }; } +operation::stack operation::to_pay_key_hash_with_sequence_lock_pattern(const short_hash& hash, uint32_t sequence_lock) +{ + return operation::stack + { + { opcode::special, CScriptNum::serialize(sequence_lock) }, + { opcode::checksequenceverify, {} }, + { opcode::drop, {} }, + { opcode::dup, {} }, + { opcode::hash160, {} }, + { opcode::special, to_chunk(hash) }, + { opcode::equalverify, {} }, + { opcode::checksig, {} } + }; +} + + +bool operation::operator==(const operation& other) const +{ + return code == other.code && data == other.data; +} + } // namspace chain } // namspace libbitcoin diff --git a/src/lib/bitcoin/chain/script/script.cpp b/src/lib/bitcoin/chain/script/script.cpp index 86f869174..a867d9cba 100644 --- a/src/lib/bitcoin/chain/script/script.cpp +++ b/src/lib/bitcoin/chain/script/script.cpp @@ -120,6 +120,9 @@ script_pattern script::pattern() const if (operation::is_pay_key_hash_with_attenuation_model_pattern(operations)) return script_pattern::pay_key_hash_with_attenuation_model; + if (operation::is_pay_key_hash_with_sequence_lock_pattern(operations)) + return script_pattern::pay_key_hash_with_sequence_lock; + return script_pattern::non_standard; } @@ -308,6 +311,8 @@ bool script::from_string(const std::string& human_readable) std::string script::to_string(uint32_t flags) const { + flags = chain::get_script_context(); + std::ostringstream value; for (auto it = operations.begin(); it != operations.end(); ++it) @@ -1466,7 +1471,7 @@ bool op_checksequenceverify(evaluation_context& context, const script& script, // To provide for future soft-fork extensibility, if the // operand has the disabled lock-time flag set, // CHECKSEQUENCEVERIFY behaves as a NOP. - if ((number.int64() & (1 << 31)/*CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG*/) != 0) + if ((number.int64() & relative_locktime_disabled) != 0) return false; return true; diff --git a/src/lib/bitcoin/chain/transaction.cpp b/src/lib/bitcoin/chain/transaction.cpp index a7dc7e3ed..7833b1119 100644 --- a/src/lib/bitcoin/chain/transaction.cpp +++ b/src/lib/bitcoin/chain/transaction.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ #include #include #include +#include namespace libbitcoin { namespace chain { @@ -169,13 +171,22 @@ bool transaction::from_data_t(reader& source) return result; } -void transaction::to_data_t(writer& sink) const +void transaction::to_data_t(writer& sink, bool for_merkle) const { sink.write_4_bytes_little_endian(version); sink.write_variable_uint_little_endian(inputs.size()); - for (const auto& input: inputs) + if (for_merkle && consensus::witness::is_dpos_enabled() && is_coinbase()) { + auto input = inputs[0]; + operation::stack ops; + ops.swap(input.script.operations); + input.script.operations.emplace_back(operation::factory_from_data(ops.front().to_data())); input.to_data(sink); + } + else { + for (const auto& input: inputs) + input.to_data(sink); + } sink.write_variable_uint_little_endian(outputs.size()); @@ -201,6 +212,8 @@ uint64_t transaction::serialized_size() const std::string transaction::to_string(uint32_t flags) const { + flags = chain::get_script_context(); + std::ostringstream value; value << "Transaction:\n" << "\tversion = " << version << "\n" @@ -228,7 +241,7 @@ hash_digest transaction::hash() const { //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mutex_.unlock_upgrade_and_lock(); - hash_.reset(new hash_digest(bitcoin_hash(to_data()))); + hash_.reset(new hash_digest(bitcoin_hash(to_data(true)))); mutex_.unlock_and_lock_upgrade(); //--------------------------------------------------------------------- } @@ -252,39 +265,50 @@ bool transaction::is_coinbase() const return (inputs.size() == 1) && inputs[0].previous_output.is_null(); } -bool transaction::is_final(uint64_t block_height, uint32_t block_time) const +bool transaction::is_pos_genesis_tx(bool is_testnet) const { - if (locktime == 0) - return true; - - auto max_locktime = block_time; + if (!is_coinbase() || outputs.size() != 1) { + return false; + } - if (locktime < locktime_threshold) - max_locktime = static_cast(block_height); + chain::script script; + wallet::payment_address pay_address(get_foundation_address(is_testnet)); + script.operations = chain::operation::to_pay_key_hash_pattern(short_hash(pay_address)); - if (locktime < max_locktime) - return true; + const auto & out = outputs[0]; + return out.is_etp() && out.value == pos_genesis_reward && + out.script.operations == script.operations; +} - for (const auto& tx_input: inputs) - if (!tx_input.is_final()) - return false; +bool transaction::is_coinstake() const +{ + return (inputs.size() > 0) + && (!inputs[0].previous_output.is_null()) + && (outputs.size() >= 2) + && (outputs[0].is_null()) //the coin stake transaction is marked with the first output empty + && (inputs[0].get_script_address() == outputs[1].get_script_address()); - return true; } -bool transaction::is_locked(size_t block_height, - uint32_t median_time_past) const +bool transaction::all_inputs_final() const { - if (version < relative_locktime_min_version || is_coinbase()) - return false; + const auto finalized = [](const input& input) + { + return input.is_final(); + }; + + return std::all_of(inputs.begin(), inputs.end(), finalized); +} - const auto locked = [block_height, median_time_past](const input& input) +bool transaction::is_final(uint64_t block_height, uint32_t block_time) const +{ + const auto max_locktime = [=]() { - return input.is_locked(block_height, median_time_past); + return locktime < locktime_threshold ? + safe_unsigned(block_height) : block_time; }; - // If any input is relative time locked the transaction is as well. - return std::any_of(inputs.begin(), inputs.end(), locked); + return locktime == 0 || locktime < max_locktime() || all_inputs_final(); } bool transaction::is_locktime_conflict() const @@ -388,31 +412,6 @@ bool transaction::has_did_transfer() const return false; } -std::string transaction::get_did_transfer_old_address() const -{ - std::string newdidstr = ""; - for (auto& elem: outputs) { - if(elem.is_did_transfer()) { - newdidstr = elem.get_script_address(); - } - - } - - if (newdidstr.empty()){ - return newdidstr; - } - - for (auto& elem: inputs) { - if(elem.get_script_address()!=newdidstr) { - newdidstr = elem.get_script_address(); - return newdidstr; - } - - } - - - return newdidstr; -} } // namspace chain } // namspace libbitcoin diff --git a/src/lib/bitcoin/constants.cpp b/src/lib/bitcoin/constants.cpp index 6d8b26207..34ee14c4a 100644 --- a/src/lib/bitcoin/constants.cpp +++ b/src/lib/bitcoin/constants.cpp @@ -19,9 +19,49 @@ * along with this program. If not, see . */ #include +#include namespace libbitcoin { + +const uint64_t pos_genesis_reward = 50000000 * 100000000ul; + +#ifndef PRIVATE_CHAIN + uint32_t coinbase_maturity = 1000; +const uint64_t future_blocktime_fork_height = 1030000; + +const size_t relative_locktime_min_version = 5; + +// PoS +const uint64_t pos_enabled_height = max_uint64; +const uint32_t pos_coinstake_max_utxos = 10; +const uint64_t pos_lock_min_value = 10000 * 100000000ul; +const uint64_t pos_lock_min_height = 100000; +const uint64_t pos_lock_gap_height = 10000; +const uint64_t pos_stake_min_value = 10000 * 100000000ul; +const uint64_t pos_stake_min_height = 1000; +const double pos_stake_factor = 1; +const uint32_t block_timespan_window = 28; + +#else //PRIVATE_CHAIN + +uint32_t coinbase_maturity = 10; +const uint64_t future_blocktime_fork_height = 10; + +const size_t relative_locktime_min_version = 2; + +// PoS +const uint64_t pos_enabled_height = 350; +const uint32_t pos_coinstake_max_utxos = 10; +const uint64_t pos_lock_min_value = 10 * 100000000ul; +const uint64_t pos_lock_min_height = 100000; +const uint64_t pos_lock_gap_height = 10000; +const uint64_t pos_stake_min_value = 100 * 100000000ul; +const uint64_t pos_stake_min_height = 100; +const double pos_stake_factor = 10; +const uint32_t block_timespan_window = 28; + +#endif //PRIVATE_CHAIN hash_number max_target() { @@ -39,4 +79,14 @@ std::string get_developer_community_address(bool is_testnet) return address; } +std::string get_foundation_address(bool is_testnet) +{ + std::string address("MSCHL3unfVqzsZbRVCJ3yVp7RgAmXiuGN3"); // foundation address for mainnet + if (is_testnet) { + address = "tF9pfqY8p6cfjuhDVZu9aXBY1CBprgrpKm"; // foundation address for testnet + } + return address; +} + + } // namespace libbitcoin diff --git a/src/lib/bitcoin/error.cpp b/src/lib/bitcoin/error.cpp index de0f70a22..f41e0ecb4 100644 --- a/src/lib/bitcoin/error.cpp +++ b/src/lib/bitcoin/error.cpp @@ -264,6 +264,34 @@ std::string error_category_impl::message(int ev) const BC_NOEXCEPT case error::sequence_locked: return "transaction currently locked"; + case error::sync_disabled: + return "block sync is disabled"; + case error::block_version_not_match: + return "block version not match"; + case error::witness_sign_invalid: + return "witness sign is invalid"; + case error::witness_mismatch: + return "witness mismatch"; + case error::witness_vote_error: + return "witness vote error"; + case error::witness_update_error: + return "witness update error"; + + case error::proof_of_stake: + return "proof of stake failed"; + case error::illegal_coinstake: + return "illegal coinstake"; + case error::miss_coinstake: + return "miss coinstake"; + case error::extra_coinstakes: + return "more than one coinstake"; + case error::coinstake_version_invalid: + return "coinstake version invalid"; + case error::cointstake_signature_invalid: + return "validate coinstake block signature failed"; + case error::check_pos_genesis_error: + return "check pos genesis block failed"; + // unknown errors case error::unknown: default: diff --git a/src/lib/bitcoin/message/block_message.cpp b/src/lib/bitcoin/message/block_message.cpp index f6b1b1367..55762072f 100644 --- a/src/lib/bitcoin/message/block_message.cpp +++ b/src/lib/bitcoin/message/block_message.cpp @@ -105,6 +105,7 @@ block_message& block_message::operator=(block_message&& other) { header = std::move(other.header); transactions = std::move(other.transactions); + blocksig = std::move(other.blocksig); originator_ = other.originator_; return *this; } diff --git a/src/lib/bitcoin/message/network_address.cpp b/src/lib/bitcoin/message/network_address.cpp index 7802299fb..d9367e5c3 100644 --- a/src/lib/bitcoin/message/network_address.cpp +++ b/src/lib/bitcoin/message/network_address.cpp @@ -19,6 +19,7 @@ * along with this program. If not, see . */ #include +#include #include #include @@ -295,6 +296,9 @@ bool network_address::is_local() const bool network_address::is_routable() const { +#ifdef PRIVATE_CHAIN + return is_valid(); +#endif return is_valid() && !(is_RFC1918() || is_RFC3927() || is_RFC4862() || (is_RFC4193() && !is_tor()) || is_RFC4843() || is_local()); } diff --git a/src/lib/bitcoin/utility/random.cpp b/src/lib/bitcoin/utility/random.cpp index 4f91e5af6..a120f26f1 100644 --- a/src/lib/bitcoin/utility/random.cpp +++ b/src/lib/bitcoin/utility/random.cpp @@ -22,81 +22,111 @@ #include #include -#include #include +#include #include -#include #include +#include namespace libbitcoin { + +using namespace bc::asio; +using namespace std::chrono; + // DO NOT USE srand() and rand() on MSVC as srand must be called per thread. -// As a result it is difficult to use safely. +// Values may be truly random depending on the underlying device. -// Not fully testable due to lack of random engine injection. -// This may be truly random depending on the underlying device. uint64_t pseudo_random() { - std::random_device device; - std::uniform_int_distribution distribution; - return distribution(device); + return pseudo_random::next(); } -// Not fully testable due to lack of random engine injection. -// This may be truly random depending on the underlying device. uint64_t nonzero_pseudo_random() { - for (auto index = 0; index < 100; ++index) - { - const auto value = pseudo_random(); - if (value > 0) - return value; - } + return pseudo_random::next(1, max_uint64); +} - // If above doesn't return something is seriously wrong with the RNG. - throw std::runtime_error("The RNG produces 100 consecutive zero values."); +uint64_t pseudo_random(uint64_t begin, uint64_t end) +{ + return pseudo_random::next(begin, end); } -// Not fully testable due to lack of random engine injection. -// This may be truly random depending on the underlying device. -void pseudo_random_fill(data_chunk& chunk) +void pseudo_random_fill(data_chunk& out) +{ + return pseudo_random::fill(out); +} + +static uint32_t get_clock_seed() +{ + const auto now = high_resolution_clock::now(); + return static_cast(now.time_since_epoch().count()); +} + +std::mt19937& pseudo_random::get_twister() { - std::random_device device; - std::uniform_int_distribution distribution; - for (uint8_t& byte: chunk) + // Boost.thread will clean up the thread statics using this function. + const auto deleter = [](std::mt19937* twister) + { + delete twister; + }; + + // Maintain thread static state space. + static boost::thread_specific_ptr twister(deleter); + + // This is thread safe because the instance is static. + if (twister.get() == nullptr) { - // uniform_int_distribution is undefined for sizes < 16 bits, - // so we generate a 16 bit value and reduce it to 8 bits. - byte = distribution(device) % std::numeric_limits::max(); + // Seed with high resolution clock. + twister.reset(new std::mt19937(get_clock_seed())); } + + return *twister; +} + +uint64_t pseudo_random::next() +{ + return next(0, max_uint64); +} + +uint64_t pseudo_random::next(uint64_t begin, uint64_t end) +{ + std::uniform_int_distribution distribution(begin, end); + return distribution(get_twister()); +} + +asio::duration pseudo_randomize(const asio::duration& expiration, + uint8_t ratio) +{ + return pseudo_random::duration(expiration, ratio); } // Randomly select a time duration in the range: // [(expiration - expiration / ratio) .. expiration] // Not fully testable due to lack of random engine injection. -asio::duration pseudo_randomize(const asio::duration& expiration, uint8_t ratio) +asio::duration pseudo_random::duration(const asio::duration& expiration, + uint8_t ratio) { if (ratio == 0) return expiration; // Uses milliseconds level resolution. - const auto max_expire = std::chrono::duration_cast( - expiration).count(); + const auto max_expire = duration_cast(expiration).count(); // [10 secs, 4] => 10000 / 4 => 2500 - const auto divisor = max_expire / ratio; + const auto limit = max_expire / ratio; - if (divisor == 0) + if (limit == 0) return expiration; // [0..2^64) % 2500 => [0..2500] - const auto random_offset = static_cast(bc::pseudo_random() % divisor); + const auto random_offset = static_cast(pseudo_random::next(0, limit)); // (10000 - [0..2500]) => [7500..10000] const auto expires = max_expire - random_offset; // [7.5..10] second duration. - return asio::milliseconds(expires); + return milliseconds(expires); } } // namespace libbitcoin diff --git a/src/lib/bitcoin/utility/variable_uint_size.cpp b/src/lib/bitcoin/utility/variable_uint_size.cpp index 6b481397b..00655f3a5 100644 --- a/src/lib/bitcoin/utility/variable_uint_size.cpp +++ b/src/lib/bitcoin/utility/variable_uint_size.cpp @@ -41,7 +41,14 @@ size_t variable_string_size(const std::string& str) return length; } -std::string limit_size_string(const std::string& str, size_t limit_size) +size_t variable_data_chunk_size(const data_chunk& data) +{ + const size_t length = data.size(); + return length + variable_uint_size(length); +} + + + std::string limit_size_string(const std::string& str, size_t limit_size) { if (str.size() > limit_size) { return str.substr(0, limit_size); diff --git a/src/lib/bitcoin/wallet/payment_address.cpp b/src/lib/bitcoin/wallet/payment_address.cpp index 7298b8f38..62b0397aa 100644 --- a/src/lib/bitcoin/wallet/payment_address.cpp +++ b/src/lib/bitcoin/wallet/payment_address.cpp @@ -270,6 +270,14 @@ payment_address payment_address::extract(const chain::script& script, case chain::script_pattern::pay_blackhole_address: BITCOIN_ASSERT(ops.size() == 1); break; + case chain::script_pattern::pay_key_hash_with_attenuation_model: + BITCOIN_ASSERT(ops.size() == 8); + BITCOIN_ASSERT(ops[5].data.size() == short_hash_size); + break; + case chain::script_pattern::pay_key_hash_with_sequence_lock: + BITCOIN_ASSERT(ops.size() == 8); + BITCOIN_ASSERT(ops[5].data.size() == short_hash_size); + break; // sign // -------------------------------------------------------------------- @@ -292,10 +300,6 @@ payment_address payment_address::extract(const chain::script& script, case chain::script_pattern::sign_script_hash: BITCOIN_ASSERT(ops.size() > 1); break; - case chain::script_pattern::pay_key_hash_with_attenuation_model: - BITCOIN_ASSERT(ops.size() == 8); - BITCOIN_ASSERT(ops[5].data.size() == short_hash_size); - break; case chain::script_pattern::non_standard: default:; } @@ -341,6 +345,10 @@ payment_address payment_address::extract(const chain::script& script, hash = to_array(ops[5].data); return payment_address(hash, p2kh_version); + case chain::script_pattern::pay_key_hash_with_sequence_lock: + hash = to_array(ops[5].data); + return payment_address(hash, p2kh_version); + // sign // -------------------------------------------------------------------- diff --git a/src/lib/bitcoin/wallet/select_outputs.cpp b/src/lib/bitcoin/wallet/select_outputs.cpp index a26dda03f..29c5cb6a4 100644 --- a/src/lib/bitcoin/wallet/select_outputs.cpp +++ b/src/lib/bitcoin/wallet/select_outputs.cpp @@ -30,7 +30,7 @@ namespace wallet { using namespace bc::chain; -void select_outputs::select(points_info& out, output_info::list unspent, +void select_outputs::select(points_info& out, output_point_info::list unspent, uint64_t minimum_value, algorithm DEBUG_ONLY(option)) { out.change = 0; @@ -39,17 +39,17 @@ void select_outputs::select(points_info& out, output_info::list unspent, if (unspent.empty()) return; - const auto below_minimum = [minimum_value](const output_info& out_info) + const auto below_minimum = [minimum_value](const output_point_info& out_info) { return out_info.value < minimum_value; }; - const auto lesser = [](const output_info& left, const output_info& right) + const auto lesser = [](const output_point_info& left, const output_point_info& right) { return left.value < right.value; }; - const auto greater = [](const output_info& left, const output_info& right) + const auto greater = [](const output_point_info& left, const output_point_info& right) { return left.value > right.value; }; diff --git a/src/lib/bitcoin/wallet/vrf_private.cpp b/src/lib/bitcoin/wallet/vrf_private.cpp new file mode 100644 index 000000000..9c9cf4cda --- /dev/null +++ b/src/lib/bitcoin/wallet/vrf_private.cpp @@ -0,0 +1,149 @@ +/** + * Copyright (c) 2016-2018 metaverse core developers (see MVS-AUTHORS) + * + * This file is part of metaverse. + * + * metaverse is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include + +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace wallet { + + +vrf_private::vrf_private() +{ + vrf_public pk; + sodium::crypto_vrf_keypair(pk.data(), secret_.data()); +} + +vrf_private::vrf_private(const vrf_private& other) +: secret_(other.secret_) +{ +} + +vrf_private::vrf_private(const data_chunk& data) +{ + auto seed = sha256_hash(data); + vrf_public pk; + sodium::crypto_vrf_keypair_from_seed(pk.data(), secret_.data(), seed.data()); +} + +// This reads the private version and sets the public to mainnet. +vrf_private::vrf_private(const vrf_seed& seed) +{ + vrf_public pk; + sodium::crypto_vrf_keypair_from_seed(pk.data(), secret_.data(), seed.data()); +} + +// Cast operators. +// ---------------------------------------------------------------------------- + +vrf_private::operator const vrf_secret&() const +{ + return secret_; +} + +// Serializer. +// ---------------------------------------------------------------------------- + +/// Accessors. +// ---------------------------------------------------------------------------- + +const vrf_secret& vrf_private::secret() const +{ + return secret_; +} + +// Methods. +// ---------------------------------------------------------------------------- + + +vrf_public vrf_private::to_public() const +{ + vrf_public pk; + sodium::crypto_vrf_sk_to_pk(pk.data(), secret_.data()); + return pk; +} + +std::string vrf_private::encoded() const +{ + return encode_base16(secret_); +} +// Operators. +// ---------------------------------------------------------------------------- + +vrf_private& vrf_private::operator=(const vrf_private& other) +{ + secret_ = other.secret_; + return *this; +} + +bool vrf_private::operator<(const vrf_private& other) const +{ + return encoded() < other.encoded(); +} + +bool vrf_private::operator==(const vrf_private& other) const +{ + return secret_ == other.secret_; +} + +bool vrf_private::operator!=(const vrf_private& other) const +{ + return !(*this == other); +} + +// We must assume mainnet for public version here. +// When converting this to public a clone of this key should be used, with the +// public version specified - after validating the private version. +std::istream& operator>>(std::istream& in, vrf_private& to) +{ + std::string value; + in >> value; + data_chunk data; + decode_base16(data, value); + to = vrf_private(data); + return in; +} + +std::ostream& operator<<(std::ostream& out, const vrf_private& of) +{ + out << of.encoded(); + return out; +} + +bool vrf_private::prove(vrf_proof& proof, const data_chunk & m) +{ + return sodium::crypto_vrf_prove(proof.data(), secret_.data(), m.data(), m.size()) == 0; +} +bool vrf_private::proof_to_hash(vrf_hash& result, const vrf_proof& proof) +{ + return sodium::crypto_vrf_proof_to_hash(result.data(), proof.data()) == 0; +} +bool vrf_private::verify(vrf_hash& result, const vrf_proof& proof, const vrf_public& pk, const data_chunk & m) +{ + return sodium::crypto_vrf_verify(result.data(), pk.data(), proof.data(), m.data(), m.size()) == 0; +} + + +} // namespace wallet +} // namespace libbitcoin diff --git a/src/lib/blockchain/block_chain_impl.cpp b/src/lib/blockchain/block_chain_impl.cpp index c28d02422..6055ab640 100644 --- a/src/lib/blockchain/block_chain_impl.cpp +++ b/src/lib/blockchain/block_chain_impl.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,10 @@ #include #include #include +#include +#include +#include + namespace libbitcoin { namespace blockchain { @@ -47,14 +52,18 @@ namespace blockchain { using namespace bc::chain; using namespace bc::database; +using namespace bc::message; using namespace boost::interprocess; using namespace std::placeholders; using boost::filesystem::path; +using string = std::string; + block_chain_impl::block_chain_impl(threadpool& pool, const blockchain::settings& chain_settings, const database::settings& database_settings) : stopped_(true), + sync_disabled_(false), settings_(chain_settings), organizer_(pool, *this, chain_settings), ////read_dispatch_(pool, NAME), @@ -148,6 +157,140 @@ void block_chain_impl::subscribe_reorganize(reorganize_handler handler) organizer_.subscribe_reorganize(handler); } + +bool block_chain_impl::check_pos_utxo_capability( + const uint64_t& height, const chain::transaction& tx, const uint32_t& out_index, + const uint64_t& out_height, bool strict, const validate_block* validate_block) +{ + if (out_index >= tx.outputs.size()){ + return false; + } + + const auto output = tx.outputs[out_index]; + + if (strict) { + if (!check_pos_utxo_height_and_value(out_height, height, output.value)) { + return false; + } + } + + if (chain::operation::is_pay_key_hash_with_lock_height_pattern(output.script.operations)){ + // deposit utxo in block + uint64_t lock_height = chain::operation:: + get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); + if (lock_height > calc_number_of_blocks(out_height, height, validate_block)) { + return false; + } + } + else if (tx.is_coinbase()){ // coin base etp maturity etp check + // add not coinbase_maturity etp into frozen + if (coinbase_maturity > calc_number_of_blocks(out_height, height, validate_block)) { + return false; + } + } + + return true; +} + +bool block_chain_impl::pos_exist_before(const uint64_t& height) +{ + auto pos = pos_enabled_height; + while (pos++ < height) { + chain::header header; + if (get_header(header, pos) && header.is_proof_of_stake()) { + return true; + } + } + + return false; +} + +bool block_chain_impl::select_utxo_for_staking( + uint64_t best_height, + const wallet::payment_address& pay_address, + chain::output_info::list& stake_outputs, + uint32_t max_count) +{ + bool result = false; + auto&& rows = get_address_history(pay_address, false); + + chain::transaction tx_temp; + uint64_t tx_height; + size_t stake_utxos = 0; + size_t collect_utxos = 0; + + for (auto & row : rows) { + if (row.output_height == 0 || row.value == 0) { + continue; + } + + // spend unconfirmed (or no spend attempted) + if ((row.spend.hash == null_hash) + && get_transaction(row.output.hash, tx_temp, tx_height)) { + BITCOIN_ASSERT(row.output.index < tx_temp.outputs.size()); + auto output = tx_temp.outputs.at(row.output.index); + if (!output.is_etp() || output.get_script_address() != pay_address.encoded()) { + continue; + } + + if (!check_pos_utxo_capability(best_height, tx_temp, row.output.index, row.output_height, false)){ + continue; + } + + bool satisfied = check_pos_utxo_height_and_value(row.output_height, best_height, row.value); + if (satisfied) { + ++stake_utxos; + stake_outputs.push_back( {output, row.output, tx_height} ); + if (stake_utxos >= max_count) { + break; + } + } + else if (collect_utxos < pos_coinstake_max_utxos + && row.value < pos_stake_min_value) { + // collect utxos to satisfy pos_stake_min_value + ++collect_utxos; + stake_outputs.push_back( {output, row.output, tx_height} ); + } + } + } + +#ifdef PRIVATE_CHAIN + if (stake_utxos > 0) { + log::info("blockchain") << "found " << stake_utxos << " stake utxos."; + } +#endif + + return (stake_utxos > 0); +} + +chain::header::ptr block_chain_impl::get_last_block_header(const chain::header& parent_header, bool is_staking) const +{ + uint64_t height = parent_header.number; + if (parent_header.is_proof_of_stake() == is_staking) { + // log::info("BLOCKCHAIN") << "get_last_block_header: prev: " + // << std::to_string(parent_header.number) << ", last: " << std::to_string(height); + return std::make_shared(parent_header); + } + + while ((is_staking && height > pos_enabled_height) || (!is_staking && height > 2)) { + --height; + + chain::header prev_header; + if (!get_header(prev_header, height)) { + log::warning("BLOCKCHAIN") << "Failed to get header at " << std::to_string(height); + return nullptr; + } + + if (prev_header.is_proof_of_stake() == is_staking) { + // log::info("BLOCKCHAIN") << "get_last_block_header: prev: " + // << std::to_string(parent_header.number) << ", last: " << std::to_string(height); + return std::make_shared(prev_header); + } + } + + return nullptr; +} + // simple_chain (no locks, not thread safe). // ---------------------------------------------------------------------------- @@ -203,6 +346,9 @@ bool block_chain_impl::get_difficulty(u256& out_difficulty, bool block_chain_impl::get_header(header& out_header, uint64_t height) const { + if (stopped()) + return false; + auto result = database_.blocks.get(height); if (!result) return false; @@ -211,6 +357,15 @@ bool block_chain_impl::get_header(header& out_header, uint64_t height) const return true; } +uint64_t block_chain_impl::get_transaction_count(uint64_t block_height) const +{ + auto result = database_.blocks.get(block_height); + if (!result) + return 0; + + return result.transaction_count(); +} + bool block_chain_impl::get_height(uint64_t& out_height, const hash_digest& block_hash) const { @@ -224,6 +379,9 @@ bool block_chain_impl::get_height(uint64_t& out_height, bool block_chain_impl::get_last_height(uint64_t& out_height) const { + if (stopped()) + return false; + size_t top; if (database_.blocks.top(top)) { @@ -287,7 +445,7 @@ bool block_chain_impl::pop_from(block_detail::list& out_blocks, // The fork is at the top of the chain, nothing to pop. if (height == top + 1) - return true; + return false; // The fork is disconnected from the chain, fail. if (height > top) @@ -298,8 +456,12 @@ bool block_chain_impl::pop_from(block_detail::list& out_blocks, for (uint64_t index = top; index >= height; --index) { - const auto block = std::make_shared(database_.pop()); - out_blocks.push_back(block); + chain::block block; + if (!database_.pop(block)) { + return false; + } + const auto sp_block = std::make_shared(std::move(block)); + out_blocks.push_back(sp_block); } return true; @@ -320,6 +482,17 @@ void block_chain_impl::stop_write() BITCOIN_ASSERT(result); } +block_chain_writer::block_chain_writer(block_chain_impl& chain) + : chain_(chain) +{ + chain_.start_write(); +} + +block_chain_writer::~block_chain_writer() +{ + chain_.stop_write(); +} + // This call is sequential, but we are preserving the callback model for now. void block_chain_impl::store(message::block_message::ptr block, block_store_handler handler) @@ -330,6 +503,12 @@ void block_chain_impl::store(message::block_message::ptr block, return; } + if (is_sync_disabled()) + { + handler(error::sync_disabled, 0); + return; + } + // We moved write to the network thread using a critical section here. // We do not want to give the thread to any other activity at this point. // A flood of valid orphans from multiple peers could tie up the CPU here, @@ -351,12 +530,10 @@ void block_chain_impl::store(message::block_message::ptr block, void block_chain_impl::do_store(message::block_message::ptr block, block_store_handler handler) { - start_write(); - - // fail fast if the block is already stored... + // fail fast if the bloc`k is already stored... if (database_.blocks.get(block->header.hash())) { - stop_write(handler, error::duplicate, 0); + handler(error::duplicate, 0); return; } @@ -365,15 +542,14 @@ void block_chain_impl::do_store(message::block_message::ptr block, // ...or if the block is already orphaned. if (!organizer_.add(detail)) { - stop_write(handler, error::duplicate, 0); + handler(error::duplicate, 0); return; } // Otherwise organize the chain... organizer_.organize(); - //...and then get the particular block's status. - stop_write(handler, detail->error(), detail->height()); + handler(detail->error(), detail->height()); } /////////////////////////////////////////////////////////////////////////////// @@ -835,6 +1011,68 @@ void block_chain_impl::fetch_block_transaction_hashes(const hash_digest& hash, fetch_serial(do_fetch); } +/// fetch hashes of transactions for a block, by block height. +void block_chain_impl::fetch_block_signature(uint64_t height, + block_signature_fetch_handler handler) +{ + if (stopped()) + { + handler(error::service_stopped, {}); + return; + } + + const auto do_fetch = [this, height, handler](size_t slock) + { + ec_signature sig; + auto found = false; + { + const auto result = database_.blocks.get(height); + if(result) + { + if (result.header().is_proof_of_stake() ) + sig = result.blocksig(); + found = true; + } + } + + return found ? + finish_fetch(slock, handler, error::success, sig) : + finish_fetch(slock, handler, error::not_found, sig); + }; + fetch_serial(do_fetch); +} + +/// fetch hashes of transactions for a block, by block hash. +void block_chain_impl::fetch_block_signature(const hash_digest& hash, + block_signature_fetch_handler handler) +{ + if (stopped()) + { + handler(error::service_stopped, {}); + return; + } + + const auto do_fetch = [this, hash, handler](size_t slock) + { + ec_signature sig; + auto found = false; + { + const auto result = database_.blocks.get(hash); + if(result) + { + if (result.header().is_proof_of_stake() ) + sig = result.blocksig(); + found = true; + } + } + + return found ? + finish_fetch(slock, handler, error::success, sig) : + finish_fetch(slock, handler, error::not_found, sig); + }; + fetch_serial(do_fetch); +} + void block_chain_impl::fetch_block_height(const hash_digest& hash, block_height_fetch_handler handler) { @@ -976,7 +1214,7 @@ bool block_chain_impl::fetch_history(const wallet::payment_address& address, mutex.lock(); auto f = [&history, &mutex](const code& ec, const history_compact::list& history_) -> void { - if((code)error::success == ec) + if (error::success == ec.value()) history = history_; mutex.unlock(); }; @@ -1006,13 +1244,13 @@ void block_chain_impl::fetch_stealth(const binary& filter, uint64_t from_height, fetch_serial(do_fetch); } -inline hash_digest block_chain_impl::get_hash(const std::string& str) +inline hash_digest block_chain_impl::get_hash(const std::string& str) const { data_chunk data(str.begin(), str.end()); return sha256_hash(data); } -inline short_hash block_chain_impl::get_short_hash(const std::string& str) +inline short_hash block_chain_impl::get_short_hash(const std::string& str) const { data_chunk data(str.begin(), str.end()); return ripemd160_hash(data); @@ -1037,7 +1275,7 @@ std::shared_ptr block_chain_impl::is_account_passwd_valid { //added by chengzhiping to protect accounts from brute force password attacks. auto *ass = account_security_strategy::get_instance(); - ass->check_locked(name); + // ass->check_locked(name); auto account = get_account(name); if (account && account->get_passwd() == get_hash(passwd)) { // account exist @@ -1045,7 +1283,7 @@ std::shared_ptr block_chain_impl::is_account_passwd_valid return account; } - ass->on_auth_passwd(name, false); + // ass->on_auth_passwd(name, false); throw std::logic_error{"account not found or incorrect password"}; return nullptr; } @@ -1366,18 +1604,76 @@ static history::list expand_history(history_compact::list& compact) return result; } +bool block_chain_impl::check_pos_capability( + uint64_t best_height, + const wallet::payment_address& pay_address, + bool need_sync_lock) +{ + history::list rows; + + if (need_sync_lock) { + rows = get_address_history(pay_address, false); + } + else { + history_compact::list history = database_.history.get(pay_address.hash(), 0, 0); + rows = expand_history(history); + } + + chain::transaction tx_temp; + uint64_t tx_height; + + for (auto & row : rows) { + if (row.output_height == 0) { + continue; + } + + if ((row.spend.hash == null_hash) + && get_transaction(row.output.hash, tx_temp, tx_height)) { + BITCOIN_ASSERT(row.output.index < tx_temp.outputs.size()); + auto output = tx_temp.outputs.at(row.output.index); + if (output.get_script_address() != pay_address.encoded()) { + continue; + } + + // deposit tx must be maturity + if (tx_height + coinbase_maturity > best_height) { + continue; + } + + if ( row.value >= pos_lock_min_value && + chain::operation::is_pay_key_hash_with_lock_height_pattern(output.script.operations)) + { + // deposit utxo in block + uint64_t lock_height = chain::operation:: + get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); + + // utxo deposit height > pos_lock_min_height and min_pos_lock_rate percent of height limited + if (lock_height >= pos_lock_min_height && + (row.output_height + lock_height - pos_lock_gap_height) > best_height){ + return true; + } + } + } + } + + return false; +} + history::list block_chain_impl::get_address_history(const wallet::payment_address& addr, bool add_memory_pool) { history_compact::list cmp_history; bool result = true; if (add_memory_pool) { result = get_history(addr, 0, 0, cmp_history); - } else { + } + else { result = fetch_history(addr, 0, 0, cmp_history); } + if (result) { return expand_history(cmp_history); } + return history::list(); } @@ -1448,12 +1744,18 @@ block_chain_impl::get_account_asset_certs(const std::string& account, const std: return ret_vector; } -std::shared_ptr block_chain_impl::get_issued_asset_certs() +std::shared_ptr block_chain_impl::get_issued_asset_certs( + const std::string& address) { auto sp_vec = std::make_shared(); auto sp_asset_certs_vec = database_.certs.get_blockchain_asset_certs(); - for (const auto& each : *sp_asset_certs_vec) + for (const auto& each : *sp_asset_certs_vec) { + if (!address.empty() && address != each.get_address()) { + continue; + } + sp_vec->emplace_back(std::move(each)); + } return sp_vec; } @@ -1535,7 +1837,7 @@ std::shared_ptr block_chain_impl::get_account_mits( uint64_t block_chain_impl::get_address_asset_volume(const std::string& addr, const std::string& asset) { uint64_t asset_volume = 0; - auto address = payment_address(addr); + auto address = wallet::payment_address(addr); auto&& rows = get_address_history(address); chain::transaction tx_temp; @@ -1577,7 +1879,7 @@ uint64_t block_chain_impl::get_asset_volume(const std::string& asset) return database_.assets.get_asset_volume(asset); } -std::string block_chain_impl::get_asset_symbol_from_business_data(const business_data& data) +std::string block_chain_impl::get_asset_symbol_from_business_data(const business_data& data) const { std::string asset_symbol(""); if (data.get_kind_value() == business_kind::asset_issue) { @@ -1840,7 +2142,8 @@ std::shared_ptr block_chain_impl::get_account_unis } /// get all the asset in blockchain -std::shared_ptr block_chain_impl::get_issued_assets(const std::string& symbol) +std::shared_ptr block_chain_impl::get_issued_assets( + const std::string& symbol, const std::string& address) { auto sp_vec = std::make_shared(); const auto is_symbol_empty = symbol.empty(); @@ -1855,6 +2158,10 @@ std::shared_ptr block_chain_impl::get_issued_assets(const st continue; } + if (!address.empty() && address != asset.get_address()) { + continue; + } + sp_vec->push_back(asset); } return sp_vec; @@ -1871,7 +2178,7 @@ std::shared_ptr block_chain_impl::get_asset_register_out /* check did symbol exist or not */ -bool block_chain_impl::is_did_exist(const std::string& did_name) +bool block_chain_impl::is_did_exist(const std::string& did_name) const { // find from blockchain database return get_registered_did(did_name) != nullptr; @@ -1962,7 +2269,7 @@ std::shared_ptr block_chain_impl::get_did_history_addresse return database_.dids.get_history_dids(hash); } -std::shared_ptr block_chain_impl::get_registered_did(const std::string& symbol) +std::shared_ptr block_chain_impl::get_registered_did(const std::string& symbol) const { std::shared_ptr sp_did(nullptr); const auto hash = get_hash(symbol); @@ -2185,7 +2492,7 @@ bool block_chain_impl::get_transaction(const hash_digest& hash, mutex.lock(); auto f = [&tx_ptr, &mutex](const code& ec, transaction_message::ptr tx_) -> void { - if((code)error::success == ec) + if (error::success == ec.value()) tx_ptr = tx_; mutex.unlock(); }; @@ -2225,7 +2532,7 @@ bool block_chain_impl::get_transaction_callback(const hash_digest& hash, auto f = [&tx_ptr, handler](const code& ec, transaction_message::ptr tx_) -> void { - if((code)error::success == ec){ + if (error::success == ec.value()){ tx_ptr = tx_; if(tx_ptr) handler(ec, *(static_cast>(tx_ptr))); @@ -2241,7 +2548,7 @@ bool block_chain_impl::get_transaction_callback(const hash_digest& hash, return ret; } -bool block_chain_impl::get_history_callback(const payment_address& address, +bool block_chain_impl::get_history_callback(const wallet::payment_address& address, size_t limit, size_t from_height, std::function handler) { @@ -2255,7 +2562,7 @@ bool block_chain_impl::get_history_callback(const payment_address& address, auto f = [&ret, handler](const code& ec, chain::history_compact::list compact) -> void { - if((code)error::success == ec){ + if (error::success == ec.value()){ history::list result; // Process and remove all outputs. @@ -2348,7 +2655,7 @@ code block_chain_impl::validate_transaction(const chain::transaction& tx) { log::debug("validate_transaction") << "ec=" << ec << " idx_vec=" << idx_vec.size(); log::debug("validate_transaction") << "ec.message=" << ec.message(); - //if((error::success == ec) && idx_vec.empty()) + //if((error::success == ec.value()) && idx_vec.empty()) ret = ec; mutex.unlock(); }; @@ -2386,7 +2693,7 @@ code block_chain_impl::broadcast_transaction(const chain::transaction& tx) log::debug("broadcast_transaction") << "ec=" << ec << " idx_vec=" << idx_vec.size(); log::debug("broadcast_transaction") << "ec.message=" << ec.message(); ret = ec; - if(error::success == ec){ + if (error::success == ec.value()) { log::trace("broadcast_transaction") << encode_hash(tx_ptr->hash()) << " validated"; } else { //send_mutex.unlock(); // incase dead lock @@ -2414,7 +2721,7 @@ bool block_chain_impl::get_history(const wallet::payment_address& address, mutex.lock(); auto f = [&history, &mutex](const code& ec, const history_compact::list& history_) -> void { - if((code)error::success == ec) + if (error::success == ec.value()) history = history_; mutex.unlock(); }; @@ -2466,6 +2773,270 @@ void block_chain_impl::safe_store_account(account& acc, std::vector pos_enabled_height) { + auto blocks_after_pos_enabled = to - std::max(from, pos_enabled_height); + to += blocks_after_pos_enabled; + } + return to; +} + +uint64_t block_chain_impl::calc_number_of_blocks( + uint64_t from, uint64_t to, const validate_block* validate_block) const +{ + if (from >= to) { + return 0; + } + + uint64_t number = to - from; + + if (to > pos_enabled_height) { + auto blocks_after_pos_enabled = to - std::max(from, pos_enabled_height); + number -= (blocks_after_pos_enabled + 1) / 2; + } + + // excludes dpos blocks + if (consensus::witness::is_dpos_enabled()) { + uint64_t start = std::max(from, consensus::witness::witness_enable_height); + if (start < to) { + chain::header out_header; + for (auto i = start; i < to; ++i) { + if ((validate_block && !validate_block->get_header(out_header, i)) + || (!validate_block && !get_header(out_header, i))) { + return 0; + } + if (out_header.is_proof_of_dpos()) { + --number; + } + } + } + } + + return number; +} + +/// @return pair of +std::pair block_chain_impl::get_locked_balance( + const std::string& address, uint64_t expiration, const std::string& asset_symbol) const +{ + const bool is_asset = !asset_symbol.empty(); + if (is_asset && wallet::symbol::is_forbidden(asset_symbol)) { + return std::make_pair(0, 0); + } + + uint64_t locked_balance = 0; + uint64_t locked_weight = 0; + auto& rThis = const_cast(*this); + + auto&& rows = rThis.get_address_history(wallet::payment_address(address)); + + chain::transaction tx_temp; + uint64_t tx_height = 0; + + uint64_t height = 0; + get_last_height(height); + + for (auto& row: rows) + { + // spend unconfirmed (or no spend attempted) + if ((row.spend.hash == null_hash) + && rThis.get_transaction(row.output.hash, tx_temp, tx_height)) + { + // tx not maturity + if (tx_height + consensus::witness::vote_maturity > height) { + continue; + } + + BITCOIN_ASSERT(row.output.index < tx_temp.outputs.size()); + const auto& output = tx_temp.outputs.at(row.output.index); + + if (is_asset != output.is_asset()) { + continue; + } + + if (is_asset && asset_symbol != output.get_asset_symbol()) { + continue; + } + + if (!operation::is_pay_key_hash_with_sequence_lock_pattern(output.script.operations)) { + continue; + } + + uint64_t lock_sequence = chain::operation:: + get_lock_sequence_from_pay_key_hash_with_sequence_lock(output.script.operations); + // use any kind of blocks + if ((tx_height + lock_sequence <= height) || + (expiration > height && tx_height + lock_sequence <= expiration)) { + continue; + } + + uint64_t locked_value = is_asset ? output.get_asset_amount() : row.value; + locked_balance += locked_value; + auto weight = std::min( + consensus::witness::epoch_cycle_height, + tx_height + lock_sequence - height); + locked_weight += locked_value * weight; + } + } + return std::make_pair(locked_balance, locked_weight); +} + +/// how to register witness? just send (p2kh) exact witness::witness_register_fee ETP +/// to DID witness::witness_registry_did, and from your DID as the source. +/// @return vector of pair of +static std::pair +filter_witness(const block_chain_impl& chain, const business_record& buss, const std::string& symbol) +{ + std::pair result; + if (buss.kind != point_kind::output) { + return result; + } + const bool is_asset = !symbol.empty(); + uint64_t amount = 0; + const auto& bussi_data = buss.data; + auto bussi_kind = bussi_data.get_kind_value(); + if (!is_asset) { + // etp business process + if (bussi_kind == business_kind::etp) { + amount = buss.val_chk_sum.value; + } + } + else { + // asset business process + if (bussi_kind == business_kind::asset_transfer) { + auto transfer = boost::get(bussi_data.get_data()); + if (symbol == transfer.get_symbol()) { + amount = transfer.get_quantity(); + } + } + } + + if (amount != consensus::witness::witness_register_fee) { + return result; + } + + chain::transaction tx_temp; + uint64_t tx_height = 0; + if (!chain.get_transaction(tx_temp, tx_height, buss.point.hash)) { + return result; + } + + const auto& output = tx_temp.outputs.at(buss.point.index); + if (!operation::is_pay_key_hash_pattern(output.script.operations)) { + return result; + } + + if (output.attach_data.get_to_did() != consensus::witness::witness_registry_did) { + return result; + } + + const auto& from_did = output.attach_data.get_from_did(); + if (from_did.empty()) { + return result; + } + + auto did_detail = chain.get_registered_did(from_did); + if (!did_detail) { + return result; + } + + const auto& from_address = did_detail->get_address(); + if (from_address.empty()) { + return result; + } + + const auto& input_ops = tx_temp.inputs.front().script.operations; + if (input_ops.size() < 2 || !is_public_key(input_ops[1].data)) { + return result; + } + + return std::make_pair(from_address, input_ops[1].data); +} + +std::vector> block_chain_impl::get_register_witnesses( + const std::string& addr, const std::string& symbol, + size_t start_height, size_t end_height, uint64_t limit, uint64_t page_number) const +{ + using addr_pubkey_pair_t = std::pair; + const bool is_asset = !symbol.empty(); + if (is_asset && wallet::symbol::is_forbidden(symbol)) { + return std::vector(); + } + + std::set witnesses; + auto sh_vec = database_.address_assets.get(addr, symbol, start_height, end_height, limit, page_number); + + for (auto iter = sh_vec->begin(); iter != sh_vec->end(); ++iter) { + auto addr_pubkey_pair = filter_witness(*this, *iter, symbol); + if (addr_pubkey_pair.first.empty()) { + continue; + } + witnesses.insert(addr_pubkey_pair); + } + + return std::vector(witnesses.begin(), witnesses.end()); +} + +/// stake holder is publickey and lockvalue pair + +std::shared_ptr block_chain_impl::get_register_witnesses_with_stake( + const std::string& addr, const std::string& symbol, + size_t start_height, size_t end_height, uint64_t limit, uint64_t page_number) const +{ + auto stakeholders = std::make_shared(); + + const bool is_asset = !symbol.empty(); + if (is_asset && wallet::symbol::is_forbidden(symbol)) { + return stakeholders; + } + + std::set witnesses; // contains pubkey + auto sh_vec = database_.address_assets.get(addr, symbol, start_height, end_height, limit, page_number); + + for (auto iter = sh_vec->begin(); iter != sh_vec->end(); ++iter) { + if (stakeholders->size() >= 10 * consensus::witness::max_candidate_count) { + break; + } + + auto addr_pubkey_pair = filter_witness(*this, *iter, symbol); + if (addr_pubkey_pair.first.empty()) { + continue; + } + + auto pubkey = encode_base16(addr_pubkey_pair.second); + if (witnesses.count(pubkey)) { + continue; + } + + auto locked_balance = get_locked_balance(addr_pubkey_pair.first, end_height); + if (locked_balance.first < consensus::witness::witness_lock_threshold) { + continue; + } + + witnesses.insert(pubkey); + auto item = std::make_shared(pubkey, locked_balance.first); + stakeholders->emplace_back(item); + } + + return stakeholders; +} } // namespace blockchain } // namespace libbitcoin diff --git a/src/lib/blockchain/block_fetcher.cpp b/src/lib/blockchain/block_fetcher.cpp index a9070dcb2..a5a78f52f 100644 --- a/src/lib/blockchain/block_fetcher.cpp +++ b/src/lib/blockchain/block_fetcher.cpp @@ -57,17 +57,40 @@ class block_fetcher private: void handle_fetch_header(const code& ec, const header& header, - block::ptr block, block_chain::block_fetch_handler handler) - { - if (ec) - { + block::ptr block, block_chain::block_fetch_handler handler) { + if (ec) { handler(ec, nullptr); return; } // Set the block header. block->header = header; - const auto hash = header.hash(); + + const auto& hash = block->header.hash(); + if (block->header.is_proof_of_stake()) + { + blockchain_.fetch_block_signature(hash, + std::bind(&block_fetcher::handle_fetch_signature, + shared_from_this(), _1, _2, block, handler)); + } else { + blockchain_.fetch_block_transaction_hashes(hash, + std::bind(&block_fetcher::fetch_transactions, + shared_from_this(), _1, _2, block, handler)); + } + } + + void handle_fetch_signature(const code& ec, const ec_signature& sig, + block::ptr block, block_chain::block_fetch_handler handler){ + + if (ec) { + handler(ec, nullptr); + return; + } + + // Set the block header. + block->blocksig = sig; + + const auto& hash = block->header.hash(); blockchain_.fetch_block_transaction_hashes(hash, std::bind(&block_fetcher::fetch_transactions, diff --git a/src/lib/blockchain/organizer.cpp b/src/lib/blockchain/organizer.cpp index 011d0ca92..a076c8cb5 100644 --- a/src/lib/blockchain/organizer.cpp +++ b/src/lib/blockchain/organizer.cpp @@ -19,6 +19,7 @@ * along with this program. If not, see . */ #include +#include #include #include @@ -31,12 +32,12 @@ #include #include #include -#include #include #include #include #include #include +#include namespace libbitcoin { namespace blockchain { @@ -46,7 +47,7 @@ using namespace bc::config; #define NAME "organizer" -organizer::organizer(threadpool& pool, simple_chain& chain, +organizer::organizer(threadpool& pool, block_chain_impl& chain, const settings& settings) : stopped_(true), use_testnet_rules_(settings.use_testnet_rules), @@ -113,19 +114,17 @@ code organizer::verify(uint64_t fork_point, *current_block, use_testnet_rules_, checkpoints_, callback); // Checks that are independent of the chain. - auto ec = validate.check_block(static_cast(this->chain_)); - if (error::success != ec) { + auto ec = validate.check_block(chain_); + if (error::success != ec.value()) { log::debug(LOG_BLOCKCHAIN) << "organizer: check_block failed! error:" << std::to_string(ec.value()) << ", fork_point: " << std::to_string(fork_point) << ", orphan_index: " << std::to_string(orphan_index); return ec; } - validate.initialize_context(); - // Checks that are dependent on height and preceding blocks. ec = validate.accept_block(); - if (error::success != ec) { + if (error::success != ec.value()) { log::debug(LOG_BLOCKCHAIN) << "organizer: accept_block failed! error:" << std::to_string(ec.value()) << ", fork_point: " << std::to_string(fork_point) << ", orphan_index: " << std::to_string(orphan_index); @@ -141,7 +140,8 @@ code organizer::verify(uint64_t fork_point, const auto total_transactions = current_block->transactions.size(); log::info(LOG_BLOCKCHAIN) - << "Block [" << height << "] verify (" << total_transactions + << get_block_version(current_block->header) + << " block [" << height << "] verify (" << total_transactions << ") txs and (" << total_inputs << ") inputs"; // Time this for logging. @@ -149,9 +149,9 @@ code organizer::verify(uint64_t fork_point, { hash_digest err_tx; // Checks that include input->output traversal. - ec = validate.connect_block(err_tx, static_cast(this->chain_)); + ec = validate.connect_block(err_tx, chain_); if(ec && err_tx != null_hash) { - dynamic_cast(chain_).pool().delete_tx(err_tx); + chain_.pool().delete_tx(err_tx); } }; @@ -164,7 +164,8 @@ code organizer::verify(uint64_t fork_point, const auto verified = ec ? "unverified" : "verified"; log::info(LOG_BLOCKCHAIN) - << "Block [" << height << "] " << verified << " in (" + << get_block_version(current_block->header) + << " block [" << height << "] verified in (" << seconds_per_block << ") secs or (" << ms_per_input << ") ms/input"; return ec; @@ -215,7 +216,40 @@ void organizer::process(block_detail::ptr process_block) // Verify the blocks in the orphan chain. if (chain_.get_height(fork_index, hash)) { - replace_chain(fork_index, orphan_chain); + bool replace_chain_done = false; + if (consensus::witness::is_dpos_enabled()) { + uint64_t current_block_height = 0; + DEBUG_ONLY(auto ok =) chain_.get_last_height(current_block_height); + BITCOIN_ASSERT(ok); + if (consensus::witness::is_witness_enabled(current_block_height)) { + auto witness_list = consensus::witness::get().get_witness_list(); + if (!consensus::witness::is_in_same_epoch(fork_index, current_block_height)) { + consensus::witness::get().update_witness_list(fork_index); + } + + auto ret = replace_chain(fork_index, orphan_chain); + replace_chain_done = true; + + const auto& num_of_poped_blocks = std::get<0>(ret); + const auto& num_of_pushed_blocks = std::get<1>(ret); + const auto need_recovery = num_of_poped_blocks == 0 && num_of_pushed_blocks == 0; + const auto need_reupdate = num_of_poped_blocks != 0 && num_of_pushed_blocks == 0; + if (need_recovery) { + consensus::witness::get().swap_witness_list(witness_list); + } + else if (need_reupdate) { + const auto& new_block_height = std::get<2>(ret); + if (!consensus::witness::is_in_same_epoch(fork_index, new_block_height)) { + consensus::witness::get().update_witness_list(new_block_height); + } + } + } + } + + if (!replace_chain_done) { + DEBUG_ONLY(auto ret =) replace_chain(fork_index, orphan_chain); + } + if(orphan_chain.empty() == false) { const auto hash = orphan_chain.back()->actual()->header.hash(); @@ -229,8 +263,10 @@ void organizer::process(block_detail::ptr process_block) } else { - orphan_pool_.add_pending_block(hash, orphan_chain.back()); - log::warning(LOG_BLOCKCHAIN) << "push pendingblock hash:" << encode_hash(hash) << " process_block hash:" << encode_hash(orphan_chain.back()->actual()->header.hash()); + bool added = orphan_pool_.add_pending_block(hash, orphan_chain.back()); + if (added) { + log::warning(LOG_BLOCKCHAIN) << "push pendingblock hash:" << encode_hash(hash) << " process_block hash:" << encode_hash(orphan_chain.back()->actual()->header.hash()); + } } // Don't mark all orphan_chain as processed here because there might be @@ -253,67 +289,115 @@ static std::set> exception_blocks { {1258192, "d7d5d80c8cb760b794f156c36b519ad9b4b10b9dfcd4e123c8f54be3c71432d3"} }; -void organizer::replace_chain(uint64_t fork_index, - block_detail::list& orphan_chain) +static u256 get_orphan_difficulty_without_verify(const block_detail::list& orphan_chain) { u256 orphan_work = 0; + for (auto& block : orphan_chain) { + orphan_work += block_work(block->actual()->header.bits); + } + return orphan_work; +} - for (uint64_t orphan = 0; orphan < orphan_chain.size(); ++orphan) - { - // This verifies the block at orphan_chain[orphan]->actual() - if(!orphan_chain[orphan]->get_is_checked_work_proof()) +// Return a tuple +std::tuple +organizer::replace_chain(uint64_t fork_index, block_detail::list& orphan_chain) +{ + auto ret = std::make_tuple(0, 0, 0); + auto& num_of_poped_blocks = std::get<0>(ret); + auto& num_of_pushed_blocks = std::get<1>(ret); + auto& current_block_height = std::get<2>(ret); + + DEBUG_ONLY(auto ok =) chain_.get_last_height(current_block_height); + BITCOIN_ASSERT(ok); + + const auto begin_index = fork_index + 1; + + u256 main_work; + DEBUG_ONLY(auto result =) chain_.get_difficulty(main_work, begin_index); + BITCOIN_ASSERT(result); + + u256 orphan_work = get_orphan_difficulty_without_verify(orphan_chain); + + if (orphan_work > main_work) { + // reset and recalc with verify + orphan_work = 0; + static std::pair failed_orphan = std::make_pair(null_hash, 0); + for (uint64_t orphan = 0; orphan < orphan_chain.size(); ++orphan) { - const auto ec = verify(fork_index, orphan_chain, orphan); - if (ec) + // This verifies the block at orphan_chain[orphan]->actual() + if(!orphan_chain[orphan]->get_is_checked_work_proof()) { - // If invalid block info is also set for the block. - if (ec != (code)error::service_stopped) + const auto ec = verify(fork_index, orphan_chain, orphan); + if (ec) { - const auto& header = orphan_chain[orphan]->actual()->header; - const auto block_hash = encode_hash(header.hash()); + // If invalid block info is also set for the block. + if (ec.value() != error::service_stopped) + { + const auto& header = orphan_chain[orphan]->actual()->header; + const auto block_hash = encode_hash(header.hash()); + + if (exception_blocks.count(std::make_pair(header.number, block_hash))) + { + orphan_chain[orphan]->set_is_checked_work_proof(true); + const auto& orphan_block = orphan_chain[orphan]->actual(); + orphan_work += block_work(orphan_block->header.bits); + continue; + } + + if (header.hash() != failed_orphan.first) { + failed_orphan = std::make_pair(header.hash(), 1); + } + else { + ++failed_orphan.second; + constexpr uint32_t too_many_fails = 100; + constexpr uint32_t max_pop_gaps = 1000; + if (failed_orphan.second >= too_many_fails + && fork_index + max_pop_gaps > current_block_height) { + failed_orphan = std::make_pair(null_hash, 0); // reset + // force pop blocks, verify again later(keep syncing). + block_chain_writer locked_write(chain_); + block_detail::list released_blocks; + chain_.pop_from(released_blocks, begin_index); + if (!released_blocks.empty()) { + log::warning(LOG_BLOCKCHAIN) + << "force pop block from " << begin_index + << ", verify again orphan block " << block_hash; + num_of_poped_blocks = released_blocks.size(); + current_block_height = released_blocks.front()->actual()->header.number - 1; + } + return ret; + } + } + + log::warning(LOG_BLOCKCHAIN) + << "Invalid block [" << block_hash << "] " << ec.message(); + } - if (exception_blocks.count(std::make_pair(header.number, block_hash))) + // Block is invalid, clip the orphans. + clip_orphans(orphan_chain, orphan, ec); + if(orphan_chain.empty()) { - orphan_chain[orphan]->set_is_checked_work_proof(true); - const auto& orphan_block = orphan_chain[orphan]->actual(); - orphan_work += block_work(orphan_block->header.bits); - continue; + log::warning(LOG_BLOCKCHAIN) << "orphan_chain.empty()"; + return ret; } - log::warning(LOG_BLOCKCHAIN) - << "Invalid block [" << block_hash << "] " << ec.message(); + // Stop summing work once we discover an invalid block + break; } - - // Block is invalid, clip the orphans. - clip_orphans(orphan_chain, orphan, ec); - if(orphan_chain.empty()) + else { - log::warning(LOG_BLOCKCHAIN) << "orphan_chain.empty()"; - return; + orphan_chain[orphan]->set_is_checked_work_proof(true); } - - // Stop summing work once we discover an invalid block - break; - } - else - { - orphan_chain[orphan]->set_is_checked_work_proof(true); } - } - const auto& orphan_block = orphan_chain[orphan]->actual(); - orphan_work += block_work(orphan_block->header.bits); + const auto& orphan_block = orphan_chain[orphan]->actual(); + orphan_work += block_work(orphan_block->header.bits); + } } // All remaining blocks in orphan_chain should all be valid now // Compare the difficulty of the 2 forks (original and orphan) - const auto begin_index = fork_index + 1; - - u256 main_work; - DEBUG_ONLY(auto result =) chain_.get_difficulty(main_work, begin_index); - BITCOIN_ASSERT(result); - - delete_fork_chain_hash(orphan_chain[orphan_chain.size() - 1]->actual()->header.previous_block_hash); + delete_fork_chain_hash(orphan_chain.back()->actual()->header.previous_block_hash); if (orphan_work <= main_work) { if(orphan_chain.size() % node::locator_cap == 0) @@ -321,24 +405,32 @@ void organizer::replace_chain(uint64_t fork_index, add_fork_chain_hash(orphan_chain.back()->actual()->header.hash()); log::debug(LOG_BLOCKCHAIN) - << "Insufficient work to reorganize at [" << begin_index << "]" << "orphan_work:" << orphan_work << " main_work:" << main_work; - return; + << "Insufficient work to reorganize at [" << begin_index << "]" + << "orphan_work:" << orphan_work << " main_work:" << main_work; + return ret; } + ////////////////// WRITE LOCKED /////////////////////////////////// + block_chain_writer locked_write(chain_); + // Replace! Switch! block_detail::list released_blocks; - auto success = chain_.pop_from(released_blocks, begin_index); + auto pop_all_success = chain_.pop_from(released_blocks, begin_index); - if (!released_blocks.empty()) + if (!released_blocks.empty()) { + num_of_poped_blocks = released_blocks.size(); + current_block_height = released_blocks.front()->actual()->header.number - 1; + if (!pop_all_success) { + log::warning(LOG_BLOCKCHAIN) + << " not all blocks poped out successfully from " << begin_index + << ", only pop backward to " << current_block_height + << ", so stop replace chain."; + return ret; + } log::warning(LOG_BLOCKCHAIN) << " blockchain fork at height:" << released_blocks.front()->actual()->header.number << " begin_index:" << encode_hash(released_blocks.front()->actual()->header.hash()) << " size:" << released_blocks.size(); - - // if pop blocks failed, stop replace - if (!success) { - log::warning(LOG_BLOCKCHAIN) << " pop_from begin_height:" << begin_index << "failed"; - return; } // We add the arriving blocks first to the main chain because if @@ -351,6 +443,7 @@ void organizer::replace_chain(uint64_t fork_index, // All arrival_blocks should be blocks from the pool. auto arrival_index = fork_index; + block_detail::list pushed_blocks; for (const auto arrival_block: orphan_chain) { orphan_pool_.remove(arrival_block); @@ -363,19 +456,24 @@ void organizer::replace_chain(uint64_t fork_index, { log::warning(LOG_BLOCKCHAIN) << " push block height:" << arrival_block->actual()->header.number - << " hash:" << encode_hash(arrival_block->actual()->header.hash()) - << "failed"; + << " hash:" << encode_hash(arrival_block->actual()->header.hash()) + << " failed"; // if push block failed, stop replace - return; + break; } else { + current_block_height = arrival_block->actual()->header.number; + pushed_blocks.push_back(arrival_block); log::debug(LOG_BLOCKCHAIN) << " push block height:" << arrival_block->actual()->header.number - << " hash:" << encode_hash(arrival_block->actual()->header.hash()); + << " hash:" << encode_hash(arrival_block->actual()->header.hash()) + << " succeed"; } } + num_of_pushed_blocks = pushed_blocks.size(); + // Add the old blocks back to the pool (as processed with orphan height). for (const auto replaced_block: released_blocks) { @@ -392,7 +490,8 @@ void organizer::replace_chain(uint64_t fork_index, } } - notify_reorganize(fork_index, orphan_chain, released_blocks); + notify_reorganize(fork_index, pushed_blocks, released_blocks); + return ret; } void organizer::remove_processed(block_detail::ptr remove_block) @@ -457,7 +556,7 @@ void organizer::fired() std::unordered_map organizer::get_fork_chain_last_block_hashes() { - unordered_map hashes; + std::unordered_map hashes; boost::unique_lock lock(mutex_fork_chain_last_block_hashes_); hashes = fork_chain_last_block_hashes_; return hashes; diff --git a/src/lib/blockchain/transaction_pool.cpp b/src/lib/blockchain/transaction_pool.cpp index ccf3ec2ff..3658862ea 100644 --- a/src/lib/blockchain/transaction_pool.cpp +++ b/src/lib/blockchain/transaction_pool.cpp @@ -38,6 +38,8 @@ using namespace chain; using namespace wallet; using namespace std::placeholders; +using string = std::string; + transaction_pool::transaction_pool(threadpool& pool, block_chain& chain, const settings& settings) : stopped_(true), @@ -125,7 +127,7 @@ void transaction_pool::handle_validated(const code& ec, transaction_ptr tx, return; } - if (ec == (code)error::input_not_found || ec == (code)error::validate_inputs_failed) + if (ec.value() == error::input_not_found || ec.value() == error::validate_inputs_failed) { BITCOIN_ASSERT(unconfirmed.size() == 1); handler(ec, tx, unconfirmed); @@ -147,7 +149,7 @@ void transaction_pool::handle_validated(const code& ec, transaction_ptr tx, } code error = check_symbol_repeat(tx); - if (error != error::success) { + if (error) { handler(error, tx, {}); return; } @@ -274,11 +276,11 @@ code transaction_pool::check_symbol_repeat(transaction_ptr tx) if (!item.tx) continue; - if((ec = check_outputs(item.tx)) != error::success) + if((ec = check_outputs(item.tx))) break; } - return ec != error::success ? ec : check_outputs(tx); + return (ec.value() != error::success) ? ec : check_outputs(tx); } // handle_confirm will never fire if handle_validate returns a failure code. @@ -463,14 +465,14 @@ void transaction_pool::exists(const hash_digest& tx_hash, bool transaction_pool::handle_reorganized(const code& ec, size_t fork_point, const block_list& new_blocks, const block_list& replaced_blocks) { - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) { log::debug(LOG_BLOCKCHAIN) << "Stopping transaction pool: " << ec.message(); return false; } - if (ec == error::mock) + if (ec.value() == error::mock) return true; if (ec) @@ -480,12 +482,6 @@ bool transaction_pool::handle_reorganized(const code& ec, size_t fork_point, return false; } - log::debug(LOG_BLOCKCHAIN) - << "Reorganize: tx pool size (" << buffer_.size() - << ") forked at (" << fork_point - << ") new blocks (" << new_blocks.size() - << ") replace blocks (" << replaced_blocks.size() << ")"; - if (replaced_blocks.empty()) { // Remove memory pool transactions that also exist in new blocks. @@ -495,6 +491,12 @@ bool transaction_pool::handle_reorganized(const code& ec, size_t fork_point, } else { + log::debug(LOG_BLOCKCHAIN) + << "Reorganize: tx pool size (" << buffer_.size() + << ") forked at (" << fork_point + << ") new blocks (" << new_blocks.size() + << ") replace blocks (" << replaced_blocks.size() << ")"; + // See http://www.jwz.org/doc/worse-is-better.html // for why we take this approach. We return with an error_code. // An alternative would be resubmit all tx from the cleared blocks. @@ -658,6 +660,12 @@ bool transaction_pool::delete_single(const hash_digest& tx_hash, const code& ec) it->handle_confirm(ec, it->tx); buffer_.erase(it); + if (ec) { + log::debug(LOG_BLOCKCHAIN) + << "delete_tx " << encode_hash(tx_hash) + << ", error code is " << ec.message(); + } + while (1) { const auto it = std::find_if(buffer_.begin(), buffer_.end(), matched); diff --git a/src/lib/blockchain/transaction_pool_index.cpp b/src/lib/blockchain/transaction_pool_index.cpp index 36d4eb493..b79cdd302 100644 --- a/src/lib/blockchain/transaction_pool_index.cpp +++ b/src/lib/blockchain/transaction_pool_index.cpp @@ -134,7 +134,7 @@ void transaction_pool_index::do_add(const transaction& tx, if (address) { const output_point point{ tx_hash, index }; - const output_info info{ point, output.value }; + const output_point_info info{ point, output.value }; outputs_map_.emplace(std::move(address), std::move(info)); } @@ -216,7 +216,7 @@ void transaction_pool_index::blockchain_history_fetched(const code& ec, } void transaction_pool_index::index_history_fetched(const code& ec, - const spend_info::list& spends, const output_info::list& outputs, + const spend_info::list& spends, const output_point_info::list& outputs, const history_list& history, fetch_handler handler) { if (ec) @@ -255,7 +255,7 @@ void transaction_pool_index::do_fetch(const payment_address& address, // This is the end of the fetch_index_history sequence. handler(error::success, to_info_list(address, spends_map_), - to_info_list(address, outputs_map_)); + to_info_list(address, outputs_map_)); } // Static helpers @@ -275,7 +275,7 @@ bool transaction_pool_index::exists(history_list& history, } bool transaction_pool_index::exists(history_list& history, - const output_info& output) + const output_point_info& output) { const auto match = [&output](const history_compact& row) { @@ -298,7 +298,7 @@ void transaction_pool_index::add(history_list& history, const spend_info& spend) history.emplace_back(std::move(row)); } -void transaction_pool_index::add(history_list& history, const output_info& output) +void transaction_pool_index::add(history_list& history, const output_point_info& output) { const history_compact row { @@ -324,9 +324,9 @@ void transaction_pool_index::add(history_list& history, } void transaction_pool_index::add(history_list& history, - const output_info::list& outputs) + const output_point_info::list& outputs) { - const auto action = [&history](const output_info& output) + const auto action = [&history](const output_point_info& output) { if (!exists(history, output)) add(history, output); diff --git a/src/lib/blockchain/validate_block.cpp b/src/lib/blockchain/validate_block.cpp index a183e08e4..4bf06a638 100644 --- a/src/lib/blockchain/validate_block.cpp +++ b/src/lib/blockchain/validate_block.cpp @@ -19,6 +19,7 @@ * along with this program. If not, see . */ #include +#include #include #include @@ -29,11 +30,15 @@ #include #include #include +#include #include #include #include #include #include +#include + +using string = std::string; namespace libbitcoin { namespace blockchain { @@ -45,164 +50,193 @@ if (stopped()) \ using namespace chain; -// Consensus rule change activation and enforcement parameters. -static constexpr uint8_t version_4 = 4; -static constexpr uint8_t version_3 = 3; -static constexpr uint8_t version_2 = 2; -static constexpr uint8_t version_1 = 1; - -// Mainnet activation parameters. -static constexpr size_t mainnet_active = 51; -static constexpr size_t mainnet_enforce = 75; -static constexpr size_t mainnet_sample = 100; - -// Testnet activation parameters. -static constexpr size_t testnet_active = 750u; -static constexpr size_t testnet_enforce = 950u; -static constexpr size_t testnet_sample = 1000u; - -// Block 173805 is the first mainnet block after date-based activation. -// Block 514 is the first testnet block after date-based activation. -static constexpr size_t mainnet_bip16_activation_height = 173805; -static constexpr size_t testnet_bip16_activation_height = 514; - -// github.com/bitcoin/bips/blob/master/bip-0030.mediawiki#specification -static constexpr size_t mainnet_bip30_exception_height1 = 91842; -static constexpr size_t mainnet_bip30_exception_height2 = 91880; -static constexpr size_t testnet_bip30_exception_height1 = 0; -static constexpr size_t testnet_bip30_exception_height2 = 0; - // The default sigops count for mutisignature scripts. static constexpr uint32_t multisig_default_sigops = 20; -// Value used to define retargeting range constraint. -static constexpr uint64_t retargeting_factor = 4; - -// Aim for blocks every 10 mins (600 seconds). -static constexpr uint64_t target_spacing_seconds = 10 * 60; - -// Target readjustment every 2 weeks (1209600 seconds). -static constexpr uint64_t target_timespan_seconds = 2 * 7 * 24 * 60 * 60; - -// The target number of blocks for 2 weeks of work (2016 blocks). -static constexpr uint64_t retargeting_interval = target_timespan_seconds / - target_spacing_seconds; - // The window by which a time stamp may exceed our current time (2 hours). static const auto time_stamp_window = asio::seconds(2 * 60 * 60); static const auto time_stamp_window_future_blocktime_fix = asio::seconds(24); // The nullptr option is for backward compatibility only. -validate_block::validate_block(size_t height, const block& block, bool testnet, +validate_block::validate_block(uint64_t height, const block& block, bool testnet, const config::checkpoint::list& checks, stopped_callback callback) : testnet_(testnet), height_(height), activations_(script_context::none_enabled), - minimum_version_(0), current_block_(block), checkpoints_(checks), stop_callback_(callback) { + initialize_context(); } void validate_block::initialize_context() { - const auto bip30_exception_height1 = testnet_ ? - testnet_bip30_exception_height1 : - mainnet_bip30_exception_height1; + activations_ = chain::get_script_context(); +} - const auto bip30_exception_height2 = testnet_ ? - testnet_bip30_exception_height2 : - mainnet_bip30_exception_height2; +// initialize_context must be called first (to set activations_). +bool validate_block::is_active(script_context flag) const +{ + return script::is_active(activations_, flag); +} - const auto bip16_activation_height = testnet_ ? - testnet_bip16_activation_height : - mainnet_bip16_activation_height; +// validate_version must be called first (to set minimum_version_). +bool validate_block::is_valid_version() const +{ + return current_block_.header.version >= chain::block_version_min && + current_block_.header.version < chain::block_version_max; +} - const auto active = testnet_ ? testnet_active : mainnet_active; - const auto enforce = testnet_ ? testnet_enforce : mainnet_enforce; - const auto sample = testnet_ ? testnet_sample : mainnet_sample; +bool validate_block::stopped() const +{ + return stop_callback_(); +} - // Continue even if this is too small or empty (fast and simpler). - const auto versions = preceding_block_versions(sample); +code validate_block::check_coinbase(const chain::header& prev_header, bool check_genesis_tx) const +{ + const auto& header = current_block_.header; + const auto& transactions = current_block_.transactions; + + const auto is_block_version_dpos = header.is_proof_of_dpos(); + const auto is_begin_of_epoch = consensus::witness::is_begin_of_epoch(height_); + + uint64_t coinbase_count = 0, coinstake_count = 0; + for (uint64_t index = 0; index < transactions.size(); ++index) { + RETURN_IF_STOPPED(); + + auto& tx = transactions[index]; - const auto ge_4 = [](uint8_t version) { return version >= version_4; }; - const auto ge_3 = [](uint8_t version) { return version >= version_3; }; - const auto ge_2 = [](uint8_t version) { return version >= version_2; }; + if (tx.is_coinstake()) { + if (!header.is_proof_of_stake() || index != 1) { + log::error(LOG_BLOCKCHAIN) << "Illegal coinstake in block " + << encode_hash(header.hash()) << ", with " + << transactions.size() << " txs, current tx: " << tx.to_string(1); + return error::illegal_coinstake; + } + + if (coinstake_count > 1) { + log::error(LOG_BLOCKCHAIN) << "Extra coinstake in block " + << encode_hash(header.hash()) << ", with " + << transactions.size() << " txs, current tx: " << tx.to_string(1); + return error::extra_coinstakes; + } + + ++coinstake_count; + continue; + } - const auto count_4 = std::count_if(versions.begin(), versions.end(), ge_4); - const auto count_3 = std::count_if(versions.begin(), versions.end(), ge_3); - const auto count_2 = std::count_if(versions.begin(), versions.end(), ge_2); + if (!tx.is_coinbase()) { + break; + } - const auto activate = [active](size_t count) { return count >= active; }; - const auto enforced = [enforce](size_t count) { return count >= enforce; }; + auto has_vote_result = coinbase_count == 0 && is_begin_of_epoch; + if ((!has_vote_result && tx.outputs.size() != 1) + || (has_vote_result && tx.outputs.size() != 2) + || (tx.outputs.size() < 1) + || (tx.outputs[0].is_etp() == false)) { + return error::first_not_coinbase; + } - // version 4/3/2 enforced based on 95% of preceding 1000 mainnet blocks. - if (enforced(count_4)) - minimum_version_ = version_4; - else if (enforced(count_3)) - minimum_version_ = version_3; - else if (enforced(count_2)) - minimum_version_ = version_2; - else - minimum_version_ = version_1; + if (is_active(script_context::bip34_enabled)) { + //check pos genesis tx + if (check_genesis_tx && index == 2) { + if (!tx.is_pos_genesis_tx(testnet_)) { + return error::check_pos_genesis_error; + } + } + // Enforce rule that the coinbase starts with serialized height. + else if (!is_valid_coinbase_height(height_, current_block_, index)) { + return error::coinbase_height_mismatch; + } + } + else { + const auto& coinbase_script = tx.inputs[0].script; + const auto coinbase_size = coinbase_script.serialized_size(false); + const auto is_dpos_coinbase = is_block_version_dpos && index == 0; + constexpr uint32_t max_coinbase_size = 100; + constexpr uint32_t max_dpos_coinbase_size = 200; + if ((coinbase_size < 2) || + (!is_dpos_coinbase && coinbase_size > max_coinbase_size) || + (is_dpos_coinbase && coinbase_size > max_dpos_coinbase_size)) { + return error::invalid_coinbase_script_size; + } + } - // good for all, no votes is needed. - activations_ |= script_context::attenuation_enabled; + ++coinbase_count; + } - // bip65/112 is activated based on 75% of preceding 1000 mainnet blocks. - if (activate(count_4)) { - activations_ |= script_context::bip65_enabled; - activations_ |= script_context::bip112_enabled; + if (coinbase_count == 0) { + return error::first_not_coinbase; } - // bip66 is activated based on 75% of preceding 1000 mainnet blocks. - if (activate(count_3)) { - activations_ |= script_context::bip66_enabled; + if (header.is_proof_of_stake() && coinstake_count == 0) { + return error::miss_coinstake; } - // bip34 is activated based on 75% of preceding 1000 mainnet blocks. - if (activate(count_2)) { - activations_ |= script_context::bip34_enabled; + auto iter_start = transactions.begin() + coinbase_count + coinstake_count; + for (auto it = iter_start; it != transactions.end(); ++it) { + RETURN_IF_STOPPED(); + + if (it->is_coinbase() || it->is_coinstake()) { + log::error(LOG_BLOCKCHAIN) << "Illegal coinbase or coinstake in block: " + << encode_hash(header.hash()) << ", with " << transactions.size() << " txs."; + return error::extra_coinbases; + } } - // bip30 applies to all but two mainnet blocks that violate the rule. - if (height_ != bip30_exception_height1 && - height_ != bip30_exception_height2) { - activations_ |= script_context::bip30_enabled; + if (!consensus::witness::is_dpos_enabled()) { + return error::success; } - // bip16 was activated with a one-time test on mainnet/testnet (~55% rule). - if (height_ >= bip16_activation_height) { - activations_ |= script_context::bip16_enabled; + consensus::witness_with_validate_block_context context(consensus::witness::get(), this); + + if (is_begin_of_epoch) { + RETURN_IF_STOPPED(); + log::debug(LOG_BLOCKCHAIN) << "begin to update_witness_list at height " << height_; + if (!consensus::witness::get().update_witness_list(current_block_)) { + log::debug(LOG_BLOCKCHAIN) + << "update witness list failed at " << height_ + << " block hash: " << encode_hash(header.hash()); + return error::witness_update_error; + } + log::debug(LOG_BLOCKCHAIN) << "end to update_witness_list at height " << height_; } -} -// initialize_context must be called first (to set activations_). -bool validate_block::is_active(script_context flag) const -{ - if (!script::is_active(activations_, flag)) - return false; + if (is_block_version_dpos) { + RETURN_IF_STOPPED(); + const auto& coinbase_script = transactions.front().inputs.front().script; + auto coinbase_input_ops = coinbase_script.operations; + + if (coinbase_input_ops.size() == 1) { + const auto& op_data = coinbase_input_ops.back().data; + chain::script eval_script; + if (eval_script.from_data(op_data, false, script::parse_mode::strict)) { + coinbase_input_ops = eval_script.operations; + } + } - const auto version = current_block_.header.version; - return - (flag == script_context::attenuation_enabled) || - (flag == script_context::bip112_enabled && version >= version_4) || - (flag == script_context::bip65_enabled && version >= version_4) || - (flag == script_context::bip66_enabled && version >= version_3) || - (flag == script_context::bip34_enabled && version >= version_2); -} + if (coinbase_input_ops.size() != 3) { +#ifdef PRIVATE_CHAIN + log::error(LOG_BLOCKCHAIN) + << "verify witness sign script failed, coinbase script is " + << coinbase_script.to_string(chain::get_script_context()); +#endif + return error::witness_sign_invalid; + } -// validate_version must be called first (to set minimum_version_). -bool validate_block::is_valid_version() const -{ - return current_block_.header.version >= minimum_version_; -} + auto endorse = operation::factory_from_data(coinbase_input_ops[1].to_data()).data; + auto pubkey = operation::factory_from_data(coinbase_input_ops[2].to_data()).data; + if (!consensus::witness::verify_sign(endorse, pubkey, header)) { + return error::witness_sign_invalid; + } + if (!consensus::witness::get().verify_signer(pubkey, header.number)) { + return error::witness_mismatch; + } + } -bool validate_block::stopped() const -{ - return stop_callback_(); + return error::success; } code validate_block::check_block(blockchain::block_chain_impl& chain) const @@ -217,18 +251,38 @@ code validate_block::check_block(blockchain::block_chain_impl& chain) const const auto& header = current_block_.header; - if (!is_valid_proof_of_work(header)) - return error::proof_of_work; + if (header.is_proof_of_stake()) { + if (!verify_stake(current_block_)) { + return error::proof_of_stake; + } + } + else if (header.is_proof_of_work()) { + if (!check_work(current_block_)) { + return error::proof_of_work; + } + } + else if (header.is_proof_of_dpos()) { + if (!check_work(current_block_)) { + return error::proof_of_work; + } + if (!current_block_.can_use_dpos_consensus()) { + return error::block_version_not_match; + } + } + else { + return error::old_version_block; + } RETURN_IF_STOPPED(); +#ifndef PRIVATE_CHAIN //TO.FIX.CHENHAO.Reject - if (current_block_.header.number == bc::consensus::future_blocktime_fork_height) { + if (current_block_.header.number == future_blocktime_fork_height) { // 校验未来区块时间攻击分叉点 bc::config::checkpoint::list blocktime_checkpoints; blocktime_checkpoints.push_back({ "ed11a074ce80cbf82b5724bea0d74319dc6f180198fa1bbfb562bcbd50089e63", - bc::consensus::future_blocktime_fork_height + future_blocktime_fork_height }); const auto block_hash = header.hash(); @@ -236,43 +290,40 @@ code validate_block::check_block(blockchain::block_chain_impl& chain) const return error::checkpoints_failed; } } +#endif - if (current_block_.header.number >= bc::consensus::future_blocktime_fork_height) { + chain::header prev_header = fetch_block(height_ - 1); + if (current_block_.header.number >= future_blocktime_fork_height) { // 未来区块时间攻击分叉,执行新规则检查 - if (!is_valid_time_stamp_new(header.timestamp)) + if (current_block_.header.number >= pos_enabled_height) { + if (!check_time_stamp(header.timestamp, asio::seconds(block_timespan_window))) { + return error::futuristic_timestamp; + } + } + else if (!check_time_stamp(header.timestamp, time_stamp_window_future_blocktime_fix)) { return error::futuristic_timestamp; + } + // 过去区块时间检查 - chain::header prev_header = fetch_block(height_ - 1); - if (current_block_.header.timestamp < prev_header.timestamp) + if (current_block_.header.timestamp < prev_header.timestamp) { return error::timestamp_too_early; + } } else { - if (!is_valid_time_stamp(header.timestamp)) + if (!check_time_stamp(header.timestamp, time_stamp_window)) { return error::futuristic_timestamp; + } } RETURN_IF_STOPPED(); - unsigned int coinbase_count = 0; - for (auto i : transactions) { - if (i.is_coinbase()) { - if (i.outputs.size() > 1 || i.outputs[0].is_etp() == false) { - return error::first_not_coinbase; - } - ++coinbase_count; - } + bool check_genesis_tx = header.is_proof_of_stake() && !chain.pos_exist_before(header.number); + auto ec = check_coinbase(prev_header, check_genesis_tx); + if (ec) { + return ec; } - if (coinbase_count == 0) { - return error::first_not_coinbase; - } - - for (auto it = transactions.begin() + coinbase_count; it != transactions.end(); ++it) - { - RETURN_IF_STOPPED(); - if (it->is_coinbase()) - return error::extra_coinbases; - } + RETURN_IF_STOPPED(); std::set assets; std::set asset_certs; @@ -285,12 +336,12 @@ code validate_block::check_block(blockchain::block_chain_impl& chain) const RETURN_IF_STOPPED(); const auto validate_tx = std::make_shared(chain, tx, *this); - auto ec = validate_tx->check_transaction(); + ec = validate_tx->check_transaction(); if (!ec) { ec = validate_tx->check_transaction_connect_input(header.number); } - for (size_t i = 0; (!ec) && (i < tx.outputs.size()); ++i) { + for (uint64_t i = 0; (!ec) && (i < tx.outputs.size()); ++i) { const auto& output = tx.outputs[i]; if (output.is_asset_issue()) { auto r = assets.insert(output.get_asset_symbol()); @@ -445,10 +496,10 @@ inline uint8_t decode_op_n(opcode code) } // TODO: move to bc::chain::operation::stack. -inline size_t count_script_sigops(const operation::stack& operations, +inline uint64_t count_script_sigops(const operation::stack& operations, bool accurate) { - size_t total_sigs = 0; + uint64_t total_sigs = 0; opcode last_opcode = opcode::bad_operation; for (const auto& op : operations) { @@ -473,9 +524,9 @@ inline size_t count_script_sigops(const operation::stack& operations, } // TODO: move to bc::chain::transaction. -size_t validate_block::legacy_sigops_count(const transaction& tx) +uint64_t validate_block::legacy_sigops_count(const transaction& tx) { - size_t total_sigs = 0; + uint64_t total_sigs = 0; for (const auto& input : tx.inputs) { const auto& operations = input.script.operations; @@ -491,9 +542,9 @@ size_t validate_block::legacy_sigops_count(const transaction& tx) return total_sigs; } -size_t validate_block::legacy_sigops_count(const transaction::list& txs) +uint64_t validate_block::legacy_sigops_count(const transaction::list& txs) { - size_t total_sigs = 0; + uint64_t total_sigs = 0; for (const auto& tx : txs) total_sigs += legacy_sigops_count(tx); @@ -504,33 +555,11 @@ size_t validate_block::legacy_sigops_count(const transaction::list& txs) code validate_block::accept_block() const { const auto& header = current_block_.header; - if (header.bits != work_required(testnet_)) + if (!header.is_proof_of_dpos() && header.bits != work_required(testnet_)) return error::incorrect_proof_of_work; RETURN_IF_STOPPED(); - //CHENHAO. future blocktime attack -#if 0 - if (header.number >= bc::consensus::future_blocktime_fork_height) { - - } else { - if (header.timestamp <= median_time_past()) - return error::timestamp_too_early; - } -#endif - - - RETURN_IF_STOPPED(); - - // Txs should be final when included in a block. - for (const auto& tx : current_block_.transactions) - { - if (!tx.is_final(height_, header.timestamp)) - return error::non_final_transaction; - - RETURN_IF_STOPPED(); - } - // Ensure that the block passes checkpoints. // This is both DOS protection and performance optimization for sync. const auto block_hash = header.hash(); @@ -543,51 +572,54 @@ code validate_block::accept_block() const if (!is_valid_version()) return error::old_version_block; - RETURN_IF_STOPPED(); - - // Enforce rule that the coinbase starts with serialized height. - if (is_active(script_context::bip34_enabled) && - !is_valid_coinbase_height(height_, current_block_)) - return error::coinbase_height_mismatch; - - if (is_active(script_context::bip112_enabled)) { - for (const auto& tx : current_block_.transactions) { - if (tx.is_locked(height_, median_time_past())) { - return error::sequence_locked; - } - - RETURN_IF_STOPPED(); - } - } return error::success; } u256 validate_block::work_required(bool is_testnet) const { + bool is_pos = current_block_.is_proof_of_stake(); chain::header prev_header = fetch_block(height_ - 1); - return HeaderAux::calculateDifficulty(const_cast(current_block_.header), prev_header); + header::ptr last_header = get_last_block_header(prev_header, is_pos); + header::ptr llast_header; + if (last_header && last_header->number > 2) { + auto height = last_header->number - 1; + chain::header prev_last_header = fetch_block(height); + llast_header = get_last_block_header(prev_last_header, is_pos); + } + + return HeaderAux::calculate_difficulty( + current_block_.header, last_header, llast_header, is_pos); } -bool validate_block::is_valid_coinbase_height(size_t height, const block& block) +bool validate_block::is_valid_coinbase_height(uint64_t height, const block& block, uint64_t index) { // There must be a transaction with an input. - if (block.transactions.empty() || - block.transactions.front().inputs.empty()) + if (block.transactions.size() < index + 1 || block.transactions[index].inputs.empty()) { return false; + } + + const auto& actual_script = block.transactions[index].inputs.front().script; + + const script_number number(height); + const auto height_data = number.data(); + + const auto is_dpos_coinbase = block.header.is_proof_of_dpos() && index == 0; + if (!is_dpos_coinbase) { + // Get the serialized coinbase input script as a byte vector. + const auto actual = actual_script.to_data(false); - // Get the serialized coinbase input script as a byte vector. - const auto& actual_tx = block.transactions.front(); - const auto& actual_script = actual_tx.inputs.front().script; - const auto actual = actual_script.to_data(false); + // Create the expected script as a byte vector. + script expected_script; + expected_script.operations.push_back({ opcode::special, height_data }); + const auto expected = expected_script.to_data(false); - // Create the expected script as a byte vector. - script expected_script; - script_number number(height); - expected_script.operations.push_back({ opcode::special, number.data() }); - const auto expected = expected_script.to_data(false); + // Require that the coinbase script match the expected coinbase script. + return std::equal(expected.begin(), expected.end(), actual.begin()); + } - // Require that the coinbase script match the expected coinbase script. - return std::equal(expected.begin(), expected.end(), actual.begin()); + // Require that the height data is same + const auto actual_data = operation::factory_from_data(actual_script.operations.front().to_data()).data; + return std::equal(height_data.begin(), height_data.end(), actual_data.begin()); } code validate_block::connect_block(hash_digest& err_tx, blockchain::block_chain_impl& chain) const @@ -612,14 +644,22 @@ code validate_block::connect_block(hash_digest& err_tx, blockchain::block_chain_ } uint64_t fees = 0; - size_t total_sigops = 0; + uint64_t total_sigops = 0; const auto count = transactions.size(); - size_t coinage_reward_coinbase_index = 1; - size_t get_coinage_reward_tx_count = 0; + uint32_t version = current_block_.header.version; + bool is_pos = current_block_.header.is_proof_of_stake(); + uint64_t coinage_reward_coinbase_index = !is_pos ? 1 : 2; + uint64_t get_coinage_reward_tx_count = 0; + + if (!check_block_signature(chain)) { + return error::cointstake_signature_invalid; + } ////////////// TODO: parallelize. ////////////// - for (size_t tx_index = 0; tx_index < count; ++tx_index) + for (uint64_t tx_index = 0; tx_index < count; ++tx_index) { + auto is_coinstake = false; + uint64_t value_in = 0; const auto& tx = transactions[tx_index]; @@ -634,6 +674,12 @@ code validate_block::connect_block(hash_digest& err_tx, blockchain::block_chain_ if (tx.is_coinbase()) continue; + if (version == block_version_pos){ + if (tx_index == 1 && tx.is_coinstake()){ + is_coinstake = true; + } + } + for (auto& output : transactions[tx_index].outputs) { if (chain::operation::is_pay_key_hash_with_lock_height_pattern(output.script.operations)) { @@ -657,14 +703,28 @@ code validate_block::connect_block(hash_digest& err_tx, blockchain::block_chain_ RETURN_IF_STOPPED(); - if (!validate_transaction::tally_fees(chain, tx, value_in, fees)) + if (!validate_transaction::tally_fees(chain, tx, value_in, fees, is_coinstake)) { err_tx = tx.hash(); return error::fees_out_of_range; } } - if (get_coinage_reward_tx_count != coinage_reward_coinbase_index - 1) { + auto check_reward_count = [=] { + uint64_t coinage_reward_count = BC_MAX_SIZE; + switch (version){ + case block_version_pow: + case block_version_dpos: + coinage_reward_count = coinage_reward_coinbase_index - 1; + break; + case block_version_pos: + coinage_reward_count = coinage_reward_coinbase_index - 2; + break; + } + return coinage_reward_count == get_coinage_reward_tx_count; + }; + + if (!check_reward_count()) { return error::invalid_coinage_reward_coinbase; } @@ -672,10 +732,29 @@ code validate_block::connect_block(hash_digest& err_tx, blockchain::block_chain_ const auto& coinbase = transactions.front(); const auto reward = coinbase.total_output_value(); - const auto value = consensus::miner::calculate_block_subsidy(height_, testnet_) + fees; + const auto value = consensus::miner::calculate_block_subsidy(height_, testnet_, version) + fees; return reward > value ? error::coinbase_too_large : error::success; } +bool validate_block::check_block_signature(blockchain::block_chain_impl& chain) const +{ + if (!current_block_.header.is_proof_of_stake()) { + return true; + } + + const auto& blocksig = current_block_.blocksig; + if (blocksig.empty() || !is_coin_stake(current_block_)){ + return false; + } + + BITCOIN_ASSERT(current_block_.transactions.size() > 1); + const auto & coinstake_tx = current_block_.transactions[1]; + const auto & head_hash = current_block_.header.hash(); + const data_chunk& data = coinstake_tx.inputs[0].script.operations.back().data; + + return verify_signature(data, head_hash, blocksig); +} + bool validate_block::is_spent_duplicate(const transaction& tx) const { const auto tx_hash = tx.hash(); @@ -697,12 +776,12 @@ bool validate_block::is_spent_duplicate(const transaction& tx) const } bool validate_block::validate_inputs(const transaction& tx, - size_t index_in_parent, uint64_t& value_in, size_t& total_sigops) const + uint64_t index_in_parent, uint64_t& value_in, uint64_t& total_sigops) const { BITCOIN_ASSERT(!tx.is_coinbase()); ////////////// TODO: parallelize. ////////////// - for (size_t input_index = 0; input_index < tx.inputs.size(); ++input_index) + for (uint64_t input_index = 0; input_index < tx.inputs.size(); ++input_index) if (!connect_input(index_in_parent, tx, input_index, value_in, total_sigops)) { @@ -715,14 +794,14 @@ bool validate_block::validate_inputs(const transaction& tx, return true; } -bool validate_block::script_hash_signature_operations_count(size_t& out_count, +bool validate_block::script_hash_signature_operations_count(uint64_t& out_count, const script& output_script, const script& input_script) { using namespace chain; constexpr auto strict = script::parse_mode::strict; if (input_script.operations.empty() || - output_script.pattern() != script_pattern::pay_script_hash) + output_script.pattern() != chain::script_pattern::pay_script_hash) { out_count = 0; return true; @@ -740,19 +819,25 @@ bool validate_block::script_hash_signature_operations_count(size_t& out_count, } bool validate_block::get_transaction(const hash_digest& tx_hash, - chain::transaction& prev_tx, size_t& prev_height) const + chain::transaction& prev_tx, uint64_t& prev_height) const { return fetch_transaction(prev_tx, prev_height, tx_hash); } -bool validate_block::connect_input(size_t index_in_parent, - const transaction& current_tx, size_t input_index, uint64_t& value_in, - size_t& total_sigops) const +bool validate_block::get_header(chain::header& out_header, uint64_t height) const +{ + out_header = fetch_block(height); + return true; +} + +bool validate_block::connect_input(uint64_t index_in_parent, + const transaction& current_tx, uint64_t input_index, uint64_t& value_in, + uint64_t& total_sigops) const { BITCOIN_ASSERT(input_index < current_tx.inputs.size()); // Lookup previous output - size_t previous_height; + uint64_t previous_height; transaction previous_tx; const auto& input = current_tx.inputs[input_index]; const auto& previous_output = input.previous_output; @@ -770,7 +855,7 @@ bool validate_block::connect_input(size_t index_in_parent, const auto& previous_tx_out = previous_tx.outputs[previous_output.index]; // Signature operations count if script_hash payment type. - size_t count; + uint64_t count; if (!script_hash_signature_operations_count(count, previous_tx_out.script, input.script)) { @@ -789,26 +874,7 @@ bool validate_block::connect_input(size_t index_in_parent, const auto output_value = previous_tx_out.value; if (output_value > max_money()) { - log::warning(LOG_BLOCKCHAIN) << "Output money exceeds 21 million."; - return false; - } - - // Check coinbase maturity has been reached - if (previous_tx.is_coinbase()) - { - BITCOIN_ASSERT(previous_height <= height_); - const auto height_difference = height_ - previous_height; - if (height_difference < coinbase_maturity) - { - log::warning(LOG_BLOCKCHAIN) << "Immature coinbase spend attempt."; - return false; - } - } - - if (!validate_transaction::check_consensus(previous_tx_out.script, - current_tx, input_index, activations_)) - { - log::warning(LOG_BLOCKCHAIN) << "Input script invalid consensus."; + log::warning(LOG_BLOCKCHAIN) << "Input money exceeds 21 million."; return false; } diff --git a/src/lib/blockchain/validate_block_impl.cpp b/src/lib/blockchain/validate_block_impl.cpp index e710742dc..1686e71d9 100644 --- a/src/lib/blockchain/validate_block_impl.cpp +++ b/src/lib/blockchain/validate_block_impl.cpp @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -32,11 +31,11 @@ namespace libbitcoin { namespace blockchain { // Value used to define median time past. -static constexpr size_t median_time_past_blocks = 11; +static constexpr uint64_t median_time_past_blocks = 11; -validate_block_impl::validate_block_impl(simple_chain& chain, - size_t fork_index, const block_detail::list& orphan_chain, - size_t orphan_index, size_t height, const chain::block& block, +validate_block_impl::validate_block_impl(block_chain_impl& chain, + uint64_t fork_index, const block_detail::list& orphan_chain, + uint64_t orphan_index, uint64_t height, const chain::block& block, bool testnet, const config::checkpoint::list& checks, stopped_callback stopped) : validate_block(height, block, testnet, checks, stopped), @@ -48,16 +47,80 @@ validate_block_impl::validate_block_impl(simple_chain& chain, { } -bool validate_block_impl::is_valid_proof_of_work(const chain::header& header) const +bool validate_block_impl::check_work(const chain::block& block) const { - chain::header parent_header; - if (orphan_index_ != 0) { - parent_header = orphan_chain_[orphan_index_ - 1]->actual()->header; + chain::header parent_header = fetch_block(block.header.number - 1); + + if (block.is_proof_of_dpos()) { + return block.header.bits == parent_header.bits; } - else { - static_cast(chain_).get_header(parent_header, header.number - 1); + + chain::header::ptr last_header = get_last_block_header(parent_header, block.is_proof_of_stake()); + BITCOIN_ASSERT(nullptr != last_header); + + return MinerAux::verify_work(block.header, last_header); +} + +bool validate_block_impl::verify_stake(const chain::block& block) const +{ + // check stake txs + const auto& txs = block.transactions; + if(!is_coin_stake(block)){ + log::error(LOG_BLOCKCHAIN) + << "Failed to check stake, invalid coinstake. height: " + << block.header.number << ", " << txs.size() << " txs."; + return false; + } + + // check stake + const auto& coinstake = txs[1]; + const auto& stake_output_point = coinstake.inputs[0].previous_output; + bc::wallet::payment_address pay_address(coinstake.inputs[0].get_script_address()); + + auto height = block.header.number - 1; + if (!chain_.check_pos_capability(height, pay_address, false)) { + log::error(LOG_BLOCKCHAIN) + << "Failed to check pos capability. height: " + << block.header.number << ", address=" << pay_address.encoded() + << ", "<< txs.size() << " txs."; + return false; + } + + uint64_t utxo_height = 0; + chain::transaction utxo_tx; + if (!fetch_transaction(utxo_tx, utxo_height, stake_output_point.hash)) { + log::error(LOG_BLOCKCHAIN) + << "Failed to check stake, can not get stake transaction " + << encode_hash(stake_output_point.hash); + return false; + } + + BITCOIN_ASSERT(stake_output_point.index < utxo_tx.outputs.size()); + + if (!chain_.check_pos_utxo_capability( + block.header.number, utxo_tx, stake_output_point.index, utxo_height, true, this)) { + log::error(LOG_BLOCKCHAIN) + << "Failed to check utxo capability, hash=" << encode_hash(stake_output_point.hash) + << ", index=" << stake_output_point.index + << ", utxo height=" << utxo_height; + + return false; } - return MinerAux::verifySeal(const_cast(header), parent_header); + + auto stake_output = utxo_tx.outputs.at(stake_output_point.index); + chain::output_info stake_info = {stake_output, stake_output_point, utxo_height}; + + return MinerAux::verify_stake(block.header, stake_info); +} + +bool validate_block_impl::is_coin_stake(const chain::block& block) const +{ + const auto& txs = block.transactions; + if (txs.size() < 2 || !txs[0].is_coinbase() || !txs[1].is_coinstake()) { + return false; + } + + return true; } u256 validate_block_impl::previous_block_bits() const @@ -67,7 +130,7 @@ u256 validate_block_impl::previous_block_bits() const } validate_block::versions validate_block_impl::preceding_block_versions( - size_t maximum) const + uint64_t maximum) const { // 1000 previous versions maximum sample. // 950 previous versions minimum required for enforcement. @@ -76,7 +139,7 @@ validate_block::versions validate_block_impl::preceding_block_versions( // Read block (top - 1) through (top - 1000) and return version vector. versions result; - for (size_t index = 0; index < size; ++index) + for (uint64_t index = 0; index < size; ++index) { const auto version = fetch_block(height_ - index - 1).version; @@ -89,7 +152,7 @@ validate_block::versions validate_block_impl::preceding_block_versions( return result; } -uint64_t validate_block_impl::actual_time_span(size_t interval) const +uint64_t validate_block_impl::actual_time_span(uint64_t interval) const { BITCOIN_ASSERT(height_ > 0 && height_ >= interval); @@ -103,7 +166,7 @@ uint64_t validate_block_impl::median_time_past() const const auto count = std::min(height_, median_time_past_blocks); std::vector times; - for (size_t i = 0; i < count; ++i) + for (uint64_t i = 0; i < count; ++i) times.push_back(fetch_block(height_ - i - 1).timestamp); // Sort and select middle (median) value from the array. @@ -111,7 +174,7 @@ uint64_t validate_block_impl::median_time_past() const return times.empty() ? 0 : times[times.size() / 2]; } -chain::header validate_block_impl::fetch_block(size_t fetch_height) const +chain::header validate_block_impl::fetch_block(uint64_t fetch_height) const { if (fetch_height > fork_index_) { const auto fetch_index = fetch_height - fork_index_ - 1; @@ -126,21 +189,40 @@ chain::header validate_block_impl::fetch_block(size_t fetch_height) const return out; } -bool tx_after_fork(size_t tx_height, size_t fork_index) +chain::header::ptr validate_block_impl::get_last_block_header(const chain::header& parent_header, bool is_staking) const +{ + uint64_t height = parent_header.number; + if (parent_header.is_proof_of_stake() == is_staking) { + // log::info(LOG_BLOCKCHAIN) << "validate_block_impl::get_last_block_header: prev: " + // << std::to_string(parent_header.number) << ", last: " << std::to_string(height); + return std::make_shared(parent_header); + } + + while ((is_staking && height > pos_enabled_height) || (!is_staking && height > 2)) { + chain::header prev_header = fetch_block(--height); + if (prev_header.is_proof_of_stake() == is_staking) { + // log::info(LOG_BLOCKCHAIN) << "validate_block_impl::get_last_block_header: prev: " + // << std::to_string(parent_header.number) << ", last: " << std::to_string(height); + return std::make_shared(prev_header); + } + } + + return nullptr; +} + +bool tx_after_fork(uint64_t tx_height, uint64_t fork_index) { return tx_height > fork_index; } bool validate_block_impl::transaction_exists(const hash_digest& tx_hash) const { - uint64_t out_height; + uint64_t tx_height; chain::transaction unused; - const auto result = chain_.get_transaction(unused, out_height, tx_hash); + const auto result = chain_.get_transaction(unused, tx_height, tx_hash); if (!result) return false; - BITCOIN_ASSERT(out_height <= max_size_t); - const auto tx_height = static_cast(out_height); return tx_height <= fork_index_; } @@ -157,13 +239,9 @@ bool validate_block_impl::is_output_spent( } bool validate_block_impl::fetch_transaction(chain::transaction& tx, - size_t& tx_height, const hash_digest& tx_hash) const + uint64_t& tx_height, const hash_digest& tx_hash) const { - uint64_t out_height; - const auto result = chain_.get_transaction(tx, out_height, tx_hash); - - BITCOIN_ASSERT(out_height <= max_size_t); - tx_height = static_cast(out_height); + const auto result = chain_.get_transaction(tx, tx_height, tx_hash); if (!result || tx_after_fork(tx_height, fork_index_)) { return fetch_orphan_transaction(tx, tx_height, tx_hash); @@ -173,9 +251,9 @@ bool validate_block_impl::fetch_transaction(chain::transaction& tx, } bool validate_block_impl::fetch_orphan_transaction(chain::transaction& tx, - size_t& tx_height, const hash_digest& tx_hash) const + uint64_t& tx_height, const hash_digest& tx_hash) const { - for (size_t orphan = 0; orphan <= orphan_index_; ++orphan) { + for (uint64_t orphan = 0; orphan <= orphan_index_; ++orphan) { const auto& orphan_block = orphan_chain_[orphan]->actual(); for (const auto& orphan_tx : orphan_block->transactions) { if (orphan_tx.hash() == tx_hash) { @@ -215,7 +293,7 @@ std::string validate_block_impl::get_did_from_address_consider_orphan_chain( // iter inputs for (const auto& input : orphan_tx.inputs) { - size_t previous_height; + uint64_t previous_height; transaction previous_tx; const auto& previous_output = input.previous_output; @@ -274,7 +352,7 @@ bool validate_block_impl::is_did_in_orphan_chain(const std::string& did) const { BITCOIN_ASSERT(!did.empty()); - for (size_t orphan = 0; orphan < orphan_index_; ++orphan) { + for (uint64_t orphan = 0; orphan < orphan_index_; ++orphan) { const auto& orphan_block = orphan_chain_[orphan]->actual(); for (const auto& orphan_tx : orphan_block->transactions) { for (auto& output : orphan_tx.outputs) { @@ -294,7 +372,7 @@ bool validate_block_impl::is_asset_in_orphan_chain(const std::string& symbol) co { BITCOIN_ASSERT(!symbol.empty()); - for (size_t orphan = 0; orphan < orphan_index_; ++orphan) { + for (uint64_t orphan = 0; orphan < orphan_index_; ++orphan) { const auto& orphan_block = orphan_chain_[orphan]->actual(); for (const auto& orphan_tx : orphan_block->transactions) { for (const auto& output : orphan_tx.outputs) { @@ -314,7 +392,7 @@ bool validate_block_impl::is_asset_cert_in_orphan_chain(const std::string& symbo { BITCOIN_ASSERT(!symbol.empty()); - for (size_t orphan = 0; orphan < orphan_index_; ++orphan) { + for (uint64_t orphan = 0; orphan < orphan_index_; ++orphan) { const auto& orphan_block = orphan_chain_[orphan]->actual(); for (const auto& orphan_tx : orphan_block->transactions) { for (const auto& output : orphan_tx.outputs) { @@ -333,7 +411,7 @@ bool validate_block_impl::is_asset_mit_in_orphan_chain(const std::string& symbol { BITCOIN_ASSERT(!symbol.empty()); - for (size_t orphan = 0; orphan < orphan_index_; ++orphan) { + for (uint64_t orphan = 0; orphan < orphan_index_; ++orphan) { const auto& orphan_block = orphan_chain_[orphan]->actual(); for (const auto& orphan_tx : orphan_block->transactions) { for (const auto& output : orphan_tx.outputs) { @@ -351,7 +429,7 @@ bool validate_block_impl::is_asset_mit_in_orphan_chain(const std::string& symbol bool validate_block_impl::is_output_spent( const chain::output_point& previous_output, - size_t index_in_parent, size_t input_index) const + uint64_t index_in_parent, uint64_t input_index) const { // Search for double spends. This must be done in both chain AND orphan. // Searching chain when this tx is an orphan is redundant but it does not @@ -367,20 +445,20 @@ bool validate_block_impl::is_output_spent( bool validate_block_impl::orphan_is_spent( const chain::output_point& previous_output, - size_t skip_tx, size_t skip_input) const + uint64_t skip_tx, uint64_t skip_input) const { - for (size_t orphan = 0; orphan <= orphan_index_; ++orphan) { + for (uint64_t orphan = 0; orphan <= orphan_index_; ++orphan) { const auto& orphan_block = orphan_chain_[orphan]->actual(); const auto& transactions = orphan_block->transactions; BITCOIN_ASSERT(!transactions.empty()); BITCOIN_ASSERT(transactions.front().is_coinbase()); - for (size_t tx_index = 0; tx_index < transactions.size(); ++tx_index) { + for (uint64_t tx_index = 0; tx_index < transactions.size(); ++tx_index) { // TODO: too deep, move this section to subfunction. const auto& orphan_tx = transactions[tx_index]; - for (size_t input_index = 0; input_index < orphan_tx.inputs.size(); ++input_index) { + for (uint64_t input_index = 0; input_index < orphan_tx.inputs.size(); ++input_index) { const auto& orphan_input = orphan_tx.inputs[input_index]; if (orphan == orphan_index_ && tx_index == skip_tx && input_index == skip_input) diff --git a/src/lib/blockchain/validate_transaction.cpp b/src/lib/blockchain/validate_transaction.cpp index faa800861..9ae43fb4c 100644 --- a/src/lib/blockchain/validate_transaction.cpp +++ b/src/lib/blockchain/validate_transaction.cpp @@ -20,6 +20,7 @@ */ #include #include +#include #include #include @@ -29,6 +30,7 @@ #include #include #include +#include #include #ifdef WITH_CONSENSUS @@ -48,7 +50,7 @@ static constexpr uint32_t max_transaction_size = 1000000; validate_transaction::validate_transaction(block_chain& chain, const chain::transaction& tx, const validate_block& validate_block) - : blockchain_(static_cast(chain)), + : blockchain_(static_cast(chain)), tx_(std::make_shared(tx)), pool_(nullptr), dispatch_(nullptr), @@ -59,7 +61,7 @@ validate_transaction::validate_transaction(block_chain& chain, validate_transaction::validate_transaction(block_chain& chain, const chain::transaction& tx, const transaction_pool& pool, dispatcher& dispatch) - : blockchain_(static_cast(chain)), + : blockchain_(static_cast(chain)), tx_(std::make_shared(tx)), pool_(&pool), dispatch_(&dispatch), @@ -68,6 +70,31 @@ validate_transaction::validate_transaction(block_chain& chain, { } +transaction& validate_transaction::get_tx() +{ + return *tx_; +} + +const transaction& validate_transaction::get_tx() const +{ + return *tx_; +} + +block_chain_impl& validate_transaction::get_blockchain() +{ + return blockchain_; +} + +const block_chain_impl& validate_transaction::get_blockchain() const +{ + return blockchain_; +} + +const validate_block* validate_transaction::get_validate_block() const +{ + return validate_block_; +} + void validate_transaction::start(validate_handler handler) { BITCOIN_ASSERT(tx_ && pool_ && dispatch_); @@ -76,7 +103,7 @@ void validate_transaction::start(validate_handler handler) const auto ec = basic_checks(); if (ec) { - if (ec == error::input_not_found) { + if (ec.value() == error::input_not_found) { handle_validate_(ec, tx_, {current_input_}); return; } @@ -127,7 +154,7 @@ bool validate_transaction::is_standard() const void validate_transaction::handle_duplicate_check( const code& ec) { - if (ec != (code)error::not_found) + if (ec.value() != error::not_found) { /////////////////////////////////////////////////////////////////////// // BUGBUG: overly restrictive, spent dups ok (BIP30). @@ -149,7 +176,7 @@ void validate_transaction::handle_duplicate_check( shared_from_this(), _1, _2)); } -void validate_transaction::reset(size_t last_height) +void validate_transaction::reset(uint64_t last_height) { // Used for checking coinbase maturity last_block_height_ = last_height; @@ -162,7 +189,7 @@ void validate_transaction::reset(size_t last_height) } void validate_transaction::set_last_height(const code& ec, - size_t last_height) + uint64_t last_height) { if (ec) { @@ -191,7 +218,7 @@ void validate_transaction::next_previous_transaction() } void validate_transaction::previous_tx_index(const code& ec, - size_t parent_height) + uint64_t parent_height) { if (ec) { @@ -221,7 +248,7 @@ bool validate_transaction::get_previous_tx(chain::transaction& prev_tx, } } else { - size_t temp_height = 0; + uint64_t temp_height = 0; if (validate_block_ && validate_block_->get_transaction(input.previous_output.hash, prev_tx, temp_height)) { prev_height = temp_height; @@ -247,13 +274,13 @@ void validate_transaction::search_pool_previous_tx() // parent_height ignored here as mempool transactions cannot be coinbase. BITCOIN_ASSERT(!previous_tx.is_coinbase()); - static constexpr size_t parent_height = 0; + static constexpr uint64_t parent_height = 0; handle_previous_tx(error::success, previous_tx, parent_height); unconfirmed_.push_back(current_input_); } void validate_transaction::handle_previous_tx(const code& ec, - const transaction& previous_tx, size_t parent_height) + const transaction& previous_tx, uint64_t parent_height) { if (ec) { log::debug(LOG_BLOCKCHAIN) << "handle_previous_tx failed: error: " @@ -287,7 +314,7 @@ void validate_transaction::handle_previous_tx(const code& ec, void validate_transaction::check_double_spend(const code& ec, const chain::input_point&) { - if (ec != (code)error::unspent_output) + if (ec.value() != error::unspent_output) { handle_validate_(error::double_spend, tx_, {}); return; @@ -308,7 +335,7 @@ void validate_transaction::check_double_spend(const code& ec, void validate_transaction::check_fees() const { code ec = check_tx_connect_input(); - if (ec != error::success) { + if (ec) { handle_validate_(ec, tx_, {}); return; } @@ -322,8 +349,9 @@ void validate_transaction::check_fees() const code validate_transaction::check_tx_connect_input() const { uint64_t fee = 0; + auto coin_stake = tx_->is_coinstake(); - if (!tally_fees(blockchain_, *tx_, value_in_, fee)) { + if (!tally_fees(blockchain_, *tx_, value_in_, fee, coin_stake)) { return error::fees_out_of_range; } @@ -373,7 +401,7 @@ static bool check_same(std::string& dest, const std::string& src) code validate_transaction::check_secondaryissue_transaction() const { const chain::transaction& tx = *tx_; - blockchain::block_chain_impl& blockchain = blockchain_; + block_chain_impl& blockchain = blockchain_; bool is_asset_secondaryissue{false}; for (auto& output : tx.outputs) { @@ -526,7 +554,7 @@ code validate_transaction::check_secondaryissue_transaction() const code validate_transaction::check_asset_issue_transaction() const { const chain::transaction& tx = *tx_; - blockchain::block_chain_impl& chain = blockchain_; + block_chain_impl& chain = blockchain_; bool is_asset_issue{false}; for (auto& output : tx.outputs) { @@ -676,7 +704,7 @@ code validate_transaction::check_asset_issue_transaction() const code validate_transaction::check_asset_cert_transaction() const { const chain::transaction& tx = *tx_; - blockchain::block_chain_impl& chain = blockchain_; + block_chain_impl& chain = blockchain_; bool is_cert{false}; for (auto& output : tx.outputs) { @@ -831,7 +859,7 @@ code validate_transaction::check_asset_cert_transaction() const code validate_transaction::check_asset_mit_transaction() const { const chain::transaction& tx = *tx_; - blockchain::block_chain_impl& chain = blockchain_; + block_chain_impl& chain = blockchain_; bool is_asset_mit{false}; for (auto& output : tx.outputs) { @@ -847,8 +875,8 @@ code validate_transaction::check_asset_mit_transaction() const std::string asset_symbol; std::string asset_address; - size_t num_mit_transfer = 0; - size_t num_mit_register = 0; + uint64_t num_mit_transfer = 0; + uint64_t num_mit_register = 0; for (auto& output : tx.outputs) { if (output.is_asset_mit_register()) { @@ -1029,7 +1057,7 @@ bool validate_transaction::check_address_registered_did(const std::string& addre code validate_transaction::check_did_transaction() const { const chain::transaction& tx = *tx_; - blockchain::block_chain_impl& chain = blockchain_; + block_chain_impl& chain = blockchain_; uint64_t fork_index = validate_block_ ? validate_block_->get_fork_index() : max_uint64; code ret = error::success; @@ -1038,15 +1066,15 @@ code validate_transaction::check_did_transaction() const for (const auto& output : tx.outputs) { - if ((ret = output.check_attachment_address(chain)) != error::success) + if ((ret = output.check_attachment_address(chain))) return ret; //to_did check(strong check) - if ((ret = check_attachment_to_did(output)) != error::success) + if ((ret = check_attachment_to_did(output))) return ret; //from_did (weak check) - if ((ret = connect_attachment_from_did(output)) != error::success) { + if ((ret = connect_attachment_from_did(output))) { return ret; } @@ -1121,7 +1149,7 @@ code validate_transaction::check_did_transaction() const bool validate_transaction::connect_did_input(const did& info) const { const chain::transaction& tx = *tx_; - blockchain::block_chain_impl& chain = blockchain_; + block_chain_impl& chain = blockchain_; if (info.get_status() == DID_TRANSFERABLE_TYPE && tx.inputs.size() != 2) { return false; @@ -1135,7 +1163,10 @@ bool validate_transaction::connect_did_input(const did& info) const chain::transaction prev_tx; uint64_t prev_height{0}; if (!get_previous_tx(prev_tx, prev_height, input)) { - log::debug(LOG_BLOCKCHAIN) << "connect_did_input: input not found: " + log::debug(LOG_BLOCKCHAIN) << "connect_did_input: " + << "symbol: " << detail_info.get_symbol() + << "address: " << detail_info.get_address() + << " input not found: " << encode_hash(input.previous_output.hash); return false; } @@ -1241,7 +1272,7 @@ code validate_transaction::connect_attachment_from_did(const output& output) con return error::did_address_not_match; } -code validate_transaction::check_transaction_connect_input(size_t last_height) +code validate_transaction::check_transaction_connect_input(uint64_t last_height) { if (last_height == 0 || tx_->is_coinbase()) { return error::success; @@ -1273,32 +1304,40 @@ code validate_transaction::check_transaction() const { code ret = error::success; - if ((ret = check_transaction_basic()) != error::success) { + if ((ret = check_transaction_basic())) { return ret; } - if ((ret = check_asset_issue_transaction()) != error::success) { + if ((ret = check_final_tx())) { + return ret; + } + + if ((ret = check_sequence_locks())) { + return ret; + } + + if ((ret = check_asset_issue_transaction())) { return ret; } if (tx_->version >= transaction_version::check_nova_feature) { - if ((ret = check_asset_cert_transaction()) != error::success) { + if ((ret = check_asset_cert_transaction())) { return ret; } - if ((ret = check_secondaryissue_transaction()) != error::success) { + if ((ret = check_secondaryissue_transaction())) { return ret; } - if ((ret = check_asset_mit_transaction()) != error::success) { + if ((ret = check_asset_mit_transaction())) { return ret; } - if ((ret = check_did_transaction()) != error::success) { + if ((ret = check_did_transaction())) { return ret; } - if ((ret = attenuation_model::check_model_param(*this)) != error::success) { + if ((ret = attenuation_model::check_model_param(*this))) { return ret; } } @@ -1306,10 +1345,114 @@ code validate_transaction::check_transaction() const return ret; } +uint64_t median_time_past(const uint64_t &height, const block_chain_impl& chain) +{ + const uint64_t median_time_past_blocks = 11; + // Read last 11 (or height if height < 11) block times into array. + const auto count = std::min(height, median_time_past_blocks); + + header header; + std::vector times; + for (uint64_t i = 1; i <= count; ++i) { + if (!chain.get_header(header, height - count + i)) { + return MAX_UINT64; + } + times.push_back(header.timestamp); + } + + // Sort and select middle (median) value from the array. + std::sort(times.begin(), times.end()); + return times.empty() ? 0 : times[times.size() / 2]; +} + +code validate_transaction::check_final_tx() const +{ + const chain::transaction& tx = *tx_; + if (tx.version < relative_locktime_min_version) { + return error::success; + } + + uint64_t height = 0; + uint64_t median_time_past_ = 0; + if (validate_block_) { + height = validate_block_->get_height(); + median_time_past_ = validate_block_->median_time_past(); + } + else { + block_chain_impl& chain = blockchain_; + chain.get_last_height(height); + median_time_past_ = median_time_past(height, chain); + ++height; // the next block's height + } + + return !tx_->is_final(height, median_time_past_) ? error::non_final_transaction : error::success; +} + +code validate_transaction::check_sequence_locks() const +{ + const chain::transaction& tx = *tx_; + if (tx.version < relative_locktime_min_version || tx_->is_coinbase()) { + return error::success; + } + + block_chain_impl& chain = blockchain_; + uint64_t last_height = 0; + uint64_t median_time_past_ = 0; + if (validate_block_) { + last_height = validate_block_->get_height(); + median_time_past_ = validate_block_->median_time_past(); + } + else { + chain.get_last_height(last_height); + median_time_past_ = median_time_past(last_height, chain); + ++last_height; // the next block's height + } + + uint64_t min_time = 0; + uint64_t min_height = 0; + + header header; + + for (auto &input : tx.inputs) { + const auto& nSequence = input.sequence; + if (nSequence & relative_locktime_disabled) { + continue; + } + + chain::transaction prev_tx; + uint64_t prev_height{0}; + if (!get_previous_tx(prev_tx, prev_height, input)) { + log::error(LOG_BLOCKCHAIN) << "check_sequence_locks: input not found: " + << encode_hash(input.previous_output.hash); + return error::input_not_found; + } + + if (nSequence & relative_locktime_time_locked) { + if (!chain.get_header(header, prev_height)) { + return error::not_found; + } + min_time = std::max(min_time, header.timestamp + uint64_t((nSequence & relative_locktime_mask) << relative_locktime_seconds_shift) - 1); + } + else { + min_height = std::max(min_height, prev_height + (nSequence & relative_locktime_mask) - 1); + } + } + + if (median_time_past_ <= min_time) { + return error::sequence_locked; + } + + if (last_height <= min_height) { + return error::sequence_locked; + } + + return error::success; +} + code validate_transaction::check_transaction_basic() const { const chain::transaction& tx = *tx_; - blockchain::block_chain_impl& chain = blockchain_; + block_chain_impl& chain = blockchain_; if (tx.version >= transaction_version::max_version) { return error::transaction_version_error; @@ -1327,7 +1470,7 @@ code validate_transaction::check_transaction_basic() const if (tx.version >= transaction_version::check_output_script) { for (auto& i : tx.outputs) { - if (i.script.pattern() == script_pattern::non_standard) { + if (i.script.pattern() == chain::script_pattern::non_standard) { return error::script_not_standard; } } @@ -1340,15 +1483,21 @@ code validate_transaction::check_transaction_basic() const return error::size_limits; // check double spend in inputs - std::set set; - for (auto& input : tx.inputs) { - auto tx_hash = libbitcoin::encode_hash(input.previous_output.hash); - auto value = tx_hash + ":" + std::to_string(input.previous_output.index); + using point_t = std::pair; + std::set set; + for (const auto& input : tx.inputs) { + point_t value(input.previous_output.hash, input.previous_output.index); if (set.count(value)) { return error::double_spend; } set.insert(value); + + if (!validate_block_) { + if (chain.get_spends_output(input.previous_output)) { + return error::duplicate_or_spent; + } + } } // Check for negative or overflow output values @@ -1397,14 +1546,7 @@ code validate_transaction::check_transaction_basic() const } } - if (tx.is_coinbase()) - { - const auto& coinbase_script = tx.inputs[0].script; - const auto coinbase_size = coinbase_script.serialized_size(false); - if (coinbase_size < 2 || coinbase_size > 100) - return error::invalid_coinbase_script_size; - } - else + if (!tx.is_coinbase()) { for (const auto& input : tx.inputs) { @@ -1423,13 +1565,18 @@ code validate_transaction::check_transaction_basic() const return error::input_not_found; } + // since unlock script for p2sh pattern are so complex, that is_sign_key_hash_with_lock_height_pattern cannot be well dealed. + if ( chain::operation::is_pay_script_hash_pattern( prev_tx.outputs[input.previous_output.index].script.operations ) ) + continue; + uint64_t lock_height = chain::operation::get_lock_height_from_sign_key_hash_with_lock_height(input.script.operations); - if (lock_height > current_blockheight - prev_output_blockheight) { + if (lock_height > chain.calc_number_of_blocks(prev_output_blockheight, current_blockheight, validate_block_)) { return error::invalid_input_script_lock_height; } } } + for (auto& output : tx.outputs) { if (chain::operation::is_pay_key_hash_with_lock_height_pattern(output.script.operations)) { @@ -1447,7 +1594,7 @@ code validate_transaction::check_transaction_basic() const // Validate script consensus conformance based on flags provided. bool validate_transaction::check_consensus(const script& prevout_script, - const transaction& current_tx, size_t input_index, uint32_t flags) + const transaction& current_tx, uint64_t input_index, uint32_t flags) { BITCOIN_ASSERT(input_index <= max_uint32); BITCOIN_ASSERT(input_index < current_tx.inputs.size()); @@ -1461,19 +1608,19 @@ bool validate_transaction::check_consensus(const script& prevout_script, // Convert native flags to libbitcoin-consensus flags. uint32_t consensus_flags = verify_flags_none; - if ((flags & script_context::bip16_enabled) != 0) + if ((flags & chain::script_context::bip16_enabled) != 0) consensus_flags |= verify_flags_p2sh; - if ((flags & script_context::bip65_enabled) != 0) + if ((flags & chain::script_context::bip65_enabled) != 0) consensus_flags |= verify_flags_checklocktimeverify; - if ((flags & script_context::bip66_enabled) != 0) + if ((flags & chain::script_context::bip66_enabled) != 0) consensus_flags |= verify_flags_dersig; - if ((flags & script_context::attenuation_enabled) != 0) + if ((flags & chain::script_context::attenuation_enabled) != 0) consensus_flags |= verify_flags_checkattenuationverify; - if ((flags & script_context::bip112_enabled) != 0) + if ((flags & chain::script_context::bip112_enabled) != 0) consensus_flags |= verify_flags_checksequenceverify; const auto result = verify_script(current_transaction.data(), @@ -1488,18 +1635,19 @@ bool validate_transaction::check_consensus(const script& prevout_script, const auto valid = script::verify(current_input_script, previous_output_script, current_tx, input_index32, flags); + const auto result = valid; #endif if (!valid) { log::warning(LOG_BLOCKCHAIN) << "Invalid transaction [" - << encode_hash(current_tx.hash()) << "]"; + << encode_hash(current_tx.hash()) << "] verify_result:" << result; } return valid; } -bool validate_transaction::connect_input( const transaction& previous_tx, size_t parent_height) +bool validate_transaction::connect_input( const transaction& previous_tx, uint64_t parent_height) { const auto& input = tx_->inputs[current_input_]; const auto& previous_outpoint = input.previous_output; @@ -1551,13 +1699,15 @@ bool validate_transaction::connect_input( const transaction& previous_tx, size_t } if (previous_tx.is_coinbase()) { - const auto height_difference = last_block_height_ - parent_height; - if (height_difference < coinbase_maturity) { + if (coinbase_maturity > blockchain_.calc_number_of_blocks(parent_height, last_block_height_, validate_block_)) { + log::debug(LOG_BLOCKCHAIN) + << "coinbase not maturity from " + << parent_height << " to " << last_block_height_; return false; } } - if (!check_consensus(previous_output.script, *tx_, current_input_, script_context::all_enabled)) { + if (!check_consensus(previous_output.script, *tx_, current_input_, chain::get_script_context())) { log::debug(LOG_BLOCKCHAIN) << "check_consensus failed"; return false; } @@ -1643,8 +1793,8 @@ bool validate_transaction::check_special_fees(bool is_testnet, const chain::tran return true; } -bool validate_transaction::tally_fees(blockchain::block_chain_impl& chain, - const transaction& tx, uint64_t value_in, uint64_t& total_fees) +bool validate_transaction::tally_fees(block_chain_impl& chain, + const transaction& tx, uint64_t value_in, uint64_t& total_fees, bool is_coinstake) { const auto value_out = tx.total_output_value(); @@ -1652,7 +1802,12 @@ bool validate_transaction::tally_fees(blockchain::block_chain_impl& chain, return false; const auto fee = value_in - value_out; - if (fee < min_tx_fee) { + + auto check_fee = [=]{ + return is_coinstake ? fee == 0 : fee >= min_tx_fee; + }; + + if( !check_fee()){ return false; } @@ -1788,7 +1943,7 @@ bool validate_transaction::check_asset_certs(const transaction& tx) const bool validate_transaction::check_asset_mit(const transaction& tx) const { - size_t num_mit = 0; + uint64_t num_mit = 0; for (const auto& output : tx.outputs) { if (output.is_asset_mit_transfer()) { if (++num_mit > 1) { @@ -1823,8 +1978,12 @@ bool validate_transaction::check_did_symbol_match(const transaction& tx) const return true; } -bool validate_transaction::is_nova_feature_activated(blockchain::block_chain_impl& chain) +bool validate_transaction::is_nova_feature_activated(block_chain_impl& chain) { +#ifdef PRIVATE_CHAIN + return true; +#endif + if (chain.chain_settings().use_testnet_rules) { return true; } diff --git a/src/lib/client/obelisk_client.cpp b/src/lib/client/obelisk_client.cpp index e873bcc2b..4f6024ded 100644 --- a/src/lib/client/obelisk_client.cpp +++ b/src/lib/client/obelisk_client.cpp @@ -67,7 +67,7 @@ bool obelisk_client::connect(const endpoint& address) for (auto attempt = 0; attempt < 1 + retries_; ++attempt) { - if (socket_.connect(host_address) == (code)error::success) + if (socket_.connect(host_address).value() == error::success) return true; // Arbitrary delay between connection attempts. diff --git a/src/lib/client/proxy.cpp b/src/lib/client/proxy.cpp index a60d1113b..f6b0caf93 100644 --- a/src/lib/client/proxy.cpp +++ b/src/lib/client/proxy.cpp @@ -219,7 +219,7 @@ void proxy::address_fetch_unspent_outputs(error_handler on_error, history_handler parse_history = [on_reply, satoshi, algorithm]( const chain::history::list& rows) { - chain::output_info::list unspent; + chain::output_point_info::list unspent; for(auto& row : rows) if (row.spend.hash == null_hash) unspent.push_back({row.output, row.value}); @@ -345,7 +345,7 @@ bool proxy::decode_transaction_index(reader& payload, bool proxy::decode_validate(reader& payload, validate_handler& handler) { - point::indexes unconfirmed; + chain::point::indexes unconfirmed; while (!payload.is_exhausted()) { diff --git a/src/lib/client/socket_stream.cpp b/src/lib/client/socket_stream.cpp index ce05c0f6b..ab9351991 100644 --- a/src/lib/client/socket_stream.cpp +++ b/src/lib/client/socket_stream.cpp @@ -51,7 +51,7 @@ bool socket_stream::read(stream& stream) data_stack data; zmq::message message; - if (socket_.receive(message) != (code)error::success) + if (socket_.receive(message).value() != error::success) return false; // Copy the message to a data stack. @@ -68,11 +68,11 @@ bool socket_stream::read(stream& stream) //// //// response_message message; //// -//// if (socket_.receive(message) != (code)error::success) +//// if (socket_.receive(message).value() != error::success) //// return false; //// //// auto response = message.get_response(); -//// return response && (stream->write(response) == (code)error::success); +//// return response && (stream->write(response).value() == error::success); ////} // TODO: optimize by passing the internal type of the message object. @@ -85,7 +85,7 @@ bool socket_stream::write(const data_stack& data) for (const auto& chunk: data) message.enqueue(chunk); - return socket_.send(message) == (code)error::success; + return socket_.send(message).value() == error::success; } ////bool socket_stream::write(const std::shared_ptr& request) @@ -95,7 +95,7 @@ bool socket_stream::write(const data_stack& data) //// //// request_message message; //// message.set_request(request); -//// return socket_.send(message) == (code)error::success; +//// return socket_.send(message).value() == error::success; ////} } // namespace client diff --git a/src/lib/consensus/clone/primitives/transaction.cpp b/src/lib/consensus/clone/primitives/transaction.cpp index aea96d8a1..c29f9afd8 100644 --- a/src/lib/consensus/clone/primitives/transaction.cpp +++ b/src/lib/consensus/clone/primitives/transaction.cpp @@ -3,15 +3,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include "primitives/transaction.h" - +#include "transaction.h" #include "hash.h" -#include "tinyformat.h" #include "utilstrencodings.h" +#include std::string COutPoint::ToString() const { - return strprintf("COutPoint(%s, %u)", hash.ToString().substr(0,10), n); + return (boost::format("COutPoint(%1%, %2%)") % hash.ToString().substr(0,10) % n).str(); } CTxIn::CTxIn(COutPoint prevoutIn, CScript scriptSigIn, uint32_t nSequenceIn) @@ -34,11 +33,11 @@ std::string CTxIn::ToString() const str += "CTxIn("; str += prevout.ToString(); if (prevout.IsNull()) - str += strprintf(", coinbase %s", HexStr(scriptSig)); + str += (boost::format(", coinbase %1%") % HexStr(scriptSig)).str(); else - str += strprintf(", scriptSig=%s", HexStr(scriptSig).substr(0, 24)); + str += (boost::format(", scriptSig=%1%") % HexStr(scriptSig).substr(0, 24)).str(); if (nSequence != std::numeric_limits::max()) - str += strprintf(", nSequence=%u", nSequence); + str += (boost::format(", nSequence=%1%") % nSequence).str(); str += ")"; return str; } @@ -56,7 +55,10 @@ uint256 CTxOut::GetHash() const std::string CTxOut::ToString() const { - return strprintf("CTxOut(nValue=%d.%08d, scriptPubKey=%s)", nValue / COIN, nValue % COIN, HexStr(scriptPubKey).substr(0, 30)); + auto val = nValue / COIN; + auto rem = nValue % COIN; + return (boost::format("CTxOut(nValue=%1%.%2$08d, scriptPubKey=%3%)") + % (nValue / COIN) % (nValue % COIN) % HexStr(scriptPubKey).substr(0, 30)).str(); } CMutableTransaction::CMutableTransaction() : nVersion(CTransaction::CURRENT_VERSION), nLockTime(0) {} @@ -128,12 +130,12 @@ unsigned int CTransaction::CalculateModifiedSize(unsigned int nTxSize) const std::string CTransaction::ToString() const { std::string str; - str += strprintf("CTransaction(hash=%s, ver=%d, vin.size=%u, vout.size=%u, nLockTime=%u)\n", - GetHash().ToString().substr(0,10), - nVersion, - vin.size(), - vout.size(), - nLockTime); + str += (boost::format("CTransaction(hash=%1%, ver=%2%, vin.size=%3%, vout.size=%4%, nLockTime=%5%)\n") + % GetHash().ToString().substr(0,10) + % nVersion + % vin.size() + % vout.size() + % nLockTime).str(); for (unsigned int i = 0; i < vin.size(); i++) str += " " + vin[i].ToString() + "\n"; for (unsigned int i = 0; i < vout.size(); i++) diff --git a/src/lib/consensus/clone/primitives/transaction.h b/src/lib/consensus/clone/primitives/transaction.h index 6784636bf..e18755c77 100644 --- a/src/lib/consensus/clone/primitives/transaction.h +++ b/src/lib/consensus/clone/primitives/transaction.h @@ -66,29 +66,6 @@ class CTxIn * disables nLockTime. */ static const uint32_t SEQUENCE_FINAL = 0xffffffff; - /* Below flags apply in the context of BIP 68*/ - /* If this flag set, input::sequence is NOT interpreted as a - * relative lock-time. */ - static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1 << 31); - - /* If input::sequence encodes a relative lock-time and this flag - * is set, the relative lock-time has units of 512 seconds, - * otherwise it specifies blocks with a granularity of 1. */ - static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22); - - /* If input::sequence encodes a relative lock-time, this mask is - * applied to extract that lock-time from the sequence field. */ - static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff; - - /* In order to use the same number of bits to encode roughly the - * same wall-clock duration, and because blocks are naturally - * limited to occur every 600s on average, the minimum granularity - * for time-based relative lock-time is fixed at 512 seconds. - * Converting from input::sequence to seconds is performed by - * multiplying by 512 = 2^9, or equivalently shifting up by - * 9 bits. */ - static const int SEQUENCE_LOCKTIME_GRANULARITY = 9; - CTxIn() { nSequence = SEQUENCE_FINAL; diff --git a/src/lib/consensus/clone/script/interpreter.cpp b/src/lib/consensus/clone/script/interpreter.cpp index dfe95db39..68d5fcdfc 100644 --- a/src/lib/consensus/clone/script/interpreter.cpp +++ b/src/lib/consensus/clone/script/interpreter.cpp @@ -12,6 +12,7 @@ #include "pubkey.h" #include "script/script.h" #include "uint256.h" +#include #include using namespace std; @@ -424,7 +425,7 @@ bool EvalScript(vector >& stack, const CScript& script, un // To provide for future soft-fork extensibility, if the // operand has the disabled lock-time flag set, // CHECKSEQUENCEVERIFY behaves as a NOP. - if ((nSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) != 0) + if ((nSequence & bc::relative_locktime_disabled) != 0) break; // Compare the specified sequence number with the input. @@ -1185,21 +1186,23 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether - // nLockTime < LOCKTIME_THRESHOLD. + // nLockTime < bc::locktime_threshold. // // We want to compare apples to apples, so fail the script // unless the type of nLockTime being tested is the same as // the nLockTime in the transaction. if (!( - (txTo->nLockTime < LOCKTIME_THRESHOLD && nLockTime < LOCKTIME_THRESHOLD) || - (txTo->nLockTime >= LOCKTIME_THRESHOLD && nLockTime >= LOCKTIME_THRESHOLD) - )) + (txTo->nLockTime < bc::locktime_threshold && nLockTime < bc::locktime_threshold) || + (txTo->nLockTime >= bc::locktime_threshold && nLockTime >= bc::locktime_threshold) + )) { return false; + } // Now that we know we're comparing apples-to-apples, the // comparison is a simple numeric one. - if (nLockTime > (int64_t)txTo->nLockTime) + if (nLockTime > (int64_t)txTo->nLockTime) { return false; + } // Finally the nLockTime feature can be disabled and thus // CHECKLOCKTIMEVERIFY bypassed if every txin has been @@ -1211,8 +1214,9 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con // prevent this condition. Alternatively we could test all // inputs, but testing just this input minimizes the data // required to prove correct CHECKLOCKTIMEVERIFY execution. - if (txTo->vin[nIn].IsFinal()) + if (txTo->vin[nIn].IsFinal()) { return false; + } return true; } @@ -1225,32 +1229,32 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con // Fail if the transaction's version number is not set high // enough to trigger BIP 68 rules. - if (static_cast(txTo->nVersion) < 2) + if (static_cast(txTo->nVersion) < bc::relative_locktime_min_version) return false; // Sequence numbers with their most significant bit set are not // consensus constrained. Testing that the transaction's sequence // number do not have this bit set prevents using this property // to get around a CHECKSEQUENCEVERIFY check. - if (txToSequence & CTxIn::SEQUENCE_LOCKTIME_DISABLE_FLAG) + if (txToSequence & bc::relative_locktime_disabled) return false; // Mask off any bits that do not have consensus-enforced meaning // before doing the integer comparisons - const uint32_t nLockTimeMask = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | CTxIn::SEQUENCE_LOCKTIME_MASK; + const uint32_t nLockTimeMask = bc::relative_locktime_time_locked | bc::relative_locktime_mask; const int64_t txToSequenceMasked = txToSequence & nLockTimeMask; const CScriptNum nSequenceMasked = nSequence & nLockTimeMask; // There are two kinds of nSequence: lock-by-blockheight // and lock-by-blocktime, distinguished by whether - // nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG. + // nSequenceMasked < bc::relative_locktime_time_locked. // // We want to compare apples to apples, so fail the script // unless the type of nSequenceMasked being tested is the same as // the nSequenceMasked in the transaction. if (!( - (txToSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked < CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) || - (txToSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG && nSequenceMasked >= CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG) + (txToSequenceMasked < bc::relative_locktime_time_locked && nSequenceMasked < bc::relative_locktime_time_locked) || + (txToSequenceMasked >= bc::relative_locktime_time_locked && nSequenceMasked >= bc::relative_locktime_time_locked) )) { return false; } diff --git a/src/lib/consensus/clone/script/script.cpp b/src/lib/consensus/clone/script/script.cpp index ecb098f06..96c1b7fc6 100644 --- a/src/lib/consensus/clone/script/script.cpp +++ b/src/lib/consensus/clone/script/script.cpp @@ -4,8 +4,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "script.h" - -#include "tinyformat.h" #include "utilstrencodings.h" using namespace std; diff --git a/src/lib/consensus/clone/script/script.h b/src/lib/consensus/clone/script/script.h index 587185a79..7d8d09db8 100644 --- a/src/lib/consensus/clone/script/script.h +++ b/src/lib/consensus/clone/script/script.h @@ -27,10 +27,6 @@ static const int MAX_OPS_PER_SCRIPT = 201; // Maximum number of public keys per multisig static const int MAX_PUBKEYS_PER_MULTISIG = 20; -// Threshold for nLockTime: below this value it is interpreted as block number, -// otherwise as UNIX timestamp. -static const unsigned int LOCKTIME_THRESHOLD = 500000000; // Tue Nov 5 00:53:20 1985 UTC - template std::vector ToByteVector(const T& in) { @@ -309,6 +305,11 @@ class CScriptNum return m_value; } + int getint64() const + { + return m_value; + } + std::vector getvch() const { return serialize(m_value); diff --git a/src/lib/consensus/clone/tinyformat.h b/src/lib/consensus/clone/tinyformat.h deleted file mode 100644 index 73d49a1fe..000000000 --- a/src/lib/consensus/clone/tinyformat.h +++ /dev/null @@ -1,1013 +0,0 @@ -// tinyformat.h -// Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com] -// -// Boost Software License - Version 1.0 -// -// Permission is hereby granted, free of charge, to any person or organization -// obtaining a copy of the software and accompanying documentation covered by -// this license (the "Software") to use, reproduce, display, distribute, -// execute, and transmit the Software, and to prepare derivative works of the -// Software, and to permit third-parties to whom the Software is furnished to -// do so, all subject to the following: -// -// The copyright notices in the Software and this entire statement, including -// the above license grant, this restriction and the following disclaimer, -// must be included in all copies of the Software, in whole or in part, and -// all derivative works of the Software, unless such copies or derivative -// works are solely in the form of machine-executable object code generated by -// a source language processor. -// -// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT -// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE -// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, -// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -// DEALINGS IN THE SOFTWARE. - -//------------------------------------------------------------------------------ -// Tinyformat: A minimal type safe printf replacement -// -// tinyformat.h is a type safe printf replacement library in a single C++ -// header file. Design goals include: -// -// * Type safety and extensibility for user defined types. -// * C99 printf() compatibility, to the extent possible using std::ostream -// * Simplicity and minimalism. A single header file to include and distribute -// with your projects. -// * Augment rather than replace the standard stream formatting mechanism -// * C++98 support, with optional C++11 niceties -// -// -// Main interface example usage -// ---------------------------- -// -// To print a date to std::cout: -// -// std::string weekday = "Wednesday"; -// const char* month = "July"; -// size_t day = 27; -// long hour = 14; -// int min = 44; -// -// tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); -// -// The strange types here emphasize the type safety of the interface; it is -// possible to print a std::string using the "%s" conversion, and a -// size_t using the "%d" conversion. A similar result could be achieved -// using either of the tfm::format() functions. One prints on a user provided -// stream: -// -// tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", -// weekday, month, day, hour, min); -// -// The other returns a std::string: -// -// std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n", -// weekday, month, day, hour, min); -// std::cout << date; -// -// These are the three primary interface functions. -// -// -// User defined format functions -// ----------------------------- -// -// Simulating variadic templates in C++98 is pretty painful since it requires -// writing out the same function for each desired number of arguments. To make -// this bearable tinyformat comes with a set of macros which are used -// internally to generate the API, but which may also be used in user code. -// -// The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and -// TINYFORMAT_PASSARGS(n) will generate a list of n argument types, -// type/name pairs and argument names respectively when called with an integer -// n between 1 and 16. We can use these to define a macro which generates the -// desired user defined function with n arguments. To generate all 16 user -// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an -// example, see the implementation of printf() at the end of the source file. -// -// -// Additional API information -// -------------------------- -// -// Error handling: Define TINYFORMAT_ERROR to customize the error handling for -// format strings which are unsupported or have the wrong number of format -// specifiers (calls assert() by default). -// -// User defined types: Uses operator<< for user defined types by default. -// Overload formatValue() for more control. - - -#ifndef TINYFORMAT_H_INCLUDED -#define TINYFORMAT_H_INCLUDED - -namespace tinyformat {} -//------------------------------------------------------------------------------ -// Config section. Customize to your liking! - -// Namespace alias to encourage brevity -namespace tfm = tinyformat; - -// Error handling; calls assert() by default. -#define TINYFORMAT_ERROR(reasonString) throw std::runtime_error(reasonString) - -// Define for C++11 variadic templates which make the code shorter & more -// general. If you don't define this, C++11 support is autodetected below. -// #define TINYFORMAT_USE_VARIADIC_TEMPLATES - - -//------------------------------------------------------------------------------ -// Implementation details. -#include -#include -#include -#include - -#ifndef TINYFORMAT_ERROR -# define TINYFORMAT_ERROR(reason) assert(0 && reason) -#endif - -#if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES) -# ifdef __GXX_EXPERIMENTAL_CXX0X__ -# define TINYFORMAT_USE_VARIADIC_TEMPLATES -# endif -#endif - -#ifdef __GNUC__ -# define TINYFORMAT_NOINLINE __attribute__((noinline)) -#elif defined(_MSC_VER) -# define TINYFORMAT_NOINLINE __declspec(noinline) -#else -# define TINYFORMAT_NOINLINE -#endif - -#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 -// std::showpos is broken on old libstdc++ as provided with OSX. See -// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html -# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND -#endif - -namespace tinyformat { - -//------------------------------------------------------------------------------ -namespace detail { - -// Test whether type T1 is convertible to type T2 -template -struct is_convertible -{ - private: - // two types of different size - struct fail { char dummy[2]; }; - struct succeed { char dummy; }; - // Try to convert a T1 to a T2 by plugging into tryConvert - static fail tryConvert(...); - static succeed tryConvert(const T2&); - static const T1& makeT1(); - public: -# ifdef _MSC_VER - // Disable spurious loss of precision warnings in tryConvert(makeT1()) -# pragma warning(push) -# pragma warning(disable:4244) -# pragma warning(disable:4267) -# endif - // Standard trick: the (...) version of tryConvert will be chosen from - // the overload set only if the version taking a T2 doesn't match. - // Then we compare the sizes of the return types to check which - // function matched. Very neat, in a disgusting kind of way :) - static const bool value = - sizeof(tryConvert(makeT1())) == sizeof(succeed); -# ifdef _MSC_VER -# pragma warning(pop) -# endif -}; - - -// Detect when a type is not a wchar_t string -template struct is_wchar { typedef int tinyformat_wchar_is_not_supported; }; -template<> struct is_wchar {}; -template<> struct is_wchar {}; -template struct is_wchar {}; -template struct is_wchar {}; - - -// Format the value by casting to type fmtT. This default implementation -// should never be called. -template::value> -struct formatValueAsType -{ - static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); } -}; -// Specialized version for types that can actually be converted to fmtT, as -// indicated by the "convertible" template parameter. -template -struct formatValueAsType -{ - static void invoke(std::ostream& out, const T& value) - { out << static_cast(value); } -}; - -#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND -template::value> -struct formatZeroIntegerWorkaround -{ - static bool invoke(std::ostream& /**/, const T& /**/) { return false; } -}; -template -struct formatZeroIntegerWorkaround -{ - static bool invoke(std::ostream& out, const T& value) - { - if (static_cast(value) == 0 && out.flags() & std::ios::showpos) - { - out << "+0"; - return true; - } - return false; - } -}; -#endif // TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND - -// Convert an arbitrary type to integer. The version with convertible=false -// throws an error. -template::value> -struct convertToInt -{ - static int invoke(const T& /*value*/) - { - TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to " - "integer for use as variable width or precision"); - return 0; - } -}; -// Specialization for convertToInt when conversion is possible -template -struct convertToInt -{ - static int invoke(const T& value) { return static_cast(value); } -}; - -} // namespace detail - - -//------------------------------------------------------------------------------ -// Variable formatting functions. May be overridden for user-defined types if -// desired. - - -// Format a value into a stream. Called from format() for all types by default. -// -// Users may override this for their own types. When this function is called, -// the stream flags will have been modified according to the format string. -// The format specification is provided in the range [fmtBegin, fmtEnd). -// -// By default, formatValue() uses the usual stream insertion operator -// operator<< to format the type T, with special cases for the %c and %p -// conversions. -template -inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, - const char* fmtEnd, const T& value) -{ -#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS - // Since we don't support printing of wchar_t using "%ls", make it fail at - // compile time in preference to printing as a void* at runtime. - typedef typename detail::is_wchar::tinyformat_wchar_is_not_supported DummyType; - (void) DummyType(); // avoid unused type warning with gcc-4.8 -#endif - // The mess here is to support the %c and %p conversions: if these - // conversions are active we try to convert the type to a char or const - // void* respectively and format that instead of the value itself. For the - // %p conversion it's important to avoid dereferencing the pointer, which - // could otherwise lead to a crash when printing a dangling (const char*). - const bool canConvertToChar = detail::is_convertible::value; - const bool canConvertToVoidPtr = detail::is_convertible::value; - if(canConvertToChar && *(fmtEnd-1) == 'c') - detail::formatValueAsType::invoke(out, value); - else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p') - detail::formatValueAsType::invoke(out, value); -#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND - else if(detail::formatZeroIntegerWorkaround::invoke(out, value)) /**/; -#endif - else - out << value; -} - - -// Overloaded version for char types to support printing as an integer -#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \ -inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ - const char* fmtEnd, charType value) \ -{ \ - switch(*(fmtEnd-1)) \ - { \ - case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ - out << static_cast(value); break; \ - default: \ - out << value; break; \ - } \ -} -// per 3.9.1: char, signed char and unsigned char are all distinct types -TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char) -TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char) -TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char) -#undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR - - -//------------------------------------------------------------------------------ -// Tools for emulating variadic templates in C++98. The basic idea here is -// stolen from the boost preprocessor metaprogramming library and cut down to -// be just general enough for what we need. - -#define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n -#define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_ ## n -#define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_ ## n -#define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n - -// To keep it as transparent as possible, the macros below have been generated -// using python via the excellent cog.py code generation script. This avoids -// the need for a bunch of complex (but more general) preprocessor tricks as -// used in boost.preprocessor. -// -// To rerun the code generation in place, use `cog.py -r tinyformat.h` -// (see http://nedbatchelder.com/code/cog). Alternatively you can just create -// extra versions by hand. - -/*[[[cog -maxParams = 16 - -def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1): - for j in range(startInd,maxParams+1): - list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)]) - cog.outl(lineTemplate % {'j':j, 'list':list}) - -makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s', - 'class T%(i)d') - -cog.outl() -makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s', - 'const T%(i)d& v%(i)d') - -cog.outl() -makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d') - -cog.outl() -cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1') -makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s', - 'v%(i)d', startInd = 2) - -cog.outl() -cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' + - ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)])) -]]]*/ -#define TINYFORMAT_ARGTYPES_1 class T1 -#define TINYFORMAT_ARGTYPES_2 class T1, class T2 -#define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3 -#define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4 -#define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5 -#define TINYFORMAT_ARGTYPES_6 class T1, class T2, class T3, class T4, class T5, class T6 -#define TINYFORMAT_ARGTYPES_7 class T1, class T2, class T3, class T4, class T5, class T6, class T7 -#define TINYFORMAT_ARGTYPES_8 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8 -#define TINYFORMAT_ARGTYPES_9 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9 -#define TINYFORMAT_ARGTYPES_10 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10 -#define TINYFORMAT_ARGTYPES_11 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11 -#define TINYFORMAT_ARGTYPES_12 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12 -#define TINYFORMAT_ARGTYPES_13 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13 -#define TINYFORMAT_ARGTYPES_14 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14 -#define TINYFORMAT_ARGTYPES_15 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15 -#define TINYFORMAT_ARGTYPES_16 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16 - -#define TINYFORMAT_VARARGS_1 const T1& v1 -#define TINYFORMAT_VARARGS_2 const T1& v1, const T2& v2 -#define TINYFORMAT_VARARGS_3 const T1& v1, const T2& v2, const T3& v3 -#define TINYFORMAT_VARARGS_4 const T1& v1, const T2& v2, const T3& v3, const T4& v4 -#define TINYFORMAT_VARARGS_5 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5 -#define TINYFORMAT_VARARGS_6 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6 -#define TINYFORMAT_VARARGS_7 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7 -#define TINYFORMAT_VARARGS_8 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8 -#define TINYFORMAT_VARARGS_9 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9 -#define TINYFORMAT_VARARGS_10 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10 -#define TINYFORMAT_VARARGS_11 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11 -#define TINYFORMAT_VARARGS_12 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12 -#define TINYFORMAT_VARARGS_13 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13 -#define TINYFORMAT_VARARGS_14 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14 -#define TINYFORMAT_VARARGS_15 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15 -#define TINYFORMAT_VARARGS_16 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15, const T16& v16 - -#define TINYFORMAT_PASSARGS_1 v1 -#define TINYFORMAT_PASSARGS_2 v1, v2 -#define TINYFORMAT_PASSARGS_3 v1, v2, v3 -#define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4 -#define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5 -#define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6 -#define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7 -#define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8 -#define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9 -#define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 -#define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 -#define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 -#define TINYFORMAT_PASSARGS_13 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 -#define TINYFORMAT_PASSARGS_14 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 -#define TINYFORMAT_PASSARGS_15 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 -#define TINYFORMAT_PASSARGS_16 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 - -#define TINYFORMAT_PASSARGS_TAIL_1 -#define TINYFORMAT_PASSARGS_TAIL_2 , v2 -#define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3 -#define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4 -#define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5 -#define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6 -#define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7 -#define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8 -#define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9 -#define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10 -#define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 -#define TINYFORMAT_PASSARGS_TAIL_12 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 -#define TINYFORMAT_PASSARGS_TAIL_13 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 -#define TINYFORMAT_PASSARGS_TAIL_14 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 -#define TINYFORMAT_PASSARGS_TAIL_15 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 -#define TINYFORMAT_PASSARGS_TAIL_16 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 - -#define TINYFORMAT_FOREACH_ARGNUM(m) \ - m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) m(15) m(16) -//[[[end]]] - - - -namespace detail { - -// Class holding current position in format string and an output stream into -// which arguments are formatted. -class FormatIterator -{ - public: - // Flags for features not representable with standard stream state - enum ExtraFormatFlags - { - Flag_None = 0, - Flag_TruncateToPrecision = 1<<0, // truncate length to stream precision() - Flag_SpacePadPositive = 1<<1, // pad positive values with spaces - Flag_VariableWidth = 1<<2, // variable field width in arg list - Flag_VariablePrecision = 1<<3 // variable field precision in arg list - }; - - // out is the output stream, fmt is the full format string - FormatIterator(std::ostream& out, const char* fmt) - : m_out(out), - m_fmt(fmt), - m_extraFlags(Flag_None), - m_wantWidth(false), - m_wantPrecision(false), - m_variableWidth(0), - m_variablePrecision(0), - m_origWidth(out.width()), - m_origPrecision(out.precision()), - m_origFlags(out.flags()), - m_origFill(out.fill()) - { } - - // Print remaining part of format string. - void finish() - { - // It would be nice if we could do this from the destructor, but we - // can't if TINFORMAT_ERROR is used to throw an exception! - m_fmt = printFormatStringLiteral(m_out, m_fmt); - if(*m_fmt != '\0') - TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); - } - - ~FormatIterator() - { - // Restore stream state - m_out.width(m_origWidth); - m_out.precision(m_origPrecision); - m_out.flags(m_origFlags); - m_out.fill(m_origFill); - } - - template - void accept(const T& value); - - private: - // Parse and return an integer from the string c, as atoi() - // On return, c is set to one past the end of the integer. - static int parseIntAndAdvance(const char*& c) - { - int i = 0; - for(;*c >= '0' && *c <= '9'; ++c) - i = 10*i + (*c - '0'); - return i; - } - - // Format at most truncLen characters of a C string to the given - // stream. Return true if formatting proceeded (generic version always - // returns false) - template - static bool formatCStringTruncate(std::ostream& /*out*/, const T& /*value*/, - std::streamsize /*truncLen*/) - { - return false; - } -# define TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(type) \ - static bool formatCStringTruncate(std::ostream& out, type* value, \ - std::streamsize truncLen) \ - { \ - std::streamsize len = 0; \ - while(len < truncLen && value[len] != 0) \ - ++len; \ - out.write(value, len); \ - return true; \ - } - // Overload for const char* and char*. Could overload for signed & - // unsigned char too, but these are technically unneeded for printf - // compatibility. - TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(const char) - TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(char) -# undef TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE - - // Print literal part of format string and return next format spec - // position. - // - // Skips over any occurrences of '%%', printing a literal '%' to the - // output. The position of the first % character of the next - // nontrivial format spec is returned, or the end of string. - static const char* printFormatStringLiteral(std::ostream& out, - const char* fmt) - { - const char* c = fmt; - for(; true; ++c) - { - switch(*c) - { - case '\0': - out.write(fmt, static_cast(c - fmt)); - return c; - case '%': - out.write(fmt, static_cast(c - fmt)); - if(*(c+1) != '%') - return c; - // for "%%", tack trailing % onto next literal section. - fmt = ++c; - break; - } - } - } - - static const char* streamStateFromFormat(std::ostream& out, - unsigned int& extraFlags, - const char* fmtStart, - int variableWidth, - int variablePrecision); - - // Private copy & assign: Kill gcc warnings with -Weffc++ - FormatIterator(const FormatIterator&); - FormatIterator& operator=(const FormatIterator&); - - // Stream, current format string & state - std::ostream& m_out; - const char* m_fmt; - unsigned int m_extraFlags; - // State machine info for handling of variable width & precision - bool m_wantWidth; - bool m_wantPrecision; - int m_variableWidth; - int m_variablePrecision; - // Saved stream state - std::streamsize m_origWidth; - std::streamsize m_origPrecision; - std::ios::fmtflags m_origFlags; - char m_origFill; -}; - - -// Accept a value for formatting into the internal stream. -template -TINYFORMAT_NOINLINE // < greatly reduces bloat in optimized builds -void FormatIterator::accept(const T& value) -{ - // Parse the format string - const char* fmtEnd = 0; - if(m_extraFlags == Flag_None && !m_wantWidth && !m_wantPrecision) - { - m_fmt = printFormatStringLiteral(m_out, m_fmt); - fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt, 0, 0); - m_wantWidth = (m_extraFlags & Flag_VariableWidth) != 0; - m_wantPrecision = (m_extraFlags & Flag_VariablePrecision) != 0; - } - // Consume value as variable width and precision specifier if necessary - if(m_extraFlags & (Flag_VariableWidth | Flag_VariablePrecision)) - { - if(m_wantWidth || m_wantPrecision) - { - int v = convertToInt::invoke(value); - if(m_wantWidth) - { - m_variableWidth = v; - m_wantWidth = false; - } - else if(m_wantPrecision) - { - m_variablePrecision = v; - m_wantPrecision = false; - } - return; - } - // If we get here, we've set both the variable precision and width as - // required and we need to rerun the stream state setup to insert these. - fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt, - m_variableWidth, m_variablePrecision); - } - - // Format the value into the stream. - if(!(m_extraFlags & (Flag_SpacePadPositive | Flag_TruncateToPrecision))) - formatValue(m_out, m_fmt, fmtEnd, value); - else - { - // The following are special cases where there's no direct - // correspondence between stream formatting and the printf() behaviour. - // Instead, we simulate the behaviour crudely by formatting into a - // temporary string stream and munging the resulting string. - std::ostringstream tmpStream; - tmpStream.copyfmt(m_out); - if(m_extraFlags & Flag_SpacePadPositive) - tmpStream.setf(std::ios::showpos); - // formatCStringTruncate is required for truncating conversions like - // "%.4s" where at most 4 characters of the c-string should be read. - // If we didn't include this special case, we might read off the end. - if(!( (m_extraFlags & Flag_TruncateToPrecision) && - formatCStringTruncate(tmpStream, value, m_out.precision()) )) - { - // Not a truncated c-string; just format normally. - formatValue(tmpStream, m_fmt, fmtEnd, value); - } - std::string result = tmpStream.str(); // allocates... yuck. - if(m_extraFlags & Flag_SpacePadPositive) - { - for(size_t i = 0, iend = result.size(); i < iend; ++i) - if(result[i] == '+') - result[i] = ' '; - } - if((m_extraFlags & Flag_TruncateToPrecision) && - (int)result.size() > (int)m_out.precision()) - m_out.write(result.c_str(), m_out.precision()); - else - m_out << result; - } - m_extraFlags = Flag_None; - m_fmt = fmtEnd; -} - - -// Parse a format string and set the stream state accordingly. -// -// The format mini-language recognized here is meant to be the one from C99, -// with the form "%[flags][width][.precision][length]type". -// -// Formatting options which can't be natively represented using the ostream -// state are returned in the extraFlags parameter which is a bitwise -// combination of values from the ExtraFormatFlags enum. -inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, - unsigned int& extraFlags, - const char* fmtStart, - int variableWidth, - int variablePrecision) -{ - if(*fmtStart != '%') - { - TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); - return fmtStart; - } - // Reset stream state to defaults. - out.width(0); - out.precision(6); - out.fill(' '); - // Reset most flags; ignore irrelevant unitbuf & skipws. - out.unsetf(std::ios::adjustfield | std::ios::basefield | - std::ios::floatfield | std::ios::showbase | std::ios::boolalpha | - std::ios::showpoint | std::ios::showpos | std::ios::uppercase); - extraFlags = Flag_None; - bool precisionSet = false; - bool widthSet = false; - const char* c = fmtStart + 1; - // 1) Parse flags - for(;; ++c) - { - switch(*c) - { - case '#': - out.setf(std::ios::showpoint | std::ios::showbase); - continue; - case '0': - // overridden by left alignment ('-' flag) - if(!(out.flags() & std::ios::left)) - { - // Use internal padding so that numeric values are - // formatted correctly, eg -00010 rather than 000-10 - out.fill('0'); - out.setf(std::ios::internal, std::ios::adjustfield); - } - continue; - case '-': - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - continue; - case ' ': - // overridden by show positive sign, '+' flag. - if(!(out.flags() & std::ios::showpos)) - extraFlags |= Flag_SpacePadPositive; - continue; - case '+': - out.setf(std::ios::showpos); - extraFlags &= ~Flag_SpacePadPositive; - continue; - } - break; - } - // 2) Parse width - if(*c >= '0' && *c <= '9') - { - widthSet = true; - out.width(parseIntAndAdvance(c)); - } - if(*c == '*') - { - widthSet = true; - if(variableWidth < 0) - { - // negative widths correspond to '-' flag set - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - variableWidth = -variableWidth; - } - out.width(variableWidth); - extraFlags |= Flag_VariableWidth; - ++c; - } - // 3) Parse precision - if(*c == '.') - { - ++c; - int precision = 0; - if(*c == '*') - { - ++c; - extraFlags |= Flag_VariablePrecision; - precision = variablePrecision; - } - else - { - if(*c >= '0' && *c <= '9') - precision = parseIntAndAdvance(c); - else if(*c == '-') // negative precisions ignored, treated as zero. - parseIntAndAdvance(++c); - } - out.precision(precision); - precisionSet = true; - } - // 4) Ignore any C99 length modifier - while(*c == 'l' || *c == 'h' || *c == 'L' || - *c == 'j' || *c == 'z' || *c == 't') - ++c; - // 5) We're up to the conversion specifier character. - // Set stream flags based on conversion specifier (thanks to the - // boost::format class for forging the way here). - bool intConversion = false; - switch(*c) - { - case 'u': case 'd': case 'i': - out.setf(std::ios::dec, std::ios::basefield); - intConversion = true; - break; - case 'o': - out.setf(std::ios::oct, std::ios::basefield); - intConversion = true; - break; - case 'X': - out.setf(std::ios::uppercase); - case 'x': case 'p': - out.setf(std::ios::hex, std::ios::basefield); - intConversion = true; - break; - case 'E': - out.setf(std::ios::uppercase); - case 'e': - out.setf(std::ios::scientific, std::ios::floatfield); - out.setf(std::ios::dec, std::ios::basefield); - break; - case 'F': - out.setf(std::ios::uppercase); - case 'f': - out.setf(std::ios::fixed, std::ios::floatfield); - break; - case 'G': - out.setf(std::ios::uppercase); - case 'g': - out.setf(std::ios::dec, std::ios::basefield); - // As in boost::format, let stream decide float format. - out.flags(out.flags() & ~std::ios::floatfield); - break; - case 'a': case 'A': - TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs " - "are not supported"); - break; - case 'c': - // Handled as special case inside formatValue() - break; - case 's': - if(precisionSet) - extraFlags |= Flag_TruncateToPrecision; - // Make %s print booleans as "true" and "false" - out.setf(std::ios::boolalpha); - break; - case 'n': - // Not supported - will cause problems! - TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported"); - break; - case '\0': - TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly " - "terminated by end of string"); - return c; - } - if(intConversion && precisionSet && !widthSet) - { - // "precision" for integers gives the minimum number of digits (to be - // padded with zeros on the left). This isn't really supported by the - // iostreams, but we can approximately simulate it with the width if - // the width isn't otherwise used. - out.width(out.precision()); - out.setf(std::ios::internal, std::ios::adjustfield); - out.fill('0'); - } - return c+1; -} - - - -//------------------------------------------------------------------------------ -// Private format function on top of which the public interface is implemented. -// We enforce a mimimum of one value to be formatted to prevent bugs looking like -// -// const char* myStr = "100% broken"; -// printf(myStr); // Parses % as a format specifier -#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES - -template -void format(FormatIterator& fmtIter, const T1& value1) -{ - fmtIter.accept(value1); - fmtIter.finish(); -} - -// General version for C++11 -template -void format(FormatIterator& fmtIter, const T1& value1, const Args&... args) -{ - fmtIter.accept(value1); - format(fmtIter, args...); -} - -#else - -inline void format(FormatIterator& fmtIter) -{ - fmtIter.finish(); -} - -// General version for C++98 -#define TINYFORMAT_MAKE_FORMAT_DETAIL(n) \ -template \ -void format(detail::FormatIterator& fmtIter, TINYFORMAT_VARARGS(n)) \ -{ \ - fmtIter.accept(v1); \ - format(fmtIter TINYFORMAT_PASSARGS_TAIL(n)); \ -} - -TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_DETAIL) -#undef TINYFORMAT_MAKE_FORMAT_DETAIL - -#endif // End C++98 variadic template emulation for format() - -} // namespace detail - - -//------------------------------------------------------------------------------ -// Implement all the main interface functions here in terms of detail::format() - -#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES - -// C++11 - the simple case -template -void format(std::ostream& out, const char* fmt, const T1& v1, const Args&... args) -{ - detail::FormatIterator fmtIter(out, fmt); - format(fmtIter, v1, args...); -} - -template -std::string format(const char* fmt, const T1& v1, const Args&... args) -{ - std::ostringstream oss; - format(oss, fmt, v1, args...); - return oss.str(); -} - -template -std::string format(const std::string &fmt, const T1& v1, const Args&... args) -{ - std::ostringstream oss; - format(oss, fmt.c_str(), v1, args...); - return oss.str(); -} - -template -void printf(const char* fmt, const T1& v1, const Args&... args) -{ - format(std::cout, fmt, v1, args...); -} - -#else - -// C++98 - define the interface functions using the wrapping macros -#define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \ - \ -template \ -void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \ -{ \ - tinyformat::detail::FormatIterator fmtIter(out, fmt); \ - tinyformat::detail::format(fmtIter, TINYFORMAT_PASSARGS(n)); \ -} \ - \ -template \ -std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \ -{ \ - std::ostringstream oss; \ - tinyformat::format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ - return oss.str(); \ -} \ - \ -template \ -std::string format(const std::string &fmt, TINYFORMAT_VARARGS(n)) \ -{ \ - std::ostringstream oss; \ - tinyformat::format(oss, fmt.c_str(), TINYFORMAT_PASSARGS(n)); \ - return oss.str(); \ -} \ - \ -template \ -void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \ -{ \ - tinyformat::format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ -} - -TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS) -#undef TINYFORMAT_MAKE_FORMAT_FUNCS -#endif - - -//------------------------------------------------------------------------------ -// Define deprecated wrapping macro for backward compatibility in tinyformat -// 1.x. Will be removed in version 2! -#define TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS -#define TINYFORMAT_WRAP_FORMAT_N(n, returnType, funcName, funcDeclSuffix, \ - bodyPrefix, streamName, bodySuffix) \ -template \ -returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt, \ - TINYFORMAT_VARARGS(n)) funcDeclSuffix \ -{ \ - bodyPrefix \ - tinyformat::format(streamName, fmt, TINYFORMAT_PASSARGS(n)); \ - bodySuffix \ -} \ - -#define TINYFORMAT_WRAP_FORMAT(returnType, funcName, funcDeclSuffix, \ - bodyPrefix, streamName, bodySuffix) \ -inline \ -returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt \ - ) funcDeclSuffix \ -{ \ - bodyPrefix \ - tinyformat::detail::FormatIterator(streamName, fmt).finish(); \ - bodySuffix \ -} \ -TINYFORMAT_WRAP_FORMAT_N(1 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(2 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(3 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(4 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(5 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(6 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(7 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(8 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(9 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(10, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(11, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(12, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(13, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(14, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(15, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ -TINYFORMAT_WRAP_FORMAT_N(16, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \ - - -} // namespace tinyformat - -#define strprintf tfm::format - -#endif // TINYFORMAT_H_INCLUDED diff --git a/src/lib/consensus/clone/utilstrencodings.cpp b/src/lib/consensus/clone/utilstrencodings.cpp index 130bc997b..a73f1e31b 100644 --- a/src/lib/consensus/clone/utilstrencodings.cpp +++ b/src/lib/consensus/clone/utilstrencodings.cpp @@ -5,12 +5,11 @@ #include "utilstrencodings.h" -#include "tinyformat.h" - #include #include #include #include +#include using namespace std; @@ -512,12 +511,12 @@ std::string FormatParagraph(const std::string& in, size_t width, size_t indent) std::string i64tostr(int64_t n) { - return strprintf("%d", n); + return std::to_string(n); } std::string itostr(int n) { - return strprintf("%d", n); + return std::to_string(n); } int64_t atoi64(const char* psz) diff --git a/src/lib/consensus/common/libdevcore/BasicType.cpp b/src/lib/consensus/common/libdevcore/BasicType.cpp index 42817cbfc..56c6e3894 100644 --- a/src/lib/consensus/common/libdevcore/BasicType.cpp +++ b/src/lib/consensus/common/libdevcore/BasicType.cpp @@ -1,13 +1,20 @@ #include +#include +#include +#include +#include +#include "crypto/common.h" #include #include #include +#include +#include using namespace libbitcoin; DEV_SIMPLE_EXCEPTION(GenesisBlockCannotBeCalculated); /*****************************/ -WorkPackage::WorkPackage(libbitcoin::chain::header& _bh): +WorkPackage::WorkPackage(chain::header& _bh): boundary(HeaderAux::boundary(_bh)), headerHash(HeaderAux::hashHead(_bh)), seedHash(HeaderAux::seedHash(_bh)) @@ -27,6 +34,7 @@ LightAllocation::~LightAllocation() { ethash_light_delete(light); } + Result LightAllocation::compute(h256& _headerHash, Nonce& _nonce) { ethash_return_value r = ethash_light_compute(light, *(ethash_h256_t*)_headerHash.data(), (uint64_t)(u64)_nonce); @@ -60,8 +68,8 @@ FullAllocation::~FullAllocation() } /*****************************/ -HeaderAux* libbitcoin::HeaderAux::s_this = nullptr; -bool libbitcoin::HeaderAux::is_testnet = false; +HeaderAux* HeaderAux::s_this = nullptr; +bool HeaderAux::is_testnet = false; HeaderAux* HeaderAux::get() @@ -72,24 +80,24 @@ HeaderAux* HeaderAux::get() } -h256 HeaderAux::seedHash(libbitcoin::chain::header& _bi) +h256 HeaderAux::seedHash(const chain::header& bi) { - unsigned _number = (unsigned) _bi.number; + unsigned _number = (unsigned) bi.number; unsigned epoch = _number / ETHASH_EPOCH_LENGTH; Guard l(get()->x_epochs); - if (epoch >= get()->m_seedHashes.size()) - { + + if (epoch >= get()->m_seedHashes.size()) { h256 ret; unsigned n = 0; - if (!get()->m_seedHashes.empty()) - { + if (!get()->m_seedHashes.empty()) { ret = get()->m_seedHashes.back(); n = get()->m_seedHashes.size() - 1; } + get()->m_seedHashes.resize(epoch + 1); // cdebug << "Searching for seedHash of epoch " << epoch; - for (; n <= epoch; ++n, ret = sha3(ret)) - { + + for (; n <= epoch; ++n, ret = sha3(ret)) { get()->m_seedHashes[n] = ret; // cdebug << "Epoch" << n << "is" << ret; } @@ -102,12 +110,12 @@ uint64_t HeaderAux::number(h256& _seedHash) Guard l(get()->x_epochs); unsigned epoch = 0; auto epochIter = get()->m_epochs.find(_seedHash); - if (epochIter == get()->m_epochs.end()) - { + if (epochIter == get()->m_epochs.end()) { // cdebug << "Searching for seedHash " << _seedHash; - for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) {} - if (epoch == 2048) - { + for (h256 h; h != _seedHash && epoch < 2048; ++epoch, h = sha3(h), get()->m_epochs[h] = epoch) + {} + + if (epoch == 2048) { std::ostringstream error; error << "apparent block number for " << _seedHash << " is too high; max is " << (ETHASH_EPOCH_LENGTH * 2048); throw std::invalid_argument(error.str()); @@ -118,17 +126,35 @@ uint64_t HeaderAux::number(h256& _seedHash) return epoch * ETHASH_EPOCH_LENGTH; } -h256 HeaderAux::hashHead(libbitcoin::chain::header& _bi) +h256 HeaderAux::boundary(const chain::header& bi) +{ + auto d = bi.bits; + return d ? (h256)u256(((bigint(1) << 255)-bigint(1) +(bigint(1) << 255) ) / d) : h256(); +} + +h256 HeaderAux::hashHead(const chain::header& bi) { h256 memo; RLPStream s; - s << (bigint) _bi.version << (bigint)_bi.bits << (bigint)_bi.number << _bi.merkle - << _bi.previous_block_hash << (bigint) _bi.timestamp ; + s << (bigint) bi.version << (bigint)bi.bits << (bigint)bi.number << bi.merkle + << bi.previous_block_hash << (bigint) bi.timestamp ; memo = sha3(s.out()); return memo; } -uint64_t HeaderAux::cacheSize(libbitcoin::chain::header& _header) +h256 HeaderAux::hash_head_pos(const chain::header& bi, const chain::output_info& stake) +{ + RLPStream s; + s << (bigint) bi.version << (bigint)bi.number + << bi.previous_block_hash << (bigint) bi.timestamp + << stake.point.hash << (bigint) stake.point.index; + + auto hash_pos = bitcoin_hash(to_chunk(s.out())); + h256 memo = h256(encode_hash(hash_pos)); + return memo; +} + +uint64_t HeaderAux::cacheSize(const chain::header& _header) { return ethash_get_cachesize((uint64_t)_header.number); } @@ -138,61 +164,105 @@ uint64_t HeaderAux::dataSize(uint64_t _blockNumber) return ethash_get_datasize(_blockNumber); } -u256 HeaderAux::calculateDifficulty(libbitcoin::chain::header& _bi, libbitcoin::chain::header& _parent) +u256 HeaderAux::calculate_difficulty( + const chain::header& current, + const chain::header::ptr prev, + const chain::header::ptr pprev, + bool is_staking) { - auto minimumDifficulty = is_testnet ? bigint(300000) : bigint(914572800); - bigint target; +#ifndef PRIVATE_CHAIN + return calculate_difficulty_v1(current, prev, pprev); +#else + if (current.number < pos_enabled_height) { + return calculate_difficulty_v1(current, prev, pprev); + } - // DO NOT MODIFY time_config in release - static uint32_t time_config{24}; - if (!_bi.number) - { + return calculate_difficulty_v2(current, prev, pprev); +#endif +} + +// Do not modify this function! It's for backward compatibility. +u256 HeaderAux::calculate_difficulty_v1( + const chain::header& current, + const chain::header::ptr prev, + const chain::header::ptr pprev) +{ + if (!current.number) { throw GenesisBlockCannotBeCalculated(); } - if(_bi.timestamp >= _parent.timestamp + time_config) - { - target = _parent.bits - (_parent.bits/1024); - } else { - target = _parent.bits + (_parent.bits/1024); +#ifndef PRIVATE_CHAIN + auto minimumDifficulty = is_testnet ? bigint(300000) : bigint(914572800); +#else + auto minimumDifficulty = bigint(1024 * 1024); +#endif + + bigint target(minimumDifficulty); + + // DO NOT MODIFY time_config in release + static uint32_t time_config{24}; + if (prev) { + if (current.timestamp >= prev->timestamp + time_config) { + target = prev->bits - (prev->bits/1024); + } + else { + target = prev->bits + (prev->bits/1024); + } } - bigint result = target; - result = std::max(minimumDifficulty, result); + bigint result = std::max(minimumDifficulty, target); return u256(std::min(result, std::numeric_limits::max())); } +bigint HeaderAux::adjust_difficulty(uint32_t actual_timespan, bigint & result) +{ + // Limit adjustment step + if (actual_timespan < block_timespan_window / 10) { + actual_timespan = block_timespan_window / 10; + } + if (actual_timespan > block_timespan_window * 10) { + actual_timespan = block_timespan_window * 10; + } + + // Retarget + const uint32_t interval = 12; + result *= ((interval + 1) * block_timespan_window); + result /= ((interval - 1) * block_timespan_window + actual_timespan + actual_timespan); + return result; +} +u256 HeaderAux::calculate_difficulty_v2( + const chain::header& current, + const chain::header::ptr prev, + const chain::header::ptr pprev) +{ +#ifndef PRIVATE_CHAIN + auto minimumDifficulty = is_testnet ? bigint(300000) : bigint(914572800); +#else + auto minimumDifficulty = bigint(1024 * 1024); +#endif -//static u256 calculateDifficulty(libbitcoin::chain::header& _bi, libbitcoin::chain::header& _parent) -//{ -// -//} + if (nullptr == prev || nullptr == pprev) { + return u256(minimumDifficulty); + } -/*****************************/ + bigint prev_bits = prev->bits; + uint32_t actual_timespan = prev->timestamp - pprev->timestamp; -ChainOperationParams::ChainOperationParams() -{ - otherParams = std::unordered_map{ - {"minGasLimit", "0x1388"}, - {"maxGasLimit", "0x7fffffffffffffff"}, - {"gasLimitBoundDivisor", "0x0400"}, - {"minimumDifficulty", "0x020000"}, - {"difficultyBoundDivisor", "0x0800"}, - {"durationLimit", "0x0d"}, - {"registrar", "5e70c0bbcd5636e0f9f9316e9f8633feb64d4050"}, - {"networkID", "0x0"} - }; - blockReward = u256("0x4563918244F40000"); -} + // Retarget + prev_bits = adjust_difficulty(actual_timespan, prev_bits); -u256 ChainOperationParams::u256Param(std::string const& _name) -{ - std::string at(""); + auto result = std::max(prev_bits, minimumDifficulty); + result = std::min(result, std::numeric_limits::max()); - auto it = otherParams.find(_name); - if (it != otherParams.end()) - at = it->second; +#ifdef PRIVATE_CHAIN + if (current.transaction_count > 0) { + log::info("difficulty") + << "last " << chain::get_block_version(*prev) + << " timespan: " << actual_timespan << " s, current height: " + << current.number << ", bits: " << result; + } +#endif - return u256(fromBigEndian(fromHex(at))); + return u256(result); } diff --git a/src/lib/consensus/common/miner/MinerAux.cpp b/src/lib/consensus/common/miner/MinerAux.cpp index 9920f07b9..e9be03f2c 100644 --- a/src/lib/consensus/common/miner/MinerAux.cpp +++ b/src/lib/consensus/common/miner/MinerAux.cpp @@ -1,25 +1,30 @@ -#include -#include -#include -#include -#include -#include -#include + +#include #include #include #include -#include #include -#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include - +#include +#include using namespace libbitcoin; using namespace std; MinerAux* libbitcoin::MinerAux::s_this = nullptr; #define LOG_MINER "etp_hash" + MinerAux::~MinerAux() { } @@ -31,7 +36,6 @@ MinerAux* MinerAux::get() return s_this; } - LightType MinerAux::get_light(h256& _seedHash) { UpgradableGuard l(get()->x_lights); @@ -47,6 +51,7 @@ static int dagCallbackShim(unsigned _p) { return 0; } + FullType MinerAux::get_full(h256& _seedHash) { FullType ret; @@ -78,8 +83,12 @@ bool MinerAux::search(libbitcoin::chain::header& header, std::functionfull, *(ethash_h256_t*)header_hash.data(), tryNonce); h256 value = h256((uint8_t*)ðashReturn.result, h256::ConstructFromPointer); h256 mixhash =h256((uint8_t*)ðashReturn.mix_hash, h256::ConstructFromPointer); - if (value <= boundary ) - { + if (value <= boundary ) { MinerAux::setNonce(header, (u64)tryNonce); MinerAux::setMixHash(header, mixhash); log::debug(LOG_MINER) << "find slolution! block height: "<< header.number << '\n'; break; } - if(is_exit() == true) - { - ethashReturn.success = false; - return ethashReturn.success; + + if (is_exit() == true) { + return false; } } + ms = std::chrono::duration_cast(std::chrono::steady_clock::now() - timeStart).count(); ms = ms? ms : 1; get()->m_rate = hashCount * 1000 / ms; @@ -114,41 +123,76 @@ bool MinerAux::search(libbitcoin::chain::header& header, std::functionx_fulls) - if (FullType dag = get()->m_fulls[seedHash].lock()) - { + if (FullType dag = get()->m_fulls[seedHash].lock()) { result = dag->compute(headerHash, nonce); - if(result.value <= HeaderAux::boundary(_header) && (result.mixHash).hex() == ((h256)_header.mixhash).hex()) - { - //log::debug(LOG_MINER) << _header.number <<" block has been verified (Full)\n"; + if (result.value <= HeaderAux::boundary(header) + && (result.mixHash).hex() == ((h256)header.mixhash).hex()) { + //log::debug(LOG_MINER) << header.number <<" block has been verified (Full)\n"; return true; } return false; } + result = get()->get_light(seedHash)->compute(headerHash, nonce); - if(result.value <= HeaderAux::boundary(_header) && (result.mixHash).hex() == ((h256)_header.mixhash).hex()) - { - //log::debug(LOG_MINER) << _header.number <<" block has been verified (Light)\n"; + if (result.value <= HeaderAux::boundary(header) + && (result.mixHash).hex() == ((h256)header.mixhash).hex()) { + //log::debug(LOG_MINER) << header.number <<" block has been verified (Light)\n"; return true; } - log::error(LOG_MINER) << _header.number <<" block verified failed !\n"; + + log::error(LOG_MINER) << header.number << " block verified failed !\n"; return false; } +bool MinerAux::verify_stake(const chain::header& header, const chain::output_info& stake_output) +{ + if (header.number + pos_stake_min_height < stake_output.height) { + return false; + } + return MinerAux::check_proof_of_stake(header, stake_output); +} +bool MinerAux::check_proof_of_stake(const chain::header& header, const chain::output_info& stake_output) +{ + bool succeed = false; + + if (stake_output.data.value >= pos_stake_min_value) { + // Base target + h256 boundary = HeaderAux::boundary(header); + uint64_t amount = std::max(1, (stake_output.data.value / coin_price())); + uint64_t stake = (uint64_t)(amount * pos_stake_factor); + + // Calculate hash + u256 pos = HeaderAux::hash_head_pos(header, stake_output); + pos /= u256(stake); + + succeed = (h256(pos) <= boundary); + +#ifdef PRIVATE_CHAIN + if (header.transaction_count > 0) { + uint64_t coin_age = header.number - stake_output.height; + log::info("verify_stake") + << (succeed ? "True" : "False") + << ", stake amount: " << (uint64_t)(stake_output.data.value / coin_price()) + << " ETPs, coin_age: " << coin_age + << ", height: " << header.number + << ", bits: " << header.bits + << ", pos: " << h256(pos) + << ", boundary: " << boundary; + } +#endif + } - + return succeed; +} diff --git a/src/lib/consensus/fts.cpp b/src/lib/consensus/fts.cpp new file mode 100644 index 000000000..69ff626e4 --- /dev/null +++ b/src/lib/consensus/fts.cpp @@ -0,0 +1,411 @@ +/** + * Copyright (c) 2016-2018 metaverse core developers (see MVS-AUTHORS) + * + * This file is part of metaverse. + * + * metaverse is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include + +#define LOG_HEADER "FTS" + +namespace libbitcoin { +namespace consensus { + + +//============================================================================== +// fts_stake_holder +//============================================================================== +fts_stake_holder::fts_stake_holder() + : address_("") + , stake_(0) +{} + +fts_stake_holder::fts_stake_holder(const std::string& address, uint64_t stake) + : address_(address) + , stake_(stake) +{} + +fts_stake_holder::fts_stake_holder(const fts_stake_holder& other) +{ + address_ = other.address_; + stake_ = other.stake_; +} + +bool fts_stake_holder::operator==(const fts_stake_holder& other) const +{ + return (address_ == other.address_ && stake_ == other.stake_); +} + +void fts_stake_holder::set_stake(uint64_t stake) +{ + stake_ = stake; +} + +void fts_stake_holder::set_address(const std::string& address) +{ + address_ = address; +} + +uint64_t fts_stake_holder::stake() const +{ + return stake_; +} + +std::string fts_stake_holder::address() const +{ + return address_; +} + +std::string fts_stake_holder::to_string() const +{ + std::ostringstream ss; + ss << "fts_stake_holder: {address: " << address_; + ss << ", stake: " << stake_ << "}"; + return ss.str(); +} + +std::shared_ptr fts_stake_holder::convert(const fts_stake_holder::list& stake_holders) +{ + auto temp = std::make_shared(); + temp->resize(stake_holders.size()); + size_t i = 0; + for (auto& holder : stake_holders) { + (*temp)[i++] = std::make_shared(holder); + } + + return temp; +} + +//============================================================================== +// fts_node +//============================================================================== + +fts_node::fts_node(const fts_node::ptr& left, const fts_node::ptr& right) + : left_(left) + , right_(right) + , stake_holder_(nullptr) + , hash_(fts::to_hash(left, right)) +{ + BITCOIN_ASSERT(left_ != nullptr && right_ != nullptr); +} + +fts_node::fts_node(const fts_stake_holder::ptr& stakeholder) + : left_(nullptr) + , right_(nullptr) + , stake_holder_(stakeholder) + , hash_(fts::to_hash(*stake_holder_)) +{ + BITCOIN_ASSERT(stake_holder_ != nullptr); +} + +fts_node::ptr fts_node::left() const +{ + return left_; +} + +fts_node::ptr fts_node::right() const +{ + return right_; +} + +fts_stake_holder::ptr fts_node::stake_holder() const +{ + return stake_holder_; +} + +hash_digest fts_node::hash() const +{ + return hash_; +} + +uint64_t fts_node::stake() const +{ + if (is_leaf()) { + return stake_holder_->stake(); + } + + return (left_->stake() + right_->stake()); +} + +bool fts_node::is_leaf() const +{ + return (left_ == nullptr && right_ == nullptr); +} + +std::string fts_node::to_string() const +{ + std::ostringstream ss; + ss << "fts_node: {hash: " << encode_hash(hash_); + ss << ", stake: " << stake() << "}"; + return ss.str(); +} + +//============================================================================== +// fts +//============================================================================== + +hash_digest fts::to_hash(const fts_node::ptr& left, const fts_node::ptr& right) +{ + BITCOIN_ASSERT(left != nullptr); + BITCOIN_ASSERT(right != nullptr); + + // Join both current hashes together (concatenate). + data_chunk concat_data; + data_sink concat_stream(concat_data); + ostream_writer concat_sink(concat_stream); + concat_sink.write_hash(left->hash()); + concat_sink.write_hash(right->hash()); + concat_sink.write_8_bytes_little_endian(left->stake()); + concat_sink.write_8_bytes_little_endian(right->stake()); + concat_stream.flush(); + + // Hash both of the hashes and stakes. + return bitcoin_hash(concat_data); +} + +hash_digest fts::to_hash(const fts_stake_holder& stakeholder) +{ + // Join both address and stake. + data_chunk concat_data; + data_sink concat_stream(concat_data); + ostream_writer concat_sink(concat_stream); + concat_sink.write_string(stakeholder.address()); + concat_sink.write_8_bytes_little_endian(stakeholder.stake()); + concat_stream.flush(); + + // Hash both of the address and stake. + return bitcoin_hash(concat_data); +} + +std::shared_ptr fts::select_by_fts( + const fts_stake_holder::ptr_list& stake_holders, + uint32_t seed, uint32_t count) +{ + auto size = stake_holders.size(); + if (size == 0) { + return nullptr; + } + + auto temp = std::make_shared(stake_holders.size()); + std::copy(stake_holders.begin(), stake_holders.end(), temp->begin()); + + if (size <= count) { + return temp; + } + + auto result = std::make_shared(); + + while (result->size() < count && temp->size() > 1) { + auto root = fts::build_merkle_tree(*temp); + BITCOIN_ASSERT(root != nullptr); + auto choosen = fts::select_by_fts(root, seed); + BITCOIN_ASSERT(choosen != nullptr && choosen->is_leaf()); + + auto holder = choosen->stake_holder(); + result->push_back(holder); + temp->erase(std::remove(temp->begin(), temp->end(), holder)); + } + + auto miss = count - result->size(); + if (miss > 0) { + for (auto& holder : *temp) { + result->push_back(holder); + + if (--miss == 0) { + break; + } + } + } + + return result; +} + +fts_node::ptr fts::build_merkle_tree(const fts_stake_holder::ptr_list& stake_holders) +{ + if (stake_holders.empty()) { + return nullptr; + } + + const size_t size = stake_holders.size(); + std::vector tree(size * 2); + + for (size_t i = 0; i < size; i++) { + auto& holder = stake_holders[i]; + tree[size + i] = std::make_shared(holder); + } + + for (size_t i = size - 1; i > 0; i--) { + fts_node::ptr left = tree[i * 2]; + fts_node::ptr right = tree[i * 2 + 1]; + tree[i] = std::make_shared(left, right); + } + + BITCOIN_ASSERT(tree.size() > 1); + return tree[1]; +} + +fts_node::ptr fts::select_by_fts(const fts_node::ptr& merkle_tree, uint32_t seed) +{ + if (merkle_tree == nullptr) { + return nullptr; + } + + fts_node::ptr node = merkle_tree; + + // Seeds the pseudo-random number generator + std::default_random_engine eng(seed); + std::uniform_int_distribution urd(0, node->stake()); + + while (!node->is_leaf()) { + auto left = node->left(); + auto right = node->right(); + uint64_t r = urd(eng) % node->stake(); + if (r < left->stake()) { + node = left; + } + else { + node = right; + } + } + + BITCOIN_ASSERT(node->is_leaf()); + return node; +} + +bool fts::verify(const fts_node::ptr& merkle_tree, uint32_t seed, const hash_digest& target_hash) +{ + if (merkle_tree == nullptr) { + return false; + } + + auto selected = select_by_fts(merkle_tree, seed); + if (selected != nullptr) { + auto mix_hash = fts::to_hash(merkle_tree, selected); + return mix_hash == target_hash; + } + + return false; +} + +void fts::test() +{ + const uint32_t seed = std::time(nullptr); + + fts_stake_holder::list stake_data = { + {"MDdET3ybWc2cGEXXxBcjtXNCcmzJe48bhc", 333000000}, + {"MQA3r2AVy9TLzoYwdyCmT2roCqTHVRk2Tj", 331000000}, + {"MWLwUrmgdGGADJmQ9nsCSHDGz6BZd1aGhw", 333000000}, + {"MUFhTGxWE2zciFYY4oQ4NJqNnz6u4Yi1dy", 8000000}, + {"MPgsYGbKfhLRptHLjHvNU2B2GumqTYSdmp", 9000000}, + {"MQibP3A6VNGqR5ZbKpkyKrU52zA8nQDZt8", 1000000}, + {"MCwCVvrQ94fHg2hjY6JYEWiXwedUa6uxFF", 2000000}, + {"M8LZMA7vVCsWrWPsZdVy4Tac5WxrPKNZTh", 7000000}, + {"MV1VWVC7NiJ6BmZPXooiamZcp51SxMUFv3", 12000000}, + {"MAGjtX89zwjXtBTeKQc1tHSatWd2ivXm64", 26000000}, + {"MUMsvrkdm3yaJDBhYq9LT9UeynKhh1fhRd", 36000000}, + {"MVgazXx68NQfMb6Dm5Xbx7HqXxUoqt6Ab9", 28000000}, + {"MJuQPM6TuewrhPsmmAWab1qep2nw9fpU5X", 29000000}, + {"MQ4Ygm2nieCM6J1EkdaHjLNokRLBoG2SCT", 33000000}, + {"MLVC5FjVrx3MKt8UmuQxSVBQZa9uD4P4ZZ", 22300000}, + {"MCiggAFxy76WRRQQwrbSfc4oWZLMZiW2mE", 11200000}, + {"MJZLuKx6EeqggDB645kiwEkRwZ9qSfnqkY", 23600000}, + {"MPNT1Z8s8SkMh4kHSgxE2MdArMoAgHRN7o", 4900000}, + {"MHuLk8CKrPFB68WcQDb4o8JPVX8ZT3p7jq", 8900000}, + {"MBV6pQbHaRFUSGCo3oxUMy7w2dVDt2B9sN", 6700000}, + {"MGG7nhM6aKXzFQK4foWEsCE7UC79q3vCcs", 2200000}, + {"MHsukoRTNjuW6FyCAmyzNwwt2geWbYN1jh", 6800000}, + {"MFBv4HW8PBxY9Cz1TpqCA9aN5zWixqwvKy", 2700000}, + {"MNyvrdXC6CNjEpKu4nJgP35mT5f38nvpp4", 3700000}, + {"MVYAmc2RUfaGPFj4B8kLvEKv38DtZ2sGs9", 3800000}, + {"MUDPXwb3nAoPChrYWGeSBfTK4p8DVg5Laj", 5500000}, + {"MM7rqzepyqAMeZ4Vn7NBzAxsuR5SruLZZg", 4400000}, + {"MMQKQYrC4YA6EVn2DeKSvMiW5hgvBDTFQ6", 3300000}, + {"MQBNSnNdgQjTcNVoJSLvoAAj29pr1MUJdL", 2200000}, + {"MADNw1zEFwxdCKpvozyjQAG6yXZ4C582pN", 1100000}, + {"M8vjBXDnisgjvAZCj9jVFnq4sxEqbBDxZ7", 45600000}, + {"MBoLXcgSbmx1ubJgVYhDbEcqDdgprcjFN5", 45300000}, + {"MP6S7vJp6EtWvRhneeLxSmo8fJrbQaXvcS", 45200000} + }; + + auto stake_holders = fts_stake_holder::convert(stake_data); + log::info(LOG_HEADER) << "test: seed: " << seed << ", stake size: " << stake_holders->size(); + + if (false) { + // select one item from stake_holders + + // first step + fts_node::ptr tree = fts::build_merkle_tree(*stake_holders); + fts_node::ptr selected = fts::select_by_fts(tree, seed); + log::info(LOG_HEADER) + << "tree one: " << encode_hash(tree->hash()) << ", stake: " << tree->stake(); + log::info(LOG_HEADER) + << "selected one: " << encode_hash(selected->hash()) << ", stake: " << selected->stake(); + + BITCOIN_ASSERT(selected->is_leaf()); + auto holder = selected->stake_holder(); + log::info(LOG_HEADER) + << "holder one: " << holder->address() << ", stake: " << holder->stake(); + + // second step + fts_node::ptr tree2 = fts::build_merkle_tree(*stake_holders); + fts_node::ptr selected2 = fts::select_by_fts(tree2, seed); + log::info(LOG_HEADER) + << "tree two: " << encode_hash(tree2->hash()) << ", stake: " << tree2->stake(); + log::info(LOG_HEADER) + << "selected two: " << encode_hash(selected2->hash()) << ", stake: " << selected2->stake(); + + BITCOIN_ASSERT(selected2->is_leaf()); + auto holder2 = selected2->stake_holder(); + log::info(LOG_HEADER) + << "holder two: " << holder2->address() << ", stake: " << holder2->stake(); + + // verify + BITCOIN_ASSERT(tree->hash() == tree2->hash()); + BITCOIN_ASSERT(selected->hash() == selected2->hash()); + } + + { + // select N items from stake_holders + std::srand(std::time(nullptr)); + const size_t N = 1 + std::rand() % (stake_holders->size() * 2); + + // first step + auto items = fts::select_by_fts(*stake_holders, seed, N); + BITCOIN_ASSERT(items != nullptr); + + // second step + auto check_items = fts::select_by_fts(*stake_holders, seed, N); + BITCOIN_ASSERT(check_items != nullptr); + + // verify + log::info(LOG_HEADER) << "choose " << items->size() << " items, seed: " + << seed << ", stake size: " << stake_holders->size() + << ", N: " << N; + BITCOIN_ASSERT(items->size() == check_items->size()); + for (size_t i = 0; i < items->size(); ++i) { + BITCOIN_ASSERT((*items)[i] == (*check_items)[i]); + log::info(LOG_HEADER) << " item " << i << ": " << (*items)[i]->to_string(); + } + } +} + + +} +} diff --git a/src/lib/consensus/miner.cpp b/src/lib/consensus/miner.cpp index c67ca9f90..c60b954bc 100644 --- a/src/lib/consensus/miner.cpp +++ b/src/lib/consensus/miner.cpp @@ -18,32 +18,37 @@ * along with this program. If not, see . */ #include -#include -#include -#include -#include +#include #include #include #include -#include +#include +#include +//#include +//#include #include #include +#include #include #include #include +#include #include #include #include +#include +#include +#include +#include -#define LOG_HEADER "consensus" +#define LOG_HEADER "Miner" using namespace std; namespace libbitcoin { namespace consensus { -static BC_CONSTEXPR uint32_t block_version = 1; -static BC_CONSTEXPR unsigned int min_tx_fee = 10000; +static BC_CONSTEXPR uint32_t min_tx_fee = 10000; // tuples: (priority, fee_per_kb, fee, transaction_ptr) typedef boost::tuple transaction_priority; @@ -66,15 +71,28 @@ bool sort_by_priority(const transaction_priority& a, const transaction_priority& }; } // end of anonymous namespace +std::string timestamp_to_string(uint32_t timestamp) +{ + typedef std::chrono::system_clock wall_clock; + auto tp = wall_clock::from_time_t(timestamp); + std::time_t rawTime = std::chrono::system_clock::to_time_t(tp); + + char buf[sizeof("yyyy-mm-dd hh:mm:ss")] = {'\0'}; + if (std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", localtime(&rawTime)) == 0) + buf[0] = '\0'; // empty if case strftime fails + return std::string(buf); +} + miner::miner(p2p_node& node) : node_(node) , state_(state::init_) , new_block_number_(0) , new_block_limit_(0) + , accept_block_version_(chain::block_version_pow) , setting_(node_.chain_impl().chain_settings()) { if (setting_.use_testnet_rules) { - bc::HeaderAux::set_as_testnet(); + HeaderAux::set_as_testnet(); } } @@ -143,12 +161,18 @@ bool miner::get_transaction(std::vector& transactions, auto& tx = **i; auto hash = tx.hash(); + if (sets.count(hash)) { + // already exist, keep unique + i = transactions.erase(i); + continue; + } + uint64_t total_input_value = 0; bool ready = get_input_etp(tx, transactions, total_input_value, previous_out_map); if (!ready) { // erase tx but not delete it from pool if parent tx is not ready i = transactions.erase(i); - break; + continue; } uint64_t total_output_value = tx.total_output_value(); @@ -156,39 +180,43 @@ bool miner::get_transaction(std::vector& transactions, // check fees if (fee < min_tx_fee || !blockchain::validate_transaction::check_special_fees(setting_.use_testnet_rules, tx, fee)) { +#ifdef MVS_DEBUG + log::debug(LOG_HEADER) << "check fees failed, delete_tx " << encode_hash(hash); +#endif i = transactions.erase(i); // delete it from pool if not enough fee node_.pool().delete_tx(hash); - break; + continue; } - auto transaction_is_ok = true; - for (auto& output : tx.outputs) { - if (tx.version >= transaction_version::check_output_script - && output.script.pattern() == script_pattern::non_standard) { + if (!setting_.transaction_pool_consistency) { + auto transaction_is_ok = true; + // check double spending + for (const auto& input : tx.inputs) { + if (node_.chain_impl().get_spends_output(input.previous_output)) { #ifdef MVS_DEBUG - log::error(LOG_HEADER) << "transaction output script error! tx:" << tx.to_string(1); + log::debug(LOG_HEADER) << "check double spending failed, delete_tx " << encode_hash(hash); #endif - node_.pool().delete_tx(hash); - transaction_is_ok = false; - break; + i = transactions.erase(i); + node_.pool().delete_tx(hash); + transaction_is_ok = false; + break; + } + } + if (!transaction_is_ok) { + continue; } } - if (transaction_is_ok && (sets.find(hash) == sets.end())) { - tx_fee_map[hash] = fee; - sets.insert(hash); - ++i; - } - else { - i = transactions.erase(i); - } + tx_fee_map[hash] = fee; + sets.insert(hash); + ++i; } } return transactions.empty() == false; } -bool miner::script_hash_signature_operations_count(size_t &count, const chain::input& input, vector& transactions) +bool miner::script_hash_signature_operations_count(uint64_t &count, const chain::input& input, vector& transactions) { const auto& previous_output = input.previous_output; transaction previous_tx; @@ -208,16 +236,17 @@ bool miner::script_hash_signature_operations_count(size_t &count, const chain::i } const auto& previous_tx_out = previous_tx.outputs[previous_output.index]; - return blockchain::validate_block::script_hash_signature_operations_count(count, previous_tx_out.script, input.script); + return blockchain::validate_block::script_hash_signature_operations_count( + count, previous_tx_out.script, input.script); } bool miner::script_hash_signature_operations_count( - size_t &count, const chain::input::list& inputs, vector& transactions) + uint64_t &count, const chain::input::list& inputs, vector& transactions) { count = 0; for (const auto& input : inputs) { - size_t c = 0; + uint64_t c = 0; if (script_hash_signature_operations_count(c, input, transactions) == false) return false; count += c; @@ -225,19 +254,6 @@ bool miner::script_hash_signature_operations_count( return true; } -#define VALUE(a) (a < 'a' ? (a - '0') : (a - 'a' + 10)) -std::string transfer_public_key(const string& key) -{ - string pub_key; - for (auto i = key.begin(); i != key.end(); i += 2) { - unsigned char a = 0; - a = (VALUE(*i) << 4) + VALUE(*(i + 1)); - pub_key.push_back(a); - } - - return pub_key; -} - miner::block_ptr miner::create_genesis_block(bool is_mainnet) { string text; @@ -260,12 +276,12 @@ miner::block_ptr miner::create_genesis_block(bool is_mainnet) // init for testnet/mainnet if (!is_mainnet) { - bc::wallet::payment_address testnet_genesis_address("tPd41bKLJGf1C5RRvaiV2mytqZB6WfM1vR"); + wallet::payment_address testnet_genesis_address("tPd41bKLJGf1C5RRvaiV2mytqZB6WfM1vR"); tx_new.outputs[0].script.operations = chain::operation::to_pay_key_hash_pattern(short_hash(testnet_genesis_address)); pblock->header.timestamp = 1479881397; } else { - bc::wallet::payment_address genesis_address("MGqHvbaH9wzdr6oUDFz4S1HptjoKQcjRve"); + wallet::payment_address genesis_address("MGqHvbaH9wzdr6oUDFz4S1HptjoKQcjRve"); tx_new.outputs[0].script.operations = chain::operation::to_pay_key_hash_pattern(short_hash(genesis_address)); pblock->header.timestamp = 1486796400; } @@ -289,7 +305,7 @@ miner::block_ptr miner::create_genesis_block(bool is_mainnet) miner::transaction_ptr miner::create_coinbase_tx( const wallet::payment_address& pay_address, uint64_t value, - uint64_t block_height, int lock_height, uint32_t reward_lock_time) + uint64_t block_height, int lock_height) { transaction_ptr ptransaction = make_shared(); @@ -301,7 +317,6 @@ miner::transaction_ptr miner::create_coinbase_tx( ptransaction->outputs.resize(1); ptransaction->outputs[0].value = value; - ptransaction->locktime = reward_lock_time; if (lock_height > 0) { ptransaction->outputs[0].script.operations = chain::operation::to_pay_key_hash_with_lock_height_pattern(short_hash(pay_address), lock_height); } else { @@ -310,8 +325,12 @@ miner::transaction_ptr miner::create_coinbase_tx( return ptransaction; } - +#ifdef PRIVATE_CHAIN +int bucket_size = 200; +#else int bucket_size = 500000; +#endif + vector lock_heights = {25200, 108000, 331200, 655200, 1314000}; vector coinage_rewards = {95890, 666666, 3200000, 8000000, 20000000}; @@ -325,9 +344,51 @@ int miner::get_lock_heights_index(uint64_t height) return ret; } -uint64_t miner::calculate_block_subsidy(uint64_t block_height, bool is_testnet) +uint64_t miner::calculate_block_subsidy(uint64_t block_height, bool is_testnet, uint32_t version) { - return uint64_t(3 * coin_price() * pow(0.95, block_height / bucket_size)); + if (version == chain::block_version_pow) { + return calculate_block_subsidy_pow(block_height, is_testnet); + } + + if (version == chain::block_version_pos) { + return calculate_block_subsidy_pos(block_height, is_testnet); + } + + if (version == chain::block_version_dpos) { + return calculate_block_subsidy_dpos(block_height, is_testnet); + } + + throw std::logic_error{"calculate_block_subsidy: unknown block version! " + std::to_string(version)}; +} + +uint64_t miner::calculate_block_subsidy_pow(uint64_t block_height, bool is_testnet) +{ + auto rate = block_height / bucket_size; + if(block_height > pos_enabled_height) + { + rate = pos_enabled_height / bucket_size; + auto period_left = pos_enabled_height % bucket_size; + auto period_right = (bucket_size - period_left) * 2; + auto period_end = pos_enabled_height + period_right; + + if(block_height >= period_end){ + rate = rate + 1 + (block_height - period_end) / (2 * bucket_size); + } + } + + return uint64_t(3 * coin_price() * pow(0.95, rate)); +} + +uint64_t miner::calculate_block_subsidy_pos(uint64_t block_height, bool is_testnet) +{ + return coin_price(1); +} + +uint64_t miner::calculate_block_subsidy_dpos(uint64_t block_height, bool is_testnet) +{ + auto result = calculate_block_subsidy_pow(block_height, is_testnet); + result /= witness::get().get_witness_number(); + return result; } uint64_t miner::calculate_lockblock_reward(uint64_t lcok_heights, uint64_t num) @@ -351,16 +412,17 @@ struct transaction_dependent { : dpendens(_dpendens), is_need_process(_is_need_process) { hash = make_shared(_hash);} }; -miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_address) +uint32_t miner::get_tx_sign_length(transaction_ptr tx) { - block_ptr pblock; - vector transactions; - map transaction_dependents; - previous_out_map_t previous_out_map; - tx_fee_map_t tx_fee_map; - get_transaction(transactions, previous_out_map, tx_fee_map); + return blockchain::validate_block::validate_block::legacy_sigops_count(*tx); +} - vector transaction_prioritys; +bool miner::get_block_transactions( + uint64_t last_height, + std::vector& txs, std::vector& reward_txs, + uint64_t& total_fee, uint32_t& total_tx_sig_length) +{ + block_ptr pblock; block_chain_impl& block_chain = node_.chain_impl(); uint64_t current_block_height = 0; @@ -368,32 +430,40 @@ miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_addr if (!block_chain.get_last_height(current_block_height) || !block_chain.get_header(prev_header, current_block_height)) { log::warning(LOG_HEADER) << "get_last_height or get_header fail. current_block_height:" << current_block_height; - return pblock; + return false; } else { pblock = make_shared(); } + vector transactions; + vector transaction_prioritys; + map transaction_dependents; + previous_out_map_t previous_out_map; + tx_fee_map_t tx_fee_map; + + if (!witness::is_begin_of_epoch(current_block_height + 1)){ + get_transaction(transactions, previous_out_map, tx_fee_map); + } + // Create coinbase tx - pblock->transactions.push_back(*create_coinbase_tx(pay_address, 0, current_block_height + 1, 0, 0)); + pblock->transactions.push_back(*create_coinbase_tx(pay_address_, 0, current_block_height + 1, 0)); // Largest block you're willing to create: - unsigned int block_max_size = blockchain::max_block_size / 2; + uint32_t block_max_size = blockchain::max_block_size / 2; // Limit to betweeen 1K and max_block_size-1K for sanity: - block_max_size = max((unsigned int)1000, min((unsigned int)(blockchain::max_block_size - 1000), block_max_size)); + block_max_size = max((uint32_t)1000, min((uint32_t)(blockchain::max_block_size - 1000), block_max_size)); // How much of the block should be dedicated to high-priority transactions, // included regardless of the fees they pay - unsigned int block_priority_size = 27000; + uint32_t block_priority_size = 27000; block_priority_size = min(block_max_size, block_priority_size); // Minimum block size you want to create; block will be filled with free transactions // until there are no more or the block reaches this size: - unsigned int block_min_size = 0; + uint32_t block_min_size = 0; block_min_size = min(block_max_size, block_min_size); - uint64_t total_fee = 0; - unsigned int block_size = 0; - unsigned int total_tx_sig_length = blockchain::validate_block::validate_block::legacy_sigops_count(*pblock->transactions.begin()); + uint32_t block_size = 0; for (auto tx : transactions) { auto tx_hash = tx->hash(); @@ -406,7 +476,7 @@ miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_addr if (prev_height != max_uint64) { uint64_t input_value = prev_output.value; - priority += (double)input_value * (current_block_height - prev_height + 1); + priority += (double)input_value * (last_height - prev_height + 1); } else { transaction_dependents[input.previous_output.hash].hash = make_shared(tx_hash); @@ -427,13 +497,11 @@ miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_addr transaction_prioritys.push_back(transaction_priority(priority, fee_per_kb, tx_fee, tx)); } - vector blocked_transactions; auto sort_func = sort_by_fee_per_kb; bool is_resort = false; make_heap(transaction_prioritys.begin(), transaction_prioritys.end(), sort_func); transaction_priority *next_transaction_priority = NULL; - uint32_t reward_lock_time = current_block_height - 1; while (!transaction_prioritys.empty() || next_transaction_priority) { transaction_priority temp_priority; @@ -472,8 +540,8 @@ miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_addr int lock_height = chain::operation::get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); coinage_reward_coinbase = create_coinbase_tx(wallet::payment_address::extract(ptx->outputs[0].script), calculate_lockblock_reward(lock_height, output.value), - current_block_height + 1, lock_height, reward_lock_time); - unsigned int tx_sig_length = blockchain::validate_block::validate_block::legacy_sigops_count(*coinage_reward_coinbase); + last_height + 1, lock_height); + uint32_t tx_sig_length = get_tx_sign_length(coinage_reward_coinbase); if (total_tx_sig_length + tx_sig_length >= blockchain::max_block_script_sigops) { continue; } @@ -481,7 +549,6 @@ miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_addr total_tx_sig_length += tx_sig_length; serialized_size += coinage_reward_coinbase->serialized_size(1); coinage_reward_coinbases.push_back(coinage_reward_coinbase); - --reward_lock_time; } } @@ -489,7 +556,7 @@ miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_addr continue; // Legacy limits on sigOps: - unsigned int tx_sig_length = blockchain::validate_block::validate_block::legacy_sigops_count(*ptx); + uint32_t tx_sig_length = get_tx_sign_length(ptx); if (total_tx_sig_length + tx_sig_length >= blockchain::max_block_script_sigops) continue; @@ -507,17 +574,19 @@ miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_addr make_heap(transaction_prioritys.begin(), transaction_prioritys.end(), sort_func); } - size_t c; + uint64_t c; if (!miner::script_hash_signature_operations_count(c, ptx->inputs, transactions) && total_tx_sig_length + tx_sig_length + c >= blockchain::max_block_script_sigops) continue; tx_sig_length += c; - blocked_transactions.push_back(ptx); - for (auto& i : coinage_reward_coinbases) { - pblock->transactions.push_back(*i); + // update txs + txs.push_back(ptx); + for (auto i : coinage_reward_coinbases) { + reward_txs.push_back(i); } + // update fee block_size += serialized_size; total_tx_sig_length += tx_sig_length; total_fee += fee; @@ -530,43 +599,506 @@ miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_addr } } - for (auto i : blocked_transactions) { + return true; +} + +miner::block_ptr miner::create_new_block(const wallet::payment_address& pay_address) +{ + if (get_accept_block_version() == chain::block_version_pow) { + return create_new_block_pow(pay_address); + } + + if (get_accept_block_version() == chain::block_version_pos) { + return create_new_block_pos(pay_address); + } + + if (get_accept_block_version() == chain::block_version_dpos) { + return create_new_block_dpos(pay_address); + } + + throw std::logic_error{"create_new_block: unknown accept block version! " + std::to_string(get_accept_block_version())}; +} + +miner::block_ptr miner::create_new_block_pow(const wallet::payment_address& pay_address) +{ + block_chain_impl& block_chain = node_.chain_impl(); + + // Get last block + uint64_t last_height = 0; + header prev_header; + if (!block_chain.get_last_height(last_height) + || !block_chain.get_header(prev_header, last_height)) { + log::warning(LOG_HEADER) << "get_last_height or get_header fail. last_height:" << last_height; + return nullptr; + } + + uint64_t block_height = last_height + 1; + uint32_t block_time = get_adjust_time(block_height); + + // create block + block_ptr pblock = make_shared(); + + // Fill in header + pblock->header.version = chain::block_version_pow; + pblock->header.number = block_height; + pblock->header.nonce = 0; + pblock->header.mixhash = 0; + pblock->header.timestamp = std::max(block_time, prev_header.timestamp); + pblock->header.previous_block_hash = prev_header.hash(); + pblock->header.bits = get_next_target_required(pblock->header, prev_header, false); + + // Create coinbase tx + transaction_ptr coinbase = create_coinbase_tx(pay_address, 0, block_height, 0); + uint32_t total_tx_sig_length = get_tx_sign_length(coinbase); + + // Get txs + uint64_t total_fee = 0; + vector txs; + vector reward_txs; + get_block_transactions(last_height, txs, reward_txs, total_fee, total_tx_sig_length); + + // Update coinbase reward + coinbase->outputs[0].value = + total_fee + calculate_block_subsidy(block_height, setting_.use_testnet_rules, pblock->header.version); + + if (witness::is_begin_of_epoch(block_height) + && !witness::get().add_witness_vote_result(*coinbase, block_height)) { + return nullptr; + } + + // Put coinbase first + pblock->transactions.push_back(*coinbase); + + // Put coinage reward_txs before txs. + for (auto i : reward_txs) { pblock->transactions.push_back(*i); } - pblock->transactions[0].outputs[0].value = - total_fee + calculate_block_subsidy(current_block_height + 1, setting_.use_testnet_rules); + // Put txs + for (auto i : txs) { + pblock->transactions.push_back(*i); + } // Fill in header - pblock->header.number = current_block_height + 1; pblock->header.transaction_count = pblock->transactions.size(); + pblock->header.merkle = pblock->generate_merkle_root(pblock->transactions); + + return pblock; +} + +miner::block_ptr miner::create_new_block_dpos(const wallet::payment_address& pay_address) +{ + block_chain_impl& block_chain = node_.chain_impl(); + + // Get last block + uint64_t last_height = 0; + header prev_header; + if (!block_chain.get_last_height(last_height) + || !block_chain.get_header(prev_header, last_height)) { + log::warning(LOG_HEADER) << "get_last_height or get_header fail. last_height:" << last_height; + return nullptr; + } + + uint64_t block_height = last_height + 1; + uint32_t block_time = get_adjust_time(block_height); + + if (is_stop_miner(block_height, nullptr)) { + return nullptr; + } + + if (!witness::get().verify_signer(public_key_data_, block_height)) { + // It is not my turn at current height. + sleep_for_mseconds(500, true); + return nullptr; + } + + // create block + block_ptr pblock = make_shared(); + + // Fill in header + pblock->header.version = chain::block_version_dpos; + pblock->header.number = block_height; + pblock->header.nonce = 0; + pblock->header.mixhash = 0; + pblock->header.timestamp = std::max(block_time, prev_header.timestamp); pblock->header.previous_block_hash = prev_header.hash(); + pblock->header.bits = prev_header.bits; + + // Create coinbase tx + transaction_ptr coinbase = create_coinbase_tx(pay_address, 0, block_height, 0); + uint32_t total_tx_sig_length = get_tx_sign_length(coinbase); + + // Get txs + uint64_t total_fee = 0; + vector txs; + vector reward_txs; + get_block_transactions(last_height, txs, reward_txs, total_fee, total_tx_sig_length); + + // Update coinbase reward + coinbase->outputs[0].value = + total_fee + calculate_block_subsidy(block_height, setting_.use_testnet_rules, pblock->header.version); + + if (witness::is_begin_of_epoch(block_height) + && !witness::get().add_witness_vote_result(*coinbase, block_height)) { + return nullptr; + } + + // Put coinbase first + pblock->transactions.push_back(*coinbase); + + // Put coinage reward_txs before txs. + for (auto i : reward_txs) { + pblock->transactions.push_back(*i); + } + + // Put txs + for (auto i : txs) { + pblock->transactions.push_back(*i); + } + + if (!pblock->can_use_dpos_consensus()) { + return nullptr; + } + + // Fill in header + pblock->header.transaction_count = pblock->transactions.size(); pblock->header.merkle = pblock->generate_merkle_root(pblock->transactions); - pblock->header.timestamp = get_adjust_time(pblock->header.number); - pblock->header.version = block_version; - pblock->header.bits = HeaderAux::calculateDifficulty(pblock->header, prev_header); + + // add witness's signature to the current block header + bc::endorsement endorse; + if (!witness::sign(endorse, private_key_, pblock->header)) { + log::error(LOG_HEADER) << "witness sign failed in create_new_block"; + return nullptr; + } + + auto& coinbase_tx = pblock->transactions.front(); + auto& coinbase_script = coinbase_tx.inputs.front().script; + auto& coinbase_input_ops = coinbase_script.operations; + coinbase_input_ops.push_back({ chain::opcode::special, endorse }); + coinbase_input_ops.push_back({ chain::opcode::special, public_key_data_ }); + +#ifdef PRIVATE_CHAIN + log::info(LOG_HEADER) + << "create a DPoS block with signatures at height " << block_height + << ", coinbase input script is " + << coinbase_script.to_string(chain::get_script_context()); +#endif + + return pblock; +} + +miner::block_ptr miner::create_new_block_pos(const wallet::payment_address& pay_address) +{ + block_chain_impl& block_chain = node_.chain_impl(); + + // Get last block + uint64_t last_height = 0; + header prev_header; + if (!block_chain.get_last_height(last_height) + || !block_chain.get_header(prev_header, last_height)) { + log::warning(LOG_HEADER) << "get_last_height or get_header fail. last_height:" << last_height; + return nullptr; + } + + uint64_t block_height = last_height + 1; + + // Check if PoS is eanbled at last_height + if (block_height < pos_enabled_height) { + log::warning(LOG_HEADER) << "PoS is not allowed at block height:" << last_height; + return nullptr; + } + + // Check deposited stake + if (!block_chain.check_pos_capability(last_height, pay_address)) { + log::error(LOG_HEADER) << "PoS mining is not allowed. no enough stake is deposited at address " << pay_address; + sleep_for_mseconds(10 * 1000); + return nullptr; + } + + // check utxo stake + chain::output_info::list stake_outputs; + if (!block_chain.select_utxo_for_staking(last_height, pay_address, stake_outputs, 1000)) { + log::error(LOG_HEADER) << "PoS mining is not allowed. no enough stake is holded at address " << pay_address; + sleep_for_mseconds(10 * 1000); + return nullptr; + } + + // create block + block_ptr pblock = make_shared(); + + // Fill in header + pblock->header.version = chain::block_version_pos; // pos + pblock->header.number = block_height; + pblock->header.previous_block_hash = prev_header.hash(); pblock->header.nonce = 0; pblock->header.mixhash = 0; + pblock->header.bits = get_next_target_required(pblock->header, prev_header, true); + + // create coinbase tx + transaction_ptr coinbase = create_coinbase_tx(pay_address, 0, block_height, 0); + uint32_t total_tx_sig_length = get_tx_sign_length(coinbase); + + // create coinstake + uint32_t start_time = get_adjust_time(block_height); + uint32_t block_time = start_time; + transaction_ptr coinstake(nullptr); + + while (nullptr == coinstake && block_time < (start_time + block_timespan_window / 2)) { + pblock->header.timestamp = std::max(block_time, prev_header.timestamp + 1); + coinstake = create_coinstake_tx(private_key_, pay_address, pblock, stake_outputs); + if (coinstake) { + break; + } + + if (is_stop_miner(block_height, pblock)) { + break; + } + + uint32_t sleep_time = pseudo_random(200, 300); + sleep_for_mseconds(sleep_time); + block_time = get_adjust_time(block_height); + } + + if (nullptr == coinstake) { + return nullptr; + } + + total_tx_sig_length += get_tx_sign_length(coinstake); + + // create pos genesis tx + transaction_ptr genesis_tx(nullptr); + if (!block_chain.pos_exist_before(block_height)) { + genesis_tx = create_pos_genesis_tx(block_height, block_time); + total_tx_sig_length += get_tx_sign_length(genesis_tx); + } + + // Get txs + uint64_t total_fee = 0; + vector txs; + vector reward_txs; + get_block_transactions(last_height, txs, reward_txs, total_fee, total_tx_sig_length); + + // Update coinbase reward + coinbase->outputs[0].value = + total_fee + calculate_block_subsidy(block_height, setting_.use_testnet_rules, pblock->header.version); + + if (witness::is_begin_of_epoch(block_height) + && !witness::get().add_witness_vote_result(*coinbase, block_height)) { + return nullptr; + } + + // Put coinbase first + pblock->transactions.push_back(*coinbase); + + // Put coinstake second + pblock->transactions.push_back(*coinstake); + + // Put pos genesis tx third + if (nullptr != genesis_tx) { + pblock->transactions.push_back(*genesis_tx); + } + + // Put coinage reward_txs before txs. + for (auto i : reward_txs) { + pblock->transactions.push_back(*i); + } + + // Put txs + for (auto i : txs) { + pblock->transactions.push_back(*i); + } + + // Fill in header + pblock->header.transaction_count = pblock->transactions.size(); + pblock->header.merkle = pblock->generate_merkle_root(pblock->transactions); + + // Sign block + if (!sign(pblock->blocksig, private_key_, pblock->header.hash())) { + log::error(LOG_HEADER) << "PoS mining failed. cann't sign block."; + return nullptr; + } +#ifdef PRIVATE_CHAIN + log::info(LOG_HEADER) + << "create a PoS block at height " << block_height + << ", header hash is " << encode_hash(pblock->header.hash()); +#endif return pblock; } -unsigned int miner::get_adjust_time(uint64_t height) const +u256 miner::get_next_target_required(const chain::header& header, const chain::header& prev_header, bool is_staking) { - typedef std::chrono::system_clock wall_clock; - const auto now = wall_clock::now(); - unsigned int t = wall_clock::to_time_t(now); + block_chain_impl& block_chain = node_.chain_impl(); + header::ptr last_header = block_chain.get_last_block_header(prev_header, is_staking); + header::ptr llast_header; + + if (last_header && last_header->number > 2) { + auto height = last_header->number - 1; + chain::header prev_last_header; + if (block_chain.get_header(prev_last_header, height)) { + llast_header = block_chain.get_last_block_header(prev_last_header, is_staking); + } + } + + return HeaderAux::calculate_difficulty(header, last_header, llast_header, is_staking); +} - if (height >= future_blocktime_fork_height) { - return t; +bool miner::sign_coinstake_tx( + const ec_secret& private_key, + transaction_ptr coinstake) +{ + const uint8_t hash_type = chain::signature_hash_algorithm::all; + + for (uint64_t i = 0; i < coinstake->inputs.size(); ++i) { + const chain::script& contract = coinstake->inputs[i].script; + // gen sign + endorsement endorse; + if (!chain::script::create_endorsement(endorse, private_key, + contract, *coinstake, i, hash_type)) { + log::error(LOG_HEADER) << "sign_coinstake_tx: get_input_sign sign failure!"; + return false; + } + + // do script + wallet::ec_private ec_private_key(private_key, 0u, true); + auto &&public_key = ec_private_key.to_public(); + data_chunk public_key_data; + public_key.to_data(public_key_data); + + chain::script ss; + ss.operations.push_back({chain::opcode::special, endorse}); + ss.operations.push_back({chain::opcode::special, public_key_data}); + + // if pre-output script is deposit tx. + if (contract.pattern() == chain::script_pattern::pay_key_hash_with_lock_height) { + uint64_t lock_height = chain::operation::get_lock_height_from_pay_key_hash_with_lock_height( + contract.operations); + ss.operations.push_back({chain::opcode::special, script_number(lock_height).data()}); + } + + coinstake->inputs[i].script = ss; } - else { - unsigned int t_past = get_median_time_past(height); - return max(t, t_past + 1); + return true; +} + +miner::transaction_ptr miner::create_pos_genesis_tx(uint64_t block_height, uint32_t block_time) +{ + auto& fmt = boost::format("MVS start POS at %1%") % timestamp_to_string(block_time); + const string text(fmt.str()); + + transaction_ptr genesis_tx = make_shared(); + + genesis_tx->inputs.resize(1); + genesis_tx->inputs[0].previous_output = output_point(null_hash, max_uint32); + genesis_tx->inputs[0].script.operations = {{chain::opcode::raw_data, {text.begin(), text.end()}}}; + + genesis_tx->outputs.resize(1); + wallet::payment_address pay_address(get_foundation_address(setting_.use_testnet_rules)); + genesis_tx->outputs[0].script.operations = chain::operation::to_pay_key_hash_pattern(short_hash(pay_address)); + genesis_tx->outputs[0].value = pos_genesis_reward; + + return genesis_tx; +} + +miner::transaction_ptr miner::create_coinstake_tx( + const ec_secret& private_key, + const wallet::payment_address& pay_address, + block_ptr pblock, const chain::output_info::list& stake_outputs) +{ + block_chain_impl& block_chain = node_.chain_impl(); + transaction_ptr coinstake = make_shared(); + coinstake->version = transaction_version::first; + + uint64_t nCredit = 0; + for (const auto& stake: stake_outputs) { + if (!block_chain.check_pos_utxo_height_and_value( + stake.height, pblock->header.number, stake.data.value)) { + continue; + } + + if (MinerAux::verify_stake(pblock->header, stake)) { + coinstake->inputs.clear(); + coinstake->outputs.clear(); + + nCredit = stake.data.value; + + // generate inputs + coinstake->inputs.emplace_back(stake.point, stake.data.script, max_input_sequence); + + // generate outputs + coinstake->outputs.emplace_back(0, chain::script(), ATTACH_NULL_TYPE); + + break; + } } + + if (coinstake->inputs.empty()) + return nullptr; + + const uint64_t pos_split_limit = pos_stake_min_value * 2; + + // Attempt to add more inputs + for (const auto& stake: stake_outputs) { + if (stake.data.value >= pos_stake_min_value) { + continue; + } + + if (nCredit >= pos_split_limit) { + break; + } + + if (coinstake->inputs.size() >= pos_coinstake_max_utxos) { + break; + } + + coinstake->inputs.emplace_back(stake.point, stake.data.script, max_input_sequence); + nCredit += stake.data.value; + } + + auto&& script_operation = chain::operation::to_pay_key_hash_pattern(short_hash(pay_address)); + // auto payment_script = chain::script{script_operation}; + + coinstake->outputs.emplace_back(nCredit, chain::script{script_operation}, + attachment(ETP_TYPE, 1, chain::etp(nCredit))); + + // split the output + if (nCredit >= pos_split_limit) { + auto value = nCredit - pos_stake_min_value; + coinstake->outputs[1].value = value; + coinstake->outputs[1].attach_data = {ETP_TYPE, 1, chain::etp(value)}; + + value = pos_stake_min_value; + coinstake->outputs.emplace_back(value, chain::script{script_operation}, + attachment(ETP_TYPE, 1, chain::etp(value))); + } + + // sign coinstake + if (!sign_coinstake_tx(private_key, coinstake)) { + return nullptr; + } + + return coinstake; +} + +chain::block_version miner::get_accept_block_version() const +{ + return accept_block_version_; +} + +void miner::set_accept_block_version(chain::block_version v) +{ + accept_block_version_ = v; } -unsigned int miner::get_median_time_past(uint64_t height) const +uint32_t miner::get_adjust_time(uint64_t height) const +{ + typedef std::chrono::system_clock wall_clock; + const auto now = wall_clock::now(); + unsigned int t = wall_clock::to_time_t(now); + return t; +} + +uint32_t miner::get_median_time_past(uint64_t height) const { block_chain_impl& block_chain = node_.chain_impl(); @@ -611,19 +1143,38 @@ std::string to_string(_T const& _t) return o.str(); } -void miner::work(const wallet::payment_address pay_address) +void miner::sleep_for_mseconds(uint32_t interval, bool force) { - log::info(LOG_HEADER) << "solo miner start with address: " << pay_address.encoded(); + if (force || (get_accept_block_version() == chain::block_version_pos)) { + std::this_thread::sleep_for(std::chrono::milliseconds(interval)); + } +} + +void miner::work(const wallet::payment_address& pay_address) +{ + log::info(LOG_HEADER) + << "solo miner start with address: " + << pay_address.encoded() + << ", accept consensus " + chain::get_block_version(get_accept_block_version()); + while (state_ != state::exit_) { block_ptr block = create_new_block(pay_address); if (block) { - if (MinerAux::search(block->header, std::bind(&miner::is_stop_miner, this, block->header.number))) { + bool can_store = (get_accept_block_version() == chain::block_version_pos) + || block->header.version == chain::block_version_dpos + || MinerAux::search(block->header, std::bind(&miner::is_stop_miner, this, block->header.number, block)); + if (can_store) { boost::uint64_t height = store_block(block); if (height == 0) { + sleep_for_mseconds(500); continue; } - log::info(LOG_HEADER) << "solo miner create new block at heigth:" << height; + log::info(LOG_HEADER) << "solo miner create " + << chain::get_block_version(block->header) + << " block at height: " << height + << ", bits: " << block->header.bits + << ", time: " << timestamp_to_string(block->header.timestamp); ++new_block_number_; if ((new_block_limit_ != 0) && (new_block_number_ >= new_block_limit_)) { @@ -632,31 +1183,44 @@ void miner::work(const wallet::payment_address pay_address) break; } } + + sleep_for_mseconds(1000); + } + else { + sleep_for_mseconds(300); } } } -bool miner::is_stop_miner(uint64_t block_height) const +bool miner::is_stop_miner(uint64_t block_height, block_ptr block) const { - return (state_ == state::exit_) || (get_height() > block_height); + if (state_ == state::exit_) { + return true; + } + + auto latest_height = get_height(); + if ((latest_height > block_height) || + (block && latest_height >= block->header.number)) { + return true; + } + + return false; } bool miner::start(const wallet::payment_address& pay_address, uint16_t number) { + if (get_accept_block_version() == chain::block_version_dpos) { + if (private_key_.empty() || public_key_data_.empty()) { + return false; + } + } + if (!thread_) { new_block_limit_ = number; thread_.reset(new boost::thread(bind(&miner::work, this, pay_address))); } - return true; -} -bool miner::start(const std::string& public_key, uint16_t number) -{ - wallet::payment_address pay_address = libbitcoin::wallet::ec_public(public_key).to_payment_address(); - if (pay_address) { - return start(pay_address, number); - } - return false; + return true; } bool miner::stop() @@ -680,21 +1244,7 @@ uint64_t miner::get_height() const return height; } -bool miner::set_miner_public_key(const string& public_key) -{ - libbitcoin::wallet::ec_public ec_public_key(public_key); - pay_address_ = ec_public_key.to_payment_address(); - if (pay_address_) { - log::debug(LOG_HEADER) << "set_miner_public_key[" << pay_address_.encoded() << "] success"; - return true; - } - else { - log::error(LOG_HEADER) << "set_miner_public_key[" << public_key << "] is not availabe!"; - return false; - } -} - -bool miner::set_miner_payment_address(const bc::wallet::payment_address& address) +bool miner::set_miner_payment_address(const wallet::payment_address& address) { if (address) { log::debug(LOG_HEADER) << "set_miner_payment_address[" << address.encoded() << "] success"; @@ -756,22 +1306,18 @@ bool miner::put_result(const std::string& nonce, const std::string& mix_hash, } if (header_hash == "0x" + to_string(HeaderAux::hashHead(new_block_->header))) { - auto s_nonce = "0x" + nonce; - uint64_t n_nonce; -#ifdef MAC_OSX - size_t sz = 0; - n_nonce = std::stoull(s_nonce, &sz, 16); -#else - if (sscanf(s_nonce.c_str(), "%lx", &n_nonce) != 1) { - log::error(LOG_HEADER) << "nonce change error\n"; - return false; + uint64_t height = 0; + try { + uint64_t n_nonce = std::stoull(nonce, 0, 16); + // nounce_mask defination is moved to the caller + uint64_t nonce_t = n_nonce ^ nounce_mask; + new_block_->header.nonce = (u64) nonce_t; + new_block_->header.mixhash = (FixedHash<32>::Arith)h256(mix_hash); + height = store_block(new_block_); + } catch (const std::exception& e) { + log::debug(LOG_HEADER) << "put_result caught exception: " << e.what(); } -#endif - // nounce_mask defination is moved to the caller by chengzhiping 2018-3-15. - uint64_t nonce_t = n_nonce ^ nounce_mask; - new_block_->header.nonce = (u64) nonce_t; - new_block_->header.mixhash = (FixedHash<32>::Arith)h256(mix_hash); - uint64_t height = store_block(new_block_); + if (height != 0) { log::debug(LOG_HEADER) << "put_result nonce:" << nonce << " mix_hash:" << mix_hash << " success with height:" << height; @@ -822,18 +1368,56 @@ bool miner::get_block_header(chain::header& block_header, const string& para) height = 0; } else if (para[0] >= '0' && para[0] <= '9') { - height = atol(para.c_str()); + height = std::stoull(para); } else { return false; } - if (block_chain.get_header(block_header, height)) + if (block_chain.get_header(block_header, height)) { + block_header.transaction_count = block_chain.get_transaction_count(height); return true; + } } return false; } +bool miner::is_witness() const +{ + if (public_key_data_.empty()) { + return false; + } + return witness::get().is_witness(witness::to_witness_id(public_key_data_)); } + +bool miner::set_pub_and_pri_key(const std::string& pubkey, const std::string& prikey) +{ + // set private key + if (!decode_base16(private_key_, prikey) + || !bc::verify(private_key_) || private_key_.empty()) { + log::error(LOG_HEADER) << "miner verify private key failed"; + return false; + } + + // set public key, ref. signrawtx + wallet::ec_private ec_private_key(private_key_, 0u, true); + auto&& public_key = ec_private_key.to_public(); + public_key.to_data(public_key_data_); + + if (public_key_data_.empty()) { + log::error(LOG_HEADER) << "miner set mining public key failed"; + return false; + } + +#ifdef PRIVATE_CHAIN + log::info(LOG_HEADER) + << "miner set mining public key " << encode_base16(public_key_data_); +#endif + + return true; } + + +} // consensus +} // libbitcoin diff --git a/src/lib/consensus/witness.cpp b/src/lib/consensus/witness.cpp new file mode 100644 index 000000000..99a125a1b --- /dev/null +++ b/src/lib/consensus/witness.cpp @@ -0,0 +1,675 @@ +/** + * Copyright (c) 2016-2018 metaverse core developers (see MVS-AUTHORS) + * + * This file is part of metaverse. + * + * metaverse is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include +#include +#include +#include +#include +#include +#include + +#define LOG_HEADER "Witness" + +namespace libbitcoin { +namespace consensus { + +uint32_t witness::pow_check_point_height = 100; +uint64_t witness::witness_enable_height = 2000000; +uint32_t witness::witness_number = 23; +uint32_t witness::epoch_cycle_height = 20000; +uint32_t witness::register_witness_lock_height = 10000; +uint64_t witness::witness_lock_threshold = 1000*(1e8); // ETP bits +uint32_t witness::vote_maturity = 24; + +const uint32_t witness::max_candidate_count = 10000; +const uint32_t witness::witness_register_fee = 123456789; +const std::string witness::witness_registry_did = "witness_registry"; + +witness* witness::instance_ = nullptr; + +uint32_t hash_digest_to_uint(const hash_digest& hash) +{ + uint32_t result = 0; + for (size_t i = 0; i < hash_size; i += 4) { + result ^= ((hash[i]<<24) + (hash[i+1]<<16) + (hash[i+2]<<8) + hash[i+3]); + } + return result; +}; + +witness::witness(p2p_node& node) + : node_(node) + , setting_(node_.chain_impl().chain_settings()) + , witness_list_() + , validate_block_(nullptr) + , mutex_() +{ +} + +witness::~witness() +{ +} + +void witness::init(p2p_node& node) +{ + static witness s_instance(node); + instance_ = &s_instance; + + if (instance_->setting_.use_testnet_rules) { + witness::pow_check_point_height = 100; + witness::witness_enable_height = 1000000; + witness::witness_number = 5; + witness::epoch_cycle_height = 1000; + witness::register_witness_lock_height = 500; + witness::witness_lock_threshold = 10*(1e8); // ETP bits + witness::vote_maturity = 6; + } + +#ifdef PRIVATE_CHAIN + witness::pow_check_point_height = 100; + witness::witness_enable_height = 100; + witness::witness_number = 23; + witness::epoch_cycle_height = 100; + witness::register_witness_lock_height = 50; + witness::witness_lock_threshold = 1*(1e8); // ETP bits + witness::vote_maturity = 6; +#endif + + BITCOIN_ASSERT(max_candidate_count >= witness_number); + BITCOIN_ASSERT(epoch_cycle_height >= vote_maturity); +} + +witness& witness::create(p2p_node& node) +{ + static std::once_flag flag; + std::call_once(flag, &witness::init, std::ref(node)); + return *instance_; +} + +witness& witness::get() +{ + BITCOIN_ASSERT_MSG(instance_, "use witness::create() before witness::get()"); + return *instance_; +} + +witness::iterator witness::finds(list& l, const witness_id& id) +{ + auto cmp = [&id](const witness_id& item){return id == item;}; + return std::find_if(std::begin(l), std::end(l), cmp); +} + +witness::const_iterator witness::finds(const list& l, const witness_id& id) +{ + auto cmp = [&id](const witness_id& item){return id == item;}; + return std::find_if(std::begin(l), std::end(l), cmp); +} + +bool witness::exists(const list& l, const witness_id& id) +{ + return finds(l, id) != l.end(); +} + +witness::list witness::get_witness_list() const +{ + shared_lock lock(mutex_); + return witness_list_; +} + +void witness::swap_witness_list(list& l) +{ + unique_lock ulock(mutex_); + witness_list_.swap(l); +} + +bool witness::is_witness(const witness_id& id) const +{ + shared_lock lock(mutex_); + return (!id.empty()) && exists(witness_list_, id); +} + +std::string witness::show_list() const +{ + shared_lock lock(mutex_); + return show_list(witness_list_); +} + +std::string witness::show_list(const list& witness_list) +{ + std::string res; + res += "witness : [\n"; + for (const auto& witness : witness_list) { + res += "\t" + witness_to_string(witness) + "\n"; + } + res += "] "; + return res; +} + +size_t witness::get_witness_number() +{ + shared_lock lock(mutex_); + return witness_list_.size(); +} + +chain::output witness::create_witness_vote_result(uint64_t height) +{ + chain::output output; + output.value = 0; + if (!calc_witness_list(height)) { + return output; + } + + shared_lock lock(mutex_); + + auto& ops = output.script.operations; + + auto mixhash = calc_mixhash(witness_list_); + data_chunk chunk = to_chunk(h256(mixhash).asBytes()); + ops.push_back({chain::opcode::special, chunk}); + + return output; +} + +bool witness::add_witness_vote_result(chain::transaction& coinbase_tx, uint64_t block_height) +{ + BITCOIN_ASSERT(coinbase_tx.is_coinbase()); + if (witness::is_begin_of_epoch(block_height)) { + auto&& vote_output = create_witness_vote_result(block_height); + if (vote_output.script.operations.empty()) { + log::error(LOG_HEADER) << "create_witness_vote_result failed"; + return false; + } + + coinbase_tx.outputs.emplace_back(vote_output); + log::debug(LOG_HEADER) << "create_witness_vote_result complete. " << vote_output.to_string(1); + } + + return true; +} + +bool witness::calc_witness_list(uint64_t height) +{ + list witness_list; + if (!calc_witness_list(witness_list, height)) { + return false; + } + + swap_witness_list(witness_list); + return true; +} + +bool witness::calc_witness_list(list& witness_list, uint64_t height) const +{ + if (!is_begin_of_epoch(height)) { +#ifdef PRIVATE_CHAIN + log::error(LOG_HEADER) << "calc witness list must at the begin of epoch, not " << height; +#endif + return false; + } + + witness_list.clear(); + + auto& chain = const_cast(node_.chain_impl()); + auto did_detail = chain.get_registered_did(witness::witness_registry_did); + if (!did_detail) { + return true; + } + + auto stakeholders = chain.get_register_witnesses_with_stake( + did_detail->get_address(), "", 0, height + register_witness_lock_height); + if (stakeholders == nullptr || stakeholders->empty()) { + return true; + } + + if (stakeholders->size() <= witness_number) { + for (const auto& stake_holder : *stakeholders) { + witness_list.emplace_back(to_chunk(stake_holder->address())); + } + } + else { + chain::header header; + if (!get_header(header, height-1)) { + return false; + } + + if (stakeholders->size() > max_candidate_count) { + std::sort(stakeholders->begin(), stakeholders->end(), + [](const fts_stake_holder::ptr& h1, const fts_stake_holder::ptr& h2){ + return h1->stake() > h2->stake(); // descendly + }); + + stakeholders->resize(max_candidate_count); + } + + // pick witness_number candidates as witness randomly by fts + uint32_t seed = hash_digest_to_uint(header.hash()); + auto selected_holders = fts::select_by_fts(*stakeholders, seed, witness_number); + for (const auto& stake_holder : *selected_holders) { + witness_list.emplace_back(to_chunk(stake_holder->address())); + } + } + +#ifdef PRIVATE_CHAIN + log::info(LOG_HEADER) +#else + log::debug(LOG_HEADER) +#endif + << "calc_witness_list at height " << height << ", " << show_list(witness_list); + return true; +} + +u256 witness::calc_mixhash(const list& witness_list) +{ + RLPStream s; + s << witness_list.size(); + for (const auto& witness : witness_list) { + s << bitcoin_hash(witness); + } + auto mix_hash = sha3(s.out()); + return mix_hash; +} + +bool witness::verify_vote_result(const chain::block& block, list& witness_list) const +{ + const auto& transactions = block.transactions; + + // check size of transactions + if (transactions.size() != 1) { + return false; + } + + auto& coinbase_tx = transactions.front(); + + // check size of outputs + if (coinbase_tx.outputs.size() != 2) { + log::debug(LOG_HEADER) + << "in verify_vote_result -> no extra output to store witness mixhash, height " << block.header.number; + return false; + } + + auto& ops = coinbase_tx.outputs.back().script.operations; + + // check operation of script where mixhash is stored. + if ((ops.size() != 1) || !chain::operation::is_push_only(ops)) { + return false; + } + + if (!calc_witness_list(witness_list, block.header.number)) { + log::debug(LOG_HEADER) + << "in verify_vote_result -> calc_witness_list failed, height " << block.header.number; + return false; + } + + auto buff = chain::operation::factory_from_data(ops[0].to_data()).data; + auto stored_mixhash = (h256::Arith)(h256((const uint8_t*)&buff[0], h256::ConstructFromPointer)); + auto calced_mixhash = (h256)calc_mixhash(witness_list); + if (calced_mixhash != stored_mixhash) { + log::debug(LOG_HEADER) + << "in verify_vote_result -> verify mixhash failed, height " << block.header.number + << ", stored_mixhash = " << stored_mixhash + << ", calced_mixhash = " << calced_mixhash; + return false; + } + + return true; +} + +chain::block::ptr witness::fetch_vote_result_block(uint64_t height) +{ + auto vote_height = get_vote_result_height(height); + if (vote_height == 0) { + return nullptr; + } + + std::promise p; + chain::block::ptr sp_block; + + node_.chain_impl().fetch_block(vote_height, + [&p, &sp_block](const code & ec, chain::block::ptr block){ + if (ec) { + p.set_value(ec); + return; + } + sp_block = block; + p.set_value(error::success); + }); + + auto result = p.get_future().get(); + if (result) { + return nullptr; + } + return sp_block; +} + +bool witness::update_witness_list(uint64_t height) +{ + if (!is_witness_enabled(height)) { + list empty; + swap_witness_list(empty); + return true; + } + + auto sp_block = fetch_vote_result_block(height); + if (!sp_block) { +#ifdef PRIVATE_CHAIN + log::info(LOG_HEADER) + << "fetch vote result block failed at height " << height; +#endif + return false; + } + return update_witness_list(*sp_block); +} + +bool witness::update_witness_list(const chain::block& block) +{ + list witness_list; + if (!verify_vote_result(block, witness_list)) { +#ifdef PRIVATE_CHAIN + log::info(LOG_HEADER) + << "verify vote result failed at height " << block.header.number; +#endif + return false; + } + + swap_witness_list(witness_list); + return true; +} + +bool witness::init_witness_list() +{ + if (!is_dpos_enabled()) { + return true; + } + + auto& chain = node_.chain_impl(); + chain.set_sync_disabled(true); + unique_lock lock(chain.get_mutex()); + + uint64_t height = 0; + if (!chain.get_last_height(height)) { + log::info(LOG_HEADER) << "get last height failed"; + return false; + } + + if (consensus::witness::is_witness_enabled(height)) { + if (!consensus::witness::get().update_witness_list(height)) { + log::info(LOG_HEADER) << "update witness list failed at height " << height; + return false; + } + log::info(LOG_HEADER) << "update witness list succeed at height " << height; + } + + chain.set_sync_disabled(false); + return true; +} + +const witness::witness_id& witness::get_witness(uint32_t slot) const +{ + shared_lock lock(mutex_); + return witness_list_.at(slot); +} + +uint32_t witness::get_slot_num(const witness_id& id) const +{ + shared_lock lock(mutex_); + auto pos = finds(witness_list_, id); + if (pos != witness_list_.end()) { + return std::distance(witness_list_.cbegin(), pos); + } + + return max_uint32; +} + +uint32_t witness::calc_slot_num(uint64_t block_height) const +{ + auto size = witness::get().get_witness_number(); + if (!is_witness_enabled(block_height) || size == 0) { + return max_uint32; + } + + auto calced_slot_num = ((block_height - witness_enable_height) % size); + auto round_begin_height = get_round_begin_height(block_height); + if (is_begin_of_epoch(round_begin_height)) { + return calced_slot_num; + } + + // remember latest calced offset to reuse it + static std::pair height_offset = {0, 0}; + + uint32_t offset = 0; + if (round_begin_height == height_offset.first) { + offset = height_offset.second; + } + else { + chain::header header; + for (auto i = round_begin_height - size; i < round_begin_height; ++i) { + if (!get_header(header, i)) { + return max_uint32; + } + offset ^= hash_digest_to_uint(header.hash()); + } + + offset %= size; + height_offset = std::make_pair(round_begin_height, offset); + } + + return (calced_slot_num + offset) % size; +} + +witness::public_key_t witness::witness_to_public_key(const witness_id& id) +{ + public_key_t public_key; + decode_base16(public_key, witness_to_string(id)); + return public_key; +} + +std::string witness::witness_to_string(const witness_id& id) +{ + return std::string(std::begin(id), std::end(id)); +} + +std::string witness::endorse_to_string(const endorsement& endorse) +{ + return encode_base16(endorse); +} + +witness::witness_id witness::to_witness_id(const public_key_t& public_key) +{ + return to_chunk(encode_base16(public_key)); +} + +std::string witness::to_string(const public_key_t& public_key) +{ + return encode_base16(public_key); +} + +bool witness::sign(endorsement& out, const ec_secret& secret, const chain::header& h) +{ + const auto sighash = h.hash(); + + ec_signature signature; + if (!bc::sign(signature, secret, sighash) || !bc::encode_signature(out, signature)) { + return false; + } + + out.push_back(h.number & 0xff); + return true; +} + +bool witness::verify_sign(const endorsement& out, const public_key_t& public_key, const chain::header& h) +{ + if (public_key.empty() || !is_public_key(public_key)) { +#ifdef PRIVATE_CHAIN + log::error(LOG_HEADER) << "verify witness sign failed, public key is wrong: " + << to_string(public_key); +#endif + return false; + } + + if (out.back() != (h.number & 0xff)) { +#ifdef PRIVATE_CHAIN + log::error(LOG_HEADER) << "verify witness sign failed, suffix wrong"; +#endif + return false; + } + + auto distinguished = out; + distinguished.pop_back(); + + ec_signature signature; + + if (!parse_signature(signature, distinguished, true)) { +#ifdef PRIVATE_CHAIN + log::error(LOG_HEADER) << "verify witness sign failed, parse_signature failed"; +#endif + return false; + } + + const auto sighash = h.hash(); + + if (!bc::verify_signature(public_key, sighash, signature)) { +#ifdef PRIVATE_CHAIN + log::error(LOG_HEADER) + << "verify witness signature failed at height " << (h.number + 1) + << ", public_key is " << to_string(public_key) + << ", signature is " << endorse_to_string(out) + << ", hash is " << encode_hash(sighash); +#endif + return false; + } + + return true; +} + +bool witness::verify_signer(const public_key_t& public_key, uint64_t height) const +{ + auto witness_slot_num = get_slot_num(to_witness_id(public_key)); + return verify_signer(witness_slot_num, height); +} + +bool witness::verify_signer(uint32_t witness_slot_num, uint64_t height) const +{ + auto size = witness::get().get_witness_number(); + if (witness_slot_num >= size) { + return false; + } + + auto calced_slot_num = calc_slot_num(height); + if (calced_slot_num == witness_slot_num) { + return true; + } + + return false; +} + +bool witness::is_dpos_enabled() +{ + return false; +} + +bool witness::is_witness_enabled(uint64_t height) +{ + return is_dpos_enabled() && height >= witness_enable_height; +} + +uint64_t witness::get_height_in_epoch(uint64_t height) +{ + return (height - witness_enable_height) % epoch_cycle_height; +} + +// vote result is stored in the beginning of each epoch +uint64_t witness::get_vote_result_height(uint64_t height) +{ + return is_witness_enabled(height) ? (height - get_height_in_epoch(height)) : 0; +} + +bool witness::is_begin_of_epoch(uint64_t height) +{ + return is_witness_enabled(height) && get_height_in_epoch(height) == 0; +} + +bool witness::is_between_vote_maturity_interval(uint64_t height) +{ + if (!is_witness_enabled(height)) { + return false; + } + + // [0 .. vote_maturity) + if (get_height_in_epoch(height) < vote_maturity) { + return true; + } + + // [epoch_cycle_height-vote_maturity .. epoch_cycle_height) + if (epoch_cycle_height - get_height_in_epoch(height) <= vote_maturity) { + return true; + } + return false; +} + +bool witness::is_in_same_epoch(uint64_t height1, uint64_t height2) +{ + auto r = std::minmax(height1, height2); + auto vote1 = get_vote_result_height(r.first); + auto vote2 = get_vote_result_height(r.second); + + if (vote1 == vote2) { + return true; + } + + if (vote2 == vote1 + epoch_cycle_height) { + return vote2 - r.first < vote_maturity; + } + + return false; +} + +uint64_t witness::get_round_begin_height(uint64_t height) +{ + auto size = witness::get().get_witness_number(); + return is_witness_enabled(height) && size > 0 + ? height - ((height - witness_enable_height) % size) + : 0; +} + +bool witness::get_header(chain::header& out_header, uint64_t height) const +{ + if (validate_block_) { + return validate_block_->get_header(out_header, height); + } + return node_.chain_impl().get_header(out_header, height); +} + +void witness::set_validate_block(const validate_block* validate_block) +{ + validate_block_ = validate_block; +} + +witness_with_validate_block_context::witness_with_validate_block_context( + witness& w, const validate_block* v) + : witness_(w) +{ + witness_.set_validate_block(v); +} + +witness_with_validate_block_context::~witness_with_validate_block_context() +{ + witness_.set_validate_block(nullptr); +} + +} // consensus +} // libbitcoin diff --git a/src/lib/database/data_base.cpp b/src/lib/database/data_base.cpp index b508cf1f8..2f5a757e8 100644 --- a/src/lib/database/data_base.cpp +++ b/src/lib/database/data_base.cpp @@ -809,17 +809,11 @@ void data_base::push(const block& block, uint64_t height) const auto tx_hash = tx.hash(); timestamp_ = block.header.timestamp; // for address_asset_database store_input/store_output used only + // Add inputs if (!tx.is_coinbase()) push_inputs(tx_hash, height, tx.inputs); - // std::string didaddress = tx.get_did_transfer_old_address(); - // if (!didaddress.empty()) { - // data_chunk data(didaddress.begin(), didaddress.end()); - // short_hash key = ripemd160_hash(data); - // address_dids.delete_old_did(key); - // } - // Add outputs push_outputs(tx_hash, height, tx.outputs); @@ -930,17 +924,19 @@ void data_base::push_stealth(const hash_digest& tx_hash, size_t height, } } -chain::block data_base::pop() +bool data_base::pop(chain::block& block) { size_t height; - DEBUG_ONLY(const auto result =) blocks.top(height); + auto result = blocks.top(height); BITCOIN_ASSERT_MSG(result, "Pop on empty database."); + if (!result) { + return false; + } const auto block_result = blocks.get(height); const auto count = block_result.transaction_count(); // Build the block for return. - chain::block block; block.header = block_result.header(); block.transactions.reserve(count); auto& txs = block.transactions; @@ -950,14 +946,14 @@ chain::block data_base::pop() const auto tx_hash = block_result.transaction_hash(tx); const auto tx_result = transactions.get(tx_hash); - //fix a bug ,synchronize block may destroy the database - /*if (!tx_result) { - continue; - }*/ - BITCOIN_ASSERT(tx_result); BITCOIN_ASSERT(tx_result.height() == height); - BITCOIN_ASSERT(tx_result.index() == static_cast(tx)); + BITCOIN_ASSERT(tx_result.index() == tx); + + //fix a bug ,synchronize block may destroy the database + if (!(tx_result && tx_result.height() == height && tx_result.index() == tx)) { + return false; + } // TODO: the deserialization should cache the hash on the tx. // Deserialize the transaction and move it to the block. @@ -983,8 +979,7 @@ chain::block data_base::pop() // Synchronise everything that was changed. synchronize(); - // Return the block. - return block; + return true; } void data_base::pop_inputs(const input::list& inputs, size_t height) @@ -1129,7 +1124,6 @@ void data_base::push_etp(const etp& etp, const short_hash& key, static_cast::type>(business_kind::etp), timestamp_, etp); address_assets.sync(); - } void data_base::push_etp_award(const etp_award& award, const short_hash& key, diff --git a/src/lib/database/databases/block_database.cpp b/src/lib/database/databases/block_database.cpp index 0f78801d5..e2932d615 100644 --- a/src/lib/database/databases/block_database.cpp +++ b/src/lib/database/databases/block_database.cpp @@ -168,10 +168,14 @@ void block_database::store(const block& block, size_t height) for (const auto& tx: block.transactions) serial.write_hash(tx.hash()); + + if (block.header.is_proof_of_stake()){ + serial.write_data(block.blocksig); + } }; const auto key = block.header.hash(); - const auto value_size = 148 + 4 + 4 + tx_count * hash_size; + const auto value_size = 148 + 4 + 4 + tx_count * hash_size + (block.header.is_proof_of_stake() ? sizeof(block.blocksig): 0); // Write block header, height, tx count and hashes to hash table. const auto position = lookup_map_.store(key, write, value_size); diff --git a/src/lib/database/memory/allocator.cpp b/src/lib/database/memory/allocator.cpp index 5ac760788..a2abb663b 100644 --- a/src/lib/database/memory/allocator.cpp +++ b/src/lib/database/memory/allocator.cpp @@ -37,17 +37,17 @@ allocator::allocator(shared_mutex& mutex) // Begin Critical Section // Acquire exclusive downgradable lock. - mutex_.lock(); + mutex_.lock_upgrade(); } uint8_t* allocator::buffer() { - BITCOIN_ASSERT_MSG(data_ != nullptr, "Downgrade must be called."); return data_; } void allocator::increment(size_t value) { + BITCOIN_ASSERT_MSG(data_ != nullptr, "Downgrade must be called."); BITCOIN_ASSERT((size_t)data_ <= bc::max_size_t - value); data_ += value; } @@ -55,10 +55,8 @@ void allocator::increment(size_t value) // protected/friend void allocator::downgrade(uint8_t* data) { - BITCOIN_ASSERT_MSG(data != nullptr, "Invalid pointer value."); - // Downgrade the exclusive lock. - mutex_.unlock_and_lock_shared(); + mutex_.unlock_upgrade_and_lock_shared(); // Save protected pointer. data_ = data; diff --git a/src/lib/database/memory/memory_map.cpp b/src/lib/database/memory/memory_map.cpp index 70b07a8b3..b0908beeb 100644 --- a/src/lib/database/memory/memory_map.cpp +++ b/src/lib/database/memory/memory_map.cpp @@ -318,24 +318,45 @@ memory_ptr memory_map::reserve(size_t size) // the required allocation and all resizing before writing a block. memory_ptr memory_map::reserve(size_t size, size_t expansion) { - // Critical Section (internal) + /////////////////////////////////////////////////////////////////////////// + // Internally preventing resize during close is not possible because of + // cross-file integrity. So we must coalesce all threads before closing. + + // Critical Section /////////////////////////////////////////////////////////////////////////// const auto memory = REMAP_ALLOCATOR(mutex_); + // The store should only have been closed after all threads terminated. + if (closed_) + { + REMAP_DOWNGRADE(memory, data_); + throw std::runtime_error("Resize failure, store already closed."); + } + if (size > file_size_) { - const auto target = size * expansion / EXPANSION_DENOMINATOR; + const size_t target = size * expansion / EXPANSION_DENOMINATOR; + + mutex_.unlock_upgrade_and_lock(); + //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // TODO: isolate cause and if recoverable (disk size) return nullptr. + // All existing database pointers are invalidated by this call. if (!truncate_mapped(target)) { handle_error("resize", filename_); throw std::runtime_error("Resize failure, disk space may be low."); } + + //--------------------------------------------------------------------- + mutex_.unlock_and_lock_upgrade(); } logical_size_ = size; REMAP_DOWNGRADE(memory, data_); + // Always return in shared lock state. + // The critical section does not end until this shared pointer is freed. return memory; /////////////////////////////////////////////////////////////////////////// } diff --git a/src/lib/database/mman-win32/mman.c b/src/lib/database/mman-win32/mman.c index fe9479963..9a7f0777d 100644 --- a/src/lib/database/mman-win32/mman.c +++ b/src/lib/database/mman-win32/mman.c @@ -1,4 +1,4 @@ -/* mman-win32 from code.google.com/p/mman-win32 (MIT License). */ +/* mman-win32 derived from code.google.com/p/mman-win32 (MIT License). */ #include "mman.h" @@ -13,19 +13,26 @@ #define FILE_MAP_EXECUTE 0x0020 #endif -/* private functions */ +/* local utilities */ -static int error(const DWORD err, const int deferr) -{ - if (err == 0) - return 0; +static const int large = (sizeof(oft__) > sizeof(DWORD)); - /* TODO: implement. */ +static int last_error(int default_value) +{ + /* TODO: implement full mapping to standard codes. */ - return err; + switch (GetLastError()) + { + case ERROR_INVALID_HANDLE: + return EBADF; + case ERROR_DISK_FULL: + return ENOSPC; + default: + return default_value; + } } -static DWORD protect_page(const int prot) +static DWORD protect_page(int prot) { DWORD protect = 0; @@ -45,7 +52,7 @@ static DWORD protect_page(const int prot) return protect; } -static DWORD protect_file(const int prot) +static DWORD protect_file(int prot) { DWORD desired_access = 0; @@ -77,19 +84,16 @@ void* mmap(void* addr, size_t len, int prot, int flags, int fildes, oft__ off) const DWORD protect = protect_page(prot); const oft__ max = off + (oft__)len; - const int less = (sizeof(oft__) <= sizeof(DWORD)); - const DWORD max_lo = less ? (DWORD)max : (DWORD)((max ) & MAXDWORD); - const DWORD max_hi = less ? (DWORD)0 : (DWORD)((max >> 32) & MAXDWORD); - const DWORD file_lo = less ? (DWORD)off : (DWORD)((off ) & MAXDWORD); - const DWORD file_hi = less ? (DWORD)0 : (DWORD)((off >> 32) & MAXDWORD); + const DWORD max_lo = large ? (DWORD)((max) & MAXDWORD) : (DWORD)max; + const DWORD max_hi = large ? (DWORD)((max >> 32) & MAXDWORD) : (DWORD)0; + const DWORD file_lo = large ? (DWORD)((off) & MAXDWORD) : (DWORD)off; + const DWORD file_hi = large ? (DWORD)((off >> 32) & MAXDWORD) : (DWORD)0; #ifdef _MSC_VER #pragma warning(pop) #endif - errno = 0; - if (len == 0 || (flags & MAP_FIXED) != 0 || prot == PROT_EXEC) { errno = EINVAL; @@ -110,7 +114,7 @@ void* mmap(void* addr, size_t len, int prot, int flags, int fildes, oft__ off) if (mapping == NULL) { - errno = error(GetLastError(), EPERM); + errno = last_error(EPERM); return MAP_FAILED; } @@ -119,25 +123,29 @@ void* mmap(void* addr, size_t len, int prot, int flags, int fildes, oft__ off) /* TODO: verify mapping handle may be closed here and then use the map. */ if (map == NULL || CloseHandle(mapping) == FALSE) { - errno = error(GetLastError(), EPERM); + errno = last_error(EPERM); return MAP_FAILED; } + errno = 0; return map; } int munmap(void* addr, size_t len) { if (UnmapViewOfFile(addr) != FALSE) + { + errno = 0; return 0; + } - errno = error(GetLastError(), EPERM); + errno = last_error(EPERM); return -1; } int madvise(void* addr, size_t len, int advice) { - /* Not implemented. */ + /* Not implemented, but should not fail (optimization). */ return 0; } @@ -147,36 +155,48 @@ int mprotect(void* addr, size_t len, int prot) const DWORD new_protect = protect_page(prot); if (VirtualProtect(addr, len, new_protect, &old_protect) != FALSE) + { + errno = 0; return 0; + } - errno = error(GetLastError(), EPERM); + errno = last_error(EPERM); return -1; } int msync(void* addr, size_t len, int flags) { if (FlushViewOfFile(addr, len) != FALSE) + { + errno = 0; return 0; + } - errno = error(GetLastError(), EPERM); + errno = last_error(EPERM); return -1; } int mlock(const void* addr, size_t len) { if (VirtualLock((LPVOID)addr, len) != FALSE) + { + errno = 0; return 0; + } - errno = error(GetLastError(), EPERM); + errno = last_error(EPERM); return -1; } int munlock(const void* addr, size_t len) { if (VirtualUnlock((LPVOID)addr, len) != FALSE) + { + errno = 0; return 0; + } - errno = error(GetLastError(), EPERM); + errno = last_error(EPERM); return -1; } @@ -188,9 +208,12 @@ int fsync(int fd) const HANDLE handle = (HANDLE)(_get_osfhandle(fd)); if (FlushFileBuffers(handle) != FALSE) + { + errno = 0; return 0; + } - errno = error(GetLastError(), EPERM); + errno = last_error(EPERM); return -1; } @@ -200,38 +223,31 @@ int ftruncate(int fd, oft__ size) LARGE_INTEGER big; if (fd < 0) + { + errno = EBADF; return -1; + } /* guard against overflow from unsigned to signed */ - if (size >= MAXINT64) + if (size >= (uint64_t)(large ? MAXINT64 : MAXINT32)) + { + errno = EFBIG; return -1; + } /* unsigned to signed, splits to high and low */ big.QuadPart = (LONGLONG)size; const HANDLE handle = (HANDLE)_get_osfhandle(fd); - const BOOL ret = SetFilePointerEx(handle, big, NULL, FILE_BEGIN); + const BOOL ok = SetFilePointerEx(handle, big, NULL, FILE_BEGIN); - if (!ret || !SetEndOfFile(handle)) + if (!ok || SetEndOfFile(handle) == FALSE) { - const DWORD error = GetLastError(); - - switch (error) - { - case ERROR_INVALID_HANDLE: - errno = EBADF; - break; - case ERROR_DISK_FULL: - errno = ENOSPC; - break; - default: - errno = EIO; - break; - } - + errno = last_error(EIO); return -1; } + errno = 0; return 0; } diff --git a/src/lib/database/result/block_result.cpp b/src/lib/database/result/block_result.cpp index 6e6f4a950..e188a438b 100644 --- a/src/lib/database/result/block_result.cpp +++ b/src/lib/database/result/block_result.cpp @@ -81,5 +81,18 @@ hash_digest block_result::transaction_hash(size_t index) const return deserial.read_hash(); } +ec_signature block_result::blocksig() const +{ + BITCOIN_ASSERT(slab_); + const auto memory = REMAP_ADDRESS(slab_); + + if (!header().is_proof_of_stake()) + return ec_signature(); + + const auto offset = header_size + height_size + count_size; + const auto first = memory + offset + transaction_count() * hash_size; + auto deserial = make_deserializer_unsafe(first); + return deserial.read_bytes(); +} } // namespace database } // namespace libbitcoin diff --git a/src/lib/explorer/config/script.cpp b/src/lib/explorer/config/script.cpp index 0a7819d80..45408f952 100644 --- a/src/lib/explorer/config/script.cpp +++ b/src/lib/explorer/config/script.cpp @@ -74,8 +74,7 @@ const data_chunk script::to_data() const const std::string script::to_string() const { - static constexpr auto flags = chain::script_context::all_enabled; - return value_.to_string(flags); + return value_.to_string(chain::get_script_context()); } script::operator const chain::script&() const @@ -100,8 +99,7 @@ std::istream& operator>>(std::istream& input, script& argument) std::ostream& operator<<(std::ostream& output, const script& argument) { - static constexpr auto flags = chain::script_context::all_enabled; - output << argument.value_.to_string(flags); + output << argument.value_.to_string(chain::get_script_context()); return output; } diff --git a/src/lib/explorer/dispatch.cpp b/src/lib/explorer/dispatch.cpp index 1b00bce2a..305f2e1b6 100644 --- a/src/lib/explorer/dispatch.cpp +++ b/src/lib/explorer/dispatch.cpp @@ -19,6 +19,7 @@ * along with this program. If not, see . */ #include +#include #include #include @@ -168,6 +169,7 @@ console_result dispatch_command(int argc, const char* argv[], if (command->category(ctgy_extension)) { +#ifndef PRIVATE_CHAIN // fixme. is_blockchain_sync has some problem. // if (command->category(ctgy_online) && node.is_blockchain_sync()) { if (command->category(ctgy_online) && @@ -178,7 +180,7 @@ console_result dispatch_command(int argc, const char* argv[], throw block_sync_required_exception{"This command is unavailable because of the height < 610000."}; } } - +#endif return static_cast(command.get())->invoke(jv_output, node); } else { diff --git a/src/lib/explorer/display.cpp b/src/lib/explorer/display.cpp index 87a3df794..44689bb28 100644 --- a/src/lib/explorer/display.cpp +++ b/src/lib/explorer/display.cpp @@ -28,7 +28,6 @@ #include #include #include -#include namespace libbitcoin { namespace explorer { @@ -79,8 +78,7 @@ void display_usage(std::ostream& stream) { stream << std::endl << BX_COMMAND_USAGE << std::endl - << format(BX_VERSION_MESSAGE) % - MVS_EXPLORER_VERSION << std::endl + << format(BX_VERSION_MESSAGE) % MVS_VERSION << std::endl << BX_COMMANDS_HEADER << std::endl << std::endl; diff --git a/src/lib/explorer/extensions/account_info.cpp b/src/lib/explorer/extensions/account_info.cpp index 666c87f14..249004b99 100644 --- a/src/lib/explorer/extensions/account_info.cpp +++ b/src/lib/explorer/extensions/account_info.cpp @@ -31,6 +31,8 @@ #include #include +#include + using namespace libbitcoin::wallet; using namespace libbitcoin::config; diff --git a/src/lib/explorer/extensions/base_helper.cpp b/src/lib/explorer/extensions/base_helper.cpp index 68e8cc8a4..3f841fc0b 100644 --- a/src/lib/explorer/extensions/base_helper.cpp +++ b/src/lib/explorer/extensions/base_helper.cpp @@ -18,6 +18,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -29,8 +30,9 @@ namespace libbitcoin { namespace explorer { namespace commands { -using bc::chain::blockchain_message; using bc::blockchain::validate_transaction; +using namespace chain; +using namespace std; utxo_attach_type get_utxo_attach_type(const chain::output& output_) { @@ -134,17 +136,17 @@ void check_did_symbol(const std::string& symbol, bool check_sensitive) void check_asset_symbol(const std::string& symbol, bool check_sensitive) { if (symbol.empty()) { - throw asset_symbol_length_exception{"Symbol cannot be empty."}; + throw asset_symbol_length_exception{"Asset symbol cannot be empty."}; } if (symbol.length() > ASSET_DETAIL_SYMBOL_FIX_SIZE) { - throw asset_symbol_length_exception{"Symbol length must be less than " + throw asset_symbol_length_exception{"Asset symbol length must be less than " + std::to_string(ASSET_DETAIL_SYMBOL_FIX_SIZE) + "."}; } if (check_sensitive) { if (bc::wallet::symbol::is_sensitive(symbol)) { - throw asset_symbol_name_exception{"Symbol " + symbol + " is forbidden."}; + throw asset_symbol_name_exception{"Asset symbol " + symbol + " is forbidden."}; } } } @@ -152,11 +154,11 @@ void check_asset_symbol(const std::string& symbol, bool check_sensitive) void check_mit_symbol(const std::string& symbol, bool check_sensitive) { if (symbol.empty()) { - throw asset_symbol_length_exception{"Symbol cannot be empty."}; + throw asset_symbol_length_exception{"MIT symbol cannot be empty."}; } if (symbol.length() > ASSET_MIT_SYMBOL_FIX_SIZE) { - throw asset_symbol_length_exception{"Symbol length must be less than " + throw asset_symbol_length_exception{"MIT symbol length must be less than " + std::to_string(ASSET_MIT_SYMBOL_FIX_SIZE) + "."}; } @@ -164,13 +166,13 @@ void check_mit_symbol(const std::string& symbol, bool check_sensitive) for (const auto& i : symbol) { if (!(std::isalnum(i) || i == '.'|| i == '@' || i == '_' || i == '-')) throw asset_symbol_name_exception( - "Symbol " + symbol + " has invalid character."); + "MIT symbol " + symbol + " has invalid character."); } if (check_sensitive) { auto upper = boost::to_upper_copy(symbol); if (bc::wallet::symbol::is_sensitive(upper)) { - throw asset_symbol_name_exception{"Symbol " + symbol + " is forbidden."}; + throw asset_symbol_name_exception{"MIT symbol " + symbol + " is forbidden."}; } } } @@ -182,6 +184,101 @@ void check_message(const std::string& message, bool check_sensitive) } } +template +struct HexTo { + ElemT value; + operator ElemT() const {return value;} + friend std::istream& operator>>(std::istream& in, HexTo& out) { + in >> std::hex >> out.value; + return in; + } +}; + +asset_cert_type check_cert_type_name(const string& cert_name, bool all) +{ + // check asset cert types + auto certs_create = asset_cert_ns::none; + std::map cert_map = { + {"naming", asset_cert_ns::naming}, + {"marriage", asset_cert_ns::marriage}, + {"kyc", asset_cert_ns::kyc} + }; + + if (all) { + cert_map["issue"] = asset_cert_ns::issue; + cert_map["domain"] = asset_cert_ns::domain; + } + + auto iter = cert_map.find(cert_name); + if (iter != cert_map.end()) { + certs_create = iter->second; + } + else { + try { + if (cert_name.compare(0, 2, "0x") == 0) { + certs_create = boost::lexical_cast>(cert_name.c_str()); + } + else { + certs_create = boost::lexical_cast(cert_name.c_str()); + } + + if (certs_create < asset_cert_ns::custom) { + throw asset_cert_exception("invalid asset cert type " + cert_name); + } + } + catch(boost::bad_lexical_cast const&) { + throw asset_cert_exception("invalid asset cert type " + cert_name); + } + } + return certs_create; +} + +asset_cert_type check_issue_cert(bc::blockchain::block_chain_impl& blockchain, + const string& account, const string& symbol, const string& cert_name) +{ + auto certs_create = check_cert_type_name(cert_name); + + // check domain naming cert not exist. + if (blockchain.is_asset_cert_exist(symbol, certs_create)) { + throw asset_cert_existed_exception( + "cert '" + symbol + "' with type '" + cert_name + "' already exists on the blockchain!"); + } + + if (certs_create == asset_cert_ns::naming) { + // check symbol is valid. + auto pos = symbol.find("."); + if (pos == std::string::npos) { + throw asset_symbol_name_exception("invalid naming cert symbol " + symbol + + ", it should contain a dot '.'"); + } + + auto&& domain = asset_cert::get_domain(symbol); + if (!asset_cert::is_valid_domain(domain)) { + throw asset_symbol_name_exception("invalid naming cert symbol " + symbol + + ", it should contain a valid domain!"); + } + + // check asset not exist. + if (blockchain.is_asset_exist(symbol, false)) { + throw asset_symbol_existed_exception( + "asset symbol '" + symbol + "' already exists on the blockchain!"); + } + + // check domain cert belong to this account. + bool exist = blockchain.is_asset_cert_exist(domain, asset_cert_ns::domain); + if (!exist) { + throw asset_cert_notfound_exception("no domain cert '" + domain + "' found!"); + } + + auto cert = blockchain.get_account_asset_cert(account, domain, asset_cert_ns::domain); + if (!cert) { + throw asset_cert_notowned_exception("no domain cert '" + domain + "' owned by " + account); + } + } + + return certs_create; +} + std::string get_address(const std::string& did_or_address, bc::blockchain::block_chain_impl& blockchain) { @@ -337,11 +434,24 @@ void sync_fetch_asset_balance(const std::string& address, bool sum_all, if (asset_amount && operation::is_pay_key_hash_with_attenuation_model_pattern(output.script.operations)) { const auto& attenuation_model_param = output.get_attenuation_model_param(); - auto diff_height = row.output_height ? (height - row.output_height) : 0; + auto diff_height = row.output_height + ? blockchain.calc_number_of_blocks(row.output_height, height) + : 0; auto available_amount = attenuation_model::get_available_asset_amount( asset_amount, diff_height, attenuation_model_param); locked_amount = asset_amount - available_amount; } + else if (asset_amount + && chain::operation::is_pay_key_hash_with_sequence_lock_pattern(output.script.operations)) { + uint64_t lock_sequence = chain::operation:: + get_lock_sequence_from_pay_key_hash_with_sequence_lock(output.script.operations); + // use any kind of blocks + if (row.output_height + lock_sequence > height) { + // utxo already in block but is locked with sequence and not mature + locked_amount = asset_amount; + } + } + if (iter == sh_asset_vec->end()) { // new item sh_asset_vec->push_back({symbol, address, asset_amount, locked_amount}); } @@ -354,6 +464,67 @@ void sync_fetch_asset_balance(const std::string& address, bool sum_all, } } +void sync_fetch_locked_balance(const std::string& address, + bc::blockchain::block_chain_impl& blockchain, + std::shared_ptr sh_vec, + const std::string& asset_symbol, + uint64_t expiration) +{ + const bool is_asset = !asset_symbol.empty(); + if (is_asset && wallet::symbol::is_forbidden(asset_symbol)) { + return; + } + + auto&& rows = blockchain.get_address_history(wallet::payment_address(address)); + + chain::transaction tx_temp; + uint64_t tx_height = 0; + + uint64_t height = 0; + blockchain.get_last_height(height); + + for (auto& row: rows) + { + // spend unconfirmed (or no spend attempted) + if ((row.spend.hash == null_hash) + && blockchain.get_transaction(row.output.hash, tx_temp, tx_height)) + { + BITCOIN_ASSERT(row.output.index < tx_temp.outputs.size()); + const auto& output = tx_temp.outputs.at(row.output.index); + + if (is_asset != output.is_asset()) { + continue; + } + + if (is_asset && asset_symbol != output.get_asset_symbol()) { + continue; + } + + if (!operation::is_pay_key_hash_with_sequence_lock_pattern(output.script.operations)) { + continue; + } + + uint64_t lock_sequence = chain::operation:: + get_lock_sequence_from_pay_key_hash_with_sequence_lock(output.script.operations); + // use any kind of blocks + if ((tx_height + lock_sequence <= height) || + (expiration > height && tx_height + lock_sequence <= expiration)) { + continue; + } + + uint64_t locked_value = is_asset ? output.get_asset_amount() : row.value; + if (locked_value == 0) { + continue; + } + + uint64_t locked_height = lock_sequence; + uint64_t expiration_height = tx_height + lock_sequence; + locked_balance locked{address, locked_value, locked_height, expiration_height}; + sh_vec->emplace_back(locked); + } + } +} + void sync_fetch_asset_deposited_balance(const std::string& address, bc::blockchain::block_chain_impl& blockchain, std::shared_ptr sh_asset_vec) @@ -391,7 +562,9 @@ void sync_fetch_asset_deposited_balance(const std::string& address, } const auto& model_param = output.get_attenuation_model_param(); - auto diff_height = row.output_height ? (height - row.output_height) : 0; + auto diff_height = row.output_height + ? blockchain.calc_number_of_blocks(row.output_height, height) + : 0; auto available_amount = attenuation_model::get_available_asset_amount( asset_amount, diff_height, model_param); uint64_t locked_amount = asset_amount - available_amount; @@ -427,7 +600,7 @@ void sync_unspend_output(bc::blockchain::block_chain_impl& blockchain, const inp std::shared_ptr tx = blockchain.get_spends_output(input); uint64_t tx_height; chain::transaction tx_temp; - if(tx == nullptr && blockchain.get_transaction(input.hash, tx_temp, tx_height)) + if (tx == nullptr && blockchain.get_transaction(input.hash, tx_temp, tx_height)) { const auto& output = tx_temp.outputs.at(input.index); @@ -437,7 +610,6 @@ void sync_unspend_output(bc::blockchain::block_chain_impl& blockchain, const inp } else if (tx != nullptr) { - for (uint32_t i = 0; i < tx->outputs.size(); i++) { const auto& output = tx->outputs.at(i); @@ -445,11 +617,8 @@ void sync_unspend_output(bc::blockchain::block_chain_impl& blockchain, const inp input_point input_ = {tx->hash(), i}; sync_unspend_output(blockchain, input_, output_list, filter); } - } - } - } auto get_asset_unspend_utxo(const std::string& symbol, @@ -477,9 +646,7 @@ auto sync_fetch_asset_deposited_view(const std::string& symbol, bc::blockchain::block_chain_impl& blockchain) -> std::shared_ptr { - std::shared_ptr output_list = get_asset_unspend_utxo(symbol, blockchain); - std::shared_ptr sh_asset_vec = std::make_shared(); chain::transaction tx_temp; @@ -506,24 +673,23 @@ auto sync_fetch_asset_deposited_view(const std::string& symbol, continue; } - if (!operation::is_pay_key_hash_with_attenuation_model_pattern(output.script.operations)) - { + if (!operation::is_pay_key_hash_with_attenuation_model_pattern(output.script.operations)) { continue; } auto asset_amount = output.get_asset_amount(); - if (asset_amount == 0) - { + if (asset_amount == 0) { continue; } const auto &model_param = output.get_attenuation_model_param(); - auto diff_height = tx_height ? (height - tx_height) : 0; + auto diff_height = tx_height + ? blockchain.calc_number_of_blocks(tx_height, height) + : 0; auto available_amount = attenuation_model::get_available_asset_amount( asset_amount, diff_height, model_param); uint64_t locked_amount = asset_amount - available_amount; - if (locked_amount == 0) - { + if (locked_amount == 0) { continue; } @@ -540,14 +706,11 @@ auto sync_fetch_asset_deposited_view(const std::string& symbol, return sh_asset_vec; } - auto sync_fetch_asset_view(const std::string& symbol, bc::blockchain::block_chain_impl& blockchain) -> std::shared_ptr { - std::shared_ptr output_list = get_asset_unspend_utxo(symbol, blockchain); - std::shared_ptr sh_asset_vec = std::make_shared(); chain::transaction tx_temp; @@ -574,20 +737,30 @@ auto sync_fetch_asset_view(const std::string& symbol, continue; } - auto asset_amount = output.get_asset_amount(); uint64_t locked_amount = 0; if (asset_amount && operation::is_pay_key_hash_with_attenuation_model_pattern(output.script.operations)) { const auto& attenuation_model_param = output.get_attenuation_model_param(); - auto diff_height = tx_height ? (height - tx_height) : 0; + auto diff_height = tx_height + ? blockchain.calc_number_of_blocks(tx_height, height) + : 0; auto available_amount = attenuation_model::get_available_asset_amount( asset_amount, diff_height, attenuation_model_param); locked_amount = asset_amount - available_amount; } + else if (asset_amount + && chain::operation::is_pay_key_hash_with_sequence_lock_pattern(output.script.operations)) { + uint64_t lock_sequence = chain::operation:: + get_lock_sequence_from_pay_key_hash_with_sequence_lock(output.script.operations); + // use any kind of blocks + if (tx_height + lock_sequence > height) { + // utxo already in block but is locked with sequence and not mature + locked_amount = asset_amount; + } + } sh_asset_vec->push_back({symbol, address, asset_amount, locked_amount}); - } } } @@ -639,9 +812,9 @@ void sync_fetch_deposited_balance(wallet::payment_address& address, // deposit utxo in block uint64_t deposit_height = chain::operation:: get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); - uint64_t expiration_height = row.output_height + deposit_height; + auto deposited_height = blockchain.calc_number_of_blocks(row.output_height, height); - if (expiration_height > height) { + if (deposit_height > deposited_height) { auto&& output_hash = encode_hash(row.output.hash); auto&& tx_hash = encode_hash(tx_temp.hash()); const auto match = [&tx_hash](const deposited_balance& balance) { @@ -658,7 +831,8 @@ void sync_fetch_deposited_balance(wallet::payment_address& address, } } else { - + uint64_t expiration_height = + blockchain.get_expiration_height(row.output_height, deposit_height); deposited_balance deposited(address_str, tx_hash, deposit_height, expiration_height); if (output_hash == tx_hash) { deposited.balance = row.value; @@ -691,13 +865,15 @@ void sync_fetchbalance(wallet::payment_address& address, blockchain.get_last_height(height); for (auto& row: rows) { - if (row.output_height == 0) { + bool tx_ready = (row.spend.hash == null_hash) + && blockchain.get_transaction(row.output.hash, tx_temp, tx_height); + // include genesis block whose height is zero. + if (row.output_height == 0 && tx_height != 0) { continue; } // spend unconfirmed (or no spend attempted) - if ((row.spend.hash == null_hash) - && blockchain.get_transaction(row.output.hash, tx_temp, tx_height)) { + if (tx_ready) { BITCOIN_ASSERT(row.output.index < tx_temp.outputs.size()); auto output = tx_temp.outputs.at(row.output.index); if (output.get_script_address() != address.encoded()) { @@ -708,14 +884,23 @@ void sync_fetchbalance(wallet::payment_address& address, // deposit utxo in block uint64_t lock_height = chain::operation:: get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); - if ((row.output_height + lock_height) > height) { + if (lock_height > blockchain.calc_number_of_blocks(row.output_height, height)) { // utxo already in block but deposit not expire frozen_balance += row.value; } } + else if (chain::operation::is_pay_key_hash_with_sequence_lock_pattern(output.script.operations)) { + uint64_t lock_sequence = chain::operation:: + get_lock_sequence_from_pay_key_hash_with_sequence_lock(output.script.operations); + // use any kind of blocks + if (row.output_height + lock_sequence > height) { + // utxo already in block but is locked with sequence and not mature + frozen_balance += row.value; + } + } else if (tx_temp.is_coinbase()) { // coin base etp maturity etp check // add not coinbase_maturity etp into frozen - if ((row.output_height + coinbase_maturity) > height) { + if (coinbase_maturity > blockchain.calc_number_of_blocks(row.output_height, height)) { frozen_balance += row.value; } } @@ -725,8 +910,9 @@ void sync_fetchbalance(wallet::payment_address& address, total_received += row.value; - if ((row.spend.hash == null_hash || row.spend_height == 0)) + if ((row.spend.hash == null_hash || row.spend_height == 0)) { confirmed_balance += row.value; + } } addr_balance.confirmed_balance = confirmed_balance; @@ -735,6 +921,83 @@ void sync_fetchbalance(wallet::payment_address& address, addr_balance.frozen_balance = frozen_balance; } +void sync_fetchbalance(wallet::payment_address& address, + bc::blockchain::block_chain_impl& blockchain, + std::shared_ptr sh_vec) +{ + auto&& rows = blockchain.get_address_history(address, false); + + chain::transaction tx_temp; + uint64_t tx_height = 0; + + uint64_t height = 0; + blockchain.get_last_height(height); + + for (const auto& row: rows) { + uint64_t unspent_balance = 0; + uint64_t frozen_balance = 0; + + if (row.value == 0) { + continue; + } + + bool tx_ready = (row.spend.hash == null_hash) + && blockchain.get_transaction(row.output.hash, tx_temp, tx_height); + // include genesis block whose height is zero. + if (row.output_height == 0 && tx_height != 0) { + continue; + } + + // spend unconfirmed (or no spend attempted) + if (!tx_ready) { + continue; + } + + BITCOIN_ASSERT(row.output.index < tx_temp.outputs.size()); + auto output = tx_temp.outputs.at(row.output.index); + if (output.get_script_address() != address.encoded()) { + continue; + } + + if (chain::operation::is_pay_key_hash_with_lock_height_pattern(output.script.operations)) { + // deposit utxo in block + uint64_t lock_height = chain::operation:: + get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); + if (lock_height > blockchain.calc_number_of_blocks(row.output_height, height)) { + // utxo already in block but deposit not expire + frozen_balance += row.value; + } + } + else if (chain::operation::is_pay_key_hash_with_sequence_lock_pattern(output.script.operations)) { + uint64_t lock_sequence = chain::operation:: + get_lock_sequence_from_pay_key_hash_with_sequence_lock(output.script.operations); + // use any kind of blocks + if (row.output_height + lock_sequence > height) { + // utxo already in block but is locked with sequence and not mature + frozen_balance += row.value; + } + } + else if (tx_temp.is_coinbase()) { // coin base etp maturity etp check + // add not coinbase_maturity etp into frozen + if (coinbase_maturity > blockchain.calc_number_of_blocks(row.output_height, height)) { + frozen_balance += row.value; + } + } + + unspent_balance += row.value; + sh_vec->emplace_back(utxo_balance{ + encode_hash(row.output.hash), row.output.index, + row.output_height, unspent_balance, frozen_balance}); + } + + if (sh_vec->size() > 1) { + auto sort_by_amount_descend = [](const utxo_balance& b1, const utxo_balance& b2){ + return b1.unspent_balance > b2.unspent_balance; + }; + std::sort(sh_vec->begin(), sh_vec->end(), sort_by_amount_descend); + } +} + bool base_transfer_common::get_spendable_output( chain::output& output, const chain::history& row, uint64_t height) const { @@ -752,6 +1015,12 @@ bool base_transfer_common::get_spendable_output( BITCOIN_ASSERT(row.output.index < tx_temp.outputs.size()); output = tx_temp.outputs.at(row.output.index); + if (exclude_etp_range_.first < exclude_etp_range_.second) { + if (output.value >= exclude_etp_range_.first && output.value < exclude_etp_range_.second) { + return false; + } + } + if (chain::operation::is_pay_key_hash_with_lock_height_pattern(output.script.operations)) { if (row.output_height == 0) { // deposit utxo in transaction pool @@ -760,15 +1029,32 @@ bool base_transfer_common::get_spendable_output( // deposit utxo in block auto lock_height = chain::operation:: get_lock_height_from_pay_key_hash_with_lock_height(output.script.operations); - if ((row.output_height + lock_height) > height) { + if (lock_height > blockchain_.calc_number_of_blocks(row.output_height, height)) { // utxo already in block but deposit not expire return false; } } - } else if (tx_temp.is_coinbase()) { // incase readd deposit + } + else if (chain::operation::is_pay_key_hash_with_sequence_lock_pattern(output.script.operations)) { + if (row.output_height == 0) { + // lock sequence utxo in transaction pool + return false; + } + else { + uint64_t lock_sequence = chain::operation:: + get_lock_sequence_from_pay_key_hash_with_sequence_lock(output.script.operations); + // use any kind of blocks + if (row.output_height + lock_sequence > height) { + // utxo already in block but is locked with sequence and not mature + return false; + } + } + } + else if (tx_temp.is_coinbase()) { // incase readd deposit // coin base etp maturity etp check // coinbase_maturity etp check - if ((row.output_height == 0) || ((row.output_height + coinbase_maturity) > height)) { + if ((row.output_height == 0) || + (coinbase_maturity > blockchain_.calc_number_of_blocks(row.output_height, height))) { return false; } } @@ -779,28 +1065,35 @@ bool base_transfer_common::get_spendable_output( // only consider etp and asset and cert. // specify parameter 'did' to true to only consider did void base_transfer_common::sync_fetchutxo( - const std::string& prikey, const std::string& addr, filter filter) + const std::string& prikey, const std::string& addr, filter filter, const history::list& spec_rows) { auto&& waddr = wallet::payment_address(addr); - auto&& rows = blockchain_.get_address_history(waddr, true); uint64_t height = 0; blockchain_.get_last_height(height); + const auto &rows = spec_rows.empty() ? blockchain_.get_address_history(waddr, true) : spec_rows; + for (auto& row: rows) { - // performance improve - if (is_payment_satisfied(filter)) { - break; - } - chain::output output; - if (!get_spendable_output(output, row, height)) { - continue; - } + if (!spec_rows.empty()) { + if (!get_spendable_output(output, row, height)) { + throw std::logic_error("output spent error."); + } + } else { + // performance improve + if (is_payment_satisfied(filter)) { + break; + } - if (output.get_script_address() != addr) { - continue; + if (!get_spendable_output(output, row, height)) { + continue; + } + + if (output.get_script_address() != addr) { + continue; + } } auto etp_amount = row.value; @@ -878,7 +1171,7 @@ void base_transfer_common::sync_fetchutxo( } } else if ((filter & FILTER_DID) && - (output.is_did_register() || output.is_did_transfer())) { // did related + (output.is_did_register() || output.is_did_transfer())) { // did related BITCOIN_ASSERT(etp_amount == 0); BITCOIN_ASSERT(asset_total_amount == 0); BITCOIN_ASSERT(cert_type == asset_cert_ns::none); @@ -902,7 +1195,9 @@ void base_transfer_common::sync_fetchutxo( && operation::is_pay_key_hash_with_attenuation_model_pattern(output.script.operations)) { const auto& attenuation_model_param = output.get_attenuation_model_param(); new_model_param_ptr = std::make_shared(); - auto diff_height = row.output_height ? (height - row.output_height) : 0; + auto diff_height = row.output_height + ? blockchain_.calc_number_of_blocks(row.output_height, height) + : 0; asset_amount = attenuation_model::get_available_asset_amount( asset_total_amount, diff_height, attenuation_model_param, new_model_param_ptr); if ((asset_amount == 0) && !is_locked_asset_as_payment()) { @@ -912,6 +1207,16 @@ void base_transfer_common::sync_fetchutxo( BITCOIN_ASSERT(asset_total_amount >= asset_amount); + auto lock_sequence = output.get_lock_sequence(); + + if (locktime_ > 0) { + // ref. is_locktime_conflict() [BIP65] + if (lock_sequence != max_input_sequence) { + continue; + } + lock_sequence = relative_locktime_disabled; + } + // add to from list address_asset_record record; @@ -919,13 +1224,14 @@ void base_transfer_common::sync_fetchutxo( record.prikey = prikey; record.script = output.script; } - record.addr = addr; + record.addr = output.get_script_address(); record.amount = etp_amount; record.symbol = asset_symbol; record.asset_amount = asset_amount; record.asset_cert = cert_type; record.output = row.output; record.type = get_utxo_attach_type(output); + record.sequence = lock_sequence; from_list_.push_back(record); @@ -941,17 +1247,15 @@ void base_transfer_common::sync_fetchutxo( auto locked_asset = asset_total_amount - record.asset_amount; std::string model_param(new_model_param_ptr->begin(), new_model_param_ptr->end()); receiver_list_.push_back({record.addr, record.symbol, - 0, locked_asset, utxo_attach_type::asset_locked_transfer, - attachment(0, 0, blockchain_message(std::move(model_param))), record.output}); + 0, locked_asset, utxo_attach_type::asset_locked_transfer, + attachment(0, 0, chain::blockchain_message(std::move(model_param))), record.output}); // in secondary issue, locked asset can also verify threshold condition if (is_locked_asset_as_payment()) { payment_asset_ = (payment_asset_ > locked_asset) - ? (payment_asset_ - locked_asset) : 0; + ? (payment_asset_ - locked_asset) : 0; } } } - - rows.clear(); } void base_transfer_common::check_fee_in_valid_range(uint64_t fee) @@ -1283,12 +1587,11 @@ void base_transfer_common::populate_tx_inputs() + std::to_string(tx_limit) + " inputs."; throw tx_validate_exception(response); } - - tx_item_idx_++; - input.sequence = max_input_sequence; + input.sequence = fromeach.sequence; input.previous_output.hash = fromeach.output.hash; input.previous_output.index = fromeach.output.index; tx_.inputs.push_back(input); + tx_item_idx_++; } } @@ -1519,14 +1822,13 @@ void base_transfer_common::sign_tx_inputs() } // do script - data_chunk data; - ss.operations.push_back({bc::chain::opcode::zero, data}); + ss.operations.push_back({bc::chain::opcode::zero, {}}); ss.operations.push_back({bc::chain::opcode::special, endorse}); chain::script script_encoded; script_encoded.from_string(multisig_script); - ss.operations.push_back({bc::chain::opcode::pushdata1, script_encoded.to_data(false)}); + ss.operations.push_back(chain::operation::from_raw_data(script_encoded.to_data(false))); } else { bc::explorer::config::script config_contract(fromeach.script); @@ -1577,10 +1879,14 @@ void base_transfer_common::send_tx() void base_transfer_common::populate_tx_header() { - tx_.locktime = 0; +#ifdef ENABLE_LOCKTIME + tx_.locktime = locktime_; +#endif + if (validate_transaction::is_nova_feature_activated(blockchain_)) { tx_.version = transaction_version::check_nova_feature; - } else { + } + else { tx_.version = transaction_version::check_output_script; } } @@ -1636,7 +1942,7 @@ std::string base_multisig_transfer_helper::get_sign_tx_multisig_script(const add void base_transaction_constructor::sum_payment_amount() { base_transfer_common::sum_payment_amount(); - if (from_vec_.empty()) { + if (from_vec_.empty() && from_list_.empty()) { throw fromaddress_empty_exception{"empty from address"}; } } @@ -1653,7 +1959,7 @@ void base_transaction_constructor::populate_change() auto addr = !mychange_.empty() ? mychange_ : from_list_.begin()->addr; receiver_list_.push_back({addr, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(message_))}); + attachment(0, 0, chain::blockchain_message(message_))}); } } @@ -1877,7 +2183,7 @@ void sending_asset::populate_change() auto addr = !mychange_.empty() ? mychange_ : from_list_.begin()->addr; receiver_list_.push_back({addr, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(message_))}); + attachment(0, 0, chain::blockchain_message(message_))}); } } @@ -2163,6 +2469,29 @@ attachment transferring_mit::populate_output_attachment(const receiver_record& r return attach; } +chain::operation::stack lock_sending::get_script_operations(const receiver_record& record) const +{ + if (record.is_lock_seq_) { + if ((chain::get_script_context() & chain::script_context::bip112_enabled) == 0) { + throw argument_legality_exception{"lock sequence(bip112) is not enabled"}; + } + const wallet::payment_address payment(record.target); + if (!payment) { + throw toaddress_invalid_exception{"invalid target address"}; + } + + if (payment.version() == wallet::payment_address::mainnet_p2kh) { + const auto& hash = payment.hash(); + return chain::operation::to_pay_key_hash_with_sequence_lock_pattern(hash, sequence_); + } + else { + throw toaddress_invalid_exception{"not supported target address " + record.target}; + } + } + + return base_transfer_helper::get_script_operations(record); +} + } //commands } // explorer } // libbitcoin diff --git a/src/lib/explorer/extensions/command_extension_func.cpp b/src/lib/explorer/extensions/command_extension_func.cpp index d06882961..a267a437e 100644 --- a/src/lib/explorer/extensions/command_extension_func.cpp +++ b/src/lib/explorer/extensions/command_extension_func.cpp @@ -40,9 +40,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -50,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -71,6 +74,7 @@ #include #include #include +#include #include // #include #include @@ -96,17 +100,18 @@ #include #include #include +#include +#include namespace libbitcoin { namespace explorer { +using namespace std; +using namespace commands; void broadcast_extension(const function)> func, std::ostream& os) { - using namespace std; - using namespace commands; - os <<"\r\n"; // account func(make_shared()); @@ -119,6 +124,7 @@ void broadcast_extension(const function)> func, std::os func(make_shared()); func(make_shared()); func(make_shared()); + func(make_shared()); os <<"\r\n"; // system @@ -135,6 +141,7 @@ void broadcast_extension(const function)> func, std::os func(make_shared()); func(make_shared()); func(make_shared()); + func(make_shared()); os <<"\r\n"; // block & tx @@ -166,17 +173,21 @@ void broadcast_extension(const function)> func, std::os func(make_shared()); func(make_shared()); func(make_shared()); + func(make_shared()); func(make_shared()); func(make_shared()); func(make_shared()); + func(make_shared()); os <<"\r\n"; // asset + func(make_shared()); func(make_shared()); func(make_shared()); func(make_shared()); func(make_shared()); func(make_shared()); + func(make_shared()); func(make_shared()); func(make_shared()); func(make_shared()); @@ -209,9 +220,6 @@ void broadcast_extension(const function)> func, std::os shared_ptr find_extension(const string& symbol) { - using namespace std; - using namespace commands; - // account if (symbol == getnewaccount::symbol()) return make_shared(); @@ -233,6 +241,8 @@ shared_ptr find_extension(const string& symbol) return make_shared(); if (symbol == importkeyfile::symbol() || symbol == "importaccountfromfile") return make_shared(); + if (symbol == importaddress::symbol()) + return make_shared(); // system if (symbol == shutdown::symbol()) @@ -275,6 +285,8 @@ shared_ptr find_extension(const string& symbol) return make_shared(); if (symbol == gettx::symbol() || symbol == "gettransaction") return make_shared(); + if (symbol == popblock::symbol()) + return make_shared(); if (symbol == "fetch-tx") return make_shared(symbol); if (symbol == listtxs::symbol()) @@ -313,6 +325,10 @@ shared_ptr find_extension(const string& symbol) return make_shared(); if (symbol == deposit::symbol()) return make_shared(); + if (symbol == lock::symbol()) + return make_shared(); + if (symbol == getlocked::symbol()) + return make_shared(); if (symbol == send::symbol() || symbol == "didsend") return make_shared(); if (symbol == sendmore::symbol() || symbol == "didsendmore") @@ -321,6 +337,8 @@ shared_ptr find_extension(const string& symbol) return make_shared(); // asset + if (symbol == validatesymbol::symbol()) + return make_shared(); if (symbol == createasset::symbol()) return make_shared(); if (symbol == deletelocalasset::symbol() || symbol == "deleteasset" ) @@ -343,6 +361,8 @@ shared_ptr find_extension(const string& symbol) return make_shared(); if (symbol == sendassetfrom::symbol() || symbol == "didsendassetfrom") return make_shared(); + if (symbol == sendmoreasset::symbol() || symbol == "sendassetmore") + return make_shared(); if (symbol == burn::symbol()) return make_shared(); if (symbol == swaptoken::symbol()) diff --git a/src/lib/explorer/extensions/commands/addnode.cpp b/src/lib/explorer/extensions/commands/addnode.cpp index 86340d26b..290047521 100644 --- a/src/lib/explorer/extensions/commands/addnode.cpp +++ b/src/lib/explorer/extensions/commands/addnode.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -41,9 +42,53 @@ console_result addnode::invoke(Json::Value& jv_output, administrator_required_checker(node, auth_.name, auth_.auth); + code errcode; + + if (option_.operation == "reseed") { + node.restart_seeding(true); + jv_output = errcode.message(); + return console_result::okay; + } else if (option_.operation == "list") { + Json::Value seeds_arr; + Json::Value peers_arr; + Json::Value banned_arr; + Json::Value manual_banned_arr; + + auto&& seeds = node.seed_address_list(); + for (const auto& seed : seeds) { + seeds_arr.append(bc::config::authority(seed).to_string()); + } + + auto&& peers = node.connections_ptr()->authority_list(); + for (const auto& authority : peers) { + // invalid authority + if (authority.to_hostname() == "[::]" && authority.port() == 0) { + continue; + } + peers_arr.append(authority.to_string()); + } + + auto&& banned = network::channel::get_banned(); + for (const auto& item : banned) { + banned_arr.append(item.first.to_string() + ", timestamp:" + std::to_string(item.second/1000)); + } + + auto&& manual_banned = network::channel::get_manual_banned(); + for (const auto& authority : manual_banned) { + manual_banned_arr.append(authority.to_string()); + } + + jv_output["seeds"] = seeds_arr; + jv_output["peers"] = peers_arr; + jv_output["banned"] = banned_arr; + jv_output["manual_banned"] = manual_banned_arr; + return console_result::okay; + } else if (argument_.address.empty()) { + throw argument_legality_exception("the option '--NODEADDRESS' is required but missing"); + } + const auto authority = libbitcoin::config::authority(argument_.address); - code errcode; auto handler = [&errcode](const code& ec){ errcode = ec; }; @@ -54,8 +99,11 @@ console_result addnode::invoke(Json::Value& jv_output, } else if ((option_.operation == "add") || (option_.operation == "")){ network::channel::manual_unban(authority); node.store(authority.to_network_address(), handler); + } else if (option_.operation == "peer") { + network::channel::manual_unban(authority); + node.connect(authority); } else { - jv_output = string("Invalid operation [") +option_.operation+"]." ; + jv_output = std::string("Invalid operation [") +option_.operation+"]." ; return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/createasset.cpp b/src/lib/explorer/extensions/commands/createasset.cpp index 7f26325d0..2c7fb1b1b 100644 --- a/src/lib/explorer/extensions/commands/createasset.cpp +++ b/src/lib/explorer/extensions/commands/createasset.cpp @@ -59,7 +59,7 @@ console_result createasset::invoke(Json::Value& jv_output, auto issued_did = option_.issuer; check_did_symbol(issued_did); - if (option_.description.length() > ASSET_DETAIL_DESCRIPTION_FIX_SIZE) + if (option_.description.length() > chain::ASSET_DETAIL_DESCRIPTION_FIX_SIZE) throw asset_description_length_exception{"asset description length must be less than 64."}; auto threshold = option_.secondaryissue_threshold; if ((threshold < -1) || (threshold > 100)) { diff --git a/src/lib/explorer/extensions/commands/createrawtx.cpp b/src/lib/explorer/extensions/commands/createrawtx.cpp index 24de0b42e..ea2854eee 100644 --- a/src/lib/explorer/extensions/commands/createrawtx.cpp +++ b/src/lib/explorer/extensions/commands/createrawtx.cpp @@ -35,24 +35,40 @@ console_result createrawtx::invoke(Json::Value& jv_output, libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); - blockchain.uppercase_symbol(option_.symbol); - if (!option_.mychange_address.empty() && !blockchain.is_valid_address(option_.mychange_address)) - throw toaddress_invalid_exception{"invalid address " + option_.mychange_address}; + // check mychange + std::string change_address; + if (!option_.mychange_address.empty()) { + change_address = get_address(option_.mychange_address, blockchain); + } // check senders - if (option_.senders.empty()) { - throw fromaddress_invalid_exception{"senders can not be empty!"}; + if (option_.senders.empty() && option_.utxos.empty()) { + throw fromaddress_invalid_exception{"senders and utxos can not both be empty!"}; } + std::vector senders; + std::set senders_set; + std::string from_did; + for (auto& each : option_.senders) { - if (!blockchain.is_valid_address(each)) { - throw fromaddress_invalid_exception{"invalid sender address " + each}; + auto addr = get_address(each, blockchain); + // filter script address + if (blockchain.is_script_address(addr)) { + throw fromaddress_invalid_exception{"invalid sender did/address " + each}; + } + if (senders_set.insert(addr).second) { + senders.emplace_back(addr); + if (from_did.empty() && !blockchain.is_valid_address(each)) { + from_did = each; + } } + } - // filter script address - if (blockchain.is_script_address(each)) { - throw fromaddress_invalid_exception{"invalid sender address " + each}; + // from did is meaningful only when there is exact one sender + if (!from_did.empty()) { + if (!(senders.size() == 1 && option_.utxos.empty())) { + from_did = ""; } } @@ -75,17 +91,23 @@ console_result createrawtx::invoke(Json::Value& jv_output, } // receiver - receiver_record record; std::vector receivers; for ( auto& each : option_.receivers) { colon_delimited2_item item(each); - record.target = item.first(); - // address check - if (!blockchain.is_valid_address(record.target)) { - throw toaddress_invalid_exception{"invalid receiver address " + record.target}; + + attachment attach; + auto addr = get_address(item.first(), attach, false, blockchain); + if (!from_did.empty()) { + attach.set_from_did(from_did); + attach.set_version(DID_ATTACH_VERIFY_VERSION); } + receiver_record record; + record.target = addr; + record.attach_elem = std::move(attach); + record.type = type; record.symbol = option_.symbol; + if (record.symbol.empty()) { record.amount = item.second(); // etp amount record.asset_amount = 0; @@ -99,7 +121,6 @@ console_result createrawtx::invoke(Json::Value& jv_output, throw argument_legality_exception{"invalid asset amount parameter " + each}; } - record.type = type; receivers.push_back(record); } @@ -110,33 +131,96 @@ console_result createrawtx::invoke(Json::Value& jv_output, std::shared_ptr sp_send_helper; switch (type) { - case utxo_attach_type::etp: - case utxo_attach_type::asset_transfer: { - sp_send_helper = std::make_shared(blockchain, type, - std::move(option_.senders), std::move(receivers), - std::move(option_.symbol), std::move(option_.mychange_address), - std::move(option_.message), option_.fee); - break; + case utxo_attach_type::etp: + case utxo_attach_type::asset_transfer: { + sp_send_helper = std::make_shared( + blockchain, type, + std::move(senders), std::move(receivers), + std::move(option_.symbol), std::move(change_address), + std::move(option_.message), + option_.fee, option_.locktime); + break; + } + + case utxo_attach_type::deposit: { + sp_send_helper = std::make_shared( + blockchain, type, + std::move(senders), std::move(receivers), + option_.deposit, std::move(change_address), + std::move(option_.message), + option_.fee, option_.locktime); + break; + } + + default: { + throw argument_legality_exception{"invalid transaction type."}; + break; + } + } + + history::list utxo_list; + std::unordered_map utxo_seq_map; //((hash, index), sequence) + for (const std::string utxo : option_.utxos) { + const auto utxo_stru = bc::split(utxo, ":"); + if ((utxo_stru.size() != 2) && (utxo_stru.size() != 3)) { + throw argument_legality_exception{"invalid utxo: " + utxo}; } - case utxo_attach_type::deposit: { - sp_send_helper = std::make_shared(blockchain, type, - std::move(option_.senders), std::move(receivers), - option_.deposit, std::move(option_.mychange_address), - std::move(option_.message), option_.fee); - break; + const bc::config::hash256 hash = utxo_stru[0]; + + uint64_t tx_height = 0; + bc::chain::transaction tx; + auto exist = blockchain.get_transaction(hash, tx, tx_height); + if (!exist) { + throw tx_notfound_exception{"transaction[" + utxo_stru[0] + "] does not exist!"}; } - default: { - throw argument_legality_exception{"invalid transaction type."}; - break; + const auto utxo_index = to_uint32_throw(utxo_stru[1], "wrong utxo index!"); + if ( !(utxo_index < tx.outputs.size()) ) { + throw tx_notfound_exception{"output index[" + utxo_stru[1] + "] of transaction[" + utxo_stru[0] + "] is out of range!"}; + } + if (utxo_stru.size() == 3) { + if ((chain::get_script_context() & chain::script_context::bip112_enabled) == 0) { + throw argument_legality_exception{"invalid utxo: " + utxo + ", lock sequence(bip112) is not enabled"}; + } + input_point utxo_point(hash, utxo_index); + if (utxo_seq_map.count(utxo_point)) { + throw argument_legality_exception{"duplicate utxo: " + utxo}; + } + const auto utxo_sequence = to_uint32_throw(utxo_stru[2], "wrong utxo sequence!"); + utxo_seq_map[utxo_point] = utxo_sequence; } + + history h; + h.output.hash = tx.hash(); + h.output.index = utxo_index; + h.output_height = tx_height; + h.value = tx.outputs[utxo_index].value; + utxo_list.push_back(h); } + if (!utxo_list.empty()) + sp_send_helper->sync_fetchutxo("", "", base_transfer_common::FILTER_ALL, utxo_list); + sp_send_helper->exec(); auto&& tx = sp_send_helper->get_transaction(); + + // set sequence + if (!utxo_seq_map.empty()) { + for (auto& input : tx.inputs) { + if (!utxo_seq_map.count(input.previous_output)) { + continue; + } + if ((input.sequence & bc::relative_locktime_disabled) || + input.sequence == 0 || + input.sequence == bc::max_input_sequence) { + input.sequence = utxo_seq_map[input.previous_output]; + } + } + } + // output json jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx, false); diff --git a/src/lib/explorer/extensions/commands/deposit.cpp b/src/lib/explorer/extensions/commands/deposit.cpp index ce540c276..567435188 100644 --- a/src/lib/explorer/extensions/commands/deposit.cpp +++ b/src/lib/explorer/extensions/commands/deposit.cpp @@ -35,8 +35,20 @@ console_result deposit::invoke(Json::Value& jv_output, { auto& blockchain = node.chain_impl(); blockchain.is_account_passwd_valid(auth_.name, auth_.auth); - if(!argument_.address.empty() && !blockchain.is_valid_address(argument_.address)) - throw address_invalid_exception{"invalid address!"}; + + attachment attach; + std::string addr; + + if (argument_.address.empty()) { + auto pvaddr = blockchain.get_account_addresses(auth_.name); + if(!pvaddr || pvaddr->empty()) + throw address_list_nullptr_exception{"nullptr for address list"}; + + addr = get_random_payment_address(pvaddr, blockchain); + } + else { + addr = get_address(argument_.address, attach, false, blockchain); + } if (argument_.deposit != 7 && argument_.deposit != 30 && argument_.deposit != 90 && argument_.deposit != 182 @@ -45,18 +57,9 @@ console_result deposit::invoke(Json::Value& jv_output, throw account_deposit_period_exception{"deposit must be one in [7, 30, 90, 182, 365]."}; } - auto pvaddr = blockchain.get_account_addresses(auth_.name); - if(!pvaddr || pvaddr->empty()) - throw address_list_nullptr_exception{"nullptr for address list"}; - - auto addr = argument_.address; - if (addr.empty()) { - addr = get_random_payment_address(pvaddr, blockchain); - } - // receiver std::vector receiver{ - {addr, "", argument_.amount, 0, utxo_attach_type::deposit, attachment()} + {addr, "", argument_.amount, 0, utxo_attach_type::deposit, attach} }; auto deposit_helper = depositing_etp(*this, blockchain, std::move(auth_.name), std::move(auth_.auth), std::move(addr), std::move(receiver), argument_.deposit, argument_.fee); diff --git a/src/lib/explorer/extensions/commands/dumpkeyfile.cpp b/src/lib/explorer/extensions/commands/dumpkeyfile.cpp index 77c80f683..63b93a992 100644 --- a/src/lib/explorer/extensions/commands/dumpkeyfile.cpp +++ b/src/lib/explorer/extensions/commands/dumpkeyfile.cpp @@ -70,6 +70,18 @@ console_result dumpkeyfile::invoke(Json::Value& jv_output, } file_root["multisigs"] = multisig_lst; + Json::Value script_lst; + script_lst.resize(0); + for (auto sc : acc->get_script_vec()) { + Json::Value script; + script["address"] = sc.get_address(); + script["script"] = encode_base16(sc.get_script()); + script["description"] = sc.get_description(); + + script_lst.append(script); + } + file_root["scripts"] = script_lst; + Json::Value result; if (!option_.is_data) { @@ -84,7 +96,7 @@ console_result dumpkeyfile::invoke(Json::Value& jv_output, argument_.dst /= keyfile_name; } else { - string dstpath = argument_.dst.string(); + std::string dstpath = argument_.dst.string(); if (dstpath.length() > 0 && dstpath[0] == '~') { char *home_dir = getenv("HOME"); if (home_dir && strlen(home_dir) != 0) { diff --git a/src/lib/explorer/extensions/commands/getaddressasset.cpp b/src/lib/explorer/extensions/commands/getaddressasset.cpp index 9d4dbd022..4d75ba6d3 100644 --- a/src/lib/explorer/extensions/commands/getaddressasset.cpp +++ b/src/lib/explorer/extensions/commands/getaddressasset.cpp @@ -37,11 +37,11 @@ console_result getaddressasset::invoke(Json::Value& jv_output, libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); - if (!blockchain.is_valid_address(argument_.address)) - throw address_invalid_exception{"invalid address!"}; + const auto address = get_address(argument_.address, blockchain); if (!option_.symbol.empty()) { // check asset symbol + blockchain.uppercase_symbol(option_.symbol); check_asset_symbol(option_.symbol); } @@ -53,7 +53,7 @@ console_result getaddressasset::invoke(Json::Value& jv_output, json_key = "assetcerts"; auto sh_vec = std::make_shared(); - sync_fetch_asset_cert_balance(argument_.address, "", blockchain, sh_vec); + sync_fetch_asset_cert_balance(address, "", blockchain, sh_vec); std::sort(sh_vec->begin(), sh_vec->end()); for (auto& elem: *sh_vec) { if (!option_.symbol.empty() && option_.symbol != elem.get_symbol()) @@ -67,7 +67,7 @@ console_result getaddressasset::invoke(Json::Value& jv_output, json_key = "assets"; auto sh_vec = std::make_shared(); - sync_fetch_asset_deposited_balance(argument_.address, blockchain, sh_vec); + sync_fetch_asset_deposited_balance(address, blockchain, sh_vec); std::sort(sh_vec->begin(), sh_vec->end()); for (auto& elem: *sh_vec) { @@ -88,7 +88,7 @@ console_result getaddressasset::invoke(Json::Value& jv_output, json_key = "assets"; auto sh_vec = std::make_shared(); - sync_fetch_asset_balance(argument_.address, true, blockchain, sh_vec); + sync_fetch_asset_balance(address, true, blockchain, sh_vec); std::sort(sh_vec->begin(), sh_vec->end()); for (auto& elem: *sh_vec) { if (!option_.symbol.empty() && option_.symbol != elem.symbol) diff --git a/src/lib/explorer/extensions/commands/getaddressetp.cpp b/src/lib/explorer/extensions/commands/getaddressetp.cpp index 6443da16f..a20c14334 100644 --- a/src/lib/explorer/extensions/commands/getaddressetp.cpp +++ b/src/lib/explorer/extensions/commands/getaddressetp.cpp @@ -23,6 +23,7 @@ #include #include #include +#include namespace libbitcoin { namespace explorer { @@ -31,36 +32,92 @@ using namespace bc::explorer::config; /************************ getaddressetp *************************/ +Json::Value to_json_value(const deposited_balance& balance) +{ + Json::Value json_balance; + json_balance["address"] = balance.address; + json_balance["deposited_balance"] = balance.balance; + json_balance["bonus_balance"] = balance.bonus; + json_balance["deposited_height"] = balance.deposited_height; + json_balance["expiration_height"] = balance.expiration_height; + json_balance["tx_hash"] = balance.tx_hash; + return json_balance; +} + +Json::Value to_json_value(const balances& addr_balance, uint8_t api_version) +{ + Json::Value json_balance; + if (api_version == 1) { + json_balance["confirmed"] = std::to_string(addr_balance.confirmed_balance); + json_balance["received"] = std::to_string(addr_balance.total_received); + json_balance["unspent"] = std::to_string(addr_balance.unspent_balance); + json_balance["frozen"] = std::to_string(addr_balance.frozen_balance); + } + else { + json_balance["confirmed"] = addr_balance.confirmed_balance; + json_balance["received"] = addr_balance.total_received; + json_balance["unspent"] = addr_balance.unspent_balance; + json_balance["frozen"] = addr_balance.frozen_balance; + json_balance["available"] = (addr_balance.unspent_balance - addr_balance.frozen_balance); + } + return json_balance; +} + +Json::Value to_json_value(const utxo_balance& balance) +{ + Json::Value json_balance; + json_balance["available"] = (balance.unspent_balance - balance.frozen_balance); + json_balance["balance"] = balance.unspent_balance; + json_balance["frozen"] = balance.frozen_balance; + json_balance["utxo_block"] = balance.output_height; + json_balance["utxo_hash"] = balance.output_hash; + json_balance["utxo_index"] = balance.output_index; + return json_balance; +} + console_result getaddressetp::invoke(Json::Value& jv_output, libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); - auto& addr = argument_.address; - bc::explorer::commands::balances addr_balance{0, 0, 0, 0}; - - sync_fetchbalance(addr, blockchain, addr_balance); - - Json::Value jv; - jv["address"] = addr.encoded(); - if (get_api_version() == 1) { - // compatible for version 1: as string value - jv["confirmed"] = std::to_string(addr_balance.confirmed_balance); - jv["received"] = std::to_string(addr_balance.total_received); - jv["unspent"] = std::to_string(addr_balance.unspent_balance); - jv["frozen"] = std::to_string(addr_balance.frozen_balance); + auto&& address = get_address(argument_.address, blockchain); + + wallet::payment_address waddr(address); + + Json::Value balances; + + if (option_.deposited) { + auto deposited_balances = std::make_shared(); + sync_fetch_deposited_balance(waddr, blockchain, deposited_balances); + for (const auto& balance : *deposited_balances) { + balances.append(to_json_value(balance)); + } + } + else if (!option_.utxo) { + commands::balances addr_balance{0, 0, 0, 0}; + sync_fetchbalance(waddr, blockchain, addr_balance); + balances = to_json_value(addr_balance, get_api_version()); + balances["address"] = address; } else { - jv["confirmed"] = addr_balance.confirmed_balance; - jv["received"] = addr_balance.total_received; - jv["unspent"] = addr_balance.unspent_balance; - jv["frozen"] = addr_balance.frozen_balance; + // range check + if (!option_.range.is_valid()) { + throw argument_legality_exception("invalid range option! " + + option_.range.encode_colon_delimited()); + } + auto utxo_balances = std::make_shared(); + sync_fetchbalance(waddr, blockchain, utxo_balances); + for (const auto& balance : *utxo_balances) { + if (option_.range.is_in_range(balance.unspent_balance)) { + balances.append(to_json_value(balance)); + } + } } if (get_api_version() <= 2) { - jv_output["balance"] = jv; + jv_output["balance"] = balances; } else { - jv_output = jv; + jv_output = balances; } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/getasset.cpp b/src/lib/explorer/extensions/commands/getasset.cpp index ca6919b5d..a48c31174 100644 --- a/src/lib/explorer/extensions/commands/getasset.cpp +++ b/src/lib/explorer/extensions/commands/getasset.cpp @@ -42,6 +42,8 @@ console_result getasset::invoke(Json::Value& jv_output, check_asset_symbol(argument_.symbol); } + auto address = get_address(option_.issuer, blockchain); + std::string json_key; Json::Value json_value; auto json_helper = config::json_helper(get_api_version()); @@ -50,7 +52,7 @@ console_result getasset::invoke(Json::Value& jv_output, json_key = "assetcerts"; // get asset cert in blockchain - auto sh_vec = blockchain.get_issued_asset_certs(); + auto sh_vec = blockchain.get_issued_asset_certs(address); if (argument_.symbol.empty()) { std::set symbols; std::sort(sh_vec->begin(), sh_vec->end()); @@ -83,7 +85,7 @@ console_result getasset::invoke(Json::Value& jv_output, json_key = "assets"; // get asset in blockchain - auto sh_vec = blockchain.get_issued_assets(argument_.symbol); + auto sh_vec = blockchain.get_issued_assets(argument_.symbol, address); if (argument_.symbol.empty()) { std::sort(sh_vec->begin(), sh_vec->end()); std::set symbols; diff --git a/src/lib/explorer/extensions/commands/getblock.cpp b/src/lib/explorer/extensions/commands/getblock.cpp index 212d78b9a..23bdc8acd 100644 --- a/src/lib/explorer/extensions/commands/getblock.cpp +++ b/src/lib/explorer/extensions/commands/getblock.cpp @@ -42,7 +42,7 @@ console_result getblock::invoke(Json::Value& jv_output, if (argument_.hash_or_height.size() < 18) { // fetch_block via height - auto block_height = std::stoull(argument_.hash_or_height); + auto block_height = to_uint64_throw(argument_.hash_or_height, "wrong block height!"); std::promise p; auto& blockchain = node.chain_impl(); diff --git a/src/lib/explorer/extensions/commands/getblockheader.cpp b/src/lib/explorer/extensions/commands/getblockheader.cpp index d73d04bbd..84e80ac39 100644 --- a/src/lib/explorer/extensions/commands/getblockheader.cpp +++ b/src/lib/explorer/extensions/commands/getblockheader.cpp @@ -40,31 +40,16 @@ using namespace bc::explorer::config; console_result getblockheader::invoke(Json::Value& jv_output, libbitcoin::server::server_node& node) { - - uint64_t height = 0; auto& blockchain = node.chain_impl(); - if (!blockchain.get_last_height(height)) { - throw block_last_height_get_exception{"query last height failure."}; - } - - if (option_.height != std::numeric_limits::max()) { - height = option_.height; - } - - const auto connection = get_connection(*this); - - obelisk_client client(connection); - - if (!client.connect(connection)) { - throw connection_exception{"Could not connect to mvsd port 9921."}; - } - - encoding json_format{"json"}; - std::ostringstream output; - callback_state state(output, output, json_format); + std::promise p; - auto on_done = [this, &jv_output](const chain::header & header) + auto handler = [this, &p, &jv_output](const code& ec, const chain::header& header) { + if (ec) { + p.set_value(ec); + return; + } + auto&& jheader = config::json_helper(get_api_version()).prop_tree(header); if (get_api_version() <= 2) { @@ -100,26 +85,34 @@ console_result getblockheader::invoke(Json::Value& jv_output, jv_output = jheader; } } - }; - auto on_error = [&state](const code & error) - { - state.succeeded(error); + p.set_value(error::success); }; // Height is ignored if both are specified. // Use the null_hash as sentinel to determine whether to use height or hash. const hash_digest& hash = option_.hash; if (hash == null_hash) { - client.blockchain_fetch_block_header(on_error, on_done, height); + uint64_t height = 0; + if (option_.height != std::numeric_limits::max()) { + height = option_.height; + } + else if (!blockchain.get_last_height(height)) { + throw block_last_height_get_exception{"query last height failure."}; + } + + blockchain.fetch_block_header(height, handler); } else { - client.blockchain_fetch_block_header(on_error, on_done, hash); + blockchain.fetch_block_header(hash, handler); } - client.wait(); + auto result = p.get_future().get(); + if (result) { + throw block_header_get_exception{ result.message() }; + } - return state.get_result(); + return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/getinfo.cpp b/src/lib/explorer/extensions/commands/getinfo.cpp index 20102e9a9..9a5cac3b6 100644 --- a/src/lib/explorer/extensions/commands/getinfo.cpp +++ b/src/lib/explorer/extensions/commands/getinfo.cpp @@ -19,7 +19,6 @@ */ #include -#include #include #include #include @@ -54,7 +53,7 @@ console_result getinfo::invoke(Json::Value& jv_output, auto& jv = jv_output; if (get_api_version() <= 2) { jv["protocol-version"] = node.network_settings().protocol; - jv["wallet-version"] = MVS_EXPLORER_VERSION; + jv["wallet-version"] = MVS_VERSION; jv["database-version"] = MVS_DATABASE_VERSION; jv["testnet"] = blockchain.chain_settings().use_testnet_rules; jv["peers"] = get_connections_count(node); @@ -69,7 +68,7 @@ console_result getinfo::invoke(Json::Value& jv_output, } else { jv["protocol_version"] = node.network_settings().protocol; - jv["wallet_version"] = MVS_EXPLORER_VERSION; + jv["wallet_version"] = MVS_VERSION; jv["database_version"] = MVS_DATABASE_VERSION; jv["testnet"] = blockchain.chain_settings().use_testnet_rules; jv["peers"] = get_connections_count(node); diff --git a/src/lib/explorer/extensions/commands/getlocked.cpp b/src/lib/explorer/extensions/commands/getlocked.cpp new file mode 100644 index 000000000..c65d5c526 --- /dev/null +++ b/src/lib/explorer/extensions/commands/getlocked.cpp @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { +using namespace bc::explorer::config; + +/************************ getlocked *************************/ + +console_result getlocked::invoke(Json::Value& jv_output, + libbitcoin::server::server_node& node) +{ + auto& blockchain = node.chain_impl(); + auto&& address = get_address(argument_.address, blockchain); + + bool is_asset = !is_default_invalid_asset_symbol(option_.asset_symbol); + if (is_asset) { + blockchain.uppercase_symbol(option_.asset_symbol); + check_asset_symbol(option_.asset_symbol); + } + auto asset_symbol = is_asset ? option_.asset_symbol : std::string(""); + + Json::Value balances; + + auto sh_vec = std::make_shared(); + sync_fetch_locked_balance(address, blockchain, sh_vec, asset_symbol, option_.expiration); + std::sort(sh_vec->begin(), sh_vec->end()); + + for (const auto& balance: *sh_vec) { + Json::Value json_balance; + json_balance["address"] = balance.address; + json_balance["locked_balance"] = balance.locked_value; + json_balance["locked_height"] = balance.locked_height; + json_balance["lock_at_height"] = balance.expiration_height - balance.locked_height; + json_balance["expiration_height"] = balance.expiration_height; + if (is_asset) { + json_balance["symbol"] = asset_symbol; + } + + balances.append(json_balance); + } + + jv_output = balances; + + return console_result::okay; +} + + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/src/lib/explorer/extensions/commands/getnewaccount.cpp b/src/lib/explorer/extensions/commands/getnewaccount.cpp index 4cd9cea42..691b8d487 100644 --- a/src/lib/explorer/extensions/commands/getnewaccount.cpp +++ b/src/lib/explorer/extensions/commands/getnewaccount.cpp @@ -33,13 +33,27 @@ using namespace bc::explorer::config; /************************ getnewaccount *************************/ +//76066276 is HD private key version +const uint64_t prefixes = wallet::hd_private::to_prefixes(76066276, 0); + console_result getnewaccount::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + bc::server::server_node& node) +{ + if (auth_.name.empty() && auth_.auth.empty()) { + return create_address(jv_output, node); + } + + return create_account(jv_output, node); +} + +console_result getnewaccount::create_account(Json::Value& jv_output, + bc::server::server_node& node) { #ifdef NDEBUG if (auth_.name.length() > 128 || auth_.name.length() < 3 || - auth_.auth.length() > 128 || auth_.auth.length() < 6) + auth_.auth.length() > 128 || auth_.auth.length() < 6) { throw argument_legality_exception{"name length in [3, 128], password length in [6, 128]"}; + } #endif auto& blockchain = node.chain_impl(); @@ -86,6 +100,57 @@ console_result getnewaccount::invoke(Json::Value& jv_output, return console_result::okay; } +console_result getnewaccount::create_address(Json::Value& jv_output, + bc::server::server_node& node) +{ + auto& blockchain = node.chain_impl(); + + // mainnet payment address version + auto payment_version = wallet::payment_address::mainnet_p2kh; + if (blockchain.chain_settings().use_testnet_rules) { + // testnet payment address version + payment_version = 127; + } + + // create mnemonic words + bc::explorer::config::language opt_language(option_.language); + auto words = get_mnemonic_new(opt_language, get_seed()); + std::string mnemonic = bc::join(words); + + // create master hd private + const auto seed = wallet::decode_mnemonic(words); + bc::config::base16 bs(seed); + const data_chunk& ds = static_cast(bs); + const wallet::hd_private master_hd_private(ds, prefixes); + + // create derive hd private at index 0 + const auto derive_hd_private = master_hd_private.derive_private(0); + std::string hk = derive_hd_private.encoded(); + const auto derive_private_key = wallet::hd_private(hk, prefixes); + + // get public key and payment address + ec_secret secret = derive_private_key.secret(); + wallet::ec_private ec_prv(secret); + ec_compressed point; + bc::secret_to_public(point, secret); + wallet::ec_public ec_pub(point, true); + wallet::payment_address pay_address(ec_pub, payment_version); + + // encode + std::string prv_key = encode_base16(secret); + std::string prv_key_wif = ec_prv.encoded(); + std::string pub_key = ec_pub.encoded(); + std::string addr = pay_address.encoded(); + + // output + jv_output["mnemonic"] = mnemonic; + jv_output["private_key"] = prv_key; + jv_output["public_key"] = pub_key; + jv_output["wif"] = prv_key_wif; + jv_output["address"] = addr; + + return console_result::okay; +} } // namespace commands } // namespace explorer diff --git a/src/lib/explorer/extensions/commands/getnewaddress.cpp b/src/lib/explorer/extensions/commands/getnewaddress.cpp index a1f8b1a7c..de6f6ddfc 100644 --- a/src/lib/explorer/extensions/commands/getnewaddress.cpp +++ b/src/lib/explorer/extensions/commands/getnewaddress.cpp @@ -24,16 +24,48 @@ #include #include #include +#include +#include namespace libbitcoin { namespace explorer { namespace commands { +void test_vrf(const bc::wallet::hd_private& private_key) +{ + wallet::vrf_private vrf_(private_key.secret()); + + log::info("VRF") << "pri:" << vrf_.encoded() + << ", pub:" << encode_base16(vrf_.to_public()); + + data_chunk data{1,2,3,4,5,6,7,8,9,10}; + wallet::vrf_proof proof; + wallet::vrf_hash result; + if (!vrf_.prove(proof, data) + || !wallet::vrf_private::proof_to_hash(result, proof)) { + log::error("Wallet")<<"generate prove failed!"; + } + + log::info("VRF") << "generate prove finish, msg:" << encode_base16(data) + << ", proof:" << encode_base16(proof) + << ", result:" << encode_base16(result); + + wallet::vrf_hash result1; + if (!wallet::vrf_private::verify(result1, proof, vrf_.to_public(), data) + || result != result1) { + log::error("VRF")<< "verify failed!" + << ", result:"<< encode_base16(result) + << ", result1:"<< encode_base16(result1); + } + + log::info("Wallet")<<"ecvrf_verify success!" + << ", result1:" << encode_base16(result1); +} /************************ getnewaddress *************************/ console_result getnewaddress::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + bc::server::server_node& node) { auto& blockchain = node.chain_impl(); auto acc = blockchain.is_account_passwd_valid(auth_.name, auth_.auth); @@ -54,17 +86,17 @@ console_result getnewaddress::invoke(Json::Value& jv_output, } Json::Value addresses; - + std::vector> account_addresses; account_addresses.reserve(option_.count); - const auto seed = decode_mnemonic(words); - libbitcoin::config::base16 bs(seed); + const auto seed = wallet::decode_mnemonic(words); + bc::config::base16 bs(seed); const data_chunk& ds = static_cast(bs); const auto prefixes = bc::wallet::hd_private::to_prefixes(76066276, 0);//76066276 is HD private key version const bc::wallet::hd_private private_key(ds, prefixes); // mainnet payment address version - auto payment_version = 50; + auto payment_version = wallet::payment_address::mainnet_p2kh; if (blockchain.chain_settings().use_testnet_rules) { // testnetpayment address version payment_version = 127; @@ -80,17 +112,22 @@ console_result getnewaddress::invoke(Json::Value& jv_output, // Create the private key from hd_key and the public version. const auto derive_private_key = bc::wallet::hd_private(hk, prefixes); - auto pk = encode_base16(derive_private_key.secret()); +#ifdef PRIVATE_CHAIN + // test_vrf(derive_private_key); +#endif + + auto pk = encode_base16(derive_private_key.secret()); addr->set_prv_key(pk.c_str(), auth_.auth); + // not store public key now ec_compressed point; - libbitcoin::secret_to_public(point, derive_private_key.secret()); + bc::secret_to_public(point, derive_private_key.secret()); // Serialize to the original compression state. - auto ep = ec_public(point, true); + auto ep = wallet::ec_public(point, true); - payment_address pa(ep, payment_version); + wallet::payment_address pa(ep, payment_version); addr->set_address(pa.encoded()); addr->set_status(1); // 1 -- enable address @@ -112,8 +149,10 @@ console_result getnewaddress::invoke(Json::Value& jv_output, jv_output["addresses"] = addresses; } else { - if(addresses.isNull()) - addresses.resize(0); + if (addresses.isNull()) { + addresses.resize(0); + } + jv_output = addresses; } diff --git a/src/lib/explorer/extensions/commands/getnewmultisig.cpp b/src/lib/explorer/extensions/commands/getnewmultisig.cpp index 5874e53da..b3c107fe4 100644 --- a/src/lib/explorer/extensions/commands/getnewmultisig.cpp +++ b/src/lib/explorer/extensions/commands/getnewmultisig.cpp @@ -29,6 +29,7 @@ namespace libbitcoin { namespace explorer { namespace commands { using namespace bc::explorer::config; +using payment_address = wallet::payment_address; console_result getnewmultisig::invoke( Json::Value& jv_output, @@ -89,6 +90,9 @@ console_result getnewmultisig::invoke( std::string self_prvkey; auto found = false; for (auto& each : *pvaddr) { + if (each.get_status() == account_address_status::script_addr) { + continue; + } self_prvkey = each.get_prv_key(auth_.auth); auto&& target_pub_key = ec_to_xxx_impl("ec-to-public", self_prvkey); if (target_pub_key == self_pubkey) { @@ -129,7 +133,7 @@ console_result getnewmultisig::invoke( auto multisig_script = acc_multisig.get_multisig_script(); chain::script payment_script; payment_script.from_string(multisig_script); - if (script_pattern::pay_multisig != payment_script.pattern()) + if (chain::script_pattern::pay_multisig != payment_script.pattern()) throw multisig_script_exception{ std::string("invalid multisig script : ") + multisig_script }; payment_address address(payment_script, payment_address::mainnet_p2sh); diff --git a/src/lib/explorer/extensions/commands/getpublickey.cpp b/src/lib/explorer/extensions/commands/getpublickey.cpp index 0c9f2513b..19deca011 100644 --- a/src/lib/explorer/extensions/commands/getpublickey.cpp +++ b/src/lib/explorer/extensions/commands/getpublickey.cpp @@ -25,6 +25,7 @@ #include #include #include +#include namespace libbitcoin { namespace explorer { @@ -36,48 +37,60 @@ console_result getpublickey::invoke(Json::Value& jv_output, { auto& blockchain = node.chain_impl(); blockchain.is_account_passwd_valid(auth_.name, auth_.auth); - if (!argument_.address.empty() && !blockchain.is_valid_address(argument_.address)) - throw address_invalid_exception{"invalid address parameter!"}; - auto addr = bc::wallet::payment_address(argument_.address); - if(addr.version() == bc::wallet::payment_address::mainnet_p2sh) // for multisig address - throw argument_legality_exception{"script address parameter not allowed!"}; - - auto pvaddr = blockchain.get_account_addresses(auth_.name); - if(!pvaddr) - throw address_list_nullptr_exception{"nullptr for address list"}; + std::string pub_key; + std::string address; - // set random address - if (argument_.address.empty()) { - argument_.address = get_random_payment_address(pvaddr, blockchain); + data_chunk public_key_data; + if (decode_base16(public_key_data, argument_.address) && + is_public_key(public_key_data)) { + pub_key = argument_.address; + auto pay_address = wallet::ec_public(public_key_data).to_payment_address(); + address = pay_address.encoded(); } + else { + auto pvaddr = blockchain.get_account_addresses(auth_.name); + if(!pvaddr) + throw address_list_nullptr_exception{"nullptr for address list"}; - // get public key - std::string prv_key; - std::string pub_key; - auto found = false; - for (auto& each : *pvaddr){ - if (each.get_address() == argument_.address) { - prv_key = each.get_prv_key(auth_.auth); - pub_key = ec_to_xxx_impl("ec-to-public", prv_key); - - found = true; - break; + if (!argument_.address.empty()) { + address = get_address(argument_.address, blockchain); + } + else { + // set random address + address = get_random_payment_address(pvaddr, blockchain); } - } - if(!found) { - throw account_address_get_exception{pub_key}; + auto addr = bc::wallet::payment_address(address); + if(addr.version() == bc::wallet::payment_address::mainnet_p2sh) // for multisig address + throw argument_legality_exception{"script address parameter not allowed!"}; + + // get public key + std::string prv_key; + auto found = false; + for (auto& each : *pvaddr){ + if (each.get_address() == address) { + prv_key = each.get_prv_key(auth_.auth); + pub_key = ec_to_xxx_impl("ec-to-public", prv_key); + + found = true; + break; + } + } + + if (!found) { + throw address_dismatch_account_exception{"target did/address does not match account. " + argument_.address}; + } } auto& root = jv_output; if (get_api_version() <= 2) { root["public-key"] = pub_key; - root["address"] = argument_.address; + root["address"] = address; } else { root["public_key"] = pub_key; - root["address"] = argument_.address; + root["address"] = address; } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/importaddress.cpp b/src/lib/explorer/extensions/commands/importaddress.cpp new file mode 100644 index 000000000..3202dbb58 --- /dev/null +++ b/src/lib/explorer/extensions/commands/importaddress.cpp @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { +namespace fs = boost::filesystem; +using namespace bc::explorer::config; +using payment_address = wallet::payment_address; + +/************************ importkeyfile *************************/ + +console_result importaddress::invoke(Json::Value& jv_output, + libbitcoin::server::server_node& node) +{ + auto& blockchain = node.chain_impl(); + auto account = blockchain.is_account_passwd_valid(auth_.name, auth_.auth); + + account_script acc_script; + + data_chunk redeem_data; + //1. script + if (blockchain.is_script_address(argument_.script)) { + + const std::string& p2sh_address = argument_.script; + acc_script.set_address(p2sh_address); + + } else if (decode_base16(redeem_data, argument_.script)) { + bc::chain::script redeem_script; + if (!redeem_script.from_data(redeem_data, false, bc::chain::script::parse_mode::strict)) { + throw redeem_script_data_exception{"error occured when parse redeem script data."}; + } + + const payment_address address(redeem_script, payment_address::mainnet_p2sh); + const std::string p2sh_address = address.encoded(); // pay address + + acc_script.set_address(p2sh_address); + acc_script.set_script(redeem_data); + + jv_output["script"] = redeem_script.to_string( chain::script_context::all_enabled ); + } else { + throw redeem_script_data_exception{"Unexpect \"SCRIPT\" argument format. Only p2sh address or hex-encoded bitcoin script is supported."}; + } + + if (account->is_script_exist(acc_script)) { + throw multisig_exist_exception{"address[" + acc_script.get_address() + "] already exist."}; + } + + { + acc_script.set_description(option_.description); + + // change account type + account->set_type(account_type::script_); + + // create account address + auto account_address = std::make_shared(); + account_address->set_name(auth_.name); + account_address->set_address(acc_script.get_address()); + + // update account and multisig account + account_address->set_status(account_address_status::script_addr); + + account->set_script(acc_script); + + // store them + blockchain.store_account(account); + blockchain.store_account_address(account_address); + } + + jv_output["address"] = acc_script.get_address(); + return console_result::okay; +} + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/src/lib/explorer/extensions/commands/importkeyfile.cpp b/src/lib/explorer/extensions/commands/importkeyfile.cpp index a0d069915..877f03f3d 100644 --- a/src/lib/explorer/extensions/commands/importkeyfile.cpp +++ b/src/lib/explorer/extensions/commands/importkeyfile.cpp @@ -144,6 +144,31 @@ console_result importkeyfile::invoke(Json::Value& jv_output, } } + //p2sh + { + const auto &redeem_scripts = file_root["scripts"]; + const int size = redeem_scripts.size(); + for (int i=0; i < size; ++i) { + const std::string desc = redeem_scripts[i]["description"].asString(); + const std::string addr = redeem_scripts[i]["address"].asString(); + const std::string hex = redeem_scripts[i]["script"].asString(); + + const std::string& script = hex.empty() ? addr : hex; + + // get n new sub-address + Json::Value jv_temp; + std::vector vec_cmds = {"importaddress", auth_.name.c_str(), auth_.auth.c_str(), + script.c_str(), + "-d", desc.c_str() + }; + + if (dispatch_command(vec_cmds.size(), vec_cmds.data(), jv_temp, node, get_api_version()) != console_result::okay) { + throw address_generate_exception{std::string("Failed to importaddress for [") + addr + "]."}; + } + + } + } + Json::Value jv_temp; std::vector vec_cmds = {"listaddresses", auth_.name.c_str(), auth_.auth.c_str()}; if (dispatch_command(vec_cmds.size(), vec_cmds.data(), jv_temp, node, get_api_version()) != console_result::okay) { diff --git a/src/lib/explorer/extensions/commands/issuecert.cpp b/src/lib/explorer/extensions/commands/issuecert.cpp index 033a531cc..0e9352a14 100644 --- a/src/lib/explorer/extensions/commands/issuecert.cpp +++ b/src/lib/explorer/extensions/commands/issuecert.cpp @@ -29,18 +29,8 @@ namespace libbitcoin { namespace explorer { namespace commands { -template -struct HexTo { - ElemT value; - operator ElemT() const {return value;} - friend std::istream& operator>>(std::istream& in, HexTo& out) { - in >> std::hex >> out.value; - return in; - } -}; - console_result issuecert::invoke (Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); blockchain.is_account_passwd_valid(auth_.name, auth_.auth); @@ -56,104 +46,46 @@ console_result issuecert::invoke (Json::Value& jv_output, if (!blockchain.is_valid_address(to_address)) { throw address_invalid_exception{"invalid did parameter! " + to_did}; } + if (!blockchain.get_account_address(auth_.name, to_address)) { throw address_dismatch_account_exception{"target did does not match account. " + to_did}; } // check asset cert types - auto certs_create = asset_cert_ns::none; - std::map cert_map = { - {"naming", asset_cert_ns::naming}, - {"marriage", asset_cert_ns::marriage}, - {"kyc", asset_cert_ns::kyc} - }; - auto iter = cert_map.find(argument_.cert); - if (iter != cert_map.end()) { - certs_create = iter->second; - } - else { - try { - if (argument_.cert.compare(0, 2, "0x") == 0) { - certs_create = boost::lexical_cast>(argument_.cert.c_str()); - } - else { - certs_create = boost::lexical_cast(argument_.cert.c_str()); - } - - if (certs_create < asset_cert_ns::custom) { - throw asset_cert_exception("invalid asset cert type " + argument_.cert); - } - } - catch(boost::bad_lexical_cast const&) { - throw asset_cert_exception("invalid asset cert type " + argument_.cert); - } - } - - if (certs_create == asset_cert_ns::naming) { - // check symbol is valid. - auto pos = argument_.symbol.find("."); - if (pos == std::string::npos) { - throw asset_symbol_name_exception("invalid naming cert symbol " + argument_.symbol - + ", it should contain a dot '.'"); - } - - auto&& domain = asset_cert::get_domain(argument_.symbol); - if (!asset_cert::is_valid_domain(domain)) { - throw asset_symbol_name_exception("invalid naming cert symbol " + argument_.symbol - + ", it should contain a valid domain!"); - } - - // check domain naming cert not exist. - if (blockchain.is_asset_cert_exist(argument_.symbol, asset_cert_ns::naming)) { - throw asset_cert_existed_exception( - "naming cert '" + argument_.symbol + "' already exists on the blockchain!"); - } - - // check asset not exist. - if (blockchain.is_asset_exist(argument_.symbol, false)) { - throw asset_symbol_existed_exception( - "asset symbol '" + argument_.symbol + "' already exists on the blockchain!"); - } - - // check domain cert belong to this account. - bool exist = blockchain.is_asset_cert_exist(domain, asset_cert_ns::domain); - if (!exist) { - throw asset_cert_notfound_exception("no domain cert '" + domain + "' found!"); - } - - auto cert = blockchain.get_account_asset_cert(auth_.name, domain, asset_cert_ns::domain); - if (!cert) { - throw asset_cert_notowned_exception("no domain cert '" + domain + "' owned by " + auth_.name); - } - } + auto certs_create = check_issue_cert(blockchain, auth_.name, argument_.symbol, argument_.cert); // receiver std::vector receiver{ - {to_address, argument_.symbol, 0, 0, + { + to_address, argument_.symbol, 0, 0, certs_create, utxo_attach_type::asset_cert_issue, - attachment("", to_did)} + attachment("", to_did) + } }; if (!option_.memo.empty()) { check_message(option_.memo); - receiver.push_back({to_address, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(option_.memo))}); + receiver.push_back({ + to_address, "", 0, 0, utxo_attach_type::message, + attachment(0, 0, chain::blockchain_message(option_.memo)) + }); } if (certs_create == asset_cert_ns::naming) { auto&& domain = asset_cert::get_domain(argument_.symbol); - receiver.push_back( - {to_address, domain, 0, 0, - asset_cert_ns::domain, utxo_attach_type::asset_cert, - attachment("", to_did)} - ); + receiver.push_back({ + to_address, domain, 0, 0, + asset_cert_ns::domain, utxo_attach_type::asset_cert, + attachment("", to_did) + }); } - auto helper = issuing_asset_cert(*this, blockchain, - std::move(auth_.name), std::move(auth_.auth), - std::move(to_address), std::move(argument_.symbol), - std::move(receiver), argument_.fee); + auto helper = issuing_asset_cert( + *this, blockchain, + std::move(auth_.name), std::move(auth_.auth), + std::move(to_address), std::move(argument_.symbol), + std::move(receiver), argument_.fee); helper.exec(); diff --git a/src/lib/explorer/extensions/commands/listaddresses.cpp b/src/lib/explorer/extensions/commands/listaddresses.cpp index 29ed068d1..47d3f6a4d 100644 --- a/src/lib/explorer/extensions/commands/listaddresses.cpp +++ b/src/lib/explorer/extensions/commands/listaddresses.cpp @@ -36,18 +36,31 @@ console_result listaddresses::invoke(Json::Value& jv_output, libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); - blockchain.is_account_passwd_valid(auth_.name, auth_.auth); + const auto acc = blockchain.is_account_passwd_valid(auth_.name, auth_.auth); auto& aroot = jv_output; Json::Value addresses; - auto vaddr = blockchain.get_account_addresses(auth_.name); - if(!vaddr) throw address_list_nullptr_exception{"nullptr for address list"}; + if (option_.b_script) { + for (auto sc : acc->get_script_vec()) { + Json::Value script; + script["address"] = sc.get_address(); + script["script"] = encode_base16(sc.get_script()); + script["description"] = sc.get_description(); - for (auto& it: *vaddr){ - addresses.append(it.get_address()); + addresses.append(script); + } + } else { + auto vaddr = blockchain.get_account_addresses(auth_.name); + if(!vaddr) throw address_list_nullptr_exception{"nullptr for address list"}; + + for (auto& it: *vaddr){ + addresses.append(it.get_address()); + } } + + if (get_api_version() == 1 && addresses.isNull()) { // compatible for v1 aroot["addresses"] = ""; } diff --git a/src/lib/explorer/extensions/commands/listtxs.cpp b/src/lib/explorer/extensions/commands/listtxs.cpp index 961e32a06..f98391f5a 100644 --- a/src/lib/explorer/extensions/commands/listtxs.cpp +++ b/src/lib/explorer/extensions/commands/listtxs.cpp @@ -24,11 +24,13 @@ #include #include #include +#include namespace libbitcoin { namespace explorer { namespace commands { using namespace bc::explorer::config; +using payment_address = wallet::payment_address; class BC_API tx_block_info { @@ -68,15 +70,28 @@ console_result listtxs::invoke(Json::Value& jv_output, { using namespace libbitcoin::config; // for hash256 auto& blockchain = node.chain_impl(); - blockchain.is_account_passwd_valid(auth_.name, auth_.auth); - // address option check - if (!argument_.address.empty() && !blockchain.is_valid_address(argument_.address)) - throw address_invalid_exception{"invalid address parameter!"}; + + auto sh_addr_vec = std::make_shared>(); + + // collect address + if (argument_.address.empty()) { + blockchain.is_account_passwd_valid(auth_.name, auth_.auth); + auto pvaddr = blockchain.get_account_addresses(auth_.name); + if (!pvaddr) + throw address_invalid_exception{"nullptr for address list"}; + + for (auto& elem : *pvaddr) { + sh_addr_vec->push_back(elem.get_address()); + } + } else { // address exist in command + auto addr = get_address(argument_.address, blockchain); + sh_addr_vec->push_back(addr); + } + // height check - if (option_.height.first() - && option_.height.second() - && (option_.height.first() >= option_.height.second())) { - throw block_height_exception{"invalid height option!"}; + if (!option_.height.is_valid()) { + throw block_height_exception{"invalid height option! " + + option_.height.encode_colon_delimited()}; } // symbol check if (!argument_.symbol.empty()) { @@ -93,20 +108,6 @@ console_result listtxs::invoke(Json::Value& jv_output, }; auto sh_txs = std::make_shared>(); - auto sh_addr_vec = std::make_shared>(); - - // collect address - if (argument_.address.empty()) { - auto pvaddr = blockchain.get_account_addresses(auth_.name); - if (!pvaddr) - throw address_invalid_exception{"nullptr for address list"}; - - for (auto& elem : *pvaddr) { - sh_addr_vec->push_back(elem.get_address()); - } - } else { // address exist in command - sh_addr_vec->push_back(argument_.address); - } // scan all addresses business record for (auto& each : *sh_addr_vec) { diff --git a/src/lib/explorer/extensions/commands/lock.cpp b/src/lib/explorer/extensions/commands/lock.cpp new file mode 100644 index 000000000..b2430f4fb --- /dev/null +++ b/src/lib/explorer/extensions/commands/lock.cpp @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + + +console_result lock::invoke(Json::Value& jv_output, + libbitcoin::server::server_node& node) +{ + auto& blockchain = node.chain_impl(); + blockchain.is_account_passwd_valid(auth_.name, auth_.auth); + + bool is_asset = !is_default_invalid_asset_symbol(option_.asset_symbol); + if (is_asset) { + blockchain.uppercase_symbol(option_.asset_symbol); + check_asset_symbol(option_.asset_symbol); + } + + attachment attach; + std::string to_address = get_address(argument_.to, attach, false, blockchain); + std::string change_address = get_address(option_.change, blockchain); + + bc::wallet::payment_address addr(to_address); + if (addr.version() == bc::wallet::payment_address::mainnet_p2sh) { // for multisig address + throw argument_legality_exception{"script address parameter not allowed!"}; + } + + auto sp_account_address = blockchain.get_account_address(auth_.name, to_address); + if (!sp_account_address) { + throw address_dismatch_account_exception{"target address does not match account. " + to_address}; + } + + if (argument_.amount <= 0) { + throw argument_legality_exception("invalid amount parameter!"); + } + + if ((argument_.sequence & bc::relative_locktime_disabled) || + argument_.sequence == 0 || + argument_.sequence == bc::max_input_sequence) { + throw argument_legality_exception( + "invalid sequence parameter!" + std::to_string(argument_.sequence)); + } + + // receiver + std::vector receiver{ + { + to_address, + (is_asset ? option_.asset_symbol : std::string("")), + (is_asset ? 0 : argument_.amount), + (is_asset ? argument_.amount : 0), + (is_asset ? utxo_attach_type::asset_transfer : utxo_attach_type::etp), + attach, + true + } + }; + + if (!option_.memo.empty()) { + check_message(option_.memo); + + receiver.push_back({ + to_address, "", 0, 0, utxo_attach_type::message, + attachment(0, 0, chain::blockchain_message(option_.memo)) + }); + } + + lock_sending send_helper(*this, blockchain, + std::move(auth_.name), std::move(auth_.auth), + "", std::move(receiver), + std::move(change_address), + option_.fee, argument_.sequence); + send_helper.exec(); + + // json output + auto tx = send_helper.get_transaction(); + jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); + + return console_result::okay; +} + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/src/lib/explorer/extensions/commands/popblock.cpp b/src/lib/explorer/extensions/commands/popblock.cpp new file mode 100644 index 000000000..c2fdb9399 --- /dev/null +++ b/src/lib/explorer/extensions/commands/popblock.cpp @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + + +/************************ popblock *************************/ +console_result popblock::invoke (Json::Value& jv_output, + libbitcoin::server::server_node& node) +{ + if (argument_.height == 0) { + throw argument_legality_exception("pop height should be greater than 0."); + } + + auto& blockchain = node.chain_impl(); + blockchain.set_sync_disabled(true); + + // lock database writing while poping + unique_lock lock(blockchain.get_mutex()); + + uint64_t old_height = 0; + if (!blockchain.get_last_height(old_height)) { + throw block_last_height_get_exception("get last height failed."); + } + + blockchain::block_detail::list released_blocks; + blockchain.pop_from(released_blocks, argument_.height); + + if (!released_blocks.empty() && consensus::witness::is_dpos_enabled()) { + uint64_t new_height = 0; + if (blockchain.get_last_height(new_height) && + !consensus::witness::is_in_same_epoch(old_height, new_height)) { + consensus::witness::get().update_witness_list(new_height); + } + } + + blockchain.set_sync_disabled(false); + + jv_output = "pop block from " + std::to_string(argument_.height) + " finished."; + + return console_result::okay; +} + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/src/lib/explorer/extensions/commands/registermit.cpp b/src/lib/explorer/extensions/commands/registermit.cpp index d1ed03d13..a0d277f4d 100644 --- a/src/lib/explorer/extensions/commands/registermit.cpp +++ b/src/lib/explorer/extensions/commands/registermit.cpp @@ -37,19 +37,19 @@ void registermit::check_symbol_content(const std::string& symbol, const std::str } // reserve 4 bytes - if (symbol.size() > (ASSET_MIT_SYMBOL_FIX_SIZE - 4)) { + if (symbol.size() > (chain::ASSET_MIT_SYMBOL_FIX_SIZE - 4)) { throw asset_symbol_length_exception{"Symbol length must be less than " - + std::to_string(ASSET_MIT_SYMBOL_FIX_SIZE - 4) + ". " + symbol}; + + std::to_string(chain::ASSET_MIT_SYMBOL_FIX_SIZE - 4) + ". " + symbol}; } // check symbol check_mit_symbol(symbol, true); // check content - if (content.size() > ASSET_MIT_CONTENT_FIX_SIZE) { + if (content.size() > chain::ASSET_MIT_CONTENT_FIX_SIZE) { throw argument_size_invalid_exception( "Content length must be less than " - + std::to_string(ASSET_MIT_CONTENT_FIX_SIZE) + ". " + content); + + std::to_string(chain::ASSET_MIT_CONTENT_FIX_SIZE) + ". " + content); } } @@ -76,10 +76,10 @@ console_result registermit::invoke (Json::Value& jv_output, else { if (option_.content.size() > 0) { // check content - if (option_.content.size() > ASSET_MIT_CONTENT_FIX_SIZE) { + if (option_.content.size() > chain::ASSET_MIT_CONTENT_FIX_SIZE) { throw argument_size_invalid_exception( "Content length must be less than " - + std::to_string(ASSET_MIT_CONTENT_FIX_SIZE) + ". " + option_.content); + + std::to_string(chain::ASSET_MIT_CONTENT_FIX_SIZE) + ". " + option_.content); } use_unified_content = true; @@ -147,8 +147,7 @@ console_result registermit::invoke (Json::Value& jv_output, auto helper = registering_mit( *this, blockchain, std::move(auth_.name), std::move(auth_.auth), - std::move(to_address), - "", std::move(mit_map), + std::move(to_address), "", std::move(mit_map), std::move(receiver), argument_.fee); helper.exec(); diff --git a/src/lib/explorer/extensions/commands/secondaryissue.cpp b/src/lib/explorer/extensions/commands/secondaryissue.cpp index d70c30f14..aff9f8838 100644 --- a/src/lib/explorer/extensions/commands/secondaryissue.cpp +++ b/src/lib/explorer/extensions/commands/secondaryissue.cpp @@ -95,7 +95,7 @@ console_result secondaryissue::invoke(Json::Value& jv_output, check_message(option_.memo); receiver.push_back({to_address, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(option_.memo))}); + attachment(0, 0, chain::blockchain_message(option_.memo))}); } auto issue_helper = secondary_issuing_asset(*this, blockchain, diff --git a/src/lib/explorer/extensions/commands/send.cpp b/src/lib/explorer/extensions/commands/send.cpp index 082895c12..67178405e 100644 --- a/src/lib/explorer/extensions/commands/send.cpp +++ b/src/lib/explorer/extensions/commands/send.cpp @@ -32,7 +32,7 @@ namespace commands { console_result send::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + libbitcoin::server::server_node& node) { auto& blockchain = node.chain_impl(); blockchain.is_account_passwd_valid(auth_.name, auth_.auth); @@ -45,6 +45,12 @@ console_result send::invoke(Json::Value& jv_output, throw argument_legality_exception("invalid amount parameter!"); } + // exclude range check + if (!option_.exclude.is_valid()) { + throw argument_legality_exception("invalid exclude option! " + + option_.exclude.encode_colon_delimited()); + } + // receiver std::vector receiver{ {to_address, "", argument_.amount, 0, utxo_attach_type::etp, attach} @@ -53,20 +59,25 @@ console_result send::invoke(Json::Value& jv_output, if (!option_.memo.empty()) { check_message(option_.memo); - receiver.push_back({to_address, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(option_.memo))}); + receiver.push_back({ + to_address, "", 0, 0, utxo_attach_type::message, + attachment(0, 0, chain::blockchain_message(option_.memo)) + }); } - auto send_helper = sending_etp(*this, blockchain, - std::move(auth_.name), std::move(auth_.auth), - "", std::move(receiver), - std::move(change_address), option_.fee); + auto send_helper = sending_etp( + *this, blockchain, + std::move(auth_.name), std::move(auth_.auth), + "", std::move(receiver), + std::move(change_address), + option_.fee, option_.locktime, + std::make_pair(option_.exclude.first(), option_.exclude.second())); send_helper.exec(); // json output auto tx = send_helper.get_transaction(); - jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); + jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); return console_result::okay; } diff --git a/src/lib/explorer/extensions/commands/sendfrom.cpp b/src/lib/explorer/extensions/commands/sendfrom.cpp index 13b4d883f..c39d8ab17 100644 --- a/src/lib/explorer/extensions/commands/sendfrom.cpp +++ b/src/lib/explorer/extensions/commands/sendfrom.cpp @@ -45,6 +45,12 @@ console_result sendfrom::invoke(Json::Value& jv_output, throw argument_legality_exception("invalid amount parameter!"); } + // exclude range check + if (!option_.exclude.is_valid()) { + throw argument_legality_exception("invalid exclude option! " + + option_.exclude.encode_colon_delimited()); + } + // receiver std::vector receiver{ {to_address, "", argument_.amount, 0, utxo_attach_type::etp, attach} @@ -54,13 +60,15 @@ console_result sendfrom::invoke(Json::Value& jv_output, check_message(option_.memo); receiver.push_back({to_address, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(option_.memo))}); + attachment(0, 0, chain::blockchain_message(option_.memo))}); } auto send_helper = sending_etp(*this, blockchain, std::move(auth_.name), std::move(auth_.auth), std::move(from_address), std::move(receiver), - std::move(change_address), option_.fee); + std::move(change_address), option_.fee, + option_.locktime, + std::make_pair(option_.exclude.first(), option_.exclude.second())); send_helper.exec(); diff --git a/src/lib/explorer/extensions/commands/sendmore.cpp b/src/lib/explorer/extensions/commands/sendmore.cpp index 418ac2885..db8912ff3 100644 --- a/src/lib/explorer/extensions/commands/sendmore.cpp +++ b/src/lib/explorer/extensions/commands/sendmore.cpp @@ -70,7 +70,7 @@ console_result sendmore::invoke (Json::Value& jv_output, } receiver.push_back({msg_address, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(option_.memo))}); + attachment(0, 0, chain::blockchain_message(option_.memo))}); } auto send_helper = sending_etp(*this, blockchain, diff --git a/src/lib/explorer/extensions/commands/sendmoreasset.cpp b/src/lib/explorer/extensions/commands/sendmoreasset.cpp new file mode 100644 index 000000000..ac5bef905 --- /dev/null +++ b/src/lib/explorer/extensions/commands/sendmoreasset.cpp @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + + +console_result sendmoreasset::invoke(Json::Value& jv_output, + libbitcoin::server::server_node& node) +{ + auto& blockchain = node.chain_impl(); + blockchain.is_account_passwd_valid(auth_.name, auth_.auth); + blockchain.uppercase_symbol(argument_.symbol); + + // check asset symbol + check_asset_symbol(argument_.symbol); + + // receiver + utxo_attach_type attach_type = option_.attenuation_model_param.empty() + ? utxo_attach_type::asset_transfer : utxo_attach_type::asset_attenuation_transfer; + + std::string msg_address; + + // receiver + std::vector receiver; + + for (auto& each : argument_.receivers) { + colon_delimited2_item item(each); + + attachment attach; + std::string address = get_address(item.first(), attach, false, blockchain); + if (item.second() <= 0) { + throw argument_legality_exception("invalid amount parameter for " + item.first()); + } + + receiver.push_back({address, argument_.symbol, 0, item.second(), attach_type, attach}); + + if (msg_address.empty()) { + msg_address = address; + } + } + + std::string change_address = get_address(option_.change, blockchain); + + // // memo + // if (!option_.memo.empty()) { + // check_message(option_.memo); + + // if (!change_address.empty()) { + // msg_address = change_address; + // } + + // receiver.push_back({msg_address, "", 0, 0, utxo_attach_type::message, + // attachment(0, 0, chain::blockchain_message(option_.memo))}); + // } + + + auto send_helper = sending_asset(*this, blockchain, + std::move(auth_.name), std::move(auth_.auth), + "", std::move(argument_.symbol), + std::move(option_.attenuation_model_param), + std::move(receiver), option_.fee, + std::move(option_.memo), + std::move(change_address)); + + send_helper.exec(); + + // json output + auto tx = send_helper.get_transaction(); + jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); + + return console_result::okay; +} + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/src/lib/explorer/extensions/commands/sendrawtx.cpp b/src/lib/explorer/extensions/commands/sendrawtx.cpp index a7a0fb6f4..e966d5f16 100644 --- a/src/lib/explorer/extensions/commands/sendrawtx.cpp +++ b/src/lib/explorer/extensions/commands/sendrawtx.cpp @@ -29,6 +29,101 @@ namespace libbitcoin { namespace explorer { namespace commands { using namespace bc::explorer::config; +using payment_address = wallet::payment_address; + +bool sort_multi_sigs(tx_type& tx_) { + bc::chain::script input_script; + bc::chain::script redeem_script; + + for (uint32_t index = 0; index < tx_.inputs.size(); ++index) { + auto &each_input = tx_.inputs[index]; + input_script = each_input.script; + + if (chain::script_pattern::sign_multisig != input_script.pattern()) + continue; + + // 1. extract address from multisig payment script + // zero sig1 sig2 ... encoded-multisig + const auto &redeem_data = input_script.operations.back().data; + if (redeem_data.empty()) { + throw redeem_script_empty_exception{"empty redeem script."}; + } + + if (!redeem_script.from_data(redeem_data, false, bc::chain::script::parse_mode::strict)) { + throw redeem_script_data_exception{"error occured when parse redeem script data."}; + } + + // Is the redeem script a standard pay (output) script? + if (redeem_script.pattern() != chain::script_pattern::pay_multisig) { + throw redeem_script_pattern_exception{"redeem script is not pay multisig pattern."}; + } + + const payment_address address(redeem_script, payment_address::mainnet_p2sh); + auto hash_address = address.encoded(); // pay address + + // rearange signature order + data_chunk data; + chain::script &script_encoded = redeem_script; + //script_encoded.from_string(multisig_script); + + bc::chain::script new_script; + // insert zero + new_script.operations.push_back(input_script.operations.front()); + + /* + "multisig-script" : "2 [ pubkey_1 ] [ pubkey_2 ] [ pubkey_3 ] 3 checkmultisig", + "script" : "zero [ signature_1 ] [ signature_2 ] [ encoded-script ]", + */ + // skip first "m" and last "n checkmultisig" + static constexpr auto op_1 = static_cast(chain::opcode::op_1); + const auto op_m = static_cast(script_encoded.operations[0].code); + if (op_m < op_1) { + return false; + } + const auto num_m = op_m - op_1 + 1u; + + auto multisig_start = script_encoded.operations.begin() + 1; + auto multisig_end = script_encoded.operations.end() - 2; + + // skip first zero and last encoded-script + auto script_op_start = input_script.operations.begin() + 1; + auto script_op_end = input_script.operations.end() - 1; + + for (auto multisig_it = multisig_start; multisig_it != multisig_end; ++multisig_it) { + for (auto script_op_it = script_op_start; script_op_it != script_op_end; ++script_op_it) { + auto endorsement = script_op_it->data; + const auto sighash_type = endorsement.back(); + auto distinguished = endorsement; + distinguished.pop_back(); + + ec_signature signature; + // from validate_transaction.cpp handle_previous_tx + auto strict = ((chain::get_script_context() & chain::script_context::bip66_enabled) != 0); + if (!parse_signature(signature, distinguished, strict)) { + log::trace("multisig") << "failed to parse_signature! " << sighash_type; + continue; + } + + if (chain::script::check_signature(signature, sighash_type, multisig_it->data, + script_encoded, tx_, index)) { + new_script.operations.push_back(*script_op_it); + break; + } + } + } + + // insert encoded-script + new_script.operations.push_back(input_script.operations.back()); + if (new_script.operations.size() < num_m + 2) { + return false; + } + + // set input script of this tx + each_input.script = new_script; + } + + return true; +} console_result sendrawtx::invoke(Json::Value& jv_output, libbitcoin::server::server_node& node) @@ -42,15 +137,27 @@ console_result sendrawtx::invoke(Json::Value& jv_output, throw tx_validate_exception{"get transaction inputs etp value error!"}; // check raw tx fee range - if (inputs_etp_val <= outputs_etp_val) + if (inputs_etp_val <= outputs_etp_val) { throw tx_validate_exception{"no enough transaction fee"}; + } base_transfer_common::check_fee_in_valid_range(inputs_etp_val - outputs_etp_val); - if (blockchain.validate_transaction(tx_)) - throw tx_validate_exception{"validate transaction failure"}; + code ec = blockchain.validate_transaction(tx_); + if (ec.value() != error::success) { + if (!sort_multi_sigs(tx_)) { + throw tx_validate_exception{"validate multi-sig transaction failure:" + ec.message()}; + } - if (blockchain.broadcast_transaction(tx_)) - throw tx_broadcast_exception{"broadcast transaction failure"}; + ec = blockchain.validate_transaction(tx_); + if (ec.value() != error::success) { + throw tx_validate_exception{"validate transaction failure: " + ec.message()}; + } + } + + ec = blockchain.broadcast_transaction(tx_); + if (ec.value() != error::success) { + throw tx_broadcast_exception{"broadcast transaction failure: " + ec.message()}; + } if (get_api_version() <= 2) { jv_output["hash"] = encode_hash(tx_.hash()); diff --git a/src/lib/explorer/extensions/commands/signmultisigtx.cpp b/src/lib/explorer/extensions/commands/signmultisigtx.cpp index e072aa4eb..8436d8c2f 100644 --- a/src/lib/explorer/extensions/commands/signmultisigtx.cpp +++ b/src/lib/explorer/extensions/commands/signmultisigtx.cpp @@ -28,6 +28,7 @@ namespace libbitcoin { namespace explorer { namespace commands { +using payment_address = wallet::payment_address; console_result signmultisigtx::invoke( Json::Value& jv_output, @@ -44,20 +45,22 @@ console_result signmultisigtx::invoke( throw address_list_empty_exception{"empty address list for this account."}; } - std::string addr_prikey(""); - if (!option_.self_publickey.empty()) { - auto owned = false; + auto get_prikey = [&](const std::string& publickey) -> std::string { for (auto& each : *pvaddr) { auto prv_key = each.get_prv_key(auth_.auth); auto pub_key = ec_to_xxx_impl("ec-to-public", prv_key); - if (option_.self_publickey == pub_key) { - owned = true; - addr_prikey = prv_key; - break; + if (publickey == pub_key) { + return prv_key; } } + return ""; + }; + + std::string addr_prikey(""); + if (!option_.self_publickey.empty()) { + addr_prikey = get_prikey(option_.self_publickey); - if (!owned) { + if (addr_prikey.empty()) { throw pubkey_dismatch_exception( "public key " + option_.self_publickey + " is not owned by " + auth_.name); } @@ -67,12 +70,11 @@ console_result signmultisigtx::invoke( bc::chain::script redeem_script; bool fullfilled = true; - std::string multisig_script; - uint32_t index = 0; - for (auto& each_input : tx_.inputs) { + for (uint32_t index = 0; index < tx_.inputs.size(); ++index) { + auto& each_input = tx_.inputs[index]; input_script = each_input.script; - if (script_pattern::sign_multisig != input_script.pattern()) + if (chain::script_pattern::sign_multisig != input_script.pattern()) continue; // 1. extract address from multisig payment script @@ -87,7 +89,7 @@ console_result signmultisigtx::invoke( } // Is the redeem script a standard pay (output) script? - if (redeem_script.pattern() != script_pattern::pay_multisig) { + if (redeem_script.pattern() != chain::script_pattern::pay_multisig) { throw redeem_script_pattern_exception{"redeem script is not pay multisig pattern."}; } @@ -101,27 +103,24 @@ console_result signmultisigtx::invoke( } // signed, nothing to do (2 == zero + encoded-script) - account_multisig acc_multisig = *(multisig_vec->begin()); - if (input_script.operations.size() >= acc_multisig.get_m() + 2) { - index++; + account_multisig acc_multisig_first = *(multisig_vec->begin()); + if (input_script.operations.size() >= acc_multisig_first.get_m() + 2) { continue; } - for (auto& acc_multisig : *multisig_vec) { + std::string multisig_script = acc_multisig_first.get_multisig_script(); + if (multisig_script.empty()) { + throw tx_sign_exception{"get_multisig_script get empty script."}; + } + + bool has_new_sign = false; + for (const auto& acc_multisig : *multisig_vec) { if (!option_.self_publickey.empty() && option_.self_publickey != acc_multisig.get_pub_key()) { continue; } if (option_.self_publickey.empty()) { - addr_prikey = ""; - for (auto& each : *pvaddr) { - auto prv_key = each.get_prv_key(auth_.auth); - auto&& pub_key = ec_to_xxx_impl("ec-to-public", prv_key); - if (pub_key == acc_multisig.get_pub_key()) { - addr_prikey = prv_key; - break; - } - } + addr_prikey = get_prikey(acc_multisig.get_pub_key()); } if (addr_prikey.empty()) { @@ -129,13 +128,9 @@ console_result signmultisigtx::invoke( "The private key of " + acc_multisig.get_pub_key() + " not found."); } - // 3. populate unlock script - multisig_script = acc_multisig.get_multisig_script(); - // log::trace("multisig_script=") << multisig_script; - // prepare sign explorer::config::hashtype sign_type; - uint8_t hash_type = (signature_hash_algorithm)sign_type; + uint8_t hash_type = (chain::signature_hash_algorithm)sign_type; bc::explorer::config::ec_private config_private_key(addr_prikey); bc::explorer::config::script config_contract(multisig_script); @@ -150,6 +145,11 @@ console_result signmultisigtx::invoke( // insert endorse before multisig script auto position = input_script.operations.end(); input_script.operations.insert(position - 1, {bc::chain::opcode::special, endorse}); + has_new_sign = true; + } + + if (!has_new_sign && !option_.self_publickey.empty()) { + throw tx_sign_exception{"has no multisig match self public key: " + option_.self_publickey}; } // rearange signature order @@ -182,7 +182,7 @@ console_result signmultisigtx::invoke( ec_signature signature; // from validate_transaction.cpp handle_previous_tx - auto strict = ((script_context::all_enabled & script_context::bip66_enabled) != 0); + auto strict = ((chain::get_script_context() & chain::script_context::bip66_enabled) != 0); if (!parse_signature(signature, distinguished, strict)) { log::trace("multisig") << "failed to parse_signature! " << sighash_type; continue; @@ -195,20 +195,19 @@ console_result signmultisigtx::invoke( } } - if (new_script.operations.size() >= acc_multisig.get_m() + 1) { + if (new_script.operations.size() >= acc_multisig_first.get_m() + 1) { break; } } // insert encoded-script new_script.operations.push_back(input_script.operations.back()); - if (new_script.operations.size() < acc_multisig.get_m() + 2) { + if (new_script.operations.size() < acc_multisig_first.get_m() + 2) { fullfilled = false; } // set input script of this tx each_input.script = new_script; - index++; } // output json diff --git a/src/lib/explorer/extensions/commands/signrawtx.cpp b/src/lib/explorer/extensions/commands/signrawtx.cpp index 498a94717..08e92e9d2 100644 --- a/src/lib/explorer/extensions/commands/signrawtx.cpp +++ b/src/lib/explorer/extensions/commands/signrawtx.cpp @@ -29,14 +29,36 @@ namespace libbitcoin { namespace explorer { namespace commands { +using payment_address = wallet::payment_address; + +std::string parse_private_key(const std::string& raw_str) +{ + bc::wallet::ec_private prv_key(raw_str); + if (true == (bool)prv_key) { + const ec_secret& secret = prv_key; + return encode_base16(secret); + } + + return raw_str; +} + console_result signrawtx::invoke(Json::Value& jv_output, - libbitcoin::server::server_node& node) + bc::server::server_node& node) { - auto& blockchain = node.chain_impl(); - blockchain.is_account_passwd_valid(auth_.name, auth_.auth); + if (option_.private_key.empty() && (auth_.name.empty() || auth_.auth.empty())) { + throw argument_legality_exception{"Missing account/password or private key!"}; + } + + auto &blockchain = node.chain_impl(); + std::shared_ptr acc(nullptr); + if (option_.private_key.empty()) { + acc = blockchain.is_account_passwd_valid(auth_.name, auth_.auth); + } tx_type tx_ = argument_.transaction; + std::map > script_sig_map; + // sign tx { uint32_t index = 0; @@ -44,71 +66,165 @@ console_result signrawtx::invoke(Json::Value& jv_output, uint64_t tx_height; for (auto& fromeach : tx_.inputs) { - if (!(blockchain.get_transaction(fromeach.previous_output.hash, tx_temp, tx_height))) - throw argument_legality_exception{"invalid transaction hash " + encode_hash(fromeach.previous_output.hash)}; + if (!(blockchain.get_transaction(fromeach.previous_output.hash, tx_temp, tx_height))) { + throw argument_legality_exception{ + "invalid transaction hash " + encode_hash(fromeach.previous_output.hash)}; + } auto output = tx_temp.outputs.at(fromeach.previous_output.index); // get address private key auto address = payment_address::extract(output.script); - if (!address || (address.version() == 0x5)) // script address : maybe multisig - throw argument_legality_exception{"invalid script " + config::script(output.script).to_string()}; - auto acc_addr = blockchain.get_account_address(auth_.name, address.encoded()); + // script address : maybe multisig + if (!address || (address.version() == payment_address::mainnet_p2sh)) { + if (acc == nullptr) { + throw argument_legality_exception{"Please use account/password to sign pay-to-script raw tx."}; + } + + const auto script_vec = acc->get_script(address.encoded()); + if (!script_vec || script_vec->empty()) { + throw argument_legality_exception{"invalid script: " + config::script(output.script).to_string()}; + } + + const auto& script = script_vec->begin(); + + // watch-only address + const data_chunk& bin_script = script->get_script(); + if (bin_script.empty()) { + throw argument_legality_exception{"watch-only script address: " + config::script(output.script).to_string()}; + } + else { + bc::explorer::config::script config_contract(bin_script); + + bc::chain::script redeem_script; + if (!redeem_script.from_data(bin_script, false, bc::chain::script::parse_mode::strict)) { + throw redeem_script_data_exception{"error occured when parse redeem script data."}; + } + + // walk the script and find potential public kes + std::set script_pk_set; + auto f = [&script_pk_set](const chain::operation& op) { + if ((op.code == chain::opcode::special) && (op.data.size() == 33)) { + script_pk_set.insert(op.data); + } + }; + std::for_each(redeem_script.operations.begin(), redeem_script.operations.end(), f); + + std::map pk_sig; + for (const auto& pk : script_pk_set) { + const payment_address script_pkh = wallet::ec_public(pk).to_payment_address(); + auto acc_addr = blockchain.get_account_address(auth_.name, script_pkh.encoded()); + if (!acc_addr) { + continue; + } + + std::string prv_key_str = acc_addr->get_prv_key(auth_.auth);; + + data_chunk public_key_data; + bc::endorsement&& edsig = sign(prv_key_str, tx_, index, config_contract, public_key_data); + pk_sig[encode_base16(public_key_data)] = encode_base16(edsig); + } + + script_sig_map[index] = pk_sig; + } + } + else { + std::string prv_key_str; + if (acc) { + prv_key_str = get_private_key(blockchain, address.encoded()); + } + else { + prv_key_str = parse_private_key(option_.private_key); + } + + bc::explorer::config::script config_contract(output.script); // previous output script + + data_chunk public_key_data; + bc::endorsement&& edsig = sign(prv_key_str, tx_, index, config_contract, public_key_data); + + // do script + bc::chain::script ss; + ss.operations.push_back({bc::chain::opcode::special, edsig}); + ss.operations.push_back({bc::chain::opcode::special, public_key_data}); + const bc::chain::script& contract = config_contract; + // if pre-output script is deposit tx. + if (contract.pattern() == bc::chain::script_pattern::pay_key_hash_with_lock_height) { + uint64_t lock_height = chain::operation::get_lock_height_from_pay_key_hash_with_lock_height( + contract.operations); + ss.operations.push_back({bc::chain::opcode::special, script_number(lock_height).data()}); + } + + // set input script of this tx + tx_.inputs[index].script = ss; + } + index++; + }// end for + } + + if (script_sig_map.empty()) { + // get raw tx + //if (blockchain.validate_transaction(tx_)) { + // throw tx_validate_exception{"validate transaction failure"}; + //} + + jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx_, true); + } + else { + for (auto iter1 = script_sig_map.begin(); iter1 != script_sig_map.end(); ++iter1) { + Json::Value pk_sig; + for (auto iter2 = iter1->second.begin(); iter2 != iter1->second.end(); ++iter2) { + pk_sig[iter2->first] = iter2->second; + } + jv_output[std::to_string(iter1->first)] = pk_sig; + } + } - if (!acc_addr) - throw argument_legality_exception{"not own address " + address.encoded()}; + return console_result::okay; +} - // paramaters - explorer::config::hashtype sign_type; - uint8_t hash_type = (signature_hash_algorithm)sign_type; +std::string signrawtx::get_private_key(blockchain::block_chain_impl& blockchain, const std::string& address) +{ + auto acc_addr = blockchain.get_account_address(auth_.name, address); + if (!acc_addr) { + throw argument_legality_exception{"Address " + address + " is not owned."}; + } - bc::explorer::config::ec_private config_private_key(acc_addr->get_prv_key(auth_.auth)); // address private key - const ec_secret& private_key = config_private_key; - bc::wallet::ec_private ec_private_key(private_key, 0u, true); + return acc_addr->get_prv_key(auth_.auth); +} - bc::explorer::config::script config_contract(output.script); // previous output script - const bc::chain::script& contract = config_contract; +bc::endorsement signrawtx::sign( + const std::string& prv_key_str, + tx_type tx_, + const uint32_t& index, + const bc::explorer::config::script& config_contract, + data_chunk& public_key_data) +{ + // paramaters + explorer::config::hashtype sign_type; + uint8_t hash_type = (chain::signature_hash_algorithm)sign_type; - // gen sign - bc::endorsement endorse; - if (!bc::chain::script::create_endorsement(endorse, private_key, - contract, tx_, index, hash_type)) - { - throw tx_sign_exception{"signrawtx sign failure"}; - } + bc::explorer::config::ec_private config_private_key(prv_key_str); + const ec_secret& secret = config_private_key; - // do script - auto&& public_key = ec_private_key.to_public(); - data_chunk public_key_data; - public_key.to_data(public_key_data); - bc::chain::script ss; - ss.operations.push_back({bc::chain::opcode::special, endorse}); - ss.operations.push_back({bc::chain::opcode::special, public_key_data}); - - // if pre-output script is deposit tx. - if (contract.pattern() == bc::chain::script_pattern::pay_key_hash_with_lock_height) { - uint64_t lock_height = chain::operation::get_lock_height_from_pay_key_hash_with_lock_height( - contract.operations); - ss.operations.push_back({bc::chain::opcode::special, script_number(lock_height).data()}); - } + bc::wallet::ec_private ec_private_key(secret); + auto&& public_key = ec_private_key.to_public(); + public_key.to_data(public_key_data); - // set input script of this tx - tx_.inputs[index].script = ss; - index++; - } // end for - } + const bc::chain::script& contract = config_contract; - // get raw tx - if (blockchain.validate_transaction(tx_)) { - throw tx_validate_exception{"validate transaction failure"}; + // gen sign + bc::endorsement endorse; + if (!bc::chain::script::create_endorsement(endorse, secret, + contract, tx_, index, hash_type)) + { + throw tx_sign_exception{"signrawtx sign failure"}; } - jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx_, true); - - return console_result::okay; + return endorse; } + } // namespace commands } // namespace explorer } // namespace libbitcoin diff --git a/src/lib/explorer/extensions/commands/startmining.cpp b/src/lib/explorer/extensions/commands/startmining.cpp index 87ba5c2e6..af7233310 100644 --- a/src/lib/explorer/extensions/commands/startmining.cpp +++ b/src/lib/explorer/extensions/commands/startmining.cpp @@ -18,11 +18,12 @@ * along with this program. If not, see . */ - -#include #include +#include +#include #include #include +#include namespace libbitcoin { namespace explorer { @@ -41,14 +42,26 @@ console_result startmining::invoke(Json::Value& jv_output, uint64_t rate; std::string difficulty; bool is_solo_mining; - node.miner().get_state(height, rate, difficulty, is_solo_mining); + miner.get_state(height, rate, difficulty, is_solo_mining); if (is_solo_mining) { throw setting_required_exception{"Currently mining, please use command to stop the running mining."}; } - auto str_addr = option_.address; + boost::to_lower(option_.consensus); + const auto is_use_pow = (option_.consensus == "pow"); + const auto is_use_pos = (option_.consensus == "pos"); + const auto is_use_dpos = (option_.consensus == "dpos"); + + std::string str_addr; + if (!option_.address.empty()) { + str_addr = get_address(option_.address, blockchain); + } if (str_addr.empty()) { + if (!is_use_pow) { + throw argument_legality_exception{"mining non-pow blocks must specify a mining address!"}; + } + Json::Value jv_temp; // get new address @@ -75,9 +88,25 @@ console_result startmining::invoke(Json::Value& jv_output, throw address_invalid_exception{"invalid address parameter! " + str_addr}; } - if (!blockchain.get_account_address(auth_.name, str_addr)) { + auto sp_account_address = blockchain.get_account_address(auth_.name, str_addr); + if (!sp_account_address) { throw address_dismatch_account_exception{"target address does not match account. " + str_addr}; } + + if (is_use_dpos || is_use_pos) { + const std::string pubkey = sp_account_address->get_pub_key(); + const std::string prikey = sp_account_address->get_prv_key(auth_.auth); + if (!miner.set_pub_and_pri_key(pubkey, prikey)) { + throw address_invalid_exception{"invalid address parameter(wrong key)! " + str_addr}; + } + + if (is_use_dpos && !miner.is_witness()) { + log::error("Mining") + << str_addr + << " is not a witness at height " + << height; + } + } } bc::wallet::payment_address addr(str_addr); @@ -86,15 +115,30 @@ console_result startmining::invoke(Json::Value& jv_output, throw argument_legality_exception{"script address parameter not allowed!"}; } + if (is_use_pow) { + miner.set_accept_block_version(chain::block_version_pow); + } + else if (is_use_pos) { + miner.set_accept_block_version(chain::block_version_pos); + } + else if (is_use_dpos) { + miner.set_accept_block_version(chain::block_version_dpos); + } + else { + throw argument_legality_exception{"wrong consensus of block version!"}; + } + // start if (miner.start(addr, option_.number)){ + std::string prompt = "solo mining started at " + + str_addr + ", accept consensus " + option_.consensus; if (option_.number == 0) { - jv_output = "solo mining started at " + str_addr; + jv_output = prompt; } else { - jv_output = "solo mining started at " + str_addr - + ", try to mine " + std::to_string(option_.number) + " block(s)."; + jv_output = prompt + ", try to mine " + std::to_string(option_.number) + " block(s)."; } - } else { + } + else { throw unknown_error_exception{"solo mining startup got error"}; } diff --git a/src/lib/explorer/extensions/commands/submitwork.cpp b/src/lib/explorer/extensions/commands/submitwork.cpp index 337510c48..c9f673c8b 100644 --- a/src/lib/explorer/extensions/commands/submitwork.cpp +++ b/src/lib/explorer/extensions/commands/submitwork.cpp @@ -31,7 +31,7 @@ namespace commands { using namespace bc::explorer::config; /************************ submitwork *************************/ -inline bool startswith(const string &str, const char *prefix) { +inline bool startswith(const std::string &str, const char *prefix) { return str.find(prefix) == 0; } @@ -40,9 +40,9 @@ console_result submitwork::invoke(Json::Value& jv_output, { auto& miner = node.miner(); - const uint64_t nounce_mask = (get_api_version() == 3) ? 0 : 0x6675636b6d657461; + const uint64_t nounce_mask = (get_api_version() >= 3) ? 0 : 0x6675636b6d657461; //Note: submitwork does not starts with 0x, while eth_submitWork does! - if (get_api_version() == 3) { + if (get_api_version() >= 3) { if ( !startswith(argument_.nonce, "0x") ) { throw argument_legality_exception{"nonce should start with \"0x\" for eth_submitWork"}; } diff --git a/src/lib/explorer/extensions/commands/swapmit.cpp b/src/lib/explorer/extensions/commands/swapmit.cpp index be232236c..1cfea1cec 100644 --- a/src/lib/explorer/extensions/commands/swapmit.cpp +++ b/src/lib/explorer/extensions/commands/swapmit.cpp @@ -105,7 +105,7 @@ console_result swapmit::invoke(Json::Value& jv_output, receiver.push_back({ to_address, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(option_.memo)) + attachment(0, 0, chain::blockchain_message(option_.memo)) }); } @@ -113,7 +113,7 @@ console_result swapmit::invoke(Json::Value& jv_output, std::string message("{\"type\":\"ETH\",\"address\":\"" + argument_.foreign_addr + "\"}"); receiver.push_back({ to_address, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(message)) + attachment(0, 0, chain::blockchain_message(message)) }); auto helper = transferring_mit( diff --git a/src/lib/explorer/extensions/commands/swaptoken.cpp b/src/lib/explorer/extensions/commands/swaptoken.cpp index 89ee15503..3af4dcbdb 100644 --- a/src/lib/explorer/extensions/commands/swaptoken.cpp +++ b/src/lib/explorer/extensions/commands/swaptoken.cpp @@ -83,7 +83,7 @@ console_result swaptoken::invoke(Json::Value& jv_output, check_message(option_.memo); receiver.push_back({to_address, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(option_.memo))}); + attachment(0, 0, chain::blockchain_message(option_.memo))}); } std::string message("{\"type\":\"ETH\",\"address\":\""+ argument_.foreign_addr + "\"}"); diff --git a/src/lib/explorer/extensions/commands/transfercert.cpp b/src/lib/explorer/extensions/commands/transfercert.cpp index 9c93e3afe..dbece9f7e 100644 --- a/src/lib/explorer/extensions/commands/transfercert.cpp +++ b/src/lib/explorer/extensions/commands/transfercert.cpp @@ -44,16 +44,8 @@ console_result transfercert::invoke (Json::Value& jv_output, // check asset cert types auto& cert_type_name = argument_.cert; - auto match = [&cert_type_name](const std::pair & item) { - return item.second == cert_type_name; - }; - const auto& type_name_map = asset_cert::get_type_name_map(); - auto iter = std::find_if(type_name_map.begin(), type_name_map.end(), match); - if (iter == type_name_map.end()) { - throw asset_cert_exception("unsupported asset cert type " + cert_type_name); - } + auto cert_type = check_cert_type_name(cert_type_name, true); - auto cert_type = iter->first; if (cert_type == asset_cert_ns::issue) { auto sh_asset = blockchain.get_issued_asset(argument_.symbol); if (!sh_asset) @@ -109,11 +101,12 @@ console_result transfercert::invoke (Json::Value& jv_output, // json output auto && tx = helper.get_transaction(); + auto json_helper = config::json_helper(get_api_version()); if (is_multisig_address) { - jv_output = config::json_helper(get_api_version()).prop_list_of_rawtx(tx, false, true); + jv_output = json_helper.prop_list_of_rawtx(tx, false, true); } else { - jv_output = config::json_helper(get_api_version()).prop_tree(tx, true); + jv_output = json_helper.prop_tree(tx, true); } return console_result::okay; diff --git a/src/lib/explorer/extensions/commands/transfermit.cpp b/src/lib/explorer/extensions/commands/transfermit.cpp index d8ebda972..3274e47af 100644 --- a/src/lib/explorer/extensions/commands/transfermit.cpp +++ b/src/lib/explorer/extensions/commands/transfermit.cpp @@ -78,7 +78,7 @@ console_result transfermit::invoke (Json::Value& jv_output, check_message(option_.memo); receiver.push_back({to_address, "", 0, 0, utxo_attach_type::message, - attachment(0, 0, blockchain_message(option_.memo))}); + attachment(0, 0, chain::blockchain_message(option_.memo))}); } auto helper = transferring_mit( diff --git a/src/lib/explorer/extensions/commands/validateaddress.cpp b/src/lib/explorer/extensions/commands/validateaddress.cpp index 194a30d08..928324626 100644 --- a/src/lib/explorer/extensions/commands/validateaddress.cpp +++ b/src/lib/explorer/extensions/commands/validateaddress.cpp @@ -27,6 +27,7 @@ namespace libbitcoin { namespace explorer { namespace commands { +using payment_address = wallet::payment_address; /************************ validateaddress *************************/ diff --git a/src/lib/explorer/extensions/commands/validatesymbol.cpp b/src/lib/explorer/extensions/commands/validatesymbol.cpp new file mode 100644 index 000000000..10d6b0ab0 --- /dev/null +++ b/src/lib/explorer/extensions/commands/validatesymbol.cpp @@ -0,0 +1,164 @@ +/** + * Copyright (c) 2016-2018 mvs developers + * + * This file is part of metaverse-explorer. + * + * metaverse-explorer is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License with + * additional permissions to the one published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. For more information see LICENSE. + * + * 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace explorer { +namespace commands { + +using namespace bc::chain; + +/************************ validatesymbol *************************/ + +console_result validatesymbol::invoke(Json::Value& jv_output, libbitcoin::server::server_node& node) +{ + auto& blockchain = node.chain_impl(); + blockchain.is_account_passwd_valid(auth_.name, auth_.auth); + blockchain.uppercase_symbol(argument_.symbol); + + if (argument_.symbol.empty()) { + throw asset_symbol_length_exception{"Symbol cannot be empty."}; + } + + auto& symbol = argument_.symbol; + + const std::string Available("available"); + const std::string Existed("existed"); + const std::string Forbidden("forbidden"); + const std::string No_Permission("no_permission"); + + // check asset symbol:available/existed/forbidden/no_permission + std::string key("asset_symbol"); + try + { + check_asset_symbol(symbol, true); + + // check asset exists + if (blockchain.is_asset_exist(symbol, true)) { + jv_output[key] = Existed; + } + else { + auto&& domain = asset_cert::get_domain(symbol); + if (blockchain.is_asset_cert_exist(domain, asset_cert_ns::domain)) { + auto result_vec = blockchain.get_account_asset_certs( + auth_.name, domain, asset_cert_ns::domain); + if (nullptr == result_vec || result_vec->empty()) { + jv_output[key] = No_Permission; + } + else { + jv_output[key] = Available; + } + } + else { + jv_output[key] = Available; + } + } + } + catch (...) + { + jv_output[key] = Forbidden; + } + + // check mit symbol:available/existed/forbidden + key = "mit_symbol"; + try + { + check_mit_symbol(symbol, true); + + // check mit exists + if (blockchain.is_asset_mit_exist(symbol)) { + jv_output[key] = Existed; + } + else { + jv_output[key] = Available; + } + } + catch (...) + { + jv_output[key] = Forbidden; + } + + // check did symbol:available/existed/forbidden + key = "did_symbol"; + try + { + check_did_symbol(symbol, true); + + // check did exists + if (blockchain.is_did_exist(symbol)) { + jv_output[key] = Existed; + } + else { + jv_output[key] = Available; + } + } + catch (...) + { + jv_output[key] = Forbidden; + } + + // check cert symbol:available/existed/forbidden/no_permission + if (!option_.cert_type.empty()) { + key = "cert_symbol"; + boost::to_lower(option_.cert_type); + + try + { + check_asset_symbol(symbol); + + auto certs_create = check_issue_cert( + blockchain, auth_.name, symbol, option_.cert_type); + if (certs_create != asset_cert_ns::none) { + jv_output[key] = Available; + } + else { + jv_output[key] = Forbidden; + } + } + catch (const asset_cert_existed_exception&) { + jv_output[key] = Existed; + } + catch (const asset_cert_notfound_exception&) { + jv_output[key] = No_Permission; + } + catch (const asset_cert_notowned_exception&) { + jv_output[key] = No_Permission; + } + catch (...) + { + jv_output[key] = Forbidden; + } + } + + return console_result::okay; +} + + + +} // namespace commands +} // namespace explorer +} // namespace libbitcoin + diff --git a/src/lib/explorer/json_helper.cpp b/src/lib/explorer/json_helper.cpp index 8e36806d0..9121e04b2 100644 --- a/src/lib/explorer/json_helper.cpp +++ b/src/lib/explorer/json_helper.cpp @@ -244,7 +244,7 @@ Json::Value json_helper::prop_list(const tx_input_type& tx_input) tree["previous_output"]["index"] = tx_input.previous_output.index; tree["sequence"] = tx_input.sequence; } - tree["script"] += script(tx_input.script).to_string(); + tree["script"] += script(tx_input.script.to_data(false)).to_string(); return tree; } Json::Value json_helper::prop_tree(const tx_input_type& tx_input) @@ -296,14 +296,14 @@ Json::Value json_helper::prop_list(const tx_output_type& tx_output) // TODO: this will eventually change due to privacy problems, see: // lists.dyne.org/lurker/message/20140812.214120.317490ae.en.html - if (!address) + if (!address && is_stealth_script(tx_output.script)) { - tree["stealth"] = Json::objectValue; uint32_t stealth_prefix; ec_compressed ephemeral_key; if (to_stealth_prefix(stealth_prefix, tx_output.script) && extract_ephemeral_key(ephemeral_key, tx_output.script)) { + tree["stealth"] = Json::objectValue; tree["stealth"]["prefix"] += stealth_prefix; tree["stealth"]["ephemeral_public_key"] += ec_public(ephemeral_key); } @@ -669,6 +669,9 @@ Json::Value json_helper::prop_list(bc::chain::attachment& attach_data) auto msg_info = boost::get(attach_data.get_attach()); tree["content"] = msg_info.get_content(); } + else if(attach_data.get_type() == ATTACH_NULL_TYPE){ + tree["type"] = "null"; + } else { tree["type"] = "unknown business"; BITCOIN_ASSERT(false); @@ -766,10 +769,16 @@ Json::Value json_helper::prop_list(const transaction& transaction, bool json) Json::Value tree; if (json) { tree["hash"] += hash256(tx.hash()); + if (version_ <= 3) { + tree["lock_time"] += tx.locktime; + tree["version"] += tx.version; + } + else { + tree["lock_time"] = tx.locktime; + tree["version"] = tx.version; + } tree["inputs"] = prop_tree_list("input", tx.inputs, json); - tree["lock_time"] += tx.locktime; tree["outputs"] = prop_tree(tx.outputs, json); // only used for output to add new field "index" - tree["version"] += tx.version; return tree; } else { @@ -796,10 +805,15 @@ Json::Value json_helper::prop_list(const transaction& transaction, uint64_t tx_h } else { tree["height"] = tx_height; } + if (version_ <= 3) { + tree["lock_time"] += tx.locktime; + tree["version"] += tx.version; + } else { + tree["lock_time"] = tx.locktime; + tree["version"] = tx.version; + } tree["inputs"] = prop_tree_list("input", tx.inputs, json); - tree["lock_time"] += tx.locktime; tree["outputs"] = prop_tree(tx.outputs, json); // only used for output to add new field "index" - tree["version"] += tx.version; return tree; } diff --git a/src/lib/explorer/utility.cpp b/src/lib/explorer/utility.cpp index 98e9828c9..fb4638e8b 100644 --- a/src/lib/explorer/utility.cpp +++ b/src/lib/explorer/utility.cpp @@ -170,5 +170,28 @@ std::ostream& write_stream(std::ostream& output, const Json::Value& tree, return output; } +uint32_t to_uint32_throw(const std::string& text, const std::string& except_desc) +{ + try { + return std::stoul(text); + } + catch(const std::exception& e) { + throw std::logic_error(except_desc + " Convert " + text + " to uint32_t caught exception: " + e.what()); + } + return 0; +} + +uint64_t to_uint64_throw(const std::string& text, const std::string& except_desc) +{ + try { + return std::stoull(text); + } + catch(const std::exception& e) { + throw std::logic_error(except_desc + " Convert " + text + " to uint64_t caught exception: " + e.what()); + } + return 0; +} + + } // namespace explorer } // namespace libbitcoin diff --git a/src/lib/network/channel.cpp b/src/lib/network/channel.cpp index 7f8c476b2..db924187d 100644 --- a/src/lib/network/channel.cpp +++ b/src/lib/network/channel.cpp @@ -140,6 +140,13 @@ void channel::handle_activity() start_inactivity(); } +bool channel::stopped(const code& ec) const +{ + return proxy::stopped() || + ec.value() == error::channel_stopped || + ec.value() == error::service_stopped; +} + // Timers (these are inherent races, requiring stranding by stop only). // ---------------------------------------------------------------------------- @@ -157,7 +164,7 @@ void channel::start_expiration() void channel::handle_expiration(const code& ec) { - if (stopped()) + if (stopped(ec)) { return; } @@ -182,7 +189,7 @@ void channel::start_inactivity() void channel::handle_inactivity(const code& ec) { - if (stopped()) + if (stopped(ec)) { return; } diff --git a/src/lib/network/hosts.cpp b/src/lib/network/hosts.cpp index 697663f09..7a1e57f26 100644 --- a/src/lib/network/hosts.cpp +++ b/src/lib/network/hosts.cpp @@ -19,6 +19,7 @@ * along with this program. If not, see . */ #include +#include #include #include @@ -26,34 +27,44 @@ #include #include #include +#include #include +#include namespace libbitcoin { namespace network { -#define NAME "hosts" +uint32_t timer_interval = 60 * 5; // 5 minutes hosts::hosts(threadpool& pool, const settings& settings) - : stopped_(true), - dispatch_(pool, NAME), - file_path_(default_data_path() / settings.hosts_file), - disabled_(settings.host_pool_capacity == 0), - pool_(pool), - seed_count(settings.seeds.size()) + : seed_count(settings.seeds.size()) + , host_pool_capacity_(std::max(settings.host_pool_capacity, 1u)) + , buffer_(host_pool_capacity_) + , backup_(host_pool_capacity_) + , inactive_(host_pool_capacity_ * 2) + , seeds_() + , stopped_(true) + , file_path_(default_data_path() / settings.hosts_file) + , disabled_(settings.host_pool_capacity == 0) + , pool_(pool) + , self_(settings.self) { -// buffer_.reserve(std::max(settings.host_pool_capacity, 1u)); } // private hosts::iterator hosts::find(const address& host) { - const auto found = [&host](const address& entry) + return find(buffer_, host); +} + +hosts::iterator hosts::find(list& buffer, const address& host) +{ + const auto found = [&host](const address & entry) { return entry.port == host.port && entry.ip == host.ip; }; - return buffer_.find(host); -// return std::find_if(buffer_.begin(), buffer_.end(), found); + return std::find_if(buffer.begin(), buffer.end(), found); } size_t hosts::count() const @@ -66,172 +77,199 @@ size_t hosts::count() const /////////////////////////////////////////////////////////////////////////// } -static std::atomic fetch_times{0}; - -static std::vector hosts_{config::authority("198.199.84.199:5252")}; +code hosts::fetch_seed(address& out, const config::authority::list& excluded_list) +{ + return fetch(seeds_, out, excluded_list); +} code hosts::fetch(address& out, const config::authority::list& excluded_list) { - /////////////////////////////////////////////////////////////////////////// + return fetch(buffer_, out, excluded_list); +} + +template +code hosts::fetch(T& buffer, address& out, const config::authority::list& excluded_list) +{ + if (disabled_) { + return error::not_found; + } + // Critical Section shared_lock lock(mutex_); - fetch_times++; - config::authority::list addresses; - list* buffer=nullptr; - { - if (stopped_) - { - return error::service_stopped; - } - - if (fetch_times % 5 == 4 && !inactive_.empty()) - buffer = &inactive_; - else - buffer = &buffer_; + if (stopped_) { + return error::service_stopped; } - for(auto entry: *buffer) - { - auto iter = std::find(excluded_list.begin(), excluded_list.end(), config::authority(entry) ); - if(iter == excluded_list.end()) - { - addresses.push_back(config::authority(entry)); - } + if (buffer.empty()) { + return error::not_found; } - if (addresses.empty()) { - if (inactive_.empty()) { - return error::not_found; - } - const auto index = static_cast(pseudo_random() % inactive_.size()); - - size_t i = 0; - for (const auto& entry : inactive_) - { - if (i == index) { - out = entry; - break; - } - i++; - } + auto match = [&excluded_list](address& addr) { + auto auth = config::authority(addr); + return std::find(excluded_list.begin(), excluded_list.end(), auth) == excluded_list.end(); + }; - return error::success; + std::vector
vec; + std::copy_if(buffer.begin(), buffer.end(), std::back_inserter(vec), match); + + if (vec.empty()) { + return error::not_found; } - const auto index = static_cast(pseudo_random() % addresses.size()); - out = addresses[index].to_network_address(); + const auto index = pseudo_random(0, vec.size() - 1); + out = vec[static_cast(index)]; -// const auto index = static_cast(pseudo_random() % hosts_.size()); -// out = hosts_[index].to_network_address(); return error::success; - /////////////////////////////////////////////////////////////////////////// +} + +hosts::address::list hosts::copy_seeds() +{ + if (disabled_) + return address::list(); + + shared_lock lock{mutex_}; + return seeds_; } hosts::address::list hosts::copy() { - address::list copy; + if (disabled_) + return address::list(); shared_lock lock{mutex_}; - copy.reserve(buffer_.size()); - for (auto& h:buffer_) { - copy.push_back(h); - } + + if (stopped_ || buffer_.empty()) + return address::list(); + + // not copy all, but just 10% ~ 20% , at least one + const auto out_count = std::max(1, + std::min(1000, buffer_.size()) / pseudo_random(5, 10)); + + const auto limit = buffer_.size(); + auto index = pseudo_random(0, limit - 1); + + address::list copy(out_count); + + for (size_t count = 0; count < out_count; ++count) + copy.push_back(buffer_[index++ % limit]); + + pseudo_random::shuffle(copy); return copy; } +bool hosts::store_cache(bool succeed_clear_buffer) +{ + if (!buffer_.empty()) { + bc::ofstream file(file_path_.string()); + const auto file_error = file.bad(); + + if (file_error) { + log::error(LOG_NETWORK) << "hosts file (" << file_path_.string() << ") open failed" ; + return false; + } + + log::debug(LOG_NETWORK) + << "sync hosts to file(" << file_path_.string() + << "), inactive size is " << inactive_.size() + << ", buffer size is " << buffer_.size(); + + for (const auto& entry : buffer_) { + // TODO: create full space-delimited network_address serialization. + // Use to/from string format as opposed to wire serialization. + if (!(channel::blacklisted(entry) || channel::manualbanned(entry))) { + file << config::authority(entry) << std::endl; + } + } + + if (succeed_clear_buffer) { + buffer_.clear(); + } + } + else { + if (boost::filesystem::exists(file_path_.string())) { + boost::filesystem::remove_all(file_path_.string()); + } + } + + return true; +} + void hosts::handle_timer(const code& ec) { - if (ec.value() != error::success){ + if (disabled_) { return; } - mutex_.lock_upgrade(); - - if (stopped_) - { - mutex_.unlock_upgrade(); + if (ec.value() != error::success) { return; } - mutex_.unlock_upgrade_and_lock(); - bc::ofstream file(file_path_.string()); - const auto file_error = file.bad(); + // Critical Section + upgrade_lock lock(mutex_); - if (!file_error) - { - log::debug(LOG_NETWORK) << "sync hosts to file(" << file_path_.string() << "), active hosts size is " - << buffer_.size() << " hosts found, inactive hosts size is " << inactive_.size(); - for (const auto& entry: buffer_) - file << config::authority(entry) << std::endl; - for (const auto& entry: inactive_) - file << config::authority(entry) << std::endl; - } - else - { - log::error(LOG_NETWORK) << "hosts file (" << file_path_.string() << ") open failed" ; - mutex_.unlock(); + if (stopped_) { return; } - mutex_.unlock(); + { + upgrade_to_unique_lock unq_lock(lock); + + if (!store_cache()) { + return; + } + } + snap_timer_->start(std::bind(&hosts::handle_timer, shared_from_this(), std::placeholders::_1)); } // load code hosts::start() { - if (disabled_) + if (disabled_) { return error::success; - /////////////////////////////////////////////////////////////////////////// + } + // Critical Section - mutex_.lock_upgrade(); + upgrade_lock lock(mutex_); - if (!stopped_) - { - mutex_.unlock_upgrade(); - //--------------------------------------------------------------------- + if (!stopped_) { return error::operation_failed; } - mutex_.unlock_upgrade_and_lock(); + upgrade_to_unique_lock unq_lock(lock); //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - snap_timer_ = std::make_shared(pool_, asio::seconds(60)); + snap_timer_ = std::make_shared(pool_, asio::seconds(timer_interval)); snap_timer_->start(std::bind(&hosts::handle_timer, shared_from_this(), std::placeholders::_1)); + stopped_ = false; + bc::ifstream file(file_path_.string()); const auto file_error = file.bad(); - - if (!file_error) - { + if (!file_error) { std::string line; - - while (std::getline(file, line)) - { + while (std::getline(file, line)) { config::authority host(line); - if (host.port() != 0) - { + if (host.port() != 0) { auto network_address = host.to_network_address(); - if(network_address.is_routable()) - { - buffer_.insert(network_address); + if (network_address.is_routable()) { + buffer_.push_back(network_address); + if (buffer_.full()) { + break; + } } - else - { - log::debug(LOG_NETWORK) << "host start is not routable," << config::authority{network_address}; + else { + log::debug(LOG_NETWORK) << "host start is not routable," + << config::authority{network_address}; } } } } - mutex_.unlock(); - /////////////////////////////////////////////////////////////////////////// - - if (file_error) - { + if (file_error) { log::debug(LOG_NETWORK) - << "Failed to save hosts file."; + << "Failed to save hosts file."; return error::file_system; } @@ -241,42 +279,26 @@ code hosts::start() // load code hosts::stop() { - if (disabled_) + if (disabled_) { return error::success; + } - /////////////////////////////////////////////////////////////////////////// // Critical Section - mutex_.lock_upgrade(); + upgrade_lock lock(mutex_); - if (stopped_) - { - mutex_.unlock_upgrade(); - //--------------------------------------------------------------------- + if (stopped_) { return error::success; } - mutex_.unlock_upgrade_and_lock(); - //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - snap_timer_->stop(); - stopped_ = true; - bc::ofstream file(file_path_.string()); - const auto file_error = file.bad(); - - if (!file_error) - { - for (const auto& entry: buffer_) - file << config::authority(entry) << std::endl; + upgrade_to_unique_lock unq_lock(lock); - buffer_.clear(); - } + // stop timer + snap_timer_->stop(); - mutex_.unlock(); - /////////////////////////////////////////////////////////////////////////// + //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + stopped_ = true; - if (file_error) - { - log::debug(LOG_NETWORK) - << "Failed to load hosts file."; + if (!store_cache(true)) { return error::file_system; } @@ -285,167 +307,285 @@ code hosts::stop() code hosts::clear() { + if (disabled_) { + return error::success; + } + // Critical Section - mutex_.lock_upgrade(); + upgrade_lock lock(mutex_); - if (stopped_) - { - mutex_.unlock_upgrade(); - //--------------------------------------------------------------------- + if (stopped_) { return error::service_stopped; } + upgrade_to_unique_lock unq_lock(lock); - mutex_.unlock_upgrade_and_lock(); - - // if the buffer is already moved to backup, call this function again will lead to the loss of backup. - // backup_ = std::move( buffer_ ); - for (auto &host : buffer_) { - backup_.insert(host); + if (!buffer_.empty()) { + backup_.clear(); + std::copy(buffer_.begin(), buffer_.end(), std::back_inserter(backup_)); + buffer_.clear(); } - buffer_.clear(); - - mutex_.unlock(); - /////////////////////////////////////////////////////////////////////////// return error::success; } code hosts::after_reseeding() { - mutex_.lock_upgrade(); + if (disabled_) { + return error::success; + } - if (stopped_) - { - mutex_.unlock_upgrade(); - //--------------------------------------------------------------------- + // Critical Section + upgrade_lock lock(mutex_); + + if (stopped_) { return error::service_stopped; } + upgrade_to_unique_lock unq_lock(lock); - mutex_.unlock_upgrade_and_lock(); //re-seeding failed and recover the buffer with backup one if (buffer_.size() <= seed_count) { - log::warning(LOG_NETWORK) << "Reseeding finished, but got address list: " << buffer_.size() << ", less than seed count: " - << seed_count << ", roll back the hosts cache."; - buffer_ = std::move(backup_); - } else { + log::debug(LOG_NETWORK) + << "Reseeding finished, buffer size: " << buffer_.size() + << ", less than seed count: " << seed_count + << ", roll back the hosts cache."; + + if (!buffer_.full()) { + for (auto& host : backup_) { + if (find(host) == buffer_.end()) { + buffer_.push_back(host); + + if (buffer_.full()) { + break; + } + } + } + } + } + else { // filter inactive hosts for (auto &host : inactive_) { - auto iter = buffer_.find(host); + auto iter = find(host); if (iter != buffer_.end()) { buffer_.erase(iter); } } - - // clear the backup - backup_.clear(); } - log::debug(LOG_NETWORK) << "Reseeding finished, and got addresses of count: " << buffer_.size(); + // clear the backup + backup_.clear(); - mutex_.unlock(); - /////////////////////////////////////////////////////////////////////////// + log::debug(LOG_NETWORK) + << "Reseeding finished, buffer size: " << buffer_.size(); return error::success; } code hosts::remove(const address& host) { - /////////////////////////////////////////////////////////////////////////// + if (disabled_) { + return error::success; + } + // Critical Section - mutex_.lock_upgrade(); + upgrade_lock lock(mutex_); - if (stopped_) - { - mutex_.unlock_upgrade(); - //--------------------------------------------------------------------- + if (stopped_) { return error::service_stopped; } - auto it = find(host); + upgrade_to_unique_lock unq_lock(lock); - if (it != buffer_.end()) - { - mutex_.unlock_upgrade_and_lock(); - //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + auto it = find(host); + if (it != buffer_.end()) { buffer_.erase(it); - - mutex_.unlock(); - //--------------------------------------------------------------------- - return error::success; } - mutex_.unlock_upgrade_and_lock(); - auto iter = inactive_.find(host); - if (iter == inactive_.end()) { - inactive_.insert(host); + if (find(inactive_, host) == inactive_.end()) { + inactive_.push_back(host); } - mutex_.unlock(); - /////////////////////////////////////////////////////////////////////////// return error::success; } -code hosts::store(const address& host) +code hosts::store_seed(const address& host) { - if (!host.is_routable()) - { - // We don't treat invalid address as an error, just log it. + // don't store blacklist and banned address + if (channel::blacklisted(host) || channel::manualbanned(host)) { return error::success; } - /////////////////////////////////////////////////////////////////////////// // Critical Section - mutex_.lock_upgrade(); + upgrade_lock lock(mutex_); - if (stopped_) - { - mutex_.unlock_upgrade(); - //--------------------------------------------------------------------- + if (stopped_) { return error::service_stopped; } - if (find(host) == buffer_.end()) - { - mutex_.unlock_upgrade_and_lock(); - //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - buffer_.insert(host); - auto iter = inactive_.find(host); - if (iter != inactive_.end()){ - inactive_.erase(iter); + auto iter = std::find_if(seeds_.begin(), seeds_.end(), + [&host](const address& item){ + return host.ip == item.ip && host.port == item.port; + }); + + if (iter == seeds_.end()) { + constexpr size_t max_seeds = 8; + constexpr size_t half_pos = max_seeds >> 1; + upgrade_to_unique_lock unq_lock(lock); + if (seeds_.size() == max_seeds) { // full + std::copy(seeds_.begin()+half_pos+1, seeds_.end(), seeds_.begin()+half_pos); + seeds_.back() = host; + } + else { + seeds_.push_back(host); } +#ifdef PRIVATE_CHAIN + log::info(LOG_NETWORK) << "store seed " << config::authority(host).to_string(); +#endif + } + + return error::success; +} - mutex_.unlock(); - //--------------------------------------------------------------------- +code hosts::remove_seed(const address& host) +{ + if (disabled_) { return error::success; } - mutex_.unlock_upgrade(); - /////////////////////////////////////////////////////////////////////////// + // Critical Section + upgrade_lock lock(mutex_); + + if (stopped_) { + return error::service_stopped; + } + + auto iter = std::find_if(seeds_.begin(), seeds_.end(), + [&host](const address& item){ + return host.ip == item.ip && host.port == item.port; + }); + + if (iter != seeds_.end()) { + upgrade_to_unique_lock unq_lock(lock); + seeds_.erase(iter); -// log::trace(LOG_NETWORK) -// << "Redundant host address from peer"; +#ifdef PRIVATE_CHAIN + log::info(LOG_NETWORK) << "remove seed " << config::authority(host).to_string(); +#endif + } - // We don't treat redundant address as an error, just log it. return error::success; } -// private -void hosts::do_store(const address& host, result_handler handler) +code hosts::store(const address& host) { - handler(store(host)); + if (disabled_) { + return error::success; + } + + if (!host.is_routable()) { + // We don't treat invalid address as an error, just log it. + return error::success; + } + + // don't store self address + auto authority = config::authority{host}; + if (authority == self_ || authority.port() == 0) { + return error::success; + } + + // don't store blacklist and banned address + if (channel::blacklisted(host) || channel::manualbanned(host)) { + return error::success; + } + + // Critical Section + upgrade_lock lock(mutex_); + + if (stopped_) { + return error::service_stopped; + } + + upgrade_to_unique_lock unq_lock(lock); + + if (find(host) == buffer_.end()) { + buffer_.push_back(host); + } + + auto iter = find(inactive_, host); + if (iter != inactive_.end()) { + inactive_.erase(iter); + } + + return error::success; } // The handler is invoked once all calls to do_store are completed. // We disperse here to allow other addresses messages to interleave hosts. void hosts::store(const address::list& hosts, result_handler handler) { - if (stopped_) + if (disabled_ || hosts.empty()) { + handler(error::success); + return; + } + + // Critical Section + upgrade_lock lock(mutex_); + + if (stopped_) { + handler(error::service_stopped); return; + } + + // Accept between 1 and all of this peer's addresses up to capacity. + const auto capacity = host_pool_capacity_; + size_t host_size = hosts.size(); + const size_t usable = std::min(host_size, capacity); + const size_t random = static_cast(pseudo_random(1, usable)); + + // But always accept at least the amount we are short if available. + const size_t gap = capacity - buffer_.size(); + const size_t accept = std::max(gap, random); + + // Convert minimum desired to step for iteration, no less than 1. + const auto step = std::max(usable / accept, size_t(1)); + size_t accepted = 0; + + { + upgrade_to_unique_lock unq_lock(lock); + //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + for (size_t index = 0; index < usable; index = ceiling_add(index, step)) { + const auto& host = hosts[index]; + + // Do not treat invalid address as an error, just log it. + if (!host.is_valid()) { + log::debug(LOG_NETWORK) + << "Invalid host address from peer."; + continue; + } + + if (channel::blacklisted(host) || channel::manualbanned(host)) { + continue; + } + + // Do not allow duplicates in the host cache. + if (find(host) == buffer_.end() + && find(inactive_, host) == inactive_.end()) { + ++accepted; + buffer_.push_back(host); + } + } + + log::debug(LOG_NETWORK) + << "Accepted (" << accepted << " of " << hosts.size() + << ") host addresses from peer." + << " inactive size is " << inactive_.size() + << ", buffer size is " << buffer_.size(); + } - dispatch_.parallel(hosts, "hosts", handler, - &hosts::do_store, shared_from_this()); + // Notice: don't unique lock this handler + handler(error::success); } } // namespace network diff --git a/src/lib/network/p2p.cpp b/src/lib/network/p2p.cpp index af370245f..d0553c100 100644 --- a/src/lib/network/p2p.cpp +++ b/src/lib/network/p2p.cpp @@ -263,7 +263,7 @@ session_outbound::ptr p2p::attach_outbound_session() bool p2p::stop() { // This is the only stop operation that can fail. - const auto result = (hosts_->stop() == (code)error::success); + const auto result = (hosts_->stop().value() == error::success); // Signal all current work to stop and free manual session. stopped_ = true; @@ -423,11 +423,27 @@ void p2p::fetch_address(const config::authority::list& excluded_list, address_ha handler(hosts_->fetch(out, excluded_list), out); } +void p2p::fetch_seed_address(const config::authority::list& excluded_list, address_handler handler) +{ + address out; + handler(hosts_->fetch_seed(out, excluded_list), out); +} + config::authority::list p2p::authority_list() { return connections_->authority_list(); } +void p2p::store_seed(const address& address, result_handler handler) +{ + handler(hosts_->store_seed(address)); +} + +void p2p::remove_seed(const address& address, result_handler handler) +{ + handler(hosts_->remove_seed(address)); +} + void p2p::store(const address& address, result_handler handler) { handler(hosts_->store(address)); @@ -454,6 +470,11 @@ p2p::address::list p2p::address_list() return hosts_->copy(); } +p2p::address::list p2p::seed_address_list() +{ + return hosts_->copy_seeds(); +} + connections::ptr p2p::connections_ptr() { return connections_; @@ -625,8 +646,15 @@ void p2p::map_port(bool) } #endif // #ifdef USE_UPNP -void p2p::restart_seeding() +void p2p::restart_seeding(bool manual) { + if (manual) { + seed->restart([](const code& ec) { + log::debug(LOG_NETWORK) << "restart_seeding manual result: " << ec.message(); + }); + return; + } + //1. clear the host::buffer_ cache const auto result = hosts_->clear(); log::debug(LOG_NETWORK) << "restart_seeding clear hosts cache: " << result.message(); diff --git a/src/lib/network/protocols/protocol_address.cpp b/src/lib/network/protocols/protocol_address.cpp index 3df3b007c..810e05573 100644 --- a/src/lib/network/protocols/protocol_address.cpp +++ b/src/lib/network/protocols/protocol_address.cpp @@ -64,7 +64,7 @@ void protocol_address::start() network_address nt_address=settings.self.to_network_address(); //for testnet don't filter local ip - if (settings.hosts_file == "hosts-test.cache") { + if (network_.is_use_testnet_rules()) { self_ = address({ { nt_address } }); SEND2(self_, handle_send, _1, self_.command); } @@ -83,7 +83,7 @@ void protocol_address::start() if (sp_out_address && (settings.self != *sp_out_address)) { const config::authority& out_address = *sp_out_address; network_address nt_address = out_address.to_network_address(); - if (settings.hosts_file == "hosts-test.cache") { + if (network_.is_use_testnet_rules()) { address self = address({ { nt_address } }); log::info("UPnP") << "send addresss " << out_address.to_string(); SEND2(self, handle_send, _1, self.command); @@ -108,22 +108,15 @@ void protocol_address::start() void protocol_address::remove_useless_address(address::ptr& message) { - auto& addresses = message->addresses; const auto& settings = network_.network_settings(); if(settings.self.port() != 0) { - auto iter = std::find_if(addresses.begin(), addresses.end(), [&settings](const message::network_address& addr){ - if(config::authority{addr} == settings.self) - { - return true; - } - return false; - }); - - if(iter != addresses.end()) - { - addresses.erase(iter); - } + auto pred = [&settings](const network_address& addr) { + auto authority = config::authority{addr}; + return authority == settings.self || authority.port() == 0; + }; + auto& addresses = message->addresses; + addresses.erase(std::remove_if(addresses.begin(), addresses.end(), pred), addresses.end()); } } @@ -133,32 +126,18 @@ void protocol_address::remove_useless_address(address::ptr& message) bool protocol_address::handle_receive_address(const code& ec, address::ptr message) { - if (stopped()) + if (stopped(ec)) return false; - if (ec) - { - log::trace(LOG_NETWORK) - << "Failure receiving address message from [" - << authority() << "] " << ec.message(); - stop(ec); - - return false; - } remove_useless_address(message); log::trace(LOG_NETWORK) << "Storing addresses from [" << authority() << "] (" << message->addresses.size() << ")"; -// if (message->addresses.size() > 1000) -// { -// return ! misbehaving(20); -// } network_address::list addresses; addresses.reserve(message->addresses.size()); for (auto& addr:message->addresses) { - //if (!channel::blacklisted(addr)) { - if (!channel::manualbanned(addr)) { + if (!channel::blacklisted(addr) && !channel::manualbanned(addr)) { addresses.push_back(addr); } } @@ -173,58 +152,27 @@ bool protocol_address::handle_receive_address(const code& ec, bool protocol_address::handle_receive_get_address(const code& ec, get_address::ptr message) { - if (stopped()) + if (stopped(ec)) return false; - if (ec) - { - log::trace(LOG_NETWORK) - << "Failure receiving get_address message from [" - << authority() << "] " << ec.message(); - stop(ec); - return false; - } - - - // TODO: allowing repeated queries can allow a channel to map our history. - // TODO: pull active hosts from host cache (currently just resending self). - // TODO: need to distort for privacy, don't send currently-connected peers. - auto&& address_list = network_.address_list(); - auto channel_authorithy = authority(); - auto iter = std::find_if(address_list.begin(), address_list.end(), [&channel_authorithy](const message::network_address& address){ - if(config::authority{address} == channel_authorithy) - { - return true; - } - return false; - }); - if(iter != address_list.end() ) + if(!address_list.empty()) { - address_list.erase(iter); - } - - if(address_list.empty()) - { - return true; + log::trace(LOG_NETWORK) + << "Sending addresses to [" << authority() << "] (" + << address_list.size() << ")"; + message::address self_address = {address_list}; + SEND2(self_address, handle_send, _1, self_address.command); } -// if (self_.addresses.empty()) -// return false; - - log::trace(LOG_NETWORK) - << "Sending addresses to [" << authority() << "] (" - << address_list.size() << ")"; - message::address self_address = {address_list}; - SEND2(self_address, handle_send, _1, self_address.command); - // RESUBSCRIBE - return true; + // do not resubscribe; one response per connection permitted + return false; } void protocol_address::handle_store_addresses(const code& ec, address::ptr message) { - if (stopped()) + if (stopped(ec)) return; if (ec) diff --git a/src/lib/network/protocols/protocol_events.cpp b/src/lib/network/protocols/protocol_events.cpp index 704239438..004819edc 100644 --- a/src/lib/network/protocols/protocol_events.cpp +++ b/src/lib/network/protocols/protocol_events.cpp @@ -43,11 +43,19 @@ protocol_events::protocol_events(p2p& network, channel::ptr channel, // Properties. // ---------------------------------------------------------------------------- -bool protocol_events::stopped() +bool protocol_events::stopped() const { return !handler_.load(); } +bool protocol_events::stopped(const code& ec) const +{ + // The service stop code may also make its way into protocol handlers. + return stopped() || + ec.value() == error::channel_stopped || + ec.value() == error::service_stopped; +} + // Start. // ---------------------------------------------------------------------------- @@ -81,7 +89,7 @@ void protocol_events::set_event(const code& ec) if (!handler) return; - if (ec == (code)error::channel_stopped) + if (ec.value() == error::channel_stopped) handler_.store(nullptr); handler(ec); diff --git a/src/lib/network/protocols/protocol_ping.cpp b/src/lib/network/protocols/protocol_ping.cpp index b0f0cc195..3abd736d2 100644 --- a/src/lib/network/protocols/protocol_ping.cpp +++ b/src/lib/network/protocols/protocol_ping.cpp @@ -62,10 +62,10 @@ void protocol_ping::start() // This is fired by the callback (i.e. base timer and stop handler). void protocol_ping::send_ping(const code& ec) { - if (stopped()) + if (stopped(ec)) return; - if (ec && ec != error::channel_timeout) + if (ec && ec.value() != error::channel_timeout) { log::trace(LOG_NETWORK) << "Failure in ping timer for [" << authority() << "] " @@ -83,7 +83,7 @@ void protocol_ping::send_ping(const code& ec) bool protocol_ping::handle_receive_ping(const code& ec, message::ping::ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -104,7 +104,7 @@ bool protocol_ping::handle_receive_ping(const code& ec, bool protocol_ping::handle_receive_pong(const code& ec, message::pong::ptr message, uint64_t nonce) { - if (stopped()) + if (stopped(ec)) return false; if (ec) diff --git a/src/lib/network/protocols/protocol_seed.cpp b/src/lib/network/protocols/protocol_seed.cpp index 1a52f5f68..763c6facc 100644 --- a/src/lib/network/protocols/protocol_seed.cpp +++ b/src/lib/network/protocols/protocol_seed.cpp @@ -91,18 +91,9 @@ void protocol_seed::handle_seeding_complete(const code& ec, bool protocol_seed::handle_receive_address(const code& ec, address::ptr message) { - if (stopped()) + if (stopped(ec)) return false; - if (ec) - { - log::trace(LOG_NETWORK) - << "Failure receiving addresses from seed [" << authority() << "] " - << ec.message(); - set_event(ec); - return false; - } - log::trace(LOG_NETWORK) << "Storing addresses from seed [" << authority() << "] (" << message->addresses.size() << ")"; @@ -115,25 +106,16 @@ bool protocol_seed::handle_receive_address(const code& ec, void protocol_seed::handle_send_address(const code& ec) { - if (stopped()) + if (stopped(ec)) return; - if (ec) - { - log::trace(LOG_NETWORK) - << "Failure sending address to seed [" << authority() << "] " - << ec.message(); - set_event(ec); - return; - } - // 1 of 3 set_event(error::success); } void protocol_seed::handle_send_get_address(const code& ec) { - if (stopped()) + if (stopped(ec)) return; if (ec) @@ -151,7 +133,7 @@ void protocol_seed::handle_send_get_address(const code& ec) void protocol_seed::handle_store_addresses(const code& ec) { - if (stopped()) + if (stopped(ec)) return; if (ec) diff --git a/src/lib/network/protocols/protocol_timer.cpp b/src/lib/network/protocols/protocol_timer.cpp index 3327005ee..b0acfdec3 100644 --- a/src/lib/network/protocols/protocol_timer.cpp +++ b/src/lib/network/protocols/protocol_timer.cpp @@ -56,7 +56,7 @@ void protocol_timer::start(const asio::duration& timeout, void protocol_timer::handle_notify(const code& ec, event_handler handler) { - if (ec == (code)error::channel_stopped) + if (ec.value() == error::channel_stopped) timer_->stop(); handler(ec); @@ -81,7 +81,7 @@ void protocol_timer::reset_timer() void protocol_timer::handle_timer(const code& ec) { - if (stopped()) + if (stopped(ec)) { return; } diff --git a/src/lib/network/protocols/protocol_version.cpp b/src/lib/network/protocols/protocol_version.cpp index cbf26d331..d8bd83ba5 100644 --- a/src/lib/network/protocols/protocol_version.cpp +++ b/src/lib/network/protocols/protocol_version.cpp @@ -117,7 +117,7 @@ void protocol_version::send_version(const message::version& self) bool protocol_version::handle_receive_version(const code& ec, version::ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -144,7 +144,7 @@ bool protocol_version::handle_receive_version(const code& ec, bool protocol_version::handle_receive_verack(const code& ec, verack::ptr) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -163,7 +163,7 @@ bool protocol_version::handle_receive_verack(const code& ec, verack::ptr) void protocol_version::handle_version_sent(const code& ec) { - if (stopped()) + if (stopped(ec)) return; if (ec) @@ -178,7 +178,7 @@ void protocol_version::handle_version_sent(const code& ec) void protocol_version::handle_verack_sent(const code& ec) { - if (stopped()) + if (stopped(ec)) return; if (ec) diff --git a/src/lib/network/proxy.cpp b/src/lib/network/proxy.cpp index af018a38f..d889eab2a 100644 --- a/src/lib/network/proxy.cpp +++ b/src/lib/network/proxy.cpp @@ -564,5 +564,17 @@ bool proxy::misbehaving(int32_t howmuch) return false; } +std::map proxy::get_banned() +{ + boost::detail::spinlock::scoped_lock guard{proxy::spinlock_}; + return banned_; +} + +std::list proxy::get_manual_banned() +{ + boost::detail::spinlock::scoped_lock guard{proxy::manual_banned_spinlock_}; + return manual_banned_; +} + } // namespace network } // namespace libbitcoin diff --git a/src/lib/network/sessions/session.cpp b/src/lib/network/sessions/session.cpp index c3a26841a..e174d9fbf 100644 --- a/src/lib/network/sessions/session.cpp +++ b/src/lib/network/sessions/session.cpp @@ -85,6 +85,11 @@ void session::fetch_address(host_handler handler) network_.fetch_address(network_.authority_list(), handler); } +void session::fetch_seed_address(host_handler handler) +{ + network_.fetch_seed_address(network_.authority_list(), handler); +} + // protected: void session::connection_count(count_handler handler) { @@ -94,6 +99,9 @@ void session::connection_count(count_handler handler) // protected: bool session::blacklisted(const authority& authority) const { + if (authority == settings_.self) { + return true; + } const auto& blocked = settings_.blacklists; // black through IP, does not care port. const auto it = std::find_if(blocked.begin(), blocked.end(), @@ -175,6 +183,11 @@ bool session::stopped() const return stopped_; } +bool session::stopped(const code& ec) const +{ + return stopped() || ec.value() == error::service_stopped; +} + // Subscribe Stop sequence. // ---------------------------------------------------------------------------- diff --git a/src/lib/network/sessions/session_batch.cpp b/src/lib/network/sessions/session_batch.cpp index c86c14970..38a734bcb 100644 --- a/src/lib/network/sessions/session_batch.cpp +++ b/src/lib/network/sessions/session_batch.cpp @@ -83,6 +83,19 @@ void session_batch::converge(const code& ec, channel::ptr channel, // Connect sequence. // ---------------------------------------------------------------------------- +// protected: +void session_batch::connect_seed(connector::ptr connect, channel_handler handler) +{ + // synchronizer state. + const auto mutex = std::make_shared(); + const auto counter = std::make_shared(0); + const auto singular = BIND5(converge, _1, _2, counter, mutex, handler); + + for (uint32_t host = 0; host < batch_size_; ++host) { + new_connect(connect, counter, singular, true); + } +} + // protected: void session_batch::connect(connector::ptr connect, channel_handler handler) { @@ -92,11 +105,11 @@ void session_batch::connect(connector::ptr connect, channel_handler handler) const auto singular = BIND5(converge, _1, _2, counter, mutex, handler); for (uint32_t host = 0; host < batch_size_; ++host) - new_connect(connect, counter, singular); + new_connect(connect, counter, singular, false); } void session_batch::new_connect(connector::ptr connect, - atomic_counter_ptr counter, channel_handler handler) + atomic_counter_ptr counter, channel_handler handler, bool only_seed) { if (stopped()) { @@ -107,20 +120,31 @@ void session_batch::new_connect(connector::ptr connect, if (counter->load() == batch_size_) return; - fetch_address(BIND5(start_connect, _1, _2, connect, counter, handler)); + + if (only_seed) { + fetch_seed_address(BIND5(start_connect, _1, _2, connect, counter, handler)); + } + else { + fetch_address(BIND5(start_connect, _1, _2, connect, counter, handler)); + } } void session_batch::start_connect(const code& ec, const authority& host, connector::ptr connect, atomic_counter_ptr counter, channel_handler handler) { - if (counter->load() == batch_size_ || ec == (code)error::service_stopped) + if (stopped(ec)) + return; + + if (counter->load() == batch_size_) return; // This termination prevents a tight loop in the empty address pool case. if (ec) { - log::warning(LOG_NETWORK) - << "Failure fetching new address: " << ec.message(); + if (ec.value() != error::not_found) { + log::warning(LOG_NETWORK) + << "Failure fetching new address: " << ec.message(); + } handler(ec, nullptr); return; } @@ -152,10 +176,8 @@ void session_batch::handle_connect(const code& ec, channel::ptr channel, log::trace(LOG_NETWORK) << "Failure connecting to [" << host << "] " << count << "," << ec.message(); - if (ec == error::channel_timeout) // if connect is not aviliable, change it into inactive state + if (ec.value() == error::channel_timeout) // if connect is not aviliable, change it into inactive state remove(host.to_network_address(), [](const code&){}); - else - store(host.to_network_address()); handler(ec, channel); return; } @@ -163,7 +185,7 @@ void session_batch::handle_connect(const code& ec, channel::ptr channel, store(host.to_network_address()); log::trace(LOG_NETWORK) - << "Connected to [" << channel->authority() << "]"; + << "Connected to [" << host << "]"; // This is the end of the connect sequence. handler(error::success, channel); diff --git a/src/lib/network/sessions/session_inbound.cpp b/src/lib/network/sessions/session_inbound.cpp index 767cdf864..167616a59 100644 --- a/src/lib/network/sessions/session_inbound.cpp +++ b/src/lib/network/sessions/session_inbound.cpp @@ -103,7 +103,7 @@ void session_inbound::start_accept(const code& ec, acceptor::ptr accept) void session_inbound::handle_accept(const code& ec, channel::ptr channel, acceptor::ptr accept) { - if (stopped()) + if (stopped(ec)) { log::trace(LOG_NETWORK) << "Suspended inbound connection."; @@ -119,7 +119,7 @@ void session_inbound::handle_accept(const code& ec, channel::ptr channel, return; } - if (blacklisted(channel->authority())) + if (channel && blacklisted(channel->authority())) { log::trace(LOG_NETWORK) << "Rejected inbound connection from [" diff --git a/src/lib/network/sessions/session_manual.cpp b/src/lib/network/sessions/session_manual.cpp index ae58b83cd..9514fd6bc 100644 --- a/src/lib/network/sessions/session_manual.cpp +++ b/src/lib/network/sessions/session_manual.cpp @@ -60,6 +60,7 @@ void session_manual::handle_started(const code& ec, result_handler handler) } connector_.store(create_connector()); + connect_timer_ = std::make_shared(pool_, asio::seconds(2)); // This is the end of the start sequence. handler(error::success); @@ -106,15 +107,20 @@ void session_manual::handle_connect(const code& ec, channel::ptr channel, const std::string& hostname, uint16_t port, channel_handler handler, uint32_t retries) { + if (channel && blacklisted(channel->authority())) { + log::debug(LOG_NETWORK) + << "Suspended blacklisted/banned manual connection [" << channel->authority() << "]"; + + handler(error::address_blocked, nullptr); + return; + } + if (ec) { - log::debug(LOG_NETWORK) + log::trace(LOG_NETWORK) << "Failure connecting [" << config::endpoint(hostname, port) << "] manually: " << ec.message(); - auto shared_this = shared_from_base(); - const auto timer = std::make_shared(pool_, asio::seconds(3)); - // Retry logic. if (settings_.manual_attempt_limit == 0) delay_new_connection(hostname, port, handler, 0); @@ -126,13 +132,13 @@ void session_manual::handle_connect(const code& ec, channel::ptr channel, return; } - log::debug(LOG_NETWORK) + log::trace(LOG_NETWORK) << "Connected manual channel [" << config::endpoint(hostname, port) << "] as [" << channel->authority() << "]"; register_channel(channel, BIND5(handle_channel_start, _1, hostname, port, channel, handler), - BIND3(handle_channel_stop, _1, hostname, port)); + BIND4(handle_channel_stop, _1, hostname, port, channel)); } void session_manual::handle_channel_start(const code& ec, @@ -147,7 +153,7 @@ void session_manual::handle_channel_start(const code& ec, << "] " << ec.message(); // Special case for already connected, do not keep trying. - if (ec == (code)error::address_in_use) + if (ec.value() == error::address_in_use) { handler(ec, channel); return; @@ -172,30 +178,29 @@ void session_manual::attach_protocols(channel::ptr channel) void session_manual::delay_new_connection(const std::string& hostname, uint16_t port , channel_handler handler, uint32_t retries) { - auto timer = std::make_shared(pool_, asio::seconds(2)); auto self = shared_from_this(); - timer->start([this, timer, self, hostname, port, handler, retries](const code& ec){ - if (stopped()) - { + connect_timer_->start([this, self, hostname, port, handler, retries](const code& ec){ + if (ec || stopped()) { return; } - auto pThis = shared_from_this(); - auto action = [this, pThis, hostname, port, handler, retries](){ - start_connect(hostname, port, handler, retries); - }; - pool_.service().post(action); + pool_.service().post( + std::bind(&session_manual::start_connect, + shared_from_base(), + hostname, port, handler, retries)); }); } // After a stop we don't use the caller's start handler, but keep connecting. void session_manual::handle_channel_stop(const code& ec, - const std::string& hostname, uint16_t port) + const std::string& hostname, uint16_t port, channel::ptr channel) { - log::debug(LOG_NETWORK) + log::trace(LOG_NETWORK) << "Manual channel stopped: " << ec.message(); - if (stopped() || (ec.value() == error::service_stopped)) + if (stopped(ec) || (channel && blacklisted(channel->authority()))) { + connect_timer_->stop(); return; + } delay_new_connection(hostname, port, [](code, channel::ptr){}, settings_.manual_attempt_limit); diff --git a/src/lib/network/sessions/session_outbound.cpp b/src/lib/network/sessions/session_outbound.cpp index 603e105c1..b5fb957e7 100644 --- a/src/lib/network/sessions/session_outbound.cpp +++ b/src/lib/network/sessions/session_outbound.cpp @@ -44,6 +44,17 @@ session_outbound::session_outbound(p2p& network) in_reseeding = false; } +session_outbound::~session_outbound() +{ + if (reseeding_timer_) { + reseeding_timer_->stop(); + } + for (auto connect_timer : connect_timer_list_) { + connect_timer->stop(); + } + connect_timer_list_.clear(); +} + // Start sequence. // ---------------------------------------------------------------------------- @@ -71,11 +82,27 @@ void session_outbound::handle_started(const code& ec, result_handler handler) outbound_counter = 0; in_reseeding = false; + reseeding_timer_ = std::make_shared(pool_, asio::seconds(60)); + const auto connect = create_connector(); - for (size_t peer = 0; peer < settings_.outbound_connections; ++peer) - { - log::debug(LOG_NETWORK) << "new connection"; - new_connection(connect); + + auto self = shared_from_this(); + auto make_timer = [this, self]() -> deadline::ptr { + connect_timer_list_.emplace_back(std::make_shared(pool_, asio::seconds(2))); + return connect_timer_list_.back(); + }; + + for (auto i = 0; i < 3; ++i) { + new_connection(connect, make_timer(), true, true); // connect seed first + } + + while (outbound_counter == 0 && !stopped()) { // loop until connected successfully + new_connection(connect, nullptr, false, false); + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + } + + for (size_t peer = 0; peer < settings_.outbound_connections; ++peer) { + new_connection(connect, make_timer(), true, false); } // This is the end of the start sequence. @@ -85,7 +112,8 @@ void session_outbound::handle_started(const code& ec, result_handler handler) // Connnect cycle. // ---------------------------------------------------------------------------- -void session_outbound::new_connection(connector::ptr connect) +void session_outbound::new_connection( + connector::ptr connect, deadline::ptr connect_timer, bool reconnect, bool only_seed) { if (stopped()) { @@ -93,25 +121,28 @@ void session_outbound::new_connection(connector::ptr connect) << "Suspended outbound connection."; return; } - static int i{0}; - int b = i++; - this->connect(connect, BIND3(handle_connect, _1, _2, connect)); + if (only_seed) { + this->connect_seed(connect, BIND6(handle_connect, _1, _2, connect, connect_timer, reconnect, only_seed)); + } + else { + this->connect(connect, BIND6(handle_connect, _1, _2, connect, connect_timer, reconnect, only_seed)); + } } -void session_outbound::delay_new_connect(connector::ptr connect) +void session_outbound::delay_new_connect(connector::ptr connect, deadline::ptr connect_timer, bool only_seed) { - auto timer = std::make_shared(pool_, asio::seconds(2)); + if (!connect_timer) { + return; + } auto self = shared_from_this(); - timer->start([this, connect, timer, self](const code& ec){ - if (stopped()) - { + connect_timer->start([this, self, connect, connect_timer, only_seed](const code& ec){ + if (ec || stopped()) { return; } - auto pThis = shared_from_this(); - auto action = [this, pThis, connect](){ - new_connection(connect); - }; - pool_.service().post(action); + pool_.service().post( + std::bind(&session_outbound::new_connection, + shared_from_base(), + connect, connect_timer, true, only_seed)); }); } @@ -126,46 +157,60 @@ void session_outbound::delay_reseeding() } in_reseeding = true; log::debug(LOG_NETWORK) << "outbound channel counter decreased, the re-seeding will be triggered 60s later!"; - auto timer = std::make_shared(pool_, asio::seconds(60)); auto self = shared_from_this(); - timer->start([this, timer, self](const code& ec){ - if (stopped()) - { + reseeding_timer_->start([this, self](const code& ec){ + if (ec || stopped()) { + in_reseeding = false; return; } - auto pThis = shared_from_this(); - auto action = [this, pThis](){ - const int counter = outbound_counter; - if (counter > 1) { - log::debug(LOG_NETWORK) << "outbound channel counter recovered to [" << counter << "], re-seeding is canceled!"; - in_reseeding = false; - return; - } - log::debug(LOG_NETWORK) << "start re-seeding!"; - network__.restart_seeding(); - in_reseeding = false; - }; - pool_.service().post(action); + pool_.service().post( + std::bind(&session_outbound::handle_reseeding, + shared_from_base())); }); } +void session_outbound::handle_reseeding() +{ + const int counter = outbound_counter; + if (counter > 1) { + log::debug(LOG_NETWORK) + << "outbound channel counter recovered to [" + << counter + << "], re-seeding is canceled!"; + } + else { + log::debug(LOG_NETWORK) << "start re-seeding!"; + network__.restart_seeding(); + } + in_reseeding = false; +} + void session_outbound::handle_connect(const code& ec, channel::ptr channel, - connector::ptr connect) + connector::ptr connect, deadline::ptr connect_timer, bool reconnect, bool only_seed) { + if (channel && blacklisted(channel->authority())) { + log::trace(LOG_NETWORK) + << "Suspended blacklisted/banned outbound connection [" << channel->authority() << "]"; + return; + } + if (ec) { log::trace(LOG_NETWORK) << "Failure connecting outbound: " << ec.message(); - delay_new_connect(connect); + if (reconnect) { + delay_new_connect(connect, connect_timer, only_seed); + } return; } log::trace(LOG_NETWORK) << "Connected to outbound channel [" << channel->authority() << "]"; ++outbound_counter; + register_channel(channel, BIND3(handle_channel_start, _1, connect, channel), - BIND3(handle_channel_stop, _1, connect, channel)); + BIND5(handle_channel_stop, _1, connect, channel, connect_timer, only_seed)); } void session_outbound::handle_channel_start(const code& ec, @@ -183,6 +228,8 @@ void session_outbound::handle_channel_start(const code& ec, } attach_protocols(channel); + + network__.store_seed(channel->authority().to_network_address(), [](const code&){}); }; void session_outbound::attach_protocols(channel::ptr channel) @@ -191,21 +238,23 @@ void session_outbound::attach_protocols(channel::ptr channel) attach(channel)->do_subscribe()->start(); } -void session_outbound::handle_channel_stop(const code& ec, - connector::ptr connect, channel::ptr channel) +void session_outbound::handle_channel_stop( + const code& ec, connector::ptr connect, channel::ptr channel, + deadline::ptr connect_timer, bool only_seed) { channel->invoke_protocol_start_handler(error::channel_stopped); - log::debug(LOG_NETWORK) << "channel stopped," << ec.message(); + log::trace(LOG_NETWORK) << "channel stopped," << ec.message(); const int counter = --outbound_counter; - if(! stopped() && ec.value() != error::service_stopped) - { - delay_new_connect(connect); - //restart the seeding procedure with in 1 minutes when outbound session count reduce to 1. - if (counter <= 1) { - delay_reseeding(); - } + if (stopped(ec)) { + return; + } + + delay_new_connect(connect, connect_timer, only_seed); + //restart the seeding procedure with in 1 minutes when outbound session count reduce to 1. + if (counter <= 1) { + delay_reseeding(); } } diff --git a/src/lib/network/sessions/session_seed.cpp b/src/lib/network/sessions/session_seed.cpp index 2aae0ae1d..7bf237acd 100644 --- a/src/lib/network/sessions/session_seed.cpp +++ b/src/lib/network/sessions/session_seed.cpp @@ -78,7 +78,7 @@ void session_seed::handle_started(const code& ec, result_handler handler) void session_seed::handle_count(size_t start_size, result_handler handler) { - if (start_size != 0) + if (start_size != 0 && settings_.seeds.empty()) { log::debug(LOG_NETWORK) << "Seeding is not required because there are " @@ -95,6 +95,11 @@ void session_seed::handle_count(size_t start_size, result_handler handler) return; } + log::debug(LOG_NETWORK) + << "Start seeding, there are " + << start_size << " cached addresses."; + + start_size = 0; // This is NOT technically the end of the start sequence, since the handler // is not invoked until seeding operations are complete. start_seeding(start_size, create_connector(), handler); @@ -132,10 +137,12 @@ void session_seed::start_seed(const config::endpoint& seed, << "Contacting seed [" << seed << "]"; // OUTBOUND CONNECT - connect->connect(seed, BIND4(handle_connect, _1, _2, seed, handler), [this](const asio::endpoint& endpoint){ + auto resolve_handler = [this](const asio::endpoint& endpoint){ + network_.store_seed(config::authority{endpoint}.to_network_address(), [](const code& ec){}); network_.store(config::authority{endpoint}.to_network_address(), [](const code& ec){}); log::debug(LOG_NETWORK) << "session seed store," << endpoint ; - }); + }; + connect->connect(seed, BIND4(handle_connect, _1, _2, seed, handler), resolve_handler); } void session_seed::handle_connect(const code& ec, channel::ptr channel, @@ -149,7 +156,7 @@ void session_seed::handle_connect(const code& ec, channel::ptr channel, return; } - if (blacklisted(channel->authority())) + if (channel && blacklisted(channel->authority())) { log::debug(LOG_NETWORK) << "Seed [" << seed << "] on blacklisted address [" diff --git a/src/lib/node/p2p_node.cpp b/src/lib/node/p2p_node.cpp index b94865217..335ed9442 100644 --- a/src/lib/node/p2p_node.cpp +++ b/src/lib/node/p2p_node.cpp @@ -168,10 +168,10 @@ void p2p_node::handle_running(const code& ec, result_handler handler) bool p2p_node::handle_reorganized(const code& ec, size_t fork_point, const block_ptr_list& incoming, const block_ptr_list& outgoing) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped() || ec.value() == error::service_stopped) return false; - if (ec == (code)error::mock) + if (ec.value() == error::mock) return true; if (ec) diff --git a/src/lib/node/protocols/protocol_block_in.cpp b/src/lib/node/protocols/protocol_block_in.cpp index c8676c555..77ae0aa2e 100644 --- a/src/lib/node/protocols/protocol_block_in.cpp +++ b/src/lib/node/protocols/protocol_block_in.cpp @@ -103,13 +103,13 @@ void protocol_block_in::start() // This is fired by the callback (i.e. base timer and stop handler). void protocol_block_in::get_block_inventory(const code& ec) { - if (stopped()) + if (stopped(ec)) { blockchain_.fired(); return; } - if (ec && ec != (code)error::channel_timeout) + if (ec && ec.value() != error::channel_timeout) { log::trace(LOG_NODE) << "Failure in block timer for [" << authority() << "] " @@ -165,7 +165,7 @@ void protocol_block_in::send_get_blocks(const hash_digest& from_hash, const hash void protocol_block_in::handle_fetch_block_locator(const code& ec, const hash_list& locator, const hash_digest& stop_hash) { - if (stopped() || ec == (code)error::service_stopped || locator.empty()) + if (stopped(ec) || locator.empty()) return; if (ec) @@ -207,7 +207,7 @@ void protocol_block_in::handle_fetch_block_locator(const code& ec, bool protocol_block_in::handle_receive_headers(const code& ec, headers_ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -235,7 +235,7 @@ bool protocol_block_in::handle_receive_headers(const code& ec, bool protocol_block_in::handle_receive_inventory(const code& ec, inventory_ptr message) { - if (stopped()) + if (stopped(ec)) { return false; } @@ -267,8 +267,7 @@ bool protocol_block_in::handle_receive_inventory(const code& ec, void protocol_block_in::handle_filter_orphans(const code& ec, get_data_ptr message) { - if (stopped() || ec == (code)error::service_stopped || - message->inventories.empty()) + if (stopped(ec) || message->inventories.empty()) return; if (ec) @@ -286,8 +285,7 @@ void protocol_block_in::handle_filter_orphans(const code& ec, void protocol_block_in::send_get_data(const code& ec, get_data_ptr message) { - if (stopped() || ec == (code)error::service_stopped || - message->inventories.empty()) + if (stopped(ec) || message->inventories.empty()) return; if (ec) @@ -312,7 +310,7 @@ void protocol_block_in::send_get_data(const code& ec, get_data_ptr message) bool protocol_block_in::handle_receive_not_found(const code& ec, message::not_found::ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -346,7 +344,7 @@ bool protocol_block_in::handle_receive_not_found(const code& ec, bool protocol_block_in::handle_receive_block(const code& ec, block_ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -380,11 +378,11 @@ bool protocol_block_in::handle_receive_block(const code& ec, block_ptr message) void protocol_block_in::handle_store_block(const code& ec, block_ptr message) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return; // Ignore the block that we already have, a common result. - if (ec == (code)error::duplicate) + if (ec.value() == error::duplicate) { log::trace(LOG_NODE) << "Redundant block from [" << authority() << "] " @@ -392,7 +390,7 @@ void protocol_block_in::handle_store_block(const code& ec, block_ptr message) return; } - if(ec == (code)error::fetch_more_block) + if(ec.value() == error::fetch_more_block) { log::trace(LOG_NODE) << "fetch more blocks start_hash:" @@ -426,13 +424,13 @@ void protocol_block_in::handle_store_block(const code& ec, block_ptr message) bool protocol_block_in::handle_reorganized(const code& ec, size_t fork_point, const block_ptr_list& incoming, const block_ptr_list& outgoing) { - if (stopped() || ec == (code)error::service_stopped || incoming.empty()) + if (stopped(ec) || incoming.empty()) { log::trace(LOG_NODE) << "protocol_block_in::handle_reorganized ," << stopped() << "," << ec.message() << "," << incoming.size(); return false; } - if (ec == (code)error::mock) + if (ec.value() == error::mock) { return true; } diff --git a/src/lib/node/protocols/protocol_block_out.cpp b/src/lib/node/protocols/protocol_block_out.cpp index 5c8e173d5..1ec607806 100644 --- a/src/lib/node/protocols/protocol_block_out.cpp +++ b/src/lib/node/protocols/protocol_block_out.cpp @@ -100,7 +100,7 @@ void protocol_block_out::start() bool protocol_block_out::handle_receive_send_headers(const code& ec, send_headers_ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -137,7 +137,7 @@ size_t protocol_block_out::locator_limit() const bool protocol_block_out::handle_receive_get_headers(const code& ec, get_headers_ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -197,7 +197,7 @@ bool protocol_block_out::handle_receive_get_headers(const code& ec, void protocol_block_out::handle_fetch_locator_headers(const code& ec, const header_list& headers) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return; if (ec) @@ -227,7 +227,7 @@ void protocol_block_out::handle_fetch_locator_headers(const code& ec, bool protocol_block_out::handle_receive_get_blocks(const code& ec, get_blocks_ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -270,7 +270,7 @@ bool protocol_block_out::handle_receive_get_blocks(const code& ec, void protocol_block_out::handle_fetch_locator_hashes(const code& ec, const hash_list& hashes) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return; if (ec) @@ -298,7 +298,7 @@ void protocol_block_out::handle_fetch_locator_hashes(const code& ec, bool protocol_block_out::handle_receive_get_data(const code& ec, get_data_ptr message) { - if (stopped()) + if (stopped(ec)) { return false; } @@ -331,12 +331,12 @@ bool protocol_block_out::handle_receive_get_data(const code& ec, void protocol_block_out::send_block(const code& ec, chain::block::ptr block, const hash_digest& hash) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) { return; } - if (ec == (code)error::not_found) + if (ec.value() == error::not_found) { log::trace(LOG_NODE) << "Block requested by [" << authority() << "] not found." << encode_hash(hash); @@ -363,10 +363,10 @@ void protocol_block_out::send_block(const code& ec, chain::block::ptr block, void protocol_block_out::send_merkle_block(const code& ec, merkle_block_ptr message, const hash_digest& hash) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return; - if (ec == (code)error::not_found) + if (ec.value() == error::not_found) { log::trace(LOG_NODE) << "Merkle block requested by [" << authority() << "] not found."; @@ -396,10 +396,10 @@ void protocol_block_out::send_merkle_block(const code& ec, bool protocol_block_out::handle_reorganized(const code& ec, size_t fork_point, const block_ptr_list& incoming, const block_ptr_list& outgoing) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return false; - if (ec == (code)error::mock) + if (ec.value() == error::mock) { return true; } diff --git a/src/lib/node/protocols/protocol_block_sync.cpp b/src/lib/node/protocols/protocol_block_sync.cpp index 4dea64655..cc0c9b9b3 100644 --- a/src/lib/node/protocols/protocol_block_sync.cpp +++ b/src/lib/node/protocols/protocol_block_sync.cpp @@ -96,7 +96,7 @@ void protocol_block_sync::send_get_blocks(event_handler complete, bool reset) void protocol_block_sync::handle_send(const code& ec, event_handler complete) { - if (stopped()) + if (stopped(ec)) return; if (ec) @@ -115,7 +115,7 @@ void protocol_block_sync::handle_send(const code& ec, event_handler complete) bool protocol_block_sync::handle_receive(const code& ec, block_ptr message, event_handler complete) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -146,19 +146,19 @@ bool protocol_block_sync::handle_receive(const code& ec, block_ptr message, // This is fired by the base timer and stop handler. void protocol_block_sync::handle_event(const code& ec, event_handler complete) { - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) { complete(ec); return ; } - if (ec == (code)error::channel_stopped) + if (ec.value() == error::channel_stopped) { complete(ec); return; } - if (ec && ec != (code)error::channel_timeout) + if (ec && ec.value() != error::channel_timeout) { log::trace(LOG_NODE) << "Failure in block sync timer for slot (" << reservation_->slot() diff --git a/src/lib/node/protocols/protocol_header_sync.cpp b/src/lib/node/protocols/protocol_header_sync.cpp index c3090801c..57bbb53b0 100644 --- a/src/lib/node/protocols/protocol_header_sync.cpp +++ b/src/lib/node/protocols/protocol_header_sync.cpp @@ -106,7 +106,7 @@ void protocol_header_sync::send_get_headers(event_handler complete) void protocol_header_sync::handle_send(const code& ec, event_handler complete) { - if (stopped()) + if (stopped(ec)) return; if (ec) @@ -121,7 +121,7 @@ void protocol_header_sync::handle_send(const code& ec, event_handler complete) bool protocol_header_sync::handle_receive(const code& ec, headers_ptr message, event_handler complete) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -172,13 +172,13 @@ bool protocol_header_sync::handle_receive(const code& ec, headers_ptr message, // This is fired by the base timer and stop handler. void protocol_header_sync::handle_event(const code& ec, event_handler complete) { - if (ec == (code)error::channel_stopped) + if (ec && ec.value() == error::channel_stopped) { complete(ec); return; } - if (ec && ec != (code)error::channel_timeout) + if (ec && ec.value() != error::channel_timeout) { log::warning(LOG_NODE) << "Failure in header sync timer for [" << authority() << "] " diff --git a/src/lib/node/protocols/protocol_transaction_in.cpp b/src/lib/node/protocols/protocol_transaction_in.cpp index 1c35f7ea2..fe49f3b07 100644 --- a/src/lib/node/protocols/protocol_transaction_in.cpp +++ b/src/lib/node/protocols/protocol_transaction_in.cpp @@ -94,7 +94,7 @@ void protocol_transaction_in::start() bool protocol_transaction_in::handle_receive_inventory(const code& ec, inventory_ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -131,8 +131,7 @@ bool protocol_transaction_in::handle_receive_inventory(const code& ec, void protocol_transaction_in::handle_filter_floaters(const code& ec, get_data_ptr message) { - if (stopped() || ec == (code)error::service_stopped || - message->inventories.empty()) + if (stopped(ec) || message->inventories.empty()) return; if (ec) @@ -152,8 +151,7 @@ void protocol_transaction_in::handle_filter_floaters(const code& ec, void protocol_transaction_in::send_get_data(const code& ec, get_data_ptr message) { - if (stopped() || ec == (code)error::service_stopped || - message->inventories.empty()) + if (stopped(ec) || message->inventories.empty()) return; if (ec) @@ -175,7 +173,7 @@ void protocol_transaction_in::send_get_data(const code& ec, bool protocol_transaction_in::handle_receive_transaction(const code& ec, transaction_ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -240,10 +238,10 @@ void protocol_transaction_in::handle_store_confirmed(const code& ec, bool protocol_transaction_in::handle_reorganized(const code& ec, size_t, const block_ptr_list&, const block_ptr_list& outgoing) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return false; - if (ec == (code)error::mock) + if (ec.value() == error::mock) { return true; } diff --git a/src/lib/node/protocols/protocol_transaction_out.cpp b/src/lib/node/protocols/protocol_transaction_out.cpp index f0a70c8b2..96b818644 100644 --- a/src/lib/node/protocols/protocol_transaction_out.cpp +++ b/src/lib/node/protocols/protocol_transaction_out.cpp @@ -90,7 +90,7 @@ void protocol_transaction_out::start() bool protocol_transaction_out::handle_receive_fee_filter(const code& ec, fee_filter_ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -116,17 +116,13 @@ bool protocol_transaction_out::handle_receive_fee_filter(const code& ec, bool protocol_transaction_out::handle_receive_memory_pool(const code& ec, memory_pool_ptr) { - if (stopped()) { - return false; - } - - if (ec) { + if (stopped(ec) || ec) { return false; } auto self = shared_from_this(); pool_.fetch([this, self](const code& ec, const std::vector& txs){ - if (stopped() || ec) { + if (stopped(ec) || ec) { log::debug(LOG_NODE) << "pool fetch transaction failed," << ec.message(); return; } @@ -150,7 +146,7 @@ bool protocol_transaction_out::handle_receive_memory_pool(const code& ec, bool protocol_transaction_out::handle_receive_get_data(const code& ec, get_data_ptr message) { - if (stopped()) + if (stopped(ec)) return false; if (ec) @@ -191,10 +187,10 @@ bool protocol_transaction_out::handle_receive_get_data(const code& ec, void protocol_transaction_out::send_transaction(const code& ec, const chain::transaction& transaction, const hash_digest& hash) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return; - if (ec == (code)error::not_found) + if (ec.value() == error::not_found) { log::trace(LOG_NODE) << "Transaction requested by [" << authority() << "] not found."; @@ -226,10 +222,10 @@ void protocol_transaction_out::send_transaction(const code& ec, bool protocol_transaction_out::handle_floated(const code& ec, const index_list& unconfirmed, transaction_ptr message) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return false; - if (ec == (code)error::mock) + if (ec.value() == error::mock) return true; if (ec) diff --git a/src/lib/protocol/packet.cpp b/src/lib/protocol/packet.cpp index 92c37e379..1dc0cd6c9 100644 --- a/src/lib/protocol/packet.cpp +++ b/src/lib/protocol/packet.cpp @@ -52,7 +52,7 @@ bool packet::receive(zmq::socket& socket) { zmq::message message; - if (socket.receive(message) != error::success || message.empty()) + if (socket.receive(message).value() != error::success || message.empty()) return false; // Optional - ROUTER sockets strip this. @@ -81,7 +81,7 @@ bool packet::send(zmq::socket& socket) // Add empty delimiter frame. message.enqueue(data_chunk{}); - return encode_payload(message) && socket.send(message) == error::success; + return encode_payload(message) && socket.send(message).value() == error::success; } ////bool packet::send(const std::shared_ptr& socket) diff --git a/src/lib/protocol/zmq/authenticator.cpp b/src/lib/protocol/zmq/authenticator.cpp index 67f28f554..5b9681e57 100644 --- a/src/lib/protocol/zmq/authenticator.cpp +++ b/src/lib/protocol/zmq/authenticator.cpp @@ -84,7 +84,7 @@ void authenticator::work() { socket router(context_, zmq::socket::role::router); - if (!started(router.bind(endpoint) == (code)error::success)) + if (!started(router.bind(endpoint).value() == error::success)) return; poller poller; @@ -107,7 +107,7 @@ void authenticator::work() message request; auto ec = router.receive(request); - if (ec != (code)error::success || request.size() < 8) + if (ec.value() != error::success || request.size() < 8) { status_code = "500"; status_text = "Internal error."; diff --git a/src/lib/protocol/zmq/worker.cpp b/src/lib/protocol/zmq/worker.cpp index 6efcd1759..6425b163a 100644 --- a/src/lib/protocol/zmq/worker.cpp +++ b/src/lib/protocol/zmq/worker.cpp @@ -102,6 +102,13 @@ bool worker::stopped() return stopped_; } +bool worker::stopped(const code& ec) +{ + return stopped() || + ec.value() == error::service_stopped || + ec.value() == error::channel_stopped; +} + // Call from work when started (connected/bound) or failed to do so. bool worker::started(bool result) { diff --git a/src/mvs-cli/main.cpp b/src/mvs-cli/main.cpp index 273a309b8..f6e1af8a9 100644 --- a/src/mvs-cli/main.cpp +++ b/src/mvs-cli/main.cpp @@ -66,7 +66,35 @@ int bc::main(int argc, char* argv[]) bc::set_utf8_stdout(); auto work_path = bc::default_data_path(); auto&& config_file = work_path / "mvs.conf"; - std::string url{"127.0.0.1:8820/rpc/v3"}; + std::string default_rpc_version = "3"; + std::string url{"127.0.0.1:8820/rpc/v" + default_rpc_version}; + + // use '-c file_name' to specify config file name + if (argc > 1 && std::string(argv[1]) == "-c") { + if (argc < 3 || std::string(argv[2]).empty()) { + std::cout << "'-c' option must followed by a non-empty config file name." + << std::endl; + return 0; + } + std::string file_name = argv[2]; + config_file = work_path / file_name; + if (!boost::filesystem::exists(config_file)) { + std::cout << "The specified config file '" + << config_file.string() + << "' does not exist!" + << std::endl; + return 0; + } + if (boost::filesystem::is_directory(config_file)) { + std::cout << "The specified config file '" + << config_file.string() + << "' is a directory!" + << std::endl; + return 0; + } + argc -= 2; + argv += 2; + } if (boost::filesystem::exists(config_file)) { const auto& path = config_file.string(); @@ -77,8 +105,10 @@ int bc::main(int argc, char* argv[]) } std::string tmp; + std::string rpc_version; po::options_description desc(""); desc.add_options() + ("server.rpc_version", po::value(&rpc_version)->default_value(default_rpc_version)) ("server.mongoose_listen", po::value(&tmp)->default_value("127.0.0.1:8820")); po::variables_map vm; @@ -91,7 +121,7 @@ int bc::main(int argc, char* argv[]) if (tmp.find("0.0.0.0") == 0) { tmp.replace(0, 7, "127.0.0.1"); } - url = tmp + "/rpc/v3"; + url = tmp + "/rpc/v" + rpc_version; } } } @@ -100,7 +130,6 @@ int bc::main(int argc, char* argv[]) HttpReq req(url, 3000, reply_handler(my_impl)); Json::Value jsonvar; - Json::Value jsonopt; jsonvar["jsonrpc"] = "2.0"; jsonvar["id"] = 1; jsonvar["method"] = (argc > 1) ? argv[1] : "help"; diff --git a/src/mvsd/CMakeLists.txt b/src/mvsd/CMakeLists.txt index a1f0b915c..2fd853847 100644 --- a/src/mvsd/CMakeLists.txt +++ b/src/mvsd/CMakeLists.txt @@ -8,12 +8,12 @@ IF(ENABLE_SHARED_LIBS) ADD_DEFINITIONS(-DBCS_DLL=1) TARGET_LINK_LIBRARIES(mvsd ${Boost_LIBRARIES} ${network_LIBRARY} ${database_LIBRARY} ${consensus_LIBRARY} ${blockchain_LIBRARY} ${bitcoin_LIBRARY} ${mongoose_LIBRARY} ${node_LIBRARY} - ${protocol_LIBRARY} ${client_LIBRARY} ${explorer_LIBRARY} ${cryptojs_LIBRARY}) + ${protocol_LIBRARY} ${client_LIBRARY} ${explorer_LIBRARY} ${cryptojs_LIBRARY} ${sodium_LIBRARY}) ELSE() ADD_DEFINITIONS(-DBCS_STATIC=1) TARGET_LINK_LIBRARIES(mvsd ${Boost_LIBRARIES} ${network_LIBRARY} ${database_LIBRARY} ${consensus_LIBRARY} - ${blockchain_LIBRARY} ${bitcoin_LIBRARY} ${mongoose_LIBRARY} ${node_LIBRARY} - ${protocol_LIBRARY} ${client_LIBRARY} ${explorer_LIBRARY} ${cryptojs_LIBRARY}) + ${blockchain_LIBRARY} ${sodium_LIBRARY} ${bitcoin_LIBRARY} ${mongoose_LIBRARY} ${node_LIBRARY} + ${protocol_LIBRARY} ${client_LIBRARY} ${explorer_LIBRARY} ${cryptojs_LIBRARY} ) ENDIF() INSTALL(TARGETS mvsd DESTINATION bin) diff --git a/src/mvsd/executor.cpp b/src/mvsd/executor.cpp index e32cbe75e..3fdc337ef 100644 --- a/src/mvsd/executor.cpp +++ b/src/mvsd/executor.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -232,6 +233,13 @@ bool executor::run() // Now that the directory is verified we can create the node for it. node_ = std::make_shared(metadata_.configured); +#ifdef PRIVATE_CHAIN + log::info(LOG_SERVER) << "running prinet"; +#else + log::info(LOG_SERVER) + << (!node_->is_use_testnet_rules() ? "running mainnet" : "running testnet"); +#endif + // The callback may be returned on the same thread. node_->start( std::bind(&executor::handle_started, @@ -254,7 +262,7 @@ bool executor::run() // Handle the completion of the start sequence and begin the run sequence. void executor::handle_started(const code& ec) { - if (ec) + if (ec && ec.value() != error::operation_failed) { log::error(LOG_SERVER) << format(BS_NODE_START_FAIL) % ec.message(); stop(ec); @@ -279,7 +287,9 @@ void executor::handle_running(const code& ec) { if (ec) { - log::info(LOG_SERVER) << format(BS_NODE_START_FAIL) % ec.message(); + if (ec.value() != error::service_stopped) { + log::info(LOG_SERVER) << format(BS_NODE_START_FAIL) % ec.message(); + } stop(ec); return; } diff --git a/src/mvsd/main.cpp b/src/mvsd/main.cpp index 5b36c3682..70055feca 100644 --- a/src/mvsd/main.cpp +++ b/src/mvsd/main.cpp @@ -54,7 +54,7 @@ int bc::main(int argc, char* argv[]) if(metadata.configured.daemon) { libbitcoin::daemon(); - static fstream fout; + static std::fstream fout; fout.open("/dev/null"); if(! fout.good()) throw std::runtime_error{"open /dev/null failed"}; diff --git a/src/mvsd/mgbubble/HttpServ.cpp b/src/mvsd/mgbubble/HttpServ.cpp index 3d909226c..4922d3f0c 100644 --- a/src/mvsd/mgbubble/HttpServ.cpp +++ b/src/mvsd/mgbubble/HttpServ.cpp @@ -26,6 +26,7 @@ #include namespace mgbubble { +using namespace libbitcoin; thread_local OStream HttpServ::out_; thread_local Tokeniser<'/'> HttpServ::uri_; @@ -61,10 +62,6 @@ void HttpServ::rpc_request(mg_connection& nc, HttpMessage data, uint8_t rpc_vers out_.rdbuf(&buf); out_.reset(200, "OK"); - const vector api20_ver_list = {2, 3}; - auto checkAPIVer = [](const vector &api_ver_list, const uint8_t &rpc_version) { - return find(api_ver_list.begin(), api_ver_list.end(), rpc_version) != api_ver_list.end(); - }; try { data.data_to_arg(rpc_version); @@ -87,7 +84,7 @@ void HttpServ::rpc_request(mg_connection& nc, HttpMessage data, uint8_t rpc_vers else out_ << jv_output.asString(); } - else if (checkAPIVer(api20_ver_list, rpc_version)) { + else { Json::Value jv_root; jv_root["jsonrpc"] = "2.0"; jv_root["id"] = data.jsonrpc_id(); @@ -101,7 +98,7 @@ void HttpServ::rpc_request(mg_connection& nc, HttpMessage data, uint8_t rpc_vers if (rpc_version == 1) { out_ << e; } - else if (checkAPIVer(api20_ver_list, rpc_version)) { + else { Json::Value root; root["jsonrpc"] = "2.0"; root["id"] = data.jsonrpc_id(); @@ -116,7 +113,7 @@ void HttpServ::rpc_request(mg_connection& nc, HttpMessage data, uint8_t rpc_vers libbitcoin::explorer::explorer_exception ex(1000, e.what()); out_ << ex; } - else if (checkAPIVer(api20_ver_list, rpc_version)) { + else { Json::Value root; root["jsonrpc"] = "2.0"; root["id"] = data.jsonrpc_id(); @@ -180,14 +177,21 @@ void HttpServ::run() { void HttpServ::on_http_req_handler(struct mg_connection& nc, http_message& msg) { - if ((mg_ncasecmp(msg.uri.p, "/rpc/v3", 7) == 0) || (mg_ncasecmp(msg.uri.p, "/rpc/v3/", 8) == 0)) { - rpc_request(nc, HttpMessage(&msg), 3); // v3 rpc - } - else if ((mg_ncasecmp(msg.uri.p, "/rpc/v2", 7) == 0) || (mg_ncasecmp(msg.uri.p, "/rpc/v2/", 8) == 0)) { - rpc_request(nc, HttpMessage(&msg), 2); // v2 rpc - } - else if ((mg_ncasecmp(msg.uri.p, "/rpc", 4) == 0) || (mg_ncasecmp(msg.uri.p, "/rpc/", 5) == 0)) { - rpc_request(nc, HttpMessage(&msg), 1); //v1 rpc + auto get_api_version = [&msg]() -> int { + if ((msg.uri.len >= 6) && (mg_ncasecmp(msg.uri.p, "/rpc/v", 6) == 0)) { + return std::max(1, std::atoi(msg.uri.p + 6)); + } + if ((msg.uri.len >= 4) && (mg_ncasecmp(msg.uri.p, "/rpc", 4) == 0)) { + if ( (msg.uri.len == 4) || (msg.uri.p[4] == '/')) { + return 1; + } + } + return 0; + }; + + auto api_version = get_api_version(); + if (api_version > 0) { + rpc_request(nc, HttpMessage(&msg), api_version); } else { std::shared_ptr con(&nc, [](struct mg_connection * ptr) { (void)(ptr); }); diff --git a/src/mvsd/mgbubble/Mongoose.cpp b/src/mvsd/mgbubble/Mongoose.cpp index 78eaa63cb..fc94508eb 100644 --- a/src/mvsd/mgbubble/Mongoose.cpp +++ b/src/mvsd/mgbubble/Mongoose.cpp @@ -77,8 +77,8 @@ void HttpMessage::data_to_arg(uint8_t rpc_version) { * } * ******************************************/ - const vector api20_ver_list = {"2.0", "3.0"}; - auto checkAPIVer = [](const vector &api_ver_list, const std::string &rpc_version){ + const std::vector api20_ver_list = {"2.0", "3.0"}; + auto checkAPIVer = [](const std::vector &api_ver_list, const std::string &rpc_version){ return find(api_ver_list.begin(), api_ver_list.end(), rpc_version) != api_ver_list.end(); }; diff --git a/src/mvsd/mgbubble/WsPushServ.cpp b/src/mvsd/mgbubble/WsPushServ.cpp index 85dfd68d4..542507a70 100644 --- a/src/mvsd/mgbubble/WsPushServ.cpp +++ b/src/mvsd/mgbubble/WsPushServ.cpp @@ -45,6 +45,7 @@ constexpr int JSON_FORMAT_VERSION = 3; namespace mgbubble { using namespace bc; using namespace libbitcoin; +using payment_address = wallet::payment_address; explorer::config::json_helper get_json_helper() { @@ -90,7 +91,7 @@ bool WsPushServ::handle_transaction_pool(const code& ec, const index_list&, mess { if (stopped()) return false; - if (ec == (code)error::mock || ec == (code)error::service_stopped) + if (ec.value() == error::mock || ec.value() == error::service_stopped) return true; if (ec) { @@ -106,7 +107,7 @@ bool WsPushServ::handle_blockchain_reorganization(const code& ec, uint64_t fork_ { if (stopped()) return false; - if (ec == (code)error::mock || ec == (code)error::service_stopped) + if (ec.value() == error::mock || ec.value() == error::service_stopped) return true; if (ec) { @@ -429,7 +430,7 @@ void WsPushServ::on_ws_frame_handler(struct mg_connection& nc, websocket_message if (!reader.parse(begin, end, root) || !root.isObject() || !root["event"].isString()) { - stringstream ss; + std::stringstream ss; ss << "parse request error, " << reader.getFormattedErrorMessages(); throw std::runtime_error(ss.str()); @@ -440,7 +441,7 @@ void WsPushServ::on_ws_frame_handler(struct mg_connection& nc, websocket_message auto event = root["event"].asString(); if (event == EV_SUBSCRIBE || event == EV_UNSUBSCRIBE) { if (!root["channel"].isString()) { - stringstream ss; + std::stringstream ss; ss << "parse request error, " << reader.getFormattedErrorMessages(); throw std::runtime_error(ss.str()); @@ -452,7 +453,7 @@ void WsPushServ::on_ws_frame_handler(struct mg_connection& nc, websocket_message if ((event == EV_SUBSCRIBE) && (channel == CH_TRANSACTION)) { if (!root["address"].isString() && !root["address"].isArray()) { - stringstream ss; + std::stringstream ss; ss << "parse request error, invalid address!" << reader.getFormattedErrorMessages(); throw std::runtime_error(ss.str()); diff --git a/src/mvsd/server/parser.cpp b/src/mvsd/server/parser.cpp index e3f3fbf87..7666688e1 100644 --- a/src/mvsd/server/parser.cpp +++ b/src/mvsd/server/parser.cpp @@ -384,6 +384,11 @@ options_metadata parser::load_settings() value(&configured.server.log_level), "Setup log level of debug log in level [TRACE,DEBUG,INFO], defaults to DEBUG." ) + ( + "server.rpc_version", + value(&configured.server.rpc_version), + "Server RPC version, defaults to empty string, only used by mvs-cli." + ) ( "server.secure_only", value(&configured.server.secure_only), @@ -490,6 +495,8 @@ bool parser::parse(int argc, const char* argv[], std::ostream& error) if (get_option(variables, BS_TESTNET_VARIABLE)) { configured.network.hosts_file = "hosts-test.cache"; + configured.network.debug_file = "debug-test.log"; + configured.network.error_file = "error-test.log"; const_cast(variables[BS_CONFIG_VARIABLE].as()) = "mvs-test.conf"; } auto data_dir = variables[BS_DATADIR_VARIABLE].as(); @@ -499,7 +506,8 @@ bool parser::parse(int argc, const char* argv[], std::ostream& error) { error << format_invalid_parameter("datadir path is invalid.") << std::endl; return false; - } } + } + } // Returns true if the settings were loaded from a file. file = load_configuration_variables(variables, BS_CONFIG_VARIABLE); } diff --git a/src/mvsd/server/server_node.cpp b/src/mvsd/server/server_node.cpp index 141acc367..3860b9a1b 100644 --- a/src/mvsd/server/server_node.cpp +++ b/src/mvsd/server/server_node.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -64,6 +65,7 @@ server_node::server_node(const configuration& configuration) rest_server_(new mgbubble::HttpServ(webpage_path_.string().data(), *this, configuration.server.mongoose_listen)), push_server_(new mgbubble::WsPushServ(*this, configuration.server.websocket_listen)) { + consensus::witness::create(*this); } // This allows for shutdown based on destruct without need to call stop. @@ -80,17 +82,21 @@ const settings& server_node::server_settings() const return configuration_.server; } -// Run sequence. -// ---------------------------------------------------------------------------- +bool server_node::is_use_testnet_rules() const +{ + return configuration_.use_testnet_rules; +} -void server_node::run(result_handler handler) +void server_node::start(result_handler handler) { - if (stopped()) + if (!stopped()) { - handler(error::service_stopped); + handler(error::operation_failed); return; } + p2p_node::start(handler); + if (!rest_server_->start() || !push_server_->start()) { log::error(LOG_SERVER) << "Http/Websocket server can not start."; @@ -98,6 +104,25 @@ void server_node::run(result_handler handler) return; } + if (!consensus::witness::get().init_witness_list()) + { + log::error(LOG_SERVER) << "init witness list failed."; + handler(error::witness_update_error); + return; + } +} + +// Run sequence. +// ---------------------------------------------------------------------------- + +void server_node::run(result_handler handler) +{ + if (stopped()) + { + handler(error::service_stopped); + return; + } + // The handler is invoked on a new thread. p2p_node::run( std::bind(&server_node::handle_running, diff --git a/src/mvsd/server/services/block_service.cpp b/src/mvsd/server/services/block_service.cpp index 1ddbb30e0..1fac0a90b 100644 --- a/src/mvsd/server/services/block_service.cpp +++ b/src/mvsd/server/services/block_service.cpp @@ -147,10 +147,10 @@ bool block_service::unbind(zmq::socket& xpub, zmq::socket& xsub) bool block_service::handle_reorganization(const code& ec, uint64_t fork_point, const block_list& new_blocks, const block_list&) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped() || ec.value() == error::service_stopped) return false; - if (ec == error::mock) + if (ec.value() == error::mock) return true; if (ec) @@ -185,7 +185,7 @@ void block_service::publish_blocks(uint32_t fork_point, zmq::socket publisher(authenticator_, zmq::socket::role::publisher); const auto ec = publisher.connect(endpoint); - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) return; if (ec) @@ -222,7 +222,7 @@ void block_service::publish_block(zmq::socket& publisher, uint32_t height, broadcast.enqueue(block->to_data(false)); const auto ec = publisher.send(broadcast); - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) return; if (ec) diff --git a/src/mvsd/server/services/heartbeat_service.cpp b/src/mvsd/server/services/heartbeat_service.cpp index 956f389f3..d337ca230 100644 --- a/src/mvsd/server/services/heartbeat_service.cpp +++ b/src/mvsd/server/services/heartbeat_service.cpp @@ -132,7 +132,7 @@ void heartbeat_service::publish(uint32_t count, zmq::socket& publisher) message.enqueue_little_endian(count); auto ec = publisher.send(message); - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) return; if (ec) diff --git a/src/mvsd/server/services/transaction_service.cpp b/src/mvsd/server/services/transaction_service.cpp index 1030a2b29..e44977127 100644 --- a/src/mvsd/server/services/transaction_service.cpp +++ b/src/mvsd/server/services/transaction_service.cpp @@ -149,10 +149,10 @@ bool transaction_service::unbind(zmq::socket& xpub, zmq::socket& xsub) bool transaction_service::handle_transaction(const code& ec, const index_list&, transaction_message::ptr tx) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped() || ec.value() == error::service_stopped) return false; - if (ec == (code)error::mock) + if (ec.value() == error::mock) { return true; } @@ -185,7 +185,7 @@ void transaction_service::publish_transaction(const transaction& tx) zmq::socket publisher(authenticator_, zmq::socket::role::publisher); auto ec = publisher.connect(endpoint); - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) return; if (ec) @@ -204,7 +204,7 @@ void transaction_service::publish_transaction(const transaction& tx) broadcast.enqueue(tx_msg.to_data(bc::message::version::level::maximum)); ec = publisher.send(broadcast); - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) return; if (ec) diff --git a/src/mvsd/server/settings.cpp b/src/mvsd/server/settings.cpp index 2cc3f3f43..18e6375b1 100644 --- a/src/mvsd/server/settings.cpp +++ b/src/mvsd/server/settings.cpp @@ -35,6 +35,7 @@ settings::settings() websocket_listen("127.0.0.1:8821"), administrator_required(false), log_level("DEBUG"), + rpc_version(""), secure_only(false), query_service_enabled(true), heartbeat_service_enabled(false), diff --git a/src/mvsd/server/utility/fetch_helpers.cpp b/src/mvsd/server/utility/fetch_helpers.cpp index 5841c97a7..fbe93c2bf 100644 --- a/src/mvsd/server/utility/fetch_helpers.cpp +++ b/src/mvsd/server/utility/fetch_helpers.cpp @@ -111,7 +111,7 @@ void chain_transaction_fetched(const code& ec, const chain::transaction& tx, const message& request, send_handler handler) { // wdy add for tx is null reference - if((code)error::not_found == ec) { + if (error::not_found == ec.value()) { handler(message(request, error::not_found)); return; } diff --git a/src/mvsd/server/workers/notification_worker.cpp b/src/mvsd/server/workers/notification_worker.cpp index fc90ead3f..5403e0c12 100644 --- a/src/mvsd/server/workers/notification_worker.cpp +++ b/src/mvsd/server/workers/notification_worker.cpp @@ -226,7 +226,7 @@ void notification_worker::send(const route& reply_to, zmq::socket notifier(authenticator_, zmq::socket::role::router); auto ec = notifier.connect(endpoint); - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) return; if (ec) @@ -241,7 +241,7 @@ void notification_worker::send(const route& reply_to, message notification(reply_to, command, id, payload); ec = notification.send(notifier); - if (ec && ec != (code)error::service_stopped) + if (ec && ec.value() != error::service_stopped) log::warning(LOG_SERVER) << "Failed to send notification to " << notification.route().display() << " " << ec.message(); @@ -464,7 +464,7 @@ void notification_worker::subscribe_penetration(const route& reply_to, bool notification_worker::handle_blockchain_reorganization(const code& ec, uint64_t fork_point, const block_list& new_blocks, const block_list&) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return false; if (ec) @@ -499,7 +499,7 @@ void notification_worker::notify_blocks(uint32_t fork_point, zmq::socket publisher(authenticator_, zmq::socket::role::publisher); const auto ec = publisher.connect(endpoint); - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) return; if (ec) @@ -542,7 +542,7 @@ void notification_worker::notify_block(zmq::socket& publisher, uint32_t height, bool notification_worker::handle_inventory(const code& ec, const bc::message::inventory::ptr packet) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return false; if (ec) @@ -568,10 +568,10 @@ bool notification_worker::handle_inventory(const code& ec, bool notification_worker::handle_transaction_pool(const code& ec, const point::indexes&, bc::message::transaction_message::ptr tx) { - if (stopped() || ec == (code)error::service_stopped) + if (stopped(ec)) return false; - if (ec == (code)error::mock) + if (ec.value() == error::mock) { return true; } diff --git a/src/mvsd/server/workers/query_worker.cpp b/src/mvsd/server/workers/query_worker.cpp index d12d7d169..53eb362cf 100644 --- a/src/mvsd/server/workers/query_worker.cpp +++ b/src/mvsd/server/workers/query_worker.cpp @@ -127,7 +127,7 @@ void query_worker::query(zmq::socket& router) { const auto ec = response.send(router); - if (ec && ec != (code)error::service_stopped) + if (ec && ec.value() != error::service_stopped) log::warning(LOG_SERVER) << "Failed to send query response to " << response.route().display() << " " << ec.message(); @@ -136,7 +136,7 @@ void query_worker::query(zmq::socket& router) message request(secure_); const auto ec = request.receive(router); - if (ec == (code)error::service_stopped) + if (ec.value() == error::service_stopped) return; if (ec) diff --git a/src/windows/mvstray/mvstray.cpp b/src/windows/mvstray/mvstray.cpp index 9ca000d86..d2d1315bb 100644 --- a/src/windows/mvstray/mvstray.cpp +++ b/src/windows/mvstray/mvstray.cpp @@ -39,7 +39,7 @@ #define IDM_EXIT 100 #define IDM_OPEN 101 #define IDM_AUTOSTART 102 -#define WM_USER_SHELLICON WM_USER + 1 +#define WM_USER_SHELLICON WM_USER + 1 #define WM_TIMER_OPEN 107 HANDLE metaverseHandle = INVALID_HANDLE_VALUE; @@ -56,10 +56,10 @@ bool InitInstance(HINSTANCE, int, LPWSTR cmdLine); LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); void KillMetaverse(); void OpenUI(); +void FindRunningMetaverse(); bool MetaverseIsRunning(); bool AutostartEnabled(); void EnableAutostart(bool enable); -bool bUIOpened = false; bool GetMetaverseExePath(TCHAR* dest, size_t destSize) { @@ -88,19 +88,22 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, UNREFERENCED_PARAMETER(lpCmdLine); CreateMutex(0, FALSE, _T("Local\\MetaverseTray")); - /*if (GetLastError() == ERROR_ALREADY_EXISTS) { - // open the UI - bUIOpened = true; - OpenUI(); - return 0; - }*/ + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + FindRunningMetaverse(); + if (metaverseHandle != INVALID_HANDLE_VALUE) + { + OpenUI(); + return 0; + } + } LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); LoadStringW(hInstance, IDC_MVSTRAY, szWindowClass, MAX_LOADSTRING); MyRegisterClass(hInstance); if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) - return FALSE; + return 0; HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MVSTRAY)); MSG msg; @@ -138,17 +141,15 @@ ATOM MyRegisterClass(HINSTANCE hInstance) return RegisterClassExW(&wcex); } - -bool InitInstance(HINSTANCE hInstance, int nCmdShow, LPWSTR cmdLine) +void FindRunningMetaverse() { - // Check if already running PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - if (Process32First(snapshot, &entry) == TRUE) + if ((snapshot != INVALID_HANDLE_VALUE) && Process32First(snapshot, &entry)) { - while (Process32Next(snapshot, &entry) == TRUE) + do { if (lstrcmp(entry.szExeFile, cMetaverseExe) == 0) { @@ -156,11 +157,15 @@ bool InitInstance(HINSTANCE hInstance, int nCmdShow, LPWSTR cmdLine) metaverseProcId = entry.th32ProcessID; break; } - } + } while (Process32Next(snapshot, &entry)); } CloseHandle(snapshot); +} +bool InitInstance(HINSTANCE hInstance, int nCmdShow, LPWSTR cmdLine) +{ + FindRunningMetaverse(); if (metaverseHandle == INVALID_HANDLE_VALUE) { // Launch Metaverse @@ -175,9 +180,10 @@ bool InitInstance(HINSTANCE hInstance, int nCmdShow, LPWSTR cmdLine) lstrcpy(cmd, path); lstrcat(cmd, _T(" ")); lstrcat(cmd, cmdLine); - if (!CreateProcess(nullptr, cmd, nullptr, nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &startupInfo, &procInfo)) - return false; + BOOL succeed = CreateProcess(nullptr, cmd, nullptr, nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &startupInfo, &procInfo); delete[] cmd; + if (!succeed) + return false; metaverseHandle = procInfo.hProcess; metaverseProcId = procInfo.dwProcessId; } @@ -200,49 +206,48 @@ bool InitInstance(HINSTANCE hInstance, int nCmdShow, LPWSTR cmdLine) Shell_NotifyIcon(NIM_ADD, &nidApp); SetTimer(hWnd, 0, 1000, nullptr); - if (!bUIOpened) { - SetTimer(hWnd, WM_TIMER_OPEN, 1000, nullptr); - } + SetTimer(hWnd, WM_TIMER_OPEN, 1000, nullptr); return true; } +void CreatePopMemu(HWND hWnd) +{ + UINT uFlag = MF_BYPOSITION | MF_STRING; + POINT lpClickPoint; + GetCursorPos(&lpClickPoint); + HMENU hPopMenu = CreatePopupMenu(); + InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_OPEN, _T("Open")); + InsertMenu(hPopMenu, 0xFFFFFFFF, MF_SEPARATOR | MF_BYPOSITION, 0, nullptr); + InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_AUTOSTART, _T("Start at Login")); + InsertMenu(hPopMenu, 0xFFFFFFFF, MF_SEPARATOR | MF_BYPOSITION, 0, nullptr); + InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_EXIT, _T("Exit")); + CheckMenuItem(hPopMenu, IDM_AUTOSTART, AutostartEnabled() ? MF_CHECKED : MF_UNCHECKED); + + SetForegroundWindow(hWnd); + TrackPopupMenu(hPopMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN, lpClickPoint.x, lpClickPoint.y, 0, hWnd, NULL); +} + LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_USER_SHELLICON: // systray msg callback - POINT lpClickPoint; switch (LOWORD(lParam)) { case WM_LBUTTONDOWN: OpenUI(); break; case WM_RBUTTONDOWN: - UINT uFlag = MF_BYPOSITION | MF_STRING; - GetCursorPos(&lpClickPoint); - HMENU hPopMenu = CreatePopupMenu(); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_OPEN, _T("Open")); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_SEPARATOR | MF_BYPOSITION, 0, nullptr); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_AUTOSTART, _T("Start at Login")); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_SEPARATOR | MF_BYPOSITION, 0, nullptr); - InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_EXIT, _T("Exit")); - bool autoStart = AutostartEnabled(); - CheckMenuItem(hPopMenu, IDM_AUTOSTART, autoStart ? MF_CHECKED : autoStart); - - SetForegroundWindow(hWnd); - TrackPopupMenu(hPopMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN, lpClickPoint.x, lpClickPoint.y, 0, hWnd, NULL); - return TRUE; - + CreatePopMemu(hWnd); + return 0; } break; case WM_COMMAND: - { - int wmId = LOWORD(wParam); // Parse the menu selections: - switch (wmId) + switch (LOWORD(wParam)) { case IDM_EXIT: DestroyWindow(hWnd); @@ -251,32 +256,31 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) OpenUI(); break; case IDM_AUTOSTART: - { - bool autoStart = AutostartEnabled(); - EnableAutostart(!autoStart); - } + EnableAutostart(!AutostartEnabled()); break; default: return DefWindowProc(hWnd, message, wParam, lParam); } - } - break; + break; case WM_DESTROY: Shell_NotifyIcon(NIM_DELETE, &nidApp); KillMetaverse(); PostQuitMessage(0); break; case WM_TIMER: - { - int wmId = LOWORD(wParam); - if (wmId == WM_TIMER_OPEN) { - OpenUI(); - KillTimer(hWnd, WM_TIMER_OPEN); - } - else if (!MetaverseIsRunning()) { - DestroyWindow(hWnd); - } - } + switch (LOWORD(wParam)) + { + case WM_TIMER_OPEN: + if (MetaverseIsRunning()) { + KillTimer(hWnd, WM_TIMER_OPEN); + OpenUI(); + } + break; + default: // heartbeat + if (!MetaverseIsRunning()) { + DestroyWindow(hWnd); + } + } break; default: return DefWindowProc(hWnd, message, wParam, lParam); diff --git a/test/test-rpc-v3/HTMLTestRunner.py b/test/test-rpc-v3/HTMLTestRunner.py index 0439bf488..78298bd80 100644 --- a/test/test-rpc-v3/HTMLTestRunner.py +++ b/test/test-rpc-v3/HTMLTestRunner.py @@ -91,7 +91,7 @@ # TODO: simplify javascript using ,ore than 1 class in the class attribute? import datetime -import StringIO +from io import BytesIO import sys import time import unittest @@ -115,7 +115,7 @@ def __init__(self, fp): self.fp = fp def write(self, s): - self.fp.write(s) + self.fp.write( bytes(s, 'utf-8') ) def writelines(self, lines): self.fp.writelines(lines) @@ -536,7 +536,7 @@ def __init__(self, verbosity=1): def startTest(self, test): TestResult.startTest(self, test) # just one buffer for both stdout and stderr - self.outputBuffer = StringIO.StringIO() + self.outputBuffer = BytesIO() stdout_redirector.fp = self.outputBuffer stderr_redirector.fp = self.outputBuffer self.stdout0 = sys.stdout @@ -628,7 +628,7 @@ def run(self, test): test(result) self.stopTime = datetime.datetime.now() self.generateReport(test, result) - print >>sys.stderr, '\nTime Elapsed: %s' % (self.stopTime-self.startTime) + print('\nTime Elapsed: %s' % (self.stopTime-self.startTime), file=sys.stderr) return result @@ -639,7 +639,7 @@ def sortResult(self, result_list): classes = [] for n,t,o,e in result_list: cls = t.__class__ - if not rmap.has_key(cls): + if not cls in rmap: rmap[cls] = [] classes.append(cls) rmap[cls].append((n,t,o,e)) @@ -684,7 +684,7 @@ def generateReport(self, test, result): report = report, ending = ending, ) - self.stream.write(output.encode('utf8')) + self.stream.write(output) def _generate_stylesheet(self): @@ -758,24 +758,23 @@ def _generate_report_test(self, rows, cid, tid, n, t, o, e): doc = t.shortDescription() or "" desc = doc and ('%s: %s' % (name, doc)) or name tmpl = has_output and self.REPORT_TEST_WITH_OUTPUT_TMPL or self.REPORT_TEST_NO_OUTPUT_TMPL - # o and e should be byte string because they are collected from stdout and stderr? if isinstance(o,str): # TODO: some problem with 'string_escape': it escape \n and mess up formating # uo = unicode(o.encode('string_escape')) - uo = o.decode('latin-1') + uo = o.encode('latin-1') else: uo = o if isinstance(e,str): # TODO: some problem with 'string_escape': it escape \n and mess up formating # ue = unicode(e.encode('string_escape')) - ue = e.decode('latin-1') + ue = e.encode('latin-1') else: ue = e script = self.REPORT_TEST_OUTPUT_TMPL % dict( id = tid, - output = saxutils.escape(uo+ue), + output = saxutils.escape( str((uo+ue), 'utf-8') ), ) row = tmpl % dict( diff --git a/test/test-rpc-v3/MOCs.py b/test/test-rpc-v3/MOCs.py index 32d9edaa5..f3f287613 100644 --- a/test/test-rpc-v3/MOCs.py +++ b/test/test-rpc-v3/MOCs.py @@ -124,7 +124,7 @@ def from_json(cls, json_report): i.previous_output = PrevOutput.from_json( json_report["previous_output"] ) i.script = json_report["script"] i.sequence = json_report["sequence"] - if i.previous_output.index <> 0xFFFFFFFF: + if i.previous_output.index != 0xFFFFFFFF: i.address = json_report["address"] return i diff --git a/test/test-rpc-v3/Roles.py b/test/test-rpc-v3/Roles.py index 41fbef6c0..9f6ee7cf3 100644 --- a/test/test-rpc-v3/Roles.py +++ b/test/test-rpc-v3/Roles.py @@ -39,7 +39,7 @@ def didaddress(self): if message and message['dids']: message = message['dids'] dids = [MOCs.Did.init(i) for i in message if i] - found_dids = filter(lambda a: a.symbol == self.did_symbol, dids) + found_dids = list( filter(lambda a: a.symbol == self.did_symbol, dids) ) assert(len(found_dids) == 1) self.did_address = found_dids[0].address @@ -199,6 +199,15 @@ def send_asset(self, to_, amount, asset_symbol=None): print("failed to send_asset: {}, {}".format(result, message)) assert (result == 0) + def sendmore_asset(self, receivers, asset_symbol=None, memo=None, fee=None): + if not asset_symbol: + asset_symbol = self.asset_symbol + + result, message = mvs_rpc.sendmore_asset(self.name, self.password, asset_symbol, receivers, memo, fee) + if (result != 0): + print("failed to sendmore_asset: {}, {}".format(result, message)) + assert (result == 0) + def send_asset_from(self, from_, to_, amount, asset_symbol=None): if not asset_symbol: asset_symbol = self.asset_symbol @@ -256,20 +265,22 @@ def didsend_asset_from(self, from_, to_, amount, symbol): return message["hash"] def mining(self, times=1): - ''' - use the mainaddress to mining x times. - do mining to get the main address rich. + + #use the mainaddress to mining x times. + #do mining to get the main address rich. result, (height_origin, _) = mvs_rpc.get_info() assert (result == 0) mvs_rpc.start_mining(self.name, self.password, self.mainaddress(), times) - for i in range(10): - time.sleep(0.1) + for i in range(times * 2 + 10): + time.sleep(0.5) result, (height_new, _) = mvs_rpc.get_info() assert (result == 0) if height_new == (height_origin + times): break + mvs_rpc.stop_mining() + time.sleep(1) return ''' from ethereum.pow.ethpow import mine @@ -286,20 +297,25 @@ def __mine__(): result, (height, difficulty) = mvs_rpc.get_info() assert (result == 0) + bin_header_hash = bytes.fromhex(header_hash[2:]) + rounds = 100 nonce = 0 while True: - bin_nonce, mixhash = mine(block_number=height+1, difficulty=difficulty, mining_hash=header_hash, + bin_nonce, mixhash = mine(block_number=height+1, difficulty=difficulty, mining_hash=bin_header_hash, rounds=rounds, start_nonce=nonce) if bin_nonce: break nonce += rounds - return bin_nonce, '0x' + common.toString(header_hash), '0x' + common.toString(mixhash) + return '0x' + bin_nonce.hex(), header_hash, '0x' + mixhash.hex() - for i in xrange(times): + for i in range(times): bin_nonce, header_hash, mix_hash = __mine__() - result, message = mvs_rpc.eth_submit_work('0x' + common.toString(bin_nonce), header_hash, mix_hash) + result, message = mvs_rpc.eth_submit_work( bin_nonce, header_hash, mix_hash) + if result != 0: + print('failed to submit work:[%s, %s, %s] -> %s' % (bin_nonce, header_hash, mix_hash, message)) assert (result == 0) + ''' def new_multisigaddress(self, description, others, required_key_num): ''' @@ -371,7 +387,7 @@ def create(self): assert (result == 0) f = open('./Zac.txt', 'w') - print >> f, self.lastword() + f.write( self.lastword() ) f.close() result, _ = mvs_rpc.new_address(self.name, self.password, 9) @@ -488,4 +504,4 @@ def delete(self): Zac = NewGuy("Zac") -__all__ = ["Alice", "Bob", "Cindy", "Dale", "Eric", "Frank", "Zac"] \ No newline at end of file +__all__ = ["Alice", "Bob", "Cindy", "Dale", "Eric", "Frank", "Zac"] diff --git a/test/test-rpc-v3/TestCase/Account/batch_account.py b/test/test-rpc-v3/TestCase/Account/batch_account.py index 4cdde7e4e..aa6337b19 100644 --- a/test/test-rpc-v3/TestCase/Account/batch_account.py +++ b/test/test-rpc-v3/TestCase/Account/batch_account.py @@ -11,7 +11,7 @@ def test_0_new_account(self): batch_amount = 5000 lastwords = [] - for i in xrange(batch_amount): + for i in range(batch_amount): ec, message = mvs_rpc.new_account("Account_%s" % i, "123456") self.assertEqual(ec, 0, message) lastwords.append( message[-1] ) @@ -20,7 +20,7 @@ def test_0_new_account(self): # each simple account record size < 300, but when getnew address, the account record will be create twice, so 600 is the reasonable record size. self.assertGreater(600 * batch_amount, current_payload_size - origin_payload_size, "each account record size shall be less than 600.") finally: - for i in xrange(batch_amount): + for i in range(batch_amount): ec, message = mvs_rpc.delete_account("Account_%s" % i, "123456", lastwords[i]) self.assertEqual(ec, 0, message) @@ -38,7 +38,7 @@ def test_1_new_address(self): origin_payload_size = database.get_payload_size(account_table_file) durations = [] - for i in xrange(round): + for i in range(round): duration, ret = common.duration_call(mvs_rpc.new_address, Zac.name, Zac.password) self.assertEqual(ret[0], 0, "mvs_rpc.new_address failed!") self.assertLess(duration, max_duration) diff --git a/test/test-rpc-v3/TestCase/Asset/batch_asset.py b/test/test-rpc-v3/TestCase/Asset/batch_asset.py index d5cffac56..634db2f06 100644 --- a/test/test-rpc-v3/TestCase/Asset/batch_asset.py +++ b/test/test-rpc-v3/TestCase/Asset/batch_asset.py @@ -13,7 +13,7 @@ def test_0_getaccountasset(self): avg_duration = 0.002 durations = [] - for i in xrange(round): + for i in range(round): print i duration, ret = common.duration_call(mvs_rpc.get_accountasset, Alice.name, Alice.password) self.assertEqual(ret[0], 0, "mvs_rpc.get_accountasset failed!") @@ -28,7 +28,7 @@ def test_1_getaddressasset(self): avg_duration = 0.005 durations = [] - for i in xrange(round): + for i in range(round): duration, ret = common.duration_call(mvs_rpc.get_addressasset, Alice.mainaddress()) self.assertEqual(ret[0], 0, "mvs_rpc.get_addressasset failed!") self.assertLess(duration, max_duration) diff --git a/test/test-rpc-v3/TestCase/Asset/test_asset.py b/test/test-rpc-v3/TestCase/Asset/test_asset.py index 8fc393536..b38266a43 100644 --- a/test/test-rpc-v3/TestCase/Asset/test_asset.py +++ b/test/test-rpc-v3/TestCase/Asset/test_asset.py @@ -26,7 +26,7 @@ def test_2_issue_asset(self): Alice.mining() account_assets = Alice.get_accountasset() - found_assets = filter(lambda a: a.symbol == Alice.asset_symbol, account_assets) + found_assets = list( filter(lambda a: a.symbol == Alice.asset_symbol, account_assets) ) self.assertEqual(len(found_assets), 1) self.assertEqual(found_assets[0].symbol, Alice.asset_symbol) self.assertEqual(found_assets[0].issuer, Alice.did_symbol) @@ -42,7 +42,7 @@ def get_asset_amount(self, role, address=None): address_assets = role.get_addressasset(address) #we only consider Alice's Asset - found_assets = filter(lambda a: a.symbol == Alice.asset_symbol, address_assets) + found_assets = list( filter(lambda a: a.symbol == Alice.asset_symbol, address_assets) ) self.assertEqual(len(found_assets), 1) previous_quantity = found_assets[0].quantity @@ -103,5 +103,24 @@ def test_5_burn_asset(self): Alice.burn_asset(1) Alice.mining() addressassets = Alice.get_addressasset(Alice.didaddress()) - addressasset = filter(lambda a: a.symbol == Alice.asset_symbol, addressassets) + addressasset = list( filter(lambda a: a.symbol == Alice.asset_symbol, addressassets) ) self.assertEqual(len(addressasset), 0) + + def test_6_sendmoreasset(self): + Alice.create_asset() + Alice.mining() + + origin_amount = self.get_asset_amount(Alice) + + mvs_rpc.new_address(Zac.name, Zac.password, 2) + receivers={ + Zac.addresslist[0]:1000, + Zac.addresslist[1]:2000 + } + + #pre-set condition + self.assertGreater(origin_amount, 3000) + Alice.sendmore_asset(receivers, Alice.asset_symbol) + Alice.mining() + + [ self.assertEqual(self.get_asset_amount(Zac, k), v) for k,v in receivers.items() ] diff --git a/test/test-rpc-v3/TestCase/Asset/test_asset_boundary.py b/test/test-rpc-v3/TestCase/Asset/test_asset_boundary.py index aafe7a48e..77d66bb0e 100644 --- a/test/test-rpc-v3/TestCase/Asset/test_asset_boundary.py +++ b/test/test-rpc-v3/TestCase/Asset/test_asset_boundary.py @@ -246,7 +246,7 @@ def test_6_getaccountasset(self): def test_7_getaddressasset(self): # invalid address ec, message = mvs_rpc.get_addressasset(Zac.mainaddress() + '1') - self.assertEqual(ec, 4010, message) + self.assertEqual(ec, 7006, message) # no asset ec, message = mvs_rpc.get_addressasset(Zac.mainaddress()) diff --git a/test/test-rpc-v3/TestCase/Asset/test_transfer_multisig_cert.py b/test/test-rpc-v3/TestCase/Asset/test_transfer_multisig_cert.py index 67ca159dd..18d385807 100644 --- a/test/test-rpc-v3/TestCase/Asset/test_transfer_multisig_cert.py +++ b/test/test-rpc-v3/TestCase/Asset/test_transfer_multisig_cert.py @@ -44,7 +44,7 @@ def test_0_transfer_multisig_cert(self): # check cert certs = Zac.get_addressasset(multisig_address, True); self.assertGreater(len(certs), 0, "not cert found at " + multisig_address) - exist_symbols = filter(lambda a: a.symbol == asset_symbol and a.cert == "issue", certs) + exist_symbols = list( filter(lambda a: a.symbol == asset_symbol and a.cert == "issue", certs) ) self.assertEqual(len(exist_symbols), 1, "not cert found at " + multisig_address) # transfer cert to Cindy @@ -60,5 +60,5 @@ def test_0_transfer_multisig_cert(self): # check cert certs = Cindy.get_addressasset(Cindy.didaddress(), True); self.assertGreater(len(certs), 0, "not cert found at " + Cindy.didaddress()) - exist_symbols = filter(lambda a: a.symbol == asset_symbol and a.cert == "issue", certs) + exist_symbols = list( filter(lambda a: a.symbol == asset_symbol and a.cert == "issue", certs) ) self.assertEqual(len(exist_symbols), 1, "not cert found at " + Cindy.didaddress()) diff --git a/test/test-rpc-v3/TestCase/BitcoinScript/__init__.py b/test/test-rpc-v3/TestCase/BitcoinScript/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test/test-rpc-v3/TestCase/BitcoinScript/test_address.py b/test/test-rpc-v3/TestCase/BitcoinScript/test_address.py new file mode 100644 index 000000000..f36841e3d --- /dev/null +++ b/test/test-rpc-v3/TestCase/BitcoinScript/test_address.py @@ -0,0 +1,66 @@ +from TestCase.MVSTestCase import * + +hex_script= "63522102578ad340083e85c739f379bbe6c6937c5da2ced52e09ac1eec43dc4c64846573210380990a7312b87abda80e5857ee6ebf798a2bf62041b07111287d19926c429d1152ae670164b3752102578ad340083e85c739f379bbe6c6937c5da2ced52e09ac1eec43dc4c64846573ac68" +p2sh_address = "3443Q4tSg9s11kwYKdn1eCMx3H8UcZdgPE" + +class TestAddress(MVSTestCaseBase): + need_mine = False + def test_0_import_adress(self): + ec, message = mvs_rpc.import_address(Alice.name, "xxx", hex_script) + self.assertEqual(ec, 1000, message) + + ec, message = mvs_rpc.import_address(Alice.name, Alice.password, "xxx") + self.assertEqual(ec, 5502, message) + + ec, message = mvs_rpc.import_address(Alice.name, Alice.password, Zac.mainaddress()) + self.assertEqual(ec, 5502, message) + + def test_1_import_adress(self): + ec, message = mvs_rpc.import_address(Alice.name, Alice.password, p2sh_address) + self.assertEqual(ec, 0, message) + + def test_2_import_adress(self): + description = "test_2_import_adress" + ec, message = mvs_rpc.import_address(Alice.name, Alice.password, hex_script, description) + self.assertEqual(ec, 0, message) + self.assertEqual(message['address'], p2sh_address) + self.assertEqual(message['script'], 'if 2 [ 02578ad340083e85c739f379bbe6c6937c5da2ced52e09ac1eec43dc4c64846573 ] [ 0380990a7312b87abda80e5857ee6ebf798a2bf62041b07111287d19926c429d11 ] 2 checkmultisig else [ 64 ] checksequenceverify drop [ 02578ad340083e85c739f379bbe6c6937c5da2ced52e09ac1eec43dc4c64846573 ] checksig endif') + + + ec, message = mvs_rpc.dump_keyfile(Alice.name, Alice.password, Alice.lastword(), keyfile="any", to_report=True) + self.assertEqual(ec, 0, message) + + self.assertEqual(message['scripts'][0]['address'], p2sh_address) + self.assertEqual(message['scripts'][0]['script'], hex_script) + self.assertEqual(message['scripts'][0]['description'], description) + + def test_3_multisig_and_script(self): + description = "test_3_multisig_and_script" + ec, message = mvs_rpc.import_address(Alice.name, Alice.password, hex_script, description) + self.assertEqual(ec, 0, message) + Alice.new_multisigaddress(description, [Bob, Cindy], 2) + + ec, message = mvs_rpc.dump_keyfile(Alice.name, Alice.password, Alice.lastword(), keyfile="any", to_report=True) + self.assertEqual(ec, 0, message) + + self.assertEqual(len( message['scripts'] ), 1) + self.assertEqual(len(message['multisigs']), 1) + + def test_4_multisig_and_script(self): + ''' + change the order in test_3_multisig_and_script + :return: + ''' + description = "test_4_multisig_and_script" + + Alice.new_multisigaddress(description, [Bob, Cindy], 2) + + ec, message = mvs_rpc.import_address(Alice.name, Alice.password, hex_script, description) + self.assertEqual(ec, 0, message) + + ec, message = mvs_rpc.dump_keyfile(Alice.name, Alice.password, Alice.lastword(), keyfile="any", to_report=True) + self.assertEqual(ec, 0, message) + + self.assertEqual(len( message['scripts'] ), 1) + self.assertEqual(len(message['multisigs']), 1) + diff --git a/test/test-rpc-v3/TestCase/BitcoinScript/test_createrawtx.py b/test/test-rpc-v3/TestCase/BitcoinScript/test_createrawtx.py new file mode 100644 index 000000000..0b1517b10 --- /dev/null +++ b/test/test-rpc-v3/TestCase/BitcoinScript/test_createrawtx.py @@ -0,0 +1,24 @@ +from TestCase.MVSTestCase import * + + +class TestCreateRawTX(MVSTestCaseBase): + need_mine = False + def test_0_createrawtx(self): + create_rawtx = lambda utxos: mvs_rpc.create_rawtx(0, [], {Zac.mainaddress():10**8}, mychange=Alice.mainaddress(), utxos=utxos) + + utxos = [("32" * 32, 0, 0xFFFFFFFF)] + ec, message = create_rawtx(utxos) + self.assertNotEqual(ec, 0, message) + + #send to get a valid tx hash + txhash = Alice.send_etp(Zac.addresslist[1], 2* 10**8) + + ec, message = create_rawtx([(txhash, 10, 0xFFFFFFFF)]) + self.assertNotEqual(ec, 0, message) + + ec, message = create_rawtx([(txhash, 0, 0xFFFFFFFF)]) + self.assertEqual(ec, 0, message) + + + + diff --git a/test/test-rpc-v3/TestCase/BitcoinScript/test_script_AB_seq_lock.py b/test/test-rpc-v3/TestCase/BitcoinScript/test_script_AB_seq_lock.py new file mode 100644 index 000000000..314f88aaf --- /dev/null +++ b/test/test-rpc-v3/TestCase/BitcoinScript/test_script_AB_seq_lock.py @@ -0,0 +1,94 @@ +from TestCase.MVSTestCase import * +from utils import compile, set_rawtx +from binascii import b2a_hex, a2b_hex + +# Alice and Bob multisig, or Alice with 10 blocks +hex_script= "6352210380990a7312b87abda80e5857ee6ebf798a2bf62041b07111287d19926c429d112102578ad340083e85c739f379bbe6c6937c5da2ced52e09ac1eec43dc4c6484657352ae67040a000000b375210380990a7312b87abda80e5857ee6ebf798a2bf62041b07111287d19926c429d11ac68" +p2sh_address = "3B95d2Nm8ZBMiENnxkTHRaHE8RhUxhd9KK" + +multisig_unlock_branch = ''' +OP_0 +%(Alice)s +%(Bob)s +OP_1 +OP_PUSHDATA1 +%(redeem)s +''' + +alice_with_10_block_branch = ''' +%(Alice)s +OP_0 +OP_PUSHDATA1 +%(redeem)s +''' + +class TestScript(MVSTestCaseBase): + def test_0_spent_by_multisig(self): + txhash = Alice.send_etp(p2sh_address, 10**8) + Alice.mining() + + # nsequence not set -- spent by multisig branch + ec, message = mvs_rpc.create_rawtx(0, [], {Zac.mainaddress():5*10**7}, mychange=p2sh_address, utxos=[(txhash, 0, 0xFFFFFFFF)]) + self.assertEqual(ec, 0, message) + + rawtx = message + dict_sig = {} # name : edsig + for role in (Alice, Bob): + # import script + ec, message = mvs_rpc.import_address(role.name, role.password, hex_script, "test_0_spent_by_multisig") + self.assertEqual(ec, 0, message) + self.assertEqual(message['address'], p2sh_address) + + ec, message = mvs_rpc.sign_rawtx(role.name, role.password, rawtx) + self.assertEqual(ec, 0, message) + + edsig = message["0"][role.get_publickey(role.mainaddress())] + dict_sig[role.name] = edsig + + unlock_script_para = {'redeem':hex_script} + unlock_script_para.update(dict_sig) + unlock_script = multisig_unlock_branch % unlock_script_para + hex_unlock_script = compile.script_to_hex(unlock_script) + + tx = set_rawtx.Transaction.parse_rawtx(rawtx) + tx.inputs[0].script = a2b_hex(hex_unlock_script) + rawtx = tx.to_rawtx() + ec, message = mvs_rpc.send_rawtx(rawtx) + self.assertEqual(ec, 0, message) + + # ------------------ devide line ----------------------------------- + Alice.mining() + + output_txhash = message + # spent by sequence lock branch + ec, message = mvs_rpc.create_rawtx(0, [], {Zac.mainaddress(): 25 * 10 ** 6}, + mychange=p2sh_address, utxos=[(output_txhash, 1, 10)]) + self.assertEqual(ec, 0, message) + + rawtx = message + dict_sig = {} # name : edsig + for role in (Alice, ): + ec, message = mvs_rpc.sign_rawtx(role.name, role.password, rawtx) + self.assertEqual(ec, 0, message) + + edsig = message["0"][role.get_publickey(role.mainaddress())] + dict_sig[role.name] = edsig + + unlock_script_para = {'redeem': hex_script} + unlock_script_para.update(dict_sig) + unlock_script = alice_with_10_block_branch % unlock_script_para + hex_unlock_script = compile.script_to_hex(unlock_script) + + tx = set_rawtx.Transaction.parse_rawtx(rawtx) + tx.inputs[0].script = a2b_hex(hex_unlock_script) + rawtx = tx.to_rawtx() + ec, message = mvs_rpc.send_rawtx(rawtx) + self.assertEqual(ec, 5304, message) # locked, need mine 10 blocks + + Alice.mining(8) + ec, message = mvs_rpc.send_rawtx(rawtx) + self.assertEqual(ec, 5304, message) + + Alice.mining(1) + ec, message = mvs_rpc.send_rawtx(rawtx) + self.assertEqual(ec, 0, message) diff --git a/test/test-rpc-v3/TestCase/BitcoinScript/test_script_chklocktime.py b/test/test-rpc-v3/TestCase/BitcoinScript/test_script_chklocktime.py new file mode 100644 index 000000000..7b566355e --- /dev/null +++ b/test/test-rpc-v3/TestCase/BitcoinScript/test_script_chklocktime.py @@ -0,0 +1,62 @@ +from TestCase.MVSTestCase import * +from utils import compile, set_rawtx +from binascii import b2a_hex, a2b_hex +import struct + +redeem_script = ''' +%(Height)s OP_CHECKLOCKTIMEVERIFY OP_DROP %(Alice)s OP_CHECKSIG +''' + +spent_script = ''' +%(SIG)s +OP_PUSHDATA1 +%(redeem)s +''' + +from TestCase.MVSTestCase import * + +class TestChkLocktime(MVSTestCaseBase): + need_mine = False + def test_0_ChkLocktime(self): + ec, (height, _) = mvs_rpc.get_info() + self.assertEqual(ec, 0) + + ec, pk = mvs_rpc.get_publickey(Alice.name, Alice.password, Alice.mainaddress()) + self.assertEqual(ec, 0) + + lk_height = height + 10 + + ht = struct.pack(" ok + # sequence = 12 -> nok + for sequence, expect_ec in [(height - prev_height + 2, 5304), (height - prev_height + 1, 0)]: + t.inputs[0].sequence = sequence + raw_tx = t.to_rawtx() + ec, message = mvs_rpc.sign_rawtx(Alice.name, Alice.password, raw_tx) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.send_rawtx(message['rawtx']) + self.assertEqual(ec, expect_ec, message) + + def test_1_enter_txpool_by_height_2sequence(self): + rawtx = prepare_rawtx() + t = Transaction.parse_rawtx(rawtx) + + self.assertGreater(len(t.inputs), 1, 'expect more than 2 inputs') + + ec, message = mvs_rpc.get_info() + self.assertEqual(ec, 0, message) + height = message[0] + + ec, message = mvs_rpc.gettx( t.inputs[0].utxo_hash[::-1].hex() ) + self.assertEqual(ec, 0, message) + prev_height_0 = message['height'] + sequence_0 = height - prev_height_0 + 1 + + ec, message = mvs_rpc.gettx( t.inputs[1].utxo_hash[::-1].hex() ) + self.assertEqual(ec, 0, message) + prev_height_1 = message['height'] + sequence_1 = height - prev_height_1 + 1 + + + for a0, a1, expect in [(0, 1, 5304), (1, 0, 5304), (0, 0, 0)]: + t.inputs[0].sequence = sequence_0 + a0 + t.inputs[1].sequence = sequence_1 + a1 + raw_tx = t.to_rawtx() + ec, message = mvs_rpc.sign_rawtx(Alice.name, Alice.password, raw_tx) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.send_rawtx(message['rawtx']) + self.assertEqual(ec, expect, message) + + def get_media_past_time(self, height): + ret = [] + for i in range(media_block_count): + ec, message = mvs_rpc.get_blockheader(height=height - i) + self.assertEqual(ec, 0, message) + ret.append( message['timestamp'] ) + ret.sort() + return ret[ int(len(ret)/2) ] + + def test_2_enter_txpool_by_time(self): + rawtx = prepare_rawtx() + t = Transaction.parse_rawtx(rawtx) + + ec, message = mvs_rpc.gettx( t.inputs[0].utxo_hash[::-1].hex() ) + self.assertEqual(ec, 0, message) + prev_height = message['height'] + + ec, message = mvs_rpc.get_blockheader(height=prev_height) + self.assertEqual(ec, 0, message) + prev_time = message['timestamp'] + + ec, message = mvs_rpc.get_info() + self.assertEqual(ec, 0, message) + height = message[0] + + curr_time = self.get_media_past_time(height) + sequence = (curr_time - prev_time) / locktime_unit + if sequence < 1: + import time + time.sleep(locktime_unit) + Alice.mining(media_block_count) + curr_time = self.get_media_past_time(height + media_block_count) + sequence = (curr_time - prev_time) / locktime_unit + sequence = int(sequence) + # eg: prev_height=1000, height=1010 + # sequence = 11 -> ok + # sequence = 12 -> nok + for sequence_, expect_ec in [(sequence + 1, 5304), (sequence, 0)]: + t.inputs[0].sequence = ((1 << 22) | sequence_) + raw_tx = t.to_rawtx() + ec, message = mvs_rpc.sign_rawtx(Alice.name, Alice.password, raw_tx) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.send_rawtx(message['rawtx']) + self.assertEqual(ec, expect_ec, message) + +class TestTxLocktime(MVSTestCaseBase): + def test_0_enter_txpool_by_height(self): + rawtx = prepare_rawtx() + t = Transaction.parse_rawtx(rawtx) + for i in range(len(t.inputs)): + t.inputs[i].sequence = (1 << 31) + + ec, message = mvs_rpc.get_info() + self.assertEqual(ec, 0, message) + height = message[0] + + for i,e in [(2, 5304), (1, 5304), (0, 0)]: + t.locktime = height + i + raw_tx = t.to_rawtx() + ec, message = mvs_rpc.sign_rawtx(Alice.name, Alice.password, raw_tx) + self.assertEqual(ec, 0, message) + ec, message = mvs_rpc.send_rawtx(message['rawtx']) + self.assertEqual(ec, e, message) \ No newline at end of file diff --git a/test/test-rpc-v3/TestCase/Transaction/test_transaction.py b/test/test-rpc-v3/TestCase/Transaction/test_transaction.py index 91f0367b7..aa92d1d14 100644 --- a/test/test-rpc-v3/TestCase/Transaction/test_transaction.py +++ b/test/test-rpc-v3/TestCase/Transaction/test_transaction.py @@ -38,7 +38,7 @@ def test_2_listtxs(self): # invalid address parameter! ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, Alice.mainaddress()+'1') - self.assertEqual(ec, 4010, message) + self.assertEqual(ec, 7006, message) ec, message = mvs_rpc.get_blockheader() self.assertEqual(ec, 0, message) diff --git a/test/test-rpc-v3/install.sh b/test/test-rpc-v3/install.sh new file mode 100644 index 000000000..0c8a19ebd --- /dev/null +++ b/test/test-rpc-v3/install.sh @@ -0,0 +1,8 @@ +sudo add-apt-repository ppa:jonathonf/python-3.6 +sudo apt-get update +sudo apt-get install python3.6 +curl https://bootstrap.pypa.io/get-pip.py | sudo python3.6 +sudo apt-get install python3.6-dev + +sudo python3.6 -m pip install -r requirements.txt +sudo python3.6 -m pip install pyethereum \ No newline at end of file diff --git a/test/test-rpc-v3/main.py b/test/test-rpc-v3/main.py index 66e42741e..0de0f28b6 100644 --- a/test/test-rpc-v3/main.py +++ b/test/test-rpc-v3/main.py @@ -118,7 +118,7 @@ def fork_test( ): f_origin.close() f_popback.close() - print "fork test passed!" + print( "fork test passed!") @@ -128,9 +128,9 @@ def fork_test( ): clear_account() ensure_Alice_balance() if len(sys.argv) >= 2 and sys.argv[1] == "fork": - print 'backup check point -> run testcase -> popblock -> check database ' + print('backup check point -> run testcase -> popblock -> check database ') fork_test() else: - print 'run testcase' + print('run testcase') run_testcase() diff --git a/test/test-rpc-v3/tools/data_base.py b/test/test-rpc-v3/tools/data_base.py index 763a56916..d275dcfac 100644 --- a/test/test-rpc-v3/tools/data_base.py +++ b/test/test-rpc-v3/tools/data_base.py @@ -72,7 +72,7 @@ def parse_header(self, f, need_bucket=True): invalid_offset = '\xFF' * self.size_of_offset bucket_array = [] if need_bucket: - for i in xrange(self.bucket_size): + for i in range(self.bucket_size): offset_str = f.read(self.size_of_offset) if offset_str == invalid_offset: continue @@ -549,7 +549,7 @@ def parse_value_func(cls, ff): assert (version < 5) input_size = get_var_len(ff, extra_padding.append) assert (input_size < 700) # max input ~ 667 ? - for i in xrange(input_size): + for i in range(input_size): #previous_output utxo_hash_ = ff.read(32) utxo_index = str2int( ff.read(4) ) @@ -563,7 +563,7 @@ def parse_value_func(cls, ff): output_size = get_var_len(ff, extra_padding.append) assert (output_size < 70) - for i in xrange(output_size): + for i in range(output_size): amount = str2int( ff.read(8) ) script_len = get_var_len(ff, extra_padding.append) assert (script_len < 256) @@ -615,7 +615,7 @@ def parse_value_func(cls, ff): tx_count32 = str2int( ff.read(4) ) # transactions - for i in xrange(tx_count32): + for i in range(tx_count32): tx_hash = ff.read(32) end = ff.tell() diff --git a/test/test-rpc-v3/utils/bitcoin_script.py b/test/test-rpc-v3/utils/bitcoin_script.py new file mode 100644 index 000000000..44905261c --- /dev/null +++ b/test/test-rpc-v3/utils/bitcoin_script.py @@ -0,0 +1,47 @@ +from pybitcoin.transactions.scripts import script_to_hex +from pybitcoin.hash import bin_double_sha256, bin_hash160 +from pybitcoin.address import bin_hash160_to_address +template = ''' +OP_IF + OP_2 %(Alice)s %(Bob)s OP_2 OP_CHECKMULTISIG +OP_ELSE + %(Sequence)s OP_CHECKSEQUENCEVERIFY OP_DROP + %(Alice)s OP_CHECKSIG +OP_ENDIF +''' + +contract = template % {'Bob' : '02578ad340083e85c739f379bbe6c6937c5da2ced52e09ac1eec43dc4c64846573', + 'Alice' : '0380990a7312b87abda80e5857ee6ebf798a2bf62041b07111287d19926c429d11', + 'Sequence' : '0a000000'} + +template2 = ''' +OP_0 +3045022100ff054d83e4f376b6b47705b8186fd1e2b61cabe70e717f052b6bf0fd00d883ec02203adaf168c7e4b32fbd66dd2adfdd42aaf6268f5e4c736978ab6c86d4e13bfcf401 +304402200eab2db325b0c95dcfed00a4554b59d3422d2eef3eed50a341da55cd83e8e06302203fc97b96df2e803dfc3113cc6ee0dd5728ced316b63dfda72c808ab48826f7e601 +OP_1 +OP_PUSHDATA1 +63522102578ad340083e85c739f379bbe6c6937c5da2ced52e09ac1eec43dc4c64846573210380990a7312b87abda80e5857ee6ebf798a2bf62041b07111287d19926c429d1152ae670164b3752102578ad340083e85c739f379bbe6c6937c5da2ced52e09ac1eec43dc4c64846573ac68 +''' + +template3 = ''' +304402204d21c19216cad74e780bd70e04518cf8f1a20108dc3bf79f7b218865524661ac022049b5de8a05d9b524ae6de3b4b221c856d16d4e3a51f7f19e685e7fc33b51abac01 +OP_1 +OP_PUSHDATA1 +6352210380990a7312b87abda80e5857ee6ebf798a2bf62041b07111287d19926c429d112102578ad340083e85c739f379bbe6c6937c5da2ced52e09ac1eec43dc4c6484657352ae67040a000000b375210380990a7312b87abda80e5857ee6ebf798a2bf62041b07111287d19926c429d11ac68 +''' + + +def compile(ct): + ct = ct.split() + #for i in ct: + # print i, '->', script_to_hex(i) + return script_to_hex(' '.join(ct)) + +if __name__ == '__main__': + script = compile(contract) + script_hash = bin_hash160(script, hex_format=True) + p2sh = bin_hash160_to_address(script_hash, 5) + print (p2sh) + print (script) + print ('-' * 80) + print (compile(template3)) \ No newline at end of file diff --git a/test/test-rpc-v3/utils/common.py b/test/test-rpc-v3/utils/common.py index 99d5372b9..43f444e96 100644 --- a/test/test-rpc-v3/utils/common.py +++ b/test/test-rpc-v3/utils/common.py @@ -48,7 +48,7 @@ def remove_file(file_path): def toHex(s): if s[:2] == '0x': s = s[2:] - return ''.join( [chr(int(s[i:i + 2], 16)) for i in xrange(0, len(s), 2)] ) + return ''.join( [chr(int(s[i:i + 2], 16)) for i in range(0, len(s), 2)] ) def toString(h): return ''.join(['%02x' % ord(i) for i in h]) diff --git a/test/test-rpc-v3/utils/compile.py b/test/test-rpc-v3/utils/compile.py new file mode 100644 index 000000000..9dbaca310 --- /dev/null +++ b/test/test-rpc-v3/utils/compile.py @@ -0,0 +1,153 @@ +OP_0 = 0 +OP_PUSHDATA1 = 76 # 0x4c +OP_PUSHDATA2 = 77 # 0x4d +OP_PUSHDATA4 = 78 # 0x4e +OP_1NEGATE = 79 +OP_RESERVED = 80 +OP_1 = 81 +OP_2 = 82 +OP_3 = 83 +OP_4 = 84 +OP_5 = 85 +OP_6 = 86 +OP_7 = 87 +OP_8 = 88 +OP_9 = 89 +OP_10 = 90 +OP_11 = 91 +OP_12 = 92 +OP_13 = 93 +OP_14 = 94 +OP_15 = 95 +OP_16 = 96 +OP_NOP = 97 +OP_VER = 98 +OP_IF = 99 +OP_NOTIF = 100 +OP_VERIF = 101 +OP_VERNOTIF = 102 +OP_ELSE = 103 +OP_ENDIF = 104 +OP_VERIFY = 105 # 0x69 +OP_RETURN = 106 # 0x6a +OP_TOALTSTACK = 107 +OP_FROMALTSTACK = 108 +OP_2DROP = 109 +OP_2DUP = 110 +OP_3DUP = 111 +OP_2OVER = 112 +OP_2ROT = 113 +OP_2SWAP = 114 +OP_IFDUP = 115 +OP_DEPTH = 116 +OP_DROP = 117 +OP_DUP = 118 +OP_NIP = 119 +OP_OVER = 120 +OP_PICK = 121 +OP_ROLL = 122 +OP_ROT = 123 +OP_SWAP = 124 +OP_TUCK = 125 +OP_CAT = 126 +OP_SUBSTR = 127 +OP_LEFT = 128 +OP_RIGHT = 129 +OP_SIZE = 130 +OP_INVERT = 131 +OP_AND = 132 +OP_OR = 133 +OP_XOR = 134 +OP_EQUAL = 135 # 0x87 +OP_EQUALVERIFY = 136 # 0x88 +OP_RESERVED1 = 137 +OP_RESERVED2 = 138 +OP_1ADD = 139 +OP_1SUB = 140 +OP_2MUL = 141 +OP_2DIV = 142 +OP_NEGATE = 143 +OP_ABS = 144 +OP_NOT = 145 +OP_0NOTEQUAL = 146 +OP_ADD = 147 +OP_SUB = 148 +OP_MUL = 149 +OP_DIV = 150 +OP_MOD = 151 +OP_LSHIFT = 152 +OP_RSHIFT = 153 +OP_BOOLAND = 154 +OP_BOOLOR = 155 +OP_NUMEQUAL = 156 +OP_NUMEQUALVERIFY = 157 +OP_NUMNOTEQUAL = 158 +OP_LESSTHAN = 159 +OP_GREATERTHAN = 160 +OP_LESSTHANOREQUAL = 161 +OP_GREATERTHANOREQUAL = 162 +OP_MIN = 163 +OP_MAX = 164 +OP_WITHIN = 165 +OP_RIPEMD160 = 166 +OP_SHA1 = 167 +OP_SHA256 = 168 +OP_HASH160 = 169 # 0xa9 +OP_HASH256 = 170 +OP_CODESEPARATOR = 171 +OP_CHECKSIG = 172 # 0xac +OP_CHECKSIGVERIFY = 173 # 0xad +OP_CHECKMULTISIG = 174 # 0xae +OP_CHECKMULTISIGVERIFY = 175 # 0xaf +OP_NOP1 = 176 +OP_NOP2 = 177 +OP_NOP3 = 178 +OP_NOP4 = 179 +OP_CHECKLOCKTIMEVERIFY = OP_NOP2 +OP_CHECKATTENUATIONVERIFY = OP_NOP3 +OP_CHECKSEQUENCEVERIFY = OP_NOP4 +OP_NOP5 = 180 +OP_NOP6 = 181 +OP_NOP7 = 182 +OP_NOP8 = 183 +OP_NOP9 = 184 +OP_NOP10 = 185 +OP_PUBKEYHASH = 253 # 0xfd +OP_PUBKEY = 254 # 0xfe +OP_INVALIDOPCODE = 255 + +def is_hex(s): + if type(s) != str: + return False + if len(s) % 2 != 0: + return False + for i in s: + if not ('0' <= i <= '9' or 'a' <= i <= 'f'): + return False + return True + +def count_bytes(hex_s): + """ Calculate the number of bytes of a given hex string. + """ + assert(is_hex(hex_s)) + return int( len(hex_s)/2 ) + +def script_to_hex(script): + """ Parse the string representation of a script and return the hex version. + Example: "OP_DUP OP_HASH160 c629...a6db OP_EQUALVERIFY OP_CHECKSIG" + """ + hex_script = '' + parts = filter(None, script.split()) + for part in parts: + if part[0:3] == 'OP_': + try: + hex_script += '%0.2x' % eval(part) + except: + raise Exception('Invalid opcode: %s' % part) + elif isinstance(part, (int)): + hex_script += '%0.2x' % part + elif is_hex(part): + hex_script += '%0.2x' % count_bytes(part) + part + else: + raise Exception('Invalid script - only opcodes and hex characters allowed.') + return hex_script \ No newline at end of file diff --git a/test/test-rpc-v3/utils/cryptojs.py b/test/test-rpc-v3/utils/cryptojs.py index 7592438cd..29b838c5c 100644 --- a/test/test-rpc-v3/utils/cryptojs.py +++ b/test/test-rpc-v3/utils/cryptojs.py @@ -17,8 +17,8 @@ def derive_key(passphrase, salt): iv_size = 16 iterations = 1 - derivedKeyWords = '' - block = '' + derivedKeyWords = b'' + block = b'' while len(derivedKeyWords) < (key_size + iv_size): hash = MD5.new() @@ -43,13 +43,14 @@ def AES_CBC_encrypt(message, key, iv): def AES_CBC_decrypt(cipher_txt, passphrase): + passphrase = bytes(passphrase, 'utf-8') s = base64.b64decode(cipher_txt) salt = s[8:16] raw_cipher_txt = s[16:] key, iv = derive_key(passphrase, salt) cipher = AES.new(key, AES.MODE_CBC, iv) msg = cipher.decrypt(raw_cipher_txt) - padding = ord(msg[-1]) - return msg[:-padding] + padding = msg[-1] + return str(msg[:-padding], 'utf-8') diff --git a/test/test-rpc-v3/utils/dump_key.py b/test/test-rpc-v3/utils/dump_key.py new file mode 100644 index 000000000..9a1a4c604 --- /dev/null +++ b/test/test-rpc-v3/utils/dump_key.py @@ -0,0 +1,264 @@ +from Crypto.Cipher import AES +import base64 +import hashlib + +def ripemd160_hash(data): + obj = hashlib.new('ripemd160', data) + return obj.digest(), obj.hexdigest() + +def sha256_hash(data): + return hashlib.sha256(data).digest() + +import struct +import zlib +import os, sys +str2int_map = { + 1: ' 0xFFFFFFFF: + if i.previous_output.index != 0xFFFFFFFF: i.address = json_report["address"] return i diff --git a/test/test-rpc/TestCase/Asset/test_asset_boundary.py b/test/test-rpc/TestCase/Asset/test_asset_boundary.py index a53449d84..3925a6cbc 100644 --- a/test/test-rpc/TestCase/Asset/test_asset_boundary.py +++ b/test/test-rpc/TestCase/Asset/test_asset_boundary.py @@ -206,7 +206,7 @@ def test_6_getaccountasset(self): def test_7_getaddressasset(self): # invalid address ec, message = mvs_rpc.get_addressasset(Zac.mainaddress()+'1') - self.assertEqual(ec, 4010, message) + self.assertEqual(ec, 7006, message) # no asset ec, message = mvs_rpc.get_addressasset(Zac.mainaddress()) diff --git a/test/test-rpc/TestCase/RawTx/test_rawtx.py b/test/test-rpc/TestCase/RawTx/test_rawtx.py index 7e02bbcc3..477b9bcdd 100644 --- a/test/test-rpc/TestCase/RawTx/test_rawtx.py +++ b/test/test-rpc/TestCase/RawTx/test_rawtx.py @@ -14,14 +14,14 @@ def test_0_create_rawtx_boundary(self): # invalid senders address ec, message = mvs_rpc.create_rawtx(0, {Alice.mainaddress() + "1"}, single_receiver,); - self.assertEqual(ec, code.fromaddress_invalid_exception, message) + self.assertEqual(ec, code.did_symbol_notfound_exception, message) # invalid receiver address ec, message = mvs_rpc.create_rawtx(0, {Alice.mainaddress()}, {Bob.mainaddress()+"1" : 10}, mychange=Alice.mainaddress()); - self.assertEqual(ec, code.toaddress_invalid_exception, message) + self.assertEqual(ec, code.did_symbol_notfound_exception, message) # invalid receiver amount ec, message = mvs_rpc.create_rawtx(0, @@ -32,7 +32,7 @@ def test_0_create_rawtx_boundary(self): # invalid mychange address ec, message = mvs_rpc.create_rawtx(0, senders, single_receiver, mychange=Alice.mainaddress()+"1"); - self.assertEqual(ec, code.toaddress_invalid_exception, message) + self.assertEqual(ec, code.did_symbol_notfound_exception, message) # invalid symbol for deposit etp ec, message = mvs_rpc.create_rawtx(1, senders, single_receiver, symbol="ABC"); diff --git a/test/test-rpc/TestCase/Transaction/test_transaction.py b/test/test-rpc/TestCase/Transaction/test_transaction.py index 91f0367b7..aa92d1d14 100644 --- a/test/test-rpc/TestCase/Transaction/test_transaction.py +++ b/test/test-rpc/TestCase/Transaction/test_transaction.py @@ -38,7 +38,7 @@ def test_2_listtxs(self): # invalid address parameter! ec, message = mvs_rpc.listtxs(Alice.name, Alice.password, Alice.mainaddress()+'1') - self.assertEqual(ec, 4010, message) + self.assertEqual(ec, 7006, message) ec, message = mvs_rpc.get_blockheader() self.assertEqual(ec, 0, message) diff --git a/test/test-rpc/main.py b/test/test-rpc/main.py index 66e42741e..30c445459 100644 --- a/test/test-rpc/main.py +++ b/test/test-rpc/main.py @@ -118,19 +118,16 @@ def fork_test( ): f_origin.close() f_popback.close() - print "fork test passed!" - - - + print("fork test passed!") if __name__ == '__main__': clear_account() ensure_Alice_balance() if len(sys.argv) >= 2 and sys.argv[1] == "fork": - print 'backup check point -> run testcase -> popblock -> check database ' + print('backup check point -> run testcase -> popblock -> check database ') fork_test() else: - print 'run testcase' + print('run testcase') run_testcase() diff --git a/test/test-rpc/utils/mvs_rpc.py b/test/test-rpc/utils/mvs_rpc.py index 0af08ddf6..290486ec5 100644 --- a/test/test-rpc/utils/mvs_rpc.py +++ b/test/test-rpc/utils/mvs_rpc.py @@ -54,7 +54,7 @@ def post(self, positional, optional): result = rpc_rsp.json()['result'] if result != None and isinstance(result, dict): if self.method not in self.method_keys: - keys = result.keys() + keys = list(result.keys()) keys.sort() self.method_keys[self.method] = keys @@ -731,5 +731,5 @@ def pop_block(height): if __name__ == "__main__": rc = RemoteCtrl("10.10.10.35") - print rc.list_balances('lxf', '123') - print list_balances('lxf', '123') + print(rc.list_balances('lxf', '123')) + print(list_balances('lxf', '123'))