Merge branch 'develop' of https://github.com/peerplays-network/peerplays into develop
This commit is contained in:
commit
7121da7eb5
126 changed files with 14822 additions and 535 deletions
127
.clang-format
Normal file
127
.clang-format
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -3
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignConsecutiveMacros: false
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortLambdasOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: false
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: AfterColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: false
|
||||
ColumnLimit: 0
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: true
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 6
|
||||
ContinuationIndentWidth: 6
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 3
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 3
|
||||
UseTab: Never
|
||||
...
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -11,6 +11,7 @@ moc_*
|
|||
hardfork.hpp
|
||||
build_xc
|
||||
data
|
||||
CMakeDoxyfile.in
|
||||
|
||||
build
|
||||
|
||||
|
|
|
|||
|
|
@ -12,26 +12,30 @@ stages:
|
|||
build:
|
||||
stage: build
|
||||
script:
|
||||
- rm -rf .git/modules/* ./docs ./libraries/fc
|
||||
- git submodule sync
|
||||
- git submodule update --init --recursive
|
||||
- cmake .
|
||||
- rm -rf build
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake ..
|
||||
- make -j$(nproc)
|
||||
artifacts:
|
||||
untracked: true
|
||||
paths:
|
||||
- libraries/
|
||||
- programs/
|
||||
- tests/
|
||||
- build/libraries/
|
||||
- build/programs/
|
||||
- build/tests/
|
||||
tags:
|
||||
- builder
|
||||
|
||||
|
||||
test:
|
||||
stage: test
|
||||
dependencies:
|
||||
dependencies:
|
||||
- build
|
||||
script:
|
||||
- ./tests/betting_test
|
||||
- ./tests/chain_test
|
||||
- ./tests/cli_test
|
||||
- ./build/tests/betting_test --log_level=message
|
||||
- ./build/tests/chain_test --log_level=message
|
||||
- ./build/tests/cli_test --log_level=message
|
||||
tags:
|
||||
- builder
|
||||
|
|
|
|||
279
CMakeDoxyfile.in
Normal file
279
CMakeDoxyfile.in
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
#
|
||||
# DO NOT EDIT! THIS FILE WAS GENERATED BY CMAKE!
|
||||
#
|
||||
|
||||
DOXYFILE_ENCODING = @DOXYGEN_DOXYFILE_ENCODING@
|
||||
PROJECT_NAME = @DOXYGEN_PROJECT_NAME@
|
||||
PROJECT_NUMBER = @DOXYGEN_PROJECT_NUMBER@
|
||||
PROJECT_BRIEF = @DOXYGEN_PROJECT_BRIEF@
|
||||
PROJECT_LOGO = @DOXYGEN_PROJECT_LOGO@
|
||||
OUTPUT_DIRECTORY = @DOXYGEN_OUTPUT_DIRECTORY@
|
||||
CREATE_SUBDIRS = @DOXYGEN_CREATE_SUBDIRS@
|
||||
ALLOW_UNICODE_NAMES = @DOXYGEN_ALLOW_UNICODE_NAMES@
|
||||
OUTPUT_LANGUAGE = @DOXYGEN_OUTPUT_LANGUAGE@
|
||||
OUTPUT_TEXT_DIRECTION = @DOXYGEN_OUTPUT_TEXT_DIRECTION@
|
||||
BRIEF_MEMBER_DESC = @DOXYGEN_BRIEF_MEMBER_DESC@
|
||||
REPEAT_BRIEF = @DOXYGEN_REPEAT_BRIEF@
|
||||
ABBREVIATE_BRIEF = @DOXYGEN_ABBREVIATE_BRIEF@
|
||||
ALWAYS_DETAILED_SEC = @DOXYGEN_ALWAYS_DETAILED_SEC@
|
||||
INLINE_INHERITED_MEMB = @DOXYGEN_INLINE_INHERITED_MEMB@
|
||||
FULL_PATH_NAMES = @DOXYGEN_FULL_PATH_NAMES@
|
||||
STRIP_FROM_PATH = @DOXYGEN_STRIP_FROM_PATH@
|
||||
STRIP_FROM_INC_PATH = @DOXYGEN_STRIP_FROM_INC_PATH@
|
||||
SHORT_NAMES = @DOXYGEN_SHORT_NAMES@
|
||||
JAVADOC_AUTOBRIEF = @DOXYGEN_JAVADOC_AUTOBRIEF@
|
||||
JAVADOC_BANNER = @DOXYGEN_JAVADOC_BANNER@
|
||||
QT_AUTOBRIEF = @DOXYGEN_QT_AUTOBRIEF@
|
||||
MULTILINE_CPP_IS_BRIEF = @DOXYGEN_MULTILINE_CPP_IS_BRIEF@
|
||||
INHERIT_DOCS = @DOXYGEN_INHERIT_DOCS@
|
||||
SEPARATE_MEMBER_PAGES = @DOXYGEN_SEPARATE_MEMBER_PAGES@
|
||||
TAB_SIZE = @DOXYGEN_TAB_SIZE@
|
||||
ALIASES = @DOXYGEN_ALIASES@
|
||||
TCL_SUBST = @DOXYGEN_TCL_SUBST@
|
||||
OPTIMIZE_OUTPUT_FOR_C = @DOXYGEN_OPTIMIZE_OUTPUT_FOR_C@
|
||||
OPTIMIZE_OUTPUT_JAVA = @DOXYGEN_OPTIMIZE_OUTPUT_JAVA@
|
||||
OPTIMIZE_FOR_FORTRAN = @DOXYGEN_OPTIMIZE_FOR_FORTRAN@
|
||||
OPTIMIZE_OUTPUT_VHDL = @DOXYGEN_OPTIMIZE_OUTPUT_VHDL@
|
||||
OPTIMIZE_OUTPUT_SLICE = @DOXYGEN_OPTIMIZE_OUTPUT_SLICE@
|
||||
EXTENSION_MAPPING = @DOXYGEN_EXTENSION_MAPPING@
|
||||
MARKDOWN_SUPPORT = @DOXYGEN_MARKDOWN_SUPPORT@
|
||||
TOC_INCLUDE_HEADINGS = @DOXYGEN_TOC_INCLUDE_HEADINGS@
|
||||
AUTOLINK_SUPPORT = @DOXYGEN_AUTOLINK_SUPPORT@
|
||||
BUILTIN_STL_SUPPORT = @DOXYGEN_BUILTIN_STL_SUPPORT@
|
||||
CPP_CLI_SUPPORT = @DOXYGEN_CPP_CLI_SUPPORT@
|
||||
SIP_SUPPORT = @DOXYGEN_SIP_SUPPORT@
|
||||
IDL_PROPERTY_SUPPORT = @DOXYGEN_IDL_PROPERTY_SUPPORT@
|
||||
DISTRIBUTE_GROUP_DOC = @DOXYGEN_DISTRIBUTE_GROUP_DOC@
|
||||
GROUP_NESTED_COMPOUNDS = @DOXYGEN_GROUP_NESTED_COMPOUNDS@
|
||||
SUBGROUPING = @DOXYGEN_SUBGROUPING@
|
||||
INLINE_GROUPED_CLASSES = @DOXYGEN_INLINE_GROUPED_CLASSES@
|
||||
INLINE_SIMPLE_STRUCTS = @DOXYGEN_INLINE_SIMPLE_STRUCTS@
|
||||
TYPEDEF_HIDES_STRUCT = @DOXYGEN_TYPEDEF_HIDES_STRUCT@
|
||||
LOOKUP_CACHE_SIZE = @DOXYGEN_LOOKUP_CACHE_SIZE@
|
||||
EXTRACT_ALL = @DOXYGEN_EXTRACT_ALL@
|
||||
EXTRACT_PRIVATE = @DOXYGEN_EXTRACT_PRIVATE@
|
||||
EXTRACT_PRIV_VIRTUAL = @DOXYGEN_EXTRACT_PRIV_VIRTUAL@
|
||||
EXTRACT_PACKAGE = @DOXYGEN_EXTRACT_PACKAGE@
|
||||
EXTRACT_STATIC = @DOXYGEN_EXTRACT_STATIC@
|
||||
EXTRACT_LOCAL_CLASSES = @DOXYGEN_EXTRACT_LOCAL_CLASSES@
|
||||
EXTRACT_LOCAL_METHODS = @DOXYGEN_EXTRACT_LOCAL_METHODS@
|
||||
EXTRACT_ANON_NSPACES = @DOXYGEN_EXTRACT_ANON_NSPACES@
|
||||
HIDE_UNDOC_MEMBERS = @DOXYGEN_HIDE_UNDOC_MEMBERS@
|
||||
HIDE_UNDOC_CLASSES = @DOXYGEN_HIDE_UNDOC_CLASSES@
|
||||
HIDE_FRIEND_COMPOUNDS = @DOXYGEN_HIDE_FRIEND_COMPOUNDS@
|
||||
HIDE_IN_BODY_DOCS = @DOXYGEN_HIDE_IN_BODY_DOCS@
|
||||
INTERNAL_DOCS = @DOXYGEN_INTERNAL_DOCS@
|
||||
CASE_SENSE_NAMES = @DOXYGEN_CASE_SENSE_NAMES@
|
||||
HIDE_SCOPE_NAMES = @DOXYGEN_HIDE_SCOPE_NAMES@
|
||||
HIDE_COMPOUND_REFERENCE= @DOXYGEN_HIDE_COMPOUND_REFERENCE@
|
||||
SHOW_INCLUDE_FILES = @DOXYGEN_SHOW_INCLUDE_FILES@
|
||||
SHOW_GROUPED_MEMB_INC = @DOXYGEN_SHOW_GROUPED_MEMB_INC@
|
||||
FORCE_LOCAL_INCLUDES = @DOXYGEN_FORCE_LOCAL_INCLUDES@
|
||||
INLINE_INFO = @DOXYGEN_INLINE_INFO@
|
||||
SORT_MEMBER_DOCS = @DOXYGEN_SORT_MEMBER_DOCS@
|
||||
SORT_BRIEF_DOCS = @DOXYGEN_SORT_BRIEF_DOCS@
|
||||
SORT_MEMBERS_CTORS_1ST = @DOXYGEN_SORT_MEMBERS_CTORS_1ST@
|
||||
SORT_GROUP_NAMES = @DOXYGEN_SORT_GROUP_NAMES@
|
||||
SORT_BY_SCOPE_NAME = @DOXYGEN_SORT_BY_SCOPE_NAME@
|
||||
STRICT_PROTO_MATCHING = @DOXYGEN_STRICT_PROTO_MATCHING@
|
||||
GENERATE_TODOLIST = @DOXYGEN_GENERATE_TODOLIST@
|
||||
GENERATE_TESTLIST = @DOXYGEN_GENERATE_TESTLIST@
|
||||
GENERATE_BUGLIST = @DOXYGEN_GENERATE_BUGLIST@
|
||||
GENERATE_DEPRECATEDLIST= @DOXYGEN_GENERATE_DEPRECATEDLIST@
|
||||
ENABLED_SECTIONS = @DOXYGEN_ENABLED_SECTIONS@
|
||||
MAX_INITIALIZER_LINES = @DOXYGEN_MAX_INITIALIZER_LINES@
|
||||
SHOW_USED_FILES = @DOXYGEN_SHOW_USED_FILES@
|
||||
SHOW_FILES = @DOXYGEN_SHOW_FILES@
|
||||
SHOW_NAMESPACES = @DOXYGEN_SHOW_NAMESPACES@
|
||||
FILE_VERSION_FILTER = @DOXYGEN_FILE_VERSION_FILTER@
|
||||
LAYOUT_FILE = @DOXYGEN_LAYOUT_FILE@
|
||||
CITE_BIB_FILES = @DOXYGEN_CITE_BIB_FILES@
|
||||
QUIET = @DOXYGEN_QUIET@
|
||||
WARNINGS = @DOXYGEN_WARNINGS@
|
||||
WARN_IF_UNDOCUMENTED = @DOXYGEN_WARN_IF_UNDOCUMENTED@
|
||||
WARN_IF_DOC_ERROR = @DOXYGEN_WARN_IF_DOC_ERROR@
|
||||
WARN_NO_PARAMDOC = @DOXYGEN_WARN_NO_PARAMDOC@
|
||||
WARN_AS_ERROR = @DOXYGEN_WARN_AS_ERROR@
|
||||
WARN_FORMAT = @DOXYGEN_WARN_FORMAT@
|
||||
WARN_LOGFILE = @DOXYGEN_WARN_LOGFILE@
|
||||
INPUT = @DOXYGEN_INPUT@
|
||||
INPUT_ENCODING = @DOXYGEN_INPUT_ENCODING@
|
||||
FILE_PATTERNS = @DOXYGEN_FILE_PATTERNS@
|
||||
RECURSIVE = @DOXYGEN_RECURSIVE@
|
||||
EXCLUDE = @DOXYGEN_EXCLUDE@
|
||||
EXCLUDE_SYMLINKS = @DOXYGEN_EXCLUDE_SYMLINKS@
|
||||
EXCLUDE_PATTERNS = @DOXYGEN_EXCLUDE_PATTERNS@
|
||||
EXCLUDE_SYMBOLS = @DOXYGEN_EXCLUDE_SYMBOLS@
|
||||
EXAMPLE_PATH = @DOXYGEN_EXAMPLE_PATH@
|
||||
EXAMPLE_PATTERNS = @DOXYGEN_EXAMPLE_PATTERNS@
|
||||
EXAMPLE_RECURSIVE = @DOXYGEN_EXAMPLE_RECURSIVE@
|
||||
IMAGE_PATH = @DOXYGEN_IMAGE_PATH@
|
||||
INPUT_FILTER = @DOXYGEN_INPUT_FILTER@
|
||||
FILTER_PATTERNS = @DOXYGEN_FILTER_PATTERNS@
|
||||
FILTER_SOURCE_FILES = @DOXYGEN_FILTER_SOURCE_FILES@
|
||||
FILTER_SOURCE_PATTERNS = @DOXYGEN_FILTER_SOURCE_PATTERNS@
|
||||
USE_MDFILE_AS_MAINPAGE = @DOXYGEN_USE_MDFILE_AS_MAINPAGE@
|
||||
SOURCE_BROWSER = @DOXYGEN_SOURCE_BROWSER@
|
||||
INLINE_SOURCES = @DOXYGEN_INLINE_SOURCES@
|
||||
STRIP_CODE_COMMENTS = @DOXYGEN_STRIP_CODE_COMMENTS@
|
||||
REFERENCED_BY_RELATION = @DOXYGEN_REFERENCED_BY_RELATION@
|
||||
REFERENCES_RELATION = @DOXYGEN_REFERENCES_RELATION@
|
||||
REFERENCES_LINK_SOURCE = @DOXYGEN_REFERENCES_LINK_SOURCE@
|
||||
SOURCE_TOOLTIPS = @DOXYGEN_SOURCE_TOOLTIPS@
|
||||
USE_HTAGS = @DOXYGEN_USE_HTAGS@
|
||||
VERBATIM_HEADERS = @DOXYGEN_VERBATIM_HEADERS@
|
||||
CLANG_ASSISTED_PARSING = @DOXYGEN_CLANG_ASSISTED_PARSING@
|
||||
CLANG_OPTIONS = @DOXYGEN_CLANG_OPTIONS@
|
||||
CLANG_DATABASE_PATH = @DOXYGEN_CLANG_DATABASE_PATH@
|
||||
ALPHABETICAL_INDEX = @DOXYGEN_ALPHABETICAL_INDEX@
|
||||
COLS_IN_ALPHA_INDEX = @DOXYGEN_COLS_IN_ALPHA_INDEX@
|
||||
IGNORE_PREFIX = @DOXYGEN_IGNORE_PREFIX@
|
||||
GENERATE_HTML = @DOXYGEN_GENERATE_HTML@
|
||||
HTML_OUTPUT = @DOXYGEN_HTML_OUTPUT@
|
||||
HTML_FILE_EXTENSION = @DOXYGEN_HTML_FILE_EXTENSION@
|
||||
HTML_HEADER = @DOXYGEN_HTML_HEADER@
|
||||
HTML_FOOTER = @DOXYGEN_HTML_FOOTER@
|
||||
HTML_STYLESHEET = @DOXYGEN_HTML_STYLESHEET@
|
||||
HTML_EXTRA_STYLESHEET = @DOXYGEN_HTML_EXTRA_STYLESHEET@
|
||||
HTML_EXTRA_FILES = @DOXYGEN_HTML_EXTRA_FILES@
|
||||
HTML_COLORSTYLE_HUE = @DOXYGEN_HTML_COLORSTYLE_HUE@
|
||||
HTML_COLORSTYLE_SAT = @DOXYGEN_HTML_COLORSTYLE_SAT@
|
||||
HTML_COLORSTYLE_GAMMA = @DOXYGEN_HTML_COLORSTYLE_GAMMA@
|
||||
HTML_TIMESTAMP = @DOXYGEN_HTML_TIMESTAMP@
|
||||
HTML_DYNAMIC_MENUS = @DOXYGEN_HTML_DYNAMIC_MENUS@
|
||||
HTML_DYNAMIC_SECTIONS = @DOXYGEN_HTML_DYNAMIC_SECTIONS@
|
||||
HTML_INDEX_NUM_ENTRIES = @DOXYGEN_HTML_INDEX_NUM_ENTRIES@
|
||||
GENERATE_DOCSET = @DOXYGEN_GENERATE_DOCSET@
|
||||
DOCSET_FEEDNAME = @DOXYGEN_DOCSET_FEEDNAME@
|
||||
DOCSET_BUNDLE_ID = @DOXYGEN_DOCSET_BUNDLE_ID@
|
||||
DOCSET_PUBLISHER_ID = @DOXYGEN_DOCSET_PUBLISHER_ID@
|
||||
DOCSET_PUBLISHER_NAME = @DOXYGEN_DOCSET_PUBLISHER_NAME@
|
||||
GENERATE_HTMLHELP = @DOXYGEN_GENERATE_HTMLHELP@
|
||||
CHM_FILE = @DOXYGEN_CHM_FILE@
|
||||
HHC_LOCATION = @DOXYGEN_HHC_LOCATION@
|
||||
GENERATE_CHI = @DOXYGEN_GENERATE_CHI@
|
||||
CHM_INDEX_ENCODING = @DOXYGEN_CHM_INDEX_ENCODING@
|
||||
BINARY_TOC = @DOXYGEN_BINARY_TOC@
|
||||
TOC_EXPAND = @DOXYGEN_TOC_EXPAND@
|
||||
GENERATE_QHP = @DOXYGEN_GENERATE_QHP@
|
||||
QCH_FILE = @DOXYGEN_QCH_FILE@
|
||||
QHP_NAMESPACE = @DOXYGEN_QHP_NAMESPACE@
|
||||
QHP_VIRTUAL_FOLDER = @DOXYGEN_QHP_VIRTUAL_FOLDER@
|
||||
QHP_CUST_FILTER_NAME = @DOXYGEN_QHP_CUST_FILTER_NAME@
|
||||
QHP_CUST_FILTER_ATTRS = @DOXYGEN_QHP_CUST_FILTER_ATTRS@
|
||||
QHP_SECT_FILTER_ATTRS = @DOXYGEN_QHP_SECT_FILTER_ATTRS@
|
||||
QHG_LOCATION = @DOXYGEN_QHG_LOCATION@
|
||||
GENERATE_ECLIPSEHELP = @DOXYGEN_GENERATE_ECLIPSEHELP@
|
||||
ECLIPSE_DOC_ID = @DOXYGEN_ECLIPSE_DOC_ID@
|
||||
DISABLE_INDEX = @DOXYGEN_DISABLE_INDEX@
|
||||
GENERATE_TREEVIEW = @DOXYGEN_GENERATE_TREEVIEW@
|
||||
ENUM_VALUES_PER_LINE = @DOXYGEN_ENUM_VALUES_PER_LINE@
|
||||
TREEVIEW_WIDTH = @DOXYGEN_TREEVIEW_WIDTH@
|
||||
EXT_LINKS_IN_WINDOW = @DOXYGEN_EXT_LINKS_IN_WINDOW@
|
||||
FORMULA_FONTSIZE = @DOXYGEN_FORMULA_FONTSIZE@
|
||||
FORMULA_TRANSPARENT = @DOXYGEN_FORMULA_TRANSPARENT@
|
||||
USE_MATHJAX = @DOXYGEN_USE_MATHJAX@
|
||||
MATHJAX_FORMAT = @DOXYGEN_MATHJAX_FORMAT@
|
||||
MATHJAX_RELPATH = @DOXYGEN_MATHJAX_RELPATH@
|
||||
MATHJAX_EXTENSIONS = @DOXYGEN_MATHJAX_EXTENSIONS@
|
||||
MATHJAX_CODEFILE = @DOXYGEN_MATHJAX_CODEFILE@
|
||||
SEARCHENGINE = @DOXYGEN_SEARCHENGINE@
|
||||
SERVER_BASED_SEARCH = @DOXYGEN_SERVER_BASED_SEARCH@
|
||||
EXTERNAL_SEARCH = @DOXYGEN_EXTERNAL_SEARCH@
|
||||
SEARCHENGINE_URL = @DOXYGEN_SEARCHENGINE_URL@
|
||||
SEARCHDATA_FILE = @DOXYGEN_SEARCHDATA_FILE@
|
||||
EXTERNAL_SEARCH_ID = @DOXYGEN_EXTERNAL_SEARCH_ID@
|
||||
EXTRA_SEARCH_MAPPINGS = @DOXYGEN_EXTRA_SEARCH_MAPPINGS@
|
||||
GENERATE_LATEX = @DOXYGEN_GENERATE_LATEX@
|
||||
LATEX_OUTPUT = @DOXYGEN_LATEX_OUTPUT@
|
||||
LATEX_CMD_NAME = @DOXYGEN_LATEX_CMD_NAME@
|
||||
MAKEINDEX_CMD_NAME = @DOXYGEN_MAKEINDEX_CMD_NAME@
|
||||
LATEX_MAKEINDEX_CMD = @DOXYGEN_LATEX_MAKEINDEX_CMD@
|
||||
COMPACT_LATEX = @DOXYGEN_COMPACT_LATEX@
|
||||
PAPER_TYPE = @DOXYGEN_PAPER_TYPE@
|
||||
EXTRA_PACKAGES = @DOXYGEN_EXTRA_PACKAGES@
|
||||
LATEX_HEADER = @DOXYGEN_LATEX_HEADER@
|
||||
LATEX_FOOTER = @DOXYGEN_LATEX_FOOTER@
|
||||
LATEX_EXTRA_STYLESHEET = @DOXYGEN_LATEX_EXTRA_STYLESHEET@
|
||||
LATEX_EXTRA_FILES = @DOXYGEN_LATEX_EXTRA_FILES@
|
||||
PDF_HYPERLINKS = @DOXYGEN_PDF_HYPERLINKS@
|
||||
USE_PDFLATEX = @DOXYGEN_USE_PDFLATEX@
|
||||
LATEX_BATCHMODE = @DOXYGEN_LATEX_BATCHMODE@
|
||||
LATEX_HIDE_INDICES = @DOXYGEN_LATEX_HIDE_INDICES@
|
||||
LATEX_SOURCE_CODE = @DOXYGEN_LATEX_SOURCE_CODE@
|
||||
LATEX_BIB_STYLE = @DOXYGEN_LATEX_BIB_STYLE@
|
||||
LATEX_TIMESTAMP = @DOXYGEN_LATEX_TIMESTAMP@
|
||||
LATEX_EMOJI_DIRECTORY = @DOXYGEN_LATEX_EMOJI_DIRECTORY@
|
||||
GENERATE_RTF = @DOXYGEN_GENERATE_RTF@
|
||||
RTF_OUTPUT = @DOXYGEN_RTF_OUTPUT@
|
||||
COMPACT_RTF = @DOXYGEN_COMPACT_RTF@
|
||||
RTF_HYPERLINKS = @DOXYGEN_RTF_HYPERLINKS@
|
||||
RTF_STYLESHEET_FILE = @DOXYGEN_RTF_STYLESHEET_FILE@
|
||||
RTF_EXTENSIONS_FILE = @DOXYGEN_RTF_EXTENSIONS_FILE@
|
||||
RTF_SOURCE_CODE = @DOXYGEN_RTF_SOURCE_CODE@
|
||||
GENERATE_MAN = @DOXYGEN_GENERATE_MAN@
|
||||
MAN_OUTPUT = @DOXYGEN_MAN_OUTPUT@
|
||||
MAN_EXTENSION = @DOXYGEN_MAN_EXTENSION@
|
||||
MAN_SUBDIR = @DOXYGEN_MAN_SUBDIR@
|
||||
MAN_LINKS = @DOXYGEN_MAN_LINKS@
|
||||
GENERATE_XML = @DOXYGEN_GENERATE_XML@
|
||||
XML_OUTPUT = @DOXYGEN_XML_OUTPUT@
|
||||
XML_PROGRAMLISTING = @DOXYGEN_XML_PROGRAMLISTING@
|
||||
XML_NS_MEMB_FILE_SCOPE = @DOXYGEN_XML_NS_MEMB_FILE_SCOPE@
|
||||
GENERATE_DOCBOOK = @DOXYGEN_GENERATE_DOCBOOK@
|
||||
DOCBOOK_OUTPUT = @DOXYGEN_DOCBOOK_OUTPUT@
|
||||
DOCBOOK_PROGRAMLISTING = @DOXYGEN_DOCBOOK_PROGRAMLISTING@
|
||||
GENERATE_AUTOGEN_DEF = @DOXYGEN_GENERATE_AUTOGEN_DEF@
|
||||
GENERATE_PERLMOD = @DOXYGEN_GENERATE_PERLMOD@
|
||||
PERLMOD_LATEX = @DOXYGEN_PERLMOD_LATEX@
|
||||
PERLMOD_PRETTY = @DOXYGEN_PERLMOD_PRETTY@
|
||||
PERLMOD_MAKEVAR_PREFIX = @DOXYGEN_PERLMOD_MAKEVAR_PREFIX@
|
||||
ENABLE_PREPROCESSING = @DOXYGEN_ENABLE_PREPROCESSING@
|
||||
MACRO_EXPANSION = @DOXYGEN_MACRO_EXPANSION@
|
||||
EXPAND_ONLY_PREDEF = @DOXYGEN_EXPAND_ONLY_PREDEF@
|
||||
SEARCH_INCLUDES = @DOXYGEN_SEARCH_INCLUDES@
|
||||
INCLUDE_PATH = @DOXYGEN_INCLUDE_PATH@
|
||||
INCLUDE_FILE_PATTERNS = @DOXYGEN_INCLUDE_FILE_PATTERNS@
|
||||
PREDEFINED = @DOXYGEN_PREDEFINED@
|
||||
EXPAND_AS_DEFINED = @DOXYGEN_EXPAND_AS_DEFINED@
|
||||
SKIP_FUNCTION_MACROS = @DOXYGEN_SKIP_FUNCTION_MACROS@
|
||||
TAGFILES = @DOXYGEN_TAGFILES@
|
||||
GENERATE_TAGFILE = @DOXYGEN_GENERATE_TAGFILE@
|
||||
ALLEXTERNALS = @DOXYGEN_ALLEXTERNALS@
|
||||
EXTERNAL_GROUPS = @DOXYGEN_EXTERNAL_GROUPS@
|
||||
EXTERNAL_PAGES = @DOXYGEN_EXTERNAL_PAGES@
|
||||
CLASS_DIAGRAMS = @DOXYGEN_CLASS_DIAGRAMS@
|
||||
DIA_PATH = @DOXYGEN_DIA_PATH@
|
||||
HIDE_UNDOC_RELATIONS = @DOXYGEN_HIDE_UNDOC_RELATIONS@
|
||||
HAVE_DOT = @DOXYGEN_HAVE_DOT@
|
||||
DOT_NUM_THREADS = @DOXYGEN_DOT_NUM_THREADS@
|
||||
DOT_FONTNAME = @DOXYGEN_DOT_FONTNAME@
|
||||
DOT_FONTSIZE = @DOXYGEN_DOT_FONTSIZE@
|
||||
DOT_FONTPATH = @DOXYGEN_DOT_FONTPATH@
|
||||
CLASS_GRAPH = @DOXYGEN_CLASS_GRAPH@
|
||||
COLLABORATION_GRAPH = @DOXYGEN_COLLABORATION_GRAPH@
|
||||
GROUP_GRAPHS = @DOXYGEN_GROUP_GRAPHS@
|
||||
UML_LOOK = @DOXYGEN_UML_LOOK@
|
||||
UML_LIMIT_NUM_FIELDS = @DOXYGEN_UML_LIMIT_NUM_FIELDS@
|
||||
TEMPLATE_RELATIONS = @DOXYGEN_TEMPLATE_RELATIONS@
|
||||
INCLUDE_GRAPH = @DOXYGEN_INCLUDE_GRAPH@
|
||||
INCLUDED_BY_GRAPH = @DOXYGEN_INCLUDED_BY_GRAPH@
|
||||
CALL_GRAPH = @DOXYGEN_CALL_GRAPH@
|
||||
CALLER_GRAPH = @DOXYGEN_CALLER_GRAPH@
|
||||
GRAPHICAL_HIERARCHY = @DOXYGEN_GRAPHICAL_HIERARCHY@
|
||||
DIRECTORY_GRAPH = @DOXYGEN_DIRECTORY_GRAPH@
|
||||
DOT_IMAGE_FORMAT = @DOXYGEN_DOT_IMAGE_FORMAT@
|
||||
INTERACTIVE_SVG = @DOXYGEN_INTERACTIVE_SVG@
|
||||
DOT_PATH = @DOXYGEN_DOT_PATH@
|
||||
DOTFILE_DIRS = @DOXYGEN_DOTFILE_DIRS@
|
||||
MSCFILE_DIRS = @DOXYGEN_MSCFILE_DIRS@
|
||||
DIAFILE_DIRS = @DOXYGEN_DIAFILE_DIRS@
|
||||
PLANTUML_JAR_PATH = @DOXYGEN_PLANTUML_JAR_PATH@
|
||||
PLANTUML_CFG_FILE = @DOXYGEN_PLANTUML_CFG_FILE@
|
||||
PLANTUML_INCLUDE_PATH = @DOXYGEN_PLANTUML_INCLUDE_PATH@
|
||||
DOT_GRAPH_MAX_NODES = @DOXYGEN_DOT_GRAPH_MAX_NODES@
|
||||
MAX_DOT_GRAPH_DEPTH = @DOXYGEN_MAX_DOT_GRAPH_DEPTH@
|
||||
DOT_TRANSPARENT = @DOXYGEN_DOT_TRANSPARENT@
|
||||
DOT_MULTI_TARGETS = @DOXYGEN_DOT_MULTI_TARGETS@
|
||||
GENERATE_LEGEND = @DOXYGEN_GENERATE_LEGEND@
|
||||
DOT_CLEANUP = @DOXYGEN_DOT_CLEANUP@
|
||||
|
|
@ -135,7 +135,7 @@ else( WIN32 ) # Apple AND Linux
|
|||
endif( APPLE )
|
||||
|
||||
if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-terminate -Wno-invalid-offsetof" )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-builtin-memcmp -Wno-parentheses -Wno-terminate -Wno-invalid-offsetof -Wno-sign-compare" )
|
||||
elseif( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" )
|
||||
if( CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 4.0.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.0.0 )
|
||||
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-invalid-partial-specialization" )
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ RUN \
|
|||
build-essential \
|
||||
ca-certificates \
|
||||
cmake \
|
||||
dnsutils \
|
||||
doxygen \
|
||||
git \
|
||||
graphviz \
|
||||
|
|
@ -22,6 +23,7 @@ RUN \
|
|||
libreadline-dev \
|
||||
libssl-dev \
|
||||
libtool \
|
||||
libzmq3-dev \
|
||||
locales \
|
||||
ntp \
|
||||
pkg-config \
|
||||
|
|
@ -57,7 +59,7 @@ RUN \
|
|||
cd build/release && \
|
||||
cmake \
|
||||
-DBOOST_ROOT="$BOOST_ROOT" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
../.. && \
|
||||
make witness_node cli_wallet && \
|
||||
install -s programs/witness_node/witness_node programs/cli_wallet/cli_wallet /usr/local/bin && \
|
||||
|
|
|
|||
|
|
@ -7,9 +7,10 @@ This is a quick introduction to get new developers and witnesses up to speed on
|
|||
The following dependencies were necessary for a clean install of Ubuntu 18.04 LTS:
|
||||
|
||||
```
|
||||
sudo apt-get install gcc-5 g++-5 cmake make libbz2-dev\
|
||||
libdb++-dev libdb-dev libssl-dev openssl libreadline-dev\
|
||||
autoconf libtool git
|
||||
sudo apt-get install autoconf bash build-essential ca-certificates cmake \
|
||||
doxygen git graphviz libbz2-dev libcurl4-openssl-dev libncurses-dev \
|
||||
libreadline-dev libssl-dev libtool libzmq3-dev locales ntp pkg-config \
|
||||
wget
|
||||
```
|
||||
## Build Boost 1.67.0
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ add_library( graphene_app
|
|||
|
||||
# need to link graphene_debug_witness because plugins aren't sufficiently isolated #246
|
||||
#target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_chain fc graphene_db graphene_net graphene_utilities graphene_debug_witness )
|
||||
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie graphene_elasticsearch )
|
||||
target_link_libraries( graphene_app graphene_market_history graphene_account_history graphene_accounts_list graphene_affiliate_stats graphene_chain fc graphene_db graphene_net graphene_time graphene_utilities graphene_debug_witness graphene_bookie graphene_elasticsearch peerplays_sidechain )
|
||||
target_include_directories( graphene_app
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../egenesis/include" )
|
||||
|
|
|
|||
|
|
@ -448,7 +448,17 @@ namespace graphene { namespace app {
|
|||
} case balance_object_type:{
|
||||
/** these are free from any accounts */
|
||||
break;
|
||||
}
|
||||
} case son_object_type:{
|
||||
const auto& aobj = dynamic_cast<const son_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->son_account );
|
||||
break;
|
||||
} case sidechain_address_object_type:{
|
||||
const auto& aobj = dynamic_cast<const sidechain_address_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->sidechain_address_account );
|
||||
break;
|
||||
}
|
||||
case sport_object_type:
|
||||
case event_group_object_type:
|
||||
case event_object_type:
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
uint64_t get_asset_count()const;
|
||||
|
||||
// Peerplays
|
||||
vector<sport_object> list_sports() const;
|
||||
vector<sport_object> list_sports() const;
|
||||
vector<event_group_object> list_event_groups(sport_id_type sport_id) const;
|
||||
vector<event_object> list_events_in_group(event_group_id_type event_group_id) const;
|
||||
vector<betting_market_group_object> list_betting_market_groups(event_id_type) const;
|
||||
|
|
@ -124,14 +124,14 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
vector<asset_object> get_lotteries( asset_id_type stop = asset_id_type(),
|
||||
unsigned limit = 100,
|
||||
asset_id_type start = asset_id_type() )const;
|
||||
vector<asset_object> get_account_lotteries( account_id_type issuer,
|
||||
vector<asset_object> get_account_lotteries( account_id_type issuer,
|
||||
asset_id_type stop,
|
||||
unsigned limit,
|
||||
asset_id_type start )const;
|
||||
asset get_lottery_balance( asset_id_type lottery_id )const;
|
||||
sweeps_vesting_balance_object get_sweeps_vesting_balance_object( account_id_type account )const;
|
||||
asset get_sweeps_vesting_balance_available_for_claim( account_id_type account )const;
|
||||
|
||||
|
||||
// Markets / feeds
|
||||
vector<limit_order_object> get_limit_orders( const asset_id_type a, const asset_id_type b, const uint32_t limit )const;
|
||||
vector<limit_order_object> get_limit_orders( const std::string& a, const std::string& b, const uint32_t limit)const;
|
||||
|
|
@ -156,6 +156,24 @@ class database_api_impl : public std::enable_shared_from_this<database_api_impl>
|
|||
fc::optional<committee_member_object> get_committee_member_by_account(const std::string account_id_or_name)const;
|
||||
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
|
||||
// SON members
|
||||
vector<optional<son_object>> get_sons(const vector<son_id_type>& son_ids)const;
|
||||
fc::optional<son_object> get_son_by_account(account_id_type account)const;
|
||||
map<string, son_id_type> lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
uint64_t get_son_count()const;
|
||||
|
||||
// SON wallets
|
||||
optional<son_wallet_object> get_active_son_wallet();
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point);
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit);
|
||||
|
||||
// Sidechain addresses
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const;
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_account(account_id_type account)const;
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const;
|
||||
fc::optional<sidechain_address_object> get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain)const;
|
||||
uint64_t get_sidechain_addresses_count()const;
|
||||
|
||||
// Votes
|
||||
vector<variant> lookup_vote_ids( const vector<vote_id_type>& votes )const;
|
||||
|
||||
|
|
@ -574,11 +592,11 @@ vector<vector<account_id_type>> database_api::get_key_references( vector<public_
|
|||
vector<vector<account_id_type>> database_api_impl::get_key_references( vector<public_key_type> keys )const
|
||||
{
|
||||
wdump( (keys) );
|
||||
|
||||
|
||||
const auto& idx = _db.get_index_type<account_index>();
|
||||
const auto& aidx = dynamic_cast<const base_primary_index&>(idx);
|
||||
const auto& refs = aidx.get_secondary_index<graphene::chain::account_member_index>();
|
||||
|
||||
|
||||
vector< vector<account_id_type> > final_result;
|
||||
final_result.reserve(keys.size());
|
||||
|
||||
|
|
@ -699,7 +717,7 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
|
|||
const auto& proposal_idx = _db.get_index_type<proposal_index>();
|
||||
const auto& pidx = dynamic_cast<const base_primary_index&>(proposal_idx);
|
||||
const auto& proposals_by_account = pidx.get_secondary_index<graphene::chain::required_approval_index>();
|
||||
|
||||
|
||||
std::map<std::string, full_account> results;
|
||||
|
||||
for (const std::string& account_name_or_id : names_or_ids)
|
||||
|
|
@ -789,7 +807,7 @@ std::map<std::string, full_account> database_api_impl::get_full_accounts( const
|
|||
acnt.withdraws.emplace_back(withdraw);
|
||||
});
|
||||
|
||||
auto pending_payouts_range =
|
||||
auto pending_payouts_range =
|
||||
_db.get_index_type<pending_dividend_payout_balance_for_holder_object_index>().indices().get<by_account_dividend_payout>().equal_range(boost::make_tuple(account->id));
|
||||
|
||||
std::copy(pending_payouts_range.first, pending_payouts_range.second, std::back_inserter(acnt.pending_dividend_payments));
|
||||
|
|
@ -1146,7 +1164,7 @@ vector<asset_object> database_api_impl::get_lotteries( asset_id_type stop,
|
|||
|
||||
return result;
|
||||
}
|
||||
vector<asset_object> database_api::get_account_lotteries( account_id_type issuer,
|
||||
vector<asset_object> database_api::get_account_lotteries( account_id_type issuer,
|
||||
asset_id_type stop,
|
||||
unsigned limit,
|
||||
asset_id_type start )const
|
||||
|
|
@ -1154,7 +1172,7 @@ vector<asset_object> database_api::get_account_lotteries( account_id_type issuer
|
|||
return my->get_account_lotteries( issuer, stop, limit, start );
|
||||
}
|
||||
|
||||
vector<asset_object> database_api_impl::get_account_lotteries( account_id_type issuer,
|
||||
vector<asset_object> database_api_impl::get_account_lotteries( account_id_type issuer,
|
||||
asset_id_type stop,
|
||||
unsigned limit,
|
||||
asset_id_type start )const
|
||||
|
|
@ -1796,6 +1814,213 @@ map<string, committee_member_id_type> database_api_impl::lookup_committee_member
|
|||
return committee_members_by_account_name;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// SON members //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
vector<optional<son_object>> database_api::get_sons(const vector<son_id_type>& son_ids)const
|
||||
{
|
||||
return my->get_sons( son_ids );
|
||||
}
|
||||
|
||||
vector<optional<son_object>> database_api_impl::get_sons(const vector<son_id_type>& son_ids)const
|
||||
{
|
||||
vector<optional<son_object>> result; result.reserve(son_ids.size());
|
||||
std::transform(son_ids.begin(), son_ids.end(), std::back_inserter(result),
|
||||
[this](son_id_type id) -> optional<son_object> {
|
||||
if(auto o = _db.find(id))
|
||||
return *o;
|
||||
return {};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
fc::optional<son_object> database_api::get_son_by_account(account_id_type account)const
|
||||
{
|
||||
return my->get_son_by_account( account );
|
||||
}
|
||||
|
||||
fc::optional<son_object> database_api_impl::get_son_by_account(account_id_type account) const
|
||||
{
|
||||
const auto& idx = _db.get_index_type<son_index>().indices().get<by_account>();
|
||||
auto itr = idx.find(account);
|
||||
if( itr != idx.end() )
|
||||
return *itr;
|
||||
return {};
|
||||
}
|
||||
|
||||
map<string, son_id_type> database_api::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const
|
||||
{
|
||||
return my->lookup_son_accounts( lower_bound_name, limit );
|
||||
}
|
||||
|
||||
map<string, son_id_type> database_api_impl::lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const
|
||||
{
|
||||
FC_ASSERT( limit <= 1000 );
|
||||
const auto& sons_by_id = _db.get_index_type<son_index>().indices().get<by_id>();
|
||||
|
||||
// we want to order sons by account name, but that name is in the account object
|
||||
// so the son_index doesn't have a quick way to access it.
|
||||
// get all the names and look them all up, sort them, then figure out what
|
||||
// records to return. This could be optimized, but we expect the
|
||||
// number of witnesses to be few and the frequency of calls to be rare
|
||||
std::map<std::string, son_id_type> sons_by_account_name;
|
||||
for (const son_object& son : sons_by_id)
|
||||
if (auto account_iter = _db.find(son.son_account))
|
||||
if (account_iter->name >= lower_bound_name) // we can ignore anything below lower_bound_name
|
||||
sons_by_account_name.insert(std::make_pair(account_iter->name, son.id));
|
||||
|
||||
auto end_iter = sons_by_account_name.begin();
|
||||
while (end_iter != sons_by_account_name.end() && limit--)
|
||||
++end_iter;
|
||||
sons_by_account_name.erase(end_iter, sons_by_account_name.end());
|
||||
return sons_by_account_name;
|
||||
}
|
||||
|
||||
uint64_t database_api::get_son_count()const
|
||||
{
|
||||
return my->get_son_count();
|
||||
}
|
||||
|
||||
uint64_t database_api_impl::get_son_count()const
|
||||
{
|
||||
return _db.get_index_type<son_index>().indices().size();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// SON Wallets //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
optional<son_wallet_object> database_api::get_active_son_wallet()
|
||||
{
|
||||
return my->get_active_son_wallet();
|
||||
}
|
||||
|
||||
optional<son_wallet_object> database_api_impl::get_active_son_wallet()
|
||||
{
|
||||
const auto& idx = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx.rbegin();
|
||||
if (obj != idx.rend()) {
|
||||
return *obj;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<son_wallet_object> database_api::get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{
|
||||
return my->get_son_wallet_by_time_point(time_point);
|
||||
}
|
||||
|
||||
optional<son_wallet_object> database_api_impl::get_son_wallet_by_time_point(time_point_sec time_point)
|
||||
{
|
||||
const auto& son_wallets_by_id = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
for (const son_wallet_object& swo : son_wallets_by_id) {
|
||||
if ((time_point >= swo.valid_from) && (time_point < swo.expires))
|
||||
return swo;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
vector<optional<son_wallet_object>> database_api::get_son_wallets(uint32_t limit)
|
||||
{
|
||||
return my->get_son_wallets(limit);
|
||||
}
|
||||
|
||||
vector<optional<son_wallet_object>> database_api_impl::get_son_wallets(uint32_t limit)
|
||||
{
|
||||
FC_ASSERT( limit <= 1000 );
|
||||
vector<optional<son_wallet_object>> result;
|
||||
const auto& son_wallets_by_id = _db.get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
for (const son_wallet_object& swo : son_wallets_by_id)
|
||||
result.push_back(swo);
|
||||
return result;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Sidechain Accounts //
|
||||
// //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api::get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const
|
||||
{
|
||||
return my->get_sidechain_addresses( sidechain_address_ids );
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api_impl::get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const
|
||||
{
|
||||
vector<optional<sidechain_address_object>> result; result.reserve(sidechain_address_ids.size());
|
||||
std::transform(sidechain_address_ids.begin(), sidechain_address_ids.end(), std::back_inserter(result),
|
||||
[this](sidechain_address_id_type id) -> optional<sidechain_address_object> {
|
||||
if(auto o = _db.find(id))
|
||||
return *o;
|
||||
return {};
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api::get_sidechain_addresses_by_account(account_id_type account)const
|
||||
{
|
||||
return my->get_sidechain_addresses_by_account( account );
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api_impl::get_sidechain_addresses_by_account(account_id_type account)const
|
||||
{
|
||||
vector<optional<sidechain_address_object>> result;
|
||||
const auto& sidechain_addresses_range = _db.get_index_type<sidechain_address_index>().indices().get<by_account>().equal_range(account);
|
||||
std::for_each(sidechain_addresses_range.first, sidechain_addresses_range.second,
|
||||
[&result] (const sidechain_address_object& sao) {
|
||||
if( sao.expires == time_point_sec::maximum() )
|
||||
result.push_back(sao);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api::get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const
|
||||
{
|
||||
return my->get_sidechain_addresses_by_sidechain( sidechain );
|
||||
}
|
||||
|
||||
vector<optional<sidechain_address_object>> database_api_impl::get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const
|
||||
{
|
||||
vector<optional<sidechain_address_object>> result;
|
||||
const auto& sidechain_addresses_range = _db.get_index_type<sidechain_address_index>().indices().get<by_sidechain>().equal_range(sidechain);
|
||||
std::for_each(sidechain_addresses_range.first, sidechain_addresses_range.second,
|
||||
[&result] (const sidechain_address_object& sao) {
|
||||
if( sao.expires == time_point_sec::maximum() )
|
||||
result.push_back(sao);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
fc::optional<sidechain_address_object> database_api::get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain)const
|
||||
{
|
||||
return my->get_sidechain_address_by_account_and_sidechain( account, sidechain );
|
||||
}
|
||||
|
||||
fc::optional<sidechain_address_object> database_api_impl::get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain)const
|
||||
{
|
||||
const auto& idx = _db.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain_and_expires>();
|
||||
auto itr = idx.find( boost::make_tuple( account, sidechain, time_point_sec::maximum() ) );
|
||||
if( itr != idx.end() )
|
||||
return *itr;
|
||||
return {};
|
||||
}
|
||||
|
||||
uint64_t database_api::get_sidechain_addresses_count()const
|
||||
{
|
||||
return my->get_sidechain_addresses_count();
|
||||
}
|
||||
|
||||
uint64_t database_api_impl::get_sidechain_addresses_count()const
|
||||
{
|
||||
return _db.get_index_type<sidechain_address_index>().indices().size();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// //
|
||||
// Votes //
|
||||
|
|
@ -1815,6 +2040,7 @@ vector<variant> database_api_impl::lookup_vote_ids( const vector<vote_id_type>&
|
|||
const auto& committee_idx = _db.get_index_type<committee_member_index>().indices().get<by_vote_id>();
|
||||
const auto& for_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_for>();
|
||||
const auto& against_worker_idx = _db.get_index_type<worker_index>().indices().get<by_vote_against>();
|
||||
const auto& son_idx = _db.get_index_type<son_index>().indices().get<by_vote_id>();
|
||||
|
||||
vector<variant> result;
|
||||
result.reserve( votes.size() );
|
||||
|
|
@ -1857,6 +2083,16 @@ vector<variant> database_api_impl::lookup_vote_ids( const vector<vote_id_type>&
|
|||
}
|
||||
break;
|
||||
}
|
||||
case vote_id_type::son:
|
||||
{
|
||||
auto itr = son_idx.find( id );
|
||||
if( itr != son_idx.end() )
|
||||
result.emplace_back( variant( *itr, 1 ) );
|
||||
else
|
||||
result.emplace_back( variant() );
|
||||
break;
|
||||
}
|
||||
|
||||
case vote_id_type::VOTE_TYPE_COUNT: break; // supress unused enum value warnings
|
||||
default:
|
||||
FC_CAPTURE_AND_THROW( fc::out_of_range_exception, (id) );
|
||||
|
|
@ -2206,7 +2442,7 @@ vector<tournament_object> database_api::get_tournaments(tournament_id_type stop,
|
|||
|
||||
vector<tournament_object> database_api_impl::get_tournaments(tournament_id_type stop,
|
||||
unsigned limit,
|
||||
tournament_id_type start)
|
||||
tournament_id_type start)
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
||||
|
|
@ -2233,7 +2469,7 @@ vector<tournament_object> database_api_impl::get_tournaments_by_state(tournament
|
|||
unsigned limit,
|
||||
tournament_id_type start,
|
||||
tournament_state state)
|
||||
{
|
||||
{
|
||||
vector<tournament_object> result;
|
||||
const auto& tournament_idx = _db.get_index_type<tournament_index>().indices().get<by_id>();
|
||||
for (auto elem: tournament_idx) {
|
||||
|
|
@ -3006,7 +3242,7 @@ void database_api_impl::handle_object_changed(bool force_notify, bool full_objec
|
|||
/// if a connection hangs then this could get backed up and result in
|
||||
/// a failure to exit cleanly.
|
||||
//fc::async([capture_this,this,updates,market_broadcast_queue](){
|
||||
//if( _subscribe_callback )
|
||||
//if( _subscribe_callback )
|
||||
// _subscribe_callback( updates );
|
||||
|
||||
for(auto id : ids)
|
||||
|
|
|
|||
|
|
@ -61,8 +61,14 @@ namespace graphene { namespace app {
|
|||
plug->plugin_set_program_options(plugin_cli_options, plugin_cfg_options);
|
||||
if( !plugin_cli_options.options().empty() )
|
||||
_cli_options.add(plugin_cli_options);
|
||||
|
||||
if( !plugin_cfg_options.options().empty() )
|
||||
{
|
||||
std::string header_name = "plugin-cfg-header-" + plug->plugin_name();
|
||||
std::string header_desc = plug->plugin_name() + " plugin options";
|
||||
_cfg_options.add_options()(header_name.c_str(), header_desc.c_str());
|
||||
_cfg_options.add(plugin_cfg_options);
|
||||
}
|
||||
|
||||
add_available_plugin( plug );
|
||||
return plug;
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@
|
|||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
|
||||
#include <graphene/chain/worker_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
|
@ -600,6 +603,102 @@ class database_api
|
|||
*/
|
||||
map<string, committee_member_id_type> lookup_committee_member_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
|
||||
/////////////////
|
||||
// SON members //
|
||||
/////////////////
|
||||
|
||||
/**
|
||||
* @brief Get a list of SONs by ID
|
||||
* @param son_ids IDs of the SONs to retrieve
|
||||
* @return The SONs corresponding to the provided IDs
|
||||
*
|
||||
* This function has semantics identical to @ref get_objects
|
||||
*/
|
||||
vector<optional<son_object>> get_sons(const vector<son_id_type>& son_ids)const;
|
||||
|
||||
/**
|
||||
* @brief Get the SON owned by a given account
|
||||
* @param account The ID of the account whose SON should be retrieved
|
||||
* @return The SON object, or null if the account does not have a SON
|
||||
*/
|
||||
fc::optional<son_object> get_son_by_account(account_id_type account)const;
|
||||
|
||||
/**
|
||||
* @brief Get names and IDs for registered SONs
|
||||
* @param lower_bound_name Lower bound of the first name to return
|
||||
* @param limit Maximum number of results to return -- must not exceed 1000
|
||||
* @return Map of SON names to corresponding IDs
|
||||
*/
|
||||
map<string, son_id_type> lookup_son_accounts(const string& lower_bound_name, uint32_t limit)const;
|
||||
|
||||
/**
|
||||
* @brief Get the total number of SONs registered with the blockchain
|
||||
*/
|
||||
uint64_t get_son_count()const;
|
||||
|
||||
/////////////////////////
|
||||
// SON Wallets //
|
||||
/////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Get active SON wallet
|
||||
* @return Active SON wallet object
|
||||
*/
|
||||
optional<son_wallet_object> get_active_son_wallet();
|
||||
|
||||
/**
|
||||
* @brief Get SON wallet that was active for a given time point
|
||||
* @param time_point Time point
|
||||
* @return SON wallet object, for the wallet that was active for a given time point
|
||||
*/
|
||||
optional<son_wallet_object> get_son_wallet_by_time_point(time_point_sec time_point);
|
||||
|
||||
/**
|
||||
* @brief Get full list of SON wallets
|
||||
* @param limit Maximum number of results to return
|
||||
* @return A list of SON wallet objects
|
||||
*/
|
||||
vector<optional<son_wallet_object>> get_son_wallets(uint32_t limit);
|
||||
|
||||
/////////////////////////
|
||||
// Sidechain Addresses //
|
||||
/////////////////////////
|
||||
|
||||
/**
|
||||
* @brief Get a list of sidechain addresses
|
||||
* @param sidechain_address_ids IDs of the sidechain addresses to retrieve
|
||||
* @return The sidechain accounts corresponding to the provided IDs
|
||||
*
|
||||
* This function has semantics identical to @ref get_objects
|
||||
*/
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses(const vector<sidechain_address_id_type>& sidechain_address_ids)const;
|
||||
|
||||
/**
|
||||
* @brief Get the sidechain addresses for a given account
|
||||
* @param account The ID of the account whose sidechain addresses should be retrieved
|
||||
* @return The sidechain addresses objects, or null if the account does not have a sidechain addresses
|
||||
*/
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_account(account_id_type account)const;
|
||||
|
||||
/**
|
||||
* @brief Get the sidechain addresses for a given sidechain
|
||||
* @param sidechain Sidechain for which addresses should be retrieved
|
||||
* @return The sidechain addresses objects, or null if the sidechain does not have any addresses
|
||||
*/
|
||||
vector<optional<sidechain_address_object>> get_sidechain_addresses_by_sidechain(sidechain_type sidechain)const;
|
||||
|
||||
/**
|
||||
* @brief Get the sidechain addresses for a given account and sidechain
|
||||
* @param account The ID of the account whose sidechain addresses should be retrieved
|
||||
* @param sidechain Sidechain for which address should be retrieved
|
||||
* @return The sidechain addresses objects, or null if the account does not have a sidechain addresses for a given sidechain
|
||||
*/
|
||||
fc::optional<sidechain_address_object> get_sidechain_address_by_account_and_sidechain(account_id_type account, sidechain_type sidechain)const;
|
||||
|
||||
/**
|
||||
* @brief Get the total number of sidechain addresses registered with the blockchain
|
||||
*/
|
||||
uint64_t get_sidechain_addresses_count()const;
|
||||
|
||||
/// WORKERS
|
||||
|
||||
|
|
@ -943,6 +1042,24 @@ FC_API(graphene::app::database_api,
|
|||
(get_committee_member_by_account)
|
||||
(lookup_committee_member_accounts)
|
||||
|
||||
// SON members
|
||||
(get_sons)
|
||||
(get_son_by_account)
|
||||
(lookup_son_accounts)
|
||||
(get_son_count)
|
||||
|
||||
// SON wallets
|
||||
(get_active_son_wallet)
|
||||
(get_son_wallet_by_time_point)
|
||||
(get_son_wallets)
|
||||
|
||||
// Sidechain addresses
|
||||
(get_sidechain_addresses)
|
||||
(get_sidechain_addresses_by_account)
|
||||
(get_sidechain_addresses_by_sidechain)
|
||||
(get_sidechain_address_by_account_and_sidechain)
|
||||
(get_sidechain_addresses_count)
|
||||
|
||||
// workers
|
||||
(get_workers_by_account)
|
||||
// Votes
|
||||
|
|
|
|||
10
libraries/chain/CMakeLists.txt
Normal file → Executable file
10
libraries/chain/CMakeLists.txt
Normal file → Executable file
|
|
@ -127,6 +127,16 @@ add_library( graphene_chain
|
|||
protocol/account_role.cpp
|
||||
account_role_evaluator.cpp
|
||||
|
||||
son_evaluator.cpp
|
||||
son_object.cpp
|
||||
|
||||
son_wallet_evaluator.cpp
|
||||
son_wallet_deposit_evaluator.cpp
|
||||
son_wallet_withdraw_evaluator.cpp
|
||||
|
||||
sidechain_address_evaluator.cpp
|
||||
sidechain_transaction_evaluator.cpp
|
||||
|
||||
${HEADERS}
|
||||
${PROTOCOL_HEADERS}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include/graphene/chain/hardfork.hpp"
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ void_result account_create_evaluator::do_evaluate( const account_create_operatio
|
|||
}
|
||||
if( d.head_block_time() < HARDFORK_999_TIME )
|
||||
FC_ASSERT( !op.extensions.value.affiliate_distributions.valid(), "Affiliate reward distributions not allowed yet" );
|
||||
if (d.head_block_time() < HARDFORK_SON_TIME)
|
||||
FC_ASSERT(op.name != "son-account", "Son account creation before SON hardfork");
|
||||
|
||||
FC_ASSERT( fee_paying_account->is_lifetime_member(), "Only Lifetime members may register an account." );
|
||||
FC_ASSERT( op.referrer(d).is_member(d.head_block_time()), "The referrer must be either a lifetime or annual subscriber." );
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ void_result asset_create_evaluator::do_evaluate( const asset_create_operation& o
|
|||
|
||||
database& d = db();
|
||||
|
||||
if (d.head_block_time() < HARDFORK_SON_TIME)
|
||||
FC_ASSERT(op.symbol != "BTC", "BTC asset creation before SON hardfork");
|
||||
|
||||
const auto& chain_parameters = d.get_global_properties().parameters;
|
||||
FC_ASSERT( op.common_options.whitelist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );
|
||||
FC_ASSERT( op.common_options.blacklist_authorities.size() <= chain_parameters.maximum_asset_whitelist_authorities );
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ processed_transaction database::push_proposal(const proposal_object& proposal)
|
|||
auto session = _undo_db.start_undo_session(true);
|
||||
for( auto& op : proposal.proposed_transaction.operations )
|
||||
eval_state.operation_results.emplace_back(apply_operation(eval_state, op));
|
||||
remove_son_proposal(proposal);
|
||||
remove(proposal);
|
||||
session.merge();
|
||||
} catch ( const fc::exception& e ) {
|
||||
|
|
@ -698,8 +699,13 @@ void database::_apply_block( const signed_block& next_block )
|
|||
_current_virtual_op = 0;
|
||||
}
|
||||
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM)
|
||||
update_witness_schedule(next_block);
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM) {
|
||||
update_witness_schedule(next_block);
|
||||
if(global_props.active_sons.size() > 0) {
|
||||
update_son_schedule(next_block);
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t missed = update_witness_missed_blocks( next_block );
|
||||
update_global_dynamic_data( next_block, missed );
|
||||
update_signing_witness(signing_witness, next_block);
|
||||
|
|
@ -729,8 +735,13 @@ void database::_apply_block( const signed_block& next_block )
|
|||
// update_global_dynamic_data() as perhaps these methods only need
|
||||
// to be called for header validation?
|
||||
update_maintenance_flag( maint_needed );
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
update_witness_schedule();
|
||||
if (global_props.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM) {
|
||||
update_witness_schedule();
|
||||
if(global_props.active_sons.size() > 0) {
|
||||
update_son_schedule();
|
||||
}
|
||||
}
|
||||
|
||||
if( !_node_property_object.debug_updates.empty() )
|
||||
apply_debug_updates();
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,8 @@
|
|||
#include <graphene/chain/custom_account_authority_object.hpp>
|
||||
#include <graphene/chain/offer_object.hpp>
|
||||
#include <graphene/chain/account_role_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_proposal_object.hpp>
|
||||
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
|
|
@ -200,4 +202,119 @@ bool database::account_role_valid(const account_role_object &aro, account_id_typ
|
|||
(aro.whitelisted_accounts.find(account) != aro.whitelisted_accounts.end()) &&
|
||||
(!op_type || (aro.allowed_operations.find(*op_type) != aro.allowed_operations.end()));
|
||||
}
|
||||
std::set<son_id_type> database::get_sons_being_deregistered()
|
||||
{
|
||||
std::set<son_id_type> ret;
|
||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get< by_id >();
|
||||
|
||||
for( auto& son_proposal : son_proposal_idx )
|
||||
{
|
||||
if(son_proposal.proposal_type == son_proposal_type::son_deregister_proposal)
|
||||
{
|
||||
ret.insert(son_proposal.son_id);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::set<son_id_type> database::get_sons_to_be_deregistered()
|
||||
{
|
||||
std::set<son_id_type> ret;
|
||||
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
||||
|
||||
for( auto& son : son_idx )
|
||||
{
|
||||
if(son.status == son_status::in_maintenance)
|
||||
{
|
||||
auto stats = son.statistics(*this);
|
||||
// TODO : We need to add a function that returns if we can deregister SON
|
||||
// i.e. with introduction of PW code, we have to make a decision if the SON
|
||||
// is needed for release of funds from the PW
|
||||
if(head_block_time() - stats.last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time()))
|
||||
{
|
||||
ret.insert(son.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::set<son_id_type> database::get_sons_being_reported_down()
|
||||
{
|
||||
std::set<son_id_type> ret;
|
||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get< by_id >();
|
||||
|
||||
for( auto& son_proposal : son_proposal_idx )
|
||||
{
|
||||
if(son_proposal.proposal_type == son_proposal_type::son_report_down_proposal)
|
||||
{
|
||||
ret.insert(son_proposal.son_id);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
fc::optional<operation> database::create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son )
|
||||
{
|
||||
son_deregister_operation son_dereg_op;
|
||||
son_dereg_op.payer = get_global_properties().parameters.son_account();
|
||||
son_dereg_op.son_id = son_id;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = paying_son;
|
||||
proposal_op.proposed_ops.push_back( op_wrapper( son_dereg_op ) );
|
||||
uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_witnesses.size() ) * 3;
|
||||
proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );
|
||||
return proposal_op;
|
||||
}
|
||||
|
||||
signed_transaction database::create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op )
|
||||
{
|
||||
signed_transaction processed_trx;
|
||||
auto dyn_props = get_dynamic_global_properties();
|
||||
processed_trx.set_reference_block( dyn_props.head_block_id );
|
||||
processed_trx.set_expiration( head_block_time() + get_global_properties().parameters.maximum_time_until_expiration );
|
||||
processed_trx.operations.push_back( op );
|
||||
current_fee_schedule().set_fee( processed_trx.operations.back() );
|
||||
|
||||
processed_trx.sign( signing_private_key, get_chain_id() );
|
||||
|
||||
return processed_trx;
|
||||
}
|
||||
|
||||
bool database::is_son_dereg_valid( son_id_type son_id )
|
||||
{
|
||||
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
||||
auto son = son_idx.find( son_id );
|
||||
if(son == son_idx.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return (son->status == son_status::in_maintenance &&
|
||||
(head_block_time() - son->statistics(*this).last_down_timestamp >= fc::seconds(get_global_properties().parameters.son_deregister_time())));
|
||||
}
|
||||
|
||||
bool database::is_son_active( son_id_type son_id )
|
||||
{
|
||||
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
||||
auto son = son_idx.find( son_id );
|
||||
if(son == son_idx.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
vector<son_id_type> active_son_ids;
|
||||
active_son_ids.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), son_id);
|
||||
return (it_son != active_son_ids.end());
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -61,6 +61,13 @@
|
|||
#include <graphene/chain/event_object.hpp>
|
||||
#include <graphene/chain/betting_market_object.hpp>
|
||||
#include <graphene/chain/global_betting_statistics_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_proposal_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/son_wallet_deposit_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
|
||||
#include <graphene/chain/account_evaluator.hpp>
|
||||
#include <graphene/chain/asset_evaluator.hpp>
|
||||
|
|
@ -87,6 +94,12 @@
|
|||
#include <graphene/chain/offer_evaluator.hpp>
|
||||
#include <graphene/chain/nft_evaluator.hpp>
|
||||
#include <graphene/chain/account_role_evaluator.hpp>
|
||||
#include <graphene/chain/son_evaluator.hpp>
|
||||
#include <graphene/chain/son_wallet_evaluator.hpp>
|
||||
#include <graphene/chain/son_wallet_deposit_evaluator.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_evaluator.hpp>
|
||||
#include <graphene/chain/sidechain_address_evaluator.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
|
||||
|
|
@ -283,6 +296,25 @@ void database::initialize_evaluators()
|
|||
register_evaluator<account_role_create_evaluator>();
|
||||
register_evaluator<account_role_update_evaluator>();
|
||||
register_evaluator<account_role_delete_evaluator>();
|
||||
register_evaluator<create_son_evaluator>();
|
||||
register_evaluator<update_son_evaluator>();
|
||||
register_evaluator<deregister_son_evaluator>();
|
||||
register_evaluator<son_heartbeat_evaluator>();
|
||||
register_evaluator<son_report_down_evaluator>();
|
||||
register_evaluator<son_maintenance_evaluator>();
|
||||
register_evaluator<recreate_son_wallet_evaluator>();
|
||||
register_evaluator<update_son_wallet_evaluator>();
|
||||
register_evaluator<create_son_wallet_deposit_evaluator>();
|
||||
register_evaluator<process_son_wallet_deposit_evaluator>();
|
||||
register_evaluator<create_son_wallet_withdraw_evaluator>();
|
||||
register_evaluator<process_son_wallet_withdraw_evaluator>();
|
||||
register_evaluator<add_sidechain_address_evaluator>();
|
||||
register_evaluator<update_sidechain_address_evaluator>();
|
||||
register_evaluator<delete_sidechain_address_evaluator>();
|
||||
register_evaluator<sidechain_transaction_create_evaluator>();
|
||||
register_evaluator<sidechain_transaction_sign_evaluator>();
|
||||
register_evaluator<sidechain_transaction_send_evaluator>();
|
||||
register_evaluator<sidechain_transaction_settle_evaluator>();
|
||||
}
|
||||
|
||||
void database::initialize_indexes()
|
||||
|
|
@ -299,6 +331,7 @@ void database::initialize_indexes()
|
|||
acnt_index->add_secondary_index<account_referrer_index>();
|
||||
|
||||
add_index< primary_index<committee_member_index, 8> >(); // 256 members per chunk
|
||||
add_index< primary_index<son_index> >();
|
||||
add_index< primary_index<witness_index, 10> >(); // 1024 witnesses per chunk
|
||||
add_index< primary_index<limit_order_index > >();
|
||||
add_index< primary_index<call_order_index > >();
|
||||
|
|
@ -332,6 +365,14 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<nft_metadata_index > >();
|
||||
add_index< primary_index<nft_index > >();
|
||||
add_index< primary_index<account_role_index> >();
|
||||
add_index< primary_index<son_proposal_index> >();
|
||||
|
||||
add_index< primary_index<son_wallet_index> >();
|
||||
add_index< primary_index<son_wallet_deposit_index> >();
|
||||
add_index< primary_index<son_wallet_withdraw_index> >();
|
||||
|
||||
add_index< primary_index<sidechain_address_index> >();
|
||||
add_index< primary_index<sidechain_transaction_index> >();
|
||||
|
||||
//Implementation object indexes
|
||||
add_index< primary_index<transaction_index > >();
|
||||
|
|
@ -348,6 +389,7 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<flat_index< block_summary_object >> >();
|
||||
add_index< primary_index<simple_index<chain_property_object > > >();
|
||||
add_index< primary_index<simple_index<witness_schedule_object > > >();
|
||||
add_index< primary_index<simple_index<son_schedule_object > > >();
|
||||
add_index< primary_index<simple_index<budget_record_object > > >();
|
||||
add_index< primary_index< special_authority_index > >();
|
||||
add_index< primary_index< buyback_index > >();
|
||||
|
|
@ -362,6 +404,7 @@ void database::initialize_indexes()
|
|||
add_index< primary_index<lottery_balance_index > >();
|
||||
add_index< primary_index<sweeps_vesting_balance_index > >();
|
||||
add_index< primary_index<offer_history_index > >();
|
||||
add_index< primary_index<son_stats_index > >();
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -1006,6 +1049,29 @@ void database::init_genesis(const genesis_state_type& genesis_state)
|
|||
});
|
||||
FC_ASSERT( _p_witness_schedule_obj->id == witness_schedule_id_type() );
|
||||
|
||||
// Initialize witness schedule
|
||||
#ifndef NDEBUG
|
||||
const son_schedule_object& sso =
|
||||
#endif
|
||||
create<son_schedule_object>([&](son_schedule_object& _sso)
|
||||
{
|
||||
// for scheduled
|
||||
memset(_sso.rng_seed.begin(), 0, _sso.rng_seed.size());
|
||||
|
||||
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||
|
||||
auto init_witnesses = get_global_properties().active_witnesses;
|
||||
|
||||
_sso.scheduler = son_scheduler();
|
||||
_sso.scheduler._min_token_count = std::max(int(init_witnesses.size()) / 2, 1);
|
||||
|
||||
|
||||
_sso.last_scheduling_block = 0;
|
||||
|
||||
_sso.recent_slots_filled = fc::uint128::max_value();
|
||||
});
|
||||
assert( sso.id == son_schedule_id_type() );
|
||||
|
||||
// Enable fees
|
||||
modify(get_global_properties(), [&genesis_state](global_property_object& p) {
|
||||
p.parameters.current_fees = genesis_state.initial_parameters.current_fees;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
#include <graphene/chain/global_property_object.hpp>
|
||||
#include <graphene/chain/market_object.hpp>
|
||||
#include <graphene/chain/special_authority_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
#include <graphene/chain/vote_count.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
|
|
@ -77,6 +78,32 @@ vector<std::reference_wrapper<const typename Index::object_type>> database::sort
|
|||
return refs;
|
||||
}
|
||||
|
||||
template<>
|
||||
vector<std::reference_wrapper<const son_object>> database::sort_votable_objects<son_index>(size_t count) const
|
||||
{
|
||||
const auto& all_sons = get_index_type<son_index>().indices().get< by_id >();
|
||||
std::vector<std::reference_wrapper<const son_object>> refs;
|
||||
for( auto& son : all_sons )
|
||||
{
|
||||
if(son.has_valid_config() && son.status != son_status::deregistered)
|
||||
{
|
||||
refs.push_back(std::cref(son));
|
||||
}
|
||||
}
|
||||
count = std::min(count, refs.size());
|
||||
std::partial_sort(refs.begin(), refs.begin() + count, refs.end(),
|
||||
[this](const son_object& a, const son_object& b)->bool {
|
||||
share_type oa_vote = _vote_tally_buffer[a.vote_id];
|
||||
share_type ob_vote = _vote_tally_buffer[b.vote_id];
|
||||
if( oa_vote != ob_vote )
|
||||
return oa_vote > ob_vote;
|
||||
return a.vote_id < b.vote_id;
|
||||
});
|
||||
|
||||
refs.resize(count, refs.front());
|
||||
return refs;
|
||||
}
|
||||
|
||||
template<class Type>
|
||||
void database::perform_account_maintenance(Type tally_helper)
|
||||
{
|
||||
|
|
@ -150,6 +177,215 @@ void database::update_worker_votes()
|
|||
}
|
||||
}
|
||||
|
||||
void database::pay_sons()
|
||||
{
|
||||
auto get_weight = []( uint64_t total_votes ) {
|
||||
int8_t bits_to_drop = std::max(int(boost::multiprecision::detail::find_msb(total_votes)) - 15, 0);
|
||||
uint16_t weight = std::max((total_votes >> bits_to_drop), uint64_t(1) );
|
||||
return weight;
|
||||
};
|
||||
time_point_sec now = head_block_time();
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
// Current requirement is that we have to pay every 24 hours, so the following check
|
||||
if( dpo.son_budget.value > 0 && ((now - dpo.last_son_payout_time) >= fc::seconds(get_global_properties().parameters.son_pay_time()))) {
|
||||
uint64_t weighted_total_txs_signed = 0;
|
||||
share_type son_budget = dpo.son_budget;
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &get_weight](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( s.owner );
|
||||
auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]);
|
||||
weighted_total_txs_signed += (s.txs_signed * son_weight);
|
||||
});
|
||||
|
||||
|
||||
// Now pay off each SON proportional to the number of transactions signed.
|
||||
get_index_type<son_stats_index>().inspect_all_objects([this, &weighted_total_txs_signed, &dpo, &son_budget, &get_weight](const object& o) {
|
||||
const son_statistics_object& s = static_cast<const son_statistics_object&>(o);
|
||||
if(s.txs_signed > 0){
|
||||
auto son_params = get_global_properties().parameters;
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find( s.owner );
|
||||
auto son_weight = get_weight(_vote_tally_buffer[son_obj->vote_id]);
|
||||
share_type pay = (s.txs_signed * son_weight * son_budget.value)/weighted_total_txs_signed;
|
||||
|
||||
modify( *son_obj, [&]( son_object& _son_obj)
|
||||
{
|
||||
_son_obj.pay_son_fee(pay, *this);
|
||||
});
|
||||
//Remove the amount paid out to SON from global SON Budget
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.son_budget -= pay;
|
||||
} );
|
||||
//Reset the tx counter in each son statistics object
|
||||
modify( s, [&]( son_statistics_object& _s)
|
||||
{
|
||||
_s.total_txs_signed += _s.txs_signed;
|
||||
_s.txs_signed = 0;
|
||||
});
|
||||
}
|
||||
});
|
||||
//Note the last son pay out time
|
||||
modify( dpo, [&]( dynamic_global_property_object& _dpo )
|
||||
{
|
||||
_dpo.last_son_payout_time = now;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_son_metrics(const vector<son_info>& curr_active_sons)
|
||||
{
|
||||
vector<son_id_type> current_sons;
|
||||
|
||||
current_sons.reserve(curr_active_sons.size());
|
||||
std::transform(curr_active_sons.begin(), curr_active_sons.end(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
const auto& son_idx = get_index_type<son_index>().indices().get< by_id >();
|
||||
for( auto& son : son_idx )
|
||||
{
|
||||
auto& stats = son.statistics(*this);
|
||||
bool is_active_son = (std::find(current_sons.begin(), current_sons.end(), son.id) != current_sons.end());
|
||||
modify( stats, [&]( son_statistics_object& _stats )
|
||||
{
|
||||
_stats.total_downtime += _stats.current_interval_downtime;
|
||||
_stats.current_interval_downtime = 0;
|
||||
if(is_active_son)
|
||||
{
|
||||
_stats.total_voted_time = _stats.total_voted_time + get_global_properties().parameters.maintenance_interval;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_son_statuses(const vector<son_info>& curr_active_sons, const vector<son_info>& new_active_sons)
|
||||
{
|
||||
vector<son_id_type> current_sons, new_sons;
|
||||
vector<son_id_type> sons_to_remove, sons_to_add;
|
||||
const auto& idx = get_index_type<son_index>().indices().get<by_id>();
|
||||
|
||||
current_sons.reserve(curr_active_sons.size());
|
||||
std::transform(curr_active_sons.begin(), curr_active_sons.end(),
|
||||
std::inserter(current_sons, current_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
new_sons.reserve(new_active_sons.size());
|
||||
std::transform(new_active_sons.begin(), new_active_sons.end(),
|
||||
std::inserter(new_sons, new_sons.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
// find all cur_active_sons members that is not in new_active_sons
|
||||
for_each(current_sons.begin(), current_sons.end(),
|
||||
[&sons_to_remove, &new_sons](const son_id_type& si)
|
||||
{
|
||||
if(std::find(new_sons.begin(), new_sons.end(), si) ==
|
||||
new_sons.end())
|
||||
{
|
||||
sons_to_remove.push_back(si);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
for( const auto& sid : sons_to_remove )
|
||||
{
|
||||
auto son = idx.find( sid );
|
||||
if(son == idx.end()) // SON is deleted already
|
||||
continue;
|
||||
// keep maintenance status for nodes becoming inactive
|
||||
if(son->status == son_status::active)
|
||||
{
|
||||
modify( *son, [&]( son_object& obj ){
|
||||
obj.status = son_status::inactive;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// find all new_active_sons members that is not in cur_active_sons
|
||||
for_each(new_sons.begin(), new_sons.end(),
|
||||
[&sons_to_add, ¤t_sons](const son_id_type& si)
|
||||
{
|
||||
if(std::find(current_sons.begin(), current_sons.end(), si) ==
|
||||
current_sons.end())
|
||||
{
|
||||
sons_to_add.push_back(si);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
for( const auto& sid : sons_to_add )
|
||||
{
|
||||
auto son = idx.find( sid );
|
||||
FC_ASSERT(son != idx.end(), "Invalid SON in active list, id={sonid}.", ("sonid", sid));
|
||||
// keep maintenance status for new nodes
|
||||
if(son->status == son_status::inactive)
|
||||
{
|
||||
modify( *son, [&]( son_object& obj ){
|
||||
obj.status = son_status::active;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ilog("New SONS");
|
||||
for(size_t i = 0; i < new_sons.size(); i++) {
|
||||
auto son = idx.find( new_sons[i] );
|
||||
if(son == idx.end()) // SON is deleted already
|
||||
continue;
|
||||
ilog( "${s}, status = ${ss}, total_votes = ${sv}", ("s", new_sons[i])("ss", son->status)("sv", son->total_votes) );
|
||||
}
|
||||
|
||||
if( sons_to_remove.size() > 0 )
|
||||
{
|
||||
remove_inactive_son_proposals(sons_to_remove);
|
||||
}
|
||||
}
|
||||
|
||||
void database::update_son_wallet(const vector<son_info>& new_active_sons)
|
||||
{
|
||||
bool should_recreate_pw = true;
|
||||
|
||||
// Expire for current son_wallet_object wallet, if exists
|
||||
const auto& idx_swi = get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto obj = idx_swi.rbegin();
|
||||
if (obj != idx_swi.rend()) {
|
||||
// Compare current wallet SONs and to-be lists of active sons
|
||||
auto cur_wallet_sons = (*obj).sons;
|
||||
|
||||
bool wallet_son_sets_equal = (cur_wallet_sons.size() == new_active_sons.size());
|
||||
if (wallet_son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) {
|
||||
wallet_son_sets_equal = wallet_son_sets_equal && cur_wallet_sons.at(i) == new_active_sons.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
should_recreate_pw = !wallet_son_sets_equal;
|
||||
|
||||
if (should_recreate_pw) {
|
||||
modify(*obj, [&, obj](son_wallet_object &swo) {
|
||||
swo.expires = head_block_time();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
should_recreate_pw = should_recreate_pw && (new_active_sons.size() >= get_chain_properties().immutable_parameters.min_son_count);
|
||||
|
||||
if (should_recreate_pw) {
|
||||
// Create new son_wallet_object, to initiate wallet recreation
|
||||
create<son_wallet_object>( [&]( son_wallet_object& obj ) {
|
||||
obj.valid_from = head_block_time();
|
||||
obj.expires = time_point_sec::maximum();
|
||||
obj.sons.insert(obj.sons.end(), new_active_sons.begin(), new_active_sons.end());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void database::pay_workers( share_type& budget )
|
||||
{
|
||||
const auto head_time = head_block_time();
|
||||
|
|
@ -207,7 +443,7 @@ void database::update_active_witnesses()
|
|||
/// accounts that vote for 0 or 1 witness do not get to express an opinion on
|
||||
/// the number of witnesses to have (they abstain and are non-voting accounts)
|
||||
|
||||
share_type stake_tally = 0;
|
||||
share_type stake_tally = 0;
|
||||
|
||||
size_t witness_count = 0;
|
||||
if( stake_target > 0 )
|
||||
|
|
@ -306,7 +542,7 @@ void database::update_active_witnesses()
|
|||
void database::update_active_committee_members()
|
||||
{ try {
|
||||
assert( _committee_count_histogram_buffer.size() > 0 );
|
||||
share_type stake_target = (_total_voting_stake-_witness_count_histogram_buffer[0]) / 2;
|
||||
share_type stake_target = (_total_voting_stake-_committee_count_histogram_buffer[0]) / 2;
|
||||
|
||||
/// accounts that vote for 0 or 1 witness do not get to express an opinion on
|
||||
/// the number of witnesses to have (they abstain and are non-voting accounts)
|
||||
|
|
@ -395,6 +631,146 @@ void database::update_active_committee_members()
|
|||
});
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::update_active_sons()
|
||||
{ try {
|
||||
assert( _son_count_histogram_buffer.size() > 0 );
|
||||
share_type stake_target = (_total_voting_stake-_son_count_histogram_buffer[0]) / 2;
|
||||
|
||||
/// accounts that vote for 0 or 1 son do not get to express an opinion on
|
||||
/// the number of sons to have (they abstain and are non-voting accounts)
|
||||
|
||||
share_type stake_tally = 0;
|
||||
|
||||
size_t son_count = 0;
|
||||
if( stake_target > 0 )
|
||||
{
|
||||
while( (son_count < _son_count_histogram_buffer.size() - 1)
|
||||
&& (stake_tally <= stake_target) )
|
||||
{
|
||||
stake_tally += _son_count_histogram_buffer[++son_count];
|
||||
}
|
||||
}
|
||||
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
const chain_parameters& cp = gpo.parameters;
|
||||
auto sons = sort_votable_objects<son_index>(cp.maximum_son_count);
|
||||
|
||||
const auto& all_sons = get_index_type<son_index>().indices();
|
||||
|
||||
auto& local_vote_buffer_ref = _vote_tally_buffer;
|
||||
for( const son_object& son : all_sons )
|
||||
{
|
||||
if(son.status == son_status::request_maintenance)
|
||||
{
|
||||
auto& stats = son.statistics(*this);
|
||||
modify( stats, [&]( son_statistics_object& _s){
|
||||
_s.last_down_timestamp = head_block_time();
|
||||
});
|
||||
}
|
||||
modify( son, [local_vote_buffer_ref]( son_object& obj ){
|
||||
obj.total_votes = local_vote_buffer_ref[obj.vote_id];
|
||||
if(obj.status == son_status::request_maintenance)
|
||||
obj.status = son_status::in_maintenance;
|
||||
});
|
||||
}
|
||||
|
||||
// Update SON authority
|
||||
if( gpo.parameters.son_account() != GRAPHENE_NULL_ACCOUNT )
|
||||
{
|
||||
modify( get(gpo.parameters.son_account()), [&]( account_object& a )
|
||||
{
|
||||
if( head_block_time() < HARDFORK_533_TIME )
|
||||
{
|
||||
map<account_id_type, uint64_t> weights;
|
||||
a.active.weight_threshold = 0;
|
||||
a.active.account_auths.clear();
|
||||
|
||||
for( const son_object& son : sons )
|
||||
{
|
||||
weights.emplace(son.son_account, uint64_t(1));
|
||||
}
|
||||
|
||||
for( const auto& weight : weights )
|
||||
{
|
||||
// Ensure that everyone has at least one vote. Zero weights aren't allowed.
|
||||
a.active.account_auths[weight.first] += 1;
|
||||
a.active.weight_threshold += 1;
|
||||
}
|
||||
|
||||
a.active.weight_threshold *= 2;
|
||||
a.active.weight_threshold /= 3;
|
||||
a.active.weight_threshold += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
vote_counter vc;
|
||||
for( const son_object& son : sons )
|
||||
vc.add( son.son_account, UINT64_C(1) );
|
||||
vc.finish_2_3( a.active );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
// Compare current and to-be lists of active sons
|
||||
auto cur_active_sons = gpo.active_sons;
|
||||
vector<son_info> new_active_sons;
|
||||
const auto &acc = get(gpo.parameters.son_account());
|
||||
for( const son_object& son : sons ) {
|
||||
son_info swi;
|
||||
swi.son_id = son.id;
|
||||
swi.weight = acc.active.account_auths.at(son.son_account);
|
||||
swi.signing_key = son.signing_key;
|
||||
swi.sidechain_public_keys = son.sidechain_public_keys;
|
||||
new_active_sons.push_back(swi);
|
||||
}
|
||||
|
||||
bool son_sets_equal = (cur_active_sons.size() == new_active_sons.size());
|
||||
if (son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_active_sons.size(); i++ ) {
|
||||
son_sets_equal = son_sets_equal && cur_active_sons.at(i) == new_active_sons.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (son_sets_equal) {
|
||||
ilog( "Active SONs set NOT CHANGED" );
|
||||
} else {
|
||||
ilog( "Active SONs set CHANGED" );
|
||||
|
||||
update_son_wallet(new_active_sons);
|
||||
update_son_statuses(cur_active_sons, new_active_sons);
|
||||
}
|
||||
|
||||
// Update son performance metrics
|
||||
update_son_metrics(cur_active_sons);
|
||||
|
||||
modify(gpo, [&]( global_property_object& gp ){
|
||||
gp.active_sons.clear();
|
||||
gp.active_sons.reserve(new_active_sons.size());
|
||||
gp.active_sons.insert(gp.active_sons.end(), new_active_sons.begin(), new_active_sons.end());
|
||||
});
|
||||
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
modify(sso, [&](son_schedule_object& _sso)
|
||||
{
|
||||
flat_set<son_id_type> active_sons;
|
||||
active_sons.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(active_sons, active_sons.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
_sso.scheduler.update(active_sons);
|
||||
// similar to witness, produce schedule for sons
|
||||
if(cur_active_sons.size() == 0 && new_active_sons.size() > 0)
|
||||
{
|
||||
witness_scheduler_rng rng(_sso.rng_seed.begin(), GRAPHENE_NEAR_SCHEDULE_CTR_IV);
|
||||
for( size_t i=0; i<new_active_sons.size(); ++i )
|
||||
_sso.scheduler.produce_schedule(rng);
|
||||
}
|
||||
});
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::initialize_budget_record( fc::time_point_sec now, budget_record& rec )const
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
|
|
@ -483,6 +859,17 @@ void database::process_budget()
|
|||
rec.witness_budget = witness_budget;
|
||||
available_funds -= witness_budget;
|
||||
|
||||
// We should not factor-in the son budget before SON HARDFORK
|
||||
share_type son_budget = 0;
|
||||
if(now >= HARDFORK_SON_TIME){
|
||||
rec.leftover_son_funds = dpo.son_budget;
|
||||
available_funds += rec.leftover_son_funds;
|
||||
son_budget = gpo.parameters.son_pay_max();
|
||||
son_budget = std::min(son_budget, available_funds);
|
||||
rec.son_budget = son_budget;
|
||||
available_funds -= son_budget;
|
||||
}
|
||||
|
||||
fc::uint128_t worker_budget_u128 = gpo.parameters.worker_budget_per_day.value;
|
||||
worker_budget_u128 *= uint64_t(time_to_maint);
|
||||
worker_budget_u128 /= 60*60*24;
|
||||
|
|
@ -502,9 +889,11 @@ void database::process_budget()
|
|||
|
||||
rec.supply_delta = rec.witness_budget
|
||||
+ rec.worker_budget
|
||||
+ rec.son_budget
|
||||
- rec.leftover_worker_funds
|
||||
- rec.from_accumulated_fees
|
||||
- rec.from_unused_witness_budget;
|
||||
- rec.from_unused_witness_budget
|
||||
- rec.leftover_son_funds;
|
||||
|
||||
modify(core, [&]( asset_dynamic_data_object& _core )
|
||||
{
|
||||
|
|
@ -513,9 +902,11 @@ void database::process_budget()
|
|||
assert( rec.supply_delta ==
|
||||
witness_budget
|
||||
+ worker_budget
|
||||
+ son_budget
|
||||
- leftover_worker_funds
|
||||
- _core.accumulated_fees
|
||||
- dpo.witness_budget
|
||||
- dpo.son_budget
|
||||
);
|
||||
_core.accumulated_fees = 0;
|
||||
});
|
||||
|
|
@ -526,6 +917,7 @@ void database::process_budget()
|
|||
// available_funds, we replace it with witness_budget
|
||||
// instead of adding it.
|
||||
_dpo.witness_budget = witness_budget;
|
||||
_dpo.son_budget = son_budget;
|
||||
_dpo.last_budget_time = now;
|
||||
});
|
||||
|
||||
|
|
@ -954,10 +1346,10 @@ void clear_expired_account_roles(database& db)
|
|||
// dividend-paying asset. This takes any deposits made to the dividend distribution account
|
||||
// since the last time it was called, and distributes them to the current owners of the
|
||||
// dividend-paying asset according to the amount they own.
|
||||
void schedule_pending_dividend_balances(database& db,
|
||||
void schedule_pending_dividend_balances(database& db,
|
||||
const asset_object& dividend_holder_asset_obj,
|
||||
const asset_dividend_data_object& dividend_data,
|
||||
const fc::time_point_sec& current_head_block_time,
|
||||
const fc::time_point_sec& current_head_block_time,
|
||||
const account_balance_index& balance_index,
|
||||
const vesting_balance_index& vesting_index,
|
||||
const total_distributed_dividend_balance_object_index& distributed_dividend_balance_index,
|
||||
|
|
@ -966,7 +1358,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
dlog("Processing dividend payments for dividend holder asset type ${holder_asset} at time ${t}",
|
||||
("holder_asset", dividend_holder_asset_obj.symbol)("t", db.head_block_time()));
|
||||
auto balance_by_acc_index = db.get_index_type< primary_index< account_balance_index > >().get_secondary_index< balances_by_account_index >();
|
||||
auto current_distribution_account_balance_range =
|
||||
auto current_distribution_account_balance_range =
|
||||
//balance_index.indices().get<by_account_asset>().equal_range(boost::make_tuple(dividend_data.dividend_distribution_account));
|
||||
balance_by_acc_index.get_account_balances(dividend_data.dividend_distribution_account);
|
||||
auto previous_distribution_account_balance_range =
|
||||
|
|
@ -977,7 +1369,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
const auto& gpo = db.get_global_properties();
|
||||
|
||||
// get the list of accounts that hold nonzero balances of the dividend asset
|
||||
auto holder_balances_begin =
|
||||
auto holder_balances_begin =
|
||||
balance_index.indices().get<by_asset_balance>().lower_bound(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
auto holder_balances_end =
|
||||
balance_index.indices().get<by_asset_balance>().upper_bound(boost::make_tuple(dividend_holder_asset_obj.id, share_type()));
|
||||
|
|
@ -1037,7 +1429,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
("previous", (int64_t)std::distance(previous_distribution_account_balance_range.first, previous_distribution_account_balance_range.second)));
|
||||
|
||||
// when we pay out the dividends to the holders, we need to know the total balance of the dividend asset in all
|
||||
// accounts other than the distribution account (it would be silly to distribute dividends back to
|
||||
// accounts other than the distribution account (it would be silly to distribute dividends back to
|
||||
// the distribution account)
|
||||
share_type total_balance_of_dividend_asset;
|
||||
if(db.head_block_time() >= HARDFORK_GPOS_TIME && dividend_holder_asset_obj.symbol == GRAPHENE_SYMBOL) { // only core
|
||||
|
|
@ -1068,7 +1460,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
share_type previous_balance;
|
||||
asset_id_type payout_asset_type;
|
||||
|
||||
if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second ||
|
||||
if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second ||
|
||||
current_distribution_account_balance_iter->second->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type)
|
||||
{
|
||||
// there are no more previous balances or there is no previous balance for this particular asset type
|
||||
|
|
@ -1076,7 +1468,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
current_balance = current_distribution_account_balance_iter->second->balance;
|
||||
idump((payout_asset_type)(current_balance));
|
||||
}
|
||||
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() ||
|
||||
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() ||
|
||||
previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->second->asset_type)
|
||||
{
|
||||
// there are no more current balances or there is no current balance for this particular previous asset type
|
||||
|
|
@ -1095,7 +1487,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
|
||||
share_type delta_balance = current_balance - previous_balance;
|
||||
|
||||
// Next, figure out if we want to share this out -- if the amount added to the distribution
|
||||
// Next, figure out if we want to share this out -- if the amount added to the distribution
|
||||
// account since last payout is too small, we won't bother.
|
||||
|
||||
share_type total_fee_per_asset_in_payout_asset;
|
||||
|
|
@ -1104,7 +1496,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
{
|
||||
payout_asset_object = &db.get_core_asset();
|
||||
total_fee_per_asset_in_payout_asset = total_fee_per_asset_in_core;
|
||||
dlog("Fee for distributing ${payout_asset_type}: ${fee}",
|
||||
dlog("Fee for distributing ${payout_asset_type}: ${fee}",
|
||||
("payout_asset_type", asset_id_type()(db).symbol)
|
||||
("fee", asset(total_fee_per_asset_in_core, asset_id_type())));
|
||||
}
|
||||
|
|
@ -1120,7 +1512,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
FC_ASSERT(total_fee_per_asset.asset_id == payout_asset_type);
|
||||
|
||||
total_fee_per_asset_in_payout_asset = total_fee_per_asset.amount;
|
||||
dlog("Fee for distributing ${payout_asset_type}: ${fee}",
|
||||
dlog("Fee for distributing ${payout_asset_type}: ${fee}",
|
||||
("payout_asset_type", payout_asset_type(db).symbol)("fee", total_fee_per_asset_in_payout_asset));
|
||||
}
|
||||
|
||||
|
|
@ -1133,7 +1525,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
wdump((total_fee_per_asset_in_payout_asset)(dividend_data.options));
|
||||
minimum_shares_to_distribute = minimum_amount_to_distribute.to_uint64();
|
||||
}
|
||||
|
||||
|
||||
dlog("Processing dividend payments of asset type ${payout_asset_type}, delta balance is ${delta_balance}", ("payout_asset_type", payout_asset_type(db).symbol)("delta_balance", delta_balance));
|
||||
if (delta_balance > 0)
|
||||
{
|
||||
|
|
@ -1146,7 +1538,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
db.modify(asset_dynamic_data_id_type()(db), [total_fee_per_asset_in_core](asset_dynamic_data_object& d) {
|
||||
d.accumulated_fees += total_fee_per_asset_in_core;
|
||||
});
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-total_fee_per_asset_in_core, asset_id_type()));
|
||||
delta_balance -= total_fee_per_asset_in_core;
|
||||
}
|
||||
|
|
@ -1161,7 +1553,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
("need", asset(total_fee_per_asset_in_core, asset_id_type()))
|
||||
("have", asset(dynamic_data.fee_pool, payout_asset_type)));
|
||||
// deduct the fee from the dividend distribution account
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-total_fee_per_asset_in_payout_asset, payout_asset_type));
|
||||
// convert it to core
|
||||
db.modify(payout_asset_object->dynamic_data(db), [total_fee_per_asset_in_core, total_fee_per_asset_in_payout_asset](asset_dynamic_data_object& d) {
|
||||
|
|
@ -1175,7 +1567,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
delta_balance -= total_fee_per_asset_in_payout_asset;
|
||||
}
|
||||
|
||||
dlog("There are ${count} holders of the dividend-paying asset, with a total balance of ${total}",
|
||||
dlog("There are ${count} holders of the dividend-paying asset, with a total balance of ${total}",
|
||||
("count", holder_account_count)
|
||||
("total", total_balance_of_dividend_asset));
|
||||
share_type remaining_amount_to_distribute = delta_balance;
|
||||
|
|
@ -1247,7 +1639,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
dlog("Pending payout: ${account_name} -> ${amount}",
|
||||
("account_name", pending_payout.owner(db).name)
|
||||
("amount", asset(pending_payout.pending_balance, pending_payout.dividend_payout_asset_type)));
|
||||
dlog("Remaining balance not paid out: ${amount}",
|
||||
dlog("Remaining balance not paid out: ${amount}",
|
||||
("amount", asset(remaining_amount_to_distribute, payout_asset_type)));
|
||||
|
||||
share_type distributed_amount = delta_balance - remaining_amount_to_distribute;
|
||||
|
|
@ -1277,7 +1669,7 @@ void schedule_pending_dividend_balances(database& db,
|
|||
// This should be extremely rare (caused by an override transfer by the asset owner).
|
||||
// Reduce all pending payouts proportionally
|
||||
share_type total_pending_balances;
|
||||
auto pending_payouts_range =
|
||||
auto pending_payouts_range =
|
||||
pending_payout_balance_index.indices().get<by_dividend_payout_account>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id, payout_asset_type));
|
||||
|
||||
for (const pending_dividend_payout_balance_for_holder_object& pending_balance_object : boost::make_iterator_range(pending_payouts_range.first, pending_payouts_range.second))
|
||||
|
|
@ -1314,10 +1706,10 @@ void schedule_pending_dividend_balances(database& db,
|
|||
}
|
||||
|
||||
// iterate
|
||||
if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second ||
|
||||
if (previous_distribution_account_balance_iter == previous_distribution_account_balance_range.second ||
|
||||
current_distribution_account_balance_iter->second->asset_type < previous_distribution_account_balance_iter->dividend_payout_asset_type)
|
||||
++current_distribution_account_balance_iter;
|
||||
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() ||
|
||||
else if (current_distribution_account_balance_iter == current_distribution_account_balance_range.end() ||
|
||||
previous_distribution_account_balance_iter->dividend_payout_asset_type < current_distribution_account_balance_iter->second->asset_type)
|
||||
++previous_distribution_account_balance_iter;
|
||||
else
|
||||
|
|
@ -1359,7 +1751,7 @@ void process_dividend_assets(database& db)
|
|||
{
|
||||
try
|
||||
{
|
||||
dlog("Dividend payout time has arrived for asset ${holder_asset}",
|
||||
dlog("Dividend payout time has arrived for asset ${holder_asset}",
|
||||
("holder_asset", dividend_holder_asset_obj.symbol));
|
||||
#ifndef NDEBUG
|
||||
// dump balances before the payouts for debugging
|
||||
|
|
@ -1371,14 +1763,14 @@ void process_dividend_assets(database& db)
|
|||
|
||||
// when we do the payouts, we first increase the balances in all of the receiving accounts
|
||||
// and use this map to keep track of the total amount of each asset paid out.
|
||||
// Afterwards, we decrease the distribution account's balance by the total amount paid out,
|
||||
// Afterwards, we decrease the distribution account's balance by the total amount paid out,
|
||||
// and modify the distributed_balances accordingly
|
||||
std::map<asset_id_type, share_type> amounts_paid_out_by_asset;
|
||||
|
||||
auto pending_payouts_range =
|
||||
auto pending_payouts_range =
|
||||
pending_payout_balance_index.indices().get<by_dividend_account_payout>().equal_range(boost::make_tuple(dividend_holder_asset_obj.id));
|
||||
// the pending_payouts_range is all payouts for this dividend asset, sorted by the holder's account
|
||||
// we iterate in this order so we can build up a list of payouts for each account to put in the
|
||||
// we iterate in this order so we can build up a list of payouts for each account to put in the
|
||||
// virtual op
|
||||
vector<asset> payouts_for_this_holder;
|
||||
fc::optional<account_id_type> last_holder_account_id;
|
||||
|
|
@ -1390,7 +1782,7 @@ void process_dividend_assets(database& db)
|
|||
auto approved_assets_iter = approved_assets.find(asset_id);
|
||||
if (approved_assets_iter != approved_assets.end())
|
||||
return approved_assets_iter->second;
|
||||
bool is_approved = is_authorized_asset(db, dividend_distribution_account_object,
|
||||
bool is_approved = is_authorized_asset(db, dividend_distribution_account_object,
|
||||
asset_id(db));
|
||||
approved_assets[asset_id] = is_approved;
|
||||
return is_approved;
|
||||
|
|
@ -1403,8 +1795,8 @@ void process_dividend_assets(database& db)
|
|||
if (last_holder_account_id && *last_holder_account_id != pending_balance_object.owner && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
payouts_for_this_holder.clear();
|
||||
|
|
@ -1416,14 +1808,14 @@ void process_dividend_assets(database& db)
|
|||
is_authorized_asset(db, pending_balance_object.owner(db), pending_balance_object.dividend_payout_asset_type(db)) &&
|
||||
is_asset_approved_for_distribution_account(pending_balance_object.dividend_payout_asset_type))
|
||||
{
|
||||
dlog("Processing payout of ${asset} to account ${account}",
|
||||
dlog("Processing payout of ${asset} to account ${account}",
|
||||
("asset", asset(pending_balance_object.pending_balance, pending_balance_object.dividend_payout_asset_type))
|
||||
("account", pending_balance_object.owner(db).name));
|
||||
|
||||
db.adjust_balance(pending_balance_object.owner,
|
||||
asset(pending_balance_object.pending_balance,
|
||||
asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
payouts_for_this_holder.push_back(asset(pending_balance_object.pending_balance,
|
||||
payouts_for_this_holder.push_back(asset(pending_balance_object.pending_balance,
|
||||
pending_balance_object.dividend_payout_asset_type));
|
||||
last_holder_account_id = pending_balance_object.owner;
|
||||
amounts_paid_out_by_asset[pending_balance_object.dividend_payout_asset_type] += pending_balance_object.pending_balance;
|
||||
|
|
@ -1439,8 +1831,8 @@ void process_dividend_assets(database& db)
|
|||
if (last_holder_account_id && payouts_for_this_holder.size())
|
||||
{
|
||||
// we've moved on to a new account, generate the dividend payment virtual op for the previous one
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
db.push_applied_operation(asset_dividend_distribution_operation(dividend_holder_asset_obj.id,
|
||||
*last_holder_account_id,
|
||||
payouts_for_this_holder));
|
||||
dlog("Just pushed virtual op for payout to ${account}", ("account", (*last_holder_account_id)(db).name));
|
||||
}
|
||||
|
|
@ -1453,11 +1845,11 @@ void process_dividend_assets(database& db)
|
|||
const asset_id_type& asset_paid_out = value.first;
|
||||
const share_type& amount_paid_out = value.second;
|
||||
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
db.adjust_balance(dividend_data.dividend_distribution_account,
|
||||
asset(-amount_paid_out,
|
||||
asset_paid_out));
|
||||
auto distributed_balance_iter =
|
||||
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().find(boost::make_tuple(dividend_holder_asset_obj.id,
|
||||
auto distributed_balance_iter =
|
||||
distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().find(boost::make_tuple(dividend_holder_asset_obj.id,
|
||||
asset_paid_out));
|
||||
assert(distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end());
|
||||
if (distributed_balance_iter != distributed_dividend_balance_index.indices().get<by_dividend_payout_asset>().end())
|
||||
|
|
@ -1474,7 +1866,7 @@ void process_dividend_assets(database& db)
|
|||
fc::optional<fc::time_point_sec> next_payout_time;
|
||||
if (dividend_data_obj.options.payout_interval)
|
||||
{
|
||||
// if there was a previous payout, make our next payment one interval
|
||||
// if there was a previous payout, make our next payment one interval
|
||||
uint32_t current_time_sec = current_head_block_time.sec_since_epoch();
|
||||
fc::time_point_sec reference_time = *dividend_data_obj.last_scheduled_payout_time;
|
||||
uint32_t next_possible_time_sec = dividend_data_obj.last_scheduled_payout_time->sec_since_epoch();
|
||||
|
|
@ -1489,12 +1881,84 @@ void process_dividend_assets(database& db)
|
|||
(dividend_data_obj.last_payout_time)
|
||||
(dividend_data_obj.options.next_payout_time));
|
||||
});
|
||||
}
|
||||
}
|
||||
FC_RETHROW_EXCEPTIONS(error, "Error while paying out dividends for holder asset ${holder_asset}", ("holder_asset", dividend_holder_asset_obj.symbol))
|
||||
}
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW() }
|
||||
|
||||
void database::perform_son_tasks()
|
||||
{
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
if(gpo.parameters.son_account() == GRAPHENE_NULL_ACCOUNT && head_block_time() >= HARDFORK_SON_TIME)
|
||||
{
|
||||
const auto& son_account = create<account_object>([&](account_object& a) {
|
||||
a.name = "son-account";
|
||||
a.statistics = create<account_statistics_object>([&a](account_statistics_object& s){
|
||||
s.owner = a.id;
|
||||
s.name = a.name;
|
||||
}).id;
|
||||
a.owner.weight_threshold = 1;
|
||||
a.active.weight_threshold = 0;
|
||||
a.registrar = a.lifetime_referrer = a.referrer = a.id;
|
||||
a.membership_expiration_date = time_point_sec::maximum();
|
||||
a.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
a.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;
|
||||
});
|
||||
|
||||
modify( gpo, [&son_account]( global_property_object& gpo ) {
|
||||
gpo.parameters.extensions.value.son_account = son_account.get_id();
|
||||
if( gpo.pending_parameters )
|
||||
gpo.pending_parameters->extensions.value.son_account = son_account.get_id();
|
||||
});
|
||||
}
|
||||
// create BTC asset here because son_account is the issuer of the BTC
|
||||
if (gpo.parameters.btc_asset() == asset_id_type() && head_block_time() >= HARDFORK_SON_TIME)
|
||||
{
|
||||
const asset_dynamic_data_object& dyn_asset =
|
||||
create<asset_dynamic_data_object>([](asset_dynamic_data_object& a) {
|
||||
a.current_supply = 0;
|
||||
});
|
||||
|
||||
const asset_object& btc_asset =
|
||||
create<asset_object>( [&gpo, &dyn_asset]( asset_object& a ) {
|
||||
a.symbol = "BTC";
|
||||
a.precision = 8;
|
||||
a.issuer = gpo.parameters.son_account();
|
||||
a.options.max_supply = GRAPHENE_MAX_SHARE_SUPPLY;
|
||||
a.options.market_fee_percent = 500; // 5%
|
||||
a.options.issuer_permissions = UIA_ASSET_ISSUER_PERMISSION_MASK;
|
||||
a.options.flags = asset_issuer_permission_flags::charge_market_fee |
|
||||
//asset_issuer_permission_flags::white_list |
|
||||
asset_issuer_permission_flags::override_authority |
|
||||
asset_issuer_permission_flags::transfer_restricted |
|
||||
asset_issuer_permission_flags::disable_confidential;
|
||||
a.options.core_exchange_rate.base.amount = 100000;
|
||||
a.options.core_exchange_rate.base.asset_id = asset_id_type(0);
|
||||
a.options.core_exchange_rate.quote.amount = 2500; // CoinMarketCap approx value
|
||||
a.options.core_exchange_rate.quote.asset_id = a.id;
|
||||
a.options.whitelist_authorities.clear(); // accounts allowed to use asset, if not empty
|
||||
a.options.blacklist_authorities.clear(); // accounts who can blacklist other accounts to use asset, if white_list flag is set
|
||||
a.options.whitelist_markets.clear(); // might be traded with
|
||||
a.options.blacklist_markets.clear(); // might not be traded with
|
||||
a.dynamic_asset_data_id = dyn_asset.id;
|
||||
});
|
||||
modify( gpo, [&btc_asset]( global_property_object& gpo ) {
|
||||
gpo.parameters.extensions.value.btc_asset = btc_asset.get_id();
|
||||
if( gpo.pending_parameters )
|
||||
gpo.pending_parameters->extensions.value.btc_asset = btc_asset.get_id();
|
||||
});
|
||||
}
|
||||
// Pay the SONs
|
||||
if (head_block_time() >= HARDFORK_SON_TIME)
|
||||
{
|
||||
// Before making a budget we should pay out SONs
|
||||
// This function should check if its time to pay sons
|
||||
// and modify the global son funds accordingly, whatever is left is passed on to next budget
|
||||
pay_sons();
|
||||
}
|
||||
}
|
||||
|
||||
void database::perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props)
|
||||
{ try {
|
||||
const auto& gpo = get_global_properties();
|
||||
|
|
@ -1517,6 +1981,7 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
d._vote_tally_buffer.resize(props.next_available_vote_id);
|
||||
d._witness_count_histogram_buffer.resize(props.parameters.maximum_witness_count / 2 + 1);
|
||||
d._committee_count_histogram_buffer.resize(props.parameters.maximum_committee_count / 2 + 1);
|
||||
d._son_count_histogram_buffer.resize(props.parameters.maximum_son_count / 2 + 1);
|
||||
d._total_voting_stake = 0;
|
||||
|
||||
auto balance_type = vesting_balance_type::normal;
|
||||
|
|
@ -1628,6 +2093,18 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
// same rationale as for witnesses
|
||||
d._committee_count_histogram_buffer[offset] += voting_stake;
|
||||
}
|
||||
if( opinion_account.options.num_son <= props.parameters.maximum_son_count )
|
||||
{
|
||||
uint16_t offset = std::min(size_t(opinion_account.options.num_son/2),
|
||||
d._son_count_histogram_buffer.size() - 1);
|
||||
// votes for a number greater than maximum_son_count
|
||||
// are turned into votes for maximum_son_count.
|
||||
//
|
||||
// in particular, this takes care of the case where a
|
||||
// member was voting for a high number, then the
|
||||
// parameter was lowered.
|
||||
d._son_count_histogram_buffer[offset] += voting_stake;
|
||||
}
|
||||
|
||||
d._total_voting_stake += voting_stake;
|
||||
}
|
||||
|
|
@ -1643,11 +2120,14 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
};
|
||||
clear_canary a(_witness_count_histogram_buffer),
|
||||
b(_committee_count_histogram_buffer),
|
||||
d(_son_count_histogram_buffer),
|
||||
c(_vote_tally_buffer);
|
||||
|
||||
perform_son_tasks();
|
||||
update_top_n_authorities(*this);
|
||||
update_active_witnesses();
|
||||
update_active_committee_members();
|
||||
update_active_sons();
|
||||
update_worker_votes();
|
||||
|
||||
const dynamic_global_property_object& dgpo = get_dynamic_global_properties();
|
||||
|
|
@ -1687,6 +2167,22 @@ void database::perform_chain_maintenance(const signed_block& next_block, const g
|
|||
p.pending_parameters->extensions.value.account_roles_max_per_account = p.parameters.extensions.value.account_roles_max_per_account;
|
||||
if( !p.pending_parameters->extensions.value.account_roles_max_lifetime.valid() )
|
||||
p.pending_parameters->extensions.value.account_roles_max_lifetime = p.parameters.extensions.value.account_roles_max_lifetime;
|
||||
if( !p.pending_parameters->extensions.value.son_vesting_amount.valid() )
|
||||
p.pending_parameters->extensions.value.son_vesting_amount = p.parameters.extensions.value.son_vesting_amount;
|
||||
if( !p.pending_parameters->extensions.value.son_vesting_period.valid() )
|
||||
p.pending_parameters->extensions.value.son_vesting_period = p.parameters.extensions.value.son_vesting_period;
|
||||
if( !p.pending_parameters->extensions.value.son_pay_max.valid() )
|
||||
p.pending_parameters->extensions.value.son_pay_max = p.parameters.extensions.value.son_pay_max;
|
||||
if( !p.pending_parameters->extensions.value.son_pay_time.valid() )
|
||||
p.pending_parameters->extensions.value.son_pay_time = p.parameters.extensions.value.son_pay_time;
|
||||
if( !p.pending_parameters->extensions.value.son_deregister_time.valid() )
|
||||
p.pending_parameters->extensions.value.son_deregister_time = p.parameters.extensions.value.son_deregister_time;
|
||||
if( !p.pending_parameters->extensions.value.son_heartbeat_frequency.valid() )
|
||||
p.pending_parameters->extensions.value.son_heartbeat_frequency = p.parameters.extensions.value.son_heartbeat_frequency;
|
||||
if( !p.pending_parameters->extensions.value.son_down_time.valid() )
|
||||
p.pending_parameters->extensions.value.son_down_time = p.parameters.extensions.value.son_down_time;
|
||||
if( !p.pending_parameters->extensions.value.son_bitcoin_min_tx_confirmations.valid() )
|
||||
p.pending_parameters->extensions.value.son_bitcoin_min_tx_confirmations = p.parameters.extensions.value.son_bitcoin_min_tx_confirmations;
|
||||
p.parameters = std::move(*p.pending_parameters);
|
||||
p.pending_parameters.reset();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -359,6 +359,63 @@ struct get_impacted_account_visitor
|
|||
void operator()( const account_role_delete_operation& op ){
|
||||
_impacted.insert( op.owner );
|
||||
}
|
||||
void operator()( const son_create_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_update_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_deregister_operation& op ) {
|
||||
_impacted.insert( op.payer);
|
||||
}
|
||||
void operator()( const son_heartbeat_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_report_down_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_maintenance_operation& op ) {
|
||||
_impacted.insert( op.owner_account );
|
||||
}
|
||||
void operator()( const son_wallet_recreate_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_update_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_deposit_create_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_deposit_process_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_withdraw_create_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const son_wallet_withdraw_process_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_add_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_update_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_address_delete_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_transaction_create_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_transaction_sign_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_transaction_send_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
void operator()( const sidechain_transaction_settle_operation& op ) {
|
||||
_impacted.insert( op.payer );
|
||||
}
|
||||
};
|
||||
|
||||
void graphene::chain::operation_get_impacted_accounts( const operation& op, flat_set<account_id_type>& result, bool ignore_custom_operation_required_auths ) {
|
||||
|
|
@ -452,6 +509,24 @@ void get_relevant_accounts( const object* obj, flat_set<account_id_type>& accoun
|
|||
accounts.insert( aobj->owner );
|
||||
accounts.insert( aobj->whitelisted_accounts.begin(), aobj->whitelisted_accounts.end() );
|
||||
break;
|
||||
} case son_object_type:{
|
||||
const auto& aobj = dynamic_cast<const son_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->son_account );
|
||||
break;
|
||||
} case son_wallet_object_type:{
|
||||
break;
|
||||
} case son_wallet_deposit_object_type:{
|
||||
break;
|
||||
} case son_wallet_withdraw_object_type:{
|
||||
break;
|
||||
} case sidechain_address_object_type:{
|
||||
const auto& aobj = dynamic_cast<const sidechain_address_object*>(obj);
|
||||
assert( aobj != nullptr );
|
||||
accounts.insert( aobj->sidechain_address_account );
|
||||
break;
|
||||
} case sidechain_transaction_object_type:{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ void database::clear_expired_proposals()
|
|||
elog("Failed to apply proposed transaction on its expiration. Deleting it.\n${proposal}\n${error}",
|
||||
("proposal", proposal)("error", e.to_detail_string()));
|
||||
}
|
||||
remove_son_proposal(proposal);
|
||||
remove(proposal);
|
||||
}
|
||||
}
|
||||
|
|
@ -730,5 +731,50 @@ void database::finalize_expired_offers(){
|
|||
}
|
||||
});
|
||||
} FC_CAPTURE_AND_RETHROW()}
|
||||
void database::remove_son_proposal( const proposal_object& proposal )
|
||||
{ try {
|
||||
if( proposal.proposed_transaction.operations.size() == 1 &&
|
||||
( proposal.proposed_transaction.operations.back().which() == operation::tag<son_deregister_operation>::value ||
|
||||
proposal.proposed_transaction.operations.back().which() == operation::tag<son_report_down_operation>::value) )
|
||||
{
|
||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get<by_proposal>();
|
||||
auto son_proposal_itr = son_proposal_idx.find( proposal.id );
|
||||
if( son_proposal_itr == son_proposal_idx.end() ) {
|
||||
return;
|
||||
}
|
||||
remove( *son_proposal_itr );
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (proposal) ) }
|
||||
|
||||
void database::remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove )
|
||||
{
|
||||
const auto& son_proposal_idx = get_index_type<son_proposal_index>().indices().get< by_id >();
|
||||
std::vector<proposal_id_type> proposals_to_remove;
|
||||
|
||||
for( auto& son_proposal : son_proposal_idx )
|
||||
{
|
||||
if(son_proposal.proposal_type == son_proposal_type::son_report_down_proposal)
|
||||
{
|
||||
auto it = std::find(son_ids_to_remove.begin(), son_ids_to_remove.end(), son_proposal.son_id);
|
||||
if (it != son_ids_to_remove.end())
|
||||
{
|
||||
ilog( "Removing inactive proposal ${p} for son ${s}", ("p", son_proposal.proposal_id) ("s",son_proposal.son_id));
|
||||
proposals_to_remove.push_back(son_proposal.proposal_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for( auto& proposal_id : proposals_to_remove )
|
||||
{
|
||||
const auto& proposal_obj = proposal_id(*this);
|
||||
remove_son_proposal(proposal_obj);
|
||||
remove(proposal_obj);
|
||||
}
|
||||
}
|
||||
|
||||
void database::remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove )
|
||||
{
|
||||
remove_inactive_son_down_proposals( son_ids_to_remove );
|
||||
}
|
||||
|
||||
} }
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@
|
|||
#include <graphene/chain/global_property_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/witness_schedule_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -72,6 +74,47 @@ witness_id_type database::get_scheduled_witness( uint32_t slot_num )const
|
|||
return wid;
|
||||
}
|
||||
|
||||
son_id_type database::get_scheduled_son( uint32_t slot_num )const
|
||||
{
|
||||
son_id_type sid;
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SHUFFLED_ALGORITHM)
|
||||
{
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
uint64_t current_aslot = dpo.current_aslot + slot_num;
|
||||
return sso.current_shuffled_sons[ current_aslot % sso.current_shuffled_sons.size() ];
|
||||
}
|
||||
if (gpo.parameters.witness_schedule_algorithm == GRAPHENE_WITNESS_SCHEDULED_ALGORITHM &&
|
||||
slot_num != 0 )
|
||||
{
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
// ask the near scheduler who goes in the given slot
|
||||
bool slot_is_near = sso.scheduler.get_slot(slot_num-1, sid);
|
||||
if(! slot_is_near)
|
||||
{
|
||||
// if the near scheduler doesn't know, we have to extend it to
|
||||
// a far scheduler.
|
||||
// n.b. instantiating it is slow, but block gaps long enough to
|
||||
// need it are likely pretty rare.
|
||||
|
||||
witness_scheduler_rng far_rng(sso.rng_seed.begin(), GRAPHENE_FAR_SCHEDULE_CTR_IV);
|
||||
|
||||
far_future_son_scheduler far_scheduler =
|
||||
far_future_son_scheduler(sso.scheduler, far_rng);
|
||||
if(!far_scheduler.get_slot(slot_num-1, sid))
|
||||
{
|
||||
// no scheduled son -- somebody set up us the bomb
|
||||
// n.b. this code path is impossible, the present
|
||||
// implementation of far_future_son_scheduler
|
||||
// returns true unconditionally
|
||||
assert( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
return sid;
|
||||
}
|
||||
|
||||
fc::time_point_sec database::get_slot_time(uint32_t slot_num)const
|
||||
{
|
||||
if( slot_num == 0 )
|
||||
|
|
@ -146,6 +189,41 @@ void database::update_witness_schedule()
|
|||
}
|
||||
}
|
||||
|
||||
void database::update_son_schedule()
|
||||
{
|
||||
const son_schedule_object& sso = son_schedule_id_type()(*this);
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
|
||||
if( head_block_num() % gpo.active_sons.size() == 0 )
|
||||
{
|
||||
modify( sso, [&]( son_schedule_object& _sso )
|
||||
{
|
||||
_sso.current_shuffled_sons.clear();
|
||||
_sso.current_shuffled_sons.reserve( gpo.active_sons.size() );
|
||||
|
||||
for( const son_info& w : gpo.active_sons )
|
||||
_sso.current_shuffled_sons.push_back( w.son_id );
|
||||
|
||||
auto now_hi = uint64_t(head_block_time().sec_since_epoch()) << 32;
|
||||
for( uint32_t i = 0; i < _sso.current_shuffled_sons.size(); ++i )
|
||||
{
|
||||
/// High performance random generator
|
||||
/// http://xorshift.di.unimi.it/
|
||||
uint64_t k = now_hi + uint64_t(i)*2685821657736338717ULL;
|
||||
k ^= (k >> 12);
|
||||
k ^= (k << 25);
|
||||
k ^= (k >> 27);
|
||||
k *= 2685821657736338717ULL;
|
||||
|
||||
uint32_t jmax = _sso.current_shuffled_sons.size() - i;
|
||||
uint32_t j = i + k%jmax;
|
||||
std::swap( _sso.current_shuffled_sons[i],
|
||||
_sso.current_shuffled_sons[j] );
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
vector<witness_id_type> database::get_near_witness_schedule()const
|
||||
{
|
||||
const witness_schedule_object& wso = get_witness_schedule_object();
|
||||
|
|
@ -226,6 +304,71 @@ void database::update_witness_schedule(const signed_block& next_block)
|
|||
idump( ( double(total_time/1000000.0)/calls) );
|
||||
}
|
||||
|
||||
void database::update_son_schedule(const signed_block& next_block)
|
||||
{
|
||||
auto start = fc::time_point::now();
|
||||
const global_property_object& gpo = get_global_properties();
|
||||
const son_schedule_object& sso = get(son_schedule_id_type());
|
||||
uint32_t schedule_needs_filled = gpo.active_sons.size();
|
||||
uint32_t schedule_slot = get_slot_at_time(next_block.timestamp);
|
||||
|
||||
// We shouldn't be able to generate _pending_block with timestamp
|
||||
// in the past, and incoming blocks from the network with timestamp
|
||||
// in the past shouldn't be able to make it this far without
|
||||
// triggering FC_ASSERT elsewhere
|
||||
|
||||
assert( schedule_slot > 0 );
|
||||
|
||||
son_id_type first_son;
|
||||
bool slot_is_near = sso.scheduler.get_slot( schedule_slot-1, first_son );
|
||||
|
||||
son_id_type son;
|
||||
|
||||
const dynamic_global_property_object& dpo = get_dynamic_global_properties();
|
||||
|
||||
assert( dpo.random.data_size() == witness_scheduler_rng::seed_length );
|
||||
assert( witness_scheduler_rng::seed_length == sso.rng_seed.size() );
|
||||
|
||||
modify(sso, [&](son_schedule_object& _sso)
|
||||
{
|
||||
_sso.slots_since_genesis += schedule_slot;
|
||||
witness_scheduler_rng rng(sso.rng_seed.data, _sso.slots_since_genesis);
|
||||
|
||||
_sso.scheduler._min_token_count = std::max(int(gpo.active_sons.size()) / 2, 1);
|
||||
|
||||
if( slot_is_near )
|
||||
{
|
||||
uint32_t drain = schedule_slot;
|
||||
while( drain > 0 )
|
||||
{
|
||||
if( _sso.scheduler.size() == 0 )
|
||||
break;
|
||||
_sso.scheduler.consume_schedule();
|
||||
--drain;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_sso.scheduler.reset_schedule( first_son );
|
||||
}
|
||||
while( !_sso.scheduler.get_slot(schedule_needs_filled, son) )
|
||||
{
|
||||
if( _sso.scheduler.produce_schedule(rng) & emit_turn )
|
||||
memcpy(_sso.rng_seed.begin(), dpo.random.data(), dpo.random.data_size());
|
||||
}
|
||||
_sso.last_scheduling_block = next_block.block_num();
|
||||
_sso.recent_slots_filled = (
|
||||
(_sso.recent_slots_filled << 1)
|
||||
+ 1) << (schedule_slot - 1);
|
||||
});
|
||||
auto end = fc::time_point::now();
|
||||
static uint64_t total_time = 0;
|
||||
static uint64_t calls = 0;
|
||||
total_time += (end - start).count();
|
||||
if( ++calls % 1000 == 0 )
|
||||
idump( ( double(total_time/1000000.0)/calls) );
|
||||
}
|
||||
|
||||
uint32_t database::update_witness_missed_blocks( const signed_block& b )
|
||||
{
|
||||
uint32_t missed_blocks = get_slot_at_time( b.timestamp );
|
||||
|
|
|
|||
|
|
@ -108,6 +108,11 @@ fc::variant_object get_config()
|
|||
result[ "GRAPHENE_RELAXED_COMMITTEE_ACCOUNT" ] = fc::variant(GRAPHENE_RELAXED_COMMITTEE_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_NULL_ACCOUNT" ] = fc::variant(GRAPHENE_NULL_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_TEMP_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_PROXY_TO_SELF_ACCOUNT" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_RAKE_FEE_ACCOUNT_ID" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_NULL_WITNESS" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_FBA_STEALTH_DESIGNATED_ASSET" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
result[ "GRAPHENE_DEFAULT_RAKE_FEE_PERCENTAGE" ] = fc::variant(GRAPHENE_TEMP_ACCOUNT, GRAPHENE_MAX_NESTED_OBJECTS);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
7
libraries/chain/hardfork.d/SON.hf
Normal file
7
libraries/chain/hardfork.d/SON.hf
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
// SON HARDFORK Wednesday, January 1, 2020 12:00:00 AM - 1577836800
|
||||
// SON HARDFORK Monday, March 30, 2020 3:00:00 PM - 1585573200
|
||||
// SON HARDFORK Monday, September 21, 2020 1:43:11 PM - 1600695791
|
||||
#ifndef HARDFORK_SON_TIME
|
||||
#include <ctime>
|
||||
#define HARDFORK_SON_TIME (fc::time_point_sec( 1585573200 ))
|
||||
#endif
|
||||
|
|
@ -45,9 +45,11 @@ struct budget_record
|
|||
// sinks of budget, should sum up to total_budget
|
||||
share_type witness_budget = 0;
|
||||
share_type worker_budget = 0;
|
||||
share_type son_budget = 0;
|
||||
|
||||
// unused budget
|
||||
share_type leftover_worker_funds = 0;
|
||||
share_type leftover_son_funds = 0;
|
||||
|
||||
// change in supply due to budget operations
|
||||
share_type supply_delta = 0;
|
||||
|
|
|
|||
|
|
@ -90,8 +90,10 @@
|
|||
|
||||
#define GRAPHENE_DEFAULT_MIN_WITNESS_COUNT (11)
|
||||
#define GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT (11)
|
||||
#define GRAPHENE_DEFAULT_MIN_SON_COUNT (5)
|
||||
#define GRAPHENE_DEFAULT_MAX_WITNESSES (1001) // SHOULD BE ODD
|
||||
#define GRAPHENE_DEFAULT_MAX_COMMITTEE (1001) // SHOULD BE ODD
|
||||
#define GRAPHENE_DEFAULT_MAX_SONS (15)
|
||||
#define GRAPHENE_DEFAULT_MAX_PROPOSAL_LIFETIME_SEC (60*60*24*7*4) // Four weeks
|
||||
#define GRAPHENE_DEFAULT_COMMITTEE_PROPOSAL_REVIEW_PERIOD_SEC (60*60*24*7*2) // Two weeks
|
||||
#define GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE (20*GRAPHENE_1_PERCENT)
|
||||
|
|
@ -227,6 +229,14 @@
|
|||
#define TOURNAMENT_MAX_WHITELIST_LENGTH 1000
|
||||
#define TOURNAMENT_MAX_START_TIME_IN_FUTURE (60*60*24*7*4) // 1 month
|
||||
#define TOURNAMENT_MAX_START_DELAY (60*60*24*7) // 1 week
|
||||
#define SON_VESTING_AMOUNT (50*GRAPHENE_BLOCKCHAIN_PRECISION) // 50 PPY
|
||||
#define SON_VESTING_PERIOD (60*60*24*2) // 2 days
|
||||
#define SON_DEREGISTER_TIME (60*60*12) // 12 Hours in seconds
|
||||
#define SON_HEARTBEAT_FREQUENCY (60*3) // 3 minutes in seconds
|
||||
#define SON_DOWN_TIME (60*3*2) // 2 Heartbeats in seconds
|
||||
#define SON_BITCOIN_MIN_TX_CONFIRMATIONS (1)
|
||||
#define SON_PAY_TIME (60*60*24) // 1 day
|
||||
#define SON_PAY_MAX (GRAPHENE_BLOCKCHAIN_PRECISION * int64_t(200))
|
||||
#define SWEEPS_DEFAULT_DISTRIBUTION_PERCENTAGE (2*GRAPHENE_1_PERCENT)
|
||||
#define SWEEPS_DEFAULT_DISTRIBUTION_ASSET (graphene::chain::asset_id_type(0))
|
||||
#define SWEEPS_VESTING_BALANCE_MULTIPLIER 100000000
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@
|
|||
|
||||
#include <graphene/chain/protocol/protocol.hpp>
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
|
||||
#include <map>
|
||||
|
|
@ -240,6 +242,22 @@ namespace graphene { namespace chain {
|
|||
*/
|
||||
witness_id_type get_scheduled_witness(uint32_t slot_num)const;
|
||||
|
||||
/**
|
||||
* @brief Get the son scheduled for block production in a slot.
|
||||
*
|
||||
* slot_num always corresponds to a time in the future.
|
||||
*
|
||||
* If slot_num == 1, returns the next scheduled son.
|
||||
* If slot_num == 2, returns the next scheduled son after
|
||||
* 1 block gap.
|
||||
*
|
||||
* Use the get_slot_time() and get_slot_at_time() functions
|
||||
* to convert between slot_num and timestamp.
|
||||
*
|
||||
* Passing slot_num == 0 returns GRAPHENE_NULL_WITNESS
|
||||
*/
|
||||
son_id_type get_scheduled_son(uint32_t slot_num)const;
|
||||
|
||||
/**
|
||||
* Get the time at which the given slot occurs.
|
||||
*
|
||||
|
|
@ -263,6 +281,8 @@ namespace graphene { namespace chain {
|
|||
vector<witness_id_type> get_near_witness_schedule()const;
|
||||
void update_witness_schedule();
|
||||
void update_witness_schedule(const signed_block& next_block);
|
||||
void update_son_schedule();
|
||||
void update_son_schedule(const signed_block& next_block);
|
||||
|
||||
void check_lottery_end_by_participants( asset_id_type asset_id );
|
||||
void check_ending_lotteries();
|
||||
|
|
@ -284,6 +304,13 @@ namespace graphene { namespace chain {
|
|||
const witness_schedule_object& get_witness_schedule_object()const;
|
||||
bool item_locked(const nft_id_type& item)const;
|
||||
bool account_role_valid(const account_role_object& aro, account_id_type account, optional<int> op_type = optional<int>()) const;
|
||||
std::set<son_id_type> get_sons_being_deregistered();
|
||||
std::set<son_id_type> get_sons_being_reported_down();
|
||||
std::set<son_id_type> get_sons_to_be_deregistered();
|
||||
fc::optional<operation> create_son_deregister_proposal( son_id_type son_id, account_id_type paying_son );
|
||||
signed_transaction create_signed_transaction( const fc::ecc::private_key& signing_private_key, const operation& op );
|
||||
bool is_son_dereg_valid( son_id_type son_id );
|
||||
bool is_son_active( son_id_type son_id );
|
||||
|
||||
time_point_sec head_block_time()const;
|
||||
uint32_t head_block_num()const;
|
||||
|
|
@ -532,9 +559,18 @@ namespace graphene { namespace chain {
|
|||
void initialize_budget_record( fc::time_point_sec now, budget_record& rec )const;
|
||||
void process_budget();
|
||||
void pay_workers( share_type& budget );
|
||||
void pay_sons();
|
||||
void perform_son_tasks();
|
||||
void perform_chain_maintenance(const signed_block& next_block, const global_property_object& global_props);
|
||||
void update_active_witnesses();
|
||||
void update_active_committee_members();
|
||||
void update_son_metrics( const vector<son_info>& curr_active_sons );
|
||||
void update_active_sons();
|
||||
void remove_son_proposal( const proposal_object& proposal );
|
||||
void remove_inactive_son_down_proposals( const vector<son_id_type>& son_ids_to_remove );
|
||||
void remove_inactive_son_proposals( const vector<son_id_type>& son_ids_to_remove );
|
||||
void update_son_statuses( const vector<son_info>& cur_active_sons, const vector<son_info>& new_active_sons );
|
||||
void update_son_wallet( const vector<son_info>& new_active_sons );
|
||||
void update_worker_votes();
|
||||
|
||||
public:
|
||||
|
|
@ -576,6 +612,7 @@ namespace graphene { namespace chain {
|
|||
vector<uint64_t> _vote_tally_buffer;
|
||||
vector<uint64_t> _witness_count_histogram_buffer;
|
||||
vector<uint64_t> _committee_count_histogram_buffer;
|
||||
vector<uint64_t> _son_count_histogram_buffer;
|
||||
uint64_t _total_voting_stake;
|
||||
|
||||
flat_map<uint32_t,block_id_type> _checkpoints;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <graphene/chain/protocol/chain_parameters.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
|
@ -51,6 +52,7 @@ namespace graphene { namespace chain {
|
|||
uint32_t next_available_vote_id = 0;
|
||||
vector<committee_member_id_type> active_committee_members; // updated once per maintenance interval
|
||||
flat_set<witness_id_type> active_witnesses; // updated once per maintenance interval
|
||||
vector<son_info> active_sons; // updated once per maintenance interval
|
||||
// n.b. witness scheduling is done by witness_schedule object
|
||||
};
|
||||
|
||||
|
|
@ -77,6 +79,9 @@ namespace graphene { namespace chain {
|
|||
time_point_sec next_maintenance_time;
|
||||
time_point_sec last_budget_time;
|
||||
share_type witness_budget;
|
||||
//Last SON Payout time, it can be different to the maintenance interval time
|
||||
time_point_sec last_son_payout_time = HARDFORK_SON_TIME;
|
||||
share_type son_budget = 0;
|
||||
uint32_t accounts_registered_this_interval = 0;
|
||||
/**
|
||||
* Every time a block is missed this increases by
|
||||
|
|
@ -133,6 +138,8 @@ FC_REFLECT_DERIVED( graphene::chain::dynamic_global_property_object, (graphene::
|
|||
(next_maintenance_time)
|
||||
(last_budget_time)
|
||||
(witness_budget)
|
||||
(last_son_payout_time)
|
||||
(son_budget)
|
||||
(accounts_registered_this_interval)
|
||||
(recently_missed_count)
|
||||
(current_aslot)
|
||||
|
|
@ -147,6 +154,7 @@ FC_REFLECT_DERIVED( graphene::chain::global_property_object, (graphene::db::obje
|
|||
(next_available_vote_id)
|
||||
(active_committee_members)
|
||||
(active_witnesses)
|
||||
(active_sons)
|
||||
)
|
||||
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::dynamic_global_property_object )
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ struct immutable_chain_parameters
|
|||
{
|
||||
uint16_t min_committee_member_count = GRAPHENE_DEFAULT_MIN_COMMITTEE_MEMBER_COUNT;
|
||||
uint16_t min_witness_count = GRAPHENE_DEFAULT_MIN_WITNESS_COUNT;
|
||||
uint16_t min_son_count = GRAPHENE_DEFAULT_MIN_SON_COUNT;
|
||||
uint32_t num_special_accounts = 0;
|
||||
uint32_t num_special_assets = 0;
|
||||
};
|
||||
|
|
@ -41,6 +42,7 @@ struct immutable_chain_parameters
|
|||
FC_REFLECT( graphene::chain::immutable_chain_parameters,
|
||||
(min_committee_member_count)
|
||||
(min_witness_count)
|
||||
(min_son_count)
|
||||
(num_special_accounts)
|
||||
(num_special_assets)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,22 @@
|
|||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class son_hardfork_visitor
|
||||
{
|
||||
public:
|
||||
typedef void result_type;
|
||||
database& db;
|
||||
proposal_id_type prop_id;
|
||||
|
||||
son_hardfork_visitor( database& _db, const proposal_id_type& _prop_id ) : db( _db ), prop_id( _prop_id ) {}
|
||||
|
||||
template<typename T>
|
||||
void operator()( const T &v ) const {}
|
||||
|
||||
void operator()( const son_deregister_operation &v );
|
||||
void operator()( const son_report_down_operation &v );
|
||||
};
|
||||
|
||||
class proposal_create_evaluator : public evaluator<proposal_create_evaluator>
|
||||
{
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -52,6 +52,9 @@ namespace graphene { namespace chain {
|
|||
/// The number of active committee members this account votes the blockchain should appoint
|
||||
/// Must not exceed the actual number of committee members voted for in @ref votes
|
||||
uint16_t num_committee = 0;
|
||||
/// The number of active son members this account votes the blockchain should appoint
|
||||
/// Must not exceed the actual number of son members voted for in @ref votes
|
||||
uint16_t num_son = 0;
|
||||
/// This is the list of vote IDs this account votes for. The weight of these votes is determined by this
|
||||
/// account's balance of core asset.
|
||||
flat_set<vote_id_type> votes;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <fc/smart_ref_fwd.hpp>
|
||||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <../hardfork.d/GPOS.hf>
|
||||
#include <memory>
|
||||
|
||||
|
|
@ -55,6 +56,18 @@ namespace graphene { namespace chain {
|
|||
/* Account Roles - Permissions Parameters */
|
||||
optional < uint16_t > account_roles_max_per_account = ACCOUNT_ROLES_MAX_PER_ACCOUNT;
|
||||
optional < uint32_t > account_roles_max_lifetime = ACCOUNT_ROLES_MAX_LIFETIME;
|
||||
|
||||
optional < uint32_t > son_vesting_amount = SON_VESTING_AMOUNT;
|
||||
optional < uint32_t > son_vesting_period = SON_VESTING_PERIOD;
|
||||
optional < uint32_t > son_pay_max = SON_PAY_MAX;
|
||||
optional < uint32_t > son_pay_time = SON_PAY_TIME;
|
||||
optional < uint32_t > son_deregister_time = SON_DEREGISTER_TIME;
|
||||
optional < uint32_t > son_heartbeat_frequency = SON_HEARTBEAT_FREQUENCY;
|
||||
optional < uint32_t > son_down_time = SON_DOWN_TIME;
|
||||
optional < uint16_t > son_bitcoin_min_tx_confirmations = SON_BITCOIN_MIN_TX_CONFIRMATIONS;
|
||||
|
||||
optional < account_id_type > son_account;
|
||||
optional < asset_id_type > btc_asset;
|
||||
};
|
||||
|
||||
struct chain_parameters
|
||||
|
|
@ -73,6 +86,7 @@ namespace graphene { namespace chain {
|
|||
uint8_t maximum_asset_feed_publishers = GRAPHENE_DEFAULT_MAX_ASSET_FEED_PUBLISHERS; ///< the maximum number of feed publishers for a given asset
|
||||
uint16_t maximum_witness_count = GRAPHENE_DEFAULT_MAX_WITNESSES; ///< maximum number of active witnesses
|
||||
uint16_t maximum_committee_count = GRAPHENE_DEFAULT_MAX_COMMITTEE; ///< maximum number of active committee_members
|
||||
uint16_t maximum_son_count = GRAPHENE_DEFAULT_MAX_SONS; ///< maximum number of active SONS
|
||||
uint16_t maximum_authority_membership = GRAPHENE_DEFAULT_MAX_AUTHORITY_MEMBERSHIP; ///< largest number of keys/accounts an authority can have
|
||||
uint16_t reserve_percent_of_fee = GRAPHENE_DEFAULT_BURN_PERCENT_OF_FEE; ///< the percentage of the network's allocation of a fee that is taken out of circulation
|
||||
uint16_t network_percent_of_fee = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE; ///< percent of transaction fees paid to network
|
||||
|
|
@ -104,7 +118,7 @@ namespace graphene { namespace chain {
|
|||
uint32_t maximum_tournament_start_time_in_future = TOURNAMENT_MAX_START_TIME_IN_FUTURE;
|
||||
uint32_t maximum_tournament_start_delay = TOURNAMENT_MAX_START_DELAY;
|
||||
uint16_t maximum_tournament_number_of_wins = TOURNAMENT_MAX_NUMBER_OF_WINS;
|
||||
|
||||
|
||||
extension<parameter_extension> extensions;
|
||||
|
||||
/** defined in fee_schedule.cpp */
|
||||
|
|
@ -161,6 +175,36 @@ namespace graphene { namespace chain {
|
|||
inline uint32_t account_roles_max_lifetime()const {
|
||||
return extensions.value.account_roles_max_lifetime.valid() ? *extensions.value.account_roles_max_lifetime : ACCOUNT_ROLES_MAX_LIFETIME;
|
||||
}
|
||||
inline uint32_t son_vesting_amount()const {
|
||||
return extensions.value.son_vesting_amount.valid() ? *extensions.value.son_vesting_amount : SON_VESTING_AMOUNT; /// current period start date
|
||||
}
|
||||
inline uint32_t son_vesting_period()const {
|
||||
return extensions.value.son_vesting_period.valid() ? *extensions.value.son_vesting_period : SON_VESTING_PERIOD; /// current period start date
|
||||
}
|
||||
inline uint16_t son_pay_max()const {
|
||||
return extensions.value.son_pay_max.valid() ? *extensions.value.son_pay_max : SON_PAY_MAX;
|
||||
}
|
||||
inline uint16_t son_pay_time()const {
|
||||
return extensions.value.son_pay_time.valid() ? *extensions.value.son_pay_time : SON_PAY_TIME;
|
||||
}
|
||||
inline uint16_t son_deregister_time()const {
|
||||
return extensions.value.son_deregister_time.valid() ? *extensions.value.son_deregister_time : SON_DEREGISTER_TIME;
|
||||
}
|
||||
inline uint16_t son_heartbeat_frequency()const {
|
||||
return extensions.value.son_heartbeat_frequency.valid() ? *extensions.value.son_heartbeat_frequency : SON_HEARTBEAT_FREQUENCY;
|
||||
}
|
||||
inline uint16_t son_down_time()const {
|
||||
return extensions.value.son_down_time.valid() ? *extensions.value.son_down_time : SON_DOWN_TIME;
|
||||
}
|
||||
inline uint16_t son_bitcoin_min_tx_confirmations()const {
|
||||
return extensions.value.son_bitcoin_min_tx_confirmations.valid() ? *extensions.value.son_bitcoin_min_tx_confirmations : SON_BITCOIN_MIN_TX_CONFIRMATIONS;
|
||||
}
|
||||
inline account_id_type son_account() const {
|
||||
return extensions.value.son_account.valid() ? *extensions.value.son_account : GRAPHENE_NULL_ACCOUNT;
|
||||
}
|
||||
inline asset_id_type btc_asset() const {
|
||||
return extensions.value.btc_asset.valid() ? *extensions.value.btc_asset : asset_id_type();
|
||||
}
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
|
@ -183,6 +227,16 @@ FC_REFLECT( graphene::chain::parameter_extension,
|
|||
(rbac_max_authorities_per_permission)
|
||||
(account_roles_max_per_account)
|
||||
(account_roles_max_lifetime)
|
||||
(son_vesting_amount)
|
||||
(son_vesting_period)
|
||||
(son_pay_max)
|
||||
(son_pay_time)
|
||||
(son_deregister_time)
|
||||
(son_heartbeat_frequency)
|
||||
(son_down_time)
|
||||
(son_bitcoin_min_tx_confirmations)
|
||||
(son_account)
|
||||
(btc_asset)
|
||||
)
|
||||
|
||||
FC_REFLECT( graphene::chain::chain_parameters,
|
||||
|
|
@ -199,6 +253,7 @@ FC_REFLECT( graphene::chain::chain_parameters,
|
|||
(maximum_asset_feed_publishers)
|
||||
(maximum_witness_count)
|
||||
(maximum_committee_count)
|
||||
(maximum_son_count)
|
||||
(maximum_authority_membership)
|
||||
(reserve_percent_of_fee)
|
||||
(network_percent_of_fee)
|
||||
|
|
|
|||
|
|
@ -50,6 +50,12 @@
|
|||
#include <graphene/chain/protocol/offer.hpp>
|
||||
#include <graphene/chain/protocol/nft_ops.hpp>
|
||||
#include <graphene/chain/protocol/account_role.hpp>
|
||||
#include <graphene/chain/protocol/son.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_address.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet_deposit.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet_withdraw.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
|
|
@ -159,7 +165,26 @@ namespace graphene { namespace chain {
|
|||
nft_set_approval_for_all_operation,
|
||||
account_role_create_operation,
|
||||
account_role_update_operation,
|
||||
account_role_delete_operation
|
||||
account_role_delete_operation,
|
||||
son_create_operation,
|
||||
son_update_operation,
|
||||
son_deregister_operation,
|
||||
son_heartbeat_operation,
|
||||
son_report_down_operation,
|
||||
son_maintenance_operation,
|
||||
son_wallet_recreate_operation,
|
||||
son_wallet_update_operation,
|
||||
son_wallet_deposit_create_operation,
|
||||
son_wallet_deposit_process_operation,
|
||||
son_wallet_withdraw_create_operation,
|
||||
son_wallet_withdraw_process_operation,
|
||||
sidechain_address_add_operation,
|
||||
sidechain_address_update_operation,
|
||||
sidechain_address_delete_operation,
|
||||
sidechain_transaction_create_operation,
|
||||
sidechain_transaction_sign_operation,
|
||||
sidechain_transaction_send_operation,
|
||||
sidechain_transaction_settle_operation
|
||||
> operation;
|
||||
|
||||
/// @} // operations group
|
||||
|
|
|
|||
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct sidechain_address_add_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
account_id_type sidechain_address_account;
|
||||
sidechain_type sidechain;
|
||||
string deposit_public_key;
|
||||
string deposit_address;
|
||||
string deposit_address_data;
|
||||
string withdraw_public_key;
|
||||
string withdraw_address;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct sidechain_address_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_address_id_type sidechain_address_id;
|
||||
account_id_type sidechain_address_account;
|
||||
sidechain_type sidechain;
|
||||
optional<string> deposit_public_key;
|
||||
optional<string> deposit_address;
|
||||
optional<string> deposit_address_data;
|
||||
optional<string> withdraw_public_key;
|
||||
optional<string> withdraw_address;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct sidechain_address_delete_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_address_id_type sidechain_address_id;
|
||||
account_id_type sidechain_address_account;
|
||||
sidechain_type sidechain;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::sidechain_address_add_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_add_operation, (fee)(payer)
|
||||
(sidechain_address_account)(sidechain)
|
||||
(deposit_public_key)(deposit_address)(deposit_address_data)
|
||||
(withdraw_public_key)(withdraw_address) )
|
||||
|
||||
FC_REFLECT(graphene::chain::sidechain_address_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_update_operation, (fee)(payer)
|
||||
(sidechain_address_id)
|
||||
(sidechain_address_account)(sidechain)
|
||||
(deposit_public_key)(deposit_address)(deposit_address_data)
|
||||
(withdraw_public_key)(withdraw_address) )
|
||||
|
||||
FC_REFLECT(graphene::chain::sidechain_address_delete_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::sidechain_address_delete_operation, (fee)(payer)
|
||||
(sidechain_address_id)
|
||||
(sidechain_address_account)(sidechain) )
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct sidechain_transaction_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_type sidechain;
|
||||
object_id_type object_id;
|
||||
std::string transaction;
|
||||
std::vector<son_info> signers;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct sidechain_transaction_sign_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type signer;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_transaction_id_type sidechain_transaction_id;
|
||||
std::string signature;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
|
||||
};
|
||||
|
||||
struct sidechain_transaction_send_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_transaction_id_type sidechain_transaction_id;
|
||||
std::string sidechain_transaction;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
|
||||
};
|
||||
|
||||
struct sidechain_transaction_settle_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
sidechain_transaction_id_type sidechain_transaction_id;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee( const fee_parameters_type& k )const { return 0; }
|
||||
};
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_create_operation, (fee)(payer)
|
||||
(sidechain)
|
||||
(object_id)
|
||||
(transaction)
|
||||
(signers) )
|
||||
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_sign_operation, (fee)(signer)(payer)
|
||||
(sidechain_transaction_id)
|
||||
(signature) )
|
||||
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_send_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_send_operation, (fee)(payer)
|
||||
(sidechain_transaction_id)
|
||||
(sidechain_transaction) )
|
||||
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_settle_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT( graphene::chain::sidechain_transaction_settle_operation, (fee)(payer)
|
||||
(sidechain_transaction_id) )
|
||||
119
libraries/chain/include/graphene/chain/protocol/son.hpp
Normal file
119
libraries/chain/include/graphene/chain/protocol/son.hpp
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type owner_account;
|
||||
std::string url;
|
||||
vesting_balance_id_type deposit;
|
||||
public_key_type signing_key;
|
||||
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||
vesting_balance_id_type pay_vb;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type owner_account;
|
||||
optional<std::string> new_url;
|
||||
optional<vesting_balance_id_type> new_deposit;
|
||||
optional<public_key_type> new_signing_key;
|
||||
optional<flat_map<sidechain_type, string>> new_sidechain_public_keys;
|
||||
optional<vesting_balance_id_type> new_pay_vb;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_deregister_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type payer;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_heartbeat_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type owner_account;
|
||||
time_point_sec ts;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
|
||||
struct son_report_down_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type payer;
|
||||
time_point_sec down_ts;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
enum class son_maintenance_request_type
|
||||
{
|
||||
request_maintenance,
|
||||
cancel_request_maintenance
|
||||
};
|
||||
|
||||
struct son_maintenance_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
son_id_type son_id;
|
||||
account_id_type owner_account;
|
||||
son_maintenance_request_type request_type = son_maintenance_request_type::request_maintenance;
|
||||
|
||||
account_id_type fee_payer()const { return owner_account; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_create_operation, (fee)(owner_account)(url)(deposit)(signing_key)(sidechain_public_keys)
|
||||
(pay_vb) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_update_operation, (fee)(son_id)(owner_account)(new_url)(new_deposit)
|
||||
(new_signing_key)(new_sidechain_public_keys)(new_pay_vb) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_deregister_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_deregister_operation, (fee)(son_id)(payer) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_heartbeat_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_heartbeat_operation, (fee)(son_id)(owner_account)(ts) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_report_down_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_report_down_operation, (fee)(son_id)(payer)(down_ts) )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::son_maintenance_request_type, (request_maintenance)(cancel_request_maintenance) )
|
||||
FC_REFLECT(graphene::chain::son_maintenance_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_maintenance_operation, (fee)(son_id)(owner_account)(request_type) )
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_wallet_recreate_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
vector<son_info> sons;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_wallet_update_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_wallet_id_type son_wallet_id;
|
||||
sidechain_type sidechain;
|
||||
string address;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_recreate_operation, (fee)(payer)(sons) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_update_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_update_operation, (fee)(payer)(son_wallet_id)(sidechain)(address) )
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
#include <fc/safe.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_wallet_deposit_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_id_type son_id;
|
||||
fc::time_point_sec timestamp;
|
||||
uint32_t block_num;
|
||||
sidechain_type sidechain;
|
||||
std::string sidechain_uid;
|
||||
std::string sidechain_transaction_id;
|
||||
std::string sidechain_from;
|
||||
std::string sidechain_to;
|
||||
std::string sidechain_currency;
|
||||
fc::safe<int64_t> sidechain_amount;
|
||||
chain::account_id_type peerplays_from;
|
||||
chain::account_id_type peerplays_to;
|
||||
chain::asset peerplays_asset;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_wallet_deposit_process_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_wallet_deposit_id_type son_wallet_deposit_id;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_deposit_create_operation, (fee)(payer)
|
||||
(son_id) (timestamp) (block_num) (sidechain)
|
||||
(sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount)
|
||||
(peerplays_from) (peerplays_to) (peerplays_asset) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_deposit_process_operation, (fee)(payer)
|
||||
(son_wallet_deposit_id) )
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/base.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
#include <fc/safe.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
struct son_wallet_withdraw_create_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_id_type son_id;
|
||||
fc::time_point_sec timestamp;
|
||||
uint32_t block_num;
|
||||
sidechain_type sidechain;
|
||||
std::string peerplays_uid;
|
||||
std::string peerplays_transaction_id;
|
||||
chain::account_id_type peerplays_from;
|
||||
chain::asset peerplays_asset;
|
||||
sidechain_type withdraw_sidechain;
|
||||
std::string withdraw_address;
|
||||
std::string withdraw_currency;
|
||||
safe<int64_t> withdraw_amount;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
struct son_wallet_withdraw_process_operation : public base_operation
|
||||
{
|
||||
struct fee_parameters_type { uint64_t fee = 0; };
|
||||
|
||||
asset fee;
|
||||
account_id_type payer;
|
||||
|
||||
son_wallet_withdraw_id_type son_wallet_withdraw_id;
|
||||
|
||||
account_id_type fee_payer()const { return payer; }
|
||||
share_type calculate_fee(const fee_parameters_type& k)const { return 0; }
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_withdraw_create_operation, (fee)(payer)
|
||||
(son_id) (timestamp) (block_num) (sidechain)
|
||||
(peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset)
|
||||
(withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount) )
|
||||
|
||||
FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation::fee_parameters_type, (fee) )
|
||||
FC_REFLECT(graphene::chain::son_wallet_withdraw_process_operation, (fee)(payer)
|
||||
(son_wallet_withdraw_id) )
|
||||
|
|
@ -177,6 +177,13 @@ namespace graphene { namespace chain {
|
|||
nft_metadata_type,
|
||||
nft_object_type,
|
||||
account_role_type,
|
||||
son_object_type,
|
||||
son_proposal_object_type,
|
||||
son_wallet_object_type,
|
||||
son_wallet_deposit_object_type,
|
||||
son_wallet_withdraw_object_type,
|
||||
sidechain_address_object_type,
|
||||
sidechain_transaction_object_type,
|
||||
OBJECT_TYPE_COUNT ///< Sentry value which contains the number of different object types
|
||||
};
|
||||
|
||||
|
|
@ -206,7 +213,9 @@ namespace graphene { namespace chain {
|
|||
impl_global_betting_statistics_object_type,
|
||||
impl_lottery_balance_object_type,
|
||||
impl_sweeps_vesting_balance_object_type,
|
||||
impl_offer_history_object_type
|
||||
impl_offer_history_object_type,
|
||||
impl_son_statistics_object_type,
|
||||
impl_son_schedule_object_type
|
||||
};
|
||||
|
||||
//typedef fc::unsigned_int object_id_type;
|
||||
|
|
@ -243,6 +252,13 @@ namespace graphene { namespace chain {
|
|||
class nft_metadata_object;
|
||||
class nft_object;
|
||||
class account_role_object;
|
||||
class son_object;
|
||||
class son_proposal_object;
|
||||
class son_wallet_object;
|
||||
class son_wallet_deposit_object;
|
||||
class son_wallet_withdraw_object;
|
||||
class sidechain_address_object;
|
||||
class sidechain_transaction_object;
|
||||
|
||||
typedef object_id< protocol_ids, account_object_type, account_object> account_id_type;
|
||||
typedef object_id< protocol_ids, asset_object_type, asset_object> asset_id_type;
|
||||
|
|
@ -275,6 +291,13 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< protocol_ids, nft_metadata_type, nft_metadata_object> nft_metadata_id_type;
|
||||
typedef object_id< protocol_ids, nft_object_type, nft_object> nft_id_type;
|
||||
typedef object_id< protocol_ids, account_role_type, account_role_object> account_role_id_type;
|
||||
typedef object_id< protocol_ids, son_object_type, son_object> son_id_type;
|
||||
typedef object_id< protocol_ids, son_proposal_object_type, son_proposal_object> son_proposal_id_type;
|
||||
typedef object_id< protocol_ids, son_wallet_object_type, son_wallet_object> son_wallet_id_type;
|
||||
typedef object_id< protocol_ids, son_wallet_deposit_object_type, son_wallet_deposit_object> son_wallet_deposit_id_type;
|
||||
typedef object_id< protocol_ids, son_wallet_withdraw_object_type, son_wallet_withdraw_object> son_wallet_withdraw_id_type;
|
||||
typedef object_id< protocol_ids, sidechain_address_object_type, sidechain_address_object> sidechain_address_id_type;
|
||||
typedef object_id< protocol_ids, sidechain_transaction_object_type,sidechain_transaction_object> sidechain_transaction_id_type;
|
||||
|
||||
// implementation types
|
||||
class global_property_object;
|
||||
|
|
@ -299,6 +322,8 @@ namespace graphene { namespace chain {
|
|||
class lottery_balance_object;
|
||||
class sweeps_vesting_balance_object;
|
||||
class offer_history_object;
|
||||
class son_statistics_object;
|
||||
class son_schedule_object;
|
||||
|
||||
typedef object_id< implementation_ids, impl_global_property_object_type, global_property_object> global_property_id_type;
|
||||
typedef object_id< implementation_ids, impl_dynamic_global_property_object_type, dynamic_global_property_object> dynamic_global_property_id_type;
|
||||
|
|
@ -328,6 +353,8 @@ namespace graphene { namespace chain {
|
|||
typedef object_id< implementation_ids, impl_lottery_balance_object_type, lottery_balance_object > lottery_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_sweeps_vesting_balance_object_type, sweeps_vesting_balance_object> sweeps_vesting_balance_id_type;
|
||||
typedef object_id< implementation_ids, impl_offer_history_object_type, offer_history_object> offer_history_id_type;
|
||||
typedef object_id< implementation_ids, impl_son_statistics_object_type, son_statistics_object > son_statistics_id_type;
|
||||
typedef object_id< implementation_ids, impl_son_schedule_object_type, son_schedule_object> son_schedule_id_type;
|
||||
|
||||
typedef fc::array<char, GRAPHENE_MAX_ASSET_SYMBOL_LENGTH> symbol_type;
|
||||
typedef fc::ripemd160 block_id_type;
|
||||
|
|
@ -463,6 +490,13 @@ FC_REFLECT_ENUM( graphene::chain::object_type,
|
|||
(nft_metadata_type)
|
||||
(nft_object_type)
|
||||
(account_role_type)
|
||||
(son_object_type)
|
||||
(son_proposal_object_type)
|
||||
(son_wallet_object_type)
|
||||
(son_wallet_deposit_object_type)
|
||||
(son_wallet_withdraw_object_type)
|
||||
(sidechain_address_object_type)
|
||||
(sidechain_transaction_object_type)
|
||||
(OBJECT_TYPE_COUNT)
|
||||
)
|
||||
FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
||||
|
|
@ -491,6 +525,8 @@ FC_REFLECT_ENUM( graphene::chain::impl_object_type,
|
|||
(impl_lottery_balance_object_type)
|
||||
(impl_sweeps_vesting_balance_object_type)
|
||||
(impl_offer_history_object_type)
|
||||
(impl_son_statistics_object_type)
|
||||
(impl_son_schedule_object_type)
|
||||
)
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::share_type )
|
||||
|
|
@ -540,6 +576,14 @@ FC_REFLECT_TYPENAME( graphene::chain::offer_history_id_type )
|
|||
FC_REFLECT_TYPENAME( graphene::chain::nft_metadata_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::nft_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::account_role_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_proposal_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_deposit_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::son_wallet_withdraw_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_address_id_type )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::sidechain_transaction_id_type )
|
||||
|
||||
|
||||
FC_REFLECT( graphene::chain::void_t, )
|
||||
|
||||
|
|
|
|||
|
|
@ -27,12 +27,14 @@
|
|||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
enum class vesting_balance_type { normal, gpos };
|
||||
enum class vesting_balance_type { normal, gpos, son };
|
||||
|
||||
inline std::string get_vesting_balance_type(vesting_balance_type type) {
|
||||
switch (type) {
|
||||
case vesting_balance_type::normal:
|
||||
return "NORMAL";
|
||||
case vesting_balance_type::son:
|
||||
return "SON";
|
||||
case vesting_balance_type::gpos:
|
||||
default:
|
||||
return "GPOS";
|
||||
|
|
@ -55,9 +57,10 @@ namespace graphene { namespace chain {
|
|||
cdd_vesting_policy_initializer( uint32_t vest_sec = 0, fc::time_point_sec sc = fc::time_point_sec() ):start_claim(sc),vesting_seconds(vest_sec){}
|
||||
};
|
||||
|
||||
typedef fc::static_variant<linear_vesting_policy_initializer, cdd_vesting_policy_initializer> vesting_policy_initializer;
|
||||
|
||||
struct dormant_vesting_policy_initializer {};
|
||||
|
||||
typedef fc::static_variant<linear_vesting_policy_initializer, cdd_vesting_policy_initializer,
|
||||
dormant_vesting_policy_initializer> vesting_policy_initializer;
|
||||
|
||||
/**
|
||||
* @brief Create a vesting balance.
|
||||
|
|
@ -127,13 +130,14 @@ FC_REFLECT( graphene::chain::vesting_balance_create_operation::fee_parameters_ty
|
|||
FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type, (fee) )
|
||||
|
||||
FC_REFLECT( graphene::chain::vesting_balance_create_operation, (fee)(creator)(owner)(amount)(policy)(balance_type) )
|
||||
FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount))
|
||||
FC_REFLECT( graphene::chain::vesting_balance_withdraw_operation, (fee)(vesting_balance)(owner)(amount) )
|
||||
|
||||
FC_REFLECT(graphene::chain::linear_vesting_policy_initializer, (begin_timestamp)(vesting_cliff_seconds)(vesting_duration_seconds) )
|
||||
FC_REFLECT(graphene::chain::cdd_vesting_policy_initializer, (start_claim)(vesting_seconds) )
|
||||
FC_REFLECT(graphene::chain::dormant_vesting_policy_initializer, )
|
||||
FC_REFLECT_TYPENAME( graphene::chain::vesting_policy_initializer )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos) )
|
||||
FC_REFLECT_ENUM( graphene::chain::vesting_balance_type, (normal)(gpos)(son) )
|
||||
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_create_operation::fee_parameters_type )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vesting_balance_withdraw_operation::fee_parameters_type )
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ struct vote_id_type
|
|||
committee,
|
||||
witness,
|
||||
worker,
|
||||
son,
|
||||
VOTE_TYPE_COUNT
|
||||
};
|
||||
|
||||
|
|
@ -143,7 +144,7 @@ void from_variant( const fc::variant& var, graphene::chain::vote_id_type& vo, ui
|
|||
|
||||
FC_REFLECT_TYPENAME( fc::flat_set<graphene::chain::vote_id_type> )
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(VOTE_TYPE_COUNT) )
|
||||
FC_REFLECT_ENUM( graphene::chain::vote_id_type::vote_type, (witness)(committee)(worker)(son)(VOTE_TYPE_COUNT) )
|
||||
FC_REFLECT( graphene::chain::vote_id_type, (content) )
|
||||
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::vote_id_type )
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_address.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class add_sidechain_address_evaluator : public evaluator<add_sidechain_address_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_address_add_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_address_add_operation& o);
|
||||
object_id_type do_apply(const sidechain_address_add_operation& o);
|
||||
};
|
||||
|
||||
class update_sidechain_address_evaluator : public evaluator<update_sidechain_address_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_address_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_address_update_operation& o);
|
||||
object_id_type do_apply(const sidechain_address_update_operation& o);
|
||||
};
|
||||
|
||||
class delete_sidechain_address_evaluator : public evaluator<delete_sidechain_address_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_address_delete_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_address_delete_operation& o);
|
||||
void_result do_apply(const sidechain_address_delete_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class sidechain_address_object
|
||||
* @brief tracks information about a sidechain addresses for user accounts.
|
||||
* @ingroup object
|
||||
*/
|
||||
class sidechain_address_object : public abstract_object<sidechain_address_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = sidechain_address_object_type;
|
||||
|
||||
account_id_type sidechain_address_account;
|
||||
sidechain_type sidechain;
|
||||
string deposit_public_key;
|
||||
string deposit_address;
|
||||
string deposit_address_data;
|
||||
string withdraw_public_key;
|
||||
string withdraw_address;
|
||||
time_point_sec valid_from;
|
||||
time_point_sec expires;
|
||||
|
||||
sidechain_address_object() :
|
||||
sidechain(sidechain_type::bitcoin),
|
||||
deposit_public_key(""),
|
||||
deposit_address(""),
|
||||
withdraw_public_key(""),
|
||||
withdraw_address("") {}
|
||||
};
|
||||
|
||||
struct by_account;
|
||||
struct by_sidechain;
|
||||
struct by_sidechain_and_deposit_public_key_and_expires;
|
||||
struct by_withdraw_public_key;
|
||||
struct by_account_and_sidechain_and_expires;
|
||||
struct by_sidechain_and_deposit_address_and_expires;
|
||||
using sidechain_address_multi_index_type = multi_index_container<
|
||||
sidechain_address_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_non_unique< tag<by_account>,
|
||||
member<sidechain_address_object, account_id_type, &sidechain_address_object::sidechain_address_account>
|
||||
>,
|
||||
ordered_non_unique< tag<by_sidechain>,
|
||||
member<sidechain_address_object, sidechain_type, &sidechain_address_object::sidechain>
|
||||
>,
|
||||
ordered_unique< tag<by_sidechain_and_deposit_public_key_and_expires>,
|
||||
composite_key<sidechain_address_object,
|
||||
member<sidechain_address_object, sidechain_type, &sidechain_address_object::sidechain>,
|
||||
member<sidechain_address_object, string, &sidechain_address_object::deposit_public_key>,
|
||||
member<sidechain_address_object, time_point_sec, &sidechain_address_object::expires>
|
||||
>
|
||||
>,
|
||||
ordered_non_unique< tag<by_withdraw_public_key>,
|
||||
member<sidechain_address_object, string, &sidechain_address_object::withdraw_public_key>
|
||||
>,
|
||||
ordered_non_unique< tag<by_account_and_sidechain_and_expires>,
|
||||
composite_key<sidechain_address_object,
|
||||
member<sidechain_address_object, account_id_type, &sidechain_address_object::sidechain_address_account>,
|
||||
member<sidechain_address_object, sidechain_type, &sidechain_address_object::sidechain>,
|
||||
member<sidechain_address_object, time_point_sec, &sidechain_address_object::expires>
|
||||
>
|
||||
>,
|
||||
ordered_non_unique< tag<by_sidechain_and_deposit_address_and_expires>,
|
||||
composite_key<sidechain_address_object,
|
||||
member<sidechain_address_object, sidechain_type, &sidechain_address_object::sidechain>,
|
||||
member<sidechain_address_object, string, &sidechain_address_object::deposit_address>,
|
||||
member<sidechain_address_object, time_point_sec, &sidechain_address_object::expires>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using sidechain_address_index = generic_index<sidechain_address_object, sidechain_address_multi_index_type>;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::sidechain_address_object, (graphene::db::object),
|
||||
(sidechain_address_account) (sidechain)
|
||||
(deposit_public_key) (deposit_address) (deposit_address_data)
|
||||
(withdraw_public_key) (withdraw_address) (valid_from) (expires) )
|
||||
22
libraries/chain/include/graphene/chain/sidechain_defs.hpp
Normal file
22
libraries/chain/include/graphene/chain/sidechain_defs.hpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
enum class sidechain_type {
|
||||
unknown,
|
||||
bitcoin,
|
||||
ethereum,
|
||||
eos,
|
||||
peerplays
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::sidechain_type,
|
||||
(unknown)
|
||||
(bitcoin)
|
||||
(ethereum)
|
||||
(eos)
|
||||
(peerplays) )
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/sidechain_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class sidechain_transaction_create_evaluator : public evaluator<sidechain_transaction_create_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_transaction_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_transaction_create_operation& o);
|
||||
object_id_type do_apply(const sidechain_transaction_create_operation& o);
|
||||
};
|
||||
|
||||
class sidechain_transaction_sign_evaluator : public evaluator<sidechain_transaction_sign_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_transaction_sign_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_transaction_sign_operation& o);
|
||||
object_id_type do_apply(const sidechain_transaction_sign_operation& o);
|
||||
};
|
||||
|
||||
class sidechain_transaction_send_evaluator : public evaluator<sidechain_transaction_send_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_transaction_send_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_transaction_send_operation& o);
|
||||
object_id_type do_apply(const sidechain_transaction_send_operation& o);
|
||||
};
|
||||
|
||||
class sidechain_transaction_settle_evaluator : public evaluator<sidechain_transaction_settle_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef sidechain_transaction_settle_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const sidechain_transaction_settle_operation& o);
|
||||
object_id_type do_apply(const sidechain_transaction_settle_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
#include <boost/multi_index/composite_key.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
enum class sidechain_transaction_status {
|
||||
invalid,
|
||||
valid,
|
||||
complete,
|
||||
sent,
|
||||
settled
|
||||
};
|
||||
|
||||
/**
|
||||
* @class sidechain_transaction_object
|
||||
* @brief tracks state of sidechain transaction during signing process.
|
||||
* @ingroup object
|
||||
*/
|
||||
class sidechain_transaction_object : public abstract_object<sidechain_transaction_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = sidechain_transaction_object_type;
|
||||
|
||||
sidechain_type sidechain;
|
||||
object_id_type object_id;
|
||||
std::string transaction;
|
||||
std::vector<son_info> signers;
|
||||
std::vector<std::pair<son_id_type, std::string>> signatures;
|
||||
std::string sidechain_transaction;
|
||||
|
||||
uint32_t total_weight = 0;
|
||||
uint32_t current_weight = 0;
|
||||
uint32_t threshold = 0;
|
||||
|
||||
sidechain_transaction_status status;
|
||||
};
|
||||
|
||||
struct by_object_id;
|
||||
struct by_sidechain_and_status;
|
||||
using sidechain_transaction_multi_index_type = multi_index_container<
|
||||
sidechain_transaction_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_object_id>,
|
||||
member<sidechain_transaction_object, object_id_type, &sidechain_transaction_object::object_id>
|
||||
>,
|
||||
ordered_non_unique< tag<by_sidechain_and_status>,
|
||||
composite_key<sidechain_transaction_object,
|
||||
member<sidechain_transaction_object, sidechain_type, &sidechain_transaction_object::sidechain>,
|
||||
member<sidechain_transaction_object, sidechain_transaction_status, &sidechain_transaction_object::status>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using sidechain_transaction_index = generic_index<sidechain_transaction_object, sidechain_transaction_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::sidechain_transaction_status,
|
||||
(invalid)
|
||||
(valid)
|
||||
(complete)
|
||||
(sent)
|
||||
(settled) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::sidechain_transaction_object, (graphene::db::object ),
|
||||
(sidechain)
|
||||
(object_id)
|
||||
(transaction)
|
||||
(signers)
|
||||
(signatures)
|
||||
(sidechain_transaction)
|
||||
(total_weight)
|
||||
(current_weight)
|
||||
(threshold)
|
||||
(status) )
|
||||
61
libraries/chain/include/graphene/chain/son_evaluator.hpp
Normal file
61
libraries/chain/include/graphene/chain/son_evaluator.hpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/son.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class create_son_evaluator : public evaluator<create_son_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_create_operation& o);
|
||||
object_id_type do_apply(const son_create_operation& o);
|
||||
};
|
||||
|
||||
class update_son_evaluator : public evaluator<update_son_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_update_operation& o);
|
||||
object_id_type do_apply(const son_update_operation& o);
|
||||
};
|
||||
|
||||
class deregister_son_evaluator : public evaluator<deregister_son_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_deregister_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_deregister_operation& o);
|
||||
void_result do_apply(const son_deregister_operation& o);
|
||||
};
|
||||
|
||||
class son_heartbeat_evaluator : public evaluator<son_heartbeat_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_heartbeat_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_heartbeat_operation& o);
|
||||
object_id_type do_apply(const son_heartbeat_operation& o);
|
||||
};
|
||||
|
||||
class son_report_down_evaluator : public evaluator<son_report_down_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_report_down_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_report_down_operation& o);
|
||||
object_id_type do_apply(const son_report_down_operation& o);
|
||||
};
|
||||
|
||||
class son_maintenance_evaluator : public evaluator<son_maintenance_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_maintenance_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_maintenance_operation& o);
|
||||
object_id_type do_apply(const son_maintenance_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
47
libraries/chain/include/graphene/chain/son_info.hpp
Normal file
47
libraries/chain/include/graphene/chain/son_info.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_info
|
||||
* @brief tracks information about a SON info required to re/create primary wallet
|
||||
* @ingroup object
|
||||
*/
|
||||
struct son_info {
|
||||
son_id_type son_id;
|
||||
weight_type weight = 0;
|
||||
public_key_type signing_key;
|
||||
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
bool operator==(const son_info& rhs) {
|
||||
bool son_sets_equal =
|
||||
(son_id == rhs.son_id) &&
|
||||
(weight == rhs.weight) &&
|
||||
(signing_key == rhs.signing_key) &&
|
||||
(sidechain_public_keys.size() == rhs.sidechain_public_keys.size());
|
||||
|
||||
if (son_sets_equal) {
|
||||
bool sidechain_public_keys_equal = true;
|
||||
for (size_t i = 0; i < sidechain_public_keys.size(); i++) {
|
||||
const auto lhs_scpk = sidechain_public_keys.nth(i);
|
||||
const auto rhs_scpk = rhs.sidechain_public_keys.nth(i);
|
||||
sidechain_public_keys_equal = sidechain_public_keys_equal &&
|
||||
(lhs_scpk->first == rhs_scpk->first) &&
|
||||
(lhs_scpk->second == rhs_scpk->second);
|
||||
}
|
||||
son_sets_equal = son_sets_equal && sidechain_public_keys_equal;
|
||||
}
|
||||
return son_sets_equal;
|
||||
}
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
FC_REFLECT( graphene::chain::son_info,
|
||||
(son_id)
|
||||
(weight)
|
||||
(signing_key)
|
||||
(sidechain_public_keys) )
|
||||
133
libraries/chain/include/graphene/chain/son_object.hpp
Normal file
133
libraries/chain/include/graphene/chain/son_object.hpp
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
enum class son_status
|
||||
{
|
||||
inactive,
|
||||
active,
|
||||
request_maintenance,
|
||||
in_maintenance,
|
||||
deregistered
|
||||
};
|
||||
/**
|
||||
* @class son_statistics_object
|
||||
* @ingroup object
|
||||
* @ingroup implementation
|
||||
*
|
||||
* This object contains regularly updated statistical data about an SON. It is provided for the purpose of
|
||||
* separating the SON transaction data that changes frequently from the SON object data that is mostly static.
|
||||
*/
|
||||
class son_statistics_object : public graphene::db::abstract_object<son_statistics_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_son_statistics_object_type;
|
||||
|
||||
son_id_type owner;
|
||||
// Lifetime total transactions signed
|
||||
uint64_t total_txs_signed = 0;
|
||||
// Transactions signed since the last son payouts
|
||||
uint64_t txs_signed = 0;
|
||||
// Total Voted Active time i.e. duration selected as part of voted active SONs
|
||||
uint64_t total_voted_time = 0;
|
||||
// Total Downtime barring the current down time in seconds, used for stats to present to user
|
||||
uint64_t total_downtime = 0;
|
||||
// Current Interval Downtime since last maintenance
|
||||
uint64_t current_interval_downtime = 0;
|
||||
// Down timestamp, if son status is in_maintenance use this
|
||||
fc::time_point_sec last_down_timestamp;
|
||||
// Last Active heartbeat timestamp
|
||||
fc::time_point_sec last_active_timestamp;
|
||||
// Deregistered Timestamp
|
||||
fc::time_point_sec deregistered_timestamp;
|
||||
// Total sidechain transactions reported by SON network while SON was active
|
||||
uint64_t total_sidechain_txs_reported = 0;
|
||||
// Sidechain transactions reported by this SON
|
||||
uint64_t sidechain_txs_reported = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class son_object
|
||||
* @brief tracks information about a SON account.
|
||||
* @ingroup object
|
||||
*/
|
||||
class son_object : public abstract_object<son_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_object_type;
|
||||
|
||||
account_id_type son_account;
|
||||
vote_id_type vote_id;
|
||||
uint64_t total_votes = 0;
|
||||
string url;
|
||||
vesting_balance_id_type deposit;
|
||||
public_key_type signing_key;
|
||||
vesting_balance_id_type pay_vb;
|
||||
son_statistics_id_type statistics;
|
||||
son_status status = son_status::inactive;
|
||||
flat_map<sidechain_type, string> sidechain_public_keys;
|
||||
|
||||
void pay_son_fee(share_type pay, database& db);
|
||||
bool has_valid_config()const;
|
||||
};
|
||||
|
||||
struct by_account;
|
||||
struct by_vote_id;
|
||||
using son_multi_index_type = multi_index_container<
|
||||
son_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_account>,
|
||||
member<son_object, account_id_type, &son_object::son_account>
|
||||
>,
|
||||
ordered_unique< tag<by_vote_id>,
|
||||
member<son_object, vote_id_type, &son_object::vote_id>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_index = generic_index<son_object, son_multi_index_type>;
|
||||
|
||||
struct by_owner;
|
||||
using son_stats_multi_index_type = multi_index_container<
|
||||
son_statistics_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_owner>,
|
||||
member<son_statistics_object, son_id_type, &son_statistics_object::owner>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_stats_index = generic_index<son_statistics_object, son_stats_multi_index_type>;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_ENUM(graphene::chain::son_status, (inactive)(active)(request_maintenance)(in_maintenance)(deregistered) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_object, (graphene::db::object),
|
||||
(son_account)(vote_id)(total_votes)(url)(deposit)(signing_key)(pay_vb)(statistics)(status)(sidechain_public_keys) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_statistics_object,
|
||||
(graphene::db::object),
|
||||
(owner)
|
||||
(total_txs_signed)
|
||||
(txs_signed)
|
||||
(total_voted_time)
|
||||
(total_downtime)
|
||||
(current_interval_downtime)
|
||||
(last_down_timestamp)
|
||||
(last_active_timestamp)
|
||||
(deregistered_timestamp)
|
||||
(total_sidechain_txs_reported)
|
||||
(sidechain_txs_reported)
|
||||
)
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/db/object.hpp>
|
||||
#include <graphene/db/generic_index.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
enum class son_proposal_type
|
||||
{
|
||||
son_deregister_proposal,
|
||||
son_report_down_proposal
|
||||
};
|
||||
|
||||
class son_proposal_object : public abstract_object<son_proposal_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_proposal_object_type;
|
||||
|
||||
son_proposal_id_type get_id()const { return id; }
|
||||
|
||||
proposal_id_type proposal_id;
|
||||
son_id_type son_id;
|
||||
son_proposal_type proposal_type;
|
||||
};
|
||||
|
||||
struct by_proposal;
|
||||
using son_proposal_multi_index_container = multi_index_container<
|
||||
son_proposal_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag< by_id >, member< object, object_id_type, &object::id > >,
|
||||
ordered_unique< tag< by_proposal >, member< son_proposal_object, proposal_id_type, &son_proposal_object::proposal_id > >
|
||||
>
|
||||
>;
|
||||
using son_proposal_index = generic_index<son_proposal_object, son_proposal_multi_index_container>;
|
||||
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_ENUM( graphene::chain::son_proposal_type, (son_deregister_proposal)(son_report_down_proposal) )
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_proposal_object, (graphene::chain::object), (proposal_id)(son_id)(proposal_type) )
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class create_son_wallet_deposit_evaluator : public evaluator<create_son_wallet_deposit_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_deposit_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_deposit_create_operation& o);
|
||||
object_id_type do_apply(const son_wallet_deposit_create_operation& o);
|
||||
};
|
||||
|
||||
class process_son_wallet_deposit_evaluator : public evaluator<process_son_wallet_deposit_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_deposit_process_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_deposit_process_operation& o);
|
||||
object_id_type do_apply(const son_wallet_deposit_process_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_wallet_deposit_object
|
||||
* @brief tracks information about a SON wallet deposit.
|
||||
* @ingroup object
|
||||
*/
|
||||
class son_wallet_deposit_object : public abstract_object<son_wallet_deposit_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_wallet_deposit_object_type;
|
||||
|
||||
time_point_sec timestamp;
|
||||
uint32_t block_num;
|
||||
sidechain_type sidechain = sidechain_type::unknown;
|
||||
std::string sidechain_uid;
|
||||
std::string sidechain_transaction_id;
|
||||
std::string sidechain_from;
|
||||
std::string sidechain_to;
|
||||
std::string sidechain_currency;
|
||||
safe<int64_t> sidechain_amount;
|
||||
chain::account_id_type peerplays_from;
|
||||
chain::account_id_type peerplays_to;
|
||||
chain::asset peerplays_asset;
|
||||
|
||||
std::map<son_id_type, weight_type> expected_reports;
|
||||
std::set<son_id_type> received_reports;
|
||||
|
||||
bool confirmed = false;
|
||||
|
||||
bool processed = false;
|
||||
};
|
||||
|
||||
struct by_sidechain_uid;
|
||||
struct by_sidechain_and_confirmed_and_processed;
|
||||
using son_wallet_deposit_multi_index_type = multi_index_container<
|
||||
son_wallet_deposit_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_sidechain_uid>,
|
||||
member<son_wallet_deposit_object, std::string, &son_wallet_deposit_object::sidechain_uid>
|
||||
>,
|
||||
ordered_non_unique< tag<by_sidechain_and_confirmed_and_processed>,
|
||||
composite_key<son_wallet_deposit_object,
|
||||
member<son_wallet_deposit_object, sidechain_type, &son_wallet_deposit_object::sidechain>,
|
||||
member<son_wallet_deposit_object, bool, &son_wallet_deposit_object::confirmed>,
|
||||
member<son_wallet_deposit_object, bool, &son_wallet_deposit_object::processed>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_wallet_deposit_index = generic_index<son_wallet_deposit_object, son_wallet_deposit_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_wallet_deposit_object, (graphene::db::object),
|
||||
(timestamp) (block_num) (sidechain)
|
||||
(sidechain_uid) (sidechain_transaction_id) (sidechain_from) (sidechain_to) (sidechain_currency) (sidechain_amount)
|
||||
(peerplays_from) (peerplays_to) (peerplays_asset)
|
||||
(expected_reports) (received_reports)
|
||||
(confirmed) (processed) )
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class recreate_son_wallet_evaluator : public evaluator<recreate_son_wallet_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_recreate_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_recreate_operation& o);
|
||||
object_id_type do_apply(const son_wallet_recreate_operation& o);
|
||||
};
|
||||
|
||||
class update_son_wallet_evaluator : public evaluator<update_son_wallet_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_update_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_update_operation& o);
|
||||
object_id_type do_apply(const son_wallet_update_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
47
libraries/chain/include/graphene/chain/son_wallet_object.hpp
Normal file
47
libraries/chain/include/graphene/chain/son_wallet_object.hpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_wallet_object
|
||||
* @brief tracks information about a SON wallet.
|
||||
* @ingroup object
|
||||
*/
|
||||
class son_wallet_object : public abstract_object<son_wallet_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_wallet_object_type;
|
||||
|
||||
time_point_sec valid_from;
|
||||
time_point_sec expires;
|
||||
|
||||
flat_map<sidechain_type, string> addresses;
|
||||
vector<son_info> sons;
|
||||
};
|
||||
|
||||
struct by_valid_from;
|
||||
struct by_expires;
|
||||
using son_wallet_multi_index_type = multi_index_container<
|
||||
son_wallet_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_valid_from>,
|
||||
member<son_wallet_object, time_point_sec, &son_wallet_object::valid_from>
|
||||
>,
|
||||
ordered_unique< tag<by_expires>,
|
||||
member<son_wallet_object, time_point_sec, &son_wallet_object::expires>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_wallet_index = generic_index<son_wallet_object, son_wallet_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_wallet_object, (graphene::db::object),
|
||||
(valid_from) (expires) (addresses) (sons) )
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/evaluator.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class create_son_wallet_withdraw_evaluator : public evaluator<create_son_wallet_withdraw_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_withdraw_create_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_withdraw_create_operation& o);
|
||||
object_id_type do_apply(const son_wallet_withdraw_create_operation& o);
|
||||
};
|
||||
|
||||
class process_son_wallet_withdraw_evaluator : public evaluator<process_son_wallet_withdraw_evaluator>
|
||||
{
|
||||
public:
|
||||
typedef son_wallet_withdraw_process_operation operation_type;
|
||||
|
||||
void_result do_evaluate(const son_wallet_withdraw_process_operation& o);
|
||||
object_id_type do_apply(const son_wallet_withdraw_process_operation& o);
|
||||
};
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
using namespace graphene::db;
|
||||
|
||||
/**
|
||||
* @class son_wallet_withdraw_object
|
||||
* @brief tracks information about a SON wallet withdrawal.
|
||||
* @ingroup object
|
||||
*/
|
||||
class son_wallet_withdraw_object : public abstract_object<son_wallet_withdraw_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = protocol_ids;
|
||||
static const uint8_t type_id = son_wallet_withdraw_object_type;
|
||||
|
||||
time_point_sec timestamp;
|
||||
uint32_t block_num;
|
||||
sidechain_type sidechain = sidechain_type::unknown;
|
||||
std::string peerplays_uid;
|
||||
std::string peerplays_transaction_id;
|
||||
chain::account_id_type peerplays_from;
|
||||
chain::asset peerplays_asset;
|
||||
sidechain_type withdraw_sidechain;
|
||||
std::string withdraw_address;
|
||||
std::string withdraw_currency;
|
||||
safe<int64_t> withdraw_amount;
|
||||
|
||||
std::map<son_id_type, weight_type> expected_reports;
|
||||
std::set<son_id_type> received_reports;
|
||||
|
||||
bool confirmed = false;
|
||||
|
||||
bool processed = false;
|
||||
};
|
||||
|
||||
struct by_peerplays_uid;
|
||||
struct by_withdraw_sidechain_and_confirmed_and_processed;
|
||||
using son_wallet_withdraw_multi_index_type = multi_index_container<
|
||||
son_wallet_withdraw_object,
|
||||
indexed_by<
|
||||
ordered_unique< tag<by_id>,
|
||||
member<object, object_id_type, &object::id>
|
||||
>,
|
||||
ordered_unique< tag<by_peerplays_uid>,
|
||||
member<son_wallet_withdraw_object, std::string, &son_wallet_withdraw_object::peerplays_uid>
|
||||
>,
|
||||
ordered_non_unique< tag<by_withdraw_sidechain_and_confirmed_and_processed>,
|
||||
composite_key<son_wallet_withdraw_object,
|
||||
member<son_wallet_withdraw_object, sidechain_type, &son_wallet_withdraw_object::withdraw_sidechain>,
|
||||
member<son_wallet_withdraw_object, bool, &son_wallet_withdraw_object::confirmed>,
|
||||
member<son_wallet_withdraw_object, bool, &son_wallet_withdraw_object::processed>
|
||||
>
|
||||
>
|
||||
>
|
||||
>;
|
||||
using son_wallet_withdraw_index = generic_index<son_wallet_withdraw_object, son_wallet_withdraw_multi_index_type>;
|
||||
} } // graphene::chain
|
||||
|
||||
FC_REFLECT_DERIVED( graphene::chain::son_wallet_withdraw_object, (graphene::db::object),
|
||||
(timestamp) (block_num) (sidechain)
|
||||
(peerplays_uid) (peerplays_transaction_id) (peerplays_from) (peerplays_asset)
|
||||
(withdraw_sidechain) (withdraw_address) (withdraw_currency) (withdraw_amount)
|
||||
(expected_reports) (received_reports)
|
||||
(confirmed) (processed) )
|
||||
|
|
@ -118,10 +118,32 @@ namespace graphene { namespace chain {
|
|||
void on_withdraw(const vesting_policy_context& ctx);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Cant withdraw anything while balance is in this policy.
|
||||
*
|
||||
* This policy is needed to register SON users where balance may be claimable only after
|
||||
* the SON object is deleted(plus a linear policy).
|
||||
* When deleting a SON member the dormant mode will be replaced by a linear policy.
|
||||
*
|
||||
* @note New funds may not be added to a dormant vesting balance.
|
||||
*/
|
||||
struct dormant_vesting_policy
|
||||
{
|
||||
asset get_allowed_withdraw(const vesting_policy_context& ctx)const;
|
||||
bool is_deposit_allowed(const vesting_policy_context& ctx)const;
|
||||
bool is_deposit_vested_allowed(const vesting_policy_context&)const { return false; }
|
||||
bool is_withdraw_allowed(const vesting_policy_context& ctx)const;
|
||||
void on_deposit(const vesting_policy_context& ctx);
|
||||
void on_deposit_vested(const vesting_policy_context&)
|
||||
{ FC_THROW( "May not deposit vested into a linear vesting balance." ); }
|
||||
void on_withdraw(const vesting_policy_context& ctx);
|
||||
};
|
||||
|
||||
typedef fc::static_variant<
|
||||
linear_vesting_policy,
|
||||
cdd_vesting_policy
|
||||
> vesting_policy;
|
||||
cdd_vesting_policy,
|
||||
dormant_vesting_policy
|
||||
> vesting_policy;
|
||||
|
||||
/**
|
||||
* Vesting balance object is a balance that is locked by the blockchain for a period of time.
|
||||
|
|
@ -140,7 +162,7 @@ namespace graphene { namespace chain {
|
|||
/// The vesting policy stores details on when funds vest, and controls when they may be withdrawn
|
||||
vesting_policy policy;
|
||||
|
||||
/// We can have 2 types of vesting, gpos and all the rest
|
||||
/// We can have 3 types of vesting, gpos, son and the rest
|
||||
vesting_balance_type balance_type = vesting_balance_type::normal;
|
||||
|
||||
vesting_balance_object() {}
|
||||
|
|
@ -224,6 +246,8 @@ FC_REFLECT(graphene::chain::cdd_vesting_policy,
|
|||
(coin_seconds_earned_last_update)
|
||||
)
|
||||
|
||||
FC_REFLECT(graphene::chain::dormant_vesting_policy, )
|
||||
|
||||
FC_REFLECT_TYPENAME( graphene::chain::vesting_policy )
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::chain::vesting_balance_object, (graphene::db::object),
|
||||
|
|
|
|||
|
|
@ -63,6 +63,17 @@ struct vote_counter
|
|||
out_auth = auth;
|
||||
}
|
||||
|
||||
void finish_2_3( authority& out_auth )
|
||||
{
|
||||
if( total_votes == 0 )
|
||||
return;
|
||||
assert( total_votes <= std::numeric_limits<uint32_t>::max() );
|
||||
uint32_t weight = uint32_t( total_votes );
|
||||
weight = (weight * 2 / 3) + 1;
|
||||
auth.weight_threshold = weight;
|
||||
out_auth = auth;
|
||||
}
|
||||
|
||||
bool is_empty()const
|
||||
{
|
||||
return (total_votes == 0);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@
|
|||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
class witness_schedule_object;
|
||||
class son_schedule_object;
|
||||
|
||||
typedef hash_ctr_rng<
|
||||
/* HashClass = */ fc::sha256,
|
||||
/* SeedLength = */ GRAPHENE_RNG_SEED_LENGTH
|
||||
|
|
@ -51,6 +54,22 @@ typedef generic_far_future_witness_scheduler<
|
|||
/* debug = */ true
|
||||
> far_future_witness_scheduler;
|
||||
|
||||
typedef generic_witness_scheduler<
|
||||
/* WitnessID = */ son_id_type,
|
||||
/* RNG = */ witness_scheduler_rng,
|
||||
/* CountType = */ decltype( chain_parameters::maximum_son_count ),
|
||||
/* OffsetType = */ uint32_t,
|
||||
/* debug = */ true
|
||||
> son_scheduler;
|
||||
|
||||
typedef generic_far_future_witness_scheduler<
|
||||
/* WitnessID = */ son_id_type,
|
||||
/* RNG = */ witness_scheduler_rng,
|
||||
/* CountType = */ decltype( chain_parameters::maximum_son_count ),
|
||||
/* OffsetType = */ uint32_t,
|
||||
/* debug = */ true
|
||||
> far_future_son_scheduler;
|
||||
|
||||
class witness_schedule_object : public graphene::db::abstract_object<witness_schedule_object>
|
||||
{
|
||||
public:
|
||||
|
|
@ -71,6 +90,26 @@ class witness_schedule_object : public graphene::db::abstract_object<witness_sch
|
|||
fc::uint128 recent_slots_filled;
|
||||
};
|
||||
|
||||
class son_schedule_object : public graphene::db::abstract_object<son_schedule_object>
|
||||
{
|
||||
public:
|
||||
static const uint8_t space_id = implementation_ids;
|
||||
static const uint8_t type_id = impl_son_schedule_object_type;
|
||||
|
||||
vector< son_id_type > current_shuffled_sons;
|
||||
|
||||
son_scheduler scheduler;
|
||||
uint32_t last_scheduling_block;
|
||||
uint64_t slots_since_genesis = 0;
|
||||
fc::array< char, sizeof(secret_hash_type) > rng_seed;
|
||||
|
||||
/**
|
||||
* Not necessary for consensus, but used for figuring out the participation rate.
|
||||
* The nth bit is 0 if the nth slot was unfilled, else it is 1.
|
||||
*/
|
||||
fc::uint128 recent_slots_filled;
|
||||
};
|
||||
|
||||
} }
|
||||
|
||||
|
||||
|
|
@ -94,6 +133,26 @@ FC_REFLECT_DERIVED(
|
|||
(recent_slots_filled)
|
||||
(current_shuffled_witnesses)
|
||||
)
|
||||
FC_REFLECT( graphene::chain::son_scheduler,
|
||||
(_turns)
|
||||
(_tokens)
|
||||
(_min_token_count)
|
||||
(_ineligible_waiting_for_token)
|
||||
(_ineligible_no_turn)
|
||||
(_eligible)
|
||||
(_schedule)
|
||||
(_lame_duck)
|
||||
)
|
||||
FC_REFLECT_DERIVED(
|
||||
graphene::chain::son_schedule_object,
|
||||
(graphene::db::object),
|
||||
(scheduler)
|
||||
(last_scheduling_block)
|
||||
(slots_since_genesis)
|
||||
(rng_seed)
|
||||
(recent_slots_filled)
|
||||
(current_shuffled_sons)
|
||||
)
|
||||
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_scheduler )
|
||||
GRAPHENE_EXTERNAL_SERIALIZATION( extern, graphene::chain::witness_schedule_object )
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <graphene/chain/proposal_evaluator.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/son_proposal_object.hpp>
|
||||
#include <graphene/chain/protocol/account.hpp>
|
||||
#include <graphene/chain/protocol/fee_schedule.hpp>
|
||||
#include <graphene/chain/protocol/tournament.hpp>
|
||||
|
|
@ -129,7 +130,7 @@ struct proposal_operation_hardfork_visitor
|
|||
|
||||
void operator()(const vesting_balance_create_operation &vbco) const {
|
||||
if(block_time < HARDFORK_GPOS_TIME)
|
||||
FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" );
|
||||
FC_ASSERT( vbco.balance_type == vesting_balance_type::normal, "balance_type in vesting create not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const custom_permission_create_operation &v) const {
|
||||
|
|
@ -208,6 +209,29 @@ struct proposal_operation_hardfork_visitor
|
|||
FC_ASSERT( block_time >= HARDFORK_NFT_TIME, "account_role_delete_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_create_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_create_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_update_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_update_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_deregister_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_deregister_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_heartbeat_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_heartbeat_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_report_down_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_report_down_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
void operator()(const son_maintenance_operation &v) const {
|
||||
FC_ASSERT( block_time >= HARDFORK_SON_TIME, "son_maintenance_operation not allowed yet!" );
|
||||
}
|
||||
|
||||
// loop and self visit in proposals
|
||||
void operator()(const proposal_create_operation &v) const {
|
||||
|
|
@ -216,6 +240,24 @@ struct proposal_operation_hardfork_visitor
|
|||
}
|
||||
};
|
||||
|
||||
void son_hardfork_visitor::operator()( const son_deregister_operation &v )
|
||||
{
|
||||
db.create<son_proposal_object>([&]( son_proposal_object& son_prop ) {
|
||||
son_prop.proposal_type = son_proposal_type::son_deregister_proposal;
|
||||
son_prop.proposal_id = prop_id;
|
||||
son_prop.son_id = v.son_id;
|
||||
});
|
||||
}
|
||||
|
||||
void son_hardfork_visitor::operator()( const son_report_down_operation &v )
|
||||
{
|
||||
db.create<son_proposal_object>([&]( son_proposal_object& son_prop ) {
|
||||
son_prop.proposal_type = son_proposal_type::son_report_down_proposal;
|
||||
son_prop.proposal_id = prop_id;
|
||||
son_prop.son_id = v.son_id;
|
||||
});
|
||||
}
|
||||
|
||||
void_result proposal_create_evaluator::do_evaluate( const proposal_create_operation& o )
|
||||
{ try {
|
||||
const database& d = db();
|
||||
|
|
@ -298,6 +340,12 @@ object_id_type proposal_create_evaluator::do_apply( const proposal_create_operat
|
|||
std::inserter(proposal.required_active_approvals, proposal.required_active_approvals.begin()));
|
||||
});
|
||||
|
||||
son_hardfork_visitor son_vtor(d, proposal.id);
|
||||
for(auto& op: o.proposed_ops)
|
||||
{
|
||||
op.op.visit(son_vtor);
|
||||
}
|
||||
|
||||
return proposal.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (o) ) }
|
||||
|
||||
|
|
|
|||
|
|
@ -174,15 +174,22 @@ void account_options::validate() const
|
|||
{
|
||||
auto needed_witnesses = num_witness;
|
||||
auto needed_committee = num_committee;
|
||||
auto needed_sons = num_son;
|
||||
|
||||
for( vote_id_type id : votes )
|
||||
if( id.type() == vote_id_type::witness && needed_witnesses )
|
||||
--needed_witnesses;
|
||||
else if ( id.type() == vote_id_type::committee && needed_committee )
|
||||
--needed_committee;
|
||||
else if ( id.type() == vote_id_type::son && needed_sons )
|
||||
--needed_sons;
|
||||
|
||||
FC_ASSERT( needed_witnesses == 0 && needed_committee == 0,
|
||||
"May not specify fewer witnesses or committee members than the number voted for.");
|
||||
FC_ASSERT( needed_witnesses == 0,
|
||||
"May not specify fewer witnesses than the number voted for.");
|
||||
FC_ASSERT( needed_committee == 0,
|
||||
"May not specify fewer committee members than the number voted for.");
|
||||
FC_ASSERT( needed_sons == 0,
|
||||
"May not specify fewer SONs than the number voted for.");
|
||||
}
|
||||
|
||||
void affiliate_reward_distribution::validate() const
|
||||
|
|
|
|||
114
libraries/chain/sidechain_address_evaluator.cpp
Normal file
114
libraries/chain/sidechain_address_evaluator.cpp
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
#include <graphene/chain/sidechain_address_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result add_sidechain_address_evaluator::do_evaluate(const sidechain_address_add_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT( op.deposit_public_key.length() > 0 && op.deposit_address.length() == 0 && op.deposit_address_data.length() == 0, "User should add a valid deposit public key and a null deposit address");
|
||||
const auto& sdpke_idx = db().get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_public_key_and_expires>();
|
||||
FC_ASSERT( sdpke_idx.find(boost::make_tuple(op.sidechain, op.deposit_public_key, time_point_sec::maximum())) == sdpke_idx.end(), "An active deposit key already exists" );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type add_sidechain_address_evaluator::do_apply(const sidechain_address_add_operation& op)
|
||||
{ try {
|
||||
const auto &sidechain_addresses_idx = db().get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain_and_expires>();
|
||||
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(op.sidechain_address_account, op.sidechain, time_point_sec::maximum()));
|
||||
|
||||
if (addr_itr != sidechain_addresses_idx.end())
|
||||
{
|
||||
db().modify(*addr_itr, [&](sidechain_address_object &sao) {
|
||||
sao.expires = db().head_block_time();
|
||||
});
|
||||
}
|
||||
|
||||
const auto& new_sidechain_address_object = db().create<sidechain_address_object>( [&]( sidechain_address_object& obj ){
|
||||
obj.sidechain_address_account = op.sidechain_address_account;
|
||||
obj.sidechain = op.sidechain;
|
||||
obj.deposit_public_key = op.deposit_public_key;
|
||||
obj.deposit_address = "";
|
||||
obj.deposit_address_data = "";
|
||||
obj.withdraw_public_key = op.withdraw_public_key;
|
||||
obj.withdraw_address = op.withdraw_address;
|
||||
obj.valid_from = db().head_block_time();
|
||||
obj.expires = time_point_sec::maximum();
|
||||
});
|
||||
return new_sidechain_address_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result update_sidechain_address_evaluator::do_evaluate(const sidechain_address_update_operation& op)
|
||||
{ try {
|
||||
const auto& sidx = db().get_index_type<son_index>().indices().get<by_account>();
|
||||
const auto& son_obj = sidx.find(op.payer);
|
||||
FC_ASSERT( son_obj != sidx.end() && db().is_son_active(son_obj->id), "Non active SON trying to update deposit address object" );
|
||||
const auto& sdpke_idx = db().get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_public_key_and_expires>();
|
||||
FC_ASSERT( op.deposit_address.valid() && op.deposit_public_key.valid() && op.deposit_address_data.valid(), "Update operation by SON is not valid");
|
||||
FC_ASSERT( (*op.deposit_address).length() > 0 && (*op.deposit_public_key).length() > 0 && (*op.deposit_address_data).length() > 0, "SON should create a valid deposit address with valid deposit public key");
|
||||
FC_ASSERT( sdpke_idx.find(boost::make_tuple(op.sidechain, *op.deposit_public_key, time_point_sec::maximum())) != sdpke_idx.end(), "Invalid update operation by SON" );
|
||||
FC_ASSERT( db().get(op.sidechain_address_id).sidechain_address_account == op.sidechain_address_account, "Invalid sidechain address account" );
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.sidechain_address_id) != idx.end(), "Invalid sidechain address ID" );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type update_sidechain_address_evaluator::do_apply(const sidechain_address_update_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.sidechain_address_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
// Case of change of Active SONs, store the outgoing address object with proper valid_from and expires updated
|
||||
if(itr->deposit_address.length() > 0) {
|
||||
db().create<sidechain_address_object>( [&]( sidechain_address_object& obj ) {
|
||||
obj.sidechain_address_account = op.sidechain_address_account;
|
||||
obj.sidechain = op.sidechain;
|
||||
obj.deposit_public_key = *op.deposit_public_key;
|
||||
obj.deposit_address = itr->deposit_address;
|
||||
obj.deposit_address_data = itr->deposit_address_data;
|
||||
obj.withdraw_public_key = *op.withdraw_public_key;
|
||||
obj.withdraw_address = *op.withdraw_address;
|
||||
obj.valid_from = itr->valid_from;
|
||||
obj.expires = db().head_block_time();
|
||||
});
|
||||
db().modify(*itr, [&](sidechain_address_object &sao) {
|
||||
if(op.deposit_address.valid()) sao.deposit_address = *op.deposit_address;
|
||||
if(op.deposit_address_data.valid()) sao.deposit_address_data = *op.deposit_address_data;
|
||||
sao.valid_from = db().head_block_time();
|
||||
});
|
||||
} else {
|
||||
// Case of SON creating deposit address for a user input
|
||||
db().modify(*itr, [&op](sidechain_address_object &sao) {
|
||||
if(op.deposit_address.valid()) sao.deposit_address = *op.deposit_address;
|
||||
if(op.deposit_address_data.valid()) sao.deposit_address_data = *op.deposit_address_data;
|
||||
});
|
||||
}
|
||||
}
|
||||
return op.sidechain_address_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result delete_sidechain_address_evaluator::do_evaluate(const sidechain_address_delete_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().get(op.sidechain_address_id).sidechain_address_account == op.sidechain_address_account);
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.sidechain_address_id) != idx.end() );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result delete_sidechain_address_evaluator::do_apply(const sidechain_address_delete_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<sidechain_address_index>().indices().get<by_id>();
|
||||
auto sidechain_address = idx.find(op.sidechain_address_id);
|
||||
if(sidechain_address != idx.end()) {
|
||||
db().modify(*sidechain_address, [&](sidechain_address_object &sao) {
|
||||
sao.expires = db().head_block_time();
|
||||
});
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
159
libraries/chain/sidechain_transaction_evaluator.cpp
Normal file
159
libraries/chain/sidechain_transaction_evaluator.cpp
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
#include <graphene/chain/sidechain_transaction_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result sidechain_transaction_create_evaluator::do_evaluate(const sidechain_transaction_create_operation &op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
FC_ASSERT((op.object_id.is<son_wallet_id_type>() || op.object_id.is<son_wallet_deposit_id_type>() || op.object_id.is<son_wallet_withdraw_id_type>()), "Invalid object id");
|
||||
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_object_id>();
|
||||
const auto &sto_obj = sto_idx.find(op.object_id);
|
||||
FC_ASSERT(sto_obj == sto_idx.end(), "Sidechain transaction for a given object is already created");
|
||||
|
||||
FC_ASSERT(!op.transaction.empty(), "Sidechain transaction data not set");
|
||||
FC_ASSERT(op.signers.size() > 0, "Sidechain transaction signers not set");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
object_id_type sidechain_transaction_create_evaluator::do_apply(const sidechain_transaction_create_operation &op)
|
||||
{ try {
|
||||
const auto &new_sidechain_transaction_object = db().create<sidechain_transaction_object>([&](sidechain_transaction_object &sto) {
|
||||
sto.sidechain = op.sidechain;
|
||||
sto.object_id = op.object_id;
|
||||
sto.transaction = op.transaction;
|
||||
sto.signers = op.signers;
|
||||
std::transform(op.signers.begin(), op.signers.end(), std::inserter(sto.signatures, sto.signatures.end()), [](const son_info &si) {
|
||||
return std::make_pair(si.son_id, std::string());
|
||||
});
|
||||
for (const auto &si : op.signers) {
|
||||
sto.total_weight = sto.total_weight + si.weight;
|
||||
}
|
||||
sto.sidechain_transaction = "";
|
||||
sto.current_weight = 0;
|
||||
sto.threshold = sto.total_weight * 2 / 3 + 1;
|
||||
sto.status = sidechain_transaction_status::valid;
|
||||
});
|
||||
return new_sidechain_transaction_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
void_result sidechain_transaction_sign_evaluator::do_evaluate(const sidechain_transaction_sign_operation &op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found");
|
||||
|
||||
const auto &son_idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
const auto &son_obj = son_idx.find(op.signer);
|
||||
FC_ASSERT(son_obj != son_idx.end(), "SON object not found");
|
||||
|
||||
bool expected = false;
|
||||
for (auto signature : sto_obj->signatures) {
|
||||
if (signature.first == son_obj->id) {
|
||||
expected = signature.second.empty();
|
||||
}
|
||||
}
|
||||
FC_ASSERT(expected, "Signer not expected");
|
||||
|
||||
FC_ASSERT(sto_obj->status == sidechain_transaction_status::valid, "Invalid transaction status");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
object_id_type sidechain_transaction_sign_evaluator::do_apply(const sidechain_transaction_sign_operation &op)
|
||||
{ try {
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
auto sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
|
||||
const auto &son_idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto son_obj = son_idx.find(op.signer);
|
||||
|
||||
db().modify(*sto_obj, [&](sidechain_transaction_object &sto) {
|
||||
for (size_t i = 0; i < sto.signatures.size(); i++) {
|
||||
if (sto.signatures.at(i).first == son_obj->id) {
|
||||
sto.signatures.at(i).second = op.signature;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < sto.signers.size(); i++) {
|
||||
if (sto.signers.at(i).son_id == son_obj->id) {
|
||||
sto.current_weight = sto.current_weight + sto.signers.at(i).weight;
|
||||
}
|
||||
}
|
||||
if (sto.current_weight >= sto.threshold) {
|
||||
sto.status = sidechain_transaction_status::complete;
|
||||
}
|
||||
});
|
||||
|
||||
db().modify(son_obj->statistics(db()), [&](son_statistics_object& sso) {
|
||||
sso.txs_signed += 1;
|
||||
});
|
||||
|
||||
return op.sidechain_transaction_id;
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
void_result sidechain_transaction_send_evaluator::do_evaluate(const sidechain_transaction_send_operation &op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found");
|
||||
|
||||
FC_ASSERT(sto_obj->status == sidechain_transaction_status::complete, "Invalid transaction status");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
object_id_type sidechain_transaction_send_evaluator::do_apply(const sidechain_transaction_send_operation &op)
|
||||
{ try {
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
auto sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
|
||||
db().modify(*sto_obj, [&](sidechain_transaction_object &sto) {
|
||||
sto.sidechain_transaction = op.sidechain_transaction;
|
||||
sto.status = sidechain_transaction_status::sent;
|
||||
});
|
||||
|
||||
return op.sidechain_transaction_id;
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
|
||||
void_result sidechain_transaction_settle_evaluator::do_evaluate(const sidechain_transaction_settle_operation &op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
const auto &sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
FC_ASSERT(sto_obj != sto_idx.end(), "Sidechain transaction object not found");
|
||||
|
||||
FC_ASSERT(sto_obj->status == sidechain_transaction_status::sent, "Invalid transaction status");
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
object_id_type sidechain_transaction_settle_evaluator::do_apply(const sidechain_transaction_settle_operation &op)
|
||||
{ try {
|
||||
const auto &sto_idx = db().get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
auto sto_obj = sto_idx.find(op.sidechain_transaction_id);
|
||||
|
||||
db().modify(*sto_obj, [&](sidechain_transaction_object &sto) {
|
||||
sto.status = sidechain_transaction_status::settled;
|
||||
});
|
||||
|
||||
return op.sidechain_transaction_id;
|
||||
} FC_CAPTURE_AND_RETHROW( ( op ) ) }
|
||||
|
||||
} // namespace chain
|
||||
} // namespace graphene
|
||||
243
libraries/chain/son_evaluator.cpp
Normal file
243
libraries/chain/son_evaluator.cpp
Normal file
|
|
@ -0,0 +1,243 @@
|
|||
#include <graphene/chain/son_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/witness_object.hpp>
|
||||
#include <graphene/chain/hardfork.hpp>
|
||||
#include <graphene/chain/vesting_balance_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result create_son_evaluator::do_evaluate(const son_create_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT(db().get(op.owner_account).is_lifetime_member(), "Only Lifetime members may register a SON.");
|
||||
FC_ASSERT(op.deposit(db()).policy.which() == vesting_policy::tag<dormant_vesting_policy>::value,
|
||||
"Deposit balance must have dormant vesting policy");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type create_son_evaluator::do_apply(const son_create_operation& op)
|
||||
{ try {
|
||||
vote_id_type vote_id;
|
||||
db().modify(db().get_global_properties(), [&vote_id](global_property_object& p) {
|
||||
vote_id = get_next_vote_id(p, vote_id_type::son);
|
||||
});
|
||||
|
||||
const auto& new_son_object = db().create<son_object>( [&]( son_object& obj ){
|
||||
obj.son_account = op.owner_account;
|
||||
obj.vote_id = vote_id;
|
||||
obj.url = op.url;
|
||||
obj.deposit = op.deposit;
|
||||
obj.signing_key = op.signing_key;
|
||||
obj.sidechain_public_keys = op.sidechain_public_keys;
|
||||
obj.pay_vb = op.pay_vb;
|
||||
obj.statistics = db().create<son_statistics_object>([&](son_statistics_object& s){s.owner = obj.id;}).id;
|
||||
});
|
||||
return new_son_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result update_son_evaluator::do_evaluate(const son_update_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
FC_ASSERT(db().get(op.son_id).son_account == op.owner_account);
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type update_son_evaluator::do_apply(const son_update_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
db().modify(*itr, [&op](son_object &so) {
|
||||
if(op.new_url.valid()) so.url = *op.new_url;
|
||||
if(op.new_deposit.valid()) so.deposit = *op.new_deposit;
|
||||
if(op.new_signing_key.valid()) so.signing_key = *op.new_signing_key;
|
||||
if(op.new_sidechain_public_keys.valid()) so.sidechain_public_keys = *op.new_sidechain_public_keys;
|
||||
if(op.new_pay_vb.valid()) so.pay_vb = *op.new_pay_vb;
|
||||
});
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result deregister_son_evaluator::do_evaluate(const son_deregister_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON_HARDFORK"); // can be removed after HF date pass
|
||||
// Only son account can deregister
|
||||
FC_ASSERT(db().is_son_dereg_valid(op.son_id) && op.payer == db().get_global_properties().parameters.son_account());
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result deregister_son_evaluator::do_apply(const son_deregister_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
const auto& ss_idx = db().get_index_type<son_stats_index>().indices().get<by_id>();
|
||||
auto son = idx.find(op.son_id);
|
||||
if(son != idx.end()) {
|
||||
vesting_balance_object deposit = son->deposit(db());
|
||||
linear_vesting_policy new_vesting_policy;
|
||||
new_vesting_policy.begin_timestamp = db().head_block_time();
|
||||
new_vesting_policy.vesting_cliff_seconds = db().get_global_properties().parameters.son_vesting_period();
|
||||
new_vesting_policy.begin_balance = deposit.balance.amount;
|
||||
|
||||
db().modify(son->deposit(db()), [&new_vesting_policy](vesting_balance_object &vbo) {
|
||||
vbo.policy = new_vesting_policy;
|
||||
});
|
||||
|
||||
db().modify(*son, [&op](son_object &so) {
|
||||
so.status = son_status::deregistered;
|
||||
});
|
||||
|
||||
auto stats_obj = ss_idx.find(son->statistics);
|
||||
if(stats_obj != ss_idx.end()) {
|
||||
db().modify(*stats_obj, [&]( son_statistics_object& sso) {
|
||||
sso.deregistered_timestamp = db().head_block_time();
|
||||
});
|
||||
}
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result son_heartbeat_evaluator::do_evaluate(const son_heartbeat_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
FC_ASSERT( itr != idx.end() );
|
||||
FC_ASSERT(itr->son_account == op.owner_account);
|
||||
auto stats = itr->statistics( db() );
|
||||
// Inactive SONs need not send heartbeats
|
||||
FC_ASSERT((itr->status == son_status::active) || (itr->status == son_status::in_maintenance) || (itr->status == son_status::request_maintenance), "Inactive SONs need not send heartbeats");
|
||||
// Account for network delays
|
||||
fc::time_point_sec min_ts = db().head_block_time() - fc::seconds(5 * db().block_interval());
|
||||
// Account for server ntp sync difference
|
||||
fc::time_point_sec max_ts = db().head_block_time() + fc::seconds(5 * db().block_interval());
|
||||
FC_ASSERT(op.ts > stats.last_active_timestamp, "Heartbeat sent without waiting minimum time");
|
||||
FC_ASSERT(op.ts > stats.last_down_timestamp, "Heartbeat sent is invalid can't be <= last down timestamp");
|
||||
FC_ASSERT(op.ts >= min_ts, "Heartbeat ts is behind the min threshold");
|
||||
FC_ASSERT(op.ts <= max_ts, "Heartbeat ts is above the max threshold");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type son_heartbeat_evaluator::do_apply(const son_heartbeat_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
const global_property_object& gpo = db().get_global_properties();
|
||||
vector<son_id_type> active_son_ids;
|
||||
active_son_ids.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info& swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
auto it_son = std::find(active_son_ids.begin(), active_son_ids.end(), op.son_id);
|
||||
bool is_son_active = true;
|
||||
|
||||
if(it_son == active_son_ids.end()) {
|
||||
is_son_active = false;
|
||||
}
|
||||
|
||||
if(itr->status == son_status::in_maintenance) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.current_interval_downtime += op.ts.sec_since_epoch() - sso.last_down_timestamp.sec_since_epoch();
|
||||
sso.last_active_timestamp = op.ts;
|
||||
} );
|
||||
|
||||
db().modify(*itr, [&is_son_active](son_object &so) {
|
||||
if(is_son_active) {
|
||||
so.status = son_status::active;
|
||||
} else {
|
||||
so.status = son_status::inactive;
|
||||
}
|
||||
});
|
||||
} else if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.last_active_timestamp = op.ts;
|
||||
} );
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result son_report_down_evaluator::do_evaluate(const son_report_down_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
FC_ASSERT(op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer.");
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_id) != idx.end() );
|
||||
auto itr = idx.find(op.son_id);
|
||||
auto stats = itr->statistics( db() );
|
||||
FC_ASSERT(itr->status == son_status::active || itr->status == son_status::request_maintenance, "Inactive/Deregistered/in_maintenance SONs cannot be reported on as down");
|
||||
FC_ASSERT(op.down_ts >= stats.last_active_timestamp, "down_ts should be greater than last_active_timestamp");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type son_report_down_evaluator::do_apply(const son_report_down_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
if ((itr->status == son_status::active) || (itr->status == son_status::request_maintenance)) {
|
||||
db().modify( itr->statistics( db() ), [&]( son_statistics_object& sso )
|
||||
{
|
||||
sso.last_down_timestamp = op.down_ts;
|
||||
});
|
||||
|
||||
db().modify(*itr, [&op](son_object &so) {
|
||||
so.status = son_status::in_maintenance;
|
||||
});
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result son_maintenance_evaluator::do_evaluate(const son_maintenance_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK"); // can be removed after HF date pass
|
||||
FC_ASSERT(db().get(op.son_id).son_account == op.owner_account);
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
FC_ASSERT( itr != idx.end() );
|
||||
// Inactive SONs can't go to maintenance, toggle between active and request_maintenance states
|
||||
if(op.request_type == son_maintenance_request_type::request_maintenance) {
|
||||
FC_ASSERT(itr->status == son_status::active, "Inactive SONs can't request for maintenance");
|
||||
} else if(op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
|
||||
FC_ASSERT(itr->status == son_status::request_maintenance, "Only maintenance requested SONs can cancel the request");
|
||||
} else {
|
||||
FC_ASSERT(false, "Invalid maintenance operation");
|
||||
}
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type son_maintenance_evaluator::do_apply(const son_maintenance_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
if(itr->status == son_status::active && op.request_type == son_maintenance_request_type::request_maintenance) {
|
||||
db().modify(*itr, [](son_object &so) {
|
||||
so.status = son_status::request_maintenance;
|
||||
});
|
||||
} else if(itr->status == son_status::request_maintenance && op.request_type == son_maintenance_request_type::cancel_request_maintenance) {
|
||||
db().modify(*itr, [](son_object &so) {
|
||||
so.status = son_status::active;
|
||||
});
|
||||
}
|
||||
}
|
||||
return op.son_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
15
libraries/chain/son_object.cpp
Normal file
15
libraries/chain/son_object.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
void son_object::pay_son_fee(share_type pay, database& db) {
|
||||
db.adjust_balance(son_account, pay);
|
||||
}
|
||||
|
||||
bool son_object::has_valid_config()const {
|
||||
return ((std::string(signing_key).length() > 0) &&
|
||||
(sidechain_public_keys.size() > 0) &&
|
||||
(sidechain_public_keys.find( sidechain_type::bitcoin ) != sidechain_public_keys.end()) &&
|
||||
(sidechain_public_keys.at(sidechain_type::bitcoin).length() > 0));
|
||||
}
|
||||
}}
|
||||
157
libraries/chain/son_wallet_deposit_evaluator.cpp
Normal file
157
libraries/chain/son_wallet_deposit_evaluator.cpp
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
#include <graphene/chain/son_wallet_deposit_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/chain_property_object.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_wallet_deposit_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result create_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
|
||||
const auto &son_idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
const auto so = son_idx.find(op.son_id);
|
||||
FC_ASSERT(so != son_idx.end(), "SON not found");
|
||||
FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner");
|
||||
|
||||
const auto &ss_idx = db().get_index_type<son_stats_index>().indices().get<by_owner>();
|
||||
FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists");
|
||||
|
||||
const auto &swdo_idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_sidechain_uid>();
|
||||
const auto swdo = swdo_idx.find(op.sidechain_uid);
|
||||
if (swdo == swdo_idx.end()) {
|
||||
auto &gpo = db().get_global_properties();
|
||||
bool expected = false;
|
||||
for (auto &si : gpo.active_sons) {
|
||||
if (op.son_id == si.son_id) {
|
||||
expected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FC_ASSERT(expected, "Only active SON can create deposit");
|
||||
} else {
|
||||
bool exactly_the_same = true;
|
||||
exactly_the_same = exactly_the_same && (swdo->sidechain == op.sidechain);
|
||||
exactly_the_same = exactly_the_same && (swdo->sidechain_uid == op.sidechain_uid);
|
||||
exactly_the_same = exactly_the_same && (swdo->sidechain_transaction_id == op.sidechain_transaction_id);
|
||||
exactly_the_same = exactly_the_same && (swdo->sidechain_from == op.sidechain_from);
|
||||
exactly_the_same = exactly_the_same && (swdo->sidechain_to == op.sidechain_to);
|
||||
exactly_the_same = exactly_the_same && (swdo->sidechain_currency == op.sidechain_currency);
|
||||
exactly_the_same = exactly_the_same && (swdo->sidechain_amount == op.sidechain_amount);
|
||||
exactly_the_same = exactly_the_same && (swdo->peerplays_from == op.peerplays_from);
|
||||
exactly_the_same = exactly_the_same && (swdo->peerplays_to == op.peerplays_to);
|
||||
exactly_the_same = exactly_the_same && (swdo->peerplays_asset == op.peerplays_asset);
|
||||
FC_ASSERT(exactly_the_same, "Invalid withdraw confirmation");
|
||||
|
||||
bool expected = false;
|
||||
for (auto &exp : swdo->expected_reports) {
|
||||
if (op.son_id == exp.first) {
|
||||
expected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FC_ASSERT(expected, "Confirmation from this SON not expected");
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type create_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_create_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_sidechain_uid>();
|
||||
auto itr = idx.find(op.sidechain_uid);
|
||||
if (itr == idx.end()) {
|
||||
const auto& new_son_wallet_deposit_object = db().create<son_wallet_deposit_object>( [&]( son_wallet_deposit_object& swdo ){
|
||||
swdo.timestamp = op.timestamp;
|
||||
swdo.block_num = op.block_num;
|
||||
swdo.sidechain = op.sidechain;
|
||||
swdo.sidechain_uid = op.sidechain_uid;
|
||||
swdo.sidechain_transaction_id = op.sidechain_transaction_id;
|
||||
swdo.sidechain_from = op.sidechain_from;
|
||||
swdo.sidechain_to = op.sidechain_to;
|
||||
swdo.sidechain_currency = op.sidechain_currency;
|
||||
swdo.sidechain_amount = op.sidechain_amount;
|
||||
swdo.peerplays_from = op.peerplays_from;
|
||||
swdo.peerplays_to = op.peerplays_to;
|
||||
swdo.peerplays_asset = op.peerplays_asset;
|
||||
|
||||
auto &gpo = db().get_global_properties();
|
||||
for (auto &si : gpo.active_sons) {
|
||||
swdo.expected_reports.insert(std::make_pair(si.son_id, si.weight));
|
||||
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
|
||||
db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) {
|
||||
sso.total_sidechain_txs_reported = sso.total_sidechain_txs_reported + 1;
|
||||
if (si.son_id == op.son_id) {
|
||||
sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
swdo.received_reports.insert(op.son_id);
|
||||
|
||||
uint64_t total_weight = 0;
|
||||
for (const auto exp : swdo.expected_reports) {
|
||||
total_weight = total_weight + exp.second;
|
||||
}
|
||||
uint64_t current_weight = 0;
|
||||
for (const auto rec : swdo.received_reports) {
|
||||
current_weight = current_weight + swdo.expected_reports.find(rec)->second;
|
||||
}
|
||||
swdo.confirmed = (current_weight > (total_weight * 2 / 3));
|
||||
|
||||
swdo.processed = false;
|
||||
});
|
||||
return new_son_wallet_deposit_object.id;
|
||||
} else {
|
||||
db().modify(*itr, [&op](son_wallet_deposit_object &swdo) {
|
||||
swdo.received_reports.insert(op.son_id);
|
||||
|
||||
uint64_t total_weight = 0;
|
||||
for (const auto exp : swdo.expected_reports) {
|
||||
total_weight = total_weight + exp.second;
|
||||
}
|
||||
uint64_t current_weight = 0;
|
||||
for (const auto rec : swdo.received_reports) {
|
||||
current_weight = current_weight + swdo.expected_reports.find(rec)->second;
|
||||
}
|
||||
swdo.confirmed = (current_weight > (total_weight * 2 / 3));
|
||||
});
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(op.son_id);
|
||||
db().modify(*stats_itr, [&op](son_statistics_object &sso) {
|
||||
sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1;
|
||||
});
|
||||
return (*itr).id;
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result process_son_wallet_deposit_evaluator::do_evaluate(const son_wallet_deposit_process_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
|
||||
const auto& itr = idx.find(op.son_wallet_deposit_id);
|
||||
FC_ASSERT(itr != idx.end(), "Son wallet deposit not found");
|
||||
FC_ASSERT(!itr->processed, "Son wallet deposit is already processed");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type process_son_wallet_deposit_evaluator::do_apply(const son_wallet_deposit_process_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_wallet_deposit_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
db().modify(*itr, [&op](son_wallet_deposit_object &swdo) {
|
||||
swdo.processed = true;
|
||||
});
|
||||
}
|
||||
return op.son_wallet_deposit_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
81
libraries/chain/son_wallet_evaluator.cpp
Normal file
81
libraries/chain/son_wallet_evaluator.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
#include <graphene/chain/son_wallet_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result recreate_son_wallet_evaluator::do_evaluate(const son_wallet_recreate_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.rbegin();
|
||||
if(itr != idx.rend())
|
||||
{
|
||||
// Compare current wallet SONs and to-be lists of active sons
|
||||
auto cur_wallet_sons = (*itr).sons;
|
||||
auto new_wallet_sons = op.sons;
|
||||
|
||||
bool son_sets_equal = (cur_wallet_sons.size() == new_wallet_sons.size());
|
||||
if (son_sets_equal) {
|
||||
for( size_t i = 0; i < cur_wallet_sons.size(); i++ ) {
|
||||
son_sets_equal = son_sets_equal && cur_wallet_sons.at(i) == new_wallet_sons.at(i);
|
||||
}
|
||||
}
|
||||
|
||||
FC_ASSERT(son_sets_equal == false, "Wallet recreation not needed, active SONs set is not changed.");
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type recreate_son_wallet_evaluator::do_apply(const son_wallet_recreate_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.rbegin();
|
||||
if(itr != idx.rend())
|
||||
{
|
||||
db().modify(*itr, [&, op](son_wallet_object &swo) {
|
||||
swo.expires = db().head_block_time();
|
||||
});
|
||||
}
|
||||
|
||||
const auto& new_son_wallet_object = db().create<son_wallet_object>( [&]( son_wallet_object& obj ){
|
||||
obj.valid_from = db().head_block_time();
|
||||
obj.expires = time_point_sec::maximum();
|
||||
obj.sons = op.sons;
|
||||
});
|
||||
return new_son_wallet_object.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result update_son_wallet_evaluator::do_evaluate(const son_wallet_update_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
FC_ASSERT( idx.find(op.son_wallet_id) != idx.end() );
|
||||
//auto itr = idx.find(op.son_wallet_id);
|
||||
//FC_ASSERT( itr->addresses.find(op.sidechain) == itr->addresses.end() ||
|
||||
// itr->addresses.at(op.sidechain).empty(), "Sidechain wallet address already set");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type update_son_wallet_evaluator::do_apply(const son_wallet_update_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_wallet_id);
|
||||
if (itr != idx.end())
|
||||
{
|
||||
if (itr->addresses.find(op.sidechain) == itr->addresses.end()) {
|
||||
db().modify(*itr, [&op](son_wallet_object &swo) {
|
||||
swo.addresses[op.sidechain] = op.address;
|
||||
});
|
||||
}
|
||||
}
|
||||
return op.son_wallet_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
155
libraries/chain/son_wallet_withdraw_evaluator.cpp
Normal file
155
libraries/chain/son_wallet_withdraw_evaluator.cpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#include <graphene/chain/son_wallet_withdraw_evaluator.hpp>
|
||||
|
||||
#include <graphene/chain/database.hpp>
|
||||
#include <graphene/chain/chain_property_object.hpp>
|
||||
#include <graphene/chain/is_authorized_asset.hpp>
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
|
||||
namespace graphene { namespace chain {
|
||||
|
||||
void_result create_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_create_operation& op)
|
||||
{ try {
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
|
||||
const auto &son_idx = db().get_index_type<son_index>().indices().get<by_id>();
|
||||
const auto so = son_idx.find(op.son_id);
|
||||
FC_ASSERT(so != son_idx.end(), "SON not found");
|
||||
FC_ASSERT(so->son_account == op.payer, "Payer is not SON account owner");
|
||||
|
||||
const auto &ss_idx = db().get_index_type<son_stats_index>().indices().get<by_owner>();
|
||||
FC_ASSERT(ss_idx.find(op.son_id) != ss_idx.end(), "Statistic object for a given SON ID does not exists");
|
||||
|
||||
const auto &swwo_idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_peerplays_uid>();
|
||||
const auto swwo = swwo_idx.find(op.peerplays_uid);
|
||||
if (swwo == swwo_idx.end()) {
|
||||
auto &gpo = db().get_global_properties();
|
||||
bool expected = false;
|
||||
for (auto &si : gpo.active_sons) {
|
||||
if (op.son_id == si.son_id) {
|
||||
expected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FC_ASSERT(expected, "Only active SON can create deposit");
|
||||
} else {
|
||||
bool exactly_the_same = true;
|
||||
exactly_the_same = exactly_the_same && (swwo->sidechain == op.sidechain);
|
||||
exactly_the_same = exactly_the_same && (swwo->peerplays_uid == op.peerplays_uid);
|
||||
exactly_the_same = exactly_the_same && (swwo->peerplays_transaction_id == op.peerplays_transaction_id);
|
||||
exactly_the_same = exactly_the_same && (swwo->peerplays_from == op.peerplays_from);
|
||||
exactly_the_same = exactly_the_same && (swwo->peerplays_asset == op.peerplays_asset);
|
||||
exactly_the_same = exactly_the_same && (swwo->withdraw_sidechain == op.withdraw_sidechain);
|
||||
exactly_the_same = exactly_the_same && (swwo->withdraw_address == op.withdraw_address);
|
||||
exactly_the_same = exactly_the_same && (swwo->withdraw_currency == op.withdraw_currency);
|
||||
exactly_the_same = exactly_the_same && (swwo->withdraw_amount == op.withdraw_amount);
|
||||
FC_ASSERT(exactly_the_same, "Invalid withdraw confirmation");
|
||||
|
||||
bool expected = false;
|
||||
for (auto &exp : swwo->expected_reports) {
|
||||
if (op.son_id == exp.first) {
|
||||
expected = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FC_ASSERT(expected, "Confirmation from this SON not expected");
|
||||
}
|
||||
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type create_son_wallet_withdraw_evaluator::do_apply(const son_wallet_withdraw_create_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_peerplays_uid>();
|
||||
auto itr = idx.find(op.peerplays_uid);
|
||||
if (itr == idx.end()) {
|
||||
const auto& new_son_wallet_withdraw_object = db().create<son_wallet_withdraw_object>( [&]( son_wallet_withdraw_object& swwo ){
|
||||
swwo.timestamp = op.timestamp;
|
||||
swwo.block_num = op.block_num;
|
||||
swwo.sidechain = op.sidechain;
|
||||
swwo.peerplays_uid = op.peerplays_uid;
|
||||
swwo.peerplays_transaction_id = op.peerplays_transaction_id;
|
||||
swwo.peerplays_from = op.peerplays_from;
|
||||
swwo.peerplays_asset = op.peerplays_asset;
|
||||
swwo.withdraw_sidechain = op.withdraw_sidechain;
|
||||
swwo.withdraw_address = op.withdraw_address;
|
||||
swwo.withdraw_currency = op.withdraw_currency;
|
||||
swwo.withdraw_amount = op.withdraw_amount;
|
||||
|
||||
auto &gpo = db().get_global_properties();
|
||||
for (auto &si : gpo.active_sons) {
|
||||
swwo.expected_reports.insert(std::make_pair(si.son_id, si.weight));
|
||||
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(si.son_id);
|
||||
db().modify(*stats_itr, [&op, &si](son_statistics_object &sso) {
|
||||
sso.total_sidechain_txs_reported = sso.total_sidechain_txs_reported + 1;
|
||||
if (si.son_id == op.son_id) {
|
||||
sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
swwo.received_reports.insert(op.son_id);
|
||||
|
||||
uint64_t total_weight = 0;
|
||||
for (const auto exp : swwo.expected_reports) {
|
||||
total_weight = total_weight + exp.second;
|
||||
}
|
||||
uint64_t current_weight = 0;
|
||||
for (const auto rec : swwo.received_reports) {
|
||||
current_weight = current_weight + swwo.expected_reports.find(rec)->second;
|
||||
}
|
||||
swwo.confirmed = (current_weight > (total_weight * 2 / 3));
|
||||
|
||||
swwo.processed = false;
|
||||
});
|
||||
return new_son_wallet_withdraw_object.id;
|
||||
} else {
|
||||
db().modify(*itr, [&op](son_wallet_withdraw_object &swwo) {
|
||||
swwo.received_reports.insert(op.son_id);
|
||||
|
||||
uint64_t total_weight = 0;
|
||||
for (const auto exp : swwo.expected_reports) {
|
||||
total_weight = total_weight + exp.second;
|
||||
}
|
||||
uint64_t current_weight = 0;
|
||||
for (const auto rec : swwo.received_reports) {
|
||||
current_weight = current_weight + swwo.expected_reports.find(rec)->second;
|
||||
}
|
||||
swwo.confirmed = (current_weight > (total_weight * 2 / 3));
|
||||
});
|
||||
auto stats_itr = db().get_index_type<son_stats_index>().indices().get<by_owner>().find(op.son_id);
|
||||
db().modify(*stats_itr, [&op](son_statistics_object &sso) {
|
||||
sso.sidechain_txs_reported = sso.sidechain_txs_reported + 1;
|
||||
});
|
||||
return (*itr).id;
|
||||
}
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
void_result process_son_wallet_withdraw_evaluator::do_evaluate(const son_wallet_withdraw_process_operation& op)
|
||||
{ try{
|
||||
FC_ASSERT(db().head_block_time() >= HARDFORK_SON_TIME, "Not allowed until SON HARDFORK");
|
||||
FC_ASSERT( op.payer == db().get_global_properties().parameters.son_account(), "SON paying account must be set as payer." );
|
||||
FC_ASSERT(db().get_global_properties().active_sons.size() >= db().get_chain_properties().immutable_parameters.min_son_count, "Min required voted SONs not present");
|
||||
|
||||
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
|
||||
const auto& itr = idx.find(op.son_wallet_withdraw_id);
|
||||
FC_ASSERT(itr != idx.end(), "Son wallet withdraw not found");
|
||||
FC_ASSERT(!itr->processed, "Son wallet withdraw is already processed");
|
||||
return void_result();
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
object_id_type process_son_wallet_withdraw_evaluator::do_apply(const son_wallet_withdraw_process_operation& op)
|
||||
{ try {
|
||||
const auto& idx = db().get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
|
||||
auto itr = idx.find(op.son_wallet_withdraw_id);
|
||||
if(itr != idx.end())
|
||||
{
|
||||
db().modify(*itr, [&op](son_wallet_withdraw_object &swwo) {
|
||||
swwo.processed = true;
|
||||
});
|
||||
}
|
||||
return op.son_wallet_withdraw_id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
} } // namespace graphene::chain
|
||||
|
|
@ -42,6 +42,9 @@ void_result vesting_balance_create_evaluator::do_evaluate( const vesting_balance
|
|||
FC_ASSERT( d.get_balance( creator_account.id, op.amount.asset_id ) >= op.amount );
|
||||
FC_ASSERT( !op.amount.asset_id(d).is_transfer_restricted() );
|
||||
|
||||
if(d.head_block_time() >= HARDFORK_SON_TIME && op.balance_type == vesting_balance_type::son) // Todo: hf check can be removed after pass
|
||||
FC_ASSERT( op.amount.amount >= d.get_global_properties().parameters.son_vesting_amount() );
|
||||
|
||||
if(d.head_block_time() < HARDFORK_GPOS_TIME) // Todo: can be removed after gpos hf time pass
|
||||
FC_ASSERT( op.balance_type == vesting_balance_type::normal);
|
||||
|
||||
|
|
@ -79,6 +82,11 @@ struct init_policy_visitor
|
|||
policy.coin_seconds_earned_last_update = now;
|
||||
p = policy;
|
||||
}
|
||||
void operator()( const dormant_vesting_policy_initializer& i )const
|
||||
{
|
||||
dormant_vesting_policy policy;
|
||||
p = policy;
|
||||
}
|
||||
};
|
||||
|
||||
object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance_create_operation& op )
|
||||
|
|
@ -110,8 +118,6 @@ object_id_type vesting_balance_create_evaluator::do_apply( const vesting_balance
|
|||
}
|
||||
obj.balance_type = op.balance_type;
|
||||
} );
|
||||
|
||||
|
||||
return vbo.id;
|
||||
} FC_CAPTURE_AND_RETHROW( (op) ) }
|
||||
|
||||
|
|
@ -133,7 +139,7 @@ void_result vesting_balance_withdraw_evaluator::do_evaluate( const vesting_balan
|
|||
const time_point_sec now = d.head_block_time();
|
||||
|
||||
const vesting_balance_object& vbo = op.vesting_balance( d );
|
||||
if(vbo.balance_type == vesting_balance_type::normal)
|
||||
if(vbo.balance_type == vesting_balance_type::normal || vbo.balance_type == vesting_balance_type::son)
|
||||
{
|
||||
FC_ASSERT( op.owner == vbo.owner, "", ("op.owner", op.owner)("vbo.owner", vbo.owner) );
|
||||
FC_ASSERT( vbo.is_withdraw_allowed( now, op.amount ), "Account has insufficient ${balance_type} Vested Balance to withdraw",
|
||||
|
|
@ -173,7 +179,7 @@ void_result vesting_balance_withdraw_evaluator::do_apply( const vesting_balance_
|
|||
//Handling all GPOS withdrawls separately from normal and SONs(future extension).
|
||||
// One request/transaction would be sufficient to withdraw from multiple vesting balance ids
|
||||
const vesting_balance_object& vbo = op.vesting_balance( d );
|
||||
if(vbo.balance_type == vesting_balance_type::normal)
|
||||
if(vbo.balance_type == vesting_balance_type::normal || vbo.balance_type == vesting_balance_type::son)
|
||||
{
|
||||
// Allow zero balance objects to stick around, (1) to comply
|
||||
// with the chain's "objects live forever" design principle, (2)
|
||||
|
|
|
|||
|
|
@ -169,6 +169,33 @@ bool cdd_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)c
|
|||
return (ctx.amount <= get_allowed_withdraw(ctx));
|
||||
}
|
||||
|
||||
asset dormant_vesting_policy::get_allowed_withdraw( const vesting_policy_context& ctx )const
|
||||
{
|
||||
share_type allowed_withdraw = 0;
|
||||
return asset( allowed_withdraw, ctx.balance.asset_id );
|
||||
}
|
||||
|
||||
void dormant_vesting_policy::on_deposit(const vesting_policy_context& ctx)
|
||||
{
|
||||
}
|
||||
|
||||
bool dormant_vesting_policy::is_deposit_allowed(const vesting_policy_context& ctx)const
|
||||
{
|
||||
return (ctx.amount.asset_id == ctx.balance.asset_id)
|
||||
&& sum_below_max_shares(ctx.amount, ctx.balance);
|
||||
}
|
||||
|
||||
void dormant_vesting_policy::on_withdraw(const vesting_policy_context& ctx)
|
||||
{
|
||||
}
|
||||
|
||||
bool dormant_vesting_policy::is_withdraw_allowed(const vesting_policy_context& ctx)const
|
||||
{
|
||||
return (ctx.amount.asset_id == ctx.balance.asset_id)
|
||||
&& (ctx.amount <= get_allowed_withdraw(ctx));
|
||||
}
|
||||
|
||||
|
||||
#define VESTING_VISITOR(NAME, MAYBE_CONST) \
|
||||
struct NAME ## _visitor \
|
||||
{ \
|
||||
|
|
|
|||
|
|
@ -10,4 +10,5 @@ add_subdirectory( generate_genesis )
|
|||
add_subdirectory( generate_uia_sharedrop_genesis )
|
||||
add_subdirectory( debug_witness )
|
||||
add_subdirectory( snapshot )
|
||||
add_subdirectory( peerplays_sidechain )
|
||||
add_subdirectory( es_objects )
|
||||
|
|
|
|||
51
libraries/plugins/peerplays_sidechain/CMakeLists.txt
Executable file
51
libraries/plugins/peerplays_sidechain/CMakeLists.txt
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
file(GLOB_RECURSE HEADERS "include/graphene/peerplays_sidechain/*.hpp")
|
||||
|
||||
add_library( peerplays_sidechain
|
||||
peerplays_sidechain_plugin.cpp
|
||||
sidechain_net_manager.cpp
|
||||
sidechain_net_handler.cpp
|
||||
sidechain_net_handler_bitcoin.cpp
|
||||
sidechain_net_handler_peerplays.cpp
|
||||
bitcoin/bech32.cpp
|
||||
bitcoin/bitcoin_address.cpp
|
||||
bitcoin/bitcoin_script.cpp
|
||||
bitcoin/bitcoin_transaction.cpp
|
||||
bitcoin/segwit_addr.cpp
|
||||
bitcoin/utils.cpp
|
||||
bitcoin/sign_bitcoin_transaction.cpp
|
||||
)
|
||||
|
||||
if (ENABLE_DEV_FEATURES)
|
||||
set(ENABLE_MULTIPLE_SONS 1)
|
||||
set(ENABLE_PEERPLAYS_ASSET_DEPOSITS 1)
|
||||
endif()
|
||||
unset(ENABLE_DEV_FEATURES)
|
||||
unset(ENABLE_DEV_FEATURES CACHE)
|
||||
|
||||
if (ENABLE_MULTIPLE_SONS)
|
||||
message ("Multiple SONs per software instance are supported")
|
||||
target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_MULTIPLE_SONS)
|
||||
endif()
|
||||
unset(ENABLE_MULTIPLE_SONS)
|
||||
unset(ENABLE_MULTIPLE_SONS CACHE)
|
||||
|
||||
if (ENABLE_PEERPLAYS_ASSET_DEPOSITS)
|
||||
message ("Depositing Peerplays assets enabled")
|
||||
target_compile_definitions(peerplays_sidechain PRIVATE ENABLE_PEERPLAYS_ASSET_DEPOSITS)
|
||||
endif()
|
||||
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS)
|
||||
unset(ENABLE_PEERPLAYS_ASSET_DEPOSITS CACHE)
|
||||
|
||||
target_link_libraries( peerplays_sidechain graphene_chain graphene_app fc zmq )
|
||||
target_include_directories( peerplays_sidechain
|
||||
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" )
|
||||
|
||||
install( TARGETS
|
||||
peerplays_sidechain
|
||||
|
||||
RUNTIME DESTINATION bin
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
INSTALL( FILES ${HEADERS} DESTINATION "include/graphene/peerplays_sidechain" )
|
||||
|
||||
193
libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp
Normal file
193
libraries/plugins/peerplays_sidechain/bitcoin/bech32.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bech32.hpp>
|
||||
|
||||
// #include <bech32.h>
|
||||
|
||||
namespace {
|
||||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** The Bech32 character set for encoding. */
|
||||
const char *CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
/** The Bech32 character set for decoding. */
|
||||
const int8_t CHARSET_REV[128] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
|
||||
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
|
||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1};
|
||||
|
||||
/** Concatenate two byte arrays. */
|
||||
data Cat(data x, const data &y) {
|
||||
x.insert(x.end(), y.begin(), y.end());
|
||||
return x;
|
||||
}
|
||||
|
||||
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
|
||||
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
|
||||
* bits correspond to earlier values. */
|
||||
uint32_t PolyMod(const data &v) {
|
||||
// The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an
|
||||
// implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) =
|
||||
// 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that
|
||||
// [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...].
|
||||
|
||||
// The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of
|
||||
// v(x) mod g(x), where g(x) is the Bech32 generator,
|
||||
// x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way
|
||||
// that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a
|
||||
// window of 1023 characters. Among the various possible BCH codes, one was selected to in
|
||||
// fact guarantee detection of up to 4 errors within a window of 89 characters.
|
||||
|
||||
// Note that the coefficients are elements of GF(32), here represented as decimal numbers
|
||||
// between {}. In this finite field, addition is just XOR of the corresponding numbers. For
|
||||
// example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires
|
||||
// treating the bits of values themselves as coefficients of a polynomial over a smaller field,
|
||||
// GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} =
|
||||
// (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a
|
||||
// = a^3 + 1 (mod a^5 + a^3 + 1) = {9}.
|
||||
|
||||
// During the course of the loop below, `c` contains the bitpacked coefficients of the
|
||||
// polynomial constructed from just the values of v that were processed so far, mod g(x). In
|
||||
// the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
|
||||
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
|
||||
// for `c`.
|
||||
uint32_t c = 1;
|
||||
for (auto v_i : v) {
|
||||
// We want to update `c` to correspond to a polynomial with one extra term. If the initial
|
||||
// value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to
|
||||
// correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to
|
||||
// process. Simplifying:
|
||||
// c'(x) = (f(x) * x + v_i) mod g(x)
|
||||
// ((f(x) mod g(x)) * x + v_i) mod g(x)
|
||||
// (c(x) * x + v_i) mod g(x)
|
||||
// If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute
|
||||
// c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x)
|
||||
// = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x)
|
||||
// = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i
|
||||
// If we call (x^6 mod g(x)) = k(x), this can be written as
|
||||
// c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x)
|
||||
|
||||
// First, determine the value of c0:
|
||||
uint8_t c0 = c >> 25;
|
||||
|
||||
// Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i:
|
||||
c = ((c & 0x1ffffff) << 5) ^ v_i;
|
||||
|
||||
// Finally, for each set bit n in c0, conditionally add {2^n}k(x):
|
||||
if (c0 & 1)
|
||||
c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}
|
||||
if (c0 & 2)
|
||||
c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13}
|
||||
if (c0 & 4)
|
||||
c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26}
|
||||
if (c0 & 8)
|
||||
c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29}
|
||||
if (c0 & 16)
|
||||
c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/** Convert to lower case. */
|
||||
inline unsigned char LowerCase(unsigned char c) {
|
||||
return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c;
|
||||
}
|
||||
|
||||
/** Expand a HRP for use in checksum computation. */
|
||||
data ExpandHRP(const std::string &hrp) {
|
||||
data ret;
|
||||
ret.reserve(hrp.size() + 90);
|
||||
ret.resize(hrp.size() * 2 + 1);
|
||||
for (size_t i = 0; i < hrp.size(); ++i) {
|
||||
unsigned char c = hrp[i];
|
||||
ret[i] = c >> 5;
|
||||
ret[i + hrp.size() + 1] = c & 0x1f;
|
||||
}
|
||||
ret[hrp.size()] = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Verify a checksum. */
|
||||
bool VerifyChecksum(const std::string &hrp, const data &values) {
|
||||
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
|
||||
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
|
||||
// list of values would result in a new valid list. For that reason, Bech32 requires the
|
||||
// resulting checksum to be 1 instead.
|
||||
return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
|
||||
}
|
||||
|
||||
/** Create a checksum. */
|
||||
data CreateChecksum(const std::string &hrp, const data &values) {
|
||||
data enc = Cat(ExpandHRP(hrp), values);
|
||||
enc.resize(enc.size() + 6); // Append 6 zeroes
|
||||
uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
|
||||
data ret(6);
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
// Convert the 5-bit groups in mod to checksum values.
|
||||
ret[i] = (mod >> (5 * (5 - i))) & 31;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace bech32 {
|
||||
|
||||
/** Encode a Bech32 string. */
|
||||
std::string Encode(const std::string &hrp, const data &values) {
|
||||
data checksum = CreateChecksum(hrp, values);
|
||||
data combined = Cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
for (auto c : combined) {
|
||||
ret += CHARSET[c];
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Decode a Bech32 string. */
|
||||
std::pair<std::string, data> Decode(const std::string &str) {
|
||||
bool lower = false, upper = false;
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
unsigned char c = str[i];
|
||||
if (c < 33 || c > 126)
|
||||
return {};
|
||||
if (c >= 'a' && c <= 'z')
|
||||
lower = true;
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
upper = true;
|
||||
}
|
||||
if (lower && upper)
|
||||
return {};
|
||||
size_t pos = str.rfind('1');
|
||||
if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) {
|
||||
return {};
|
||||
}
|
||||
data values(str.size() - 1 - pos);
|
||||
for (size_t i = 0; i < str.size() - 1 - pos; ++i) {
|
||||
unsigned char c = str[i + pos + 1];
|
||||
int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c];
|
||||
if (rev == -1) {
|
||||
return {};
|
||||
}
|
||||
values[i] = rev;
|
||||
}
|
||||
std::string hrp;
|
||||
for (size_t i = 0; i < pos; ++i) {
|
||||
hrp += LowerCase(str[i]);
|
||||
}
|
||||
if (!VerifyChecksum(hrp, values)) {
|
||||
return {};
|
||||
}
|
||||
return {hrp, data(values.begin(), values.end() - 6)};
|
||||
}
|
||||
|
||||
}}}} // namespace graphene::peerplays_sidechain::bitcoin::bech32
|
||||
|
|
@ -0,0 +1,455 @@
|
|||
#include <fc/crypto/base58.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/segwit_addr.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
bool bitcoin_address::operator==(const bitcoin_address &btc_addr) const {
|
||||
return (this->address == btc_addr.address) &&
|
||||
(this->type == btc_addr.type) &&
|
||||
(this->raw_address == btc_addr.raw_address) &&
|
||||
(this->network_type == btc_addr.network_type);
|
||||
}
|
||||
|
||||
bytes bitcoin_address::get_script() const {
|
||||
switch (type) {
|
||||
case payment_type::NULLDATA:
|
||||
return script_builder() << op::RETURN << raw_address;
|
||||
case payment_type::P2PK:
|
||||
return script_builder() << raw_address << op::CHECKSIG;
|
||||
case payment_type::P2PKH:
|
||||
return script_builder() << op::DUP << op::HASH160 << raw_address << op::EQUALVERIFY << op::CHECKSIG;
|
||||
case payment_type::P2SH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
return script_builder() << op::HASH160 << raw_address << op::EQUAL;
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH:
|
||||
return script_builder() << op::_0 << raw_address;
|
||||
default:
|
||||
return raw_address;
|
||||
}
|
||||
}
|
||||
|
||||
payment_type bitcoin_address::determine_type() {
|
||||
if (is_p2pk()) {
|
||||
return payment_type::P2PK;
|
||||
} else if (is_p2wpkh()) {
|
||||
return payment_type::P2WPKH;
|
||||
} else if (is_p2wsh()) {
|
||||
return payment_type::P2WSH;
|
||||
} else if (is_p2pkh()) {
|
||||
return payment_type::P2PKH;
|
||||
} else if (is_p2sh()) {
|
||||
return payment_type::P2SH;
|
||||
} else {
|
||||
return payment_type::NULLDATA;
|
||||
}
|
||||
}
|
||||
|
||||
bytes bitcoin_address::determine_raw_address() {
|
||||
bytes result;
|
||||
switch (type) {
|
||||
case payment_type::P2PK: {
|
||||
result = parse_hex(address);
|
||||
break;
|
||||
}
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH: {
|
||||
std::string prefix(address.compare(0, 4, "bcrt") == 0 ? std::string(address.begin(), address.begin() + 4) : std::string(address.begin(), address.begin() + 2));
|
||||
const auto &decode_bech32 = segwit_addr::decode(prefix, address);
|
||||
result = bytes(decode_bech32.second.begin(), decode_bech32.second.end());
|
||||
break;
|
||||
}
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
case payment_type::P2PKH:
|
||||
case payment_type::P2SH: {
|
||||
bytes hex_addr = fc::from_base58(address);
|
||||
result = bytes(hex_addr.begin() + 1, hex_addr.begin() + 21);
|
||||
break;
|
||||
}
|
||||
case payment_type::NULLDATA:
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool bitcoin_address::check_segwit_address(const size_segwit_address &size) const {
|
||||
if (!address.compare(0, 4, "bcrt") || !address.compare(0, 2, "bc") || !address.compare(0, 2, "tb")) {
|
||||
std::string prefix(!address.compare(0, 4, "bcrt") ? std::string(address.begin(), address.begin() + 4) : std::string(address.begin(), address.begin() + 2));
|
||||
|
||||
const auto &decode_bech32 = segwit_addr::decode(prefix, address);
|
||||
|
||||
if (decode_bech32.first == -1 || decode_bech32.second.size() != size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2pk() const {
|
||||
try {
|
||||
bool prefix = !address.compare(0, 2, "02") || !address.compare(0, 2, "03");
|
||||
if (address.size() == 66 && prefix) {
|
||||
parse_hex(address);
|
||||
return true;
|
||||
}
|
||||
} catch (fc::exception e) {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2wpkh() const {
|
||||
return check_segwit_address(size_segwit_address::P2WPKH);
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2wsh() const {
|
||||
return check_segwit_address(size_segwit_address::P2WSH);
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2pkh() const {
|
||||
try {
|
||||
bytes hex_addr = fc::from_base58(address);
|
||||
if (hex_addr.size() == 25 && (static_cast<unsigned char>(hex_addr[0]) == 0x00 ||
|
||||
static_cast<unsigned char>(hex_addr[0]) == 0x6f)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (fc::exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool bitcoin_address::is_p2sh() const {
|
||||
try {
|
||||
bytes hex_addr = fc::from_base58(address);
|
||||
if (hex_addr.size() == 25 && (static_cast<unsigned char>(hex_addr[0]) == 0x05 ||
|
||||
static_cast<unsigned char>(hex_addr[0]) == 0xc4)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (fc::exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
btc_multisig_address::btc_multisig_address(const size_t n_required, const accounts_keys &keys) :
|
||||
keys_required(n_required),
|
||||
witnesses_keys(keys) {
|
||||
create_redeem_script();
|
||||
create_address();
|
||||
type = payment_type::P2SH;
|
||||
}
|
||||
|
||||
size_t btc_multisig_address::count_intersection(const accounts_keys &keys) const {
|
||||
FC_ASSERT(keys.size() > 0);
|
||||
|
||||
int intersections_count = 0;
|
||||
for (auto &key : keys) {
|
||||
auto witness_key = witnesses_keys.find(key.first);
|
||||
if (witness_key == witnesses_keys.end())
|
||||
continue;
|
||||
if (key.second == witness_key->second)
|
||||
intersections_count++;
|
||||
}
|
||||
return intersections_count;
|
||||
}
|
||||
|
||||
void btc_multisig_address::create_redeem_script() {
|
||||
FC_ASSERT(keys_required > 0);
|
||||
FC_ASSERT(keys_required < witnesses_keys.size());
|
||||
redeem_script.clear();
|
||||
redeem_script.push_back(op_num[keys_required - 1]);
|
||||
for (const auto &key : witnesses_keys) {
|
||||
std::stringstream ss;
|
||||
ss << std::hex << key.second.key_data.size();
|
||||
auto key_size_hex = parse_hex(ss.str());
|
||||
redeem_script.insert(redeem_script.end(), key_size_hex.begin(), key_size_hex.end());
|
||||
redeem_script.insert(redeem_script.end(), key.second.key_data.begin(), key.second.key_data.end());
|
||||
}
|
||||
redeem_script.push_back(op_num[witnesses_keys.size() - 1]);
|
||||
redeem_script.push_back(OP_CHECKMULTISIG);
|
||||
}
|
||||
|
||||
void btc_multisig_address::create_address() {
|
||||
FC_ASSERT(redeem_script.size() > 0);
|
||||
raw_address.clear();
|
||||
fc::sha256 hash256 = fc::sha256::hash(redeem_script.data(), redeem_script.size());
|
||||
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
|
||||
bytes temp_addr_hash(parse_hex(hash160.str()));
|
||||
|
||||
raw_address.push_back(OP_HASH160);
|
||||
std::stringstream ss;
|
||||
ss << std::hex << temp_addr_hash.size();
|
||||
auto address_size_hex = parse_hex(ss.str());
|
||||
raw_address.insert(raw_address.end(), address_size_hex.begin(), address_size_hex.end());
|
||||
raw_address.insert(raw_address.end(), temp_addr_hash.begin(), temp_addr_hash.end());
|
||||
raw_address.push_back(OP_EQUAL);
|
||||
}
|
||||
|
||||
btc_multisig_segwit_address::btc_multisig_segwit_address(const size_t n_required, const accounts_keys &keys) :
|
||||
btc_multisig_address(n_required, keys) {
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
type = payment_type::P2SH;
|
||||
}
|
||||
|
||||
bool btc_multisig_segwit_address::operator==(const btc_multisig_segwit_address &addr) const {
|
||||
if (address != addr.address || redeem_script != addr.redeem_script ||
|
||||
witnesses_keys != addr.witnesses_keys || witness_script != addr.witness_script ||
|
||||
raw_address != addr.raw_address)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<public_key_type> btc_multisig_segwit_address::get_keys() {
|
||||
std::vector<public_key_type> keys;
|
||||
for (const auto &k : witnesses_keys) {
|
||||
keys.push_back(k.second);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
void btc_multisig_segwit_address::create_witness_script() {
|
||||
const auto redeem_sha256 = fc::sha256::hash(redeem_script.data(), redeem_script.size());
|
||||
witness_script.push_back(OP_0);
|
||||
witness_script.push_back(0x20); // PUSH_32
|
||||
witness_script.insert(witness_script.end(), redeem_sha256.data(), redeem_sha256.data() + redeem_sha256.data_size());
|
||||
}
|
||||
|
||||
void btc_multisig_segwit_address::create_segwit_address() {
|
||||
fc::sha256 hash256 = fc::sha256::hash(witness_script.data(), witness_script.size());
|
||||
fc::ripemd160 hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
|
||||
|
||||
raw_address = bytes(hash160.data(), hash160.data() + hash160.data_size());
|
||||
address = fc::to_base58(get_address_bytes(raw_address));
|
||||
}
|
||||
|
||||
bytes btc_multisig_segwit_address::get_address_bytes(const bytes &script_hash) {
|
||||
bytes address_bytes(1, TESTNET_SCRIPT); // 1 byte version
|
||||
address_bytes.insert(address_bytes.end(), script_hash.begin(), script_hash.end());
|
||||
fc::sha256 hash256 = fc::sha256::hash(fc::sha256::hash(address_bytes.data(), address_bytes.size()));
|
||||
address_bytes.insert(address_bytes.end(), hash256.data(), hash256.data() + 4); // 4 byte checksum
|
||||
|
||||
return address_bytes;
|
||||
}
|
||||
|
||||
btc_weighted_multisig_address::btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
|
||||
network ntype) {
|
||||
network_type = ntype;
|
||||
create_redeem_script(keys_data);
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
type = payment_type::P2WSH;
|
||||
}
|
||||
|
||||
void btc_weighted_multisig_address::create_redeem_script(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
|
||||
script_builder builder;
|
||||
uint32_t total_weight = 0;
|
||||
builder << uint32_t(0);
|
||||
for (auto &p : keys_data) {
|
||||
total_weight += p.second;
|
||||
builder << op::SWAP;
|
||||
builder << p.first.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::IF;
|
||||
builder << uint32_t(p.second);
|
||||
builder << op::ADD;
|
||||
builder << op::ENDIF;
|
||||
}
|
||||
uint32_t threshold_weight = total_weight * 2 / 3 + 1;
|
||||
builder << threshold_weight;
|
||||
builder << op::GREATERTHANOREQUAL;
|
||||
|
||||
redeem_script_ = builder;
|
||||
|
||||
fc::sha256 sh = fc::sha256::hash(redeem_script_);
|
||||
raw_address = bytes(sh.data(), sh.data() + sh.data_size());
|
||||
}
|
||||
|
||||
void btc_weighted_multisig_address::create_witness_script() {
|
||||
script_builder builder;
|
||||
builder << op::_0;
|
||||
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
|
||||
|
||||
witness_script_ = builder;
|
||||
}
|
||||
|
||||
void btc_weighted_multisig_address::create_segwit_address() {
|
||||
std::string hrp;
|
||||
switch (network_type) {
|
||||
case (network::mainnet):
|
||||
hrp = "bc";
|
||||
break;
|
||||
case (network::testnet):
|
||||
hrp = "tb";
|
||||
break;
|
||||
case (network::regtest):
|
||||
hrp = "bcrt";
|
||||
break;
|
||||
}
|
||||
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
|
||||
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
|
||||
address = segwit_addr::encode(hrp, 0, hash_data);
|
||||
}
|
||||
|
||||
btc_one_or_m_of_n_multisig_address::btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data,
|
||||
const uint8_t nrequired, const std::vector<fc::ecc::public_key> &keys_data,
|
||||
network ntype) {
|
||||
network_type = ntype;
|
||||
create_redeem_script(user_key_data, nrequired, keys_data);
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
type = payment_type::P2WSH;
|
||||
}
|
||||
void btc_one_or_m_of_n_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data,
|
||||
const uint8_t nrequired, const std::vector<fc::ecc::public_key> &keys_data) {
|
||||
script_builder builder;
|
||||
builder << op::IF;
|
||||
builder << user_key_data.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::ELSE;
|
||||
builder << static_cast<uint32_t>(nrequired);
|
||||
for (auto &key : keys_data) {
|
||||
builder << key.serialize();
|
||||
}
|
||||
builder << static_cast<uint32_t>(keys_data.size());
|
||||
builder << op::CHECKMULTISIG;
|
||||
builder << op::ENDIF;
|
||||
redeem_script_ = builder;
|
||||
fc::sha256 sh = fc::sha256::hash(redeem_script_);
|
||||
raw_address = bytes(sh.data(), sh.data() + sh.data_size());
|
||||
}
|
||||
void btc_one_or_m_of_n_multisig_address::create_witness_script() {
|
||||
script_builder builder;
|
||||
builder << op::_0;
|
||||
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
|
||||
witness_script_ = builder;
|
||||
}
|
||||
void btc_one_or_m_of_n_multisig_address::create_segwit_address() {
|
||||
std::string hrp;
|
||||
switch (network_type) {
|
||||
case (network::mainnet):
|
||||
hrp = "bc";
|
||||
break;
|
||||
case (network::testnet):
|
||||
hrp = "tb";
|
||||
break;
|
||||
case (network::regtest):
|
||||
hrp = "bcrt";
|
||||
break;
|
||||
}
|
||||
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
|
||||
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
|
||||
address = segwit_addr::encode(hrp, 0, hash_data);
|
||||
}
|
||||
|
||||
btc_one_or_weighted_multisig_address::btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data,
|
||||
const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
|
||||
bitcoin_address::network ntype) {
|
||||
network_type = ntype;
|
||||
create_redeem_script(user_key_data, keys_data);
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
type = payment_type::P2WSH;
|
||||
}
|
||||
|
||||
void btc_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
|
||||
script_builder builder;
|
||||
builder << user_key_data.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::IF;
|
||||
builder << op::_1;
|
||||
builder << op::ELSE;
|
||||
uint32_t total_weight = 0;
|
||||
builder << uint32_t(0);
|
||||
for (auto &p : keys_data) {
|
||||
total_weight += p.second;
|
||||
builder << op::SWAP;
|
||||
builder << p.first.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::IF;
|
||||
builder << uint32_t(p.second);
|
||||
builder << op::ADD;
|
||||
builder << op::ENDIF;
|
||||
}
|
||||
uint32_t threshold_weight = total_weight * 2 / 3 + 1;
|
||||
builder << threshold_weight;
|
||||
builder << op::GREATERTHANOREQUAL;
|
||||
builder << op::ENDIF;
|
||||
redeem_script_ = builder;
|
||||
fc::sha256 sh = fc::sha256::hash(redeem_script_);
|
||||
raw_address = bytes(sh.data(), sh.data() + sh.data_size());
|
||||
}
|
||||
|
||||
void btc_one_or_weighted_multisig_address::create_witness_script() {
|
||||
script_builder builder;
|
||||
builder << op::_0;
|
||||
builder << fc::sha256::hash(redeem_script_.data(), redeem_script_.size());
|
||||
witness_script_ = builder;
|
||||
}
|
||||
|
||||
void btc_one_or_weighted_multisig_address::create_segwit_address() {
|
||||
std::string hrp;
|
||||
switch (network_type) {
|
||||
case (network::mainnet):
|
||||
hrp = "bc";
|
||||
break;
|
||||
case (network::testnet):
|
||||
hrp = "tb";
|
||||
break;
|
||||
case (network::regtest):
|
||||
hrp = "bcrt";
|
||||
break;
|
||||
}
|
||||
fc::sha256 sh = fc::sha256::hash(&redeem_script_[0], redeem_script_.size());
|
||||
std::vector<uint8_t> hash_data(sh.data(), sh.data() + sh.data_size());
|
||||
address = segwit_addr::encode(hrp, 0, hash_data);
|
||||
}
|
||||
|
||||
btc_timelocked_one_or_weighted_multisig_address::btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data, bitcoin_address::network ntype) :
|
||||
btc_one_or_weighted_multisig_address(user_key_data, keys_data, ntype),
|
||||
latency_(latency) {
|
||||
create_redeem_script(user_key_data, keys_data);
|
||||
create_witness_script();
|
||||
create_segwit_address();
|
||||
}
|
||||
|
||||
void btc_timelocked_one_or_weighted_multisig_address::create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data) {
|
||||
script_builder builder;
|
||||
builder << user_key_data.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::IF;
|
||||
builder << uint32_t(latency_);
|
||||
builder << op::CHECKSEQUENCEVERIFY;
|
||||
builder << op::DROP;
|
||||
builder << op::_1;
|
||||
builder << op::ELSE;
|
||||
uint32_t total_weight = 0;
|
||||
builder << uint32_t(0);
|
||||
for (auto &p : keys_data) {
|
||||
total_weight += p.second;
|
||||
builder << op::SWAP;
|
||||
builder << p.first.serialize();
|
||||
builder << op::CHECKSIG;
|
||||
builder << op::IF;
|
||||
builder << uint32_t(p.second);
|
||||
builder << op::ADD;
|
||||
builder << op::ENDIF;
|
||||
}
|
||||
uint32_t threshold_weight = total_weight * 2 / 3 + 1;
|
||||
builder << threshold_weight;
|
||||
builder << op::GREATERTHANOREQUAL;
|
||||
builder << op::ENDIF;
|
||||
redeem_script_ = builder;
|
||||
fc::sha256 sh = fc::sha256::hash(redeem_script_);
|
||||
raw_address = bytes(sh.data(), sh.data() + sh.data_size());
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
script_builder &script_builder::operator<<(op opcode) {
|
||||
const auto op_byte = static_cast<uint8_t>(opcode);
|
||||
if (op_byte < 0 || op_byte > 0xff)
|
||||
throw std::runtime_error("script_builder::operator<<(OP): invalid opcode");
|
||||
script.push_back(op_byte);
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(uint32_t number) {
|
||||
if (number == 0) {
|
||||
script.push_back(static_cast<uint8_t>(op::_0));
|
||||
} else if (number <= 16) {
|
||||
script.push_back(static_cast<uint8_t>(op::_1) + number - 1);
|
||||
} else {
|
||||
bytes pack_buf;
|
||||
while (number) {
|
||||
pack_buf.push_back(number & 0xff);
|
||||
number >>= 8;
|
||||
}
|
||||
// - If the most significant byte is >= 0x80 and the value is positive, push a
|
||||
// new zero-byte to make the significant byte < 0x80 again. So, the result can
|
||||
// be 5 bytes max.
|
||||
if (pack_buf.back() & 0x80)
|
||||
pack_buf.push_back(0);
|
||||
*this << pack_buf;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(size_t size) {
|
||||
write_compact_size(script, size);
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(const bytes &sc) {
|
||||
write_compact_size(script, sc.size());
|
||||
script.insert(script.end(), sc.begin(), sc.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(const fc::sha256 &hash) {
|
||||
write_compact_size(script, hash.data_size());
|
||||
script.insert(script.end(), hash.data(), hash.data() + hash.data_size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(const fc::ripemd160 &hash) {
|
||||
write_compact_size(script, hash.data_size());
|
||||
script.insert(script.end(), hash.data(), hash.data() + hash.data_size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
script_builder &script_builder::operator<<(const fc::ecc::public_key_data &pubkey_data) {
|
||||
write_compact_size(script, pubkey_data.size());
|
||||
script.insert(script.end(), pubkey_data.begin(), pubkey_data.begin() + pubkey_data.size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
#include <fc/crypto/base58.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_script.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
bool out_point::operator==(const out_point &op) const {
|
||||
if (this->hash == op.hash &&
|
||||
this->n == op.n) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_in::operator==(const tx_in &ti) const {
|
||||
if (this->prevout == ti.prevout &&
|
||||
this->scriptSig == ti.scriptSig &&
|
||||
this->nSequence == ti.nSequence) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::operator==(const tx_out &to) const {
|
||||
if (this->value == to.value &&
|
||||
this->scriptPubKey == to.scriptPubKey) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2wsh() const {
|
||||
if (scriptPubKey.size() == 34 && scriptPubKey[0] == static_cast<char>(0x00) && scriptPubKey[1] == static_cast<char>(0x20)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2wpkh() const {
|
||||
if (scriptPubKey.size() == 22 && scriptPubKey[0] == static_cast<char>(0x00) && scriptPubKey[1] == static_cast<char>(0x14)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2pkh() const {
|
||||
if (scriptPubKey.size() == 25 && scriptPubKey[0] == static_cast<char>(0x76) && scriptPubKey[1] == static_cast<char>(0xa9) &&
|
||||
scriptPubKey[2] == static_cast<char>(0x14) && scriptPubKey[23] == static_cast<char>(0x88) && scriptPubKey[24] == static_cast<char>(0xac)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2sh() const {
|
||||
if (scriptPubKey.size() == 23 && scriptPubKey[0] == static_cast<char>(0xa9) &&
|
||||
scriptPubKey[1] == static_cast<char>(0x14) && scriptPubKey[22] == static_cast<char>(0x87)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool tx_out::is_p2pk() const {
|
||||
if (scriptPubKey.size() == 35 && scriptPubKey[0] == static_cast<char>(0x21) && scriptPubKey[34] == static_cast<char>(0xac)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes tx_out::get_data_or_script() const {
|
||||
if (is_p2pkh()) {
|
||||
return bytes(scriptPubKey.begin() + 3, scriptPubKey.begin() + 23);
|
||||
} else if (is_p2sh() || is_p2wpkh()) {
|
||||
return bytes(scriptPubKey.begin() + 2, scriptPubKey.begin() + 22);
|
||||
} else if (is_p2wsh()) {
|
||||
return bytes(scriptPubKey.begin() + 2, scriptPubKey.begin() + 34);
|
||||
} else if (is_p2pk()) {
|
||||
return bytes(scriptPubKey.begin() + 1, scriptPubKey.begin() + 34);
|
||||
}
|
||||
return scriptPubKey;
|
||||
}
|
||||
|
||||
bool bitcoin_transaction::operator!=(const bitcoin_transaction &bt) const {
|
||||
if (this->nVersion != bt.nVersion ||
|
||||
this->vin != bt.vin ||
|
||||
this->vout != bt.vout ||
|
||||
this->nLockTime != bt.nLockTime) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fc::sha256 bitcoin_transaction::get_hash() const {
|
||||
const auto bytes = pack(*this, true);
|
||||
const auto hash = fc::sha256::hash(fc::sha256::hash(bytes.data(), bytes.size()));
|
||||
std::reverse(hash.data(), hash.data() + hash.data_size());
|
||||
return hash;
|
||||
}
|
||||
|
||||
fc::sha256 bitcoin_transaction::get_txid() const {
|
||||
const auto bytes = pack(*this, false);
|
||||
const auto hash = fc::sha256::hash(fc::sha256::hash(bytes.data(), bytes.size()));
|
||||
std::reverse(hash.data(), hash.data() + hash.data_size());
|
||||
return hash;
|
||||
}
|
||||
|
||||
size_t bitcoin_transaction::get_vsize() const {
|
||||
static const auto witness_scale_factor = 4;
|
||||
|
||||
fc::datastream<size_t> no_wit_ds;
|
||||
pack(no_wit_ds, *this, false);
|
||||
|
||||
fc::datastream<size_t> wit_ds;
|
||||
pack(wit_ds, *this, true);
|
||||
|
||||
const size_t weight = no_wit_ds.tellp() * (witness_scale_factor - 1) + wit_ds.tellp();
|
||||
const size_t vsize = (weight + witness_scale_factor - 1) / witness_scale_factor;
|
||||
|
||||
return vsize;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::set_version(int32_t version) {
|
||||
tx.nVersion = version;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::set_locktime(uint32_t lock_time) {
|
||||
tx.nLockTime = lock_time;
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_in(const fc::sha256 &txid, uint32_t n_out, const bytes &script_code, bool front, uint32_t sequence) {
|
||||
add_in(payment_type::P2WSH, txid, n_out, script_code, front, sequence);
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_in(payment_type type, const fc::sha256 &txid, uint32_t n_out, const bytes &script_code, bool front, uint32_t sequence) {
|
||||
out_point prevout;
|
||||
prevout.hash = txid;
|
||||
prevout.n = n_out;
|
||||
|
||||
tx_in txin;
|
||||
txin.prevout = prevout;
|
||||
txin.nSequence = sequence;
|
||||
|
||||
add_in(type, txin, script_code, front);
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_in(payment_type type, tx_in txin, const bytes &script_code, bool front) {
|
||||
switch (type) {
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
FC_ASSERT(script_code != bytes());
|
||||
txin.scriptSig = script_code;
|
||||
break;
|
||||
default: {
|
||||
if (txin.prevout.hash == fc::sha256("0000000000000000000000000000000000000000000000000000000000000000")) { //coinbase
|
||||
FC_ASSERT(script_code != bytes());
|
||||
txin.scriptSig = script_code;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (front) {
|
||||
tx.vin.insert(tx.vin.begin(), txin);
|
||||
} else {
|
||||
tx.vin.push_back(txin);
|
||||
}
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out(payment_type type, int64_t amount, const std::string &base58_address, bool front) {
|
||||
// TODO: add checks
|
||||
const auto address_bytes = fc::from_base58(base58_address);
|
||||
add_out(type, amount, bytes(address_bytes.begin() + 1, address_bytes.begin() + 1 + 20), front);
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out(payment_type type, int64_t amount, const fc::ecc::public_key_data &pubkey, bool front) {
|
||||
FC_ASSERT(is_payment_to_pubkey(type));
|
||||
|
||||
if (type == payment_type::P2PK) {
|
||||
const auto pubkey_bytes = bytes(pubkey.begin(), pubkey.begin() + pubkey.size());
|
||||
add_out(type, amount, pubkey_bytes, front);
|
||||
} else {
|
||||
const auto hash256 = fc::sha256::hash(pubkey.begin(), pubkey.size());
|
||||
const auto hash160 = fc::ripemd160::hash(hash256.data(), hash256.data_size());
|
||||
add_out(type, amount, bytes(hash160.data(), hash160.data() + hash160.data_size()), front);
|
||||
}
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out(payment_type type, int64_t amount, const bytes &script_code, bool front) {
|
||||
tx_out out;
|
||||
out.value = amount;
|
||||
out.scriptPubKey = get_script_pubkey(type, script_code);
|
||||
|
||||
if (front) {
|
||||
tx.vout.insert(tx.vout.begin(), out);
|
||||
} else {
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
}
|
||||
|
||||
void bitcoin_transaction_builder::add_out_all_type(const uint64_t &amount, const bitcoin_address &address, bool front) {
|
||||
switch (address.get_type()) {
|
||||
case payment_type::P2PK: {
|
||||
bytes raw_address(address.get_raw_address());
|
||||
fc::ecc::public_key_data public_key;
|
||||
std::copy(raw_address.begin(), raw_address.end(), public_key.begin());
|
||||
add_out(address.get_type(), amount, public_key, front);
|
||||
break;
|
||||
}
|
||||
case payment_type::P2PKH:
|
||||
case payment_type::P2SH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH: {
|
||||
add_out(address.get_type(), amount, address.get_address(), front);
|
||||
break;
|
||||
}
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH: {
|
||||
add_out(address.get_type(), amount, address.get_raw_address(), front);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool bitcoin_transaction_builder::is_payment_to_pubkey(payment_type type) {
|
||||
switch (type) {
|
||||
case payment_type::P2PK:
|
||||
case payment_type::P2PKH:
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bytes bitcoin_transaction_builder::get_script_pubkey(payment_type type, const bytes &script_code) {
|
||||
switch (type) {
|
||||
case payment_type::NULLDATA:
|
||||
return script_builder() << op::RETURN << script_code;
|
||||
case payment_type::P2PK:
|
||||
return script_builder() << script_code << op::CHECKSIG;
|
||||
case payment_type::P2PKH:
|
||||
return script_builder() << op::DUP << op::HASH160 << script_code << op::EQUALVERIFY << op::CHECKSIG;
|
||||
case payment_type::P2SH:
|
||||
case payment_type::P2SH_WPKH:
|
||||
case payment_type::P2SH_WSH:
|
||||
return script_builder() << op::HASH160 << script_code << op::EQUAL;
|
||||
case payment_type::P2WPKH:
|
||||
case payment_type::P2WSH:
|
||||
return script_builder() << op::_0 << script_code;
|
||||
default:
|
||||
return script_code;
|
||||
}
|
||||
}
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
/* Copyright (c) 2017 Pieter Wuille
|
||||
*
|
||||
* 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 <graphene/peerplays_sidechain/bitcoin/bech32.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/segwit_addr.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** Convert from one power-of-2 number base to another. */
|
||||
template <int frombits, int tobits, bool pad>
|
||||
bool convertbits(data &out, const data &in) {
|
||||
int acc = 0;
|
||||
int bits = 0;
|
||||
const int maxv = (1 << tobits) - 1;
|
||||
const int max_acc = (1 << (frombits + tobits - 1)) - 1;
|
||||
for (size_t i = 0; i < in.size(); ++i) {
|
||||
int value = in[i];
|
||||
acc = ((acc << frombits) | value) & max_acc;
|
||||
bits += frombits;
|
||||
while (bits >= tobits) {
|
||||
bits -= tobits;
|
||||
out.push_back((acc >> bits) & maxv);
|
||||
}
|
||||
}
|
||||
if (pad) {
|
||||
if (bits)
|
||||
out.push_back((acc << (tobits - bits)) & maxv);
|
||||
} else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace segwit_addr {
|
||||
|
||||
/** Decode a SegWit address. */
|
||||
std::pair<int, data> decode(const std::string &hrp, const std::string &addr) {
|
||||
std::pair<std::string, data> dec = bech32::Decode(addr);
|
||||
if (dec.first != hrp || dec.second.size() < 1)
|
||||
return std::make_pair(-1, data());
|
||||
data conv;
|
||||
if (!convertbits<5, 8, false>(conv, data(dec.second.begin() + 1, dec.second.end())) ||
|
||||
conv.size() < 2 || conv.size() > 40 || dec.second[0] > 16 || (dec.second[0] == 0 && conv.size() != 20 && conv.size() != 32)) {
|
||||
return std::make_pair(-1, data());
|
||||
}
|
||||
return std::make_pair(dec.second[0], conv);
|
||||
}
|
||||
|
||||
/** Encode a SegWit address. */
|
||||
std::string encode(const std::string &hrp, int witver, const data &witprog) {
|
||||
data enc;
|
||||
enc.push_back(witver);
|
||||
convertbits<8, 5, true>(enc, witprog);
|
||||
std::string ret = bech32::Encode(hrp, enc);
|
||||
if (decode(hrp, ret).first == -1)
|
||||
return "";
|
||||
return ret;
|
||||
}
|
||||
|
||||
}}}} // namespace graphene::peerplays_sidechain::bitcoin::segwit_addr
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
#include <graphene/peerplays_sidechain/bitcoin/serialize.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/sign_bitcoin_transaction.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
const secp256k1_context_t *btc_context() {
|
||||
static secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &scriptCode, int64_t amount,
|
||||
size_t in_index, int hash_type, bool is_witness) {
|
||||
fc::datastream<size_t> ps;
|
||||
if (is_witness)
|
||||
pack_tx_witness_signature(ps, scriptCode, tx, in_index, amount, hash_type);
|
||||
else
|
||||
pack_tx_signature(ps, scriptCode, tx, in_index, hash_type);
|
||||
|
||||
std::vector<char> vec(ps.tellp());
|
||||
if (!vec.empty()) {
|
||||
fc::datastream<char *> ds(vec.data(), vec.size());
|
||||
if (is_witness)
|
||||
pack_tx_witness_signature(ds, scriptCode, tx, in_index, amount, hash_type);
|
||||
else
|
||||
pack_tx_signature(ds, scriptCode, tx, in_index, hash_type);
|
||||
}
|
||||
|
||||
return fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size()));
|
||||
}
|
||||
|
||||
std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign) {
|
||||
bytes sig;
|
||||
sig.resize(72);
|
||||
int sig_len = sig.size();
|
||||
|
||||
FC_ASSERT(secp256k1_ecdsa_sign(
|
||||
context_sign,
|
||||
reinterpret_cast<unsigned char *>(hash.data()),
|
||||
reinterpret_cast<unsigned char *>(sig.data()),
|
||||
&sig_len,
|
||||
reinterpret_cast<const unsigned char *>(privkey.data()),
|
||||
secp256k1_nonce_function_rfc6979,
|
||||
nullptr)); // TODO: replace assert with exception
|
||||
|
||||
sig.resize(sig_len);
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
std::vector<bytes> sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const bytes &privkey,
|
||||
const secp256k1_context_t *context_sign, int hash_type) {
|
||||
FC_ASSERT(tx.vin.size() == redeem_scripts.size() && tx.vin.size() == amounts.size());
|
||||
FC_ASSERT(!privkey.empty());
|
||||
|
||||
std::vector<bytes> signatures;
|
||||
for (size_t i = 0; i < tx.vin.size(); i++) {
|
||||
const auto sighash = get_signature_hash(tx, redeem_scripts[i], static_cast<int64_t>(amounts[i]), i, hash_type, true);
|
||||
auto sig = privkey_sign(privkey, sighash, context_sign);
|
||||
sig.push_back(static_cast<uint8_t>(hash_type));
|
||||
|
||||
signatures.push_back(sig);
|
||||
}
|
||||
return signatures;
|
||||
}
|
||||
|
||||
void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts, bool use_mulisig_workaround) {
|
||||
FC_ASSERT(tx.vin.size() == redeem_scripts.size());
|
||||
|
||||
for (size_t i = 0; i < tx.vin.size(); i++) {
|
||||
if (use_mulisig_workaround)
|
||||
tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), bytes()); // Bitcoin workaround CHECKMULTISIG bug
|
||||
tx.vin[i].scriptWitness.push_back(redeem_scripts[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context) {
|
||||
std::vector<unsigned char> sig_temp(sig.begin(), sig.end());
|
||||
std::vector<unsigned char> pubkey_temp(pubkey.begin(), pubkey.end());
|
||||
std::vector<unsigned char> msg_temp(msg.begin(), msg.end());
|
||||
|
||||
int result = secp256k1_ecdsa_verify(context, msg_temp.data(), sig_temp.data(), sig_temp.size(), pubkey_temp.data(), pubkey_temp.size());
|
||||
return result == 1;
|
||||
}
|
||||
|
||||
std::vector<std::vector<bytes>> sort_sigs(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context_t *context) {
|
||||
FC_ASSERT(redeem_scripts.size() == amounts.size());
|
||||
|
||||
using data = std::pair<size_t, bytes>;
|
||||
struct comp {
|
||||
bool operator()(const data &lhs, const data &rhs) const {
|
||||
return lhs.first < rhs.first;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::vector<bytes>> new_stacks;
|
||||
|
||||
for (size_t i = 0; i < redeem_scripts.size(); i++) {
|
||||
const std::vector<bytes> &keys = get_pubkey_from_redeemScript(redeem_scripts[i]);
|
||||
const auto &sighash = get_signature_hash(tx, redeem_scripts[i], static_cast<int64_t>(amounts[i]), i, 1, true).str();
|
||||
bytes sighash_temp(parse_hex(sighash));
|
||||
|
||||
std::vector<bytes> stack(tx.vin[i].scriptWitness);
|
||||
std::vector<bool> marker(tx.vin[i].scriptWitness.size(), false);
|
||||
std::set<data, comp> sigs;
|
||||
|
||||
for (size_t j = 0; j < keys.size(); j++) {
|
||||
for (size_t l = 0; l < stack.size(); l++) {
|
||||
if (!verify_sig(stack[l], keys[j], sighash_temp, context) || marker[l])
|
||||
continue;
|
||||
sigs.insert(std::make_pair(j, stack[l]));
|
||||
marker[l] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<bytes> temp_sig;
|
||||
for (auto s : sigs) {
|
||||
temp_sig.push_back(s.second);
|
||||
}
|
||||
new_stacks.push_back(temp_sig);
|
||||
}
|
||||
return new_stacks;
|
||||
}
|
||||
|
||||
void add_signatures_to_transaction_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set) {
|
||||
for (unsigned int i = 0; i < signature_set.size(); i++) {
|
||||
std::vector<bytes> signatures = signature_set[i];
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.push_back(signatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void add_signatures_to_transaction_weighted_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set) {
|
||||
for (unsigned int i = 0; i < signature_set.size(); i++) {
|
||||
std::vector<bytes> signatures = signature_set[i];
|
||||
FC_ASSERT(signatures.size() == tx.vin.size(), "Invalid signatures number");
|
||||
// push signatures in reverse order because script starts to check the top signature on the stack first
|
||||
for (unsigned int i = 0; i < tx.vin.size(); i++)
|
||||
tx.vin[i].scriptWitness.insert(tx.vin[i].scriptWitness.begin(), signatures[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void add_signatures_to_transaction_user_weighted_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set) {
|
||||
add_signatures_to_transaction_weighted_multisig(tx, signature_set);
|
||||
for (size_t itr = 0; itr < tx.vin.size(); itr++) {
|
||||
tx.vin[0].scriptWitness.push_back(bytes());
|
||||
}
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
99
libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp
Normal file
99
libraries/plugins/peerplays_sidechain/bitcoin/utils.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <fc/crypto/base58.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/utils.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
fc::ecc::public_key_data create_public_key_data(const std::vector<char> &public_key) {
|
||||
FC_ASSERT(public_key.size() == 33);
|
||||
fc::ecc::public_key_data key;
|
||||
for (size_t i = 0; i < 33; i++) {
|
||||
key.at(i) = public_key[i];
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
bytes get_privkey_bytes(const std::string &privkey_base58) {
|
||||
const auto privkey = fc::from_base58(privkey_base58);
|
||||
// Remove version and hash
|
||||
return bytes(privkey.cbegin() + 1, privkey.cbegin() + 1 + 32);
|
||||
}
|
||||
|
||||
bytes parse_hex(const std::string &str) {
|
||||
bytes vec(str.size() / 2);
|
||||
fc::from_hex(str, vec.data(), vec.size());
|
||||
return vec;
|
||||
}
|
||||
|
||||
std::vector<bytes> get_pubkey_from_redeemScript(bytes script) {
|
||||
FC_ASSERT(script.size() >= 37);
|
||||
|
||||
script.erase(script.begin());
|
||||
script.erase(script.end() - 2, script.end());
|
||||
|
||||
std::vector<bytes> result;
|
||||
uint64_t count = script.size() / 34;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
result.push_back(bytes(script.begin() + (34 * i) + 1, script.begin() + (34 * (i + 1))));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bytes public_key_data_to_bytes(const fc::ecc::public_key_data &key) {
|
||||
bytes result;
|
||||
result.resize(key.size());
|
||||
std::copy(key.begin(), key.end(), result.begin());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<bytes> read_byte_arrays_from_string(const std::string &string_buf) {
|
||||
std::stringstream ss(string_buf);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json(ss, json);
|
||||
|
||||
std::vector<bytes> data;
|
||||
for (auto &v : json) {
|
||||
std::string hex = v.second.data();
|
||||
bytes item;
|
||||
item.resize(hex.size() / 2);
|
||||
fc::from_hex(hex, (char *)&item[0], item.size());
|
||||
data.push_back(item);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string write_transaction_signatures(const std::vector<bytes> &data) {
|
||||
std::string res = "[";
|
||||
for (unsigned int idx = 0; idx < data.size(); ++idx) {
|
||||
res += "\"" + fc::to_hex((char *)&data[idx][0], data[idx].size()) + "\"";
|
||||
if (idx != data.size() - 1)
|
||||
res += ",";
|
||||
}
|
||||
res += "]";
|
||||
return res;
|
||||
}
|
||||
|
||||
void read_transaction_data(const std::string &string_buf, std::string &tx_hex, std::vector<uint64_t> &in_amounts, std::string &redeem_script) {
|
||||
std::stringstream ss(string_buf);
|
||||
boost::property_tree::ptree json;
|
||||
boost::property_tree::read_json(ss, json);
|
||||
tx_hex = json.get<std::string>("tx_hex");
|
||||
in_amounts.clear();
|
||||
for (auto &v : json.get_child("in_amounts"))
|
||||
in_amounts.push_back(fc::to_uint64(v.second.data()));
|
||||
redeem_script = json.get<std::string>("redeem_script");
|
||||
}
|
||||
|
||||
std::string write_transaction_data(const std::string &tx, const std::vector<uint64_t> &in_amounts, const std::string &redeem_script) {
|
||||
std::string res = "{\"tx_hex\":\"" + tx + "\",\"in_amounts\":[";
|
||||
for (unsigned int idx = 0; idx < in_amounts.size(); ++idx) {
|
||||
res += fc::to_string(in_amounts[idx]);
|
||||
if (idx != in_amounts.size() - 1)
|
||||
res += ",";
|
||||
}
|
||||
res += "],\"redeem_script\":\"" + redeem_script + "\"}";
|
||||
return res;
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Bech32 is a string encoding format used in newer address types.
|
||||
// The output consists of a human-readable part (alphanumeric), a
|
||||
// separator character (1), and a base32 data section, the last
|
||||
// 6 characters of which are a checksum.
|
||||
//
|
||||
// For more information, see BIP 173.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace bech32 {
|
||||
|
||||
/** Encode a Bech32 string. Returns the empty string in case of failure. */
|
||||
std::string Encode(const std::string &hrp, const std::vector<uint8_t> &values);
|
||||
|
||||
/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
|
||||
std::pair<std::string, std::vector<uint8_t>> Decode(const std::string &str);
|
||||
|
||||
}}}} // namespace graphene::peerplays_sidechain::bitcoin::bech32
|
||||
|
|
@ -0,0 +1,249 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/utils.hpp>
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
const bytes op_num = {0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f}; // OP_1 - OP_15
|
||||
|
||||
class bitcoin_address {
|
||||
|
||||
public:
|
||||
enum network {
|
||||
mainnet,
|
||||
testnet,
|
||||
regtest
|
||||
};
|
||||
|
||||
bitcoin_address() = default;
|
||||
|
||||
bitcoin_address(const std::string &addr, network ntype = network::regtest) :
|
||||
address(addr),
|
||||
type(determine_type()),
|
||||
raw_address(determine_raw_address()),
|
||||
network_type(ntype) {
|
||||
}
|
||||
|
||||
bool operator==(const bitcoin_address &btc_addr) const;
|
||||
|
||||
payment_type get_type() const {
|
||||
return type;
|
||||
}
|
||||
|
||||
std::string get_address() const {
|
||||
return address;
|
||||
}
|
||||
|
||||
bytes get_raw_address() const {
|
||||
return raw_address;
|
||||
}
|
||||
|
||||
bytes get_script() const;
|
||||
|
||||
network get_network_type() const {
|
||||
return network_type;
|
||||
}
|
||||
|
||||
private:
|
||||
enum size_segwit_address { P2WSH = 32,
|
||||
P2WPKH = 20 };
|
||||
|
||||
payment_type determine_type();
|
||||
|
||||
bytes determine_raw_address();
|
||||
|
||||
bool check_segwit_address(const size_segwit_address &size) const;
|
||||
|
||||
bool is_p2pk() const;
|
||||
|
||||
bool is_p2wpkh() const;
|
||||
|
||||
bool is_p2wsh() const;
|
||||
|
||||
bool is_p2pkh() const;
|
||||
|
||||
bool is_p2sh() const;
|
||||
|
||||
public:
|
||||
std::string address;
|
||||
|
||||
payment_type type;
|
||||
|
||||
bytes raw_address;
|
||||
|
||||
network network_type;
|
||||
};
|
||||
|
||||
class btc_multisig_address : public bitcoin_address {
|
||||
|
||||
public:
|
||||
btc_multisig_address() = default;
|
||||
|
||||
btc_multisig_address(const size_t n_required, const accounts_keys &keys);
|
||||
|
||||
size_t count_intersection(const accounts_keys &keys) const;
|
||||
|
||||
bytes get_redeem_script() const {
|
||||
return redeem_script;
|
||||
}
|
||||
|
||||
private:
|
||||
void create_redeem_script();
|
||||
|
||||
void create_address();
|
||||
|
||||
public:
|
||||
enum address_types { MAINNET_SCRIPT = 5,
|
||||
TESTNET_SCRIPT = 196 };
|
||||
|
||||
enum { OP_0 = 0x00,
|
||||
OP_EQUAL = 0x87,
|
||||
OP_HASH160 = 0xa9,
|
||||
OP_CHECKMULTISIG = 0xae };
|
||||
|
||||
bytes redeem_script;
|
||||
|
||||
size_t keys_required = 0;
|
||||
|
||||
accounts_keys witnesses_keys;
|
||||
};
|
||||
|
||||
// multisig segwit address (P2WSH)
|
||||
// https://0bin.net/paste/nfnSf0HcBqBUGDto#7zJMRUhGEBkyh-eASQPEwKfNHgQ4D5KrUJRsk8MTPSa
|
||||
class btc_multisig_segwit_address : public btc_multisig_address {
|
||||
|
||||
public:
|
||||
btc_multisig_segwit_address() = default;
|
||||
|
||||
btc_multisig_segwit_address(const size_t n_required, const accounts_keys &keys);
|
||||
|
||||
bool operator==(const btc_multisig_segwit_address &addr) const;
|
||||
|
||||
bytes get_witness_script() const {
|
||||
return witness_script;
|
||||
}
|
||||
|
||||
std::vector<public_key_type> get_keys();
|
||||
|
||||
private:
|
||||
void create_witness_script();
|
||||
|
||||
void create_segwit_address();
|
||||
|
||||
bytes get_address_bytes(const bytes &script_hash);
|
||||
|
||||
public:
|
||||
bytes witness_script;
|
||||
};
|
||||
|
||||
class btc_weighted_multisig_address : public bitcoin_address {
|
||||
|
||||
public:
|
||||
btc_weighted_multisig_address() = default;
|
||||
|
||||
btc_weighted_multisig_address(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
|
||||
network network_type = network::regtest);
|
||||
|
||||
bytes get_redeem_script() const {
|
||||
return redeem_script_;
|
||||
}
|
||||
bytes get_witness_script() const {
|
||||
return witness_script_;
|
||||
}
|
||||
|
||||
private:
|
||||
void create_redeem_script(const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data);
|
||||
void create_witness_script();
|
||||
void create_segwit_address();
|
||||
|
||||
public:
|
||||
bytes redeem_script_;
|
||||
bytes witness_script_;
|
||||
};
|
||||
|
||||
class btc_one_or_m_of_n_multisig_address : public bitcoin_address {
|
||||
public:
|
||||
btc_one_or_m_of_n_multisig_address() = default;
|
||||
btc_one_or_m_of_n_multisig_address(const fc::ecc::public_key &user_key_data, const uint8_t nrequired, const std::vector<fc::ecc::public_key> &keys_data,
|
||||
network network_type = network::regtest);
|
||||
bytes get_redeem_script() const {
|
||||
return redeem_script_;
|
||||
}
|
||||
bytes get_witness_script() const {
|
||||
return witness_script_;
|
||||
}
|
||||
|
||||
private:
|
||||
void create_redeem_script(const fc::ecc::public_key &user_key_data, const uint8_t nrequired, const std::vector<fc::ecc::public_key> &keys_data);
|
||||
void create_witness_script();
|
||||
void create_segwit_address();
|
||||
|
||||
public:
|
||||
bytes redeem_script_;
|
||||
bytes witness_script_;
|
||||
};
|
||||
|
||||
class btc_one_or_weighted_multisig_address : public bitcoin_address {
|
||||
public:
|
||||
btc_one_or_weighted_multisig_address() = default;
|
||||
btc_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
|
||||
network network_type = network::regtest);
|
||||
bytes get_redeem_script() const {
|
||||
return redeem_script_;
|
||||
}
|
||||
bytes get_witness_script() const {
|
||||
return witness_script_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data);
|
||||
void create_witness_script();
|
||||
void create_segwit_address();
|
||||
|
||||
public:
|
||||
bytes redeem_script_;
|
||||
bytes witness_script_;
|
||||
};
|
||||
|
||||
class btc_timelocked_one_or_weighted_multisig_address : public btc_one_or_weighted_multisig_address {
|
||||
public:
|
||||
btc_timelocked_one_or_weighted_multisig_address() = default;
|
||||
btc_timelocked_one_or_weighted_multisig_address(const fc::ecc::public_key &user_key_data, uint32_t latency, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data,
|
||||
network network_type = network::regtest);
|
||||
|
||||
private:
|
||||
void create_redeem_script(const fc::ecc::public_key &user_key_data, const std::vector<std::pair<fc::ecc::public_key, uint16_t>> &keys_data);
|
||||
|
||||
uint32_t latency_;
|
||||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
||||
FC_REFLECT_ENUM(graphene::peerplays_sidechain::bitcoin::bitcoin_address::network,
|
||||
(mainnet)(testnet)(regtest))
|
||||
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::bitcoin_address, (address)(type)(raw_address)(network_type));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_multisig_address, (graphene::peerplays_sidechain::bitcoin::bitcoin_address),
|
||||
(redeem_script)(keys_required)(witnesses_keys));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_multisig_segwit_address, (graphene::peerplays_sidechain::bitcoin::btc_multisig_address), (witness_script));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_weighted_multisig_address,
|
||||
(graphene::peerplays_sidechain::bitcoin::bitcoin_address),
|
||||
(redeem_script_)(witness_script_));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_one_or_m_of_n_multisig_address,
|
||||
(graphene::peerplays_sidechain::bitcoin::bitcoin_address),
|
||||
(redeem_script_)(witness_script_));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_one_or_weighted_multisig_address,
|
||||
(graphene::peerplays_sidechain::bitcoin::bitcoin_address),
|
||||
(redeem_script_)(witness_script_));
|
||||
|
||||
FC_REFLECT_DERIVED(graphene::peerplays_sidechain::bitcoin::btc_timelocked_one_or_weighted_multisig_address,
|
||||
(graphene::peerplays_sidechain::bitcoin::btc_one_or_weighted_multisig_address),
|
||||
(latency_));
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
enum class op {
|
||||
// push value
|
||||
_0 = 0x00,
|
||||
_1 = 0x51,
|
||||
_2 = 0x52,
|
||||
_3 = 0x53,
|
||||
_4 = 0x54,
|
||||
_5 = 0x55,
|
||||
_6 = 0x56,
|
||||
_7 = 0x57,
|
||||
_8 = 0x58,
|
||||
_9 = 0x59,
|
||||
_10 = 0x5a,
|
||||
_11 = 0x5b,
|
||||
_12 = 0x5c,
|
||||
_13 = 0x5d,
|
||||
_14 = 0x5e,
|
||||
_15 = 0x5f,
|
||||
_16 = 0x60,
|
||||
|
||||
// control
|
||||
IF = 0x63,
|
||||
NOTIF = 0x64,
|
||||
ELSE = 0x67,
|
||||
ENDIF = 0x68,
|
||||
RETURN = 0x6a,
|
||||
|
||||
// stack ops
|
||||
DROP = 0x75,
|
||||
DUP = 0x76,
|
||||
SWAP = 0x7c,
|
||||
|
||||
// bit logic
|
||||
EQUAL = 0x87,
|
||||
EQUALVERIFY = 0x88,
|
||||
ADD = 0x93,
|
||||
GREATERTHAN = 0xa0,
|
||||
GREATERTHANOREQUAL = 0xa2,
|
||||
|
||||
// crypto
|
||||
HASH160 = 0xa9,
|
||||
CHECKSIG = 0xac,
|
||||
CHECKMULTISIG = 0xae,
|
||||
// Locktime
|
||||
CHECKLOCKTIMEVERIFY = 0xb1,
|
||||
CHECKSEQUENCEVERIFY = 0xb2,
|
||||
};
|
||||
|
||||
class script_builder {
|
||||
|
||||
public:
|
||||
script_builder &operator<<(op opcode);
|
||||
|
||||
script_builder &operator<<(uint32_t number);
|
||||
|
||||
script_builder &operator<<(size_t size);
|
||||
|
||||
script_builder &operator<<(const bytes &sc);
|
||||
|
||||
script_builder &operator<<(const fc::sha256 &hash);
|
||||
|
||||
script_builder &operator<<(const fc::ripemd160 &hash);
|
||||
|
||||
script_builder &operator<<(const fc::ecc::public_key_data &pubkey_data);
|
||||
|
||||
operator bytes() const {
|
||||
return std::move(script);
|
||||
}
|
||||
|
||||
private:
|
||||
bytes script;
|
||||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#pragma once
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
struct out_point {
|
||||
fc::sha256 hash;
|
||||
uint32_t n = 0;
|
||||
out_point() = default;
|
||||
out_point(fc::sha256 _hash, uint32_t _n) :
|
||||
hash(_hash),
|
||||
n(_n) {
|
||||
}
|
||||
bool operator==(const out_point &op) const;
|
||||
};
|
||||
|
||||
struct tx_in {
|
||||
|
||||
bool operator==(const tx_in &ti) const;
|
||||
|
||||
out_point prevout;
|
||||
bytes scriptSig;
|
||||
uint32_t nSequence = 0xffffffff;
|
||||
std::vector<bytes> scriptWitness;
|
||||
};
|
||||
|
||||
struct tx_out {
|
||||
int64_t value = 0;
|
||||
bytes scriptPubKey;
|
||||
|
||||
bool operator==(const tx_out &to) const;
|
||||
|
||||
bool is_p2wsh() const;
|
||||
|
||||
bool is_p2wpkh() const;
|
||||
|
||||
bool is_p2pkh() const;
|
||||
|
||||
bool is_p2sh() const;
|
||||
|
||||
bool is_p2pk() const;
|
||||
|
||||
bytes get_data_or_script() const;
|
||||
};
|
||||
|
||||
struct bitcoin_transaction {
|
||||
|
||||
bool operator!=(const bitcoin_transaction &bt) const;
|
||||
|
||||
int32_t nVersion = 1;
|
||||
std::vector<tx_in> vin;
|
||||
std::vector<tx_out> vout;
|
||||
uint32_t nLockTime = 0;
|
||||
|
||||
fc::sha256 get_hash() const;
|
||||
fc::sha256 get_txid() const;
|
||||
size_t get_vsize() const;
|
||||
};
|
||||
|
||||
class bitcoin_transaction_builder {
|
||||
|
||||
public:
|
||||
bitcoin_transaction_builder() = default;
|
||||
|
||||
bitcoin_transaction_builder(const bitcoin_transaction _tx) :
|
||||
tx(_tx) {
|
||||
}
|
||||
|
||||
void set_version(int32_t version);
|
||||
|
||||
void set_locktime(uint32_t lock_time);
|
||||
|
||||
void add_in(const fc::sha256 &txid, uint32_t n_out,
|
||||
const bytes &script_code, bool front = false, uint32_t sequence = 0xffffffff);
|
||||
|
||||
void add_in(payment_type type, const fc::sha256 &txid, uint32_t n_out,
|
||||
const bytes &script_code, bool front = false, uint32_t sequence = 0xffffffff);
|
||||
|
||||
void add_in(payment_type type, tx_in txin, const bytes &script_code, bool front = false);
|
||||
|
||||
void add_out(payment_type type, int64_t amount, const std::string &base58_address, bool front = false);
|
||||
|
||||
void add_out(payment_type type, int64_t amount, const fc::ecc::public_key_data &pubkey, bool front = false);
|
||||
|
||||
void add_out(payment_type type, int64_t amount, const bytes &script_code, bool front = false);
|
||||
|
||||
void add_out_all_type(const uint64_t &amount, const bitcoin_address &address, bool front = false);
|
||||
|
||||
bitcoin_transaction get_transaction() const {
|
||||
return tx;
|
||||
}
|
||||
|
||||
private:
|
||||
inline bool is_payment_to_pubkey(payment_type type);
|
||||
|
||||
bytes get_script_pubkey(payment_type type, const bytes &script_code);
|
||||
|
||||
bitcoin_transaction tx;
|
||||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::out_point, (hash)(n))
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::tx_in, (prevout)(scriptSig)(nSequence)(scriptWitness))
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::tx_out, (value)(scriptPubKey))
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::bitcoin_transaction, (nVersion)(vin)(vout)(nLockTime))
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/* Copyright (c) 2017 Pieter Wuille
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin { namespace segwit_addr {
|
||||
|
||||
/** Decode a SegWit address. Returns (witver, witprog). witver = -1 means failure. */
|
||||
std::pair<int, std::vector<uint8_t>> decode(const std::string &hrp, const std::string &addr);
|
||||
|
||||
/** Encode a SegWit address. Empty string means failure. */
|
||||
std::string encode(const std::string &hrp, int witver, const std::vector<uint8_t> &witprog);
|
||||
|
||||
}}}} // namespace graphene::peerplays_sidechain::bitcoin::segwit_addr
|
||||
|
|
@ -0,0 +1,354 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_transaction.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
inline void write_compact_size(bytes &vec, size_t size) {
|
||||
bytes sb;
|
||||
sb.reserve(2);
|
||||
if (size < 253) {
|
||||
sb.insert(sb.end(), static_cast<uint8_t>(size));
|
||||
} else if (size <= std::numeric_limits<unsigned short>::max()) {
|
||||
uint16_t tmp = htole16(static_cast<uint16_t>(size));
|
||||
sb.insert(sb.end(), static_cast<uint8_t>(253));
|
||||
sb.insert(sb.end(), reinterpret_cast<const char *>(tmp), reinterpret_cast<const char *>(tmp) + sizeof(tmp));
|
||||
} else if (size <= std::numeric_limits<unsigned int>::max()) {
|
||||
uint32_t tmp = htole32(static_cast<uint32_t>(size));
|
||||
sb.insert(sb.end(), static_cast<uint8_t>(254));
|
||||
sb.insert(sb.end(), reinterpret_cast<const char *>(tmp), reinterpret_cast<const char *>(tmp) + sizeof(tmp));
|
||||
} else {
|
||||
uint64_t tmp = htole64(static_cast<uint64_t>(size));
|
||||
sb.insert(sb.end(), static_cast<uint8_t>(255));
|
||||
sb.insert(sb.end(), reinterpret_cast<const char *>(tmp), reinterpret_cast<const char *>(tmp) + sizeof(tmp));
|
||||
}
|
||||
vec.insert(vec.end(), sb.begin(), sb.end());
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack_compact_size(Stream &s, size_t size) {
|
||||
if (size < 253) {
|
||||
fc::raw::pack(s, static_cast<uint8_t>(size));
|
||||
} else if (size <= std::numeric_limits<unsigned short>::max()) {
|
||||
fc::raw::pack(s, static_cast<uint8_t>(253));
|
||||
fc::raw::pack(s, htole16(static_cast<uint16_t>(size)));
|
||||
} else if (size <= std::numeric_limits<unsigned int>::max()) {
|
||||
fc::raw::pack(s, static_cast<uint8_t>(254));
|
||||
fc::raw::pack(s, htole32(static_cast<uint32_t>(size)));
|
||||
} else {
|
||||
fc::raw::pack(s, static_cast<uint8_t>(255));
|
||||
fc::raw::pack(s, htole64(static_cast<uint64_t>(size)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline uint64_t unpack_compact_size(Stream &s) {
|
||||
uint8_t size;
|
||||
uint64_t size_ret;
|
||||
|
||||
fc::raw::unpack(s, size);
|
||||
|
||||
if (size < 253) {
|
||||
size_ret = size;
|
||||
} else if (size == 253) {
|
||||
uint16_t tmp;
|
||||
fc::raw::unpack(s, tmp);
|
||||
size_ret = le16toh(tmp);
|
||||
if (size_ret < 253)
|
||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()");
|
||||
} else if (size == 254) {
|
||||
uint32_t tmp;
|
||||
fc::raw::unpack(s, tmp);
|
||||
size_ret = le32toh(tmp);
|
||||
if (size_ret < 0x10000u)
|
||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()");
|
||||
} else {
|
||||
uint32_t tmp;
|
||||
fc::raw::unpack(s, tmp);
|
||||
size_ret = le64toh(tmp);
|
||||
if (size_ret < 0x100000000ULL)
|
||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "non-canonical unpack_compact_size()");
|
||||
}
|
||||
|
||||
if (size_ret > 0x08000000)
|
||||
FC_THROW_EXCEPTION(fc::parse_error_exception, "unpack_compact_size(): size too large");
|
||||
|
||||
return size_ret;
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, int64_t &u, uint32_t _max_depth = FC_PACK_MAX_DEPTH) {
|
||||
s.read((char *)&u, sizeof(u));
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, int32_t &u, uint32_t _max_depth = FC_PACK_MAX_DEPTH) {
|
||||
s.read((char *)&u, sizeof(u));
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const std::vector<char> &v) {
|
||||
pack_compact_size(s, v.size());
|
||||
if (!v.empty())
|
||||
s.write(v.data(), v.size());
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, std::vector<char> &v) {
|
||||
const auto size = unpack_compact_size(s);
|
||||
v.resize(size);
|
||||
if (size)
|
||||
s.read(v.data(), size);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
inline void pack(Stream &s, const T &val) {
|
||||
fc::raw::pack(s, val);
|
||||
}
|
||||
|
||||
template <typename Stream, typename T>
|
||||
inline void unpack(Stream &s, T &val) {
|
||||
fc::raw::unpack(s, val);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const out_point &op) {
|
||||
fc::sha256 reversed(op.hash);
|
||||
std::reverse(reversed.data(), reversed.data() + reversed.data_size());
|
||||
s.write(reversed.data(), reversed.data_size());
|
||||
pack(s, op.n);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, out_point &op) {
|
||||
uint64_t hash_size = op.hash.data_size();
|
||||
std::unique_ptr<char[]> hash_data(new char[hash_size]);
|
||||
s.read(hash_data.get(), hash_size);
|
||||
std::reverse(hash_data.get(), hash_data.get() + hash_size);
|
||||
|
||||
op.hash = fc::sha256(hash_data.get(), hash_size);
|
||||
unpack(s, op.n);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const tx_in &in) {
|
||||
pack(s, in.prevout);
|
||||
pack(s, in.scriptSig);
|
||||
pack(s, in.nSequence);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, tx_in &in) {
|
||||
unpack(s, in.prevout);
|
||||
unpack(s, in.scriptSig);
|
||||
unpack(s, in.nSequence);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const tx_out &out) {
|
||||
pack(s, out.value);
|
||||
pack(s, out.scriptPubKey);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, tx_out &out) {
|
||||
unpack(s, out.value);
|
||||
unpack(s, out.scriptPubKey);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack(Stream &s, const bitcoin_transaction &tx, bool with_witness = true) {
|
||||
uint8_t flags = 0;
|
||||
|
||||
if (with_witness) {
|
||||
for (const auto &in : tx.vin) {
|
||||
if (!in.scriptWitness.empty()) {
|
||||
flags |= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pack(s, tx.nVersion);
|
||||
|
||||
if (flags) {
|
||||
pack_compact_size(s, 0);
|
||||
pack(s, flags);
|
||||
}
|
||||
|
||||
pack_compact_size(s, tx.vin.size());
|
||||
for (const auto &in : tx.vin)
|
||||
pack(s, in);
|
||||
|
||||
pack_compact_size(s, tx.vout.size());
|
||||
for (const auto &out : tx.vout)
|
||||
pack(s, out);
|
||||
|
||||
if (flags & 1) {
|
||||
for (const auto in : tx.vin) {
|
||||
pack_compact_size(s, in.scriptWitness.size());
|
||||
for (const auto &sc : in.scriptWitness)
|
||||
pack(s, sc);
|
||||
}
|
||||
}
|
||||
|
||||
pack(s, tx.nLockTime);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void unpack(Stream &s, bitcoin_transaction &tx) {
|
||||
uint8_t flags = 0;
|
||||
|
||||
unpack(s, tx.nVersion);
|
||||
|
||||
auto vin_size = unpack_compact_size(s);
|
||||
if (vin_size == 0) {
|
||||
unpack(s, flags);
|
||||
vin_size = unpack_compact_size(s);
|
||||
}
|
||||
|
||||
tx.vin.reserve(vin_size);
|
||||
for (size_t i = 0; i < vin_size; i++) {
|
||||
tx_in in;
|
||||
unpack(s, in);
|
||||
tx.vin.push_back(in);
|
||||
}
|
||||
|
||||
const auto vout_size = unpack_compact_size(s);
|
||||
tx.vout.reserve(vout_size);
|
||||
for (size_t i = 0; i < vout_size; i++) {
|
||||
tx_out out;
|
||||
unpack(s, out);
|
||||
tx.vout.push_back(out);
|
||||
}
|
||||
|
||||
if (flags & 1) {
|
||||
for (auto &in : tx.vin) {
|
||||
uint64_t stack_size = unpack_compact_size(s);
|
||||
in.scriptWitness.reserve(stack_size);
|
||||
for (uint64_t i = 0; i < stack_size; i++) {
|
||||
std::vector<char> script;
|
||||
unpack(s, script);
|
||||
in.scriptWitness.push_back(script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unpack(s, tx.nLockTime);
|
||||
}
|
||||
|
||||
inline std::vector<char> pack(const bitcoin_transaction &v, bool with_witness = true) {
|
||||
fc::datastream<size_t> ps;
|
||||
pack(ps, v, with_witness);
|
||||
std::vector<char> vec(ps.tellp());
|
||||
|
||||
if (!vec.empty()) {
|
||||
fc::datastream<char *> ds(vec.data(), size_t(vec.size()));
|
||||
pack(ds, v, with_witness);
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
inline bitcoin_transaction unpack(const std::vector<char> &s) {
|
||||
try {
|
||||
bitcoin_transaction tmp;
|
||||
if (!s.empty()) {
|
||||
fc::datastream<const char *> ds(s.data(), size_t(s.size()));
|
||||
unpack(ds, tmp);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
FC_RETHROW_EXCEPTIONS(warn, "error unpacking ${type}", ("type", "transaction"))
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack_tx_signature(Stream &s, const std::vector<char> &scriptPubKey, const bitcoin_transaction &tx, unsigned int in_index, int hash_type) {
|
||||
pack(s, tx.nVersion);
|
||||
|
||||
pack_compact_size(s, tx.vin.size());
|
||||
for (size_t i = 0; i < tx.vin.size(); i++) {
|
||||
const auto &in = tx.vin[i];
|
||||
pack(s, in.prevout);
|
||||
if (i == in_index)
|
||||
pack(s, scriptPubKey);
|
||||
else
|
||||
pack_compact_size(s, 0); // Blank signature
|
||||
pack(s, in.nSequence);
|
||||
}
|
||||
|
||||
pack_compact_size(s, tx.vout.size());
|
||||
for (const auto &out : tx.vout)
|
||||
pack(s, out);
|
||||
|
||||
pack(s, tx.nLockTime);
|
||||
pack(s, hash_type);
|
||||
}
|
||||
|
||||
template <typename Stream>
|
||||
inline void pack_tx_witness_signature(Stream &s, const std::vector<char> &scriptCode, const bitcoin_transaction &tx, unsigned int in_index, int64_t amount, int hash_type) {
|
||||
|
||||
fc::sha256 hash_prevouts;
|
||||
fc::sha256 hash_sequence;
|
||||
fc::sha256 hash_output;
|
||||
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
for (const auto in : tx.vin)
|
||||
pack(ps, in.prevout);
|
||||
|
||||
std::vector<char> vec(ps.tellp());
|
||||
if (vec.size()) {
|
||||
fc::datastream<char *> ds(vec.data(), size_t(vec.size()));
|
||||
for (const auto in : tx.vin)
|
||||
pack(ds, in.prevout);
|
||||
}
|
||||
|
||||
hash_prevouts = fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size()));
|
||||
}
|
||||
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
for (const auto in : tx.vin)
|
||||
pack(ps, in.nSequence);
|
||||
|
||||
std::vector<char> vec(ps.tellp());
|
||||
if (vec.size()) {
|
||||
fc::datastream<char *> ds(vec.data(), size_t(vec.size()));
|
||||
for (const auto in : tx.vin)
|
||||
pack(ds, in.nSequence);
|
||||
}
|
||||
|
||||
hash_sequence = fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size()));
|
||||
};
|
||||
|
||||
{
|
||||
fc::datastream<size_t> ps;
|
||||
for (const auto out : tx.vout)
|
||||
pack(ps, out);
|
||||
|
||||
std::vector<char> vec(ps.tellp());
|
||||
if (vec.size()) {
|
||||
fc::datastream<char *> ds(vec.data(), size_t(vec.size()));
|
||||
for (const auto out : tx.vout)
|
||||
pack(ds, out);
|
||||
}
|
||||
|
||||
hash_output = fc::sha256::hash(fc::sha256::hash(vec.data(), vec.size()));
|
||||
}
|
||||
|
||||
pack(s, tx.nVersion);
|
||||
pack(s, hash_prevouts);
|
||||
pack(s, hash_sequence);
|
||||
|
||||
pack(s, tx.vin[in_index].prevout);
|
||||
pack(s, scriptCode);
|
||||
pack(s, amount);
|
||||
pack(s, tx.vin[in_index].nSequence);
|
||||
|
||||
pack(s, hash_output);
|
||||
pack(s, tx.nLockTime);
|
||||
pack(s, hash_type);
|
||||
}
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
#include <secp256k1.h>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
class bitcoin_transaction;
|
||||
|
||||
const secp256k1_context_t *btc_context();
|
||||
|
||||
fc::sha256 get_signature_hash(const bitcoin_transaction &tx, const bytes &scriptPubKey, int64_t amount,
|
||||
size_t in_index, int hash_type, bool is_witness);
|
||||
|
||||
std::vector<char> privkey_sign(const bytes &privkey, const fc::sha256 &hash, const secp256k1_context_t *context_sign = nullptr);
|
||||
|
||||
std::vector<bytes> sign_witness_transaction_part(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const bytes &privkey,
|
||||
const secp256k1_context_t *context_sign = nullptr, int hash_type = 1);
|
||||
|
||||
void sign_witness_transaction_finalize(bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts, bool use_mulisig_workaround = true);
|
||||
|
||||
bool verify_sig(const bytes &sig, const bytes &pubkey, const bytes &msg, const secp256k1_context_t *context);
|
||||
|
||||
std::vector<std::vector<bytes>> sort_sigs(const bitcoin_transaction &tx, const std::vector<bytes> &redeem_scripts,
|
||||
const std::vector<uint64_t> &amounts, const secp256k1_context_t *context);
|
||||
|
||||
void add_signatures_to_transaction_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set);
|
||||
|
||||
void add_signatures_to_transaction_weighted_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set);
|
||||
|
||||
void add_signatures_to_transaction_user_weighted_multisig(bitcoin_transaction &tx, std::vector<std::vector<bytes>> &signature_set);
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include <fc/reflect/reflect.hpp>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
class bitcoin_transaction;
|
||||
|
||||
using bytes = std::vector<char>;
|
||||
using accounts_keys = std::map<graphene::chain::son_id_type, graphene::chain::public_key_type>;
|
||||
using full_btc_transaction = std::pair<bitcoin_transaction, uint64_t>;
|
||||
|
||||
enum class payment_type {
|
||||
NULLDATA,
|
||||
P2PK,
|
||||
P2PKH,
|
||||
P2SH,
|
||||
P2WPKH,
|
||||
P2WSH,
|
||||
P2SH_WPKH,
|
||||
P2SH_WSH
|
||||
};
|
||||
|
||||
enum class sidechain_proposal_type {
|
||||
ISSUE_BTC,
|
||||
SEND_BTC_TRANSACTION,
|
||||
REVERT_BTC_TRANSACTION
|
||||
};
|
||||
|
||||
struct prev_out {
|
||||
bool operator!=(const prev_out &obj) const {
|
||||
if (this->hash_tx != obj.hash_tx ||
|
||||
this->n_vout != obj.n_vout ||
|
||||
this->amount != obj.amount) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash_tx;
|
||||
uint32_t n_vout;
|
||||
uint64_t amount;
|
||||
};
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
||||
FC_REFLECT_ENUM(graphene::peerplays_sidechain::bitcoin::payment_type, (NULLDATA)(P2PK)(P2PKH)(P2SH)(P2WPKH)(P2WSH)(P2SH_WPKH)(P2SH_WSH));
|
||||
FC_REFLECT_ENUM(graphene::peerplays_sidechain::bitcoin::sidechain_proposal_type, (ISSUE_BTC)(SEND_BTC_TRANSACTION)(REVERT_BTC_TRANSACTION));
|
||||
FC_REFLECT(graphene::peerplays_sidechain::bitcoin::prev_out, (hash_tx)(n_vout)(amount));
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
#include <fc/crypto/elliptic.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <graphene/peerplays_sidechain/bitcoin/types.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain { namespace bitcoin {
|
||||
|
||||
fc::ecc::public_key_data create_public_key_data(const std::vector<char> &public_key);
|
||||
|
||||
bytes get_privkey_bytes(const std::string &privkey_base58);
|
||||
|
||||
bytes parse_hex(const std::string &str);
|
||||
|
||||
std::vector<bytes> get_pubkey_from_redeemScript(bytes script);
|
||||
|
||||
bytes public_key_data_to_bytes(const fc::ecc::public_key_data &key);
|
||||
|
||||
std::vector<bytes> read_byte_arrays_from_string(const std::string &string_buf);
|
||||
|
||||
std::string write_transaction_signatures(const std::vector<bytes> &data);
|
||||
|
||||
void read_transaction_data(const std::string &string_buf, std::string &tx_hex, std::vector<uint64_t> &in_amounts, std::string &redeem_script);
|
||||
|
||||
std::string write_transaction_data(const std::string &tx, const std::vector<uint64_t> &in_amounts, const std::string &redeem_script);
|
||||
|
||||
}}} // namespace graphene::peerplays_sidechain::bitcoin
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <fc/safe.hpp>
|
||||
#include <fc/time.hpp>
|
||||
|
||||
#include <graphene/chain/protocol/asset.hpp>
|
||||
#include <graphene/chain/protocol/types.hpp>
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
using namespace graphene::chain;
|
||||
|
||||
using bytes = std::vector<unsigned char>;
|
||||
|
||||
struct prev_out {
|
||||
bool operator!=(const prev_out &obj) const {
|
||||
if (this->hash_tx != obj.hash_tx ||
|
||||
this->n_vout != obj.n_vout ||
|
||||
this->amount != obj.amount) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hash_tx;
|
||||
uint32_t n_vout;
|
||||
uint64_t amount;
|
||||
};
|
||||
|
||||
struct info_for_vin {
|
||||
info_for_vin() = default;
|
||||
|
||||
info_for_vin(const prev_out &_out, const std::string &_address, bytes _script = bytes(), bool _resend = false);
|
||||
|
||||
bool operator!=(const info_for_vin &obj) const;
|
||||
|
||||
struct comparer {
|
||||
bool operator()(const info_for_vin &lhs, const info_for_vin &rhs) const;
|
||||
};
|
||||
|
||||
static uint64_t count_id_info_for_vin;
|
||||
uint64_t id;
|
||||
|
||||
fc::sha256 identifier;
|
||||
|
||||
prev_out out;
|
||||
std::string address;
|
||||
bytes script;
|
||||
|
||||
bool used = false;
|
||||
bool resend = false;
|
||||
};
|
||||
|
||||
struct sidechain_event_data {
|
||||
fc::time_point_sec timestamp;
|
||||
uint32_t block_num;
|
||||
sidechain_type sidechain;
|
||||
std::string sidechain_uid;
|
||||
std::string sidechain_transaction_id;
|
||||
std::string sidechain_from;
|
||||
std::string sidechain_to;
|
||||
std::string sidechain_currency;
|
||||
fc::safe<int64_t> sidechain_amount;
|
||||
account_id_type peerplays_from;
|
||||
account_id_type peerplays_to;
|
||||
asset peerplays_asset;
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/app/plugin.hpp>
|
||||
|
||||
#include <graphene/chain/son_object.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
using namespace chain;
|
||||
|
||||
namespace detail {
|
||||
class peerplays_sidechain_plugin_impl;
|
||||
}
|
||||
|
||||
class peerplays_sidechain_plugin : public graphene::app::plugin {
|
||||
public:
|
||||
peerplays_sidechain_plugin();
|
||||
virtual ~peerplays_sidechain_plugin();
|
||||
|
||||
std::string plugin_name() const override;
|
||||
virtual void plugin_set_program_options(
|
||||
boost::program_options::options_description &cli,
|
||||
boost::program_options::options_description &cfg) override;
|
||||
virtual void plugin_initialize(const boost::program_options::variables_map &options) override;
|
||||
virtual void plugin_startup() override;
|
||||
|
||||
std::unique_ptr<detail::peerplays_sidechain_plugin_impl> my;
|
||||
|
||||
std::set<chain::son_id_type> &get_sons();
|
||||
const son_id_type get_current_son_id();
|
||||
const son_object get_current_son_object();
|
||||
const son_object get_son_object(son_id_type son_id);
|
||||
bool is_active_son(son_id_type son_id);
|
||||
bool is_son_deregistered(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <fc/signals.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/sidechain_transaction_object.hpp>
|
||||
#include <graphene/chain/son_wallet_deposit_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/defs.hpp>
|
||||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_handler {
|
||||
public:
|
||||
sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
|
||||
virtual ~sidechain_net_handler();
|
||||
|
||||
sidechain_type get_sidechain();
|
||||
std::vector<std::string> get_sidechain_deposit_addresses();
|
||||
std::vector<std::string> get_sidechain_withdraw_addresses();
|
||||
std::string get_private_key(std::string public_key);
|
||||
|
||||
bool proposal_exists(int32_t operation_tag, const object_id_type &object_id, boost::optional<chain::operation &> proposal_op = boost::none);
|
||||
bool signer_expected(const sidechain_transaction_object &sto, son_id_type signer);
|
||||
bool approve_proposal(const proposal_id_type &proposal_id, const son_id_type &son_id);
|
||||
void sidechain_event_data_received(const sidechain_event_data &sed);
|
||||
|
||||
void process_proposals();
|
||||
void process_active_sons_change();
|
||||
void create_deposit_addresses();
|
||||
void process_deposits();
|
||||
void process_withdrawals();
|
||||
void process_sidechain_transactions();
|
||||
void send_sidechain_transactions();
|
||||
void settle_sidechain_transactions();
|
||||
|
||||
virtual bool process_proposal(const proposal_object &po) = 0;
|
||||
virtual void process_primary_wallet() = 0;
|
||||
virtual void process_sidechain_addresses() = 0;
|
||||
virtual bool process_deposit(const son_wallet_deposit_object &swdo) = 0;
|
||||
virtual bool process_withdrawal(const son_wallet_withdraw_object &swwo) = 0;
|
||||
virtual std::string process_sidechain_transaction(const sidechain_transaction_object &sto) = 0;
|
||||
virtual std::string send_sidechain_transaction(const sidechain_transaction_object &sto) = 0;
|
||||
virtual int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto) = 0;
|
||||
|
||||
protected:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
graphene::chain::database &database;
|
||||
sidechain_type sidechain;
|
||||
|
||||
std::map<std::string, std::string> private_keys;
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/bitcoin/bitcoin_address.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <zmq.hpp>
|
||||
|
||||
#include <fc/network/http/connection.hpp>
|
||||
#include <fc/signals.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class btc_txout {
|
||||
public:
|
||||
std::string txid_;
|
||||
unsigned int out_num_;
|
||||
uint64_t amount_;
|
||||
};
|
||||
|
||||
class bitcoin_rpc_client {
|
||||
public:
|
||||
bitcoin_rpc_client(std::string _ip, uint32_t _rpc, std::string _user, std::string _password, std::string _wallet, std::string _wallet_password);
|
||||
|
||||
std::string addmultisigaddress(const uint32_t nrequired, const std::vector<std::string> public_keys);
|
||||
std::string combinepsbt(const vector<std::string> &psbts);
|
||||
std::string createmultisig(const uint32_t nrequired, const std::vector<std::string> public_keys);
|
||||
std::string createpsbt(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs);
|
||||
std::string createrawtransaction(const std::vector<btc_txout> &ins, const fc::flat_map<std::string, double> outs);
|
||||
std::string createwallet(const std::string &wallet_name);
|
||||
std::string decodepsbt(std::string const &tx_psbt);
|
||||
std::string decoderawtransaction(std::string const &tx_hex);
|
||||
std::string encryptwallet(const std::string &passphrase);
|
||||
uint64_t estimatesmartfee(uint16_t conf_target = 128);
|
||||
std::string finalizepsbt(std::string const &tx_psbt);
|
||||
std::string getaddressinfo(const std::string &address);
|
||||
std::string getblock(const std::string &block_hash, int32_t verbosity = 2);
|
||||
std::string getrawtransaction(const std::string &txid, const bool verbose = false);
|
||||
std::string gettransaction(const std::string &txid, const bool include_watch_only = false);
|
||||
std::string getblockchaininfo();
|
||||
void importaddress(const std::string &address_or_script, const std::string &label = "", const bool rescan = true, const bool p2sh = false);
|
||||
std::vector<btc_txout> listunspent(const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
|
||||
std::vector<btc_txout> listunspent_by_address_and_amount(const std::string &address, double transfer_amount, const uint32_t minconf = 1, const uint32_t maxconf = 9999999);
|
||||
std::string loadwallet(const std::string &filename);
|
||||
std::string sendrawtransaction(const std::string &tx_hex);
|
||||
std::string signrawtransactionwithwallet(const std::string &tx_hash);
|
||||
std::string unloadwallet(const std::string &filename);
|
||||
//std::string walletlock();
|
||||
std::string walletprocesspsbt(std::string const &tx_psbt);
|
||||
//bool walletpassphrase(const std::string &passphrase, uint32_t timeout = 60);
|
||||
|
||||
private:
|
||||
fc::http::reply send_post_request(std::string body, bool show_log = false);
|
||||
|
||||
std::string ip;
|
||||
uint32_t rpc_port;
|
||||
std::string user;
|
||||
std::string password;
|
||||
std::string wallet;
|
||||
std::string wallet_password;
|
||||
|
||||
fc::http::header authorization;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class zmq_listener {
|
||||
public:
|
||||
zmq_listener(std::string _ip, uint32_t _zmq);
|
||||
|
||||
fc::signal<void(const std::string &)> event_received;
|
||||
|
||||
private:
|
||||
void handle_zmq();
|
||||
std::vector<zmq::message_t> receive_multipart();
|
||||
|
||||
std::string ip;
|
||||
uint32_t zmq_port;
|
||||
|
||||
zmq::context_t ctx;
|
||||
zmq::socket_t socket;
|
||||
};
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class sidechain_net_handler_bitcoin : public sidechain_net_handler {
|
||||
public:
|
||||
sidechain_net_handler_bitcoin(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
|
||||
virtual ~sidechain_net_handler_bitcoin();
|
||||
|
||||
bool process_proposal(const proposal_object &po);
|
||||
void process_primary_wallet();
|
||||
void process_sidechain_addresses();
|
||||
bool process_deposit(const son_wallet_deposit_object &swdo);
|
||||
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
|
||||
std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
std::string send_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
|
||||
private:
|
||||
std::string ip;
|
||||
uint32_t zmq_port;
|
||||
uint32_t rpc_port;
|
||||
std::string rpc_user;
|
||||
std::string rpc_password;
|
||||
std::string wallet;
|
||||
std::string wallet_password;
|
||||
|
||||
std::unique_ptr<bitcoin_rpc_client> bitcoin_client;
|
||||
std::unique_ptr<zmq_listener> listener;
|
||||
|
||||
fc::future<void> on_changed_objects_task;
|
||||
bitcoin::bitcoin_address::network network_type;
|
||||
|
||||
std::string create_primary_wallet_address(const std::vector<son_info> &son_pubkeys);
|
||||
|
||||
std::string create_primary_wallet_transaction(const son_wallet_object &prev_swo, std::string new_sw_address);
|
||||
std::string create_deposit_transaction(const son_wallet_deposit_object &swdo);
|
||||
std::string create_withdrawal_transaction(const son_wallet_withdraw_object &swwo);
|
||||
|
||||
std::string create_transaction(const std::vector<btc_txout> &inputs, const fc::flat_map<std::string, double> outputs, std::string &redeem_script);
|
||||
std::string sign_transaction(const sidechain_transaction_object &sto);
|
||||
std::string send_transaction(const sidechain_transaction_object &sto);
|
||||
|
||||
void handle_event(const std::string &event_data);
|
||||
std::string get_redeemscript_for_userdeposit(const std::string &user_address);
|
||||
std::vector<info_for_vin> extract_info_from_block(const std::string &_block);
|
||||
void on_changed_objects(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
|
||||
void on_changed_objects_cb(const vector<object_id_type> &ids, const flat_set<account_id_type> &accounts);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fc/signals.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_handler_peerplays : public sidechain_net_handler {
|
||||
public:
|
||||
sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options);
|
||||
virtual ~sidechain_net_handler_peerplays();
|
||||
|
||||
bool process_proposal(const proposal_object &po);
|
||||
void process_primary_wallet();
|
||||
void process_sidechain_addresses();
|
||||
bool process_deposit(const son_wallet_deposit_object &swdo);
|
||||
bool process_withdrawal(const son_wallet_withdraw_object &swwo);
|
||||
std::string process_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
std::string send_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
int64_t settle_sidechain_transaction(const sidechain_transaction_object &sto);
|
||||
|
||||
private:
|
||||
void on_applied_block(const signed_block &b);
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#pragma once
|
||||
|
||||
#include <graphene/chain/sidechain_defs.hpp>
|
||||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
class sidechain_net_manager {
|
||||
public:
|
||||
sidechain_net_manager(peerplays_sidechain_plugin &_plugin);
|
||||
virtual ~sidechain_net_manager();
|
||||
|
||||
bool create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options);
|
||||
void process_proposals();
|
||||
void process_active_sons_change();
|
||||
void create_deposit_addresses();
|
||||
void process_deposits();
|
||||
void process_withdrawals();
|
||||
void process_sidechain_transactions();
|
||||
void send_sidechain_transactions();
|
||||
void settle_sidechain_transactions();
|
||||
|
||||
private:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
graphene::chain::database &database;
|
||||
std::vector<std::unique_ptr<sidechain_net_handler>> net_handlers;
|
||||
};
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,698 @@
|
|||
#include <graphene/peerplays_sidechain/peerplays_sidechain_plugin.hpp>
|
||||
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/range/algorithm_ext/insert.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/smart_ref_impl.hpp>
|
||||
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
#include <graphene/chain/protocol/transfer.hpp>
|
||||
#include <graphene/chain/sidechain_address_object.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/chain/son_wallet_withdraw_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
namespace bpo = boost::program_options;
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class peerplays_sidechain_plugin_impl {
|
||||
public:
|
||||
peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin &_plugin);
|
||||
virtual ~peerplays_sidechain_plugin_impl();
|
||||
|
||||
void plugin_set_program_options(
|
||||
boost::program_options::options_description &cli,
|
||||
boost::program_options::options_description &cfg);
|
||||
void plugin_initialize(const boost::program_options::variables_map &opt);
|
||||
void plugin_startup();
|
||||
|
||||
std::set<chain::son_id_type> &get_sons();
|
||||
const son_id_type get_current_son_id();
|
||||
const son_object get_current_son_object();
|
||||
const son_object get_son_object(son_id_type son_id);
|
||||
bool is_active_son(son_id_type son_id);
|
||||
bool is_son_deregistered(son_id_type son_id);
|
||||
bool is_son_deregister_op_valid(const chain::operation &op);
|
||||
bool is_son_down_op_valid(const chain::operation &op);
|
||||
bool is_valid_son_proposal(const chain::proposal_object &proposal);
|
||||
fc::ecc::private_key get_private_key(son_id_type son_id);
|
||||
fc::ecc::private_key get_private_key(chain::public_key_type public_key);
|
||||
|
||||
void schedule_heartbeat_loop();
|
||||
void heartbeat_loop();
|
||||
void schedule_son_processing();
|
||||
void son_processing();
|
||||
void approve_proposals();
|
||||
void create_son_down_proposals();
|
||||
void create_son_deregister_proposals();
|
||||
|
||||
void process_proposals();
|
||||
void process_active_sons_change();
|
||||
void create_deposit_addresses();
|
||||
void process_deposits();
|
||||
void process_withdrawals();
|
||||
void process_sidechain_transactions();
|
||||
void send_sidechain_transactions();
|
||||
void settle_sidechain_transactions();
|
||||
|
||||
private:
|
||||
peerplays_sidechain_plugin &plugin;
|
||||
|
||||
boost::program_options::variables_map options;
|
||||
|
||||
bool config_ready_son;
|
||||
bool config_ready_bitcoin;
|
||||
bool config_ready_peerplays;
|
||||
|
||||
son_id_type current_son_id;
|
||||
|
||||
std::unique_ptr<peerplays_sidechain::sidechain_net_manager> net_manager;
|
||||
std::set<chain::son_id_type> sons;
|
||||
std::map<chain::public_key_type, fc::ecc::private_key> private_keys;
|
||||
fc::future<void> _heartbeat_task;
|
||||
fc::future<void> _son_processing_task;
|
||||
|
||||
bool first_block_skipped;
|
||||
void on_applied_block(const signed_block &b);
|
||||
};
|
||||
|
||||
peerplays_sidechain_plugin_impl::peerplays_sidechain_plugin_impl(peerplays_sidechain_plugin &_plugin) :
|
||||
plugin(_plugin),
|
||||
config_ready_son(false),
|
||||
config_ready_bitcoin(false),
|
||||
config_ready_peerplays(false),
|
||||
current_son_id(son_id_type(std::numeric_limits<uint32_t>().max())),
|
||||
net_manager(nullptr),
|
||||
first_block_skipped(false) {
|
||||
}
|
||||
|
||||
peerplays_sidechain_plugin_impl::~peerplays_sidechain_plugin_impl() {
|
||||
try {
|
||||
if (_heartbeat_task.valid())
|
||||
_heartbeat_task.cancel_and_wait(__FUNCTION__);
|
||||
} catch (fc::canceled_exception &) {
|
||||
//Expected exception. Move along.
|
||||
} catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
}
|
||||
|
||||
try {
|
||||
if (_son_processing_task.valid())
|
||||
_son_processing_task.cancel_and_wait(__FUNCTION__);
|
||||
} catch (fc::canceled_exception &) {
|
||||
//Expected exception. Move along.
|
||||
} catch (fc::exception &e) {
|
||||
edump((e.to_detail_string()));
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::plugin_set_program_options(
|
||||
boost::program_options::options_description &cli,
|
||||
boost::program_options::options_description &cfg) {
|
||||
auto default_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(std::string("nathan")));
|
||||
string son_id_example = fc::json::to_string(chain::son_id_type(5));
|
||||
string son_id_example2 = fc::json::to_string(chain::son_id_type(6));
|
||||
|
||||
cli.add_options()("son-id", bpo::value<vector<string>>(), ("ID of SON controlled by this node (e.g. " + son_id_example + ", quotes are required)").c_str());
|
||||
cli.add_options()("son-ids", bpo::value<string>(), ("IDs of multiple SONs controlled by this node (e.g. [" + son_id_example + ", " + son_id_example2 + "], quotes are required)").c_str());
|
||||
cli.add_options()("peerplays-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair(chain::public_key_type(default_priv_key.get_public_key()), graphene::utilities::key_to_wif(default_priv_key))),
|
||||
"Tuple of [PublicKey, WIF private key] (may specify multiple times)");
|
||||
cli.add_options()("bitcoin-node-ip", bpo::value<string>()->default_value("127.0.0.1"), "IP address of Bitcoin node");
|
||||
cli.add_options()("bitcoin-node-zmq-port", bpo::value<uint32_t>()->default_value(11111), "ZMQ port of Bitcoin node");
|
||||
cli.add_options()("bitcoin-node-rpc-port", bpo::value<uint32_t>()->default_value(8332), "RPC port of Bitcoin node");
|
||||
cli.add_options()("bitcoin-node-rpc-user", bpo::value<string>()->default_value("1"), "Bitcoin RPC user");
|
||||
cli.add_options()("bitcoin-node-rpc-password", bpo::value<string>()->default_value("1"), "Bitcoin RPC password");
|
||||
cli.add_options()("bitcoin-wallet", bpo::value<string>(), "Bitcoin wallet");
|
||||
cli.add_options()("bitcoin-wallet-password", bpo::value<string>(), "Bitcoin wallet password");
|
||||
cli.add_options()("bitcoin-private-key", bpo::value<vector<string>>()->composing()->multitoken()->DEFAULT_VALUE_VECTOR(std::make_pair("02d0f137e717fb3aab7aff99904001d49a0a636c5e1342f8927a4ba2eaee8e9772", "cVN31uC9sTEr392DLVUEjrtMgLA8Yb3fpYmTRj7bomTm6nn2ANPr")),
|
||||
"Tuple of [Bitcoin public key, Bitcoin private key] (may specify multiple times)");
|
||||
cfg.add(cli);
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::plugin_initialize(const boost::program_options::variables_map &opt) {
|
||||
options = opt;
|
||||
config_ready_son = (options.count("son-id") || options.count("son-ids")) && options.count("peerplays-private-key");
|
||||
if (config_ready_son) {
|
||||
LOAD_VALUE_SET(options, "son-id", sons, chain::son_id_type)
|
||||
if (options.count("son-ids"))
|
||||
boost::insert(sons, fc::json::from_string(options.at("son-ids").as<string>()).as<vector<chain::son_id_type>>(5));
|
||||
config_ready_son = config_ready_son && !sons.empty();
|
||||
|
||||
#ifndef ENABLE_MULTIPLE_SONS
|
||||
if (sons.size() > 1) {
|
||||
FC_THROW("Invalid configuration, multiple SON IDs provided");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (options.count("peerplays-private-key")) {
|
||||
const std::vector<std::string> key_id_to_wif_pair_strings = options["peerplays-private-key"].as<std::vector<std::string>>();
|
||||
for (const std::string &key_id_to_wif_pair_string : key_id_to_wif_pair_strings) {
|
||||
auto key_id_to_wif_pair = graphene::app::dejsonify<std::pair<chain::public_key_type, std::string>>(key_id_to_wif_pair_string, 5);
|
||||
ilog("Public Key: ${public}", ("public", key_id_to_wif_pair.first));
|
||||
fc::optional<fc::ecc::private_key> private_key = graphene::utilities::wif_to_key(key_id_to_wif_pair.second);
|
||||
if (!private_key) {
|
||||
// the key isn't in WIF format; see if they are still passing the old native private key format. This is
|
||||
// just here to ease the transition, can be removed soon
|
||||
try {
|
||||
private_key = fc::variant(key_id_to_wif_pair.second, 2).as<fc::ecc::private_key>(1);
|
||||
} catch (const fc::exception &) {
|
||||
FC_THROW("Invalid WIF-format private key ${key_string}", ("key_string", key_id_to_wif_pair.second));
|
||||
}
|
||||
}
|
||||
private_keys[key_id_to_wif_pair.first] = *private_key;
|
||||
}
|
||||
config_ready_son = config_ready_son && !private_keys.empty();
|
||||
}
|
||||
}
|
||||
if (!config_ready_son) {
|
||||
wlog("Haven't set up SON parameters");
|
||||
throw;
|
||||
}
|
||||
|
||||
config_ready_bitcoin = options.count("bitcoin-node-ip") &&
|
||||
options.count("bitcoin-node-zmq-port") && options.count("bitcoin-node-rpc-port") &&
|
||||
options.count("bitcoin-node-rpc-user") && options.count("bitcoin-node-rpc-password") &&
|
||||
/*options.count( "bitcoin-wallet" ) && options.count( "bitcoin-wallet-password" ) &&*/
|
||||
options.count("bitcoin-private-key");
|
||||
if (!config_ready_bitcoin) {
|
||||
wlog("Haven't set up Bitcoin sidechain parameters");
|
||||
}
|
||||
|
||||
//config_ready_ethereum = options.count( "ethereum-node-ip" ) &&
|
||||
// options.count( "ethereum-address" ) && options.count( "ethereum-public-key" ) && options.count( "ethereum-private-key" );
|
||||
//if (!config_ready_ethereum) {
|
||||
// wlog("Haven't set up Ethereum sidechain parameters");
|
||||
//}
|
||||
|
||||
config_ready_peerplays = true;
|
||||
if (!config_ready_peerplays) {
|
||||
wlog("Haven't set up Peerplays sidechain parameters");
|
||||
}
|
||||
|
||||
if (!(config_ready_bitcoin /*&& config_ready_ethereum*/ && config_ready_peerplays)) {
|
||||
wlog("Haven't set up any sidechain parameters");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::plugin_startup() {
|
||||
|
||||
if (config_ready_son) {
|
||||
ilog("Starting ${n} SON instances", ("n", sons.size()));
|
||||
|
||||
schedule_heartbeat_loop();
|
||||
} else {
|
||||
elog("No sons configured! Please add SON IDs and private keys to configuration.");
|
||||
}
|
||||
|
||||
net_manager = std::unique_ptr<sidechain_net_manager>(new sidechain_net_manager(plugin));
|
||||
|
||||
if (config_ready_bitcoin) {
|
||||
net_manager->create_handler(sidechain_type::bitcoin, options);
|
||||
ilog("Bitcoin sidechain handler running");
|
||||
}
|
||||
|
||||
//if (config_ready_ethereum) {
|
||||
// net_manager->create_handler(sidechain_type::ethereum, options);
|
||||
// ilog("Ethereum sidechain handler running");
|
||||
//}
|
||||
|
||||
if (config_ready_peerplays) {
|
||||
net_manager->create_handler(sidechain_type::peerplays, options);
|
||||
ilog("Peerplays sidechain handler running");
|
||||
}
|
||||
|
||||
plugin.database().applied_block.connect([&](const signed_block &b) {
|
||||
on_applied_block(b);
|
||||
});
|
||||
}
|
||||
|
||||
std::set<chain::son_id_type> &peerplays_sidechain_plugin_impl::get_sons() {
|
||||
return sons;
|
||||
}
|
||||
|
||||
const son_id_type peerplays_sidechain_plugin_impl::get_current_son_id() {
|
||||
return current_son_id;
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin_impl::get_current_son_object() {
|
||||
return get_son_object(current_son_id);
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin_impl::get_son_object(son_id_type son_id) {
|
||||
const auto &idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find(son_id);
|
||||
if (son_obj == idx.end())
|
||||
return {};
|
||||
return *son_obj;
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_active_son(son_id_type son_id) {
|
||||
const auto &idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find(son_id);
|
||||
if (son_obj == idx.end())
|
||||
return false;
|
||||
|
||||
const chain::global_property_object &gpo = plugin.database().get_global_properties();
|
||||
vector<son_id_type> active_son_ids;
|
||||
active_son_ids.reserve(gpo.active_sons.size());
|
||||
std::transform(gpo.active_sons.begin(), gpo.active_sons.end(),
|
||||
std::inserter(active_son_ids, active_son_ids.end()),
|
||||
[](const son_info &swi) {
|
||||
return swi.son_id;
|
||||
});
|
||||
|
||||
auto it = std::find(active_son_ids.begin(), active_son_ids.end(), son_id);
|
||||
|
||||
return (it != active_son_ids.end());
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_son_deregistered(son_id_type son_id) {
|
||||
const auto &idx = plugin.database().get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
auto son_obj = idx.find(son_id);
|
||||
if (son_obj == idx.end())
|
||||
return true;
|
||||
|
||||
if(son_obj->status == chain::son_status::deregistered) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_son_deregister_op_valid(const chain::operation &op) {
|
||||
son_deregister_operation deregister_op = op.get<son_deregister_operation>();
|
||||
return plugin.database().is_son_dereg_valid(deregister_op.son_id);
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_son_down_op_valid(const chain::operation &op) {
|
||||
chain::database &d = plugin.database();
|
||||
const chain::global_property_object &gpo = d.get_global_properties();
|
||||
const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties();
|
||||
const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
son_report_down_operation down_op = op.get<son_report_down_operation>();
|
||||
auto son_obj = idx.find(down_op.son_id);
|
||||
if (son_obj == idx.end()) {
|
||||
return false;
|
||||
}
|
||||
auto stats = son_obj->statistics(d);
|
||||
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time);
|
||||
int64_t down_threshold = gpo.parameters.son_down_time();
|
||||
if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) &&
|
||||
((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(son_id_type son_id) {
|
||||
return get_private_key(get_son_object(son_id).signing_key);
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin_impl::get_private_key(chain::public_key_type public_key) {
|
||||
auto private_key_itr = private_keys.find(public_key);
|
||||
if (private_key_itr != private_keys.end()) {
|
||||
return private_key_itr->second;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::schedule_heartbeat_loop() {
|
||||
fc::time_point now = fc::time_point::now();
|
||||
int64_t time_to_next_heartbeat = plugin.database().get_global_properties().parameters.son_heartbeat_frequency();
|
||||
|
||||
fc::time_point next_wakeup(now + fc::seconds(time_to_next_heartbeat));
|
||||
|
||||
_heartbeat_task = fc::schedule([this] {
|
||||
heartbeat_loop();
|
||||
},
|
||||
next_wakeup, "SON Heartbeat Production");
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::heartbeat_loop() {
|
||||
schedule_heartbeat_loop();
|
||||
chain::database &d = plugin.database();
|
||||
|
||||
for (son_id_type son_id : sons) {
|
||||
if (is_active_son(son_id) || get_son_object(son_id).status == chain::son_status::in_maintenance) {
|
||||
|
||||
ilog("Sending heartbeat for SON ${son}", ("son", son_id));
|
||||
chain::son_heartbeat_operation op;
|
||||
op.owner_account = get_son_object(son_id).son_account;
|
||||
op.son_id = son_id;
|
||||
op.ts = fc::time_point::now() + fc::seconds(0);
|
||||
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(son_id), op);
|
||||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
trx.validate();
|
||||
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending heartbeat failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
fut.wait(fc::seconds(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::schedule_son_processing() {
|
||||
fc::time_point now = fc::time_point::now();
|
||||
int64_t time_to_next_son_processing = 500000;
|
||||
|
||||
fc::time_point next_wakeup(now + fc::microseconds(time_to_next_son_processing));
|
||||
|
||||
_son_processing_task = fc::schedule([this] {
|
||||
son_processing();
|
||||
},
|
||||
next_wakeup, "SON Processing");
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::son_processing() {
|
||||
if (plugin.database().get_global_properties().active_sons.size() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
fc::time_point now_fine = fc::time_point::now();
|
||||
fc::time_point_sec now = now_fine + fc::microseconds(500000);
|
||||
if (plugin.database().get_slot_time(1) < now) {
|
||||
return; // Not synced
|
||||
}
|
||||
|
||||
chain::son_id_type scheduled_son_id = plugin.database().get_scheduled_son(1);
|
||||
ilog("Scheduled SON: ${scheduled_son_id} Now: ${now} ",
|
||||
("scheduled_son_id", scheduled_son_id)("now", now));
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_son_deregistered(son_id)) {
|
||||
continue;
|
||||
}
|
||||
current_son_id = son_id;
|
||||
|
||||
// These tasks are executed by
|
||||
// - All active SONs, no matter if scheduled
|
||||
// - All previously active SONs
|
||||
approve_proposals();
|
||||
process_proposals();
|
||||
process_sidechain_transactions();
|
||||
|
||||
if (plugin.is_active_son(son_id)) {
|
||||
// Tasks that are executed by scheduled and active SON only
|
||||
if (current_son_id == scheduled_son_id) {
|
||||
|
||||
create_son_down_proposals();
|
||||
|
||||
create_son_deregister_proposals();
|
||||
|
||||
process_active_sons_change();
|
||||
|
||||
create_deposit_addresses();
|
||||
|
||||
process_deposits();
|
||||
|
||||
process_withdrawals();
|
||||
|
||||
process_sidechain_transactions();
|
||||
|
||||
send_sidechain_transactions();
|
||||
|
||||
settle_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin_impl::is_valid_son_proposal(const chain::proposal_object &proposal) {
|
||||
if (proposal.proposed_transaction.operations.size() == 1) {
|
||||
int32_t op_idx_0 = proposal.proposed_transaction.operations[0].which();
|
||||
chain::operation op = proposal.proposed_transaction.operations[0];
|
||||
|
||||
if (op_idx_0 == chain::operation::tag<chain::son_report_down_operation>::value) {
|
||||
return is_son_down_op_valid(op);
|
||||
}
|
||||
|
||||
if (op_idx_0 == chain::operation::tag<chain::son_deregister_operation>::value) {
|
||||
return is_son_deregister_op_valid(op);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::approve_proposals() {
|
||||
|
||||
auto check_approve_proposal = [&](const chain::son_id_type &son_id, const chain::proposal_object &proposal) {
|
||||
if (!is_valid_son_proposal(proposal)) {
|
||||
return;
|
||||
}
|
||||
ilog("Sending approval for ${p} from ${s}", ("p", proposal.id)("s", son_id));
|
||||
chain::proposal_update_operation puo;
|
||||
puo.fee_paying_account = get_son_object(son_id).son_account;
|
||||
puo.proposal = proposal.id;
|
||||
puo.active_approvals_to_add = {get_son_object(son_id).son_account};
|
||||
chain::signed_transaction trx = plugin.database().create_signed_transaction(plugin.get_private_key(son_id), puo);
|
||||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
trx.validate();
|
||||
plugin.database().push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending approval failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
fut.wait(fc::seconds(10));
|
||||
};
|
||||
|
||||
const auto &idx = plugin.database().get_index_type<proposal_index>().indices().get<by_id>();
|
||||
vector<proposal_id_type> proposals;
|
||||
for (const auto &proposal : idx) {
|
||||
proposals.push_back(proposal.id);
|
||||
}
|
||||
|
||||
for (const auto proposal_id : proposals) {
|
||||
|
||||
const object *obj = plugin.database().find_object(proposal_id);
|
||||
const chain::proposal_object *proposal_ptr = dynamic_cast<const chain::proposal_object *>(obj);
|
||||
if (proposal_ptr == nullptr) {
|
||||
continue;
|
||||
}
|
||||
const proposal_object proposal = *proposal_ptr;
|
||||
|
||||
if (proposal.available_active_approvals.find(get_current_son_object().son_account) != proposal.available_active_approvals.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
check_approve_proposal(get_current_son_id(), proposal);
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::create_son_down_proposals() {
|
||||
auto create_son_down_proposal = [&](chain::son_id_type son_id, fc::time_point_sec last_active_ts) {
|
||||
chain::database &d = plugin.database();
|
||||
const chain::global_property_object &gpo = d.get_global_properties();
|
||||
|
||||
chain::son_report_down_operation son_down_op;
|
||||
son_down_op.payer = d.get_global_properties().parameters.son_account();
|
||||
son_down_op.son_id = son_id;
|
||||
son_down_op.down_ts = last_active_ts;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = get_current_son_object().son_account;
|
||||
proposal_op.proposed_ops.emplace_back(op_wrapper(son_down_op));
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(d.head_block_time().sec_since_epoch() + lifetime);
|
||||
return proposal_op;
|
||||
};
|
||||
|
||||
chain::database &d = plugin.database();
|
||||
const chain::global_property_object &gpo = d.get_global_properties();
|
||||
const chain::dynamic_global_property_object &dgpo = d.get_dynamic_global_properties();
|
||||
const auto &idx = d.get_index_type<chain::son_index>().indices().get<by_id>();
|
||||
std::set<son_id_type> sons_being_reported_down = d.get_sons_being_reported_down();
|
||||
chain::son_id_type my_son_id = get_current_son_id();
|
||||
for (auto son_inf : gpo.active_sons) {
|
||||
if (my_son_id == son_inf.son_id || (sons_being_reported_down.find(son_inf.son_id) != sons_being_reported_down.end())) {
|
||||
continue;
|
||||
}
|
||||
auto son_obj = idx.find(son_inf.son_id);
|
||||
auto stats = son_obj->statistics(d);
|
||||
fc::time_point_sec last_maintenance_time = dgpo.next_maintenance_time - gpo.parameters.maintenance_interval;
|
||||
fc::time_point_sec last_active_ts = ((stats.last_active_timestamp > last_maintenance_time) ? stats.last_active_timestamp : last_maintenance_time);
|
||||
int64_t down_threshold = gpo.parameters.son_down_time();
|
||||
if (((son_obj->status == chain::son_status::active) || (son_obj->status == chain::son_status::request_maintenance)) &&
|
||||
((fc::time_point::now() - last_active_ts) > fc::seconds(down_threshold))) {
|
||||
ilog("Sending son down proposal for ${t} from ${s}", ("t", std::string(object_id_type(son_obj->id)))("s", std::string(object_id_type(my_son_id))));
|
||||
chain::proposal_create_operation op = create_son_down_proposal(son_inf.son_id, last_active_ts);
|
||||
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), op);
|
||||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
trx.validate();
|
||||
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending son down proposal failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
fut.wait(fc::seconds(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::create_son_deregister_proposals() {
|
||||
chain::database &d = plugin.database();
|
||||
std::set<son_id_type> sons_to_be_dereg = d.get_sons_to_be_deregistered();
|
||||
chain::son_id_type my_son_id = get_current_son_id();
|
||||
|
||||
if (sons_to_be_dereg.size() > 0) {
|
||||
// We shouldn't raise proposals for the SONs for which a de-reg
|
||||
// proposal is already raised.
|
||||
std::set<son_id_type> sons_being_dereg = d.get_sons_being_deregistered();
|
||||
for (auto &son : sons_to_be_dereg) {
|
||||
// New SON to be deregistered
|
||||
if (sons_being_dereg.find(son) == sons_being_dereg.end() && my_son_id != son) {
|
||||
// Creating the de-reg proposal
|
||||
auto op = d.create_son_deregister_proposal(son, get_son_object(my_son_id).son_account);
|
||||
if (op.valid()) {
|
||||
// Signing and pushing into the txs to be included in the block
|
||||
ilog("Sending son deregister proposal for ${p} from ${s}", ("p", son)("s", my_son_id));
|
||||
chain::signed_transaction trx = d.create_signed_transaction(plugin.get_private_key(get_son_object(my_son_id).signing_key), *op);
|
||||
fc::future<bool> fut = fc::async([&]() {
|
||||
try {
|
||||
trx.validate();
|
||||
d.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending son deregister proposal failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
fut.wait(fc::seconds(10));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_proposals() {
|
||||
net_manager->process_proposals();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_active_sons_change() {
|
||||
net_manager->process_active_sons_change();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::create_deposit_addresses() {
|
||||
net_manager->create_deposit_addresses();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_deposits() {
|
||||
net_manager->process_deposits();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_withdrawals() {
|
||||
net_manager->process_withdrawals();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::process_sidechain_transactions() {
|
||||
net_manager->process_sidechain_transactions();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::send_sidechain_transactions() {
|
||||
net_manager->send_sidechain_transactions();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::settle_sidechain_transactions() {
|
||||
net_manager->settle_sidechain_transactions();
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin_impl::on_applied_block(const signed_block &b) {
|
||||
if (first_block_skipped) {
|
||||
schedule_son_processing();
|
||||
} else {
|
||||
first_block_skipped = true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
peerplays_sidechain_plugin::peerplays_sidechain_plugin() :
|
||||
my(new detail::peerplays_sidechain_plugin_impl(*this)) {
|
||||
}
|
||||
|
||||
peerplays_sidechain_plugin::~peerplays_sidechain_plugin() {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string peerplays_sidechain_plugin::plugin_name() const {
|
||||
return "peerplays_sidechain";
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin::plugin_set_program_options(
|
||||
boost::program_options::options_description &cli,
|
||||
boost::program_options::options_description &cfg) {
|
||||
my->plugin_set_program_options(cli, cfg);
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin::plugin_initialize(const boost::program_options::variables_map &options) {
|
||||
ilog("peerplays sidechain plugin: plugin_initialize() begin");
|
||||
my->plugin_initialize(options);
|
||||
ilog("peerplays sidechain plugin: plugin_initialize() end");
|
||||
}
|
||||
|
||||
void peerplays_sidechain_plugin::plugin_startup() {
|
||||
ilog("peerplays sidechain plugin: plugin_startup() begin");
|
||||
my->plugin_startup();
|
||||
ilog("peerplays sidechain plugin: plugin_startup() end");
|
||||
}
|
||||
|
||||
std::set<chain::son_id_type> &peerplays_sidechain_plugin::get_sons() {
|
||||
return my->get_sons();
|
||||
}
|
||||
|
||||
const son_id_type peerplays_sidechain_plugin::get_current_son_id() {
|
||||
return my->get_current_son_id();
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin::get_current_son_object() {
|
||||
return my->get_current_son_object();
|
||||
}
|
||||
|
||||
const son_object peerplays_sidechain_plugin::get_son_object(son_id_type son_id) {
|
||||
return my->get_son_object(son_id);
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin::is_active_son(son_id_type son_id) {
|
||||
return my->is_active_son(son_id);
|
||||
}
|
||||
|
||||
bool peerplays_sidechain_plugin::is_son_deregistered(son_id_type son_id) {
|
||||
return my->is_son_deregistered(son_id);
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(son_id_type son_id) {
|
||||
return my->get_private_key(son_id);
|
||||
}
|
||||
|
||||
fc::ecc::private_key peerplays_sidechain_plugin::get_private_key(chain::public_key_type public_key) {
|
||||
return my->get_private_key(public_key);
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
569
libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp
Normal file
569
libraries/plugins/peerplays_sidechain/sidechain_net_handler.cpp
Normal file
|
|
@ -0,0 +1,569 @@
|
|||
#include <graphene/peerplays_sidechain/sidechain_net_handler.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/smart_ref_fwd.hpp>
|
||||
#include <graphene/chain/chain_property_object.hpp>
|
||||
#include <graphene/chain/proposal_object.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_handler::sidechain_net_handler(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||
plugin(_plugin),
|
||||
database(_plugin.database()) {
|
||||
}
|
||||
|
||||
sidechain_net_handler::~sidechain_net_handler() {
|
||||
}
|
||||
|
||||
sidechain_type sidechain_net_handler::get_sidechain() {
|
||||
return sidechain;
|
||||
}
|
||||
|
||||
std::vector<std::string> sidechain_net_handler::get_sidechain_deposit_addresses() {
|
||||
std::vector<std::string> result;
|
||||
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
|
||||
const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get<by_sidechain>();
|
||||
const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain);
|
||||
std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second,
|
||||
[&result](const sidechain_address_object &sao) {
|
||||
result.push_back(sao.deposit_address);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> sidechain_net_handler::get_sidechain_withdraw_addresses() {
|
||||
std::vector<std::string> result;
|
||||
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
|
||||
const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get<by_sidechain>();
|
||||
const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain);
|
||||
std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second,
|
||||
[&result](const sidechain_address_object &sao) {
|
||||
result.push_back(sao.withdraw_address);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler::get_private_key(std::string public_key) {
|
||||
auto private_key_itr = private_keys.find(public_key);
|
||||
if (private_key_itr != private_keys.end()) {
|
||||
return private_key_itr->second;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
bool sidechain_net_handler::proposal_exists(int32_t operation_tag, const object_id_type &object_id, boost::optional<chain::operation &> proposal_op) {
|
||||
|
||||
bool result = false;
|
||||
|
||||
const auto &idx = database.get_index_type<proposal_index>().indices().get<by_id>();
|
||||
vector<proposal_id_type> proposals;
|
||||
for (const auto &proposal : idx) {
|
||||
proposals.push_back(proposal.id);
|
||||
}
|
||||
|
||||
for (const auto proposal_id : proposals) {
|
||||
const auto &idx = database.get_index_type<proposal_index>().indices().get<by_id>();
|
||||
const auto po = idx.find(proposal_id);
|
||||
if (po != idx.end()) {
|
||||
|
||||
int32_t op_idx_0 = -1;
|
||||
chain::operation op_obj_idx_0;
|
||||
|
||||
if (po->proposed_transaction.operations.size() >= 1) {
|
||||
op_idx_0 = po->proposed_transaction.operations[0].which();
|
||||
op_obj_idx_0 = po->proposed_transaction.operations[0];
|
||||
}
|
||||
|
||||
switch (op_idx_0) {
|
||||
case chain::operation::tag<chain::son_wallet_update_operation>::value: {
|
||||
result = (op_obj_idx_0.get<son_wallet_update_operation>().son_wallet_id == object_id);
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::son_wallet_deposit_process_operation>::value: {
|
||||
result = (op_obj_idx_0.get<son_wallet_deposit_process_operation>().son_wallet_deposit_id == object_id);
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value: {
|
||||
result = (op_obj_idx_0.get<son_wallet_withdraw_process_operation>().son_wallet_withdraw_id == object_id);
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::sidechain_transaction_sign_operation>::value: {
|
||||
if (proposal_op) {
|
||||
chain::operation proposal_op_obj_0 = proposal_op.get();
|
||||
result = ((proposal_op_obj_0.get<sidechain_transaction_sign_operation>().sidechain_transaction_id == op_obj_idx_0.get<sidechain_transaction_sign_operation>().sidechain_transaction_id) &&
|
||||
(proposal_op_obj_0.get<sidechain_transaction_sign_operation>().signer == op_obj_idx_0.get<sidechain_transaction_sign_operation>().signer) &&
|
||||
(proposal_op_obj_0.get<sidechain_transaction_sign_operation>().signature == op_obj_idx_0.get<sidechain_transaction_sign_operation>().signature));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool sidechain_net_handler::signer_expected(const sidechain_transaction_object &sto, son_id_type signer) {
|
||||
bool expected = false;
|
||||
for (auto signature : sto.signatures) {
|
||||
if (signature.first == signer) {
|
||||
expected = signature.second.empty();
|
||||
}
|
||||
}
|
||||
return expected;
|
||||
}
|
||||
|
||||
bool sidechain_net_handler::approve_proposal(const proposal_id_type &proposal_id, const son_id_type &son_id) {
|
||||
|
||||
proposal_update_operation op;
|
||||
op.fee_paying_account = plugin.get_son_object(son_id).son_account;
|
||||
op.proposal = proposal_id;
|
||||
op.active_approvals_to_add = {plugin.get_son_object(son_id).son_account};
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending approval from ${son_id} for proposal ${proposal_id} failed with exception ${e}",
|
||||
("son_id", son_id)("proposal_id", proposal_id)("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_handler::sidechain_event_data_received(const sidechain_event_data &sed) {
|
||||
ilog("sidechain_event_data:");
|
||||
ilog(" timestamp: ${timestamp}", ("timestamp", sed.timestamp));
|
||||
ilog(" block_num: ${block_num}", ("block_num", sed.block_num));
|
||||
ilog(" sidechain: ${sidechain}", ("sidechain", sed.sidechain));
|
||||
ilog(" sidechain_uid: ${uid}", ("uid", sed.sidechain_uid));
|
||||
ilog(" sidechain_transaction_id: ${transaction_id}", ("transaction_id", sed.sidechain_transaction_id));
|
||||
ilog(" sidechain_from: ${from}", ("from", sed.sidechain_from));
|
||||
ilog(" sidechain_to: ${to}", ("to", sed.sidechain_to));
|
||||
ilog(" sidechain_currency: ${currency}", ("currency", sed.sidechain_currency));
|
||||
ilog(" sidechain_amount: ${amount}", ("amount", sed.sidechain_amount));
|
||||
ilog(" peerplays_from: ${peerplays_from}", ("peerplays_from", sed.peerplays_from));
|
||||
ilog(" peerplays_to: ${peerplays_to}", ("peerplays_to", sed.peerplays_to));
|
||||
ilog(" peerplays_asset: ${peerplays_asset}", ("peerplays_asset", sed.peerplays_asset));
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
asset_id_type btc_asset_id = database.get_global_properties().parameters.btc_asset();
|
||||
std::string btc_asset_id_str = fc::to_string(btc_asset_id.space_id) + "." +
|
||||
fc::to_string(btc_asset_id.type_id) + "." +
|
||||
fc::to_string((uint64_t)btc_asset_id.instance);
|
||||
|
||||
#ifdef ENABLE_PEERPLAYS_ASSET_DEPOSITS
|
||||
// Accepts BTC and peerplays asset deposits
|
||||
bool deposit_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) != 0));
|
||||
bool withdraw_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0));
|
||||
#else
|
||||
// Accepts BTC deposits only
|
||||
bool deposit_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare("BTC") == 0));
|
||||
bool withdraw_condition = ((sed.peerplays_to == gpo.parameters.son_account()) && (sed.sidechain_currency.compare(btc_asset_id_str) == 0));
|
||||
#endif
|
||||
|
||||
// Deposit request
|
||||
if (deposit_condition) {
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_active_son(son_id)) {
|
||||
|
||||
son_wallet_deposit_create_operation op;
|
||||
op.payer = plugin.get_son_object(son_id).son_account;
|
||||
op.son_id = son_id;
|
||||
op.timestamp = sed.timestamp;
|
||||
op.block_num = sed.block_num;
|
||||
op.sidechain = sed.sidechain;
|
||||
op.sidechain_uid = sed.sidechain_uid;
|
||||
op.sidechain_transaction_id = sed.sidechain_transaction_id;
|
||||
op.sidechain_from = sed.sidechain_from;
|
||||
op.sidechain_to = sed.sidechain_to;
|
||||
op.sidechain_currency = sed.sidechain_currency;
|
||||
op.sidechain_amount = sed.sidechain_amount;
|
||||
op.peerplays_from = sed.peerplays_from;
|
||||
op.peerplays_to = sed.peerplays_to;
|
||||
op.peerplays_asset = sed.peerplays_asset;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending son wallet deposit create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Withdrawal request
|
||||
if (withdraw_condition) {
|
||||
// BTC Payout only (for now)
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_account_and_sidechain_and_expires>();
|
||||
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sed.peerplays_from, sidechain_type::bitcoin, time_point_sec::maximum()));
|
||||
if (addr_itr == sidechain_addresses_idx.end())
|
||||
return;
|
||||
|
||||
for (son_id_type son_id : plugin.get_sons()) {
|
||||
if (plugin.is_active_son(son_id)) {
|
||||
|
||||
son_wallet_withdraw_create_operation op;
|
||||
op.payer = plugin.get_son_object(son_id).son_account;
|
||||
op.son_id = son_id;
|
||||
op.timestamp = sed.timestamp;
|
||||
op.block_num = sed.block_num;
|
||||
op.sidechain = sed.sidechain;
|
||||
op.peerplays_uid = sed.sidechain_uid;
|
||||
op.peerplays_transaction_id = sed.sidechain_transaction_id;
|
||||
op.peerplays_from = sed.peerplays_from;
|
||||
op.peerplays_asset = sed.peerplays_asset;
|
||||
// BTC payout only (for now)
|
||||
op.withdraw_sidechain = sidechain_type::bitcoin;
|
||||
op.withdraw_address = addr_itr->withdraw_address;
|
||||
op.withdraw_currency = "BTC";
|
||||
price btc_price = database.get<asset_object>(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate;
|
||||
op.withdraw_amount = sed.peerplays_asset.amount * btc_price.quote.amount / btc_price.base.amount;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(son_id), op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending son wallet withdraw create operation by ${son} failed with exception ${e}", ("son", son_id)("e", e.what()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_proposals() {
|
||||
const auto &idx = database.get_index_type<proposal_index>().indices().get<by_id>();
|
||||
vector<proposal_id_type> proposals;
|
||||
for (const auto &proposal : idx) {
|
||||
proposals.push_back(proposal.id);
|
||||
}
|
||||
|
||||
for (const auto proposal_id : proposals) {
|
||||
const auto &idx = database.get_index_type<proposal_index>().indices().get<by_id>();
|
||||
const auto po = idx.find(proposal_id);
|
||||
if (po != idx.end()) {
|
||||
|
||||
if (po->available_active_approvals.find(plugin.get_current_son_object().son_account) != po->available_active_approvals.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool should_process = false;
|
||||
|
||||
int32_t op_idx_0 = -1;
|
||||
chain::operation op_obj_idx_0;
|
||||
|
||||
if (po->proposed_transaction.operations.size() >= 1) {
|
||||
op_idx_0 = po->proposed_transaction.operations[0].which();
|
||||
op_obj_idx_0 = po->proposed_transaction.operations[0];
|
||||
}
|
||||
|
||||
int32_t op_idx_1 = -1;
|
||||
chain::operation op_obj_idx_1;
|
||||
|
||||
if (po->proposed_transaction.operations.size() >= 2) {
|
||||
op_idx_1 = po->proposed_transaction.operations[1].which();
|
||||
op_obj_idx_1 = po->proposed_transaction.operations[1];
|
||||
}
|
||||
|
||||
switch (op_idx_0) {
|
||||
case chain::operation::tag<chain::son_wallet_update_operation>::value: {
|
||||
should_process = (op_obj_idx_0.get<son_wallet_update_operation>().sidechain == sidechain);
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::son_wallet_deposit_process_operation>::value: {
|
||||
son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get<son_wallet_deposit_process_operation>().son_wallet_deposit_id;
|
||||
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
|
||||
const auto swdo = idx.find(swdo_id);
|
||||
if (swdo != idx.end()) {
|
||||
should_process = (swdo->sidechain == sidechain);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value: {
|
||||
son_wallet_withdraw_id_type swwo_id = op_obj_idx_0.get<son_wallet_withdraw_process_operation>().son_wallet_withdraw_id;
|
||||
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_id>();
|
||||
const auto swwo = idx.find(swwo_id);
|
||||
if (swwo != idx.end()) {
|
||||
should_process = (swwo->withdraw_sidechain == sidechain);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::sidechain_transaction_sign_operation>::value: {
|
||||
sidechain_transaction_id_type st_id = op_obj_idx_0.get<sidechain_transaction_sign_operation>().sidechain_transaction_id;
|
||||
son_id_type signer = op_obj_idx_0.get<sidechain_transaction_sign_operation>().signer;
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
const auto sto = idx.find(st_id);
|
||||
if (sto != idx.end()) {
|
||||
should_process = ((sto->sidechain == sidechain) && (sto->status == sidechain_transaction_status::valid) && signer_expected(*sto, signer));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::sidechain_transaction_settle_operation>::value: {
|
||||
sidechain_transaction_id_type st_id = op_obj_idx_0.get<sidechain_transaction_settle_operation>().sidechain_transaction_id;
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_id>();
|
||||
const auto sto = idx.find(st_id);
|
||||
if (sto != idx.end()) {
|
||||
should_process = (sto->sidechain == sidechain);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
should_process = false;
|
||||
elog("==================================================");
|
||||
elog("Proposal not processed ${po}", ("po", *po));
|
||||
elog("==================================================");
|
||||
}
|
||||
|
||||
if (should_process) {
|
||||
bool should_approve = process_proposal(*po);
|
||||
if (should_approve) {
|
||||
approve_proposal(po->id, plugin.get_current_son_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_active_sons_change() {
|
||||
process_primary_wallet();
|
||||
}
|
||||
|
||||
void sidechain_net_handler::create_deposit_addresses() {
|
||||
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) {
|
||||
return;
|
||||
}
|
||||
process_sidechain_addresses();
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_deposits() {
|
||||
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_sidechain_and_confirmed_and_processed>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_deposit_object &swdo) {
|
||||
if (swdo.id == object_id_type(0, 0, 0)) {
|
||||
return;
|
||||
}
|
||||
//Ignore the deposits which are not valid anymore, considered refunds.
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>().indices().get<by_sidechain_and_deposit_address_and_expires>();
|
||||
const auto &addr_itr = sidechain_addresses_idx.find(std::make_tuple(sidechain, swdo.sidechain_to, time_point_sec::maximum()));
|
||||
if (addr_itr == sidechain_addresses_idx.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ilog("Deposit to process: ${swdo}", ("swdo", swdo));
|
||||
|
||||
bool process_deposit_result = process_deposit(swdo);
|
||||
|
||||
if (!process_deposit_result) {
|
||||
wlog("Deposit not processed: ${swdo}", ("swdo", swdo));
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_withdrawals() {
|
||||
if (database.get_global_properties().active_sons.size() < database.get_chain_properties().immutable_parameters.min_son_count) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &idx = database.get_index_type<son_wallet_withdraw_index>().indices().get<by_withdraw_sidechain_and_confirmed_and_processed>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, true, false));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const son_wallet_withdraw_object &swwo) {
|
||||
if (swwo.id == object_id_type(0, 0, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ilog("Withdraw to process: ${swwo}", ("swwo", swwo));
|
||||
|
||||
bool process_withdrawal_result = process_withdrawal(swwo);
|
||||
|
||||
if (!process_withdrawal_result) {
|
||||
wlog("Withdraw not processed: ${swwo}", ("swwo", swwo));
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sidechain_net_handler::process_sidechain_transactions() {
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::valid));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
if ((sto.id == object_id_type(0, 0, 0)) || !signer_expected(sto, plugin.get_current_son_id())) {
|
||||
return;
|
||||
}
|
||||
|
||||
ilog("Sidechain transaction to process: ${sto}", ("sto", sto.id));
|
||||
|
||||
std::string processed_sidechain_tx = process_sidechain_transaction(sto);
|
||||
|
||||
if (processed_sidechain_tx.empty()) {
|
||||
wlog("Sidechain transaction not processed: ${sto}", ("sto", sto.id));
|
||||
return;
|
||||
}
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
sidechain_transaction_sign_operation sts_op;
|
||||
sts_op.signer = plugin.get_current_son_id();
|
||||
sts_op.payer = gpo.parameters.son_account();
|
||||
sts_op.sidechain_transaction_id = sto.id;
|
||||
sts_op.signature = processed_sidechain_tx;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
proposal_op.proposed_ops.emplace_back(sts_op);
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::sidechain_transaction_sign_operation>::value, sto.id, proposal_op.proposed_ops[0].op)) {
|
||||
return;
|
||||
}
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for sidechain transaction sign operation failed with exception ${e}", ("e", e.what()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sidechain_net_handler::send_sidechain_transactions() {
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::complete));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
if (sto.id == object_id_type(0, 0, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ilog("Sidechain transaction to send: ${sto}", ("sto", sto.id));
|
||||
|
||||
std::string sidechain_transaction = send_sidechain_transaction(sto);
|
||||
|
||||
if (sidechain_transaction.empty()) {
|
||||
wlog("Sidechain transaction not sent: ${sto}", ("sto", sto.id));
|
||||
return;
|
||||
}
|
||||
|
||||
sidechain_transaction_send_operation sts_op;
|
||||
sts_op.payer = plugin.get_current_son_object().son_account;
|
||||
sts_op.sidechain_transaction_id = sto.id;
|
||||
sts_op.sidechain_transaction = sidechain_transaction;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), sts_op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for sidechain transaction send operation failed with exception ${e}", ("e", e.what()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void sidechain_net_handler::settle_sidechain_transactions() {
|
||||
const auto &idx = database.get_index_type<sidechain_transaction_index>().indices().get<by_sidechain_and_status>();
|
||||
const auto &idx_range = idx.equal_range(std::make_tuple(sidechain, sidechain_transaction_status::sent));
|
||||
|
||||
std::for_each(idx_range.first, idx_range.second, [&](const sidechain_transaction_object &sto) {
|
||||
if (sto.id == object_id_type(0, 0, 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (proposal_exists(chain::operation::tag<chain::sidechain_transaction_settle_operation>::value, sto.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ilog("Sidechain transaction to settle: ${sto}", ("sto", sto.id));
|
||||
|
||||
int64_t settle_amount = settle_sidechain_transaction(sto);
|
||||
|
||||
if (settle_amount < 0) {
|
||||
wlog("Sidechain transaction not settled: ${sto}", ("sto", sto.id));
|
||||
return;
|
||||
}
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
sidechain_transaction_settle_operation sts_op;
|
||||
sts_op.payer = gpo.parameters.son_account();
|
||||
sts_op.sidechain_transaction_id = sto.id;
|
||||
proposal_op.proposed_ops.emplace_back(sts_op);
|
||||
|
||||
if (settle_amount != 0) {
|
||||
if (sto.object_id.is<son_wallet_deposit_id_type>()) {
|
||||
asset_issue_operation ai_op;
|
||||
ai_op.fee = asset(2001000);
|
||||
ai_op.issuer = gpo.parameters.son_account();
|
||||
ai_op.asset_to_issue = asset(settle_amount, database.get_global_properties().parameters.btc_asset());
|
||||
ai_op.issue_to_account = database.get<son_wallet_deposit_object>(sto.object_id).peerplays_from;
|
||||
proposal_op.proposed_ops.emplace_back(ai_op);
|
||||
}
|
||||
|
||||
if (sto.object_id.is<son_wallet_withdraw_id_type>()) {
|
||||
asset_reserve_operation ar_op;
|
||||
ar_op.fee = asset(2001000);
|
||||
ar_op.payer = gpo.parameters.son_account();
|
||||
ar_op.amount_to_reserve = asset(settle_amount, database.get_global_properties().parameters.btc_asset());
|
||||
proposal_op.proposed_ops.emplace_back(ar_op);
|
||||
}
|
||||
}
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for sidechain transaction settle operation failed with exception ${e}", ("e", e.what()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,306 @@
|
|||
#include <graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
#include <boost/algorithm/hex.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
#include <fc/crypto/base64.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/network/ip.hpp>
|
||||
|
||||
#include <graphene/chain/account_object.hpp>
|
||||
#include <graphene/chain/protocol/son_wallet.hpp>
|
||||
#include <graphene/chain/son_info.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/utilities/key_conversion.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_handler_peerplays::sidechain_net_handler_peerplays(peerplays_sidechain_plugin &_plugin, const boost::program_options::variables_map &options) :
|
||||
sidechain_net_handler(_plugin, options) {
|
||||
sidechain = sidechain_type::peerplays;
|
||||
|
||||
if (options.count("peerplays-private-key")) {
|
||||
const std::vector<std::string> pub_priv_keys = options["peerplays-private-key"].as<std::vector<std::string>>();
|
||||
for (const std::string &itr_key_pair : pub_priv_keys) {
|
||||
auto key_pair = graphene::app::dejsonify<std::pair<std::string, std::string>>(itr_key_pair, 5);
|
||||
ilog("Peerplays Public Key: ${public}", ("public", key_pair.first));
|
||||
if (!key_pair.first.length() || !key_pair.second.length()) {
|
||||
FC_THROW("Invalid public private key pair.");
|
||||
}
|
||||
private_keys[key_pair.first] = key_pair.second;
|
||||
}
|
||||
}
|
||||
|
||||
database.applied_block.connect([&](const signed_block &b) {
|
||||
on_applied_block(b);
|
||||
});
|
||||
}
|
||||
|
||||
sidechain_net_handler_peerplays::~sidechain_net_handler_peerplays() {
|
||||
}
|
||||
|
||||
bool sidechain_net_handler_peerplays::process_proposal(const proposal_object &po) {
|
||||
|
||||
ilog("Proposal to process: ${po}, SON id ${son_id}", ("po", po.id)("son_id", plugin.get_current_son_id()));
|
||||
|
||||
bool should_approve = false;
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
int32_t op_idx_0 = -1;
|
||||
chain::operation op_obj_idx_0;
|
||||
|
||||
if (po.proposed_transaction.operations.size() >= 1) {
|
||||
op_idx_0 = po.proposed_transaction.operations[0].which();
|
||||
op_obj_idx_0 = po.proposed_transaction.operations[0];
|
||||
}
|
||||
|
||||
switch (op_idx_0) {
|
||||
|
||||
case chain::operation::tag<chain::son_wallet_update_operation>::value: {
|
||||
should_approve = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::son_wallet_deposit_process_operation>::value: {
|
||||
son_wallet_deposit_id_type swdo_id = op_obj_idx_0.get<son_wallet_deposit_process_operation>().son_wallet_deposit_id;
|
||||
const auto &idx = database.get_index_type<son_wallet_deposit_index>().indices().get<by_id>();
|
||||
const auto swdo = idx.find(swdo_id);
|
||||
if (swdo != idx.end()) {
|
||||
|
||||
uint32_t swdo_block_num = swdo->block_num;
|
||||
std::string swdo_sidechain_transaction_id = swdo->sidechain_transaction_id;
|
||||
uint32_t swdo_op_idx = std::stoll(swdo->sidechain_uid.substr(swdo->sidechain_uid.find_last_of("-") + 1));
|
||||
|
||||
const auto &block = database.fetch_block_by_number(swdo_block_num);
|
||||
|
||||
for (const auto &tx : block->transactions) {
|
||||
if (tx.id().str() == swdo_sidechain_transaction_id) {
|
||||
operation op = tx.operations[swdo_op_idx];
|
||||
transfer_operation t_op = op.get<transfer_operation>();
|
||||
|
||||
asset sidechain_asset = asset(swdo->sidechain_amount, fc::variant(swdo->sidechain_currency, 1).as<asset_id_type>(1));
|
||||
price sidechain_asset_price = database.get<asset_object>(sidechain_asset.asset_id).options.core_exchange_rate;
|
||||
asset peerplays_asset = asset(sidechain_asset.amount * sidechain_asset_price.base.amount / sidechain_asset_price.quote.amount);
|
||||
|
||||
should_approve = (gpo.parameters.son_account() == t_op.to) &&
|
||||
(swdo->peerplays_from == t_op.from) &&
|
||||
(sidechain_asset == t_op.amount) &&
|
||||
(swdo->peerplays_asset == peerplays_asset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::son_wallet_withdraw_process_operation>::value: {
|
||||
should_approve = false;
|
||||
break;
|
||||
}
|
||||
|
||||
case chain::operation::tag<chain::sidechain_transaction_settle_operation>::value: {
|
||||
should_approve = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
should_approve = false;
|
||||
elog("==================================================");
|
||||
elog("Proposal not considered for approval ${po}", ("po", po));
|
||||
elog("==================================================");
|
||||
}
|
||||
|
||||
return should_approve;
|
||||
}
|
||||
|
||||
void sidechain_net_handler_peerplays::process_primary_wallet() {
|
||||
return;
|
||||
}
|
||||
|
||||
void sidechain_net_handler_peerplays::process_sidechain_addresses() {
|
||||
const auto &sidechain_addresses_idx = database.get_index_type<sidechain_address_index>();
|
||||
const auto &sidechain_addresses_by_sidechain_idx = sidechain_addresses_idx.indices().get<by_sidechain>();
|
||||
const auto &sidechain_addresses_by_sidechain_range = sidechain_addresses_by_sidechain_idx.equal_range(sidechain);
|
||||
std::for_each(sidechain_addresses_by_sidechain_range.first, sidechain_addresses_by_sidechain_range.second,
|
||||
[&](const sidechain_address_object &sao) {
|
||||
if (sao.expires == time_point_sec::maximum()) {
|
||||
if (sao.deposit_address == "") {
|
||||
sidechain_address_update_operation op;
|
||||
op.payer = plugin.get_current_son_object().son_account;
|
||||
op.sidechain_address_id = sao.id;
|
||||
op.sidechain_address_account = sao.sidechain_address_account;
|
||||
op.sidechain = sao.sidechain;
|
||||
op.deposit_public_key = sao.deposit_public_key;
|
||||
op.deposit_address = sao.withdraw_address;
|
||||
op.deposit_address_data = sao.withdraw_address;
|
||||
op.withdraw_public_key = sao.withdraw_public_key;
|
||||
op.withdraw_address = sao.withdraw_address;
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending transaction for update deposit address operation failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
bool sidechain_net_handler_peerplays::process_deposit(const son_wallet_deposit_object &swdo) {
|
||||
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
asset_issue_operation ai_op;
|
||||
ai_op.issuer = gpo.parameters.son_account();
|
||||
price btc_price = database.get<asset_object>(database.get_global_properties().parameters.btc_asset()).options.core_exchange_rate;
|
||||
ai_op.asset_to_issue = asset(swdo.peerplays_asset.amount * btc_price.quote.amount / btc_price.base.amount, database.get_global_properties().parameters.btc_asset());
|
||||
ai_op.issue_to_account = swdo.peerplays_from;
|
||||
|
||||
signed_transaction tx;
|
||||
auto dyn_props = database.get_dynamic_global_properties();
|
||||
tx.set_reference_block(dyn_props.head_block_id);
|
||||
tx.set_expiration(database.head_block_time() + gpo.parameters.maximum_time_until_expiration);
|
||||
tx.operations.push_back(ai_op);
|
||||
database.current_fee_schedule().set_fee(tx.operations.back());
|
||||
|
||||
std::stringstream ss;
|
||||
fc::raw::pack(ss, tx, 1000);
|
||||
std::string tx_str = boost::algorithm::hex(ss.str());
|
||||
|
||||
if (!tx_str.empty()) {
|
||||
const chain::global_property_object &gpo = database.get_global_properties();
|
||||
|
||||
sidechain_transaction_create_operation stc_op;
|
||||
stc_op.payer = gpo.parameters.son_account();
|
||||
stc_op.object_id = swdo.id;
|
||||
stc_op.sidechain = sidechain;
|
||||
stc_op.transaction = tx_str;
|
||||
stc_op.signers = gpo.active_sons;
|
||||
|
||||
proposal_create_operation proposal_op;
|
||||
proposal_op.fee_paying_account = plugin.get_current_son_object().son_account;
|
||||
proposal_op.proposed_ops.emplace_back(stc_op);
|
||||
uint32_t lifetime = (gpo.parameters.block_interval * gpo.active_witnesses.size()) * 3;
|
||||
proposal_op.expiration_time = time_point_sec(database.head_block_time().sec_since_epoch() + lifetime);
|
||||
|
||||
signed_transaction trx = database.create_signed_transaction(plugin.get_private_key(plugin.get_current_son_id()), proposal_op);
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return true;
|
||||
} catch (fc::exception e) {
|
||||
elog("Sending proposal for deposit sidechain transaction create operation failed with exception ${e}", ("e", e.what()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool sidechain_net_handler_peerplays::process_withdrawal(const son_wallet_withdraw_object &swwo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_peerplays::process_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||
|
||||
std::stringstream ss_trx(boost::algorithm::unhex(sto.transaction));
|
||||
signed_transaction trx;
|
||||
fc::raw::unpack(ss_trx, trx, 1000);
|
||||
|
||||
fc::optional<fc::ecc::private_key> privkey = graphene::utilities::wif_to_key(get_private_key(plugin.get_current_son_object().sidechain_public_keys.at(sidechain)));
|
||||
signature_type st = trx.sign(*privkey, database.get_chain_id());
|
||||
|
||||
std::stringstream ss_st;
|
||||
fc::raw::pack(ss_st, st, 1000);
|
||||
std::string st_str = boost::algorithm::hex(ss_st.str());
|
||||
|
||||
return st_str;
|
||||
}
|
||||
|
||||
std::string sidechain_net_handler_peerplays::send_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||
|
||||
std::stringstream ss_trx(boost::algorithm::unhex(sto.transaction));
|
||||
signed_transaction trx;
|
||||
fc::raw::unpack(ss_trx, trx, 1000);
|
||||
|
||||
for (auto signature : sto.signatures) {
|
||||
if (!signature.second.empty()) {
|
||||
std::stringstream ss_st(boost::algorithm::unhex(signature.second));
|
||||
signature_type st;
|
||||
fc::raw::unpack(ss_st, st, 1000);
|
||||
|
||||
trx.signatures.push_back(st);
|
||||
trx.signees.clear();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
trx.validate();
|
||||
database.push_transaction(trx, database::validation_steps::skip_block_size_check);
|
||||
if (plugin.app().p2p_node())
|
||||
plugin.app().p2p_node()->broadcast(net::trx_message(trx));
|
||||
return trx.id().str();
|
||||
} catch (fc::exception e) {
|
||||
elog("Sidechain transaction failed with exception ${e}", ("e", e.what()));
|
||||
return "";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
int64_t sidechain_net_handler_peerplays::settle_sidechain_transaction(const sidechain_transaction_object &sto) {
|
||||
int64_t settle_amount = 0;
|
||||
return settle_amount;
|
||||
}
|
||||
|
||||
void sidechain_net_handler_peerplays::on_applied_block(const signed_block &b) {
|
||||
for (const auto &trx : b.transactions) {
|
||||
size_t operation_index = -1;
|
||||
for (auto op : trx.operations) {
|
||||
operation_index = operation_index + 1;
|
||||
if (op.which() == operation::tag<transfer_operation>::value) {
|
||||
transfer_operation transfer_op = op.get<transfer_operation>();
|
||||
if (transfer_op.to != plugin.database().get_global_properties().parameters.son_account()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "peerplays"
|
||||
<< "-" << trx.id().str() << "-" << operation_index;
|
||||
std::string sidechain_uid = ss.str();
|
||||
|
||||
sidechain_event_data sed;
|
||||
sed.timestamp = database.head_block_time();
|
||||
sed.block_num = database.head_block_num();
|
||||
sed.sidechain = sidechain_type::peerplays;
|
||||
sed.sidechain_uid = sidechain_uid;
|
||||
sed.sidechain_transaction_id = trx.id().str();
|
||||
sed.sidechain_from = fc::to_string(transfer_op.from.space_id) + "." + fc::to_string(transfer_op.from.type_id) + "." + fc::to_string((uint64_t)transfer_op.from.instance);
|
||||
sed.sidechain_to = fc::to_string(transfer_op.to.space_id) + "." + fc::to_string(transfer_op.to.type_id) + "." + fc::to_string((uint64_t)transfer_op.to.instance);
|
||||
sed.sidechain_currency = fc::to_string(transfer_op.amount.asset_id.space_id) + "." + fc::to_string(transfer_op.amount.asset_id.type_id) + "." + fc::to_string((uint64_t)transfer_op.amount.asset_id.instance);
|
||||
sed.sidechain_amount = transfer_op.amount.amount;
|
||||
sed.peerplays_from = transfer_op.from;
|
||||
sed.peerplays_to = transfer_op.to;
|
||||
price asset_price = database.get<asset_object>(transfer_op.amount.asset_id).options.core_exchange_rate;
|
||||
sed.peerplays_asset = asset(transfer_op.amount.amount * asset_price.base.amount / asset_price.quote.amount);
|
||||
sidechain_event_data_received(sed);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
#include <graphene/peerplays_sidechain/sidechain_net_manager.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <graphene/chain/son_wallet_object.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_bitcoin.hpp>
|
||||
#include <graphene/peerplays_sidechain/sidechain_net_handler_peerplays.hpp>
|
||||
|
||||
namespace graphene { namespace peerplays_sidechain {
|
||||
|
||||
sidechain_net_manager::sidechain_net_manager(peerplays_sidechain_plugin &_plugin) :
|
||||
plugin(_plugin),
|
||||
database(_plugin.database()) {
|
||||
}
|
||||
|
||||
sidechain_net_manager::~sidechain_net_manager() {
|
||||
}
|
||||
|
||||
bool sidechain_net_manager::create_handler(sidechain_type sidechain, const boost::program_options::variables_map &options) {
|
||||
|
||||
bool ret_val = false;
|
||||
|
||||
switch (sidechain) {
|
||||
case sidechain_type::bitcoin: {
|
||||
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_bitcoin(plugin, options));
|
||||
net_handlers.push_back(std::move(h));
|
||||
ret_val = true;
|
||||
break;
|
||||
}
|
||||
case sidechain_type::peerplays: {
|
||||
std::unique_ptr<sidechain_net_handler> h = std::unique_ptr<sidechain_net_handler>(new sidechain_net_handler_peerplays(plugin, options));
|
||||
net_handlers.push_back(std::move(h));
|
||||
ret_val = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_proposals() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_proposals();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_active_sons_change() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_active_sons_change();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::create_deposit_addresses() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->create_deposit_addresses();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_deposits() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_deposits();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_withdrawals() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_withdrawals();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::process_sidechain_transactions() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->process_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::send_sidechain_transactions() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->send_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
void sidechain_net_manager::settle_sidechain_transactions() {
|
||||
for (size_t i = 0; i < net_handlers.size(); i++) {
|
||||
net_handlers.at(i)->settle_sidechain_transactions();
|
||||
}
|
||||
}
|
||||
|
||||
}} // namespace graphene::peerplays_sidechain
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue